@solidxai/core 0.1.11-beta.1 → 0.1.11-beta.12
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 +46 -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/controllers/service.controller.d.ts +1 -0
- package/dist/controllers/service.controller.d.ts.map +1 -1
- package/dist/controllers/service.controller.js +2 -2
- package/dist/controllers/service.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/environment.helper.d.ts +1 -0
- package/dist/helpers/environment.helper.d.ts.map +1 -1
- package/dist/helpers/environment.helper.js +4 -0
- package/dist/helpers/environment.helper.js.map +1 -1
- package/dist/helpers/field-crud-managers/IntFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/IntFieldCrudManager.js +4 -1
- package/dist/helpers/field-crud-managers/IntFieldCrudManager.js.map +1 -1
- package/dist/helpers/model-metadata-helper.service.d.ts +2 -1
- package/dist/helpers/model-metadata-helper.service.d.ts.map +1 -1
- package/dist/helpers/model-metadata-helper.service.js +9 -5
- package/dist/helpers/model-metadata-helper.service.js.map +1 -1
- package/dist/helpers/module-metadata-helper.service.js +1 -1
- package/dist/helpers/module-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/helpers/solid-registry.d.ts +2 -1
- package/dist/helpers/solid-registry.d.ts.map +1 -1
- package/dist/helpers/solid-registry.js.map +1 -1
- package/dist/helpers/typeorm-db-helper.d.ts +1 -0
- package/dist/helpers/typeorm-db-helper.d.ts.map +1 -1
- package/dist/helpers/typeorm-db-helper.js +12 -0
- package/dist/helpers/typeorm-db-helper.js.map +1 -1
- 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/interfaces.d.ts +1 -0
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/repository/scheduled-job.repository.d.ts +2 -2
- package/dist/repository/scheduled-job.repository.d.ts.map +1 -1
- package/dist/repository/scheduled-job.repository.js +6 -3
- package/dist/repository/scheduled-job.repository.js.map +1 -1
- package/dist/repository/security-rule.repository.d.ts +2 -2
- package/dist/repository/security-rule.repository.d.ts.map +1 -1
- package/dist/repository/security-rule.repository.js +6 -3
- package/dist/repository/security-rule.repository.js.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts +11 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +163 -3
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +128 -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/authentication.service.d.ts +2 -0
- package/dist/services/authentication.service.d.ts.map +1 -1
- package/dist/services/authentication.service.js +32 -11
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.d.ts.map +1 -1
- package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.js +7 -5
- package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.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-helper.service.d.ts.map +1 -1
- package/dist/services/crud-helper.service.js +25 -12
- package/dist/services/crud-helper.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 +63 -2
- package/dist/services/datasource-introspection.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection.service.js +665 -188
- 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/locale.service.d.ts +14 -0
- package/dist/services/locale.service.d.ts.map +1 -1
- package/dist/services/locale.service.js +83 -0
- package/dist/services/locale.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/menu-item-metadata.service.d.ts.map +1 -1
- package/dist/services/menu-item-metadata.service.js +1 -0
- package/dist/services/menu-item-metadata.service.js.map +1 -1
- package/dist/services/model-metadata.service.d.ts +3 -0
- package/dist/services/model-metadata.service.d.ts.map +1 -1
- package/dist/services/model-metadata.service.js +78 -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 +28 -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/services/role-metadata.service.d.ts +1 -0
- package/dist/services/role-metadata.service.d.ts.map +1 -1
- package/dist/services/role-metadata.service.js +23 -2
- package/dist/services/role-metadata.service.js.map +1 -1
- package/dist/services/settings/default-settings-provider.service.d.ts +244 -0
- package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
- package/dist/services/settings/default-settings-provider.service.js +120 -0
- package/dist/services/settings/default-settings-provider.service.js.map +1 -1
- package/dist/services/solid-ts-morph.service.d.ts +6 -0
- package/dist/services/solid-ts-morph.service.d.ts.map +1 -1
- package/dist/services/solid-ts-morph.service.js +43 -0
- package/dist/services/solid-ts-morph.service.js.map +1 -1
- package/dist/services/view-metadata.service.d.ts.map +1 -1
- package/dist/services/view-metadata.service.js +4 -7
- package/dist/services/view-metadata.service.js.map +1 -1
- 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 +5 -1
- package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
- package/dist/subscribers/audit.subscriber.js +76 -12
- package/dist/subscribers/audit.subscriber.js.map +1 -1
- package/dist/subscribers/computed-entity-field.subscriber.d.ts +9 -1
- package/dist/subscribers/computed-entity-field.subscriber.d.ts.map +1 -1
- package/dist/subscribers/computed-entity-field.subscriber.js +45 -11
- package/dist/subscribers/computed-entity-field.subscriber.js.map +1 -1
- package/dist/subscribers/created-by-updated-by.subscriber.d.ts.map +1 -1
- package/dist/subscribers/created-by-updated-by.subscriber.js.map +1 -1
- package/dist/subscribers/field-metadata.subscriber.d.ts.map +1 -1
- package/dist/subscribers/field-metadata.subscriber.js.map +1 -1
- package/dist/subscribers/scheduled-job.subscriber.d.ts.map +1 -1
- package/dist/subscribers/scheduled-job.subscriber.js +3 -2
- package/dist/subscribers/scheduled-job.subscriber.js.map +1 -1
- package/dist/subscribers/security-rule.subscriber.d.ts.map +1 -1
- package/dist/subscribers/security-rule.subscriber.js +5 -3
- package/dist/subscribers/security-rule.subscriber.js.map +1 -1
- 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/controllers/service.controller.ts +3 -2
- 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/environment.helper.ts +11 -0
- package/src/helpers/field-crud-managers/IntFieldCrudManager.ts +4 -1
- package/src/helpers/model-metadata-helper.service.ts +8 -3
- package/src/helpers/module-metadata-helper.service.ts +1 -1
- package/src/helpers/nodemon-heartbeat.ts +59 -0
- package/src/helpers/solid-registry.ts +7 -1
- package/src/helpers/typeorm-db-helper.ts +15 -0
- package/src/index.ts +3 -2
- package/src/interfaces/datasource-introspection-provider.interface.ts +1 -0
- package/src/interfaces.ts +1 -0
- package/src/repository/scheduled-job.repository.ts +10 -4
- package/src/repository/security-rule.repository.ts +10 -4
- package/src/seeders/module-metadata-seeder.service.ts +192 -2
- package/src/seeders/seed-data/solid-core-metadata.json +128 -34
- package/src/services/api-key.service.ts +36 -36
- package/src/services/authentication.service.ts +46 -12
- package/src/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.ts +11 -5
- package/src/services/computed-fields/entity/concat-entity-computed-field-provider.service.ts +15 -1
- package/src/services/crud-helper.service.ts +25 -12
- 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 +827 -164
- package/src/services/import-transaction.service.ts +5 -0
- package/src/services/locale.service.ts +111 -3
- package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +2 -2
- package/src/services/menu-item-metadata.service.ts +2 -1
- package/src/services/model-metadata.service.ts +109 -1
- package/src/services/module-metadata.service.ts +36 -0
- package/src/services/removed-field-migration.service.ts +334 -0
- package/src/services/role-metadata.service.ts +26 -2
- package/src/services/settings/default-settings-provider.service.ts +120 -0
- package/src/services/solid-ts-morph.service.ts +61 -1
- package/src/services/view-metadata.service.ts +4 -8
- package/src/solid-core.module.ts +4 -0
- package/src/subscribers/audit.subscriber.ts +101 -18
- package/src/subscribers/computed-entity-field.subscriber.ts +81 -28
- package/src/subscribers/created-by-updated-by.subscriber.ts +3 -0
- package/src/subscribers/field-metadata.subscriber.ts +3 -0
- package/src/subscribers/scheduled-job.subscriber.ts +3 -2
- package/src/subscribers/security-rule.subscriber.ts +8 -3
- 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/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,95 @@ 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);
|
|
192
|
-
if (plan.migration.willGenerate && plan.migration.content && plan.migration.filePath) {
|
|
193
|
-
await fs.mkdir(path.dirname(plan.migration.filePath), { recursive: true });
|
|
194
|
-
await fs.writeFile(plan.migration.filePath, plan.migration.content, "utf8");
|
|
195
|
-
}
|
|
196
276
|
const persistedModelId = mappingDto.modelId ?? persistedModel?.id ?? null;
|
|
197
277
|
return {
|
|
198
278
|
data: {
|
|
199
279
|
modelId: persistedModelId,
|
|
200
280
|
operation: plan.operation,
|
|
201
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
|
+
}
|
|
293
|
+
const migrationWritten = await this.writeMigrationFile(plan.migration);
|
|
294
|
+
const datasourceFile = await this.syncTypeormDatasourceFile(module.name, datasource, plan.modelPayload.singularName);
|
|
295
|
+
return {
|
|
296
|
+
data: {
|
|
297
|
+
modelId: mappingDto.modelId,
|
|
298
|
+
operation: plan.operation,
|
|
202
299
|
migration: {
|
|
203
300
|
...plan.migration,
|
|
204
|
-
written:
|
|
301
|
+
written: migrationWritten,
|
|
205
302
|
},
|
|
303
|
+
datasourceFile,
|
|
206
304
|
summary: plan.summary,
|
|
207
305
|
},
|
|
208
306
|
};
|
|
209
307
|
}
|
|
308
|
+
async generateCode(moduleId) {
|
|
309
|
+
await this.loadModule(moduleId);
|
|
310
|
+
const output = await this.moduleMetadataService.generateCodeViaCtl(moduleId);
|
|
311
|
+
return {
|
|
312
|
+
data: {
|
|
313
|
+
moduleId,
|
|
314
|
+
output,
|
|
315
|
+
message: "Module code generation completed.",
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
async runMigration(moduleId, datasourceName) {
|
|
320
|
+
const module = await this.loadModule(moduleId);
|
|
321
|
+
const datasource = this.requireDatasource(datasourceName);
|
|
322
|
+
const cwd = path.join(process.cwd(), "..");
|
|
323
|
+
const outputs = [];
|
|
324
|
+
const migrationOutput = await this.commandService.executeCommandWithArgs({
|
|
325
|
+
command: "npx",
|
|
326
|
+
args: ["-y", "@solidxai/solidctl@latest", "migration", "-d", datasource.name, "run"],
|
|
327
|
+
cwd,
|
|
328
|
+
});
|
|
329
|
+
outputs.push([
|
|
330
|
+
`npx @solidxai/solidctl@latest migration -d ${datasource.name} run`,
|
|
331
|
+
migrationOutput,
|
|
332
|
+
].filter(Boolean).join("\n"));
|
|
333
|
+
const buildOutput = await this.commandService.executeCommandWithArgs({
|
|
334
|
+
command: "npx",
|
|
335
|
+
args: ["-y", "@solidxai/solidctl@latest", "build"],
|
|
336
|
+
cwd,
|
|
337
|
+
});
|
|
338
|
+
outputs.push([
|
|
339
|
+
"npx @solidxai/solidctl@latest build",
|
|
340
|
+
buildOutput,
|
|
341
|
+
].filter(Boolean).join("\n"));
|
|
342
|
+
const seedOutput = await this.commandService.executeCommandWithArgs({
|
|
343
|
+
command: "npx",
|
|
344
|
+
args: ["-y", "@solidxai/solidctl@latest", "seed", "--modules-to-seed", module.name],
|
|
345
|
+
cwd,
|
|
346
|
+
});
|
|
347
|
+
outputs.push([
|
|
348
|
+
`npx @solidxai/solidctl@latest seed --modules-to-seed ${module.name}`,
|
|
349
|
+
seedOutput,
|
|
350
|
+
].filter(Boolean).join("\n"));
|
|
351
|
+
return {
|
|
352
|
+
data: {
|
|
353
|
+
moduleId,
|
|
354
|
+
datasource: datasource.name,
|
|
355
|
+
output: outputs.join("\n\n"),
|
|
356
|
+
command: [
|
|
357
|
+
`npx @solidxai/solidctl@latest migration -d ${datasource.name} run`,
|
|
358
|
+
"npx @solidxai/solidctl@latest build",
|
|
359
|
+
`npx @solidxai/solidctl@latest seed --modules-to-seed ${module.name}`,
|
|
360
|
+
].join("\n"),
|
|
361
|
+
message: `Migration, build, and seed completed successfully for datasource "${datasource.name}".`,
|
|
362
|
+
},
|
|
363
|
+
};
|
|
364
|
+
}
|
|
210
365
|
async buildMappingPreview(moduleId, mappingDto) {
|
|
211
366
|
const module = await this.loadModule(moduleId);
|
|
212
367
|
const datasource = this.requireDatasource(mappingDto.dataSource);
|
|
@@ -219,6 +374,8 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
219
374
|
if (!tableRecord) {
|
|
220
375
|
throw new common_1.NotFoundException(`Table "${mappingDto.tableName}" was not found on datasource "${datasource.name}".`);
|
|
221
376
|
}
|
|
377
|
+
const tableColumns = inventory.columnsByKey.get(tableKey) ?? [];
|
|
378
|
+
this.validateTablePrimaryKeyForMapping(tableRecord, tableColumns, mappingDto);
|
|
222
379
|
const existingModel = mappingDto.mapped && mappingDto.modelId
|
|
223
380
|
? await this.modelMetadataRepository.findOne({
|
|
224
381
|
where: { id: mappingDto.modelId },
|
|
@@ -233,14 +390,14 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
233
390
|
if (mappingDto.mapped && !existingModel) {
|
|
234
391
|
throw new common_1.NotFoundException(`Mapped model "${mappingDto.modelId}" was not found.`);
|
|
235
392
|
}
|
|
236
|
-
const reviewedModel = this.normalizeReviewedMetadataModel(mappingDto.reviewedModel, mappingDto);
|
|
393
|
+
const reviewedModel = this.normalizeReviewedMetadataModel(mappingDto.reviewedModel, mappingDto, tableColumns);
|
|
237
394
|
const modelPayload = reviewedModel
|
|
238
395
|
? this.buildModelPayloadFromReviewedModel(moduleId, mappingDto, existingModel, reviewedModel)
|
|
239
396
|
: this.buildModelPayload(moduleId, mappingDto, existingModel);
|
|
240
397
|
const metadataJson = reviewedModel
|
|
241
398
|
? await this.buildMetadataJsonPreviewFromReviewedModel(module.name, reviewedModel)
|
|
242
399
|
: await this.buildMetadataJsonPreview(module.name, mappingDto, existingModel);
|
|
243
|
-
const migration = await this.buildMigrationPreview(module.name, datasource, mappingDto,
|
|
400
|
+
const migration = await this.buildMigrationPreview(module.name, datasource, mappingDto, tableColumns);
|
|
244
401
|
const includedColumnNames = mappingDto.columns
|
|
245
402
|
.filter((column) => !column.handledBySuperclass && column.include)
|
|
246
403
|
.map((column) => column.columnName);
|
|
@@ -252,6 +409,13 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
252
409
|
mappedModelId: mappingDto.modelId ?? null,
|
|
253
410
|
metadataJson,
|
|
254
411
|
migration,
|
|
412
|
+
datasourceFile: {
|
|
413
|
+
filePath: path.resolve(process.cwd(), "src", `typeorm-${datasource.name}-datasource.ts`),
|
|
414
|
+
created: false,
|
|
415
|
+
updated: false,
|
|
416
|
+
entityRegistered: false,
|
|
417
|
+
migrationGlobRegistered: false,
|
|
418
|
+
},
|
|
255
419
|
summary: {
|
|
256
420
|
includedColumnNames,
|
|
257
421
|
removedColumnNames,
|
|
@@ -297,6 +461,7 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
297
461
|
};
|
|
298
462
|
}
|
|
299
463
|
buildModelPayloadFromReviewedModel(moduleId, mappingDto, existingModel, reviewedModel) {
|
|
464
|
+
const resolvedUserKeyFieldName = this.resolveReviewedUserKeyFieldName(reviewedModel.fields, reviewedModel.userKeyFieldUserKey, mappingDto.userKeyField);
|
|
300
465
|
const systemFieldPayloads = existingModel
|
|
301
466
|
? existingModel.fields
|
|
302
467
|
.filter((field) => field.isSystem)
|
|
@@ -307,7 +472,7 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
307
472
|
return {
|
|
308
473
|
...field,
|
|
309
474
|
...(matchedExistingField?.id ? { id: matchedExistingField.id } : {}),
|
|
310
|
-
isUserKey: field.name ===
|
|
475
|
+
isUserKey: field.name === resolvedUserKeyFieldName,
|
|
311
476
|
isSystem: field.isSystem ?? false,
|
|
312
477
|
};
|
|
313
478
|
});
|
|
@@ -335,6 +500,7 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
335
500
|
async buildMetadataJsonPreview(moduleName, mappingDto, existingModel) {
|
|
336
501
|
const filePath = await this.moduleMetadataHelperService.getModuleMetadataFilePath(moduleName);
|
|
337
502
|
const previewFields = await this.buildMetadataPreviewFields(mappingDto, existingModel);
|
|
503
|
+
const resolvedUserKeyFieldName = this.resolveReviewedUserKeyFieldName(previewFields, mappingDto.userKeyField, mappingDto.userKeyField) ?? mappingDto.userKeyField;
|
|
338
504
|
return {
|
|
339
505
|
filePath,
|
|
340
506
|
model: {
|
|
@@ -345,7 +511,7 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
345
511
|
dataSource: mappingDto.dataSource,
|
|
346
512
|
dataSourceType: mappingDto.dataSourceType,
|
|
347
513
|
tableName: mappingDto.tableName,
|
|
348
|
-
userKeyFieldUserKey:
|
|
514
|
+
userKeyFieldUserKey: resolvedUserKeyFieldName,
|
|
349
515
|
isChild: false,
|
|
350
516
|
legacyTableType: mappingDto.legacyTableType,
|
|
351
517
|
parentModelUserKey: "",
|
|
@@ -394,10 +560,6 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
394
560
|
}
|
|
395
561
|
const serialized = Array.from(finalFields.values())
|
|
396
562
|
.filter((field) => !field.isSystem)
|
|
397
|
-
.map((field) => ({
|
|
398
|
-
...field,
|
|
399
|
-
isUserKey: field.name === mappingDto.userKeyField,
|
|
400
|
-
}))
|
|
401
563
|
.sort((left, right) => {
|
|
402
564
|
const leftOrder = typeof left.id === "number" ? left.id : Number.MAX_SAFE_INTEGER;
|
|
403
565
|
const rightOrder = typeof right.id === "number" ? right.id : Number.MAX_SAFE_INTEGER;
|
|
@@ -410,6 +572,10 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
410
572
|
for (const field of serialized) {
|
|
411
573
|
previewFields.push(await this.fieldMetadataService.createFieldConfig(field));
|
|
412
574
|
}
|
|
575
|
+
const resolvedUserKeyFieldName = this.resolveReviewedUserKeyFieldName(previewFields, mappingDto.userKeyField, mappingDto.userKeyField);
|
|
576
|
+
for (const field of previewFields) {
|
|
577
|
+
field.isUserKey = field.name === resolvedUserKeyFieldName;
|
|
578
|
+
}
|
|
413
579
|
return previewFields;
|
|
414
580
|
}
|
|
415
581
|
async buildMigrationPreview(moduleName, datasource, mappingDto, tableColumns) {
|
|
@@ -455,7 +621,13 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
455
621
|
}
|
|
456
622
|
return rest;
|
|
457
623
|
}
|
|
458
|
-
|
|
624
|
+
sanitizeConfiguredFieldPayload(fieldConfig) {
|
|
625
|
+
if (!fieldConfig || typeof fieldConfig !== "object" || Array.isArray(fieldConfig)) {
|
|
626
|
+
return {};
|
|
627
|
+
}
|
|
628
|
+
return this.sanitizeExistingFieldPayload(fieldConfig);
|
|
629
|
+
}
|
|
630
|
+
normalizeReviewedMetadataModel(reviewedModel, mappingDto, tableColumns) {
|
|
459
631
|
if (!reviewedModel) {
|
|
460
632
|
return null;
|
|
461
633
|
}
|
|
@@ -504,10 +676,14 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
504
676
|
}
|
|
505
677
|
fieldNames.add(loweredName);
|
|
506
678
|
}
|
|
507
|
-
const
|
|
508
|
-
|
|
679
|
+
const requestedUserKeyFieldUserKey = readString("userKeyFieldUserKey");
|
|
680
|
+
const resolvedUserKeyFieldName = this.resolveReviewedUserKeyFieldName(normalizedFields, requestedUserKeyFieldUserKey, mappingDto.userKeyField);
|
|
681
|
+
const inheritedGeneratedIdUserKey = this.isGeneratedIdInheritedUserKey(mappingDto.legacyTableType, requestedUserKeyFieldUserKey);
|
|
682
|
+
if (!resolvedUserKeyFieldName && !inheritedGeneratedIdUserKey) {
|
|
509
683
|
throw new common_1.BadRequestException("Reviewed metadata JSON must keep the selected userKeyFieldUserKey in the fields array.");
|
|
510
684
|
}
|
|
685
|
+
this.validateReviewedPrimaryKeyFields(normalizedFields, tableColumns);
|
|
686
|
+
const userKeyFieldUserKey = resolvedUserKeyFieldName ?? requestedUserKeyFieldUserKey;
|
|
511
687
|
return {
|
|
512
688
|
singularName: readString("singularName"),
|
|
513
689
|
pluralName: readString("pluralName"),
|
|
@@ -526,10 +702,39 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
526
702
|
internationalisation: readBoolean("internationalisation"),
|
|
527
703
|
fields: normalizedFields.map((field) => ({
|
|
528
704
|
...field,
|
|
529
|
-
isUserKey: field.name ===
|
|
705
|
+
isUserKey: field.name === resolvedUserKeyFieldName,
|
|
530
706
|
})),
|
|
531
707
|
};
|
|
532
708
|
}
|
|
709
|
+
resolveReviewedUserKeyFieldName(reviewedFields, requestedUserKeyFieldName, fallbackUserKeyFieldName) {
|
|
710
|
+
const activeFields = reviewedFields.filter((field) => !field.isMarkedForRemoval);
|
|
711
|
+
const normalizedRequested = `${requestedUserKeyFieldName ?? ""}`.trim().toLowerCase();
|
|
712
|
+
const normalizedFallback = `${fallbackUserKeyFieldName ?? ""}`.trim().toLowerCase();
|
|
713
|
+
const byExactRequested = activeFields.find((field) => field.name?.toLowerCase?.() === normalizedRequested);
|
|
714
|
+
if (byExactRequested) {
|
|
715
|
+
return byExactRequested.name;
|
|
716
|
+
}
|
|
717
|
+
const byExistingUserKeyFlag = activeFields.find((field) => field.isUserKey);
|
|
718
|
+
if (byExistingUserKeyFlag) {
|
|
719
|
+
return byExistingUserKeyFlag.name;
|
|
720
|
+
}
|
|
721
|
+
const byPrimaryKeyFallback = activeFields.find((field) => {
|
|
722
|
+
if (!field.isPrimaryKey) {
|
|
723
|
+
return false;
|
|
724
|
+
}
|
|
725
|
+
const normalizedName = `${field.name ?? ""}`.trim().toLowerCase();
|
|
726
|
+
return normalizedName === normalizedFallback || normalizedName === "id" || normalizedName === "legacyid";
|
|
727
|
+
});
|
|
728
|
+
if (byPrimaryKeyFallback) {
|
|
729
|
+
return byPrimaryKeyFallback.name;
|
|
730
|
+
}
|
|
731
|
+
const byFallbackFieldName = activeFields.find((field) => field.name?.toLowerCase?.() === normalizedFallback);
|
|
732
|
+
return byFallbackFieldName?.name ?? null;
|
|
733
|
+
}
|
|
734
|
+
isGeneratedIdInheritedUserKey(legacyTableType, requestedUserKeyFieldName) {
|
|
735
|
+
return legacyTableType === legacy_table_type_enum_1.LegacyTableType.GENERATED_ID
|
|
736
|
+
&& `${requestedUserKeyFieldName ?? ""}`.trim().toLowerCase() === "id";
|
|
737
|
+
}
|
|
533
738
|
normalizeReviewedField(field, index) {
|
|
534
739
|
if (!field || typeof field !== "object" || Array.isArray(field)) {
|
|
535
740
|
throw new common_1.BadRequestException(`Reviewed metadata JSON field at index ${index} is not a valid object.`);
|
|
@@ -577,32 +782,37 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
577
782
|
}
|
|
578
783
|
buildFieldPayload(column, mappingDto, existingField) {
|
|
579
784
|
const preserved = existingField ? this.sanitizeExistingFieldPayload(existingField) : {};
|
|
785
|
+
const configured = this.sanitizeConfiguredFieldPayload(column.fieldConfig);
|
|
786
|
+
const merged = {
|
|
787
|
+
...preserved,
|
|
788
|
+
...configured,
|
|
789
|
+
};
|
|
580
790
|
const maxLength = column.characterMaximumLength && ["shortText", "longText"].includes(column.solidFieldType)
|
|
581
791
|
? column.characterMaximumLength
|
|
582
|
-
:
|
|
792
|
+
: merged.max;
|
|
583
793
|
return {
|
|
584
|
-
...
|
|
794
|
+
...merged,
|
|
585
795
|
...(column.fieldId ? { id: column.fieldId } : {}),
|
|
586
796
|
name: column.fieldName,
|
|
587
|
-
displayName:
|
|
588
|
-
description:
|
|
797
|
+
displayName: column.displayName?.trim() || merged.displayName || (0, lodash_1.startCase)(column.fieldName),
|
|
798
|
+
description: merged.description ?? "",
|
|
589
799
|
type: column.solidFieldType,
|
|
590
800
|
ormType: column.ormType,
|
|
591
|
-
length:
|
|
592
|
-
defaultValue:
|
|
593
|
-
required:
|
|
594
|
-
unique:
|
|
595
|
-
encrypt:
|
|
596
|
-
index:
|
|
801
|
+
length: merged.length ?? column.characterMaximumLength ?? undefined,
|
|
802
|
+
defaultValue: merged.defaultValue ?? column.defaultValue ?? undefined,
|
|
803
|
+
required: merged.required ?? (!column.nullable || column.isPrimaryKey),
|
|
804
|
+
unique: merged.unique ?? false,
|
|
805
|
+
encrypt: merged.encrypt ?? false,
|
|
806
|
+
index: merged.index ?? false,
|
|
597
807
|
max: maxLength ?? undefined,
|
|
598
|
-
min:
|
|
599
|
-
private:
|
|
600
|
-
isSystem:
|
|
808
|
+
min: merged.min ?? undefined,
|
|
809
|
+
private: merged.private ?? false,
|
|
810
|
+
isSystem: merged.isSystem ?? false,
|
|
601
811
|
isMarkedForRemoval: false,
|
|
602
812
|
columnName: column.columnName,
|
|
603
813
|
isUserKey: mappingDto.userKeyField === column.fieldName,
|
|
604
|
-
enableAuditTracking:
|
|
605
|
-
isMultiSelect:
|
|
814
|
+
enableAuditTracking: merged.enableAuditTracking ?? mappingDto.enableAuditTracking,
|
|
815
|
+
isMultiSelect: merged.isMultiSelect ?? false,
|
|
606
816
|
isPrimaryKey: column.isPrimaryKey,
|
|
607
817
|
};
|
|
608
818
|
}
|
|
@@ -696,23 +906,19 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
696
906
|
const generatedIdUniqueName = this.buildMigrationObjectName("UQ", tableNameLiteral, "ss_id");
|
|
697
907
|
const upColumnStatements = missingSystemColumns.map((definition) => {
|
|
698
908
|
const columnLiteral = this.renderTableColumnLiteral(definition);
|
|
699
|
-
return
|
|
700
|
-
`
|
|
701
|
-
` await queryRunner.addColumn(tableName, new TableColumn(${columnLiteral}));`,
|
|
702
|
-
" }",
|
|
703
|
-
].join("\n");
|
|
909
|
+
return ` if (!(await queryRunner.hasColumn(tableName, ${JSON.stringify(definition.columnName)})))\n`
|
|
910
|
+
+ ` await queryRunner.addColumn(tableName, new TableColumn(${columnLiteral}));`;
|
|
704
911
|
}).join("\n\n");
|
|
705
912
|
const postUpStatements = [];
|
|
706
913
|
if (missingSystemColumns.some((definition) => definition.columnName === "ss_deleted_at")) {
|
|
707
914
|
postUpStatements.push([
|
|
708
915
|
" {",
|
|
709
916
|
" const table = await queryRunner.getTable(tableName);",
|
|
710
|
-
` if (table && !table.indices.some((index) => index.name === ${JSON.stringify(deletedAtIndexName)}))
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
" }",
|
|
917
|
+
` if (table && !table.indices.some((index) => index.name === ${JSON.stringify(deletedAtIndexName)}))`,
|
|
918
|
+
` await queryRunner.createIndex(tableName, new TableIndex(${this.renderTableIndexLiteral({
|
|
919
|
+
name: deletedAtIndexName,
|
|
920
|
+
columnNames: ["ss_deleted_at"],
|
|
921
|
+
})}));`,
|
|
716
922
|
" }",
|
|
717
923
|
].join("\n"));
|
|
718
924
|
}
|
|
@@ -720,13 +926,12 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
720
926
|
postUpStatements.push([
|
|
721
927
|
" {",
|
|
722
928
|
" const table = await queryRunner.getTable(tableName);",
|
|
723
|
-
` if (table && !table.indices.some((index) => index.name === ${JSON.stringify(generatedIdUniqueName)}))
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
" }",
|
|
929
|
+
` if (table && !table.indices.some((index) => index.name === ${JSON.stringify(generatedIdUniqueName)}))`,
|
|
930
|
+
` await queryRunner.createIndex(tableName, new TableIndex(${this.renderTableIndexLiteral({
|
|
931
|
+
name: generatedIdUniqueName,
|
|
932
|
+
columnNames: ["ss_id"],
|
|
933
|
+
isUnique: true,
|
|
934
|
+
})}));`,
|
|
730
935
|
" }",
|
|
731
936
|
].join("\n"));
|
|
732
937
|
}
|
|
@@ -736,9 +941,8 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
736
941
|
" {",
|
|
737
942
|
" const table = await queryRunner.getTable(tableName);",
|
|
738
943
|
` const index = table?.indices.find((entry) => entry.name === ${JSON.stringify(generatedIdUniqueName)});`,
|
|
739
|
-
" if (index)
|
|
944
|
+
" if (index)",
|
|
740
945
|
" await queryRunner.dropIndex(tableName, index);",
|
|
741
|
-
" }",
|
|
742
946
|
" }",
|
|
743
947
|
].join("\n"));
|
|
744
948
|
}
|
|
@@ -747,20 +951,16 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
747
951
|
" {",
|
|
748
952
|
" const table = await queryRunner.getTable(tableName);",
|
|
749
953
|
` const index = table?.indices.find((entry) => entry.name === ${JSON.stringify(deletedAtIndexName)});`,
|
|
750
|
-
" if (index)
|
|
954
|
+
" if (index)",
|
|
751
955
|
" await queryRunner.dropIndex(tableName, index);",
|
|
752
|
-
" }",
|
|
753
956
|
" }",
|
|
754
957
|
].join("\n"));
|
|
755
958
|
}
|
|
756
959
|
downStatements.push(...missingSystemColumns
|
|
757
960
|
.slice()
|
|
758
961
|
.reverse()
|
|
759
|
-
.map((definition) =>
|
|
760
|
-
`
|
|
761
|
-
` await queryRunner.dropColumn(tableName, ${JSON.stringify(definition.columnName)});`,
|
|
762
|
-
" }",
|
|
763
|
-
].join("\n")));
|
|
962
|
+
.map((definition) => ` if (await queryRunner.hasColumn(tableName, ${JSON.stringify(definition.columnName)}))\n`
|
|
963
|
+
+ ` await queryRunner.dropColumn(tableName, ${JSON.stringify(definition.columnName)});`));
|
|
764
964
|
return [
|
|
765
965
|
"import { MigrationInterface, QueryRunner, TableColumn, TableIndex } from \"typeorm\";",
|
|
766
966
|
"",
|
|
@@ -783,27 +983,36 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
783
983
|
}
|
|
784
984
|
renderTableColumnLiteral(definition) {
|
|
785
985
|
const properties = [
|
|
786
|
-
`
|
|
787
|
-
`
|
|
986
|
+
`name: ${JSON.stringify(definition.columnName)}`,
|
|
987
|
+
`type: ${JSON.stringify(definition.type)}`,
|
|
788
988
|
];
|
|
789
989
|
if (definition.length) {
|
|
790
|
-
properties.push(`
|
|
990
|
+
properties.push(`length: ${JSON.stringify(definition.length)}`);
|
|
791
991
|
}
|
|
792
992
|
if (typeof definition.isNullable === "boolean") {
|
|
793
|
-
properties.push(`
|
|
993
|
+
properties.push(`isNullable: ${definition.isNullable}`);
|
|
794
994
|
}
|
|
795
995
|
if (definition.defaultExpression) {
|
|
796
|
-
properties.push(`
|
|
996
|
+
properties.push(`default: ${JSON.stringify(definition.defaultExpression)}`);
|
|
797
997
|
}
|
|
798
998
|
if (definition.isGenerated) {
|
|
799
|
-
properties.push("
|
|
800
|
-
properties.push("
|
|
999
|
+
properties.push("isGenerated: true");
|
|
1000
|
+
properties.push("generationStrategy: \"increment\"");
|
|
801
1001
|
}
|
|
802
1002
|
if (definition.isUnique) {
|
|
803
|
-
properties.push("
|
|
1003
|
+
properties.push("isUnique: true");
|
|
1004
|
+
}
|
|
1005
|
+
return `{ ${properties.join(", ")}, }`;
|
|
1006
|
+
}
|
|
1007
|
+
renderTableIndexLiteral(definition) {
|
|
1008
|
+
const properties = [
|
|
1009
|
+
`name: ${JSON.stringify(definition.name)}`,
|
|
1010
|
+
`columnNames: ${JSON.stringify(definition.columnNames)}`,
|
|
1011
|
+
];
|
|
1012
|
+
if (definition.isUnique) {
|
|
1013
|
+
properties.push("isUnique: true");
|
|
804
1014
|
}
|
|
805
|
-
properties.
|
|
806
|
-
return properties.join("\n");
|
|
1015
|
+
return `{ ${properties.join(", ")}, }`;
|
|
807
1016
|
}
|
|
808
1017
|
buildMigrationObjectName(prefix, tableName, columnName) {
|
|
809
1018
|
const hash = (0, crypto_1.createHash)("md5").update(`${tableName}:${columnName}:${prefix}`).digest("hex").slice(0, 24);
|
|
@@ -832,12 +1041,14 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
832
1041
|
async buildTableInventory(datasource) {
|
|
833
1042
|
const provider = this.getProvider(datasource.type);
|
|
834
1043
|
const runtimeDataSource = this.resolveDataSourceByName(datasource.name);
|
|
835
|
-
const [
|
|
1044
|
+
const [rawTables, rawColumns, models] = await Promise.all([
|
|
836
1045
|
provider.listTables(runtimeDataSource),
|
|
837
1046
|
provider.listColumns(runtimeDataSource),
|
|
838
1047
|
this.modelMetadataRepository.find({ relations: { module: true } }),
|
|
839
1048
|
]);
|
|
840
|
-
const
|
|
1049
|
+
const tables = rawTables.filter((table) => !this.isSolidxInternalTableName(table.tableName));
|
|
1050
|
+
const columns = rawColumns.filter((column) => !this.isSolidxInternalTableName(column.tableName));
|
|
1051
|
+
const modelLookup = this.buildModelLookup(models);
|
|
841
1052
|
const columnsByKey = this.groupColumnsByTable(columns);
|
|
842
1053
|
const tableKeys = new Set();
|
|
843
1054
|
for (const table of tables) {
|
|
@@ -857,8 +1068,10 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
857
1068
|
}
|
|
858
1069
|
const suggestedLegacyTableType = this.detectLegacyTableType(datasource.type, tableColumns);
|
|
859
1070
|
const suggestedIdColumn = this.getSuggestedIdColumn(datasource.type, tableColumns, suggestedLegacyTableType);
|
|
860
|
-
const mappedModel =
|
|
1071
|
+
const mappedModel = this.resolveMappedModel(modelLookup, datasource.name, tableName);
|
|
861
1072
|
const physicalColumns = tableColumns.filter((column) => !this.isHandledBySuperclass(column.columnName, suggestedLegacyTableType));
|
|
1073
|
+
const primaryKeyColumns = tableColumns.filter((column) => column.isPrimaryKey);
|
|
1074
|
+
const hasPrimaryKey = primaryKeyColumns.length > 0;
|
|
862
1075
|
return {
|
|
863
1076
|
schema,
|
|
864
1077
|
tableName,
|
|
@@ -875,6 +1088,12 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
875
1088
|
suggestedUserKeyField: this.buildUserKeyCandidates(tableColumns)[0]?.name ?? "id",
|
|
876
1089
|
columnCount: tableColumns.length,
|
|
877
1090
|
physicalColumnCount: physicalColumns.length,
|
|
1091
|
+
hasPrimaryKey,
|
|
1092
|
+
primaryKeyColumnCount: primaryKeyColumns.length,
|
|
1093
|
+
primaryKeyColumnNames: primaryKeyColumns.map((column) => column.columnName),
|
|
1094
|
+
mappingBlockedReason: hasPrimaryKey
|
|
1095
|
+
? null
|
|
1096
|
+
: this.buildMissingPrimaryKeyMessage(tableName),
|
|
878
1097
|
};
|
|
879
1098
|
})
|
|
880
1099
|
.filter((record) => Boolean(record))
|
|
@@ -892,16 +1111,37 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
892
1111
|
columnsByKey,
|
|
893
1112
|
};
|
|
894
1113
|
}
|
|
895
|
-
|
|
896
|
-
const
|
|
1114
|
+
buildModelLookup(models) {
|
|
1115
|
+
const byDatasourceAndTable = new Map();
|
|
1116
|
+
const byTableName = new Map();
|
|
897
1117
|
for (const model of models) {
|
|
898
1118
|
if (!model.tableName) {
|
|
899
1119
|
continue;
|
|
900
1120
|
}
|
|
901
|
-
|
|
902
|
-
|
|
1121
|
+
byDatasourceAndTable.set(this.toModelMapKey(model.dataSource, model.tableName), model);
|
|
1122
|
+
const normalizedTableName = model.tableName.trim().toLowerCase();
|
|
1123
|
+
const bucket = byTableName.get(normalizedTableName);
|
|
1124
|
+
if (bucket) {
|
|
1125
|
+
bucket.push(model);
|
|
1126
|
+
continue;
|
|
1127
|
+
}
|
|
1128
|
+
byTableName.set(normalizedTableName, [model]);
|
|
903
1129
|
}
|
|
904
|
-
return
|
|
1130
|
+
return {
|
|
1131
|
+
byDatasourceAndTable,
|
|
1132
|
+
byTableName,
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
resolveMappedModel(lookup, datasourceName, tableName) {
|
|
1136
|
+
const exactMatch = lookup.byDatasourceAndTable.get(this.toModelMapKey(datasourceName, tableName));
|
|
1137
|
+
if (exactMatch) {
|
|
1138
|
+
return exactMatch;
|
|
1139
|
+
}
|
|
1140
|
+
const fallbackMatches = lookup.byTableName.get(tableName.trim().toLowerCase()) ?? [];
|
|
1141
|
+
if (fallbackMatches.length === 1) {
|
|
1142
|
+
return fallbackMatches[0];
|
|
1143
|
+
}
|
|
1144
|
+
return fallbackMatches[0] ?? null;
|
|
905
1145
|
}
|
|
906
1146
|
groupColumnsByTable(columns) {
|
|
907
1147
|
const grouped = new Map();
|
|
@@ -916,6 +1156,9 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
916
1156
|
}
|
|
917
1157
|
return grouped;
|
|
918
1158
|
}
|
|
1159
|
+
isSolidxInternalTableName(tableName) {
|
|
1160
|
+
return typeof tableName === "string" && tableName.toLowerCase().startsWith("ss_");
|
|
1161
|
+
}
|
|
919
1162
|
detectLegacyTableType(datasourceType, columns) {
|
|
920
1163
|
const normalizedDatasourceType = datasourceType.toLowerCase();
|
|
921
1164
|
const hasExistingGeneratedId = columns.some((column) => column.isAutoGenerated);
|
|
@@ -983,12 +1226,16 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
983
1226
|
}
|
|
984
1227
|
toDetailColumnRecord(column, legacyTableType, datasourceType) {
|
|
985
1228
|
const superclassFieldName = this.getSuperclassFieldName(column.columnName, legacyTableType);
|
|
1229
|
+
const normalizedColumnName = column.columnName.toLowerCase();
|
|
986
1230
|
const fieldName = legacyTableType === legacy_table_type_enum_1.LegacyTableType.EXISTING_ID && column.isAutoGenerated
|
|
987
1231
|
? "id"
|
|
988
|
-
:
|
|
1232
|
+
: legacyTableType === legacy_table_type_enum_1.LegacyTableType.GENERATED_ID && normalizedColumnName === "id"
|
|
1233
|
+
? "legacyId"
|
|
1234
|
+
: this.toFieldName(column.columnName);
|
|
989
1235
|
return {
|
|
990
1236
|
columnName: column.columnName,
|
|
991
1237
|
fieldName,
|
|
1238
|
+
displayName: (0, lodash_1.startCase)(fieldName),
|
|
992
1239
|
dataType: column.dataType,
|
|
993
1240
|
solidFieldType: this.toSolidFieldType(column),
|
|
994
1241
|
ormType: this.toOrmFieldType(column, datasourceType),
|
|
@@ -999,10 +1246,49 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
999
1246
|
numericScale: column.numericScale,
|
|
1000
1247
|
ordinalPosition: column.ordinalPosition,
|
|
1001
1248
|
isAutoGenerated: column.isAutoGenerated,
|
|
1249
|
+
isPrimaryKey: column.isPrimaryKey,
|
|
1002
1250
|
handledBySuperclass: Boolean(superclassFieldName),
|
|
1003
1251
|
superclassFieldName,
|
|
1004
1252
|
};
|
|
1005
1253
|
}
|
|
1254
|
+
validateTablePrimaryKeyForMapping(tableRecord, tableColumns, mappingDto) {
|
|
1255
|
+
const detectedPrimaryKeyColumns = tableColumns.filter((column) => column.isPrimaryKey);
|
|
1256
|
+
if (!detectedPrimaryKeyColumns.length) {
|
|
1257
|
+
throw new common_1.BadRequestException(tableRecord.mappingBlockedReason ?? this.buildMissingPrimaryKeyMessage(tableRecord.tableName));
|
|
1258
|
+
}
|
|
1259
|
+
const requestColumnsByName = new Map(mappingDto.columns.map((column) => [column.columnName.trim().toLowerCase(), column]));
|
|
1260
|
+
for (const primaryKeyColumn of detectedPrimaryKeyColumns) {
|
|
1261
|
+
const matchingRequestColumn = requestColumnsByName.get(primaryKeyColumn.columnName.trim().toLowerCase());
|
|
1262
|
+
if (!matchingRequestColumn) {
|
|
1263
|
+
throw new common_1.BadRequestException(`Primary key column "${primaryKeyColumn.columnName}" must remain present in the mapping request for table "${tableRecord.tableName}".`);
|
|
1264
|
+
}
|
|
1265
|
+
if (!matchingRequestColumn.include) {
|
|
1266
|
+
throw new common_1.BadRequestException(`Primary key column "${primaryKeyColumn.columnName}" cannot be excluded from the mapping for table "${tableRecord.tableName}".`);
|
|
1267
|
+
}
|
|
1268
|
+
if (!matchingRequestColumn.isPrimaryKey) {
|
|
1269
|
+
throw new common_1.BadRequestException(`Primary key column "${primaryKeyColumn.columnName}" must stay marked as isPrimaryKey for table "${tableRecord.tableName}".`);
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
validateReviewedPrimaryKeyFields(reviewedFields, tableColumns) {
|
|
1274
|
+
const detectedPrimaryKeyColumns = tableColumns.filter((column) => column.isPrimaryKey);
|
|
1275
|
+
if (!detectedPrimaryKeyColumns.length) {
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
const activeReviewedFields = reviewedFields.filter((field) => !field.isMarkedForRemoval);
|
|
1279
|
+
for (const primaryKeyColumn of detectedPrimaryKeyColumns) {
|
|
1280
|
+
const matchingReviewedField = activeReviewedFields.find((field) => `${field.columnName ?? ""}`.trim().toLowerCase() === primaryKeyColumn.columnName.trim().toLowerCase());
|
|
1281
|
+
if (!matchingReviewedField) {
|
|
1282
|
+
throw new common_1.BadRequestException(`Reviewed metadata JSON must keep datasource primary key column "${primaryKeyColumn.columnName}" in the fields array.`);
|
|
1283
|
+
}
|
|
1284
|
+
if (!matchingReviewedField.isPrimaryKey) {
|
|
1285
|
+
throw new common_1.BadRequestException(`Reviewed metadata JSON must keep datasource primary key column "${primaryKeyColumn.columnName}" marked with isPrimaryKey: true.`);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
buildMissingPrimaryKeyMessage(tableName) {
|
|
1290
|
+
return `Table "${tableName}" cannot be mapped because the datasource table does not define any primary key. Add a real primary key to the table before continuing with SolidX mapping.`;
|
|
1291
|
+
}
|
|
1006
1292
|
getSuperclassFieldName(columnName, legacyTableType) {
|
|
1007
1293
|
return this.getLegacySuperclassFieldName(columnName, legacyTableType);
|
|
1008
1294
|
}
|
|
@@ -1033,127 +1319,70 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
1033
1319
|
}
|
|
1034
1320
|
return "LegacyCommonEntityWithExistingId";
|
|
1035
1321
|
}
|
|
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";
|
|
1322
|
+
getDatasourceTypeFamily(dataType) {
|
|
1323
|
+
const normalized = dataType.toLowerCase();
|
|
1324
|
+
for (const [family, aliases] of Object.entries(DATASOURCE_TYPE_FAMILY_ALIASES)) {
|
|
1325
|
+
if (aliases.includes(normalized)) {
|
|
1326
|
+
return family;
|
|
1327
|
+
}
|
|
1059
1328
|
}
|
|
1060
|
-
if (
|
|
1329
|
+
if (normalized.includes("json")) {
|
|
1061
1330
|
return "json";
|
|
1062
1331
|
}
|
|
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";
|
|
1332
|
+
if (normalized.includes("date")) {
|
|
1333
|
+
return "datetime";
|
|
1074
1334
|
}
|
|
1075
|
-
return
|
|
1335
|
+
return null;
|
|
1076
1336
|
}
|
|
1077
|
-
|
|
1078
|
-
const
|
|
1079
|
-
const
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
return "
|
|
1083
|
-
|
|
1337
|
+
toSolidFieldType(column) {
|
|
1338
|
+
const family = this.getDatasourceTypeFamily(column.dataType);
|
|
1339
|
+
const charLength = column.characterMaximumLength ?? null;
|
|
1340
|
+
switch (family) {
|
|
1341
|
+
case "boolean":
|
|
1342
|
+
return "boolean";
|
|
1343
|
+
case "bigint":
|
|
1084
1344
|
return "bigint";
|
|
1085
|
-
|
|
1345
|
+
case "int":
|
|
1086
1346
|
return "int";
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1347
|
+
case "numeric":
|
|
1348
|
+
case "decimal":
|
|
1349
|
+
case "float":
|
|
1090
1350
|
return "decimal";
|
|
1091
|
-
|
|
1351
|
+
case "date":
|
|
1092
1352
|
return "date";
|
|
1093
|
-
|
|
1353
|
+
case "time":
|
|
1094
1354
|
return "time";
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
if (["datetime", "smalldatetime", "timestamp"].includes(normalized) || normalized.includes("date"))
|
|
1355
|
+
case "datetime":
|
|
1356
|
+
case "datetimeWithTimezone":
|
|
1098
1357
|
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"))
|
|
1358
|
+
case "json":
|
|
1359
|
+
case "jsonb":
|
|
1127
1360
|
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";
|
|
1361
|
+
case "uuid":
|
|
1362
|
+
return "uuid";
|
|
1363
|
+
case "mediumText":
|
|
1364
|
+
case "longText":
|
|
1365
|
+
return "longText";
|
|
1366
|
+
case "shortText":
|
|
1367
|
+
case "unicodeShortText":
|
|
1368
|
+
if (charLength !== null && charLength > 255) {
|
|
1369
|
+
return "longText";
|
|
1370
|
+
}
|
|
1371
|
+
return "shortText";
|
|
1372
|
+
default:
|
|
1373
|
+
return "shortText";
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
toOrmFieldType(column, datasourceType) {
|
|
1377
|
+
const family = this.getDatasourceTypeFamily(column.dataType);
|
|
1378
|
+
const normalizedDatasourceType = datasourceType.toLowerCase();
|
|
1379
|
+
const ormType = family
|
|
1380
|
+
? ORM_TYPE_BY_DATASOURCE_FAMILY[normalizedDatasourceType]?.[family]
|
|
1381
|
+
: undefined;
|
|
1382
|
+
if (ormType) {
|
|
1383
|
+
return ormType;
|
|
1384
|
+
}
|
|
1385
|
+
return normalizedDatasourceType === "postgres" ? "varchar" : "varchar";
|
|
1157
1386
|
}
|
|
1158
1387
|
toFieldName(columnName) {
|
|
1159
1388
|
return (0, lodash_1.camelCase)(columnName.replace(/^ss_/, ""));
|
|
@@ -1190,6 +1419,252 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
1190
1419
|
toModelMapKey(datasourceName, tableName) {
|
|
1191
1420
|
return `${datasourceName.toLowerCase()}::${tableName.toLowerCase()}`;
|
|
1192
1421
|
}
|
|
1422
|
+
async syncTypeormDatasourceFile(moduleName, datasource, singularName) {
|
|
1423
|
+
const datasourceFilePath = path.resolve(process.cwd(), "src", `typeorm-${datasource.name}-datasource.ts`);
|
|
1424
|
+
const entityClassName = (0, string_helper_1.classify)(singularName);
|
|
1425
|
+
const entityImportPath = `./${moduleName}/entities/${(0, lodash_1.kebabCase)(singularName)}.entity`;
|
|
1426
|
+
const migrationGlob = `join(__dirname, './${moduleName}/migrations/${datasource.name}/*.{ts,js}')`;
|
|
1427
|
+
const existingContent = await fs.readFile(datasourceFilePath, "utf8").catch(() => null);
|
|
1428
|
+
if (!existingContent) {
|
|
1429
|
+
const rendered = this.renderTypeormDatasourceTemplate({
|
|
1430
|
+
datasource,
|
|
1431
|
+
entityClassName,
|
|
1432
|
+
entityImportPath,
|
|
1433
|
+
migrationGlob,
|
|
1434
|
+
});
|
|
1435
|
+
await fs.writeFile(datasourceFilePath, rendered, "utf8");
|
|
1436
|
+
return {
|
|
1437
|
+
filePath: datasourceFilePath,
|
|
1438
|
+
created: true,
|
|
1439
|
+
updated: false,
|
|
1440
|
+
entityRegistered: true,
|
|
1441
|
+
migrationGlobRegistered: true,
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1444
|
+
const project = new ts_morph_1.Project({
|
|
1445
|
+
skipAddingFilesFromTsConfig: true,
|
|
1446
|
+
manipulationSettings: {
|
|
1447
|
+
quoteKind: ts_morph_1.QuoteKind.Single,
|
|
1448
|
+
indentationText: ts_morph_1.IndentationText.FourSpaces,
|
|
1449
|
+
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
|
|
1450
|
+
},
|
|
1451
|
+
});
|
|
1452
|
+
const sourceFile = project.createSourceFile(datasourceFilePath, existingContent, { overwrite: true });
|
|
1453
|
+
const entityImportDeclaration = sourceFile.getImportDeclarations().find((declaration) => declaration.getModuleSpecifierValue() === entityImportPath);
|
|
1454
|
+
if (entityImportDeclaration) {
|
|
1455
|
+
const alreadyImported = entityImportDeclaration.getNamedImports().some((namedImport) => namedImport.getName() === entityClassName);
|
|
1456
|
+
if (!alreadyImported) {
|
|
1457
|
+
entityImportDeclaration.addNamedImport(entityClassName);
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
else {
|
|
1461
|
+
sourceFile.addImportDeclaration({
|
|
1462
|
+
moduleSpecifier: entityImportPath,
|
|
1463
|
+
namedImports: [entityClassName],
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
const entitiesArray = sourceFile
|
|
1467
|
+
.getVariableDeclarationOrThrow("entities")
|
|
1468
|
+
.getInitializerIfKindOrThrow(ts_morph_1.SyntaxKind.ArrayLiteralExpression);
|
|
1469
|
+
const entityAlreadyRegistered = this.arrayLiteralContainsValue(entitiesArray, entityClassName);
|
|
1470
|
+
if (!entityAlreadyRegistered) {
|
|
1471
|
+
entitiesArray.addElement(entityClassName);
|
|
1472
|
+
}
|
|
1473
|
+
const dataSourceOptions = this.getTypeormDatasourceOptions(sourceFile);
|
|
1474
|
+
const migrationsArray = this.getObjectPropertyArrayLiteral(dataSourceOptions, "migrations");
|
|
1475
|
+
const migrationAlreadyRegistered = this.arrayLiteralContainsValue(migrationsArray, migrationGlob);
|
|
1476
|
+
if (!migrationAlreadyRegistered) {
|
|
1477
|
+
migrationsArray.addElement(migrationGlob);
|
|
1478
|
+
}
|
|
1479
|
+
sourceFile.organizeImports();
|
|
1480
|
+
sourceFile.formatText();
|
|
1481
|
+
const nextContent = sourceFile.getFullText();
|
|
1482
|
+
const updated = nextContent !== existingContent;
|
|
1483
|
+
if (updated) {
|
|
1484
|
+
await fs.writeFile(datasourceFilePath, nextContent, "utf8");
|
|
1485
|
+
}
|
|
1486
|
+
return {
|
|
1487
|
+
filePath: datasourceFilePath,
|
|
1488
|
+
created: false,
|
|
1489
|
+
updated,
|
|
1490
|
+
entityRegistered: true,
|
|
1491
|
+
migrationGlobRegistered: true,
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
async writeMigrationFile(migration) {
|
|
1495
|
+
if (!migration.willGenerate || !migration.content || !migration.filePath) {
|
|
1496
|
+
return false;
|
|
1497
|
+
}
|
|
1498
|
+
await fs.mkdir(path.dirname(migration.filePath), { recursive: true });
|
|
1499
|
+
await fs.writeFile(migration.filePath, migration.content, "utf8");
|
|
1500
|
+
const writtenContent = await fs.readFile(migration.filePath, "utf8").catch(() => null);
|
|
1501
|
+
if (writtenContent !== migration.content) {
|
|
1502
|
+
throw new common_1.BadRequestException(`Migration file could not be verified after writing: ${migration.filePath}`);
|
|
1503
|
+
}
|
|
1504
|
+
return true;
|
|
1505
|
+
}
|
|
1506
|
+
renderTypeormDatasourceTemplate(input) {
|
|
1507
|
+
const { datasource, entityClassName, entityImportPath, migrationGlob } = input;
|
|
1508
|
+
const envPrefix = datasource.envPrefix?.toUpperCase?.() || "DEFAULT";
|
|
1509
|
+
const portFallback = datasource.type === "postgres" ? 5432 : datasource.type === "mysql" ? 3306 : 1433;
|
|
1510
|
+
const project = new ts_morph_1.Project({
|
|
1511
|
+
skipAddingFilesFromTsConfig: true,
|
|
1512
|
+
manipulationSettings: {
|
|
1513
|
+
quoteKind: ts_morph_1.QuoteKind.Single,
|
|
1514
|
+
indentationText: ts_morph_1.IndentationText.FourSpaces,
|
|
1515
|
+
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
|
|
1516
|
+
},
|
|
1517
|
+
});
|
|
1518
|
+
const sourceFile = project.createSourceFile("typeorm-template.ts", "", { overwrite: true });
|
|
1519
|
+
sourceFile.addImportDeclaration({ moduleSpecifier: "reflect-metadata" });
|
|
1520
|
+
sourceFile.addImportDeclaration({ moduleSpecifier: "path", namedImports: ["join"] });
|
|
1521
|
+
sourceFile.addImportDeclaration({
|
|
1522
|
+
moduleSpecifier: "dotenv",
|
|
1523
|
+
namedImports: [{ name: "config", alias: "dotenvConfig" }],
|
|
1524
|
+
});
|
|
1525
|
+
sourceFile.addStatements("dotenvConfig({ path: join(__dirname, '../.env') });");
|
|
1526
|
+
sourceFile.addStatements("");
|
|
1527
|
+
sourceFile.addImportDeclaration({ moduleSpecifier: "typeorm", namedImports: ["DataSource"] });
|
|
1528
|
+
sourceFile.addImportDeclaration({
|
|
1529
|
+
moduleSpecifier: "@solidxai/core",
|
|
1530
|
+
namespaceImport: "SolidCoreModuleExports",
|
|
1531
|
+
});
|
|
1532
|
+
sourceFile.addImportDeclaration({
|
|
1533
|
+
moduleSpecifier: "@solidxai/core",
|
|
1534
|
+
namedImports: ["getDynamicModuleNames", "parseBooleanEnv"],
|
|
1535
|
+
});
|
|
1536
|
+
sourceFile.addImportDeclaration({
|
|
1537
|
+
moduleSpecifier: "typeorm",
|
|
1538
|
+
namedImports: ["getMetadataArgsStorage"],
|
|
1539
|
+
});
|
|
1540
|
+
sourceFile.addImportDeclaration({
|
|
1541
|
+
moduleSpecifier: "typeorm-naming-strategies",
|
|
1542
|
+
namedImports: ["SnakeNamingStrategy"],
|
|
1543
|
+
});
|
|
1544
|
+
sourceFile.addImportDeclaration({
|
|
1545
|
+
moduleSpecifier: entityImportPath,
|
|
1546
|
+
namedImports: [entityClassName],
|
|
1547
|
+
});
|
|
1548
|
+
sourceFile.addFunction({
|
|
1549
|
+
name: "getEntitiesFromExports",
|
|
1550
|
+
parameters: [{ name: "exports", type: "Record<string, any>" }],
|
|
1551
|
+
statements: (writer) => {
|
|
1552
|
+
writer.writeLine("const metadataStorage = getMetadataArgsStorage();");
|
|
1553
|
+
writer.writeLine("return Object.values(exports).filter((item) =>");
|
|
1554
|
+
writer.indent(() => {
|
|
1555
|
+
writer.writeLine("metadataStorage.tables.some((table) => table.target === item),");
|
|
1556
|
+
});
|
|
1557
|
+
writer.write(");");
|
|
1558
|
+
},
|
|
1559
|
+
});
|
|
1560
|
+
sourceFile.addVariableStatement({
|
|
1561
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
1562
|
+
declarations: [
|
|
1563
|
+
{
|
|
1564
|
+
name: "coreEntities",
|
|
1565
|
+
initializer: "getEntitiesFromExports(SolidCoreModuleExports)",
|
|
1566
|
+
},
|
|
1567
|
+
],
|
|
1568
|
+
});
|
|
1569
|
+
sourceFile.addVariableStatement({
|
|
1570
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
1571
|
+
declarations: [
|
|
1572
|
+
{
|
|
1573
|
+
name: "dynamicModules",
|
|
1574
|
+
initializer: "getDynamicModuleNames()",
|
|
1575
|
+
},
|
|
1576
|
+
],
|
|
1577
|
+
});
|
|
1578
|
+
sourceFile.addVariableStatement({
|
|
1579
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
1580
|
+
declarations: [
|
|
1581
|
+
{
|
|
1582
|
+
name: "entities",
|
|
1583
|
+
initializer: `[\n ...coreEntities,\n ${entityClassName},\n]`,
|
|
1584
|
+
},
|
|
1585
|
+
],
|
|
1586
|
+
});
|
|
1587
|
+
sourceFile.addVariableStatement({
|
|
1588
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
1589
|
+
declarations: [
|
|
1590
|
+
{
|
|
1591
|
+
name: "logging",
|
|
1592
|
+
initializer: `parseBooleanEnv('${envPrefix}_DATABASE_LOGGING')`,
|
|
1593
|
+
},
|
|
1594
|
+
],
|
|
1595
|
+
});
|
|
1596
|
+
sourceFile.addStatements("// IMPORTANT: synchronize must be false when using migrations");
|
|
1597
|
+
sourceFile.addVariableStatement({
|
|
1598
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
1599
|
+
isExported: true,
|
|
1600
|
+
declarations: [
|
|
1601
|
+
{
|
|
1602
|
+
name: "DefaultDataSource",
|
|
1603
|
+
initializer: (writer) => {
|
|
1604
|
+
writer.write("new DataSource(");
|
|
1605
|
+
writer.inlineBlock(() => {
|
|
1606
|
+
writer.writeLine(`type: '${datasource.type}',`);
|
|
1607
|
+
writer.blankLine();
|
|
1608
|
+
writer.writeLine(`host: process.env.${envPrefix}_DATABASE_HOST,`);
|
|
1609
|
+
writer.writeLine(`port: +(process.env.${envPrefix}_DATABASE_PORT || ${portFallback}),`);
|
|
1610
|
+
writer.writeLine(`username: process.env.${envPrefix}_DATABASE_USER,`);
|
|
1611
|
+
writer.writeLine(`password: process.env.${envPrefix}_DATABASE_PASSWORD,`);
|
|
1612
|
+
writer.writeLine(`database: process.env.${envPrefix}_DATABASE_NAME,`);
|
|
1613
|
+
writer.blankLine();
|
|
1614
|
+
writer.writeLine("entities,");
|
|
1615
|
+
writer.writeLine("migrations: [");
|
|
1616
|
+
writer.indent(() => {
|
|
1617
|
+
writer.writeLine(`${migrationGlob},`);
|
|
1618
|
+
});
|
|
1619
|
+
writer.writeLine("],");
|
|
1620
|
+
writer.writeLine("synchronize: false,");
|
|
1621
|
+
writer.writeLine("logging,");
|
|
1622
|
+
writer.writeLine("namingStrategy: new SnakeNamingStrategy(),");
|
|
1623
|
+
if (datasource.type === "mssql") {
|
|
1624
|
+
writer.writeLine("options: {");
|
|
1625
|
+
writer.indent(() => {
|
|
1626
|
+
writer.writeLine("encrypt: false,");
|
|
1627
|
+
writer.writeLine("trustServerCertificate: true,");
|
|
1628
|
+
});
|
|
1629
|
+
writer.writeLine("},");
|
|
1630
|
+
}
|
|
1631
|
+
});
|
|
1632
|
+
writer.write(")");
|
|
1633
|
+
},
|
|
1634
|
+
},
|
|
1635
|
+
],
|
|
1636
|
+
});
|
|
1637
|
+
sourceFile.organizeImports();
|
|
1638
|
+
sourceFile.formatText();
|
|
1639
|
+
return sourceFile.getFullText();
|
|
1640
|
+
}
|
|
1641
|
+
arrayLiteralContainsValue(arrayLiteral, rawValue) {
|
|
1642
|
+
const normalizedTarget = rawValue.replace(/\s+/g, "");
|
|
1643
|
+
return arrayLiteral.getElements().some((element) => element.getText().replace(/\s+/g, "") === normalizedTarget);
|
|
1644
|
+
}
|
|
1645
|
+
getTypeormDatasourceOptions(sourceFile) {
|
|
1646
|
+
const defaultDataSourceDeclaration = sourceFile.getVariableDeclarationOrThrow("DefaultDataSource");
|
|
1647
|
+
const initializer = defaultDataSourceDeclaration.getInitializer();
|
|
1648
|
+
if (!initializer || !ts_morph_1.Node.isNewExpression(initializer)) {
|
|
1649
|
+
throw new common_1.BadRequestException("Unable to locate DefaultDataSource initializer in datasource file.");
|
|
1650
|
+
}
|
|
1651
|
+
const options = initializer.getArguments()[0];
|
|
1652
|
+
if (!options || !ts_morph_1.Node.isObjectLiteralExpression(options)) {
|
|
1653
|
+
throw new common_1.BadRequestException("Unable to locate DefaultDataSource options object in datasource file.");
|
|
1654
|
+
}
|
|
1655
|
+
return options;
|
|
1656
|
+
}
|
|
1657
|
+
getObjectPropertyArrayLiteral(objectLiteral, propertyName) {
|
|
1658
|
+
const property = objectLiteral.getProperty(propertyName);
|
|
1659
|
+
if (!property || !ts_morph_1.Node.isPropertyAssignment(property)) {
|
|
1660
|
+
throw new common_1.BadRequestException(`Unable to locate "${propertyName}" property in datasource file.`);
|
|
1661
|
+
}
|
|
1662
|
+
const initializer = property.getInitializer();
|
|
1663
|
+
if (!initializer || !ts_morph_1.Node.isArrayLiteralExpression(initializer)) {
|
|
1664
|
+
throw new common_1.BadRequestException(`Unable to locate "${propertyName}" array in datasource file.`);
|
|
1665
|
+
}
|
|
1666
|
+
return initializer;
|
|
1667
|
+
}
|
|
1193
1668
|
buildSynchronizeBlockedMessage(datasource) {
|
|
1194
1669
|
return `Datasource "${datasource.displayName}" has synchronize enabled. Set ${datasource.envPrefix}_DATABASE_SYNCHRONIZE=false before starting legacy table introspection.`;
|
|
1195
1670
|
}
|
|
@@ -1271,6 +1746,8 @@ exports.DatasourceIntrospectionService = DatasourceIntrospectionService = __deco
|
|
|
1271
1746
|
model_metadata_helper_service_1.ModelMetadataHelperService,
|
|
1272
1747
|
field_metadata_service_1.FieldMetadataService,
|
|
1273
1748
|
model_metadata_service_1.ModelMetadataService,
|
|
1749
|
+
module_metadata_service_1.ModuleMetadataService,
|
|
1750
|
+
command_service_1.CommandService,
|
|
1274
1751
|
mssql_datasource_introspection_provider_service_1.MssqlDatasourceIntrospectionProviderService,
|
|
1275
1752
|
mysql_datasource_introspection_provider_service_1.MysqlDatasourceIntrospectionProviderService,
|
|
1276
1753
|
postgres_datasource_introspection_provider_service_1.PostgresDatasourceIntrospectionProviderService])
|