diff --git a/src/Infrastructure/HealthChecks/BindleHealthCheck.cs b/src/Infrastructure/HealthChecks/BindleHealthCheck.cs index 9726c9218..e15af2042 100644 --- a/src/Infrastructure/HealthChecks/BindleHealthCheck.cs +++ b/src/Infrastructure/HealthChecks/BindleHealthCheck.cs @@ -13,16 +13,16 @@ public BindleHealthCheck(IConfiguration configuration) _client = new BindleClient(configuration.GetConnectionString("Bindle")); } - public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { try { - _client.QueryInvoices(); - return Task.FromResult(HealthCheckResult.Healthy("A healthy result.")); + await _client.QueryInvoices(); + return HealthCheckResult.Healthy("A healthy result."); } catch (Exception) { - return Task.FromResult(new HealthCheckResult(context.Registration.FailureStatus, "An unhealthy result.")); + return new HealthCheckResult(context.Registration.FailureStatus, "An unhealthy result."); } } } diff --git a/src/Web/Extensions/HealthCheckExtensions.cs b/src/Web/Extensions/HealthCheckExtensions.cs new file mode 100644 index 000000000..efaf322c5 --- /dev/null +++ b/src/Web/Extensions/HealthCheckExtensions.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Newtonsoft.Json; +using System.Net.Mime; + +namespace Hippo.Web.Extensions; + +public static class HealthCheckExtensions +{ + public static IEndpointConventionBuilder MapCustomHealthChecks( + this IEndpointRouteBuilder endpoints, string endpoint) + { + return endpoints.MapHealthChecks(endpoint, new HealthCheckOptions + { + ResultStatusCodes = + { + [HealthStatus.Healthy] = StatusCodes.Status200OK, + [HealthStatus.Unhealthy] = StatusCodes.Status503ServiceUnavailable + }, + ResponseWriter = async (context, report) => + { + var result = JsonConvert.SerializeObject( + new HealthInfo + { + ServiceName = "Hippo", + Status = report.Status.ToString(), + Subservices = new List( + report.Entries.Select(e => new HealthInfo + { + ServiceName = e.Key ?? string.Empty, + Description = e.Value.Description ?? string.Empty, + Status = Enum.GetName(typeof(HealthStatus), e.Value.Status) + ?? string.Empty, + }) + ) + } + ); + context.Response.ContentType = MediaTypeNames.Application.Json; + await context.Response.WriteAsync(result); + } + }); + } +} diff --git a/src/Web/Extensions/HealthInfo.cs b/src/Web/Extensions/HealthInfo.cs new file mode 100644 index 000000000..0b6148a26 --- /dev/null +++ b/src/Web/Extensions/HealthInfo.cs @@ -0,0 +1,9 @@ +namespace Hippo.Web.Extensions; + +public class HealthInfo +{ + public string ServiceName { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public string Status { get; set; } = string.Empty; + public List Subservices { get; set; } = new List(); +} diff --git a/src/Web/Program.cs b/src/Web/Program.cs index 23fbf318e..1b3d0c6ec 100644 --- a/src/Web/Program.cs +++ b/src/Web/Program.cs @@ -1,5 +1,3 @@ -using System.Text; -using System.Text.Json.Serialization; using FluentValidation.AspNetCore; using Hippo.Application; using Hippo.Application.Common.Config; @@ -8,6 +6,7 @@ using Hippo.Infrastructure.Data; using Hippo.Infrastructure.HealthChecks; using Hippo.Infrastructure.Identity; +using Hippo.Web.Extensions; using Hippo.Web.Services; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; @@ -15,6 +14,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; +using System.Text; +using System.Text.Json.Serialization; var builder = WebApplication.CreateBuilder(args); @@ -116,7 +117,7 @@ "API", "api/{controller}/{action}/{id?}" ); - endpoints.MapHealthChecks("/healthz"); + endpoints.MapCustomHealthChecks("/healthz"); endpoints.MapSwagger(); });