JavaRush /Java Blog /Random-IT /Creare un sistema di monitoraggio dei prezzi dei bigliett...
Roman Beekeeper
Livello 35

Creare un sistema di monitoraggio dei prezzi dei biglietti aerei: una guida passo passo [Parte 1]

Pubblicato nel gruppo Random-IT

Contenuto:

Creare un sistema di monitoraggio dei prezzi dei biglietti aerei: una guida passo passo [Parte 1] - 1Ciao a tutti, comunità JavaRush! Oggi parleremo passo dopo passo di come scrivere un'applicazione Spring Boot per monitorare i prezzi dei biglietti aerei. L'articolo è destinato a persone che hanno un'idea su:
  • REST e come vengono creati gli endpoint REST;
  • database relazionali;
  • il lavoro degli esperti (in particolare, cos'è la dipendenza);
  • Oggetto JSON;
  • principi di registrazione.
Comportamento atteso:
  1. Puoi selezionare un volo per una data specifica e monitorarne il prezzo. L'utente è identificato tramite l'indirizzo email. Non appena viene effettuata una sottoscrizione ad una variazione di prezzo, l'utente riceve una notifica via e-mail.
  2. Ogni 30 minuti (questo intervallo è configurato tramite application.properties) viene ricalcolato il prezzo minimo di un volo per tutti gli abbonamenti. Se uno dei valori è sceso, l'utente riceverà una notifica via email.
  3. Tutti gli abbonamenti con una data di volo obsoleta verranno eliminati.
  4. Tramite l'API REST puoi:
    • creare un abbonamento;
    • modificare;
    • ricevere tutti gli abbonamenti via e-mail;
    • eliminare l'abbonamento.

Piano d'azione per raggiungere l'obiettivo

È necessario iniziare dal fatto che le informazioni sui voli devono essere prese da qualche parte. In genere, i siti Web forniscono un'API REST aperta attraverso la quale è possibile recuperare le informazioni.

L'API (Application Programming Interface) è un'interfaccia attraverso la quale è possibile interagire con un'applicazione. Da questo possiamo costruire un ponte su cosa sia un'API REST.

Un'API REST è un'interfaccia di richieste REST che può essere utilizzata per comunicare con un'applicazione web.

Per fare ciò utilizzeremo Skyscanner , o meglio, l'API (sul sito web dell'API Rakuten ). Successivamente, è necessario scegliere la struttura giusta come base di base. Il più popolare e richiesto è l'ecosistema Spring e la corona della loro creazione: Spring Boot. Puoi andare sul loro sito ufficiale, oppure puoi leggere l'articolo su Habré . Per memorizzare gli abbonamenti degli utenti utilizzeremo il database H2 integrato . Per leggere da JSON alle lezioni e viceversa, utilizzeremo il Jackson Project ( ecco il collegamento sulla nostra risorsa ). Utilizzeremo spring-boot-starter-mail per inviare messaggi agli utenti Affinché l'applicazione ricalcoli il prezzo minimo con una determinata frequenza, utilizzeremo Spring Scheduler . Per creare un'API REST utilizzeremo spring-boot-starter-web . Per non scrivere codice preso in prestito (getter, setter, override equals e hashcode, toString() per gli oggetti), utilizzeremo Project Lombok . Per sentire e vedere l'API REST, utilizzeremo Swagger 2 e immediatamente l'interfaccia utente Swagger (interfaccia utente) per il monitoraggio in tempo reale. Ecco come appare adesso: Creare un sistema di monitoraggio dei prezzi dei biglietti aerei: una guida passo passo [Parte 1] - 2dove ci sono 4 query di riposo che corrispondono alla creazione, modifica, acquisizione ed eliminazione degli abbonamenti.

Esplorando l'API di Skyscanner

