@solidxai/core 0.1.10-beta.24 → 0.1.10-beta.26

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 (230) hide show
  1. package/dist/controllers/action-metadata.controller.d.ts +1 -20
  2. package/dist/controllers/action-metadata.controller.d.ts.map +1 -1
  3. package/dist/controllers/agent-event.controller.d.ts +1 -20
  4. package/dist/controllers/agent-event.controller.d.ts.map +1 -1
  5. package/dist/controllers/agent-session.controller.d.ts +1 -20
  6. package/dist/controllers/agent-session.controller.d.ts.map +1 -1
  7. package/dist/controllers/ai-interaction.controller.d.ts +1 -20
  8. package/dist/controllers/ai-interaction.controller.d.ts.map +1 -1
  9. package/dist/controllers/chatter-message-details.controller.d.ts +1 -20
  10. package/dist/controllers/chatter-message-details.controller.d.ts.map +1 -1
  11. package/dist/controllers/chatter-message.controller.d.ts +1 -20
  12. package/dist/controllers/chatter-message.controller.d.ts.map +1 -1
  13. package/dist/controllers/dashboard-user-layout.controller.d.ts +1 -20
  14. package/dist/controllers/dashboard-user-layout.controller.d.ts.map +1 -1
  15. package/dist/controllers/email-template.controller.d.ts +1 -20
  16. package/dist/controllers/email-template.controller.d.ts.map +1 -1
  17. package/dist/controllers/export-template.controller.d.ts +1 -20
  18. package/dist/controllers/export-template.controller.d.ts.map +1 -1
  19. package/dist/controllers/export-transaction.controller.d.ts +1 -20
  20. package/dist/controllers/export-transaction.controller.d.ts.map +1 -1
  21. package/dist/controllers/import-transaction-error-log.controller.d.ts +1 -20
  22. package/dist/controllers/import-transaction-error-log.controller.d.ts.map +1 -1
  23. package/dist/controllers/import-transaction.controller.d.ts +1 -20
  24. package/dist/controllers/import-transaction.controller.d.ts.map +1 -1
  25. package/dist/controllers/list-of-values.controller.d.ts +1 -20
  26. package/dist/controllers/list-of-values.controller.d.ts.map +1 -1
  27. package/dist/controllers/locale.controller.d.ts +1 -20
  28. package/dist/controllers/locale.controller.d.ts.map +1 -1
  29. package/dist/controllers/mcp-audit-log.controller.d.ts +1 -20
  30. package/dist/controllers/mcp-audit-log.controller.d.ts.map +1 -1
  31. package/dist/controllers/media.controller.d.ts +1 -20
  32. package/dist/controllers/media.controller.d.ts.map +1 -1
  33. package/dist/controllers/menu-item-metadata.controller.d.ts +1 -20
  34. package/dist/controllers/menu-item-metadata.controller.d.ts.map +1 -1
  35. package/dist/controllers/model-metadata.controller.d.ts +10 -30
  36. package/dist/controllers/model-metadata.controller.d.ts.map +1 -1
  37. package/dist/controllers/model-metadata.controller.js +2 -2
  38. package/dist/controllers/model-sequence.controller.d.ts +1 -20
  39. package/dist/controllers/model-sequence.controller.d.ts.map +1 -1
  40. package/dist/controllers/module-metadata-explorer.controller.d.ts +171 -0
  41. package/dist/controllers/module-metadata-explorer.controller.d.ts.map +1 -0
  42. package/dist/controllers/module-metadata-explorer.controller.js +143 -0
  43. package/dist/controllers/module-metadata-explorer.controller.js.map +1 -0
  44. package/dist/controllers/module-metadata.controller.d.ts +5 -0
  45. package/dist/controllers/module-metadata.controller.d.ts.map +1 -1
  46. package/dist/controllers/module-metadata.controller.js +12 -0
  47. package/dist/controllers/module-metadata.controller.js.map +1 -1
  48. package/dist/controllers/module-package.controller.d.ts +622 -0
  49. package/dist/controllers/module-package.controller.d.ts.map +1 -0
  50. package/dist/controllers/module-package.controller.js +150 -0
  51. package/dist/controllers/module-package.controller.js.map +1 -0
  52. package/dist/controllers/mq-message-queue.controller.d.ts +1 -20
  53. package/dist/controllers/mq-message-queue.controller.d.ts.map +1 -1
  54. package/dist/controllers/mq-message.controller.d.ts +1 -20
  55. package/dist/controllers/mq-message.controller.d.ts.map +1 -1
  56. package/dist/controllers/permission-metadata.controller.d.ts +1 -20
  57. package/dist/controllers/permission-metadata.controller.d.ts.map +1 -1
  58. package/dist/controllers/role-metadata.controller.d.ts +1 -20
  59. package/dist/controllers/role-metadata.controller.d.ts.map +1 -1
  60. package/dist/controllers/saved-filters.controller.d.ts +1 -20
  61. package/dist/controllers/saved-filters.controller.d.ts.map +1 -1
  62. package/dist/controllers/scheduled-job.controller.d.ts +1 -20
  63. package/dist/controllers/scheduled-job.controller.d.ts.map +1 -1
  64. package/dist/controllers/security-rule.controller.d.ts +1 -20
  65. package/dist/controllers/security-rule.controller.d.ts.map +1 -1
  66. package/dist/controllers/sms-template.controller.d.ts +1 -20
  67. package/dist/controllers/sms-template.controller.d.ts.map +1 -1
  68. package/dist/controllers/user-activity-history.controller.d.ts +1 -20
  69. package/dist/controllers/user-activity-history.controller.d.ts.map +1 -1
  70. package/dist/controllers/user-view-metadata.controller.d.ts +1 -20
  71. package/dist/controllers/user-view-metadata.controller.d.ts.map +1 -1
  72. package/dist/controllers/user.controller.d.ts +1 -20
  73. package/dist/controllers/user.controller.d.ts.map +1 -1
  74. package/dist/controllers/view-metadata.controller.d.ts +1 -20
  75. package/dist/controllers/view-metadata.controller.d.ts.map +1 -1
  76. package/dist/dtos/confirm-module-package-import.dto.d.ts +4 -0
  77. package/dist/dtos/confirm-module-package-import.dto.d.ts.map +1 -0
  78. package/dist/dtos/confirm-module-package-import.dto.js +34 -0
  79. package/dist/dtos/confirm-module-package-import.dto.js.map +1 -0
  80. package/dist/dtos/metadata-explorer-references-query.dto.d.ts +10 -0
  81. package/dist/dtos/metadata-explorer-references-query.dto.d.ts.map +1 -0
  82. package/dist/dtos/metadata-explorer-references-query.dto.js +69 -0
  83. package/dist/dtos/metadata-explorer-references-query.dto.js.map +1 -0
  84. package/dist/dtos/metadata-explorer-search-query.dto.d.ts +8 -0
  85. package/dist/dtos/metadata-explorer-search-query.dto.d.ts.map +1 -0
  86. package/dist/dtos/metadata-explorer-search-query.dto.js +55 -0
  87. package/dist/dtos/metadata-explorer-search-query.dto.js.map +1 -0
  88. package/dist/dtos/metadata-explorer-write.dto.d.ts +4 -0
  89. package/dist/dtos/metadata-explorer-write.dto.d.ts.map +1 -0
  90. package/dist/dtos/metadata-explorer-write.dto.js +30 -0
  91. package/dist/dtos/metadata-explorer-write.dto.js.map +1 -0
  92. package/dist/dtos/run-module-package-build.dto.d.ts +5 -0
  93. package/dist/dtos/run-module-package-build.dto.d.ts.map +1 -0
  94. package/dist/dtos/run-module-package-build.dto.js +38 -0
  95. package/dist/dtos/run-module-package-build.dto.js.map +1 -0
  96. package/dist/dtos/run-module-package-seed.dto.d.ts +4 -0
  97. package/dist/dtos/run-module-package-seed.dto.d.ts.map +1 -0
  98. package/dist/dtos/run-module-package-seed.dto.js +34 -0
  99. package/dist/dtos/run-module-package-seed.dto.js.map +1 -0
  100. package/dist/helpers/command.service.js +1 -1
  101. package/dist/helpers/command.service.js.map +1 -1
  102. package/dist/helpers/module-metadata-helper.service.d.ts +2 -0
  103. package/dist/helpers/module-metadata-helper.service.d.ts.map +1 -1
  104. package/dist/helpers/module-metadata-helper.service.js +14 -12
  105. package/dist/helpers/module-metadata-helper.service.js.map +1 -1
  106. package/dist/helpers/module.helper.d.ts.map +1 -1
  107. package/dist/helpers/module.helper.js +11 -6
  108. package/dist/helpers/module.helper.js.map +1 -1
  109. package/dist/helpers/typeorm-migration-helpers.d.ts +12 -0
  110. package/dist/helpers/typeorm-migration-helpers.d.ts.map +1 -0
  111. package/dist/helpers/typeorm-migration-helpers.js +119 -0
  112. package/dist/helpers/typeorm-migration-helpers.js.map +1 -0
  113. package/dist/index.d.ts +8 -0
  114. package/dist/index.d.ts.map +1 -1
  115. package/dist/index.js +8 -0
  116. package/dist/index.js.map +1 -1
  117. package/dist/seeders/module-metadata-seeder.service.d.ts +1 -0
  118. package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
  119. package/dist/seeders/module-metadata-seeder.service.js +57 -38
  120. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  121. package/dist/seeders/module-test-data.service.js +1 -1
  122. package/dist/seeders/module-test-data.service.js.map +1 -1
  123. package/dist/seeders/seed-data/solid-core-metadata.json +2364 -1106
  124. package/dist/services/crud-helper.service.d.ts +18 -0
  125. package/dist/services/crud-helper.service.d.ts.map +1 -1
  126. package/dist/services/crud-helper.service.js +67 -1
  127. package/dist/services/crud-helper.service.js.map +1 -1
  128. package/dist/services/crud.service.d.ts +1 -21
  129. package/dist/services/crud.service.d.ts.map +1 -1
  130. package/dist/services/crud.service.js +23 -87
  131. package/dist/services/crud.service.js.map +1 -1
  132. package/dist/services/genai/ingest-metadata.service.js +2 -2
  133. package/dist/services/genai/ingest-metadata.service.js.map +1 -1
  134. package/dist/services/media.service.d.ts +1 -20
  135. package/dist/services/media.service.d.ts.map +1 -1
  136. package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts.map +1 -1
  137. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +31 -2
  138. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
  139. package/dist/services/mediaStorageProviders/file-storage-provider.d.ts.map +1 -1
  140. package/dist/services/mediaStorageProviders/file-storage-provider.js +5 -1
  141. package/dist/services/mediaStorageProviders/file-storage-provider.js.map +1 -1
  142. package/dist/services/model-metadata.service.d.ts +15 -30
  143. package/dist/services/model-metadata.service.d.ts.map +1 -1
  144. package/dist/services/model-metadata.service.js +249 -29
  145. package/dist/services/model-metadata.service.js.map +1 -1
  146. package/dist/services/module-metadata-explorer.service.d.ts +161 -0
  147. package/dist/services/module-metadata-explorer.service.d.ts.map +1 -0
  148. package/dist/services/module-metadata-explorer.service.js +485 -0
  149. package/dist/services/module-metadata-explorer.service.js.map +1 -0
  150. package/dist/services/module-metadata.service.d.ts +7 -0
  151. package/dist/services/module-metadata.service.d.ts.map +1 -1
  152. package/dist/services/module-metadata.service.js +121 -2
  153. package/dist/services/module-metadata.service.js.map +1 -1
  154. package/dist/services/module-package.service.d.ts +266 -0
  155. package/dist/services/module-package.service.d.ts.map +1 -0
  156. package/dist/services/module-package.service.js +980 -0
  157. package/dist/services/module-package.service.js.map +1 -0
  158. package/dist/services/settings/default-settings-provider.service.d.ts +18 -0
  159. package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
  160. package/dist/services/settings/default-settings-provider.service.js +10 -0
  161. package/dist/services/settings/default-settings-provider.service.js.map +1 -1
  162. package/dist/solid-core.module.d.ts.map +1 -1
  163. package/dist/solid-core.module.js +10 -2
  164. package/dist/solid-core.module.js.map +1 -1
  165. package/dist-tests/api/authenticate.spec.js +119 -0
  166. package/dist-tests/api/authenticate.spec.js.map +1 -0
  167. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +97 -0
  168. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +1 -0
  169. package/dist-tests/api/ping.spec.js +21 -0
  170. package/dist-tests/api/ping.spec.js.map +1 -0
  171. package/dist-tests/helpers/auth.js +41 -0
  172. package/dist-tests/helpers/auth.js.map +1 -0
  173. package/dist-tests/helpers/env.js +11 -0
  174. package/dist-tests/helpers/env.js.map +1 -0
  175. package/docs/dashboards/AGENTIC_DASHBOARD_IMPLEMENTATION_PLAN.md +438 -0
  176. package/docs/dashboards/dashboard-curl-smoke-tests.txt +146 -0
  177. package/docs/dashboards/delete-legacy-dashboard-metadata.sql +172 -0
  178. package/docs/grouping-enhancements.md +89 -0
  179. package/docs/java-spring/README.md +3 -0
  180. package/docs/java-spring/solid-core-module-deep-dive-report.md +1317 -0
  181. package/docs/module-package-import-handoff.md +691 -0
  182. package/docs/seed-changes.md +65 -0
  183. package/docs/test-data-workflow.md +200 -0
  184. package/docs/type-declaration-import-issue.md +24 -0
  185. package/package.json +2 -1
  186. package/src/controllers/module-metadata-explorer.controller.ts +83 -0
  187. package/src/controllers/module-metadata.controller.ts +6 -0
  188. package/src/controllers/module-package.controller.ts +102 -0
  189. package/src/dtos/confirm-module-package-import.dto.ts +12 -0
  190. package/src/dtos/metadata-explorer-references-query.dto.ts +39 -0
  191. package/src/dtos/metadata-explorer-search-query.dto.ts +28 -0
  192. package/src/dtos/metadata-explorer-write.dto.ts +11 -0
  193. package/src/dtos/run-module-package-build.dto.ts +14 -0
  194. package/src/dtos/run-module-package-seed.dto.ts +12 -0
  195. package/src/helpers/command.service.ts +1 -1
  196. package/src/helpers/module-metadata-helper.service.ts +19 -21
  197. package/src/helpers/module.helper.ts +12 -9
  198. package/src/helpers/typeorm-migration-helpers.ts +163 -0
  199. package/src/index.ts +8 -0
  200. package/src/seeders/module-metadata-seeder.service.ts +63 -38
  201. package/src/seeders/module-test-data.service.ts +1 -1
  202. package/src/seeders/seed-data/solid-core-metadata.json +2365 -1107
  203. package/src/services/crud-helper.service.ts +79 -1
  204. package/src/services/crud.service.ts +26 -119
  205. package/src/services/dashboard-providers/README.md +1 -1
  206. package/src/services/genai/ingest-metadata.service.ts +3 -3
  207. package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +37 -2
  208. package/src/services/mediaStorageProviders/file-storage-provider.ts +5 -1
  209. package/src/services/model-metadata.service.ts +302 -98
  210. package/src/services/module-metadata-explorer.service.ts +670 -0
  211. package/src/services/module-metadata.service.ts +136 -10
  212. package/src/services/module-package.service.ts +1171 -0
  213. package/src/services/settings/default-settings-provider.service.ts +10 -0
  214. package/src/solid-core.module.ts +10 -2
  215. package/.claude/settings.local.json +0 -15
  216. package/CLAUDE.md +0 -26
  217. package/dist/services/database/database-bootstrap.service.d.ts +0 -12
  218. package/dist/services/database/database-bootstrap.service.d.ts.map +0 -1
  219. package/dist/services/database/database-bootstrap.service.js +0 -115
  220. package/dist/services/database/database-bootstrap.service.js.map +0 -1
  221. package/sql/default/mariadb/proc_CleanupModelMetadata.sql +0 -153
  222. package/sql/default/mariadb/proc_CleanupModuleMetadata.sql +0 -56
  223. package/sql/default/mssql/proc_CleanupModuleMetadata.sql +0 -78
  224. package/sql/default/mysql/proc_CleanupModelMetadata.sql +0 -153
  225. package/sql/default/mysql/proc_CleanupModuleMetadata.sql +0 -56
  226. package/sql/default/postgres/proc_CleanupModelMetadata.sql +0 -148
  227. package/sql/default/postgres/proc_CleanupModuleMetadata.sql +0 -50
  228. package/sql/default/postgres/scratchpad.sql.txt +0 -12
  229. package/src/services/1.js +0 -6
  230. package/src/services/database/database-bootstrap.service.ts +0 -91
