@postxl/generator 0.38.2 → 0.40.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 (56) hide show
  1. package/dist/generator.js +43 -21
  2. package/dist/generators/enums/types.generator.js +1 -1
  3. package/dist/generators/indices/businesslogic-actiontypes.generator.js +1 -1
  4. package/dist/generators/indices/businesslogic-update-module.generator.js +2 -2
  5. package/dist/generators/indices/businesslogic-update-service.generator.js +1 -1
  6. package/dist/generators/indices/businesslogic-view-module.generator.js +2 -2
  7. package/dist/generators/indices/businesslogic-view-service.generator.js +1 -1
  8. package/dist/generators/indices/{seed-service.generator.d.ts → data-types.generator.d.ts} +2 -2
  9. package/dist/generators/indices/data-types.generator.js +48 -0
  10. package/dist/generators/indices/datamock-module.generator.js +11 -11
  11. package/dist/generators/indices/datamocker.generator.js +1 -1
  12. package/dist/generators/indices/datamodule.generator.js +34 -52
  13. package/dist/generators/indices/dataservice.generator.js +202 -9
  14. package/dist/generators/indices/dispatcher-service.generator.js +20 -10
  15. package/dist/generators/indices/emptydatabasemigration.generator.d.ts +2 -0
  16. package/dist/generators/indices/emptydatabasemigration.generator.js +14 -7
  17. package/dist/generators/indices/importexport-convert-import-functions.generator.d.ts +9 -0
  18. package/dist/generators/indices/importexport-convert-import-functions.generator.js +528 -0
  19. package/dist/generators/indices/importexport-exporter-class.generator.d.ts +9 -0
  20. package/dist/generators/indices/importexport-exporter-class.generator.js +116 -0
  21. package/dist/generators/indices/importexport-import-service.generator.d.ts +9 -0
  22. package/dist/generators/indices/importexport-import-service.generator.js +563 -0
  23. package/dist/generators/indices/{seeddata-type.generator.d.ts → importexport-types.generator.d.ts} +2 -2
  24. package/dist/generators/indices/importexport-types.generator.js +234 -0
  25. package/dist/generators/indices/repositories.generator.js +8 -8
  26. package/dist/generators/indices/seed-migration.generator.js +1 -1
  27. package/dist/generators/indices/seed-template.generator.js +1 -1
  28. package/dist/generators/indices/selectors.generator.d.ts +7 -0
  29. package/dist/generators/indices/selectors.generator.js +82 -0
  30. package/dist/generators/indices/{seed-template-decoder.generator.d.ts → testdata-service.generator.d.ts} +2 -2
  31. package/dist/generators/indices/testdata-service.generator.js +71 -0
  32. package/dist/generators/models/businesslogic-update.generator.js +6 -6
  33. package/dist/generators/models/businesslogic-view.generator.js +4 -4
  34. package/dist/generators/models/importexport-decoder.generator.d.ts +23 -0
  35. package/dist/generators/models/importexport-decoder.generator.js +234 -0
  36. package/dist/generators/models/react.generator/library.generator.js +4 -0
  37. package/dist/generators/models/react.generator/modals.generator.js +35 -8
  38. package/dist/generators/models/repository.generator.js +156 -18
  39. package/dist/generators/models/route.generator.js +2 -2
  40. package/dist/generators/models/stub.generator.js +1 -1
  41. package/dist/generators/models/types.generator.js +1 -1
  42. package/dist/lib/id-collector.d.ts +43 -0
  43. package/dist/lib/id-collector.js +53 -0
  44. package/dist/lib/imports.d.ts +1 -1
  45. package/dist/lib/meta.d.ts +480 -122
  46. package/dist/lib/meta.js +187 -74
  47. package/dist/lib/schema/schema.d.ts +58 -43
  48. package/dist/lib/schema/types.d.ts +63 -12
  49. package/dist/lib/schema/types.js +27 -7
  50. package/dist/lib/utils/string.d.ts +1 -0
  51. package/dist/lib/utils/string.js +1 -0
  52. package/dist/prisma/parse.js +4 -4
  53. package/package.json +2 -2
  54. package/dist/generators/indices/seed-service.generator.js +0 -356
  55. package/dist/generators/indices/seed-template-decoder.generator.js +0 -151
  56. package/dist/generators/indices/seeddata-type.generator.js +0 -42
