From ec31adbd7fd6a20ffc2fe62a87fa5b37a3bd9fa3 Mon Sep 17 00:00:00 2001 From: Shaed Parkar Date: Thu, 14 Sep 2023 23:49:39 +0100 Subject: [PATCH 1/7] Refactor health check --- .../appsettings.json | 8 ++- .../packages.lock.json | 37 +++++++++-- .../BookingQueueSubscriber.csproj | 1 + .../Contract/Responses/HealthCheckResponse.cs | 30 --------- .../Health/HealthCheckExtensions.cs | 36 ++++++++++ .../HealthCheckFunction.cs | 66 ++++++------------- .../BookingQueueSubscriber/Startup.cs | 3 +- .../BookingQueueSubscriber/packages.lock.json | 37 +++++++++-- 8 files changed, 129 insertions(+), 89 deletions(-) delete mode 100644 BookingQueueSubscriber/BookingQueueSubscriber/Contract/Responses/HealthCheckResponse.cs create mode 100644 BookingQueueSubscriber/BookingQueueSubscriber/Health/HealthCheckExtensions.cs diff --git a/BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/appsettings.json b/BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/appsettings.json index 776c64df..300890cf 100644 --- a/BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/appsettings.json +++ b/BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/appsettings.json @@ -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" + } } \ No newline at end of file diff --git a/BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/packages.lock.json b/BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/packages.lock.json index 6ee9c0ae..56d46572 100644 --- a/BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/packages.lock.json +++ b/BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/packages.lock.json @@ -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", @@ -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", @@ -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": { @@ -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, )", diff --git a/BookingQueueSubscriber/BookingQueueSubscriber/BookingQueueSubscriber.csproj b/BookingQueueSubscriber/BookingQueueSubscriber/BookingQueueSubscriber.csproj index 54032550..b16873aa 100644 --- a/BookingQueueSubscriber/BookingQueueSubscriber/BookingQueueSubscriber.csproj +++ b/BookingQueueSubscriber/BookingQueueSubscriber/BookingQueueSubscriber.csproj @@ -11,6 +11,7 @@ latestmajor + diff --git a/BookingQueueSubscriber/BookingQueueSubscriber/Contract/Responses/HealthCheckResponse.cs b/BookingQueueSubscriber/BookingQueueSubscriber/Contract/Responses/HealthCheckResponse.cs deleted file mode 100644 index 69cb7d9e..00000000 --- a/BookingQueueSubscriber/BookingQueueSubscriber/Contract/Responses/HealthCheckResponse.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections; - -namespace BookingQueueSubscriber.Contract.Responses -{ - public class HealthCheckResponse - { - public HealthCheckResponse() - { - VideoApiHealth = new HealthCheck(); - AppVersion = new ApplicationVersion(); - } - - public HealthCheck VideoApiHealth { get; set; } - public ApplicationVersion AppVersion { get; set; } - - } - - public class HealthCheck - { - public bool Successful { get; set; } - public string ErrorMessage { get; set; } - public IDictionary Data { get; set; } - } - - public class ApplicationVersion - { - public string FileVersion { get; set; } - public string InformationVersion { get; set; } - } -} diff --git a/BookingQueueSubscriber/BookingQueueSubscriber/Health/HealthCheckExtensions.cs b/BookingQueueSubscriber/BookingQueueSubscriber/Health/HealthCheckExtensions.cs new file mode 100644 index 00000000..0debce87 --- /dev/null +++ b/BookingQueueSubscriber/BookingQueueSubscriber/Health/HealthCheckExtensions.cs @@ -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>().Value; + services.AddHealthChecks() + .AddCheck("self", () => HealthCheckResult.Healthy()) + .AddUrlGroup( + new Uri( + new Uri(servicesConfiguration.VideoApiUrl), + "/healthcheck/health"), + name: "Video API", + failureStatus: HealthStatus.Unhealthy, + tags: new[] {"services"}) + .AddUrlGroup( + new Uri( + new Uri(servicesConfiguration.BookingsApiUrl), + "/healthcheck/health"), + name: "Bookings API", + failureStatus: HealthStatus.Unhealthy, + tags: new[] {"services"}) + .AddUrlGroup( + new Uri( + new Uri(servicesConfiguration.UserApiUrl), + "/healthcheck/health"), + name: "User API", + failureStatus: HealthStatus.Unhealthy, + tags: new[] {"services"}); + return services; + } +} diff --git a/BookingQueueSubscriber/BookingQueueSubscriber/HealthCheckFunction.cs b/BookingQueueSubscriber/BookingQueueSubscriber/HealthCheckFunction.cs index 29f5cf20..a28e7a1e 100644 --- a/BookingQueueSubscriber/BookingQueueSubscriber/HealthCheckFunction.cs +++ b/BookingQueueSubscriber/BookingQueueSubscriber/HealthCheckFunction.cs @@ -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 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(a => a.Version); - applicationVersion.InformationVersion = GetExecutingAssemblyAttribute(a => a.InformationalVersion); - return applicationVersion; - } - private string GetExecutingAssemblyAttribute(Func value) where T : Attribute - { - T attribute = (T)Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), typeof(T)); - return value.Invoke(attribute); } } } diff --git a/BookingQueueSubscriber/BookingQueueSubscriber/Startup.cs b/BookingQueueSubscriber/BookingQueueSubscriber/Startup.cs index d2c919fc..07c1c68c 100644 --- a/BookingQueueSubscriber/BookingQueueSubscriber/Startup.cs +++ b/BookingQueueSubscriber/BookingQueueSubscriber/Startup.cs @@ -1,3 +1,4 @@ +using BookingQueueSubscriber.Health; using BookingQueueSubscriber.Services.NotificationApi; using BookingQueueSubscriber.Services.UserApi; using BookingQueueSubscriber.Services.VideoApi; @@ -155,7 +156,7 @@ public static void RegisterServices(IServiceCollection services, IConfiguration services.AddSingleton(new FeatureToggles(configuration["FeatureToggle:SdkKey"], envName)); } - + services.AddVhHealthChecks(); } private static void RegisterMessageHandlers(IServiceCollection serviceCollection) diff --git a/BookingQueueSubscriber/BookingQueueSubscriber/packages.lock.json b/BookingQueueSubscriber/BookingQueueSubscriber/packages.lock.json index aa07a47a..7827b9ab 100644 --- a/BookingQueueSubscriber/BookingQueueSubscriber/packages.lock.json +++ b/BookingQueueSubscriber/BookingQueueSubscriber/packages.lock.json @@ -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, )", @@ -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", @@ -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": { From b49fa1a156d72cbf1b7f5f455186f73caa311ba0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 15 Sep 2023 11:20:43 +0100 Subject: [PATCH 2/7] VIH-0000 - Renovate - Update Helm release java to v4.2.0 (#212) * VIH-0000 - Renovate - Update Helm release java to v4.2.0 * Bumping chart version --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: hmcts-ado-cnp <174565+hmcts-ado-cnp[bot]@users.noreply.github.com> --- charts/vh-booking-queue-subscriber/Chart.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/vh-booking-queue-subscriber/Chart.yaml b/charts/vh-booking-queue-subscriber/Chart.yaml index 9a6a5d70..d4914a88 100644 --- a/charts/vh-booking-queue-subscriber/Chart.yaml +++ b/charts/vh-booking-queue-subscriber/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: vh-booking-queue-subscriber home: https://github.com/hmcts/vh-booking-queue-subscriber -version: 0.0.13 +version: 0.0.14 description: Helm Chart for VH Bookings Queue Subscriber maintainers: - name: VH Devops @@ -13,5 +13,5 @@ dependencies: repository: https://hmctspublic.azurecr.io/helm/v1/repo/ - name: java - version: 4.1.5 + version: 4.2.0 repository: https://hmctspublic.azurecr.io/helm/v1/repo/ \ No newline at end of file From 1dd2d319ac8025f46a00b9a3c87eb5d619f577a7 Mon Sep 17 00:00:00 2001 From: Shaed Parkar Date: Fri, 15 Sep 2023 16:04:13 +0100 Subject: [PATCH 3/7] Add healthcheck tests --- .../HealthCheckTests.cs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/HealthCheckFunctionTests/HealthCheckTests.cs diff --git a/BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/HealthCheckFunctionTests/HealthCheckTests.cs b/BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/HealthCheckFunctionTests/HealthCheckTests.cs new file mode 100644 index 00000000..49cc5cf3 --- /dev/null +++ b/BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/HealthCheckFunctionTests/HealthCheckTests.cs @@ -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 _healthCheckServiceMock; + + [SetUp] + public void Setup() + { + _healthCheckServiceMock = new Mock(); + _sut = new HealthCheckFunction(_healthCheckServiceMock.Object); + } + + [Test] + public async Task Should_return_200_when_health_check_is_healthy() + { + // Arrange + var healthReport = new HealthReport(new Dictionary + { + {"test", new HealthReportEntry(HealthStatus.Healthy, "test", TimeSpan.Zero, null, null)} + }, TimeSpan.Zero); + _healthCheckServiceMock.Setup(x=> x.CheckHealthAsync(It.IsAny?>(), It.IsAny())) + .ReturnsAsync(healthReport); + + // Act + var result = await _sut.HealthCheck(null, Mock.Of()); + + // 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 + { + {"test", new HealthReportEntry(HealthStatus.Unhealthy, "test", TimeSpan.Zero, new Exception("Not Working Right Now"), null)} + }, TimeSpan.Zero); + _healthCheckServiceMock.Setup(x=> x.CheckHealthAsync(It.IsAny?>(), It.IsAny())) + .ReturnsAsync(healthReport); + + // Act + var result = await _sut.HealthCheck(null, Mock.Of()); + + // Assert + var serviceUnavailableResult = result as ObjectResult; + Assert.NotNull(serviceUnavailableResult); + Assert.AreEqual((int) HttpStatusCode.ServiceUnavailable, serviceUnavailableResult.StatusCode); + } +} \ No newline at end of file From 2f801a836eb631028b21c08bc78652daf889d7d3 Mon Sep 17 00:00:00 2001 From: shaed-parkar <41630528+shaed-parkar@users.noreply.github.com> Date: Mon, 18 Sep 2023 12:02:30 +0100 Subject: [PATCH 4/7] VIH-19177 Default to non-ejud template when participant does not have an ejud username (for applicable roles only) (#214) This applies to Judge and Judicial Office Holders only --- .../AddNotificationRequestMapper.cs | 24 +- .../VideoWeb/VideoWebServiceFake.cs | 16 +- ...gIsReadyForVideoIntegrationMessageTests.cs | 222 ++++++++++++++++++ .../RunTests.cs | 2 + 4 files changed, 244 insertions(+), 20 deletions(-) create mode 100644 BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/BookingQueueSubscriberFunctionTests/HearingIsReadyForVideoIntegrationMessageTests.cs diff --git a/BookingQueueSubscriber/BookingQueueSubscriber.Services/NotificationApi/AddNotificationRequestMapper.cs b/BookingQueueSubscriber/BookingQueueSubscriber.Services/NotificationApi/AddNotificationRequestMapper.cs index 2a8cb085..303fccd4 100644 --- a/BookingQueueSubscriber/BookingQueueSubscriber.Services/NotificationApi/AddNotificationRequestMapper.cs +++ b/BookingQueueSubscriber/BookingQueueSubscriber.Services/NotificationApi/AddNotificationRequestMapper.cs @@ -66,13 +66,14 @@ public static AddNotificationRequest MapToNewHearingNotification(HearingDto hear var parameters = InitHearingNotificationParams(hearing); NotificationType notificationType; - if (participant.UserRole.Contains(RoleNames.Judge, StringComparison.InvariantCultureIgnoreCase) && eJudFeatureEnabled && participant.HasEjdUsername()) + var isJudge = participant.UserRole.Contains(RoleNames.Judge, StringComparison.InvariantCultureIgnoreCase); + + if (isJudge && eJudFeatureEnabled && participant.HasEjdUsername()) { notificationType = NotificationType.HearingConfirmationEJudJudge; parameters.Add(NotifyParams.Judge, participant.DisplayName); } - else if (participant.UserRole.Contains(RoleNames.Judge, StringComparison.InvariantCultureIgnoreCase) && - !eJudFeatureEnabled) + else if (isJudge) // default to non ejud template for judges without ejud username { notificationType = NotificationType.HearingConfirmationJudge; parameters.Add(NotifyParams.Judge, participant.DisplayName); @@ -184,14 +185,15 @@ public static AddNotificationRequest MapToMultiDayHearingConfirmationNotificatio {NotifyParams.NumberOfDays, days.ToString()} }; NotificationType notificationType; - if (participant.UserRole.Contains(NotifyParams.Judge, StringComparison.InvariantCultureIgnoreCase) && - eJudFeatureEnabled && participant.HasEjdUsername()) + + var isJudge = participant.UserRole.Contains(RoleNames.Judge, StringComparison.InvariantCultureIgnoreCase); + + if (isJudge && eJudFeatureEnabled && participant.HasEjdUsername()) { notificationType = NotificationType.HearingConfirmationEJudJudgeMultiDay; parameters.Add(NotifyParams.Judge, participant.DisplayName); } - else if (participant.UserRole.Contains(RoleNames.Judge, StringComparison.InvariantCultureIgnoreCase) && - !eJudFeatureEnabled) + else if (isJudge) { notificationType = NotificationType.HearingConfirmationJudgeMultiDay; parameters.Add(NotifyParams.Judge, participant.DisplayName); @@ -241,13 +243,15 @@ public static AddNotificationRequest MapToDemoOrTestNotification(HearingDto hear }; NotificationType notificationType; - if (participant.UserRole.Contains(RoleNames.JudicialOfficeHolder, StringComparison.InvariantCultureIgnoreCase) && eJudFeatureEnabled - && participant.HasEjdUsername()) + var isJudge = participant.UserRole.Contains(RoleNames.Judge, StringComparison.InvariantCultureIgnoreCase); + var isJudicialOfficeHolder = participant.UserRole.Contains(RoleNames.JudicialOfficeHolder, StringComparison.InvariantCultureIgnoreCase); + + if (isJudicialOfficeHolder && eJudFeatureEnabled && participant.HasEjdUsername()) { notificationType = NotificationType.EJudJohDemoOrTest; parameters.Add(NotifyParams.JudicialOfficeHolder, $"{participant.FirstName} {participant.LastName}"); } - else if (participant.UserRole.Contains(RoleNames.Judge, StringComparison.InvariantCultureIgnoreCase)) + else if (isJudge) { var contactEmailForNonEJudJudgeUser = GetContactEmailForNonEJudJudgeUser(participant); bool isEmailEjud = participant.HasEjdUsername(); diff --git a/BookingQueueSubscriber/BookingQueueSubscriber.Services/VideoWeb/VideoWebServiceFake.cs b/BookingQueueSubscriber/BookingQueueSubscriber.Services/VideoWeb/VideoWebServiceFake.cs index 807afe72..b2e6ef87 100644 --- a/BookingQueueSubscriber/BookingQueueSubscriber.Services/VideoWeb/VideoWebServiceFake.cs +++ b/BookingQueueSubscriber/BookingQueueSubscriber.Services/VideoWeb/VideoWebServiceFake.cs @@ -28,16 +28,6 @@ public Task PushNewConferenceAdded(Guid conferenceId) public Task PushParticipantsUpdatedMessage(Guid conferenceId, UpdateConferenceParticipantsRequest request) { - DefaultContractResolver contractResolver = new DefaultContractResolver - { - NamingStrategy = new SnakeCaseNamingStrategy() - }; - - string json = JsonConvert.SerializeObject(request, new JsonSerializerSettings - { - ContractResolver = contractResolver, - Formatting = Formatting.Indented - }); PushParticipantsUpdatedMessageCount++; return Task.FromResult(HttpStatusCode.OK); } @@ -62,5 +52,11 @@ public Task PushCloseConsultationBetweenEndpointAndParticipant(Guid conferenceId { return Task.FromResult(HttpStatusCode.OK); } + + public void ClearRequests() + { + PushParticipantsUpdatedMessageCount = PushNewConferenceAddedMessageCount = + PushAllocationToCsoUpdatedMessageCount = PushEndpointsUpdatedMessageCount = 0; + } } } diff --git a/BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/BookingQueueSubscriberFunctionTests/HearingIsReadyForVideoIntegrationMessageTests.cs b/BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/BookingQueueSubscriberFunctionTests/HearingIsReadyForVideoIntegrationMessageTests.cs new file mode 100644 index 00000000..66b89c30 --- /dev/null +++ b/BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/BookingQueueSubscriberFunctionTests/HearingIsReadyForVideoIntegrationMessageTests.cs @@ -0,0 +1,222 @@ +using BookingQueueSubscriber.Services.MessageHandlers.Core; +using BookingQueueSubscriber.Services.NotificationApi; +using BookingQueueSubscriber.Services.VideoApi; +using BookingQueueSubscriber.Services.VideoWeb; +using BookingQueueSubscriber.UnitTests.MessageHandlers; +using Microsoft.Extensions.Logging; +using NotificationApi.Contract; + +namespace BookingQueueSubscriber.UnitTests.BookingQueueSubscriberFunctionTests; + +public class HearingIsReadyForVideoIntegrationMessageTests +{ + private readonly IServiceProvider _serviceProvider = ServiceProviderFactory.ServiceProvider; + private VideoApiServiceFake _videoApiService; + private VideoWebServiceFake _videoWebService; + private NotificationServiceFake _notificationService; + private BookingQueueSubscriberFunction _sut; + private ILogger _logger; + + [SetUp] + public void SetUp() + { + _logger = new Mock>().Object; + _videoApiService = (VideoApiServiceFake) _serviceProvider.GetService(); + _videoWebService = (VideoWebServiceFake) _serviceProvider.GetService(); + _notificationService = (NotificationServiceFake) _serviceProvider.GetService(); + _sut = new BookingQueueSubscriberFunction(new MessageHandlerFactory(ServiceProviderFactory.ServiceProvider), _logger); + } + + [TearDown] + public void TearDown() + { + _videoApiService.ClearRequests(); + _videoWebService.ClearRequests(); + _notificationService.EJudFetaureEnabled = false; + } + + [Test] + public async Task should_process_a_single_day_hearing_ready_event_with_a_judge_only() + { + var judgeEmail = "automation_judge_judge_1@hmcts.net"; + const string message = @"{ + '$type': 'BookingsApi.Infrastructure.Services.IntegrationEvents.EventMessage, BookingsApi.Infrastructure.Services', + 'id': 'e0bbb9ed-ce49-4e69-94e7-3e35e7010206', + 'timestamp': '2023-09-15T09:03:50.889496Z', + 'integration_event': { + '$type': 'BookingsApi.Infrastructure.Services.IntegrationEvents.Events.HearingIsReadyForVideoIntegrationEvent, BookingsApi.Infrastructure.Services', + 'hearing': { + '$type': 'BookingsApi.Infrastructure.Services.Dtos.HearingDto, BookingsApi.Infrastructure.Services', + 'hearing_id': 'e2ef8a71-6d22-486b-8876-a69aceac86d7', + 'group_id': null, + 'scheduled_date_time': '2023-09-15T09:08:46.636188Z', + 'scheduled_duration': 5, + 'case_type': 'Civil Money Claims', + 'case_number': '6918/2815', + 'case_name': 'Bookings Api Integration Automated 9532050', + 'hearing_venue_name': 'Birmingham Civil and Family Justice Centre', + 'record_audio': true, + 'hearing_type': 'First Application' + }, + 'participants': [ + { + '$type': 'BookingsApi.Infrastructure.Services.Dtos.ParticipantDto, BookingsApi.Infrastructure.Services', + 'participant_id': 'c20b90b2-8fb1-4e65-b77b-fd381821ccad', + 'fullname': 'Mrs Automation_Judge Judge_1', + 'username': 'automation_judge_judge_1@hearings.reform.hmcts.net', + 'first_name': 'Automation_Judge', + 'last_name': 'Judge_1', + 'contact_email': 'automation_judge_judge_1@hmcts.net', + 'contact_telephone': '01234567890', + 'display_name': 'Automation_Judge Judge_1', + 'hearing_role': 'Judge', + 'user_role': 'Judge', + 'case_group_type': 'judge', + 'representee': '', + 'linked_participants': [], + 'contact_email_for_non_e_jud_judge_user': '', + 'contact_phone_for_non_e_jud_judge_user': '', + 'send_hearing_notification_if_new': true + } + ], + 'endpoints': [] + } +}"; + await _sut.Run(message); + + _videoApiService.BookNewConferenceCount.Should().Be(1); + _videoWebService.PushNewConferenceAddedMessageCount.Should().Be(1); + _notificationService.NotificationRequests.Should() + .Contain(x => + x.ContactEmail == judgeEmail && x.NotificationType == NotificationType.HearingConfirmationJudge); + } + + [Test] + public async Task should_process_a_single_day_hearing_ready_event_with_a_judge_and_participants() + { + const string message = @"{ + '$type': 'BookingsApi.Infrastructure.Services.IntegrationEvents.EventMessage, BookingsApi.Infrastructure.Services', + 'id': '25839fbd-d19a-4ff8-908d-1c844b9171bc', + 'timestamp': '2023-09-15T09:17:22.211731Z', + 'integration_event': { + '$type': 'BookingsApi.Infrastructure.Services.IntegrationEvents.Events.HearingIsReadyForVideoIntegrationEvent, BookingsApi.Infrastructure.Services', + 'hearing': { + '$type': 'BookingsApi.Infrastructure.Services.Dtos.HearingDto, BookingsApi.Infrastructure.Services', + 'hearing_id': '3946edba-933e-49cb-a328-e350814b6fa2', + 'group_id': null, + 'scheduled_date_time': '2023-09-15T09:22:18.10116Z', + 'scheduled_duration': 5, + 'case_type': 'Civil Money Claims', + 'case_number': '6918/2815', + 'case_name': 'Bookings Api Integration Automated 9532050', + 'hearing_venue_name': 'Birmingham Civil and Family Justice Centre', + 'record_audio': true, + 'hearing_type': 'First Application' + }, + 'participants': [ + { + '$type': 'BookingsApi.Infrastructure.Services.Dtos.ParticipantDto, BookingsApi.Infrastructure.Services', + 'participant_id': 'c0102934-9c49-4cea-bcfc-45fbf503f8a0', + 'fullname': 'Mrs Automation_Respondent LitigantInPerson_1', + 'username': 'automation_respondent_litigantinperson_1@hearings.reform.hmcts.net', + 'first_name': 'Automation_Respondent', + 'last_name': 'LitigantInPerson_1', + 'contact_email': 'automation_respondent_litigantinperson_1@hmcts.net', + 'contact_telephone': '01234567890', + 'display_name': 'Automation_Respondent LitigantInPerson_1', + 'hearing_role': 'Litigant in person', + 'user_role': 'Individual', + 'case_group_type': 'respondent', + 'representee': '', + 'linked_participants': [], + 'contact_email_for_non_e_jud_judge_user': null, + 'contact_phone_for_non_e_jud_judge_user': null, + 'send_hearing_notification_if_new': true + }, + { + '$type': 'BookingsApi.Infrastructure.Services.Dtos.ParticipantDto, BookingsApi.Infrastructure.Services', + 'participant_id': '04850798-4b69-4d86-8fb7-5bafda64703d', + 'fullname': 'Mrs Automation_Respondent Representative_1', + 'username': 'automation_respondent_representative_1@hearings.reform.hmcts.net', + 'first_name': 'Automation_Respondent', + 'last_name': 'Representative_1', + 'contact_email': 'automation_respondent_representative_1@hmcts.net', + 'contact_telephone': '01234567890', + 'display_name': 'Automation_Respondent Representative_1', + 'hearing_role': 'Representative', + 'user_role': 'Representative', + 'case_group_type': 'respondent', + 'representee': 'Automation_Respondent LitigantInPerson_1', + 'linked_participants': [], + 'contact_email_for_non_e_jud_judge_user': null, + 'contact_phone_for_non_e_jud_judge_user': null, + 'send_hearing_notification_if_new': true + }, + { + '$type': 'BookingsApi.Infrastructure.Services.Dtos.ParticipantDto, BookingsApi.Infrastructure.Services', + 'participant_id': '9e30d442-53ca-458b-90d8-9588cee1ddde', + 'fullname': 'Mrs Automation_Judge Judge_1', + 'username': 'automation_judge_judge_1@hearings.reform.hmcts.net', + 'first_name': 'Automation_Judge', + 'last_name': 'Judge_1', + 'contact_email': 'automation_judge_judge_1@hmcts.net', + 'contact_telephone': '01234567890', + 'display_name': 'Automation_Judge Judge_1', + 'hearing_role': 'Judge', + 'user_role': 'Judge', + 'case_group_type': 'judge', + 'representee': '', + 'linked_participants': [], + 'contact_email_for_non_e_jud_judge_user': '', + 'contact_phone_for_non_e_jud_judge_user': '', + 'send_hearing_notification_if_new': true + }, + { + '$type': 'BookingsApi.Infrastructure.Services.Dtos.ParticipantDto, BookingsApi.Infrastructure.Services', + 'participant_id': 'd6275f3c-5b21-4362-a0ca-d8319a68492b', + 'fullname': 'Mrs Automation_Applicant LitigantInPerson_1', + 'username': 'automation_applicant_litigantinperson_1@hearings.reform.hmcts.net', + 'first_name': 'Automation_Applicant', + 'last_name': 'LitigantInPerson_1', + 'contact_email': 'automation_applicant_litigantinperson_1@hmcts.net', + 'contact_telephone': '01234567890', + 'display_name': 'Automation_Applicant LitigantInPerson_1', + 'hearing_role': 'Litigant in person', + 'user_role': 'Individual', + 'case_group_type': 'applicant', + 'representee': '', + 'linked_participants': [], + 'contact_email_for_non_e_jud_judge_user': null, + 'contact_phone_for_non_e_jud_judge_user': null, + 'send_hearing_notification_if_new': true + }, + { + '$type': 'BookingsApi.Infrastructure.Services.Dtos.ParticipantDto, BookingsApi.Infrastructure.Services', + 'participant_id': 'c9ca299c-c83d-4a1b-bef9-efa8fbb1d57f', + 'fullname': 'Mrs Automation_Applicant Representative_1', + 'username': 'automation_applicant_representative_1@hearings.reform.hmcts.net', + 'first_name': 'Automation_Applicant', + 'last_name': 'Representative_1', + 'contact_email': 'automation_applicant_representative_1@hmcts.net', + 'contact_telephone': '01234567890', + 'display_name': 'Automation_Applicant Representative_1', + 'hearing_role': 'Representative', + 'user_role': 'Representative', + 'case_group_type': 'applicant', + 'representee': 'Automation_Applicant LitigantInPerson_1', + 'linked_participants': [], + 'contact_email_for_non_e_jud_judge_user': null, + 'contact_phone_for_non_e_jud_judge_user': null, + 'send_hearing_notification_if_new': true + } + ], + 'endpoints': [] + } +}"; + await _sut.Run(message); + + _videoApiService.BookNewConferenceCount.Should().Be(1); + _videoWebService.PushNewConferenceAddedMessageCount.Should().Be(1); + _notificationService.NotificationRequests.Should().NotBeNullOrEmpty(); + } +} \ No newline at end of file diff --git a/BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/BookingQueueSubscriberFunctionTests/RunTests.cs b/BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/BookingQueueSubscriberFunctionTests/RunTests.cs index 323ce34c..8e0ee6d0 100644 --- a/BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/BookingQueueSubscriberFunctionTests/RunTests.cs +++ b/BookingQueueSubscriber/BookingQueueSubscriber.UnitTests/BookingQueueSubscriberFunctionTests/RunTests.cs @@ -38,6 +38,8 @@ public void OneTimeSetup() public void TearDown() { _videoApiService.ClearRequests(); + _notificationService.EJudFetaureEnabled = false; + _bookingsApi.EJudFeatureEnabled = false; } [Test] From a1bbca0a54b0c579fa43e5213f7c4608d2af4501 Mon Sep 17 00:00:00 2001 From: Shaed Parkar Date: Tue, 19 Sep 2023 12:17:02 +0100 Subject: [PATCH 5/7] use sds path --- charts/vh-booking-queue-subscriber/values.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/vh-booking-queue-subscriber/values.yaml b/charts/vh-booking-queue-subscriber/values.yaml index 33dd4cce..679e219c 100644 --- a/charts/vh-booking-queue-subscriber/values.yaml +++ b/charts/vh-booking-queue-subscriber/values.yaml @@ -1,10 +1,11 @@ --- java: - image: 'sdshmctspublic.azurecr.io/vh/booking-queue-subscriber:latest' + image: "sdshmctspublic.azurecr.io/vh/booking-queue-subscriber:latest" releaseNameOverride: vh-booking-queue-subscriber aadIdentityName: vh-aad-identity readinessPath: /health/liveness livenessPath: /health/liveness + startupPath: /health/liveness applicationPort: 8080 keyVaults: vh-infra-core: @@ -85,7 +86,7 @@ java: function: releaseNameOverride: vh-booking-queue-subscriber scaleType: Object - object: + object: apiVersion: apps/v1 kind: Deployment name: vh-booking-queue-subscriber @@ -99,4 +100,3 @@ function: enabled: true triggerPodIdentityProvider: azure triggers: [] - From bffcca32f3504b4226fda15a17dc34a07db675b8 Mon Sep 17 00:00:00 2001 From: hmcts-ado-cnp <174565+hmcts-ado-cnp[bot]@users.noreply.github.com> Date: Tue, 19 Sep 2023 11:19:57 +0000 Subject: [PATCH 6/7] Bumping chart version --- charts/vh-booking-queue-subscriber/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/vh-booking-queue-subscriber/Chart.yaml b/charts/vh-booking-queue-subscriber/Chart.yaml index d4914a88..bca01f6f 100644 --- a/charts/vh-booking-queue-subscriber/Chart.yaml +++ b/charts/vh-booking-queue-subscriber/Chart.yaml @@ -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 From a2865bcbc9428ff9e565fa7f7b53c824fd996232 Mon Sep 17 00:00:00 2001 From: Shaed Parkar Date: Thu, 21 Sep 2023 14:32:12 +0100 Subject: [PATCH 7/7] update api paths for liveness checks --- .../BookingQueueSubscriber/Health/HealthCheckExtensions.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/BookingQueueSubscriber/BookingQueueSubscriber/Health/HealthCheckExtensions.cs b/BookingQueueSubscriber/BookingQueueSubscriber/Health/HealthCheckExtensions.cs index 0debce87..a133e09e 100644 --- a/BookingQueueSubscriber/BookingQueueSubscriber/Health/HealthCheckExtensions.cs +++ b/BookingQueueSubscriber/BookingQueueSubscriber/Health/HealthCheckExtensions.cs @@ -13,21 +13,21 @@ public static IServiceCollection AddVhHealthChecks(this IServiceCollection servi .AddUrlGroup( new Uri( new Uri(servicesConfiguration.VideoApiUrl), - "/healthcheck/health"), + "/health/liveness"), name: "Video API", failureStatus: HealthStatus.Unhealthy, tags: new[] {"services"}) .AddUrlGroup( new Uri( new Uri(servicesConfiguration.BookingsApiUrl), - "/healthcheck/health"), + "/health/liveness"), name: "Bookings API", failureStatus: HealthStatus.Unhealthy, tags: new[] {"services"}) .AddUrlGroup( new Uri( new Uri(servicesConfiguration.UserApiUrl), - "/healthcheck/health"), + "/health/liveness"), name: "User API", failureStatus: HealthStatus.Unhealthy, tags: new[] {"services"});