initial commit

This commit is contained in:
Ben Irvin 2023-05-05 13:11:59 +02:00
parent a7d616e158
commit 7747d1d3d1
2 changed files with 50 additions and 6 deletions

View File

@ -18,6 +18,7 @@ const {
cloneDeep,
get,
mergeAll,
isString,
} = require('lodash/fp');
const _ = require('lodash');
const parseType = require('./parse-type');
@ -46,7 +47,7 @@ class InvalidSortError extends Error {
}
const validateOrder = (order) => {
if (!['asc', 'desc'].includes(order.toLocaleLowerCase())) {
if (!isString(order) || !['asc', 'desc'].includes(order.toLocaleLowerCase())) {
throw new InvalidOrderError();
}
};
@ -80,6 +81,13 @@ const convertSortQueryParams = (sortQuery) => {
};
const convertSingleSortQueryParam = (sortQuery) => {
if (!sortQuery) {
return {};
}
if (!isString(sortQuery)) {
throw new Error('Invalid sort query');
}
// split field and order param with default order to ascending
const [field, order = 'asc'] = sortQuery.split(':');
@ -89,6 +97,8 @@ const convertSingleSortQueryParam = (sortQuery) => {
validateOrder(order);
// TODO: field should be a valid path on an object model
return _.set({}, field, order);
};
@ -368,6 +378,7 @@ const convertNestedPopulate = (subPopulate, schema) => {
return query;
};
// TODO: ensure field is valid in content types (will probably have to check strapi.contentTypes since it can be a string.path)
const convertFieldsQueryParams = (fields, depth = 0) => {
if (depth === 0 && fields === '*') {
return undefined;
@ -387,6 +398,33 @@ const convertFieldsQueryParams = (fields, depth = 0) => {
throw new Error('Invalid fields parameter. Expected a string or an array of strings');
};
// TODO: this is temporary as a POC, get operators from @strapi/database (will likely require refactoring)
const isOperator = (key) => {
return key.startsWith('$');
};
const isValidSchemaAttribute = (key, schema) => {
if (key.trim().toLowerCase() === 'id') {
return true;
}
if (!schema) {
return false;
}
// case insensitive check if the attribute exists in the schema
const findAttribute = key.toLowerCase();
if (
Object.keys(schema.attributes)
.map((attributeName) => attributeName.toLowerCase())
.includes(findAttribute)
) {
return true;
}
return false;
};
const convertFiltersQueryParams = (filters, schema) => {
// Filters need to be either an array or an object
// Here we're only checking for 'object' type since typeof [] => object and typeof {} => object
@ -401,10 +439,6 @@ const convertFiltersQueryParams = (filters, schema) => {
};
const convertAndSanitizeFilters = (filters, schema) => {
if (!isPlainObject(filters)) {
return filters;
}
if (Array.isArray(filters)) {
return (
filters
@ -415,14 +449,23 @@ const convertAndSanitizeFilters = (filters, schema) => {
);
}
// This must come after check for Array or else arrays are not filtered
if (!isPlainObject(filters)) {
return filters;
}
const removeOperator = (operator) => delete filters[operator];
// Here, `key` can either be an operator or an attribute name
for (const [key, value] of Object.entries(filters)) {
const attribute = get(key, schema?.attributes);
const validKey = isOperator(key) || isValidSchemaAttribute(key, schema);
if (!validKey) {
removeOperator(key);
}
// Handle attributes
if (attribute) {
else if (attribute) {
// Relations
if (attribute.type === 'relation') {
filters[key] = convertAndSanitizeFilters(value, strapi.getModel(attribute.target));

View File

@ -35,6 +35,7 @@ const defaultSanitizeOutput = async (schema, entity) => {
);
};
// TODO: remove fields that don't exist on the schema
const defaultSanitizeFilters = curry((schema, filters) => {
return pipeAsync(
// Remove dynamic zones from filters