@solidstarters/solid-core 1.2.93 → 1.2.96

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 (164) hide show
  1. package/dist/controllers/action-metadata.controller.d.ts +3 -1
  2. package/dist/controllers/action-metadata.controller.d.ts.map +1 -1
  3. package/dist/controllers/chatter-message-details.controller.d.ts +3 -1
  4. package/dist/controllers/chatter-message-details.controller.d.ts.map +1 -1
  5. package/dist/controllers/chatter-message.controller.d.ts +3 -1
  6. package/dist/controllers/chatter-message.controller.d.ts.map +1 -1
  7. package/dist/controllers/email-template.controller.d.ts +3 -1
  8. package/dist/controllers/email-template.controller.d.ts.map +1 -1
  9. package/dist/controllers/export-template.controller.d.ts +3 -1
  10. package/dist/controllers/export-template.controller.d.ts.map +1 -1
  11. package/dist/controllers/export-transaction.controller.d.ts +3 -1
  12. package/dist/controllers/export-transaction.controller.d.ts.map +1 -1
  13. package/dist/controllers/import-transaction-error-log.controller.d.ts +3 -1
  14. package/dist/controllers/import-transaction-error-log.controller.d.ts.map +1 -1
  15. package/dist/controllers/import-transaction.controller.d.ts +3 -1
  16. package/dist/controllers/import-transaction.controller.d.ts.map +1 -1
  17. package/dist/controllers/list-of-values.controller.d.ts +3 -1
  18. package/dist/controllers/list-of-values.controller.d.ts.map +1 -1
  19. package/dist/controllers/locale.controller.d.ts +43 -0
  20. package/dist/controllers/locale.controller.d.ts.map +1 -0
  21. package/dist/controllers/locale.controller.js +179 -0
  22. package/dist/controllers/locale.controller.js.map +1 -0
  23. package/dist/controllers/media.controller.d.ts +3 -1
  24. package/dist/controllers/media.controller.d.ts.map +1 -1
  25. package/dist/controllers/menu-item-metadata.controller.d.ts +3 -1
  26. package/dist/controllers/menu-item-metadata.controller.d.ts.map +1 -1
  27. package/dist/controllers/mq-message-queue.controller.d.ts +3 -1
  28. package/dist/controllers/mq-message-queue.controller.d.ts.map +1 -1
  29. package/dist/controllers/mq-message.controller.d.ts +3 -1
  30. package/dist/controllers/mq-message.controller.d.ts.map +1 -1
  31. package/dist/controllers/permission-metadata.controller.d.ts +3 -1
  32. package/dist/controllers/permission-metadata.controller.d.ts.map +1 -1
  33. package/dist/controllers/role-metadata.controller.d.ts +3 -1
  34. package/dist/controllers/role-metadata.controller.d.ts.map +1 -1
  35. package/dist/controllers/saved-filters.controller.d.ts +3 -1
  36. package/dist/controllers/saved-filters.controller.d.ts.map +1 -1
  37. package/dist/controllers/security-rule.controller.d.ts +3 -1
  38. package/dist/controllers/security-rule.controller.d.ts.map +1 -1
  39. package/dist/controllers/setting.controller.d.ts +3 -1
  40. package/dist/controllers/setting.controller.d.ts.map +1 -1
  41. package/dist/controllers/sms-template.controller.d.ts +3 -1
  42. package/dist/controllers/sms-template.controller.d.ts.map +1 -1
  43. package/dist/controllers/user-view-metadata.controller.d.ts +3 -1
  44. package/dist/controllers/user-view-metadata.controller.d.ts.map +1 -1
  45. package/dist/controllers/user.controller.d.ts +3 -1
  46. package/dist/controllers/user.controller.d.ts.map +1 -1
  47. package/dist/controllers/view-metadata.controller.d.ts +4 -1
  48. package/dist/controllers/view-metadata.controller.d.ts.map +1 -1
  49. package/dist/dtos/basic-filters.dto.d.ts +2 -0
  50. package/dist/dtos/basic-filters.dto.d.ts.map +1 -1
  51. package/dist/dtos/basic-filters.dto.js +11 -1
  52. package/dist/dtos/basic-filters.dto.js.map +1 -1
  53. package/dist/dtos/create-locale.dto.d.ts +6 -0
  54. package/dist/dtos/create-locale.dto.d.ts.map +1 -0
  55. package/dist/dtos/create-locale.dto.js +43 -0
  56. package/dist/dtos/create-locale.dto.js.map +1 -0
  57. package/dist/dtos/create-model-metadata.dto.d.ts +1 -0
  58. package/dist/dtos/create-model-metadata.dto.d.ts.map +1 -1
  59. package/dist/dtos/create-model-metadata.dto.js +7 -2
  60. package/dist/dtos/create-model-metadata.dto.js.map +1 -1
  61. package/dist/dtos/create-saved-filters.dto.d.ts +1 -1
  62. package/dist/dtos/create-saved-filters.dto.d.ts.map +1 -1
  63. package/dist/dtos/create-saved-filters.dto.js +3 -3
  64. package/dist/dtos/create-saved-filters.dto.js.map +1 -1
  65. package/dist/dtos/update-locale.dto.d.ts +7 -0
  66. package/dist/dtos/update-locale.dto.d.ts.map +1 -0
  67. package/dist/dtos/update-locale.dto.js +48 -0
  68. package/dist/dtos/update-locale.dto.js.map +1 -0
  69. package/dist/dtos/update-saved-filters.dto.d.ts +1 -1
  70. package/dist/dtos/update-saved-filters.dto.d.ts.map +1 -1
  71. package/dist/dtos/update-saved-filters.dto.js +3 -3
  72. package/dist/dtos/update-saved-filters.dto.js.map +1 -1
  73. package/dist/entities/common.entity.d.ts +3 -0
  74. package/dist/entities/common.entity.d.ts.map +1 -1
  75. package/dist/entities/common.entity.js +13 -1
  76. package/dist/entities/common.entity.js.map +1 -1
  77. package/dist/entities/locale.entity.d.ts +7 -0
  78. package/dist/entities/locale.entity.d.ts.map +1 -0
  79. package/dist/entities/locale.entity.js +43 -0
  80. package/dist/entities/locale.entity.js.map +1 -0
  81. package/dist/entities/model-metadata.entity.d.ts +1 -0
  82. package/dist/entities/model-metadata.entity.d.ts.map +1 -1
  83. package/dist/entities/model-metadata.entity.js +5 -1
  84. package/dist/entities/model-metadata.entity.js.map +1 -1
  85. package/dist/entities/saved-filters.entity.d.ts +1 -1
  86. package/dist/entities/saved-filters.entity.d.ts.map +1 -1
  87. package/dist/entities/saved-filters.entity.js +2 -2
  88. package/dist/entities/saved-filters.entity.js.map +1 -1
  89. package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.d.ts.map +1 -1
  90. package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.js.map +1 -1
  91. package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.d.ts.map +1 -1
  92. package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.js.map +1 -1
  93. package/dist/helpers/module-metadata-helper.service.d.ts.map +1 -1
  94. package/dist/helpers/module-metadata-helper.service.js.map +1 -1
  95. package/dist/helpers/solid-registry.d.ts +2 -0
  96. package/dist/helpers/solid-registry.d.ts.map +1 -1
  97. package/dist/helpers/solid-registry.js +5 -0
  98. package/dist/helpers/solid-registry.js.map +1 -1
  99. package/dist/index.d.ts +5 -0
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/index.js +5 -0
  102. package/dist/index.js.map +1 -1
  103. package/dist/seeders/seed-data/solid-core-metadata.json +204 -8
  104. package/dist/services/crud-helper.service.d.ts +2 -1
  105. package/dist/services/crud-helper.service.d.ts.map +1 -1
  106. package/dist/services/crud-helper.service.js +35 -2
  107. package/dist/services/crud-helper.service.js.map +1 -1
  108. package/dist/services/crud.service.d.ts +3 -1
  109. package/dist/services/crud.service.d.ts.map +1 -1
  110. package/dist/services/crud.service.js +22 -4
  111. package/dist/services/crud.service.js.map +1 -1
  112. package/dist/services/locale.service.d.ts +26 -0
  113. package/dist/services/locale.service.d.ts.map +1 -0
  114. package/dist/services/locale.service.js +64 -0
  115. package/dist/services/locale.service.js.map +1 -0
  116. package/dist/services/media.service.d.ts +3 -1
  117. package/dist/services/media.service.d.ts.map +1 -1
  118. package/dist/services/mediaStorageProviders/file-storage-provider.js.map +1 -1
  119. package/dist/services/selection-providers/locale-list-selection-provider.service.d.ts +9 -0
  120. package/dist/services/selection-providers/locale-list-selection-provider.service.d.ts.map +1 -0
  121. package/dist/services/selection-providers/locale-list-selection-provider.service.js +87 -0
  122. package/dist/services/selection-providers/locale-list-selection-provider.service.js.map +1 -0
  123. package/dist/services/view-metadata.service.d.ts +3 -0
  124. package/dist/services/view-metadata.service.d.ts.map +1 -1
  125. package/dist/services/view-metadata.service.js +73 -7
  126. package/dist/services/view-metadata.service.js.map +1 -1
  127. package/dist/solid-core.module.d.ts.map +1 -1
  128. package/dist/solid-core.module.js +8 -0
  129. package/dist/solid-core.module.js.map +1 -1
  130. package/dist/subscribers/model.subscriber.d.ts.map +1 -1
  131. package/dist/subscribers/model.subscriber.js +24 -0
  132. package/dist/subscribers/model.subscriber.js.map +1 -1
  133. package/dist/transformers/datetime-transformer.d.ts +4 -0
  134. package/dist/transformers/datetime-transformer.d.ts.map +1 -0
  135. package/dist/transformers/datetime-transformer.js +11 -0
  136. package/dist/transformers/datetime-transformer.js.map +1 -0
  137. package/dist/tsconfig.tsbuildinfo +1 -1
  138. package/package.json +2 -1
  139. package/src/controllers/locale.controller.ts +94 -0
  140. package/src/dtos/basic-filters.dto.ts +13 -1
  141. package/src/dtos/create-locale.dto.ts +17 -0
  142. package/src/dtos/create-model-metadata.dto.ts +5 -1
  143. package/src/dtos/create-saved-filters.dto.ts +3 -3
  144. package/src/dtos/update-locale.dto.ts +23 -0
  145. package/src/dtos/update-saved-filters.dto.ts +3 -3
  146. package/src/entities/common.entity.ts +11 -1
  147. package/src/entities/locale.entity.ts +14 -0
  148. package/src/entities/model-metadata.entity.ts +3 -0
  149. package/src/entities/saved-filters.entity.ts +1 -1
  150. package/src/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.ts +2 -1
  151. package/src/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.ts +1 -0
  152. package/src/helpers/module-metadata-helper.service.ts +0 -1
  153. package/src/helpers/solid-registry.ts +11 -2
  154. package/src/index.ts +6 -3
  155. package/src/seeders/seed-data/solid-core-metadata.json +204 -8
  156. package/src/services/crud-helper.service.ts +57 -14
  157. package/src/services/crud.service.ts +56 -33
  158. package/src/services/locale.service.ts +37 -0
  159. package/src/services/mediaStorageProviders/file-storage-provider.ts +1 -1
  160. package/src/services/selection-providers/locale-list-selection-provider.service.ts +58 -0
  161. package/src/services/view-metadata.service.ts +179 -14
  162. package/src/solid-core.module.ts +8 -0
  163. package/src/subscribers/model.subscriber.ts +24 -0
  164. package/src/transformers/datetime-transformer.ts +12 -0
