Skip to content

Commit

Permalink
Some suggestions (#1243)
Browse files Browse the repository at this point in the history
  • Loading branch information
MagnusSandgren authored Oct 7, 2024
1 parent 49a3b89 commit 89accc8
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 100 deletions.
2 changes: 1 addition & 1 deletion Digdir.Domain.Dialogporten.sln
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ Global
{AF35FFCA-1206-4C08-A003-DA4A1344CCD5} = {CADB8189-4AA1-4732-844A-C41DBF3EC8B7}
{0900E3CF-F9D8-4B29-957F-484B3B028D6D} = {320B47A0-5EB8-4B6E-8C84-90633A1849CA}
{E389C7C8-9610-40AC-86DC-769B1B7DC78E} = {CADB8189-4AA1-4732-844A-C41DBF3EC8B7}
{6A485C65-3613-4A49-A16F-2789119F6F38} = {320B47A0-5EB8-4B6E-8C84-90633A1849CA}
{6A485C65-3613-4A49-A16F-2789119F6F38} = {096E9B69-6783-4446-A895-0B6D7729A0D9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B2FE67FF-7622-4AFB-AD8E-961B6A39D888}
Expand Down
41 changes: 14 additions & 27 deletions src/Digdir.Domain.Dialogporten.WebApi/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
using NSwag;
using Serilog;
using Digdir.Library.Utils.AspNet;
using static Digdir.Library.Utils.AspNet.HealthCheckExtensions;
using Microsoft.Extensions.Options;

// Using two-stage initialization to catch startup errors.
Log.Logger = new LoggerConfiguration()
Expand Down Expand Up @@ -127,29 +127,18 @@ static void BuildAndRun(string[] args)
.AddControllers(options => options.InputFormatters.Insert(0, JsonPatchInputFormatter.Get()))
.AddNewtonsoftJson()
.Services
// Add health checks with the retrieved URLs
.AddAspNetHealthChecks((x, y) => x.HealthCheckSettings.HttpGetEndpointsToCheck = y
.GetRequiredService<IOptions<WebApiSettings>>().Value?
.Authentication?
.JwtBearerTokenSchemas?
.Select(z => z.WellKnown)
.ToList() ?? [])

// Auth
.AddDialogportenAuthentication(builder.Configuration)
.AddAuthorization();

// Retrieve JWT bearer token schema URLs from configuration
var authSettings = builder.Configuration
.GetSection(WebApiSettings.SectionName)
.Get<WebApiSettings>();

var wellKnownUrls = authSettings?
.Authentication
.JwtBearerTokenSchemas
.Select(schema => schema.WellKnown)
.Where(url => !string.IsNullOrEmpty(url))
.ToList() ?? new List<string>();

// Add health checks with the retrieved URLs
builder.Services.AddAspNetHealthChecks(new AspNetHealthChecksSettings
{
HttpGetEndpointsToCheck = wellKnownUrls
});

if (builder.Environment.IsDevelopment())
{
var localDevelopmentSettings = builder.Configuration.GetLocalDevelopmentSettings();
Expand All @@ -165,6 +154,9 @@ static void BuildAndRun(string[] args)

var app = builder.Build();

app.MapAspNetHealthChecks()
.MapControllers();

app.UseHttpsRedirection()
.UseSerilogRequestLogging()
.UseDefaultExceptionHandler()
Expand All @@ -173,11 +165,8 @@ static void BuildAndRun(string[] args)
.UseAuthorization()
.UseServiceOwnerOnBehalfOfPerson()
.UseUserTypeValidation()
.UseAzureConfiguration();

app.MapAspNetHealthChecks();

app.UseAddSwaggerCorsHeader()
.UseAzureConfiguration()
.UseAddSwaggerCorsHeader()
.UseSwaggerGen(config =>
{
config.PostProcess = (document, _) =>
Expand Down Expand Up @@ -221,8 +210,6 @@ static void BuildAndRun(string[] args)
x.Errors.ResponseBuilder = ErrorResponseBuilderExtensions.ResponseBuilder;
});

app.MapControllers();

app.Run();
}

Expand All @@ -238,4 +225,4 @@ static void IgnoreEmptyCollections(JsonTypeInfo typeInfo)
}

// ReSharper disable once ClassNeverInstantiated.Global
public sealed partial class Program;
public sealed partial class Program;
48 changes: 48 additions & 0 deletions src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Digdir.Library.Utils.AspNet.HealthChecks;
using HealthChecks.UI.Client;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;

namespace Digdir.Library.Utils.AspNet;

