From 138b3dcfc33a78d04edcaa951df64dd667cca0ea Mon Sep 17 00:00:00 2001 From: Andrei Timar Date: Sun, 30 Mar 2025 16:03:05 +0300 Subject: [PATCH 01/14] refactor: relocate PluginRegistrationService --- .../ConfigureInMemoryPluginExtensions.cs | 14 ++++++++++ .../PluginRegistrationService.cs | 7 +++-- .../Extensions/PluginsConfigExtensions.cs | 6 ++--- .../IPluginRegistrationService.cs | 5 +--- .../PluginRegistrationServiceTests.cs | 27 ++++--------------- 5 files changed, 26 insertions(+), 33 deletions(-) create mode 100644 src/Adapters/Driven/Persistence.InMemory/Extensions/ConfigureInMemoryPluginExtensions.cs rename {ScriptBee/Plugin => src/Adapters/Driven/Persistence.InMemory}/PluginRegistrationService.cs (81%) rename {ScriptBee/Plugin => src/Application/Ports/Driven/Ports.Plugins}/IPluginRegistrationService.cs (70%) rename {ScriptBee.Tests/Plugin => test/Adapters/Driven/Persistence.InMemory.Tests}/PluginRegistrationServiceTests.cs (56%) diff --git a/src/Adapters/Driven/Persistence.InMemory/Extensions/ConfigureInMemoryPluginExtensions.cs b/src/Adapters/Driven/Persistence.InMemory/Extensions/ConfigureInMemoryPluginExtensions.cs new file mode 100644 index 00000000..170d405e --- /dev/null +++ b/src/Adapters/Driven/Persistence.InMemory/Extensions/ConfigureInMemoryPluginExtensions.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.DependencyInjection; +using ScriptBee.Ports.Plugins; + +namespace ScriptBee.Persistence.InMemory.Extensions; + +public static class ConfigureInMemoryPluginExtensions +{ + public static IServiceCollection AddInMemoryPluginServices(this IServiceCollection services) + { + return services + .AddSingleton() + .AddSingleton(); + } +} diff --git a/ScriptBee/Plugin/PluginRegistrationService.cs b/src/Adapters/Driven/Persistence.InMemory/PluginRegistrationService.cs similarity index 81% rename from ScriptBee/Plugin/PluginRegistrationService.cs rename to src/Adapters/Driven/Persistence.InMemory/PluginRegistrationService.cs index 23334059..9d9f63b7 100644 --- a/ScriptBee/Plugin/PluginRegistrationService.cs +++ b/src/Adapters/Driven/Persistence.InMemory/PluginRegistrationService.cs @@ -1,8 +1,7 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; +using System.Collections.Concurrent; +using ScriptBee.Ports.Plugins; -namespace ScriptBee.Plugin; +namespace ScriptBee.Persistence.InMemory; public class PluginRegistrationService : IPluginRegistrationService { diff --git a/src/Adapters/Driving/Analysis.Web/Extensions/PluginsConfigExtensions.cs b/src/Adapters/Driving/Analysis.Web/Extensions/PluginsConfigExtensions.cs index a506a196..b76d5306 100644 --- a/src/Adapters/Driving/Analysis.Web/Extensions/PluginsConfigExtensions.cs +++ b/src/Adapters/Driving/Analysis.Web/Extensions/PluginsConfigExtensions.cs @@ -1,5 +1,5 @@ -using ScriptBee.Persistence.InMemory; -using ScriptBee.Ports.Plugins; +using ScriptBee.Persistence.InMemory.Extensions; +using ScriptBee.Service.Plugin.Extensions; namespace ScriptBee.Analysis.Web.Extensions; @@ -7,6 +7,6 @@ public static class PluginsConfigExtensions { public static IServiceCollection AddPluginsConfig(this IServiceCollection services) { - return services.AddSingleton(); + return services.AddInMemoryPluginServices().AddPluginServices(); } } diff --git a/ScriptBee/Plugin/IPluginRegistrationService.cs b/src/Application/Ports/Driven/Ports.Plugins/IPluginRegistrationService.cs similarity index 70% rename from ScriptBee/Plugin/IPluginRegistrationService.cs rename to src/Application/Ports/Driven/Ports.Plugins/IPluginRegistrationService.cs index 9257b517..284f2903 100644 --- a/ScriptBee/Plugin/IPluginRegistrationService.cs +++ b/src/Application/Ports/Driven/Ports.Plugins/IPluginRegistrationService.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; - -namespace ScriptBee.Plugin; +namespace ScriptBee.Ports.Plugins; public interface IPluginRegistrationService { diff --git a/ScriptBee.Tests/Plugin/PluginRegistrationServiceTests.cs b/test/Adapters/Driven/Persistence.InMemory.Tests/PluginRegistrationServiceTests.cs similarity index 56% rename from ScriptBee.Tests/Plugin/PluginRegistrationServiceTests.cs rename to test/Adapters/Driven/Persistence.InMemory.Tests/PluginRegistrationServiceTests.cs index 5213bbf3..6a3d2f89 100644 --- a/ScriptBee.Tests/Plugin/PluginRegistrationServiceTests.cs +++ b/test/Adapters/Driven/Persistence.InMemory.Tests/PluginRegistrationServiceTests.cs @@ -1,27 +1,13 @@ -using System; -using System.Collections.Generic; -using ScriptBee.Plugin; -using Xunit; - -namespace ScriptBee.Tests.Plugin; +namespace ScriptBee.Persistence.InMemory.Tests; public class PluginRegistrationServiceTests { - private readonly PluginRegistrationService _pluginRegistrationService; - - public PluginRegistrationServiceTests() - { - _pluginRegistrationService = new PluginRegistrationService(); - } + private readonly PluginRegistrationService _pluginRegistrationService = new(); [Fact] public void GivenPluginKindAndAcceptedTypes_WhenAdd_ThenValuesAreRegistered() { - var acceptedTypes = new HashSet - { - typeof(string), - typeof(PluginRegistrationService) - }; + var acceptedTypes = new HashSet { typeof(string), typeof(PluginRegistrationService) }; _pluginRegistrationService.Add("kind", acceptedTypes); @@ -32,12 +18,9 @@ public void GivenPluginKindAndAcceptedTypes_WhenAdd_ThenValuesAreRegistered() [Fact] public void GivenExistingPluginKind_WhenAdd_ThenPreviousValuesIsOverriden() { - var acceptedTypes = new HashSet - { - typeof(object), - }; + var acceptedTypes = new HashSet { typeof(object) }; - _pluginRegistrationService.Add("kind", new HashSet()); + _pluginRegistrationService.Add("kind", []); _pluginRegistrationService.Add("kind", acceptedTypes); Assert.True(_pluginRegistrationService.TryGetValue("kind", out var registeredTypes)); From 4bd5524f0fa172b3fd7d30022071ba4d3114d06e Mon Sep 17 00:00:00 2001 From: Andrei Timar Date: Sun, 30 Mar 2025 16:13:00 +0300 Subject: [PATCH 02/14] feat: add plugin use case, service and tests projects --- ScriptBee.sln | 18 ++++++++++++++++++ .../Service.Plugin/Service.Plugin.csproj | 16 ++++++++++++++++ .../UseCases.Plugin/UseCases.Plugin.csproj | 10 ++++++++++ .../Service.Plugin.Tests.csproj | 10 ++++++++++ 4 files changed, 54 insertions(+) create mode 100644 src/Application/Domain/Service.Plugin/Service.Plugin.csproj create mode 100644 src/Application/Ports/Driving/UseCases.Plugin/UseCases.Plugin.csproj create mode 100644 test/Application/Domain/Service.Plugin.Tests/Service.Plugin.Tests.csproj diff --git a/ScriptBee.sln b/ScriptBee.sln index fe6d601a..248db6cd 100644 --- a/ScriptBee.sln +++ b/ScriptBee.sln @@ -65,6 +65,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rest.Tests", "test\Adapters EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Analysis.Instance.Docker.Tests", "test\Adapters\Driven\Analysis.Instance.Docker.Tests\Analysis.Instance.Docker.Tests.csproj", "{C28E26D4-8B3A-4A6C-934F-8D9E4F391158}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UseCases.Plugin", "src\Application\Ports\Driving\UseCases.Plugin\UseCases.Plugin.csproj", "{69DC6781-AD7D-4D98-BAC6-FE1332C1C8CF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Service.Plugin.Tests", "test\Application\Domain\Service.Plugin.Tests\Service.Plugin.Tests.csproj", "{77157A0A-4C28-46E1-B970-39B11F8C8DF4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Service.Plugin", "src\Application\Domain\Service.Plugin\Service.Plugin.csproj", "{23230FA8-50CC-4883-ABC3-8BA66BC92023}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -199,5 +205,17 @@ Global {C28E26D4-8B3A-4A6C-934F-8D9E4F391158}.Debug|Any CPU.Build.0 = Debug|Any CPU {C28E26D4-8B3A-4A6C-934F-8D9E4F391158}.Release|Any CPU.ActiveCfg = Release|Any CPU {C28E26D4-8B3A-4A6C-934F-8D9E4F391158}.Release|Any CPU.Build.0 = Release|Any CPU + {69DC6781-AD7D-4D98-BAC6-FE1332C1C8CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {69DC6781-AD7D-4D98-BAC6-FE1332C1C8CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {69DC6781-AD7D-4D98-BAC6-FE1332C1C8CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {69DC6781-AD7D-4D98-BAC6-FE1332C1C8CF}.Release|Any CPU.Build.0 = Release|Any CPU + {77157A0A-4C28-46E1-B970-39B11F8C8DF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77157A0A-4C28-46E1-B970-39B11F8C8DF4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77157A0A-4C28-46E1-B970-39B11F8C8DF4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77157A0A-4C28-46E1-B970-39B11F8C8DF4}.Release|Any CPU.Build.0 = Release|Any CPU + {23230FA8-50CC-4883-ABC3-8BA66BC92023}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {23230FA8-50CC-4883-ABC3-8BA66BC92023}.Debug|Any CPU.Build.0 = Debug|Any CPU + {23230FA8-50CC-4883-ABC3-8BA66BC92023}.Release|Any CPU.ActiveCfg = Release|Any CPU + {23230FA8-50CC-4883-ABC3-8BA66BC92023}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/src/Application/Domain/Service.Plugin/Service.Plugin.csproj b/src/Application/Domain/Service.Plugin/Service.Plugin.csproj new file mode 100644 index 00000000..8061f647 --- /dev/null +++ b/src/Application/Domain/Service.Plugin/Service.Plugin.csproj @@ -0,0 +1,16 @@ + + + + ScriptBee.Service.Plugin + + + + + + + + + + + + diff --git a/src/Application/Ports/Driving/UseCases.Plugin/UseCases.Plugin.csproj b/src/Application/Ports/Driving/UseCases.Plugin/UseCases.Plugin.csproj new file mode 100644 index 00000000..5fb1c46f --- /dev/null +++ b/src/Application/Ports/Driving/UseCases.Plugin/UseCases.Plugin.csproj @@ -0,0 +1,10 @@ + + + + ScriptBee.UseCases.Plugin + + + + + + diff --git a/test/Application/Domain/Service.Plugin.Tests/Service.Plugin.Tests.csproj b/test/Application/Domain/Service.Plugin.Tests/Service.Plugin.Tests.csproj new file mode 100644 index 00000000..52ac7133 --- /dev/null +++ b/test/Application/Domain/Service.Plugin.Tests/Service.Plugin.Tests.csproj @@ -0,0 +1,10 @@ + + + ScriptBee.Service.Plugin.Tests + + + + + + + From cf105b8e0430d1ab8cd434d8201bb54b8c65e08b Mon Sep 17 00:00:00 2001 From: Andrei Timar Date: Sun, 30 Mar 2025 16:13:30 +0300 Subject: [PATCH 03/14] test: relocated plugin test internal classes --- .../Plugin/Internals/TestLinkerPlugin.cs | 21 ----- .../Plugin/Internals/TestLoaderPlugin.cs | 21 ----- .../Plugin/Internals/TestPlugin.cs | 17 ---- .../Internals/TestScriptGeneratorPlugin.cs | 89 ------------------- .../Internals/TestLinkerPlugin.cs | 14 +++ .../Internals/TestLoaderPlugin.cs | 14 +++ .../Internals/TestPlugin.cs | 15 ++++ .../Internals/TestScriptGeneratorPlugin.cs | 73 +++++++++++++++ 8 files changed, 116 insertions(+), 148 deletions(-) delete mode 100644 ScriptBee.Tests/Plugin/Internals/TestLinkerPlugin.cs delete mode 100644 ScriptBee.Tests/Plugin/Internals/TestLoaderPlugin.cs delete mode 100644 ScriptBee.Tests/Plugin/Internals/TestPlugin.cs delete mode 100644 ScriptBee.Tests/Plugin/Internals/TestScriptGeneratorPlugin.cs create mode 100644 test/Application/Domain/Service.Plugin.Tests/Internals/TestLinkerPlugin.cs create mode 100644 test/Application/Domain/Service.Plugin.Tests/Internals/TestLoaderPlugin.cs create mode 100644 test/Application/Domain/Service.Plugin.Tests/Internals/TestPlugin.cs create mode 100644 test/Application/Domain/Service.Plugin.Tests/Internals/TestScriptGeneratorPlugin.cs diff --git a/ScriptBee.Tests/Plugin/Internals/TestLinkerPlugin.cs b/ScriptBee.Tests/Plugin/Internals/TestLinkerPlugin.cs deleted file mode 100644 index 5ce688d4..00000000 --- a/ScriptBee.Tests/Plugin/Internals/TestLinkerPlugin.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using DxWorks.ScriptBee.Plugin.Api; - -namespace ScriptBee.Tests.Plugin.Internals; - -internal class TestLinkerPlugin : IModelLinker -{ - public Task LinkModel(Dictionary, Dictionary> context, - Dictionary? configuration = default, CancellationToken cancellationToken = default) - { - return Task.CompletedTask; - } - - public string GetName() - { - return ""; - } -} diff --git a/ScriptBee.Tests/Plugin/Internals/TestLoaderPlugin.cs b/ScriptBee.Tests/Plugin/Internals/TestLoaderPlugin.cs deleted file mode 100644 index 798518b7..00000000 --- a/ScriptBee.Tests/Plugin/Internals/TestLoaderPlugin.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using DxWorks.ScriptBee.Plugin.Api; - -namespace ScriptBee.Tests.Plugin.Internals; - -internal class TestLoaderPlugin : IModelLoader -{ - public Task>> LoadModel(List fileStreams, - Dictionary? configuration = default, CancellationToken cancellationToken = default) - { - return Task.FromResult(new Dictionary>()); - } - - public string GetName() - { - return ""; - } -} diff --git a/ScriptBee.Tests/Plugin/Internals/TestPlugin.cs b/ScriptBee.Tests/Plugin/Internals/TestPlugin.cs deleted file mode 100644 index e926c29c..00000000 --- a/ScriptBee.Tests/Plugin/Internals/TestPlugin.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using ScriptBee.Plugin.Manifest; - -namespace ScriptBee.Tests.Plugin.Internals; - -public record TestPlugin(string Id, Version Version, string FolderPath = "path") : - Models.Plugin(FolderPath, Id, Version, - new TestPluginManifest { ExtensionPoints = new List { new TestPluginExtensionPoint() } }); - -public class TestPluginManifest : PluginManifest -{ -} - -public class TestPluginExtensionPoint : PluginExtensionPoint -{ -} diff --git a/ScriptBee.Tests/Plugin/Internals/TestScriptGeneratorPlugin.cs b/ScriptBee.Tests/Plugin/Internals/TestScriptGeneratorPlugin.cs deleted file mode 100644 index 76cc9ec0..00000000 --- a/ScriptBee.Tests/Plugin/Internals/TestScriptGeneratorPlugin.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using DxWorks.ScriptBee.Plugin.Api; - -namespace ScriptBee.Tests.Plugin.Internals; - -internal class TestScriptGeneratorPlugin : IScriptGeneratorStrategy -{ - public string Language => ""; - public string Extension => ""; - - public string ExtractValidScript(string script) - { - return script; - } - - public string GenerateClassName(Type classType) - { - return ""; - } - - public string GenerateClassName(Type classType, Type baseClassType, out HashSet baseClassGenericTypes) - { - baseClassGenericTypes = new HashSet(); - return ""; - } - - public string GenerateClassStart() - { - return ""; - } - - public string GenerateClassEnd() - { - return ""; - } - - public string GenerateField(string fieldModifier, Type fieldType, string fieldName, out HashSet genericTypes) - { - genericTypes = new HashSet(); - return ""; - } - - public string GenerateProperty(string propertyModifier, Type propertyType, string propertyName, - out HashSet genericTypes) - { - genericTypes = new HashSet(); - return ""; - } - - public string GenerateMethod(string methodModifier, Type methodType, string methodName, - List> methodParams, - out HashSet genericTypes) - { - genericTypes = new HashSet(); - return ""; - } - - public string GenerateModelDeclaration(string modelType) - { - return ""; - } - - public Task GenerateSampleCode() - { - return Task.FromResult(""); - } - - public string GenerateEmptyClass() - { - return ""; - } - - public Task GenerateImports() - { - return Task.FromResult(""); - } - - public string GetStartComment() - { - return ""; - } - - public string GetEndComment() - { - return ""; - } -} diff --git a/test/Application/Domain/Service.Plugin.Tests/Internals/TestLinkerPlugin.cs b/test/Application/Domain/Service.Plugin.Tests/Internals/TestLinkerPlugin.cs new file mode 100644 index 00000000..e6dcd075 --- /dev/null +++ b/test/Application/Domain/Service.Plugin.Tests/Internals/TestLinkerPlugin.cs @@ -0,0 +1,14 @@ +using DxWorks.ScriptBee.Plugin.Api; + +namespace ScriptBee.Service.Plugin.Tests.Internals; + +internal class TestLinkerPlugin : IModelLinker +{ + public Task LinkModel( + Dictionary, Dictionary> context, + Dictionary? configuration = null, + CancellationToken cancellationToken = default + ) => Task.CompletedTask; + + public string GetName() => ""; +} diff --git a/test/Application/Domain/Service.Plugin.Tests/Internals/TestLoaderPlugin.cs b/test/Application/Domain/Service.Plugin.Tests/Internals/TestLoaderPlugin.cs new file mode 100644 index 00000000..cc5b015a --- /dev/null +++ b/test/Application/Domain/Service.Plugin.Tests/Internals/TestLoaderPlugin.cs @@ -0,0 +1,14 @@ +using DxWorks.ScriptBee.Plugin.Api; + +namespace ScriptBee.Service.Plugin.Tests.Internals; + +internal class TestLoaderPlugin : IModelLoader +{ + public Task>> LoadModel( + List fileStreams, + Dictionary? configuration = null, + CancellationToken cancellationToken = default + ) => Task.FromResult(new Dictionary>()); + + public string GetName() => ""; +} diff --git a/test/Application/Domain/Service.Plugin.Tests/Internals/TestPlugin.cs b/test/Application/Domain/Service.Plugin.Tests/Internals/TestPlugin.cs new file mode 100644 index 00000000..a5c23efd --- /dev/null +++ b/test/Application/Domain/Service.Plugin.Tests/Internals/TestPlugin.cs @@ -0,0 +1,15 @@ +using ScriptBee.Domain.Model.Plugin.Manifest; + +namespace ScriptBee.Service.Plugin.Tests.Internals; + +public record TestPlugin(string Id, Version Version, string FolderPath = "path") + : Domain.Model.Plugin.Plugin( + FolderPath, + Id, + Version, + new TestPluginManifest { ExtensionPoints = [new TestPluginExtensionPoint()] } + ); + +public class TestPluginManifest : PluginManifest; + +public class TestPluginExtensionPoint : PluginExtensionPoint; diff --git a/test/Application/Domain/Service.Plugin.Tests/Internals/TestScriptGeneratorPlugin.cs b/test/Application/Domain/Service.Plugin.Tests/Internals/TestScriptGeneratorPlugin.cs new file mode 100644 index 00000000..a9b8ce00 --- /dev/null +++ b/test/Application/Domain/Service.Plugin.Tests/Internals/TestScriptGeneratorPlugin.cs @@ -0,0 +1,73 @@ +using DxWorks.ScriptBee.Plugin.Api; + +namespace ScriptBee.Service.Plugin.Tests.Internals; + +internal class TestScriptGeneratorPlugin : IScriptGeneratorStrategy +{ + public string Language => ""; + public string Extension => ""; + + public string ExtractValidScript(string script) => script; + + public string GenerateClassName(Type classType) => ""; + + public string GenerateClassName( + Type classType, + Type baseClassType, + out HashSet baseClassGenericTypes + ) + { + baseClassGenericTypes = []; + return ""; + } + + public string GenerateClassStart() => ""; + + public string GenerateClassEnd() => ""; + + public string GenerateField( + string fieldModifier, + Type fieldType, + string fieldName, + out HashSet genericTypes + ) + { + genericTypes = []; + return ""; + } + + public string GenerateProperty( + string propertyModifier, + Type propertyType, + string propertyName, + out HashSet genericTypes + ) + { + genericTypes = []; + return ""; + } + + public string GenerateMethod( + string methodModifier, + Type methodType, + string methodName, + List> methodParams, + out HashSet genericTypes + ) + { + genericTypes = []; + return ""; + } + + public string GenerateModelDeclaration(string modelType) => ""; + + public Task GenerateSampleCode() => Task.FromResult(""); + + public string GenerateEmptyClass() => ""; + + public Task GenerateImports() => Task.FromResult(""); + + public string GetStartComment() => ""; + + public string GetEndComment() => ""; +} From 18c5c1e074debb81ea3a5bc8d6c18054090a4d6b Mon Sep 17 00:00:00 2001 From: Andrei Timar Date: Sun, 30 Mar 2025 16:16:52 +0300 Subject: [PATCH 04/14] refactor: relocated PluginManager --- ScriptBee/Plugin/PluginManager.cs | 54 ------------- .../Domain/Service.Plugin/PluginManager.cs | 33 ++++++++ .../PluginManagerTests.cs | 75 +++++++++++++++++++ 3 files changed, 108 insertions(+), 54 deletions(-) delete mode 100644 ScriptBee/Plugin/PluginManager.cs create mode 100644 src/Application/Domain/Service.Plugin/PluginManager.cs create mode 100644 test/Application/Domain/Service.Plugin.Tests/PluginManagerTests.cs diff --git a/ScriptBee/Plugin/PluginManager.cs b/ScriptBee/Plugin/PluginManager.cs deleted file mode 100644 index d6ffa175..00000000 --- a/ScriptBee/Plugin/PluginManager.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using ScriptBee.Config; -using ScriptBee.Plugin.Installer; -using ScriptBee.ProjectContext; -using Serilog; - -namespace ScriptBee.Plugin; - -public class PluginManager -{ - private readonly IPluginReader _pluginReader; - private readonly IPluginLoader _pluginLoader; - private readonly IPluginUninstaller _pluginUninstaller; - private readonly IProjectFileStructureManager _projectFileStructureManager; - private readonly ILogger _logger; - - public PluginManager(IPluginReader pluginReader, IPluginLoader pluginLoader, IPluginUninstaller pluginUninstaller, - IProjectFileStructureManager projectFileStructureManager, ILogger logger) - { - _pluginReader = pluginReader; - _pluginLoader = pluginLoader; - _pluginUninstaller = pluginUninstaller; - _logger = logger; - _projectFileStructureManager = projectFileStructureManager; - } - - // todo move to task - public void LoadPlugins() - { - _logger.Information("Create ScriptBee folder structure"); - _projectFileStructureManager.CreateScriptBeeFolderStructure(); - - _logger.Information("Delete marked plugins"); - _pluginUninstaller.DeleteMarkedPlugins(); - - // todo filter custom plugin definitions first - // todo iterate over all plugin definitions and load them - _logger.Information("Loading plugins"); - - var plugins = _pluginReader.ReadPlugins(ConfigFolders.PathToPlugins); - - foreach (var plugin in plugins) - { - try - { - _pluginLoader.Load(plugin); - } - catch (Exception e) - { - _logger.Error(e, "Failed to load plugin {Plugin}", plugin); - } - } - } -} diff --git a/src/Application/Domain/Service.Plugin/PluginManager.cs b/src/Application/Domain/Service.Plugin/PluginManager.cs new file mode 100644 index 00000000..74c85528 --- /dev/null +++ b/src/Application/Domain/Service.Plugin/PluginManager.cs @@ -0,0 +1,33 @@ +using Microsoft.Extensions.Logging; +using ScriptBee.Domain.Model.Config; +using ScriptBee.UseCases.Plugin; + +namespace ScriptBee.Service.Plugin; + +public class PluginManager( + IPluginReader pluginReader, + IPluginLoader pluginLoader, + ILogger logger +) +{ + public void LoadPlugins() + { + // todo filter custom plugin definitions first + // todo iterate over all plugin definitions and load them + logger.LogInformation("Loading plugins"); + + var plugins = pluginReader.ReadPlugins(ConfigFolders.PathToPlugins); + + foreach (var plugin in plugins) + { + try + { + pluginLoader.Load(plugin); + } + catch (Exception e) + { + logger.LogError(e, "Failed to load plugin {Plugin}", plugin); + } + } + } +} diff --git a/test/Application/Domain/Service.Plugin.Tests/PluginManagerTests.cs b/test/Application/Domain/Service.Plugin.Tests/PluginManagerTests.cs new file mode 100644 index 00000000..13e19fcc --- /dev/null +++ b/test/Application/Domain/Service.Plugin.Tests/PluginManagerTests.cs @@ -0,0 +1,75 @@ +using Microsoft.Extensions.Logging; +using NSubstitute; +using ScriptBee.Domain.Model.Config; +using ScriptBee.Service.Plugin.Tests.Internals; +using ScriptBee.UseCases.Plugin; + +namespace ScriptBee.Service.Plugin.Tests; + +public class PluginManagerTests +{ + private readonly IPluginReader _pluginReader = Substitute.For(); + private readonly IPluginLoader _pluginLoader = Substitute.For(); + private readonly ILogger _logger = Substitute.For>(); + + private readonly PluginManager _pluginManager; + + public PluginManagerTests() + { + _pluginManager = new PluginManager(_pluginReader, _pluginLoader, _logger); + } + + [Fact] + public void GivenEmptyPlugins_WhenLoadPlugins_ThenNoPluginsLoaded() + { + _pluginReader + .ReadPlugins(ConfigFolders.PathToPlugins) + .Returns(new List()); + + _pluginManager.LoadPlugins(); + + _pluginLoader.Received(0).Load(Arg.Any()); + } + + [Fact] + public void GivenAllValidPlugins_WhenLoadPlugins_ThenAllPluginsAreLoaded() + { + _pluginReader + .ReadPlugins(ConfigFolders.PathToPlugins) + .Returns( + new List + { + new TestPlugin("id", new Version(0, 0, 0, 1)), + new TestPlugin("id", new Version(0, 0, 0, 2)), + new TestPlugin("id", new Version(0, 0, 0, 3)), + } + ); + + _pluginManager.LoadPlugins(); + + _pluginLoader.Received(3).Load(Arg.Any()); + } + + [Fact] + public void GivenSomeInvalidPlugins_WhenLoadPlugins_ThenAllValidPluginsAreLoaded() + { + var expectedException = new Exception("Test exception"); + + Domain.Model.Plugin.Plugin testPlugin1 = new TestPlugin("id", new Version(0, 0, 0, 1)); + Domain.Model.Plugin.Plugin testPlugin2 = new TestPlugin("id", new Version(0, 0, 1, 1)); + Domain.Model.Plugin.Plugin testPlugin3 = new TestPlugin("id", new Version(0, 0, 2, 1)); + + _pluginReader + .ReadPlugins(ConfigFolders.PathToPlugins) + .Returns( + new List { testPlugin1, testPlugin2, testPlugin3 } + ); + _pluginLoader.When(x => x.Load(testPlugin2)).Throw(expectedException); + + _pluginManager.LoadPlugins(); + + _logger + .Received(1) + .LogError(expectedException, "Failed to load plugin {Plugin}", testPlugin2); + } +} From 7728514cf9903ac15acf87f27495f645897ba161 Mon Sep 17 00:00:00 2001 From: Andrei Timar Date: Sun, 30 Mar 2025 16:17:08 +0300 Subject: [PATCH 05/14] refactor: relocated PluginNameGenerator --- .../Service.Plugin}/PluginNameGenerator.cs | 4 +--- .../PluginNameGeneratorTests.cs | 16 ++++++---------- 2 files changed, 7 insertions(+), 13 deletions(-) rename {ScriptBee/Plugin => src/Application/Domain/Service.Plugin}/PluginNameGenerator.cs (95%) rename {ScriptBee.Tests/Plugin => test/Application/Domain/Service.Plugin.Tests}/PluginNameGeneratorTests.cs (83%) diff --git a/ScriptBee/Plugin/PluginNameGenerator.cs b/src/Application/Domain/Service.Plugin/PluginNameGenerator.cs similarity index 95% rename from ScriptBee/Plugin/PluginNameGenerator.cs rename to src/Application/Domain/Service.Plugin/PluginNameGenerator.cs index 8029f1d1..73209e97 100644 --- a/ScriptBee/Plugin/PluginNameGenerator.cs +++ b/src/Application/Domain/Service.Plugin/PluginNameGenerator.cs @@ -1,6 +1,4 @@ -using System; - -namespace ScriptBee.Plugin; +namespace ScriptBee.Service.Plugin; public static class PluginNameGenerator { diff --git a/ScriptBee.Tests/Plugin/PluginNameGeneratorTests.cs b/test/Application/Domain/Service.Plugin.Tests/PluginNameGeneratorTests.cs similarity index 83% rename from ScriptBee.Tests/Plugin/PluginNameGeneratorTests.cs rename to test/Application/Domain/Service.Plugin.Tests/PluginNameGeneratorTests.cs index a5b9c371..70b08e10 100644 --- a/ScriptBee.Tests/Plugin/PluginNameGeneratorTests.cs +++ b/test/Application/Domain/Service.Plugin.Tests/PluginNameGeneratorTests.cs @@ -1,8 +1,4 @@ -using System; -using ScriptBee.Plugin; -using Xunit; - -namespace ScriptBee.Tests.Plugin; +namespace ScriptBee.Service.Plugin.Tests; public class PluginNameGeneratorTests { @@ -31,7 +27,9 @@ public void GivenPluginIdAndVersion_WhenGetPluginName_ThenPluginNameIsReturned() [InlineData(" @1.0.0")] [InlineData("\t@\t")] [InlineData("\t@4.2.0.1")] - public void GivenInvalidFolderNane_WhenGetPluginNameAndVersion_ThenNullIsReturned(string folderName) + public void GivenInvalidFolderNane_WhenGetPluginNameAndVersion_ThenNullIsReturned( + string folderName + ) { var (id, version) = PluginNameGenerator.GetPluginNameAndVersion(folderName); @@ -49,8 +47,7 @@ public void GivenFolderNameWithDelimiterAndVersion_WhenGetPluginNameAndVersion_T } [Fact] - public void - GivenFolderNameWithDelimiterAtTheBeginningInPluginId_WhenGetPluginNameAndVersion_ThenPluginNameAndVersionAreReturned() + public void GivenFolderNameWithDelimiterAtTheBeginningInPluginId_WhenGetPluginNameAndVersion_ThenPluginNameAndVersionAreReturned() { var (id, version) = PluginNameGenerator.GetPluginNameAndVersion("@plugin/plugin@1.0.5"); @@ -59,8 +56,7 @@ public void } [Fact] - public void - GivenFolderNameWithDelimiterInTheMiddleOfPluginId_WhenGetPluginNameAndVersion_ThenPluginNameAndVersionAreReturned() + public void GivenFolderNameWithDelimiterInTheMiddleOfPluginId_WhenGetPluginNameAndVersion_ThenPluginNameAndVersionAreReturned() { var (id, version) = PluginNameGenerator.GetPluginNameAndVersion("plugin@1.2.2@1.0.5"); From 78aaf46305d258c50264fedf6483e8fef2d6b972 Mon Sep 17 00:00:00 2001 From: Andrei Timar Date: Sun, 30 Mar 2025 16:17:33 +0300 Subject: [PATCH 06/14] refactor: relocated DllLoader --- ScriptBee/Plugin/IDllLoader.cs | 9 --------- .../Domain/Service.Plugin}/DllLoader.cs | 17 +++++++++-------- .../ConfigurePluginServiceExtension.cs | 12 ++++++++++++ .../Domain/Service.Plugin/IDllLoader.cs | 9 +++++++++ 4 files changed, 30 insertions(+), 17 deletions(-) delete mode 100644 ScriptBee/Plugin/IDllLoader.cs rename {ScriptBee/Plugin => src/Application/Domain/Service.Plugin}/DllLoader.cs (75%) create mode 100644 src/Application/Domain/Service.Plugin/Extensions/ConfigurePluginServiceExtension.cs create mode 100644 src/Application/Domain/Service.Plugin/IDllLoader.cs diff --git a/ScriptBee/Plugin/IDllLoader.cs b/ScriptBee/Plugin/IDllLoader.cs deleted file mode 100644 index acc6626b..00000000 --- a/ScriptBee/Plugin/IDllLoader.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace ScriptBee.Plugin; - -public interface IDllLoader -{ - IEnumerable<(Type @interface, Type concrete)> LoadDllTypes(string fullPathToDll, ISet acceptedPluginTypes); -} diff --git a/ScriptBee/Plugin/DllLoader.cs b/src/Application/Domain/Service.Plugin/DllLoader.cs similarity index 75% rename from ScriptBee/Plugin/DllLoader.cs rename to src/Application/Domain/Service.Plugin/DllLoader.cs index 12c76e26..9fe3b857 100644 --- a/ScriptBee/Plugin/DllLoader.cs +++ b/src/Application/Domain/Service.Plugin/DllLoader.cs @@ -1,14 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; +using System.Reflection; -namespace ScriptBee.Plugin; +namespace ScriptBee.Service.Plugin; public class DllLoader : IDllLoader { - public IEnumerable<(Type @interface, Type concrete)> LoadDllTypes(string fullPathToDll, - ISet acceptedPluginTypes) + public IEnumerable<(Type @interface, Type concrete)> LoadDllTypes( + string fullPathToDll, + ISet acceptedPluginTypes + ) { AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve; @@ -34,6 +33,8 @@ public class DllLoader : IDllLoader private static Assembly CurrentDomainOnAssemblyResolve(object? sender, ResolveEventArgs args) { - return ((AppDomain)sender).GetAssemblies().FirstOrDefault(assembly => assembly.FullName == args.Name); + return ((AppDomain)sender) + .GetAssemblies() + .FirstOrDefault(assembly => assembly.FullName == args.Name); } } diff --git a/src/Application/Domain/Service.Plugin/Extensions/ConfigurePluginServiceExtension.cs b/src/Application/Domain/Service.Plugin/Extensions/ConfigurePluginServiceExtension.cs new file mode 100644 index 00000000..2b6e85fa --- /dev/null +++ b/src/Application/Domain/Service.Plugin/Extensions/ConfigurePluginServiceExtension.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace ScriptBee.Service.Plugin.Extensions; + +public static class ConfigurePluginServiceExtension +{ + public static IServiceCollection AddPluginServices(this IServiceCollection services) + { + return services + .AddSingleton(); + } +} diff --git a/src/Application/Domain/Service.Plugin/IDllLoader.cs b/src/Application/Domain/Service.Plugin/IDllLoader.cs new file mode 100644 index 00000000..be8bba2d --- /dev/null +++ b/src/Application/Domain/Service.Plugin/IDllLoader.cs @@ -0,0 +1,9 @@ +namespace ScriptBee.Service.Plugin; + +public interface IDllLoader +{ + IEnumerable<(Type @interface, Type concrete)> LoadDllTypes( + string fullPathToDll, + ISet acceptedPluginTypes + ); +} From e554495e3a6deb4bd1ac6ff4c18cceb3ecd7bfe8 Mon Sep 17 00:00:00 2001 From: Andrei Timar Date: Sun, 30 Mar 2025 16:21:29 +0300 Subject: [PATCH 07/14] feat: call plugin manager when analysis service starts --- .../Driving/Analysis.Web/Analysis.Web.csproj | 1 + src/Adapters/Driving/Analysis.Web/Program.cs | 5 +++++ src/Adapters/Driving/Web/Program.cs | 12 +----------- .../Domain/Service.Plugin/PluginManager.cs | 2 +- .../Driving/UseCases.Plugin/IManagePluginsUseCase.cs | 6 ++++++ 5 files changed, 14 insertions(+), 12 deletions(-) create mode 100644 src/Application/Ports/Driving/UseCases.Plugin/IManagePluginsUseCase.cs diff --git a/src/Adapters/Driving/Analysis.Web/Analysis.Web.csproj b/src/Adapters/Driving/Analysis.Web/Analysis.Web.csproj index b9c7e879..10a63376 100644 --- a/src/Adapters/Driving/Analysis.Web/Analysis.Web.csproj +++ b/src/Adapters/Driving/Analysis.Web/Analysis.Web.csproj @@ -13,6 +13,7 @@ + diff --git a/src/Adapters/Driving/Analysis.Web/Program.cs b/src/Adapters/Driving/Analysis.Web/Program.cs index de1269f6..c4f6b4a7 100644 --- a/src/Adapters/Driving/Analysis.Web/Program.cs +++ b/src/Adapters/Driving/Analysis.Web/Program.cs @@ -5,6 +5,7 @@ using ScriptBee.Common.Web; using ScriptBee.Common.Web.EndpointDefinition; using ScriptBee.Common.Web.Extensions; +using ScriptBee.UseCases.Plugin; using Serilog; var builder = WebApplication.CreateBuilder(args); @@ -58,6 +59,10 @@ app.UseEndpointDefinitions(); +var pluginManager = app.Services.GetRequiredService(); + +pluginManager.LoadPlugins(); + app.Run(); public partial class Program; diff --git a/src/Adapters/Driving/Web/Program.cs b/src/Adapters/Driving/Web/Program.cs index cf906420..f32446b9 100644 --- a/src/Adapters/Driving/Web/Program.cs +++ b/src/Adapters/Driving/Web/Program.cs @@ -1,9 +1,4 @@ -// TODO: uncomment and implement when functionality is refactored -// using ScriptBee.Marketplace.Client; -// using ScriptBee.Plugin; -// using ScriptBeeWebApp.Hubs; - -using FluentValidation; +using FluentValidation; using Microsoft.AspNetCore.Server.Kestrel.Core; using ScriptBee.Common.Web; using ScriptBee.Common.Web.EndpointDefinition; @@ -78,11 +73,6 @@ app.UseEndpointDefinitions(); -// var pluginManager = app.Services.GetRequiredService(); - -// todo move to task -// pluginManager.LoadPlugins(); - app.Run(); public partial class Program; diff --git a/src/Application/Domain/Service.Plugin/PluginManager.cs b/src/Application/Domain/Service.Plugin/PluginManager.cs index 74c85528..ddc6001e 100644 --- a/src/Application/Domain/Service.Plugin/PluginManager.cs +++ b/src/Application/Domain/Service.Plugin/PluginManager.cs @@ -8,7 +8,7 @@ public class PluginManager( IPluginReader pluginReader, IPluginLoader pluginLoader, ILogger logger -) +) : IManagePluginsUseCase { public void LoadPlugins() { diff --git a/src/Application/Ports/Driving/UseCases.Plugin/IManagePluginsUseCase.cs b/src/Application/Ports/Driving/UseCases.Plugin/IManagePluginsUseCase.cs new file mode 100644 index 00000000..bf4cae6d --- /dev/null +++ b/src/Application/Ports/Driving/UseCases.Plugin/IManagePluginsUseCase.cs @@ -0,0 +1,6 @@ +namespace ScriptBee.UseCases.Plugin; + +public interface IManagePluginsUseCase +{ + public void LoadPlugins(); +} From 674a276b2fc63bfd89393c2b93cfea6294c73a8e Mon Sep 17 00:00:00 2001 From: Andrei Timar Date: Sun, 30 Mar 2025 16:29:51 +0300 Subject: [PATCH 08/14] refactor: relocated IPluginLoader and IPluginReader --- ScriptBee/Plugin/IPluginLoader.cs | 7 ------- ScriptBee/Plugin/IPluginReader.cs | 10 ---------- src/Application/Domain/Service.Plugin/PluginManager.cs | 1 + .../Ports/Driven/Ports.Plugins/IPluginLoader.cs | 8 ++++++++ .../Ports/Driven/Ports.Plugins/IPluginReader.cs | 10 ++++++++++ .../Domain/Service.Plugin.Tests/PluginManagerTests.cs | 4 ++-- 6 files changed, 21 insertions(+), 19 deletions(-) delete mode 100644 ScriptBee/Plugin/IPluginLoader.cs delete mode 100644 ScriptBee/Plugin/IPluginReader.cs create mode 100644 src/Application/Ports/Driven/Ports.Plugins/IPluginLoader.cs create mode 100644 src/Application/Ports/Driven/Ports.Plugins/IPluginReader.cs diff --git a/ScriptBee/Plugin/IPluginLoader.cs b/ScriptBee/Plugin/IPluginLoader.cs deleted file mode 100644 index b83f9827..00000000 --- a/ScriptBee/Plugin/IPluginLoader.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ScriptBee.Plugin; - -public interface IPluginLoader -{ - // todo make this return Task - void Load(Models.Plugin plugin); -} diff --git a/ScriptBee/Plugin/IPluginReader.cs b/ScriptBee/Plugin/IPluginReader.cs deleted file mode 100644 index eb9d8e5a..00000000 --- a/ScriptBee/Plugin/IPluginReader.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace ScriptBee.Plugin; - -public interface IPluginReader -{ - Models.Plugin? ReadPlugin(string pluginPath); - - IEnumerable ReadPlugins(string pluginFolderPath); -} diff --git a/src/Application/Domain/Service.Plugin/PluginManager.cs b/src/Application/Domain/Service.Plugin/PluginManager.cs index ddc6001e..f31c0612 100644 --- a/src/Application/Domain/Service.Plugin/PluginManager.cs +++ b/src/Application/Domain/Service.Plugin/PluginManager.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Logging; using ScriptBee.Domain.Model.Config; +using ScriptBee.Ports.Plugins; using ScriptBee.UseCases.Plugin; namespace ScriptBee.Service.Plugin; diff --git a/src/Application/Ports/Driven/Ports.Plugins/IPluginLoader.cs b/src/Application/Ports/Driven/Ports.Plugins/IPluginLoader.cs new file mode 100644 index 00000000..0139a842 --- /dev/null +++ b/src/Application/Ports/Driven/Ports.Plugins/IPluginLoader.cs @@ -0,0 +1,8 @@ +using ScriptBee.Domain.Model.Plugin; + +namespace ScriptBee.Ports.Plugins; + +public interface IPluginLoader +{ + void Load(Plugin plugin); +} diff --git a/src/Application/Ports/Driven/Ports.Plugins/IPluginReader.cs b/src/Application/Ports/Driven/Ports.Plugins/IPluginReader.cs new file mode 100644 index 00000000..efe5c9ae --- /dev/null +++ b/src/Application/Ports/Driven/Ports.Plugins/IPluginReader.cs @@ -0,0 +1,10 @@ +using ScriptBee.Domain.Model.Plugin; + +namespace ScriptBee.Ports.Plugins; + +public interface IPluginReader +{ + Plugin? ReadPlugin(string pluginPath); + + IEnumerable ReadPlugins(string pluginFolderPath); +} diff --git a/test/Application/Domain/Service.Plugin.Tests/PluginManagerTests.cs b/test/Application/Domain/Service.Plugin.Tests/PluginManagerTests.cs index 13e19fcc..3e162a56 100644 --- a/test/Application/Domain/Service.Plugin.Tests/PluginManagerTests.cs +++ b/test/Application/Domain/Service.Plugin.Tests/PluginManagerTests.cs @@ -1,8 +1,8 @@ using Microsoft.Extensions.Logging; using NSubstitute; using ScriptBee.Domain.Model.Config; +using ScriptBee.Ports.Plugins; using ScriptBee.Service.Plugin.Tests.Internals; -using ScriptBee.UseCases.Plugin; namespace ScriptBee.Service.Plugin.Tests; @@ -69,7 +69,7 @@ public void GivenSomeInvalidPlugins_WhenLoadPlugins_ThenAllValidPluginsAreLoaded _pluginManager.LoadPlugins(); _logger - .Received(1) + .ReceivedWithAnyArgs() .LogError(expectedException, "Failed to load plugin {Plugin}", testPlugin2); } } From 6b365c83301493d545e63e66f0952fcd2a5abd0b Mon Sep 17 00:00:00 2001 From: Andrei Timar Date: Sun, 30 Mar 2025 16:42:18 +0300 Subject: [PATCH 09/14] refactor: relocate plugin manifest yaml reader --- Directory.Packages.props | 3 +- ScriptBee.Tests/FilePathAttribute.cs | 34 ---------- .../IPluginManifestYamlFileReader.cs | 9 --- .../PluginManifestYamlFileReader.cs | 35 ---------- .../FileManagement/Yaml/ParsingEventBuffer.cs | 31 --------- .../PluginManifestSpecTypeDiscriminator.cs | 61 ----------------- .../IPluginDiscriminatorHolder.cs | 5 +- .../IPluginManifestYamlFileReader.cs | 8 +++ .../Persistence.File/Persistence.File.csproj | 5 +- .../PluginDiscriminatorHolder.cs | 8 +-- .../PluginManifestYamlFileReader.cs | 33 ++++++++++ .../Yaml/AbstractNodeNodeTypeResolver.cs | 66 ++++++++++++++----- .../Yaml/ITypeDiscriminator.cs | 4 +- .../Yaml/ParserExtensions.cs | 16 +++-- .../Yaml/ParsingEventBuffer.cs | 22 +++++++ .../PluginManifestSpecTypeDiscriminator.cs | 63 ++++++++++++++++++ .../FilePathAttribute.cs | 38 +++++++++++ .../PluginManifestYamlParsingTests.cs | 25 ++++--- .../HelperFunctionsPluginManifest.yaml | 0 .../TestData/LinkerPluginManifest.yaml | 0 .../TestData/LoaderPluginManifest.yaml | 0 .../TestData/PluginBundlePluginManifest.yaml | 0 .../ScriptGeneratorPluginManifest.yaml | 0 .../TestData/ScriptRunnerPluginManifest.yaml | 0 .../Manifest/TestData/UiPluginManifest.yaml | 0 .../Persistence.File.Tests.csproj | 6 ++ 26 files changed, 250 insertions(+), 222 deletions(-) delete mode 100644 ScriptBee.Tests/FilePathAttribute.cs delete mode 100644 ScriptBee/FileManagement/IPluginManifestYamlFileReader.cs delete mode 100644 ScriptBee/FileManagement/PluginManifestYamlFileReader.cs delete mode 100644 ScriptBee/FileManagement/Yaml/ParsingEventBuffer.cs delete mode 100644 ScriptBee/FileManagement/Yaml/PluginManifestSpecTypeDiscriminator.cs rename {ScriptBee/FileManagement => src/Adapters/Driven/Persistence.File}/IPluginDiscriminatorHolder.cs (53%) create mode 100644 src/Adapters/Driven/Persistence.File/IPluginManifestYamlFileReader.cs rename {ScriptBee/FileManagement => src/Adapters/Driven/Persistence.File}/PluginDiscriminatorHolder.cs (79%) create mode 100644 src/Adapters/Driven/Persistence.File/PluginManifestYamlFileReader.cs rename {ScriptBee/FileManagement => src/Adapters/Driven/Persistence.File}/Yaml/AbstractNodeNodeTypeResolver.cs (74%) rename {ScriptBee/FileManagement => src/Adapters/Driven/Persistence.File}/Yaml/ITypeDiscriminator.cs (71%) rename {ScriptBee/FileManagement => src/Adapters/Driven/Persistence.File}/Yaml/ParserExtensions.cs (80%) create mode 100644 src/Adapters/Driven/Persistence.File/Yaml/ParsingEventBuffer.cs create mode 100644 src/Adapters/Driven/Persistence.File/Yaml/PluginManifestSpecTypeDiscriminator.cs create mode 100644 test/Adapters/Driven/Persistence.File.Tests/FilePathAttribute.cs rename {ScriptBee.Tests/Plugin => test/Adapters/Driven/Persistence.File.Tests}/Manifest/PluginManifestYamlParsingTests.cs (92%) rename {ScriptBee.Tests/Plugin => test/Adapters/Driven/Persistence.File.Tests}/Manifest/TestData/HelperFunctionsPluginManifest.yaml (100%) rename {ScriptBee.Tests/Plugin => test/Adapters/Driven/Persistence.File.Tests}/Manifest/TestData/LinkerPluginManifest.yaml (100%) rename {ScriptBee.Tests/Plugin => test/Adapters/Driven/Persistence.File.Tests}/Manifest/TestData/LoaderPluginManifest.yaml (100%) rename {ScriptBee.Tests/Plugin => test/Adapters/Driven/Persistence.File.Tests}/Manifest/TestData/PluginBundlePluginManifest.yaml (100%) rename {ScriptBee.Tests/Plugin => test/Adapters/Driven/Persistence.File.Tests}/Manifest/TestData/ScriptGeneratorPluginManifest.yaml (100%) rename {ScriptBee.Tests/Plugin => test/Adapters/Driven/Persistence.File.Tests}/Manifest/TestData/ScriptRunnerPluginManifest.yaml (100%) rename {ScriptBee.Tests/Plugin => test/Adapters/Driven/Persistence.File.Tests}/Manifest/TestData/UiPluginManifest.yaml (100%) diff --git a/Directory.Packages.props b/Directory.Packages.props index c9141dd0..99cf5134 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -29,6 +29,7 @@ + @@ -40,4 +41,4 @@ - + \ No newline at end of file diff --git a/ScriptBee.Tests/FilePathAttribute.cs b/ScriptBee.Tests/FilePathAttribute.cs deleted file mode 100644 index a83b37ee..00000000 --- a/ScriptBee.Tests/FilePathAttribute.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Runtime.CompilerServices; -using Xunit.Sdk; - -namespace ScriptBee.Tests; - -public class FilePathAttribute : DataAttribute -{ - private readonly string _filePath; - private readonly string? _parameter; - - public FilePathAttribute(string filePath, string? parameter = null, [CallerFilePath] string testFilePath = "") - { - var testDataFolder = - Path.GetRelativePath(Directory.GetCurrentDirectory(), Directory.GetParent(testFilePath)!.FullName) - .Replace("..\\", ""); - _filePath = Path.Combine(Directory.GetCurrentDirectory(), testDataFolder, filePath); - _parameter = parameter; - } - - public override IEnumerable GetData(MethodInfo testMethod) - { - if (_parameter is null) - { - yield return new object[] { _filePath }; - } - else - { - yield return new object[] { _filePath, _parameter }; - } - } -} diff --git a/ScriptBee/FileManagement/IPluginManifestYamlFileReader.cs b/ScriptBee/FileManagement/IPluginManifestYamlFileReader.cs deleted file mode 100644 index f1c75281..00000000 --- a/ScriptBee/FileManagement/IPluginManifestYamlFileReader.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ScriptBee.Plugin.Manifest; - -namespace ScriptBee.FileManagement; - -// todo try to generify -public interface IPluginManifestYamlFileReader -{ - PluginManifest Read(string filePath); -} diff --git a/ScriptBee/FileManagement/PluginManifestYamlFileReader.cs b/ScriptBee/FileManagement/PluginManifestYamlFileReader.cs deleted file mode 100644 index f028e2b1..00000000 --- a/ScriptBee/FileManagement/PluginManifestYamlFileReader.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.IO; -using ScriptBee.FileManagement.Yaml; -using ScriptBee.Plugin.Manifest; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.NamingConventions; -using YamlDotNet.Serialization.NodeDeserializers; - -namespace ScriptBee.FileManagement; - -public class PluginManifestYamlFileReader : IPluginManifestYamlFileReader -{ - private readonly IPluginDiscriminatorHolder _pluginDiscriminatorHolder; - - public PluginManifestYamlFileReader(IPluginDiscriminatorHolder pluginDiscriminatorHolder) - { - _pluginDiscriminatorHolder = pluginDiscriminatorHolder; - } - - public PluginManifest Read(string filePath) - { - var namingConvention = CamelCaseNamingConvention.Instance; - var deserializer = new DeserializerBuilder() - .WithNamingConvention(namingConvention) - .WithNodeDeserializer( - inner => new AbstractNodeNodeTypeResolver(inner, - new PluginManifestSpecTypeDiscriminator(namingConvention, - _pluginDiscriminatorHolder.GetDiscriminatedTypes())), - s => s.InsteadOf()) - .Build(); - - using var reader = new StreamReader(filePath); - - return deserializer.Deserialize(reader); - } -} diff --git a/ScriptBee/FileManagement/Yaml/ParsingEventBuffer.cs b/ScriptBee/FileManagement/Yaml/ParsingEventBuffer.cs deleted file mode 100644 index d4440b07..00000000 --- a/ScriptBee/FileManagement/Yaml/ParsingEventBuffer.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Collections.Generic; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; - -namespace ScriptBee.FileManagement.Yaml; - -public class ParsingEventBuffer : IParser -{ - private readonly LinkedList _buffer; - - private LinkedListNode? _current; - - public ParsingEventBuffer(LinkedList events) - { - _buffer = events; - _current = events.First; - } - - public ParsingEvent? Current => _current?.Value; - - public bool MoveNext() - { - _current = _current?.Next; - return _current is not null; - } - - public void Reset() - { - _current = _buffer.First; - } -} diff --git a/ScriptBee/FileManagement/Yaml/PluginManifestSpecTypeDiscriminator.cs b/ScriptBee/FileManagement/Yaml/PluginManifestSpecTypeDiscriminator.cs deleted file mode 100644 index 1f603a99..00000000 --- a/ScriptBee/FileManagement/Yaml/PluginManifestSpecTypeDiscriminator.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using ScriptBee.Plugin.Manifest; -using YamlDotNet.Core.Events; -using YamlDotNet.Serialization; - -namespace ScriptBee.FileManagement.Yaml; - -public class PluginManifestSpecTypeDiscriminator : ITypeDiscriminator -{ - private const string TargetKey = nameof(PluginExtensionPoint.Kind); - private readonly string _targetKey; - private readonly Dictionary _typeLookup; - - public PluginManifestSpecTypeDiscriminator(INamingConvention namingConvention, Dictionary typeLookup) - { - _typeLookup = typeLookup; - _targetKey = namingConvention.Apply(TargetKey); - } - - public Type BaseType => typeof(PluginExtensionPoint); - - public bool TryResolve(ParsingEventBuffer buffer, out Type? suggestedType) - { - suggestedType = null; - if (buffer.TryFindMappingEntry(scalar => _targetKey == scalar.Value, out var key, out var value)) - { - // read the value of the kind key - if (value is Scalar valueScalar) - { - suggestedType = CheckName(valueScalar.Value); - - return true; - } - - FailEmpty(); - } - - // we could not find our key, thus we could not determine correct child type - - return false; - } - - private void FailEmpty() - { - throw new Exception($"Could not determine expectation type, {_targetKey} has an empty value"); - } - - private Type CheckName(string value) - { - if (_typeLookup.TryGetValue(value, out var childType)) - { - return childType; - } - - var enumerable = _typeLookup.Keys.Aggregate("", (s, s1) => s + "," + s1); - throw new Exception( - $"Could not match `{_targetKey}: {value} to a known expectation. Expecting one of: {enumerable}"); - } -} diff --git a/ScriptBee/FileManagement/IPluginDiscriminatorHolder.cs b/src/Adapters/Driven/Persistence.File/IPluginDiscriminatorHolder.cs similarity index 53% rename from ScriptBee/FileManagement/IPluginDiscriminatorHolder.cs rename to src/Adapters/Driven/Persistence.File/IPluginDiscriminatorHolder.cs index 3832c21b..b0493d9e 100644 --- a/ScriptBee/FileManagement/IPluginDiscriminatorHolder.cs +++ b/src/Adapters/Driven/Persistence.File/IPluginDiscriminatorHolder.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; - -namespace ScriptBee.FileManagement; +namespace ScriptBee.Persistence.File; public interface IPluginDiscriminatorHolder { diff --git a/src/Adapters/Driven/Persistence.File/IPluginManifestYamlFileReader.cs b/src/Adapters/Driven/Persistence.File/IPluginManifestYamlFileReader.cs new file mode 100644 index 00000000..ff661264 --- /dev/null +++ b/src/Adapters/Driven/Persistence.File/IPluginManifestYamlFileReader.cs @@ -0,0 +1,8 @@ +using ScriptBee.Domain.Model.Plugin.Manifest; + +namespace ScriptBee.Persistence.File; + +public interface IPluginManifestYamlFileReader +{ + PluginManifest Read(string filePath); +} diff --git a/src/Adapters/Driven/Persistence.File/Persistence.File.csproj b/src/Adapters/Driven/Persistence.File/Persistence.File.csproj index b08ee904..1b899a46 100644 --- a/src/Adapters/Driven/Persistence.File/Persistence.File.csproj +++ b/src/Adapters/Driven/Persistence.File/Persistence.File.csproj @@ -3,13 +3,16 @@ ScriptBee.Persistence.File - + + + + diff --git a/ScriptBee/FileManagement/PluginDiscriminatorHolder.cs b/src/Adapters/Driven/Persistence.File/PluginDiscriminatorHolder.cs similarity index 79% rename from ScriptBee/FileManagement/PluginDiscriminatorHolder.cs rename to src/Adapters/Driven/Persistence.File/PluginDiscriminatorHolder.cs index dd13a600..00cec5b7 100644 --- a/ScriptBee/FileManagement/PluginDiscriminatorHolder.cs +++ b/src/Adapters/Driven/Persistence.File/PluginDiscriminatorHolder.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; -using ScriptBee.Plugin.Manifest; +using ScriptBee.Domain.Model.Plugin.Manifest; -namespace ScriptBee.FileManagement; +namespace ScriptBee.Persistence.File; // todo make it more extensible public class PluginDiscriminatorHolder : IPluginDiscriminatorHolder @@ -17,7 +15,7 @@ public Dictionary GetDiscriminatedTypes() { PluginKind.HelperFunctions, typeof(HelperFunctionsPluginExtensionPoint) }, { PluginKind.Loader, typeof(LoaderPluginExtensionPoint) }, { PluginKind.Linker, typeof(LinkerPluginExtensionPoint) }, - { PluginKind.Ui, typeof(UiPluginExtensionPoint) } + { PluginKind.Ui, typeof(UiPluginExtensionPoint) }, }; } } diff --git a/src/Adapters/Driven/Persistence.File/PluginManifestYamlFileReader.cs b/src/Adapters/Driven/Persistence.File/PluginManifestYamlFileReader.cs new file mode 100644 index 00000000..4fe1ec4b --- /dev/null +++ b/src/Adapters/Driven/Persistence.File/PluginManifestYamlFileReader.cs @@ -0,0 +1,33 @@ +using ScriptBee.Domain.Model.Plugin.Manifest; +using ScriptBee.Persistence.File.Yaml; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; +using YamlDotNet.Serialization.NodeDeserializers; + +namespace ScriptBee.Persistence.File; + +public class PluginManifestYamlFileReader(IPluginDiscriminatorHolder pluginDiscriminatorHolder) + : IPluginManifestYamlFileReader +{ + public PluginManifest Read(string filePath) + { + var namingConvention = CamelCaseNamingConvention.Instance; + var deserializer = new DeserializerBuilder() + .WithNamingConvention(namingConvention) + .WithNodeDeserializer( + inner => new AbstractNodeNodeTypeResolver( + inner, + new PluginManifestSpecTypeDiscriminator( + namingConvention, + pluginDiscriminatorHolder.GetDiscriminatedTypes() + ) + ), + s => s.InsteadOf() + ) + .Build(); + + using var reader = new StreamReader(filePath); + + return deserializer.Deserialize(reader); + } +} diff --git a/ScriptBee/FileManagement/Yaml/AbstractNodeNodeTypeResolver.cs b/src/Adapters/Driven/Persistence.File/Yaml/AbstractNodeNodeTypeResolver.cs similarity index 74% rename from ScriptBee/FileManagement/Yaml/AbstractNodeNodeTypeResolver.cs rename to src/Adapters/Driven/Persistence.File/Yaml/AbstractNodeNodeTypeResolver.cs index 54d89571..e01606f0 100644 --- a/ScriptBee/FileManagement/Yaml/AbstractNodeNodeTypeResolver.cs +++ b/src/Adapters/Driven/Persistence.File/Yaml/AbstractNodeNodeTypeResolver.cs @@ -1,32 +1,38 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using YamlDotNet.Core; +using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NodeDeserializers; -namespace ScriptBee.FileManagement.Yaml; +namespace ScriptBee.Persistence.File.Yaml; public class AbstractNodeNodeTypeResolver : INodeDeserializer { private readonly INodeDeserializer _original; private readonly ITypeDiscriminator[] _typeDiscriminators; - public AbstractNodeNodeTypeResolver(INodeDeserializer original, params ITypeDiscriminator[] discriminators) + public AbstractNodeNodeTypeResolver( + INodeDeserializer original, + params ITypeDiscriminator[] discriminators + ) { if (original is not ObjectNodeDeserializer) { throw new ArgumentException( - $"{nameof(AbstractNodeNodeTypeResolver)} requires the original resolver to be a {nameof(ObjectNodeDeserializer)}"); + $"{nameof(AbstractNodeNodeTypeResolver)} requires the original resolver to be a {nameof(ObjectNodeDeserializer)}" + ); } _original = original; _typeDiscriminators = discriminators; } - public bool Deserialize(IParser reader, Type expectedType, Func nestedObjectDeserializer, - out object? value) + public bool Deserialize( + IParser reader, + Type expectedType, + Func nestedObjectDeserializer, + out object? value, + ObjectDeserializer rootDeserializer + ) { // we're essentially "in front of" the normal ObjectNodeDeserializer. // We could let it check if the current event is a mapping, but we also need to know. @@ -41,7 +47,13 @@ public bool Deserialize(IParser reader, Type expectedType, Func supportedTypes, - ParsingEventBuffer buffer) + private static Type CheckWithDiscriminators( + Type expectedType, + IEnumerable supportedTypes, + ParsingEventBuffer buffer + ) { foreach (var discriminator in supportedTypes) { @@ -83,7 +109,8 @@ private static Type CheckWithDiscriminators(Type expectedType, IEnumerable ReadNestedMapping(IParser reader) @@ -106,19 +133,22 @@ private static void CheckReturnedType(Type baseType, Type? candidateType) if (candidateType is null) { throw new NullReferenceException( - $"The type resolver for AbstractNodeNodeTypeResolver returned null. It must return a valid sub-type of {baseType}."); + $"The type resolver for AbstractNodeNodeTypeResolver returned null. It must return a valid sub-type of {baseType}." + ); } if (candidateType == baseType) { throw new InvalidOperationException( - $"The type resolver for AbstractNodeNodeTypeResolver returned the abstract type. It must return a valid sub-type of {baseType}."); + $"The type resolver for AbstractNodeNodeTypeResolver returned the abstract type. It must return a valid sub-type of {baseType}." + ); } if (!baseType.IsAssignableFrom(candidateType)) { throw new InvalidOperationException( - $"The type resolver for AbstractNodeNodeTypeResolver returned a type ({candidateType}) that is not a valid sub type of {baseType}"); + $"The type resolver for AbstractNodeNodeTypeResolver returned a type ({candidateType}) that is not a valid sub type of {baseType}" + ); } } } diff --git a/ScriptBee/FileManagement/Yaml/ITypeDiscriminator.cs b/src/Adapters/Driven/Persistence.File/Yaml/ITypeDiscriminator.cs similarity index 71% rename from ScriptBee/FileManagement/Yaml/ITypeDiscriminator.cs rename to src/Adapters/Driven/Persistence.File/Yaml/ITypeDiscriminator.cs index 4e95cd06..2d25bd69 100644 --- a/ScriptBee/FileManagement/Yaml/ITypeDiscriminator.cs +++ b/src/Adapters/Driven/Persistence.File/Yaml/ITypeDiscriminator.cs @@ -1,6 +1,4 @@ -using System; - -namespace ScriptBee.FileManagement.Yaml; +namespace ScriptBee.Persistence.File.Yaml; public interface ITypeDiscriminator { diff --git a/ScriptBee/FileManagement/Yaml/ParserExtensions.cs b/src/Adapters/Driven/Persistence.File/Yaml/ParserExtensions.cs similarity index 80% rename from ScriptBee/FileManagement/Yaml/ParserExtensions.cs rename to src/Adapters/Driven/Persistence.File/Yaml/ParserExtensions.cs index 97a8cf7b..1cc1198c 100644 --- a/ScriptBee/FileManagement/Yaml/ParserExtensions.cs +++ b/src/Adapters/Driven/Persistence.File/Yaml/ParserExtensions.cs @@ -1,13 +1,16 @@ -using System; -using YamlDotNet.Core; +using YamlDotNet.Core; using YamlDotNet.Core.Events; -namespace ScriptBee.FileManagement.Yaml; +namespace ScriptBee.Persistence.File.Yaml; public static class ParserExtensions { - public static bool TryFindMappingEntry(this ParsingEventBuffer parser, Func selector, out Scalar? key, - out ParsingEvent? value) + public static bool TryFindMappingEntry( + this ParsingEventBuffer parser, + Func selector, + out Scalar? key, + out ParsingEvent? value + ) { parser.Consume(); do @@ -36,7 +39,8 @@ public static bool TryFindMappingEntry(this ParsingEventBuffer parser, Func events) : IParser +{ + private LinkedListNode? _current = events.First; + + public ParsingEvent? Current => _current?.Value; + + public bool MoveNext() + { + _current = _current?.Next; + return _current is not null; + } + + public void Reset() + { + _current = events.First; + } +} diff --git a/src/Adapters/Driven/Persistence.File/Yaml/PluginManifestSpecTypeDiscriminator.cs b/src/Adapters/Driven/Persistence.File/Yaml/PluginManifestSpecTypeDiscriminator.cs new file mode 100644 index 00000000..27731127 --- /dev/null +++ b/src/Adapters/Driven/Persistence.File/Yaml/PluginManifestSpecTypeDiscriminator.cs @@ -0,0 +1,63 @@ +using ScriptBee.Domain.Model.Plugin.Manifest; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace ScriptBee.Persistence.File.Yaml; + +public class PluginManifestSpecTypeDiscriminator( + INamingConvention namingConvention, + Dictionary typeLookup +) : ITypeDiscriminator +{ + private const string TargetKey = nameof(PluginExtensionPoint.Kind); + private readonly string _targetKey = namingConvention.Apply(TargetKey); + + public Type BaseType => typeof(PluginExtensionPoint); + + public bool TryResolve(ParsingEventBuffer buffer, out Type? suggestedType) + { + suggestedType = null; + if ( + buffer.TryFindMappingEntry( + scalar => _targetKey == scalar.Value, + out var key, + out var value + ) + ) + { + // read the value of the kind key + if (value is Scalar valueScalar) + { + suggestedType = CheckName(valueScalar.Value); + + return true; + } + + FailEmpty(); + } + + // we could not find our key, thus we could not determine correct child type + + return false; + } + + private void FailEmpty() + { + throw new Exception( + $"Could not determine expectation type, {_targetKey} has an empty value" + ); + } + + private Type CheckName(string value) + { + if (typeLookup.TryGetValue(value, out var childType)) + { + return childType; + } + + var enumerable = typeLookup.Keys.Aggregate("", (s, s1) => s + "," + s1); + throw new Exception( + $"Could not match `{_targetKey}: {value} to a known expectation. Expecting one of: {enumerable}" + ); + } +} diff --git a/test/Adapters/Driven/Persistence.File.Tests/FilePathAttribute.cs b/test/Adapters/Driven/Persistence.File.Tests/FilePathAttribute.cs new file mode 100644 index 00000000..1ae6898e --- /dev/null +++ b/test/Adapters/Driven/Persistence.File.Tests/FilePathAttribute.cs @@ -0,0 +1,38 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using Xunit.Sdk; + +namespace ScriptBee.Persistence.File.Tests; + +public class FilePathAttribute : DataAttribute +{ + private readonly string _filePath; + private readonly string? _parameter; + + public FilePathAttribute( + string filePath, + string? parameter = null, + [CallerFilePath] string testFilePath = "" + ) + { + var testDataFolder = Path.GetRelativePath( + Directory.GetCurrentDirectory(), + Directory.GetParent(testFilePath)!.FullName + ) + .Replace("..\\", ""); + _filePath = Path.Combine(Directory.GetCurrentDirectory(), testDataFolder, filePath); + _parameter = parameter; + } + + public override IEnumerable GetData(MethodInfo testMethod) + { + if (_parameter is null) + { + yield return [_filePath]; + } + else + { + yield return [_filePath, _parameter]; + } + } +} diff --git a/ScriptBee.Tests/Plugin/Manifest/PluginManifestYamlParsingTests.cs b/test/Adapters/Driven/Persistence.File.Tests/Manifest/PluginManifestYamlParsingTests.cs similarity index 92% rename from ScriptBee.Tests/Plugin/Manifest/PluginManifestYamlParsingTests.cs rename to test/Adapters/Driven/Persistence.File.Tests/Manifest/PluginManifestYamlParsingTests.cs index cff6e902..c5ff5124 100644 --- a/ScriptBee.Tests/Plugin/Manifest/PluginManifestYamlParsingTests.cs +++ b/test/Adapters/Driven/Persistence.File.Tests/Manifest/PluginManifestYamlParsingTests.cs @@ -1,18 +1,12 @@ -using System.Linq; -using ScriptBee.FileManagement; -using ScriptBee.Plugin.Manifest; -using Xunit; +using ScriptBee.Domain.Model.Plugin.Manifest; -namespace ScriptBee.Tests.Plugin.Manifest; +namespace ScriptBee.Persistence.File.Tests.Manifest; public class PluginManifestYamlParsingTests { - private readonly PluginManifestYamlFileReader _yamlFileReader; - - public PluginManifestYamlParsingTests() - { - _yamlFileReader = new PluginManifestYamlFileReader(new PluginDiscriminatorHolder()); - } + private readonly PluginManifestYamlFileReader _yamlFileReader = new( + new PluginDiscriminatorHolder() + ); [Theory] [FilePath("TestData/ScriptGeneratorPluginManifest.yaml")] @@ -25,7 +19,8 @@ public void GivenManifestContent_ThenScriptGeneratorPluginIsConstructed(string f Assert.Equal("ScriptBee", pluginManifest.Author); Assert.Equal("Description", pluginManifest.Description); - var extensionPoint = (ScriptGeneratorPluginExtensionPoint)pluginManifest.ExtensionPoints.Single(); + var extensionPoint = (ScriptGeneratorPluginExtensionPoint) + pluginManifest.ExtensionPoints.Single(); Assert.Equal("ScriptGenerator", extensionPoint.Kind); Assert.Equal("Plugin.dll", extensionPoint.EntryPoint); @@ -45,7 +40,8 @@ public void GivenManifestContent_ThenScriptRunnerPluginManifestIsConstructed(str Assert.Equal("ScriptBee", pluginManifest.Author); Assert.Equal("Description", pluginManifest.Description); - var extensionPoint = (ScriptRunnerPluginExtensionPoint)pluginManifest.ExtensionPoints.Single(); + var extensionPoint = (ScriptRunnerPluginExtensionPoint) + pluginManifest.ExtensionPoints.Single(); Assert.Equal("ScriptRunner", extensionPoint.Kind); Assert.Equal("Plugin.dll", extensionPoint.EntryPoint); @@ -64,7 +60,8 @@ public void GivenManifestContent_ThenHelperFunctionsPluginManifestIsConstructed( Assert.Equal("ScriptBee", pluginManifest.Author); Assert.Equal("Description", pluginManifest.Description); - var extensionPoint = (HelperFunctionsPluginExtensionPoint)pluginManifest.ExtensionPoints.Single(); + var extensionPoint = (HelperFunctionsPluginExtensionPoint) + pluginManifest.ExtensionPoints.Single(); Assert.Equal("HelperFunctions", extensionPoint.Kind); Assert.Equal("Plugin.dll", extensionPoint.EntryPoint); diff --git a/ScriptBee.Tests/Plugin/Manifest/TestData/HelperFunctionsPluginManifest.yaml b/test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/HelperFunctionsPluginManifest.yaml similarity index 100% rename from ScriptBee.Tests/Plugin/Manifest/TestData/HelperFunctionsPluginManifest.yaml rename to test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/HelperFunctionsPluginManifest.yaml diff --git a/ScriptBee.Tests/Plugin/Manifest/TestData/LinkerPluginManifest.yaml b/test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/LinkerPluginManifest.yaml similarity index 100% rename from ScriptBee.Tests/Plugin/Manifest/TestData/LinkerPluginManifest.yaml rename to test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/LinkerPluginManifest.yaml diff --git a/ScriptBee.Tests/Plugin/Manifest/TestData/LoaderPluginManifest.yaml b/test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/LoaderPluginManifest.yaml similarity index 100% rename from ScriptBee.Tests/Plugin/Manifest/TestData/LoaderPluginManifest.yaml rename to test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/LoaderPluginManifest.yaml diff --git a/ScriptBee.Tests/Plugin/Manifest/TestData/PluginBundlePluginManifest.yaml b/test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/PluginBundlePluginManifest.yaml similarity index 100% rename from ScriptBee.Tests/Plugin/Manifest/TestData/PluginBundlePluginManifest.yaml rename to test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/PluginBundlePluginManifest.yaml diff --git a/ScriptBee.Tests/Plugin/Manifest/TestData/ScriptGeneratorPluginManifest.yaml b/test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/ScriptGeneratorPluginManifest.yaml similarity index 100% rename from ScriptBee.Tests/Plugin/Manifest/TestData/ScriptGeneratorPluginManifest.yaml rename to test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/ScriptGeneratorPluginManifest.yaml diff --git a/ScriptBee.Tests/Plugin/Manifest/TestData/ScriptRunnerPluginManifest.yaml b/test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/ScriptRunnerPluginManifest.yaml similarity index 100% rename from ScriptBee.Tests/Plugin/Manifest/TestData/ScriptRunnerPluginManifest.yaml rename to test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/ScriptRunnerPluginManifest.yaml diff --git a/ScriptBee.Tests/Plugin/Manifest/TestData/UiPluginManifest.yaml b/test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/UiPluginManifest.yaml similarity index 100% rename from ScriptBee.Tests/Plugin/Manifest/TestData/UiPluginManifest.yaml rename to test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/UiPluginManifest.yaml diff --git a/test/Adapters/Driven/Persistence.File.Tests/Persistence.File.Tests.csproj b/test/Adapters/Driven/Persistence.File.Tests/Persistence.File.Tests.csproj index 946af25f..c46a8f49 100644 --- a/test/Adapters/Driven/Persistence.File.Tests/Persistence.File.Tests.csproj +++ b/test/Adapters/Driven/Persistence.File.Tests/Persistence.File.Tests.csproj @@ -9,4 +9,10 @@ + + + + PreserveNewest + + From 165fbde0ec97559d9330ec24ae6bd357ca8767d5 Mon Sep 17 00:00:00 2001 From: Andrei Timar Date: Sun, 30 Mar 2025 16:43:01 +0300 Subject: [PATCH 10/14] refactor: relocated PluginNameGenerator and tests --- .../Driven/Persistence.File}/PluginNameGenerator.cs | 2 +- .../Driven/Persistence.File.Tests}/PluginNameGeneratorTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/{Application/Domain/Service.Plugin => Adapters/Driven/Persistence.File}/PluginNameGenerator.cs (95%) rename test/{Application/Domain/Service.Plugin.Tests => Adapters/Driven/Persistence.File.Tests}/PluginNameGeneratorTests.cs (98%) diff --git a/src/Application/Domain/Service.Plugin/PluginNameGenerator.cs b/src/Adapters/Driven/Persistence.File/PluginNameGenerator.cs similarity index 95% rename from src/Application/Domain/Service.Plugin/PluginNameGenerator.cs rename to src/Adapters/Driven/Persistence.File/PluginNameGenerator.cs index 73209e97..65c75409 100644 --- a/src/Application/Domain/Service.Plugin/PluginNameGenerator.cs +++ b/src/Adapters/Driven/Persistence.File/PluginNameGenerator.cs @@ -1,4 +1,4 @@ -namespace ScriptBee.Service.Plugin; +namespace ScriptBee.Persistence.File; public static class PluginNameGenerator { diff --git a/test/Application/Domain/Service.Plugin.Tests/PluginNameGeneratorTests.cs b/test/Adapters/Driven/Persistence.File.Tests/PluginNameGeneratorTests.cs similarity index 98% rename from test/Application/Domain/Service.Plugin.Tests/PluginNameGeneratorTests.cs rename to test/Adapters/Driven/Persistence.File.Tests/PluginNameGeneratorTests.cs index 70b08e10..ed13dd4c 100644 --- a/test/Application/Domain/Service.Plugin.Tests/PluginNameGeneratorTests.cs +++ b/test/Adapters/Driven/Persistence.File.Tests/PluginNameGeneratorTests.cs @@ -1,4 +1,4 @@ -namespace ScriptBee.Service.Plugin.Tests; +namespace ScriptBee.Persistence.File.Tests; public class PluginNameGeneratorTests { From 5dd427b1cf4a4b7bf07ab56b01614cbe9ef051b1 Mon Sep 17 00:00:00 2001 From: Andrei Timar Date: Sun, 30 Mar 2025 16:45:43 +0300 Subject: [PATCH 11/14] refactor: grouped Persistence file adapter for plugins in separate folder --- .../{ => Plugin}/IPluginDiscriminatorHolder.cs | 2 +- .../{ => Plugin}/IPluginManifestYamlFileReader.cs | 2 +- .../{ => Plugin}/PluginDiscriminatorHolder.cs | 2 +- .../{ => Plugin}/PluginManifestYamlFileReader.cs | 4 ++-- .../Persistence.File/{ => Plugin}/PluginNameGenerator.cs | 2 +- .../{ => Plugin}/Yaml/AbstractNodeNodeTypeResolver.cs | 2 +- .../Persistence.File/{ => Plugin}/Yaml/ITypeDiscriminator.cs | 2 +- .../Persistence.File/{ => Plugin}/Yaml/ParserExtensions.cs | 2 +- .../Persistence.File/{ => Plugin}/Yaml/ParsingEventBuffer.cs | 2 +- .../{ => Plugin}/Yaml/PluginManifestSpecTypeDiscriminator.cs | 2 +- .../Extensions/ConfigurePluginServiceExtension.cs | 3 +-- .../Persistence.File.Tests/Persistence.File.Tests.csproj | 2 +- .../{ => Plugin}/Manifest/PluginManifestYamlParsingTests.cs | 1 + .../Manifest/TestData/HelperFunctionsPluginManifest.yaml | 0 .../{ => Plugin}/Manifest/TestData/LinkerPluginManifest.yaml | 0 .../{ => Plugin}/Manifest/TestData/LoaderPluginManifest.yaml | 0 .../Manifest/TestData/PluginBundlePluginManifest.yaml | 0 .../Manifest/TestData/ScriptGeneratorPluginManifest.yaml | 0 .../Manifest/TestData/ScriptRunnerPluginManifest.yaml | 0 .../{ => Plugin}/Manifest/TestData/UiPluginManifest.yaml | 0 .../{ => Plugin}/PluginNameGeneratorTests.cs | 4 +++- 21 files changed, 17 insertions(+), 15 deletions(-) rename src/Adapters/Driven/Persistence.File/{ => Plugin}/IPluginDiscriminatorHolder.cs (68%) rename src/Adapters/Driven/Persistence.File/{ => Plugin}/IPluginManifestYamlFileReader.cs (76%) rename src/Adapters/Driven/Persistence.File/{ => Plugin}/PluginDiscriminatorHolder.cs (94%) rename src/Adapters/Driven/Persistence.File/{ => Plugin}/PluginManifestYamlFileReader.cs (92%) rename src/Adapters/Driven/Persistence.File/{ => Plugin}/PluginNameGenerator.cs (95%) rename src/Adapters/Driven/Persistence.File/{ => Plugin}/Yaml/AbstractNodeNodeTypeResolver.cs (99%) rename src/Adapters/Driven/Persistence.File/{ => Plugin}/Yaml/ITypeDiscriminator.cs (73%) rename src/Adapters/Driven/Persistence.File/{ => Plugin}/Yaml/ParserExtensions.cs (97%) rename src/Adapters/Driven/Persistence.File/{ => Plugin}/Yaml/ParsingEventBuffer.cs (89%) rename src/Adapters/Driven/Persistence.File/{ => Plugin}/Yaml/PluginManifestSpecTypeDiscriminator.cs (97%) rename test/Adapters/Driven/Persistence.File.Tests/{ => Plugin}/Manifest/PluginManifestYamlParsingTests.cs (99%) rename test/Adapters/Driven/Persistence.File.Tests/{ => Plugin}/Manifest/TestData/HelperFunctionsPluginManifest.yaml (100%) rename test/Adapters/Driven/Persistence.File.Tests/{ => Plugin}/Manifest/TestData/LinkerPluginManifest.yaml (100%) rename test/Adapters/Driven/Persistence.File.Tests/{ => Plugin}/Manifest/TestData/LoaderPluginManifest.yaml (100%) rename test/Adapters/Driven/Persistence.File.Tests/{ => Plugin}/Manifest/TestData/PluginBundlePluginManifest.yaml (100%) rename test/Adapters/Driven/Persistence.File.Tests/{ => Plugin}/Manifest/TestData/ScriptGeneratorPluginManifest.yaml (100%) rename test/Adapters/Driven/Persistence.File.Tests/{ => Plugin}/Manifest/TestData/ScriptRunnerPluginManifest.yaml (100%) rename test/Adapters/Driven/Persistence.File.Tests/{ => Plugin}/Manifest/TestData/UiPluginManifest.yaml (100%) rename test/Adapters/Driven/Persistence.File.Tests/{ => Plugin}/PluginNameGeneratorTests.cs (96%) diff --git a/src/Adapters/Driven/Persistence.File/IPluginDiscriminatorHolder.cs b/src/Adapters/Driven/Persistence.File/Plugin/IPluginDiscriminatorHolder.cs similarity index 68% rename from src/Adapters/Driven/Persistence.File/IPluginDiscriminatorHolder.cs rename to src/Adapters/Driven/Persistence.File/Plugin/IPluginDiscriminatorHolder.cs index b0493d9e..4be61c6b 100644 --- a/src/Adapters/Driven/Persistence.File/IPluginDiscriminatorHolder.cs +++ b/src/Adapters/Driven/Persistence.File/Plugin/IPluginDiscriminatorHolder.cs @@ -1,4 +1,4 @@ -namespace ScriptBee.Persistence.File; +namespace ScriptBee.Persistence.File.Plugin; public interface IPluginDiscriminatorHolder { diff --git a/src/Adapters/Driven/Persistence.File/IPluginManifestYamlFileReader.cs b/src/Adapters/Driven/Persistence.File/Plugin/IPluginManifestYamlFileReader.cs similarity index 76% rename from src/Adapters/Driven/Persistence.File/IPluginManifestYamlFileReader.cs rename to src/Adapters/Driven/Persistence.File/Plugin/IPluginManifestYamlFileReader.cs index ff661264..aa0fe309 100644 --- a/src/Adapters/Driven/Persistence.File/IPluginManifestYamlFileReader.cs +++ b/src/Adapters/Driven/Persistence.File/Plugin/IPluginManifestYamlFileReader.cs @@ -1,6 +1,6 @@ using ScriptBee.Domain.Model.Plugin.Manifest; -namespace ScriptBee.Persistence.File; +namespace ScriptBee.Persistence.File.Plugin; public interface IPluginManifestYamlFileReader { diff --git a/src/Adapters/Driven/Persistence.File/PluginDiscriminatorHolder.cs b/src/Adapters/Driven/Persistence.File/Plugin/PluginDiscriminatorHolder.cs similarity index 94% rename from src/Adapters/Driven/Persistence.File/PluginDiscriminatorHolder.cs rename to src/Adapters/Driven/Persistence.File/Plugin/PluginDiscriminatorHolder.cs index 00cec5b7..df8d253b 100644 --- a/src/Adapters/Driven/Persistence.File/PluginDiscriminatorHolder.cs +++ b/src/Adapters/Driven/Persistence.File/Plugin/PluginDiscriminatorHolder.cs @@ -1,6 +1,6 @@ using ScriptBee.Domain.Model.Plugin.Manifest; -namespace ScriptBee.Persistence.File; +namespace ScriptBee.Persistence.File.Plugin; // todo make it more extensible public class PluginDiscriminatorHolder : IPluginDiscriminatorHolder diff --git a/src/Adapters/Driven/Persistence.File/PluginManifestYamlFileReader.cs b/src/Adapters/Driven/Persistence.File/Plugin/PluginManifestYamlFileReader.cs similarity index 92% rename from src/Adapters/Driven/Persistence.File/PluginManifestYamlFileReader.cs rename to src/Adapters/Driven/Persistence.File/Plugin/PluginManifestYamlFileReader.cs index 4fe1ec4b..e6c56744 100644 --- a/src/Adapters/Driven/Persistence.File/PluginManifestYamlFileReader.cs +++ b/src/Adapters/Driven/Persistence.File/Plugin/PluginManifestYamlFileReader.cs @@ -1,10 +1,10 @@ using ScriptBee.Domain.Model.Plugin.Manifest; -using ScriptBee.Persistence.File.Yaml; +using ScriptBee.Persistence.File.Plugin.Yaml; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NodeDeserializers; -namespace ScriptBee.Persistence.File; +namespace ScriptBee.Persistence.File.Plugin; public class PluginManifestYamlFileReader(IPluginDiscriminatorHolder pluginDiscriminatorHolder) : IPluginManifestYamlFileReader diff --git a/src/Adapters/Driven/Persistence.File/PluginNameGenerator.cs b/src/Adapters/Driven/Persistence.File/Plugin/PluginNameGenerator.cs similarity index 95% rename from src/Adapters/Driven/Persistence.File/PluginNameGenerator.cs rename to src/Adapters/Driven/Persistence.File/Plugin/PluginNameGenerator.cs index 65c75409..43231134 100644 --- a/src/Adapters/Driven/Persistence.File/PluginNameGenerator.cs +++ b/src/Adapters/Driven/Persistence.File/Plugin/PluginNameGenerator.cs @@ -1,4 +1,4 @@ -namespace ScriptBee.Persistence.File; +namespace ScriptBee.Persistence.File.Plugin; public static class PluginNameGenerator { diff --git a/src/Adapters/Driven/Persistence.File/Yaml/AbstractNodeNodeTypeResolver.cs b/src/Adapters/Driven/Persistence.File/Plugin/Yaml/AbstractNodeNodeTypeResolver.cs similarity index 99% rename from src/Adapters/Driven/Persistence.File/Yaml/AbstractNodeNodeTypeResolver.cs rename to src/Adapters/Driven/Persistence.File/Plugin/Yaml/AbstractNodeNodeTypeResolver.cs index e01606f0..392f847c 100644 --- a/src/Adapters/Driven/Persistence.File/Yaml/AbstractNodeNodeTypeResolver.cs +++ b/src/Adapters/Driven/Persistence.File/Plugin/Yaml/AbstractNodeNodeTypeResolver.cs @@ -3,7 +3,7 @@ using YamlDotNet.Serialization; using YamlDotNet.Serialization.NodeDeserializers; -namespace ScriptBee.Persistence.File.Yaml; +namespace ScriptBee.Persistence.File.Plugin.Yaml; public class AbstractNodeNodeTypeResolver : INodeDeserializer { diff --git a/src/Adapters/Driven/Persistence.File/Yaml/ITypeDiscriminator.cs b/src/Adapters/Driven/Persistence.File/Plugin/Yaml/ITypeDiscriminator.cs similarity index 73% rename from src/Adapters/Driven/Persistence.File/Yaml/ITypeDiscriminator.cs rename to src/Adapters/Driven/Persistence.File/Plugin/Yaml/ITypeDiscriminator.cs index 2d25bd69..0822bd15 100644 --- a/src/Adapters/Driven/Persistence.File/Yaml/ITypeDiscriminator.cs +++ b/src/Adapters/Driven/Persistence.File/Plugin/Yaml/ITypeDiscriminator.cs @@ -1,4 +1,4 @@ -namespace ScriptBee.Persistence.File.Yaml; +namespace ScriptBee.Persistence.File.Plugin.Yaml; public interface ITypeDiscriminator { diff --git a/src/Adapters/Driven/Persistence.File/Yaml/ParserExtensions.cs b/src/Adapters/Driven/Persistence.File/Plugin/Yaml/ParserExtensions.cs similarity index 97% rename from src/Adapters/Driven/Persistence.File/Yaml/ParserExtensions.cs rename to src/Adapters/Driven/Persistence.File/Plugin/Yaml/ParserExtensions.cs index 1cc1198c..b64c8306 100644 --- a/src/Adapters/Driven/Persistence.File/Yaml/ParserExtensions.cs +++ b/src/Adapters/Driven/Persistence.File/Plugin/Yaml/ParserExtensions.cs @@ -1,7 +1,7 @@ using YamlDotNet.Core; using YamlDotNet.Core.Events; -namespace ScriptBee.Persistence.File.Yaml; +namespace ScriptBee.Persistence.File.Plugin.Yaml; public static class ParserExtensions { diff --git a/src/Adapters/Driven/Persistence.File/Yaml/ParsingEventBuffer.cs b/src/Adapters/Driven/Persistence.File/Plugin/Yaml/ParsingEventBuffer.cs similarity index 89% rename from src/Adapters/Driven/Persistence.File/Yaml/ParsingEventBuffer.cs rename to src/Adapters/Driven/Persistence.File/Plugin/Yaml/ParsingEventBuffer.cs index 3616e67e..1c9e0540 100644 --- a/src/Adapters/Driven/Persistence.File/Yaml/ParsingEventBuffer.cs +++ b/src/Adapters/Driven/Persistence.File/Plugin/Yaml/ParsingEventBuffer.cs @@ -1,7 +1,7 @@ using YamlDotNet.Core; using YamlDotNet.Core.Events; -namespace ScriptBee.Persistence.File.Yaml; +namespace ScriptBee.Persistence.File.Plugin.Yaml; public class ParsingEventBuffer(LinkedList events) : IParser { diff --git a/src/Adapters/Driven/Persistence.File/Yaml/PluginManifestSpecTypeDiscriminator.cs b/src/Adapters/Driven/Persistence.File/Plugin/Yaml/PluginManifestSpecTypeDiscriminator.cs similarity index 97% rename from src/Adapters/Driven/Persistence.File/Yaml/PluginManifestSpecTypeDiscriminator.cs rename to src/Adapters/Driven/Persistence.File/Plugin/Yaml/PluginManifestSpecTypeDiscriminator.cs index 27731127..de88783e 100644 --- a/src/Adapters/Driven/Persistence.File/Yaml/PluginManifestSpecTypeDiscriminator.cs +++ b/src/Adapters/Driven/Persistence.File/Plugin/Yaml/PluginManifestSpecTypeDiscriminator.cs @@ -2,7 +2,7 @@ using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace ScriptBee.Persistence.File.Yaml; +namespace ScriptBee.Persistence.File.Plugin.Yaml; public class PluginManifestSpecTypeDiscriminator( INamingConvention namingConvention, diff --git a/src/Application/Domain/Service.Plugin/Extensions/ConfigurePluginServiceExtension.cs b/src/Application/Domain/Service.Plugin/Extensions/ConfigurePluginServiceExtension.cs index 2b6e85fa..48a5609c 100644 --- a/src/Application/Domain/Service.Plugin/Extensions/ConfigurePluginServiceExtension.cs +++ b/src/Application/Domain/Service.Plugin/Extensions/ConfigurePluginServiceExtension.cs @@ -6,7 +6,6 @@ public static class ConfigurePluginServiceExtension { public static IServiceCollection AddPluginServices(this IServiceCollection services) { - return services - .AddSingleton(); + return services.AddSingleton(); } } diff --git a/test/Adapters/Driven/Persistence.File.Tests/Persistence.File.Tests.csproj b/test/Adapters/Driven/Persistence.File.Tests/Persistence.File.Tests.csproj index c46a8f49..03f63912 100644 --- a/test/Adapters/Driven/Persistence.File.Tests/Persistence.File.Tests.csproj +++ b/test/Adapters/Driven/Persistence.File.Tests/Persistence.File.Tests.csproj @@ -11,7 +11,7 @@ - + PreserveNewest diff --git a/test/Adapters/Driven/Persistence.File.Tests/Manifest/PluginManifestYamlParsingTests.cs b/test/Adapters/Driven/Persistence.File.Tests/Plugin/Manifest/PluginManifestYamlParsingTests.cs similarity index 99% rename from test/Adapters/Driven/Persistence.File.Tests/Manifest/PluginManifestYamlParsingTests.cs rename to test/Adapters/Driven/Persistence.File.Tests/Plugin/Manifest/PluginManifestYamlParsingTests.cs index c5ff5124..69e69add 100644 --- a/test/Adapters/Driven/Persistence.File.Tests/Manifest/PluginManifestYamlParsingTests.cs +++ b/test/Adapters/Driven/Persistence.File.Tests/Plugin/Manifest/PluginManifestYamlParsingTests.cs @@ -1,4 +1,5 @@ using ScriptBee.Domain.Model.Plugin.Manifest; +using ScriptBee.Persistence.File.Plugin; namespace ScriptBee.Persistence.File.Tests.Manifest; diff --git a/test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/HelperFunctionsPluginManifest.yaml b/test/Adapters/Driven/Persistence.File.Tests/Plugin/Manifest/TestData/HelperFunctionsPluginManifest.yaml similarity index 100% rename from test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/HelperFunctionsPluginManifest.yaml rename to test/Adapters/Driven/Persistence.File.Tests/Plugin/Manifest/TestData/HelperFunctionsPluginManifest.yaml diff --git a/test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/LinkerPluginManifest.yaml b/test/Adapters/Driven/Persistence.File.Tests/Plugin/Manifest/TestData/LinkerPluginManifest.yaml similarity index 100% rename from test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/LinkerPluginManifest.yaml rename to test/Adapters/Driven/Persistence.File.Tests/Plugin/Manifest/TestData/LinkerPluginManifest.yaml diff --git a/test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/LoaderPluginManifest.yaml b/test/Adapters/Driven/Persistence.File.Tests/Plugin/Manifest/TestData/LoaderPluginManifest.yaml similarity index 100% rename from test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/LoaderPluginManifest.yaml rename to test/Adapters/Driven/Persistence.File.Tests/Plugin/Manifest/TestData/LoaderPluginManifest.yaml diff --git a/test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/PluginBundlePluginManifest.yaml b/test/Adapters/Driven/Persistence.File.Tests/Plugin/Manifest/TestData/PluginBundlePluginManifest.yaml similarity index 100% rename from test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/PluginBundlePluginManifest.yaml rename to test/Adapters/Driven/Persistence.File.Tests/Plugin/Manifest/TestData/PluginBundlePluginManifest.yaml diff --git a/test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/ScriptGeneratorPluginManifest.yaml b/test/Adapters/Driven/Persistence.File.Tests/Plugin/Manifest/TestData/ScriptGeneratorPluginManifest.yaml similarity index 100% rename from test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/ScriptGeneratorPluginManifest.yaml rename to test/Adapters/Driven/Persistence.File.Tests/Plugin/Manifest/TestData/ScriptGeneratorPluginManifest.yaml diff --git a/test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/ScriptRunnerPluginManifest.yaml b/test/Adapters/Driven/Persistence.File.Tests/Plugin/Manifest/TestData/ScriptRunnerPluginManifest.yaml similarity index 100% rename from test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/ScriptRunnerPluginManifest.yaml rename to test/Adapters/Driven/Persistence.File.Tests/Plugin/Manifest/TestData/ScriptRunnerPluginManifest.yaml diff --git a/test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/UiPluginManifest.yaml b/test/Adapters/Driven/Persistence.File.Tests/Plugin/Manifest/TestData/UiPluginManifest.yaml similarity index 100% rename from test/Adapters/Driven/Persistence.File.Tests/Manifest/TestData/UiPluginManifest.yaml rename to test/Adapters/Driven/Persistence.File.Tests/Plugin/Manifest/TestData/UiPluginManifest.yaml diff --git a/test/Adapters/Driven/Persistence.File.Tests/PluginNameGeneratorTests.cs b/test/Adapters/Driven/Persistence.File.Tests/Plugin/PluginNameGeneratorTests.cs similarity index 96% rename from test/Adapters/Driven/Persistence.File.Tests/PluginNameGeneratorTests.cs rename to test/Adapters/Driven/Persistence.File.Tests/Plugin/PluginNameGeneratorTests.cs index ed13dd4c..7b55bc08 100644 --- a/test/Adapters/Driven/Persistence.File.Tests/PluginNameGeneratorTests.cs +++ b/test/Adapters/Driven/Persistence.File.Tests/Plugin/PluginNameGeneratorTests.cs @@ -1,4 +1,6 @@ -namespace ScriptBee.Persistence.File.Tests; +using ScriptBee.Persistence.File.Plugin; + +namespace ScriptBee.Persistence.File.Tests; public class PluginNameGeneratorTests { From 9336438f66fff8c9e90483527574673d672f7789 Mon Sep 17 00:00:00 2001 From: Andrei Timar Date: Sun, 30 Mar 2025 16:53:25 +0300 Subject: [PATCH 12/14] refactor: relocated PluginReader --- ScriptBee.Tests/Plugin/PluginReaderTests.cs | 94 ------------------- ScriptBee/Plugin/PluginReader.cs | 76 --------------- .../Driven/Persistence.File}/FileService.cs | 13 +-- .../Driven/Persistence.File}/IFileService.cs | 12 +-- .../Persistence.File/Plugin/PluginReader.cs | 69 ++++++++++++++ .../Plugin/PluginReaderTests.cs | 81 ++++++++++++++++ 6 files changed, 160 insertions(+), 185 deletions(-) delete mode 100644 ScriptBee.Tests/Plugin/PluginReaderTests.cs delete mode 100644 ScriptBee/Plugin/PluginReader.cs rename {ScriptBee/FileManagement => src/Adapters/Driven/Persistence.File}/FileService.cs (81%) rename {ScriptBee/FileManagement => src/Adapters/Driven/Persistence.File}/IFileService.cs (83%) create mode 100644 src/Adapters/Driven/Persistence.File/Plugin/PluginReader.cs create mode 100644 test/Adapters/Driven/Persistence.File.Tests/Plugin/PluginReaderTests.cs diff --git a/ScriptBee.Tests/Plugin/PluginReaderTests.cs b/ScriptBee.Tests/Plugin/PluginReaderTests.cs deleted file mode 100644 index cf220ac5..00000000 --- a/ScriptBee.Tests/Plugin/PluginReaderTests.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using AutoFixture; -using Moq; -using ScriptBee.FileManagement; -using ScriptBee.Plugin; -using ScriptBee.Plugin.Manifest; -using Serilog; -using Xunit; - -namespace ScriptBee.Tests.Plugin; - -public class PluginReaderTests -{ - private readonly Mock _fileServiceMock; - private readonly Fixture _fixture; - private readonly Mock _loggerMock; - - private readonly PluginReader _pluginReader; - private readonly Mock _yamlFileReaderMock; - - public PluginReaderTests() - { - _loggerMock = new Mock(); - _fileServiceMock = new Mock(); - _yamlFileReaderMock = new Mock(); - _fixture = new Fixture(); - - _pluginReader = new PluginReader(_loggerMock.Object, _fileServiceMock.Object, _yamlFileReaderMock.Object); - } - - [Fact] - public void GivenNoPluginsInFolder_WhenReadPlugins_ThenReturnEmptyList() - { - _fileServiceMock.Setup(x => x.GetDirectories(It.IsAny())).Returns(new List()); - - var result = _pluginReader.ReadPlugins("path"); - - Assert.Empty(result); - } - - [Fact] - public void GivenFolderWithNoManifest_WhenReadPlugins_ThenReturnEmptyList() - { - _fileServiceMock.Setup(x => x.GetDirectories(It.IsAny())).Returns(new List { "path" }); - _fileServiceMock.Setup(x => x.FileExists(It.IsAny())).Returns(false); - - var result = _pluginReader.ReadPlugins("path"); - - Assert.Empty(result); - } - - [Fact] - public void GivenErrorWhileReadingManifest_WhenReadPlugins_ThenReturnEmptyList() - { - _fileServiceMock.Setup(x => x.GetDirectories(It.IsAny())).Returns(new List { "path" }); - _fileServiceMock.Setup(x => x.FileExists(It.IsAny())).Returns(true); - _yamlFileReaderMock.Setup(x => x.Read(It.IsAny())).Throws(new Exception()); - - var result = _pluginReader.ReadPlugins("path"); - - Assert.Empty(result); - } - - [Fact] - public void GivenValidManifests_WhenReadPlugins_ThenReturnManifests() - { - const string path1ManifestYaml = "path1/manifest.yaml"; - const string path2ManifestYaml = "path2/manifest.yaml"; - var pluginExtensionPoint1 = _fixture.Create(); - var pluginExtensionPoint2 = _fixture.Create(); - var pluginManifest1 = _fixture.Build() - .With(p => p.ExtensionPoints, new List { pluginExtensionPoint1 }) - .Create(); - var pluginManifest2 = _fixture.Build() - .With(p => p.ExtensionPoints, new List { pluginExtensionPoint2 }) - .Create(); - _fileServiceMock.Setup(x => x.GetDirectories(It.IsAny())) - .Returns(new List { "path1", "path2" }); - _fileServiceMock.Setup(x => x.CombinePaths("path1", "manifest.yaml")).Returns(path1ManifestYaml); - _fileServiceMock.Setup(x => x.CombinePaths("path2", "manifest.yaml")).Returns(path2ManifestYaml); - _fileServiceMock.Setup(x => x.FileExists(path1ManifestYaml)).Returns(true); - _fileServiceMock.Setup(x => x.FileExists(path2ManifestYaml)).Returns(true); - _fileServiceMock.Setup(x => x.GetFileName("path1")).Returns("path1@0.0.0"); - _fileServiceMock.Setup(x => x.GetFileName("path2")).Returns("path2@0.0.0"); - _yamlFileReaderMock.Setup(x => x.Read(path1ManifestYaml)).Returns(pluginManifest1); - _yamlFileReaderMock.Setup(x => x.Read(path2ManifestYaml)).Returns(pluginManifest2); - - var result = _pluginReader.ReadPlugins("path"); - - Assert.Equal(2, result.Count()); - } -} diff --git a/ScriptBee/Plugin/PluginReader.cs b/ScriptBee/Plugin/PluginReader.cs deleted file mode 100644 index 5f06b1be..00000000 --- a/ScriptBee/Plugin/PluginReader.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using ScriptBee.FileManagement; -using ScriptBee.Plugin.Manifest; -using Serilog; - -namespace ScriptBee.Plugin; - -public class PluginReader : IPluginReader -{ - private const string ManifestYaml = "manifest.yaml"; - private readonly IFileService _fileService; - - private readonly ILogger _logger; - private readonly IPluginManifestYamlFileReader _pluginManifestYamlFileReader; - - public PluginReader(ILogger logger, IFileService fileService, - IPluginManifestYamlFileReader pluginManifestYamlFileReader) - { - _logger = logger; - _fileService = fileService; - _pluginManifestYamlFileReader = pluginManifestYamlFileReader; - } - - public Models.Plugin? ReadPlugin(string pluginPath) - { - _logger.Information("Reading manifest from {PluginDirectory}", pluginPath); - - try - { - var pluginManifest = ReadManifest(pluginPath); - - if (pluginManifest != null) - { - var folderName = _fileService.GetFileName(pluginPath); - var (id, version) = PluginNameGenerator.GetPluginNameAndVersion(folderName); - - if (id is null || version is null) - { - _logger.Warning("Plugin {PluginDirectory} has invalid name or version", pluginPath); - return null; - } - - return new Models.Plugin(pluginPath, id, version, pluginManifest); - } - - _logger.Warning("Manifest not found in {PluginDirectory}", pluginPath); - return null; - } - catch (Exception e) - { - _logger.Error(e, "Error reading manifest from {PluginDirectory}", pluginPath); - } - - return null; - } - - public IEnumerable ReadPlugins(string pluginFolderPath) - { - var pluginDirectories = _fileService.GetDirectories(pluginFolderPath).ToList(); - - _logger.Information("Found {PluginCount} plugin directories", pluginDirectories.Count); - - return pluginDirectories.Select(ReadPlugin) - .Where(plugin => plugin is not null) - .OfType(); - } - - private PluginManifest? ReadManifest(string pluginDirectory) - { - var path = _fileService.CombinePaths(pluginDirectory, ManifestYaml); - - return _fileService.FileExists(path) ? _pluginManifestYamlFileReader.Read(path) : null; - } -} diff --git a/ScriptBee/FileManagement/FileService.cs b/src/Adapters/Driven/Persistence.File/FileService.cs similarity index 81% rename from ScriptBee/FileManagement/FileService.cs rename to src/Adapters/Driven/Persistence.File/FileService.cs index afed5c8c..9ed5e085 100644 --- a/ScriptBee/FileManagement/FileService.cs +++ b/src/Adapters/Driven/Persistence.File/FileService.cs @@ -1,7 +1,4 @@ -using System.Collections.Generic; -using System.IO; - -namespace ScriptBee.FileManagement; +namespace ScriptBee.Persistence.File; public sealed class FileService : IFileService { @@ -29,7 +26,7 @@ public bool DirectoryExists(string path) public bool FileExists(string path) { - return File.Exists(path); + return System.IO.File.Exists(path); } public string CombinePaths(string path1, string path2) @@ -41,7 +38,7 @@ public void DeleteFile(string path) { if (FileExists(path)) { - File.Delete(path); + System.IO.File.Delete(path); } } @@ -55,12 +52,12 @@ public void DeleteDirectory(string path) public IEnumerable ReadAllLines(string path) { - return File.ReadAllLines(path); + return System.IO.File.ReadAllLines(path); } public void AppendTextToFile(string path, string text) { - File.AppendAllLines(path, new List { text }); + System.IO.File.AppendAllLines(path, new List { text }); } public void CreateFolder(string path) diff --git a/ScriptBee/FileManagement/IFileService.cs b/src/Adapters/Driven/Persistence.File/IFileService.cs similarity index 83% rename from ScriptBee/FileManagement/IFileService.cs rename to src/Adapters/Driven/Persistence.File/IFileService.cs index 6d433843..c59cf094 100644 --- a/ScriptBee/FileManagement/IFileService.cs +++ b/src/Adapters/Driven/Persistence.File/IFileService.cs @@ -1,17 +1,15 @@ -using System.Collections.Generic; - -namespace ScriptBee.FileManagement; +namespace ScriptBee.Persistence.File; public interface IFileService { string GetFileName(string path); - + IEnumerable GetDirectories(string path); bool DirectoryExists(string path); - + bool FileExists(string path); - + string CombinePaths(string path1, string path2); void DeleteFile(string path); @@ -21,6 +19,6 @@ public interface IFileService IEnumerable ReadAllLines(string path); void AppendTextToFile(string path, string text); - + void CreateFolder(string path); } diff --git a/src/Adapters/Driven/Persistence.File/Plugin/PluginReader.cs b/src/Adapters/Driven/Persistence.File/Plugin/PluginReader.cs new file mode 100644 index 00000000..59a1d9f2 --- /dev/null +++ b/src/Adapters/Driven/Persistence.File/Plugin/PluginReader.cs @@ -0,0 +1,69 @@ +using Microsoft.Extensions.Logging; +using ScriptBee.Domain.Model.Plugin.Manifest; +using ScriptBee.Ports.Plugins; + +namespace ScriptBee.Persistence.File.Plugin; + +public class PluginReader( + ILogger logger, + IFileService fileService, + IPluginManifestYamlFileReader pluginManifestYamlFileReader +) : IPluginReader +{ + private const string ManifestYaml = "manifest.yaml"; + + public Domain.Model.Plugin.Plugin? ReadPlugin(string pluginPath) + { + logger.LogInformation("Reading manifest from {PluginDirectory}", pluginPath); + + try + { + var pluginManifest = ReadManifest(pluginPath); + + if (pluginManifest != null) + { + var folderName = fileService.GetFileName(pluginPath); + var (id, version) = PluginNameGenerator.GetPluginNameAndVersion(folderName); + + if (id is null || version is null) + { + logger.LogWarning( + "Plugin {PluginDirectory} has invalid name or version", + pluginPath + ); + return null; + } + + return new Domain.Model.Plugin.Plugin(pluginPath, id, version, pluginManifest); + } + + logger.LogWarning("Manifest not found in {PluginDirectory}", pluginPath); + return null; + } + catch (Exception e) + { + logger.LogError(e, "Error reading manifest from {PluginDirectory}", pluginPath); + } + + return null; + } + + public IEnumerable ReadPlugins(string pluginFolderPath) + { + var pluginDirectories = fileService.GetDirectories(pluginFolderPath).ToList(); + + logger.LogInformation("Found {PluginCount} plugin directories", pluginDirectories.Count); + + return pluginDirectories + .Select(ReadPlugin) + .Where(plugin => plugin is not null) + .OfType(); + } + + private PluginManifest? ReadManifest(string pluginDirectory) + { + var path = fileService.CombinePaths(pluginDirectory, ManifestYaml); + + return fileService.FileExists(path) ? pluginManifestYamlFileReader.Read(path) : null; + } +} diff --git a/test/Adapters/Driven/Persistence.File.Tests/Plugin/PluginReaderTests.cs b/test/Adapters/Driven/Persistence.File.Tests/Plugin/PluginReaderTests.cs new file mode 100644 index 00000000..671cdf2c --- /dev/null +++ b/test/Adapters/Driven/Persistence.File.Tests/Plugin/PluginReaderTests.cs @@ -0,0 +1,81 @@ +using Microsoft.Extensions.Logging; +using NSubstitute; +using ScriptBee.Domain.Model.Plugin.Manifest; +using ScriptBee.Persistence.File.Plugin; + +namespace ScriptBee.Persistence.File.Tests.Plugin; + +public class PluginReaderTests +{ + private readonly ILogger _logger = Substitute.For>(); + private readonly IFileService _fileService = Substitute.For(); + + private readonly IPluginManifestYamlFileReader _pluginManifestYamlFileReader = + Substitute.For(); + + private readonly PluginReader _pluginReader; + + public PluginReaderTests() + { + _pluginReader = new PluginReader(_logger, _fileService, _pluginManifestYamlFileReader); + } + + [Fact] + public void GivenNoPluginsInFolder_WhenReadPlugins_ThenReturnEmptyList() + { + _fileService.GetDirectories(Arg.Any()).Returns(new List()); + + var result = _pluginReader.ReadPlugins("path"); + + Assert.Empty(result); + } + + [Fact] + public void GivenFolderWithNoManifest_WhenReadPlugins_ThenReturnEmptyList() + { + _fileService.GetDirectories(Arg.Any()).Returns(new List { "path" }); + _fileService.FileExists(Arg.Any()).Returns(false); + + var result = _pluginReader.ReadPlugins("path"); + + Assert.Empty(result); + } + + [Fact] + public void GivenErrorWhileReadingManifest_WhenReadPlugins_ThenReturnEmptyList() + { + _fileService.GetDirectories(Arg.Any()).Returns(new List { "path" }); + _fileService.FileExists(Arg.Any()).Returns(true); + _pluginManifestYamlFileReader.When(x => x.Read(Arg.Any())).Throws(new Exception()); + + var result = _pluginReader.ReadPlugins("path"); + + Assert.Empty(result); + } + + [Fact] + public void GivenValidManifests_WhenReadPlugins_ThenReturnManifests() + { + const string path1ManifestYaml = "path1/manifest.yaml"; + const string path2ManifestYaml = "path2/manifest.yaml"; + var pluginExtensionPoint1 = new ScriptGeneratorPluginExtensionPoint(); + var pluginExtensionPoint2 = new UiPluginExtensionPoint(); + var pluginManifest1 = new PluginManifest { ExtensionPoints = [pluginExtensionPoint1] }; + var pluginManifest2 = new PluginManifest { ExtensionPoints = [pluginExtensionPoint2] }; + _fileService + .GetDirectories(Arg.Any()) + .Returns(new List { "path1", "path2" }); + _fileService.CombinePaths("path1", "manifest.yaml").Returns(path1ManifestYaml); + _fileService.CombinePaths("path2", "manifest.yaml").Returns(path2ManifestYaml); + _fileService.FileExists(path1ManifestYaml).Returns(true); + _fileService.FileExists(path2ManifestYaml).Returns(true); + _fileService.GetFileName("path1").Returns("path1@0.0.0"); + _fileService.GetFileName("path2").Returns("path2@0.0.0"); + _pluginManifestYamlFileReader.Read(path1ManifestYaml).Returns(pluginManifest1); + _pluginManifestYamlFileReader.Read(path2ManifestYaml).Returns(pluginManifest2); + + var result = _pluginReader.ReadPlugins("path"); + + Assert.Equal(2, result.Count()); + } +} From c61853c131355ce96b9d8fcaa706eba046d95e52 Mon Sep 17 00:00:00 2001 From: Andrei Timar Date: Sun, 30 Mar 2025 17:07:03 +0300 Subject: [PATCH 13/14] refactor: relocated PluginLoader --- ScriptBee.Tests/Plugin/PluginLoaderTests.cs | 96 --------------- ScriptBee.Tests/Plugin/PluginManagerTests.cs | 100 --------------- ScriptBee/Plugin/PluginLoader.cs | 54 -------- .../ConfigurePluginServiceExtension.cs | 4 +- .../Domain/Service.Plugin/IPluginLoader.cs | 6 + .../Domain/Service.Plugin/PluginLoader.cs | 51 ++++++++ .../Driven/Ports.Plugins/IPluginLoader.cs | 8 -- .../Service.Plugin.Tests/PluginLoaderTests.cs | 115 ++++++++++++++++++ 8 files changed, 175 insertions(+), 259 deletions(-) delete mode 100644 ScriptBee.Tests/Plugin/PluginLoaderTests.cs delete mode 100644 ScriptBee.Tests/Plugin/PluginManagerTests.cs delete mode 100644 ScriptBee/Plugin/PluginLoader.cs create mode 100644 src/Application/Domain/Service.Plugin/IPluginLoader.cs create mode 100644 src/Application/Domain/Service.Plugin/PluginLoader.cs delete mode 100644 src/Application/Ports/Driven/Ports.Plugins/IPluginLoader.cs create mode 100644 test/Application/Domain/Service.Plugin.Tests/PluginLoaderTests.cs diff --git a/ScriptBee.Tests/Plugin/PluginLoaderTests.cs b/ScriptBee.Tests/Plugin/PluginLoaderTests.cs deleted file mode 100644 index 14639917..00000000 --- a/ScriptBee.Tests/Plugin/PluginLoaderTests.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using System.Collections.Generic; -using Moq; -using ScriptBee.FileManagement; -using ScriptBee.Plugin; -using ScriptBee.Tests.Plugin.Internals; -using Serilog; -using Xunit; - -namespace ScriptBee.Tests.Plugin; - -public class PluginLoaderTests -{ - private readonly Mock _dllLoaderMock; - private readonly Mock _fileServiceMock; - private readonly Mock _loggerMock; - - private readonly PluginLoader _pluginLoader; - private readonly Mock _pluginRegistrationServiceMock; - private readonly Mock _pluginRepositoryMock; - - public PluginLoaderTests() - { - _loggerMock = new Mock(); - _fileServiceMock = new Mock(); - _dllLoaderMock = new Mock(); - _pluginRepositoryMock = new Mock(); - _pluginRegistrationServiceMock = new Mock(); - - _pluginLoader = new PluginLoader(_loggerMock.Object, _fileServiceMock.Object, _dllLoaderMock.Object, - _pluginRepositoryMock.Object, _pluginRegistrationServiceMock.Object); - } - - [Fact] - public void GivenUnregisteredPlugin_WhenLoad_ThenMessageIsLogged() - { - HashSet? nullTypes = null; - var plugin = new TestPlugin("id", new Version(0, 0, 0, 1)); - plugin.Manifest.ExtensionPoints[0].Kind = "kind"; - plugin.Manifest.ExtensionPoints[0].EntryPoint = "entryPoint"; - - _pluginRegistrationServiceMock.Setup(s => s.TryGetValue(It.IsAny(), out nullTypes)) - .Returns(false); - - _pluginLoader.Load(plugin); - - _loggerMock.Verify(l => - l.Warning("Plugin kind '{PluginKind}' from '{EntryPoint}' has no relevant Dlls to load", "kind", - "entryPoint")); - } - - [Fact] - public void GivenRegisteredPluginWithNoAcceptedTypes_WhenLoad_ThenPluginManifestIsLoaded() - { - var acceptedTypes = new HashSet(); - var plugin = new TestPlugin("id", new Version(0, 0, 0, 1)); - - _pluginRegistrationServiceMock.Setup(s => s.TryGetValue(It.IsAny(), out acceptedTypes)) - .Returns(true); - _fileServiceMock.Setup(s => s.CombinePaths(plugin.FolderPath, plugin.Manifest.ExtensionPoints[0].EntryPoint)) - .Returns("entrypoint.dll"); - _dllLoaderMock.Setup(l => l.LoadDllTypes("entrypoint.dll", acceptedTypes)) - .Returns(new List<(Type @interface, Type concrete)>()); - - _pluginLoader.Load(plugin); - - _pluginRepositoryMock.Verify(r => r.RegisterPlugin(plugin), - Times.Once()); - } - - [Fact] - public void GivenRegisteredPluginWithMultipleAcceptedTypes_WhenLoad_ThenPluginManifestIsLoaded() - { - var acceptedTypes = new HashSet - { - typeof(string), - typeof(object) - }; - var plugin = new TestPlugin("id", new Version(0, 0, 0, 1)); - - _pluginRegistrationServiceMock.Setup(s => s.TryGetValue(It.IsAny(), out acceptedTypes)) - .Returns(true); - _fileServiceMock.Setup(s => s.CombinePaths(plugin.FolderPath, plugin.Manifest.ExtensionPoints[0].EntryPoint)) - .Returns("entrypoint.dll"); - _dllLoaderMock.Setup(l => l.LoadDllTypes("entrypoint.dll", acceptedTypes)) - .Returns(new List<(Type @interface, Type concrete)> - { - (typeof(string), typeof(string)), - (typeof(object), typeof(object)) - }); - - _pluginLoader.Load(plugin); - - _pluginRepositoryMock.Verify(r => r.RegisterPlugin(plugin, typeof(object), typeof(object)), Times.Once()); - } -} diff --git a/ScriptBee.Tests/Plugin/PluginManagerTests.cs b/ScriptBee.Tests/Plugin/PluginManagerTests.cs deleted file mode 100644 index 73b6cccd..00000000 --- a/ScriptBee.Tests/Plugin/PluginManagerTests.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System; -using System.Collections.Generic; -using Moq; -using ScriptBee.Config; -using ScriptBee.Plugin; -using ScriptBee.Plugin.Installer; -using ScriptBee.ProjectContext; -using ScriptBee.Tests.Plugin.Internals; -using Serilog; -using Xunit; - -namespace ScriptBee.Tests.Plugin; - -public class PluginManagerTests -{ - private readonly Mock _pluginReaderMock; - private readonly Mock _pluginLoaderMock; - private readonly Mock _pluginUninstallerMock; - private readonly Mock _loggerMock; - private readonly Mock _projectFileStructureManagerMock; - - private readonly PluginManager _pluginManager; - - public PluginManagerTests() - { - _pluginReaderMock = new Mock(); - _pluginLoaderMock = new Mock(); - _pluginUninstallerMock = new Mock(); - _projectFileStructureManagerMock = new Mock(); - _loggerMock = new Mock(); - - _pluginManager = new PluginManager(_pluginReaderMock.Object, _pluginLoaderMock.Object, - _pluginUninstallerMock.Object, _projectFileStructureManagerMock.Object, _loggerMock.Object); - } - - [Fact] - public void GivenEmptyPlugins_WhenLoadPlugins_ThenNoPluginsLoaded() - { - _pluginReaderMock.Setup(x => x.ReadPlugins(ConfigFolders.PathToPlugins)) - .Returns(new List()); - - _pluginManager.LoadPlugins(); - - _pluginLoaderMock.Verify(x => x.Load(It.IsAny()), Times.Never()); - } - - [Fact] - public void GivenAllValidPlugins_WhenLoadPlugins_ThenAllPluginsAreLoaded() - { - _pluginReaderMock.Setup(x => x.ReadPlugins(ConfigFolders.PathToPlugins)) - .Returns(new List - { - new TestPlugin("id", new Version(0, 0, 0, 1)), - new TestPlugin("id", new Version(0, 0, 0, 2)), - new TestPlugin("id", new Version(0, 0, 0, 3)), - }); - - _pluginManager.LoadPlugins(); - - _pluginLoaderMock.Verify(x => x.Load(It.IsAny()), Times.Exactly(3)); - _projectFileStructureManagerMock.Verify(x => x.CreateScriptBeeFolderStructure(), Times.Once()); - } - - [Fact] - public void GivenSomeInvalidPlugins_WhenLoadPlugins_ThenAllValidPluginsAreLoaded() - { - var expectedException = new Exception("Test exception"); - - Models.Plugin testPlugin1 = new TestPlugin("id", new Version(0, 0, 0, 1)); - Models.Plugin testPlugin2 = new TestPlugin("id", new Version(0, 0, 1, 1)); - Models.Plugin testPlugin3 = new TestPlugin("id", new Version(0, 0, 2, 1)); - - _pluginReaderMock.Setup(x => x.ReadPlugins(ConfigFolders.PathToPlugins)) - .Returns(new List - { - testPlugin1, - testPlugin2, - testPlugin3, - }); - _pluginLoaderMock.Setup(l => l.Load(testPlugin2)).Throws(expectedException); - - _pluginManager.LoadPlugins(); - - _loggerMock.Verify(l => l.Error(expectedException, "Failed to load plugin {Plugin}", testPlugin2), - Times.Once()); - _projectFileStructureManagerMock.Verify(x => x.CreateScriptBeeFolderStructure(), Times.Once()); - } - - [Fact] - public void WhenLoadPlugins_ThenMarkedForDeletePluginsAreUninstalled() - { - _pluginReaderMock.Setup(x => x.ReadPlugins(ConfigFolders.PathToPlugins)) - .Returns(new List()); - - _pluginManager.LoadPlugins(); - - _pluginUninstallerMock.Verify(x => x.DeleteMarkedPlugins(), Times.Once()); - _projectFileStructureManagerMock.Verify(x => x.CreateScriptBeeFolderStructure(), Times.Once()); - } -} diff --git a/ScriptBee/Plugin/PluginLoader.cs b/ScriptBee/Plugin/PluginLoader.cs deleted file mode 100644 index f0c71230..00000000 --- a/ScriptBee/Plugin/PluginLoader.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Linq; -using ScriptBee.FileManagement; -using Serilog; - -namespace ScriptBee.Plugin; - -public class PluginLoader : IPluginLoader -{ - private readonly IDllLoader _dllLoader; - private readonly IFileService _fileService; - private readonly ILogger _logger; - private readonly IPluginRegistrationService _pluginRegistrationService; - private readonly IPluginRepository _pluginRepository; - - public PluginLoader(ILogger logger, IFileService fileService, IDllLoader dllLoader, - IPluginRepository pluginRepository, IPluginRegistrationService pluginRegistrationService) - { - _logger = logger; - _fileService = fileService; - _dllLoader = dllLoader; - _pluginRepository = pluginRepository; - _pluginRegistrationService = pluginRegistrationService; - } - - public void Load(Models.Plugin plugin) - { - foreach (var extensionPoint in plugin.Manifest.ExtensionPoints) - { - if (_pluginRegistrationService.TryGetValue(extensionPoint.Kind, out var acceptedPluginTypes)) - { - if (!acceptedPluginTypes!.Any()) - { - _pluginRepository.RegisterPlugin(plugin); - return; - } - - var path = _fileService.CombinePaths(plugin.FolderPath, extensionPoint.EntryPoint); - - var loadDllTypes = _dllLoader.LoadDllTypes(path, acceptedPluginTypes!).ToList(); - - foreach (var (@interface, concrete) in loadDllTypes) - { - _pluginRepository.RegisterPlugin(plugin, @interface, concrete); - } - } - else - { - _logger.Warning("Plugin kind '{PluginKind}' from '{EntryPoint}' has no relevant Dlls to load", extensionPoint.Kind, extensionPoint.EntryPoint); - } - } - - _logger.Information("Plugin {PluginName} loaded", plugin.Manifest.Name); - } -} diff --git a/src/Application/Domain/Service.Plugin/Extensions/ConfigurePluginServiceExtension.cs b/src/Application/Domain/Service.Plugin/Extensions/ConfigurePluginServiceExtension.cs index 48a5609c..d9170b70 100644 --- a/src/Application/Domain/Service.Plugin/Extensions/ConfigurePluginServiceExtension.cs +++ b/src/Application/Domain/Service.Plugin/Extensions/ConfigurePluginServiceExtension.cs @@ -6,6 +6,8 @@ public static class ConfigurePluginServiceExtension { public static IServiceCollection AddPluginServices(this IServiceCollection services) { - return services.AddSingleton(); + return services + .AddSingleton() + .AddSingleton(); } } diff --git a/src/Application/Domain/Service.Plugin/IPluginLoader.cs b/src/Application/Domain/Service.Plugin/IPluginLoader.cs new file mode 100644 index 00000000..1f252269 --- /dev/null +++ b/src/Application/Domain/Service.Plugin/IPluginLoader.cs @@ -0,0 +1,6 @@ +namespace ScriptBee.Service.Plugin; + +public interface IPluginLoader +{ + void Load(Domain.Model.Plugin.Plugin plugin); +} diff --git a/src/Application/Domain/Service.Plugin/PluginLoader.cs b/src/Application/Domain/Service.Plugin/PluginLoader.cs new file mode 100644 index 00000000..df8cdff4 --- /dev/null +++ b/src/Application/Domain/Service.Plugin/PluginLoader.cs @@ -0,0 +1,51 @@ +using Microsoft.Extensions.Logging; +using ScriptBee.Ports.Plugins; + +namespace ScriptBee.Service.Plugin; + +public class PluginLoader( + ILogger logger, + IDllLoader dllLoader, + IPluginRepository pluginRepository, + IPluginRegistrationService pluginRegistrationService +) : IPluginLoader +{ + public void Load(Domain.Model.Plugin.Plugin plugin) + { + foreach (var extensionPoint in plugin.Manifest.ExtensionPoints) + { + if ( + pluginRegistrationService.TryGetValue( + extensionPoint.Kind, + out var acceptedPluginTypes + ) + ) + { + if (acceptedPluginTypes!.Count == 0) + { + pluginRepository.RegisterPlugin(plugin); + return; + } + + var path = Path.Combine(plugin.FolderPath, extensionPoint.EntryPoint); + + var loadDllTypes = dllLoader.LoadDllTypes(path, acceptedPluginTypes!).ToList(); + + foreach (var (@interface, concrete) in loadDllTypes) + { + pluginRepository.RegisterPlugin(plugin, @interface, concrete); + } + } + else + { + logger.LogWarning( + "Plugin kind '{PluginKind}' from '{EntryPoint}' has no relevant Dlls to load", + extensionPoint.Kind, + extensionPoint.EntryPoint + ); + } + } + + logger.LogInformation("Plugin {PluginName} loaded", plugin.Manifest.Name); + } +} diff --git a/src/Application/Ports/Driven/Ports.Plugins/IPluginLoader.cs b/src/Application/Ports/Driven/Ports.Plugins/IPluginLoader.cs deleted file mode 100644 index 0139a842..00000000 --- a/src/Application/Ports/Driven/Ports.Plugins/IPluginLoader.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ScriptBee.Domain.Model.Plugin; - -namespace ScriptBee.Ports.Plugins; - -public interface IPluginLoader -{ - void Load(Plugin plugin); -} diff --git a/test/Application/Domain/Service.Plugin.Tests/PluginLoaderTests.cs b/test/Application/Domain/Service.Plugin.Tests/PluginLoaderTests.cs new file mode 100644 index 00000000..17cd3f33 --- /dev/null +++ b/test/Application/Domain/Service.Plugin.Tests/PluginLoaderTests.cs @@ -0,0 +1,115 @@ +using Microsoft.Extensions.Logging; +using NSubstitute; +using ScriptBee.Ports.Plugins; +using ScriptBee.Service.Plugin.Tests.Internals; + +namespace ScriptBee.Service.Plugin.Tests; + +public class PluginLoaderTests +{ + private readonly IDllLoader _dllLoader = Substitute.For(); + private readonly ILogger _logger = Substitute.For>(); + + private readonly IPluginRegistrationService _pluginRegistrationService = + Substitute.For(); + + private readonly IPluginRepository _pluginRepository = Substitute.For(); + + private readonly PluginLoader _pluginLoader; + + public PluginLoaderTests() + { + _pluginLoader = new PluginLoader( + _logger, + _dllLoader, + _pluginRepository, + _pluginRegistrationService + ); + } + + [Fact] + public void GivenUnregisteredPlugin_WhenLoad_ThenMessageIsLogged() + { + HashSet? nullTypes = null; + var plugin = new TestPlugin("id", new Version(0, 0, 0, 1)); + plugin.Manifest.ExtensionPoints[0].Kind = "kind"; + plugin.Manifest.ExtensionPoints[0].EntryPoint = "entryPoint"; + _pluginRegistrationService + .TryGetValue(Arg.Any(), out Arg.Any?>()) + .Returns(x => + { + x[1] = nullTypes; + return false; + }); + + _pluginLoader.Load(plugin); + + _logger + .Received(1) + .ReceivedWithAnyArgs() + .LogWarning( + "Plugin kind '{PluginKind}' from '{EntryPoint}' has no relevant Dlls to load", + "kind", + "entryPoint" + ); + } + + [Fact] + public void GivenRegisteredPluginWithNoAcceptedTypes_WhenLoad_ThenPluginManifestIsLoaded() + { + var acceptedTypes = new HashSet(); + var plugin = new TestPlugin("id", new Version(0, 0, 0, 1)); + var entryPoint = Path.Combine( + plugin.FolderPath, + plugin.Manifest.ExtensionPoints[0].EntryPoint + ); + _pluginRegistrationService + .TryGetValue(Arg.Any(), out Arg.Any?>()) + .Returns(x => + { + x[1] = acceptedTypes; + return true; + }); + _dllLoader + .LoadDllTypes(entryPoint, acceptedTypes) + .Returns(new List<(Type @interface, Type concrete)>()); + + _pluginLoader.Load(plugin); + + _pluginRepository.Received(1).ReceivedWithAnyArgs().RegisterPlugin(plugin); + } + + [Fact] + public void GivenRegisteredPluginWithMultipleAcceptedTypes_WhenLoad_ThenPluginManifestIsLoaded() + { + var acceptedTypes = new HashSet { typeof(string), typeof(object) }; + var plugin = new TestPlugin("id", new Version(0, 0, 0, 1)); + var entryPoint = Path.Combine( + plugin.FolderPath, + plugin.Manifest.ExtensionPoints[0].EntryPoint + ); + _pluginRegistrationService + .TryGetValue(Arg.Any(), out Arg.Any?>()) + .Returns(x => + { + x[1] = acceptedTypes; + return true; + }); + _dllLoader + .LoadDllTypes(entryPoint, acceptedTypes) + .Returns( + new List<(Type @interface, Type concrete)> + { + (typeof(string), typeof(string)), + (typeof(object), typeof(object)), + } + ); + + _pluginLoader.Load(plugin); + + _pluginRepository + .Received(1) + .ReceivedWithAnyArgs() + .RegisterPlugin(plugin, typeof(object), typeof(object)); + } +} From 364897a41e274071ea1c9512518488b167b66cfb Mon Sep 17 00:00:00 2001 From: Andrei Timar Date: Sun, 30 Mar 2025 17:14:53 +0300 Subject: [PATCH 14/14] feat: configure file adapters --- .../ConfigureFileAdapterExtensions.cs | 27 +++++++++++++++++++ .../Extensions/FileConfigExtensions.cs | 20 -------------- src/Adapters/Driving/Analysis.Web/Program.cs | 3 ++- .../ConfigurePluginServiceExtension.cs | 4 ++- 4 files changed, 32 insertions(+), 22 deletions(-) create mode 100644 src/Adapters/Driven/Persistence.File/Extensions/ConfigureFileAdapterExtensions.cs delete mode 100644 src/Adapters/Driving/Analysis.Web/Extensions/FileConfigExtensions.cs diff --git a/src/Adapters/Driven/Persistence.File/Extensions/ConfigureFileAdapterExtensions.cs b/src/Adapters/Driven/Persistence.File/Extensions/ConfigureFileAdapterExtensions.cs new file mode 100644 index 00000000..0b437810 --- /dev/null +++ b/src/Adapters/Driven/Persistence.File/Extensions/ConfigureFileAdapterExtensions.cs @@ -0,0 +1,27 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using ScriptBee.Persistence.File.Config; +using ScriptBee.Persistence.File.Plugin; +using ScriptBee.Ports.Files; +using ScriptBee.Ports.Plugins; + +namespace ScriptBee.Persistence.File.Extensions; + +public static class ConfigureFileAdapterExtensions +{ + public static IServiceCollection AddFileAdapters( + this IServiceCollection services, + IConfigurationSection userFolderConfigurationSection + ) + { + services.Configure(userFolderConfigurationSection); + return services + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton(); + } +} diff --git a/src/Adapters/Driving/Analysis.Web/Extensions/FileConfigExtensions.cs b/src/Adapters/Driving/Analysis.Web/Extensions/FileConfigExtensions.cs deleted file mode 100644 index 7450ddde..00000000 --- a/src/Adapters/Driving/Analysis.Web/Extensions/FileConfigExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using ScriptBee.Persistence.File; -using ScriptBee.Persistence.File.Config; -using ScriptBee.Ports.Files; - -namespace ScriptBee.Analysis.Web.Extensions; - -public static class FileConfigExtensions -{ - public static IServiceCollection AddFileConfig( - this IServiceCollection services, - IConfigurationSection userFolderConfigurationSection - ) - { - services.Configure(userFolderConfigurationSection); - return services - .AddSingleton() - .AddSingleton() - .AddSingleton(); - } -} diff --git a/src/Adapters/Driving/Analysis.Web/Program.cs b/src/Adapters/Driving/Analysis.Web/Program.cs index c4f6b4a7..63b26d6f 100644 --- a/src/Adapters/Driving/Analysis.Web/Program.cs +++ b/src/Adapters/Driving/Analysis.Web/Program.cs @@ -5,6 +5,7 @@ using ScriptBee.Common.Web; using ScriptBee.Common.Web.EndpointDefinition; using ScriptBee.Common.Web.Extensions; +using ScriptBee.Persistence.File.Extensions; using ScriptBee.UseCases.Plugin; using Serilog; @@ -21,7 +22,7 @@ .AddProblemDetailsDefaults() .AddMongoDb(mongoConnectionString) .AddCommonServices() - .AddFileConfig(userFolderConfigurationSection) + .AddFileAdapters(userFolderConfigurationSection) .AddPluginsConfig() .AddProjectContextConfig() .AddRunScriptServices(); diff --git a/src/Application/Domain/Service.Plugin/Extensions/ConfigurePluginServiceExtension.cs b/src/Application/Domain/Service.Plugin/Extensions/ConfigurePluginServiceExtension.cs index d9170b70..68b50525 100644 --- a/src/Application/Domain/Service.Plugin/Extensions/ConfigurePluginServiceExtension.cs +++ b/src/Application/Domain/Service.Plugin/Extensions/ConfigurePluginServiceExtension.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using ScriptBee.UseCases.Plugin; namespace ScriptBee.Service.Plugin.Extensions; @@ -8,6 +9,7 @@ public static IServiceCollection AddPluginServices(this IServiceCollection servi { return services .AddSingleton() - .AddSingleton(); + .AddSingleton() + .AddSingleton(); } }