JavaRush /Java Blog /Random-KO /항공권 가격 모니터링 시스템 구축: 단계별 가이드 [1부]
Roman Beekeeper
레벨 35

항공권 가격 모니터링 시스템 구축: 단계별 가이드 [1부]

Random-KO 그룹에 게시되었습니다

콘텐츠:

항공권 가격 모니터링 시스템 구축 단계별 안내 [1부] - 1안녕하세요, JavaRush 커뮤니티 여러분! 오늘은 항공권 가격을 단계별로 모니터링하기 위한 Spring Boot 애플리케이션을 작성하는 방법에 대해 이야기하겠습니다. 이 기사는 다음에 대한 아이디어가 있는 사람들을 대상으로 작성되었습니다.
  • REST 및 REST 엔드포인트 구축 방법
  • 관계형 데이터베이스;
  • Maven의 작업(특히 종속성이란 무엇입니까?)
  • JSON 객체;
  • 로깅 원칙.
예상되는 동작:
  1. 특정 날짜의 항공편을 선택하고 가격을 추적할 수 있습니다. 사용자는 이메일 주소로 식별됩니다. 가격 변경을 구독하는 즉시 사용자는 이메일로 알림을 받습니다.
  2. 30분마다(이 간격은 application.properties를 통해 구성됨) 모든 구독에 대해 항공편의 최소 가격이 다시 계산됩니다. 값 중 하나가 낮아지면 사용자는 이메일로 알림을 받게 됩니다.
  3. 비행 날짜가 오래된 모든 구독은 삭제됩니다.
  4. REST API를 통해 다음을 수행할 수 있습니다.
    • 구독을 생성합니다.
    • 편집하다;
    • 이메일로 모든 구독을 수신합니다.
    • 구독 삭제.

목표 달성을 위한 실천 계획

항공편에 대한 정보를 어딘가에서 가져와야 한다는 사실부터 시작해야 합니다. 일반적으로 웹사이트는 정보를 검색할 수 있는 개방형 REST API를 제공합니다.

API(애플리케이션 프로그래밍 인터페이스)는 애플리케이션과 상호 작용할 수 있는 인터페이스입니다. 이를 통해 REST API가 무엇인지에 대한 연결을 구축할 수 있습니다.

REST API는 웹 애플리케이션과 통신하는 데 사용할 수 있는 REST 요청의 인터페이스입니다.

이를 위해 Skyscanner 또는 API(Rakuten API 웹사이트 )를 사용합니다. 다음으로, 기본 기반으로 적합한 프레임워크를 선택해야 합니다. 가장 인기 있고 수요가 많은 것은 Spring 생태계와 그 창작의 정점인 Spring Boot입니다. 공식 웹사이트를 방문하거나 Habré의 기사를 읽어보세요 . 사용자 구독을 저장하기 위해 내장된 H2 데이터베이스를 사용합니다 . JSON에서 클래스까지 읽으려면 Jackson 프로젝트를 사용합니다( 여기 리소스에 대한 링크가 있습니다 ). 우리는 사용자에게 메시지를 보내기 위해 spring-boot-starter-mail을 사용할 것이고 , 애플리케이션이 주어진 빈도로 최소 가격을 다시 계산하도록 하기 위해 Spring Scheduler를 사용할 것입니다 . REST API를 생성하기 위해 spring-boot-starter-web을 사용할 것입니다 . 빌린 코드 (getter, setter, override equals 및 hashcode, 객체용 toString())를 작성하지 않기 위해 Project Lombok을 사용합니다 . REST API를 느끼고 보기 위해 Swagger 2를 사용하고 실시간 추적을 위해 즉시 Swagger UI(사용자 인터페이스)를 사용하겠습니다. 현재 모습은 다음과 같습니다. 항공권 가격 모니터링 시스템 구축 단계별 안내 [1부] - 2여기에는 구독 생성, 편집, 가져오기 및 삭제에 해당하는 4개의 나머지 쿼리가 있습니다.

Skyscanner API 탐색

rakuten api 링크를 따라가 보겠습니다 . 먼저 등록해야 합니다. 항공권 가격 모니터링 시스템 구축 단계별 안내 [1부] - 3사이트를 사용하고 사이트에 게시된 공개 API에 요청하기 위한 고유 키를 받으려면 이 모든 것이 필요합니다. 이러한 API 중 하나는 우리에게 필요한 Skyscanner Flight Search 입니다 . 이제 그것이 어떻게 작동하는지 알아 봅시다. GET List Places 요청을 찾아보겠습니다. 그림은 데이터를 입력하고 Test Endpoint 를 시작해야 함을 보여줍니다 . 그 결과 오른쪽에 JSON 개체 형식으로 응답이 수신됩니다. 항공권 가격 모니터링 시스템 구축: 단계별 가이드 [1부] - 4요청은 다음과 같이 생성됩니다.

