@solidxai/core 0.1.11-beta.0 → 0.1.11-beta.1

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 (51) hide show
  1. package/dist/repository/scheduled-job.repository.d.ts +3 -0
  2. package/dist/repository/scheduled-job.repository.d.ts.map +1 -1
  3. package/dist/repository/scheduled-job.repository.js +60 -0
  4. package/dist/repository/scheduled-job.repository.js.map +1 -1
  5. package/dist/seeders/module-metadata-seeder.service.d.ts +32 -1
  6. package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
  7. package/dist/seeders/module-metadata-seeder.service.js +1011 -230
  8. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  9. package/dist/services/action-metadata.service.d.ts +7 -0
  10. package/dist/services/action-metadata.service.d.ts.map +1 -1
  11. package/dist/services/action-metadata.service.js +76 -3
  12. package/dist/services/action-metadata.service.js.map +1 -1
  13. package/dist/services/role-metadata.service.d.ts +4 -0
  14. package/dist/services/role-metadata.service.d.ts.map +1 -1
  15. package/dist/services/role-metadata.service.js +72 -31
  16. package/dist/services/role-metadata.service.js.map +1 -1
  17. package/dist/services/view-metadata.service.d.ts +6 -0
  18. package/dist/services/view-metadata.service.d.ts.map +1 -1
  19. package/dist/services/view-metadata.service.js +66 -1
  20. package/dist/services/view-metadata.service.js.map +1 -1
  21. package/dist-tests/api/authenticate.spec.js +119 -0
  22. package/dist-tests/api/authenticate.spec.js.map +1 -0
  23. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +97 -0
  24. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +1 -0
  25. package/dist-tests/api/ping.spec.js +21 -0
  26. package/dist-tests/api/ping.spec.js.map +1 -0
  27. package/dist-tests/helpers/auth.js +41 -0
  28. package/dist-tests/helpers/auth.js.map +1 -0
  29. package/dist-tests/helpers/env.js +11 -0
  30. package/dist-tests/helpers/env.js.map +1 -0
  31. package/docs/agent-hub-grooming.md +301 -0
  32. package/docs/dashboards/AGENTIC_DASHBOARD_IMPLEMENTATION_PLAN.md +438 -0
  33. package/docs/dashboards/dashboard-curl-smoke-tests.txt +146 -0
  34. package/docs/dashboards/delete-legacy-dashboard-metadata.sql +172 -0
  35. package/docs/datasource-introspection-ddl-analysis.md +326 -0
  36. package/docs/datasource-introspection-implementation-plan.md +306 -0
  37. package/docs/grouping-enhancements.md +89 -0
  38. package/docs/java-spring/README.md +3 -0
  39. package/docs/java-spring/solid-core-module-deep-dive-report.md +1317 -0
  40. package/docs/module-package-import-handoff.md +691 -0
  41. package/docs/seed-changes.md +65 -0
  42. package/docs/test-data-workflow.md +200 -0
  43. package/docs/type-declaration-import-issue.md +24 -0
  44. package/package.json +1 -1
  45. package/src/repository/scheduled-job.repository.ts +73 -0
  46. package/src/seeders/module-metadata-seeder.service.ts +1223 -262
  47. package/src/services/action-metadata.service.ts +87 -2
  48. package/src/services/role-metadata.service.ts +86 -47
  49. package/src/services/view-metadata.service.ts +79 -1
  50. package/.claude/settings.local.json +0 -15
  51. package/src/services/1.js +0 -6
