JavaRush /Blog Java /Random-FR /Créer un système de surveillance du prix des billets d'av...
Roman Beekeeper
Niveau 35

Créer un système de surveillance du prix des billets d'avion : un guide étape par étape [Partie 1]

Publié dans le groupe Random-FR

Contenu:

Créer un système de suivi des prix des billets d'avion : un guide étape par étape [Partie 1] - 1Bonjour à tous, communauté JavaRush ! Aujourd'hui, nous allons expliquer comment écrire une application Spring Boot pour surveiller étape par étape les prix des billets d'avion. L'article est destiné aux personnes qui ont une idée sur :
  • REST et comment les points de terminaison REST sont construits ;
  • bases de données relationnelles ;
  • le travail de maven (en particulier, qu'est-ce que la dépendance) ;
  • Objet JSON ;
  • principes de journalisation.
Comportement attendu:
  1. Vous pouvez sélectionner un vol pour une date spécifique et en suivre le prix. L'utilisateur est identifié par son adresse email. Dès qu'une souscription à un changement de prix est effectuée, l'utilisateur reçoit une notification par email.
  2. Toutes les 30 minutes (cet intervalle est configuré via application.properties), le prix minimum d'un vol est recalculé pour tous les abonnements. Si l'une des valeurs devient inférieure, l'utilisateur recevra une notification par email.
  3. Tous les abonnements avec une date de vol obsolète seront supprimés.
  4. Via l'API REST, vous pouvez :
    • créer un abonnement ;
    • modifier;
    • recevoir tous les abonnements par email ;
    • supprimer l'abonnement.

Plan d'action pour atteindre l'objectif

Vous devez commencer par le fait que les informations sur les vols doivent provenir de quelque part. En règle générale, les sites Web fournissent une API REST ouverte grâce à laquelle les informations peuvent être récupérées.

L'API (interface de programmation d'application) est une interface grâce à laquelle vous pouvez interagir avec une application. À partir de là, nous pouvons construire un pont vers ce qu’est une API REST.

Une API REST est une interface de requêtes REST qui peut être utilisée pour communiquer avec une application web.

Pour ce faire, nous utiliserons Skyscanner , ou plutôt l'API (sur le site Rakuten API ). Ensuite, vous devez choisir le bon framework comme base de base. Le plus populaire et le plus demandé est l'écosystème Spring et la couronne de leur création - Spring Boot. Vous pouvez aller sur leur site officiel, ou vous pouvez lire l'article sur Habré . Pour stocker les abonnements des utilisateurs, nous utiliserons la base de données H2 intégrée . Pour lire du JSON aux classes et inversement, nous utiliserons le projet Jackson ( voici le lien sur notre ressource ). Nous utiliserons spring-boot-starter-mail pour envoyer des messages aux utilisateurs .Pour que l'application recalcule le prix minimum à une fréquence donnée, nous utiliserons Spring Scheduler . Pour créer une API REST, nous utiliserons spring-boot-starter-web . Afin de ne pas écrire de code emprunté (getters, setters, override equals et hashcode, toString() pour les objets), nous utiliserons le Project Lombok . Pour ressentir et voir l'API REST, nous utiliserons Swagger 2 et immédiatement Swagger UI (interface utilisateur) pour le suivi en temps réel. Voici à quoi cela ressemble maintenant : Créer un système de surveillance du prix des billets d'avion : un guide étape par étape [Partie 1] - 2où se trouvent 4 requêtes restantes qui correspondent à la création, la modification, l'obtention et la suppression d'abonnements.

Explorer l'API Skyscanner

