json-2-joi 1.0.0 → 1.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 CHANGED
@@ -1,3 +1,29 @@
1
+ ### 1.1.0
2
+
3
+ - Support providing `joiOptions` for JOI default instance creation.
4
+ - Support directly passing a Joi instance for schema building.
5
+
6
+ ### 1.0.0
7
+
8
+ - Provided `refineDescription(schema)`, `allowNull` options
9
+ - Change cycle reference resovling without self-generating id and rely on $id if defined in JSON schema
10
+ - Support `const` keyword in JSON schema
11
+ - Support `example` setting for Joi based on JSON schema's `examples`, `default` or `enum`
12
+ - Support `contains` keyword in JSON schema using Joi's `has` method
13
+ - Support `$anchor` keyword in JSON schema
14
+ - Remove `defaults` API. Please refer to section **Reuse JOI Schema Resolver** for similar usage.
15
+ - **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
+ * id
17
+ * [baseUri]#anchor
18
+ * [baseUri]#/$defs/shared
19
+ * [baseUri]#/$defs/shared/properties/level1
20
+
21
+ **subSchemas will be modified** for two reason:
22
+ * 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.
24
+
25
+ ## Before Fork
26
+
1
27
  ### 9.0.1
2
28
 
3
29
  * #112 Use joi.link to only resolve strictly recursive self-references
package/README.md CHANGED
@@ -1,4 +1,4 @@
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
 
@@ -12,21 +12,24 @@ 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.
15
18
  - **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
19
  * id
17
20
  * [baseUri]#anchor
18
21
  * [baseUri]#/$defs/shared
19
22
  * [baseUri]#/$defs/shared/properties/level1
20
23
 
21
- **subSchemas** will be modified for two reason:
24
+ **subSchemas will be modified** for two reason:
22
25
  * 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).
26
+ * New key-value pair will be added into it if the schema or any its subschema being parsed has `$id` field.
24
27
 
25
28
  ## Usage
26
29
 
27
30
  ### Schema Support
28
31
 
29
- `json2Joi` does not support all of json-schema.
32
+ `json-2-joi` does not support all of json-schema.
30
33
 
31
34
  Here is a list of some known missing keyword support still being worked on:
32
35
 
@@ -36,10 +39,11 @@ Please file issues for other unsupported features.
36
39
 
37
40
  ### API
38
41
 
39
- - `json2Joi.schema(schema [, options])`
42
+ - `json2Joi.schema(schema [, options, joiInstance])`
40
43
  - `schema` - a JSON schema or a string type representation (such as `'integer'`).
41
44
  - `options` - an (optional) object of additional options such as `subSchemas` and custom `types`.
42
- - `json2Joi.resolver(options)` - Creates a schema resolver based on `options`.
45
+ - `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.
46
+ - `json2Joi.resolver(options [, joiInstance])` - Creates a schema resolver based on `options`. `joiInstance` is same as above.
43
47
 
44
48
  ### Options
45
49
 
@@ -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
+ * `useDefaults` will take effect only if `noDefaults` is NOT true in `joiOptions`.
93
+ * `unknown` setting (originally set by schema.additionalProperties !== false) of the schema will take effect only if `allowUnknown` is NOT provided in `joiOptions`.
94
+
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,6 +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
+ joiOptions: Joi.object().unknown(true).default({}),
23
24
  strictMode: Joi.boolean().default(false),
24
25
  useDefaults: Joi.boolean().default(false)
25
26
  });
@@ -32,10 +33,59 @@ function isObject(value) {
32
33
  return value !== null && typeof value === 'object';
33
34
  }
34
35
 
36
+ function isNotEmptyObject(value) {
37
+ return isObject(value) && Object.keys(value).length > 0;
38
+ }
39
+
35
40
  function normalizedId(id) {
36
41
  return id.replace(/[^a-zA-Z0-9]/g, '_');
37
42
  }
38
43
 
44
+ function getJoiInstance(joiOptions, extensions, joiInstance) {
45
+ if (joiInstance) {
46
+ return joiInstance;
47
+ }
48
+ if (isNotEmptyObject(joiOptions)) {
49
+ joiInstance = Joi.defaults((schema) => schema.options(joiOptions));
50
+ } else {
51
+ joiInstance = Joi;
52
+ }
53
+
54
+ return joiInstance.extend(
55
+ {
56
+ type: 'object',
57
+ base: joiInstance.object(),
58
+ coerce: {
59
+ from: 'string',
60
+ method(value) {
61
+ if (typeof value !== 'string' || (value[0] !== '{' && !/^\s*\{/.test(value))) {
62
+ return;
63
+ }
64
+ try {
65
+ return { value: Bourne.parse(value) };
66
+ } catch (ignoreErr) { } // eslint-disable-line no-empty
67
+ }
68
+ }
69
+ },
70
+ {
71
+ type: 'array',
72
+ base: joiInstance.array(),
73
+ coerce: {
74
+ from: 'string',
75
+ method(value) {
76
+ if (typeof value !== 'string' || (value[0] !== '[' && !/^\s*\[/.test(value))) {
77
+ return;
78
+ }
79
+ try {
80
+ return { value: Bourne.parse(value) };
81
+ } catch (ignoreErr) { } // eslint-disable-line no-empty
82
+ }
83
+ }
84
+ },
85
+ ...extensions
86
+ );
87
+ }
88
+
39
89
  const mapToObj = m => {
40
90
  return Array.from(m).reduce((obj, [key, value]) => {
41
91
  obj[key] = Joi.isSchema(value) ? value.describe() : value;
@@ -44,12 +94,12 @@ const mapToObj = m => {
44
94
  };
45
95
 
46
96
  class SchemaResolver {
47
- constructor(options) {
97
+ constructor(options, joiInstance) {
48
98
  const validatedResult = optionsSchema.validate(options);
49
99
 
50
100
  Hoek.assert(!validatedResult.error, validatedResult.error);
51
101
 
52
- const { subSchemas = {}, refineType, refineSchema, strictMode, useDefaults, extensions = [], allowNull = false, refineDescription } = validatedResult.value;
102
+ const { subSchemas = {}, refineType, refineSchema, strictMode, useDefaults, extensions = [], allowNull = false, refineDescription, joiOptions } = validatedResult.value;
53
103
 
54
104
  this.subSchemas = subSchemas;
55
105
  this.refineType = refineType;
@@ -58,42 +108,10 @@ class SchemaResolver {
58
108
  this.strictMode = strictMode;
59
109
  this.useDefaults = useDefaults;
60
110
  this.allowNull = allowNull;
111
+ this.joiOptions = joiOptions;
61
112
  this.joiSharedSchemas = new Map();
62
113
 
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
- );
114
+ this.joi = getJoiInstance(joiOptions, extensions, joiInstance);
97
115
 
98
116
  for (let sharedSchemaId in this.subSchemas) {
99
117
  const sharedSchema = this.subSchemas[sharedSchemaId];
@@ -214,7 +232,7 @@ class SchemaResolver {
214
232
  resolvedSchema = this.refineSchema(resolvedSchema, schema);
215
233
  }
216
234
 
217
- if (this.useDefaults && schema.default !== undefined) {
235
+ if (this.joiOptions.noDefaults !== true && this.useDefaults && schema.default !== undefined) {
218
236
  resolvedSchema = resolvedSchema.default(schema.default)
219
237
  }
220
238
 
@@ -426,7 +444,7 @@ class SchemaResolver {
426
444
 
427
445
  if (isObject(schema.additionalProperties)) {
428
446
  joischema = joischema.pattern(/^/, this.resolve(schema.additionalProperties, baseUri));
429
- } else {
447
+ } else if (typeof this.joiOptions.allowUnknown === 'undefined') {
430
448
  joischema = joischema.unknown(schema.additionalProperties !== false);
431
449
  }
432
450
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json-2-joi",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "license": "Apache 2.0",
5
5
  "description": "Converts json-schema to Joi schema.",
6
6
  "main": "./index.js",