https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com/apiservices/autosuggest/v1.0/{country}/{currency}/{locale}/?query={query}
모든 매개변수는 이 공식으로 대체되며 다음과 같은 결과를 얻습니다.

https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com/apiservices/autosuggest/v1.0/UK/GBP/en-GB/?query=Stockholm
두 개의 헤더가 다음 요청에 전달됩니다.

.header("x-rapidapi-host", "skyscanner-skyscanner-flight-search-v1.p.rapidapi.com")
.header("x-rapidapi-key", "sing-up-for-key"),
sign-up-for-key등록 후 발급되는 곳 . 가격 하락을 추적하려면 Browse Quotes 엔드포인트가 필요합니다 . 직접 찾아보세요 :)

Spring Boot를 기반으로 애플리케이션 프레임워크 만들기

Spring Boot로 프로젝트를 빠르고 쉽게 생성하려면 Spring 초기화를 사용할 수 있습니다 . 다음 옵션을 선택하세요.
  1. 메이븐 프로젝트
  2. 자바
  3. 2.1.10
  4. 그룹 - 필요하다고 생각하는 것(예: ru.javarush)
  5. 아티팩트 - 정확히 동일합니다(예: 항공편 모니터링).
  6. 종속성 검색에서 다음을 찾습니다.
    • 스프링 웹
    • 자바 메일 발신자
    • 스프링 데이터 Jpa
    • H2 데이터베이스
그런 다음 생성을 클릭합니다 . 그게 전부입니다. 완성된 프로젝트가 아카이브로 다운로드됩니다. 문제가 해결되지 않으면 내가 원하는 프로젝트를 저장한 링크를 사용할 수 있습니다 . 물론 직접 해보고 작동 방식을 이해하는 것이 더 좋습니다. 애플리케이션은 세 가지 레이어로 구성됩니다.
  • 컨트롤러 - 애플리케이션에 로그인합니다. REST API는 여기에 설명됩니다.
  • SERVICE는 비즈니스 로직 계층입니다. 여기에서는 애플리케이션의 전체 논리를 설명합니다.
  • REPOSITORY - 데이터베이스 작업을 위한 레이어입니다.
또한 Skyscanner Flight Search API용 클라이언트와 관련된 클래스는 별도의 패키지로 제공됩니다.

프로젝트에서 Skyscanner Flight Search API에 대한 요청을 위한 클라이언트를 작성 중입니다.

Skyscanner는 API 사용 방법에 대한 기사를 친절하게 제공했습니다 (활성 요청이 있는 세션은 생성하지 않습니다). "클라이언트 작성"이란 무엇을 의미합니까? 특정 매개변수를 사용하여 특정 URL에 대한 요청을 생성하고 다시 전송되는 데이터에 대한 DTO(데이터 전송 개체)를 준비해야 합니다. 사이트에는 네 가지 요청 그룹이 있습니다.
  1. 실시간 항공편 검색 - 현재로서는 불필요하다고 생각하지 않습니다.
  2. 장소 - 글을 쓰자.
  3. 항공편 가격 검색 - 모든 정보를 얻을 수 있는 단일 요청을 사용합니다.
  4. 현지화 - 어떤 데이터가 지원되는지 알 수 있도록 추가해 보겠습니다.

현지화 요청을 위한 클라이언트 서비스를 만듭니다.

