From 5cc6c4bf7ddf21ef976c7a8acf40a241f665e261 Mon Sep 17 00:00:00 2001 From: Ivan Smith Date: Thu, 16 Jan 2025 21:18:39 +0300 Subject: [PATCH 01/10] a project has been created, an extension class for IHealthChecksBuilder has been initialized, as well as Neo4jClientHealthCheck, which implements the IHealthCheck interface . Installed package for working with Neo4j --- AspNetCore.Diagnostics.HealthChecks.sln | 7 +++++++ Directory.Packages.props | 3 ++- .../Neo4jClientHealthCheckBuilderExtensions.cs | 10 ++++++++++ .../HealthChecks.Neo4jClient.csproj | 11 +++++++++++ .../Neo4jClientHealthCheck.cs | 12 ++++++++++++ 5 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/HealthChecks.Neo4jClient/DependencyInjection/Neo4jClientHealthCheckBuilderExtensions.cs create mode 100644 src/HealthChecks.Neo4jClient/HealthChecks.Neo4jClient.csproj create mode 100644 src/HealthChecks.Neo4jClient/Neo4jClientHealthCheck.cs diff --git a/AspNetCore.Diagnostics.HealthChecks.sln b/AspNetCore.Diagnostics.HealthChecks.sln index 6249bfaf2a..7361bbb922 100644 --- a/AspNetCore.Diagnostics.HealthChecks.sln +++ b/AspNetCore.Diagnostics.HealthChecks.sln @@ -323,6 +323,8 @@ 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 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -905,6 +907,10 @@ 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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1054,6 +1060,7 @@ 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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2B8C62A1-11B6-469F-874C-A02443256568} diff --git a/Directory.Packages.props b/Directory.Packages.props index 9035eadb36..e57cf52c80 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -73,6 +73,7 @@ + @@ -117,4 +118,4 @@ - + \ 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..a737942476 --- /dev/null +++ b/src/HealthChecks.Neo4jClient/DependencyInjection/Neo4jClientHealthCheckBuilderExtensions.cs @@ -0,0 +1,10 @@ +namespace Microsoft.Extensions.DependencyInjection; + +public static class Neo4jClientHealthCheckBuilderExtensions +{ + public static IHealthChecksBuilder AddNeo4jClient(this IHealthChecksBuilder builder) + { + + return builder; + } +} 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..8941402498 --- /dev/null +++ b/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheck.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace HealthChecks.Neo4jClient; + +/// +/// A health check for Neo4j databases. +/// +public class Neo4jClientHealthCheck : IHealthCheck +{ + /// + public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) => throw new NotImplementedException(); +} From 174a23209470cd405bf067013e234abe1838b1c9 Mon Sep 17 00:00:00 2001 From: Ivan Smith Date: Sat, 18 Jan 2025 00:06:22 +0300 Subject: [PATCH 02/10] Added methods for DI Container, realization for health check and class for options --- ...Neo4jClientHealthCheckBuilderExtensions.cs | 51 ++++++++++++- .../Neo4jClientHealthCheck.cs | 34 ++++++++- .../Neo4jClientHealthCheckOptions.cs | 74 +++++++++++++++++++ src/HealthChecks.Neo4jClient/README.md | 1 + 4 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs create mode 100644 src/HealthChecks.Neo4jClient/README.md diff --git a/src/HealthChecks.Neo4jClient/DependencyInjection/Neo4jClientHealthCheckBuilderExtensions.cs b/src/HealthChecks.Neo4jClient/DependencyInjection/Neo4jClientHealthCheckBuilderExtensions.cs index a737942476..f2f891bce5 100644 --- a/src/HealthChecks.Neo4jClient/DependencyInjection/Neo4jClientHealthCheckBuilderExtensions.cs +++ b/src/HealthChecks.Neo4jClient/DependencyInjection/Neo4jClientHealthCheckBuilderExtensions.cs @@ -1,10 +1,57 @@ +using HealthChecks.Neo4jClient; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Neo4jClient; + namespace Microsoft.Extensions.DependencyInjection; +/// +/// Extension methods to configure . +/// public static class Neo4jClientHealthCheckBuilderExtensions { - public static IHealthChecksBuilder AddNeo4jClient(this IHealthChecksBuilder builder) + private const string HEALTH_CHECK_NAME = "neo4j"; + + 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); + } + + 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; + return builder.Add(healthCheckRegistration); } } diff --git a/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheck.cs b/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheck.cs index 8941402498..9672f3ed97 100644 --- a/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheck.cs +++ b/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheck.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Diagnostics.HealthChecks; +using Neo4jClient; namespace HealthChecks.Neo4jClient; @@ -7,6 +8,37 @@ namespace HealthChecks.Neo4jClient; /// public class Neo4jClientHealthCheck : IHealthCheck { + private readonly Neo4jClientHealthCheckOptions _options; + + public Neo4jClientHealthCheck(Neo4jClientHealthCheckOptions options) + { + Guard.ThrowIfNull(options); + _options = options; + } + /// - public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) => throw new NotImplementedException(); + 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..05262550c7 --- /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 IBoltGraphClient? 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 bolt protocol. + /// + public string? Username { get; set; } + + /// + /// Password that will be used to connect to the database using the bolt protocol. + /// + public string? Password { get; set; } + + /// + /// Realm that will be used to connect to the database using the bolt protocol. + /// + 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(IBoltGraphClient graphClient) + { + GraphClient = Guard.ThrowIfNull(graphClient); + } + + /// + /// Creates instance of . + /// + /// Host that will be used to connect to the database. + /// 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 = Guard.ThrowIfNull(realm, true); + } +} diff --git a/src/HealthChecks.Neo4jClient/README.md b/src/HealthChecks.Neo4jClient/README.md new file mode 100644 index 0000000000..35a26d4da2 --- /dev/null +++ b/src/HealthChecks.Neo4jClient/README.md @@ -0,0 +1 @@ +# Neo4j Health check From 3ad557223c4903c80c4e43448868815426b4855e Mon Sep 17 00:00:00 2001 From: Ivan Smith Date: Sat, 18 Jan 2025 16:51:36 +0300 Subject: [PATCH 03/10] Updated Neo4jHealthCheckOptions comments, unit tests for Neo4jClientHealthCheckBuilderExtensions created. --- AspNetCore.Diagnostics.HealthChecks.sln | 7 ++ Directory.Packages.props | 2 +- .../Neo4jClientHealthCheck.cs | 6 +- .../Neo4jClientHealthCheckOptions.cs | 8 +- .../DependencyInjection/RegistrationTests.cs | 73 +++++++++++++++++++ .../HealthChecks.Neo4jClient.Tests.csproj | 7 ++ .../HealthChecks.Neo4jClient.approved.txt | 29 ++++++++ 7 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 test/HealthChecks.Neo4jClient.Tests/DependencyInjection/RegistrationTests.cs create mode 100644 test/HealthChecks.Neo4jClient.Tests/HealthChecks.Neo4jClient.Tests.csproj create mode 100644 test/HealthChecks.Neo4jClient.Tests/HealthChecks.Neo4jClient.approved.txt diff --git a/AspNetCore.Diagnostics.HealthChecks.sln b/AspNetCore.Diagnostics.HealthChecks.sln index 7361bbb922..1af84c5a47 100644 --- a/AspNetCore.Diagnostics.HealthChecks.sln +++ b/AspNetCore.Diagnostics.HealthChecks.sln @@ -325,6 +325,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthChecks.ClickHouse.Tes 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 @@ -911,6 +913,10 @@ Global {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 @@ -1061,6 +1067,7 @@ Global {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 e57cf52c80..7be64caaf2 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -112,7 +112,7 @@ - + diff --git a/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheck.cs b/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheck.cs index 9672f3ed97..098c45b3af 100644 --- a/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheck.cs +++ b/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheck.cs @@ -10,10 +10,12 @@ public class Neo4jClientHealthCheck : IHealthCheck { private readonly Neo4jClientHealthCheckOptions _options; + /// + /// Creates an instance with the options passed to it + /// public Neo4jClientHealthCheck(Neo4jClientHealthCheckOptions options) { - Guard.ThrowIfNull(options); - _options = options; + _options = Guard.ThrowIfNull(options); } /// diff --git a/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs b/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs index 05262550c7..1619edaec4 100644 --- a/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs +++ b/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs @@ -19,17 +19,17 @@ public class Neo4jClientHealthCheckOptions public string? Host { get; set; } /// - /// Username that will be used to connect to the database using the bolt protocol. + /// 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 bolt protocol. + /// 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 bolt protocol. + /// Realm that will be used to connect to the database using the . /// public string? Realm { get; set; } @@ -69,6 +69,6 @@ public Neo4jClientHealthCheckOptions(string? host, string? username, string? pas Host = Guard.ThrowIfNull(host, true); Username = Guard.ThrowIfNull(username, true); Password = Guard.ThrowIfNull(password, true); - Realm = Guard.ThrowIfNull(realm, true); + Realm = realm; } } diff --git a/test/HealthChecks.Neo4jClient.Tests/DependencyInjection/RegistrationTests.cs b/test/HealthChecks.Neo4jClient.Tests/DependencyInjection/RegistrationTests.cs new file mode 100644 index 0000000000..4ffa6eb839 --- /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", "P@ssword"); + 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", "P@ssword"); + 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", "P@ssword", 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/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..a255d4960b --- /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.IBoltGraphClient graphClient) { } + public Neo4jClientHealthCheckOptions(string? host, string? username, string? password, string? realm) { } + public Neo4j.Driver.EncryptionLevel? EncryptionLevel { get; set; } + public Neo4jClient.IBoltGraphClient? 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 From b875f74c3e4d98a0aca067c5743aaaa414f580a3 Mon Sep 17 00:00:00 2001 From: Ivan Smith Date: Sat, 18 Jan 2025 16:58:16 +0300 Subject: [PATCH 04/10] IBoltGraphClient changed to IGraphClient --- .../Neo4jClientHealthCheckBuilderExtensions.cs | 2 +- .../Neo4jClientHealthCheckOptions.cs | 12 ++++++------ .../DependencyInjection/RegistrationTests.cs | 2 +- .../HealthChecks.Neo4jClient.approved.txt | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/HealthChecks.Neo4jClient/DependencyInjection/Neo4jClientHealthCheckBuilderExtensions.cs b/src/HealthChecks.Neo4jClient/DependencyInjection/Neo4jClientHealthCheckBuilderExtensions.cs index f2f891bce5..7f3a9d1024 100644 --- a/src/HealthChecks.Neo4jClient/DependencyInjection/Neo4jClientHealthCheckBuilderExtensions.cs +++ b/src/HealthChecks.Neo4jClient/DependencyInjection/Neo4jClientHealthCheckBuilderExtensions.cs @@ -13,7 +13,7 @@ public static class Neo4jClientHealthCheckBuilderExtensions public static IHealthChecksBuilder AddNeo4jClient( this IHealthChecksBuilder builder, - Func graphClientFactory, + Func graphClientFactory, string? name = default, HealthStatus? failureStatus = default, IEnumerable? tags = default, diff --git a/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs b/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs index 1619edaec4..974e937ed3 100644 --- a/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs +++ b/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs @@ -11,25 +11,25 @@ public class Neo4jClientHealthCheckOptions /// /// Client for connecting to a database. /// - public IBoltGraphClient? GraphClient { get; set; } - + 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 . + /// 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 . + /// 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 . + /// Realm that will be used to connect to the database using the . /// public string? Realm { get; set; } @@ -52,7 +52,7 @@ public class Neo4jClientHealthCheckOptions /// Creates instance of . /// /// The client for connecting to the database. - public Neo4jClientHealthCheckOptions(IBoltGraphClient graphClient) + public Neo4jClientHealthCheckOptions(IGraphClient graphClient) { GraphClient = Guard.ThrowIfNull(graphClient); } diff --git a/test/HealthChecks.Neo4jClient.Tests/DependencyInjection/RegistrationTests.cs b/test/HealthChecks.Neo4jClient.Tests/DependencyInjection/RegistrationTests.cs index 4ffa6eb839..e33c51c81b 100644 --- a/test/HealthChecks.Neo4jClient.Tests/DependencyInjection/RegistrationTests.cs +++ b/test/HealthChecks.Neo4jClient.Tests/DependencyInjection/RegistrationTests.cs @@ -18,7 +18,7 @@ await boltClient.Cypher services.AddSingleton(boltClient); services.AddHealthChecks() - .AddNeo4jClient(f => f.GetRequiredService()); + .AddNeo4jClient(f => f.GetRequiredService()); await using var serviceProvider = services.BuildServiceProvider(); var options = serviceProvider.GetRequiredService>(); diff --git a/test/HealthChecks.Neo4jClient.Tests/HealthChecks.Neo4jClient.approved.txt b/test/HealthChecks.Neo4jClient.Tests/HealthChecks.Neo4jClient.approved.txt index a255d4960b..5b829dd65a 100644 --- a/test/HealthChecks.Neo4jClient.Tests/HealthChecks.Neo4jClient.approved.txt +++ b/test/HealthChecks.Neo4jClient.Tests/HealthChecks.Neo4jClient.approved.txt @@ -7,10 +7,10 @@ namespace HealthChecks.Neo4jClient } public class Neo4jClientHealthCheckOptions { - public Neo4jClientHealthCheckOptions(Neo4jClient.IBoltGraphClient graphClient) { } + public Neo4jClientHealthCheckOptions(Neo4jClient.IGraphClient graphClient) { } public Neo4jClientHealthCheckOptions(string? host, string? username, string? password, string? realm) { } public Neo4j.Driver.EncryptionLevel? EncryptionLevel { get; set; } - public Neo4jClient.IBoltGraphClient? GraphClient { get; set; } + public Neo4jClient.IGraphClient? GraphClient { get; set; } public string? Host { get; set; } public string? Password { get; set; } public string? Realm { get; set; } @@ -24,6 +24,6 @@ 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) { } + 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 From f40abe8e2a7a68e2e99285c386922b80cd7f2e1b Mon Sep 17 00:00:00 2001 From: Ivan Smith Date: Sat, 18 Jan 2025 16:58:40 +0300 Subject: [PATCH 05/10] code style fix --- src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs b/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs index 974e937ed3..52860ee0b4 100644 --- a/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs +++ b/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs @@ -12,7 +12,7 @@ public class Neo4jClientHealthCheckOptions /// Client for connecting to a database. /// public IGraphClient? GraphClient { get; set; } - + /// /// Host that will be used to connect to the database. /// From 047e0ee506d48a616510b99206bd3c9d7cf543ea Mon Sep 17 00:00:00 2001 From: Ivan Smith Date: Sat, 18 Jan 2025 18:10:02 +0300 Subject: [PATCH 06/10] Added comments for Neo4jClientHealthCheckBuilderExtensions, updated tests --- ...Neo4jClientHealthCheckBuilderExtensions.cs | 26 +++++++++++++++++++ .../DependencyInjection/RegistrationTests.cs | 6 ++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/HealthChecks.Neo4jClient/DependencyInjection/Neo4jClientHealthCheckBuilderExtensions.cs b/src/HealthChecks.Neo4jClient/DependencyInjection/Neo4jClientHealthCheckBuilderExtensions.cs index 7f3a9d1024..38cf85ae23 100644 --- a/src/HealthChecks.Neo4jClient/DependencyInjection/Neo4jClientHealthCheckBuilderExtensions.cs +++ b/src/HealthChecks.Neo4jClient/DependencyInjection/Neo4jClientHealthCheckBuilderExtensions.cs @@ -11,6 +11,19 @@ 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, @@ -36,6 +49,19 @@ public static IHealthChecksBuilder AddNeo4jClient( 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, diff --git a/test/HealthChecks.Neo4jClient.Tests/DependencyInjection/RegistrationTests.cs b/test/HealthChecks.Neo4jClient.Tests/DependencyInjection/RegistrationTests.cs index e33c51c81b..e4fbe568cd 100644 --- a/test/HealthChecks.Neo4jClient.Tests/DependencyInjection/RegistrationTests.cs +++ b/test/HealthChecks.Neo4jClient.Tests/DependencyInjection/RegistrationTests.cs @@ -8,7 +8,7 @@ public class RegistrationTests public async Task add_health_check_when_properly_configured() { var services = new ServiceCollection(); - var boltClient = new BoltGraphClient("bolt://localhost:7687", "neo4j", "P@ssword"); + var boltClient = new BoltGraphClient("bolt://localhost:7687", "neo4j", "neo4j"); await boltClient.ConnectAsync(); await boltClient.Cypher .Create("(a:Test{Name: $param})") @@ -32,7 +32,7 @@ await boltClient.Cypher 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", "P@ssword"); + var boltClient = new BoltGraphClient("bolt://localhost:7687", "neo4j", "neo4j"); await boltClient.ConnectAsync(); await boltClient.Cypher .Create("(a:Test{Name: $param})") @@ -58,7 +58,7 @@ await boltClient.Cypher 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", "P@ssword", null); + var healthCheckOptions = new Neo4jClientHealthCheckOptions("bolt://localhost:7687", "neo4j", "neo4j", null); services.AddHealthChecks() .AddNeo4jClient(healthCheckOptions); From e573d6ae1021f442fb8587235fa71e4c92248763 Mon Sep 17 00:00:00 2001 From: Ivan Smith Date: Sat, 18 Jan 2025 18:35:27 +0300 Subject: [PATCH 07/10] Added functional tests for Neo4jHealthCheck --- .../Functional/Neo4jClientHealthCheckTests.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 test/HealthChecks.Neo4jClient.Tests/Functional/Neo4jClientHealthCheckTests.cs 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); + } +} From b36eede342d63b60cde6193984a8fb31d0ab420e Mon Sep 17 00:00:00 2001 From: Ivan Smith Date: Sat, 18 Jan 2025 19:19:23 +0300 Subject: [PATCH 08/10] Added sample for README.md file --- .../Neo4jClientHealthCheckOptions.cs | 2 +- src/HealthChecks.Neo4jClient/README.md | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs b/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs index 52860ee0b4..fea46ebda2 100644 --- a/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs +++ b/src/HealthChecks.Neo4jClient/Neo4jClientHealthCheckOptions.cs @@ -60,7 +60,7 @@ public Neo4jClientHealthCheckOptions(IGraphClient graphClient) /// /// Creates instance of . /// - /// Host that will be used to connect to the database. + /// 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. diff --git a/src/HealthChecks.Neo4jClient/README.md b/src/HealthChecks.Neo4jClient/README.md index 35a26d4da2..37a63b3dab 100644 --- a/src/HealthChecks.Neo4jClient/README.md +++ b/src/HealthChecks.Neo4jClient/README.md @@ -1 +1,22 @@ # Neo4j Health check + +This healthCheck checks the status of a database in a neo4j DBMS using the BOLT protocol to establish a connection + +# 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(_ => _.GetRequiredService()); +``` From f8eaf9ac578aee90f31ed40554e7e660e23ab960 Mon Sep 17 00:00:00 2001 From: Ivan Smith Date: Sat, 18 Jan 2025 19:20:39 +0300 Subject: [PATCH 09/10] Updated README.md --- src/HealthChecks.Neo4jClient/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HealthChecks.Neo4jClient/README.md b/src/HealthChecks.Neo4jClient/README.md index 37a63b3dab..a4ef5ed09e 100644 --- a/src/HealthChecks.Neo4jClient/README.md +++ b/src/HealthChecks.Neo4jClient/README.md @@ -18,5 +18,5 @@ var graphClient = new BoltGraphClient("bolt://localhost:7687", "neo4j", "neo4j") services.AddSingleton(graphClient); services.AddHealthChecks() - .AddNeo4jClient(_ => _.GetRequiredService()); + .AddNeo4jClient(sp => sp.GetRequiredService()); ``` From 1aa155f73b69bee5df09f0167672cf0c4d21bc8c Mon Sep 17 00:00:00 2001 From: Ivan Smith Date: Sat, 18 Jan 2025 19:24:54 +0300 Subject: [PATCH 10/10] Updated README.md --- src/HealthChecks.Neo4jClient/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/HealthChecks.Neo4jClient/README.md b/src/HealthChecks.Neo4jClient/README.md index a4ef5ed09e..4a8c78872c 100644 --- a/src/HealthChecks.Neo4jClient/README.md +++ b/src/HealthChecks.Neo4jClient/README.md @@ -1,6 +1,8 @@ # Neo4j Health check -This healthCheck checks the status of a database in a neo4j DBMS using the BOLT protocol to establish a connection +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