joi-to-json 2.6.0 → 2.6.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/README.md +147 -0
- package/lib/parsers/json.js +21 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -95,6 +95,153 @@ const jsonSchema = parse(joiSchema)
|
|
|
95
95
|
// const openApiSchema = parse(joiSchema, 'open-api')
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
### Joi to OpenAPI
|
|
99
|
+
|
|
100
|
+
Most Joi specifications result in the expected OpenAPI schema.
|
|
101
|
+
|
|
102
|
+
E.g.,
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
const joi = require('joi')
|
|
106
|
+
const { dump } = require('js-yaml')
|
|
107
|
+
const { writeFile } = require('fs/promises')
|
|
108
|
+
|
|
109
|
+
const joiSchema = joi.object().keys({
|
|
110
|
+
uuid: joi.string().uuid({ version: ['uuidv3', 'uuidv5'] }),
|
|
111
|
+
nickName: joi.string().required().example('鹄思乱想').description('Hero Nickname').min(3).max(20).pattern(/^[a-z]+$/, { name: 'alpha', invert: true }),
|
|
112
|
+
avatar: joi.string().required().uri(),
|
|
113
|
+
email: joi.string().email(),
|
|
114
|
+
ip: joi.string().ip({ version: ['ipv4', 'ipv6'] }),
|
|
115
|
+
hostname: joi.string().hostname().insensitive(),
|
|
116
|
+
gender: joi.string().valid('Male', 'Female', '', null).default('Male'),
|
|
117
|
+
isoDateString: joi.string().isoDate(),
|
|
118
|
+
isoDurationString: joi.string().isoDuration(),
|
|
119
|
+
birthday: joi.date().iso(),
|
|
120
|
+
certificate: joi.binary().encoding('base64'),
|
|
121
|
+
tags: joi.array().items(joi.string().required()).length(2),
|
|
122
|
+
nested: joi.object().keys({
|
|
123
|
+
key: joi.string()
|
|
124
|
+
}).unknown(true)
|
|
125
|
+
}).unknown(false)
|
|
126
|
+
|
|
127
|
+
async function writeYAML(targetPath) {
|
|
128
|
+
const openApiSchema = parse(joiSchema, 'open-api')
|
|
129
|
+
|
|
130
|
+
const openApiSchemaYAML = dump(openApiSchema, {lineWidth: 120, noCompatMode: true})
|
|
131
|
+
await writeFile(targetPath, openApiSchemaYAML)
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
results in
|
|
136
|
+
|
|
137
|
+
```yaml
|
|
138
|
+
type: object
|
|
139
|
+
required:
|
|
140
|
+
- nickName
|
|
141
|
+
- avatar
|
|
142
|
+
properties:
|
|
143
|
+
uuid:
|
|
144
|
+
type: string
|
|
145
|
+
format: uuid
|
|
146
|
+
nickName:
|
|
147
|
+
description: Hero Nickname
|
|
148
|
+
type: string
|
|
149
|
+
pattern: ^[a-z]+$
|
|
150
|
+
minLength: 3,
|
|
151
|
+
maxLength: 20,
|
|
152
|
+
example: 鹄思乱想
|
|
153
|
+
avatar:
|
|
154
|
+
type: string
|
|
155
|
+
format: uri
|
|
156
|
+
email:
|
|
157
|
+
type: string
|
|
158
|
+
format: email
|
|
159
|
+
ip:
|
|
160
|
+
type: string
|
|
161
|
+
oneOf:
|
|
162
|
+
- format: ipv4
|
|
163
|
+
- format: ipv6
|
|
164
|
+
hostname:
|
|
165
|
+
type: string
|
|
166
|
+
format: hostname
|
|
167
|
+
gender:
|
|
168
|
+
type: string
|
|
169
|
+
default: Male
|
|
170
|
+
enum:
|
|
171
|
+
- Male
|
|
172
|
+
- Female
|
|
173
|
+
- ''
|
|
174
|
+
- null
|
|
175
|
+
nullable: true
|
|
176
|
+
isoDateString:
|
|
177
|
+
type: string
|
|
178
|
+
format: date-time
|
|
179
|
+
isoDurationString:
|
|
180
|
+
type: string
|
|
181
|
+
format: duration
|
|
182
|
+
birthday:
|
|
183
|
+
type: string
|
|
184
|
+
format: date-time
|
|
185
|
+
certificate:
|
|
186
|
+
type: string
|
|
187
|
+
format: binary
|
|
188
|
+
tags:
|
|
189
|
+
type: array
|
|
190
|
+
items:
|
|
191
|
+
type: string
|
|
192
|
+
minItems: 2
|
|
193
|
+
maxItems: 2
|
|
194
|
+
nested:
|
|
195
|
+
type: object
|
|
196
|
+
properties:
|
|
197
|
+
key:
|
|
198
|
+
type: string
|
|
199
|
+
additionalProperties: true
|
|
200
|
+
additionalProperties: false
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Some OpenAPI features are not supported directly in Joi, but Joi schemas can be annotated with `joi.any().meta({…})`
|
|
204
|
+
to get them in the OpenAPI schema:
|
|
205
|
+
|
|
206
|
+
```js
|
|
207
|
+
…
|
|
208
|
+
|
|
209
|
+
const joiSchema = joi.object().keys({
|
|
210
|
+
deprecatedProperty: joi.string().meta({ deprecated: true }).required(),
|
|
211
|
+
readOnlyProperty: joi.string().meta({ readOnly: true }),
|
|
212
|
+
writeOnlyProperty: joi.string().meta({ writeOnly: true }),
|
|
213
|
+
xMeta: joi.string().meta({ 'x-meta': 42 }),
|
|
214
|
+
unknownMetaProperty: joi.string().meta({ unknownMeta: 42 })
|
|
215
|
+
}).unknown(true)
|
|
216
|
+
|
|
217
|
+
…
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
begets:
|
|
221
|
+
|
|
222
|
+
```yaml
|
|
223
|
+
type: object
|
|
224
|
+
required:
|
|
225
|
+
- deprecatedProperty
|
|
226
|
+
properties:
|
|
227
|
+
deprecatedProperty:
|
|
228
|
+
type: string
|
|
229
|
+
deprecated: true
|
|
230
|
+
readOnlyProperty:
|
|
231
|
+
type: string
|
|
232
|
+
readOnly: true
|
|
233
|
+
writeOnlyProperty:
|
|
234
|
+
type: string
|
|
235
|
+
writeOnly: true
|
|
236
|
+
xMeta:
|
|
237
|
+
type: string
|
|
238
|
+
x-meta: 42
|
|
239
|
+
unknownMetaProperty:
|
|
240
|
+
type: string
|
|
241
|
+
# unknownMeta is not exported
|
|
242
|
+
additionalProperties: true
|
|
243
|
+
```
|
|
244
|
+
|
|
98
245
|
## Browser support
|
|
99
246
|
For generating JSON Schema in a browser you should use below import syntax for `joi` library in order to work because the `joi` browser minimized build does not have `describe` api which the `joi-to-json` relies on.
|
|
100
247
|
|
package/lib/parsers/json.js
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
/* eslint no-use-before-define: 'off' */
|
|
2
2
|
const _ = require('lodash')
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Recognize the `joi.override` representation in `describe()` output.
|
|
6
|
+
*
|
|
7
|
+
* `joi.override` is a Symbol that can be used in `joi.any().valid(…)`
|
|
8
|
+
* statements, to reset the list of valid values. In `describe()` output, it
|
|
9
|
+
* turns up as an object with 1 property:
|
|
10
|
+
*
|
|
11
|
+
* ```
|
|
12
|
+
* { override: true }
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
function isJoiOverride(e) {
|
|
16
|
+
return typeof e === 'object'
|
|
17
|
+
&& e !== null
|
|
18
|
+
&& Object.keys(e).length === 1
|
|
19
|
+
&& e.override === true
|
|
20
|
+
}
|
|
21
|
+
|
|
4
22
|
class JoiJsonSchemaParser {
|
|
5
23
|
constructor() {
|
|
6
24
|
this.childrenFieldName = this._getChildrenFieldName()
|
|
@@ -124,8 +142,9 @@ class JoiJsonSchemaParser {
|
|
|
124
142
|
|
|
125
143
|
_getEnum(fieldDefn) {
|
|
126
144
|
const enumList = fieldDefn[this.enumFieldName]
|
|
127
|
-
|
|
128
|
-
|
|
145
|
+
const filteredEnumList = enumList ? _.filter(enumList, e => !isJoiOverride(e)) : enumList
|
|
146
|
+
if (fieldDefn.flags && fieldDefn.flags.only && _.size(filteredEnumList) > 1) {
|
|
147
|
+
return _.uniq(filteredEnumList)
|
|
129
148
|
}
|
|
130
149
|
}
|
|
131
150
|
|