diff --git a/AspNetCore.Diagnostics.HealthChecks.sln b/AspNetCore.Diagnostics.HealthChecks.sln
index 6249bfaf2a..1af84c5a47 100644
--- a/AspNetCore.Diagnostics.HealthChecks.sln
+++ b/AspNetCore.Diagnostics.HealthChecks.sln
@@ -323,6 +323,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthChecks.ClickHouse", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthChecks.ClickHouse.Tests", "test\HealthChecks.ClickHouse.Tests\HealthChecks.ClickHouse.Tests.csproj", "{2FB5CB9F-F870-48DE-BD1D-306AE86A67CA}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthChecks.Neo4jClient", "src\HealthChecks.Neo4jClient\HealthChecks.Neo4jClient.csproj", "{3B70557A-BC3F-4CEF-8EAB-C57CE07DCEC5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthChecks.Neo4jClient.Tests", "test\HealthChecks.Neo4jClient.Tests\HealthChecks.Neo4jClient.Tests.csproj", "{64624A04-52CF-4558-811A-4FDD8E8FD4E6}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -905,6 +909,14 @@ Global
{2FB5CB9F-F870-48DE-BD1D-306AE86A67CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2FB5CB9F-F870-48DE-BD1D-306AE86A67CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2FB5CB9F-F870-48DE-BD1D-306AE86A67CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3B70557A-BC3F-4CEF-8EAB-C57CE07DCEC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3B70557A-BC3F-4CEF-8EAB-C57CE07DCEC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3B70557A-BC3F-4CEF-8EAB-C57CE07DCEC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3B70557A-BC3F-4CEF-8EAB-C57CE07DCEC5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {64624A04-52CF-4558-811A-4FDD8E8FD4E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {64624A04-52CF-4558-811A-4FDD8E8FD4E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {64624A04-52CF-4558-811A-4FDD8E8FD4E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {64624A04-52CF-4558-811A-4FDD8E8FD4E6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1054,6 +1066,8 @@ Global
{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}
+ {3B70557A-BC3F-4CEF-8EAB-C57CE07DCEC5} = {2A3FD988-2BB8-43CF-B3A2-B70E648259D4}
+ {64624A04-52CF-4558-811A-4FDD8E8FD4E6} = {FF4414C2-8863-4ADA-8A1D-4B9F25C361FE}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2B8C62A1-11B6-469F-874C-A02443256568}
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 9035eadb36..7be64caaf2 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -73,6 +73,7 @@
+
@@ -111,10 +112,10 @@
-
+
-
+
\ No newline at end of file
diff --git a/src/HealthChecks.Neo4jClient/DependencyInjection/Neo4jClientHealthCheckBuilderExtensions.cs b/src/HealthChecks.Neo4jClient/DependencyInjection/Neo4jClientHealthCheckBuilderExtensions.cs
new file mode 100644
index 0000000000..38cf85ae23
--- /dev/null
+++ b/src/HealthChecks.Neo4jClient/DependencyInjection/Neo4jClientHealthCheckBuilderExtensions.cs
@@ -0,0 +1,83 @@
+using HealthChecks.Neo4jClient;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using Neo4jClient;
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+///
+/// Extension methods to configure .
+///
+public static class Neo4jClientHealthCheckBuilderExtensions
+{
+ private const string HEALTH_CHECK_NAME = "neo4j";
+
+ ///
+ /// Add a health check for Neo4j databases.
+ ///
+ /// The extension for .
+ /// A factory to build .
+ /// The health check name. Optional. If null the type name 'neo4j' 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 .
+ public static IHealthChecksBuilder AddNeo4jClient(
+ this IHealthChecksBuilder builder,
+ Func graphClientFactory,
+ string? name = default,
+ HealthStatus? failureStatus = default,
+ IEnumerable? tags = default,
+ TimeSpan? timeout = default)
+ {
+ var healthCheckRegistration = new HealthCheckRegistration(
+ name ?? HEALTH_CHECK_NAME,
+ sp =>
+ {
+ var graphClient = graphClientFactory(sp);
+ var options = new Neo4jClientHealthCheckOptions(graphClient);
+
+ return new Neo4jClientHealthCheck(options);
+ },
+ failureStatus,
+ tags,
+ timeout
+ );
+
+ return builder.Add(healthCheckRegistration);
+ }
+
+ ///
+ /// Add a health check for Neo4j databases.
+ ///
+ /// The extension for .
+ /// instance for health check.
+ /// The health check name. Optional. If null the type name 'neo4j' 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 .
+ public static IHealthChecksBuilder AddNeo4jClient(
+ this IHealthChecksBuilder builder,
+ Neo4jClientHealthCheckOptions healthCheckOptions,
+ string? name = default,
+ HealthStatus? failureStatus = default,
+ IEnumerable? tags = default,
+ TimeSpan? timeout = default)
+ {
+ var healthCheckRegistration = new HealthCheckRegistration(
+ name ?? HEALTH_CHECK_NAME,
+ _ => new Neo4jClientHealthCheck(healthCheckOptions),
+ failureStatus,
+ tags,
+ timeout
+ );
+
+ return builder.Add(healthCheckRegistration);
+ }
+}
diff --git a/src/HealthChecks.Neo4jClient/HealthChecks.Neo4jClient.csproj b/src/HealthChecks.Neo4jClient/HealthChecks.Neo4jClient.csproj
new file mode 100644
index 0000000000..b4ba232d95
--- /dev/null
+++ b/src/HealthChecks.Neo4jClient/HealthChecks.Neo4jClient.csproj
@@ -0,0 +1,11 @@
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
diff --git a/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheck.cs b/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheck.cs
new file mode 100644
index 0000000000..098c45b3af
--- /dev/null
+++ b/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheck.cs
@@ -0,0 +1,46 @@
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using Neo4jClient;
+
+namespace HealthChecks.Neo4jClient;
+
+///
+/// A health check for Neo4j databases.
+///
+public class Neo4jClientHealthCheck : IHealthCheck
+{
+ private readonly Neo4jClientHealthCheckOptions _options;
+
+ ///
+ /// Creates an instance with the options passed to it
+ ///
+ public Neo4jClientHealthCheck(Neo4jClientHealthCheckOptions options)
+ {
+ _options = Guard.ThrowIfNull(options);
+ }
+
+ ///
+ public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
+ {
+ try
+ {
+ _options.GraphClient ??= new BoltGraphClient(new Uri(_options.Host),
+ _options.Username,
+ _options.Password,
+ _options.Realm,
+ _options.EncryptionLevel,
+ _options.SerializeNullValues,
+ _options.UseDriverDataTypes);
+
+
+ var graphClient = _options.GraphClient;
+
+ await graphClient.ConnectAsync().ConfigureAwait(false);
+
+ return HealthCheckResult.Healthy();
+ }
+ catch (Exception ex)
+ {
+ return HealthCheckResult.Unhealthy(ex.Message, ex);
+ }
+ }
+}
diff --git a/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs b/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs
new file mode 100644
index 0000000000..fea46ebda2
--- /dev/null
+++ b/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs
@@ -0,0 +1,74 @@
+using Neo4j.Driver;
+using Neo4jClient;
+
+namespace HealthChecks.Neo4jClient;
+
+///
+/// Options for .
+///
+public class Neo4jClientHealthCheckOptions
+{
+ ///
+ /// Client for connecting to a database.
+ ///
+ public IGraphClient? GraphClient { get; set; }
+
+ ///
+ /// Host that will be used to connect to the database.
+ ///
+ public string? Host { get; set; }
+
+ ///
+ /// Username that will be used to connect to the database using the .
+ ///
+ public string? Username { get; set; }
+
+ ///
+ /// Password that will be used to connect to the database using the .
+ ///
+ public string? Password { get; set; }
+
+ ///
+ /// Realm that will be used to connect to the database using the .
+ ///
+ public string? Realm { get; set; }
+
+ ///
+ /// Sets the encryption level for connecting to the database
+ ///
+ public EncryptionLevel? EncryptionLevel { get; set; }
+
+ ///
+ /// Sets the encryption level for connecting to the database
+ ///
+ public bool SerializeNullValues { get; set; } = false;
+
+ ///
+ /// Sets the encryption level for connecting to the database
+ ///
+ public bool UseDriverDataTypes { get; set; } = false;
+
+ ///
+ /// Creates instance of .
+ ///
+ /// The client for connecting to the database.
+ public Neo4jClientHealthCheckOptions(IGraphClient graphClient)
+ {
+ GraphClient = Guard.ThrowIfNull(graphClient);
+ }
+
+ ///
+ /// Creates instance of .
+ ///
+ /// Host that will be used to connect to the database. example: bolt://localhost:7687
+ /// Username that will be used to connect to the database.
+ /// Password that will be used to connect to the database.
+ /// realm that will be used to connect to the database.
+ public Neo4jClientHealthCheckOptions(string? host, string? username, string? password, string? realm)
+ {
+ Host = Guard.ThrowIfNull(host, true);
+ Username = Guard.ThrowIfNull(username, true);
+ Password = Guard.ThrowIfNull(password, true);
+ Realm = realm;
+ }
+}
diff --git a/src/HealthChecks.Neo4jClient/README.md b/src/HealthChecks.Neo4jClient/README.md
new file mode 100644
index 0000000000..4a8c78872c
--- /dev/null
+++ b/src/HealthChecks.Neo4jClient/README.md
@@ -0,0 +1,24 @@
+# Neo4j Health check
+
+This health сheck verifies the status of a database in a neo4j DBMS using the BOLT protocol to establish a connection
+
+This library uses the [Neo4jClient](https://www.nuget.org/packages/Neo4jClient/) package to connect to the database
+
+# Sample
+
+Using options class
+```csharp
+var options = new Neo4jClientHealthCheckOptions("bolt://localhost:7687", "neo4j", "neo4j", realm: null);
+
+services.AddHealthChecks()
+ .AddNeo4jClient(options);
+```
+
+Using client from service provider
+```csharp
+var graphClient = new BoltGraphClient("bolt://localhost:7687", "neo4j", "neo4j");
+services.AddSingleton(graphClient);
+
+services.AddHealthChecks()
+ .AddNeo4jClient(sp => sp.GetRequiredService());
+```
diff --git a/test/HealthChecks.Neo4jClient.Tests/DependencyInjection/RegistrationTests.cs b/test/HealthChecks.Neo4jClient.Tests/DependencyInjection/RegistrationTests.cs
new file mode 100644
index 0000000000..e4fbe568cd
--- /dev/null
+++ b/test/HealthChecks.Neo4jClient.Tests/DependencyInjection/RegistrationTests.cs
@@ -0,0 +1,73 @@
+using Neo4jClient;
+
+namespace HealthChecks.Neo4jClient.Tests.DependencyInjection;
+
+public class RegistrationTests
+{
+ [Fact]
+ public async Task add_health_check_when_properly_configured()
+ {
+ var services = new ServiceCollection();
+ var boltClient = new BoltGraphClient("bolt://localhost:7687", "neo4j", "neo4j");
+ await boltClient.ConnectAsync();
+ await boltClient.Cypher
+ .Create("(a:Test{Name: $param})")
+ .WithParam("param", "name123")
+ .ExecuteWithoutResultsAsync();
+
+ services.AddSingleton(boltClient);
+
+ services.AddHealthChecks()
+ .AddNeo4jClient(f => f.GetRequiredService());
+
+ await using var serviceProvider = services.BuildServiceProvider();
+ var options = serviceProvider.GetRequiredService>();
+
+ var registration = options.Value.Registrations.First();
+
+ registration.Name.ShouldBe("neo4j");
+ }
+
+ [Fact]
+ public async Task add_health_check_when_an_instance_of_bolt_graph_client_is_passed_to_options_class()
+ {
+ var services = new ServiceCollection();
+ var boltClient = new BoltGraphClient("bolt://localhost:7687", "neo4j", "neo4j");
+ await boltClient.ConnectAsync();
+ await boltClient.Cypher
+ .Create("(a:Test{Name: $param})")
+ .WithParam("param", "name123")
+ .ExecuteWithoutResultsAsync();
+
+ services.AddSingleton(boltClient);
+
+ var healthCheckOptions = new Neo4jClientHealthCheckOptions(boltClient);
+
+ services.AddHealthChecks()
+ .AddNeo4jClient(healthCheckOptions);
+
+ await using var serviceProvider = services.BuildServiceProvider();
+ var options = serviceProvider.GetRequiredService>();
+
+ var registration = options.Value.Registrations.First();
+
+ registration.Name.ShouldBe("neo4j");
+ }
+
+ [Fact]
+ public async Task add_health_check_when_bolt_graph_client_configured_from_options_class()
+ {
+ var services = new ServiceCollection();
+ var healthCheckOptions = new Neo4jClientHealthCheckOptions("bolt://localhost:7687", "neo4j", "neo4j", null);
+
+ services.AddHealthChecks()
+ .AddNeo4jClient(healthCheckOptions);
+
+ await using var serviceProvider = services.BuildServiceProvider();
+ var options = serviceProvider.GetRequiredService>();
+
+ var registration = options.Value.Registrations.First();
+
+ registration.Name.ShouldBe("neo4j");
+ }
+}
diff --git a/test/HealthChecks.Neo4jClient.Tests/Functional/Neo4jClientHealthCheckTests.cs b/test/HealthChecks.Neo4jClient.Tests/Functional/Neo4jClientHealthCheckTests.cs
new file mode 100644
index 0000000000..03e3f2767f
--- /dev/null
+++ b/test/HealthChecks.Neo4jClient.Tests/Functional/Neo4jClientHealthCheckTests.cs
@@ -0,0 +1,33 @@
+using System.Net;
+using Neo4jClient;
+
+namespace HealthChecks.Neo4jClient.Tests.Functional;
+
+public class Neo4jClientHealthCheckTests
+{
+ [Fact]
+ public async Task be_unhealthy_when_username_not_right_with_503_status_code_response_from_test_server()
+ {
+ var webHostBuilder = new WebHostBuilder()
+ .ConfigureServices(services =>
+ {
+ var graphClient = new BoltGraphClient("bolt://localhost:7687", "neo4j_should_be_not_right_name", "neo4j");
+ services.AddSingleton(graphClient);
+
+ services.AddHealthChecks()
+ .AddNeo4jClient(_ => _.GetRequiredService(), tags: ["neo4j"]);
+ })
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health", new HealthCheckOptions
+ {
+ Predicate = r => r.Tags.Contains("neo4j")
+ });
+ });
+
+ using var server = new TestServer(webHostBuilder);
+ using var response = await server.CreateRequest("/health").GetAsync();
+
+ response.StatusCode.ShouldBe(HttpStatusCode.ServiceUnavailable);
+ }
+}
diff --git a/test/HealthChecks.Neo4jClient.Tests/HealthChecks.Neo4jClient.Tests.csproj b/test/HealthChecks.Neo4jClient.Tests/HealthChecks.Neo4jClient.Tests.csproj
new file mode 100644
index 0000000000..71e8df6597
--- /dev/null
+++ b/test/HealthChecks.Neo4jClient.Tests/HealthChecks.Neo4jClient.Tests.csproj
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/test/HealthChecks.Neo4jClient.Tests/HealthChecks.Neo4jClient.approved.txt b/test/HealthChecks.Neo4jClient.Tests/HealthChecks.Neo4jClient.approved.txt
new file mode 100644
index 0000000000..5b829dd65a
--- /dev/null
+++ b/test/HealthChecks.Neo4jClient.Tests/HealthChecks.Neo4jClient.approved.txt
@@ -0,0 +1,29 @@
+namespace HealthChecks.Neo4jClient
+{
+ public class Neo4jClientHealthCheck : Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck
+ {
+ public Neo4jClientHealthCheck(HealthChecks.Neo4jClient.Neo4jClientHealthCheckOptions options) { }
+ public System.Threading.Tasks.Task CheckHealthAsync(Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckContext context, System.Threading.CancellationToken cancellationToken = default) { }
+ }
+ public class Neo4jClientHealthCheckOptions
+ {
+ public Neo4jClientHealthCheckOptions(Neo4jClient.IGraphClient graphClient) { }
+ public Neo4jClientHealthCheckOptions(string? host, string? username, string? password, string? realm) { }
+ public Neo4j.Driver.EncryptionLevel? EncryptionLevel { get; set; }
+ public Neo4jClient.IGraphClient? GraphClient { get; set; }
+ public string? Host { get; set; }
+ public string? Password { get; set; }
+ public string? Realm { get; set; }
+ public bool SerializeNullValues { get; set; }
+ public bool UseDriverDataTypes { get; set; }
+ public string? Username { get; set; }
+ }
+}
+namespace Microsoft.Extensions.DependencyInjection
+{
+ public static class Neo4jClientHealthCheckBuilderExtensions
+ {
+ public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddNeo4jClient(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, HealthChecks.Neo4jClient.Neo4jClientHealthCheckOptions healthCheckOptions, string? name = null, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default, System.Collections.Generic.IEnumerable? tags = null, System.TimeSpan? timeout = default) { }
+ public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddNeo4jClient(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, System.Func graphClientFactory, 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