nlbone 0.1.33__tar.gz → 0.1.35__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 (70) hide show
  1. {nlbone-0.1.33 → nlbone-0.1.35}/PKG-INFO +2 -1
  2. {nlbone-0.1.33 → nlbone-0.1.35}/pyproject.toml +3 -2
  3. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/http_clients/uploadchi.py +34 -12
  4. nlbone-0.1.35/src/nlbone/container.py +31 -0
  5. nlbone-0.1.35/src/nlbone/interfaces/api/middleware/access_log.py +30 -0
  6. nlbone-0.1.35/src/nlbone/interfaces/api/middleware/add_request_context.py +52 -0
  7. nlbone-0.1.35/src/nlbone/interfaces/api/middleware/authentication.py +56 -0
  8. nlbone-0.1.35/src/nlbone/utils/context.py +42 -0
  9. nlbone-0.1.35/src/nlbone/utils/time.py +5 -0
  10. nlbone-0.1.33/src/nlbone/container.py +0 -13
  11. {nlbone-0.1.33 → nlbone-0.1.35}/.gitignore +0 -0
  12. {nlbone-0.1.33 → nlbone-0.1.35}/LICENSE +0 -0
  13. {nlbone-0.1.33 → nlbone-0.1.35}/README.md +0 -0
  14. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/__init__.py +0 -0
  15. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/__init__.py +0 -0
  16. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/auth/__init__.py +0 -0
  17. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/auth/keycloak.py +0 -0
  18. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/db/__init__.py +0 -0
  19. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/db/memory.py +0 -0
  20. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/db/postgres.py +0 -0
  21. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/db/query_builder.py +0 -0
  22. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/db/sqlalchemy/__init__.py +0 -0
  23. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/db/sqlalchemy/base.py +0 -0
  24. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/db/sqlalchemy/engine.py +0 -0
  25. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/db/sqlalchemy/query/__init__.py +0 -0
  26. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/db/sqlalchemy/query/builder.py +0 -0
  27. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/db/sqlalchemy/query/coercion.py +0 -0
  28. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/db/sqlalchemy/query/filters.py +0 -0
  29. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/db/sqlalchemy/query/ordering.py +0 -0
  30. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/db/sqlalchemy/query/types.py +0 -0
  31. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/db/sqlalchemy/schema.py +0 -0
  32. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/http_clients/__init__.py +0 -0
  33. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/http_clients/email_gateway.py +0 -0
  34. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/http_clients/uploadchi_async.py +0 -0
  35. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/messaging/__init__.py +0 -0
  36. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/adapters/messaging/redis.py +0 -0
  37. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/config/__init__.py +0 -0
  38. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/config/logging.py +0 -0
  39. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/config/settings.py +0 -0
  40. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/core/__init__.py +0 -0
  41. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/core/application/__init__.py +0 -0
  42. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/core/application/services/__init__.py +0 -0
  43. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/core/application/services.py +0 -0
  44. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/core/application/use_cases/__init__.py +0 -0
  45. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/core/application/use_cases/register_user.py +0 -0
  46. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/core/domain/__init__.py +0 -0
  47. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/core/domain/events.py +0 -0
  48. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/core/domain/models.py +0 -0
  49. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/core/ports/__init__.py +0 -0
  50. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/core/ports/auth.py +0 -0
  51. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/core/ports/files.py +0 -0
  52. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/core/ports/messaging.py +0 -0
  53. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/core/ports/repo.py +0 -0
  54. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/interfaces/__init__.py +0 -0
  55. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/interfaces/api/__init__.py +0 -0
  56. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/interfaces/api/dependencies/__init__.py +0 -0
  57. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/interfaces/api/dependencies/db.py +0 -0
  58. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/interfaces/api/exceptions.py +0 -0
  59. {nlbone-0.1.33/src/nlbone/interfaces/cli → nlbone-0.1.35/src/nlbone/interfaces/api/middleware}/__init__.py +0 -0
  60. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/interfaces/api/pagination/__init__.py +0 -0
  61. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/interfaces/api/pagination/offset_base.py +0 -0
  62. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/interfaces/api/routers.py +0 -0
  63. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/interfaces/api/schemas.py +0 -0
  64. {nlbone-0.1.33/src/nlbone/interfaces/jobs → nlbone-0.1.35/src/nlbone/interfaces/cli}/__init__.py +0 -0
  65. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/interfaces/cli/init_db.py +0 -0
  66. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/interfaces/cli/main.py +0 -0
  67. {nlbone-0.1.33/src/nlbone/utils → nlbone-0.1.35/src/nlbone/interfaces/jobs}/__init__.py +0 -0
  68. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/interfaces/jobs/sync_tokens.py +0 -0
  69. {nlbone-0.1.33 → nlbone-0.1.35}/src/nlbone/types.py +0 -0
  70. /nlbone-0.1.33/src/nlbone/utils/time.py → /nlbone-0.1.35/src/nlbone/utils/__init__.py +0 -0
