@postxl/generator 0.39.0 → 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 (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 +1 -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
package/dist/generator.js CHANGED
@@ -48,24 +48,27 @@ const businesslogic_update_service_generator_1 = require("./generators/indices/b
48
48
  const businesslogic_view_index_generator_1 = require("./generators/indices/businesslogic-view-index.generator");
49
49
  const businesslogic_view_module_generator_1 = require("./generators/indices/businesslogic-view-module.generator");
50
50
  const businesslogic_view_service_generator_1 = require("./generators/indices/businesslogic-view-service.generator");
51
+ const data_types_generator_1 = require("./generators/indices/data-types.generator");
51
52
  const datamock_module_generator_1 = require("./generators/indices/datamock-module.generator");
52
53
  const datamocker_generator_1 = require("./generators/indices/datamocker.generator");
53
54
  const datamodule_generator_1 = require("./generators/indices/datamodule.generator");
54
55
  const dataservice_generator_1 = require("./generators/indices/dataservice.generator");
55
56
  const dispatcher_service_generator_1 = require("./generators/indices/dispatcher-service.generator");
56
57
  const emptydatabasemigration_generator_1 = require("./generators/indices/emptydatabasemigration.generator");
58
+ const importexport_convert_import_functions_generator_1 = require("./generators/indices/importexport-convert-import-functions.generator");
59
+ const importexport_exporter_class_generator_1 = require("./generators/indices/importexport-exporter-class.generator");
60
+ const importexport_import_service_generator_1 = require("./generators/indices/importexport-import-service.generator");
61
+ const importexport_types_generator_1 = require("./generators/indices/importexport-types.generator");
57
62
  const repositories_generator_1 = require("./generators/indices/repositories.generator");
58
63
  const seed_migration_generator_1 = require("./generators/indices/seed-migration.generator");
59
- const seed_service_generator_1 = require("./generators/indices/seed-service.generator");
60
64
  const seed_template_generator_1 = require("./generators/indices/seed-template.generator");
61
- const seed_template_decoder_generator_1 = require("./generators/indices/seed-template-decoder.generator");
62
- const seeddata_type_generator_1 = require("./generators/indices/seeddata-type.generator");
63
65
  const selectors_generator_1 = require("./generators/indices/selectors.generator");
64
66
  const stubs_generator_1 = require("./generators/indices/stubs.generator");
65
67
  const testdata_service_generator_1 = require("./generators/indices/testdata-service.generator");
66
68
  const types_generator_2 = require("./generators/indices/types.generator");
67
69
  const businesslogic_update_generator_1 = require("./generators/models/businesslogic-update.generator");
68
70
  const businesslogic_view_generator_1 = require("./generators/models/businesslogic-view.generator");
71
+ const importexport_decoder_generator_1 = require("./generators/models/importexport-decoder.generator");
69
72
  const react_generator_2 = require("./generators/models/react.generator");
70
73
  const repository_generator_1 = require("./generators/models/repository.generator");
71
74
  const route_generator_1 = require("./generators/models/route.generator");
@@ -80,14 +83,15 @@ const parse_1 = require("./prisma/parse");
80
83
  const CONFIG_SCHEMA = zod_1.z
81
84
  .object({
82
85
  project: zod_1.z.string(),
83
- pathToTypes: zod_1.z.string().optional(),
84
86
  pathToDataLib: zod_1.z.string().optional(),
85
87
  pathToCypress: zod_1.z.string().optional(),
86
88
  pathToE2ELib: zod_1.z.string().optional(),
89
+ pathToImportExport: zod_1.z.string().optional(),
87
90
  pathToActions: zod_1.z.string().optional(),
88
91
  pathToBusinessLogic: zod_1.z.string().optional(),
89
- pathToSeedLib: zod_1.z.string().optional(),
90
92
  pathToSeedData: zod_1.z.string().optional(),
93
+ pathToSeedLib: zod_1.z.string().optional(),
94
+ pathToTypes: zod_1.z.string().optional(),
91
95
  trpcRoutesFolder: zod_1.z.string().optional(),
92
96
  reactFolderOutput: zod_1.z.string().optional(),
93
97
  migrationsFolder: zod_1.z.string().optional(),
@@ -107,25 +111,27 @@ const CONFIG_SCHEMA = zod_1.z
107
111
  dataLibPath: (0, types_1.toPath)(s.pathToDataLib || 'repos'),
108
112
  cypressPath: (0, types_1.toPath)(s.pathToCypress || './e2e/cypress/'),
109
113
  e2eLibPath: (0, types_1.toPath)(s.pathToE2ELib || './e2e/src/'),
114
+ importExportPath: (0, types_1.toPath)(s.pathToImportExport || 'import-export'),
110
115
  actionsPath: (0, types_1.toPath)(s.pathToActions || 'actions'),
111
116
  businessLogicPath: (0, types_1.toPath)(s.pathToBusinessLogic || 'business-logic'),
112
- reactFolderPath: (0, types_1.toPath)(s.reactFolderOutput || 'react'),
117
+ migrationsFolderPath: (0, types_1.toPath)(s.migrationsFolder || 'migrations'),
113
118
  modelTypeDefinitionsPath: (0, types_1.toPath)(s.pathToTypes || 'types'),
114
- seedLibPath: (0, types_1.toPath)(s.pathToSeedLib || 'seed'),
119
+ reactFolderPath: (0, types_1.toPath)(s.reactFolderOutput || 'react'),
115
120
  seedDataPath: (0, types_1.toPath)(s.pathToSeedData || 'seed-data'),
121
+ seedLibPath: (0, types_1.toPath)(s.pathToSeedLib || 'seed'),
116
122
  trpcRoutesFolderPath: (0, types_1.toPath)(s.trpcRoutesFolder || 'trpc'),
117
- migrationsFolderPath: (0, types_1.toPath)(s.migrationsFolder || 'migrations'),
118
123
  },
119
124
  randomSeed: s.randomSeed,
120
125
  force: s.force,
121
126
  disableGenerators: {
122
- types: s.pathToTypes === undefined,
123
- data: s.pathToDataLib === undefined,
124
127
  actions: s.pathToActions === undefined,
125
128
  businessLogic: s.pathToBusinessLogic === undefined,
129
+ data: s.pathToDataLib === undefined,
130
+ importExport: s.pathToImportExport === undefined,
131
+ react: s.reactFolderOutput === undefined,
126
132
  seed: s.pathToSeedLib === undefined,
127
133
  trpc: s.trpcRoutesFolder === undefined,
128
- react: s.reactFolderOutput === undefined,
134
+ types: s.pathToTypes === undefined,
129
135
  },
130
136
  userType: (0, types_1.toTypeName)(`User`),
131
137
  };
@@ -176,13 +182,17 @@ function generate({ models, enums, config, prismaClientPath, logger, }) {
176
182
  if (!config.disableGenerators.seed) {
177
183
  generated.write(`/${meta.seed.filePath}.ts`, (0, seed_generator_1.generateSeedModel)({ model, itemCount: 5, meta }));
178
184
  }
179
- // Generate Repositories
185
+ // Data
180
186
  if (!config.disableGenerators.data) {
181
187
  generated.write(`/${meta.data.stubFilePath}.ts`, (0, stub_generator_1.generateStub)({ model, meta }));
182
- generated.write(`/${meta.data.repoFilePath}.ts`, (0, repository_generator_1.generateRepository)({ model, meta }));
183
- generated.write(`/${meta.data.mockRepoFilePath}.ts`, (0, repository_generator_1.generateMockRepository)({ model, meta }));
188
+ generated.write(`/${meta.data.repository.filePath}.ts`, (0, repository_generator_1.generateRepository)({ model, meta }));
189
+ generated.write(`/${meta.data.mockRepository.filePath}.ts`, (0, repository_generator_1.generateMockRepository)({ model, meta }));
184
190
  generated.write(`/${meta.data.dataMockerStubFilePath}.ts`, (0, stub_generator_1.generateStub)({ model, meta }));
185
191
  }
192
+ // Import-Export
193
+ if (!config.disableGenerators.importExport) {
194
+ generated.write(`/${meta.importExport.decoder.filePath}.ts`, (0, importexport_decoder_generator_1.generateModelImportExportDecoder)({ model, meta }));
195
+ }
186
196
  // Business Logic
187
197
  if (!config.disableGenerators.businessLogic) {
188
198
  generated.write(`/${meta.businessLogic.view.serviceFilePath}.ts`, (0, businesslogic_view_generator_1.generateModelBusinessLogicView)({ model, meta }));
@@ -213,14 +223,15 @@ function generate({ models, enums, config, prismaClientPath, logger, }) {
213
223
  if (!config.disableGenerators.data) {
214
224
  generated.write(`/${meta.data.dataMockModuleFilePath}.ts`, (0, datamock_module_generator_1.generateDataMockModule)({ models, meta }));
215
225
  generated.write(`/${meta.data.dataModuleFilePath}.ts`, (0, datamodule_generator_1.generateDataModule)({ models, meta }));
216
- generated.write(`/${meta.data.dataServiceFilePath}.ts`, (0, dataservice_generator_1.generateDataService)({ models, meta }));
226
+ generated.write(`/${meta.data.dataService.filePath}.ts`, (0, dataservice_generator_1.generateDataService)({ models, meta }));
217
227
  generated.write(`/${meta.data.dataMockerFilePath}.ts`, (0, datamocker_generator_1.generateDataMocker)({ models, meta }));
218
228
  generated.write(`/${meta.data.selectorsFilePath}.ts`, (0, selectors_generator_1.generateSelectors)());
219
229
  generated.write(`/${meta.data.testDataServiceFilePath}.ts`, (0, testdata_service_generator_1.generateTestDataService)({ models, meta }));
220
230
  generated.write(`/${meta.data.dataMockerStubIndexFilePath}.ts`, (0, stubs_generator_1.generateDataMockerStubsIndex)({ models, meta }));
221
- generated.write(`/${meta.data.repositoriesConstFilePath}.ts`, (0, repositories_generator_1.generateRepositoriesArray)({ models, meta }));
222
- generated.write(`/${meta.data.repositoriesIndexFilePath}.ts`, (0, repositories_generator_1.generateRepositoriesIndex)({ models, meta }));
231
+ generated.write(`/${meta.data.repository.constFilePath}.ts`, (0, repositories_generator_1.generateRepositoriesArray)({ models, meta }));
232
+ generated.write(`/${meta.data.repository.indexFilePath}.ts`, (0, repositories_generator_1.generateRepositoriesIndex)({ models, meta }));
223
233
  generated.write(`/${meta.data.stubIndexFilePath}.ts`, (0, stubs_generator_1.generateStubsIndex)({ models, meta }));
234
+ generated.write(`/${meta.data.types.filePath}.ts`, (0, data_types_generator_1.generateDataTypes)({ models, meta }));
224
235
  // We only generate the empty database migration if the migration folder already has an existing migration
225
236
  // Else we would generate a migration that deletes from tables that have not yet been created in the database
226
237
  // We include this check here as the template does not come with any migration - hence this migration should also not be generated
@@ -228,8 +239,16 @@ function generate({ models, enums, config, prismaClientPath, logger, }) {
228
239
  generated.write((0, emptydatabasemigration_generator_1.deriveEmptyDatabaseMigrationFilePath)(meta), (0, emptydatabasemigration_generator_1.generateEmptyDatabaseStoredProcedure)({ models, meta }));
229
240
  }
230
241
  }
242
+ if (!config.disableGenerators.importExport) {
243
+ generated.write(`/${meta.importExport.types.filePath}.ts`, (0, importexport_types_generator_1.generateImportExportTypes)({ models, meta }));
244
+ generated.write(`/${meta.importExport.exporterClass.filePath}.ts`, (0, importexport_exporter_class_generator_1.generateImportExportExporterClass)({ models, meta }));
245
+ generated.write(`/${meta.importExport.importService.filePath}.ts`, (0, importexport_import_service_generator_1.generateImportExportImportService)({ models, meta }));
246
+ generated.write(`/${meta.importExport.decoder.indexFilePath}.ts`, (0, importexport_decoder_generator_1.generateImportExportDecoderIndex)({ models, meta }));
247
+ generated.write(`/${meta.importExport.decoder.fullDecoderFilePath}.ts`, (0, importexport_decoder_generator_1.generateImportExportDecoder)({ models, meta }));
248
+ generated.write(`/${meta.importExport.converterFunctions.filePath}.ts`, (0, importexport_convert_import_functions_generator_1.generateImportExportConvertImportFunctions)({ models, meta }));
249
+ }
231
250
  if (!config.disableGenerators.actions) {
232
- generated.write(`/${meta.actions.dispatcherServiceFilePath}.ts`, (0, dispatcher_service_generator_1.generateActionsDispatcherService)({ models, meta }));
251
+ generated.write(`/${meta.actions.dispatcherService.filePath}.ts`, (0, dispatcher_service_generator_1.generateActionsDispatcherService)({ models, meta }));
233
252
  }
234
253
  if (!config.disableGenerators.businessLogic) {
235
254
  generated.write(`/${meta.businessLogic.view.indexFilePath}.ts`, (0, businesslogic_view_index_generator_1.generateBusinessLogicViewIndex)({ models, meta }));
@@ -241,11 +260,8 @@ function generate({ models, enums, config, prismaClientPath, logger, }) {
241
260
  generated.write(`/${meta.businessLogic.update.actionTypesFilePath}.ts`, (0, businesslogic_actiontypes_generator_1.generateBusinessLogicActionTypes)({ models, meta }));
242
261
  }
243
262
  if (!config.disableGenerators.seed) {
244
- generated.write(`/${meta.seed.seedDataTypeFilePath}.ts`, (0, seeddata_type_generator_1.generateSeedDataType)({ models, meta }));
245
- generated.write(`/${meta.seed.serviceFilePath}.ts`, (0, seed_service_generator_1.generateSeedService)({ models, meta }));
246
263
  generated.write(`/${meta.seedData.initialMigrationFilePath}.ts`, (0, seed_migration_generator_1.generateSeedMigration)({ models, meta }));
247
264
  generated.write(`/${meta.seedData.templateExcelFilePath}`, yield (0, seed_template_generator_1.generateSeedExcelTemplate)({ models }));
248
- generated.write(`/${meta.seedData.templateDecoderFilePath}.ts`, (0, seed_template_decoder_generator_1.generateSeedTemplateDecoder)({ models, meta }));
249
265
  }
250
266
  if (!config.disableGenerators.trpc) {
251
267
  generated.write(`/${meta.trpc.routesFilePath}.ts`, (0, route_generator_1.generateRoutesIndex)({ models, meta }));
@@ -41,7 +41,7 @@ export class ${moduleName} {
41
41
  */
42
42
  static getInstance(): DynamicModule {
43
43
  if (!${moduleName}.cachedModule) {
44
- throw new Error('${moduleName} must be called via .provide first!')
44
+ throw new Error('${moduleName} must be called via .forRoot first!')
45
45
  }
46
46
  return ${moduleName}.cachedModule
47
47
  }
@@ -40,7 +40,7 @@ export class ${moduleName} {
40
40
  */
41
41
  static getInstance(): DynamicModule {
42
42
  if (!${moduleName}.cachedModule) {
43
- throw new Error('${moduleName} must be called via .provide first!')
43
+ throw new Error('${moduleName} must be called via .forRoot first!')
44
44
  }
45
45
  return ${moduleName}.cachedModule
46
46
  }
@@ -1,9 +1,9 @@
1
1
  import { SchemaMetaData } from '../../lib/meta';
2
2
  import { Model } from '../../lib/schema/schema';
3
3
  /**
4
- * Generates index file for all seed files.
4
+ * Generates type for BulkMutations.
5
5
  */
6
- export declare function generateSeedService({ models }: {
6
+ export declare function generateDataTypes({ models, meta }: {
7
7
  models: Model[];
8
8
  meta: SchemaMetaData;
9
9
  }): string;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateDataTypes = void 0;
4
+ const imports_1 = require("../../lib/imports");
5
+ const meta_1 = require("../../lib/meta");
6
+ /**
7
+ * Generates type for BulkMutations.
8
+ */
9
+ function generateDataTypes({ models, meta }) {
10
+ const { types } = meta.data;
11
+ const imports = imports_1.ImportsGenerator.from(types.filePath);
12
+ const dto = meta.types.dto;
13
+ imports.addImports({
14
+ [meta.types.importPath]: [dto.create, dto.update, dto.upsert, dto.genericModel, dto.idType],
15
+ });
16
+ const modelTypes = [];
17
+ for (const model of models) {
18
+ const modelMeta = (0, meta_1.getModelMetadata)({ model });
19
+ imports.addImport({
20
+ items: [modelMeta.types.typeName, modelMeta.types.brandedIdType],
21
+ from: modelMeta.types.importPath,
22
+ });
23
+ modelTypes.push(`${modelMeta.seed.constantName}?: ${types.bulkMutationForModel}<${modelMeta.types.typeName}, ${modelMeta.types.brandedIdType}>`);
24
+ }
25
+ return /* ts */ `
26
+ ${imports.generate()}
27
+
28
+ /**
29
+ * Generic type for BulkMutation for a model.
30
+ */
31
+ export type ${types.bulkMutationForModel}<
32
+ Model extends ${dto.genericModel}<ID>,
33
+ ID extends ${dto.idType}
34
+ > = {
35
+ create?: ${dto.create}<Model, ID>[]
36
+ update?: ${dto.update}<Model, ID>[]
37
+ upsert?: ${dto.upsert}<Model, ID>[]
38
+ delete?: ID[]
39
+ }
40
+
41
+ /**
42
+ * Bulk changes for all models.
43
+ */
44
+ export type ${types.bulkMutation} = {
45
+ ${modelTypes.join('\n')}
46
+ }`;
47
+ }
48
+ exports.generateDataTypes = generateDataTypes;
@@ -34,20 +34,20 @@ function generateDataMockModule({ models, meta }) {
34
34
  const mm = models.map((model) => ({ model, meta: (0, meta_1.getModelMetadata)({ model }) }));
35
35
  const imports = imports_1.ImportsGenerator.from(meta.data.dataMockModuleFilePath).addImports({
36
36
  [meta.data.dataModuleFilePath]: [Types.toVariableName('DataModule')],
37
- [meta.data.dataServiceFilePath]: [Types.toVariableName('DataService')],
37
+ [meta.data.dataService.filePath]: [meta.data.dataService.class],
38
38
  // we need to import the file directly instead via the normal index file as this would cause a circular dependency else
39
- [meta.actions.importPath]: [meta.actions.actionExecutionMock],
39
+ [meta.actions.importPath]: [meta.actions.actionExecution.mock],
40
40
  });
41
41
  for (const { model, meta } of mm) {
42
42
  imports.addImport({ items: [model.typeName], from: meta.types.importPath });
43
- imports.addImport({ items: [meta.data.repositoryClassName], from: meta.data.repoFilePath });
44
- imports.addImport({ items: [meta.data.mockRepositoryClassName], from: meta.data.mockRepoFilePath });
43
+ imports.addImport({ items: [meta.data.repository.className], from: meta.data.repository.filePath });
44
+ imports.addImport({ items: [meta.data.mockRepository.className], from: meta.data.mockRepository.filePath });
45
45
  }
46
46
  const providers = mm
47
47
  .map(({ meta }) => `{
48
- provide: ${meta.data.repositoryClassName},
48
+ provide: ${meta.data.repository.className},
49
49
  useFactory: async () => {
50
- const repository = new ${meta.data.mockRepositoryClassName}()
50
+ const repository = new ${meta.data.mockRepository.className}()
51
51
  if (!!seed && !!seed.${meta.seed.constantName}) {
52
52
  await repository.reInit({items: seed.${meta.seed.constantName}, execution })
53
53
  }
@@ -73,7 +73,7 @@ export class ${meta.data.dataMockModuleName} {
73
73
  const cachedModule = {
74
74
  module: ${meta.data.moduleName},
75
75
  imports: [DbModule.provideMock()],
76
- providers: providers,
76
+ providers,
77
77
  exports: providers,
78
78
  global: true,
79
79
  }
@@ -1,45 +1,21 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  Object.defineProperty(exports, "__esModule", { value: true });
26
3
  exports.generateDataModule = void 0;
27
4
  const imports_1 = require("../../lib/imports");
28
5
  const meta_1 = require("../../lib/meta");
29
- const Types = __importStar(require("../../lib/schema/types"));
30
6
  /**
31
7
  * Generates a data module class.
32
8
  */
33
9
  function generateDataModule({ models, meta }) {
34
10
  const mm = models.map((model) => ({ model, meta: (0, meta_1.getModelMetadata)({ model }) }));
35
11
  const imports = imports_1.ImportsGenerator.from(meta.data.dataModuleFilePath).addImports({
36
- [meta.data.dataServiceFilePath]: [Types.toVariableName('DataService')],
12
+ [meta.data.dataService.filePath]: [meta.data.dataService.class],
37
13
  [meta.data.dataMockModuleFilePath]: [meta.data.dataMockModuleName],
38
14
  });
39
15
  for (const { meta } of mm) {
40
16
  imports.addImport({
41
- items: [meta.data.repositoryClassName],
42
- from: meta.data.repoFilePath,
17
+ items: [meta.data.repository.className],
18
+ from: meta.data.repository.filePath,
43
19
  });
44
20
  }
45
21
  const moduleName = meta.data.moduleName;
@@ -63,7 +39,7 @@ export class ${moduleName} {
63
39
  */
64
40
  static getInstance(): DynamicModule {
65
41
  if (!${moduleName}.cachedModule) {
66
- throw new Error('${moduleName} must be called via .provide first!')
42
+ throw new Error('${moduleName} must be called via .forRoot first!')
67
43
  }
68
44
  return ${moduleName}.cachedModule
69
45
  }
@@ -91,7 +67,7 @@ export class ${moduleName} {
91
67
  // We need to initialize the user repository right at the beginning,
92
68
  // so that we have the rootUser available for the other modules - including
93
69
  // the action and seed modules which will ensure that seed data is created.
94
- const userRepository: FactoryProvider<UserRepository> = {
70
+ const userRepositoryProvider: FactoryProvider<UserRepository> = {
95
71
  provide: UserRepository,
96
72
  inject: [DbService],
97
73
  useFactory: async (dbService: DbService) => {
@@ -101,11 +77,14 @@ export class ${moduleName} {
101
77
  },
102
78
  }
103
79
 
104
- const repositoryProviders = [
105
- userRepository,
80
+ const providers = [
81
+ DataService,
82
+
83
+ userRepositoryProvider,
84
+
106
85
  ${mm
107
86
  .filter((mm) => mm.model.name !== 'User')
108
- .map(({ meta }) => meta.data.repositoryClassName)
87
+ .map(({ meta }) => meta.data.repository.className)
109
88
  .join(',')}
110
89
  ]
111
90
 
@@ -113,8 +92,8 @@ export class ${moduleName} {
113
92
  module: ${moduleName},
114
93
  global: true,
115
94
  imports: [DbModule.forRoot()],
116
- providers: [DataService, ...repositoryProviders],
117
- exports: [DataService, ...repositoryProviders],
95
+ providers,
96
+ exports: providers,
118
97
  }
119
98
 
120
99
  return ${moduleName}.cachedModule
@@ -8,30 +8,55 @@ const meta_1 = require("../../lib/meta");
8
8
  */
9
9
  function generateDataService({ models, meta }) {
10
10
  const mm = models.map((model) => ({ model, meta: (0, meta_1.getModelMetadata)({ model }) }));
11
- const imports = imports_1.ImportsGenerator.from(meta.data.dataServiceFilePath);
11
+ const imports = imports_1.ImportsGenerator.from(meta.data.dataService.filePath);
12
12
  for (const { meta } of mm) {
13
13
  imports.addImport({
14
- items: [meta.data.repositoryClassName],
15
- from: meta.data.repoFilePath,
14
+ items: [meta.data.repository.className],
15
+ from: meta.data.repository.filePath,
16
16
  });
17
17
  }
18
18
  const constructor = mm
19
- .map(({ meta }) => `public ${meta.data.dataServiceName} :${meta.data.repositoryClassName}`)
19
+ .map(({ meta }) => `public ${meta.data.dataServiceName} :${meta.data.repository.className}`)
20
20
  .join(',\n');
21
21
  const initializer = mm.map(({ meta }) => `await this.${meta.data.dataServiceName}.init()`).join('\n');
22
22
  const excelExports = mm
23
- .map(({ meta }) => `${meta.data.excelExportTableName}: mapValues(await this.${meta.data.dataServiceName}.getAll()),`)
23
+ .map(({ meta }) => `${meta.importExport.tableName}: mapValues(await this.${meta.data.dataServiceName}.getAll()),`)
24
24
  .join('\n');
25
25
  const isEmptyChecks = mm.map(({ meta }) => `(await this.${meta.data.dataServiceName}.count()) === 0`).join(' &&');
26
+ // Building blocks for bulk mutations
27
+ imports.addImports({
28
+ [meta.types.importPath]: [meta.types.dto.create, meta.types.dto.update],
29
+ [meta.data.repository.typeFilePath]: [meta.data.repository.typeName],
30
+ [meta.data.types.filePath]: [meta.data.types.bulkMutation],
31
+ [meta.actions.importPath]: [meta.actions.actionExecution.interface],
32
+ });
33
+ const creates = [];
34
+ const updates = [];
35
+ const upserts = [];
36
+ const deletes = [];
37
+ for (const model of models) {
38
+ const modelMeta = (0, meta_1.getModelMetadata)({ model });
39
+ creates.push(`await this.create({ name: '${modelMeta.userFriendlyName}', data: data.${modelMeta.seed.constantName}?.create, repo: this.${modelMeta.data.dataServiceName}, execution })`);
40
+ updates.push(`await this.update({ name: '${modelMeta.userFriendlyName}', data: data.${modelMeta.seed.constantName}?.update, repo: this.${modelMeta.data.dataServiceName}, execution })`);
41
+ upserts.push(`await this.upsert({ name: '${modelMeta.userFriendlyName}', data: data.${modelMeta.seed.constantName}?.upsert, repo: this.${modelMeta.data.dataServiceName}, execution })`);
42
+ deletes.push(`await this.delete({ name: '${modelMeta.userFriendlyName}', data: data.${modelMeta.seed.constantName}?.delete, repo: this.${modelMeta.data.dataServiceName}, execution })`);
43
+ }
26
44
  return /* ts */ `
27
- import { Injectable } from '@nestjs/common'
45
+ import { Injectable, Logger } from '@nestjs/common'
28
46
 
29
- import { mapValues } from '@pxl/common'
47
+ import { format, mapValues, pluralize } from '@pxl/common'
30
48
 
31
49
  ${imports.generate()}
32
50
 
51
+ // If true, the bulk mutation will be executed one by one, instead of all at once (which is the default).
52
+ // Also, each item will be logged to the console.
53
+ // This should help to quickly identify the item that causes an error in a bulk mutation.
54
+ const DEBUG = false
55
+
33
56
  @Injectable()
34
- export class ${meta.data.dataServiceClassName} {
57
+ export class ${meta.data.dataService.class} {
58
+ private readonly logger = new Logger(${meta.data.dataService.class}.name)
59
+
35
60
  constructor(${constructor}) {}
36
61
 
37
62
  public async prepareExcelExport() {
@@ -49,6 +74,174 @@ export class ${meta.data.dataServiceClassName} {
49
74
  ${isEmptyChecks}
50
75
  )
51
76
  }
77
+ public async ${meta.data.dataService.executeBulkMutations}(
78
+ { steps, execution }:
79
+ { steps: ${meta.data.types.bulkMutation}[]; execution: ${meta.actions.actionExecution.interface} }
80
+ ) {
81
+ let index = 0
82
+ for (const step of steps) {
83
+ this.logger.log(\`Uploading data step \${++index}/\${steps.length}\`)
84
+ await this.${meta.data.dataService.executeBulkMutation}({ data: step, execution })
85
+ }
86
+ }
87
+
88
+ public async ${meta.data.dataService.executeBulkMutation}(
89
+ { data, execution }:
90
+ { data: ${meta.data.types.bulkMutation}, execution: ${meta.actions.actionExecution.interface}}
91
+ ) {
92
+ // NOTE: the order of these calls is important, because of foreign key constraints
93
+ // The current order is based on the order of the models in the schema
94
+ // Change the order based on your needs.
95
+ // Attention: Depending on the dependencies in seed data, you may also have to take care of relations,
96
+ // e.g. by removing any foreign key first - and then update these after all data is created.
97
+ // Alternatively, you can also do this in multiple steps
98
+ ${creates.join('\n')}
99
+
100
+ ${updates.join('\n')}
101
+
102
+ ${upserts.join('\n')}
103
+
104
+ ${deletes.join('\n')}
105
+ }
106
+
107
+ private async create<T extends { id: ID }, ID extends number | string | boolean>({
108
+ name,
109
+ data,
110
+ repo,
111
+ execution,
112
+ }: {
113
+ name: string
114
+ data: ${meta.types.dto.create}<T, ID>[] | undefined
115
+ repo: ${meta.data.repository.typeName}<T, ID>
116
+ execution: ${meta.actions.actionExecution.interface}
117
+ }): Promise<void> {
118
+ if (!data) {
119
+ return
120
+ }
121
+ if (!DEBUG) {
122
+ await repo.createMany?.({ items: data, execution })
123
+ } else {
124
+ let i = 0
125
+ for (const item of data) {
126
+ i++
127
+ this.logger.log(\`Creating \${name} \${i} - \${_getItemDescription(item)}\`)
128
+ try {
129
+ await repo.create?.({ item, execution })
130
+ } catch (error) {
131
+ this.logger.error(\`Error creating \${name} \${i} - \${_getItemDescription(item)}\`, error)
132
+ throw error
133
+ }
134
+ }
135
+ }
136
+ this.logger.log(\`✅ Created \${format(data.length)} \${pluralize(name)}\`)
137
+ }
138
+
139
+ private async update<T extends { id: ID }, ID extends number | string | boolean>({
140
+ name,
141
+ data,
142
+ repo,
143
+ execution,
144
+ }: {
145
+ name: string
146
+ data: ${meta.types.dto.update}<T, ID>[] | undefined
147
+ repo: ${meta.data.repository.typeName}<T, ID>
148
+ execution: ${meta.actions.actionExecution.interface}
149
+ }): Promise<void> {
150
+ if (!data) {
151
+ return
152
+ }
153
+ if (!DEBUG) {
154
+ await repo.updateMany?.({ items: data, execution })
155
+ } else {
156
+ let i = 0
157
+ for (const item of data) {
158
+ i++
159
+ this.logger.log(\`Updating \${name} \${i} - \${_getItemDescription(item)}\`)
160
+ try {
161
+ await repo.update?.({ item, execution })
162
+ } catch (error) {
163
+ this.logger.error(\`Error updating \${name} \${i} - \${_getItemDescription(item)}\`, error)
164
+ throw error
165
+ }
166
+ }
167
+ }
168
+ this.logger.log(\`✅ Updated \${format(data.length)} \${pluralize(name)}\`)
169
+ }
170
+
171
+ private async upsert<T extends { id: ID }, ID extends number | string | boolean>({
172
+ name,
173
+ data,
174
+ repo,
175
+ execution,
176
+ }: {
177
+ name: string
178
+ data: (${meta.types.dto.create}<T, ID> | ${meta.types.dto.update}<T, ID>)[] | undefined
179
+ repo: ${meta.data.repository.typeName}<T, ID>
180
+ execution: ${meta.actions.actionExecution.interface}
181
+ }): Promise<void> {
182
+ if (!data) {
183
+ return
184
+ }
185
+ if (!DEBUG) {
186
+ await repo.upsertMany?.({ items: data, execution })
187
+ } else {
188
+ let i = 0
189
+ for (const item of data) {
190
+ i++
191
+ this.logger.log(\`Upserting \${name} \${i} - \${_getItemDescription(item)}\`)
192
+ try {
193
+ await repo.upsert?.({ item, execution })
194
+ } catch (error) {
195
+ this.logger.error(\`Error upserting \${name} \${i} - \${_getItemDescription(item)}\`, error)
196
+ throw error
197
+ }
198
+ }
199
+ }
200
+ this.logger.log(\`✅ Upserted \${format(data.length)} \${pluralize(name)}\`)
201
+ }
202
+
203
+ private async delete<T extends { id: ID }, ID extends number | string | boolean>({
204
+ name,
205
+ data,
206
+ repo,
207
+ execution,
208
+ }: {
209
+ name: string
210
+ data: ID[] | undefined
211
+ repo: ${meta.data.repository.typeName}<T, ID>
212
+ execution: ${meta.actions.actionExecution.interface}
213
+ }): Promise<void> {
214
+ if (!data) {
215
+ return
216
+ }
217
+ if (!DEBUG) {
218
+ await repo.deleteMany?.({ ids: data, execution })
219
+ } else {
220
+ let i = 0
221
+ for (const id of data) {
222
+ i++
223
+ this.logger.log(\`Deleting \${name} \${i} - id: \${id.toString()}\`)
224
+ try {
225
+ await repo.delete?.({ id, execution })
226
+ } catch (error) {
227
+ this.logger.error(\`Error deleting \${name} \${i} - id: \${id.toString()}\`, error)
228
+ throw error
229
+ }
230
+ }
231
+ }
232
+ this.logger.log(\`✅ Deleted \${format(data.length)} \${pluralize(name)}\`)
233
+ }
234
+ }
235
+
236
+ function _getItemDescription<T extends Partial<{ id: ID }>, ID extends number | string | boolean>(item: T): string {
237
+ const id = \`id: \${item.id !== undefined ? item.id.toString() : 'not provided'}\`
238
+
239
+ // If the item has a name, we can assume it is a string and add it to the description
240
+ if ('name' in item) {
241
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
242
+ return \`\${id}, name: \${item.name}\`
243
+ }
244
+ return id
52
245
  }
53
246
  `;
54
247
  }
@@ -7,8 +7,9 @@ const meta_1 = require("../../lib/meta");
7
7
  * Generates the action dispatcher service.
8
8
  */
9
9
  function generateActionsDispatcherService({ models, meta }) {
10
- const imports = imports_1.ImportsGenerator.from(meta.seed.serviceFilePath).addImports({
10
+ const imports = imports_1.ImportsGenerator.from(meta.actions.dispatcherService.filePath).addImports({
11
11
  [meta.seed.importPath]: [meta.seed.serviceClassName],
12
+ [meta.importExport.importPath]: [meta.importExport.importService.name],
12
13
  [meta.businessLogic.update.importPath]: [meta.businessLogic.update.serviceClassName],
13
14
  [meta.types.importPath]: meta.config.userType,
14
15
  });
@@ -30,11 +31,15 @@ import { Action, ResultOfAction } from './actions.types'
30
31
 
31
32
 
32
33
  @Injectable()
33
- export class DispatcherService {
34
- // as the SeedModule gets instantiated after the ActionsModule, we use this hack to avoid a circular dependency:
35
- // we set the seedService to a dummy value and then overwrite it in the ${meta.seed.serviceClassName} constructor.
34
+ export class ${meta.actions.dispatcherService.class} {
35
+
36
+ // As SeedModule and ImportExportModule get instantiated after the ActionsModule, we use this hack to avoid a circular dependency:
37
+ // we set the seedService and the importService to value and then overwrite it in the services' constructors.
36
38
  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
37
- public seedService: ${meta.seed.serviceClassName} = {} as unknown as any
39
+ public seedService: SeedService = {} as unknown as any
40
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
41
+ public ${meta.importExport.importService.sharedName}: ImportService = {} as unknown as any
42
+
38
43
  constructor(
39
44
  private readonly updateService: ${meta.businessLogic.update.serviceClassName},
40
45
  private readonly dbService: DbService,
@@ -72,6 +77,10 @@ export class DispatcherService {
72
77
  .join('\n')}
73
78
  case 'seed':
74
79
  return this.seedService.dispatch({ action, execution })
80
+
81
+ case 'import':
82
+ return this.importService.dispatch({ action, execution })
83
+
75
84
  default:
76
85
  throw new ExhaustiveSwitchCheck(action)
77
86
  }
@@ -0,0 +1,9 @@
1
+ import { SchemaMetaData } from '../../lib/meta';
2
+ import { Model } from '../../lib/schema/schema';
3
+ /**
4
+ * Generates the Exporter class for the Import-Export module
5
+ */
6
+ export declare function generateImportExportConvertImportFunctions({ models, meta, }: {
7
+ models: Model[];
8
+ meta: SchemaMetaData;
9
+ }): string;