Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ feat: add exception handling to notification server #394

Merged
merged 1 commit into from
Nov 5, 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.depromeet.error;

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 ErrorDomain domainCode;
private String code;
private String title;
private String message;

@Override
public ErrorCode toErrorCode() {
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.depromeet.error;

import org.springframework.http.HttpStatus;

/**
* This interface provides a basic specification for defining domain error codes.
*
*
* This interface used to extend errorCodeEnum.
*/
public interface ErrorCodeInterface {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

이 인터페이스를 확장하여 ErrorEnum을 추가하시면 됩니다.


/**
* Returns the HTTP status code.
*
* @return The HTTP status code.
*/
HttpStatus getStatus();


/**
* Returns the error code. It is not duplicated by domain at all.
*
* @return The error code.
*/
String getCode();

/**
* Returns the error domain code. All errors are categorized by domain, so check the ErrorDomain code to see them.
*
* @return The domain code.
*/
ErrorDomain getDomainCode();

/**
* Returns the error title - Short Human Readable Error Title.
*
* @return The error title.
*/
String getTitle();

/**
* Returns the error message - Human Readable Error Message, It is also recommended that you write solutions together.
*
* @return The error message.
*/
String getMessage();

/**
* Converts this to an ErrorCode object.
*
* @return An ErrorCode object.
*/
ErrorCode toErrorCode();

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

public enum ErrorDomain {
COMMON, AUTH, FIREBASE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.depromeet.error;

import lombok.Builder;
import lombok.Getter;
import org.springframework.http.ResponseEntity;

import java.time.LocalDateTime;

@Getter
@Builder
public class ErrorResponse {
private final LocalDateTime timestamp = LocalDateTime.now();
private final int status;
private final ErrorDomain errorDomain;
private final String code;
private final String title;
private final String message;

public static <T extends ErrorCodeInterface> ResponseEntity<ErrorResponse> toResponseEntity(T errorCode) {
return ResponseEntity
.status(errorCode.getStatus())
.body(ErrorResponse.builder()
.status(errorCode.getStatus().value())
.errorDomain(errorCode.getDomainCode())
.code(errorCode.getCode())
.title(errorCode.getTitle())
.message(errorCode.getMessage())
.build());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.depromeet.error;


import com.depromeet.error.exceptions.BusinessException;
import com.depromeet.error.code.GlobalErrorCode;
import com.depromeet.error.exceptions.NotFoundException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.client.HttpClientErrorException;

@Slf4j
@RestControllerAdvice
@RequiredArgsConstructor
public class GlobalExceptionHandler {

@ExceptionHandler(BusinessException.class)
protected ResponseEntity<ErrorResponse> handleBusinessException(BusinessException e) {
return ErrorResponse.toResponseEntity(e.getErrorCode());
}

@ExceptionHandler(NotFoundException.class)
protected ResponseEntity<ErrorResponse> handleNotFoundException(NotFoundException e) {
return ErrorResponse.toResponseEntity(e.getErrorCode());
}


@ExceptionHandler(HttpClientErrorException.BadRequest.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
protected ResponseEntity<ErrorResponse> handleBadRequestException(HttpClientErrorException.BadRequest e) {
return ErrorResponse.toResponseEntity(GlobalErrorCode.BAD_REQUEST);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.depromeet.error.code;

import com.depromeet.error.ErrorCode;
import com.depromeet.error.ErrorCodeInterface;
import com.depromeet.error.ErrorDomain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum FireBaseErrorCode implements ErrorCodeInterface {

FIRE_BASE_INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "FireBase Internal Server Error", "FireBase Internal Server Error"),
TOO_MUCH_REQUEST(HttpStatus.TOO_MANY_REQUESTS, "Too Much Request to FireBase Server", "Too Much Request to FireBase Server, Request After 3 Minutes")
;

private final HttpStatus status;
private final ErrorDomain domainCode = ErrorDomain.FIREBASE;
private final String code = name().toUpperCase();
private final String title;
private final String message;

public ErrorCode toErrorCode() {
return ErrorCode
.builder()
.status(status)
.domainCode(domainCode)
.code(code)
.message(message)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.depromeet.error.code;

import com.depromeet.error.ErrorCode;
import com.depromeet.error.ErrorCodeInterface;
import com.depromeet.error.ErrorDomain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum GlobalErrorCode implements ErrorCodeInterface {

BAD_REQUEST(HttpStatus.BAD_REQUEST, "Bad Request", "This Request BadRequest"),
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "Internal Server Error", "Internal Server Error"),
INVALID_INPUT_VALUE(HttpStatus.BAD_REQUEST, "Invalid Input Value", "Invalid Input Value"),
EXTERNAL_SERVER_EXCEPTION(HttpStatus.INTERNAL_SERVER_ERROR, "External Sever Error","Invalid Input Value");

private final HttpStatus status;
private final String code = name().toUpperCase();
private final ErrorDomain domainCode = ErrorDomain.COMMON;
private final String title;
private final String message;

public ErrorCode toErrorCode() {
return ErrorCode.builder()
.status(status)
.domainCode(domainCode)
.code(code)
.message(message)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.depromeet.error.code;

import com.depromeet.error.ErrorCode;
import com.depromeet.error.ErrorCodeInterface;
import com.depromeet.error.ErrorDomain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum TokenErrorCode implements ErrorCodeInterface {

TOKEN_NOT_FOUND(HttpStatus.NOT_FOUND, "Token is not found", "Token is not found"),
;

private final HttpStatus status;
private final ErrorDomain domainCode = ErrorDomain.FIREBASE;
private final String code = name().toUpperCase();
private final String title;
private final String message;

public ErrorCode toErrorCode() {
return ErrorCode
.builder()
.status(status)
.domainCode(domainCode)
.code(code)
.message(message)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.depromeet.error.exceptions;


import com.depromeet.error.ErrorCode;
import com.depromeet.error.ErrorCodeInterface;
import lombok.Getter;

@Getter
public class BusinessException extends RuntimeException {
private final ErrorCode errorCode;
private final Exception exception;

public <T extends ErrorCodeInterface> BusinessException(T errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode.toErrorCode();
this.exception = getException();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.depromeet.error.exceptions;

import com.depromeet.error.ErrorCode;
import com.depromeet.error.ErrorCodeInterface;
import lombok.Getter;

@Getter
public class ExternalServerException extends RuntimeException {
private final ErrorCode errorCode;
private final Exception exception;

public <T extends ErrorCodeInterface> ExternalServerException(T errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode.toErrorCode();
this.exception = getException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.depromeet.error.exceptions;

import com.depromeet.error.ErrorCode;
import com.depromeet.error.ErrorCodeInterface;
import lombok.Getter;

@Getter
public class NotFoundException extends RuntimeException {
private final ErrorCode errorCode;
private final Exception exception;

public <T extends ErrorCodeInterface> NotFoundException(T errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode.toErrorCode();
this.exception = getException();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.depromeet.external.fcm;

import com.depromeet.error.exceptions.BusinessException;
import com.depromeet.error.code.GlobalErrorCode;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
Expand Down Expand Up @@ -29,8 +31,7 @@ private void initialize() {
FirebaseApp.initializeApp(options);
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Firebase initialization failed");
throw new BusinessException(GlobalErrorCode.BAD_REQUEST);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
import com.depromeet.dto.request.AllPushRequestDto;
import com.depromeet.dto.request.PushRequestDto;
import com.depromeet.dto.request.TopicPushRequestDto;
import com.depromeet.error.ErrorCode;
import com.depromeet.error.code.FireBaseErrorCode;
import com.depromeet.error.exceptions.ExternalServerException;
import com.depromeet.external.fcm.FcmService;
import com.depromeet.repository.UserDeviceRepository;
import com.google.firebase.FirebaseException;
import com.google.firebase.messaging.FirebaseMessagingException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
Expand Down Expand Up @@ -40,6 +44,7 @@ public void sendPush(PushRequestDto pushRequestDto) {
}
}
} catch (FirebaseMessagingException e) {
throw new ExternalServerException(FireBaseErrorCode.FIRE_BASE_INTERNAL_SERVER_ERROR);
}

notificationService.save(pushRequestDto);
Expand All @@ -58,6 +63,7 @@ public void sendAllPush(AllPushRequestDto pushRequestDto) {
fcmService.sendMulticastMessageSync(tokens, pushRequestDto.getContent());
}
} catch (FirebaseMessagingException e) {
throw new ExternalServerException(FireBaseErrorCode.FIRE_BASE_INTERNAL_SERVER_ERROR);
}

notificationService.save(pushRequestDto);
Expand All @@ -72,6 +78,7 @@ public void sendTopicPush(TopicPushRequestDto tokenPushRequestDto) {
fcmService.sendTopicMessageSync(tokenPushRequestDto.getTopic(), tokenPushRequestDto.getContent());
}
} catch (FirebaseMessagingException e) {
throw new ExternalServerException(FireBaseErrorCode.FIRE_BASE_INTERNAL_SERVER_ERROR);
}

notificationService.save(tokenPushRequestDto);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import com.depromeet.domain.UserDevice;
import com.depromeet.dto.request.TokenRequestDto;
import com.depromeet.error.code.TokenErrorCode;
import com.depromeet.error.exceptions.BusinessException;
import com.depromeet.error.exceptions.NotFoundException;
import com.depromeet.repository.UserDeviceRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
Expand Down Expand Up @@ -33,7 +36,7 @@ public void saveToken(TokenRequestDto tokenRequestDto) {
@Transactional
public void deleteToken(Long userId) {
var userDevice = userDeviceRepository.findByUserId(userId)
.orElseThrow(() -> new RuntimeException("Token not found for userId: " + userId));
.orElseThrow(() -> new NotFoundException(TokenErrorCode.TOKEN_NOT_FOUND));
userDeviceRepository.delete(userDevice);
}
}
Loading