json-2-joi 2.5.0 → 3.0.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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/lib/resolver.js +48 -27
  3. package/package.json +10 -11
package/README.md CHANGED
@@ -90,7 +90,7 @@ const schema = Json2Joi.schema({
90
90
 
91
91
  const { error, value } = schema.validate({firstName: 'John', lastName: 'Doe', age: 45});
92
92
  ```
93
- schema.additionalProperties !== false
93
+
94
94
  ### joiOptions
95
95
 
96
96
  When this option is provided, a Joi instance will be created as `joiInstance = Joi.defaults((schema) => schema.options(joiOptions));` for building Joi Schema.
package/lib/resolver.js CHANGED
@@ -1,4 +1,3 @@
1
- /* global Map */
2
1
  const Joi = require('joi');
3
2
  const Hoek = require('@hapi/hoek');
4
3
  const Bourne = require('@hapi/bourne');
@@ -124,7 +123,9 @@ function getJoiInstance(joiOptions, extensions, joiInstance) {
124
123
  }
125
124
  try {
126
125
  return { value: Bourne.parse(value) };
127
- } catch (ignoreErr) { } // eslint-disable-line no-empty
126
+ } catch (ignoreErr) { /* eslint-disable-line no-unused-vars */
127
+ // Purposefully ignoring error
128
+ }
128
129
  }
129
130
  }
130
131
  },
@@ -139,7 +140,9 @@ function getJoiInstance(joiOptions, extensions, joiInstance) {
139
140
  }
140
141
  try {
141
142
  return { value: Bourne.parse(value) };
142
- } catch (ignoreErr) { } // eslint-disable-line no-empty
143
+ } catch (ignoreErr) { /* eslint-disable-line no-unused-vars */
144
+ // Purposefully ignoring error
145
+ }
143
146
  }
144
147
  }
145
148
  },
@@ -222,7 +225,7 @@ class SchemaResolver {
222
225
  return joiSchema;
223
226
  }
224
227
 
225
- resolve(schema, resolveOptions = {}, baseUri = (schema.$id || '')) {
228
+ resolve(schema, resolveOptions = {}, baseUri = (schema.$id || ''), isPropertyRequired = false) {
226
229
  if (this.joi.isSchema(schema)) {
227
230
  return schema;
228
231
  }
@@ -242,17 +245,17 @@ class SchemaResolver {
242
245
 
243
246
  if (typeof schema === 'string') {
244
247
  // If schema is itself a string, interpret it as a type
245
- resolvedSchema = this.resolveType({ type: schema }, resolveOptions, baseUri);
248
+ resolvedSchema = this.resolveType({ type: schema }, resolveOptions, baseUri, isPropertyRequired);
246
249
  } else if (schema.$ref) {
247
250
  if (schema.$ref.trim() === '#') {
248
251
  return baseUri ? this.joi.link(`#${normalizedId(baseUri)}`) : this.joi.link('/');
249
252
  }
250
253
 
251
- resolvedSchema = this.resolve(this.resolveReference(schema.$ref, baseUri), resolveOptions, baseUri);
254
+ resolvedSchema = this.resolve(this.resolveReference(schema.$ref, baseUri), resolveOptions, baseUri, isPropertyRequired);
252
255
  } else {
253
256
  const partialSchemas = [];
254
257
  if (schema.type) {
255
- partialSchemas.push(this.resolveType(schema, resolveOptions, baseUri));
258
+ partialSchemas.push(this.resolveType(schema, resolveOptions, baseUri, isPropertyRequired));
256
259
  } else if (schema.properties) {
257
260
  // if no type is specified, just properties
258
261
  partialSchemas.push(this.object(schema, resolveOptions, baseUri))
@@ -282,7 +285,6 @@ class SchemaResolver {
282
285
  }
283
286
  if (partialSchemas.length === 0) {
284
287
  //Fall through to whatever.
285
- //eslint-disable-next-line no-console
286
288
  console.warn('WARNING: schema missing a \'type\' or \'$ref\' or \'enum\': \n%s', JSON.stringify(schema, null, 2));
287
289
  //TODO: Handle better
288
290
  partialSchemas.push(this.joi.any());
@@ -298,7 +300,9 @@ class SchemaResolver {
298
300
  resolvedSchema = resolvedSchema.default(schema.default)
299
301
  }
300
302
 
301
- if (schemaId) {
303
+ resolvedSchema = this.copyMetaAnnotations(schema, resolvedSchema);
304
+
305
+ if (schemaId && !(this.joiSharedSchemas.get(schemaId))) {
302
306
  resolvedSchema = resolvedSchema.id(normalizedId(schemaId));
303
307
  this.joiSharedSchemas.set(schemaId, resolvedSchema);
304
308
  }
@@ -307,6 +311,15 @@ class SchemaResolver {
307
311
  this.joiSharedSchemas.set(`${baseUri}#${schema.$anchor}`, resolvedSchema);
308
312
  }
309
313
 
314
+ if (isPropertyRequired) {
315
+ resolvedSchema = resolvedSchema.required();
316
+ if (resolveOptions.strictRequired) {
317
+ resolvedSchema = resolvedSchema.invalid(null, '');
318
+ }
319
+ } else {
320
+ resolvedSchema = allowNullIfNeeded(resolvedSchema, resolveOptions);
321
+ }
322
+
310
323
  return resolvedSchema;
311
324
  }
312
325
 
@@ -358,7 +371,7 @@ class SchemaResolver {
358
371
  return refSchema;
359
372
  }
360
373
 
361
- resolveType(schema, resolveOptions, baseUri) {
374
+ resolveType(schema, resolveOptions, baseUri, isPropertyRequired = false) {
362
375
  let joischema;
363
376
 
364
377
  const joitype = (type, options, format) => {
@@ -370,7 +383,7 @@ class SchemaResolver {
370
383
 
371
384
  switch (type) {
372
385
  case 'array':
373
- joischema = this.array(schema, options, baseUri);
386
+ joischema = this.array(schema, options, baseUri, isPropertyRequired);
374
387
  break;
375
388
  case 'boolean':
376
389
  joischema = this.joi.boolean();
@@ -460,6 +473,22 @@ class SchemaResolver {
460
473
  return addEnumRestriction(joischema, schema.enum, resolveOptions);
461
474
  }
462
475
 
476
+ copyMetaAnnotations(schema, joischema) {
477
+ const metaDataNames = ['title', 'deprecated', 'readOnly', 'writeOnly'];
478
+ const metas = {};
479
+ for (const metaName of metaDataNames) {
480
+ if (typeof schema[metaName] !== 'undefined') {
481
+ metas[metaName] = schema[metaName];
482
+ }
483
+ }
484
+
485
+ if (Object.keys(metas).length === 0) {
486
+ return joischema;
487
+ }
488
+
489
+ return joischema.meta(metas);
490
+ }
491
+
463
492
  resolveOneOf(schema, resolveOptions, baseUri) {
464
493
  Hoek.assert(Array.isArray(schema.oneOf), 'Expected oneOf to be an array.');
465
494
 
@@ -500,17 +529,9 @@ class SchemaResolver {
500
529
 
501
530
  Object.keys(jsonSchema.properties).forEach((key) => {
502
531
  const property = jsonSchema.properties[key];
532
+ const isPropertyRequired = Array.isArray(jsonSchema.required) && !!~jsonSchema.required.indexOf(key);
503
533
 
504
- let joischema = this.resolve(property, resolveOptions, baseUri);
505
-
506
- if (Array.isArray(jsonSchema.required) && !!~jsonSchema.required.indexOf(key)) {
507
- joischema = joischema.required();
508
- if (resolveOptions.strictRequired) {
509
- joischema = joischema.invalid(null, '');
510
- }
511
- } else {
512
- joischema = allowNullIfNeeded(joischema, resolveOptions);
513
- }
534
+ const joischema = this.resolve(property, resolveOptions, baseUri, isPropertyRequired);
514
535
 
515
536
  schemas[key] = joischema;
516
537
  });
@@ -533,26 +554,26 @@ class SchemaResolver {
533
554
  return joischema;
534
555
  }
535
556
 
536
- resolveAsArray(value, resolveOptions, baseUri) {
557
+ resolveAsArray(value, resolveOptions, baseUri, isPropertyRequired = false) {
537
558
  return [].concat(value).map((v) => {
538
559
  const vSchema = this.resolve(v, resolveOptions, baseUri);
539
- if (resolveOptions.strictArrayRequired) {
560
+ if (resolveOptions.strictArrayRequired && isPropertyRequired) {
540
561
  return vSchema.invalid(...resolveOptions.customizedNullValues);
541
562
  }
542
563
  return allowNullIfNeeded(vSchema, resolveOptions)
543
564
  });
544
565
  }
545
566
 
546
- array(schema, resolveOptions, baseUri) {
567
+ array(schema, resolveOptions, baseUri, isPropertyRequired = false) {
547
568
  let joischema = this.joi.array();
548
569
  let items;
549
570
 
550
571
  if (schema.items) {
551
- items = this.resolveAsArray(schema.items, resolveOptions, baseUri);
572
+ items = this.resolveAsArray(schema.items, resolveOptions, baseUri, isPropertyRequired);
552
573
 
553
574
  joischema = joischema.items(...items);
554
575
  } else if (schema.ordered) {
555
- items = this.resolveAsArray(schema.ordered, resolveOptions, baseUri);
576
+ items = this.resolveAsArray(schema.ordered, resolveOptions, baseUri, isPropertyRequired);
556
577
  joischema = joischema.ordered(...items);
557
578
  }
558
579
 
@@ -560,7 +581,7 @@ class SchemaResolver {
560
581
  joischema = joischema.max(items.length);
561
582
  }
562
583
 
563
- if (resolveOptions.strictArrayRequired && !schema.minItems) {
584
+ if (isPropertyRequired && resolveOptions.strictArrayRequired && !schema.minItems) {
564
585
  schema.minItems = 1;
565
586
  }
566
587
  isNumber(schema.minItems) && (joischema = joischema.min(schema.minItems));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json-2-joi",
3
- "version": "2.5.0",
3
+ "version": "3.0.0",
4
4
  "license": "Apache 2.0",
5
5
  "description": "Converts json-schema to Joi schema. (Enhanced enjoi)",
6
6
  "main": "./index.js",
@@ -13,9 +13,6 @@
13
13
  "type": "git",
14
14
  "url": "git://github.com/kenspirit/json-2-joi.git"
15
15
  },
16
- "engines": {
17
- "node": ">=12"
18
- },
19
16
  "keywords": [
20
17
  "json-to-joi",
21
18
  "json-2-joi",
@@ -35,17 +32,19 @@
35
32
  "homepage": "https://github.com/kenspirit/json-2-joi",
36
33
  "dependencies": {
37
34
  "@hapi/bourne": "^3.0.0",
38
- "@hapi/hoek": "^9.3.0",
39
- "debug": "^4.3.4"
35
+ "@hapi/hoek": "^11.0.4",
36
+ "debug": "^4.3.5"
40
37
  },
41
38
  "peerDependencies": {
42
- "joi": "^17.6.0"
39
+ "joi": "^17.13.3"
43
40
  },
44
41
  "devDependencies": {
45
- "joi": "^17.6.0",
46
- "eslint": "^6.8.0",
47
- "nyc": "^15.1.0",
48
- "tape": "^5.5.3"
42
+ "@eslint/js": "^9.6.0",
43
+ "eslint": "^9.6.0",
44
+ "globals": "^15.7.0",
45
+ "joi": "^17.13.3",
46
+ "nyc": "^17.0.0",
47
+ "tape": "^5.8.1"
49
48
  },
50
49
  "files": [
51
50
  "lib/**/*.js",