Skip to content

Commit 32842ed

Browse files
Add support for array of types in schema definition
1 parent 682cf04 commit 32842ed

File tree

2 files changed

+234
-2
lines changed

2 files changed

+234
-2
lines changed

index.js

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ function build (schema, options) {
9090

9191
var dependencies = []
9292
var dependenciesName = []
93-
if (hasAnyOf(schema)) {
93+
if (hasAnyOf(schema) || hasArrayOfTypes(schema)) {
9494
dependencies.push(new Ajv())
9595
dependenciesName.push('ajv')
9696
}
@@ -113,6 +113,40 @@ function hasAnyOf (schema) {
113113
return false
114114
}
115115

116+
function hasArrayOfTypes (schema) {
117+
if (Array.isArray(schema.type)) { return true }
118+
var i
119+
120+
if (schema.type === 'object') {
121+
if (schema.properties) {
122+
var propertyKeys = Object.keys(schema.properties)
123+
for (i = 0; i < propertyKeys.length; i++) {
124+
if (hasArrayOfTypes(schema.properties[propertyKeys[i]])) {
125+
return true
126+
}
127+
}
128+
}
129+
} else if (schema.type === 'array') {
130+
if (Array.isArray(schema.items)) {
131+
for (i = 0; i < schema.items.length; i++) {
132+
if (hasArrayOfTypes(schema.items[i])) {
133+
return true
134+
}
135+
}
136+
} else if (schema.items) {
137+
return hasArrayOfTypes(schema.items)
138+
}
139+
} else if (Array.isArray(schema.anyOf)) {
140+
for (i = 0; i < schema.anyOf.length; i++) {
141+
if (hasArrayOfTypes(schema.anyOf[i])) {
142+
return true
143+
}
144+
}
145+
}
146+
147+
return false
148+
}
149+
116150
function $asNull () {
117151
return 'null'
118152
}
@@ -517,6 +551,7 @@ function buildArray (schema, code, name, externalSchema, fullSchema) {
517551
condition += `Array.isArray(obj${accessor})`
518552
break
519553
default:
554+
// TODO: item.type can be an array of types.
520555
throw new Error(`${item.type} unsupported`)
521556
}
522557
return {
@@ -622,7 +657,19 @@ function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKe
622657
} else throw new Error(`${schema} unsupported`)
623658
break
624659
default:
625-
throw new Error(`${type} unsupported`)
660+
if (Array.isArray(type)) {
661+
type.forEach((type, index) => {
662+
var tempSchema = {type: type}
663+
var nestedResult = nested(laterCode, name, key, tempSchema, externalSchema, fullSchema, subKey)
664+
code += `
665+
${index === 0 ? 'if' : 'else if'}(ajv.validate(${require('util').inspect(tempSchema, {depth: null})}, obj${accessor}))
666+
${nestedResult.code}
667+
`
668+
laterCode = nestedResult.laterCode
669+
})
670+
} else {
671+
throw new Error(`${type} unsupported`)
672+
}
626673
}
627674

628675
return {

test/typesArray.test.js

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
'use strict'
2+
3+
const test = require('tap').test
4+
const build = require('..')
5+
6+
test('simple object with multi-type property', (t) => {
7+
t.plan(2)
8+
9+
const schema = {
10+
title: 'simple object with multi-type property',
11+
type: 'object',
12+
properties: {
13+
stringOrNumber: {
14+
type: ['string', 'number']
15+
}
16+
}
17+
}
18+
const stringify = build(schema)
19+
20+
try {
21+
const value = stringify({
22+
stringOrNumber: 'string'
23+
})
24+
t.is(value, '{"stringOrNumber":"string"}')
25+
} catch (e) {
26+
t.fail()
27+
}
28+
29+
try {
30+
const value = stringify({
31+
stringOrNumber: 42
32+
})
33+
t.is(value, '{"stringOrNumber":42}')
34+
} catch (e) {
35+
t.fail()
36+
}
37+
})
38+
39+
test('object with array of multiple types', (t) => {
40+
t.plan(3)
41+
42+
const schema = {
43+
title: 'object with array of multiple types',
44+
type: 'object',
45+
properties: {
46+
arrayOfStringsAndNumbers: {
47+
type: 'array',
48+
items: {
49+
type: ['string', 'number']
50+
}
51+
}
52+
}
53+
}
54+
const stringify = build(schema)
55+
56+
try {
57+
const value = stringify({
58+
arrayOfStringsAndNumbers: ['string1', 'string2']
59+
})
60+
t.is(value, '{"arrayOfStringsAndNumbers":["string1","string2"]}')
61+
} catch (e) {
62+
console.log(e)
63+
t.fail()
64+
}
65+
66+
try {
67+
const value = stringify({
68+
arrayOfStringsAndNumbers: [42, 7]
69+
})
70+
t.is(value, '{"arrayOfStringsAndNumbers":[42,7]}')
71+
} catch (e) {
72+
t.fail()
73+
}
74+
75+
try {
76+
const value = stringify({
77+
arrayOfStringsAndNumbers: ['string1', 42, 7, 'string2']
78+
})
79+
t.is(value, '{"arrayOfStringsAndNumbers":["string1",42,7,"string2"]}')
80+
} catch (e) {
81+
t.fail()
82+
}
83+
})
84+
85+
// TODO: Multiple types inside tuples are currently unsupported
86+
// test('object with tuple of multiple types', (t) => {
87+
// t.plan(2)
88+
89+
// const schema = {
90+
// title: 'object with array of multiple types',
91+
// type: 'object',
92+
// properties: {
93+
// fixedTupleOfStringsAndNumbers: {
94+
// type: 'array',
95+
// items: [
96+
// {
97+
// type: 'string'
98+
// },
99+
// {
100+
// type: 'number'
101+
// },
102+
// {
103+
// type: ['string', 'number']
104+
// }
105+
// ]
106+
// }
107+
// }
108+
// }
109+
// const stringify = build(schema)
110+
111+
// try {
112+
// const value = stringify({
113+
// fixedTupleOfStringsAndNumbers: ['string1', 42, 7]
114+
// })
115+
// t.is(value, '{"fixedTupleOfStringsAndNumbers":["string1",42,7]}')
116+
// } catch (e) {
117+
// console.log(e)
118+
// t.fail()
119+
// }
120+
121+
// try {
122+
// const value = stringify({
123+
// fixedTupleOfStringsAndNumbers: ['string1', 42, 7]
124+
// })
125+
// t.is(value, '{"fixedTupleOfStringsAndNumbers":["string1",42,"string2"]}')
126+
// } catch (e) {
127+
// console.log(e)
128+
// t.fail()
129+
// }
130+
// })
131+
132+
test('object with anyOf and multiple types', (t) => {
133+
t.plan(3)
134+
135+
const schema = {
136+
title: 'object with anyOf and multiple types',
137+
type: 'object',
138+
properties: {
139+
objectOrBoolean: {
140+
anyOf: [
141+
{
142+
type: 'object',
143+
properties: {
144+
stringOrNumber: {
145+
type: ['string', 'number']
146+
}
147+
}
148+
},
149+
{
150+
type: 'boolean'
151+
}
152+
]
153+
}
154+
}
155+
}
156+
const stringify = build(schema)
157+
158+
try {
159+
const value = stringify({
160+
objectOrBoolean: { stringOrNumber: 'string' }
161+
})
162+
t.is(value, '{"objectOrBoolean":{"stringOrNumber":"string"}}')
163+
} catch (e) {
164+
console.log(e)
165+
t.fail()
166+
}
167+
168+
try {
169+
const value = stringify({
170+
objectOrBoolean: { stringOrNumber: 42 }
171+
})
172+
t.is(value, '{"objectOrBoolean":{"stringOrNumber":42}}')
173+
} catch (e) {
174+
t.fail()
175+
}
176+
177+
try {
178+
const value = stringify({
179+
objectOrBoolean: true
180+
})
181+
t.is(value, '{"objectOrBoolean":true}')
182+
} catch (e) {
183+
t.fail()
184+
}
185+
})

0 commit comments

Comments
 (0)