diff --git a/ghost/limit-service/lib/limit-service.js b/ghost/limit-service/lib/limit-service.js index 7b9d2d4842..f56b274f74 100644 --- a/ghost/limit-service/lib/limit-service.js +++ b/ghost/limit-service/lib/limit-service.js @@ -1,4 +1,3 @@ -const errors = require('@tryghost/errors'); const {MaxLimit, FlagLimit} = require('./limit'); const config = require('./config'); const _ = require('lodash'); @@ -15,8 +14,15 @@ class LimitService { * @param {Object} options.limits - hash containing limit configurations keyed by limit name and containing * @param {String} options.helpLink - URL pointing to help resources for when limit is reached * @param {Object} options.db - knex db connection instance or other data source for the limit checks + * @param {Object} options.errors - instance of errors compatible with Ghost-Ignition's errors (https://github.com/TryGhost/Ignition#errors) */ - loadLimits({limits, helpLink, db}) { + loadLimits({limits, helpLink, db, errors}) { + if (!errors) { + throw new Error(`Config Missing: 'errors' is required. `); + } + + this.errors = errors; + Object.keys(limits).forEach((name) => { name = _.camelCase(name); @@ -25,9 +31,9 @@ class LimitService { let limitConfig = _.merge({}, limits[name], config[name]); if (_.has(limitConfig, 'max')) { - this.limits[name] = new MaxLimit({name: name, config: limitConfig, helpLink, db}); + this.limits[name] = new MaxLimit({name: name, config: limitConfig, helpLink, db, errors}); } else { - this.limits[name] = new FlagLimit({name: name, config: limitConfig, helpLink}); + this.limits[name] = new FlagLimit({name: name, config: limitConfig, helpLink, errors}); } } }); @@ -46,7 +52,7 @@ class LimitService { await this.limits[limitName].errorIfIsOverLimit(); return false; } catch (error) { - if (error instanceof errors.HostLimitError) { + if (error instanceof this.errors.HostLimitError) { return true; } } @@ -61,7 +67,7 @@ class LimitService { await this.limits[limitName].errorIfWouldGoOverLimit(); return false; } catch (error) { - if (error instanceof errors.HostLimitError) { + if (error instanceof this.errors.HostLimitError) { return true; } } diff --git a/ghost/limit-service/lib/limit.js b/ghost/limit-service/lib/limit.js index aa555e2bc3..034a373bd1 100644 --- a/ghost/limit-service/lib/limit.js +++ b/ghost/limit-service/lib/limit.js @@ -1,15 +1,14 @@ -const errors = require('@tryghost/errors'); - // run in context allows us to change the templateSettings without causing havoc const _ = require('lodash').runInContext(); _.templateSettings.interpolate = /{{([\s\S]+?)}}/g; class Limit { - constructor({name, error, helpLink, db}) { + constructor({name, error, helpLink, db, errors}) { this.name = name; this.error = error; this.helpLink = helpLink; this.db = db; + this.errors = errors; } generateError() { @@ -37,9 +36,10 @@ class MaxLimit extends Limit { * @param {Function} options.config.currentCountQuery - query checking the state that would be compared against the limit * @param {String} options.helpLink - URL to the resource explaining how the limit works * @param {Object} options.db - instance of knex db connection that currentCountQuery can use to run state check through + * @param {Object} options.errors - instance of errors compatible with Ghost-Ignition's errors (https://github.com/TryGhost/Ignition#errors) */ - constructor({name, config, helpLink, db}) { - super({name, error: config.error || '', helpLink, db}); + constructor({name, config, helpLink, db, errors}) { + super({name, error: config.error || '', helpLink, db, errors}); if (config.max === undefined) { throw new errors.IncorrectUsageError('Attempted to setup a max limit without a limit'); @@ -74,7 +74,7 @@ class MaxLimit extends Limit { errorObj.errorDetails.limit = this.max; errorObj.errorDetails.total = count; - return new errors.HostLimitError(errorObj); + return new this.errors.HostLimitError(errorObj); } async currentCountQuery() { @@ -119,9 +119,10 @@ class FlagLimit extends Limit { * @param {Number} options.config.disabled - disabled/enabled flag for the limit * @param {String} options.helpLink - URL to the resource explaining how the limit works * @param {Object} options.db - instance of knex db connection that currentCountQuery can use to run state check through + * @param {Object} options.errors - instance of errors compatible with Ghost-Ignition's errors (https://github.com/TryGhost/Ignition#errors) */ - constructor({name, config, helpLink, db}) { - super({name, error: config.error || '', helpLink, db}); + constructor({name, config, helpLink, db, errors}) { + super({name, error: config.error || '', helpLink, db, errors}); this.disabled = config.disabled; this.fallbackMessage = `Your plan does not support ${_.lowerCase(this.name)}. Please upgrade to enable ${_.lowerCase(this.name)}.`; @@ -136,7 +137,7 @@ class FlagLimit extends Limit { errorObj.message = this.fallbackMessage; } - return new errors.HostLimitError(errorObj); + return new this.errors.HostLimitError(errorObj); } /** diff --git a/ghost/limit-service/test/fixtures/errors.js b/ghost/limit-service/test/fixtures/errors.js new file mode 100644 index 0000000000..5968fa0353 --- /dev/null +++ b/ghost/limit-service/test/fixtures/errors.js @@ -0,0 +1,25 @@ +class Error { + constructor({errorType, errorDetails, message}) { + this.errorType = errorType; + this.errorDetails = errorDetails; + this.message = message; + } +} + +class IncorrectUsageError extends Error { + constructor(options) { + super(Object.assign({errorType: 'IncorrectUsageError'}, options)); + } +} + +class HostLimitError extends Error { + constructor(options) { + super(Object.assign({errorType: 'HostLimitError'}, options)); + } +} + +// NOTE: this module is here to serve as a dummy fixture for Ghost-Ignition's errors (https://github.com/TryGhost/Ignition#errors) +module.exports = { + IncorrectUsageError, + HostLimitError +}; diff --git a/ghost/limit-service/test/limit-service.test.js b/ghost/limit-service/test/limit-service.test.js index 8b59e3d8ed..8db30c3fff 100644 --- a/ghost/limit-service/test/limit-service.test.js +++ b/ghost/limit-service/test/limit-service.test.js @@ -5,6 +5,8 @@ require('./utils'); const LimitService = require('../lib/limit-service'); const {MaxLimit, FlagLimit} = require('../lib/limit'); +const errors = require('./fixtures/errors'); + describe('Limit Service', function () { describe('Lodash Template', function () { it('Does not get clobbered by this lib', function () { @@ -17,7 +19,15 @@ describe('Limit Service', function () { describe('Error Messages', function () { it('Formats numbers correctly', function () { - let limit = new MaxLimit({name: 'test', config: {max: 35000000, currentCountQuery: () => {}, error: 'Your plan supports up to {{max}} staff users. Please upgrade to add more.'}}); + let limit = new MaxLimit({ + name: 'test', + config: { + max: 35000000, + currentCountQuery: () => {}, + error: 'Your plan supports up to {{max}} staff users. Please upgrade to add more.' + }, + errors + }); let error = limit.generateError(35000001); @@ -28,12 +38,26 @@ describe('Limit Service', function () { }); describe('Loader', function () { + it('throws if errors configuration is not specified', function () { + const limitService = new LimitService(); + + let limits = {staff: {max: 2}}; + + try { + limitService.loadLimits({limits}); + should.fail(limitService, 'Should have errored'); + } catch (err) { + should.exist(err); + err.message.should.equal(`Config Missing: 'errors' is required`); + } + }); + it('can load a basic limit', function () { const limitService = new LimitService(); let limits = {staff: {max: 2}}; - limitService.loadLimits({limits}); + limitService.loadLimits({limits, errors}); limitService.limits.should.be.an.Object().with.properties(['staff']); limitService.limits.staff.should.be.an.instanceOf(MaxLimit); @@ -46,7 +70,7 @@ describe('Limit Service', function () { let limits = {staff: {max: 2}, members: {max: 100}}; - limitService.loadLimits({limits}); + limitService.loadLimits({limits, errors}); limitService.limits.should.be.an.Object().with.properties(['staff', 'members']); limitService.limits.staff.should.be.an.instanceOf(MaxLimit); @@ -60,7 +84,7 @@ describe('Limit Service', function () { let limits = {customThemes: {disabled: true}}; - limitService.loadLimits({limits}); + limitService.loadLimits({limits, errors}); limitService.limits.should.be.an.Object().with.properties(['customThemes']); limitService.limits.customThemes.should.be.an.instanceOf(FlagLimit); @@ -75,7 +99,7 @@ describe('Limit Service', function () { let limits = {custom_themes: {disabled: true}}; - limitService.loadLimits({limits}); + limitService.loadLimits({limits, errors}); limitService.limits.should.be.an.Object().with.properties(['customThemes']); limitService.limits.customThemes.should.be.an.instanceOf(FlagLimit); diff --git a/ghost/limit-service/test/limit.test.js b/ghost/limit-service/test/limit.test.js index ed6493d82b..9e5629d15f 100644 --- a/ghost/limit-service/test/limit.test.js +++ b/ghost/limit-service/test/limit.test.js @@ -2,6 +2,7 @@ // const testUtils = require('./utils'); require('./utils'); +const errors = require('./fixtures/errors'); const {MaxLimit} = require('../lib/limit'); describe('Limit Service', function () { @@ -11,7 +12,7 @@ describe('Limit Service', function () { const config = {}; try { - const limit = new MaxLimit({name: 'no limits!', config}); + const limit = new MaxLimit({name: 'no limits!', config, errors}); should.fail(limit, 'Should have errored'); } catch (err) { should.exist(err); @@ -24,7 +25,7 @@ describe('Limit Service', function () { const config = {}; try { - const limit = new MaxLimit({name: 'no accountability!', config}); + const limit = new MaxLimit({name: 'no accountability!', config, errors}); should.fail(limit, 'Should have errored'); } catch (err) { should.exist(err); @@ -40,7 +41,7 @@ describe('Limit Service', function () { max: 3, currentCountQuery: () => 42 }; - const limit = new MaxLimit({name: 'maxy', config}); + const limit = new MaxLimit({name: 'maxy', config, errors}); try { await limit.errorIfIsOverLimit(); @@ -65,7 +66,7 @@ describe('Limit Service', function () { currentCountQuery: () => 1 }; - const limit = new MaxLimit({name: 'maxy', config}); + const limit = new MaxLimit({name: 'maxy', config, errors}); await limit.errorIfIsOverLimit(); }); @@ -76,7 +77,7 @@ describe('Limit Service', function () { currentCountQuery: () => 10 }; - const limit = new MaxLimit({name: 'maxy', config}); + const limit = new MaxLimit({name: 'maxy', config, errors}); // should pass as the limit is exactly on the limit 10 >= 10 await limit.errorIfIsOverLimit({max: 10}); @@ -106,7 +107,7 @@ describe('Limit Service', function () { max: 1, currentCountQuery: () => 1 }; - const limit = new MaxLimit({name: 'maxy', config}); + const limit = new MaxLimit({name: 'maxy', config, errors}); try { await limit.errorIfWouldGoOverLimit(); @@ -131,7 +132,7 @@ describe('Limit Service', function () { currentCountQuery: () => 1 }; - const limit = new MaxLimit({name: 'maxy', config}); + const limit = new MaxLimit({name: 'maxy', config, errors}); await limit.errorIfWouldGoOverLimit(); }); @@ -142,7 +143,7 @@ describe('Limit Service', function () { currentCountQuery: () => 10 }; - const limit = new MaxLimit({name: 'maxy', config}); + const limit = new MaxLimit({name: 'maxy', config, errors}); // should pass as the limit is overridden to 10 + 1 = 11 await limit.errorIfWouldGoOverLimit({max: 11});