diff --git a/src/main/java/git/tracehub/pmo/controller/AdviceController.java b/src/main/java/git/tracehub/pmo/controller/AdviceController.java index 2e423fd..964cd66 100644 --- a/src/main/java/git/tracehub/pmo/controller/AdviceController.java +++ b/src/main/java/git/tracehub/pmo/controller/AdviceController.java @@ -83,6 +83,26 @@ public ResponseEntity handle( ).value(); } + /** + * Handle IllegalArgumentException. + * + * @param exception Exception + * @return ResponseEntity + */ + @ExceptionHandler(IllegalArgumentException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseEntity handle( + final IllegalArgumentException exception + ) { + return new Logged( + exception, + new RestError( + exception.getMessage(), + HttpStatus.BAD_REQUEST + ) + ).value(); + } + /** * Handle ResourceNotFoundException. * diff --git a/src/main/java/git/tracehub/pmo/controller/TicketController.java b/src/main/java/git/tracehub/pmo/controller/TicketController.java index fbef526..db602ac 100644 --- a/src/main/java/git/tracehub/pmo/controller/TicketController.java +++ b/src/main/java/git/tracehub/pmo/controller/TicketController.java @@ -22,6 +22,7 @@ import git.tracehub.pmo.ticket.Ticket; import git.tracehub.pmo.ticket.Tickets; import jakarta.validation.Valid; +import java.util.Optional; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; @@ -50,31 +51,28 @@ public class TicketController { /** * Ticket by path to job and repository. * - * @param job Path to job * @param repo Repository name - * @return Ticket - */ - @GetMapping("/job") - public Ticket byJob( - @RequestParam final String job, - @RequestParam final String repo - ) { - return this.tickets.byJob(job, repo); - } - - /** - * Ticket by issue number and repository. - * + * @param job Path to job * @param number Issue number - * @param repo Repository name * @return Ticket */ - @GetMapping("/number") - public Ticket byNumber( - @RequestParam final Integer number, - @RequestParam final String repo + @GetMapping + public Ticket byJob( + @RequestParam final String repo, + @RequestParam final Optional job, + @RequestParam final Optional number ) { - return this.tickets.byNumber(number, repo); + final Ticket ticket; + if (job.isPresent()) { + ticket = this.tickets.byJob(job.get(), repo); + } else if (number.isPresent()) { + ticket = this.tickets.byNumber(number.get(), repo); + } else { + throw new IllegalArgumentException( + "Either job or number must be present" + ); + } + return ticket; } /** diff --git a/src/test/java/git/tracehub/pmo/controller/AdviceControllerTest.java b/src/test/java/git/tracehub/pmo/controller/AdviceControllerTest.java index e8a5e7d..4632b26 100644 --- a/src/test/java/git/tracehub/pmo/controller/AdviceControllerTest.java +++ b/src/test/java/git/tracehub/pmo/controller/AdviceControllerTest.java @@ -64,6 +64,20 @@ void handlesResourceAlreadyExistsException() { ); } + @Test + void handlesIllegalArgumentException() { + final String message = "IllegalArgumentException"; + MatcherAssert.assertThat( + "IllegalArgumentException isn't handled", + new AdviceController().handle( + new IllegalArgumentException(message) + ), + new IsEqual<>( + new RestError(message, HttpStatus.BAD_REQUEST).value() + ) + ); + } + @Test void handlesResourceNotFoundException() { final String message = "ResourceNotFoundException"; diff --git a/src/test/java/git/tracehub/pmo/controller/TicketControllerTest.java b/src/test/java/git/tracehub/pmo/controller/TicketControllerTest.java index b2be4e9..1ad8cb1 100644 --- a/src/test/java/git/tracehub/pmo/controller/TicketControllerTest.java +++ b/src/test/java/git/tracehub/pmo/controller/TicketControllerTest.java @@ -66,7 +66,7 @@ final class TicketControllerTest { @Test void returnsForbiddenOnUnauthorizedUser() throws Exception { this.mvc.perform( - MockMvcRequestBuilders.post("%s/job".formatted(TicketControllerTest.URL)) + MockMvcRequestBuilders.post(TicketControllerTest.URL) .contentType(MediaType.APPLICATION_JSON) ).andExpect(MockMvcResultMatchers.status().isForbidden()); } @@ -74,7 +74,7 @@ void returnsForbiddenOnUnauthorizedUser() throws Exception { @Test void returnsTicketByJob() throws Exception { this.mvc.perform( - MockMvcRequestBuilders.get("%s/job".formatted(TicketControllerTest.URL)) + MockMvcRequestBuilders.get(TicketControllerTest.URL) .param("job", "path/to/job") .param("repo", "user/repo") .with(SecurityMockMvcRequestPostProcessors.jwt()) @@ -85,7 +85,7 @@ void returnsTicketByJob() throws Exception { @Test void returnsTicketByIssueNumber() throws Exception { this.mvc.perform( - MockMvcRequestBuilders.get("%s/number".formatted(TicketControllerTest.URL)) + MockMvcRequestBuilders.get(TicketControllerTest.URL) .param("number", "1") .param("repo", "user/repo") .with(SecurityMockMvcRequestPostProcessors.jwt()) @@ -132,4 +132,21 @@ void throwsOnInvalidRequestBody() throws Exception { ); } + @Test + void throwsOnEmptyRequestParams() throws Exception { + this.mvc.perform( + MockMvcRequestBuilders.get(TicketControllerTest.URL) + .param("repo", "user/repo") + .with(SecurityMockMvcRequestPostProcessors.jwt()) + .contentType(MediaType.APPLICATION_JSON) + ).andExpect(MockMvcResultMatchers.status().isBadRequest()) + .andExpect( + MockMvcResultMatchers.content().string( + new MutableJson() + .with("message", "Either job or number must be present") + .toString() + ) + ); + } + } diff --git a/src/test/java/it/web/RetrieveTicketByJobITCase.java b/src/test/java/it/web/RetrieveTicketByJobITCase.java index 68993dc..6a37e98 100644 --- a/src/test/java/it/web/RetrieveTicketByJobITCase.java +++ b/src/test/java/it/web/RetrieveTicketByJobITCase.java @@ -52,7 +52,7 @@ final class RetrieveTicketByJobITCase /** * Raw Endpoint. */ - private static final String RAW = "http://localhost:%s/tickets/job"; + private static final String RAW = "http://localhost:%s/tickets"; /** * Application Port. @@ -127,4 +127,41 @@ void throwsOnInvalidJob() throws Exception { ); } + @Test + void throwsOnEmptyJob() throws Exception { + final String repo = "user/test"; + final Response response = new JdkRequest( + RetrieveTicketByJobITCase.RAW.formatted(this.port) + ).uri() + .queryParam("repo", repo) + .back() + .method(Request.GET) + .header( + HttpHeaders.AUTHORIZATION, + "Bearer %s".formatted( + new KeycloakToken( + KeycloakIntegration.KEYCLOAK.getAuthServerUrl() + ).value() + ) + ).fetch(); + MatcherAssert.assertThat( + "Response Status %s does not match to expected one".formatted( + response.status() + ), + response.status(), + new IsEqual<>(400) + ); + MatcherAssert.assertThat( + "Message %s isn't correct".formatted(response.body()), + response.body(), + new IsEqual<>( + new MutableJson() + .with( + "message", + "Either job or number must be present" + ).toString() + ) + ); + } + } diff --git a/src/test/java/it/web/RetrieveTicketByNumberITCase.java b/src/test/java/it/web/RetrieveTicketByNumberITCase.java index 61a52c4..5ae3034 100644 --- a/src/test/java/it/web/RetrieveTicketByNumberITCase.java +++ b/src/test/java/it/web/RetrieveTicketByNumberITCase.java @@ -52,7 +52,7 @@ final class RetrieveTicketByNumberITCase /** * Raw Endpoint. */ - private static final String RAW = "http://localhost:%s/tickets/number"; + private static final String RAW = "http://localhost:%s/tickets"; /** * Application Port. @@ -62,7 +62,7 @@ final class RetrieveTicketByNumberITCase @Test @Sql("classpath:pre/sql/projects.sql") - void retrievesTicketByJobSuccessfully() throws Exception { + void retrievesTicketByIssueNumberSuccessfully() throws Exception { final Response response = new JdkRequest( RetrieveTicketByNumberITCase.RAW.formatted(this.port) ).uri() @@ -127,4 +127,41 @@ void throwsOnInvalidNumber() throws Exception { ); } + @Test + void throwsOnEmptyNumber() throws Exception { + final String repo = "user/test"; + final Response response = new JdkRequest( + RetrieveTicketByNumberITCase.RAW.formatted(this.port) + ).uri() + .queryParam("repo", repo) + .back() + .method(Request.GET) + .header( + HttpHeaders.AUTHORIZATION, + "Bearer %s".formatted( + new KeycloakToken( + KeycloakIntegration.KEYCLOAK.getAuthServerUrl() + ).value() + ) + ).fetch(); + MatcherAssert.assertThat( + "Response Status %s does not match to expected one".formatted( + response.status() + ), + response.status(), + new IsEqual<>(400) + ); + MatcherAssert.assertThat( + "Message %s isn't correct".formatted(response.body()), + response.body(), + new IsEqual<>( + new MutableJson() + .with( + "message", + "Either job or number must be present" + ).toString() + ) + ); + } + }