@@ -282,7 +282,13 @@ export class CrudHelperService {
282
282
  const orderOptionKeys = Object.keys(orderOptions) as Array<keyof typeof orderOptions>;
283
283
  orderOptionKeys.forEach((key) => {
284
284
  const value = orderOptions[key] as 'ASC' | 'DESC';
285
- qb.addOrderBy(`${entityAlias}.${key}`, value);
285
+ const field = String(key);
286
+ if (field.includes('.')) {
287
+ const { alias, property } = this.ensureRelationPathJoined(qb, entityAlias, field.split('.'));
288
+ qb.addOrderBy(`${alias}.${property}`, value);
289
+ } else {
290
+ qb.addOrderBy(`${entityAlias}.${field}`, value);
291
+ }
286
292
  });
287
293
  }
288
294
  }
@@ -743,4 +749,76 @@ export class CrudHelperService {
743
749
  return matchingPermssions.length > 0
744
750
  }
745
751
 
752
+ pagedResponse<T>(offset: number | undefined, limit: number | undefined, count: number, entities: T[]) {
753
+ const safeLimit = limit ?? count ?? 0;
754
+ const safeOffset = offset ?? 0;
755
+ const currentPage = safeLimit ? Math.floor(safeOffset / safeLimit) + 1 : 1;
756
+ const totalPages = safeLimit ? Math.ceil(count / safeLimit) : 1;
757
+ const nextPage = safeLimit && currentPage < totalPages ? currentPage + 1 : null;
758
+ const prevPage = safeLimit && currentPage > 1 ? currentPage - 1 : null;
759
+ return {
760
+ meta: {
761
+ totalRecords: count,
762
+ currentPage,
763
+ nextPage,
764
+ prevPage,
765
+ totalPages,
766
+ perPage: safeLimit ? +safeLimit : 0,
767
+ },
768
+ records: entities,
769
+ };
770
+ }
771
+
772
+ async executeGroupPipeline<T>(
773
+ filterQb: SelectQueryBuilder<T>,
774
+ basicFilterDto: BasicFilterDto,
775
+ alias: string,
776
+ createQbFn: () => Promise<SelectQueryBuilder<T>>,
777
+ postProcessEntities?: (entities: T[]) => Promise<void>
778
+ ): Promise<{ meta: { totalRecords: number }; groupMeta: any[]; groupRecords: any[] }> {
779
+ const groupByFields = this.normalize(basicFilterDto.groupBy);
780
+ if (!groupByFields.length) throw new BadRequestException(ERROR_MESSAGES.INVALID_GROUP_BY_COUNT);
781
+
782
+ if (basicFilterDto.populateGroup) {
783
+ const hasRelationGroup = groupByFields.some(f => f.includes('.'));
784
+ if (hasRelationGroup) throw new BadRequestException('populateGroup is not supported when grouping on relation fields. Fetch group metadata first and retrieve records in a separate call.');
785
+ }
786
+
787
+ const { aliasMap: groupAliasMap, formatMap: groupFormatMap, expressionMap: groupExpressionMap } =
788
+ this.applyGroupBySelections(filterQb, groupByFields, alias);
789
+ const aggregateAliasMap = this.applyAggregates(filterQb, basicFilterDto.aggregates, alias);
790
+ this.applyGroupSortingAndPagination(filterQb, basicFilterDto.sort, { ...groupAliasMap, ...aggregateAliasMap }, basicFilterDto.limit, basicFilterDto.offset);
791
+
792
+ const groupByResult = await filterQb.getRawMany();
793
+ const totalGroups = await this.countGroups(filterQb);
794
+ const aggregateAliasSet = new Set(Object.values(aggregateAliasMap));
795
+
796
+ const groupMeta = [];
797
+ const groupRecords = [];
798
+
799
+ for (const group of groupByResult) {
800
+ groupMeta.push(this.createGroupMeta(group, aggregateAliasSet, groupByFields, groupAliasMap, groupFormatMap));
801
+
802
+ if (basicFilterDto.populateGroup) {
803
+ let groupQb = await createQbFn();
804
+ const { groupBy: _gb, aggregates: _agg, ...rest } = basicFilterDto;
805
+ const groupFilterDto: BasicFilterDto = {
806
+ ...rest,
807
+ ...basicFilterDto.groupFilter,
808
+ groupBy: undefined,
809
+ aggregates: undefined,
810
+ sort: basicFilterDto.groupFilter?.sort,
811
+ };
812
+ groupQb = this.buildFilterQuery(groupQb, groupFilterDto, alias);
813
+ groupQb = this.buildGroupByRecordsQuery(groupQb, group, alias, groupAliasMap, aggregateAliasMap, groupExpressionMap);
814
+ const [entities, count] = await groupQb.getManyAndCount();
815
+ if (postProcessEntities) await postProcessEntities(entities);
816
+ const groupData = this.pagedResponse(basicFilterDto.groupFilter?.offset, basicFilterDto.groupFilter?.limit, count, entities);
817
+ groupRecords.push(this.createGroupRecords(group, aggregateAliasSet, groupData, groupByFields, groupAliasMap, groupFormatMap));
818
+ }
819
+ }
820
+
821
+ return { meta: { totalRecords: totalGroups }, groupMeta, groupRecords };
822
+ }
823
+
746
824
  }
