Skip to content

Commit 7db0422

Browse files
committed
fix: Upgrade ajv to 8.3.0.
BREAKING CHANGE: Some validation error messages have changed because of the AJV upgrade.
1 parent 2da23ec commit 7db0422

File tree

7 files changed

+56
-66
lines changed

7 files changed

+56
-66
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
},
7878
"dependencies": {
7979
"@apidevtools/json-schema-ref-parser": "^9.0.3",
80-
"ajv": "^6.12.2",
80+
"ajv": "^8.3.0",
8181
"body-parser": "^1.18.3",
8282
"content-type": "^1.0.4",
8383
"deep-freeze": "0.0.1",

src/oas3/Schema/validators.ts

+14-25
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Ajv from 'ajv';
1+
import Ajv, { ValidateFunction } from 'ajv';
22
import traveseSchema from 'json-schema-traverse';
33
import { CustomFormats, IValidationError, ParameterLocation, ValidatorFunction } from '../../types';
44
import { resolveRef } from '../../utils/json-schema-resolve-ref';
@@ -48,26 +48,17 @@ function getParameterDescription(parameterLocation: ParameterLocation) {
4848
return description;
4949
}
5050

51-
function addCustomFormats(
52-
ajv: Ajv.Ajv,
53-
customFormats: CustomFormats
54-
): { [k: string]: Ajv.FormatDefinition } {
55-
return Object.keys(customFormats).reduce<{ [k: string]: Ajv.FormatDefinition }>(
56-
(result: { [k: string]: Ajv.FormatDefinition }, key: string) => {
57-
const customFormat = customFormats[key];
58-
if (typeof customFormat === 'function' || customFormat instanceof RegExp) {
59-
result[key] = { type: 'string', validate: customFormat };
60-
} else if (customFormat.type === 'string') {
61-
result[key] = { type: 'string', validate: customFormat.validate };
62-
} else if (customFormat.type === 'number') {
63-
result[key] = { type: 'number', validate: customFormat.validate };
64-
}
65-
66-
ajv.addFormat(key, result[key]);
67-
return result;
68-
},
69-
{}
70-
);
51+
function addCustomFormats(ajv: Ajv, customFormats: CustomFormats) {
52+
for (const key of Object.keys(customFormats)) {
53+
const customFormat = customFormats[key];
54+
if (typeof customFormat === 'function' || customFormat instanceof RegExp) {
55+
ajv.addFormat(key, { type: 'string', validate: customFormat });
56+
} else if (customFormat.type === 'string') {
57+
ajv.addFormat(key, { type: 'string', validate: customFormat.validate });
58+
} else if (customFormat.type === 'number') {
59+
ajv.addFormat(key, { type: 'number', validate: customFormat.validate });
60+
}
61+
}
7162
}
7263

7364
function removeExamples(schema: any) {
@@ -139,7 +130,7 @@ function doValidate(
139130
schemaPtr: string,
140131
parameterLocation: ParameterLocation,
141132
parameterRequired: boolean,
142-
ajvValidate: Ajv.ValidateFunction,
133+
ajvValidate: ValidateFunction,
143134
json: any
144135
) {
145136
const value = { value: json };
@@ -167,7 +158,7 @@ function doValidate(
167158
ajvValidate(value);
168159
if (ajvValidate.errors) {
169160
errors = ajvValidate.errors.map((err) => {
170-
let pathPtr = err.dataPath || '';
161+
let pathPtr = err.instancePath || '';
171162
if (pathPtr.startsWith('/value')) {
172163
pathPtr = pathPtr.slice(6);
173164
}
@@ -226,8 +217,6 @@ function generateValidator(
226217
useDefaults: true,
227218
coerceTypes: allowTypeCoercion ? 'array' : false,
228219
removeAdditional: allowTypeCoercion ? 'failing' : false,
229-
jsonPointers: true,
230-
nullable: true,
231220
allErrors: schemaContext.options.allErrors,
232221
});
233222
addCustomFormats(ajv, customFormats);

test/integration/integration/customErrorFormattingTest.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ describe('integration test', function () {
104104
name: 'name',
105105
path: '',
106106
},
107-
message: 'should NOT be shorter than 2 characters',
107+
message: 'must NOT have fewer than 2 characters',
108108
keyword: 'minLength',
109109
params: {
110110
limit: 2,

test/integration/integration/integrationTest.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ describe('integration test', function () {
119119
name: 'name',
120120
path: '',
121121
},
122-
message: 'should NOT be shorter than 2 characters',
122+
message: 'must NOT have fewer than 2 characters',
123123
},
124124
],
125125
});

test/integration/integration/openapi.yaml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
openapi: '3.0.1'
1+
openapi: '3.1.0'
22
info:
33
version: 1.0.0
44
title: Exegesis Integration Test
@@ -186,6 +186,7 @@ components:
186186
in: 'header'
187187
schemas:
188188
Error:
189+
type: object
189190
required:
190191
- message
191192
properties:

test/oas3/ResponsesTest.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,11 @@ describe('oas3 Responses', function () {
140140
name: 'body',
141141
path: '',
142142
},
143-
message: "should have required property 'foo'",
143+
message: "must have required property 'foo'",
144144
ajvError: {
145-
dataPath: '/value',
145+
instancePath: '/value',
146146
keyword: 'required',
147-
message: "should have required property 'foo'",
147+
message: "must have required property 'foo'",
148148
params: {
149149
missingProperty: 'foo',
150150
},
@@ -241,11 +241,11 @@ describe('oas3 Responses', function () {
241241
name: 'body',
242242
path: '',
243243
},
244-
message: "should have required property 'message'",
244+
message: "must have required property 'message'",
245245
ajvError: {
246-
dataPath: '/value',
246+
instancePath: '/value',
247247
keyword: 'required',
248-
message: "should have required property 'message'",
248+
message: "must have required property 'message'",
249249
params: {
250250
missingProperty: 'message',
251251
},

test/oas3/Schema/validatorsTest.ts

+31-31
Original file line numberDiff line numberDiff line change
@@ -110,17 +110,17 @@ describe('schema validators', function () {
110110

111111
expect(validator('foo').errors).to.eql([
112112
{
113-
message: 'should be number',
113+
message: 'must be number',
114114
location: {
115115
in: 'query',
116116
name: 'foo',
117117
docPath: '/components/schemas/number',
118118
path: '',
119119
},
120120
ajvError: {
121-
dataPath: '/value',
121+
instancePath: '/value',
122122
keyword: 'type',
123-
message: 'should be number',
123+
message: 'must be number',
124124
params: {
125125
type: 'number',
126126
},
@@ -144,17 +144,17 @@ describe('schema validators', function () {
144144

145145
expect(validator({}).errors, 'should still require "b"').to.eql([
146146
{
147-
message: "should have required property 'b'",
147+
message: "must have required property 'b'",
148148
location: {
149149
in: 'request',
150150
name: 'body',
151151
docPath: '/components/schemas/object',
152152
path: '',
153153
},
154154
ajvError: {
155-
dataPath: '/value',
155+
instancePath: '/value',
156156
keyword: 'required',
157-
message: "should have required property 'b'",
157+
message: "must have required property 'b'",
158158
params: {
159159
missingProperty: 'b',
160160
},
@@ -177,17 +177,17 @@ describe('schema validators', function () {
177177

178178
expect(validator({}).errors, 'should still require "a"').to.eql([
179179
{
180-
message: "should have required property 'a'",
180+
message: "must have required property 'a'",
181181
location: {
182182
in: 'request',
183183
name: 'body',
184184
docPath: '/components/schemas/object',
185185
path: '',
186186
},
187187
ajvError: {
188-
dataPath: '/value',
188+
instancePath: '/value',
189189
keyword: 'required',
190-
message: "should have required property 'a'",
190+
message: "must have required property 'a'",
191191
params: {
192192
missingProperty: 'a',
193193
},
@@ -223,17 +223,17 @@ describe('schema validators', function () {
223223

224224
expect(validator({ a: 'hello' }).errors).to.eql([
225225
{
226-
message: 'should be number',
226+
message: 'must be number',
227227
location: {
228228
in: 'request',
229229
name: 'body',
230230
docPath: '/components/schemas/object2',
231231
path: '/a',
232232
},
233233
ajvError: {
234-
dataPath: '/value/a',
234+
instancePath: '/value/a',
235235
keyword: 'type',
236-
message: 'should be number',
236+
message: 'must be number',
237237
params: {
238238
type: 'number',
239239
},
@@ -266,17 +266,17 @@ describe('schema validators', function () {
266266
// sure it correctly catches decimal values.
267267
expect(validator(7.5).errors).to.eql([
268268
{
269-
message: 'should be integer',
269+
message: 'must be integer',
270270
location: {
271271
in: 'query',
272272
name: 'foo',
273273
docPath: '/components/schemas/int32',
274274
path: '',
275275
},
276276
ajvError: {
277-
dataPath: '/value',
277+
instancePath: '/value',
278278
keyword: 'type',
279-
message: 'should be integer',
279+
message: 'must be integer',
280280
params: {
281281
type: 'integer',
282282
},
@@ -288,17 +288,17 @@ describe('schema validators', function () {
288288
// The expected failure result for an int32 format failure.
289289
const int32FailValue = [
290290
{
291-
message: 'should match format "int32"',
291+
message: 'must match format "int32"',
292292
location: {
293293
in: 'query',
294294
name: 'foo',
295295
docPath: '/components/schemas/int32',
296296
path: '',
297297
},
298298
ajvError: {
299-
dataPath: '/value',
299+
instancePath: '/value',
300300
keyword: 'format',
301-
message: 'should match format "int32"',
301+
message: 'must match format "int32"',
302302
params: {
303303
format: 'int32',
304304
},
@@ -479,9 +479,9 @@ describe('schema validators', function () {
479479
errors: [
480480
{
481481
ajvError: {
482-
dataPath: '/value',
482+
instancePath: '/value',
483483
keyword: 'required',
484-
message: "should have required property 'a'",
484+
message: "must have required property 'a'",
485485
params: {
486486
missingProperty: 'a',
487487
},
@@ -493,7 +493,7 @@ describe('schema validators', function () {
493493
name: 'body',
494494
path: '',
495495
},
496-
message: "should have required property 'a'",
496+
message: "must have required property 'a'",
497497
},
498498
],
499499
value: {},
@@ -517,9 +517,9 @@ describe('schema validators', function () {
517517
errors: [
518518
{
519519
ajvError: {
520-
dataPath: '/value',
520+
instancePath: '/value',
521521
keyword: 'required',
522-
message: "should have required property 'a'",
522+
message: "must have required property 'a'",
523523
params: {
524524
missingProperty: 'a',
525525
},
@@ -531,13 +531,13 @@ describe('schema validators', function () {
531531
name: 'body',
532532
path: '',
533533
},
534-
message: "should have required property 'a'",
534+
message: "must have required property 'a'",
535535
},
536536
{
537537
ajvError: {
538-
dataPath: '/value',
538+
instancePath: '/value',
539539
keyword: 'required',
540-
message: "should have required property 'b'",
540+
message: "must have required property 'b'",
541541
params: {
542542
missingProperty: 'b',
543543
},
@@ -549,7 +549,7 @@ describe('schema validators', function () {
549549
name: 'body',
550550
path: '',
551551
},
552-
message: "should have required property 'b'",
552+
message: "must have required property 'b'",
553553
},
554554
],
555555
value: {},
@@ -594,9 +594,9 @@ describe('schema validators', function () {
594594
errors: [
595595
{
596596
ajvError: {
597-
dataPath: '/value',
597+
instancePath: '/value',
598598
keyword: 'type',
599-
message: 'should be number',
599+
message: 'must be number',
600600
params: {
601601
type: 'number',
602602
},
@@ -608,7 +608,7 @@ describe('schema validators', function () {
608608
name: 'body',
609609
path: '',
610610
},
611-
message: 'should be number',
611+
message: 'must be number',
612612
},
613613
],
614614
value: '9',
@@ -685,7 +685,7 @@ describe('schema validators', function () {
685685
kind: 'test',
686686
}).errors?.[0].message,
687687
'should be invalid with 0 items'
688-
).to.equal('should NOT have fewer than 1 items');
688+
).to.equal('must NOT have fewer than 1 items');
689689

690690
expect(
691691
validator({

0 commit comments

Comments
 (0)