Skip to content

Commit 677cacf

Browse files
pileroucdimascio
authored andcommitted
ResponseValidator's Ajv can be useful too.
So we return an object that contains both request ajv and response ajv : ```javascript ajvs = { req : 'Ajv object' res : 'Ajv object' } ``` cdimascio#683
1 parent a727f2d commit 677cacf

File tree

5 files changed

+133
-26
lines changed

5 files changed

+133
-26
lines changed

README.md

+25-4
Original file line numberDiff line numberDiff line change
@@ -1156,17 +1156,35 @@ Instead of initialize OpenApiValidator with middleware, you can get a configured
11561156

11571157

11581158
```javascript
1159-
const ajv = await OpenApiValidator.ajv({
1159+
const ajvs = await OpenApiValidator.ajv({
11601160
apiSpec: './openapi.yaml',
11611161
validateRequests: true, // (default)
11621162
validateResponses: true, // false by default
11631163
});
11641164

1165-
const req : ReqClass = {
1165+
const customObj = {
11661166
id : '507f191e810c19729de860ea',
11671167
}
11681168

1169-
ajv.validate(
1169+
const isReqValid = ajvs.req.validate(
1170+
{
1171+
type: 'object',
1172+
properties: {
1173+
id: {
1174+
$ref: '#/components/schemas/ObjectId',
1175+
},
1176+
},
1177+
required: ['token'],
1178+
additionalProperties: false,
1179+
},
1180+
customObj
1181+
);
1182+
1183+
// isReqValid = true
1184+
// No error in ajvs.req.errors
1185+
1186+
1187+
const isResValid = ajvs.res.validate(
11701188
{
11711189
type: 'object',
11721190
properties: {
@@ -1177,8 +1195,11 @@ ajv.validate(
11771195
required: ['token'],
11781196
additionalProperties: false,
11791197
},
1180-
req
1198+
customObj
11811199
);
1200+
1201+
// isResValid = true
1202+
// No error in ajvs.res.errors
11821203
```
11831204

11841205
## FAQ

src/framework/types.ts

+6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as ajv from 'ajv';
22
import * as multer from 'multer';
33
import { Request, Response, NextFunction } from 'express';
4+
import { Ajv } from 'ajv';
45
export { OpenAPIFrameworkArgs };
56

67
export type BodySchema =
@@ -155,6 +156,11 @@ export namespace OpenAPIV3 {
155156
version: string;
156157
}
157158

159+
export interface Ajvs {
160+
req?: Ajv,
161+
res?: Ajv
162+
}
163+
158164
export interface ContactObject {
159165
name?: string;
160166
url?: string;

src/middlewares/openapi.response.validator.ts

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
} from '../framework/types';
1818
import * as mediaTypeParser from 'media-typer';
1919
import * as contentTypeParser from 'content-type';
20+
import { Ajv } from 'ajv';
2021

2122
interface ValidateResult {
2223
validators: { [key: string]: ajv.ValidateFunction };
@@ -318,4 +319,8 @@ export class ResponseValidator {
318319
mediaTypeParsed.subtype === 'json' || mediaTypeParsed.suffix === 'json'
319320
);
320321
}
322+
323+
public getAJV() : Ajv {
324+
return this.ajvBody;
325+
}
321326
}

src/openapi.validator.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { defaultSerDes } from './framework/base.serdes';
2121
import { SchemaPreprocessor } from './middlewares/parsers/schema.preprocessor';
2222
import { AjvOptions } from './framework/ajv/options';
2323
import { Ajv } from 'ajv';
24+
import Ajvs = OpenAPIV3.Ajvs;
2425

