Skip to content

Commit 176a09d

Browse files
authored
Add typesense resource (#143)
* Add typesense resource * Remove .net7 * Nuget bump * Static API Key
1 parent 37c1e47 commit 176a09d

File tree

9 files changed

+230
-1
lines changed

9 files changed

+230
-1
lines changed

global.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"sdk": {
3-
"version": "9.0.101",
3+
"version": "9.0.203",
44
"rollForward": "minor"
55
}
66
}

src/Squadron.sln

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gitea", "Gitea\Gitea.csproj
9494
EndProject
9595
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gitea.Tests", "Gitea.Tests\Gitea.Tests.csproj", "{8FFFFA62-88DC-44BB-A451-E74455AFC8D0}"
9696
EndProject
97+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Typesense", "Typesense\Typesense.csproj", "{20885D12-9998-40F1-8153-22B3C0ADAAD2}"
98+
EndProject
99+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Typesense.Tests", "Typesense.Tests\Typesense.Tests.csproj", "{84CBA1BF-7C75-491E-813A-9F1987F32D63}"
100+
EndProject
97101
Global
98102
GlobalSection(SolutionConfigurationPlatforms) = preSolution
99103
Debug|Any CPU = Debug|Any CPU
@@ -644,6 +648,10 @@ Global
644648
{8FFFFA62-88DC-44BB-A451-E74455AFC8D0}.Release|x64.Build.0 = Release|Any CPU
645649
{8FFFFA62-88DC-44BB-A451-E74455AFC8D0}.Release|x86.ActiveCfg = Release|Any CPU
646650
{8FFFFA62-88DC-44BB-A451-E74455AFC8D0}.Release|x86.Build.0 = Release|Any CPU
651+
{20885D12-9998-40F1-8153-22B3C0ADAAD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
652+
{20885D12-9998-40F1-8153-22B3C0ADAAD2}.Debug|Any CPU.Build.0 = Debug|Any CPU
653+
{84CBA1BF-7C75-491E-813A-9F1987F32D63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
654+
{84CBA1BF-7C75-491E-813A-9F1987F32D63}.Debug|Any CPU.Build.0 = Debug|Any CPU
647655
EndGlobalSection
648656
GlobalSection(SolutionProperties) = preSolution
649657
HideSolutionNode = FALSE
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<Import Project="$(CCTestProjectProps)" Condition="Exists('$(CCTestProjectProps)')" />
3+
4+
<PropertyGroup>
5+
<AssemblyName>Squadron.Typesense.Tests</AssemblyName>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<ProjectReference Include="..\Typesense\Typesense.csproj" />
10+
</ItemGroup>
11+
12+
<ItemGroup>
13+
<None Update="xunit.runner.json">
14+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
15+
</None>
16+
</ItemGroup>
17+
18+
<ItemGroup>
19+
<PackageReference Include="Typesense" Version="7.21.0" />
20+
</ItemGroup>
21+
22+
</Project>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
using FluentAssertions;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using Typesense;
7+
using Typesense.Setup;
8+
using Xunit;
9+
10+
namespace Squadron
11+
{
12+
public class TypesenseResourceTests : IClassFixture<TypesenseResource>
13+
{
14+
private readonly TypesenseResource _typesenseResource;
15+
16+
public TypesenseResourceTests(TypesenseResource typesenseResource)
17+
{
18+
_typesenseResource = typesenseResource;
19+
}
20+
21+
[Fact]
22+
public void CreateAndGetDocument_NoError()
23+
{
24+
var provider = new ServiceCollection()
25+
.AddTypesenseClient(config =>
26+
{
27+
config.ApiKey = _typesenseResource.ApiKey;
28+
config.Nodes = new List<Node> { new Node("localhost", _typesenseResource.Port.ToString(), "http") };
29+
}, enableHttpCompression: false)
30+
.BuildServiceProvider();
31+
32+
ITypesenseClient client = provider.GetRequiredService<ITypesenseClient>();
33+
34+
//Act
35+
Action action = () =>
36+
{
37+
var schema = new Schema("users",
38+
new[] { new Field("id", FieldType.String),
39+
new Field("name", FieldType.String) });
40+
41+
client.CreateCollection(schema);
42+
client.CreateDocument("users", new User{ Id = "1", Name = "John Doe" });
43+
44+
Task<User> _ = client.RetrieveDocument<User>("users", "1");
45+
};
46+
47+
//Assert
48+
action.Should().NotThrow();
49+
}
50+
}
51+
52+
internal class User
53+
{
54+
public string Id { get; set; }
55+
56+
public string Name { get; set; }
57+
}
58+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"appDomain": "denied",
3+
"parallelizeAssembly": true
4+
}

src/Typesense/Typesense.csproj

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<Import Project="$(CCResourceProjectProps)" Condition="Exists('$(CCResourceProjectProps)')" />
3+
<PropertyGroup>
4+
<AssemblyName>Squadron.Typesense</AssemblyName>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<ProjectReference Include="..\Core\Core.csproj" />
9+
</ItemGroup>
10+
11+
</Project>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System;
2+
using System.IO;
3+
4+
namespace Squadron
5+
{
6+
/// <summary>
7+
/// Default RavenDB resource options
8+
/// </summary>
9+
public class TypesenseDefaultOptions : ContainerResourceOptions
10+
{
11+
/// <summary>
12+
/// Configure resource options
13+
/// </summary>
14+
/// <param name="builder"></param>
15+
public override void Configure(ContainerResourceBuilder builder)
16+
{
17+
var localdata = PrepareLocalDirectory();
18+
var apiKey = "secretKey";
19+
20+
builder
21+
.Name("typesense")
22+
.Image("typesense/typesense:27.1")
23+
.InternalPort(8108)
24+
.Password(apiKey)
25+
.AddCmd("--api-key", apiKey)
26+
.AddCmd("--data-dir", "/data")
27+
.AddVolume($"{localdata}:/data")
28+
.PreferLocalImage();
29+
}
30+
31+
private static string PrepareLocalDirectory()
32+
{
33+
var localdata = Path.GetTempPath();
34+
if (!Directory.Exists(localdata))
35+
{
36+
Directory.CreateDirectory(localdata);
37+
}
38+
39+
return localdata;
40+
}
41+
}
42+
}

src/Typesense/TypesenseResource.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using Xunit;
4+
5+
namespace Squadron
6+
{
7+
/// <inheritdoc/>
8+
public class TypesenseResource : TypesenseResource<TypesenseDefaultOptions>
9+
{
10+
}
11+
12+
/// <summary>
13+
/// Represents a Typesense resource that can be used by unit tests.
14+
/// </summary>
15+
/// <seealso cref="IDisposable"/>
16+
public class TypesenseResource<TOptions>
17+
: ContainerResource<TOptions>,
18+
IAsyncLifetime
19+
where TOptions : ContainerResourceOptions, new()
20+
{
21+
public string BaseUrl { get; private set; }
22+
public int Port { get; private set; }
23+
public string ApiKey { get; set; }
24+
25+
/// <inheritdoc cref="IAsyncLifetime"/>
26+
public override async Task InitializeAsync()
27+
{
28+
await base.InitializeAsync();
29+
BaseUrl = $"http://{Manager.Instance.Address}:{Manager.Instance.HostPort}";
30+
Port = Manager.Instance.HostPort;
31+
ApiKey = Settings.Password;
32+
33+
await Initializer.WaitAsync(
34+
new TypesenseStatus(BaseUrl, ApiKey));
35+
}
36+
}
37+
}

src/Typesense/TypesenseStatus.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System;
2+
using System.IO;
3+
using System.Net.Http;
4+
using System.Text.Json;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
8+
namespace Squadron
9+
{
10+
/// <summary>
11+
/// Status checker for Typesense
12+
/// </summary>
13+
/// <seealso cref="IResourceStatusProvider" />
14+
public class TypesenseStatus : IResourceStatusProvider
15+
{
16+
private readonly string _baseUrl;
17+
private readonly string _apiKey;
18+
19+
/// <summary>
20+
/// Initializes a new instance of the <see cref="TypesenseStatus"/> class.
21+
/// </summary>
22+
/// <param name="baseUrl">The ConnectionString</param>
23+
public TypesenseStatus(string baseUrl, string apiKey)
24+
{
25+
_baseUrl = baseUrl;
26+
_apiKey = apiKey;
27+
}
28+
29+
/// <inheritdoc/>
30+
public async Task<Status> IsReadyAsync(CancellationToken cancellationToken)
31+
{
32+
var httpClient = new HttpClient() { BaseAddress = new Uri(_baseUrl) };
33+
httpClient.DefaultRequestHeaders.Add("X-TYPESENSE-API-KEY", _apiKey);
34+
HttpResponseMessage response = await httpClient.GetAsync("/health", cancellationToken);
35+
if (response.IsSuccessStatusCode)
36+
{
37+
Stream stream = await response.Content.ReadAsStreamAsync();
38+
using JsonDocument jsonDocument = await JsonDocument.ParseAsync(stream, cancellationToken: cancellationToken);
39+
JsonElement jsonResponse = jsonDocument.RootElement;
40+
41+
return new Status { IsReady = jsonResponse.GetProperty("ok").GetBoolean() };
42+
}
43+
44+
return new Status { IsReady = false };
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)