Suivons le lien vers l'API rakuten . Vous devez d'abord vous inscrire. Créer un système de surveillance du prix des billets d'avion : un guide étape par étape [Partie 1] - 3Tout cela est nécessaire pour recevoir une clé unique pour utiliser leur site et faire des requêtes aux API publiques qui y sont publiées. L'une de ces API est la recherche de vols Skyscanner dont nous avons besoin . Voyons maintenant comment cela fonctionne. Trouvons la requête GET List Places. L'image montre que vous devez remplir les données et démarrer Test Endpoint , à la suite de quoi nous recevons une réponse sous la forme d'un objet JSON à droite : Créer un système de suivi des prix des billets d'avion : un guide étape par étape [Partie 1] - 4et la requête sera créée comme ceci :

https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com/apiservices/autosuggest/v1.0/{country}/{currency}/{locale}/?query={query}
et tous les paramètres seront substitués dans cette formule, nous obtenons :

https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com/apiservices/autosuggest/v1.0/UK/GBP/en-GB/?query=Stockholm
et deux en-têtes seront passés à ces requêtes :

.header("x-rapidapi-host", "skyscanner-skyscanner-flight-search-v1.p.rapidapi.com")
.header("x-rapidapi-key", "sing-up-for-key"),
sign-up-for-keyil est délivré après enregistrement. Pour suivre la baisse des prix, nous aurons besoin du point de terminaison Browse Quotes . Trouvez-le vous-même :)

Création d'un framework d'application basé sur Spring Boot

Pour créer rapidement et facilement un projet avec Spring Boot, vous pouvez utiliser Spring Initializr . Sélectionnez les options suivantes :
  1. Projet Maven
  2. Java
  3. 2.1.10
  4. groupe - selon ce que vous jugez nécessaire, par exemple ru.javarush
  5. artefact - exactement le même, par exemple surveillance des vols
  6. dans la recherche de dépendances, nous recherchons les éléments suivants :
    • Web de printemps
    • Expéditeur de courrier Java
    • Données de printemps Jpa
    • Base de données H2
Et puis cliquez sur Générer . Ça y est : le projet terminé sera téléchargé sous forme d'archive. Si quelque chose ne fonctionne pas, vous pouvez utiliser le lien où j'ai enregistré le projet souhaité . Bien sûr, il est préférable de le faire vous-même et de comprendre comment cela fonctionne. L'application comprendra trois couches :
  • CONTRÔLEUR - connectez-vous à l'application. L'API REST sera décrite ici
  • SERVICE est une couche de logique métier. Toute la logique de l'application sera décrite ici.
  • REPOSITORY - couche pour travailler avec la base de données.
De plus, les cours liés au client pour l'API Skyscanner Flight Search constitueront un package distinct.

Nous écrivons au client pour les demandes adressées à l'API Skyscanner Flight Search dans le projet

Skyscanner a aimablement fourni un article expliquant comment utiliser son API (nous ne créerons pas de session avec une requête active). Que signifie « écrire à un client » ? Nous devons créer une requête vers une URL spécifique avec certains paramètres et préparer un DTO (objet de transfert de données) pour les données qui nous sont renvoyées. Il existe quatre groupes de demandes sur le site :
  1. Recherche de vols en direct - nous ne la considérerons pas comme inutile pour le moment.
  2. Lieux - écrivons.
  3. Parcourir les prix des vols - nous utiliserons une seule requête pour obtenir toutes les informations.
  4. Localisation - ajoutons-le pour que nous sachions quelles données sont prises en charge.

Créez un service client pour la demande de localisation :

Le plan est simple comme un navet cuit à la vapeur : créez une requête, regardez les paramètres, regardez la réponse. Il existe deux requêtes : Liste des marqueurs et Devises. Commençons par les devises. La figure montre qu'il s'agit d'une requête sans champs supplémentaires : elle est nécessaire pour obtenir des informations sur les devises prises en charge : Créer un système de surveillance du prix des billets d'avion : un guide étape par étape [Partie 1] - 6La réponse se présente sous la forme d'un objet JSON, qui contient une collection des mêmes objets, par exemple :
{
"Code":"LYD"
"Symbol":"د.ل.‏"
"ThousandsSeparator":","
"DecimalSeparator":"."
"SymbolOnLeft":true
"SpaceBetweenAmountAndSymbol":false
"RoundingCoefficient":0
"DecimalDigits":3
}
Créons un CurrencyDto pour cet objet :
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;
}
Où:
  • @Data est une annotation du projet Lombok et génère tous les getters, setters, remplace les méthodes toString(), equals() et hashCode(). Ce qui améliore la lisibilité du code et accélère le temps d'écriture des objets POJO ;
  • @JsonProperty("Code") est une annotation du projet Jackson qui indique quel champ sera attribué à cette variable. C'est-à-dire qu'un champ en JSON égal à Code sera attribué à la variable code .