@@ -7,6 +7,13 @@ export declare class ActionMetadataService extends CRUDService<ActionMetadata> {
7
7
  readonly entityManager: EntityManager;
8
8
  readonly repo: ActionMetadataRepository;
9
9
  readonly moduleRef: ModuleRef;
10
+ private readonly logger;
11
+ private readonly comparableFields;
12
+ private readonly relationCompareFields;
13
+ private readonly jsonCompareFields;
14
+ private normalizeJsonFieldValue;
15
+ private getCanonicalJsonFieldString;
16
+ private hasChanges;
10
17
  constructor(entityManager: EntityManager, repo: ActionMetadataRepository, moduleRef: ModuleRef);
11
18
  findOneByUserKey(name: string, relations?: {}): Promise<ActionMetadata>;
12
19
  upsert(updateSolidActionDto: any): Promise<any>;
@@ -1 +1 @@
1
- {"version":3,"file":"action-metadata.service.d.ts","sourceRoot":"","sources":["../../src/services/action-metadata.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGxC,OAAO,EAAE,wBAAwB,EAAE,MAAM,2CAA2C,CAAC;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEpE,qBACa,qBAAsB,SAAQ,WAAW,CAAC,cAAc,CAAC;IAGlE,QAAQ,CAAC,aAAa,EAAE,aAAa;IAGrC,QAAQ,CAAC,IAAI,EAAE,wBAAwB;IACvC,QAAQ,CAAC,SAAS,EAAE,SAAS;gBAJpB,aAAa,EAAE,aAAa,EAG5B,IAAI,EAAE,wBAAwB,EAC9B,SAAS,EAAE,SAAS;IAMzB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,KAAK;IAU7C,MAAM,CAAC,oBAAoB,EAAE,GAAG;CAoBvC"}
1
+ {"version":3,"file":"action-metadata.service.d.ts","sourceRoot":"","sources":["../../src/services/action-metadata.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGxC,OAAO,EAAE,wBAAwB,EAAE,MAAM,2CAA2C,CAAC;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEpE,qBACa,qBAAsB,SAAQ,WAAW,CAAC,cAAc,CAAC;IA4ElE,QAAQ,CAAC,aAAa,EAAE,aAAa;IAGrC,QAAQ,CAAC,IAAI,EAAE,wBAAwB;IACvC,QAAQ,CAAC,SAAS,EAAE,SAAS;IA/E/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0C;IACjE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAYtB;IACX,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAwC;IAC9E,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAkC;IAEpE,OAAO,CAAC,uBAAuB;IAgC/B,OAAO,CAAC,2BAA2B;IAInC,OAAO,CAAC,UAAU;gBAsBP,aAAa,EAAE,aAAa,EAG5B,IAAI,EAAE,wBAAwB,EAC9B,SAAS,EAAE,SAAS;IAMzB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,KAAK;IAU7C,MAAM,CAAC,oBAAoB,EAAE,GAAG;CAgCvC"}
@@ -11,6 +11,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
11
11
  var __param = (this && this.__param) || function (paramIndex, decorator) {
12
12
  return function (target, key) { decorator(target, key, paramIndex); }
13
13
  };
14
+ var ActionMetadataService_1;
14
15
  Object.defineProperty(exports, "__esModule", { value: true });
15
16
  exports.ActionMetadataService = void 0;
16
17
  const common_1 = require("@nestjs/common");
@@ -19,12 +20,74 @@ const typeorm_1 = require("@nestjs/typeorm");
19
20
  const crud_service_1 = require("./crud.service");
20
21
  const typeorm_2 = require("typeorm");
21
22
  const action_metadata_repository_1 = require("../repository/action-metadata.repository");
22
- let ActionMetadataService = class ActionMetadataService extends crud_service_1.CRUDService {
23
+ let ActionMetadataService = ActionMetadataService_1 = class ActionMetadataService extends crud_service_1.CRUDService {
24
+ normalizeJsonFieldValue(value) {
25
+ if (typeof value === 'string') {
26
+ const trimmedValue = value.trim();
27
+ if ((trimmedValue.startsWith('{') && trimmedValue.endsWith('}')) ||
28
+ (trimmedValue.startsWith('[') && trimmedValue.endsWith(']'))) {
29
+ try {
30
+ return this.normalizeJsonFieldValue(JSON.parse(trimmedValue));
31
+ }
32
+ catch {
33
+ return value;
34
+ }
35
+ }
36
+ return value;
37
+ }
38
+ if (Array.isArray(value)) {
39
+ return value.map((item) => this.normalizeJsonFieldValue(item));
40
+ }
41
+ if (value && typeof value === 'object') {
42
+ return Object.keys(value)
43
+ .sort()
44
+ .reduce((result, key) => {
45
+ result[key] = this.normalizeJsonFieldValue(value[key]);
46
+ return result;
47
+ }, {});
48
+ }
49
+ return value;
50
+ }
51
+ getCanonicalJsonFieldString(value) {
52
+ return JSON.stringify(this.normalizeJsonFieldValue(value) ?? null);
53
+ }
54
+ hasChanges(existingSolidAction, updateSolidActionDto) {
55
+ return this.comparableFields.some((key) => {
56
+ const value = updateSolidActionDto[key];
57
+ if (typeof value === 'undefined') {
58
+ return false;
59
+ }
60
+ if (this.relationCompareFields.has(key)) {
61
+ const relationValue = value;
62
+ return existingSolidAction[key]?.id !== relationValue?.id;
63
+ }
64
+ if (this.jsonCompareFields.has(key)) {
65
+ return this.getCanonicalJsonFieldString(existingSolidAction[key]) !== this.getCanonicalJsonFieldString(value);
66
+ }
67
+ return existingSolidAction[key] !== value;
68
+ });
69
+ }
23
70
  constructor(entityManager, repo, moduleRef) {
24
71
  super(entityManager, repo, 'actionMetadata', 'solid-core', moduleRef);
25
72
  this.entityManager = entityManager;
26
73
  this.repo = repo;
27
74
  this.moduleRef = moduleRef;
75
+ this.logger = new common_1.Logger(ActionMetadataService_1.name);
76
+ this.comparableFields = [
77
+ 'name',
78
+ 'displayName',
79
+ 'type',
80
+ 'domain',
81
+ 'context',
82
+ 'customComponent',
83
+ 'customIsModal',
84
+ 'serverEndpoint',
85
+ 'module',
86
+ 'model',
87
+ 'view',
88
+ ];
89
+ this.relationCompareFields = new Set(['module', 'model', 'view']);
90
+ this.jsonCompareFields = new Set(['domain', 'context']);
28
91
  }
29
92
  async findOneByUserKey(name, relations = {}) {
30
93
  const entity = await this.repo.findOne({
@@ -39,9 +102,19 @@ let ActionMetadataService = class ActionMetadataService extends crud_service_1.C
39
102
  const existingSolidAction = await this.repo.findOne({
40
103
  where: {
41
104
  name: updateSolidActionDto.name
42
- }
105
+ },
106
+ relations: {
107
+ module: true,
108
+ model: true,
109
+ view: true,
110
+ },
43
111
  });
44
112
  if (existingSolidAction) {
113
+ const hasChanges = this.hasChanges(existingSolidAction, updateSolidActionDto);
114
+ if (!hasChanges) {
115
+ this.logger.debug(`Skipping action upsert for ${updateSolidActionDto.name}; no changes detected.`);
116
+ return existingSolidAction;
117
+ }
45
118
  const updatedSolidActionDto = { ...existingSolidAction, ...updateSolidActionDto };
46
119
  return this.repo.save(updatedSolidActionDto);
47
120
  }
@@ -52,7 +125,7 @@ let ActionMetadataService = class ActionMetadataService extends crud_service_1.C
52
125
  }
53
126
  };
54
127
  exports.ActionMetadataService = ActionMetadataService;
55
- exports.ActionMetadataService = ActionMetadataService = __decorate([
128
+ exports.ActionMetadataService = ActionMetadataService = ActionMetadataService_1 = __decorate([
56
129
  (0, common_1.Injectable)(),
57
130
  __param(0, (0, typeorm_1.InjectEntityManager)()),
58
131
  __metadata("design:paramtypes", [typeorm_2.EntityManager,
@@ -1 +1 @@
1
- {"version":3,"file":"action-metadata.service.js","sourceRoot":"","sources":["../../src/services/action-metadata.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAA4C;AAC5C,uCAAyC;AACzC,6CAAsD;AACtD,iDAAwD;AACxD,qCAAwC;AAGxC,yFAAqF;AAI9E,IAAM,qBAAqB,GAA3B,MAAM,qBAAsB,SAAQ,0BAA2B;IACpE,YAEW,aAA4B,EAG5B,IAA8B,EAC9B,SAAoB;QAG7B,KAAK,CAAC,aAAa,EAAE,IAAI,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;QAP7D,kBAAa,GAAb,aAAa,CAAe;QAG5B,SAAI,GAAJ,IAAI,CAA0B;QAC9B,cAAS,GAAT,SAAS,CAAW;IAI/B,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,IAAY,EAAE,SAAS,GAAG,EAAE;QACjD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;YACrC,KAAK,EAAE;gBACL,IAAI,EAAE,IAAI;aACX;YACD,SAAS,EAAE,SAAS;SACrB,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,oBAAyB;QAEpC,MAAM,mBAAmB,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;YAClD,KAAK,EAAE;gBACL,IAAI,EAAE,oBAAoB,CAAC,IAAI;aAChC;SACF,CAAC,CAAA;QAGF,IAAI,mBAAmB,EAAE,CAAC;YACxB,MAAM,qBAAqB,GAAG,EAAE,GAAG,mBAAmB,EAAE,GAAG,oBAAoB,EAAE,CAAC;YAClF,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;QAC/C,CAAC;aAEI,CAAC;YACJ,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;YAC9D,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;CAEF,CAAA;AA3CY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;IAGR,WAAA,IAAA,6BAAmB,GAAE,CAAA;qCACE,uBAAa;QAGtB,qDAAwB;QACnB,gBAAS;GAPpB,qBAAqB,CA2CjC","sourcesContent":["import { Injectable } from '@nestjs/common';\nimport { ModuleRef } from \"@nestjs/core\";\nimport { InjectEntityManager } from '@nestjs/typeorm';\nimport { CRUDService } from 'src/services/crud.service';\nimport { EntityManager } from 'typeorm';\n\n\nimport { ActionMetadataRepository } from 'src/repository/action-metadata.repository';\nimport { ActionMetadata } from '../entities/action-metadata.entity';\n\n@Injectable()\nexport class ActionMetadataService extends CRUDService<ActionMetadata> {\n constructor(\n @InjectEntityManager()\n readonly entityManager: EntityManager,\n // @InjectRepository(ActionMetadata, 'default')\n // readonly repo: Repository<ActionMetadata>,\n readonly repo: ActionMetadataRepository,\n readonly moduleRef: ModuleRef\n\n ) {\n super(entityManager, repo, 'actionMetadata', 'solid-core', moduleRef);\n }\n\n async findOneByUserKey(name: string, relations = {}) {\n const entity = await this.repo.findOne({\n where: {\n name: name,\n },\n relations: relations,\n });\n return entity;\n }\n\n async upsert(updateSolidActionDto: any) {\n // First check if module already exists using name\n const existingSolidAction = await this.repo.findOne({\n where: {\n name: updateSolidActionDto.name\n }\n })\n\n // if found\n if (existingSolidAction) {\n const updatedSolidActionDto = { ...existingSolidAction, ...updateSolidActionDto };\n return this.repo.save(updatedSolidActionDto);\n }\n // if not found - create new \n else {\n const moduleMetadata = this.repo.create(updateSolidActionDto);\n return this.repo.save(moduleMetadata);\n }\n }\n\n}\n"]}
1
+ {"version":3,"file":"action-metadata.service.js","sourceRoot":"","sources":["../../src/services/action-metadata.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable, Logger } from '@nestjs/common';\nimport { ModuleRef } from \"@nestjs/core\";\nimport { InjectEntityManager } from '@nestjs/typeorm';\nimport { CRUDService } from 'src/services/crud.service';\nimport { EntityManager } from 'typeorm';\n\n\nimport { ActionMetadataRepository } from 'src/repository/action-metadata.repository';\nimport { ActionMetadata } from '../entities/action-metadata.entity';\n\n@Injectable()\nexport class ActionMetadataService extends CRUDService<ActionMetadata> {\n private readonly logger = new Logger(ActionMetadataService.name);\n private readonly comparableFields = [\n 'name',\n 'displayName',\n 'type',\n 'domain',\n 'context',\n 'customComponent',\n 'customIsModal',\n 'serverEndpoint',\n 'module',\n 'model',\n 'view',\n ] as const;\n private readonly relationCompareFields = new Set(['module', 'model', 'view']);\n private readonly jsonCompareFields = new Set(['domain', 'context']);\n\n private normalizeJsonFieldValue(value: any): any {\n if (typeof value === 'string') {\n const trimmedValue = value.trim();\n if (\n (trimmedValue.startsWith('{') && trimmedValue.endsWith('}')) ||\n (trimmedValue.startsWith('[') && trimmedValue.endsWith(']'))\n ) {\n try {\n return this.normalizeJsonFieldValue(JSON.parse(trimmedValue));\n } catch {\n return value;\n }\n }\n return value;\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => this.normalizeJsonFieldValue(item));\n }\n\n if (value && typeof value === 'object') {\n return Object.keys(value)\n .sort()\n .reduce((result, key) => {\n result[key] = this.normalizeJsonFieldValue(value[key]);\n return result;\n }, {} as Record<string, any>);\n }\n\n return value;\n }\n\n private getCanonicalJsonFieldString(value: any): string {\n return JSON.stringify(this.normalizeJsonFieldValue(value) ?? null);\n }\n\n private hasChanges(existingSolidAction: ActionMetadata, updateSolidActionDto: any): boolean {\n return this.comparableFields.some((key) => {\n const value = updateSolidActionDto[key];\n if (typeof value === 'undefined') {\n return false;\n }\n\n if (this.relationCompareFields.has(key)) {\n const relationValue = value as any;\n return (existingSolidAction as any)[key]?.id !== relationValue?.id;\n }\n\n if (this.jsonCompareFields.has(key)) {\n return this.getCanonicalJsonFieldString((existingSolidAction as any)[key]) !== this.getCanonicalJsonFieldString(value);\n }\n\n return (existingSolidAction as any)[key] !== value;\n });\n }\n\n constructor(\n @InjectEntityManager()\n readonly entityManager: EntityManager,\n // @InjectRepository(ActionMetadata, 'default')\n // readonly repo: Repository<ActionMetadata>,\n readonly repo: ActionMetadataRepository,\n readonly moduleRef: ModuleRef\n\n ) {\n super(entityManager, repo, 'actionMetadata', 'solid-core', moduleRef);\n }\n\n async findOneByUserKey(name: string, relations = {}) {\n const entity = await this.repo.findOne({\n where: {\n name: name,\n },\n relations: relations,\n });\n return entity;\n }\n\n async upsert(updateSolidActionDto: any) {\n // First check if module already exists using name\n const existingSolidAction = await this.repo.findOne({\n where: {\n name: updateSolidActionDto.name\n },\n relations: {\n module: true,\n model: true,\n view: true,\n },\n })\n\n // if found\n if (existingSolidAction) {\n const hasChanges = this.hasChanges(existingSolidAction, updateSolidActionDto);\n\n if (!hasChanges) {\n this.logger.debug(`Skipping action upsert for ${updateSolidActionDto.name}; no changes detected.`);\n return existingSolidAction;\n }\n\n const updatedSolidActionDto = { ...existingSolidAction, ...updateSolidActionDto };\n return this.repo.save(updatedSolidActionDto);\n }\n // if not found - create new \n else {\n const moduleMetadata = this.repo.create(updateSolidActionDto);\n return this.repo.save(moduleMetadata);\n }\n }\n\n}\n"]}
@@ -11,6 +11,7 @@ export declare class RoleMetadataService extends CRUDService<RoleMetadata> {
11
11
  readonly permissionRepository: PermissionMetadataRepository;
12
12
  readonly moduleRef: ModuleRef;
13
13
  private readonly logger;
14
+ private readonly permissionAssignmentBatchSize;
14
15
  constructor(entityManager: EntityManager, repo: RoleMetadataRepository, permissionRepository: PermissionMetadataRepository, moduleRef: ModuleRef);
15
16
  findRoleByName(roleName: string): Promise<RoleMetadata>;
16
17
  assertRoleExistsByName(roleName: string): Promise<void>;
@@ -21,5 +22,8 @@ export declare class RoleMetadataService extends CRUDService<RoleMetadata> {
21
22
  private _addPermissionsToRole;
22
23
  removePermissionsFromRole(roleName: string, permissionNames: string[]): Promise<RoleMetadata>;
23
24
  private preloadPermissionByName;
25
+ private loadTargetPermissions;
26
+ private getRolePermissionJoinInfo;
27
+ private insertRolePermissionMappings;
24
28
  }
25
29
  //# sourceMappingURL=role-metadata.service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"role-metadata.service.d.ts","sourceRoot":"","sources":["../../src/services/role-metadata.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAM,MAAM,SAAS,CAAC;AAG5C,OAAO,EAAE,4BAA4B,EAAE,MAAM,+CAA+C,CAAC;AAC7F,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAEzE,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAEhE,qBACa,mBAAoB,SAAQ,WAAW,CAAC,YAAY,CAAC;IAK9D,QAAQ,CAAC,aAAa,EAAE,aAAa;IAGrC,QAAQ,CAAC,IAAI,EAAE,sBAAsB;IAGrC,QAAQ,CAAC,oBAAoB,EAAE,4BAA4B;IAC3D,QAAQ,CAAC,SAAS,EAAE,SAAS;IAX/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;gBAIpD,aAAa,EAAE,aAAa,EAG5B,IAAI,EAAE,sBAAsB,EAG5B,oBAAoB,EAAE,4BAA4B,EAClD,SAAS,EAAE,SAAS;IAMzB,cAAc,CAAC,QAAQ,EAAE,MAAM;IAe/B,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAevD,sBAAsB,CAAC,KAAK,EAAE,qBAAqB,EAAE;IAmDrD,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAIhE,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC;IAIxF,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE;YAItD,qBAAqB;IAiE7B,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC;YAsBrF,uBAAuB;CAWtC"}
1
+ {"version":3,"file":"role-metadata.service.d.ts","sourceRoot":"","sources":["../../src/services/role-metadata.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAM,MAAM,SAAS,CAAC;AAG5C,OAAO,EAAE,4BAA4B,EAAE,MAAM,+CAA+C,CAAC;AAC7F,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAEzE,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAEhE,qBACa,mBAAoB,SAAQ,WAAW,CAAC,YAAY,CAAC;IAM9D,QAAQ,CAAC,aAAa,EAAE,aAAa;IAGrC,QAAQ,CAAC,IAAI,EAAE,sBAAsB;IAGrC,QAAQ,CAAC,oBAAoB,EAAE,4BAA4B;IAC3D,QAAQ,CAAC,SAAS,EAAE,SAAS;IAZ/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;IAC/D,OAAO,CAAC,QAAQ,CAAC,6BAA6B,CAAO;gBAI1C,aAAa,EAAE,aAAa,EAG5B,IAAI,EAAE,sBAAsB,EAG5B,oBAAoB,EAAE,4BAA4B,EAClD,SAAS,EAAE,SAAS;IAMzB,cAAc,CAAC,QAAQ,EAAE,MAAM;IAe/B,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAevD,sBAAsB,CAAC,KAAK,EAAE,qBAAqB,EAAE;IAmDrD,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAIhE,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC;IAIxF,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE;YAItD,qBAAqB;IA8C7B,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC;YAsBrF,uBAAuB;YAUvB,qBAAqB;IAiBnC,OAAO,CAAC,yBAAyB;YAmBnB,4BAA4B;CAsB3C"}
@@ -30,6 +30,7 @@ let RoleMetadataService = RoleMetadataService_1 = class RoleMetadataService exte
30
30
  this.permissionRepository = permissionRepository;
31
31
  this.moduleRef = moduleRef;
32
32
  this.logger = new common_1.Logger(RoleMetadataService_1.name);
33
+ this.permissionAssignmentBatchSize = 500;
33
34
  }
34
35
  async findRoleByName(roleName) {
35
36
  const entity = await this.repo.findOne({
@@ -97,46 +98,41 @@ let RoleMetadataService = RoleMetadataService_1 = class RoleMetadataService exte
97
98
  async _addPermissionsToRole(roleName, permissionNames) {
98
99
  const role = await this.repo.findOne({
99
100
  where: { name: roleName },
100
- relations: {
101
- permissions: true
101
+ select: {
102
+ id: true,
103
+ name: true,
102
104
  }
103
105
  });
104
106
  if (!role) {
105
107
  throw new Error(`Role '${roleName}' not found.`);
106
108
  }
107
- let newPermissions;
108
- if (permissionNames && permissionNames.length != 0) {
109
- newPermissions = await this.permissionRepository.find({ where: { name: (0, typeorm_2.In)(permissionNames) } });
110
- if (newPermissions.length !== permissionNames.length) {
111
- throw new Error(`One or more permissions not found.`);
112
- }
109
+ const joinInfo = this.getRolePermissionJoinInfo();
110
+ const targetPermissions = await this.loadTargetPermissions(permissionNames);
111
+ if (!permissionNames || permissionNames.length === 0) {
112
+ await this.entityManager
113
+ .createQueryBuilder()
114
+ .delete()
115
+ .from(joinInfo.tableName)
116
+ .where(`${joinInfo.roleIdColumn} = :roleId`, { roleId: role.id })
117
+ .execute();
118
+ await this.insertRolePermissionMappings(role.id, targetPermissions.map((permission) => permission.id), joinInfo);
113
119
  }
114
120
  else {
115
- newPermissions = await this.permissionRepository.find();
116
- if (newPermissions.length == 0) {
117
- throw new Error(`No permissions configured in the system. Did you forget to run the PermissionSeederService?`);
118
- }
119
- }
120
- if (role.permissions && role.permissions.length > 0) {
121
- for (let i = 0; i < newPermissions.length; i++) {
122
- const newPermission = newPermissions[i];
123
- let newPermissionFound = true;
124
- for (let j = 0; j < role.permissions.length; j++) {
125
- const existingPermission = role.permissions[j];
126
- if (existingPermission.name === newPermission.name) {
127
- newPermissionFound = false;
128
- break;
129
- }
130
- }
131
- if (newPermissionFound) {
132
- role.permissions.push(newPermission);
133
- }
121
+ const existingRows = await this.entityManager
122
+ .createQueryBuilder()
123
+ .select(joinInfo.permissionIdColumn, 'permissionId')
124
+ .from(joinInfo.tableName, 'role_permission')
125
+ .where(`${joinInfo.roleIdColumn} = :roleId`, { roleId: role.id })
126
+ .getRawMany();
127
+ const existingPermissionIds = new Set(existingRows.map((row) => Number(row.permissionId)));
128
+ const missingPermissionIds = targetPermissions
129
+ .map((permission) => permission.id)
130
+ .filter((permissionId) => !existingPermissionIds.has(permissionId));
131
+ if (missingPermissionIds.length > 0) {
132
+ await this.insertRolePermissionMappings(role.id, missingPermissionIds, joinInfo);
134
133
  }
135
134
  }
136
- else {
137
- role.permissions = newPermissions;
138
- }
139
- return await this.repo.save(role);
135
+ return await this.findRoleByName(roleName);
140
136
  }
141
137
  async removePermissionsFromRole(roleName, permissionNames) {
142
138
  const role = await this.repo.findOne({
@@ -162,6 +158,51 @@ let RoleMetadataService = RoleMetadataService_1 = class RoleMetadataService exte
162
158
  }
163
159
  return existingPermission;
164
160
  }
161
+ async loadTargetPermissions(permissionNames) {
162
+ if (permissionNames && permissionNames.length !== 0) {
163
+ const uniquePermissionNames = [...new Set(permissionNames)];
164
+ const permissions = await this.permissionRepository.find({ where: { name: (0, typeorm_2.In)(uniquePermissionNames) } });
165
+ if (permissions.length !== uniquePermissionNames.length) {
166
+ throw new Error(`One or more permissions not found.`);
167
+ }
168
+ return permissions;
169
+ }
170
+ const permissions = await this.permissionRepository.find();
171
+ if (permissions.length === 0) {
172
+ throw new Error(`No permissions configured in the system. Did you forget to run the PermissionSeederService?`);
173
+ }
174
+ return permissions;
175
+ }
176
+ getRolePermissionJoinInfo() {
177
+ const relation = this.repo.metadata.relations.find((candidate) => candidate.propertyName === 'permissions');
178
+ if (!relation?.junctionEntityMetadata) {
179
+ throw new Error('Role-permission join table metadata not found.');
180
+ }
181
+ const roleIdColumn = relation.joinColumns[0]?.databaseName;
182
+ const permissionIdColumn = relation.inverseJoinColumns[0]?.databaseName;
183
+ if (!roleIdColumn || !permissionIdColumn) {
184
+ throw new Error('Role-permission join table column metadata not found.');
185
+ }
186
+ return {
187
+ tableName: relation.junctionEntityMetadata.tableName,
188
+ roleIdColumn,
189
+ permissionIdColumn,
190
+ };
191
+ }
192
+ async insertRolePermissionMappings(roleId, permissionIds, joinInfo) {
193
+ for (let index = 0; index < permissionIds.length; index += this.permissionAssignmentBatchSize) {
194
+ const batch = permissionIds.slice(index, index + this.permissionAssignmentBatchSize);
195
+ await this.entityManager
196
+ .createQueryBuilder()
197
+ .insert()
198
+ .into(joinInfo.tableName)
199
+ .values(batch.map((permissionId) => ({
200
+ [joinInfo.roleIdColumn]: roleId,
201
+ [joinInfo.permissionIdColumn]: permissionId,
202
+ })))
203
+ .execute();
204
+ }
205
+ }
165
206
  };
166
207
  exports.RoleMetadataService = RoleMetadataService;
167
208
  exports.RoleMetadataService = RoleMetadataService = RoleMetadataService_1 = __decorate([
@@ -1 +1 @@
1
- {"version":3,"file":"role-metadata.service.js","sourceRoot":"","sources":["../../src/services/role-metadata.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAuE;AACvE,uCAAyC;AACzC,6CAAsD;AACtD,iDAAwD;AACxD,qCAA4C;AAE5C,gEAA8D;AAC9D,iGAA6F;AAC7F,qFAAiF;AAM1E,IAAM,mBAAmB,2BAAzB,MAAM,mBAAoB,SAAQ,0BAAyB;IAGhE,YAEE,aAAqC,EAG5B,IAA4B,EAG5B,oBAAkD,EAClD,SAAoB;QAG7B,KAAK,CAAC,aAAa,EAAE,IAAI,EAAE,cAAc,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;QAV3D,kBAAa,GAAb,aAAa,CAAe;QAG5B,SAAI,GAAJ,IAAI,CAAwB;QAG5B,yBAAoB,GAApB,oBAAoB,CAA8B;QAClD,cAAS,GAAT,SAAS,CAAW;QAXd,WAAM,GAAG,IAAI,eAAM,CAAC,qBAAmB,CAAC,IAAI,CAAC,CAAC;IAe/D,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,QAAgB;QACnC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;YACrC,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;aACf;YACD,SAAS,EAAE;gBACT,WAAW,EAAE,IAAI;aAClB;SACF,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,0BAAiB,CAAC,WAAW,QAAQ,YAAY,CAAC,CAAC;QAC/D,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,sBAAsB,CAAC,QAAgB;QAC3C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;YACrC,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;aACf;YACD,MAAM,EAAE;gBACN,EAAE,EAAE,IAAI;aACT;SACF,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,0BAAiB,CAAC,WAAW,QAAQ,YAAY,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAGD,KAAK,CAAC,sBAAsB,CAAC,KAA8B;QACzD,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,KAAK,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC;YACzC,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC;gBAG1B,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;oBAC3C,KAAK,EAAE;wBACL,IAAI,EAAE,OAAO,CAAC,IAAI;qBACnB;oBACD,SAAS,EAAE,EAAE;iBACd,CAAC,CAAC;gBAGH,IAAI,CAAC,YAAY,EAAE,CAAC;oBAClB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,OAAO,CAAC,IAAI,iCAAiC,CAAC,CAAC;oBAEzE,IAAI,WAAW,GAAG,EAAE,CAAC;oBAErB,IAAI,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBAC1D,MAAM,OAAO,CAAC,GAAG,CACf,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,CAAC,uBAAuB,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CACrF,CAAC;oBACJ,CAAC;oBAGD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;oBAC9C,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC7B,CAAC;qBAAM,CAAC;gBAgBR,CAAC;YACH,CAAC;YAAC,OAAO,KAAU,EAAE,CAAC;gBACpB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,uBAAuB,CAAC,QAAgB;QAC5C,OAAO,MAAM,IAAI,CAAC,qBAAqB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC1D,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,QAAgB,EAAE,eAAyB;QACpE,OAAO,MAAM,IAAI,CAAC,qBAAqB,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,QAAgB,EAAE,cAAwB;QAClE,OAAO,MAAM,IAAI,CAAC,qBAAqB,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;IACpE,CAAC;IAEO,KAAK,CAAC,qBAAqB,CAAC,QAAgB,EAAE,eAAyB;QAC7E,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;YACnC,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,SAAS,EAAE;gBACT,WAAW,EAAE,IAAI;aAClB;SACF,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,SAAS,QAAQ,cAAc,CAAC,CAAC;QACnD,CAAC;QAKD,IAAI,cAAoC,CAAC;QAGzC,IAAI,eAAe,IAAI,eAAe,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YAGnD,cAAc,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAA,YAAE,EAAC,eAAe,CAAC,EAAE,EAAE,CAAC,CAAC;YAChG,IAAI,cAAc,CAAC,MAAM,KAAK,eAAe,CAAC,MAAM,EAAE,CAAC;gBACrD,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;aACI,CAAC;YAKJ,cAAc,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC;YACxD,IAAI,cAAc,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;gBAC/B,MAAM,IAAI,KAAK,CAAC,6FAA6F,CAAC,CAAC;YACjH,CAAC;QACH,CAAC;QAKD,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC/C,MAAM,aAAa,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;gBACxC,IAAI,kBAAkB,GAAG,IAAI,CAAC;gBAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBACjD,MAAM,kBAAkB,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;oBAC/C,IAAI,kBAAkB,CAAC,IAAI,KAAK,aAAa,CAAC,IAAI,EAAE,CAAC;wBACnD,kBAAkB,GAAG,KAAK,CAAC;wBAC3B,MAAM;oBACR,CAAC;gBACH,CAAC;gBAED,IAAI,kBAAkB,EAAE,CAAC;oBACvB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;gBACvC,CAAC;YACH,CAAC;QAEH,CAAC;aAEI,CAAC;YACJ,IAAI,CAAC,WAAW,GAAG,cAAc,CAAC;QACpC,CAAC;QAED,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,yBAAyB,CAAC,QAAgB,EAAE,eAAyB;QAGzE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;YACnC,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;aACf;YACD,SAAS,EAAE;gBACT,WAAW,EAAE,IAAI;aAClB;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,+BAAc,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC3D,CAAC;QAGD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QAErG,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAEO,KAAK,CAAC,uBAAuB,CAAC,IAAY;QAChD,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC;YACjE,KAAK,EAAE,EAAE,IAAI,EAAE;SAChB,CAAC,CAAC;QACH,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACxB,MAAM,IAAI,0BAAiB,CAAC,+BAAc,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC;QACzE,CAAC;QACD,OAAO,kBAAkB,CAAC;IAC5B,CAAC;CAGF,CAAA;AAjNY,kDAAmB;8BAAnB,mBAAmB;IAD/B,IAAA,mBAAU,GAAE;IAKR,WAAA,IAAA,6BAAmB,GAAE,CAAA;qCACE,uBAAa;QAGtB,iDAAsB;QAGN,6DAA4B;QACvC,gBAAS;GAZpB,mBAAmB,CAiN/B","sourcesContent":["import { Injectable, Logger, NotFoundException } from '@nestjs/common';\nimport { ModuleRef } from \"@nestjs/core\";\nimport { InjectEntityManager } from '@nestjs/typeorm';\nimport { CRUDService } from 'src/services/crud.service';\nimport { EntityManager, In } from 'typeorm';\n\nimport { ERROR_MESSAGES } from 'src/constants/error-messages';\nimport { PermissionMetadataRepository } from 'src/repository/permission-metadata.repository';\nimport { RoleMetadataRepository } from 'src/repository/role-metadata.repository';\nimport { CreateRoleMetadataDto } from '../dtos/create-role-metadata.dto';\nimport { PermissionMetadata } from '../entities/permission-metadata.entity';\nimport { RoleMetadata } from '../entities/role-metadata.entity';\n\n@Injectable()\nexport class RoleMetadataService extends CRUDService<RoleMetadata> {\n private readonly logger = new Logger(RoleMetadataService.name);\n\n constructor(\n @InjectEntityManager()\n readonly entityManager: EntityManager,\n // @InjectRepository(RoleMetadata, 'default')\n // readonly repo: Repository<RoleMetadata>,\n readonly repo: RoleMetadataRepository,\n // @InjectRepository(PermissionMetadata)\n // private readonly permissionRepository: Repository<PermissionMetadata>,\n readonly permissionRepository: PermissionMetadataRepository,\n readonly moduleRef: ModuleRef\n\n ) {\n super(entityManager, repo, 'roleMetadata', 'solid-core', moduleRef);\n }\n\n async findRoleByName(roleName: string) {\n const entity = await this.repo.findOne({\n where: {\n name: roleName\n },\n relations: {\n permissions: true\n }\n });\n if (!entity) {\n throw new NotFoundException(`Entity #${roleName} not found`);\n }\n return entity;\n }\n\n async assertRoleExistsByName(roleName: string): Promise<void> {\n const entity = await this.repo.findOne({\n where: {\n name: roleName\n },\n select: {\n id: true\n }\n });\n if (!entity) {\n throw new NotFoundException(`Entity #${roleName} not found`);\n }\n }\n\n // OK\n async createRolesIfNotExists(roles: CreateRoleMetadataDto[]) {\n for (let id = 0; id < roles.length; id++) {\n try {\n const roleObj = roles[id];\n // this.logger.log(`Resolving role: ${JSON.stringify(roleObj)}`);\n\n const existingRole = await this.repo.findOne({\n where: {\n name: roleObj.name,\n },\n relations: {},\n });\n\n // Create only if not existing already.\n if (!existingRole) {\n this.logger.debug(`Role ${roleObj.name} does not exist, hence creating`);\n\n let permissions = [];\n\n if (roleObj.permissions && roleObj.permissions.length > 0) {\n await Promise.all(\n roleObj.permissions.map(permission => this.preloadPermissionByName(permission.name)),\n );\n }\n\n // const role = this.repo.create({ ...roleObj, permissions });\n const role = this.repo.create({ ...roleObj });\n await this.repo.save(role);\n } else {\n /*\n this.logger.debug(`Role ${roleObj.name} already exists`);\n const existingPermissions = existingRole.permissions.map(permission => permission.name);\n const newPermissions = roleObj.permissions.map(permission => permission.name);\n const permissionsToAdd = newPermissions.filter(permission => !existingPermissions.includes(permission));\n const permissionsToRemove = existingPermissions.filter(permission => !newPermissions.includes(permission));\n this.logger.debug(`Permissions to add: ${JSON.stringify(permissionsToAdd)}`);\n if (permissionsToAdd.length > 0) {\n await this.addPermissionsToRole(roleObj.name, permissionsToAdd);\n }\n this.logger.debug(`Permissions to remove: ${JSON.stringify(permissionsToRemove)}`);\n if (permissionsToRemove.length > 0) {\n await this.removePermissionsFromRole(roleObj.name, permissionsToRemove);\n }\n */\n }\n } catch (error: any) {\n this.logger.error(error);\n }\n }\n }\n\n async addAllPermissionsToRole(roleName: string): Promise<RoleMetadata> {\n return await this._addPermissionsToRole(roleName, null);\n }\n\n async addPermissionsToRole(roleName: string, permissionNames: string[]): Promise<RoleMetadata> {\n return await this._addPermissionsToRole(roleName, permissionNames);\n }\n\n async addPermissionToRole(roleName: string, permissionName: string[]) {\n return await this._addPermissionsToRole(roleName, permissionName);\n }\n\n private async _addPermissionsToRole(roleName: string, permissionNames: string[]): Promise<RoleMetadata> {\n const role = await this.repo.findOne({\n where: { name: roleName },\n relations: {\n permissions: true\n }\n });\n if (!role) {\n throw new Error(`Role '${roleName}' not found.`);\n }\n\n // this.logger.log(`Found role ${roleName}`);\n\n // The new set of permissions which are to be added to this role.\n let newPermissions: PermissionMetadata[];\n\n // Load all the specified permissions in the system. \n if (permissionNames && permissionNames.length != 0) {\n // this.logger.log(`Loading specified permissions.`);\n\n newPermissions = await this.permissionRepository.find({ where: { name: In(permissionNames) } });\n if (newPermissions.length !== permissionNames.length) {\n throw new Error(`One or more permissions not found.`);\n }\n }\n else {\n // this.logger.log(`Loading all permissions in system.`);\n\n // Load all permissions in the system. \n // TODO: Do we want to convert this to a paginated query to avoid having to load a very large permissions table into memory?\n newPermissions = await this.permissionRepository.find();\n if (newPermissions.length == 0) {\n throw new Error(`No permissions configured in the system. Did you forget to run the PermissionSeederService?`);\n }\n }\n\n // this.logger.log(`Adding ${newPermissions.length} permissions to role ${roleName}.`);\n\n // if there are already permissions assigned. \n if (role.permissions && role.permissions.length > 0) {\n for (let i = 0; i < newPermissions.length; i++) {\n const newPermission = newPermissions[i];\n let newPermissionFound = true;\n for (let j = 0; j < role.permissions.length; j++) {\n const existingPermission = role.permissions[j];\n if (existingPermission.name === newPermission.name) {\n newPermissionFound = false;\n break;\n }\n }\n\n if (newPermissionFound) {\n role.permissions.push(newPermission);\n }\n }\n\n }\n // else we create a new permissions set. \n else {\n role.permissions = newPermissions;\n }\n\n return await this.repo.save(role);\n }\n\n async removePermissionsFromRole(roleName: string, permissionNames: string[]): Promise<RoleMetadata> {\n\n // load the role with the respective permissions.\n const role = await this.repo.findOne({\n where: {\n name: roleName\n },\n relations: {\n permissions: true\n }\n });\n\n if (!role) {\n throw new Error(ERROR_MESSAGES.ROLE_NOT_FOUND(roleName));\n }\n\n // modify the permissions array.\n role.permissions = role.permissions.filter(permission => !permissionNames.includes(permission.name));\n\n return await this.repo.save(role);\n }\n\n private async preloadPermissionByName(name: string): Promise<PermissionMetadata> {\n const existingPermission = await this.permissionRepository.findOne({\n where: { name },\n });\n if (!existingPermission) {\n throw new NotFoundException(ERROR_MESSAGES.PERMISSION_NOT_EXIST(name));\n }\n return existingPermission;\n }\n\n\n}\n"]}
1
+ {"version":3,"file":"role-metadata.service.js","sourceRoot":"","sources":["../../src/services/role-metadata.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable, Logger, NotFoundException } from '@nestjs/common';\nimport { ModuleRef } from \"@nestjs/core\";\nimport { InjectEntityManager } from '@nestjs/typeorm';\nimport { CRUDService } from 'src/services/crud.service';\nimport { EntityManager, In } from 'typeorm';\n\nimport { ERROR_MESSAGES } from 'src/constants/error-messages';\nimport { PermissionMetadataRepository } from 'src/repository/permission-metadata.repository';\nimport { RoleMetadataRepository } from 'src/repository/role-metadata.repository';\nimport { CreateRoleMetadataDto } from '../dtos/create-role-metadata.dto';\nimport { PermissionMetadata } from '../entities/permission-metadata.entity';\nimport { RoleMetadata } from '../entities/role-metadata.entity';\n\n@Injectable()\nexport class RoleMetadataService extends CRUDService<RoleMetadata> {\n private readonly logger = new Logger(RoleMetadataService.name);\n private readonly permissionAssignmentBatchSize = 500;\n\n constructor(\n @InjectEntityManager()\n readonly entityManager: EntityManager,\n // @InjectRepository(RoleMetadata, 'default')\n // readonly repo: Repository<RoleMetadata>,\n readonly repo: RoleMetadataRepository,\n // @InjectRepository(PermissionMetadata)\n // private readonly permissionRepository: Repository<PermissionMetadata>,\n readonly permissionRepository: PermissionMetadataRepository,\n readonly moduleRef: ModuleRef\n\n ) {\n super(entityManager, repo, 'roleMetadata', 'solid-core', moduleRef);\n }\n\n async findRoleByName(roleName: string) {\n const entity = await this.repo.findOne({\n where: {\n name: roleName\n },\n relations: {\n permissions: true\n }\n });\n if (!entity) {\n throw new NotFoundException(`Entity #${roleName} not found`);\n }\n return entity;\n }\n\n async assertRoleExistsByName(roleName: string): Promise<void> {\n const entity = await this.repo.findOne({\n where: {\n name: roleName\n },\n select: {\n id: true\n }\n });\n if (!entity) {\n throw new NotFoundException(`Entity #${roleName} not found`);\n }\n }\n\n // OK\n async createRolesIfNotExists(roles: CreateRoleMetadataDto[]) {\n for (let id = 0; id < roles.length; id++) {\n try {\n const roleObj = roles[id];\n // this.logger.log(`Resolving role: ${JSON.stringify(roleObj)}`);\n\n const existingRole = await this.repo.findOne({\n where: {\n name: roleObj.name,\n },\n relations: {},\n });\n\n // Create only if not existing already.\n if (!existingRole) {\n this.logger.debug(`Role ${roleObj.name} does not exist, hence creating`);\n\n let permissions = [];\n\n if (roleObj.permissions && roleObj.permissions.length > 0) {\n await Promise.all(\n roleObj.permissions.map(permission => this.preloadPermissionByName(permission.name)),\n );\n }\n\n // const role = this.repo.create({ ...roleObj, permissions });\n const role = this.repo.create({ ...roleObj });\n await this.repo.save(role);\n } else {\n /*\n this.logger.debug(`Role ${roleObj.name} already exists`);\n const existingPermissions = existingRole.permissions.map(permission => permission.name);\n const newPermissions = roleObj.permissions.map(permission => permission.name);\n const permissionsToAdd = newPermissions.filter(permission => !existingPermissions.includes(permission));\n const permissionsToRemove = existingPermissions.filter(permission => !newPermissions.includes(permission));\n this.logger.debug(`Permissions to add: ${JSON.stringify(permissionsToAdd)}`);\n if (permissionsToAdd.length > 0) {\n await this.addPermissionsToRole(roleObj.name, permissionsToAdd);\n }\n this.logger.debug(`Permissions to remove: ${JSON.stringify(permissionsToRemove)}`);\n if (permissionsToRemove.length > 0) {\n await this.removePermissionsFromRole(roleObj.name, permissionsToRemove);\n }\n */\n }\n } catch (error: any) {\n this.logger.error(error);\n }\n }\n }\n\n async addAllPermissionsToRole(roleName: string): Promise<RoleMetadata> {\n return await this._addPermissionsToRole(roleName, null);\n }\n\n async addPermissionsToRole(roleName: string, permissionNames: string[]): Promise<RoleMetadata> {\n return await this._addPermissionsToRole(roleName, permissionNames);\n }\n\n async addPermissionToRole(roleName: string, permissionName: string[]) {\n return await this._addPermissionsToRole(roleName, permissionName);\n }\n\n private async _addPermissionsToRole(roleName: string, permissionNames: string[]): Promise<RoleMetadata> {\n const role = await this.repo.findOne({\n where: { name: roleName },\n select: {\n id: true,\n name: true,\n }\n });\n if (!role) {\n throw new Error(`Role '${roleName}' not found.`);\n }\n\n const joinInfo = this.getRolePermissionJoinInfo();\n const targetPermissions = await this.loadTargetPermissions(permissionNames);\n\n if (!permissionNames || permissionNames.length === 0) {\n await this.entityManager\n .createQueryBuilder()\n .delete()\n .from(joinInfo.tableName)\n .where(`${joinInfo.roleIdColumn} = :roleId`, { roleId: role.id })\n .execute();\n\n await this.insertRolePermissionMappings(role.id, targetPermissions.map((permission) => permission.id), joinInfo);\n }\n else {\n const existingRows = await this.entityManager\n .createQueryBuilder()\n .select(joinInfo.permissionIdColumn, 'permissionId')\n .from(joinInfo.tableName, 'role_permission')\n .where(`${joinInfo.roleIdColumn} = :roleId`, { roleId: role.id })\n .getRawMany();\n\n const existingPermissionIds = new Set(existingRows.map((row) => Number(row.permissionId)));\n const missingPermissionIds = targetPermissions\n .map((permission) => permission.id)\n .filter((permissionId) => !existingPermissionIds.has(permissionId));\n\n if (missingPermissionIds.length > 0) {\n await this.insertRolePermissionMappings(role.id, missingPermissionIds, joinInfo);\n }\n }\n\n return await this.findRoleByName(roleName);\n }\n\n async removePermissionsFromRole(roleName: string, permissionNames: string[]): Promise<RoleMetadata> {\n\n // load the role with the respective permissions.\n const role = await this.repo.findOne({\n where: {\n name: roleName\n },\n relations: {\n permissions: true\n }\n });\n\n if (!role) {\n throw new Error(ERROR_MESSAGES.ROLE_NOT_FOUND(roleName));\n }\n\n // modify the permissions array.\n role.permissions = role.permissions.filter(permission => !permissionNames.includes(permission.name));\n\n return await this.repo.save(role);\n }\n\n private async preloadPermissionByName(name: string): Promise<PermissionMetadata> {\n const existingPermission = await this.permissionRepository.findOne({\n where: { name },\n });\n if (!existingPermission) {\n throw new NotFoundException(ERROR_MESSAGES.PERMISSION_NOT_EXIST(name));\n }\n return existingPermission;\n }\n\n private async loadTargetPermissions(permissionNames: string[] | null): Promise<PermissionMetadata[]> {\n if (permissionNames && permissionNames.length !== 0) {\n const uniquePermissionNames = [...new Set(permissionNames)];\n const permissions = await this.permissionRepository.find({ where: { name: In(uniquePermissionNames) } });\n if (permissions.length !== uniquePermissionNames.length) {\n throw new Error(`One or more permissions not found.`);\n }\n return permissions;\n }\n\n const permissions = await this.permissionRepository.find();\n if (permissions.length === 0) {\n throw new Error(`No permissions configured in the system. Did you forget to run the PermissionSeederService?`);\n }\n return permissions;\n }\n\n private getRolePermissionJoinInfo(): { tableName: string; roleIdColumn: string; permissionIdColumn: string } {\n const relation = this.repo.metadata.relations.find((candidate) => candidate.propertyName === 'permissions');\n if (!relation?.junctionEntityMetadata) {\n throw new Error('Role-permission join table metadata not found.');\n }\n\n const roleIdColumn = relation.joinColumns[0]?.databaseName;\n const permissionIdColumn = relation.inverseJoinColumns[0]?.databaseName;\n if (!roleIdColumn || !permissionIdColumn) {\n throw new Error('Role-permission join table column metadata not found.');\n }\n\n return {\n tableName: relation.junctionEntityMetadata.tableName,\n roleIdColumn,\n permissionIdColumn,\n };\n }\n\n private async insertRolePermissionMappings(\n roleId: number,\n permissionIds: number[],\n joinInfo: { tableName: string; roleIdColumn: string; permissionIdColumn: string },\n ): Promise<void> {\n for (let index = 0; index < permissionIds.length; index += this.permissionAssignmentBatchSize) {\n const batch = permissionIds.slice(index, index + this.permissionAssignmentBatchSize);\n await this.entityManager\n .createQueryBuilder()\n .insert()\n .into(joinInfo.tableName)\n .values(\n batch.map((permissionId) => ({\n [joinInfo.roleIdColumn]: roleId,\n [joinInfo.permissionIdColumn]: permissionId,\n })),\n )\n .execute();\n }\n }\n\n\n}\n"]}
@@ -21,6 +21,12 @@ export declare class ViewMetadataService extends CRUDService<ViewMetadata> {
21
21
  private readonly modelMetadataRepo;
22
22
  private readonly modelMetadataHelperService;
23
23
  readonly moduleRef: ModuleRef;
24
+ private readonly comparableFields;
25
+ private readonly relationCompareFields;
26
+ private readonly jsonCompareFields;
27
+ private normalizeJsonFieldValue;
28
+ private getCanonicalJsonFieldString;
29
+ private hasChanges;
24
30
  constructor(actionMetadataService: ActionMetadataService, menuItemMetadataService: MenuItemMetadataService, introspectService: SolidIntrospectService, userViewMetadataService: UserViewMetadataService, entityManager: EntityManager, repo: ViewMetadataRepository, modelMetadataRepo: ModelMetadataRepository, modelMetadataHelperService: ModelMetadataHelperService, moduleRef: ModuleRef);
25
31
  private readonly logger;
26
32
  private getEntityRecordsInAllLocales;
@@ -1 +1 @@
1
- {"version":3,"file":"view-metadata.service.d.ts","sourceRoot":"","sources":["../../src/services/view-metadata.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAIxC,OAAO,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAEvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AACnF,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAGvE,qBACa,mBAAoB,SAAQ,WAAW,CAAC,YAAY,CAAC;IAE9D,QAAQ,CAAC,qBAAqB,EAAE,qBAAqB;IACrD,QAAQ,CAAC,uBAAuB,EAAE,uBAAuB;IACzD,QAAQ,CAAC,iBAAiB,EAAE,sBAAsB;IAClD,QAAQ,CAAC,uBAAuB,EAAE,uBAAuB;IAEzD,QAAQ,CAAC,aAAa,EAAE,aAAa;IAErC,QAAQ,CAAC,IAAI,EAAE,sBAAsB;IAMrC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,0BAA0B;IAC3C,QAAQ,CAAC,SAAS,EAAE,SAAS;gBAfpB,qBAAqB,EAAE,qBAAqB,EAC5C,uBAAuB,EAAE,uBAAuB,EAChD,iBAAiB,EAAE,sBAAsB,EACzC,uBAAuB,EAAE,uBAAuB,EAEhD,aAAa,EAAE,aAAa,EAE5B,IAAI,EAAE,sBAAsB,EAMpB,iBAAiB,EAAE,uBAAuB,EAC1C,0BAA0B,EAAE,0BAA0B,EAC9D,SAAS,EAAE,SAAS;IAK/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;YAGjD,4BAA4B;IA8DpC,SAAS,CAAC,KAAK,KAAA,EAAE,UAAU,KAAA;;;;;;;;;YAsSnB,kBAAkB;IAyB1B,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,KAAK;IAU7C,MAAM,CAAC,kBAAkB,EAAE,qBAAqB;IAgBhD,kBAAkB,CAAC,kBAAkB,EAAE,qBAAqB;CAgBnE"}
1
+ {"version":3,"file":"view-metadata.service.d.ts","sourceRoot":"","sources":["../../src/services/view-metadata.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAIxC,OAAO,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAEvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AACnF,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAGvE,qBACa,mBAAoB,SAAQ,WAAW,CAAC,YAAY,CAAC;IAsE9D,QAAQ,CAAC,qBAAqB,EAAE,qBAAqB;IACrD,QAAQ,CAAC,uBAAuB,EAAE,uBAAuB;IACzD,QAAQ,CAAC,iBAAiB,EAAE,sBAAsB;IAClD,QAAQ,CAAC,uBAAuB,EAAE,uBAAuB;IAEzD,QAAQ,CAAC,aAAa,EAAE,aAAa;IAErC,QAAQ,CAAC,IAAI,EAAE,sBAAsB;IAMrC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,0BAA0B;IAC3C,QAAQ,CAAC,SAAS,EAAE,SAAS;IApF/B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAQtB;IACX,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAgC;IACtE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAkC;IAEpE,OAAO,CAAC,uBAAuB;IAgC/B,OAAO,CAAC,2BAA2B;IAInC,OAAO,CAAC,UAAU;gBAqBP,qBAAqB,EAAE,qBAAqB,EAC5C,uBAAuB,EAAE,uBAAuB,EAChD,iBAAiB,EAAE,sBAAsB,EACzC,uBAAuB,EAAE,uBAAuB,EAEhD,aAAa,EAAE,aAAa,EAE5B,IAAI,EAAE,sBAAsB,EAMpB,iBAAiB,EAAE,uBAAuB,EAC1C,0BAA0B,EAAE,0BAA0B,EAC9D,SAAS,EAAE,SAAS;IAK/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;YAGjD,4BAA4B;IA8DpC,SAAS,CAAC,KAAK,KAAA,EAAE,UAAU,KAAA;;;;;;;;;YAsSnB,kBAAkB;IAyB1B,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,KAAK;IAU7C,MAAM,CAAC,kBAAkB,EAAE,qBAAqB;IA0BhD,kBAAkB,CAAC,kBAAkB,EAAE,qBAAqB;CAgBnE"}
@@ -30,6 +30,52 @@ const menu_item_metadata_service_1 = require("./menu-item-metadata.service");
30
30
  const solid_introspect_service_1 = require("./solid-introspect.service");
31
31
  const user_view_metadata_service_1 = require("./user-view-metadata.service");
32
32
  let ViewMetadataService = ViewMetadataService_1 = class ViewMetadataService extends crud_service_1.CRUDService {
33
+ normalizeJsonFieldValue(value) {
34
+ if (typeof value === 'string') {
35
+ const trimmedValue = value.trim();
36
+ if ((trimmedValue.startsWith('{') && trimmedValue.endsWith('}')) ||
37
+ (trimmedValue.startsWith('[') && trimmedValue.endsWith(']'))) {
38
+ try {
39
+ return this.normalizeJsonFieldValue(JSON.parse(trimmedValue));
40
+ }
41
+ catch {
42
+ return value;
43
+ }
44
+ }
45
+ return value;
46
+ }
47
+ if (Array.isArray(value)) {
48
+ return value.map((item) => this.normalizeJsonFieldValue(item));
49
+ }
50
+ if (value && typeof value === 'object') {
51
+ return Object.keys(value)
52
+ .sort()
53
+ .reduce((result, key) => {
54
+ result[key] = this.normalizeJsonFieldValue(value[key]);
55
+ return result;
56
+ }, {});
57
+ }
58
+ return value;
59
+ }
60
+ getCanonicalJsonFieldString(value) {
61
+ return JSON.stringify(this.normalizeJsonFieldValue(value) ?? null);
62
+ }
63
+ hasChanges(existingSolidView, updateSolidViewDto) {
64
+ return this.comparableFields.some((key) => {
65
+ const value = updateSolidViewDto[key];
66
+ if (typeof value === 'undefined') {
67
+ return false;
68
+ }
69
+ if (this.relationCompareFields.has(key)) {
70
+ const relationValue = value;
71
+ return existingSolidView[key]?.id !== relationValue?.id;
72
+ }
73
+ if (this.jsonCompareFields.has(key)) {
74
+ return this.getCanonicalJsonFieldString(existingSolidView[key]) !== this.getCanonicalJsonFieldString(value);
75
+ }
76
+ return existingSolidView[key] !== value;
77
+ });
78
+ }
33
79
  constructor(actionMetadataService, menuItemMetadataService, introspectService, userViewMetadataService, entityManager, repo, modelMetadataRepo, modelMetadataHelperService, moduleRef) {
34
80
  super(entityManager, repo, 'viewMetadata', 'solid-core', moduleRef);
35
81
  this.actionMetadataService = actionMetadataService;
@@ -41,6 +87,17 @@ let ViewMetadataService = ViewMetadataService_1 = class ViewMetadataService exte
41
87
  this.modelMetadataRepo = modelMetadataRepo;
42
88
  this.modelMetadataHelperService = modelMetadataHelperService;
43
89
  this.moduleRef = moduleRef;
90
+ this.comparableFields = [
91
+ 'name',
92
+ 'displayName',
93
+ 'type',
94
+ 'context',
95
+ 'layout',
96
+ 'module',
97
+ 'model',
98
+ ];
99
+ this.relationCompareFields = new Set(['module', 'model']);
100
+ this.jsonCompareFields = new Set(['layout', 'context']);
44
101
  this.logger = new common_1.Logger(ViewMetadataService_1.name);
45
102
  }
46
103
  async getEntityRecordsInAllLocales(modelName, id, defaultEntityLocaleIdFromQuery) {
@@ -289,8 +346,16 @@ let ViewMetadataService = ViewMetadataService_1 = class ViewMetadataService exte
289
346
  return entity;
290
347
  }
291
348
  async upsert(updateSolidViewDto) {
292
- const existingSolidView = await this.findOneByUserKey(updateSolidViewDto.name);
349
+ const existingSolidView = await this.findOneByUserKey(updateSolidViewDto.name, {
350
+ module: true,
351
+ model: true,
352
+ });
293
353
  if (existingSolidView) {
354
+ const hasChanges = this.hasChanges(existingSolidView, updateSolidViewDto);
355
+ if (!hasChanges) {
356
+ this.logger.debug(`Skipping view upsert for ${updateSolidViewDto.name}; no changes detected.`);
357
+ return existingSolidView;
358
+ }
294
359
  const updatedSolidViewDto = { ...existingSolidView, ...updateSolidViewDto };
295
360
  return this.repo.save(updatedSolidViewDto);
296
361
  }