Skip to content

Commit

Permalink
add constants and handle exception in product (#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
khanhduzz authored Oct 15, 2024
1 parent 1addf1d commit e71a22e
Show file tree
Hide file tree
Showing 13 changed files with 320 additions and 0 deletions.
18 changes: 18 additions & 0 deletions product/src/main/java/com/fjb/product/constants/ApiConstant.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.fjb.product.constants;

public final class ApiConstant {
public static final String CODE_200 = "200";
public static final String OK = "OK";
public static final String CODE_404 = "404";
public static final String NOT_FOUND = "NOT_FOUND";
public static final String CODE_201 = "201";
public static final String CREATED = "CREATED";
public static final String CODE_400 = "400";
public static final String BAD_REQUEST = "BAD_REQUEST";
public static final String CODE_204 = "204";
public static final String NO_CONTENT = "NO_CONTENT";
public static final String ACCESS_DENIED = "ACCESS_DENIED";

private ApiConstant() {
}
}
10 changes: 10 additions & 0 deletions product/src/main/java/com/fjb/product/constants/MessageCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.fjb.product.constants;

public final class MessageCode {
public static final String PRODUCT_NOT_FOUND = "PRODUCT_NOT_FOUND";
public static final String NAME_ALREADY_EXISTED = "NAME_ALREADY_EXISTED";
public static final String REQUEST_INFORMATION_NOT_VALID = "REQUEST_INFORMATION_NOT_VALID";

private MessageCode() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.fjb.product.constants;

public final class PageableConstant {
public static final String DEFAULT_PAGE_SIZE = "10";
public static final String DEFAULT_PAGE_NUMBER = "0";

private PageableConstant() {
}
}
11 changes: 11 additions & 0 deletions product/src/main/java/com/fjb/product/dto/response/ErrorVm.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.fjb.product.dto.response;

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

public record ErrorVm(String statusCode, String title, String detail, List<String> fieldErrors) {

public ErrorVm(String statusCode, String title, String detail) {
this(statusCode, title, detail, new ArrayList<>());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.fjb.product.exception;

public class AccessDeniedException extends RuntimeException {
public AccessDeniedException(final String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package com.fjb.product.exception;

import com.fjb.product.constants.MessageCode;
import com.fjb.product.dto.response.ErrorVm;
import jakarta.validation.ConstraintViolationException;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;

@ControllerAdvice
@Slf4j
public class ApiExceptionHandler {
private static final String ERROR_LOG_FORMAT = "Error: URI: {}, ErrorCode: {}, Message: {}";

@ExceptionHandler(NotFoundException.class)
public ResponseEntity<ErrorVm> handleNotFoundException(NotFoundException ex, WebRequest request) {
HttpStatus status = HttpStatus.NOT_FOUND;
String message = ex.getMessage();

return buildErrorResponse(status, message, null, ex, request, status.value());
}

@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<ErrorVm> handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {

HttpStatus status = HttpStatus.BAD_REQUEST;

List<String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(error -> error.getField() + " " + error.getDefaultMessage())
.toList();

return buildErrorResponse(status, MessageCode.REQUEST_INFORMATION_NOT_VALID, errors, ex, null, status.value());
}

@ExceptionHandler(Exception.class)
protected ResponseEntity<ErrorVm> handleOtherException(Exception ex, WebRequest request) {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
String message = ex.getMessage();

return buildErrorResponse(status, message, null, ex, request, status.value());
}

@ExceptionHandler(BadRequestException.class)
public ResponseEntity<ErrorVm> handleBadRequestException(BadRequestException ex, WebRequest request) {
return handleBadRequest(ex, request);
}

@ExceptionHandler({ConstraintViolationException.class})
public ResponseEntity<ErrorVm> handleConstraintViolation(ConstraintViolationException ex) {
HttpStatus status = HttpStatus.BAD_REQUEST;

List<String> errors = ex.getConstraintViolations().stream()
.map(violation -> String.format("%s %s: %s",
violation.getRootBeanClass().getName(),
violation.getPropertyPath(),
violation.getMessage()))
.toList();

return buildErrorResponse(status, MessageCode.REQUEST_INFORMATION_NOT_VALID, errors, ex, null, status.value());
}

@ExceptionHandler(DataIntegrityViolationException.class)
public ResponseEntity<ErrorVm> handleDataIntegrityViolationException(DataIntegrityViolationException ex) {
return handleBadRequest(ex, null);
}

@ExceptionHandler(DuplicatedException.class)
protected ResponseEntity<ErrorVm> handleDuplicated(DuplicatedException ex) {
return handleBadRequest(ex, null);
}


@ExceptionHandler(MissingServletRequestParameterException.class)
protected ResponseEntity<ErrorVm> handleMissingParams(MissingServletRequestParameterException e) {
return handleBadRequest(e, null);
}


@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<ErrorVm> handleAccessDeniedException(AccessDeniedException ex, WebRequest request) {
HttpStatus status = HttpStatus.FORBIDDEN;
String message = ex.getMessage();

return buildErrorResponse(status, message, null, ex, request, status.value());
}

private String getServletPath(WebRequest webRequest) {
ServletWebRequest servletRequest = (ServletWebRequest) webRequest;
return servletRequest.getRequest().getServletPath();
}

private ResponseEntity<ErrorVm> handleBadRequest(Exception ex, WebRequest request) {
HttpStatus status = HttpStatus.BAD_REQUEST;
String message = ex.getMessage();

return buildErrorResponse(status, message, null, ex, request, status.value());
}

private ResponseEntity<ErrorVm> buildErrorResponse(HttpStatus status, String message, List<String> errors,
Exception ex, WebRequest request, int statusCode) {
ErrorVm errorVm =
new ErrorVm(status.toString(), status.getReasonPhrase(), message, errors);

if (request != null) {
log.error(ERROR_LOG_FORMAT, this.getServletPath(request), statusCode, message);
}
log.error(message, ex);
return ResponseEntity.status(status).body(errorVm);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.fjb.product.exception;


import com.fjb.product.utils.MessagesUtils;

public class BadRequestException extends RuntimeException {

private final String message;

public BadRequestException(String message) {
this.message = MessagesUtils.getMessage(message);
}

public BadRequestException(String errorCode, Object... var2) {
this.message = MessagesUtils.getMessage(errorCode, var2);
}

@Override
public String getMessage() {
return message;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.fjb.product.exception;


import com.fjb.product.utils.MessagesUtils;

public class DuplicatedException extends RuntimeException {

private final String message;

public DuplicatedException(String errorCode, Object... var2) {
this.message = MessagesUtils.getMessage(errorCode, var2);
}

@Override
public String getMessage() {
return message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.fjb.product.exception;


import com.fjb.product.utils.MessagesUtils;

public class ForbiddenException extends RuntimeException {
private final String message;

public ForbiddenException(String errorCode, Object... var2) {
this.message = MessagesUtils.getMessage(errorCode, var2);
}

@Override
public String getMessage() {
return message;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.fjb.product.exception;


import com.fjb.product.utils.MessagesUtils;

public class NotFoundException extends RuntimeException {
private final String message;

public NotFoundException(String errorCode, Object... var2) {
this.message = MessagesUtils.getMessage(errorCode, var2);
}

@Override
public String getMessage() {
return message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.fjb.product.utils;

import com.fjb.product.constants.ApiConstant;
import com.fjb.product.exception.AccessDeniedException;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;

public final class AuthenticationUtils {

private AuthenticationUtils() {
}

public static String extractUserId() {
Authentication authentication = getAuthentication();

if (authentication instanceof AnonymousAuthenticationToken) {
throw new AccessDeniedException(ApiConstant.ACCESS_DENIED);
}

JwtAuthenticationToken contextHolder = (JwtAuthenticationToken) authentication;

return contextHolder.getToken().getSubject();
}

public static String extractJwt() {
return ((Jwt) getAuthentication().getPrincipal()).getTokenValue();
}

public static Authentication getAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
}
26 changes: 26 additions & 0 deletions product/src/main/java/com/fjb/product/utils/MessagesUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.fjb.product.utils;

import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.MessageFormatter;

public class MessagesUtils {
private static final ResourceBundle messageBundle = ResourceBundle.getBundle("messages.messages",
Locale.getDefault());

private MessagesUtils() {
}

public static String getMessage(String errorCode, Object... var2) {
String message;
try {
message = messageBundle.getString(errorCode);
} catch (MissingResourceException ex) {
message = errorCode;
}
FormattingTuple formattingTuple = MessageFormatter.arrayFormat(message, var2);
return formattingTuple.getMessage();
}
}
8 changes: 8 additions & 0 deletions product/src/main/resources/messages/messages.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
PRODUCT_NOT_FOUND=The product {} is not found
NAME_ALREADY_EXISTED=The name {} is already existed
ACCESS_DENIED=Access denied
NO_CONTENT=No content
BAD_REQUEST=Bad request
CREATED=Created
NOT_FOUND=Not found
REQUEST_INFORMATION_NOT_VALID=Request information is not valid

0 comments on commit e71a22e

Please sign in to comment.