maleo-foundation 0.2.100__py3-none-any.whl → 0.3.1__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/enums.py CHANGED
@@ -169,4 +169,19 @@ class BaseEnums:
169
169
  EXP_3DY = int(3*24*60*60)
170
170
  EXP_1WK = int(1*7*24*60*60)
171
171
  EXP_2WK = int(2*7*24*60*60)
172
- EXP_1MO = int(1*30*24*60*60)
172
+ EXP_1MO = int(1*30*24*60*60)
173
+
174
+ class Service(StrEnum):
175
+ TELEMETRY = "maleo-telemetry"
176
+ METADATA = "maleo-metadata"
177
+ IDENTITY = "maleo-identity"
178
+ ACCESS = "maleo-access"
179
+ WORKSHOP = "maleo-workshop"
180
+ SOAPIE = "maleo-soapie"
181
+ MEDIX = "maleo-medix"
182
+ FHIR = "maleo-fhir"
183
+ DICOM = "maleo-dicom"
184
+ SCRIBE = "maleo-scribe"
185
+ CDS = "maleo-cds"
186
+ IMAGING = "maleo-imaging"
187
+ MCU = "maleo-mcu"
@@ -1,7 +1,7 @@
1
1
  import os
2
2
  from contextlib import contextmanager
3
3
  from pydantic import BaseModel, Field
4
- from sqlalchemy import MetaData
4
+ from sqlalchemy import MetaData, text
5
5
  from sqlalchemy.engine import Engine, create_engine
6
6
  from sqlalchemy.exc import SQLAlchemyError
7
7
  from sqlalchemy.ext.declarative import DeclarativeMeta
@@ -124,6 +124,21 @@ class DatabaseManager:
124
124
  def session(self) -> SessionManager:
125
125
  return self._session
126
126
 
127
+ def check_connection(self) -> bool:
128
+ """Check database connectivity by executing a simple query."""
129
+ self._logger.info("Checking database connectivity...")
130
+ try:
131
+ with self._session.get() as session:
132
+ session.execute(text("SELECT 1"))
133
+ self._logger.info("Database connectivity check successful.")
134
+ return True
135
+ except SQLAlchemyError as se:
136
+ self._logger.error(f"Database connectivity check failed: {se}", exc_info=True)
137
+ return False
138
+ except Exception as e:
139
+ self._logger.error(f"Unexpected error during connectivity check: {e}", exc_info=True)
140
+ return False
141
+
127
142
  def dispose(self) -> None:
128
143
  #* Dispose session
129
144
  if self._session is not None:
@@ -5,6 +5,7 @@ from pathlib import Path
5
5
  from pydantic_settings import BaseSettings
6
6
  from pydantic import BaseModel, Field
7
7
  from redis.asyncio.client import Redis
8
+ from redis.exceptions import RedisError
8
9
  from starlette.exceptions import HTTPException
9
10
  from starlette.types import Lifespan, AppType
10
11
  from sqlalchemy import MetaData
@@ -332,6 +333,15 @@ class ServiceManager:
332
333
  def cache(self) -> CacheManagers:
333
334
  return self._cache
334
335
 
336
+ def check_redis_connection(self) -> bool:
337
+ try:
338
+ self._redis.ping()
339
+ self._loggers.application.info("Redis connection check successful.")
340
+ return True
341
+ except RedisError as e:
342
+ self._loggers.application.error(f"Redis connection check failed: {e}", exc_info=True)
343
+ return False
344
+
335
345
  def _initialize_cloud_storage(self) -> None:
336
346
  environment = (
337
347
  BaseEnums.EnvironmentType.STAGING
@@ -395,7 +405,7 @@ class ServiceManager:
395
405
  def create_app(
396
406
  self,
397
407
  router: APIRouter,
398
- lifespan: Optional[Lifespan[AppType]]=None
408
+ lifespan: Optional[Lifespan[AppType]] = None
399
409
  ) -> FastAPI:
400
410
  self._loggers.application.info("Creating FastAPI application")
401
411
  root_path = "" if self._settings.ENVIRONMENT == "local" else f"/{self._configs.service.key.removeprefix("maleo-")}"
@@ -217,7 +217,8 @@ class RequestLogger:
217
217
  log_func(
218
218
  f"Request | ID: {request_context.request_id} {authentication_info} | "
219
219
  f"IP: {request_context.ip_address} | Host: {request_context.host} | "
220
- f"Method: {request_context.method} | URL: {request_context.url} - "
220
+ f"Method: {request_context.method} | URL: {request_context.url} | "
221
+ f"Query Parameters: {request_context.query_params} - "
221
222
  f"Response | Status: {response.status_code}"
222
223
  )
223
224
 
@@ -239,7 +240,8 @@ class RequestLogger:
239
240
  self.logger.error(
240
241
  f"Request | ID: {request_context.request_id} {authentication_info} | "
241
242
  f"IP: {request_context.ip_address} | Host: {request_context.host} | "
242
- f"Method: {request_context.method} | URL: {request_context.url} - "
243
+ f"Method: {request_context.method} | URL: {request_context.url} | "
244
+ f"Query Parameters: {request_context.query_params} - "
243
245
  f"Response | Status: 500 | Exception:\n{json.dumps(error_details, indent=4)}"
244
246
  )
245
247
 
@@ -1,7 +1,7 @@
1
1
  from datetime import datetime, timezone
2
2
  from uuid import UUID
3
- from pydantic import BaseModel, Field, model_validator
4
- from typing import Dict, Optional, Any
3
+ from pydantic import BaseModel, Field
4
+ from typing import Optional
5
5
  from .request import RequestContext
6
6
  from .token import MaleoFoundationTokenGeneralTransfers
7
7
  from maleo_foundation.types import BaseTypes
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
  from datetime import datetime, timezone
3
- from pydantic import BaseModel, Field
3
+ from pydantic import BaseModel, Field, field_serializer
4
+ from starlette.datastructures import QueryParams
4
5
  from typing import Dict, Any
5
6
  from uuid import UUID
6
7
  from maleo_foundation.types import BaseTypes
@@ -10,8 +11,8 @@ class RequestContext(BaseModel):
10
11
  requested_at: datetime = Field(datetime.now(tz=timezone.utc), description="Request timestamp")
11
12
  method: str = Field(..., description="Request's method")
12
13
  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")
14
+ path_params: BaseTypes.OptionalStringToAnyDict = Field(None, description="Request's path parameters")
15
+ query_params: BaseTypes.OptionalString = Field(None, description="Request's query parameters")
15
16
  ip_address: str = Field("unknown", description="Client's IP address")
16
17
  is_internal: BaseTypes.OptionalBoolean = Field(None, description="True if IP is internal")
17
18
  user_agent: BaseTypes.OptionalString = Field(None, description="User-Agent string")
@@ -24,14 +25,21 @@ class RequestContext(BaseModel):
24
25
  forwarded_proto: BaseTypes.OptionalString = Field(None, description="Forwarded protocol (http/https)")
25
26
  language: BaseTypes.OptionalString = Field(None, description="Accepted languages from client")
26
27
 
28
+ class Config:
29
+ arbitrary_types_allowed = True
30
+
31
+ @field_serializer('query_params', when_used='json')
32
+ def serialize_query_params(self, qp: QueryParams, _info) -> str:
33
+ return str(qp)
34
+
27
35
  def to_google_pubsub_object(self) -> Dict[str, Any]:
28
36
  result = {
29
37
  "request_id": str(self.request_id),
30
38
  "requested_at": self.requested_at.isoformat(),
31
39
  "method": self.method,
32
40
  "url": self.url,
33
- "path_params": {"map": self.path_params},
34
- "query_params": {"map": self.query_params},
41
+ "path_params": None if self.path_params is None else {"map": self.path_params},
42
+ "query_params": None if self.query_params is None else {"string": self.query_params},
35
43
  "ip_address": self.ip_address,
36
44
  "is_internal": None if self.is_internal is None else {"boolean": self.is_internal},
37
45
  "user_agent": None if self.user_agent is None else {"string": self.user_agent},
@@ -54,8 +62,8 @@ class RequestContext(BaseModel):
54
62
  requested_at = datetime.fromisoformat(obj["requested_at"]),
55
63
  method = obj["method"],
56
64
  url = obj["url"],
57
- path_params = obj["path_params"]["map"] or {},
58
- query_params = obj["query_params"]["map"] or {},
65
+ path_params = None if obj["path_params`"] is None else obj["path_params"]["map"],
66
+ query_params = None if obj["query_params"] is None else obj["query_params"]["string"],
59
67
  ip_address = obj["ip_address"],
60
68
  is_internal = None if obj["is_internal"] is None else bool(obj["is_internal"]["boolean"]),
61
69
  user_agent = None if obj["user_agent"] is None else obj["user_agent"]["string"],
@@ -39,10 +39,10 @@ def extract_request_context(request: Request) -> RequestContext:
39
39
  requested_at=datetime.now(tz=timezone.utc),
40
40
  method=request.method,
41
41
  url=request.url.path,
42
- path_params=dict(request.path_params),
43
- query_params=dict(request.query_params),
42
+ path_params=None if not request.path_params else request.path_params,
43
+ query_params=None if not request.query_params else str(request.query_params),
44
44
  ip_address=ip_address,
45
- is_internal=ip_address.startswith("10.") or ip_address.startswith("192.168.") or ip_address.startswith("172."),
45
+ is_internal=None if ip_address=="unknown" else (ip_address.startswith("10.") or ip_address.startswith("192.168.") or ip_address.startswith("172.")),
46
46
  user_agent=headers.get("user-agent"),
47
47
  ua_browser=ua_browser,
48
48
  ua_mobile=headers.get("sec-ch-ua-mobile"),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maleo_foundation
3
- Version: 0.2.100
3
+ Version: 0.3.1
4
4
  Summary: Foundation package for Maleo
5
5
  Author-email: Agra Bima Yuda <agra@nexmedis.com>
6
6
  License: MIT
@@ -2,7 +2,7 @@ maleo_foundation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
2
2
  maleo_foundation/authentication.py,sha256=6Kw-cl9PlxPNc2nD7Z4sYug2_MhRas6IfyVygQyTHlc,1584
3
3
  maleo_foundation/authorization.py,sha256=HGXCJ47AU1YFMDSmGhrMMmlHnAyghcFZVUiPa1FSvog,283
4
4
  maleo_foundation/constants.py,sha256=JLQNTY5LJ0gjsT2aicNTRyi9hcOtk31ffFS6ywZXbZM,1449
5
- maleo_foundation/enums.py,sha256=_xPH4Vdg6DmbRKJw9NP0yBbX8IQO-WMZaRrejCITQfI,4905
5
+ maleo_foundation/enums.py,sha256=POXtnQq6i7DRlfSUvEOR2YxyViqjsILxucrBQuxlJ-c,5350
6
6
  maleo_foundation/extended_types.py,sha256=pIKt-_9tby4rmune3fmWcCW_mohaNRh_1lywBmdc-L4,301
7
7
  maleo_foundation/rest_controller_result.py,sha256=4KbCmk70IEHj1L1bNJfFg1Y3ifnRSnmvK6dYyVJddok,2014
8
8
  maleo_foundation/types.py,sha256=bUcCR-qRlxxttMxJQnVmtBic3EXEd_urcC2P55evWPc,2451
@@ -32,9 +32,9 @@ maleo_foundation/expanded_types/encryption/__init__.py,sha256=yvqKW_VprQP_3YSL-A
32
32
  maleo_foundation/expanded_types/encryption/aes.py,sha256=1rj43qjIO0TePpr1mErT_NJnqFZSgMM9gybfFxsTams,488
33
33
  maleo_foundation/expanded_types/encryption/rsa.py,sha256=Esf_H8nMz2kOLAWa3M7dlD-sFdFIZylNjB_qB20Yaww,488
34
34
  maleo_foundation/managers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
- maleo_foundation/managers/db.py,sha256=qPRm9MI9lJUAdZeJqv6CB3A2xvLzgN-pm-IQtcU9C8w,5352
35
+ maleo_foundation/managers/db.py,sha256=y5oP3bTXKeXYKqng-E_HZ-3wC0ZPtl5bls0AEEej6zM,6050
36
36
  maleo_foundation/managers/middleware.py,sha256=IjHPuOzXKEK44DqhJejJ6wXacAiUi9GwWAdeuwD0lI4,4271
37
- maleo_foundation/managers/service.py,sha256=b9rNukydWfA8JnIvEbK5crkv3f1VWS5TKivAObTS9Xc,19334
37
+ maleo_foundation/managers/service.py,sha256=LwkDW2U8NRiMrZxtlA9xYx2uj23PBayKqJIaO9NzGpM,19727
38
38
  maleo_foundation/managers/cache/__init__.py,sha256=CeY0oof2bVl_v5WS-FKXNwn2gf3xrEMfUsHK9cHo59s,471
39
39
  maleo_foundation/managers/cache/base.py,sha256=oXxeMjw4iptFwvdESeYF0mgmEzoAzzCAQbnYwTO5tZ4,867
40
40
  maleo_foundation/managers/cache/redis.py,sha256=LMxBNCmal6JjsAi7FTeN191PR1zFGw4jjfrJ9lJ_zu0,1162
@@ -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=7uR6UQKOAeAwjy8deI2z1VgsMl4rd6rvm9V3gSAewEI,16303
50
+ maleo_foundation/middlewares/base.py,sha256=IszulI930Fm4T4LQZJIyV8kebAlXt4joR6knB3QZUgA,16437
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
@@ -63,9 +63,9 @@ maleo_foundation/models/schemas/signature.py,sha256=pP78JZpoizQguVKXv4AXQmB8ebVy
63
63
  maleo_foundation/models/schemas/token.py,sha256=Ay-ntAiKeBjCT4YYw0S3Zd4e-KvHSYvG_hzCMYzd5qY,567
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
- maleo_foundation/models/transfers/general/database.py,sha256=BRWQL6g3BmizPoq3-0SaiDmaxGMGrSUaamVJz_63Kt8,1984
66
+ maleo_foundation/models/transfers/general/database.py,sha256=LEMhHNmqoT7zhZisdI0ZEmFX8zENWUKfcQ4FJsAfr8E,1956
67
67
  maleo_foundation/models/transfers/general/key.py,sha256=S37SqD3qwTbgMk7785hW7Kl9d4Kouh4uPZcGoyMQ_-Q,755
68
- maleo_foundation/models/transfers/general/request.py,sha256=rKol7_Qs-Ylv51P7p_amg13heBzJePoz3yjE96xpsN8,4369
68
+ maleo_foundation/models/transfers/general/request.py,sha256=NTYFOmucc4bMfoocBeHGNLs6mbRdqXyVtp1_yDiv_Gg,4849
69
69
  maleo_foundation/models/transfers/general/signature.py,sha256=J9xQy2HjpCQOnES7RJqsUnDgjFPuakQ1mxyfdTdstSE,297
70
70
  maleo_foundation/models/transfers/general/token.py,sha256=PU-_wLFaY2i8pwRi9-jlk4nh7XzoTKOk0BEsUCGbD80,4979
71
71
  maleo_foundation/models/transfers/parameters/__init__.py,sha256=oKW4RPIEISISRjsJzD8lsCGY1HhZRTzshPpWHcJu86k,353
@@ -104,7 +104,7 @@ maleo_foundation/utils/cleaner.py,sha256=dv80jDd8uAXuqyB5_UPbT2JksJ-hsq286DB2WCj
104
104
  maleo_foundation/utils/client.py,sha256=CGwn8eH5WlwnE5tPMfMAH5V3BItBgVmYBZnXpLjTVSc,2826
105
105
  maleo_foundation/utils/controller.py,sha256=Ub1R-JN6spmXakYrOY7igwaNt4Sg8LASASdXymxZcCI,6954
106
106
  maleo_foundation/utils/exceptions.py,sha256=z24kzEP2geaAEElxXaEy7ln6KodebXvtlu-h1inZ_nw,6376
107
- maleo_foundation/utils/extractor.py,sha256=5ijen3e6mgUXVXIxzvKJqrn5t0g4V5lRzq50PdVhdjo,2092
107
+ maleo_foundation/utils/extractor.py,sha256=ZNX0sQKcUwwh7paUZpdR04a18s8Ru2xNXhWZl-XN3l4,2197
108
108
  maleo_foundation/utils/logging.py,sha256=yXo2ztzt5PBEtyPR8OtYhz5Ob5uwDVwa9wpbxnh_pys,7827
109
109
  maleo_foundation/utils/merger.py,sha256=MdocyCOtIhqjcmqx2mJ0V8vtwsrunRXqhRdrBCruh7Q,622
110
110
  maleo_foundation/utils/query.py,sha256=hhISpBAZ4SV_pGf7uGBC6fzLrs_yj5_8gj-kFFeeNrw,8060
@@ -122,7 +122,7 @@ maleo_foundation/utils/loaders/credential/__init__.py,sha256=qopTKvcMVoTFwyRijeg
122
122
  maleo_foundation/utils/loaders/credential/google.py,sha256=yEL0DIVjiKSlZOxogC5pr_NAdWth9dj_ZatLjRsutIk,1269
123
123
  maleo_foundation/utils/loaders/key/__init__.py,sha256=hVygcC2ImHc_aVrSrOmyedR8tMUZokWUKCKOSh5ctbo,106
124
124
  maleo_foundation/utils/loaders/key/rsa.py,sha256=gDhyX6iTFtHiluuhFCozaZ3pOLKU2Y9TlrNMK_GVyGU,3796
125
- maleo_foundation-0.2.100.dist-info/METADATA,sha256=vM9xZ3jQQCw6ofcprNRVxsaQ4e8Rsf01i3maXDu_AoE,3599
126
- maleo_foundation-0.2.100.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
127
- maleo_foundation-0.2.100.dist-info/top_level.txt,sha256=_iBos3F_bhEOdjOnzeiEYSrCucasc810xXtLBXI8cQc,17
128
- maleo_foundation-0.2.100.dist-info/RECORD,,
125
+ maleo_foundation-0.3.1.dist-info/METADATA,sha256=7ZpZo-lRgi3r6lL7pMtWUQPrRcP8yn6rNlqKR5KKRY8,3597
126
+ maleo_foundation-0.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
127
+ maleo_foundation-0.3.1.dist-info/top_level.txt,sha256=_iBos3F_bhEOdjOnzeiEYSrCucasc810xXtLBXI8cQc,17
128
+ maleo_foundation-0.3.1.dist-info/RECORD,,