maleo-foundation 0.0.1__py3-none-any.whl → 0.0.2__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.
Files changed (52) hide show
  1. maleo_foundation/__init__.py +0 -0
  2. maleo_foundation/clients/__init__.py +7 -0
  3. maleo_foundation/clients/general/__init__.py +4 -0
  4. maleo_foundation/clients/general/http.py +41 -0
  5. maleo_foundation/clients/google/__init__.py +4 -0
  6. maleo_foundation/clients/google/cloud/__init__.py +8 -0
  7. maleo_foundation/clients/google/cloud/logging.py +45 -0
  8. maleo_foundation/clients/google/cloud/secret.py +92 -0
  9. maleo_foundation/clients/google/cloud/storage.py +77 -0
  10. maleo_foundation/constants.py +8 -0
  11. maleo_foundation/controller.py +50 -0
  12. maleo_foundation/db/__init__.py +3 -0
  13. maleo_foundation/db/database.py +41 -0
  14. maleo_foundation/db/engine.py +40 -0
  15. maleo_foundation/db/session.py +64 -0
  16. maleo_foundation/models/__init__.py +13 -0
  17. maleo_foundation/models/enums.py +66 -0
  18. maleo_foundation/models/responses.py +99 -0
  19. maleo_foundation/models/schemas/__init__.py +9 -0
  20. maleo_foundation/models/schemas/general.py +105 -0
  21. maleo_foundation/models/schemas/parameter.py +16 -0
  22. maleo_foundation/models/schemas/result.py +89 -0
  23. maleo_foundation/models/transfers/__init__.py +7 -0
  24. maleo_foundation/models/transfers/parameters/__init__.py +9 -0
  25. maleo_foundation/models/transfers/parameters/client.py +65 -0
  26. maleo_foundation/models/transfers/parameters/general.py +13 -0
  27. maleo_foundation/models/transfers/parameters/service.py +121 -0
  28. maleo_foundation/models/transfers/results/__init__.py +7 -0
  29. maleo_foundation/models/transfers/results/client/__init__.py +7 -0
  30. maleo_foundation/models/transfers/results/client/controllers/__init__.py +5 -0
  31. maleo_foundation/models/transfers/results/client/controllers/http.py +35 -0
  32. maleo_foundation/models/transfers/results/client/service.py +27 -0
  33. maleo_foundation/models/transfers/results/service/__init__.py +9 -0
  34. maleo_foundation/models/transfers/results/service/controllers/__init__.py +5 -0
  35. maleo_foundation/models/transfers/results/service/controllers/rest.py +22 -0
  36. maleo_foundation/models/transfers/results/service/general.py +38 -0
  37. maleo_foundation/models/transfers/results/service/query.py +42 -0
  38. maleo_foundation/models/types/__init__.py +9 -0
  39. maleo_foundation/models/types/client.py +40 -0
  40. maleo_foundation/models/types/query.py +40 -0
  41. maleo_foundation/models/types/service.py +40 -0
  42. maleo_foundation/utils/__init__.py +9 -0
  43. maleo_foundation/utils/exceptions.py +74 -0
  44. maleo_foundation/utils/formatter/__init__.py +4 -0
  45. maleo_foundation/utils/formatter/case.py +37 -0
  46. maleo_foundation/utils/logger.py +68 -0
  47. {maleo_foundation-0.0.1.dist-info → maleo_foundation-0.0.2.dist-info}/METADATA +1 -1
  48. maleo_foundation-0.0.2.dist-info/RECORD +50 -0
  49. maleo_foundation-0.0.2.dist-info/top_level.txt +1 -0
  50. maleo_foundation-0.0.1.dist-info/RECORD +0 -4
  51. maleo_foundation-0.0.1.dist-info/top_level.txt +0 -1
  52. {maleo_foundation-0.0.1.dist-info → maleo_foundation-0.0.2.dist-info}/WHEEL +0 -0
