prisma-criteria
Version:
Parses, validates, and creates a criteria object that can be passed to the Prisma "findMany" method to query a list of resources matching the given filters, pagination and order.
80 lines • 4.19 kB
JavaScript
import { isValidationOk } from './shared/validation.util.js';
import { validateOrder } from './order-validation.js';
import { processUserInputFilters } from './filters/index.js';
import { stringToNumber } from './shared/string-to-number.util.js';
import { PRISMA_LOGIC_OPERATORS } from './filters/validation/consts.js';
import assert from 'assert';
export function createPrismaCriteria(userInputCriteria, criteriaOptions) {
const { filters: defaultFilters, orderBy: defaultOrderBy, pagination: defaultPagination } = criteriaOptions.defaults ?? {};
if (defaultPagination && defaultPagination.pageSize < 1)
throw new Error('The minimium page size is "1".');
if (defaultPagination && defaultPagination.pageNumber < 1)
throw new Error('The minimium page number is "1".');
if (criteriaOptions.rules.allowedFieldsToOrderBy.length > 1)
throw new Error('You can only order by one field (for now).');
const onlyStringUserInputs = Object.fromEntries(Object.entries(userInputCriteria)
.filter(entry => typeof entry[1] === 'string'));
const processedUserInputFilters = processUserInputFilters(onlyStringUserInputs.filters, criteriaOptions.rules.allowedFilters);
const userProvidedAndDefaultFilters = (Object.values(PRISMA_LOGIC_OPERATORS)).reduce((acc, logicOperator) => {
const isPresentInDefaultFilters = Object.hasOwn(defaultFilters ?? {}, logicOperator);
const isPresentInProcessedFilters = Object.hasOwn(processedUserInputFilters, logicOperator);
if (!isPresentInDefaultFilters && !isPresentInProcessedFilters)
return acc;
return {
...acc,
[logicOperator]: [
...(isPresentInDefaultFilters ? defaultFilters[logicOperator] : []),
...(isPresentInProcessedFilters ? processedUserInputFilters[logicOperator] : [])
]
};
}, {});
let orderBy = defaultOrderBy;
const validationOfUsersInputOrderBy = validateOrder(onlyStringUserInputs.orderBy, onlyStringUserInputs.orderDir, criteriaOptions.rules.allowedFieldsToOrderBy);
if (isValidationOk(validationOfUsersInputOrderBy))
orderBy = validationOfUsersInputOrderBy.ok.orderBy;
let take = defaultPagination?.pageSize;
const validationOfUsersInputPageSize = stringToNumber(onlyStringUserInputs.pageSize);
if (isValidationOk(validationOfUsersInputPageSize) &&
validationOfUsersInputPageSize.ok > 0) {
take = Math.min(validationOfUsersInputPageSize.ok, criteriaOptions.rules.pageSizeMax);
}
// Skip can be undefined, and take defined, but no the otherwise.
// Skip depends on "take", because:
// - If there is no "take" defined, it isn't possible to paginate.
// - If skip is 0, then doesn't matter if take is 20, page should stay 0.
let skip = defaultPagination && take
? (defaultPagination.pageNumber - 1) * take
: undefined;
const validationOfUsersInputPageNumber = stringToNumber(onlyStringUserInputs.pageNumber);
if (isValidationOk(validationOfUsersInputPageNumber) &&
validationOfUsersInputPageNumber.ok > 0 &&
take)
skip = (validationOfUsersInputPageNumber.ok - 1) * take;
const didUserProvideFilters = userInputCriteria.filters?.length === 0;
const thereAreValidFilters = Object
.values(userProvidedAndDefaultFilters)
.some((logicOperatorFilters) => logicOperatorFilters.length > 0);
const whereClause = {};
switch (true) {
// Doesn't return any match
case (didUserProvideFilters && !thereAreValidFilters):
whereClause.where = { OR: [] };
break;
// Ignores the where clause (as if it were not provided)
case (!didUserProvideFilters):
whereClause.where = { AND: [] };
break;
// return the matched places with the filters
case (thereAreValidFilters):
whereClause.where = userProvidedAndDefaultFilters;
break;
default: assert.fail('Shouln\'t reach this point');
}
return {
...whereClause,
orderBy,
skip,
take
};
}
//# sourceMappingURL=index.js.map