From 53daa4609f0fe91d20d4aa5853602b7e3aee5948 Mon Sep 17 00:00:00 2001 From: Pawel Pabich Date: Tue, 17 Oct 2023 14:37:21 +1000 Subject: [PATCH] PoC for polymorphic contract --- .../PolymorphicTypeContractFixture.cs | 100 ++++++++++++++++++ .../Transport/Protocol/TypeRegistry.cs | 14 +++ 2 files changed, 114 insertions(+) create mode 100644 source/Halibut.Tests/PolymorphicTypeContractFixture.cs diff --git a/source/Halibut.Tests/PolymorphicTypeContractFixture.cs b/source/Halibut.Tests/PolymorphicTypeContractFixture.cs new file mode 100644 index 00000000..cfd00c72 --- /dev/null +++ b/source/Halibut.Tests/PolymorphicTypeContractFixture.cs @@ -0,0 +1,100 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using Halibut.Tests.Support; +using Halibut.Tests.Support.TestAttributes; +using Halibut.Tests.Support.TestCases; +using Halibut.Tests.TestServices.Async; +using Halibut.Tests.Util; +using Halibut.TestUtils.Contracts; +using Halibut.Util; +using NUnit.Framework; + +namespace Halibut.Tests +{ + public class PolymorphicTypeContractFixture : BaseTest + { + + [Test] + [LatestClientAndLatestServiceTestCases(testAsyncServicesAsWell: true, testSyncService:false, testNetworkConditions: false)] + public async Task ExecuteServiceWithPolymorphicContract(ClientAndServiceTestCase clientAndServiceTestCase) + { + var clientAndService = await clientAndServiceTestCase.CreateTestCaseBuilder() + .AsLatestClientAndLatestServiceBuilder() + .WithAsyncService(() => new AsyncPolymorphicService()) + .Build(CancellationToken); + + var polymorphicServiceClient = clientAndService.CreateClient(); + + (await polymorphicServiceClient.ExecuteScriptAsync(new ExecuteScriptCommand("NoOp", new LocalEnvironment()))) + .Should() + .Be("Local:NoOp"); + + (await polymorphicServiceClient.ExecuteScriptAsync(new ExecuteScriptCommand("NoOp", new KubernetesJobEnvironment("Image", "https://dockerhub.com")))) + .Should() + .Be("K8s:NoOp:Imagehttps://dockerhub.com"); + } + } + + public interface IPolymorphicService + { + string ExecuteScript(ExecuteScriptCommand command); + } + + public interface IAsyncPolymorphicService + { + Task ExecuteScriptAsync(ExecuteScriptCommand command, CancellationToken cancellationToken); + } + + public interface IAsyncClientPolymorphicService + { + Task ExecuteScriptAsync(ExecuteScriptCommand command); + } + + public class AsyncPolymorphicService : IAsyncPolymorphicService + { + public async Task ExecuteScriptAsync(ExecuteScriptCommand command, CancellationToken cancellationToken) + { + await Task.CompletedTask; + return command.ExecutionEnvironment switch + { + LocalEnvironment _ => $"Local:{command.Script}", + KubernetesJobEnvironment k8s => $"K8s:{command.Script}:{k8s.ContainerImage}{k8s.ContainerImageRepositoryUrl}", + _ => throw new ArgumentOutOfRangeException() + }; + } + } + + public class ExecuteScriptCommand + { + public ExecuteScriptCommand(string script, IExecutionEnvironment executionEnvironment) + { + Script = script; + ExecutionEnvironment = executionEnvironment; + } + + public string Script { get; } + public IExecutionEnvironment ExecutionEnvironment { get; } + } + + public interface IExecutionEnvironment + { + } + + public class LocalEnvironment : IExecutionEnvironment + { + } + + public class KubernetesJobEnvironment : IExecutionEnvironment + { + public KubernetesJobEnvironment(string containerImage, string containerImageRepositoryUrl) + { + ContainerImage = containerImage; + ContainerImageRepositoryUrl = containerImageRepositoryUrl; + } + + public string ContainerImage { get; } + public string ContainerImageRepositoryUrl { get; } + } +} \ No newline at end of file diff --git a/source/Halibut/Transport/Protocol/TypeRegistry.cs b/source/Halibut/Transport/Protocol/TypeRegistry.cs index 0272f4cf..24ce50fd 100644 --- a/source/Halibut/Transport/Protocol/TypeRegistry.cs +++ b/source/Halibut/Transport/Protocol/TypeRegistry.cs @@ -81,6 +81,20 @@ IEnumerable SubTypesFor(Type type) yield return t; } } + + if (type.IsInterface || type.IsAbstract) + { + var derivedTypes = AppDomain.CurrentDomain.GetAssemblies() + .Where(asm => !asm.IsDynamic) + .SelectMany(asm => asm.GetExportedTypes()) + .Where(t => !t.IsAbstract) + .Where(type.IsAssignableFrom); + + foreach (var derivedType in derivedTypes) + { + yield return derivedType; + } + } } public bool IsInAllowedTypes(Type type)