Skip to content
This repository was archived by the owner on Dec 19, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// SPDX-License-Identifier: Apache-2.0
// Licensed to the Ed-Fi Alliance under one or more agreements.
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
// See the LICENSE and NOTICES files in the project root for more information.

using EdFi.Ods.AdminApi.Infrastructure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Logging;
using NUnit.Framework;
using Shouldly;
using System.Collections.Generic;
using System.Linq;

namespace EdFi.Ods.AdminApi.UnitTests.Infrastructure;

[TestFixture]
public class HealthCheckServiceExtensionsTests
{
[Test]
public void AddHealthCheck_ShouldRegisterBothAdminAndSecurityHealthChecks_WhenMultiTenancyDisabled()
{
// Arrange
var services = new ServiceCollection();
services.AddLogging(); // Required for health checks
var configuration = CreateTestConfiguration(multiTenancy: false);

// Act
services.AddHealthCheck(configuration);

// Assert - Check that health check services are registered
var healthCheckServiceDescriptor = services.FirstOrDefault(s => s.ServiceType == typeof(HealthCheckService));
healthCheckServiceDescriptor.ShouldNotBeNull();
}

[Test]
public void AddHealthCheck_ShouldRegisterMultiTenantHealthChecks_WhenMultiTenancyEnabled()
{
// Arrange
var services = new ServiceCollection();
services.AddLogging(); // Required for health checks
var configuration = CreateTestConfiguration(multiTenancy: true);

// Act
services.AddHealthCheck(configuration);

// Assert - Check that health check services are registered
var healthCheckServiceDescriptor = services.FirstOrDefault(s => s.ServiceType == typeof(HealthCheckService));
healthCheckServiceDescriptor.ShouldNotBeNull();
}

private static IConfigurationRoot CreateTestConfiguration(bool multiTenancy)
{
var configData = new Dictionary<string, string>
{
["AppSettings:DatabaseEngine"] = "SqlServer",
["AppSettings:MultiTenancy"] = multiTenancy.ToString(),
["ConnectionStrings:EdFi_Admin"] = "Data Source=test;Initial Catalog=EdFi_Admin_Test;Integrated Security=True",
["ConnectionStrings:EdFi_Security"] = "Data Source=test;Initial Catalog=EdFi_Security_Test;Integrated Security=True"
};

if (multiTenancy)
{
configData["Tenants:tenant1:ConnectionStrings:EdFi_Admin"] = "Data Source=test;Initial Catalog=EdFi_Admin_Tenant1;Integrated Security=True";
configData["Tenants:tenant1:ConnectionStrings:EdFi_Security"] = "Data Source=test;Initial Catalog=EdFi_Security_Tenant1;Integrated Security=True";
configData["Tenants:tenant2:ConnectionStrings:EdFi_Admin"] = "Data Source=test;Initial Catalog=EdFi_Admin_Tenant2;Integrated Security=True";
configData["Tenants:tenant2:ConnectionStrings:EdFi_Security"] = "Data Source=test;Initial Catalog=EdFi_Security_Tenant2;Integrated Security=True";
}

return new ConfigurationBuilder()
.AddInMemoryCollection(configData)
.Build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,26 @@ public static IServiceCollection AddHealthCheck(this IServiceCollection services

return services;
}

public static IServiceCollection AddHealthCheck(
this IServiceCollection services,
string adminConnectionString,
string securityConnectionString,
bool isSqlServer)
{
var hcBuilder = services.AddHealthChecks();

if (isSqlServer)
{
hcBuilder.AddSqlServer(adminConnectionString, name: "EdFi_Admin", tags: ["Databases"]);
hcBuilder.AddSqlServer(securityConnectionString, name: "EdFi_Security", tags: ["Databases"]);
}
else
{
hcBuilder.AddNpgSql(adminConnectionString, name: "EdFi_Admin", tags: ["Databases"]);
hcBuilder.AddNpgSql(securityConnectionString, name: "EdFi_Security", tags: ["Databases"]);
}

return services;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
// See the LICENSE and NOTICES files in the project root for more information.

using EdFi.Ods.AdminApi.Common.Infrastructure.ErrorHandling;
using EdFi.Ods.AdminApi.Infrastructure.Extensions;
using EdFi.Ods.AdminApi.Common.Infrastructure;
using EdFi.Ods.AdminApi.Common.Infrastructure.Extensions;
using EdFi.Ods.AdminApi.Common.Settings;
Expand All @@ -18,10 +17,31 @@ public static IServiceCollection AddHealthCheck(
IConfigurationRoot configuration
)
{
Dictionary<string, string> connectionStrings;
var databaseEngine = configuration.Get("AppSettings:DatabaseEngine", "SqlServer");
var multiTenancyEnabled = configuration.Get("AppSettings:MultiTenancy", false);
var connectionStringName = "EdFi_Admin";

if (!string.IsNullOrEmpty(databaseEngine))
{
var isSqlServer = DatabaseEngineEnum.Parse(databaseEngine).Equals(DatabaseEngineEnum.SqlServer);
var hcBuilder = services.AddHealthChecks();

// Add health checks for both EdFi_Admin and EdFi_Security databases
AddDatabaseHealthChecks(hcBuilder, configuration, "EdFi_Admin", multiTenancyEnabled, isSqlServer);
AddDatabaseHealthChecks(hcBuilder, configuration, "EdFi_Security", multiTenancyEnabled, isSqlServer);
}

return services;
}

private static void AddDatabaseHealthChecks(
IHealthChecksBuilder hcBuilder,
IConfigurationRoot configuration,
string connectionStringName,
bool multiTenancyEnabled,
bool isSqlServer
)
{
Dictionary<string, string> connectionStrings;

if (multiTenancyEnabled)
{
Expand All @@ -42,24 +62,20 @@ IConfigurationRoot configuration
};
}

if (!string.IsNullOrEmpty(databaseEngine))
foreach (var connectionString in connectionStrings)
{
var isSqlServer = DatabaseEngineEnum.Parse(databaseEngine).Equals(DatabaseEngineEnum.SqlServer);
var hcBuilder = services.AddHealthChecks();
var healthCheckName = multiTenancyEnabled
? $"{connectionString.Key}_{connectionStringName}"
: connectionStringName;

foreach (var connectionString in connectionStrings)
if (isSqlServer)
{
hcBuilder.AddSqlServer(connectionString.Value, name: healthCheckName, tags: ["Databases"]);
}
else
{
if (isSqlServer)
{
hcBuilder.AddSqlServer(connectionString.Value, name: connectionString.Key);
}
else
{
hcBuilder.AddNpgSql(connectionString.Value, name: connectionString.Key);
}
hcBuilder.AddNpgSql(connectionString.Value, name: healthCheckName, tags: ["Databases"]);
}
}

return services;
}
}
26 changes: 25 additions & 1 deletion Application/EdFi.Ods.AdminApi/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
// See the LICENSE and NOTICES files in the project root for more information.

using System.Net;
using EdFi.Ods.AdminApi.Common.Constants;
using EdFi.Ods.AdminApi.Common.Infrastructure;
using EdFi.Ods.AdminApi.Common.Infrastructure.MultiTenancy;
using EdFi.Ods.AdminApi.Features;
using EdFi.Ods.AdminApi.Infrastructure;
using log4net;
using log4net.Config;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.Diagnostics.HealthChecks;

var builder = WebApplication.CreateBuilder(args);

Expand Down Expand Up @@ -61,7 +64,28 @@
app.MapFeatureEndpoints();

app.MapControllers();
app.UseHealthChecks("/health");
app.UseHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = async (context, report) =>
{
context.Response.ContentType = "application/json";

// 200 OK if all are healthy, 503 Service Unavailable if any are unhealthy
context.Response.StatusCode = report.Status == HealthStatus.Unhealthy ? (int)HttpStatusCode.ServiceUnavailable : (int)HttpStatusCode.OK;

var response = new
{
Status = report.Status.ToString(),
Results = report.Entries.GroupBy(x => x.Value.Tags.FirstOrDefault()).Select(x => new
{
Name = x.Key,
Status = x.Min(y => y.Value.Status).ToString()
})
};

await context.Response.WriteAsJsonAsync(response);
}
});

if (app.Configuration.GetValue<bool>("SwaggerSettings:EnableSwagger"))
{
Expand Down
Loading