joi-to-json 2.6.1 → 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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ### 3.0.0
2
+
3
+ - [BREAKING] Default enable feature: relation operator `and`, `or`, `nand`, `xor`, `oxor`.
package/README.md CHANGED
@@ -249,6 +249,10 @@ For generating JSON Schema in a browser you should use below import syntax for `
249
249
  import Joi from 'joi/lib/index';
250
250
  ```
251
251
 
252
+ ## Special Joi Operator Support
253
+
254
+ * [Logical Relation Operator](./docs/logical_rel_support.md)
255
+
252
256
  ## Test
253
257
 
254
258
  >npm run test
package/index.js CHANGED
@@ -22,7 +22,7 @@ const parsers = {
22
22
  'open-api': JoiOpenApiSchemaParser
23
23
  }
24
24
 
25
- function parse(joiObj, type = 'json', definitions = {}) {
25
+ function parse(joiObj, type = 'json', definitions = {}, parserOptions = {}) {
26
26
  if (typeof joiObj.describe !== 'function') {
27
27
  throw new Error('Not an joi object.')
28
28
  }
@@ -59,7 +59,7 @@ function parse(joiObj, type = 'json', definitions = {}) {
59
59
  throw new Error(`No parser is registered for ${type}`)
60
60
  }
61
61
 
62
- return new parser().parse(joiBaseSpec, definitions)
62
+ return new parser(parserOptions).parse(joiBaseSpec, definitions)
63
63
  }
64
64
 
65
65
  module.exports = parse
@@ -1,247 +1,258 @@
1
- const _ = require('lodash')
2
-
3
- class JoiSpecConvertor {
4
- constructor() {}
5
-
6
- static getVersion(joiObj) {
7
- return joiObj._currentJoi.version
8
- }
9
-
10
- static getSupportVersion() {
11
- return '12'
12
- }
13
-
14
- _convertObject(joiObj) {
15
- if (joiObj.children) {
16
- joiObj.keys = joiObj.children
17
- delete joiObj.children
18
- }
19
-
20
- _.each(joiObj.keys, (value, _key) => {
21
- this.toBaseSpec(value)
22
- })
23
-
24
- _.each(joiObj.patterns, (pattern, _idx) => {
25
- this.toBaseSpec(pattern.rule)
26
- })
27
- }
28
-
29
- _convertDate(joiObj) {
30
- if (joiObj.flags.timestamp) {
31
- joiObj.flags.format = joiObj.flags.timestamp
32
- delete joiObj.flags.timestamp
33
- delete joiObj.flags.multiplier
34
- } else if (joiObj.flags.format = {}) {
35
- joiObj.flags.format = 'iso'
36
- }
37
- }
38
-
39
- _convertAlternatives(joiObj) {
40
- let isWhenCase = false
41
- if (joiObj.alternatives.length === 1) {
42
- const condition = joiObj.alternatives[0]
43
- isWhenCase = !!condition.then || !!condition.otherwise
44
- }
45
-
46
- if (isWhenCase) {
47
- // when case
48
- joiObj.type = 'any'
49
- joiObj.whens = joiObj.alternatives
50
-
51
- if (joiObj.flags.presence === 'ignore') {
52
- // FIXME: Not sure when this flag is set.
53
- delete joiObj.flags.presence
54
- }
55
- if (joiObj.whens.peek) {
56
- joiObj.whens.is = joiObj.whens.peek
57
- delete joiObj.whens.peek
58
- }
59
-
60
- joiObj.whens[0] = _.mapValues(joiObj.whens[0], (value, condition) => {
61
- switch (condition) {
62
- case 'is':
63
- this.toBaseSpec(value)
64
- value.type = 'any'
65
- // FIXME: Not sure when this is set.
66
- value.allow.splice(0, 0, { 'override': true })
67
- break
68
- case 'otherwise':
69
- this.toBaseSpec(value)
70
- break
71
- case 'then':
72
- this.toBaseSpec(value)
73
- break
74
- case 'ref':
75
- if (value.indexOf('ref:') === 0) {
76
- value = {
77
- path: value.replace('ref:', '').split('.')
78
- }
79
- }
80
- break
81
- }
82
- return value
83
- })
84
-
85
- delete joiObj.alternatives
86
- delete joiObj.base
87
- } else {
88
- joiObj.matches = _.map(joiObj.alternatives, (alternative) => {
89
- return {
90
- schema: this.toBaseSpec(alternative)
91
- }
92
- })
93
- delete joiObj.alternatives
94
- }
95
- }
96
-
97
- _convertString(joiObj) {
98
- delete joiObj.options
99
-
100
- _.each(joiObj.rules, (rule, _idx) => {
101
- const name = rule.name
102
- if (['min', 'max'].includes(name)) {
103
- rule.args = {
104
- limit: rule.arg
105
- }
106
- } else if (['lowercase', 'uppercase'].includes(name)) {
107
- rule.name = 'case'
108
- rule.args = {
109
- direction: name.replace('case', '')
110
- }
111
- delete joiObj.flags.case
112
- } else if (rule.arg) {
113
- rule.args = {
114
- options: rule.arg
115
- }
116
- }
117
- if (name === 'regex') {
118
- rule.name = 'pattern'
119
- rule.args.regex = rule.arg.pattern.toString()
120
- delete rule.args.options.pattern
121
- }
122
- delete rule.arg
123
- })
124
- }
125
-
126
- _convertNumber(joiObj) {
127
- _.each(joiObj.rules, (rule, _idx) => {
128
- const name = rule.name
129
- if (['positive', 'negative'].includes(name)) {
130
- rule.args = {
131
- sign: name
132
- }
133
- rule.name = 'sign'
134
- }
135
- if (['greater', 'less', 'min', 'max', 'precision'].includes(name)) {
136
- rule.args = {
137
- limit: rule.arg
138
- }
139
- }
140
- if (name === 'multiple') {
141
- rule.args = {
142
- base: rule.arg
143
- }
144
- }
145
- delete rule.arg
146
- })
147
-
148
- if (joiObj.flags.precision) {
149
- delete joiObj.flags.precision
150
- }
151
- }
152
-
153
- _convertBoolean(joiObj) {
154
- if (joiObj.flags.insensitive === false) {
155
- joiObj.flags.sensitive = true
156
- }
157
- delete joiObj.flags.insensitive
158
- }
159
-
160
- _convertArray(joiObj) {
161
- if (joiObj.flags.sparse === false) {
162
- delete joiObj.flags.sparse
163
- }
164
-
165
- _.each(joiObj.rules, (rule, _idx) => {
166
- const name = rule.name
167
- if (['min', 'max', 'length'].includes(name)) {
168
- rule.args = {
169
- limit: rule.arg
170
- }
171
- }
172
- delete rule.arg
173
- })
174
-
175
- _.each(joiObj.items, (item, _idx) => {
176
- this.toBaseSpec(item)
177
- })
178
- }
179
-
180
- _convertExamples(_joiObj) {
181
- }
182
-
183
- toBaseSpec(joiObj) {
184
- joiObj.flags = joiObj.flags || {}
185
-
186
- if (joiObj.flags.allowOnly) {
187
- joiObj.flags.only = joiObj.flags.allowOnly
188
- delete joiObj.flags.allowOnly
189
- }
190
- if (joiObj.flags.allowUnknown) {
191
- joiObj.flags.unknown = joiObj.flags.allowUnknown
192
- delete joiObj.flags.allowUnknown
193
- }
194
- if (joiObj.description) {
195
- joiObj.flags.description = joiObj.description
196
- delete joiObj.description
197
- }
198
- if (joiObj.options) {
199
- joiObj.preferences = joiObj.options
200
- delete joiObj.options
201
- }
202
- if (joiObj.valids) {
203
- joiObj.allow = joiObj.valids
204
- delete joiObj.valids
205
- }
206
- if (joiObj.meta) {
207
- joiObj.metas = joiObj.meta
208
- delete joiObj.meta
209
- }
210
- delete joiObj.invalids
211
-
212
- this._convertExamples(joiObj)
213
-
214
- switch (joiObj.type) {
215
- case 'object':
216
- this._convertObject(joiObj)
217
- break
218
- case 'date':
219
- this._convertDate(joiObj)
220
- break
221
- case 'alternatives':
222
- this._convertAlternatives(joiObj)
223
- break
224
- case 'string':
225
- this._convertString(joiObj)
226
- break
227
- case 'number':
228
- this._convertNumber(joiObj)
229
- break
230
- case 'boolean':
231
- this._convertBoolean(joiObj)
232
- break
233
- case 'array':
234
- this._convertArray(joiObj)
235
- break
236
- default:
237
- break
238
- }
239
- if (_.isEmpty(joiObj.flags)) {
240
- delete joiObj.flags
241
- }
242
-
243
- return joiObj
244
- }
245
- }
246
-
247
- module.exports = JoiSpecConvertor
1
+ const _ = require('lodash')
2
+
3
+ class JoiSpecConvertor {
4
+ constructor() {}
5
+
6
+ static getVersion(joiObj) {
7
+ return joiObj._currentJoi.version
8
+ }
9
+
10
+ static getSupportVersion() {
11
+ return '12'
12
+ }
13
+
14
+ _convertObject(joiObj) {
15
+ if (joiObj.children) {
16
+ joiObj.keys = joiObj.children
17
+ delete joiObj.children
18
+ }
19
+
20
+ _.each(joiObj.keys, (value, _key) => {
21
+ this.toBaseSpec(value)
22
+ })
23
+
24
+ _.each(joiObj.patterns, (pattern, _idx) => {
25
+ this.toBaseSpec(pattern.rule)
26
+ })
27
+
28
+ this._convertDependencies(joiObj)
29
+ }
30
+
31
+ _convertDependencies(joiObj) {
32
+ _.each(joiObj.dependencies, (dependency) => {
33
+ if (dependency.type) {
34
+ dependency.rel = dependency.type
35
+ delete dependency.type
36
+ }
37
+ })
38
+ }
39
+
40
+ _convertDate(joiObj) {
41
+ if (joiObj.flags.timestamp) {
42
+ joiObj.flags.format = joiObj.flags.timestamp
43
+ delete joiObj.flags.timestamp
44
+ delete joiObj.flags.multiplier
45
+ } else if (joiObj.flags.format = {}) {
46
+ joiObj.flags.format = 'iso'
47
+ }
48
+ }
49
+
50
+ _convertAlternatives(joiObj) {
51
+ let isWhenCase = false
52
+ if (joiObj.alternatives.length === 1) {
53
+ const condition = joiObj.alternatives[0]
54
+ isWhenCase = !!condition.then || !!condition.otherwise
55
+ }
56
+
57
+ if (isWhenCase) {
58
+ // when case
59
+ joiObj.type = 'any'
60
+ joiObj.whens = joiObj.alternatives
61
+
62
+ if (joiObj.flags.presence === 'ignore') {
63
+ // FIXME: Not sure when this flag is set.
64
+ delete joiObj.flags.presence
65
+ }
66
+ if (joiObj.whens.peek) {
67
+ joiObj.whens.is = joiObj.whens.peek
68
+ delete joiObj.whens.peek
69
+ }
70
+
71
+ joiObj.whens[0] = _.mapValues(joiObj.whens[0], (value, condition) => {
72
+ switch (condition) {
73
+ case 'is':
74
+ this.toBaseSpec(value)
75
+ value.type = 'any'
76
+ // FIXME: Not sure when this is set.
77
+ value.allow.splice(0, 0, { 'override': true })
78
+ break
79
+ case 'otherwise':
80
+ this.toBaseSpec(value)
81
+ break
82
+ case 'then':
83
+ this.toBaseSpec(value)
84
+ break
85
+ case 'ref':
86
+ if (value.indexOf('ref:') === 0) {
87
+ value = {
88
+ path: value.replace('ref:', '').split('.')
89
+ }
90
+ }
91
+ break
92
+ }
93
+ return value
94
+ })
95
+
96
+ delete joiObj.alternatives
97
+ delete joiObj.base
98
+ } else {
99
+ joiObj.matches = _.map(joiObj.alternatives, (alternative) => {
100
+ return {
101
+ schema: this.toBaseSpec(alternative)
102
+ }
103
+ })
104
+ delete joiObj.alternatives
105
+ }
106
+ }
107
+
108
+ _convertString(joiObj) {
109
+ delete joiObj.options
110
+
111
+ _.each(joiObj.rules, (rule, _idx) => {
112
+ const name = rule.name
113
+ if (['min', 'max'].includes(name)) {
114
+ rule.args = {
115
+ limit: rule.arg
116
+ }
117
+ } else if (['lowercase', 'uppercase'].includes(name)) {
118
+ rule.name = 'case'
119
+ rule.args = {
120
+ direction: name.replace('case', '')
121
+ }
122
+ delete joiObj.flags.case
123
+ } else if (rule.arg) {
124
+ rule.args = {
125
+ options: rule.arg
126
+ }
127
+ }
128
+ if (name === 'regex') {
129
+ rule.name = 'pattern'
130
+ rule.args.regex = rule.arg.pattern.toString()
131
+ delete rule.args.options.pattern
132
+ }
133
+ delete rule.arg
134
+ })
135
+ }
136
+
137
+ _convertNumber(joiObj) {
138
+ _.each(joiObj.rules, (rule, _idx) => {
139
+ const name = rule.name
140
+ if (['positive', 'negative'].includes(name)) {
141
+ rule.args = {
142
+ sign: name
143
+ }
144
+ rule.name = 'sign'
145
+ }
146
+ if (['greater', 'less', 'min', 'max', 'precision'].includes(name)) {
147
+ rule.args = {
148
+ limit: rule.arg
149
+ }
150
+ }
151
+ if (name === 'multiple') {
152
+ rule.args = {
153
+ base: rule.arg
154
+ }
155
+ }
156
+ delete rule.arg
157
+ })
158
+
159
+ if (joiObj.flags.precision) {
160
+ delete joiObj.flags.precision
161
+ }
162
+ }
163
+
164
+ _convertBoolean(joiObj) {
165
+ if (joiObj.flags.insensitive === false) {
166
+ joiObj.flags.sensitive = true
167
+ }
168
+ delete joiObj.flags.insensitive
169
+ }
170
+
171
+ _convertArray(joiObj) {
172
+ if (joiObj.flags.sparse === false) {
173
+ delete joiObj.flags.sparse
174
+ }
175
+
176
+ _.each(joiObj.rules, (rule, _idx) => {
177
+ const name = rule.name
178
+ if (['min', 'max', 'length'].includes(name)) {
179
+ rule.args = {
180
+ limit: rule.arg
181
+ }
182
+ }
183
+ delete rule.arg
184
+ })
185
+
186
+ _.each(joiObj.items, (item, _idx) => {
187
+ this.toBaseSpec(item)
188
+ })
189
+ }
190
+
191
+ _convertExamples(_joiObj) {
192
+ }
193
+
194
+ toBaseSpec(joiObj) {
195
+ joiObj.flags = joiObj.flags || {}
196
+
197
+ if (joiObj.flags.allowOnly) {
198
+ joiObj.flags.only = joiObj.flags.allowOnly
199
+ delete joiObj.flags.allowOnly
200
+ }
201
+ if (joiObj.flags.allowUnknown) {
202
+ joiObj.flags.unknown = joiObj.flags.allowUnknown
203
+ delete joiObj.flags.allowUnknown
204
+ }
205
+ if (joiObj.description) {
206
+ joiObj.flags.description = joiObj.description
207
+ delete joiObj.description
208
+ }
209
+ if (joiObj.options) {
210
+ joiObj.preferences = joiObj.options
211
+ delete joiObj.options
212
+ }
213
+ if (joiObj.valids) {
214
+ joiObj.allow = joiObj.valids
215
+ delete joiObj.valids
216
+ }
217
+ if (joiObj.meta) {
218
+ joiObj.metas = joiObj.meta
219
+ delete joiObj.meta
220
+ }
221
+ delete joiObj.invalids
222
+
223
+ this._convertExamples(joiObj)
224
+
225
+ switch (joiObj.type) {
226
+ case 'object':
227
+ this._convertObject(joiObj)
228
+ break
229
+ case 'date':
230
+ this._convertDate(joiObj)
231
+ break
232
+ case 'alternatives':
233
+ this._convertAlternatives(joiObj)
234
+ break
235
+ case 'string':
236
+ this._convertString(joiObj)
237
+ break
238
+ case 'number':
239
+ this._convertNumber(joiObj)
240
+ break
241
+ case 'boolean':
242
+ this._convertBoolean(joiObj)
243
+ break
244
+ case 'array':
245
+ this._convertArray(joiObj)
246
+ break
247
+ default:
248
+ break
249
+ }
250
+ if (_.isEmpty(joiObj.flags)) {
251
+ delete joiObj.flags
252
+ }
253
+
254
+ return joiObj
255
+ }
256
+ }
257
+
258
+ module.exports = JoiSpecConvertor
@@ -1,21 +1,25 @@
1
- const _ = require('lodash')
2
- const JoiJsonSchemaParser = require('./json')
3
-
4
- class JoiJsonDraftSchemaParser extends JoiJsonSchemaParser {
5
- _setNumberFieldProperties(fieldSchema, fieldDefn) {
6
- super._setNumberFieldProperties(fieldSchema, fieldDefn)
7
-
8
- if (typeof fieldSchema.minimum !== 'undefined' && fieldSchema.minimum === fieldSchema.exclusiveMinimum) {
9
- fieldSchema.exclusiveMinimum = true
10
- }
11
- if (typeof fieldSchema.maximum !== 'undefined' && fieldSchema.maximum === fieldSchema.exclusiveMaximum) {
12
- fieldSchema.exclusiveMaximum = true
13
- }
14
- }
15
-
16
- _getLocalSchemaBasePath() {
17
- return '#/definitions'
18
- }
19
- }
20
-
21
- module.exports = JoiJsonDraftSchemaParser
1
+ const _ = require('lodash')
2
+ const JoiJsonSchemaParser = require('./json')
3
+
4
+ class JoiJsonDraftSchemaParser extends JoiJsonSchemaParser {
5
+ constructor(opts = {}) {
6
+ super(_.merge({ logicalOpParser: { xor: null } }, opts))
7
+ }
8
+
9
+ _setNumberFieldProperties(fieldSchema, fieldDefn) {
10
+ super._setNumberFieldProperties(fieldSchema, fieldDefn)
11
+
12
+ if (typeof fieldSchema.minimum !== 'undefined' && fieldSchema.minimum === fieldSchema.exclusiveMinimum) {
13
+ fieldSchema.exclusiveMinimum = true
14
+ }
15
+ if (typeof fieldSchema.maximum !== 'undefined' && fieldSchema.maximum === fieldSchema.exclusiveMaximum) {
16
+ fieldSchema.exclusiveMaximum = true
17
+ }
18
+ }
19
+
20
+ _getLocalSchemaBasePath() {
21
+ return '#/definitions'
22
+ }
23
+ }
24
+
25
+ module.exports = JoiJsonDraftSchemaParser
@@ -1,5 +1,52 @@
1
1
  /* eslint no-use-before-define: 'off' */
2
2
  const _ = require('lodash')
3
+ const combinations = require('combinations')
4
+
5
+ /**
6
+ * Default Joi logical operator (or/and/nand/xor/oxor) parsers
7
+ */
8
+ const LOGICAL_OP_PARSER = {
9
+ or: function (schema, dependency) {
10
+ schema.anyOf = _.map(dependency.peers, (peer) => {
11
+ return { required: [peer] }
12
+ })
13
+ },
14
+ and: function (schema, dependency) {
15
+ schema.oneOf = [{ required: dependency.peers }]
16
+ schema.oneOf.push({
17
+ allOf: _.map(dependency.peers, (peer) => {
18
+ return { not: { required: [peer] } }
19
+ })
20
+ })
21
+ },
22
+ nand: function (schema, dependency) {
23
+ schema.not = { required: dependency.peers }
24
+ },
25
+ xor: function (schema, dependency) {
26
+ schema.if = {
27
+ propertyNames: { enum: dependency.peers },
28
+ minProperties: 2
29
+ }
30
+ schema.then = false
31
+ schema.else = {
32
+ oneOf: _.map(dependency.peers, (peer) => {
33
+ return { required: [peer] }
34
+ })
35
+ }
36
+ },
37
+ oxor: function (schema, dependency) {
38
+ schema.oneOf = _.map(dependency.peers, (peer) => {
39
+ return { required: [peer] }
40
+ })
41
+ schema.oneOf.push({
42
+ not: {
43
+ oneOf: _.map(combinations(dependency.peers, 1, 2), (combination) => {
44
+ return { required: combination }
45
+ })
46
+ }
47
+ })
48
+ }
49
+ }
3
50
 
4
51
  /**
5
52
  * Recognize the `joi.override` representation in `describe()` output.
@@ -20,12 +67,18 @@ function isJoiOverride(e) {
20
67
  }
21
68
 
22
69
  class JoiJsonSchemaParser {
23
- constructor() {
70
+ constructor(opts = {}) {
24
71
  this.childrenFieldName = this._getChildrenFieldName()
25
72
  this.optionsFieldName = this._getOptionsFieldName()
26
73
  this.ruleArgFieldName = this._getRuleArgFieldName()
27
74
  this.enumFieldName = this._getEnumFieldName()
28
75
  this.allowUnknownFlagName = this._getAllowUnknownFlagName()
76
+
77
+ if (opts.logicalOpParser === false) {
78
+ this.logicalOpParser = {}
79
+ } else {
80
+ this.logicalOpParser = _.merge({}, LOGICAL_OP_PARSER, opts.logicalOpParser)
81
+ }
29
82
  }
30
83
 
31
84
  parse(joiSpec, definitions = {}, level = 0) {
@@ -241,6 +294,39 @@ class JoiJsonSchemaParser {
241
294
  if (_.isEmpty(schema.required)) {
242
295
  delete schema.required
243
296
  }
297
+
298
+ this._setObjectDependencies(schema, joiSpec)
299
+ }
300
+
301
+ _setObjectDependencies(schema, joiSpec) {
302
+ if (!_.isArray(joiSpec.dependencies) || joiSpec.dependencies.length === 0) {
303
+ return
304
+ }
305
+
306
+ if (joiSpec.dependencies.length === 1) {
307
+ this._setDependencySubSchema(schema, joiSpec.dependencies[0])
308
+ } else {
309
+ schema.allOf = _.compact(_.map(joiSpec.dependencies, (dependency) => {
310
+ const subSchema = this._setDependencySubSchema({}, dependency)
311
+ if (_.isEmpty(subSchema)) {
312
+ return null
313
+ }
314
+ return subSchema
315
+ }))
316
+ if (schema.allOf.length === 0) {
317
+ // When the logicalOpParser is set to false
318
+ delete schema.allOf
319
+ }
320
+ }
321
+ }
322
+
323
+ _setDependencySubSchema(schema, dependency) {
324
+ const opParser = this.logicalOpParser[dependency.rel]
325
+ if (typeof opParser !== 'function') {
326
+ return schema
327
+ }
328
+ opParser(schema, dependency)
329
+ return schema
244
330
  }
245
331
 
246
332
  _setNumberFieldProperties(fieldSchema, fieldDefn) {
@@ -1,83 +1,83 @@
1
- const _ = require('lodash')
2
- const JoiJsonSchemaParser = require('./json-draft-04')
3
-
4
- class JoiOpenApiSchemaParser extends JoiJsonSchemaParser {
5
- parse(joiSpec, definitions = {}, level = 0) {
6
- const fullSchema = super.parse(joiSpec, definitions, level)
7
- const schema = _.pick(fullSchema, [
8
- '$ref',
9
- 'title',
10
- 'multipleOf',
11
- 'maximum',
12
- 'exclusiveMaximum',
13
- 'minimum',
14
- 'exclusiveMinimum',
15
- 'maxLength',
16
- 'minLength',
17
- 'pattern',
18
- 'maxItems',
19
- 'minItems',
20
- 'uniqueItems',
21
- 'maxProperties',
22
- 'minProperties',
23
- 'required',
24
- 'enum',
25
- 'description',
26
- 'format',
27
- 'default',
28
- 'type',
29
-
30
- 'allOf',
31
- 'oneOf',
32
- 'anyOf',
33
- 'not',
34
- 'items',
35
- 'properties',
36
- 'additionalProperties',
37
-
38
- 'example',
39
- 'nullable'
40
- ])
41
-
42
- if (!_.isEmpty(joiSpec.metas)) {
43
- _.each(joiSpec.metas, meta => {
44
- _.each(meta, (value, key) => {
45
- if (key.startsWith('x-') || key === 'deprecated' || key === 'readOnly' || key === 'writeOnly') {
46
- schema[key] = value
47
- }
48
- })
49
- })
50
- }
51
-
52
- if (level === 0 && !_.isEmpty(definitions)) {
53
- schema.schemas = definitions
54
- }
55
-
56
- if (fullSchema.const) {
57
- schema.enum = [fullSchema.const]
58
- }
59
-
60
- return schema
61
- }
62
-
63
- _getLocalSchemaBasePath() {
64
- return '#/components/schemas'
65
- }
66
-
67
- _setBasicProperties(fieldSchema, fieldDefn) {
68
- super._setBasicProperties(fieldSchema, fieldDefn)
69
-
70
- if (!_.isEmpty(fieldSchema.examples)) {
71
- fieldSchema.example = fieldSchema.examples[0]
72
- }
73
- }
74
-
75
- _addNullTypeIfNullable(fieldSchema, fieldDefn) {
76
- const enums = _.get(fieldDefn, this.enumFieldName)
77
- if (Array.isArray(enums) && enums.includes(null)) {
78
- fieldSchema.nullable = true
79
- }
80
- }
81
- }
82
-
83
- module.exports = JoiOpenApiSchemaParser
1
+ const _ = require('lodash')
2
+ const JoiJsonSchemaParser = require('./json-draft-04')
3
+
4
+ class JoiOpenApiSchemaParser extends JoiJsonSchemaParser {
5
+ parse(joiSpec, definitions = {}, level = 0) {
6
+ const fullSchema = super.parse(joiSpec, definitions, level)
7
+ const schema = _.pick(fullSchema, [
8
+ '$ref',
9
+ 'title',
10
+ 'multipleOf',
11
+ 'maximum',
12
+ 'exclusiveMaximum',
13
+ 'minimum',
14
+ 'exclusiveMinimum',
15
+ 'maxLength',
16
+ 'minLength',
17
+ 'pattern',
18
+ 'maxItems',
19
+ 'minItems',
20
+ 'uniqueItems',
21
+ 'maxProperties',
22
+ 'minProperties',
23
+ 'required',
24
+ 'enum',
25
+ 'description',
26
+ 'format',
27
+ 'default',
28
+ 'type',
29
+
30
+ 'allOf',
31
+ 'oneOf',
32
+ 'anyOf',
33
+ 'not',
34
+ 'items',
35
+ 'properties',
36
+ 'additionalProperties',
37
+
38
+ 'example',
39
+ 'nullable'
40
+ ])
41
+
42
+ if (!_.isEmpty(joiSpec.metas)) {
43
+ _.each(joiSpec.metas, meta => {
44
+ _.each(meta, (value, key) => {
45
+ if (key.startsWith('x-') || key === 'deprecated' || key === 'readOnly' || key === 'writeOnly') {
46
+ schema[key] = value
47
+ }
48
+ })
49
+ })
50
+ }
51
+
52
+ if (level === 0 && !_.isEmpty(definitions)) {
53
+ schema.schemas = definitions
54
+ }
55
+
56
+ if (fullSchema.const) {
57
+ schema.enum = [fullSchema.const]
58
+ }
59
+
60
+ return schema
61
+ }
62
+
63
+ _getLocalSchemaBasePath() {
64
+ return '#/components/schemas'
65
+ }
66
+
67
+ _setBasicProperties(fieldSchema, fieldDefn) {
68
+ super._setBasicProperties(fieldSchema, fieldDefn)
69
+
70
+ if (!_.isEmpty(fieldSchema.examples)) {
71
+ fieldSchema.example = fieldSchema.examples[0]
72
+ }
73
+ }
74
+
75
+ _addNullTypeIfNullable(fieldSchema, fieldDefn) {
76
+ const enums = _.get(fieldDefn, this.enumFieldName)
77
+ if (Array.isArray(enums) && enums.includes(null)) {
78
+ fieldSchema.nullable = true
79
+ }
80
+ }
81
+ }
82
+
83
+ module.exports = JoiOpenApiSchemaParser
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "joi-to-json",
3
- "version": "2.6.1",
3
+ "version": "3.0.0",
4
4
  "description": "joi to JSON / OpenAPI Schema Converter",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -23,6 +23,7 @@
23
23
  "oas"
24
24
  ],
25
25
  "dependencies": {
26
+ "combinations": "^1.0.0",
26
27
  "lodash": "^4.17.21",
27
28
  "semver-compare": "^1.0.0"
28
29
  },