@ditojs/server 1.6.0 → 1.8.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/package.json +8 -8
- package/src/app/Application.js +3 -3
- package/src/app/Validator.js +2 -1
- package/src/models/Model.js +4 -2
- package/src/models/definitions/properties.js +1 -3
- package/src/schema/keywords/_range.js +3 -2
- package/src/schema/keywords/_validate.js +1 -1
- package/src/schema/properties.js +60 -120
- package/src/schema/properties.test.js +269 -275
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ditojs/server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Dito.js Server – Dito.js is a declarative and modern web framework, based on Objection.js, Koa.js and Vue.js",
|
|
6
6
|
"repository": "https://github.com/ditojs/dito/tree/master/packages/server",
|
|
@@ -21,16 +21,16 @@
|
|
|
21
21
|
"node >= 16"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@ditojs/admin": "^1.
|
|
25
|
-
"@ditojs/build": "^1.
|
|
26
|
-
"@ditojs/router": "^1.
|
|
27
|
-
"@ditojs/utils": "^1.
|
|
24
|
+
"@ditojs/admin": "^1.8.0",
|
|
25
|
+
"@ditojs/build": "^1.8.0",
|
|
26
|
+
"@ditojs/router": "^1.8.0",
|
|
27
|
+
"@ditojs/utils": "^1.8.0",
|
|
28
28
|
"@koa/cors": "^3.3.0",
|
|
29
29
|
"@koa/multer": "^3.0.0",
|
|
30
30
|
"@originjs/vite-plugin-commonjs": "^1.0.3",
|
|
31
31
|
"ajv": "^8.11.0",
|
|
32
32
|
"ajv-formats": "^2.1.1",
|
|
33
|
-
"aws-sdk": "^2.
|
|
33
|
+
"aws-sdk": "^2.1141.0",
|
|
34
34
|
"axios": "^0.27.2",
|
|
35
35
|
"bcryptjs": "^2.4.3",
|
|
36
36
|
"bytes": "^3.1.2",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"repl": "^0.1.3",
|
|
68
68
|
"uuid": "^8.3.2",
|
|
69
69
|
"vite": "^2.9.9",
|
|
70
|
-
"vite-plugin-vue2": "^2.0.
|
|
70
|
+
"vite-plugin-vue2": "^2.0.1",
|
|
71
71
|
"vue": "^2.6.14",
|
|
72
72
|
"vue-template-compiler": "^2.6.14"
|
|
73
73
|
},
|
|
@@ -79,5 +79,5 @@
|
|
|
79
79
|
"knex": "^2.0.0",
|
|
80
80
|
"objection": "^3.0.1"
|
|
81
81
|
},
|
|
82
|
-
"gitHead": "
|
|
82
|
+
"gitHead": "390e0a7e0782b3cb5cff5e5d0454a35c7a954df5"
|
|
83
83
|
}
|
package/src/app/Application.js
CHANGED
|
@@ -454,9 +454,9 @@ export class Application extends Koa {
|
|
|
454
454
|
} else if (parameters) {
|
|
455
455
|
throw new Error(`Invalid parameters definition: ${parameters}`)
|
|
456
456
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
457
|
+
const schema = properties
|
|
458
|
+
? convertSchema({ type: 'object', properties }, options)
|
|
459
|
+
: null
|
|
460
460
|
const validate = this.compileValidator(schema, {
|
|
461
461
|
// For parameters, always coerce types, including arrays.
|
|
462
462
|
coerceTypes: 'array',
|
package/src/app/Validator.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import objection from 'objection'
|
|
2
|
-
import Ajv from 'ajv'
|
|
2
|
+
import Ajv from 'ajv/dist/2020.js'
|
|
3
3
|
import addFormats from 'ajv-formats'
|
|
4
4
|
import { isArray, isObject, clone, isAsync, isPromise } from '@ditojs/utils'
|
|
5
5
|
import { formatJson } from '../utils/index.js'
|
|
@@ -368,6 +368,7 @@ const validatorOptions = {
|
|
|
368
368
|
$data: false,
|
|
369
369
|
$comment: false,
|
|
370
370
|
coerceTypes: false,
|
|
371
|
+
discriminator: true,
|
|
371
372
|
multipleOfPrecision: false,
|
|
372
373
|
ownProperties: true,
|
|
373
374
|
removeAdditional: false,
|
package/src/models/Model.js
CHANGED
|
@@ -360,13 +360,15 @@ export class Model extends objection.Model {
|
|
|
360
360
|
|
|
361
361
|
static get jsonSchema() {
|
|
362
362
|
return this._getCached('jsonSchema', () => {
|
|
363
|
-
const schema = convertSchema(
|
|
363
|
+
const schema = convertSchema({
|
|
364
|
+
type: 'object',
|
|
365
|
+
properties: this.definition.properties
|
|
366
|
+
})
|
|
364
367
|
addRelationSchemas(this, schema.properties)
|
|
365
368
|
// Merge in root-level schema additions
|
|
366
369
|
merge(schema, this.definition.schema)
|
|
367
370
|
return {
|
|
368
371
|
$id: this.name,
|
|
369
|
-
$schema: 'http://json-schema.org/draft-07/schema',
|
|
370
372
|
...schema
|
|
371
373
|
}
|
|
372
374
|
}, {})
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { mergeReversed } from '../../utils/index.js'
|
|
2
|
-
import { expandSchemaShorthand } from '../../schema/index.js'
|
|
3
2
|
|
|
4
3
|
export default function properties(values) {
|
|
5
4
|
const properties = mergeReversed(values)
|
|
@@ -55,8 +54,7 @@ export default function properties(values) {
|
|
|
55
54
|
// NOTE: Substitutions on all other levels happen in convertSchema()
|
|
56
55
|
const ids = []
|
|
57
56
|
const rest = []
|
|
58
|
-
for (
|
|
59
|
-
property = expandSchemaShorthand(property)
|
|
57
|
+
for (const [name, property] of Object.entries(properties)) {
|
|
60
58
|
// Also sort properties by kind: primary id > foreign id > rest:
|
|
61
59
|
const entry = [name, property]
|
|
62
60
|
if (property.primary) {
|
|
@@ -2,11 +2,12 @@ export const range = {
|
|
|
2
2
|
type: ['number', 'integer'],
|
|
3
3
|
metaSchema: {
|
|
4
4
|
type: 'array',
|
|
5
|
-
|
|
5
|
+
prefixItems: [
|
|
6
6
|
{ type: 'number' },
|
|
7
7
|
{ type: 'number' }
|
|
8
8
|
],
|
|
9
|
-
|
|
9
|
+
minItems: 2,
|
|
10
|
+
items: false
|
|
10
11
|
},
|
|
11
12
|
macro(config) {
|
|
12
13
|
return {
|
package/src/schema/properties.js
CHANGED
|
@@ -1,59 +1,65 @@
|
|
|
1
1
|
import { isObject, isArray, isString } from '@ditojs/utils'
|
|
2
2
|
|
|
3
3
|
export function convertSchema(schema, options = {}) {
|
|
4
|
-
if (
|
|
5
|
-
// TODO: Consider removing string short-hand
|
|
6
|
-
// Nested shorthand expansion
|
|
7
|
-
schema = { type: schema }
|
|
8
|
-
} else if (isArray(schema)) {
|
|
4
|
+
if (isArray(schema)) {
|
|
9
5
|
// Needed for allOf, anyOf, oneOf, not, items:
|
|
10
6
|
schema = schema.map(entry => convertSchema(entry, options))
|
|
11
|
-
}
|
|
12
|
-
|
|
7
|
+
} else if (isObject(schema)) {
|
|
8
|
+
// Create a shallow clone so we can modify and return:
|
|
9
|
+
schema = { ...schema }
|
|
13
10
|
const { type } = schema
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
if (schema.required === true) {
|
|
12
|
+
// Our 'required' is not the same as JSON Schema's: Use the 'required'
|
|
13
|
+
// format instead that only validates if the required value is not empty,
|
|
14
|
+
// meaning neither nullish nor an empty string. The JSON schema `required`
|
|
15
|
+
// array is generated seperately below through `convertProperties()`.
|
|
16
|
+
delete schema.required
|
|
17
|
+
schema = addFormat(schema, 'required')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Convert properties
|
|
21
|
+
let hasConvertedProperties = false
|
|
22
|
+
if (schema.properties) {
|
|
23
|
+
const { properties, required } = convertProperties(
|
|
24
|
+
schema.properties,
|
|
25
|
+
options
|
|
26
|
+
)
|
|
27
|
+
schema.properties = properties
|
|
28
|
+
if (required.length > 0) {
|
|
29
|
+
schema.required = required
|
|
30
|
+
}
|
|
31
|
+
hasConvertedProperties = true
|
|
32
|
+
}
|
|
33
|
+
if (schema.patternProperties) {
|
|
34
|
+
// TODO: Don't we need to handle required here too?
|
|
35
|
+
const { properties } = convertProperties(
|
|
36
|
+
schema.patternProperties,
|
|
37
|
+
options
|
|
38
|
+
)
|
|
39
|
+
schema.patternProperties = properties
|
|
40
|
+
hasConvertedProperties = true
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Convert array items
|
|
44
|
+
schema.prefixItems &&= convertSchema(schema.prefixItems, options)
|
|
45
|
+
schema.items &&= convertSchema(schema.items, options)
|
|
46
|
+
|
|
47
|
+
// Handle nested allOf, anyOf, oneOf, not fields
|
|
48
|
+
for (const key of ['allOf', 'anyOf', 'oneOf', 'not']) {
|
|
49
|
+
if (key in schema) {
|
|
50
|
+
schema[key] = convertSchema(schema[key], options)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
19
54
|
if (isString(type)) {
|
|
20
55
|
// Convert schema property notation to JSON schema
|
|
21
56
|
const jsonType = jsonTypes[type]
|
|
22
57
|
if (jsonType) {
|
|
23
58
|
schema.type = jsonType
|
|
24
|
-
if (
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
schema.properties,
|
|
29
|
-
options
|
|
30
|
-
)
|
|
31
|
-
schema.properties = properties
|
|
32
|
-
if (required.length > 0) {
|
|
33
|
-
schema.required = required
|
|
34
|
-
}
|
|
35
|
-
setAdditionalProperties = true
|
|
36
|
-
}
|
|
37
|
-
if (schema.patternProperties) {
|
|
38
|
-
const { properties } = expandProperties(
|
|
39
|
-
schema.patternProperties,
|
|
40
|
-
options
|
|
41
|
-
)
|
|
42
|
-
schema.patternProperties = properties
|
|
43
|
-
setAdditionalProperties = true
|
|
44
|
-
}
|
|
45
|
-
if (setAdditionalProperties) {
|
|
46
|
-
// Invert the logic of `additionalProperties` so that it needs to be
|
|
47
|
-
// explicitely set to `true`:
|
|
48
|
-
if (!('additionalProperties' in schema)) {
|
|
49
|
-
schema.additionalProperties = false
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
} else if (jsonType === 'array') {
|
|
53
|
-
const { items } = schema
|
|
54
|
-
if (items) {
|
|
55
|
-
schema.items = convertSchema(items, options)
|
|
56
|
-
}
|
|
59
|
+
if (hasConvertedProperties && !('additionalProperties' in schema)) {
|
|
60
|
+
// Invert the logic of `additionalProperties` so that it needs to be
|
|
61
|
+
// explicitely set to `true`:
|
|
62
|
+
schema.additionalProperties = false
|
|
57
63
|
}
|
|
58
64
|
} else if (['date', 'datetime', 'timestamp'].includes(type)) {
|
|
59
65
|
// Date properties can be submitted both as a string or a Date object.
|
|
@@ -61,7 +67,7 @@ export function convertSchema(schema, options = {}) {
|
|
|
61
67
|
// to handle both types correctly.
|
|
62
68
|
schema.type = ['string', 'object']
|
|
63
69
|
schema = addFormat(schema, 'date-time')
|
|
64
|
-
} else {
|
|
70
|
+
} else if (type !== 'null') {
|
|
65
71
|
// A reference to another model as nested JSON data, use $ref or
|
|
66
72
|
// instanceof instead of type, based on the passed option:
|
|
67
73
|
if (options.useInstanceOf) {
|
|
@@ -86,26 +92,6 @@ export function convertSchema(schema, options = {}) {
|
|
|
86
92
|
}
|
|
87
93
|
}
|
|
88
94
|
}
|
|
89
|
-
} else {
|
|
90
|
-
// This is a root properties schema or nested object without type that
|
|
91
|
-
// may need expanding.
|
|
92
|
-
const expanded = expandSchemaShorthand(schema)
|
|
93
|
-
schema = expanded !== schema
|
|
94
|
-
// Only call convertSchema() if it actually changed...
|
|
95
|
-
? convertSchema(expanded, options)
|
|
96
|
-
: expanded
|
|
97
|
-
// Handle nested allOf, anyOf, oneOf, not properties
|
|
98
|
-
for (const key of ['allOf', 'anyOf', 'oneOf', 'not']) {
|
|
99
|
-
if (key in schema) {
|
|
100
|
-
schema[key] = convertSchema(schema[key], options)
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (required) {
|
|
105
|
-
// Our 'required' is not the same as JSON Schema's: Use the 'required'
|
|
106
|
-
// format instead that only validates if the required value is not
|
|
107
|
-
// empty, meaning neither nullish nor an empty string.
|
|
108
|
-
schema = addFormat(schema, 'required')
|
|
109
95
|
}
|
|
110
96
|
if (excludeDefaults[schema.default]) {
|
|
111
97
|
delete schema.default
|
|
@@ -119,11 +105,10 @@ export function convertSchema(schema, options = {}) {
|
|
|
119
105
|
return schema
|
|
120
106
|
}
|
|
121
107
|
|
|
122
|
-
export function
|
|
108
|
+
export function convertProperties(schemaProperties, options) {
|
|
123
109
|
const properties = {}
|
|
124
110
|
const required = []
|
|
125
|
-
for (
|
|
126
|
-
property = expandSchemaShorthand(property)
|
|
111
|
+
for (const [key, property] of Object.entries(schemaProperties)) {
|
|
127
112
|
properties[key] = convertSchema(property, options)
|
|
128
113
|
if (property?.required) {
|
|
129
114
|
required.push(key)
|
|
@@ -132,60 +117,15 @@ export function expandProperties(schemaProperties, options) {
|
|
|
132
117
|
return { properties, required }
|
|
133
118
|
}
|
|
134
119
|
|
|
135
|
-
export function expandSchemaShorthand(schema) {
|
|
136
|
-
// TODO: Consider removing all short-hand schema expansion.
|
|
137
|
-
if (isString(schema)) {
|
|
138
|
-
schema = {
|
|
139
|
-
type: schema
|
|
140
|
-
}
|
|
141
|
-
} else if (isArray(schema)) {
|
|
142
|
-
schema = {
|
|
143
|
-
type: 'array',
|
|
144
|
-
items: schema.length > 1 ? schema : schema[0],
|
|
145
|
-
// The array short-forms sets an empty array as the default.
|
|
146
|
-
default: []
|
|
147
|
-
}
|
|
148
|
-
} else if (
|
|
149
|
-
// Expand objects to `type: 'object'`...
|
|
150
|
-
isObject(schema) &&
|
|
151
|
-
!(
|
|
152
|
-
// ...but only if they don't define any of these properties:
|
|
153
|
-
isString(schema.type) ||
|
|
154
|
-
isString(schema.$ref) ||
|
|
155
|
-
isArray(schema.allOf) ||
|
|
156
|
-
isArray(schema.anyOf) ||
|
|
157
|
-
isArray(schema.oneOf) ||
|
|
158
|
-
isObject(schema.not)
|
|
159
|
-
)
|
|
160
|
-
) {
|
|
161
|
-
// Separate object short-hand into property definitions and other fields.
|
|
162
|
-
const properties = {}
|
|
163
|
-
const rest = {}
|
|
164
|
-
for (const [key, value] of Object.entries(schema)) {
|
|
165
|
-
// Property definitions are either objects or string / array short-hands:
|
|
166
|
-
if (isObject(value) || isString(value) || isArray(value)) {
|
|
167
|
-
properties[key] = value
|
|
168
|
-
} else {
|
|
169
|
-
rest[key] = value
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
schema = {
|
|
173
|
-
type: 'object',
|
|
174
|
-
properties,
|
|
175
|
-
...rest
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return schema
|
|
179
|
-
}
|
|
180
|
-
|
|
181
120
|
function addFormat(schema, newFormat) {
|
|
182
121
|
// Support multiple `format` keywords through `allOf`:
|
|
183
|
-
|
|
122
|
+
const { allOf, format, ...rest } = schema
|
|
184
123
|
if (format || allOf) {
|
|
185
|
-
allOf
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
124
|
+
if (!allOf?.find(({ format }) => format === newFormat)) {
|
|
125
|
+
schema = {
|
|
126
|
+
...rest,
|
|
127
|
+
allOf: [...(allOf ?? []), { format }, { format: newFormat }]
|
|
128
|
+
}
|
|
189
129
|
}
|
|
190
130
|
} else {
|
|
191
131
|
schema.format = newFormat
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { convertSchema
|
|
1
|
+
import { convertSchema } from './properties.js'
|
|
2
2
|
|
|
3
3
|
describe('convertSchema()', () => {
|
|
4
4
|
it('expands objects with properties to full JSON schemas', () => {
|
|
@@ -22,7 +22,10 @@ describe('convertSchema()', () => {
|
|
|
22
22
|
type: 'array'
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
-
expect(convertSchema(
|
|
25
|
+
expect(convertSchema({
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties
|
|
28
|
+
})).toEqual({
|
|
26
29
|
type: 'object',
|
|
27
30
|
properties,
|
|
28
31
|
additionalProperties: false
|
|
@@ -56,7 +59,10 @@ describe('convertSchema()', () => {
|
|
|
56
59
|
default: null
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
|
-
expect(convertSchema(
|
|
62
|
+
expect(convertSchema({
|
|
63
|
+
type: 'object',
|
|
64
|
+
properties
|
|
65
|
+
})).toEqual({
|
|
60
66
|
type: 'object',
|
|
61
67
|
properties,
|
|
62
68
|
additionalProperties: false
|
|
@@ -65,8 +71,11 @@ describe('convertSchema()', () => {
|
|
|
65
71
|
|
|
66
72
|
it(`expands 'text' typess to 'string' JSON schema typess`, () => {
|
|
67
73
|
expect(convertSchema({
|
|
68
|
-
|
|
69
|
-
|
|
74
|
+
type: 'object',
|
|
75
|
+
properties: {
|
|
76
|
+
myText: {
|
|
77
|
+
type: 'text'
|
|
78
|
+
}
|
|
70
79
|
}
|
|
71
80
|
})).toEqual({
|
|
72
81
|
type: 'object',
|
|
@@ -79,87 +88,49 @@ describe('convertSchema()', () => {
|
|
|
79
88
|
})
|
|
80
89
|
})
|
|
81
90
|
|
|
82
|
-
it('
|
|
91
|
+
it('adds `required` arrays and formats for required properties', () => {
|
|
83
92
|
expect(convertSchema({
|
|
84
|
-
myNumber: 'number'
|
|
85
|
-
})).toEqual({
|
|
86
93
|
type: 'object',
|
|
87
94
|
properties: {
|
|
95
|
+
myString: {
|
|
96
|
+
type: 'string',
|
|
97
|
+
required: true
|
|
98
|
+
},
|
|
88
99
|
myNumber: {
|
|
89
|
-
type: 'number'
|
|
100
|
+
type: 'number',
|
|
101
|
+
required: true
|
|
90
102
|
}
|
|
91
|
-
}
|
|
92
|
-
additionalProperties: false
|
|
93
|
-
})
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
it('expands array property short-hands to array property schemas', () => {
|
|
97
|
-
expect(convertSchema({
|
|
98
|
-
myArray: [{
|
|
99
|
-
type: 'number'
|
|
100
|
-
}]
|
|
103
|
+
}
|
|
101
104
|
})).toEqual({
|
|
102
105
|
type: 'object',
|
|
103
106
|
properties: {
|
|
104
|
-
|
|
105
|
-
type: '
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
myString: {
|
|
108
|
+
type: 'string',
|
|
109
|
+
format: 'required'
|
|
110
|
+
},
|
|
111
|
+
myNumber: {
|
|
112
|
+
type: 'number',
|
|
113
|
+
format: 'required'
|
|
110
114
|
}
|
|
111
115
|
},
|
|
112
|
-
additionalProperties: false
|
|
116
|
+
additionalProperties: false,
|
|
117
|
+
required: ['myString', 'myNumber']
|
|
113
118
|
})
|
|
114
119
|
})
|
|
115
120
|
|
|
116
|
-
it('
|
|
121
|
+
it('preserves JSON schema-style `required` arrays', () => {
|
|
117
122
|
expect(convertSchema({
|
|
118
|
-
myArray: [{
|
|
119
|
-
myNumber: 'number'
|
|
120
|
-
}]
|
|
121
|
-
})).toEqual({
|
|
122
123
|
type: 'object',
|
|
124
|
+
required: ['myString', 'myNumber'],
|
|
123
125
|
properties: {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
items: {
|
|
127
|
-
type: 'object',
|
|
128
|
-
properties: {
|
|
129
|
-
myNumber: {
|
|
130
|
-
type: 'number'
|
|
131
|
-
}
|
|
132
|
-
},
|
|
133
|
-
additionalProperties: false
|
|
134
|
-
},
|
|
135
|
-
default: []
|
|
136
|
-
}
|
|
137
|
-
},
|
|
138
|
-
additionalProperties: false
|
|
139
|
-
})
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
it('adds `required` arrays and formats for required properties', () => {
|
|
143
|
-
expect(convertSchema({
|
|
144
|
-
myString: {
|
|
145
|
-
type: 'string',
|
|
146
|
-
required: true
|
|
147
|
-
},
|
|
148
|
-
myNumber: {
|
|
149
|
-
type: 'number',
|
|
150
|
-
required: true
|
|
126
|
+
myString: { type: 'string' },
|
|
127
|
+
myNumber: { type: 'number' }
|
|
151
128
|
}
|
|
152
129
|
})).toEqual({
|
|
153
130
|
type: 'object',
|
|
154
131
|
properties: {
|
|
155
|
-
myString: {
|
|
156
|
-
|
|
157
|
-
format: 'required'
|
|
158
|
-
},
|
|
159
|
-
myNumber: {
|
|
160
|
-
type: 'number',
|
|
161
|
-
format: 'required'
|
|
162
|
-
}
|
|
132
|
+
myString: { type: 'string' },
|
|
133
|
+
myNumber: { type: 'number' }
|
|
163
134
|
},
|
|
164
135
|
additionalProperties: false,
|
|
165
136
|
required: ['myString', 'myNumber']
|
|
@@ -168,9 +139,12 @@ describe('convertSchema()', () => {
|
|
|
168
139
|
|
|
169
140
|
it(`expands 'object' schemas with properties to JSON schemas allowing no additional properties`, () => {
|
|
170
141
|
expect(convertSchema({
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
142
|
+
type: 'object',
|
|
143
|
+
properties: {
|
|
144
|
+
myText: {
|
|
145
|
+
type: 'object',
|
|
146
|
+
properties: {}
|
|
147
|
+
}
|
|
174
148
|
}
|
|
175
149
|
})).toEqual({
|
|
176
150
|
type: 'object',
|
|
@@ -187,10 +161,13 @@ describe('convertSchema()', () => {
|
|
|
187
161
|
|
|
188
162
|
it('preserves preexisting settings for no additional properties', () => {
|
|
189
163
|
expect(convertSchema({
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
164
|
+
type: 'object',
|
|
165
|
+
properties: {
|
|
166
|
+
myText: {
|
|
167
|
+
type: 'object',
|
|
168
|
+
additionalProperties: true,
|
|
169
|
+
properties: {}
|
|
170
|
+
}
|
|
194
171
|
}
|
|
195
172
|
})).toEqual({
|
|
196
173
|
type: 'object',
|
|
@@ -207,12 +184,15 @@ describe('convertSchema()', () => {
|
|
|
207
184
|
|
|
208
185
|
it('expands nested object schemas with required properties', () => {
|
|
209
186
|
expect(convertSchema({
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
187
|
+
type: 'object',
|
|
188
|
+
properties: {
|
|
189
|
+
myText: {
|
|
190
|
+
type: 'object',
|
|
191
|
+
properties: {
|
|
192
|
+
myProperty: {
|
|
193
|
+
type: 'text',
|
|
194
|
+
required: true
|
|
195
|
+
}
|
|
216
196
|
}
|
|
217
197
|
}
|
|
218
198
|
}
|
|
@@ -237,11 +217,14 @@ describe('convertSchema()', () => {
|
|
|
237
217
|
|
|
238
218
|
it(`expands 'object' schemas with patternProperties`, () => {
|
|
239
219
|
expect(convertSchema({
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
'
|
|
244
|
-
|
|
220
|
+
type: 'object',
|
|
221
|
+
properties: {
|
|
222
|
+
myText: {
|
|
223
|
+
type: 'object',
|
|
224
|
+
patternProperties: {
|
|
225
|
+
'^.*$': {
|
|
226
|
+
type: 'text'
|
|
227
|
+
}
|
|
245
228
|
}
|
|
246
229
|
}
|
|
247
230
|
}
|
|
@@ -264,14 +247,17 @@ describe('convertSchema()', () => {
|
|
|
264
247
|
|
|
265
248
|
it('expands datetime types to their JSON schema representation', () => {
|
|
266
249
|
expect(convertSchema({
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
250
|
+
type: 'object',
|
|
251
|
+
properties: {
|
|
252
|
+
myDate: {
|
|
253
|
+
type: 'date'
|
|
254
|
+
},
|
|
255
|
+
myDateTime: {
|
|
256
|
+
type: 'datetime'
|
|
257
|
+
},
|
|
258
|
+
myTimeStamp: {
|
|
259
|
+
type: 'timestamp'
|
|
260
|
+
}
|
|
275
261
|
}
|
|
276
262
|
})).toEqual({
|
|
277
263
|
type: 'object',
|
|
@@ -295,8 +281,11 @@ describe('convertSchema()', () => {
|
|
|
295
281
|
|
|
296
282
|
it('expands unrecognized types to `$ref` references', () => {
|
|
297
283
|
expect(convertSchema({
|
|
298
|
-
|
|
299
|
-
|
|
284
|
+
type: 'object',
|
|
285
|
+
properties: {
|
|
286
|
+
myModel: {
|
|
287
|
+
type: 'MyModel'
|
|
288
|
+
}
|
|
300
289
|
}
|
|
301
290
|
})).toEqual({
|
|
302
291
|
type: 'object',
|
|
@@ -311,8 +300,11 @@ describe('convertSchema()', () => {
|
|
|
311
300
|
|
|
312
301
|
it(`expands unrecognized types to \`instanceof\` keywords when the \`useInstanceOf\` option is provided`, () => {
|
|
313
302
|
expect(convertSchema({
|
|
314
|
-
|
|
315
|
-
|
|
303
|
+
type: 'object',
|
|
304
|
+
properties: {
|
|
305
|
+
myModel: {
|
|
306
|
+
type: 'MyModel'
|
|
307
|
+
}
|
|
316
308
|
}
|
|
317
309
|
}, {
|
|
318
310
|
useInstanceOf: true
|
|
@@ -330,9 +322,12 @@ describe('convertSchema()', () => {
|
|
|
330
322
|
|
|
331
323
|
it('handles `nullable: true` correctly (now natively supported)', () => {
|
|
332
324
|
expect(convertSchema({
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
325
|
+
type: 'object',
|
|
326
|
+
properties: {
|
|
327
|
+
myString: {
|
|
328
|
+
type: 'string',
|
|
329
|
+
nullable: true
|
|
330
|
+
}
|
|
336
331
|
}
|
|
337
332
|
})).toEqual({
|
|
338
333
|
type: 'object',
|
|
@@ -348,9 +343,12 @@ describe('convertSchema()', () => {
|
|
|
348
343
|
|
|
349
344
|
it(`handles \`nullable: true\` references correctly`, () => {
|
|
350
345
|
expect(convertSchema({
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
346
|
+
type: 'object',
|
|
347
|
+
properties: {
|
|
348
|
+
myModel: {
|
|
349
|
+
type: 'MyModel',
|
|
350
|
+
nullable: true
|
|
351
|
+
}
|
|
354
352
|
}
|
|
355
353
|
})).toEqual({
|
|
356
354
|
type: 'object',
|
|
@@ -368,9 +366,12 @@ describe('convertSchema()', () => {
|
|
|
368
366
|
|
|
369
367
|
it(`handles \`nullable: true\` dates correctly (now natively supported)`, () => {
|
|
370
368
|
expect(convertSchema({
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
369
|
+
type: 'object',
|
|
370
|
+
properties: {
|
|
371
|
+
myDate: {
|
|
372
|
+
type: 'date',
|
|
373
|
+
nullable: true
|
|
374
|
+
}
|
|
374
375
|
}
|
|
375
376
|
})).toEqual({
|
|
376
377
|
type: 'object',
|
|
@@ -387,10 +388,13 @@ describe('convertSchema()', () => {
|
|
|
387
388
|
|
|
388
389
|
it(`handles \`nullable: true\` enums correctly`, () => {
|
|
389
390
|
expect(convertSchema({
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
391
|
+
type: 'object',
|
|
392
|
+
properties: {
|
|
393
|
+
myEnum: {
|
|
394
|
+
type: 'string',
|
|
395
|
+
enum: ['one', 'two', 'three'],
|
|
396
|
+
nullable: true
|
|
397
|
+
}
|
|
394
398
|
}
|
|
395
399
|
})).toEqual({
|
|
396
400
|
type: 'object',
|
|
@@ -404,195 +408,185 @@ describe('convertSchema()', () => {
|
|
|
404
408
|
additionalProperties: false
|
|
405
409
|
})
|
|
406
410
|
})
|
|
407
|
-
})
|
|
408
411
|
|
|
409
|
-
it('
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
{
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
type: 'string',
|
|
430
|
-
required: true
|
|
412
|
+
it('converts schemas within oneOf properties', () => {
|
|
413
|
+
expect(convertSchema({
|
|
414
|
+
type: 'object',
|
|
415
|
+
properties: {
|
|
416
|
+
myList: {
|
|
417
|
+
type: 'array',
|
|
418
|
+
items: {
|
|
419
|
+
oneOf: [
|
|
420
|
+
{
|
|
421
|
+
type: 'object',
|
|
422
|
+
properties: {
|
|
423
|
+
prop1: {
|
|
424
|
+
type: 'string',
|
|
425
|
+
required: true
|
|
426
|
+
},
|
|
427
|
+
prop2: {
|
|
428
|
+
type: 'number',
|
|
429
|
+
required: true
|
|
430
|
+
}
|
|
431
|
+
}
|
|
431
432
|
},
|
|
432
|
-
|
|
433
|
-
type: '
|
|
434
|
-
|
|
433
|
+
{
|
|
434
|
+
type: 'object',
|
|
435
|
+
properties: {
|
|
436
|
+
prop3: {
|
|
437
|
+
type: 'string',
|
|
438
|
+
required: true
|
|
439
|
+
},
|
|
440
|
+
prop4: {
|
|
441
|
+
type: 'number',
|
|
442
|
+
required: true
|
|
443
|
+
}
|
|
444
|
+
}
|
|
435
445
|
}
|
|
436
|
-
|
|
446
|
+
]
|
|
437
447
|
}
|
|
438
|
-
|
|
448
|
+
}
|
|
439
449
|
}
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
450
|
+
})).toEqual({
|
|
451
|
+
type: 'object',
|
|
452
|
+
properties: {
|
|
453
|
+
myList: {
|
|
454
|
+
type: 'array',
|
|
455
|
+
items: {
|
|
456
|
+
oneOf: [
|
|
457
|
+
{
|
|
458
|
+
type: 'object',
|
|
459
|
+
properties: {
|
|
460
|
+
prop1: {
|
|
461
|
+
type: 'string',
|
|
462
|
+
format: 'required'
|
|
463
|
+
},
|
|
464
|
+
prop2: {
|
|
465
|
+
type: 'number',
|
|
466
|
+
format: 'required'
|
|
467
|
+
}
|
|
454
468
|
},
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
format: 'required'
|
|
458
|
-
}
|
|
469
|
+
required: ['prop1', 'prop2'],
|
|
470
|
+
additionalProperties: false
|
|
459
471
|
},
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
472
|
+
{
|
|
473
|
+
type: 'object',
|
|
474
|
+
properties: {
|
|
475
|
+
prop3: {
|
|
476
|
+
type: 'string',
|
|
477
|
+
format: 'required'
|
|
478
|
+
},
|
|
479
|
+
prop4: {
|
|
480
|
+
type: 'number',
|
|
481
|
+
format: 'required'
|
|
482
|
+
}
|
|
469
483
|
},
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
required: ['prop3', 'prop4'],
|
|
476
|
-
additionalProperties: false
|
|
477
|
-
}
|
|
478
|
-
]
|
|
484
|
+
required: ['prop3', 'prop4'],
|
|
485
|
+
additionalProperties: false
|
|
486
|
+
}
|
|
487
|
+
]
|
|
488
|
+
}
|
|
479
489
|
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
|
|
490
|
+
},
|
|
491
|
+
additionalProperties: false
|
|
492
|
+
})
|
|
483
493
|
})
|
|
484
|
-
})
|
|
485
494
|
|
|
486
|
-
it('
|
|
487
|
-
|
|
488
|
-
myObject: {
|
|
495
|
+
it('supports `required: true` on object', () => {
|
|
496
|
+
expect(convertSchema({
|
|
489
497
|
type: 'object',
|
|
490
|
-
required: true,
|
|
491
498
|
properties: {
|
|
492
|
-
|
|
493
|
-
type: '
|
|
494
|
-
required: true
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
properties: {
|
|
505
|
-
myObject: {
|
|
506
|
-
type: 'object',
|
|
507
|
-
format: 'required',
|
|
508
|
-
properties: {
|
|
509
|
-
prop1: {
|
|
510
|
-
format: 'required',
|
|
511
|
-
type: 'string'
|
|
512
|
-
},
|
|
513
|
-
prop2: {
|
|
514
|
-
format: 'required',
|
|
515
|
-
type: 'number'
|
|
499
|
+
myObject: {
|
|
500
|
+
type: 'object',
|
|
501
|
+
required: true,
|
|
502
|
+
properties: {
|
|
503
|
+
prop1: {
|
|
504
|
+
type: 'string',
|
|
505
|
+
required: true
|
|
506
|
+
},
|
|
507
|
+
prop2: {
|
|
508
|
+
type: 'number',
|
|
509
|
+
required: true
|
|
510
|
+
}
|
|
516
511
|
}
|
|
517
|
-
}
|
|
518
|
-
additionalProperties: false,
|
|
519
|
-
required: ['prop1', 'prop2']
|
|
520
|
-
}
|
|
521
|
-
},
|
|
522
|
-
additionalProperties: false,
|
|
523
|
-
required: ['myObject']
|
|
524
|
-
})
|
|
525
|
-
})
|
|
526
|
-
|
|
527
|
-
it('support `required` on object short-hand', () => {
|
|
528
|
-
expect(convertSchema({
|
|
529
|
-
myObject: {
|
|
530
|
-
required: true,
|
|
531
|
-
prop1: {
|
|
532
|
-
type: 'string',
|
|
533
|
-
required: true
|
|
534
|
-
},
|
|
535
|
-
prop2: {
|
|
536
|
-
type: 'number',
|
|
537
|
-
required: true
|
|
512
|
+
}
|
|
538
513
|
}
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
514
|
+
})).toEqual({
|
|
515
|
+
type: 'object',
|
|
516
|
+
properties: {
|
|
517
|
+
myObject: {
|
|
518
|
+
type: 'object',
|
|
519
|
+
format: 'required',
|
|
520
|
+
properties: {
|
|
521
|
+
prop1: {
|
|
522
|
+
format: 'required',
|
|
523
|
+
type: 'string'
|
|
524
|
+
},
|
|
525
|
+
prop2: {
|
|
526
|
+
format: 'required',
|
|
527
|
+
type: 'number'
|
|
528
|
+
}
|
|
550
529
|
},
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
}
|
|
555
|
-
},
|
|
556
|
-
additionalProperties: false,
|
|
557
|
-
required: ['prop1', 'prop2']
|
|
558
|
-
}
|
|
559
|
-
},
|
|
560
|
-
additionalProperties: false,
|
|
561
|
-
required: ['myObject']
|
|
562
|
-
})
|
|
563
|
-
})
|
|
564
|
-
|
|
565
|
-
describe('expandSchemaShorthand()', () => {
|
|
566
|
-
it('expands strings to schemas', () => {
|
|
567
|
-
expect(expandSchemaShorthand('number')).toEqual({
|
|
568
|
-
type: 'number'
|
|
569
|
-
})
|
|
570
|
-
})
|
|
571
|
-
|
|
572
|
-
it('expands array property short-hands to array property schemas', () => {
|
|
573
|
-
expect(expandSchemaShorthand([{
|
|
574
|
-
type: 'number'
|
|
575
|
-
}])).toEqual({
|
|
576
|
-
type: 'array',
|
|
577
|
-
items: {
|
|
578
|
-
type: 'number'
|
|
530
|
+
additionalProperties: false,
|
|
531
|
+
required: ['prop1', 'prop2']
|
|
532
|
+
}
|
|
579
533
|
},
|
|
580
|
-
|
|
534
|
+
additionalProperties: false,
|
|
535
|
+
required: ['myObject']
|
|
581
536
|
})
|
|
582
537
|
})
|
|
583
538
|
|
|
584
|
-
it('
|
|
585
|
-
expect(
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
539
|
+
it('processes discriminator schemas correctly', () => {
|
|
540
|
+
expect(convertSchema({
|
|
541
|
+
type: 'object',
|
|
542
|
+
discriminator: { propertyName: 'foo' },
|
|
543
|
+
required: ['foo'],
|
|
544
|
+
oneOf: [
|
|
545
|
+
{
|
|
546
|
+
properties: {
|
|
547
|
+
foo: { const: 'x' },
|
|
548
|
+
a: {
|
|
549
|
+
type: 'string',
|
|
550
|
+
required: true
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
properties: {
|
|
556
|
+
foo: { enum: ['y', 'z'] },
|
|
557
|
+
b: {
|
|
558
|
+
type: 'string',
|
|
559
|
+
required: true
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
]
|
|
589
564
|
})).toEqual({
|
|
590
565
|
type: 'object',
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
566
|
+
discriminator: { propertyName: 'foo' },
|
|
567
|
+
required: ['foo'],
|
|
568
|
+
oneOf: [
|
|
569
|
+
{
|
|
570
|
+
properties: {
|
|
571
|
+
foo: { const: 'x' },
|
|
572
|
+
a: {
|
|
573
|
+
type: 'string',
|
|
574
|
+
format: 'required'
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
required: ['a']
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
properties: {
|
|
581
|
+
foo: { enum: ['y', 'z'] },
|
|
582
|
+
b: {
|
|
583
|
+
type: 'string',
|
|
584
|
+
format: 'required'
|
|
585
|
+
}
|
|
586
|
+
},
|
|
587
|
+
required: ['b']
|
|
594
588
|
}
|
|
595
|
-
|
|
589
|
+
]
|
|
596
590
|
})
|
|
597
591
|
})
|
|
598
592
|
})
|