@postxl/generator 0.68.0 → 0.69.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 (58) hide show
  1. package/dist/generator.js +34 -43
  2. package/dist/generators/indices/{datamodule.generator.d.ts → data/module.generator.d.ts} +2 -2
  3. package/dist/generators/indices/{datamodule.generator.js → data/module.generator.js} +4 -5
  4. package/dist/generators/indices/{dataservice.generator.d.ts → data/service.generator.d.ts} +2 -2
  5. package/dist/generators/indices/{dataservice.generator.js → data/service.generator.js} +5 -5
  6. package/dist/generators/indices/{data-types.generator.d.ts → data/types.generator.d.ts} +2 -2
  7. package/dist/generators/indices/{data-types.generator.js → data/types.generator.js} +2 -2
  8. package/dist/generators/indices/{importexport-convert-import-functions.generator.d.ts → import-export/importexport-convert-import-functions.generator.d.ts} +2 -2
  9. package/dist/generators/indices/{importexport-convert-import-functions.generator.js → import-export/importexport-convert-import-functions.generator.js} +4 -26
  10. package/dist/generators/indices/{importexport-exporter-class.generator.d.ts → import-export/importexport-exporter-class.generator.d.ts} +2 -2
  11. package/dist/generators/indices/{importexport-exporter-class.generator.js → import-export/importexport-exporter-class.generator.js} +4 -5
  12. package/dist/generators/indices/{importexport-import-service.generator.d.ts → import-export/importexport-import-service.generator.d.ts} +2 -2
  13. package/dist/generators/indices/{importexport-import-service.generator.js → import-export/importexport-import-service.generator.js} +4 -4
  14. package/dist/generators/indices/{importexport-types.generator.d.ts → import-export/importexport-types.generator.d.ts} +2 -2
  15. package/dist/generators/indices/{importexport-types.generator.js → import-export/importexport-types.generator.js} +2 -2
  16. package/dist/generators/indices/types.generator.js +13 -1
  17. package/dist/generators/indices/{businesslogic-actiontypes.generator.d.ts → update/actiontypes.generator.d.ts} +2 -2
  18. package/dist/generators/indices/{businesslogic-actiontypes.generator.js → update/actiontypes.generator.js} +3 -3
  19. package/dist/generators/indices/{businesslogic-update-module.generator.d.ts → update/module.generator.d.ts} +2 -2
  20. package/dist/generators/indices/{businesslogic-update-module.generator.js → update/module.generator.js} +2 -2
  21. package/dist/generators/indices/{businesslogic-update-service.generator.d.ts → update/service.generator.d.ts} +2 -2
  22. package/dist/generators/indices/{businesslogic-update-service.generator.js → update/service.generator.js} +2 -2
  23. package/dist/generators/indices/{businesslogic-view-module.generator.d.ts → view/module.generator.d.ts} +2 -2
  24. package/dist/generators/indices/{businesslogic-view-module.generator.js → view/module.generator.js} +2 -2
  25. package/dist/generators/indices/{businesslogic-view-service.generator.d.ts → view/service.generator.d.ts} +2 -2
  26. package/dist/generators/indices/{businesslogic-view-service.generator.js → view/service.generator.js} +2 -2
  27. package/dist/generators/models/repository.generator.d.ts +0 -46
  28. package/dist/generators/models/repository.generator.js +348 -796
  29. package/dist/generators/models/route.generator.js +2 -2
  30. package/dist/generators/models/types.generator.js +27 -2
  31. package/dist/generators/models/{businesslogic-update.generator.d.ts → update/service.generator.d.ts} +2 -2
  32. package/dist/generators/models/update/service.generator.js +252 -0
  33. package/dist/generators/models/{businesslogic-view.generator.d.ts → view/service.generator.d.ts} +2 -2
  34. package/dist/generators/models/{businesslogic-view.generator.js → view/service.generator.js} +12 -18
  35. package/dist/lib/attributes.d.ts +0 -5
  36. package/dist/lib/meta.d.ts +16 -54
  37. package/dist/lib/meta.js +2 -18
  38. package/dist/prisma/attributes.js +0 -2
  39. package/package.json +1 -1
  40. package/dist/generators/indices/datamock-module.generator.d.ts +0 -9
  41. package/dist/generators/indices/datamock-module.generator.js +0 -64
  42. package/dist/generators/indices/datamocker.generator.d.ts +0 -9
  43. package/dist/generators/indices/datamocker.generator.js +0 -88
  44. package/dist/generators/indices/emptydatabasemigration.generator.d.ts +0 -11
  45. package/dist/generators/indices/emptydatabasemigration.generator.js +0 -34
  46. package/dist/generators/indices/testdata-service.generator.d.ts +0 -9
  47. package/dist/generators/indices/testdata-service.generator.js +0 -84
  48. package/dist/generators/models/businesslogic-update.generator.js +0 -324
  49. /package/dist/generators/models/{react.generator → react}/context.generator.d.ts +0 -0
  50. /package/dist/generators/models/{react.generator → react}/context.generator.js +0 -0
  51. /package/dist/generators/models/{react.generator → react}/index.d.ts +0 -0
  52. /package/dist/generators/models/{react.generator → react}/index.js +0 -0
  53. /package/dist/generators/models/{react.generator → react}/library.generator.d.ts +0 -0
  54. /package/dist/generators/models/{react.generator → react}/library.generator.js +0 -0
  55. /package/dist/generators/models/{react.generator → react}/lookup.generator.d.ts +0 -0
  56. /package/dist/generators/models/{react.generator → react}/lookup.generator.js +0 -0
  57. /package/dist/generators/models/{react.generator → react}/modals.generator.d.ts +0 -0
  58. /package/dist/generators/models/{react.generator → react}/modals.generator.js +0 -0
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getRepositoryMethodsTypeSignatures = exports.generateMockRepository = exports.generateRepository = void 0;
3
+ exports.generateRepository = void 0;
4
4
  const imports_1 = require("../../lib/imports");
5
5
  const meta_1 = require("../../lib/meta");
6
6
  const fields_1 = require("../../lib/schema/fields");
@@ -21,29 +21,13 @@ function generateRepository({ model, meta }) {
21
21
  (0, types_1.toAnnotatedTypeName)(model.typeName),
22
22
  (0, types_1.toAnnotatedTypeName)(model.brandedIdType),
23
23
  meta.types.toBrandedIdTypeFnName,
24
- (0, types_1.toAnnotatedTypeName)(meta.types.dto.create),
25
- (0, types_1.toAnnotatedTypeName)(meta.types.dto.update),
26
- (0, types_1.toAnnotatedTypeName)(meta.types.dto.upsert),
24
+ meta.types.dto.create,
25
+ meta.types.dto.update,
26
+ meta.types.dto.upsert,
27
+ meta.types.dto.clone,
27
28
  ],
28
29
  [schemaMeta.actions.execution.interfaceLocation.import]: [schemaMeta.actions.execution.interface],
29
30
  });
30
- // NOTE: We first generate different parts of the code responsible for a particular task
31
- // and then we combine them into the final code block.
32
- //
33
- // Based on the model, the repository is generated slightly differently:
34
- // 1.) if the model has a default field, the repository will have a public variable "defaultValue"
35
- // that is set and verified during init,
36
- // 2.) if the model has a generated id, the repository will have a function "generateNextId" that
37
- // is used to generate the next id. The `create` and `createMany` functions will use this function
38
- // and allow being called with an id,
39
- // 3.) unique string fields are ensured to be unique,
40
- // 4.) max length string fields are ensured to not exceed their max length,
41
- // 5.) index for fields marked with index or unique attribute,
42
- // 6.) relations are indexed by the foreign key.
43
- //
44
- // The repository is generated differently based on whether the model is stored in the database or in memory.
45
- // If the model is stored in the database, all CUD operations will result in a database call. If the model is
46
- // stored in memory, all CUD operations will be performed in memory without any database calls.
47
31
  const idBlocks = generateIdBlocks({ model, meta });