2526
export {
2627
OpenApiValidatorOpts,
@@ -91,7 +92,7 @@ export class OpenApiValidator {
9192
this.ajvOpts = new AjvOptions(options);
9293
}
9394

94-
installAjv(spec: Promise<Spec>): Promise<Ajv> {
95+
installAjv(spec: Promise<Spec>): Promise<Ajvs> {
9596
return spec
9697
.then((spec) => {
9798
const apiDoc = spec.apiDoc;
@@ -107,7 +108,15 @@ export class OpenApiValidator {
107108
responseApiDoc: sp.apiDocRes,
108109
error: null,
109110
};*/
110-
return new middlewares.RequestValidator(apiDoc, this.ajvOpts.request).getAJV();
111+
return {
112+
req : new middlewares.RequestValidator(apiDoc, this.ajvOpts.request).getAJV(),
113+
res : new middlewares.ResponseValidator(
114+
apiDoc,
115+
this.ajvOpts.response,
116+
// This has already been converted from boolean if required
117+
this.options.validateResponses as ValidateResponseOpts,)
118+
.getAJV(),
119+
};
111120
});
112121
}
113122

test/ajv.return.spec.ts

+86-20
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { expect } from 'chai';
33

44
import { date, dateTime } from '../src/framework/base.serdes';
55
import * as OpenApiValidator from '../src';
6-
import { Ajv } from 'ajv';
6+
import { OpenAPIV3 } from '../src/framework/types';
7+
import Ajvs = OpenAPIV3.Ajvs;
78

89
const apiSpecPath = path.join('test', 'resources', 'serdes.yaml');
910

@@ -20,10 +21,25 @@ class ObjectID {
2021
}
2122

2223
describe('ajv.return', () => {
23-
let ajv : Ajv = null;
24+
let ajvs : Ajvs = null;
25+
26+
class ReqRes {
27+
id?: string|ObjectID
28+
}
29+
30+
const customSchema = {
31+
type: 'object',
32+
properties: {
33+
id: {
34+
$ref: '#/components/schemas/ObjectId',
35+
},
36+
},
37+
required: ['id'],
38+
additionalProperties: false,
39+
};
2440

2541
before(async () => {
26-
ajv = await OpenApiValidator.ajv({
42+
ajvs = await OpenApiValidator.ajv({
2743
apiSpec: apiSpecPath,
2844
validateRequests: {
2945
coerceTypes: true
@@ -44,30 +60,80 @@ describe('ajv.return', () => {
4460
});
4561
});
4662

47-
it('should control BAD id format and throw an error', async () => {
48-
class ReqClass {
49-
id: string|ObjectID
50-
}
51-
52-
const req : ReqClass = {
63+
it('should control request and deserialize string to object', async () => {
64+
const req : ReqRes = {
5365
id : '507f191e810c19729de860ea',
5466
}
5567

56-
ajv.validate(
57-
{
58-
type: 'object',
59-
properties: {
60-
id: {
61-
$ref: '#/components/schemas/ObjectId',
62-
},
63-
},
64-
required: ['token'],
65-
additionalProperties: false,
66-
},
68+
const isValid = ajvs.req.validate(
69+
customSchema,
6770
req
6871
);
72+
expect(isValid).to.be.equal(true);
73+
expect(ajvs.req.errors).to.be.equal(null);
6974
expect(req.id instanceof ObjectID).to.be.true;
7075
});
76+
77+
it('should control request and return error if id is not set', async () => {
78+
const req : ReqRes = {
79+
// No id but it is required
80+
// id : '507f191e810c19729de860ea',
81+
}
82+
83+
const isValid = ajvs.req.validate(
84+
customSchema,
85+
req
86+
);
87+
expect(isValid).to.be.equal(false);
88+
expect(ajvs.req.errors.length).to.be.equal(1);
89+
expect(ajvs.req.errors[0].message).to.be.equal('should have required property \'id\'');
90+
});
91+
92+
it('should control request and return error if id is in bad format', async () => {
93+
const req : ReqRes = {
94+
id : 'notAnObjectId',
95+
}
96+
97+
const isValid = ajvs.req.validate(
98+
customSchema,
99+
req
100+
);
101+
expect(isValid).to.be.equal(false);
102+
expect(ajvs.req.errors.length).to.be.equal(1);
103+
expect(ajvs.req.errors[0].message).to.be.equal('should match pattern "^[0-9a-fA-F]{24}$"');
104+
});
105+
106+
107+
it('should control response and serialize object to string', async () => {
108+
const res : ReqRes = {
109+
id : new ObjectID('507f191e810c19729de860ea'),
110+
}
111+
112+
const isValid = ajvs.res.validate(
113+
customSchema,
114+
res
115+
);
116+
expect(res.id).to.be.equal('507f191e810c19729de860ea');
117+
expect(isValid).to.be.equal(true);
118+
expect(ajvs.res.errors).to.be.equal(null);
119+
});
120+
121+
it('should control response and return an error if id is not set', async () => {
122+
123+
const res : ReqRes = {
124+
// No id but it is required
125+
// id : '507f191e810c19729de860ea',
126+
//id : new ObjectID('507f191e810c19729de860ea'),
127+
}
128+
129+
const isValid = ajvs.res.validate(
130+
customSchema,
131+
res
132+
);
133+
expect(isValid).to.be.equal(false);
134+
expect(ajvs.res.errors.length).to.be.equal(1);
135+
expect(ajvs.res.errors[0].message).to.be.equal('should have required property \'id\'');
136+
});
71137
});
72138

73139

0 commit comments

Comments
 (0)