Skip to content

Commit eddf96e

Browse files
author
Shogun
committed
fix: Improved validation handling.
1 parent 854c2aa commit eddf96e

File tree

4 files changed

+54
-21
lines changed

4 files changed

+54
-21
lines changed

lib/validation.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,17 @@ function convertValidationErrors(section, data, validationErrors) {
6262
if (section === 'querystring') {
6363
section = 'query';
6464
}
65+
// For each error
6566
for (const e of validationErrors) {
66-
// For each error
67-
let baseKey = e.dataPath.substring(e.dataPath.startsWith('.') ? 1 : 0);
68-
let key = baseKey;
6967
let message = '';
68+
// Normalize the key
69+
let key = e.dataPath;
70+
if (key.startsWith('.')) {
71+
key = key.substring(1);
72+
}
73+
if (key.startsWith('[') && key.endsWith(']')) {
74+
key = key.substring(1, key.length - 1);
75+
}
7076
// Depending on the type
7177
switch (e.keyword) {
7278
case 'required':
@@ -128,12 +134,15 @@ function convertValidationErrors(section, data, validationErrors) {
128134
message = `${e.message.replace(/^should/, 'must')} (${e.keyword})`;
129135
}
130136
// Find the property to add
131-
let property = Array.from(new Set([baseKey, key].filter((p) => p)))
132-
.join('.')
133-
.replace(/\[(\d+)\]/g, '.$1');
137+
let property = key
138+
.replace(/\[(\d+)\]/g, '.$1') // Array path
139+
.replace(/\[([^\]]+)\]/g, '.$1'); // Object path
134140
if (!property) {
135141
property = '$root';
136142
}
143+
if (property.match(/(?:^['"])(?:[^\.]+)(?:['"]$)/)) {
144+
property = property.substring(1, property.length - 1);
145+
}
137146
errors[property] = message;
138147
}
139148
return { [section]: errors };

package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,10 @@
2828
"scripts": {
2929
"lint": "tslint --project tsconfig.test.json -t stylish src/*.ts test/*.ts",
3030
"test": "jest test --coverage --coverageReporters=html --coverageReporters=text",
31-
"ci": "jest test --coverage --ci --coverageReporters=json",
32-
"sanity": "yarn lint && yarn test",
31+
"ci": "yarn lint && jest --coverage --coverageThreshold='{\"global\":{\"branches\":90,\"functions\":90,\"lines\":90,\"statements\":90}}' --ci --coverageReporters=json",
3332
"prebuild": "rm -rf lib/* types/* && yarn lint",
3433
"build": "tsc -p .",
35-
"prepublishOnly": "yarn sanity",
34+
"prepublishOnly": "yarn ci",
3635
"postpublish": "git push origin && git push origin -f --tags"
3736
},
3837
"dependencies": {

src/validation.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,21 @@ export function convertValidationErrors(
8585
section = 'query'
8686
}
8787

88+
// For each error
8889
for (const e of validationErrors) {
89-
// For each error
90-
let baseKey = e.dataPath.substring(e.dataPath.startsWith('.') ? 1 : 0)
91-
let key = baseKey
9290
let message = ''
9391

92+
// Normalize the key
93+
let key = e.dataPath
94+
95+
if (key.startsWith('.')) {
96+
key = key.substring(1)
97+
}
98+
99+
if (key.startsWith('[') && key.endsWith(']')) {
100+
key = key.substring(1, key.length - 1)
101+
}
102+
94103
// Depending on the type
95104
switch (e.keyword) {
96105
case 'required':
@@ -159,10 +168,16 @@ export function convertValidationErrors(
159168
}
160169

161170
// Find the property to add
162-
let property = Array.from(new Set([baseKey, key].filter((p: string) => p)))
163-
.join('.')
164-
.replace(/\[(\d+)\]/g, '.$1')
171+
let property = key
172+
.replace(/\[(\d+)\]/g, '.$1') // Array path
173+
.replace(/\[([^\]]+)\]/g, '.$1') // Object path
174+
175+
// Remove useless quotes
176+
if (property.match(/(?:^['"])(?:[^\.]+)(?:['"]$)/)) {
177+
property = property.substring(1, property.length - 1)
178+
}
165179

180+
// Fix empty properties
166181
if (!property) {
167182
property = '$root'
168183
}

test/validation.spec.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import Ajv from 'ajv'
22
import { ValidationResult } from 'fastify'
33
import { convertValidationErrors, niceJoin } from '../src'
44

5-
describe.only('Validation', function(): void {
5+
describe('Validation', function(): void {
66
it('should correctly parse validation errors', function(): void {
77
const ajv = new Ajv({
88
removeAdditional: false,
@@ -125,10 +125,16 @@ describe.only('Validation', function(): void {
125125
objectPath: {
126126
type: 'object',
127127
properties: {
128-
abc: {
128+
'x-abc': {
129+
type: 'number'
130+
},
131+
cde: {
129132
type: 'number'
130133
}
131134
}
135+
},
136+
'needs-quotes': {
137+
type: 'number'
132138
}
133139
},
134140
additionalProperties: false,
@@ -162,8 +168,10 @@ describe.only('Validation', function(): void {
162168
noMessage: true,
163169
arrayPath: ['abc'],
164170
objectPath: {
165-
abc: 'abc'
166-
}
171+
'x-abc': 'abc',
172+
cde: 'cde'
173+
},
174+
'needs-quotes': 'nq'
167175
}
168176

169177
const expected = {
@@ -180,7 +188,8 @@ describe.only('Validation', function(): void {
180188
minItems: 'must be an array with at least 2 items',
181189
maxItems: 'must be an array with at most 2 items',
182190
'arrayPath.0': 'must be a valid number',
183-
'objectPath.abc': 'must be a valid number',
191+
"objectPath.'x-abc'": 'must be a valid number',
192+
'objectPath.cde': 'must be a valid number',
184193
minimum: 'must be a number greater than or equal to 5',
185194
maximum: 'must be a number less than or equal to 5',
186195
number: 'must be a valid number',
@@ -195,7 +204,8 @@ describe.only('Validation', function(): void {
195204
response:
196205
'The response returned from the endpoint violates its specification for the HTTP status invalidResponse.',
197206
responseCode: 'This endpoint cannot respond with HTTP status invalidResponseCode.',
198-
noMessage: 'must match format "noMessage" (format)'
207+
noMessage: 'must match format "noMessage" (format)',
208+
'needs-quotes': 'must be a valid number'
199209
}
200210
}
201211

0 commit comments

Comments
 (0)