Skip to content
This repository was archived by the owner on Jul 28, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
75d78ec
feat: migrations. docker, program and dbContext, csproj updated for …
Warren-Pitterson May 12, 2025
6779b27
feat: db-setup no longer needed in compose
Warren-Pitterson May 12, 2025
d75f4dc
feat: connectionString no longer hardcoded
Warren-Pitterson May 12, 2025
de14772
chore: variable casing
Warren-Pitterson May 12, 2025
2fd2c48
feat: logging added
Warren-Pitterson May 12, 2025
42b2525
chore: tidy up
Warren-Pitterson May 12, 2025
a9729ae
chore: explicit directories
Warren-Pitterson May 12, 2025
1ac6c1a
Merge branch 'main' into feat/Database-migration
Warren-Pitterson May 13, 2025
6d6c015
feat: change targer from API to Mesh project
Warren-Pitterson May 13, 2025
fad274a
feat: refactor to target mesh, builds database with migrations,
Warren-Pitterson May 13, 2025
a1ef903
refactor: revert unnecessary API changes
Warren-Pitterson May 13, 2025
12bb95d
Merge branch 'main' into feat/DTOSS-8740-MESH-Handshake
Warren-Pitterson May 15, 2025
3acca93
feat: MeshHandshake function and tests
Warren-Pitterson May 15, 2025
023db4a
chore: removed comment
Warren-Pitterson May 15, 2025
547f6aa
chore: removed unnecessary usings
Warren-Pitterson May 15, 2025
320b4f3
fix: removed package wrongfully added to incorrect project
Warren-Pitterson May 15, 2025
2e40b7d
feat: IMeshHandshakeFunctionConfiguration added to program.cs
Warren-Pitterson May 15, 2025
e3170a6
feat: update .env example
Warren-Pitterson May 15, 2025
547fa8f
feat: removed duplicate logging and updated tests
Warren-Pitterson May 15, 2025
0bfbc8b
feat: removed unnecessary DI registration
Warren-Pitterson May 15, 2025
2ca3fc0
test: sonarQube issue fix
Warren-Pitterson May 15, 2025
d38495b
refactor: remove redundant jump
Warren-Pitterson May 15, 2025
fe3ddbd
refactor: remove time from log statements
Warren-Pitterson May 16, 2025
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
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ NbssMailboxId=X26ABC1
MeshApiBaseUrl=http://localhost:8700/messageexchange
ASPNETCORE_ENVIRONMENT=Development
FileDiscoveryTimerExpression=*/5 * * * *
MeshHandshakeTimerExpression=0 0 0 * * * # Midnight
QueueUrl=http://127.0.0.1:10001
FileExtractQueueName=file-extract
FileTransformQueueName=file-transform
Expand Down
3 changes: 2 additions & 1 deletion src/ServiceLayer.Mesh/Configuration/AppConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ public class AppConfiguration :
IFileDiscoveryFunctionConfiguration,
IFileExtractFunctionConfiguration,
IFileExtractQueueClientConfiguration,
IFileTransformQueueClientConfiguration
IFileTransformQueueClientConfiguration,
IMeshHandshakeFunctionConfiguration
{
public string NbssMeshMailboxId => GetRequired("NbssMailboxId");

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace ServiceLayer.Mesh.Configuration
{
public interface IMeshHandshakeFunctionConfiguration
{
string NbssMeshMailboxId { get; }
}
}
38 changes: 36 additions & 2 deletions src/ServiceLayer.Mesh/Functions/MeshHandshakeFunction.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,40 @@
namespace ServiceLayer.Mesh.Functions;
using Azure;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using NHS.MESH.Client.Contracts.Services;
using ServiceLayer.Mesh.Configuration;

public class MeshHandshakeFunction
namespace ServiceLayer.Mesh.Functions
{
public class MeshHandshakeFunction(
ILogger<MeshHandshakeFunction> logger,
IMeshOperationService meshOperationService,
IMeshHandshakeFunctionConfiguration configuration)
{
[Function("MeshHandshakeFunction")]
public async Task Run([TimerTrigger("%MeshHandshakeTimerExpression%")] TimerInfo myTimer)
{
logger.LogInformation("{FunctionName} started", nameof(MeshHandshakeFunction));

try
{
var response = await meshOperationService.MeshHandshakeAsync(configuration.NbssMeshMailboxId);

if (response.IsSuccessful)
{
logger.LogInformation("Mesh handshake completed successfully for mailbox {MailboxId}. Status: {Status}",
response.Response.MailboxId, response.IsSuccessful);
}
else
{
logger.LogWarning("Mesh handshake failed for mailbox {MailboxId}. Error: {Error}",
configuration.NbssMeshMailboxId, response.Error);
}
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred during mesh handshake for mailbox {MailboxId}.", configuration.NbssMeshMailboxId);
}
}
}
}
3 changes: 3 additions & 0 deletions src/ServiceLayer.Mesh/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
using Azure.Storage.Blobs;
using ServiceLayer.Mesh.Configuration;
using ServiceLayer.Mesh.Messaging;
using NHS.MESH.Client.Contracts.Services;
using NHS.MESH.Client.Services;

var host = new HostBuilder()
.ConfigureFunctionsWebApplication()
Expand Down Expand Up @@ -63,6 +65,7 @@
services.AddTransient<IFileExtractFunctionConfiguration, AppConfiguration>();
services.AddTransient<IFileExtractQueueClientConfiguration, AppConfiguration>();
services.AddTransient<IFileTransformQueueClientConfiguration, AppConfiguration>();
services.AddTransient<IMeshHandshakeFunctionConfiguration, AppConfiguration>();
});


