Skip to content

Commit

Permalink
♻️ Refactor : refactoring errorCode by domain (#415)
Browse files Browse the repository at this point in the history
* ♻️ Refactor : refactoring errorCode by domain

* ✨ feat: change reflection to ClassPathScanningCandidateComponentProvider

* ✅ test : add errorcode duplication test
  • Loading branch information
seonghun-dev authored Jan 27, 2024
1 parent 05732d6 commit e469d5e
Show file tree
Hide file tree
Showing 56 changed files with 871 additions and 331 deletions.
2 changes: 1 addition & 1 deletion backend/streetdrop-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,4 @@ test {
tasks.named('test') {
useJUnitPlatform()
finalizedBy jacocoTestReport
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.depromeet.common.error;

import com.depromeet.common.error.dto.ErrorCode;
import com.depromeet.common.error.dto.ErrorResponseDto;
import com.depromeet.common.error.exception.common.BusinessException;
import com.depromeet.common.error.exception.common.NotFoundException;
import com.depromeet.common.error.handler.ErrorEvent;
import com.depromeet.common.error.dto.CommonErrorCode;
import com.depromeet.common.error.event.ErrorEvent;
import com.depromeet.common.error.exception.internal.BusinessException;
import com.depromeet.common.error.exception.internal.NotFoundException;
import com.depromeet.common.error.http.dto.HttpErrorResponseDto;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -15,6 +15,7 @@
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.client.HttpClientErrorException;

import java.sql.SQLException;

Expand All @@ -25,70 +26,60 @@ public class GlobalExceptionHandler {

private final ApplicationEventPublisher eventPublisher;

@ExceptionHandler(HttpClientErrorException.Forbidden.class)
protected ResponseEntity<HttpErrorResponseDto> handleForbiddenException(
final HttpClientErrorException.Forbidden e,
final HttpServletRequest request
) {
return HttpErrorResponseDto.toResponseEntity(CommonErrorCode.FORBIDDEN);
}

@ExceptionHandler(BusinessException.class)
protected ResponseEntity<ErrorResponseDto> handleBusinessException(
protected ResponseEntity<HttpErrorResponseDto> handleBusinessException(
final BusinessException e,
final HttpServletRequest request
) {
log.error("BusinessException: {} {}", e.getErrorCode(), request.getRequestURL());
return ResponseEntity
.status(e.getErrorCode().getStatus().value())
.body(new ErrorResponseDto(e.getErrorCode()));
return HttpErrorResponseDto.toResponseEntity(e.getErrorCode());
}

@ExceptionHandler(NotFoundException.class)
protected ResponseEntity<ErrorResponseDto> handleNotFoundException(
final NotFoundException e,
final HttpServletRequest request) {

log.error("MemberNotFoundException: {} {}", e.getErrorCode(), request.getRequestURL());
return ResponseEntity
.status(e.getErrorCode().getStatus().value())
.body(new ErrorResponseDto(e.getErrorCode()));
protected ResponseEntity<HttpErrorResponseDto> handleNotFoundException(
final NotFoundException e
) {
return HttpErrorResponseDto.toResponseEntity(e.getErrorCode());
}

@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<ErrorResponseDto> handleMethodArgumentNotValidException(
protected ResponseEntity<HttpErrorResponseDto> handleMethodArgumentNotValidException(
final MethodArgumentNotValidException e,
final HttpServletRequest request
) {
log.error("MethodArgumentNotValidException: {} {}", e.getMessage(), request.getRequestURL());
ErrorResponseDto errorResponseDto = ErrorResponseDto.builder()
.code(ErrorCode.METHOD_ARGUMENT_NOT_VALID.getCode())
.status(ErrorCode.METHOD_ARGUMENT_NOT_VALID.getStatus().value())
.error(ErrorCode.METHOD_ARGUMENT_NOT_VALID.getStatus().name())
HttpErrorResponseDto httpErrorResponseDto = HttpErrorResponseDto.builder()
.errorResponseCode(CommonErrorCode.METHOD_ARGUMENT_NOT_VALID.getErrorResponseCode())
.status(CommonErrorCode.METHOD_ARGUMENT_NOT_VALID.getStatus().value())
.title(CommonErrorCode.METHOD_ARGUMENT_NOT_VALID.getTitle())
.message(e.getAllErrors().get(0).getDefaultMessage()).build();

return ResponseEntity
.status(ErrorCode.METHOD_ARGUMENT_NOT_VALID.getStatus().value())
.body(errorResponseDto);
.status(CommonErrorCode.METHOD_ARGUMENT_NOT_VALID.getStatus().value())
.body(httpErrorResponseDto);
}

@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
protected ResponseEntity<ErrorResponseDto> handleMethodNotSupportException(
final HttpRequestMethodNotSupportedException e
) {
ErrorResponseDto errorResponseDto = ErrorResponseDto.builder()
.code(ErrorCode.METHOD_NOT_ALLOWED.getCode())
.status(ErrorCode.METHOD_NOT_ALLOWED.getStatus().value())
.error(ErrorCode.METHOD_NOT_ALLOWED.getStatus().name())
.message(e.getMessage()).build();

return ResponseEntity
.status(ErrorCode.METHOD_NOT_ALLOWED.getStatus().value())
.body(errorResponseDto);
protected ResponseEntity<HttpErrorResponseDto> handleMethodNotSupportException() {
return HttpErrorResponseDto.toResponseEntity(CommonErrorCode.METHOD_NOT_ALLOWED);
}

@ExceptionHandler(value = {Exception.class, RuntimeException.class, SQLException.class, DataIntegrityViolationException.class})
protected ResponseEntity<ErrorResponseDto> handleInternalException(
protected ResponseEntity<HttpErrorResponseDto> handleInternalException(
final Exception e,
final HttpServletRequest request
) {
log.error("Exception: {} {}", e.getMessage(), request.getRequestURL());
eventPublisher.publishEvent(new ErrorEvent(ErrorCode.INTERNAL_SERVER_ERROR, request, e));
return ResponseEntity
.status(ErrorCode.INTERNAL_SERVER_ERROR.getStatus().value())
.body(new ErrorResponseDto(ErrorCode.INTERNAL_SERVER_ERROR));
eventPublisher.publishEvent(new ErrorEvent<>(CommonErrorCode.INTERNAL_SERVER_ERROR, request, e));
return HttpErrorResponseDto.toResponseEntity(CommonErrorCode.INTERNAL_SERVER_ERROR);
}

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.depromeet.common.error.dto;

import com.depromeet.common.error.dto.interfaces.ErrorCode;
import com.depromeet.common.error.dto.interfaces.ErrorCodeInterface;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum CommonErrorCode implements ErrorCodeInterface {
/*
* Basic Client Error
*/
BAD_REQUEST(HttpStatus.BAD_REQUEST, "COMMON_BAD_REQUEST", "Bad Request", "The request could not be understood or was missing required parameters."),
METHOD_ARGUMENT_NOT_VALID(HttpStatus.BAD_REQUEST, "COMMON_METHOD_ARGUMENT_NOT_VALID", "Method Argument Not Valid", "One or more method arguments are not valid."),
UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "COMMON_UNAUTHORIZED", "Unauthenticated", "Authentication is required and has failed or has not been provided."),
FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON_FORBIDDEN", "Forbidden", "Access to the requested resource is forbidden."),
NOT_FOUND(HttpStatus.NOT_FOUND, "COMMON_NOT_FOUND", "Not Found", "The requested resource could not be found."),
METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, "COMMON_METHOD_NOT_ALLOWED", "Method Not Allowed", "The method received in the request-line is known by the origin server but not supported."),
CONFLICT(HttpStatus.CONFLICT, "COMMON_CONFLICT", "Conflict", "The request could not be completed due to a conflict with the current state of the target resource."),

/*
* StreetDrop Common Error
*/
CANNOT_USE_BANNED_WORD(HttpStatus.BAD_REQUEST, "COMMON_CAN_NOT_USE_BANNED_WORD", "Can Not Use Banned Word", "Cannot Use Banned Word"),

/*
* Basic Server Error
*/
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON_INTERNAL_SERVER_ERROR", "Internal Server Error", "An unexpected error occurred"),
NOT_IMPLEMENTED(HttpStatus.NOT_IMPLEMENTED, "COMMON_NOT_IMPLEMENTED", "Not Implemented", "The server does not support the functionality required to fulfill the request.");


private final HttpStatus status;
private final String errorResponseCode;
private final String title;
private final String message;

@Override
public ErrorCode toErrorCode() {
return ErrorCode
.builder()
.status(status)
.errorResponseCode(errorResponseCode)
.title(title)
.message(message)
.build();
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.depromeet.common.error.dto;

import com.depromeet.common.error.dto.interfaces.ErrorCode;

import java.util.Optional;

public class ErrorCodeMapper {
public static Optional<ErrorCode> findByErrorCode(String code) {
for (ErrorCode errorCode : ErrorCode.values()) {
if (errorCode.getCode().equals(code)) {
return Optional.of(errorCode);
}
}
return Optional.empty();
var result = StreetDropErrorCodeList.getInstance().getStreetDropErrorCodeList();
return result.stream().filter(streetDropErrorCode -> streetDropErrorCode.getErrorResponseCode().equals(code))
.findFirst().map(StreetDropErrorCode::getErrorCode);
}

}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.depromeet.common.error.dto;

import com.depromeet.common.error.dto.interfaces.ErrorCode;
import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public class StreetDropErrorCode {

private String errorResponseCode;

private ErrorCode errorCode;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.depromeet.common.error.dto;

import com.depromeet.common.error.dto.interfaces.ErrorCode;
import com.depromeet.common.error.dto.interfaces.ErrorCodeInterface;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.core.type.filter.TypeFilter;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Getter
@Slf4j
public class StreetDropErrorCodeList {

private static volatile StreetDropErrorCodeList instance;
private final List<StreetDropErrorCode> streetDropErrorCodeList;

private StreetDropErrorCodeList() {
streetDropErrorCodeList = createStreetDropErrorCodeList();
}

public static StreetDropErrorCodeList getInstance(){
if (instance == null) {
synchronized (StreetDropErrorCodeList.class) {
if (instance == null) {
instance = new StreetDropErrorCodeList();
}
}
}
return instance;
}

private synchronized List<StreetDropErrorCode> createStreetDropErrorCodeList() {
List<StreetDropErrorCode> streetDropErrorCodeList = new ArrayList<>();

try {
ClassPathScanningCandidateComponentProvider s = new ClassPathScanningCandidateComponentProvider(false);

TypeFilter tf = new AssignableTypeFilter(ErrorCodeInterface.class);
s.addIncludeFilter(tf);

Set<BeanDefinition> components = s.findCandidateComponents("com.depromeet");

for (BeanDefinition component : components) {
Class<?> className = Class.forName(component.getBeanClassName());

if (className.isEnum()) {
for (var errorCode : className.getEnumConstants()) {
if (errorCode != null) {
String errorResponseCode = (String) errorCode.getClass().getMethod("getErrorResponseCode").invoke(errorCode);
ErrorCode error = (ErrorCode) errorCode.getClass().getMethod("toErrorCode").invoke(errorCode);
var streetDropError = new StreetDropErrorCode(errorResponseCode, error);
streetDropErrorCodeList.add(streetDropError);
}
}
}
}

} catch (Exception ignored) {
}

return streetDropErrorCodeList;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.depromeet.common.error.dto.interfaces;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
@Builder
public class ErrorCode implements ErrorCodeInterface {
private HttpStatus status;
private String errorResponseCode;
private String title;
private String message;

@Override
public ErrorCode toErrorCode() {
return this;
}

public void appendMessage(String additionalMessage) {
this.message += " " + additionalMessage;
}

}
Loading

0 comments on commit e469d5e

Please sign in to comment.