diff --git a/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs.UnitTests/InnerApi/WhenBuildingGetVacanciesRequest.cs b/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs.UnitTests/InnerApi/WhenBuildingGetVacanciesRequest.cs index c1d688ee87..ed23578538 100644 --- a/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs.UnitTests/InnerApi/WhenBuildingGetVacanciesRequest.cs +++ b/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs.UnitTests/InnerApi/WhenBuildingGetVacanciesRequest.cs @@ -21,11 +21,14 @@ public void Then_The_Request_Url_Is_Correctly_Built( List? levels, bool disabilityConfident) { - var actual = new GetVacanciesRequest(lat, lon, distance, whatSearchTerm, pageNumber, pageSize, categories, levels, sort, disabilityConfident); + var actual = new GetVacanciesRequest(lat, lon, distance, whatSearchTerm, pageNumber, pageSize, categories, levels, sort, disabilityConfident, new List + { + VacancyDataSource.Nhs + }); actual.GetUrl .Should() - .Be($"/api/vacancies?lat={lat}&lon={lon}&distanceInMiles={distance}&sort={sort}&pageNumber={pageNumber}&pageSize={pageSize}&categories={string.Join("&categories=", categories)}&levels={string.Join("&levels=", levels)}&searchTerm={whatSearchTerm}&disabilityConfident={disabilityConfident}"); + .Be($"/api/vacancies?lat={lat}&lon={lon}&distanceInMiles={distance}&sort={sort}&pageNumber={pageNumber}&pageSize={pageSize}&categories={string.Join("&categories=", categories)}&levels={string.Join("&levels=", levels)}&searchTerm={whatSearchTerm}&disabilityConfident={disabilityConfident}&additionalDataSources=Nhs&postedInLastNumberOfDays=7"); actual.Version.Should().Be("2.0"); } } diff --git a/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs.UnitTests/SavedSearches/WhenHandlingGetSavedSearchesQueryHandler.cs b/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs.UnitTests/SavedSearches/WhenHandlingGetSavedSearchesQueryHandler.cs index 7b26158e52..ec12072d4b 100644 --- a/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs.UnitTests/SavedSearches/WhenHandlingGetSavedSearchesQueryHandler.cs +++ b/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs.UnitTests/SavedSearches/WhenHandlingGetSavedSearchesQueryHandler.cs @@ -108,7 +108,8 @@ public async Task Then_The_Vacancies_Not_Matches_Saved_Searches_Returns_Empty( var actual = await sut.Handle(mockQuery, It.IsAny()); actual.Should().NotBeNull(); - actual.SavedSearchResults.Count.Should().Be(0); + actual.SavedSearchResults.Count.Should().Be(3); + actual.SavedSearchResults.TrueForAll(c => c.Vacancies.Count == 0).Should().BeTrue(); actual.SavedSearchResults.FirstOrDefault()?.User.Should().NotBeNull(); actual.SavedSearchResults.FirstOrDefault()?.User?.Should().BeEquivalentTo(getCandidateApiResponse, options => options.Excluding(ex => ex.Status)); actual.PageIndex.Should().Be(mockGetSavedSearchesApiResponse.PageIndex); @@ -251,7 +252,8 @@ public async Task When_SearchParameters_Lat_Long_Route_Categories_Null_Then_Gets categories.Select(cat => cat.Id.ToString()).ToList(), savedSearch.SearchParameters.SelectedLevelIds, mockQuery.ApprenticeshipSearchResultsSortOrder, - savedSearch.SearchParameters.DisabilityConfident); + savedSearch.SearchParameters.DisabilityConfident, + new List { VacancyDataSource.Nhs }); mockFindApprenticeshipApiClient.Setup(client => client.Get(It.Is(c => c.GetUrl == getVacanciesExpectedUrl.GetUrl))).ReturnsAsync(getVacanciesResponse); diff --git a/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs.UnitTests/SavedSearches/WhenHandlingPostSendSavedSearchNotificationCommand.cs b/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs.UnitTests/SavedSearches/WhenHandlingPostSendSavedSearchNotificationCommand.cs index 84c24ed473..63b8bc655b 100644 --- a/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs.UnitTests/SavedSearches/WhenHandlingPostSendSavedSearchNotificationCommand.cs +++ b/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs.UnitTests/SavedSearches/WhenHandlingPostSendSavedSearchNotificationCommand.cs @@ -13,48 +13,81 @@ using System.Net; using PatchSavedSearch = SFA.DAS.FindApprenticeshipJobs.Domain.Models.PatchSavedSearch; -namespace SFA.DAS.FindApprenticeshipJobs.UnitTests.SavedSearches +namespace SFA.DAS.FindApprenticeshipJobs.UnitTests.SavedSearches; + +[TestFixture] +public class WhenHandlingPostSendSavedSearchNotificationCommand { - [TestFixture] - public class WhenHandlingPostSendSavedSearchNotificationCommand + [Test] + [MoqAutoData] + public async Task Then_The_SavedSearch_Is_Patched_LastEmailSent_Updated_And_Email_Sent( + PostSendSavedSearchNotificationCommand command, + EmailEnvironmentHelper emailEnvironmentHelper, + [Frozen] Mock> findApprenticeshipApiClient, + [Frozen] Mock notificationService, + PostSendSavedSearchNotificationCommandHandler handler) { - [Test] - [MoqAutoData] - public async Task Then_The_SavedSearch_Is_Patched_LastEmailSent_Updated_And_Email_Sent( + var expectedPatchSavedSearchApiRequest = + new PatchSavedSearchApiRequest(command.Id, new JsonPatchDocument()); + + findApprenticeshipApiClient + .Setup(x => x.PatchWithResponseCode( + It.Is(c => + c.PatchUrl == expectedPatchSavedSearchApiRequest.PatchUrl + ))) + .ReturnsAsync(new ApiResponse(null!, HttpStatusCode.OK, string.Empty)); + + + await handler.Handle(command, CancellationToken.None); + + findApprenticeshipApiClient.Verify(x => x.PatchWithResponseCode(It.Is(c => + c.PatchUrl.Contains(command.Id.ToString(), StringComparison.CurrentCultureIgnoreCase) + && c.Data.Operations[1].path == "/LastRunDate" + && DateTime.Parse(c.Data.Operations[1].value.ToString()).Date.Equals(DateTime.UtcNow.Date) + && c.Data.Operations[0].path == "/EmailLastSendDate" + && DateTime.Parse(c.Data.Operations[0].value.ToString()).Date.Equals(DateTime.UtcNow.Date))), Times.Once); + + notificationService.Verify(x => x.Send( + It.Is(c => + c.RecipientsAddress == command.User.Email + && c.TemplateId == emailEnvironmentHelper.SavedSearchEmailNotificationTemplateId + && c.Tokens["firstName"] == command.User.FirstName + && c.Tokens["newApprenticeships"] == $"{command.Vacancies.Count.ToString()} new {(command.Vacancies.Count == 1 ? "apprenticeship" : "apprenticeships")}" + && c.Tokens["searchAlertDescriptor"] == $"{command.SearchTerm} in {command.Location}" + && !string.IsNullOrEmpty(c.Tokens["unsubscribeLink"]) + && !string.IsNullOrEmpty(c.Tokens["searchUrl"]) + && !string.IsNullOrEmpty(c.Tokens["searchParams"]) + ) + ), Times.Once); + } + + [Test] + [MoqAutoData] + public async Task Then_When_No_Vacancies_LastRun_Updated_And_No_Email_Sent( PostSendSavedSearchNotificationCommand command, EmailEnvironmentHelper emailEnvironmentHelper, [Frozen] Mock> findApprenticeshipApiClient, [Frozen] Mock notificationService, PostSendSavedSearchNotificationCommandHandler handler) - { - var expectedPatchSavedSearchApiRequest = - new PatchSavedSearchApiRequest(command.Id, new JsonPatchDocument()); - - findApprenticeshipApiClient - .Setup(x => x.PatchWithResponseCode( - It.Is(c => - c.PatchUrl == expectedPatchSavedSearchApiRequest.PatchUrl - ))) - .ReturnsAsync(new ApiResponse(null!, HttpStatusCode.OK, string.Empty)); - - - await handler.Handle(command, CancellationToken.None); - - findApprenticeshipApiClient.Verify(x => x.PatchWithResponseCode(It.Is(c => - c.PatchUrl.Contains(command.Id.ToString(), StringComparison.CurrentCultureIgnoreCase))), Times.Once); - - notificationService.Verify(x => x.Send( - It.Is(c => - c.RecipientsAddress == command.User.Email - && c.TemplateId == emailEnvironmentHelper.SavedSearchEmailNotificationTemplateId - && c.Tokens["firstName"] == command.User.FirstName - && c.Tokens["newApprenticeships"] == $"{command.Vacancies.Count.ToString()} new {(command.Vacancies.Count == 1 ? "apprenticeship" : "apprenticeships")}" - && c.Tokens["searchAlertDescriptor"] == $"{command.SearchTerm} in {command.Location}" - && !string.IsNullOrEmpty(c.Tokens["unsubscribeLink"]) - && !string.IsNullOrEmpty(c.Tokens["searchUrl"]) - && !string.IsNullOrEmpty(c.Tokens["searchParams"]) - ) - ), Times.Once); - } + { + command.Vacancies = []; + var expectedPatchSavedSearchApiRequest = + new PatchSavedSearchApiRequest(command.Id, new JsonPatchDocument()); + + findApprenticeshipApiClient + .Setup(x => x.PatchWithResponseCode( + It.Is(c => + c.PatchUrl == expectedPatchSavedSearchApiRequest.PatchUrl + ))) + .ReturnsAsync(new ApiResponse(null!, HttpStatusCode.OK, string.Empty)); + + await handler.Handle(command, CancellationToken.None); + + findApprenticeshipApiClient.Verify(x => x.PatchWithResponseCode(It.Is(c => + c.PatchUrl.Contains(command.Id.ToString(), StringComparison.CurrentCultureIgnoreCase) + && c.Data.Operations[0].path == "/LastRunDate" + && DateTime.Parse(c.Data.Operations[0].value.ToString()).Date.Equals(DateTime.UtcNow.Date))), Times.Once); + + notificationService.Verify(x => x.Send(It.IsAny()), Times.Never); } } \ No newline at end of file diff --git a/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs/Application/Commands/SavedSearch/SendNotification/PostSendSavedSearchNotificationCommandHandler.cs b/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs/Application/Commands/SavedSearch/SendNotification/PostSendSavedSearchNotificationCommandHandler.cs index 23198f8aed..d60cd8cb18 100644 --- a/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs/Application/Commands/SavedSearch/SendNotification/PostSendSavedSearchNotificationCommandHandler.cs +++ b/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs/Application/Commands/SavedSearch/SendNotification/PostSendSavedSearchNotificationCommandHandler.cs @@ -5,7 +5,6 @@ using SFA.DAS.FindApprenticeshipJobs.InnerApi.Requests; using SFA.DAS.Notifications.Messages.Commands; using SFA.DAS.SharedOuterApi.Configuration; -using SFA.DAS.SharedOuterApi.InnerApi.Responses; using SFA.DAS.SharedOuterApi.Interfaces; namespace SFA.DAS.FindApprenticeshipJobs.Application.Commands.SavedSearch.SendNotification @@ -18,11 +17,22 @@ public record PostSendSavedSearchNotificationCommandHandler( public async Task Handle(PostSendSavedSearchNotificationCommand command, CancellationToken cancellationToken) { var jsonPatchDocument = new JsonPatchDocument(); - jsonPatchDocument.Replace(x => x.EmailLastSendDate, DateTime.UtcNow); + if (command.Vacancies.Count != 0) + { + jsonPatchDocument.Replace(x => x.EmailLastSendDate, DateTime.UtcNow); + } + jsonPatchDocument.Replace(x => x.LastRunDate, DateTime.UtcNow); - + var patchRequest = new PatchSavedSearchApiRequest(command.Id, jsonPatchDocument); + if (command.Vacancies.Count == 0) + { + await FindApprenticeshipApiClient.PatchWithResponseCode(patchRequest); + return Unit.Value; + } + + var vacanciesEmailSnippet = EmailTemplateBuilder.GetSavedSearchVacanciesSnippet(EmailEnvironmentHelper, command.Vacancies, command.Location != null); @@ -58,7 +68,5 @@ await Task.WhenAll( return Unit.Value; } - - } } diff --git a/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs/Application/Queries/SavedSearch/GetSavedSearches/GetSavedSearchesQueryHandler.cs b/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs/Application/Queries/SavedSearch/GetSavedSearches/GetSavedSearchesQueryHandler.cs index 044c4b9c2a..3ffcd8bbc1 100644 --- a/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs/Application/Queries/SavedSearch/GetSavedSearches/GetSavedSearchesQueryHandler.cs +++ b/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs/Application/Queries/SavedSearch/GetSavedSearches/GetSavedSearchesQueryHandler.cs @@ -31,7 +31,14 @@ public async Task Handle(GetSavedSearchesQuery requ TotalCount = savedSearchResponse.TotalCount, SavedSearchResults = [] }; + + var routesTask = CourseService.GetRoutes(); + var levelsTask = CourseService.GetLevels(); + await Task.WhenAll(routesTask, levelsTask); + var routesList = routesTask.Result; + var levelsList = levelsTask.Result; + foreach (var savedSearch in savedSearchResponse.SavedSearches) { var candidate = @@ -40,14 +47,6 @@ await CandidateApiClient.Get( if (candidate == null || candidate.Status == UserStatus.Deleted) continue; - var routesTask = CourseService.GetRoutes(); - var levelsTask = CourseService.GetLevels(); - - await Task.WhenAll(routesTask, levelsTask); - - var routesList = routesTask.Result; - var levelsList = levelsTask.Result; - var categories = routesList.Routes .Where(route => savedSearch.SearchParameters.SelectedRouteIds != null @@ -79,9 +78,14 @@ await CandidateApiClient.Get( categories.Select(cat => cat.Id.ToString()).ToList(), savedSearch.SearchParameters.SelectedLevelIds, request.ApprenticeshipSearchResultsSortOrder, - savedSearch.SearchParameters.DisabilityConfident)); + savedSearch.SearchParameters.DisabilityConfident, + new List + { + VacancyDataSource.Nhs + })); - if (vacanciesResponse != null && vacanciesResponse.ApprenticeshipVacancies.Any()) + if (vacanciesResponse != null) + { searchResultList.Add(new GetSavedSearchesQueryResult.SearchResult { Id = savedSearch.Id, @@ -97,6 +101,7 @@ await CandidateApiClient.Get( (GetSavedSearchesQueryResult.SearchResult.ApprenticeshipVacancy) x) .ToList() }); + } } return new GetSavedSearchesQueryResult diff --git a/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs/InnerApi/Requests/GetVacanciesRequest.cs b/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs/InnerApi/Requests/GetVacanciesRequest.cs index 892619b8d2..ee76b04020 100644 --- a/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs/InnerApi/Requests/GetVacanciesRequest.cs +++ b/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs/InnerApi/Requests/GetVacanciesRequest.cs @@ -1,4 +1,5 @@ -using SFA.DAS.SharedOuterApi.Interfaces; +using System.Web; +using SFA.DAS.SharedOuterApi.Interfaces; using SFA.DAS.FindApprenticeshipJobs.Domain.Models; namespace SFA.DAS.FindApprenticeshipJobs.InnerApi.Requests @@ -15,6 +16,7 @@ public class GetVacanciesRequest : IGetApiRequest private readonly string _searchTerm; private readonly VacancySort _sort; private readonly bool _disabilityConfident; + private readonly string _additionalDataSources; public GetVacanciesRequest( double? lat, @@ -26,7 +28,8 @@ public GetVacanciesRequest( IReadOnlyCollection categories, IReadOnlyCollection? levels, VacancySort sort, - bool disabilityConfident) + bool disabilityConfident, + IReadOnlyCollection additionalDataSources) { _lat = lat; _lon = lon; @@ -38,10 +41,15 @@ public GetVacanciesRequest( _levels = levels is { Count: > 0 } ? string.Join("&levels=", levels) : string.Empty; _searchTerm = searchTerm; _disabilityConfident = disabilityConfident; - + _additionalDataSources = additionalDataSources is { Count: > 0 } ? string.Join("&additionalDataSources=", additionalDataSources) : string.Empty; } public string Version => "2.0"; - public string GetUrl => $"/api/vacancies?lat={_lat}&lon={_lon}&distanceInMiles={_distance}&sort={_sort}&pageNumber={_pageNumber}&pageSize={_pageSize}&categories={_categories}&levels={_levels}&searchTerm={_searchTerm}&disabilityConfident={_disabilityConfident}"; + public string GetUrl => $"/api/vacancies?lat={_lat}&lon={_lon}&distanceInMiles={_distance}&sort={_sort}&pageNumber={_pageNumber}&pageSize={_pageSize}&categories={_categories}&levels={_levels}&searchTerm={_searchTerm}&disabilityConfident={_disabilityConfident}&additionalDataSources={_additionalDataSources}&postedInLastNumberOfDays=7"; } +} +public enum VacancyDataSource +{ + Raa, + Nhs, } \ No newline at end of file diff --git a/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs/InnerApi/Responses/GetVacanciesResponse.cs b/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs/InnerApi/Responses/GetVacanciesResponse.cs index 14424b068f..15a54e9588 100644 --- a/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs/InnerApi/Responses/GetVacanciesResponse.cs +++ b/src/FindApprenticeshipJobs/SFA.DAS.FindApprenticeshipJobs/InnerApi/Responses/GetVacanciesResponse.cs @@ -1,15 +1,9 @@ -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace SFA.DAS.FindApprenticeshipJobs.InnerApi.Responses { public class GetVacanciesResponse { - [JsonPropertyName("total")] - public long Total { get; set; } - - [JsonPropertyName("totalFound")] - public long TotalFound { get; set; } - [JsonPropertyName("apprenticeshipVacancies")] public IEnumerable ApprenticeshipVacancies { get; set; } = []; } @@ -49,15 +43,12 @@ public class GetVacanciesListItem [JsonPropertyName("distance")] public decimal? Distance { get; set; } - [JsonPropertyName("score")] - public double Score { get; set; } - [JsonPropertyName("standardTitle")] public string CourseTitle { get; set; } [JsonPropertyName("standardLevel")] - public int CourseLevel { get; set; } - + public string CourseLevel { get; set; } + [JsonPropertyName("vacancySource")] public string VacancySource { get; set; } }