Skip to content

Commit 10639cd

Browse files
ralphdoeshockey
andcommitted
feat(validation): enum values should conform to their schema's type (#2079)
* feature/validator_enum_types - Validator in schemas.js added - tests also added * feature/validator_enum_types - Validator in schemas.js added - tests also added * feature/validator_enum_types - PR Fixes * feature/validator_enum_types - Change Requests added * feature/validator_enum_types - Array is not a valid object in JSON, tests added Co-authored-by: kyle shockey <kyleshockey@gmail.com>
1 parent a1ed321 commit 10639cd

File tree

2 files changed

+385
-0
lines changed
  • src/plugins/validate-semantic/validators/2and3
  • test/unit/plugins/validate-semantic/2and3

2 files changed

+385
-0
lines changed

src/plugins/validate-semantic/validators/2and3/schemas.js

+39
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,42 @@ export const validate2And3TypeArrayRequiresItems = () => (system) => {
2222
}, [])
2323
})
2424
}
25+
26+
export const validate2And3TypesInDefaultValuesMatchesWithEnum = () => (system) => {
27+
return system.validateSelectors
28+
.allSchemas()
29+
.then(nodes => {
30+
return nodes.reduce((acc, node) => {
31+
const schemaObj = node.node
32+
const { type } = schemaObj || {}
33+
const enumeration = schemaObj.enum
34+
if (enumeration !== null && typeof enumeration !== "undefined") {
35+
var enumIndex = 0
36+
enumeration.forEach((element, index) => {
37+
var isValidFormat = true
38+
if (type === "array" && (!Array.isArray(element) || element === null)) {
39+
isValidFormat = false
40+
enumIndex = index
41+
} else if ((type === "number" || type === "string" || type === "boolean") && !(typeof element === type)) {
42+
isValidFormat = false
43+
enumIndex = index
44+
} else if (type === "integer" && !Number.isInteger(element)) {
45+
isValidFormat = false
46+
enumIndex = index
47+
} else if (type === "object" && ((element === null) || !(typeof element === type) || Array.isArray(element))) {
48+
isValidFormat = false
49+
enumIndex = index
50+
}
51+
if (!isValidFormat) {
52+
acc.push({
53+
message: "enum value should conform to its schema's `type`",
54+
path: [...node.path, "enum", enumIndex],
55+
level: "warning",
56+
})
57+
}
58+
})
59+
}
60+
return acc
61+
}, [])
62+
})
63+
}

test/unit/plugins/validate-semantic/2and3/schemas.js

