diff --git a/src/main/java/com/backend/coapp/exception/ApiException.java b/src/main/java/com/backend/coapp/exception/ApiException.java new file mode 100644 index 00000000..48329bfe --- /dev/null +++ b/src/main/java/com/backend/coapp/exception/ApiException.java @@ -0,0 +1,24 @@ +package com.backend.coapp.exception; + +import org.springframework.http.HttpStatus; + +/** Interface for exception from API call */ +public abstract class ApiException extends RuntimeException { + /** + * getStatus + * + * @return HTTPS Status + */ + public abstract HttpStatus getStatus(); + + /** + * getErrorCode + * + * @return custom error code + */ + public abstract Object getErrorCode(); + + protected ApiException(String message) { + super(message); + } +} diff --git a/src/main/java/com/backend/coapp/exception/application/ApplicationNotFoundException.java b/src/main/java/com/backend/coapp/exception/application/ApplicationNotFoundException.java index a62d1fa4..417016a2 100644 --- a/src/main/java/com/backend/coapp/exception/application/ApplicationNotFoundException.java +++ b/src/main/java/com/backend/coapp/exception/application/ApplicationNotFoundException.java @@ -1,7 +1,22 @@ package com.backend.coapp.exception.application; -public class ApplicationNotFoundException extends RuntimeException { +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.ApplicationErrorCode; +import org.springframework.http.HttpStatus; + +/** This exception will be thrown when application not found. */ +public class ApplicationNotFoundException extends ApiException { public ApplicationNotFoundException() { super("Could not find application"); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.NOT_FOUND; + } + + @Override + public Object getErrorCode() { + return ApplicationErrorCode.APPLICATION_NOT_FOUND; + } } diff --git a/src/main/java/com/backend/coapp/exception/application/ApplicationNotOwnedException.java b/src/main/java/com/backend/coapp/exception/application/ApplicationNotOwnedException.java index bde9f67b..85a5bba3 100644 --- a/src/main/java/com/backend/coapp/exception/application/ApplicationNotOwnedException.java +++ b/src/main/java/com/backend/coapp/exception/application/ApplicationNotOwnedException.java @@ -1,7 +1,22 @@ package com.backend.coapp.exception.application; -public class ApplicationNotOwnedException extends RuntimeException { +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.ApplicationErrorCode; +import org.springframework.http.HttpStatus; + +/** This exception will be thrown will the application is not belong to a user. */ +public class ApplicationNotOwnedException extends ApiException { public ApplicationNotOwnedException() { super("Application is not owned by user. No operation is allowed."); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.FORBIDDEN; + } + + @Override + public Object getErrorCode() { + return ApplicationErrorCode.APPLICATION_NOT_OWN; + } } diff --git a/src/main/java/com/backend/coapp/exception/application/ApplicationServiceFailException.java b/src/main/java/com/backend/coapp/exception/application/ApplicationServiceFailException.java index 1e63a3cf..bfc61948 100644 --- a/src/main/java/com/backend/coapp/exception/application/ApplicationServiceFailException.java +++ b/src/main/java/com/backend/coapp/exception/application/ApplicationServiceFailException.java @@ -1,7 +1,22 @@ package com.backend.coapp.exception.application; -public class ApplicationServiceFailException extends RuntimeException { +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.SystemErrorCode; +import org.springframework.http.HttpStatus; + +/** This exception will be thrown when application service fail (internal) */ +public class ApplicationServiceFailException extends ApiException { public ApplicationServiceFailException(String message) { super(message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.INTERNAL_SERVER_ERROR; + } + + @Override + public Object getErrorCode() { + return SystemErrorCode.INTERNAL_ERROR; + } } diff --git a/src/main/java/com/backend/coapp/exception/application/DuplicateApplicationException.java b/src/main/java/com/backend/coapp/exception/application/DuplicateApplicationException.java index 465b0fb0..99dd02b8 100644 --- a/src/main/java/com/backend/coapp/exception/application/DuplicateApplicationException.java +++ b/src/main/java/com/backend/coapp/exception/application/DuplicateApplicationException.java @@ -1,9 +1,24 @@ package com.backend.coapp.exception.application; -public class DuplicateApplicationException extends RuntimeException { +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.ApplicationErrorCode; +import org.springframework.http.HttpStatus; + +/** This application will be thrown when the application already exist */ +public class DuplicateApplicationException extends ApiException { public DuplicateApplicationException(String jobTitle, String companyId) { super( String.format( "User has already created an application for '%s' at company %s", jobTitle, companyId)); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.CONFLICT; + } + + @Override + public Object getErrorCode() { + return ApplicationErrorCode.DUPLICATE_APPLICATION; + } } diff --git a/src/main/java/com/backend/coapp/exception/application/NoChangesDetectedException.java b/src/main/java/com/backend/coapp/exception/application/NoChangesDetectedException.java index f149fe9d..7f8355fb 100644 --- a/src/main/java/com/backend/coapp/exception/application/NoChangesDetectedException.java +++ b/src/main/java/com/backend/coapp/exception/application/NoChangesDetectedException.java @@ -1,7 +1,22 @@ package com.backend.coapp.exception.application; -public class NoChangesDetectedException extends RuntimeException { +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.ApplicationErrorCode; +import org.springframework.http.HttpStatus; + +/** This exception will be thrown when no changed detected in updated application */ +public class NoChangesDetectedException extends ApiException { public NoChangesDetectedException(String message) { super(message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.BAD_REQUEST; + } + + @Override + public Object getErrorCode() { + return ApplicationErrorCode.NO_CHANGE_DETECTED_TO_UPDATE; + } } diff --git a/src/main/java/com/backend/coapp/exception/application/UnauthorizedApplicationAccessException.java b/src/main/java/com/backend/coapp/exception/application/UnauthorizedApplicationAccessException.java index eece00b3..a028494f 100644 --- a/src/main/java/com/backend/coapp/exception/application/UnauthorizedApplicationAccessException.java +++ b/src/main/java/com/backend/coapp/exception/application/UnauthorizedApplicationAccessException.java @@ -1,7 +1,22 @@ package com.backend.coapp.exception.application; -public class UnauthorizedApplicationAccessException extends RuntimeException { +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.ApplicationErrorCode; +import org.springframework.http.HttpStatus; + +/** This exception will be thrown when the user tries to access the unauthorized resource. */ +public class UnauthorizedApplicationAccessException extends ApiException { public UnauthorizedApplicationAccessException(String message) { super(message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.FORBIDDEN; + } + + @Override + public Object getErrorCode() { + return ApplicationErrorCode.UNAUTHORIZED_APPLICATION_ACCESS; + } } diff --git a/src/main/java/com/backend/coapp/exception/auth/AuthAccountAlreadyVerifyException.java b/src/main/java/com/backend/coapp/exception/auth/AuthAccountAlreadyVerifyException.java index 8030419d..c455539b 100644 --- a/src/main/java/com/backend/coapp/exception/auth/AuthAccountAlreadyVerifyException.java +++ b/src/main/java/com/backend/coapp/exception/auth/AuthAccountAlreadyVerifyException.java @@ -1,10 +1,14 @@ package com.backend.coapp.exception.auth; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.AuthErrorCode; +import org.springframework.http.HttpStatus; + /** * This exception will be thrown if a client tries to verify an account that have been already * verified. */ -public class AuthAccountAlreadyVerifyException extends RuntimeException { +public class AuthAccountAlreadyVerifyException extends ApiException { public AuthAccountAlreadyVerifyException() { super("Account has been verified."); } @@ -12,4 +16,14 @@ public AuthAccountAlreadyVerifyException() { public AuthAccountAlreadyVerifyException(String message) { super("Account has been verified. " + message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.METHOD_NOT_ALLOWED; + } + + @Override + public Object getErrorCode() { + return AuthErrorCode.ACCOUNT_ALREADY_VERIFIED; + } } diff --git a/src/main/java/com/backend/coapp/exception/auth/AuthAccountNotYetActivatedException.java b/src/main/java/com/backend/coapp/exception/auth/AuthAccountNotYetActivatedException.java index 84777836..141ae0f5 100644 --- a/src/main/java/com/backend/coapp/exception/auth/AuthAccountNotYetActivatedException.java +++ b/src/main/java/com/backend/coapp/exception/auth/AuthAccountNotYetActivatedException.java @@ -1,10 +1,14 @@ package com.backend.coapp.exception.auth; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.AuthErrorCode; +import org.springframework.http.HttpStatus; + /** * This exception will be thrown if client tries to request service with an account that not yet * activate */ -public class AuthAccountNotYetActivatedException extends RuntimeException { +public class AuthAccountNotYetActivatedException extends ApiException { public AuthAccountNotYetActivatedException() { super( "Account has not been activated yet. Please use verification code to activate the account."); @@ -15,4 +19,14 @@ public AuthAccountNotYetActivatedException(String message) { "Account has not been activated yet. Please use verification code to activate the account. " + message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.UNAUTHORIZED; + } + + @Override + public Object getErrorCode() { + return AuthErrorCode.ACCOUNT_NOT_ACTIVATED; + } } diff --git a/src/main/java/com/backend/coapp/exception/auth/AuthBadCredentialException.java b/src/main/java/com/backend/coapp/exception/auth/AuthBadCredentialException.java index d097f987..fd1d31f6 100644 --- a/src/main/java/com/backend/coapp/exception/auth/AuthBadCredentialException.java +++ b/src/main/java/com/backend/coapp/exception/auth/AuthBadCredentialException.java @@ -1,7 +1,11 @@ package com.backend.coapp.exception.auth; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.AuthErrorCode; +import org.springframework.http.HttpStatus; + /** This exception will be thrown when a user tries to log in with invalid credential. */ -public class AuthBadCredentialException extends RuntimeException { +public class AuthBadCredentialException extends ApiException { public AuthBadCredentialException() { super("Incorrect email or password"); } @@ -9,4 +13,14 @@ public AuthBadCredentialException() { public AuthBadCredentialException(String message) { super("Bad credential. " + message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.UNAUTHORIZED; + } + + @Override + public Object getErrorCode() { + return AuthErrorCode.INVALID_EMAIL_OR_PASSWORD; + } } diff --git a/src/main/java/com/backend/coapp/exception/auth/AuthEmailAlreadyUsedException.java b/src/main/java/com/backend/coapp/exception/auth/AuthEmailAlreadyUsedException.java index d21b9c09..7a737927 100644 --- a/src/main/java/com/backend/coapp/exception/auth/AuthEmailAlreadyUsedException.java +++ b/src/main/java/com/backend/coapp/exception/auth/AuthEmailAlreadyUsedException.java @@ -1,10 +1,14 @@ package com.backend.coapp.exception.auth; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.AuthErrorCode; +import org.springframework.http.HttpStatus; + /** * This exception will be thrown if client tries to register a new account with an email that has * been used. */ -public class AuthEmailAlreadyUsedException extends RuntimeException { +public class AuthEmailAlreadyUsedException extends ApiException { public AuthEmailAlreadyUsedException() { super("An account with that email already exists."); } @@ -12,4 +16,14 @@ public AuthEmailAlreadyUsedException() { public AuthEmailAlreadyUsedException(String message) { super("An account with that email already exists. " + message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.CONFLICT; + } + + @Override + public Object getErrorCode() { + return AuthErrorCode.EXIST_ACCOUNT_WITH_EMAIL; + } } diff --git a/src/main/java/com/backend/coapp/exception/auth/AuthEmailNotRegisteredException.java b/src/main/java/com/backend/coapp/exception/auth/AuthEmailNotRegisteredException.java index 083eca91..3c6b1259 100644 --- a/src/main/java/com/backend/coapp/exception/auth/AuthEmailNotRegisteredException.java +++ b/src/main/java/com/backend/coapp/exception/auth/AuthEmailNotRegisteredException.java @@ -1,11 +1,13 @@ package com.backend.coapp.exception.auth; -import org.springframework.security.core.userdetails.UsernameNotFoundException; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.AuthErrorCode; +import org.springframework.http.HttpStatus; /** * This exception will be thrown when client tries to access information with unregistered email. */ -public class AuthEmailNotRegisteredException extends UsernameNotFoundException { +public class AuthEmailNotRegisteredException extends ApiException { public AuthEmailNotRegisteredException() { super("Email is not yet registered."); } @@ -13,4 +15,14 @@ public AuthEmailNotRegisteredException() { public AuthEmailNotRegisteredException(String message) { super("Email is not yet registered. " + message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.BAD_REQUEST; + } + + @Override + public Object getErrorCode() { + return AuthErrorCode.EMAIL_NOT_REGISTERED; + } } diff --git a/src/main/java/com/backend/coapp/exception/auth/EmailInvalidAddressException.java b/src/main/java/com/backend/coapp/exception/auth/EmailInvalidAddressException.java index 5d70594f..05f00b27 100644 --- a/src/main/java/com/backend/coapp/exception/auth/EmailInvalidAddressException.java +++ b/src/main/java/com/backend/coapp/exception/auth/EmailInvalidAddressException.java @@ -1,7 +1,11 @@ package com.backend.coapp.exception.auth; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.AuthErrorCode; +import org.springframework.http.HttpStatus; + /** This exception will be thrown if the provided email is invalid. */ -public class EmailInvalidAddressException extends RuntimeException { +public class EmailInvalidAddressException extends ApiException { public EmailInvalidAddressException() { super("Invalid email or email not exit."); } @@ -9,4 +13,14 @@ public EmailInvalidAddressException() { public EmailInvalidAddressException(String message) { super("Invalid email or email not exit. " + message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.BAD_REQUEST; + } + + @Override + public Object getErrorCode() { + return AuthErrorCode.INVALID_EMAIL; + } } diff --git a/src/main/java/com/backend/coapp/exception/auth/EmailServiceException.java b/src/main/java/com/backend/coapp/exception/auth/EmailServiceException.java index 752fab10..5a67d47a 100644 --- a/src/main/java/com/backend/coapp/exception/auth/EmailServiceException.java +++ b/src/main/java/com/backend/coapp/exception/auth/EmailServiceException.java @@ -1,7 +1,11 @@ package com.backend.coapp.exception.auth; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.SystemErrorCode; +import org.springframework.http.HttpStatus; + /** This exception will be thrown if there is a failure related to JavaMailSender service. */ -public class EmailServiceException extends RuntimeException { +public class EmailServiceException extends ApiException { public EmailServiceException() { super("JavaMailSender Service Failure."); @@ -10,4 +14,14 @@ public EmailServiceException() { public EmailServiceException(String message) { super("JavaMailSender Service Failure. " + message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.INTERNAL_SERVER_ERROR; + } + + @Override + public Object getErrorCode() { + return SystemErrorCode.INTERNAL_ERROR; + } } diff --git a/src/main/java/com/backend/coapp/exception/auth/IncorrectCodeException.java b/src/main/java/com/backend/coapp/exception/auth/IncorrectCodeException.java index 4fa92d21..ed839d53 100644 --- a/src/main/java/com/backend/coapp/exception/auth/IncorrectCodeException.java +++ b/src/main/java/com/backend/coapp/exception/auth/IncorrectCodeException.java @@ -1,10 +1,14 @@ package com.backend.coapp.exception.auth; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.AuthErrorCode; +import org.springframework.http.HttpStatus; + /** * This exception will be thrown when user provide incorrect verification code for verifying * account/updating password */ -public class IncorrectCodeException extends RuntimeException { +public class IncorrectCodeException extends ApiException { public IncorrectCodeException() { super("The code provided is incorrect. Please check your email again."); } @@ -12,4 +16,14 @@ public IncorrectCodeException() { public IncorrectCodeException(String message) { super("The code provided is incorrect. Please check your email again. " + message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.BAD_REQUEST; + } + + @Override + public Object getErrorCode() { + return AuthErrorCode.INVALID_CONFIRMATION_CODE; + } } diff --git a/src/main/java/com/backend/coapp/exception/auth/JwtServiceFailException.java b/src/main/java/com/backend/coapp/exception/auth/JwtServiceFailException.java index 59069501..c58f8231 100644 --- a/src/main/java/com/backend/coapp/exception/auth/JwtServiceFailException.java +++ b/src/main/java/com/backend/coapp/exception/auth/JwtServiceFailException.java @@ -1,8 +1,22 @@ package com.backend.coapp.exception.auth; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.SystemErrorCode; +import org.springframework.http.HttpStatus; + /** This exception will be thrown when there is unknown JWT service */ -public class JwtServiceFailException extends RuntimeException { +public class JwtServiceFailException extends ApiException { public JwtServiceFailException(String message) { super("JWT Service failure." + message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.INTERNAL_SERVER_ERROR; + } + + @Override + public Object getErrorCode() { + return SystemErrorCode.INTERNAL_ERROR; + } } diff --git a/src/main/java/com/backend/coapp/exception/auth/UserInvalidPasswordChangeException.java b/src/main/java/com/backend/coapp/exception/auth/UserInvalidPasswordChangeException.java index d418de80..ec478142 100644 --- a/src/main/java/com/backend/coapp/exception/auth/UserInvalidPasswordChangeException.java +++ b/src/main/java/com/backend/coapp/exception/auth/UserInvalidPasswordChangeException.java @@ -1,8 +1,22 @@ package com.backend.coapp.exception.auth; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.UserErrorCode; +import org.springframework.http.HttpStatus; + /** This exception will be thrown when user update password with old password. */ -public class UserInvalidPasswordChangeException extends RuntimeException { +public class UserInvalidPasswordChangeException extends ApiException { public UserInvalidPasswordChangeException() { super("New password must be different from old password"); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.BAD_REQUEST; + } + + @Override + public Object getErrorCode() { + return UserErrorCode.NEW_PASSWORD_SAME_WITH_OLD_PASSWORD; + } } diff --git a/src/main/java/com/backend/coapp/exception/auth/UserServiceFailException.java b/src/main/java/com/backend/coapp/exception/auth/UserServiceFailException.java index 9e79d7cb..2223af16 100644 --- a/src/main/java/com/backend/coapp/exception/auth/UserServiceFailException.java +++ b/src/main/java/com/backend/coapp/exception/auth/UserServiceFailException.java @@ -1,7 +1,21 @@ package com.backend.coapp.exception.auth; -public class UserServiceFailException extends RuntimeException { +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.SystemErrorCode; +import org.springframework.http.HttpStatus; + +public class UserServiceFailException extends ApiException { public UserServiceFailException(String message) { super("User service fail. " + message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.INTERNAL_SERVER_ERROR; + } + + @Override + public Object getErrorCode() { + return SystemErrorCode.INTERNAL_ERROR; + } } diff --git a/src/main/java/com/backend/coapp/exception/company/CompanyAlreadyExistsException.java b/src/main/java/com/backend/coapp/exception/company/CompanyAlreadyExistsException.java index 97059e8a..31561d87 100644 --- a/src/main/java/com/backend/coapp/exception/company/CompanyAlreadyExistsException.java +++ b/src/main/java/com/backend/coapp/exception/company/CompanyAlreadyExistsException.java @@ -1,10 +1,13 @@ package com.backend.coapp.exception.company; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.CompanyErrorCode; import lombok.Getter; +import org.springframework.http.HttpStatus; /** thrown when attempting to create a company that already exists */ @Getter -public class CompanyAlreadyExistsException extends RuntimeException { +public class CompanyAlreadyExistsException extends ApiException { private final String existingCompanyId; @@ -12,4 +15,14 @@ public CompanyAlreadyExistsException(String existingCompanyId) { super("A company with this name already exists."); this.existingCompanyId = existingCompanyId; } + + @Override + public HttpStatus getStatus() { + return HttpStatus.CONFLICT; + } + + @Override + public Object getErrorCode() { + return CompanyErrorCode.COMPANY_ALREADY_EXISTS; + } } diff --git a/src/main/java/com/backend/coapp/exception/company/CompanyNotFoundException.java b/src/main/java/com/backend/coapp/exception/company/CompanyNotFoundException.java index e112bef5..93727b9a 100644 --- a/src/main/java/com/backend/coapp/exception/company/CompanyNotFoundException.java +++ b/src/main/java/com/backend/coapp/exception/company/CompanyNotFoundException.java @@ -1,8 +1,22 @@ package com.backend.coapp.exception.company; -public class CompanyNotFoundException extends RuntimeException { +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.CompanyErrorCode; +import org.springframework.http.HttpStatus; + +public class CompanyNotFoundException extends ApiException { public CompanyNotFoundException() { super("Company with this companyId does not exist."); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.NOT_FOUND; + } + + @Override + public Object getErrorCode() { + return CompanyErrorCode.COMPANY_NOT_FOUND; + } } diff --git a/src/main/java/com/backend/coapp/exception/company/CompanyServiceFailException.java b/src/main/java/com/backend/coapp/exception/company/CompanyServiceFailException.java index f05fb754..3f862c0c 100644 --- a/src/main/java/com/backend/coapp/exception/company/CompanyServiceFailException.java +++ b/src/main/java/com/backend/coapp/exception/company/CompanyServiceFailException.java @@ -1,9 +1,23 @@ package com.backend.coapp.exception.company; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.SystemErrorCode; +import org.springframework.http.HttpStatus; + /** thrown when company service operations fail */ -public class CompanyServiceFailException extends RuntimeException { +public class CompanyServiceFailException extends ApiException { public CompanyServiceFailException(String message) { super(message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.INTERNAL_SERVER_ERROR; + } + + @Override + public Object getErrorCode() { + return SystemErrorCode.INTERNAL_ERROR; + } } diff --git a/src/main/java/com/backend/coapp/exception/company/InvalidWebsiteException.java b/src/main/java/com/backend/coapp/exception/company/InvalidWebsiteException.java index 7ef9d315..e0a31d29 100644 --- a/src/main/java/com/backend/coapp/exception/company/InvalidWebsiteException.java +++ b/src/main/java/com/backend/coapp/exception/company/InvalidWebsiteException.java @@ -1,9 +1,23 @@ package com.backend.coapp.exception.company; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.CompanyErrorCode; +import org.springframework.http.HttpStatus; + /** thrown when a website URL is invalid */ -public class InvalidWebsiteException extends RuntimeException { +public class InvalidWebsiteException extends ApiException { public InvalidWebsiteException() { super("The website URL is not valid."); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.BAD_REQUEST; + } + + @Override + public Object getErrorCode() { + return CompanyErrorCode.INVALID_WEBSITE; + } } diff --git a/src/main/java/com/backend/coapp/exception/genai/ConcurrencyException.java b/src/main/java/com/backend/coapp/exception/genai/ConcurrencyException.java index b7ac9ae9..65094f74 100644 --- a/src/main/java/com/backend/coapp/exception/genai/ConcurrencyException.java +++ b/src/main/java/com/backend/coapp/exception/genai/ConcurrencyException.java @@ -1,8 +1,22 @@ package com.backend.coapp.exception.genai; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.GenAIErrorCode; +import org.springframework.http.HttpStatus; + /** This exception will be thrown when user make more than 2 requests at the same time */ -public class ConcurrencyException extends RuntimeException { +public class ConcurrencyException extends ApiException { public ConcurrencyException(String message) { super(message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.CONFLICT; + } + + @Override + public Object getErrorCode() { + return GenAIErrorCode.OTHER_REQUEST_IN_PROGRESS; + } } diff --git a/src/main/java/com/backend/coapp/exception/genai/ExperienceNotFoundException.java b/src/main/java/com/backend/coapp/exception/genai/ExperienceNotFoundException.java index 99dc024e..3311bfdc 100644 --- a/src/main/java/com/backend/coapp/exception/genai/ExperienceNotFoundException.java +++ b/src/main/java/com/backend/coapp/exception/genai/ExperienceNotFoundException.java @@ -1,8 +1,22 @@ package com.backend.coapp.exception.genai; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.UserExperienceErrorCode; +import org.springframework.http.HttpStatus; + /** This exception will be thrown when experience request by client does NOT exist */ -public class ExperienceNotFoundException extends RuntimeException { +public class ExperienceNotFoundException extends ApiException { public ExperienceNotFoundException() { super("Experience does NOT exist."); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.NOT_FOUND; + } + + @Override + public Object getErrorCode() { + return UserExperienceErrorCode.EXPERIENCE_NOT_FOUND; + } } diff --git a/src/main/java/com/backend/coapp/exception/genai/ExperienceNotOwnedException.java b/src/main/java/com/backend/coapp/exception/genai/ExperienceNotOwnedException.java index 6f391594..999ef587 100644 --- a/src/main/java/com/backend/coapp/exception/genai/ExperienceNotOwnedException.java +++ b/src/main/java/com/backend/coapp/exception/genai/ExperienceNotOwnedException.java @@ -1,11 +1,25 @@ package com.backend.coapp.exception.genai; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.UserExperienceErrorCode; +import org.springframework.http.HttpStatus; + /** * This exception will be thrown when the user tries to update/delete the experience that doesn't * belong to the user. */ -public class ExperienceNotOwnedException extends RuntimeException { +public class ExperienceNotOwnedException extends ApiException { public ExperienceNotOwnedException(String message) { super("Experience record does not belong to this user. " + message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.FORBIDDEN; + } + + @Override + public Object getErrorCode() { + return UserExperienceErrorCode.EXPERIENCE_NOT_OWN; + } } diff --git a/src/main/java/com/backend/coapp/exception/genai/GenAIQuotaExceededException.java b/src/main/java/com/backend/coapp/exception/genai/GenAIQuotaExceededException.java index 12be3a30..2c5bcf13 100644 --- a/src/main/java/com/backend/coapp/exception/genai/GenAIQuotaExceededException.java +++ b/src/main/java/com/backend/coapp/exception/genai/GenAIQuotaExceededException.java @@ -1,9 +1,23 @@ package com.backend.coapp.exception.genai; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.GenAIErrorCode; +import org.springframework.http.HttpStatus; + /** This exception will be thrown when the user exceed request to use GenAI limit. */ -public class GenAIQuotaExceededException extends RuntimeException { +public class GenAIQuotaExceededException extends ApiException { public GenAIQuotaExceededException() { super( "You have reached monthly usage limit for Co-App chatbot. Please wait before making more requests."); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.TOO_MANY_REQUESTS; + } + + @Override + public Object getErrorCode() { + return GenAIErrorCode.OVER_LIMIT_CHATBOT_REQUEST; + } } diff --git a/src/main/java/com/backend/coapp/exception/genai/GenAIServiceException.java b/src/main/java/com/backend/coapp/exception/genai/GenAIServiceException.java index 5fe2e518..78261511 100644 --- a/src/main/java/com/backend/coapp/exception/genai/GenAIServiceException.java +++ b/src/main/java/com/backend/coapp/exception/genai/GenAIServiceException.java @@ -1,9 +1,23 @@ package com.backend.coapp.exception.genai; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.SystemErrorCode; +import org.springframework.http.HttpStatus; + /** * This is internal exception, which will be thrown when there is something wrong with GenAI service */ -public class GenAIServiceException extends RuntimeException { +public class GenAIServiceException extends ApiException { + @Override + public HttpStatus getStatus() { + return HttpStatus.INTERNAL_SERVER_ERROR; + } + + @Override + public Object getErrorCode() { + return SystemErrorCode.INTERNAL_ERROR; + } + public GenAIServiceException(String message) { super("GenAI service failed. " + message); } diff --git a/src/main/java/com/backend/coapp/exception/genai/GenAIUsageManagementServiceException.java b/src/main/java/com/backend/coapp/exception/genai/GenAIUsageManagementServiceException.java index 98e753ec..1bec66bf 100644 --- a/src/main/java/com/backend/coapp/exception/genai/GenAIUsageManagementServiceException.java +++ b/src/main/java/com/backend/coapp/exception/genai/GenAIUsageManagementServiceException.java @@ -1,7 +1,11 @@ package com.backend.coapp.exception.genai; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.SystemErrorCode; +import org.springframework.http.HttpStatus; + /** This exception will be thrown when something goes wrong in GenAI usage management service. */ -public class GenAIUsageManagementServiceException extends RuntimeException { +public class GenAIUsageManagementServiceException extends ApiException { public GenAIUsageManagementServiceException() { super("GenAI usage management failure."); } @@ -9,4 +13,14 @@ public GenAIUsageManagementServiceException() { public GenAIUsageManagementServiceException(String message) { super("GenAI usage management failure. " + message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.INTERNAL_SERVER_ERROR; + } + + @Override + public Object getErrorCode() { + return SystemErrorCode.INTERNAL_ERROR; + } } diff --git a/src/main/java/com/backend/coapp/exception/genai/OverCharacterLimitException.java b/src/main/java/com/backend/coapp/exception/genai/OverCharacterLimitException.java index e076ecb3..c4d036ea 100644 --- a/src/main/java/com/backend/coapp/exception/genai/OverCharacterLimitException.java +++ b/src/main/java/com/backend/coapp/exception/genai/OverCharacterLimitException.java @@ -1,7 +1,21 @@ package com.backend.coapp.exception.genai; -public class OverCharacterLimitException extends RuntimeException { +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.GenAIErrorCode; +import org.springframework.http.HttpStatus; + +public class OverCharacterLimitException extends ApiException { public OverCharacterLimitException(String message) { super(message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.BAD_REQUEST; + } + + @Override + public Object getErrorCode() { + return GenAIErrorCode.OVER_LIMIT_CHARACTER; + } } diff --git a/src/main/java/com/backend/coapp/exception/global/InvalidRequestException.java b/src/main/java/com/backend/coapp/exception/global/InvalidRequestException.java index 09e574fc..4228a7a9 100644 --- a/src/main/java/com/backend/coapp/exception/global/InvalidRequestException.java +++ b/src/main/java/com/backend/coapp/exception/global/InvalidRequestException.java @@ -1,7 +1,11 @@ package com.backend.coapp.exception.global; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.RequestErrorCode; +import org.springframework.http.HttpStatus; + /** This exception will be thrown when the request containing invalid input(s). */ -public class InvalidRequestException extends RuntimeException { +public class InvalidRequestException extends ApiException { public InvalidRequestException() { super("Invalid inputs of the request."); @@ -10,4 +14,14 @@ public InvalidRequestException() { public InvalidRequestException(String message) { super("Invalid inputs of the request. " + message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.BAD_REQUEST; + } + + @Override + public Object getErrorCode() { + return RequestErrorCode.REQUEST_HAS_NULL_OR_EMPTY_FIELD; + } } diff --git a/src/main/java/com/backend/coapp/exception/global/UserNotFoundException.java b/src/main/java/com/backend/coapp/exception/global/UserNotFoundException.java index d594c913..bfe200a3 100644 --- a/src/main/java/com/backend/coapp/exception/global/UserNotFoundException.java +++ b/src/main/java/com/backend/coapp/exception/global/UserNotFoundException.java @@ -1,7 +1,21 @@ package com.backend.coapp.exception.global; -public class UserNotFoundException extends RuntimeException { +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.UserErrorCode; +import org.springframework.http.HttpStatus; + +public class UserNotFoundException extends ApiException { public UserNotFoundException() { super("Could not find user"); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.BAD_REQUEST; + } + + @Override + public Object getErrorCode() { + return UserErrorCode.USER_NOT_EXIST; + } } diff --git a/src/main/java/com/backend/coapp/exception/review/ReviewAlreadyExistsException.java b/src/main/java/com/backend/coapp/exception/review/ReviewAlreadyExistsException.java index b7378329..e463a32d 100644 --- a/src/main/java/com/backend/coapp/exception/review/ReviewAlreadyExistsException.java +++ b/src/main/java/com/backend/coapp/exception/review/ReviewAlreadyExistsException.java @@ -1,12 +1,25 @@ package com.backend.coapp.exception.review; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.ReviewErrorCode; import lombok.Getter; +import org.springframework.http.HttpStatus; /** thrown when attempting to create a review that already exists for a user/company pair */ @Getter -public class ReviewAlreadyExistsException extends RuntimeException { +public class ReviewAlreadyExistsException extends ApiException { public ReviewAlreadyExistsException() { super("You have already submitted a review for this company."); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.CONFLICT; + } + + @Override + public Object getErrorCode() { + return ReviewErrorCode.REVIEW_ALREADY_EXISTS; + } } diff --git a/src/main/java/com/backend/coapp/exception/review/ReviewNotFoundException.java b/src/main/java/com/backend/coapp/exception/review/ReviewNotFoundException.java index b6c79b88..6d760391 100644 --- a/src/main/java/com/backend/coapp/exception/review/ReviewNotFoundException.java +++ b/src/main/java/com/backend/coapp/exception/review/ReviewNotFoundException.java @@ -1,9 +1,23 @@ package com.backend.coapp.exception.review; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.ReviewErrorCode; +import org.springframework.http.HttpStatus; + /** thrown when a review is not found */ -public class ReviewNotFoundException extends RuntimeException { +public class ReviewNotFoundException extends ApiException { public ReviewNotFoundException() { super("Review with the provided id does not exist for this company."); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.NOT_FOUND; + } + + @Override + public Object getErrorCode() { + return ReviewErrorCode.REVIEW_NOT_FOUND; + } } diff --git a/src/main/java/com/backend/coapp/exception/review/ReviewServiceFailException.java b/src/main/java/com/backend/coapp/exception/review/ReviewServiceFailException.java index dd026c7f..413d4a60 100644 --- a/src/main/java/com/backend/coapp/exception/review/ReviewServiceFailException.java +++ b/src/main/java/com/backend/coapp/exception/review/ReviewServiceFailException.java @@ -1,9 +1,23 @@ package com.backend.coapp.exception.review; +import com.backend.coapp.exception.ApiException; +import com.backend.coapp.model.enumeration.SystemErrorCode; +import org.springframework.http.HttpStatus; + /** thrown when review service operations fail */ -public class ReviewServiceFailException extends RuntimeException { +public class ReviewServiceFailException extends ApiException { public ReviewServiceFailException(String message) { super(message); } + + @Override + public HttpStatus getStatus() { + return HttpStatus.INTERNAL_SERVER_ERROR; + } + + @Override + public Object getErrorCode() { + return SystemErrorCode.INTERNAL_ERROR; + } } diff --git a/src/main/java/com/backend/coapp/handler/GlobalExceptionHandler.java b/src/main/java/com/backend/coapp/handler/GlobalExceptionHandler.java index 043d7f7a..7f302916 100644 --- a/src/main/java/com/backend/coapp/handler/GlobalExceptionHandler.java +++ b/src/main/java/com/backend/coapp/handler/GlobalExceptionHandler.java @@ -1,26 +1,16 @@ package com.backend.coapp.handler; +import com.backend.coapp.exception.ApiException; import com.backend.coapp.exception.application.*; import com.backend.coapp.exception.auth.*; -import com.backend.coapp.exception.company.CompanyAlreadyExistsException; -import com.backend.coapp.exception.company.CompanyNotFoundException; -import com.backend.coapp.exception.company.CompanyServiceFailException; -import com.backend.coapp.exception.company.InvalidWebsiteException; import com.backend.coapp.exception.genai.*; -import com.backend.coapp.exception.global.InvalidRequestException; -import com.backend.coapp.exception.global.UserNotFoundException; -import com.backend.coapp.exception.review.ReviewAlreadyExistsException; -import com.backend.coapp.exception.review.ReviewNotFoundException; -import com.backend.coapp.exception.review.ReviewServiceFailException; import com.backend.coapp.model.enumeration.*; import java.time.LocalDate; -import java.util.HashMap; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.security.core.AuthenticationException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import tools.jackson.databind.exc.InvalidFormatException; @@ -30,336 +20,28 @@ @Slf4j public class GlobalExceptionHandler { - @ExceptionHandler(InvalidRequestException.class) - public ResponseEntity> handleInvalidRequest(InvalidRequestException ex) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body( - Map.of( - "error", - RequestErrorCode.REQUEST_HAS_NULL_OR_EMPTY_FIELD, - "message", - ex.getMessage())); - } - - @ExceptionHandler(AuthEmailNotRegisteredException.class) - public ResponseEntity> handleEmailNotRegistered( - AuthEmailNotRegisteredException ex) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body(Map.of("error", AuthErrorCode.EMAIL_NOT_REGISTERED, "message", ex.getMessage())); - } - - @ExceptionHandler(EmailServiceException.class) - public ResponseEntity> handleEmailServiceException(EmailServiceException ex) { - String errorMessage = "ERROR: Email service failed: " + ex.getMessage(); - log.error(errorMessage); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body( - Map.of( - "error", - SystemErrorCode.INTERNAL_ERROR, - "message", - "Unable to send verification email. Please try again later.")); - } - - @ExceptionHandler(RuntimeException.class) - public ResponseEntity> handleRuntimeException(RuntimeException ex) { - String errorMessage = "ERROR: Reset verification code service failed: " + ex.getMessage(); - log.error(errorMessage); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body( - Map.of( - "error", - SystemErrorCode.INTERNAL_ERROR, - "message", - "Internal failure. Please try again later.")); - } - - @ExceptionHandler(EmailInvalidAddressException.class) - public ResponseEntity> handleEmailInvalidAddressException( - EmailInvalidAddressException ex) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body(Map.of("error", AuthErrorCode.INVALID_EMAIL, "message", ex.getMessage())); - } - - @ExceptionHandler(AuthEmailAlreadyUsedException.class) - public ResponseEntity> handleAuthEmailAlreadyUsedException( - AuthEmailAlreadyUsedException ex) { - return ResponseEntity.status(409) - .body(Map.of("error", AuthErrorCode.EXIST_ACCOUNT_WITH_EMAIL, "message", ex.getMessage())); - } - - @ExceptionHandler(AuthAccountNotYetActivatedException.class) - public ResponseEntity> handleAuthAccountNotYetActivatedException( - AuthAccountNotYetActivatedException ex) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(Map.of("error", AuthErrorCode.ACCOUNT_NOT_ACTIVATED, "message", ex.getMessage())); - } - - @ExceptionHandler(IncorrectCodeException.class) - public ResponseEntity> handleIncorrectCodeException( - IncorrectCodeException ex) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body(Map.of("error", AuthErrorCode.INVALID_CONFIRMATION_CODE, "message", ex.getMessage())); - } - - @ExceptionHandler(AuthAccountAlreadyVerifyException.class) - public ResponseEntity> handleAuthAccountAlreadyVerifyException( - AuthAccountAlreadyVerifyException ex) { - return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED) - .body(Map.of("error", AuthErrorCode.ACCOUNT_ALREADY_VERIFIED, "message", ex.getMessage())); - } - - @ExceptionHandler(AuthenticationException.class) - public ResponseEntity> handleAuthenticationException( - AuthenticationException ex) { - String errorMessage = "ERROR: JWT Service failed: " + ex.getMessage(); - log.error(errorMessage); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body( - Map.of( - "error", - SystemErrorCode.INTERNAL_ERROR, - "message", - "Authentication failed. Please try again later.")); - } - - @ExceptionHandler(AuthBadCredentialException.class) - public ResponseEntity> handleAuthBadCredentialException( - AuthBadCredentialException ex) { - - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(Map.of("error", AuthErrorCode.INVALID_EMAIL_OR_PASSWORD, "message", ex.getMessage())); - } - - @ExceptionHandler(JwtServiceFailException.class) - public ResponseEntity> handleJwtServiceFailException( - JwtServiceFailException ex) { - String errorMessage = "ERROR: JWT Service failed: " + ex.getMessage(); - log.error(errorMessage); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body( - Map.of( - "error", - SystemErrorCode.INTERNAL_ERROR, - "message", - "Authentication failed. Please try again.")); - } - - @ExceptionHandler(UserServiceFailException.class) - public ResponseEntity> handleUserServiceFailException( - UserServiceFailException ex) { - String errorMessage = "ERROR: User Service failed: " + ex.getMessage(); - log.error(errorMessage); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body( - Map.of( - "error", - SystemErrorCode.INTERNAL_ERROR, - "message", - "User service fail. Please try again.")); - } - - @ExceptionHandler(CompanyAlreadyExistsException.class) - public ResponseEntity> handleCompanyAlreadyExistsException( - CompanyAlreadyExistsException ex) { - Map response = new HashMap<>(); - response.put("error", CompanyErrorCode.COMPANY_ALREADY_EXISTS); - response.put("message", ex.getMessage()); - return ResponseEntity.status(HttpStatus.CONFLICT).body(response); - } - - @ExceptionHandler(CompanyNotFoundException.class) - public ResponseEntity> handleCompanyNotFoundException( - CompanyNotFoundException ex) { - return ResponseEntity.status(HttpStatus.NOT_FOUND) - .body(Map.of("error", CompanyErrorCode.COMPANY_NOT_FOUND, "message", ex.getMessage())); - } - - @ExceptionHandler(InvalidWebsiteException.class) - public ResponseEntity> handleInvalidWebsiteException( - InvalidWebsiteException ex) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body(Map.of("error", CompanyErrorCode.INVALID_WEBSITE, "message", ex.getMessage())); - } - - @ExceptionHandler(CompanyServiceFailException.class) - public ResponseEntity> handleCompanyServiceFailException( - CompanyServiceFailException ex) { - String errorMessage = "ERROR: Company Service failed: " + ex.getMessage(); - log.error(errorMessage); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body( - Map.of( - "error", - SystemErrorCode.INTERNAL_ERROR, - "message", - "An unexpected error occurred while processing your request.")); - } - - @ExceptionHandler(DuplicateApplicationException.class) - public ResponseEntity> handleDuplicateApplicationException( - DuplicateApplicationException ex) { - return ResponseEntity.status(HttpStatus.CONFLICT) - .body( - Map.of( - "error", ApplicationErrorCode.DUPLICATE_APPLICATION, "message", ex.getMessage())); - } - - @ExceptionHandler(ApplicationNotFoundException.class) - public ResponseEntity> handleApplicationNotFoundException( - ApplicationNotFoundException ex) { - return ResponseEntity.status(HttpStatus.NOT_FOUND) - .body( - Map.of( - "error", ApplicationErrorCode.APPLICATION_NOT_FOUND, "message", ex.getMessage())); - } - - @ExceptionHandler(UnauthorizedApplicationAccessException.class) - public ResponseEntity> handleUnauthorizedApplicationAccessException( - UnauthorizedApplicationAccessException ex) { - return ResponseEntity.status(HttpStatus.FORBIDDEN) - .body( - Map.of( - "error", - ApplicationErrorCode.UNAUTHORIZED_APPLICATION_ACCESS, - "message", - ex.getMessage())); - } - - @ExceptionHandler(ApplicationServiceFailException.class) - public ResponseEntity> handleApplicationServiceFailException( - ApplicationServiceFailException ex) { - String errorMessage = "ERROR: Application Service failed: " + ex.getMessage(); - log.error(errorMessage); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body( - Map.of( - "error", - SystemErrorCode.INTERNAL_ERROR, - "message", - "An unexpected error occurred while processing your application.")); - } - - @ExceptionHandler(ReviewAlreadyExistsException.class) - public ResponseEntity> handleReviewAlreadyExistsException( - ReviewAlreadyExistsException ex) { - Map response = new HashMap<>(); - response.put("error", ReviewErrorCode.REVIEW_ALREADY_EXISTS); - response.put("message", ex.getMessage()); - return ResponseEntity.status(HttpStatus.CONFLICT).body(response); - } - - @ExceptionHandler(ReviewNotFoundException.class) - public ResponseEntity> handleReviewNotFoundException( - ReviewNotFoundException ex) { - return ResponseEntity.status(HttpStatus.NOT_FOUND) - .body(Map.of("error", ReviewErrorCode.REVIEW_NOT_FOUND, "message", ex.getMessage())); - } - - @ExceptionHandler(ReviewServiceFailException.class) - public ResponseEntity> handleReviewServiceFailException( - ReviewServiceFailException ex) { - String errorMessage = "ERROR: Review Service failed: " + ex.getMessage(); - log.error(errorMessage); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body( - Map.of( - "error", - SystemErrorCode.INTERNAL_ERROR, - "message", - "An unexpected error occurred while processing your review.")); - } - - @ExceptionHandler(UserNotFoundException.class) - public ResponseEntity> handleUserNotExitException(UserNotFoundException ex) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body(Map.of("error", UserErrorCode.USER_NOT_EXIST, "message", ex.getMessage())); - } - - @ExceptionHandler(UserInvalidPasswordChangeException.class) - public ResponseEntity> handleUserUpdateSamePasswordException( - UserInvalidPasswordChangeException ex) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body( - Map.of( - "error", - UserErrorCode.NEW_PASSWORD_SAME_WITH_OLD_PASSWORD, - "message", - ex.getMessage())); - } - - @ExceptionHandler(NoChangesDetectedException.class) - public ResponseEntity> handleNoChangesDetectedException( - NoChangesDetectedException ex) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body( - Map.of( - "error", - ApplicationErrorCode.NO_CHANGE_DETECTED_TO_UPDATE, - "message", - ex.getMessage())); - } - - @ExceptionHandler(OverCharacterLimitException.class) - public ResponseEntity> handleOverCharacterLimitException( - OverCharacterLimitException ex) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body(Map.of("error", GenAIErrorCode.OVER_LIMIT_CHARACTER, "message", ex.getMessage())); - } - - @ExceptionHandler(ApplicationNotOwnedException.class) - public ResponseEntity> handleApplicationNotOwnedException( - ApplicationNotOwnedException ex) { - return ResponseEntity.status(HttpStatus.FORBIDDEN) - .body( - Map.of("error", ApplicationErrorCode.APPLICATION_NOT_OWN, "message", ex.getMessage())); - } - - @ExceptionHandler(GenAIQuotaExceededException.class) - public ResponseEntity> handleGenAIQuotaExceededException( - GenAIQuotaExceededException ex) { - return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS) - .body( - Map.of("error", GenAIErrorCode.OVER_LIMIT_CHATBOT_REQUEST, "message", ex.getMessage())); - } - - @ExceptionHandler(ConcurrencyException.class) - public ResponseEntity> handleConcurrencyException(ConcurrencyException ex) { - return ResponseEntity.status(HttpStatus.CONFLICT) - .body( - Map.of("error", GenAIErrorCode.OTHER_REQUEST_IN_PROGRESS, "message", ex.getMessage())); - } - - @ExceptionHandler(GenAIUsageManagementServiceException.class) - public ResponseEntity> handleGenAIUsageManagementServiceException( - GenAIUsageManagementServiceException ex) { - String errorMessage = "ERROR: Application Service failed: " + ex.getMessage(); - log.error(errorMessage); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body( - Map.of( - "error", - SystemErrorCode.INTERNAL_ERROR, - "message", - "An unexpected error occurred while processing your request. Please try again later.")); + @ExceptionHandler(ApiException.class) + public ResponseEntity> handleApiException(ApiException ex) { + String responseMessage = ex.getMessage(); + if (ex.getStatus() == HttpStatus.INTERNAL_SERVER_ERROR) { + log.error("Failure %s".formatted(ex.getMessage())); + responseMessage = "Service is unavailable. Please try again later."; + } + return errorResponse(ex.getStatus(), ex.getErrorCode(), responseMessage); } - @ExceptionHandler(ExperienceNotOwnedException.class) - public ResponseEntity> handleExperienceNotOwnedException( - ExperienceNotOwnedException ex) { - return ResponseEntity.status(HttpStatus.FORBIDDEN) - .body( - Map.of( - "error", UserExperienceErrorCode.EXPERIENCE_NOT_OWN, "message", ex.getMessage())); + @ExceptionHandler(Exception.class) + public ResponseEntity> handleUnknown(Exception ex) { + log.error("Unhandled exception", ex); + return errorResponse( + HttpStatus.INTERNAL_SERVER_ERROR, + SystemErrorCode.INTERNAL_ERROR, + "An unexpected error occurred."); } - @ExceptionHandler(ExperienceNotFoundException.class) - public ResponseEntity> handleExperienceNotFoundException( - ExperienceNotFoundException ex) { - return ResponseEntity.status(HttpStatus.NOT_FOUND) - .body( - Map.of( - "error", UserExperienceErrorCode.EXPERIENCE_NOT_FOUND, "message", ex.getMessage())); + private ResponseEntity> errorResponse( + HttpStatus status, Object code, String message) { + return ResponseEntity.status(status).body(Map.of("error", code, "message", message)); } @ExceptionHandler(HttpMessageNotReadableException.class) diff --git a/src/test/java/com/backend/coapp/controller/GenAIResumeAdvisorControllerTest.java b/src/test/java/com/backend/coapp/controller/GenAIResumeAdvisorControllerTest.java index ba143ba1..1b2b7921 100644 --- a/src/test/java/com/backend/coapp/controller/GenAIResumeAdvisorControllerTest.java +++ b/src/test/java/com/backend/coapp/controller/GenAIResumeAdvisorControllerTest.java @@ -11,10 +11,7 @@ import com.backend.coapp.dto.request.GenAIResumeAdvisorRequest; import com.backend.coapp.exception.application.ApplicationNotFoundException; import com.backend.coapp.exception.application.ApplicationNotOwnedException; -import com.backend.coapp.exception.genai.ConcurrencyException; -import com.backend.coapp.exception.genai.GenAIQuotaExceededException; -import com.backend.coapp.exception.genai.GenAIUsageManagementServiceException; -import com.backend.coapp.exception.genai.OverCharacterLimitException; +import com.backend.coapp.exception.genai.*; import com.backend.coapp.exception.global.UserNotFoundException; import com.backend.coapp.model.document.UserModel; import com.backend.coapp.model.enumeration.SystemErrorCode; @@ -256,6 +253,24 @@ void resumeAdvisor_whenServiceFails_expect500() throws Exception { .getAdvice(mockUser.getId(), validRequest.getApplicationId(), validRequest.getUserPrompt()); } + @Test + void resumeAdvisor_whenGenAIServiceFails_expect500() throws Exception { + when(genAIResumeAdvisorService.getAdvice(anyString(), any(), anyString())) + .thenThrow(new GenAIServiceException("Internal error")); + + mockMvc + .perform( + post("/api/resume-ai-advisor") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(validRequest)) + .principal(this.authentication)) + .andExpect(status().isInternalServerError()) + .andExpect(jsonPath("$.error").value(SystemErrorCode.INTERNAL_ERROR.name())); + + verify(genAIResumeAdvisorService, times(1)) + .getAdvice(mockUser.getId(), validRequest.getApplicationId(), validRequest.getUserPrompt()); + } + @Test void getRemainingQuota_whenSuccess_expectOkAndRemainingQuota() throws Exception { when(genAIUsageManagementService.getNumberOfRequestLeft(anyString())).thenReturn(7);