Skip to content

Commit

Permalink
VIH-10175 health check refactor (#215)
Browse files Browse the repository at this point in the history
  • Loading branch information
shaed-parkar authored Sep 22, 2023
1 parent 2f801a8 commit 4f095cc
Show file tree
Hide file tree
Showing 11 changed files with 193 additions and 93 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System.Net;
using System.Threading;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Logging;

namespace BookingQueueSubscriber.UnitTests.HealthCheckFunctionTests;

public class HealthCheckTests
{
private HealthCheckFunction _sut;
private Mock<HealthCheckService> _healthCheckServiceMock;

[SetUp]
public void Setup()
{
_healthCheckServiceMock = new Mock<HealthCheckService>();
_sut = new HealthCheckFunction(_healthCheckServiceMock.Object);
}

[Test]
public async Task Should_return_200_when_health_check_is_healthy()
{
// Arrange
var healthReport = new HealthReport(new Dictionary<string, HealthReportEntry>
{
{"test", new HealthReportEntry(HealthStatus.Healthy, "test", TimeSpan.Zero, null, null)}
}, TimeSpan.Zero);
_healthCheckServiceMock.Setup(x=> x.CheckHealthAsync(It.IsAny<Func<HealthCheckRegistration,bool>?>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(healthReport);

// Act
var result = await _sut.HealthCheck(null, Mock.Of<ILogger>());

// Assert
var okResult = result as ObjectResult;
Assert.NotNull(okResult);
Assert.AreEqual((int) HttpStatusCode.OK, okResult.StatusCode);
}

[Test]
public async Task Should_return_503_when_health_check_is_unhealthy()
{
// Arrange
var healthReport = new HealthReport(new Dictionary<string, HealthReportEntry>
{
{"test", new HealthReportEntry(HealthStatus.Unhealthy, "test", TimeSpan.Zero, new Exception("Not Working Right Now"), null)}
}, TimeSpan.Zero);
_healthCheckServiceMock.Setup(x=> x.CheckHealthAsync(It.IsAny<Func<HealthCheckRegistration,bool>?>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(healthReport);

// Act
var result = await _sut.HealthCheck(null, Mock.Of<ILogger>());

// Assert
var serviceUnavailableResult = result as ObjectResult;
Assert.NotNull(serviceUnavailableResult);
Assert.AreEqual((int) HttpStatusCode.ServiceUnavailable, serviceUnavailableResult.StatusCode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,11 @@
}
},
"ApplicationInsights:ConnectionString": "InstrumentationKey=TestInstrumentationKey;IngestionEndpoint=https://test.com/;LiveEndpoint=https://test.com/",
"VhServices:EnableVideoApiStub": true
"VhServices": {
"BookingsApiUrl": "https://localhost:5300",
"VideoApiUrl": "https://localhost:59390",
"UserApiUrl": "https://localhost:5200",
"NotificationApiUrl": "https://localhost:59390",
"EnableVideoApiStub": "true"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@
"resolved": "9.9.0.77355",
"contentHash": "zuF3evXam55/+6En2//ggUeI+MWApgYi0K7Z5nMJkuQoRso0NYMVISGZnR+u1LilwSHPP/ZWDrhpVYNEH4v2Xw=="
},
"AspNetCore.HealthChecks.Uris": {
"type": "Transitive",
"resolved": "6.0.3",
"contentHash": "EY0Vh8s2UrbnyvM/QhbyYuCnbrBw36BKkdh5LqdINxqAGnlPFQXf+/UoNlH/76MTEyg+nvdp2wjr5MqWDkVFaQ==",
"dependencies": {
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.0",
"Microsoft.Extensions.Http": "6.0.0"
}
},
"Azure.Core": {
"type": "Transitive",
"resolved": "1.25.0",
Expand Down Expand Up @@ -789,6 +798,22 @@
"System.Text.Json": "4.7.2"
}
},
"Microsoft.Extensions.Diagnostics.HealthChecks": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "ktOGFY2uJ6QqZbLgLZgYg6qWuOnwKEIYbpgGDR/1QY8E+8NhnL75dJZ+WDl88h7Q4JkIFeTkFBUGF5QmNcfUEg==",
"dependencies": {
"Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.0",
"Microsoft.Extensions.Hosting.Abstractions": "6.0.0",
"Microsoft.Extensions.Logging.Abstractions": "6.0.0",
"Microsoft.Extensions.Options": "6.0.0"
}
},
"Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "6C9uhsA7GwT1qlXF+1JgOktilrWBSMLNVcwIjg63UvFaDVizg8fYTv826MC58dznvvT9yG31gGwsN1cFfg+yZQ=="
},
"Microsoft.Extensions.FileProviders.Abstractions": {
"type": "Transitive",
"resolved": "6.0.0",
Expand Down Expand Up @@ -826,13 +851,12 @@
},
"Microsoft.Extensions.Hosting.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "+k4AEn68HOJat5gj1TWa6X28WlirNQO9sPIIeQbia+91n03esEtMSSoekSTpMjUzjqtJWQN3McVx0GvSPFHF/Q==",
"resolved": "6.0.0",
"contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "2.2.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.FileProviders.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging.Abstractions": "2.2.0"
"Microsoft.Extensions.Configuration.Abstractions": "6.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0",
"Microsoft.Extensions.FileProviders.Abstractions": "6.0.0"
}
},
"Microsoft.Extensions.Http": {
Expand Down Expand Up @@ -2240,6 +2264,7 @@
"bookingqueuesubscriber": {
"type": "Project",
"dependencies": {
"AspNetCore.HealthChecks.Uris": "[6.0.3, )",
"BookingQueueSubscriber.Services": "[1.0.0, )",
"BookingsApi.Client": "[1.46.10, )",
"Microsoft.ApplicationInsights.AspNetCore": "[2.21.0, )",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<LangVersion>latestmajor</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="6.0.3" />
<PackageReference Include="BookingsApi.Client" Version="1.46.10" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.21.0" />
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Microsoft.Extensions.Diagnostics.HealthChecks;

namespace BookingQueueSubscriber.Health;

public static class HealthCheckExtensions
{
public static IServiceCollection AddVhHealthChecks(this IServiceCollection services)
{
var container = services.BuildServiceProvider();
var servicesConfiguration = container.GetService<IOptions<ServicesConfiguration>>().Value;
services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy())
.AddUrlGroup(
new Uri(
new Uri(servicesConfiguration.VideoApiUrl),
"/health/liveness"),
name: "Video API",
failureStatus: HealthStatus.Unhealthy,
tags: new[] {"services"})
.AddUrlGroup(
new Uri(
new Uri(servicesConfiguration.BookingsApiUrl),
"/health/liveness"),
name: "Bookings API",
failureStatus: HealthStatus.Unhealthy,
tags: new[] {"services"})
.AddUrlGroup(
new Uri(
new Uri(servicesConfiguration.UserApiUrl),
"/health/liveness"),
name: "User API",
failureStatus: HealthStatus.Unhealthy,
tags: new[] {"services"});
return services;
}
}
Original file line number Diff line number Diff line change
@@ -1,67 +1,43 @@
using BookingQueueSubscriber.Contract.Responses;
using VideoApi.Client;
using Microsoft.Extensions.Diagnostics.HealthChecks;

namespace BookingQueueSubscriber
{
public class HealthCheckFunction
{
private readonly IVideoApiClient _videoApiClient;

private readonly HealthCheckService _healthCheck;

public HealthCheckFunction(IVideoApiClient videoApiClient)
public HealthCheckFunction(HealthCheckService healthCheck)
{
_videoApiClient = videoApiClient;
_healthCheck = healthCheck;
}

[FunctionName("HealthCheck")]
public async Task<IActionResult> HealthCheck(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "health/liveness")] HttpRequest req, ILogger log)
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "health/liveness")]
HttpRequest req, ILogger log)
{
var response = new HealthCheckResponse
var report = await _healthCheck.CheckHealthAsync();
var healthCheckResponse = new
{
VideoApiHealth = { Successful = true },
AppVersion = GetApplicationVersion()
status = report.Status.ToString(),
details = report.Entries.Select(e => new
{
key = e.Key, value = Enum.GetName(typeof(HealthStatus), e.Value.Status),
error = e.Value.Exception?.Message
})
};

try
{
await _videoApiClient.GetExpiredOpenConferencesAsync();
}
catch (Exception ex)
{
log.LogError(ex, "Unable to retrieve expired open conferences");
response.VideoApiHealth = HandleVideoApiCallException(ex);
}

return new OkObjectResult(response);
}
var statusCode = report.Status == HealthStatus.Healthy
? (int) HttpStatusCode.OK
: (int) HttpStatusCode.ServiceUnavailable;

