From 0a95ca7c7b312691bb856981b52a080c520266da Mon Sep 17 00:00:00 2001 From: Ivo Mattus Date: Fri, 5 Jul 2024 14:24:54 +0300 Subject: [PATCH] SIVA-596 Update ValidationExceptionHandler methods --- .../webapp/ValidationExceptionHandler.java | 23 +++++++- .../ValidationExceptionHandlerTest.java | 58 +++++++++---------- 2 files changed, 47 insertions(+), 34 deletions(-) diff --git a/siva-parent/siva-webapp/src/main/java/ee/openeid/siva/webapp/ValidationExceptionHandler.java b/siva-parent/siva-webapp/src/main/java/ee/openeid/siva/webapp/ValidationExceptionHandler.java index db3f9f14c..b47bce2e0 100644 --- a/siva-parent/siva-webapp/src/main/java/ee/openeid/siva/webapp/ValidationExceptionHandler.java +++ b/siva-parent/siva-webapp/src/main/java/ee/openeid/siva/webapp/ValidationExceptionHandler.java @@ -26,8 +26,10 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.util.MimeType; import org.springframework.validation.BindingResult; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException; @@ -37,6 +39,12 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.NoHandlerFoundException; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + @Slf4j @RestControllerAdvice public class ValidationExceptionHandler { @@ -112,7 +120,8 @@ public RequestValidationError handleNoHandlerFoundException(NoHandlerFoundExcept @ResponseStatus(value = HttpStatus.METHOD_NOT_ALLOWED) public RequestValidationError handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) { RequestValidationError requestValidationError = new RequestValidationError(); - requestValidationError.addFieldError("methodNotAllowed", "Request method " + e.getMethod() + " is not supported"); + requestValidationError.addFieldError("methodNotAllowed", "Only the following request methods are supported: " + + asCommaSeparatedList(e.getSupportedHttpMethods(), HttpMethod::name)); return requestValidationError; } @@ -128,7 +137,8 @@ public RequestValidationError handleHttpMessageNotReadableException(HttpMessageN @ResponseStatus(value = HttpStatus.UNSUPPORTED_MEDIA_TYPE) public RequestValidationError handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) { RequestValidationError requestValidationError = new RequestValidationError(); - requestValidationError.addFieldError("contentTypeNotSupported", "Content-Type " + e.getContentType() + " is not supported"); + requestValidationError.addFieldError("contentTypeNotSupported", "Only the following content types are supported: " + + asCommaSeparatedList(e.getSupportedMediaTypes(), MimeType::toString)); return requestValidationError; } @@ -151,4 +161,13 @@ public void setMessageSource(MessageSource messageSource) { this.messageSource = messageSource; } + private static String asCommaSeparatedList(Collection elements, Function mapper) { + List mappedElements = Optional + .ofNullable(elements).stream().flatMap(Collection::stream) + .map(mapper) + .collect(Collectors.toList()); + + return String.join(", ", mappedElements); + } + } diff --git a/siva-parent/siva-webapp/src/test/java/ee/openeid/siva/webapp/ValidationExceptionHandlerTest.java b/siva-parent/siva-webapp/src/test/java/ee/openeid/siva/webapp/ValidationExceptionHandlerTest.java index d639bdde7..fa6ee6140 100644 --- a/siva-parent/siva-webapp/src/test/java/ee/openeid/siva/webapp/ValidationExceptionHandlerTest.java +++ b/siva-parent/siva-webapp/src/test/java/ee/openeid/siva/webapp/ValidationExceptionHandlerTest.java @@ -243,7 +243,7 @@ void performPostRequest_WhenEndpointIsInvalid_ThrowsNoHandlerFoundException(Stri @ParameterizedTest @MethodSource("provideHttpMethodsAndValidationEndpoints") - void performRequest_WhenEndpointIsSetToValidateOrHashcodeAndHttpMethodIsInvalid_ThrowsHttpRequestMethodNotSupportedException(HttpMethod httpMethod, String endpoint, String requestErrorMessage) throws Exception { + void performRequest_WhenEndpointIsSetToValidateOrHashcodeAndHttpMethodIsInvalid_ThrowsHttpRequestMethodNotSupportedException(HttpMethod httpMethod, String endpoint) throws Exception { mockMvc.perform(MockMvcRequestBuilders.request(httpMethod, endpoint) .contentType(MediaType.APPLICATION_JSON) .content(request().toString().getBytes())) @@ -251,13 +251,13 @@ void performRequest_WhenEndpointIsSetToValidateOrHashcodeAndHttpMethodIsInvalid_ .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.jsonPath("$.requestErrors", hasSize(1))) .andExpect(MockMvcResultMatchers.jsonPath("$.requestErrors[0].key", is("methodNotAllowed"))) - .andExpect(MockMvcResultMatchers.jsonPath("$.requestErrors[0].message", containsString(requestErrorMessage))) + .andExpect(MockMvcResultMatchers.jsonPath("$.requestErrors[0].message", containsString("Only the following request methods are supported: POST"))) .andReturn(); } @ParameterizedTest @MethodSource("provideHttpMethods") - void performRequest_WhenEndpointIsSetToGetDataFilesAndHttpMethodIsInvalid_ThrowsHttpRequestMethodNotSupportedException(HttpMethod httpMethod, String requestErrorMessage) throws Exception { + void performRequest_WhenEndpointIsSetToGetDataFilesAndHttpMethodIsInvalid_ThrowsHttpRequestMethodNotSupportedException(HttpMethod httpMethod) throws Exception { mockMvcDataFiles.perform(MockMvcRequestBuilders.request(httpMethod, GET_DATA_FILES_URL_TEMPLATE) .contentType(MediaType.APPLICATION_JSON) .content(dataFileRequest().toString().getBytes())) @@ -265,7 +265,7 @@ void performRequest_WhenEndpointIsSetToGetDataFilesAndHttpMethodIsInvalid_Throws .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.jsonPath("$.requestErrors", hasSize(1))) .andExpect(MockMvcResultMatchers.jsonPath("$.requestErrors[0].key", is("methodNotAllowed"))) - .andExpect(MockMvcResultMatchers.jsonPath("$.requestErrors[0].message", containsString(requestErrorMessage))) + .andExpect(MockMvcResultMatchers.jsonPath("$.requestErrors[0].message", containsString("Only the following request methods are supported: POST"))) .andReturn(); } @@ -299,27 +299,27 @@ void performPostRequest_WhenEndpointIsSetToGetDataFilesAndJsonContentIsInvalid_T @ParameterizedTest @MethodSource("provideUnsupportedMediaTypesAndValidationEndpoints") - void performPostRequest_WhenEndpointIsSetToValidateOrHashcodeAndContentTypeIsInvalid_ThrowsHttpMediaTypeNotSupportedException(String endpoint, String mediaType, String requestErrorMessage) throws Exception { + void performPostRequest_WhenEndpointIsSetToValidateOrHashcodeAndContentTypeIsInvalid_ThrowsHttpMediaTypeNotSupportedException(String mediaType, String endpoint) throws Exception { mockMvc.perform(post(endpoint) .contentType(mediaType) .content(request().toString().getBytes())) .andExpect(MockMvcResultMatchers.status().isUnsupportedMediaType()) .andExpect(MockMvcResultMatchers.jsonPath("$.requestErrors", hasSize(1))) .andExpect(MockMvcResultMatchers.jsonPath("$.requestErrors[0].key", is("contentTypeNotSupported"))) - .andExpect(MockMvcResultMatchers.jsonPath("$.requestErrors[0].message", containsString(requestErrorMessage))) + .andExpect(MockMvcResultMatchers.jsonPath("$.requestErrors[0].message", containsString("Only the following content types are supported: application/json, application/*+json"))) .andReturn(); } @ParameterizedTest @MethodSource("provideUnsupportedMediaTypes") - void performPostRequest_WhenEndpointIsSetToGetDataFilesAndContentTypeIsInvalid_ThrowsHttpMediaTypeNotSupportedException(String mediaType, String requestErrorMessage) throws Exception { + void performPostRequest_WhenEndpointIsSetToGetDataFilesAndContentTypeIsInvalid_ThrowsHttpMediaTypeNotSupportedException(String mediaType) throws Exception { mockMvcDataFiles.perform(post(GET_DATA_FILES_URL_TEMPLATE) .contentType(mediaType) .content(dataFileRequest().toString().getBytes())) .andExpect(MockMvcResultMatchers.status().isUnsupportedMediaType()) .andExpect(MockMvcResultMatchers.jsonPath("$.requestErrors", hasSize(1))) .andExpect(MockMvcResultMatchers.jsonPath("$.requestErrors[0].key", is("contentTypeNotSupported"))) - .andExpect(MockMvcResultMatchers.jsonPath("$.requestErrors[0].message", containsString(requestErrorMessage))) + .andExpect(MockMvcResultMatchers.jsonPath("$.requestErrors[0].message", containsString("Only the following content types are supported: application/json, application/*+json"))) .andReturn(); } @@ -414,18 +414,18 @@ private JSONObject requestWithInvalidFormatSignatureFile() { } private static Stream provideHttpMethodsAndValidationEndpoints() { - return provideValidationEndpoints() - .flatMap(endpoint -> provideHttpMethods() - .map(args -> Arguments.of(args.get()[0], endpoint, "Request method " + ((HttpMethod) args.get()[0]).name() + " is not supported"))); + return provideHttpMethods() + .flatMap(httpMethod -> provideValidationEndpoints() + .map(endpoint -> Arguments.of(httpMethod, endpoint))); } - private static Stream provideHttpMethods() { + private static Stream provideHttpMethods() { return Stream.of( - Arguments.of(HttpMethod.GET, "Request method GET is not supported"), - Arguments.of(HttpMethod.PUT, "Request method PUT is not supported"), - Arguments.of(HttpMethod.DELETE, "Request method DELETE is not supported"), - Arguments.of(HttpMethod.PATCH, "Request method PATCH is not supported"), - Arguments.of(HttpMethod.HEAD, "Request method HEAD is not supported") + HttpMethod.GET, + HttpMethod.PUT, + HttpMethod.DELETE, + HttpMethod.PATCH, + HttpMethod.HEAD ); } @@ -452,19 +452,18 @@ private static Stream provideInvalidJsonContent() { } private static Stream provideUnsupportedMediaTypesAndValidationEndpoints() { - return provideValidationEndpoints() - .map(Arguments::of) - .flatMap(endpointArgs -> provideUnsupportedMediaTypes() - .map(mediaTypeArgs -> appendArguments(endpointArgs, mediaTypeArgs))); + return provideUnsupportedMediaTypes() + .flatMap(mediaType -> provideValidationEndpoints() + .map(endpoint -> Arguments.of(mediaType, endpoint))); } - private static Stream provideUnsupportedMediaTypes() { + private static Stream provideUnsupportedMediaTypes() { return Stream.of( - Arguments.of(MediaType.APPLICATION_XML_VALUE, "Content-Type application/xml is not supported"), - Arguments.of(MediaType.TEXT_PLAIN_VALUE, "Content-Type text/plain is not supported"), - Arguments.of("application/x-yaml", "Content-Type application/x-yaml is not supported"), - Arguments.of(MediaType.TEXT_HTML_VALUE, "Content-Type text/html is not supported"), - Arguments.of("", "Content-Type application/octet-stream is not supported") + MediaType.APPLICATION_XML_VALUE, + MediaType.TEXT_PLAIN_VALUE, + "application/x-yaml", + MediaType.TEXT_HTML_VALUE, + "" ); } @@ -472,9 +471,4 @@ private static Stream provideValidationEndpoints() { return Stream.of(VALIDATE_URL_TEMPLATE, HASHCODE_VALIDATION_URL_TEMPLATE); } - private static Arguments appendArguments(Arguments... arguments) { - return Arguments.of(Stream.of(arguments) - .flatMap(args -> Stream.of(args.get())) - .toArray()); - } }