Expand Down
119 changes: 119 additions & 0 deletions tests/ServiceLayer.Mesh.Tests/Functions/MeshHandshakeFunctionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using Moq;
using NHS.MESH.Client.Contracts.Services;
using NHS.MESH.Client.Models;
using ServiceLayer.Mesh.Configuration;
using ServiceLayer.Mesh.Functions;

namespace ServiceLayer.Mesh.Tests.Functions;

public class MeshHandshakeFunctionTests
{
private readonly Mock<ILogger<MeshHandshakeFunction>> _loggerMock;
private readonly Mock<IMeshOperationService> _meshOperationServiceMock;
private readonly Mock<IMeshHandshakeFunctionConfiguration> _configurationMock;
private readonly MeshHandshakeFunction _function;
private readonly TimerInfo _timerInfo;
private const string TestMailboxId = "test-mailbox-123";

public MeshHandshakeFunctionTests()
{
_loggerMock = new Mock<ILogger<MeshHandshakeFunction>>();
_meshOperationServiceMock = new Mock<IMeshOperationService>();
_configurationMock = new Mock<IMeshHandshakeFunctionConfiguration>();
_timerInfo = new TimerInfo();

_configurationMock.Setup(c => c.NbssMeshMailboxId).Returns(TestMailboxId);
_function = new MeshHandshakeFunction(
_loggerMock.Object,
_meshOperationServiceMock.Object,
_configurationMock.Object
);
}

[Fact]
public async Task Run_SuccessfulHandshake_LogsSuccessAndCompletion()
{
// Arrange
var successfulResponse = new MeshResponse<HandshakeResponse>
{
IsSuccessful = true,
Response = new HandshakeResponse { MailboxId = TestMailboxId }
};
_meshOperationServiceMock
.Setup(s => s.MeshHandshakeAsync(TestMailboxId))
.ReturnsAsync(successfulResponse);

// Act
await _function.Run(_timerInfo);

// Assert
_meshOperationServiceMock.Verify(s => s.MeshHandshakeAsync(TestMailboxId), Times.Once());
VerifyLogMessage(LogLevel.Information, "MeshHandshakeFunction started");
VerifyLogMessage(LogLevel.Information, "Mesh handshake completed successfully for mailbox");
}

[Fact]
public async Task Run_FailedHandshake_LogsWarningAndCompletion()
{
// Arrange
var failedResponse = new MeshResponse<HandshakeResponse>
{
IsSuccessful = false,
Error = new APIErrorResponse
{
ErrorDescription = "Authentication failed"
}
};
_meshOperationServiceMock
.Setup(s => s.MeshHandshakeAsync(TestMailboxId))
.ReturnsAsync(failedResponse);

// Act
await _function.Run(_timerInfo);

// Assert
_meshOperationServiceMock.Verify(s => s.MeshHandshakeAsync(TestMailboxId), Times.Once());
VerifyLogMessage(LogLevel.Information, "MeshHandshakeFunction started");
VerifyLogMessage(LogLevel.Warning, "Mesh handshake failed");
}

[Fact]
public async Task Run_ExceptionThrown_LogsErrorAndCompletion()
{
// Arrange
var expectedException = new InvalidOperationException("Connection failed");
_meshOperationServiceMock
.Setup(s => s.MeshHandshakeAsync(TestMailboxId))
.ThrowsAsync(expectedException);

// Act
await _function.Run(_timerInfo);

// Assert
_meshOperationServiceMock.Verify(s => s.MeshHandshakeAsync(TestMailboxId), Times.Once());
VerifyLogMessage(LogLevel.Information, "MeshHandshakeFunction started");
VerifyLogMessage(LogLevel.Error, "An error occurred during mesh handshake");
_loggerMock.Verify(
x => x.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.IsAny<It.IsAnyType>(),
expectedException,
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);
}

private void VerifyLogMessage(LogLevel level, string expectedMessage)
{
_loggerMock.Verify(
x => x.Log(
level,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => (v.ToString() ?? string.Empty).Contains(expectedMessage)),
It.IsAny<Exception?>(),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);
}
}
Loading