This commit is contained in:
+121
@@ -0,0 +1,121 @@
|
||||
import { DefinedError, ErrorObject } from 'ajv';
|
||||
import type { JSONSchema6 } from 'json-schema';
|
||||
import { ValidationError } from './types/ValidationError';
|
||||
import { filterSingleErrorPerProperty } from './lib/filter';
|
||||
import { getSuggestion } from './lib/suggestions';
|
||||
import { cleanAjvMessage, getLastSegment, pointerToDotNotation, safeJsonPointer } from './lib/utils';
|
||||
|
||||
export interface BetterAjvErrorsOptions {
|
||||
errors: ErrorObject[] | null | undefined;
|
||||
data: any;
|
||||
schema: JSONSchema6;
|
||||
basePath?: string;
|
||||
}
|
||||
|
||||
export const betterAjvErrors = ({
|
||||
errors,
|
||||
data,
|
||||
schema,
|
||||
basePath = '{base}',
|
||||
}: BetterAjvErrorsOptions): ValidationError[] => {
|
||||
if (!Array.isArray(errors) || errors.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const definedErrors = filterSingleErrorPerProperty(errors as DefinedError[]);
|
||||
|
||||
return definedErrors.map((error) => {
|
||||
const path = pointerToDotNotation(basePath + error.instancePath);
|
||||
const prop = getLastSegment(error.instancePath);
|
||||
const defaultContext = {
|
||||
errorType: error.keyword,
|
||||
};
|
||||
const defaultMessage = `${prop ? `property '${prop}'` : path} ${cleanAjvMessage(error.message as string)}`;
|
||||
|
||||
let validationError: ValidationError;
|
||||
|
||||
switch (error.keyword) {
|
||||
case 'additionalProperties': {
|
||||
const additionalProp = error.params.additionalProperty;
|
||||
const suggestionPointer = error.schemaPath.replace('#', '').replace('/additionalProperties', '');
|
||||
const { properties } = safeJsonPointer({
|
||||
object: schema,
|
||||
pnter: suggestionPointer,
|
||||
fallback: { properties: {} },
|
||||
});
|
||||
validationError = {
|
||||
message: `'${additionalProp}' property is not expected to be here`,
|
||||
suggestion: getSuggestion({
|
||||
value: additionalProp,
|
||||
suggestions: Object.keys(properties ?? {}),
|
||||
format: (suggestion) => `Did you mean property '${suggestion}'?`,
|
||||
}),
|
||||
path,
|
||||
context: defaultContext,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 'enum': {
|
||||
const suggestions = error.params.allowedValues.map((value) => value.toString());
|
||||
const prop = getLastSegment(error.instancePath);
|
||||
const value = safeJsonPointer({ object: data, pnter: error.instancePath, fallback: '' });
|
||||
validationError = {
|
||||
message: `'${prop}' property must be equal to one of the allowed values`,
|
||||
suggestion: getSuggestion({
|
||||
value,
|
||||
suggestions,
|
||||
}),
|
||||
path,
|
||||
context: {
|
||||
...defaultContext,
|
||||
allowedValues: error.params.allowedValues,
|
||||
},
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 'type': {
|
||||
const prop = getLastSegment(error.instancePath);
|
||||
const type = error.params.type;
|
||||
validationError = {
|
||||
message: `'${prop}' property type must be ${type}`,
|
||||
path,
|
||||
context: defaultContext,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 'required': {
|
||||
validationError = {
|
||||
message: `${path} must have required property '${error.params.missingProperty}'`,
|
||||
path,
|
||||
context: defaultContext,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 'const': {
|
||||
return {
|
||||
message: `'${prop}' property must be equal to the allowed value`,
|
||||
path,
|
||||
context: {
|
||||
...defaultContext,
|
||||
allowedValue: error.params.allowedValue,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
return { message: defaultMessage, path, context: defaultContext };
|
||||
}
|
||||
|
||||
// Remove empty properties
|
||||
const errorEntries = Object.entries(validationError);
|
||||
for (const [key, value] of errorEntries as [keyof ValidationError, unknown][]) {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
delete validationError[key];
|
||||
}
|
||||
}
|
||||
|
||||
return validationError;
|
||||
});
|
||||
};
|
||||
|
||||
export { ValidationError };
|
||||
Reference in New Issue
Block a user