File without changes
@@ -0,0 +1,7 @@
1
+ from __future__ import annotations
2
+ from .general import BaseGeneralClients
3
+ from .google import GoogleClients
4
+
5
+ class BaseClients:
6
+ General = BaseGeneralClients
7
+ Google = GoogleClients
@@ -0,0 +1,4 @@
1
+ from .http import HTTPClientManager
2
+
3
+ class BaseGeneralClients:
4
+ HTTP = HTTPClientManager
@@ -0,0 +1,41 @@
1
+ import httpx
2
+ from contextlib import asynccontextmanager
3
+ from typing import AsyncGenerator, Optional
4
+
5
+ class HTTPClientManager:
6
+ _client:Optional[httpx.AsyncClient] = None
7
+
8
+ @classmethod
9
+ def initialize(cls) -> None:
10
+ """Initialize the HTTP client if not already initialized."""
11
+ if cls._client is None:
12
+ cls._client = httpx.AsyncClient()
13
+
14
+ @classmethod
15
+ async def _client_handler(cls) -> AsyncGenerator[httpx.AsyncClient, None]:
16
+ """Reusable generator for client handling."""
17
+ if cls._client is None:
18
+ raise RuntimeError("Client has not been initialized. Call initialize first.")
19
+
20
+ yield cls._client
21
+
22
+ @classmethod
23
+ async def inject(cls) -> AsyncGenerator[httpx.AsyncClient, None]:
24
+ return cls._client_handler()
25
+
26
+ @classmethod
27
+ @asynccontextmanager
28
+ async def get(cls) -> AsyncGenerator[httpx.AsyncClient, None]:
29
+ """
30
+ Async context manager for manual HTTP client handling.
31
+ Supports `async with HTTPClientManager.get() as client:`
32
+ """
33
+ async for client in cls._client_handler():
34
+ yield client
35
+
36
+ @classmethod
37
+ async def dispose(cls) -> None:
38
+ """Dispose of the HTTP client and release any resources."""
39
+ if cls._client is not None:
40
+ await cls._client.aclose()
41
+ cls._client = None
@@ -0,0 +1,4 @@
1
+ from .cloud import GoogleCloudClients
2
+
3
+ class GoogleClients:
4
+ Cloud = GoogleCloudClients
@@ -0,0 +1,8 @@
1
+ from .logging import GoogleCloudLogging
2
+ from .secret import GoogleSecretManager
3
+ from .storage import GoogleCloudStorage
4
+
5
+ class GoogleCloudClients:
6
+ Logging = GoogleCloudLogging
7
+ Secret = GoogleSecretManager
8
+ Storage = GoogleCloudStorage
@@ -0,0 +1,45 @@
1
+ import os
2
+ from google.auth import default
3
+ from google.cloud.logging import Client
4
+ from google.cloud.logging.handlers import CloudLoggingHandler
5
+ from google.oauth2 import service_account
6
+ from typing import Optional
7
+
8
+ class GoogleCloudLogging:
9
+ _client:Optional[Client] = None
10
+
11
+ @classmethod
12
+ def initialize(cls) -> Client:
13
+ """Initialize the cloud logging if not already initialized."""
14
+ if cls._client is None:
15
+ #* Setup credentials with fallback chain
16
+ credentials = None
17
+ credentials_file = os.getenv("GOOGLE_APPLICATION_CREDENTIALS")
18
+ try:
19
+ if credentials_file:
20
+ credentials = service_account.Credentials.from_service_account_file(credentials_file)
21
+ else:
22
+ credentials, _ = default()
23
+ except Exception as e:
24
+ raise ValueError(f"Failed to initialize credentials: {str(e)}")
25
+
26
+ cls._client = Client(credentials=credentials)
27
+ cls._client.setup_logging()
28
+
29
+ @classmethod
30
+ def dispose(cls) -> None:
31
+ """Dispose of the cloud logging and release any resources."""
32
+ if cls._client is not None:
33
+ cls._client.close()
34
+ cls._client = None
35
+
36
+ @classmethod
37
+ def _get_client(cls) -> Client:
38
+ """Retrieve the cloud logging client, initializing it if necessary."""
39
+ cls.initialize()
40
+ return cls._client
41
+
42
+ @classmethod
43
+ def create_handler(cls, name:str):
44
+ cls.initialize()
45
+ return CloudLoggingHandler(client=cls._client, name=name)
@@ -0,0 +1,92 @@
1
+ import os
2
+ from google.api_core import retry
3
+ from google.api_core.exceptions import NotFound
4
+ from google.auth import default
5
+ from google.cloud import secretmanager
6
+ from google.oauth2 import service_account
7
+ from typing import Optional
8
+
9
+ class GoogleSecretManager:
10
+ _project:Optional[str] = None
11
+ _client:Optional[secretmanager.SecretManagerServiceClient] = None
12
+
13
+ @classmethod
14
+ def initialize(cls, project_id:Optional[str] = None) -> None:
15
+ """Initialize the cloud storage if not already initialized."""
16
+ cls._project = project_id or os.getenv("GCP_PROJECT_ID")
17
+ if cls._project is None:
18
+ raise ValueError("GCP_PROJECT_ID environment variable must be set if no project_id is provided")
19
+ if cls._client is None:
20
+ #* Setup credentials with fallback chain
21
+ credentials = None
22
+ credentials_file = os.getenv("GOOGLE_APPLICATION_CREDENTIALS")
23
+ try:
24
+ if credentials_file:
25
+ credentials = service_account.Credentials.from_service_account_file(credentials_file)
26
+ else:
27
+ credentials, _ = default()
28
+ except Exception as e:
29
+ raise ValueError(f"Failed to initialize credentials: {str(e)}")
30
+
31
+ cls._client = secretmanager.SecretManagerServiceClient(credentials=credentials)
32
+
33
+ @classmethod
34
+ def dispose(cls):
35
+ """Disposes of the Google Secret Manager client"""
36
+ if cls._client is not None:
37
+ cls._client = None
38
+ if cls._project is not None:
39
+ cls._project = None
40
+
41
+ @classmethod
42
+ @retry.Retry(
43
+ predicate=retry.if_exception_type(Exception),
44
+ timeout=5
45
+ )
46
+ def get(
47
+ cls,
48
+ name:str,
49
+ version:str = "latest",
50
+ ) -> Optional[str]:
51
+ try:
52
+ secret_path = f"projects/{cls._project}/secrets/{name}/versions/{version}"
53
+ request = secretmanager.AccessSecretVersionRequest(name=secret_path)
54
+ response = cls._client.access_secret_version(request=request)
55
+ return response.payload.data.decode()
56
+ except Exception as e:
57
+ return None
58
+
59
+ @classmethod
60
+ @retry.Retry(
61
+ predicate=retry.if_exception_type(Exception),
62
+ timeout=5
63
+ )
64
+ def create(
65
+ cls,
66
+ name:str,
67
+ data:str
68
+ ) -> Optional[str]:
69
+ parent = f"projects/{cls._project}"
70
+ secret_path = f"{parent}/secrets/{name}"
71
+ try:
72
+ #* Check if the secret already exists
73
+ request = secretmanager.GetSecretRequest(name=secret_path)
74
+ cls._client.get_secret(request=request)
75
+
76
+ except NotFound:
77
+ #* Secret does not exist, create it first
78
+ try:
79
+ secret = secretmanager.Secret(name=name, replication={"automatic": {}})
80
+ request = secretmanager.CreateSecretRequest(parent=parent, secret_id=name, secret=secret)
81
+ cls._client.create_secret(request=request)
82
+ except Exception as e:
83
+ return None
84
+
85
+ #* Add a new secret version
86
+ try:
87
+ payload = secretmanager.SecretPayload(data=data.encode()) # ✅ Fixed attribute name
88
+ request = secretmanager.AddSecretVersionRequest(parent=secret_path, payload=payload)
89
+ response = cls._client.add_secret_version(request=request)
90
+ return data
91
+ except Exception as e:
92
+ return None
@@ -0,0 +1,77 @@
1
+ import os
2
+ from datetime import timedelta
3
+ from google.auth import default
4
+ from google.cloud.storage import Bucket, Client
5
+ from google.oauth2 import service_account
6
+ from typing import Optional
7
+
8
+ class GoogleCloudStorage:
9
+ _client:Optional[Client] = None
10
+ _bucket:Optional[Bucket] = None
11
+
12
+ @classmethod
13
+ def initialize(cls) -> None:
14
+ """Initialize the cloud storage if not already initialized."""
15
+ if cls._client is None:
16
+ #* Setup credentials with fallback chain
17
+ credentials = None
18
+ credentials_file = os.getenv("GOOGLE_APPLICATION_CREDENTIALS")
19
+ try:
20
+ if credentials_file:
21
+ credentials = service_account.Credentials.from_service_account_file(credentials_file)
22
+ else:
23
+ credentials, _ = default()
24
+ except Exception as e:
25
+ raise ValueError(f"Failed to initialize credentials: {str(e)}")
26
+
27
+ cls._client = Client(credentials=credentials)
28
+
29
+ #* Preload bucket
30
+ bucket_name = os.getenv("GCS_BUCKET_NAME")
31
+ if not bucket_name:
32
+ cls._client.close()
33
+ raise ValueError("GCS_BUCKET_NAME environment variable must be set")
34
+
35
+ #* Validate bucket existence
36
+ bucket = cls._client.lookup_bucket(bucket_name)
37
+ if bucket is None:
38
+ raise ValueError(f"Bucket '{bucket_name}' does not exist.")
39
+
40
+ cls._bucket = bucket
41
+
42
+ @classmethod
43
+ def dispose(cls) -> None:
44
+ """Dispose of the cloud storage and release any resources."""
45
+ if cls._client is not None:
46
+ cls._client.close()
47
+ cls._client = None
48
+ cls._bucket = None
49
+
50
+ @classmethod
51
+ def _get_client(cls) -> Client:
52
+ """Retrieve the cloud storage client, initializing it if necessary."""
53
+ cls.initialize()
54
+ return cls._client
55
+
56
+ @classmethod
57
+ def generate_signed_url(cls, location:str) -> str:
58
+ """
59
+ generate signed URL of a file in the bucket based on its location.
60
+
61
+ Args:
62
+ location: str
63
+ Location of the file inside the bucket
64
+
65
+ Returns:
66
+ str: File's pre-signed download url
67
+
68
+ Raises:
69
+ ValueError: If the file does not exist
70
+ """
71
+ cls.initialize()
72
+ blob = cls._bucket.blob(blob_name=location)
73
+ if not blob.exists():
74
+ raise ValueError(f"File '{location}' did not exists.")
75
+
76
+ url = blob.generate_signed_url(version="v4", expiration=timedelta(minutes=15), method="GET")
77
+ return url
@@ -0,0 +1,8 @@
1
+ import re
2
+
3
+ EMAIL_REGEX:str = r"^[^\s@]+@[^\s@]+\.[^\s@]+$"
4
+ TOKEN_COOKIE_KEY_NAME="token"
5
+ REFRESH_TOKEN_DURATION_DAYS:int = 7
6
+ ACCESS_TOKEN_DURATION_MINUTES:int = 5
7
+ SORT_COLUMN_PATTERN = re.compile(r'^[a-z_]+\.(asc|desc)$')
8
+ DATE_FILTER_PATTERN = re.compile(r'^[a-z_]+(?:\|from::\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2}))?(?:\|to::\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2}))?$')
@@ -0,0 +1,50 @@
1
+ from fastapi import status
2
+ from typing import Type, Any
3
+ from maleo_foundation.models.enums import BaseEnums
4
+ from maleo_foundation.models.responses import BaseResponses
5
+ from maleo_foundation.models.transfers.results.service.controllers.rest import BaseServiceRESTControllerResults
6
+ from maleo_foundation.models.transfers.parameters.service import BaseServiceParametersTransfers
7
+ from maleo_foundation.models.types.service import BaseServiceTypes
8
+
9
+ class BaseController:
10
+ @staticmethod
11
+ def check_unique_existence(
12
+ check:BaseServiceParametersTransfers.UniqueFieldCheck,
13
+ get_single_parameters_class:Type[BaseServiceTypes.GetSingleParameter],
14
+ get_single_service_function:BaseServiceTypes.SyncGetSingleFunction,
15
+ create_failed_response_class:Type[BaseResponses.Fail],
16
+ update_failed_response_class:Type[BaseResponses.Fail],
17
+ **additional_get_parameters:Any
18
+ ) -> BaseServiceRESTControllerResults:
19
+ """Generic helper function to check if a unique value exists in the database."""
20
+
21
+ #* Return early if nullable and no new value
22
+ if check.nullable and check.new_value is None:
23
+ return BaseServiceRESTControllerResults(success=True, content=None)
24
+
25
+ #* Return early if values are unchanged on update
26
+ if check.operation == BaseEnums.OperationType.UPDATE and check.old_value == check.new_value:
27
+ return BaseServiceRESTControllerResults(success=True, content=None)
28
+
29
+ #* Prepare parameters to query for existing data
30
+ get_single_parameters = get_single_parameters_class(identifier=check.field, value=check.new_value)
31
+
32
+ #* Query the existing data using provided function
33
+ service_result:BaseServiceTypes.GetSingleResult = get_single_service_function(parameters=get_single_parameters, **additional_get_parameters)
34
+ if not service_result.success:
35
+ content = BaseResponses.ServerError.model_validate(service_result.model_dump(exclude_unset=True)).model_dump()
36
+ return BaseServiceRESTControllerResults(success=False, content=content, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
37
+
38
+ #* Handle case if duplicate is found
39
+ if service_result.data:
40
+ description = f"External error: {check.field} of '{check.new_value}' already exists in the database"
41
+ other = check.suggestion or f"Select another {check.field} value"
42
+ if check.operation == BaseEnums.OperationType.CREATE:
43
+ content = create_failed_response_class(description=description, other=other).model_dump()
44
+ elif check.operation == BaseEnums.OperationType.UPDATE:
45
+ content = update_failed_response_class(description=description, other=other).model_dump()
46
+
47
+ return BaseServiceRESTControllerResults(success=False, content=content, status_code=status.HTTP_400_BAD_REQUEST)
48
+
49
+ #* No duplicates found
50
+ return BaseServiceRESTControllerResults(success=True, content=None)
@@ -0,0 +1,3 @@
1
+ from .database import DatabaseManager
2
+ from .engine import EngineManager
3
+ from .session import SessionManager
@@ -0,0 +1,41 @@
1
+ from sqlalchemy import Engine, MetaData, Column, Integer, UUID, TIMESTAMP, Enum, func
2
+ from sqlalchemy.orm import declarative_base, declared_attr
3
+ from uuid import uuid4
4
+ from maleo_foundation.models.enums import BaseEnums
5
+ from maleo_foundation.utils.formatter.case import CaseFormatter
6
+
7
+ Base = declarative_base() #* Correct way to define a declarative base
8
+
9
+ class DatabaseManager:
10
+ class Base(Base): #* Inheriting from declarative_base
11
+ __abstract__ = True #* Ensures this class is not treated as a table itself
12
+
13
+ @declared_attr
14
+ def __tablename__(cls) -> str:
15
+ """Automatically generates table name (in snake_case) based on class name."""
16
+ return CaseFormatter.to_snake_case(cls.__name__)
17
+
18
+ #* ----- ----- Common columns definition ----- ----- *#
19
+
20
+ #* Identifiers
21
+ id = Column(name="id", type_=Integer, primary_key=True)
22
+ uuid = Column(name="uuid", type_=UUID, default=uuid4, unique=True, nullable=False)
23
+
24
+ #* Timestamps
25
+ created_at = Column(name="created_at", type_=TIMESTAMP(timezone=True), server_default=func.now(), nullable=False)
26
+ updated_at = Column(name="updated_at", type_=TIMESTAMP(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False)
27
+ deleted_at = Column(name="deleted_at", type_=TIMESTAMP(timezone=True))
28
+ restored_at = Column(name="restored_at", type_=TIMESTAMP(timezone=True))
29
+ deactivated_at = Column(name="deactivated_at", type_=TIMESTAMP(timezone=True))
30
+ activated_at = Column(name="activated_at", type_=TIMESTAMP(timezone=True), server_default=func.now(), nullable=False)
31
+
32
+ #* Statuses
33
+ status = Column(name="status", type_=Enum(BaseEnums.StatusType, name="statustype"), default=BaseEnums.StatusType.ACTIVE, nullable=False)
34
+
35
+ #* Explicitly define the type of metadata
36
+ metadata:MetaData = Base.metadata
37
+
38
+ @staticmethod
39
+ def initialize(engine: Engine):
40
+ """Creates the database tables if they do not exist."""
41
+ DatabaseManager.metadata.create_all(engine)
@@ -0,0 +1,40 @@
1
+ import os
2
+ from sqlalchemy import create_engine, Engine
3
+ from typing import Optional
4
+ from maleo_foundation.utils.logger import BaseLogger
5
+
6
+ class EngineManager:
7
+ _logger:Optional[BaseLogger] = None
8
+ _engine:Optional[Engine] = None
9
+
10
+ @classmethod
11
+ def initialize(cls, logger:BaseLogger, url:Optional[str] = None) -> Engine:
12
+ """Initialize the engine if not already initialized."""
13
+ if cls._engine is None:
14
+ cls._logger = logger
15
+ url = url or os.getenv("DB_CONNECTION_STRING")
16
+ if url is None:
17
+ raise ValueError("DB_CONNECTION_STRING environment variable must be set if url is not provided")
18
+ cls._engine = create_engine(url=url, echo=False, pool_pre_ping=True, pool_recycle=3600)
19
+ cls._logger.info("EngineManager initialized successfully.")
20
+ return cls._engine
21
+
22
+ @classmethod
23
+ def get(cls) -> Engine:
24
+ """Retrieve the engine, initializing it if necessary."""
25
+ if cls._logger is None:
26
+ raise RuntimeError("Logger has not been initialized. Call initialize(db_connection_string, logger) first.")
27
+ if cls._engine is None:
28
+ raise RuntimeError("Engine has not been initialized. Call initialize(db_connection_string, logger) first.")
29
+
30
+ return cls._engine
31
+
32
+ @classmethod
33
+ def dispose(cls) -> None:
34
+ """Dispose of the engine and release any resources."""
35
+ if cls._engine is not None:
36
+ cls._engine.dispose()
37
+ cls._engine = None
38
+
39
+ cls._logger.info("Engine disposed successfully.")
40
+ cls._logger = None
@@ -0,0 +1,64 @@
1
+ from contextlib import contextmanager
2
+ from sqlalchemy import Engine
3
+ from sqlalchemy.orm import sessionmaker, Session
4
+ from sqlalchemy.exc import SQLAlchemyError
5
+ from typing import Generator, Optional
6
+ from maleo_foundation.utils.logger import BaseLogger
7
+
8
+ class SessionManager:
9
+ _logger:Optional[BaseLogger] = None
10
+ _sessionmaker:Optional[sessionmaker[Session]] = None
11
+
12
+ @classmethod
13
+ def initialize(cls, logger:BaseLogger, engine:Engine) -> None:
14
+ """Initialize the sessionmaker if not already initialized."""
15
+ if cls._sessionmaker is None:
16
+ cls._logger = logger
17
+ cls._sessionmaker = sessionmaker(bind=engine, expire_on_commit=False)
18
+ cls._logger.info("SessionManager initialized successfully.")
19
+
20
+ @classmethod
21
+ def _session_handler(cls) -> Generator[Session, None, None]:
22
+ """Reusable function for managing database sessions."""
23
+ if cls._logger is None:
24
+ raise RuntimeError("Logger has not been initialized. Call initialize() first.")
25
+ if cls._sessionmaker is None:
26
+ raise RuntimeError("SessionLocal has not been initialized. Call initialize() first.")
27
+
28
+ session = cls._sessionmaker()
29
+ cls._logger.debug("New database session created.")
30
+ try:
31
+ yield session #* Provide session
32
+ session.commit() #* Auto-commit on success
33
+ except SQLAlchemyError as e:
34
+ session.rollback() #* Rollback on error
35
+ cls._logger.error(f"[SQLAlchemyError] Database transaction failed: {e}", exc_info=True)
36
+ raise
37
+ except Exception as e:
38
+ session.rollback() #* Rollback on error
39
+ cls._logger.error(f"[Exception] Database transaction failed: {e}", exc_info=True)
40
+ raise
41
+ finally:
42
+ session.close() #* Ensure session closes
43
+ cls._logger.debug("Database session closed.")
44
+
45
+ @classmethod
46
+ def inject(cls) -> Generator[Session, None, None]:
47
+ """Returns a generator that yields a SQLAlchemy session for dependency injection."""
48
+ return cls._session_handler()
49
+
50
+ @classmethod
51
+ @contextmanager
52
+ def get(cls) -> Generator[Session, None, None]:
53
+ """Context manager for manual session handling. Supports `with SessionManager.get() as session:`"""
54
+ yield from cls._session_handler()
55
+
56
+ @classmethod
57
+ def dispose(cls) -> None:
58
+ """Dispose of the sessionmaker and release any resources."""
59
+ if cls._sessionmaker is not None:
60
+ cls._sessionmaker.close_all()
61
+ cls._sessionmaker = None
62
+
63
+ cls._logger.info("SessionManager disposed successfully.")
64
+ cls._logger = None
@@ -0,0 +1,13 @@
1
+ from __future__ import annotations
2
+ from .enums import BaseEnums
3
+ from .schemas import BaseSchemas
4
+ from .transfers import BaseTransfers
5
+ from .responses import BaseResponses
6
+ from .types import BaseTypes
7
+
8
+ class BaseModels:
9
+ Enums = BaseEnums
10
+ Schemas = BaseSchemas
11
+ Transfers = BaseTransfers
12
+ Responses = BaseResponses
13
+ Types = BaseTypes
@@ -0,0 +1,66 @@
1
+ import logging
2
+ from enum import IntEnum, StrEnum
3
+ from fastapi import responses
4
+
5
+ class BaseEnums:
6
+ class StatusType(StrEnum):
7
+ DELETED = "deleted"
8
+ INACTIVE = "inactive"
9
+ ACTIVE = "active"
10
+
11
+ class UserType(StrEnum):
12
+ REGULAR = "regular"
13
+ PROXY = "proxy"
14
+
15
+ class SortOrder(StrEnum):
16
+ ASC = "asc"
17
+ DESC = "desc"
18
+
19
+ class StatusUpdateAction(StrEnum):
20
+ ACTIVATE = "activate"
21
+ DEACTIVATE = "deactivate"
22
+ RESTORE = "restore"
23
+ DELETE = "delete"
24
+
25
+ class TokenType(StrEnum):
26
+ REFRESH = "refresh"
27
+ ACCESS = "access"
28
+
29
+ class OperationType(StrEnum):
30
+ CREATE = "create"
31
+ UPDATE = "update"
32
+
33
+ class UniqueIdentifiers(StrEnum):
34
+ ID = "id"
35
+ UUID = "uuid"
36
+
37
+ class RESTControllerResponseType(StrEnum):
38
+ NONE = "none"
39
+ HTML = "html"
40
+ TEXT = "text"
41
+ JSON = "json"
42
+ REDIRECT = "redirect"
43
+ STREAMING = "streaming"
44
+ FILE = "file"
45
+
46
+ def get_response_type(self) -> type[responses.Response]:
47
+ """Returns the corresponding FastAPI Response type."""
48
+ return {
49
+ BaseEnums.RESTControllerResponseType.NONE: responses.Response,
50
+ BaseEnums.RESTControllerResponseType.HTML: responses.HTMLResponse,
51
+ BaseEnums.RESTControllerResponseType.TEXT: responses.PlainTextResponse,
52
+ BaseEnums.RESTControllerResponseType.JSON: responses.JSONResponse,
53
+ BaseEnums.RESTControllerResponseType.REDIRECT: responses.RedirectResponse,
54
+ BaseEnums.RESTControllerResponseType.STREAMING: responses.StreamingResponse,
55
+ BaseEnums.RESTControllerResponseType.FILE: responses.FileResponse,
56
+ }.get(self, responses.Response)
57
+
58
+ class LoggerLevel(IntEnum):
59
+ CRITICAL = logging.CRITICAL
60
+ FATAL = logging.FATAL
61
+ ERROR = logging.ERROR
62
+ WARNING = logging.WARNING
63
+ WARN = logging.WARN
64
+ INFO = logging.INFO
65
+ DEBUG = logging.DEBUG
66
+ NOTSET = logging.NOTSET