private HealthCheck HandleVideoApiCallException(Exception ex)
{
var isApiException = ex is VideoApiException;
var healthCheck = new HealthCheck { Successful = true };
if (isApiException && ((VideoApiException)ex).StatusCode != (int)HttpStatusCode.InternalServerError)
return new ObjectResult(healthCheckResponse)
{
return healthCheck;
}

healthCheck.Successful = false;
healthCheck.ErrorMessage = ex.Message;
healthCheck.Data = ex.Data;
StatusCode = statusCode
};

return healthCheck;
}
private ApplicationVersion GetApplicationVersion()
{
var applicationVersion = new ApplicationVersion();
applicationVersion.FileVersion = GetExecutingAssemblyAttribute<AssemblyFileVersionAttribute>(a => a.Version);
applicationVersion.InformationVersion = GetExecutingAssemblyAttribute<AssemblyInformationalVersionAttribute>(a => a.InformationalVersion);
return applicationVersion;
}

private string GetExecutingAssemblyAttribute<T>(Func<T, string> value) where T : Attribute
{
T attribute = (T)Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), typeof(T));
return value.Invoke(attribute);
}
}
}
3 changes: 2 additions & 1 deletion BookingQueueSubscriber/BookingQueueSubscriber/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using BookingQueueSubscriber.Health;
using BookingQueueSubscriber.Services.NotificationApi;
using BookingQueueSubscriber.Services.UserApi;
using BookingQueueSubscriber.Services.VideoApi;
Expand Down Expand Up @@ -155,7 +156,7 @@ public static void RegisterServices(IServiceCollection services, IConfiguration
services.AddSingleton<IFeatureToggles>(new FeatureToggles(configuration["FeatureToggle:SdkKey"], envName));
}


