From ac238b360a2e3b62346bbb4db99fa0dd6e3237b4 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 2 Oct 2022 02:34:27 +0200 Subject: [PATCH 1/2] Added feature-flags --- src/bot/config/appsettings.development.json | 8 +++ .../config/appsettings.edrafts-pc-ubuntu.json | 8 +++ src/bot/config/appsettings.production.json | 8 +++ src/bot/config/appsettings.staging.json | 8 +++ src/bot/startup.py | 9 ++- src/bot/startup_discord_extension.py | 53 +++++++++------ .../configuration/feature_flags_settings.py | 65 +++++++++++++++++++ 7 files changed, 138 insertions(+), 21 deletions(-) create mode 100644 src/bot_core/configuration/feature_flags_settings.py diff --git a/src/bot/config/appsettings.development.json b/src/bot/config/appsettings.development.json index 53ed5d70..d75fdfea 100644 --- a/src/bot/config/appsettings.development.json +++ b/src/bot/config/appsettings.development.json @@ -17,6 +17,14 @@ "de" ] }, + "FeatureFlag": { + "AdminModule": true, + "BaseModule": true, + "BootLogModule": true, + "DatabaseModule": true, + "ModeratorModule": true, + "PermissionModule": true + }, "DiscordBot": { "Token": "OTk4MTU5NjczODkzMDYwNzM4.GN3QyA.yvWO6L7Eu36gXQ7ARDs0Jg2J1VqIDnHLou5lT4", "Prefix": "!kd " diff --git a/src/bot/config/appsettings.edrafts-pc-ubuntu.json b/src/bot/config/appsettings.edrafts-pc-ubuntu.json index 8c26c3ea..6523b2d9 100644 --- a/src/bot/config/appsettings.edrafts-pc-ubuntu.json +++ b/src/bot/config/appsettings.edrafts-pc-ubuntu.json @@ -19,6 +19,14 @@ "Token": "OTk4MTYwNDI3Njg5MTgxMjM3.GI7h67.BqD6Lu1Tz0MuG8iktYrcLnHi1pNozyMiWFGTKI", "Prefix": "!ke " }, + "FeatureFlag": { + "AdminModule": true, + "BaseModule": true, + "BootLogModule": true, + "DatabaseModule": true, + "ModeratorModule": true, + "PermissionModule": true + }, "Bot": { "910199451145076828": { "MessageDeleteTimer": 2 diff --git a/src/bot/config/appsettings.production.json b/src/bot/config/appsettings.production.json index 91872272..1f2dd507 100644 --- a/src/bot/config/appsettings.production.json +++ b/src/bot/config/appsettings.production.json @@ -28,6 +28,14 @@ "Buffered": "true", "AuthPlugin": "mysql_native_password" }, + "FeatureFlag": { + "AdminModule": true, + "BaseModule": true, + "BootLogModule": true, + "DatabaseModule": true, + "ModeratorModule": true, + "PermissionModule": true + }, "Bot": { "650366049023295514": { "MessageDeleteTimer": 2 diff --git a/src/bot/config/appsettings.staging.json b/src/bot/config/appsettings.staging.json index b69da3dd..3ddd1c3f 100644 --- a/src/bot/config/appsettings.staging.json +++ b/src/bot/config/appsettings.staging.json @@ -17,6 +17,14 @@ "de" ] }, + "FeatureFlag": { + "AdminModule": true, + "BaseModule": true, + "BootLogModule": true, + "DatabaseModule": true, + "ModeratorModule": true, + "PermissionModule": true + }, "Bot": { "910199451145076828": { "MessageDeleteTimer": 4 diff --git a/src/bot/startup.py b/src/bot/startup.py index db5fd508..4f936340 100644 --- a/src/bot/startup.py +++ b/src/bot/startup.py @@ -10,6 +10,7 @@ from cpl_core.environment import ApplicationEnvironment from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.pipes.date_time_offset_pipe import DateTimeOffsetPipe from bot_core.service.client_utils_service import ClientUtilsService from bot_core.service.message_service import MessageService @@ -39,6 +40,7 @@ class Startup(StartupABC): self._start_time = datetime.now() self._config: Optional[ConfigurationABC] = None + self._feature_flags: Optional[FeatureFlagsSettings] = None def configure_configuration(self, configuration: ConfigurationABC, environment: ApplicationEnvironment) -> ConfigurationABC: environment.set_working_directory(os.path.dirname(os.path.realpath(__file__))) @@ -52,6 +54,7 @@ class Startup(StartupABC): configuration.add_configuration('Startup_StartTime', str(self._start_time)) self._config = configuration + self._feature_flags = configuration.get_configuration(FeatureFlagsSettings) return configuration def configure_services(self, services: ServiceCollectionABC, environment: ApplicationEnvironment) -> ServiceProviderABC: @@ -61,7 +64,8 @@ class Startup(StartupABC): services.add_db_context(DBContext, self._config.get_configuration(DatabaseSettings)) # general services - services.add_transient(BaseHelperABC, BaseHelperService) + if self._feature_flags.base_module: + services.add_transient(BaseHelperABC, BaseHelperService) services.add_transient(MessageServiceABC, MessageService) services.add_transient(ClientUtilsServiceABC, ClientUtilsService) @@ -69,7 +73,8 @@ class Startup(StartupABC): services.add_transient(DateTimeOffsetPipe) # module services - services.add_singleton(PermissionServiceABC, PermissionService) + if self._feature_flags.permission_module: + services.add_singleton(PermissionServiceABC, PermissionService) # data services services.add_transient(ServerRepositoryABC, ServerRepositoryService) diff --git a/src/bot/startup_discord_extension.py b/src/bot/startup_discord_extension.py index 4d20e684..8e7b93ec 100644 --- a/src/bot/startup_discord_extension.py +++ b/src/bot/startup_discord_extension.py @@ -1,3 +1,5 @@ +from typing import Optional + from cpl_core.application import StartupExtensionABC from cpl_core.configuration import ConfigurationABC from cpl_core.dependency_injection import ServiceCollectionABC @@ -5,6 +7,7 @@ from cpl_core.environment import ApplicationEnvironmentABC from cpl_discord import get_discord_collection from cpl_discord.discord_event_types_enum import DiscordEventTypesEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from modules.admin.command.restart_command import RestartCommand from modules.admin.command.shutdown_command import ShutdownCommand from modules.base.command.afk_command import AFKCommand @@ -27,40 +30,52 @@ from modules.permission.events.permission_on_ready_event import PermissionOnRead class StartupDiscordExtension(StartupExtensionABC): def __init__(self): - pass + self._feature_flags: Optional[FeatureFlagsSettings] = None def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC): - pass + self._feature_flags = config.get_configuration(FeatureFlagsSettings) def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): services.add_discord() dc = get_discord_collection(services) """ commands """ # admin - dc.add_command(RestartCommand) - dc.add_command(ShutdownCommand) + if self._feature_flags.admin_module: + dc.add_command(RestartCommand) + dc.add_command(ShutdownCommand) # moderator - dc.add_command(PurgeCommand) + if self._feature_flags.moderator_module: + dc.add_command(PurgeCommand) # simple - dc.add_command(AFKCommand) - dc.add_command(HelpCommand) - dc.add_command(InfoCommand) - dc.add_command(PingCommand) - dc.add_command(UserInfoCommand) + if self._feature_flags.base_module: + dc.add_command(AFKCommand) + dc.add_command(HelpCommand) + dc.add_command(InfoCommand) + dc.add_command(PingCommand) + dc.add_command(UserInfoCommand) """ events """ # on_command_error - dc.add_event(DiscordEventTypesEnum.on_command_error.value, BaseOnCommandErrorEvent) + if self._feature_flags.base_module: + dc.add_event(DiscordEventTypesEnum.on_command_error.value, BaseOnCommandErrorEvent) # on_member_join - dc.add_event(DiscordEventTypesEnum.on_member_join.value, BaseOnMemberJoinEvent) + if self._feature_flags.base_module: + dc.add_event(DiscordEventTypesEnum.on_member_join.value, BaseOnMemberJoinEvent) # on_member_remove - dc.add_event(DiscordEventTypesEnum.on_member_join.value, BaseOnMemberRemoveEvent) + if self._feature_flags.base_module: + dc.add_event(DiscordEventTypesEnum.on_member_join.value, BaseOnMemberRemoveEvent) # on_member_update - dc.add_event(DiscordEventTypesEnum.on_member_update.value, PermissionOnMemberUpdateEvent) + if self._feature_flags.permission_module: + dc.add_event(DiscordEventTypesEnum.on_member_update.value, PermissionOnMemberUpdateEvent) # on_message - dc.add_event(DiscordEventTypesEnum.on_message.value, BaseOnMessageEvent) + if self._feature_flags.base_module: + dc.add_event(DiscordEventTypesEnum.on_message.value, BaseOnMessageEvent) # on_voice_state_update - dc.add_event(DiscordEventTypesEnum.on_voice_state_update.value, BaseOnVoiceStateUpdateEvent) + if self._feature_flags.base_module: + dc.add_event(DiscordEventTypesEnum.on_voice_state_update.value, BaseOnVoiceStateUpdateEvent) # on_ready - dc.add_event(DiscordEventTypesEnum.on_ready.value, DatabaseOnReadyEvent) - dc.add_event(DiscordEventTypesEnum.on_ready.value, PermissionOnReadyEvent) - dc.add_event(DiscordEventTypesEnum.on_ready.value, BootLogOnReadyEvent) # has to be last + if self._feature_flags.database_module: + dc.add_event(DiscordEventTypesEnum.on_ready.value, DatabaseOnReadyEvent) + if self._feature_flags.permission_module: + dc.add_event(DiscordEventTypesEnum.on_ready.value, PermissionOnReadyEvent) + if self._feature_flags.boot_log_module: + dc.add_event(DiscordEventTypesEnum.on_ready.value, BootLogOnReadyEvent) # has to be last diff --git a/src/bot_core/configuration/feature_flags_settings.py b/src/bot_core/configuration/feature_flags_settings.py new file mode 100644 index 00000000..8d280744 --- /dev/null +++ b/src/bot_core/configuration/feature_flags_settings.py @@ -0,0 +1,65 @@ +import traceback +from typing import Optional + +from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC +from cpl_core.console import Console + + +class FeatureFlagsSettings(ConfigurationModelABC): + + def __init__(self): + ConfigurationModelABC.__init__(self) + + self._admin_module = False # 02.10.2022 #48 + self._base_module = True # 02.10.2022 #48 + self._boot_log_module = True # 02.10.2022 #48 + self._database_module = True # 02.10.2022 #48 + self._moderator_module = False # 02.10.2022 #48 + self._permission_module = True # 02.10.2022 #48 + + @property + def admin_module(self) -> bool: + return self._admin_module + + @property + def base_module(self) -> bool: + return self._base_module + + @property + def boot_log_module(self) -> bool: + return self._boot_log_module + + @property + def database_module(self) -> bool: + return self._database_module + + @property + def moderator_module(self) -> bool: + return self._moderator_module + + @property + def permission_module(self) -> bool: + return self._permission_module + + def from_dict(self, settings: dict): + try: + if 'AdminModule' in settings: + self._admin_module = settings['AdminModule'] + + if 'BaseModule' in settings: + self._base_module = settings['BaseModule'] + + if 'BootLogModule' in settings: + self._boot_log_module = settings['BootLogModule'] + + if 'DatabaseModule' in settings: + self._database_module = settings['DatabaseModule'] + + if 'ModeratorModule' in settings: + self._moderator_module = settings['ModeratorModule'] + + if 'PermissionModule' in settings: + self._permission_module = settings['PermissionModule'] + except Exception as e: + Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') + Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') From 890cb6f6b5487b2d0786365131a957a3c10b62c7 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 2 Oct 2022 02:54:08 +0200 Subject: [PATCH 2/2] Added feature-flags #48 --- src/bot/config/appsettings.development.json | 2 +- .../config/appsettings.edrafts-pc-ubuntu.json | 2 +- src/bot/config/appsettings.production.json | 2 +- src/bot/config/appsettings.staging.json | 2 +- src/bot/main.py | 14 ++++++---- src/bot/startup.py | 14 ---------- src/bot/startup_settings_extension.py | 28 +++++++++++++++++++ src/modules/boot_log/boot_log_extension.py | 5 ++++ src/modules/database/database_extension.py | 4 +++ 9 files changed, 49 insertions(+), 24 deletions(-) create mode 100644 src/bot/startup_settings_extension.py diff --git a/src/bot/config/appsettings.development.json b/src/bot/config/appsettings.development.json index d75fdfea..44dbc166 100644 --- a/src/bot/config/appsettings.development.json +++ b/src/bot/config/appsettings.development.json @@ -17,7 +17,7 @@ "de" ] }, - "FeatureFlag": { + "FeatureFlags": { "AdminModule": true, "BaseModule": true, "BootLogModule": true, diff --git a/src/bot/config/appsettings.edrafts-pc-ubuntu.json b/src/bot/config/appsettings.edrafts-pc-ubuntu.json index 6523b2d9..20585cb5 100644 --- a/src/bot/config/appsettings.edrafts-pc-ubuntu.json +++ b/src/bot/config/appsettings.edrafts-pc-ubuntu.json @@ -19,7 +19,7 @@ "Token": "OTk4MTYwNDI3Njg5MTgxMjM3.GI7h67.BqD6Lu1Tz0MuG8iktYrcLnHi1pNozyMiWFGTKI", "Prefix": "!ke " }, - "FeatureFlag": { + "FeatureFlags": { "AdminModule": true, "BaseModule": true, "BootLogModule": true, diff --git a/src/bot/config/appsettings.production.json b/src/bot/config/appsettings.production.json index 1f2dd507..515c60af 100644 --- a/src/bot/config/appsettings.production.json +++ b/src/bot/config/appsettings.production.json @@ -28,7 +28,7 @@ "Buffered": "true", "AuthPlugin": "mysql_native_password" }, - "FeatureFlag": { + "FeatureFlags": { "AdminModule": true, "BaseModule": true, "BootLogModule": true, diff --git a/src/bot/config/appsettings.staging.json b/src/bot/config/appsettings.staging.json index 3ddd1c3f..605fe7fc 100644 --- a/src/bot/config/appsettings.staging.json +++ b/src/bot/config/appsettings.staging.json @@ -17,7 +17,7 @@ "de" ] }, - "FeatureFlag": { + "FeatureFlags": { "AdminModule": true, "BaseModule": true, "BootLogModule": true, diff --git a/src/bot/main.py b/src/bot/main.py index 0bfb5a82..29c81fc1 100644 --- a/src/bot/main.py +++ b/src/bot/main.py @@ -8,6 +8,7 @@ from bot.application import Application from bot.startup import Startup from bot.startup_discord_extension import StartupDiscordExtension from bot.startup_migration_extension import StartupMigrationExtension +from bot.startup_settings_extension import StartupSettingsExtension from modules.boot_log.boot_log_extension import BootLogExtension from modules.database.database_extension import DatabaseExtension @@ -18,12 +19,13 @@ class Program: self.app: Optional[Application] = None async def start(self): - app_builder = ApplicationBuilder(Application) - app_builder.use_extension(StartupDiscordExtension) - app_builder.use_extension(StartupMigrationExtension) - app_builder.use_extension(BootLogExtension) - app_builder.use_extension(DatabaseExtension) - app_builder.use_startup(Startup) + app_builder = ApplicationBuilder(Application) \ + .use_extension(StartupSettingsExtension) \ + .use_extension(StartupDiscordExtension) \ + .use_extension(StartupMigrationExtension) \ + .use_extension(BootLogExtension) \ + .use_extension(DatabaseExtension) \ + .use_startup(Startup) self.app: Application = await app_builder.build_async() await self.app.run_async() diff --git a/src/bot/startup.py b/src/bot/startup.py index 4f936340..689c9819 100644 --- a/src/bot/startup.py +++ b/src/bot/startup.py @@ -1,5 +1,3 @@ -import os -from datetime import datetime from typing import Optional from cpl_core.application import StartupABC @@ -37,22 +35,10 @@ class Startup(StartupABC): def __init__(self): StartupABC.__init__(self) - self._start_time = datetime.now() - self._config: Optional[ConfigurationABC] = None self._feature_flags: Optional[FeatureFlagsSettings] = None def configure_configuration(self, configuration: ConfigurationABC, environment: ApplicationEnvironment) -> ConfigurationABC: - environment.set_working_directory(os.path.dirname(os.path.realpath(__file__))) - configuration.add_environment_variables('KDB_') - configuration.add_environment_variables('DISCORD_') - - configuration.add_json_file(f'config/appsettings.json', optional=False) - configuration.add_json_file(f'config/appsettings.{environment.environment_name}.json', optional=True) - configuration.add_json_file(f'config/appsettings.{environment.host_name}.json', optional=True) - - configuration.add_configuration('Startup_StartTime', str(self._start_time)) - self._config = configuration self._feature_flags = configuration.get_configuration(FeatureFlagsSettings) return configuration diff --git a/src/bot/startup_settings_extension.py b/src/bot/startup_settings_extension.py new file mode 100644 index 00000000..8c27e69b --- /dev/null +++ b/src/bot/startup_settings_extension.py @@ -0,0 +1,28 @@ +import os +from datetime import datetime + +from cpl_core.application import StartupExtensionABC +from cpl_core.configuration import ConfigurationABC +from cpl_core.dependency_injection import ServiceCollectionABC +from cpl_core.environment import ApplicationEnvironmentABC + + +class StartupSettingsExtension(StartupExtensionABC): + + def __init__(self): + self._start_time = datetime.now() + + def configure_configuration(self, configuration: ConfigurationABC, environment: ApplicationEnvironmentABC): + # this shit has to be done here because we need settings in subsequent startup extensions + environment.set_working_directory(os.path.dirname(os.path.realpath(__file__))) + configuration.add_environment_variables('KDB_') + configuration.add_environment_variables('DISCORD_') + + configuration.add_json_file(f'config/appsettings.json', optional=False) + configuration.add_json_file(f'config/appsettings.{environment.environment_name}.json', optional=True) + configuration.add_json_file(f'config/appsettings.{environment.host_name}.json', optional=True) + + configuration.add_configuration('Startup_StartTime', str(self._start_time)) + + def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): + pass diff --git a/src/modules/boot_log/boot_log_extension.py b/src/modules/boot_log/boot_log_extension.py index 8bb36c7e..42db4b8e 100644 --- a/src/modules/boot_log/boot_log_extension.py +++ b/src/modules/boot_log/boot_log_extension.py @@ -5,6 +5,8 @@ from cpl_core.configuration import ConfigurationABC from cpl_core.dependency_injection import ServiceProviderABC from cpl_core.logging import LoggerABC +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings + class BootLogExtension(ApplicationExtensionABC): @@ -12,6 +14,9 @@ class BootLogExtension(ApplicationExtensionABC): pass async def run(self, config: ConfigurationABC, services: ServiceProviderABC): + feature_flags: FeatureFlagsSettings = config.get_configuration(FeatureFlagsSettings) + if not feature_flags.boot_log_module: + return logger: LoggerABC = services.get_service(LoggerABC) logger.debug(__name__, 'BootLog extension started') config.add_configuration('Bot_StartTime', str(datetime.now())) diff --git a/src/modules/database/database_extension.py b/src/modules/database/database_extension.py index edcca9b9..883752bb 100644 --- a/src/modules/database/database_extension.py +++ b/src/modules/database/database_extension.py @@ -5,6 +5,7 @@ from cpl_core.configuration import ConfigurationABC from cpl_core.dependency_injection import ServiceProviderABC from cpl_core.logging import LoggerABC +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_data.service.migration_service import MigrationService @@ -14,6 +15,9 @@ class DatabaseExtension(ApplicationExtensionABC): pass async def run(self, config: ConfigurationABC, services: ServiceProviderABC): + feature_flags: FeatureFlagsSettings = config.get_configuration(FeatureFlagsSettings) + if not feature_flags.database_module: + return logger: LoggerABC = services.get_service(LoggerABC) logger.debug(__name__, 'Database extension started') config.add_configuration('Database_StartTime', str(datetime.now()))