@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.
Files changed (244) hide show
  1. package/.claude/settings.local.json +16 -0
  2. package/CLAUDE.md +26 -0
  3. package/dist/commands/migrate-removed-fields.command.d.ts +19 -0
  4. package/dist/commands/migrate-removed-fields.command.d.ts.map +1 -0
  5. package/dist/commands/migrate-removed-fields.command.js +77 -0
  6. package/dist/commands/migrate-removed-fields.command.js.map +1 -0
  7. package/dist/controllers/datasource-introspection.controller.d.ts +46 -1
  8. package/dist/controllers/datasource-introspection.controller.d.ts.map +1 -1
  9. package/dist/controllers/datasource-introspection.controller.js +38 -0
  10. package/dist/controllers/datasource-introspection.controller.js.map +1 -1
  11. package/dist/controllers/service.controller.d.ts +1 -0
  12. package/dist/controllers/service.controller.d.ts.map +1 -1
  13. package/dist/controllers/service.controller.js +2 -2
  14. package/dist/controllers/service.controller.js.map +1 -1
  15. package/dist/dtos/datasource-introspection-mapping.dto.d.ts +5 -0
  16. package/dist/dtos/datasource-introspection-mapping.dto.d.ts.map +1 -1
  17. package/dist/dtos/datasource-introspection-mapping.dto.js +24 -2
  18. package/dist/dtos/datasource-introspection-mapping.dto.js.map +1 -1
  19. package/dist/entities/{legacy-common-with-id.entity.d.ts → legacy-common-with-generated-id.entity.d.ts} +2 -2
  20. package/dist/entities/legacy-common-with-generated-id.entity.d.ts.map +1 -0
  21. package/dist/entities/{legacy-common-with-id.entity.js → legacy-common-with-generated-id.entity.js} +4 -4
  22. package/dist/entities/legacy-common-with-generated-id.entity.js.map +1 -0
  23. package/dist/entities/{legacy-common.entity.d.ts → legacy-common.entity-with-existing-id.entity.d.ts} +1 -1
  24. package/dist/entities/legacy-common.entity-with-existing-id.entity.d.ts.map +1 -0
  25. package/dist/entities/{legacy-common.entity.js → legacy-common.entity-with-existing-id.entity.js} +1 -1
  26. package/dist/entities/legacy-common.entity-with-existing-id.entity.js.map +1 -0
  27. package/dist/helpers/command.service.d.ts +2 -0
  28. package/dist/helpers/command.service.d.ts.map +1 -1
  29. package/dist/helpers/command.service.js +6 -2
  30. package/dist/helpers/command.service.js.map +1 -1
  31. package/dist/helpers/environment.helper.d.ts +1 -0
  32. package/dist/helpers/environment.helper.d.ts.map +1 -1
  33. package/dist/helpers/environment.helper.js +4 -0
  34. package/dist/helpers/environment.helper.js.map +1 -1
  35. package/dist/helpers/field-crud-managers/IntFieldCrudManager.d.ts.map +1 -1
  36. package/dist/helpers/field-crud-managers/IntFieldCrudManager.js +4 -1
  37. package/dist/helpers/field-crud-managers/IntFieldCrudManager.js.map +1 -1
  38. package/dist/helpers/model-metadata-helper.service.d.ts +2 -1
  39. package/dist/helpers/model-metadata-helper.service.d.ts.map +1 -1
  40. package/dist/helpers/model-metadata-helper.service.js +9 -5
  41. package/dist/helpers/model-metadata-helper.service.js.map +1 -1
  42. package/dist/helpers/nodemon-heartbeat.d.ts +5 -0
  43. package/dist/helpers/nodemon-heartbeat.d.ts.map +1 -0
  44. package/dist/helpers/nodemon-heartbeat.js +67 -0
  45. package/dist/helpers/nodemon-heartbeat.js.map +1 -0
  46. package/dist/helpers/solid-registry.d.ts +2 -1
  47. package/dist/helpers/solid-registry.d.ts.map +1 -1
  48. package/dist/helpers/solid-registry.js.map +1 -1
  49. package/dist/helpers/typeorm-db-helper.d.ts +1 -0
  50. package/dist/helpers/typeorm-db-helper.d.ts.map +1 -1
  51. package/dist/helpers/typeorm-db-helper.js +12 -0
  52. package/dist/helpers/typeorm-db-helper.js.map +1 -1
  53. package/dist/index.d.ts +3 -2
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +3 -2
  56. package/dist/index.js.map +1 -1
  57. package/dist/interfaces/datasource-introspection-provider.interface.d.ts +1 -0
  58. package/dist/interfaces/datasource-introspection-provider.interface.d.ts.map +1 -1
  59. package/dist/interfaces/datasource-introspection-provider.interface.js.map +1 -1
  60. package/dist/interfaces.d.ts +1 -0
  61. package/dist/interfaces.d.ts.map +1 -1
  62. package/dist/interfaces.js.map +1 -1
  63. package/dist/repository/scheduled-job.repository.d.ts +2 -2
  64. package/dist/repository/scheduled-job.repository.d.ts.map +1 -1
  65. package/dist/repository/scheduled-job.repository.js +6 -3
  66. package/dist/repository/scheduled-job.repository.js.map +1 -1
  67. package/dist/repository/security-rule.repository.d.ts +2 -2
  68. package/dist/repository/security-rule.repository.d.ts.map +1 -1
  69. package/dist/repository/security-rule.repository.js +6 -3
  70. package/dist/repository/security-rule.repository.js.map +1 -1
  71. package/dist/seeders/module-metadata-seeder.service.d.ts +11 -1
  72. package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
  73. package/dist/seeders/module-metadata-seeder.service.js +163 -3
  74. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  75. package/dist/seeders/seed-data/solid-core-metadata.json +128 -34
  76. package/dist/services/api-key.service.d.ts.map +1 -1
  77. package/dist/services/api-key.service.js.map +1 -1
  78. package/dist/services/authentication.service.d.ts +2 -0
  79. package/dist/services/authentication.service.d.ts.map +1 -1
  80. package/dist/services/authentication.service.js +32 -11
  81. package/dist/services/authentication.service.js.map +1 -1
  82. package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.d.ts.map +1 -1
  83. package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.js +7 -5
  84. package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.js.map +1 -1
  85. package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.d.ts +4 -0
  86. package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.d.ts.map +1 -1
  87. package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.js +18 -1
  88. package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.js.map +1 -1
  89. package/dist/services/crud-helper.service.d.ts.map +1 -1
  90. package/dist/services/crud-helper.service.js +25 -12
  91. package/dist/services/crud-helper.service.js.map +1 -1
  92. package/dist/services/crud.service.d.ts +1 -0
  93. package/dist/services/crud.service.d.ts.map +1 -1
  94. package/dist/services/crud.service.js +27 -0
  95. package/dist/services/crud.service.js.map +1 -1
  96. package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.d.ts.map +1 -1
  97. package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.js +12 -0
  98. package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.js.map +1 -1
  99. package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.d.ts.map +1 -1
  100. package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.js +2 -0
  101. package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.js.map +1 -1
  102. package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.d.ts.map +1 -1
  103. package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.js +28 -15
  104. package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.js.map +1 -1
  105. package/dist/services/datasource-introspection.service.d.ts +62 -2
  106. package/dist/services/datasource-introspection.service.d.ts.map +1 -1
  107. package/dist/services/datasource-introspection.service.js +654 -186
  108. package/dist/services/datasource-introspection.service.js.map +1 -1
  109. package/dist/services/import-transaction.service.d.ts.map +1 -1
  110. package/dist/services/import-transaction.service.js +3 -0
  111. package/dist/services/import-transaction.service.js.map +1 -1
  112. package/dist/services/locale.service.d.ts +14 -0
  113. package/dist/services/locale.service.d.ts.map +1 -1
  114. package/dist/services/locale.service.js +83 -0
  115. package/dist/services/locale.service.js.map +1 -1
  116. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +10 -10
  117. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
  118. package/dist/services/menu-item-metadata.service.d.ts.map +1 -1
  119. package/dist/services/menu-item-metadata.service.js +1 -0
  120. package/dist/services/menu-item-metadata.service.js.map +1 -1
  121. package/dist/services/model-metadata.service.d.ts +3 -0
  122. package/dist/services/model-metadata.service.d.ts.map +1 -1
  123. package/dist/services/model-metadata.service.js +78 -0
  124. package/dist/services/model-metadata.service.js.map +1 -1
  125. package/dist/services/module-metadata.service.d.ts.map +1 -1
  126. package/dist/services/module-metadata.service.js +28 -0
  127. package/dist/services/module-metadata.service.js.map +1 -1
  128. package/dist/services/removed-field-migration.service.d.ts +30 -0
  129. package/dist/services/removed-field-migration.service.d.ts.map +1 -0
  130. package/dist/services/removed-field-migration.service.js +319 -0
  131. package/dist/services/removed-field-migration.service.js.map +1 -0
  132. package/dist/services/role-metadata.service.d.ts +1 -0
  133. package/dist/services/role-metadata.service.d.ts.map +1 -1
  134. package/dist/services/role-metadata.service.js +23 -2
  135. package/dist/services/role-metadata.service.js.map +1 -1
  136. package/dist/services/settings/default-settings-provider.service.d.ts +244 -0
  137. package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
  138. package/dist/services/settings/default-settings-provider.service.js +120 -0
  139. package/dist/services/settings/default-settings-provider.service.js.map +1 -1
  140. package/dist/services/solid-ts-morph.service.d.ts +6 -0
  141. package/dist/services/solid-ts-morph.service.d.ts.map +1 -1
  142. package/dist/services/solid-ts-morph.service.js +43 -0
  143. package/dist/services/solid-ts-morph.service.js.map +1 -1
  144. package/dist/services/view-metadata.service.d.ts.map +1 -1
  145. package/dist/services/view-metadata.service.js +4 -7
  146. package/dist/services/view-metadata.service.js.map +1 -1
  147. package/dist/solid-core.module.d.ts.map +1 -1
  148. package/dist/solid-core.module.js +4 -0
  149. package/dist/solid-core.module.js.map +1 -1
  150. package/dist/subscribers/audit.subscriber.d.ts +5 -1
  151. package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
  152. package/dist/subscribers/audit.subscriber.js +76 -12
  153. package/dist/subscribers/audit.subscriber.js.map +1 -1
  154. package/dist/subscribers/computed-entity-field.subscriber.d.ts +9 -1
  155. package/dist/subscribers/computed-entity-field.subscriber.d.ts.map +1 -1
  156. package/dist/subscribers/computed-entity-field.subscriber.js +45 -11
  157. package/dist/subscribers/computed-entity-field.subscriber.js.map +1 -1
  158. package/dist/subscribers/created-by-updated-by.subscriber.d.ts.map +1 -1
  159. package/dist/subscribers/created-by-updated-by.subscriber.js.map +1 -1
  160. package/dist/subscribers/field-metadata.subscriber.d.ts.map +1 -1
  161. package/dist/subscribers/field-metadata.subscriber.js.map +1 -1
  162. package/dist/subscribers/scheduled-job.subscriber.d.ts.map +1 -1
  163. package/dist/subscribers/scheduled-job.subscriber.js +3 -2
  164. package/dist/subscribers/scheduled-job.subscriber.js.map +1 -1
  165. package/dist/subscribers/security-rule.subscriber.d.ts.map +1 -1
  166. package/dist/subscribers/security-rule.subscriber.js +5 -3
  167. package/dist/subscribers/security-rule.subscriber.js.map +1 -1
  168. package/package.json +1 -1
  169. package/src/commands/migrate-removed-fields.command.ts +81 -0
  170. package/src/controllers/datasource-introspection.controller.ts +27 -1
  171. package/src/controllers/service.controller.ts +3 -2
  172. package/src/dtos/datasource-introspection-mapping.dto.ts +15 -0
  173. package/src/entities/{legacy-common-with-id.entity.ts → legacy-common-with-generated-id.entity.ts} +1 -1
  174. package/src/helpers/command.service.ts +10 -2
  175. package/src/helpers/environment.helper.ts +11 -0
  176. package/src/helpers/field-crud-managers/IntFieldCrudManager.ts +4 -1
  177. package/src/helpers/model-metadata-helper.service.ts +8 -3
  178. package/src/helpers/nodemon-heartbeat.ts +59 -0
  179. package/src/helpers/solid-registry.ts +7 -1
  180. package/src/helpers/typeorm-db-helper.ts +15 -0
  181. package/src/index.ts +3 -2
  182. package/src/interfaces/datasource-introspection-provider.interface.ts +1 -0
  183. package/src/interfaces.ts +1 -0
  184. package/src/repository/scheduled-job.repository.ts +10 -4
  185. package/src/repository/security-rule.repository.ts +10 -4
  186. package/src/seeders/module-metadata-seeder.service.ts +192 -2
  187. package/src/seeders/seed-data/solid-core-metadata.json +128 -34
  188. package/src/services/1.js +6 -0
  189. package/src/services/api-key.service.ts +36 -36
  190. package/src/services/authentication.service.ts +46 -12
  191. package/src/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.ts +11 -5
  192. package/src/services/computed-fields/entity/concat-entity-computed-field-provider.service.ts +15 -1
  193. package/src/services/crud-helper.service.ts +25 -12
  194. package/src/services/crud.service.ts +36 -11
  195. package/src/services/datasource-introspection/mssql-datasource-introspection-provider.service.ts +12 -0
  196. package/src/services/datasource-introspection/mysql-datasource-introspection-provider.service.ts +2 -0
  197. package/src/services/datasource-introspection/postgres-datasource-introspection-provider.service.ts +28 -15
  198. package/src/services/datasource-introspection.service.ts +811 -161
  199. package/src/services/import-transaction.service.ts +5 -0
  200. package/src/services/locale.service.ts +111 -3
  201. package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +2 -2
  202. package/src/services/menu-item-metadata.service.ts +2 -1
  203. package/src/services/model-metadata.service.ts +109 -1
  204. package/src/services/module-metadata.service.ts +36 -0
  205. package/src/services/removed-field-migration.service.ts +334 -0
  206. package/src/services/role-metadata.service.ts +26 -2
  207. package/src/services/settings/default-settings-provider.service.ts +120 -0
  208. package/src/services/solid-ts-morph.service.ts +61 -1
  209. package/src/services/view-metadata.service.ts +4 -8
  210. package/src/solid-core.module.ts +4 -0
  211. package/src/subscribers/audit.subscriber.ts +101 -18
  212. package/src/subscribers/computed-entity-field.subscriber.ts +81 -28
  213. package/src/subscribers/created-by-updated-by.subscriber.ts +3 -0
  214. package/src/subscribers/field-metadata.subscriber.ts +3 -0
  215. package/src/subscribers/scheduled-job.subscriber.ts +3 -2
  216. package/src/subscribers/security-rule.subscriber.ts +8 -3
  217. package/dist/entities/legacy-common-with-id.entity.d.ts.map +0 -1
  218. package/dist/entities/legacy-common-with-id.entity.js.map +0 -1
  219. package/dist/entities/legacy-common.entity.d.ts.map +0 -1
  220. package/dist/entities/legacy-common.entity.js.map +0 -1
  221. package/dist-tests/api/authenticate.spec.js +0 -119
  222. package/dist-tests/api/authenticate.spec.js.map +0 -1
  223. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +0 -97
  224. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +0 -1
  225. package/dist-tests/api/ping.spec.js +0 -21
  226. package/dist-tests/api/ping.spec.js.map +0 -1
  227. package/dist-tests/helpers/auth.js +0 -41
  228. package/dist-tests/helpers/auth.js.map +0 -1
  229. package/dist-tests/helpers/env.js +0 -11
  230. package/dist-tests/helpers/env.js.map +0 -1
  231. package/docs/agent-hub-grooming.md +0 -301
  232. package/docs/dashboards/AGENTIC_DASHBOARD_IMPLEMENTATION_PLAN.md +0 -438
  233. package/docs/dashboards/dashboard-curl-smoke-tests.txt +0 -146
  234. package/docs/dashboards/delete-legacy-dashboard-metadata.sql +0 -172
  235. package/docs/datasource-introspection-ddl-analysis.md +0 -326
  236. package/docs/datasource-introspection-implementation-plan.md +0 -306
  237. package/docs/grouping-enhancements.md +0 -89
  238. package/docs/java-spring/README.md +0 -3
  239. package/docs/java-spring/solid-core-module-deep-dive-report.md +0 -1317
  240. package/docs/module-package-import-handoff.md +0 -691
  241. package/docs/seed-changes.md +0 -65
  242. package/docs/test-data-workflow.md +0 -200
  243. package/docs/type-declaration-import-issue.md +0 -24
  244. /package/src/entities/{legacy-common.entity.ts → legacy-common.entity-with-existing-id.entity.ts} +0 -0
