@jskit-ai/crud-core 0.1.63 → 0.1.64
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.descriptor.mjs +4 -2
- package/package.json +18 -10
- package/src/server/crudModuleConfig.js +25 -5
- package/src/server/fieldAccess.js +9 -39
- package/src/server/listFilters.js +384 -389
- package/src/server/listQueryValidators.js +39 -77
- package/src/server/lookupHydration.js +4 -1
- package/src/server/repositorySupport.js +71 -121
- package/src/server/resourceRuntime/index.js +49 -74
- package/src/server/resourceRuntime/lookupHydration.js +4 -1
- package/src/server/routeContracts.js +74 -0
- package/src/server/serviceEvents.js +75 -4
- package/src/shared/crudFieldSupport.js +54 -0
- package/src/shared/crudNamespaceSupport.js +1 -27
- package/src/shared/crudResource.js +1 -0
- package/test/createCrudServiceFromResource.test.js +30 -28
- package/test/{crudFieldMetaSupport.test.js → crudFieldSupport.test.js} +1 -1
- package/test/crudModuleConfig.test.js +33 -0
- package/test/crudResource.test.js +97 -0
- package/test/listFilters.test.js +221 -59
- package/test/listQueryValidators.test.js +131 -97
- package/test/repositorySupport.test.js +241 -241
- package/test/resourceRuntime.test.js +204 -248
- package/test/routeContracts.test.js +146 -0
- package/test/serviceEvents.test.js +41 -1
- package/test/serviceMethods.test.js +12 -10
- package/src/shared/crudFieldMetaSupport.js +0 -153
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import test from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
+
import { createSchema } from "json-rest-schema";
|
|
3
4
|
import {
|
|
4
5
|
DEFAULT_LIST_LIMIT,
|
|
5
6
|
normalizeCrudListLimit,
|
|
@@ -80,6 +81,13 @@ function createQueryDouble() {
|
|
|
80
81
|
};
|
|
81
82
|
}
|
|
82
83
|
|
|
84
|
+
function createOperationSchemaDefinition(structure = {}, mode = "replace") {
|
|
85
|
+
return {
|
|
86
|
+
schema: createSchema(structure),
|
|
87
|
+
mode
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
83
91
|
test("normalizeCrudListLimit enforces fallback and max", () => {
|
|
84
92
|
assert.equal(normalizeCrudListLimit(null), DEFAULT_LIST_LIMIT);
|
|
85
93
|
assert.equal(normalizeCrudListLimit("abc"), DEFAULT_LIST_LIMIT);
|
|
@@ -262,59 +270,48 @@ test("deriveRepositoryMappingFromResource reads schema keys and repository colum
|
|
|
262
270
|
const resource = {
|
|
263
271
|
operations: {
|
|
264
272
|
view: {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}
|
|
273
|
+
output: createOperationSchemaDefinition({
|
|
274
|
+
id: { type: "integer", required: true },
|
|
275
|
+
firstName: { type: "string", required: true },
|
|
276
|
+
createdAt: {
|
|
277
|
+
type: "string",
|
|
278
|
+
required: true,
|
|
279
|
+
actualField: "created_at"
|
|
273
280
|
}
|
|
274
|
-
}
|
|
281
|
+
})
|
|
275
282
|
},
|
|
276
283
|
create: {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
284
|
+
body: createOperationSchemaDefinition({
|
|
285
|
+
firstName: { type: "string" },
|
|
286
|
+
vetId: {
|
|
287
|
+
type: "integer",
|
|
288
|
+
actualField: "vet_id",
|
|
289
|
+
relation: {
|
|
290
|
+
kind: "lookup",
|
|
291
|
+
namespace: "vets",
|
|
292
|
+
valueKey: "id"
|
|
283
293
|
}
|
|
284
294
|
}
|
|
285
|
-
}
|
|
295
|
+
}, "create")
|
|
286
296
|
},
|
|
287
297
|
patch: {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
type: "
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
298
|
+
body: createOperationSchemaDefinition({
|
|
299
|
+
archivedAt: {
|
|
300
|
+
type: "string",
|
|
301
|
+
actualField: "archived_at"
|
|
302
|
+
},
|
|
303
|
+
vetId: {
|
|
304
|
+
type: "integer",
|
|
305
|
+
actualField: "vet_id",
|
|
306
|
+
relation: {
|
|
307
|
+
kind: "lookup",
|
|
308
|
+
namespace: "vets",
|
|
309
|
+
valueKey: "id"
|
|
294
310
|
}
|
|
295
311
|
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
},
|
|
299
|
-
fieldMeta: [
|
|
300
|
-
{
|
|
301
|
-
key: "createdAt",
|
|
302
|
-
repository: { column: "created_at" }
|
|
303
|
-
},
|
|
304
|
-
{
|
|
305
|
-
key: "vetId",
|
|
306
|
-
repository: { column: "vet_id" },
|
|
307
|
-
relation: {
|
|
308
|
-
kind: "lookup",
|
|
309
|
-
namespace: "vets",
|
|
310
|
-
valueKey: "id"
|
|
311
|
-
}
|
|
312
|
-
},
|
|
313
|
-
{
|
|
314
|
-
key: "archivedAt",
|
|
315
|
-
repository: { column: "archived_at" }
|
|
312
|
+
}, "patch")
|
|
316
313
|
}
|
|
317
|
-
|
|
314
|
+
}
|
|
318
315
|
};
|
|
319
316
|
|
|
320
317
|
const mapping = deriveRepositoryMappingFromResource(resource);
|
|
@@ -335,36 +332,23 @@ test("deriveRepositoryMappingFromResource treats virtual output fields as non-co
|
|
|
335
332
|
const resource = {
|
|
336
333
|
operations: {
|
|
337
334
|
view: {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
335
|
+
output: createOperationSchemaDefinition({
|
|
336
|
+
id: { type: "integer", required: true },
|
|
337
|
+
firstName: { type: "string", required: true },
|
|
338
|
+
remainingBatchWeight: {
|
|
339
|
+
type: "number",
|
|
340
|
+
storage: {
|
|
341
|
+
virtual: true
|
|
345
342
|
}
|
|
346
343
|
}
|
|
347
|
-
}
|
|
344
|
+
})
|
|
348
345
|
},
|
|
349
346
|
create: {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
properties: {
|
|
354
|
-
firstName: { type: "string" }
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
},
|
|
360
|
-
fieldMeta: [
|
|
361
|
-
{
|
|
362
|
-
key: "remainingBatchWeight",
|
|
363
|
-
repository: {
|
|
364
|
-
storage: "virtual"
|
|
365
|
-
}
|
|
347
|
+
body: createOperationSchemaDefinition({
|
|
348
|
+
firstName: { type: "string" }
|
|
349
|
+
}, "create")
|
|
366
350
|
}
|
|
367
|
-
|
|
351
|
+
}
|
|
368
352
|
};
|
|
369
353
|
|
|
370
354
|
const mapping = deriveRepositoryMappingFromResource(resource);
|
|
@@ -379,40 +363,32 @@ test("deriveRepositoryMappingFromResource rejects virtual fields in create schem
|
|
|
379
363
|
const resource = {
|
|
380
364
|
operations: {
|
|
381
365
|
view: {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
366
|
+
output: createOperationSchemaDefinition({
|
|
367
|
+
id: { type: "integer", required: true },
|
|
368
|
+
remainingBatchWeight: {
|
|
369
|
+
type: "number",
|
|
370
|
+
storage: {
|
|
371
|
+
virtual: true
|
|
388
372
|
}
|
|
389
373
|
}
|
|
390
|
-
}
|
|
374
|
+
})
|
|
391
375
|
},
|
|
392
376
|
create: {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
type: "
|
|
396
|
-
|
|
397
|
-
|
|
377
|
+
body: createOperationSchemaDefinition({
|
|
378
|
+
remainingBatchWeight: {
|
|
379
|
+
type: "number",
|
|
380
|
+
storage: {
|
|
381
|
+
virtual: true
|
|
398
382
|
}
|
|
399
383
|
}
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
},
|
|
403
|
-
fieldMeta: [
|
|
404
|
-
{
|
|
405
|
-
key: "remainingBatchWeight",
|
|
406
|
-
repository: {
|
|
407
|
-
storage: "virtual"
|
|
408
|
-
}
|
|
384
|
+
}, "create")
|
|
409
385
|
}
|
|
410
|
-
|
|
386
|
+
}
|
|
411
387
|
};
|
|
412
388
|
|
|
413
389
|
assert.throws(
|
|
414
390
|
() => deriveRepositoryMappingFromResource(resource),
|
|
415
|
-
/resource create schema field "remainingBatchWeight" cannot use
|
|
391
|
+
/resource create schema field "remainingBatchWeight" cannot use storage\.virtual/
|
|
416
392
|
);
|
|
417
393
|
});
|
|
418
394
|
|
|
@@ -420,48 +396,35 @@ test("deriveRepositoryMappingFromResource rejects virtual fields in patch schema
|
|
|
420
396
|
const resource = {
|
|
421
397
|
operations: {
|
|
422
398
|
view: {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
399
|
+
output: createOperationSchemaDefinition({
|
|
400
|
+
id: { type: "integer", required: true },
|
|
401
|
+
remainingBatchWeight: {
|
|
402
|
+
type: "number",
|
|
403
|
+
storage: {
|
|
404
|
+
virtual: true
|
|
429
405
|
}
|
|
430
406
|
}
|
|
431
|
-
}
|
|
407
|
+
})
|
|
432
408
|
},
|
|
433
409
|
create: {
|
|
434
|
-
|
|
435
|
-
schema: {
|
|
436
|
-
type: "object",
|
|
437
|
-
properties: {}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
410
|
+
body: createOperationSchemaDefinition({}, "create")
|
|
440
411
|
},
|
|
441
412
|
patch: {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
type: "
|
|
445
|
-
|
|
446
|
-
|
|
413
|
+
body: createOperationSchemaDefinition({
|
|
414
|
+
remainingBatchWeight: {
|
|
415
|
+
type: "number",
|
|
416
|
+
storage: {
|
|
417
|
+
virtual: true
|
|
447
418
|
}
|
|
448
419
|
}
|
|
449
|
-
}
|
|
420
|
+
}, "patch")
|
|
450
421
|
}
|
|
451
|
-
}
|
|
452
|
-
fieldMeta: [
|
|
453
|
-
{
|
|
454
|
-
key: "remainingBatchWeight",
|
|
455
|
-
repository: {
|
|
456
|
-
storage: "virtual"
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
]
|
|
422
|
+
}
|
|
460
423
|
};
|
|
461
424
|
|
|
462
425
|
assert.throws(
|
|
463
426
|
() => deriveRepositoryMappingFromResource(resource),
|
|
464
|
-
/resource patch schema field "remainingBatchWeight" cannot use
|
|
427
|
+
/resource patch schema field "remainingBatchWeight" cannot use storage\.virtual/
|
|
465
428
|
);
|
|
466
429
|
});
|
|
467
430
|
|
|
@@ -469,29 +432,18 @@ test("deriveRepositoryMappingFromResource excludes runtime-only lookups output k
|
|
|
469
432
|
const resource = {
|
|
470
433
|
operations: {
|
|
471
434
|
view: {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
firstName: { type: "string" },
|
|
478
|
-
lookups: { type: "object" }
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
}
|
|
435
|
+
output: createOperationSchemaDefinition({
|
|
436
|
+
id: { type: "integer", required: true },
|
|
437
|
+
firstName: { type: "string", required: true },
|
|
438
|
+
lookups: { type: "object", opaque: true }
|
|
439
|
+
})
|
|
482
440
|
},
|
|
483
441
|
create: {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
properties: {
|
|
488
|
-
firstName: { type: "string" }
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
}
|
|
442
|
+
body: createOperationSchemaDefinition({
|
|
443
|
+
firstName: { type: "string" }
|
|
444
|
+
}, "create")
|
|
492
445
|
}
|
|
493
|
-
}
|
|
494
|
-
fieldMeta: []
|
|
446
|
+
}
|
|
495
447
|
};
|
|
496
448
|
|
|
497
449
|
const mapping = deriveRepositoryMappingFromResource(resource);
|
|
@@ -507,79 +459,58 @@ test("deriveRepositoryMappingFromResource excludes custom lookup output containe
|
|
|
507
459
|
},
|
|
508
460
|
operations: {
|
|
509
461
|
view: {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
firstName: { type: "string" },
|
|
516
|
-
lookupData: { type: "object" }
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
}
|
|
462
|
+
output: createOperationSchemaDefinition({
|
|
463
|
+
id: { type: "integer", required: true },
|
|
464
|
+
firstName: { type: "string", required: true },
|
|
465
|
+
lookupData: { type: "object", opaque: true }
|
|
466
|
+
})
|
|
520
467
|
},
|
|
521
468
|
create: {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
properties: {
|
|
526
|
-
firstName: { type: "string" }
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
}
|
|
469
|
+
body: createOperationSchemaDefinition({
|
|
470
|
+
firstName: { type: "string" }
|
|
471
|
+
}, "create")
|
|
530
472
|
}
|
|
531
|
-
}
|
|
532
|
-
fieldMeta: []
|
|
473
|
+
}
|
|
533
474
|
};
|
|
534
475
|
|
|
535
476
|
const mapping = deriveRepositoryMappingFromResource(resource);
|
|
536
477
|
assert.deepEqual(mapping.outputKeys, ["id", "firstName"]);
|
|
537
478
|
});
|
|
538
479
|
|
|
539
|
-
test("deriveRepositoryMappingFromResource
|
|
480
|
+
test("deriveRepositoryMappingFromResource rejects non-schema view output definitions", () => {
|
|
540
481
|
const resource = {
|
|
541
482
|
operations: {
|
|
542
483
|
view: {
|
|
543
|
-
|
|
484
|
+
output: {
|
|
544
485
|
schema: {
|
|
545
486
|
type: "object"
|
|
546
487
|
}
|
|
547
488
|
}
|
|
548
489
|
},
|
|
549
490
|
create: {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
properties: {
|
|
554
|
-
firstName: { type: "string" }
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
}
|
|
491
|
+
body: createOperationSchemaDefinition({
|
|
492
|
+
firstName: { type: "string" }
|
|
493
|
+
}, "create")
|
|
558
494
|
}
|
|
559
495
|
}
|
|
560
496
|
};
|
|
561
497
|
|
|
562
498
|
assert.throws(
|
|
563
499
|
() => deriveRepositoryMappingFromResource(resource),
|
|
564
|
-
/operations\.view\.
|
|
500
|
+
/operations\.view\.output\.schema must be a json-rest-schema schema instance/
|
|
565
501
|
);
|
|
566
502
|
});
|
|
567
503
|
|
|
568
|
-
test("deriveRepositoryMappingFromResource
|
|
504
|
+
test("deriveRepositoryMappingFromResource rejects non-schema create body definitions", () => {
|
|
569
505
|
const resource = {
|
|
570
506
|
operations: {
|
|
571
507
|
view: {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
properties: {
|
|
576
|
-
id: { type: "integer" }
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
}
|
|
508
|
+
output: createOperationSchemaDefinition({
|
|
509
|
+
id: { type: "integer", required: true }
|
|
510
|
+
})
|
|
580
511
|
},
|
|
581
512
|
create: {
|
|
582
|
-
|
|
513
|
+
body: {
|
|
583
514
|
schema: {
|
|
584
515
|
type: "object"
|
|
585
516
|
}
|
|
@@ -590,7 +521,7 @@ test("deriveRepositoryMappingFromResource throws when create schema properties a
|
|
|
590
521
|
|
|
591
522
|
assert.throws(
|
|
592
523
|
() => deriveRepositoryMappingFromResource(resource),
|
|
593
|
-
/operations\.create\.
|
|
524
|
+
/operations\.create\.body\.schema must be a json-rest-schema schema instance/
|
|
594
525
|
);
|
|
595
526
|
});
|
|
596
527
|
|
|
@@ -598,52 +529,32 @@ test("deriveRepositoryMappingFromResource tracks writable column-backed write se
|
|
|
598
529
|
const resource = {
|
|
599
530
|
operations: {
|
|
600
531
|
view: {
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
532
|
+
output: createOperationSchemaDefinition({
|
|
533
|
+
id: { type: "integer", required: true },
|
|
534
|
+
scheduledAt: { type: "dateTime", required: true },
|
|
535
|
+
archivedAt: { type: "dateTime", required: true },
|
|
536
|
+
remainingBatchWeight: {
|
|
537
|
+
type: "number",
|
|
538
|
+
storage: {
|
|
539
|
+
virtual: true
|
|
609
540
|
}
|
|
610
541
|
}
|
|
611
|
-
}
|
|
542
|
+
})
|
|
612
543
|
},
|
|
613
544
|
create: {
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
properties: {
|
|
618
|
-
scheduledAt: { type: "string", format: "date-time" }
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
}
|
|
545
|
+
body: createOperationSchemaDefinition({
|
|
546
|
+
scheduledAt: { type: "dateTime" }
|
|
547
|
+
}, "create")
|
|
622
548
|
},
|
|
623
549
|
patch: {
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
type: "
|
|
627
|
-
|
|
628
|
-
archivedAt: {
|
|
629
|
-
anyOf: [
|
|
630
|
-
{ type: "string", format: "date-time" },
|
|
631
|
-
{ type: "null" }
|
|
632
|
-
]
|
|
633
|
-
}
|
|
634
|
-
}
|
|
550
|
+
body: createOperationSchemaDefinition({
|
|
551
|
+
archivedAt: {
|
|
552
|
+
type: "dateTime",
|
|
553
|
+
nullable: true
|
|
635
554
|
}
|
|
636
|
-
}
|
|
555
|
+
}, "patch")
|
|
637
556
|
}
|
|
638
|
-
}
|
|
639
|
-
fieldMeta: [
|
|
640
|
-
{
|
|
641
|
-
key: "remainingBatchWeight",
|
|
642
|
-
repository: {
|
|
643
|
-
storage: "virtual"
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
]
|
|
557
|
+
}
|
|
647
558
|
};
|
|
648
559
|
|
|
649
560
|
const mapping = deriveRepositoryMappingFromResource(resource);
|
|
@@ -653,43 +564,132 @@ test("deriveRepositoryMappingFromResource tracks writable column-backed write se
|
|
|
653
564
|
});
|
|
654
565
|
});
|
|
655
566
|
|
|
656
|
-
test("deriveRepositoryMappingFromResource keeps explicit
|
|
567
|
+
test("deriveRepositoryMappingFromResource keeps explicit storage.writeSerializer metadata", () => {
|
|
657
568
|
const resource = {
|
|
658
569
|
operations: {
|
|
659
570
|
view: {
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
571
|
+
output: createOperationSchemaDefinition({
|
|
572
|
+
id: { type: "integer", required: true },
|
|
573
|
+
arrivalDatetime: {
|
|
574
|
+
type: "dateTime",
|
|
575
|
+
required: true,
|
|
576
|
+
storage: {
|
|
577
|
+
writeSerializer: "datetime-utc"
|
|
666
578
|
}
|
|
667
579
|
}
|
|
668
|
-
}
|
|
580
|
+
})
|
|
669
581
|
},
|
|
670
582
|
create: {
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
type: "
|
|
674
|
-
|
|
675
|
-
|
|
583
|
+
body: createOperationSchemaDefinition({
|
|
584
|
+
arrivalDatetime: {
|
|
585
|
+
type: "dateTime",
|
|
586
|
+
storage: {
|
|
587
|
+
writeSerializer: "datetime-utc"
|
|
676
588
|
}
|
|
677
589
|
}
|
|
678
|
-
}
|
|
590
|
+
}, "create")
|
|
679
591
|
}
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
const mapping = deriveRepositoryMappingFromResource(resource);
|
|
596
|
+
assert.deepEqual(mapping.writeSerializerByKey, {
|
|
597
|
+
arrivalDatetime: "datetime-utc"
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
test("deriveRepositoryMappingFromResource ignores transport export shape and uses authored field definitions", () => {
|
|
602
|
+
const viewSchema = createSchema({
|
|
603
|
+
recordId: {
|
|
604
|
+
type: "id",
|
|
605
|
+
required: true,
|
|
606
|
+
actualField: "record_id"
|
|
607
|
+
},
|
|
608
|
+
title: {
|
|
609
|
+
type: "string",
|
|
610
|
+
required: true
|
|
611
|
+
},
|
|
612
|
+
scheduledAt: {
|
|
613
|
+
type: "dateTime",
|
|
614
|
+
required: true,
|
|
615
|
+
actualField: "scheduled_at"
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
const createInputSchema = createSchema({
|
|
619
|
+
title: {
|
|
620
|
+
type: "string",
|
|
621
|
+
required: true
|
|
680
622
|
},
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
623
|
+
scheduledAt: {
|
|
624
|
+
type: "dateTime",
|
|
625
|
+
required: true,
|
|
626
|
+
actualField: "scheduled_at"
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
const patchInputSchema = createSchema({
|
|
630
|
+
scheduledAt: {
|
|
631
|
+
type: "dateTime",
|
|
632
|
+
required: false,
|
|
633
|
+
actualField: "scheduled_at"
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
viewSchema.toJsonSchema = () => ({
|
|
638
|
+
type: "object",
|
|
639
|
+
properties: {
|
|
640
|
+
title: {
|
|
641
|
+
type: "number"
|
|
642
|
+
},
|
|
643
|
+
scheduledAt: {
|
|
644
|
+
type: "string"
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
createInputSchema.toJsonSchema = () => ({
|
|
649
|
+
type: "object",
|
|
650
|
+
properties: {
|
|
651
|
+
scheduledAt: {
|
|
652
|
+
type: "string"
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
patchInputSchema.toJsonSchema = () => ({
|
|
657
|
+
type: "object",
|
|
658
|
+
properties: {}
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
const resource = {
|
|
662
|
+
operations: {
|
|
663
|
+
view: {
|
|
664
|
+
output: {
|
|
665
|
+
schema: viewSchema,
|
|
666
|
+
mode: "replace"
|
|
667
|
+
}
|
|
668
|
+
},
|
|
669
|
+
create: {
|
|
670
|
+
body: {
|
|
671
|
+
schema: createInputSchema,
|
|
672
|
+
mode: "create"
|
|
673
|
+
}
|
|
674
|
+
},
|
|
675
|
+
patch: {
|
|
676
|
+
body: {
|
|
677
|
+
schema: patchInputSchema,
|
|
678
|
+
mode: "patch"
|
|
686
679
|
}
|
|
687
680
|
}
|
|
688
|
-
|
|
681
|
+
}
|
|
689
682
|
};
|
|
690
683
|
|
|
691
684
|
const mapping = deriveRepositoryMappingFromResource(resource);
|
|
685
|
+
assert.deepEqual(mapping.outputKeys, ["recordId", "title", "scheduledAt"]);
|
|
686
|
+
assert.deepEqual(mapping.outputRecordIdKeys, ["recordId"]);
|
|
687
|
+
assert.deepEqual(mapping.listSearchColumns, ["title"]);
|
|
692
688
|
assert.deepEqual(mapping.writeSerializerByKey, {
|
|
693
|
-
|
|
689
|
+
scheduledAt: "datetime-utc"
|
|
690
|
+
});
|
|
691
|
+
assert.deepEqual(mapping.columnOverrides, {
|
|
692
|
+
recordId: "record_id",
|
|
693
|
+
scheduledAt: "scheduled_at"
|
|
694
694
|
});
|
|
695
695
|
});
|