Skip to content
This repository has been archived by the owner on Jan 15, 2024. It is now read-only.

Feature/#98 setup tourapi exception #99

Merged
merged 6 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ dependencies {
//javers
implementation 'org.javers:javers-core:7.2.0'

// xml jaxb
implementation 'jakarta.xml.bind:jakarta.xml.bind-api:+'


}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public Accommodation bringAccommodation(

var body = accommodationsSimpleSearchResponse.response().body();

if (body.totalCount() == 0) {
if (body.numOfRows() == 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아주 잘 캐치하셨네요!!

throw new NoAccommodationsFromAPIException();
}

Expand Down Expand Up @@ -59,7 +59,7 @@ public List<Accommodation> bringAccommodations(

var body = accommodationsSimpleSearchResponse.response().body();

if (body.totalCount() == 0) {
if (body.numOfRows() == 0) {
throw new NoAccommodationsFromAPIException();
}
var items = body.items().item();
Expand Down Expand Up @@ -103,7 +103,7 @@ public List<Room> bringRooms(long accommodationId) {

var body = roomTourAPIResponse.response().body();

if (body.totalCount() == 0) {
if (body.numOfRows() == 0) {
throw new NoRoomsFromAPIException();
}

Expand Down
35 changes: 29 additions & 6 deletions src/main/java/ybe/mini/travelserver/global/api/TourAPIUtils.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
package ybe.mini.travelserver.global.api;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Unmarshaller;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.util.StreamUtils;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.HttpMessageConverterExtractor;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.DefaultUriBuilderFactory;
import ybe.mini.travelserver.global.api.dto.AccommodationTourAPIResponse;
import ybe.mini.travelserver.global.api.dto.RoomTourAPIResponse;
import ybe.mini.travelserver.global.exception.api.WrongCallBackException;
import ybe.mini.travelserver.global.exception.api.TourAPIXMLErrorResponse;
import ybe.mini.travelserver.global.exception.api.WrongRequestException;
import ybe.mini.travelserver.global.exception.api.WrongXMLFormatException;

import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

Expand All @@ -31,6 +38,14 @@ public class TourAPIUtils {
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory();
factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE);
restTemplate.setUriTemplateHandler(factory);

ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
.featuresToEnable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)
.build();

MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(objectMapper);

restTemplate.getMessageConverters().add(0, converter);
}

private static StringBuilder buildCommonUrl(String endpoint) {
Expand Down Expand Up @@ -63,9 +78,17 @@ private static <T> T fetchDataFromAPI(
clientHttpResponse -> {
MediaType contentType = clientHttpResponse.getHeaders().getContentType();
if (contentType != null && contentType.includes(MediaType.TEXT_XML)) {
String body = StreamUtils.copyToString(clientHttpResponse.getBody(), Charset.defaultCharset());
log.error("공공포텅 오류 XML 반환 : {}", body);
throw new WrongCallBackException();
try {
JAXBContext jaxbContext = JAXBContext.newInstance(TourAPIXMLErrorResponse.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
TourAPIXMLErrorResponse tourAPIXMLErrorResponse = (TourAPIXMLErrorResponse) unmarshaller.unmarshal(clientHttpResponse.getBody());
log.error("공공포털 오류 XML 반환 : {}", tourAPIXMLErrorResponse);

String errorMessage = tourAPIXMLErrorResponse.getErrorHeader().getReturnAuthMsg();
throw new WrongRequestException(errorMessage);
} catch (JAXBException e) {
throw new WrongXMLFormatException(e);
}
}

return new HttpMessageConverterExtractor<>(responseType, restTemplate.getMessageConverters())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,38 @@
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import ybe.mini.travelserver.global.exception.ErrorMessage;

import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.SERVICE_UNAVAILABLE;
import static org.springframework.http.HttpStatus.*;

@Getter
@AllArgsConstructor
public enum TourAPIErrorMessage implements ErrorMessage {
NO_ACCOMMODATIONS_FROM_API(BAD_REQUEST, "API로부터 숙소를 가져오지 못했습니다."),
NO_ROOMS_FROM_API(BAD_REQUEST, "API로부터 객실을 가져오지 못했습니다."),
WRONG_CALLBACK(SERVICE_UNAVAILABLE, "잘못된 콜백입니다.");
// Tour API 숙박, 객실 비즈니스 오류
NO_ACCOMMODATIONS_FROM_API(NO_CONTENT, "숙소 결과가 반환된 것이 없습니다"),
NO_ROOMS_FROM_API(NO_CONTENT, "객실 결과가 반환된 것이 없습니다"),

// Tour API Keyword 검색 부분 오류
INVALID_REQUEST_PARAMETER_ERROR(BAD_REQUEST, "잘못된 요청 파라메터입니다."),
NO_MANDATORY_REQUEST_PARAMETERS_ERROR(BAD_REQUEST, "필수 요청 파라메터가 없습니다."),
TEMPORARILY_DISABLE_THE_SERVICEKEY_ERROR(FORBIDDEN, "일시적으로 사용할 수 없는 서비스 키입니다."),
UNSIGNED_CALL_ERROR(FORBIDDEN, "서명되지 않은 호출입니다."),
NODATA_ERROR(NOT_FOUND, "데이터가 없습니다."),
SERVICETIMEOUT_ERROR(GATEWAY_TIMEOUT, "서비스 연결 실패입니다."),
DB_ERROR(INTERNAL_SERVER_ERROR, "데이터베이스 에러입니다."),

// Tour API 공통 오류
APPLICATION_ERROR(INTERNAL_SERVER_ERROR, "Tour API 서버 어플리케이션 에러입니다"),
HTTP_ERROR(INTERNAL_SERVER_ERROR, "Tour API 서버 HTTP 에러입니다"),
NO_OPENAPI_SERVICE_ERROR(INTERNAL_SERVER_ERROR, "해당 Tour API 서비스가 없거나 폐기되었습니다"),
SERVICE_ACCESS_DENIED_ERROR(FORBIDDEN, "Tour API 서비스 접근이 거부되었습니다"),
LIMITED_NUMBER_OF_SERVICE_REQUESTS_EXCEEDS_ERROR(FORBIDDEN, "Tour API 서비스 요청 제한 횟수를 초과하였습니다"),
SERVICE_KEY_IS_NOT_REGISTERED_ERROR(FORBIDDEN, "등록되지 않은 Tour API 서비스 키입니다"),
DEADLINE_HAS_EXPIRED_ERROR(FORBIDDEN, "Tour API 서비스 활용 기간이 만료되었습니다"),
UNREGISTERED_IP_ERROR(FORBIDDEN, "등록되지 않은 Tour API 서비스 IP입니다"),
UNKNOWN_ERROR(INTERNAL_SERVER_ERROR, "Tour API 서버 알 수 없는 에러입니다");

private final HttpStatus status;
private final String message;
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package ybe.mini.travelserver.global.exception.api;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import ybe.mini.travelserver.global.exception.ProblemDetailCreator;

import static ybe.mini.travelserver.global.exception.api.TourAPIErrorMessage.*;


@RestControllerAdvice
public class TourAPIExceptionHandler extends ProblemDetailCreator<TourAPIErrorMessage> {
protected TourAPIExceptionHandler() {
Expand All @@ -14,12 +18,23 @@ protected TourAPIExceptionHandler() {

@ExceptionHandler(NoAccommodationsFromAPIException.class)
public ProblemDetail handleNotGatheredAccommodationsFromAPIException(HttpServletRequest request) {
return createProblemDetail(TourAPIErrorMessage.NO_ACCOMMODATIONS_FROM_API, request);
return createProblemDetail(NO_ACCOMMODATIONS_FROM_API, request);
}

@ExceptionHandler(NoRoomsFromAPIException.class)
public ProblemDetail handleNotGatheredRoomsFromAPIException(HttpServletRequest request) {
return createProblemDetail(TourAPIErrorMessage.NO_ROOMS_FROM_API, request);
return createProblemDetail(NO_ROOMS_FROM_API, request);
}

@ExceptionHandler(WrongXMLFormatException.class)
public ProblemDetail handleWrongXMLFormatException(
WrongXMLFormatException ex,
HttpServletRequest request) {
return createProblemDetail(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value(), request);
}

@ExceptionHandler(WrongRequestException.class)
public ProblemDetail handleWrongCallBackException(WrongRequestException ex, HttpServletRequest request) {
return createProblemDetail(valueOf(ex.getMessage()), request);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package ybe.mini.travelserver.global.exception.api;

import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
import lombok.Getter;

@Getter
@XmlRootElement(name = "OpenAPI_ServiceResponse")
public class TourAPIXMLErrorResponse {

@XmlElement(name = "cmmMsgHeader")
private ErrorHeader errorHeader;

@Getter
public static class ErrorHeader {

@XmlElement(name = "errMsg")
private String errMsg;

@XmlElement(name = "returnAuthMsg")
private String returnAuthMsg;

@XmlElement(name = "returnReasonCode")
private String returnReasonCode;

}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ybe.mini.travelserver.global.exception.api;

public class WrongRequestException extends RuntimeException {

public final String msg;

public WrongRequestException(String msg) {
this.msg = msg;
}

@Override
public String getMessage() {
return msg;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ybe.mini.travelserver.global.exception.api;

import jakarta.xml.bind.JAXBException;

public class WrongXMLFormatException extends RuntimeException {
public WrongXMLFormatException(JAXBException e) {
super(e);
}
}