@@ -0,0 +1,334 @@
1
+ import * as fs from "fs/promises";
2
+ import { BadRequestException, Injectable, Logger, NotFoundException } from "@nestjs/common";
3
+ import { ModuleRef } from "@nestjs/core";
4
+ import { getDataSourceToken } from "@nestjs/typeorm";
5
+ import { kebabCase, snakeCase } from "lodash";
6
+ import * as path from "path";
7
+ import { ERROR_MESSAGES } from "src/constants/error-messages";
8
+ import { RelationType, SolidFieldType } from "src/dtos/create-field-metadata.dto";
9
+ import { FieldMetadata } from "src/entities/field-metadata.entity";
10
+ import { ModelMetadata } from "src/entities/model-metadata.entity";
11
+ import { ModuleMetadataHelperService } from "src/helpers/module-metadata-helper.service";
12
+ import { classify } from "src/helpers/string.helper";
13
+ import { FieldMetadataRepository } from "src/repository/field-metadata.repository";
14
+ import { ModelMetadataRepository } from "src/repository/model-metadata.repository";
15
+ import { DataSource, EntityMetadata, QueryRunner, Table } from "typeorm";
16
+
17
+ export interface RemovedFieldMigrationResult {
18
+ dryRun: boolean;
19
+ modelName: string;
20
+ operations: string[];
21
+ removedFieldNames: string[];
22
+ }
23
+
24
+ @Injectable()
25
+ export class RemovedFieldMigrationService {
26
+ constructor(
27
+ private readonly modelMetadataRepo: ModelMetadataRepository,
28
+ private readonly fieldMetadataRepo: FieldMetadataRepository,
29
+ private readonly moduleMetadataHelperService: ModuleMetadataHelperService,
30
+ private readonly moduleRef: ModuleRef,
31
+ ) { }
32
+
33
+ private readonly logger = new Logger(RemovedFieldMigrationService.name);
34
+
35
+ // Cleans fields marked for removal by updating schema state and metadata for a single model.
36
+ async migrateMarkedFields(modelUserKey: string, dryRun: boolean = false): Promise<RemovedFieldMigrationResult> {
37
+ if (!modelUserKey) {
38
+ throw new BadRequestException("Model name is required");
39
+ }
40
+
41
+ const model = await this.modelMetadataRepo.findOne({
42
+ where: { singularName: modelUserKey },
43
+ relations: { fields: true, module: true },
44
+ });
45
+
46
+ if (!model) {
47
+ throw new NotFoundException(ERROR_MESSAGES.MODEL_NOT_FOUND(modelUserKey));
48
+ }
49
+
50
+ const fieldsForRemoval = model.fields.filter((field) => field.isMarkedForRemoval);
51
+ const operations: string[] = [];
52
+
53
+ if (fieldsForRemoval.length === 0) {
54
+ const message = `No fields marked for removal were found for model "${model.singularName}".`;
55
+ this.logger.log(message);
56
+ operations.push(message);
57
+ return {
58
+ dryRun,
59
+ modelName: model.singularName,
60
+ operations,
61
+ removedFieldNames: [],
62
+ };
63
+ }
64
+
65
+ const dataSource = await this.resolveDataSource(model.dataSource);
66
+ const entityMetadata = this.resolveEntityMetadata(dataSource, model);
67
+ const queryRunner = dataSource.createQueryRunner();
68
+
69
+ try {
70
+ await queryRunner.connect();
71
+
72
+ if (!dryRun) {
73
+ await queryRunner.startTransaction();
74
+ }
75
+
76
+ for (const field of fieldsForRemoval) {
77
+ await this.cleanupMarkedField({
78
+ field,
79
+ model,
80
+ entityMetadata,
81
+ queryRunner,
82
+ dryRun,
83
+ operations,
84
+ });
85
+ }
86
+
87
+ if (!dryRun) {
88
+ await queryRunner.commitTransaction();
89
+ }
90
+ } catch (error) {
91
+ if (!dryRun && queryRunner.isTransactionActive) {
92
+ await queryRunner.rollbackTransaction();
93
+ }
94
+ throw error;
95
+ } finally {
96
+ await queryRunner.release();
97
+ }
98
+
99
+ return {
100
+ dryRun,
101
+ modelName: model.singularName,
102
+ operations,
103
+ removedFieldNames: fieldsForRemoval.map((field) => field.name),
104
+ };
105
+ }
106
+
107
+ private async cleanupMarkedField(params: { field: FieldMetadata; model: ModelMetadata; entityMetadata?: EntityMetadata; queryRunner: QueryRunner; dryRun: boolean; operations: string[]; }): Promise<void> {
108
+ const { field, model, entityMetadata, queryRunner, dryRun, operations } = params;
109
+ const relationMetadata = entityMetadata?.relations.find((relation) => relation.propertyName === field.name);
110
+ const resolvedTableName = entityMetadata?.tableName || model.tableName;
111
+
112
+ if (field.type !== SolidFieldType.relation) {
113
+ const columnCandidates = this.buildColumnCandidates(field, entityMetadata, relationMetadata);
114
+ await this.dropColumnsForField(resolvedTableName, field, columnCandidates, queryRunner, dryRun, operations);
115
+ return;
116
+ }
117
+
118
+ if (field.relationType === RelationType.manyToOne) {
119
+ const columnCandidates = this.buildColumnCandidates(field, entityMetadata, relationMetadata);
120
+ await this.dropColumnsForField(resolvedTableName, field, columnCandidates, queryRunner, dryRun, operations);
121
+ return;
122
+ }
123
+
124
+ if (field.relationType === RelationType.manyTomany) {
125
+ const joinTableName = relationMetadata?.junctionEntityMetadata?.tableName || field.relationJoinTableName;
126
+ const ownsJoinTable = relationMetadata?.isOwning || field.isRelationManyToManyOwner;
127
+
128
+ if (!ownsJoinTable) {
129
+ operations.push(`No direct database cleanup required for inverse many-to-many field "${field.name}".`);
130
+ return;
131
+ }
132
+
133
+ if (!joinTableName) {
134
+ operations.push(`Skipping join-table cleanup for "${field.name}" because no join table name could be resolved.`);
135
+ return;
136
+ }
137
+
138
+ await this.dropJoinTable(joinTableName, field, queryRunner, dryRun, operations);
139
+ return;
140
+ }
141
+
142
+ operations.push(`No direct database cleanup required for relation field "${field.name}" with type "${field.relationType}".`);
143
+ }
144
+
145
+ private async dropColumnsForField(tableName: string, field: FieldMetadata, columnCandidates: string[], queryRunner: QueryRunner, dryRun: boolean, operations: string[],): Promise<void> {
146
+ if (!tableName) {
147
+ operations.push(`Skipping field "${field.name}" because the model table name could not be resolved.`);
148
+ return;
149
+ }
150
+
151
+ if (columnCandidates.length === 0) {
152
+ operations.push(`Skipping field "${field.name}" because no column candidates could be resolved.`);
153
+ return;
154
+ }
155
+
156
+ const handledColumns = new Set<string>();
157
+ let droppedAnyColumn = false;
158
+
159
+ for (const columnName of columnCandidates) {
160
+ if (!columnName || handledColumns.has(columnName)) {
161
+ continue;
162
+ }
163
+
164
+ handledColumns.add(columnName);
165
+ const table = await this.loadTable(queryRunner, tableName);
166
+ const column = table?.columns.find((tableColumn) => tableColumn.name === columnName);
167
+
168
+ if (!column) {
169
+ continue;
170
+ }
171
+
172
+ droppedAnyColumn = true;
173
+ await this.dropColumnArtifacts(table, columnName, queryRunner, dryRun, operations);
174
+ }
175
+
176
+ if (!droppedAnyColumn) {
177
+ operations.push(`No database column found for field "${field.name}" on table "${tableName}". Metadata cleanup will still proceed.`);
178
+ }
179
+ }
180
+
181
+ private async dropColumnArtifacts(table: Table, columnName: string, queryRunner: QueryRunner, dryRun: boolean, operations: string[],): Promise<void> {
182
+ for (const foreignKey of table.foreignKeys.filter((item) => item.columnNames.includes(columnName))) {
183
+ operations.push(`Drop foreign key "${foreignKey.name}" on "${table.name}.${columnName}"`);
184
+ if (!dryRun) {
185
+ await queryRunner.dropForeignKey(table, foreignKey);
186
+ }
187
+ }
188
+
189
+ for (const uniqueConstraint of table.uniques.filter((item) => item.columnNames.includes(columnName))) {
190
+ operations.push(`Drop unique constraint "${uniqueConstraint.name}" on "${table.name}.${columnName}"`);
191
+ if (!dryRun) {
192
+ await queryRunner.dropUniqueConstraint(table, uniqueConstraint);
193
+ }
194
+ }
195
+
196
+ for (const index of table.indices.filter((item) => item.columnNames.includes(columnName))) {
197
+ operations.push(`Drop index "${index.name}" on "${table.name}.${columnName}"`);
198
+ if (!dryRun) {
199
+ await queryRunner.dropIndex(table, index);
200
+ }
201
+ }
202
+
203
+ operations.push(`Drop column "${table.name}.${columnName}"`);
204
+ if (!dryRun) {
205
+ await queryRunner.dropColumn(table, columnName);
206
+ }
207
+ }
208
+
209
+ private async dropJoinTable(joinTableName: string, field: FieldMetadata, queryRunner: QueryRunner, dryRun: boolean, operations: string[]): Promise<void> {
210
+ const table = await this.loadTable(queryRunner, joinTableName);
211
+ if (!table) {
212
+ operations.push(`Join table "${joinTableName}" for field "${field.name}" does not exist. Metadata cleanup will still proceed.`);
213
+ return;
214
+ }
215
+
216
+ operations.push(`Drop join table "${joinTableName}" for field "${field.name}"`);
217
+ if (!dryRun) {
218
+ await queryRunner.dropTable(joinTableName);
219
+ }
220
+ }
221
+
222
+ private buildColumnCandidates(field: FieldMetadata, entityMetadata?: EntityMetadata, relationMetadata?: EntityMetadata["relations"][number]): string[] {
223
+ const columnCandidates = new Set<string>();
224
+
225
+ relationMetadata?.joinColumns?.forEach((column) => {
226
+ if (column.databaseName) {
227
+ columnCandidates.add(column.databaseName);
228
+ }
229
+ });
230
+
231
+ entityMetadata?.columns
232
+ .filter((column) => column.propertyName === field.name)
233
+ .forEach((column) => {
234
+ if (column.databaseName) {
235
+ columnCandidates.add(column.databaseName);
236
+ }
237
+ });
238
+
239
+ if (field.columnName) {
240
+ columnCandidates.add(field.columnName);
241
+ }
242
+
243
+ if (field.relationCoModelColumnName) {
244
+ columnCandidates.add(field.relationCoModelColumnName);
245
+ }
246
+
247
+ if (field.type === SolidFieldType.relation && field.relationType === RelationType.manyToOne) {
248
+ columnCandidates.add(`${snakeCase(field.name)}_id`);
249
+ } else {
250
+ columnCandidates.add(snakeCase(field.name));
251
+ }
252
+
253
+ return [...columnCandidates].filter(Boolean);
254
+ }
255
+
256
+ private async resolveDataSource(dataSourceName?: string): Promise<DataSource> {
257
+ const normalizedDataSourceName = dataSourceName && dataSourceName !== "default" ? dataSourceName : undefined;
258
+ const token = normalizedDataSourceName ? getDataSourceToken(normalizedDataSourceName) : getDataSourceToken();
259
+ let dataSource: DataSource | undefined;
260
+
261
+ try {
262
+ dataSource = this.moduleRef.get<DataSource>(token, { strict: false });
263
+ } catch (error: any) {
264
+ throw new NotFoundException(`Datasource "${normalizedDataSourceName ?? "default"}" could not be resolved: ${error?.message ?? error}`);
265
+ }
266
+
267
+ if (!dataSource) {
268
+ throw new NotFoundException(`Datasource "${normalizedDataSourceName ?? "default"}" could not be resolved.`);
269
+ }
270
+
271
+ if (!dataSource.isInitialized) {
272
+ await dataSource.initialize();
273
+ }
274
+
275
+ return dataSource;
276
+ }
277
+
278
+ private resolveEntityMetadata(dataSource: DataSource, model: ModelMetadata): EntityMetadata | undefined {
279
+ const candidates = [classify(model.singularName), model.singularName, model.tableName].filter(Boolean);
280
+
281
+ for (const candidate of candidates) {
282
+ try {
283
+ return dataSource.getMetadata(candidate);
284
+ } catch {
285
+ // Try the next candidate.
286
+ }
287
+ }
288
+
289
+ return dataSource.entityMetadatas.find((metadata) => metadata.tableName === model.tableName);
290
+ }
291
+
292
+ private async resolveMetadataFilePath(moduleName: string): Promise<string> {
293
+ const defaultPath = await this.moduleMetadataHelperService.getModuleMetadataFilePath(moduleName);
294
+ if (await this.fileExists(defaultPath)) {
295
+ return defaultPath;
296
+ }
297
+
298
+ const dashModuleName = kebabCase(moduleName);
299
+ const moduleMetadataFilePath = path.resolve(
300
+ process.cwd(),
301
+ "module-metadata",
302
+ dashModuleName,
303
+ `${dashModuleName}-metadata.json`,
304
+ );
305
+
306
+ if (await this.fileExists(moduleMetadataFilePath)) {
307
+ return moduleMetadataFilePath;
308
+ }
309
+
310
+ return defaultPath;
311
+ }
312
+
313
+ private async fileExists(filePath: string): Promise<boolean> {
314
+ try {
315
+ await fs.access(filePath);
316
+ return true;
317
+ } catch {
318
+ return false;
319
+ }
320
+ }
321
+
322
+ private async loadTable(queryRunner: QueryRunner, tableName: string): Promise<Table | undefined> {
323
+ if (!tableName) {
324
+ return undefined;
325
+ }
326
+
327
+ const hasTable = await queryRunner.hasTable(tableName);
328
+ if (!hasTable) {
329
+ return undefined;
330
+ }
331
+
332
+ return queryRunner.getTable(tableName) ?? undefined;
333
+ }
334
+ }
@@ -8,6 +8,7 @@ import { ERROR_MESSAGES } from 'src/constants/error-messages';
8
8
  import { PermissionMetadataRepository } from 'src/repository/permission-metadata.repository';
