@solidstarters/solid-core 1.2.56 → 1.2.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidstarters/solid-core",
3
- "version": "1.2.56",
3
+ "version": "1.2.57",
4
4
  "description": "This module is a NestJS module containing all the required core providers required by a Solid application",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -208,7 +208,7 @@ export class ModuleMetadataSeederService {
208
208
  this.logger.debug(`[End] Processing security rules for ${moduleMetadata.name}`);
209
209
 
210
210
  // List Of Values
211
- this.logger.debug(`[Start] Processing security rules for ${moduleMetadata.name}`);
211
+ this.logger.debug(`[Start] Processing List Of Values for ${moduleMetadata.name}`);
212
212
  const listOfValues: CreateListOfValuesDto[] = overallMetadata.listOfValues;
213
213
  await this.seedListOfValues(listOfValues);
214
214
  this.logger.debug(`[End] Processing List Of Values for ${moduleMetadata.name}`);
@@ -52,7 +52,8 @@ export class CrudHelperService {
52
52
  return;
53
53
  }
54
54
  else { // Recursively call the applyFilters method to handle nested conditions
55
- selectQb.leftJoin(`${alias}.${key}`, key);
55
+ const joinField = `${alias}.${key}`;
56
+ if (!this.isRelationJoined(selectQb, joinField)) selectQb.leftJoin(joinField, key);
56
57
  this.applyFilters(qb, primaryFilterObj, key, selectQb);
57
58
  }
58
59
  });
@@ -150,6 +151,11 @@ export class CrudHelperService {
150
151
  return queryBuilder.expressionMap.joinAttributes.some(join => join.entityOrProperty === joinProperty);
151
152
  }
152
153
 
154
+ private hasJoins(queryBuilder: SelectQueryBuilder<any>): boolean {
155
+ return queryBuilder.expressionMap.joinAttributes.length > 0;
156
+ }
157
+
158
+
153
159
  buildFilterQuery(qb: SelectQueryBuilder<any>, basicFilterDto: BasicFilterDto, entityAlias: string): SelectQueryBuilder<any> { //TODO : Check how to pass a type to SelectQueryBuilder instead of any
154
160
  let { limit, offset, showSoftDeleted, filters } = basicFilterDto;
155
161
  const { fields, sort, groupBy, populate = [] } = basicFilterDto;
@@ -163,6 +169,11 @@ export class CrudHelperService {
163
169
  throw new Error('buildFilterQuery: Only 1 Group by field is supported currently');
164
170
  }
165
171
 
172
+ // Depending upon the populate option, apply the join clause
173
+ if (normalizedPopulate && normalizedPopulate.length) {
174
+ this.buildPopulateQuery(normalizedPopulate, entityAlias, qb);
175
+ }
176
+
166
177
  if (filters) {
167
178
  qb.where(new Brackets(whereQb => {
168
179
  this.applyFilters(whereQb, filters, entityAlias, qb);
@@ -177,15 +188,6 @@ export class CrudHelperService {
177
188
  }));
178
189
  }
179
190
 
180
- // Depending upon the populate option, apply the join clause
181
- if (normalizedPopulate && normalizedPopulate.length) {
182
- normalizedPopulate.forEach((relation) => {
183
- // Check if the relation is already joined, if not then join it
184
- const joinProperty = `${entityAlias}.${relation}`;
185
- if (!this.isRelationJoined(qb, joinProperty)) qb.leftJoinAndSelect(joinProperty, relation);
186
- });
187
- }
188
-
189
191
  // Depending upon the order option, apply the order by clause
190
192
  if (normalizedSort && normalizedSort.length) {
191
193
  const orderOptions = this.orderOptions(normalizedSort);
@@ -214,9 +216,33 @@ export class CrudHelperService {
214
216
  qb.addGroupBy(`${entityAlias}.${field}`);
215
217
  });
216
218
  }
217
- // Apply the pagination options
218
- if (limit) qb.limit(limit);
219
- if (offset) qb.offset(offset);
219
+
220
+ // Apply the pagination options & handle the case when the query has joins
221
+ if (limit) this.hasJoins(qb) ? qb.take(limit) : qb.limit(limit);
222
+ if (offset) this.hasJoins(qb) ? qb.skip(offset): qb.offset(offset);
223
+ return qb;
224
+ }
225
+
226
+ private buildPopulateQuery(normalizedPopulate: string[], entityAlias: string, qb: SelectQueryBuilder<any>) {
227
+ normalizedPopulate.forEach((relation) => {
228
+ this.buildJoinQueryForRelation(qb, entityAlias, relation);
229
+ });
230
+ return qb;
231
+ }
232
+
233
+ private buildJoinQueryForRelation(qb: SelectQueryBuilder<any>, entityAlias: string, relation: string) {
234
+ // We split the joinProperty to get the alias of the entity we are joining
235
+ const relationParts = relation.split('.');
236
+ let parentAlias = entityAlias;
237
+ relationParts.forEach((part, i) => {
238
+ const joinProperty = `${parentAlias}.${part}`;
239
+ // Check if the relation is already joined, if not then join it
240
+ if (!this.isRelationJoined(qb, joinProperty)) {
241
+ const joinAlias = relationParts.slice(0, i + 1).join('_');
242
+ qb.leftJoinAndSelect(joinProperty, joinAlias);
243
+ }
244
+ parentAlias = part; // Update the parent alias for the next iteration
245
+ });
220
246
  return qb;
221
247
  }
222
248
 
@@ -99,20 +99,6 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
99
99
  }
100
100
  }
101
101
 
102
- private async loadInverseRelationFields() {
103
- const fieldMetadataRepo = this.entityManager.getRepository(FieldMetadata);
104
- // Since the fields in the dto could be a result of being on a inverse side of a relation, we need to get the field configuration from the inverse side to process it
105
- const inverseRelationFields = await fieldMetadataRepo.find({
106
- where: {
107
- type: 'relation',
108
- relationCoModelSingularName: this.modelName,
109
- relationCreateInverse: true,
110
- },
111
- relations: ['model'],
112
- });
113
- return inverseRelationFields;
114
- }
115
-
116
102
  private async loadModel() {
117
103
  return await this.modelMetadataService.findOneBySingularName(this.modelName, {
118
104
  fields: {
@@ -414,28 +400,22 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
414
400
  const alias = 'entity';
415
401
  // Extract the required keys from the input query
416
402
  let { limit, offset, populateMedia, populateGroup, groupFilter } = basicFilterDto;
417
- const model = await this.loadModel();
403
+ const {singularName} = await this.loadModel();
418
404
  // Check wheather user has update permission for model
419
405
  if (solidRequestContext.activeUser) {
420
- const hasPermission = this.crudHelperService.hasReadPermissionOnModel(solidRequestContext.activeUser, model.singularName);
406
+ const hasPermission = this.crudHelperService.hasReadPermissionOnModel(solidRequestContext.activeUser, singularName);
421
407
  if (!hasPermission) {
422
408
  throw new BadRequestException('Forbidden');
423
409
  }
424
410
  }
425
411
 
426
- // Exclude one-to-many and many-to-one relations from the initial filter query, since they will be queried separately
427
- const relationsExcludedFromInitialQuery = this.relationsExcludedFromInitialQuery(model, basicFilterDto.populate);
428
- basicFilterDto = this.getRevisedFilterDto(basicFilterDto, relationsExcludedFromInitialQuery);
429
-
430
412
  // Create above query on pincode table using query builder
431
413
  var qb: SelectQueryBuilder<T> = this.repo.createQueryBuilder(alias)
432
414
  qb = this.crudHelperService.buildFilterQuery(qb, basicFilterDto, alias);
433
415
 
434
416
  if (basicFilterDto.groupBy) {
435
- const relationsExcludedFromInitialQuery = this.relationsExcludedFromInitialQuery(model, groupFilter.populate);
436
- groupFilter = this.getRevisedFilterDto(groupFilter, relationsExcludedFromInitialQuery);
437
417
  // Get the records and the count
438
- const { groupMeta, groupRecords } = await this.handleGroupFind(qb, groupFilter, populateGroup, alias, populateMedia, relationsExcludedFromInitialQuery);
418
+ const { groupMeta, groupRecords } = await this.handleGroupFind(qb, groupFilter, populateGroup, alias, populateMedia);
439
419
  return {
440
420
  groupMeta,
441
421
  groupRecords,
@@ -443,7 +423,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
443
423
  }
444
424
  else {
445
425
  // Get the records and the count
446
- const { meta, records } = await this.handleNonGroupFind(qb, populateMedia, offset, limit, alias, relationsExcludedFromInitialQuery);
426
+ const { meta, records } = await this.handleNonGroupFind(qb, populateMedia, offset, limit, alias);
447
427
  return {
448
428
  meta,
449
429
  records,
@@ -451,28 +431,9 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
451
431
  }
452
432
  }
453
433
 
454
- private getRevisedFilterDto(basicFilterDto: BasicFilterDto, relationsExcludedFromInitialQuery: string[]): BasicFilterDto {
455
- const normalizedPopulate = this.crudHelperService.normalize(basicFilterDto.populate);
456
- if (normalizedPopulate.length === 0 || relationsExcludedFromInitialQuery.length === 0) return basicFilterDto;
457
- return { ...basicFilterDto, populate: normalizedPopulate.filter(populate => !relationsExcludedFromInitialQuery.includes(populate)) };
458
- }
459
-
460
- private relationsExcludedFromInitialQuery(model: ModelMetadata, relationsToBePopulated: string[] = []): string[] {
461
- const relationToBeExcluded =
462
- model.fields
463
- .filter(field => field.type === 'relation' && [RelationType.manyTomany, RelationType.oneToMany].includes(field.relationType as RelationType))
464
- .map(field => field.name);
465
- return relationsToBePopulated.filter(relation => relationToBeExcluded.includes(relation));
466
- }
467
-
468
- private async handleNonGroupFind(qb: SelectQueryBuilder<T>, populateMedia: string[], offset: number, limit: number, alias: string, relationsExcludedFromInitialQuery: string[]) {
434
+ private async handleNonGroupFind(qb: SelectQueryBuilder<T>, populateMedia: string[], offset: number, limit: number, alias: string) {
469
435
  const [entities, count] = await qb.getManyAndCount();
470
436
 
471
- // Populate the excluded relations for the entities
472
- if (relationsExcludedFromInitialQuery.length > 0) {
473
- await this.populateExcludedRelations(entities, relationsExcludedFromInitialQuery, alias);
474
- }
475
-
476
437
  // Populate the entity with the media
477
438
  if (populateMedia && populateMedia.length > 0) {
478
439
  await this.handlePopulateMedia(populateMedia, entities);
@@ -481,36 +442,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
481
442
  return this.wrapFindResponse(offset, limit, count, entities);
482
443
  }
483
444
 
484
- private async populateExcludedRelations(entities: T[], relationsExcludedFromInitialQuery: string[], alias: string) {
485
- //@ts-ignore
486
- const ids = entities.map(entity => entity.id);
487
-
488
- // Fire a query to get the records from the relation entity which match the ids
489
- // Create a map with key as the entity id and value as the qb records
490
- const relationEntitiesMap = {};
491
- for (const relation of relationsExcludedFromInitialQuery) {
492
- const qb = this.repo.createQueryBuilder(`${alias}`)
493
- .leftJoinAndSelect(`${alias}.${relation}`, relation)
494
- .where(`${alias}.id IN (:...ids)`, { ids })
495
- // .limit(DEFAULT_LIMIT)
496
- // .offset(DEFAULT_OFFSET);
497
- const relationEntities = await qb.getMany();
498
- relationEntitiesMap[relation] = relationEntities;
499
- }
500
-
501
- // Iterate over the map and assign the relation entities to the entity
502
- for (const relation of relationsExcludedFromInitialQuery) {
503
- for (const entity of entities) {
504
- const entityRelations = relationEntitiesMap[relation]
505
- //@ts-ignore
506
- .filter((joinedEntity: T) => joinedEntity.id === entity.id)
507
- .flatMap((joinedEntity: T) => joinedEntity[relation]);
508
- entity[relation] = entityRelations;
509
- }
510
- }
511
- }
512
-
513
- private async handleGroupFind(qb: SelectQueryBuilder<T>, groupFilter: BasicFilterDto, populateGroup: boolean, alias: string, populateMedia: string[], relationsExcludedFromInitialQuery: string[]) {
445
+ private async handleGroupFind(qb: SelectQueryBuilder<T>, groupFilter: BasicFilterDto, populateGroup: boolean, alias: string, populateMedia: string[]) {
514
446
  const groupByResult = await qb.getRawMany();
515
447
 
516
448
  const groupMeta = [];
@@ -523,11 +455,6 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
523
455
  groupByQb = this.crudHelperService.buildGroupByRecordsQuery(groupByQb, group, alias);
524
456
  const [entities, count] = await groupByQb.getManyAndCount();
525
457
 
526
- // Populate the excluded relations for the entities
527
- if (relationsExcludedFromInitialQuery.length > 0) {
528
- await this.populateExcludedRelations(entities, relationsExcludedFromInitialQuery, alias);
529
- }
530
-
531
458
  // Populate the entity with the media
532
459
  if (populateMedia && populateMedia.length > 0) {
533
460
  await this.handlePopulateMedia(populateMedia, entities);