계획은 찐 순무처럼 간단합니다. 요청을 만들고, 매개변수를 살펴보고, 응답을 살펴보세요. 목록 표시와 통화라는 두 가지 쿼리가 있습니다. 통화부터 시작해 보겠습니다. 그림은 추가 필드가 없는 요청임을 보여줍니다. 지원되는 통화에 대한 정보를 가져오는 데 필요합니다. 항공권 가격 모니터링 시스템 구축 단계별 가이드 [1부] - 6응답은 동일한 개체의 컬렉션을 포함하는 JSON 개체 형식입니다. 예를 들면 다음과 같습니다.
{
"Code":"LYD"
"Symbol":"د.ل.‏"
"ThousandsSeparator":","
"DecimalSeparator":"."
"SymbolOnLeft":true
"SpaceBetweenAmountAndSymbol":false
"RoundingCoefficient":0
"DecimalDigits":3
}
이 객체에 대한 CurrentDto를 생성해 보겠습니다.
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;
}
어디:
  • @Data는 Lombok 프로젝트 의 주석 이며 모든 getter, setter, 재정의 toString(), equals() 및 hashCode() 메서드를 생성합니다. 코드 가독성을 향상시키고 POJO 객체 작성 시간을 단축하는 방법
  • @JsonProperty("Code")는 이 변수에 할당될 필드를 알려주는 Jackson 프로젝트의 주석입니다. 즉, Code와 동일한 JSON 필드가 코드 변수에 할당됩니다 .
Skyscanner의 공식 기사에서는 REST 요청에 UniRest 라이브러리를 사용할 것을 제안합니다 . 따라서 REST를 통해 요청을 구현하는 또 다른 서비스를 작성하겠습니다. 이는 UniRestService 입니다 . 이렇게 하려면 maven에 새 종속성을 추가하세요.
<dependency>
  <groupId>com.mashape.unirest</groupId>
  <artifactId>unirest-java</artifactId>
  <version>1.4.9</version>
</dependency>
다음으로 REST 요청을 수행하는 서비스를 작성하겠습니다. 물론 각 클라이언트/서비스에 대해 인터페이스와 구현을 생성합니다.
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);

}
구현은 다음과 같습니다.
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;
   }
}
그 본질은 우리가 관심 있는 모든 요청이 GET 요청에 대해 생성된다는 것입니다. 이 서비스는 기성 요청을 수락하고 다음과 같은 필수 헤더를 추가합니다.
.header("x-rapidapi-host", "skyscanner-skyscanner-flight-search-v1.p.rapidapi.com")
.header("x-rapidapi-key", xRapidApiKey)
속성에서 데이터를 가져오려면 아래와 같이 @Value 주석을 사용합니다.
@Value("${x.rapid.api.key}")
private String xRapidApiKey;
application.properties에는 이 변수에 주입되어야 하는 x.rapid.api.key라는 속성이 있을 것이라고 나와 있습니다. 우리는 하드코딩된 값을 제거하고 프로그램 코드에서 이 변수의 정의를 도출합니다. 또한 이 애플리케이션을 GitHub에 게시할 때 이 속성의 값을 추가하지 않습니다. 이는 보안상의 이유로 수행됩니다. REST 요청과 함께 작동하는 서비스를 작성했습니다. 이제 현지화 서비스를 작성할 차례입니다. 우리는 OOP를 기반으로 애플리케이션을 구축 중이므로 LocalizationClient 인터페이스 와 해당 구현 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();

}
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>>() {
       });
   }
}
어디
  • @Autowired는 이 클래스에 객체를 삽입하고 이를 생성하지 않고, 즉 새 객체 작업 없이 사용해야 함을 나타내는 주석입니다.
  • @Component는 나중에 @Autowired 주석을 사용하여 주입할 수 있도록 이 객체를 애플리케이션 컨텍스트에 추가해야 함을 나타내는 주석입니다.
  • ObjectMapper objectMapper는 이 모든 것을 Java 객체로 변환하는 Jackson 프로젝트의 객체입니다.
  • 통화DTO 및 국가Dto:
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;
}
프로젝트의 어느 부분에나 ObjectMapper를 삽입하기 위해 구성 클래스를 통해 ApplicationContext에 ObjectMapper를 생성하고 추가했습니다.
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;
   }
}
@Configuration 주석은 Spring에 이 클래스에 몇 가지 구성이 있을 것임을 알려줍니다. 그리고 이를 위해 ObjectMapper를 추가했습니다. 마찬가지로 PlacesClient 및 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;
}
그리고
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>>() {
       });
   }
}
여기서 PlacesDto의 형식은 다음과 같습니다.
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;
}
마지막으로 필요한 데이터를 기반으로 항공편의 최저 가격과 필요한 모든 정보를 반환하는 클라이언트 서비스인 FlightPriceClient 및 FlightPriceClientImpl이 있습니다. 우리는 하나의 요청인 browserQuotes만 구현하겠습니다. FlightPrice클라이언트:
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;
   }
}
FlightClientException은 다음과 같습니다.
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;
}
그 결과 PlacesCl의 데이터에 따르면
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION