@postxl/generator 0.39.0 → 0.40.1

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.
Files changed (38) hide show
  1. package/dist/generator.js +37 -21
  2. package/dist/generators/indices/businesslogic-update-module.generator.js +1 -1
  3. package/dist/generators/indices/businesslogic-view-module.generator.js +1 -1
  4. package/dist/generators/indices/{seed-service.generator.d.ts → data-types.generator.d.ts} +2 -2
  5. package/dist/generators/indices/data-types.generator.js +48 -0
  6. package/dist/generators/indices/datamock-module.generator.js +7 -7
  7. package/dist/generators/indices/datamodule.generator.js +13 -34
  8. package/dist/generators/indices/dataservice.generator.js +201 -8
  9. package/dist/generators/indices/dispatcher-service.generator.js +14 -5
  10. package/dist/generators/indices/importexport-convert-import-functions.generator.d.ts +9 -0
  11. package/dist/generators/indices/importexport-convert-import-functions.generator.js +528 -0
  12. package/dist/generators/indices/{seed-template-decoder.generator.d.ts → importexport-exporter-class.generator.d.ts} +2 -2
  13. package/dist/generators/indices/importexport-exporter-class.generator.js +116 -0
  14. package/dist/generators/indices/importexport-import-service.generator.d.ts +9 -0
  15. package/dist/generators/indices/importexport-import-service.generator.js +563 -0
  16. package/dist/generators/indices/{seeddata-type.generator.d.ts → importexport-types.generator.d.ts} +2 -2
  17. package/dist/generators/indices/importexport-types.generator.js +234 -0
  18. package/dist/generators/indices/repositories.generator.js +7 -7
  19. package/dist/generators/indices/seed-template.generator.js +1 -1
  20. package/dist/generators/indices/testdata-service.generator.js +5 -4
  21. package/dist/generators/models/businesslogic-update.generator.js +5 -5
  22. package/dist/generators/models/businesslogic-view.generator.js +3 -3
  23. package/dist/generators/models/importexport-decoder.generator.d.ts +23 -0
  24. package/dist/generators/models/importexport-decoder.generator.js +234 -0
  25. package/dist/generators/models/repository.generator.js +50 -21
  26. package/dist/lib/imports.d.ts +1 -1
  27. package/dist/lib/meta.d.ts +468 -134
  28. package/dist/lib/meta.js +187 -80
  29. package/dist/lib/schema/schema.d.ts +54 -43
  30. package/dist/lib/schema/types.d.ts +63 -12
  31. package/dist/lib/schema/types.js +27 -7
  32. package/dist/lib/utils/string.d.ts +1 -0
  33. package/dist/lib/utils/string.js +4 -0
  34. package/dist/prisma/parse.js +4 -4
  35. package/package.json +1 -1
  36. package/dist/generators/indices/seed-service.generator.js +0 -354
  37. package/dist/generators/indices/seed-template-decoder.generator.js +0 -151
  38. package/dist/generators/indices/seeddata-type.generator.js +0 -42
