maleo-foundation 0.2.98__py3-none-any.whl → 0.2.99__py3-none-any.whl

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.
@@ -18,7 +18,7 @@ from maleo_foundation.models.responses import BaseResponses
18
18
  from maleo_foundation.models.transfers.general.token import MaleoFoundationTokenGeneralTransfers
19
19
  from maleo_foundation.models.transfers.parameters.token import MaleoFoundationTokenParametersTransfers
20
20
  from maleo_foundation.models.transfers.parameters.signature import MaleoFoundationSignatureParametersTransfers
21
- from maleo_foundation.models.transfers.general import RequestContextTransfers
21
+ from maleo_foundation.models.transfers.general.request import RequestContext
22
22
  from maleo_foundation.utils.extractor import extract_request_context
23
23
  from maleo_foundation.utils.logging import MiddlewareLogger
24
24
 
@@ -46,7 +46,7 @@ class RateLimiter:
46
46
 
47
47
  def is_rate_limited(
48
48
  self,
49
- request_context: RequestContextTransfers
49
+ request_context: RequestContext
50
50
  ) -> bool:
51
51
  """Check if client IP is rate limited and record the request."""
52
52
  with self._lock:
@@ -117,7 +117,7 @@ class ResponseBuilder:
117
117
  self,
118
118
  authentication: Authentication,
119
119
  response: Response,
120
- request_context: RequestContextTransfers,
120
+ request_context: RequestContext,
121
121
  responded_at: datetime,
122
122
  process_time: float
123
123
  ) -> Response:
@@ -139,7 +139,7 @@ class ResponseBuilder:
139
139
  def _add_signature_header(
140
140
  self,
141
141
  response: Response,
142
- request_context: RequestContextTransfers,
142
+ request_context: RequestContext,
143
143
  responded_at: datetime,
144
144
  process_time: float
145
145
  ) -> None:
@@ -161,7 +161,7 @@ class ResponseBuilder:
161
161
 
162
162
  def _add_new_authorization_header(
163
163
  self,
164
- request_context:RequestContextTransfers,
164
+ request_context:RequestContext,
165
165
  authentication:Authentication,
166
166
  response:Response
167
167
  ) -> None:
@@ -185,7 +185,7 @@ class ResponseBuilder:
185
185
 
186
186
  def _should_regenerate_auth(
187
187
  self,
188
- request_context: RequestContextTransfers,
188
+ request_context: RequestContext,
189
189
  authentication: Authentication,
190
190
  response: Response
191
191
  ) -> bool:
@@ -207,7 +207,7 @@ class RequestLogger:
207
207
  self,
208
208
  authentication: Authentication,
209
209
  response: Response,
210
- request_context: RequestContextTransfers,
210
+ request_context: RequestContext,
211
211
  log_level: str = "info"
212
212
  ) -> None:
213
213
  """Log request and response details."""
@@ -225,7 +225,7 @@ class RequestLogger:
225
225
  self,
226
226
  authentication: Authentication,
227
227
  error: Exception,
228
- request_context: RequestContextTransfers
228
+ request_context: RequestContext
229
229
  ) -> None:
230
230
  """Log exception details."""
231
231
  authentication_info = self._get_authentication_info(authentication)
@@ -338,7 +338,7 @@ class BaseMiddleware(BaseHTTPMiddleware):
338
338
  def _create_rate_limit_response(
339
339
  self,
340
340
  authentication: Authentication,
341
- request_context: RequestContextTransfers,
341
+ request_context: RequestContext,
342
342
  start_time: float
343
343
  ) -> Response:
344
344
  """Create rate limit exceeded response."""
@@ -355,7 +355,7 @@ class BaseMiddleware(BaseHTTPMiddleware):
355
355
  self,
356
356
  authentication: Authentication,
357
357
  response: Response,
358
- request_context: RequestContextTransfers,
358
+ request_context: RequestContext,
359
359
  start_time: float,
360
360
  log_level: str = "info"
361
361
  ) -> Response:
@@ -379,7 +379,7 @@ class BaseMiddleware(BaseHTTPMiddleware):
379
379
  self,
380
380
  authentication: Authentication,
381
381
  error: Exception,
382
- request_context: RequestContextTransfers,
382
+ request_context: RequestContext,
383
383
  start_time: float