L' article officiel de Skyscanner suggère d'utiliser la bibliothèque UniRest pour les requêtes REST . Par conséquent, nous écrirons un autre service qui implémentera les requêtes via REST. Ce sera UniRestService . Pour ce faire, ajoutez une nouvelle dépendance à maven :
<dependency>
  <groupId>com.mashape.unirest</groupId>
  <artifactId>unirest-java</artifactId>
  <version>1.4.9</version>
</dependency>
Ensuite, nous écrirons un service qui effectuera les requêtes REST. Bien entendu, pour chaque client/service nous créerons une interface et sa mise en œuvre :
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);

}
Et sa mise en œuvre :
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;
   }
}
Son essence est que toutes les requêtes qui nous intéressent sont créées pour les requêtes GET, et ce service accepte une requête prête à l'emploi et ajoute les en-têtes nécessaires tels que :
.header("x-rapidapi-host", "skyscanner-skyscanner-flight-search-v1.p.rapidapi.com")
.header("x-rapidapi-key", xRapidApiKey)
Pour extraire des données de propriétés, utilisez l'annotation @Value comme indiqué ci-dessous :
@Value("${x.rapid.api.key}")
private String xRapidApiKey;
Il indique que dans application.properties, il y aura une propriété nommée x.rapid.api.key, qui doit être injectée dans cette variable. Nous nous débarrassons des valeurs codées en dur et dérivons la définition de cette variable du code du programme. De plus, lorsque je publie cette application sur GitHub je n'ajoute pas la valeur de cette propriété. Ceci est fait pour des raisons de sécurité. Nous avons écrit un service qui fonctionnera avec les requêtes REST, il est maintenant temps d'avoir un service de localisation. Nous construisons une application basée sur la POO, nous créons donc l' interface LocalizationClient et son implémentation 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();

}
et implémentation de LocalizationClientImpl
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>>() {
       });
   }
}
  • @Autowired est une annotation qui indique que vous devez injecter un objet dans cette classe et l'utiliser sans le créer, c'est-à-dire sans la nouvelle opération Object ;
  • @Component est une annotation qui indique que cet objet doit être ajouté au contexte d'application afin qu'il puisse ensuite être injecté à l'aide de l'annotation @Autowired ;
  • ObjectMapper objectMapper est un objet du projet Jackson qui traduit tout cela en objets Java.
  • DeviseDTO et CountryDto :
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;
}
Pour injecter un ObjectMapper dans n'importe quelle partie du projet, j'ai ajouté sa création et son ajout à ApplicationContext via une classe de configuration.
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'annotation @Configuration indique à Spring qu'il y aura certaines configurations dans cette classe. Et juste pour cela, j'ai ajouté ObjectMapper. De même, nous ajoutons PlacesClient et 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;
}
et
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>>() {
       });
   }
}
où PlacesDto a la forme :
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;
}
Et enfin, un service client qui, sur la base des données nécessaires, renverra le prix minimum d'un vol et toutes les informations nécessaires : FlightPriceClient et FlightPriceClientImpl. Nous n’implémenterons qu’une seule requête BrowseQuotes. ClientPrixVol :
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);
}
PrixVolClientImpl
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;
   }
}
où FlightClientException ressemble à :
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;
}
En conséquence, selon les données de PlacesCl
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION