@jskit-ai/crud-core 0.1.53 → 0.1.55
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 +2 -2
- package/package.json +9 -10
- package/src/server/lookupHydration.js +20 -20
- package/src/server/lookupPathSupport.js +5 -5
- package/src/server/{lookupProviders.js → lookups.js} +14 -14
- package/src/server/repositorySupport.js +76 -6
- package/src/server/resourceRuntime/index.js +1413 -0
- package/src/server/resourceRuntime/lookupHydration.js +685 -0
- package/src/server/serviceEvents.js +1 -1
- package/src/server/serviceMethods.js +32 -31
- package/src/shared/crudFieldMetaSupport.js +64 -1
- package/test/createCrudServiceFromResource.test.js +58 -74
- package/test/lookups.test.js +135 -0
- package/test/repositorySupport.test.js +160 -6
- package/test/resourceRuntime.test.js +916 -0
- package/test/serviceEvents.test.js +3 -3
- package/test/serviceMethods.test.js +14 -38
- package/src/server/createCrudRepositoryFromResource.js +0 -66
- package/src/server/repositoryMethods.js +0 -1485
- package/test/createCrudRepositoryFromResource.test.js +0 -2086
- package/test/lookupProviders.test.js +0 -135
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.55",
|
|
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.55"
|
|
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.55",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -13,10 +13,9 @@
|
|
|
13
13
|
"./shared/crudFieldMetaSupport": "./src/shared/crudFieldMetaSupport.js",
|
|
14
14
|
"./shared/crudNamespaceSupport": "./src/shared/crudNamespaceSupport.js",
|
|
15
15
|
"./server/repositorySupport": "./src/server/repositorySupport.js",
|
|
16
|
-
"./server/
|
|
16
|
+
"./server/resourceRuntime": "./src/server/resourceRuntime/index.js",
|
|
17
17
|
"./server/createHooksToCollectChildren": "./src/server/createHooksToCollectChildren.js",
|
|
18
|
-
"./server/
|
|
19
|
-
"./server/lookupProviders": "./src/server/lookupProviders.js",
|
|
18
|
+
"./server/lookups": "./src/server/lookups.js",
|
|
20
19
|
"./server/serviceEvents": "./src/server/serviceEvents.js",
|
|
21
20
|
"./server/serviceMethods": "./src/server/serviceMethods.js",
|
|
22
21
|
"./server/fieldAccess": "./src/server/fieldAccess.js",
|
|
@@ -27,12 +26,12 @@
|
|
|
27
26
|
},
|
|
28
27
|
"dependencies": {
|
|
29
28
|
"@tanstack/vue-query": "^5.90.5",
|
|
30
|
-
"@jskit-ai/database-runtime": "0.1.
|
|
31
|
-
"@jskit-ai/kernel": "0.1.
|
|
32
|
-
"@jskit-ai/realtime": "0.1.
|
|
33
|
-
"@jskit-ai/shell-web": "0.1.
|
|
34
|
-
"@jskit-ai/users-core": "0.1.
|
|
35
|
-
"@jskit-ai/users-web": "0.1.
|
|
29
|
+
"@jskit-ai/database-runtime": "0.1.47",
|
|
30
|
+
"@jskit-ai/kernel": "0.1.47",
|
|
31
|
+
"@jskit-ai/realtime": "0.1.46",
|
|
32
|
+
"@jskit-ai/shell-web": "0.1.46",
|
|
33
|
+
"@jskit-ai/users-core": "0.1.57",
|
|
34
|
+
"@jskit-ai/users-web": "0.1.62",
|
|
36
35
|
"typebox": "^1.0.81"
|
|
37
36
|
}
|
|
38
37
|
}
|
|
@@ -11,13 +11,13 @@ import { normalizeCrudLookupApiPath } from "./lookupPathSupport.js";
|
|
|
11
11
|
const DEFAULT_LOOKUP_INCLUDE = "*";
|
|
12
12
|
const DEFAULT_LOOKUP_MAX_DEPTH = 3;
|
|
13
13
|
const MAX_LOOKUP_MAX_DEPTH = 10;
|
|
14
|
-
const
|
|
14
|
+
const LOOKUP_OWNERSHIP_FILTER_VALUES = Object.freeze([
|
|
15
15
|
"public",
|
|
16
16
|
"user",
|
|
17
17
|
"workspace",
|
|
18
18
|
"workspace_user"
|
|
19
19
|
]);
|
|
20
|
-
const
|
|
20
|
+
const LOOKUP_OWNERSHIP_FILTER_SET = new Set(LOOKUP_OWNERSHIP_FILTER_VALUES);
|
|
21
21
|
|
|
22
22
|
function normalizeLookupRelationEntry(entry = {}, outputKeys = new Set()) {
|
|
23
23
|
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
@@ -162,7 +162,7 @@ function createCrudLookupRuntime(resource = {}, { outputKeys = [] } = {}) {
|
|
|
162
162
|
context: "crud lookup runtime container key"
|
|
163
163
|
});
|
|
164
164
|
const namespace =
|
|
165
|
-
normalizeCrudLookupNamespace(resource?.
|
|
165
|
+
normalizeCrudLookupNamespace(resource?.namespace) ||
|
|
166
166
|
normalizeCrudLookupNamespace(resource?.apiPath);
|
|
167
167
|
const defaults = resolveLookupRuntimeDefaults(resource);
|
|
168
168
|
|
|
@@ -374,10 +374,10 @@ function buildLookupHydrationPlan(
|
|
|
374
374
|
};
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
-
function
|
|
378
|
-
const resolver = callOptions?.
|
|
377
|
+
function resolveLookupResolver(repositoryOptions = {}, callOptions = {}, { context = "crudRepository" } = {}) {
|
|
378
|
+
const resolver = callOptions?.resolveLookup || repositoryOptions?.resolveLookup;
|
|
379
379
|
if (typeof resolver !== "function") {
|
|
380
|
-
throw new TypeError(`${context} requires
|
|
380
|
+
throw new TypeError(`${context} requires resolveLookup(relation) to hydrate lookups.`);
|
|
381
381
|
}
|
|
382
382
|
return resolver;
|
|
383
383
|
}
|
|
@@ -477,28 +477,28 @@ function resolveGroupChildInclude(group = {}) {
|
|
|
477
477
|
return [...includeSet].join(",");
|
|
478
478
|
}
|
|
479
479
|
|
|
480
|
-
function
|
|
480
|
+
function normalizeLookup(provider, relation = {}, { context = "crudRepository" } = {}) {
|
|
481
481
|
if (!provider || typeof provider !== "object" || Array.isArray(provider)) {
|
|
482
|
-
throw new Error(`${context} could not resolve lookup
|
|
482
|
+
throw new Error(`${context} could not resolve lookup "${relation.namespace}".`);
|
|
483
483
|
}
|
|
484
484
|
if (typeof provider.listByIds !== "function") {
|
|
485
|
-
throw new Error(`${context} lookup
|
|
485
|
+
throw new Error(`${context} lookup "${relation.namespace}" must expose listByIds(ids, options).`);
|
|
486
486
|
}
|
|
487
487
|
return provider;
|
|
488
488
|
}
|
|
489
489
|
|
|
490
|
-
function
|
|
490
|
+
function resolveLookupOwnershipFilter(provider = {}, { context = "crudRepository" } = {}) {
|
|
491
491
|
const normalizedOwnershipFilter = normalizeText(provider?.ownershipFilter).toLowerCase();
|
|
492
492
|
if (!normalizedOwnershipFilter) {
|
|
493
493
|
return "";
|
|
494
494
|
}
|
|
495
495
|
|
|
496
|
-
if (
|
|
496
|
+
if (LOOKUP_OWNERSHIP_FILTER_SET.has(normalizedOwnershipFilter)) {
|
|
497
497
|
return normalizedOwnershipFilter;
|
|
498
498
|
}
|
|
499
499
|
|
|
500
500
|
throw new TypeError(
|
|
501
|
-
`${context} lookup
|
|
501
|
+
`${context} lookup ownershipFilter must be one of: ${LOOKUP_OWNERSHIP_FILTER_VALUES.join(", ")}.`
|
|
502
502
|
);
|
|
503
503
|
}
|
|
504
504
|
|
|
@@ -509,26 +509,26 @@ function resolveLookupVisibilityContext(
|
|
|
509
509
|
{ context = "crudRepository" } = {}
|
|
510
510
|
) {
|
|
511
511
|
const parentVisibilityContext = normalizeVisibilityContext(callOptions?.visibilityContext);
|
|
512
|
-
const
|
|
512
|
+
const lookupOwnershipFilter = resolveLookupOwnershipFilter(provider, {
|
|
513
513
|
context
|
|
514
514
|
});
|
|
515
515
|
|
|
516
|
-
if (!
|
|
516
|
+
if (!lookupOwnershipFilter) {
|
|
517
517
|
if (parentVisibilityContext.visibility !== "public") {
|
|
518
518
|
throw new Error(
|
|
519
|
-
`${context} lookup
|
|
519
|
+
`${context} lookup "${relation.namespace}" must declare ownershipFilter when parent visibility is "${parentVisibilityContext.visibility}".`
|
|
520
520
|
);
|
|
521
521
|
}
|
|
522
522
|
return callOptions?.visibilityContext;
|
|
523
523
|
}
|
|
524
524
|
|
|
525
525
|
const nextVisibilityContext = {
|
|
526
|
-
visibility:
|
|
526
|
+
visibility: lookupOwnershipFilter
|
|
527
527
|
};
|
|
528
|
-
if (
|
|
528
|
+
if (lookupOwnershipFilter === "workspace" || lookupOwnershipFilter === "workspace_user") {
|
|
529
529
|
nextVisibilityContext.scopeOwnerId = parentVisibilityContext.scopeOwnerId;
|
|
530
530
|
}
|
|
531
|
-
if (
|
|
531
|
+
if (lookupOwnershipFilter === "user" || lookupOwnershipFilter === "workspace_user") {
|
|
532
532
|
nextVisibilityContext.userId = parentVisibilityContext.userId;
|
|
533
533
|
}
|
|
534
534
|
|
|
@@ -624,7 +624,7 @@ async function hydrateCrudLookupRecords(
|
|
|
624
624
|
return sourceRecords;
|
|
625
625
|
}
|
|
626
626
|
|
|
627
|
-
const
|
|
627
|
+
const resolveLookup = resolveLookupResolver(repositoryOptions, callOptions, {
|
|
628
628
|
context: runtime?.context || "crudRepository"
|
|
629
629
|
});
|
|
630
630
|
|
|
@@ -636,7 +636,7 @@ async function hydrateCrudLookupRecords(
|
|
|
636
636
|
continue;
|
|
637
637
|
}
|
|
638
638
|
|
|
639
|
-
const provider =
|
|
639
|
+
const provider = normalizeLookup(resolveLookup(group.relation), group.relation, {
|
|
640
640
|
context: runtime?.context || "crudRepository"
|
|
641
641
|
});
|
|
642
642
|
const childVisibilityContext = resolveLookupVisibilityContext(
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "@jskit-ai/kernel/shared/support/crudLookup";
|
|
5
5
|
import { toSnakeCase } from "@jskit-ai/kernel/shared/support/stringCase";
|
|
6
6
|
|
|
7
|
-
function requireCrudLookupNamespace(value = "", { context = "
|
|
7
|
+
function requireCrudLookupNamespace(value = "", { context = "crudLookup" } = {}) {
|
|
8
8
|
const normalizedNamespace = normalizeCrudLookupNamespace(value);
|
|
9
9
|
if (!normalizedNamespace) {
|
|
10
10
|
throw new Error(`${context} requires relation.namespace.`);
|
|
@@ -13,7 +13,7 @@ function requireCrudLookupNamespace(value = "", { context = "crudLookupProvider"
|
|
|
13
13
|
return normalizedNamespace;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
function
|
|
16
|
+
function resolveCrudLookupToken(namespace = "", { context = "crudLookup" } = {}) {
|
|
17
17
|
const normalizedNamespace = requireCrudLookupNamespace(namespace, {
|
|
18
18
|
context
|
|
19
19
|
});
|
|
@@ -22,10 +22,10 @@ function resolveCrudLookupProviderToken(namespace = "", { context = "crudLookupP
|
|
|
22
22
|
.map((segment) => toSnakeCase(segment))
|
|
23
23
|
.filter(Boolean)
|
|
24
24
|
.join(".");
|
|
25
|
-
return `
|
|
25
|
+
return `lookup.${tokenPart}`;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
function resolveCrudLookupNamespaceFromRelation(relation = {}, { context = "
|
|
28
|
+
function resolveCrudLookupNamespaceFromRelation(relation = {}, { context = "crudLookup" } = {}) {
|
|
29
29
|
const normalizedNamespace =
|
|
30
30
|
normalizeCrudLookupNamespace(relation?.namespace) ||
|
|
31
31
|
normalizeCrudLookupNamespace(relation?.apiPath);
|
|
@@ -41,5 +41,5 @@ export {
|
|
|
41
41
|
normalizeCrudLookupNamespace,
|
|
42
42
|
requireCrudLookupNamespace,
|
|
43
43
|
resolveCrudLookupNamespaceFromRelation,
|
|
44
|
-
|
|
44
|
+
resolveCrudLookupToken
|
|
45
45
|
};
|
|
@@ -1,55 +1,55 @@
|
|
|
1
1
|
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
resolveCrudLookupToken,
|
|
4
4
|
resolveCrudLookupNamespaceFromRelation
|
|
5
5
|
} from "./lookupPathSupport.js";
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const LOOKUP_OWNERSHIP_FILTER_VALUES = Object.freeze([
|
|
8
8
|
"public",
|
|
9
9
|
"user",
|
|
10
10
|
"workspace",
|
|
11
11
|
"workspace_user"
|
|
12
12
|
]);
|
|
13
|
-
const
|
|
13
|
+
const LOOKUP_OWNERSHIP_FILTER_SET = new Set(LOOKUP_OWNERSHIP_FILTER_VALUES);
|
|
14
14
|
|
|
15
|
-
function
|
|
15
|
+
function normalizeLookupOwnershipFilter(value, { context = "crudLookup ownershipFilter" } = {}) {
|
|
16
16
|
const normalized = normalizeText(value).toLowerCase();
|
|
17
17
|
if (!normalized) {
|
|
18
18
|
return "";
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
if (
|
|
21
|
+
if (LOOKUP_OWNERSHIP_FILTER_SET.has(normalized)) {
|
|
22
22
|
return normalized;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
throw new TypeError(
|
|
26
|
-
`${context} must be one of: ${
|
|
26
|
+
`${context} must be one of: ${LOOKUP_OWNERSHIP_FILTER_VALUES.join(", ")}.`
|
|
27
27
|
);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
function
|
|
30
|
+
function createCrudLookupResolver(scope, { context = "crudLookup" } = {}) {
|
|
31
31
|
if (!scope || typeof scope.make !== "function") {
|
|
32
32
|
throw new Error(`${context} requires scope.make().`);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
return function
|
|
35
|
+
return function resolveLookup(relation = {}) {
|
|
36
36
|
const namespace = resolveCrudLookupNamespaceFromRelation(relation, {
|
|
37
37
|
context
|
|
38
38
|
});
|
|
39
39
|
return scope.make(
|
|
40
|
-
|
|
40
|
+
resolveCrudLookupToken(namespace, {
|
|
41
41
|
context
|
|
42
42
|
})
|
|
43
43
|
);
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
function
|
|
47
|
+
function createCrudLookup(repository, { context = "crudLookup", ownershipFilter = "" } = {}) {
|
|
48
48
|
if (!repository || typeof repository.listByIds !== "function") {
|
|
49
49
|
throw new Error(`${context} requires repository.listByIds(ids, options).`);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
const normalizedOwnershipFilter =
|
|
52
|
+
const normalizedOwnershipFilter = normalizeLookupOwnershipFilter(
|
|
53
53
|
ownershipFilter || repository?.ownershipFilter,
|
|
54
54
|
{
|
|
55
55
|
context: `${context} ownershipFilter`
|
|
@@ -69,7 +69,7 @@ function createCrudLookupProvider(repository, { context = "crudLookupProvider",
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
export {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
resolveCrudLookupToken,
|
|
73
|
+
createCrudLookupResolver,
|
|
74
|
+
createCrudLookup
|
|
75
75
|
};
|
|
@@ -8,7 +8,12 @@ import {
|
|
|
8
8
|
resolveCrudLookupContainerKey,
|
|
9
9
|
resolveCrudLookupFieldKeys
|
|
10
10
|
} from "@jskit-ai/kernel/shared/support/crudLookup";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
CRUD_FIELD_REPOSITORY_STORAGE_COLUMN,
|
|
13
|
+
CRUD_FIELD_REPOSITORY_STORAGE_VIRTUAL,
|
|
14
|
+
isCrudRuntimeOutputOnlyFieldKey,
|
|
15
|
+
normalizeCrudFieldRepositoryConfig
|
|
16
|
+
} from "../shared/crudFieldMetaSupport.js";
|
|
12
17
|
|
|
13
18
|
const DEFAULT_LIST_LIMIT = 20;
|
|
14
19
|
const MAX_LIST_LIMIT = 100;
|
|
@@ -70,7 +75,8 @@ function resolveColumnName(fieldKey, overrides = {}) {
|
|
|
70
75
|
function buildRepositoryColumnMetadata({
|
|
71
76
|
outputKeys = [],
|
|
72
77
|
writeKeys = [],
|
|
73
|
-
columnOverrides = {}
|
|
78
|
+
columnOverrides = {},
|
|
79
|
+
fieldStorageByKey = {}
|
|
74
80
|
} = {}) {
|
|
75
81
|
const normalizedOutputKeys = (Array.isArray(outputKeys) ? outputKeys : [])
|
|
76
82
|
.map((key) => String(key || "").trim())
|
|
@@ -80,6 +86,9 @@ function buildRepositoryColumnMetadata({
|
|
|
80
86
|
.filter(Boolean);
|
|
81
87
|
|
|
82
88
|
const deriveMapping = (key) => {
|
|
89
|
+
if (fieldStorageByKey?.[key] !== CRUD_FIELD_REPOSITORY_STORAGE_COLUMN) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
83
92
|
const column = resolveColumnName(key, columnOverrides);
|
|
84
93
|
if (!column) {
|
|
85
94
|
return null;
|
|
@@ -100,6 +109,13 @@ function buildRepositoryColumnMetadata({
|
|
|
100
109
|
});
|
|
101
110
|
}
|
|
102
111
|
|
|
112
|
+
function resolveOptionalObjectSchemaProperties(schema, options = {}) {
|
|
113
|
+
if (!schema) {
|
|
114
|
+
return {};
|
|
115
|
+
}
|
|
116
|
+
return requireObjectSchemaProperties(schema, options);
|
|
117
|
+
}
|
|
118
|
+
|
|
103
119
|
function requireObjectSchemaProperties(schema, { context = "crudRepository", schemaLabel = "schema" } = {}) {
|
|
104
120
|
if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
|
|
105
121
|
throw new TypeError(`${context} requires ${schemaLabel} to be an object schema.`);
|
|
@@ -189,6 +205,7 @@ function deriveRepositoryMappingFromResource(resource = {}, { context = "crudRep
|
|
|
189
205
|
|
|
190
206
|
const outputSchema = operations?.view?.outputValidator?.schema;
|
|
191
207
|
const writeSchema = operations?.create?.bodyValidator?.schema;
|
|
208
|
+
const patchSchema = operations?.patch?.bodyValidator?.schema;
|
|
192
209
|
const outputProperties = requireObjectSchemaProperties(outputSchema, {
|
|
193
210
|
context,
|
|
194
211
|
schemaLabel: "operations.view.outputValidator.schema"
|
|
@@ -197,6 +214,10 @@ function deriveRepositoryMappingFromResource(resource = {}, { context = "crudRep
|
|
|
197
214
|
context,
|
|
198
215
|
schemaLabel: "operations.create.bodyValidator.schema"
|
|
199
216
|
});
|
|
217
|
+
const patchProperties = resolveOptionalObjectSchemaProperties(patchSchema, {
|
|
218
|
+
context,
|
|
219
|
+
schemaLabel: "operations.patch.bodyValidator.schema"
|
|
220
|
+
});
|
|
200
221
|
const lookupContainerKey = resolveCrudLookupContainerKey(resource, {
|
|
201
222
|
context: `${context} resource.contract.lookup.containerKey`
|
|
202
223
|
});
|
|
@@ -205,20 +226,63 @@ function deriveRepositoryMappingFromResource(resource = {}, { context = "crudRep
|
|
|
205
226
|
(key) => !isCrudRuntimeOutputOnlyFieldKey(key, { lookupContainerKey })
|
|
206
227
|
)
|
|
207
228
|
);
|
|
208
|
-
const writeKeys = Object.freeze(
|
|
229
|
+
const writeKeys = Object.freeze([
|
|
230
|
+
...Object.keys(writeProperties),
|
|
231
|
+
...Object.keys(patchProperties).filter((key) => !Object.hasOwn(writeProperties, key))
|
|
232
|
+
]);
|
|
209
233
|
|
|
234
|
+
const fieldStorageByKey = {};
|
|
210
235
|
const columnOverrides = {};
|
|
211
236
|
for (const entry of normalizeResourceFieldMetaEntries(resource.fieldMeta)) {
|
|
212
237
|
const key = normalizeText(entry.key);
|
|
213
|
-
|
|
214
|
-
if (!key || !dbColumn) {
|
|
238
|
+
if (!key) {
|
|
215
239
|
continue;
|
|
216
240
|
}
|
|
217
|
-
|
|
241
|
+
const repositoryConfig = normalizeCrudFieldRepositoryConfig(entry, {
|
|
242
|
+
context: `${context} resource.fieldMeta`,
|
|
243
|
+
fieldKey: key
|
|
244
|
+
});
|
|
245
|
+
fieldStorageByKey[key] = repositoryConfig.storage;
|
|
246
|
+
if (repositoryConfig.column) {
|
|
247
|
+
columnOverrides[key] = repositoryConfig.column;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
for (const key of [...outputKeys, ...writeKeys]) {
|
|
252
|
+
if (!fieldStorageByKey[key]) {
|
|
253
|
+
fieldStorageByKey[key] = CRUD_FIELD_REPOSITORY_STORAGE_COLUMN;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const virtualOutputKeys = [];
|
|
258
|
+
const columnBackedOutputKeys = [];
|
|
259
|
+
for (const key of outputKeys) {
|
|
260
|
+
const storage = fieldStorageByKey[key] || CRUD_FIELD_REPOSITORY_STORAGE_COLUMN;
|
|
261
|
+
if (storage === CRUD_FIELD_REPOSITORY_STORAGE_VIRTUAL) {
|
|
262
|
+
virtualOutputKeys.push(key);
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
columnBackedOutputKeys.push(key);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
for (const key of virtualOutputKeys) {
|
|
269
|
+
if (Object.hasOwn(writeProperties, key)) {
|
|
270
|
+
throw new Error(
|
|
271
|
+
`${context} resource create schema field "${key}" cannot use repository.storage "virtual".`
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
if (Object.hasOwn(patchProperties, key)) {
|
|
275
|
+
throw new Error(
|
|
276
|
+
`${context} resource patch schema field "${key}" cannot use repository.storage "virtual".`
|
|
277
|
+
);
|
|
278
|
+
}
|
|
218
279
|
}
|
|
219
280
|
|
|
220
281
|
const listSearchColumns = [];
|
|
221
282
|
for (const [key, schema] of Object.entries(outputProperties)) {
|
|
283
|
+
if ((fieldStorageByKey[key] || CRUD_FIELD_REPOSITORY_STORAGE_COLUMN) !== CRUD_FIELD_REPOSITORY_STORAGE_COLUMN) {
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
222
286
|
if (!schemaIncludesStringType(schema)) {
|
|
223
287
|
continue;
|
|
224
288
|
}
|
|
@@ -232,6 +296,9 @@ function deriveRepositoryMappingFromResource(resource = {}, { context = "crudRep
|
|
|
232
296
|
|
|
233
297
|
const parentFilterColumns = {};
|
|
234
298
|
for (const key of resolveCrudLookupFieldKeys(resource, { allowKeys: writeKeys })) {
|
|
299
|
+
if ((fieldStorageByKey[key] || CRUD_FIELD_REPOSITORY_STORAGE_COLUMN) !== CRUD_FIELD_REPOSITORY_STORAGE_COLUMN) {
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
235
302
|
const columnName = resolveColumnName(key, columnOverrides);
|
|
236
303
|
if (!columnName) {
|
|
237
304
|
continue;
|
|
@@ -249,7 +316,10 @@ function deriveRepositoryMappingFromResource(resource = {}, { context = "crudRep
|
|
|
249
316
|
return Object.freeze({
|
|
250
317
|
outputKeys,
|
|
251
318
|
writeKeys,
|
|
319
|
+
fieldStorageByKey: Object.freeze(fieldStorageByKey),
|
|
252
320
|
columnOverrides: Object.freeze(columnOverrides),
|
|
321
|
+
columnBackedOutputKeys: Object.freeze(columnBackedOutputKeys),
|
|
322
|
+
virtualOutputKeys: Object.freeze(virtualOutputKeys),
|
|
253
323
|
listSearchColumns: Object.freeze(listSearchColumns),
|
|
254
324
|
parentFilterColumns: Object.freeze(parentFilterColumns),
|
|
255
325
|
outputRecordIdKeys: Object.freeze(outputRecordIdKeys)
|