WIP: dev into master #184
@@ -2,6 +2,7 @@ from starlette.responses import JSONResponse
|
|||||||
|
|
||||||
from cpl import api
|
from cpl import api
|
||||||
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 import ApplicationBuilder
|
from cpl.application import ApplicationBuilder
|
||||||
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
|
||||||
@@ -9,6 +10,7 @@ from cpl.core.configuration import Configuration
|
|||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
from cpl.core.environment import Environment
|
from cpl.core.environment import Environment
|
||||||
from cpl.core.utils.cache import Cache
|
from cpl.core.utils.cache import Cache
|
||||||
|
from cpl.database.mysql.mysql_module import MySQLModule
|
||||||
from scoped_service import ScopedService
|
from scoped_service import ScopedService
|
||||||
from service import PingService
|
from service import PingService
|
||||||
|
|
||||||
@@ -23,7 +25,8 @@ def main():
|
|||||||
# builder.services.add_logging()
|
# builder.services.add_logging()
|
||||||
builder.services.add_structured_logging()
|
builder.services.add_structured_logging()
|
||||||
builder.services.add_transient(PingService)
|
builder.services.add_transient(PingService)
|
||||||
builder.services.add_module(api)
|
builder.services.add_module(MySQLModule)
|
||||||
|
builder.services.add_module(ApiModule)
|
||||||
|
|
||||||
builder.services.add_scoped(ScopedService)
|
builder.services.add_scoped(ScopedService)
|
||||||
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"Project": {
|
|
||||||
"Name": "database",
|
|
||||||
"Version": {
|
|
||||||
"Major": "0",
|
|
||||||
"Minor": "0",
|
|
||||||
"Micro": "0"
|
|
||||||
},
|
|
||||||
"Author": "",
|
|
||||||
"AuthorEmail": "",
|
|
||||||
"Description": "",
|
|
||||||
"LongDescription": "",
|
|
||||||
"URL": "",
|
|
||||||
"CopyrightDate": "",
|
|
||||||
"CopyrightName": "",
|
|
||||||
"LicenseName": "",
|
|
||||||
"LicenseDescription": "",
|
|
||||||
"Dependencies": [
|
|
||||||
"sh_cpl==2021.4.2.dev1"
|
|
||||||
],
|
|
||||||
"PythonVersion": ">=3.9.2",
|
|
||||||
"PythonPath": {},
|
|
||||||
"Classifiers": []
|
|
||||||
},
|
|
||||||
"Build": {
|
|
||||||
"ProjectType": "console",
|
|
||||||
"SourcePath": "src",
|
|
||||||
"OutputPath": "dist",
|
|
||||||
"Main": "main",
|
|
||||||
"EntryPoint": "database",
|
|
||||||
"IncludePackageData": false,
|
|
||||||
"Included": [],
|
|
||||||
"Excluded": [
|
|
||||||
"*/__pycache__",
|
|
||||||
"*/logs",
|
|
||||||
"*/tests"
|
|
||||||
],
|
|
||||||
"PackageData": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"Workspace": {
|
|
||||||
"DefaultProject": "di",
|
|
||||||
"Projects": {
|
|
||||||
"di": "src/di/di.json"
|
|
||||||
},
|
|
||||||
"Scripts": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"Workspace": {
|
|
||||||
"DefaultProject": "general",
|
|
||||||
"Projects": {
|
|
||||||
"general": "src/general/general.json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
{
|
|
||||||
"Project": {
|
|
||||||
"Name": "general",
|
|
||||||
"Version": {
|
|
||||||
"Major": "2021",
|
|
||||||
"Minor": "04",
|
|
||||||
"Micro": "01"
|
|
||||||
},
|
|
||||||
"Author": "Sven Heidemann",
|
|
||||||
"AuthorEmail": "sven.heidemann@sh-edraft.de",
|
|
||||||
"Description": "sh-edraft Common Python library",
|
|
||||||
"LongDescription": "sh-edraft Common Python library",
|
|
||||||
"URL": "https://www.sh-edraft.de",
|
|
||||||
"CopyrightDate": "2020 - 2021",
|
|
||||||
"CopyrightName": "sh-edraft.de",
|
|
||||||
"LicenseName": "MIT",
|
|
||||||
"LicenseDescription": "MIT, see LICENSE for more details.",
|
|
||||||
"Dependencies": [
|
|
||||||
"cpl-core==2022.10.0.post9",
|
|
||||||
"cpl-translation==2022.10.0.post2",
|
|
||||||
"cpl-query==2022.10.0.post2"
|
|
||||||
],
|
|
||||||
"DevDependencies": [
|
|
||||||
"cpl-cli==2022.10"
|
|
||||||
],
|
|
||||||
"PythonVersion": ">=3.10",
|
|
||||||
"PythonPath": {
|
|
||||||
"linux": "../../venv/bin/python",
|
|
||||||
"win32": ""
|
|
||||||
},
|
|
||||||
"Classifiers": []
|
|
||||||
},
|
|
||||||
"Build": {
|
|
||||||
"ProjectType": "console",
|
|
||||||
"SourcePath": "",
|
|
||||||
"OutputPath": "dist",
|
|
||||||
"Main": "main",
|
|
||||||
"EntryPoint": "",
|
|
||||||
"IncludePackageData": true,
|
|
||||||
"Included": [
|
|
||||||
"*/templates"
|
|
||||||
],
|
|
||||||
"Excluded": [
|
|
||||||
"*/__pycache__",
|
|
||||||
"*/logs",
|
|
||||||
"*/tests"
|
|
||||||
],
|
|
||||||
"PackageData": {},
|
|
||||||
"ProjectReferences": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
class Custom:
|
|
||||||
def __init__(self):
|
|
||||||
print("hello")
|
|
||||||
@@ -4,7 +4,8 @@ from cpl.core.configuration import Configuration
|
|||||||
from cpl.core.environment import Environment
|
from cpl.core.environment import Environment
|
||||||
from cpl.core.pipes import IPAddressPipe
|
from cpl.core.pipes import IPAddressPipe
|
||||||
from cpl.dependency import ServiceCollection
|
from cpl.dependency import ServiceCollection
|
||||||
from example.custom.general.src.hosted_service import Hosted
|
from cpl.mail.mail_module import MailModule
|
||||||
|
from hosted_service import Hosted
|
||||||
from scoped_service import ScopedService
|
from scoped_service import ScopedService
|
||||||
from test_service import TestService
|
from test_service import TestService
|
||||||
|
|
||||||
@@ -20,7 +21,7 @@ class Startup(StartupABC):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def configure_services(services: ServiceCollection):
|
def configure_services(services: ServiceCollection):
|
||||||
services.add_logging()
|
services.add_logging()
|
||||||
services.add_module(mail)
|
services.add_module(MailModule)
|
||||||
services.add_transient(IPAddressPipe)
|
services.add_transient(IPAddressPipe)
|
||||||
services.add_singleton(TestService)
|
services.add_singleton(TestService)
|
||||||
services.add_scoped(ScopedService)
|
services.add_scoped(ScopedService)
|
||||||
@@ -1,36 +1,4 @@
|
|||||||
from cpl.dependency.service_collection import ServiceCollection as _ServiceCollection
|
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
def add_api(collection: _ServiceCollection):
|
|
||||||
try:
|
|
||||||
from cpl.database import mysql
|
|
||||||
|
|
||||||
collection.add_module(mysql)
|
|
||||||
except ImportError as e:
|
|
||||||
from cpl.core.errors import dependency_error
|
|
||||||
|
|
||||||
dependency_error("cpl-database", e)
|
|
||||||
|
|
||||||
try:
|
|
||||||
from cpl import auth
|
|
||||||
from cpl.auth import permission
|
|
||||||
|
|
||||||
collection.add_module(auth)
|
|
||||||
collection.add_module(permission)
|
|
||||||
except ImportError as e:
|
|
||||||
from cpl.core.errors import dependency_error
|
|
||||||
|
|
||||||
dependency_error("cpl-auth", e)
|
|
||||||
|
|
||||||
from cpl.api.registry.policy import PolicyRegistry
|
|
||||||
from cpl.api.registry.route import RouteRegistry
|
|
||||||
|
|
||||||
collection.add_singleton(PolicyRegistry)
|
|
||||||
collection.add_singleton(RouteRegistry)
|
|
||||||
|
|
||||||
|
|
||||||
_ServiceCollection.with_module(add_api, __name__)
|
|
||||||
|
|||||||
@@ -25,7 +25,10 @@ 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.application.abc.application_abc import ApplicationABC
|
from cpl.application.abc.application_abc import ApplicationABC
|
||||||
|
from cpl.auth.auth_module import AuthModule
|
||||||
|
from cpl.auth.permission.permission_module import PermissionsModule
|
||||||
from cpl.core.configuration import Configuration
|
from cpl.core.configuration import Configuration
|
||||||
from cpl.dependency.inject import inject
|
from cpl.dependency.inject import inject
|
||||||
from cpl.dependency.service_provider import ServiceProvider
|
from cpl.dependency.service_provider import ServiceProvider
|
||||||
@@ -35,7 +38,7 @@ PolicyInput = Union[dict[str, PolicyResolver], Policy]
|
|||||||
|
|
||||||
class WebApp(ApplicationABC):
|
class WebApp(ApplicationABC):
|
||||||
def __init__(self, services: ServiceProvider):
|
def __init__(self, services: ServiceProvider):
|
||||||
super().__init__(services, [auth, api])
|
super().__init__(services, [AuthModule, PermissionsModule, ApiModule])
|
||||||
self._app: Starlette | None = None
|
self._app: Starlette | None = None
|
||||||
|
|
||||||
self._logger = services.get_service(APILogger)
|
self._logger = services.get_service(APILogger)
|
||||||
|
|||||||
26
src/cpl-api/cpl/api_module.py
Normal file
26
src/cpl-api/cpl/api_module.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
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)
|
||||||
@@ -49,6 +49,7 @@ class ApplicationBuilder(Generic[TApp]):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
dependency_error(
|
dependency_error(
|
||||||
|
type(app).__name__,
|
||||||
module,
|
module,
|
||||||
ImportError(
|
ImportError(
|
||||||
f"Required module '{module}' for application '{app.__class__.__name__}' is not loaded. Load using 'add_module({module})' method."
|
f"Required module '{module}' for application '{app.__class__.__name__}' is not loaded. Load using 'add_module({module})' method."
|
||||||
|
|||||||
@@ -5,10 +5,8 @@ from cpl.application.abc import ApplicationABC as _ApplicationABC
|
|||||||
from cpl.auth import permission as _permission
|
from cpl.auth import permission as _permission
|
||||||
from cpl.auth.keycloak.keycloak_admin import KeycloakAdmin as _KeycloakAdmin
|
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 cpl.dependency.service_collection import ServiceCollection as _ServiceCollection
|
|
||||||
from .logger import AuthLogger
|
|
||||||
from .keycloak_settings import KeycloakSettings
|
from .keycloak_settings import KeycloakSettings
|
||||||
from .permission_seeder import PermissionSeeder
|
from .logger import AuthLogger
|
||||||
|
|
||||||
|
|
||||||
def _with_permissions(self: _ApplicationABC, *permissions: Type[Enum]) -> _ApplicationABC:
|
def _with_permissions(self: _ApplicationABC, *permissions: Type[Enum]) -> _ApplicationABC:
|
||||||
@@ -19,66 +17,5 @@ def _with_permissions(self: _ApplicationABC, *permissions: Type[Enum]) -> _Appli
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
def _add_daos(collection: _ServiceCollection):
|
|
||||||
from .schema._administration.auth_user_dao import AuthUserDao
|
|
||||||
from .schema._administration.api_key_dao import ApiKeyDao
|
|
||||||
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(AuthUserDao)
|
|
||||||
collection.add_singleton(ApiKeyDao)
|
|
||||||
collection.add_singleton(ApiKeyPermissionDao)
|
|
||||||
collection.add_singleton(PermissionDao)
|
|
||||||
collection.add_singleton(RoleDao)
|
|
||||||
collection.add_singleton(RolePermissionDao)
|
|
||||||
collection.add_singleton(RoleUserDao)
|
|
||||||
|
|
||||||
|
|
||||||
def add_auth(collection: _ServiceCollection):
|
|
||||||
import os
|
|
||||||
|
|
||||||
try:
|
|
||||||
from cpl.database.service.migration_service import MigrationService
|
|
||||||
from cpl.database.model.server_type import ServerType, ServerTypes
|
|
||||||
|
|
||||||
collection.add_singleton(_KeycloakClient)
|
|
||||||
collection.add_singleton(_KeycloakAdmin)
|
|
||||||
|
|
||||||
_add_daos(collection)
|
|
||||||
|
|
||||||
provider = collection.build()
|
|
||||||
migration_service: MigrationService = provider.get_service(MigrationService)
|
|
||||||
if ServerType.server_type == ServerTypes.POSTGRES:
|
|
||||||
migration_service.with_directory(
|
|
||||||
os.path.join(os.path.dirname(os.path.realpath(__file__)), "scripts/postgres")
|
|
||||||
)
|
|
||||||
elif ServerType.server_type == ServerTypes.MYSQL:
|
|
||||||
migration_service.with_directory(os.path.join(os.path.dirname(os.path.realpath(__file__)), "scripts/mysql"))
|
|
||||||
except ImportError as e:
|
|
||||||
from cpl.core.console import Console
|
|
||||||
|
|
||||||
Console.error("cpl-database is not installed", str(e))
|
|
||||||
|
|
||||||
|
|
||||||
def add_permission(collection: _ServiceCollection):
|
|
||||||
from .permission_seeder import PermissionSeeder
|
|
||||||
from .permission.permissions_registry import PermissionsRegistry
|
|
||||||
from .permission.permissions import Permissions
|
|
||||||
|
|
||||||
try:
|
|
||||||
from cpl.database.abc.data_seeder_abc import DataSeederABC
|
|
||||||
|
|
||||||
collection.add_singleton(DataSeederABC, PermissionSeeder)
|
|
||||||
PermissionsRegistry.with_enum(Permissions)
|
|
||||||
except ImportError as e:
|
|
||||||
from cpl.core.console import Console
|
|
||||||
|
|
||||||
Console.error("cpl-database is not installed", str(e))
|
|
||||||
|
|
||||||
|
|
||||||
_ServiceCollection.with_module(add_auth, __name__)
|
|
||||||
_ServiceCollection.with_module(add_permission, _permission.__name__)
|
|
||||||
_ApplicationABC.extend(_ApplicationABC.with_permissions, _with_permissions)
|
_ApplicationABC.extend(_ApplicationABC.with_permissions, _with_permissions)
|
||||||
|
|||||||
44
src/cpl-auth/cpl/auth/auth_module.py
Normal file
44
src/cpl-auth/cpl/auth/auth_module.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
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.model.server_type import ServerType, ServerTypes
|
||||||
|
from cpl.database.service.migration_service import MigrationService
|
||||||
|
from cpl.dependency.module import Module, TModule
|
||||||
|
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):
|
||||||
|
@staticmethod
|
||||||
|
def dependencies() -> list[TModule]:
|
||||||
|
return [DatabaseModule]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def register(collection: ServiceCollection):
|
||||||
|
collection.add_singleton(_KeycloakClient)
|
||||||
|
collection.add_singleton(_KeycloakAdmin)
|
||||||
|
|
||||||
|
collection.add_singleton(AuthUserDao)
|
||||||
|
collection.add_singleton(ApiKeyDao)
|
||||||
|
collection.add_singleton(ApiKeyPermissionDao)
|
||||||
|
collection.add_singleton(PermissionDao)
|
||||||
|
collection.add_singleton(RoleDao)
|
||||||
|
collection.add_singleton(RolePermissionDao)
|
||||||
|
collection.add_singleton(RoleUserDao)
|
||||||
|
|
||||||
|
provider = collection.build()
|
||||||
|
migration_service: MigrationService = provider.get_service(MigrationService)
|
||||||
|
if ServerType.server_type == ServerTypes.POSTGRES:
|
||||||
|
migration_service.with_directory(
|
||||||
|
os.path.join(os.path.dirname(os.path.realpath(__file__)), "scripts/postgres")
|
||||||
|
)
|
||||||
|
elif ServerType.server_type == ServerTypes.MYSQL:
|
||||||
|
migration_service.with_directory(os.path.join(os.path.dirname(os.path.realpath(__file__)), "scripts/mysql"))
|
||||||
34
src/cpl-auth/cpl/auth/permission/permission_module.py
Normal file
34
src/cpl-auth/cpl/auth/permission/permission_module.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
from cpl.auth.permission.permission_seeder import PermissionSeeder
|
||||||
|
from cpl.auth.permission.permissions import Permissions
|
||||||
|
from cpl.auth.permission.permissions_registry import PermissionsRegistry
|
||||||
|
from cpl.database.abc.data_seeder_abc import DataSeederABC
|
||||||
|
from cpl.database.model.server_type import ServerType, ServerTypes
|
||||||
|
from cpl.dependency.module import Module, TModule
|
||||||
|
from cpl.dependency.service_collection import ServiceCollection
|
||||||
|
|
||||||
|
|
||||||
|
class PermissionsModule(Module):
|
||||||
|
@staticmethod
|
||||||
|
def dependencies() -> list[TModule]:
|
||||||
|
from cpl.database.database_module import DatabaseModule
|
||||||
|
|
||||||
|
r = [DatabaseModule]
|
||||||
|
|
||||||
|
match ServerType.server_type:
|
||||||
|
case ServerTypes.POSTGRES:
|
||||||
|
from cpl.database.postgres.postgres_module import PostgresModule
|
||||||
|
|
||||||
|
r.append(PostgresModule)
|
||||||
|
case ServerTypes.MYSQL:
|
||||||
|
from cpl.database.mysql.mysql_module import MySQLModule
|
||||||
|
|
||||||
|
r.append(MySQLModule)
|
||||||
|
case _:
|
||||||
|
raise Exception(f"Unsupported database type: {ServerType.server_type}")
|
||||||
|
|
||||||
|
return r
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def register(collection: ServiceCollection):
|
||||||
|
collection.add_singleton(DataSeederABC, PermissionSeeder)
|
||||||
|
PermissionsRegistry.with_enum(Permissions)
|
||||||
@@ -3,8 +3,8 @@ import traceback
|
|||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
|
|
||||||
|
|
||||||
def dependency_error(package_name: str, e: ImportError) -> None:
|
def dependency_error(src: str, package_name: str, e: ImportError = None) -> None:
|
||||||
Console.error(f"'{package_name}' is required to use this feature. Please install it and try again.")
|
Console.error(f"'{package_name}' is required to use feature: {src}. Please install it and try again.")
|
||||||
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)
|
||||||
@@ -13,3 +13,14 @@ def dependency_error(package_name: str, e: ImportError) -> None:
|
|||||||
Console.write_line("->", str(e))
|
Console.write_line("->", str(e))
|
||||||
|
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
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})`.")
|
||||||
|
tb = traceback.format_exc()
|
||||||
|
if not tb.startswith("NoneType: None"):
|
||||||
|
Console.write_line("->", tb)
|
||||||
|
|
||||||
|
elif e is not None:
|
||||||
|
Console.write_line("->", str(e))
|
||||||
|
|
||||||
|
exit(1)
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
from typing import Type
|
|
||||||
|
|
||||||
from cpl.application.abc import ApplicationABC as _ApplicationABC
|
from cpl.application.abc import ApplicationABC as _ApplicationABC
|
||||||
from cpl.dependency import ServiceCollection as _ServiceCollection
|
|
||||||
from . import mysql as _mysql
|
from . import mysql as _mysql
|
||||||
from . import postgres as _postgres
|
from . import postgres as _postgres
|
||||||
from .table_manager import TableManager
|
from .table_manager import TableManager
|
||||||
@@ -32,43 +30,5 @@ def _with_seeders(self: _ApplicationABC) -> _ApplicationABC:
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
def _add(collection: _ServiceCollection, db_context: Type, default_port: int, server_type: str):
|
|
||||||
from cpl.core.console import Console
|
|
||||||
from cpl.core.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.database_settings import DatabaseSettings
|
|
||||||
from cpl.database.service.migration_service import MigrationService
|
|
||||||
from cpl.database.service.seeder_service import SeederService
|
|
||||||
from cpl.database.schema.executed_migration_dao import ExecutedMigrationDao
|
|
||||||
|
|
||||||
try:
|
|
||||||
ServerType.set_server_type(ServerTypes(server_type))
|
|
||||||
Configuration.set("DB_DEFAULT_PORT", default_port)
|
|
||||||
|
|
||||||
collection.add_singleton(DBContextABC, db_context)
|
|
||||||
collection.add_singleton(ExecutedMigrationDao)
|
|
||||||
collection.add_singleton(MigrationService)
|
|
||||||
collection.add_singleton(SeederService)
|
|
||||||
except ImportError as e:
|
|
||||||
Console.error("cpl-database is not installed", str(e))
|
|
||||||
|
|
||||||
|
|
||||||
def add_mysql(collection: _ServiceCollection):
|
|
||||||
from cpl.database.mysql.db_context import DBContext
|
|
||||||
from cpl.database.model import ServerTypes
|
|
||||||
|
|
||||||
_add(collection, DBContext, 3306, ServerTypes.MYSQL.value)
|
|
||||||
|
|
||||||
|
|
||||||
def add_postgres(collection: _ServiceCollection):
|
|
||||||
from cpl.database.mysql.db_context import DBContext
|
|
||||||
from cpl.database.model import ServerTypes
|
|
||||||
|
|
||||||
_add(collection, DBContext, 5432, ServerTypes.POSTGRES.value)
|
|
||||||
|
|
||||||
|
|
||||||
_ServiceCollection.with_module(add_mysql, _mysql.__name__)
|
|
||||||
_ServiceCollection.with_module(add_postgres, _postgres.__name__)
|
|
||||||
_ApplicationABC.extend(_ApplicationABC.with_migrations, _with_migrations)
|
_ApplicationABC.extend(_ApplicationABC.with_migrations, _with_migrations)
|
||||||
_ApplicationABC.extend(_ApplicationABC.with_seeders, _with_seeders)
|
_ApplicationABC.extend(_ApplicationABC.with_seeders, _with_seeders)
|
||||||
|
|||||||
22
src/cpl-database/cpl/database/database_module.py
Normal file
22
src/cpl-database/cpl/database/database_module.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
from cpl.core.errors import module_dependency_error
|
||||||
|
from cpl.database.model.server_type import ServerType
|
||||||
|
from cpl.database.schema.executed_migration_dao import ExecutedMigrationDao
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseModule(Module):
|
||||||
|
@staticmethod
|
||||||
|
def dependencies() -> list[TModule]:
|
||||||
|
if not ServerType.has_server_type:
|
||||||
|
module_dependency_error(__name__, "MySQLModule or PostgresModule")
|
||||||
|
|
||||||
|
return []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def register(collection: ServiceCollection):
|
||||||
|
collection.add_singleton(ExecutedMigrationDao)
|
||||||
|
collection.add_singleton(MigrationService)
|
||||||
|
collection.add_singleton(SeederService)
|
||||||
@@ -15,6 +15,11 @@ class ServerType:
|
|||||||
assert isinstance(server_type, ServerTypes), f"Expected ServerType but got {type(server_type)}"
|
assert isinstance(server_type, ServerTypes), f"Expected ServerType but got {type(server_type)}"
|
||||||
cls._server_type = server_type
|
cls._server_type = server_type
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@property
|
||||||
|
def has_server_type(cls) -> bool:
|
||||||
|
return cls._server_type is not None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@property
|
@property
|
||||||
def server_type(cls) -> ServerTypes:
|
def server_type(cls) -> ServerTypes:
|
||||||
|
|||||||
19
src/cpl-database/cpl/database/mysql/mysql_module.py
Normal file
19
src/cpl-database/cpl/database/mysql/mysql_module.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
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.mysql.db_context import DBContext
|
||||||
|
from cpl.dependency.module import Module, TModule
|
||||||
|
from cpl.dependency.service_collection import ServiceCollection
|
||||||
|
|
||||||
|
|
||||||
|
class MySQLModule(Module):
|
||||||
|
@staticmethod
|
||||||
|
def dependencies() -> list[TModule]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def register(collection: ServiceCollection):
|
||||||
|
ServerType.set_server_type(ServerTypes(ServerTypes.MYSQL.value))
|
||||||
|
Configuration.set("DB_DEFAULT_PORT", 3306)
|
||||||
|
|
||||||
|
collection.add_singleton(DBContextABC, DBContext)
|
||||||
20
src/cpl-database/cpl/database/postgres/postgres_module.py
Normal file
20
src/cpl-database/cpl/database/postgres/postgres_module.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
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.postgres.db_context import DBContext
|
||||||
|
from cpl.dependency.module import Module, TModule
|
||||||
|
from cpl.dependency.service_collection import ServiceCollection
|
||||||
|
|
||||||
|
|
||||||
|
class PostgresModule(Module):
|
||||||
|
@staticmethod
|
||||||
|
def dependencies() -> list[TModule]:
|
||||||
|
return [DatabaseModule]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def register(collection: ServiceCollection):
|
||||||
|
ServerType.set_server_type(ServerTypes(ServerTypes.POSTGRES.value))
|
||||||
|
Configuration.set("DB_DEFAULT_PORT", 5432)
|
||||||
|
|
||||||
|
collection.add_singleton(DBContextABC, DBContext)
|
||||||
14
src/cpl-dependency/cpl/dependency/module.py
Normal file
14
src/cpl-dependency/cpl/dependency/module.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from abc import abstractmethod, ABC
|
||||||
|
from typing import Type
|
||||||
|
|
||||||
|
TModule = Type["Module"]
|
||||||
|
|
||||||
|
class Module(ABC):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@abstractmethod
|
||||||
|
def dependencies() -> list[TModule]: ...
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@abstractmethod
|
||||||
|
def register(collection: "ServiceCollection"): ...
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
|
from inspect import isclass
|
||||||
from typing import Union, Type, Callable, Self
|
from typing import Union, Type, Callable, Self
|
||||||
|
|
||||||
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
|
||||||
from cpl.dependency.hosted.startup_task import StartupTask
|
from cpl.dependency.hosted.startup_task import StartupTask
|
||||||
|
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
|
||||||
@@ -16,7 +18,7 @@ class ServiceCollection:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def with_module(cls, func: Callable, name: str = None) -> type[Self]:
|
def with_module(cls, func: Callable, name: str = None) -> type[Self]:
|
||||||
cls._modules[func.__name__ if name is None else name] = func
|
# cls._modules[func.__name__ if name is None else name] = func
|
||||||
return cls
|
return cls
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -74,16 +76,23 @@ class ServiceCollection:
|
|||||||
sp = ServiceProvider(self._service_descriptors)
|
sp = ServiceProvider(self._service_descriptors)
|
||||||
return sp
|
return sp
|
||||||
|
|
||||||
def add_module(self, module: str | object) -> Self:
|
def add_module(self, module: Type[Module]) -> Self:
|
||||||
if not isinstance(module, str):
|
assert isclass(module), "Module must be a Module"
|
||||||
module = module.__name__
|
assert issubclass(module, Module), f"Module must be subclass of {Module.__name__}"
|
||||||
|
|
||||||
if module not in self._modules:
|
name = module.__name__
|
||||||
|
if module in self._modules:
|
||||||
raise ValueError(f"Module {module} not found")
|
raise ValueError(f"Module {module} not found")
|
||||||
|
|
||||||
self._modules[module](self)
|
for dependency in module.dependencies():
|
||||||
if module not in self._loaded_modules:
|
if dependency.__name__ not in self._loaded_modules:
|
||||||
self._loaded_modules.add(module)
|
self.add_module(dependency)
|
||||||
|
|
||||||
|
module().register(self)
|
||||||
|
|
||||||
|
if name not in self._loaded_modules:
|
||||||
|
self._loaded_modules.add(name)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_logging(self) -> Self:
|
def add_logging(self) -> Self:
|
||||||
|
|||||||
15
src/cpl-mail/cpl/mail/mail_module.py
Normal file
15
src/cpl-mail/cpl/mail/mail_module.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
from cpl.dependency import ServiceCollection
|
||||||
|
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):
|
||||||
|
@staticmethod
|
||||||
|
def dependencies() -> list[TModule]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def register(collection: ServiceCollection):
|
||||||
|
collection.add_singleton(EMailClientABC, EMailClient)
|
||||||
@@ -1,15 +1,20 @@
|
|||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
from cpl.core.pipes.pipe_abc import PipeABC
|
from cpl.core.pipes.pipe_abc import PipeABC
|
||||||
|
from cpl.core.typing import T
|
||||||
|
from cpl.dependency import get_provider
|
||||||
from cpl.translation.translation_service_abc import TranslationServiceABC
|
from cpl.translation.translation_service_abc import TranslationServiceABC
|
||||||
|
|
||||||
|
|
||||||
class TranslatePipe(PipeABC):
|
class TranslatePipe(PipeABC):
|
||||||
def __init__(self, translation: TranslationServiceABC):
|
@staticmethod
|
||||||
self._translation = translation
|
def to_str(value: T, *args) -> str:
|
||||||
|
|
||||||
def transform(self, value: any, *args):
|
|
||||||
try:
|
try:
|
||||||
return self._translation.translate(value)
|
translations = get_provider().get_service(TranslationServiceABC)
|
||||||
|
return translations.translate(value)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
Console.error(f"Translation {value} not found")
|
Console.error(f"Translation {value} not found")
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_str(value: str, *args) -> T:
|
||||||
|
pass
|
||||||
|
|||||||
14
src/cpl-translation/cpl/translation/translation_module.py
Normal file
14
src/cpl-translation/cpl/translation/translation_module.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from cpl.dependency import ServiceCollection
|
||||||
|
from cpl.dependency.module import Module, TModule
|
||||||
|
from cpl.translation.translation_service import TranslationService
|
||||||
|
from cpl.translation.translation_service_abc import TranslationServiceABC
|
||||||
|
|
||||||
|
|
||||||
|
class TranslationModule(Module):
|
||||||
|
@staticmethod
|
||||||
|
def dependencies() -> list[TModule]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def register(collection: ServiceCollection):
|
||||||
|
collection.add_singleton(TranslationServiceABC, TranslationService)
|
||||||
Reference in New Issue
Block a user