384
384
  ) -> Response:
385
385
  """Handle exceptions and create error response."""
@@ -0,0 +1,37 @@
1
+ from datetime import datetime, timezone
2
+ from uuid import UUID
3
+ from pydantic import BaseModel, Field, model_validator
4
+ from typing import Dict, Optional, Any
5
+ from .request import RequestContext
6
+ from .token import MaleoFoundationTokenGeneralTransfers
7
+ from maleo_foundation.types import BaseTypes
8
+
9
+ class DatabaseAccess(BaseModel):
10
+ accessed_at: datetime = Field(datetime.now(tz=timezone.utc), description="Accessed at timestamp")
11
+ request_id: UUID = Field(..., description="Request Id")
12
+ request_context: RequestContext = Field(..., description="Request context")
13
+ organization_id: BaseTypes.OptionalInteger = Field(None, ge=1, description="Organization Id")
14
+ user_id: int = Field(0, ge=0, description="User Id")
15
+ token_string: BaseTypes.OptionalString = Field(None, description="Token string")
16
+ token_payload: Optional[MaleoFoundationTokenGeneralTransfers.DecodePayload] = Field(None, description="Token payload")
17
+ service: str = Field(..., description="Service key")
18
+ table: str = Field(..., description="Table name")
19
+ data_id: int = Field(..., ge=1, description="Data Id")
20
+ data: BaseTypes.StringToAnyDict = Field(..., description="Data")
21
+
22
+ def to_google_pubsub_object(self) -> BaseTypes.StringToAnyDict:
23
+ result = {
24
+ "accessed_at": self.accessed_at.isoformat(),
25
+ "request_id": str(self.request_id),
26
+ "request_context": self.request_context.to_google_pubsub_object(),
27
+ "organization_id": None if self.organization_id is None else {"int": self.organization_id},
28
+ "user_id": self.user_id,
29
+ "token_string": None if self.token_string is None else {"string": self.token_string},
30
+ "token_payload": None if self.token_payload is None else self.token_payload.to_google_pubsub_object(),
31
+ "service": self.service,
32
+ "table": self.table,
33
+ "data_id": self.data_id,
34
+ "data": self.data
35
+ }
36
+
37
+ return result
@@ -0,0 +1,70 @@
1
+ from __future__ import annotations
2
+ from datetime import datetime, timezone
3
+ from pydantic import BaseModel, Field
4
+ from typing import Dict, Any
5
+ from uuid import UUID
6
+ from maleo_foundation.types import BaseTypes
7
+
8
+ class RequestContext(BaseModel):
9
+ request_id: UUID = Field(..., description="Unique identifier for tracing the request")
10
+ requested_at: datetime = Field(datetime.now(tz=timezone.utc), description="Request timestamp")
11
+ method: str = Field(..., description="Request's method")
12
+ url: str = Field(..., description="Request's URL")
13
+ path_params: Dict = Field(..., description="Request's path parameters")
14
+ query_params: Dict = Field(..., description="Request's query parameters")
15
+ ip_address: str = Field("unknown", description="Client's IP address")
16
+ is_internal: BaseTypes.OptionalBoolean = Field(None, description="True if IP is internal")
17
+ user_agent: BaseTypes.OptionalString = Field(None, description="User-Agent string")
18
+ ua_browser: BaseTypes.OptionalString = Field(None, description="Browser info from sec-ch-ua")
19
+ ua_mobile: BaseTypes.OptionalString = Field(None, description="Is mobile device?")
20
+ platform: BaseTypes.OptionalString = Field(None, description="Client platform or OS")
21
+ referer: BaseTypes.OptionalString = Field(None, description="Referrer URL")
22
+ origin: BaseTypes.OptionalString = Field(None, description="Origin of the request")
23
+ host: BaseTypes.OptionalString = Field(None, description="Host header from request")
24
+ forwarded_proto: BaseTypes.OptionalString = Field(None, description="Forwarded protocol (http/https)")
25
+ language: BaseTypes.OptionalString = Field(None, description="Accepted languages from client")
26
+
27
+ def to_google_pubsub_object(self) -> Dict[str, Any]:
28
+ result = {
29
+ "request_id": str(self.request_id),
30
+ "requested_at": self.requested_at.isoformat(),
31
+ "method": self.method,
32
+ "url": self.url,
33
+ "path_params": {"map": self.path_params},
34
+ "query_params": {"map": self.query_params},
35
+ "ip_address": self.ip_address,
36
+ "is_internal": None if self.is_internal is None else {"boolean": self.is_internal},
37
+ "user_agent": None if self.user_agent is None else {"string": self.user_agent},
38
+ "ua_browser": None if self.ua_browser is None else {"string": self.ua_browser},
39
+ "ua_mobile": None if self.ua_mobile is None else {"string": self.ua_mobile},
40
+ "platform": None if self.platform is None else {"string": self.platform},
41
+ "referer": None if self.referer is None else {"array": self.referer},
42
+ "origin": None if self.origin is None else {"array": self.origin},
43
+ "host": None if self.host is None else {"array": self.host},
44
+ "forwarded_proto": None if self.forwarded_proto is None else {"array": self.forwarded_proto},
45
+ "language": None if self.language is None else {"array": self.language}
46
+ }
47
+
48
+ return result
49
+
50
+ @classmethod
51
+ def from_google_pubsub_object(cls, obj:Dict[str, Any]):
52
+ return cls(
53
+ request_id = UUID(obj["request_id"]),
54
+ requested_at = datetime.fromisoformat(obj["requested_at"]),
55
+ method = obj["method"],
56
+ url = obj["url"],
57
+ path_params = obj["path_params"]["map"] or {},
58
+ query_params = obj["query_params"]["map"] or {},
59
+ ip_address = obj["ip_address"],
60
+ is_internal = None if obj["is_internal"] is None else bool(obj["is_internal"]["boolean"]),
61
+ user_agent = None if obj["user_agent"] is None else obj["user_agent"]["string"],
62
+ ua_browser = None if obj["ua_browser"] is None else obj["ua_browser"]["string"],
63
+ ua_mobile = None if obj["ua_mobile"] is None else obj["ua_mobile"]["string"],
64
+ platform = None if obj["platform"] is None else obj["platform"]["string"],
65
+ referer = None if obj["referer"] is None else obj["referer"]["string"],
66
+ origin = None if obj["origin"] is None else obj["origin"]["string"],
67
+ host = None if obj["host"] is None else obj["host"]["string"],
68
+ forwarded_proto = None if obj["forwarded_proto"] is None else obj["forwarded_proto"]["string"],
69
+ language = None if obj["language"] is None else obj["language"]["string"],
70
+ )
@@ -1,9 +1,9 @@
1
1
  from fastapi.requests import Request
