@postxl/generator 0.16.8 → 0.19.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 +8 -1
- package/dist/generators/indices/businesslogicmodule.generator.js +34 -8
- package/dist/generators/indices/datamockmodule.generator.js +7 -15
- package/dist/generators/indices/datamodule.generator.js +31 -31
- package/dist/generators/indices/dataservice.generator.js +2 -2
- package/dist/generators/indices/emptydatabasemigration.generator.d.ts +12 -1
- package/dist/generators/indices/emptydatabasemigration.generator.js +34 -2
- package/dist/generators/indices/seed-service.generator.d.ts +9 -0
- package/dist/generators/indices/seed-service.generator.js +162 -0
- package/dist/generators/indices/seed-template-decoder.generator.js +1 -3
- package/dist/generators/models/seed.generator.js +8 -0
- package/dist/lib/meta.d.ts +8 -0
- package/dist/lib/meta.js +5 -3
- package/package.json +2 -2
package/dist/generator.js
CHANGED
|
@@ -67,6 +67,7 @@ const businesslogicmodule_generator_1 = require("./generators/indices/businesslo
|
|
|
67
67
|
const businesslogic_generator_1 = require("./generators/models/businesslogic.generator");
|
|
68
68
|
const seed_template_decoder_generator_1 = require("./generators/indices/seed-template-decoder.generator");
|
|
69
69
|
const seed_template_generator_1 = require("./generators/indices/seed-template.generator");
|
|
70
|
+
const seed_service_generator_1 = require("./generators/indices/seed-service.generator");
|
|
70
71
|
const CONFIG_SCHEMA = zod_1.z
|
|
71
72
|
.object({
|
|
72
73
|
project: zod_1.z.string(),
|
|
@@ -197,7 +198,12 @@ function generate({ models, enums, config, prismaClientPath, logger, }) {
|
|
|
197
198
|
generated.write(`/${meta.data.repositoriesConstFilePath}.ts`, (0, repositories_generator_1.generateRepositoriesArray)({ models, meta }));
|
|
198
199
|
generated.write(`/${meta.data.repositoriesIndexFilePath}.ts`, (0, repositories_generator_1.generateRepositoriesIndex)({ models, meta }));
|
|
199
200
|
generated.write(`/${meta.data.stubIndexFilePath}.ts`, (0, stubs_generator_1.generateStubsIndex)({ models, meta }));
|
|
200
|
-
|
|
201
|
+
// We only generate the empty database migration if the migration folder already has an existing migration
|
|
202
|
+
// Else we would generate a migration that deletes from tables that have not yet been created in the database
|
|
203
|
+
// We include this check here as the template does not come with any migration - hence this migration should also not be generated
|
|
204
|
+
if ((0, emptydatabasemigration_generator_1.prismaMigrationExists)(meta)) {
|
|
205
|
+
generated.write((0, emptydatabasemigration_generator_1.deriveEmptyDatabaseMigrationFilePath)(meta), (0, emptydatabasemigration_generator_1.generateEmptyDatabaseStoredProcedure)({ models, meta }));
|
|
206
|
+
}
|
|
201
207
|
}
|
|
202
208
|
if (!config.disableGenerators.businessLogic) {
|
|
203
209
|
generated.write(`/${meta.businessLogic.indexFilePath}.ts`, (0, businesslogicindex_generator_1.generateBusinessLogicIndex)({ models, meta }));
|
|
@@ -206,6 +212,7 @@ function generate({ models, enums, config, prismaClientPath, logger, }) {
|
|
|
206
212
|
}
|
|
207
213
|
if (!config.disableGenerators.seed) {
|
|
208
214
|
generated.write(`/${meta.seed.indexFilePath}.ts`, (0, seed_generator_1.generateSeedIndex)({ models, meta }));
|
|
215
|
+
generated.write(`/${meta.seed.serviceFilePath}.ts`, (0, seed_service_generator_1.generateSeedService)({ models, meta }));
|
|
209
216
|
generated.write(`/${meta.seed.templateExcelFilePath}`, yield (0, seed_template_generator_1.generateSeedExcelTemplate)({ models }));
|
|
210
217
|
generated.write(`/${meta.seed.templateDecoderFilePath}.ts`, (0, seed_template_decoder_generator_1.generateSeedTemplateDecoder)({ models, meta }));
|
|
211
218
|
}
|
|
@@ -27,19 +27,45 @@ function generateBusinessLogicModule({ models, meta }) {
|
|
|
27
27
|
});
|
|
28
28
|
providers.push(meta.businessLogic.serviceClassName);
|
|
29
29
|
}
|
|
30
|
+
const moduleName = meta.businessLogic.moduleName;
|
|
30
31
|
return `
|
|
31
|
-
import {
|
|
32
|
+
import { DynamicModule } from '@${meta.config.project}/common'
|
|
32
33
|
|
|
33
34
|
${imports.generate()}
|
|
34
35
|
|
|
35
|
-
const providers = [${providers.join(', ')}]
|
|
36
|
-
@Module({
|
|
37
|
-
imports: [${meta.data.moduleName}],
|
|
38
|
-
providers,
|
|
39
|
-
exports: providers,
|
|
40
|
-
})
|
|
41
|
-
export class BusinessLogicModule {}
|
|
42
36
|
|
|
37
|
+
export class ${moduleName} {
|
|
38
|
+
/**
|
|
39
|
+
* Internal cache for the module instance.
|
|
40
|
+
*/
|
|
41
|
+
private static cachedModule: DynamicModule | undefined = undefined
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The getInstance method should be called by any module that needs the module.
|
|
45
|
+
*/
|
|
46
|
+
static getInstance(): DynamicModule {
|
|
47
|
+
if (!${moduleName}.cachedModule) throw new Error('${moduleName} must be called via .provide first!')
|
|
48
|
+
return ${moduleName}.cachedModule
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The forRoot method should only be called once by the root module.
|
|
53
|
+
*/
|
|
54
|
+
static forRoot(): DynamicModule {
|
|
55
|
+
if (${moduleName}.cachedModule) {
|
|
56
|
+
throw new Error('${moduleName} is already instantiated, please call .forRoot only once from root...')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const providers = [${providers.join(', ')}]
|
|
60
|
+
${moduleName}.cachedModule = {
|
|
61
|
+
module: ${moduleName},
|
|
62
|
+
providers,
|
|
63
|
+
imports: [${meta.data.moduleName}.getInstance()],
|
|
64
|
+
exports: providers,
|
|
65
|
+
}
|
|
66
|
+
return ${moduleName}.cachedModule
|
|
67
|
+
}
|
|
68
|
+
}
|
|
43
69
|
`;
|
|
44
70
|
}
|
|
45
71
|
exports.generateBusinessLogicModule = generateBusinessLogicModule;
|
|
@@ -69,33 +69,25 @@ import { DbModule } from '@${meta.config.project}/db'
|
|
|
69
69
|
${imports.generate()}
|
|
70
70
|
|
|
71
71
|
export class DataMockModule {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
static isInstantiated(): boolean {
|
|
75
|
-
return !!DataMockModule.cachedModule
|
|
76
|
-
}
|
|
77
|
-
static getInstance(): DataModule {
|
|
78
|
-
if (!DataMockModule.cachedModule) throw new Error('DataMockModule must be called via .mock first!')
|
|
79
|
-
return DataMockModule.cachedModule
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-unused-vars
|
|
83
|
-
static async mock(seed?: MockData): Promise<DynamicModule> {
|
|
72
|
+
|
|
73
|
+
static mock(seed?: MockData): DynamicModule {
|
|
84
74
|
const providers = [
|
|
85
75
|
DataService,
|
|
86
76
|
TestDataService,
|
|
87
77
|
${providers}
|
|
88
78
|
]
|
|
89
79
|
|
|
90
|
-
|
|
80
|
+
const cachedModule = {
|
|
91
81
|
module: DataModule,
|
|
92
82
|
imports: [DbModule.provideMock()],
|
|
93
83
|
providers: providers,
|
|
94
84
|
exports: providers,
|
|
95
85
|
global: true,
|
|
96
86
|
}
|
|
97
|
-
|
|
98
|
-
|
|
87
|
+
|
|
88
|
+
${meta.data.moduleName}._storeMockModule(cachedModule)
|
|
89
|
+
|
|
90
|
+
return cachedModule
|
|
99
91
|
}
|
|
100
92
|
}
|
|
101
93
|
|
|
@@ -49,52 +49,52 @@ function generateDataModule({ models, meta }) {
|
|
|
49
49
|
}
|
|
50
50
|
const moduleName = meta.data.moduleName;
|
|
51
51
|
return `
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
52
|
+
import { Provider } from '@nestjs/common'
|
|
53
|
+
import { DynamicModule } from '@${meta.config.project}/common'
|
|
54
|
+
import { DbModule } from '@${meta.config.project}/db'
|
|
54
55
|
|
|
55
|
-
import {
|
|
56
|
+
import { Repository } from './repository.type'
|
|
56
57
|
|
|
57
58
|
${imports.generate()}
|
|
58
59
|
|
|
59
|
-
const createRepositoryProvider = <T extends AsyncInit>(t: Type<T>, loadData: boolean): Provider => ({
|
|
60
|
-
inject: [DbService],
|
|
61
|
-
provide: t,
|
|
62
|
-
useFactory: async (dbService: DbService): Promise<T> => {
|
|
63
|
-
const repository = new t(dbService)
|
|
64
|
-
await dbService.isInitialized(t.name)
|
|
65
|
-
if (loadData) {
|
|
66
|
-
await repository.init()
|
|
67
|
-
}
|
|
68
|
-
return repository
|
|
69
|
-
},
|
|
70
|
-
})
|
|
71
|
-
|
|
72
60
|
export class ${moduleName} {
|
|
61
|
+
/**
|
|
62
|
+
* Internal cache for the module instance.
|
|
63
|
+
*/
|
|
73
64
|
private static cachedModule: DynamicModule | undefined = undefined
|
|
74
65
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
static getInstance(): ${moduleName} {
|
|
66
|
+
/**
|
|
67
|
+
* The getInstance method should be called by any module that needs the module.
|
|
68
|
+
*/
|
|
69
|
+
static getInstance(): DynamicModule {
|
|
80
70
|
if (!${moduleName}.cachedModule) throw new Error('${moduleName} must be called via .provide first!')
|
|
81
71
|
return ${moduleName}.cachedModule
|
|
82
72
|
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Stores another module instance in the cache.
|
|
76
|
+
* Note: This should only be used for testing purposes.
|
|
77
|
+
*/
|
|
78
|
+
static _storeMockModule(module: DynamicModule): void {
|
|
79
|
+
${moduleName}.cachedModule = module
|
|
80
|
+
}
|
|
83
81
|
|
|
84
|
-
|
|
82
|
+
/**
|
|
83
|
+
* The forRoot method should only be called once by the root module.
|
|
84
|
+
*/
|
|
85
|
+
static forRoot(): DynamicModule {
|
|
85
86
|
if (${moduleName}.cachedModule) {
|
|
86
|
-
|
|
87
|
-
return ${moduleName}.cachedModule
|
|
87
|
+
throw new Error('${moduleName} is already instantiated, please call .forRoot only once from root...')
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
const repositoryProviders = [
|
|
91
|
-
${mm.map(({ meta }) =>
|
|
91
|
+
${mm.map(({ meta }) => meta.data.repositoryClassName).join(',')}
|
|
92
92
|
]
|
|
93
93
|
|
|
94
94
|
${moduleName}.cachedModule = {
|
|
95
95
|
module: ${moduleName},
|
|
96
96
|
global: true,
|
|
97
|
-
imports: [DbModule.
|
|
97
|
+
imports: [DbModule.forRoot()],
|
|
98
98
|
providers: [DataService, ...repositoryProviders],
|
|
99
99
|
exports: [DataService, ...repositoryProviders],
|
|
100
100
|
}
|
|
@@ -108,16 +108,16 @@ export class ${moduleName} {
|
|
|
108
108
|
return ${moduleName}.cachedModule
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
TestDataService,
|
|
111
|
+
const repositoryProviders = [
|
|
112
|
+
${mm.map(({ meta }) => meta.data.repositoryClassName).join(',')}
|
|
114
113
|
]
|
|
114
|
+
const providers = [TestDataService, ...repositoryProviders]
|
|
115
115
|
|
|
116
116
|
${moduleName}.cachedModule = {
|
|
117
117
|
module: ${moduleName},
|
|
118
118
|
global: true,
|
|
119
|
-
imports: [DbModule.
|
|
120
|
-
providers
|
|
119
|
+
imports: [DbModule.forRoot()],
|
|
120
|
+
providers,
|
|
121
121
|
exports: providers,
|
|
122
122
|
}
|
|
123
123
|
|
|
@@ -18,7 +18,7 @@ function generateDataService({ models, meta }) {
|
|
|
18
18
|
const constructor = mm
|
|
19
19
|
.map(({ meta }) => `public ${meta.data.dataServiceName} :${meta.data.repositoryClassName}`)
|
|
20
20
|
.join(',\n');
|
|
21
|
-
const initializer = mm.map(({ meta }) => `this.${meta.data.dataServiceName}.init()`).join('
|
|
21
|
+
const initializer = mm.map(({ meta }) => `await this.${meta.data.dataServiceName}.init()`).join('\n');
|
|
22
22
|
const excelExports = mm
|
|
23
23
|
.map(({ meta }) => `${meta.data.excelExportTableName}: mapValues(this.${meta.data.dataServiceName}.getAll()),`)
|
|
24
24
|
.join('\n');
|
|
@@ -40,7 +40,7 @@ export class DataService {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
public async init(): Promise<void> {
|
|
43
|
-
|
|
43
|
+
${initializer}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
public isEmpty(): boolean {
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import { SchemaMetaData } from '../../lib/meta';
|
|
2
2
|
import { Model } from '../../lib/schema/schema';
|
|
3
3
|
/**
|
|
4
|
-
* Generates a
|
|
4
|
+
* Generates a the Prisma migration to create the `emptyDatabase` stored procedure.
|
|
5
5
|
*/
|
|
6
6
|
export declare function generateEmptyDatabaseStoredProcedure({ models }: {
|
|
7
7
|
models: Model[];
|
|
8
8
|
meta: SchemaMetaData;
|
|
9
9
|
}): string;
|
|
10
|
+
/**
|
|
11
|
+
* Generates a the full file path for the Prisma migration to create the `emptyDatabase` stored procedure.
|
|
12
|
+
*
|
|
13
|
+
* This is done by finding the first migration in the migrations folder and using its timestamp + 1.
|
|
14
|
+
* In case no migration is found, an error is shown!
|
|
15
|
+
*/
|
|
16
|
+
export declare function deriveEmptyDatabaseMigrationFilePath(meta: SchemaMetaData): string;
|
|
17
|
+
/**
|
|
18
|
+
* Checks if any Prisma migration exists.
|
|
19
|
+
*/
|
|
20
|
+
export declare function prismaMigrationExists(meta: SchemaMetaData): boolean;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.generateEmptyDatabaseStoredProcedure = void 0;
|
|
6
|
+
exports.prismaMigrationExists = exports.deriveEmptyDatabaseMigrationFilePath = exports.generateEmptyDatabaseStoredProcedure = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
4
8
|
/**
|
|
5
|
-
* Generates a
|
|
9
|
+
* Generates a the Prisma migration to create the `emptyDatabase` stored procedure.
|
|
6
10
|
*/
|
|
7
11
|
function generateEmptyDatabaseStoredProcedure({ models }) {
|
|
8
12
|
const tables = models
|
|
@@ -24,3 +28,31 @@ $BODY$ LANGUAGE plpgsql;
|
|
|
24
28
|
`;
|
|
25
29
|
}
|
|
26
30
|
exports.generateEmptyDatabaseStoredProcedure = generateEmptyDatabaseStoredProcedure;
|
|
31
|
+
/**
|
|
32
|
+
* Generates a the full file path for the Prisma migration to create the `emptyDatabase` stored procedure.
|
|
33
|
+
*
|
|
34
|
+
* This is done by finding the first migration in the migrations folder and using its timestamp + 1.
|
|
35
|
+
* In case no migration is found, an error is shown!
|
|
36
|
+
*/
|
|
37
|
+
function deriveEmptyDatabaseMigrationFilePath(meta) {
|
|
38
|
+
const firstMigration = getFirstPrismaMigration(meta);
|
|
39
|
+
if (firstMigration === undefined) {
|
|
40
|
+
throw new Error(`No Prisma migration found in ${meta.migrationsPath}! Please run "prisma migrate dev" first.`);
|
|
41
|
+
}
|
|
42
|
+
const existingTimestamp = firstMigration.split('_')[0];
|
|
43
|
+
const nextTimestamp = (Number.parseInt(existingTimestamp) + 1).toString();
|
|
44
|
+
return `/${meta.migrationsPath}/${nextTimestamp}_emptyDatabase/migration.sql`;
|
|
45
|
+
}
|
|
46
|
+
exports.deriveEmptyDatabaseMigrationFilePath = deriveEmptyDatabaseMigrationFilePath;
|
|
47
|
+
/**
|
|
48
|
+
* Checks if any Prisma migration exists.
|
|
49
|
+
*/
|
|
50
|
+
function prismaMigrationExists(meta) {
|
|
51
|
+
const firstMigration = getFirstPrismaMigration(meta);
|
|
52
|
+
return firstMigration !== undefined;
|
|
53
|
+
}
|
|
54
|
+
exports.prismaMigrationExists = prismaMigrationExists;
|
|
55
|
+
function getFirstPrismaMigration(meta) {
|
|
56
|
+
const folder = meta.migrationsPath;
|
|
57
|
+
return fs_1.default.readdirSync(folder).find((f) => fs_1.default.statSync(`${folder}/${f}`).isDirectory());
|
|
58
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SchemaMetaData } from '../../lib/meta';
|
|
2
|
+
import { Model } from '../../lib/schema/schema';
|
|
3
|
+
/**
|
|
4
|
+
* Generates index file for all seed files.
|
|
5
|
+
*/
|
|
6
|
+
export declare function generateSeedService({ models, meta }: {
|
|
7
|
+
models: Model[];
|
|
8
|
+
meta: SchemaMetaData;
|
|
9
|
+
}): string;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateSeedService = void 0;
|
|
4
|
+
const imports_1 = require("../../lib/imports");
|
|
5
|
+
const meta_1 = require("../../lib/meta");
|
|
6
|
+
/**
|
|
7
|
+
* Generates index file for all seed files.
|
|
8
|
+
*/
|
|
9
|
+
function generateSeedService({ models, meta }) {
|
|
10
|
+
const imports = imports_1.ImportsGenerator.from(meta.seed.serviceFilePath).addImport({
|
|
11
|
+
from: meta.seed.templateDecoderFilePath,
|
|
12
|
+
items: [meta.seed.templateDecoderName],
|
|
13
|
+
});
|
|
14
|
+
const dataLoader = [];
|
|
15
|
+
for (const model of models) {
|
|
16
|
+
const modelMeta = (0, meta_1.getModelMetadata)({ model });
|
|
17
|
+
dataLoader.push(`await this.upload({ name: '${modelMeta.userFriendlyName}', data: data.${modelMeta.seed.constantName}, repo: this.dataService.${modelMeta.data.dataServiceName}, log })`);
|
|
18
|
+
}
|
|
19
|
+
return `
|
|
20
|
+
import { Injectable, Logger } from '@nestjs/common'
|
|
21
|
+
import { format, pluralize } from '@pxl/common'
|
|
22
|
+
import { DataService, Repository } from '@pxl/data'
|
|
23
|
+
import { DbService } from '@pxl/db'
|
|
24
|
+
import { XlPortService } from '@pxl/xlport'
|
|
25
|
+
${imports.generate()}
|
|
26
|
+
|
|
27
|
+
import * as SEEDDATA from './data'
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Configuration options for seeding
|
|
31
|
+
*/
|
|
32
|
+
export type SeedServiceOptions = {
|
|
33
|
+
/**
|
|
34
|
+
* If true, the database will be emptied before seeding.
|
|
35
|
+
* This is only possible if the database is set to test database!
|
|
36
|
+
*/
|
|
37
|
+
reset?: boolean
|
|
38
|
+
/**
|
|
39
|
+
* If true, the seed data will be logged to the console.
|
|
40
|
+
* Note: This is significantly slower - but can be useful for debugging.
|
|
41
|
+
*/
|
|
42
|
+
log?: boolean
|
|
43
|
+
/**
|
|
44
|
+
* If true, data will be loaded into the repositories after seeding.
|
|
45
|
+
* This should be used on startup of the application, but typically
|
|
46
|
+
* not when running tests or from the CLI.
|
|
47
|
+
*/
|
|
48
|
+
loadDataAfterSeeding?: boolean
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const DEFAULTOPTIONS: SeedServiceOptions = { reset: false, log: false, loadDataAfterSeeding: false }
|
|
52
|
+
@Injectable()
|
|
53
|
+
export class SeedService {
|
|
54
|
+
private readonly logger = new Logger(SeedService.name)
|
|
55
|
+
constructor(
|
|
56
|
+
private readonly dataService: DataService,
|
|
57
|
+
private readonly xlPortService: XlPortService,
|
|
58
|
+
private dbService: DbService,
|
|
59
|
+
) {}
|
|
60
|
+
/**
|
|
61
|
+
* Seeds the database with the given data. Any data not provided will be taken from the default seed data.
|
|
62
|
+
*/
|
|
63
|
+
public async seed({ data, options = DEFAULTOPTIONS }: { data?: Partial<typeof SEEDDATA>; options?: SeedServiceOptions }) {
|
|
64
|
+
return this._seed({ data: { ...SEEDDATA, ...data }, options })
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Seeds the database with the given data from an Excel file.
|
|
69
|
+
*/
|
|
70
|
+
public async seedFromExcel({
|
|
71
|
+
templatePath = './template.xlsx',
|
|
72
|
+
options = DEFAULTOPTIONS,
|
|
73
|
+
}: {
|
|
74
|
+
templatePath?: string
|
|
75
|
+
options?: SeedServiceOptions
|
|
76
|
+
}) {
|
|
77
|
+
const xlData = this.xlPortService.importFromFile(templatePath)
|
|
78
|
+
|
|
79
|
+
const dataParsed = ${meta.seed.templateDecoderName}.safeParse(xlData)
|
|
80
|
+
if (!dataParsed.success) {
|
|
81
|
+
throw new Error(\`Error in parsing Excel seed data: \${JSON.stringify(dataParsed.error.message)}\`)
|
|
82
|
+
}
|
|
83
|
+
return this._seed({ data: dataParsed.data, options })
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private async _seed({ data, options: { loadDataAfterSeeding, reset, log } }: { data: typeof SEEDDATA; options: SeedServiceOptions }) {
|
|
87
|
+
if (reset) {
|
|
88
|
+
this.logger.log('Resetting database')
|
|
89
|
+
await this.dbService.emptyDatabase()
|
|
90
|
+
} else {
|
|
91
|
+
const config = await this.dbService.config.findFirst({ where: { id: true } })
|
|
92
|
+
if (!config) {
|
|
93
|
+
throw new Error('Database is not initialized')
|
|
94
|
+
}
|
|
95
|
+
if (config.isSeeded) {
|
|
96
|
+
this.logger.log('Database is already seeded')
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
await this.uploadData({ data, log })
|
|
102
|
+
|
|
103
|
+
await this.dbService.config.update({
|
|
104
|
+
where: { id: true },
|
|
105
|
+
data: { isSeeded: true },
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
if (loadDataAfterSeeding) {
|
|
109
|
+
await this.dataService.init()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this.logger.log('✅ Done')
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private async uploadData({ log = false, data }: { data: typeof SEEDDATA; log?: boolean }) {
|
|
116
|
+
// NOTE: the order of these calls is important, because of foreign key constraints
|
|
117
|
+
// The current order is based on the order of the models in the schema
|
|
118
|
+
// Change the order based on your needs.
|
|
119
|
+
// Attention: Depending on the dependencies in seed data, you may also have to take care of relations,
|
|
120
|
+
// e.g. by removing any foreign key first - and then update these after all data is created.
|
|
121
|
+
${dataLoader.join('\n')}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private async upload<T extends { id: ID }, ID extends number | string | boolean>({
|
|
125
|
+
name,
|
|
126
|
+
data,
|
|
127
|
+
repo,
|
|
128
|
+
log = false,
|
|
129
|
+
onlyOnReset = true,
|
|
130
|
+
}: {
|
|
131
|
+
name: string
|
|
132
|
+
data: T[]
|
|
133
|
+
repo: Repository<T, ID>
|
|
134
|
+
log?: boolean
|
|
135
|
+
onlyOnReset?: boolean
|
|
136
|
+
}): Promise<void> {
|
|
137
|
+
if (repo.count() > 0 && onlyOnReset) {
|
|
138
|
+
this.logger.log(\`\${pluralize(name)} already exist, skipping\`)
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
if (!log) {
|
|
142
|
+
await repo.createMany(data)
|
|
143
|
+
} else {
|
|
144
|
+
let i = 0
|
|
145
|
+
for (const item of data) {
|
|
146
|
+
i++
|
|
147
|
+
console.log(name, i, item.id)
|
|
148
|
+
try {
|
|
149
|
+
await repo.createWithId?.(item)
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.log(item)
|
|
152
|
+
throw error
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.logger.log(\`✅ Created \${format(data.length)} \${pluralize(name)}\`)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
`;
|
|
161
|
+
}
|
|
162
|
+
exports.generateSeedService = generateSeedService;
|
|
@@ -28,7 +28,7 @@ function generateSeedTemplateDecoder({ models, meta }) {
|
|
|
28
28
|
|
|
29
29
|
${decoders.join('\n')}
|
|
30
30
|
|
|
31
|
-
export const
|
|
31
|
+
export const ${meta.seed.templateDecoderName} = z
|
|
32
32
|
.object({${tableDecoders.join(',\n')}})
|
|
33
33
|
.transform((item) => ({${renameTransforms.join(',\n')}}))
|
|
34
34
|
`;
|
|
@@ -73,8 +73,6 @@ function generateTableDecoder({ model, meta, imports, }) {
|
|
|
73
73
|
break;
|
|
74
74
|
}
|
|
75
75
|
case 'enum': {
|
|
76
|
-
const refEnumMeta = (0, meta_1.getEnumMetadata)({ enumerator: field.enumerator });
|
|
77
|
-
imports.addImport({ items: [field.enumerator.tsTypeName], from: refEnumMeta.types.importPath });
|
|
78
76
|
fieldDecoders.push(`${fieldMeta.excelColumnName}: z.enum([${field.enumerator.values.map((v) => `'${v}'`).join(', ')}])${field.isRequired ? '' : '.nullable()'}`);
|
|
79
77
|
break;
|
|
80
78
|
}
|
|
@@ -112,6 +112,7 @@ function quoteSingleQuote(str) {
|
|
|
112
112
|
return str.replace(/'/g, "\\'");
|
|
113
113
|
}
|
|
114
114
|
function generateScalarFieldExample({ field, model, index, mode, }) {
|
|
115
|
+
var _a;
|
|
115
116
|
const { hasExample, example } = generateFieldExample({ field, model, index, mode });
|
|
116
117
|
switch (field.tsTypeName) {
|
|
117
118
|
case 'string': {
|
|
@@ -143,6 +144,10 @@ function generateScalarFieldExample({ field, model, index, mode, }) {
|
|
|
143
144
|
if (hasExample) {
|
|
144
145
|
return `${example}`;
|
|
145
146
|
}
|
|
147
|
+
//in case the field is configured with `IsDefaultField` attribute, the first item should be true, all other false
|
|
148
|
+
if (((_a = model.defaultField) === null || _a === void 0 ? void 0 : _a.name) === field.name) {
|
|
149
|
+
return index === 0 ? 'true' : 'false';
|
|
150
|
+
}
|
|
146
151
|
return generateMockBoolean();
|
|
147
152
|
}
|
|
148
153
|
case 'Date': {
|
|
@@ -204,6 +209,9 @@ function generateMockDate() {
|
|
|
204
209
|
return `new Date('${d.toISOString()}')`;
|
|
205
210
|
}
|
|
206
211
|
function generateRelationFieldExample({ field, index, model, mode, }) {
|
|
212
|
+
if (!field.isRequired) {
|
|
213
|
+
return 'null';
|
|
214
|
+
}
|
|
207
215
|
const referenceId = faker_1.faker.datatype.number({ min: 1, max: mode.itemCount });
|
|
208
216
|
const refModelMeta = (0, meta_1.getModelMetadata)({ model: field.relationToModel });
|
|
209
217
|
const brandingFn = refModelMeta.types.toBrandedIdTypeFnName;
|
package/dist/lib/meta.d.ts
CHANGED
|
@@ -73,6 +73,10 @@ export type SchemaMetaData = {
|
|
|
73
73
|
* Path to the index file for the seed package.
|
|
74
74
|
*/
|
|
75
75
|
indexFilePath: Types.Path;
|
|
76
|
+
/**
|
|
77
|
+
* Path to the SeedService file.
|
|
78
|
+
*/
|
|
79
|
+
serviceFilePath: Types.Path;
|
|
76
80
|
/**
|
|
77
81
|
* Path to seed Excel template file.
|
|
78
82
|
*/
|
|
@@ -81,6 +85,10 @@ export type SchemaMetaData = {
|
|
|
81
85
|
* Path to template decoder file.
|
|
82
86
|
*/
|
|
83
87
|
templateDecoderFilePath: Types.Path;
|
|
88
|
+
/**
|
|
89
|
+
* The name of the template decoder function.
|
|
90
|
+
*/
|
|
91
|
+
templateDecoderName: Types.Fnction;
|
|
84
92
|
/**
|
|
85
93
|
* Path that may be used in the import statement.
|
|
86
94
|
*/
|
package/dist/lib/meta.js
CHANGED
|
@@ -55,9 +55,11 @@ function getSchemaMetadata({ config }) {
|
|
|
55
55
|
importPath: Types.toPath(`@${config.project}/trpc`),
|
|
56
56
|
},
|
|
57
57
|
seed: {
|
|
58
|
-
indexFilePath: Types.toPath(`${config.paths.seedPath}index`),
|
|
58
|
+
indexFilePath: Types.toPath(`${config.paths.seedPath}data/index`),
|
|
59
|
+
serviceFilePath: Types.toPath(`${config.paths.seedPath}seed.service`),
|
|
59
60
|
templateExcelFilePath: Types.toPath(`${config.paths.seedPath}template.xlsx`),
|
|
60
|
-
templateDecoderFilePath: Types.toPath(`${config.paths.seedPath}seed
|
|
61
|
+
templateDecoderFilePath: Types.toPath(`${config.paths.seedPath}seed.decoder`),
|
|
62
|
+
templateDecoderName: Types.toFunction(`seedTemplateDecoder`),
|
|
61
63
|
importPath: Types.toPath(`@${config.project}/seed`),
|
|
62
64
|
randomSeed: config.randomSeed,
|
|
63
65
|
},
|
|
@@ -108,7 +110,7 @@ function getModelMetadata({ model }) {
|
|
|
108
110
|
dataRepositoryVariableName: Types.toVariableName(`data`),
|
|
109
111
|
},
|
|
110
112
|
seed: {
|
|
111
|
-
filePath: Types.toPath(`${config.paths.seedPath}
|
|
113
|
+
filePath: Types.toPath(`${config.paths.seedPath}data/${uncapitalizedPlural}`),
|
|
112
114
|
constantName: Types.toVariableName(`${uncapitalizedPlural}`),
|
|
113
115
|
importPath: Types.toPath(`@${config.project}/seed`),
|
|
114
116
|
excel: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@postxl/generator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"main": "./dist/generator.js",
|
|
5
5
|
"typings": "./dist/generator.d.ts",
|
|
6
6
|
"bin": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"scripts": {
|
|
46
46
|
"build": "tsc -p tsconfig.build.json",
|
|
47
47
|
"prepublish": "tsc -p tsconfig.build.json",
|
|
48
|
-
"dev": "tsc -
|
|
48
|
+
"dev": "tsc -p tsconfig.build.json -w",
|
|
49
49
|
"test": "jest",
|
|
50
50
|
"test:watch": "jest --watch",
|
|
51
51
|
"test:types": "tsc --noEmit",
|