diff --git a/src/sh_edraft/configuration/__init__.py b/src/sh_edraft/configuration/__init__.py index 10389a5b..0aaef2f3 100644 --- a/src/sh_edraft/configuration/__init__.py +++ b/src/sh_edraft/configuration/__init__.py @@ -20,6 +20,7 @@ __version__ = '2020.12.5' from collections import namedtuple # imports: +from .configuration import Configuration VersionInfo = namedtuple('VersionInfo', 'major minor micro') version_info = VersionInfo(major=2020, minor=12, micro=5) diff --git a/src/sh_edraft/configuration/base/__init__.py b/src/sh_edraft/configuration/base/__init__.py index b6b73bcd..a9585439 100644 --- a/src/sh_edraft/configuration/base/__init__.py +++ b/src/sh_edraft/configuration/base/__init__.py @@ -20,6 +20,7 @@ __version__ = '2020.12.5' from collections import namedtuple # imports: +from .configuration_base import ConfigurationBase from .configuration_model_base import ConfigurationModelBase VersionInfo = namedtuple('VersionInfo', 'major minor micro') diff --git a/src/sh_edraft/configuration/base/configuration_base.py b/src/sh_edraft/configuration/base/configuration_base.py index 4db03091..bf44a8c1 100644 --- a/src/sh_edraft/configuration/base/configuration_base.py +++ b/src/sh_edraft/configuration/base/configuration_base.py @@ -1,23 +1,34 @@ -from abc import abstractmethod +from abc import abstractmethod, ABC from collections import Callable +from typing import Type from sh_edraft.configuration.base.configuration_model_base import ConfigurationModelBase -from sh_edraft.service.base.service_base import ServiceBase +from sh_edraft.environment.base.environment_base import EnvironmentBase -class ConfigurationBase(ServiceBase): +class ConfigurationBase(ABC): @abstractmethod - def __init__(self): - ServiceBase.__init__(self) - self._config: dict[type, object] = {} + def __init__(self): pass @property @abstractmethod - def config(self) -> dict[type, object]: pass + def environment(self) -> EnvironmentBase: pass @abstractmethod - def add_config_by_type(self, key_type: type, value: object): pass + def add_environment_variables(self, prefix: str): pass @abstractmethod - def get_config_by_type(self, search_type: ConfigurationModelBase) -> Callable[ConfigurationModelBase]: pass + def add_argument_variables(self): pass + + @abstractmethod + def add_json_file(self, name: str, optional: bool = None): pass + + @abstractmethod + def add_configuration(self, key_type: type, value: object): pass + + @abstractmethod + def get_configuration(self, search_type: Type[ConfigurationModelBase]) -> Callable[ConfigurationModelBase]: pass + + @abstractmethod + def create(self): pass diff --git a/src/sh_edraft/configuration/base/configuration_model_base.py b/src/sh_edraft/configuration/base/configuration_model_base.py index 06b65ba5..9772a648 100644 --- a/src/sh_edraft/configuration/base/configuration_model_base.py +++ b/src/sh_edraft/configuration/base/configuration_model_base.py @@ -3,5 +3,8 @@ from abc import ABC, abstractmethod class ConfigurationModelBase(ABC): + @abstractmethod + def __init__(self): pass + @abstractmethod def from_dict(self, settings: dict): pass diff --git a/src/sh_edraft/configuration/configuration.py b/src/sh_edraft/configuration/configuration.py index 67140807..dc0ef33f 100644 --- a/src/sh_edraft/configuration/configuration.py +++ b/src/sh_edraft/configuration/configuration.py @@ -1,27 +1,114 @@ -from collections import Callable +import json +import os +import sys from sh_edraft.configuration.base.configuration_model_base import ConfigurationModelBase 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 class Configuration(ConfigurationBase): def __init__(self): - super().__init__() + ConfigurationBase.__init__(self) + + self._hosting_environment = HostingEnvironment() + self._config: dict[type, ConfigurationModelBase] = {} @property - def config(self): - return self._config + def environment(self) -> EnvironmentBase: + return self._hosting_environment - def create(self): pass + @staticmethod + def _print_info(name: str, message: str): + Console.write_line(f'[{name}] {message}', 'green') - def add_config_by_type(self, key_type: type, value: object): + @staticmethod + def _print_warn(name: str, message: str): + Console.write_line(f'[{name}] {message}', 'yellow') + + @staticmethod + def _print_error(name: str, message: str): + Console.write_line(f'[{name}] {message}', 'red') + + def _set_variable(self, name: str, value: str): + if name == ConfigurationVariableName.environment.value: + self._hosting_environment.environment_name = EnvironmentName(value) + + elif name == ConfigurationVariableName.name.value: + self._hosting_environment.application_name = value + + elif name == ConfigurationVariableName.customer.value: + self._hosting_environment.customer = value + + def add_environment_variables(self, prefix: str): + for variable in ConfigurationVariableName.to_list(): + var_name = f'{prefix}{variable}' + if var_name in [key.upper() for key in os.environ.keys()]: + self._set_variable(variable, os.environ[var_name]) + + def add_argument_variables(self): + for arg in sys.argv[1:]: + try: + argument = arg.split('--')[1].split('=')[0].upper() + value = arg.split('=')[1] + + if argument not in ConfigurationVariableName.to_list(): + raise Exception(f'Invalid argument name: {argument}') + + self._set_variable(argument, value) + except Exception as e: + self._print_error(__name__, f'Invalid argument: {arg} -> {e}') + exit() + + def add_json_file(self, name: str, optional: bool = None): + if self._hosting_environment.content_root_path.endswith('/') and not name.startswith('/'): + file_path = f'{self._hosting_environment.content_root_path}{name}' + else: + file_path = f'{self._hosting_environment.content_root_path}/{name}' + + if not os.path.isfile(file_path): + if not optional: + self._print_error(__name__, f'File not found: {file_path}') + exit() + + self._print_warn(__name__, f'Not Loaded config file: {file_path}') + return None + + config_from_file = self._load_json_file(file_path) + for sub in ConfigurationModelBase.__subclasses__(): + for key, value in config_from_file.items(): + if sub.__name__ == key: + configuration = sub() + configuration.from_dict(value) + self.add_configuration(sub, configuration) + + def _load_json_file(self, file: str) -> dict: + try: + # open config file, create if not exists + with open(file, encoding='utf-8') as cfg: + # load json + json_cfg = json.load(cfg) + self._print_info(__name__, f'Loaded config file: {file}') + return json_cfg + except Exception as e: + self._print_error(__name__, f'Cannot load config file: {file}! -> {e}') + return {} + + def add_configuration(self, key_type: type, value: ConfigurationModelBase): self._config[key_type] = value - def get_config_by_type(self, search_type: type) -> Callable[ConfigurationModelBase]: + def get_configuration(self, search_type: type) -> ConfigurationModelBase: if search_type not in self._config: raise Exception(f'Config model by type {search_type} not found') for config_model in self._config: if config_model == search_type: return self._config[config_model] + + def create(self): + pass diff --git a/src/sh_edraft/configuration/model/__init__.py b/src/sh_edraft/configuration/model/__init__.py new file mode 100644 index 00000000..a3c9f248 --- /dev/null +++ b/src/sh_edraft/configuration/model/__init__.py @@ -0,0 +1,3 @@ +# imports: + +from .configuration_variable_name import ConfigurationVariableName diff --git a/src/sh_edraft/configuration/model/configuration_variable_name.py b/src/sh_edraft/configuration/model/configuration_variable_name.py new file mode 100644 index 00000000..c89deb88 --- /dev/null +++ b/src/sh_edraft/configuration/model/configuration_variable_name.py @@ -0,0 +1,12 @@ +from enum import Enum + + +class ConfigurationVariableName(Enum): + + environment = 'ENVIRONMENT' + name = 'NAME' + customer = 'CUSTOMER' + + @staticmethod + def to_list(): + return [var.value for var in ConfigurationVariableName] diff --git a/src/sh_edraft/environment/__init__.py b/src/sh_edraft/environment/__init__.py new file mode 100644 index 00000000..28c021f3 --- /dev/null +++ b/src/sh_edraft/environment/__init__.py @@ -0,0 +1,3 @@ +# imports: + +from .hosting_environment import HostingEnvironment diff --git a/src/sh_edraft/environment/base/__init__.py b/src/sh_edraft/environment/base/__init__.py new file mode 100644 index 00000000..fec6499d --- /dev/null +++ b/src/sh_edraft/environment/base/__init__.py @@ -0,0 +1,3 @@ +# imports: + +from .environment_base import EnvironmentBase diff --git a/src/sh_edraft/environment/base/environment_base.py b/src/sh_edraft/environment/base/environment_base.py new file mode 100644 index 00000000..f5dc9266 --- /dev/null +++ b/src/sh_edraft/environment/base/environment_base.py @@ -0,0 +1,43 @@ +from abc import ABC, abstractmethod + + +class EnvironmentBase(ABC): + + @abstractmethod + def __init__(self): pass + + @property + @abstractmethod + def environment_name(self) -> str: pass + + @environment_name.setter + @abstractmethod + def environment_name(self, environment_name: str): pass + + @property + @abstractmethod + def application_name(self) -> str: pass + + @application_name.setter + @abstractmethod + def application_name(self, application_name: str): pass + + @property + @abstractmethod + def customer(self) -> str: pass + + @customer.setter + @abstractmethod + def customer(self, customer: str): pass + + @property + @abstractmethod + def content_root_path(self) -> str: pass + + @content_root_path.setter + @abstractmethod + def content_root_path(self, content_root_path: str): pass + + @property + @abstractmethod + def host_name(self) -> str: pass diff --git a/src/sh_edraft/environment/hosting_environment.py b/src/sh_edraft/environment/hosting_environment.py new file mode 100644 index 00000000..2f2021ff --- /dev/null +++ b/src/sh_edraft/environment/hosting_environment.py @@ -0,0 +1,52 @@ +from socket import gethostname +from typing import Optional + +from sh_edraft.environment.base.environment_base import EnvironmentBase +from sh_edraft.environment.model.environment_name import EnvironmentName + + +class HostingEnvironment(EnvironmentBase): + + def __init__(self, name: EnvironmentName = EnvironmentName.production, crp: str = './'): + EnvironmentBase.__init__(self) + + self._environment_name: Optional[EnvironmentName] = name + self._app_name: Optional[str] = None + self._customer: Optional[str] = None + self._content_root_path: Optional[str] = crp + + @property + def environment_name(self) -> str: + return str(self._environment_name.value) + + @environment_name.setter + def environment_name(self, environment_name: str): + self._environment_name = EnvironmentName(environment_name) + + @property + def application_name(self) -> str: + return self._app_name if self._app_name is not None else '' + + @application_name.setter + def application_name(self, application_name: str): + self._app_name = application_name + + @property + def customer(self) -> str: + return self._customer if self._customer is not None else '' + + @customer.setter + def customer(self, customer: str): + self._customer = customer + + @property + def content_root_path(self) -> str: + return self._content_root_path + + @content_root_path.setter + def content_root_path(self, content_root_path: str): + self._content_root_path = content_root_path + + @property + def host_name(self): + return gethostname() diff --git a/src/sh_edraft/environment/model/__init__.py b/src/sh_edraft/environment/model/__init__.py new file mode 100644 index 00000000..3204981f --- /dev/null +++ b/src/sh_edraft/environment/model/__init__.py @@ -0,0 +1,3 @@ +# imports: + +from .environment_name import EnvironmentName diff --git a/src/sh_edraft/environment/model/environment_name.py b/src/sh_edraft/environment/model/environment_name.py new file mode 100644 index 00000000..18abcbb5 --- /dev/null +++ b/src/sh_edraft/environment/model/environment_name.py @@ -0,0 +1,9 @@ +from enum import Enum + + +class EnvironmentName(Enum): + + production = 'production' + staging = 'staging' + testing = 'testing' + development = 'development' diff --git a/src/sh_edraft/hosting/__init__.py b/src/sh_edraft/hosting/__init__.py index ceeb2b0e..ec999843 100644 --- a/src/sh_edraft/hosting/__init__.py +++ b/src/sh_edraft/hosting/__init__.py @@ -1,3 +1,4 @@ # imports: from .application_host import ApplicationHost +from .application_runtime import ApplicationRuntime diff --git a/src/sh_edraft/hosting/application_host.py b/src/sh_edraft/hosting/application_host.py index f9b8fbf9..e5e6b362 100644 --- a/src/sh_edraft/hosting/application_host.py +++ b/src/sh_edraft/hosting/application_host.py @@ -1,37 +1,35 @@ from datetime import datetime +from sh_edraft.configuration.configuration import Configuration +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 class ApplicationHost(ApplicationHostBase): - + def __init__(self): ApplicationHostBase.__init__(self) - self._services = ServiceProvider() + self._config = Configuration() + self._app_runtime = ApplicationRuntime(self._config) + self._services = ServiceProvider(self._app_runtime) + self._start_time: datetime = datetime.now() self._end_time: datetime = datetime.now() @property - def services(self): + def configuration(self) -> ConfigurationBase: + return self._config + + @property + def application_runtime(self) -> ApplicationRuntimeBase: + return self._app_runtime + + @property + def services(self) -> ServiceProviderBase: return self._services - @property - def end_time(self) -> datetime: - return self._end_time - - @end_time.setter - def end_time(self, end_time: datetime): - self._end_time = end_time - - @property - def start_time(self) -> datetime: - return self._start_time - - @start_time.setter - def start_time(self, start_time: datetime): - self._start_time = start_time - - @property - def date_time_now(self) -> datetime: - return datetime.now() + def create(self): pass diff --git a/src/sh_edraft/hosting/application_runtime.py b/src/sh_edraft/hosting/application_runtime.py new file mode 100644 index 00000000..5e903bb5 --- /dev/null +++ b/src/sh_edraft/hosting/application_runtime.py @@ -0,0 +1,38 @@ +from datetime import datetime + +from sh_edraft.configuration.base.configuration_base import ConfigurationBase +from sh_edraft.hosting.base.application_runtime_base import ApplicationRuntimeBase + + +class ApplicationRuntime(ApplicationRuntimeBase): + + def __init__(self, config: ConfigurationBase): + ApplicationRuntimeBase.__init__(self) + + self._app_configuration = config + self._start_time: datetime = datetime.now() + self._end_time: datetime = datetime.now() + + @property + def configuration(self) -> ConfigurationBase: + return self._app_configuration + + @property + def start_time(self) -> datetime: + return self._start_time + + @start_time.setter + def start_time(self, start_time: datetime): + self._start_time = start_time + + @property + def end_time(self) -> datetime: + return self._end_time + + @end_time.setter + def end_time(self, end_time: datetime): + self._end_time = end_time + + @property + def date_time_now(self) -> datetime: + return datetime.now() diff --git a/src/sh_edraft/hosting/base/__init__.py b/src/sh_edraft/hosting/base/__init__.py index 96c21b77..dfb25a31 100644 --- a/src/sh_edraft/hosting/base/__init__.py +++ b/src/sh_edraft/hosting/base/__init__.py @@ -1,2 +1,4 @@ # imports: +from .application_base import ApplicationBase from .application_host_base import ApplicationHostBase +from .application_runtime_base import ApplicationRuntimeBase diff --git a/src/sh_edraft/hosting/base/application_base.py b/src/sh_edraft/hosting/base/application_base.py new file mode 100644 index 00000000..00f72997 --- /dev/null +++ b/src/sh_edraft/hosting/base/application_base.py @@ -0,0 +1,19 @@ +from abc import ABC, abstractmethod + + +class ApplicationBase(ABC): + + @abstractmethod + def __init__(self): pass + + @abstractmethod + def create_application_host(self): pass + + @abstractmethod + def create_configuration(self): pass + + @abstractmethod + def create_services(self): pass + + @abstractmethod + def main(self): pass diff --git a/src/sh_edraft/hosting/base/application_host_base.py b/src/sh_edraft/hosting/base/application_host_base.py index 20dbd9a5..4ee8c0f5 100644 --- a/src/sh_edraft/hosting/base/application_host_base.py +++ b/src/sh_edraft/hosting/base/application_host_base.py @@ -1,5 +1,8 @@ from abc import ABC, abstractmethod -from datetime import datetime + +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 class ApplicationHostBase(ABC): @@ -9,14 +12,15 @@ class ApplicationHostBase(ABC): @property @abstractmethod - def start_time(self) -> datetime: pass - - @start_time.setter - def start_time(self, start_time: datetime): pass + def configuration(self) -> ConfigurationBase: pass @property @abstractmethod - def end_time(self): pass + def application_runtime(self) -> ApplicationRuntimeBase: pass - @end_time.setter - def end_time(self, end_time: datetime): pass + @property + @abstractmethod + def services(self) -> ServiceProviderBase: pass + + @abstractmethod + def create(self): pass diff --git a/src/sh_edraft/hosting/base/application_runtime_base.py b/src/sh_edraft/hosting/base/application_runtime_base.py new file mode 100644 index 00000000..fd644270 --- /dev/null +++ b/src/sh_edraft/hosting/base/application_runtime_base.py @@ -0,0 +1,34 @@ +from abc import ABC, abstractmethod +from datetime import datetime + +from sh_edraft.configuration.base.configuration_base import ConfigurationBase + + +class ApplicationRuntimeBase(ABC): + + @abstractmethod + def __init__(self): pass + + @property + @abstractmethod + def configuration(self) -> ConfigurationBase: pass + + @property + @abstractmethod + def start_time(self) -> datetime: pass + + @start_time.setter + @abstractmethod + def start_time(self, start_time: datetime): pass + + @property + @abstractmethod + def end_time(self): pass + + @end_time.setter + @abstractmethod + def end_time(self, end_time: datetime): pass + + @property + @abstractmethod + def date_time_now(self) -> datetime: pass diff --git a/src/sh_edraft/hosting/model/__init__.py b/src/sh_edraft/hosting/model/__init__.py index e69de29b..52f86f25 100644 --- a/src/sh_edraft/hosting/model/__init__.py +++ b/src/sh_edraft/hosting/model/__init__.py @@ -0,0 +1 @@ +# imports: diff --git a/src/sh_edraft/logging/base/logger_base.py b/src/sh_edraft/logging/base/logger_base.py index 47949898..2dc5de50 100644 --- a/src/sh_edraft/logging/base/logger_base.py +++ b/src/sh_edraft/logging/base/logger_base.py @@ -1,20 +1,14 @@ from abc import abstractmethod -from sh_edraft.hosting.application_host import ApplicationHost -from sh_edraft.logging.model.logging_settings import LoggingSettings from sh_edraft.service.base.service_base import ServiceBase -from sh_edraft.time.model.time_format_settings import TimeFormatSettings class LoggerBase(ServiceBase): @abstractmethod - def __init__(self, logging_settings: LoggingSettings, time_format: TimeFormatSettings): + def __init__(self): ServiceBase.__init__(self) - self._log_settings: LoggingSettings = logging_settings - self._time_format_settings: TimeFormatSettings = time_format - @abstractmethod def header(self, string: str): pass diff --git a/src/sh_edraft/logging/logger.py b/src/sh_edraft/logging/logger.py index f7a0a361..01b58937 100644 --- a/src/sh_edraft/logging/logger.py +++ b/src/sh_edraft/logging/logger.py @@ -1,10 +1,9 @@ import datetime import os import traceback -from collections import Callable from string import Template -from sh_edraft.hosting.application_host import ApplicationHost +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_level import LoggingLevel @@ -14,14 +13,16 @@ from sh_edraft.utils.console import Console class Logger(LoggerBase): - def __init__(self, logging_settings: LoggingSettings, time_format: TimeFormatSettings, app_host: ApplicationHost): - LoggerBase.__init__(self, logging_settings, time_format) + def __init__(self, logging_settings: LoggingSettings, time_format: TimeFormatSettings, app_runtime: ApplicationRuntimeBase): + LoggerBase.__init__(self) - self._app_host: ApplicationHost = app_host + self._app_runtime = app_runtime + self._log_settings: LoggingSettings = logging_settings + self._time_format_settings: TimeFormatSettings = time_format self._log = Template(self._log_settings.filename).substitute( - date_time_now=self._app_host.date_time_now.strftime(self._time_format_settings.date_time_format), - start_time=self._app_host.start_time.strftime(self._time_format_settings.date_time_log_format) + date_time_now=self._app_runtime.date_time_now.strftime(self._time_format_settings.date_time_format), + start_time=self._app_runtime.start_time.strftime(self._time_format_settings.date_time_log_format) ) self._path = self._log_settings.path self._level = self._log_settings.level @@ -46,7 +47,7 @@ class Logger(LoggerBase): if not os.path.exists(self._path): os.mkdir(self._path) except Exception as e: - self.fatal(__name__, 'Cannot create log dir', ex=e) + self._fatal_console(__name__, 'Cannot create log dir', ex=e) """ create new log file """ try: @@ -56,16 +57,19 @@ class Logger(LoggerBase): Console.write_line(f'[{__name__}]: Using log file: {path}') f.close() except Exception as e: - self.fatal(__name__, 'Cannot open log file', ex=e) + self._fatal_console(__name__, 'Cannot open log file', ex=e) def _append_log(self, string): try: # open log file and append always + if not os.path.isdir(self._path): + self._fatal_console(__name__, 'Log directory not found') + with open(self._path + self._log, "a+", encoding="utf-8") as f: f.write(string + '\n') f.close() except Exception as e: - self.error(__name__, f'Cannot append log file, message: {string}', ex=e) + self._fatal_console(__name__, f'Cannot append log file, message: {string}', ex=e) def _get_string(self, name: str, level: LoggingLevel, message: str) -> str: log_level = level.name @@ -142,9 +146,9 @@ class Logger(LoggerBase): if ex is not None: tb = traceback.format_exc() self.error(name, message) - output = self._get_string(name, LoggingLevel.ERROR, f'{ex} -> {tb}') + output = self._get_string(name, LoggingLevel.FATAL, f'{ex} -> {tb}') else: - output = self._get_string(name, LoggingLevel.ERROR, message) + output = self._get_string(name, LoggingLevel.FATAL, message) # check if message can be written to log if self._level.value >= LoggingLevel.FATAL.value: @@ -155,3 +159,18 @@ class Logger(LoggerBase): Console.write_line(output, 'red') exit() + + def _fatal_console(self, name: str, message: str, ex: Exception = None): + output = '' + if ex is not None: + tb = traceback.format_exc() + self.error(name, message) + output = self._get_string(name, LoggingLevel.ERROR, f'{ex} -> {tb}') + else: + output = self._get_string(name, LoggingLevel.ERROR, message) + + # check if message can be shown in console + if self._console.value >= LoggingLevel.FATAL.value: + Console.write_line(output, 'red') + + exit() diff --git a/src/sh_edraft/logging/model/__init__.py b/src/sh_edraft/logging/model/__init__.py index 6712752d..8c8ce494 100644 --- a/src/sh_edraft/logging/model/__init__.py +++ b/src/sh_edraft/logging/model/__init__.py @@ -22,6 +22,7 @@ from collections import namedtuple # imports: from .logging_level import LoggingLevel from .logging_settings import LoggingSettings +from .logging_settings_name import LoggingSettingsName VersionInfo = namedtuple('VersionInfo', 'major minor micro') version_info = VersionInfo(major=2020, minor=12, micro=5) diff --git a/src/sh_edraft/logging/model/logging_settings.py b/src/sh_edraft/logging/model/logging_settings.py index c5d1d77e..47889dea 100644 --- a/src/sh_edraft/logging/model/logging_settings.py +++ b/src/sh_edraft/logging/model/logging_settings.py @@ -2,7 +2,7 @@ import traceback from typing import Optional from sh_edraft.configuration.base.configuration_model_base import ConfigurationModelBase -from sh_edraft.logging.model.logging_settings_name import LogSettingsName +from sh_edraft.logging.model.logging_settings_name import LoggingSettingsName from sh_edraft.utils.console import Console from sh_edraft.logging.model.logging_level import LoggingLevel @@ -11,7 +11,6 @@ class LoggingSettings(ConfigurationModelBase): def __init__(self): ConfigurationModelBase.__init__(self) - self._path: Optional[str] = None self._filename: Optional[str] = None self._console: Optional[LoggingLevel] = None @@ -51,10 +50,10 @@ class LoggingSettings(ConfigurationModelBase): def from_dict(self, settings: dict): try: - self._path = settings[LogSettingsName.path.value] - self._filename = settings[LogSettingsName.filename.value] - self._console = LoggingLevel[settings[LogSettingsName.console_level.value]] - self._level = LoggingLevel[settings[LogSettingsName.file_level.value]] + self._path = settings[LoggingSettingsName.path.value] + self._filename = settings[LoggingSettingsName.filename.value] + self._console = LoggingLevel[settings[LoggingSettingsName.console_level.value]] + self._level = LoggingLevel[settings[LoggingSettingsName.file_level.value]] except Exception as e: - Console.write_line(f'[ ERROR ] [ {__name__} ]: Reading error in {LogSettingsName.log.value} settings', 'red') + 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/logging/model/logging_settings_name.py b/src/sh_edraft/logging/model/logging_settings_name.py index 3bd90037..323e5735 100644 --- a/src/sh_edraft/logging/model/logging_settings_name.py +++ b/src/sh_edraft/logging/model/logging_settings_name.py @@ -1,9 +1,8 @@ from enum import Enum -class LogSettingsName(Enum): +class LoggingSettingsName(Enum): - log = 'Log' path = 'Path' filename = 'Filename' console_level = 'ConsoleLogLevel' diff --git a/src/sh_edraft/publishing/base/publisher_base.py b/src/sh_edraft/publishing/base/publisher_base.py index f2038018..f3f899ca 100644 --- a/src/sh_edraft/publishing/base/publisher_base.py +++ b/src/sh_edraft/publishing/base/publisher_base.py @@ -1,19 +1,14 @@ from abc import abstractmethod -from sh_edraft.logging.base.logger_base import LoggerBase -from sh_edraft.publishing.model.publish_settings_model import PublishSettingsModel from sh_edraft.service.base.service_base import ServiceBase class PublisherBase(ServiceBase): @abstractmethod - def __init__(self, logger: LoggerBase, publish_settings: PublishSettingsModel): + def __init__(self): ServiceBase.__init__(self) - self._logger: LoggerBase = logger - self._publish_settings: PublishSettingsModel = publish_settings - @property @abstractmethod def source_path(self) -> str: pass diff --git a/src/sh_edraft/publishing/model/__init__.py b/src/sh_edraft/publishing/model/__init__.py index 91e2bd18..98f26949 100644 --- a/src/sh_edraft/publishing/model/__init__.py +++ b/src/sh_edraft/publishing/model/__init__.py @@ -22,6 +22,8 @@ from collections import namedtuple # imports: from .template import Template from .template_enum import TemplateEnum +from .publish_settings_model import PublishSettings +from .publish_settings_name import PublishSettingsName VersionInfo = namedtuple('VersionInfo', 'major minor micro') version_info = VersionInfo(major=2020, minor=12, micro=5) diff --git a/src/sh_edraft/publishing/model/publish_settings_model.py b/src/sh_edraft/publishing/model/publish_settings_model.py index 898d01d9..3886262d 100644 --- a/src/sh_edraft/publishing/model/publish_settings_model.py +++ b/src/sh_edraft/publishing/model/publish_settings_model.py @@ -7,7 +7,7 @@ from sh_edraft.publishing.model.publish_settings_name import PublishSettingsName from sh_edraft.utils import Console -class PublishSettingsModel(ConfigurationModelBase): +class PublishSettings(ConfigurationModelBase): def __init__(self): ConfigurationModelBase.__init__(self) diff --git a/src/sh_edraft/publishing/publisher.py b/src/sh_edraft/publishing/publisher.py index 1e7f55e6..2e922f36 100644 --- a/src/sh_edraft/publishing/publisher.py +++ b/src/sh_edraft/publishing/publisher.py @@ -4,14 +4,17 @@ from string import Template as stringTemplate from sh_edraft.logging.base.logger_base import LoggerBase from sh_edraft.publishing.base.publisher_base import PublisherBase -from sh_edraft.publishing.model.publish_settings_model import PublishSettingsModel +from sh_edraft.publishing.model.publish_settings_model import PublishSettings from sh_edraft.publishing.model.template import Template class Publisher(PublisherBase): - def __init__(self, logger: LoggerBase, publish_settings: PublishSettingsModel): - super().__init__(logger, publish_settings) + def __init__(self, logger: LoggerBase, publish_settings: PublishSettings): + PublisherBase.__init__(self) + + self._logger: LoggerBase = logger + self._publish_settings: PublishSettings = publish_settings @property def source_path(self) -> str: diff --git a/src/sh_edraft/service/__init__.py b/src/sh_edraft/service/__init__.py index dbc93593..52003f0b 100644 --- a/src/sh_edraft/service/__init__.py +++ b/src/sh_edraft/service/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ -sh_edraft.service +sh_edraft.services ~~~~~~~~~~~~~~~~~~~ @@ -11,7 +11,7 @@ sh_edraft.service """ -__title__ = 'sh_edraft.service' +__title__ = 'sh_edraft.services' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2020 sh-edraft.de' diff --git a/src/sh_edraft/service/base/__init__.py b/src/sh_edraft/service/base/__init__.py index e2c43c24..6ce7f45b 100644 --- a/src/sh_edraft/service/base/__init__.py +++ b/src/sh_edraft/service/base/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ -sh_edraft.service.base +sh_edraft.services.base ~~~~~~~~~~~~~~~~~~~ @@ -11,7 +11,7 @@ sh_edraft.service.base """ -__title__ = 'sh_edraft.service.base' +__title__ = 'sh_edraft.services.base' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2020 sh-edraft.de' diff --git a/src/sh_edraft/service/base/service_provider_base.py b/src/sh_edraft/service/base/service_provider_base.py index 9deb0918..58d3a64f 100644 --- a/src/sh_edraft/service/base/service_provider_base.py +++ b/src/sh_edraft/service/base/service_provider_base.py @@ -11,14 +11,6 @@ class ServiceProviderBase(ServiceBase): def __init__(self): ServiceBase.__init__(self) - self._transient_services: dict[Type[ServiceBase], Type[ServiceBase]] = {} - self._scoped_services: dict[Type[ServiceBase], Type[ServiceBase]] = {} - self._singleton_services: dict[Type[ServiceBase], ServiceBase] = {} - - @property - @abstractmethod - def config(self): pass - @abstractmethod def add_transient(self, service_type: Type[ServiceBase], service: Type[ServiceBase]): pass @@ -26,7 +18,7 @@ class ServiceProviderBase(ServiceBase): def add_scoped(self, service_type: Type[ServiceBase], service: Type[ServiceBase]): pass @abstractmethod - def add_singleton(self, service_type: Type[ServiceBase], service: ServiceBase): pass + def add_singleton(self, service_type: Type[ServiceBase], service: Callable[ServiceBase]): pass @abstractmethod def get_service(self, instance_type: Type[ServiceBase]) -> Callable[ServiceBase]: pass diff --git a/src/sh_edraft/service/model/__init__.py b/src/sh_edraft/service/model/__init__.py index 96c24ada..b95fe3c2 100644 --- a/src/sh_edraft/service/model/__init__.py +++ b/src/sh_edraft/service/model/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ -sh_edraft.service.base +sh_edraft.services.base ~~~~~~~~~~~~~~~~~~~ @@ -11,7 +11,7 @@ sh_edraft.service.base """ -__title__ = 'sh_edraft.service.base' +__title__ = 'sh_edraft.services.base' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2020 sh-edraft.de' diff --git a/src/sh_edraft/service/service_provider.py b/src/sh_edraft/service/service_provider.py index 66cb567e..4207b0f5 100644 --- a/src/sh_edraft/service/service_provider.py +++ b/src/sh_edraft/service/service_provider.py @@ -2,22 +2,21 @@ from collections import Callable from inspect import signature, Parameter from typing import Type -from sh_edraft.configuration.configuration import Configuration -from sh_edraft.hosting.base.application_host_base import ApplicationHostBase 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.base.service_base import ServiceBase class ServiceProvider(ServiceProviderBase): - def __init__(self): - super().__init__() - self._config = Configuration() + def __init__(self, app_runtime: ApplicationRuntimeBase): + ServiceProviderBase.__init__(self) + self._app_runtime: ApplicationRuntimeBase = app_runtime - @property - def config(self): - return self._config + self._transient_services: dict[Type[ServiceBase], Type[ServiceBase]] = {} + self._scoped_services: dict[Type[ServiceBase], Type[ServiceBase]] = {} + self._singleton_services: dict[Type[ServiceBase], ServiceBase] = {} def create(self): pass @@ -27,18 +26,16 @@ class ServiceProvider(ServiceProviderBase): for param in sig.parameters.items(): parameter = param[1] if parameter.name != 'self' and parameter.annotation != Parameter.empty: - if issubclass(parameter.annotation, ServiceBase): + if issubclass(parameter.annotation, ApplicationRuntimeBase): + params.append(self._app_runtime) + + elif issubclass(parameter.annotation, ServiceBase): params.append(self.get_service(parameter.annotation)) - elif issubclass(parameter.annotation, ConfigurationModelBase) or issubclass(parameter.annotation, ApplicationHostBase): - params.append(self._config.get_config_by_type(parameter.annotation)) + elif issubclass(parameter.annotation, ConfigurationModelBase): + params.append(self._app_runtime.configuration.get_configuration(parameter.annotation)) return service(*params) - # try: - # instance.init(args) - # return instance - # except Exception as e: - # print(colored(f'Argument error\n{e}', 'red')) def add_transient(self, service_type: Type[ServiceBase], service: Type[ServiceBase]): self._transient_services[service_type] = service @@ -46,7 +43,7 @@ class ServiceProvider(ServiceProviderBase): def add_scoped(self, service_type: Type[ServiceBase], service: Type[ServiceBase]): self._scoped_services[service_type] = service - def add_singleton(self, service_type: Type[ServiceBase], service: ServiceBase): + 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') diff --git a/src/sh_edraft/time/model/time_format_settings.py b/src/sh_edraft/time/model/time_format_settings.py index f092e27b..64508ef2 100644 --- a/src/sh_edraft/time/model/time_format_settings.py +++ b/src/sh_edraft/time/model/time_format_settings.py @@ -9,6 +9,7 @@ from sh_edraft.utils.console import Console class TimeFormatSettings(ConfigurationModelBase): def __init__(self): + ConfigurationModelBase.__init__(self) self._date_format: Optional[str] = None self._time_format: Optional[str] = None self._date_time_format: Optional[str] = None @@ -55,5 +56,5 @@ class TimeFormatSettings(ConfigurationModelBase): self._date_time_format = settings[TimeFormatSettingsNames.date_time_format.value] self._date_time_log_format = settings[TimeFormatSettingsNames.date_time_log_format.value] except Exception as e: - Console.write_line(f'[ ERROR ] [ {__name__} ]: Reading error in {TimeFormatSettingsNames.formats.value} settings') + Console.write_line(f'[ ERROR ] [ {__name__} ]: Reading error in {self.__name__} settings') Console.write_line(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}', 'red') diff --git a/src/sh_edraft/time/model/time_format_settings_names.py b/src/sh_edraft/time/model/time_format_settings_names.py index 4afc21cd..3858de10 100644 --- a/src/sh_edraft/time/model/time_format_settings_names.py +++ b/src/sh_edraft/time/model/time_format_settings_names.py @@ -3,7 +3,6 @@ from enum import Enum class TimeFormatSettingsNames(Enum): - formats = 'TimeFormats' date_format = 'DateFormat' time_format = 'TimeFormat' date_time_format = 'DateTimeFormat' diff --git a/src/tests/appsettings.development.json b/src/tests/appsettings.development.json new file mode 100644 index 00000000..62ec6c61 --- /dev/null +++ b/src/tests/appsettings.development.json @@ -0,0 +1,8 @@ +{ + "LoggingSettings": { + "Path": "logs/", + "Filename": "log_$start_time.log", + "ConsoleLogLevel": "TRACE", + "FileLogLevel": "TRACE" + } +} \ No newline at end of file diff --git a/src/tests/appsettings.edrafts-pc.json b/src/tests/appsettings.edrafts-pc.json new file mode 100644 index 00000000..62ec6c61 --- /dev/null +++ b/src/tests/appsettings.edrafts-pc.json @@ -0,0 +1,8 @@ +{ + "LoggingSettings": { + "Path": "logs/", + "Filename": "log_$start_time.log", + "ConsoleLogLevel": "TRACE", + "FileLogLevel": "TRACE" + } +} \ No newline at end of file diff --git a/src/tests/appsettings.json b/src/tests/appsettings.json new file mode 100644 index 00000000..fd8ddf6c --- /dev/null +++ b/src/tests/appsettings.json @@ -0,0 +1,15 @@ +{ + "TimeFormatSettings": { + "DateFormat": "%Y-%m-%d", + "TimeFormat": "%H:%M:%S", + "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f", + "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S" + }, + + "LoggingSettings": { + "Path": "logs/", + "Filename": "log_$start_time.log", + "ConsoleLogLevel": "ERROR", + "FileLogLevel": "WARN" + } +} \ No newline at end of file diff --git a/src/tests/appsettings.testing.json b/src/tests/appsettings.testing.json new file mode 100644 index 00000000..3b586a2c --- /dev/null +++ b/src/tests/appsettings.testing.json @@ -0,0 +1,17 @@ +{ + "LoggingSettings": { + "Path": "logs/", + "Filename": "log_$start_time.log", + "ConsoleLogLevel": "TRACE", + "FileLogLevel": "TRACE" + }, + + "PublishSettings": { + "SourcePath": "../", + "DistPath": "../../dist", + "Templates": [], + "IncludedFiles": [], + "ExcludedFiles": [], + "TemplateEnding": "_template.txt" + } +} \ No newline at end of file diff --git a/src/tests/logging/__init__.py b/src/tests/configuration/__init__.py similarity index 100% rename from src/tests/logging/__init__.py rename to src/tests/configuration/__init__.py diff --git a/src/tests/configuration/config.py b/src/tests/configuration/config.py new file mode 100644 index 00000000..27218978 --- /dev/null +++ b/src/tests/configuration/config.py @@ -0,0 +1,93 @@ +import os +import unittest +from typing import cast + +from sh_edraft.configuration import Configuration +from sh_edraft.environment.model import EnvironmentName +from sh_edraft.hosting import ApplicationHost +from sh_edraft.logging.model import LoggingSettings, LoggingLevel +from sh_edraft.publishing.model import PublishSettings +from sh_edraft.time.model import TimeFormatSettings + + +class ConfigTest(unittest.TestCase): + + def setUp(self): + self._app_host = ApplicationHost() + self._config = cast(Configuration, self._app_host.configuration) + + def test_create(self): + print(f'{__name__}.test_create:') + self.assertIsNotNone(self._config) + self._config.create() + self.assertIsNotNone(self._config) + + self.assertEqual(len(self._config._config), 0) + self.assertIsNotNone(self._app_host.application_runtime) + + def test_env_vars(self): + print(f'{__name__}.test_env_vars:') + self._config.add_environment_variables('PYTHON_') + self._config.add_environment_variables('CPL_') + + def test_arguments(self): + print(f'{__name__}.test_arguments:') + self._config.add_argument_variables() + self.assertEqual(self._config.environment.environment_name, EnvironmentName.testing.value) + + def test_appsettings(self): + print(f'{__name__}.test_appsettings:') + self._config.add_json_file(f'appsettings.json') + + time_formats: TimeFormatSettings = cast(TimeFormatSettings, self._config.get_configuration(TimeFormatSettings)) + self.assertIsNotNone(time_formats) + self.assertEqual(time_formats.date_format, '%Y-%m-%d') + self.assertEqual(time_formats.time_format, '%H:%M:%S') + self.assertEqual(time_formats.date_time_format, '%Y-%m-%d %H:%M:%S.%f') + self.assertEqual(time_formats.date_time_log_format, '%Y-%m-%d_%H-%M-%S') + + logging = cast(LoggingSettings, self._config.get_configuration(LoggingSettings)) + self.assertIsNotNone(logging) + self.assertEqual(logging.path, 'logs/') + self.assertEqual(logging.filename, 'log_$start_time.log') + self.assertEqual(logging.console.value, LoggingLevel.ERROR.value) + self.assertEqual(logging.level.value, LoggingLevel.WARN.value) + + with self.assertRaises(Exception): + publish: PublishSettings = cast(PublishSettings, self._config.get_configuration(PublishSettings)) + + def test_appsettings_environment(self): + print(f'{__name__}.test_appsettings_environment:') + self._config.add_argument_variables() + self._config.add_json_file(f'appsettings.{self._config.environment.environment_name}.json') + + logging = cast(LoggingSettings, self._config.get_configuration(LoggingSettings)) + self.assertIsNotNone(logging) + self.assertEqual(logging.path, 'logs/') + self.assertEqual(logging.filename, 'log_$start_time.log') + self.assertEqual(logging.console.value, LoggingLevel.TRACE.value) + self.assertEqual(logging.level.value, LoggingLevel.TRACE.value) + + publish: PublishSettings = cast(PublishSettings, self._config.get_configuration(PublishSettings)) + self.assertIsNotNone(publish) + self.assertEqual(publish.source_path, '../') + self.assertEqual(publish.dist_path, '../../dist') + self.assertEqual(publish.templates, []) + self.assertEqual(publish.included_files, []) + self.assertEqual(publish.excluded_files, []) + self.assertEqual(publish.template_ending, '_template.txt') + + def test_appsettings_host(self): + print(f'{__name__}.test_appsettings_host:') + self._config.add_json_file(f'appsettings.{self._config.environment.host_name}.json') + + def test_appsettings_customer(self): + print(f'{__name__}.test_appsettings_customer:') + file_name = f'appsettings.{self._config.environment.customer}.json' + with self.assertRaises(SystemExit): + if os.path.isfile(f'{self._config.environment.content_root_path}/{file_name}'): + os.remove(f'{self._config.environment.content_root_path}/{file_name}') + + self._config.add_json_file(file_name) + + self._config.add_json_file(file_name, optional=True) diff --git a/src/tests/publishing/__init__.py b/src/tests/hosting/__init__.py similarity index 100% rename from src/tests/publishing/__init__.py rename to src/tests/hosting/__init__.py diff --git a/src/tests/hosting/app_host.py b/src/tests/hosting/app_host.py new file mode 100644 index 00000000..ec940481 --- /dev/null +++ b/src/tests/hosting/app_host.py @@ -0,0 +1,33 @@ +import unittest +import datetime + +from sh_edraft.configuration.base import ConfigurationBase +from sh_edraft.hosting import ApplicationHost +from sh_edraft.hosting.base import ApplicationRuntimeBase +from sh_edraft.service.base import ServiceProviderBase + + +class AppHostTest(unittest.TestCase): + + def setUp(self): + pass + + def test_create(self): + print(f'{__name__}.test_create:') + app_host = ApplicationHost() + self.assertIsNotNone(app_host) + app_host.create() + + self.assertIsNotNone(app_host.configuration) + self.assertTrue(isinstance(app_host.configuration, ConfigurationBase)) + + self.assertIsNotNone(app_host.application_runtime) + self.assertTrue(isinstance(app_host.application_runtime, ApplicationRuntimeBase)) + + self.assertIsNotNone(app_host.services) + self.assertTrue(isinstance(app_host.services, ServiceProviderBase)) + + self.assertIsNotNone(app_host._start_time) + self.assertTrue(isinstance(app_host._start_time, datetime.datetime)) + self.assertIsNotNone(app_host._end_time) + self.assertTrue(isinstance(app_host._end_time, datetime.datetime)) diff --git a/src/tests/publisher.py b/src/tests/publisher.py deleted file mode 100644 index 847d76b4..00000000 --- a/src/tests/publisher.py +++ /dev/null @@ -1,59 +0,0 @@ -import os - -from sh_edraft.logging.base.logger_base import LoggerBase -from sh_edraft.publishing.base import PublisherBase -from sh_edraft.service.base import ServiceProviderBase -from sh_edraft.coding.model import Version -from sh_edraft.publishing import Publisher -from sh_edraft.publishing.model import Template - - -class PublisherTest: - - @staticmethod - def start(services: ServiceProviderBase): - version = Version(2020, 12, 5).to_dict() - templates = [ - Template( - '../../publish_templates/*_template.txt', - '*', - '', - '', - '2020', - 'sh-edraft.de', - 'MIT', - ', see LICENSE for more details.', - '', - 'Sven Heidemann', - version - ), - Template( - '../../publish_templates/*_template.txt', - 'sh_edraft', - 'common python library', - 'Library to share common classes and models used at sh-edraft.de', - '2020', - 'sh-edraft.de', - 'MIT', - ', see LICENSE for more details.', - '', - 'Sven Heidemann', - version - ) - ] - - source = '../' - dist = '../../dist' - - services.add_transient(Publisher, services.get_service(LoggerBase), source, dist, templates) - publisher: Publisher = services.get_service(PublisherBase) - - publisher.exclude('../tests/') - publisher.include('../../LICENSE') - publisher.include('../../README.md') - - publisher.create() - publisher.publish() - - if not os.path.isdir(dist): - raise Exception(f'{__name__}: Dist path was not created') diff --git a/src/tests/service_providing/service_provider.py b/src/tests/service_providing/service_provider.py index 1c136976..4ddc5734 100644 --- a/src/tests/service_providing/service_provider.py +++ b/src/tests/service_providing/service_provider.py @@ -1,54 +1,30 @@ import unittest +from typing import cast from sh_edraft.hosting import ApplicationHost from sh_edraft.logging import Logger from sh_edraft.logging.base import LoggerBase -from sh_edraft.logging.model import LoggingSettings from sh_edraft.publishing import Publisher from sh_edraft.publishing.base import PublisherBase -from sh_edraft.publishing.model.publish_settings_model import PublishSettingsModel from sh_edraft.service import ServiceProvider from sh_edraft.service.base import ServiceBase -from sh_edraft.time.model import TimeFormatSettings class ServiceProviderTest(unittest.TestCase): def setUp(self): self._app_host = ApplicationHost() - self._services = self._app_host.services + self._config = self._app_host.configuration + self._config.create() + self._config.add_environment_variables('PYTHON_') + self._config.add_environment_variables('CPL_') + self._config.add_argument_variables() + self._config.add_json_file(f'appsettings.json') + self._config.add_json_file(f'appsettings.{self._config.environment.environment_name}.json') + self._config.add_json_file(f'appsettings.{self._config.environment.host_name}.json', optional=True) + self._services: ServiceProvider = cast(ServiceProvider, self._app_host.services) self._services.create() - self._log_settings = LoggingSettings() - self._log_settings.from_dict({ - "Path": "logs/", - "Filename": "log_$start_time.log", - "ConsoleLogLevel": "TRACE", - "FileLogLevel": "TRACE" - }) - self._services.config.add_config_by_type(LoggingSettings, self._log_settings) - - self._time_format_settings = TimeFormatSettings() - self._time_format_settings.from_dict({ - "DateFormat": "%Y-%m-%d", - "TimeFormat": "%H:%M:%S", - "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f", - "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S" - }) - self._services.config.add_config_by_type(TimeFormatSettings, self._time_format_settings) - self._services.config.add_config_by_type(ApplicationHost, self._app_host) - - self._publish_settings_model = PublishSettingsModel() - self._publish_settings_model.from_dict({ - "SourcePath": "../", - "DistPath": "../../dist", - "Templates": [], - "IncludedFiles": [], - "ExcludedFiles": [], - "TemplateEnding": "_template.txt", - }) - self._services.config.add_config_by_type(PublishSettingsModel, self._publish_settings_model) - def _check_general_requirements(self): self.assertIsNotNone(self._services) @@ -59,7 +35,7 @@ class ServiceProviderTest(unittest.TestCase): def test_create(self): print(f'{__name__}.test_create:') - provider = ServiceProvider() + provider = ServiceProvider(self._app_host.application_runtime) self.assertIsNotNone(provider) provider.create() self.assertIsNotNone(provider) @@ -75,7 +51,9 @@ class ServiceProviderTest(unittest.TestCase): for service_type in self._services._singleton_services: service = self._services._singleton_services[service_type] if service_type == LoggerBase and ( - isinstance(service, Logger) and isinstance(service, LoggerBase) and isinstance(service, ServiceBase) + isinstance(service, Logger) and + isinstance(service, LoggerBase) and + isinstance(service, ServiceBase) ): if not found: found = True @@ -86,7 +64,9 @@ class ServiceProviderTest(unittest.TestCase): for service_type in self._services._singleton_services: service = self._services._singleton_services[service_type] if service_type == PublisherBase and ( - isinstance(service, Publisher) and isinstance(service, PublisherBase) and isinstance(service, ServiceBase) + isinstance(service, Publisher) and + isinstance(service, PublisherBase) and + isinstance(service, ServiceBase) ): if not found: found = True @@ -104,9 +84,7 @@ class ServiceProviderTest(unittest.TestCase): self.assertTrue(isinstance(logger, LoggerBase)) self.assertTrue(isinstance(logger, ServiceBase)) - self.assertEqual(logger._log_settings, self._log_settings) - self.assertEqual(logger._time_format_settings, self._time_format_settings) - self.assertEqual(logger._app_host, self._app_host) + self.assertEqual(logger._app_runtime, self._app_host.application_runtime) def test_add_scoped(self): print(f'{__name__}.test_add_scoped:') diff --git a/src/tests/services/__init__.py b/src/tests/services/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/tests/services/logging/__init__.py b/src/tests/services/logging/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/tests/logging/logger.py b/src/tests/services/logging/logger.py similarity index 66% rename from src/tests/logging/logger.py rename to src/tests/services/logging/logger.py index 37ea1346..4a17331e 100644 --- a/src/tests/logging/logger.py +++ b/src/tests/services/logging/logger.py @@ -1,12 +1,13 @@ import os import shutil import unittest -from datetime import datetime from string import Template +from typing import cast from sh_edraft.hosting import ApplicationHost from sh_edraft.logging import Logger from sh_edraft.logging.model import LoggingSettings +from sh_edraft.service import ServiceProvider from sh_edraft.time.model import TimeFormatSettings @@ -14,55 +15,50 @@ class LoggerTest(unittest.TestCase): def setUp(self): self._app_host = ApplicationHost() - self._services = self._app_host.services + self._config = self._app_host.configuration + self._config.create() + self._config.add_environment_variables('PYTHON_') + self._config.add_environment_variables('CPL_') + self._config.add_argument_variables() + self._config.add_json_file(f'appsettings.json') + self._config.add_json_file(f'appsettings.{self._config.environment.environment_name}.json') + self._config.add_json_file(f'appsettings.{self._config.environment.host_name}.json', optional=True) + self._services: ServiceProvider = cast(ServiceProvider, self._app_host.services) self._services.create() - self._log_settings = LoggingSettings() - self._log_settings.from_dict({ - "Path": "logs/", - "Filename": "log_$start_time.log", - "ConsoleLogLevel": "TRACE", - "FileLogLevel": "TRACE" - }) - - self._time_format_settings = TimeFormatSettings() - self._time_format_settings.from_dict({ - "DateFormat": "%Y-%m-%d", - "TimeFormat": "%H:%M:%S", - "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f", - "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S" - }) + self._app_runtime = self._app_host.application_runtime + self._log_settings: LoggingSettings = self._config.get_configuration(LoggingSettings) + self._time_format_settings: TimeFormatSettings = self._config.get_configuration(TimeFormatSettings) def tearDown(self): if os.path.isdir(self._log_settings.path): shutil.rmtree(self._log_settings.path) def _check_general_requirements(self): - self.assertIsNotNone(self._services) self.assertIsNotNone(self._log_settings) self.assertIsNotNone(self._time_format_settings) def test_create(self): print(f'{__name__}.test_create:') - logger = Logger(self._log_settings, self._time_format_settings, self._app_host) + logger = Logger(self._log_settings, self._time_format_settings, self._app_runtime) logger.create() self.assertTrue(os.path.isdir(self._log_settings.path)) log_file = Template(self._log_settings.filename).substitute( - date_time_now=self._app_host.date_time_now.strftime(self._time_format_settings.date_time_format), - start_time=self._app_host.start_time.strftime(self._time_format_settings.date_time_log_format) + date_time_now=self._app_runtime.date_time_now.strftime(self._time_format_settings.date_time_format), + start_time=self._app_runtime.start_time.strftime(self._time_format_settings.date_time_log_format) ) self.assertTrue(os.path.isfile(self._log_settings.path + log_file)) def test_header(self): print(f'{__name__}.test_header:') - logger = Logger(self._log_settings, self._time_format_settings, self._app_host) + logger = Logger(self._log_settings, self._time_format_settings, self._app_runtime) logger.create() logger.header('HeaderTest:') log_file = Template(self._log_settings.filename).substitute( - date_time_now=self._app_host.date_time_now.strftime(self._time_format_settings.date_time_format), - start_time=self._app_host.start_time.strftime(self._time_format_settings.date_time_log_format) + date_time_now=self._app_runtime.date_time_now.strftime(self._time_format_settings.date_time_format), + start_time=self._app_runtime.start_time.strftime(self._time_format_settings.date_time_log_format) ) log_content = [] @@ -78,13 +74,13 @@ class LoggerTest(unittest.TestCase): def test_trace(self): print(f'{__name__}.test_trace:') - logger = Logger(self._log_settings, self._time_format_settings, self._app_host) + logger = Logger(self._log_settings, self._time_format_settings, self._app_runtime) logger.create() logger.trace(__name__, f'{__name__}.test_trace:') log_file = Template(self._log_settings.filename).substitute( - date_time_now=self._app_host.date_time_now.strftime(self._time_format_settings.date_time_format), - start_time=self._app_host.start_time.strftime(self._time_format_settings.date_time_log_format) + date_time_now=self._app_runtime.date_time_now.strftime(self._time_format_settings.date_time_format), + start_time=self._app_runtime.start_time.strftime(self._time_format_settings.date_time_log_format) ) log_content = [] @@ -100,13 +96,13 @@ class LoggerTest(unittest.TestCase): def test_debug(self): print(f'{__name__}.test_debug:') - logger = Logger(self._log_settings, self._time_format_settings, self._app_host) + logger = Logger(self._log_settings, self._time_format_settings, self._app_runtime) logger.create() logger.debug(__name__, f'{__name__}.test_debug:') log_file = Template(self._log_settings.filename).substitute( - date_time_now=self._app_host.date_time_now.strftime(self._time_format_settings.date_time_format), - start_time=self._app_host.start_time.strftime(self._time_format_settings.date_time_log_format) + date_time_now=self._app_runtime.date_time_now.strftime(self._time_format_settings.date_time_format), + start_time=self._app_runtime.start_time.strftime(self._time_format_settings.date_time_log_format) ) log_content = [] @@ -122,13 +118,13 @@ class LoggerTest(unittest.TestCase): def test_info(self): print(f'{__name__}.test_info:') - logger = Logger(self._log_settings, self._time_format_settings, self._app_host) + logger = Logger(self._log_settings, self._time_format_settings, self._app_runtime) logger.create() logger.info(__name__, f'{__name__}.test_info:') log_file = Template(self._log_settings.filename).substitute( - date_time_now=self._app_host.date_time_now.strftime(self._time_format_settings.date_time_format), - start_time=self._app_host.start_time.strftime(self._time_format_settings.date_time_log_format) + date_time_now=self._app_runtime.date_time_now.strftime(self._time_format_settings.date_time_format), + start_time=self._app_runtime.start_time.strftime(self._time_format_settings.date_time_log_format) ) log_content = [] @@ -144,13 +140,13 @@ class LoggerTest(unittest.TestCase): def test_warn(self): print(f'{__name__}.test_warn:') - logger = Logger(self._log_settings, self._time_format_settings, self._app_host) + logger = Logger(self._log_settings, self._time_format_settings, self._app_runtime) logger.create() logger.warn(__name__, f'{__name__}.test_warn:') log_file = Template(self._log_settings.filename).substitute( - date_time_now=self._app_host.date_time_now.strftime(self._time_format_settings.date_time_format), - start_time=self._app_host.start_time.strftime(self._time_format_settings.date_time_log_format) + date_time_now=self._app_runtime.date_time_now.strftime(self._time_format_settings.date_time_format), + start_time=self._app_runtime.start_time.strftime(self._time_format_settings.date_time_log_format) ) log_content = [] @@ -166,13 +162,13 @@ class LoggerTest(unittest.TestCase): def test_error(self): print(f'{__name__}.test_error:') - logger = Logger(self._log_settings, self._time_format_settings, self._app_host) + logger = Logger(self._log_settings, self._time_format_settings, self._app_runtime) logger.create() logger.error(__name__, f'{__name__}.test_error:') log_file = Template(self._log_settings.filename).substitute( - date_time_now=self._app_host.date_time_now.strftime(self._time_format_settings.date_time_format), - start_time=self._app_host.start_time.strftime(self._time_format_settings.date_time_log_format) + date_time_now=self._app_runtime.date_time_now.strftime(self._time_format_settings.date_time_format), + start_time=self._app_runtime.start_time.strftime(self._time_format_settings.date_time_log_format) ) log_content = [] @@ -188,14 +184,14 @@ class LoggerTest(unittest.TestCase): def test_fatal(self): print(f'{__name__}.test_fatal:') - logger = Logger(self._log_settings, self._time_format_settings, self._app_host) + logger = Logger(self._log_settings, self._time_format_settings, self._app_runtime) logger.create() with self.assertRaises(SystemExit): logger.fatal(__name__, f'{__name__}.test_fatal:') log_file = Template(self._log_settings.filename).substitute( - date_time_now=self._app_host.date_time_now.strftime(self._time_format_settings.date_time_format), - start_time=self._app_host.start_time.strftime(self._time_format_settings.date_time_log_format) + date_time_now=self._app_runtime.date_time_now.strftime(self._time_format_settings.date_time_format), + start_time=self._app_runtime.start_time.strftime(self._time_format_settings.date_time_log_format) ) log_content = [] @@ -207,4 +203,4 @@ class LoggerTest(unittest.TestCase): print('Cannot open log file', e) self.assertGreater(len(log_content), 0) - self.assertTrue(log_content[len(log_content) - 1].endswith(f'[ ERROR ] [ {__name__} ]: {__name__}.test_fatal:\n')) + self.assertTrue(log_content[len(log_content) - 1].endswith(f'[ FATAL ] [ {__name__} ]: {__name__}.test_fatal:\n')) diff --git a/src/tests/services/publishing/__init__.py b/src/tests/services/publishing/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/tests/publishing/publisher.py b/src/tests/services/publishing/publisher.py similarity index 66% rename from src/tests/publishing/publisher.py rename to src/tests/services/publishing/publisher.py index ff64d0ae..afb17eaa 100644 --- a/src/tests/publishing/publisher.py +++ b/src/tests/services/publishing/publisher.py @@ -4,35 +4,17 @@ import unittest from sh_edraft.hosting import ApplicationHost from sh_edraft.logging import Logger -from sh_edraft.logging.base import LoggerBase from sh_edraft.logging.model import LoggingSettings from sh_edraft.publishing import Publisher -from sh_edraft.publishing.base import PublisherBase from sh_edraft.publishing.model import Template -from sh_edraft.publishing.model.publish_settings_model import PublishSettingsModel +from sh_edraft.publishing.model import PublishSettings from sh_edraft.coding.model import Version from sh_edraft.time.model import TimeFormatSettings class PublisherTest(unittest.TestCase): - def _config(self): - self._log_settings = LoggingSettings() - self._log_settings.from_dict({ - "Path": "logs/", - "Filename": "log_$start_time.log", - "ConsoleLogLevel": "TRACE", - "FileLogLevel": "TRACE" - }) - - self._time_format_settings = TimeFormatSettings() - self._time_format_settings.from_dict({ - "DateFormat": "%Y-%m-%d", - "TimeFormat": "%H:%M:%S", - "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f", - "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S" - }) - + def _configure(self): self._version = Version(2020, 12, 5).to_dict() templates = [ Template( @@ -66,7 +48,7 @@ class PublisherTest(unittest.TestCase): self._source_path = '../' self._dist_path = '../../dist' - self._publish_settings_model = PublishSettingsModel() + self._publish_settings_model = PublishSettings() self._publish_settings_model.from_dict({ "SourcePath": self._source_path, "DistPath": self._dist_path, @@ -77,10 +59,23 @@ class PublisherTest(unittest.TestCase): }) def setUp(self): - self._config() - self._app_host = ApplicationHost() - self._logger = Logger(self._log_settings, self._time_format_settings, self._app_host) + self._config = self._app_host.configuration + self._config.create() + self._config.add_environment_variables('PYTHON_') + self._config.add_environment_variables('CPL_') + self._config.add_argument_variables() + self._config.add_json_file(f'appsettings.json') + self._config.add_json_file(f'appsettings.{self._config.environment.environment_name}.json') + self._config.add_json_file(f'appsettings.{self._config.environment.host_name}.json', optional=True) + + self._app_runtime = self._app_host.application_runtime + + self._configure() + + self._log_settings: LoggingSettings = self._config.get_configuration(LoggingSettings) + self._time_format_settings: TimeFormatSettings = self._config.get_configuration(TimeFormatSettings) + self._logger = Logger(self._log_settings, self._time_format_settings, self._app_host.application_runtime) self._logger.create() def tearDown(self): @@ -88,8 +83,10 @@ class PublisherTest(unittest.TestCase): shutil.rmtree(self._log_settings.path) def test_create(self): + print(f'{__name__}.test_create:') publisher: Publisher = Publisher(self._logger, self._publish_settings_model) self.assertIsNotNone(publisher) publisher.create() self.assertTrue(os.path.isdir(self._dist_path)) + self.assertEqual(publisher._publish_settings, self._publish_settings_model) diff --git a/src/tests/tester.py b/src/tests/tester.py index dfb11e49..d7b17030 100644 --- a/src/tests/tester.py +++ b/src/tests/tester.py @@ -1,7 +1,9 @@ import unittest -from tests.logging.logger import LoggerTest -from tests.publishing.publisher import PublisherTest +from tests.configuration.config import ConfigTest +from tests.hosting.app_host import AppHostTest +from tests.services.logging.logger import LoggerTest +from tests.services.publishing.publisher import PublisherTest from tests.service_providing.service_provider import ServiceProviderTest @@ -11,6 +13,18 @@ class Tester: self._suite = unittest.TestSuite() def create(self): + # hosting app host + self._suite.addTest(AppHostTest('test_create')) + + # configuration + self._suite.addTest(ConfigTest('test_create')) + self._suite.addTest(ConfigTest('test_env_vars')) + self._suite.addTest(ConfigTest('test_arguments')) + self._suite.addTest(ConfigTest('test_appsettings')) + self._suite.addTest(ConfigTest('test_appsettings_environment')) + self._suite.addTest(ConfigTest('test_appsettings_host')) + self._suite.addTest(ConfigTest('test_appsettings_customer')) + # providing self._suite.addTest(ServiceProviderTest('test_create')) self._suite.addTest(ServiceProviderTest('test_add_singleton')) diff --git a/src/tests_dev/__init__.py b/src/tests_dev/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/tests_dev/appsettings.development.json b/src/tests_dev/appsettings.development.json new file mode 100644 index 00000000..62ec6c61 --- /dev/null +++ b/src/tests_dev/appsettings.development.json @@ -0,0 +1,8 @@ +{ + "LoggingSettings": { + "Path": "logs/", + "Filename": "log_$start_time.log", + "ConsoleLogLevel": "TRACE", + "FileLogLevel": "TRACE" + } +} \ No newline at end of file diff --git a/src/tests_dev/appsettings.json b/src/tests_dev/appsettings.json new file mode 100644 index 00000000..fd8ddf6c --- /dev/null +++ b/src/tests_dev/appsettings.json @@ -0,0 +1,15 @@ +{ + "TimeFormatSettings": { + "DateFormat": "%Y-%m-%d", + "TimeFormat": "%H:%M:%S", + "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f", + "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S" + }, + + "LoggingSettings": { + "Path": "logs/", + "Filename": "log_$start_time.log", + "ConsoleLogLevel": "ERROR", + "FileLogLevel": "WARN" + } +} \ No newline at end of file diff --git a/src/tests_dev/main.py b/src/tests_dev/main.py new file mode 100644 index 00000000..cfb45c60 --- /dev/null +++ b/src/tests_dev/main.py @@ -0,0 +1,8 @@ +from tests_dev.program import Program + +if __name__ == '__main__': + program = Program() + program.create_application_host() + program.create_configuration() + program.create_services() + program.main() diff --git a/src/tests_dev/program.py b/src/tests_dev/program.py new file mode 100644 index 00000000..2ee1b09b --- /dev/null +++ b/src/tests_dev/program.py @@ -0,0 +1,45 @@ +from typing import Optional + +from sh_edraft.configuration.base import ConfigurationBase +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 + + +class Program(ApplicationBase): + + def __init__(self): + ApplicationBase.__init__(self) + + self._app_host: Optional[ApplicationHost] = None + self._services: Optional[ServiceProviderBase] = None + self._configuration: Optional[ConfigurationBase] = None + self._logger: Optional[LoggerBase] = None + + def create_application_host(self): + self._app_host = ApplicationHost() + self._configuration = self._app_host.configuration + self._services = self._app_host.services + + def create_configuration(self): + self._configuration.create() + self._configuration.add_environment_variables('PYTHON_') + self._configuration.add_environment_variables('CPL_') + 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) + + def create_services(self): + self._services.create() + self._services.add_singleton(LoggerBase, Logger) + self._logger = self._services.get_service(LoggerBase) + self._logger.create() + + def main(self): + self._logger.header(f'{self._configuration.environment.application_name}:') + self._logger.debug(__name__, f'Host: {self._configuration.environment.host_name}') + self._logger.debug(__name__, f'Environment: {self._configuration.environment.environment_name}') + self._logger.debug(__name__, f'Customer: {self._configuration.environment.customer}')