@postxl/generator 0.39.0 → 0.40.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/generator.js +37 -21
  2. package/dist/generators/indices/businesslogic-update-module.generator.js +1 -1
  3. package/dist/generators/indices/businesslogic-view-module.generator.js +1 -1
  4. package/dist/generators/indices/{seed-service.generator.d.ts → data-types.generator.d.ts} +2 -2
  5. package/dist/generators/indices/data-types.generator.js +48 -0
  6. package/dist/generators/indices/datamock-module.generator.js +7 -7
  7. package/dist/generators/indices/datamodule.generator.js +13 -34
  8. package/dist/generators/indices/dataservice.generator.js +201 -8
  9. package/dist/generators/indices/dispatcher-service.generator.js +14 -5
  10. package/dist/generators/indices/importexport-convert-import-functions.generator.d.ts +9 -0
  11. package/dist/generators/indices/importexport-convert-import-functions.generator.js +528 -0
  12. package/dist/generators/indices/{seed-template-decoder.generator.d.ts → importexport-exporter-class.generator.d.ts} +2 -2
  13. package/dist/generators/indices/importexport-exporter-class.generator.js +116 -0
  14. package/dist/generators/indices/importexport-import-service.generator.d.ts +9 -0
  15. package/dist/generators/indices/importexport-import-service.generator.js +563 -0
  16. package/dist/generators/indices/{seeddata-type.generator.d.ts → importexport-types.generator.d.ts} +2 -2
  17. package/dist/generators/indices/importexport-types.generator.js +234 -0
  18. package/dist/generators/indices/repositories.generator.js +7 -7
  19. package/dist/generators/indices/seed-template.generator.js +1 -1
  20. package/dist/generators/indices/testdata-service.generator.js +5 -4
  21. package/dist/generators/models/businesslogic-update.generator.js +5 -5
  22. package/dist/generators/models/businesslogic-view.generator.js +3 -3
  23. package/dist/generators/models/importexport-decoder.generator.d.ts +23 -0
  24. package/dist/generators/models/importexport-decoder.generator.js +234 -0
  25. package/dist/generators/models/repository.generator.js +50 -21
  26. package/dist/lib/imports.d.ts +1 -1
  27. package/dist/lib/meta.d.ts +468 -134
  28. package/dist/lib/meta.js +187 -80
  29. package/dist/lib/schema/schema.d.ts +54 -43
  30. package/dist/lib/schema/types.d.ts +63 -12
  31. package/dist/lib/schema/types.js +27 -7
  32. package/dist/lib/utils/string.d.ts +1 -0
  33. package/dist/lib/utils/string.js +4 -0
  34. package/dist/prisma/parse.js +4 -4
  35. package/package.json +1 -1
  36. package/dist/generators/indices/seed-service.generator.js +0 -354
  37. package/dist/generators/indices/seed-template-decoder.generator.js +0 -151
  38. package/dist/generators/indices/seeddata-type.generator.js +0 -42
@@ -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
  };