services.AddVhHealthChecks();
}

private static void RegisterMessageHandlers(IServiceCollection serviceCollection)
Expand Down
37 changes: 31 additions & 6 deletions BookingQueueSubscriber/BookingQueueSubscriber/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@
"version": 1,
"dependencies": {
"net6.0": {
"AspNetCore.HealthChecks.Uris": {
"type": "Direct",
"requested": "[6.0.3, )",
"resolved": "6.0.3",
"contentHash": "EY0Vh8s2UrbnyvM/QhbyYuCnbrBw36BKkdh5LqdINxqAGnlPFQXf+/UoNlH/76MTEyg+nvdp2wjr5MqWDkVFaQ==",
"dependencies": {
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.0",
"Microsoft.Extensions.Http": "6.0.0"
}
},
"BookingsApi.Client": {
"type": "Direct",
"requested": "[1.46.10, )",
Expand Down Expand Up @@ -771,6 +781,22 @@
"System.Text.Json": "4.7.2"
}
},
"Microsoft.Extensions.Diagnostics.HealthChecks": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "ktOGFY2uJ6QqZbLgLZgYg6qWuOnwKEIYbpgGDR/1QY8E+8NhnL75dJZ+WDl88h7Q4JkIFeTkFBUGF5QmNcfUEg==",
"dependencies": {
"Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.0",
"Microsoft.Extensions.Hosting.Abstractions": "6.0.0",
"Microsoft.Extensions.Logging.Abstractions": "6.0.0",
"Microsoft.Extensions.Options": "6.0.0"
}
},
"Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "6C9uhsA7GwT1qlXF+1JgOktilrWBSMLNVcwIjg63UvFaDVizg8fYTv826MC58dznvvT9yG31gGwsN1cFfg+yZQ=="
},
"Microsoft.Extensions.FileProviders.Abstractions": {
"type": "Transitive",
"resolved": "6.0.0",
Expand Down Expand Up @@ -808,13 +834,12 @@
},
"Microsoft.Extensions.Hosting.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "+k4AEn68HOJat5gj1TWa6X28WlirNQO9sPIIeQbia+91n03esEtMSSoekSTpMjUzjqtJWQN3McVx0GvSPFHF/Q==",
"resolved": "6.0.0",
"contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "2.2.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.FileProviders.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging.Abstractions": "2.2.0"
"Microsoft.Extensions.Configuration.Abstractions": "6.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0",
"Microsoft.Extensions.FileProviders.Abstractions": "6.0.0"
}
},
"Microsoft.Extensions.Logging": {
Expand Down
2 changes: 1 addition & 1 deletion charts/vh-booking-queue-subscriber/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
apiVersion: v2
name: vh-booking-queue-subscriber
home: https://github.com/hmcts/vh-booking-queue-subscriber
version: 0.0.14
version: 0.0.15
description: Helm Chart for VH Bookings Queue Subscriber
maintainers:
- name: VH Devops
Expand Down
Loading

0 comments on commit 4f095cc

Please sign in to comment.