@@ -1,3 +1,44 @@
1
+ /**
2
+ * The name of a class, e.g. "AggregationRepository".
3
+ */
4
+ export type ClassName = string & {
5
+ readonly ___type: 'ClassName';
6
+ };
7
+ /**
8
+ * Converts a raw string to a branded ClassName.
9
+ */
10
+ export declare const toClassName: (t: string) => ClassName;
11
+ /**
12
+ * Name of a "Discriminant" in a "Discriminated Union".
13
+ *
14
+ * (Background)[https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions]:
15
+ * A "discriminated union" in TypeScript is a union type with a common, singleton type property — the discriminant.
16
+ * E.g.:
17
+ * ```ts
18
+ * type Shape = Square | Rectangle | Circle
19
+ * type Square = { kind: "square"; size: number }
20
+ * type Rectangle = { kind: "rectangle"; width: number; height: number }
21
+ * type Circle = { kind: "circle"; radius: number }
22
+ * ```
23
+ * In the above example, the `kind` property is the discriminant.
24
+ */
25
+ export type DiscriminantName = string & {
26
+ readonly ___type: 'DiscriminantName';
27
+ };
28
+ /**
29
+ * Brands a raw string as a discriminant.
30
+ */
31
+ export declare const toDiscriminantName: (t: string) => DiscriminantName;
32
+ /**
33
+ * The name of a model, e.g. "Aggregation".
34
+ */
35
+ export type ModelName = string & {
36
+ readonly ___type: 'ModelName';
37
+ };
38
+ /**
39
+ * Brands a raw string to a ModelName identifier.
40
+ */
41
+ export declare const toModelName: (t: string) => ModelName;
1
42
  /**
2
43
  * Name of a Typescript type, e.g. "Aggregation".
3
44
  */
@@ -9,25 +50,35 @@ export type TypeName = string & {
9
50
  */
10
51
  export declare const toTypeName: (t: string) => TypeName;
11
52
  /**
12
- * The name of a function (e.g. "toAggregation").
13
- */
14
- export type Fnction = string & {
15
- readonly ___type: 'FunctionName';
53
+ * The name of a model field, e.g. "name".
54
+ */
55
+ export type FieldName = string & {
56
+ readonly ___type: 'FieldName';
16
57
  };
17
58
  /**
18
- * Converts a string to a branded function name.
59
+ * Brands a raw string to a FieldName identifier.
19
60
  */
20
- export declare const toFunction: (t: string) => Fnction;
61
+ export declare const toFieldName: (t: string) => FieldName;
21
62
  /**
22
- * The name of a class, e.g. "AggregationRepository".
63
+ * The name of an Enum, e.g. "Language".
64
+ */
65
+ export type EnumName = string & {
66
+ readonly ___type: 'EnumName';
67
+ };
68
+ /**
69
+ * Brands a raw string to an EnumName identifier.
23
70
  */
24
- export type ClassName = string & {
25
- readonly ___type: 'ClassName';
71
+ export declare const toEnumName: (t: string) => EnumName;
72
+ /**
73
+ * The name of a function (e.g. "toAggregation").
74
+ */
75
+ export type FunctionName = string & {
76
+ readonly ___type: 'FunctionName';
26
77
  };
27
78
  /**
28
- * Converts a raw string to a branded ClassName.
79
+ * Converts a string to a branded function name.
29
80
  */
30
- export declare const toClassName: (t: string) => ClassName;
81
+ export declare const toFunctionName: (t: string) => FunctionName;
31
82
  /**
32
83
  * The name of a variable or a property in the generated code.
33
84
  */
@@ -73,4 +124,4 @@ export declare const toPath: (t: string) => Path;
73
124
  /**
74
125
  * Branded string values that can be used as import statement values in the generators
75
126
  */
76
- export type ImportableTypes = Fnction | ClassName | TypeName | VariableName;
127
+ export type ImportableTypes = FunctionName | ClassName | TypeName | VariableName;
@@ -1,22 +1,42 @@
1
1
  "use strict";
2
2
  // MARK: - Generated content related types
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.toPath = exports.toFolderName = exports.toFileName = exports.toVariableName = exports.toClassName = exports.toFunction = exports.toTypeName = void 0;
4
+ exports.toPath = exports.toFolderName = exports.toFileName = exports.toVariableName = exports.toFunctionName = exports.toEnumName = exports.toFieldName = exports.toTypeName = exports.toModelName = exports.toDiscriminantName = exports.toClassName = void 0;
5
+ /**
6
+ * Converts a raw string to a branded ClassName.
7
+ */
8
+ const toClassName = (t) => t;
9
+ exports.toClassName = toClassName;
10
+ /**
11
+ * Brands a raw string as a discriminant.
12
+ */
13
+ const toDiscriminantName = (t) => t;
14
+ exports.toDiscriminantName = toDiscriminantName;
15
+ /**
16
+ * Brands a raw string to a ModelName identifier.
17
+ */
18
+ const toModelName = (t) => t;
19
+ exports.toModelName = toModelName;
5
20
  /**
6
21
  * Brands a raw string to a TypeScript Type identifier.
7
22
  */
8
23
  const toTypeName = (t) => t;
9
24
  exports.toTypeName = toTypeName;
10
25
  /**
11
- * Converts a string to a branded function name.
26
+ * Brands a raw string to a FieldName identifier.
12
27
  */
13
- const toFunction = (t) => t;
14
- exports.toFunction = toFunction;
28
+ const toFieldName = (t) => t;
29
+ exports.toFieldName = toFieldName;
15
30
  /**
16
- * Converts a raw string to a branded ClassName.
31
+ * Brands a raw string to an EnumName identifier.
17
32
  */
18
- const toClassName = (t) => t;
19
- exports.toClassName = toClassName;
33
+ const toEnumName = (t) => t;
34
+ exports.toEnumName = toEnumName;
35
+ /**
36
+ * Converts a string to a branded function name.
37
+ */
38
+ const toFunctionName = (t) => t;
39
+ exports.toFunctionName = toFunctionName;
20
40
  /**
21
41
  * Converts a string to a branded VariableName.
22
42
  */
@@ -31,5 +31,6 @@ export declare const conjugateNames: (name: string) => {
31
31
  pluralized: string;
32
32
  uncapitalizedPlural: string;
33
33
  uncapitalized: string;
34
+ capitalized: string;
34
35
  capitalizedPlural: string;
35
36
  };
@@ -113,6 +113,7 @@ const conjugateNames = (name) => ({
113
113
  pluralized: (0, exports.capitalize)((0, exports.pluralize)(name)),
114
114
  uncapitalizedPlural: (0, exports.uncapitalize)((0, exports.pluralize)(name)),
115
115
  uncapitalized: (0, exports.uncapitalize)(name),
116
+ capitalized: (0, exports.capitalize)(name),
116
117
  capitalizedPlural: (0, exports.capitalize)((0, exports.pluralize)(name)),
117
118
  });
118
119
  exports.conjugateNames = conjugateNames;
@@ -62,7 +62,7 @@ function isModelNotIgnored(model) {
62
62
  function parseModelCore({ dmmfModel, config, }) {
63
63
  const attributes = (0, attributes_1.getModelAttributes)(dmmfModel);
64
64
  return {
65
- name: (0, string_1.toPascalCase)(dmmfModel.name),
65
+ name: Types.toModelName((0, string_1.toPascalCase)(dmmfModel.name)),
66
66
  description: attributes.description,
67
67
  typeName: Types.toTypeName((0, string_1.toPascalCase)(dmmfModel.name)),
68
68
  sourceName: dmmfModel.name,
@@ -121,7 +121,7 @@ function parseModel({ dmmfModel, enums, models, config, }) {
121
121
  .map((dmmfField) => {
122
122
  const attributes = (0, attributes_1.getFieldAttributes)(dmmfField);
123
123
  const shared = {
124
- name: (0, string_1.toCamelCase)(dmmfField.name),
124
+ name: Types.toFieldName((0, string_1.toCamelCase)(dmmfField.name)),
125
125
  description: attributes.description,
126
126
  sourceName: dmmfField.name,
127
127
  isRequired: dmmfField.isRequired,
@@ -136,7 +136,7 @@ function parseModel({ dmmfModel, enums, models, config, }) {
136
136
  if (!refField) {
137
137
  (0, error_1.throwError)(`Investigate: Relation field ${dmmfField.name} not found.`);
138
138
  }
139
- return Object.assign(Object.assign({ kind: 'relation' }, shared), { relatedModelBacklinkFieldName: refField.name, typeName: Types.toTypeName(dmmfField.type), unbrandedTypeName: getTsTypeForId(dmmfField), relationToModel: refModel });
139
+ return Object.assign(Object.assign({ kind: 'relation' }, shared), { relatedModelBacklinkFieldName: Types.toFieldName(refField.name), typeName: Types.toTypeName(dmmfField.type), unbrandedTypeName: getTsTypeForId(dmmfField), relationToModel: refModel });
140
140
  }
141
141
  if (dmmfField.isId) {
142
142
  const isGeneratedField = isAutoIncrementField(dmmfField) || isUUIDField(dmmfField);
@@ -281,7 +281,7 @@ function isFieldIgnored({ field }) {
281
281
  function parseEnum({ dmmfEnum, config }) {
282
282
  const attributes = (0, attributes_1.getEnumAttributes)(dmmfEnum);
283
283
  return {
284
- name: dmmfEnum.name,
284
+ name: Types.toEnumName(dmmfEnum.name),
285
285
  description: attributes.description,
286
286
  attributes,
287
287
  tsTypeName: Types.toTypeName((0, string_1.toPascalCase)(dmmfEnum.name)),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postxl/generator",
3
- "version": "0.38.2",
3
+ "version": "0.40.0",
4
4
  "main": "./dist/generator.js",
5
5
  "typings": "./dist/generator.d.ts",
6
6
  "bin": {
@@ -34,7 +34,7 @@
34
34
  "@types/eslint": "^8.44.7",
35
35
  "@types/jest": "^29.5.0",
36
36
  "@types/node": "18.15.10",
37
- "jest": "29.5.0",
37
+ "jest": "29.7.0",
38
38
  "prisma": "5.2.0",
39
39
  "ts-jest": "29.0.5",
40
40
  "ts-node": "10.9.1",
@@ -1,356 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateSeedService = void 0;
4
- const meta_1 = require("../../lib/meta");
5
- /**
6
- * Generates index file for all seed files.
7
- */
8
- function generateSeedService({ models }) {
9
- const creates = [];
10
- const updates = [];
11
- const upserts = [];
12
- const deletes = [];
13
- for (const model of models) {
14
- const modelMeta = (0, meta_1.getModelMetadata)({ model });
15
- creates.push(`await this.create({ name: '${modelMeta.userFriendlyName}', data: data.${modelMeta.seed.constantName}?.create, repo: this.dataService.${modelMeta.data.dataServiceName}, execution })`);
16
- updates.push(`await this.update({ name: '${modelMeta.userFriendlyName}', data: data.${modelMeta.seed.constantName}?.update, repo: this.dataService.${modelMeta.data.dataServiceName}, execution })`);
17
- upserts.push(`await this.upsert({ name: '${modelMeta.userFriendlyName}', data: data.${modelMeta.seed.constantName}?.upsert, repo: this.dataService.${modelMeta.data.dataServiceName}, execution })`);
18
- deletes.push(`await this.delete({ name: '${modelMeta.userFriendlyName}', data: data.${modelMeta.seed.constantName}?.delete, repo: this.dataService.${modelMeta.data.dataServiceName}, execution })`);
19
- }
20
- return `
21
- import { Injectable, Logger } from '@nestjs/common'
22
- import { ExhaustiveSwitchCheck, format, pluralize } from '@pxl/common'
23
- import { DataService, Repository } from '@pxl/data'
24
- import { DbService } from '@pxl/db'
25
- import { XlPortService } from '@pxl/xlport'
26
-
27
- import { ActionExecution, DispatcherService } from '@pxl/actions'
28
- import { RootUserService } from '@pxl/root-user'
29
-
30
- import {
31
- ActionPreparation_Seed,
32
- ActionPreparation_Seed_Excel,
33
- Action_Seed,
34
- Action_Seed_CustomLogic,
35
- Action_Seed_Excel,
36
- createActionSeedExcel,
37
- } from './action.types'
38
- import { SeedData } from './seed-data.type'
39
- import { CreateDTO, UpdateDTO } from '@pxl/types'
40
- import { SEED_MIGRATIONS } from '@pxl/seed-data'
41
-
42
- // If true, the seed data will be created/updated one by one, instead of all at once (which is the default).
43
- // Also, each item will be logged to the console.
44
- // This should help to quickly identify the item that causes an error in a bulk operation.
45
- const DEBUG = false
46
-
47
- @Injectable()
48
- export class SeedService {
49
- private readonly logger = new Logger(SeedService.name)
50
-
51
- constructor(
52
- private readonly dataService: DataService,
53
- private readonly xlPortService: XlPortService,
54
- private readonly dbService: DbService,
55
- private readonly dispatcherService: DispatcherService,
56
- private readonly rootUserService: RootUserService,
57
- ) {
58
- dispatcherService.seedService = this
59
- }
60
-
61
- /**
62
- * To be called on application startup. Will validate the seed data migrations and execute any pending migrations.
63
- *
64
- * Each migration is an action, i.e. will be dispatched via the default dispatcher service and logged like any other action.
65
- */
66
- public async seed() {
67
- this.verifyIntegrity()
68
-
69
- let executed = 0
70
- for (const migration of SEED_MIGRATIONS) {
71
- // Skip any migrations that have already been executed
72
- const migrationExists = await this.dbService.action.findFirst({
73
- where: { migrationOrder: migration.order, status: 'Success' },
74
- })
75
- if (migrationExists) {
76
- continue
77
- }
78
-
79
- const action = await this.convertToAction(migration)
80
-
81
- this.logger.log(\`Executing seed migration \${migration.order} - \${migration.name}\`)
82
- await this.dispatcherService.dispatch({ action, user: this.rootUserService.rootUser })
83
-
84
- executed++
85
- }
86
-
87
- if (executed === 0) {
88
- if (SEED_MIGRATIONS.length > 0) {
89
- this.logger.log(\`✅ All \${format(SEED_MIGRATIONS.length)} seed migrations have already been applied executed\`)
90
- } else {
91
- this.logger.log(\`No seed migrations found.\`)
92
- }
93
- } else {
94
- if (executed === SEED_MIGRATIONS.length) {
95
- this.logger.log(\`✅ Executed all \${format(executed)} seed migrations\`)
96
- } else {
97
- this.logger.log(
98
- \`✅ Executed \${format(executed)} seed migrations, skipped \${format(SEED_MIGRATIONS.length - executed)} previous migrations\`,
99
- )
100
- }
101
- }
102
- }
103
-
104
- private async convertToAction(migration: ActionPreparation_Seed): Promise<Action_Seed> {
105
- switch (migration.type) {
106
- case 'data':
107
- return migration
108
- case 'excel':
109
- return await this.convertExcelToAction(migration)
110
- case 'custom':
111
- return migration
112
- default:
113
- throw new ExhaustiveSwitchCheck(migration)
114
- }
115
- }
116
-
117
- /**
118
- * Will be call by the dispatcher service to dispatch the given action.
119
- */
120
- public async dispatch({ action, execution }: { action: Action_Seed; execution: ActionExecution }) {
121
- switch (action.type) {
122
- case 'data':
123
- return this.uploadDataSteps({ steps: action.payload, execution })
124
- case 'excel':
125
- return this.uploadData({ data: action.payload.data, execution })
126
- case 'custom':
127
- return this.handleCustomLogic({ action, execution })
128
-
129
- default:
130
- throw new ExhaustiveSwitchCheck(action)
131
- }
132
- }
133
-
134
- /**
135
- * Checks if the order of the migrations is correct and if there are any missing migrations.
136
- */
137
- private verifyIntegrity() {
138
- let startOrder = 0
139
- for (const { name, order } of SEED_MIGRATIONS) {
140
- if (order <= startOrder) {
141
- throw new Error(
142
- \`Invalid migration order. The order of migration "\${name}" must be after \${startOrder} but is \${order}. Is the order field correct and the order in the SEED_MIGRATIONS array correct?\`,
143
- )
144
- }
145
- startOrder = order
146
- }
147
- }
148
-
149
- /**
150
- * Reads the Excel file and parses it using the provided decoder.
151
- * If the parsing is successful, the data is packaged into an Action_Seed_Excel.
152
- */
153
- private async convertExcelToAction({
154
- name,
155
- order,
156
- filename,
157
- decoder,
158
- }: ActionPreparation_Seed_Excel): Promise<Action_Seed_Excel> {
159
- const xlData = await this.xlPortService.importFromFile(filename)
160
- if (xlData.status === 'error') {
161
- this.logger.error(xlData.message)
162
- throw new Error(\`Error importing Excel data\`)
163
- }
164
-
165
- const dataParsed = decoder.safeParse(xlData.data.tables)
166
- if (!dataParsed.success) {
167
- this.logger.error(dataParsed.error.message)
168
- throw new Error(\`Error parsing Excel seed data\`)
169
- }
170
-
171
- return createActionSeedExcel({ name, order, filename, data: dataParsed.data })
172
- }
173
-
174
- /**
175
- * Executes the custom logic of the given migration.
176
- * To do this, we retrieve the handler from the migration and call it, injecting the data service into the handler.
177
- */
178
- private handleCustomLogic({ action, execution }: { action: Action_Seed_CustomLogic; execution: ActionExecution }) {
179
- const actionPreparation = SEED_MIGRATIONS.find((m) => m.order === action.order)
180
- if (!actionPreparation) {
181
- throw new Error(\`Cannot find custom handler for migration with order \${action.order}\`)
182
- }
183
- if (actionPreparation.type !== 'custom') {
184
- throw new Error(\`Cannot find custom handler for migration with order \${action.order}\`)
185
- }
186
- return actionPreparation.handler({ action, data: this.dataService, execution })
187
- }
188
- /**
189
- * Executes the data loading step by step
190
- */
191
- private async uploadDataSteps({ steps, execution }: { steps: SeedData[]; execution: ActionExecution }) {
192
- let index = 0
193
- for (const step of steps) {
194
- this.logger.log(\`Uploading data step \${++index}/\${steps.length}\`)
195
- await this.uploadData({ data: step, execution })
196
- }
197
- }
198
-
199
- private async uploadData({ data, execution }: { data: SeedData; execution: ActionExecution }) {
200
- // NOTE: the order of these calls is important, because of foreign key constraints
201
- // The current order is based on the order of the models in the schema
202
- // Change the order based on your needs.
203
- // Attention: Depending on the dependencies in seed data, you may also have to take care of relations,
204
- // e.g. by removing any foreign key first - and then update these after all data is created.
205
- // Alternatively, you can also do this in multiple steps
206
- ${creates.join('\n')}
207
-
208
- ${updates.join('\n')}
209
-
210
- ${upserts.join('\n')}
211
-
212
- ${deletes.join('\n')}
213
- }
214
-
215
- private async create<T extends { id: ID }, ID extends number | string | boolean>({
216
- name,
217
- data,
218
- repo,
219
- execution,
220
- }: {
221
- name: string
222
- data: CreateDTO<T, ID>[] | undefined
223
- repo: Repository<T, ID>
224
- execution: ActionExecution
225
- }): Promise<void> {
226
- if (!data) {
227
- return
228
- }
229
- if (!DEBUG) {
230
- await repo.createMany?.({ items: data, execution })
231
- } else {
232
- let i = 0
233
- for (const item of data) {
234
- i++
235
- this.logger.log(\`Creating \${name} \${i} - \${getItemDescription(item)}\`)
236
- try {
237
- await repo.create?.({ item, execution })
238
- } catch (error) {
239
- this.logger.error(\`Error creating \${name} \${i} - \${getItemDescription(item)}\`, error)
240
- throw error
241
- }
242
- }
243
- }
244
- this.logger.log(\`✅ Created \${format(data.length)} \${pluralize(name)}\`)
245
- }
246
-
247
- private async update<T extends { id: ID }, ID extends number | string | boolean>({
248
- name,
249
- data,
250
- repo,
251
- execution,
252
- }: {
253
- name: string
254
- data: UpdateDTO<T, ID>[] | undefined
255
- repo: Repository<T, ID>
256
- execution: ActionExecution
257
- }): Promise<void> {
258
- if (!data) {
259
- return
260
- }
261
- if (!DEBUG) {
262
- await repo.updateMany?.({ items: data, execution })
263
- } else {
264
- let i = 0
265
- for (const item of data) {
266
- i++
267
- this.logger.log(\`Updating \${name} \${i} - \${getItemDescription(item)}\`)
268
- try {
269
- await repo.update?.({ item, execution })
270
- } catch (error) {
271
- this.logger.error(\`Error updating \${name} \${i} - \${getItemDescription(item)}\`, error)
272
- throw error
273
- }
274
- }
275
- }
276
- this.logger.log(\`✅ Updated \${format(data.length)} \${pluralize(name)}\`)
277
- }
278
-
279
- private async upsert<T extends { id: ID }, ID extends number | string | boolean>({
280
- name,
281
- data,
282
- repo,
283
- execution,
284
- }: {
285
- name: string
286
- data: (CreateDTO<T, ID> | UpdateDTO<T, ID>)[] | undefined
287
- repo: Repository<T, ID>
288
- execution: ActionExecution
289
- }): Promise<void> {
290
- if (!data) {
291
- return
292
- }
293
- if (!DEBUG) {
294
- await repo.upsertMany?.({ items: data, execution })
295
- } else {
296
- let i = 0
297
- for (const item of data) {
298
- i++
299
- this.logger.log(\`Upserting \${name} \${i} - \${getItemDescription(item)}\`)
300
- try {
301
- await repo.upsert?.({ item, execution })
302
- } catch (error) {
303
- this.logger.error(\`Error upserting \${name} \${i} - \${getItemDescription(item)}\`, error)
304
- throw error
305
- }
306
- }
307
- }
308
- this.logger.log(\`✅ Upserted \${format(data.length)} \${pluralize(name)}\`)
309
- }
310
-
311
- private async delete<T extends { id: ID }, ID extends number | string | boolean>({
312
- name,
313
- data,
314
- repo,
315
- execution,
316
- }: {
317
- name: string
318
- data: ID[] | undefined
319
- repo: Repository<T, ID>
320
- execution: ActionExecution
321
- }): Promise<void> {
322
- if (!data) {
323
- return
324
- }
325
- if (!DEBUG) {
326
- await repo.deleteMany?.({ ids: data, execution })
327
- } else {
328
- let i = 0
329
- for (const id of data) {
330
- i++
331
- this.logger.log(\`Deleting \${name} \${i} - id: \${id.toString()}\`)
332
- try {
333
- await repo.delete?.({ id, execution })
334
- } catch (error) {
335
- this.logger.error(\`Error deleting \${name} \${i} - id: \${id.toString()}\`, error)
336
- throw error
337
- }
338
- }
339
- }
340
- this.logger.log(\`✅ Deleted \${format(data.length)} \${pluralize(name)}\`)
341
- }
342
- }
343
-
344
- function getItemDescription<T extends Partial<{ id: ID }>, ID extends number | string | boolean>(item: T): string {
345
- const id = \`id: \${item.id !== undefined ? item.id.toString() : 'not provided'}\`
346
-
347
- // If the item has a name, we can assume it is a string and add it to the description
348
- if ('name' in item) {
349
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
350
- return \`\${id}, name: \${item.name}\`
351
- }
352
- return id
353
- }
354
- `;
355
- }
356
- exports.generateSeedService = generateSeedService;