@@ -1,4 +1,4 @@
1
- import { BadRequestException, InternalServerErrorException, NotFoundException } from "@nestjs/common";
1
+ import { BadRequestException, ConflictException, HttpException, InternalServerErrorException, NotFoundException } from "@nestjs/common";
2
2
  import { DiscoveryService, ModuleRef } from "@nestjs/core";
3
3
  import { isArray } from "class-validator";
4
4
  import { CommonEntity } from "../entities/common.entity";
@@ -41,7 +41,6 @@ import { SolidRegistry } from "src/helpers/solid-registry";
41
41
  import { getMediaStorageProvider } from "./mediaStorageProviders";
42
42
  import { ModelMetadataService } from "./model-metadata.service";
43
43
  import { RequestContextService } from "./request-context.service";
44
- import { BasicGroupFilterDto } from "src/dtos/basic-group-filters.dto";
45
44
 
46
45
 
47
46
  export class CRUDService<T extends CommonEntity> { // Add two generic value i.e Person,CreatePersonDto, so we get the proper types in our service
@@ -480,10 +479,10 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
480
479
  return false; // If it is not a partial update, then do not skip computation
481
480
  }
482
481
 
483
- async find(basicFilterDto: BasicFilterDto, solidRequestContext: any = {}) {
482
+ async find(basicFilterDto: BasicFilterDto, solidRequestContext: any = {}): Promise<any> {
484
483
  const alias = 'entity';
485
484
  // Extract the required keys from the input query
486
- let { limit, offset, populateMedia, populateGroup, groupFilter } = basicFilterDto;
485
+ let { limit, offset, populateMedia } = basicFilterDto;
487
486
  const populateUserIdFields = this.crudHelperService.extractUserIdFieldsFromPopulate(basicFilterDto.populate);
488
487
 
489
488
  const { singularName, internationalisation, draftPublishWorkflow } = await this.loadModel();
@@ -502,41 +501,19 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
502
501
  // Create above query on pincode table using query builder
503
502
  var qb: SelectQueryBuilder<T> = await this.repo.createSecurityRuleAwareQueryBuilder(alias)
504
503
 
505
- if (basicFilterDto.groupBy) {
504
+ if (basicFilterDto.groupBy?.length) {
506
505
  const groupFilterQb = (internationalisation && draftPublishWorkflow)
507
506
  ? this.crudHelperService.buildFilterQuery(qb, basicFilterDto, alias, internationalisation, draftPublishWorkflow, this.moduleRef, FilterCombinator.AND, false, false)
508
507
  : this.crudHelperService.buildFilterQuery(qb, basicFilterDto, alias, undefined, undefined, undefined, FilterCombinator.AND, false, false);
509
508
 
510
- const groupByFields = this.crudHelperService.normalize(basicFilterDto.groupBy);
511
- if (!groupByFields.length) {
512
- throw new BadRequestException(ERROR_MESSAGES.INVALID_GROUP_BY_COUNT);
513
- }
514
-
515
- if (basicFilterDto.populateGroup) {
516
- const hasRelationGroup = groupByFields.some(field => field.includes('.'));
517
- if (hasRelationGroup) {
518
- throw new BadRequestException('populateGroup is not supported when grouping on relation fields. Fetch group metadata first and retrieve records in a separate call.');
509
+ return this.crudHelperService.executeGroupPipeline(
510
+ groupFilterQb, basicFilterDto, alias,
511
+ () => this.repo.createSecurityRuleAwareQueryBuilder(alias),
512
+ async (entities) => {
513
+ if (populateUserIdFields?.length) await this.handlePopulateUserIdFields(populateUserIdFields, entities);
514
+ if (populateMedia?.length) await this.handlePopulateMedia(populateMedia, entities);
519
515
  }
520
- }
521
-
522
- const { aliasMap: groupAliasMap, formatMap: groupFormatMap, expressionMap: groupExpressionMap } = this.crudHelperService.applyGroupBySelections(groupFilterQb, groupByFields, alias);
523
- const aggregateAliasMap = this.crudHelperService.applyAggregates(groupFilterQb, basicFilterDto.aggregates, alias);
524
- const sortAliasMap = { ...groupAliasMap, ...aggregateAliasMap };
525
- this.crudHelperService.applyGroupSortingAndPagination(groupFilterQb, basicFilterDto.sort, sortAliasMap, limit, offset);
526
-
527
- const groupByResult = await groupFilterQb.getRawMany();
528
- const totalGroups = await this.crudHelperService.countGroups(groupFilterQb);
529
-
530
- const groupByFieldsOrdered = this.crudHelperService.normalize(basicFilterDto.groupBy || []);
531
- const { groupMeta, groupRecords } = await this.handleGroupFind(groupByResult, groupFilter, populateGroup, alias, populateUserIdFields, populateMedia, basicFilterDto, groupAliasMap, aggregateAliasMap, groupByFieldsOrdered, groupFormatMap, groupExpressionMap);
532
-
533
- return {
534
- meta: {
535
- "totalRecords": totalGroups
536
- },
537
- groupMeta,
538
- groupRecords,
539
- }
516
+ );
540
517
  }
541
518
  else {
542
519
  qb = (internationalisation && draftPublishWorkflow)
@@ -566,78 +543,8 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
566
543
  return this.wrapFindResponse(offset, limit, count, entities);
567
544
  }
568
545
 
569
- private async handleGroupFind(
570
- groupByResult: any[],
571
- groupFilter: BasicGroupFilterDto | undefined,
572
- populateGroup: boolean,
573
- alias: string,
574
- populateUserIdFields: UserIdFields[],
575
- populateMedia: string[],
576
- baseFilterDto: BasicFilterDto,
577
- groupAliasMap: Record<string, string>,
578
- aggregateAliasMap: Record<string, string>,
579
- groupByFieldsOrdered: string[],
580
- groupFormatMap: Record<string, string | undefined>,
581
- groupExpressionMap: Record<string, string>
582
- ) {
583
- const groupMeta = [];
584
- const groupRecords = [];
585
- const aggregateAliasSet = new Set(Object.values(aggregateAliasMap));
586
- // For each group, get the records and the count
587
- for (const group of groupByResult) {
588
- if (populateGroup) {
589
- let groupByQb: SelectQueryBuilder<T> = await this.repo.createSecurityRuleAwareQueryBuilder(alias);
590
- const groupFilterDto: BasicFilterDto = {
591
- ...baseFilterDto,
592
- ...groupFilter,
593
- groupBy: undefined,
594
- aggregates: undefined,
595
- // Only use explicit groupFilter.sort for record ordering; group-level sorts can contain
596
- // group expressions (e.g. createdAt:day) that are invalid on record queries.
597
- sort: groupFilter?.sort,
598
- };
599
- groupByQb = this.crudHelperService.buildFilterQuery(groupByQb, groupFilterDto, alias);
600
- groupByQb = this.crudHelperService.buildGroupByRecordsQuery(groupByQb, group, alias, groupAliasMap, aggregateAliasMap, groupExpressionMap);
601
- const [entities, count] = await groupByQb.getManyAndCount();
602
-
603
- // Populate the entity with the userId fields
604
- if (populateUserIdFields && populateUserIdFields.length > 0) {
605
- await this.handlePopulateUserIdFields(populateUserIdFields, entities);
606
- }
607
-
608
- // Populate the entity with the media
609
- if (populateMedia && populateMedia.length > 0) {
610
- await this.handlePopulateMedia(populateMedia, entities);
611
- }
612
- const groupData = this.wrapFindResponse(groupFilter?.offset, groupFilter?.limit, count, entities);
613
- groupRecords.push(this.crudHelperService.createGroupRecords(group, aggregateAliasSet, groupData, groupByFieldsOrdered, groupAliasMap, groupFormatMap));
614
- }
615
- groupMeta.push(this.crudHelperService.createGroupMeta(group, aggregateAliasSet, groupByFieldsOrdered, groupAliasMap, groupFormatMap));
616
- }
617
- return { groupMeta, groupRecords };
618
- }
619
-
620
546
  private wrapFindResponse(offset: number | undefined, limit: number | undefined, count: number, entities: T[]) {
621
- const safeLimit = limit ?? count ?? 0;
622
- const safeOffset = offset ?? 0;
623
- const currentPage = safeLimit ? Math.floor(safeOffset / safeLimit) + 1 : 1;
624
- const totalPages = safeLimit ? Math.ceil(count / safeLimit) : 1;
625
-
626
- const nextPage = safeLimit && currentPage < totalPages ? currentPage + 1 : null;
627
- const prevPage = safeLimit && currentPage > 1 ? currentPage - 1 : null;
628
-
629
- const r = {
630
- meta: {
631
- totalRecords: count,
632
- currentPage: currentPage,
633
- nextPage: nextPage,
634
- prevPage: prevPage,
635
- totalPages: totalPages,
636
- perPage: safeLimit ? +safeLimit : 0,
637
- },
638
- records: entities
639
- };
640
- return r;
547
+ return this.crudHelperService.pagedResponse(offset, limit, count, entities);
641
548
  }
642
549
 
643
550
  // entities is an array of T
@@ -911,7 +818,7 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
911
818
  });
912
819
 
913
820
  if (!softDeletedRows) {
914
- throw new Error(ERROR_MESSAGES.NO_SOFT_DELETED_RECORD_FOUND);
821
+ throw new NotFoundException(ERROR_MESSAGES.NO_SOFT_DELETED_RECORD_FOUND);
915
822
  }
916
823
 
917
824
  await this.repo.update(id, {
@@ -921,13 +828,13 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
921
828
 
922
829
  return { message: SUCCESS_MESSAGES.RECORD_RECOVERED, data: softDeletedRows };
923
830
  } catch (error: any) {
924
- if (error instanceof QueryFailedError) {
925
- if ((error as any).code === '23505') {
926
- throw new Error(ERROR_MESSAGES.CONFLICTING_RECORD_ON_UNARCHIVE);
927
- }
831
+ if (error instanceof QueryFailedError && (error as any).code === '23505') {
832
+ throw new ConflictException(ERROR_MESSAGES.CONFLICTING_RECORD_ON_UNARCHIVE);
928
833
  }
929
-
930
- throw new Error(error);
834
+ if (error instanceof HttpException) {
835
+ throw error;
836
+ }
837
+ throw new InternalServerErrorException(error?.message ?? String(error));
931
838
  }
932
839
  }
933
840
 
@@ -956,7 +863,7 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
956
863
  });
957
864
 
958
865
  if (softDeletedRows.length === 0) {
959
- throw new Error(ERROR_MESSAGES.NO_SOFT_DELETED_RECORDS_FOUND);
866
+ throw new NotFoundException(ERROR_MESSAGES.NO_SOFT_DELETED_RECORDS_FOUND);
960
867
  }
961
868
 
962
869
  // Recover the specific records by setting deletedAt to null
@@ -967,13 +874,13 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
967
874
 
968
875
  return { message: SUCCESS_MESSAGES.SELECTED_RECORDS_RECOVERED, recoveredIds: ids };
969
876
  } catch (error: any) {
970
- if (error instanceof QueryFailedError) {
971
- if ((error as any).code === "23505") {
972
- throw new Error(ERROR_MESSAGES.CONFLICTING_RECORD_ON_UNARCHIVE);
973
- }
877
+ if (error instanceof QueryFailedError && (error as any).code === "23505") {
878
+ throw new ConflictException(ERROR_MESSAGES.CONFLICTING_RECORD_ON_UNARCHIVE);
974
879
  }
975
-
976
- throw new Error(error);
880
+ if (error instanceof HttpException) {
881
+ throw error;
882
+ }
883
+ throw new InternalServerErrorException(error?.message ?? String(error));
977
884
  }
978
885
  }