@@ -53,6 +53,9 @@ const irregularPlurals = {
53
53
  datum: 'data',
54
54
  diagnosis: 'diagnoses',
55
55
  focus: 'foci',
56
+ // While `histories` is the plural of `history`, in a model context, `history` makes more sense:
57
+ // If you have a model called `UserHistory`, calling the table `UserHistories` would be confusing.
58
+ history: 'history',
56
59
  hypothesis: 'hypotheses',
57
60
  index: 'indices',
58
61
  matrix: 'matrices',
@@ -113,6 +116,7 @@ const conjugateNames = (name) => ({
113
116
  pluralized: (0, exports.capitalize)((0, exports.pluralize)(name)),
114
117
  uncapitalizedPlural: (0, exports.uncapitalize)((0, exports.pluralize)(name)),
115
118
  uncapitalized: (0, exports.uncapitalize)(name),
119
+ capitalized: (0, exports.capitalize)(name),
116
120
  capitalizedPlural: (0, exports.capitalize)((0, exports.pluralize)(name)),
117
121
  });
118
122
  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.39.0",
3
+ "version": "0.40.1",
4
4
  "main": "./dist/generator.js",
5
5
  "typings": "./dist/generator.d.ts",
6
6
  "bin": {
@@ -1,354 +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 /* ts */ `
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 { IActionExecution, DispatcherService } from '@pxl/actions'
28
-
29
- import {
30
- ActionPreparation_Seed,
31
- ActionPreparation_Seed_Excel,
32
- Action_Seed,
33
- Action_Seed_CustomLogic,
34
- Action_Seed_Excel,
35
- createActionSeedExcel,
36
- } from './action.types'
37
- import { SeedData } from './seed-data.type'
38
- import { CreateDTO, UpdateDTO } from '@pxl/types'
39
- import { SEED_MIGRATIONS } from '@pxl/seed-data'
40
-
41
- // If true, the seed data will be created/updated one by one, instead of all at once (which is the default).
42
- // Also, each item will be logged to the console.
43
- // This should help to quickly identify the item that causes an error in a bulk operation.
44
- const DEBUG = false
45
-
46
- @Injectable()
47
- export class SeedService {
48
- private readonly logger = new Logger(SeedService.name)
49
-
50
- constructor(
51
- private readonly dataService: DataService,
52
- private readonly xlPortService: XlPortService,
53
- private readonly dbService: DbService,
54
- private readonly dispatcherService: DispatcherService,
55
- ) {
56
- dispatcherService.seedService = this
57
- }
58
-
59
- /**
60
- * To be called on application startup. Will validate the seed data migrations and execute any pending migrations.
61
- *
62
- * Each migration is an action, i.e. will be dispatched via the default dispatcher service and logged like any other action.
63
- */
64
- public async seed() {
65
- this.verifyIntegrity()
66
-
67
- let executed = 0
68
- for (const migration of SEED_MIGRATIONS) {
69
- // Skip any migrations that have already been executed
70
- const migrationExists = await this.dbService.action.findFirst({
71
- where: { migrationOrder: migration.order, status: 'Success' },
72
- })
73
- if (migrationExists) {
74
- continue
75
- }
76
-
77
- const action = await this.convertToAction(migration)
78
-
79
- this.logger.log(\`Executing seed migration \${migration.order} - \${migration.name}\`)
80
- await this.dispatcherService.dispatch({ action, user: this.dataService.users.rootUser })
81
-
82
- executed++
83
- }
84
-
85
- if (executed === 0) {
86
- if (SEED_MIGRATIONS.length > 0) {
87
- this.logger.log(\`✅ All \${format(SEED_MIGRATIONS.length)} seed migrations have already been applied executed\`)
88
- } else {
89
- this.logger.log(\`No seed migrations found.\`)
90
- }
91
- } else {
92
- if (executed === SEED_MIGRATIONS.length) {
93
- this.logger.log(\`✅ Executed all \${format(executed)} seed migrations\`)
94
- } else {
95
- this.logger.log(
96
- \`✅ Executed \${format(executed)} seed migrations, skipped \${format(SEED_MIGRATIONS.length - executed)} previous migrations\`,
97
- )
98
- }
99
- }
100
- }
101
-
102
- private async convertToAction(migration: ActionPreparation_Seed): Promise<Action_Seed> {
103
- switch (migration.type) {
104
- case 'data':
105
- return migration
106
- case 'excel':
107
- return await this.convertExcelToAction(migration)
108
- case 'custom':
109
- return migration
110
- default:
111
- throw new ExhaustiveSwitchCheck(migration)
112
- }
113
- }
114
-
115
- /**
116
- * Will be call by the dispatcher service to dispatch the given action.
117
- */
118
- public async dispatch({ action, execution }: { action: Action_Seed; execution: IActionExecution }) {
119
- switch (action.type) {
120
- case 'data':
121
- return this.uploadDataSteps({ steps: action.payload, execution })
122
- case 'excel':
123
- return this.uploadData({ data: action.payload.data, execution })
124
- case 'custom':
125
- return this.handleCustomLogic({ action, execution })
126
-
127
- default:
128
- throw new ExhaustiveSwitchCheck(action)
129
- }
130
- }
131
-
132
- /**
133
- * Checks if the order of the migrations is correct and if there are any missing migrations.
134
- */
135
- private verifyIntegrity() {
136
- let startOrder = 0
137
- for (const { name, order } of SEED_MIGRATIONS) {
138
- if (order <= startOrder) {
139
- throw new Error(
140
- \`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?\`,
141
- )
142
- }
143
- startOrder = order
144
- }
145
- }
146
-
147
- /**
148
- * Reads the Excel file and parses it using the provided decoder.
149
- * If the parsing is successful, the data is packaged into an Action_Seed_Excel.
150
- */
151
- private async convertExcelToAction({
152
- name,
153
- order,
154
- filename,
155
- decoder,
156
- }: ActionPreparation_Seed_Excel): Promise<Action_Seed_Excel> {
157
- const xlData = await this.xlPortService.importFromFile(filename)
158
- if (xlData.status === 'error') {
159
- this.logger.error(xlData.message)
160
- throw new Error(\`Error importing Excel data\`)
161
- }
162
-
163
- const dataParsed = decoder.safeParse(xlData.data.tables)
164
- if (!dataParsed.success) {
165
- this.logger.error(dataParsed.error.message)
166
- throw new Error(\`Error parsing Excel seed data\`)
167
- }
168
-
169
- return createActionSeedExcel({ name, order, filename, data: dataParsed.data })
170
- }
171
-
172
- /**
173
- * Executes the custom logic of the given migration.
174
- * To do this, we retrieve the handler from the migration and call it, injecting the data service into the handler.
175
- */
176
- private handleCustomLogic({ action, execution }: { action: Action_Seed_CustomLogic; execution: IActionExecution }) {
177
- const actionPreparation = SEED_MIGRATIONS.find((m) => m.order === action.order)
178
- if (!actionPreparation) {
179
- throw new Error(\`Cannot find custom handler for migration with order \${action.order}\`)
180
- }
181
- if (actionPreparation.type !== 'custom') {
182
- throw new Error(\`Cannot find custom handler for migration with order \${action.order}\`)
183
- }
184
- return actionPreparation.handler({ action, data: this.dataService, execution })
185
- }
186
- /**
187
- * Executes the data loading step by step
188
- */
189
- private async uploadDataSteps({ steps, execution }: { steps: SeedData[]; execution: IActionExecution }) {
190
- let index = 0
191
- for (const step of steps) {
192
- this.logger.log(\`Uploading data step \${++index}/\${steps.length}\`)
193
- await this.uploadData({ data: step, execution })
194
- }
195
- }
196
-
197
- private async uploadData({ data, execution }: { data: SeedData; execution: IActionExecution }) {
198
- // NOTE: the order of these calls is important, because of foreign key constraints
199
- // The current order is based on the order of the models in the schema
200
- // Change the order based on your needs.
201
- // Attention: Depending on the dependencies in seed data, you may also have to take care of relations,
202
- // e.g. by removing any foreign key first - and then update these after all data is created.
203
- // Alternatively, you can also do this in multiple steps
204
- ${creates.join('\n')}
205
-
206
- ${updates.join('\n')}
207
-
208
- ${upserts.join('\n')}
209
-
210
- ${deletes.join('\n')}
211
- }
212
-
213
- private async create<T extends { id: ID }, ID extends number | string | boolean>({
214
- name,
215
- data,
216
- repo,
217
- execution,
218
- }: {
219
- name: string
220
- data: CreateDTO<T, ID>[] | undefined
221
- repo: Repository<T, ID>
222
- execution: IActionExecution
223
- }): Promise<void> {
224
- if (!data) {
225
- return
226
- }
227
- if (!DEBUG) {
228
- await repo.createMany?.({ items: data, execution })
229
- } else {
230
- let i = 0
231
- for (const item of data) {
232
- i++
233
- this.logger.log(\`Creating \${name} \${i} - \${getItemDescription(item)}\`)
234
- try {
235
- await repo.create?.({ item, execution })
236
- } catch (error) {
237
- this.logger.error(\`Error creating \${name} \${i} - \${getItemDescription(item)}\`, error)
238
- throw error
239
- }
240
- }
241
- }
242
- this.logger.log(\`✅ Created \${format(data.length)} \${pluralize(name)}\`)
243
- }
244
-
245
- private async update<T extends { id: ID }, ID extends number | string | boolean>({
246
- name,
247
- data,
248
- repo,
249
- execution,
250
- }: {
251
- name: string
252
- data: UpdateDTO<T, ID>[] | undefined
253
- repo: Repository<T, ID>
254
- execution: IActionExecution
255
- }): Promise<void> {
256
- if (!data) {
257
- return
258
- }
259
- if (!DEBUG) {
260
- await repo.updateMany?.({ items: data, execution })
261
- } else {
262
- let i = 0
263
- for (const item of data) {
264
- i++
265
- this.logger.log(\`Updating \${name} \${i} - \${getItemDescription(item)}\`)
266
- try {
267
- await repo.update?.({ item, execution })
268
- } catch (error) {
269
- this.logger.error(\`Error updating \${name} \${i} - \${getItemDescription(item)}\`, error)
270
- throw error
271
- }
272
- }
273
- }
274
- this.logger.log(\`✅ Updated \${format(data.length)} \${pluralize(name)}\`)
275
- }
276
-
277
- private async upsert<T extends { id: ID }, ID extends number | string | boolean>({
278
- name,
279
- data,
280
- repo,
281
- execution,
282
- }: {
283
- name: string
284
- data: (CreateDTO<T, ID> | UpdateDTO<T, ID>)[] | undefined
285
- repo: Repository<T, ID>
286
- execution: IActionExecution
287
- }): Promise<void> {
288
- if (!data) {
289
- return
290
- }
291
- if (!DEBUG) {
292
- await repo.upsertMany?.({ items: data, execution })
293
- } else {
294
- let i = 0
295
- for (const item of data) {
296
- i++
297
- this.logger.log(\`Upserting \${name} \${i} - \${getItemDescription(item)}\`)
298
- try {
299
- await repo.upsert?.({ item, execution })
300
- } catch (error) {
301
- this.logger.error(\`Error upserting \${name} \${i} - \${getItemDescription(item)}\`, error)
302
- throw error
303
- }
304
- }
305
- }
306
- this.logger.log(\`✅ Upserted \${format(data.length)} \${pluralize(name)}\`)
307
- }
308
-
309
- private async delete<T extends { id: ID }, ID extends number | string | boolean>({
310
- name,
311
- data,
312
- repo,
313
- execution,
314
- }: {
315
- name: string
316
- data: ID[] | undefined
317
- repo: Repository<T, ID>
318
- execution: IActionExecution
319
- }): Promise<void> {
320
- if (!data) {
321
- return
322
- }
323
- if (!DEBUG) {
324
- await repo.deleteMany?.({ ids: data, execution })
325
- } else {
326
- let i = 0
327
- for (const id of data) {
328
- i++
329
- this.logger.log(\`Deleting \${name} \${i} - id: \${id.toString()}\`)
330
- try {
331
- await repo.delete?.({ id, execution })
332
- } catch (error) {
333
- this.logger.error(\`Error deleting \${name} \${i} - id: \${id.toString()}\`, error)
334
- throw error
335
- }
336
- }
337
- }
338
- this.logger.log(\`✅ Deleted \${format(data.length)} \${pluralize(name)}\`)
339
- }
340
- }
341
-
342
- function getItemDescription<T extends Partial<{ id: ID }>, ID extends number | string | boolean>(item: T): string {
343
- const id = \`id: \${item.id !== undefined ? item.id.toString() : 'not provided'}\`
344
-
345
- // If the item has a name, we can assume it is a string and add it to the description
346
- if ('name' in item) {
347
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
348
- return \`\${id}, name: \${item.name}\`
349
- }
350
- return id
351
- }
352
- `;
353
- }
354
- exports.generateSeedService = generateSeedService;