48
32
  const defaultValueBlocks = generateDefaultBlocks({ model, meta });
49
33
  const uniqueStringFieldsBlocks = generateUniqueFieldsBlocks({ model, meta });
@@ -51,43 +35,15 @@ function generateRepository({ model, meta }) {
51
35
  const validationBlocks = generateValidationBlocks({ model, meta });
52
36
  const indexBlocks = generateIndexBlocks({ model, schemaMeta, meta, imports });
53
37
  const relationsBlocks = generateRelationsBlocks({ model, meta, imports });
54
- let mainBlocks;
55
- if (model.attributes.inMemoryOnly) {
56
- mainBlocks = _generateMainBuildingBlocks_InMemoryOnly({
57
- model,
58
- meta,
59
- imports,
60
- blocks: {
61
- uniqueStringFieldsBlocks,
62
- relationsBlocks,
63
- defaultValueBlocks,
64
- idBlocks,
65
- indexBlocks,
66
- maxLengthBlocks,
67
- validationBlocks,
68
- },
69
- });
70
- }
71
- else {
72
- mainBlocks = generateMainBuildingBlocks_InDatabase({
73
- model,
74
- meta,
75
- schemaMeta,
76
- imports,
77
- blocks: {
78
- uniqueStringFieldsBlocks,
79
- relationsBlocks,
80
- defaultValueBlocks,
81
- idBlocks,
82
- indexBlocks,
83
- maxLengthBlocks,
84
- validationBlocks,
85
- },
86
- });
87
- }
38
+ imports.addImports({
39
+ [meta.types.importPath]: [meta.types.zodDecoderFnNames.fromDatabase],
40
+ [schemaMeta.backendModules.db.databaseService.location.import]: [schemaMeta.backendModules.db.databaseService.name],
41
+ [schemaMeta.backendModules.db.typesImportPath]: [(0, types_1.toAnnotatedTypeName)((0, types_1.toTypeName)(`${model.sourceName} as DbType`))],
42
+ [(0, types_1.toPackageName)('@postxl/runtime')]: [(0, types_1.toFunctionName)('format'), (0, types_1.toFunctionName)('pluralize')],
43
+ });
44
+ const userRepositorySpecificBlocks = generateUserRepositorySpecificBlocks_InDatabase({ model, meta, imports });
88
45
  return `
89
46
  import { Injectable, Logger } from '@nestjs/common'
90
-
91
47
  ${idBlocks.libraryImports}
92
48
  ${imports.generate()}
93
49
 
@@ -108,19 +64,43 @@ export class ${meta.data.repository.className} implements Repository<${model.typ
108
64
 
109
65
  ${indexBlocks.nestedMapDeclarations.join('\n')}
110
66
 
111
- ${mainBlocks.userRepositorySpecificBlocks.rootUserNameConst}
67
+ ${userRepositorySpecificBlocks.rootUserNameConst}
112
68
 
113
- ${mainBlocks.userRepositorySpecificBlocks.getterBlock}
69
+ ${userRepositorySpecificBlocks.getterBlock}
114
70
 
115
- ${mainBlocks.constructorCode}
71
+ constructor(protected db: ${schemaMeta.backendModules.db.databaseService.name}) {}
116
72
 
117
- ${mainBlocks.initCode}
73
+ public async init() {
74
+ this.data.clear()
75
+
76
+ ${relationsBlocks.clearCode.join('\n')}
77
+
78
+ ${uniqueStringFieldsBlocks.clearCode.join('\n')}
79
+
80
+ ${defaultValueBlocks.init.resetCode}
81
+
82
+ ${indexBlocks.initCode.join('\n')}
83
+
84
+ const data = await this.db.${meta.data.repository.getMethodFnName}.findMany({})
118
85
 
119
- ${mainBlocks.reInitCode}
86
+ for (const rawItem of data) {
87
+ const item = this.${meta.data.repository.decoderFnName}(rawItem)
88
+ this.set(item)
120
89
 
121
- ${mainBlocks.deleteAllCode}
90
+ ${defaultValueBlocks.init.setCode}
91
+ }
92
+
93
+ ${idBlocks.initCode}
94
+
95
+ ${defaultValueBlocks.init.checkCode}
96
+
97
+ ${userRepositorySpecificBlocks.initCall}
98
+
99
+ this.logger.log(\`\${format(this.data.size)} \${pluralize('${model.typeName}', this.data.size)} loaded\`)
100
+ ${indexBlocks.initLogCode.join('\n')}
101
+ }
122
102
 
123
- ${mainBlocks.userRepositorySpecificBlocks.rootUserInitializeBlock}
103
+ ${userRepositorySpecificBlocks.rootUserInitializeBlock}
124
104
 
125
105
  // NOTE: The current implementation is synchronous, but it needs to be async to conform to the interface.
126
106
  // eslint-disable-next-line @typescript-eslint/require-await
@@ -161,21 +141,254 @@ export class ${meta.data.repository.className} implements Repository<${model.typ
161
141
  return this.data.size
162
142
  }
163
143
 
164
- ${mainBlocks.createCode}
144
+ ${(0, jsdoc_1.toJsDocComment)([
145
+ `Checks that item has the ${idField.name} field.`,
146
+ `In case none exists, ${idBlocks.verifyFunctionComment}`,
147
+ uniqueStringFieldsBlocks.verifyFunctionComment,
148
+ maxLengthBlocks.verifyFunctionComment,
149
+ ])}
150
+ private verifyItem(
151
+ item: ${idBlocks.verifyFunctionParameterType}
152
+ ): ${idBlocks.createFunctionParameterType} {
153
+ ${idBlocks.verifyCode}
154
+
155
+ ${maxLengthBlocks.verifyCode.join('\n')}
156
+
157
+ ${uniqueStringFieldsBlocks.verifyCode.join('\n')}
158
+
159
+ ${validationBlocks.verifyCode.join('\n')}
160
+
161
+ return {
162
+ ${idField.name},
163
+ ${model.fields
164
+ .filter((f) => f.kind !== 'id' && !f.attributes.isReadonly)
165
+ .map((f) => `${f.name}: item.${f.name}`)
166
+ .join(',\n')}
167
+ }
168
+ }
169
+
170
+ private toCreateItem(item: ${idBlocks.createFunctionParameterType}) {
171
+ return {
172
+ ${model.fields
173
+ .filter((f) => !f.attributes.isReadonly || f.kind === 'id')
174
+ .map((f) => `${f.sourceName}: item.${f.name}`)
175
+ .join(',\n')},
176
+ }
177
+ }
178
+
179
+ ${(0, jsdoc_1.toJsDocComment)([`Creates a new ${meta.userFriendlyName} and returns it.`])}
180
+ public async create(
181
+ { item, execution }: { item: ${meta.types.dto.create}, execution: ${schemaMeta.actions.execution.interface} }
182
+ ): Promise<${model.typeName}> {
183
+ const mutationId = await execution.startCreateMutation({
184
+ model: '${meta.actions.actionScopeConstType}',
185
+ createObject: item
186
+ })
187
+
188
+ try {
189
+ const newItem = this.${meta.data.repository.decoderFnName}(
190
+ await this.db.${meta.data.repository.getMethodFnName}.create({
191
+ data: this.toCreateItem(this.verifyItem(item)),
192
+ }),
193
+ )
194
+
195
+ this.set(newItem)
196
+ await execution.finishCreateMutation({ mutationId, createdObject: newItem, entityId: newItem.id })
197
+ return newItem
198
+ } catch (error) {
199
+ await execution.errorMutation({ mutationId, error })
200
+ throw error
201
+ }
202
+ }
203
+
204
+ ${(0, jsdoc_1.toJsDocComment)([`Creates multiple new ${meta.userFriendlyNamePlural} and returns them.`])}
205
+ public async createMany(
206
+ {items, execution}: { items: ${meta.types.dto.create}[], execution: ${schemaMeta.actions.execution.interface} }
207
+ ): Promise<${model.typeName}[]> {
208
+ const mutationId = await execution.startCreateManyMutation({
209
+ model: '${meta.actions.actionScopeConstType}',
210
+ createObjects: items
211
+ })
212
+
213
+ try {
214
+ const newItems = items.map((item) => this.verifyItem(item))
215
+
216
+ await this.db.${meta.data.repository.getMethodFnName}.createMany({ data: newItems.map(i => this.toCreateItem(i)) })
217
+
218
+ const dbItems = await this.db.${meta.data.repository.getMethodFnName}.findMany({
219
+ where: {
220
+ ${model.idField.sourceName}: { in: newItems.map(i => i.${model.idField.name}) }
221
+ }
222
+ })
223
+
224
+ const result = dbItems.map((item) => this.${meta.data.repository.decoderFnName}(item))
225
+
226
+ for (const item of result) {
227
+ this.set(item)
228
+ }
165
229
 
166
- ${mainBlocks.createManyCode}
230
+ await execution.finishCreateManyMutation({
231
+ mutationId,
232
+ createdObjects: result,
233
+ entityIds: newItems.map((i) => i.id),
234
+ })
235
+ return result
236
+ } catch (error) {
237
+ await execution.errorMutation({ mutationId, error })
238
+ throw error
239
+ }
240
+ }
241
+
242
+ ${(0, jsdoc_1.toJsDocComment)([`Updates a ${meta.userFriendlyName} and returns it.`])}
243
+ public async update(
244
+ { item, execution }: { item: ${meta.types.dto.update}, execution: ${schemaMeta.actions.execution.interface} }
245
+ ): Promise<${model.typeName}> {
246
+ const existingItem = await this.get(item.${idField.name})
247
+ if (!existingItem) {
248
+ throw new Error(\`Could not update ${meta.userFriendlyName} with id \${item.id}. Not found!\`)
249
+ }
250
+
251
+ const mutationId = await execution.startUpdateMutation({
252
+ model: '${meta.actions.actionScopeConstType}',
253
+ entityId: item.id,
254
+ sourceObject: existingItem,
255
+ updateObject: item,
256
+ })
257
+ try {
258
+ ${maxLengthBlocks.updateCode.join('\n')}
167
259
 
168
- ${mainBlocks.updateCode}
260
+ ${uniqueStringFieldsBlocks.updateCode.join('\n')}
261
+
262
+ const newItem = this.${meta.data.repository.decoderFnName}(
263
+ await this.db.${meta.data.repository.getMethodFnName}.update({
264
+ where: {
265
+ ${idField.sourceName}: item.${idField.name},
266
+ },
267
+ data: {
268
+ ${[...model.fields.values()]
269
+ .filter((f) => f.kind !== 'id' && !f.attributes.isReadonly)
270
+ .map((f) => f.isRequired
271
+ ? `${f.sourceName}: item.${f.name} ?? existingItem.${f.name}`
272
+ : `${f.sourceName}: item.${f.name}`)
273
+ .join(',\n')}
274
+ },
275
+ }),
276
+ )
277
+
278
+ this.remove(existingItem)
279
+ this.set(newItem)
280
+
281
+ await execution.finishUpdateMutation({ mutationId, updatedObject: newItem })
282
+ return newItem
283
+ } catch (error) {
284
+ await execution.errorMutation({ mutationId, error })
285
+ throw error
286
+ }
287
+ }
169
288
 
170
- ${mainBlocks.updateManyCode}
289
+ ${(0, jsdoc_1.toJsDocComment)([`Updates multiple ${meta.userFriendlyNamePlural} and returns them.`])}
290
+ public async updateMany(
291
+ { items, execution }: { items: ${meta.types.dto.update}[], execution: ${schemaMeta.actions.execution.interface} }
292
+ ): Promise<${model.typeName}[]> {
293
+ const result: ${model.typeName}[] = []
294
+ for (const item of items) {
295
+ try {
296
+ const updated = await this.update({ item, execution })
297
+ result.push(updated)
298
+ } catch {
299
+ /* empty */
300
+ }
301
+ }
302
+ return result
303
+ }
171
304
 
172
- ${mainBlocks.upsertCode}
305
+ ${(0, jsdoc_1.toJsDocComment)([`Creates or updates a ${meta.userFriendlyName} and returns it.`])}
306
+ public async upsert(
307
+ { item, execution }: { item: ${meta.types.dto.upsert}, execution: ${schemaMeta.actions.execution.interface} }
308
+ ): Promise<${model.typeName}> {
309
+ const existingItem = item.${model.idField.name} ? (await this.get(item.${model.idField.name})) : null
310
+ if (existingItem) {
311
+ return this.update({ item: item as ${meta.types.dto.update}, execution })
312
+ } else {
313
+ return this.create({ item: item as ${meta.types.dto.create}, execution })
314
+ }
315
+ }
173
316
 
174
- ${mainBlocks.upsertManyCode}
317
+ ${(0, jsdoc_1.toJsDocComment)([`Creates or updates multiple ${meta.userFriendlyNamePlural} and returns them.`])}
318
+ public async upsertMany(
319
+ { items, execution }: { items: ${meta.types.dto.upsert}[], execution: ${schemaMeta.actions.execution.interface} }
320
+ ): Promise<${model.typeName}[]> {
321
+ const result: ${model.typeName}[] = []
322
+ for (const item of items) {
323
+ try {
324
+ const updated = await this.upsert({ item, execution })
325
+ result.push(updated)
326
+ } catch {
327
+ /* empty */
328
+ }
329
+ }
330
+ return result
331
+ }
175
332
 
176
- ${mainBlocks.deleteCode}
333
+ ${(0, jsdoc_1.toJsDocComment)([`Deletes a ${meta.userFriendlyName} and returns its id.`])}
334
+ public async delete(
335
+ { id, execution }: { id: ${model.brandedIdType}, execution: ${schemaMeta.actions.execution.interface} }
336
+ ): Promise<${model.brandedIdType}> {
337
+ const existingItem = await this.get(id)
338
+ if (!existingItem) {
339
+ throw new Error(\`Could not delete ${model.typeName} with id \${id}. Not found!\`)
340
+ }
177
341
 
178
- ${mainBlocks.deleteManyCode}
342
+ const mutationId = await execution.startDeleteMutation({
343
+ model: '${meta.actions.actionScopeConstType}',
344
+ entityId: id,
345
+ sourceObject: existingItem,
346
+ })
347
+ try {
348
+
349
+ await this.db.${meta.data.repository.getMethodFnName}.delete({ where: { ${idField.sourceName}:id } })
350
+
351
+ this.remove(existingItem)
352
+ await execution.finishDeleteMutation({ mutationId })
353
+ return id
354
+ } catch (error) {
355
+ await execution.errorMutation({ mutationId, error })
356
+ throw error
357
+ }
358
+ }
359
+
360
+ ${(0, jsdoc_1.toJsDocComment)([`Deletes multiple ${meta.userFriendlyNamePlural} and returns their ids.`])}
361
+ public async deleteMany(
362
+ { ids, execution }: { ids: ${model.brandedIdType}[], execution: ${schemaMeta.actions.execution.interface} }
363
+ ): Promise<${model.brandedIdType}[]> {
364
+ const deletedIds: ${model.brandedIdType}[] = []
365
+ for (const id of ids) {
366
+ try {
367
+ await this.delete({ id, execution })
368
+ deletedIds.push(id)
369
+ } catch {
370
+ /* empty */
371
+ }
372
+ }
373
+ return deletedIds
374
+ }
375
+
376
+ ${(0, jsdoc_1.toJsDocComment)([`Creates a new ${meta.userFriendlyName} shallow clone and returns it.`])}
377
+ public async clone({ id, item, execution }: {
378
+ id: ${model.brandedIdType}
379
+ item: (item: ${model.typeName}) => ${meta.types.dto.clone},
380
+ execution: ${schemaMeta.actions.execution.interface}
381
+ }): Promise<${model.typeName}> {
382
+ const source = await this.get(id)
383
+ if (!source) {
384
+ throw new Error(\`${meta.userFriendlyName} with id \${id} not found\`)
385
+ }
386
+
387
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
388
+ const { id: _, ...data } = { ...source, ...item(source) }
389
+
390
+ return await this.create({ item: data, execution })
391
+ }
179
392
 
180
393
  ${relationsBlocks.getterFunctions.join('\n')}
181
394
 
@@ -210,575 +423,20 @@ export class ${meta.data.repository.className} implements Repository<${model.typ
210
423
  ${indexBlocks.removeCode.join('\n')}
211
424
  }
212
425
 
213
- ${mainBlocks.databaseDecoderCode}
426
+ /**
427
+ * Utility function that converts a given Database object to a TypeScript model instance
428
+ */
429
+ private ${meta.data.repository.decoderFnName}(
430
+ item: Pick<DbType, ${model.fields.map((f) => `'${f.sourceName}'`).join(' | ')}>
431
+ ): ${model.typeName} {
432
+ return ${meta.types.zodDecoderFnNames.fromDatabase}.parse({
433
+ ${model.fields.map((f) => `${f.name}: item.${f.sourceName}`).join(',\n')}
434
+ })
435
+ }
214
436
  }
215
437
  `;
216
438
  }
