mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 03:44:29 +03:00
Added "maxPeriodic" limit type
refs https://github.com/TryGhost/Team/issues/588 - This is a scaffolding for a new limit type which should allow to check limits based on periods (for example related to billing, subscription cycles)
This commit is contained in:
parent
84fa478e0f
commit
413549f9c0
5
ghost/limit-service/lib/date-utils.js
Normal file
5
ghost/limit-service/lib/date-utils.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const SUPPORTED_INTERVALS = ['month'];
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
SUPPORTED_INTERVALS
|
||||||
|
};
|
@ -1,5 +1,7 @@
|
|||||||
// run in context allows us to change the templateSettings without causing havoc
|
// run in context allows us to change the templateSettings without causing havoc
|
||||||
const _ = require('lodash').runInContext();
|
const _ = require('lodash').runInContext();
|
||||||
|
const {SUPPORTED_INTERVALS} = require('./date-utils');
|
||||||
|
|
||||||
_.templateSettings.interpolate = /{{([\s\S]+?)}}/g;
|
_.templateSettings.interpolate = /{{([\s\S]+?)}}/g;
|
||||||
|
|
||||||
class Limit {
|
class Limit {
|
||||||
@ -110,6 +112,51 @@ class MaxLimit extends Limit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MaxPeriodicLimit extends Limit {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Object} options
|
||||||
|
* @param {String} options.name - name of the limit
|
||||||
|
* @param {Object} options.config - limit configuration
|
||||||
|
* @param {Number} options.config.maxPeriodic - maximum limit the limit would check against
|
||||||
|
* @param {Function} options.config.currentCountQuery - query checking the state that would be compared against the limit
|
||||||
|
* @param {('month')} options.config.interval - an interval to take into account when checking the limit. Currently only supports 'month' value
|
||||||
|
* @param {String} options.config.startDate - start date in ISO 8601 format (https://en.wikipedia.org/wiki/ISO_8601), used to calculate period intervals
|
||||||
|
* @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, errors}) {
|
||||||
|
super({name, error: config.error || '', helpLink, db, errors});
|
||||||
|
|
||||||
|
if (config.maxPeriodic === undefined) {
|
||||||
|
throw new errors.IncorrectUsageError({message: 'Attempted to setup a periodic max limit without a limit'});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.currentCountQuery) {
|
||||||
|
throw new errors.IncorrectUsageError({message: 'Attempted to setup a periodic max limit without a current count query'});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.interval) {
|
||||||
|
throw new errors.IncorrectUsageError({message: 'Attempted to setup a periodic max limit without an interval'});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SUPPORTED_INTERVALS.includes(config.interval)) {
|
||||||
|
throw new errors.IncorrectUsageError({message: `Attempted to setup a periodic max limit without unsupported interval. Please specify one of: ${SUPPORTED_INTERVALS}`});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.startDate) {
|
||||||
|
throw new errors.IncorrectUsageError({message: 'Attempted to setup a periodic max limit without a start date'});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentCountQueryFn = config.currentCountQuery;
|
||||||
|
this.maxPeriodic = config.maxPeriodic;
|
||||||
|
this.interval = config.interval;
|
||||||
|
this.startDate = config.startDate;
|
||||||
|
this.fallbackMessage = `This action would exceed the ${_.lowerCase(this.name)} limit on your current plan.`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class FlagLimit extends Limit {
|
class FlagLimit extends Limit {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -203,6 +250,7 @@ class AllowlistLimit extends Limit {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
MaxLimit,
|
MaxLimit,
|
||||||
|
MaxPeriodicLimit,
|
||||||
FlagLimit,
|
FlagLimit,
|
||||||
AllowlistLimit
|
AllowlistLimit
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
require('./utils');
|
require('./utils');
|
||||||
|
|
||||||
const errors = require('./fixtures/errors');
|
const errors = require('./fixtures/errors');
|
||||||
const {MaxLimit, AllowlistLimit, FlagLimit} = require('../lib/limit');
|
const {MaxLimit, AllowlistLimit, FlagLimit, MaxPeriodicLimit} = require('../lib/limit');
|
||||||
|
|
||||||
describe('Limit Service', function () {
|
describe('Limit Service', function () {
|
||||||
describe('Flag Limit', function () {
|
describe('Flag Limit', function () {
|
||||||
@ -205,6 +205,93 @@ describe('Limit Service', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Periodic Max Limit', function () {
|
||||||
|
describe('Constructor', function () {
|
||||||
|
it('throws if initialized without a maxPeriodic limit', function () {
|
||||||
|
const config = {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const limit = new MaxPeriodicLimit({name: 'no limits!', config, errors});
|
||||||
|
should.fail(limit, 'Should have errored');
|
||||||
|
} catch (err) {
|
||||||
|
should.exist(err);
|
||||||
|
should.exist(err.errorType);
|
||||||
|
should.equal(err.errorType, 'IncorrectUsageError');
|
||||||
|
err.message.should.match(/periodic max limit without a limit/gi);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if initialized without a current count query', function () {
|
||||||
|
const config = {
|
||||||
|
maxPeriodic: 100
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const limit = new MaxPeriodicLimit({name: 'no accountability!', config, errors});
|
||||||
|
should.fail(limit, 'Should have errored');
|
||||||
|
} catch (err) {
|
||||||
|
should.exist(err);
|
||||||
|
should.exist(err.errorType);
|
||||||
|
should.equal(err.errorType, 'IncorrectUsageError');
|
||||||
|
err.message.should.match(/periodic max limit without a current count query/gi);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if initialized without interval', function () {
|
||||||
|
const config = {
|
||||||
|
maxPeriodic: 100,
|
||||||
|
currentCountQuery: () => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const limit = new MaxPeriodicLimit({name: 'no accountability!', config, errors});
|
||||||
|
should.fail(limit, 'Should have errored');
|
||||||
|
} catch (err) {
|
||||||
|
should.exist(err);
|
||||||
|
should.exist(err.errorType);
|
||||||
|
should.equal(err.errorType, 'IncorrectUsageError');
|
||||||
|
err.message.should.match(/periodic max limit without an interval/gi);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if initialized with unsupported interval', function () {
|
||||||
|
const config = {
|
||||||
|
maxPeriodic: 100,
|
||||||
|
currentCountQuery: () => {},
|
||||||
|
interval: 'week'
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const limit = new MaxPeriodicLimit({name: 'no accountability!', config, errors});
|
||||||
|
should.fail(limit, 'Should have errored');
|
||||||
|
} catch (err) {
|
||||||
|
should.exist(err);
|
||||||
|
should.exist(err.errorType);
|
||||||
|
should.equal(err.errorType, 'IncorrectUsageError');
|
||||||
|
err.message.should.match(/periodic max limit without unsupported interval. Please specify one of: month/gi);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if initialized without start date', function () {
|
||||||
|
const config = {
|
||||||
|
maxPeriodic: 100,
|
||||||
|
currentCountQuery: () => {},
|
||||||
|
interval: 'month'
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const limit = new MaxPeriodicLimit({name: 'no accountability!', config, errors});
|
||||||
|
should.fail(limit, 'Should have errored');
|
||||||
|
} catch (err) {
|
||||||
|
should.exist(err);
|
||||||
|
should.exist(err.errorType);
|
||||||
|
should.equal(err.errorType, 'IncorrectUsageError');
|
||||||
|
err.message.should.match(/periodic max limit without a start date/gi);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Allowlist limit', function () {
|
describe('Allowlist limit', function () {
|
||||||
it('rejects when the allowlist config isn\'t specified', async function () {
|
it('rejects when the allowlist config isn\'t specified', async function () {
|
||||||
try {
|
try {
|
||||||
|
Loading…
Reference in New Issue
Block a user