@@ -0,0 +1,563 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateImportExportImportService = void 0;
4
+ const imports_1 = require("../../lib/imports");
5
+ const meta_1 = require("../../lib/meta");
6
+ const string_1 = require("../../lib/utils/string");
7
+ /**
8
+ * Generates the Exporter class for the Import-Export module
9
+ */
10
+ function generateImportExportImportService({ models, meta }) {
11
+ const imports = imports_1.ImportsGenerator.from(meta.importExport.importService.filePath);
12
+ const { types, decoder, converterFunctions } = meta.importExport;
13
+ const { delta_Fields, delta, delta_Model } = types;
14
+ const { create, errors, unchanged, update } = delta_Model;
15
+ const { nonUnique, invalidReference, invalidType, isRequiredDependency, missingField } = errors;
16
+ const { dto } = meta.types;
17
+ imports.addImports({
18
+ [meta.data.importPath]: [meta.data.dataService.class, meta.data.types.bulkMutation],
19
+ [meta.types.importPath]: [dto.genericModel, dto.idType, dto.update],
20
+ [meta.actions.importPath]: [meta.actions.actionExecution.interface, meta.actions.dispatcherService.class],
21
+ [types.filePath]: [delta_Fields, delta_Model.type, delta],
22
+ [decoder.fullDecoderFilePath]: [decoder.decodedPXLModelDataTypeName],
23
+ [converterFunctions.filePath]: [converterFunctions.deltaToBulkMutations],
24
+ });
25
+ const resultAssignments = [];
26
+ const detectDeltaFunctions = [];
27
+ for (const model of models) {
28
+ const modelMeta = (0, meta_1.getModelMetadata)({ model });
29
+ imports.addImport({
30
+ from: modelMeta.types.importPath,
31
+ items: [modelMeta.types.typeName, modelMeta.types.brandedIdType],
32
+ });
33
+ resultAssignments.push(`${modelMeta.seed.constantName}: await this.detect${modelMeta.internalSingularNameCapitalized}Delta(data.${modelMeta.seed.constantName}),`);
34
+ detectDeltaFunctions.push(generateDetectDeltaFunction({ model, modelMeta, models, schemaMeta: meta, imports }));
35
+ }
36
+ return /* ts */ `
37
+ import { Injectable } from '@nestjs/common'
38
+ import { ExhaustiveSwitchCheck } from '@pxl/common'
39
+
40
+ ${imports.generate()}
41
+
42
+ import { Action_Import } from './actions'
43
+
44
+
45
+ type Delta_Result<Model extends ${dto.genericModel}<ID>, ID extends ${dto.idType}> =
46
+ | { type: '${create.discriminant}' }
47
+ | { type: '${update.discriminant}'; delta: ${delta_Fields}<Model, ID>; existingItem: Model }
48
+ | { type: '${delta_Model.delete.discriminant}'; existingItem: Model }
49
+ | { type: '${unchanged.discriminant}' }
50
+
51
+ @Injectable()
52
+ export class ${meta.importExport.importService.name} {
53
+ constructor(
54
+ private readonly data: ${meta.data.dataService.class},
55
+ private readonly dispatcher: ${meta.actions.dispatcherService.class},
56
+ ) {
57
+ // We have a circular dependency between DispatcherService and ${meta.importExport.importService.name}.
58
+ // In order to avoid trouble, instead of using Nest's forwardRef(), we inject the dispatcher service here
59
+ // and set the dispatcher's ${meta.importExport.importService.sharedName} to this instance.
60
+ this.dispatcher.${meta.importExport.importService.sharedName} =this
61
+ }
62
+
63
+
64
+ /**
65
+ * Will be called by the dispatcher service to dispatch the given action.
66
+ */
67
+ public async dispatch({
68
+ action,
69
+ execution
70
+ }: {
71
+ action: Action_Import;
72
+ execution: ${meta.actions.actionExecution.interface}
73
+ }) {
74
+ switch (action.type) {
75
+ case 'detect-delta':
76
+ return this.detectDelta(action.payload.data)
77
+ case 'execute-upsert':
78
+ return this.executeUpsert({ delta: action.payload, execution })
79
+ case 'execute-direct-upsert':
80
+ return this.executeDirectUpsert({ data: action.payload.data, execution })
81
+
82
+ default:
83
+ throw new ExhaustiveSwitchCheck(action)
84
+ }
85
+ }
86
+
87
+ public async detectDelta(data: ${decoder.decodedPXLModelDataTypeName}): Promise<{
88
+ bulkMutations: ${meta.data.types.bulkMutation}[]
89
+ upsertResult: ${delta}
90
+ }> {
91
+ const upsertResult: ${delta} = {
92
+ ${resultAssignments.join('\n')}
93
+ }
94
+
95
+ const bulkMutations: ${meta.data.types.bulkMutation}[] = ${converterFunctions.deltaToBulkMutations}(upsertResult)
96
+ return { bulkMutations, upsertResult }
97
+ }
98
+
99
+ public async executeUpsert({
100
+ delta,
101
+ execution
102
+ }: {
103
+ delta: Delta;
104
+ execution: ${meta.actions.actionExecution.interface}
105
+ }) {
106
+ const bulkMutations = ${converterFunctions.deltaToBulkMutations}(delta)
107
+ return this.data.${meta.data.dataService.executeBulkMutations}({ steps: bulkMutations, execution })
108
+ }
109
+
110
+ public async executeDirectUpsert({
111
+ data,
112
+ execution
113
+ }: {
114
+ data: ${meta.importExport.decoder.decodedPXLModelDataTypeName};
115
+ execution: ${meta.actions.actionExecution.interface}
116
+ }) {
117
+ const { bulkMutations } = await this.detectDelta(data)
118
+ return this.data.${meta.data.dataService.executeBulkMutations}({ steps: bulkMutations, execution })
119
+ }
120
+
121
+ /**
122
+ * Generic function to determine the what action (create/update/delete/unchanged) to take with an item
123
+ * In case the type is update it also includes the delta for a given item. In case of delete, it also includes
124
+ * the existing item.
125
+ *
126
+ * NOTE: The function does not implement any logic to automatically determine if an item should be deleted.
127
+ * Implement this logic based on the business requirements!
128
+ */
129
+ private async determineDeltaResult<Model extends ${dto.genericModel}<ID>, ID extends ${dto.idType}>({
130
+ item,
131
+ getId,
132
+ getDelta,
133
+ }: {
134
+ item: Model
135
+ getId: (id: ID) => Promise<Model | null>
136
+ getDelta: ({ item, existingItem }: { item: Model; existingItem: Model }) => ${delta_Fields}<Model, ID>
137
+ }): Promise<Delta_Result<Model, ID>> {
138
+ if (item.id === undefined || item.id === '') {
139
+ return Promise.resolve({ type: '${create.discriminant}' })
140
+ }
141
+ const existingItem = await getId(item.id)
142
+
143
+ if (!existingItem) {
144
+ return Promise.resolve({ type: '${create.discriminant}' })
145
+ }
146
+
147
+ const delta = getDelta({ item, existingItem })
148
+
149
+ if (Object.keys(delta).length > 0) {
150
+ return Promise.resolve({ type: '${update.discriminant}', delta, existingItem })
151
+ }
152
+
153
+ // We do not have a default logic to identify deletions. Implement it here, e.g. using a
154
+ // custom "Action" field in the Excel table. If it is set to "Delete", we can handle the delete here.
155
+ // return Promise.resolve({type: '${delta_Model.delete.discriminant}', existingItem})
156
+
157
+ // If we reach this point, we assume the item has not changed
158
+ return Promise.resolve({ type: '${unchanged.discriminant}' })
159
+ }
160
+
161
+ private getDelta<Model extends ${dto.genericModel}<ID>, ID extends ${dto.idType}>({
162
+ item,
163
+ existingItem,
164
+ properties,
165
+ }: {
166
+ item: Model
167
+ existingItem: Model
168
+ properties: (keyof Omit<Model, 'id'>)[]
169
+ }): ${delta_Fields}<Model, ID> {
170
+ const delta: ${delta_Fields}<Model, ID> = {}
171
+
172
+ for (const property of properties) {
173
+ addDelta({ delta, existingItem, item, property })
174
+ }
175
+
176
+ return delta
177
+ }
178
+
179
+ /**
180
+ * Detects changes for a model and returns the result as a Delta_Model object.
181
+ */
182
+ private async detectModelDelta<Model extends ${dto.genericModel}<ID>, ID extends ${dto.idType}, ModelErrors>({
183
+ items,
184
+ getId,
185
+ getDelta,
186
+ validateCreate,
187
+ validateUpdate,
188
+ validateDelete,
189
+ }: {
190
+ items?: Model[]
191
+ getId: (id: ID) => Promise<Model | null>
192
+ getDelta: (args: { item: Model; existingItem: Model }) => ${delta_Fields}<Model, ID>
193
+ validateCreate: (args: { item: Model }) => Promise<ModelErrors[]>
194
+ validateUpdate: (args: { item: Model; existingItem: Model }) => Promise<ModelErrors[]>
195
+ validateDelete: (args: { existingItem: Model }) => Promise<ModelErrors[]>
196
+ }): Promise<Delta_Model<Model, ID, ModelErrors>[]> {
197
+ const upsertResult: Delta_Model<Model, ID, ModelErrors>[] = []
198
+
199
+ if (items === undefined || items.length === 0) {
200
+ return Promise.resolve(upsertResult)
201
+ }
202
+
203
+ for (const item of items) {
204
+ const deltaResult = await this.determineDeltaResult({ item, getId, getDelta })
205
+
206
+ switch (deltaResult.type) {
207
+ case 'create': {
208
+ const errors = await validateCreate({ item })
209
+ if (errors.length === 0) {
210
+ upsertResult.push({ type: '${create.discriminant}', input: item, createDTO: item })
211
+ } else {
212
+ upsertResult.push({ type: '${errors.discriminant}', input: item, errors })
213
+ }
214
+ break
215
+ }
216
+
217
+ case 'update': {
218
+ const errors = await validateUpdate({ item, existingItem: deltaResult.existingItem })
219
+ if (errors.length === 0) {
220
+ upsertResult.push({
221
+ type: '${update.discriminant}',
222
+ input: item,
223
+ delta: deltaResult.delta,
224
+ updateDTO: deltaToUpdateDtoFunction({ id: item.id, delta: deltaResult.delta }),
225
+ })
226
+ } else {
227
+ upsertResult.push({ type: '${errors.discriminant}', input: item, errors })
228
+ }
229
+ break
230
+ }
231
+
232
+ case 'delete': {
233
+ const errors = await validateDelete({ existingItem: deltaResult.existingItem })
234
+ if (errors.length === 0) {
235
+ upsertResult.push({ type: '${delta_Model.delete.discriminant}', input: item })
236
+ } else {
237
+ upsertResult.push({ type: '${errors.discriminant}', input: item, errors })
238
+ }
239
+ break
240
+ }
241
+
242
+ case 'unchanged': {
243
+ upsertResult.push({ type: '${unchanged.discriminant}', input: item })
244
+ break
245
+ }
246
+
247
+ default:
248
+ throw new ExhaustiveSwitchCheck(deltaResult)
249
+ }
250
+ }
251
+
252
+ return Promise.resolve(upsertResult)
253
+ }
254
+
255
+ /**
256
+ * Internal helper function that removes undefined values from an array.
257
+ */
258
+ private keepErrors<ModelErrors>(errors: (ModelErrors | undefined)[]): ModelErrors[] {
259
+ return errors.filter((error) => error !== undefined) as ModelErrors[]
260
+ }
261
+
262
+ private validateRequiredFields<
263
+ Model extends ${dto.genericModel}<ID>,
264
+ ID extends ${dto.idType},
265
+ FieldName extends Extract<keyof Omit<Model, 'id'>, string>
266
+ >({
267
+ item,
268
+ fieldNames,
269
+ }: {
270
+ item: Model
271
+ fieldNames: FieldName[]
272
+ }): ${missingField.type}<FieldName>[] {
273
+ const errors: ${missingField.type}<FieldName>[] = []
274
+ for (const fieldName of fieldNames) {
275
+ if (item[fieldName] === undefined) {
276
+ errors.push({ error: '${missingField.discriminant}', fieldName })
277
+ }
278
+ }
279
+ if (errors.length > 0) {
280
+ return errors
281
+ } else {
282
+ return []
283
+ }
284
+ }
285
+
286
+ private async validateReferenceField<
287
+ Model extends ${dto.genericModel}<ID>,
288
+ ID extends ${dto.idType},
289
+ RelatedModel extends ${dto.genericModel}<RelatedID>,
290
+ RelatedID extends ${dto.idType},
291
+ FieldName extends keyof Omit<Model, 'id'> & string,
292
+ Value extends RelatedID,
293
+ RelatedModelName extends string,
294
+ >({
295
+ item,
296
+ fieldName,
297
+ relatedModel,
298
+ getExistingItem,
299
+ }: {
300
+ item: Partial<Model>
301
+ fieldName: FieldName
302
+ relatedModel: RelatedModelName
303
+ getExistingItem: (id: RelatedID) => Promise<RelatedModel | null>
304
+ }): Promise<${invalidReference.type}<RelatedModelName, FieldName, Value> | undefined> {
305
+ const value: Value | undefined = item[fieldName] as Value | undefined
306
+ if (value === undefined) {
307
+ return undefined
308
+ }
309
+ const existingItem = await getExistingItem(value)
310
+ if (!existingItem) {
311
+ return { error: '${invalidReference.discriminant}', relatedModel, fieldName, value }
312
+ }
313
+ return undefined
314
+ }
315
+
316
+ private async validateUniqueField<
317
+ Model extends ${dto.genericModel}<ID>,
318
+ ID extends ${dto.idType},
319
+ FieldName extends keyof Omit<Model, 'id'> & string,
320
+ Value extends Model[FieldName],
321
+ >({
322
+ item,
323
+ fieldName,
324
+ getExistingItem,
325
+ }: {
326
+ item: Partial<Model>
327
+ fieldName: FieldName
328
+ getExistingItem: (fieldValue: Value) => Promise<Model | undefined>
329
+ }): Promise<${nonUnique.type}<FieldName, Value> | undefined> {
330
+ const value: Value | undefined = item[fieldName] as Value | undefined
331
+ if (value === undefined) {
332
+ return undefined
333
+ }
334
+ const existingItem = await getExistingItem(value)
335
+ if (existingItem) {
336
+ return { error: '${nonUnique.discriminant}', fieldName, value }
337
+ }
338
+ return undefined
339
+ }
340
+
341
+ private validateInt<
342
+ Model extends ${dto.genericModel}<ID>,
343
+ ID extends ${dto.idType},
344
+ FieldName extends keyof Omit<Model, 'id'> & string,
345
+ >({
346
+ item,
347
+ fieldName,
348
+ }: {
349
+ item: Model
350
+ fieldName: FieldName
351
+ }): ${invalidType.type}<FieldName, 'Int', number> | undefined {
352
+ const value: number | undefined | null = item[fieldName] as number | undefined | null
353
+ //we handle undefined and null in other value checks so we can ignore them here
354
+ if (value === undefined || value === null) {
355
+ return undefined
356
+ }
357
+
358
+ if (value !== Math.floor(value)) {
359
+ return { error: '${invalidType.discriminant}', fieldName, typeExpected: 'Int', value }
360
+ }
361
+ return undefined
362
+ }
363
+
364
+ private async validateRequiredDependency<
365
+ Model extends ${dto.genericModel}<ID>,
366
+ ID extends ${dto.idType},
367
+ RelatedModelName extends string,
368
+ RelatedField extends string,
369
+ RelatedModel extends ${dto.genericModel}<RelatedID>,
370
+ RelatedID extends ${dto.idType},
371
+ >({
372
+ item,
373
+ relatedModel,
374
+ relatedField,
375
+ getReferencedItems,
376
+ }: {
377
+ item: Model
378
+ relatedModel: RelatedModelName
379
+ relatedField: RelatedField
380
+ getReferencedItems: (id: ID) => Promise<RelatedModel[]>
381
+ }): Promise<
382
+ ${isRequiredDependency.type}<RelatedModelName, RelatedField, RelatedModel, RelatedID> | undefined
383
+ > {
384
+ const relatedItems = await getReferencedItems(item.id)
385
+ if (relatedItems.length > 0) {
386
+ return undefined
387
+ }
388
+ return { error: '${isRequiredDependency.discriminant}', relatedModel, relatedField, relatedItems }
389
+ }
390
+
391
+ ${detectDeltaFunctions.join('\n')}
392
+ }
393
+
394
+ /**
395
+ * Adds a delta entry for a property to the given delta object if the property
396
+ * of the item is different from the existing item
397
+ */
398
+ function addDelta<Model extends ${dto.genericModel}<ID>, ID extends ${dto.idType}>({
399
+ delta,
400
+ existingItem,
401
+ item,
402
+ property,
403
+ }: {
404
+ delta: ${delta_Fields}<Model, ID>
405
+ existingItem: Model
406
+ item: Model
407
+ property: keyof Omit<Model, 'id'>
408
+ }): void {
409
+ if (existingItem[property] === item[property]) {
410
+ return
411
+ }
412
+ delta[property] = { old: existingItem[property], new: item[property] }
413
+ }
414
+
415
+ /**
416
+ * Converts a Delta to an UpdateDTO.
417
+ */
418
+ function deltaToUpdateDtoFunction<Model extends ${dto.genericModel}<ID>, ID extends ${dto.idType}>({
419
+ id,
420
+ delta,
421
+ }: {
422
+ id: ID
423
+ delta: ${delta_Fields}<Model, ID>
424
+ }): ${dto.update}<Model, ID> {
425
+ const updateDTO = {
426
+ id: id,
427
+ } as UpdateDTO<Model, ID>
428
+
429
+ for (const [key, value] of Object.entries(delta)) {
430
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
431
+ const updatedValue = value.new as Model[keyof Omit<Model, 'id'>]
432
+ updateDTO[key as keyof Omit<Model, 'id'>] = updatedValue
433
+ }
434
+ return updateDTO
435
+ }`;
436
+ }
437
+ exports.generateImportExportImportService = generateImportExportImportService;
438
+ function generateDetectDeltaFunction({ model, modelMeta, models, schemaMeta, imports, }) {
439
+ const { types } = schemaMeta.importExport;
440
+ const { delta_Model } = types;
441
+ imports.addImport({
442
+ from: types.filePath,
443
+ items: [modelMeta.importExport.delta_Model_Errors],
444
+ });
445
+ const returnType = `${delta_Model.type}<${model.typeName}, ${modelMeta.types.brandedIdType}, ${modelMeta.importExport.delta_Model_Errors}>[]`;
446
+ const sharedValidations = [];
447
+ const requiredFields = [];
448
+ const fieldNames = [];
449
+ for (const field of model.fields) {
450
+ if (field.name === 'id' ||
451
+ field.attributes.isReadonly ||
452
+ field.attributes.isCreatedAt ||
453
+ field.attributes.isUpdatedAt) {
454
+ continue;
455
+ }
456
+ const fieldName = field.name;
457
+ fieldNames.push(`'${fieldName}'`);
458
+ if (field.isRequired) {
459
+ requiredFields.push(`'${fieldName}'`);
460
+ }
461
+ if (field.kind === 'scalar') {
462
+ if (field.isUnique) {
463
+ imports.addImport({ from: schemaMeta.importExport.types.filePath, items: [delta_Model.errors.nonUnique.type] });
464
+ const getByName = `getBy${(0, string_1.toPascalCase)(fieldName)}`;
465
+ sharedValidations.push(`this.validateUniqueField({
466
+ item,
467
+ fieldName: '${fieldName}',
468
+ getExistingItem: (value) => this.data.${modelMeta.data.dataServiceName}.${getByName}(value)
469
+ })`);
470
+ }
471
+ if (field.validation && field.validation.type === 'int') {
472
+ imports.addImport({
473
+ from: schemaMeta.importExport.types.filePath,
474
+ items: [delta_Model.errors.invalidType.type],
475
+ });
476
+ sharedValidations.push(`this.validateInt({item, fieldName: '${fieldName}'})`);
477
+ }
478
+ }
479
+ else if (field.kind === 'relation') {
480
+ const relatedModelMeta = (0, meta_1.getModelMetadata)({ model: field.relationToModel });
481
+ imports.addImport({
482
+ from: schemaMeta.importExport.types.filePath,
483
+ items: [delta_Model.errors.invalidReference.type],
484
+ });
485
+ sharedValidations.push(`this.validateReferenceField({
486
+ item,
487
+ fieldName: '${fieldName}',
488
+ relatedModel: '${field.relationToModel.name}',
489
+ getExistingItem: (id: ${field.relationToModel.brandedIdType}) => this.data.${relatedModelMeta.data.dataServiceName}.get(id)
490
+ })`);
491
+ }
492
+ }
493
+ const deleteValidations = [];
494
+ for (const relatedModel of models) {
495
+ if (relatedModel.name === model.name) {
496
+ continue;
497
+ }
498
+ for (const relatedField of relatedModel.fields) {
499
+ if (relatedField.kind !== 'relation' ||
500
+ relatedField.relationToModel.name !== model.name ||
501
+ !relatedField.isRequired) {
502
+ continue;
503
+ }
504
+ imports.addImport({
505
+ from: schemaMeta.importExport.types.filePath,
506
+ items: [delta_Model.errors.isRequiredDependency.type],
507
+ });
508
+ const relatedModelMeta = (0, meta_1.getModelMetadata)({ model: relatedModel });
509
+ const relatedFieldName = (0, string_1.toPascalCase)(relatedField.name);
510
+ deleteValidations.push(`
511
+ await this.validateRequiredDependency({
512
+ item: existingItem,
513
+ relatedModel: '${relatedModel.name}',
514
+ relatedField: '${relatedField.name}',
515
+ getReferencedItems: async (id: ${modelMeta.types.brandedIdType}) => [
516
+ ...(await this.data.${relatedModelMeta.data.dataServiceName}.getItemsFor${relatedFieldName}(id)).values()
517
+ ]
518
+ })`);
519
+ }
520
+ }
521
+ if (requiredFields.length > 0) {
522
+ imports.addImport({ from: schemaMeta.importExport.types.filePath, items: [delta_Model.errors.missingField.type] });
523
+ }
524
+ const requiredFieldsValidation = requiredFields.length > 0
525
+ ? `...this.validateRequiredFields({item, fieldNames: [${requiredFields.join(',')}]}),`
526
+ : '';
527
+ const sharedValidationsFunction = sharedValidations.length > 0
528
+ ? `(item: ${model.name}) => Promise.all([${sharedValidations.join(',\n')}])`
529
+ : 'async () => Promise.resolve([])';
530
+ const validateDelete = deleteValidations.length > 0
531
+ ? `async ({ existingItem }) => this.keepErrors([${deleteValidations.join(',\n')}])`
532
+ : 'async () => Promise.resolve([])';
533
+ return /* ts */ `
534
+ /**
535
+ * Detects changes for ${modelMeta.userFriendlyName} and returns the result as a ${delta_Model.type} object.
536
+ */
537
+ private async detect${modelMeta.internalSingularNameCapitalized}Delta(
538
+ items?: ${model.typeName}[]
539
+ ): Promise<${returnType}> {
540
+ const sharedValidations: (item: ${model.name}) => Promise<(${modelMeta.importExport.delta_Model_Errors} | undefined)[]> =
541
+ ${sharedValidationsFunction}
542
+
543
+ return this.detectModelDelta({
544
+ items,
545
+ getId: (id: ${model.brandedIdType}) => this.data.${modelMeta.data.dataServiceName}.get(id),
546
+ getDelta: ({ item, existingItem }: { item: ${model.name}; existingItem: ${model.name} }) =>
547
+ this.getDelta({
548
+ item,
549
+ existingItem,
550
+ properties: [${fieldNames.join(',')}],
551
+ }),
552
+ validateCreate: async ({ item }) =>
553
+ Promise.resolve(
554
+ this.keepErrors([
555
+ ${requiredFieldsValidation}
556
+ ...(await sharedValidations(item)),
557
+ ]),
558
+ ),
559
+ validateUpdate: async ({ item }) => Promise.resolve(this.keepErrors(await sharedValidations(item))),
560
+ validateDelete: ${validateDelete},
561
+ })
562
+ }`;
563
+ }
@@ -1,9 +1,9 @@
1
1
  import { SchemaMetaData } from '../../lib/meta';
2
2
  import { Model } from '../../lib/schema/schema';
3
3
  /**
4
- * Generates a type for all SeedData.
4
+ * Generates types for import export module.
5
5
  */
6
- export declare function generateSeedDataType({ models, meta }: {
6
+ export declare function generateImportExportTypes({ models, meta }: {
7
7
  models: Model[];
8
8
  meta: SchemaMetaData;
9
9
  }): string;