217
439
  exports.generateRepository = generateRepository;
218
- /**
219
- * Generates a mock repository data structure for a given model: same a repository, but in memory only.
220
- */
221
- function generateMockRepository({ model: modelSource, meta: metaSource, }) {
222
- // We re-use the repository block, but we change the meta data to use the mock repository name and the model to be in memory only
223
- const meta = Object.assign(Object.assign({}, metaSource), { data: Object.assign(Object.assign({}, metaSource.data), { repository: Object.assign(Object.assign({}, metaSource.data.repository), { className: metaSource.data.mockRepository.className, location: metaSource.data.mockRepository.location }) }) });
224
- const model = Object.assign(Object.assign({}, modelSource), { attributes: Object.assign(Object.assign({}, modelSource.attributes), { inMemoryOnly: true }) });
225
- return generateRepository({ model, meta });
226
- }
227
- exports.generateMockRepository = generateMockRepository;
228
- /**
229
- * Generates the main building blocks of the repository for in-memory model.
230
- */
231
- function _generateMainBuildingBlocks_InMemoryOnly({ model, meta, imports, blocks, }) {
232
- const methodTypeSignatures = getRepositoryMethodsTypeSignatures({ model, meta });
233
- const userRepositorySpecificBlocks = generateUserRepositorySpecificBlocks_InMemoryOnly({ model, meta, imports });
234
- imports.addImport({ from: (0, types_1.toPackageName)('@postxl/runtime'), items: [(0, types_1.toFunctionName)('removeUndefinedProperties')] });
235
- return {
236
- constructorCode: '',
237
- userRepositorySpecificBlocks,
238
- initCode: `
239
- // NOTE: The current implementation is synchronous, but it needs to be async to conform to the interface.
240
- // eslint-disable-next-line @typescript-eslint/require-await
241
- public async init() {
242
- this.data.clear()
243
-
244
- ${blocks.relationsBlocks.clearCode.join('\n')}
245
-
246
- ${blocks.uniqueStringFieldsBlocks.clearCode.join('\n')}
247
-
248
- ${blocks.defaultValueBlocks.init.resetCode}
249
-
250
- ${blocks.indexBlocks.initCode.join('\n')}
251
-
252
- ${userRepositorySpecificBlocks.initCall}
253
- }`,
254
- reInitCode: `
255
- public async reInit({items, execution}: ${methodTypeSignatures.createMany.parameters[0]}): Promise<void> {
256
- await this.init()
257
- await this.createMany({items, execution})
258
- }`,
259
- deleteAllCode: `
260
- public async deleteAll(): Promise<void> {
261
- return this.init()
262
- }`,
263
- createCode: `
264
- ${(0, jsdoc_1.toJsDocComment)([
265
- `Checks that item has the ${model.idField.name} field.`,
266
- `In case none exists, ${blocks.idBlocks.verifyFunctionComment}`,
267
- blocks.uniqueStringFieldsBlocks.verifyFunctionComment,
268
- blocks.maxLengthBlocks.verifyFunctionComment,
269
- ])}
270
- private verifyItem(
271
- item: ${blocks.idBlocks.verifyFunctionParameterType}
272
- ): ${model.name} {
273
- ${blocks.idBlocks.verifyCode}
274
-
275
- ${blocks.maxLengthBlocks.verifyCode.join('\n')}
276
-
277
- ${blocks.uniqueStringFieldsBlocks.verifyCode.join('\n')}
278
-
279
- ${blocks.validationBlocks.verifyCode.join('\n')}
280
-
281
- return {
282
- ${model.idField.name},
283
- ${model.fields
284
- .filter((f) => f.kind !== 'id' && !f.attributes.isReadonly)
285
- .map((f) => `${f.name}: item.${f.name}`)
286
- .join(',\n')},
287
- ${model.createdAtField ? `${model.createdAtField.sourceName}: new Date(),` : ''}
288
- ${model.updatedAtField ? `${model.updatedAtField.sourceName}: new Date(),` : ''}
289
- }
290
- }
291
-
292
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.create.jsDoc)}
293
- // Non-mocked version is async - so we keep type-compatible signatures for create() and createWithId()
294
- // eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-unused-vars
295
- public async create(
296
- { item, execution }: ${methodTypeSignatures.create.parameters[0]}
297
- ): ${methodTypeSignatures.create.returnType} {
298
- const mutationId = await execution.startCreateMutation({
299
- model: '${meta.actions.actionScopeConstType}',
300
- createObject: item
301
- })
302
-
303
- try {
304
- const newItem = this.verifyItem(item)
305
-
306
- this.set(newItem)
307
- await execution.finishCreateMutation({ mutationId, createdObject: newItem, entityId: newItem.id })
308
- return newItem
309
- } catch (error) {
310
- await execution.errorMutation({ mutationId, error })
311
- throw error
312
- }
313
- }`,
314
- createManyCode: `
315
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.createMany.jsDoc)}
316
- public async createMany(
317
- { items, execution }: ${methodTypeSignatures.createMany.parameters[0]}
318
- ): ${methodTypeSignatures.createMany.returnType} {
319
- const mutationId = await execution.startCreateManyMutation({
320
- model: '${meta.actions.actionScopeConstType}',
321
- createObjects: items
322
- })
323
-
324
- try {
325
- const newItems = items.map((item) => this.verifyItem(item))
326
-
327
- for (const item of newItems) {
328
- this.set(item)
329
- }
330
-
331
- await execution.finishCreateManyMutation({
332
- mutationId,
333
- createdObjects: newItems,
334
- entityIds: newItems.map((i) => i.id),
335
- })
336
- return newItems
337
- } catch (error) {
338
- await execution.errorMutation({ mutationId, error })
339
- throw error
340
- }
341
- }`,
342
- updateCode: `
343
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.update.jsDoc)}
344
- public async update(
345
- { item, execution }: ${methodTypeSignatures.update.parameters[0]}
346
- ): ${methodTypeSignatures.update.returnType} {
347
- const existingItem = await this.get(item.id)
348
-
349
- if (!existingItem) {
350
- throw new Error(\`Could not update ${meta.userFriendlyName} with id \${item.id}. Not found!\`)
351
- }
352
- const mutationId = await execution.startUpdateMutation({
353
- model: '${meta.actions.actionScopeConstType}',
354
- entityId: item.id,
355
- sourceObject: existingItem,
356
- updateObject: item,
357
- })
358
- try {
359
- ${blocks.maxLengthBlocks.updateCode.join('\n')}
360
-
361
- ${blocks.uniqueStringFieldsBlocks.updateCode.join('\n')}
362
-
363
- const newItem = { ...existingItem, ...removeUndefinedProperties(item) }
364
- ${model.updatedAtField ? `newItem.${model.updatedAtField.name} = new Date()` : ''}
365
-
366
- this.remove(existingItem)
367
- this.set(newItem)
368
-
369
- await execution.finishUpdateMutation({ mutationId, updatedObject: newItem })
370
- return newItem
371
- } catch (error) {
372
- await execution.errorMutation({ mutationId, error })
373
- throw error
374
- }
375
- }`,
376
- updateManyCode: `
377
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.updateMany.jsDoc)}
378
- public async updateMany(
379
- { items, execution }: ${methodTypeSignatures.updateMany.parameters[0]}
380
- ): ${methodTypeSignatures.updateMany.returnType} {
381
- const result: ${model.typeName}[] = []
382
- for (const item of items) {
383
- try {
384
- const updated = await this.update({ item, execution })
385
- result.push(updated)
386
- } catch {
387
- /* empty */
388
- }
389
- }
390
- return result
391
- }`,
392
- upsertCode: `
393
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.upsert.jsDoc)}
394
- public async upsert(
395
- { item, execution }: ${methodTypeSignatures.upsert.parameters[0]}
396
- ): ${methodTypeSignatures.upsert.returnType} {
397
- const existingItem = item.${model.idField.name} ? (await this.get(item.${model.idField.name})) : null
398
- if (existingItem) {
399
- return this.update({ item: item as ${meta.types.dto.update}, execution })
400
- } else {
401
- return this.create({ item: item as ${meta.types.dto.create}, execution })
402
- }
403
- }`,
404
- upsertManyCode: `
405
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.upsertMany.jsDoc)}
406
- public async upsertMany(
407
- { items, execution }: ${methodTypeSignatures.upsertMany.parameters[0]}
408
- ): ${methodTypeSignatures.upsertMany.returnType} {
409
- const result: ${model.typeName}[] = []
410
- for (const item of items) {
411
- try {
412
- const updated = await this.upsert({ item, execution })
413
- result.push(updated)
414
- } catch {
415
- /* empty */
416
- }
417
- }
418
- return result
419
- }`,
420
- deleteCode: `
421
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.delete.jsDoc)}
422
- public async delete(
423
- { id, execution }: ${methodTypeSignatures.delete.parameters[0]}
424
- ): ${methodTypeSignatures.delete.returnType} {
425
- const existingItem = await this.get(id)
426
- if (!existingItem) {
427
- throw new Error(\`Could not delete ${model.typeName} with id \${id}. Not found!\`)
428
- }
429
- const mutationId = await execution.startDeleteMutation({
430
- model: '${meta.actions.actionScopeConstType}',
431
- entityId: id,
432
- sourceObject: existingItem,
433
- })
434
- try {
435
- this.remove(existingItem)
436
- await execution.finishDeleteMutation({ mutationId })
437
- return id
438
- } catch (error) {
439
- await execution.errorMutation({ mutationId, error })
440
- throw error
441
- }
442
- }`,
443
- deleteManyCode: `
444
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.deleteMany.jsDoc)}
445
- public async deleteMany(
446
- { ids, execution }: ${methodTypeSignatures.deleteMany.parameters[0]}
447
- ): ${methodTypeSignatures.deleteMany.returnType} {
448
- const deletedIds: ${model.brandedIdType}[] = []
449
- for (const id of ids) {
450
- try {
451
- await this.delete({ id, execution })
452
- deletedIds.push(id)
453
- } catch {
454
- /* empty */
455
- }
456
- }
457
- return deletedIds
458
- }`,
459
- databaseDecoderCode: ``,
460
- };
461
- }
462
- /**
463
- * Generates the methods of the repository for a model that is stored in the database.
464
- */
465
- function generateMainBuildingBlocks_InDatabase({ model, meta, schemaMeta, imports, blocks, }) {
466
- const decoderFunctionName = meta.data.repository.decoderFnName;
467
- const { idField } = model;
468
- imports.addImports({
469
- [meta.types.importPath]: [meta.types.zodDecoderFnNames.fromDatabase],
470
- [schemaMeta.backendModules.db.databaseService.location.import]: [schemaMeta.backendModules.db.databaseService.name],
471
- [schemaMeta.backendModules.db.typesImportPath]: [(0, types_1.toAnnotatedTypeName)((0, types_1.toTypeName)(`${model.sourceName} as DbType`))],
472
- [(0, types_1.toPackageName)('@postxl/runtime')]: [(0, types_1.toFunctionName)('format'), (0, types_1.toFunctionName)('pluralize')],
473
- });
474
- const dbTableName = [model.sourceSchemaName, model.sourceName]
475
- .filter((s) => s != null)
476
- .map((part) => `"${part}"`)
477
- .join('.');
478
- const methodTypeSignatures = getRepositoryMethodsTypeSignatures({ model, meta });
479
- const userRepositorySpecificBlocks = generateUserRepositorySpecificBlocks_InDatabase({ model, meta, imports });
480
- return {
481
- constructorCode: `constructor(protected db: ${schemaMeta.backendModules.db.databaseService.name}) {}`,
482
- userRepositorySpecificBlocks,
483
- initCode: `
484
- public async init() {
485
- this.data.clear()
486
-
487
- ${blocks.relationsBlocks.clearCode.join('\n')}
488
-
489
- ${blocks.uniqueStringFieldsBlocks.clearCode.join('\n')}
490
-
491
- ${blocks.defaultValueBlocks.init.resetCode}
492
-
493
- ${blocks.indexBlocks.initCode.join('\n')}
494
-
495
- const data = await this.db.${meta.data.repository.getMethodFnName}.findMany({})
496
-
497
- for (const rawItem of data) {
498
- const item = this.${decoderFunctionName}(rawItem)
499
- this.set(item)
500
-
501
- ${blocks.defaultValueBlocks.init.setCode}
502
- }
503
-
504
- ${blocks.idBlocks.initCode}
505
-
506
- ${blocks.defaultValueBlocks.init.checkCode}
507
-
508
- ${userRepositorySpecificBlocks.initCall}
509
-
510
- this.logger.log(\`\${format(this.data.size)} \${pluralize('${model.typeName}', this.data.size)} loaded\`)
511
- ${blocks.indexBlocks.initLogCode.join('\n')}
512
- }`,
513
- reInitCode: `
514
- public async reInit(
515
- { items, execution }: ${methodTypeSignatures.createMany.parameters[0]}
516
- ): Promise<void> {
517
- if (!this.db.isTestDB) {
518
- const errorMsg =
519
- 'ReInit() shall only be called in tests using MockRepositories or in DB configured for E2E tests!'
520
- this.logger.error(errorMsg)
521
- throw new Error(errorMsg)
522
- }
523
-
524
- await this.db.runOnlyOnTestDb(() => this.createMany({ items, execution }))
525
-
526
- return this.init()
527
- }`,
528
- deleteAllCode: `
529
- public async deleteAll(): Promise<void> {
530
- await this.db.runOnlyOnTestDb(() => this.db.$executeRaw\`DELETE FROM ${dbTableName}\`)
531
-
532
- return this.init()
533
- }
534
- `,
535
- // prettier-ignore
536
- createCode: `
537
- ${(0, jsdoc_1.toJsDocComment)([
538
- `Checks that item has the ${idField.name} field.`,
539
- `In case none exists, ${blocks.idBlocks.verifyFunctionComment}`,
540
- blocks.uniqueStringFieldsBlocks.verifyFunctionComment,
541
- blocks.maxLengthBlocks.verifyFunctionComment,
542
- ])}
543
- private verifyItem(
544
- item: ${blocks.idBlocks.verifyFunctionParameterType}
545
- ): ${blocks.idBlocks.createFunctionParameterType} {
546
- ${blocks.idBlocks.verifyCode}
547
-
548
- ${blocks.maxLengthBlocks.verifyCode.join('\n')}
549
-
550
- ${blocks.uniqueStringFieldsBlocks.verifyCode.join('\n')}
551
-
552
- ${blocks.validationBlocks.verifyCode.join('\n')}
553
-
554
- return {
555
- ${idField.name},
556
- ${model.fields
557
- .filter((f) => f.kind !== 'id' && !f.attributes.isReadonly)
558
- .map((f) => `${f.name}: item.${f.name}`)
559
- .join(',\n')}
560
- }
561
- }
562
-
563
- private toCreateItem(item: ${blocks.idBlocks.createFunctionParameterType}) {
564
- return {
565
- ${model.fields
566
- .filter((f) => !f.attributes.isReadonly || f.kind === 'id')
567
- .map((f) => `${f.sourceName}: item.${f.name}`)
568
- .join(',\n')},
569
- }
570
- }
571
-
572
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.create.jsDoc)}
573
- public async create(
574
- { item, execution }: ${methodTypeSignatures.create.parameters[0]}
575
- ): ${methodTypeSignatures.create.returnType} {
576
- const mutationId = await execution.startCreateMutation({
577
- model: '${meta.actions.actionScopeConstType}',
578
- createObject: item
579
- })
580
-
581
- try {
582
- const newItem = this.${decoderFunctionName}(
583
- await this.db.${meta.data.repository.getMethodFnName}.create({
584
- data: this.toCreateItem(this.verifyItem(item)),
585
- }),
586
- )
587
-
588
- this.set(newItem)
589
- await execution.finishCreateMutation({ mutationId, createdObject: newItem, entityId: newItem.id })
590
- return newItem
591
- } catch (error) {
592
- await execution.errorMutation({ mutationId, error })
593
- throw error
594
- }
595
- }`,
596
- // prettier-ignore
597
- createManyCode: `
598
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.createMany.jsDoc)}
599
- public async createMany(
600
- {items, execution}: ${methodTypeSignatures.createMany.parameters[0]}
601
- ): ${methodTypeSignatures.createMany.returnType} {
602
- const mutationId = await execution.startCreateManyMutation({
603
- model: '${meta.actions.actionScopeConstType}',
604
- createObjects: items
605
- })
606
-
607
- try {
608
- const newItems = items.map((item) => this.verifyItem(item))
609
-
610
- await this.db.${meta.data.repository.getMethodFnName}.createMany({ data: newItems.map(i => this.toCreateItem(i)) })
611
-
612
- const dbItems = await this.db.${meta.data.repository.getMethodFnName}.findMany({
613
- where: {
614
- ${model.idField.sourceName}: { in: newItems.map(i => i.${model.idField.name}) }
615
- }
616
- })
617
-
618
- const result = dbItems.map((item) => this.${decoderFunctionName}(item))
619
-
620
- for (const item of result) {
621
- this.set(item)
622
- }
623
-
624
- await execution.finishCreateManyMutation({
625
- mutationId,
626
- createdObjects: result,
627
- entityIds: newItems.map((i) => i.id),
628
- })
629
- return result
630
- } catch (error) {
631
- await execution.errorMutation({ mutationId, error })
632
- throw error
633
- }
634
- }`,
635
- updateCode: `
636
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.update.jsDoc)}
637
- public async update(
638
- { item, execution }: ${methodTypeSignatures.update.parameters[0]}
639
- ): ${methodTypeSignatures.update.returnType} {
640
- const existingItem = await this.get(item.${idField.name})
641
- if (!existingItem) {
642
- throw new Error(\`Could not update ${meta.userFriendlyName} with id \${item.id}. Not found!\`)
643
- }
644
-
645
- const mutationId = await execution.startUpdateMutation({
646
- model: '${meta.actions.actionScopeConstType}',
647
- entityId: item.id,
648
- sourceObject: existingItem,
649
- updateObject: item,
650
- })
651
- try {
652
- ${blocks.maxLengthBlocks.updateCode.join('\n')}
653
-
654
- ${blocks.uniqueStringFieldsBlocks.updateCode.join('\n')}
655
-
656
- const newItem = this.${decoderFunctionName}(
657
- await this.db.${meta.data.repository.getMethodFnName}.update({
658
- where: {
659
- ${idField.sourceName}: item.${idField.name},
660
- },
661
- data: {
662
- ${[...model.fields.values()]
663
- .filter((f) => f.kind !== 'id' && !f.attributes.isReadonly)
664
- .map((f) => f.isRequired
665
- ? `${f.sourceName}: item.${f.name} ?? existingItem.${f.name}`
666
- : `${f.sourceName}: item.${f.name}`)
667
- .join(',\n')}
668
- },
669
- }),
670
- )
671
-
672
- this.remove(existingItem)
673
- this.set(newItem)
674
-
675
- await execution.finishUpdateMutation({ mutationId, updatedObject: newItem })
676
- return newItem
677
- } catch (error) {
678
- await execution.errorMutation({ mutationId, error })
679
- throw error
680
- }
681
- }`,
682
- updateManyCode: `
683
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.updateMany.jsDoc)}
684
- public async updateMany(
685
- { items, execution }: ${methodTypeSignatures.updateMany.parameters[0]}
686
- ): ${methodTypeSignatures.updateMany.returnType} {
687
- const result: ${model.typeName}[] = []
688
- for (const item of items) {
689
- try {
690
- const updated = await this.update({ item, execution })
691
- result.push(updated)
692
- } catch {
693
- /* empty */
694
- }
695
- }
696
- return result
697
- }`,
698
- upsertCode: `
699
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.upsert.jsDoc)}
700
- public async upsert(
701
- { item, execution }: ${methodTypeSignatures.upsert.parameters[0]}
702
- ): ${methodTypeSignatures.upsert.returnType} {
703
- const existingItem = item.${model.idField.name} ? (await this.get(item.${model.idField.name})) : null
704
- if (existingItem) {
705
- return this.update({ item: item as ${meta.types.dto.update}, execution })
706
- } else {
707
- return this.create({ item: item as ${meta.types.dto.create}, execution })
708
- }
709
- }`,
710
- upsertManyCode: `
711
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.upsertMany.jsDoc)}
712
- public async upsertMany(
713
- { items, execution }: ${methodTypeSignatures.upsertMany.parameters[0]}
714
- ): ${methodTypeSignatures.upsertMany.returnType} {
715
- const result: ${model.typeName}[] = []
716
- for (const item of items) {
717
- try {
718
- const updated = await this.upsert({ item, execution })
719
- result.push(updated)
720
- } catch {
721
- /* empty */
722
- }
723
- }
724
- return result
725
- }`,
726
- deleteCode: `
727
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.delete.jsDoc)}
728
- public async delete(
729
- { id, execution }: ${methodTypeSignatures.delete.parameters[0]}
730
- ): ${methodTypeSignatures.delete.returnType} {
731
- const existingItem = await this.get(id)
732
- if (!existingItem) {
733
- throw new Error(\`Could not delete ${model.typeName} with id \${id}. Not found!\`)
734
- }
735
-
736
- const mutationId = await execution.startDeleteMutation({
737
- model: '${meta.actions.actionScopeConstType}',
738
- entityId: id,
739
- sourceObject: existingItem,
740
- })
741
- try {
742
-
743
- await this.db.${meta.data.repository.getMethodFnName}.delete({ where: { ${idField.sourceName}:id } })
744
-
745
- this.remove(existingItem)
746
- await execution.finishDeleteMutation({ mutationId })
747
- return id
748
- } catch (error) {
749
- await execution.errorMutation({ mutationId, error })
750
- throw error
751
- }
752
- }`,
753
- deleteManyCode: `
754
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.deleteMany.jsDoc)}
755
- public async deleteMany(
756
- { ids, execution }: ${methodTypeSignatures.deleteMany.parameters[0]}
757
- ): ${methodTypeSignatures.deleteMany.returnType} {
758
- const deletedIds: ${model.brandedIdType}[] = []
759
- for (const id of ids) {
760
- try {
761
- await this.delete({ id, execution })
762
- deletedIds.push(id)
763
- } catch {
764
- /* empty */
765
- }
766
- }
767
- return deletedIds
768
- }`,
769
- databaseDecoderCode: `
770
- /**
771
- * Utility function that converts a given Database object to a TypeScript model instance
772
- */
773
- private ${decoderFunctionName}(
774
- item: Pick<DbType, ${model.fields.map((f) => `'${f.sourceName}'`).join(' | ')}>
775
- ): ${model.typeName} {
776
- return ${meta.types.zodDecoderFnNames.fromDatabase}.parse({
777
- ${model.fields.map((f) => `${f.name}: item.${f.sourceName}`).join(',\n')}
778
- })
779
- }`,
780
- };
781
- }
782
440
  function generateUserRepositorySpecificBlocks_InDatabase({ model, meta, imports, }) {
783
441
  if (model.name !== 'User') {
784
442
  return {
@@ -815,40 +473,6 @@ function generateUserRepositorySpecificBlocks_InDatabase({ model, meta, imports,
815
473
  }`,
816
474
  };
817
475
  }
818
- function generateUserRepositorySpecificBlocks_InMemoryOnly({ model, meta, imports, }) {
819
- if (model.name !== 'User') {
820
- return {
821
- rootUserNameConst: '',
822
- getterBlock: '',
823
- initCall: '',
824
- rootUserInitializeBlock: '',
825
- };
826
- }
827
- const { rootUserId, rootUserValue } = generateSharedRootUserBlocks({ model, meta, imports });
828
- return {
829
- rootUserNameConst: `public static ROOT_USER_ID = ${meta.types.toBrandedIdTypeFnName}(${rootUserId})`,
830
- getterBlock: `
831
- // We initialize the root user in the init() function
832
- private _rootUser!: ${meta.types.typeName}
833
- public get rootUser(): ${meta.types.typeName} {
834
- return this._rootUser
835
- }`,
836
- initCall: `await this.initializeRootUser()`,
837
- rootUserInitializeBlock: `
838
- private async initializeRootUser(): Promise<void> {
839
- const existingRootUser = await this.get(${meta.data.repository.className}.ROOT_USER_ID)
840
- if (existingRootUser) {
841
- this._rootUser = existingRootUser
842
- return
843
- }
844
-
845
- const rawUser = { ${rootUserValue} }
846
- const newRootUser = this.verifyItem (rawUser)
847
- this.set(newRootUser)
848
- this._rootUser = newRootUser
849
- }`,
850
- };
851
- }
852
476
  function generateSharedRootUserBlocks({ model, meta, imports, }) {
853
477
  var _a;
854
478
  const providedDefault = model.attributes.systemUser;
@@ -969,139 +593,67 @@ function generateSharedRootUserBlocks({ model, meta, imports, }) {
969
593
  function generateIdBlocks({ model, meta }) {
970
594
  const idField = model.idField;
971
595
  if (!idField.isGenerated) {
972
- return _generateIdBlocks_NoGeneration({ idField, model, meta });
596
+ return {
597
+ libraryImports: '',
598
+ generateNextIdFunctionName: '',
599
+ initCode: '',
600
+ verifyFunctionComment: `an error is thrown as field has no default setting in schema.`,
601
+ verifyFunctionParameterType: meta.types.dto.create,
602
+ verifyCode: `
603
+ if (item.${idField.name} === undefined) {
604
+ throw new Error('Id field ${idField.name} is required!')
605
+ }
606
+ const ${idField.name} = ${meta.types.toBrandedIdTypeFnName}(item.${idField.name})`,
607
+ setCode: '',
608
+ createFunctionParameterType: model.typeName,
609
+ };
973
610
  }
974
611
  if (idField.schemaType === 'Int') {
975
- return _generateIdBlock_Int({ idField, model, meta });
612
+ const generatedFields = model.fields.filter((f) => f.kind === 'id' || f.attributes.isReadonly);
613
+ const readonlyFields = model.fields.filter((f) => f.attributes.isReadonly && f.kind !== 'id');
614
+ return {
615
+ libraryImports: '',
616
+ generateNextIdFunctionName: `
617
+ protected currentMaxId = 0
618
+ public generateNextId(): ${model.brandedIdType} {
619
+ return ${meta.types.toBrandedIdTypeFnName}(++this.currentMaxId)
620
+ }`,
621
+ initCode: `this.currentMaxId = (await this.db.${meta.data.repository.getMethodFnName}.aggregate({ _max: { ${idField.sourceName}: true } }))._max.${idField.sourceName} ?? 0`,
622
+ verifyFunctionComment: 'the id is generated by increasing the highest former id and assigned to the item.',
623
+ // prettier-ignore
624
+ verifyFunctionParameterType: `(Omit<${model.typeName}, ${generatedFields.map((f) => `'${f.name}'`).join(' | ')}> & Partial<{${idField.name}: ${idField.unbrandedTypeName}}>)`,
625
+ verifyCode: `const ${idField.name} = (item.${idField.name} !== undefined) ? ${meta.types.toBrandedIdTypeFnName}(item.${idField.name}) : this.generateNextId()`,
626
+ createFunctionParameterType:
627
+ // NOTE: In case we have readonly fields, we need to omit them from the create function.
628
+ readonlyFields.length === 0
629
+ ? model.typeName
630
+ : `Omit<${model.typeName}, ${readonlyFields.map((f) => `'${f.name}'`).join(' |')}>`,
631
+ setCode: `if (item.id > this.currentMaxId) { this.currentMaxId = item.id }`,
632
+ };
976
633
  }
977
634
  if (idField.schemaType === 'String') {
978
- return _generateIdBlock_UUID({ idField, model, meta });
979
- }
980
- throw new Error(`Repository block only supports Id generation for number and strings! Found ${idField.schemaType} for model ${model.name} instead!`);
981
- }
982
- /**
983
- * Returns a collection of type signatures for the repository methods of a given model.
984
- */
985
- // NOTE: We export this function as an interface simplification to the repository generator. Internally, we
986
- // use the same functions as the ones used in this function which we don't want to expose.
987
- function getRepositoryMethodsTypeSignatures({ model, meta }) {
988
- const schemaMeta = (0, meta_1.getSchemaMetadata)({ config: model.schemaConfig });
989
- return {
990
- create: {
991
- jsDoc: [`Creates a new ${meta.userFriendlyName} and returns it.`],
992
- parameters: [`{item: ${meta.types.dto.create}, execution: ${schemaMeta.actions.execution.interface}}`],
993
- returnType: `Promise<${model.typeName}>`,
994
- },
995
- createMany: {
996
- jsDoc: [`Creates multiple new ${meta.userFriendlyNamePlural} and returns them.`],
997
- parameters: [`{items: ${meta.types.dto.create}[], execution: ${schemaMeta.actions.execution.interface}}`],
998
- returnType: `Promise<${model.typeName}[]>`,
999
- },
1000
- update: {
1001
- jsDoc: [`Updates a ${meta.userFriendlyName} and returns it.`],
1002
- parameters: [`{item: ${meta.types.dto.update}, execution: ${schemaMeta.actions.execution.interface}}`],
1003
- returnType: `Promise<${model.typeName}>`,
1004
- },
1005
- updateMany: {
1006
- jsDoc: [`Updates multiple ${meta.userFriendlyNamePlural} and returns them.`],
1007
- parameters: [`{items: ${meta.types.dto.update}[], execution: ${schemaMeta.actions.execution.interface}}`],
1008
- returnType: `Promise<${model.typeName}[]>`,
1009
- },
1010
- upsert: {
1011
- jsDoc: [`Creates or updates a ${meta.userFriendlyName} and returns it.`],
1012
- parameters: [`{item: ${meta.types.dto.upsert}, execution: ${schemaMeta.actions.execution.interface}}`],
1013
- returnType: `Promise<${model.typeName}>`,
1014
- },
1015
- upsertMany: {
1016
- jsDoc: [`Creates or updates multiple ${meta.userFriendlyNamePlural} and returns them.`],
1017
- parameters: [`{items: ${meta.types.dto.upsert}[], execution: ${schemaMeta.actions.execution.interface}}`],
1018
- returnType: `Promise<${model.typeName}[]>`,
1019
- },
1020
- delete: {
1021
- jsDoc: [`Deletes a ${meta.userFriendlyName} and returns its id.`],
1022
- parameters: [`{id: ${model.brandedIdType}, execution: ${schemaMeta.actions.execution.interface}}`],
1023
- returnType: `Promise<${model.brandedIdType}>`,
1024
- },
1025
- deleteMany: {
1026
- jsDoc: [`Deletes multiple ${meta.userFriendlyNamePlural} and returns their ids.`],
1027
- parameters: [`{ids: ${model.brandedIdType}[], execution: ${schemaMeta.actions.execution.interface}}`],
1028
- returnType: `Promise<${model.brandedIdType}[]>`,
1029
- },
1030
- };
1031
- }
1032
- exports.getRepositoryMethodsTypeSignatures = getRepositoryMethodsTypeSignatures;
1033
- /**
1034
- * Generates the id block code chunks for a model that requires an ID field to be manually
1035
- * supplied to the create function.
1036
- */
1037
- function _generateIdBlocks_NoGeneration({ idField, model, meta, }) {
1038
- return {
1039
- libraryImports: '',
1040
- generateNextIdFunctionName: '',
1041
- initCode: '',
1042
- verifyFunctionComment: `an error is thrown as field has no default setting in schema.`,
1043
- verifyFunctionParameterType: meta.types.dto.create,
1044
- verifyCode: `
1045
- if (item.${idField.name} === undefined) {
1046
- throw new Error('Id field ${idField.name} is required!')
1047
- }
1048
- const ${idField.name} = ${meta.types.toBrandedIdTypeFnName}(item.${idField.name})`,
1049
- setCode: '',
1050
- createFunctionParameterType: model.typeName,
1051
- };
1052
- }
1053
- /**
1054
- * Generates the id block code chunks for a model that has an integer id field.
1055
- * Given chunks make sure that the id is unique if provided or generate a new one if not.
1056
- */
1057
- function _generateIdBlock_Int({ idField, model, meta, }) {
1058
- const generatedFields = model.fields.filter((f) => f.kind === 'id' || f.attributes.isReadonly);
1059
- const readonlyFields = model.fields.filter((f) => f.attributes.isReadonly && f.kind !== 'id');
1060
- return {
1061
- libraryImports: '',
1062
- generateNextIdFunctionName: `
1063
- protected currentMaxId = 0
1064
- public generateNextId(): ${model.brandedIdType} {
1065
- return ${meta.types.toBrandedIdTypeFnName}(++this.currentMaxId)
1066
- }`,
1067
- initCode: `this.currentMaxId = (await this.db.${meta.data.repository.getMethodFnName}.aggregate({ _max: { ${idField.sourceName}: true } }))._max.${idField.sourceName} ?? 0`,
1068
- verifyFunctionComment: 'the id is generated by increasing the highest former id and assigned to the item.',
1069
- // prettier-ignore
1070
- verifyFunctionParameterType: `(Omit<${model.typeName}, ${generatedFields.map((f) => `'${f.name}'`).join(' | ')}> & Partial<{${idField.name}: ${idField.unbrandedTypeName}}>)`,
1071
- verifyCode: `const ${idField.name} = (item.${idField.name} !== undefined) ? ${meta.types.toBrandedIdTypeFnName}(item.${idField.name}) : this.generateNextId()`,
1072
- createFunctionParameterType:
1073
- // NOTE: In case we have readonly fields, we need to omit them from the create function.
1074
- readonlyFields.length === 0
1075
- ? model.typeName
1076
- : `Omit<${model.typeName}, ${readonlyFields.map((f) => `'${f.name}'`).join(' |')}>`,
1077
- setCode: `if (item.id > this.currentMaxId) { this.currentMaxId = item.id }`,
1078
- };
1079
- }
1080
- /**
1081
- * Generates the id block code chunks for a model that has a UUID id field.
1082
- * It allows you to provide a custom id or generates a new one if not.
1083
- */
1084
- function _generateIdBlock_UUID({ idField, model, meta, }) {
1085
- const dbGeneratedFields = model.fields.filter((f) => f.kind === 'id' || f.attributes.isReadonly);
1086
- const readonlyFields = model.fields.filter((f) => f.attributes.isReadonly && f.kind !== 'id');
1087
- return {
1088
- libraryImports: `import { randomUUID } from 'crypto'`,
1089
- generateNextIdFunctionName: `
635
+ const dbGeneratedFields = model.fields.filter((f) => f.kind === 'id' || f.attributes.isReadonly);
636
+ const readonlyFields = model.fields.filter((f) => f.attributes.isReadonly && f.kind !== 'id');
637
+ return {
638
+ libraryImports: `import { randomUUID } from 'crypto'`,
639
+ generateNextIdFunctionName: `
1090
640
  public generateNextId(): ${model.brandedIdType} {
1091
641
  return ${meta.types.toBrandedIdTypeFnName}(randomUUID())
1092
642
  }`,
1093
- initCode: '',
1094
- verifyFunctionComment: 'a new UUID is generated and assigned to the item.',
1095
- // prettier-ignore
1096
- verifyFunctionParameterType: `(Omit<${model.typeName}, ${dbGeneratedFields.map((f) => `'${f.name}'`).join(' | ')}> & Partial<{${idField.name}: ${idField.unbrandedTypeName}}>)`,
1097
- verifyCode: `const ${idField.name} = (item.${idField.name} !== undefined) ? ${meta.types.toBrandedIdTypeFnName}(item.${idField.name}) : this.generateNextId()`,
1098
- createFunctionParameterType:
1099
- // NOTE: In case we have readonly fields, we need to omit them from the create function.
1100
- readonlyFields.length === 0
1101
- ? model.typeName
1102
- : `Omit<${model.typeName}, ${readonlyFields.map((f) => `'${f.name}'`).join(' |')}>`,
1103
- setCode: '',
1104
- };
643
+ initCode: '',
644
+ verifyFunctionComment: 'a new UUID is generated and assigned to the item.',
645
+ // prettier-ignore
646
+ verifyFunctionParameterType: `(Omit<${model.typeName}, ${dbGeneratedFields.map((f) => `'${f.name}'`).join(' | ')}> & Partial<{${idField.name}: ${idField.unbrandedTypeName}}>)`,
647
+ verifyCode: `const ${idField.name} = (item.${idField.name} !== undefined) ? ${meta.types.toBrandedIdTypeFnName}(item.${idField.name}) : this.generateNextId()`,
648
+ createFunctionParameterType:
649
+ // NOTE: In case we have readonly fields, we need to omit them from the create function.
650
+ readonlyFields.length === 0
651
+ ? model.typeName
652
+ : `Omit<${model.typeName}, ${readonlyFields.map((f) => `'${f.name}'`).join(' |')}>`,
653
+ setCode: '',
654
+ };
655
+ }
656
+ throw new Error(`Repository block only supports Id generation for number and strings! Found ${idField.schemaType} for model ${model.name} instead!`);
1105
657
  }
1106
658
  /**
1107
659
  * Returns the code chunks that define the default value property and its initialization.