diff --git a/src/FindAnApprenticeship/SFA.DAS.FindAnApprenticeship.UnitTests/Application/Commands/WhenHandlingCreateCandidateCommand.cs b/src/FindAnApprenticeship/SFA.DAS.FindAnApprenticeship.UnitTests/Application/Commands/WhenHandlingCreateCandidateCommand.cs index 811804f853..324c9aa4dd 100644 --- a/src/FindAnApprenticeship/SFA.DAS.FindAnApprenticeship.UnitTests/Application/Commands/WhenHandlingCreateCandidateCommand.cs +++ b/src/FindAnApprenticeship/SFA.DAS.FindAnApprenticeship.UnitTests/Application/Commands/WhenHandlingCreateCandidateCommand.cs @@ -50,6 +50,85 @@ public async Task Then_If_Candidate_Already_Exists_Then_Details_Are_Returned( result.Status.Should().Be(candidate.Status); } } + + [Test, MoqAutoData] + public async Task Then_If_Candidate_Does_Not_Exist_By_Id_But_Exists_By_Email_With_No_GovIdentifier_Then_Details_Are_Returned_And_Identifier_Updated( + CreateCandidateCommand command, + string govUkId, + GetCandidateApiResponse candidate, + PostCandidateApiResponse response, + GetLegacyUserByEmailApiResponse legacyUserByEmailApiResponse, + [Frozen] Mock> mockApiClient, + [Frozen] Mock> mockLegacyApiClient, + CreateCandidateCommandHandler handler) + { + command.GovUkIdentifier = govUkId; + command.Email = candidate.Email; + candidate.GovUkIdentifier = null; + + var expectedGetCandidateRequest = new GetCandidateApiRequest(govUkId); + var expectedGetCandidateByEmailRequest = new GetCandidateByEmailApiRequest(command.Email); + mockApiClient.Setup(x => x.GetWithResponseCode( + It.Is(r => r.GetUrl == expectedGetCandidateRequest.GetUrl))) + .ReturnsAsync(new ApiResponse(null!, HttpStatusCode.NotFound, string.Empty)); + mockApiClient.Setup(x => x.GetWithResponseCode( + It.Is(r => r.GetUrl == expectedGetCandidateByEmailRequest.GetUrl))) + .ReturnsAsync(new ApiResponse(candidate, HttpStatusCode.OK, string.Empty)); + mockApiClient.Setup(x => x.PutWithResponseCode( + It.Is(r => + r.PutUrl.Contains(candidate.Id.ToString()) + && ((PutCandidateApiRequestData)r.Data).GovUkIdentifier == govUkId))).ReturnsAsync(new ApiResponse(null, HttpStatusCode.OK, string.Empty)); + + var result = await handler.Handle(command, CancellationToken.None); + + using (new AssertionScope()) + { + result.GovUkIdentifier.Should().BeEquivalentTo(govUkId); + result.Email.Should().BeEquivalentTo(candidate.Email); + result.FirstName.Should().BeEquivalentTo(candidate.FirstName); + result.LastName.Should().BeEquivalentTo(candidate.LastName); + result.Id.Should().Be(candidate.Id); + result.Status.Should().Be(candidate.Status); + } + mockApiClient.Verify(x => x.PutWithResponseCode( + It.Is(r => + r.PutUrl.Contains(candidate.Id.ToString()) + && ((PutCandidateApiRequestData)r.Data).GovUkIdentifier == govUkId)), Times.Once()); + } + + + [Test, MoqAutoData] + public async Task Then_If_Candidate_Does_Not_Exist_By_Id_But_Exists_By_Email_With_GovIdentifier_Null_Returned( + CreateCandidateCommand command, + string govUkId, + GetCandidateApiResponse candidate, + PostCandidateApiResponse response, + GetLegacyUserByEmailApiResponse legacyUserByEmailApiResponse, + [Frozen] Mock> mockApiClient, + [Frozen] Mock> mockLegacyApiClient, + CreateCandidateCommandHandler handler) + { + command.GovUkIdentifier = govUkId; + command.Email = candidate.Email; + candidate.GovUkIdentifier = govUkId; + + var expectedGetCandidateRequest = new GetCandidateApiRequest(govUkId); + var expectedGetCandidateByEmailRequest = new GetCandidateByEmailApiRequest(command.Email); + mockApiClient.Setup(x => x.GetWithResponseCode( + It.Is(r => r.GetUrl == expectedGetCandidateRequest.GetUrl))) + .ReturnsAsync(new ApiResponse(null!, HttpStatusCode.NotFound, string.Empty)); + mockApiClient.Setup(x => x.GetWithResponseCode( + It.Is(r => r.GetUrl == expectedGetCandidateByEmailRequest.GetUrl))) + .ReturnsAsync(new ApiResponse(candidate, HttpStatusCode.OK, string.Empty)); + mockApiClient.Setup(x => x.PutWithResponseCode( + It.Is(r => + r.PutUrl.Contains(candidate.Id.ToString()) + && ((PutCandidateApiRequestData)r.Data).GovUkIdentifier == govUkId))).ReturnsAsync(new ApiResponse(null, HttpStatusCode.OK, string.Empty)); + + var result = await handler.Handle(command, CancellationToken.None); + + result.IsEmailAddressMigrated.Should().BeTrue(); + } [Test, MoqAutoData] public async Task Then_If_Candidate_Already_Exists_And_Email_Is_Different_Then_Updated_And_Details_Are_Returned( @@ -106,11 +185,13 @@ public async Task Then_If_Candidate_Email_Address_Has_Already_Been_Migrated_Then mockApiClient.Setup(x => x.GetWithResponseCode( It.Is(r => r.GetUrl == expectedGetCandidateRequest.GetUrl))) .ReturnsAsync(new ApiResponse(null, HttpStatusCode.NotFound, string.Empty)); - var expectedGetMigratedCandidateRequest = new GetCandidateByMigratedEmailApiRequest(command.Email); mockApiClient.Setup(x => x.GetWithResponseCode( It.Is(r => r.GetUrl == expectedGetMigratedCandidateRequest.GetUrl))) .ReturnsAsync(new ApiResponse(migratedCandidate, HttpStatusCode.OK, string.Empty)); + mockApiClient.Setup(x => x.GetWithResponseCode( + It.IsAny())) + .ReturnsAsync(new ApiResponse(null, HttpStatusCode.NotFound, string.Empty)); var result = await handler.Handle(command, CancellationToken.None); @@ -146,6 +227,9 @@ public async Task Then_If_Legacy_User_Exists_Details_Are_Migrated_With_Address( mockApiClient.Setup(x => x.GetWithResponseCode( It.Is(r => r.GetUrl == expectedGetMigratedCandidateRequest.GetUrl))) .ReturnsAsync(new ApiResponse(null, HttpStatusCode.NotFound, string.Empty)); + mockApiClient.Setup(x => x.GetWithResponseCode( + It.IsAny())) + .ReturnsAsync(new ApiResponse(null, HttpStatusCode.NotFound, string.Empty)); var expectedRequest = new PostCandidateApiRequest(command.GovUkIdentifier, expectedPostData); @@ -245,6 +329,9 @@ public async Task Then_The_Post_Is_Sent_And_If_Legacy_Api_Has_DateOfBirth_As_Dat .Setup(client => client.Get( It.Is(r => r.GetUrl == legacyGetRequest.GetUrl))) .ReturnsAsync(legacyUserByEmailApiResponse); + mockApiClient.Setup(x => x.GetWithResponseCode( + It.IsAny())) + .ReturnsAsync(new ApiResponse(null, HttpStatusCode.NotFound, string.Empty)); var result = await handler.Handle(command, CancellationToken.None); @@ -277,6 +364,9 @@ public async Task And_Api_Returns_Null_Then_Return_Null( mockApiClient.Setup(x => x.GetWithResponseCode( It.Is(r => r.GetUrl == expectedGetCandidateRequest.GetUrl))) .ReturnsAsync(new ApiResponse(null, HttpStatusCode.NotFound, string.Empty)); + mockApiClient.Setup(x => x.GetWithResponseCode( + It.IsAny())) + .ReturnsAsync(new ApiResponse(null, HttpStatusCode.NotFound, string.Empty)); var expectedGetMigratedCandidateRequest = new GetCandidateByMigratedEmailApiRequest(command.Email); mockApiClient.Setup(x => x.GetWithResponseCode( @@ -335,6 +425,9 @@ public async Task Then_LegacyApi_Returns_Null_The_Post_Is_Sent_And_Data_Returned .Setup(client => client.Get( It.Is(r => r.GetUrl == legacyGetRequest.GetUrl))) .ReturnsAsync(() => null); + mockApiClient.Setup(x => x.GetWithResponseCode( + It.IsAny())) + .ReturnsAsync(new ApiResponse(null, HttpStatusCode.NotFound, string.Empty)); var result = await handler.Handle(command, CancellationToken.None); @@ -393,6 +486,9 @@ public async Task Then_Legacy_Applications_Are_Migrated( .Setup(client => client.Get( It.Is(r => r.GetUrl == legacyGetRequest.GetUrl))) .ReturnsAsync(legacyUserByEmailApiResponse); + mockApiClient.Setup(x => x.GetWithResponseCode( + It.IsAny())) + .ReturnsAsync(new ApiResponse(null, HttpStatusCode.NotFound, string.Empty)); await handler.Handle(command, CancellationToken.None); @@ -446,6 +542,9 @@ public async Task Then_Legacy_UserDetails_Found_Status_Return_As_InProgress( .Setup(client => client.Get( It.Is(r => r.GetUrl == legacyGetRequest.GetUrl))) .ReturnsAsync(legacyUserByEmailApiResponse); + mockApiClient.Setup(x => x.GetWithResponseCode( + It.IsAny())) + .ReturnsAsync(new ApiResponse(null, HttpStatusCode.NotFound, string.Empty)); var result = await handler.Handle(command, CancellationToken.None); result.Status.Should().Be(UserStatus.InProgress); diff --git a/src/FindAnApprenticeship/SFA.DAS.FindAnApprenticeship.UnitTests/InnerApi/CandidateApi/Requests/WhenBuildingGetCandidateByEmailApiRequest.cs b/src/FindAnApprenticeship/SFA.DAS.FindAnApprenticeship.UnitTests/InnerApi/CandidateApi/Requests/WhenBuildingGetCandidateByEmailApiRequest.cs new file mode 100644 index 0000000000..e18e468a3b --- /dev/null +++ b/src/FindAnApprenticeship/SFA.DAS.FindAnApprenticeship.UnitTests/InnerApi/CandidateApi/Requests/WhenBuildingGetCandidateByEmailApiRequest.cs @@ -0,0 +1,20 @@ +using System.Web; +using AutoFixture.NUnit3; +using FluentAssertions; +using NUnit.Framework; +using SFA.DAS.FindAnApprenticeship.InnerApi.CandidateApi.Requests; + +namespace SFA.DAS.FindAnApprenticeship.UnitTests.InnerApi.CandidateApi.Requests; + +public class WhenBuildingGetCandidateByEmailApiRequest +{ + [Test, AutoData] + public void Then_The_Request_Is_Built_Correctly(string emailValue) + { + var email = $"{emailValue}@£$^£@!@{emailValue}.com"; + + var actual = new GetCandidateByEmailApiRequest(email); + + actual.GetUrl.Should().Be($"api/candidates/email/{HttpUtility.UrlEncode(email)}"); + } +} \ No newline at end of file diff --git a/src/FindAnApprenticeship/SFA.DAS.FindAnApprenticeship/Application/Commands/Candidate/CreateCandidateCommandHandler.cs b/src/FindAnApprenticeship/SFA.DAS.FindAnApprenticeship/Application/Commands/Candidate/CreateCandidateCommandHandler.cs index ab77940acf..92e8a33830 100644 --- a/src/FindAnApprenticeship/SFA.DAS.FindAnApprenticeship/Application/Commands/Candidate/CreateCandidateCommandHandler.cs +++ b/src/FindAnApprenticeship/SFA.DAS.FindAnApprenticeship/Application/Commands/Candidate/CreateCandidateCommandHandler.cs @@ -52,6 +52,42 @@ await candidateApiClient.GetWithResponseCode( }; } + if (existingUser.StatusCode == HttpStatusCode.NotFound) + { + existingUser = + await candidateApiClient.GetWithResponseCode( + new GetCandidateByEmailApiRequest(request.Email)); + if (existingUser.StatusCode != HttpStatusCode.NotFound) + { + if (existingUser.Body.GovUkIdentifier != null) + { + return new CreateCandidateCommandResult + { + IsEmailAddressMigrated = true + }; + } + + var updateEmailRequest = new PutCandidateApiRequest(existingUser.Body.Id, new PutCandidateApiRequestData + { + GovUkIdentifier = request.GovUkIdentifier + }); + + await candidateApiClient.PutWithResponseCode(updateEmailRequest); + + return new CreateCandidateCommandResult + { + Id = existingUser.Body.Id, + GovUkIdentifier = request.GovUkIdentifier, + Email = request.Email, + FirstName = existingUser.Body.FirstName, + LastName = existingUser.Body.LastName, + PhoneNumber = existingUser.Body.PhoneNumber, + DateOfBirth = existingUser.Body.DateOfBirth, + Status = existingUser.Body.Status + }; + } + } + var userWithMigratedEmail = await candidateApiClient.GetWithResponseCode( new GetCandidateByMigratedEmailApiRequest(request.Email)); diff --git a/src/FindAnApprenticeship/SFA.DAS.FindAnApprenticeship/InnerApi/CandidateApi/Requests/GetCandidateByEmailApiRequest.cs b/src/FindAnApprenticeship/SFA.DAS.FindAnApprenticeship/InnerApi/CandidateApi/Requests/GetCandidateByEmailApiRequest.cs new file mode 100644 index 0000000000..a9f511438b --- /dev/null +++ b/src/FindAnApprenticeship/SFA.DAS.FindAnApprenticeship/InnerApi/CandidateApi/Requests/GetCandidateByEmailApiRequest.cs @@ -0,0 +1,9 @@ +using System.Web; +using SFA.DAS.SharedOuterApi.Interfaces; + +namespace SFA.DAS.FindAnApprenticeship.InnerApi.CandidateApi.Requests; + +public class GetCandidateByEmailApiRequest(string email) : IGetApiRequest +{ + public string GetUrl => $"api/candidates/email/{HttpUtility.UrlEncode(email)}"; +} \ No newline at end of file diff --git a/src/FindAnApprenticeship/SFA.DAS.FindAnApprenticeship/InnerApi/CandidateApi/Requests/PutCandidateApiRequest.cs b/src/FindAnApprenticeship/SFA.DAS.FindAnApprenticeship/InnerApi/CandidateApi/Requests/PutCandidateApiRequest.cs index 5afaf2d114..4f0051f74e 100644 --- a/src/FindAnApprenticeship/SFA.DAS.FindAnApprenticeship/InnerApi/CandidateApi/Requests/PutCandidateApiRequest.cs +++ b/src/FindAnApprenticeship/SFA.DAS.FindAnApprenticeship/InnerApi/CandidateApi/Requests/PutCandidateApiRequest.cs @@ -19,4 +19,5 @@ public class PutCandidateApiRequestData public UserStatus? Status { get; set; } public string MigratedEmail { get; set; } public Guid? MigratedCandidateId { get; set; } + public string? GovUkIdentifier { get; set; } } \ No newline at end of file