@postxl/generator 0.34.0 → 0.36.0

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 (60) hide show
  1. package/dist/generator.js +91 -32
  2. package/dist/generators/indices/businesslogic-actiontypes.generator.d.ts +9 -0
  3. package/dist/generators/indices/businesslogic-actiontypes.generator.js +39 -0
  4. package/dist/generators/indices/businesslogic-update-index.generator.d.ts +9 -0
  5. package/dist/generators/indices/businesslogic-update-index.generator.js +20 -0
  6. package/dist/generators/indices/businesslogic-update-module.generator.d.ts +9 -0
  7. package/dist/generators/indices/businesslogic-update-module.generator.js +69 -0
  8. package/dist/generators/indices/businesslogic-update-service.generator.d.ts +9 -0
  9. package/dist/generators/indices/{businesslogicservice.generator.js → businesslogic-update-service.generator.js} +9 -10
  10. package/dist/generators/indices/businesslogic-view-index.generator.d.ts +9 -0
  11. package/dist/generators/indices/businesslogic-view-index.generator.js +19 -0
  12. package/dist/generators/indices/{businesslogicindex.generator.d.ts → businesslogic-view-module.generator.d.ts} +2 -2
  13. package/dist/generators/indices/{businesslogicmodule.generator.js → businesslogic-view-module.generator.js} +22 -26
  14. package/dist/generators/indices/{businesslogicservice.generator.d.ts → businesslogic-view-service.generator.d.ts} +1 -1
  15. package/dist/generators/indices/businesslogic-view-service.generator.js +34 -0
  16. package/dist/generators/indices/{datamockmodule.generator.js → datamock-module.generator.js} +8 -16
  17. package/dist/generators/indices/datamocker.generator.js +46 -40
  18. package/dist/generators/indices/datamodule.generator.js +7 -13
  19. package/dist/generators/indices/{businesslogicmodule.generator.d.ts → dispatcher-service.generator.d.ts} +2 -2
  20. package/dist/generators/indices/dispatcher-service.generator.js +81 -0
  21. package/dist/generators/indices/seed-migration.generator.d.ts +9 -0
  22. package/dist/generators/indices/seed-migration.generator.js +35 -0
  23. package/dist/generators/indices/seed-service.generator.d.ts +1 -1
  24. package/dist/generators/indices/seed-service.generator.js +327 -123
  25. package/dist/generators/indices/seed-template-decoder.generator.js +22 -6
  26. package/dist/generators/indices/{seed.generator.d.ts → seeddata-type.generator.d.ts} +2 -2
  27. package/dist/generators/indices/seeddata-type.generator.js +42 -0
  28. package/dist/generators/indices/types.generator.d.ts +1 -1
  29. package/dist/generators/indices/types.generator.js +8 -6
  30. package/dist/generators/models/businesslogic-update.generator.d.ts +10 -0
  31. package/dist/generators/models/businesslogic-update.generator.js +243 -0
  32. package/dist/generators/models/businesslogic-view.generator.d.ts +10 -0
  33. package/dist/generators/models/{businesslogic.generator.js → businesslogic-view.generator.js} +24 -72
  34. package/dist/generators/models/react.generator/modals.generator.js +2 -2
  35. package/dist/generators/models/repository.generator.d.ts +9 -0
  36. package/dist/generators/models/repository.generator.js +420 -131
  37. package/dist/generators/models/route.generator.js +45 -55
  38. package/dist/generators/models/seed.generator.js +6 -2
  39. package/dist/generators/models/types.generator.js +60 -13
  40. package/dist/lib/attributes.d.ts +5 -0
  41. package/dist/lib/imports.d.ts +23 -2
  42. package/dist/lib/imports.js +19 -1
  43. package/dist/lib/meta.d.ts +287 -34
  44. package/dist/lib/meta.js +87 -16
  45. package/dist/lib/schema/schema.d.ts +24 -6
  46. package/dist/lib/schema/types.d.ts +4 -0
  47. package/dist/lib/utils/jsdoc.d.ts +1 -1
  48. package/dist/lib/utils/jsdoc.js +8 -6
  49. package/dist/lib/utils/string.js +2 -1
  50. package/dist/lib/vfs.d.ts +33 -0
  51. package/dist/lib/vfs.js +157 -0
  52. package/dist/prisma/attributes.js +7 -3
  53. package/dist/prisma/parse.js +25 -5
  54. package/package.json +8 -3
  55. package/dist/generators/indices/businesslogicindex.generator.js +0 -19
  56. package/dist/generators/indices/seed.generator.js +0 -17
  57. package/dist/generators/indices/testdataservice.generator.d.ts +0 -9
  58. package/dist/generators/indices/testdataservice.generator.js +0 -78
  59. package/dist/generators/models/businesslogic.generator.d.ts +0 -9
  60. /package/dist/generators/indices/{datamockmodule.generator.d.ts → datamock-module.generator.d.ts} +0 -0
@@ -13,19 +13,18 @@ const string_1 = require("../../lib/utils/string");
13
13
  */
