From 78dcbdcbaaba319cc59ab93c28b7855e37090563 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 29 Nov 2020 21:36:16 +0100 Subject: [PATCH] Improved structure and added basics for database module --- src/sh_edraft/coding/model/version.py | 2 + src/sh_edraft/configuration/configuration.py | 4 +- src/sh_edraft/database/__init__.py | 3 + src/sh_edraft/database/base/__init__.py | 3 + .../database/base/database_connection_base.py | 10 +++ src/sh_edraft/database/database_connection.py | 35 ++++++++ src/sh_edraft/database/model/__init__.py | 0 .../database/model/database_settings.py | 80 +++++++++++++++++++ .../database/model/database_settings_name.py | 10 +++ src/sh_edraft/hosting/application_host.py | 4 +- .../hosting/base/application_host_base.py | 2 +- src/sh_edraft/logging/logger.py | 4 +- .../model/publish_settings_model.py | 4 +- src/sh_edraft/publishing/model/template.py | 1 + src/sh_edraft/service/__init__.py | 1 - src/sh_edraft/service/base/__init__.py | 1 - src/sh_edraft/service/model/__init__.py | 1 - src/sh_edraft/service/providing/__init__.py | 3 + .../service/providing/base/__init__.py | 3 + .../base/service_provider_base.py | 0 .../service/providing/model/__init__.py | 3 + .../{ => providing}/model/provide_state.py | 2 +- .../{ => providing}/service_provider.py | 6 +- src/sh_edraft/utils/__init__.py | 1 + src/sh_edraft/utils/credential_manager.py | 17 ++++ src/tests_dev/appsettings.development.json | 6 ++ src/tests_dev/program.py | 15 +++- 27 files changed, 202 insertions(+), 19 deletions(-) create mode 100644 src/sh_edraft/database/__init__.py create mode 100644 src/sh_edraft/database/base/__init__.py create mode 100644 src/sh_edraft/database/base/database_connection_base.py create mode 100644 src/sh_edraft/database/database_connection.py create mode 100644 src/sh_edraft/database/model/__init__.py create mode 100644 src/sh_edraft/database/model/database_settings.py create mode 100644 src/sh_edraft/database/model/database_settings_name.py create mode 100644 src/sh_edraft/service/providing/__init__.py create mode 100644 src/sh_edraft/service/providing/base/__init__.py rename src/sh_edraft/service/{ => providing}/base/service_provider_base.py (100%) create mode 100644 src/sh_edraft/service/providing/model/__init__.py rename src/sh_edraft/service/{ => providing}/model/provide_state.py (85%) rename src/sh_edraft/service/{ => providing}/service_provider.py (93%) create mode 100644 src/sh_edraft/utils/credential_manager.py diff --git a/src/sh_edraft/coding/model/version.py b/src/sh_edraft/coding/model/version.py index 26e4f9c0..be2d5a3d 100644 --- a/src/sh_edraft/coding/model/version.py +++ b/src/sh_edraft/coding/model/version.py @@ -12,6 +12,8 @@ class Version(ConfigurationModelBase): minor: int = None, micro: float = None ): + ConfigurationModelBase.__init__(self) + self._major: Optional[int] = major self._minor: Optional[int] = minor self._micro: Optional[int] = micro diff --git a/src/sh_edraft/configuration/configuration.py b/src/sh_edraft/configuration/configuration.py index dc0ef33f..f32148f0 100644 --- a/src/sh_edraft/configuration/configuration.py +++ b/src/sh_edraft/configuration/configuration.py @@ -7,8 +7,8 @@ from sh_edraft.configuration.base.configuration_base import ConfigurationBase from sh_edraft.configuration.model.configuration_variable_name import ConfigurationVariableName from sh_edraft.environment.base.environment_base import EnvironmentBase from sh_edraft.environment.hosting_environment import HostingEnvironment -from sh_edraft.environment.model import EnvironmentName -from sh_edraft.utils import Console +from sh_edraft.environment.model.environment_name import EnvironmentName +from sh_edraft.utils.console import Console class Configuration(ConfigurationBase): diff --git a/src/sh_edraft/database/__init__.py b/src/sh_edraft/database/__init__.py new file mode 100644 index 00000000..eae4c82d --- /dev/null +++ b/src/sh_edraft/database/__init__.py @@ -0,0 +1,3 @@ +# imports: + +from .database_connection import DatabaseConnection diff --git a/src/sh_edraft/database/base/__init__.py b/src/sh_edraft/database/base/__init__.py new file mode 100644 index 00000000..876327c9 --- /dev/null +++ b/src/sh_edraft/database/base/__init__.py @@ -0,0 +1,3 @@ +# imports: + +from .database_connection_base import DatabaseConnectionBase diff --git a/src/sh_edraft/database/base/database_connection_base.py b/src/sh_edraft/database/base/database_connection_base.py new file mode 100644 index 00000000..9307c9b1 --- /dev/null +++ b/src/sh_edraft/database/base/database_connection_base.py @@ -0,0 +1,10 @@ +from abc import abstractmethod + +from sh_edraft.service.base.service_base import ServiceBase + + +class DatabaseConnectionBase(ServiceBase): + + @abstractmethod + def __init__(self): + ServiceBase.__init__(self) diff --git a/src/sh_edraft/database/database_connection.py b/src/sh_edraft/database/database_connection.py new file mode 100644 index 00000000..799f714b --- /dev/null +++ b/src/sh_edraft/database/database_connection.py @@ -0,0 +1,35 @@ +from typing import Optional + +from sqlalchemy import engine, create_engine +from sqlalchemy.orm import session, sessionmaker + +from sh_edraft.database.base.database_connection_base import DatabaseConnectionBase +from sh_edraft.database.model.database_settings import DatabaseSettings + + +class DatabaseConnection(DatabaseConnectionBase): + + def __init__(self, database_settings: DatabaseSettings): + DatabaseConnectionBase.__init__(self) + + self._db_settings = database_settings + + self._engine: Optional[engine] = None + self._session: Optional[session] = None + self._credentials: Optional[str] = None + + def create(self): + self._engine = create_engine(self._db_settings.decrypted_connection_string) + + if self._db_settings.encoding is not None: + self._engine.encoding = self._db_settings.encoding + + if self._db_settings.case_sensitive is not None: + self._engine.case_sensitive = self._db_settings.case_sensitive + + if self._db_settings.echo is not None: + self._engine.echo = self._db_settings.echo + + db_session = sessionmaker(bind=self._engine) + self._session = db_session() + diff --git a/src/sh_edraft/database/model/__init__.py b/src/sh_edraft/database/model/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/sh_edraft/database/model/database_settings.py b/src/sh_edraft/database/model/database_settings.py new file mode 100644 index 00000000..8c2def60 --- /dev/null +++ b/src/sh_edraft/database/model/database_settings.py @@ -0,0 +1,80 @@ +import traceback +from typing import Optional + +from sh_edraft.configuration.base.configuration_model_base import ConfigurationModelBase +from sh_edraft.database.model.database_settings_name import DatabaseSettingsName +from sh_edraft.utils.credential_manager import CredentialManager +from sh_edraft.utils.console import Console + + +class DatabaseSettings(ConfigurationModelBase): + + def __init__(self): + ConfigurationModelBase.__init__(self) + + self._connection_string: Optional[str] = None + self._credentials: Optional[str] = None + self._encoding: Optional[str] = None + self._case_sensitive: Optional[bool] = None + self._echo: Optional[bool] = None + + @property + def connection_string(self) -> str: + return self._connection_string + + @connection_string.setter + def connection_string(self, connection_string: str): + self._connection_string = connection_string + + @property + def decrypted_connection_string(self) -> str: + return CredentialManager.build_string(self._connection_string, self._credentials) + + @property + def credentials(self) -> str: + return self._credentials + + @credentials.setter + def credentials(self, credentials: str): + self._credentials = credentials + + @property + def encoding(self) -> str: + return self._encoding + + @encoding.setter + def encoding(self, encoding: str) -> None: + self._encoding = encoding + + @property + def case_sensitive(self) -> bool: + return self._case_sensitive + + @case_sensitive.setter + def case_sensitive(self, case_sensitive: bool) -> None: + self._case_sensitive = case_sensitive + + @property + def echo(self) -> bool: + return self._echo + + @echo.setter + def echo(self, echo: bool) -> None: + self._echo = echo + + def from_dict(self, settings: dict): + try: + self._connection_string = settings[DatabaseSettingsName.connection_string.value] + self._credentials = settings[DatabaseSettingsName.credentials.value] + + if DatabaseSettingsName.encoding.value in settings: + self._encoding = settings[DatabaseSettingsName.encoding.value] + + if DatabaseSettingsName.case_sensitive.value in settings: + self._case_sensitive = bool(settings[DatabaseSettingsName.case_sensitive.value]) + + if DatabaseSettingsName.echo.value in settings: + self._echo = bool(settings[DatabaseSettingsName.echo.value]) + except Exception as e: + Console.write_line(f'[ ERROR ] [ {__name__} ]: Reading error in {self.__name__} settings', 'red') + Console.write_line(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}', 'red') diff --git a/src/sh_edraft/database/model/database_settings_name.py b/src/sh_edraft/database/model/database_settings_name.py new file mode 100644 index 00000000..57ece941 --- /dev/null +++ b/src/sh_edraft/database/model/database_settings_name.py @@ -0,0 +1,10 @@ +from enum import Enum + + +class DatabaseSettingsName(Enum): + + connection_string = 'ConnectionString' + credentials = 'Credentials' + encoding = 'Encoding' + case_sensitive = 'CaseSensitive' + echo = 'Echo' diff --git a/src/sh_edraft/hosting/application_host.py b/src/sh_edraft/hosting/application_host.py index e5e6b362..d86283d0 100644 --- a/src/sh_edraft/hosting/application_host.py +++ b/src/sh_edraft/hosting/application_host.py @@ -5,8 +5,8 @@ from sh_edraft.configuration.base.configuration_base import ConfigurationBase from sh_edraft.hosting.base.application_runtime_base import ApplicationRuntimeBase from sh_edraft.hosting.application_runtime import ApplicationRuntime from sh_edraft.hosting.base.application_host_base import ApplicationHostBase -from sh_edraft.service.service_provider import ServiceProvider -from sh_edraft.service.base.service_provider_base import ServiceProviderBase +from sh_edraft.service.providing.service_provider import ServiceProvider +from sh_edraft.service.providing.base.service_provider_base import ServiceProviderBase class ApplicationHost(ApplicationHostBase): diff --git a/src/sh_edraft/hosting/base/application_host_base.py b/src/sh_edraft/hosting/base/application_host_base.py index 4ee8c0f5..fb816ccb 100644 --- a/src/sh_edraft/hosting/base/application_host_base.py +++ b/src/sh_edraft/hosting/base/application_host_base.py @@ -2,7 +2,7 @@ from abc import ABC, abstractmethod from sh_edraft.configuration.base.configuration_base import ConfigurationBase from sh_edraft.hosting.base.application_runtime_base import ApplicationRuntimeBase -from sh_edraft.service.base.service_provider_base import ServiceProviderBase +from sh_edraft.service.providing.base.service_provider_base import ServiceProviderBase class ApplicationHostBase(ABC): diff --git a/src/sh_edraft/logging/logger.py b/src/sh_edraft/logging/logger.py index 01b58937..f2159ad2 100644 --- a/src/sh_edraft/logging/logger.py +++ b/src/sh_edraft/logging/logger.py @@ -5,9 +5,9 @@ from string import Template from sh_edraft.hosting.base.application_runtime_base import ApplicationRuntimeBase from sh_edraft.logging.base.logger_base import LoggerBase -from sh_edraft.logging.model import LoggingSettings +from sh_edraft.logging.model.logging_settings import LoggingSettings from sh_edraft.logging.model.logging_level import LoggingLevel -from sh_edraft.time.model import TimeFormatSettings +from sh_edraft.time.model.time_format_settings import TimeFormatSettings from sh_edraft.utils.console import Console diff --git a/src/sh_edraft/publishing/model/publish_settings_model.py b/src/sh_edraft/publishing/model/publish_settings_model.py index 3886262d..a743692a 100644 --- a/src/sh_edraft/publishing/model/publish_settings_model.py +++ b/src/sh_edraft/publishing/model/publish_settings_model.py @@ -2,9 +2,9 @@ import traceback from typing import Optional from sh_edraft.configuration.base.configuration_model_base import ConfigurationModelBase -from sh_edraft.publishing.model import Template +from sh_edraft.publishing.model.template import Template from sh_edraft.publishing.model.publish_settings_name import PublishSettingsName -from sh_edraft.utils import Console +from sh_edraft.utils.console import Console class PublishSettings(ConfigurationModelBase): diff --git a/src/sh_edraft/publishing/model/template.py b/src/sh_edraft/publishing/model/template.py index 922a5df5..91ba29e3 100644 --- a/src/sh_edraft/publishing/model/template.py +++ b/src/sh_edraft/publishing/model/template.py @@ -21,6 +21,7 @@ class Template(ConfigurationModelBase): author: Optional[str] = None, version: Optional[dict] = None ): + ConfigurationModelBase.__init__(self) self._template_path: Optional[str] = template_path self._name: Optional[str] = name self._description: Optional[str] = description diff --git a/src/sh_edraft/service/__init__.py b/src/sh_edraft/service/__init__.py index 52003f0b..abfc6044 100644 --- a/src/sh_edraft/service/__init__.py +++ b/src/sh_edraft/service/__init__.py @@ -20,7 +20,6 @@ __version__ = '2020.12.5' from collections import namedtuple # imports: -from .service_provider import ServiceProvider VersionInfo = namedtuple('VersionInfo', 'major minor micro') version_info = VersionInfo(major=2020, minor=12, micro=5) diff --git a/src/sh_edraft/service/base/__init__.py b/src/sh_edraft/service/base/__init__.py index 6ce7f45b..d3d865d7 100644 --- a/src/sh_edraft/service/base/__init__.py +++ b/src/sh_edraft/service/base/__init__.py @@ -21,7 +21,6 @@ from collections import namedtuple # imports: from .service_base import ServiceBase -from .service_provider_base import ServiceProviderBase VersionInfo = namedtuple('VersionInfo', 'major minor micro') version_info = VersionInfo(major=2020, minor=12, micro=5) diff --git a/src/sh_edraft/service/model/__init__.py b/src/sh_edraft/service/model/__init__.py index b95fe3c2..05760565 100644 --- a/src/sh_edraft/service/model/__init__.py +++ b/src/sh_edraft/service/model/__init__.py @@ -20,7 +20,6 @@ __version__ = '2020.12.5' from collections import namedtuple # imports: -from .provide_state import ProvideState VersionInfo = namedtuple('VersionInfo', 'major minor micro') version_info = VersionInfo(major=2020, minor=12, micro=5) diff --git a/src/sh_edraft/service/providing/__init__.py b/src/sh_edraft/service/providing/__init__.py new file mode 100644 index 00000000..b8bf2077 --- /dev/null +++ b/src/sh_edraft/service/providing/__init__.py @@ -0,0 +1,3 @@ +# imports: + +from .service_provider import ServiceProviderBase diff --git a/src/sh_edraft/service/providing/base/__init__.py b/src/sh_edraft/service/providing/base/__init__.py new file mode 100644 index 00000000..4b5ed5b7 --- /dev/null +++ b/src/sh_edraft/service/providing/base/__init__.py @@ -0,0 +1,3 @@ +# imports: + +from .service_provider_base import ServiceProviderBase diff --git a/src/sh_edraft/service/base/service_provider_base.py b/src/sh_edraft/service/providing/base/service_provider_base.py similarity index 100% rename from src/sh_edraft/service/base/service_provider_base.py rename to src/sh_edraft/service/providing/base/service_provider_base.py diff --git a/src/sh_edraft/service/providing/model/__init__.py b/src/sh_edraft/service/providing/model/__init__.py new file mode 100644 index 00000000..7c4dd264 --- /dev/null +++ b/src/sh_edraft/service/providing/model/__init__.py @@ -0,0 +1,3 @@ +# imports: + +from .provide_state import ProvideState diff --git a/src/sh_edraft/service/model/provide_state.py b/src/sh_edraft/service/providing/model/provide_state.py similarity index 85% rename from src/sh_edraft/service/model/provide_state.py rename to src/sh_edraft/service/providing/model/provide_state.py index 619e0ed5..52d0af05 100644 --- a/src/sh_edraft/service/model/provide_state.py +++ b/src/sh_edraft/service/providing/model/provide_state.py @@ -1,6 +1,6 @@ from typing import Type -from sh_edraft.service.base import ServiceBase +from sh_edraft.service.base.service_base import ServiceBase class ProvideState: diff --git a/src/sh_edraft/service/service_provider.py b/src/sh_edraft/service/providing/service_provider.py similarity index 93% rename from src/sh_edraft/service/service_provider.py rename to src/sh_edraft/service/providing/service_provider.py index 4207b0f5..205f8359 100644 --- a/src/sh_edraft/service/service_provider.py +++ b/src/sh_edraft/service/providing/service_provider.py @@ -4,7 +4,7 @@ from typing import Type from sh_edraft.configuration.base.configuration_model_base import ConfigurationModelBase from sh_edraft.hosting.base.application_runtime_base import ApplicationRuntimeBase -from sh_edraft.service.base.service_provider_base import ServiceProviderBase +from sh_edraft.service.providing.base.service_provider_base import ServiceProviderBase from sh_edraft.service.base.service_base import ServiceBase @@ -45,8 +45,8 @@ class ServiceProvider(ServiceProviderBase): def add_singleton(self, service_type: Type[ServiceBase], service: Callable[ServiceBase]): for known_service in self._singleton_services: - if type(known_service) == type(service_type): - raise Exception(f'Service with type {type(service_type)} already exists') + if type(known_service) == service_type: + raise Exception(f'Service with type {service_type} already exists') self._singleton_services[service_type] = self._create_instance(service) diff --git a/src/sh_edraft/utils/__init__.py b/src/sh_edraft/utils/__init__.py index 4130c05d..10d2e9e5 100644 --- a/src/sh_edraft/utils/__init__.py +++ b/src/sh_edraft/utils/__init__.py @@ -21,6 +21,7 @@ from collections import namedtuple # imports: from .console import Console +from .credential_manager import CredentialManager VersionInfo = namedtuple('VersionInfo', 'major minor micro') version_info = VersionInfo(major=2020, minor=12, micro=5) diff --git a/src/sh_edraft/utils/credential_manager.py b/src/sh_edraft/utils/credential_manager.py new file mode 100644 index 00000000..7f9977d1 --- /dev/null +++ b/src/sh_edraft/utils/credential_manager.py @@ -0,0 +1,17 @@ +import base64 + + +class CredentialManager: + + @staticmethod + def encrypt(string: str) -> str: + return base64.b64encode(string.encode('utf-8')).decode('utf-8') + + @staticmethod + def decrypt(string: str) -> str: + return base64.b64decode(string).decode('utf-8') + + @staticmethod + def build_string(string: str, credentials: str): + return string.replace('$credentials', CredentialManager.decrypt(credentials)) + diff --git a/src/tests_dev/appsettings.development.json b/src/tests_dev/appsettings.development.json index 62ec6c61..aa370960 100644 --- a/src/tests_dev/appsettings.development.json +++ b/src/tests_dev/appsettings.development.json @@ -4,5 +4,11 @@ "Filename": "log_$start_time.log", "ConsoleLogLevel": "TRACE", "FileLogLevel": "TRACE" + }, + + "DatabaseSettings": { + "ConnectionString": "mysql+mysqlconnector://sh_messenger_server:$credentials@localhost/sh_messenger_server", + "Credentials": "ZkM1U1dyU0hXM3oyI3BfXg==", + "Encoding": "utf8mb4" } } \ No newline at end of file diff --git a/src/tests_dev/program.py b/src/tests_dev/program.py index 2ee1b09b..e17733be 100644 --- a/src/tests_dev/program.py +++ b/src/tests_dev/program.py @@ -1,11 +1,13 @@ from typing import Optional from sh_edraft.configuration.base import ConfigurationBase +from sh_edraft.database import DatabaseConnection +from sh_edraft.database.base import DatabaseConnectionBase from sh_edraft.hosting import ApplicationHost from sh_edraft.hosting.base import ApplicationBase from sh_edraft.logging import Logger from sh_edraft.logging.base import LoggerBase -from sh_edraft.service.base import ServiceProviderBase +from sh_edraft.service.providing.base import ServiceProviderBase class Program(ApplicationBase): @@ -17,6 +19,7 @@ class Program(ApplicationBase): self._services: Optional[ServiceProviderBase] = None self._configuration: Optional[ConfigurationBase] = None self._logger: Optional[LoggerBase] = None + self._db_connection: Optional[DatabaseConnectionBase] = None def create_application_host(self): self._app_host = ApplicationHost() @@ -30,13 +33,19 @@ class Program(ApplicationBase): self._configuration.add_argument_variables() self._configuration.add_json_file(f'appsettings.json') self._configuration.add_json_file(f'appsettings.{self._configuration.environment.environment_name}.json') - self._configuration.add_json_file(f'appsettings.{self._configuration.environment.host_name}.json', optional=True) + self._configuration.add_json_file( + f'appsettings.{self._configuration.environment.host_name}.json', + optional=True + ) def create_services(self): self._services.create() self._services.add_singleton(LoggerBase, Logger) - self._logger = self._services.get_service(LoggerBase) + self._services.add_singleton(DatabaseConnectionBase, DatabaseConnection) + self._logger: Logger = self._services.get_service(LoggerBase) self._logger.create() + self._db_connection: DatabaseConnection = self._services.get_service(DatabaseConnectionBase) + self._db_connection.create() def main(self): self._logger.header(f'{self._configuration.environment.application_name}:')