public static class AspNetUtilitiesExtensions
{
public static IServiceCollection AddAspNetHealthChecks(
this IServiceCollection services,
Action<AspNetUtilitiesSettings>? configure = null)
=> services.AddAspNetHealthChecks((x, _) => configure?.Invoke(x));

public static IServiceCollection AddAspNetHealthChecks(this IServiceCollection services, Action<AspNetUtilitiesSettings, IServiceProvider>? configure = null)
{
var optionsBuilder = services.AddOptions<AspNetUtilitiesSettings>();

if (configure is not null)
{
optionsBuilder.Configure(configure);
}

return services
.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy(), tags: ["self"])
.AddCheck<EndpointsHealthCheck>(
"Endpoints",
failureStatus: HealthStatus.Unhealthy,
tags: ["external"])
.Services;
}

public static WebApplication MapAspNetHealthChecks(this WebApplication app) =>
app.MapHealthCheckEndpoint("/health/startup", check => check.Tags.Contains("dependencies"))
.MapHealthCheckEndpoint("/health/liveness", check => check.Tags.Contains("self"))
.MapHealthCheckEndpoint("/health/readiness", check => check.Tags.Contains("critical"))
.MapHealthCheckEndpoint("/health", check => check.Tags.Contains("dependencies"))
.MapHealthCheckEndpoint("/health/deep", check => check.Tags.Contains("dependencies") || check.Tags.Contains("external"));

private static WebApplication MapHealthCheckEndpoint(this WebApplication app, string path, Func<HealthCheckRegistration, bool> predicate)
{
app.MapHealthChecks(path, new HealthCheckOptions { Predicate = predicate, ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse });
return app;
}
}
11 changes: 11 additions & 0 deletions src/Digdir.Library.Utils.AspNet/AspNetUtilitiesSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Digdir.Library.Utils.AspNet;

public sealed class AspNetUtilitiesSettings
{
public HealthCheckSettings HealthCheckSettings { get; set; } = new();
}

public sealed class HealthCheckSettings
{
public List<string> HttpGetEndpointsToCheck { get; set; } = [];
}
54 changes: 0 additions & 54 deletions src/Digdir.Library.Utils.AspNet/HealthCheckExtensions.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
Expand All @@ -17,30 +13,34 @@ internal sealed class EndpointsHealthCheck : IHealthCheck
public EndpointsHealthCheck(
IHttpClientFactory httpClientFactory,
ILogger<EndpointsHealthCheck> logger,
IOptions<EndpointsHealthCheckOptions> options)
IOptions<AspNetUtilitiesSettings> options)
{
_httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_endpoints = options?.Value?.GetEndpoints ?? throw new ArgumentNullException(nameof(options));
_endpoints = options.Value.HealthCheckSettings.HttpGetEndpointsToCheck;
}

public async Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default)
{
// TODO: Denne har en sterk avhengighet på infrastruktur registreringen. Fiks
var client = _httpClientFactory.CreateClient("HealthCheckClient");
var unhealthyEndpoints = new List<string>();

foreach (var url in _endpoints)
{
try
{
// TODO: Kan kanskje paralelliseres? Trengs sannsynligvis ikke...
var response = await client.GetAsync(url, cancellationToken);
if (!response.IsSuccessStatusCode)
if (response.IsSuccessStatusCode)
{
_logger.LogWarning("Health check failed for endpoint: {Url}. Status Code: {StatusCode}", url, response.StatusCode);
unhealthyEndpoints.Add($"{url} (Status Code: {response.StatusCode})");
continue;
}

_logger.LogWarning("Health check failed for endpoint: {Url}. Status Code: {StatusCode}", url, response.StatusCode);
unhealthyEndpoints.Add($"{url} (Status Code: {response.StatusCode})");
}
catch (Exception ex)
{
Expand All @@ -49,17 +49,12 @@ public async Task<HealthCheckResult> CheckHealthAsync(
}
}

if (unhealthyEndpoints.Count > 0)
if (unhealthyEndpoints.Count <= 0)
{
var description = $"The following endpoints are unhealthy: {string.Join(", ", unhealthyEndpoints)}";
return HealthCheckResult.Unhealthy(description);
return HealthCheckResult.Healthy("All endpoints are healthy.");
}

return HealthCheckResult.Healthy("All endpoints are healthy.");
var description = $"The following endpoints are unhealthy: {string.Join(", ", unhealthyEndpoints)}";
return HealthCheckResult.Unhealthy(description);
}
}

internal sealed class EndpointsHealthCheckOptions
{
public List<string> GetEndpoints { get; set; } = new();
}

0 comments on commit 89accc8

Please sign in to comment.