JavaRush /Java-Blog /Random-DE /Erstellen eines Systems zur Überwachung der Flugticketpre...

Erstellen eines Systems zur Überwachung der Flugticketpreise: eine Schritt-für-Schritt-Anleitung [Teil 1]

Veröffentlicht in der Gruppe Random-DE

Inhalt:

Erstellen eines Systems zur Überwachung von Flugticketpreisen: eine Schritt-für-Schritt-Anleitung [Teil 1] - 1Hallo zusammen, JavaRush-Community! Heute werden wir Schritt für Schritt darüber sprechen, wie man eine Spring Boot-Anwendung zur Überwachung von Flugticketpreisen schreibt. Der Artikel richtet sich an Personen, die eine Idee haben zu:
  • REST und wie REST-Endpunkte erstellt werden;
  • relationale Datenbanken;
  • die Arbeit von Maven (insbesondere was ist Abhängigkeit);
  • JSON-Objekt;
  • Protokollierungsprinzipien.
Erwartetes Verhalten:
  1. Sie können einen Flug für ein bestimmtes Datum auswählen und den Preis dafür verfolgen. Der Benutzer wird anhand seiner E-Mail-Adresse identifiziert. Sobald eine Anmeldung zu einer Preisänderung erfolgt, erhält der Nutzer eine Benachrichtigung per E-Mail.
  2. Alle 30 Minuten (dieses Intervall wird über application.properties konfiguriert) wird der Mindestpreis für einen Flug für alle Abonnements neu berechnet. Sollte einer der Werte niedriger geworden sein, erhält der Nutzer eine Benachrichtigung per E-Mail.
  3. Alle Abonnements mit veraltetem Flugdatum werden gelöscht.
  4. Über die REST-API können Sie:
    • ein Abonnement erstellen;
    • bearbeiten;
    • alle Abonnements per E-Mail erhalten;
    • Abonnement löschen.

Aktionsplan zur Erreichung des Ziels

Sie müssen damit beginnen, dass Informationen zu Flügen von irgendwoher stammen müssen. Normalerweise stellen Websites eine offene REST-API bereit, über die Informationen abgerufen werden können.

API (Application Programming Interface) ist eine Schnittstelle, über die Sie mit einer Anwendung interagieren können. Daraus können wir eine Brücke zu dem schlagen, was eine REST-API ist.

Eine REST-API ist eine Schnittstelle von REST-Anfragen, die zur Kommunikation mit einer Webanwendung verwendet werden kann.

Dazu verwenden wir Skyscanner bzw. die API (auf der Rakuten-API- Website ). Als nächstes müssen Sie das richtige Framework als Grundlage auswählen. Am beliebtesten und gefragtesten ist das Spring-Ökosystem und die Krone ihrer Kreation – Spring Boot. Sie können ihre offizielle Website besuchen oder den Artikel über Habré lesen . Zum Speichern von Benutzerabonnements verwenden wir die integrierte H2- Datenbank . Um von JSON in Klassen und zurück zu lesen, verwenden wir das Jackson-Projekt ( hier ist der Link zu unserer Ressource ). Wir verwenden Spring-Boot-Starter-Mail , um Nachrichten an Benutzer zu senden . Damit die Anwendung den Mindestpreis in einer bestimmten Häufigkeit neu berechnen kann, verwenden wir Spring Scheduler . Um eine REST-API zu erstellen, verwenden wir spring-boot-starter-web . Um keinen geliehenen Code zu schreiben (Getter, Setter, Override-Equals und Hashcode, toString() für Objekte), verwenden wir Project Lombok . Um die REST-API zu spüren und zu sehen, verwenden wir Swagger 2 und sofort Swagger UI (Benutzeroberfläche) für die Echtzeitverfolgung. So sieht es jetzt aus: Erstellen eines Systems zur Überwachung der Flugticketpreise: eine Schritt-für-Schritt-Anleitung [Teil 1] - 2Es gibt vier weitere Abfragen, die dem Erstellen, Bearbeiten, Abrufen und Löschen von Abonnements entsprechen.

Entdecken Sie die Skyscanner-API

Folgen wir dem Link zur Rakuten-API . Zuerst müssen Sie sich registrieren. Erstellen eines Systems zur Überwachung der Flugticketpreise: eine Schritt-für-Schritt-Anleitung [Teil 1] – 3All dies ist erforderlich, um einen eindeutigen Schlüssel zur Nutzung ihrer Website zu erhalten und Anfragen an die darauf veröffentlichten öffentlichen APIs zu stellen. Eine dieser APIs ist die Skyscanner-Flugsuche, die wir benötigen . Lassen Sie uns nun herausfinden, wie es funktioniert. Suchen wir nach der Anfrage „GET List Places“. Das Bild zeigt, dass Sie die Daten ausfüllen und Test Endpoint starten müssen , woraufhin wir rechts eine Antwort in Form eines JSON-Objekts erhalten: Erstellen eines Systems zur Überwachung von Flugticketpreisen: eine Schritt-für-Schritt-Anleitung [Teil 1] - 4und die Anfrage wird wie folgt erstellt:

https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com/apiservices/autosuggest/v1.0/{country}/{currency}/{locale}/?query={query}
und alle Parameter werden in diese Formel eingesetzt, wir erhalten:

https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com/apiservices/autosuggest/v1.0/UK/GBP/en-GB/?query=Stockholm
und an diese Anfragen werden zwei Header übergeben:

.header("x-rapidapi-host", "skyscanner-skyscanner-flight-search-v1.p.rapidapi.com")
.header("x-rapidapi-key", "sing-up-for-key"),
wo sign-up-for-keyes nach der Registrierung ausgestellt wird. Um den Preisverfall zu verfolgen, benötigen wir den Endpunkt „Browse Quotes“. Finden Sie es selbst :)

Erstellen eines Anwendungsframeworks basierend auf Spring Boot

Um schnell und einfach ein Projekt mit Spring Boot zu erstellen, können Sie Spring Initializr verwenden . Wählen Sie die folgenden Optionen:
  1. Maven-Projekt
  2. Java
  3. 2.1.10
  4. Gruppe – was auch immer Sie für notwendig halten, zum Beispiel ru.javarush
  5. Artefakt - genau das Gleiche, zum Beispiel Flugüberwachung
  6. Bei der Abhängigkeitssuche suchen wir nach Folgendem:
    • Frühlingsnetz
    • Java-Mail-Absender
    • Spring Data Jpa
    • H2-Datenbank
Klicken Sie dann auf „Generieren“ . Das war's: Das fertige Projekt wird als Archiv heruntergeladen. Wenn etwas nicht klappt, kannst du den Link verwenden, wo ich das gewünschte Projekt gespeichert habe . Natürlich ist es besser, dies selbst zu tun und zu verstehen, wie es funktioniert. Die Bewerbung besteht aus drei Ebenen:
  • CONTROLLER – Melden Sie sich bei der Anwendung an. Die REST-API wird hier beschrieben
  • SERVICE ist eine Geschäftslogikschicht. Hier wird die gesamte Logik der Anwendung beschrieben.
  • REPOSITORY – Ebene für die Arbeit mit der Datenbank.
Außerdem werden Klassen, die sich auf den Client für die Skyscanner-Flugsuch-API beziehen, ein separates Paket sein.

Wir schreiben im Projekt einen Client für Anfragen an die Skyscanner Flight Search API

Skyscanner hat freundlicherweise einen Artikel zur Verwendung seiner API bereitgestellt (wir erstellen keine Sitzung mit einer aktiven Anfrage). Was bedeutet es, „einem Kunden zu schreiben“? Wir müssen eine Anfrage an eine bestimmte URL mit bestimmten Parametern erstellen und ein DTO (Data Transfer Object) für die an uns zurückübertragenen Daten vorbereiten. Auf der Website gibt es vier Gruppen von Anfragen:
  1. Live-Flugsuche – wir halten es im Moment nicht für unnötig.
  2. Orte – lasst uns schreiben.
  3. Flugpreise durchsuchen – wir verwenden eine Anfrage, in der Sie alle Informationen erhalten.
  4. Lokalisierung – fügen wir sie hinzu, damit wir wissen, welche Daten unterstützt werden.

Erstellen Sie einen Client-Service für die Lokalisierungsanfrage:

Der Plan ist so einfach wie eine gedünstete Rübe: Erstellen Sie eine Anfrage, schauen Sie sich die Parameter an, schauen Sie sich die Antwort an. Es gibt zwei Abfragen: Listenmarkierungen und Währungen. Beginnen wir mit Währungen. Die Abbildung zeigt, dass es sich um eine Anfrage ohne zusätzliche Felder handelt: Sie wird benötigt, um Informationen über unterstützte Währungen zu erhalten: Erstellen eines Systems zur Überwachung der Flugticketpreise: eine Schritt-für-Schritt-Anleitung [Teil 1] – 6Die Antwort erfolgt in Form eines JSON-Objekts, das eine Sammlung derselben Objekte enthält, zum Beispiel:
{
"Code":"LYD"
"Symbol":"د.ل.‏"
"ThousandsSeparator":","
"DecimalSeparator":"."
"SymbolOnLeft":true
"SpaceBetweenAmountAndSymbol":false
"RoundingCoefficient":0
"DecimalDigits":3
}
Erstellen wir ein „CurrencyDto“ für dieses Objekt:
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;
}
Wo:
  • @Data ist eine Annotation aus dem Lombok-Projekt und generiert alle Getter-, Setter- und Override-Methoden toString(), equal() und hashCode(). Was verbessert die Lesbarkeit des Codes und beschleunigt das Schreiben von POJO- Objekten?
  • @JsonProperty("Code") ist eine Anmerkung aus dem Jackson-Projekt, die angibt, welches Feld dieser Variablen zugewiesen wird. Das heißt, der Codevariablen wird ein Feld in JSON zugewiesen, das Code entspricht .
Der offizielle Artikel von Skyscanner schlägt vor, die UniRest- Bibliothek für REST-Anfragen zu verwenden . Daher werden wir einen weiteren Dienst schreiben, der Anfragen über REST umsetzt. Dies wird UniRestService sein . Fügen Sie dazu eine neue Abhängigkeit zu Maven hinzu:
<dependency>
  <groupId>com.mashape.unirest</groupId>
  <artifactId>unirest-java</artifactId>
  <version>1.4.9</version>
</dependency>
Als Nächstes schreiben wir einen Dienst, der REST-Anfragen ausführt. Selbstverständlich erstellen wir für jeden Kunden/Dienst eine Schnittstelle und deren Implementierung:
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);

}
Und seine Umsetzung:
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;
   }
}
Sein Kern besteht darin, dass alle Anfragen, an denen wir interessiert sind, für GET-Anfragen erstellt werden und dieser Dienst eine vorgefertigte Anfrage akzeptiert und die erforderlichen Header hinzufügt wie:
.header("x-rapidapi-host", "skyscanner-skyscanner-flight-search-v1.p.rapidapi.com")
.header("x-rapidapi-key", xRapidApiKey)
Um Daten aus Eigenschaften zu übernehmen, verwenden Sie die Annotation @Value wie unten gezeigt:
@Value("${x.rapid.api.key}")
private String xRapidApiKey;
Darin heißt es, dass es in application.properties eine Eigenschaft namens x.rapid.api.key geben wird, die in diese Variable eingefügt werden muss. Wir entfernen uns von fest codierten Werten und leiten die Definition dieser Variablen aus dem Programmcode ab. Wenn ich diese Anwendung auf GitHub veröffentliche, füge ich außerdem nicht den Wert dieser Eigenschaft hinzu. Dies erfolgt aus Sicherheitsgründen. Wir haben einen Dienst geschrieben, der mit REST-Anfragen funktioniert. Jetzt ist es Zeit für einen Dienst zur Lokalisierung. Wir erstellen eine auf OOP basierende Anwendung, also erstellen wir die LocalizationClient- Schnittstelle und ihre Implementierung 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();

}
und Implementierung von 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>>() {
       });
   }
}
Wo
  • @Autowired ist eine Annotation, die besagt, dass Sie ein Objekt in diese Klasse einfügen und verwenden müssen, ohne es zu erstellen, d. h. ohne die neue Objektoperation;
  • @Component ist eine Annotation, die besagt, dass dieses Objekt zum Anwendungskontext hinzugefügt werden muss, damit es später mithilfe der Annotation @Autowired eingefügt werden kann.
  • ObjectMapper objectMapper ist ein Objekt aus dem Jackson-Projekt, das all dies in Java-Objekte übersetzt.
  • WährungDTO und 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;
}
Um einen ObjectMapper in einen beliebigen Teil des Projekts einzufügen, habe ich das Erstellen und Hinzufügen zum ApplicationContext über eine Konfigurationsklasse hinzugefügt.
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;
   }
}
Die Annotation @Configuration teilt Spring mit, dass es in dieser Klasse einige Konfigurationen geben wird. Und genau dafür habe ich ObjectMapper hinzugefügt. Ebenso fügen wir PlacesClient und PlacesClientImpl hinzu:
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;
}
Und
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>>() {
       });
   }
}
wobei PlacesDto die Form hat:
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;
}
Und schließlich ein Kundenservice, der auf der Grundlage der erforderlichen Daten den Mindestpreis für einen Flug und alle erforderlichen Informationen zurückgibt: FlightPriceClient und FlightPriceClientImpl. Wir werden nur eine Anfrage implementieren, browseQuotes. FlightPriceClient:
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);
}
FlightPriceClientImpl
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;
   }
}
wobei FlightClientException wie folgt aussieht:
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;
}
Nach Angaben von PlacesCl
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION