Skip to content

Commit 8c05b57

Browse files
AaronJanraymondfeng
authored andcommitted
feat(rest): allow developers to transform AJV error objects
1 parent a1f9712 commit 8c05b57

File tree

3 files changed

+68
-1
lines changed

3 files changed

+68
-1
lines changed

packages/rest/src/__tests__/acceptance/validation/validation.acceptance.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,64 @@ describe('Validation at REST level', () => {
246246
});
247247
});
248248

249+
context(
250+
'with request body validation options - {ajvErrorTransformer: [Function]}',
251+
() => {
252+
class ProductController {
253+
@post('/products')
254+
async create(
255+
@requestBody({required: true}) data: Product,
256+
): Promise<Product> {
257+
return new Product(data);
258+
}
259+
}
260+
261+
before(() =>
262+
givenAnAppAndAClient(ProductController, {
263+
nullable: false,
264+
compiledSchemaCache: new WeakMap(),
265+
$data: true,
266+
ajvErrorTransformer: errors => {
267+
return errors.map(e => ({
268+
...e,
269+
message: `(translated) ${e.message}`,
270+
}));
271+
},
272+
}),
273+
);
274+
after(() => app.stop());
275+
276+
it('transforms error messages provided by AJV', async () => {
277+
const DATA = {
278+
name: 'iPhone',
279+
description: 'iPhone',
280+
};
281+
const res = await client
282+
.post('/products')
283+
.send(DATA)
284+
.expect(422);
285+
286+
expect(res.body).to.eql({
287+
error: {
288+
statusCode: 422,
289+
name: 'UnprocessableEntityError',
290+
message:
291+
'The request body is invalid. See error object `details` property for more info.',
292+
code: 'VALIDATION_FAILED',
293+
details: [
294+
{
295+
path: '',
296+
code: 'required',
297+
message: "(translated) should have required property 'price'",
298+
info: {missingProperty: 'price'},
299+
},
300+
],
301+
},
302+
});
303+
});
304+
},
305+
);
306+
249307
// A request body schema can be provided explicitly by the user
250308
// as an inlined content[type].schema property.
251309
context('for fully-specified request body', () => {

packages/rest/src/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ export interface RequestBodyValidationOptions extends ajv.Options {
107107
* - `string[]`: Add an array of keywords from `ajv-keywords`
108108
*/
109109
ajvKeywords?: true | string[];
110+
/**
111+
* A function that transform the `ErrorObject`s reported by AJV.
112+
* This could be used for error messages customization, localization, etc.
113+
*/
114+
ajvErrorTransformer?: (errors: ajv.ErrorObject[]) => ajv.ErrorObject[];
110115
}
111116

112117
/* eslint-disable @typescript-eslint/no-explicit-any */

packages/rest/src/validation/request-body.validator.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ function validateValueAgainstSchema(
137137
return;
138138
}
139139

140-
const validationErrors = validate.errors;
140+
let validationErrors = validate.errors as AJV.ErrorObject[];
141141

142142
/* istanbul ignore if */
143143
if (debug.enabled) {
@@ -148,6 +148,10 @@ function validateValueAgainstSchema(
148148
);
149149
}
150150

151+
if (typeof options.ajvErrorTransformer === 'function') {
152+
validationErrors = options.ajvErrorTransformer(validationErrors);
153+
}
154+
151155
const error = RestHttpErrors.invalidRequestBody();
152156
error.details = _.map(validationErrors, e => {
153157
return {

0 commit comments

Comments
 (0)