Changed entry lookup helper to respect the resource type

refs #9866

- the entry helper is used for static pages and post lookups
- now that we support changing the api version, we have to respect the resource type
- for v2: we ask the pages controller for static pages
- in v0.1: pages and posts lived on the same route
- we are talking about the content API (!) - not admin api
This commit is contained in:
kirrg001 2018-10-17 14:22:33 +02:00 committed by Katharina Irrgang
parent 803a325ade
commit 12ff70497f
7 changed files with 365 additions and 220 deletions

View File

@ -63,10 +63,11 @@ function getPostData(req, res, next) {
}));
}
helpers.entryLookup(urlWithoutSubdirectoryWithoutAmp, {permalinks}, res.locals)
// @NOTE: amp is not supported for static pages
helpers.entryLookup(urlWithoutSubdirectoryWithoutAmp, {permalinks, resourceType: 'posts'}, res.locals)
.then((result) => {
if (result && result.post) {
req.body.post = result.post;
if (result && result.entry) {
req.body.post = result.entry;
}
next();

View File

@ -15,10 +15,10 @@ module.exports = function entryController(req, res, next) {
return helpers.entryLookup(req.path, res.routerOptions, res.locals)
.then(function then(lookup) {
// Format data 1
const post = lookup ? lookup.post : false;
const entry = lookup ? lookup.entry : false;
if (!post) {
debug('no post');
if (!entry) {
debug('no entry');
return next();
}
@ -31,7 +31,7 @@ module.exports = function entryController(req, res, next) {
// CASE: last param is of url is /edit, redirect to admin
if (lookup.isEditURL) {
debug('redirect. is edit url');
return urlService.utils.redirectToAdmin(302, res, '/editor/' + post.id);
return urlService.utils.redirectToAdmin(302, res, '/editor/' + entry.id);
}
/**
@ -50,7 +50,7 @@ module.exports = function entryController(req, res, next) {
*
* That's why we have to check against the router type.
*/
if (urlService.getResourceById(post.id).config.type !== res.routerOptions.resourceType) {
if (urlService.getResourceById(entry.id).config.type !== res.routerOptions.resourceType) {
debug('not my resource type');
return next();
}
@ -67,18 +67,18 @@ module.exports = function entryController(req, res, next) {
* @NOTE:
* This file is used for v0.1 and v2. v0.1 returns relative urls, v2 returns absolute urls.
*/
if (urlService.utils.absoluteToRelative(post.url, {withoutSubdirectory: true}) !== req.path) {
if (urlService.utils.absoluteToRelative(entry.url, {withoutSubdirectory: true}) !== req.path) {
debug('redirect');
return urlService.utils.redirect301(res, url.format({
pathname: url.parse(post.url).pathname,
pathname: url.parse(entry.url).pathname,
search: url.parse(req.originalUrl).search
}));
}
helpers.secure(req, post);
helpers.secure(req, entry);
filters.doFilter('prePostsRender', post, res.locals)
filters.doFilter('prePostsRender', entry, res.locals)
.then(helpers.renderEntry(req, res));
})
.catch(helpers.handleError(next));

View File

@ -30,29 +30,29 @@ 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 post.
*
* @TODO:
*
* We actually need to differentiate here between pages and posts controller for v2.
* Currently this API call is without context object and it works out of the box, because the v2 serializer
* only forces `page:true|false` if you send a content key.
*
* It's also a little tricky, because the v0.1 has no pages controller.
*
* Query database to find entry.
* @deprecated: `author`, will be removed in Ghost 3.0
*/
return api.posts.read(_.extend(_.pick(params, 'slug', 'id'), {include: 'author,authors,tags'}))
return api[resourceType]
.read(_.extend(_.pick(params, 'slug', 'id'), {include: 'author,authors,tags'}))
.then(function then(result) {
const post = result.posts[0];
const entry = result[resourceType][0];
if (!post) {
if (!entry) {
return Promise.resolve();
}
return {
post: post,
entry: entry,
isEditURL: isEditURL,
isUnknownOption: isEditURL ? false : !!params.options
};

View File

@ -33,6 +33,11 @@ function formatPageResponse(result) {
/**
* similar to formatPageResponse, but for entries (post or page)
*
* @TODO
* In the future, we should return {page: entry} or {post:entry).
* But for now, we would break the themes if we just change it.
*
* @return {Object} containing page variables
*/
function formatResponse(post) {

View File

@ -123,8 +123,8 @@ describe('Unit - apps/amp/lib/router', function () {
urlService.getPermalinkByUrl.withArgs('/welcome/').returns('/:slug/');
helpers.entryLookup.withArgs('/welcome/', {permalinks: '/:slug/'}).resolves({
post: post
helpers.entryLookup.withArgs('/welcome/', {permalinks: '/:slug/', resourceType: 'posts'}).resolves({
entry: post
});
ampController.getPostData(req, res, function () {
@ -139,8 +139,8 @@ describe('Unit - apps/amp/lib/router', function () {
urlService.getPermalinkByUrl.withArgs('/welcome/').returns('/:slug/');
helpers.entryLookup.withArgs('/welcome/', {permalinks: '/:slug/'}).resolves({
post: post
helpers.entryLookup.withArgs('/welcome/', {permalinks: '/:slug/', resourceType: 'posts'}).resolves({
entry: post
});
ampController.getPostData(req, res, function () {
@ -154,7 +154,8 @@ describe('Unit - apps/amp/lib/router', function () {
urlService.getPermalinkByUrl.withArgs('/welcome/').returns('/:slug/');
helpers.entryLookup.withArgs('/welcome/', {permalinks: '/:slug/'}).rejects(new common.errors.NotFoundError());
helpers.entryLookup.withArgs('/welcome/', {permalinks: '/:slug/', resourceType: 'posts'})
.rejects(new common.errors.NotFoundError());
ampController.getPostData(req, res, function (err) {
(err instanceof common.errors.NotFoundError).should.be.true();

View File

@ -84,7 +84,7 @@ describe('Unit - services/routing/controllers/entry', function () {
entryLookUpStub.withArgs(req.path, res.routerOptions)
.resolves({
post: post
entry: post
});
renderStub.callsFake(function () {
@ -102,7 +102,7 @@ describe('Unit - services/routing/controllers/entry', function () {
entryLookUpStub.withArgs(req.path, res.routerOptions)
.resolves({
isUnknownOption: true,
post: post
entry: post
});
controllers.entry(req, res, function (err) {
@ -117,7 +117,7 @@ describe('Unit - services/routing/controllers/entry', function () {
entryLookUpStub.withArgs(req.path, res.routerOptions)
.resolves({
isEditURL: true,
post: post
entry: post
});
urlService.utils.redirectToAdmin.callsFake(function (statusCode, res, editorUrl) {
@ -141,7 +141,7 @@ describe('Unit - services/routing/controllers/entry', function () {
entryLookUpStub.withArgs(req.path, res.routerOptions)
.resolves({
post: post
entry: post
});
controllers.entry(req, res, function (err) {
@ -165,7 +165,7 @@ describe('Unit - services/routing/controllers/entry', function () {
entryLookUpStub.withArgs(req.path, res.routerOptions)
.resolves({
post: post
entry: post
});
urlService.utils.redirect301.callsFake(function (res, postUrl) {
@ -194,7 +194,7 @@ describe('Unit - services/routing/controllers/entry', function () {
entryLookUpStub.withArgs(req.path, res.routerOptions)
.resolves({
post: post
entry: post
});
urlService.utils.redirect301.callsFake(function (res, postUrl) {

View File

@ -13,224 +13,362 @@ describe('Unit - services/routing/helpers/entry-lookup', function () {
sandbox.restore();
});
beforeEach(function () {
sandbox.stub(api.posts, 'read');
locals = {apiVersion: 'v0.1'};
});
describe('Permalinks: /:slug/', function () {
const routerOptions = {
permalinks: '/:slug/'
};
describe('v0.1', function () {
beforeEach(function () {
posts = [
testUtils.DataGenerator.forKnex.createPost({url: '/test/', slug: 'test'})
];
sandbox.stub(api.posts, 'read');
api.posts.read.withArgs({slug: posts[0].slug, include: 'author,authors,tags'})
.resolves({
posts: posts
});
locals = {apiVersion: 'v0.1'};
});
it('can lookup absolute url: /:slug/', function (done) {
const testUrl = 'http://127.0.0.1:2369' + posts[0].url;
describe('static pages', function () {
const routerOptions = {
permalinks: '/:slug/',
resourceType: 'pages'
};
helpers.entryLookup(testUrl, routerOptions, locals).then(function (lookup) {
api.posts.read.calledOnce.should.be.true();
should.exist(lookup.post);
lookup.post.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.false();
let pages;
done();
}).catch(done);
});
beforeEach(function () {
pages = [
testUtils.DataGenerator.forKnex.createPost({url: '/test/', slug: 'test', page: true})
];
it('can lookup relative url: /:slug/', function (done) {
const testUrl = posts[0].url;
api.posts.read.withArgs({slug: pages[0].slug, include: 'author,authors,tags'})
.resolves({
posts: pages
});
});
helpers.entryLookup(testUrl, routerOptions, locals).then(function (lookup) {
api.posts.read.calledOnce.should.be.true();
should.exist(lookup.post);
lookup.post.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.false();
it('ensure posts controller is triggered', function () {
const testUrl = 'http://127.0.0.1:2369' + pages[0].url;
done();
}).catch(done);
});
it('cannot lookup absolute url: /:year/:month/:day/:slug/', function (done) {
const testUrl = 'http://127.0.0.1:2369/2016/01/01' + posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
it('cannot lookup relative url: /:year/:month/:day/:slug/', function (done) {
const testUrl = '/2016/01/01' + posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
});
describe('Permalinks: /:year/:month/:day/:slug/', function () {
const routerOptions = {
permalinks: '/:year/:month/:day/:slug/'
};
beforeEach(function () {
posts = [
testUtils.DataGenerator.forKnex.createPost({url: '/2016/01/01/example/', slug: 'example'})
];
api.posts.read.withArgs({slug: posts[0].slug, include: 'author,authors,tags'})
.resolves({
posts: posts
});
});
it('cannot lookup absolute url: /:slug/', function (done) {
const testUrl = 'http://127.0.0.1:2369/' + posts[0].slug;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
it('cannot lookup relative url using :slug', function (done) {
const testUrl = posts[0].slug;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
it('can lookup absolute url: /:year/:month/:day/:slug/', function (done) {
const testUrl = 'http://127.0.0.1:2369' + posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
return helpers.entryLookup(testUrl, routerOptions, locals).then(function (lookup) {
api.posts.read.calledOnce.should.be.true();
should.exist(lookup.post);
lookup.post.should.have.property('url', posts[0].url);
should.exist(lookup.entry);
lookup.entry.should.have.property('url', pages[0].url);
lookup.isEditURL.should.be.false();
});
});
});
describe('Permalinks: /:slug/', function () {
const routerOptions = {
permalinks: '/:slug/',
resourceType: 'posts'
};
beforeEach(function () {
posts = [
testUtils.DataGenerator.forKnex.createPost({url: '/test/', slug: 'test'})
];
api.posts.read.withArgs({slug: posts[0].slug, include: 'author,authors,tags'})
.resolves({
posts: posts
});
});
it('can lookup absolute url: /:slug/', function (done) {
const testUrl = 'http://127.0.0.1:2369' + posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals).then(function (lookup) {
api.posts.read.calledOnce.should.be.true();
should.exist(lookup.entry);
lookup.entry.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.false();
done();
})
.catch(done);
});
}).catch(done);
});
it('can lookup relative url: /:year/:month/:day/:slug/', function (done) {
const testUrl = posts[0].url;
it('can lookup relative url: /:slug/', function (done) {
const testUrl = posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
helpers.entryLookup(testUrl, routerOptions, locals).then(function (lookup) {
api.posts.read.calledOnce.should.be.true();
should.exist(lookup.post);
lookup.post.should.have.property('url', posts[0].url);
should.exist(lookup.entry);
lookup.entry.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.false();
done();
})
.catch(done);
}).catch(done);
});
it('cannot lookup absolute url: /:year/:month/:day/:slug/', function (done) {
const testUrl = 'http://127.0.0.1:2369/2016/01/01' + posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
it('cannot lookup relative url: /:year/:month/:day/:slug/', function (done) {
const testUrl = '/2016/01/01' + posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
});
describe('Permalinks: /:year/:month/:day/:slug/', function () {
const routerOptions = {
permalinks: '/:year/:month/:day/:slug/'
};
beforeEach(function () {
posts = [
testUtils.DataGenerator.forKnex.createPost({url: '/2016/01/01/example/', slug: 'example'})
];
api.posts.read.withArgs({slug: posts[0].slug, include: 'author,authors,tags'})
.resolves({
posts: posts
});
});
it('cannot lookup absolute url: /:slug/', function (done) {
const testUrl = 'http://127.0.0.1:2369/' + posts[0].slug;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
it('cannot lookup relative url using :slug', function (done) {
const testUrl = posts[0].slug;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
it('can lookup absolute url: /:year/:month/:day/:slug/', function (done) {
const testUrl = 'http://127.0.0.1:2369' + posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.true();
should.exist(lookup.entry);
lookup.entry.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.false();
done();
})
.catch(done);
});
it('can lookup relative url: /:year/:month/:day/:slug/', function (done) {
const testUrl = posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.true();
should.exist(lookup.entry);
lookup.entry.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.false();
done();
})
.catch(done);
});
});
describe('with url options', function () {
const routerOptions = {
permalinks: '/:slug/:options(edit)?'
};
beforeEach(function () {
posts = [
testUtils.DataGenerator.forKnex.createPost({url: '/test/', slug: 'test'})
];
api.posts.read.withArgs({slug: posts[0].slug, include: 'author,authors,tags'})
.resolves({posts: posts});
});
it('can lookup absolute url: /:slug/edit/', function (done) {
const testUrl = 'http://127.0.0.1:2369' + posts[0].url + 'edit/';
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.true();
lookup.entry.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.true();
done();
})
.catch(done);
});
it('can lookup relative url: /:slug/edit/', function (done) {
const testUrl = posts[0].url + 'edit/';
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.true();
lookup.entry.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.true();
done();
})
.catch(done);
});
it('cannot lookup absolute url: /:year/:month/:day/:slug/edit/', function (done) {
const testUrl = 'http://127.0.0.1:2369/2016/01/01' + posts[0].url + 'edit/';
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
it('cannot lookup relative url: /:year/:month/:day/:slug/edit/', function (done) {
const testUrl = '/2016/01/01' + posts[0].url + 'edit/';
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
it('unknown url option', function (done) {
const testUrl = posts[0].url + 'not-edit/';
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
});
});
describe('with url options', function () {
const routerOptions = {
permalinks: '/:slug/:options(edit)?'
};
describe('v2', function () {
describe('static pages', function () {
const routerOptions = {
permalinks: '/:slug/',
resourceType: 'pages'
};
beforeEach(function () {
posts = [
testUtils.DataGenerator.forKnex.createPost({url: '/test/', slug: 'test'})
];
let pages;
let postsReadStub;
let pagesReadStub;
api.posts.read.withArgs({slug: posts[0].slug, include: 'author,authors,tags'})
.resolves({posts: posts});
beforeEach(function () {
pages = [
testUtils.DataGenerator.forKnex.createPost({url: '/test/', slug: 'test', page: true})
];
postsReadStub = sandbox.stub();
pagesReadStub = sandbox.stub();
pagesReadStub.withArgs({slug: pages[0].slug, include: 'author,authors,tags'})
.resolves({
pages: pages
});
sandbox.stub(api.v2, 'posts').get(() => {
return {
read: postsReadStub
};
});
sandbox.stub(api.v2, 'pages').get(() => {
return {
read: pagesReadStub
};
});
locals = {apiVersion: 'v2'};
});
it('ensure pages controller is triggered', function () {
const testUrl = 'http://127.0.0.1:2369' + pages[0].url;
return helpers.entryLookup(testUrl, routerOptions, locals).then(function (lookup) {
postsReadStub.called.should.be.false();
pagesReadStub.calledOnce.should.be.true();
should.exist(lookup.entry);
lookup.entry.should.have.property('url', pages[0].url);
lookup.isEditURL.should.be.false();
});
});
});
it('can lookup absolute url: /:slug/edit/', function (done) {
const testUrl = 'http://127.0.0.1:2369' + posts[0].url + 'edit/';
describe('posts', function () {
const routerOptions = {
permalinks: '/:slug/',
resourceType: 'posts'
};
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.true();
lookup.post.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.true();
done();
})
.catch(done);
});
let posts;
let postsReadStub;
let pagesReadStub;
it('can lookup relative url: /:slug/edit/', function (done) {
const testUrl = posts[0].url + 'edit/';
beforeEach(function () {
posts = [
testUtils.DataGenerator.forKnex.createPost({url: '/test/', slug: 'test'})
];
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.true();
lookup.post.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.true();
done();
})
.catch(done);
});
postsReadStub = sandbox.stub();
pagesReadStub = sandbox.stub();
it('cannot lookup absolute url: /:year/:month/:day/:slug/edit/', function (done) {
const testUrl = 'http://127.0.0.1:2369/2016/01/01' + posts[0].url + 'edit/';
postsReadStub.withArgs({slug: posts[0].slug, include: 'author,authors,tags'})
.resolves({
posts: posts
});
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
sandbox.stub(api.v2, 'posts').get(() => {
return {
read: postsReadStub
};
});
it('cannot lookup relative url: /:year/:month/:day/:slug/edit/', function (done) {
const testUrl = '/2016/01/01' + posts[0].url + 'edit/';
sandbox.stub(api.v2, 'pages').get(() => {
return {
read: pagesReadStub
};
});
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
locals = {apiVersion: 'v2'};
});
it('unknown url option', function (done) {
const testUrl = posts[0].url + 'not-edit/';
it('ensure posts controller is triggered', function () {
const testUrl = 'http://127.0.0.1:2369' + posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
return helpers.entryLookup(testUrl, routerOptions, locals).then(function (lookup) {
postsReadStub.calledOnce.should.be.true();
pagesReadStub.called.should.be.false();
should.exist(lookup.entry);
lookup.entry.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.false();
});
});
});
});
});