private async Task StartSearch(Guid searchId, AvailabilityRequest request, AccommodationBookingSettings searchSettings, Dictionary <Suppliers, List <SupplierCodeMapping> > accommodationCodes, AgentContext agent, string languageCode) { foreach (var supplier in searchSettings.EnabledConnectors) { if (!accommodationCodes.TryGetValue(supplier, out var supplierCodeMappings)) { await _stateStorage.SaveState(searchId, SupplierAvailabilitySearchState.Completed(searchId, new List <string>(0), 0), supplier); continue; } // Starting search tasks in a separate thread StartSearchTask(supplier, supplierCodeMappings); } void StartSearchTask(Suppliers supplier, List <SupplierCodeMapping> supplierCodeMappings) { Task.Run(async() => { using var scope = _serviceScopeFactory.CreateScope(); await WideAvailabilitySearchTask .Create(scope.ServiceProvider) .Start(searchId, request, supplierCodeMappings, supplier, agent, languageCode, searchSettings); }); } }
public void Provider_search_results_count_should_sum() { var searchId = new Guid("c273b8eb-5351-424a-a10b-910ed755f6d5"); var supplierStates = new List <(Suppliers, SupplierAvailabilitySearchState)> { (Suppliers.Etg, SupplierAvailabilitySearchState.Completed(searchId, new List <string>(), 10)), (Suppliers.Netstorming, SupplierAvailabilitySearchState.Completed(searchId, new List <string>(), 15)), (Suppliers.Illusions, SupplierAvailabilitySearchState.Completed(searchId, new List <string>(), 144)) }; var wideAvailabilitySearchState = WideAvailabilitySearchState.FromSupplierStates(searchId, supplierStates); Assert.Equal(169, wideAvailabilitySearchState.ResultCount); }
public void Search_state_should_contain_all_errors() { var searchId = new Guid("ae05b78f-4488-4845-9f7d-bad3d4cd177e"); var errors = new[] { "Failed to connect", "Failed to fetch", "Server error" }; var supplierStates = new List <(Suppliers, SupplierAvailabilitySearchState)> { (Suppliers.Etg, SupplierAvailabilitySearchState.Failed(searchId, errors[0])), (Suppliers.Netstorming, SupplierAvailabilitySearchState.Failed(searchId, errors[1])), (Suppliers.Illusions, SupplierAvailabilitySearchState.Failed(searchId, errors[2])) }; var wideAvailabilitySearchState = WideAvailabilitySearchState.FromSupplierStates(searchId, supplierStates); foreach (var error in errors) { Assert.Contains(error, wideAvailabilitySearchState.Error); } }
public void Provider_search_results_with_duplicates_should_sum_unique_results_count() { var searchId = new Guid("c273b8eb-5351-424a-a10b-910ed755f6d5"); var supplierStates = new List <(Suppliers, SupplierAvailabilitySearchState)> { (Suppliers.Etg, SupplierAvailabilitySearchState.Completed(searchId, new List <string?> { "1", "2", "6", null }, 10)), (Suppliers.Netstorming, SupplierAvailabilitySearchState.Completed(searchId, new List <string?> { "1", "3", null }, 15)), (Suppliers.Illusions, SupplierAvailabilitySearchState.Completed(searchId, new List <string?> { "1", "4", "6" }, 144)) }; var wideAvailabilitySearchState = WideAvailabilitySearchState.FromSupplierStates(searchId, supplierStates); Assert.Equal(166, wideAvailabilitySearchState.ResultCount); }
public Task SaveState(Guid searchId, SupplierAvailabilitySearchState state, Suppliers supplier) => _multiProviderAvailabilityStorage.Save(searchId.ToString(), state, supplier);
public async Task Start(Guid searchId, AvailabilityRequest request, Suppliers supplier, AgentContext agent, string languageCode, AccommodationBookingSettings searchSettings) { var supplierConnector = _supplierConnectorManager.Get(supplier); try { _logger.LogProviderAvailabilitySearchStarted($"Availability search with id '{searchId}' on supplier '{supplier}' started"); await GetAvailability(request, languageCode) .Bind(ConvertCurrencies) .Map(ApplyMarkups) .Map(Convert) .Tap(SaveResult) .Finally(SaveState); } catch (Exception ex) { // TODO: Add sentry error notification _logger.LogProviderAvailabilitySearchException(ex); var result = ProblemDetailsBuilder.Fail <List <AccommodationAvailabilityResult> >("Server error", HttpStatusCode.InternalServerError); await SaveState(result); } async Task <Result <EdoContracts.Accommodations.Availability, ProblemDetails> > GetAvailability(AvailabilityRequest request, string languageCode) { var saveToStorageTask = _storage.SaveState(searchId, SupplierAvailabilitySearchState.Pending(searchId), supplier); var getAvailabilityTask = supplierConnector.GetAvailability(request, languageCode); await Task.WhenAll(saveToStorageTask, getAvailabilityTask); return(getAvailabilityTask.Result); } async Task <Result <EdoContracts.Accommodations.Availability, ProblemDetails> > ConvertCurrencies(EdoContracts.Accommodations.Availability availabilityDetails) { var convertedResults = new List <SlimAccommodationAvailability>(availabilityDetails.Results.Count); foreach (var slimAccommodationAvailability in availabilityDetails.Results) { // Currency can differ in different results var(_, isFailure, convertedAccommodationAvailability, error) = await _priceProcessor.ConvertCurrencies(agent, slimAccommodationAvailability, AvailabilityResultsExtensions.ProcessPrices, AvailabilityResultsExtensions.GetCurrency); if (isFailure) { return(Result.Failure <EdoContracts.Accommodations.Availability, ProblemDetails>(error)); } convertedResults.Add(convertedAccommodationAvailability); } return(new EdoContracts.Accommodations.Availability(availabilityDetails.AvailabilityId, availabilityDetails.NumberOfNights, availabilityDetails.CheckInDate, availabilityDetails.CheckOutDate, convertedResults, availabilityDetails.NumberOfProcessedAccommodations)); } async Task <EdoContracts.Accommodations.Availability> ApplyMarkups(EdoContracts.Accommodations.Availability response) { var markup = await _priceProcessor.ApplyMarkups(agent, response, AvailabilityResultsExtensions.ProcessPrices); return(markup.Data); } async Task <List <AccommodationAvailabilityResult> > Convert(EdoContracts.Accommodations.Availability details) { var supplierAccommodationIds = details.Results .Select(r => new SupplierAccommodationId(supplier, r.Accommodation.Id)) .Distinct() .ToList(); var duplicates = await _duplicatesService.GetDuplicateReports(supplierAccommodationIds); var timestamp = _dateTimeProvider.UtcNow().Ticks; return(details .Results .Select(accommodationAvailability => { var minPrice = accommodationAvailability.RoomContractSets.Min(r => r.Rate.FinalPrice.Amount); var maxPrice = accommodationAvailability.RoomContractSets.Max(r => r.Rate.FinalPrice.Amount); var accommodationId = new SupplierAccommodationId(supplier, accommodationAvailability.Accommodation.Id); var resultId = Guid.NewGuid(); var duplicateReportId = duplicates.TryGetValue(accommodationId, out var reportId) ? reportId : string.Empty; var roomContractSets = accommodationAvailability.RoomContractSets .ToRoomContractSetList() .ApplySearchFilters(searchSettings, _dateTimeProvider, request.CheckInDate); return new AccommodationAvailabilityResult(resultId, timestamp, details.AvailabilityId, accommodationAvailability.Accommodation, roomContractSets, duplicateReportId, minPrice, maxPrice, request.CheckInDate, request.CheckOutDate); }) .Where(a => a.RoomContractSets.Any()) .ToList()); } Task SaveResult(List <AccommodationAvailabilityResult> results) => _storage.SaveResults(searchId, supplier, results); Task SaveState(Result <List <AccommodationAvailabilityResult>, ProblemDetails> result) { var state = result.IsSuccess ? SupplierAvailabilitySearchState.Completed(searchId, result.Value.Select(r => r.DuplicateReportId).ToList(), result.Value.Count) : SupplierAvailabilitySearchState.Failed(searchId, result.Error.Detail); if (state.TaskState == AvailabilitySearchTaskState.Completed) { _logger.LogProviderAvailabilitySearchSuccess( $"Availability search with id '{searchId}' on supplier '{supplier}' finished successfully with '{state.ResultCount}' results"); } else { _logger.LogProviderAvailabilitySearchFailure( $"Availability search with id '{searchId}' on supplier '{supplier}' finished with state '{state.TaskState}', error '{state.Error}'"); } return(_storage.SaveState(searchId, state, supplier)); } }