@@ -53,7 +53,7 @@ export class CrudHelperService {
53
53
  }
54
54
  else { // Recursively call the applyFilters method to handle nested conditions
55
55
  const joinField = `${alias}.${key}`;
56
- if (!this.isRelationJoined(selectQb, joinField)) selectQb.leftJoin(joinField, key);
56
+ if (!this.isRelationJoined(selectQb, joinField)) selectQb.leftJoin(joinField, key);
57
57
  this.applyFilters(qb, primaryFilterObj, key, selectQb);
58
58
  }
59
59
  });
@@ -155,10 +155,9 @@ export class CrudHelperService {
155
155
  return queryBuilder.expressionMap.joinAttributes.length > 0;
156
156
  }
157
157
 
158
-
159
- buildFilterQuery(qb: SelectQueryBuilder<any>, basicFilterDto: BasicFilterDto, entityAlias: string): SelectQueryBuilder<any> { //TODO : Check how to pass a type to SelectQueryBuilder instead of any
158
+ buildFilterQuery(qb: SelectQueryBuilder<any>, basicFilterDto: BasicFilterDto, entityAlias: string, internationalisation?: boolean, draftPublishWorkflow?: boolean): SelectQueryBuilder<any> { // TODO : Check how to pass a type to SelectQueryBuilder instead of any
160
159
  let { limit, offset, showSoftDeleted, filters } = basicFilterDto;
161
- const { fields, sort, groupBy, populate = [], populateMedia=[] } = basicFilterDto;
160
+ const { fields, sort, groupBy, populate = [], populateMedia = [], locale, status } = basicFilterDto;
162
161
 
163
162
  // Normalize the fields, sort, groupBy and populate options i.e (since they can be either a string or an array of strings, when coming from the request)
164
163
  const normalizedFields = this.normalize(fields);
@@ -187,6 +186,25 @@ export class CrudHelperService {
187
186
  }));
