@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ditojs/server",
3
- "version": "1.6.0",
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.6.0",
25
- "@ditojs/build": "^1.5.5",
26
- "@ditojs/router": "^1.6.0",
27
- "@ditojs/utils": "^1.6.0",
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.1135.0",
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.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": "a442b0e47237fef8aeb65793fa3f5bc6621442ce"
82
+ "gitHead": "390e0a7e0782b3cb5cff5e5d0454a35c7a954df5"
83
83
  }
@@ -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
- // NOTE: If properties is null, schema and validate will become null too.
458
- // NOTE: If it is not null, it will get expanded to an object schema.
459
- const schema = convertSchema(properties, options)
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',
@@ -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,
@@ -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(this.definition.properties)
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 (let [name, property] of Object.entries(properties)) {
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
- items: [
5
+ prefixItems: [
6
6
  { type: 'number' },
7
7
  { type: 'number' }
8
8
  ],
9
- additionalItems: false
9
+ minItems: 2,
10
+ items: false
10
11
  },
11
12
  macro(config) {
12
13
  return {
@@ -1,4 +1,4 @@
1
- import Ajv from 'ajv'
1
+ import Ajv from 'ajv/dist/2020.js'
2
2
  import { isNumber, isArray } from '@ditojs/utils'
3
3
 
4
4
  export const validate = {
@@ -1,59 +1,65 @@
1
1
  import { isObject, isArray, isString } from '@ditojs/utils'
2
2
 
3
3
  export function convertSchema(schema, options = {}) {
4
- if (isString(schema)) {
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
- if (isObject(schema)) {
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
- // Create a shallow clone so we can modify and return, excluding our
15
- // `required` boolean which will get converted to a format further down, and
16
- // to JSON schema's `required` array through `expandProperties()`:
17
- const { required, ...rest } = schema
18
- schema = rest
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 (jsonType === 'object') {
25
- let setAdditionalProperties = false
26
- if (schema.properties) {
27
- const { properties, required } = expandProperties(
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 expandProperties(schemaProperties, options) {
108
+ export function convertProperties(schemaProperties, options) {
123
109
  const properties = {}
124
110
  const required = []
125
- for (let [key, property] of Object.entries(schemaProperties)) {
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
- let { allOf, format, ...rest } = schema
122
+ const { allOf, format, ...rest } = schema
184
123
  if (format || allOf) {
185
- allOf ||= []
186
- if (!allOf.find(({ format }) => format === newFormat)) {
187
- allOf.push({ format }, { format: newFormat })
188
- schema = { ...rest, allOf }
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, expandSchemaShorthand } from './properties.js'
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(properties)).toEqual({
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(properties)).toEqual({
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
- myText: {
69
- type: 'text'
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('expands strings property short-hands to property schemas', () => {
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
- myArray: {
105
- type: 'array',
106
- items: {
107
- type: 'number'
108
- },
109
- default: []
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('expands nested array property and object short-hands', () => {
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
- myArray: {
125
- type: 'array',
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
- type: 'string',
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
- myText: {
172
- type: 'object',
173
- properties: {}
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
- myText: {
191
- type: 'object',
192
- additionalProperties: true,
193
- properties: {}
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
- myText: {
211
- type: 'object',
212
- properties: {
213
- myProperty: {
214
- type: 'text',
215
- required: true
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
- myText: {
241
- type: 'object',
242
- patternProperties: {
243
- '^.*$': {
244
- type: 'text'
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
- myDate: {
268
- type: 'date'
269
- },
270
- myDateTime: {
271
- type: 'datetime'
272
- },
273
- myTimeStamp: {
274
- type: 'timestamp'
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
- myModel: {
299
- type: 'MyModel'
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
- myModel: {
315
- type: 'MyModel'
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
- myString: {
334
- type: 'string',
335
- nullable: true
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
- myModel: {
352
- type: 'MyModel',
353
- nullable: true
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
- myDate: {
372
- type: 'date',
373
- nullable: true
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
- myEnum: {
391
- type: 'string',
392
- enum: ['one', 'two', 'three'],
393
- nullable: true
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('convert schemas within oneOf properties', () => {
410
- expect(convertSchema({
411
- myList: {
412
- type: 'array',
413
- items: {
414
- oneOf: [
415
- {
416
- prop1: {
417
- type: 'string',
418
- required: true
419
- },
420
- prop2: {
421
- type: 'number',
422
- required: true
423
- }
424
- },
425
- {
426
- type: 'object',
427
- properties: {
428
- prop3: {
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
- prop4: {
433
- type: 'number',
434
- required: true
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
- })).toEqual({
442
- type: 'object',
443
- properties: {
444
- myList: {
445
- type: 'array',
446
- items: {
447
- oneOf: [
448
- {
449
- type: 'object',
450
- properties: {
451
- prop1: {
452
- type: 'string',
453
- format: 'required'
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
- prop2: {
456
- type: 'number',
457
- format: 'required'
458
- }
469
+ required: ['prop1', 'prop2'],
470
+ additionalProperties: false
459
471
  },
460
- required: ['prop1', 'prop2'],
461
- additionalProperties: false
462
- },
463
- {
464
- type: 'object',
465
- properties: {
466
- prop3: {
467
- type: 'string',
468
- format: 'required'
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
- prop4: {
471
- type: 'number',
472
- format: 'required'
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
- additionalProperties: false
490
+ },
491
+ additionalProperties: false
492
+ })
483
493
  })
484
- })
485
494
 
486
- it('support `required: true` on object', () => {
487
- expect(convertSchema({
488
- myObject: {
495
+ it('supports `required: true` on object', () => {
496
+ expect(convertSchema({
489
497
  type: 'object',
490
- required: true,
491
498
  properties: {
492
- prop1: {
493
- type: 'string',
494
- required: true
495
- },
496
- prop2: {
497
- type: 'number',
498
- required: true
499
- }
500
- }
501
- }
502
- })).toEqual({
503
- type: 'object',
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
- })).toEqual({
541
- type: 'object',
542
- properties: {
543
- myObject: {
544
- type: 'object',
545
- format: 'required',
546
- properties: {
547
- prop1: {
548
- type: 'string',
549
- format: 'required'
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
- prop2: {
552
- type: 'number',
553
- format: 'required'
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
- default: []
534
+ additionalProperties: false,
535
+ required: ['myObject']
581
536
  })
582
537
  })
583
538
 
584
- it('expands objects property short-hands to object property schemas', () => {
585
- expect(expandSchemaShorthand({
586
- myNumber: {
587
- type: 'number'
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
- properties: {
592
- myNumber: {
593
- type: 'number'
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
  })