Skip to content

Commit

Permalink
list allowed values in enum validation errors
Browse files Browse the repository at this point in the history
  • Loading branch information
Carmine DiMascio committed Oct 23, 2019
1 parent a10fbc0 commit 80274ec
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 11 deletions.
14 changes: 8 additions & 6 deletions src/middlewares/openapi.request.validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
extractContentType,
validationError,
ajvErrorsToValidatorError,
augmentAjvErrors,
} from './util';
import ono from 'ono';
import { NextFunction, Response } from 'express';
Expand All @@ -22,7 +23,11 @@ export class RequestValidator {
this.ajv = createRequestAjv(apiDocs, options);
}

public validate(req: OpenApiRequest, res: Response, next: NextFunction): void {
public validate(
req: OpenApiRequest,
res: Response,
next: NextFunction,
): void {
if (!req.openapi) {
// this path was not found in open api and
// this path is not defined under an openapi base path
Expand Down Expand Up @@ -138,12 +143,11 @@ export class RequestValidator {
: undefined,
};
const valid = validator(reqToValidate);
// save errors, Ajv overwrites errors on each validation call (race condition?)
// TODO look into Ajv async errors plugins
const errors = [...(validator.errors || [])];
if (valid) {
next();
} else {
// TODO look into Ajv async errors plugins
const errors = augmentAjvErrors([...(validator.errors || [])]);
const err = ajvErrorsToValidatorError(400, errors);
const message = this.ajv.errorsText(errors, { dataVar: 'request' });
throw ono(err, message);
Expand All @@ -166,8 +170,6 @@ export class RequestValidator {
}
}



private requestBodyToSchema(path, contentType, requestBody: any = {}) {
if (requestBody.content) {
const content = requestBody.content[contentType];
Expand Down
3 changes: 2 additions & 1 deletion src/middlewares/openapi.response.validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as Ajv from 'ajv';
import mung from './modded.express.mung';
import { createResponseAjv } from './ajv';
import {
augmentAjvErrors,
extractContentType,
ajvErrorsToValidatorError,
validationError,
Expand Down Expand Up @@ -84,7 +85,7 @@ export class ResponseValidator {
});

if (!valid) {
const errors = validator.errors;
const errors = augmentAjvErrors(validator.errors);
const message = this.ajv.errorsText(errors, {
dataVar: '', // responses
});
Expand Down
19 changes: 18 additions & 1 deletion src/middlewares/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,23 @@ export function validationError(
return ono(err, message);
}

/**
* (side-effecting) modifies the errors object
* TODO - do this some other way
* @param errors
*/
export function augmentAjvErrors(errors: Ajv.ErrorObject[] = []): Ajv.ErrorObject[] {
errors.forEach(e => {
if (e.keyword === 'enum') {
const params: any = e.params;
const allowedEnumValues = params && params.allowedValues;
e.message = !!allowedEnumValues
? `${e.message}: ${allowedEnumValues.join(', ')}`
: e.message;
}
});
return errors;
}
export function ajvErrorsToValidatorError(
status: number,
errors: Ajv.ErrorObject[],
Expand All @@ -51,7 +68,7 @@ export function ajvErrorsToValidatorError(
params.missingProperty &&
e.dataPath + '.' + params.missingProperty;
const additionalProperty =
e.params &&
params &&
params.additionalProperty &&
e.dataPath + '.' + params.additionalProperty;
const path = required || additionalProperty || e.dataPath || e.schemaPath;
Expand Down
23 changes: 20 additions & 3 deletions test/routes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ describe(packageJson.name, () => {
expect(e).to.have.length(1);
expect(e[0].path).to.contain('testJson');
expect(e[0].message).to.equal(
'should be equal to one of the allowed values',
'should be equal to one of the allowed values: bar, baz',
);
}));

Expand All @@ -126,7 +126,7 @@ describe(packageJson.name, () => {
expect(e).to.have.length(1);
expect(e[0].path).to.contain('testArray');
expect(e[0].message).to.equal(
'should be equal to one of the allowed values',
'should be equal to one of the allowed values: foo, bar, baz',
);
}));

Expand All @@ -152,7 +152,7 @@ describe(packageJson.name, () => {
expect(e).to.have.length(1);
expect(e[0].path).to.contain('testArrayExplode');
expect(e[0].message).to.equal(
'should be equal to one of the allowed values',
'should be equal to one of the allowed values: foo, bar, baz',
);
}));
});
Expand Down Expand Up @@ -316,6 +316,23 @@ describe(packageJson.name, () => {
});
});

it('should return 400 an invalid enum value is given', async () => {
return request(apps[i])
.get(`${basePath}/pets`)
.query({
limit: 10,
test: 'one',
testArray: ['unknown_value'],
})
.expect(400)
.then(r => {
const e = r.body.errors;
expect(e[0].message).equals(
'should be equal to one of the allowed values: foo, bar, baz',
);
});
});

it('should handle multiple path params with coereion', async () => {
const id = '10';
const attributeId = '12';
Expand Down

0 comments on commit 80274ec

Please sign in to comment.