Seguiamo il collegamento all'API rakuten . Per prima cosa devi registrarti, Creare un sistema di monitoraggio dei prezzi dei biglietti aerei: una guida passo passo [Parte 1] - 3tutto ciò è necessario per ricevere una chiave univoca per utilizzare il proprio sito ed effettuare richieste alle API pubbliche che sono pubblicate su di esso. Una di queste API è la ricerca voli Skyscanner di cui abbiamo bisogno . Ora scopriamo come funziona. Troviamo la richiesta GET List Places. L'immagine mostra che è necessario compilare i dati e avviare Test Endpoint , a seguito del quale riceviamo una risposta sotto forma di oggetto JSON sulla destra: Creare un sistema per monitorare i prezzi dei biglietti aerei: una guida passo passo [Parte 1] - 4e la richiesta verrà creata in questo modo:

https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com/apiservices/autosuggest/v1.0/{country}/{currency}/{locale}/?query={query}
e tutti i parametri verranno sostituiti in questa formula, otteniamo:

https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com/apiservices/autosuggest/v1.0/UK/GBP/en-GB/?query=Stockholm
e a queste richieste verranno passate due intestazioni:

.header("x-rapidapi-host", "skyscanner-skyscanner-flight-search-v1.p.rapidapi.com")
.header("x-rapidapi-key", "sing-up-for-key"),
dove sign-up-for-keyviene rilasciato dopo la registrazione. Per tenere traccia del calo dei prezzi, avremo bisogno dell'endpoint Sfoglia quotazioni . Trovalo tu stesso :)

Creazione di un framework applicativo basato su Spring Boot

Per creare rapidamente e facilmente un progetto con Spring Boot, puoi utilizzare Spring Initializr . Seleziona le seguenti opzioni:
  1. Progetto Maven
  2. Giava
  3. 2.1.10
  4. group: qualunque cosa tu ritenga necessaria, ad esempio ru.javarush
  5. artefatto - esattamente lo stesso, ad esempio il monitoraggio dei voli
  6. nella ricerca delle dipendenze cerchiamo quanto segue:
    • Rete primaverile
    • Mittente di posta Java
    • Dati primaverili Jpa
    • Banca dati H2
E quindi fare clic su Genera . Questo è tutto: il progetto finito verrà scaricato come archivio. Se qualcosa non funziona, puoi utilizzare il link in cui ho salvato il progetto desiderato . Naturalmente, è meglio farlo da soli e capire come funziona. L'applicazione sarà composta da tre livelli:
  • CONTROLLER: accedi all'applicazione. L'API REST verrà descritta qui
  • SERVICE è un livello di logica aziendale. L'intera logica dell'applicazione verrà descritta qui.
  • REPOSITORY - livello per lavorare con il database.
Inoltre, le lezioni relative al client per l'API di ricerca voli di Skyscanner costituiranno un pacchetto separato.

Stiamo scrivendo a un client per le richieste all'API di ricerca voli di Skyscanner nel progetto

Skyscanner ha gentilmente fornito un articolo su come utilizzare la propria API (non creeremo una sessione con una richiesta attiva). Cosa significa "scrivere a un cliente"? Dobbiamo creare una richiesta a un URL specifico con determinati parametri e preparare un DTO (oggetto di trasferimento dati) per i dati che ci vengono trasferiti. Sono presenti quattro gruppi di richieste sul sito:
  1. Ricerca voli in tempo reale: al momento non lo considereremo superfluo.
  2. Luoghi: scriviamo.
  3. Sfoglia i prezzi dei voli: utilizzeremo una richiesta in cui potrai ottenere tutte le informazioni.
  4. Localizzazione: aggiungiamola in modo da sapere quali dati sono supportati.

Crea un servizio client per la richiesta di localizzazione:

Il piano è semplice come una rapa al vapore: crea una richiesta, guarda i parametri, guarda la risposta. Ci sono due query: Indicatori di elenco e Valute. Cominciamo con le valute. La figura mostra che si tratta di una richiesta senza campi aggiuntivi: è necessaria per ottenere informazioni sulle valute supportate: Creare un sistema di monitoraggio dei prezzi dei biglietti aerei: una guida passo passo [Parte 1] - 6La risposta è sotto forma di un oggetto JSON, che contiene una raccolta degli stessi oggetti, ad esempio:
{
"Code":"LYD"
"Symbol":"د.ل.‏"
"ThousandsSeparator":","
"DecimalSeparator":"."
"SymbolOnLeft":true
"SpaceBetweenAmountAndSymbol":false
"RoundingCoefficient":0
"DecimalDigits":3
}
Creiamo un CurrencyDto per questo oggetto:
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

/**
* Data transfer object for Currency.
*/
@Data
public class CurrencyDto {

   @JsonProperty("Code")
   private String code;

   @JsonProperty("Symbol")
   private String symbol;

   @JsonProperty("ThousandsSeparator")
   private String thousandsSeparator;

   @JsonProperty("DecimalSeparator")
   private String decimalSeparator;

   @JsonProperty("SymbolOnLeft")
   private boolean symbolOnLeft;

   @JsonProperty("SpaceBetweenAmountAndSymbol")
   private boolean spaceBetweenAmountAndSymbol;

   @JsonProperty("RoundingCoefficient")
   private int roundingCoefficient;

   @JsonProperty("DecimalDigits")
   private int decimalDigits;
}
Dove:
  • @Data è un'annotazione del progetto Lombok e genera tutti i metodi getter, setter, override toString(), equals() e hashCode(). Cosa migliora la leggibilità del codice e velocizza i tempi di scrittura degli oggetti POJO ;
  • @JsonProperty("Codice") è un'annotazione del Jackson Project che indica quale campo verrà assegnato a questa variabile. Cioè alla variabile code verrà assegnato un campo in JSON uguale a Code .
L' articolo ufficiale di Skyscanner suggerisce di utilizzare la libreria UniRest per le richieste REST . Pertanto, scriveremo un altro servizio che implementerà le richieste tramite REST. Questo sarà UniRestService . Per fare ciò, aggiungi una nuova dipendenza a Maven:
<dependency>
  <groupId>com.mashape.unirest</groupId>
  <artifactId>unirest-java</artifactId>
  <version>1.4.9</version>
</dependency>
Successivamente, scriveremo un servizio che eseguirà le richieste REST. Naturalmente per ogni cliente/servizio creeremo un'interfaccia e la sua implementazione:
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;

/**
* Service, which is manipulating with Rest calls.
*/
public interface UniRestService {

   /**
   * Create GET request based on provided {@param path} with needed headers.
   *
   * @param path provided path with all the needed data
   * @return {@link HttpResponse<jsonnode>} response object.
   */
   HttpResponse<jsonnode> get(String path);

}
E la sua implementazione:
import com.github.romankh3.flightsmonitoring.exception.FlightClientException;
import com.github.romankh3.flightsmonitoring.client.service.UniRestService;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

/**
* {@inheritDoc}
*/
@Slf4j
@Service
public class UniRestServiceImpl implements UniRestService {

   public static final String HOST = "https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com";

   public static final String PLACES_FORMAT = "/apiservices/autosuggest/v1.0/%s/%s/%s/?query=%s";
   public static final String CURRENCIES_FORMAT = "/apiservices/reference/v1.0/currencies";
   public static final String COUNTRIES_FORMAT = "/apiservices/reference/v1.0/countries/%s";

   public static final String PLACES_KEY = "Places";
   public static final String CURRENCIES_KEY = "Currencies";
   public static final String COUNTRIES_KEY = "Countries";

   @Value("${x.rapid.api.key}")
   private String xRapidApiKey;

