/// <summary> /// A function to retrieve flight's quotes from multiple web services concurrently. /// The search response is then joined from each individual flight services once the all search is completed. /// </summary> /// <param name="modelQuoteRequest">A model encapsulating a flight request</param> /// <returns></returns> public IEnumerable <IFlightResponse> BrowseQuotes(IFlightRequest modelQuoteRequest) { var responses = new List <IFlightResponse>(); //response place holder var childTasks = new List <Task <List <IFlightResponse> > >(); //list of task running concurrently try { LogRequest(modelQuoteRequest); //parent task to hold response until all child task running are completed. var parentTask = StartSearch(modelQuoteRequest, childTasks); //wait for all child tasks to complete parentTask.Wait(); //join search response form each collaborator childTasks.ForEach(_ => responses.AddRange(_.Result)); } catch (Exception ex) { LogErrors(ex, childTasks); } Logger.Info($"All search completed: responses found ({responses.Count})"); return(responses); }
private static Uri ParseApiRequestUri(IFlightRequest modelQuoteRequest) { var browseQuotesUrl = $"{ApiServiceUrl}/browsequotes/v1.0/US/USD/en-US"; return(new Uri($"{browseQuotesUrl}/{modelQuoteRequest.Origin}/{modelQuoteRequest.Destination}" + $"/{modelQuoteRequest.DepartDate}?inboundpartialdate={modelQuoteRequest.ReturnDate}")); }
private static IFlightResponse ParseResponse(IFlightRequest modelQuoteRequest, SkyscannerBrowseQuoteResponse response, Quote quote) { IFlightItinerary outboundItinerary = GetItinerary(response, quote.OutboundLeg); IFlightItinerary inboundItinerary = GetItinerary(response, quote.InboundLeg); var flightItineraries = new List <IFlightItinerary>(); if (inboundItinerary != null) { flightItineraries.Add(inboundItinerary); } if (outboundItinerary != null) { flightItineraries.Add(outboundItinerary); } IFlightResponse aFlightResponse = new FlightResponse() { QuoteSource = "Skyscanner", TripTotalPrice = TryGetCost(quote.MinPrice) ?? quote.MinPrice, BookingLink = GetBookingLink(modelQuoteRequest), FlightItineraries = flightItineraries }; return(aFlightResponse); }
private static HttpRequestMessage GetHttpRequestMessage(IFlightRequest modelQuoteRequest) { var httpRequest = new HttpRequestMessage() { Method = HttpMethod.Get, }; httpRequest.Headers.Add(RapidApiHostAppSettingName, SecretRapidApiHost); httpRequest.Headers.Add(RapidApiKeyAppSettingName, SecretRapidApiKey); httpRequest.RequestUri = ParseApiRequestUri(modelQuoteRequest); return(httpRequest); }
/// <summary> /// The URI used to perform the query /// </summary> /// <param name="modelQuoteRequest">User request</param> /// <returns>The Uri to perform flight request</returns> public string GetKayakUriForSearch(IFlightRequest modelQuoteRequest) { if (KayakFlightsUrl == null) { throw new Exception($"AppSettings '{KayakFlightUrlKey}' found found."); } var tripQuoteUrl = $"{KayakFlightsUrl}/{modelQuoteRequest.Origin}-{modelQuoteRequest.Destination}/" + $"{modelQuoteRequest.DepartDate}"; if (modelQuoteRequest.ReturnDate != null) { tripQuoteUrl = $"{tripQuoteUrl}/{modelQuoteRequest.ReturnDate}"; } return($"{tripQuoteUrl}?sort=bestflight_a"); }
private void Validate(IFlightRequest flightRequest) { const int locationCodeLength = 3; //Find out the 3-letter code of a location (airport, city) or identify which airport or city uses a particular code if (flightRequest.Origin.IsNullOrEmpty() || flightRequest.Origin.Length < locationCodeLength) { ModelState.AddModelError("originPlace", "REQUIRED The origin place, minimum a 3-letter code of a location (airport, city)"); } if (flightRequest.Destination.IsNullOrEmpty() || flightRequest.Destination.Length < locationCodeLength) { ModelState.AddModelError("destinationPlace", "REQUIRED The destination place, minimum a 3-letter code of a location (airport, city)"); } //make sure depart/return dates make sense ValidateDates(flightRequest.DepartDate, flightRequest.ReturnDate); }
/// <summary> /// Perform the actual web api search for flights sync /// </summary> /// <param name="modelQuoteRequest">model wrapping the actual user request</param> /// <returns></returns> public async Task <SkyscannerBrowseQuoteResponse> BrowseQuotesAsync(IFlightRequest modelQuoteRequest) { if (!ValidateApiSetting()) { throw new Exception("Require skyscanner secret api host, key and url"); } using (var httpRequest = GetHttpRequestMessage(modelQuoteRequest)) using (var responseMessage = await LocalHttpClient.SendAsync(httpRequest)) { if (!responseMessage.IsSuccessStatusCode) { throw new Exception($"Rapid Skyscanner-Flight search status code `{responseMessage.StatusCode}` Error:{responseMessage.ReasonPhrase}"); } using (var readStream = new StreamReader(await responseMessage.Content.ReadAsStreamAsync(), Encoding.UTF8)) return(JsonConvert.DeserializeObject <SkyscannerBrowseQuoteResponse>(readStream.ReadToEnd())); } }
/// <summary> /// Browse for real-time flight quotes /// </summary> /// <param name="request">the user flight request</param> /// <returns></returns> public async Task <List <IFlightResponse> > BrowseQuotesAsync(IFlightRequest request) { var realTimeFlights = new List <IFlightResponse>(); try { var numOfLodAttempts = 0; do //run query until result is found but not exceeding the maximum number of tries. { await LoadQuotes(request, realTimeFlights); numOfLodAttempts++; }while (ContinueSearch(realTimeFlights, numOfLodAttempts)); } catch (Exception ex) { Logger.Error($"{ex.Message}|{ex.InnerException}"); } return(realTimeFlights); }
/// <summary> /// Browse for flights quotes from skyscanner Api /// </summary> /// <param name="request">An user flight request</param> /// <returns></returns> public async Task <List <IFlightResponse> > BrowseQuotesAsync(IFlightRequest request) { var flightResponses = new List <IFlightResponse>(); try { //load flights from skyscanner api SkyscannerBrowseQuoteResponse response = await Loader.BrowseQuotesAsync(request); //parse flight response to the model shown to the user flightResponses.AddRange(response.Quotes.Select(quote => ParseResponse(request, response, quote))); } catch (Exception ex) { //log exception Logger.Error($"{ex.Message}|{ex.InnerException}"); } //return flight response for one-was or round trip. return(flightResponses); }
/// <summary> /// Load real-time flights prices /// </summary> /// <param name="modelQuoteRequest">model representing the users flight request</param> /// <param name="flights">list of flight to append results to the end</param> /// <returns></returns> private async Task LoadQuotes(IFlightRequest modelQuoteRequest, List <IFlightResponse> flights) { var kayakLoader = new KayakLoader(modelQuoteRequest); IHtmlCollection <IElement> searchResults = await kayakLoader.GetFlightCollection(); if (searchResults == null) { return; } foreach (IElement searchResult in searchResults) { var flight = new FlightResponse { QuoteSource = "Kayak", TripTotalPrice = KayakLoader.GetFlightCost(searchResult), BookingLink = kayakLoader.RequestUrl, FlightItineraries = KayakLoader.GetFlightItineraries(searchResult) }; flights.Add(flight); } }
private Task StartSearch(IFlightRequest modelQuoteRequest, ICollection <Task <List <IFlightResponse> > > childTasks) { return(Task.Factory.StartNew(() => { var childFactory = new TaskFactory(TaskCreationOptions.AttachedToParent, TaskContinuationOptions.None); foreach (var service in _flightQuoteServiceCollaborators) { var serviceName = service.GetType().Name; Logger.Info($"{serviceName} initiate service quote search."); childFactory.StartNew(() => { //search for flights var aTask = Task.Run(async() => await service.BrowseQuotesAsync(modelQuoteRequest)); //on search complete log search result count aTask.GetAwaiter().OnCompleted(() => Logger.Info($"{serviceName} search completed. responses ({aTask.Result.Count})")); //track child task childTasks.Add(aTask); }); } })); }
private static void LogRequest(IFlightRequest modelQuoteRequest) { Logger.Info($"Initiating flight search from {modelQuoteRequest.Origin} to " + $"{modelQuoteRequest.Destination} depart on {modelQuoteRequest.DepartDate} " + $"return {modelQuoteRequest.ReturnDate}"); }
/// <summary> /// Initialize url request using user search detail. /// </summary> /// <param name="modelQuoteRequest"></param> public KayakLoader(IFlightRequest modelQuoteRequest) { RequestUrl = GetKayakUriForSearch(modelQuoteRequest); }
private static string GetBookingLink(IFlightRequest modelQuoteRequest) { return($"{Loader.GetBaseBookingUrl()}/{modelQuoteRequest.Origin}/{modelQuoteRequest.Destination}" + $"/{modelQuoteRequest.DepartDate}/{modelQuoteRequest.ReturnDate}"); }