From 4ad277521b280354a7418577c654511c8d5b9209 Mon Sep 17 00:00:00 2001 From: Arunima George Date: Fri, 11 Jul 2025 09:56:08 +0100 Subject: [PATCH] TD-5790: Save Successful email transactions for One-off emails --- .../RegistrationServiceTests.cs | 24 +++++++++---------- .../UserServiceTests.cs | 6 +++++ .../ElfhUserService.cs | 10 ++++---- .../RegistrationService.cs | 5 ++-- .../Configuration/GovNotifyTemplates.cs | 18 ++++++++++++++ .../Configuration/Settings.cs | 5 ++++ LearningHub.Nhs.UserApi/appsettings.json | 6 ++++- 7 files changed, 55 insertions(+), 19 deletions(-) create mode 100644 LearningHub.Nhs.UserApi.Shared/Configuration/GovNotifyTemplates.cs diff --git a/LearningHub.Nhs.UserApi.Services.UnitTests/RegistrationServiceTests.cs b/LearningHub.Nhs.UserApi.Services.UnitTests/RegistrationServiceTests.cs index b1da4c2..36d8932 100644 --- a/LearningHub.Nhs.UserApi.Services.UnitTests/RegistrationServiceTests.cs +++ b/LearningHub.Nhs.UserApi.Services.UnitTests/RegistrationServiceTests.cs @@ -51,7 +51,7 @@ public async Task GetRegistrationStatus_ExistingEmail_BlueUser() var optionsMock = new Mock>(); - var registrationService = new RegistrationService(userRepositoryMock.Object, userGroupTypeInputValidationRepositoryMock.Object, null, null, null, null, null, null, null, null, null, null, null, null, optionsMock.Object, null, null, null, null, null, null, this.countryLookupRepoMock.Object); + var registrationService = new RegistrationService(userRepositoryMock.Object, userGroupTypeInputValidationRepositoryMock.Object, null, null, null, null, null, null, null, null, null, null, null, null, optionsMock.Object, null, null, null, null, null, null, this.countryLookupRepoMock.Object, null); var status = await registrationService.GetRegistrationStatus("test@here.com", this.ipAddress); Assert.IsType(status); @@ -79,7 +79,7 @@ public async Task GetRegistrationStatus_ExistingEmail_NonBlueUser() var optionsMock = new Mock>(); - var registrationService = new RegistrationService(userRepositoryMock.Object, userGroupTypeInputValidationRepositoryMock.Object, null, null, null, null, null, null, null, null, null, null, null, null, optionsMock.Object, null, null, userGroupRepositoryMock.Object, null, null, null, this.countryLookupRepoMock.Object); + var registrationService = new RegistrationService(userRepositoryMock.Object, userGroupTypeInputValidationRepositoryMock.Object, null, null, null, null, null, null, null, null, null, null, null, null, optionsMock.Object, null, null, userGroupRepositoryMock.Object, null, null, null, this.countryLookupRepoMock.Object, null); var status = await registrationService.GetRegistrationStatus("test@here.com", this.ipAddress); Assert.IsType(status); @@ -103,7 +103,7 @@ public async Task GetRegistrationStatus_NonExistingEmail_BlueUser() var optionsMock = new Mock>(); - var registrationService = new RegistrationService(userRepositoryMock.Object, userGroupTypeInputValidationRepositoryMock.Object, null, null, null, null, null, null, null, null, null, null, null, null, optionsMock.Object, null, null, null, null, null, null, this.countryLookupRepoMock.Object); + var registrationService = new RegistrationService(userRepositoryMock.Object, userGroupTypeInputValidationRepositoryMock.Object, null, null, null, null, null, null, null, null, null, null, null, null, optionsMock.Object, null, null, null, null, null, null, this.countryLookupRepoMock.Object, null); var status = await registrationService.GetRegistrationStatus("test@here.com", this.ipAddress); Assert.IsType(status); @@ -127,7 +127,7 @@ public async Task GetRegistrationStatus_NonExistingEmail_NonBlueUser() var optionsMock = new Mock>(); - var registrationService = new RegistrationService(userRepositoryMock.Object, userGroupTypeInputValidationRepositoryMock.Object, null, null, null, null, null, null, null, null, null, null, null, null, optionsMock.Object, null, null, null, null, null, null, this.countryLookupRepoMock.Object); + var registrationService = new RegistrationService(userRepositoryMock.Object, userGroupTypeInputValidationRepositoryMock.Object, null, null, null, null, null, null, null, null, null, null, null, null, optionsMock.Object, null, null, null, null, null, null, this.countryLookupRepoMock.Object, null); var status = await registrationService.GetRegistrationStatus("test@here.com", this.ipAddress); Assert.IsType(status); @@ -145,7 +145,7 @@ public async Task RegisterUser_Invalid_NoEmailOrNames() { var optionsMock = new Mock>(); - var registrationService = new RegistrationService(null, null, null, null, null, null, null, null, null, null, null, null, null, null, optionsMock.Object, null, null, null, null, null, null, this.countryLookupRepoMock.Object); + var registrationService = new RegistrationService(null, null, null, null, null, null, null, null, null, null, null, null, null, null, optionsMock.Object, null, null, null, null, null, null, this.countryLookupRepoMock.Object, null); var validationResult = await registrationService.RegisterUser( new RegistrationRequestViewModel() { @@ -177,7 +177,7 @@ public async Task RegisterUser_MissingRegionForEngland() { var optionsMock = new Mock>(); - var registrationService = new RegistrationService(null, null, null, null, null, null, null, null, null, null, null, null, null, null, optionsMock.Object, null, null, null, null, null, null, this.countryLookupRepoMock.Object); + var registrationService = new RegistrationService(null, null, null, null, null, null, null, null, null, null, null, null, null, null, optionsMock.Object, null, null, null, null, null, null, this.countryLookupRepoMock.Object, null); var validationResult = await registrationService.RegisterUser( new RegistrationRequestViewModel() { @@ -207,7 +207,7 @@ public async Task RegisterUser_MissingCountryJobRoleSpecialtyAndLocation() { var optionsMock = new Mock>(); - var registrationService = new RegistrationService(null, null, null, null, null, null, null, null, null, null, null, null, null, null, optionsMock.Object, null, null, null, null, null, null, this.countryLookupRepoMock.Object); + var registrationService = new RegistrationService(null, null, null, null, null, null, null, null, null, null, null, null, null, null, optionsMock.Object, null, null, null, null, null, null, this.countryLookupRepoMock.Object, null); var validationResult = await registrationService.RegisterUser( new RegistrationRequestViewModel() { @@ -252,7 +252,7 @@ public async Task RegisterUser_GMCFailure() var optionsMock = new Mock>(); - var registrationService = new RegistrationService(null, null, jobRoleRepositoryMock.Object, null, null, null, null, null, null, null, null, medicalcouncilServiceMock.Object, null, null, optionsMock.Object, null, null, null, null, null, null, this.countryLookupRepoMock.Object); + var registrationService = new RegistrationService(null, null, jobRoleRepositoryMock.Object, null, null, null, null, null, null, null, null, medicalcouncilServiceMock.Object, null, null, optionsMock.Object, null, null, null, null, null, null, this.countryLookupRepoMock.Object, null); var validationResult = await registrationService.RegisterUser( new RegistrationRequestViewModel() { @@ -294,7 +294,7 @@ public async Task RegisterUser_GDCFailure() var optionsMock = new Mock>(); - var registrationService = new RegistrationService(null, null, jobRoleRepositoryMock.Object, null, null, null, null, null, null, null, null, medicalcouncilServiceMock.Object, null, null, optionsMock.Object, null, null, null, null, null, null, this.countryLookupRepoMock.Object); + var registrationService = new RegistrationService(null, null, jobRoleRepositoryMock.Object, null, null, null, null, null, null, null, null, medicalcouncilServiceMock.Object, null, null, optionsMock.Object, null, null, null, null, null, null, this.countryLookupRepoMock.Object, null); var validationResult = await registrationService.RegisterUser( new RegistrationRequestViewModel() { @@ -336,7 +336,7 @@ public async Task RegisterUser_NMCFailure() var optionsMock = new Mock>(); - var registrationService = new RegistrationService(null, null, jobRoleRepositoryMock.Object, null, null, null, null, null, null, null, null, medicalcouncilServiceMock.Object, null, null, optionsMock.Object, null, null, null, null, null, null, this.countryLookupRepoMock.Object); + var registrationService = new RegistrationService(null, null, jobRoleRepositoryMock.Object, null, null, null, null, null, null, null, null, medicalcouncilServiceMock.Object, null, null, optionsMock.Object, null, null, null, null, null, null, this.countryLookupRepoMock.Object, null); var validationResult = await registrationService.RegisterUser( new RegistrationRequestViewModel() { @@ -381,7 +381,7 @@ public async Task RegisterUser_UserAlreadyExists() var optionsMock = new Mock>(); - var registrationService = new RegistrationService(null, null, jobRoleRepositoryMock.Object, null, null, null, null, null, null, null, null, null, userServiceMock.Object, null, optionsMock.Object, null, medicalCouncilRepositoryMock.Object, null, null, null, null, this.countryLookupRepoMock.Object); + var registrationService = new RegistrationService(null, null, jobRoleRepositoryMock.Object, null, null, null, null, null, null, null, null, null, userServiceMock.Object, null, optionsMock.Object, null, medicalCouncilRepositoryMock.Object, null, null, null, null, this.countryLookupRepoMock.Object, null); var validationResult = await registrationService.RegisterUser( new RegistrationRequestViewModel() { @@ -456,7 +456,7 @@ public async Task RegisterUser_Success() var emailLogRepositoryMock = new Mock(); emailLogRepositoryMock.Setup(r => r.CreateAsync(It.IsAny(), It.IsAny())); - var registrationService = new RegistrationService(userRepositoryMock.Object, userGroupTypeInputValidationRepositoryMock.Object, jobRoleRepositoryMock.Object, userUserGroupRepositoryMock.Object, userEmploymentRepositoryMock.Object, emailTemplateRepositoryMock.Object, emailLogRepositoryMock.Object, systemSettingsRepositoryMock.Object, userPasswordValidationTokenRepositoryMock.Object, tenantRepositoryMock.Object, tenantSmtpRepositoryMock.Object, null, userServiceMock.Object, userHistoryServiceMock.Object, this.GetSettings(), loginWizardServiceMock.Object, null, null, null, null, null, this.countryLookupRepoMock.Object); + var registrationService = new RegistrationService(userRepositoryMock.Object, userGroupTypeInputValidationRepositoryMock.Object, jobRoleRepositoryMock.Object, userUserGroupRepositoryMock.Object, userEmploymentRepositoryMock.Object, emailTemplateRepositoryMock.Object, emailLogRepositoryMock.Object, systemSettingsRepositoryMock.Object, userPasswordValidationTokenRepositoryMock.Object, tenantRepositoryMock.Object, tenantSmtpRepositoryMock.Object, null, userServiceMock.Object, userHistoryServiceMock.Object, this.GetSettings(), loginWizardServiceMock.Object, null, null, null, null, null, this.countryLookupRepoMock.Object, null); var validationResult = await registrationService.RegisterUser( new RegistrationRequestViewModel() { diff --git a/LearningHub.Nhs.UserApi.Services.UnitTests/UserServiceTests.cs b/LearningHub.Nhs.UserApi.Services.UnitTests/UserServiceTests.cs index 7e85e6d..3f0cb64 100644 --- a/LearningHub.Nhs.UserApi.Services.UnitTests/UserServiceTests.cs +++ b/LearningHub.Nhs.UserApi.Services.UnitTests/UserServiceTests.cs @@ -543,6 +543,7 @@ public async Task GetUserRole_Administrator() null, null, null, + null, null); var role = await userService.GetUserRoleAsync(1); @@ -591,6 +592,7 @@ public async Task GetUserRole_BlueUser() null, null, null, + null, null); var role = await userService.GetUserRoleAsync(1); @@ -836,6 +838,7 @@ public async Task GetUserRole_none() null, null, null, + null, null); var role = await userService.GetUserRoleAsync(1); @@ -885,6 +888,7 @@ public async Task UpdateUserSecurityQuestions_Create() null, null, this.NewMapper(), + null, null); await userService.UpdateUserSecurityQuestions(userSecurityQuestions, userId); @@ -936,6 +940,7 @@ public async Task UpdateUserSecurityQuestions_CreateAndUpdate() null, null, this.NewMapper(), + null, null); await userService.UpdateUserSecurityQuestions(userSecurityQuestions, userId); @@ -977,6 +982,7 @@ public async Task InvalidateElfhUserCache_InvalidateSuccess() this.elfhCacheSettingOptions, elfhCacheMock.Object, this.NewMapper(), + null, null); await userService.InvalidateElfhUserCacheAsync(123456, "test.user", CancellationToken.None); diff --git a/LearningHub.Nhs.UserApi.Services/ElfhUserService.cs b/LearningHub.Nhs.UserApi.Services/ElfhUserService.cs index 1bb6254..0de671e 100644 --- a/LearningHub.Nhs.UserApi.Services/ElfhUserService.cs +++ b/LearningHub.Nhs.UserApi.Services/ElfhUserService.cs @@ -673,13 +673,14 @@ public async Task SendAdminPasswordResetEmail(int u await this.userPasswordValidationTokenRepository.CreateAsync(userId, userPasswordValidationToken); var personalisation = new Dictionary(); - personalisation["name"] = (user.FirstName + " " + user.LastName).ToTitleCase(); + personalisation["name"] = user.FirstName; personalisation["username"] = user.UserName; + personalisation["new password"] = userPasswordValidationToken.ValidateUrl; var emailRequest = new EmailRequest { Recipient = user.EmailAddress, - TemplateId = "a6574e74-8769-417b-90ef-c4a4a1be5250", + TemplateId = this.settings.Value.GovNotifyTemplates.ForgottenUsernameOrPassword, Personalisation = personalisation, }; @@ -743,13 +744,14 @@ public async Task SendForgotPasswordEmail(string emailAddress) await this.userPasswordValidationTokenRepository.CreateAsync(user.Id, userPasswordValidationToken); var personalisation = new Dictionary(); - personalisation["name"] = (user.FirstName + " " + user.LastName).ToTitleCase(); + personalisation["name"] = user.FirstName; personalisation["username"] = user.UserName; + personalisation["new password"] = userPasswordValidationToken.ValidateUrl; var emailRequest = new EmailRequest { Recipient = user.EmailAddress, - TemplateId = "a6574e74-8769-417b-90ef-c4a4a1be5250", + TemplateId = this.settings.Value.GovNotifyTemplates.ForgottenUsernameOrPassword, Personalisation = personalisation, }; diff --git a/LearningHub.Nhs.UserApi.Services/RegistrationService.cs b/LearningHub.Nhs.UserApi.Services/RegistrationService.cs index c0bbeeb..bb1084f 100644 --- a/LearningHub.Nhs.UserApi.Services/RegistrationService.cs +++ b/LearningHub.Nhs.UserApi.Services/RegistrationService.cs @@ -354,13 +354,14 @@ public async Task RegisterUser(RegistrationRequestV await this.userPasswordValidationTokenRepository.CreateAsync(userId, userPasswordValidationToken); var personalisation = new Dictionary(); - personalisation["name"] = (newUser.FirstName + " " + newUser.LastName).ToTitleCase(); + personalisation["name"] = newUser.FirstName; personalisation["username"] = newUser.UserName; + personalisation["password"] = userPasswordValidationToken.ValidateUrl; var emailRequest = new EmailRequest { Recipient = registrationRequest.EmailAddress, - TemplateId = "fc3752d7-4c04-4d40-87fd-ad1264072ae9", + TemplateId = this.settings.GovNotifyTemplates.NHSLearningHubRegistration, Personalisation = personalisation, }; diff --git a/LearningHub.Nhs.UserApi.Shared/Configuration/GovNotifyTemplates.cs b/LearningHub.Nhs.UserApi.Shared/Configuration/GovNotifyTemplates.cs new file mode 100644 index 0000000..9166666 --- /dev/null +++ b/LearningHub.Nhs.UserApi.Shared/Configuration/GovNotifyTemplates.cs @@ -0,0 +1,18 @@ +namespace LearningHub.Nhs.UserApi.Shared.Configuration +{ + /// + /// GovNotifyTemplates. + /// + public class GovNotifyTemplates + { + /// + /// Gets or sets the ForgottenUsernameOrPassword ID. + /// + public string ForgottenUsernameOrPassword { get; set; } + + /// + /// Gets or sets the NHSLearningHubRegistration ID. + /// + public string NHSLearningHubRegistration { get; set; } + } +} diff --git a/LearningHub.Nhs.UserApi.Shared/Configuration/Settings.cs b/LearningHub.Nhs.UserApi.Shared/Configuration/Settings.cs index b2a9c1b..ca4bf53 100644 --- a/LearningHub.Nhs.UserApi.Shared/Configuration/Settings.cs +++ b/LearningHub.Nhs.UserApi.Shared/Configuration/Settings.cs @@ -49,5 +49,10 @@ public class Settings /// Gets or sets the security questions required. /// public int SecurityQuestionsRequired { get; set; } + + /// + /// Gets or sets the GovNotifyTemplates. + /// + public GovNotifyTemplates GovNotifyTemplates { get; set; } } } diff --git a/LearningHub.Nhs.UserApi/appsettings.json b/LearningHub.Nhs.UserApi/appsettings.json index 802d41e..f7fce96 100644 --- a/LearningHub.Nhs.UserApi/appsettings.json +++ b/LearningHub.Nhs.UserApi/appsettings.json @@ -36,7 +36,11 @@ "ElfhUserLoadByUserIdKey": "", "ElfhUserLoadByUserNameKey": "" }, - "SecurityQuestionsRequired": 2 + "SecurityQuestionsRequired": 2, + "GovNotifyTemplates": { + "ForgottenUsernameOrPassword": "", + "NHSLearningHubRegistration": "" + } }, "OpenApiConfig": { "OpenApiUrl": "",