   /**
    * {@inheritDoc}
    */
   @Override
   public HttpResponse<jsonnode> get(String path) {
       HttpResponse<jsonnode> response = null;
       try {
           response = Unirest.get(HOST + path)
                   .header("x-rapidapi-host", "skyscanner-skyscanner-flight-search-v1.p.rapidapi.com")
                   .header("x-rapidapi-key", xRapidApiKey)
                   .asJson();
       } catch (UnirestException e) {
           throw new FlightClientException(String.format("Request failed, path=%s", HOST + path), e);
       }

       log.info("Response from Get request, on path={}, statusCode={}, response={}", path, response.getStatus(), response.getBody().toString());
       return response;
   }
}
La sua essenza è che tutte le richieste a cui siamo interessati vengono create per richieste GET e questo servizio accetta una richiesta già pronta e aggiunge le intestazioni necessarie come:
.header("x-rapidapi-host", "skyscanner-skyscanner-flight-search-v1.p.rapidapi.com")
.header("x-rapidapi-key", xRapidApiKey)
Per prendere dati dalle proprietà, utilizzare l'annotazione @Value come mostrato di seguito:
@Value("${x.rapid.api.key}")
private String xRapidApiKey;
Dice che in application.properties ci sarà una proprietà denominata x.rapid.api.key, che deve essere inserita in questa variabile. Ci liberiamo dei valori codificati e deriviamo la definizione di questa variabile dal codice del programma. Inoltre quando pubblico questa applicazione su GitHub non aggiungo il valore di questa proprietà. Questo viene fatto per motivi di sicurezza. Abbiamo scritto un servizio che funzionerà con le richieste REST, ora è il momento di un servizio per la localizzazione. Stiamo costruendo un'applicazione basata su OOP, quindi creiamo l' interfaccia LocalizationClient e la sua implementazione LocalisationClientImpl :
import com.github.romankh3.flightsmonitoring.client.dto.CountryDto;
import com.github.romankh3.flightsmonitoring.client.dto.CurrencyDto;
import java.io.IOException;
import java.util.List;

/**
* Client for SkyScanner localisation.
*/
public interface LocalisationClient {

   /**
    * Retrieve the market countries that SkyScanner flight search API support. Most suppliers (airlines,
    * travel agents and car hire dealers) set their fares based on the market (or country of purchase).
    * It is therefore necessary to specify the market country in every query.
    *
    * @param locale locale of the response.
    *
    * @return the collection of the {@link CountryDto} objects.
    *
    * @throws IOException
    */
   List<CountryDto> retrieveCountries(String locale);

   /**
    * Retrieve the currencies that we ScyScanner flight search API.
    *
    * @return the collection of the {@link CurrencyDto} objects.
    *
    * @throws IOException
    */
   List<CurrencyDto> retrieveCurrencies();

}
e implementazione di LocalisationClientImpl
import static com.github.romankh3.flightsmonitoring.client.service.impl.UniRestServiceImpl.COUNTRIES_FORMAT;
import static com.github.romankh3.flightsmonitoring.client.service.impl.UniRestServiceImpl.COUNTRIES_KEY;
import static com.github.romankh3.flightsmonitoring.client.service.impl.UniRestServiceImpl.CURRENCIES_FORMAT;
import static com.github.romankh3.flightsmonitoring.client.service.impl.UniRestServiceImpl.CURRENCIES_KEY;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.romankh3.flightsmonitoring.client.dto.CountryDto;
import com.github.romankh3.flightsmonitoring.client.dto.CurrencyDto;
import com.github.romankh3.flightsmonitoring.client.service.LocalisationClient;
import com.github.romankh3.flightsmonitoring.client.service.UniRestService;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.exceptions.UnirestException;
import java.io.IOException;
import java.util.List;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
* {@inheritDoc}
*/
@Component
public class LocalisationClientImpl implements LocalisationClient {

   @Autowired
   private UniRestService uniRestService;

   @Autowired
   private ObjectMapper objectMapper;

