@solidxai/core 0.1.11-beta.1 → 0.1.11-beta.11
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/.claude/settings.local.json +16 -0
- package/CLAUDE.md +26 -0
- 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/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 +62 -2
- package/dist/services/datasource-introspection.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection.service.js +654 -186
- 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/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/1.js +6 -0
- 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 +811 -161
- 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/dist-tests/api/authenticate.spec.js +0 -119
- package/dist-tests/api/authenticate.spec.js.map +0 -1
- package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +0 -97
- package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +0 -1
- package/dist-tests/api/ping.spec.js +0 -21
- package/dist-tests/api/ping.spec.js.map +0 -1
- package/dist-tests/helpers/auth.js +0 -41
- package/dist-tests/helpers/auth.js.map +0 -1
- package/dist-tests/helpers/env.js +0 -11
- package/dist-tests/helpers/env.js.map +0 -1
- package/docs/agent-hub-grooming.md +0 -301
- package/docs/dashboards/AGENTIC_DASHBOARD_IMPLEMENTATION_PLAN.md +0 -438
- package/docs/dashboards/dashboard-curl-smoke-tests.txt +0 -146
- package/docs/dashboards/delete-legacy-dashboard-metadata.sql +0 -172
- package/docs/datasource-introspection-ddl-analysis.md +0 -326
- package/docs/datasource-introspection-implementation-plan.md +0 -306
- package/docs/grouping-enhancements.md +0 -89
- package/docs/java-spring/README.md +0 -3
- package/docs/java-spring/solid-core-module-deep-dive-report.md +0 -1317
- package/docs/module-package-import-handoff.md +0 -691
- package/docs/seed-changes.md +0 -65
- package/docs/test-data-workflow.md +0 -200
- package/docs/type-declaration-import-issue.md +0 -24
- /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);
|
|
@@ -219,6 +377,8 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
219
377
|
if (!tableRecord) {
|
|
220
378
|
throw new common_1.NotFoundException(`Table "${mappingDto.tableName}" was not found on datasource "${datasource.name}".`);
|
|
221
379
|
}
|
|
380
|
+
const tableColumns = inventory.columnsByKey.get(tableKey) ?? [];
|
|
381
|
+
this.validateTablePrimaryKeyForMapping(tableRecord, tableColumns, mappingDto);
|
|
222
382
|
const existingModel = mappingDto.mapped && mappingDto.modelId
|
|
223
383
|
? await this.modelMetadataRepository.findOne({
|
|
224
384
|
where: { id: mappingDto.modelId },
|
|
@@ -233,14 +393,14 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
233
393
|
if (mappingDto.mapped && !existingModel) {
|
|
234
394
|
throw new common_1.NotFoundException(`Mapped model "${mappingDto.modelId}" was not found.`);
|
|
235
395
|
}
|
|
236
|
-
const reviewedModel = this.normalizeReviewedMetadataModel(mappingDto.reviewedModel, mappingDto);
|
|
396
|
+
const reviewedModel = this.normalizeReviewedMetadataModel(mappingDto.reviewedModel, mappingDto, tableColumns);
|
|
237
397
|
const modelPayload = reviewedModel
|
|
238
398
|
? this.buildModelPayloadFromReviewedModel(moduleId, mappingDto, existingModel, reviewedModel)
|
|
239
399
|
: this.buildModelPayload(moduleId, mappingDto, existingModel);
|
|
240
400
|
const metadataJson = reviewedModel
|
|
241
401
|
? await this.buildMetadataJsonPreviewFromReviewedModel(module.name, reviewedModel)
|
|
242
402
|
: await this.buildMetadataJsonPreview(module.name, mappingDto, existingModel);
|
|
243
|
-
const migration = await this.buildMigrationPreview(module.name, datasource, mappingDto,
|
|
403
|
+
const migration = await this.buildMigrationPreview(module.name, datasource, mappingDto, tableColumns);
|
|
244
404
|
const includedColumnNames = mappingDto.columns
|
|
245
405
|
.filter((column) => !column.handledBySuperclass && column.include)
|
|
246
406
|
.map((column) => column.columnName);
|
|
@@ -252,6 +412,13 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
252
412
|
mappedModelId: mappingDto.modelId ?? null,
|
|
253
413
|
metadataJson,
|
|
254
414
|
migration,
|
|
415
|
+
datasourceFile: {
|
|
416
|
+
filePath: path.resolve(process.cwd(), "src", `typeorm-${datasource.name}-datasource.ts`),
|
|
417
|
+
created: false,
|
|
418
|
+
updated: false,
|
|
419
|
+
entityRegistered: false,
|
|
420
|
+
migrationGlobRegistered: false,
|
|
421
|
+
},
|
|
255
422
|
summary: {
|
|
256
423
|
includedColumnNames,
|
|
257
424
|
removedColumnNames,
|
|
@@ -297,6 +464,7 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
297
464
|
};
|
|
298
465
|
}
|
|
299
466
|
buildModelPayloadFromReviewedModel(moduleId, mappingDto, existingModel, reviewedModel) {
|
|
467
|
+
const resolvedUserKeyFieldName = this.resolveReviewedUserKeyFieldName(reviewedModel.fields, reviewedModel.userKeyFieldUserKey, mappingDto.userKeyField);
|
|
300
468
|
const systemFieldPayloads = existingModel
|
|
301
469
|
? existingModel.fields
|
|
302
470
|
.filter((field) => field.isSystem)
|
|
@@ -307,7 +475,7 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
307
475
|
return {
|
|
308
476
|
...field,
|
|
309
477
|
...(matchedExistingField?.id ? { id: matchedExistingField.id } : {}),
|
|
310
|
-
isUserKey: field.name ===
|
|
478
|
+
isUserKey: field.name === resolvedUserKeyFieldName,
|
|
311
479
|
isSystem: field.isSystem ?? false,
|
|
312
480
|
};
|
|
313
481
|
});
|
|
@@ -335,6 +503,7 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
335
503
|
async buildMetadataJsonPreview(moduleName, mappingDto, existingModel) {
|
|
336
504
|
const filePath = await this.moduleMetadataHelperService.getModuleMetadataFilePath(moduleName);
|
|
337
505
|
const previewFields = await this.buildMetadataPreviewFields(mappingDto, existingModel);
|
|
506
|
+
const resolvedUserKeyFieldName = this.resolveReviewedUserKeyFieldName(previewFields, mappingDto.userKeyField, mappingDto.userKeyField) ?? mappingDto.userKeyField;
|
|
338
507
|
return {
|
|
339
508
|
filePath,
|
|
340
509
|
model: {
|
|
@@ -345,7 +514,7 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
345
514
|
dataSource: mappingDto.dataSource,
|
|
346
515
|
dataSourceType: mappingDto.dataSourceType,
|
|
347
516
|
tableName: mappingDto.tableName,
|
|
348
|
-
userKeyFieldUserKey:
|
|
517
|
+
userKeyFieldUserKey: resolvedUserKeyFieldName,
|
|
349
518
|
isChild: false,
|
|
350
519
|
legacyTableType: mappingDto.legacyTableType,
|
|
351
520
|
parentModelUserKey: "",
|
|
@@ -394,10 +563,6 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
394
563
|
}
|
|
395
564
|
const serialized = Array.from(finalFields.values())
|
|
396
565
|
.filter((field) => !field.isSystem)
|
|
397
|
-
.map((field) => ({
|
|
398
|
-
...field,
|
|
399
|
-
isUserKey: field.name === mappingDto.userKeyField,
|
|
400
|
-
}))
|
|
401
566
|
.sort((left, right) => {
|
|
402
567
|
const leftOrder = typeof left.id === "number" ? left.id : Number.MAX_SAFE_INTEGER;
|
|
403
568
|
const rightOrder = typeof right.id === "number" ? right.id : Number.MAX_SAFE_INTEGER;
|
|
@@ -410,6 +575,10 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
410
575
|
for (const field of serialized) {
|
|
411
576
|
previewFields.push(await this.fieldMetadataService.createFieldConfig(field));
|
|
412
577
|
}
|
|
578
|
+
const resolvedUserKeyFieldName = this.resolveReviewedUserKeyFieldName(previewFields, mappingDto.userKeyField, mappingDto.userKeyField);
|
|
579
|
+
for (const field of previewFields) {
|
|
580
|
+
field.isUserKey = field.name === resolvedUserKeyFieldName;
|
|
581
|
+
}
|
|
413
582
|
return previewFields;
|
|
414
583
|
}
|
|
415
584
|
async buildMigrationPreview(moduleName, datasource, mappingDto, tableColumns) {
|
|
@@ -455,7 +624,13 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
455
624
|
}
|
|
456
625
|
return rest;
|
|
457
626
|
}
|
|
458
|
-
|
|
627
|
+
sanitizeConfiguredFieldPayload(fieldConfig) {
|
|
628
|
+
if (!fieldConfig || typeof fieldConfig !== "object" || Array.isArray(fieldConfig)) {
|
|
629
|
+
return {};
|
|
630
|
+
}
|
|
631
|
+
return this.sanitizeExistingFieldPayload(fieldConfig);
|
|
632
|
+
}
|
|
633
|
+
normalizeReviewedMetadataModel(reviewedModel, mappingDto, tableColumns) {
|
|
459
634
|
if (!reviewedModel) {
|
|
460
635
|
return null;
|
|
461
636
|
}
|
|
@@ -504,10 +679,14 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
504
679
|
}
|
|
505
680
|
fieldNames.add(loweredName);
|
|
506
681
|
}
|
|
507
|
-
const
|
|
508
|
-
|
|
682
|
+
const requestedUserKeyFieldUserKey = readString("userKeyFieldUserKey");
|
|
683
|
+
const resolvedUserKeyFieldName = this.resolveReviewedUserKeyFieldName(normalizedFields, requestedUserKeyFieldUserKey, mappingDto.userKeyField);
|
|
684
|
+
const inheritedGeneratedIdUserKey = this.isGeneratedIdInheritedUserKey(mappingDto.legacyTableType, requestedUserKeyFieldUserKey);
|
|
685
|
+
if (!resolvedUserKeyFieldName && !inheritedGeneratedIdUserKey) {
|
|
509
686
|
throw new common_1.BadRequestException("Reviewed metadata JSON must keep the selected userKeyFieldUserKey in the fields array.");
|
|
510
687
|
}
|
|
688
|
+
this.validateReviewedPrimaryKeyFields(normalizedFields, tableColumns);
|
|
689
|
+
const userKeyFieldUserKey = resolvedUserKeyFieldName ?? requestedUserKeyFieldUserKey;
|
|
511
690
|
return {
|
|
512
691
|
singularName: readString("singularName"),
|
|
513
692
|
pluralName: readString("pluralName"),
|
|
@@ -526,10 +705,39 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
526
705
|
internationalisation: readBoolean("internationalisation"),
|
|
527
706
|
fields: normalizedFields.map((field) => ({
|
|
528
707
|
...field,
|
|
529
|
-
isUserKey: field.name ===
|
|
708
|
+
isUserKey: field.name === resolvedUserKeyFieldName,
|
|
530
709
|
})),
|
|
531
710
|
};
|
|
532
711
|
}
|
|
712
|
+
resolveReviewedUserKeyFieldName(reviewedFields, requestedUserKeyFieldName, fallbackUserKeyFieldName) {
|
|
713
|
+
const activeFields = reviewedFields.filter((field) => !field.isMarkedForRemoval);
|
|
714
|
+
const normalizedRequested = `${requestedUserKeyFieldName ?? ""}`.trim().toLowerCase();
|
|
715
|
+
const normalizedFallback = `${fallbackUserKeyFieldName ?? ""}`.trim().toLowerCase();
|
|
716
|
+
const byExactRequested = activeFields.find((field) => field.name?.toLowerCase?.() === normalizedRequested);
|
|
717
|
+
if (byExactRequested) {
|
|
718
|
+
return byExactRequested.name;
|
|
719
|
+
}
|
|
720
|
+
const byExistingUserKeyFlag = activeFields.find((field) => field.isUserKey);
|
|
721
|
+
if (byExistingUserKeyFlag) {
|
|
722
|
+
return byExistingUserKeyFlag.name;
|
|
723
|
+
}
|
|
724
|
+
const byPrimaryKeyFallback = activeFields.find((field) => {
|
|
725
|
+
if (!field.isPrimaryKey) {
|
|
726
|
+
return false;
|
|
727
|
+
}
|
|
728
|
+
const normalizedName = `${field.name ?? ""}`.trim().toLowerCase();
|
|
729
|
+
return normalizedName === normalizedFallback || normalizedName === "id" || normalizedName === "legacyid";
|
|
730
|
+
});
|
|
731
|
+
if (byPrimaryKeyFallback) {
|
|
732
|
+
return byPrimaryKeyFallback.name;
|
|
733
|
+
}
|
|
734
|
+
const byFallbackFieldName = activeFields.find((field) => field.name?.toLowerCase?.() === normalizedFallback);
|
|
735
|
+
return byFallbackFieldName?.name ?? null;
|
|
736
|
+
}
|
|
737
|
+
isGeneratedIdInheritedUserKey(legacyTableType, requestedUserKeyFieldName) {
|
|
738
|
+
return legacyTableType === legacy_table_type_enum_1.LegacyTableType.GENERATED_ID
|
|
739
|
+
&& `${requestedUserKeyFieldName ?? ""}`.trim().toLowerCase() === "id";
|
|
740
|
+
}
|
|
533
741
|
normalizeReviewedField(field, index) {
|
|
534
742
|
if (!field || typeof field !== "object" || Array.isArray(field)) {
|
|
535
743
|
throw new common_1.BadRequestException(`Reviewed metadata JSON field at index ${index} is not a valid object.`);
|
|
@@ -577,32 +785,37 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
577
785
|
}
|
|
578
786
|
buildFieldPayload(column, mappingDto, existingField) {
|
|
579
787
|
const preserved = existingField ? this.sanitizeExistingFieldPayload(existingField) : {};
|
|
788
|
+
const configured = this.sanitizeConfiguredFieldPayload(column.fieldConfig);
|
|
789
|
+
const merged = {
|
|
790
|
+
...preserved,
|
|
791
|
+
...configured,
|
|
792
|
+
};
|
|
580
793
|
const maxLength = column.characterMaximumLength && ["shortText", "longText"].includes(column.solidFieldType)
|
|
581
794
|
? column.characterMaximumLength
|
|
582
|
-
:
|
|
795
|
+
: merged.max;
|
|
583
796
|
return {
|
|
584
|
-
...
|
|
797
|
+
...merged,
|
|
585
798
|
...(column.fieldId ? { id: column.fieldId } : {}),
|
|
586
799
|
name: column.fieldName,
|
|
587
|
-
displayName:
|
|
588
|
-
description:
|
|
800
|
+
displayName: column.displayName?.trim() || merged.displayName || (0, lodash_1.startCase)(column.fieldName),
|
|
801
|
+
description: merged.description ?? "",
|
|
589
802
|
type: column.solidFieldType,
|
|
590
803
|
ormType: column.ormType,
|
|
591
|
-
length:
|
|
592
|
-
defaultValue:
|
|
593
|
-
required:
|
|
594
|
-
unique:
|
|
595
|
-
encrypt:
|
|
596
|
-
index:
|
|
804
|
+
length: merged.length ?? column.characterMaximumLength ?? undefined,
|
|
805
|
+
defaultValue: merged.defaultValue ?? column.defaultValue ?? undefined,
|
|
806
|
+
required: merged.required ?? (!column.nullable || column.isPrimaryKey),
|
|
807
|
+
unique: merged.unique ?? false,
|
|
808
|
+
encrypt: merged.encrypt ?? false,
|
|
809
|
+
index: merged.index ?? false,
|
|
597
810
|
max: maxLength ?? undefined,
|
|
598
|
-
min:
|
|
599
|
-
private:
|
|
600
|
-
isSystem:
|
|
811
|
+
min: merged.min ?? undefined,
|
|
812
|
+
private: merged.private ?? false,
|
|
813
|
+
isSystem: merged.isSystem ?? false,
|
|
601
814
|
isMarkedForRemoval: false,
|
|
602
815
|
columnName: column.columnName,
|
|
603
816
|
isUserKey: mappingDto.userKeyField === column.fieldName,
|
|
604
|
-
enableAuditTracking:
|
|
605
|
-
isMultiSelect:
|
|
817
|
+
enableAuditTracking: merged.enableAuditTracking ?? mappingDto.enableAuditTracking,
|
|
818
|
+
isMultiSelect: merged.isMultiSelect ?? false,
|
|
606
819
|
isPrimaryKey: column.isPrimaryKey,
|
|
607
820
|
};
|
|
608
821
|
}
|
|
@@ -696,23 +909,19 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
696
909
|
const generatedIdUniqueName = this.buildMigrationObjectName("UQ", tableNameLiteral, "ss_id");
|
|
697
910
|
const upColumnStatements = missingSystemColumns.map((definition) => {
|
|
698
911
|
const columnLiteral = this.renderTableColumnLiteral(definition);
|
|
699
|
-
return
|
|
700
|
-
`
|
|
701
|
-
` await queryRunner.addColumn(tableName, new TableColumn(${columnLiteral}));`,
|
|
702
|
-
" }",
|
|
703
|
-
].join("\n");
|
|
912
|
+
return ` if (!(await queryRunner.hasColumn(tableName, ${JSON.stringify(definition.columnName)})))\n`
|
|
913
|
+
+ ` await queryRunner.addColumn(tableName, new TableColumn(${columnLiteral}));`;
|
|
704
914
|
}).join("\n\n");
|
|
705
915
|
const postUpStatements = [];
|
|
706
916
|
if (missingSystemColumns.some((definition) => definition.columnName === "ss_deleted_at")) {
|
|
707
917
|
postUpStatements.push([
|
|
708
918
|
" {",
|
|
709
919
|
" const table = await queryRunner.getTable(tableName);",
|
|
710
|
-
` if (table && !table.indices.some((index) => index.name === ${JSON.stringify(deletedAtIndexName)}))
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
" }",
|
|
920
|
+
` if (table && !table.indices.some((index) => index.name === ${JSON.stringify(deletedAtIndexName)}))`,
|
|
921
|
+
` await queryRunner.createIndex(tableName, new TableIndex(${this.renderTableIndexLiteral({
|
|
922
|
+
name: deletedAtIndexName,
|
|
923
|
+
columnNames: ["ss_deleted_at"],
|
|
924
|
+
})}));`,
|
|
716
925
|
" }",
|
|
717
926
|
].join("\n"));
|
|
718
927
|
}
|
|
@@ -720,13 +929,12 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
720
929
|
postUpStatements.push([
|
|
721
930
|
" {",
|
|
722
931
|
" const table = await queryRunner.getTable(tableName);",
|
|
723
|
-
` if (table && !table.indices.some((index) => index.name === ${JSON.stringify(generatedIdUniqueName)}))
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
" }",
|
|
932
|
+
` if (table && !table.indices.some((index) => index.name === ${JSON.stringify(generatedIdUniqueName)}))`,
|
|
933
|
+
` await queryRunner.createIndex(tableName, new TableIndex(${this.renderTableIndexLiteral({
|
|
934
|
+
name: generatedIdUniqueName,
|
|
935
|
+
columnNames: ["ss_id"],
|
|
936
|
+
isUnique: true,
|
|
937
|
+
})}));`,
|
|
730
938
|
" }",
|
|
731
939
|
].join("\n"));
|
|
732
940
|
}
|
|
@@ -736,9 +944,8 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
736
944
|
" {",
|
|
737
945
|
" const table = await queryRunner.getTable(tableName);",
|
|
738
946
|
` const index = table?.indices.find((entry) => entry.name === ${JSON.stringify(generatedIdUniqueName)});`,
|
|
739
|
-
" if (index)
|
|
947
|
+
" if (index)",
|
|
740
948
|
" await queryRunner.dropIndex(tableName, index);",
|
|
741
|
-
" }",
|
|
742
949
|
" }",
|
|
743
950
|
].join("\n"));
|
|
744
951
|
}
|
|
@@ -747,20 +954,16 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
747
954
|
" {",
|
|
748
955
|
" const table = await queryRunner.getTable(tableName);",
|
|
749
956
|
` const index = table?.indices.find((entry) => entry.name === ${JSON.stringify(deletedAtIndexName)});`,
|
|
750
|
-
" if (index)
|
|
957
|
+
" if (index)",
|
|
751
958
|
" await queryRunner.dropIndex(tableName, index);",
|
|
752
|
-
" }",
|
|
753
959
|
" }",
|
|
754
960
|
].join("\n"));
|
|
755
961
|
}
|
|
756
962
|
downStatements.push(...missingSystemColumns
|
|
757
963
|
.slice()
|
|
758
964
|
.reverse()
|
|
759
|
-
.map((definition) =>
|
|
760
|
-
`
|
|
761
|
-
` await queryRunner.dropColumn(tableName, ${JSON.stringify(definition.columnName)});`,
|
|
762
|
-
" }",
|
|
763
|
-
].join("\n")));
|
|
965
|
+
.map((definition) => ` if (await queryRunner.hasColumn(tableName, ${JSON.stringify(definition.columnName)}))\n`
|
|
966
|
+
+ ` await queryRunner.dropColumn(tableName, ${JSON.stringify(definition.columnName)});`));
|
|
764
967
|
return [
|
|
765
968
|
"import { MigrationInterface, QueryRunner, TableColumn, TableIndex } from \"typeorm\";",
|
|
766
969
|
"",
|
|
@@ -783,27 +986,36 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
783
986
|
}
|
|
784
987
|
renderTableColumnLiteral(definition) {
|
|
785
988
|
const properties = [
|
|
786
|
-
`
|
|
787
|
-
`
|
|
989
|
+
`name: ${JSON.stringify(definition.columnName)}`,
|
|
990
|
+
`type: ${JSON.stringify(definition.type)}`,
|
|
788
991
|
];
|
|
789
992
|
if (definition.length) {
|
|
790
|
-
properties.push(`
|
|
993
|
+
properties.push(`length: ${JSON.stringify(definition.length)}`);
|
|
791
994
|
}
|
|
792
995
|
if (typeof definition.isNullable === "boolean") {
|
|
793
|
-
properties.push(`
|
|
996
|
+
properties.push(`isNullable: ${definition.isNullable}`);
|
|
794
997
|
}
|
|
795
998
|
if (definition.defaultExpression) {
|
|
796
|
-
properties.push(`
|
|
999
|
+
properties.push(`default: ${JSON.stringify(definition.defaultExpression)}`);
|
|
797
1000
|
}
|
|
798
1001
|
if (definition.isGenerated) {
|
|
799
|
-
properties.push("
|
|
800
|
-
properties.push("
|
|
1002
|
+
properties.push("isGenerated: true");
|
|
1003
|
+
properties.push("generationStrategy: \"increment\"");
|
|
801
1004
|
}
|
|
802
1005
|
if (definition.isUnique) {
|
|
803
|
-
properties.push("
|
|
1006
|
+
properties.push("isUnique: true");
|
|
804
1007
|
}
|
|
805
|
-
properties.
|
|
806
|
-
|
|
1008
|
+
return `{ ${properties.join(", ")}, }`;
|
|
1009
|
+
}
|
|
1010
|
+
renderTableIndexLiteral(definition) {
|
|
1011
|
+
const properties = [
|
|
1012
|
+
`name: ${JSON.stringify(definition.name)}`,
|
|
1013
|
+
`columnNames: ${JSON.stringify(definition.columnNames)}`,
|
|
1014
|
+
];
|
|
1015
|
+
if (definition.isUnique) {
|
|
1016
|
+
properties.push("isUnique: true");
|
|
1017
|
+
}
|
|
1018
|
+
return `{ ${properties.join(", ")}, }`;
|
|
807
1019
|
}
|
|
808
1020
|
buildMigrationObjectName(prefix, tableName, columnName) {
|
|
809
1021
|
const hash = (0, crypto_1.createHash)("md5").update(`${tableName}:${columnName}:${prefix}`).digest("hex").slice(0, 24);
|
|
@@ -832,12 +1044,14 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
832
1044
|
async buildTableInventory(datasource) {
|
|
833
1045
|
const provider = this.getProvider(datasource.type);
|
|
834
1046
|
const runtimeDataSource = this.resolveDataSourceByName(datasource.name);
|
|
835
|
-
const [
|
|
1047
|
+
const [rawTables, rawColumns, models] = await Promise.all([
|
|
836
1048
|
provider.listTables(runtimeDataSource),
|
|
837
1049
|
provider.listColumns(runtimeDataSource),
|
|
838
1050
|
this.modelMetadataRepository.find({ relations: { module: true } }),
|
|
839
1051
|
]);
|
|
840
|
-
const
|
|
1052
|
+
const tables = rawTables.filter((table) => !this.isSolidxInternalTableName(table.tableName));
|
|
1053
|
+
const columns = rawColumns.filter((column) => !this.isSolidxInternalTableName(column.tableName));
|
|
1054
|
+
const modelLookup = this.buildModelLookup(models);
|
|
841
1055
|
const columnsByKey = this.groupColumnsByTable(columns);
|
|
842
1056
|
const tableKeys = new Set();
|
|
843
1057
|
for (const table of tables) {
|
|
@@ -857,8 +1071,10 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
857
1071
|
}
|
|
858
1072
|
const suggestedLegacyTableType = this.detectLegacyTableType(datasource.type, tableColumns);
|
|
859
1073
|
const suggestedIdColumn = this.getSuggestedIdColumn(datasource.type, tableColumns, suggestedLegacyTableType);
|
|
860
|
-
const mappedModel =
|
|
1074
|
+
const mappedModel = this.resolveMappedModel(modelLookup, datasource.name, tableName);
|
|
861
1075
|
const physicalColumns = tableColumns.filter((column) => !this.isHandledBySuperclass(column.columnName, suggestedLegacyTableType));
|
|
1076
|
+
const primaryKeyColumns = tableColumns.filter((column) => column.isPrimaryKey);
|
|
1077
|
+
const hasPrimaryKey = primaryKeyColumns.length > 0;
|
|
862
1078
|
return {
|
|
863
1079
|
schema,
|
|
864
1080
|
tableName,
|
|
@@ -875,6 +1091,12 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
875
1091
|
suggestedUserKeyField: this.buildUserKeyCandidates(tableColumns)[0]?.name ?? "id",
|
|
876
1092
|
columnCount: tableColumns.length,
|
|
877
1093
|
physicalColumnCount: physicalColumns.length,
|
|
1094
|
+
hasPrimaryKey,
|
|
1095
|
+
primaryKeyColumnCount: primaryKeyColumns.length,
|
|
1096
|
+
primaryKeyColumnNames: primaryKeyColumns.map((column) => column.columnName),
|
|
1097
|
+
mappingBlockedReason: hasPrimaryKey
|
|
1098
|
+
? null
|
|
1099
|
+
: this.buildMissingPrimaryKeyMessage(tableName),
|
|
878
1100
|
};
|
|
879
1101
|
})
|
|
880
1102
|
.filter((record) => Boolean(record))
|
|
@@ -892,16 +1114,37 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
892
1114
|
columnsByKey,
|
|
893
1115
|
};
|
|
894
1116
|
}
|
|
895
|
-
|
|
896
|
-
const
|
|
1117
|
+
buildModelLookup(models) {
|
|
1118
|
+
const byDatasourceAndTable = new Map();
|
|
1119
|
+
const byTableName = new Map();
|
|
897
1120
|
for (const model of models) {
|
|
898
1121
|
if (!model.tableName) {
|
|
899
1122
|
continue;
|
|
900
1123
|
}
|
|
901
|
-
|
|
902
|
-
|
|
1124
|
+
byDatasourceAndTable.set(this.toModelMapKey(model.dataSource, model.tableName), model);
|
|
1125
|
+
const normalizedTableName = model.tableName.trim().toLowerCase();
|
|
1126
|
+
const bucket = byTableName.get(normalizedTableName);
|
|
1127
|
+
if (bucket) {
|
|
1128
|
+
bucket.push(model);
|
|
1129
|
+
continue;
|
|
1130
|
+
}
|
|
1131
|
+
byTableName.set(normalizedTableName, [model]);
|
|
1132
|
+
}
|
|
1133
|
+
return {
|
|
1134
|
+
byDatasourceAndTable,
|
|
1135
|
+
byTableName,
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
resolveMappedModel(lookup, datasourceName, tableName) {
|
|
1139
|
+
const exactMatch = lookup.byDatasourceAndTable.get(this.toModelMapKey(datasourceName, tableName));
|
|
1140
|
+
if (exactMatch) {
|
|
1141
|
+
return exactMatch;
|
|
903
1142
|
}
|
|
904
|
-
|
|
1143
|
+
const fallbackMatches = lookup.byTableName.get(tableName.trim().toLowerCase()) ?? [];
|
|
1144
|
+
if (fallbackMatches.length === 1) {
|
|
1145
|
+
return fallbackMatches[0];
|
|
1146
|
+
}
|
|
1147
|
+
return fallbackMatches[0] ?? null;
|
|
905
1148
|
}
|
|
906
1149
|
groupColumnsByTable(columns) {
|
|
907
1150
|
const grouped = new Map();
|
|
@@ -916,6 +1159,9 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
916
1159
|
}
|
|
917
1160
|
return grouped;
|
|
918
1161
|
}
|
|
1162
|
+
isSolidxInternalTableName(tableName) {
|
|
1163
|
+
return typeof tableName === "string" && tableName.toLowerCase().startsWith("ss_");
|
|
1164
|
+
}
|
|
919
1165
|
detectLegacyTableType(datasourceType, columns) {
|
|
920
1166
|
const normalizedDatasourceType = datasourceType.toLowerCase();
|
|
921
1167
|
const hasExistingGeneratedId = columns.some((column) => column.isAutoGenerated);
|
|
@@ -983,12 +1229,16 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
983
1229
|
}
|
|
984
1230
|
toDetailColumnRecord(column, legacyTableType, datasourceType) {
|
|
985
1231
|
const superclassFieldName = this.getSuperclassFieldName(column.columnName, legacyTableType);
|
|
1232
|
+
const normalizedColumnName = column.columnName.toLowerCase();
|
|
986
1233
|
const fieldName = legacyTableType === legacy_table_type_enum_1.LegacyTableType.EXISTING_ID && column.isAutoGenerated
|
|
987
1234
|
? "id"
|
|
988
|
-
:
|
|
1235
|
+
: legacyTableType === legacy_table_type_enum_1.LegacyTableType.GENERATED_ID && normalizedColumnName === "id"
|
|
1236
|
+
? "legacyId"
|
|
1237
|
+
: this.toFieldName(column.columnName);
|
|
989
1238
|
return {
|
|
990
1239
|
columnName: column.columnName,
|
|
991
1240
|
fieldName,
|
|
1241
|
+
displayName: (0, lodash_1.startCase)(fieldName),
|
|
992
1242
|
dataType: column.dataType,
|
|
993
1243
|
solidFieldType: this.toSolidFieldType(column),
|
|
994
1244
|
ormType: this.toOrmFieldType(column, datasourceType),
|
|
@@ -999,10 +1249,49 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
999
1249
|
numericScale: column.numericScale,
|
|
1000
1250
|
ordinalPosition: column.ordinalPosition,
|
|
1001
1251
|
isAutoGenerated: column.isAutoGenerated,
|
|
1252
|
+
isPrimaryKey: column.isPrimaryKey,
|
|
1002
1253
|
handledBySuperclass: Boolean(superclassFieldName),
|
|
1003
1254
|
superclassFieldName,
|
|
1004
1255
|
};
|
|
1005
1256
|
}
|
|
1257
|
+
validateTablePrimaryKeyForMapping(tableRecord, tableColumns, mappingDto) {
|
|
1258
|
+
const detectedPrimaryKeyColumns = tableColumns.filter((column) => column.isPrimaryKey);
|
|
1259
|
+
if (!detectedPrimaryKeyColumns.length) {
|
|
1260
|
+
throw new common_1.BadRequestException(tableRecord.mappingBlockedReason ?? this.buildMissingPrimaryKeyMessage(tableRecord.tableName));
|
|
1261
|
+
}
|
|
1262
|
+
const requestColumnsByName = new Map(mappingDto.columns.map((column) => [column.columnName.trim().toLowerCase(), column]));
|
|
1263
|
+
for (const primaryKeyColumn of detectedPrimaryKeyColumns) {
|
|
1264
|
+
const matchingRequestColumn = requestColumnsByName.get(primaryKeyColumn.columnName.trim().toLowerCase());
|
|
1265
|
+
if (!matchingRequestColumn) {
|
|
1266
|
+
throw new common_1.BadRequestException(`Primary key column "${primaryKeyColumn.columnName}" must remain present in the mapping request for table "${tableRecord.tableName}".`);
|
|
1267
|
+
}
|
|
1268
|
+
if (!matchingRequestColumn.include) {
|
|
1269
|
+
throw new common_1.BadRequestException(`Primary key column "${primaryKeyColumn.columnName}" cannot be excluded from the mapping for table "${tableRecord.tableName}".`);
|
|
1270
|
+
}
|
|
1271
|
+
if (!matchingRequestColumn.isPrimaryKey) {
|
|
1272
|
+
throw new common_1.BadRequestException(`Primary key column "${primaryKeyColumn.columnName}" must stay marked as isPrimaryKey for table "${tableRecord.tableName}".`);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
validateReviewedPrimaryKeyFields(reviewedFields, tableColumns) {
|
|
1277
|
+
const detectedPrimaryKeyColumns = tableColumns.filter((column) => column.isPrimaryKey);
|
|
1278
|
+
if (!detectedPrimaryKeyColumns.length) {
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
const activeReviewedFields = reviewedFields.filter((field) => !field.isMarkedForRemoval);
|
|
1282
|
+
for (const primaryKeyColumn of detectedPrimaryKeyColumns) {
|
|
1283
|
+
const matchingReviewedField = activeReviewedFields.find((field) => `${field.columnName ?? ""}`.trim().toLowerCase() === primaryKeyColumn.columnName.trim().toLowerCase());
|
|
1284
|
+
if (!matchingReviewedField) {
|
|
1285
|
+
throw new common_1.BadRequestException(`Reviewed metadata JSON must keep datasource primary key column "${primaryKeyColumn.columnName}" in the fields array.`);
|
|
1286
|
+
}
|
|
1287
|
+
if (!matchingReviewedField.isPrimaryKey) {
|
|
1288
|
+
throw new common_1.BadRequestException(`Reviewed metadata JSON must keep datasource primary key column "${primaryKeyColumn.columnName}" marked with isPrimaryKey: true.`);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
buildMissingPrimaryKeyMessage(tableName) {
|
|
1293
|
+
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.`;
|
|
1294
|
+
}
|
|
1006
1295
|
getSuperclassFieldName(columnName, legacyTableType) {
|
|
1007
1296
|
return this.getLegacySuperclassFieldName(columnName, legacyTableType);
|
|
1008
1297
|
}
|
|
@@ -1033,127 +1322,70 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
1033
1322
|
}
|
|
1034
1323
|
return "LegacyCommonEntityWithExistingId";
|
|
1035
1324
|
}
|
|
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";
|
|
1325
|
+
getDatasourceTypeFamily(dataType) {
|
|
1326
|
+
const normalized = dataType.toLowerCase();
|
|
1327
|
+
for (const [family, aliases] of Object.entries(DATASOURCE_TYPE_FAMILY_ALIASES)) {
|
|
1328
|
+
if (aliases.includes(normalized)) {
|
|
1329
|
+
return family;
|
|
1330
|
+
}
|
|
1059
1331
|
}
|
|
1060
|
-
if (
|
|
1332
|
+
if (normalized.includes("json")) {
|
|
1061
1333
|
return "json";
|
|
1062
1334
|
}
|
|
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";
|
|
1335
|
+
if (normalized.includes("date")) {
|
|
1336
|
+
return "datetime";
|
|
1074
1337
|
}
|
|
1075
|
-
return
|
|
1338
|
+
return null;
|
|
1076
1339
|
}
|
|
1077
|
-
|
|
1078
|
-
const
|
|
1079
|
-
const
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
return "
|
|
1083
|
-
|
|
1340
|
+
toSolidFieldType(column) {
|
|
1341
|
+
const family = this.getDatasourceTypeFamily(column.dataType);
|
|
1342
|
+
const charLength = column.characterMaximumLength ?? null;
|
|
1343
|
+
switch (family) {
|
|
1344
|
+
case "boolean":
|
|
1345
|
+
return "boolean";
|
|
1346
|
+
case "bigint":
|
|
1084
1347
|
return "bigint";
|
|
1085
|
-
|
|
1086
|
-
return "int";
|
|
1087
|
-
if (["numeric"].includes(normalized))
|
|
1088
|
-
return "numeric";
|
|
1089
|
-
if (["decimal", "float", "double", "real", "money", "smallmoney"].includes(normalized))
|
|
1090
|
-
return "decimal";
|
|
1091
|
-
if (["date"].includes(normalized))
|
|
1092
|
-
return "date";
|
|
1093
|
-
if (["time"].includes(normalized))
|
|
1094
|
-
return "time";
|
|
1095
|
-
if (["datetime2"].includes(normalized))
|
|
1096
|
-
return "datetime2";
|
|
1097
|
-
if (["datetime", "smalldatetime", "timestamp"].includes(normalized) || normalized.includes("date"))
|
|
1098
|
-
return "datetime";
|
|
1099
|
-
if (["json", "jsonb", "simple-json", "simplejson"].includes(normalized) || normalized.includes("json"))
|
|
1100
|
-
return "simple-json";
|
|
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))
|
|
1348
|
+
case "int":
|
|
1111
1349
|
return "int";
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1350
|
+
case "numeric":
|
|
1351
|
+
case "decimal":
|
|
1352
|
+
case "float":
|
|
1115
1353
|
return "decimal";
|
|
1116
|
-
|
|
1117
|
-
return "double";
|
|
1118
|
-
if (["bit", "bool", "boolean"].includes(normalized))
|
|
1119
|
-
return "boolean";
|
|
1120
|
-
if (["date"].includes(normalized))
|
|
1354
|
+
case "date":
|
|
1121
1355
|
return "date";
|
|
1122
|
-
|
|
1356
|
+
case "time":
|
|
1123
1357
|
return "time";
|
|
1124
|
-
|
|
1358
|
+
case "datetime":
|
|
1359
|
+
case "datetimeWithTimezone":
|
|
1125
1360
|
return "datetime";
|
|
1126
|
-
|
|
1361
|
+
case "json":
|
|
1362
|
+
case "jsonb":
|
|
1127
1363
|
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";
|
|
1364
|
+
case "uuid":
|
|
1365
|
+
return "uuid";
|
|
1366
|
+
case "mediumText":
|
|
1367
|
+
case "longText":
|
|
1368
|
+
return "longText";
|
|
1369
|
+
case "shortText":
|
|
1370
|
+
case "unicodeShortText":
|
|
1371
|
+
if (charLength !== null && charLength > 255) {
|
|
1372
|
+
return "longText";
|
|
1373
|
+
}
|
|
1374
|
+
return "shortText";
|
|
1375
|
+
default:
|
|
1376
|
+
return "shortText";
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
toOrmFieldType(column, datasourceType) {
|
|
1380
|
+
const family = this.getDatasourceTypeFamily(column.dataType);
|
|
1381
|
+
const normalizedDatasourceType = datasourceType.toLowerCase();
|
|
1382
|
+
const ormType = family
|
|
1383
|
+
? ORM_TYPE_BY_DATASOURCE_FAMILY[normalizedDatasourceType]?.[family]
|
|
1384
|
+
: undefined;
|
|
1385
|
+
if (ormType) {
|
|
1386
|
+
return ormType;
|
|
1387
|
+
}
|
|
1388
|
+
return normalizedDatasourceType === "postgres" ? "varchar" : "varchar";
|
|
1157
1389
|
}
|
|
1158
1390
|
toFieldName(columnName) {
|
|
1159
1391
|
return (0, lodash_1.camelCase)(columnName.replace(/^ss_/, ""));
|
|
@@ -1190,6 +1422,240 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
|
|
|
1190
1422
|
toModelMapKey(datasourceName, tableName) {
|
|
1191
1423
|
return `${datasourceName.toLowerCase()}::${tableName.toLowerCase()}`;
|
|
1192
1424
|
}
|
|
1425
|
+
async syncTypeormDatasourceFile(moduleName, datasource, singularName) {
|
|
1426
|
+
const datasourceFilePath = path.resolve(process.cwd(), "src", `typeorm-${datasource.name}-datasource.ts`);
|
|
1427
|
+
const entityClassName = (0, string_helper_1.classify)(singularName);
|
|
1428
|
+
const entityImportPath = `./${moduleName}/entities/${(0, lodash_1.kebabCase)(singularName)}.entity`;
|
|
1429
|
+
const migrationGlob = `join(__dirname, './${moduleName}/migrations/${datasource.name}/*.{ts,js}')`;
|
|
1430
|
+
const existingContent = await fs.readFile(datasourceFilePath, "utf8").catch(() => null);
|
|
1431
|
+
if (!existingContent) {
|
|
1432
|
+
const rendered = this.renderTypeormDatasourceTemplate({
|
|
1433
|
+
datasource,
|
|
1434
|
+
entityClassName,
|
|
1435
|
+
entityImportPath,
|
|
1436
|
+
migrationGlob,
|
|
1437
|
+
});
|
|
1438
|
+
await fs.writeFile(datasourceFilePath, rendered, "utf8");
|
|
1439
|
+
return {
|
|
1440
|
+
filePath: datasourceFilePath,
|
|
1441
|
+
created: true,
|
|
1442
|
+
updated: false,
|
|
1443
|
+
entityRegistered: true,
|
|
1444
|
+
migrationGlobRegistered: true,
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
const project = new ts_morph_1.Project({
|
|
1448
|
+
skipAddingFilesFromTsConfig: true,
|
|
1449
|
+
manipulationSettings: {
|
|
1450
|
+
quoteKind: ts_morph_1.QuoteKind.Single,
|
|
1451
|
+
indentationText: ts_morph_1.IndentationText.FourSpaces,
|
|
1452
|
+
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
|
|
1453
|
+
},
|
|
1454
|
+
});
|
|
1455
|
+
const sourceFile = project.createSourceFile(datasourceFilePath, existingContent, { overwrite: true });
|
|
1456
|
+
const entityImportDeclaration = sourceFile.getImportDeclarations().find((declaration) => declaration.getModuleSpecifierValue() === entityImportPath);
|
|
1457
|
+
if (entityImportDeclaration) {
|
|
1458
|
+
const alreadyImported = entityImportDeclaration.getNamedImports().some((namedImport) => namedImport.getName() === entityClassName);
|
|
1459
|
+
if (!alreadyImported) {
|
|
1460
|
+
entityImportDeclaration.addNamedImport(entityClassName);
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
else {
|
|
1464
|
+
sourceFile.addImportDeclaration({
|
|
1465
|
+
moduleSpecifier: entityImportPath,
|
|
1466
|
+
namedImports: [entityClassName],
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
const entitiesArray = sourceFile
|
|
1470
|
+
.getVariableDeclarationOrThrow("entities")
|
|
1471
|
+
.getInitializerIfKindOrThrow(ts_morph_1.SyntaxKind.ArrayLiteralExpression);
|
|
1472
|
+
const entityAlreadyRegistered = this.arrayLiteralContainsValue(entitiesArray, entityClassName);
|
|
1473
|
+
if (!entityAlreadyRegistered) {
|
|
1474
|
+
entitiesArray.addElement(entityClassName);
|
|
1475
|
+
}
|
|
1476
|
+
const dataSourceOptions = this.getTypeormDatasourceOptions(sourceFile);
|
|
1477
|
+
const migrationsArray = this.getObjectPropertyArrayLiteral(dataSourceOptions, "migrations");
|
|
1478
|
+
const migrationAlreadyRegistered = this.arrayLiteralContainsValue(migrationsArray, migrationGlob);
|
|
1479
|
+
if (!migrationAlreadyRegistered) {
|
|
1480
|
+
migrationsArray.addElement(migrationGlob);
|
|
1481
|
+
}
|
|
1482
|
+
sourceFile.organizeImports();
|
|
1483
|
+
sourceFile.formatText();
|
|
1484
|
+
const nextContent = sourceFile.getFullText();
|
|
1485
|
+
const updated = nextContent !== existingContent;
|
|
1486
|
+
if (updated) {
|
|
1487
|
+
await fs.writeFile(datasourceFilePath, nextContent, "utf8");
|
|
1488
|
+
}
|
|
1489
|
+
return {
|
|
1490
|
+
filePath: datasourceFilePath,
|
|
1491
|
+
created: false,
|
|
1492
|
+
updated,
|
|
1493
|
+
entityRegistered: true,
|
|
1494
|
+
migrationGlobRegistered: true,
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1497
|
+
renderTypeormDatasourceTemplate(input) {
|
|
1498
|
+
const { datasource, entityClassName, entityImportPath, migrationGlob } = input;
|
|
1499
|
+
const envPrefix = datasource.envPrefix?.toUpperCase?.() || "DEFAULT";
|
|
1500
|
+
const portFallback = datasource.type === "postgres" ? 5432 : datasource.type === "mysql" ? 3306 : 1433;
|
|
1501
|
+
const project = new ts_morph_1.Project({
|
|
1502
|
+
skipAddingFilesFromTsConfig: true,
|
|
1503
|
+
manipulationSettings: {
|
|
1504
|
+
quoteKind: ts_morph_1.QuoteKind.Single,
|
|
1505
|
+
indentationText: ts_morph_1.IndentationText.FourSpaces,
|
|
1506
|
+
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
|
|
1507
|
+
},
|
|
1508
|
+
});
|
|
1509
|
+
const sourceFile = project.createSourceFile("typeorm-template.ts", "", { overwrite: true });
|
|
1510
|
+
sourceFile.addImportDeclaration({ moduleSpecifier: "reflect-metadata" });
|
|
1511
|
+
sourceFile.addImportDeclaration({ moduleSpecifier: "path", namedImports: ["join"] });
|
|
1512
|
+
sourceFile.addImportDeclaration({
|
|
1513
|
+
moduleSpecifier: "dotenv",
|
|
1514
|
+
namedImports: [{ name: "config", alias: "dotenvConfig" }],
|
|
1515
|
+
});
|
|
1516
|
+
sourceFile.addStatements("dotenvConfig({ path: join(__dirname, '../.env') });");
|
|
1517
|
+
sourceFile.addStatements("");
|
|
1518
|
+
sourceFile.addImportDeclaration({ moduleSpecifier: "typeorm", namedImports: ["DataSource"] });
|
|
1519
|
+
sourceFile.addImportDeclaration({
|
|
1520
|
+
moduleSpecifier: "@solidxai/core",
|
|
1521
|
+
namespaceImport: "SolidCoreModuleExports",
|
|
1522
|
+
});
|
|
1523
|
+
sourceFile.addImportDeclaration({
|
|
1524
|
+
moduleSpecifier: "@solidxai/core",
|
|
1525
|
+
namedImports: ["getDynamicModuleNames", "parseBooleanEnv"],
|
|
1526
|
+
});
|
|
1527
|
+
sourceFile.addImportDeclaration({
|
|
1528
|
+
moduleSpecifier: "typeorm",
|
|
1529
|
+
namedImports: ["getMetadataArgsStorage"],
|
|
1530
|
+
});
|
|
1531
|
+
sourceFile.addImportDeclaration({
|
|
1532
|
+
moduleSpecifier: "typeorm-naming-strategies",
|
|
1533
|
+
namedImports: ["SnakeNamingStrategy"],
|
|
1534
|
+
});
|
|
1535
|
+
sourceFile.addImportDeclaration({
|
|
1536
|
+
moduleSpecifier: entityImportPath,
|
|
1537
|
+
namedImports: [entityClassName],
|
|
1538
|
+
});
|
|
1539
|
+
sourceFile.addFunction({
|
|
1540
|
+
name: "getEntitiesFromExports",
|
|
1541
|
+
parameters: [{ name: "exports", type: "Record<string, any>" }],
|
|
1542
|
+
statements: (writer) => {
|
|
1543
|
+
writer.writeLine("const metadataStorage = getMetadataArgsStorage();");
|
|
1544
|
+
writer.writeLine("return Object.values(exports).filter((item) =>");
|
|
1545
|
+
writer.indent(() => {
|
|
1546
|
+
writer.writeLine("metadataStorage.tables.some((table) => table.target === item),");
|
|
1547
|
+
});
|
|
1548
|
+
writer.write(");");
|
|
1549
|
+
},
|
|
1550
|
+
});
|
|
1551
|
+
sourceFile.addVariableStatement({
|
|
1552
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
1553
|
+
declarations: [
|
|
1554
|
+
{
|
|
1555
|
+
name: "coreEntities",
|
|
1556
|
+
initializer: "getEntitiesFromExports(SolidCoreModuleExports)",
|
|
1557
|
+
},
|
|
1558
|
+
],
|
|
1559
|
+
});
|
|
1560
|
+
sourceFile.addVariableStatement({
|
|
1561
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
1562
|
+
declarations: [
|
|
1563
|
+
{
|
|
1564
|
+
name: "dynamicModules",
|
|
1565
|
+
initializer: "getDynamicModuleNames()",
|
|
1566
|
+
},
|
|
1567
|
+
],
|
|
1568
|
+
});
|
|
1569
|
+
sourceFile.addVariableStatement({
|
|
1570
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
1571
|
+
declarations: [
|
|
1572
|
+
{
|
|
1573
|
+
name: "entities",
|
|
1574
|
+
initializer: `[\n ...coreEntities,\n ${entityClassName},\n]`,
|
|
1575
|
+
},
|
|
1576
|
+
],
|
|
1577
|
+
});
|
|
1578
|
+
sourceFile.addVariableStatement({
|
|
1579
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
1580
|
+
declarations: [
|
|
1581
|
+
{
|
|
1582
|
+
name: "logging",
|
|
1583
|
+
initializer: `parseBooleanEnv('${envPrefix}_DATABASE_LOGGING')`,
|
|
1584
|
+
},
|
|
1585
|
+
],
|
|
1586
|
+
});
|
|
1587
|
+
sourceFile.addStatements("// IMPORTANT: synchronize must be false when using migrations");
|
|
1588
|
+
sourceFile.addVariableStatement({
|
|
1589
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
1590
|
+
isExported: true,
|
|
1591
|
+
declarations: [
|
|
1592
|
+
{
|
|
1593
|
+
name: "DefaultDataSource",
|
|
1594
|
+
initializer: (writer) => {
|
|
1595
|
+
writer.write("new DataSource(");
|
|
1596
|
+
writer.inlineBlock(() => {
|
|
1597
|
+
writer.writeLine(`type: '${datasource.type}',`);
|
|
1598
|
+
writer.blankLine();
|
|
1599
|
+
writer.writeLine(`host: process.env.${envPrefix}_DATABASE_HOST,`);
|
|
1600
|
+
writer.writeLine(`port: +(process.env.${envPrefix}_DATABASE_PORT || ${portFallback}),`);
|
|
1601
|
+
writer.writeLine(`username: process.env.${envPrefix}_DATABASE_USER,`);
|
|
1602
|
+
writer.writeLine(`password: process.env.${envPrefix}_DATABASE_PASSWORD,`);
|
|
1603
|
+
writer.writeLine(`database: process.env.${envPrefix}_DATABASE_NAME,`);
|
|
1604
|
+
writer.blankLine();
|
|
1605
|
+
writer.writeLine("entities,");
|
|
1606
|
+
writer.writeLine("migrations: [");
|
|
1607
|
+
writer.indent(() => {
|
|
1608
|
+
writer.writeLine(`${migrationGlob},`);
|
|
1609
|
+
});
|
|
1610
|
+
writer.writeLine("],");
|
|
1611
|
+
writer.writeLine("synchronize: false,");
|
|
1612
|
+
writer.writeLine("logging,");
|
|
1613
|
+
writer.writeLine("namingStrategy: new SnakeNamingStrategy(),");
|
|
1614
|
+
if (datasource.type === "mssql") {
|
|
1615
|
+
writer.writeLine("options: {");
|
|
1616
|
+
writer.indent(() => {
|
|
1617
|
+
writer.writeLine("encrypt: false,");
|
|
1618
|
+
writer.writeLine("trustServerCertificate: true,");
|
|
1619
|
+
});
|
|
1620
|
+
writer.writeLine("},");
|
|
1621
|
+
}
|
|
1622
|
+
});
|
|
1623
|
+
writer.write(")");
|
|
1624
|
+
},
|
|
1625
|
+
},
|
|
1626
|
+
],
|
|
1627
|
+
});
|
|
1628
|
+
sourceFile.organizeImports();
|
|
1629
|
+
sourceFile.formatText();
|
|
1630
|
+
return sourceFile.getFullText();
|
|
1631
|
+
}
|
|
1632
|
+
arrayLiteralContainsValue(arrayLiteral, rawValue) {
|
|
1633
|
+
const normalizedTarget = rawValue.replace(/\s+/g, "");
|
|
1634
|
+
return arrayLiteral.getElements().some((element) => element.getText().replace(/\s+/g, "") === normalizedTarget);
|
|
1635
|
+
}
|
|
1636
|
+
getTypeormDatasourceOptions(sourceFile) {
|
|
1637
|
+
const defaultDataSourceDeclaration = sourceFile.getVariableDeclarationOrThrow("DefaultDataSource");
|
|
1638
|
+
const initializer = defaultDataSourceDeclaration.getInitializer();
|
|
1639
|
+
if (!initializer || !ts_morph_1.Node.isNewExpression(initializer)) {
|
|
1640
|
+
throw new common_1.BadRequestException("Unable to locate DefaultDataSource initializer in datasource file.");
|
|
1641
|
+
}
|
|
1642
|
+
const options = initializer.getArguments()[0];
|
|
1643
|
+
if (!options || !ts_morph_1.Node.isObjectLiteralExpression(options)) {
|
|
1644
|
+
throw new common_1.BadRequestException("Unable to locate DefaultDataSource options object in datasource file.");
|
|
1645
|
+
}
|
|
1646
|
+
return options;
|
|
1647
|
+
}
|
|
1648
|
+
getObjectPropertyArrayLiteral(objectLiteral, propertyName) {
|
|
1649
|
+
const property = objectLiteral.getProperty(propertyName);
|
|
1650
|
+
if (!property || !ts_morph_1.Node.isPropertyAssignment(property)) {
|
|
1651
|
+
throw new common_1.BadRequestException(`Unable to locate "${propertyName}" property in datasource file.`);
|
|
1652
|
+
}
|
|
1653
|
+
const initializer = property.getInitializer();
|
|
1654
|
+
if (!initializer || !ts_morph_1.Node.isArrayLiteralExpression(initializer)) {
|
|
1655
|
+
throw new common_1.BadRequestException(`Unable to locate "${propertyName}" array in datasource file.`);
|
|
1656
|
+
}
|
|
1657
|
+
return initializer;
|
|
1658
|
+
}
|
|
1193
1659
|
buildSynchronizeBlockedMessage(datasource) {
|
|
1194
1660
|
return `Datasource "${datasource.displayName}" has synchronize enabled. Set ${datasource.envPrefix}_DATABASE_SYNCHRONIZE=false before starting legacy table introspection.`;
|
|
1195
1661
|
}
|
|
@@ -1271,6 +1737,8 @@ exports.DatasourceIntrospectionService = DatasourceIntrospectionService = __deco
|
|
|
1271
1737
|
model_metadata_helper_service_1.ModelMetadataHelperService,
|
|
1272
1738
|
field_metadata_service_1.FieldMetadataService,
|
|
1273
1739
|
model_metadata_service_1.ModelMetadataService,
|
|
1740
|
+
module_metadata_service_1.ModuleMetadataService,
|
|
1741
|
+
command_service_1.CommandService,
|
|
1274
1742
|
mssql_datasource_introspection_provider_service_1.MssqlDatasourceIntrospectionProviderService,
|
|
1275
1743
|
mysql_datasource_introspection_provider_service_1.MysqlDatasourceIntrospectionProviderService,
|
|
1276
1744
|
postgres_datasource_introspection_provider_service_1.PostgresDatasourceIntrospectionProviderService])
|