Add CommonError Module

This commit is contained in:
MangKyu
2022-01-02 22:47:22 +09:00
parent 834da77249
commit 241cfe41b5
7 changed files with 358 additions and 0 deletions

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -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();
// }
}

View File

@@ -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();
// }
}
}

View File

@@ -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);
}
}