json-2-joi 1.0.0 → 2.0.1

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 CHANGED
@@ -1,3 +1,37 @@
1
+ ### 2.0.1
2
+
3
+ - Set correct examples data for Array type field.
4
+
5
+ ### 2.0.0
6
+
7
+ * [BREAKING] Remove `useDefaults` and `strictMode` options
8
+
9
+ ### 1.1.0
10
+
11
+ - Support providing `joiOptions` for JOI default instance creation.
12
+ - Support directly passing a Joi instance for schema building.
13
+
14
+ ### 1.0.0
15
+
16
+ - Provided `refineDescription(schema)`, `allowNull` options
17
+ - Change cycle reference resovling without self-generating id and rely on $id if defined in JSON schema
18
+ - Support `const` keyword in JSON schema
19
+ - Support `example` setting for Joi based on JSON schema's `examples`, `default` or `enum`
20
+ - Support `contains` keyword in JSON schema using Joi's `has` method
21
+ - Support `$anchor` keyword in JSON schema
22
+ - Remove `defaults` API. Please refer to section **Reuse JOI Schema Resolver** for similar usage.
23
+ - **Big Change** on `$ref` and `subSchemas`. Please refer to [test/test-references.js](test/test-references.js) for detail usage. Currently, four format of `$ref` are supported:
24
+ * id
25
+ * [baseUri]#anchor
26
+ * [baseUri]#/$defs/shared
27
+ * [baseUri]#/$defs/shared/properties/level1
28
+
29
+ **subSchemas will be modified** for two reason:
30
+ * If any subSchema does not have `$id` field, it will be added using the corresponding key in `subSchemas` object.
31
+ * New key-value pair will be added into it if the schema or any its subschema being parsed has `$id` field.
32
+
33
+ ## Before Fork
34
+
1
35
  ### 9.0.1
2
36
 