188
187
  }
189
188
 
189
+ let finalLocale = locale
190
+ if (internationalisation) {
191
+ // If locale is not provided in the filter dto, then assume it is the default locale to be used.
192
+ if (!finalLocale) {
193
+ // TODO: get the default locale from the database.
194
+ // This should be replaced with the actual default locale from the database, make sure to cache this request.
195
+ // @Sundaram consult with Oswald and use a registry based approach to fetch the default locale, making sure that it gets cached and we don't have to query again and again.
196
+ finalLocale = 'en';
197
+ }
198
+ qb.andWhere(`${entityAlias}.localeName = :locale`, { locale: finalLocale });
199
+ }
200
+
201
+ if (draftPublishWorkflow && status) {
202
+ if (basicFilterDto.status === 'published') {
203
+ qb.andWhere(`${entityAlias}.publishedAt IS NOT NULL`);
204
+ } else if (basicFilterDto.status === 'draft') {
205
+ qb.andWhere(`${entityAlias}.publishedAt IS NULL`);
206
+ }
207
+ }
190
208
  // Depending upon the select option, apply the select clause
191
209
  if (normalizedFields && normalizedFields.length) {
192
210
  qb.select(normalizedFields.map(field => {
@@ -223,22 +241,22 @@ export class CrudHelperService {
223
241
  qb.addGroupBy(`${entityAlias}.${field}`);
224
242
  });
225
243
  }
226
-
244
+
227
245
  // Apply the pagination options & handle the case when the query has joins
228
246
  if (limit) this.hasJoins(qb) ? qb.take(limit) : qb.limit(limit);
229
- if (offset) this.hasJoins(qb) ? qb.skip(offset): qb.offset(offset);
247
+ if (offset) this.hasJoins(qb) ? qb.skip(offset) : qb.offset(offset);
230
248
  return qb;
231
249
  }
232
250
 
233
251
  additionalRelationsRequiredForMediaPopulation(normalizedPopulateMedia: string[]) {
234
252
  // Populate relations containing the media field
235
253
  return normalizedPopulateMedia
236
- .filter(pm => pm.includes("."))
237
- .map((pm) => {
238
- const mediaPathParts = pm.split('.');
239
- if (mediaPathParts.length <= 1) return pm;
240
- return mediaPathParts.slice(0, -1).join('.');
241
- });
254
+ .filter(pm => pm.includes("."))
255
+ .map((pm) => {
256
+ const mediaPathParts = pm.split('.');
257
+ if (mediaPathParts.length <= 1) return pm;
258
+ return mediaPathParts.slice(0, -1).join('.');
259
+ });
242
260
  }
243
261
 
244
262
  private buildPopulateQuery(normalizedPopulate: string[], entityAlias: string, qb: SelectQueryBuilder<any>) {
@@ -257,12 +275,12 @@ export class CrudHelperService {
257
275
  // Check if the relation is already joined, if not then join it
258
276
  if (!this.isRelationJoined(qb, joinProperty)) {
259
277
  const joinAlias = relationParts.slice(0, i + 1).join('_');
260
- qb.leftJoinAndSelect(joinProperty, joinAlias);
278
+ qb.leftJoinAndSelect(joinProperty, joinAlias);
261
279
  }
262
280
  else {
263
281
  // Since in populate, we are create a unique alias based on the relation path
264
282
  //If the join is already present, it is probably because of the relation being passed in the where filter i.e applyFilters method
265
- qb.addSelect(`${part}`);
283
+ qb.addSelect(`${part}`);
266
284
  }
267
285
  parentAlias = part; // Update the parent alias for the next iteration
268
286
  });
@@ -332,6 +350,31 @@ export class CrudHelperService {
332
350
  };
333
351
  }