2
- from maleo_foundation.models.transfers.general import RequestContextTransfers
2
+ from maleo_foundation.models.transfers.general.request import RequestContext
3
3
 
4
4
  class ContextDependencies:
5
5
  @staticmethod
6
6
  def get_request_context(
7
7
  request: Request
8
- ) -> RequestContextTransfers:
8
+ ) -> RequestContext:
9
9
  return request.state.request_context
@@ -2,7 +2,7 @@ from datetime import datetime, timezone
2
2
  from fastapi import Request
3
3
  from starlette.requests import HTTPConnection
4
4
  from uuid import uuid4
5
- from maleo_foundation.models.transfers.general import RequestContextTransfers
5
+ from maleo_foundation.models.transfers.general.request import RequestContext
6
6
 
7
7
  def extract_client_ip(conn: HTTPConnection) -> str:
8
8
  """Extract client IP with more robust handling of proxies"""
@@ -21,7 +21,7 @@ def extract_client_ip(conn: HTTPConnection) -> str:
21
21
  #* Fall back to direct client connection
22
22
  return conn.client.host if conn.client else "unknown"
23
23
 
24
- def extract_request_context(request: Request) -> RequestContextTransfers:
24
+ def extract_request_context(request: Request) -> RequestContext:
25
25
  headers = request.headers
26
26
 
27
27
  request_id = headers.get("x-request-id")
@@ -34,7 +34,7 @@ def extract_request_context(request: Request) -> RequestContextTransfers:
34
34
  if ua_browser:
35
35
  ua_browser = ua_browser.replace('"', "").split(",")[0].strip()
36
36
 
