Skip to content

Commit

Permalink
Changed preview controller to support v0.1 and v2
Browse files Browse the repository at this point in the history
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
  • Loading branch information
kirrg001 committed Oct 18, 2018
1 parent dcf6c04 commit e302be2
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 97 deletions.
4 changes: 4 additions & 0 deletions core/server/api/v2/index.js
Expand Up @@ -65,5 +65,9 @@ module.exports = {

get users() {
return shared.pipeline(require('./users'), localUtils);
},

get preview() {
return shared.pipeline(require('./preview'), localUtils);
}
};
41 changes: 41 additions & 0 deletions 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;
});
}
}
};
4 changes: 4 additions & 0 deletions core/server/api/v2/utils/serializers/output/index.js
Expand Up @@ -53,5 +53,9 @@ module.exports = {

get users() {
return require('./users');
},

get preview() {
return require('./preview');
}
};
7 changes: 7 additions & 0 deletions 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)]
};
}
};
3 changes: 2 additions & 1 deletion core/server/services/routing/PreviewRouter.js
Expand Up @@ -19,7 +19,8 @@ class PreviewRouter extends ParentRouter {

_prepareContext(req, res, next) {
res.routerOptions = {
type: 'entry'
type: 'entry',
resourceType: 'preview'
};

next();
Expand Down
20 changes: 10 additions & 10 deletions core/server/services/routing/controllers/preview.js
Expand Up @@ -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();
Expand Down
251 changes: 165 additions & 86 deletions core/test/unit/services/routing/controllers/preview_spec.js
Expand Up @@ -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));
});
});
});

0 comments on commit e302be2

Please sign in to comment.