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.
- maleo_foundation/middlewares/base.py +11 -11
- maleo_foundation/models/transfers/general/pubsub.py +37 -0
- maleo_foundation/models/transfers/general/request.py +70 -0
- maleo_foundation/utils/dependencies/context.py +2 -2
- maleo_foundation/utils/extractor.py +3 -3
- {maleo_foundation-0.2.98.dist-info → maleo_foundation-0.2.99.dist-info}/METADATA +1 -1
- {maleo_foundation-0.2.98.dist-info → maleo_foundation-0.2.99.dist-info}/RECORD +9 -7
- {maleo_foundation-0.2.98.dist-info → maleo_foundation-0.2.99.dist-info}/WHEEL +0 -0
- {maleo_foundation-0.2.98.dist-info → maleo_foundation-0.2.99.dist-info}/top_level.txt +0 -0
@@ -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
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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
|
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
|
-
) ->
|
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
|
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) ->
|
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
|
37
|
+
return RequestContext(
|
38
38
|
request_id=request_id,
|
39
39
|
requested_at=datetime.now(tz=timezone.utc),
|
40
40
|
method=request.method,
|
@@ -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=
|
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=
|
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=
|
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.
|
124
|
-
maleo_foundation-0.2.
|
125
|
-
maleo_foundation-0.2.
|
126
|
-
maleo_foundation-0.2.
|
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,,
|
File without changes
|
File without changes
|