diff --git a/LearningHub.Nhs.MessagingService/Interfaces/IGovNotifyService.cs b/LearningHub.Nhs.MessagingService/Interfaces/IGovNotifyService.cs
new file mode 100644
index 000000000..23d0d3738
--- /dev/null
+++ b/LearningHub.Nhs.MessagingService/Interfaces/IGovNotifyService.cs
@@ -0,0 +1,29 @@
+namespace LearningHub.Nhs.MessagingService.Interfaces
+{
+ using System.Collections.Generic;
+ using System.Threading.Tasks;
+
+ ///
+ /// IMessageServices.
+ ///
+ public interface IGovNotifyService
+ {
+ ///
+ /// Send EmailAsync.
+ ///
+ /// email.
+ /// templateId.
+ /// personalisation.
+ /// The .
+ Task SendEmailAsync(string email, string templateId, Dictionary personalisation);
+
+ ///
+ /// Send SmsAsync.
+ ///
+ /// phoneNumber.
+ /// templateId.
+ /// personalisation.
+ /// The .
+ Task SendSmsAsync(string phoneNumber, string templateId, Dictionary personalisation);
+ }
+}
diff --git a/LearningHub.Nhs.MessagingService/LearningHub.Nhs.MessagingService.csproj b/LearningHub.Nhs.MessagingService/LearningHub.Nhs.MessagingService.csproj
new file mode 100644
index 000000000..d21593e42
--- /dev/null
+++ b/LearningHub.Nhs.MessagingService/LearningHub.Nhs.MessagingService.csproj
@@ -0,0 +1,16 @@
+
+
+
+ net8.0
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
diff --git a/LearningHub.Nhs.MessagingService/Model/MessagingServiceModel.cs b/LearningHub.Nhs.MessagingService/Model/MessagingServiceModel.cs
new file mode 100644
index 000000000..66a3f210e
--- /dev/null
+++ b/LearningHub.Nhs.MessagingService/Model/MessagingServiceModel.cs
@@ -0,0 +1,21 @@
+namespace LearningHub.Nhs.MessagingService.Model
+{
+ ///
+ /// MessagingServiceModel.
+ ///
+ public class MessagingServiceModel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MessagingServiceModel()
+ {
+ // Current = this;
+ }
+
+ ///
+ /// Gets or Sets ApiKey.
+ ///
+ public string GovNotifyApiKey { get; set; } = string.Empty;
+ }
+}
diff --git a/LearningHub.Nhs.MessagingService/Model/SendEmailRequest.cs b/LearningHub.Nhs.MessagingService/Model/SendEmailRequest.cs
new file mode 100644
index 000000000..da0044425
--- /dev/null
+++ b/LearningHub.Nhs.MessagingService/Model/SendEmailRequest.cs
@@ -0,0 +1,28 @@
+namespace LearningHub.Nhs.MessagingService.Model
+{
+ using System.Collections.Generic;
+ using System.ComponentModel.DataAnnotations;
+
+ ///
+ /// Defines the .
+ ///
+ public class SendEmailRequest
+ {
+ ///
+ /// Gets or Sets the Email.
+ ///
+ [Required]
+ public string Email { get; set; }
+
+ ///
+ /// Gets or Sets the TemplateId.
+ ///
+ [Required]
+ public string TemplateId { get; set; }
+
+ ///
+ /// Gets or Sets the Personalisation.
+ ///
+ public Dictionary Personalisation { get; set; }
+ }
+}
diff --git a/LearningHub.Nhs.MessagingService/Model/SendSmsRequest.cs b/LearningHub.Nhs.MessagingService/Model/SendSmsRequest.cs
new file mode 100644
index 000000000..cb2481cfe
--- /dev/null
+++ b/LearningHub.Nhs.MessagingService/Model/SendSmsRequest.cs
@@ -0,0 +1,28 @@
+namespace LearningHub.Nhs.MessagingService.Model
+{
+ using System.Collections.Generic;
+ using System.ComponentModel.DataAnnotations;
+
+ ///
+ /// Defines the .
+ ///
+ public class SendSmsRequest
+ {
+ ///
+ /// Gets or Sets the PhoneNumber.
+ ///
+ [Required]
+ public string PhoneNumber { get; set; }
+
+ ///
+ /// Gets or Sets the TemplateId.
+ ///
+ [Required]
+ public string TemplateId { get; set; }
+
+ ///
+ /// Gets or Sets the Personalisation.
+ ///
+ public Dictionary Personalisation { get; set; }
+ }
+}
diff --git a/LearningHub.Nhs.MessagingService/Services/GovNotifyService.cs b/LearningHub.Nhs.MessagingService/Services/GovNotifyService.cs
new file mode 100644
index 000000000..b81c8e7b2
--- /dev/null
+++ b/LearningHub.Nhs.MessagingService/Services/GovNotifyService.cs
@@ -0,0 +1,91 @@
+namespace LearningHub.Nhs.MessagingService.Services
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Text.Json;
+ using System.Threading.Tasks;
+ using LearningHub.Nhs.MessagingService.Interfaces;
+ using LearningHub.Nhs.MessagingService.Model;
+ using Microsoft.Extensions.Options;
+ using Notify.Client;
+
+ ///
+ /// GovNotify Service class.
+ ///
+ public class GovNotifyService : IGovNotifyService
+ {
+ private readonly NotificationClient client;
+
+ ///
+ /// Initializes a new instance of the class.
+ /// GovNotifyService.
+ ///
+ /// The Messaging Service Model.
+ public GovNotifyService(IOptions options)
+ {
+ this.client = new NotificationClient(options.Value.GovNotifyApiKey);
+ }
+
+ ///
+ /// Sends an email via the UK Gov.Notify service.
+ ///
+ /// The recipient's email address.
+ /// The ID of the Gov.Notify template to be used for the email.
+ ///
+ /// A dictionary containing key-value pairs for personalising the email template.
+ /// Keys should match the placeholders in the Gov.Notify template.
+ ///
+ ///
+ /// A unique message ID representing the queued email.
+ ///
+ public async Task SendEmailAsync(string email, string templateId, Dictionary personalisation)
+ {
+ var normalisedPersonlisation = new Dictionary();
+ foreach (var item in personalisation)
+ {
+ if (item.Value is JsonElement element)
+ {
+ normalisedPersonlisation[item.Key] = element.ToString();
+ }
+ else
+ {
+ normalisedPersonlisation[item.Key] = item.Value;
+ }
+ }
+
+ var response = await this.client.SendEmailAsync(email, templateId, normalisedPersonlisation);
+ return response.id;
+ }
+
+ ///
+ /// Sends an SMS via the UK Gov.Notify service.
+ ///
+ /// The recipient's phone number.
+ /// The ID of the Gov.Notify template to be used for the SMS.
+ ///
+ /// A dictionary containing key-value pairs for personalising the SMS template.
+ /// Keys should match the placeholders in the Gov.Notify template.
+ ///
+ ///
+ /// A unique message ID representing the queued SMS.
+ ///
+ public async Task SendSmsAsync(string phoneNumber, string templateId, Dictionary personalisation)
+ {
+ var normalisedPersonlisation = new Dictionary();
+ foreach (var item in personalisation)
+ {
+ if (item.Value is JsonElement element)
+ {
+ normalisedPersonlisation[item.Key] = element.ToString();
+ }
+ else
+ {
+ normalisedPersonlisation[item.Key] = item.Value;
+ }
+ }
+
+ var response = await this.client.SendSmsAsync(phoneNumber, templateId, normalisedPersonlisation);
+ return response.id;
+ }
+ }
+}
diff --git a/LearningHub.Nhs.MessagingService/Startup.cs b/LearningHub.Nhs.MessagingService/Startup.cs
new file mode 100644
index 000000000..522b98bda
--- /dev/null
+++ b/LearningHub.Nhs.MessagingService/Startup.cs
@@ -0,0 +1,25 @@
+namespace LearningHub.Nhs.MessagingService
+{
+ using LearningHub.Nhs.MessagingService.Interfaces;
+ using LearningHub.Nhs.MessagingService.Model;
+ using LearningHub.Nhs.MessagingService.Services;
+ using Microsoft.Extensions.Configuration;
+ using Microsoft.Extensions.DependencyInjection;
+
+ ///
+ /// Contains methods to configure the project to be called in an API startup class.
+ ///
+ public static class Startup
+ {
+ ///
+ /// Registers the implementations in the project with ASP.NET DI.
+ ///
+ /// The IServiceCollection.
+ /// The IConfiguration.
+ public static void AddMessagingServices(this IServiceCollection services, IConfiguration configuration)
+ {
+ services.Configure(configuration.GetSection("GovNotify"));
+ services.AddScoped();
+ }
+ }
+}
diff --git a/LearningHub.Nhs.WebUI.sln b/LearningHub.Nhs.WebUI.sln
index 5aea6885f..e4ca88071 100644
--- a/LearningHub.Nhs.WebUI.sln
+++ b/LearningHub.Nhs.WebUI.sln
@@ -81,6 +81,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LearningHub.Nhs.ReportApi.S
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LearningHub.Nhs.WebUI.AutomatedUiTests", "LearningHub.Nhs.WebUI.AutomatedUiTests\LearningHub.Nhs.WebUI.AutomatedUiTests.csproj", "{A84EC50B-2B01-4819-A2B1-BD867B7595CA}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MessagingService", "MessagingService", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LearningHub.Nhs.MessagingService", "LearningHub.Nhs.MessagingService\LearningHub.Nhs.MessagingService.csproj", "{713B0099-60E3-4D28-980F-448FC68BC7EE}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -345,6 +349,14 @@ Global
{A84EC50B-2B01-4819-A2B1-BD867B7595CA}.Release|Any CPU.Build.0 = Release|Any CPU
{A84EC50B-2B01-4819-A2B1-BD867B7595CA}.Release|x64.ActiveCfg = Release|Any CPU
{A84EC50B-2B01-4819-A2B1-BD867B7595CA}.Release|x64.Build.0 = Release|Any CPU
+ {713B0099-60E3-4D28-980F-448FC68BC7EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {713B0099-60E3-4D28-980F-448FC68BC7EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {713B0099-60E3-4D28-980F-448FC68BC7EE}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {713B0099-60E3-4D28-980F-448FC68BC7EE}.Debug|x64.Build.0 = Debug|Any CPU
+ {713B0099-60E3-4D28-980F-448FC68BC7EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {713B0099-60E3-4D28-980F-448FC68BC7EE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {713B0099-60E3-4D28-980F-448FC68BC7EE}.Release|x64.ActiveCfg = Release|Any CPU
+ {713B0099-60E3-4D28-980F-448FC68BC7EE}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -380,6 +392,7 @@ Global
{E585A74A-F358-4446-B10E-0FF07B4FF601} = {A4209011-1740-4902-B889-2F2FAF98383F}
{AAC8306E-1DEB-460D-84C7-40166D189B88} = {A4209011-1740-4902-B889-2F2FAF98383F}
{6167F037-166C-4C5A-81BE-55618E77D4E8} = {A4209011-1740-4902-B889-2F2FAF98383F}
+ {713B0099-60E3-4D28-980F-448FC68BC7EE} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1ECA38C8-7C69-4DE6-8293-852C603F4217}
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/GovNotifyMessagingController.cs b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/GovNotifyMessagingController.cs
new file mode 100644
index 000000000..05066a292
--- /dev/null
+++ b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/GovNotifyMessagingController.cs
@@ -0,0 +1,78 @@
+namespace LearningHub.NHS.OpenAPI.Controllers
+{
+ using System;
+ using System.Threading.Tasks;
+ using LearningHub.Nhs.MessagingService.Interfaces;
+ using LearningHub.Nhs.MessagingService.Model;
+ using Microsoft.AspNetCore.Authorization;
+ using Microsoft.AspNetCore.Mvc;
+
+ ///
+ /// GovNotify Messaging Controller.
+ ///
+ [Route("GovNotifyMessage")]
+ [Authorize]
+ public class GovNotifyMessagingController : OpenApiControllerBase
+ {
+ private readonly IGovNotifyService messageService;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The catalogue service.
+ public GovNotifyMessagingController(IGovNotifyService messageService)
+ {
+ this.messageService = messageService;
+ }
+
+ ///
+ /// Sends an email using UK Gov.Notify.
+ ///
+ /// personalisation.
+ /// The .
+ [Route("sendemail")]
+ [HttpPost]
+ public async Task SendEmailAsync([FromBody] SendEmailRequest request)
+ {
+ try
+ {
+ if (string.IsNullOrWhiteSpace(request.Email) || string.IsNullOrWhiteSpace(request.TemplateId))
+ {
+ return this.BadRequest("Email and template ID are required");
+ }
+
+ var response = await this.messageService.SendEmailAsync(request.Email, request.TemplateId, request.Personalisation);
+ return this.Ok(response);
+ }
+ catch (Exception ex)
+ {
+ return this.Ok(ex.Message);
+ }
+ }
+
+ ///
+ /// Sends a sms using UK Gov.Notify.
+ ///
+ /// SendSmsRequest.
+ /// The .
+ [Route("sendsms")]
+ [HttpPost]
+ public async Task SendSmsAsync([FromBody] SendSmsRequest request)
+ {
+ try
+ {
+ if (string.IsNullOrWhiteSpace(request.PhoneNumber) || string.IsNullOrWhiteSpace(request.TemplateId))
+ {
+ return this.BadRequest("phoneNumber and template ID are required");
+ }
+
+ var response = await this.messageService.SendSmsAsync(request.PhoneNumber, request.TemplateId, request.Personalisation);
+ return this.Ok(response);
+ }
+ catch (Exception ex)
+ {
+ return this.Ok(ex.Message);
+ }
+ }
+ }
+}
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/LearningHub.NHS.OpenAPI.csproj b/OpenAPI/LearningHub.Nhs.OpenApi/LearningHub.NHS.OpenAPI.csproj
index 83639e186..6ed354944 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi/LearningHub.NHS.OpenAPI.csproj
+++ b/OpenAPI/LearningHub.Nhs.OpenApi/LearningHub.NHS.OpenAPI.csproj
@@ -31,6 +31,7 @@
+
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/Startup.cs b/OpenAPI/LearningHub.Nhs.OpenApi/Startup.cs
index 627401b12..f3dc3f51b 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi/Startup.cs
+++ b/OpenAPI/LearningHub.Nhs.OpenApi/Startup.cs
@@ -30,6 +30,7 @@ namespace LearningHub.NHS.OpenAPI
using System.Configuration;
using System;
using LearningHub.Nhs.Models.Extensions;
+ using LearningHub.Nhs.MessagingService;
///
/// The Startup class.
@@ -84,6 +85,7 @@ public void ConfigureServices(IServiceCollection services)
services.AddApplicationInsightsTelemetry();
services.AddControllers(options => options.Filters.Add(new HttpResponseExceptionFilter()));
services.AddControllers(opt => { opt.Filters.Add(new AuthorizeFilter()); });
+ services.AddMessagingServices(this.Configuration);
services.AddSwaggerGen(
c =>
{
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/SwaggerDefinitions/v1.3.0.json b/OpenAPI/LearningHub.Nhs.OpenApi/SwaggerDefinitions/v1.3.0.json
index 0cde7cef1..9633b6117 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi/SwaggerDefinitions/v1.3.0.json
+++ b/OpenAPI/LearningHub.Nhs.OpenApi/SwaggerDefinitions/v1.3.0.json
@@ -6033,6 +6033,85 @@
}
}
}
+ },
+ "/GovNotifyMessage/sendsms": {
+ "post": {
+ "tags": [
+ "SendSms"
+ ],
+ "summary": "Send an SMS.",
+ "required": [ "mobileNumber", "templateId" ],
+ "requestBody": {
+ "description": "The Sends an SMS using the UK Gov.Notify service.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SendSmsRequest"
+ }
+ },
+ "text/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SendSmsRequest"
+ }
+ },
+ "application/*+json": {
+ "schema": {
+ "$ref": "#/components/schemas/SendSmsRequest"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "SMS successfully sent."
+ },
+ "400": {
+ "description": "Bad request, invalid input."
+ },
+ "500": {
+ "description": "Internal server error."
+ }
+ }
+ }
+ },
+ "/GovNotifyMessage/sendemail": {
+ "post": {
+ "tags": [
+ "SendEmail"
+ ],
+ "summary": "Send an Email.",
+ "requestBody": {
+ "description": "Sends an email using the UK Gov.Notify service.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SendEmailRequest"
+ }
+ },
+ "text/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SendEmailRequest"
+ }
+ },
+ "application/*+json": {
+ "schema": {
+ "$ref": "#/components/schemas/SendEmailRequest"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Email successfully sent."
+ },
+ "400": {
+ "description": "Bad request, invalid input."
+ },
+ "500": {
+ "description": "Internal server error."
+ }
+ }
+ }
}
},
"components": {
@@ -14138,6 +14217,51 @@
}
},
"additionalProperties": false
+ },
+ "SendSmsRequest": {
+ "type": "object",
+ "required": [ "mobileNumber", "templateId" ],
+ "properties": {
+ "mobileNumber": {
+ "type": "string",
+ "nullable": false,
+ "description": "The recipient's mobile number."
+ },
+ "templateId": {
+ "type": "string",
+ "nullable": false,
+ "description": "The ID of the template to be used for the email."
+ },
+ "personalisation": {
+ "type": "object",
+ "additionalProperties:": {
+ "type": "string"
+ },
+ "description": "Key-value pairs for personalising the sms template."
+ }
+ }
+ },
+ "SendEmailRequest": {
+ "type": "object",
+ "properties": {
+ "emailAddress": {
+ "type": "string",
+ "nullable": false,
+ "description": "The recipient's email address."
+ },
+ "templateId": {
+ "type": "string",
+ "nullable": false,
+ "description": "The ID of the template to be used for the email."
+ },
+ "personalisation": {
+ "type": "object",
+ "additionalProperties:": {
+ "type": "string"
+ },
+ "description": "Key-value pairs for personalising the email template."
+ }
+ }
}
},
"securitySchemes": {
diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/appsettings.json b/OpenAPI/LearningHub.Nhs.OpenApi/appsettings.json
index 92f23e650..56c507cff 100644
--- a/OpenAPI/LearningHub.Nhs.OpenApi/appsettings.json
+++ b/OpenAPI/LearningHub.Nhs.OpenApi/appsettings.json
@@ -84,5 +84,8 @@
"ResponseType": "code id_token",
"AuthSecret": "",
"AuthTimeout": 20
+ },
+ "GovNotify": {
+ "GovNotifyApiKey": ""
}
}