Skip to content
This repository was archived by the owner on Jul 28, 2025. It is now read-only.

Commit 9d0e5d8

Browse files
feat: MESH-Handshake (#13)
1 parent bd1f52b commit 9d0e5d8

File tree

6 files changed

+168
-3
lines changed

6 files changed

+168
-3
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ NbssMailboxId=X26ABC1
1313
MeshApiBaseUrl=http://localhost:8700/messageexchange
1414
ASPNETCORE_ENVIRONMENT=Development
1515
FileDiscoveryTimerExpression=*/5 * * * *
16+
MeshHandshakeTimerExpression=0 0 0 * * * # Midnight
1617
QueueUrl=http://127.0.0.1:10001
1718
FileExtractQueueName=file-extract
1819
FileTransformQueueName=file-transform

src/ServiceLayer.Mesh/Configuration/AppConfiguration.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ public class AppConfiguration :
44
IFileDiscoveryFunctionConfiguration,
55
IFileExtractFunctionConfiguration,
66
IFileExtractQueueClientConfiguration,
7-
IFileTransformQueueClientConfiguration
7+
IFileTransformQueueClientConfiguration,
8+
IMeshHandshakeFunctionConfiguration
89
{
910
public string NbssMeshMailboxId => GetRequired("NbssMailboxId");
1011

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace ServiceLayer.Mesh.Configuration
2+
{
3+
public interface IMeshHandshakeFunctionConfiguration
4+
{
5+
string NbssMeshMailboxId { get; }
6+
}
7+
}
Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,40 @@
1-
namespace ServiceLayer.Mesh.Functions;
1+
using Azure;
2+
using Microsoft.Azure.Functions.Worker;
3+
using Microsoft.Extensions.Logging;
4+
using NHS.MESH.Client.Contracts.Services;
5+
using ServiceLayer.Mesh.Configuration;
26

3-
public class MeshHandshakeFunction
7+
namespace ServiceLayer.Mesh.Functions
48
{
9+
public class MeshHandshakeFunction(
10+
ILogger<MeshHandshakeFunction> logger,
11+
IMeshOperationService meshOperationService,
12+
IMeshHandshakeFunctionConfiguration configuration)
13+
{
14+
[Function("MeshHandshakeFunction")]
15+
public async Task Run([TimerTrigger("%MeshHandshakeTimerExpression%")] TimerInfo myTimer)
16+
{
17+
logger.LogInformation("{FunctionName} started", nameof(MeshHandshakeFunction));
518

19+
try
20+
{
21+
var response = await meshOperationService.MeshHandshakeAsync(configuration.NbssMeshMailboxId);
22+
23+
if (response.IsSuccessful)
24+
{
25+
logger.LogInformation("Mesh handshake completed successfully for mailbox {MailboxId}. Status: {Status}",
26+
response.Response.MailboxId, response.IsSuccessful);
27+
}
28+
else
29+
{
30+
logger.LogWarning("Mesh handshake failed for mailbox {MailboxId}. Error: {Error}",
31+
configuration.NbssMeshMailboxId, response.Error);
32+
}
33+
}
34+
catch (Exception ex)
35+
{
36+
logger.LogError(ex, "An error occurred during mesh handshake for mailbox {MailboxId}.", configuration.NbssMeshMailboxId);
37+
}
38+
}
39+
}
640
}

src/ServiceLayer.Mesh/Program.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
using Azure.Storage.Blobs;
1010
using ServiceLayer.Mesh.Configuration;
1111
using ServiceLayer.Mesh.Messaging;
12+
using NHS.MESH.Client.Contracts.Services;
13+
using NHS.MESH.Client.Services;
1214

1315
var host = new HostBuilder()
1416
.ConfigureFunctionsWebApplication()
@@ -63,6 +65,7 @@
6365
services.AddTransient<IFileExtractFunctionConfiguration, AppConfiguration>();
6466
services.AddTransient<IFileExtractQueueClientConfiguration, AppConfiguration>();
6567
services.AddTransient<IFileTransformQueueClientConfiguration, AppConfiguration>();
68+
services.AddTransient<IMeshHandshakeFunctionConfiguration, AppConfiguration>();
6669
});
6770

6871

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using Microsoft.Azure.Functions.Worker;
2+
using Microsoft.Extensions.Logging;
3+
using Moq;
4+
using NHS.MESH.Client.Contracts.Services;
5+
using NHS.MESH.Client.Models;
6+
using ServiceLayer.Mesh.Configuration;
7+
using ServiceLayer.Mesh.Functions;
8+
9+
namespace ServiceLayer.Mesh.Tests.Functions;
10+
11+
public class MeshHandshakeFunctionTests
12+
{
13+
private readonly Mock<ILogger<MeshHandshakeFunction>> _loggerMock;
14+
private readonly Mock<IMeshOperationService> _meshOperationServiceMock;
15+
private readonly Mock<IMeshHandshakeFunctionConfiguration> _configurationMock;
16+
private readonly MeshHandshakeFunction _function;
17+
private readonly TimerInfo _timerInfo;
18+
private const string TestMailboxId = "test-mailbox-123";
19+
20+
public MeshHandshakeFunctionTests()
21+
{
22+
_loggerMock = new Mock<ILogger<MeshHandshakeFunction>>();
23+
_meshOperationServiceMock = new Mock<IMeshOperationService>();
24+
_configurationMock = new Mock<IMeshHandshakeFunctionConfiguration>();
25+
_timerInfo = new TimerInfo();
26+
27+
_configurationMock.Setup(c => c.NbssMeshMailboxId).Returns(TestMailboxId);
28+
_function = new MeshHandshakeFunction(
29+
_loggerMock.Object,
30+
_meshOperationServiceMock.Object,
31+
_configurationMock.Object
32+
);
33+
}
34+
35+
[Fact]
36+
public async Task Run_SuccessfulHandshake_LogsSuccessAndCompletion()
37+
{
38+
// Arrange
39+
var successfulResponse = new MeshResponse<HandshakeResponse>
40+
{
41+
IsSuccessful = true,
42+
Response = new HandshakeResponse { MailboxId = TestMailboxId }
43+
};
44+
_meshOperationServiceMock
45+
.Setup(s => s.MeshHandshakeAsync(TestMailboxId))
46+
.ReturnsAsync(successfulResponse);
47+
48+
// Act
49+
await _function.Run(_timerInfo);
50+
51+
// Assert
52+
_meshOperationServiceMock.Verify(s => s.MeshHandshakeAsync(TestMailboxId), Times.Once());
53+
VerifyLogMessage(LogLevel.Information, "MeshHandshakeFunction started");
54+
VerifyLogMessage(LogLevel.Information, "Mesh handshake completed successfully for mailbox");
55+
}
56+
57+
[Fact]
58+
public async Task Run_FailedHandshake_LogsWarningAndCompletion()
59+
{
60+
// Arrange
61+
var failedResponse = new MeshResponse<HandshakeResponse>
62+
{
63+
IsSuccessful = false,
64+
Error = new APIErrorResponse
65+
{
66+
ErrorDescription = "Authentication failed"
67+
}
68+
};
69+
_meshOperationServiceMock
70+
.Setup(s => s.MeshHandshakeAsync(TestMailboxId))
71+
.ReturnsAsync(failedResponse);
72+
73+
// Act
74+
await _function.Run(_timerInfo);
75+
76+
// Assert
77+
_meshOperationServiceMock.Verify(s => s.MeshHandshakeAsync(TestMailboxId), Times.Once());
78+
VerifyLogMessage(LogLevel.Information, "MeshHandshakeFunction started");
79+
VerifyLogMessage(LogLevel.Warning, "Mesh handshake failed");
80+
}
81+
82+
[Fact]
83+
public async Task Run_ExceptionThrown_LogsErrorAndCompletion()
84+
{
85+
// Arrange
86+
var expectedException = new InvalidOperationException("Connection failed");
87+
_meshOperationServiceMock
88+
.Setup(s => s.MeshHandshakeAsync(TestMailboxId))
89+
.ThrowsAsync(expectedException);
90+
91+
// Act
92+
await _function.Run(_timerInfo);
93+
94+
// Assert
95+
_meshOperationServiceMock.Verify(s => s.MeshHandshakeAsync(TestMailboxId), Times.Once());
96+
VerifyLogMessage(LogLevel.Information, "MeshHandshakeFunction started");
97+
VerifyLogMessage(LogLevel.Error, "An error occurred during mesh handshake");
98+
_loggerMock.Verify(
99+
x => x.Log(
100+
LogLevel.Error,
101+
It.IsAny<EventId>(),
102+
It.IsAny<It.IsAnyType>(),
103+
expectedException,
104+
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
105+
Times.Once);
106+
}
107+
108+
private void VerifyLogMessage(LogLevel level, string expectedMessage)
109+
{
110+
_loggerMock.Verify(
111+
x => x.Log(
112+
level,
113+
It.IsAny<EventId>(),
114+
It.Is<It.IsAnyType>((v, t) => (v.ToString() ?? string.Empty).Contains(expectedMessage)),
115+
It.IsAny<Exception?>(),
116+
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
117+
Times.Once);
118+
}
119+
}

0 commit comments

Comments
 (0)