37
- return RequestContextTransfers(
37
+ return RequestContext(
38
38
  request_id=request_id,
39
39
  requested_at=datetime.now(tz=timezone.utc),
40
40
  method=request.method,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maleo_foundation
3
- Version: 0.2.98
3
+ Version: 0.2.99
4
4
  Summary: Foundation package for Maleo
5
5
  Author-email: Agra Bima Yuda <agra@nexmedis.com>
6
6
  License: MIT
@@ -47,7 +47,7 @@ maleo_foundation/managers/client/google/parameter.py,sha256=Jy-rMz_xhepmxBI2rWPx
47
47
  maleo_foundation/managers/client/google/secret.py,sha256=m-mjaLvYMLgAEn1OxmP0IVTYFQi1jSpDdutLxNA6t6g,4620
48
48
  maleo_foundation/managers/client/google/storage.py,sha256=lEPw4N07nV9r7KjvF2Pb3RM1ZQBK9Riqj7vh6XOEY5Q,5417
49
49
  maleo_foundation/middlewares/authentication.py,sha256=PkC1mtI1LwX6iBTNvpFf-kxFNt6YkshWupM_lt7CaCI,4851
50
- maleo_foundation/middlewares/base.py,sha256=35UEt9gZlQmhp4pmgB2D0qGT8Rxrh2zVCDdO0TIs0N0,16394
50
+ maleo_foundation/middlewares/base.py,sha256=7uR6UQKOAeAwjy8deI2z1VgsMl4rd6rvm9V3gSAewEI,16303
51
51
  maleo_foundation/middlewares/cors.py,sha256=9hLh_h253bvIn7-A7mUblyrJQ37XNpV7aLeHW6ZTHz8,2294
52
52
  maleo_foundation/models/__init__.py,sha256=AaKehO7c1HyKhoTGRmNHDddSeBXkW-_YNrpOGBu8Ms8,246
53
53
  maleo_foundation/models/responses.py,sha256=rhCQ9ERe1BgM2Bx7c4_Qj0Eoq7bJG0xdbnhCiTao6eI,5668
@@ -64,6 +64,8 @@ maleo_foundation/models/schemas/token.py,sha256=Ay-ntAiKeBjCT4YYw0S3Zd4e-KvHSYvG
64
64
  maleo_foundation/models/transfers/__init__.py,sha256=oJLJ3Geeme6vBw7R2Dhvdvg4ziVvzEYAGJaP-tm_90w,299
65
65
  maleo_foundation/models/transfers/general/__init__.py,sha256=rzHpxhyNphl5RVrsAlzEvuYEonmWgb69gJ8XOLMvFDc,4686
66
66
  maleo_foundation/models/transfers/general/key.py,sha256=S37SqD3qwTbgMk7785hW7Kl9d4Kouh4uPZcGoyMQ_-Q,755
67
+ maleo_foundation/models/transfers/general/pubsub.py,sha256=AeHGvMd9UrDUTrX5ui3u4PJIIfJG4gR8Ju3rsUpcesw,1966
68
+ maleo_foundation/models/transfers/general/request.py,sha256=4Eiu8ZAG4GCSb-OVtECqUmepdbUrB9V6IHnxejXEIJE,4371
67
69
  maleo_foundation/models/transfers/general/signature.py,sha256=J9xQy2HjpCQOnES7RJqsUnDgjFPuakQ1mxyfdTdstSE,297
68
70
  maleo_foundation/models/transfers/general/token.py,sha256=PU-_wLFaY2i8pwRi9-jlk4nh7XzoTKOk0BEsUCGbD80,4979
69
71
  maleo_foundation/models/transfers/parameters/__init__.py,sha256=oKW4RPIEISISRjsJzD8lsCGY1HhZRTzshPpWHcJu86k,353
@@ -102,7 +104,7 @@ maleo_foundation/utils/cleaner.py,sha256=CqVF3TPLBL50l6QEwDdDme9oJOmQ6S8VD4Yw1ri
102
104
  maleo_foundation/utils/client.py,sha256=CGwn8eH5WlwnE5tPMfMAH5V3BItBgVmYBZnXpLjTVSc,2826
103
105
  maleo_foundation/utils/controller.py,sha256=Ub1R-JN6spmXakYrOY7igwaNt4Sg8LASASdXymxZcCI,6954
104
106
  maleo_foundation/utils/exceptions.py,sha256=z24kzEP2geaAEElxXaEy7ln6KodebXvtlu-h1inZ_nw,6376
105
- maleo_foundation/utils/extractor.py,sha256=yS73o13S_hjiSU1PLptg0fF4cSLmC97-2WyvD7sHGeY,2111
107
+ maleo_foundation/utils/extractor.py,sha256=5ijen3e6mgUXVXIxzvKJqrn5t0g4V5lRzq50PdVhdjo,2092
106
108
  maleo_foundation/utils/logging.py,sha256=yXo2ztzt5PBEtyPR8OtYhz5Ob5uwDVwa9wpbxnh_pys,7827
107
109
  maleo_foundation/utils/merger.py,sha256=MdocyCOtIhqjcmqx2mJ0V8vtwsrunRXqhRdrBCruh7Q,622
108
110
  maleo_foundation/utils/query.py,sha256=hhISpBAZ4SV_pGf7uGBC6fzLrs_yj5_8gj-kFFeeNrw,8060
@@ -110,7 +112,7 @@ maleo_foundation/utils/repository.py,sha256=pxpws2PDyXwGACms_1azlabqzT6q1x41aYAQ
110
112
  maleo_foundation/utils/searcher.py,sha256=aHj3Lm9arrkWT76lYBtsFEJwLMRfHvPeOtVDwlEVgyU,516
111
113
  maleo_foundation/utils/dependencies/__init__.py,sha256=0KKGrdfj8Cc5A4SRk_ZBAxzOP795Mizdb4zIBh07KC4,122
112
114
  maleo_foundation/utils/dependencies/auth.py,sha256=LmduYWx3SZoVGsvrEMTXUy0Ur_Etkx1Z6rvVuw8a7ME,729
113
- maleo_foundation/utils/dependencies/context.py,sha256=ANqynJVME4wl-greJouoSP74cy7NlboI46CheGP2KSY,293
115
+ maleo_foundation/utils/dependencies/context.py,sha256=ee23A8o9JTV5CwAoaWQxzEfHE3s2ln-EzYJvi1X80v8,283
114
116
  maleo_foundation/utils/formatter/__init__.py,sha256=iKf5YCbEdg1qKnFHyKqqcQbqAqEeRUf8mhI3v3dQoj8,78
115
117
  maleo_foundation/utils/formatter/case.py,sha256=K2iwVrYXP0HQ5I3VxHYyzxC2LzNjlkiWXjrN2azMIDA,1357
116
118
  maleo_foundation/utils/loaders/__init__.py,sha256=P_3ycGfeDXFjAi8bE4iLWHxBveqUIdpHgGv-klRWM3s,282
@@ -120,7 +122,7 @@ maleo_foundation/utils/loaders/credential/__init__.py,sha256=qopTKvcMVoTFwyRijeg
120
122
  maleo_foundation/utils/loaders/credential/google.py,sha256=yEL0DIVjiKSlZOxogC5pr_NAdWth9dj_ZatLjRsutIk,1269
121
123
  maleo_foundation/utils/loaders/key/__init__.py,sha256=hVygcC2ImHc_aVrSrOmyedR8tMUZokWUKCKOSh5ctbo,106
122
124
  maleo_foundation/utils/loaders/key/rsa.py,sha256=gDhyX6iTFtHiluuhFCozaZ3pOLKU2Y9TlrNMK_GVyGU,3796
123
- maleo_foundation-0.2.98.dist-info/METADATA,sha256=8ZinmqAZ0RtlOGjb-d8-i4_oFqbRGNHnfbn7Y6ItpI0,3598
124
- maleo_foundation-0.2.98.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
125
- maleo_foundation-0.2.98.dist-info/top_level.txt,sha256=_iBos3F_bhEOdjOnzeiEYSrCucasc810xXtLBXI8cQc,17
126
- maleo_foundation-0.2.98.dist-info/RECORD,,
125
+ maleo_foundation-0.2.99.dist-info/METADATA,sha256=Cw79wvF0EE9W54nU38webzShSl1EZjq3TTgo4zbC84g,3598
126
+ maleo_foundation-0.2.99.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
127
+ maleo_foundation-0.2.99.dist-info/top_level.txt,sha256=_iBos3F_bhEOdjOnzeiEYSrCucasc810xXtLBXI8cQc,17
128
+ maleo_foundation-0.2.99.dist-info/RECORD,,