nlbone 0.6.16__tar.gz → 0.6.17__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 (100) hide show
  1. {nlbone-0.6.16 → nlbone-0.6.17}/PKG-INFO +1 -1
  2. {nlbone-0.6.16 → nlbone-0.6.17}/pyproject.toml +1 -1
  3. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/additional_filed/assembler.py +19 -6
  4. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/additional_filed/field_registry.py +43 -0
  5. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/additional_filed/resolver.py +30 -0
  6. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/dependencies/auth.py +4 -4
  7. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/pagination/offset_base.py +16 -16
  8. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/schema/base_response_model.py +0 -1
  9. {nlbone-0.6.16 → nlbone-0.6.17}/.gitignore +0 -0
  10. {nlbone-0.6.16 → nlbone-0.6.17}/LICENSE +0 -0
  11. {nlbone-0.6.16 → nlbone-0.6.17}/README.md +0 -0
  12. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/__init__.py +0 -0
  13. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/__init__.py +0 -0
  14. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/auth/__init__.py +0 -0
  15. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/auth/keycloak.py +0 -0
  16. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/auth/token_provider.py +0 -0
  17. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/cache/__init__.py +0 -0
  18. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/cache/async_redis.py +0 -0
  19. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/cache/memory.py +0 -0
  20. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/cache/pubsub_listener.py +0 -0
  21. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/cache/redis.py +0 -0
  22. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/db/__init__.py +0 -0
  23. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/db/postgres/__init__.py +0 -0
  24. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/db/postgres/audit.py +0 -0
  25. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/db/postgres/base.py +0 -0
  26. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/db/postgres/engine.py +0 -0
  27. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/db/postgres/query_builder.py +0 -0
  28. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/db/postgres/repository.py +0 -0
  29. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/db/postgres/schema.py +0 -0
  30. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/db/postgres/uow.py +0 -0
  31. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/db/redis/__init__.py +0 -0
  32. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/db/redis/client.py +0 -0
  33. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/http_clients/__init__.py +0 -0
  34. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/http_clients/pricing/__init__.py +0 -0
  35. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/http_clients/pricing/pricing_service.py +0 -0
  36. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/http_clients/uploadchi/__init__.py +0 -0
  37. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/http_clients/uploadchi/uploadchi.py +0 -0
  38. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/http_clients/uploadchi/uploadchi_async.py +0 -0
  39. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/messaging/__init__.py +0 -0
  40. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/messaging/event_bus.py +0 -0
  41. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/messaging/redis.py +0 -0
  42. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/percolation/__init__.py +0 -0
  43. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/adapters/percolation/connection.py +0 -0
  44. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/config/__init__.py +0 -0
  45. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/config/logging.py +0 -0
  46. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/config/settings.py +0 -0
  47. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/container.py +0 -0
  48. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/core/__init__.py +0 -0
  49. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/core/application/__init__.py +0 -0
  50. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/core/application/base_worker.py +0 -0
  51. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/core/application/events.py +0 -0
  52. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/core/application/services/__init__.py +0 -0
  53. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/core/application/services.py +0 -0
  54. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/core/application/use_case.py +0 -0
  55. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/core/domain/__init__.py +0 -0
  56. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/core/domain/base.py +0 -0
  57. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/core/domain/events.py +0 -0
  58. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/core/domain/models.py +0 -0
  59. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/core/ports/__init__.py +0 -0
  60. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/core/ports/auth.py +0 -0
  61. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/core/ports/cache.py +0 -0
  62. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/core/ports/event_bus.py +0 -0
  63. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/core/ports/files.py +0 -0
  64. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/core/ports/messaging.py +0 -0
  65. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/core/ports/repo.py +0 -0
  66. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/core/ports/uow.py +0 -0
  67. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/__init__.py +0 -0
  68. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/__init__.py +0 -0
  69. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/additional_filed/__init__.py +0 -0
  70. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/additional_filed/default_field_rules/__init__.py +0 -0
  71. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/additional_filed/default_field_rules/image_field_rules.py +0 -0
  72. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/dependencies/__init__.py +0 -0
  73. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/dependencies/async_auth.py +0 -0
  74. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/dependencies/db.py +0 -0
  75. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/dependencies/uow.py +0 -0
  76. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/exception_handlers.py +0 -0
  77. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/exceptions.py +0 -0
  78. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/middleware/__init__.py +0 -0
  79. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/middleware/access_log.py +0 -0
  80. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/middleware/add_request_context.py +0 -0
  81. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/middleware/authentication.py +0 -0
  82. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/pagination/__init__.py +0 -0
  83. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/routers.py +0 -0
  84. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/schema/__init__.py +0 -0
  85. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/schema/adaptive_schema.py +0 -0
  86. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/api/schemas.py +0 -0
  87. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/cli/__init__.py +0 -0
  88. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/cli/init_db.py +0 -0
  89. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/cli/main.py +0 -0
  90. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/jobs/__init__.py +0 -0
  91. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/interfaces/jobs/sync_tokens.py +0 -0
  92. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/types.py +0 -0
  93. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/utils/__init__.py +0 -0
  94. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/utils/cache.py +0 -0
  95. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/utils/cache_keys.py +0 -0
  96. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/utils/cache_registry.py +0 -0
  97. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/utils/context.py +0 -0
  98. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/utils/http.py +0 -0
  99. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/utils/redactor.py +0 -0
  100. {nlbone-0.6.16 → nlbone-0.6.17}/src/nlbone/utils/time.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nlbone