+346
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,349 @@ describe(`validation plugin - semantic - 2and3 schemas`, () => {
169169
})
170170
})
171171
})
172+
173+
describe(`values in Enum must be instance of the defined type`, () => {
174+
// Numbers tests
175+
it("should return an error for a text value in a enum number type in OpenApi 3", () => {
176+
const spec = {
177+
openapi: "3.0.0",
178+
"paths": {
179+
"/pets": {
180+
"get": {
181+
"parameters": [
182+
{
183+
name: "number",
184+
in: "query",
185+
schema: {
186+
type: "number",
187+
enum: [1, "text", 3]
188+
}
189+
},
190+
]
191+
}
192+
}
193+
}
194+
}
195+
196+
return validateHelper(spec)
197+
.then(system => {
198+
const allErrors = system.errSelectors.allErrors().toJS()
199+
expect(allErrors.length).toEqual(1)
200+
expect(allErrors[0]).toInclude({
201+
level: "warning",
202+
message: "enum value should conform to its schema's `type`",
203+
path: ["paths", "/pets", "get", "parameters", "0", "schema", "enum", 1]
204+
})
205+
})
206+
})
207+
208+
it("should return an error for a number value inside quotes in a enum number type in OpenApi 3", () => {
209+
const spec = {
210+
openapi: "3.0.0",
211+
"paths": {
212+
"/pets": {
213+
"get": {
214+
"parameters": [
215+
{
216+
name: "number",
217+
in: "query",
218+
schema: {
219+
type: "number",
220+
enum: [1, 2, "3"]
221+
}
222+
},
223+
]
224+
}
225+
}
226+
}
227+
}
228+
229+
return validateHelper(spec)
230+
.then(system => {
231+
const allErrors = system.errSelectors.allErrors().toJS()
232+
expect(allErrors.length).toEqual(1)
233+
expect(allErrors[0]).toInclude({
234+
level: "warning",
235+
message: "enum value should conform to its schema's `type`",
236+
path: ["paths", "/pets", "get", "parameters", "0", "schema", "enum", 2]
237+
})
238+
})
239+
})
240+
241+
it("should not return an error when all items are number in a enum number type in OpenApi 3", () => {
242+
const spec = {
243+
openapi: "3.0.0",
244+
"paths": {
245+
"/pets": {
246+
"get": {
247+
"parameters": [
248+
{
249+
name: "number",
250+
in: "query",
251+
schema: {
252+
type: "number",
253+
enum: [1, 2, 3]
254+
}
255+
},
256+
]
257+
}
258+
}
259+
}
260+
}
261+
262+
return validateHelper(spec)
263+
.then(system => {
264+
const allErrors = system.errSelectors.allErrors().toJS()
265+
expect(allErrors.length).toEqual(0)
266+
})
267+
})
268+
269+
//Array Tests
270+
271+
it("should return an error for a non array value in a enum array type in OpenApi 3", () => {
272+
const spec = {
273+
openapi: "3.0.0",
274+
"paths": {
275+
"/pets": {
276+
"get": {
277+
"parameters": [
278+
{
279+
name: "arraySample",
280+
in: "query",
281+
schema: {
282+
type: "array",
283+
enum: [1, 2, 3],
284+
items: {
285+
type: "array",
286+
items: {
287+
type: "number"
288+
}
289+
}
290+
}
291+
},
292+
]
293+
}
294+
}
295+
}
296+
}
297+
298+
return validateHelper(spec)
299+
.then(system => {
300+
const allErrors = system.errSelectors.allErrors().toJS().filter((err => err.source !== ""))
301+
expect(allErrors.length).toEqual(3)
302+
expect(allErrors[0]).toInclude({
303+
level: "warning",
304+
message: "enum value should conform to its schema's `type`",
305+
path: ["paths", "/pets", "get", "parameters", "0", "schema", "enum", 0]
306+
})
307+
expect(allErrors[1]).toInclude({
308+
level: "warning",
309+
message: "enum value should conform to its schema's `type`",
310+
path: ["paths", "/pets", "get", "parameters", "0", "schema", "enum", 1]
311+
})
312+
expect(allErrors[2]).toInclude({
313+
level: "warning",
314+
message: "enum value should conform to its schema's `type`",
315+
path: ["paths", "/pets", "get", "parameters", "0", "schema", "enum", 2]
316+
})
317+
})
318+
})
319+
320+
it("should not return a type error when all items are array in a enum array type in OpenApi 3", () => {
321+
const spec = {
322+
openapi: "3.0.0",
323+
"paths": {
324+
"/pets": {
325+
"get": {
326+
"parameters": [
327+
{
328+
name: "arraySample",
329+
in: "query",
330+
schema: {
331+
type: "array",
332+
enum: [[1,2],[3,4]],
333+
items: {
334+
type: "number"
335+
}
336+
}
337+
},
338+
]
339+
}
340+
}
341+
}
342+
}
343+
344+
return validateHelper(spec)
345+
.then(system => {
346+
const allErrors = system.errSelectors.allErrors().toJS()
347+
expect(allErrors.length).toEqual(0)
348+
})
349+
})
350+
351+
//Object Tests
352+
353+
it("should return an error for a non object value (array) in a enum object type in OpenApi 3", () => {
354+
const spec = {
355+
openapi: "3.0.0",
356+
"paths": {
357+
"/pets": {
358+
"get": {
359+
"parameters": [
360+
{
361+
name: "objectSample",
362+
in: "query",
363+
schema: {
364+
type: "object",
365+
enum: [[1,3], 2, 3]
366+
}
367+
},
368+
]
369+
}
370+
}
371+
}
372+
}
373+
374+
return validateHelper(spec)
375+
.then(system => {
376+
const allErrors = system.errSelectors.allErrors().toJS()
377+
expect(allErrors.length).toEqual(3)
378+
expect(allErrors[0]).toInclude({
379+
level: "warning",
380+
message: "enum value should conform to its schema's `type`",
381+
path: ["paths", "/pets", "get", "parameters", "0", "schema", "enum", 0]
382+
})
383+
expect(allErrors[1]).toInclude({
384+
level: "warning",
385+
message: "enum value should conform to its schema's `type`",
386+
path: ["paths", "/pets", "get", "parameters", "0", "schema", "enum", 1]
387+
})
388+
expect(allErrors[2]).toInclude({
389+
level: "warning",
390+
message: "enum value should conform to its schema's `type`",
391+
path: ["paths", "/pets", "get", "parameters", "0", "schema", "enum", 2]
392+
})
393+
})
394+
})
395+
396+
it("should return an error for a null value in a enum object type in OpenApi 3", () => {
397+
const spec = {
398+
openapi: "3.0.0",
399+
"paths": {
400+
"/pets": {
401+
"get": {
402+
"parameters": [
403+
{
404+
name: "objectSample",
405+
in: "query",
406+
schema: {
407+
type: "object",
408+
enum: [null]
409+
}
410+
},
411+
]
412+
}
413+
}
414+
}
415+
}
416+
417+
return validateHelper(spec)
418+
.then(system => {
419+
const allErrors = system.errSelectors.allErrors().toJS()
420+
expect(allErrors[0]).toInclude({
421+
level: "warning",
422+
message: "enum value should conform to its schema's `type`",
423+
path: ["paths", "/pets", "get", "parameters", "0", "schema", "enum", 0]
424+
})
425+
})
426+
})
427+
428+
it("should not return an error when all items are array in a enum array type in OpenApi 3", () => {
429+
const spec = {
430+
openapi: "3.0.0",
431+
"paths": {
432+
"/pets": {
433+
"get": {
434+
"parameters": [
435+
{
436+
name: "objectSample",
437+
in: "query",
438+
schema: {
439+
type: "object",
440+
enum: [{ok: "Sample"},{}]
441+
}
442+
},
443+
]
444+
}
445+
}
446+
}
447+
}
448+
449+
return validateHelper(spec)
450+
.then(system => {
451+
const allErrors = system.errSelectors.allErrors().toJS()
452+
expect(allErrors.length).toEqual(0)
453+
})
454+
})
455+
456+
//Boolean Tests
457+
458+
it("should return an error for a non boolean value in a boolean array type in OpenApi 3", () => {
459+
const spec = {
460+
openapi: "3.0.0",
461+
"paths": {
462+
"/pets": {
463+
"get": {
464+
"parameters": [
465+
{
466+
name: "booleanEnum",
467+
in: "query",
468+
schema: {
469+
type: "boolean",
470+
enum: [1, true, false]
471+
}
472+
},
473+
]
474+
}
475+
}
476+
}
477+
}
478+
479+
return validateHelper(spec)
480+
.then(system => {
481+
const allErrors = system.errSelectors.allErrors().toJS()
482+
expect(allErrors[0]).toInclude({
483+
level: "warning",
484+
message: "enum value should conform to its schema's `type`",
485+
path: ["paths", "/pets", "get", "parameters", "0", "schema", "enum", 0]
486+
})
487+
})
488+
})
489+
490+
it("should not return an error when all items are boolean in a boolean array type in OpenApi 3", () => {
491+
const spec = {
492+
openapi: "3.0.0",
493+
"paths": {
494+
"/pets": {
495+
"get": {
496+
"parameters": [
497+
{
498+
name: "booleanEnum",
499+
in: "query",
500+
schema: {
501+
type: "boolean",
502+
enum: [true, false]
503+
}
504+
},
505+
]
506+
}
507+
}
508+
}
509+
}
510+
511+
return validateHelper(spec)
512+
.then(system => {
513+
const allErrors = system.errSelectors.allErrors().toJS()
514+
expect(allErrors.length).toEqual(0)
515+
})
516+
})
517+
})

0 commit comments

Comments
 (0)