9
9
  import { RoleMetadataRepository } from 'src/repository/role-metadata.repository';
10
10
  import { CreateRoleMetadataDto } from '../dtos/create-role-metadata.dto';
11
+ import { ModuleMetadata } from '../entities/module-metadata.entity';
11
12
  import { PermissionMetadata } from '../entities/permission-metadata.entity';
12
13
  import { RoleMetadata } from '../entities/role-metadata.entity';
13
14
 
@@ -65,13 +66,16 @@ export class RoleMetadataService extends CRUDService<RoleMetadata> {
65
66
  for (let id = 0; id < roles.length; id++) {
66
67
  try {
67
68
  const roleObj = roles[id];
69
+ const module = await this.resolveRoleModule(roleObj);
68
70
  // this.logger.log(`Resolving role: ${JSON.stringify(roleObj)}`);
69
71
 
70
72
  const existingRole = await this.repo.findOne({
71
73
  where: {
72
74
  name: roleObj.name,
73
75
  },
74
- relations: {},
76
+ relations: {
77
+ module: true,
78
+ },
75
79
  });
76
80
 
77
81
  // Create only if not existing already.
@@ -87,9 +91,13 @@ export class RoleMetadataService extends CRUDService<RoleMetadata> {
87
91
  }
88
92
 
89
93
  // const role = this.repo.create({ ...roleObj, permissions });
90
- const role = this.repo.create({ ...roleObj });
94
+ const role = this.repo.create({ ...roleObj, ...(module ? { module } : {}) });
91
95
  await this.repo.save(role);
92
96
  } else {
97
+ if (module && existingRole.module?.id !== module.id) {
98
+ existingRole.module = module;
99
+ await this.repo.save(existingRole);
100
+ }
93
101
  /*
94
102
  this.logger.debug(`Role ${roleObj.name} already exists`);
95
103
  const existingPermissions = existingRole.permissions.map(permission => permission.name);
@@ -112,6 +120,22 @@ export class RoleMetadataService extends CRUDService<RoleMetadata> {
112
120
  }
113
121
  }
114
122
 
123
+ private async resolveRoleModule(roleObj: CreateRoleMetadataDto): Promise<ModuleMetadata | null> {
124
+ if (roleObj.moduleId) {
125
+ return await this.entityManager.getRepository(ModuleMetadata).findOne({
126
+ where: { id: roleObj.moduleId },
127
+ });
128
+ }
129
+
130
+ if (roleObj.moduleUserKey) {
131
+ return await this.entityManager.getRepository(ModuleMetadata).findOne({
132
+ where: { name: roleObj.moduleUserKey },
133
+ });
134
+ }
135
+
136
+ return null;
137
+ }
138
+
115
139
  async addAllPermissionsToRole(roleName: string): Promise<RoleMetadata> {
116
140
  return await this._addPermissionsToRole(roleName, null);
117
141
  }