@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/dist/seeders/module-metadata-seeder.service.js +1 -1
- package/dist/services/crud-helper.service.d.ts +3 -0
- package/dist/services/crud-helper.service.d.ts.map +1 -1
- package/dist/services/crud-helper.service.js +30 -10
- package/dist/services/crud-helper.service.js.map +1 -1
- package/dist/services/crud.service.d.ts +0 -4
- package/dist/services/crud.service.d.ts.map +1 -1
- package/dist/services/crud.service.js +6 -60
- package/dist/services/crud.service.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/seeders/module-metadata-seeder.service.ts +1 -1
- package/src/services/crud-helper.service.ts +39 -13
- package/src/services/crud.service.ts +6 -79
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidstarters/solid-core",
|
|
3
|
-
"version": "1.2.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
if (
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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);
|