   /**
    * {@inheritDoc}
    */
   @Override
   public List<CountryDto> retrieveCountries(String locale) throws IOException {
       HttpResponse<JsonNode> response = uniRestService.get(String.format(COUNTRIES_FORMAT, locale));

       if (response.getStatus() != HttpStatus.SC_OK) {
           return null;
       }

       String jsonList = response.getBody().getObject().get(COUNTRIES_KEY).toString();

       return objectMapper.readValue(jsonList, new TypeReference<List<CountryDto>>() {
       });
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public List<CurrencyDto> retrieveCurrencies() throws IOException, UnirestException {

       HttpResponse<JsonNode> response = uniRestService.get(CURRENCIES_FORMAT);
       if (response.getStatus() != HttpStatus.SC_OK) {
           return null;
       }

       String jsonList = response.getBody().getObject().get(CURRENCIES_KEY).toString();

       return objectMapper.readValue(jsonList, new TypeReference<List<CurrencyDto>>() {
       });
   }
}
Dove
  • @Autowired è un'annotazione che dice che è necessario iniettare un oggetto in questa classe e usarlo senza crearlo, cioè senza la nuova operazione Object;
  • @Component è un'annotazione che dice che questo oggetto deve essere aggiunto al contesto dell'applicazione in modo che possa successivamente essere iniettato utilizzando l'annotazione @Autowired;
  • ObjectMapper objectMapper è un oggetto del Jackson Project che traduce tutto questo in oggetti Java.
  • ValutaDTO e PaeseDto:
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

/**
* Data transfer object for Currency.
*/
@Data
public class CurrencyDto {

   @JsonProperty("Code")
   private String code;

   @JsonProperty("Symbol")
   private String symbol;

   @JsonProperty("ThousandsSeparator")
   private String thousandsSeparator;

   @JsonProperty("DecimalSeparator")
   private String decimalSeparator;

   @JsonProperty("SymbolOnLeft")
   private boolean symbolOnLeft;

   @JsonProperty("SpaceBetweenAmountAndSymbol")
   private boolean spaceBetweenAmountAndSymbol;

   @JsonProperty("RoundingCoefficient")
   private int roundingCoefficient;

   @JsonProperty("DecimalDigits")
   private int decimalDigits;
}
	и
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

/**
* Data transfer object for Country.
*/
@Data
public class CountryDto {

   @JsonProperty("Code")
   private String code;

   @JsonProperty("Name")
   private String name;
}
Per inserire un ObjectMapper in qualsiasi parte del progetto, ho aggiunto la creazione e l'aggiunta all'ApplicationContext tramite una classe di configurazione.
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* {@link Configuration} class.
*/
@Configuration
public class Config {

   @Bean
   public ObjectMapper objectMapper() {
       ObjectMapper objectMapper = new ObjectMapper();
       objectMapper.registerModule(new JavaTimeModule());
       return objectMapper;
   }
}
L'annotazione @Configuration dice a Spring che ci saranno alcune configurazioni in questa classe. E proprio per questo ho aggiunto ObjectMapper. Allo stesso modo, aggiungiamo PlacesClient e PlacesClientImpl:
import com.github.romankh3.flightsmonitoring.client.dto.PlaceDto;
import com.github.romankh3.flightsmonitoring.client.dto.PlacesDto;
import com.mashape.unirest.http.exceptions.UnirestException;
import java.io.IOException;
import java.util.List;

/**
* SkyScanner client.
*/
public interface PlacesClient {

   /**
    * Get a list of places that match a query string based on arguments.
    *
    * @param query the code of the city.
    * @param country the code of the country.
    * @param currency the code of the currency.
    * @param locale the code of the locale.
    * @return the collection of the {@link PlaceDto} objects.
    */
   List<PlacesDto> retrieveListPlaces(String query, String country, String currency, String locale)
           throws IOException, UnirestException;
}
E
import static com.github.romankh3.flightsmonitoring.client.service.impl.UniRestServiceImpl.PLACES_FORMAT;
import static com.github.romankh3.flightsmonitoring.client.service.impl.UniRestServiceImpl.PLACES_KEY;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.romankh3.flightsmonitoring.client.dto.PlacesDto;
import com.github.romankh3.flightsmonitoring.client.service.PlacesClient;
import com.github.romankh3.flightsmonitoring.client.service.UniRestService;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.exceptions.UnirestException;
import java.io.IOException;
import java.util.List;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
* {@inheritDoc}
*/
@Service
public class PlacesClientImpl implements PlacesClient {

