@solidxai/core 0.1.11-beta.2 → 0.1.11-beta.5
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/commands/migrate-removed-fields.command.d.ts +19 -0
- package/dist/commands/migrate-removed-fields.command.d.ts.map +1 -0
- package/dist/commands/migrate-removed-fields.command.js +77 -0
- package/dist/commands/migrate-removed-fields.command.js.map +1 -0
- package/dist/controllers/datasource-introspection.controller.d.ts +38 -1
- package/dist/controllers/datasource-introspection.controller.d.ts.map +1 -1
- package/dist/controllers/datasource-introspection.controller.js +38 -0
- package/dist/controllers/datasource-introspection.controller.js.map +1 -1
- package/dist/dtos/datasource-introspection-mapping.dto.d.ts +5 -0
- package/dist/dtos/datasource-introspection-mapping.dto.d.ts.map +1 -1
- package/dist/dtos/datasource-introspection-mapping.dto.js +24 -2
- package/dist/dtos/datasource-introspection-mapping.dto.js.map +1 -1
- package/dist/entities/{legacy-common-with-id.entity.d.ts → legacy-common-with-generated-id.entity.d.ts} +2 -2
- package/dist/entities/legacy-common-with-generated-id.entity.d.ts.map +1 -0
- package/dist/entities/{legacy-common-with-id.entity.js → legacy-common-with-generated-id.entity.js} +4 -4
- package/dist/entities/legacy-common-with-generated-id.entity.js.map +1 -0
- package/dist/entities/{legacy-common.entity.d.ts → legacy-common.entity-with-existing-id.entity.d.ts} +1 -1
- package/dist/entities/legacy-common.entity-with-existing-id.entity.d.ts.map +1 -0
- package/dist/entities/{legacy-common.entity.js → legacy-common.entity-with-existing-id.entity.js} +1 -1
- package/dist/entities/legacy-common.entity-with-existing-id.entity.js.map +1 -0
- package/dist/helpers/command.service.d.ts +2 -0
- package/dist/helpers/command.service.d.ts.map +1 -1
- package/dist/helpers/command.service.js +6 -2
- package/dist/helpers/command.service.js.map +1 -1
- package/dist/helpers/model-metadata-helper.service.js +3 -3
- package/dist/helpers/model-metadata-helper.service.js.map +1 -1
- package/dist/helpers/nodemon-heartbeat.d.ts +5 -0
- package/dist/helpers/nodemon-heartbeat.d.ts.map +1 -0
- package/dist/helpers/nodemon-heartbeat.js +67 -0
- package/dist/helpers/nodemon-heartbeat.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/interfaces/datasource-introspection-provider.interface.d.ts +1 -0
- package/dist/interfaces/datasource-introspection-provider.interface.d.ts.map +1 -1
- package/dist/interfaces/datasource-introspection-provider.interface.js.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts +7 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +101 -1
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +121 -34
- package/dist/services/api-key.service.d.ts.map +1 -1
- package/dist/services/api-key.service.js.map +1 -1
- package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.d.ts +4 -0
- package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.d.ts.map +1 -1
- package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.js +18 -1
- package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.js.map +1 -1
- package/dist/services/crud.service.d.ts +1 -0
- package/dist/services/crud.service.d.ts.map +1 -1
- package/dist/services/crud.service.js +27 -0
- package/dist/services/crud.service.js.map +1 -1
- package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.js +12 -0
- package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.js.map +1 -1
- package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.js +2 -0
- package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.js.map +1 -1
- package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.js +28 -15
- package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.js.map +1 -1
- package/dist/services/datasource-introspection.service.d.ts +53 -2
- package/dist/services/datasource-introspection.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection.service.js +559 -174
- package/dist/services/datasource-introspection.service.js.map +1 -1
- package/dist/services/import-transaction.service.d.ts.map +1 -1
- package/dist/services/import-transaction.service.js +3 -0
- package/dist/services/import-transaction.service.js.map +1 -1
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +10 -10
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
- package/dist/services/model-metadata.service.d.ts +1 -0
- package/dist/services/model-metadata.service.d.ts.map +1 -1
- package/dist/services/model-metadata.service.js +28 -0
- package/dist/services/model-metadata.service.js.map +1 -1
- package/dist/services/module-metadata.service.d.ts.map +1 -1
- package/dist/services/module-metadata.service.js +27 -0
- package/dist/services/module-metadata.service.js.map +1 -1
- package/dist/services/removed-field-migration.service.d.ts +30 -0
- package/dist/services/removed-field-migration.service.d.ts.map +1 -0
- package/dist/services/removed-field-migration.service.js +319 -0
- package/dist/services/removed-field-migration.service.js.map +1 -0
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +4 -0
- package/dist/solid-core.module.js.map +1 -1
- package/dist/subscribers/audit.subscriber.d.ts +4 -1
- package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
- package/dist/subscribers/audit.subscriber.js +54 -6
- package/dist/subscribers/audit.subscriber.js.map +1 -1
- package/dist-tests/api/authenticate.spec.js +119 -0
- package/dist-tests/api/authenticate.spec.js.map +1 -0
- package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +97 -0
- package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +1 -0
- package/dist-tests/api/ping.spec.js +21 -0
- package/dist-tests/api/ping.spec.js.map +1 -0
- package/dist-tests/helpers/auth.js +41 -0
- package/dist-tests/helpers/auth.js.map +1 -0
- package/dist-tests/helpers/env.js +11 -0
- package/dist-tests/helpers/env.js.map +1 -0
- package/docs/agent-hub-grooming.md +301 -0
- package/docs/dashboards/AGENTIC_DASHBOARD_IMPLEMENTATION_PLAN.md +438 -0
- package/docs/dashboards/dashboard-curl-smoke-tests.txt +146 -0
- package/docs/dashboards/delete-legacy-dashboard-metadata.sql +172 -0
- package/docs/datasource-introspection-ddl-analysis.md +326 -0
- package/docs/datasource-introspection-implementation-plan.md +306 -0
- package/docs/grouping-enhancements.md +89 -0
- package/docs/java-spring/README.md +3 -0
- package/docs/java-spring/solid-core-module-deep-dive-report.md +1317 -0
- package/docs/module-package-import-handoff.md +691 -0
- package/docs/seed-changes.md +65 -0
- package/docs/test-data-workflow.md +200 -0
- package/docs/type-declaration-import-issue.md +24 -0
- package/package.json +1 -1
- package/src/commands/migrate-removed-fields.command.ts +81 -0
- package/src/controllers/datasource-introspection.controller.ts +27 -1
- package/src/dtos/datasource-introspection-mapping.dto.ts +15 -0
- package/src/entities/{legacy-common-with-id.entity.ts → legacy-common-with-generated-id.entity.ts} +1 -1
- package/src/helpers/command.service.ts +10 -2
- package/src/helpers/model-metadata-helper.service.ts +1 -1
- package/src/helpers/nodemon-heartbeat.ts +59 -0
- package/src/index.ts +3 -2
- package/src/interfaces/datasource-introspection-provider.interface.ts +1 -0
- package/src/seeders/module-metadata-seeder.service.ts +118 -0
- package/src/seeders/seed-data/solid-core-metadata.json +121 -34
- package/src/services/api-key.service.ts +36 -36
- package/src/services/computed-fields/entity/concat-entity-computed-field-provider.service.ts +15 -1
- package/src/services/crud.service.ts +36 -11
- package/src/services/datasource-introspection/mssql-datasource-introspection-provider.service.ts +12 -0
- package/src/services/datasource-introspection/mysql-datasource-introspection-provider.service.ts +2 -0
- package/src/services/datasource-introspection/postgres-datasource-introspection-provider.service.ts +28 -15
- package/src/services/datasource-introspection.service.ts +653 -150
- package/src/services/import-transaction.service.ts +5 -0
- package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +2 -2
- package/src/services/model-metadata.service.ts +45 -1
- package/src/services/module-metadata.service.ts +35 -0
- package/src/services/removed-field-migration.service.ts +334 -0
- package/src/solid-core.module.ts +4 -0
- package/src/subscribers/audit.subscriber.ts +70 -5
- package/.claude/settings.local.json +0 -15
- package/CLAUDE.md +0 -26
- package/dist/entities/legacy-common-with-id.entity.d.ts.map +0 -1
- package/dist/entities/legacy-common-with-id.entity.js.map +0 -1
- package/dist/entities/legacy-common.entity.d.ts.map +0 -1
- package/dist/entities/legacy-common.entity.js.map +0 -1
- package/src/services/1.js +0 -6
- /package/src/entities/{legacy-common.entity.ts → legacy-common.entity-with-existing-id.entity.ts} +0 -0
|
@@ -50,19 +50,101 @@ const crypto_1 = require("crypto");
|
|
|
50
50
|
const fs = __importStar(require("fs/promises"));
|
|
51
51
|
const path = __importStar(require("path"));
|
|
52
52
|
const lodash_1 = require("lodash");
|
|
53
|
+
const ts_morph_1 = require("ts-morph");
|
|
53
54
|
const legacy_table_type_enum_1 = require("../enums/legacy-table-type.enum");
|
|
55
|
+
const string_helper_1 = require("../helpers/string.helper");
|
|
54
56
|
const module_metadata_helper_service_1 = require("../helpers/module-metadata-helper.service");
|
|
55
57
|
const model_metadata_helper_service_1 = require("../helpers/model-metadata-helper.service");
|
|
56
58
|
const model_metadata_repository_1 = require("../repository/model-metadata.repository");
|
|
57
59
|
const module_metadata_repository_1 = require("../repository/module-metadata.repository");
|
|
58
60
|
const datasource_management_service_1 = require("./datasource-management.service");
|
|
61
|
+
const command_service_1 = require("../helpers/command.service");
|
|
59
62
|
const field_metadata_service_1 = require("./field-metadata.service");
|
|
60
63
|
const model_metadata_service_1 = require("./model-metadata.service");
|
|
64
|
+
const module_metadata_service_1 = require("./module-metadata.service");
|
|
61
65
|
const mssql_datasource_introspection_provider_service_1 = require("./datasource-introspection/mssql-datasource-introspection-provider.service");
|
|
62
66
|
const mysql_datasource_introspection_provider_service_1 = require("./datasource-introspection/mysql-datasource-introspection-provider.service");
|
|
63
67
|
const postgres_datasource_introspection_provider_service_1 = require("./datasource-introspection/postgres-datasource-introspection-provider.service");
|
|
68
|
+
const DATASOURCE_TYPE_FAMILY_ALIASES = {
|
|
69
|
+
boolean: ["bit", "bool", "boolean"],
|
|
70
|
+
bigint: ["bigint"],
|
|
71
|
+
int: ["int", "integer", "smallint", "tinyint"],
|
|
72
|
+
numeric: ["numeric"],
|
|
73
|
+
decimal: ["decimal", "money", "smallmoney"],
|
|
74
|
+
float: ["float", "double", "double precision", "real", "float4", "float8"],
|
|
75
|
+
date: ["date"],
|
|
76
|
+
time: ["time", "time without time zone", "time with time zone"],
|
|
77
|
+
datetime: ["datetime", "datetime2", "smalldatetime", "timestamp", "timestamp without time zone"],
|
|
78
|
+
datetimeWithTimezone: ["timestamptz", "timestamp with time zone"],
|
|
79
|
+
json: ["json", "simple-json", "simplejson"],
|
|
80
|
+
jsonb: ["jsonb"],
|
|
81
|
+
uuid: ["uniqueidentifier", "uuid"],
|
|
82
|
+
longText: ["text", "ntext", "longtext", "clob"],
|
|
83
|
+
mediumText: ["mediumtext"],
|
|
84
|
+
shortText: ["varchar", "char", "citext"],
|
|
85
|
+
unicodeShortText: ["nvarchar", "nchar"],
|
|
86
|
+
};
|
|
87
|
+
const ORM_TYPE_BY_DATASOURCE_FAMILY = {
|
|
88
|
+
mssql: {
|
|
89
|
+
boolean: "bit",
|
|
90
|
+
bigint: "bigint",
|
|
91
|
+
int: "int",
|
|
92
|
+
numeric: "numeric",
|
|
93
|
+
decimal: "decimal",
|
|
94
|
+
float: "decimal",
|
|
95
|
+
date: "date",
|
|
96
|
+
time: "time",
|
|
97
|
+
datetime: "datetime",
|
|
98
|
+
datetimeWithTimezone: "datetime",
|
|
99
|
+
json: "simple-json",
|
|
100
|
+
jsonb: "simple-json",
|
|
101
|
+
uuid: "uniqueidentifier",
|
|
102
|
+
longText: "text",
|
|
103
|
+
mediumText: "text",
|
|
104
|
+
shortText: "varchar",
|
|
105
|
+
unicodeShortText: "nvarchar",
|
|
106
|
+
},
|
|
107
|
+
mysql: {
|
|
108
|
+
boolean: "boolean",
|
|
109
|
+
bigint: "int",
|
|
110
|
+
int: "int",
|
|
111
|
+
numeric: "decimal",
|
|
112
|
+
decimal: "decimal",
|
|
113
|
+
float: "double",
|
|
114
|
+
date: "date",
|
|
115
|
+
time: "time",
|
|
116
|
+
datetime: "datetime",
|
|
117
|
+
datetimeWithTimezone: "datetime",
|
|
118
|
+
json: "json",
|
|
119
|
+
jsonb: "json",
|
|
120
|
+
uuid: "varchar",
|
|
121
|
+
longText: "longtext",
|
|
122
|
+
mediumText: "mediumtext",
|
|
123
|
+
shortText: "varchar",
|
|
124
|
+
unicodeShortText: "varchar",
|
|
125
|
+
},
|
|
126
|
+
postgres: {
|
|
127
|
+
boolean: "boolean",
|
|
128
|
+
bigint: "bigint",
|
|
129
|
+
int: "integer",
|
|
130
|
+
numeric: "decimal",
|
|
131
|
+
decimal: "decimal",
|
|
132
|
+
float: "decimal",
|
|
133
|
+
date: "date",
|
|
134
|
+
time: "time",
|
|
135
|
+
datetime: "timestamp",
|
|
136
|
+
datetimeWithTimezone: "timestamptz",
|
|
137
|
+
json: "json",
|
|
138
|
+
jsonb: "jsonb",
|
|
139
|
+
uuid: "uuid",
|
|
140
|
+
longText: "text",
|
|
141
|
+
mediumText: "text",
|
|
142
|
+
shortText: "varchar",
|
|
143
|
+
unicodeShortText: "varchar",
|
|
144
|
+
},
|
|
145
|
+
};
|
|
64
146
|
let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
65
|
-
constructor(moduleRef, datasourceManagementService, moduleMetadataRepository, modelMetadataRepository, moduleMetadataHelperService, modelMetadataHelperService, fieldMetadataService, modelMetadataService, mssqlProvider, mysqlProvider, postgresProvider) {
|
|
147
|
+
constructor(moduleRef, datasourceManagementService, moduleMetadataRepository, modelMetadataRepository, moduleMetadataHelperService, modelMetadataHelperService, fieldMetadataService, modelMetadataService, moduleMetadataService, commandService, mssqlProvider, mysqlProvider, postgresProvider) {
|
|
66
148
|
this.moduleRef = moduleRef;
|
|
67
149
|
this.datasourceManagementService = datasourceManagementService;
|
|
68
150
|
this.moduleMetadataRepository = moduleMetadataRepository;
|
|
@@ -71,6 +153,8 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
71
153
|
this.modelMetadataHelperService = modelMetadataHelperService;
|
|
72
154
|
this.fieldMetadataService = fieldMetadataService;
|
|
73
155
|
this.modelMetadataService = modelMetadataService;
|
|
156
|
+
this.moduleMetadataService = moduleMetadataService;
|
|
157
|
+
this.commandService = commandService;
|
|
74
158
|
this.mssqlProvider = mssqlProvider;
|
|
75
159
|
this.mysqlProvider = mysqlProvider;
|
|
76
160
|
this.postgresProvider = postgresProvider;
|
|
@@ -189,24 +273,98 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
189
273
|
const persistedModel = plan.operation === "update" && mappingDto.modelId
|
|
190
274
|
? await this.modelMetadataService.update(mappingDto.modelId, plan.modelPayload)
|
|
191
275
|
: await this.modelMetadataService.create(plan.modelPayload);
|
|
276
|
+
const persistedModelId = mappingDto.modelId ?? persistedModel?.id ?? null;
|
|
277
|
+
return {
|
|
278
|
+
data: {
|
|
279
|
+
modelId: persistedModelId,
|
|
280
|
+
operation: plan.operation,
|
|
281
|
+
metadataJson: plan.metadataJson,
|
|
282
|
+
summary: plan.summary,
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
async createMigrationArtifacts(moduleId, mappingDto) {
|
|
287
|
+
const plan = await this.buildMappingPreview(moduleId, mappingDto);
|
|
288
|
+
const module = await this.loadModule(moduleId);
|
|
289
|
+
const datasource = this.requireDatasource(mappingDto.dataSource);
|
|
290
|
+
if (!mappingDto.modelId) {
|
|
291
|
+
throw new common_1.BadRequestException("Persist the model mapping first so the generated entity can be registered before creating migration artifacts.");
|
|
292
|
+
}
|
|
192
293
|
if (plan.migration.willGenerate && plan.migration.content && plan.migration.filePath) {
|
|
193
294
|
await fs.mkdir(path.dirname(plan.migration.filePath), { recursive: true });
|
|
194
295
|
await fs.writeFile(plan.migration.filePath, plan.migration.content, "utf8");
|
|
195
296
|
}
|
|
196
|
-
const
|
|
297
|
+
const datasourceFile = await this.syncTypeormDatasourceFile(module.name, datasource, plan.modelPayload.singularName);
|
|
197
298
|
return {
|
|
198
299
|
data: {
|
|
199
|
-
modelId:
|
|
300
|
+
modelId: mappingDto.modelId,
|
|
200
301
|
operation: plan.operation,
|
|
201
|
-
metadataJson: plan.metadataJson,
|
|
202
302
|
migration: {
|
|
203
303
|
...plan.migration,
|
|
204
304
|
written: Boolean(plan.migration.willGenerate && plan.migration.filePath),
|
|
205
305
|
},
|
|
306
|
+
datasourceFile,
|
|
206
307
|
summary: plan.summary,
|
|
207
308
|
},
|
|
208
309
|
};
|
|
209
310
|
}
|
|
311
|
+
async generateCode(moduleId) {
|
|
312
|
+
await this.loadModule(moduleId);
|
|
313
|
+
const output = await this.moduleMetadataService.generateCodeViaCtl(moduleId);
|
|
314
|
+
return {
|
|
315
|
+
data: {
|
|
316
|
+
moduleId,
|
|
317
|
+
output,
|
|
318
|
+
message: "Module code generation completed.",
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
async runMigration(moduleId, datasourceName) {
|
|
323
|
+
const module = await this.loadModule(moduleId);
|
|
324
|
+
const datasource = this.requireDatasource(datasourceName);
|
|
325
|
+
const cwd = path.join(process.cwd(), "..");
|
|
326
|
+
const outputs = [];
|
|
327
|
+
const migrationOutput = await this.commandService.executeCommandWithArgs({
|
|
328
|
+
command: "npx",
|
|
329
|
+
args: ["-y", "@solidxai/solidctl@latest", "migration", "-d", datasource.name, "run"],
|
|
330
|
+
cwd,
|
|
331
|
+
});
|
|
332
|
+
outputs.push([
|
|
333
|
+
`npx @solidxai/solidctl@latest migration -d ${datasource.name} run`,
|
|
334
|
+
migrationOutput,
|
|
335
|
+
].filter(Boolean).join("\n"));
|
|
336
|
+
const buildOutput = await this.commandService.executeCommandWithArgs({
|
|
337
|
+
command: "npx",
|
|
338
|
+
args: ["-y", "@solidxai/solidctl@latest", "build"],
|
|
339
|
+
cwd,
|
|
340
|
+
});
|
|
341
|
+
outputs.push([
|
|
342
|
+
"npx @solidxai/solidctl@latest build",
|
|
343
|
+
buildOutput,
|
|
344
|
+
].filter(Boolean).join("\n"));
|
|
345
|
+
const seedOutput = await this.commandService.executeCommandWithArgs({
|
|
346
|
+
command: "npx",
|
|
347
|
+
args: ["-y", "@solidxai/solidctl@latest", "seed", "--modules-to-seed", module.name],
|
|
348
|
+
cwd,
|
|
349
|
+
});
|
|
350
|
+
outputs.push([
|
|
351
|
+
`npx @solidxai/solidctl@latest seed --modules-to-seed ${module.name}`,
|
|
352
|
+
seedOutput,
|
|
353
|
+
].filter(Boolean).join("\n"));
|
|
354
|
+
return {
|
|
355
|
+
data: {
|
|
356
|
+
moduleId,
|
|
357
|
+
datasource: datasource.name,
|
|
358
|
+
output: outputs.join("\n\n"),
|
|
359
|
+
command: [
|
|
360
|
+
`npx @solidxai/solidctl@latest migration -d ${datasource.name} run`,
|
|
361
|
+
"npx @solidxai/solidctl@latest build",
|
|
362
|
+
`npx @solidxai/solidctl@latest seed --modules-to-seed ${module.name}`,
|
|
363
|
+
].join("\n"),
|
|
364
|
+
message: `Migration, build, and seed completed successfully for datasource "${datasource.name}".`,
|
|
365
|
+
},
|
|
366
|
+
};
|
|
367
|
+
}
|
|
210
368
|
async buildMappingPreview(moduleId, mappingDto) {
|
|
211
369
|
const module = await this.loadModule(moduleId);
|
|
212
370
|
const datasource = this.requireDatasource(mappingDto.dataSource);
|
|
@@ -252,6 +410,13 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
252
410
|
mappedModelId: mappingDto.modelId ?? null,
|
|
253
411
|
metadataJson,
|
|
254
412
|
migration,
|
|
413
|
+
datasourceFile: {
|
|
414
|
+
filePath: path.resolve(process.cwd(), "src", `typeorm-${datasource.name}-datasource.ts`),
|
|
415
|
+
created: false,
|
|
416
|
+
updated: false,
|
|
417
|
+
entityRegistered: false,
|
|
418
|
+
migrationGlobRegistered: false,
|
|
419
|
+
},
|
|
255
420
|
summary: {
|
|
256
421
|
includedColumnNames,
|
|
257
422
|
removedColumnNames,
|
|
@@ -455,6 +620,12 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
455
620
|
}
|
|
456
621
|
return rest;
|
|
457
622
|
}
|
|
623
|
+
sanitizeConfiguredFieldPayload(fieldConfig) {
|
|
624
|
+
if (!fieldConfig || typeof fieldConfig !== "object" || Array.isArray(fieldConfig)) {
|
|
625
|
+
return {};
|
|
626
|
+
}
|
|
627
|
+
return this.sanitizeExistingFieldPayload(fieldConfig);
|
|
628
|
+
}
|
|
458
629
|
normalizeReviewedMetadataModel(reviewedModel, mappingDto) {
|
|
459
630
|
if (!reviewedModel) {
|
|
460
631
|
return null;
|
|
@@ -577,32 +748,37 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
577
748
|
}
|
|
578
749
|
buildFieldPayload(column, mappingDto, existingField) {
|
|
579
750
|
const preserved = existingField ? this.sanitizeExistingFieldPayload(existingField) : {};
|
|
751
|
+
const configured = this.sanitizeConfiguredFieldPayload(column.fieldConfig);
|
|
752
|
+
const merged = {
|
|
753
|
+
...preserved,
|
|
754
|
+
...configured,
|
|
755
|
+
};
|
|
580
756
|
const maxLength = column.characterMaximumLength && ["shortText", "longText"].includes(column.solidFieldType)
|
|
581
757
|
? column.characterMaximumLength
|
|
582
|
-
:
|
|
758
|
+
: merged.max;
|
|
583
759
|
return {
|
|
584
|
-
...
|
|
760
|
+
...merged,
|
|
585
761
|
...(column.fieldId ? { id: column.fieldId } : {}),
|
|
586
762
|
name: column.fieldName,
|
|
587
|
-
displayName:
|
|
588
|
-
description:
|
|
763
|
+
displayName: column.displayName?.trim() || merged.displayName || (0, lodash_1.startCase)(column.fieldName),
|
|
764
|
+
description: merged.description ?? "",
|
|
589
765
|
type: column.solidFieldType,
|
|
590
766
|
ormType: column.ormType,
|
|
591
|
-
length:
|
|
592
|
-
defaultValue:
|
|
593
|
-
required:
|
|
594
|
-
unique:
|
|
595
|
-
encrypt:
|
|
596
|
-
index:
|
|
767
|
+
length: merged.length ?? column.characterMaximumLength ?? undefined,
|
|
768
|
+
defaultValue: merged.defaultValue ?? column.defaultValue ?? undefined,
|
|
769
|
+
required: merged.required ?? (!column.nullable || column.isPrimaryKey),
|
|
770
|
+
unique: merged.unique ?? false,
|
|
771
|
+
encrypt: merged.encrypt ?? false,
|
|
772
|
+
index: merged.index ?? false,
|
|
597
773
|
max: maxLength ?? undefined,
|
|
598
|
-
min:
|
|
599
|
-
private:
|
|
600
|
-
isSystem:
|
|
774
|
+
min: merged.min ?? undefined,
|
|
775
|
+
private: merged.private ?? false,
|
|
776
|
+
isSystem: merged.isSystem ?? false,
|
|
601
777
|
isMarkedForRemoval: false,
|
|
602
778
|
columnName: column.columnName,
|
|
603
779
|
isUserKey: mappingDto.userKeyField === column.fieldName,
|
|
604
|
-
enableAuditTracking:
|
|
605
|
-
isMultiSelect:
|
|
780
|
+
enableAuditTracking: merged.enableAuditTracking ?? mappingDto.enableAuditTracking,
|
|
781
|
+
isMultiSelect: merged.isMultiSelect ?? false,
|
|
606
782
|
isPrimaryKey: column.isPrimaryKey,
|
|
607
783
|
};
|
|
608
784
|
}
|
|
@@ -696,23 +872,19 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
696
872
|
const generatedIdUniqueName = this.buildMigrationObjectName("UQ", tableNameLiteral, "ss_id");
|
|
697
873
|
const upColumnStatements = missingSystemColumns.map((definition) => {
|
|
698
874
|
const columnLiteral = this.renderTableColumnLiteral(definition);
|
|
699
|
-
return
|
|
700
|
-
`
|
|
701
|
-
` await queryRunner.addColumn(tableName, new TableColumn(${columnLiteral}));`,
|
|
702
|
-
" }",
|
|
703
|
-
].join("\n");
|
|
875
|
+
return ` if (!(await queryRunner.hasColumn(tableName, ${JSON.stringify(definition.columnName)})))\n`
|
|
876
|
+
+ ` await queryRunner.addColumn(tableName, new TableColumn(${columnLiteral}));`;
|
|
704
877
|
}).join("\n\n");
|
|
705
878
|
const postUpStatements = [];
|
|
706
879
|
if (missingSystemColumns.some((definition) => definition.columnName === "ss_deleted_at")) {
|
|
707
880
|
postUpStatements.push([
|
|
708
881
|
" {",
|
|
709
882
|
" const table = await queryRunner.getTable(tableName);",
|
|
710
|
-
` if (table && !table.indices.some((index) => index.name === ${JSON.stringify(deletedAtIndexName)}))
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
" }",
|
|
883
|
+
` if (table && !table.indices.some((index) => index.name === ${JSON.stringify(deletedAtIndexName)}))`,
|
|
884
|
+
` await queryRunner.createIndex(tableName, new TableIndex(${this.renderTableIndexLiteral({
|
|
885
|
+
name: deletedAtIndexName,
|
|
886
|
+
columnNames: ["ss_deleted_at"],
|
|
887
|
+
})}));`,
|
|
716
888
|
" }",
|
|
717
889
|
].join("\n"));
|
|
718
890
|
}
|
|
@@ -720,13 +892,12 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
720
892
|
postUpStatements.push([
|
|
721
893
|
" {",
|
|
722
894
|
" const table = await queryRunner.getTable(tableName);",
|
|
723
|
-
` if (table && !table.indices.some((index) => index.name === ${JSON.stringify(generatedIdUniqueName)}))
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
" }",
|
|
895
|
+
` if (table && !table.indices.some((index) => index.name === ${JSON.stringify(generatedIdUniqueName)}))`,
|
|
896
|
+
` await queryRunner.createIndex(tableName, new TableIndex(${this.renderTableIndexLiteral({
|
|
897
|
+
name: generatedIdUniqueName,
|
|
898
|
+
columnNames: ["ss_id"],
|
|
899
|
+
isUnique: true,
|
|
900
|
+
})}));`,
|
|
730
901
|
" }",
|
|
731
902
|
].join("\n"));
|
|
732
903
|
}
|
|
@@ -736,9 +907,8 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
736
907
|
" {",
|
|
737
908
|
" const table = await queryRunner.getTable(tableName);",
|
|
738
909
|
` const index = table?.indices.find((entry) => entry.name === ${JSON.stringify(generatedIdUniqueName)});`,
|
|
739
|
-
" if (index)
|
|
910
|
+
" if (index)",
|
|
740
911
|
" await queryRunner.dropIndex(tableName, index);",
|
|
741
|
-
" }",
|
|
742
912
|
" }",
|
|
743
913
|
].join("\n"));
|
|
744
914
|
}
|
|
@@ -747,20 +917,16 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
747
917
|
" {",
|
|
748
918
|
" const table = await queryRunner.getTable(tableName);",
|
|
749
919
|
` const index = table?.indices.find((entry) => entry.name === ${JSON.stringify(deletedAtIndexName)});`,
|
|
750
|
-
" if (index)
|
|
920
|
+
" if (index)",
|
|
751
921
|
" await queryRunner.dropIndex(tableName, index);",
|
|
752
|
-
" }",
|
|
753
922
|
" }",
|
|
754
923
|
].join("\n"));
|
|
755
924
|
}
|
|
756
925
|
downStatements.push(...missingSystemColumns
|
|
757
926
|
.slice()
|
|
758
927
|
.reverse()
|
|
759
|
-
.map((definition) =>
|
|
760
|
-
`
|
|
761
|
-
` await queryRunner.dropColumn(tableName, ${JSON.stringify(definition.columnName)});`,
|
|
762
|
-
" }",
|
|
763
|
-
].join("\n")));
|
|
928
|
+
.map((definition) => ` if (await queryRunner.hasColumn(tableName, ${JSON.stringify(definition.columnName)}))\n`
|
|
929
|
+
+ ` await queryRunner.dropColumn(tableName, ${JSON.stringify(definition.columnName)});`));
|
|
764
930
|
return [
|
|
765
931
|
"import { MigrationInterface, QueryRunner, TableColumn, TableIndex } from \"typeorm\";",
|
|
766
932
|
"",
|
|
@@ -783,27 +949,36 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
783
949
|
}
|
|
784
950
|
renderTableColumnLiteral(definition) {
|
|
785
951
|
const properties = [
|
|
786
|
-
`
|
|
787
|
-
`
|
|
952
|
+
`name: ${JSON.stringify(definition.columnName)}`,
|
|
953
|
+
`type: ${JSON.stringify(definition.type)}`,
|
|
788
954
|
];
|
|
789
955
|
if (definition.length) {
|
|
790
|
-
properties.push(`
|
|
956
|
+
properties.push(`length: ${JSON.stringify(definition.length)}`);
|
|
791
957
|
}
|
|
792
958
|
if (typeof definition.isNullable === "boolean") {
|
|
793
|
-
properties.push(`
|
|
959
|
+
properties.push(`isNullable: ${definition.isNullable}`);
|
|
794
960
|
}
|
|
795
961
|
if (definition.defaultExpression) {
|
|
796
|
-
properties.push(`
|
|
962
|
+
properties.push(`default: ${JSON.stringify(definition.defaultExpression)}`);
|
|
797
963
|
}
|
|
798
964
|
if (definition.isGenerated) {
|
|
799
|
-
properties.push("
|
|
800
|
-
properties.push("
|
|
965
|
+
properties.push("isGenerated: true");
|
|
966
|
+
properties.push("generationStrategy: \"increment\"");
|
|
801
967
|
}
|
|
802
968
|
if (definition.isUnique) {
|
|
803
|
-
properties.push("
|
|
969
|
+
properties.push("isUnique: true");
|
|
804
970
|
}
|
|
805
|
-
properties.
|
|
806
|
-
|
|
971
|
+
return `{ ${properties.join(", ")}, }`;
|
|
972
|
+
}
|
|
973
|
+
renderTableIndexLiteral(definition) {
|
|
974
|
+
const properties = [
|
|
975
|
+
`name: ${JSON.stringify(definition.name)}`,
|
|
976
|
+
`columnNames: ${JSON.stringify(definition.columnNames)}`,
|
|
977
|
+
];
|
|
978
|
+
if (definition.isUnique) {
|
|
979
|
+
properties.push("isUnique: true");
|
|
980
|
+
}
|
|
981
|
+
return `{ ${properties.join(", ")}, }`;
|
|
807
982
|
}
|
|
808
983
|
buildMigrationObjectName(prefix, tableName, columnName) {
|
|
809
984
|
const hash = (0, crypto_1.createHash)("md5").update(`${tableName}:${columnName}:${prefix}`).digest("hex").slice(0, 24);
|
|
@@ -832,12 +1007,14 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
832
1007
|
async buildTableInventory(datasource) {
|
|
833
1008
|
const provider = this.getProvider(datasource.type);
|
|
834
1009
|
const runtimeDataSource = this.resolveDataSourceByName(datasource.name);
|
|
835
|
-
const [
|
|
1010
|
+
const [rawTables, rawColumns, models] = await Promise.all([
|
|
836
1011
|
provider.listTables(runtimeDataSource),
|
|
837
1012
|
provider.listColumns(runtimeDataSource),
|
|
838
1013
|
this.modelMetadataRepository.find({ relations: { module: true } }),
|
|
839
1014
|
]);
|
|
840
|
-
const
|
|
1015
|
+
const tables = rawTables.filter((table) => !this.isSolidxInternalTableName(table.tableName));
|
|
1016
|
+
const columns = rawColumns.filter((column) => !this.isSolidxInternalTableName(column.tableName));
|
|
1017
|
+
const modelLookup = this.buildModelLookup(models);
|
|
841
1018
|
const columnsByKey = this.groupColumnsByTable(columns);
|
|
842
1019
|
const tableKeys = new Set();
|
|
843
1020
|
for (const table of tables) {
|
|
@@ -857,7 +1034,7 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
857
1034
|
}
|
|
858
1035
|
const suggestedLegacyTableType = this.detectLegacyTableType(datasource.type, tableColumns);
|
|
859
1036
|
const suggestedIdColumn = this.getSuggestedIdColumn(datasource.type, tableColumns, suggestedLegacyTableType);
|
|
860
|
-
const mappedModel =
|
|
1037
|
+
const mappedModel = this.resolveMappedModel(modelLookup, datasource.name, tableName);
|
|
861
1038
|
const physicalColumns = tableColumns.filter((column) => !this.isHandledBySuperclass(column.columnName, suggestedLegacyTableType));
|
|
862
1039
|
return {
|
|
863
1040
|
schema,
|
|
@@ -892,16 +1069,37 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
892
1069
|
columnsByKey,
|
|
893
1070
|
};
|
|
894
1071
|
}
|
|
895
|
-
|
|
896
|
-
const
|
|
1072
|
+
buildModelLookup(models) {
|
|
1073
|
+
const byDatasourceAndTable = new Map();
|
|
1074
|
+
const byTableName = new Map();
|
|
897
1075
|
for (const model of models) {
|
|
898
1076
|
if (!model.tableName) {
|
|
899
1077
|
continue;
|
|
900
1078
|
}
|
|
901
|
-
|
|
902
|
-
|
|
1079
|
+
byDatasourceAndTable.set(this.toModelMapKey(model.dataSource, model.tableName), model);
|
|
1080
|
+
const normalizedTableName = model.tableName.trim().toLowerCase();
|
|
1081
|
+
const bucket = byTableName.get(normalizedTableName);
|
|
1082
|
+
if (bucket) {
|
|
1083
|
+
bucket.push(model);
|
|
1084
|
+
continue;
|
|
1085
|
+
}
|
|
1086
|
+
byTableName.set(normalizedTableName, [model]);
|
|
903
1087
|
}
|
|
904
|
-
return
|
|
1088
|
+
return {
|
|
1089
|
+
byDatasourceAndTable,
|
|
1090
|
+
byTableName,
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
resolveMappedModel(lookup, datasourceName, tableName) {
|
|
1094
|
+
const exactMatch = lookup.byDatasourceAndTable.get(this.toModelMapKey(datasourceName, tableName));
|
|
1095
|
+
if (exactMatch) {
|
|
1096
|
+
return exactMatch;
|
|
1097
|
+
}
|
|
1098
|
+
const fallbackMatches = lookup.byTableName.get(tableName.trim().toLowerCase()) ?? [];
|
|
1099
|
+
if (fallbackMatches.length === 1) {
|
|
1100
|
+
return fallbackMatches[0];
|
|
1101
|
+
}
|
|
1102
|
+
return fallbackMatches[0] ?? null;
|
|
905
1103
|
}
|
|
906
1104
|
groupColumnsByTable(columns) {
|
|
907
1105
|
const grouped = new Map();
|
|
@@ -916,6 +1114,9 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
916
1114
|
}
|
|
917
1115
|
return grouped;
|
|
918
1116
|
}
|
|
1117
|
+
isSolidxInternalTableName(tableName) {
|
|
1118
|
+
return typeof tableName === "string" && tableName.toLowerCase().startsWith("ss_");
|
|
1119
|
+
}
|
|
919
1120
|
detectLegacyTableType(datasourceType, columns) {
|
|
920
1121
|
const normalizedDatasourceType = datasourceType.toLowerCase();
|
|
921
1122
|
const hasExistingGeneratedId = columns.some((column) => column.isAutoGenerated);
|
|
@@ -983,12 +1184,16 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
983
1184
|
}
|
|
984
1185
|
toDetailColumnRecord(column, legacyTableType, datasourceType) {
|
|
985
1186
|
const superclassFieldName = this.getSuperclassFieldName(column.columnName, legacyTableType);
|
|
1187
|
+
const normalizedColumnName = column.columnName.toLowerCase();
|
|
986
1188
|
const fieldName = legacyTableType === legacy_table_type_enum_1.LegacyTableType.EXISTING_ID && column.isAutoGenerated
|
|
987
1189
|
? "id"
|
|
988
|
-
:
|
|
1190
|
+
: legacyTableType === legacy_table_type_enum_1.LegacyTableType.GENERATED_ID && normalizedColumnName === "id"
|
|
1191
|
+
? "legacyId"
|
|
1192
|
+
: this.toFieldName(column.columnName);
|
|
989
1193
|
return {
|
|
990
1194
|
columnName: column.columnName,
|
|
991
1195
|
fieldName,
|
|
1196
|
+
displayName: (0, lodash_1.startCase)(fieldName),
|
|
992
1197
|
dataType: column.dataType,
|
|
993
1198
|
solidFieldType: this.toSolidFieldType(column),
|
|
994
1199
|
ormType: this.toOrmFieldType(column, datasourceType),
|
|
@@ -999,6 +1204,7 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
999
1204
|
numericScale: column.numericScale,
|
|
1000
1205
|
ordinalPosition: column.ordinalPosition,
|
|
1001
1206
|
isAutoGenerated: column.isAutoGenerated,
|
|
1207
|
+
isPrimaryKey: column.isPrimaryKey,
|
|
1002
1208
|
handledBySuperclass: Boolean(superclassFieldName),
|
|
1003
1209
|
superclassFieldName,
|
|
1004
1210
|
};
|
|
@@ -1033,127 +1239,70 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
1033
1239
|
}
|
|
1034
1240
|
return "LegacyCommonEntityWithExistingId";
|
|
1035
1241
|
}
|
|
1036
|
-
|
|
1037
|
-
const normalized =
|
|
1038
|
-
const
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
if (["bigint"].includes(normalized)) {
|
|
1043
|
-
return "bigint";
|
|
1044
|
-
}
|
|
1045
|
-
if (["int", "integer", "smallint", "tinyint"].includes(normalized)) {
|
|
1046
|
-
return "int";
|
|
1047
|
-
}
|
|
1048
|
-
if (["numeric", "decimal", "float", "double", "real", "money", "smallmoney"].includes(normalized)) {
|
|
1049
|
-
return "decimal";
|
|
1050
|
-
}
|
|
1051
|
-
if (["date"].includes(normalized)) {
|
|
1052
|
-
return "date";
|
|
1053
|
-
}
|
|
1054
|
-
if (["time"].includes(normalized)) {
|
|
1055
|
-
return "time";
|
|
1056
|
-
}
|
|
1057
|
-
if (["datetime", "datetime2", "timestamp", "timestamptz", "smalldatetime"].includes(normalized) || normalized.includes("date")) {
|
|
1058
|
-
return "datetime";
|
|
1242
|
+
getDatasourceTypeFamily(dataType) {
|
|
1243
|
+
const normalized = dataType.toLowerCase();
|
|
1244
|
+
for (const [family, aliases] of Object.entries(DATASOURCE_TYPE_FAMILY_ALIASES)) {
|
|
1245
|
+
if (aliases.includes(normalized)) {
|
|
1246
|
+
return family;
|
|
1247
|
+
}
|
|
1059
1248
|
}
|
|
1060
|
-
if (
|
|
1249
|
+
if (normalized.includes("json")) {
|
|
1061
1250
|
return "json";
|
|
1062
1251
|
}
|
|
1063
|
-
if (
|
|
1064
|
-
return "
|
|
1065
|
-
}
|
|
1066
|
-
if (["text", "ntext", "mediumtext", "longtext", "clob"].includes(normalized)) {
|
|
1067
|
-
return "longText";
|
|
1068
|
-
}
|
|
1069
|
-
if (["varchar", "nvarchar", "char", "nchar", "citext"].includes(normalized)) {
|
|
1070
|
-
if (charLength !== null && charLength > 255) {
|
|
1071
|
-
return "longText";
|
|
1072
|
-
}
|
|
1073
|
-
return "shortText";
|
|
1252
|
+
if (normalized.includes("date")) {
|
|
1253
|
+
return "datetime";
|
|
1074
1254
|
}
|
|
1075
|
-
return
|
|
1255
|
+
return null;
|
|
1076
1256
|
}
|
|
1077
|
-
|
|
1078
|
-
const
|
|
1079
|
-
const
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
return "
|
|
1083
|
-
|
|
1257
|
+
toSolidFieldType(column) {
|
|
1258
|
+
const family = this.getDatasourceTypeFamily(column.dataType);
|
|
1259
|
+
const charLength = column.characterMaximumLength ?? null;
|
|
1260
|
+
switch (family) {
|
|
1261
|
+
case "boolean":
|
|
1262
|
+
return "boolean";
|
|
1263
|
+
case "bigint":
|
|
1084
1264
|
return "bigint";
|
|
1085
|
-
|
|
1265
|
+
case "int":
|
|
1086
1266
|
return "int";
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1267
|
+
case "numeric":
|
|
1268
|
+
case "decimal":
|
|
1269
|
+
case "float":
|
|
1090
1270
|
return "decimal";
|
|
1091
|
-
|
|
1271
|
+
case "date":
|
|
1092
1272
|
return "date";
|
|
1093
|
-
|
|
1273
|
+
case "time":
|
|
1094
1274
|
return "time";
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
if (["datetime", "smalldatetime", "timestamp"].includes(normalized) || normalized.includes("date"))
|
|
1275
|
+
case "datetime":
|
|
1276
|
+
case "datetimeWithTimezone":
|
|
1098
1277
|
return "datetime";
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
if (["uniqueidentifier", "uuid"].includes(normalized))
|
|
1102
|
-
return "uniqueidentifier";
|
|
1103
|
-
if (["text", "ntext", "mediumtext", "longtext", "clob"].includes(normalized))
|
|
1104
|
-
return "text";
|
|
1105
|
-
if (["nvarchar", "nchar"].includes(normalized))
|
|
1106
|
-
return "nvarchar";
|
|
1107
|
-
return "varchar";
|
|
1108
|
-
}
|
|
1109
|
-
if (normalizedDatasourceType === "mysql") {
|
|
1110
|
-
if (["bigint"].includes(normalized))
|
|
1111
|
-
return "int";
|
|
1112
|
-
if (["int", "integer", "smallint", "tinyint"].includes(normalized))
|
|
1113
|
-
return "int";
|
|
1114
|
-
if (["decimal", "numeric"].includes(normalized))
|
|
1115
|
-
return "decimal";
|
|
1116
|
-
if (["float", "double", "real", "money", "smallmoney"].includes(normalized))
|
|
1117
|
-
return "double";
|
|
1118
|
-
if (["bit", "bool", "boolean"].includes(normalized))
|
|
1119
|
-
return "boolean";
|
|
1120
|
-
if (["date"].includes(normalized))
|
|
1121
|
-
return "date";
|
|
1122
|
-
if (["time"].includes(normalized))
|
|
1123
|
-
return "time";
|
|
1124
|
-
if (["datetime", "timestamp", "smalldatetime"].includes(normalized) || normalized.includes("date"))
|
|
1125
|
-
return "datetime";
|
|
1126
|
-
if (["json", "jsonb", "simple-json", "simplejson"].includes(normalized) || normalized.includes("json"))
|
|
1278
|
+
case "json":
|
|
1279
|
+
case "jsonb":
|
|
1127
1280
|
return "json";
|
|
1128
|
-
|
|
1129
|
-
return "
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
return "json";
|
|
1154
|
-
if (["text", "ntext", "mediumtext", "longtext", "clob"].includes(normalized))
|
|
1155
|
-
return "text";
|
|
1156
|
-
return "varchar";
|
|
1281
|
+
case "uuid":
|
|
1282
|
+
return "uuid";
|
|
1283
|
+
case "mediumText":
|
|
1284
|
+
case "longText":
|
|
1285
|
+
return "longText";
|
|
1286
|
+
case "shortText":
|
|
1287
|
+
case "unicodeShortText":
|
|
1288
|
+
if (charLength !== null && charLength > 255) {
|
|
1289
|
+
return "longText";
|
|
1290
|
+
}
|
|
1291
|
+
return "shortText";
|
|
1292
|
+
default:
|
|
1293
|
+
return "shortText";
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
toOrmFieldType(column, datasourceType) {
|
|
1297
|
+
const family = this.getDatasourceTypeFamily(column.dataType);
|
|
1298
|
+
const normalizedDatasourceType = datasourceType.toLowerCase();
|
|
1299
|
+
const ormType = family
|
|
1300
|
+
? ORM_TYPE_BY_DATASOURCE_FAMILY[normalizedDatasourceType]?.[family]
|
|
1301
|
+
: undefined;
|
|
1302
|
+
if (ormType) {
|
|
1303
|
+
return ormType;
|
|
1304
|
+
}
|
|
1305
|
+
return normalizedDatasourceType === "postgres" ? "varchar" : "varchar";
|
|
1157
1306
|
}
|
|
1158
1307
|
toFieldName(columnName) {
|
|
1159
1308
|
return (0, lodash_1.camelCase)(columnName.replace(/^ss_/, ""));
|
|
@@ -1190,6 +1339,240 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
1190
1339
|
toModelMapKey(datasourceName, tableName) {
|
|
1191
1340
|
return `${datasourceName.toLowerCase()}::${tableName.toLowerCase()}`;
|
|
1192
1341
|
}
|
|
1342
|
+
async syncTypeormDatasourceFile(moduleName, datasource, singularName) {
|
|
1343
|
+
const datasourceFilePath = path.resolve(process.cwd(), "src", `typeorm-${datasource.name}-datasource.ts`);
|
|
1344
|
+
const entityClassName = (0, string_helper_1.classify)(singularName);
|
|
1345
|
+
const entityImportPath = `./${moduleName}/entities/${(0, lodash_1.kebabCase)(singularName)}.entity`;
|
|
1346
|
+
const migrationGlob = `join(__dirname, './${moduleName}/migrations/${datasource.name}/*.{ts,js}')`;
|
|
1347
|
+
const existingContent = await fs.readFile(datasourceFilePath, "utf8").catch(() => null);
|
|
1348
|
+
if (!existingContent) {
|
|
1349
|
+
const rendered = this.renderTypeormDatasourceTemplate({
|
|
1350
|
+
datasource,
|
|
1351
|
+
entityClassName,
|
|
1352
|
+
entityImportPath,
|
|
1353
|
+
migrationGlob,
|
|
1354
|
+
});
|
|
1355
|
+
await fs.writeFile(datasourceFilePath, rendered, "utf8");
|
|
1356
|
+
return {
|
|
1357
|
+
filePath: datasourceFilePath,
|
|
1358
|
+
created: true,
|
|
1359
|
+
updated: false,
|
|
1360
|
+
entityRegistered: true,
|
|
1361
|
+
migrationGlobRegistered: true,
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1364
|
+
const project = new ts_morph_1.Project({
|
|
1365
|
+
skipAddingFilesFromTsConfig: true,
|
|
1366
|
+
manipulationSettings: {
|
|
1367
|
+
quoteKind: ts_morph_1.QuoteKind.Single,
|
|
1368
|
+
indentationText: ts_morph_1.IndentationText.FourSpaces,
|
|
1369
|
+
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
|
|
1370
|
+
},
|
|
1371
|
+
});
|
|
1372
|
+
const sourceFile = project.createSourceFile(datasourceFilePath, existingContent, { overwrite: true });
|
|
1373
|
+
const entityImportDeclaration = sourceFile.getImportDeclarations().find((declaration) => declaration.getModuleSpecifierValue() === entityImportPath);
|
|
1374
|
+
if (entityImportDeclaration) {
|
|
1375
|
+
const alreadyImported = entityImportDeclaration.getNamedImports().some((namedImport) => namedImport.getName() === entityClassName);
|
|
1376
|
+
if (!alreadyImported) {
|
|
1377
|
+
entityImportDeclaration.addNamedImport(entityClassName);
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
else {
|
|
1381
|
+
sourceFile.addImportDeclaration({
|
|
1382
|
+
moduleSpecifier: entityImportPath,
|
|
1383
|
+
namedImports: [entityClassName],
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1386
|
+
const entitiesArray = sourceFile
|
|
1387
|
+
.getVariableDeclarationOrThrow("entities")
|
|
1388
|
+
.getInitializerIfKindOrThrow(ts_morph_1.SyntaxKind.ArrayLiteralExpression);
|
|
1389
|
+
const entityAlreadyRegistered = this.arrayLiteralContainsValue(entitiesArray, entityClassName);
|
|
1390
|
+
if (!entityAlreadyRegistered) {
|
|
1391
|
+
entitiesArray.addElement(entityClassName);
|
|
1392
|
+
}
|
|
1393
|
+
const dataSourceOptions = this.getTypeormDatasourceOptions(sourceFile);
|
|
1394
|
+
const migrationsArray = this.getObjectPropertyArrayLiteral(dataSourceOptions, "migrations");
|
|
1395
|
+
const migrationAlreadyRegistered = this.arrayLiteralContainsValue(migrationsArray, migrationGlob);
|
|
1396
|
+
if (!migrationAlreadyRegistered) {
|
|
1397
|
+
migrationsArray.addElement(migrationGlob);
|
|
1398
|
+
}
|
|
1399
|
+
sourceFile.organizeImports();
|
|
1400
|
+
sourceFile.formatText();
|
|
1401
|
+
const nextContent = sourceFile.getFullText();
|
|
1402
|
+
const updated = nextContent !== existingContent;
|
|
1403
|
+
if (updated) {
|
|
1404
|
+
await fs.writeFile(datasourceFilePath, nextContent, "utf8");
|
|
1405
|
+
}
|
|
1406
|
+
return {
|
|
1407
|
+
filePath: datasourceFilePath,
|
|
1408
|
+
created: false,
|
|
1409
|
+
updated,
|
|
1410
|
+
entityRegistered: true,
|
|
1411
|
+
migrationGlobRegistered: true,
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
renderTypeormDatasourceTemplate(input) {
|
|
1415
|
+
const { datasource, entityClassName, entityImportPath, migrationGlob } = input;
|
|
1416
|
+
const envPrefix = datasource.envPrefix?.toUpperCase?.() || "DEFAULT";
|
|
1417
|
+
const portFallback = datasource.type === "postgres" ? 5432 : datasource.type === "mysql" ? 3306 : 1433;
|
|
1418
|
+
const project = new ts_morph_1.Project({
|
|
1419
|
+
skipAddingFilesFromTsConfig: true,
|
|
1420
|
+
manipulationSettings: {
|
|
1421
|
+
quoteKind: ts_morph_1.QuoteKind.Single,
|
|
1422
|
+
indentationText: ts_morph_1.IndentationText.FourSpaces,
|
|
1423
|
+
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
|
|
1424
|
+
},
|
|
1425
|
+
});
|
|
1426
|
+
const sourceFile = project.createSourceFile("typeorm-template.ts", "", { overwrite: true });
|
|
1427
|
+
sourceFile.addImportDeclaration({ moduleSpecifier: "reflect-metadata" });
|
|
1428
|
+
sourceFile.addImportDeclaration({ moduleSpecifier: "path", namedImports: ["join"] });
|
|
1429
|
+
sourceFile.addImportDeclaration({
|
|
1430
|
+
moduleSpecifier: "dotenv",
|
|
1431
|
+
namedImports: [{ name: "config", alias: "dotenvConfig" }],
|
|
1432
|
+
});
|
|
1433
|
+
sourceFile.addStatements("dotenvConfig({ path: join(__dirname, '../.env') });");
|
|
1434
|
+
sourceFile.addStatements("");
|
|
1435
|
+
sourceFile.addImportDeclaration({ moduleSpecifier: "typeorm", namedImports: ["DataSource"] });
|
|
1436
|
+
sourceFile.addImportDeclaration({
|
|
1437
|
+
moduleSpecifier: "@solidxai/core",
|
|
1438
|
+
namespaceImport: "SolidCoreModuleExports",
|
|
1439
|
+
});
|
|
1440
|
+
sourceFile.addImportDeclaration({
|
|
1441
|
+
moduleSpecifier: "@solidxai/core",
|
|
1442
|
+
namedImports: ["getDynamicModuleNames", "parseBooleanEnv"],
|
|
1443
|
+
});
|
|
1444
|
+
sourceFile.addImportDeclaration({
|
|
1445
|
+
moduleSpecifier: "typeorm",
|
|
1446
|
+
namedImports: ["getMetadataArgsStorage"],
|
|
1447
|
+
});
|
|
1448
|
+
sourceFile.addImportDeclaration({
|
|
1449
|
+
moduleSpecifier: "typeorm-naming-strategies",
|
|
1450
|
+
namedImports: ["SnakeNamingStrategy"],
|
|
1451
|
+
});
|
|
1452
|
+
sourceFile.addImportDeclaration({
|
|
1453
|
+
moduleSpecifier: entityImportPath,
|
|
1454
|
+
namedImports: [entityClassName],
|
|
1455
|
+
});
|
|
1456
|
+
sourceFile.addFunction({
|
|
1457
|
+
name: "getEntitiesFromExports",
|
|
1458
|
+
parameters: [{ name: "exports", type: "Record<string, any>" }],
|
|
1459
|
+
statements: (writer) => {
|
|
1460
|
+
writer.writeLine("const metadataStorage = getMetadataArgsStorage();");
|
|
1461
|
+
writer.writeLine("return Object.values(exports).filter((item) =>");
|
|
1462
|
+
writer.indent(() => {
|
|
1463
|
+
writer.writeLine("metadataStorage.tables.some((table) => table.target === item),");
|
|
1464
|
+
});
|
|
1465
|
+
writer.write(");");
|
|
1466
|
+
},
|
|
1467
|
+
});
|
|
1468
|
+
sourceFile.addVariableStatement({
|
|
1469
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
1470
|
+
declarations: [
|
|
1471
|
+
{
|
|
1472
|
+
name: "coreEntities",
|
|
1473
|
+
initializer: "getEntitiesFromExports(SolidCoreModuleExports)",
|
|
1474
|
+
},
|
|
1475
|
+
],
|
|
1476
|
+
});
|
|
1477
|
+
sourceFile.addVariableStatement({
|
|
1478
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
1479
|
+
declarations: [
|
|
1480
|
+
{
|
|
1481
|
+
name: "dynamicModules",
|
|
1482
|
+
initializer: "getDynamicModuleNames()",
|
|
1483
|
+
},
|
|
1484
|
+
],
|
|
1485
|
+
});
|
|
1486
|
+
sourceFile.addVariableStatement({
|
|
1487
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
1488
|
+
declarations: [
|
|
1489
|
+
{
|
|
1490
|
+
name: "entities",
|
|
1491
|
+
initializer: `[\n ...coreEntities,\n ${entityClassName},\n]`,
|
|
1492
|
+
},
|
|
1493
|
+
],
|
|
1494
|
+
});
|
|
1495
|
+
sourceFile.addVariableStatement({
|
|
1496
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
1497
|
+
declarations: [
|
|
1498
|
+
{
|
|
1499
|
+
name: "logging",
|
|
1500
|
+
initializer: `parseBooleanEnv('${envPrefix}_DATABASE_LOGGING')`,
|
|
1501
|
+
},
|
|
1502
|
+
],
|
|
1503
|
+
});
|
|
1504
|
+
sourceFile.addStatements("// IMPORTANT: synchronize must be false when using migrations");
|
|
1505
|
+
sourceFile.addVariableStatement({
|
|
1506
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
1507
|
+
isExported: true,
|
|
1508
|
+
declarations: [
|
|
1509
|
+
{
|
|
1510
|
+
name: "DefaultDataSource",
|
|
1511
|
+
initializer: (writer) => {
|
|
1512
|
+
writer.write("new DataSource(");
|
|
1513
|
+
writer.inlineBlock(() => {
|
|
1514
|
+
writer.writeLine(`type: '${datasource.type}',`);
|
|
1515
|
+
writer.blankLine();
|
|
1516
|
+
writer.writeLine(`host: process.env.${envPrefix}_DATABASE_HOST,`);
|
|
1517
|
+
writer.writeLine(`port: +(process.env.${envPrefix}_DATABASE_PORT || ${portFallback}),`);
|
|
1518
|
+
writer.writeLine(`username: process.env.${envPrefix}_DATABASE_USER,`);
|
|
1519
|
+
writer.writeLine(`password: process.env.${envPrefix}_DATABASE_PASSWORD,`);
|
|
1520
|
+
writer.writeLine(`database: process.env.${envPrefix}_DATABASE_NAME,`);
|
|
1521
|
+
writer.blankLine();
|
|
1522
|
+
writer.writeLine("entities,");
|
|
1523
|
+
writer.writeLine("migrations: [");
|
|
1524
|
+
writer.indent(() => {
|
|
1525
|
+
writer.writeLine(`${migrationGlob},`);
|
|
1526
|
+
});
|
|
1527
|
+
writer.writeLine("],");
|
|
1528
|
+
writer.writeLine("synchronize: false,");
|
|
1529
|
+
writer.writeLine("logging,");
|
|
1530
|
+
writer.writeLine("namingStrategy: new SnakeNamingStrategy(),");
|
|
1531
|
+
if (datasource.type === "mssql") {
|
|
1532
|
+
writer.writeLine("options: {");
|
|
1533
|
+
writer.indent(() => {
|
|
1534
|
+
writer.writeLine("encrypt: false,");
|
|
1535
|
+
writer.writeLine("trustServerCertificate: true,");
|
|
1536
|
+
});
|
|
1537
|
+
writer.writeLine("},");
|
|
1538
|
+
}
|
|
1539
|
+
});
|
|
1540
|
+
writer.write(")");
|
|
1541
|
+
},
|
|
1542
|
+
},
|
|
1543
|
+
],
|
|
1544
|
+
});
|
|
1545
|
+
sourceFile.organizeImports();
|
|
1546
|
+
sourceFile.formatText();
|
|
1547
|
+
return sourceFile.getFullText();
|
|
1548
|
+
}
|
|
1549
|
+
arrayLiteralContainsValue(arrayLiteral, rawValue) {
|
|
1550
|
+
const normalizedTarget = rawValue.replace(/\s+/g, "");
|
|
1551
|
+
return arrayLiteral.getElements().some((element) => element.getText().replace(/\s+/g, "") === normalizedTarget);
|
|
1552
|
+
}
|
|
1553
|
+
getTypeormDatasourceOptions(sourceFile) {
|
|
1554
|
+
const defaultDataSourceDeclaration = sourceFile.getVariableDeclarationOrThrow("DefaultDataSource");
|
|
1555
|
+
const initializer = defaultDataSourceDeclaration.getInitializer();
|
|
1556
|
+
if (!initializer || !ts_morph_1.Node.isNewExpression(initializer)) {
|
|
1557
|
+
throw new common_1.BadRequestException("Unable to locate DefaultDataSource initializer in datasource file.");
|
|
1558
|
+
}
|
|
1559
|
+
const options = initializer.getArguments()[0];
|
|
1560
|
+
if (!options || !ts_morph_1.Node.isObjectLiteralExpression(options)) {
|
|
1561
|
+
throw new common_1.BadRequestException("Unable to locate DefaultDataSource options object in datasource file.");
|
|
1562
|
+
}
|
|
1563
|
+
return options;
|
|
1564
|
+
}
|
|
1565
|
+
getObjectPropertyArrayLiteral(objectLiteral, propertyName) {
|
|
1566
|
+
const property = objectLiteral.getProperty(propertyName);
|
|
1567
|
+
if (!property || !ts_morph_1.Node.isPropertyAssignment(property)) {
|
|
1568
|
+
throw new common_1.BadRequestException(`Unable to locate "${propertyName}" property in datasource file.`);
|
|
1569
|
+
}
|
|
1570
|
+
const initializer = property.getInitializer();
|
|
1571
|
+
if (!initializer || !ts_morph_1.Node.isArrayLiteralExpression(initializer)) {
|
|
1572
|
+
throw new common_1.BadRequestException(`Unable to locate "${propertyName}" array in datasource file.`);
|
|
1573
|
+
}
|
|
1574
|
+
return initializer;
|
|
1575
|
+
}
|
|
1193
1576
|
buildSynchronizeBlockedMessage(datasource) {
|
|
1194
1577
|
return `Datasource "${datasource.displayName}" has synchronize enabled. Set ${datasource.envPrefix}_DATABASE_SYNCHRONIZE=false before starting legacy table introspection.`;
|
|
1195
1578
|
}
|
|
@@ -1271,6 +1654,8 @@ exports.DatasourceIntrospectionService = DatasourceIntrospectionService = __deco
|
|
|
1271
1654
|
model_metadata_helper_service_1.ModelMetadataHelperService,
|
|
1272
1655
|
field_metadata_service_1.FieldMetadataService,
|
|
1273
1656
|
model_metadata_service_1.ModelMetadataService,
|
|
1657
|
+
module_metadata_service_1.ModuleMetadataService,
|
|
1658
|
+
command_service_1.CommandService,
|
|
1274
1659
|
mssql_datasource_introspection_provider_service_1.MssqlDatasourceIntrospectionProviderService,
|
|
1275
1660
|
mysql_datasource_introspection_provider_service_1.MysqlDatasourceIntrospectionProviderService,
|
|
1276
1661
|
postgres_datasource_introspection_provider_service_1.PostgresDatasourceIntrospectionProviderService])
|