334
352
 
353
+ async countGroupedRecords(qb: SelectQueryBuilder<any>, basicFilterDto: BasicFilterDto, entityAlias: string) { //TODO : Check how to pass a type to SelectQueryBuilder instead of any
354
+ const { limit, offset, ...rest } = basicFilterDto;
355
+
356
+ const filteredDto = { ...rest, limit: undefined, offset: undefined };
357
+
358
+ const filteredQB = this.buildFilterQuery(qb, filteredDto as BasicFilterDto, entityAlias);
359
+
360
+ // Select only the group field and count distinct rows
361
+ const groupByField = filteredDto.groupBy;
362
+
363
+ if (!groupByField || (Array.isArray(groupByField) && groupByField.length !== 1)) {
364
+ throw new Error('Exactly one groupBy field is required to count grouped records.');
365
+ }
366
+
367
+ const field = Array.isArray(groupByField) ? groupByField[0] : groupByField;
368
+ const rawResults = await filteredQB
369
+ .select([]) // Remove prior select fields
370
+ .addSelect(`${entityAlias}.${field}`, 'groupField')
371
+ .groupBy(`${entityAlias}.${field}`)
372
+ .limit(undefined) // Important: prevent LIMIT 1 from propagating
373
+ .offset(undefined)
374
+ .getRawMany();
375
+
376
+ return rawResults.length;
377
+ }
335
378
 
336
379
  hasReadPermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
337
380
  const permissionNames = [`${classify(modelName)}Controller.findOne`, `${classify(modelName)}Controller.findMany`];
@@ -52,7 +52,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
52
52
  //We can just have the Model Entity here
53
53
  ) { }
54
54
 
55
- async create(createDto: any, files: Express.Multer.File[] = [],solidRequestContext :any = {}): Promise<T> {
55
+ async create(createDto: any, files: Express.Multer.File[] = [], solidRequestContext: any = {}): Promise<T> {
56
56
  // This class will be extended by the generated service class i.e PersonService
57
57
  // The data required to identify the model and module name will be passed from the generate CrudService subclass
58
58
  //TODO: Algorithm to create the entity
@@ -215,7 +215,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
215
215
  throw new BadRequestException('Forbidden');
216
216
  }
217
217
  }
218
-
218
+
219
219
  const model = await this.modelMetadataService.findOneBySingularName(this.modelName, {
220
220
  fields: {
221
221
  model: true,
@@ -232,10 +232,26 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
232
232
  if (!entity) {
233
233
  throw new Error(`Entity [${this.moduleName}.${this.modelName}] with id ${id} not found`);
234
234
  }
235
+
236
+ // If the model has internationalisation enabled, delete children with defaultEntityLocaleId === this entity's id
237
+ if (model.internationalisation) {
238
+ // Find all child entities where defaultEntityLocaleId === this entity's id
239
+ const childEntities = await this.repo.find({
240
+ where: { defaultEntityLocaleId: id } as any
241
+ });
242
+
243
+ if (childEntities.length > 0) {
244
+ if (model.enableSoftDelete === true) {
245
+ await this.repo.softRemove(childEntities);
246
+ } else {
247
+ await this.repo.remove(childEntities);
248
+ }
249
+ }
250
+ }
251
+
235
252
  if (model.enableSoftDelete === true) {
236
253
  await this.repo.softRemove(entity);
237
254
  return this.repo.save(entity);
238
-
239
255
  } else {
240
256
  return this.repo.remove(entity);
241
257
  }
@@ -365,7 +381,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
365
381
  // Validation against the selectionStatic values. No transformation is required
366
382
  // If the value is not in the selectionStatic values, then throw
367
383
  // Also validate against the selectionType
368
- const options = { ...commonOptions, selectionStaticValues: fieldMetadata.selectionStaticValues, selectionValueType: fieldMetadata.selectionValueType as SelectionValueType, isMultiSelect: fieldMetadata.isMultiSelect};
384
+ const options = { ...commonOptions, selectionStaticValues: fieldMetadata.selectionStaticValues, selectionValueType: fieldMetadata.selectionValueType as SelectionValueType, isMultiSelect: fieldMetadata.isMultiSelect };
369
385
  return new SelectionStaticFieldCrudManager(options);
370
386
  }
371
387
  case SolidFieldType.selectionDynamic: {// [HOLD]
@@ -374,7 +390,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
374
390
  // dataSource: string; // The name of the selection provider
375
391
  // filterSchema : json // This is a custom json object that every data source will handle accordingly. We could validate the query against the selection provider
376
392
  // values : string[]; // The values returned by the selection provider
377
- const options = { ...commonOptions, selectionDynamicProvider: fieldMetadata.selectionDynamicProvider, selectionDynamicProviderCtxt: fieldMetadata.selectionDynamicProviderCtxt, selectionValueType: fieldMetadata.selectionValueType as SelectionValueType, discoveryService: this.discoveryService, isMultiSelect: fieldMetadata.isMultiSelect};
393
+ const options = { ...commonOptions, selectionDynamicProvider: fieldMetadata.selectionDynamicProvider, selectionDynamicProviderCtxt: fieldMetadata.selectionDynamicProviderCtxt, selectionValueType: fieldMetadata.selectionValueType as SelectionValueType, discoveryService: this.discoveryService, isMultiSelect: fieldMetadata.isMultiSelect };
378
394
  return new SelectionDynamicFieldCrudManager(options);
379
395
  }
380
396
  case SolidFieldType.uuid: {
@@ -398,7 +414,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
398
414
  const alias = 'entity';
399
415
  // Extract the required keys from the input query
400
416
  let { limit, offset, populateMedia, populateGroup, groupFilter } = basicFilterDto;
401
- const {singularName} = await this.loadModel();
417
+ const { singularName, internationalisation, draftPublishWorkflow } = await this.loadModel();
402
418
  // Check wheather user has update permission for model
403
419
  if (solidRequestContext.activeUser) {
404
420
  const hasPermission = this.crudHelperService.hasReadPermissionOnModel(solidRequestContext.activeUser, singularName);
@@ -410,11 +426,18 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
410
426
  // Create above query on pincode table using query builder
411
427
  var qb: SelectQueryBuilder<T> = this.repo.createQueryBuilder(alias)
412
428
  qb = this.crudHelperService.buildFilterQuery(qb, basicFilterDto, alias);
413
-
429
+
430
+
414
431
  if (basicFilterDto.groupBy) {
415
432
  // Get the records and the count
416
- const { groupMeta, groupRecords } = await this.handleGroupFind(qb, groupFilter, populateGroup, alias, populateMedia);
433
+ const { groupMeta, groupRecords } = await this.handleGroupFind(qb, groupFilter, populateGroup, alias, populateMedia, internationalisation, draftPublishWorkflow);
434
+ const totalGroups = await this.crudHelperService.countGroupedRecords(qb, basicFilterDto, alias);
435
+ qb = this.crudHelperService.buildFilterQuery(qb, basicFilterDto, alias, internationalisation, draftPublishWorkflow);
436
+
417
437
  return {
438
+ meta: {
439
+ "totalRecords": totalGroups
440
+ },
418
441
  groupMeta,
419
442
  groupRecords,
420
443
  }
@@ -440,7 +463,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
440
463
  return this.wrapFindResponse(offset, limit, count, entities);
441
464
  }
442
465
 
443
- private async handleGroupFind(qb: SelectQueryBuilder<T>, groupFilter: BasicFilterDto, populateGroup: boolean, alias: string, populateMedia: string[]) {
466
+ private async handleGroupFind(qb: SelectQueryBuilder<T>, groupFilter: BasicFilterDto, populateGroup: boolean, alias: string, populateMedia: string[], internationalisation: boolean, draftPublishWorkflow: boolean) {
444
467
  const groupByResult = await qb.getRawMany();
445
468
 
446
469
  const groupMeta = [];
@@ -449,7 +472,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
449
472
  for (const group of groupByResult) {
450
473
  if (populateGroup) {
451
474
  let groupByQb: SelectQueryBuilder<T> = this.repo.createQueryBuilder(alias);
452
- groupByQb = this.crudHelperService.buildFilterQuery(groupByQb, groupFilter, alias);
475
+ groupByQb = this.crudHelperService.buildFilterQuery(groupByQb, groupFilter, alias, internationalisation, draftPublishWorkflow);
453
476
  groupByQb = this.crudHelperService.buildGroupByRecordsQuery(groupByQb, group, alias);
454
477
  const [entities, count] = await groupByQb.getManyAndCount();
455
478
 
@@ -486,12 +509,12 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
486
509
  return r;
487
510
  }
488
511
 
489
- private async handlePopulateMedia(populateMedia: string[], entities: T[]) {
512
+ private async handlePopulateMedia(populateMedia: string[], entities: T[]) {
490
513
  const model = await this.entityManager.getRepository(ModelMetadata).findOne({
491
514
  where: {
492
515
  singularName: this.modelName,
493
516
  },
494
- relations: ['fields', 'fields.mediaStorageProvider', 'fields.model','module'],
517
+ relations: ['fields', 'fields.mediaStorageProvider', 'fields.model', 'module'],
495
518
  });
496
519
 
497
520
  // Will iterate through every entity & all populateMedia & call getMediaDetails for each field
@@ -501,7 +524,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
501
524
  }
502
525
  }
503
526
  return entities;
504
- }
527
+ }
505
528
 
506
529
  // Adds the media with full URL to the entity / nested entity
507
530
  private async populateMediaObject(mediaFieldPath: string, model: ModelMetadata, entity: T) {
@@ -515,12 +538,12 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
515
538
  // We can assume that the media field entity model is already populated as part of the entity data
516
539
  const mediaFieldEntities = this.getMediaFieldEntities(entity, pathParts);
517
540
  if (!mediaFieldEntities || mediaFieldEntities.length === 0) {
518
- return ;//no need to populate data if relation not exists
541
+ return;//no need to populate data if relation not exists
519
542
  }
520
543
  // Populate the media field entities with the full URL
521
544
  for (const mediaFieldEntity of mediaFieldEntities) {
522
545
  const mediaWithFullUrl = await this.getMediaWithFullUrl(mediaFieldEntity, mediaFieldMetadata);
523
- this.appendMediaKey(mediaWithFullUrl, mediaFieldEntity, mediaFieldMetadata.name);
546
+ this.appendMediaKey(mediaWithFullUrl, mediaFieldEntity, mediaFieldMetadata.name);
524
547
  // mediaFieldEntity['_media'][mediaFieldPath] = mediaWithFullUrl
525
548
  }
526
549
  }
@@ -546,7 +569,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
546
569
  entity['_media'] = {
547
570
  [mediaFieldPath]: mediaWithFullUrl
548
571
  };
549
- }
572
+ }
550
573
  }
551
574
 
552
575
  private getMediaFieldEntities(entity: T, mediaPathParts: string[]): T[] {
@@ -562,12 +585,12 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
562
585
  return isArray(entityPart) ? entityPart : [entityPart];
563
586
  }
564
587
 
565
- async getMediaWithFullUrl(mediaEntity: any, mediaFieldMetadata: FieldMetadata): Promise<MediaWithFullUrl[]>{
566
- const storageProviderMetadata = mediaFieldMetadata.mediaStorageProvider;
567
- const storageProviderType = storageProviderMetadata.type as MediaStorageProviderType;
568
- const storageProvider = await getMediaStorageProvider(this.moduleRef, storageProviderType);
569
- const mediaDetails = await storageProvider.retrieve(mediaEntity, mediaFieldMetadata);
570
- return mediaDetails as MediaWithFullUrl[];
588
+ async getMediaWithFullUrl(mediaEntity: any, mediaFieldMetadata: FieldMetadata): Promise<MediaWithFullUrl[]> {
589
+ const storageProviderMetadata = mediaFieldMetadata.mediaStorageProvider;
590
+ const storageProviderType = storageProviderMetadata.type as MediaStorageProviderType;
591
+ const storageProvider = await getMediaStorageProvider(this.moduleRef, storageProviderType);
592
+ const mediaDetails = await storageProvider.retrieve(mediaEntity, mediaFieldMetadata);
593
+ return mediaDetails as MediaWithFullUrl[];
571
594
  }
572
595
 
573
596
  async findOne(id: number, query: any, solidRequestContext: any = {}) {
@@ -610,7 +633,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
610
633
  return entity;
611
634
  }
612
635
 
613
- async insertMany(createDtos: any[], filesArray: Express.Multer.File[][] = [],solidRequestContext: any = {}): Promise<T[]> {
636
+ async insertMany(createDtos: any[], filesArray: Express.Multer.File[][] = [], solidRequestContext: any = {}): Promise<T[]> {
614
637
 
615
638
 
616
639
  // if (createDtos.length !== filesArray.length) {
@@ -669,7 +692,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
669
692
  return savedEntities;
670
693
  }
671
694
 
672
- async deleteMany(ids: number[],solidRequestContext: any = {}): Promise<any> {
695
+ async deleteMany(ids: number[], solidRequestContext: any = {}): Promise<any> {
673
696
 
674
697
  if (!ids || ids.length === 0) {
675
698
  throw new Error('At least one ID is required for deletion');
@@ -713,7 +736,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
713
736
  // return removedEntities
714
737
  }
715
738
 
716
- async recover(id: number,solidRequestContext: any = {}) {
739
+ async recover(id: number, solidRequestContext: any = {}) {
717
740
  try {
718
741
  const loadedmodel = await this.loadModel();
719
742
  // Check wheather user has update permission for model
@@ -753,7 +776,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
753
776
  }
754
777
  }
755
778
 
756
- async recoverMany(ids: number[],solidRequestContext: any = {}) {
779
+ async recoverMany(ids: number[], solidRequestContext: any = {}) {
757
780
  try {
758
781
  const loadedmodel = await this.loadModel();
759
782
  // Check wheather user has update permission for model
@@ -808,32 +831,32 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
808
831
  if (!pathParts || pathParts.length === 0) {
809
832
  throw new BadRequestException('Path parts cannot be empty');
810
833
  }
811
-
834
+
812
835
  const [currentPart, ...remainingParts] = pathParts;
813
836
  const field = fields.find(field => field.name === currentPart);
814
-
837
+
815
838
  if (!field) {
816
839
  throw new BadRequestException(`Field ${currentPart} not found in model ${this.modelName}`);
817
840
  }
818
-
841
+
819
842
  // Base case: last part, return the field
820
843
  if (remainingParts.length === 0) {
821
844
  return field;
822
845
  }
823
-
846
+
824
847
  if (!field.relationCoModelSingularName) {
825
848
  throw new BadRequestException(`Field ${field.name} does not define a relationCoModelSingularName`);
826
849
  }
827
-
850
+
828
851
  const relationCoModel = await this.entityManager.getRepository(ModelMetadata).findOne({
829
852
  where: { singularName: field.relationCoModelSingularName },
830
853
  relations: ['fields', 'fields.mediaStorageProvider', 'fields.model'],
831
854
  });
832
-
855
+
833
856
  if (!relationCoModel) {
834
857
  throw new BadRequestException(`Model ${field.relationCoModelSingularName} not found`);
835
858
  }
836
-
859
+
837
860
  return this.getFieldMetadataRecursively(remainingParts, relationCoModel.fields);
838
861
  }
839
862
  }
@@ -0,0 +1,37 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
3
+ import { DiscoveryService, ModuleRef } from "@nestjs/core";
4
+ import { EntityManager, Repository } from 'typeorm';
5
+
6
+ import { CRUDService } from 'src/services/crud.service';
7
+ import { ModelMetadataService } from 'src/services/model-metadata.service';
8
+ import { ModuleMetadataService } from 'src/services/module-metadata.service';
9
+ import { ConfigService } from '@nestjs/config';
10
+ import { FileService } from 'src/services/file.service';
11
+ import { CrudHelperService } from 'src/services/crud-helper.service';
12
+ import { ModelMetadata } from 'src/entities/model-metadata.entity';
13
+ import { RequestContextService } from './request-context.service';
14
+ import { Locale } from 'src/entities/locale.entity';
15
+ @Injectable()
16
+ export class LocaleService extends CRUDService<Locale>{
17
+ constructor(
18
+ readonly modelMetadataService: ModelMetadataService,
19
+ readonly moduleMetadataService: ModuleMetadataService,
20
+ readonly configService: ConfigService,
21
+ readonly fileService: FileService,
22
+ readonly discoveryService: DiscoveryService,
23
+ readonly crudHelperService: CrudHelperService,
24
+ @InjectEntityManager()
25
+ readonly entityManager: EntityManager,
26
+ @InjectRepository(Locale, 'default')
27
+ readonly repo: Repository<Locale>,
28
+ @InjectRepository(Locale, 'default')
29
+ readonly moduleRef: ModuleRef,
30
+ @InjectRepository(ModelMetadata)
31
+ private readonly modelMetadataRepo: Repository<ModelMetadata>,
32
+ readonly requestContextService: RequestContextService
33
+ ) {
34
+ super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService,entityManager, repo, 'locale', 'solid-core', moduleRef);
35
+ }
36
+
37
+ }
@@ -45,7 +45,7 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
45
45
  await this.fileService.deleteFile(file.path);
46
46
 
47
47
  // Create an entry in the media table
48
- const mediaEntity = await this.mediaRepository.createMedia({
48
+ const mediaEntity = await this.mediaRepository.createMedia({
49
49
  entityId: entity.id,
50
50
  modelMetadataId: mediaFieldMetadata.model.id,
51
51
  relativeUri: this.getFileName(file),
@@ -0,0 +1,58 @@
1
+ import { Injectable } from "@nestjs/common";
2
+ import { ModelMetadataService } from "src/services/model-metadata.service";
3
+ import { SelectionProvider } from "src/decorators/selection-provider.decorator";
4
+ import { ISelectionProvider, ISelectionProviderContext, ISelectionProviderValues } from "../../interfaces";
5
+ // import localeCodes from 'locale-codes';
6
+ import * as locale from 'locale-codes'
7
+
8
+
9
+ @SelectionProvider()
10
+ @Injectable()
11
+ export class LocaleListSelectionProvider implements ISelectionProvider<ISelectionProviderContext> {
12
+
13
+ constructor() {
14
+ }
15
+
16
+ help(): string {
17
+ return "# Gets all locales available to the user";
18
+ }
19
+
20
+ name(): string {
21
+ return 'LocaleListSelectionProvider';
22
+ }
23
+
24
+ async value(optionValue: string, ctxt: ISelectionProviderContext): Promise<ISelectionProviderValues | any> {
25
+ const locales = locale.all
26
+ .filter(code => code.tag === optionValue)
27
+ .map(code => ({ label: `${code.name} (${code.tag})`, value: code.tag }));
28
+
29
+ return locales.length > 0 ? locales[0] : null;
30
+ }
31
+
32
+ async values(query: string, ctxt: ISelectionProviderContext): Promise<readonly ISelectionProviderValues[]> {
33
+
34
+ const locales = locale.all
35
+ .filter(code => {
36
+ // Look at the documentation here - https://www.npmjs.com/package/locale-codes
37
+ // In this if you check the section - https://www.npmjs.com/package/locale-codes#locale-list
38
+
39
+ // We disabled this because this was not returning the basic locale codes like 'en', 'fr', etc.
40
+ // We are only interested in the locales that have a tag with a hyphen (-) in it.
41
+ // if (code.tag.includes('-')) {
42
+ if (query) {
43
+ const lowerCaseQuery = query.toLowerCase();
44
+ return code.name.toLowerCase().includes(lowerCaseQuery) || code.tag.toLowerCase().includes(lowerCaseQuery);
45
+ }
46
+ return true;
47
+ // }
48
+ return false;
49
+ })
50
+ .map(code => ({
51
+ label: `${code.name} (${code.tag})`,
52
+ value: code.tag,
53
+ }));
54
+
55
+ return locales;
56
+
57
+ }
58
+ }