diff --git a/.github/codecov.yml b/.github/codecov.yml
index 50efcbdde1..961484dd88 100644
--- a/.github/codecov.yml
+++ b/.github/codecov.yml
@@ -107,6 +107,8 @@ flags:
carryforward: true
SqlServer:
carryforward: true
+ SurrealDb:
+ carryforward: true
System:
carryforward: true
UI:
diff --git a/.github/labeler.yml b/.github/labeler.yml
index fd15465173..40bb7f54f9 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -183,6 +183,9 @@ sqlserver:
- changed-files:
- any-glob-to-any-file: [src/HealthChecks.SqlServer/**/*]
+surrealdb:
+ - src/HealthChecks.SurrealDb/**/*
+
system:
- changed-files:
- any-glob-to-any-file: [src/HealthChecks.System/**/*]
diff --git a/.github/workflows/healthchecks_surrealdb_cd.yml b/.github/workflows/healthchecks_surrealdb_cd.yml
new file mode 100644
index 0000000000..85f5bf3fa8
--- /dev/null
+++ b/.github/workflows/healthchecks_surrealdb_cd.yml
@@ -0,0 +1,16 @@
+name: HealthChecks SurrealDB CD
+
+on:
+ push:
+ tags:
+ - release-surrealdb-*
+ - release-all-*
+
+jobs:
+ build:
+ uses: ./.github/workflows/reusable_cd_workflow.yml
+ secrets: inherit
+ with:
+ BUILD_CONFIG: Release
+ PROJECT_PATH: ./src/HealthChecks.SurrealDb/HealthChecks.SurrealDb.csproj
+ PACKAGE_NAME: AspNetCore.HealthChecks.SurrealDb
diff --git a/.github/workflows/healthchecks_surrealdb_cd_preview.yml b/.github/workflows/healthchecks_surrealdb_cd_preview.yml
new file mode 100644
index 0000000000..d38e0f111a
--- /dev/null
+++ b/.github/workflows/healthchecks_surrealdb_cd_preview.yml
@@ -0,0 +1,17 @@
+name: HealthChecks SurrealDB Preview CD
+
+on:
+ push:
+ tags:
+ - preview-surrealdb-*
+ - preview-all-*
+
+jobs:
+ build:
+ uses: ./.github/workflows/reusable_cd_preview_workflow.yml
+ secrets: inherit
+ with:
+ BUILD_CONFIG: Release
+ VERSION_SUFFIX_PREFIX: rc1
+ PROJECT_PATH: ./src/HealthChecks.SurrealDb/HealthChecks.SurrealDb.csproj
+ PACKAGE_NAME: AspNetCore.HealthChecks.SurrealDb
diff --git a/.github/workflows/healthchecks_surrealdb_ci.yml b/.github/workflows/healthchecks_surrealdb_ci.yml
new file mode 100644
index 0000000000..d6ae7feec8
--- /dev/null
+++ b/.github/workflows/healthchecks_surrealdb_ci.yml
@@ -0,0 +1,76 @@
+name: HealthChecks SurrealDB CI
+
+on:
+ workflow_dispatch:
+ push:
+ branches: [master]
+ paths:
+ - src/HealthChecks.SurrealDb/**
+ - test/HealthChecks.SurrealDb.Tests/**
+ - test/_SHARED/**
+ - .github/workflows/healthchecks_surrealdb_ci.yml
+ - Directory.Build.props
+ - Directory.Build.targets
+ - Directory.Packages.props
+ tags-ignore:
+ - release-*
+ - preview-*
+
+ pull_request:
+ branches: [master]
+ paths:
+ - src/HealthChecks.SurrealDb/**
+ - test/HealthChecks.SurrealDb.Tests/**
+ - test/_SHARED/**
+ - .github/workflows/healthchecks_surrealdb_ci.yml
+ - Directory.Build.props
+ - Directory.Build.targets
+ - Directory.Packages.props
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Start SurrealDB
+ uses: surrealdb/setup-surreal@v2
+ with:
+ surrealdb_version: latest
+ surrealdb_port: 8000
+ surrealdb_username: root
+ surrealdb_password: root
+ surrealdb_auth: true
+ surrealdb_strict:
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: |
+ 8.0.x
+ 9.0.x
+ - name: Restore
+ run: |
+ dotnet restore ./src/HealthChecks.SurrealDb/HealthChecks.SurrealDb.csproj &&
+ dotnet restore ./test/HealthChecks.SurrealDb.Tests/HealthChecks.SurrealDb.Tests.csproj
+ - name: Check formatting
+ run: |
+ dotnet format --no-restore --verify-no-changes --severity warn ./src/HealthChecks.SurrealDb/HealthChecks.SurrealDb.csproj || (echo "Run 'dotnet format' to fix issues" && exit 1) &&
+ dotnet format --no-restore --verify-no-changes --severity warn ./test/HealthChecks.SurrealDb.Tests/HealthChecks.SurrealDb.Tests.csproj || (echo "Run 'dotnet format' to fix issues" && exit 1)
+ - name: Build
+ run: |
+ dotnet build --no-restore ./src/HealthChecks.SurrealDb/HealthChecks.SurrealDb.csproj &&
+ dotnet build --no-restore ./test/HealthChecks.SurrealDb.Tests/HealthChecks.SurrealDb.Tests.csproj
+ - name: Test
+ run: >
+ dotnet test
+ ./test/HealthChecks.SurrealDb.Tests/HealthChecks.SurrealDb.Tests.csproj
+ --no-restore
+ --no-build
+ --collect "XPlat Code Coverage"
+ --results-directory .coverage
+ --
+ DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover
+ - name: Upload Coverage
+ uses: codecov/codecov-action@v3
+ with:
+ flags: SurrealDb
+ directory: .coverage
diff --git a/AspNetCore.Diagnostics.HealthChecks.sln b/AspNetCore.Diagnostics.HealthChecks.sln
index 64d8fec892..6249bfaf2a 100644
--- a/AspNetCore.Diagnostics.HealthChecks.sln
+++ b/AspNetCore.Diagnostics.HealthChecks.sln
@@ -315,6 +315,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthChecks.Rabbitmq.v6",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthChecks.RabbitMQ.v6.Tests", "test\HealthChecks.RabbitMQ.v6.Tests\HealthChecks.RabbitMQ.v6.Tests.csproj", "{2787F63E-ABEA-9461-CDF3-97FE7C5C3DCC}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HealthChecks.SurrealDb", "src\HealthChecks.SurrealDb\HealthChecks.SurrealDb.csproj", "{97DAA09F-E0FA-4D5B-A72B-896161E570DA}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HealthChecks.SurrealDb.Tests", "test\HealthChecks.SurrealDb.Tests\HealthChecks.SurrealDb.Tests.csproj", "{44BB97EE-88DB-4C9B-8195-2C6D889AE391}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthChecks.ClickHouse", "src\HealthChecks.ClickHouse\HealthChecks.ClickHouse.csproj", "{96E2B0A3-02BD-456B-8888-4D96DABA99EB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthChecks.ClickHouse.Tests", "test\HealthChecks.ClickHouse.Tests\HealthChecks.ClickHouse.Tests.csproj", "{2FB5CB9F-F870-48DE-BD1D-306AE86A67CA}"
@@ -885,6 +889,14 @@ Global
{2787F63E-ABEA-9461-CDF3-97FE7C5C3DCC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2787F63E-ABEA-9461-CDF3-97FE7C5C3DCC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2787F63E-ABEA-9461-CDF3-97FE7C5C3DCC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {97DAA09F-E0FA-4D5B-A72B-896161E570DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {97DAA09F-E0FA-4D5B-A72B-896161E570DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {97DAA09F-E0FA-4D5B-A72B-896161E570DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {97DAA09F-E0FA-4D5B-A72B-896161E570DA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {44BB97EE-88DB-4C9B-8195-2C6D889AE391}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {44BB97EE-88DB-4C9B-8195-2C6D889AE391}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {44BB97EE-88DB-4C9B-8195-2C6D889AE391}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {44BB97EE-88DB-4C9B-8195-2C6D889AE391}.Release|Any CPU.Build.0 = Release|Any CPU
{96E2B0A3-02BD-456B-8888-4D96DABA99EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{96E2B0A3-02BD-456B-8888-4D96DABA99EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{96E2B0A3-02BD-456B-8888-4D96DABA99EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -1038,6 +1050,8 @@ Global
{D49CF52C-9D21-4D98-8A15-A2B259E9C003} = {FF4414C2-8863-4ADA-8A1D-4B9F25C361FE}
{C76D7349-A3D2-7277-93C6-EE92E8E447A5} = {2A3FD988-2BB8-43CF-B3A2-B70E648259D4}
{2787F63E-ABEA-9461-CDF3-97FE7C5C3DCC} = {FF4414C2-8863-4ADA-8A1D-4B9F25C361FE}
+ {97DAA09F-E0FA-4D5B-A72B-896161E570DA} = {2A3FD988-2BB8-43CF-B3A2-B70E648259D4}
+ {44BB97EE-88DB-4C9B-8195-2C6D889AE391} = {FF4414C2-8863-4ADA-8A1D-4B9F25C361FE}
{96E2B0A3-02BD-456B-8888-4D96DABA99EB} = {2A3FD988-2BB8-43CF-B3A2-B70E648259D4}
{2FB5CB9F-F870-48DE-BD1D-306AE86A67CA} = {FF4414C2-8863-4ADA-8A1D-4B9F25C361FE}
EndGlobalSection
diff --git a/Directory.Packages.props b/Directory.Packages.props
index af73e3b085..14504ae867 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -95,6 +95,7 @@
+
diff --git a/build/versions.props b/build/versions.props
index 1acaa55c81..3f14c77254 100644
--- a/build/versions.props
+++ b/build/versions.props
@@ -58,6 +58,7 @@
9.0.0
9.0.0
9.0.0
+ 9.0.0
9.0.0
9.0.0
9.0.0
diff --git a/src/HealthChecks.ClickHouse/HealthChecks.ClickHouse.csproj b/src/HealthChecks.ClickHouse/HealthChecks.ClickHouse.csproj
index 124a2eb6e7..7036eb3f29 100644
--- a/src/HealthChecks.ClickHouse/HealthChecks.ClickHouse.csproj
+++ b/src/HealthChecks.ClickHouse/HealthChecks.ClickHouse.csproj
@@ -5,6 +5,7 @@
$(PackageTags);Beat;ClickHouse
HealthChecks.ClickHouse is a health check for ClickHouse.
$(HealthCheckClickHouse)
+ false
diff --git a/src/HealthChecks.SurrealDb/DependencyInjection/SurrealDbHealthCheckBuilderExtensions.cs b/src/HealthChecks.SurrealDb/DependencyInjection/SurrealDbHealthCheckBuilderExtensions.cs
new file mode 100644
index 0000000000..12107ecb1d
--- /dev/null
+++ b/src/HealthChecks.SurrealDb/DependencyInjection/SurrealDbHealthCheckBuilderExtensions.cs
@@ -0,0 +1,53 @@
+using HealthChecks.SurrealDb;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using SurrealDb.Net;
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+///
+/// Extension methods to configure .
+///
+public static class SurrealDbHealthCheckBuilderExtensions
+{
+ private const string NAME = "surrealdb";
+
+ ///
+ /// Add a health check for SurrealDB.
+ ///
+ /// The .
+ ///
+ /// An optional factory to obtain instance.
+ /// When not provided, is simply resolved from .
+ ///
+ /// The health check name. Optional. If null the type name 'surrealdb' will be used for the name.
+ ///
+ /// The that should be reported when the health check fails. Optional. If null then
+ /// the default status of will be reported.
+ ///
+ /// A list of tags that can be used to filter sets of health checks. Optional.
+ /// An optional representing the timeout of the check.
+ /// The specified .
+ public static IHealthChecksBuilder AddSurreal(
+ this IHealthChecksBuilder builder,
+ Func? factory = null,
+ string? name = default,
+ HealthStatus? failureStatus = default,
+ IEnumerable? tags = default,
+ TimeSpan? timeout = default)
+ {
+ return builder.Add(new HealthCheckRegistration(
+ name ?? NAME,
+ sp => Factory(sp, factory),
+ failureStatus,
+ tags,
+ timeout));
+
+ static SurrealDbHealthCheck Factory(IServiceProvider sp, Func? factory)
+ {
+ // The user might have registered a factory for SurrealDbClient type, but not for the abstraction (ISurrealDbClient).
+ // That is why we try to resolve ISurrealDbClient first.
+ ISurrealDbClient client = factory?.Invoke(sp) ?? sp.GetService() ?? sp.GetRequiredService();
+ return new(client);
+ }
+ }
+}
diff --git a/src/HealthChecks.SurrealDb/HealthChecks.SurrealDb.csproj b/src/HealthChecks.SurrealDb/HealthChecks.SurrealDb.csproj
new file mode 100644
index 0000000000..54b83b1c23
--- /dev/null
+++ b/src/HealthChecks.SurrealDb/HealthChecks.SurrealDb.csproj
@@ -0,0 +1,15 @@
+
+
+
+ netstandard2.1;$(DefaultNetCoreApp)
+ $(PackageTags);SurrealDb
+ HealthChecks.SurrealDb is the health check package for SurrealDB.
+ $(HealthCheckSurrealDb)
+ false
+
+
+
+
+
+
+
diff --git a/src/HealthChecks.SurrealDb/README.md b/src/HealthChecks.SurrealDb/README.md
new file mode 100644
index 0000000000..dc747278f9
--- /dev/null
+++ b/src/HealthChecks.SurrealDb/README.md
@@ -0,0 +1,15 @@
+## SurrealDB Health Check
+
+This health check verifies the ability to communicate with [SurrealDb](https://surrealdb.com). It uses the provided [ISurrealDbClient](https://surrealdb.com/docs/sdk/dotnet).
+
+### Defaults
+
+By default, the `ISurrealDbClient` instance is resolved from service provider.
+
+```csharp
+void Configure(IHealthChecksBuilder builder)
+{
+ builder.Services.AddSurreal("Server=http://localhost:8000;Namespace=test;Database=test");
+ builder.AddHealthChecks().AddSurreal();
+}
+```
diff --git a/src/HealthChecks.SurrealDb/SurrealDbHealthCheck.cs b/src/HealthChecks.SurrealDb/SurrealDbHealthCheck.cs
new file mode 100644
index 0000000000..c4248d8a93
--- /dev/null
+++ b/src/HealthChecks.SurrealDb/SurrealDbHealthCheck.cs
@@ -0,0 +1,34 @@
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using SurrealDb.Net;
+
+namespace HealthChecks.SurrealDb;
+
+///
+/// A health check for SurrealDb services.
+///
+public class SurrealDbHealthCheck : IHealthCheck
+{
+ private readonly ISurrealDbClient _client;
+
+ public SurrealDbHealthCheck(ISurrealDbClient client)
+ {
+ _client = Guard.ThrowIfNull(client);
+ }
+
+ ///
+ public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
+ {
+ try
+ {
+ await _client.Connect(cancellationToken).ConfigureAwait(false);
+
+ return await _client.Health(cancellationToken).ConfigureAwait(false)
+ ? HealthCheckResult.Healthy()
+ : HealthCheckResult.Unhealthy();
+ }
+ catch (Exception ex)
+ {
+ return new HealthCheckResult(context.Registration.FailureStatus, exception: ex);
+ }
+ }
+}
diff --git a/test/HealthChecks.SurrealDb.Tests/DependencyInjection/RegistrationTests.cs b/test/HealthChecks.SurrealDb.Tests/DependencyInjection/RegistrationTests.cs
new file mode 100644
index 0000000000..290072f18e
--- /dev/null
+++ b/test/HealthChecks.SurrealDb.Tests/DependencyInjection/RegistrationTests.cs
@@ -0,0 +1,70 @@
+using SurrealDb.Net;
+
+namespace HealthChecks.SurrealDb.Tests.DependencyInjection;
+
+public class surrealdb_registration_should
+{
+ private const string ConnectionString = "Server=http://localhost:8000;Namespace=test;Database=test;Username=root;Password=root";
+
+ [Fact]
+ public void add_health_check_when_properly_configured()
+ {
+ var services = new ServiceCollection();
+ services.AddSurreal(ConnectionString);
+ services
+ .AddHealthChecks()
+ .AddSurreal();
+
+ using var serviceProvider = services.BuildServiceProvider();
+ var options = serviceProvider.GetRequiredService>();
+
+ var registration = options.Value.Registrations.First();
+ var check = registration.Factory(serviceProvider);
+
+ registration.Name.ShouldBe("surrealdb");
+ check.ShouldBeOfType();
+ }
+
+ [Fact]
+ public void add_named_health_check_when_properly_configured()
+ {
+ var services = new ServiceCollection();
+ services.AddSurreal(ConnectionString);
+ services
+ .AddHealthChecks()
+ .AddSurreal(name: "my-surrealdb-1");
+
+ using var serviceProvider = services.BuildServiceProvider();
+ var options = serviceProvider.GetRequiredService>();
+
+ var registration = options.Value.Registrations.First();
+ var check = registration.Factory(serviceProvider);
+
+ registration.Name.ShouldBe("my-surrealdb-1");
+ check.ShouldBeOfType();
+ }
+
+ [Fact]
+ public void add_health_check_with_connection_string_factory_when_properly_configured()
+ {
+ var services = new ServiceCollection();
+ services.AddSurreal(ConnectionString);
+ bool factoryCalled = false;
+ services.AddHealthChecks()
+ .AddSurreal(sp =>
+ {
+ factoryCalled = true;
+ return sp.GetRequiredService();
+ });
+
+ using var serviceProvider = services.BuildServiceProvider();
+ var options = serviceProvider.GetRequiredService>();
+
+ var registration = options.Value.Registrations.First();
+ var check = registration.Factory(serviceProvider);
+
+ registration.Name.ShouldBe("surrealdb");
+ check.ShouldBeOfType();
+ factoryCalled.ShouldBeTrue();
+ }
+}
diff --git a/test/HealthChecks.SurrealDb.Tests/Functional/SurrealDbHealthCheckTests.cs b/test/HealthChecks.SurrealDb.Tests/Functional/SurrealDbHealthCheckTests.cs
new file mode 100644
index 0000000000..cdf6c21a76
--- /dev/null
+++ b/test/HealthChecks.SurrealDb.Tests/Functional/SurrealDbHealthCheckTests.cs
@@ -0,0 +1,64 @@
+using System.Net;
+
+namespace HealthChecks.SurrealDb.Tests.Functional;
+
+public class surrealdb_healthcheck_should
+{
+ [Fact]
+ public async Task be_healthy_if_surrealdb_is_available()
+ {
+ const string connectionString = "Server=http://localhost:8000;Namespace=test;Database=test;Username=root;Password=root";
+
+ var webHostBuilder = new WebHostBuilder()
+ .ConfigureServices(services =>
+ {
+ services
+ .AddSurreal(connectionString);
+ services
+ .AddHealthChecks()
+ .AddSurreal(tags: ["surrealdb"]);
+ })
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health", new HealthCheckOptions
+ {
+ Predicate = r => r.Tags.Contains("surrealdb")
+ });
+ });
+
+ using var server = new TestServer(webHostBuilder);
+
+ using var response = await server.CreateRequest("/health").GetAsync();
+
+ response.StatusCode.ShouldBe(HttpStatusCode.OK);
+ }
+
+ [Fact]
+ public async Task be_unhealthy_if_surrealdb_is_not_available()
+ {
+ const string connectionString = "Server=http://localhost:1234;Namespace=test;Database=test;Username=root;Password=root";
+
+ var webHostBuilder = new WebHostBuilder()
+ .ConfigureServices(services =>
+ {
+ services
+ .AddSurreal(connectionString);
+ services
+ .AddHealthChecks()
+ .AddSurreal(tags: ["surrealdb"]);
+ })
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health", new HealthCheckOptions
+ {
+ Predicate = r => r.Tags.Contains("surrealdb")
+ });
+ });
+
+ using var server = new TestServer(webHostBuilder);
+
+ using var response = await server.CreateRequest("/health").GetAsync();
+
+ response.StatusCode.ShouldBe(HttpStatusCode.ServiceUnavailable);
+ }
+}
diff --git a/test/HealthChecks.SurrealDb.Tests/HealthChecks.SurrealDb.Tests.csproj b/test/HealthChecks.SurrealDb.Tests/HealthChecks.SurrealDb.Tests.csproj
new file mode 100644
index 0000000000..1d0531a26b
--- /dev/null
+++ b/test/HealthChecks.SurrealDb.Tests/HealthChecks.SurrealDb.Tests.csproj
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/test/HealthChecks.SurrealDb.Tests/HealthChecks.SurrealDb.approved.txt b/test/HealthChecks.SurrealDb.Tests/HealthChecks.SurrealDb.approved.txt
new file mode 100644
index 0000000000..8fb3ecf195
--- /dev/null
+++ b/test/HealthChecks.SurrealDb.Tests/HealthChecks.SurrealDb.approved.txt
@@ -0,0 +1,15 @@
+namespace HealthChecks.SurrealDb
+{
+ public class SurrealDbHealthCheck : Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck
+ {
+ public SurrealDbHealthCheck(SurrealDb.Net.ISurrealDbClient client) { }
+ public System.Threading.Tasks.Task CheckHealthAsync(Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckContext context, System.Threading.CancellationToken cancellationToken = default) { }
+ }
+}
+namespace Microsoft.Extensions.DependencyInjection
+{
+ public static class SurrealDbHealthCheckBuilderExtensions
+ {
+ public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddSurreal(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, System.Func? factory = null, string? name = null, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default, System.Collections.Generic.IEnumerable? tags = null, System.TimeSpan? timeout = default) { }
+ }
+}
\ No newline at end of file