diff --git a/core/server/api/v2/index.js b/core/server/api/v2/index.js index 46d2f0705619..34cc997db0e6 100644 --- a/core/server/api/v2/index.js +++ b/core/server/api/v2/index.js @@ -65,5 +65,9 @@ module.exports = { get users() { return shared.pipeline(require('./users'), localUtils); + }, + + get preview() { + return shared.pipeline(require('./preview'), localUtils); } }; diff --git a/core/server/api/v2/preview.js b/core/server/api/v2/preview.js new file mode 100644 index 000000000000..2d653c8b908b --- /dev/null +++ b/core/server/api/v2/preview.js @@ -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; + }); + } + } +}; diff --git a/core/server/api/v2/utils/serializers/output/index.js b/core/server/api/v2/utils/serializers/output/index.js index 7c62ad2272d9..b5f9310af916 100644 --- a/core/server/api/v2/utils/serializers/output/index.js +++ b/core/server/api/v2/utils/serializers/output/index.js @@ -53,5 +53,9 @@ module.exports = { get users() { return require('./users'); + }, + + get preview() { + return require('./preview'); } }; diff --git a/core/server/api/v2/utils/serializers/output/preview.js b/core/server/api/v2/utils/serializers/output/preview.js new file mode 100644 index 000000000000..5339284b6371 --- /dev/null +++ b/core/server/api/v2/utils/serializers/output/preview.js @@ -0,0 +1,7 @@ +module.exports = { + all(model, apiConfig, frame) { + frame.response = { + preview: [model.toJSON(frame.options)] + }; + } +}; diff --git a/core/server/services/routing/PreviewRouter.js b/core/server/services/routing/PreviewRouter.js index 988e58ccce2d..6d3c38e1bd20 100644 --- a/core/server/services/routing/PreviewRouter.js +++ b/core/server/services/routing/PreviewRouter.js @@ -19,7 +19,8 @@ class PreviewRouter extends ParentRouter { _prepareContext(req, res, next) { res.routerOptions = { - type: 'entry' + type: 'entry', + resourceType: 'preview' }; next(); diff --git a/core/server/services/routing/controllers/preview.js b/core/server/services/routing/controllers/preview.js index 3d42af6885bd..98daee1b65b5 100644 --- a/core/server/services/routing/controllers/preview.js +++ b/core/server/services/routing/controllers/preview.js @@ -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(); diff --git a/core/test/unit/services/routing/controllers/preview_spec.js b/core/test/unit/services/routing/controllers/preview_spec.js index c79c8df29b95..7ba6315a8d6e 100644 --- a/core/test/unit/services/routing/controllers/preview_spec.js +++ b/core/test/unit/services/routing/controllers/preview_spec.js @@ -28,115 +28,194 @@ describe('Unit - services/routing/controllers/preview', function () { configUtils.restore(); }); - beforeEach(function () { - post = testUtils.DataGenerator.forKnex.createPost({status: 'draft'}); - - apiResponse = { - posts: [post] - }; - - req = { - path: '/', - params: { - uuid: 'something' - }, - route: {} - }; - - res = { - locals: { - apiVersion: 'v0.1' - }, - render: sinon.spy(), - redirect: sinon.spy(), - set: sinon.spy() - }; - - secureStub = sandbox.stub(); - renderStub = sandbox.stub(); + describe('v0.1', function () { + beforeEach(function () { + post = testUtils.DataGenerator.forKnex.createPost({status: 'draft'}); + + apiResponse = { + posts: [post] + }; + + req = { + path: '/', + params: { + uuid: 'something' + }, + route: {} + }; + + res = { + routerOptions: { + resourceType: 'preview' + }, + locals: { + apiVersion: 'v0.1' + }, + 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'); + + sandbox.stub(api.posts, 'read').withArgs({ + uuid: req.params.uuid, + status: 'all', + include: 'author,authors,tags' + }).callsFake(function () { + return Promise.resolve(apiResponse); + }); + }); - sandbox.stub(urlService.utils, 'redirectToAdmin'); - sandbox.stub(urlService.utils, 'redirect301'); - sandbox.stub(urlService, 'getUrlByResourceId'); + it('should render post', function (done) { + filters.doFilter.withArgs('prePostsRender', post, res.locals).resolves(); - sandbox.stub(helpers, 'secure').get(function () { - return secureStub; - }); + helpers.renderEntry.callsFake(function () { + renderStub.called.should.be.true(); + secureStub.called.should.be.true(); + done(); + }); - sandbox.stub(helpers, 'renderEntry').get(function () { - return renderStub; + 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 render post', function (done) { - filters.doFilter.withArgs('prePostsRender', post, res.locals).resolves(); + it('should call redirect if post is published', function (done) { + post.status = 'published'; + urlService.getUrlByResourceId.withArgs(post.id).returns('/something/'); - helpers.renderEntry.callsFake(function () { - renderStub.called.should.be.true(); - secureStub.called.should.be.true(); - done(); + 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)); }); - controllers.preview(req, res, failTest(done)); - }); + it('should call redirect if /edit/ (options param) is detected', function (done) { + req.params.options = 'edit'; - it('should call next if post is not found', function (done) { - apiResponse = {posts: []}; + 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, function (err) { - should.not.exist(err); - renderStub.called.should.be.false(); - secureStub.called.should.be.false(); - done(); + controllers.preview(req, res, failTest(done)); }); - }); - it('should call redirect if post is published', function (done) { - post.status = 'published'; - urlService.getUrlByResourceId.withArgs(post.id).returns('/something/'); + it('should call next for unknown options param detected', function (done) { + req.params.options = 'abcde'; - 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, function (err) { + should.not.exist(err); + 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(); + describe('v2', function () { + let previewStub; + + 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 for unknown options param detected', function (done) { - req.params.options = 'abcde'; + 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(); + controllers.preview(req, res, failTest(done)); }); }); });