diff --git a/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/GlobalExceptionHandler.java b/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/GlobalExceptionHandler.java index 9c4ba025e..5aa509079 100644 --- a/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/GlobalExceptionHandler.java +++ b/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/GlobalExceptionHandler.java @@ -1,13 +1,13 @@ package org.finos.vuu.layoutserver.controller; +import java.util.NoSuchElementException; import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; -import java.util.NoSuchElementException; - @ControllerAdvice public class GlobalExceptionHandler { @@ -16,8 +16,12 @@ public ResponseEntity handleNotFound(NoSuchElementException ex) { return new ResponseEntity<>(ex.getMessage(), org.springframework.http.HttpStatus.NOT_FOUND); } - @ExceptionHandler({MethodArgumentNotValidException.class, MethodArgumentTypeMismatchException.class}) + @ExceptionHandler({ + MethodArgumentNotValidException.class, + MethodArgumentTypeMismatchException.class, + HttpMessageNotReadableException.class}) public ResponseEntity handleBadRequest(Exception ex) { - return new ResponseEntity<>(ex.getMessage(), org.springframework.http.HttpStatus.BAD_REQUEST); + return new ResponseEntity<>(ex.getMessage(), + org.springframework.http.HttpStatus.BAD_REQUEST); } } diff --git a/layout-server/src/test/java/org/finos/vuu/layoutserver/integration/LayoutIntegrationTest.java b/layout-server/src/test/java/org/finos/vuu/layoutserver/integration/LayoutIntegrationTest.java index e9bbd61fd..764254cf6 100644 --- a/layout-server/src/test/java/org/finos/vuu/layoutserver/integration/LayoutIntegrationTest.java +++ b/layout-server/src/test/java/org/finos/vuu/layoutserver/integration/LayoutIntegrationTest.java @@ -1,11 +1,14 @@ package org.finos.vuu.layoutserver.integration; import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.anyOf; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -89,7 +92,12 @@ void getLayout_validIDButLayoutDoesNotExist_returns404() throws Exception { void getLayout_invalidId_returns400() throws Exception { String layoutID = "invalidUUID"; - mockMvc.perform(get("/layouts/{id}", layoutID)).andExpect(status().isBadRequest()); + mockMvc.perform(get("/layouts/{id}", layoutID)) + .andExpect(status().isBadRequest()) + .andExpect(content().string( + "Failed to convert value of type 'java.lang.String' to required type 'java.util" + + ".UUID'; nested exception is java.lang.IllegalArgumentException: Invalid " + + "UUID string: invalidUUID")); } @Test @@ -167,7 +175,15 @@ void createLayout_invalidLayout_returns400() throws Exception { mockMvc.perform(post("/layouts") .content(invalidLayout) .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isBadRequest()); + .andExpect(status().isBadRequest()) + .andExpect(content().string( + "JSON parse error: Unrecognized token 'invalidLayout': was expecting (JSON " + + "String, Number, Array, Object or token 'null', 'true' or 'false'); nested " + + "exception is com.fasterxml.jackson.core.JsonParseException: Unrecognized " + + "token 'invalidLayout': was expecting (JSON String, Number, Array, Object " + + "or token 'null', 'true' or 'false')\n" + + " at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream);" + + " line: 1, column: 14]")); } @Test @@ -179,7 +195,18 @@ void createLayout_validLayoutButInvalidMetadata_returns400AndDoesNotCreateLayout mockMvc.perform(post("/layouts") .content(objectMapper.writeValueAsString(layoutRequest)) .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isBadRequest()); + .andExpect(status().isBadRequest()) + .andExpect(content().string( + "Validation failed for argument [0] in public org.finos.vuu.layoutserver.dto" + + ".response.LayoutResponseDTO org.finos.vuu.layoutserver.controller" + + ".LayoutController.createLayout(org.finos.vuu.layoutserver.dto.request" + + ".LayoutRequestDTO): [Field error in object 'layoutRequestDTO' on field " + + "'metadata': rejected value [null]; codes [NotNull.layoutRequestDTO" + + ".metadata,NotNull.metadata,NotNull.org.finos.vuu.layoutserver.dto.request" + + ".MetadataRequestDTO,NotNull]; arguments [org.springframework.context" + + ".support.DefaultMessageSourceResolvable: codes [layoutRequestDTO.metadata," + + "metadata]; arguments []; default message [metadata]]; default message " + + "[Please provide valid metadata]] ")); assertThat(layoutRepository.findAll()).isEmpty(); } @@ -221,7 +248,48 @@ void updateLayout_invalidRequestBodyDefinitionIsBlankAndMetadataIsNull_returns40 mockMvc.perform(put("/layouts/{id}", layout.getId()) .content(objectMapper.writeValueAsString(request)) .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isBadRequest()); + .andExpect(status().isBadRequest()) + .andExpect(content().string(anyOf( + equalTo( + "Validation failed for argument [1] in public void org.finos.vuu.layoutserver" + + ".controller.LayoutController.updateLayout(java.util.UUID,org.finos.vuu" + + ".layoutserver.dto.request.LayoutRequestDTO) with 2 errors: [Field " + + "error in" + + " object 'layoutRequestDTO' on field 'metadata': rejected value [null]; " + + "codes [NotNull.layoutRequestDTO.metadata,NotNull.metadata,NotNull.org" + + ".finos.vuu.layoutserver.dto.request.MetadataRequestDTO,NotNull]; " + + "arguments" + + " [org.springframework.context.support.DefaultMessageSourceResolvable: " + + "codes [layoutRequestDTO.metadata,metadata]; arguments []; default " + + "message " + + "[metadata]]; default message [Please provide valid metadata]] [Field " + + "error" + + " in object 'layoutRequestDTO' on field 'definition': rejected value []; " + + "codes [NotBlank.layoutRequestDTO.definition,NotBlank.definition,NotBlank" + + ".java.lang.String,NotBlank]; arguments [org.springframework.context" + + ".support.DefaultMessageSourceResolvable: codes [layoutRequestDTO" + + ".definition,definition]; arguments []; default message [definition]]; " + + "default message [Please provide a valid definition]] "), + equalTo( + "Validation failed for argument [1] in public void org.finos.vuu.layoutserver" + + ".controller.LayoutController.updateLayout(java.util.UUID,org.finos.vuu" + + ".layoutserver.dto.request.LayoutRequestDTO) with 2 errors: [Field " + + "error in" + + " object 'layoutRequestDTO' on field 'metadata': rejected value [null]; " + + "codes [NotNull.layoutRequestDTO.metadata,NotNull.metadata,NotNull.org" + + ".finos.vuu.layoutserver.dto.request.MetadataRequestDTO,NotNull]; " + + "arguments" + + " [org.springframework.context.support.DefaultMessageSourceResolvable: " + + "codes [layoutRequestDTO.metadata,metadata]; arguments []; default " + + "message " + + "[metadata]]; default message [Please provide valid metadata]] [Field " + + "error" + + " in object 'layoutRequestDTO' on field 'definition': rejected value []; " + + "codes [NotBlank.layoutRequestDTO.definition,NotBlank.definition,NotBlank" + + ".java.lang.String,NotBlank]; arguments [org.springframework.context" + + ".support.DefaultMessageSourceResolvable: codes [layoutRequestDTO" + + ".definition,definition]; arguments []; default message [definition]]; " + + "default message [Please provide a valid definition]] ")))); assertThat(layoutRepository.findById(layout.getId()).orElseThrow()).isEqualTo(layout); } @@ -235,9 +303,21 @@ void updateLayout_invalidRequestBodyUnexpectedFormat_returns400AndLayoutDoesNotC mockMvc.perform(put("/layouts/{id}", layout.getId()) .content(objectMapper.writeValueAsString(request)) .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isBadRequest()); - - assertThat(layoutRepository.findById(layout.getId()).orElseThrow()).isEqualTo(layout); + .andExpect(status().isBadRequest()) + .andExpect(content().string( + "JSON parse error: Cannot construct instance of `org.finos.vuu.layoutserver.dto" + + ".request.LayoutRequestDTO` (although at least one Creator exists): no " + + "String-argument constructor/factory method to deserialize from String " + + "value ('invalidRequest'); nested exception is com.fasterxml.jackson" + + ".databind.exc.MismatchedInputException: Cannot construct instance of `org" + + ".finos.vuu.layoutserver.dto.request.LayoutRequestDTO` (although at least " + + "one Creator exists): no String-argument constructor/factory method to " + + "deserialize from String value ('invalidRequest')\n" + + " at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream);" + + " line: 1, column: 1]")); + + assertThat(layoutRepository.findById(layout.getId()).orElseThrow()).isEqualTo( + layout); } @Test @@ -259,7 +339,11 @@ void updateLayout_invalidId_returns400() throws Exception { mockMvc.perform(put("/layouts/{id}", layoutID) .content(objectMapper.writeValueAsString(layoutRequest)) .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isBadRequest()); + .andExpect(status().isBadRequest()) + .andExpect(content().string( + "Failed to convert value of type 'java.lang.String' to required type 'java.util" + + ".UUID'; nested exception is java.lang.IllegalArgumentException: Invalid " + + "UUID string: invalidUUID")); } @Test @@ -284,7 +368,12 @@ void deleteLayout_validIDLayoutDoesNotExist_returnsNotFound() throws Exception { void deleteLayout_invalidId_returns400() throws Exception { String layoutID = "invalidUUID"; - mockMvc.perform(delete("/layouts/{id}", layoutID)).andExpect(status().isBadRequest()); + mockMvc.perform(delete("/layouts/{id}", layoutID)) + .andExpect(status().isBadRequest()) + .andExpect(content().string( + "Failed to convert value of type 'java.lang.String' to required type 'java.util" + + ".UUID'; nested exception is java.lang.IllegalArgumentException: Invalid " + + "UUID string: invalidUUID")); } private Layout createDefaultLayoutInDatabase() {