nlbone 0.6.8__tar.gz → 0.6.10__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 (91) hide show
  1. {nlbone-0.6.8 → nlbone-0.6.10}/PKG-INFO +1 -1
  2. {nlbone-0.6.8 → nlbone-0.6.10}/pyproject.toml +1 -1
  3. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/db/postgres/query_builder.py +104 -87
  4. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/http_clients/pricing/pricing_service.py +19 -3
  5. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/container.py +4 -3
  6. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/api/pagination/offset_base.py +14 -12
  7. {nlbone-0.6.8 → nlbone-0.6.10}/.gitignore +0 -0
  8. {nlbone-0.6.8 → nlbone-0.6.10}/LICENSE +0 -0
  9. {nlbone-0.6.8 → nlbone-0.6.10}/README.md +0 -0
  10. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/__init__.py +0 -0
  11. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/__init__.py +0 -0
  12. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/auth/__init__.py +0 -0
  13. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/auth/keycloak.py +0 -0
  14. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/auth/token_provider.py +0 -0
  15. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/cache/__init__.py +0 -0
  16. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/cache/async_redis.py +0 -0
  17. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/cache/memory.py +0 -0
  18. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/cache/pubsub_listener.py +0 -0
  19. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/cache/redis.py +0 -0
  20. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/db/__init__.py +0 -0
  21. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/db/postgres/__init__.py +0 -0
  22. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/db/postgres/audit.py +0 -0
  23. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/db/postgres/base.py +0 -0
  24. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/db/postgres/engine.py +0 -0
  25. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/db/postgres/repository.py +0 -0
  26. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/db/postgres/schema.py +0 -0
  27. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/db/postgres/uow.py +0 -0
  28. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/db/redis/__init__.py +0 -0
  29. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/db/redis/client.py +0 -0
  30. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/http_clients/__init__.py +0 -0
  31. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/http_clients/pricing/__init__.py +0 -0
  32. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/http_clients/uploadchi/__init__.py +0 -0
  33. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/http_clients/uploadchi/uploadchi.py +0 -0
  34. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/http_clients/uploadchi/uploadchi_async.py +0 -0
  35. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/messaging/__init__.py +0 -0
  36. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/messaging/event_bus.py +0 -0
  37. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/messaging/redis.py +0 -0
  38. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/percolation/__init__.py +0 -0
  39. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/adapters/percolation/connection.py +0 -0
  40. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/config/__init__.py +0 -0
  41. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/config/logging.py +0 -0
  42. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/config/settings.py +0 -0
  43. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/core/__init__.py +0 -0
  44. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/core/application/__init__.py +0 -0
  45. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/core/application/base_worker.py +0 -0
  46. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/core/application/events.py +0 -0
  47. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/core/application/services/__init__.py +0 -0
  48. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/core/application/services.py +0 -0
  49. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/core/application/use_case.py +0 -0
  50. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/core/domain/__init__.py +0 -0
  51. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/core/domain/base.py +0 -0
  52. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/core/domain/events.py +0 -0
  53. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/core/domain/models.py +0 -0
  54. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/core/ports/__init__.py +0 -0
  55. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/core/ports/auth.py +0 -0
  56. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/core/ports/cache.py +0 -0
  57. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/core/ports/event_bus.py +0 -0
  58. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/core/ports/files.py +0 -0
  59. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/core/ports/messaging.py +0 -0
  60. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/core/ports/repo.py +0 -0
  61. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/core/ports/uow.py +0 -0
  62. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/__init__.py +0 -0
  63. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/api/__init__.py +0 -0
  64. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/api/dependencies/__init__.py +0 -0
  65. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/api/dependencies/async_auth.py +0 -0
  66. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/api/dependencies/auth.py +0 -0
  67. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/api/dependencies/db.py +0 -0
  68. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/api/dependencies/uow.py +0 -0
  69. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/api/exception_handlers.py +0 -0
  70. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/api/exceptions.py +0 -0
  71. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/api/middleware/__init__.py +0 -0
  72. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/api/middleware/access_log.py +0 -0
  73. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/api/middleware/add_request_context.py +0 -0
  74. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/api/middleware/authentication.py +0 -0
  75. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/api/pagination/__init__.py +0 -0
  76. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/api/routers.py +0 -0
  77. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/api/schemas.py +0 -0
  78. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/cli/__init__.py +0 -0
  79. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/cli/init_db.py +0 -0
  80. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/cli/main.py +0 -0
  81. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/jobs/__init__.py +0 -0
  82. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/interfaces/jobs/sync_tokens.py +0 -0
  83. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/types.py +0 -0
  84. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/utils/__init__.py +0 -0
  85. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/utils/cache.py +0 -0
  86. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/utils/cache_keys.py +0 -0
  87. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/utils/cache_registry.py +0 -0
  88. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/utils/context.py +0 -0
  89. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/utils/http.py +0 -0
  90. {nlbone-0.6.8 → nlbone-0.6.10}/src/nlbone/utils/redactor.py +0 -0
  91. {nlbone-0.6.8 → nlbone-0.6.10}/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.8
