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);
                });
            }
        }
Пример #2
0
        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);
        }
Пример #3
0
        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);
            }
        }
Пример #4
0
        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);
        }
Пример #5
0
 public Task SaveState(Guid searchId, SupplierAvailabilitySearchState state, Suppliers supplier)
 => _multiProviderAvailabilityStorage.Save(searchId.ToString(), state, supplier);
Пример #6
0
        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));
            }
        }