mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-01 13:54:35 +03:00
🐛 Added error handling to prev/next post helpers
refs #8703 - On API error, call inverse with a data error, the same as the get helper
This commit is contained in:
parent
b6b2930d15
commit
c49dba12a0
@ -103,7 +103,7 @@ get = function get(resource, options) {
|
|||||||
apiMethod;
|
apiMethod;
|
||||||
|
|
||||||
if (!options.fn) {
|
if (!options.fn) {
|
||||||
data.error = i18n.t('warnings.helpers.get.mustBeCalledAsBlock');
|
data.error = i18n.t('warnings.helpers.get.mustBeCalledAsBlock', {helperName: 'get'});
|
||||||
logging.warn(data.error);
|
logging.warn(data.error);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
@ -136,6 +136,7 @@ get = function get(resource, options) {
|
|||||||
blockParams: blockParams
|
blockParams: blockParams
|
||||||
});
|
});
|
||||||
}).catch(function error(err) {
|
}).catch(function error(err) {
|
||||||
|
logging.error(err);
|
||||||
data.error = err.message;
|
data.error = err.message;
|
||||||
return options.inverse(self, {data: data});
|
return options.inverse(self, {data: data});
|
||||||
});
|
});
|
||||||
|
@ -6,23 +6,36 @@
|
|||||||
var proxy = require('./proxy'),
|
var proxy = require('./proxy'),
|
||||||
Promise = require('bluebird'),
|
Promise = require('bluebird'),
|
||||||
|
|
||||||
|
logging = proxy.logging,
|
||||||
|
i18n = proxy.i18n,
|
||||||
|
createFrame = proxy.hbs.handlebars.createFrame,
|
||||||
|
|
||||||
api = proxy.api,
|
api = proxy.api,
|
||||||
isPost = proxy.checks.isPost,
|
isPost = proxy.checks.isPost,
|
||||||
|
|
||||||
fetch;
|
fetch;
|
||||||
|
|
||||||
fetch = function fetch(apiOptions, options) {
|
fetch = function fetch(apiOptions, options, data) {
|
||||||
return api.posts.read(apiOptions).then(function (result) {
|
var self = this;
|
||||||
var related = result.posts[0];
|
|
||||||
|
|
||||||
if (related.previous) {
|
return api.posts
|
||||||
return options.fn(related.previous);
|
.read(apiOptions)
|
||||||
} else if (related.next) {
|
.then(function handleSuccess(result) {
|
||||||
return options.fn(related.next);
|
var related = result.posts[0];
|
||||||
} else {
|
|
||||||
return options.inverse(this);
|
if (related.previous) {
|
||||||
}
|
return options.fn(related.previous, {data: data});
|
||||||
});
|
} else if (related.next) {
|
||||||
|
return options.fn(related.next, {data: data});
|
||||||
|
} else {
|
||||||
|
return options.inverse(self, {data: data});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function handleError(err) {
|
||||||
|
logging.error(err);
|
||||||
|
data.error = err.message;
|
||||||
|
return options.inverse(self, {data: data});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// If prevNext method is called without valid post data then we must return a promise, if there is valid post data
|
// If prevNext method is called without valid post data then we must return a promise, if there is valid post data
|
||||||
@ -31,14 +44,21 @@ fetch = function fetch(apiOptions, options) {
|
|||||||
module.exports = function prevNext(options) {
|
module.exports = function prevNext(options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
var apiOptions = {
|
var data = createFrame(options.data),
|
||||||
include: options.name === 'prev_post' ? 'previous,previous.author,previous.tags' : 'next,next.author,next.tags'
|
apiOptions = {
|
||||||
};
|
include: options.name === 'prev_post' ? 'previous,previous.author,previous.tags' : 'next,next.author,next.tags'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!options.fn) {
|
||||||
|
data.error = i18n.t('warnings.helpers.mustBeCalledAsBlock', {helperName: options.name});
|
||||||
|
logging.warn(data.error);
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
if (isPost(this) && this.status === 'published') {
|
if (isPost(this) && this.status === 'published') {
|
||||||
apiOptions.slug = this.slug;
|
apiOptions.slug = this.slug;
|
||||||
return fetch(apiOptions, options);
|
return fetch(apiOptions, options, data);
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve(options.inverse(this));
|
return Promise.resolve(options.inverse(this, {data: data}));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -487,11 +487,11 @@
|
|||||||
"helperNotAvailable": "The \\{\\{{helperName}\\}\\} helper is not available.",
|
"helperNotAvailable": "The \\{\\{{helperName}\\}\\} helper is not available.",
|
||||||
"flagMustBeEnabled": "The {flagName} flag must be enabled in labs if you wish to use the \\{\\{{helperName}\\}\\} helper.",
|
"flagMustBeEnabled": "The {flagName} flag must be enabled in labs if you wish to use the \\{\\{{helperName}\\}\\} helper.",
|
||||||
"seeLink": "See {url}",
|
"seeLink": "See {url}",
|
||||||
|
"mustBeCalledAsBlock": "The \\{\\{{helperName}\\}\\} helper must be called as a block. E.g. \\{\\{#{helperName}\\}\\} \\{\\{/{helperName}\\}\\}",
|
||||||
"foreach": {
|
"foreach": {
|
||||||
"iteratorNeeded": "Need to pass an iterator to #foreach"
|
"iteratorNeeded": "Need to pass an iterator to #foreach"
|
||||||
},
|
},
|
||||||
"get": {
|
"get": {
|
||||||
"mustBeCalledAsBlock": "Get helper must be called as a block",
|
|
||||||
"invalidResource": "Invalid resource given to get helper"
|
"invalidResource": "Invalid resource given to get helper"
|
||||||
},
|
},
|
||||||
"has": {
|
"has": {
|
||||||
|
@ -5,6 +5,7 @@ var should = require('should'), // jshint ignore:line
|
|||||||
// Stuff we are testing
|
// Stuff we are testing
|
||||||
helpers = require('../../../server/helpers'),
|
helpers = require('../../../server/helpers'),
|
||||||
api = require('../../../server/api'),
|
api = require('../../../server/api'),
|
||||||
|
errors = require('../../../server/errors'),
|
||||||
|
|
||||||
sandbox = sinon.sandbox.create();
|
sandbox = sinon.sandbox.create();
|
||||||
|
|
||||||
@ -31,25 +32,29 @@ describe('{{next_post}} helper', function () {
|
|||||||
inverse = sinon.spy(),
|
inverse = sinon.spy(),
|
||||||
optionsData = {name: 'next_post', fn: fn, inverse: inverse};
|
optionsData = {name: 'next_post', fn: fn, inverse: inverse};
|
||||||
|
|
||||||
helpers.prev_post.call({
|
helpers.prev_post
|
||||||
html: 'content',
|
.call({
|
||||||
status: 'published',
|
html: 'content',
|
||||||
mobiledoc: markdownToMobiledoc('ff'),
|
status: 'published',
|
||||||
title: 'post2',
|
mobiledoc: markdownToMobiledoc('ff'),
|
||||||
slug: 'current',
|
title: 'post2',
|
||||||
created_at: new Date(0),
|
slug: 'current',
|
||||||
url: '/current/'
|
created_at: new Date(0),
|
||||||
}, optionsData).then(function () {
|
url: '/current/'
|
||||||
fn.calledOnce.should.be.true();
|
}, optionsData)
|
||||||
inverse.calledOnce.should.be.false();
|
.then(function () {
|
||||||
|
fn.calledOnce.should.be.true();
|
||||||
|
inverse.calledOnce.should.be.false();
|
||||||
|
|
||||||
readPostStub.calledOnce.should.be.true();
|
console.log(fn.firstCall.args);
|
||||||
readPostStub.firstCall.args[0].include.should.eql('next,next.author,next.tags');
|
fn.firstCall.args.should.have.lengthOf(2);
|
||||||
done();
|
fn.firstCall.args[0].should.have.properties('slug', 'title');
|
||||||
}).catch(function (err) {
|
fn.firstCall.args[1].should.be.an.Object().and.have.property('data');
|
||||||
console.log('err ', err);
|
readPostStub.calledOnce.should.be.true();
|
||||||
done(err);
|
readPostStub.firstCall.args[0].include.should.eql('next,next.author,next.tags');
|
||||||
});
|
done();
|
||||||
|
})
|
||||||
|
.catch(done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -67,20 +72,27 @@ describe('{{next_post}} helper', function () {
|
|||||||
inverse = sinon.spy(),
|
inverse = sinon.spy(),
|
||||||
optionsData = {name: 'next_post', fn: fn, inverse: inverse};
|
optionsData = {name: 'next_post', fn: fn, inverse: inverse};
|
||||||
|
|
||||||
helpers.prev_post.call({
|
helpers.prev_post
|
||||||
html: 'content',
|
.call({
|
||||||
mobiledoc: markdownToMobiledoc('ff'),
|
html: 'content',
|
||||||
title: 'post2',
|
mobiledoc: markdownToMobiledoc('ff'),
|
||||||
slug: 'current',
|
title: 'post2',
|
||||||
created_at: new Date(0),
|
slug: 'current',
|
||||||
url: '/current/'
|
created_at: new Date(0),
|
||||||
}, optionsData).then(function () {
|
url: '/current/'
|
||||||
fn.called.should.be.false();
|
}, optionsData)
|
||||||
inverse.called.should.be.true();
|
.then(function () {
|
||||||
done();
|
fn.called.should.be.false();
|
||||||
}).catch(function (err) {
|
inverse.called.should.be.true();
|
||||||
done(err);
|
|
||||||
});
|
console.log(inverse.firstCall.args);
|
||||||
|
inverse.firstCall.args.should.have.lengthOf(2);
|
||||||
|
inverse.firstCall.args[0].should.have.properties('slug', 'title');
|
||||||
|
inverse.firstCall.args[1].should.be.an.Object().and.have.property('data');
|
||||||
|
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -98,14 +110,15 @@ describe('{{next_post}} helper', function () {
|
|||||||
inverse = sinon.spy(),
|
inverse = sinon.spy(),
|
||||||
optionsData = {name: 'next_post', fn: fn, inverse: inverse};
|
optionsData = {name: 'next_post', fn: fn, inverse: inverse};
|
||||||
|
|
||||||
helpers.prev_post.call({}, optionsData).then(function () {
|
helpers.prev_post
|
||||||
fn.called.should.be.false();
|
.call({}, optionsData)
|
||||||
inverse.called.should.be.true();
|
.then(function () {
|
||||||
readPostStub.called.should.be.false();
|
fn.called.should.be.false();
|
||||||
done();
|
inverse.called.should.be.true();
|
||||||
}).catch(function (err) {
|
readPostStub.called.should.be.false();
|
||||||
done(err);
|
done();
|
||||||
});
|
})
|
||||||
|
.catch(done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -125,22 +138,77 @@ describe('{{next_post}} helper', function () {
|
|||||||
inverse = sinon.spy(),
|
inverse = sinon.spy(),
|
||||||
optionsData = {name: 'next_post', fn: fn, inverse: inverse};
|
optionsData = {name: 'next_post', fn: fn, inverse: inverse};
|
||||||
|
|
||||||
helpers.prev_post.call({
|
helpers.prev_post
|
||||||
html: 'content',
|
.call({
|
||||||
status: 'published',
|
html: 'content',
|
||||||
mobiledoc: markdownToMobiledoc('ff'),
|
status: 'draft',
|
||||||
title: 'post2',
|
mobiledoc: markdownToMobiledoc('ff'),
|
||||||
slug: 'current',
|
title: 'post2',
|
||||||
created_at: new Date(0),
|
slug: 'current',
|
||||||
url: '/current/'
|
created_at: new Date(0),
|
||||||
}, optionsData)
|
url: '/current/'
|
||||||
|
}, optionsData)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
fn.called.should.be.true();
|
fn.called.should.be.false();
|
||||||
inverse.called.should.be.false();
|
inverse.called.should.be.true();
|
||||||
done();
|
done();
|
||||||
}).catch(function (err) {
|
})
|
||||||
done(err);
|
.catch(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('general error handling', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
readPostStub = sandbox.stub(api.posts, 'read', function () {
|
||||||
|
return Promise.reject(new errors.NotFoundError({message: 'Something wasn\'t found'}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle error from the API', function (done) {
|
||||||
|
var fn = sinon.spy(),
|
||||||
|
inverse = sinon.spy(),
|
||||||
|
optionsData = {name: 'next_post', fn: fn, inverse: inverse};
|
||||||
|
|
||||||
|
helpers.prev_post
|
||||||
|
.call({
|
||||||
|
html: 'content',
|
||||||
|
status: 'published',
|
||||||
|
mobiledoc: markdownToMobiledoc('ff'),
|
||||||
|
title: 'post2',
|
||||||
|
slug: 'current',
|
||||||
|
created_at: new Date(0),
|
||||||
|
url: '/current/'
|
||||||
|
}, optionsData)
|
||||||
|
.then(function () {
|
||||||
|
fn.called.should.be.false();
|
||||||
|
inverse.calledOnce.should.be.true();
|
||||||
|
|
||||||
|
inverse.firstCall.args[1].should.be.an.Object().and.have.property('data');
|
||||||
|
inverse.firstCall.args[1].data.should.be.an.Object().and.have.property('error');
|
||||||
|
inverse.firstCall.args[1].data.error.should.match(/^Something wasn't found/);
|
||||||
|
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show warning for call without any options', function (done) {
|
||||||
|
var fn = sinon.spy(),
|
||||||
|
inverse = sinon.spy(),
|
||||||
|
optionsData = {name: 'next_post'};
|
||||||
|
|
||||||
|
helpers.prev_post
|
||||||
|
.call(
|
||||||
|
{},
|
||||||
|
optionsData
|
||||||
|
)
|
||||||
|
.then(function () {
|
||||||
|
fn.called.should.be.false();
|
||||||
|
inverse.called.should.be.false();
|
||||||
|
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(done);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user