@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.
@@ -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
- groupStage._id = dateFields.has(field) ? getDateFormat(field, dateFormat) : groupId[field];
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.17.2",
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.14.0",
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.17.2",
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": "832f9362009d55b03e0a51c3f558c439f158d9e6"
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
- groupStage._id = dateFields.has(field) ? getDateFormat(field, dateFormat) : groupId[field]
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
- return result
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()