   @Autowired
   private UniRestService uniRestService;

   @Autowired
   private ObjectMapper objectMapper;


   /**
    * {@inheritDoc}
    */
   @Override
   public List<PlacesDto> retrieveListPlaces(String query, String country, String currency, String locale)
           throws IOException, UnirestException {
       HttpResponse<JsonNode> response = uniRestService
               .get(String.format(PLACES_FORMAT, country, currency, locale, query));

       if (response.getStatus() != HttpStatus.SC_OK) {
           return null;
       }

       String jsonList = response.getBody().getObject().get(PLACES_KEY).toString();

       return objectMapper.readValue(jsonList, new TypeReference<List<PlacesDto>>() {
       });
   }
}
dove PlacesDto ha la forma:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.github.romankh3.flightsmonitoring.client.service.PlacesClient;
import lombok.Data;

/**
* Using for {@link PlacesClient}.
*/
@Data
public class PlacesDto {

   @JsonProperty("PlaceId")
   private String placeId;

   @JsonProperty("PlaceName")
   private String placeName;

   @JsonProperty("CountryId")
   private String countryId;

   @JsonProperty("RegionId")
   private String regionId;

   @JsonProperty("CityId")
   private String cityId;

   @JsonProperty("CountryName")
   private String countryName;
}
E infine un servizio clienti che, in base ai dati necessari, restituirà il prezzo minimo per un volo e tutte le informazioni necessarie: FlightPriceClient e FlightPriceClientImpl. Implementeremo solo una richiesta di browserQuotes. Prezzo Volo Cliente:
import com.github.romankh3.flightsmonitoring.client.dto.FlightPricesDto;

/**
* Browse flight prices.
*/
public interface FlightPricesClient {

   /**
    * Browse quotes for current flight based on provided arguments. One-way ticket.
    *
    * @param country the country from
    * @param currency the currency to get price
    * @param locale locale for the response
    * @param originPlace origin place
    * @param destinationPlace destination place
    * @param outboundPartialDate outbound date
    * @return {@link FlightPricesDto} object.
    */
   FlightPricesDto browseQuotes(String country, String currency, String locale, String originPlace,
           String destinationPlace, String outboundPartialDate);

   /**
    * Browse quotes for current flight based on provided arguments. Round trip ticket.
    *
    * @param country the country from
    * @param currency the currency to get price
    * @param locale locale for the response
    * @param originPlace origin place
    * @param destinationPlace destination place
    * @param outboundPartialDate outbound date
    * @param inboundPartialDate inbound date
    * @return {@link FlightPricesDto} object.
    */
   FlightPricesDto browseQuotes(String country, String currency, String locale, String originPlace,
           String destinationPlace, String outboundPartialDate, String inboundPartialDate);
}
VoloPrezzoClienteImpl
import static com.github.romankh3.flightsmonitoring.client.service.impl.UniRestServiceImpl.CURRENCIES_KEY;
import static com.github.romankh3.flightsmonitoring.client.service.impl.UniRestServiceImpl.PLACES_KEY;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.romankh3.flightsmonitoring.client.dto.CarrierDto;
import com.github.romankh3.flightsmonitoring.client.dto.CurrencyDto;
import com.github.romankh3.flightsmonitoring.client.dto.FlightPricesDto;
import com.github.romankh3.flightsmonitoring.client.dto.PlaceDto;
import com.github.romankh3.flightsmonitoring.client.dto.QuoteDto;
import com.github.romankh3.flightsmonitoring.client.dto.ValidationErrorDto;
import com.github.romankh3.flightsmonitoring.client.service.FlightPricesClient;
import com.github.romankh3.flightsmonitoring.client.service.UniRestService;
import com.github.romankh3.flightsmonitoring.exception.FlightClientException;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import java.io.IOException;
import java.util.List;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
* {@inheritDoc}
*/
@Service
public class FlightPricesClientImpl implements FlightPricesClient {

