Skip to content

Commit

Permalink
Use @ServerExceptionMapper instead of JaxRS ExceptionMappers to fix m…
Browse files Browse the repository at this point in the history
…igrating to `quarkus-rest-jackson`. Also implements some simplification of error handling etc. There can still be some improvements but this is passing tests for now.

Signed-off-by: appiepollo14 <[email protected]>
  • Loading branch information
appiepollo14 committed Oct 19, 2024
1 parent 1d17379 commit bdeb1ab
Show file tree
Hide file tree
Showing 16 changed files with 83 additions and 137 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
<artifactId>quarkus-rest-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,24 @@

import com.fasterxml.jackson.annotation.JsonRootName;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
import org.example.realworldapi.domain.model.constants.ValidationMessages;
import org.example.realworldapi.domain.model.user.CreateUserInput;

@Getter
@Setter
@JsonRootName("user")
@RegisterForReflection
public class NewUserRequest {
@NotBlank(message = ValidationMessages.USERNAME_MUST_BE_NOT_BLANK)
private String username;

@NotBlank(message = ValidationMessages.EMAIL_MUST_BE_NOT_BLANK)
private String email;

@NotBlank(message = ValidationMessages.PASSWORD_MUST_BE_NOT_BLANK)
private String password;

public CreateUserInput toCreateUserInput() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import lombok.AllArgsConstructor;
Expand Down Expand Up @@ -37,8 +36,7 @@ public class UsersResource {
@Produces(MediaType.APPLICATION_JSON)
public Response create(
@Valid @NotNull(message = ValidationMessages.REQUEST_BODY_MUST_BE_NOT_NULL)
NewUserRequest newUserRequest,
@Context SecurityException context) {
NewUserRequest newUserRequest) {
final var user = createUser.handle(newUserRequest.toCreateUserInput());
final var token = tokenProvider.createUserToken(user.getId().toString());
return Response.ok(new UserResponse(user, token)).status(Response.Status.CREATED).build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
package org.example.realworldapi.domain.exception;

public class ArticleNotFoundException extends BusinessException {

public ArticleNotFoundException() {
super(5, "article not found");
}
}
public class ArticleNotFoundException extends RuntimeException {}
Original file line number Diff line number Diff line change
@@ -1,25 +1,12 @@
package org.example.realworldapi.domain.exception;

import static java.util.Collections.singletonList;

import java.util.LinkedList;
import java.util.List;
import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
@EqualsAndHashCode(callSuper = false)
public class BusinessException extends RuntimeException {

private final int code;
private final List<String> messages;

public BusinessException(int code, String message) {
super(message);
this.code = code;
this.messages = new LinkedList<>(singletonList(message));
}

public BusinessException(int code, List<String> messages) {
super(String.join(", ", messages));
this.code = code;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
package org.example.realworldapi.domain.exception;

public class CommentNotFoundException extends BusinessException {
public CommentNotFoundException() {
super(6, "comment not found");
}
}
public class CommentNotFoundException extends RuntimeException {}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package org.example.realworldapi.domain.exception;

public class EmailAlreadyExistsException extends BusinessException {

public class EmailAlreadyExistsException extends RuntimeException {
public EmailAlreadyExistsException() {
super(3, "email already exists");
super("email already exists");
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
package org.example.realworldapi.domain.exception;

public class InvalidPasswordException extends BusinessException {
public InvalidPasswordException() {
super(4, "invalid password");
}
}
public class InvalidPasswordException extends RuntimeException {}
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
package org.example.realworldapi.domain.exception;

public class TagNotFoundException extends BusinessException {

public TagNotFoundException() {
super(7, "tag not found");
}
}
public class TagNotFoundException extends RuntimeException {}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package org.example.realworldapi.domain.exception;

public class UserNotFoundException extends BusinessException {

public class UserNotFoundException extends RuntimeException {
public UserNotFoundException() {
super(1, "user not found");
super("user not found");
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package org.example.realworldapi.domain.exception;

public class UsernameAlreadyExistsException extends BusinessException {

public class UsernameAlreadyExistsException extends RuntimeException {
public UsernameAlreadyExistsException() {
super(2, "username already exists");
super("username already exists");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@ public Response toResponse(ConstraintViolationException e) {

e.getConstraintViolations()
.iterator()
.forEachRemaining(
contraint -> {
errorResponse.getBody().add(contraint.getMessage());
});
.forEachRemaining(constraint -> errorResponse.getBody().add(constraint.getMessage()));

return Response.ok(errorResponse).status(422).build();
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.example.realworldapi.infrastructure.web.mapper;

import jakarta.ws.rs.core.Response;
import org.example.realworldapi.application.web.model.response.ErrorResponse;
import org.example.realworldapi.domain.exception.*;
import org.jboss.resteasy.reactive.server.ServerExceptionMapper;

public class ExceptionMappers {

@ServerExceptionMapper
public Response emailAlreadyExists(EmailAlreadyExistsException e) {
return Response.ok(errorResponse(e)).status(Response.Status.CONFLICT.getStatusCode()).build();
}

@ServerExceptionMapper
public Response usernameAlreadyExists(UsernameAlreadyExistsException e) {
return Response.ok(errorResponse(e)).status(Response.Status.CONFLICT.getStatusCode()).build();
}

@ServerExceptionMapper
public Response userNotfound(UserNotFoundException e) {
return Response.ok(errorResponse(e)).status(Response.Status.NOT_FOUND.getStatusCode()).build();
}

@ServerExceptionMapper
public Response invalidPassword(InvalidPasswordException e) {
return Response.ok(errorResponse(e))
.status(Response.Status.UNAUTHORIZED.getStatusCode())
.build();
}

@ServerExceptionMapper
public Response tagNotFound(TagNotFoundException e) {
return Response.ok(errorResponse(e)).status(Response.Status.NOT_FOUND.getStatusCode()).build();
}

@ServerExceptionMapper
public Response articleNotFound(ArticleNotFoundException e) {
return Response.ok(errorResponse(e)).status(Response.Status.NOT_FOUND.getStatusCode()).build();
}

@ServerExceptionMapper
public Response modelValidation(ModelValidationException e) {
return Response.ok(errorResponse(e)).status(422).build();
}

private ErrorResponse errorResponse(RuntimeException e) {
return new ErrorResponse(e.getMessage());
}
}
2 changes: 1 addition & 1 deletion src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
quarkus.resteasy.path=/api
quarkus.rest.path=/api

# Database configuration
quarkus.datasource.db-kind=postgresql
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@
import org.junit.jupiter.api.Test;

@QuarkusTest
public class UsersResourceIntegrationTest extends AbstractIntegrationTest {
class UsersResourceIntegrationTest extends AbstractIntegrationTest {

private final String USERS_RESOURCE_PATH = API_PREFIX + "/users";
private final String LOGIN_PATH = USERS_RESOURCE_PATH + "/login";

@Test
public void
givenAValidUser_whenCallingRegisterUserEndpoint_thenReturnAnUserWithTokenFieldAndCode201()
throws JsonProcessingException {
void givenAValidUser_whenCallingRegisterUserEndpoint_thenReturnAnUserWithTokenFieldAndCode201()
throws JsonProcessingException {

NewUserRequest newUser = new NewUserRequest();
newUser.setUsername("user");
Expand Down Expand Up @@ -56,9 +55,8 @@ public class UsersResourceIntegrationTest extends AbstractIntegrationTest {
}

@Test
public void
givenAPersistedUser_whenCallingRegisterUserEndpointWithExistingEmail_thenReturnCode409()
throws JsonProcessingException {
void givenAPersistedUser_whenCallingRegisterUserEndpointWithExistingEmail_thenReturnCode409()
throws JsonProcessingException {

String userPassword = "123";

Expand All @@ -80,9 +78,8 @@ public class UsersResourceIntegrationTest extends AbstractIntegrationTest {
}

@Test
public void
givenAPersistedUser_whenCallingRegisterUserEndpointWithExistingUsername_thenReturnCode409()
throws JsonProcessingException {
void givenAPersistedUser_whenCallingRegisterUserEndpointWithExistingUsername_thenReturnCode409()
throws JsonProcessingException {

String userPassword = "123";

Expand All @@ -104,7 +101,7 @@ public class UsersResourceIntegrationTest extends AbstractIntegrationTest {
}

@Test
public void givenAnInvalidUser_thenReturnErrorsWith422Code() throws JsonProcessingException {
void givenAnInvalidUser_thenReturnErrorsWith422Code() throws JsonProcessingException {

NewUserRequest newUser = new NewUserRequest();

Expand All @@ -126,7 +123,7 @@ public void givenAnInvalidUser_thenReturnErrorsWith422Code() throws JsonProcessi
}

@Test
public void givenAnInvalidEmail_thenReturnErrorsWith422Code() throws JsonProcessingException {
void givenAnInvalidEmail_thenReturnErrorsWith422Code() throws JsonProcessingException {

NewUserRequest newUser = new NewUserRequest();
newUser.setUsername("username");
Expand All @@ -148,7 +145,7 @@ public void givenAnInvalidEmail_thenReturnErrorsWith422Code() throws JsonProcess
}

@Test
public void givenAInvalidLogin_whenExecuteLoginEndpoint_shouldReturnErrorsWith422Code()
void givenAInvalidLogin_whenExecuteLoginEndpoint_shouldReturnErrorsWith422Code()
throws JsonProcessingException {

LoginRequest loginRequest = new LoginRequest();
Expand All @@ -164,7 +161,7 @@ public void givenAInvalidLogin_whenExecuteLoginEndpoint_shouldReturnErrorsWith42
}

@Test
public void givenAInvalidLoginEmail_whenExecuteLoginEndpoint_shouldReturnUnauthorized()
void givenAInvalidLoginEmail_whenExecuteLoginEndpoint_shouldReturnUnauthorized()
throws JsonProcessingException {

String userPassword = "123";
Expand All @@ -184,7 +181,7 @@ public void givenAInvalidLoginEmail_whenExecuteLoginEndpoint_shouldReturnUnautho
}

@Test
public void givenAInvalidLoginPassword_whenExecuteLoginEndpoint_shouldReturnUnauthorized()
void givenAInvalidLoginPassword_whenExecuteLoginEndpoint_shouldReturnUnauthorized()
throws JsonProcessingException {

final var user = createUserEntity("user1", "[email protected]", "123", "bio", "image");
Expand Down

0 comments on commit bdeb1ab

Please sign in to comment.