0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-06 22:40:14 -05:00

Changed preview controller to support v0.1 and v2

refs #9866

- invent preview api, but only used internally
  - the idea of a preview api is definitiely reaslistic and came up in the past a couple of times
- by that we don't have to differentiate between pages or posts controller
- still support v0.1
- preview controller is not registered for http, only internal handling
This commit is contained in:
kirrg001 2018-10-17 18:15:43 +02:00 committed by Katharina Irrgang
parent dcf6c0483c
commit e302be2749
7 changed files with 237 additions and 101 deletions

View file

@ -65,5 +65,9 @@ module.exports = {
get users() {
return shared.pipeline(require('./users'), localUtils);
},
get preview() {
return shared.pipeline(require('./preview'), localUtils);
}
};

View file

@ -0,0 +1,41 @@
const common = require('../../lib/common');
const models = require('../../models');
const ALLOWED_INCLUDES = ['author', 'authors', 'tags'];
module.exports = {
docName: 'preview',
read: {
permissions: true,
options: [
'include'
],
data: [
'uuid'
],
validation: {
options: {
include: {
values: ALLOWED_INCLUDES
}
},
data: {
uuid: {
required: true
}
}
},
query(frame) {
return models.Post.findOne(Object.assign({status: 'all'}, frame.data), frame.options)
.then((model) => {
if (!model) {
throw new common.errors.NotFoundError({
message: common.i18n.t('errors.api.posts.postNotFound')
});
}
return model;
});
}
}
};

View file

@ -53,5 +53,9 @@ module.exports = {
get users() {
return require('./users');
},
get preview() {
return require('./preview');
}
};

View file

@ -0,0 +1,7 @@
module.exports = {
all(model, apiConfig, frame) {
frame.response = {
preview: [model.toJSON(frame.options)]
};
}
};

View file