   public static final String BROWSE_QUOTES_FORMAT = "/apiservices/browsequotes/v1.0/%s/%s/%s/%s/%s/%s";
   public static final String OPTIONAL_BROWSE_QUOTES_FORMAT = BROWSE_QUOTES_FORMAT + "?inboundpartialdate=%s";

   public static final String QUOTES_KEY = "Quotes";
   public static final String ROUTES_KEY = "Routes";
   public static final String DATES_KEY = "Dates";
   public static final String CARRIERS_KEY = "Carriers";
   public static final String VALIDATIONS_KEY = "ValidationErrors";

   @Autowired
   private UniRestService uniRestService;

   @Autowired
   private ObjectMapper objectMapper;

   /**
    * {@inheritDoc}
    */
   @Override
   public FlightPricesDto browseQuotes(String country, String currency, String locale, String originPlace,
           String destinationPlace, String outboundPartialDate) {

       HttpResponse<JsonNode> response = uniRestService.get(String
               .format(BROWSE_QUOTES_FORMAT, country, currency, locale, originPlace, destinationPlace,
                       outboundPartialDate));
       return mapToObject(response);
   }

   public FlightPricesDto browseQuotes(String country, String currency, String locale, String originPlace,
           String destinationPlace, String outboundPartialDate, String inboundPartialDate) {
       HttpResponse<JsonNode> response = uniRestService.get(String
               .format(OPTIONAL_BROWSE_QUOTES_FORMAT, country, currency, locale, originPlace, destinationPlace,
                       outboundPartialDate, inboundPartialDate));
       return mapToObject(response);
   }

   private FlightPricesDto mapToObject(HttpResponse<JsonNode> response) {
       if (response.getStatus() == HttpStatus.SC_OK) {
           FlightPricesDto flightPricesDto = new FlightPricesDto();
           flightPricesDto.setQuotas(readValue(response.getBody().getObject().get(QUOTES_KEY).toString(),
                   new TypeReference<List<QuoteDto>>() {
                   }));
           flightPricesDto.setCarriers(readValue(response.getBody().getObject().get(CARRIERS_KEY).toString(),
                   new TypeReference<List<CarrierDto>>() {
                   }));
           flightPricesDto.setCurrencies(readValue(response.getBody().getObject().get(CURRENCIES_KEY).toString(),
                   new TypeReference<List<CurrencyDto>>() {
                   }));
           flightPricesDto.setPlaces(readValue(response.getBody().getObject().get(PLACES_KEY).toString(),
                   new TypeReference<List<PlaceDto>>() {
                   }));
           return flightPricesDto;
       }
       throw new FlightClientException(String.format("There are validation errors. statusCode = %s", response.getStatus()),
               readValue(response.getBody().getObject().get(VALIDATIONS_KEY).toString(),
                       new TypeReference<List<ValidationErrorDto>>() {
                       }));
   }

   private <T> List<T> readValue(String resultAsString, TypeReference<List<T>> valueTypeRef) {
       List<T> list;
       try {
           list = objectMapper.readValue(resultAsString, valueTypeRef);
       } catch (IOException e) {
           throw new FlightClientException("Object Mapping failure.", e);
       }
       return list;
   }
}
dove FlightClientException appare così:
import com.github.romankh3.flightsmonitoring.client.dto.ValidationErrorDto;
import java.util.List;

/**
* A {@link RuntimeException} that is thrown in case of an flight monitoring failures.
*/
public final class FlightClientException extends RuntimeException {

   public FlightClientException(String message) {
       super(message);
   }

   public FlightClientException(String message, Throwable throwable) {
       super(message, throwable);
   }

   public FlightClientException(String message, List<ValidationErrorDto> errors) {
       super(message);
       this.validationErrorDtos = errors;
   }

   private List<ValidationErrorDto> validationErrorDtos;
}
Di conseguenza, secondo i dati di PlacesCl
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION