Skip to content

Commit 274bea4

Browse files
mokkabonnaMartin Hansen
authored andcommitted
WIP if-then-else
1 parent 7edaab5 commit 274bea4

File tree

2 files changed

+335
-6
lines changed

2 files changed

+335
-6
lines changed

src/index.js

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ var without = require('lodash/without')
1818
var withoutArr = (arr, ...rest) => without.apply(null, [arr].concat(flatten(rest)))
1919
var isPropertyRelated = (key) => contains(propertyRelated, key)
2020
var isItemsRelated = (key) => contains(itemsRelated, key)
21+
var isConditionalRelated = (key) => contains(conditonalRelated, key)
2122
var contains = (arr, val) => arr.indexOf(val) !== -1
2223
var isEmptySchema = (obj) => (!keys(obj).length) && obj !== false && obj !== true
2324
var isSchema = (val) => isPlainObject(val) || val === true || val === false
@@ -174,9 +175,14 @@ function callGroupResolver(keys, resolverName, schemas, mergeSchemas, options, p
174175
}, {})
175176
}).filter(notUndefined), compare)
176177

177-
var related = resolverName === 'properties'
178-
? propertyRelated
179-
: itemsRelated
178+
const map = {
179+
properties: propertyRelated,
180+
items: itemsRelated,
181+
if: conditonalRelated
182+
}
183+
184+
const isIf = resolverName === 'if'
185+
const related = map[resolverName]
180186

181187
var mergers = related.reduce(function(all, key) {
182188
if (contains(schemaGroupProps, key)) {
@@ -202,7 +208,11 @@ function callGroupResolver(keys, resolverName, schemas, mergeSchemas, options, p
202208
throwIncompatible(compacted, parents.concat(resolverName))
203209
}
204210

205-
return cleanupReturnValue(result)
211+
if (isIf) {
212+
return result
213+
} else {
214+
return cleanupReturnValue(result)
215+
}
206216
}
207217
}
208218

@@ -244,6 +254,7 @@ function createRequiredMetaArray(arr) {
244254

245255
var propertyRelated = ['properties', 'patternProperties', 'additionalProperties']
246256
var itemsRelated = ['items', 'additionalItems']
257+
var conditonalRelated = ['if', 'then', 'else']
247258
var schemaGroupProps = ['properties', 'patternProperties', 'definitions', 'dependencies']
248259
var schemaArrays = ['anyOf', 'oneOf']
249260
var schemaProps = [
@@ -402,6 +413,27 @@ var defaultResolvers = {
402413
if (enums.length) {
403414
return sortBy(enums)
404415
}
416+
},
417+
if(values, props, mergers, options) {
418+
const allWithConditional = values.filter(schema =>
419+
conditonalRelated.some(keyword => schema.hasOwnProperty(keyword)))
420+
421+
// merge sub schemas completely
422+
// if,then,else must not be merged to the base schema, but if they contain allOf themselves, that should be merged
423+
function merge(schema) {
424+
const obj = {}
425+
if (schema.hasOwnProperty('if')) obj.if = mergers.if([schema.if])
426+
if (schema.hasOwnProperty('then')) obj.then = mergers.then([schema.then])
427+
if (schema.hasOwnProperty('else')) obj.else = mergers.else([schema.else])
428+
return obj
429+
}
430+
431+
// first schema with any of the 3 keywords is used as base
432+
const first = merge(allWithConditional.shift())
433+
return allWithConditional.reduce((all, schema) => {
434+
all.allOf = (all.allOf || []).concat(merge(schema))
435+
return all
436+
}, first)
405437
}
406438
}
407439

@@ -475,6 +507,9 @@ function merger(rootSchema, options, totalSchemas) {
475507
var itemKeys = allKeys.filter(isItemsRelated)
476508
pullAll(allKeys, itemKeys)
477509

510+
var conditonalKeys = allKeys.filter(isConditionalRelated)
511+
pullAll(allKeys, conditonalKeys)
512+
478513
allKeys.forEach(function(key) {
479514
var values = getValues(schemas, key)
480515
var compacted = uniqWith(values.filter(notUndefined), compareProp(key))
@@ -506,10 +541,12 @@ function merger(rootSchema, options, totalSchemas) {
506541
}
507542

508543
var calledWithArray = false
509-
merged[key] = resolver(compacted, parents.concat(key), merger, options, function(unresolvedSchemas) {
544+
const reportUnresolved = unresolvedSchemas => {
510545
calledWithArray = Array.isArray(unresolvedSchemas)
511546
return addToAllOf(unresolvedSchemas)
512-
})
547+
}
548+
549+
merged[key] = resolver(compacted, parents.concat(key), merger, options, reportUnresolved)
513550

514551
if (merged[key] === undefined && !calledWithArray) {
515552
throwIncompatible(compacted, parents.concat(key))
@@ -521,6 +558,7 @@ function merger(rootSchema, options, totalSchemas) {
521558

522559
Object.assign(merged, callGroupResolver(propertyKeys, 'properties', schemas, mergeSchemas, options, parents))
523560
Object.assign(merged, callGroupResolver(itemKeys, 'items', schemas, mergeSchemas, options, parents))
561+
Object.assign(merged, callGroupResolver(conditonalKeys, 'if', schemas, mergeSchemas, options, parents))
524562

525563
function addToAllOf(unresolvedSchemas) {
526564
merged.allOf = mergeWithArray(merged.allOf, unresolvedSchemas)

test/specs/if-then-else.spec.js

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
var chai = require('chai')
2+
var merger = require('../../src')
3+
var sinon = require('sinon')
4+
var _ = require('lodash')
5+
var expect = chai.expect
6+
var Ajv = require('ajv').default
7+
8+
var ajv = new Ajv()
9+
describe.only('if then else', function() {
10+
it('moves the if then else to the base schema if none there', () => {
11+
const result = merger({
12+
allOf: [{
13+
if: {
14+
required: ['prop1']
15+
},
16+
then: {},
17+
else: {}
18+
}]
19+
})
20+
21+
expect(result).to.eql({
22+
if: {
23+
required: ['prop1']
24+
},
25+
then: {},
26+
else: {}
27+
})
28+
})
29+
30+
it('does NOT move the if then else to the base schema if something already there', () => {
31+
const result = merger({
32+
if: {
33+
minimum: 5
34+
},
35+
then: {
36+
maximum: 2
37+
},
38+
else: {
39+
maximum: 10
40+
},
41+
allOf: [{
42+
if: {
43+
required: ['prop1']
44+
},
45+
then: {},
46+
else: {}
47+
}]
48+
})
49+
50+
expect(result).to.eql({
51+
if: {
52+
minimum: 5
53+
},
54+
then: {
55+
maximum: 2
56+
},
57+
else: {
58+
maximum: 10
59+
},
60+
allOf: [{
61+
if: {
62+
required: ['prop1']
63+
},
64+
then: {},
65+
else: {}
66+
}]
67+
})
68+
})
69+
70+
it('moves the unaffected keywords to the base schema', () => {
71+
const result = merger({
72+
properties: {
73+
name: {
74+
type: 'string',
75+
minLength: 3
76+
}
77+
},
78+
if: {
79+
minimum: 5
80+
},
81+
then: {
82+
maximum: 2
83+
},
84+
else: {
85+
maximum: 10
86+
},
87+
allOf: [{
88+
properties: {
89+
name: {
90+
type: 'string',
91+
minLength: 5
92+
}
93+
},
94+
if: {
95+
required: ['prop1']
96+
},
97+
then: {},
98+
else: {}
99+
}]
100+
})
101+
102+
expect(result).to.eql({
103+
properties: {
104+
name: {
105+
type: 'string',
106+
minLength: 5
107+
}
108+
},
109+
if: {
110+
minimum: 5
111+
},
112+
then: {
113+
maximum: 2
114+
},
115+
else: {
116+
maximum: 10
117+
},
118+
allOf: [{
119+
if: {
120+
required: ['prop1']
121+
},
122+
then: {},
123+
else: {}
124+
}]
125+
})
126+
})
127+
128+
it('should not move to base schema if only some keywords are not present', () => {
129+
const result = merger({
130+
else: false,
131+
allOf: [{
132+
if: {
133+
required: ['prop1']
134+
},
135+
then: {},
136+
else: {}
137+
}]
138+
})
139+
140+
expect(result).to.eql({
141+
else: false,
142+
allOf: [{
143+
if: {
144+
required: ['prop1']
145+
},
146+
then: {},
147+
else: {}
148+
}]
149+
})
150+
151+
const result2 = merger({
152+
then: false,
153+
allOf: [{
154+
if: {
155+
required: ['prop1']
156+
},
157+
then: {},
158+
else: {}
159+
}]
160+
})
161+
162+
expect(result2).to.eql({
163+
then: false,
164+
allOf: [{
165+
if: {
166+
required: ['prop1']
167+
},
168+
then: {},
169+
else: {}
170+
}]
171+
})
172+
173+
const result3 = merger({
174+
if: false,
175+
allOf: [{
176+
if: {
177+
required: ['prop1']
178+
},
179+
then: {},
180+
else: {}
181+
}]
182+
})
183+
184+
expect(result3).to.eql({
185+
if: false,
186+
allOf: [{
187+
if: {
188+
required: ['prop1']
189+
},
190+
then: {},
191+
else: {}
192+
}]
193+
})
194+
})
195+
196+
it('works with undefined value, it is as if not there. NOT the same as empty schema', () => {
197+
const result = merger({
198+
if: undefined,
199+
then: undefined,
200+
else: undefined,
201+
allOf: [{
202+
if: {
203+
required: ['prop1']
204+
},
205+
then: {},
206+
else: {}
207+
}]
208+
})
209+
210+
expect(result).to.eql({
211+
if: {
212+
required: ['prop1']
213+
},
214+
then: {},
215+
else: {}
216+
})
217+
})
218+
219+
it('removes empty allOf', () => {
220+
const result = merger({
221+
if: {
222+
required: ['prop1']
223+
},
224+
then: {},
225+
else: {},
226+
allOf: [{
227+
properties: {
228+
name: {
229+
type: 'string'
230+
}
231+
}
232+
}]
233+
})
234+
235+
expect(result).to.eql({
236+
properties: {
237+
name: {
238+
type: 'string'
239+
}
240+
},
241+
if: {
242+
required: ['prop1']
243+
},
244+
then: {},
245+
else: {}
246+
})
247+
})
248+
249+
it('works with resolver that does not manage to resolve it\'s schemas', () => {
250+
const result = merger({
251+
required: ['123'],
252+
if: {},
253+
then: {},
254+
else: {},
255+
allOf: [{
256+
required: ['234'],
257+
if: {
258+
required: ['prop1']
259+
},
260+
then: {},
261+
else: {}
262+
}]
263+
264+
}, {
265+
resolvers: {
266+
foo(values, paths, mergeSchemas, options, reportUnresolved) {
267+
var key = paths.pop()
268+
reportUnresolved(values.map((val) => {
269+
return {
270+
[key]: val
271+
}
272+
}))
273+
}
274+
}
275+
})
276+
277+
expect(result).to.eql({
278+
required: ['123', '234'],
279+
if: {},
280+
then: {},
281+
else: {},
282+
allOf: [{
283+
if: {
284+
required: ['prop1']
285+
},
286+
then: {},
287+
else: {}
288+
}]
289+
})
290+
})
291+
})

0 commit comments

Comments
 (0)