Compare commits
4 Commits
2025.09.25
...
2025.09.26
| Author | SHA1 | Date | |
|---|---|---|---|
| 56a16cbeba | |||
| d05d947d54 | |||
| 0529269747 | |||
| e3e1703ff8 |
@@ -25,7 +25,11 @@ jobs:
|
|||||||
git tag
|
git tag
|
||||||
DATE=$(date +'%Y.%m.%d')
|
DATE=$(date +'%Y.%m.%d')
|
||||||
TAG_COUNT=$(git tag -l "${DATE}.*" | wc -l)
|
TAG_COUNT=$(git tag -l "${DATE}.*" | wc -l)
|
||||||
BUILD_NUMBER=$(($TAG_COUNT + 1))
|
if [ "$TAG_COUNT" -eq 0 ]; then
|
||||||
|
BUILD_NUMBER=0
|
||||||
|
else
|
||||||
|
BUILD_NUMBER=$(($TAG_COUNT + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
VERSION_SUFFIX=${{ inputs.version_suffix }}
|
VERSION_SUFFIX=${{ inputs.version_suffix }}
|
||||||
if [ -n "$VERSION_SUFFIX" ] && [ "$VERSION_SUFFIX" = "dev" ]; then
|
if [ -n "$VERSION_SUFFIX" ] && [ "$VERSION_SUFFIX" = "dev" ]; then
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
from starlette.responses import JSONResponse
|
from starlette.responses import JSONResponse
|
||||||
|
|
||||||
from cpl import api
|
from cpl.api.api_module import ApiModule
|
||||||
from cpl.api.application.web_app import WebApp
|
from cpl.api.application.web_app import WebApp
|
||||||
from cpl.api_module import ApiModule
|
from cpl.application.application_builder import ApplicationBuilder
|
||||||
from cpl.application import ApplicationBuilder
|
from cpl.auth import AuthModule
|
||||||
from cpl.auth.permission.permissions import Permissions
|
from cpl.auth.permission.permissions import Permissions
|
||||||
from cpl.auth.schema import AuthUser, Role
|
from cpl.auth.schema import AuthUser, Role
|
||||||
from cpl.core.configuration import Configuration
|
from cpl.core.configuration import Configuration
|
||||||
@@ -40,7 +40,13 @@ def main():
|
|||||||
app.with_authentication()
|
app.with_authentication()
|
||||||
app.with_authorization()
|
app.with_authorization()
|
||||||
|
|
||||||
app.with_route(path="/route1", fn=lambda r: JSONResponse("route1"), method="GET", authentication=True, permissions=[Permissions.administrator])
|
app.with_route(
|
||||||
|
path="/route1",
|
||||||
|
fn=lambda r: JSONResponse("route1"),
|
||||||
|
method="GET",
|
||||||
|
authentication=True,
|
||||||
|
permissions=[Permissions.administrator],
|
||||||
|
)
|
||||||
app.with_routes_directory("routes")
|
app.with_routes_directory("routes")
|
||||||
|
|
||||||
provider = builder.service_provider
|
provider = builder.service_provider
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from .error import APIError, AlreadyExists, EndpointNotImplemented, Forbidden, NotFound, Unauthorized
|
from .error import APIError, AlreadyExists, EndpointNotImplemented, Forbidden, NotFound, Unauthorized
|
||||||
from .logger import APILogger
|
from .logger import APILogger
|
||||||
from .settings import ApiSettings
|
from .settings import ApiSettings
|
||||||
|
from .api_module import ApiModule
|
||||||
|
|||||||
21
src/cpl-api/cpl/api/api_module.py
Normal file
21
src/cpl-api/cpl/api/api_module.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
from cpl.auth.auth_module import AuthModule
|
||||||
|
from cpl.auth.permission.permission_module import PermissionsModule
|
||||||
|
from cpl.database.database_module import DatabaseModule
|
||||||
|
from cpl.dependency.module import Module
|
||||||
|
from cpl.dependency.service_collection import ServiceCollection
|
||||||
|
|
||||||
|
|
||||||
|
class ApiModule(Module):
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def register(collection: ServiceCollection):
|
||||||
|
from cpl.api.registry.policy import PolicyRegistry
|
||||||
|
from cpl.api.registry.route import RouteRegistry
|
||||||
|
|
||||||
|
collection.add_module(DatabaseModule)
|
||||||
|
collection.add_module(AuthModule)
|
||||||
|
collection.add_module(PermissionsModule)
|
||||||
|
|
||||||
|
collection.add_singleton(PolicyRegistry)
|
||||||
|
collection.add_singleton(RouteRegistry)
|
||||||
@@ -25,7 +25,7 @@ from cpl.api.registry.route import RouteRegistry
|
|||||||
from cpl.api.router import Router
|
from cpl.api.router import Router
|
||||||
from cpl.api.settings import ApiSettings
|
from cpl.api.settings import ApiSettings
|
||||||
from cpl.api.typing import HTTPMethods, PartialMiddleware, PolicyResolver
|
from cpl.api.typing import HTTPMethods, PartialMiddleware, PolicyResolver
|
||||||
from cpl.api_module import ApiModule
|
from cpl.api.api_module import ApiModule
|
||||||
from cpl.application.abc.application_abc import ApplicationABC
|
from cpl.application.abc.application_abc import ApplicationABC
|
||||||
from cpl.auth.auth_module import AuthModule
|
from cpl.auth.auth_module import AuthModule
|
||||||
from cpl.auth.permission.permission_module import PermissionsModule
|
from cpl.auth.permission.permission_module import PermissionsModule
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
from cpl.api.registry.policy import PolicyRegistry
|
|
||||||
from cpl.api.registry.route import RouteRegistry
|
|
||||||
from cpl.auth.auth_module import AuthModule
|
|
||||||
from cpl.auth.permission.permission_module import PermissionsModule
|
|
||||||
from cpl.core.errors import dependency_error
|
|
||||||
from cpl.database.database_module import DatabaseModule
|
|
||||||
from cpl.database.model.server_type import ServerType, ServerTypes
|
|
||||||
from cpl.database.mysql.mysql_module import MySQLModule
|
|
||||||
from cpl.dependency.module import Module, TModule
|
|
||||||
|
|
||||||
|
|
||||||
class ApiModule(Module):
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def dependencies() -> list[TModule]:
|
|
||||||
return [AuthModule, DatabaseModule, PermissionsModule]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def register(collection: "ServiceCollection"):
|
|
||||||
collection.add_module(DatabaseModule)
|
|
||||||
|
|
||||||
collection.add_module(AuthModule)
|
|
||||||
collection.add_module(PermissionsModule)
|
|
||||||
|
|
||||||
collection.add_singleton(PolicyRegistry)
|
|
||||||
collection.add_singleton(RouteRegistry)
|
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
from .application_builder import ApplicationBuilder
|
from .application_builder import ApplicationBuilder
|
||||||
from .host import Host
|
from .host import Host
|
||||||
|
|||||||
@@ -77,4 +77,4 @@ class Host:
|
|||||||
if asyncio.iscoroutinefunction(func):
|
if asyncio.iscoroutinefunction(func):
|
||||||
return cls.get_loop().run_until_complete(func(*args, **kwargs))
|
return cls.get_loop().run_until_complete(func(*args, **kwargs))
|
||||||
|
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from cpl.auth.keycloak.keycloak_admin import KeycloakAdmin as _KeycloakAdmin
|
|||||||
from cpl.auth.keycloak.keycloak_client import KeycloakClient as _KeycloakClient
|
from cpl.auth.keycloak.keycloak_client import KeycloakClient as _KeycloakClient
|
||||||
from .keycloak_settings import KeycloakSettings
|
from .keycloak_settings import KeycloakSettings
|
||||||
from .logger import AuthLogger
|
from .logger import AuthLogger
|
||||||
|
from .auth_module import AuthModule
|
||||||
|
|
||||||
|
|
||||||
def _with_permissions(self: _ApplicationABC, *permissions: Type[Enum]) -> _ApplicationABC:
|
def _with_permissions(self: _ApplicationABC, *permissions: Type[Enum]) -> _ApplicationABC:
|
||||||
@@ -17,5 +18,4 @@ def _with_permissions(self: _ApplicationABC, *permissions: Type[Enum]) -> _Appli
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_ApplicationABC.extend(_ApplicationABC.with_permissions, _with_permissions)
|
_ApplicationABC.extend(_ApplicationABC.with_permissions, _with_permissions)
|
||||||
|
|||||||
@@ -1,30 +1,31 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from cpl.auth.keycloak.keycloak_admin import KeycloakAdmin as _KeycloakAdmin
|
|
||||||
from cpl.auth.keycloak.keycloak_client import KeycloakClient as _KeycloakClient
|
|
||||||
from cpl.database.database_module import DatabaseModule
|
from cpl.database.database_module import DatabaseModule
|
||||||
from cpl.database.model.server_type import ServerType, ServerTypes
|
from cpl.database.model.server_type import ServerType, ServerTypes
|
||||||
|
from cpl.database.mysql.mysql_module import MySQLModule
|
||||||
|
from cpl.database.postgres.postgres_module import PostgresModule
|
||||||
from cpl.database.service.migration_service import MigrationService
|
from cpl.database.service.migration_service import MigrationService
|
||||||
from cpl.dependency.module import Module, TModule
|
from cpl.dependency.module import Module
|
||||||
from cpl.dependency.service_collection import ServiceCollection
|
from cpl.dependency.service_collection import ServiceCollection
|
||||||
from .schema._administration.api_key_dao import ApiKeyDao
|
|
||||||
from .schema._administration.auth_user_dao import AuthUserDao
|
|
||||||
from .schema._permission.api_key_permission_dao import ApiKeyPermissionDao
|
|
||||||
from .schema._permission.permission_dao import PermissionDao
|
|
||||||
from .schema._permission.role_dao import RoleDao
|
|
||||||
from .schema._permission.role_permission_dao import RolePermissionDao
|
|
||||||
from .schema._permission.role_user_dao import RoleUserDao
|
|
||||||
|
|
||||||
|
|
||||||
class AuthModule(Module):
|
class AuthModule(Module):
|
||||||
@staticmethod
|
dependencies = [DatabaseModule, (MySQLModule, PostgresModule)]
|
||||||
def dependencies() -> list[TModule]:
|
|
||||||
return [DatabaseModule]
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def register(collection: ServiceCollection):
|
def register(collection: ServiceCollection):
|
||||||
collection.add_singleton(_KeycloakClient)
|
from cpl.auth.keycloak.keycloak_admin import KeycloakAdmin
|
||||||
collection.add_singleton(_KeycloakAdmin)
|
from cpl.auth.keycloak.keycloak_client import KeycloakClient
|
||||||
|
from .schema._administration.api_key_dao import ApiKeyDao
|
||||||
|
from .schema._administration.auth_user_dao import AuthUserDao
|
||||||
|
from .schema._permission.api_key_permission_dao import ApiKeyPermissionDao
|
||||||
|
from .schema._permission.permission_dao import PermissionDao
|
||||||
|
from .schema._permission.role_dao import RoleDao
|
||||||
|
from .schema._permission.role_permission_dao import RolePermissionDao
|
||||||
|
from .schema._permission.role_user_dao import RoleUserDao
|
||||||
|
|
||||||
|
collection.add_singleton(KeycloakClient)
|
||||||
|
collection.add_singleton(KeycloakAdmin)
|
||||||
|
|
||||||
collection.add_singleton(AuthUserDao)
|
collection.add_singleton(AuthUserDao)
|
||||||
collection.add_singleton(ApiKeyDao)
|
collection.add_singleton(ApiKeyDao)
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
from .permission_module import PermissionsModule
|
||||||
|
from .permission_seeder import PermissionSeeder
|
||||||
|
from .permissions import Permissions
|
||||||
|
from .permissions_registry import PermissionsRegistry
|
||||||
|
|||||||
@@ -3,16 +3,13 @@ from cpl.auth.permission.permission_seeder import PermissionSeeder
|
|||||||
from cpl.auth.permission.permissions import Permissions
|
from cpl.auth.permission.permissions import Permissions
|
||||||
from cpl.auth.permission.permissions_registry import PermissionsRegistry
|
from cpl.auth.permission.permissions_registry import PermissionsRegistry
|
||||||
from cpl.database.abc.data_seeder_abc import DataSeederABC
|
from cpl.database.abc.data_seeder_abc import DataSeederABC
|
||||||
from cpl.dependency.module import Module, TModule
|
from cpl.database.database_module import DatabaseModule
|
||||||
|
from cpl.dependency.module import Module
|
||||||
from cpl.dependency.service_collection import ServiceCollection
|
from cpl.dependency.service_collection import ServiceCollection
|
||||||
|
|
||||||
|
|
||||||
class PermissionsModule(Module):
|
class PermissionsModule(Module):
|
||||||
@staticmethod
|
dependencies = [DatabaseModule, AuthModule]
|
||||||
def dependencies() -> list[TModule]:
|
|
||||||
from cpl.database.database_module import DatabaseModule
|
|
||||||
|
|
||||||
return [DatabaseModule, AuthModule]
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def register(collection: ServiceCollection):
|
def register(collection: ServiceCollection):
|
||||||
|
|||||||
@@ -14,8 +14,9 @@ def dependency_error(src: str, package_name: str, e: ImportError = None) -> None
|
|||||||
|
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
def module_dependency_error(src: str, module: str, e: ImportError = None) -> None:
|
def module_dependency_error(src: str, module: str, e: ImportError = None) -> None:
|
||||||
Console.error(f"'{module}' is required to use feature: {src}. Please initialize it with `add_module({module})`.")
|
Console.error(f"'{module}' is required by '{src}'. Please initialize it with `add_module({module})`.")
|
||||||
tb = traceback.format_exc()
|
tb = traceback.format_exc()
|
||||||
if not tb.startswith("NoneType: None"):
|
if not tb.startswith("NoneType: None"):
|
||||||
Console.write_line("->", tb)
|
Console.write_line("->", tb)
|
||||||
@@ -23,4 +24,4 @@ def module_dependency_error(src: str, module: str, e: ImportError = None) -> Non
|
|||||||
elif e is not None:
|
elif e is not None:
|
||||||
Console.write_line("->", str(e))
|
Console.write_line("->", str(e))
|
||||||
|
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ import os
|
|||||||
from cpl.application.abc import ApplicationABC as _ApplicationABC
|
from cpl.application.abc import ApplicationABC as _ApplicationABC
|
||||||
from . import mysql as _mysql
|
from . import mysql as _mysql
|
||||||
from . import postgres as _postgres
|
from . import postgres as _postgres
|
||||||
|
from .database_module import DatabaseModule
|
||||||
from .table_manager import TableManager
|
from .table_manager import TableManager
|
||||||
|
from .logger import DBLogger
|
||||||
|
|
||||||
|
|
||||||
def _with_migrations(self: _ApplicationABC, *paths: str | list[str]) -> _ApplicationABC:
|
def _with_migrations(self: _ApplicationABC, *paths: str | list[str]) -> _ApplicationABC:
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
from .connection_abc import ConnectionABC
|
from .connection_abc import ConnectionABC
|
||||||
|
from .data_access_object_abc import DataAccessObjectABC
|
||||||
|
from .data_seeder_abc import DataSeederABC
|
||||||
from .db_context_abc import DBContextABC
|
from .db_context_abc import DBContextABC
|
||||||
from .db_join_model_abc import DbJoinModelABC
|
from .db_join_model_abc import DbJoinModelABC
|
||||||
from .db_model_abc import DbModelABC
|
from .db_model_abc import DbModelABC
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from cpl.database.logger import DBLogger
|
|||||||
from cpl.database.model.server_type import ServerType, ServerTypes
|
from cpl.database.model.server_type import ServerType, ServerTypes
|
||||||
from cpl.database.postgres.sql_select_builder import SQLSelectBuilder
|
from cpl.database.postgres.sql_select_builder import SQLSelectBuilder
|
||||||
from cpl.database.typing import T_DBM, Attribute, AttributeFilters, AttributeSorts
|
from cpl.database.typing import T_DBM, Attribute, AttributeFilters, AttributeSorts
|
||||||
from cpl.dependency import get_provider
|
from cpl.dependency.context import get_provider
|
||||||
|
|
||||||
|
|
||||||
class DataAccessObjectABC(ABC, Generic[T_DBM]):
|
class DataAccessObjectABC(ABC, Generic[T_DBM]):
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from abc import abstractmethod
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
from cpl.database import TableManager
|
from cpl.database.table_manager import TableManager
|
||||||
from cpl.database.abc.data_access_object_abc import DataAccessObjectABC
|
from cpl.database.abc.data_access_object_abc import DataAccessObjectABC
|
||||||
from cpl.database.abc.db_model_abc import DbModelABC
|
from cpl.database.abc.db_model_abc import DbModelABC
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,18 @@
|
|||||||
from cpl.core.errors import module_dependency_error
|
from cpl.database.mysql.mysql_module import MySQLModule
|
||||||
from cpl.database.model.server_type import ServerType
|
from cpl.database.postgres.postgres_module import PostgresModule
|
||||||
from cpl.database.schema.executed_migration_dao import ExecutedMigrationDao
|
from cpl.dependency.module import Module
|
||||||
from cpl.database.service.migration_service import MigrationService
|
|
||||||
from cpl.database.service.seeder_service import SeederService
|
|
||||||
from cpl.dependency.module import Module, TModule
|
|
||||||
from cpl.dependency.service_collection import ServiceCollection
|
from cpl.dependency.service_collection import ServiceCollection
|
||||||
|
|
||||||
|
|
||||||
class DatabaseModule(Module):
|
class DatabaseModule(Module):
|
||||||
@staticmethod
|
dependencies = [(MySQLModule, PostgresModule)]
|
||||||
def dependencies() -> list[TModule]:
|
|
||||||
if not ServerType.has_server_type:
|
|
||||||
module_dependency_error(__name__, "MySQLModule or PostgresModule")
|
|
||||||
|
|
||||||
return []
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def register(collection: ServiceCollection):
|
def register(collection: ServiceCollection):
|
||||||
|
from cpl.database.schema import ExecutedMigrationDao
|
||||||
|
from cpl.database.service.migration_service import MigrationService
|
||||||
|
from cpl.database.service.seeder_service import SeederService
|
||||||
|
|
||||||
collection.add_singleton(ExecutedMigrationDao)
|
collection.add_singleton(ExecutedMigrationDao)
|
||||||
collection.add_singleton(MigrationService)
|
collection.add_singleton(MigrationService)
|
||||||
collection.add_singleton(SeederService)
|
collection.add_singleton(SeederService)
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
from .connection import DatabaseConnection
|
||||||
|
from .db_context import DBContext
|
||||||
|
from .mysql_module import MySQLModule
|
||||||
|
from .mysql_pool import MySQLPool
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
from cpl.core.configuration.configuration import Configuration
|
from cpl.core.configuration.configuration import Configuration
|
||||||
from cpl.database.abc.db_context_abc import DBContextABC
|
|
||||||
from cpl.database.model.server_type import ServerTypes, ServerType
|
from cpl.database.model.server_type import ServerTypes, ServerType
|
||||||
from cpl.database.mysql.db_context import DBContext
|
from cpl.dependency.module import Module
|
||||||
from cpl.dependency.module import Module, TModule
|
|
||||||
from cpl.dependency.service_collection import ServiceCollection
|
from cpl.dependency.service_collection import ServiceCollection
|
||||||
|
|
||||||
|
|
||||||
class MySQLModule(Module):
|
class MySQLModule(Module):
|
||||||
@staticmethod
|
dependencies = []
|
||||||
def dependencies() -> list[TModule]:
|
|
||||||
return []
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def register(collection: ServiceCollection):
|
def register(collection: ServiceCollection):
|
||||||
|
from cpl.database.abc.db_context_abc import DBContextABC
|
||||||
|
from cpl.database.mysql.db_context import DBContext
|
||||||
|
|
||||||
ServerType.set_server_type(ServerTypes(ServerTypes.MYSQL.value))
|
ServerType.set_server_type(ServerTypes(ServerTypes.MYSQL.value))
|
||||||
Configuration.set("DB_DEFAULT_PORT", 3306)
|
Configuration.set("DB_DEFAULT_PORT", 3306)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
from .db_context import DBContext
|
||||||
|
from .postgres_module import PostgresModule
|
||||||
|
from .postgres_pool import PostgresPool
|
||||||
|
from .sql_select_builder import SQLSelectBuilder
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
from cpl.core.configuration.configuration import Configuration
|
from cpl.core.configuration.configuration import Configuration
|
||||||
from cpl.database.abc.db_context_abc import DBContextABC
|
|
||||||
from cpl.database.database_module import DatabaseModule
|
|
||||||
from cpl.database.model.server_type import ServerTypes, ServerType
|
from cpl.database.model.server_type import ServerTypes, ServerType
|
||||||
from cpl.database.postgres.db_context import DBContext
|
from cpl.dependency.module import Module
|
||||||
from cpl.dependency.module import Module, TModule
|
|
||||||
from cpl.dependency.service_collection import ServiceCollection
|
from cpl.dependency.service_collection import ServiceCollection
|
||||||
|
|
||||||
|
|
||||||
class PostgresModule(Module):
|
class PostgresModule(Module):
|
||||||
@staticmethod
|
dependencies = []
|
||||||
def dependencies() -> list[TModule]:
|
|
||||||
return [DatabaseModule]
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def register(collection: ServiceCollection):
|
def register(collection: ServiceCollection):
|
||||||
|
from cpl.database.abc.db_context_abc import DBContextABC
|
||||||
|
from cpl.database.postgres.db_context import DBContext
|
||||||
|
|
||||||
ServerType.set_server_type(ServerTypes(ServerTypes.POSTGRES.value))
|
ServerType.set_server_type(ServerTypes(ServerTypes.POSTGRES.value))
|
||||||
Configuration.set("DB_DEFAULT_PORT", 5432)
|
Configuration.set("DB_DEFAULT_PORT", 5432)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
from .executed_migration import ExecutedMigration
|
||||||
|
from .executed_migration_dao import ExecutedMigrationDao
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from cpl.database import TableManager
|
from cpl.database.table_manager import TableManager
|
||||||
from cpl.database.abc.data_access_object_abc import DataAccessObjectABC
|
from cpl.database.abc.data_access_object_abc import DataAccessObjectABC
|
||||||
from cpl.database.schema.executed_migration import ExecutedMigration
|
from cpl.database.schema.executed_migration import ExecutedMigration
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from cpl.database.abc import DBContextABC
|
from cpl.database.abc.db_context_abc import DBContextABC
|
||||||
from cpl.database.logger import DBLogger
|
from cpl.database.logger import DBLogger
|
||||||
from cpl.database.model import Migration
|
from cpl.database.model.migration import Migration
|
||||||
from cpl.database.model.server_type import ServerType, ServerTypes
|
from cpl.database.model.server_type import ServerType, ServerTypes
|
||||||
from cpl.database.schema.executed_migration import ExecutedMigration
|
from cpl.database.schema.executed_migration import ExecutedMigration
|
||||||
from cpl.database.schema.executed_migration_dao import ExecutedMigrationDao
|
from cpl.database.schema.executed_migration_dao import ExecutedMigrationDao
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
from .hosted_service import HostedService
|
from .hosted_service import HostedService
|
||||||
from .startup_task import StartupTask
|
from .startup_task import StartupTask
|
||||||
|
|||||||
@@ -6,4 +6,4 @@ class HostedService(ABC):
|
|||||||
async def start(self): ...
|
async def start(self): ...
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def stop(self): ...
|
async def stop(self): ...
|
||||||
|
|||||||
@@ -1,13 +1,22 @@
|
|||||||
from abc import abstractmethod, ABC
|
from abc import abstractmethod, ABC
|
||||||
from typing import Type
|
from inspect import isclass
|
||||||
|
|
||||||
TModule = Type["Module"]
|
|
||||||
|
|
||||||
class Module(ABC):
|
class Module(ABC):
|
||||||
|
__REQUIRED_VARS = ["dependencies"]
|
||||||
|
|
||||||
@staticmethod
|
def __init_subclass__(cls):
|
||||||
@abstractmethod
|
super().__init_subclass__()
|
||||||
def dependencies() -> list[TModule]: ...
|
for var in cls.__REQUIRED_VARS:
|
||||||
|
if not hasattr(cls, var):
|
||||||
|
raise TypeError(f"Can't instantiate abstract class {cls.__name__} without {var} attribute")
|
||||||
|
|
||||||
|
if not isinstance(getattr(cls, var), list):
|
||||||
|
raise TypeError(f"Can't instantiate abstract class {cls.__name__} with non-list {var} attribute")
|
||||||
|
|
||||||
|
for dep in getattr(cls, var):
|
||||||
|
if not isinstance(dep, (list, tuple)) and not isclass(dep):
|
||||||
|
raise TypeError(f"Can't instantiate abstract class {cls.__name__} with invalid dependency {dep}")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from inspect import isclass
|
from inspect import isclass
|
||||||
from typing import Union, Type, Callable, Self
|
from typing import Union, Callable, Self, Type
|
||||||
|
|
||||||
|
from cpl.core.errors import module_dependency_error
|
||||||
from cpl.core.log.logger_abc import LoggerABC
|
from cpl.core.log.logger_abc import LoggerABC
|
||||||
from cpl.core.typing import T, Service
|
from cpl.core.typing import T, Service
|
||||||
from cpl.core.utils.cache import Cache
|
from cpl.core.utils.cache import Cache
|
||||||
@@ -9,6 +10,7 @@ from cpl.dependency.module import Module
|
|||||||
from cpl.dependency.service_descriptor import ServiceDescriptor
|
from cpl.dependency.service_descriptor import ServiceDescriptor
|
||||||
from cpl.dependency.service_lifetime import ServiceLifetimeEnum
|
from cpl.dependency.service_lifetime import ServiceLifetimeEnum
|
||||||
from cpl.dependency.service_provider import ServiceProvider
|
from cpl.dependency.service_provider import ServiceProvider
|
||||||
|
from cpl.dependency.typing import TModule, TService, TStartupTask
|
||||||
|
|
||||||
|
|
||||||
class ServiceCollection:
|
class ServiceCollection:
|
||||||
@@ -16,11 +18,6 @@ class ServiceCollection:
|
|||||||
|
|
||||||
_modules: dict[str, Callable] = {}
|
_modules: dict[str, Callable] = {}
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def with_module(cls, func: Callable, name: str = None) -> type[Self]:
|
|
||||||
# cls._modules[func.__name__ if name is None else name] = func
|
|
||||||
return cls
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._service_descriptors: list[ServiceDescriptor] = []
|
self._service_descriptors: list[ServiceDescriptor] = []
|
||||||
self._loaded_modules: set[str] = set()
|
self._loaded_modules: set[str] = set()
|
||||||
@@ -29,6 +26,47 @@ class ServiceCollection:
|
|||||||
def loaded_modules(self) -> set[str]:
|
def loaded_modules(self) -> set[str]:
|
||||||
return self._loaded_modules
|
return self._loaded_modules
|
||||||
|
|
||||||
|
def _check_dependency(self, module: TModule, dependency: TModule | TService, optional: bool = False) -> bool:
|
||||||
|
if not issubclass(dependency, Module):
|
||||||
|
found_services = [
|
||||||
|
x
|
||||||
|
for x in self._service_descriptors
|
||||||
|
if x.service_type == dependency or x.base_type == dependency or isinstance(x.implementation, dependency)
|
||||||
|
]
|
||||||
|
|
||||||
|
if len(found_services) > 0:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if optional:
|
||||||
|
return False
|
||||||
|
|
||||||
|
module_dependency_error(module.__name__, dependency.__name__)
|
||||||
|
|
||||||
|
if dependency.__name__ not in self._loaded_modules:
|
||||||
|
if optional:
|
||||||
|
return False
|
||||||
|
|
||||||
|
module_dependency_error(module.__name__, dependency.__name__)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _check_dependencies(self, module: TModule):
|
||||||
|
dependencies: list[TModule | Type] = getattr(module, "dependencies", [])
|
||||||
|
for dependency in dependencies:
|
||||||
|
if isinstance(dependency, (list, tuple)):
|
||||||
|
deps_exists = [self._check_dependency(module, dep, optional=True) for dep in dependency]
|
||||||
|
|
||||||
|
if not any(deps_exists):
|
||||||
|
if len(dependency) > 1:
|
||||||
|
names = ", ".join([dep.__name__ for dep in dependency[:-1]]) + f" or {dependency[-1].__name__}"
|
||||||
|
else:
|
||||||
|
names = dependency[0].__name__
|
||||||
|
|
||||||
|
module_dependency_error(module.__name__, names)
|
||||||
|
continue
|
||||||
|
|
||||||
|
self._check_dependency(module, dependency)
|
||||||
|
|
||||||
def _add_descriptor(self, service: Union[type, object], lifetime: ServiceLifetimeEnum, base_type: Callable = None):
|
def _add_descriptor(self, service: Union[type, object], lifetime: ServiceLifetimeEnum, base_type: Callable = None):
|
||||||
found = False
|
found = False
|
||||||
for descriptor in self._service_descriptors:
|
for descriptor in self._service_descriptors:
|
||||||
@@ -44,7 +82,9 @@ class ServiceCollection:
|
|||||||
|
|
||||||
self._service_descriptors.append(ServiceDescriptor(service, lifetime, base_type))
|
self._service_descriptors.append(ServiceDescriptor(service, lifetime, base_type))
|
||||||
|
|
||||||
def _add_descriptor_by_lifetime(self, service_type: Type, lifetime: ServiceLifetimeEnum, service: Callable = None):
|
def _add_descriptor_by_lifetime(
|
||||||
|
self, service_type: TService | T, lifetime: ServiceLifetimeEnum, service: Callable = None
|
||||||
|
):
|
||||||
if service is not None:
|
if service is not None:
|
||||||
self._add_descriptor(service, lifetime, service_type)
|
self._add_descriptor(service, lifetime, service_type)
|
||||||
else:
|
else:
|
||||||
@@ -52,19 +92,19 @@ class ServiceCollection:
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_singleton(self, service_type: T, service: Service = None) -> Self:
|
def add_singleton(self, service_type: TService | T, service: Service = None) -> Self:
|
||||||
self._add_descriptor_by_lifetime(service_type, ServiceLifetimeEnum.singleton, service)
|
self._add_descriptor_by_lifetime(service_type, ServiceLifetimeEnum.singleton, service)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_scoped(self, service_type: T, service: Service = None) -> Self:
|
def add_scoped(self, service_type: TService | T, service: Service = None) -> Self:
|
||||||
self._add_descriptor_by_lifetime(service_type, ServiceLifetimeEnum.scoped, service)
|
self._add_descriptor_by_lifetime(service_type, ServiceLifetimeEnum.scoped, service)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_transient(self, service_type: T, service: Service = None) -> Self:
|
def add_transient(self, service_type: TService | T, service: Service = None) -> Self:
|
||||||
self._add_descriptor_by_lifetime(service_type, ServiceLifetimeEnum.transient, service)
|
self._add_descriptor_by_lifetime(service_type, ServiceLifetimeEnum.transient, service)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_startup_task(self, task: Type[StartupTask]) -> Self:
|
def add_startup_task(self, task: TStartupTask) -> Self:
|
||||||
self.add_singleton(StartupTask, task)
|
self.add_singleton(StartupTask, task)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@@ -76,17 +116,15 @@ class ServiceCollection:
|
|||||||
sp = ServiceProvider(self._service_descriptors)
|
sp = ServiceProvider(self._service_descriptors)
|
||||||
return sp
|
return sp
|
||||||
|
|
||||||
def add_module(self, module: Type[Module]) -> Self:
|
def add_module(self, module: TModule) -> Self:
|
||||||
assert isclass(module), "Module must be a Module"
|
assert isclass(module), "Module must be a Module"
|
||||||
assert issubclass(module, Module), f"Module must be subclass of {Module.__name__}"
|
assert issubclass(module, Module), f"Module must be subclass of {Module.__name__}"
|
||||||
|
|
||||||
name = module.__name__
|
name = module.__name__
|
||||||
if module in self._modules:
|
if module in self._modules:
|
||||||
raise ValueError(f"Module {module} not found")
|
raise ValueError(f"Module {name} is already registered")
|
||||||
|
|
||||||
for dependency in module.dependencies():
|
self._check_dependencies(module)
|
||||||
if dependency.__name__ not in self._loaded_modules:
|
|
||||||
self.add_module(dependency)
|
|
||||||
|
|
||||||
module().register(self)
|
module().register(self)
|
||||||
|
|
||||||
@@ -114,6 +152,6 @@ class ServiceCollection:
|
|||||||
self.add_transient(wrapper)
|
self.add_transient(wrapper)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_cache(self, t: Type[T]):
|
def add_cache(self, t: TService):
|
||||||
self._service_descriptors.append(ServiceDescriptor(Cache(t=t), ServiceLifetimeEnum.singleton, Cache[t]))
|
self._service_descriptors.append(ServiceDescriptor(Cache(t=t), ServiceLifetimeEnum.singleton, Cache[t]))
|
||||||
return self
|
return self
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ class ServiceProvider:
|
|||||||
type_args = list(typing.get_args(service_type))
|
type_args = list(typing.get_args(service_type))
|
||||||
|
|
||||||
for descriptor in self._service_descriptors:
|
for descriptor in self._service_descriptors:
|
||||||
if typing.get_origin(service_type) is None and (descriptor.service_type == service_type or issubclass(descriptor.base_type, service_type)):
|
if typing.get_origin(service_type) is None and (
|
||||||
|
descriptor.service_type == service_type or issubclass(descriptor.base_type, service_type)
|
||||||
|
):
|
||||||
return descriptor
|
return descriptor
|
||||||
|
|
||||||
descriptor_base_type = typing.get_origin(descriptor.base_type) or descriptor.base_type
|
descriptor_base_type = typing.get_origin(descriptor.base_type) or descriptor.base_type
|
||||||
@@ -65,9 +67,7 @@ class ServiceProvider:
|
|||||||
implementations.append(descriptor.implementation)
|
implementations.append(descriptor.implementation)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
implementation = self._build_service(
|
implementation = self._build_service(descriptor, *args, origin_service_type=service_type, **kwargs)
|
||||||
descriptor, *args, origin_service_type=service_type, **kwargs
|
|
||||||
)
|
|
||||||
if descriptor.lifetime in (ServiceLifetimeEnum.singleton, ServiceLifetimeEnum.scoped):
|
if descriptor.lifetime in (ServiceLifetimeEnum.singleton, ServiceLifetimeEnum.scoped):
|
||||||
descriptor.implementation = implementation
|
descriptor.implementation = implementation
|
||||||
|
|
||||||
@@ -81,7 +81,9 @@ class ServiceProvider:
|
|||||||
parameter = param[1]
|
parameter = param[1]
|
||||||
if parameter.name != "self" and parameter.annotation != Parameter.empty:
|
if parameter.name != "self" and parameter.annotation != Parameter.empty:
|
||||||
if typing.get_origin(parameter.annotation) == list:
|
if typing.get_origin(parameter.annotation) == list:
|
||||||
params.append(self._get_services(typing.get_args(parameter.annotation)[0], service_type=origin_service_type))
|
params.append(
|
||||||
|
self._get_services(typing.get_args(parameter.annotation)[0], service_type=origin_service_type)
|
||||||
|
)
|
||||||
|
|
||||||
elif parameter.annotation == Source:
|
elif parameter.annotation == Source:
|
||||||
params.append(origin_service_type.__name__)
|
params.append(origin_service_type.__name__)
|
||||||
@@ -104,7 +106,9 @@ class ServiceProvider:
|
|||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
def _build_service(self, descriptor: ServiceDescriptor, *args, origin_service_type: type = None, **kwargs) -> object:
|
def _build_service(
|
||||||
|
self, descriptor: ServiceDescriptor, *args, origin_service_type: type = None, **kwargs
|
||||||
|
) -> object:
|
||||||
if descriptor.implementation is not None:
|
if descriptor.implementation is not None:
|
||||||
service_type = type(descriptor.implementation)
|
service_type = type(descriptor.implementation)
|
||||||
else:
|
else:
|
||||||
@@ -131,7 +135,11 @@ class ServiceProvider:
|
|||||||
yield scoped_provider
|
yield scoped_provider
|
||||||
|
|
||||||
def get_hosted_services(self) -> list[Optional[T]]:
|
def get_hosted_services(self) -> list[Optional[T]]:
|
||||||
hosted_services = [self.get_service(d.service_type) for d in self._service_descriptors if d.lifetime == ServiceLifetimeEnum.hosted]
|
hosted_services = [
|
||||||
|
self.get_service(d.service_type)
|
||||||
|
for d in self._service_descriptors
|
||||||
|
if d.lifetime == ServiceLifetimeEnum.hosted
|
||||||
|
]
|
||||||
return hosted_services
|
return hosted_services
|
||||||
|
|
||||||
def get_service(self, service_type: Type[T], *args, **kwargs) -> Optional[T]:
|
def get_service(self, service_type: Type[T], *args, **kwargs) -> Optional[T]:
|
||||||
|
|||||||
9
src/cpl-dependency/cpl/dependency/typing.py
Normal file
9
src/cpl-dependency/cpl/dependency/typing.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from typing import Type
|
||||||
|
|
||||||
|
from cpl.core.typing import T
|
||||||
|
from cpl.dependency.hosted import StartupTask
|
||||||
|
from cpl.dependency.module import Module
|
||||||
|
|
||||||
|
TModule = Type[Module]
|
||||||
|
TService = Type[T]
|
||||||
|
TStartupTask = Type[StartupTask]
|
||||||
@@ -1,20 +1,6 @@
|
|||||||
from cpl.dependency import ServiceCollection as _ServiceCollection
|
|
||||||
from .abc.email_client_abc import EMailClientABC
|
from .abc.email_client_abc import EMailClientABC
|
||||||
from .email_client import EMailClient
|
from .email_client import EMailClient
|
||||||
from .email_client_settings import EMailClientSettings
|
from .email_client_settings import EMailClientSettings
|
||||||
from .email_model import EMail
|
from .email_model import EMail
|
||||||
from .logger import MailLogger
|
from .logger import MailLogger
|
||||||
|
from .mail_module import MailModule
|
||||||
|
|
||||||
def add_mail(collection: _ServiceCollection):
|
|
||||||
from cpl.core.console import Console
|
|
||||||
from cpl.core.log import LoggerABC
|
|
||||||
|
|
||||||
try:
|
|
||||||
collection.add_singleton(EMailClientABC, EMailClient)
|
|
||||||
collection.add_transient(LoggerABC, MailLogger)
|
|
||||||
except ImportError as e:
|
|
||||||
Console.error("cpl-translation is not installed", str(e))
|
|
||||||
|
|
||||||
|
|
||||||
_ServiceCollection.with_module(add_mail, __name__)
|
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
from cpl.dependency import ServiceCollection
|
from cpl.dependency.service_collection import ServiceCollection
|
||||||
from cpl.dependency.module import Module, TModule
|
from cpl.dependency.module import Module, TModule
|
||||||
from cpl.mail.email_client import EMailClient
|
|
||||||
from cpl.mail.abc.email_client_abc import EMailClientABC
|
|
||||||
|
|
||||||
|
|
||||||
class MailModule(Module):
|
class MailModule(Module):
|
||||||
@staticmethod
|
dependencies = []
|
||||||
def dependencies() -> list[TModule]:
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def register(collection: ServiceCollection):
|
def register(collection: ServiceCollection):
|
||||||
collection.add_singleton(EMailClientABC, EMailClient)
|
from cpl.mail.abc.email_client_abc import EMailClientABC
|
||||||
|
from cpl.mail.email_client import EMailClient
|
||||||
|
|
||||||
|
collection.add_singleton(EMailClientABC, EMailClient)
|
||||||
|
|||||||
@@ -1,22 +1,5 @@
|
|||||||
from cpl.dependency import ServiceCollection as _ServiceCollection
|
|
||||||
from .translate_pipe import TranslatePipe
|
from .translate_pipe import TranslatePipe
|
||||||
|
from .translation_module import TranslationModule
|
||||||
from .translation_service import TranslationService
|
from .translation_service import TranslationService
|
||||||
from .translation_service_abc import TranslationServiceABC
|
from .translation_service_abc import TranslationServiceABC
|
||||||
from .translation_settings import TranslationSettings
|
from .translation_settings import TranslationSettings
|
||||||
|
|
||||||
|
|
||||||
def add_translation(collection: _ServiceCollection):
|
|
||||||
from cpl.core.console import Console
|
|
||||||
from cpl.core.pipes import PipeABC
|
|
||||||
from cpl.translation.translate_pipe import TranslatePipe
|
|
||||||
from cpl.translation.translation_service import TranslationService
|
|
||||||
from cpl.translation.translation_service_abc import TranslationServiceABC
|
|
||||||
|
|
||||||
try:
|
|
||||||
collection.add_singleton(TranslationServiceABC, TranslationService)
|
|
||||||
collection.add_transient(PipeABC, TranslatePipe)
|
|
||||||
except ImportError as e:
|
|
||||||
Console.error("cpl-translation is not installed", str(e))
|
|
||||||
|
|
||||||
|
|
||||||
_ServiceCollection.with_module(add_translation, __name__)
|
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
from cpl.dependency import ServiceCollection
|
from cpl.dependency.service_collection import ServiceCollection
|
||||||
from cpl.dependency.module import Module, TModule
|
from cpl.dependency.module import Module
|
||||||
from cpl.translation.translation_service import TranslationService
|
|
||||||
from cpl.translation.translation_service_abc import TranslationServiceABC
|
from cpl.translation.translation_service_abc import TranslationServiceABC
|
||||||
|
|
||||||
|
|
||||||
class TranslationModule(Module):
|
class TranslationModule(Module):
|
||||||
@staticmethod
|
dependencies = []
|
||||||
def dependencies() -> list[TModule]:
|
|
||||||
return []
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def register(collection: ServiceCollection):
|
def register(collection: ServiceCollection):
|
||||||
|
from cpl.translation.translation_service import TranslationService
|
||||||
|
|
||||||
collection.add_singleton(TranslationServiceABC, TranslationService)
|
collection.add_singleton(TranslationServiceABC, TranslationService)
|
||||||
|
|||||||
Reference in New Issue
Block a user