3
- Version: 0.6.16
3
+ Version: 0.6.17
4
4
  Summary: Backbone package for interfaces and infrastructure in Python projects
5
5
  Author-email: Amir Hosein Kahkbazzadeh <a.khakbazzadeh@gmail.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "nlbone"
7
- version = "0.6.16"
7
+ version = "0.6.17"
8
8
  description = "Backbone package for interfaces and infrastructure in Python projects"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -11,20 +11,33 @@ def assemble_response(
11
11
  obj: Any,
12
12
  reg: ResourceRegistry,
13
13
  selected_rules: Dict[str, FieldRule],
14
- base_schema: type[BaseModel] | None,
15
14
  session,
15
+ base_schema: type[BaseModel] | None,
16
+ scope_map: dict[str, set[str]] = None,
16
17
  ) -> Dict[str, Any]:
17
18
  base = {f: getattr(obj, f, None) for f in reg.default_fields - set(reg.rules.keys())}
18
19
  if base_schema:
19
20
  base = base_schema.model_validate(base).model_dump()
20
21
 
21
- ctx = {"file_service": Container.file_service(), "entity": obj, "db": session}
22
- for name, rule in selected_rules.items():
22
+ ctx = {
23
+ "file_service": Container.file_service(),
24
+ "entity": obj,
25
+ "db": session,
26
+ "pricing_service": Container.pricing_service(),
27
+ }
28
+ roots = {name.split(".", 1)[0] for name in selected_rules.keys()}
29
+ for root in roots:
30
+ rule = reg.rules.get(root)
31
+ if not rule:
32
+ continue
33
+
23
34
  if rule.loader:
24
- value = inject_dependencies(rule.loader, dependencies=ctx)
35
+ dependencies = ctx | {"scope": scope_map.get(root, {""})} if scope_map else ctx
36
+ value = inject_dependencies(rule.loader, dependencies=dependencies)
25
37
  else:
26
- value = _get_nested_attr(obj, name)
27
- _put_nested_key(base, name, value)
38
+ value = _get_nested_attr(obj, root)
39
+
40
+ _put_nested_key(base, root, value)
28
41
 
29
42
  return base
30
43
 
@@ -15,6 +15,10 @@ def _schema_fields(schema: Type[BaseModel], by_alias: bool = True) -> Set[str]:
15
15
  return names
16
16
 
17
17
 
18
+ def _prefix_name(prefix: str, name: str) -> str:
19
+ return f"{prefix}.{name}" if name else prefix
20
+
21
+
18
22
  @dataclass(frozen=True)
19
23
  class FieldRule:
20
24
  """
@@ -51,6 +55,7 @@ class ResourceRegistry:
51
55
  rules: Dict[str, FieldRule] = field(default_factory=dict)
52
56
  bundles: Dict[str, Set[str]] = field(default_factory=dict)
53
57
  _schema_fields_cache: Set[str] = field(default_factory=set, repr=False)
58
+ mounts: Dict[str, "ResourceRegistry"] = field(default_factory=dict)
54
59
 
55
60
  def add_rule(self, rule: FieldRule) -> "ResourceRegistry":
56
61
  self.rules[rule.name] = rule
@@ -103,3 +108,41 @@ class ResourceRegistry:
103
108
 
104
109
  self.default_fields = final
105
110
  return self
111
+
112
+ def mount(
113
+ self,
114
+ child: "ResourceRegistry",
115
+ *,
116
+ at: str,
117
+ include_child_defaults: bool = False,
118
+ include_child_bundles: bool = True,
119
+ ) -> "ResourceRegistry":
120
+ # 1) rules
121
+ for r in child.rules.values():
122
+ prefixed = FieldRule(
123
+ name=_prefix_name(at, r.name),
124
+ default=(include_child_defaults and r.default),
125
+ permission=r.permission,
126
+ deps=tuple(_prefix_name(at, d) for d in r.deps),
127
+ columns=r.columns,
128
+ join_paths=r.join_paths,
129
+ loader=None, # load by parent
130
+ )
131
+ self.add_rule(prefixed)
132
+
133
+ if include_child_defaults:
134
+ self.default_fields |= {_prefix_name(at, f) for f in child.default_fields}
135
+
136
+ if include_child_bundles:
137
+ for bname, items in child.bundles.items():
138
+ pb = _prefix_name(at, bname)
139
+ prefixed_items = set()
140
+ for it in items:
141
+ if it.startswith("@"):
142
+ prefixed_items.add(_prefix_name(at, it))
143
+ else:
144
+ prefixed_items.add(_prefix_name(at, it))
145
+ self.add_bundle(pb, prefixed_items)
146
+
147
+ self.mounts[at] = child
148
+ return self
@@ -1,3 +1,4 @@
1
+ from collections import defaultdict
1
2
  from typing import Dict, List, Set, Tuple
2
3
 
3
4
  from nlbone.interfaces.api.additional_filed.field_registry import (
@@ -89,6 +90,18 @@ def resolve_requested_fields(
89
90
  for f in list(selected_rules.keys()):
90
91
  add_deps(f)
91
92
 
93
+ parents_to_add = set()
94
+ for f in list(final):
95
+ if "." in f:
96
+ parent = f.split(".", 1)[0]
97
+ if parent in reg.rules:
98
+ parents_to_add.add(parent)
99
+
100
+ final |= parents_to_add
101
+ for p in parents_to_add:
102
+ if p not in selected_rules and p in reg.rules:
103
+ selected_rules[p] = reg.rules[p]
104
+
92
105
  # validate deps too
93
106
  unknown_deps = {d for d in final if (d not in reg.default_fields and d not in reg.rules)}
94
107
  if unknown_deps:
@@ -130,3 +143,20 @@ def build_query_plan(
130
143
  columns.extend(r.columns or [])
131
144
  joins.extend(r.join_paths or [])
132
145
  return columns, joins
146
+
147
+
148
+ def build_field_scope(requested_fields: Set[str]) -> dict[str, set[str]]:
149
+ """
150
+ {'variants', 'variants.cost', 'supplier.address.city'} →
151
+ {
152
+ 'variants': {'', 'cost'},
153
+ 'supplier': {'address.city'}
154
+ }
155
+ """
156
+ scope: dict[str, set[str]] = defaultdict(set)
157
+ for f in requested_fields:
158
+ parts = f.split(".", 1)
159
+ root = parts[0]
160
+ suffix = parts[1] if len(parts) == 2 else "" # '' یعنی خود root
161
+ scope[root].add(suffix)
162
+ return scope
@@ -78,10 +78,10 @@ def client_or_user_has_access_func(permissions=None, client_permissions=None):
78
78
  if not token:
79
79
  raise UnauthorizedException()
80
80
  needed = client_permissions or permissions
81
- if client_has_access_func(permissions=needed):
82
- return
83
- if user_has_access_func(permissions=needed):
84
- return
81
+ try:
82
+ client_has_access_func(permissions=needed)
83
+ except Exception:
84
+ user_has_access_func(permissions=needed)
85
85
 
86
86
 
87
87
  def client_or_user_has_access(*, permissions=None, client_permissions=None):
@@ -13,20 +13,20 @@ class PaginateRequest:
13
13
  """
14
14
 
15
15
  def __init__(
16
- self,
17
- limit: int = 10,
18
- offset: int = 0,
19
- sort: Optional[str] = None,
20
- filters: Optional[str] = Query(None, description="e.g. title:abc"),
21
- include: Optional[str] = None,
16
+ self,
17
+ limit: int = 10,
18
+ offset: int = 0,
19
+ sort: Optional[str] = None,
20
+ filters: Optional[str] = Query(None, description="e.g. title:abc"),
21
+ include: Optional[str] = None,
22
22
  ) -> None:
23
23
  self.limit = max(0, limit)
24
24
  self.offset = max(0, offset)
25
25
  self.sort = self._parse_sort(sort)
26
26
  self.filters = self._parse_filters(filters or "")
27
27
  self.include_ids: List[int] = ([int(x) for x in include.split(",") if x.strip().isdigit()] if include else [])[
28
- :50
29
- ]
28
+ :50
29
+ ]
30
30
 
31
31
  @staticmethod
32
32
  def _parse_sort(sort_str: Optional[str]) -> list[dict[str, str]]:
@@ -78,8 +78,8 @@ class PaginateRequest:
78
78
  filters_dict[key] = value_cast
79
79
  return filters_dict
80
80
 
81
- def remove_deleted(self, deleted_at_field: str = 'deleted_at'):
82
- self.filters = self.filters | {'deleted_at': None}
81
+ def remove_deleted(self, deleted_at_field: str = "deleted_at"):
82
+ self.filters = self.filters | {"deleted_at": None}
83
83
 
84
84
 
85
85
  class PaginateResponse:
@@ -88,12 +88,12 @@ class PaginateResponse:
88
88
  """
89
89
 
90
90
  def __init__(
91
- self,
92
- data: list[Any],
93
- total_count: int | None,
94
- limit: int,
95
- offset: int,
96
- use_data_key: bool = True,
91
+ self,
92
+ data: list[Any],
93
+ total_count: int | None,
94
+ limit: int,
95
+ offset: int,
96
+ use_data_key: bool = True,
97
97
  ) -> None:
98
98
  self.data = data
99
99
  self.total_count = total_count
@@ -1,4 +1,3 @@
1
-
2
1
  from pydantic import BaseModel, ConfigDict, model_serializer
3
2
 
4
3
  EXCLUDE_NONE = "exclude_none"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes