From 42b25d536a70381a5d39cadff6c7b5d4f9ef6648 Mon Sep 17 00:00:00 2001 From: Michael Clayson Date: Fri, 6 Sep 2024 10:37:00 +0100 Subject: [PATCH 1/9] temp commit --- .../Clients/MeshConnectClient.cs | 35 +++++++++++++------ .../NHS.Mesh.Client/NHS.Mesh.Client.csproj | 2 +- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/application/DotNetMeshClient/NHS.Mesh.Client/Clients/MeshConnectClient.cs b/application/DotNetMeshClient/NHS.Mesh.Client/Clients/MeshConnectClient.cs index eb82bee..ad73b2a 100644 --- a/application/DotNetMeshClient/NHS.Mesh.Client/Clients/MeshConnectClient.cs +++ b/application/DotNetMeshClient/NHS.Mesh.Client/Clients/MeshConnectClient.cs @@ -7,6 +7,7 @@ namespace NHS.MESH.Client.Clients; +using Microsoft.Extensions.Logging; using NHS.MESH.Client.Configuration; using NHS.MESH.Client.Contracts.Clients; using NHS.MESH.Client.Contracts.Configurations; @@ -15,6 +16,8 @@ namespace NHS.MESH.Client.Clients; using System.Net; using System.Net.Http; using System.Runtime.InteropServices; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; /// The MESH connect client for MESH API calls. public class MeshConnectClient : IMeshConnectClient @@ -27,13 +30,16 @@ public class MeshConnectClient : IMeshConnectClient private readonly MailboxConfigurationResolver _mailboxConfigurationResolver; + private readonly ILogger _logger; + /// Initializes a new instance of the class. /// The HTTP Client. /// The MESH Connect Configuration. - public MeshConnectClient(IMeshConnectConfiguration meshConnectConfiguration, MailboxConfigurationResolver mailboxConfigurationResolver) + public MeshConnectClient(IMeshConnectConfiguration meshConnectConfiguration, MailboxConfigurationResolver mailboxConfigurationResolver, ILogger logger) { _meshConnectConfiguration = meshConnectConfiguration; _mailboxConfigurationResolver = mailboxConfigurationResolver; + _logger = logger; } /// @@ -43,6 +49,7 @@ public MeshConnectClient(IMeshConnectConfiguration meshConnectConfiguration, Mai /// public async Task SendRequestAsync(HttpRequestMessage httpRequestMessage, string mailboxId) { + _logger.LogInformation("Sending HttpRequest to mesh"); MailboxConfiguration mailboxConfiguration = _mailboxConfigurationResolver.GetMailboxConfiguration(mailboxId); var authHeader = MeshAuthorizationHelper.GenerateAuthHeaderValue(mailboxId,mailboxConfiguration.Password!,mailboxConfiguration.SharedKey!); httpRequestMessage.Headers.Add("authorization", authHeader); @@ -62,15 +69,23 @@ private async Task SendHttpRequest(HttpRequestMessage httpR if(mailboxConfiguration.Cert != null) { - var certificate = mailboxConfiguration.Cert; - handler.ClientCertificateOptions = ClientCertificateOption.Manual; - handler.ClientCertificates.Add(certificate); - if(httpRequestMessage.RequestUri.Host == "localhost"){ - handler.ServerCertificateCustomValidationCallback = - (httpRequestMessage, cert, cetChain, policyErrors) => - { - return true; - }; //ignores the ca for localhost testing + byte[] pfxRawData = mailboxConfiguration.Cert.Export(X509ContentType.Pfx, "123456"); + + using (X509Certificate2 pfxCertWithKey = new X509Certificate2(pfxRawData, "123456")) + { + _logger.LogInformation("Adding Certificate to HTTP Call"); + handler.ClientCertificateOptions = ClientCertificateOption.Manual; + handler.ClientCertificates.Add(pfxCertWithKey); + //handler.SslProtocols.Tls12; + handler.SslProtocols = SslProtocols.Tls12; + //if(httpRequestMessage.RequestUri.Host == "localhost"){ + handler.ServerCertificateCustomValidationCallback = + (httpRequestMessage, cert, cetChain, policyErrors) => + { + _logger.LogWarning("Bypassing Server certificate Validation Check"); + return true; + }; //ignores the ca for localhost testing + //} } } diff --git a/application/DotNetMeshClient/NHS.Mesh.Client/NHS.Mesh.Client.csproj b/application/DotNetMeshClient/NHS.Mesh.Client/NHS.Mesh.Client.csproj index a4b6129..7a57d81 100644 --- a/application/DotNetMeshClient/NHS.Mesh.Client/NHS.Mesh.Client.csproj +++ b/application/DotNetMeshClient/NHS.Mesh.Client/NHS.Mesh.Client.csproj @@ -3,7 +3,7 @@ net8.0 enable enable - 1.0.0 + 1.0.3 Michael Clayson NHS Digital dotnet mesh client From 1fe29070ae383571b2350803236ec56ed7d3f5ba Mon Sep 17 00:00:00 2001 From: Michael Clayson Date: Fri, 6 Sep 2024 13:25:15 +0100 Subject: [PATCH 2/9] Refactor Cert Auth --- .../Clients/MeshConnectClient.cs | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/application/DotNetMeshClient/NHS.Mesh.Client/Clients/MeshConnectClient.cs b/application/DotNetMeshClient/NHS.Mesh.Client/Clients/MeshConnectClient.cs index ad73b2a..5b327fa 100644 --- a/application/DotNetMeshClient/NHS.Mesh.Client/Clients/MeshConnectClient.cs +++ b/application/DotNetMeshClient/NHS.Mesh.Client/Clients/MeshConnectClient.cs @@ -49,12 +49,22 @@ public MeshConnectClient(IMeshConnectConfiguration meshConnectConfiguration, Mai /// public async Task SendRequestAsync(HttpRequestMessage httpRequestMessage, string mailboxId) { - _logger.LogInformation("Sending HttpRequest to mesh"); - MailboxConfiguration mailboxConfiguration = _mailboxConfigurationResolver.GetMailboxConfiguration(mailboxId); - var authHeader = MeshAuthorizationHelper.GenerateAuthHeaderValue(mailboxId,mailboxConfiguration.Password!,mailboxConfiguration.SharedKey!); - httpRequestMessage.Headers.Add("authorization", authHeader); - - return await SendHttpRequest(httpRequestMessage,mailboxConfiguration); + try + { + _logger.LogInformation($"Sending HttpRequest to mesh: { httpRequestMessage.RequestUri }"); + MailboxConfiguration mailboxConfiguration = _mailboxConfigurationResolver.GetMailboxConfiguration(mailboxId); + var authHeader = MeshAuthorizationHelper.GenerateAuthHeaderValue(mailboxId,mailboxConfiguration.Password!,mailboxConfiguration.SharedKey!); + httpRequestMessage.Headers.Add("authorization", authHeader); + var result = await SendHttpRequest(httpRequestMessage,mailboxConfiguration); + var contentString = await result.Content.ReadAsStringAsync(); + _logger.LogInformation(contentString); + return result; + } + catch(Exception ex) + { + _logger.LogCritical(ex,"Exception encountered while calling MESH API"); + throw; + } } @@ -69,13 +79,13 @@ private async Task SendHttpRequest(HttpRequestMessage httpR if(mailboxConfiguration.Cert != null) { - byte[] pfxRawData = mailboxConfiguration.Cert.Export(X509ContentType.Pfx, "123456"); + // byte[] pfxRawData = mailboxConfiguration.Cert.Export(X509ContentType.Pfx, "123456"); - using (X509Certificate2 pfxCertWithKey = new X509Certificate2(pfxRawData, "123456")) - { + // using (X509Certificate2 pfxCertWithKey = new X509Certificate2(pfxRawData, "123456")) + // { _logger.LogInformation("Adding Certificate to HTTP Call"); handler.ClientCertificateOptions = ClientCertificateOption.Manual; - handler.ClientCertificates.Add(pfxCertWithKey); + handler.ClientCertificates.Add(mailboxConfiguration.Cert); //handler.SslProtocols.Tls12; handler.SslProtocols = SslProtocols.Tls12; //if(httpRequestMessage.RequestUri.Host == "localhost"){ @@ -86,7 +96,7 @@ private async Task SendHttpRequest(HttpRequestMessage httpR return true; }; //ignores the ca for localhost testing //} - } + //} } httpClient = new HttpClient(handler) From bec82371546b0511035ba3950b63aa96ee9f4b42 Mon Sep 17 00:00:00 2001 From: Michael Clayson Date: Mon, 9 Sep 2024 15:47:26 +0100 Subject: [PATCH 3/9] wip: certificate auth changes --- .../NHS.Mesh.Client/Clients/MeshConnectClient.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/application/DotNetMeshClient/NHS.Mesh.Client/Clients/MeshConnectClient.cs b/application/DotNetMeshClient/NHS.Mesh.Client/Clients/MeshConnectClient.cs index 5b327fa..2a1109e 100644 --- a/application/DotNetMeshClient/NHS.Mesh.Client/Clients/MeshConnectClient.cs +++ b/application/DotNetMeshClient/NHS.Mesh.Client/Clients/MeshConnectClient.cs @@ -92,6 +92,15 @@ private async Task SendHttpRequest(HttpRequestMessage httpR handler.ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => { + // It is possible to inspect the certificate provided by the server. + _logger.LogInformation($"Requested URI: {httpRequestMessage.RequestUri}"); + _logger.LogInformation($"Effective date: {cert?.GetEffectiveDateString()}"); + _logger.LogInformation($"Exp date: {cert?.GetExpirationDateString()}"); + _logger.LogInformation($"Issuer: {cert?.Issuer}"); + _logger.LogInformation($"Subject: {cert?.Subject}"); + + // Based on the custom logic it is possible to decide whether the client considers certificate valid or not + _logger.LogInformation($"Errors: {policyErrors}"); _logger.LogWarning("Bypassing Server certificate Validation Check"); return true; }; //ignores the ca for localhost testing From 3843125ab8493631371d07a6e087033deac95684 Mon Sep 17 00:00:00 2001 From: Michael Clayson Date: Tue, 10 Sep 2024 11:41:59 +0100 Subject: [PATCH 4/9] feat: adding Content encoding to MetaData add setting on compressed send, setting helpers to internal --- .../NHS.Mesh.Client/Helpers/FileHelpers.cs | 11 +++++++---- .../NHS.Mesh.Client/Models/MessageMetaData.cs | 1 + .../NHS.Mesh.Client/Services/MeshOutboxService.cs | 13 +++++++++++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/application/DotNetMeshClient/NHS.Mesh.Client/Helpers/FileHelpers.cs b/application/DotNetMeshClient/NHS.Mesh.Client/Helpers/FileHelpers.cs index 8ed3a9a..4ad915f 100644 --- a/application/DotNetMeshClient/NHS.Mesh.Client/Helpers/FileHelpers.cs +++ b/application/DotNetMeshClient/NHS.Mesh.Client/Helpers/FileHelpers.cs @@ -5,7 +5,7 @@ namespace NHS.MESH.Client.Helpers; public static class FileHelpers { - public static async Task CreateFileAttachment(HttpResponseMessage httpResponseMessage) + internal static async Task CreateFileAttachment(HttpResponseMessage httpResponseMessage) { int? chunkNumber = null; @@ -26,14 +26,17 @@ public static async Task CreateFileAttachment(HttpResponseMessag }; return fileAttachment; } - public static MessageMetaData CreateMessageMetaData(HttpResponseMessage httpResponseMessage) + internal static MessageMetaData CreateMessageMetaData(HttpResponseMessage httpResponseMessage) { return new MessageMetaData { WorkflowID = httpResponseMessage.Headers.GetHeaderItemValue("mex-workflowid"), ToMailbox = httpResponseMessage.Headers.GetHeaderItemValue("mex-to"), FromMailbox = httpResponseMessage.Headers.GetHeaderItemValue("mex-from"), - MessageId = httpResponseMessage.Headers.GetHeaderItemValue("mex-messageid") + MessageId = httpResponseMessage.Headers.GetHeaderItemValue("mex-messageid"), + ContentEncoding = httpResponseMessage.Headers.GetHeaderItemValue("content-encoding"), + + }; } @@ -63,7 +66,7 @@ public static string GenerateChecksum(byte[] data) } } - public static bool IsFileTooLarge(byte[] data, long MaxLength) + internal static bool IsFileTooLarge(byte[] data, long MaxLength) { return data.Length >= MaxLength; } diff --git a/application/DotNetMeshClient/NHS.Mesh.Client/Models/MessageMetaData.cs b/application/DotNetMeshClient/NHS.Mesh.Client/Models/MessageMetaData.cs index 5f4c7ab..5ee2663 100644 --- a/application/DotNetMeshClient/NHS.Mesh.Client/Models/MessageMetaData.cs +++ b/application/DotNetMeshClient/NHS.Mesh.Client/Models/MessageMetaData.cs @@ -9,5 +9,6 @@ public class MessageMetaData public string? FileName { get; set; } public string? MessageType { get; set; } public string? ChunkRange {get; set;} + public string? ContentEncoding {get;set;} public int? TotalChunks { get; set;} } diff --git a/application/DotNetMeshClient/NHS.Mesh.Client/Services/MeshOutboxService.cs b/application/DotNetMeshClient/NHS.Mesh.Client/Services/MeshOutboxService.cs index 70dd31c..d9dfa99 100644 --- a/application/DotNetMeshClient/NHS.Mesh.Client/Services/MeshOutboxService.cs +++ b/application/DotNetMeshClient/NHS.Mesh.Client/Services/MeshOutboxService.cs @@ -63,7 +63,7 @@ public async Task> SendCompressedMessageAsync( // Body var content = await FileHelpers.CompressFileAsync(file.Content); - var meshResponse = await SendSingleMessage(uri, fromMailboxId, toMailboxId, workflowId, content, file.FileName, localId, subject, includeChecksum); + var meshResponse = await SendSingleCompressedMessage(uri, fromMailboxId, toMailboxId, workflowId, content, file.FileName, localId, subject, includeChecksum); return await ResponseHelper.CreateMeshResponse(meshResponse, async _ => JsonSerializer.Deserialize(await _.Content.ReadAsStringAsync())); } @@ -148,7 +148,6 @@ public async Task> SendChunkedMessageAsync(str Uri chunkMessageURI = new Uri($"{_meshConnectConfiguration.MeshApiBaseUrl}/{fromMailboxId}/{_meshConnectConfiguration.MeshApiOutboxUriPath}/{messageId}/{i + 1}"); var chunk = await FileHelpers.CompressFileAsync(chunkedFiles[i]); var chunkMeshResponse = await SendMessageChunk(chunkMessageURI, fromMailboxId, toMailboxId, workflowId, chunk, file.FileName, i + 1, chunkedFiles.Count, localId, subject, includeChecksum); - var responseString = await chunkMeshResponse.Content.ReadAsStringAsync(); //TODO REMOVE var meshResponse = await ResponseHelper.CreateMeshResponse(chunkMeshResponse, async _ => JsonSerializer.Deserialize(await _.Content.ReadAsStringAsync())); if (!meshResponse.IsSuccessful) @@ -207,6 +206,16 @@ private async Task SendSingleMessage(Uri uri, string fromMa } + private async Task SendSingleCompressedMessage(Uri uri, string fromMailboxId, string toMailboxId, string workflowId, HttpContent content, string fileName, string? localId = null, string? subject = null, bool includeChecksum = false) + { + var httpRequestMessage = await BuildMessage(uri, fromMailboxId, toMailboxId, workflowId, content, fileName, localId, subject, includeChecksum); + httpRequestMessage.Headers.TryAddWithoutValidation("content-encoding","GZIP"); + var meshResponse = await _meshConnectClient.SendRequestAsync(httpRequestMessage,fromMailboxId); + + return meshResponse; + + } + private async Task SendMessageChunk(Uri uri, string fromMailboxId, string toMailboxId, string workflowId, HttpContent content, string fileName, int chunkNumber, int chunkLength, string? localId = null, string? subject = null, bool includeChecksum = false) { var httpRequestMessage = await BuildMessage(uri, fromMailboxId, toMailboxId, workflowId, content, fileName, localId, subject, includeChecksum); From f195b2950b2522d7f753af3692688be7d3b791c5 Mon Sep 17 00:00:00 2001 From: Michael Clayson Date: Tue, 10 Sep 2024 15:35:08 +0100 Subject: [PATCH 5/9] chore: remove response logging --- .../Clients/MeshConnectClient.cs | 40 +++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/application/DotNetMeshClient/NHS.Mesh.Client/Clients/MeshConnectClient.cs b/application/DotNetMeshClient/NHS.Mesh.Client/Clients/MeshConnectClient.cs index 2a1109e..a55e177 100644 --- a/application/DotNetMeshClient/NHS.Mesh.Client/Clients/MeshConnectClient.cs +++ b/application/DotNetMeshClient/NHS.Mesh.Client/Clients/MeshConnectClient.cs @@ -56,8 +56,6 @@ public async Task SendRequestAsync(HttpRequestMessage httpR var authHeader = MeshAuthorizationHelper.GenerateAuthHeaderValue(mailboxId,mailboxConfiguration.Password!,mailboxConfiguration.SharedKey!); httpRequestMessage.Headers.Add("authorization", authHeader); var result = await SendHttpRequest(httpRequestMessage,mailboxConfiguration); - var contentString = await result.Content.ReadAsStringAsync(); - _logger.LogInformation(contentString); return result; } catch(Exception ex) @@ -79,33 +77,25 @@ private async Task SendHttpRequest(HttpRequestMessage httpR if(mailboxConfiguration.Cert != null) { - // byte[] pfxRawData = mailboxConfiguration.Cert.Export(X509ContentType.Pfx, "123456"); - - // using (X509Certificate2 pfxCertWithKey = new X509Certificate2(pfxRawData, "123456")) - // { _logger.LogInformation("Adding Certificate to HTTP Call"); handler.ClientCertificateOptions = ClientCertificateOption.Manual; handler.ClientCertificates.Add(mailboxConfiguration.Cert); - //handler.SslProtocols.Tls12; handler.SslProtocols = SslProtocols.Tls12; - //if(httpRequestMessage.RequestUri.Host == "localhost"){ - handler.ServerCertificateCustomValidationCallback = - (httpRequestMessage, cert, cetChain, policyErrors) => - { - // It is possible to inspect the certificate provided by the server. - _logger.LogInformation($"Requested URI: {httpRequestMessage.RequestUri}"); - _logger.LogInformation($"Effective date: {cert?.GetEffectiveDateString()}"); - _logger.LogInformation($"Exp date: {cert?.GetExpirationDateString()}"); - _logger.LogInformation($"Issuer: {cert?.Issuer}"); - _logger.LogInformation($"Subject: {cert?.Subject}"); - - // Based on the custom logic it is possible to decide whether the client considers certificate valid or not - _logger.LogInformation($"Errors: {policyErrors}"); - _logger.LogWarning("Bypassing Server certificate Validation Check"); - return true; - }; //ignores the ca for localhost testing - //} - //} + handler.ServerCertificateCustomValidationCallback = + (httpRequestMessage, cert, cetChain, policyErrors) => + { + // It is possible to inspect the certificate provided by the server. + _logger.LogInformation($"Requested URI: {httpRequestMessage.RequestUri}"); + _logger.LogInformation($"Effective date: {cert?.GetEffectiveDateString()}"); + _logger.LogInformation($"Exp date: {cert?.GetExpirationDateString()}"); + _logger.LogInformation($"Issuer: {cert?.Issuer}"); + _logger.LogInformation($"Subject: {cert?.Subject}"); + + // Based on the custom logic it is possible to decide whether the client considers certificate valid or not + _logger.LogInformation($"Errors: {policyErrors}"); + _logger.LogWarning("Bypassing Server certificate Validation Check"); + return true; + }; } httpClient = new HttpClient(handler) From 9a480624841b4b6ecf2b61bbac93b60864b0c8cd Mon Sep 17 00:00:00 2001 From: Michael Clayson Date: Tue, 17 Sep 2024 12:43:22 +0100 Subject: [PATCH 6/9] readme --- README.md | 197 +++++++++++++++++++++++++++++++++++++----------------- Usage.md | 0 2 files changed, 136 insertions(+), 61 deletions(-) create mode 100644 Usage.md diff --git a/README.md b/README.md index 29f8680..8558b85 100644 --- a/README.md +++ b/README.md @@ -1,107 +1,182 @@ -# Repository Template +# dotnet-mesh-client [![CI/CD Pull Request](https://github.com/nhs-england-tools/repository-template/actions/workflows/cicd-1-pull-request.yaml/badge.svg)](https://github.com/nhs-england-tools/repository-template/actions/workflows/cicd-1-pull-request.yaml) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=repository-template&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=repository-template) -Start with an overview or a brief description of what the project is about and what it does. For example - - -Welcome to our repository template designed to streamline your project setup! This robust template provides a reliable starting point for your new projects, covering an essential tech stack and encouraging best practices in documenting. - -This repository template aims to foster a user-friendly development environment by ensuring that every included file is concise and adequately self-documented. By adhering to this standard, we can promote increased clarity and maintainability throughout your project's lifecycle. Bundled within this template are resources that pave the way for seamless repository creation. Currently supported technologies are: - -- Terraform -- Docker - -Make use of this repository template to expedite your project setup and enhance your productivity right from the get-go. Enjoy the advantage of having a well-structured, self-documented project that reduces overhead and increases focus on what truly matters - coding! +A dotnet client for accessing the [NHS MESH API](https://digital.nhs.uk/developer/api-catalogue/message-exchange-for-social-care-and-health-api#api-description__end-to-end-process-to-integrate-with-mesh-api) ## Table of Contents -- [Repository Template](#repository-template) +- [dotnet mesh client](dotnet-mesh-client) - [Table of Contents](#table-of-contents) - [Setup](#setup) - [Prerequisites](#prerequisites) - [Configuration](#configuration) - [Usage](#usage) + - [Mesh Operation Service](#mesh-operation-service) + - [Mesh Inbox Service](#mesh-inbox-service) + - [Mesh Outbox Service](#mesh-outbox-service) - [Testing](#testing) - - [Design](#design) - - [Diagrams](#diagrams) - - [Modularity](#modularity) - - [Contributing](#contributing) - - [Contacts](#contacts) + - [Licence](#licence) ## Setup -By including preferably a one-liner or if necessary a set of clear CLI instructions we improve user experience. This should be a frictionless installation process that works on various operating systems (macOS, Linux, Windows WSL) and handles all the dependencies. +### Prerequisites + +Currently this is not published to any nuget repository. + +To use this package within your dotnet solution we suggest using [git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules). + +to pull down the repository using git submodules the below command `git submodule add https://github.com/NHSDigital/dotnet-mesh-client` in the directory you wish to pull down the solution into. + +the `NHS.Mesh.Client.csproj` should then be added to the solution file. +then the client can be added as a project reference to any project which requires it. + +### Configuration + +The client needs to be registered as a service for Dependency Injection this can be done by using the below code: -Clone the repository +the parameters will need to be updated for your specific mailboxes and environments. +the certificate will need to be converted or created as a `.pfx` file that stores both the certificate and the private key. -```shell -git clone https://github.com/nhs-england-tools/repository-template.git -cd nhs-england-tools/repository-template +```c# + services.AddMeshClient(_ => _.MeshApiBaseUrl = 'MESHURL') + .AddMailbox("MYMAILBOX",new NHS.MESH.Client.Configuration.MailboxConfiguration + { + Password = "Password", + SharedKey = "SHAREDKEY", + Cert = new X509Certificate2("path to .pfx file","PFX File password") + }) + .Build(); ``` -### Prerequisites +Multiple mailboxes can be added by including more `.AddMailbox` methods to the builder and will be resolved when calling the various functions depending on the mailboxId passed to the function. + +## Usage -The following software packages, or their equivalents, are expected to be installed and configured: +To use all of the functions the needed service class will need to be injected in to the class which requires them as below +To use functions in the mesh Operation Service this needs to injected in the code as below: -- [Docker](https://www.docker.com/) container runtime or a compatible tool, e.g. [Podman](https://podman.io/), -- [asdf](https://asdf-vm.com/) version manager, -- [GNU make](https://www.gnu.org/software/make/) 3.82 or later, -- [GNU coreutils](https://www.gnu.org/software/coreutils/) and [GNU binutils](https://www.gnu.org/software/binutils/) may be required to build dependencies like Python, which may need to be compiled during installation. For macOS users, this has been scripted and automated by the `dotfiles` project; please see this [script](https://github.com/nhs-england-tools/dotfiles/blob/main/assets/20-install-base-packages.macos.sh) for details, -- [Python](https://www.python.org/) required to run Git hooks, -- [`jq`](https://jqlang.github.io/jq/) a lightweight and flexible command-line JSON processor. +```c# + public class ExampleService + { + private readonly IMeshOperationService _meshOperationService -> [!NOTE]
-> The version of GNU make available by default on macOS is earlier than 3.82. You will need to upgrade it or certain `make` tasks will fail. On macOS, you will need [Homebrew](https://brew.sh/) installed, then to install `make`, like so: -> -> ```shell -> brew install make -> ``` -> -> You will then see instructions to fix your `$PATH` variable to make the newly installed version available. If you are using [dotfiles](https://github.com/nhs-england-tools/dotfiles), this is all done for you. + public ExampleService(IMeshOperationService meshOperationService) + { + _meshOperationService = meshOperationService; + } -### Configuration + // methods in ExampleService that execute operation service methods. + } +``` + +The return type from these functions a `MeshResponse` where T is the successful response data type. +This response also contains an `IsSuccessful` flag which will indicate is the call to MESH returned a successful response. +If the response is unsuccessful the `Error` property will contain a `APIErrorResponse` object that will have further information. +Otherwise the response data will be within the `Response` property. + +### Mesh Operation Service -Installation and configuration of the toolchain dependencies +To use this inject `IMeshOperationService` class as shown in [Usage](#usage). -```shell -make config +#### Handshake / Validate a mailbox + +Use this endpoint to check that MESH can be reached and that the authentication you are using is correct. This endpoint only needs to be called once every 24 hours. This endpoint updates the details of the connection history held for your mailbox and is similar to a keep-alive or ping message, in that it allows monitoring on the Spine to be aware of the active use of a mailbox despite a lack of traffic. + +to implement call the + +```c# + var result = await _meshOperationService.MeshHandshakeAsync(mailboxId); ``` -## Usage +This will return the Mailbox Id. -After a successful installation, provide an informative example of how this project can be used. Additional code snippets, screenshots and demos work well in this space. You may also link to the other documentation resources, e.g. the [User Guide](./docs/user-guide.md) to demonstrate more use cases and to show more features. +### Mesh Inbox Service -### Testing +To use this inject `IMeshInboxService` class as shown in [Usage](#usage). +This class contains methods used for receiving messages from MESH. -There are `make` tasks for you to configure to run your tests. Run `make test` to see how they work. You should be able to use the same entry points for local development as in your CI pipeline. +#### Check an Inbox -## Design +Returns the message identifier of messages in the mailbox inbox ready for download. +to implement call the below: -### Diagrams +```c# + var result = await _meshInboxService.GetMessagesAsync(mailboxId); +``` + +this will return a list of MessageIds that are ready to download. -The [C4 model](https://c4model.com/) is a simple and intuitive way to create software architecture diagrams that are clear, consistent, scalable and most importantly collaborative. This should result in documenting all the system interfaces, external dependencies and integration points. +#### Get Message By Id -![Repository Template](./docs/diagrams/Repository_Template_GitHub_Generic.png) +Retrieves a message based on the message identifier obtained from the 'GetMessagesAsync' method. +Note this will not retrieve chunked messages. + +```c# + var result = await _meshInboxService.GetMessageByIdAsync(mailboxId, messageId); +``` -### Modularity +The response to this will return a `GetMeshResponse` Object which will contain a `FileAttachment` & `MessageMetaData` -Most of the projects are built with customisability and extendability in mind. At a minimum, this can be achieved by implementing service level configuration options and settings. The intention of this section is to show how this can be used. If the system processes data, you could mention here for example how the input is prepared for testing - anonymised, synthetic or live data. +#### Get Chunked Message By Id + +Retrieves a chunked message based on the message identifier obtained from the 'GetMessagesAsync' method. + +```c# + var result = await _meshInboxService.GetChunkedMessageByIdAsync(mailboxId, messageId); +``` -## Contributing +The response to this will return a `GetMeshResponse` Object which will contain a `List` & `MessageMetaData` -Describe or link templates on how to raise an issue, feature request or make a contribution to the codebase. Reference the other documentation files, like +Note: the list of File Attachments can be passed to the helper method ReassembleChunkedFile as below which will return a File Attachment. + +```c# + var assembledFile = await FileHelpers.ReassembleChunkedFile(getMessageResponse.Response.FileAttachments); +``` + +#### Get Head Message By Id + +This method will retrieve a message metadata based on the message_id obtained from the 'GetMessagesAsync' method. + +```c# + var result = await _meshInboxService.GetHeadMessageByIdAsync(mailboxId, messageId); +``` -- Environment setup for contribution, i.e. `CONTRIBUTING.md` -- Coding standards, branching, linting, practices for development and testing -- Release process, versioning, changelog -- Backlog, board, roadmap, ways of working -- High-level requirements, guiding principles, decision records, etc. +this response will return an object with a `MessageMetaData` property. -## Contacts +#### Acknowledge Message By Id -Provide a way to contact the owners of this project. It can be a team, an individual or information on the means of getting in touch via active communication channels, e.g. opening a GitHub discussion, raising an issue, etc. +This method will acknowledge the successful download of a message. + +```c# + var result = await _meshInboxService.AcknowledgeMessageByIdAsync(mailboxId, messageId); +``` + +this will return the Id of the message acknowledge. + +### Mesh Outbox Service + +To use this inject `IMeshOutboxService` class as shown in [Usage](#usage). +This class contains methods used for sending messages to MESH. + +All of the sending methods will expect the below list of parameters + +| **Parameter Name** | **Data Type** | **Required** | **Description** | +|--------------------|----------------|--------------|-------------------------------------------------------------------------------------------| +| fromMailboxId | string | Y | Sender mailbox Id | +| toMailboxId | string | Y | Recipient mailbox ID | +| workFlowId | string | Y | Identifies the type of message being sent e.g. Pathology, GP Capitation. | +| file | FileAttachment | Y | contains the details of the file to be sent | +| localId | string | N | local identifier, your reference | +| subject | string | N | additional message subject | +| includeChecksum | bool | N | By default this is false, if true a header will be added with an MD5 Checksum of the file | + + +### Testing + +There are `make` tasks for you to configure to run your tests. Run `make test` to see how they work. You should be able to use the same entry points for local development as in your CI pipeline. ## Licence diff --git a/Usage.md b/Usage.md new file mode 100644 index 0000000..e69de29 From d3f851ad906cc4b6009796d45bff3d35f735047d Mon Sep 17 00:00:00 2001 From: Michael Clayson Date: Tue, 1 Apr 2025 10:22:03 +0100 Subject: [PATCH 7/9] update readme with serverSideCertificate Collection --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8558b85..d75a723 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ The client needs to be registered as a service for Dependency Injection this can the parameters will need to be updated for your specific mailboxes and environments. the certificate will need to be converted or created as a `.pfx` file that stores both the certificate and the private key. +the serverSideCertificateCollection should be populated with the certificates of the MESH side server, This is to ensure the host connected to is the expected host. ```c# services.AddMeshClient(_ => _.MeshApiBaseUrl = 'MESHURL') @@ -46,7 +47,8 @@ the certificate will need to be converted or created as a `.pfx` file that store { Password = "Password", SharedKey = "SHAREDKEY", - Cert = new X509Certificate2("path to .pfx file","PFX File password") + Cert = new X509Certificate2("path to .pfx file","PFX File password"), + serverSideCertCollection = new X509Certificate2Collection() }) .Build(); ``` From 1103453eaf17a464e1883ac3c808ed25d3735c2e Mon Sep 17 00:00:00 2001 From: Michael Clayson Date: Tue, 1 Apr 2025 10:28:24 +0100 Subject: [PATCH 8/9] md formatting --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index d75a723..cad2948 100644 --- a/README.md +++ b/README.md @@ -175,7 +175,6 @@ All of the sending methods will expect the below list of parameters | subject | string | N | additional message subject | | includeChecksum | bool | N | By default this is false, if true a header will be added with an MD5 Checksum of the file | - ### Testing There are `make` tasks for you to configure to run your tests. Run `make test` to see how they work. You should be able to use the same entry points for local development as in your CI pipeline. From ccf18e3a712e9495c6700d5c3e12c773a4e58945 Mon Sep 17 00:00:00 2001 From: Michael Clayson Date: Tue, 1 Apr 2025 10:40:15 +0100 Subject: [PATCH 9/9] update check english words --- scripts/config/vale/styles/Vocab/words/accept.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/config/vale/styles/Vocab/words/accept.txt b/scripts/config/vale/styles/Vocab/words/accept.txt index eb9cd04..d559681 100644 --- a/scripts/config/vale/styles/Vocab/words/accept.txt +++ b/scripts/config/vale/styles/Vocab/words/accept.txt @@ -15,3 +15,8 @@ idempotence onboarding toolchain [A-Z]+s +dotnet +nuget +mailboxId +localId +bool