979
886
 
@@ -38,7 +38,7 @@ Not persisted:
38
38
 
39
39
  At runtime, metadata is read from file paths resolved by `ModuleMetadataHelperService`:
40
40
 
41
- 1. `module-metadata/<module-name>/<module-name>-metadata.json`
41
+ 1. `src/<module-name>/metadata/<module-name>-metadata.json`
42
42
  2. fallback (solid-core local run): `src/seeders/seed-data/solid-core-metadata.json`
43
43
  3. fallback (consuming project): `node_modules/@solidxai/core/src/seeders/seed-data/solid-core-metadata.json`
44
44
 
@@ -97,14 +97,14 @@ export class IngestMetadataService {
97
97
  for (let i = 0; i < enabledModules.length; i++) {
98
98
  const enabledModule = enabledModules[i];
99
99
  const fileName = `${enabledModule}-metadata.json`;
100
- const enabledModuleSeedFile = `module-metadata/${enabledModule}/${fileName}`;
100
+ const enabledModuleSeedFile = `src/${enabledModule}/metadata/${fileName}`;
101
101
  const fullPath = path.join(process.cwd(), enabledModuleSeedFile);
102
102
  const overallMetadata: any = JSON.parse(fs.readFileSync(fullPath, 'utf-8').toString());
103
103
 
104
104
  const moduleMetadata: CreateModuleMetadataDto = overallMetadata.moduleMetadata;
105
105
 
106
106
  // Manage all the ingestion info file paths...
107
- const enabledModulIngestionInfoFile = `module-metadata/${enabledModule}/genai/${enabledModule}-ingested-info.json`;
107
+ const enabledModulIngestionInfoFile = `src/${enabledModule}/metadata/genai/${enabledModule}-ingested-info.json`;
108
108
  const enabledModulIngestionInfoFullPath = path.join(process.cwd(), enabledModulIngestionInfoFile);
109
109
  const ingestionInfo: ModuleRAGIngestionInfo = fs.existsSync(enabledModulIngestionInfoFullPath) ? JSON.parse(fs.readFileSync(enabledModulIngestionInfoFullPath, 'utf-8').toString()) : {};
110
110
  const enabledModulIngestionInfoDir = path.dirname(enabledModulIngestionInfoFullPath);
@@ -795,4 +795,4 @@ ${JSON.stringify(model)}
795
795
  this.logger.log(`[OK] Ingested field ${moduleName}.${modelName}.${fieldName} → id=${newId}, hash=${schemaHash.slice(0, 8)}…`);
796
796
  return newId;
797
797
  }
798
- }
798
+ }
@@ -21,8 +21,43 @@ export class FileS3StorageProvider<T> implements MediaStorageProvider<T> {
21
21
  readonly mediaRepository: MediaRepository,
22
22
  ) { }