3
37
  * #112 Use joi.link to only resolve strictly recursive self-references
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
-
1
+ ![Test](https://github.com/kenspirit/json-2-joi/workflows/Test/badge.svg) [![npm version](https://badge.fury.io/js/json-2-joi.svg)](https://badge.fury.io/js/json-2-joi)
2
2
 
3
3
  # json-2-joi
4
4
 
5
5
  Converts a JSON schema to a Joi schema for object validation.
6
6
 
7
- ## Change based on the forked version (Json2Joi):
7
+ ## Change based on the forked version (Enjoi):
8
8
 
9
9
  - Provided `refineDescription(schema)`, `allowNull` options
10
10
  - Change cycle reference resovling without self-generating id and rely on $id if defined in JSON schema
@@ -12,21 +12,25 @@ Converts a JSON schema to a Joi schema for object validation.
12
12
  - Support `example` setting for Joi based on JSON schema's `examples`, `default` or `enum`
13
13
  - Support `contains` keyword in JSON schema using Joi's `has` method
14
14
  - Support `$anchor` keyword in JSON schema
15
+ - Support providing `joiOptions` for JOI default instance creation.
16
+ - Support directly passing a Joi instance for schema building.
17
+ - Remove `defaults` API. Please refer to section **Reuse JOI Schema Resolver** for similar usage.
18
+ - Remove `strictMode` and `useDefaults` option.
15
19
  - **Big Change** on `$ref` and `subSchemas`. Please refer to [test/test-references.js](test/test-references.js) for detail usage. Currently, four format of `$ref` are supported:
16
20
  * id
17
21
  * [baseUri]#anchor
18
22
  * [baseUri]#/$defs/shared
19
23
  * [baseUri]#/$defs/shared/properties/level1
20
24
 
21
- **subSchemas** will be modified for two reason:
25
+ **subSchemas will be modified** for two reason:
22
26
  * If any subSchema does not have `$id` field, it will be added using the corresponding key in `subSchemas` object.
23
- * New key-value pair will be added into it if the **schema** or any its subschema being parsed has `$id` field. Root schema will be added into it as well (if missing $id field, empty string is used as key).
27
+ * New key-value pair will be added into it if the schema or any its subschema being parsed has `$id` field.
24
28
 
25
29
  ## Usage
26
30
 
27
31
  ### Schema Support
28
32
 
29
- `json2Joi` does not support all of json-schema.
33
+ `json-2-joi` does not support all of json-schema.
30
34
 
31
35
  Here is a list of some known missing keyword support still being worked on:
32
36
 
@@ -36,10 +40,11 @@ Please file issues for other unsupported features.
36
40
 
37
41
  ### API
38
42
 
39
- - `json2Joi.schema(schema [, options])`
43
+ - `json2Joi.schema(schema [, options, joiInstance])`
40
44
  - `schema` - a JSON schema or a string type representation (such as `'integer'`).
41
45
  - `options` - an (optional) object of additional options such as `subSchemas` and custom `types`.
42
- - `json2Joi.resolver(options)` - Creates a schema resolver based on `options`.
46
+ - `joiInstance` - a (optional) Joi instance to be used. When passed, it will be used directly. `extensions` option does not take effect and the Object/Array auto coerce extensions will not be included as well.
47
+ - `json2Joi.resolver(options [, joiInstance])` - Creates a schema resolver based on `options`. `joiInstance` is same as above.
43
48
 
44
49
  ### Options
45
50
 
@@ -49,7 +54,6 @@ Please file issues for other unsupported features.
49
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
50
55
  - `extensions` - an array of extensions to pass [joi.extend](https://github.com/hapijs/joi/blob/master/API.md#extendextension).
51
56
  - `allowNull` - Allows null value when the field is NOT required with a default value of `false`.
52
- - `strictMode` - make schemas `strict(value)` with a default value of `false`.
53
57
 
54
58
  Example:
55
59
 
@@ -79,6 +83,15 @@ const schema = Json2Joi.schema({
79
83
 
80
84
  const { error, value } = schema.validate({firstName: 'John', lastName: 'Doe', age: 45});
81
85
  ```
86
+ schema.additionalProperties !== false
87
+ ### joiOptions
88
+
89
+ When this option is provided, a Joi instance will be created as `joiInstance = Joi.defaults((schema) => schema.options(joiOptions));` for building Joi Schema.
90
+
91
+ As a result:
92
+ * `unknown` setting (originally set by schema.additionalProperties !== false) of the schema will take effect only if `allowUnknown` is NOT provided in `joiOptions`.
93
+ * Original `useDefaults` option will be replaced by `noDefaults` in `joiOptions`.
94
+ * Original `strictMode` option will be replaced by `convert` in `joiOptions`.
82
95
 
83
96
  ### Sub Schemas
84
97
 
package/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  const SchemaResolver = require('./lib/resolver');
2
2
 
3
- function resolver(options = {}) {
4
- return new SchemaResolver(options);
3
+ function resolver(options = {}, joiInstance) {
4
+ return new SchemaResolver(options, joiInstance);
5
5
  }
6
6
 
7
- function schema(schema, options = { }) {
8
- const joiResolver = resolver(options);
7
+ function schema(schema, options = { }, joiInstance) {
8
+ const joiResolver = resolver(options, joiInstance);
9
9
  const joiSchema = joiResolver.convert(schema);
10
10
  return joiSchema;
11
11
  }
package/lib/resolver.js CHANGED
@@ -20,8 +20,7 @@ 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
- strictMode: Joi.boolean().default(false),
24
- useDefaults: Joi.boolean().default(false)
23
+ joiOptions: Joi.object().unknown(true).default({})
25
24
  });
26
25
 
27
26
  function isNumber(value) {
@@ -32,10 +31,59 @@ function isObject(value) {
32
31
  return value !== null && typeof value === 'object';
33
32
  }
34
33
 
34
+ function isNotEmptyObject(value) {
35
+ return isObject(value) && Object.keys(value).length > 0;
36
+ }
37
+
35
38
  function normalizedId(id) {
36
39
  return id.replace(/[^a-zA-Z0-9]/g, '_');
37
40
  }
38
41
 
42
+ function getJoiInstance(joiOptions, extensions, joiInstance) {
43
+ if (joiInstance) {
44
+ return joiInstance;
45
+ }
46
+ if (isNotEmptyObject(joiOptions)) {
47
+ joiInstance = Joi.defaults((schema) => schema.options(joiOptions));
48
+ } else {
49
+ joiInstance = Joi;
50
+ }
51
+
52
+ return joiInstance.extend(
53
+ {
54
+ type: 'object',
55
+ base: joiInstance.object(),
56
+ coerce: {
57
+ from: 'string',
58
+ method(value) {
59
+ if (typeof value !== 'string' || (value[0] !== '{' && !/^\s*\{/.test(value))) {
60
+ return;
61
+ }
62
+ try {
63
+ return { value: Bourne.parse(value) };
64
+ } catch (ignoreErr) { } // eslint-disable-line no-empty
65
+ }
66
+ }
67
+ },
68
+ {
69
+ type: 'array',
70
+ base: joiInstance.array(),
71
+ coerce: {
72
+ from: 'string',
73
+ method(value) {
74
+ if (typeof value !== 'string' || (value[0] !== '[' && !/^\s*\[/.test(value))) {
75
+ return;
76
+ }
77
+ try {
78
+ return { value: Bourne.parse(value) };
79
+ } catch (ignoreErr) { } // eslint-disable-line no-empty
80
+ }
81
+ }
82
+ },
83
+ ...extensions
84
+ );
85
+ }
86
+
39
87
  const mapToObj = m => {
40
88
  return Array.from(m).reduce((obj, [key, value]) => {
41
89
  obj[key] = Joi.isSchema(value) ? value.describe() : value;
@@ -44,56 +92,22 @@ const mapToObj = m => {
44
92
  };
45
93
 
46
94
  class SchemaResolver {
47
- constructor(options) {
95
+ constructor(options, joiInstance) {
48
96
  const validatedResult = optionsSchema.validate(options);
49
97
 
50
98
  Hoek.assert(!validatedResult.error, validatedResult.error);
51
99
 
52
- const { subSchemas = {}, refineType, refineSchema, strictMode, useDefaults, extensions = [], allowNull = false, refineDescription } = validatedResult.value;
100
+ const { subSchemas = {}, refineType, refineSchema, extensions = [], allowNull = false, refineDescription, joiOptions } = validatedResult.value;
53
101
 
54
102
  this.subSchemas = subSchemas;
55
103
  this.refineType = refineType;
56
104
  this.refineDescription = refineDescription;
57
105
  this.refineSchema = refineSchema;
58
- this.strictMode = strictMode;
59
- this.useDefaults = useDefaults;
60
106
  this.allowNull = allowNull;
107
+ this.joiOptions = joiOptions;
61
108
  this.joiSharedSchemas = new Map();
62
109
 
63
- this.joi = Joi.extend(
64
- {
65
- type: 'object',
66
- base: Joi.object(),
67
- coerce: {
68
- from: 'string',
69
- method(value) {
70
- if (typeof value !== 'string' || (value[0] !== '{' && !/^\s*\{/.test(value))) {
71
- return;
72
- }
73
-
74
- try {
75
- return { value: Bourne.parse(value) };
76
- } catch (ignoreErr) { } // eslint-disable-line no-empty
77
- }
78
- }
79
- },
80
- {
81
- type: 'array',
82
- base: Joi.array(),
83
- coerce: {
84
- from: 'string',
85
- method(value) {
86
- if (typeof value !== 'string' || (value[0] !== '[' && !/^\s*\[/.test(value))) {
87
- return;
88
- }
89
- try {
90
- return { value: Bourne.parse(value) };
91
- } catch (ignoreErr) { } // eslint-disable-line no-empty
92
- }
93
- }
94
- },
95
- ...extensions
96
- );
110
+ this.joi = getJoiInstance(joiOptions, extensions, joiInstance);
97
111
 
98
112
  for (let sharedSchemaId in this.subSchemas) {
99
113
  const sharedSchema = this.subSchemas[sharedSchemaId];
@@ -214,7 +228,7 @@ class SchemaResolver {
214
228
  resolvedSchema = this.refineSchema(resolvedSchema, schema);
215
229
  }
216
230
 
217
- if (this.useDefaults && schema.default !== undefined) {
231
+ if (schema.default !== undefined) {
218
232
  resolvedSchema = resolvedSchema.default(schema.default)
219
233
  }
220
234
 
@@ -319,7 +333,7 @@ class SchemaResolver {
319
333
 
320
334
  Hoek.assert(joischema, 'Could not resolve type: ' + schema.type + '.');
321
335
 
322
- return joischema.strict(this.strictMode);
336
+ return joischema;
323
337
  }
324
338
 
325
339
  if (Array.isArray(schema.type)) {
@@ -359,7 +373,7 @@ class SchemaResolver {
359
373
  example = schema.enum[0];
360
374
  }
361
375
  if (example) {
362
- joischema = joischema.example(example);
376
+ joischema = joischema.type === 'array' && !Array.isArray(example) ? joischema.example([example]) : joischema.example(example);
363
377
  }
364
378
 
365
379
  return joischema;
@@ -426,7 +440,7 @@ class SchemaResolver {
426
440
 
427
441
  if (isObject(schema.additionalProperties)) {
428
442
  joischema = joischema.pattern(/^/, this.resolve(schema.additionalProperties, baseUri));
429
- } else {
443
+ } else if (typeof this.joiOptions.allowUnknown === 'undefined') {
430
444
  joischema = joischema.unknown(schema.additionalProperties !== false);
431
445
  }
432
446
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json-2-joi",
3
- "version": "1.0.0",
3
+ "version": "2.0.1",
4
4
  "license": "Apache 2.0",
5
5
  "description": "Converts json-schema to Joi schema.",
6
6
  "main": "./index.js",