oplnk-python-utils 1.2.1__tar.gz → 1.2.3__tar.gz

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 (22) hide show
  1. {oplnk_python_utils-1.2.1/oplnk_python_utils.egg-info → oplnk_python_utils-1.2.3}/PKG-INFO +60 -15
  2. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/README.md +59 -14
  3. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/oplnk_python_utils/__init__.py +1 -1
  4. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/oplnk_python_utils/query/query_builder.py +51 -55
  5. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3/oplnk_python_utils.egg-info}/PKG-INFO +60 -15
  6. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/setup.py +1 -1
  7. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/LICENSE +0 -0
  8. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/MANIFEST.in +0 -0
  9. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/oplnk_python_utils/cache/__init__.py +0 -0
  10. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/oplnk_python_utils/cache/cache_utils.py +0 -0
  11. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/oplnk_python_utils/core/__init__.py +0 -0
  12. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/oplnk_python_utils/core/data.py +0 -0
  13. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/oplnk_python_utils/core/parsing.py +0 -0
  14. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/oplnk_python_utils/core/serialization.py +0 -0
  15. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/oplnk_python_utils/query/__init__.py +0 -0
  16. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/oplnk_python_utils/reports/__init__.py +0 -0
  17. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/oplnk_python_utils.egg-info/SOURCES.txt +0 -0
  18. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/oplnk_python_utils.egg-info/dependency_links.txt +0 -0
  19. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/oplnk_python_utils.egg-info/not-zip-safe +0 -0
  20. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/oplnk_python_utils.egg-info/requires.txt +0 -0
  21. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/oplnk_python_utils.egg-info/top_level.txt +0 -0
  22. {oplnk_python_utils-1.2.1 → oplnk_python_utils-1.2.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oplnk-python-utils
3
- Version: 1.2.1
3
+ Version: 1.2.3
4
4
  Summary: Reusable utilities for caching, data manipulation, parsing, and MongoDB query building
5
5
  Home-page: https://github.com/openlinkspa/oplnk-python-utils
6
6
  Author: OpenLink SpA
@@ -642,11 +642,11 @@ query = {
642
642
  "populate": ["creation_user", "modification_user"]
643
643
  }
644
644
 
645
- # Result will include (original ID automatically removed, replaced by populated object):
645
+ # Result will include (original ID removed, replaced by populated object):
646
646
  # {
647
647
  # "data": [{
648
648
  # "_id": "123",
649
- # "creation_user": { // Replaces the original ID field
649
+ # "creation_user_info": { // Populated object
650
650
  # "_id": "user_id_string",
651
651
  # "name": "John Doe",
652
652
  # "person": "person_id"
@@ -657,49 +657,69 @@ query = {
657
657
 
658
658
  **Reserved fields:**
659
659
 
660
- - `creation_user` → automatically populated, original ID is excluded
661
- - `modification_user` → automatically populated, original ID is excluded
660
+ - `creation_user` → automatically populated as `creation_user_info`, original ID is excluded
661
+ - `modification_user` → automatically populated as `modification_user_info`, original ID is excluded
662
662
 
663
663
  These fields are only populated when explicitly requested via the `populate` parameter, following the Strapi philosophy of on-demand loading.
664
664
 
665
665
  ### Populate Field Sanitization (v1.2.1)
666
666
 
667
- By default, when you populate a relation, the original ID field is automatically removed from the response (Strapi-like behavior):
667
+ The behavior of original ID fields depends on whether they are being populated:
668
+
669
+ #### Without populate - ID field shown as-is (from database):
668
670
 
669
671
  ```python
670
- # Request with populate - establishment_id will be automatically excluded:
672
+ # No populate - field shown normally from database
673
+ query = {
674
+ "fields": ["name", "establishment_id"]
675
+ }
676
+
677
+ # Response:
678
+ # {
679
+ # "data": [{
680
+ # "_id": "123",
681
+ # "name": "Product",
682
+ # "establishment_id": "est_123" // Shown normally
683
+ # }]
684
+ # }
685
+ ```
686
+
687
+ #### With populate - ID field removed by default:
688
+
689
+ ```python
690
+ # With populate but no explicit fields request
671
691
  query = {
672
692
  "populate": ["establishment"]
673
693
  }
674
694
 
675
- # Response - establishment_id is not present:
695
+ # Response - establishment_id automatically excluded:
676
696
  # {
677
697
  # "data": [{
678
698
  # "_id": "123",
679
699
  # "name": "Product",
680
- # "establishment": { // Populated object replaces the ID
700
+ # "establishment": { // Populated object
681
701
  # "_id": "est_123",
682
702
  # "name": "Main Office"
683
703
  # }
684
- # // establishment_id is NOT here
685
704
  # }]
686
705
  # }
687
706
  ```
688
707
 
689
- **To keep both the ID and populated object, explicitly request it in `fields`:**
708
+ #### With populate AND explicit fields request - show both:
690
709
 
691
710
  ```python
711
+ # Request both populate and the original ID field explicitly
692
712
  query = {
693
713
  "populate": ["establishment"],
694
- "fields": ["name", "establishment_id"] # Request the ID explicitly
714
+ "fields": ["name", "establishment_id"] # Explicitly request the ID
695
715
  }
696
716
 
697
- # Response - both ID and populated object are present:
717
+ # Response - both are present:
698
718
  # {
699
719
  # "data": [{
700
720
  # "_id": "123",
701
721
  # "name": "Product",
702
- # "establishment_id": "est_123", // Now included
722
+ # "establishment_id": "est_123", // Shown because explicitly requested
703
723
  # "establishment": {
704
724
  # "_id": "est_123",
705
725
  # "name": "Main Office"
@@ -708,7 +728,32 @@ query = {
708
728
  # }
709
729
  ```
710
730
 
711
- **For audit fields:** The original ID is always excluded (no option to keep it, since there's no `creation_user_id` field).
731
+ #### Audit fields behavior:
732
+
733
+ ```python
734
+ # Without populate - original field shown
735
+ query = {
736
+ "fields": ["name", "creation_user"]
737
+ }
738
+ # Response:
739
+ # { "creation_user": "user_123" }
740
+
741
+ # With populate - original field removed, *_info shown instead
742
+ query = {
743
+ "populate": ["creation_user"]
744
+ }
745
+ # Response:
746
+ # { "creation_user_info": { "_id": "user_123", ... } }
747
+ # Note: creation_user field is NOT shown
748
+
749
+ # With populate AND explicit request for original - show both
750
+ query = {
751
+ "populate": ["creation_user"],
752
+ "fields": ["creation_user"] # Explicitly request the ID
753
+ }
754
+ # Response:
755
+ # { "creation_user": "user_123", "creation_user_info": { "_id": "user_123", ... } }
756
+ ```
712
757
 
713
758
  ### Custom Pipelines
714
759
 
@@ -600,11 +600,11 @@ query = {
600
600
  "populate": ["creation_user", "modification_user"]
601
601
  }
602
602
 
603
- # Result will include (original ID automatically removed, replaced by populated object):
603
+ # Result will include (original ID removed, replaced by populated object):
604
604
  # {
605
605
  # "data": [{
606
606
  # "_id": "123",
607
- # "creation_user": { // Replaces the original ID field
607
+ # "creation_user_info": { // Populated object
608
608
  # "_id": "user_id_string",
609
609
  # "name": "John Doe",
610
610
  # "person": "person_id"
@@ -615,49 +615,69 @@ query = {
615
615
 
616
616
  **Reserved fields:**
617
617
 
618
- - `creation_user` → automatically populated, original ID is excluded
619
- - `modification_user` → automatically populated, original ID is excluded
618
+ - `creation_user` → automatically populated as `creation_user_info`, original ID is excluded
619
+ - `modification_user` → automatically populated as `modification_user_info`, original ID is excluded
620
620
 
621
621
  These fields are only populated when explicitly requested via the `populate` parameter, following the Strapi philosophy of on-demand loading.
622
622
 
623
623
  ### Populate Field Sanitization (v1.2.1)
624
624
 
625
- By default, when you populate a relation, the original ID field is automatically removed from the response (Strapi-like behavior):
625
+ The behavior of original ID fields depends on whether they are being populated:
626
+
627
+ #### Without populate - ID field shown as-is (from database):
626
628
 
627
629
  ```python
628
- # Request with populate - establishment_id will be automatically excluded:
630
+ # No populate - field shown normally from database
631
+ query = {
632
+ "fields": ["name", "establishment_id"]
633
+ }
634
+
635
+ # Response:
636
+ # {
637
+ # "data": [{
638
+ # "_id": "123",
639
+ # "name": "Product",
640
+ # "establishment_id": "est_123" // Shown normally
641
+ # }]
642
+ # }
643
+ ```
644
+
645
+ #### With populate - ID field removed by default:
646
+
647
+ ```python
648
+ # With populate but no explicit fields request
629
649
  query = {
630
650
  "populate": ["establishment"]
631
651
  }
632
652
 
633
- # Response - establishment_id is not present:
653
+ # Response - establishment_id automatically excluded:
634
654
  # {
635
655
  # "data": [{
636
656
  # "_id": "123",
637
657
  # "name": "Product",
638
- # "establishment": { // Populated object replaces the ID
658
+ # "establishment": { // Populated object
639
659
  # "_id": "est_123",
640
660
  # "name": "Main Office"
641
661
  # }
642
- # // establishment_id is NOT here
643
662
  # }]
644
663
  # }
645
664
  ```
646
665
 
647
- **To keep both the ID and populated object, explicitly request it in `fields`:**
666
+ #### With populate AND explicit fields request - show both:
648
667
 
649
668
  ```python
669
+ # Request both populate and the original ID field explicitly
650
670
  query = {
651
671
  "populate": ["establishment"],
652
- "fields": ["name", "establishment_id"] # Request the ID explicitly
672
+ "fields": ["name", "establishment_id"] # Explicitly request the ID
653
673
  }
654
674
 
655
- # Response - both ID and populated object are present:
675
+ # Response - both are present:
656
676
  # {
657
677
  # "data": [{
658
678
  # "_id": "123",
659
679
  # "name": "Product",
660
- # "establishment_id": "est_123", // Now included
680
+ # "establishment_id": "est_123", // Shown because explicitly requested
661
681
  # "establishment": {
662
682
  # "_id": "est_123",
663
683
  # "name": "Main Office"
@@ -666,7 +686,32 @@ query = {
666
686
  # }
667
687
  ```
668
688
 
669
- **For audit fields:** The original ID is always excluded (no option to keep it, since there's no `creation_user_id` field).
689
+ #### Audit fields behavior:
690
+
691
+ ```python
692
+ # Without populate - original field shown
693
+ query = {
694
+ "fields": ["name", "creation_user"]
695
+ }
696
+ # Response:
697
+ # { "creation_user": "user_123" }
698
+
699
+ # With populate - original field removed, *_info shown instead
700
+ query = {
701
+ "populate": ["creation_user"]
702
+ }
703
+ # Response:
704
+ # { "creation_user_info": { "_id": "user_123", ... } }
705
+ # Note: creation_user field is NOT shown
706
+
707
+ # With populate AND explicit request for original - show both
708
+ query = {
709
+ "populate": ["creation_user"],
710
+ "fields": ["creation_user"] # Explicitly request the ID
711
+ }
712
+ # Response:
713
+ # { "creation_user": "user_123", "creation_user_info": { "_id": "user_123", ... } }
714
+ ```
670
715
 
671
716
  ### Custom Pipelines
672
717
 
@@ -7,7 +7,7 @@ from . import core
7
7
  from . import cache
8
8
  from . import query
9
9
 
10
- __version__ = "1.2.1"
10
+ __version__ = "1.2.3"
11
11
  __author__ = "OpenLink SpA"
12
12
 
13
13
  __all__ = ["core", "cache", "query", "reports", "__version__"]
@@ -21,7 +21,7 @@ RESERVED_AUDIT_FIELDS = {
21
21
  "creation_user": {
22
22
  "collection": "users",
23
23
  "field_name": "creation_user",
24
- "alias": "creation_user",
24
+ "alias": "creation_user_info",
25
25
  "field_type": "id",
26
26
  "project_exclude": {
27
27
  "role": 0,
@@ -49,7 +49,7 @@ RESERVED_AUDIT_FIELDS = {
49
49
  "modification_user": {
50
50
  "collection": "users",
51
51
  "field_name": "modification_user",
52
- "alias": "modification_user",
52
+ "alias": "modification_user_info",
53
53
  "field_type": "id",
54
54
  "project_exclude": {
55
55
  "role": 0,
@@ -778,47 +778,46 @@ class QueryBuilder:
778
778
 
779
779
  return pipeline_copy
780
780
 
781
- def _get_populated_field_mappings(self, query_populate: Any) -> Dict[str, str]:
781
+ def _get_populated_fields_list(self, query_populate: Any) -> List[str]:
782
782
  """
783
- Build a mapping of populated field names to their original ID field names.
783
+ Get list of field names that are being populated.
784
784
 
785
- For example: {"establishment": "establishment_id", "creation_user": "_original_creation_user"}
785
+ For example: ["establishment", "creation_user"]
786
786
 
787
787
  Args:
788
788
  query_populate: The populate parameter (list or dict)
789
789
 
790
790
  Returns:
791
- Dict mapping populated field names to their original ID fields
791
+ List of field names being populated
792
792
  """
793
- mappings = {}
793
+ populated = []
794
794
 
795
795
  if isinstance(query_populate, list):
796
- for field in query_populate:
797
- if field in RESERVED_AUDIT_FIELDS:
798
- # Audit fields: map to the temporary original field
799
- mappings[field] = f"_original_{field}"
800
- else:
801
- # Normal relations: try to find the ID field
802
- for relation in self.relations:
803
- relation_name = relation[1]
804
- # Remove _id or _ids suffix to get base name
805
- base_name = relation_name.replace("_ids", "").replace("_id", "")
806
- if base_name == field:
807
- mappings[field] = relation_name
808
- break
796
+ populated = query_populate
809
797
  elif isinstance(query_populate, dict):
810
- for field in query_populate.keys():
811
- if field in RESERVED_AUDIT_FIELDS:
812
- mappings[field] = f"_original_{field}"
813
- else:
814
- for relation in self.relations:
815
- relation_name = relation[1]
816
- base_name = relation_name.replace("_ids", "").replace("_id", "")
817
- if base_name == field:
818
- mappings[field] = relation_name
819
- break
798
+ populated = list(query_populate.keys())
799
+
800
+ return populated
801
+
802
+ def _get_id_field_for_relation(self, relation_name: str) -> Optional[str]:
803
+ """
804
+ Get the ID field name for a given relation.
820
805
 
821
- return mappings
806
+ For example: "establishment" -> "establishment_id"
807
+
808
+ Args:
809
+ relation_name: The relation name (e.g., "establishment")
810
+
811
+ Returns:
812
+ The ID field name (e.g., "establishment_id") or None if not found
813
+ """
814
+ for relation in self.relations:
815
+ field_name = relation[1]
816
+ # Remove _id or _ids suffix to get base name
817
+ base_name = field_name.replace("_ids", "").replace("_id", "")
818
+ if base_name == relation_name:
819
+ return field_name
820
+ return None
822
821
 
823
822
  def sanitize_result(
824
823
  self, query: Dict[str, Any], data_result: Any, meta_result: Any
@@ -842,22 +841,31 @@ class QueryBuilder:
842
841
  if not meta_result or "totalCount" not in meta_result:
843
842
  meta_result = {"totalCount": 0}
844
843
 
845
- # Sanitize populate fields: remove original ID fields when populated
844
+ # Sanitize populate fields: remove original ID fields when populated (unless requested in fields)
846
845
  if "populate" in query and isinstance(data_result, list):
847
846
  fields_requested = query.get("fields", [])
848
- populated_mappings = self._get_populated_field_mappings(query["populate"])
847
+ populated_fields = self._get_populated_fields_list(query["populate"])
849
848
 
850
849
  for doc in data_result:
851
- for populated_field, original_id_field in populated_mappings.items():
852
- # If it's a reserved audit field (starts with _original_), always remove the temp field
853
- if original_id_field.startswith("_original_"):
854
- if original_id_field in doc:
855
- del doc[original_id_field]
856
- # For normal relations, only remove if not explicitly requested in fields
857
- else:
858
- if original_id_field not in fields_requested:
859
- if original_id_field in doc:
860
- del doc[original_id_field]
850
+ # For audit fields: remove original if populated version exists and not requested in fields
851
+ for field_name, field_config in RESERVED_AUDIT_FIELDS.items():
852
+ if field_name in populated_fields:
853
+ # If the populated alias exists and original field is not explicitly requested
854
+ if (
855
+ field_config["alias"] in doc
856
+ and field_name not in fields_requested
857
+ ):
858
+ if field_name in doc:
859
+ del doc[field_name]
860
+
861
+ # For normal relations: remove original ID unless explicitly requested in fields
862
+ for populated_field in populated_fields:
863
+ if populated_field not in RESERVED_AUDIT_FIELDS:
864
+ # Try to find the ID field for this relation
865
+ id_field = self._get_id_field_for_relation(populated_field)
866
+ if id_field and id_field not in fields_requested:
867
+ if id_field in doc:
868
+ del doc[id_field]
861
869
 
862
870
  return {"data": data_result, "meta": meta_result}
863
871
 
@@ -880,12 +888,6 @@ class QueryBuilder:
880
888
  field_config = RESERVED_AUDIT_FIELDS[field_name]
881
889
 
882
890
  lookup_pipeline = [
883
- # Save the original ID before lookup (will be removed during sanitization)
884
- {
885
- "$addFields": {
886
- f"_original_{field_config['field_name']}": f"${field_config['field_name']}"
887
- }
888
- },
889
891
  {
890
892
  "$lookup": {
891
893
  "from": field_config["collection"],
@@ -943,13 +945,7 @@ class QueryBuilder:
943
945
  else:
944
946
  let_value = {"lid": f"${relation_field_name}"}
945
947
 
946
- # Save the original ID field before lookup (will be sanitized unless requested in fields)
947
948
  lookup_relation = [
948
- {
949
- "$addFields": {
950
- f"_original_{relation_field_name}": f"${relation_field_name}"
951
- }
952
- },
953
949
  {
954
950
  "$lookup": {
955
951
  "from": f"{_prefix}{collection_name_relation}",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oplnk-python-utils
3
- Version: 1.2.1
3
+ Version: 1.2.3
4
4
  Summary: Reusable utilities for caching, data manipulation, parsing, and MongoDB query building
5
5
  Home-page: https://github.com/openlinkspa/oplnk-python-utils
6
6
  Author: OpenLink SpA
@@ -642,11 +642,11 @@ query = {
642
642
  "populate": ["creation_user", "modification_user"]
643
643
  }
644
644
 
645
- # Result will include (original ID automatically removed, replaced by populated object):
645
+ # Result will include (original ID removed, replaced by populated object):
646
646
  # {
647
647
  # "data": [{
648
648
  # "_id": "123",
649
- # "creation_user": { // Replaces the original ID field
649
+ # "creation_user_info": { // Populated object
650
650
  # "_id": "user_id_string",
651
651
  # "name": "John Doe",
652
652
  # "person": "person_id"
@@ -657,49 +657,69 @@ query = {
657
657
 
658
658
  **Reserved fields:**
659
659
 
660
- - `creation_user` → automatically populated, original ID is excluded
661
- - `modification_user` → automatically populated, original ID is excluded
660
+ - `creation_user` → automatically populated as `creation_user_info`, original ID is excluded
661
+ - `modification_user` → automatically populated as `modification_user_info`, original ID is excluded
662
662
 
663
663
  These fields are only populated when explicitly requested via the `populate` parameter, following the Strapi philosophy of on-demand loading.
664
664
 
665
665
  ### Populate Field Sanitization (v1.2.1)
666
666
 
667
- By default, when you populate a relation, the original ID field is automatically removed from the response (Strapi-like behavior):
667
+ The behavior of original ID fields depends on whether they are being populated:
668
+
669
+ #### Without populate - ID field shown as-is (from database):
668
670
 
669
671
  ```python
670
- # Request with populate - establishment_id will be automatically excluded:
672
+ # No populate - field shown normally from database
673
+ query = {
674
+ "fields": ["name", "establishment_id"]
675
+ }
676
+
677
+ # Response:
678
+ # {
679
+ # "data": [{
680
+ # "_id": "123",
681
+ # "name": "Product",
682
+ # "establishment_id": "est_123" // Shown normally
683
+ # }]
684
+ # }
685
+ ```
686
+
687
+ #### With populate - ID field removed by default:
688
+
689
+ ```python
690
+ # With populate but no explicit fields request
671
691
  query = {
672
692
  "populate": ["establishment"]
673
693
  }
674
694
 
675
- # Response - establishment_id is not present:
695
+ # Response - establishment_id automatically excluded:
676
696
  # {
677
697
  # "data": [{
678
698
  # "_id": "123",
679
699
  # "name": "Product",
680
- # "establishment": { // Populated object replaces the ID
700
+ # "establishment": { // Populated object
681
701
  # "_id": "est_123",
682
702
  # "name": "Main Office"
683
703
  # }
684
- # // establishment_id is NOT here
685
704
  # }]
686
705
  # }
687
706
  ```
688
707
 
689
- **To keep both the ID and populated object, explicitly request it in `fields`:**
708
+ #### With populate AND explicit fields request - show both:
690
709
 
691
710
  ```python
711
+ # Request both populate and the original ID field explicitly
692
712
  query = {
693
713
  "populate": ["establishment"],
694
- "fields": ["name", "establishment_id"] # Request the ID explicitly
714
+ "fields": ["name", "establishment_id"] # Explicitly request the ID
695
715
  }
696
716
 
697
- # Response - both ID and populated object are present:
717
+ # Response - both are present:
698
718
  # {
699
719
  # "data": [{
700
720
  # "_id": "123",
701
721
  # "name": "Product",
702
- # "establishment_id": "est_123", // Now included
722
+ # "establishment_id": "est_123", // Shown because explicitly requested
703
723
  # "establishment": {
704
724
  # "_id": "est_123",
705
725
  # "name": "Main Office"
@@ -708,7 +728,32 @@ query = {
708
728
  # }
709
729
  ```
710
730
 
711
- **For audit fields:** The original ID is always excluded (no option to keep it, since there's no `creation_user_id` field).
731
+ #### Audit fields behavior:
732
+
733
+ ```python
734
+ # Without populate - original field shown
735
+ query = {
736
+ "fields": ["name", "creation_user"]
737
+ }
738
+ # Response:
739
+ # { "creation_user": "user_123" }
740
+
741
+ # With populate - original field removed, *_info shown instead
742
+ query = {
743
+ "populate": ["creation_user"]
744
+ }
745
+ # Response:
746
+ # { "creation_user_info": { "_id": "user_123", ... } }
747
+ # Note: creation_user field is NOT shown
748
+
749
+ # With populate AND explicit request for original - show both
750
+ query = {
751
+ "populate": ["creation_user"],
752
+ "fields": ["creation_user"] # Explicitly request the ID
753
+ }
754
+ # Response:
755
+ # { "creation_user": "user_123", "creation_user_info": { "_id": "user_123", ... } }
756
+ ```
712
757
 
713
758
  ### Custom Pipelines
714
759
 
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="oplnk-python-utils",
5
- version="1.2.1",
5
+ version="1.2.3",
6
6
  author="OpenLink SpA",
7
7
  author_email="contacto@openlink.cl",
8
8
  description="Reusable utilities for caching, data manipulation, parsing, and MongoDB query building",