@leancodepl/validation
Version:
CQRS validation helpers for command and query validation
129 lines (125 loc) • 4.66 kB
JavaScript
;
/**
* Creates a validation error handler that processes errors with type-safe error code mapping.
*
* @template TAllErrors - Error codes map type extending Record<string, number>
* @template TInResult - Type of results accumulated from previous handlers
* @param validationErrors - Array of validation errors to process
* @param errorCodesMap - Mapping of error names to numeric codes
* @param validationResults - Optional array of previous handler results
* @returns Handler with handle, handleAll, and check methods
* @example
* ```typescript
* const errorCodes = { EmailExists: 1, InvalidEmail: 2 } as const;
* const errors = [{ ErrorCode: 1, ErrorMessage: 'Email exists', PropertyName: 'Email', AttemptedValue: 'test@example.com' }];
*
* handleValidationErrors(errors, errorCodes)
* .handle('EmailExists', () => console.warn('Email already registered'))
* .handle('InvalidEmail', () => console.warn('Invalid email format'))
* .check();
* ```
*/ function handleValidationErrors(validationErrors, errorCodesMap, validationResults = []) {
const handle = (validationErrorsToHandle, handler)=>{
let result = undefined;
for (const validationErrorToHandle of Array.isArray(validationErrorsToHandle) ? validationErrorsToHandle : [
validationErrorsToHandle
]){
const ve = validationErrors.find((ve)=>ve.ErrorCode === errorCodesMap[validationErrorToHandle]);
if (ve) {
result = handler(validationErrorToHandle, ve);
break;
}
}
let nextResult = validationResults;
if (result !== undefined) {
nextResult = [
...nextResult,
result
];
}
return handleValidationErrors(validationErrors, errorCodesMap, nextResult);
};
const handleAll = (_validationErrorsToHandle, handler)=>{
let result = undefined;
const validationErrorsToHandle = Array.isArray(_validationErrorsToHandle) ? _validationErrorsToHandle : [
_validationErrorsToHandle
];
const foundErrors = validationErrorsToHandle.reduce((prev, cur)=>{
const ves = validationErrors.filter((ve)=>ve.ErrorCode === errorCodesMap[cur]);
if (ves.length === 0) {
return prev;
}
return [
...prev,
{
errorName: cur,
errors: ves
}
];
}, []);
if (foundErrors.length > 0) {
result = handler(foundErrors);
}
let nextResult = validationResults;
if (result !== undefined) {
nextResult = [
...nextResult,
result
];
}
return handleValidationErrors(validationErrors, errorCodesMap, nextResult);
};
return {
handle,
handleAll,
check: (reducer)=>{
if (reducer) {
return validationResults.reduce(reducer.reducer, reducer.initialValue);
}
return;
}
};
}
/**
* Handles CQRS command responses and transforms them into validation error handlers.
*
* @template TErrors - Error codes map type extending Record<string, number>
* @param response - API response containing command result
* @param errorCodesMap - Mapping of error names to numeric codes
* @returns Validation error handler with success/failure support
* @example
* ```typescript
* const errorCodes = { UserNotFound: 1 } as const;
* const response = await commandClient.execute(createUserCommand);
*
* handleResponse(response, errorCodes)
* .handle('success', () => console.log('User created'))
* .handle('failure', () => console.log('Network error'))
* .handle('UserNotFound', () => console.log('User not found'))
* .check();
* ```
*/ function handleResponse(response, errorCodesMap) {
const newErrorCodesMap = {
...errorCodesMap,
success: -1,
failure: -2
};
const validationErrors = response.isSuccess ? response.result.WasSuccessful ? [
{
AttemptedValue: "",
ErrorMessage: "",
PropertyName: "",
ErrorCode: -1
}
] : response.result.ValidationErrors : [
{
AttemptedValue: "",
ErrorMessage: "",
PropertyName: "",
ErrorCode: -2
}
];
return handleValidationErrors(validationErrors, newErrorCodesMap);
}
exports.handleResponse = handleResponse;
exports.handleValidationErrors = handleValidationErrors;