@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.
- package/dist/generator.js +43 -21
- package/dist/generators/enums/types.generator.js +1 -1
- package/dist/generators/indices/businesslogic-actiontypes.generator.js +1 -1
- package/dist/generators/indices/businesslogic-update-module.generator.js +2 -2
- package/dist/generators/indices/businesslogic-update-service.generator.js +1 -1
- package/dist/generators/indices/businesslogic-view-module.generator.js +2 -2
- package/dist/generators/indices/businesslogic-view-service.generator.js +1 -1
- package/dist/generators/indices/{seed-service.generator.d.ts → data-types.generator.d.ts} +2 -2
- package/dist/generators/indices/data-types.generator.js +48 -0
- package/dist/generators/indices/datamock-module.generator.js +11 -11
- package/dist/generators/indices/datamocker.generator.js +1 -1
- package/dist/generators/indices/datamodule.generator.js +34 -52
- package/dist/generators/indices/dataservice.generator.js +202 -9
- package/dist/generators/indices/dispatcher-service.generator.js +20 -10
- package/dist/generators/indices/emptydatabasemigration.generator.d.ts +2 -0
- package/dist/generators/indices/emptydatabasemigration.generator.js +14 -7
- package/dist/generators/indices/importexport-convert-import-functions.generator.d.ts +9 -0
- package/dist/generators/indices/importexport-convert-import-functions.generator.js +528 -0
- package/dist/generators/indices/importexport-exporter-class.generator.d.ts +9 -0
- package/dist/generators/indices/importexport-exporter-class.generator.js +116 -0
- package/dist/generators/indices/importexport-import-service.generator.d.ts +9 -0
- package/dist/generators/indices/importexport-import-service.generator.js +563 -0
- package/dist/generators/indices/{seeddata-type.generator.d.ts → importexport-types.generator.d.ts} +2 -2
- package/dist/generators/indices/importexport-types.generator.js +234 -0
- package/dist/generators/indices/repositories.generator.js +8 -8
- package/dist/generators/indices/seed-migration.generator.js +1 -1
- package/dist/generators/indices/seed-template.generator.js +1 -1
- package/dist/generators/indices/selectors.generator.d.ts +7 -0
- package/dist/generators/indices/selectors.generator.js +82 -0
- package/dist/generators/indices/{seed-template-decoder.generator.d.ts → testdata-service.generator.d.ts} +2 -2
- package/dist/generators/indices/testdata-service.generator.js +71 -0
- package/dist/generators/models/businesslogic-update.generator.js +6 -6
- package/dist/generators/models/businesslogic-view.generator.js +4 -4
- package/dist/generators/models/importexport-decoder.generator.d.ts +23 -0
- package/dist/generators/models/importexport-decoder.generator.js +234 -0
- package/dist/generators/models/react.generator/library.generator.js +4 -0
- package/dist/generators/models/react.generator/modals.generator.js +35 -8
- package/dist/generators/models/repository.generator.js +156 -18
- package/dist/generators/models/route.generator.js +2 -2
- package/dist/generators/models/stub.generator.js +1 -1
- package/dist/generators/models/types.generator.js +1 -1
- package/dist/lib/id-collector.d.ts +43 -0
- package/dist/lib/id-collector.js +53 -0
- package/dist/lib/imports.d.ts +1 -1
- package/dist/lib/meta.d.ts +480 -122
- package/dist/lib/meta.js +187 -74
- package/dist/lib/schema/schema.d.ts +58 -43
- package/dist/lib/schema/types.d.ts +63 -12
- package/dist/lib/schema/types.js +27 -7
- package/dist/lib/utils/string.d.ts +1 -0
- package/dist/lib/utils/string.js +1 -0
- package/dist/prisma/parse.js +4 -4
- package/package.json +2 -2
- package/dist/generators/indices/seed-service.generator.js +0 -356
- package/dist/generators/indices/seed-template-decoder.generator.js +0 -151
- 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
|
|
13
|
-
|
|
14
|
-
export type
|
|
15
|
-
readonly ___type: '
|
|
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
|
-
*
|
|
59
|
+
* Brands a raw string to a FieldName identifier.
|
|
19
60
|
*/
|
|
20
|
-
export declare const
|
|
61
|
+
export declare const toFieldName: (t: string) => FieldName;
|
|
21
62
|
/**
|
|
22
|
-
* The name of
|
|
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
|
|
25
|
-
|
|
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
|
|
79
|
+
* Converts a string to a branded function name.
|
|
29
80
|
*/
|
|
30
|
-
export declare const
|
|
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 =
|
|
127
|
+
export type ImportableTypes = FunctionName | ClassName | TypeName | VariableName;
|
package/dist/lib/schema/types.js
CHANGED
|
@@ -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.
|
|
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
|
-
*
|
|
26
|
+
* Brands a raw string to a FieldName identifier.
|
|
12
27
|
*/
|
|
13
|
-
const
|
|
14
|
-
exports.
|
|
28
|
+
const toFieldName = (t) => t;
|
|
29
|
+
exports.toFieldName = toFieldName;
|
|
15
30
|
/**
|
|
16
|
-
*
|
|
31
|
+
* Brands a raw string to an EnumName identifier.
|
|
17
32
|
*/
|
|
18
|
-
const
|
|
19
|
-
exports.
|
|
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
|
*/
|
package/dist/lib/utils/string.js
CHANGED
|
@@ -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;
|
package/dist/prisma/parse.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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;
|