0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-04-01 02:41:39 -05:00

Removed unnecessary labels mapper & serializer

refs: https://github.com/TryGhost/Toolbox/issues/245

- There's no need for a mapper or serializer as labels uses the default behaviour
- Added a full suite of tests, consolidating from regression and using the new framework to prove nothing is broken
This commit is contained in:
Hannah Wolfe 2022-03-21 17:04:32 +00:00
parent e68cb8b314
commit 396dd5f7f9
7 changed files with 283 additions and 147 deletions
core/server/api/canary/utils/serializers/output
test
e2e-api/admin
regression/api/admin

View file

@ -145,10 +145,6 @@ module.exports = {
return require('./emails');
},
get labels() {
return require('./labels');
},
get snippets() {
return require('./snippets');
},

View file

@ -1,25 +0,0 @@
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:labels');
const mappers = require('./mappers');
module.exports = {
all(models, apiConfig, frame) {
debug('all');
if (!models) {
return;
}
if (models.meta) {
frame.response = {
labels: models.data.map(model => mappers.labels(model, frame)),
meta: models.meta
};
return;
}
frame.response = {
labels: [mappers.labels(models, frame)]
};
}
};

View file

@ -3,7 +3,6 @@ module.exports = {
emails: require('./emails'),
images: require('./images'),
integrations: require('./integrations'),
labels: require('./labels'),
pages: require('./pages'),
posts: require('./posts'),
settings: require('./settings'),

View file

@ -1,4 +0,0 @@
module.exports = (model, frame) => {
const jsonModel = model.toJSON ? model.toJSON(frame.options) : model;
return jsonModel;
};

View file

@ -0,0 +1,188 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Labels API Can add 1: [body] 1`] = `
Object {
"labels": Array [
Object {
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"name": "test",
"slug": "test",
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
},
],
}
`;
exports[`Labels API Can add 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "154",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/labels\\\\/\\[a-f0-9\\]\\{24\\}\\\\//,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Labels API Can browse with member count 1: [body] 1`] = `
Object {
"labels": Array [
Object {
"count": Object {
"members": 0,
},
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"name": "test",
"slug": "test",
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
},
],
"meta": Object {
"pagination": Object {
"limit": 15,
"next": null,
"page": 1,
"pages": 1,
"prev": null,
"total": 1,
},
},
}
`;
exports[`Labels API Can browse with member count 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "264",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Labels API Can destroy 1: [body] 1`] = `
Object {
"labels": Array [
Object {
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"name": "testing",
"slug": "test",
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
},
],
}
`;
exports[`Labels API Can destroy 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "157",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Labels API Can destroy 3: [body] 1`] = `Object {}`;
exports[`Labels API Can destroy 4: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin",
"x-cache-invalidate": "/*",
"x-powered-by": "Express",
}
`;
exports[`Labels API Can read by slug and edit 1: [body] 1`] = `
Object {
"labels": Array [
Object {
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"name": "test",
"slug": "test",
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
},
],
}
`;
exports[`Labels API Can read by slug and edit 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "154",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Labels API Can read by slug and edit 3: [body] 1`] = `
Object {
"labels": Array [
Object {
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"name": "testing",
"slug": "test",
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
},
],
}
`;
exports[`Labels API Can read by slug and edit 4: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "157",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-cache-invalidate": "/*",
"x-powered-by": "Express",
}
`;
exports[`Labels API Errors when adding label with the same name 1: [body] 1`] = `
Object {
"errors": Array [
Object {
"code": null,
"context": "Label already exists",
"details": null,
"help": null,
"id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
"message": "Validation error, cannot save label.",
"property": null,
"type": "ValidationError",
},
],
}
`;
exports[`Labels API Errors when adding label with the same name 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "220",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;

View file

@ -1,65 +1,111 @@
const path = require('path');
const should = require('should');
const supertest = require('supertest');
const sinon = require('sinon');
const testUtils = require('../../utils');
const localUtils = require('./utils');
const config = require('../../../core/shared/config');
const {agentProvider, fixtureManager, matchers} = require('../../utils/e2e-framework');
const {anyObjectId, anyISODateTime, anyErrorId, anyEtag, anyLocationFor} = matchers;
const matchLabel = {
id: anyObjectId,
created_at: anyISODateTime,
updated_at: anyISODateTime
};
describe('Labels API', function () {
let request;
after(function () {
sinon.restore();
});
let agent;
before(async function () {
await localUtils.startGhost();
request = supertest.agent(config.get('url'));
await localUtils.doAuth(request);
agent = await agentProvider.getAdminAPIAgent();
await fixtureManager.init();
await agent.loginAsOwner();
});
it('Can add', async function () {
const label = {
name: 'test'
};
await agent
.post('labels')
.body({labels: [{
name: 'test'
}]})
.expectStatus(201)
.matchBodySnapshot({
labels: [matchLabel]
})
.matchHeaderSnapshot({
etag: anyEtag,
location: anyLocationFor('labels')
});
});
const res = await request
.post(localUtils.API.getApiQuery(`labels/`))
.send({labels: [label]})
.set('Origin', config.get('url'))
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(201);
it('Errors when adding label with the same name', async function () {
await agent
.post('labels')
.body({labels: [{
name: 'test'
}]})
.expectStatus(422)
.matchBodySnapshot({
errors: [{
id: anyErrorId
should.not.exist(res.headers['x-cache-invalidate']);
const jsonResponse = res.body;
should.exist(jsonResponse);
should.exist(jsonResponse.labels);
jsonResponse.labels.should.have.length(1);
jsonResponse.labels[0].name.should.equal(label.name);
jsonResponse.labels[0].slug.should.equal(label.name);
should.exist(res.headers.location);
res.headers.location.should.equal(`http://127.0.0.1:2369${localUtils.API.getApiQuery('labels/')}${res.body.labels[0].id}/`);
}]
})
.matchHeaderSnapshot({
etag: anyEtag
});
});
it('Can browse with member count', async function () {
const res = await request
.get(localUtils.API.getApiQuery('labels/?include=count.members'))
.set('Origin', config.get('url'))
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200);
await agent
.get('labels/?include=count.members')
.expectStatus(200)
.matchBodySnapshot({
labels: [matchLabel]
})
.matchHeaderSnapshot({
etag: anyEtag
});
});
should.not.exist(res.headers['x-cache-invalidate']);
const jsonResponse = res.body;
should.exist(jsonResponse);
should.exist(jsonResponse.labels);
it('Can read by slug and edit', async function () {
const {body} = await agent
.get('labels/slug/test/')
.expectStatus(200)
.matchBodySnapshot({
labels: [matchLabel]
})
.matchHeaderSnapshot({
etag: anyEtag
});
jsonResponse.labels.should.have.length(1);
should.exist(jsonResponse.labels[0].count);
jsonResponse.labels[0].count.members.should.equal(0);
const id = body.labels[0].id;
await agent
.put(`labels/${id}`)
.body({labels: [{name: 'testing'}]})
.expectStatus(200)
.matchBodySnapshot({
labels: [matchLabel]
})
.matchHeaderSnapshot({
etag: anyEtag
});
});
it('Can destroy', async function () {
const {body} = await agent
.get('labels/slug/test/')
.expectStatus(200)
.matchBodySnapshot({
labels: [matchLabel]
})
.matchHeaderSnapshot({
etag: anyEtag
});
const id = body.labels[0].id;
await agent
.delete(`labels/${id}`)
.expectStatus(204)
.matchBodySnapshot()
.matchHeaderSnapshot({
etag: anyEtag
});
});
});

View file

@ -1,64 +0,0 @@
const path = require('path');
const should = require('should');
const supertest = require('supertest');
const sinon = require('sinon');
const testUtils = require('../../../utils');
const localUtils = require('./utils');
const config = require('../../../../core/shared/config');
let request;
describe('Labels API', function () {
after(function () {
sinon.restore();
});
before(async function () {
await localUtils.startGhost();
request = supertest.agent(config.get('url'));
await localUtils.doAuth(request);
});
it('Errors when adding label with the same name', function () {
const label = {
name: 'test'
};
return request
.post(localUtils.API.getApiQuery(`labels/`))
.send({labels: [label]})
.set('Origin', config.get('url'))
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(201)
.then((res) => {
should.not.exist(res.headers['x-cache-invalidate']);
const jsonResponse = res.body;
should.exist(jsonResponse);
should.exist(jsonResponse.labels);
jsonResponse.labels.should.have.length(1);
jsonResponse.labels[0].name.should.equal(label.name);
jsonResponse.labels[0].slug.should.equal(label.name);
})
.then(() => {
return request
.post(localUtils.API.getApiQuery(`labels/`))
.send({labels: [label]})
.set('Origin', config.get('url'))
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(422);
})
.then((res) => {
should.not.exist(res.headers['x-cache-invalidate']);
const jsonResponse = res.body;
should.exist(jsonResponse);
should.exist(jsonResponse.errors);
jsonResponse.errors.should.have.length(1);
jsonResponse.errors[0].type.should.equal('ValidationError');
jsonResponse.errors[0].context.should.equal('Label already exists');
});
});
});