@jskit-ai/crud-core 0.1.62 → 0.1.63
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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default Object.freeze({
|
|
2
2
|
packageVersion: 1,
|
|
3
3
|
packageId: "@jskit-ai/crud-core",
|
|
4
|
-
version: "0.1.
|
|
4
|
+
version: "0.1.63",
|
|
5
5
|
kind: "runtime",
|
|
6
6
|
description: "Shared CRUD helpers used by CRUD modules.",
|
|
7
7
|
dependsOn: [
|
|
@@ -26,7 +26,7 @@ export default Object.freeze({
|
|
|
26
26
|
mutations: {
|
|
27
27
|
dependencies: {
|
|
28
28
|
runtime: {
|
|
29
|
-
"@jskit-ai/crud-core": "0.1.
|
|
29
|
+
"@jskit-ai/crud-core": "0.1.63"
|
|
30
30
|
},
|
|
31
31
|
dev: {}
|
|
32
32
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/crud-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.63",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -26,12 +26,12 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@tanstack/vue-query": "^5.90.5",
|
|
29
|
-
"@jskit-ai/database-runtime": "0.1.
|
|
30
|
-
"@jskit-ai/kernel": "0.1.
|
|
31
|
-
"@jskit-ai/realtime": "0.1.
|
|
32
|
-
"@jskit-ai/shell-web": "0.1.
|
|
33
|
-
"@jskit-ai/users-core": "0.1.
|
|
34
|
-
"@jskit-ai/users-web": "0.1.
|
|
29
|
+
"@jskit-ai/database-runtime": "0.1.55",
|
|
30
|
+
"@jskit-ai/kernel": "0.1.55",
|
|
31
|
+
"@jskit-ai/realtime": "0.1.54",
|
|
32
|
+
"@jskit-ai/shell-web": "0.1.54",
|
|
33
|
+
"@jskit-ai/users-core": "0.1.65",
|
|
34
|
+
"@jskit-ai/users-web": "0.1.70",
|
|
35
35
|
"typebox": "^1.0.81"
|
|
36
36
|
}
|
|
37
37
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
normalizeDbRecordId,
|
|
3
|
+
toDatabaseDateTimeUtc
|
|
4
|
+
} from "@jskit-ai/database-runtime/shared";
|
|
2
5
|
import { AppError } from "@jskit-ai/kernel/server/runtime/errors";
|
|
3
6
|
import { RECORD_ID_PATTERN } from "@jskit-ai/kernel/shared/validators";
|
|
4
7
|
import { normalizeRecordId, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
@@ -10,6 +13,7 @@ import {
|
|
|
10
13
|
} from "@jskit-ai/kernel/shared/support/crudLookup";
|
|
11
14
|
import {
|
|
12
15
|
CRUD_FIELD_REPOSITORY_STORAGE_COLUMN,
|
|
16
|
+
CRUD_FIELD_REPOSITORY_WRITE_SERIALIZER_DATETIME_UTC,
|
|
13
17
|
CRUD_FIELD_REPOSITORY_STORAGE_VIRTUAL,
|
|
14
18
|
isCrudRuntimeOutputOnlyFieldKey,
|
|
15
19
|
normalizeCrudFieldRepositoryConfig
|
|
@@ -17,6 +21,9 @@ import {
|
|
|
17
21
|
|
|
18
22
|
const DEFAULT_LIST_LIMIT = 20;
|
|
19
23
|
const MAX_LIST_LIMIT = 100;
|
|
24
|
+
const CRUD_WRITE_SERIALIZERS = Object.freeze({
|
|
25
|
+
[CRUD_FIELD_REPOSITORY_WRITE_SERIALIZER_DATETIME_UTC]: (value) => toDatabaseDateTimeUtc(value)
|
|
26
|
+
});
|
|
20
27
|
|
|
21
28
|
function normalizeCrudListCursor(cursor = null, { allowEmpty = true } = {}) {
|
|
22
29
|
if (cursor === undefined || cursor === null) {
|
|
@@ -170,6 +177,23 @@ function schemaIncludesStringType(schema = {}) {
|
|
|
170
177
|
return variants.some((entry) => schemaIncludesStringType(entry));
|
|
171
178
|
}
|
|
172
179
|
|
|
180
|
+
function schemaIncludesDateTimeFormat(schema = {}) {
|
|
181
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (normalizeText(schema.format).toLowerCase() === "date-time") {
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const variants = Array.isArray(schema.anyOf)
|
|
190
|
+
? schema.anyOf
|
|
191
|
+
: Array.isArray(schema.oneOf)
|
|
192
|
+
? schema.oneOf
|
|
193
|
+
: [];
|
|
194
|
+
return variants.some((entry) => schemaIncludesDateTimeFormat(entry));
|
|
195
|
+
}
|
|
196
|
+
|
|
173
197
|
function schemaIncludesRecordIdType(schema = {}) {
|
|
174
198
|
if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
|
|
175
199
|
return false;
|
|
@@ -233,6 +257,7 @@ function deriveRepositoryMappingFromResource(resource = {}, { context = "crudRep
|
|
|
233
257
|
|
|
234
258
|
const fieldStorageByKey = {};
|
|
235
259
|
const columnOverrides = {};
|
|
260
|
+
const writeSerializerByKey = {};
|
|
236
261
|
for (const entry of normalizeResourceFieldMetaEntries(resource.fieldMeta)) {
|
|
237
262
|
const key = normalizeText(entry.key);
|
|
238
263
|
if (!key) {
|
|
@@ -246,6 +271,9 @@ function deriveRepositoryMappingFromResource(resource = {}, { context = "crudRep
|
|
|
246
271
|
if (repositoryConfig.column) {
|
|
247
272
|
columnOverrides[key] = repositoryConfig.column;
|
|
248
273
|
}
|
|
274
|
+
if (repositoryConfig.writeSerializer) {
|
|
275
|
+
writeSerializerByKey[key] = repositoryConfig.writeSerializer;
|
|
276
|
+
}
|
|
249
277
|
}
|
|
250
278
|
|
|
251
279
|
for (const key of [...outputKeys, ...writeKeys]) {
|
|
@@ -313,9 +341,27 @@ function deriveRepositoryMappingFromResource(resource = {}, { context = "crudRep
|
|
|
313
341
|
}
|
|
314
342
|
}
|
|
315
343
|
|
|
344
|
+
for (const key of writeKeys) {
|
|
345
|
+
if ((fieldStorageByKey[key] || CRUD_FIELD_REPOSITORY_STORAGE_COLUMN) !== CRUD_FIELD_REPOSITORY_STORAGE_COLUMN) {
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (writeSerializerByKey[key]) {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const schema = writeProperties[key] || patchProperties[key];
|
|
354
|
+
if (!schemaIncludesDateTimeFormat(schema)) {
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
writeSerializerByKey[key] = CRUD_FIELD_REPOSITORY_WRITE_SERIALIZER_DATETIME_UTC;
|
|
359
|
+
}
|
|
360
|
+
|
|
316
361
|
return Object.freeze({
|
|
317
362
|
outputKeys,
|
|
318
363
|
writeKeys,
|
|
364
|
+
writeSerializerByKey: Object.freeze(writeSerializerByKey),
|
|
319
365
|
fieldStorageByKey: Object.freeze(fieldStorageByKey),
|
|
320
366
|
columnOverrides: Object.freeze(columnOverrides),
|
|
321
367
|
columnBackedOutputKeys: Object.freeze(columnBackedOutputKeys),
|
|
@@ -431,8 +477,11 @@ function applyCrudListQueryFilters(
|
|
|
431
477
|
return nextQuery;
|
|
432
478
|
}
|
|
433
479
|
|
|
434
|
-
function buildWritePayload(sourcePayload = {}, fieldKeys = [], overrides = {}) {
|
|
480
|
+
function buildWritePayload(sourcePayload = {}, fieldKeys = [], overrides = {}, { serializerByKey = {} } = {}) {
|
|
435
481
|
const source = normalizeObjectInput(sourcePayload);
|
|
482
|
+
const normalizedSerializerByKey = serializerByKey && typeof serializerByKey === "object" && !Array.isArray(serializerByKey)
|
|
483
|
+
? serializerByKey
|
|
484
|
+
: {};
|
|
436
485
|
const payload = {};
|
|
437
486
|
for (const key of fieldKeys) {
|
|
438
487
|
const normalizedKey = String(key || "").trim();
|
|
@@ -443,7 +492,19 @@ function buildWritePayload(sourcePayload = {}, fieldKeys = [], overrides = {}) {
|
|
|
443
492
|
if (!Object.hasOwn(source, normalizedKey)) {
|
|
444
493
|
continue;
|
|
445
494
|
}
|
|
446
|
-
|
|
495
|
+
const value = source[normalizedKey];
|
|
496
|
+
const serializerId = normalizeText(normalizedSerializerByKey[normalizedKey]).toLowerCase();
|
|
497
|
+
if (value === null || value === undefined || !serializerId) {
|
|
498
|
+
payload[columnName] = value;
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const serializer = CRUD_WRITE_SERIALIZERS[serializerId];
|
|
503
|
+
if (typeof serializer !== "function") {
|
|
504
|
+
throw new Error(`crudRepository write serializer "${serializerId}" is not supported.`);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
payload[columnName] = serializer(value);
|
|
447
508
|
}
|
|
448
509
|
return payload;
|
|
449
510
|
}
|
|
@@ -1186,7 +1186,14 @@ async function createRecord(runtime, knex, payload = {}, callOptions = {}) {
|
|
|
1186
1186
|
}
|
|
1187
1187
|
);
|
|
1188
1188
|
|
|
1189
|
-
let insertPayload = buildWritePayload(
|
|
1189
|
+
let insertPayload = buildWritePayload(
|
|
1190
|
+
sourcePayload,
|
|
1191
|
+
runtime.mapping.writeKeys,
|
|
1192
|
+
runtime.mapping.columnOverrides,
|
|
1193
|
+
{
|
|
1194
|
+
serializerByKey: runtime.mapping.writeSerializerByKey
|
|
1195
|
+
}
|
|
1196
|
+
);
|
|
1190
1197
|
const timestamp = toInsertDateTime();
|
|
1191
1198
|
if (runtime.defaults.createdAtColumn && !Object.hasOwn(insertPayload, runtime.defaults.createdAtColumn)) {
|
|
1192
1199
|
insertPayload[runtime.defaults.createdAtColumn] = timestamp;
|
|
@@ -1276,7 +1283,14 @@ async function updateRecordById(runtime, knex, recordId, patch = {}, callOptions
|
|
|
1276
1283
|
stageKey: "operations.updateById.preparePatch"
|
|
1277
1284
|
}
|
|
1278
1285
|
);
|
|
1279
|
-
const dbPatch = buildWritePayload(
|
|
1286
|
+
const dbPatch = buildWritePayload(
|
|
1287
|
+
sourcePatch,
|
|
1288
|
+
runtime.mapping.writeKeys,
|
|
1289
|
+
runtime.mapping.columnOverrides,
|
|
1290
|
+
{
|
|
1291
|
+
serializerByKey: runtime.mapping.writeSerializerByKey
|
|
1292
|
+
}
|
|
1293
|
+
);
|
|
1280
1294
|
|
|
1281
1295
|
if (runtime.defaults.updatedAtColumn) {
|
|
1282
1296
|
dbPatch[runtime.defaults.updatedAtColumn] = toInsertDateTime();
|
|
@@ -9,6 +9,30 @@ const CRUD_LOOKUP_FORM_CONTROL_AUTOCOMPLETE = "autocomplete";
|
|
|
9
9
|
const CRUD_LOOKUP_FORM_CONTROL_SELECT = "select";
|
|
10
10
|
const CRUD_FIELD_REPOSITORY_STORAGE_COLUMN = "column";
|
|
11
11
|
const CRUD_FIELD_REPOSITORY_STORAGE_VIRTUAL = "virtual";
|
|
12
|
+
const CRUD_FIELD_REPOSITORY_WRITE_SERIALIZER_DATETIME_UTC = "datetime-utc";
|
|
13
|
+
|
|
14
|
+
function normalizeCrudFieldRepositoryWriteSerializer(
|
|
15
|
+
value,
|
|
16
|
+
{
|
|
17
|
+
context = "crud fieldMeta repository",
|
|
18
|
+
fieldKey = ""
|
|
19
|
+
} = {}
|
|
20
|
+
) {
|
|
21
|
+
const normalizedFieldKey = normalizeText(fieldKey);
|
|
22
|
+
const normalizedValue = normalizeText(value).toLowerCase();
|
|
23
|
+
if (!normalizedValue) {
|
|
24
|
+
return "";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (normalizedValue === CRUD_FIELD_REPOSITORY_WRITE_SERIALIZER_DATETIME_UTC) {
|
|
28
|
+
return normalizedValue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
throw new Error(
|
|
32
|
+
`${context}${normalizedFieldKey ? `["${normalizedFieldKey}"]` : ""} repository.writeSerializer must be ` +
|
|
33
|
+
`"${CRUD_FIELD_REPOSITORY_WRITE_SERIALIZER_DATETIME_UTC}" when provided.`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
12
36
|
|
|
13
37
|
function checkCrudLookupFormControl(
|
|
14
38
|
value,
|
|
@@ -70,7 +94,7 @@ function normalizeCrudFieldRepositoryConfig(
|
|
|
70
94
|
|
|
71
95
|
const repositoryKeys = Object.keys(repository);
|
|
72
96
|
for (const repositoryKey of repositoryKeys) {
|
|
73
|
-
if (repositoryKey !== "column" && repositoryKey !== "storage") {
|
|
97
|
+
if (repositoryKey !== "column" && repositoryKey !== "storage" && repositoryKey !== "writeSerializer") {
|
|
74
98
|
throw new Error(
|
|
75
99
|
`${context}${normalizedFieldKey ? `["${normalizedFieldKey}"]` : ""} does not support repository.${repositoryKey}.`
|
|
76
100
|
);
|
|
@@ -79,10 +103,14 @@ function normalizeCrudFieldRepositoryConfig(
|
|
|
79
103
|
|
|
80
104
|
const column = normalizeText(repository.column);
|
|
81
105
|
const storage = normalizeText(repository.storage).toLowerCase();
|
|
106
|
+
const writeSerializer = normalizeCrudFieldRepositoryWriteSerializer(repository.writeSerializer, {
|
|
107
|
+
context,
|
|
108
|
+
fieldKey: normalizedFieldKey
|
|
109
|
+
});
|
|
82
110
|
|
|
83
|
-
if (!column && !storage) {
|
|
111
|
+
if (!column && !storage && !writeSerializer) {
|
|
84
112
|
throw new Error(
|
|
85
|
-
`${context}${normalizedFieldKey ? `["${normalizedFieldKey}"]` : ""} requires repository.column or repository.
|
|
113
|
+
`${context}${normalizedFieldKey ? `["${normalizedFieldKey}"]` : ""} requires repository.column, repository.storage, or repository.writeSerializer.`
|
|
86
114
|
);
|
|
87
115
|
}
|
|
88
116
|
|
|
@@ -96,22 +124,30 @@ function normalizeCrudFieldRepositoryConfig(
|
|
|
96
124
|
`${context}${normalizedFieldKey ? `["${normalizedFieldKey}"]` : ""} repository.storage "virtual" cannot define repository.column.`
|
|
97
125
|
);
|
|
98
126
|
}
|
|
127
|
+
if (storage === CRUD_FIELD_REPOSITORY_STORAGE_VIRTUAL && writeSerializer) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
`${context}${normalizedFieldKey ? `["${normalizedFieldKey}"]` : ""} repository.storage "virtual" cannot define repository.writeSerializer.`
|
|
130
|
+
);
|
|
131
|
+
}
|
|
99
132
|
|
|
100
133
|
return Object.freeze({
|
|
101
134
|
storage: storage === CRUD_FIELD_REPOSITORY_STORAGE_VIRTUAL
|
|
102
135
|
? CRUD_FIELD_REPOSITORY_STORAGE_VIRTUAL
|
|
103
136
|
: CRUD_FIELD_REPOSITORY_STORAGE_COLUMN,
|
|
104
|
-
column
|
|
137
|
+
column,
|
|
138
|
+
writeSerializer
|
|
105
139
|
});
|
|
106
140
|
}
|
|
107
141
|
|
|
108
142
|
export {
|
|
109
143
|
CRUD_FIELD_REPOSITORY_STORAGE_COLUMN,
|
|
110
144
|
CRUD_FIELD_REPOSITORY_STORAGE_VIRTUAL,
|
|
145
|
+
CRUD_FIELD_REPOSITORY_WRITE_SERIALIZER_DATETIME_UTC,
|
|
111
146
|
CRUD_LOOKUP_FORM_CONTROL_AUTOCOMPLETE,
|
|
112
147
|
CRUD_LOOKUP_FORM_CONTROL_SELECT,
|
|
113
148
|
CRUD_RUNTIME_LOOKUPS_FIELD_KEY,
|
|
114
149
|
checkCrudLookupFormControl,
|
|
115
150
|
isCrudRuntimeOutputOnlyFieldKey,
|
|
116
|
-
normalizeCrudFieldRepositoryConfig
|
|
151
|
+
normalizeCrudFieldRepositoryConfig,
|
|
152
|
+
normalizeCrudFieldRepositoryWriteSerializer
|
|
117
153
|
};
|
|
@@ -132,6 +132,34 @@ test("buildWritePayload respects defined keys", () => {
|
|
|
132
132
|
});
|
|
133
133
|
});
|
|
134
134
|
|
|
135
|
+
test("buildWritePayload serializes configured date-time keys to database shape", () => {
|
|
136
|
+
const payload = buildWritePayload(
|
|
137
|
+
{
|
|
138
|
+
scheduledAt: "2026-04-23T10:11:12.000Z",
|
|
139
|
+
archivedAt: null,
|
|
140
|
+
title: "Example"
|
|
141
|
+
},
|
|
142
|
+
["scheduledAt", "archivedAt", "title"],
|
|
143
|
+
{
|
|
144
|
+
scheduledAt: "scheduled_at",
|
|
145
|
+
archivedAt: "archived_at",
|
|
146
|
+
title: "title"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
serializerByKey: {
|
|
150
|
+
scheduledAt: "datetime-utc",
|
|
151
|
+
archivedAt: "datetime-utc"
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
assert.deepEqual(payload, {
|
|
157
|
+
scheduled_at: "2026-04-23 10:11:12.000",
|
|
158
|
+
archived_at: null,
|
|
159
|
+
title: "Example"
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
135
163
|
test("applyCrudListQueryFilters applies search and cursor filters", () => {
|
|
136
164
|
const { query, calls } = createQueryDouble();
|
|
137
165
|
const result = applyCrudListQueryFilters(query, {
|
|
@@ -565,3 +593,103 @@ test("deriveRepositoryMappingFromResource throws when create schema properties a
|
|
|
565
593
|
/operations\.create\.bodyValidator\.schema\.properties/
|
|
566
594
|
);
|
|
567
595
|
});
|
|
596
|
+
|
|
597
|
+
test("deriveRepositoryMappingFromResource tracks writable column-backed write serializers", () => {
|
|
598
|
+
const resource = {
|
|
599
|
+
operations: {
|
|
600
|
+
view: {
|
|
601
|
+
outputValidator: {
|
|
602
|
+
schema: {
|
|
603
|
+
type: "object",
|
|
604
|
+
properties: {
|
|
605
|
+
id: { type: "integer" },
|
|
606
|
+
scheduledAt: { type: "string", format: "date-time" },
|
|
607
|
+
archivedAt: { type: "string", format: "date-time" },
|
|
608
|
+
remainingBatchWeight: { type: "number" }
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
create: {
|
|
614
|
+
bodyValidator: {
|
|
615
|
+
schema: {
|
|
616
|
+
type: "object",
|
|
617
|
+
properties: {
|
|
618
|
+
scheduledAt: { type: "string", format: "date-time" }
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
},
|
|
623
|
+
patch: {
|
|
624
|
+
bodyValidator: {
|
|
625
|
+
schema: {
|
|
626
|
+
type: "object",
|
|
627
|
+
properties: {
|
|
628
|
+
archivedAt: {
|
|
629
|
+
anyOf: [
|
|
630
|
+
{ type: "string", format: "date-time" },
|
|
631
|
+
{ type: "null" }
|
|
632
|
+
]
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
},
|
|
639
|
+
fieldMeta: [
|
|
640
|
+
{
|
|
641
|
+
key: "remainingBatchWeight",
|
|
642
|
+
repository: {
|
|
643
|
+
storage: "virtual"
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
]
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
const mapping = deriveRepositoryMappingFromResource(resource);
|
|
650
|
+
assert.deepEqual(mapping.writeSerializerByKey, {
|
|
651
|
+
scheduledAt: "datetime-utc",
|
|
652
|
+
archivedAt: "datetime-utc"
|
|
653
|
+
});
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
test("deriveRepositoryMappingFromResource keeps explicit repository.writeSerializer metadata", () => {
|
|
657
|
+
const resource = {
|
|
658
|
+
operations: {
|
|
659
|
+
view: {
|
|
660
|
+
outputValidator: {
|
|
661
|
+
schema: {
|
|
662
|
+
type: "object",
|
|
663
|
+
properties: {
|
|
664
|
+
id: { type: "integer" },
|
|
665
|
+
arrivalDatetime: { type: "string", format: "date-time" }
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
},
|
|
670
|
+
create: {
|
|
671
|
+
bodyValidator: {
|
|
672
|
+
schema: {
|
|
673
|
+
type: "object",
|
|
674
|
+
properties: {
|
|
675
|
+
arrivalDatetime: { type: "string", format: "date-time" }
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
},
|
|
681
|
+
fieldMeta: [
|
|
682
|
+
{
|
|
683
|
+
key: "arrivalDatetime",
|
|
684
|
+
repository: {
|
|
685
|
+
writeSerializer: "datetime-utc"
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
]
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
const mapping = deriveRepositoryMappingFromResource(resource);
|
|
692
|
+
assert.deepEqual(mapping.writeSerializerByKey, {
|
|
693
|
+
arrivalDatetime: "datetime-utc"
|
|
694
|
+
});
|
|
695
|
+
});
|