@postxl/generator 0.38.1 → 0.39.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 +6 -0
- 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 +1 -1
- package/dist/generators/indices/businesslogic-update-service.generator.js +1 -1
- package/dist/generators/indices/businesslogic-view-module.generator.js +1 -1
- package/dist/generators/indices/businesslogic-view-service.generator.js +1 -1
- package/dist/generators/indices/datamock-module.generator.js +5 -5
- package/dist/generators/indices/datamocker.generator.js +1 -1
- package/dist/generators/indices/datamodule.generator.js +30 -27
- package/dist/generators/indices/dataservice.generator.js +2 -2
- package/dist/generators/indices/dispatcher-service.generator.js +6 -5
- package/dist/generators/indices/emptydatabasemigration.generator.d.ts +2 -0
- package/dist/generators/indices/emptydatabasemigration.generator.js +14 -7
- package/dist/generators/indices/repositories.generator.js +1 -1
- package/dist/generators/indices/seed-migration.generator.js +1 -1
- package/dist/generators/indices/seed-service.generator.js +11 -13
- package/dist/generators/indices/seed-template-decoder.generator.js +1 -1
- package/dist/generators/indices/seeddata-type.generator.js +2 -2
- package/dist/generators/indices/selectors.generator.d.ts +7 -0
- package/dist/generators/indices/selectors.generator.js +82 -0
- package/dist/generators/indices/testdata-service.generator.d.ts +9 -0
- package/dist/generators/indices/testdata-service.generator.js +70 -0
- package/dist/generators/models/businesslogic-update.generator.js +1 -1
- package/dist/generators/models/businesslogic-view.generator.js +1 -1
- 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 +110 -1
- 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/meta.d.ts +24 -0
- package/dist/lib/meta.js +6 -0
- package/dist/lib/schema/schema.d.ts +4 -0
- package/package.json +2 -2
package/dist/generator.js
CHANGED
|
@@ -60,7 +60,9 @@ const seed_service_generator_1 = require("./generators/indices/seed-service.gene
|
|
|
60
60
|
const seed_template_generator_1 = require("./generators/indices/seed-template.generator");
|
|
61
61
|
const seed_template_decoder_generator_1 = require("./generators/indices/seed-template-decoder.generator");
|
|
62
62
|
const seeddata_type_generator_1 = require("./generators/indices/seeddata-type.generator");
|
|
63
|
+
const selectors_generator_1 = require("./generators/indices/selectors.generator");
|
|
63
64
|
const stubs_generator_1 = require("./generators/indices/stubs.generator");
|
|
65
|
+
const testdata_service_generator_1 = require("./generators/indices/testdata-service.generator");
|
|
64
66
|
const types_generator_2 = require("./generators/indices/types.generator");
|
|
65
67
|
const businesslogic_update_generator_1 = require("./generators/models/businesslogic-update.generator");
|
|
66
68
|
const businesslogic_view_generator_1 = require("./generators/models/businesslogic-view.generator");
|
|
@@ -81,6 +83,7 @@ const CONFIG_SCHEMA = zod_1.z
|
|
|
81
83
|
pathToTypes: zod_1.z.string().optional(),
|
|
82
84
|
pathToDataLib: zod_1.z.string().optional(),
|
|
83
85
|
pathToCypress: zod_1.z.string().optional(),
|
|
86
|
+
pathToE2ELib: zod_1.z.string().optional(),
|
|
84
87
|
pathToActions: zod_1.z.string().optional(),
|
|
85
88
|
pathToBusinessLogic: zod_1.z.string().optional(),
|
|
86
89
|
pathToSeedLib: zod_1.z.string().optional(),
|
|
@@ -103,6 +106,7 @@ const CONFIG_SCHEMA = zod_1.z
|
|
|
103
106
|
paths: {
|
|
104
107
|
dataLibPath: (0, types_1.toPath)(s.pathToDataLib || 'repos'),
|
|
105
108
|
cypressPath: (0, types_1.toPath)(s.pathToCypress || './e2e/cypress/'),
|
|
109
|
+
e2eLibPath: (0, types_1.toPath)(s.pathToE2ELib || './e2e/src/'),
|
|
106
110
|
actionsPath: (0, types_1.toPath)(s.pathToActions || 'actions'),
|
|
107
111
|
businessLogicPath: (0, types_1.toPath)(s.pathToBusinessLogic || 'business-logic'),
|
|
108
112
|
reactFolderPath: (0, types_1.toPath)(s.reactFolderOutput || 'react'),
|
|
@@ -211,6 +215,8 @@ function generate({ models, enums, config, prismaClientPath, logger, }) {
|
|
|
211
215
|
generated.write(`/${meta.data.dataModuleFilePath}.ts`, (0, datamodule_generator_1.generateDataModule)({ models, meta }));
|
|
212
216
|
generated.write(`/${meta.data.dataServiceFilePath}.ts`, (0, dataservice_generator_1.generateDataService)({ models, meta }));
|
|
213
217
|
generated.write(`/${meta.data.dataMockerFilePath}.ts`, (0, datamocker_generator_1.generateDataMocker)({ models, meta }));
|
|
218
|
+
generated.write(`/${meta.data.selectorsFilePath}.ts`, (0, selectors_generator_1.generateSelectors)());
|
|
219
|
+
generated.write(`/${meta.data.testDataServiceFilePath}.ts`, (0, testdata_service_generator_1.generateTestDataService)({ models, meta }));
|
|
214
220
|
generated.write(`/${meta.data.dataMockerStubIndexFilePath}.ts`, (0, stubs_generator_1.generateDataMockerStubsIndex)({ models, meta }));
|
|
215
221
|
generated.write(`/${meta.data.repositoriesConstFilePath}.ts`, (0, repositories_generator_1.generateRepositoriesArray)({ models, meta }));
|
|
216
222
|
generated.write(`/${meta.data.repositoriesIndexFilePath}.ts`, (0, repositories_generator_1.generateRepositoriesIndex)({ models, meta }));
|
|
@@ -21,7 +21,7 @@ function generateBusinessLogicActionTypes({ models, meta }) {
|
|
|
21
21
|
actionsTypeElements.push(modelMeta.businessLogic.update.actionName);
|
|
22
22
|
actionResultTypeElements.push(`${modelMeta.businessLogic.update.actionModelDiscriminantName}: ${modelMeta.businessLogic.update.actionResultName}`);
|
|
23
23
|
}
|
|
24
|
-
return `
|
|
24
|
+
return /* ts */ `
|
|
25
25
|
${imports.generate()}
|
|
26
26
|
|
|
27
27
|
/**
|
|
@@ -24,7 +24,7 @@ function generateBusinessLogicUpdateModule({ models, meta }) {
|
|
|
24
24
|
providers.push(meta.businessLogic.update.serviceClassName);
|
|
25
25
|
}
|
|
26
26
|
const moduleName = meta.businessLogic.update.moduleName;
|
|
27
|
-
return `
|
|
27
|
+
return /* ts */ `
|
|
28
28
|
import { DynamicModule } from '@${meta.config.project}/common'
|
|
29
29
|
|
|
30
30
|
${imports.generate()}
|
|
@@ -20,7 +20,7 @@ function generateBusinessLogicUpdateService({ models, meta, }) {
|
|
|
20
20
|
const constructor = mm
|
|
21
21
|
.map(({ meta }) => `@Inject(forwardRef(() => ${meta.businessLogic.update.serviceClassName})) public readonly ${meta.businessLogic.update.serviceVariableName} :${meta.businessLogic.update.serviceClassName}`)
|
|
22
22
|
.join(',\n');
|
|
23
|
-
return `
|
|
23
|
+
return /* ts */ `
|
|
24
24
|
import { Inject, Injectable, forwardRef } from '@nestjs/common'
|
|
25
25
|
|
|
26
26
|
${imports.generate()}
|
|
@@ -23,7 +23,7 @@ function generateBusinessLogicViewModule({ models, meta }) {
|
|
|
23
23
|
providers.push(meta.businessLogic.view.serviceClassName);
|
|
24
24
|
}
|
|
25
25
|
const moduleName = meta.businessLogic.view.moduleName;
|
|
26
|
-
return `
|
|
26
|
+
return /* ts */ `
|
|
27
27
|
import { DynamicModule } from '@${meta.config.project}/common'
|
|
28
28
|
|
|
29
29
|
${imports.generate()}
|
|
@@ -20,7 +20,7 @@ function generateBusinessLogicViewService({ models, meta }) {
|
|
|
20
20
|
const constructor = mm
|
|
21
21
|
.map(({ meta }) => `@Inject(forwardRef(() => ${meta.businessLogic.view.serviceClassName})) public readonly ${meta.businessLogic.view.serviceVariableName} :${meta.businessLogic.view.serviceClassName}`)
|
|
22
22
|
.join(',\n');
|
|
23
|
-
return `
|
|
23
|
+
return /* ts */ `
|
|
24
24
|
import { Inject, Injectable, forwardRef } from '@nestjs/common'
|
|
25
25
|
|
|
26
26
|
${imports.generate()}
|
|
@@ -36,7 +36,7 @@ function generateDataMockModule({ models, meta }) {
|
|
|
36
36
|
[meta.data.dataModuleFilePath]: [Types.toVariableName('DataModule')],
|
|
37
37
|
[meta.data.dataServiceFilePath]: [Types.toVariableName('DataService')],
|
|
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.
|
|
39
|
+
[meta.actions.importPath]: [meta.actions.actionExecutionMock],
|
|
40
40
|
});
|
|
41
41
|
for (const { model, meta } of mm) {
|
|
42
42
|
imports.addImport({ items: [model.typeName], from: meta.types.importPath });
|
|
@@ -55,13 +55,13 @@ function generateDataMockModule({ models, meta }) {
|
|
|
55
55
|
}
|
|
56
56
|
}`)
|
|
57
57
|
.join(', ');
|
|
58
|
-
return `
|
|
58
|
+
return /* ts */ `
|
|
59
59
|
import { DynamicModule } from '@pxl/common'
|
|
60
60
|
import { DbModule } from '@${meta.config.project}/db'
|
|
61
61
|
|
|
62
62
|
${imports.generate()}
|
|
63
63
|
|
|
64
|
-
export class
|
|
64
|
+
export class ${meta.data.dataMockModuleName} {
|
|
65
65
|
static mock(seed?: MockData): DynamicModule {
|
|
66
66
|
const execution = new MockActionExecution()
|
|
67
67
|
|
|
@@ -71,7 +71,7 @@ export class DataMockModule {
|
|
|
71
71
|
]
|
|
72
72
|
|
|
73
73
|
const cachedModule = {
|
|
74
|
-
module:
|
|
74
|
+
module: ${meta.data.moduleName},
|
|
75
75
|
imports: [DbModule.provideMock()],
|
|
76
76
|
providers: providers,
|
|
77
77
|
exports: providers,
|
|
@@ -84,7 +84,7 @@ export class DataMockModule {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
export interface
|
|
87
|
+
export interface ${meta.data.dataMockDataType} {
|
|
88
88
|
${mm.map(({ model, meta }) => `${meta.data.mockDataPropertyName}?: ${model.typeName}[]`).join('\n')}
|
|
89
89
|
}
|
|
90
90
|
`;
|
|
@@ -71,7 +71,7 @@ function generateAddDataFunction(model, meta) {
|
|
|
71
71
|
`;
|
|
72
72
|
});
|
|
73
73
|
// TODO: Move the publicly accessible function's name to the metadata definition!
|
|
74
|
-
return `
|
|
74
|
+
return /* ts */ `
|
|
75
75
|
public add${model.typeName}(
|
|
76
76
|
item?: Partial<Omit<${model.typeName}, '${idField.name}'> & { ${idField.name}: ${idField.unbrandedTypeName} }>
|
|
77
77
|
): DataMocker {
|
|
@@ -34,6 +34,7 @@ function generateDataModule({ 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.dataModuleFilePath).addImports({
|
|
36
36
|
[meta.data.dataServiceFilePath]: [Types.toVariableName('DataService')],
|
|
37
|
+
[meta.data.dataMockModuleFilePath]: [meta.data.dataMockModuleName],
|
|
37
38
|
});
|
|
38
39
|
for (const { meta } of mm) {
|
|
39
40
|
imports.addImport({
|
|
@@ -42,9 +43,12 @@ function generateDataModule({ models, meta }) {
|
|
|
42
43
|
});
|
|
43
44
|
}
|
|
44
45
|
const moduleName = meta.data.moduleName;
|
|
45
|
-
return `
|
|
46
|
+
return /* ts */ `
|
|
47
|
+
import { FactoryProvider } from '@nestjs/common'
|
|
48
|
+
|
|
46
49
|
import { DynamicModule } from '@${meta.config.project}/common'
|
|
47
|
-
import { DbModule } from '@${meta.config.project}/db'
|
|
50
|
+
import { DbModule, DbService } from '@${meta.config.project}/db'
|
|
51
|
+
import { E2EConfig } from '@${meta.config.project}/e2e'
|
|
48
52
|
|
|
49
53
|
${imports.generate()}
|
|
50
54
|
|
|
@@ -75,47 +79,46 @@ export class ${moduleName} {
|
|
|
75
79
|
/**
|
|
76
80
|
* The forRoot method should only be called once by the root module.
|
|
77
81
|
*/
|
|
78
|
-
static forRoot(): DynamicModule {
|
|
82
|
+
static forRoot(options: E2EConfig): DynamicModule {
|
|
79
83
|
if (${moduleName}.cachedModule) {
|
|
80
84
|
throw new Error('${moduleName} is already instantiated, please call .forRoot only once from root...')
|
|
81
85
|
}
|
|
82
86
|
|
|
83
|
-
|
|
84
|
-
${
|
|
85
|
-
]
|
|
86
|
-
|
|
87
|
-
${moduleName}.cachedModule = {
|
|
88
|
-
module: ${moduleName},
|
|
89
|
-
global: true,
|
|
90
|
-
imports: [DbModule.forRoot()],
|
|
91
|
-
providers: [DataService, ...repositoryProviders],
|
|
92
|
-
exports: [DataService, ...repositoryProviders],
|
|
87
|
+
if (options.isE2ETest && !options.useDatabaseForE2E) {
|
|
88
|
+
return ${meta.data.dataMockModuleName}.mock()
|
|
93
89
|
}
|
|
94
90
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
91
|
+
// We need to initialize the user repository right at the beginning,
|
|
92
|
+
// so that we have the rootUser available for the other modules - including
|
|
93
|
+
// the action and seed modules which will ensure that seed data is created.
|
|
94
|
+
const userRepository: FactoryProvider<UserRepository> = {
|
|
95
|
+
provide: UserRepository,
|
|
96
|
+
inject: [DbService],
|
|
97
|
+
useFactory: async (dbService: DbService) => {
|
|
98
|
+
const repository = new UserRepository(dbService)
|
|
99
|
+
await repository.init()
|
|
100
|
+
return repository
|
|
101
|
+
},
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
const
|
|
105
|
-
|
|
104
|
+
const repositoryProviders = [
|
|
105
|
+
userRepository,
|
|
106
|
+
${mm
|
|
107
|
+
.filter((mm) => mm.model.name !== 'User')
|
|
108
|
+
.map(({ meta }) => meta.data.repositoryClassName)
|
|
109
|
+
.join(',')}
|
|
106
110
|
]
|
|
107
|
-
|
|
111
|
+
|
|
108
112
|
${moduleName}.cachedModule = {
|
|
109
113
|
module: ${moduleName},
|
|
110
114
|
global: true,
|
|
111
115
|
imports: [DbModule.forRoot()],
|
|
112
|
-
providers,
|
|
113
|
-
exports:
|
|
116
|
+
providers: [DataService, ...repositoryProviders],
|
|
117
|
+
exports: [DataService, ...repositoryProviders],
|
|
114
118
|
}
|
|
115
119
|
|
|
116
120
|
return ${moduleName}.cachedModule
|
|
117
121
|
}
|
|
118
|
-
}
|
|
119
|
-
`;
|
|
122
|
+
}`;
|
|
120
123
|
}
|
|
121
124
|
exports.generateDataModule = generateDataModule;
|
|
@@ -23,7 +23,7 @@ function generateDataService({ models, meta }) {
|
|
|
23
23
|
.map(({ meta }) => `${meta.data.excelExportTableName}: 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
|
-
return `
|
|
26
|
+
return /* ts */ `
|
|
27
27
|
import { Injectable } from '@nestjs/common'
|
|
28
28
|
|
|
29
29
|
import { mapValues } from '@pxl/common'
|
|
@@ -31,7 +31,7 @@ import { mapValues } from '@pxl/common'
|
|
|
31
31
|
${imports.generate()}
|
|
32
32
|
|
|
33
33
|
@Injectable()
|
|
34
|
-
export class
|
|
34
|
+
export class ${meta.data.dataServiceClassName} {
|
|
35
35
|
constructor(${constructor}) {}
|
|
36
36
|
|
|
37
37
|
public async prepareExcelExport() {
|
|
@@ -17,7 +17,7 @@ function generateActionsDispatcherService({ models, meta }) {
|
|
|
17
17
|
const modelMeta = (0, meta_1.getModelMetadata)({ model });
|
|
18
18
|
dataLoader.push(`await this.upload({ name: '${modelMeta.userFriendlyName}', data: data.${modelMeta.seed.constantName}, repo: this.dataService.${modelMeta.data.dataServiceName}, log })`);
|
|
19
19
|
}
|
|
20
|
-
return `
|
|
20
|
+
return /* ts */ `
|
|
21
21
|
import { Injectable } from '@nestjs/common'
|
|
22
22
|
|
|
23
23
|
import { ExhaustiveSwitchCheck } from '@pxl/common'
|
|
@@ -25,7 +25,7 @@ import { DbService } from '@pxl/db'
|
|
|
25
25
|
|
|
26
26
|
${imports.generate()}
|
|
27
27
|
|
|
28
|
-
import {
|
|
28
|
+
import { ActionExecutionFactory, IActionExecution } from './actionExecution'
|
|
29
29
|
import { Action, ResultOfAction } from './actions.types'
|
|
30
30
|
|
|
31
31
|
|
|
@@ -37,14 +37,15 @@ export class DispatcherService {
|
|
|
37
37
|
public seedService: ${meta.seed.serviceClassName} = {} as unknown as any
|
|
38
38
|
constructor(
|
|
39
39
|
private readonly updateService: ${meta.businessLogic.update.serviceClassName},
|
|
40
|
-
private readonly dbService: DbService
|
|
40
|
+
private readonly dbService: DbService,
|
|
41
|
+
private readonly actionExecutionFactory: ActionExecutionFactory,
|
|
41
42
|
) {}
|
|
42
43
|
|
|
43
44
|
public async dispatch<A extends Action>({ action, user }: {
|
|
44
45
|
action: A;
|
|
45
46
|
user: ${meta.config.userType}
|
|
46
47
|
}): Promise<ResultOfAction<A>> {
|
|
47
|
-
const execution = await
|
|
48
|
+
const execution = await this.actionExecutionFactory.create({ action, dbService: this.dbService, user })
|
|
48
49
|
|
|
49
50
|
try {
|
|
50
51
|
const result = await (this.execute({ action, execution }) as Promise<ResultOfAction<A>>)
|
|
@@ -58,7 +59,7 @@ export class DispatcherService {
|
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
private async execute({ action, execution }: { action: Action; execution:
|
|
62
|
+
private async execute({ action, execution }: { action: Action; execution: IActionExecution }) {
|
|
62
63
|
switch (action.scope) {
|
|
63
64
|
${models
|
|
64
65
|
.map((model) => {
|
|
@@ -2,6 +2,8 @@ import { SchemaMetaData } from '../../lib/meta';
|
|
|
2
2
|
import { Model } from '../../lib/schema/schema';
|
|
3
3
|
/**
|
|
4
4
|
* Generates a the Prisma migration to create the `emptyDatabase` stored procedure.
|
|
5
|
+
*
|
|
6
|
+
* The routine is used in e2e tests to empty the database before each test.
|
|
5
7
|
*/
|
|
6
8
|
export declare function generateEmptyDatabaseStoredProcedure({ models }: {
|
|
7
9
|
models: Model[];
|
|
@@ -7,13 +7,20 @@ exports.prismaMigrationExists = exports.deriveEmptyDatabaseMigrationFilePath = e
|
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
/**
|
|
9
9
|
* Generates a the Prisma migration to create the `emptyDatabase` stored procedure.
|
|
10
|
+
*
|
|
11
|
+
* The routine is used in e2e tests to empty the database before each test.
|
|
10
12
|
*/
|
|
11
13
|
function generateEmptyDatabaseStoredProcedure({ models }) {
|
|
12
|
-
const
|
|
13
|
-
.map((model) => `\t${model.sourceSchemaName !== undefined ? `"${model.sourceSchemaName}".` : ''}"${model.sourceName}"`)
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
14
|
+
const modelTables = models
|
|
15
|
+
.map((model) => `\t${model.sourceSchemaName !== undefined ? `"${model.sourceSchemaName}".` : ''}"${model.sourceName}"`);
|
|
16
|
+
// We determine the schema used for all system tables by looking at the User model's schema.
|
|
17
|
+
const userModel = models.find((model) => model.name === 'User');
|
|
18
|
+
if (!userModel) {
|
|
19
|
+
throw new Error('Model definition for "User" could not be found - hence schema for system table cannot be derived!');
|
|
20
|
+
}
|
|
21
|
+
const configSchema = userModel.sourceSchemaName !== undefined ? `"${userModel.sourceSchemaName}".` : '';
|
|
22
|
+
const systemTables = ["Action", "Mutation"].map((table) => `\t${configSchema}"${table}"`);
|
|
23
|
+
const configTableName = `${configSchema}"Config"`;
|
|
17
24
|
return `
|
|
18
25
|
CREATE OR REPLACE PROCEDURE "emptyDatabase"() AS $BODY$ BEGIN IF EXISTS (
|
|
19
26
|
SELECT
|
|
@@ -22,8 +29,8 @@ CREATE OR REPLACE PROCEDURE "emptyDatabase"() AS $BODY$ BEGIN IF EXISTS (
|
|
|
22
29
|
) THEN RETURN;
|
|
23
30
|
END IF;
|
|
24
31
|
TRUNCATE TABLE
|
|
25
|
-
${
|
|
26
|
-
|
|
32
|
+
${[...systemTables, ...modelTables].join(',\n')};
|
|
33
|
+
|
|
27
34
|
END;
|
|
28
35
|
$BODY$ LANGUAGE plpgsql;
|
|
29
36
|
`;
|
|
@@ -17,7 +17,7 @@ function generateSeedMigration({ models, meta }) {
|
|
|
17
17
|
});
|
|
18
18
|
modelTypes.push(`${modelMeta.seed.constantName}: { create: ${modelMeta.seed.constantName} }`);
|
|
19
19
|
}
|
|
20
|
-
return `
|
|
20
|
+
return /* ts */ `
|
|
21
21
|
import { createActionSeedData } from '${meta.seed.importPath}'
|
|
22
22
|
${imports.generate()}
|
|
23
23
|
|
|
@@ -17,15 +17,14 @@ function generateSeedService({ models }) {
|
|
|
17
17
|
upserts.push(`await this.upsert({ name: '${modelMeta.userFriendlyName}', data: data.${modelMeta.seed.constantName}?.upsert, repo: this.dataService.${modelMeta.data.dataServiceName}, execution })`);
|
|
18
18
|
deletes.push(`await this.delete({ name: '${modelMeta.userFriendlyName}', data: data.${modelMeta.seed.constantName}?.delete, repo: this.dataService.${modelMeta.data.dataServiceName}, execution })`);
|
|
19
19
|
}
|
|
20
|
-
return `
|
|
20
|
+
return /* ts */ `
|
|
21
21
|
import { Injectable, Logger } from '@nestjs/common'
|
|
22
22
|
import { ExhaustiveSwitchCheck, format, pluralize } from '@pxl/common'
|
|
23
23
|
import { DataService, Repository } from '@pxl/data'
|
|
24
24
|
import { DbService } from '@pxl/db'
|
|
25
25
|
import { XlPortService } from '@pxl/xlport'
|
|
26
26
|
|
|
27
|
-
import {
|
|
28
|
-
import { RootUserService } from '@pxl/root-user'
|
|
27
|
+
import { IActionExecution, DispatcherService } from '@pxl/actions'
|
|
29
28
|
|
|
30
29
|
import {
|
|
31
30
|
ActionPreparation_Seed,
|
|
@@ -53,7 +52,6 @@ export class SeedService {
|
|
|
53
52
|
private readonly xlPortService: XlPortService,
|
|
54
53
|
private readonly dbService: DbService,
|
|
55
54
|
private readonly dispatcherService: DispatcherService,
|
|
56
|
-
private readonly rootUserService: RootUserService,
|
|
57
55
|
) {
|
|
58
56
|
dispatcherService.seedService = this
|
|
59
57
|
}
|
|
@@ -79,7 +77,7 @@ export class SeedService {
|
|
|
79
77
|
const action = await this.convertToAction(migration)
|
|
80
78
|
|
|
81
79
|
this.logger.log(\`Executing seed migration \${migration.order} - \${migration.name}\`)
|
|
82
|
-
await this.dispatcherService.dispatch({ action, user: this.
|
|
80
|
+
await this.dispatcherService.dispatch({ action, user: this.dataService.users.rootUser })
|
|
83
81
|
|
|
84
82
|
executed++
|
|
85
83
|
}
|
|
@@ -117,7 +115,7 @@ export class SeedService {
|
|
|
117
115
|
/**
|
|
118
116
|
* Will be call by the dispatcher service to dispatch the given action.
|
|
119
117
|
*/
|
|
120
|
-
public async dispatch({ action, execution }: { action: Action_Seed; execution:
|
|
118
|
+
public async dispatch({ action, execution }: { action: Action_Seed; execution: IActionExecution }) {
|
|
121
119
|
switch (action.type) {
|
|
122
120
|
case 'data':
|
|
123
121
|
return this.uploadDataSteps({ steps: action.payload, execution })
|
|
@@ -175,7 +173,7 @@ export class SeedService {
|
|
|
175
173
|
* Executes the custom logic of the given migration.
|
|
176
174
|
* To do this, we retrieve the handler from the migration and call it, injecting the data service into the handler.
|
|
177
175
|
*/
|
|
178
|
-
private handleCustomLogic({ action, execution }: { action: Action_Seed_CustomLogic; execution:
|
|
176
|
+
private handleCustomLogic({ action, execution }: { action: Action_Seed_CustomLogic; execution: IActionExecution }) {
|
|
179
177
|
const actionPreparation = SEED_MIGRATIONS.find((m) => m.order === action.order)
|
|
180
178
|
if (!actionPreparation) {
|
|
181
179
|
throw new Error(\`Cannot find custom handler for migration with order \${action.order}\`)
|
|
@@ -188,7 +186,7 @@ export class SeedService {
|
|
|
188
186
|
/**
|
|
189
187
|
* Executes the data loading step by step
|
|
190
188
|
*/
|
|
191
|
-
private async uploadDataSteps({ steps, execution }: { steps: SeedData[]; execution:
|
|
189
|
+
private async uploadDataSteps({ steps, execution }: { steps: SeedData[]; execution: IActionExecution }) {
|
|
192
190
|
let index = 0
|
|
193
191
|
for (const step of steps) {
|
|
194
192
|
this.logger.log(\`Uploading data step \${++index}/\${steps.length}\`)
|
|
@@ -196,7 +194,7 @@ export class SeedService {
|
|
|
196
194
|
}
|
|
197
195
|
}
|
|
198
196
|
|
|
199
|
-
private async uploadData({ data, execution }: { data: SeedData; execution:
|
|
197
|
+
private async uploadData({ data, execution }: { data: SeedData; execution: IActionExecution }) {
|
|
200
198
|
// NOTE: the order of these calls is important, because of foreign key constraints
|
|
201
199
|
// The current order is based on the order of the models in the schema
|
|
202
200
|
// Change the order based on your needs.
|
|
@@ -221,7 +219,7 @@ export class SeedService {
|
|
|
221
219
|
name: string
|
|
222
220
|
data: CreateDTO<T, ID>[] | undefined
|
|
223
221
|
repo: Repository<T, ID>
|
|
224
|
-
execution:
|
|
222
|
+
execution: IActionExecution
|
|
225
223
|
}): Promise<void> {
|
|
226
224
|
if (!data) {
|
|
227
225
|
return
|
|
@@ -253,7 +251,7 @@ export class SeedService {
|
|
|
253
251
|
name: string
|
|
254
252
|
data: UpdateDTO<T, ID>[] | undefined
|
|
255
253
|
repo: Repository<T, ID>
|
|
256
|
-
execution:
|
|
254
|
+
execution: IActionExecution
|
|
257
255
|
}): Promise<void> {
|
|
258
256
|
if (!data) {
|
|
259
257
|
return
|
|
@@ -285,7 +283,7 @@ export class SeedService {
|
|
|
285
283
|
name: string
|
|
286
284
|
data: (CreateDTO<T, ID> | UpdateDTO<T, ID>)[] | undefined
|
|
287
285
|
repo: Repository<T, ID>
|
|
288
|
-
execution:
|
|
286
|
+
execution: IActionExecution
|
|
289
287
|
}): Promise<void> {
|
|
290
288
|
if (!data) {
|
|
291
289
|
return
|
|
@@ -317,7 +315,7 @@ export class SeedService {
|
|
|
317
315
|
name: string
|
|
318
316
|
data: ID[] | undefined
|
|
319
317
|
repo: Repository<T, ID>
|
|
320
|
-
execution:
|
|
318
|
+
execution: IActionExecution
|
|
321
319
|
}): Promise<void> {
|
|
322
320
|
if (!data) {
|
|
323
321
|
return
|
|
@@ -22,7 +22,7 @@ function generateSeedTemplateDecoder({ models, meta }) {
|
|
|
22
22
|
tableDecoders.push(`${modelMeta.seed.excel.tableName}: ${modelMeta.seed.decoder.decoderName}`);
|
|
23
23
|
renameTransforms.push(`${modelMeta.seed.decoder.dataName}: item['${modelMeta.seed.excel.tableName}']`);
|
|
24
24
|
}
|
|
25
|
-
return `
|
|
25
|
+
return /* ts */ `
|
|
26
26
|
import * as z from 'zod'
|
|
27
27
|
|
|
28
28
|
${imports.generate()}
|
|
@@ -26,14 +26,14 @@ function generateSeedDataType({ models, meta }) {
|
|
|
26
26
|
}`);
|
|
27
27
|
mockConverters.push(`${modelMeta.seed.constantName}: { create: data.${modelMeta.seed.constantName} }`);
|
|
28
28
|
}
|
|
29
|
-
return `
|
|
29
|
+
return /* ts */ `
|
|
30
30
|
${imports.generate()}
|
|
31
31
|
|
|
32
32
|
export type SeedData = {
|
|
33
33
|
${modelTypes.join('\n')}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export function
|
|
36
|
+
export function ${meta.seed.mockDataToCreateFunction}(data: ${meta.data.dataMockDataType}): SeedData {
|
|
37
37
|
return {
|
|
38
38
|
${mockConverters.join(',\n')}
|
|
39
39
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates list of component selectors for Cypress tests.
|
|
3
|
+
*
|
|
4
|
+
* Note: This generator does not need the models or meta data passed in.
|
|
5
|
+
* Instead it uses the SelectorCollector singleton that already collected all ids during the generation process of the individual models.
|
|
6
|
+
*/
|
|
7
|
+
export declare function generateSelectors(): string;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateSelectors = void 0;
|
|
4
|
+
const id_collector_1 = require("../../lib/id-collector");
|
|
5
|
+
/**
|
|
6
|
+
* List of ids that are not generated but hardcoded across the template.
|
|
7
|
+
*/
|
|
8
|
+
const HARDCODED_IDS = [
|
|
9
|
+
'indexPage-buttons-create',
|
|
10
|
+
'confirmationModal-buttons-confirm',
|
|
11
|
+
'confirmationModal-buttons-cancel',
|
|
12
|
+
];
|
|
13
|
+
/**
|
|
14
|
+
* Generates list of component selectors for Cypress tests.
|
|
15
|
+
*
|
|
16
|
+
* Note: This generator does not need the models or meta data passed in.
|
|
17
|
+
* Instead it uses the SelectorCollector singleton that already collected all ids during the generation process of the individual models.
|
|
18
|
+
*/
|
|
19
|
+
function generateSelectors() {
|
|
20
|
+
// SelectorCollector is a static singleton that collects all ids during the generation process. We flush it here to get all ids.
|
|
21
|
+
const collectedIds = id_collector_1.SelectorCollector.flush();
|
|
22
|
+
const object = {};
|
|
23
|
+
const ids = [...HARDCODED_IDS, ...collectedIds];
|
|
24
|
+
ids.sort();
|
|
25
|
+
ids.map((id) => {
|
|
26
|
+
addCypressSelectorToNestedObject({ object, id });
|
|
27
|
+
});
|
|
28
|
+
const selectors = JSON.stringify(object, null, 2);
|
|
29
|
+
return /* ts */ `
|
|
30
|
+
export const SELECTORS =
|
|
31
|
+
${selectors}
|
|
32
|
+
`;
|
|
33
|
+
}
|
|
34
|
+
exports.generateSelectors = generateSelectors;
|
|
35
|
+
/**
|
|
36
|
+
* Converts an id like `post-create-name` to a nested object like `{post: {create: {name: '[data-cy=post-create-name]'}}}`.
|
|
37
|
+
*/
|
|
38
|
+
function addCypressSelectorToNestedObject({ object, id, }) {
|
|
39
|
+
const keys = id.split('-');
|
|
40
|
+
extendObject(object, keys, `[data-cy=${id}]`);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Recursively traverses the keys and adds the value to the object in a nested way.
|
|
44
|
+
* E.g. `{object, keys: ['post', 'create', 'name'], value}` will extend `object` with `{post: {create: {name: value}}}`.
|
|
45
|
+
*
|
|
46
|
+
*/
|
|
47
|
+
function extendObject(object, keys, value) {
|
|
48
|
+
const key = keys.shift();
|
|
49
|
+
// no item left -> we are at the end of the recursion
|
|
50
|
+
if (!key) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
// create the nested object if it does not exist yet
|
|
54
|
+
object[key] = object[key] || {};
|
|
55
|
+
// In case the object already has a value, we check if it is a string (and not a nested object).
|
|
56
|
+
// If it is a string, it we check if the value is the same as the new value. In this case is is a duplicate and hence fine.
|
|
57
|
+
// However, if it is a string and a different value, it means we have an inconsistent ID structure.
|
|
58
|
+
// E.g. this can happen when we have three entries: "post-create", "post-create-name", "post-create-description".
|
|
59
|
+
// In this case, the "post-create" entry will be a string - and we cannot nest `{name: ..., description: ...}` in it.
|
|
60
|
+
if (key in object && typeof object[key] === 'string') {
|
|
61
|
+
if (keys.length !== 0) {
|
|
62
|
+
throw new Error(`Key ${key} already exists with value: ${object[key]} but we are trying to nest ${keys.join('-')}`);
|
|
63
|
+
}
|
|
64
|
+
if (object[key] !== value) {
|
|
65
|
+
throw new Error(`Duplicate key ${key} with different values: ${object[key]} and ${value}`);
|
|
66
|
+
}
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// If there are still keys left, we recursively call this function with the nested object and the remaining keys.
|
|
70
|
+
if (keys.length !== 0) {
|
|
71
|
+
extendObject(object[key], keys, value);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// If there are no keys left, we need to verify that the object might not already have a nested object.
|
|
75
|
+
// This can happen, e.g. when we have two entries: "post-create-name" and "post-create".
|
|
76
|
+
// In this case, the `post.create` entry will be a nested object`{name: ...}`.
|
|
77
|
+
// If we would set `post.create` to a string, we would lose the nested object.
|
|
78
|
+
if (key in object && typeof object[key] !== 'string' && Object.keys(object[key]).length !== 0) {
|
|
79
|
+
throw new Error(`Key ${key} already has nested object ${JSON.stringify(object[key])} but we are trying to set it to ${value}`);
|
|
80
|
+
}
|
|
81
|
+
object[key] = value;
|
|
82
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SchemaMetaData } from '../../lib/meta';
|
|
2
|
+
import { Model } from '../../lib/schema/schema';
|
|
3
|
+
/**
|
|
4
|
+
* Generates the TestDataService class.
|
|
5
|
+
*/
|
|
6
|
+
export declare function generateTestDataService({ models, meta: schemaMeta, }: {
|
|
7
|
+
models: Model[];
|
|
8
|
+
meta: SchemaMetaData;
|
|
9
|
+
}): string;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateTestDataService = void 0;
|
|
4
|
+
const imports_1 = require("../../lib/imports");
|
|
5
|
+
const meta_1 = require("../../lib/meta");
|
|
6
|
+
const types_1 = require("../../lib/schema/types");
|
|
7
|
+
/**
|
|
8
|
+
* Generates the TestDataService class.
|
|
9
|
+
*/
|
|
10
|
+
function generateTestDataService({ models, meta: schemaMeta, }) {
|
|
11
|
+
const imports = imports_1.ImportsGenerator.from(schemaMeta.data.testDataServiceFilePath);
|
|
12
|
+
imports.addImports({
|
|
13
|
+
[schemaMeta.actions.importPath]: [(0, types_1.toClassName)('ActionExecutionFactory')],
|
|
14
|
+
[schemaMeta.data.importPath]: [schemaMeta.data.dataServiceClassName, schemaMeta.data.dataMockDataType],
|
|
15
|
+
[schemaMeta.seed.importPath]: [(0, types_1.toClassName)('createActionSeedData'), schemaMeta.seed.mockDataToCreateFunction],
|
|
16
|
+
});
|
|
17
|
+
const reInitCalls = [];
|
|
18
|
+
const modelMetas = models.map((model) => ({ model, meta: (0, meta_1.getModelMetadata)({ model }) }));
|
|
19
|
+
for (const { model, meta } of modelMetas) {
|
|
20
|
+
if (model.defaultField) {
|
|
21
|
+
imports.addImport({ items: [meta.data.stubGenerationFnName], from: meta.data.importPath });
|
|
22
|
+
const stubDefault = `${meta.data.stubGenerationFnName}({ ${model.defaultField.name}: true })`;
|
|
23
|
+
reInitCalls.push(`await this.dataService.${meta.data.dataServiceName}.reInit({ items: mockData.${meta.seed.constantName}?.create ?? [${stubDefault}], execution: actionExecution })`);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
reInitCalls.push(`await this.dataService.${meta.data.dataServiceName}.reInit({ items: mockData.${meta.seed.constantName}?.create ?? [], execution: actionExecution })`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return /* ts */ `
|
|
30
|
+
import { Injectable, Logger } from '@nestjs/common'
|
|
31
|
+
|
|
32
|
+
import { DbService } from '@pxl/db'
|
|
33
|
+
${imports.generate()}
|
|
34
|
+
|
|
35
|
+
@Injectable()
|
|
36
|
+
export class TestDataService {
|
|
37
|
+
private logger = new Logger(TestDataService.name)
|
|
38
|
+
|
|
39
|
+
constructor(
|
|
40
|
+
private db: DbService,
|
|
41
|
+
private dataService: ${schemaMeta.data.dataServiceClassName},
|
|
42
|
+
private actionExecutionFactory: ActionExecutionFactory,
|
|
43
|
+
) {}
|
|
44
|
+
|
|
45
|
+
public async resetTestData(data: MockData) {
|
|
46
|
+
await this.db.emptyDatabase()
|
|
47
|
+
|
|
48
|
+
// We need to init the user repository first so the root user is created
|
|
49
|
+
await this.dataService.users.init()
|
|
50
|
+
|
|
51
|
+
const mockData = ${schemaMeta.seed.mockDataToCreateFunction}(data)
|
|
52
|
+
const action = createActionSeedData({ name: 'E2E', order: 0, data: [] })
|
|
53
|
+
const actionExecution = await this.actionExecutionFactory.create({
|
|
54
|
+
action,
|
|
55
|
+
dbService: this.db,
|
|
56
|
+
user: this.dataService.users.rootUser,
|
|
57
|
+
})
|
|
58
|
+
try {
|
|
59
|
+
${reInitCalls.join('\n')}
|
|
60
|
+
|
|
61
|
+
await actionExecution.success()
|
|
62
|
+
this.logger.log(\`✅ Reset test data\`)
|
|
63
|
+
} catch (e) {
|
|
64
|
+
await actionExecution.error(e)
|
|
65
|
+
throw e
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}`;
|
|
69
|
+
}
|
|
70
|
+
exports.generateTestDataService = generateTestDataService;
|
|
@@ -41,7 +41,7 @@ function generateModelBusinessLogicUpdate({ model, meta }) {
|
|
|
41
41
|
`@Inject(forwardRef(() => ${schemaMeta.businessLogic.view.serviceClassName})) private readonly ${viewServiceClassName}: ${schemaMeta.businessLogic.view.serviceClassName}`,
|
|
42
42
|
];
|
|
43
43
|
const methodTypeSignatures = (0, repository_generator_1.getRepositoryMethodsTypeSignatures)({ model, meta });
|
|
44
|
-
return `
|
|
44
|
+
return /* ts */ `
|
|
45
45
|
import { Inject, Injectable, forwardRef } from '@nestjs/common'
|
|
46
46
|
import { ExhaustiveSwitchCheck } from '@pxl/common'
|
|
47
47
|
|
|
@@ -92,7 +92,7 @@ function generateModelBusinessLogicView({ model, meta }) {
|
|
|
92
92
|
return item
|
|
93
93
|
}
|
|
94
94
|
`;
|
|
95
|
-
return `
|
|
95
|
+
return /* ts */ `
|
|
96
96
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
97
97
|
import { Inject, Injectable, forwardRef } from '@nestjs/common'
|
|
98
98
|
import { FilterOperator } from '@pxl/common'
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateModelLibraryComponents = void 0;
|
|
4
|
+
const id_collector_1 = require("../../../lib/id-collector");
|
|
4
5
|
const imports_1 = require("../../../lib/imports");
|
|
5
6
|
/**
|
|
6
7
|
* Generates components that may be used to list all entries of a given data type.
|
|
@@ -10,6 +11,7 @@ const imports_1 = require("../../../lib/imports");
|
|
|
10
11
|
*/
|
|
11
12
|
function generateModelLibraryComponents({ model, meta }) {
|
|
12
13
|
const { react: { context, components }, } = meta;
|
|
14
|
+
const selectorCollector = id_collector_1.SelectorCollector.from(meta.seed.constantName + '-card');
|
|
13
15
|
const imports = imports_1.ImportsGenerator.from(meta.react.folderPath)
|
|
14
16
|
.addImport({
|
|
15
17
|
items: [model.typeName],
|
|
@@ -58,11 +60,13 @@ function generateModelLibraryComponents({ model, meta }) {
|
|
|
58
60
|
label: 'Edit',
|
|
59
61
|
icon: 'pencil-on-paper',
|
|
60
62
|
onClick: () => setIsEditModalOpen(true),
|
|
63
|
+
__cypress_action_selector__: "${selectorCollector.idFor('edit', { typePrefix: 'actions' })}",
|
|
61
64
|
},
|
|
62
65
|
{
|
|
63
66
|
label: 'Delete',
|
|
64
67
|
icon: 'trash',
|
|
65
68
|
onClick: () => setIsDeleteModalOpen(true),
|
|
69
|
+
__cypress_action_selector__: "${selectorCollector.idFor('delete', { typePrefix: 'actions' })}",
|
|
66
70
|
},
|
|
67
71
|
]}
|
|
68
72
|
/>
|
|
@@ -24,6 +24,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
exports.generateDeleteModalModelComponent = exports.generateEditModalModelComponent = exports.generateModelCreateModalComponent = void 0;
|
|
27
|
+
const id_collector_1 = require("../../../lib/id-collector");
|
|
27
28
|
const imports_1 = require("../../../lib/imports");
|
|
28
29
|
const meta_1 = require("../../../lib/meta");
|
|
29
30
|
const fields_1 = require("../../../lib/schema/fields");
|
|
@@ -36,6 +37,7 @@ const StringUtils = __importStar(require("../../../lib/utils/string"));
|
|
|
36
37
|
function generateModelCreateModalComponent({ model, meta }) {
|
|
37
38
|
const { fields } = model;
|
|
38
39
|
const { react: { components: { modals }, }, trpc, } = meta;
|
|
40
|
+
const selectorCollector = id_collector_1.SelectorCollector.from(meta.seed.constantName + '-createModal');
|
|
39
41
|
return `
|
|
40
42
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
41
43
|
${getFormImports({ model, meta })}
|
|
@@ -130,9 +132,10 @@ export const ${modals.createComponentName} = ({ show, onHide }: { show: boolean;
|
|
|
130
132
|
fill="fill"
|
|
131
133
|
onClick={submitForm}
|
|
132
134
|
loading={isSubmitting}
|
|
135
|
+
__cypress_selector__="${selectorCollector.idFor('submit', { typePrefix: 'buttons' })}"
|
|
133
136
|
/>
|
|
134
137
|
}>
|
|
135
|
-
${getFormFieldComponents({ model })}
|
|
138
|
+
${getFormFieldComponents({ model, selectorCollector })}
|
|
136
139
|
</ModalWithActions>
|
|
137
140
|
)}
|
|
138
141
|
</Typed.Formik>
|
|
@@ -149,6 +152,7 @@ exports.generateModelCreateModalComponent = generateModelCreateModalComponent;
|
|
|
149
152
|
function generateEditModalModelComponent({ model, meta }) {
|
|
150
153
|
const { fields } = model;
|
|
151
154
|
const { react: { components }, trpc, } = meta;
|
|
155
|
+
const selectorCollector = id_collector_1.SelectorCollector.from(meta.seed.constantName + '-editModal');
|
|
152
156
|
return `
|
|
153
157
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
154
158
|
${getFormImports({ model, meta })}
|
|
@@ -257,10 +261,11 @@ export const ${components.modals.editComponentName} = ({
|
|
|
257
261
|
color="primary"
|
|
258
262
|
onClick={submitForm}
|
|
259
263
|
loading={isSubmitting}
|
|
264
|
+
__cypress_selector__="${selectorCollector.idFor('submit', { typePrefix: 'buttons' })}"
|
|
260
265
|
/>
|
|
261
266
|
}
|
|
262
267
|
>
|
|
263
|
-
${getFormFieldComponents({ model })}
|
|
268
|
+
${getFormFieldComponents({ model, selectorCollector })}
|
|
264
269
|
</ModalWithActions>
|
|
265
270
|
)}
|
|
266
271
|
</Typed.Formik>
|
|
@@ -528,7 +533,7 @@ function getEditFormikMutationData({ model: { fields } }) {
|
|
|
528
533
|
/**
|
|
529
534
|
* Returns a string containing all the components that should appear in the Formik form for this model.
|
|
530
535
|
*/
|
|
531
|
-
function getFormFieldComponents({ model }) {
|
|
536
|
+
function getFormFieldComponents({ model, selectorCollector, }) {
|
|
532
537
|
var _a;
|
|
533
538
|
const form = new serializer_1.Serializer();
|
|
534
539
|
for (const field of model.fields.values()) {
|
|
@@ -550,7 +555,11 @@ function getFormFieldComponents({ model }) {
|
|
|
550
555
|
form.append(`
|
|
551
556
|
<div>
|
|
552
557
|
<Label>${label}</Label>
|
|
553
|
-
<Typed.TextField
|
|
558
|
+
<Typed.TextField
|
|
559
|
+
name="${formikFieldName}"
|
|
560
|
+
placeholder="Type..."
|
|
561
|
+
__cypress_field_selector__="${selectorCollector.idFor(field.name, { typePrefix: 'fields' })}"
|
|
562
|
+
/>
|
|
554
563
|
</div>
|
|
555
564
|
`);
|
|
556
565
|
break scalar;
|
|
@@ -563,7 +572,12 @@ function getFormFieldComponents({ model }) {
|
|
|
563
572
|
form.append(`
|
|
564
573
|
<div>
|
|
565
574
|
<Label>${label}</Label>
|
|
566
|
-
<Typed.NumberField
|
|
575
|
+
<Typed.NumberField
|
|
576
|
+
name="${formikFieldName}"
|
|
577
|
+
placeholder="2511"
|
|
578
|
+
decimals={${decimals}}
|
|
579
|
+
__cypress_field_selector__="${selectorCollector.idFor(field.name, { typePrefix: 'fields' })}"
|
|
580
|
+
/>
|
|
567
581
|
</div>
|
|
568
582
|
`);
|
|
569
583
|
break scalar;
|
|
@@ -572,7 +586,11 @@ function getFormFieldComponents({ model }) {
|
|
|
572
586
|
form.append(`
|
|
573
587
|
<div>
|
|
574
588
|
<Label>Is ${label}</Label>
|
|
575
|
-
<Typed.CheckBoxField
|
|
589
|
+
<Typed.CheckBoxField
|
|
590
|
+
name="${formikFieldName}"
|
|
591
|
+
label="${label}"
|
|
592
|
+
__cypress_field_selector__="${selectorCollector.idFor(field.name, { typePrefix: 'fields' })}"
|
|
593
|
+
/>
|
|
576
594
|
</div>
|
|
577
595
|
`);
|
|
578
596
|
break scalar;
|
|
@@ -593,7 +611,12 @@ function getFormFieldComponents({ model }) {
|
|
|
593
611
|
form.append(`
|
|
594
612
|
<div>
|
|
595
613
|
<Label>${refMeta.userFriendlyName}</Label>
|
|
596
|
-
<Typed.${refMeta.react.components.forms.searchFieldName}
|
|
614
|
+
<Typed.${refMeta.react.components.forms.searchFieldName}
|
|
615
|
+
name="${formikFieldName}"
|
|
616
|
+
placeholder="Search..."
|
|
617
|
+
__cypress_options_selector__="${selectorCollector.idFor(field.name, { typePrefix: 'options' })}"
|
|
618
|
+
__cypress_combobox_selector__="${selectorCollector.idFor(field.name, { typePrefix: 'fields' })}"
|
|
619
|
+
/>
|
|
597
620
|
</div>
|
|
598
621
|
`);
|
|
599
622
|
break;
|
|
@@ -603,7 +626,11 @@ function getFormFieldComponents({ model }) {
|
|
|
603
626
|
form.append(`
|
|
604
627
|
<div>
|
|
605
628
|
<Label>${label}</Label>
|
|
606
|
-
<Typed.${enumMeta.react.selectFieldName}
|
|
629
|
+
<Typed.${enumMeta.react.selectFieldName}
|
|
630
|
+
name="${formikFieldName}"
|
|
631
|
+
placeholder="Search..."
|
|
632
|
+
__cypress_field_selector__="${selectorCollector.idFor(field.name, { typePrefix: 'fields' })}"
|
|
633
|
+
/>
|
|
607
634
|
</div>
|
|
608
635
|
`);
|
|
609
636
|
break;
|
|
@@ -55,6 +55,7 @@ function generateRepository({ model, meta }) {
|
|
|
55
55
|
model,
|
|
56
56
|
meta,
|
|
57
57
|
schemaMeta,
|
|
58
|
+
imports,
|
|
58
59
|
blocks: {
|
|
59
60
|
uniqueStringFieldsBlocks,
|
|
60
61
|
defaultValueBlocks,
|
|
@@ -101,6 +102,10 @@ export class ${meta.data.repositoryClassName} implements Repository<${model.type
|
|
|
101
102
|
|
|
102
103
|
${indexBlocks.nestedMapDeclarations.join('\n')}
|
|
103
104
|
|
|
105
|
+
${mainBlocks.userRepositorySpecificBlocks.rootUserNameConst}
|
|
106
|
+
|
|
107
|
+
${mainBlocks.userRepositorySpecificBlocks.getterBlock}
|
|
108
|
+
|
|
104
109
|
${mainBlocks.constructorCode}
|
|
105
110
|
|
|
106
111
|
${mainBlocks.initCode}
|
|
@@ -109,6 +114,8 @@ export class ${meta.data.repositoryClassName} implements Repository<${model.type
|
|
|
109
114
|
|
|
110
115
|
${mainBlocks.deleteAllCode}
|
|
111
116
|
|
|
117
|
+
${mainBlocks.userRepositorySpecificBlocks.rootUserInitializeBlock}
|
|
118
|
+
|
|
112
119
|
public async get(id: ${model.brandedIdType} | null): Promise<${model.typeName} | null> {
|
|
113
120
|
if (id === null) {
|
|
114
121
|
return Promise.resolve(null)
|
|
@@ -205,10 +212,12 @@ exports.generateMockRepository = generateMockRepository;
|
|
|
205
212
|
/**
|
|
206
213
|
* Generates the main building blocks of the repository for in-memory model.
|
|
207
214
|
*/
|
|
208
|
-
function _generateMainBuildingBlocks_InMemoryOnly({ model, meta, blocks, }) {
|
|
215
|
+
function _generateMainBuildingBlocks_InMemoryOnly({ model, meta, imports, blocks, }) {
|
|
209
216
|
const methodTypeSignatures = getRepositoryMethodsTypeSignatures({ model, meta });
|
|
217
|
+
const userRepositorySpecificBlocks = generateUserRepositorySpecificBlocks_InMemoryOnly({ model, meta, imports });
|
|
210
218
|
return {
|
|
211
219
|
constructorCode: '',
|
|
220
|
+
userRepositorySpecificBlocks,
|
|
212
221
|
initCode: `
|
|
213
222
|
public async init() {
|
|
214
223
|
this.data.clear()
|
|
@@ -218,6 +227,8 @@ function _generateMainBuildingBlocks_InMemoryOnly({ model, meta, blocks, }) {
|
|
|
218
227
|
|
|
219
228
|
${blocks.indexBlocks.initCode.join('\n')}
|
|
220
229
|
|
|
230
|
+
${userRepositorySpecificBlocks.initCall}
|
|
231
|
+
|
|
221
232
|
return Promise.resolve()
|
|
222
233
|
}`,
|
|
223
234
|
reInitCode: `
|
|
@@ -446,8 +457,10 @@ function generateMainBuildingBlocks_InDatabase({ model, meta, imports, blocks, }
|
|
|
446
457
|
.map((part) => `"${part}"`)
|
|
447
458
|
.join('.');
|
|
448
459
|
const methodTypeSignatures = getRepositoryMethodsTypeSignatures({ model, meta });
|
|
460
|
+
const userRepositorySpecificBlocks = generateUserRepositorySpecificBlocks_InDatabase({ model, meta, imports });
|
|
449
461
|
return {
|
|
450
462
|
constructorCode: `constructor(protected db: DbService) {}`,
|
|
463
|
+
userRepositorySpecificBlocks,
|
|
451
464
|
initCode: `
|
|
452
465
|
public async init() {
|
|
453
466
|
this.data.clear()
|
|
@@ -471,6 +484,8 @@ function generateMainBuildingBlocks_InDatabase({ model, meta, imports, blocks, }
|
|
|
471
484
|
|
|
472
485
|
${blocks.defaultValueBlocks.init.checkCode}
|
|
473
486
|
|
|
487
|
+
${userRepositorySpecificBlocks.initCall}
|
|
488
|
+
|
|
474
489
|
this.logger.log(\`\${format(this.data.size)} \${pluralize('${model.typeName}', this.data.size)} loaded\`)
|
|
475
490
|
${blocks.indexBlocks.initLogCode.join('\n')}
|
|
476
491
|
}`,
|
|
@@ -741,6 +756,100 @@ function generateMainBuildingBlocks_InDatabase({ model, meta, imports, blocks, }
|
|
|
741
756
|
}`,
|
|
742
757
|
};
|
|
743
758
|
}
|
|
759
|
+
function generateUserRepositorySpecificBlocks_InDatabase({ model, meta, imports, }) {
|
|
760
|
+
if (model.name !== 'User') {
|
|
761
|
+
return {
|
|
762
|
+
rootUserNameConst: '',
|
|
763
|
+
getterBlock: '',
|
|
764
|
+
initCall: '',
|
|
765
|
+
rootUserInitializeBlock: '',
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
imports.addImport({
|
|
769
|
+
from: meta.types.importPath,
|
|
770
|
+
items: [(0, types_1.toTypeName)('UserRole')],
|
|
771
|
+
});
|
|
772
|
+
return {
|
|
773
|
+
rootUserNameConst: `public static ROOT_USER_ID = ${meta.types.toBrandedIdTypeFnName}('root')`,
|
|
774
|
+
getterBlock: `
|
|
775
|
+
// We initialize the root user in the init() function
|
|
776
|
+
private _rootUser!: ${meta.types.typeName}
|
|
777
|
+
public get rootUser(): ${meta.types.typeName} {
|
|
778
|
+
return this._rootUser
|
|
779
|
+
}`,
|
|
780
|
+
initCall: `await this.initializeRootUser()`,
|
|
781
|
+
rootUserInitializeBlock: `
|
|
782
|
+
private async initializeRootUser(): Promise<void> {
|
|
783
|
+
const existingRootUser = await this.get(${meta.data.repositoryClassName}.ROOT_USER_ID)
|
|
784
|
+
if (existingRootUser) {
|
|
785
|
+
this._rootUser = existingRootUser
|
|
786
|
+
return
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
const rawUser = await this.db.user.create({
|
|
790
|
+
data: {
|
|
791
|
+
id: ${meta.data.repositoryClassName}.ROOT_USER_ID,
|
|
792
|
+
name: 'System',
|
|
793
|
+
email: 'system@postxl.com',
|
|
794
|
+
role: UserRole.Admin,
|
|
795
|
+
login: 'not-set',
|
|
796
|
+
familyName: 'System user',
|
|
797
|
+
age: 0,
|
|
798
|
+
countryId: null,
|
|
799
|
+
},
|
|
800
|
+
})
|
|
801
|
+
const newRootUser = this.toUser(rawUser)
|
|
802
|
+
this.set(newRootUser)
|
|
803
|
+
this._rootUser = newRootUser
|
|
804
|
+
}`,
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
function generateUserRepositorySpecificBlocks_InMemoryOnly({ model, meta, imports, }) {
|
|
808
|
+
if (model.name !== 'User') {
|
|
809
|
+
return {
|
|
810
|
+
rootUserNameConst: '',
|
|
811
|
+
getterBlock: '',
|
|
812
|
+
initCall: '',
|
|
813
|
+
rootUserInitializeBlock: '',
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
imports.addImport({
|
|
817
|
+
from: meta.types.importPath,
|
|
818
|
+
items: [(0, types_1.toTypeName)('UserRole')],
|
|
819
|
+
});
|
|
820
|
+
return {
|
|
821
|
+
rootUserNameConst: `public static ROOT_USER_ID = ${meta.types.toBrandedIdTypeFnName}('root')`,
|
|
822
|
+
getterBlock: `
|
|
823
|
+
// We initialize the root user in the init() function
|
|
824
|
+
private _rootUser!: ${meta.types.typeName}
|
|
825
|
+
public get rootUser(): ${meta.types.typeName} {
|
|
826
|
+
return this._rootUser
|
|
827
|
+
}`,
|
|
828
|
+
initCall: `await this.initializeRootUser()`,
|
|
829
|
+
rootUserInitializeBlock: `
|
|
830
|
+
private async initializeRootUser(): Promise<void> {
|
|
831
|
+
const existingRootUser = await this.get(${meta.data.repositoryClassName}.ROOT_USER_ID)
|
|
832
|
+
if (existingRootUser) {
|
|
833
|
+
this._rootUser = existingRootUser
|
|
834
|
+
return
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
const rawUser = {
|
|
838
|
+
id: ${meta.data.repositoryClassName}.ROOT_USER_ID,
|
|
839
|
+
name: 'System',
|
|
840
|
+
email: 'system@postxl.com',
|
|
841
|
+
role: UserRole.Admin,
|
|
842
|
+
login: 'not-set',
|
|
843
|
+
familyName: 'System user',
|
|
844
|
+
age: 0,
|
|
845
|
+
countryId: null,
|
|
846
|
+
}
|
|
847
|
+
const newRootUser = this.verifyItem (rawUser)
|
|
848
|
+
this.set(newRootUser)
|
|
849
|
+
this._rootUser = newRootUser
|
|
850
|
+
}`,
|
|
851
|
+
};
|
|
852
|
+
}
|
|
744
853
|
/**
|
|
745
854
|
* Generates code chunks responsible for verifying the ID validity of a model instance and generating the id
|
|
746
855
|
* value of a model with auto-generated id.
|
|
@@ -31,7 +31,7 @@ function generateRoute({ model, meta }) {
|
|
|
31
31
|
meta.businessLogic.update.createActionFunctionNameDeleteMany,
|
|
32
32
|
],
|
|
33
33
|
});
|
|
34
|
-
return `
|
|
34
|
+
return /* ts */ `
|
|
35
35
|
import { z } from 'zod'
|
|
36
36
|
import { procedure, router } from '../trpc'
|
|
37
37
|
|
|
@@ -107,7 +107,7 @@ function generateRoutesIndex({ models, meta }) {
|
|
|
107
107
|
for (const { meta } of mm) {
|
|
108
108
|
imports.addImport({ items: [meta.trpc.routerName], from: meta.trpc.routerFilePath });
|
|
109
109
|
}
|
|
110
|
-
return `
|
|
110
|
+
return /* ts */ `
|
|
111
111
|
${imports.generate()}
|
|
112
112
|
|
|
113
113
|
/**
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
type ElementId = string & {
|
|
2
|
+
__brand: 'ElementId';
|
|
3
|
+
};
|
|
4
|
+
/**
|
|
5
|
+
* The SelectorCollector is used to generate and track HTML element IDs.
|
|
6
|
+
* The idea is that it is instantiated once and then used to generate IDs throughout the generators.
|
|
7
|
+
* After the files are generated, all generated IDs can be serialized to a file.
|
|
8
|
+
*
|
|
9
|
+
* An ID can for instance be something like `post-create-name, `${model.name}-${componentType}-${fieldName}`.
|
|
10
|
+
*
|
|
11
|
+
* Each code generator can instantiate an SelectorCollector - the constructor will require the current model & component type.
|
|
12
|
+
* All generated Ids will be stored in a global static Set so we can serialize them later.
|
|
13
|
+
*/
|
|
14
|
+
export declare class SelectorCollector {
|
|
15
|
+
private _prefix;
|
|
16
|
+
/**
|
|
17
|
+
* We store all generated IDs in a global static Set so we can serialize them later.
|
|
18
|
+
*/
|
|
19
|
+
private static _ids;
|
|
20
|
+
constructor(_prefix: string);
|
|
21
|
+
/**
|
|
22
|
+
* Creates a new instance of the SelectorCollector.
|
|
23
|
+
*/
|
|
24
|
+
static from(prefix: string): SelectorCollector;
|
|
25
|
+
/**
|
|
26
|
+
* Generates a new ID for an element and stores it in the collector
|
|
27
|
+
*/
|
|
28
|
+
idFor(
|
|
29
|
+
/**
|
|
30
|
+
* The name of the element, e.g. `age`, `submit`.
|
|
31
|
+
*/
|
|
32
|
+
elementName: string, options?: {
|
|
33
|
+
/**
|
|
34
|
+
* Optional prefix for the type of the element, e.g. `field` or `button`.
|
|
35
|
+
*/
|
|
36
|
+
typePrefix?: string;
|
|
37
|
+
}): string;
|
|
38
|
+
/**
|
|
39
|
+
* Returns all generated IDs and resets the stored IDs.
|
|
40
|
+
*/
|
|
41
|
+
static flush(): ElementId[];
|
|
42
|
+
}
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SelectorCollector = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* The SelectorCollector is used to generate and track HTML element IDs.
|
|
6
|
+
* The idea is that it is instantiated once and then used to generate IDs throughout the generators.
|
|
7
|
+
* After the files are generated, all generated IDs can be serialized to a file.
|
|
8
|
+
*
|
|
9
|
+
* An ID can for instance be something like `post-create-name, `${model.name}-${componentType}-${fieldName}`.
|
|
10
|
+
*
|
|
11
|
+
* Each code generator can instantiate an SelectorCollector - the constructor will require the current model & component type.
|
|
12
|
+
* All generated Ids will be stored in a global static Set so we can serialize them later.
|
|
13
|
+
*/
|
|
14
|
+
class SelectorCollector {
|
|
15
|
+
constructor(_prefix) {
|
|
16
|
+
this._prefix = _prefix;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new instance of the SelectorCollector.
|
|
20
|
+
*/
|
|
21
|
+
static from(prefix) {
|
|
22
|
+
return new SelectorCollector(prefix);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Generates a new ID for an element and stores it in the collector
|
|
26
|
+
*/
|
|
27
|
+
idFor(
|
|
28
|
+
/**
|
|
29
|
+
* The name of the element, e.g. `age`, `submit`.
|
|
30
|
+
*/
|
|
31
|
+
elementName, options) {
|
|
32
|
+
const { typePrefix } = options !== null && options !== void 0 ? options : {};
|
|
33
|
+
const id = [this._prefix, typePrefix, elementName].filter((x) => x !== undefined && x !== '').join('-');
|
|
34
|
+
if (SelectorCollector._ids.has(id)) {
|
|
35
|
+
throw new Error(`ID ${id} already exists.`);
|
|
36
|
+
}
|
|
37
|
+
SelectorCollector._ids.add(id);
|
|
38
|
+
return id;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Returns all generated IDs and resets the stored IDs.
|
|
42
|
+
*/
|
|
43
|
+
static flush() {
|
|
44
|
+
const result = [...SelectorCollector._ids.values()];
|
|
45
|
+
SelectorCollector._ids.clear();
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.SelectorCollector = SelectorCollector;
|
|
50
|
+
/**
|
|
51
|
+
* We store all generated IDs in a global static Set so we can serialize them later.
|
|
52
|
+
*/
|
|
53
|
+
SelectorCollector._ids = new Set();
|
package/dist/lib/meta.d.ts
CHANGED
|
@@ -51,10 +51,22 @@ export type SchemaMetaData = {
|
|
|
51
51
|
* Path to the file containing the mock data for the database.
|
|
52
52
|
*/
|
|
53
53
|
dataMockModuleFilePath: Types.Path;
|
|
54
|
+
/**
|
|
55
|
+
* Name of the mock data module class.
|
|
56
|
+
*/
|
|
57
|
+
dataMockModuleName: Types.ClassName;
|
|
58
|
+
/**
|
|
59
|
+
* Name of the data mock type/interface.
|
|
60
|
+
*/
|
|
61
|
+
dataMockDataType: Types.TypeName;
|
|
54
62
|
/**
|
|
55
63
|
* Path to the file containing data service class definitions.
|
|
56
64
|
*/
|
|
57
65
|
dataServiceFilePath: Types.Path;
|
|
66
|
+
/**
|
|
67
|
+
* Name of the data service class.
|
|
68
|
+
*/
|
|
69
|
+
dataServiceClassName: Types.ClassName;
|
|
58
70
|
/**
|
|
59
71
|
* Path to the file containing the repository type definition.
|
|
60
72
|
*/
|
|
@@ -67,6 +79,14 @@ export type SchemaMetaData = {
|
|
|
67
79
|
* Path to the file containing data mocker class definitions.
|
|
68
80
|
*/
|
|
69
81
|
dataMockerFilePath: Types.Path;
|
|
82
|
+
/**
|
|
83
|
+
* Path to the file containing component selectors for e2e tests.
|
|
84
|
+
*/
|
|
85
|
+
selectorsFilePath: Types.Path;
|
|
86
|
+
/**
|
|
87
|
+
* Path to the file containing the testDataService class definition.
|
|
88
|
+
*/
|
|
89
|
+
testDataServiceFilePath: Types.Path;
|
|
70
90
|
/**
|
|
71
91
|
* Path to the file containing data mocker class definitions.
|
|
72
92
|
*/
|
|
@@ -207,6 +227,10 @@ export type SchemaMetaData = {
|
|
|
207
227
|
* Path that may be used in the import statement.
|
|
208
228
|
*/
|
|
209
229
|
importPath: Types.Path;
|
|
230
|
+
/**
|
|
231
|
+
* Name of the function that converts mock data to seed data.
|
|
232
|
+
*/
|
|
233
|
+
mockDataToCreateFunction: Types.Fnction;
|
|
210
234
|
};
|
|
211
235
|
trpc: {
|
|
212
236
|
/**
|
package/dist/lib/meta.js
CHANGED
|
@@ -45,10 +45,15 @@ function getSchemaMetadata({ config }) {
|
|
|
45
45
|
importPath: Types.toPath(`@${config.project}/data`),
|
|
46
46
|
dataModuleFilePath: Types.toPath(`${config.paths.dataLibPath}data.module`),
|
|
47
47
|
dataMockModuleFilePath: Types.toPath(`${config.paths.dataLibPath}data.mock.module`),
|
|
48
|
+
dataMockModuleName: Types.toClassName(`DataMockModule`),
|
|
49
|
+
dataMockDataType: Types.toTypeName(`MockData`),
|
|
48
50
|
dataServiceFilePath: Types.toPath(`${config.paths.dataLibPath}data.service`),
|
|
51
|
+
dataServiceClassName: Types.toClassName(`DataService`),
|
|
49
52
|
repositoryTypeFilePath: Types.toPath(`${config.paths.dataLibPath}repository.type`),
|
|
50
53
|
repositoryTypeName: Types.toTypeName(`Repository`),
|
|
51
54
|
dataMockerFilePath: Types.toPath(`${config.paths.cypressPath}support/data-mocker.class`),
|
|
55
|
+
selectorsFilePath: Types.toPath(`${config.paths.cypressPath}support/selectors`),
|
|
56
|
+
testDataServiceFilePath: Types.toPath(`${config.paths.e2eLibPath}test-data.service`),
|
|
52
57
|
dataMockerStubImportPath: Types.toPath(`${config.paths.cypressPath}support/stubs`),
|
|
53
58
|
dataMockerStubIndexFilePath: Types.toPath(`${config.paths.cypressPath}support/stubs/index`),
|
|
54
59
|
repositoriesConstFilePath: Types.toPath(`${config.paths.dataLibPath}repositories/repositories`),
|
|
@@ -94,6 +99,7 @@ function getSchemaMetadata({ config }) {
|
|
|
94
99
|
serviceFilePath: Types.toPath(`${config.paths.seedLibPath}seed.service`),
|
|
95
100
|
serviceClassName: Types.toClassName(`SeedService`),
|
|
96
101
|
importPath: Types.toPath(`@${config.project}/seed`),
|
|
102
|
+
mockDataToCreateFunction: Types.toFunction(`mockDataToCreate`),
|
|
97
103
|
},
|
|
98
104
|
types: {
|
|
99
105
|
indexFilePath: Types.toPath(`${config.paths.modelTypeDefinitionsPath}index`),
|
|
@@ -63,6 +63,10 @@ export type SchemaConfig = {
|
|
|
63
63
|
* Path to the directory containing Cypress project.
|
|
64
64
|
*/
|
|
65
65
|
cypressPath: Types.Path;
|
|
66
|
+
/**
|
|
67
|
+
* Path to the directory containing e2e tests.
|
|
68
|
+
*/
|
|
69
|
+
e2eLibPath: Types.Path;
|
|
66
70
|
/**
|
|
67
71
|
* Path to the directory containing actions.
|
|
68
72
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@postxl/generator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.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",
|