23
23
 
24
- storeStreams(streamPairs: [Readable, string][], entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]> {
25
- throw new Error("Method not implemented.");
24
+ async storeStreams(streamPairs: [Readable, string][], entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]> {
25
+ const isSupportedEntity = entity instanceof CommonEntity
26
+ || entity instanceof LegacyCommonEntityWithExistingId
27
+ || entity instanceof LegacyCommonEntityWithGeneratedId;
28
+ if (!isSupportedEntity) {
29
+ throw new Error("Entity must be an instance of CommonEntity, LegacyCommonEntityWithExistingId or LegacyCommonEntityWithGeneratedId");
30
+ }
31
+ const result: Media[] = [];
32
+ const storageProvider = mediaFieldMetadata.mediaStorageProvider;
33
+ const region = this.getEffectiveRegion(storageProvider.region);
34
+
35
+ for (const [stream, fileName] of streamPairs) {
36
+ const bucketName = storageProvider.bucketName;
37
+
38
+ // Buffer the stream so we can get the byte count and upload in one pass
39
+ const chunks: Buffer[] = [];
40
+ for await (const chunk of stream) {
41
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
42
+ }
43
+ const fileData = Buffer.concat(chunks);
44
+ const fileSize = fileData.length;
45
+
46
+ await this.s3FileService.write(`${bucketName}:${fileName}`, fileData, { region });
47
+
48
+ const mediaEntity = await this.mediaRepository.createMedia({
49
+ // @ts-ignore
50
+ entityId: entity.id,
51
+ modelMetadataId: mediaFieldMetadata.model.id,
52
+ relativeUri: fileName,
53
+ fileSize,
54
+ mediaStorageProviderMetadataId: mediaFieldMetadata.mediaStorageProvider.id,
55
+ fieldMetadataId: mediaFieldMetadata.id
56
+ }) as unknown as Media;
57
+ result.push(mediaEntity);
58
+ this.logger.debug(`Stored media with`, mediaEntity);
59
+ }
60
+ return result;
26
61
  }
27
62
 
28
63
  async retrieve(entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]> {
@@ -8,6 +8,7 @@ import { MediaRepository } from "src/repository/media.repository";
8
8
  import { DiskFileService } from "src/services/file";
9
9
  import { Readable } from "stream";
10
10
  import * as path from "path";
11
+ import * as fs from "fs";
11
12
  import { SettingService } from "../setting.service";
12
13
  import { DEFAULT_MEDIA_FILE_STORAGE_DIR } from "src/services/settings/default-settings-provider.service";
13
14
  import type { SolidCoreSetting } from "src/services/settings/default-settings-provider.service";
@@ -80,12 +81,15 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
80
81
  for (const pair of streamPairs) {
81
82
  const stream = pair[0];
82
83
  const fileName = pair[1];
83
- await this.fileService.writeStream(this.getFullFilePath(fileName), stream);
84
+ const fullPath = this.getFullFilePath(fileName);
85
+ await this.fileService.writeStream(fullPath, stream);
86
+ const { size: fileSize } = await fs.promises.stat(fullPath);
84
87
  const mediaEntity = await this.mediaRepository.createMedia({
85
88
  //@ts-ignore
86
89
  entityId: entity.id,
87
90
  modelMetadataId: mediaFieldMetadata.model.id,
88
91
  relativeUri: fileName,
92
+ fileSize,
89
93
  mediaStorageProviderMetadataId: mediaFieldMetadata.mediaStorageProvider.id,
90
94
  fieldMetadataId: mediaFieldMetadata.id
91
95
  }) as unknown as Media;