mirror of https://github.com/TryGhost/Ghost.git
Renamed /users to /authors for Content API V2 (#10096)
refs #10061 - Made /authors endpoint available in Content API V2
This commit is contained in:
parent
3b8621e19c
commit
ff6bf5f318
|
@ -0,0 +1,66 @@
|
|||
const Promise = require('bluebird');
|
||||
const common = require('../../lib/common');
|
||||
const models = require('../../models');
|
||||
const ALLOWED_INCLUDES = ['count.posts'];
|
||||
|
||||
module.exports = {
|
||||
docName: 'authors',
|
||||
|
||||
browse: {
|
||||
options: [
|
||||
'include',
|
||||
'filter',
|
||||
'fields',
|
||||
'limit',
|
||||
'status',
|
||||
'order',
|
||||
'page'
|
||||
],
|
||||
validation: {
|
||||
options: {
|
||||
include: {
|
||||
values: ALLOWED_INCLUDES
|
||||
}
|
||||
}
|
||||
},
|
||||
permissions: true,
|
||||
query(frame) {
|
||||
return models.User.findPage(frame.options);
|
||||
}
|
||||
},
|
||||
|
||||
read: {
|
||||
options: [
|
||||
'include',
|
||||
'filter',
|
||||
'fields'
|
||||
],
|
||||
data: [
|
||||
'id',
|
||||
'slug',
|
||||
'status',
|
||||
'email',
|
||||
'role'
|
||||
],
|
||||
validation: {
|
||||
options: {
|
||||
include: {
|
||||
values: ALLOWED_INCLUDES
|
||||
}
|
||||
}
|
||||
},
|
||||
permissions: true,
|
||||
query(frame) {
|
||||
return models.User.findOne(frame.data, frame.options)
|
||||
.then((model) => {
|
||||
if (!model) {
|
||||
return Promise.reject(new common.errors.NotFoundError({
|
||||
message: common.i18n.t('errors.api.authors.notFound')
|
||||
}));
|
||||
}
|
||||
|
||||
return model;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
|
@ -77,5 +77,9 @@ module.exports = {
|
|||
|
||||
get slack() {
|
||||
return shared.pipeline(require('./slack'), localUtils);
|
||||
},
|
||||
|
||||
get authors() {
|
||||
return shared.pipeline(require('./authors'), localUtils);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
const debug = require('ghost-ignition').debug('api:v2:utils:serializers:output:authors');
|
||||
const mapper = require('./utils/mapper');
|
||||
|
||||
module.exports = {
|
||||
browse(models, apiConfig, frame) {
|
||||
debug('browse');
|
||||
|
||||
frame.response = {
|
||||
authors: models.data.map(model => mapper.mapUser(model, frame)),
|
||||
meta: models.meta
|
||||
};
|
||||
|
||||
debug(frame.response);
|
||||
},
|
||||
|
||||
read(model, apiConfig, frame) {
|
||||
debug('read');
|
||||
|
||||
frame.response = {
|
||||
authors: [mapper.mapUser(model, frame)]
|
||||
};
|
||||
|
||||
debug(frame.response);
|
||||
}
|
||||
};
|
|
@ -61,5 +61,9 @@ module.exports = {
|
|||
|
||||
get oembed() {
|
||||
return require('./oembed');
|
||||
},
|
||||
|
||||
get authors() {
|
||||
return require('./authors');
|
||||
}
|
||||
};
|
||||
|
|
|
@ -64,7 +64,7 @@ function getPostData(req, res, next) {
|
|||
}
|
||||
|
||||
// @NOTE: amp is not supported for static pages
|
||||
helpers.entryLookup(urlWithoutSubdirectoryWithoutAmp, {permalinks, resourceType: 'posts'}, res.locals)
|
||||
helpers.entryLookup(urlWithoutSubdirectoryWithoutAmp, {permalinks, query: {resource: 'posts'}}, res.locals)
|
||||
.then((result) => {
|
||||
if (result && result.entry) {
|
||||
req.body.post = result.entry;
|
||||
|
|
|
@ -37,6 +37,9 @@ const RESOURCES = {
|
|||
pages: {
|
||||
alias: 'pages',
|
||||
resource: 'posts'
|
||||
},
|
||||
authors: {
|
||||
alias: 'authors'
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -145,6 +148,13 @@ get = function get(resource, options) {
|
|||
const controller = api[apiVersion][RESOURCES[resource].alias] ? RESOURCES[resource].alias : RESOURCES[resource].resource;
|
||||
const action = isBrowse(apiOptions) ? 'browse' : 'read';
|
||||
|
||||
// CASE: no fallback defined e.g. v0.1 tries to fetch "authors"
|
||||
if (!controller) {
|
||||
data.error = i18n.t('warnings.helpers.get.invalidResource');
|
||||
logging.warn(data.error);
|
||||
return Promise.resolve(options.inverse(self, {data: data}));
|
||||
}
|
||||
|
||||
// Parse the options we're going to pass to the API
|
||||
apiOptions = parseOptions(this, apiOptions);
|
||||
|
||||
|
|
|
@ -341,6 +341,7 @@ User = ghostBookshelf.Model.extend({
|
|||
|
||||
// CASE: The `withRelated` parameter is allowed when using the public API, but not the `roles` value.
|
||||
// Otherwise we expose too much information.
|
||||
// @TODO: the target controller should define the allowed includes, but not the model layer O_O (https://github.com/TryGhost/Ghost/issues/10106)
|
||||
if (options && options.context && options.context.public) {
|
||||
if (options.withRelated && options.withRelated.indexOf('roles') !== -1) {
|
||||
options.withRelated.splice(options.withRelated.indexOf('roles'), 1);
|
||||
|
|
|
@ -91,6 +91,10 @@ class CollectionRouter extends ParentRouter {
|
|||
order: this.order,
|
||||
permalinks: this.permalinks.getValue({withUrlOptions: true}),
|
||||
resourceType: this.getResourceType(),
|
||||
query: {
|
||||
alias: 'posts',
|
||||
resource: 'posts'
|
||||
},
|
||||
context: this.context,
|
||||
frontPageTemplate: 'home',
|
||||
templates: this.templates,
|
||||
|
|
|
@ -20,7 +20,10 @@ class PreviewRouter extends ParentRouter {
|
|||
_prepareContext(req, res, next) {
|
||||
res.routerOptions = {
|
||||
type: 'entry',
|
||||
resourceType: 'preview'
|
||||
query: {
|
||||
alias: 'preview',
|
||||
resource: 'posts'
|
||||
}
|
||||
};
|
||||
|
||||
next();
|
||||
|
|
|
@ -39,6 +39,10 @@ class StaticPagesRouter extends ParentRouter {
|
|||
filter: this.filter,
|
||||
permalinks: this.permalinks.getValue(),
|
||||
resourceType: this.getResourceType(),
|
||||
query: {
|
||||
alias: 'pages',
|
||||
resource: 'posts'
|
||||
},
|
||||
context: ['page']
|
||||
};
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ class TaxonomyRouter extends ParentRouter {
|
|||
type: 'channel',
|
||||
name: this.taxonomyKey,
|
||||
permalinks: this.permalinks.getValue(),
|
||||
data: {[this.taxonomyKey]: _.omit(RESOURCE_CONFIG.QUERY[this.taxonomyKey], ['alias', 'internal'])},
|
||||
data: {[this.taxonomyKey]: _.omit(RESOURCE_CONFIG.QUERY[this.taxonomyKey], ['internal'])},
|
||||
filter: RESOURCE_CONFIG.TAXONOMIES[this.taxonomyKey].filter,
|
||||
resourceType: this.getResourceType(),
|
||||
context: [this.taxonomyKey],
|
||||
|
@ -70,7 +70,7 @@ class TaxonomyRouter extends ParentRouter {
|
|||
}
|
||||
|
||||
getResourceType() {
|
||||
return RESOURCE_CONFIG.QUERY[this.taxonomyKey].resource;
|
||||
return RESOURCE_CONFIG.QUERY[this.taxonomyKey].alias;
|
||||
}
|
||||
|
||||
getRoute() {
|
||||
|
|
|
@ -10,8 +10,7 @@ module.exports.QUERY = {
|
|||
}
|
||||
},
|
||||
author: {
|
||||
internal: true,
|
||||
alias: 'users',
|
||||
alias: 'authors',
|
||||
type: 'read',
|
||||
resource: 'users',
|
||||
options: {
|
||||
|
@ -20,7 +19,7 @@ module.exports.QUERY = {
|
|||
}
|
||||
},
|
||||
user: {
|
||||
alias: 'users',
|
||||
alias: 'authors',
|
||||
type: 'read',
|
||||
resource: 'users',
|
||||
options: {
|
||||
|
|
|
@ -14,20 +14,10 @@ module.exports = function previewController(req, res, next) {
|
|||
include: 'author,authors,tags'
|
||||
};
|
||||
|
||||
let resourceType = res.routerOptions.resourceType;
|
||||
|
||||
/**
|
||||
* @TODO:
|
||||
* Remove fallback to posts if we drop v0.1.
|
||||
*/
|
||||
if (!api[resourceType]) {
|
||||
resourceType = 'posts';
|
||||
}
|
||||
|
||||
api[resourceType]
|
||||
(api[res.routerOptions.query.alias] || api[res.routerOptions.query.resource])
|
||||
.read(params)
|
||||
.then(function then(result) {
|
||||
const post = result[resourceType][0];
|
||||
const post = (result[res.routerOptions.query.alias] || result[res.routerOptions.query.resource])[0];
|
||||
|
||||
if (!post) {
|
||||
return next();
|
||||
|
|
|
@ -8,7 +8,7 @@ function processQuery(query, locals) {
|
|||
query = _.cloneDeep(query);
|
||||
|
||||
// Return a promise for the api query
|
||||
return api[query.resource][query.type](query.options);
|
||||
return (api[query.alias] || api[query.resource])[query.type](query.options);
|
||||
}
|
||||
|
||||
module.exports = function staticController(req, res, next) {
|
||||
|
@ -31,7 +31,7 @@ module.exports = function staticController(req, res, next) {
|
|||
if (config.type === 'browse') {
|
||||
response.data[name] = result[name];
|
||||
} else {
|
||||
response.data[name] = result[name][config.resource];
|
||||
response.data[name] = result[name][config.alias] || result[name][config.resource];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -30,22 +30,14 @@ function entryLookup(postUrl, routerOptions, locals) {
|
|||
isEditURL = true;
|
||||
}
|
||||
|
||||
let resourceType = routerOptions.resourceType;
|
||||
|
||||
// @NOTE: v0.1 does not have a pages controller.
|
||||
// @TODO: remove me when we drop v0.1
|
||||
if (!api[resourceType]) {
|
||||
resourceType = 'posts';
|
||||
}
|
||||
|
||||
/**
|
||||
* Query database to find entry.
|
||||
* @deprecated: `author`, will be removed in Ghost 3.0
|
||||
*/
|
||||
return api[resourceType]
|
||||
return (api[routerOptions.query.alias] || api[routerOptions.query.resource])
|
||||
.read(_.extend(_.pick(params, 'slug', 'id'), {include: 'author,authors,tags'}))
|
||||
.then(function then(result) {
|
||||
const entry = result[resourceType][0];
|
||||
const entry = (result[routerOptions.query.alias] || result[routerOptions.query.resource])[0];
|
||||
|
||||
if (!entry) {
|
||||
return Promise.resolve();
|
||||
|
|
|
@ -2,27 +2,31 @@
|
|||
* # Fetch Data
|
||||
* Dynamically build and execute queries on the API
|
||||
*/
|
||||
const _ = require('lodash'),
|
||||
Promise = require('bluebird'),
|
||||
defaultPostQuery = {};
|
||||
const _ = require('lodash');
|
||||
const Promise = require('bluebird');
|
||||
|
||||
// The default settings for a default post query
|
||||
const queryDefaults = {
|
||||
type: 'browse',
|
||||
resource: 'posts',
|
||||
alias: 'posts',
|
||||
options: {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Default post query needs to always include author, authors & tags
|
||||
*
|
||||
* @deprecated: `author`, will be removed in Ghost 3.0
|
||||
*/
|
||||
_.extend(defaultPostQuery, queryDefaults, {
|
||||
const defaultQueryOptions = {
|
||||
options: {
|
||||
include: 'author,authors,tags'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Default post query needs to always include author, authors & tags
|
||||
*/
|
||||
const defaultPostQuery = _.cloneDeep(queryDefaults);
|
||||
defaultPostQuery.options = defaultQueryOptions.options;
|
||||
|
||||
/**
|
||||
* ## Process Query
|
||||
|
@ -39,7 +43,6 @@ function processQuery(query, slugParam, locals) {
|
|||
|
||||
query = _.cloneDeep(query);
|
||||
|
||||
// Ensure that all the properties are filled out
|
||||
_.defaultsDeep(query, queryDefaults);
|
||||
|
||||
// Replace any slugs, see TaxonomyRouter. We replace any '%s' by the slug
|
||||
|
@ -48,7 +51,7 @@ function processQuery(query, slugParam, locals) {
|
|||
});
|
||||
|
||||
// Return a promise for the api query
|
||||
return api[query.resource][query.type](query.options);
|
||||
return (api[query.alias] || api[query.resource])[query.type](query.options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -100,7 +103,7 @@ function fetchData(pathOptions, routerOptions, locals) {
|
|||
if (config.type === 'browse') {
|
||||
response.data[name] = results[name];
|
||||
} else {
|
||||
response.data[name] = results[name][config.resource];
|
||||
response.data[name] = results[name][config.alias] || results[name][config.resource];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -48,7 +48,6 @@ _private.validateData = function validateData(object) {
|
|||
|
||||
let [resourceKey, slug] = shortForm.split('.');
|
||||
|
||||
// @NOTE: `data: author.foo` is not allowed currently, because this will make {{author}} available in the theme, which is deprecated (single author usage)
|
||||
if (!RESOURCE_CONFIG.QUERY[resourceKey] ||
|
||||
(RESOURCE_CONFIG.QUERY[resourceKey].hasOwnProperty('internal') && RESOURCE_CONFIG.QUERY[resourceKey].internal === true)) {
|
||||
throw new common.errors.ValidationError({
|
||||
|
@ -58,7 +57,7 @@ _private.validateData = function validateData(object) {
|
|||
}
|
||||
|
||||
longForm.query[options.resourceKey || resourceKey] = {};
|
||||
longForm.query[options.resourceKey || resourceKey] = _.omit(_.cloneDeep(RESOURCE_CONFIG.QUERY[resourceKey]), 'alias');
|
||||
longForm.query[options.resourceKey || resourceKey] = _.cloneDeep(RESOURCE_CONFIG.QUERY[resourceKey]);
|
||||
|
||||
// redirect is enabled by default when using the short form
|
||||
longForm.router = {
|
||||
|
@ -148,6 +147,7 @@ _private.validateData = function validateData(object) {
|
|||
});
|
||||
|
||||
const DEFAULT_RESOURCE = _.find(RESOURCE_CONFIG.QUERY, {resource: data.query[key].resource});
|
||||
data.query[key] = _.defaults(data.query[key], _.omit(DEFAULT_RESOURCE, 'options'));
|
||||
|
||||
data.query[key].options = _.pick(object.data[key], allowedQueryOptions);
|
||||
if (data.query[key].type === 'read') {
|
||||
|
|
|
@ -109,7 +109,7 @@ const resourcesConfig = [
|
|||
}
|
||||
},
|
||||
{
|
||||
type: 'users',
|
||||
type: 'authors',
|
||||
modelOptions: {
|
||||
modelName: 'User',
|
||||
exclude: [
|
||||
|
|
|
@ -349,6 +349,9 @@
|
|||
"posts": {
|
||||
"postNotFound": "Post not found."
|
||||
},
|
||||
"authors": {
|
||||
"notFound": "Author not found."
|
||||
},
|
||||
"pages": {
|
||||
"pageNotFound": "Page not found."
|
||||
},
|
||||
|
|
|
@ -16,9 +16,9 @@ module.exports = function apiRoutes() {
|
|||
router.get('/pages/slug/:slug', mw.authenticatePublic, apiv2.http(apiv2.pages.read));
|
||||
|
||||
// ## Users
|
||||
router.get('/users', mw.authenticatePublic, apiv2.http(apiv2.users.browse));
|
||||
router.get('/users/:id', mw.authenticatePublic, apiv2.http(apiv2.users.read));
|
||||
router.get('/users/slug/:slug', mw.authenticatePublic, apiv2.http(apiv2.users.read));
|
||||
router.get('/authors', mw.authenticatePublic, apiv2.http(apiv2.authors.browse));
|
||||
router.get('/authors/:id', mw.authenticatePublic, apiv2.http(apiv2.authors.read));
|
||||
router.get('/authors/slug/:slug', mw.authenticatePublic, apiv2.http(apiv2.authors.read));
|
||||
|
||||
// ## Tags
|
||||
router.get('/tags', mw.authenticatePublic, apiv2.http(apiv2.tags.browse));
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
const should = require('should');
|
||||
const supertest = require('supertest');
|
||||
const _ = require('lodash');
|
||||
const url = require('url');
|
||||
const configUtils = require('../../../../utils/configUtils');
|
||||
const config = require('../../../../../../core/server/config');
|
||||
const models = require('../../../../../../core/server/models');
|
||||
const testUtils = require('../../../../utils');
|
||||
const localUtils = require('./utils');
|
||||
const ghost = testUtils.startGhost;
|
||||
let request;
|
||||
|
||||
describe('Authors Content API V2', function () {
|
||||
let ghostServer;
|
||||
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function () {
|
||||
return testUtils.initFixtures('users:no-owner', 'user:inactive', 'posts', 'api_keys');
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
configUtils.restore();
|
||||
});
|
||||
|
||||
const validKey = localUtils.getValidKey();
|
||||
|
||||
it('browse authors', function (done) {
|
||||
request.get(localUtils.API.getApiQuery(`authors/?key=${validKey}`))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.authors);
|
||||
testUtils.API.checkResponse(jsonResponse, 'authors');
|
||||
jsonResponse.authors.should.have.length(7);
|
||||
|
||||
// We don't expose the email address, status and other attrs.
|
||||
testUtils.API.checkResponse(jsonResponse.authors[0], 'author', ['url'], null, null, {public: true});
|
||||
|
||||
should.exist(res.body.authors[0].url);
|
||||
should.exist(url.parse(res.body.authors[0].url).protocol);
|
||||
should.exist(url.parse(res.body.authors[0].url).host);
|
||||
|
||||
// Public api returns all authors, but no status! Locked/Inactive authors can still have written articles.
|
||||
models.User.findPage(Object.assign({status: 'all'}, testUtils.context.internal))
|
||||
.then((response) => {
|
||||
_.map(response.data, (model) => model.toJSON()).length.should.eql(7);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('browse authors: throws error if trying to fetch roles', function (done) {
|
||||
request.get(localUtils.API.getApiQuery(`authors/?key=${validKey}&include=roles`))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(422)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('browse user by slug: count.posts', function (done) {
|
||||
request.get(localUtils.API.getApiQuery(`authors/slug/ghost/?key=${validKey}&include=count.posts`))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
var jsonResponse = res.body;
|
||||
|
||||
should.exist(jsonResponse.authors);
|
||||
jsonResponse.authors.should.have.length(1);
|
||||
|
||||
// We don't expose the email address.
|
||||
testUtils.API.checkResponse(jsonResponse.authors[0], 'author', ['count', 'url'], null, null, {public: true});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('browse user by id: count.posts', function (done) {
|
||||
request.get(localUtils.API.getApiQuery(`authors/1/?key=${validKey}&include=count.posts`))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
var jsonResponse = res.body;
|
||||
|
||||
should.exist(jsonResponse.authors);
|
||||
jsonResponse.authors.should.have.length(1);
|
||||
|
||||
// We don't expose the email address.
|
||||
testUtils.API.checkResponse(jsonResponse.authors[0], 'author', ['count', 'url'], null, null, {public: true});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('browse user with count.posts', function (done) {
|
||||
request.get(localUtils.API.getApiQuery(`authors/?key=${validKey}&include=count.posts&order=count.posts ASC`))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var jsonResponse = res.body;
|
||||
|
||||
should.exist(jsonResponse.authors);
|
||||
jsonResponse.authors.should.have.length(7);
|
||||
|
||||
// We don't expose the email address.
|
||||
testUtils.API.checkResponse(jsonResponse.authors[0], 'author', ['count', 'url'], null, null, {public: true});
|
||||
|
||||
// Each user should have the correct count
|
||||
_.find(jsonResponse.authors, {slug:'joe-bloggs'}).count.posts.should.eql(4);
|
||||
_.find(jsonResponse.authors, {slug:'contributor'}).count.posts.should.eql(0);
|
||||
_.find(jsonResponse.authors, {slug:'slimer-mcectoplasm'}).count.posts.should.eql(0);
|
||||
_.find(jsonResponse.authors, {slug:'jimothy-bogendath'}).count.posts.should.eql(0);
|
||||
_.find(jsonResponse.authors, {slug: 'smith-wellingsworth'}).count.posts.should.eql(0);
|
||||
_.find(jsonResponse.authors, {slug:'ghost'}).count.posts.should.eql(7);
|
||||
_.find(jsonResponse.authors, {slug:'inactive'}).count.posts.should.eql(0);
|
||||
|
||||
const ids = jsonResponse.authors
|
||||
.filter(author => (author.slug !== 'ghost'))
|
||||
.filter(author => (author.slug !== 'inactive'))
|
||||
.map(user=> user.id);
|
||||
|
||||
ids.should.eql([
|
||||
testUtils.DataGenerator.Content.users[1].id,
|
||||
testUtils.DataGenerator.Content.users[2].id,
|
||||
testUtils.DataGenerator.Content.users[3].id,
|
||||
testUtils.DataGenerator.Content.users[7].id,
|
||||
testUtils.DataGenerator.Content.users[0].id
|
||||
]);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('browse authors: post count', function (done) {
|
||||
request.get(localUtils.API.getApiQuery(`authors/?key=${validKey}&include=count.posts`))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.authors);
|
||||
testUtils.API.checkResponse(jsonResponse, 'authors');
|
||||
jsonResponse.authors.should.have.length(7);
|
||||
|
||||
// We don't expose the email address.
|
||||
testUtils.API.checkResponse(jsonResponse.authors[0], 'author', ['count', 'url'], null, null, {public: true});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,247 +0,0 @@
|
|||
const should = require('should');
|
||||
const supertest = require('supertest');
|
||||
const _ = require('lodash');
|
||||
const url = require('url');
|
||||
const configUtils = require('../../../../utils/configUtils');
|
||||
const config = require('../../../../../../core/server/config');
|
||||
const models = require('../../../../../../core/server/models');
|
||||
const testUtils = require('../../../../utils');
|
||||
const localUtils = require('./utils');
|
||||
const ghost = testUtils.startGhost;
|
||||
let request;
|
||||
|
||||
describe('Users Content API V2', function () {
|
||||
let ghostServer;
|
||||
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function () {
|
||||
return testUtils.initFixtures('users:no-owner', 'user:inactive', 'posts', 'api_keys');
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
configUtils.restore();
|
||||
});
|
||||
|
||||
const validKey = localUtils.getValidKey();
|
||||
|
||||
it('browse users', function (done) {
|
||||
request.get(localUtils.API.getApiQuery(`users/?key=${validKey}`))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.users);
|
||||
testUtils.API.checkResponse(jsonResponse, 'users');
|
||||
jsonResponse.users.should.have.length(7);
|
||||
|
||||
// We don't expose the email address, status and other attrs.
|
||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['url'], null, null, {public: true});
|
||||
|
||||
should.exist(res.body.users[0].url);
|
||||
should.exist(url.parse(res.body.users[0].url).protocol);
|
||||
should.exist(url.parse(res.body.users[0].url).host);
|
||||
|
||||
// Public api returns all users, but no status! Locked/Inactive users can still have written articles.
|
||||
models.User.findPage(Object.assign({status: 'all'}, testUtils.context.internal))
|
||||
.then((response) => {
|
||||
_.map(response.data, (model) => model.toJSON()).length.should.eql(7);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('browse users: ignores fetching roles', function (done) {
|
||||
request.get(localUtils.API.getApiQuery(`users/?key=${validKey}&include=roles`))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.users);
|
||||
testUtils.API.checkResponse(jsonResponse, 'users');
|
||||
jsonResponse.users.should.have.length(7);
|
||||
|
||||
// We don't expose the email address.
|
||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['url'], null, null, {public: true});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('browse user by slug: ignores fetching roles', function (done) {
|
||||
request.get(localUtils.API.getApiQuery(`users/slug/ghost/?key=${validKey}&include=roles`))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
var jsonResponse = res.body;
|
||||
|
||||
should.exist(jsonResponse.users);
|
||||
jsonResponse.users.should.have.length(1);
|
||||
|
||||
// We don't expose the email address.
|
||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['url'], null, null, {public: true});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('browse user by slug: count.posts', function (done) {
|
||||
request.get(localUtils.API.getApiQuery(`users/slug/ghost/?key=${validKey}&include=count.posts`))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
var jsonResponse = res.body;
|
||||
|
||||
should.exist(jsonResponse.users);
|
||||
jsonResponse.users.should.have.length(1);
|
||||
|
||||
// We don't expose the email address.
|
||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['count', 'url'], null, null, {public: true});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('browse user by id: count.posts', function (done) {
|
||||
request.get(localUtils.API.getApiQuery(`users/1/?key=${validKey}&include=count.posts`))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
var jsonResponse = res.body;
|
||||
|
||||
should.exist(jsonResponse.users);
|
||||
jsonResponse.users.should.have.length(1);
|
||||
|
||||
// We don't expose the email address.
|
||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['count', 'url'], null, null, {public: true});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('browse user with count.posts', function (done) {
|
||||
request.get(localUtils.API.getApiQuery(`users/?key=${validKey}&include=count.posts&order=count.posts ASC`))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var jsonResponse = res.body;
|
||||
|
||||
should.exist(jsonResponse.users);
|
||||
jsonResponse.users.should.have.length(7);
|
||||
|
||||
// We don't expose the email address.
|
||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['count', 'url'], null, null, {public: true});
|
||||
|
||||
// Each user should have the correct count
|
||||
_.find(jsonResponse.users, {slug:'joe-bloggs'}).count.posts.should.eql(4);
|
||||
_.find(jsonResponse.users, {slug:'contributor'}).count.posts.should.eql(0);
|
||||
_.find(jsonResponse.users, {slug:'slimer-mcectoplasm'}).count.posts.should.eql(0);
|
||||
_.find(jsonResponse.users, {slug:'jimothy-bogendath'}).count.posts.should.eql(0);
|
||||
_.find(jsonResponse.users, {slug: 'smith-wellingsworth'}).count.posts.should.eql(0);
|
||||
_.find(jsonResponse.users, {slug:'ghost'}).count.posts.should.eql(7);
|
||||
_.find(jsonResponse.users, {slug:'inactive'}).count.posts.should.eql(0);
|
||||
|
||||
const ids = jsonResponse.users
|
||||
.filter(user => (user.slug !== 'ghost'))
|
||||
.filter(user => (user.slug !== 'inactive'))
|
||||
.map(user=> user.id);
|
||||
|
||||
ids.should.eql([
|
||||
testUtils.DataGenerator.Content.users[1].id,
|
||||
testUtils.DataGenerator.Content.users[2].id,
|
||||
testUtils.DataGenerator.Content.users[3].id,
|
||||
testUtils.DataGenerator.Content.users[7].id,
|
||||
testUtils.DataGenerator.Content.users[0].id
|
||||
]);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('browse user by id: ignores fetching roles', function (done) {
|
||||
request.get(localUtils.API.getApiQuery(`users/1/?key=${validKey}&include=roles`))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
var jsonResponse = res.body;
|
||||
|
||||
should.exist(jsonResponse.users);
|
||||
jsonResponse.users.should.have.length(1);
|
||||
|
||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['url'], null, null, {public: true});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('browse users: post count', function (done) {
|
||||
request.get(localUtils.API.getApiQuery(`users/?key=${validKey}&include=count.posts`))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.users);
|
||||
testUtils.API.checkResponse(jsonResponse, 'users');
|
||||
jsonResponse.users.should.have.length(7);
|
||||
|
||||
// We don't expose the email address.
|
||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['count', 'url'], null, null, {public: true});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -79,7 +79,7 @@ describe('Integration: services/url/UrlService', function () {
|
|||
});
|
||||
|
||||
router2.getFilter.returns(false);
|
||||
router2.getResourceType.returns('users');
|
||||
router2.getResourceType.returns('authors');
|
||||
router2.getPermalinks.returns({
|
||||
getValue: function () {
|
||||
return '/author/:slug/';
|
||||
|
@ -147,7 +147,7 @@ describe('Integration: services/url/UrlService', function () {
|
|||
generator.getUrls().length.should.eql(5);
|
||||
}
|
||||
|
||||
if (generator.router.getResourceType() === 'users') {
|
||||
if (generator.router.getResourceType() === 'authors') {
|
||||
generator.getUrls().length.should.eql(5);
|
||||
}
|
||||
});
|
||||
|
@ -377,7 +377,7 @@ describe('Integration: services/url/UrlService', function () {
|
|||
});
|
||||
|
||||
router3.getFilter.returns(false);
|
||||
router3.getResourceType.returns('users');
|
||||
router3.getResourceType.returns('authors');
|
||||
router3.getPermalinks.returns({
|
||||
getValue: function () {
|
||||
return '/persons/:slug/';
|
||||
|
@ -451,7 +451,7 @@ describe('Integration: services/url/UrlService', function () {
|
|||
generator.getUrls().length.should.eql(5);
|
||||
}
|
||||
|
||||
if (generator.router.getResourceType() === 'users') {
|
||||
if (generator.router.getResourceType() === 'authors') {
|
||||
generator.getUrls().length.should.eql(5);
|
||||
}
|
||||
});
|
||||
|
@ -616,7 +616,7 @@ describe('Integration: services/url/UrlService', function () {
|
|||
});
|
||||
|
||||
router3.getFilter.returns(false);
|
||||
router3.getResourceType.returns('users');
|
||||
router3.getResourceType.returns('authors');
|
||||
router3.getPermalinks.returns({
|
||||
getValue: function () {
|
||||
return '/persons/:slug/';
|
||||
|
|
|
@ -897,6 +897,7 @@ describe('Integration - Web - Site', function () {
|
|||
data: {
|
||||
query: {
|
||||
tag: {
|
||||
alias: 'tags',
|
||||
resource: 'tags',
|
||||
type: 'read',
|
||||
options: {
|
||||
|
@ -915,6 +916,7 @@ describe('Integration - Web - Site', function () {
|
|||
data: {
|
||||
query: {
|
||||
apollo: {
|
||||
alias: 'tags',
|
||||
resource: 'tags',
|
||||
type: 'read',
|
||||
options: {
|
||||
|
@ -1225,6 +1227,7 @@ describe('Integration - Web - Site', function () {
|
|||
data: {
|
||||
query: {
|
||||
tag: {
|
||||
alias: 'tags',
|
||||
resource: 'tags',
|
||||
type: 'read',
|
||||
options: {
|
||||
|
@ -1244,6 +1247,7 @@ describe('Integration - Web - Site', function () {
|
|||
data: {
|
||||
query: {
|
||||
tag: {
|
||||
alias: 'tags',
|
||||
resource: 'tags',
|
||||
type: 'read',
|
||||
options: {
|
||||
|
@ -1264,6 +1268,7 @@ describe('Integration - Web - Site', function () {
|
|||
data: {
|
||||
query: {
|
||||
tag: {
|
||||
alias: 'authors',
|
||||
resource: 'users',
|
||||
type: 'read',
|
||||
options: {
|
||||
|
@ -1288,6 +1293,7 @@ describe('Integration - Web - Site', function () {
|
|||
data: {
|
||||
query: {
|
||||
tag: {
|
||||
alias: 'authors',
|
||||
resource: 'users',
|
||||
type: 'read',
|
||||
options: {
|
||||
|
|
|
@ -123,7 +123,7 @@ describe('Unit - apps/amp/lib/router', function () {
|
|||
|
||||
urlService.getPermalinkByUrl.withArgs('/welcome/').returns('/:slug/');
|
||||
|
||||
helpers.entryLookup.withArgs('/welcome/', {permalinks: '/:slug/', resourceType: 'posts'}).resolves({
|
||||
helpers.entryLookup.withArgs('/welcome/', {permalinks: '/:slug/', query: {resource: 'posts'}}).resolves({
|
||||
entry: post
|
||||
});
|
||||
|
||||
|
@ -139,7 +139,7 @@ describe('Unit - apps/amp/lib/router', function () {
|
|||
|
||||
urlService.getPermalinkByUrl.withArgs('/welcome/').returns('/:slug/');
|
||||
|
||||
helpers.entryLookup.withArgs('/welcome/', {permalinks: '/:slug/', resourceType: 'posts'}).resolves({
|
||||
helpers.entryLookup.withArgs('/welcome/', {permalinks: '/:slug/', query: {resource: 'posts'}}).resolves({
|
||||
entry: post
|
||||
});
|
||||
|
||||
|
@ -154,7 +154,7 @@ describe('Unit - apps/amp/lib/router', function () {
|
|||
|
||||
urlService.getPermalinkByUrl.withArgs('/welcome/').returns('/:slug/');
|
||||
|
||||
helpers.entryLookup.withArgs('/welcome/', {permalinks: '/:slug/', resourceType: 'posts'})
|
||||
helpers.entryLookup.withArgs('/welcome/', {permalinks: '/:slug/', query: {resource: 'posts'}})
|
||||
.rejects(new common.errors.NotFoundError());
|
||||
|
||||
ampController.getPostData(req, res, function (err) {
|
||||
|
|
|
@ -283,6 +283,29 @@ describe('{{#get}} helper', function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe('authors v0.1', function () {
|
||||
let browseUsersStub;
|
||||
const meta = {pagination: {}};
|
||||
|
||||
beforeEach(function () {
|
||||
browseUsersStub = sandbox.stub(api["v0.1"].users, 'browse');
|
||||
browseUsersStub.returns(new Promise.resolve({users: [], meta: meta}));
|
||||
});
|
||||
|
||||
it('browse users v0.1', function (done) {
|
||||
helpers.get.call(
|
||||
{},
|
||||
'authors',
|
||||
{hash: {}, data: locals, fn: fn, inverse: inverse}
|
||||
).then(function () {
|
||||
inverse.calledOnce.should.be.true();
|
||||
should.exist(inverse.args[0][1].data.error);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('users v2', function () {
|
||||
let browseUsersStub;
|
||||
const meta = {pagination: {}};
|
||||
|
@ -290,9 +313,9 @@ describe('{{#get}} helper', function () {
|
|||
beforeEach(function () {
|
||||
locals = {root: {_locals: {apiVersion: 'v2'}}};
|
||||
|
||||
browseUsersStub = sandbox.stub(api["v2"], 'users').get(() => {
|
||||
browseUsersStub = sandbox.stub(api["v2"], 'authors').get(() => {
|
||||
return {
|
||||
browse: sandbox.stub().resolves({users: [], meta: meta})
|
||||
browse: sandbox.stub().resolves({authors: [], meta: meta})
|
||||
};
|
||||
});
|
||||
});
|
||||
|
@ -306,8 +329,40 @@ describe('{{#get}} helper', function () {
|
|||
labsStub.calledOnce.should.be.true();
|
||||
|
||||
fn.called.should.be.true();
|
||||
fn.firstCall.args[0].should.be.an.Object().with.property('users');
|
||||
fn.firstCall.args[0].users.should.eql([]);
|
||||
fn.firstCall.args[0].should.be.an.Object().with.property('authors');
|
||||
fn.firstCall.args[0].authors.should.eql([]);
|
||||
inverse.called.should.be.false();
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('authors v2', function () {
|
||||
let browseUsersStub;
|
||||
const meta = {pagination: {}};
|
||||
|
||||
beforeEach(function () {
|
||||
locals = {root: {_locals: {apiVersion: 'v2'}}};
|
||||
|
||||
browseUsersStub = sandbox.stub(api["v2"], 'authors').get(() => {
|
||||
return {
|
||||
browse: sandbox.stub().resolves({authors: [], meta: meta})
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
it('browse users', function (done) {
|
||||
helpers.get.call(
|
||||
{},
|
||||
'authors',
|
||||
{hash: {}, data: locals, fn: fn, inverse: inverse}
|
||||
).then(function () {
|
||||
labsStub.calledOnce.should.be.true();
|
||||
|
||||
fn.called.should.be.true();
|
||||
fn.firstCall.args[0].should.be.an.Object().with.property('authors');
|
||||
fn.firstCall.args[0].authors.should.eql([]);
|
||||
inverse.called.should.be.false();
|
||||
|
||||
done();
|
||||
|
|
|
@ -136,6 +136,7 @@ describe('UNIT - services/routing/CollectionRouter', function () {
|
|||
type: 'collection',
|
||||
filter: 'page:false',
|
||||
permalinks: '/:slug/:options(edit)?/',
|
||||
query: {alias: 'posts', resource: 'posts'},
|
||||
frontPageTemplate: 'home',
|
||||
templates: [],
|
||||
identifier: collectionRouter.identifier,
|
||||
|
@ -158,6 +159,7 @@ describe('UNIT - services/routing/CollectionRouter', function () {
|
|||
type: 'collection',
|
||||
filter: 'page:false',
|
||||
permalinks: '/:slug/:options(edit)?/',
|
||||
query: {alias: 'posts', resource: 'posts'},
|
||||
frontPageTemplate: 'home',
|
||||
templates: ['index', 'home'],
|
||||
identifier: collectionRouter.identifier,
|
||||
|
|
|
@ -72,7 +72,7 @@ describe('UNIT - services/routing/TaxonomyRouter', function () {
|
|||
name: 'tag',
|
||||
permalinks: '/tag/:slug/',
|
||||
resourceType: RESOURCE_CONFIG.QUERY.tag.resource,
|
||||
data: {tag: _.omit(RESOURCE_CONFIG.QUERY.tag, 'alias')},
|
||||
data: {tag: RESOURCE_CONFIG.QUERY.tag},
|
||||
filter: RESOURCE_CONFIG.TAXONOMIES.tag.filter,
|
||||
context: ['tag'],
|
||||
slugTemplate: true,
|
||||
|
|
|
@ -46,7 +46,7 @@ describe('Unit - services/routing/controllers/preview', function () {
|
|||
|
||||
res = {
|
||||
routerOptions: {
|
||||
resourceType: 'preview'
|
||||
query: {alias: 'preview', resource: 'posts'}
|
||||
},
|
||||
locals: {
|
||||
apiVersion: 'v0.1'
|
||||
|
@ -165,7 +165,7 @@ describe('Unit - services/routing/controllers/preview', function () {
|
|||
|
||||
res = {
|
||||
routerOptions: {
|
||||
resourceType: 'preview'
|
||||
query: {alias: 'preview', resource: 'posts'}
|
||||
},
|
||||
locals: {
|
||||
apiVersion: 'v2'
|
||||
|
|
|
@ -23,7 +23,7 @@ describe('Unit - services/routing/helpers/entry-lookup', function () {
|
|||
describe('static pages', function () {
|
||||
const routerOptions = {
|
||||
permalinks: '/:slug/',
|
||||
resourceType: 'pages'
|
||||
query: {alias: 'pages', resource: 'posts'}
|
||||
};
|
||||
|
||||
let pages;
|
||||
|
@ -54,7 +54,7 @@ describe('Unit - services/routing/helpers/entry-lookup', function () {
|
|||
describe('Permalinks: /:slug/', function () {
|
||||
const routerOptions = {
|
||||
permalinks: '/:slug/',
|
||||
resourceType: 'posts'
|
||||
query: {alias: 'pages', resource: 'posts'}
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
|
@ -121,7 +121,8 @@ describe('Unit - services/routing/helpers/entry-lookup', function () {
|
|||
|
||||
describe('Permalinks: /:year/:month/:day/:slug/', function () {
|
||||
const routerOptions = {
|
||||
permalinks: '/:year/:month/:day/:slug/'
|
||||
permalinks: '/:year/:month/:day/:slug/',
|
||||
query: {alias: 'pages', resource: 'posts'}
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
|
@ -192,7 +193,8 @@ describe('Unit - services/routing/helpers/entry-lookup', function () {
|
|||
|
||||
describe('with url options', function () {
|
||||
const routerOptions = {
|
||||
permalinks: '/:slug/:options(edit)?'
|
||||
permalinks: '/:slug/:options(edit)?',
|
||||
query: {alias: 'pages', resource: 'posts'}
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
|
@ -272,7 +274,7 @@ describe('Unit - services/routing/helpers/entry-lookup', function () {
|
|||
describe('static pages', function () {
|
||||
const routerOptions = {
|
||||
permalinks: '/:slug/',
|
||||
resourceType: 'pages'
|
||||
query: {alias: 'pages', resource: 'posts'}
|
||||
};
|
||||
|
||||
let pages;
|
||||
|
@ -323,7 +325,7 @@ describe('Unit - services/routing/helpers/entry-lookup', function () {
|
|||
describe('posts', function () {
|
||||
const routerOptions = {
|
||||
permalinks: '/:slug/',
|
||||
resourceType: 'posts'
|
||||
query: {alias: 'posts', resource: 'posts'}
|
||||
};
|
||||
|
||||
let posts;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
api = require('../../../../../server/api'),
|
||||
api = require('../../../../../server/api')['v0.1'],
|
||||
helpers = require('../../../../../server/services/routing/helpers'),
|
||||
testUtils = require('../../../../utils'),
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
@ -154,6 +154,7 @@ describe('Unit - services/routing/helpers/fetch-data', function () {
|
|||
filter: 'tags:%s',
|
||||
data: {
|
||||
tag: {
|
||||
alias: 'tags',
|
||||
type: 'read',
|
||||
resource: 'tags',
|
||||
options: {slug: '%s'}
|
||||
|
|
|
@ -349,6 +349,9 @@ describe('UNIT: services/settings/validate', function () {
|
|||
bed: 'tag.bed',
|
||||
dream: 'tag.dream'
|
||||
}
|
||||
},
|
||||
'/lala/': {
|
||||
data: 'author.carsten'
|
||||
}
|
||||
},
|
||||
collections: {
|
||||
|
@ -378,6 +381,7 @@ describe('UNIT: services/settings/validate', function () {
|
|||
data: {
|
||||
query: {
|
||||
tag: {
|
||||
alias: 'tags',
|
||||
resource: 'tags',
|
||||
type: 'read',
|
||||
options: {
|
||||
|
@ -396,6 +400,7 @@ describe('UNIT: services/settings/validate', function () {
|
|||
data: {
|
||||
query: {
|
||||
user: {
|
||||
alias: 'authors',
|
||||
resource: 'users',
|
||||
type: 'read',
|
||||
options: {
|
||||
|
@ -405,7 +410,7 @@ describe('UNIT: services/settings/validate', function () {
|
|||
}
|
||||
},
|
||||
router: {
|
||||
users: [{redirect: true, slug: 'ghost'}]
|
||||
authors: [{redirect: true, slug: 'ghost'}]
|
||||
}
|
||||
},
|
||||
templates: []
|
||||
|
@ -414,6 +419,7 @@ describe('UNIT: services/settings/validate', function () {
|
|||
data: {
|
||||
query: {
|
||||
tag: {
|
||||
alias: 'tags',
|
||||
resource: 'tags',
|
||||
type: 'read',
|
||||
options: {
|
||||
|
@ -432,6 +438,7 @@ describe('UNIT: services/settings/validate', function () {
|
|||
data: {
|
||||
query: {
|
||||
bed: {
|
||||
alias: 'tags',
|
||||
resource: 'tags',
|
||||
type: 'read',
|
||||
options: {
|
||||
|
@ -440,6 +447,7 @@ describe('UNIT: services/settings/validate', function () {
|
|||
}
|
||||
},
|
||||
dream: {
|
||||
alias: 'tags',
|
||||
resource: 'tags',
|
||||
type: 'read',
|
||||
options: {
|
||||
|
@ -453,6 +461,25 @@ describe('UNIT: services/settings/validate', function () {
|
|||
}
|
||||
},
|
||||
templates: []
|
||||
},
|
||||
'/lala/': {
|
||||
data: {
|
||||
query: {
|
||||
author: {
|
||||
alias: 'authors',
|
||||
resource: 'users',
|
||||
type: 'read',
|
||||
options: {
|
||||
slug: 'carsten',
|
||||
visibility: 'public'
|
||||
}
|
||||
}
|
||||
},
|
||||
router: {
|
||||
authors: [{redirect: true, slug: 'carsten'}]
|
||||
}
|
||||
},
|
||||
templates: []
|
||||
}
|
||||
},
|
||||
collections: {
|
||||
|
@ -461,6 +488,7 @@ describe('UNIT: services/settings/validate', function () {
|
|||
data: {
|
||||
query: {
|
||||
home: {
|
||||
alias: 'pages',
|
||||
resource: 'posts',
|
||||
type: 'read',
|
||||
options: {
|
||||
|
@ -481,6 +509,7 @@ describe('UNIT: services/settings/validate', function () {
|
|||
data: {
|
||||
query: {
|
||||
something: {
|
||||
alias: 'tags',
|
||||
resource: 'tags',
|
||||
type: 'read',
|
||||
options: {
|
||||
|
@ -500,6 +529,7 @@ describe('UNIT: services/settings/validate', function () {
|
|||
data: {
|
||||
query: {
|
||||
tag: {
|
||||
alias: 'tags',
|
||||
resource: 'tags',
|
||||
type: 'read',
|
||||
options: {
|
||||
|
@ -540,7 +570,7 @@ describe('UNIT: services/settings/validate', function () {
|
|||
},
|
||||
'/partyparty/': {
|
||||
data: {
|
||||
posts: {
|
||||
people: {
|
||||
resource: 'users',
|
||||
type: 'read',
|
||||
slug: 'djgutelaune',
|
||||
|
@ -571,6 +601,7 @@ describe('UNIT: services/settings/validate', function () {
|
|||
data: {
|
||||
query: {
|
||||
food: {
|
||||
alias: 'posts',
|
||||
resource: 'posts',
|
||||
type: 'browse',
|
||||
options: {}
|
||||
|
@ -586,6 +617,7 @@ describe('UNIT: services/settings/validate', function () {
|
|||
data: {
|
||||
query: {
|
||||
posts: {
|
||||
alias: 'posts',
|
||||
resource: 'posts',
|
||||
type: 'read',
|
||||
options: {
|
||||
|
@ -604,7 +636,8 @@ describe('UNIT: services/settings/validate', function () {
|
|||
'/partyparty/': {
|
||||
data: {
|
||||
query: {
|
||||
posts: {
|
||||
people: {
|
||||
alias: 'authors',
|
||||
resource: 'users',
|
||||
type: 'read',
|
||||
options: {
|
||||
|
@ -614,7 +647,7 @@ describe('UNIT: services/settings/validate', function () {
|
|||
}
|
||||
},
|
||||
router: {
|
||||
users: [{redirect: true, slug: 'djgutelaune'}]
|
||||
authors: [{redirect: true, slug: 'djgutelaune'}]
|
||||
}
|
||||
},
|
||||
templates: []
|
||||
|
@ -626,6 +659,7 @@ describe('UNIT: services/settings/validate', function () {
|
|||
data: {
|
||||
query: {
|
||||
gym: {
|
||||
alias: 'posts',
|
||||
resource: 'posts',
|
||||
type: 'read',
|
||||
options: {
|
||||
|
@ -703,24 +737,6 @@ describe('UNIT: services/settings/validate', function () {
|
|||
throw new Error('should fail');
|
||||
});
|
||||
|
||||
it('errors: data shortform author is not allowed', function () {
|
||||
try {
|
||||
validate({
|
||||
collections: {
|
||||
'/magic/': {
|
||||
permalink: '/{slug}/',
|
||||
data: 'author.food'
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
(err instanceof common.errors.ValidationError).should.be.true();
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error('should fail');
|
||||
});
|
||||
|
||||
it('errors: data longform name is author', function () {
|
||||
try {
|
||||
validate({
|
||||
|
|
|
@ -68,7 +68,7 @@ describe('Unit: services/url/Resources', function () {
|
|||
created.tags.length.should.eql(testUtils.DataGenerator.forKnex.tags.length);
|
||||
|
||||
// all mocked users are active
|
||||
created.users.length.should.eql(testUtils.DataGenerator.forKnex.users.length);
|
||||
created.authors.length.should.eql(testUtils.DataGenerator.forKnex.users.length);
|
||||
done();
|
||||
});
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ var _ = require('lodash'),
|
|||
posts: ['posts', 'meta'],
|
||||
tags: ['tags', 'meta'],
|
||||
users: ['users', 'meta'],
|
||||
authors: ['authors', 'meta'],
|
||||
settings: ['settings', 'meta'],
|
||||
subscribers: ['subscribers', 'meta'],
|
||||
roles: ['roles'],
|
||||
|
@ -45,6 +46,22 @@ var _ = require('lodash'),
|
|||
)
|
||||
.value()
|
||||
},
|
||||
author: _(schema.users)
|
||||
.keys()
|
||||
.without(
|
||||
'password',
|
||||
'email',
|
||||
'ghost_auth_access_token',
|
||||
'ghost_auth_id',
|
||||
'created_at',
|
||||
'created_by',
|
||||
'updated_at',
|
||||
'updated_by',
|
||||
'last_seen',
|
||||
'status'
|
||||
)
|
||||
.value()
|
||||
,
|
||||
// Tag API swaps parent_id to parent
|
||||
tag: _(schema.tags).keys().without('parent_id').concat('parent').value(),
|
||||
setting: _.keys(schema.settings),
|
||||
|
|
Loading…
Reference in New Issue