From 464071503bd239dc7baf8448ba8bb3c092867a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=9F=D0=B8=D1=81?= =?UTF-8?q?=D1=8C=D0=BC=D0=B5=D0=BD=D0=BD=D1=8B=D0=B9?= <8alig8@gmail.com> Date: Mon, 27 Nov 2023 15:38:02 +0300 Subject: [PATCH 1/2] Initial commit. --- .editorconfig | 286 ++++++++++++++++++ .github/workflows/netcorelibrary.yml | 62 ++++ .gitignore | 22 ++ LICENSE | 4 +- Monq.Plugins.Abstractions.sln | 46 +++ README.md | 66 +++- .../Monq.Plugins.Abstractions.Tests.csproj | 26 ++ .../PluginTests.cs | 12 + .../Converters/DictionaryConverter.cs | 22 ++ .../PluginNotConfiguredException.cs | 12 + .../Extensions/ObjectExtensions.cs | 80 +++++ .../IPluginTaskBootstrap.cs | 23 ++ .../IPluginTaskPostProcessor.cs | 14 + .../IPluginTaskStrategy.cs | 16 + .../Monq.Plugins.Abstractions.csproj | 65 ++++ src/Monq.Plugins.Abstractions/PluginTask.cs | 36 +++ 16 files changed, 789 insertions(+), 3 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/workflows/netcorelibrary.yml create mode 100644 .gitignore create mode 100644 Monq.Plugins.Abstractions.sln create mode 100644 src/Monq.Plugins.Abstractions.Tests/Monq.Plugins.Abstractions.Tests.csproj create mode 100644 src/Monq.Plugins.Abstractions.Tests/PluginTests.cs create mode 100644 src/Monq.Plugins.Abstractions/Converters/DictionaryConverter.cs create mode 100644 src/Monq.Plugins.Abstractions/Exceptions/PluginNotConfiguredException.cs create mode 100644 src/Monq.Plugins.Abstractions/Extensions/ObjectExtensions.cs create mode 100644 src/Monq.Plugins.Abstractions/IPluginTaskBootstrap.cs create mode 100644 src/Monq.Plugins.Abstractions/IPluginTaskPostProcessor.cs create mode 100644 src/Monq.Plugins.Abstractions/IPluginTaskStrategy.cs create mode 100644 src/Monq.Plugins.Abstractions/Monq.Plugins.Abstractions.csproj create mode 100644 src/Monq.Plugins.Abstractions/PluginTask.cs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9dcd410 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,286 @@ +# Удалите строку ниже, если вы хотите наследовать параметры .editorconfig из каталогов, расположенных выше в иерархии +root = true + +# Файлы csproj +[*.csproj] +tab_size = 2 +indent_size = 2 +indent_style = space + +# Файлы C# +[*.cs] + +#### Основные параметры EditorConfig #### + +# Отступы и интервалы +indent_size = 4 +indent_style = space +tab_width = 4 + +# Предпочтения для новых строк +end_of_line = crlf +insert_final_newline = true + +#### Параметры MONQ +csharp_default_private_modifier = implicit + +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = error +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.required_modifiers = const +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +##### Namespace per file scope +csharp_style_namespace_declarations= file_scoped:suggestion +csharp_indent_braces=false + +spelling_languages = en-us,ru-ru + +#### Рекомендации по написанию кода .NET #### + +# Упорядочение Using +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false + +# Предпочтения для this. и Me. +dotnet_style_qualification_for_event = false:silent +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_property = false:silent + +# Параметры использования ключевых слов языка и типов BCL +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent + +# Предпочтения для скобок +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent + +# Предпочтения модификатора +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent + +# Выражения уровень предпочтения +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_throw_expression = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion + +# Предпочтения для полей +dotnet_style_readonly_field = true:suggestion + +# Настройки параметров +dotnet_code_quality_unused_parameters = all:suggestion + +#### Рекомендации по написанию кода C# #### + +# Предпочтения var +csharp_style_var_elsewhere = true:suggestion +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion + +# Члены, заданные выражениями +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:suggestion +csharp_style_expression_bodied_local_functions = false:suggestion +csharp_style_expression_bodied_methods = true:suggestion +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:suggestion + +# Настройки соответствия шаблонов +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_prefer_switch_expression = true:suggestion + +# Настройки проверки на null +csharp_style_conditional_delegate_call = true:suggestion + +# Предпочтения модификатора +csharp_prefer_static_local_function = true:suggestion +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async + +# Предпочтения для блоков кода +csharp_prefer_braces = false:silent +csharp_prefer_simple_using_statement = false:suggestion + +# Выражения уровень предпочтения +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent + +# предпочтения для директивы using +csharp_using_directive_placement = outside_namespace:silent + +#### Правила форматирования C# #### + +# Предпочтения для новых строк +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Предпочтения для отступов +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Предпочтения для интервалов +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Предпочтения переноса +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Стили именования #### + +# Правила именования + +dotnet_naming_rule.interface_should_be_начинается_с_i.severity = suggestion +dotnet_naming_rule.interface_should_be_начинается_с_i.symbols = interface +dotnet_naming_rule.interface_should_be_начинается_с_i.style = начинается_с_i + +dotnet_naming_rule.типы_should_be_всечастиспрописнойбуквы.severity = suggestion +dotnet_naming_rule.типы_should_be_всечастиспрописнойбуквы.symbols = типы +dotnet_naming_rule.типы_should_be_всечастиспрописнойбуквы.style = pascal_case_style + +dotnet_naming_rule.не_являющиеся_полем_члены_should_be_всечастиспрописнойбуквы.severity = suggestion +dotnet_naming_rule.не_являющиеся_полем_члены_should_be_всечастиспрописнойбуквы.symbols = не_являющиеся_полем_члены +dotnet_naming_rule.не_являющиеся_полем_члены_should_be_всечастиспрописнойбуквы.style = pascal_case_style + +dotnet_naming_rule.частное_или_внутреннее_поле_should_be_начинается_с__.severity = error +dotnet_naming_rule.частное_или_внутреннее_поле_should_be_начинается_с__.symbols = частное_или_внутреннее_поле +dotnet_naming_rule.частное_или_внутреннее_поле_should_be_начинается_с__.style = начинается_с__ + +# Спецификации символов + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.частное_или_внутреннее_поле.applicable_kinds = field +dotnet_naming_symbols.частное_или_внутреннее_поле.applicable_accessibilities = internal, private +dotnet_naming_symbols.частное_или_внутреннее_поле.required_modifiers = + +dotnet_naming_symbols.типы.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.типы.applicable_accessibilities = public, internal, private, protected, protected_internal +dotnet_naming_symbols.типы.required_modifiers = + +dotnet_naming_symbols.не_являющиеся_полем_члены.applicable_kinds = property, event, method +dotnet_naming_symbols.не_являющиеся_полем_члены.applicable_accessibilities = public, internal, private, protected, protected_internal +dotnet_naming_symbols.не_являющиеся_полем_члены.required_modifiers = + +# Стили именования + +dotnet_naming_style.всечастиспрописнойбуквы.required_prefix = +dotnet_naming_style.всечастиспрописнойбуквы.required_suffix = +dotnet_naming_style.всечастиспрописнойбуквы.word_separator = +dotnet_naming_style.всечастиспрописнойбуквы.capitalization = pascal_case + +dotnet_naming_style.начинается_с_i.required_prefix = I +dotnet_naming_style.начинается_с_i.required_suffix = +dotnet_naming_style.начинается_с_i.word_separator = +dotnet_naming_style.начинается_с_i.capitalization = pascal_case + +dotnet_naming_style.начинается_с__.required_prefix = _ +dotnet_naming_style.начинается_с__.required_suffix = +dotnet_naming_style.начинается_с__.word_separator = +dotnet_naming_style.начинается_с__.capitalization = camel_case +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_prefer_pattern_matching = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent +csharp_style_prefer_readonly_struct = true:suggestion +csharp_style_prefer_readonly_struct_member = true:suggestion + +[*.{cs,vb}] +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +end_of_line = crlf +dotnet_code_quality_unused_parameters = all:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +dotnet_style_qualification_for_field = false:warning +dotnet_style_qualification_for_property = false:warning +dotnet_style_qualification_for_method = false:warning +dotnet_style_qualification_for_event = false:warning +dotnet_style_allow_multiple_blank_lines_experimental = false:warning +dotnet_style_allow_statement_immediately_after_block_experimental = true:silent +dotnet_style_readonly_field = true:suggestion +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent diff --git a/.github/workflows/netcorelibrary.yml b/.github/workflows/netcorelibrary.yml new file mode 100644 index 0000000..75db6df --- /dev/null +++ b/.github/workflows/netcorelibrary.yml @@ -0,0 +1,62 @@ +name: NetCore-Library + +# Run this workflow every time a new commit pushed to your repository +on: + push: + # Sequence of patterns matched against refs/heads + branches: + # Push events on main branch + - master + - develop + +jobs: + # Set the job key. The key is displayed as the job name + # when a job name is not provided + build-and-publish: + # Name the Job + name: Build and Publish Library + # Set the type of machine to run on + runs-on: ubuntu-latest + env: + MAINAPP_ASP_PROJECT_PATH: "src/Monq.Plugins.Abstractions" + TEST_MAINAPP_ASP_PROJECT_PATH: "src/Monq.Plugins.Abstractions.Tests" + + steps: + # Checks out a copy of your repository on the ubuntu-latest machine + - uses: actions/checkout@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 7.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build + run: | + dotnet build -c Release --no-restore + echo "APP_VERSION=v$(cat ${MAINAPP_ASP_PROJECT_PATH}/*.csproj | grep '' | head -n 1 | awk -F '>' '{print $2;}' | awk -F '<' '{print $1;}' | sed 's/\-\*//g')" >> $GITHUB_ENV + - name: Test + run: | + dotnet add $TEST_MAINAPP_ASP_PROJECT_PATH package coverlet.msbuild --version ${coverletMsbuildVersion:-3.0.3} > /dev/null 2>&1 + dotnet test $TEST_MAINAPP_ASP_PROJECT_PATH -c Release --no-build --no-restore /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:CoverletOutput=../../build_report/opencover.xml + - name: Pack Alpha + if: github.ref == 'refs/heads/develop' + run: dotnet pack -c Release --no-restore $MAINAPP_ASP_PROJECT_PATH -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg --version-suffix "rev-$GITHUB_RUN_ID" + - name: Pack Release + if: github.ref == 'refs/heads/master' + run: dotnet pack -c Release --no-restore $MAINAPP_ASP_PROJECT_PATH -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg + - name: Publish + run: + dotnet nuget push $MAINAPP_ASP_PROJECT_PATH/bin/Release/*.nupkg -k $NUGET_TOKEN -s $NUGET_SERVER + env: + NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }} + NUGET_SERVER: "https://api.nuget.org/v3/index.json" + - name: Create GitHub Release + if: github.ref == 'refs/heads/master' + id: create_release + uses: actions/create-release@latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ env.APP_VERSION }} + draft: false + prerelease: false \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85d6680 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +/.project +/.vs/ +**/bin/ +**/obj/ +**/artifacts/ +**/project.lock.json +**/appsettings.Development.json +**/.vscode/ +**/wwwroot/lib/ +**/*.xproj.user +**/*.csproj.user +**/aspnet_consul_config.json +/.cr + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# ReSharper +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user \ No newline at end of file diff --git a/LICENSE b/LICENSE index 17ee54c..f4700e4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Monq +Copyright (c) 2020 MONQ Digital lab Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +SOFTWARE. \ No newline at end of file diff --git a/Monq.Plugins.Abstractions.sln b/Monq.Plugins.Abstractions.sln new file mode 100644 index 0000000..1307c34 --- /dev/null +++ b/Monq.Plugins.Abstractions.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34024.191 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monq.Plugins.Abstractions", "src\Monq.Plugins.Abstractions\Monq.Plugins.Abstractions.csproj", "{31C96D7B-6E68-47FF-8127-FE80C5EB0E8F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monq.Plugins.Abstractions.Tests", "src\Monq.Plugins.Abstractions.Tests\Monq.Plugins.Abstractions.Tests.csproj", "{3EE6E9B6-EFE0-4085-82B3-EFCDA0E81961}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7EBB1D4E-A99D-4765-9D25-8742AEF5D72A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{ADC65B4F-DD31-4169-8EFC-C852344AB347}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .gitignore = .gitignore + .github\workflows\netcorelibrary.yml = .github\workflows\netcorelibrary.yml + NuGet.config = NuGet.config + README.md = README.md + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {31C96D7B-6E68-47FF-8127-FE80C5EB0E8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {31C96D7B-6E68-47FF-8127-FE80C5EB0E8F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {31C96D7B-6E68-47FF-8127-FE80C5EB0E8F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {31C96D7B-6E68-47FF-8127-FE80C5EB0E8F}.Release|Any CPU.Build.0 = Release|Any CPU + {3EE6E9B6-EFE0-4085-82B3-EFCDA0E81961}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3EE6E9B6-EFE0-4085-82B3-EFCDA0E81961}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3EE6E9B6-EFE0-4085-82B3-EFCDA0E81961}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3EE6E9B6-EFE0-4085-82B3-EFCDA0E81961}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {31C96D7B-6E68-47FF-8127-FE80C5EB0E8F} = {7EBB1D4E-A99D-4765-9D25-8742AEF5D72A} + {3EE6E9B6-EFE0-4085-82B3-EFCDA0E81961} = {7EBB1D4E-A99D-4765-9D25-8742AEF5D72A} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {657E1993-D538-490A-AF16-70A39EDCBE0F} + EndGlobalSection +EndGlobal diff --git a/README.md b/README.md index 7bebb11..bde322f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,66 @@ # Monq.Plugins.Abstractions -A library with a set of methods, classes and interfaces for implementing DLL plugins for MONQ agents. + +Библиотека с набором методов, классов и интерфейсов для реализации DLL плагинов для агентов MONQ. + +## Руководство к написанию плагинов агента + +### Создание проекта + +1. Подключить к проекту библиотеку `Monq.Plugins.Abstractions`. + + Библиотека предоставляет два основных интерфейса, которые должны быть реализованы в каждом плагине: + + - `IPluginTaskStrategy` - интерфейс с методом запуска плагинного задания. + - `IPluginTaskBootstrap` - интерфейс с методом регистрации сервисов для работы плагинного задания. + +2. Версию плагина определить в .csproj файле основного проекта. + + ``` xml + 1.0.0 + $(VersionSuffix) + $(Version)-$(VersionSuffix) + ``` + +3. Указать название сборки в .csproj файле основного проекта, которое будет совпадать с названием вызывающей команды плагина в скриптах задания. + + ``` xml + pluginCommand + ``` + + ``` yaml + # yaml agent task script + ... + steps: + - plugin: pluginCommand + ... + ``` + +4. Подключаемые библиотеки, которые определены на агенте, необходимо исключать из финальной публикации во избежание конфликтов. Для этого в .csproj файле основного проекта указать настройки пакета: + + ``` xml + + false + runtime + + ``` + + Список подключенных на агенте библиотек указан в разделе [Список библиотек](#список-библиотек). + +### Сборка и использование на агенте + +Команда для сборки: `dotnet publish -c Release`. В папке с опубликованными файлами будет находиться нужный набор файлов для подключения на агенте. + +Чтобы подключить плагин на агенте, нужно в папке с плагинами агента создать папку, совпадающую с названием вызывающей команды плагина в скриптах задания. В папку нужно перенести опубликованные файлы в первозданном виде. + +### Список библиотек + +| Название | Версия | +|---|---| +| AutoMapper | 10.1.1 | +| Microsoft.Data.SqlClient | 3.0.1 | +| Microsoft.EntityFrameworkCore.SqlServer | 5.0.11 | +| Microsoft.Extensions.Http | 5.0.0 | +| Microsoft.Extensions.Logging.Abstractions | 5.0.0 | +| Microsoft.Extensions.Options.ConfigurationExtensions | 5.0.0 | +| Monq.Core.BasicDotNetMicroservice | 6.1.0 | +| Monq.Plugins.Abstractions | 1.4.2 | diff --git a/src/Monq.Plugins.Abstractions.Tests/Monq.Plugins.Abstractions.Tests.csproj b/src/Monq.Plugins.Abstractions.Tests/Monq.Plugins.Abstractions.Tests.csproj new file mode 100644 index 0000000..4575efb --- /dev/null +++ b/src/Monq.Plugins.Abstractions.Tests/Monq.Plugins.Abstractions.Tests.csproj @@ -0,0 +1,26 @@ + + + + net7.0 + false + enable + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/src/Monq.Plugins.Abstractions.Tests/PluginTests.cs b/src/Monq.Plugins.Abstractions.Tests/PluginTests.cs new file mode 100644 index 0000000..80317a9 --- /dev/null +++ b/src/Monq.Plugins.Abstractions.Tests/PluginTests.cs @@ -0,0 +1,12 @@ +using Xunit; + +namespace Monq.Plugins.Abstractions.Tests; + +public class PluginTests +{ + [Fact(DisplayName = "Проверка регистрации плагина.", Skip = "Реализовать")] + public void ShouldProperlyRegisterPlugin() + { + // TODO: реализовать. + } +} diff --git a/src/Monq.Plugins.Abstractions/Converters/DictionaryConverter.cs b/src/Monq.Plugins.Abstractions/Converters/DictionaryConverter.cs new file mode 100644 index 0000000..ba8d84c --- /dev/null +++ b/src/Monq.Plugins.Abstractions/Converters/DictionaryConverter.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Monq.Plugins.Abstractions.Converters; + +#pragma warning disable CS1591 // Отсутствует комментарий XML для открытого видимого типа или члена +public class DictionaryConverter : CustomCreationConverter> +{ + public override IDictionary Create(Type objectType) + => new Dictionary(); + + public override bool CanConvert(Type objectType) + => objectType == typeof(object) || base.CanConvert(objectType); + + public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + { + if (reader.TokenType is JsonToken.StartObject or JsonToken.Null) + return base.ReadJson(reader, objectType, existingValue, serializer); + + return serializer.Deserialize(reader); + } +} diff --git a/src/Monq.Plugins.Abstractions/Exceptions/PluginNotConfiguredException.cs b/src/Monq.Plugins.Abstractions/Exceptions/PluginNotConfiguredException.cs new file mode 100644 index 0000000..a4f30ce --- /dev/null +++ b/src/Monq.Plugins.Abstractions/Exceptions/PluginNotConfiguredException.cs @@ -0,0 +1,12 @@ +namespace Monq.Plugins.Abstractions.Exceptions; + +#pragma warning disable CS1591 // Отсутствует комментарий XML для открытого видимого типа или члена +public class PluginNotConfiguredException : Exception +{ + const string DefaultMessage = "Plugin is not configured."; + + public PluginNotConfiguredException(string? details = null) + : base($"{DefaultMessage}{(string.IsNullOrEmpty(details) ? string.Empty : $" Details: {details}")}") + { + } +} diff --git a/src/Monq.Plugins.Abstractions/Extensions/ObjectExtensions.cs b/src/Monq.Plugins.Abstractions/Extensions/ObjectExtensions.cs new file mode 100644 index 0000000..39eeaaf --- /dev/null +++ b/src/Monq.Plugins.Abstractions/Extensions/ObjectExtensions.cs @@ -0,0 +1,80 @@ +using Monq.Plugins.Abstractions.Converters; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; +using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs; + +namespace Monq.Plugins.Abstractions.Extensions; + +/// +/// Методы расширения для работы с объектами. +/// +public static class ObjectExtensions +{ + static readonly JsonSerializerSettings _serializerOptions = new() + { + NullValueHandling = NullValueHandling.Ignore, + Converters = { new DictionaryConverter(), new StringEnumConverter() }, + ContractResolver = new CamelCasePropertyNamesContractResolver() + }; + + /// + /// Преобразовать объект в словарь. + /// + /// Исходный объект. + /// + public static IDictionary ToDictionary(this object source) + { + var json = JsonConvert.SerializeObject(source, _serializerOptions); + var result = JsonConvert.DeserializeObject>(json, _serializerOptions) + ?? new Dictionary(); + return result; + } + + /// + /// Преобразовать словарь в объект. + /// + /// + /// Исходный словарь. + /// + public static T ToObject(this IDictionary source) + where T : class, new() + => source.ToObject(ignoreErrors: true); + + // REM: для плагинных систем стоит перегружать методы определением отдельных сигнатур, + // а не добавлением необязательных параметров, чтобы не поломать обратную совместимость. + /// + /// Преобразовать словарь в объект. + /// + /// + /// Исходный словарь. + /// Флаг игнорирования ошибок конвертации. + /// + public static T ToObject(this IDictionary source, bool ignoreErrors = true) + where T : class, new() + { + var json = JsonConvert.SerializeObject(source, _serializerOptions); + + T Deserialization() + { + return JsonConvert.DeserializeObject(json, _serializerOptions) ?? new(); + } + var result = ignoreErrors + ? IgnoreErrors(Deserialization) + : Deserialization(); + + return result; + } + + static T IgnoreErrors(Func operation) + where T : class, new() + { + _serializerOptions.Error += Ignore; + var result = operation.Invoke(); + _serializerOptions.Error -= Ignore; + return result; + } + + static void Ignore(object? sender, ErrorEventArgs args) + => args.ErrorContext.Handled = true; +} diff --git a/src/Monq.Plugins.Abstractions/IPluginTaskBootstrap.cs b/src/Monq.Plugins.Abstractions/IPluginTaskBootstrap.cs new file mode 100644 index 0000000..b4d86f2 --- /dev/null +++ b/src/Monq.Plugins.Abstractions/IPluginTaskBootstrap.cs @@ -0,0 +1,23 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Monq.Plugins.Abstractions.Models; + +namespace Monq.Plugins.Abstractions; + +/// +/// Интерфейс загрузчика задания. +/// +public interface IPluginTaskBootstrap +{ + /// + /// Выполнить регистрацию методов работы с заданием. + /// + /// Коллекция сервисов. + /// Конфигурация приложения. + void RegisterServiceProvider(IServiceCollection services, IConfiguration configuration); + + /// + /// Задание плагина. + /// + PluginTask PluginTask { get; } +} diff --git a/src/Monq.Plugins.Abstractions/IPluginTaskPostProcessor.cs b/src/Monq.Plugins.Abstractions/IPluginTaskPostProcessor.cs new file mode 100644 index 0000000..b91ee66 --- /dev/null +++ b/src/Monq.Plugins.Abstractions/IPluginTaskPostProcessor.cs @@ -0,0 +1,14 @@ +namespace Monq.Plugins.Abstractions; + +/// +/// Интерфейс обработчика результата задания после его выполнения. +/// +public interface IPluginTaskPostProcessor +{ + /// + /// Выполнить обработку задания после его выполнения. + /// + /// Выходные данные задания. + /// , показывающий выполнение операции. + Task Process(IDictionary outputs); +} diff --git a/src/Monq.Plugins.Abstractions/IPluginTaskStrategy.cs b/src/Monq.Plugins.Abstractions/IPluginTaskStrategy.cs new file mode 100644 index 0000000..f1babef --- /dev/null +++ b/src/Monq.Plugins.Abstractions/IPluginTaskStrategy.cs @@ -0,0 +1,16 @@ +namespace Monq.Plugins.Abstractions; + +/// +/// Стратегия выполнения задания плагина. +/// +public interface IPluginTaskStrategy +{ + /// + /// Выполнить задание для плагина. + /// + /// Переменные задания плагина. + /// Список защищённых переменных. + /// Токен для отмены выполнения задания. + /// Выходные данные задания. + Task> Run(IDictionary variables, IEnumerable securedVariables, CancellationToken cancellationToken); +} diff --git a/src/Monq.Plugins.Abstractions/Monq.Plugins.Abstractions.csproj b/src/Monq.Plugins.Abstractions/Monq.Plugins.Abstractions.csproj new file mode 100644 index 0000000..defe11a --- /dev/null +++ b/src/Monq.Plugins.Abstractions/Monq.Plugins.Abstractions.csproj @@ -0,0 +1,65 @@ + + + + 2.0.0 + $(VersionSuffix) + $(Version)-$(VersionSuffix) + true + net5.0;net6.0;net7.0 + Yaroslav Lobov + MONQ Digital lab + Monq.Plugins.Abstractions + Monq.Plugins.Abstractions + MIT + true + monq monq-agent plugins library + https://github.com/MONQDL/Monq.Plugins.Abstractions + https://github.com/MONQDL/Monq.Plugins.Abstractions + MONQ agent library to create agent plugins. + true + true + snupkg + enable + enable + 10.0 + IDE1006 + + + + + false + runtime + + + false + runtime + + + + + + false + runtime + + + false + runtime + + + + + + false + runtime + + + false + runtime + + + + + + + + diff --git a/src/Monq.Plugins.Abstractions/PluginTask.cs b/src/Monq.Plugins.Abstractions/PluginTask.cs new file mode 100644 index 0000000..9fd598c --- /dev/null +++ b/src/Monq.Plugins.Abstractions/PluginTask.cs @@ -0,0 +1,36 @@ +namespace Monq.Plugins.Abstractions.Models; + +/// +/// Задание плагина. +/// +public class PluginTask +{ + /// + /// Название задания. + /// + public string Name { get; } + + /// + /// Команда задания. + /// + public string Command { get; } + + /// + /// Тип стратегии выполнения задания. + /// + public Type ProcessorStrategyType { get; } + + /// + /// Тип стратегии пост-обработки задания. + /// + public Type? PostProcessorStrategyType { get; init; } + + /// + /// Конструктор задания. + /// + /// Название задания. + /// Команда задания. + /// Тип стратегии выполнения задания. + public PluginTask(string name, string command, Type processorStrategyType) + => (Name, Command, ProcessorStrategyType) = (name, command, processorStrategyType); +} From 6651e4348dcd4c697c39aa9be3dbb791c3dcfc65 Mon Sep 17 00:00:00 2001 From: Sergey Pismennyi <8alig8@gmail.com> Date: Mon, 27 Nov 2023 16:12:59 +0300 Subject: [PATCH 2/2] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index bde322f..68b0369 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ ## Руководство к написанию плагинов агента +Основная документация по написанию плагинов в [репозиторие](https://github.com/MONQDL/agent-docs). + ### Создание проекта 1. Подключить к проекту библиотеку `Monq.Plugins.Abstractions`.