14
14
  function generateRepository({ model, meta }) {
15
15
  const { idField } = model;
16
- const imports = imports_1.ImportsGenerator.from(meta.data.repoFilePath)
17
- .addImport({
18
- items: [model.typeName, model.brandedIdType],
19
- from: meta.types.importPath,
20
- })
21
- .addImport({
22
- items: [(0, types_1.toClassName)('Repository')],
23
- // TODO: Unify DataLib Imports https://github.com/PostXL/PostXL/issues/344!
24
- from: (0, types_1.toFileName)(model.schemaConfig.paths.dataLibPath + 'repository.type'),
25
- })
26
- .addImport({
27
- items: [meta.types.toBrandedIdTypeFnName],
28
- from: meta.types.importPath,
16
+ const schemaMeta = (0, meta_1.getSchemaMetadata)({ config: model.schemaConfig });
17
+ const imports = imports_1.ImportsGenerator.from(meta.data.repoFilePath).addImports({
18
+ [schemaMeta.data.repositoryTypeFilePath]: schemaMeta.data.repositoryTypeName,
19
+ [meta.types.importPath]: [
20
+ model.typeName,
21
+ model.brandedIdType,
22
+ meta.types.toBrandedIdTypeFnName,
23
+ meta.types.dto.create,
24
+ meta.types.dto.update,
25
+ meta.types.dto.upsert,
26
+ ],
27
+ [schemaMeta.actions.importPath]: [schemaMeta.actions.actionExecutionInterface],
29
28
  });
30
29
  // NOTE: We first generate different parts of the code responsible for a particular task
31
30
  // and then we combine them into the final code block.
@@ -40,6 +39,10 @@ function generateRepository({ model, meta }) {
40
39
  // 4.) max length string fields are ensured to not exceed their max length,
41
40
  // 5.) index for fields marked with index attribute,
42
41
  // 6.) relations are indexed by the foreign key.
42
+ //
43
+ // The repository is generated differently based on whether the model is stored in the database or in memory.
44
+ // If the model is stored in the database, all CUD operations will result in a database call. If the model is
45
+ // stored in memory, all CUD operations will be performed in memory without any database calls.
43
46
  const idBlocks = generateIdBlocks({ model, meta });
44
47
  const defaultValueBlocks = generateDefaultBlocks({ model, meta });
45
48
  const uniqueStringFieldsBlocks = generateUniqueFieldsBlocks({ model, meta });
@@ -51,6 +54,7 @@ function generateRepository({ model, meta }) {
51
54
  mainBlocks = _generateMainBuildingBlocks_InMemoryOnly({
52
55
  model,
53
56
  meta,
57
+ schemaMeta,
54
58
  blocks: {
55
59
  uniqueStringFieldsBlocks,
56
60
  defaultValueBlocks,
@@ -64,6 +68,7 @@ function generateRepository({ model, meta }) {
64
68
  mainBlocks = generateMainBuildingBlocks_InDatabase({
65
69
  model,
66
70
  meta,
71
+ schemaMeta,
67
72
  imports,
68
73
  blocks: {
69
74
  uniqueStringFieldsBlocks,
@@ -139,8 +144,16 @@ export class ${meta.data.repositoryClassName} implements Repository<${model.type
139
144
 
140
145
  ${mainBlocks.updateCode}
141
146
 
147
+ ${mainBlocks.updateManyCode}
148
+
149
+ ${mainBlocks.upsertCode}
150
+
151
+ ${mainBlocks.upsertManyCode}
152
+
142
153
  ${mainBlocks.deleteCode}
143
154
 
155
+ ${mainBlocks.deleteManyCode}
156
+
144
157
  ${relationsBlocks.getterFunctions.join('\n')}
145
158
 
146
159
  ${maxLengthBlocks.ensureMaxLengthFunctions.join('\n')}
@@ -208,22 +221,24 @@ function _generateMainBuildingBlocks_InMemoryOnly({ model, meta, blocks, }) {
208
221
  return Promise.resolve()
209
222
  }`,
210
223
  reInitCode: `
211
- public async reInit(data: ${model.typeName}[]): Promise<void> {
224
+ public async reInit({items, execution}: ${methodTypeSignatures.createMany.parameters[0]}): Promise<void> {
212
225
  await this.init()
213
- await this.createMany(data)
226
+ await this.createMany({items, execution})
214
227
  }`,
215
228
  deleteAllCode: `
216
229
  public async deleteAll(): Promise<void> {
217
230
  return this.init()
218
231
  }`,
219
232
  createCode: `
220
- ${(0, jsdoc_1.jsDocComment)([
233
+ ${(0, jsdoc_1.toJsDocComment)([
221
234
  `Checks that item has the ${model.idField.name} field.`,
222
235
  `In case none exists, ${blocks.idBlocks.verifyFunctionComment}`,
223
236
  blocks.uniqueStringFieldsBlocks.verifyFunctionComment,
224
237
  blocks.maxLengthBlocks.verifyFunctionComment,
225
238
  ])}
226
- private verifyItem(item: ${blocks.idBlocks.verifyFunctionParameterType}): ${model.name} {
239
+ private verifyItem(
240
+ item: ${blocks.idBlocks.verifyFunctionParameterType}
241
+ ): ${model.name} {
227
242
  ${blocks.idBlocks.verifyCode}
228
243
 
229
244
  ${blocks.maxLengthBlocks.verifyCode.join('\n')}
@@ -239,61 +254,178 @@ function _generateMainBuildingBlocks_InMemoryOnly({ model, meta, blocks, }) {
239
254
  ${model.createdAtField ? `${model.createdAtField.sourceName}: new Date(),` : ''}
240
255
  ${model.updatedAtField ? `${model.updatedAtField.sourceName}: new Date(),` : ''}
241
256
  }
242
- }
243
-
257
+ }
258
+
259
+ ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.create.jsDoc)}
244
260
  // Non-mocked version is async - so we keep type-compatible signatures for create() and createWithId()
245
261
  // eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-unused-vars
246
262
  public async create(
247
- item: ${methodTypeSignatures.create.parameters[0]}
263
+ { item, execution }: ${methodTypeSignatures.create.parameters[0]}
248
264
  ): ${methodTypeSignatures.create.returnType} {
249
- const newItem = await Promise.resolve(this.verifyItem(item))
250
-
251
- this.set(newItem)
252
- return newItem
265
+ const mutationId = await execution.startCreateMutation({
266
+ model: '${meta.actions.actionScopeConstType}',
267
+ createObject: item
268
+ })
269
+
270
+ try {
271
+ const newItem = await Promise.resolve(this.verifyItem(item))
272
+
273
+ this.set(newItem)
274
+ await execution.finishCreateMutation({ mutationId, createdObject: newItem, entityId: newItem.id })
275
+ return newItem
276
+ } catch (error) {
277
+ await execution.errorMutation({ mutationId, error })
278
+ throw error
279
+ }
253
280
  }`,
254
281
  createManyCode: `
255
- public async createMany(items: ${blocks.idBlocks.verifyFunctionParameterType}[]) {
256
- const newItems = items.map((item) => this.verifyItem(item))
257
-
258
- await Promise.resolve()
282
+ ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.createMany.jsDoc)}
283
+ public async createMany(
284
+ { items, execution }: ${methodTypeSignatures.createMany.parameters[0]}
285
+ ): ${methodTypeSignatures.createMany.returnType} {
286
+ const mutationId = await execution.startCreateManyMutation({
287
+ model: '${meta.actions.actionScopeConstType}',
288
+ createObjects: items
289
+ })
259
290
 
260
- for (const item of newItems) {
261
- this.set(item)
291
+ try {
292
+ const newItems = items.map((item) => this.verifyItem(item))
293
+
294
+ await Promise.resolve()
295
+
296
+ for (const item of newItems) {
297
+ this.set(item)
298
+ }
299
+
300
+ await execution.finishCreateManyMutation({
301
+ mutationId,
302
+ createdObjects: newItems,
303
+ entityIds: newItems.map((i) => i.id),
304
+ })
305
+ return newItems
306
+ } catch (error) {
307
+ await execution.errorMutation({ mutationId, error })
308
+ throw error
262
309
  }
263
-
264
- return newItems
265
310
  }`,
266
- // prettier-ignore
267
311
  updateCode: `
268
- public async update(item: ${methodTypeSignatures.update.parameters[0]}): ${methodTypeSignatures.update.returnType} {
312
+ ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.update.jsDoc)}
313
+ public async update(
314
+ { item, execution }: ${methodTypeSignatures.update.parameters[0]}
315
+ ): ${methodTypeSignatures.update.returnType} {
269
316
  const existingItem = this.get(item.id)
270
317
 
271
318
  if (!existingItem) {
272
319
  throw new Error(\`Could not update ${meta.userFriendlyName} with id \${item.id}. Not found!\`)
273
320
  }
321
+ const mutationId = await execution.startUpdateMutation({
322
+ model: 'post',
323
+ entityId: item.id,
324
+ sourceObject: existingItem,
325
+ updateObject: item,
326
+ })
327
+ try {
328
+ ${blocks.maxLengthBlocks.updateCode.join('\n')}
274
329
 
275
- ${blocks.maxLengthBlocks.updateCode.join('\n')}
276
-
277
- ${blocks.uniqueStringFieldsBlocks.updateCode.join('\n')}
278
-
279
- const newItem = await Promise.resolve({ ...existingItem, ...item })
280
- ${model.updatedAtField ? `newItem.${model.updatedAtField.name} = new Date()` : ''}
281
-
282
- this.remove(existingItem)
283
- this.set(newItem)
284
-
285
- return newItem
330
+ ${blocks.uniqueStringFieldsBlocks.updateCode.join('\n')}
331
+
332
+ const newItem = await Promise.resolve({ ...existingItem, ...item })
333
+ ${model.updatedAtField ? `newItem.${model.updatedAtField.name} = new Date()` : ''}
334
+
335
+ this.remove(existingItem)
336
+ this.set(newItem)
337
+
338
+ await execution.finishUpdateMutation({ mutationId, updatedObject: newItem })
339
+ return newItem
340
+ } catch (error) {
341
+ await execution.errorMutation({ mutationId, error })
342
+ throw error
343
+ }
344
+ }`,
345
+ updateManyCode: `
346
+ ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.updateMany.jsDoc)}
347
+ public async updateMany(
348
+ { items, execution }: ${methodTypeSignatures.updateMany.parameters[0]}
349
+ ): ${methodTypeSignatures.updateMany.returnType} {
350
+ const result: ${model.typeName}[] = []
351
+ for (const item of items) {
352
+ try {
353
+ const updated = await this.update({ item, execution })
354
+ result.push(updated)
355
+ } catch {
356
+ /* empty */
357
+ }
358
+ }
359
+ return result
360
+ }`,
361
+ upsertCode: `
362
+ ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.upsert.jsDoc)}
363
+ public async upsert(
364
+ { item, execution }: ${methodTypeSignatures.upsert.parameters[0]}
365
+ ): ${methodTypeSignatures.upsert.returnType} {
366
+ const existingItem = item.${model.idField.name} ? this.get(item.${model.idField.name}) : null
367
+ if (existingItem) {
368
+ return this.update({ item: item as ${meta.types.dto.update}, execution })
369
+ } else {
370
+ return this.create({ item: item as ${meta.types.dto.create}, execution })
371
+ }
372
+ }`,
373
+ upsertManyCode: `
374
+ ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.upsertMany.jsDoc)}
375
+ public async upsertMany(
376
+ { items, execution }: ${methodTypeSignatures.upsertMany.parameters[0]}
377
+ ): ${methodTypeSignatures.upsertMany.returnType} {
378
+ const result: ${model.typeName}[] = []
379
+ for (const item of items) {
380
+ try {
381
+ const updated = await this.upsert({ item, execution })
382
+ result.push(updated)
383
+ } catch {
384
+ /* empty */
385
+ }
386
+ }
387
+ return result
286
388
  }`,
287
389
  deleteCode: `
288
- public async delete(id: ${methodTypeSignatures.delete.parameters[0]}): ${methodTypeSignatures.delete.returnType} {
390
+ ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.delete.jsDoc)}
391
+ public async delete(
392
+ { id, execution }: ${methodTypeSignatures.delete.parameters[0]}
393
+ ): ${methodTypeSignatures.delete.returnType} {
289
394
  const existingItem = this.get(id)
290
395
  if (!existingItem) {
291
396
  throw new Error(\`Could not delete ${model.typeName} with id \${id}. Not found!\`)
292
397
  }
293
-
294
- await Promise.resolve()
398
+ const mutationId = await execution.startDeleteMutation({
399
+ model: '${meta.actions.actionScopeConstType}',
400
+ entityId: id,
401
+ sourceObject: existingItem,
402
+ })
403
+ try {
404
+ await Promise.resolve()
295
405
 
296
- this.remove(existingItem)
406
+ this.remove(existingItem)
407
+ await execution.finishDeleteMutation({ mutationId })
408
+ return id
409
+ } catch (error) {
410
+ await execution.errorMutation({ mutationId, error })
411
+ throw error
412
+ }
413
+ }`,
414
+ deleteManyCode: `
415
+ ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.deleteMany.jsDoc)}
416
+ public async deleteMany(
417
+ { ids, execution }: ${methodTypeSignatures.deleteMany.parameters[0]}
418
+ ): ${methodTypeSignatures.deleteMany.returnType} {
419
+ const deletedIds: ${model.brandedIdType}[] = []
420
+ for (const id of ids) {
421
+ try {
422
+ await this.delete({ id, execution })
423
+ deletedIds.push(id)
424
+ } catch {
425
+ /* empty */
426
+ }
427
+ }
428
+ return deletedIds
297
429
  }`,
298
430
  databaseDecoderCode: ``,
299
431
  };
@@ -304,15 +436,10 @@ function _generateMainBuildingBlocks_InMemoryOnly({ model, meta, blocks, }) {
304
436
  function generateMainBuildingBlocks_InDatabase({ model, meta, imports, blocks, }) {
305
437
  const decoderFunctionName = meta.data.repository.decoderFnName;
306
438
  const { idField } = model;
307
- imports
308
- .addImport({ items: [meta.types.zodDecoderFnName], from: meta.types.importPath })
309
- .addImport({
310
- items: [`DbService`, `${model.sourceName} as DbType`].map(types_1.toTypeName),
311
- from: (0, types_1.toFileName)('@pxl/db'),
312
- })
313
- .addImport({
314
- items: [`format`, `pluralize`].map(types_1.toTypeName),
315
- from: (0, types_1.toFileName)('@pxl/common'),
439
+ imports.addImports({
440
+ [meta.types.importPath]: [meta.types.zodDecoderFnNames.fromDatabase],
441
+ [(0, types_1.toFileName)('@pxl/db')]: [`DbService`, `${model.sourceName} as DbType`].map(types_1.toTypeName),
442
+ [(0, types_1.toFileName)('@pxl/common')]: [`format`, `pluralize`].map(types_1.toTypeName),
316
443
  });
317
444
  const dbTableName = [model.sourceSchemaName, model.sourceName]
318
445
  .filter((s) => s != null)
@@ -348,7 +475,9 @@ function generateMainBuildingBlocks_InDatabase({ model, meta, imports, blocks, }
348
475
  ${blocks.indexBlocks.initLogCode.join('\n')}
349
476
  }`,
350
477
  reInitCode: `
351
- public async reInit(data: ${model.typeName}[]): Promise<void> {
478
+ public async reInit(
479
+ { items, execution }: ${methodTypeSignatures.createMany.parameters[0]}
480
+ ): Promise<void> {
352
481
  if (!this.db.useE2ETestDB) {
353
482
  const errorMsg =
354
483
  'ReInit() shall only be called in tests using MockRepositories or in DB configured for E2E tests!'
@@ -356,7 +485,7 @@ function generateMainBuildingBlocks_InDatabase({ model, meta, imports, blocks, }
356
485
  throw new Error(errorMsg)
357
486
  }
358
487
 
359
- await this.db.runOnlyOnTestDb(() => this.db.${meta.data.repository.getMethodFnName}.createMany({ data: data.map((i) => this.toCreateItem(i)) }))
488
+ await this.db.runOnlyOnTestDb(() => this.createMany({ items, execution }))
360
489
 
361
490
  return this.init()
362
491
  }`,
@@ -367,8 +496,9 @@ function generateMainBuildingBlocks_InDatabase({ model, meta, imports, blocks, }
367
496
  return this.init()
368
497
  }
369
498
  `,
499
+ // prettier-ignore
370
500
  createCode: `
371
- ${(0, jsdoc_1.jsDocComment)([
501
+ ${(0, jsdoc_1.toJsDocComment)([
372
502
  `Checks that item has the ${idField.name} field.`,
373
503
  `In case none exists, ${blocks.idBlocks.verifyFunctionComment}`,
374
504
  blocks.uniqueStringFieldsBlocks.verifyFunctionComment,
@@ -395,98 +525,218 @@ function generateMainBuildingBlocks_InDatabase({ model, meta, imports, blocks, }
395
525
  private toCreateItem(item: ${blocks.idBlocks.createFunctionParameterType}) {
396
526
  return {
397
527
  ${model.fields
398
- .filter((f) => !f.attributes.isReadonly)
528
+ .filter((f) => !f.attributes.isReadonly || f.kind === 'id')
399
529
  .map((f) => `${f.sourceName}: item.${f.name}`)
400
530
  .join(',\n')},
401
531
  }
402
532
  }
403
533
 
534
+ ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.create.jsDoc)}
404
535
  public async create(
405
- item: ${methodTypeSignatures.create.parameters[0]}
406
- ): ${methodTypeSignatures.create.returnType} {
407
- const newItem = this.${decoderFunctionName}(
408
- await this.db.${meta.data.repository.getMethodFnName}.create({
409
- data: this.toCreateItem(this.verifyItem(item)),
410
- }),
411
- )
536
+ { item, execution }: ${methodTypeSignatures.create.parameters[0]}
537
+ ): ${methodTypeSignatures.create.returnType} {
538
+ const mutationId = await execution.startCreateMutation({
539
+ model: '${meta.actions.actionScopeConstType}',
540
+ createObject: item
541
+ })
542
+
543
+ try {
544
+ const newItem = this.${decoderFunctionName}(
545
+ await this.db.${meta.data.repository.getMethodFnName}.create({
546
+ data: this.toCreateItem(this.verifyItem(item)),
547
+ }),
548
+ )
412
549
 
413
- this.set(newItem)
414
- return newItem
550
+ this.set(newItem)
551
+ await execution.finishCreateMutation({ mutationId, createdObject: newItem, entityId: newItem.id })
552
+ return newItem
553
+ } catch (error) {
554
+ await execution.errorMutation({ mutationId, error })
555
+ throw error
556
+ }
415
557
  }`,
558
+ // prettier-ignore
416
559
  createManyCode: `
417
- public async createMany(items: ${blocks.idBlocks.verifyFunctionParameterType}[]) {
418
- const newItems = items.map((item) => this.verifyItem(item))
419
-
420
- await this.db.${meta.data.repository.getMethodFnName}.createMany({ data: newItems.map(i => this.toCreateItem(i)) })
421
-
422
- const dbItems = await this.db.${meta.data.repository.getMethodFnName}.findMany({
423
- where: {
424
- ${model.idField.sourceName}: { in: newItems.map(i => i.${model.idField.name}) }
425
- }
560
+ ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.createMany.jsDoc)}
561
+ public async createMany(
562
+ {items, execution}: ${methodTypeSignatures.createMany.parameters[0]}
563
+ ): ${methodTypeSignatures.createMany.returnType} {
564
+ const mutationId = await execution.startCreateManyMutation({
565
+ model: '${meta.actions.actionScopeConstType}',
566
+ createObjects: items
426
567
  })
568
+
569
+ try {
570
+ const newItems = items.map((item) => this.verifyItem(item))
571
+
572
+ await this.db.${meta.data.repository.getMethodFnName}.createMany({ data: newItems.map(i => this.toCreateItem(i)) })
573
+
574
+ const dbItems = await this.db.${meta.data.repository.getMethodFnName}.findMany({
575
+ where: {
576
+ ${model.idField.sourceName}: { in: newItems.map(i => i.${model.idField.name}) }
577
+ }
578
+ })
579
+
580
+ const result = dbItems.map((item) => this.${decoderFunctionName}(item))
581
+
582
+ for (const item of result) {
583
+ this.set(item)
584
+ }
427
585
 
428
- const result = dbItems.map((item) => this.${decoderFunctionName}(item))
429
-
430
- for (const item of result) {
431
- this.set(item)
586
+ await execution.finishCreateManyMutation({
587
+ mutationId,
588
+ createdObjects: result,
589
+ entityIds: newItems.map((i) => i.id),
590
+ })
591
+ return result
592
+ } catch (error) {
593
+ await execution.errorMutation({ mutationId, error })
594
+ throw error
432
595
  }
433
-
434
- return result
435
596
  }`,
436
- // prettier-ignore
437
597
  updateCode: `
438
- public async update(item: ${methodTypeSignatures.update.parameters[0]}): ${methodTypeSignatures.update.returnType} {
598
+ ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.update.jsDoc)}
599
+ public async update(
600
+ { item, execution }: ${methodTypeSignatures.update.parameters[0]}
601
+ ): ${methodTypeSignatures.update.returnType} {
439
602
  const existingItem = this.get(item.${idField.name})
440
603
  if (!existingItem) {
441
604
  throw new Error(\`Could not update ${meta.userFriendlyName} with id \${item.id}. Not found!\`)
442
605
  }
443
606
 
444
- ${blocks.maxLengthBlocks.updateCode.join('\n')}
445
-
446
- ${blocks.uniqueStringFieldsBlocks.updateCode.join('\n')}
447
-
448
- const newItem = this.${decoderFunctionName}(
449
- await this.db.${meta.data.repository.getMethodFnName}.update({
450
- where: {
451
- ${idField.sourceName}: item.${idField.name},
452
- },
453
- data: {
454
- ${Array.from(model.fields.values())
607
+ const mutationId = await execution.startUpdateMutation({
608
+ model: 'post',
609
+ entityId: item.id,
610
+ sourceObject: existingItem,
611
+ updateObject: item,
612
+ })
613
+ try {
614
+ ${blocks.maxLengthBlocks.updateCode.join('\n')}
615
+
616
+ ${blocks.uniqueStringFieldsBlocks.updateCode.join('\n')}
617
+
618
+ const newItem = this.${decoderFunctionName}(
619
+ await this.db.${meta.data.repository.getMethodFnName}.update({
620
+ where: {
621
+ ${idField.sourceName}: item.${idField.name},
622
+ },
623
+ data: {
624
+ ${[...model.fields.values()]
455
625
  .filter((f) => f.kind !== 'id' && !f.attributes.isReadonly)
456
626
  .map((f) => f.isRequired
457
627
  ? `${f.sourceName}: item.${f.name} ?? existingItem.${f.name}`
458
628
  : `${f.sourceName}: item.${f.name}`)
459
629
  .join(',\n')}
460
- },
461
- }),
462
- )
463
-
464
- this.remove(existingItem)
465
- this.set(newItem)
466
-
467
- return newItem
630
+ },
631
+ }),
632
+ )
633
+
634
+ this.remove(existingItem)
635
+ this.set(newItem)
636
+
637
+ await execution.finishUpdateMutation({ mutationId, updatedObject: newItem })
638
+ return newItem
639
+ } catch (error) {
640
+ await execution.errorMutation({ mutationId, error })
641
+ throw error
642
+ }
643
+ }`,
644
+ updateManyCode: `
645
+ ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.updateMany.jsDoc)}
646
+ public async updateMany(
647
+ { items, execution }: ${methodTypeSignatures.updateMany.parameters[0]}
648
+ ): ${methodTypeSignatures.updateMany.returnType} {
649
+ const result: ${model.typeName}[] = []
650
+ for (const item of items) {
651
+ try {
652
+ const updated = await this.update({ item, execution })
653
+ result.push(updated)
654
+ } catch {
655
+ /* empty */
656
+ }
657
+ }
658
+ return result
659
+ }`,
660
+ upsertCode: `
661
+ ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.upsert.jsDoc)}
662
+ public async upsert(
663
+ { item, execution }: ${methodTypeSignatures.upsert.parameters[0]}
664
+ ): ${methodTypeSignatures.upsert.returnType} {
665
+ const existingItem = item.${model.idField.name} ? this.get(item.${model.idField.name}) : null
666
+ if (existingItem) {
667
+ return this.update({ item: item as ${meta.types.dto.update}, execution })
668
+ } else {
669
+ return this.create({ item: item as ${meta.types.dto.create}, execution })
670
+ }
671
+ }`,
672
+ upsertManyCode: `
673
+ ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.upsertMany.jsDoc)}
674
+ public async upsertMany(
675
+ { items, execution }: ${methodTypeSignatures.upsertMany.parameters[0]}
676
+ ): ${methodTypeSignatures.upsertMany.returnType} {
677
+ const result: ${model.typeName}[] = []
678
+ for (const item of items) {
679
+ try {
680
+ const updated = await this.upsert({ item, execution })
681
+ result.push(updated)
682
+ } catch {
683
+ /* empty */
684
+ }
685
+ }
686
+ return result
468
687
  }`,
469
688
  deleteCode: `
470
- public async delete(id: ${methodTypeSignatures.delete.parameters[0]}): ${methodTypeSignatures.delete.returnType} {
689
+ ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.delete.jsDoc)}
690
+ public async delete(
691
+ { id, execution }: ${methodTypeSignatures.delete.parameters[0]}
692
+ ): ${methodTypeSignatures.delete.returnType} {
471
693
  const existingItem = this.get(id)
472
694
  if (!existingItem) {
473
695
  throw new Error(\`Could not delete ${model.typeName} with id \${id}. Not found!\`)
474
696
  }
475
697
 
476
- await this.db.${meta.data.repository.getMethodFnName}.delete({ where: { ${idField.sourceName}:id } })
477
-
478
- this.remove(existingItem)
698
+ const mutationId = await execution.startDeleteMutation({
699
+ model: '${meta.actions.actionScopeConstType}',
700
+ entityId: id,
701
+ sourceObject: existingItem,
702
+ })
703
+ try {
704
+
705
+ await this.db.${meta.data.repository.getMethodFnName}.delete({ where: { ${idField.sourceName}:id } })
706
+
707
+ this.remove(existingItem)
708
+ await execution.finishDeleteMutation({ mutationId })
709
+ return id
710
+ } catch (error) {
711
+ await execution.errorMutation({ mutationId, error })
712
+ throw error
713
+ }
714
+ }`,
715
+ deleteManyCode: `
716
+ ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.deleteMany.jsDoc)}
717
+ public async deleteMany(
718
+ { ids, execution }: ${methodTypeSignatures.deleteMany.parameters[0]}
719
+ ): ${methodTypeSignatures.deleteMany.returnType} {
720
+ const deletedIds: ${model.brandedIdType}[] = []
721
+ for (const id of ids) {
722
+ try {
723
+ await this.delete({ id, execution })
724
+ deletedIds.push(id)
725
+ } catch {
726
+ /* empty */
727
+ }
728
+ }
729
+ return deletedIds
479
730
  }`,
480
- // prettier-ignore
481
731
  databaseDecoderCode: `
482
732
  /**
483
733
  * Utility function that converts a given Database object to a TypeScript model instance
484
734
  */
485
735
  private ${decoderFunctionName}(
486
- item: Pick<DbType, ${Array.from(model.fields.values()).map((f) => `'${f.sourceName}'`).join(' | ')}>
736
+ item: Pick<DbType, ${model.fields.map((f) => `'${f.sourceName}'`).join(' | ')}>
487
737
  ): ${model.typeName} {
488
- return ${meta.types.zodDecoderFnName}.parse({
489
- ${Array.from(model.fields.values()).map((f) => `${f.name}: item.${f.sourceName}`).join(',\n')}
738
+ return ${meta.types.zodDecoderFnNames.fromDatabase}.parse({
739
+ ${model.fields.map((f) => `${f.name}: item.${f.sourceName}`).join(',\n')}
490
740
  })
491
741
  }`,
492
742
  };
@@ -514,19 +764,47 @@ function generateIdBlocks({ model, meta }) {
514
764
  // NOTE: We export this function as an interface simplification to the repository generator. Internally, we
515
765
  // use the same functions as the ones used in this function which we don't want to expose.
516
766
  function getRepositoryMethodsTypeSignatures({ model, meta }) {
517
- const { verifyFunctionParameterType } = generateIdBlocks({ model, meta });
767
+ const schemaMeta = (0, meta_1.getSchemaMetadata)({ config: model.schemaConfig });
518
768
  return {
519
769
  create: {
520
- parameters: [verifyFunctionParameterType],
770
+ jsDoc: [`Creates a new ${meta.userFriendlyName} and returns it.`],
771
+ parameters: [`{item: ${meta.types.dto.create}, execution: ${schemaMeta.actions.actionExecutionInterface}}`],
521
772
  returnType: `Promise<${model.typeName}>`,
522
773
  },
774
+ createMany: {
775
+ jsDoc: [`Creates multiple new ${meta.userFriendlyNamePlural} and returns them.`],
776
+ parameters: [`{items: ${meta.types.dto.create}[], execution: ${schemaMeta.actions.actionExecutionInterface}}`],
777
+ returnType: `Promise<${model.typeName}[]>`,
778
+ },
523
779
  update: {
524
- parameters: [`Partial<${model.typeName}> & { ${model.idField.name}: ${model.brandedIdType} }`],
780
+ jsDoc: [`Updates a ${meta.userFriendlyName} and returns it.`],
781
+ parameters: [`{item: ${meta.types.dto.update}, execution: ${schemaMeta.actions.actionExecutionInterface}}`],
782
+ returnType: `Promise<${model.typeName}>`,
783
+ },
784
+ updateMany: {
785
+ jsDoc: [`Updates multiple ${meta.userFriendlyNamePlural} and returns them.`],
786
+ parameters: [`{items: ${meta.types.dto.update}[], execution: ${schemaMeta.actions.actionExecutionInterface}}`],
787
+ returnType: `Promise<${model.typeName}[]>`,
788
+ },
789
+ upsert: {
790
+ jsDoc: [`Creates or updates a ${meta.userFriendlyName} and returns it.`],
791
+ parameters: [`{item: ${meta.types.dto.upsert}, execution: ${schemaMeta.actions.actionExecutionInterface}}`],
525
792
  returnType: `Promise<${model.typeName}>`,
526
793
  },
794
+ upsertMany: {
795
+ jsDoc: [`Creates or updates multiple ${meta.userFriendlyNamePlural} and returns them.`],
796
+ parameters: [`{items: ${meta.types.dto.upsert}[], execution: ${schemaMeta.actions.actionExecutionInterface}}`],
797
+ returnType: `Promise<${model.typeName}[]>`,
798
+ },
527
799
  delete: {
528
- parameters: [model.brandedIdType],
529
- returnType: `Promise<void>`,
800
+ jsDoc: [`Deletes a ${meta.userFriendlyName} and returns its id.`],
801
+ parameters: [`{id: ${model.brandedIdType}, execution: ${schemaMeta.actions.actionExecutionInterface}}`],
802
+ returnType: `Promise<${model.brandedIdType}>`,
803
+ },
804
+ deleteMany: {
805
+ jsDoc: [`Deletes multiple ${meta.userFriendlyNamePlural} and returns their ids.`],
806
+ parameters: [`{ids: ${model.brandedIdType}[], execution: ${schemaMeta.actions.actionExecutionInterface}}`],
807
+ returnType: `Promise<${model.brandedIdType}[]>`,
530
808
  },
531
809
  };
532
810
  }
@@ -541,8 +819,7 @@ function _generateIdBlocks_NoGeneration({ idField, model, meta, }) {
541
819
  generateNextIdFunctionName: '',
542
820
  initCode: '',
543
821
  verifyFunctionComment: `an error is thrown as field has no default setting in schema.`,
544
- // TODO: This creates a bug!
545
- verifyFunctionParameterType: model.typeName,
822
+ verifyFunctionParameterType: meta.types.dto.create,
546
823
  verifyCode: `
547
824
  if (item.${idField.name} === undefined) {
548
825
  throw new Error('Id field ${idField.name} is required!')
@@ -558,7 +835,7 @@ function _generateIdBlocks_NoGeneration({ idField, model, meta, }) {
558
835
  */
559
836
  function _generateIdBlock_Int({ idField, model, meta, }) {
560
837
  const generatedFields = model.fields.filter((f) => f.kind === 'id' || f.attributes.isReadonly);
561
- const readonlyFields = model.fields.filter((f) => f.attributes.isReadonly);
838
+ const readonlyFields = model.fields.filter((f) => f.attributes.isReadonly && f.kind !== 'id');
562
839
  return {
563
840
  libraryImports: '',
564
841
  generateNextIdFunctionName: `
@@ -585,7 +862,7 @@ function _generateIdBlock_Int({ idField, model, meta, }) {
585
862
  */
586
863
  function _generateIdBlock_UUID({ idField, model, meta, }) {
587
864
  const dbGeneratedFields = model.fields.filter((f) => f.kind === 'id' || f.attributes.isReadonly);
588
- const readonlyFields = model.fields.filter((f) => f.attributes.isReadonly);
865
+ const readonlyFields = model.fields.filter((f) => f.attributes.isReadonly && f.kind !== 'id');
589
866
  return {
590
867
  libraryImports: `import { randomUUID } from 'crypto'`,
591
868
  generateNextIdFunctionName: `
@@ -673,8 +950,12 @@ function generateUniqueFieldsBlocks({ model }) {
673
950
  * Utility function that ensures that the ${f.name} field is unique
674
951
  */
675
952
  private ${getEnsureUniqueFnName(f)}(item: { ${f.name}?: string }) {
676
- if (!item.${f.name}) return
677
- if (!this.uniqueIds.${f.name}.has(item.${f.name})) return
953
+ if (!item.${f.name}) {
954
+ return
955
+ }
956
+ if (!this.uniqueIds.${f.name}.has(item.${f.name})) {
957
+ return
958
+ }
678
959
  let counter = 1
679
960
 
680
961
  let ${f.name}: string
@@ -813,23 +1094,31 @@ function generateRelationsBlocks({ model, imports, }) {
813
1094
  from: relationModelMeta.types.importPath,
814
1095
  });
815
1096
  result.mapDeclarations.push(`protected ${r.name}Map: Map<${relationModelMeta.types.brandedIdType}, Map<${model.brandedIdType}, ${model.typeName}>> = new Map()`);
816
- // prettier-ignore
817
1097
  result.getterFunctions.push(`
818
1098
  /**
819
1099
  * Function to retrieve all ${(0, string_1.pluralize)(model.name)} that are related to a ${r.name}
820
1100
  */
821
- public ${fieldMeta.getByForeignKeyMethodFnName}(id: ${relationModelMeta.types.brandedIdType}): Map<${model.brandedIdType}, ${model.typeName}> {
1101
+ public ${fieldMeta.getByForeignKeyMethodFnName}(
1102
+ id: ${relationModelMeta.types.brandedIdType}
1103
+ ): Map<${model.brandedIdType}, ${model.typeName}> {
822
1104
  const result = this.${r.name}Map.get(id)
823
- if (!result) return new Map()
1105
+ if (!result) {
1106
+ return new Map()
1107
+ }
824
1108
  return new Map(result)
825
1109
  }
826
1110
 
827
1111
  /**
828
1112
  * Function to retrieve all ${model.brandedIdType}s that are related to a ${r.name}
829
1113
  */
830
- public ${fieldMeta.getByForeignKeyIdsMethodFnName}(id: ${relationModelMeta.types.brandedIdType}): ${model.brandedIdType}[] {
1114
+ public ${fieldMeta.getByForeignKeyIdsMethodFnName}(
1115
+ id: ${relationModelMeta.types.brandedIdType}
1116
+ ): ${model.brandedIdType}[] {
831
1117
  const s = this.${r.name}Map.get(id)
832
- if (!s) return []
1118
+ if (!s) {
1119
+ return []
1120
+ }
1121
+
833
1122
  return Array.from(s.keys())
834
1123
  }`);
835
1124
  result.setCode.push(`