Add CommonError Module
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
package com.mangkyu.employment.interview.app.common.erros.errorcode;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum CommonErrorCode implements ErrorCode {
|
||||
|
||||
INVALID_PARAMETER(HttpStatus.BAD_REQUEST, "Invalid parameter included"),
|
||||
RESOURCE_NOT_FOUND(HttpStatus.NOT_FOUND, "Resource not exists"),
|
||||
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "Internal server error"),
|
||||
;
|
||||
|
||||
private final HttpStatus httpStatus;
|
||||
private final String message;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.mangkyu.employment.interview.app.common.erros.errorcode;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum CustomErrorCode implements ErrorCode {
|
||||
|
||||
INVALID_PARAMETER(HttpStatus.BAD_REQUEST, "Invalid parameter included"),
|
||||
RESOURCE_NOT_FOUND(HttpStatus.NOT_FOUND, "Cannot find resource."),
|
||||
;
|
||||
|
||||
private final HttpStatus httpStatus;
|
||||
private final String message;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.mangkyu.employment.interview.app.common.erros.errorcode;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public interface ErrorCode {
|
||||
|
||||
String name();
|
||||
|
||||
HttpStatus getHttpStatus();
|
||||
|
||||
String getMessage();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.mangkyu.employment.interview.app.common.erros.exception;
|
||||
|
||||
|
||||
import com.mangkyu.employment.interview.app.common.erros.errorcode.ErrorCode;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public class QuizException extends Exception {
|
||||
|
||||
private final ErrorCode errorCode;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.mangkyu.employment.interview.app.common.erros.handler;
|
||||
|
||||
import com.mangkyu.employment.interview.app.common.erros.errorcode.CommonErrorCode;
|
||||
import com.mangkyu.employment.interview.app.common.erros.errorcode.ErrorCode;
|
||||
import com.mangkyu.employment.interview.app.common.erros.response.ErrorResponse;
|
||||
import com.mangkyu.employment.interview.app.common.erros.exception.QuizException;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
|
||||
|
||||
@ExceptionHandler(QuizException.class)
|
||||
public ResponseEntity<Object> handleQuizException(final QuizException e) {
|
||||
final ErrorCode errorCode = e.getErrorCode();
|
||||
return handleExceptionInternal(errorCode);
|
||||
}
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public ResponseEntity<Object> handleIllegalArgument(final IllegalArgumentException e) {
|
||||
final ErrorCode errorCode = CommonErrorCode.INVALID_PARAMETER;
|
||||
return handleExceptionInternal(errorCode, e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Object> handleMethodArgumentNotValid(
|
||||
final MethodArgumentNotValidException e,
|
||||
final HttpHeaders headers,
|
||||
final HttpStatus status,
|
||||
final WebRequest request) {
|
||||
|
||||
final ErrorCode errorCode = CommonErrorCode.INVALID_PARAMETER;
|
||||
return handleExceptionInternal(e, errorCode);
|
||||
}
|
||||
|
||||
// @ExceptionHandler({ConstraintViolationException.class})
|
||||
// public ResponseEntity<Object> handleConstraintViolation(final ConstraintViolationException e) {
|
||||
// final ErrorCode errorCode = CommonErrorCode.INVALID_PARAMETER;
|
||||
// return handleExceptionInternal(e, errorCode);
|
||||
// }
|
||||
|
||||
@ExceptionHandler({Exception.class})
|
||||
public ResponseEntity<Object> handleAllException(final Exception ex) {
|
||||
final ErrorCode errorCode = CommonErrorCode.INTERNAL_SERVER_ERROR;
|
||||
return handleExceptionInternal(errorCode);
|
||||
}
|
||||
|
||||
private ResponseEntity<Object> handleExceptionInternal(final ErrorCode errorCode) {
|
||||
return ResponseEntity.status(errorCode.getHttpStatus())
|
||||
.body(makeErrorResponse(errorCode));
|
||||
}
|
||||
|
||||
private ErrorResponse makeErrorResponse(final ErrorCode errorCode) {
|
||||
return ErrorResponse.builder()
|
||||
.code(errorCode.name())
|
||||
.message(errorCode.getMessage())
|
||||
.build();
|
||||
}
|
||||
|
||||
private ResponseEntity<Object> handleExceptionInternal(final ErrorCode errorCode, final String message) {
|
||||
return ResponseEntity.status(errorCode.getHttpStatus())
|
||||
.body(makeErrorResponse(errorCode, message));
|
||||
}
|
||||
|
||||
private ErrorResponse makeErrorResponse(final ErrorCode errorCode, final String message) {
|
||||
return ErrorResponse.builder()
|
||||
.code(errorCode.name())
|
||||
.message(message)
|
||||
.build();
|
||||
}
|
||||
|
||||
private ResponseEntity<Object> handleExceptionInternal(final BindException e, final ErrorCode errorCode) {
|
||||
return ResponseEntity.status(errorCode.getHttpStatus())
|
||||
.body(makeErrorResponse(e, errorCode));
|
||||
}
|
||||
|
||||
private ErrorResponse makeErrorResponse(final BindException e, final ErrorCode errorCode) {
|
||||
final List<ErrorResponse.ValidationError> validationErrorList = e.getBindingResult()
|
||||
.getFieldErrors()
|
||||
.stream()
|
||||
.map(ErrorResponse.ValidationError::of)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return ErrorResponse.builder()
|
||||
.code(errorCode.name())
|
||||
.message(errorCode.getMessage())
|
||||
.errors(validationErrorList)
|
||||
.build();
|
||||
}
|
||||
|
||||
// private ResponseEntity<Object> handleExceptionInternal(final ConstraintViolationException e, final ErrorCode errorCode) {
|
||||
// return ResponseEntity.status(errorCode.getHttpStatus())
|
||||
// .body(makeErrorResponse(e, errorCode));
|
||||
// }
|
||||
|
||||
// private ErrorResponse makeErrorResponse(final ConstraintViolationException e, final ErrorCode errorCode) {
|
||||
// final List<ErrorResponse.ValidationError> validationErrorList = e.getConstraintViolations()
|
||||
// .stream()
|
||||
// .map(ErrorResponse.ValidationError::of)
|
||||
// .collect(Collectors.toList());
|
||||
//
|
||||
// return ErrorResponse.builder()
|
||||
// .code(errorCode.name())
|
||||
// .message(errorCode.getMessage())
|
||||
// .errors(validationErrorList)
|
||||
// .build();
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.mangkyu.employment.interview.app.common.erros.response;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.FieldError;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
@RequiredArgsConstructor
|
||||
public class ErrorResponse {
|
||||
|
||||
private final String code;
|
||||
private final String message;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private final List<ValidationError> errors;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
@RequiredArgsConstructor
|
||||
public static class ValidationError {
|
||||
private final String field;
|
||||
private final String message;
|
||||
|
||||
public static ValidationError of(final FieldError fieldError) {
|
||||
return ValidationError.builder()
|
||||
.field(fieldError.getField())
|
||||
.message(fieldError.getDefaultMessage())
|
||||
.build();
|
||||
}
|
||||
|
||||
// public static ValidationError of(final ConstraintViolation<?> violation) {
|
||||
// final String propertyPath = violation.getPropertyPath().toString();
|
||||
//
|
||||
// return ValidationError.builder()
|
||||
// .field(StringUtils.substringAfterLast(propertyPath, "."))
|
||||
// .message(violation.getMessage())
|
||||
// .build();
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package com.mangkyu.employment.interview.app.common.erros.handler;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.mangkyu.employment.interview.app.common.erros.errorcode.CommonErrorCode;
|
||||
import com.mangkyu.employment.interview.app.common.erros.errorcode.ErrorCode;
|
||||
import com.mangkyu.employment.interview.app.common.erros.response.ErrorResponse;
|
||||
import com.mangkyu.employment.interview.app.common.erros.exception.QuizException;
|
||||
import com.mangkyu.employment.interview.app.quiz.controller.QuizController;
|
||||
import com.mangkyu.employment.interview.app.quiz.dto.AddQuizRequest;
|
||||
import com.mangkyu.employment.interview.app.quiz.service.QuizService;
|
||||
import com.mangkyu.employment.interview.enums.value.QuizLevel;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.ResultActions;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class GlobalExceptionHandlerTest {
|
||||
|
||||
@InjectMocks
|
||||
private QuizController target;
|
||||
|
||||
@Mock
|
||||
private QuizService quizService;
|
||||
|
||||
private MockMvc mockMvc;
|
||||
@Spy
|
||||
private Validator validator = new LocalValidatorFactoryBean();
|
||||
|
||||
@BeforeEach
|
||||
public void init() {
|
||||
mockMvc = MockMvcBuilders.standaloneSetup(target)
|
||||
.setControllerAdvice(new GlobalExceptionHandler())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleQuizException() throws Exception {
|
||||
// given
|
||||
final long quizId = -1;
|
||||
final String url = "/quiz/" + quizId;
|
||||
final ErrorCode errorCode = CommonErrorCode.RESOURCE_NOT_FOUND;
|
||||
|
||||
doThrow(new QuizException(errorCode)).when(quizService).getQuiz(quizId);
|
||||
|
||||
// when
|
||||
final ResultActions result = mockMvc.perform(
|
||||
MockMvcRequestBuilders.get(url)
|
||||
);
|
||||
|
||||
// then
|
||||
final ResultActions resultActions = result.andExpect(status().isNotFound());
|
||||
final String stringResponse = resultActions.andReturn().getResponse().getContentAsString();
|
||||
final ErrorResponse errorResponse = new Gson().fromJson(stringResponse, ErrorResponse.class);
|
||||
|
||||
|
||||
assertThat(errorResponse.getCode()).isEqualTo(errorCode.name());
|
||||
assertThat(errorResponse.getMessage()).isEqualTo(errorCode.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleIllegalArgument() throws Exception {
|
||||
// given
|
||||
final long quizId = -1;
|
||||
final String url = "/quiz/" + quizId;
|
||||
final String message = "message";
|
||||
|
||||
doThrow(new IllegalArgumentException(message)).when(quizService).getQuiz(quizId);
|
||||
|
||||
// when
|
||||
final ResultActions result = mockMvc.perform(
|
||||
MockMvcRequestBuilders.get(url)
|
||||
);
|
||||
|
||||
// then
|
||||
final ResultActions resultActions = result.andExpect(status().isBadRequest());
|
||||
final String stringResponse = resultActions.andReturn().getResponse().getContentAsString();
|
||||
final ErrorResponse errorResponse = new Gson().fromJson(stringResponse, ErrorResponse.class);
|
||||
|
||||
|
||||
assertThat(errorResponse.getCode()).isEqualTo(CommonErrorCode.INVALID_PARAMETER.name());
|
||||
assertThat(errorResponse.getMessage()).isEqualTo(message);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleMethodArgumentNotValid() throws Exception {
|
||||
// given
|
||||
final String url = "/quiz";
|
||||
final ErrorCode errorCode = CommonErrorCode.INVALID_PARAMETER;
|
||||
|
||||
final AddQuizRequest addQuizRequest = AddQuizRequest.builder()
|
||||
.title("Title")
|
||||
.quizLevel(Collections.singletonList(QuizLevel.NEW))
|
||||
.build();
|
||||
|
||||
// when
|
||||
final ResultActions result = mockMvc.perform(
|
||||
MockMvcRequestBuilders.post(url)
|
||||
.content(new Gson().toJson(addQuizRequest))
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
);
|
||||
|
||||
// then
|
||||
final ResultActions resultActions = result.andExpect(status().isBadRequest());
|
||||
final String stringResponse = resultActions.andReturn().getResponse().getContentAsString();
|
||||
final ErrorResponse errorResponse = new Gson().fromJson(stringResponse, ErrorResponse.class);
|
||||
|
||||
assertThat(errorResponse.getCode()).isEqualTo(errorCode.name());
|
||||
assertThat(errorResponse.getMessage()).isEqualTo(errorCode.getMessage());
|
||||
assertThat(errorResponse.getErrors().size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user