strapi/packages/strapi-utils/lib/convert-rest-query-params.js

245 lines
5.9 KiB
JavaScript
Raw Normal View History

'use strict';
2019-03-13 19:27:18 +01:00
/**
* Converts the standard Strapi REST query params to a moe usable format for querying
* You can read more here: https://strapi.io/documentation/developer-docs/latest/developer-resources/content-api/content-api.html#filters
2019-03-13 19:27:18 +01:00
*/
const _ = require('lodash');
const {
constants: { DP_PUB_STATES },
} = require('./content-types');
2019-03-13 19:27:18 +01:00
const BOOLEAN_OPERATORS = ['or'];
const QUERY_OPERATORS = ['_where', '_or'];
2019-03-13 19:27:18 +01:00
/**
* Global converter
2019-03-13 19:27:18 +01:00
* @param {Object} params
* @param defaults
2019-03-13 19:27:18 +01:00
*/
const convertRestQueryParams = (params = {}, defaults = {}) => {
2019-03-13 19:27:18 +01:00
if (typeof params !== 'object' || params === null) {
throw new Error(
`convertRestQueryParams expected an object got ${params === null ? 'null' : typeof params}`
2019-03-13 19:27:18 +01:00
);
}
let finalParams = Object.assign({ start: 0, limit: 100 }, defaults);
2019-03-13 19:27:18 +01:00
if (Object.keys(params).length === 0) {
return finalParams;
}
if (_.has(params, '_sort')) {
2019-03-29 16:53:25 +01:00
Object.assign(finalParams, convertSortQueryParams(params._sort));
2019-03-13 19:27:18 +01:00
}
if (_.has(params, '_start')) {
2019-03-29 16:53:25 +01:00
Object.assign(finalParams, convertStartQueryParams(params._start));
2019-03-13 19:27:18 +01:00
}
if (_.has(params, '_limit')) {
2019-03-29 16:53:25 +01:00
Object.assign(finalParams, convertLimitQueryParams(params._limit));
2019-03-13 19:27:18 +01:00
}
if (_.has(params, '_publicationState')) {
Object.assign(finalParams, convertPublicationStateParams(params._publicationState));
}
2021-04-08 14:13:39 +02:00
const whereParams = convertExtraRootParams(
_.omit(params, ['_sort', '_start', '_limit', '_where', '_publicationState'])
);
const whereClauses = [];
2019-03-13 19:27:18 +01:00
if (_.keys(whereParams).length > 0) {
whereClauses.push(...convertWhereParams(whereParams));
}
if (_.has(params, '_where')) {
whereClauses.push(...convertWhereParams(params._where));
}
Object.assign(finalParams, { where: whereClauses });
2019-03-13 19:27:18 +01:00
return finalParams;
};
2021-04-08 14:13:39 +02:00
/**
* Convert params prefixed with _ by removing the prefix after we have handle the internal params
* NOTE: This is only a temporary patch for v3 to handle extra params coming from plugins
* @param {object} params
* @returns {object}
*/
const convertExtraRootParams = params => {
return Object.entries(params).reduce((acc, [key, value]) => {
2021-04-08 14:47:05 +02:00
if (_.startsWith(key, '_') && !QUERY_OPERATORS.includes(key)) {
2021-04-08 14:13:39 +02:00
acc[key.slice(1)] = value;
} else {
acc[key] = value;
}
return acc;
}, {});
};
2019-03-13 19:27:18 +01:00
/**
* Sort query parser
* @param {string} sortQuery - ex: id:asc,price:desc
*/
const convertSortQueryParams = sortQuery => {
if (typeof sortQuery !== 'string') {
throw new Error(`convertSortQueryParams expected a string, got ${typeof sortQuery}`);
2019-03-13 19:27:18 +01:00
}
const sortKeys = [];
sortQuery.split(',').forEach(part => {
// split field and order param with default order to ascending
const [field, order = 'asc'] = part.split(':');
if (field.length === 0) {
throw new Error('Field cannot be empty');
}
if (!['asc', 'desc'].includes(order.toLocaleLowerCase())) {
throw new Error('order can only be one of asc|desc|ASC|DESC');
}
sortKeys.push({ field, order: order.toLowerCase() });
});
return {
sort: sortKeys,
};
};
/**
* Start query parser
* @param {string} startQuery - ex: id:asc,price:desc
*/
const convertStartQueryParams = startQuery => {
const startAsANumber = _.toNumber(startQuery);
if (!_.isInteger(startAsANumber) || startAsANumber < 0) {
throw new Error(`convertStartQueryParams expected a positive integer got ${startAsANumber}`);
2019-03-13 19:27:18 +01:00
}
return {
start: startAsANumber,
};
};
/**
* Limit query parser
* @param {string} limitQuery - ex: id:asc,price:desc
*/
const convertLimitQueryParams = limitQuery => {
const limitAsANumber = _.toNumber(limitQuery);
if (!_.isInteger(limitAsANumber) || (limitAsANumber !== -1 && limitAsANumber < 0)) {
throw new Error(`convertLimitQueryParams expected a positive integer got ${limitAsANumber}`);
2019-03-13 19:27:18 +01:00
}
return {
limit: limitAsANumber,
};
};
/**
* publicationState query parser
* @param {string} publicationState - eg: 'live', 'preview'
*/
const convertPublicationStateParams = publicationState => {
if (!DP_PUB_STATES.includes(publicationState)) {
throw new Error(
`convertPublicationStateParams expected a value from: ${DP_PUB_STATES.join(
', '
)}. Got ${publicationState} instead`
);
}
return { publicationState };
};
2019-03-13 19:27:18 +01:00
// List of all the possible filters
const VALID_REST_OPERATORS = [
2019-03-13 19:27:18 +01:00
'eq',
'ne',
'in',
'nin',
'contains',
'ncontains',
'containss',
'ncontainss',
'lt',
'lte',
'gt',
'gte',
2019-07-04 19:10:17 -03:00
'null',
2019-03-13 19:27:18 +01:00
];
/**
* Parse where params
*/
const convertWhereParams = whereParams => {
let finalWhere = [];
if (Array.isArray(whereParams)) {
return whereParams.reduce((acc, whereParam) => {
return acc.concat(convertWhereParams(whereParam));
}, []);
}
2019-03-13 19:27:18 +01:00
Object.keys(whereParams).forEach(whereClause => {
const { field, operator = 'eq', value } = convertWhereClause(
whereClause,
whereParams[whereClause]
);
finalWhere.push({
field,
operator,
value,
});
});
return finalWhere;
};
/**
* Parse single where param
* @param {string} whereClause - Any possible where clause e.g: id_ne text_ncontains
* @param {string} value - the value of the where clause e.g id_ne=value
*/
const convertWhereClause = (whereClause, value) => {
const separatorIndex = whereClause.lastIndexOf('_');
// eq operator
if (separatorIndex === -1) {
return { field: whereClause, value };
}
// split field and operator
const field = whereClause.substring(0, separatorIndex);
const operator = whereClause.slice(separatorIndex + 1);
if (BOOLEAN_OPERATORS.includes(operator) && field === '') {
return { field: null, operator, value: [].concat(value).map(convertWhereParams) };
}
2019-03-13 19:27:18 +01:00
// the field as underscores
if (!VALID_REST_OPERATORS.includes(operator)) {
2019-03-13 19:27:18 +01:00
return { field: whereClause, value };
}
return { field, operator, value };
};
module.exports = {
convertRestQueryParams,
VALID_REST_OPERATORS,
QUERY_OPERATORS,
};