mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 03:44:29 +03:00
🐛 Fixed Tiers API erroring when invalid filter passed (#19845)
closes ENG-730 closes https://linear.app/tryghost/issue/ENG-730/ We've updated the input serializer to parse the filter, and responded with an error if it cannot be parsed correctly. Now that it's parsed, we can pass a mongo query object through the stack, which will lend itself to better typing for this code, which is a direction we want to go in anyway. We've had to update all the internal usages of the `browse` method to use mongo query objects.
This commit is contained in:
parent
36f11a65a0
commit
5a5ddcb609
@ -1,10 +1,26 @@
|
|||||||
|
const {BadRequestError} = require('@tryghost/errors');
|
||||||
const localUtils = require('../../index');
|
const localUtils = require('../../index');
|
||||||
|
const nql = require('@tryghost/nql-lang');
|
||||||
|
const tpl = require('@tryghost/tpl');
|
||||||
|
|
||||||
|
const messages = {
|
||||||
|
invalidNQLFilter: 'The NQL filter you passed was invalid.'
|
||||||
|
};
|
||||||
|
|
||||||
const forceActiveFilter = (frame) => {
|
const forceActiveFilter = (frame) => {
|
||||||
if (frame.options.filter) {
|
if (frame.options.filter) {
|
||||||
frame.options.filter = `(${frame.options.filter})+active:true`;
|
frame.options.filter = {
|
||||||
|
$and: [
|
||||||
|
{
|
||||||
|
active: true
|
||||||
|
},
|
||||||
|
frame.options.filter
|
||||||
|
]
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
frame.options.filter = 'active:true';
|
frame.options.filter = {
|
||||||
|
active: true
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -41,6 +57,18 @@ function convertTierInput(input) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
all(_apiConfig, frame) {
|
all(_apiConfig, frame) {
|
||||||
|
if (frame.options.filter) {
|
||||||
|
try {
|
||||||
|
frame.options.filter = nql.parse(frame.options.filter);
|
||||||
|
} catch (err) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: tpl(messages.invalidNQLFilter)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
frame.options.filter = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (localUtils.isContentAPI(frame)) {
|
if (localUtils.isContentAPI(frame)) {
|
||||||
// CASE: content api can only have active tiers
|
// CASE: content api can only have active tiers
|
||||||
forceActiveFilter(frame);
|
forceActiveFilter(frame);
|
||||||
|
@ -55,7 +55,9 @@ const initMembersCSVImporter = ({stripeAPIService}) => {
|
|||||||
},
|
},
|
||||||
getTierByName: async (name) => {
|
getTierByName: async (name) => {
|
||||||
const tiers = await tiersService.api.browse({
|
const tiers = await tiersService.api.browse({
|
||||||
filter: `name:'${name}'`
|
filter: {
|
||||||
|
name
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (tiers.data.length > 0) {
|
if (tiers.data.length > 0) {
|
||||||
|
@ -86,7 +86,8 @@ module.exports = class TierRepository {
|
|||||||
* @returns {Promise<import('@tryghost/tiers/lib/Tier')[]>}
|
* @returns {Promise<import('@tryghost/tiers/lib/Tier')[]>}
|
||||||
*/
|
*/
|
||||||
async getAll(options = {}) {
|
async getAll(options = {}) {
|
||||||
const filter = nql(options.filter, {});
|
const filter = nql();
|
||||||
|
filter.filter = options.filter || {};
|
||||||
return Promise.all(this.#store.slice().filter((item) => {
|
return Promise.all(this.#store.slice().filter((item) => {
|
||||||
return filter.queryJSON(this.toPrimitive(item));
|
return filter.queryJSON(this.toPrimitive(item));
|
||||||
}).map((tier) => {
|
}).map((tier) => {
|
||||||
|
@ -55,7 +55,8 @@ class InMemoryTierRepository {
|
|||||||
* @returns {Promise<Tier[]>}
|
* @returns {Promise<Tier[]>}
|
||||||
*/
|
*/
|
||||||
async getAll(options = {}) {
|
async getAll(options = {}) {
|
||||||
const filter = nql(options.filter, {});
|
const filter = nql();
|
||||||
|
filter.filter = options.filter || {};
|
||||||
return this.#store.slice().filter((item) => {
|
return this.#store.slice().filter((item) => {
|
||||||
return filter.queryJSON(this.toPrimitive(item));
|
return filter.queryJSON(this.toPrimitive(item));
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
const ObjectID = require('bson-objectid').default;
|
const ObjectID = require('bson-objectid').default;
|
||||||
const {BadRequestError} = require('@tryghost/errors');
|
const {BadRequestError, IncorrectUsageError} = require('@tryghost/errors');
|
||||||
const Tier = require('./Tier');
|
const Tier = require('./Tier');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,11 +42,16 @@ module.exports = class TiersAPI {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {object} [options]
|
* @param {object} [options]
|
||||||
* @param {string} [options.filter] - An NQL filter string
|
* @param {any} [options.filter] - A mongo query object
|
||||||
*
|
*
|
||||||
* @returns {Promise<Page<Tier>>}
|
* @returns {Promise<Page<Tier>>}
|
||||||
*/
|
*/
|
||||||
async browse(options = {}) {
|
async browse(options = {}) {
|
||||||
|
if (typeof options.filter === 'string') {
|
||||||
|
throw new IncorrectUsageError({
|
||||||
|
message: 'filter must be a mongo query object'
|
||||||
|
});
|
||||||
|
}
|
||||||
const tiers = await this.#repository.getAll(options);
|
const tiers = await this.#repository.getAll(options);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -83,7 +88,12 @@ module.exports = class TiersAPI {
|
|||||||
*/
|
*/
|
||||||
async readDefaultTier(options = {}) {
|
async readDefaultTier(options = {}) {
|
||||||
const [defaultTier] = await this.#repository.getAll({
|
const [defaultTier] = await this.#repository.getAll({
|
||||||
filter: 'type:paid+active:true',
|
filter: {
|
||||||
|
$and: [
|
||||||
|
{type: 'paid'},
|
||||||
|
{active: true}
|
||||||
|
]
|
||||||
|
},
|
||||||
limit: 1,
|
limit: 1,
|
||||||
...options
|
...options
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user