json-2-joi 2.0.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +62 -2
- package/lib/resolver.js +117 -76
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
### 2.1.0
|
|
2
|
+
|
|
3
|
+
- Fix the issue that `enum` is only applied to `string` type or when missing type info.
|
|
4
|
+
- Add `strictEnum` and `enableEnum` option.
|
|
5
|
+
- Allow override options when calling `convert` api.
|
|
6
|
+
|
|
1
7
|
### 2.0.2
|
|
2
8
|
|
|
3
9
|
- Remove `min` setting from JOI schema if `minLength` is not defined in JSON schema.
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Converts a JSON schema to a Joi schema for object validation.
|
|
|
6
6
|
|
|
7
7
|
## Change based on the forked version (enjoi):
|
|
8
8
|
|
|
9
|
-
- Provided `refineDescription(schema)`, `allowNull` options
|
|
9
|
+
- Provided `refineDescription(schema)`, `allowNull`, `strictEnum`, `enableEnum` options
|
|
10
10
|
- Change cycle reference resovling without self-generating id and rely on $id if defined in JSON schema
|
|
11
11
|
- Support `const` keyword in JSON schema
|
|
12
12
|
- Support `example` setting for Joi based on JSON schema's `examples`, `default` or `enum`
|
|
@@ -53,7 +53,9 @@ Please file issues for other unsupported features.
|
|
|
53
53
|
- `refineType(type, format)` - an (optional) function to call to apply to type based on the type and format of the JSON schema.
|
|
54
54
|
- `refineSchema(joiSchema, jsonSchema)` - an (optional) function to call to apply to adjust Joi schema base on the original JSON schema. Primary use case is handling `nullable` flag in OpenAPI 3.0
|
|
55
55
|
- `extensions` - an array of extensions to pass [joi.extend](https://github.com/hapijs/joi/blob/master/API.md#extendextension).
|
|
56
|
-
- `allowNull` -
|
|
56
|
+
- `allowNull` - Default as `false`. When `true` and the field has no `enum` setting, `null` value will be allowed.
|
|
57
|
+
- `strictEnum` - Default as `true`. When `false`, and `allowNull` is `true`, `null` (and empty string additionally for string type) will be added to `enum` list.
|
|
58
|
+
- `enableEnum` - Default as `true`. When `false`, `enum` restriction will not be added to Joi Schema.
|
|
57
59
|
|
|
58
60
|
Example:
|
|
59
61
|
|
|
@@ -204,6 +206,64 @@ const lengthSchema = json2Joi.convert({
|
|
|
204
206
|
});
|
|
205
207
|
```
|
|
206
208
|
|
|
209
|
+
#### Override options during conversion
|
|
210
|
+
|
|
211
|
+
When reusing the Resolver and calling `convert` API, it's possible that different schema might have different requirement.
|
|
212
|
+
|
|
213
|
+
It's possible to override these options for each conversion:
|
|
214
|
+
|
|
215
|
+
* refineType
|
|
216
|
+
* refineSchema
|
|
217
|
+
* refineDescription
|
|
218
|
+
* allowNull
|
|
219
|
+
* strictEnum
|
|
220
|
+
* enableEnum
|
|
221
|
+
|
|
222
|
+
However, the overriden options do not have impact on the `subSchemas` as they are preprocessed during Resolver construction.
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
const subSchemas = {
|
|
226
|
+
measurement: {
|
|
227
|
+
type: 'object',
|
|
228
|
+
properties: {
|
|
229
|
+
quantity: {
|
|
230
|
+
type: 'number',
|
|
231
|
+
enum: [0, 1]
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
const enjoi = Enjoi.resolver({
|
|
237
|
+
subSchemas,
|
|
238
|
+
enableEnum: false
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
const jsonSchema = {
|
|
242
|
+
type: 'object',
|
|
243
|
+
properties: {
|
|
244
|
+
weight: {
|
|
245
|
+
type: 'object',
|
|
246
|
+
properties: {
|
|
247
|
+
quantity: {
|
|
248
|
+
type: 'number',
|
|
249
|
+
enum: [0, 1]
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
length: {
|
|
254
|
+
$ref: 'measurement'
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const schema1 = enjoi.convert(jsonSchema, { enableEnum: true });
|
|
260
|
+
const schema2 = enjoi.convert(jsonSchema);
|
|
261
|
+
|
|
262
|
+
t.ok(schema1.validate({ weight: { quantity: 2 } }).error, 'follow overridden options');
|
|
263
|
+
t.ok(!schema1.validate({ length: { quantity: 2 } }).error, 'no impact on subschemas');
|
|
264
|
+
t.ok(!schema2.validate({ weight: { quantity: 2 } }).error, 'still use original options');
|
|
265
|
+
```
|
|
266
|
+
|
|
207
267
|
### Custom Types
|
|
208
268
|
|
|
209
269
|
Custom types can be provided through the `extensions` option.
|
package/lib/resolver.js
CHANGED
|
@@ -20,9 +20,33 @@ const optionsSchema = Joi.object({
|
|
|
20
20
|
refineSchema: Joi.func().allow(null),
|
|
21
21
|
refineDescription: Joi.func().allow(null),
|
|
22
22
|
allowNull: Joi.boolean().default(false),
|
|
23
|
+
strictEnum: Joi.boolean().default(true),
|
|
24
|
+
enableEnum: Joi.boolean().default(true),
|
|
23
25
|
joiOptions: Joi.object().unknown(true).default({})
|
|
24
26
|
});
|
|
25
27
|
|
|
28
|
+
const overridableOptions = {
|
|
29
|
+
refineType: Joi.func().allow(null),
|
|
30
|
+
refineSchema: Joi.func().allow(null),
|
|
31
|
+
refineDescription: Joi.func().allow(null),
|
|
32
|
+
allowNull: Joi.boolean(),
|
|
33
|
+
strictEnum: Joi.boolean(),
|
|
34
|
+
enableEnum: Joi.boolean()
|
|
35
|
+
};
|
|
36
|
+
const overridableOptionKeys = Object.keys(overridableOptions);
|
|
37
|
+
const overrideOptionsSchema = Joi.object(overridableOptions);
|
|
38
|
+
|
|
39
|
+
function mergeOverridableOptions(target, ...sources) {
|
|
40
|
+
sources.forEach((src) => {
|
|
41
|
+
overridableOptionKeys.forEach((key) => {
|
|
42
|
+
if (typeof src[key] !== 'undefined') {
|
|
43
|
+
target[key] = src[key];
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
});
|
|
47
|
+
return target;
|
|
48
|
+
}
|
|
49
|
+
|
|
26
50
|
function isNumber(value) {
|
|
27
51
|
return typeof value === 'number';
|
|
28
52
|
}
|
|
@@ -37,11 +61,31 @@ function isNotEmptyObject(value) {
|
|
|
37
61
|
|
|
38
62
|
function allowNullIfNeeded(schema, allowNull = false) {
|
|
39
63
|
if (allowNull) {
|
|
64
|
+
const definition = schema.describe();
|
|
65
|
+
if (definition.flags && definition.flags.only && definition.allow) {
|
|
66
|
+
// Already has enum restriction
|
|
67
|
+
return schema;
|
|
68
|
+
}
|
|
40
69
|
return schema.allow(null);
|
|
41
70
|
}
|
|
42
71
|
return schema;
|
|
43
72
|
}
|
|
44
73
|
|
|
74
|
+
function addEnumRestriction(joiSchema, enumList, { allowNull = false, strictEnum = true, enableEnum = true }) {
|
|
75
|
+
if (!enableEnum || !Array.isArray(enumList) || enumList.length === 0) {
|
|
76
|
+
return joiSchema;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const valids = [].concat(enumList);
|
|
80
|
+
if (allowNull && !strictEnum) {
|
|
81
|
+
valids.push(null);
|
|
82
|
+
if (joiSchema.type === 'string') {
|
|
83
|
+
valids.push('');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return joiSchema.valid(...valids);
|
|
87
|
+
}
|
|
88
|
+
|
|
45
89
|
function normalizedId(id) {
|
|
46
90
|
return id.replace(/[^a-zA-Z0-9]/g, '_');
|
|
47
91
|
}
|
|
@@ -104,13 +148,10 @@ class SchemaResolver {
|
|
|
104
148
|
|
|
105
149
|
Hoek.assert(!validatedResult.error, validatedResult.error);
|
|
106
150
|
|
|
107
|
-
const { subSchemas = {},
|
|
151
|
+
const { subSchemas = {}, extensions = [], joiOptions } = validatedResult.value;
|
|
108
152
|
|
|
153
|
+
this.resolveOptions = mergeOverridableOptions({}, validatedResult.value);
|
|
109
154
|
this.subSchemas = subSchemas;
|
|
110
|
-
this.refineType = refineType;
|
|
111
|
-
this.refineDescription = refineDescription;
|
|
112
|
-
this.refineSchema = refineSchema;
|
|
113
|
-
this.allowNull = allowNull;
|
|
114
155
|
this.joiOptions = joiOptions;
|
|
115
156
|
this.joiSharedSchemas = new Map();
|
|
116
157
|
|
|
@@ -122,7 +163,7 @@ class SchemaResolver {
|
|
|
122
163
|
// shorthand schema type does not need to set $id
|
|
123
164
|
sharedSchema.$id = sharedSchemaId;
|
|
124
165
|
}
|
|
125
|
-
this.joiSharedSchemas.set(sharedSchemaId, this.resolve(sharedSchema));
|
|
166
|
+
this.joiSharedSchemas.set(sharedSchemaId, this.resolve(sharedSchema, this.resolveOptions));
|
|
126
167
|
}
|
|
127
168
|
|
|
128
169
|
const itr = this.joiSharedSchemas.keys();
|
|
@@ -132,26 +173,29 @@ class SchemaResolver {
|
|
|
132
173
|
}
|
|
133
174
|
}
|
|
134
175
|
|
|
135
|
-
resolveShareSchemas(schema,
|
|
176
|
+
resolveShareSchemas(schema, resolveOptions, sharedProperties = '$defs', baseUri = '') {
|
|
136
177
|
const localShared = schema[sharedProperties]
|
|
137
178
|
if (localShared) {
|
|
138
179
|
for (let sharedSchemaId in localShared) {
|
|
139
180
|
this.joiSharedSchemas.set(
|
|
140
181
|
`${baseUri}#/${sharedProperties}/${sharedSchemaId}`,
|
|
141
|
-
this.resolve(localShared[sharedSchemaId], baseUri)
|
|
182
|
+
this.resolve(localShared[sharedSchemaId], resolveOptions, baseUri)
|
|
142
183
|
);
|
|
143
184
|
}
|
|
144
185
|
}
|
|
145
186
|
}
|
|
146
187
|
|
|
147
|
-
convert(schema) {
|
|
188
|
+
convert(schema, options = {}) {
|
|
148
189
|
const validateSchema = schemaSchema.validate(schema);
|
|
190
|
+
const { value: overrideOptions, error: overrideOptionsError } = overrideOptionsSchema.validate(options);
|
|
149
191
|
|
|
150
192
|
Hoek.assert(!validateSchema.error, validateSchema.error);
|
|
193
|
+
Hoek.assert(!overrideOptionsError, overrideOptionsError);
|
|
151
194
|
|
|
152
195
|
const schemaId = schema.$id || '';
|
|
153
196
|
this.subSchemas[schemaId] = schema;
|
|
154
|
-
|
|
197
|
+
|
|
198
|
+
const joiSchema = this.resolve(schema, mergeOverridableOptions({}, this.resolveOptions, overrideOptions));
|
|
155
199
|
|
|
156
200
|
// Cleanup all shared schema registered other than the initial shared ones
|
|
157
201
|
delete this.subSchemas[schemaId];
|
|
@@ -165,7 +209,7 @@ class SchemaResolver {
|
|
|
165
209
|
return joiSchema;
|
|
166
210
|
}
|
|
167
211
|
|
|
168
|
-
resolve(schema, baseUri = (schema.$id || '')) {
|
|
212
|
+
resolve(schema, resolveOptions = {}, baseUri = (schema.$id || '')) {
|
|
169
213
|
if (this.joi.isSchema(schema)) {
|
|
170
214
|
return schema;
|
|
171
215
|
}
|
|
@@ -180,46 +224,48 @@ class SchemaResolver {
|
|
|
180
224
|
}
|
|
181
225
|
}
|
|
182
226
|
|
|
183
|
-
this.resolveShareSchemas(schema,
|
|
184
|
-
this.resolveShareSchemas(schema,
|
|
227
|
+
this.resolveShareSchemas(schema, resolveOptions, '$defs', baseUri);
|
|
228
|
+
this.resolveShareSchemas(schema, resolveOptions, 'definitions', baseUri);
|
|
185
229
|
|
|
186
230
|
if (typeof schema === 'string') {
|
|
187
231
|
// If schema is itself a string, interpret it as a type
|
|
188
|
-
resolvedSchema = this.resolveType({ type: schema });
|
|
232
|
+
resolvedSchema = this.resolveType({ type: schema }, resolveOptions, baseUri);
|
|
189
233
|
} else if (schema.$ref) {
|
|
190
234
|
if (schema.$ref.trim() === '#') {
|
|
191
235
|
return baseUri ? this.joi.link(`#${normalizedId(baseUri)}`) : this.joi.link('/');
|
|
192
236
|
}
|
|
193
237
|
|
|
194
|
-
resolvedSchema = this.resolve(this.resolveReference(schema.$ref, baseUri), baseUri);
|
|
238
|
+
resolvedSchema = this.resolve(this.resolveReference(schema.$ref, baseUri), resolveOptions, baseUri);
|
|
195
239
|
} else {
|
|
196
240
|
const partialSchemas = [];
|
|
197
241
|
if (schema.type) {
|
|
198
|
-
partialSchemas.push(this.resolveType(schema, baseUri));
|
|
242
|
+
partialSchemas.push(this.resolveType(schema, resolveOptions, baseUri));
|
|
199
243
|
} else if (schema.properties) {
|
|
200
244
|
// if no type is specified, just properties
|
|
201
|
-
partialSchemas.push(this.object(schema, baseUri))
|
|
245
|
+
partialSchemas.push(this.object(schema, resolveOptions, baseUri))
|
|
202
246
|
} else if (schema.format) {
|
|
203
247
|
// if no type is specified, just format
|
|
204
248
|
partialSchemas.push(this.string(schema))
|
|
205
249
|
} else if (schema.enum) {
|
|
206
250
|
// If no type is specified, just enum
|
|
207
|
-
partialSchemas.push(
|
|
251
|
+
partialSchemas.push(
|
|
252
|
+
addEnumRestriction(this.joi.any().example(schema.enum[0]), schema.enum, resolveOptions)
|
|
253
|
+
);
|
|
208
254
|
} else if (schema.const) {
|
|
209
255
|
// Constant value
|
|
210
256
|
partialSchemas.push(this.getValueJoiType(schema.const).valid(schema.const));
|
|
211
257
|
}
|
|
212
258
|
if (schema.anyOf) {
|
|
213
|
-
partialSchemas.push(this.resolveAnyOf(schema, baseUri));
|
|
259
|
+
partialSchemas.push(this.resolveAnyOf(schema, resolveOptions, baseUri));
|
|
214
260
|
}
|
|
215
261
|
if (schema.allOf) {
|
|
216
|
-
partialSchemas.push(this.resolveAllOf(schema, baseUri));
|
|
262
|
+
partialSchemas.push(this.resolveAllOf(schema, resolveOptions, baseUri));
|
|
217
263
|
}
|
|
218
264
|
if (schema.oneOf) {
|
|
219
|
-
partialSchemas.push(this.resolveOneOf(schema, baseUri));
|
|
265
|
+
partialSchemas.push(this.resolveOneOf(schema, resolveOptions, baseUri));
|
|
220
266
|
}
|
|
221
267
|
if (schema.not) {
|
|
222
|
-
partialSchemas.push(this.resolveNot(schema, baseUri));
|
|
268
|
+
partialSchemas.push(this.resolveNot(schema, resolveOptions, baseUri));
|
|
223
269
|
}
|
|
224
270
|
if (partialSchemas.length === 0) {
|
|
225
271
|
//Fall through to whatever.
|
|
@@ -231,8 +277,8 @@ class SchemaResolver {
|
|
|
231
277
|
resolvedSchema = partialSchemas.length === 1 ? partialSchemas[0] : this.joi.alternatives(partialSchemas).match('all');
|
|
232
278
|
}
|
|
233
279
|
|
|
234
|
-
if (
|
|
235
|
-
resolvedSchema =
|
|
280
|
+
if (resolveOptions.refineSchema) {
|
|
281
|
+
resolvedSchema = resolveOptions.refineSchema(resolvedSchema, schema);
|
|
236
282
|
}
|
|
237
283
|
|
|
238
284
|
if (schema.default !== undefined) {
|
|
@@ -299,7 +345,7 @@ class SchemaResolver {
|
|
|
299
345
|
return refSchema;
|
|
300
346
|
}
|
|
301
347
|
|
|
302
|
-
resolveType(schema, baseUri) {
|
|
348
|
+
resolveType(schema, resolveOptions, baseUri) {
|
|
303
349
|
let joischema;
|
|
304
350
|
|
|
305
351
|
const typeDefinitionMap = {
|
|
@@ -307,16 +353,16 @@ class SchemaResolver {
|
|
|
307
353
|
default: 'default'
|
|
308
354
|
};
|
|
309
355
|
|
|
310
|
-
const joitype = (type, format) => {
|
|
356
|
+
const joitype = (type, options, format) => {
|
|
311
357
|
let joischema;
|
|
312
358
|
|
|
313
|
-
if (
|
|
314
|
-
type =
|
|
359
|
+
if (options.refineType) {
|
|
360
|
+
type = options.refineType(type, format);
|
|
315
361
|
}
|
|
316
362
|
|
|
317
363
|
switch (type) {
|
|
318
364
|
case 'array':
|
|
319
|
-
joischema = this.array(schema, baseUri);
|
|
365
|
+
joischema = this.array(schema, options, baseUri);
|
|
320
366
|
break;
|
|
321
367
|
case 'boolean':
|
|
322
368
|
joischema = this.joi.boolean();
|
|
@@ -326,7 +372,7 @@ class SchemaResolver {
|
|
|
326
372
|
joischema = this.number(schema);
|
|
327
373
|
break;
|
|
328
374
|
case 'object':
|
|
329
|
-
joischema = this.object(schema, baseUri);
|
|
375
|
+
joischema = this.object(schema, options, baseUri);
|
|
330
376
|
break;
|
|
331
377
|
case 'string':
|
|
332
378
|
joischema = this.string(schema);
|
|
@@ -347,12 +393,12 @@ class SchemaResolver {
|
|
|
347
393
|
const schemas = [];
|
|
348
394
|
|
|
349
395
|
for (let i = 0; i < schema.type.length; i++) {
|
|
350
|
-
schemas.push(joitype(schema.type[i], schema.format));
|
|
396
|
+
schemas.push(joitype(schema.type[i], resolveOptions, schema.format));
|
|
351
397
|
}
|
|
352
398
|
|
|
353
399
|
joischema = this.joi.alternatives(schemas);
|
|
354
400
|
} else {
|
|
355
|
-
joischema = joitype(schema.type, schema.format);
|
|
401
|
+
joischema = joitype(schema.type, resolveOptions, schema.format);
|
|
356
402
|
}
|
|
357
403
|
|
|
358
404
|
Object.keys(typeDefinitionMap).forEach(function (key) {
|
|
@@ -362,8 +408,8 @@ class SchemaResolver {
|
|
|
362
408
|
});
|
|
363
409
|
|
|
364
410
|
let desc = schema.description;
|
|
365
|
-
if (
|
|
366
|
-
desc =
|
|
411
|
+
if (resolveOptions.refineDescription) {
|
|
412
|
+
desc = resolveOptions.refineDescription(schema);
|
|
367
413
|
}
|
|
368
414
|
if (desc) {
|
|
369
415
|
joischema = joischema.description(desc);
|
|
@@ -383,70 +429,69 @@ class SchemaResolver {
|
|
|
383
429
|
joischema = joischema.type === 'array' && !Array.isArray(example) ? joischema.example([example]) : joischema.example(example);
|
|
384
430
|
}
|
|
385
431
|
|
|
386
|
-
return joischema;
|
|
432
|
+
return addEnumRestriction(joischema, schema.enum, resolveOptions);
|
|
387
433
|
}
|
|
388
434
|
|
|
389
|
-
resolveOneOf(schema, baseUri) {
|
|
435
|
+
resolveOneOf(schema, resolveOptions, baseUri) {
|
|
390
436
|
Hoek.assert(Array.isArray(schema.oneOf), 'Expected oneOf to be an array.');
|
|
391
437
|
|
|
392
|
-
return this.joi.alternatives(schema.oneOf.map(schema => this.resolve(schema, baseUri))).match('one');
|
|
438
|
+
return this.joi.alternatives(schema.oneOf.map(schema => this.resolve(schema, resolveOptions, baseUri))).match('one');
|
|
393
439
|
}
|
|
394
440
|
|
|
395
|
-
resolveAnyOf(schema, baseUri) {
|
|
441
|
+
resolveAnyOf(schema, resolveOptions, baseUri) {
|
|
396
442
|
Hoek.assert(Array.isArray(schema.anyOf), 'Expected anyOf to be an array.');
|
|
397
443
|
|
|
398
|
-
return this.joi.alternatives(schema.anyOf.map(schema => this.resolve(schema, baseUri))).match('any');
|
|
444
|
+
return this.joi.alternatives(schema.anyOf.map(schema => this.resolve(schema, resolveOptions, baseUri))).match('any');
|
|
399
445
|
}
|
|
400
446
|
|
|
401
|
-
resolveAllOf(schema, baseUri) {
|
|
447
|
+
resolveAllOf(schema, resolveOptions, baseUri) {
|
|
402
448
|
Hoek.assert(Array.isArray(schema.allOf), 'Expected allOf to be an array.');
|
|
403
449
|
|
|
404
|
-
return this.joi.alternatives(schema.allOf.map(schema => this.resolve(schema, baseUri))).match('all');
|
|
450
|
+
return this.joi.alternatives(schema.allOf.map(schema => this.resolve(schema, resolveOptions, baseUri))).match('all');
|
|
405
451
|
}
|
|
406
452
|
|
|
407
|
-
resolveNot(schema, baseUri) {
|
|
453
|
+
resolveNot(schema, resolveOptions, baseUri) {
|
|
408
454
|
Hoek.assert(isObject(schema.not), 'Expected Not to be an object.');
|
|
409
455
|
|
|
410
456
|
return this.joi.alternatives().conditional(
|
|
411
457
|
'.',
|
|
412
458
|
{
|
|
413
|
-
not: this.resolve(schema.not, baseUri),
|
|
459
|
+
not: this.resolve(schema.not, resolveOptions, baseUri),
|
|
414
460
|
then: this.joi.any(),
|
|
415
461
|
otherwise: this.joi.any().forbidden()
|
|
416
462
|
}
|
|
417
463
|
);
|
|
418
464
|
}
|
|
419
465
|
|
|
420
|
-
|
|
466
|
+
resolveProperties(jsonSchema, resolveOptions, baseUri) {
|
|
467
|
+
const schemas = {};
|
|
421
468
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
if (!isObject(schema.properties)) {
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
469
|
+
if (!isObject(jsonSchema.properties)) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
428
472
|
|
|
429
|
-
|
|
430
|
-
|
|
473
|
+
Object.keys(jsonSchema.properties).forEach((key) => {
|
|
474
|
+
const property = jsonSchema.properties[key];
|
|
431
475
|
|
|
432
|
-
|
|
476
|
+
let joischema = this.resolve(property, resolveOptions, baseUri);
|
|
433
477
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
478
|
+
if (Array.isArray(jsonSchema.required) && !!~jsonSchema.required.indexOf(key)) {
|
|
479
|
+
joischema = joischema.required();
|
|
480
|
+
} else {
|
|
481
|
+
joischema = allowNullIfNeeded(joischema, resolveOptions.allowNull);
|
|
482
|
+
}
|
|
439
483
|
|
|
440
|
-
|
|
441
|
-
|
|
484
|
+
schemas[key] = joischema;
|
|
485
|
+
});
|
|
442
486
|
|
|
443
|
-
|
|
444
|
-
|
|
487
|
+
return schemas;
|
|
488
|
+
}
|
|
445
489
|
|
|
446
|
-
|
|
490
|
+
object(schema, resolveOptions, baseUri) {
|
|
491
|
+
let joischema = this.joi.object(this.resolveProperties(schema, resolveOptions, baseUri));
|
|
447
492
|
|
|
448
493
|
if (isObject(schema.additionalProperties)) {
|
|
449
|
-
joischema = joischema.pattern(/^/, this.resolve(schema.additionalProperties, baseUri));
|
|
494
|
+
joischema = joischema.pattern(/^/, this.resolve(schema.additionalProperties, resolveOptions, baseUri));
|
|
450
495
|
} else if (typeof this.joiOptions.allowUnknown === 'undefined') {
|
|
451
496
|
joischema = joischema.unknown(schema.additionalProperties !== false);
|
|
452
497
|
}
|
|
@@ -457,25 +502,25 @@ class SchemaResolver {
|
|
|
457
502
|
return joischema;
|
|
458
503
|
}
|
|
459
504
|
|
|
460
|
-
resolveAsArray(value, baseUri) {
|
|
505
|
+
resolveAsArray(value, resolveOptions, baseUri) {
|
|
461
506
|
if (Array.isArray(value)) {
|
|
462
507
|
// found an array, thus its _per type_
|
|
463
|
-
return value.map((v) => allowNullIfNeeded(this.resolve(v, baseUri),
|
|
508
|
+
return value.map((v) => allowNullIfNeeded(this.resolve(v, resolveOptions, baseUri), resolveOptions.allowNull));
|
|
464
509
|
}
|
|
465
510
|
// it's a single entity, so just resolve it normally
|
|
466
|
-
return [allowNullIfNeeded(this.resolve(value, baseUri),
|
|
511
|
+
return [allowNullIfNeeded(this.resolve(value, resolveOptions, baseUri), resolveOptions.allowNull)];
|
|
467
512
|
}
|
|
468
513
|
|
|
469
|
-
array(schema, baseUri) {
|
|
514
|
+
array(schema, resolveOptions, baseUri) {
|
|
470
515
|
let joischema = this.joi.array();
|
|
471
516
|
let items;
|
|
472
517
|
|
|
473
518
|
if (schema.items) {
|
|
474
|
-
items = this.resolveAsArray(schema.items, baseUri);
|
|
519
|
+
items = this.resolveAsArray(schema.items, resolveOptions, baseUri);
|
|
475
520
|
|
|
476
521
|
joischema = joischema.items(...items);
|
|
477
522
|
} else if (schema.ordered) {
|
|
478
|
-
items = this.resolveAsArray(schema.ordered, baseUri);
|
|
523
|
+
items = this.resolveAsArray(schema.ordered, resolveOptions, baseUri);
|
|
479
524
|
joischema = joischema.ordered(...items);
|
|
480
525
|
}
|
|
481
526
|
|
|
@@ -490,7 +535,7 @@ class SchemaResolver {
|
|
|
490
535
|
joischema = joischema.unique();
|
|
491
536
|
}
|
|
492
537
|
if (schema.contains) {
|
|
493
|
-
joischema = joischema.has(this.resolve(schema.contains, baseUri));
|
|
538
|
+
joischema = joischema.has(this.resolve(schema.contains, resolveOptions, baseUri));
|
|
494
539
|
}
|
|
495
540
|
|
|
496
541
|
return joischema;
|
|
@@ -519,10 +564,6 @@ class SchemaResolver {
|
|
|
519
564
|
const timeRegex = '([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(.[0-9]+)?(Z|(\\+|-)([01][0-9]|2[0-3]):([0-5][0-9]))';
|
|
520
565
|
const dateTimeRegex = dateRegex + 'T' + timeRegex;
|
|
521
566
|
|
|
522
|
-
if (schema.enum) {
|
|
523
|
-
return this.joi.string().valid(...schema.enum);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
567
|
switch (schema.format) {
|
|
527
568
|
case 'date':
|
|
528
569
|
return joischema.regex(new RegExp('^' + dateRegex + '$', 'i'), 'JsonSchema date format');
|
|
@@ -561,7 +602,7 @@ class SchemaResolver {
|
|
|
561
602
|
regularString(schema, joischema) {
|
|
562
603
|
schema.pattern && (joischema = joischema.regex(new RegExp(schema.pattern)));
|
|
563
604
|
|
|
564
|
-
if ((typeof schema.minLength === 'undefined' || schema.minLength === 0) && !schema.pattern && !schema.format) {
|
|
605
|
+
if ((typeof schema.minLength === 'undefined' || schema.minLength === 0) && !schema.pattern && !schema.format && !schema.enum) {
|
|
565
606
|
joischema = joischema.allow('');
|
|
566
607
|
}
|
|
567
608
|
|