From a704855fe1825ad005d7276ad54eff364042c591 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 7 Oct 2024 19:10:17 +0200 Subject: [PATCH 1/4] Add typesense resource --- src/Squadron.sln | 28 +++++++++ src/Typesense.Tests/Typesense.Tests.csproj | 22 +++++++ src/Typesense.Tests/TypesenseResourceTests.cs | 58 +++++++++++++++++++ src/Typesense.Tests/xunit.runner.json | 4 ++ src/Typesense/Typesense.csproj | 11 ++++ src/Typesense/TypesenseDefaultOptions.cs | 42 ++++++++++++++ src/Typesense/TypesenseResource.cs | 37 ++++++++++++ src/Typesense/TypesenseStatus.cs | 47 +++++++++++++++ 8 files changed, 249 insertions(+) create mode 100644 src/Typesense.Tests/Typesense.Tests.csproj create mode 100644 src/Typesense.Tests/TypesenseResourceTests.cs create mode 100644 src/Typesense.Tests/xunit.runner.json create mode 100644 src/Typesense/Typesense.csproj create mode 100644 src/Typesense/TypesenseDefaultOptions.cs create mode 100644 src/Typesense/TypesenseResource.cs create mode 100644 src/Typesense/TypesenseStatus.cs diff --git a/src/Squadron.sln b/src/Squadron.sln index 679acab..d28247e 100644 --- a/src/Squadron.sln +++ b/src/Squadron.sln @@ -90,6 +90,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClickHouse.Tests", "ClickHo EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClickHouse", "ClickHouse\ClickHouse.csproj", "{8A09F209-2008-4E4E-A524-DA2D35032FCD}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Typesense", "Typesense\Typesense.csproj", "{40DF4F0E-2D25-491D-BBF0-AC760C3FF530}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Typesense.Tests", "Typesense.Tests\Typesense.Tests.csproj", "{803EB4D7-5CEA-46F1-8A0C-9BB290C5A3D7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -616,6 +620,30 @@ Global {8A09F209-2008-4E4E-A524-DA2D35032FCD}.Release|x64.Build.0 = Release|Any CPU {8A09F209-2008-4E4E-A524-DA2D35032FCD}.Release|x86.ActiveCfg = Release|Any CPU {8A09F209-2008-4E4E-A524-DA2D35032FCD}.Release|x86.Build.0 = Release|Any CPU + {40DF4F0E-2D25-491D-BBF0-AC760C3FF530}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {40DF4F0E-2D25-491D-BBF0-AC760C3FF530}.Debug|Any CPU.Build.0 = Debug|Any CPU + {40DF4F0E-2D25-491D-BBF0-AC760C3FF530}.Debug|x64.ActiveCfg = Debug|Any CPU + {40DF4F0E-2D25-491D-BBF0-AC760C3FF530}.Debug|x64.Build.0 = Debug|Any CPU + {40DF4F0E-2D25-491D-BBF0-AC760C3FF530}.Debug|x86.ActiveCfg = Debug|Any CPU + {40DF4F0E-2D25-491D-BBF0-AC760C3FF530}.Debug|x86.Build.0 = Debug|Any CPU + {40DF4F0E-2D25-491D-BBF0-AC760C3FF530}.Release|Any CPU.ActiveCfg = Release|Any CPU + {40DF4F0E-2D25-491D-BBF0-AC760C3FF530}.Release|Any CPU.Build.0 = Release|Any CPU + {40DF4F0E-2D25-491D-BBF0-AC760C3FF530}.Release|x64.ActiveCfg = Release|Any CPU + {40DF4F0E-2D25-491D-BBF0-AC760C3FF530}.Release|x64.Build.0 = Release|Any CPU + {40DF4F0E-2D25-491D-BBF0-AC760C3FF530}.Release|x86.ActiveCfg = Release|Any CPU + {40DF4F0E-2D25-491D-BBF0-AC760C3FF530}.Release|x86.Build.0 = Release|Any CPU + {803EB4D7-5CEA-46F1-8A0C-9BB290C5A3D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {803EB4D7-5CEA-46F1-8A0C-9BB290C5A3D7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {803EB4D7-5CEA-46F1-8A0C-9BB290C5A3D7}.Debug|x64.ActiveCfg = Debug|Any CPU + {803EB4D7-5CEA-46F1-8A0C-9BB290C5A3D7}.Debug|x64.Build.0 = Debug|Any CPU + {803EB4D7-5CEA-46F1-8A0C-9BB290C5A3D7}.Debug|x86.ActiveCfg = Debug|Any CPU + {803EB4D7-5CEA-46F1-8A0C-9BB290C5A3D7}.Debug|x86.Build.0 = Debug|Any CPU + {803EB4D7-5CEA-46F1-8A0C-9BB290C5A3D7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {803EB4D7-5CEA-46F1-8A0C-9BB290C5A3D7}.Release|Any CPU.Build.0 = Release|Any CPU + {803EB4D7-5CEA-46F1-8A0C-9BB290C5A3D7}.Release|x64.ActiveCfg = Release|Any CPU + {803EB4D7-5CEA-46F1-8A0C-9BB290C5A3D7}.Release|x64.Build.0 = Release|Any CPU + {803EB4D7-5CEA-46F1-8A0C-9BB290C5A3D7}.Release|x86.ActiveCfg = Release|Any CPU + {803EB4D7-5CEA-46F1-8A0C-9BB290C5A3D7}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Typesense.Tests/Typesense.Tests.csproj b/src/Typesense.Tests/Typesense.Tests.csproj new file mode 100644 index 0000000..28a5c42 --- /dev/null +++ b/src/Typesense.Tests/Typesense.Tests.csproj @@ -0,0 +1,22 @@ + + + + + Squadron.Typesense.Tests + + + + + + + + + Always + + + + + + + + diff --git a/src/Typesense.Tests/TypesenseResourceTests.cs b/src/Typesense.Tests/TypesenseResourceTests.cs new file mode 100644 index 0000000..1c6e782 --- /dev/null +++ b/src/Typesense.Tests/TypesenseResourceTests.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Typesense; +using Typesense.Setup; +using Xunit; + +namespace Squadron +{ + public class TypesenseResourceTests : IClassFixture + { + private readonly TypesenseResource _typesenseResource; + + public TypesenseResourceTests(TypesenseResource typesenseResource) + { + _typesenseResource = typesenseResource; + } + + [Fact] + public void CreateAndGetDocument_NoError() + { + var provider = new ServiceCollection() + .AddTypesenseClient(config => + { + config.ApiKey = _typesenseResource.ApiKey; + config.Nodes = new List { new Node("localhost", _typesenseResource.Port.ToString(), "http") }; + }, enableHttpCompression: false) + .BuildServiceProvider(); + + ITypesenseClient client = provider.GetRequiredService(); + + //Act + Action action = () => + { + var schema = new Schema("users", + new[] { new Field("id", FieldType.String), + new Field("name", FieldType.String) }); + + client.CreateCollection(schema); + client.CreateDocument("users", new User{ Id = "1", Name = "John Doe" }); + + Task _ = client.RetrieveDocument("users", "1"); + }; + + //Assert + action.Should().NotThrow(); + } + } + + internal class User + { + public string Id { get; set; } + + public string Name { get; set; } + } +} diff --git a/src/Typesense.Tests/xunit.runner.json b/src/Typesense.Tests/xunit.runner.json new file mode 100644 index 0000000..bd5fcdd --- /dev/null +++ b/src/Typesense.Tests/xunit.runner.json @@ -0,0 +1,4 @@ +{ + "appDomain": "denied", + "parallelizeAssembly": true +} \ No newline at end of file diff --git a/src/Typesense/Typesense.csproj b/src/Typesense/Typesense.csproj new file mode 100644 index 0000000..fdc8ab0 --- /dev/null +++ b/src/Typesense/Typesense.csproj @@ -0,0 +1,11 @@ + + + + Squadron.Typesense + + + + + + + diff --git a/src/Typesense/TypesenseDefaultOptions.cs b/src/Typesense/TypesenseDefaultOptions.cs new file mode 100644 index 0000000..cad68fd --- /dev/null +++ b/src/Typesense/TypesenseDefaultOptions.cs @@ -0,0 +1,42 @@ +using System; +using System.IO; + +namespace Squadron +{ + /// + /// Default RavenDB resource options + /// + public class TypesenseDefaultOptions : ContainerResourceOptions + { + /// + /// Configure resource options + /// + /// + public override void Configure(ContainerResourceBuilder builder) + { + var password = Guid.NewGuid().ToString("N"); + var localdata = PrepareLocalDirectory(); + + builder + .Name("typesense") + .Image("typesense/typesense:27.1") + .InternalPort(8108) + .Password(password) + .AddCmd("--api-key", password) + .AddCmd("--data-dir", "/data") + .AddVolume($"{localdata}:/data") + .PreferLocalImage(); + } + + private static string PrepareLocalDirectory() + { + var localdata = Path.GetTempPath(); + if (!Directory.Exists(localdata)) + { + Directory.CreateDirectory(localdata); + } + + return localdata; + } + } +} diff --git a/src/Typesense/TypesenseResource.cs b/src/Typesense/TypesenseResource.cs new file mode 100644 index 0000000..a6c98af --- /dev/null +++ b/src/Typesense/TypesenseResource.cs @@ -0,0 +1,37 @@ +using System; +using System.Threading.Tasks; +using Xunit; + +namespace Squadron +{ + /// + public class TypesenseResource : TypesenseResource + { + } + + /// + /// Represents a Typesense resource that can be used by unit tests. + /// + /// + public class TypesenseResource + : ContainerResource, + IAsyncLifetime + where TOptions : ContainerResourceOptions, new() + { + public string BaseUrl { get; private set; } + public int Port { get; private set; } + public string ApiKey { get; set; } + + /// + public override async Task InitializeAsync() + { + await base.InitializeAsync(); + BaseUrl = $"http://{Manager.Instance.Address}:{Manager.Instance.HostPort}"; + Port = Manager.Instance.HostPort; + ApiKey = Settings.Password; + + await Initializer.WaitAsync( + new TypesenseStatus(BaseUrl, ApiKey)); + } + } +} diff --git a/src/Typesense/TypesenseStatus.cs b/src/Typesense/TypesenseStatus.cs new file mode 100644 index 0000000..a5ec399 --- /dev/null +++ b/src/Typesense/TypesenseStatus.cs @@ -0,0 +1,47 @@ +using System; +using System.IO; +using System.Net.Http; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace Squadron +{ + /// + /// Status checker for Typesense + /// + /// + public class TypesenseStatus : IResourceStatusProvider + { + private readonly string _baseUrl; + private readonly string _apiKey; + + /// + /// Initializes a new instance of the class. + /// + /// The ConnectionString + public TypesenseStatus(string baseUrl, string apiKey) + { + _baseUrl = baseUrl; + _apiKey = apiKey; + } + + /// + public async Task IsReadyAsync(CancellationToken cancellationToken) + { + var httpClient = new HttpClient() { BaseAddress = new Uri(_baseUrl) }; + httpClient.DefaultRequestHeaders.Add("X-TYPESENSE-API-KEY", _apiKey); + HttpResponseMessage response = await httpClient.GetAsync("/health", cancellationToken); + if (response.IsSuccessStatusCode) + { + Stream stream = await response.Content.ReadAsStreamAsync(); + using JsonDocument jsonDocument = await JsonDocument.ParseAsync(stream, cancellationToken: cancellationToken); + JsonElement jsonResponse = jsonDocument.RootElement; + + return new Status { IsReady = jsonResponse.GetProperty("ok").GetBoolean() }; + } + + return new Status { IsReady = false }; + } + } +} From 6f64b24841fff9891f0a4cc0b4bc04c82a4f3819 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 7 Oct 2024 20:04:57 +0200 Subject: [PATCH 2/4] Remove .net7 --- src/Core/Core.csproj | 11 ++++++----- src/Dependencies.props | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index afd27f7..3f7a1df 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -13,11 +13,11 @@ - - - - - + + + + + @@ -29,6 +29,7 @@ + diff --git a/src/Dependencies.props b/src/Dependencies.props index 44bd57c..81efed5 100644 --- a/src/Dependencies.props +++ b/src/Dependencies.props @@ -4,7 +4,7 @@ - net6.0;net7.0;net8.0 - netstandard2.0;net6.0;net7.0 + net6.0;net8.0 + netstandard2.0;net6.0;net8.0 From 22b86af4b4975d29fd5cc5c04914ec79567bd3e2 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 7 Oct 2024 20:33:04 +0200 Subject: [PATCH 3/4] Nuget bump --- src/Core/Core.csproj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index 3f7a1df..7a40674 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -29,9 +29,8 @@ - - + From e119f65b7672ef814e68dd98390fb362c7c8536d Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 9 Oct 2024 20:08:35 +0200 Subject: [PATCH 4/4] Static API Key --- src/Typesense/TypesenseDefaultOptions.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Typesense/TypesenseDefaultOptions.cs b/src/Typesense/TypesenseDefaultOptions.cs index cad68fd..886c7fa 100644 --- a/src/Typesense/TypesenseDefaultOptions.cs +++ b/src/Typesense/TypesenseDefaultOptions.cs @@ -14,15 +14,15 @@ public class TypesenseDefaultOptions : ContainerResourceOptions /// public override void Configure(ContainerResourceBuilder builder) { - var password = Guid.NewGuid().ToString("N"); var localdata = PrepareLocalDirectory(); + var apiKey = "secretKey"; builder .Name("typesense") .Image("typesense/typesense:27.1") .InternalPort(8108) - .Password(password) - .AddCmd("--api-key", password) + .Password(apiKey) + .AddCmd("--api-key", apiKey) .AddCmd("--data-dir", "/data") .AddVolume($"{localdata}:/data") .PreferLocalImage();