@elsikora/nestjs-crud-automator
Version:
A library for automating the creation of CRUD operations in NestJS.
147 lines (144 loc) • 8.62 kB
JavaScript
import { __decorate } from '../../external/tslib/tslib.es6.js';
import { FILTER_OPERATOR_REGISTRY_CONSTANT } from '../../constant/filter/operator-registry.constant.js';
import { EFilterOperand } from '../../enum/filter/operand.enum.js';
import '../../enum/filter/operation.enum.js';
import '../../enum/filter/order-direction.enum.js';
import { ValidatorConstraint } from 'class-validator';
// eslint-disable-next-line @elsikora/typescript/naming-convention
let HasPairedCustomSuffixesFieldsValidator = class HasPairedCustomSuffixesFieldsValidator {
/**
* Provides a custom error message describing the validation failure
* @param {ValidationArguments} properties - Validation arguments containing the object being validated
* @returns {string} A descriptive error message explaining why validation failed
*/
defaultMessage(properties) {
const object = properties.object;
const fieldGroups = object.__fieldGroups;
const operatorSuffix = object.__operatorSuffix;
const valueSuffixes = object.__valueSuffixes;
const indexableObject = properties.object;
for (const [baseName, groupSuffixes] of fieldGroups) {
const hasValueSuffix = valueSuffixes.some((suffix) => groupSuffixes.has(suffix));
if (hasValueSuffix && !groupSuffixes.has(operatorSuffix)) {
return `group "${baseName}" with value suffix must have an operator suffix [${operatorSuffix}]`;
}
if (groupSuffixes.has(operatorSuffix)) {
const operatorField = `${baseName}[${operatorSuffix}]`;
const operatorValue = indexableObject[operatorField];
const operator = operatorValue;
const operatorConfig = FILTER_OPERATOR_REGISTRY_CONSTANT.OPERATORS[operator];
if (!operatorConfig) {
return `Invalid operator "${operator}" for group "${baseName}"`;
}
if (operatorConfig.operand === EFilterOperand.NONE) {
const valueCount = valueSuffixes.filter((suffix) => groupSuffixes.has(suffix)).length;
if (valueCount > 0) {
return `group "${baseName}" with ${operator} operation should not have any values`;
}
continue;
}
if (groupSuffixes.size === 1) {
return `group "${baseName}" with operator suffix must have at least one value with suffix [${[...valueSuffixes].join(", ")}]`;
}
const valueFields = valueSuffixes.filter((suffix) => groupSuffixes.has(suffix)).map((suffix) => `${baseName}[${suffix}]`);
if (valueFields.length === 0) {
return `group "${baseName}" requires a value for ${operator} operation`;
}
if (valueFields.length > 1) {
return `group "${baseName}" can only have one value with suffix [${[...valueSuffixes].join(", ")}] when operator is present`;
}
// @ts-ignore
// eslint-disable-next-line @elsikora/typescript/no-unsafe-assignment
const value = indexableObject[valueFields[0]];
const isArray = Array.isArray(value);
if ((operatorConfig.operand === EFilterOperand.PAIR || operatorConfig.operand === EFilterOperand.VALUES) && !isArray) {
return `group "${baseName}" with ${operator} operation requires an array value`;
}
if (operatorConfig.operand === EFilterOperand.VALUE && isArray) {
return `group "${baseName}" with ${operator} operation requires a single value, not an array`;
}
if (isArray) {
if (operatorConfig.operand === EFilterOperand.PAIR && value.length !== FILTER_OPERATOR_REGISTRY_CONSTANT.PAIR_OPERAND_COUNT) {
return `group "${baseName}" with ${operator} operation requires exactly ${String(FILTER_OPERATOR_REGISTRY_CONSTANT.PAIR_OPERAND_COUNT)} values`;
}
if (operatorConfig.operand === EFilterOperand.VALUES && value.length < FILTER_OPERATOR_REGISTRY_CONSTANT.VALUES_MINIMUM_OPERAND_COUNT) {
return `group "${baseName}" with ${operator} operation requires at least ${String(FILTER_OPERATOR_REGISTRY_CONSTANT.VALUES_MINIMUM_OPERAND_COUNT)} value`;
}
}
}
}
return `fields must have valid operator-value suffix pairs`;
}
/**
* Validates that fields with suffixes follow the proper operator-value pairing pattern
* @param {unknown} _value - The value being validated (unused)
* @param {{ constraints: [string, Array<string>] } & ValidationArguments} properties - Validation arguments and constraints
* @returns {boolean} True if the object has properly paired suffixes, false otherwise
*/
validate(_value, properties) {
const [operatorSuffix, valueSuffixes] = properties.constraints;
const indexableObject = properties.object;
const fields = Object.keys(indexableObject);
const fieldGroups = new Map();
const suffixPattern = [operatorSuffix, ...valueSuffixes].map((suffix) => suffix.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw `\$&`)).join("|");
const regex = new RegExp(String.raw `^(.+?)\[(${suffixPattern})\]$`);
for (const field of fields) {
const match = regex.exec(field);
if (match) {
const [, baseName, suffix] = match;
if (!fieldGroups.has(baseName)) {
fieldGroups.set(baseName, new Set());
}
if (indexableObject[field] !== undefined) {
fieldGroups.get(baseName)?.add(suffix);
}
}
}
properties.object.__fieldGroups = fieldGroups;
properties.object.__operatorSuffix = operatorSuffix;
properties.object.__valueSuffixes = valueSuffixes;
for (const [baseName, groupSuffixes] of fieldGroups) {
const hasValueSuffix = valueSuffixes.some((suffix) => groupSuffixes.has(suffix));
if (hasValueSuffix && !groupSuffixes.has(operatorSuffix)) {
return false;
}
if (groupSuffixes.has(operatorSuffix)) {
const operatorField = `${baseName}[${operatorSuffix}]`;
const operatorValue = indexableObject[operatorField];
const operator = operatorValue;
const operatorConfig = FILTER_OPERATOR_REGISTRY_CONSTANT.OPERATORS[operator];
if (!operatorConfig)
return false;
if (operatorConfig.operand === EFilterOperand.NONE) {
const valueCount = valueSuffixes.filter((suffix) => groupSuffixes.has(suffix)).length;
if (valueCount > 0)
return false;
continue;
}
const valueFields = valueSuffixes.filter((suffix) => groupSuffixes.has(suffix)).map((suffix) => `${baseName}[${suffix}]`);
if (valueFields.length !== 1)
return false;
// @ts-ignore
// eslint-disable-next-line @elsikora/typescript/no-unsafe-assignment
const value = indexableObject[valueFields[0]];
const isArray = Array.isArray(value);
if ((operatorConfig.operand === EFilterOperand.PAIR || operatorConfig.operand === EFilterOperand.VALUES) && !isArray)
return false;
if (operatorConfig.operand === EFilterOperand.VALUE && isArray)
return false;
if (isArray) {
if (operatorConfig.operand === EFilterOperand.PAIR && value.length !== FILTER_OPERATOR_REGISTRY_CONSTANT.PAIR_OPERAND_COUNT)
return false;
if (operatorConfig.operand === EFilterOperand.VALUES && value.length < FILTER_OPERATOR_REGISTRY_CONSTANT.VALUES_MINIMUM_OPERAND_COUNT)
return false;
}
}
}
return true;
}
};
HasPairedCustomSuffixesFieldsValidator = __decorate([
ValidatorConstraint({ async: false, name: "has-paired-custom-suffixes-fields" })
], HasPairedCustomSuffixesFieldsValidator);
export { HasPairedCustomSuffixesFieldsValidator };
//# sourceMappingURL=paired-custom-suffixes-fields.validator.js.map