0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-27 22:49:56 -05:00

Added tags ctrl to v2 (#10000)

refs #9866
This commit is contained in:
Katharina Irrgang 2018-10-12 23:10:43 +02:00 committed by GitHub
parent 310526b6c5
commit 1ee4d53bfe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 358 additions and 6 deletions

View file

@ -53,5 +53,9 @@ module.exports = {
get upload() {
return shared.pipeline(require('./upload'), localUtils);
},
get tags() {
return shared.pipeline(require('./tags'), localUtils);
}
};

147
core/server/api/v2/tags.js Normal file
View file

@ -0,0 +1,147 @@
const Promise = require('bluebird');
const common = require('../../lib/common');
const models = require('../../models');
const ALLOWED_INCLUDES = ['count.posts'];
module.exports = {
docName: 'tags',
browse: {
options: [
'include',
'filter',
'fields',
'limit',
'order',
'debug'
],
validation: {
options: {
include: {
values: ALLOWED_INCLUDES
}
}
},
permissions: true,
query(frame) {
return models.Tag.findPage(frame.options);
}
},
read: {
options: [
'include',
'filter',
'fields',
'debug'
],
data: [
'id',
'slug',
'visibility'
],
validation: {
options: {
include: {
values: ALLOWED_INCLUDES
}
}
},
permissions: true,
query(frame) {
return models.Tag.findOne(frame.data, frame.options)
.then((model) => {
if (!model) {
return Promise.reject(new common.errors.NotFoundError({
message: common.i18n.t('errors.api.tags.tagNotFound')
}));
}
return model;
});
}
},
add: {
statusCode: 201,
headers: {
cacheInvalidate: true
},
options: [
'include'
],
validation: {
options: {
include: {
values: ALLOWED_INCLUDES
}
},
data: {
name: {
required: true
}
}
},
permissions: true,
query(frame) {
return models.Tag.add(frame.data.tags[0], frame.options);
}
},
edit: {
headers: {
cacheInvalidate: true
},
options: [
'id',
'include'
],
validation: {
options: {
include: {
values: ALLOWED_INCLUDES
},
id: {
required: true
}
}
},
permissions: true,
query(frame) {
return models.Tag.edit(frame.data.tags[0], frame.options)
.then((model) => {
if (!model) {
return Promise.reject(new common.errors.NotFoundError({
message: common.i18n.t('errors.api.tags.tagNotFound')
}));
}
return model;
});
}
},
destroy: {
statusCode: 204,
headers: {
cacheInvalidate: true
},
options: [
'id'
],
validation: {
options: {
include: {
values: ALLOWED_INCLUDES
},
id: {
required: true
}
}
},
permissions: true,
query(frame) {
return models.Tag.destroy(frame.options).return(null);
}
}
};

View file

@ -41,5 +41,9 @@ module.exports = {
get upload() {
return require('./upload');
},
get tags() {
return require('./tags');
}
};

View file

@ -0,0 +1,37 @@
const debug = require('ghost-ignition').debug('api:v2:utils:serializers:output:tags');
const urlService = require('../../../../../services/url');
const absoluteUrls = (tag) => {
tag.url = urlService.getUrlByResourceId(tag.id, {absolute: true});
if (tag.feature_image) {
tag.feature_image = urlService.utils.urlFor('image', {image: tag.feature_image}, true);
}
return tag;
};
module.exports = {
all(models, apiConfig, frame) {
debug('all');
if (!models) {
return;
}
if (models.meta) {
frame.response = {
tags: models.data.map(model => absoluteUrls(model.toJSON(frame.options))),
meta: models.meta
};
return;
}
frame.response = {
tags: [absoluteUrls(models.toJSON(frame.options))]
};
debug(frame.response);
}
};

View file

@ -65,12 +65,12 @@ module.exports = function apiRoutes() {
router.del('/users/:id', mw.authAdminAPI, api.http(api.users.destroy));
// ## Tags
router.get('/tags', mw.authAdminAPI, api.http(api.tags.browse));
router.get('/tags/:id', mw.authAdminAPI, api.http(api.tags.read));
router.get('/tags/slug/:slug', mw.authAdminAPI, api.http(api.tags.read));
router.post('/tags', mw.authAdminAPI, api.http(api.tags.add));
router.put('/tags/:id', mw.authAdminAPI, api.http(api.tags.edit));
router.del('/tags/:id', mw.authAdminAPI, api.http(api.tags.destroy));
router.get('/tags', mw.authAdminAPI, apiv2.http(apiv2.tags.browse));
router.get('/tags/:id', mw.authAdminAPI, apiv2.http(apiv2.tags.read));
router.get('/tags/slug/:slug', mw.authAdminAPI, apiv2.http(apiv2.tags.read));
router.post('/tags', mw.authAdminAPI, apiv2.http(apiv2.tags.add));
router.put('/tags/:id', mw.authAdminAPI, apiv2.http(apiv2.tags.edit));
router.del('/tags/:id', mw.authAdminAPI, apiv2.http(apiv2.tags.destroy));
// ## Subscribers
router.get('/subscribers', shared.middlewares.labs.subscribers, mw.authAdminAPI, apiv2.http(apiv2.subscribers.browse));

View file

@ -0,0 +1,160 @@
const should = require('should');
const supertest = require('supertest');
const testUtils = require('../../../../utils');
const localUtils = require('./utils');
const config = require('../../../../../../core/server/config');
const ghost = testUtils.startGhost;
let request;
describe('Tag API V2', function () {
let ghostServer;
before(function () {
return ghost()
.then(function (_ghostServer) {
ghostServer = _ghostServer;
request = supertest.agent(config.get('url'));
})
.then(function () {
return localUtils.doAuth(request, 'posts');
});
});
it('browse', function () {
return request
.get(localUtils.API.getApiQuery('tags/?include=count.posts&order=name%20DESC'))
.set('Origin', config.get('url'))
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.then((res) => {
should.not.exist(res.headers['x-cache-invalidate']);
const jsonResponse = res.body;
should.exist(jsonResponse);
should.exist(jsonResponse.tags);
jsonResponse.tags.should.have.length(6);
testUtils.API.checkResponse(jsonResponse.tags[0], 'tag', ['count', 'url']);
testUtils.API.isISO8601(jsonResponse.tags[0].created_at).should.be.true();
jsonResponse.tags[0].created_at.should.be.an.instanceof(String);
jsonResponse.meta.pagination.should.have.property('page', 1);
jsonResponse.meta.pagination.should.have.property('limit', 15);
jsonResponse.meta.pagination.should.have.property('pages', 1);
jsonResponse.meta.pagination.should.have.property('total', 6);
jsonResponse.meta.pagination.should.have.property('next', null);
jsonResponse.meta.pagination.should.have.property('prev', null);
jsonResponse.tags[0].url.should.eql(`${config.get('url')}/tag/pollo/`);
should.exist(jsonResponse.tags[0].count.posts);
});
});
it('read', function () {
return request
.get(localUtils.API.getApiQuery(`tags/${testUtils.existingData.tags[0].id}/?include=count.posts`))
.set('Origin', config.get('url'))
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.then((res) => {
should.not.exist(res.headers['x-cache-invalidate']);
const jsonResponse = res.body;
should.exist(jsonResponse);
should.exist(jsonResponse.tags);
jsonResponse.tags.should.have.length(1);
testUtils.API.checkResponse(jsonResponse.tags[0], 'tag', ['count', 'url']);
should.exist(jsonResponse.tags[0].count.posts);
jsonResponse.tags[0].url.should.eql(`${config.get('url')}/tag/getting-started/`);
});
});
it('add', function () {
const tag = testUtils.DataGenerator.forKnex.createTag();
return request
.post(localUtils.API.getApiQuery('tags/'))
.set('Origin', config.get('url'))
.send({
tags: [tag]
})
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(201)
.then((res) => {
should.exist(res.headers['x-cache-invalidate']);
const jsonResponse = res.body;
should.exist(jsonResponse);
should.exist(jsonResponse.tags);
jsonResponse.tags.should.have.length(1);
// @TODO: model layer has no defaults for these properties
testUtils.API.checkResponse(jsonResponse.tags[0], 'tag', ['url'], [
'feature_image',
'meta_description',
'meta_title',
'parent'
]);
testUtils.API.isISO8601(jsonResponse.tags[0].created_at).should.be.true();
});
});
it('add internal', function () {
const tag = testUtils.DataGenerator.forKnex.createTag({
name: '#test',
slug: null
});
return request
.post(localUtils.API.getApiQuery('tags/'))
.set('Origin', config.get('url'))
.send({
tags: [tag]
})
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(201)
.then((res) => {
should.exist(res.headers['x-cache-invalidate']);
const jsonResponse = res.body;
should.exist(jsonResponse);
jsonResponse.tags[0].visibility.should.eql('internal');
jsonResponse.tags[0].name.should.eql('#test');
jsonResponse.tags[0].slug.should.eql('hash-test');
});
});
it('edit', function () {
return request
.put(localUtils.API.getApiQuery(`tags/${testUtils.existingData.tags[0].id}`))
.set('Origin', config.get('url'))
.send({
tags: [Object.assign({}, testUtils.existingData.tags[0], {description: 'hey ho ab ins klo'})]
})
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.then((res) => {
should.exist(res.headers['x-cache-invalidate']);
const jsonResponse = res.body;
should.exist(jsonResponse);
should.exist(jsonResponse.tags);
jsonResponse.tags.should.have.length(1);
testUtils.API.checkResponse(jsonResponse.tags[0], 'tag', ['url']);
jsonResponse.tags[0].description.should.eql('hey ho ab ins klo');
});
});
it('destroy', function () {
return request
.del(localUtils.API.getApiQuery(`tags/${testUtils.existingData.tags[0].id}`))
.set('Origin', config.get('url'))
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(204)
.then((res) => {
should.exist(res.headers['x-cache-invalidate']);
res.body.should.eql({});
});
});
});