@ditojs/server 1.7.0 → 1.8.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ditojs/server",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
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,10 +21,10 @@
21
21
  "node >= 16"
22
22
  ],
23
23
  "dependencies": {
24
- "@ditojs/admin": "^1.7.0",
25
- "@ditojs/build": "^1.7.0",
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",
@@ -79,5 +79,5 @@
79
79
  "knex": "^2.0.0",
80
80
  "objection": "^3.0.1"
81
81
  },
82
- "gitHead": "20bdea5596f70da591987707df7aa5336ecb6aa3"
82
+ "gitHead": "bb2c9e85083eabc51d1f4b1fd0bffd6b306b0e47"
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',
@@ -360,7 +360,10 @@ 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)
@@ -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) {
@@ -1,19 +1,14 @@
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)) {
13
8
  const { type } = schema
14
9
  // Create a shallow clone so we can modify and return, excluding our
15
10
  // `required` boolean which will get converted to a format further down, and
16
- // to JSON schema's `required` array through `expandProperties()`:
11
+ // to JSON schema's `required` array through `convertProperties()`:
17
12
  const { required, ...rest } = schema
18
13
  schema = rest
19
14
  if (isString(type)) {
@@ -24,7 +19,7 @@ export function convertSchema(schema, options = {}) {
24
19
  if (jsonType === 'object') {
25
20
  let setAdditionalProperties = false
26
21
  if (schema.properties) {
27
- const { properties, required } = expandProperties(
22
+ const { properties, required } = convertProperties(
28
23
  schema.properties,
29
24
  options
30
25
  )
@@ -35,7 +30,8 @@ export function convertSchema(schema, options = {}) {
35
30
  setAdditionalProperties = true
36
31
  }
37
32
  if (schema.patternProperties) {
38
- const { properties } = expandProperties(
33
+ // TODO: Don't we need to handle required here too?
34
+ const { properties } = convertProperties(
39
35
  schema.patternProperties,
40
36
  options
41
37
  )
@@ -87,13 +83,6 @@ export function convertSchema(schema, options = {}) {
87
83
  }
88
84
  }
89
85
  } 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
86
  // Handle nested allOf, anyOf, oneOf, not properties
98
87
  for (const key of ['allOf', 'anyOf', 'oneOf', 'not']) {
99
88
  if (key in schema) {
@@ -119,11 +108,10 @@ export function convertSchema(schema, options = {}) {
119
108
  return schema
120
109
  }
121
110
 
122
- export function expandProperties(schemaProperties, options) {
111
+ export function convertProperties(schemaProperties, options) {
123
112
  const properties = {}
124
113
  const required = []
125
- for (let [key, property] of Object.entries(schemaProperties)) {
126
- property = expandSchemaShorthand(property)
114
+ for (const [key, property] of Object.entries(schemaProperties)) {
127
115
  properties[key] = convertSchema(property, options)
128
116
  if (property?.required) {
129
117
  required.push(key)
@@ -132,52 +120,6 @@ export function expandProperties(schemaProperties, options) {
132
120
  return { properties, required }
133
121
  }
134
122
 
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
123
  function addFormat(schema, newFormat) {
182
124
  // Support multiple `format` keywords through `allOf`:
183
125
  let { allOf, format, ...rest } = schema
@@ -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,89 +71,35 @@ describe('convertSchema()', () => {
65
71
 
66
72
  it(`expands 'text' typess to 'string' JSON schema typess`, () => {
67
73
  expect(convertSchema({
68
- myText: {
69
- type: 'text'
70
- }
71
- })).toEqual({
72
74
  type: 'object',
73
75
  properties: {
74
76
  myText: {
75
- type: 'string'
76
- }
77
- },
78
- additionalProperties: false
79
- })
80
- })
81
-
82
- it('expands strings property short-hands to property schemas', () => {
83
- expect(convertSchema({
84
- myNumber: 'number'
85
- })).toEqual({
86
- type: 'object',
87
- properties: {
88
- myNumber: {
89
- type: 'number'
77
+ type: 'text'
90
78
  }
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
- }]
79
+ }
101
80
  })).toEqual({
102
81
  type: 'object',
103
82
  properties: {
104
- myArray: {
105
- type: 'array',
106
- items: {
107
- type: 'number'
108
- },
109
- default: []
83
+ myText: {
84
+ type: 'string'
110
85
  }
111
86
  },
112
87
  additionalProperties: false
113
88
  })
114
89
  })
115
90
 
116
- it('expands nested array property and object short-hands', () => {
91
+ it('adds `required` arrays and formats for required properties', () => {
117
92
  expect(convertSchema({
118
- myArray: [{
119
- myNumber: 'number'
120
- }]
121
- })).toEqual({
122
93
  type: 'object',
123
94
  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: []
95
+ myString: {
96
+ type: 'string',
97
+ required: true
98
+ },
99
+ myNumber: {
100
+ type: 'number',
101
+ required: true
136
102
  }
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
151
103
  }
152
104
  })).toEqual({
153
105
  type: 'object',
@@ -168,9 +120,12 @@ describe('convertSchema()', () => {
168
120
 
169
121
  it(`expands 'object' schemas with properties to JSON schemas allowing no additional properties`, () => {
170
122
  expect(convertSchema({
171
- myText: {
172
- type: 'object',
173
- properties: {}
123
+ type: 'object',
124
+ properties: {
125
+ myText: {
126
+ type: 'object',
127
+ properties: {}
128
+ }
174
129
  }
175
130
  })).toEqual({
176
131
  type: 'object',
@@ -187,10 +142,13 @@ describe('convertSchema()', () => {
187
142
 
188
143
  it('preserves preexisting settings for no additional properties', () => {
189
144
  expect(convertSchema({
190
- myText: {
191
- type: 'object',
192
- additionalProperties: true,
193
- properties: {}
145
+ type: 'object',
146
+ properties: {
147
+ myText: {
148
+ type: 'object',
149
+ additionalProperties: true,
150
+ properties: {}
151
+ }
194
152
  }
195
153
  })).toEqual({
196
154
  type: 'object',
@@ -207,12 +165,15 @@ describe('convertSchema()', () => {
207
165
 
208
166
  it('expands nested object schemas with required properties', () => {
209
167
  expect(convertSchema({
210
- myText: {
211
- type: 'object',
212
- properties: {
213
- myProperty: {
214
- type: 'text',
215
- required: true
168
+ type: 'object',
169
+ properties: {
170
+ myText: {
171
+ type: 'object',
172
+ properties: {
173
+ myProperty: {
174
+ type: 'text',
175
+ required: true
176
+ }
216
177
  }
217
178
  }
218
179
  }
@@ -237,11 +198,14 @@ describe('convertSchema()', () => {
237
198
 
238
199
  it(`expands 'object' schemas with patternProperties`, () => {
239
200
  expect(convertSchema({
240
- myText: {
241
- type: 'object',
242
- patternProperties: {
243
- '^.*$': {
244
- type: 'text'
201
+ type: 'object',
202
+ properties: {
203
+ myText: {
204
+ type: 'object',
205
+ patternProperties: {
206
+ '^.*$': {
207
+ type: 'text'
208
+ }
245
209
  }
246
210
  }
247
211
  }
@@ -264,14 +228,17 @@ describe('convertSchema()', () => {
264
228
 
265
229
  it('expands datetime types to their JSON schema representation', () => {
266
230
  expect(convertSchema({
267
- myDate: {
268
- type: 'date'
269
- },
270
- myDateTime: {
271
- type: 'datetime'
272
- },
273
- myTimeStamp: {
274
- type: 'timestamp'
231
+ type: 'object',
232
+ properties: {
233
+ myDate: {
234
+ type: 'date'
235
+ },
236
+ myDateTime: {
237
+ type: 'datetime'
238
+ },
239
+ myTimeStamp: {
240
+ type: 'timestamp'
241
+ }
275
242
  }
276
243
  })).toEqual({
277
244
  type: 'object',
@@ -295,8 +262,11 @@ describe('convertSchema()', () => {
295
262
 
296
263
  it('expands unrecognized types to `$ref` references', () => {
297
264
  expect(convertSchema({
298
- myModel: {
299
- type: 'MyModel'
265
+ type: 'object',
266
+ properties: {
267
+ myModel: {
268
+ type: 'MyModel'
269
+ }
300
270
  }
301
271
  })).toEqual({
302
272
  type: 'object',
@@ -311,8 +281,11 @@ describe('convertSchema()', () => {
311
281
 
312
282
  it(`expands unrecognized types to \`instanceof\` keywords when the \`useInstanceOf\` option is provided`, () => {
313
283
  expect(convertSchema({
314
- myModel: {
315
- type: 'MyModel'
284
+ type: 'object',
285
+ properties: {
286
+ myModel: {
287
+ type: 'MyModel'
288
+ }
316
289
  }
317
290
  }, {
318
291
  useInstanceOf: true
@@ -330,9 +303,12 @@ describe('convertSchema()', () => {
330
303
 
331
304
  it('handles `nullable: true` correctly (now natively supported)', () => {
332
305
  expect(convertSchema({
333
- myString: {
334
- type: 'string',
335
- nullable: true
306
+ type: 'object',
307
+ properties: {
308
+ myString: {
309
+ type: 'string',
310
+ nullable: true
311
+ }
336
312
  }
337
313
  })).toEqual({
338
314
  type: 'object',
@@ -348,9 +324,12 @@ describe('convertSchema()', () => {
348
324
 
349
325
  it(`handles \`nullable: true\` references correctly`, () => {
350
326
  expect(convertSchema({
351
- myModel: {
352
- type: 'MyModel',
353
- nullable: true
327
+ type: 'object',
328
+ properties: {
329
+ myModel: {
330
+ type: 'MyModel',
331
+ nullable: true
332
+ }
354
333
  }
355
334
  })).toEqual({
356
335
  type: 'object',
@@ -368,9 +347,12 @@ describe('convertSchema()', () => {
368
347
 
369
348
  it(`handles \`nullable: true\` dates correctly (now natively supported)`, () => {
370
349
  expect(convertSchema({
371
- myDate: {
372
- type: 'date',
373
- nullable: true
350
+ type: 'object',
351
+ properties: {
352
+ myDate: {
353
+ type: 'date',
354
+ nullable: true
355
+ }
374
356
  }
375
357
  })).toEqual({
376
358
  type: 'object',
@@ -387,10 +369,13 @@ describe('convertSchema()', () => {
387
369
 
388
370
  it(`handles \`nullable: true\` enums correctly`, () => {
389
371
  expect(convertSchema({
390
- myEnum: {
391
- type: 'string',
392
- enum: ['one', 'two', 'three'],
393
- nullable: true
372
+ type: 'object',
373
+ properties: {
374
+ myEnum: {
375
+ type: 'string',
376
+ enum: ['one', 'two', 'three'],
377
+ nullable: true
378
+ }
394
379
  }
395
380
  })).toEqual({
396
381
  type: 'object',
@@ -404,195 +389,131 @@ describe('convertSchema()', () => {
404
389
  additionalProperties: false
405
390
  })
406
391
  })
407
- })
408
392
 
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
393
+ it('convert schemas within oneOf properties', () => {
394
+ expect(convertSchema({
395
+ type: 'object',
396
+ properties: {
397
+ myList: {
398
+ type: 'array',
399
+ items: {
400
+ oneOf: [
401
+ {
402
+ type: 'object',
403
+ properties: {
404
+ prop1: {
405
+ type: 'string',
406
+ required: true
407
+ },
408
+ prop2: {
409
+ type: 'number',
410
+ required: true
411
+ }
412
+ }
431
413
  },
432
- prop4: {
433
- type: 'number',
434
- required: true
414
+ {
415
+ type: 'object',
416
+ properties: {
417
+ prop3: {
418
+ type: 'string',
419
+ required: true
420
+ },
421
+ prop4: {
422
+ type: 'number',
423
+ required: true
424
+ }
425
+ }
435
426
  }
436
- }
427
+ ]
437
428
  }
438
- ]
429
+ }
439
430
  }
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'
431
+ })).toEqual({
432
+ type: 'object',
433
+ properties: {
434
+ myList: {
435
+ type: 'array',
436
+ items: {
437
+ oneOf: [
438
+ {
439
+ type: 'object',
440
+ properties: {
441
+ prop1: {
442
+ type: 'string',
443
+ format: 'required'
444
+ },
445
+ prop2: {
446
+ type: 'number',
447
+ format: 'required'
448
+ }
454
449
  },
455
- prop2: {
456
- type: 'number',
457
- format: 'required'
458
- }
450
+ required: ['prop1', 'prop2'],
451
+ additionalProperties: false
459
452
  },
460
- required: ['prop1', 'prop2'],
461
- additionalProperties: false
462
- },
463
- {
464
- type: 'object',
465
- properties: {
466
- prop3: {
467
- type: 'string',
468
- format: 'required'
453
+ {
454
+ type: 'object',
455
+ properties: {
456
+ prop3: {
457
+ type: 'string',
458
+ format: 'required'
459
+ },
460
+ prop4: {
461
+ type: 'number',
462
+ format: 'required'
463
+ }
469
464
  },
470
- prop4: {
471
- type: 'number',
472
- format: 'required'
473
- }
474
- },
475
- required: ['prop3', 'prop4'],
476
- additionalProperties: false
477
- }
478
- ]
465
+ required: ['prop3', 'prop4'],
466
+ additionalProperties: false
467
+ }
468
+ ]
469
+ }
479
470
  }
480
- }
481
- },
482
- additionalProperties: false
471
+ },
472
+ additionalProperties: false
473
+ })
483
474
  })
484
- })
485
475
 
486
- it('support `required: true` on object', () => {
487
- expect(convertSchema({
488
- myObject: {
476
+ it('support `required: true` on object', () => {
477
+ expect(convertSchema({
489
478
  type: 'object',
490
- required: true,
491
479
  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'
516
- }
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
538
- }
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'
550
- },
551
- prop2: {
552
- type: 'number',
553
- format: 'required'
480
+ myObject: {
481
+ type: 'object',
482
+ required: true,
483
+ properties: {
484
+ prop1: {
485
+ type: 'string',
486
+ required: true
487
+ },
488
+ prop2: {
489
+ type: 'number',
490
+ required: true
491
+ }
554
492
  }
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'
579
- },
580
- default: []
581
- })
582
- })
583
-
584
- it('expands objects property short-hands to object property schemas', () => {
585
- expect(expandSchemaShorthand({
586
- myNumber: {
587
- type: 'number'
493
+ }
588
494
  }
589
495
  })).toEqual({
590
496
  type: 'object',
591
497
  properties: {
592
- myNumber: {
593
- type: 'number'
498
+ myObject: {
499
+ type: 'object',
500
+ format: 'required',
501
+ properties: {
502
+ prop1: {
503
+ format: 'required',
504
+ type: 'string'
505
+ },
506
+ prop2: {
507
+ format: 'required',
508
+ type: 'number'
509
+ }
510
+ },
511
+ additionalProperties: false,
512
+ required: ['prop1', 'prop2']
594
513
  }
595
- }
514
+ },
515
+ additionalProperties: false,
516
+ required: ['myObject']
596
517
  })
597
518
  })
598
519
  })