3
+ Version: 0.6.10
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.8"
7
+ version = "0.6.10"
8
8
  description = "Backbone package for interfaces and infrastructure in Python projects"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1,6 +1,6 @@
1
1
  from typing import Any, Callable, Optional, Sequence, Type, Union
2
2
 
3
- from sqlalchemy import asc, desc, or_
3
+ from sqlalchemy import asc, desc, or_, and_, case, literal
4
4
  from sqlalchemy.dialects.postgresql import ENUM as PGEnum
5
5
  from sqlalchemy.orm import Query, Session
6
6
  from sqlalchemy.orm.interfaces import LoaderOption
@@ -21,7 +21,7 @@ from sqlalchemy.sql.sqltypes import (
21
21
  from nlbone.interfaces.api.exceptions import UnprocessableEntityException
22
22
  from nlbone.interfaces.api.pagination import PaginateRequest, PaginateResponse
23
23
 
24
- NULL_SENTINELS = {"None", "null", ""}
24
+ NULL_SENTINELS = ("None", "null", "")
25
25
 
26
26
 
27
27
  class _InvalidEnum(Exception):
@@ -29,8 +29,15 @@ class _InvalidEnum(Exception):
29
29
 
30
30
 
31
31
  def _apply_order(pagination: PaginateRequest, entity, query):
32
+ order_clauses = []
33
+
34
+ include_ids = getattr(pagination, "include_ids", []) or []
35
+ if include_ids and hasattr(entity, "id"):
36
+ id_col = getattr(entity, "id")
37
+ whens = [(id_col == _id, idx) for idx, _id in enumerate(include_ids)]
38
+ order_clauses.append(asc(case(*whens, else_=literal(999_999))))
39
+
32
40
  if pagination.sort:
33
- order_clauses = []
34
41
  for sort in pagination.sort:
35
42
  field = sort["field"]
36
43
  order = sort["order"]
@@ -42,8 +49,8 @@ def _apply_order(pagination: PaginateRequest, entity, query):
42
49
  else:
43
50
  order_clauses.append(desc(column))
44
51
 
45
- if order_clauses:
46
- query = query.order_by(*order_clauses)
52
+ if order_clauses:
53
+ query = query.order_by(*order_clauses)
47
54
  return query
48
55
 
49
56
 
@@ -101,89 +108,100 @@ def _parse_field_and_op(field: str):
101
108
 
102
109
 
103
110
  def _apply_filters(pagination, entity, query):
104
- if not getattr(pagination, "filters", None):
111
+ if not getattr(pagination, "filters", None) and not getattr(pagination, "include_ids", None):
105
112
  return query
106
113
 
107
- for raw_field, value in pagination.filters.items():
108
- if value is None or value in NULL_SENTINELS or value == [] or value == {}:
109
- value = None
110
-
111
- field, op_hint = _parse_field_and_op(raw_field)
112
-
113
- if not hasattr(entity, field):
114
- continue
115
-
116
- col = getattr(entity, field)
117
- coltype = getattr(col, "type", None)
118
-
119
- def coerce(v):
120
- if v is None:
121
- return None
122
- # Enums
123
- if isinstance(coltype, (SAEnum, PGEnum)):
124
- return _coerce_enum(coltype, v)
125
- # Text
126
- if _is_text_type(coltype):
127
- return str(v)
128
- # Numbers
129
- if isinstance(coltype, (Integer, BigInteger, SmallInteger)):
130
- return int(v)
131
- if isinstance(coltype, (Float, Numeric)):
132
- return float(v)
133
- # Booleans
134
- if isinstance(coltype, Boolean):
135
- if isinstance(v, bool):
136
- return v
137
- if isinstance(v, (int, float)):
138
- return bool(v)
139
- if isinstance(v, str):
140
- vl = v.strip().lower()
141
- if vl in {"true", "1", "yes", "y", "t"}:
114
+ predicates = []
115
+
116
+ if getattr(pagination, "filters", None):
117
+ for raw_field, value in pagination.filters.items():
118
+ if value is None or value in NULL_SENTINELS or value == [] or value == {}:
119
+ value = None
120
+
121
+ field, op_hint = _parse_field_and_op(raw_field)
122
+
123
+ if not hasattr(entity, field):
124
+ continue
125
+
126
+ col = getattr(entity, field)
127
+ coltype = getattr(col, "type", None)
128
+
129
+ def coerce(v):
130
+ if v is None:
131
+ return None
132
+ # Enums
133
+ if isinstance(coltype, (SAEnum, PGEnum)):
134
+ return _coerce_enum(coltype, v)
135
+ # Text
136
+ if _is_text_type(coltype):
137
+ return str(v)
138
+ # Numbers
139
+ if isinstance(coltype, (Integer, BigInteger, SmallInteger)):
140
+ return int(v)
141
+ if isinstance(coltype, (Float, Numeric)):
142
+ return float(v)
143
+ # Booleans
144
+ if isinstance(coltype, Boolean):
145
+ if isinstance(v, bool):
146
+ return v
147
+ if isinstance(v, (int, float)):
148
+ return bool(v)
149
+ if isinstance(v, str):
150
+ vl = v.strip().lower()
151
+ if vl in {"true", "1", "yes", "y", "t"}:
152
+ return True
153
+ if vl in {"false", "0", "no", "n", "f"}:
154
+ return False
155
+ return None
156
+ return v
157
+
158
+ try:
159
+ def _use_ilike(v) -> bool:
160
+ if op_hint == "ilike":
161
+ return True
162
+ if _is_text_type(coltype) and isinstance(v, str) and _looks_like_wildcard(v):
142
163
  return True
143
- if vl in {"false", "0", "no", "n", "f"}:
144
- return False
145
- return None
146
- # fallback
147
- return v
164
+ return False
148
165
 
149
- try:
150
- # Decide operator: explicit __ilike OR automatic if wildcard on text
151
- def _use_ilike(v) -> bool:
152
- if op_hint == "ilike":
153
- return True
154
- if _is_text_type(coltype) and isinstance(v, str) and _looks_like_wildcard(v):
155
- return True
156
- return False
157
-
158
- if isinstance(value, (list, tuple, set)):
159
- vals = [v for v in value if v not in (None, "", "null", "None")]
160
- if not vals:
161
- continue
162
-
163
- # if any value signals ilike, apply OR of ilike; else IN / EQs
164
- if any(_use_ilike(v) for v in vals) and _is_text_type(coltype):
165
- patterns = [_to_sql_like_pattern(str(v)) for v in vals]
166
- query = query.filter(or_(*[col.ilike(p) for p in patterns]))
167
- else:
168
- coerced = [coerce(v) for v in vals]
169
- if not coerced:
166
+ if isinstance(value, (list, tuple, set)):
167
+ vals = [v for v in value if v not in (None, "", "null", "None")]
168
+ if not vals:
170
169
  continue
171
- query = query.filter(col.in_(coerced))
172
- else:
173
- if _use_ilike(value) and _is_text_type(coltype):
174
- pattern = _to_sql_like_pattern(str(value))
175
- query = query.filter(col.ilike(pattern))
170
+
171
+ if any(_use_ilike(v) for v in vals) and _is_text_type(coltype):
172
+ patterns = [_to_sql_like_pattern(str(v)) for v in vals]
173
+ predicates.append(or_(*[col.ilike(p) for p in patterns]))
174
+ else:
175
+ coerced = [coerce(v) for v in vals]
176
+ if not coerced:
177
+ continue
178
+ predicates.append(col.in_(coerced))
176
179
  else:
177
- v = coerce(value)
178
- if v is None:
179
- query = query.filter(col.is_(None))
180
+ if _use_ilike(value) and _is_text_type(coltype):
181
+ pattern = _to_sql_like_pattern(str(value))
182
+ predicates.append(col.ilike(pattern))
180
183
  else:
181
- query = query.filter(col == v)
182
-
183
- except _InvalidEnum as e:
184
- # Surface validation error like before
185
- raise UnprocessableEntityException(str(e), loc=["query", "filters", raw_field]) from e
186
-
184
+ v = coerce(value)
185
+ if v is None:
186
+ predicates.append(col.is_(None))
187
+ else:
188
+ predicates.append(col == v)
189
+
190
+ except _InvalidEnum as e:
191
+ raise UnprocessableEntityException(str(e), loc=["query", "filters", raw_field]) from e
192
+
193
+ include_ids = getattr(pagination, "include_ids", []) or []
194
+ if include_ids and hasattr(entity, "id"):
195
+ id_col = getattr(entity, "id")
196
+ include_pred = id_col.in_(include_ids)
197
+ if predicates:
198
+ final_pred = or_(and_(*predicates), include_pred)
199
+ else:
200
+ final_pred = or_(and_(*[1 == 1]), include_pred)
201
+ return query.filter(final_pred)
202
+
203
+ if predicates:
204
+ query = query.filter(and_(*predicates))
187
205
  return query
188
206
 
189
207
 
@@ -210,24 +228,24 @@ def _serialize_item(item: Any, output_cls: OutputType) -> Any:
210
228
 
211
229
  if hasattr(output_cls, "model_validate"):
212
230
  try:
213
- model = output_cls.model_validate(item, from_attributes=True) # type: ignore[attr-defined]
231
+ model = output_cls.model_validate(item, from_attributes=True)
214
232
  if hasattr(model, "model_dump"):
215
- return model.model_dump() # type: ignore[attr-defined]
233
+ return model.model_dump()
216
234
  return model
217
235
  except Exception:
218
236
  pass
219
237
 
220
238
  if hasattr(output_cls, "from_orm"):
221
239
  try:
222
- model = output_cls.from_orm(item) # type: ignore[attr-defined]
240
+ model = output_cls.from_orm(item)
223
241
  if hasattr(model, "dict"):
224
- return model.dict() # type: ignore[attr-defined]
242
+ return model.dict()
225
243
  return model
226
244
  except Exception:
227
245
  pass
228
246
 
229
247
  try:
230
- obj = output_cls(item) # type: ignore[call-arg]
248
+ obj = output_cls(item)
231
249
  try:
232
250
  from dataclasses import asdict, is_dataclass
233
251
 
@@ -249,7 +267,6 @@ def get_paginated_response(
249
267
  output_cls: Optional[Type] = None,
250
268
  eager_options: Optional[Sequence[LoaderOption]] = None,
251
269
  ) -> dict:
252
- # پایه‌ی کوئری
253
270
  query = session.query(entity)
254
271
  if eager_options:
255
272
  query = query.options(*eager_options)
@@ -1,5 +1,6 @@
1
+ from decimal import Decimal
1
2
  from enum import Enum
2
- from typing import List, Literal, Optional
3
+ from typing import Dict, List, Literal, Optional, Union
3
4
 
4
5
  import httpx
5
6
  import requests
@@ -32,7 +33,7 @@ class Product(BaseModel):
32
33
 
33
34
 
34
35
  class Pricing(BaseModel):
35
- source: Literal["formula", "static"]
36
+ source: Optional[Literal["formula", "static"]] = None
36
37
  price: Optional[float] = None
37
38
  discount: Optional[float] = None
38
39
  discount_type: Optional[DiscountType] = None
@@ -61,7 +62,7 @@ class PricingRule(BaseModel):
61
62
  pricing: Pricing
62
63
 
63
64
 
64
- class CalculatePriceOut(RootModel[List[PricingRule]]):
65
+ class CalculatePriceOut(RootModel[Union[List[PricingRule], Dict[str, PricingRule]]]):
65
66
  pass
66
67
 
67
68
 
@@ -98,3 +99,18 @@ class PricingService:
98
99
  return CalculatePriceOut.model_validate(root=[])
99
100
 
100
101
  return CalculatePriceOut.model_validate(r.json())
102
+
103
+ def exchange_rates(self) -> Dict[str, Decimal]:
104
+ r = self._client.get(
105
+ f"{self._base_url}/variables/key/exchange_rates",
106
+ headers=auth_headers(self._token_provider.get_access_token()),
107
+ timeout=self._timeout,
108
+ verify=False,
109
+ )
110
+
111
+ if r.status_code != 200:
112
+ raise PricingError(r.status_code, r.text)
113
+
114
+ values = r.json().get("values")
115
+
116
+ return {f"{value['key']}": Decimal(value["value"]) for value in values}
@@ -15,6 +15,7 @@ from nlbone.adapters.http_clients import PricingService
15
15
  from nlbone.adapters.http_clients.uploadchi import UploadchiClient
16
16
  from nlbone.adapters.http_clients.uploadchi.uploadchi_async import UploadchiAsyncClient
17
17
  from nlbone.adapters.messaging import InMemoryEventBus
18
+ from nlbone.config.settings import Settings
18
19
  from nlbone.core.ports import EventBusPort
19
20
  from nlbone.core.ports.cache import AsyncCachePort, CachePort
20
21
  from nlbone.core.ports.files import AsyncFileServicePort, FileServicePort
@@ -62,12 +63,12 @@ class Container(containers.DeclarativeContainer):
62
63
  def create_container(settings: Optional[Any] = None) -> Container:
63
64
  c = Container()
64
65
  if settings is not None:
65
- if hasattr(settings, "model_dump"):
66
+ if isinstance(settings, Settings):
67
+ c.config.override(settings)
68
+ elif hasattr(settings, "model_dump"):
66
69
  c.config.from_dict(settings.model_dump()) # Pydantic v2
67
70
  elif hasattr(settings, "dict"):
68
71
  c.config.from_dict(settings.dict()) # Pydantic v1
69
72
  elif isinstance(settings, Mapping):
70
73
  c.config.from_dict(dict(settings))
71
- else:
72
- c.config.override(settings)
73
74
  return c
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  from math import ceil
3
- from typing import Any, Optional
3
+ from typing import Any, Optional, List
4
4
 
5
5
  from fastapi import Query
6
6
 
@@ -13,16 +13,18 @@ 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"),
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,
21
22
  ) -> None:
22
23
  self.limit = max(0, limit)
23
24
  self.offset = max(0, offset)
24
25
  self.sort = self._parse_sort(sort)
25
26
  self.filters = self._parse_filters(filters or "")
27
+ self.include_ids: List[int] = ([int(x) for x in include.split(",") if x.strip().isdigit()] if include else [])[:50]
26
28
 
27
29
  @staticmethod
28
30
  def _parse_sort(sort_str: Optional[str]) -> list[dict[str, str]]:
@@ -81,12 +83,12 @@ class PaginateResponse:
81
83
  """
82
84
 
83
85
  def __init__(
84
- self,
85
- data: list[Any],
86
- total_count: int | None,
87
- limit: int,
88
- offset: int,
89
- use_data_key: bool = True,
86
+ self,
87
+ data: list[Any],
88
+ total_count: int | None,
89
+ limit: int,
90
+ offset: int,
91
+ use_data_key: bool = True,
90
92
  ) -> None:
91
93
  self.data = data
92
94
  self.total_count = total_count
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes