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 +26 -0
- package/README.md +19 -6
- package/index.js +4 -4
- package/lib/resolver.js +56 -38
- package/package.json +1 -1
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
|
+
 [](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
|
|
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
|
|
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
|
-
`
|
|
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
|
-
- `
|
|
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 =
|
|
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
|
|