@@ -1,12 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nlbone
3
- Version: 0.1.33
3
+ Version: 0.1.35
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
7
7
  License-File: LICENSE
8
8
  Requires-Python: >=3.10
9
9
  Requires-Dist: anyio>=4.0
10
+ Requires-Dist: dependency-injector>=4.48.1
10
11
  Requires-Dist: fastapi>=0.116
11
12
  Requires-Dist: httpx>=0.27
12
13
  Requires-Dist: psycopg>=3.2.9
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "nlbone"
7
- version = "0.1.33"
7
+ version = "0.1.35"
8
8
  description = "Backbone package for interfaces and infrastructure in Python projects"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -21,7 +21,8 @@ dependencies = [
21
21
  "starlette>=0.47",
22
22
  "uvicorn>=0.35",
23
23
  "sqlalchemy>=2.0",
24
- "psycopg>=3.2.9"
24
+ "psycopg>=3.2.9",
25
+ "dependency-injector>=4.48.1"
25
26
  ]
26
27
 
27
28
  [project.optional-dependencies]
@@ -1,27 +1,35 @@
1
1
  from __future__ import annotations
2
2
  import json
3
3
  from typing import Any, Optional
4
+ from urllib.parse import urlparse, urlunparse
5
+
4
6
  import httpx
7
+ import requests
5
8
 
6
9
  from nlbone.core.ports.files import FileServicePort
7
10
  from nlbone.config.settings import get_settings
8
11
 
12
+
9
13
  class UploadchiError(RuntimeError):
10
14
  def __init__(self, status: int, detail: Any | None = None):
11
15
  super().__init__(f"Uploadchi HTTP {status}: {detail}")
12
16
  self.status = status
13
17
  self.detail = detail
14
18
 
19
+
15
20
  def _resolve_token(explicit: str | None) -> str | None:
16
21
  if explicit is not None:
17
22
  return explicit
18
23
  s = get_settings()
19
24
  return s.UPLOADCHI_TOKEN.get_secret_value() if s.UPLOADCHI_TOKEN else None
20
25
 
26
+
21
27
  def _auth_headers(token: str | None) -> dict[str, str]:
22
28
  return {"Authorization": f"Bearer {token}"} if token else {}
23
29
 
24
- def _build_list_query(limit: int, offset: int, filters: dict[str, Any] | None, sort: list[tuple[str, str]] | None) -> dict[str, Any]:
30
+
31
+ def _build_list_query(limit: int, offset: int, filters: dict[str, Any] | None, sort: list[tuple[str, str]] | None) -> \
32
+ dict[str, Any]:
25
33
  q: dict[str, Any] = {"limit": limit, "offset": offset}
26
34
  if filters:
27
35
  q["filters"] = json.dumps(filters)
