@drax/crud-back 3.17.2 → 3.18.0
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/repository/AbstractMongoRepository.js +18 -2
- package/package.json +4 -4
- package/src/repository/AbstractMongoRepository.ts +23 -2
- package/test/controllers/PersonController.test.ts +50 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/types/repository/AbstractMongoRepository.d.ts +1 -0
- package/types/repository/AbstractMongoRepository.d.ts.map +1 -1
|
@@ -9,6 +9,9 @@ class AbstractMongoRepository {
|
|
|
9
9
|
this._populateFields = [];
|
|
10
10
|
this._lean = true;
|
|
11
11
|
}
|
|
12
|
+
getNestedValue(source, path) {
|
|
13
|
+
return path.split('.').reduce((value, key) => value == null ? undefined : value[key], source);
|
|
14
|
+
}
|
|
12
15
|
assertId(id) {
|
|
13
16
|
if (!mongoose.Types.ObjectId.isValid(id)) {
|
|
14
17
|
console.log(`Invalid ID: ${id} is not a valid ObjectId.`);
|
|
@@ -415,7 +418,8 @@ class AbstractMongoRepository {
|
|
|
415
418
|
});
|
|
416
419
|
if (groupFields.length === 1) {
|
|
417
420
|
const field = groupFields[0];
|
|
418
|
-
|
|
421
|
+
const fieldAlias = groupFieldAliases.get(field);
|
|
422
|
+
groupStage._id = dateFields.has(field) ? getDateFormat(field, dateFormat) : groupId[fieldAlias];
|
|
419
423
|
}
|
|
420
424
|
else if (groupFields.length > 1) {
|
|
421
425
|
groupStage._id = groupId;
|
|
@@ -436,7 +440,19 @@ class AbstractMongoRepository {
|
|
|
436
440
|
}, { $sort: { count: -1 } });
|
|
437
441
|
// console.log("pipeline", JSON.stringify(pipeline, null, 2))
|
|
438
442
|
const result = await this._model.aggregate(pipeline).exec();
|
|
439
|
-
return result
|
|
443
|
+
return result.map((item) => {
|
|
444
|
+
const normalizedItem = { ...item };
|
|
445
|
+
groupFields.forEach(field => {
|
|
446
|
+
if (!field.includes('.') || normalizedItem[field] !== undefined) {
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
const nestedValue = this.getNestedValue(normalizedItem, field);
|
|
450
|
+
if (nestedValue !== undefined) {
|
|
451
|
+
normalizedItem[field] = nestedValue;
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
return normalizedItem;
|
|
455
|
+
});
|
|
440
456
|
}
|
|
441
457
|
}
|
|
442
458
|
export default AbstractMongoRepository;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "3.
|
|
6
|
+
"version": "3.18.0",
|
|
7
7
|
"description": "Crud utils across modules",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"types": "types/index.d.ts",
|
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
"author": "Cristian Incarnato & Drax Team",
|
|
23
23
|
"license": "ISC",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@drax/common-back": "^3.
|
|
25
|
+
"@drax/common-back": "^3.18.0",
|
|
26
26
|
"@drax/common-share": "^3.0.0",
|
|
27
27
|
"@drax/identity-share": "^3.15.0",
|
|
28
|
-
"@drax/media-back": "^3.
|
|
28
|
+
"@drax/media-back": "^3.18.0",
|
|
29
29
|
"@graphql-tools/load-files": "^7.0.0",
|
|
30
30
|
"@graphql-tools/merge": "^9.0.4",
|
|
31
31
|
"mongoose": "^8.23.0",
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
"typescript": "^5.9.3",
|
|
48
48
|
"vitest": "^3.2.4"
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "c7709f3912ba1f8ae6a7e78376e17d50b32cfa7d"
|
|
51
51
|
}
|
|
@@ -30,6 +30,10 @@ class AbstractMongoRepository<T, C, U> implements IDraxCrud<T, C, U> {
|
|
|
30
30
|
protected _populateFields: string[] | PopulateOptions[] = []
|
|
31
31
|
protected _lean: boolean = true
|
|
32
32
|
|
|
33
|
+
private getNestedValue(source: any, path: string): any {
|
|
34
|
+
return path.split('.').reduce((value, key) => value == null ? undefined : value[key], source)
|
|
35
|
+
}
|
|
36
|
+
|
|
33
37
|
assertId(id: string): void {
|
|
34
38
|
if (!mongoose.Types.ObjectId.isValid(id)) {
|
|
35
39
|
console.log(`Invalid ID: ${id} is not a valid ObjectId.`)
|
|
@@ -541,7 +545,8 @@ class AbstractMongoRepository<T, C, U> implements IDraxCrud<T, C, U> {
|
|
|
541
545
|
|
|
542
546
|
if (groupFields.length === 1) {
|
|
543
547
|
const field = groupFields[0]
|
|
544
|
-
|
|
548
|
+
const fieldAlias = groupFieldAliases.get(field) as string
|
|
549
|
+
groupStage._id = dateFields.has(field) ? getDateFormat(field, dateFormat) : groupId[fieldAlias]
|
|
545
550
|
} else if (groupFields.length > 1) {
|
|
546
551
|
groupStage._id = groupId
|
|
547
552
|
}
|
|
@@ -567,7 +572,23 @@ class AbstractMongoRepository<T, C, U> implements IDraxCrud<T, C, U> {
|
|
|
567
572
|
)
|
|
568
573
|
// console.log("pipeline", JSON.stringify(pipeline, null, 2))
|
|
569
574
|
const result = await this._model.aggregate(pipeline).exec()
|
|
570
|
-
|
|
575
|
+
|
|
576
|
+
return result.map((item: any) => {
|
|
577
|
+
const normalizedItem = {...item}
|
|
578
|
+
|
|
579
|
+
groupFields.forEach(field => {
|
|
580
|
+
if (!field.includes('.') || normalizedItem[field] !== undefined) {
|
|
581
|
+
return
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const nestedValue = this.getNestedValue(normalizedItem, field)
|
|
585
|
+
if (nestedValue !== undefined) {
|
|
586
|
+
normalizedItem[field] = nestedValue
|
|
587
|
+
}
|
|
588
|
+
})
|
|
589
|
+
|
|
590
|
+
return normalizedItem
|
|
591
|
+
})
|
|
571
592
|
}
|
|
572
593
|
}
|
|
573
594
|
|
|
@@ -627,6 +627,56 @@ describe("Person Controller Test", function () {
|
|
|
627
627
|
]))
|
|
628
628
|
})
|
|
629
629
|
|
|
630
|
+
it("should groupBy a single embedded nested field", async () => {
|
|
631
|
+
const { accessToken } = await testSetup.rootUserLogin()
|
|
632
|
+
await testSetup.dropCollection('Person')
|
|
633
|
+
|
|
634
|
+
const entityData: IPersonBase[] = [
|
|
635
|
+
{
|
|
636
|
+
fullname: "Argentina One",
|
|
637
|
+
address: { street: "Main St", country: "Argentina", city: "Buenos Aires" }
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
fullname: "Argentina Two",
|
|
641
|
+
address: { street: "Second St", country: "Argentina", city: "Cordoba" }
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
fullname: "Chile One",
|
|
645
|
+
address: { street: "Third St", country: "Chile", city: "Santiago" }
|
|
646
|
+
}
|
|
647
|
+
]
|
|
648
|
+
|
|
649
|
+
for (const data of entityData) {
|
|
650
|
+
const createResp = await testSetup.fastifyInstance.inject({
|
|
651
|
+
method: 'POST',
|
|
652
|
+
url: '/api/person',
|
|
653
|
+
payload: data,
|
|
654
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
|
655
|
+
})
|
|
656
|
+
|
|
657
|
+
expect(createResp.statusCode).toBe(200)
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
const groupResp = await testSetup.fastifyInstance.inject({
|
|
661
|
+
method: 'GET',
|
|
662
|
+
url: '/api/person/group-by?fields=address.country',
|
|
663
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
|
664
|
+
})
|
|
665
|
+
|
|
666
|
+
const groupResult = await groupResp.json()
|
|
667
|
+
expect(groupResp.statusCode).toBe(200)
|
|
668
|
+
expect(groupResult).toEqual(expect.arrayContaining([
|
|
669
|
+
expect.objectContaining({
|
|
670
|
+
'address.country': 'Argentina',
|
|
671
|
+
count: 2
|
|
672
|
+
}),
|
|
673
|
+
expect.objectContaining({
|
|
674
|
+
'address.country': 'Chile',
|
|
675
|
+
count: 1
|
|
676
|
+
})
|
|
677
|
+
]))
|
|
678
|
+
})
|
|
679
|
+
|
|
630
680
|
// 9. Error Handling - Not Found
|
|
631
681
|
it("should handle error responses correctly when person is not found", async () => {
|
|
632
682
|
const { accessToken } = await testSetup.rootUserLogin()
|