@ -19,7 +19,8 @@ class PreviewRouter extends ParentRouter {
_prepareContext(req, res, next) {
res.routerOptions = {
type: 'entry'
type: 'entry',
resourceType: 'preview'
};
next();

View file

@ -14,20 +14,20 @@ module.exports = function previewController(req, res, next) {
include: 'author,authors,tags'
};
let resourceType = res.routerOptions.resourceType;
/**
* @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.
* Furthermore, the preview router is used for pages and posts and we just receive a uuid. How to know
* which controller to call? pages or posts?
* Remove fallback to posts if we drop v0.1.
*/
api.posts.read(params)
if (!api[resourceType]) {
resourceType = 'posts';
}
api[resourceType]
.read(params)
.then(function then(result) {
const post = result.posts[0];
const post = result[resourceType][0];
if (!post) {
return next();

View file

@ -28,115 +28,194 @@ describe('Unit - services/routing/controllers/preview', function () {
configUtils.restore();
});
beforeEach(function () {
post = testUtils.DataGenerator.forKnex.createPost({status: 'draft'});
describe('v0.1', function () {
beforeEach(function () {
post = testUtils.DataGenerator.forKnex.createPost({status: 'draft'});
apiResponse = {
posts: [post]
};
apiResponse = {
posts: [post]
};
req = {
path: '/',
params: {
uuid: 'something'
},
route: {}
};
req = {
path: '/',
params: {
uuid: 'something'
},
route: {}
};
res = {
locals: {
apiVersion: 'v0.1'
},
render: sinon.spy(),
redirect: sinon.spy(),
set: sinon.spy()
};
res = {
routerOptions: {
resourceType: 'preview'
},
locals: {
apiVersion: 'v0.1'
},
render: sinon.spy(),
redirect: sinon.spy(),
set: sinon.spy()
};
secureStub = sandbox.stub();
renderStub = sandbox.stub();
secureStub = sandbox.stub();
renderStub = sandbox.stub();
sandbox.stub(urlService.utils, 'redirectToAdmin');
sandbox.stub(urlService.utils, 'redirect301');
sandbox.stub(urlService, 'getUrlByResourceId');
sandbox.stub(urlService.utils, 'redirectToAdmin');
sandbox.stub(urlService.utils, 'redirect301');
sandbox.stub(urlService, 'getUrlByResourceId');
sandbox.stub(helpers, 'secure').get(function () {
return secureStub;
sandbox.stub(helpers, 'secure').get(function () {
return secureStub;
});
sandbox.stub(helpers, 'renderEntry').get(function () {
return renderStub;
});
sandbox.stub(filters, 'doFilter');
sandbox.stub(api.posts, 'read').withArgs({
uuid: req.params.uuid,
status: 'all',
include: 'author,authors,tags'
}).callsFake(function () {
return Promise.resolve(apiResponse);
});
});
sandbox.stub(helpers, 'renderEntry').get(function () {
return renderStub;
it('should render post', function (done) {
filters.doFilter.withArgs('prePostsRender', post, res.locals).resolves();
helpers.renderEntry.callsFake(function () {
renderStub.called.should.be.true();
secureStub.called.should.be.true();
done();
});
controllers.preview(req, res, failTest(done));
});
sandbox.stub(filters, 'doFilter');
it('should call next if post is not found', function (done) {
apiResponse = {posts: []};
sandbox.stub(api.posts, 'read').withArgs({
uuid: req.params.uuid,
status: 'all',
include: 'author,authors,tags'
}).callsFake(function () {
return Promise.resolve(apiResponse);
controllers.preview(req, res, function (err) {
should.not.exist(err);
renderStub.called.should.be.false();
secureStub.called.should.be.false();
done();
});
});
it('should call redirect if post is published', function (done) {
post.status = 'published';
urlService.getUrlByResourceId.withArgs(post.id).returns('/something/');
urlService.utils.redirect301.callsFake(function (res, postUrl) {
postUrl.should.eql('/something/');
renderStub.called.should.be.false();
secureStub.called.should.be.false();
done();
});
controllers.preview(req, res, failTest(done));
});
it('should call redirect if /edit/ (options param) is detected', function (done) {
req.params.options = 'edit';
urlService.utils.redirectToAdmin.callsFake(function (statusCode, res, editorUrl) {
statusCode.should.eql(302);
editorUrl.should.eql(EDITOR_URL + post.id);
renderStub.called.should.be.false();
secureStub.called.should.be.false();
done();
});
controllers.preview(req, res, failTest(done));
});
it('should call next for unknown options param detected', function (done) {
req.params.options = 'abcde';
controllers.preview(req, res, function (err) {
should.not.exist(err);
renderStub.called.should.be.false();
secureStub.called.should.be.false();
done();
});
});
});
it('should render post', function (done) {
filters.doFilter.withArgs('prePostsRender', post, res.locals).resolves();
describe('v2', function () {
let previewStub;
helpers.renderEntry.callsFake(function () {
renderStub.called.should.be.true();
secureStub.called.should.be.true();
done();
beforeEach(function () {
post = testUtils.DataGenerator.forKnex.createPost({status: 'draft'});
apiResponse = {
preview: [post]
};
req = {
path: '/',
params: {
uuid: 'something'
},
route: {}
};
res = {
routerOptions: {
resourceType: 'preview'
},
locals: {
apiVersion: 'v2'
},
render: sinon.spy(),
redirect: sinon.spy(),
set: sinon.spy()
};
secureStub = sandbox.stub();
renderStub = sandbox.stub();
sandbox.stub(urlService.utils, 'redirectToAdmin');
sandbox.stub(urlService.utils, 'redirect301');
sandbox.stub(urlService, 'getUrlByResourceId');
sandbox.stub(helpers, 'secure').get(function () {
return secureStub;
});
sandbox.stub(helpers, 'renderEntry').get(function () {
return renderStub;
});
sandbox.stub(filters, 'doFilter');
previewStub = sandbox.stub();
previewStub.withArgs({
uuid: req.params.uuid,
status: 'all',
include: 'author,authors,tags'
}).resolves(apiResponse);
sandbox.stub(api.v2, 'preview').get(() => {
return {
read: previewStub
};
});
});
controllers.preview(req, res, failTest(done));
});
it('should render post', function (done) {
filters.doFilter.withArgs('prePostsRender', post, res.locals).resolves();
it('should call next if post is not found', function (done) {
apiResponse = {posts: []};
helpers.renderEntry.callsFake(function () {
renderStub.called.should.be.true();
secureStub.called.should.be.true();
done();
});
controllers.preview(req, res, function (err) {
should.not.exist(err);
renderStub.called.should.be.false();
secureStub.called.should.be.false();
done();
});
});
it('should call redirect if post is published', function (done) {
post.status = 'published';
urlService.getUrlByResourceId.withArgs(post.id).returns('/something/');
urlService.utils.redirect301.callsFake(function (res, postUrl) {
postUrl.should.eql('/something/');
renderStub.called.should.be.false();
secureStub.called.should.be.false();
done();
});
controllers.preview(req, res, failTest(done));
});
it('should call redirect if /edit/ (options param) is detected', function (done) {
req.params.options = 'edit';
urlService.utils.redirectToAdmin.callsFake(function (statusCode, res, editorUrl) {
statusCode.should.eql(302);
editorUrl.should.eql(EDITOR_URL + post.id);
renderStub.called.should.be.false();
secureStub.called.should.be.false();
done();
});
controllers.preview(req, res, failTest(done));
});
it('should call next for unknown options param detected', function (done) {
req.params.options = 'abcde';
controllers.preview(req, res, function (err) {
should.not.exist(err);
renderStub.called.should.be.false();
secureStub.called.should.be.false();
done();
controllers.preview(req, res, failTest(done));
});
});
});