Skip to content

Commit 2e2d753

Browse files
feat: add required check for object key not in properties (#226)
* feat: add required check for object key not in properties * perf: remove split, creates the required list of array fields at build time * refactor: remove message fail. Add string interpolation
1 parent 6b252a7 commit 2e2d753

File tree

4 files changed

+205
-2
lines changed

4 files changed

+205
-2
lines changed

index.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,8 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
631631
schema = refFinder(schema.$ref, fullSchema, externalSchema)
632632
}
633633

634+
var required = schema.required
635+
634636
Object.keys(schema.properties || {}).forEach((key, i, a) => {
635637
if (schema.properties[key].$ref) {
636638
// if the schema object is deep in the tree, we must resolve the ref in the parent scope
@@ -709,12 +711,14 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
709711

710712
var defaultValue = schema.properties[key].default
711713
if (defaultValue !== undefined) {
714+
required = filterRequired(schema.required, key)
712715
code += `
713716
} else {
714717
${addComma}
715718
json += '${asString}:${sanitizeKey(JSON.stringify(defaultValue).replace(/\\/g, '\\\\'))}'
716719
`
717720
} else if (schema.required && schema.required.indexOf(key) !== -1) {
721+
required = filterRequired(schema.required, key)
718722
code += `
719723
} else {
720724
throw new Error('${sanitized} is required!')
@@ -731,9 +735,33 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
731735
`
732736
}
733737
})
738+
739+
if (required && required.length > 0) {
740+
code += 'var required = ['
741+
for (var i = 0; i < required.length; i++) {
742+
if (i > 0) {
743+
code += ','
744+
}
745+
code += `${$asString(required[i]).replace(/\\/g, '\\\\')}`
746+
}
747+
code += ']'
748+
code += `
749+
for (var i = 0; i < required.length; i++) {
750+
if (obj[required[i]] === undefined) throw new Error(required[i] + ' is required!')
751+
}
752+
`
753+
}
754+
734755
return { code: code, laterCode: laterCode }
735756
}
736757

758+
function filterRequired (required, key) {
759+
if (!required) {
760+
return required
761+
}
762+
return required.filter(k => k !== key)
763+
}
764+
737765
function buildCodeWithAllOfs (schema, code, laterCode, name, externalSchema, fullSchema) {
738766
if (schema.allOf) {
739767
schema.allOf.forEach((ss) => {

test/allof.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ test('object with nested allOfs', (t) => {
135135
allOf: [
136136
{
137137
required: [
138-
'id'
138+
'id1'
139139
],
140140
type: 'object',
141141
properties: {

test/ref.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,7 @@ test('ref external to relative definition', (t) => {
820820

821821
const schema = {
822822
type: 'object',
823-
required: ['foo'],
823+
required: ['fooParent'],
824824
properties: {
825825
fooParent: { $ref: 'relative:to:local' }
826826
}

test/required.test.js

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,181 @@ test('object with required field', (t) => {
4141
}
4242
})
4343

44+
test('object with required field not in properties schema', (t) => {
45+
t.plan(4)
46+
47+
const schema = {
48+
title: 'object with required field',
49+
type: 'object',
50+
properties: {
51+
num: {
52+
type: 'integer'
53+
}
54+
},
55+
required: ['str']
56+
}
57+
const stringify = build(schema)
58+
59+
try {
60+
stringify({})
61+
t.fail()
62+
} catch (e) {
63+
t.is(e.message, 'str is required!')
64+
t.pass()
65+
}
66+
67+
try {
68+
stringify({
69+
num: 42
70+
})
71+
t.fail()
72+
} catch (e) {
73+
t.is(e.message, 'str is required!')
74+
t.pass()
75+
}
76+
})
77+
78+
test('object with required field not in properties schema with additional properties true', (t) => {
79+
t.plan(4)
80+
81+
const schema = {
82+
title: 'object with required field',
83+
type: 'object',
84+
properties: {
85+
num: {
86+
type: 'integer'
87+
}
88+
},
89+
additionalProperties: true,
90+
required: ['str']
91+
}
92+
const stringify = build(schema)
93+
94+
try {
95+
stringify({})
96+
t.fail()
97+
} catch (e) {
98+
t.is(e.message, 'str is required!')
99+
t.pass()
100+
}
101+
102+
try {
103+
stringify({
104+
num: 42
105+
})
106+
t.fail()
107+
} catch (e) {
108+
t.is(e.message, 'str is required!')
109+
t.pass()
110+
}
111+
})
112+
113+
test('object with multiple required field not in properties schema', (t) => {
114+
t.plan(6)
115+
116+
const schema = {
117+
title: 'object with required field',
118+
type: 'object',
119+
properties: {
120+
num: {
121+
type: 'integer'
122+
}
123+
},
124+
additionalProperties: true,
125+
required: ['num', 'key1', 'key2']
126+
}
127+
const stringify = build(schema)
128+
129+
try {
130+
stringify({})
131+
t.fail()
132+
} catch (e) {
133+
t.is(e.message, 'num is required!')
134+
t.pass()
135+
}
136+
137+
try {
138+
stringify({
139+
num: 42
140+
})
141+
t.fail()
142+
} catch (e) {
143+
t.is(e.message, 'key1 is required!')
144+
t.pass()
145+
}
146+
147+
try {
148+
stringify({
149+
num: 42,
150+
key1: 'some'
151+
})
152+
t.fail()
153+
} catch (e) {
154+
t.is(e.message, 'key2 is required!')
155+
t.pass()
156+
}
157+
})
158+
159+
test('object with required bool', (t) => {
160+
t.plan(3)
161+
162+
const schema = {
163+
title: 'object with required field',
164+
type: 'object',
165+
properties: {
166+
num: {
167+
type: 'integer'
168+
}
169+
},
170+
additionalProperties: true,
171+
required: ['bool']
172+
}
173+
const stringify = build(schema)
174+
175+
try {
176+
stringify({})
177+
t.fail()
178+
} catch (e) {
179+
t.is(e.message, 'bool is required!')
180+
t.pass()
181+
}
182+
183+
try {
184+
stringify({
185+
bool: false
186+
})
187+
t.pass()
188+
} catch (e) {
189+
t.fail(e.message)
190+
}
191+
})
192+
193+
test('required nullable', (t) => {
194+
t.plan(1)
195+
196+
const schema = {
197+
title: 'object with required field',
198+
type: 'object',
199+
properties: {
200+
num: {
201+
type: ['integer']
202+
}
203+
},
204+
additionalProperties: true,
205+
required: ['null']
206+
}
207+
const stringify = build(schema)
208+
209+
try {
210+
stringify({
211+
null: null
212+
})
213+
t.pass()
214+
} catch (e) {
215+
t.fail(e.message)
216+
}
217+
})
218+
44219
test('required numbers', (t) => {
45220
t.plan(3)
46221

0 commit comments

Comments
 (0)