@postxl/generators 1.11.3 → 1.11.5
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/dist/backend-import/generators/detect-delta/detect-delta-all-models.generator.js +70 -43
- package/dist/backend-import/generators/detect-delta/detect-delta-all-models.generator.js.map +1 -1
- package/dist/backend-import/generators/detect-delta/detect-delta-functions.generator.js +241 -4
- package/dist/backend-import/generators/detect-delta/detect-delta-functions.generator.js.map +1 -1
- package/dist/backend-import/generators/detect-delta/model-function.generator.js +113 -9
- package/dist/backend-import/generators/detect-delta/model-function.generator.js.map +1 -1
- package/dist/backend-import/import.generator.d.ts +4 -0
- package/dist/backend-import/import.generator.js +2 -0
- package/dist/backend-import/import.generator.js.map +1 -1
- package/dist/decoders/datamodel-decoder.generator.js +80 -1
- package/dist/decoders/datamodel-decoder.generator.js.map +1 -1
- package/dist/decoders/discriminated-union.decoder.generator.js +76 -9
- package/dist/decoders/discriminated-union.decoder.generator.js.map +1 -1
- package/dist/decoders/model-decoder.generator.js +120 -17
- package/dist/decoders/model-decoder.generator.js.map +1 -1
- package/dist/e2e/generators/docker-sh.generator.js +13 -5
- package/dist/e2e/generators/docker-sh.generator.js.map +1 -1
- package/dist/e2e/template/e2e/specs/example.spec.ts-snapshots/Navigate-to-homepage-and-take-snapshot-1-chromium-linux.png +0 -0
- package/dist/frontend-core/template/src/pages/dashboard/dashboard.page.tsx +1 -1
- package/package.json +2 -2
|
@@ -13,11 +13,16 @@ function generateDetectDeltaAllModels(context) {
|
|
|
13
13
|
.add(context.import.detectDelta.types.generic.modelType)
|
|
14
14
|
.addType(context.import.detectDelta.types.generic.errors.type)
|
|
15
15
|
.add(context.import.detectDelta.types.generic.errors.typeGuard)
|
|
16
|
+
.add(context.import.detectDelta._utils.ensureItemsHaveIds)
|
|
16
17
|
.addImport({
|
|
17
18
|
from: (0, generator_1.toPackageName)('@postxl/utils'),
|
|
18
19
|
items: [(0, generator_1.toFunctionName)('red'), (0, generator_1.toFunctionName)('format'), (0, generator_1.toFunctionName)('pluralize')],
|
|
20
|
+
})
|
|
21
|
+
.addImport({
|
|
22
|
+
from: (0, generator_1.toPackageName)('node:crypto'),
|
|
23
|
+
items: [(0, generator_1.toFunctionName)('randomUUID')],
|
|
19
24
|
});
|
|
20
|
-
const { resultAssignments, idMapTypes,
|
|
25
|
+
const { resultAssignments, idMapTypes, keyMapTypes, deltaTypes, prepareImportMapsAssignments, prepareImportMapsIdMapResult, prepareImportMapsKeyMapResult, } = prepareModelDeltaTypes({
|
|
21
26
|
context,
|
|
22
27
|
imports,
|
|
23
28
|
});
|
|
@@ -45,39 +50,25 @@ export type KeyMap = {
|
|
|
45
50
|
${keyMapTypes.join('\n')}
|
|
46
51
|
}
|
|
47
52
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
return result
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function toIdMap<Model extends {id: ID}, ID extends string>(items?: Model[]): Map<ID, Model> {
|
|
63
|
-
const result = new Map<ID, Model>()
|
|
64
|
-
if (!items) return result
|
|
65
|
-
for (const item of items) {
|
|
66
|
-
result.set(item.id, item)
|
|
67
|
-
}
|
|
68
|
-
return result
|
|
69
|
-
}
|
|
53
|
+
/**
|
|
54
|
+
* Pass 1: Ensure all items have IDs assigned.
|
|
55
|
+
* For items without an ID, check database by key or generate a new UUID.
|
|
56
|
+
* This populates idMap and keyMap with items that have proper IDs.
|
|
57
|
+
*/
|
|
58
|
+
async function prepareImportMaps(
|
|
59
|
+
dataService: ${context.repositories.dataService.name},
|
|
60
|
+
data: ${decoders.dataModel.decodedType.name},
|
|
61
|
+
): Promise<{ idMap: ${context.import.detectDelta.allModels.idMapType.name}; keyMap: KeyMap }> {
|
|
62
|
+
${prepareImportMapsAssignments.join('\n')}
|
|
70
63
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
64
|
+
return {
|
|
65
|
+
idMap: {
|
|
66
|
+
${prepareImportMapsIdMapResult.join(',\n')}
|
|
67
|
+
},
|
|
68
|
+
keyMap: {
|
|
69
|
+
${prepareImportMapsKeyMapResult.join(',\n')}
|
|
70
|
+
},
|
|
79
71
|
}
|
|
80
|
-
return result
|
|
81
72
|
}
|
|
82
73
|
|
|
83
74
|
export async function detectDelta(
|
|
@@ -85,11 +76,18 @@ export async function detectDelta(
|
|
|
85
76
|
data: ${decoders.dataModel.decodedType.name},
|
|
86
77
|
throwInCaseOfError = true,
|
|
87
78
|
): Promise<${allModels.type.name}> {
|
|
88
|
-
|
|
89
|
-
|
|
79
|
+
// Pass 1: Ensure all items have IDs and build lookup maps
|
|
80
|
+
// This must happen FIRST so that when resolving relation keys,
|
|
81
|
+
// referenced items already have their IDs assigned.
|
|
82
|
+
const { idMap, keyMap } = await prepareImportMaps(dataService, data)
|
|
83
|
+
|
|
84
|
+
// Pass 2 & 3: Run delta detection (which includes relation key resolution)
|
|
85
|
+
// The resolveRelationKeys callback in each model's detectDelta will use
|
|
86
|
+
// idMap and keyMap to resolve relation field keys to proper IDs.
|
|
90
87
|
const result: ${allModels.type.name} = {
|
|
91
88
|
${resultAssignments.join(',\n')}
|
|
92
89
|
}
|
|
90
|
+
|
|
93
91
|
const errors = ${allModels.extractErrors.name}(result)
|
|
94
92
|
if (Object.keys(errors).length > 0 && throwInCaseOfError) {
|
|
95
93
|
throw new Error(${allModels.summarizeErrors.name}(errors))
|
|
@@ -139,9 +137,11 @@ function prepareModelDeltaTypes({ context, imports }) {
|
|
|
139
137
|
const deltaTypes = [];
|
|
140
138
|
const resultAssignments = [];
|
|
141
139
|
const idMapTypes = [];
|
|
142
|
-
const idMapInit = [];
|
|
143
140
|
const keyMapTypes = [];
|
|
144
|
-
const
|
|
141
|
+
const prepareImportMapsAssignments = [];
|
|
142
|
+
const prepareImportMapsIdMapResult = [];
|
|
143
|
+
const prepareImportMapsKeyMapResult = [];
|
|
144
|
+
const ensureItemsHaveIdsFn = context.import.detectDelta._utils.ensureItemsHaveIds.name;
|
|
145
145
|
for (const model of context.models.values()) {
|
|
146
146
|
imports
|
|
147
147
|
//
|
|
@@ -150,18 +150,45 @@ function prepareModelDeltaTypes({ context, imports }) {
|
|
|
150
150
|
.add(model.import.delta.errors)
|
|
151
151
|
.add(model.import.delta.detect);
|
|
152
152
|
const keyFieldName = model.keyField.name;
|
|
153
|
-
|
|
153
|
+
const dataEntry = model.decoder.dataModelEntry;
|
|
154
|
+
deltaTypes.push(`${dataEntry}?: ${context.import.detectDelta.types.generic.modelType.name}<
|
|
154
155
|
${model.types.name},
|
|
155
156
|
${model.types.id.name},
|
|
156
157
|
${model.import.delta.errors.name}
|
|
157
158
|
>[]`);
|
|
158
|
-
idMapTypes.push(`'${
|
|
159
|
-
idMapInit.push(`${model.decoder.dataModelEntry}: toIdMap(data.${model.decoder.dataModelEntry})`);
|
|
159
|
+
idMapTypes.push(`'${dataEntry}': Map<${model.types.id.name}, ${model.types.name}>`);
|
|
160
160
|
// KeyMap uses the keyField for lookups (supports string or number keys)
|
|
161
|
-
keyMapTypes.push(`'${
|
|
162
|
-
|
|
163
|
-
|
|
161
|
+
keyMapTypes.push(`'${dataEntry}': Map<string | number, ${model.types.name}>`);
|
|
162
|
+
// Generate the prepareImportMaps call for this model
|
|
163
|
+
// For models with NoRepository, we can't look up by key from database - just generate new IDs
|
|
164
|
+
if (model.repository.kind === 'NoRepository') {
|
|
165
|
+
prepareImportMapsAssignments.push(`const ${dataEntry}Result = await ${ensureItemsHaveIdsFn}({
|
|
166
|
+
items: data.${dataEntry},
|
|
167
|
+
keyFieldName: '${keyFieldName}',
|
|
168
|
+
getByKey: async () => null, // NoRepository - can't look up from database
|
|
169
|
+
generateId: () => randomUUID() as ${model.types.id.name},
|
|
170
|
+
})`);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
prepareImportMapsAssignments.push(`const ${dataEntry}Result = await ${ensureItemsHaveIdsFn}({
|
|
174
|
+
items: data.${dataEntry},
|
|
175
|
+
keyFieldName: '${keyFieldName}',
|
|
176
|
+
getByKey: (key) => dataService.${model.repository.dataServiceVariable}.getByKey(key),
|
|
177
|
+
generateId: () => randomUUID() as ${model.types.id.name},
|
|
178
|
+
})`);
|
|
179
|
+
}
|
|
180
|
+
prepareImportMapsIdMapResult.push(`${dataEntry}: ${dataEntry}Result.idMap`);
|
|
181
|
+
prepareImportMapsKeyMapResult.push(`${dataEntry}: ${dataEntry}Result.keyMap`);
|
|
182
|
+
resultAssignments.push(`${dataEntry}: await ${model.import.delta.detect.name}(dataService, data.${dataEntry} ?? [], idMap, keyMap)`);
|
|
164
183
|
}
|
|
165
|
-
return {
|
|
184
|
+
return {
|
|
185
|
+
resultAssignments,
|
|
186
|
+
deltaTypes,
|
|
187
|
+
idMapTypes,
|
|
188
|
+
keyMapTypes,
|
|
189
|
+
prepareImportMapsAssignments,
|
|
190
|
+
prepareImportMapsIdMapResult,
|
|
191
|
+
prepareImportMapsKeyMapResult,
|
|
192
|
+
};
|
|
166
193
|
}
|
|
167
194
|
//# sourceMappingURL=detect-delta-all-models.generator.js.map
|
package/dist/backend-import/generators/detect-delta/detect-delta-all-models.generator.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect-delta-all-models.generator.js","sourceRoot":"","sources":["../../../../src/backend-import/generators/detect-delta/detect-delta-all-models.generator.ts"],"names":[],"mappings":";;AAIA,
|
|
1
|
+
{"version":3,"file":"detect-delta-all-models.generator.js","sourceRoot":"","sources":["../../../../src/backend-import/generators/detect-delta/detect-delta-all-models.generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -82,6 +82,11 @@ export type ${detectDelta._utils.validation.params.name}<Model extends ${dto.gen
|
|
|
82
82
|
* Detects changes for a model and returns the result as a Delta_Model object.
|
|
83
83
|
* When both id and keyField are provided, id takes precedence for looking up existing records.
|
|
84
84
|
* Falls back to keyField when id is not provided.
|
|
85
|
+
*
|
|
86
|
+
* IMPORTANT: For correct delta detection, relation field keys must be resolved to IDs
|
|
87
|
+
* BEFORE calling this function. Use the resolveRelationKeys callback to do this.
|
|
88
|
+
* Otherwise, the delta calculation will incorrectly show changes for relation fields
|
|
89
|
+
* when comparing keys against IDs.
|
|
85
90
|
*/
|
|
86
91
|
export async function ${detectDelta._utils.detectModelDelta.name}<Model extends ${dto.genericModel.name}<ID>, ID extends ${dto.idType.name}, ModelErrors, KeyFieldName extends keyof Model>({
|
|
87
92
|
items,
|
|
@@ -89,6 +94,7 @@ export async function ${detectDelta._utils.detectModelDelta.name}<Model extends
|
|
|
89
94
|
getById,
|
|
90
95
|
getByKey,
|
|
91
96
|
getDelta,
|
|
97
|
+
resolveRelationKeys,
|
|
92
98
|
validate
|
|
93
99
|
}: {
|
|
94
100
|
items?: Model[]
|
|
@@ -99,6 +105,12 @@ export async function ${detectDelta._utils.detectModelDelta.name}<Model extends
|
|
|
99
105
|
/** Function to look up an existing record by its key value (fallback when id is not provided) */
|
|
100
106
|
getByKey: (key: Model[KeyFieldName]) => Promise<Model | null>
|
|
101
107
|
getDelta: (args: { item: Model; existingItem: Model }) => ${delta_Fields.name}<Model, ID>
|
|
108
|
+
/**
|
|
109
|
+
* Function to resolve relation field keys to IDs in the item.
|
|
110
|
+
* This MUST be called before delta calculation to ensure correct comparison.
|
|
111
|
+
* Returns the resolved item and any errors encountered during resolution.
|
|
112
|
+
*/
|
|
113
|
+
resolveRelationKeys: (item: Model) => Promise<{ item: Model; errors: ModelErrors[] }>
|
|
102
114
|
validate: ${detectDelta._utils.validation.params.name}<Model, ID, ModelErrors>
|
|
103
115
|
}): Promise<${modelType.name}<Model, ID, ModelErrors>[]> {
|
|
104
116
|
const upsertResult: ${modelType.name}<Model, ID, ModelErrors>[] = []
|
|
@@ -108,7 +120,18 @@ export async function ${detectDelta._utils.detectModelDelta.name}<Model extends
|
|
|
108
120
|
}
|
|
109
121
|
|
|
110
122
|
for (const item of items) {
|
|
111
|
-
|
|
123
|
+
// IMPORTANT: Resolve relation field keys to IDs BEFORE delta calculation.
|
|
124
|
+
// This ensures that when we compare the item against existing records,
|
|
125
|
+
// relation fields contain actual IDs (not keys), enabling correct delta detection.
|
|
126
|
+
const { item: resolvedItem, errors: resolutionErrors } = await resolveRelationKeys(item)
|
|
127
|
+
|
|
128
|
+
// If key resolution failed, report errors and skip this item
|
|
129
|
+
if (resolutionErrors.length > 0) {
|
|
130
|
+
upsertResult.push({ type: 'errors', input: item, errors: resolutionErrors })
|
|
131
|
+
continue
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const deltaResult = await determineDeltaResult({ item: resolvedItem, keyFieldName, getById, getByKey, getDelta })
|
|
112
135
|
const errors = deltaResult.type === 'unchanged' ? [] : await validate[deltaResult.type](deltaResult as any)
|
|
113
136
|
|
|
114
137
|
if (errors.length !== 0) {
|
|
@@ -117,7 +140,7 @@ export async function ${detectDelta._utils.detectModelDelta.name}<Model extends
|
|
|
117
140
|
}
|
|
118
141
|
switch (deltaResult.type) {
|
|
119
142
|
case '${create.discriminant}':
|
|
120
|
-
upsertResult.push({ type: '${create.discriminant}', input: item, createDto:
|
|
143
|
+
upsertResult.push({ type: '${create.discriminant}', input: item, createDto: resolvedItem })
|
|
121
144
|
break
|
|
122
145
|
|
|
123
146
|
case '${update.discriminant}':
|
|
@@ -281,6 +304,137 @@ export function ${detectDelta._utils.keepErrors.name}<O, I extends O | undefined
|
|
|
281
304
|
return errors.filter((error) => error !== undefined) as O[]
|
|
282
305
|
}
|
|
283
306
|
|
|
307
|
+
/**
|
|
308
|
+
* Checks if the item needs an ID assigned
|
|
309
|
+
*/
|
|
310
|
+
function needsIdAssignment<ID extends ${dto.idType.name}>(id: ID | undefined): boolean {
|
|
311
|
+
return !id || id === ('' as unknown as ID)
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Extracts and validates the key value from an item
|
|
316
|
+
*/
|
|
317
|
+
function getValidKeyValue<Model, KeyFieldName extends keyof Model>(
|
|
318
|
+
item: Model,
|
|
319
|
+
keyFieldName: KeyFieldName,
|
|
320
|
+
): string | number | null {
|
|
321
|
+
const keyValue = item[keyFieldName] as string | number | undefined | null
|
|
322
|
+
|
|
323
|
+
if (keyValue === undefined || keyValue === null || keyValue === '') {
|
|
324
|
+
return null
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return keyValue
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Assigns an ID to an item by either finding existing entity or generating new ID
|
|
332
|
+
*/
|
|
333
|
+
async function assignIdToItem<Model extends ${dto.genericModel.name}<ID>, ID extends ${dto.idType.name}>({
|
|
334
|
+
item,
|
|
335
|
+
keyValue,
|
|
336
|
+
getByKey,
|
|
337
|
+
generateId,
|
|
338
|
+
}: {
|
|
339
|
+
item: Model
|
|
340
|
+
keyValue: string | number | null
|
|
341
|
+
getByKey: (key: string) => Promise<Model | null>
|
|
342
|
+
generateId: () => ID
|
|
343
|
+
}): Promise<void> {
|
|
344
|
+
// If no valid key, generate new ID
|
|
345
|
+
if (keyValue === null) {
|
|
346
|
+
item.id = generateId()
|
|
347
|
+
return
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Try to find existing entity by key
|
|
351
|
+
const existingItem = await getByKey(String(keyValue))
|
|
352
|
+
item.id = existingItem ? existingItem.id : generateId()
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Adds an item to the ID and key lookup maps
|
|
357
|
+
*/
|
|
358
|
+
function addItemToMaps<
|
|
359
|
+
Model extends ${dto.genericModel.name}<ID>,
|
|
360
|
+
ID extends ${dto.idType.name},
|
|
361
|
+
KeyFieldName extends keyof Model,
|
|
362
|
+
>({
|
|
363
|
+
item,
|
|
364
|
+
keyFieldName,
|
|
365
|
+
idMap,
|
|
366
|
+
keyMap,
|
|
367
|
+
}: {
|
|
368
|
+
item: Model
|
|
369
|
+
keyFieldName: KeyFieldName
|
|
370
|
+
idMap: Map<ID, Model>
|
|
371
|
+
keyMap: Map<string | number, Model>
|
|
372
|
+
}): void {
|
|
373
|
+
idMap.set(item.id, item)
|
|
374
|
+
|
|
375
|
+
const keyValue = getValidKeyValue(item, keyFieldName)
|
|
376
|
+
if (keyValue !== null) {
|
|
377
|
+
keyMap.set(keyValue, item)
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Ensures all items have IDs assigned.
|
|
383
|
+
* For items without an ID (undefined, null, or empty string):
|
|
384
|
+
* 1. If keyField value is provided, check database for existing entity with that key -> use its ID
|
|
385
|
+
* 2. Otherwise, generate a new UUID
|
|
386
|
+
*
|
|
387
|
+
* This must be called BEFORE building idMap/keyMap to ensure all items have proper IDs
|
|
388
|
+
* that can be referenced by other models during relation key resolution.
|
|
389
|
+
*
|
|
390
|
+
* @returns Object containing:
|
|
391
|
+
* - items: The items with IDs assigned
|
|
392
|
+
* - idMap: Map of ID -> item (for items with IDs)
|
|
393
|
+
* - keyMap: Map of keyField -> item (for all items with keyField values)
|
|
394
|
+
*/
|
|
395
|
+
export async function ${detectDelta._utils.ensureItemsHaveIds.name}<
|
|
396
|
+
Model extends ${dto.genericModel.name}<ID>,
|
|
397
|
+
ID extends ${dto.idType.name},
|
|
398
|
+
KeyFieldName extends keyof Model,
|
|
399
|
+
>({
|
|
400
|
+
items,
|
|
401
|
+
keyFieldName,
|
|
402
|
+
getByKey,
|
|
403
|
+
generateId,
|
|
404
|
+
}: {
|
|
405
|
+
items?: Model[]
|
|
406
|
+
/** The name of the keyField for this model */
|
|
407
|
+
keyFieldName: KeyFieldName
|
|
408
|
+
/** Function to look up an existing entity by keyField value from the database */
|
|
409
|
+
getByKey: (key: string) => Promise<Model | null>
|
|
410
|
+
/** Function to generate a new ID (e.g., crypto.randomUUID() with brand) */
|
|
411
|
+
generateId: () => ID
|
|
412
|
+
}): Promise<{
|
|
413
|
+
items: Model[]
|
|
414
|
+
idMap: Map<ID, Model>
|
|
415
|
+
keyMap: Map<string | number, Model>
|
|
416
|
+
}> {
|
|
417
|
+
const idMap = new Map<ID, Model>()
|
|
418
|
+
const keyMap = new Map<string | number, Model>()
|
|
419
|
+
|
|
420
|
+
if (!items || items.length === 0) {
|
|
421
|
+
return { items: [], idMap, keyMap }
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
for (const item of items) {
|
|
425
|
+
// Assign ID if needed
|
|
426
|
+
if (needsIdAssignment(item.id)) {
|
|
427
|
+
const keyValue = getValidKeyValue(item, keyFieldName)
|
|
428
|
+
await assignIdToItem({ item, keyValue, getByKey, generateId })
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Add item to lookup maps
|
|
432
|
+
addItemToMaps({ item, keyFieldName, idMap, keyMap })
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return { items, idMap, keyMap }
|
|
436
|
+
}
|
|
437
|
+
|
|
284
438
|
/**
|
|
285
439
|
* Validates that the model has all required fields.
|
|
286
440
|
*/
|
|
@@ -308,8 +462,80 @@ export function ${detectDelta._utils.validation.requiredFields.name}<
|
|
|
308
462
|
}
|
|
309
463
|
}
|
|
310
464
|
|
|
465
|
+
/**
|
|
466
|
+
* Resolves a relation field value (which could be an ID or a key) to the actual ID.
|
|
467
|
+
* This is used before delta calculation to ensure relation fields contain IDs, not keys.
|
|
468
|
+
*
|
|
469
|
+
* Resolution order:
|
|
470
|
+
* 1. Check idMap (if value is already an ID in the current import batch)
|
|
471
|
+
* 2. Check keyMap (if value is a keyField value from imported data)
|
|
472
|
+
* 3. Database lookup by ID
|
|
473
|
+
* 4. Database lookup by keyField
|
|
474
|
+
*
|
|
475
|
+
* @returns The resolved ID if found, or an error if the reference is invalid
|
|
476
|
+
*/
|
|
477
|
+
export async function ${detectDelta._utils.resolveRelationKey.name}<
|
|
478
|
+
ID extends ${dto.idType.name},
|
|
479
|
+
RelatedModel extends ${dto.genericModel.name}<ID>,
|
|
480
|
+
FieldName extends string,
|
|
481
|
+
RelatedModelName extends string,
|
|
482
|
+
KeyType extends string | number = string | number,
|
|
483
|
+
>({
|
|
484
|
+
value,
|
|
485
|
+
fieldName,
|
|
486
|
+
relatedModel,
|
|
487
|
+
idMapEntry,
|
|
488
|
+
keyMapEntry,
|
|
489
|
+
getById,
|
|
490
|
+
getByKey,
|
|
491
|
+
}: {
|
|
492
|
+
/** The value to resolve - could be an ID or a keyField value */
|
|
493
|
+
value: ID
|
|
494
|
+
/** The name of the field being resolved (for error reporting) */
|
|
495
|
+
fieldName: FieldName
|
|
496
|
+
/** The name of the related model (for error reporting) */
|
|
497
|
+
relatedModel: RelatedModelName
|
|
498
|
+
/** Map of IDs to items from the current import batch */
|
|
499
|
+
idMapEntry: Map<ID, RelatedModel>
|
|
500
|
+
/** Map of keyField values to items from the current import batch */
|
|
501
|
+
keyMapEntry: Map<KeyType, RelatedModel>
|
|
502
|
+
/** Function to look up an item by ID from the database */
|
|
503
|
+
getById: (id: ID) => Promise<RelatedModel | null>
|
|
504
|
+
/** Function to look up an item by keyField from the database */
|
|
505
|
+
getByKey: (key: string) => Promise<RelatedModel | null>
|
|
506
|
+
}): Promise<{ id: ID } | { error: ${errors.invalidReference.type.name}<RelatedModelName, FieldName, ID> }> {
|
|
507
|
+
// 1. Try idMap (if value is an id from the current import batch)
|
|
508
|
+
const byId = idMapEntry.get(value)
|
|
509
|
+
if (byId) {
|
|
510
|
+
return { id: byId.id }
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// 2. Try keyMap (if value is a keyField value from imported data)
|
|
514
|
+
const byKey = keyMapEntry.get(value as unknown as KeyType)
|
|
515
|
+
if (byKey) {
|
|
516
|
+
return { id: byKey.id }
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// 3. Try database lookup by ID
|
|
520
|
+
const fromDbById = await getById(value)
|
|
521
|
+
if (fromDbById) {
|
|
522
|
+
return { id: fromDbById.id }
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// 4. Try database lookup by keyField
|
|
526
|
+
const fromDbByKey = await getByKey(String(value))
|
|
527
|
+
if (fromDbByKey) {
|
|
528
|
+
return { id: fromDbByKey.id }
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Not found - return error
|
|
532
|
+
return { error: { error: '${errors.invalidReference.discriminant}', relatedModel, fieldName, value } }
|
|
533
|
+
}
|
|
534
|
+
|
|
311
535
|
/**
|
|
312
536
|
* Validates that the field is a reference to an existing item.
|
|
537
|
+
* If the value is a key (not an ID), it resolves the key to the actual ID and updates the item.
|
|
538
|
+
* This allows importing Excel data where relation fields contain human-readable keys instead of UUIDs.
|
|
313
539
|
*/
|
|
314
540
|
export async function ${detectDelta._utils.validation.referenceField.name}<
|
|
315
541
|
Model extends ${dto.genericModel.name}<ID>,
|
|
@@ -328,16 +554,27 @@ export async function ${detectDelta._utils.validation.referenceField.name}<
|
|
|
328
554
|
item: Partial<Model>
|
|
329
555
|
fieldName: FieldName
|
|
330
556
|
relatedModel: RelatedModelName
|
|
331
|
-
|
|
557
|
+
/**
|
|
558
|
+
* Function to get an existing item by ID or key.
|
|
559
|
+
* Should try ID lookup first, then key lookup.
|
|
560
|
+
* Returns the item if found, null otherwise.
|
|
561
|
+
*/
|
|
562
|
+
getExistingItem: (idOrKey: RelatedID) => Promise<RelatedModel | null>
|
|
332
563
|
}): Promise<${errors.invalidReference.type.name}<RelatedModelName, FieldName, Value> | undefined> {
|
|
333
564
|
const value: Value | undefined | null = item[fieldName] as Value | undefined | null
|
|
334
|
-
if (value === undefined || value === null) {
|
|
565
|
+
if (value === undefined || value === null) {
|
|
335
566
|
return undefined
|
|
336
567
|
}
|
|
337
568
|
const existingItem = await getExistingItem(value)
|
|
338
569
|
if (!existingItem) {
|
|
339
570
|
return { error: '${errors.invalidReference.discriminant}', relatedModel, fieldName, value }
|
|
340
571
|
}
|
|
572
|
+
// If the existing item was found and its ID differs from the provided value,
|
|
573
|
+
// it means the value was a key (not an ID). Replace the key with the actual ID.
|
|
574
|
+
// This ensures the item has the correct ID for database operations.
|
|
575
|
+
if (existingItem.id !== value) {
|
|
576
|
+
(item as Record<string, unknown>)[fieldName] = existingItem.id
|
|
577
|
+
}
|
|
341
578
|
return undefined
|
|
342
579
|
}
|
|
343
580
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect-delta-functions.generator.js","sourceRoot":"","sources":["../../../../src/backend-import/generators/detect-delta/detect-delta-functions.generator.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,
|
|
1
|
+
{"version":3,"file":"detect-delta-functions.generator.js","sourceRoot":"","sources":["../../../../src/backend-import/generators/detect-delta/detect-delta-functions.generator.ts"],"names":[],"mappings":"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"}
|