@@ -29,6 +37,7 @@ def _build_list_query(limit: int, offset: int, filters: dict[str, Any] | None, s
29
37
  q["sort"] = ",".join([f"{f}:{o}" for f, o in sort])
30
38
  return q
31
39
 
40
+
32
41
  def _filename_from_cd(cd: str | None, fallback: str) -> str:
33
42
  if not cd:
34
43
  return fallback
@@ -36,49 +45,62 @@ def _filename_from_cd(cd: str | None, fallback: str) -> str:
36
45
  return cd.split("filename=", 1)[1].strip("\"'")
37
46
  return fallback
38
47
 
48
+
49
+ def _normalize_https_base(url: str) -> str:
50
+ p = urlparse(url.strip())
51
+ p = p._replace(scheme="https") # enforce https
52
+ if p.path.endswith("/"):
53
+ p = p._replace(path=p.path.rstrip("/"))
54
+ return str(urlunparse(p))
55
+
56
+
39
57
  class UploadchiClient(FileServicePort):
40
- def __init__(self, base_url: Optional[str] = None, timeout_seconds: Optional[float] = None, client: httpx.Client | None = None) -> None:
58
+ def __init__(self, base_url: Optional[str] = None, timeout_seconds: Optional[float] = None,
59
+ client: httpx.Client | None = None) -> None:
41
60
  s = get_settings()
42
- self._base_url = base_url or str(s.UPLOADCHI_BASE_URL)
61
+ self._base_url = _normalize_https_base(base_url or str(s.UPLOADCHI_BASE_URL))
43
62
  self._timeout = timeout_seconds or float(s.HTTP_TIMEOUT_SECONDS)
44
- self._client = client or httpx.Client(base_url=self._base_url, timeout=self._timeout, follow_redirects=True)
63
+ self._client = client or requests.session()
45
64
 
46
65
  def close(self) -> None:
47
66
  self._client.close()
48
67
 
49
- def upload_file(self, file_bytes: bytes, filename: str, params: dict[str, Any] | None = None, token: str | None = None) -> dict:
68
+ def upload_file(self, file_bytes: bytes, filename: str, params: dict[str, Any] | None = None,
69
+ token: str | None = None) -> dict:
50
70
  tok = _resolve_token(token)
51
71
  files = {"file": (filename, file_bytes)}
52
72
  data = (params or {}).copy()
53
- r = self._client.post("", files=files, data=data, headers=_auth_headers(tok))
73
+ r = self._client.post(self._base_url, files=files, data=data, headers=_auth_headers(tok))
54
74
  if r.status_code >= 400:
55
75
  raise UploadchiError(r.status_code, r.text)
56
76
  return r.json()
57
77
 
58
78
  def commit_file(self, file_id: int, client_id: str, token: str | None = None) -> None:
59
79
  tok = _resolve_token(token)
60
- r = self._client.post(f"/{file_id}/commit", headers=_auth_headers(tok), params={"client_id": client_id} if client_id else None)
80
+ r = self._client.post(f"{self._base_url}/{file_id}/commit", headers=_auth_headers(tok),
81
+ params={"client_id": client_id} if client_id else None)
61
82
  if r.status_code not in (204, 200):
62
83
  raise UploadchiError(r.status_code, r.text)
63
84
 
64
- def list_files(self, limit: int = 10, offset: int = 0, filters: dict[str, Any] | None = None, sort: list[tuple[str, str]] | None = None, token: str | None = None) -> dict:
85
+ def list_files(self, limit: int = 10, offset: int = 0, filters: dict[str, Any] | None = None,
86
+ sort: list[tuple[str, str]] | None = None, token: str | None = None) -> dict:
65
87
  tok = _resolve_token(token)
66
88
  q = _build_list_query(limit, offset, filters, sort)
67
- r = self._client.get("", params=q, headers=_auth_headers(tok))
89
+ r = self._client.get(self._base_url, params=q, headers=_auth_headers(tok))
68
90
  if r.status_code >= 400:
69
91
  raise UploadchiError(r.status_code, r.text)
70
92
  return r.json()
71
93
 
72
94
  def get_file(self, file_id: int, token: str | None = None) -> dict:
73
95
  tok = _resolve_token(token)
74
- r = self._client.get(f"/{file_id}", headers=_auth_headers(tok))
96
+ r = self._client.get(f"{self._base_url}/{file_id}", headers=_auth_headers(tok))
75
97
  if r.status_code >= 400:
76
98
  raise UploadchiError(r.status_code, r.text)
77
99
  return r.json()
78
100
 
79
101
  def download_file(self, file_id: int, token: str | None = None) -> tuple[bytes, str, str]:
80
102
  tok = _resolve_token(token)
81
- r = self._client.get(f"/{file_id}/download", headers=_auth_headers(tok))
103
+ r = self._client.get(f"{self._base_url}/{file_id}/download", headers=_auth_headers(tok))
82
104
  if r.status_code >= 400:
83
105
  raise UploadchiError(r.status_code, r.text)
84
106
  filename = _filename_from_cd(r.headers.get("content-disposition"), fallback=f"file-{file_id}")
@@ -87,6 +109,6 @@ class UploadchiClient(FileServicePort):
87
109
 
88
110
  def delete_file(self, file_id: int, token: str | None = None) -> None:
89
111
  tok = _resolve_token(token)
90
- r = self._client.delete(f"/{file_id}", headers=_auth_headers(tok))
112
+ r = self._client.delete(f"{self._base_url}/{file_id}", headers=_auth_headers(tok))
91
113
  if r.status_code not in (204, 200):
92
114
  raise UploadchiError(r.status_code, r.text)
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+ from typing import Any, Mapping, Optional
3
+
4
+ from dependency_injector import containers, providers
5
+
6
+ from nlbone.adapters.http_clients.uploadchi import UploadchiClient
7
+ from nlbone.adapters.http_clients.uploadchi_async import UploadchiAsyncClient
8
+ from nlbone.adapters.auth.keycloak import KeycloakAuthService
9
+ from nlbone.core.ports.files import FileServicePort, AsyncFileServicePort
10
+
11
+
12
+ class Container(containers.DeclarativeContainer):
13
+ config = providers.Configuration(strict=False)
14
+
15
+ auth: providers.Singleton[KeycloakAuthService] = providers.Singleton(KeycloakAuthService, settings=config)
16
+ file_service: providers.Singleton[FileServicePort] = providers.Singleton(UploadchiClient)
17
+ afiles_service: providers.Singleton[AsyncFileServicePort] = providers.Singleton(UploadchiAsyncClient)
18
+
19
+
20
+ def create_container(settings: Optional[Any] = None) -> Container:
21
+ c = Container()
22
+ if settings is not None:
23
+ if hasattr(settings, "model_dump"):
24
+ c.config.from_dict(settings.model_dump()) # Pydantic v2
25
+ elif hasattr(settings, "dict"):
26
+ c.config.from_dict(settings.dict()) # Pydantic v1
27
+ elif isinstance(settings, Mapping):
28
+ c.config.from_dict(dict(settings))
29
+ else:
30
+ c.config.override(settings)
31
+ return c
@@ -0,0 +1,30 @@
1
+ import time
2
+ from typing import Callable
3
+ from fastapi import Request
4
+ from starlette.middleware.base import BaseHTTPMiddleware
5
+
6
+ from nlbone.config.logging import get_logger
7
+
8
+ logger = get_logger(__name__)
9
+
10
+ class AccessLogMiddleware(BaseHTTPMiddleware):
11
+ async def dispatch(self, request: Request, call_next: Callable):
12
+ start = time.perf_counter()
13
+ status_code = None
14
+ try:
15
+ response = await call_next(request)
16
+ status_code = getattr(response, "status_code", None)
17
+ return response
18
+ except Exception:
19
+ status_code = 500
20
+ raise
21
+ finally:
22
+ dur_ms = int((time.perf_counter() - start) * 1000)
23
+ logger.info({
24
+ "event": "access",
25
+ "method": request.method,
26
+ "path": request.url.path,
27
+ "status": status_code,
28
+ "duration_ms": dur_ms,
29
+ "query": request.url.query,
30
+ })
@@ -0,0 +1,52 @@
1
+ from __future__ import annotations
2
+ from typing import Optional
3
+ from uuid import uuid4
4
+
5
+ from starlette.datastructures import Headers
6
+ from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
7
+ from starlette.requests import Request
8
+
9
+ from nlbone import config
10
+ from nlbone.utils.context import request_ctx, request_id_ctx, bind_context, reset_context
11
+
12
+
13
+ def mock_request(user=None, token: Optional[str] = None):
14
+ request = Request({
15
+ "type": "http",
16
+ "headers": Headers({"User-Agent": "Testing-Agent"}).raw,
17
+ "client": {"host": "192.168.1.1", "port": 80},
18
+ "method": "GET",
19
+ "path": "/__test__",
20
+ })
21
+ request.state.user = user
22
+ request.state.token = token
23
+ return request
24
+
25
+
26
+ def current_request() -> Optional[Request]:
27
+ req = request_ctx.get()
28
+ if config.ENV == 'test' and req is None:
29
+ return mock_request()
30
+ return req
31
+
32
+
33
+ def current_request_id() -> Optional[str]:
34
+ return request_id_ctx.get()
35
+
36
+
37
+ class AddRequestContextMiddleware(BaseHTTPMiddleware):
38
+ async def dispatch(self, request: Request, call_next: RequestResponseEndpoint):
39
+ incoming_req_id = request.headers.get("X-Request-ID")
40
+ req_id = incoming_req_id or str(uuid4())
41
+
42
+ user_id = getattr(getattr(request, "state", None), "user_id", None) or request.headers.get("X-User-Id")
43
+ ip = request.client.host if request.client else None
44
+ ua = request.headers.get("user-agent")
45
+
46
+ tokens = bind_context(request=request, request_id=req_id, user_id=user_id, ip=ip, user_agent=ua)
47
+ try:
48
+ response = await call_next(request)
49
+ response.headers.setdefault("X-Request-ID", req_id)
50
+ return response
51
+ finally:
52
+ reset_context(tokens)
@@ -0,0 +1,56 @@
1
+ from typing import Optional, Callable, Union
2
+ from fastapi import Request
3
+ from starlette.middleware.base import BaseHTTPMiddleware
4
+
5
+ try:
6
+ from dependency_injector import providers
7
+ ProviderType = providers.Provider # type: ignore
8
+ except Exception:
9
+ ProviderType = object
10
+
11
+ from nlbone.core.ports.auth import AuthService
12
+
13
+
14
+ def _to_factory(auth: Union[AuthService, Callable[[], AuthService], ProviderType]):
15
+ try:
16
+ from dependency_injector import providers as _p # type: ignore
17
+ if isinstance(auth, _p.Provider):
18
+ return auth
19
+ except Exception:
20
+ pass
21
+ if callable(auth) and not hasattr(auth, "verify_token"):
22
+ return auth
23
+ return lambda: auth
24
+
25
+
26
+ class AuthenticationMiddleware(BaseHTTPMiddleware):
27
+ def __init__(self, app, auth: Union[AuthService, Callable[[], AuthService], ProviderType]):
28
+ super().__init__(app)
29
+ self._get_auth = _to_factory(auth)
30
+
31
+ async def dispatch(self, request: Request, call_next):
32
+ request.state.client_id = request.headers.get("X-Client-Id")
33
+ request.state.user_id = None
34
+ request.state.token = None
35
+
36
+ token: Optional[str] = None
37
+ authz = request.headers.get("Authorization")
38
+ if authz:
39
+ try:
40
+ scheme, token = authz.split(" ", 1)
41
+ if scheme.lower() != "bearer":
42
+ token = None
43
+ except ValueError:
44
+ token = None
45
+
46
+ if token:
47
+ request.state.token = token
48
+ try:
49
+ service: AuthService = self._get_auth()
50
+ data = service.verify_token(token)
51
+ if data:
52
+ request.state.user_id = data.get("user_id")
53
+ except Exception:
54
+ pass
55
+
56
+ return await call_next(request)
@@ -0,0 +1,42 @@
1
+ from contextvars import ContextVar
2
+ from typing import Any, Optional
3
+
4
+ request_ctx: ContextVar[Any | None] = ContextVar("request", default=None)
5
+ request_id_ctx: ContextVar[str | None] = ContextVar("request_id", default=None)
6
+ user_id_ctx: ContextVar[str | None] = ContextVar("user_id", default=None)
7
+ ip_ctx: ContextVar[str | None] = ContextVar("ip", default=None)
8
+ user_agent_ctx: ContextVar[str | None] = ContextVar("user_agent", default=None)
9
+
10
+ def bind_context(*, request: Any | None = None, request_id: str | None = None,
11
+ user_id: str | None = None, ip: str | None = None,
12
+ user_agent: str | None = None) -> dict[ContextVar, Any]:
13
+ tokens: dict[ContextVar, Any] = {}
14
+ if request is not None:
15
+ tokens[request_ctx] = request_ctx.set(request)
16
+ if request_id is not None:
17
+ tokens[request_id_ctx] = request_id_ctx.set(request_id)
18
+ if user_id is not None:
19
+ tokens[user_id_ctx] = user_id_ctx.set(user_id)
20
+ if ip is not None:
21
+ tokens[ip_ctx] = ip_ctx.set(ip)
22
+ if user_agent is not None:
23
+ tokens[user_agent_ctx] = user_agent_ctx.set(user_agent)
24
+ return tokens
25
+
26
+ def reset_context(tokens: dict[ContextVar, Any]):
27
+ for var, token in tokens.items():
28
+ var.reset(token)
29
+
30
+ def current_request():
31
+ return request_ctx.get()
32
+
33
+ def current_request_id() -> Optional[str]:
34
+ return request_id_ctx.get()
35
+
36
+ def current_context_dict() -> dict[str, Any]:
37
+ return {
38
+ "request_id": request_id_ctx.get(),
39
+ "user_id": user_id_ctx.get(),
40
+ "ip": ip_ctx.get(),
41
+ "user_agent": user_agent_ctx.get(),
42
+ }
@@ -0,0 +1,5 @@
1
+ from datetime import datetime, timezone
2
+
3
+
4
+ def now() -> datetime:
5
+ return datetime.now(timezone.utc)
@@ -1,13 +0,0 @@
1
- from nlbone.adapters.http_clients.uploadchi import UploadchiClient
2
- from nlbone.adapters.http_clients.uploadchi_async import UploadchiAsyncClient
3
- from nlbone.config.settings import Settings
4
- from nlbone.adapters.auth.keycloak import KeycloakAuthService
5
- from nlbone.core.ports.files import FileServicePort, AsyncFileServicePort
6
-
7
-
8
- class Container:
9
- def __init__(self, settings: Settings | None = None):
10
- self.settings = settings or Settings()
11
- self.auth: KeycloakAuthService = KeycloakAuthService(self.settings)
12
- self.file_service: FileServicePort = UploadchiClient()
13
- self.afiles_service: AsyncFileServicePort = UploadchiAsyncClient()
File without changes
File without changes
File without changes
File without changes
File without changes