From ce27b317462440030a54716b5e12e92f30053eb1 Mon Sep 17 00:00:00 2001 From: Arnaud THOREL <146101071+arnaud-thorel-of@users.noreply.github.com> Date: Tue, 8 Oct 2024 14:51:06 +0200 Subject: [PATCH] feat: rethrow notfound exception as badRequest on rpc calls (#46) --- .../postgrest/PostgrestRestTemplate.java | 13 +++++++++-- .../querydsl/postgrest/PostgrestRpcTest.java | 19 ++++++++++++++++ .../postgrest/PostgrestWebClient.java | 16 +++++++++----- .../postgrest/PostgrestWebClientRpcTest.java | 19 ++++++++++++++++ .../exceptions/PostgrestRequestException.java | 22 +++++++++++++++++++ 5 files changed, 82 insertions(+), 7 deletions(-) diff --git a/querydsl-postgrest-resttemplate-adapter/src/main/java/fr/ouestfrance/querydsl/postgrest/PostgrestRestTemplate.java b/querydsl-postgrest-resttemplate-adapter/src/main/java/fr/ouestfrance/querydsl/postgrest/PostgrestRestTemplate.java index 95ddfb7..6279db6 100644 --- a/querydsl-postgrest-resttemplate-adapter/src/main/java/fr/ouestfrance/querydsl/postgrest/PostgrestRestTemplate.java +++ b/querydsl-postgrest-resttemplate-adapter/src/main/java/fr/ouestfrance/querydsl/postgrest/PostgrestRestTemplate.java @@ -4,6 +4,7 @@ import fr.ouestfrance.querydsl.postgrest.model.CountItem; import fr.ouestfrance.querydsl.postgrest.model.HeaderRange; import fr.ouestfrance.querydsl.postgrest.model.RangeResponse; +import fr.ouestfrance.querydsl.postgrest.model.exceptions.PostgrestRequestException; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import org.springframework.core.ParameterizedTypeReference; @@ -13,6 +14,9 @@ import org.springframework.http.ResponseEntity; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestClientResponseException; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; @@ -99,8 +103,13 @@ public List count(String resource, Map> map) { @Override public V rpc(String rpcName, Map> map, Object body, Type type) { - return (V) restTemplate.exchange( - getUri(rpcName, map), HttpMethod.POST, new HttpEntity<>(body, new HttpHeaders()), ParameterizedTypeReference.forType(type)).getBody(); + try { + return (V) restTemplate.exchange( + getUri(rpcName, map), HttpMethod.POST, new HttpEntity<>(body, new HttpHeaders()), ParameterizedTypeReference.forType(type)) + .getBody(); + } catch (HttpClientErrorException.NotFound exception) { + throw new PostgrestRequestException(rpcName, exception.getMessage(), exception, exception.getResponseBodyAsString()); + } } diff --git a/querydsl-postgrest-resttemplate-adapter/src/test/java/fr/ouestfrance/querydsl/postgrest/PostgrestRpcTest.java b/querydsl-postgrest-resttemplate-adapter/src/test/java/fr/ouestfrance/querydsl/postgrest/PostgrestRpcTest.java index f2f06d8..418552f 100644 --- a/querydsl-postgrest-resttemplate-adapter/src/test/java/fr/ouestfrance/querydsl/postgrest/PostgrestRpcTest.java +++ b/querydsl-postgrest-resttemplate-adapter/src/test/java/fr/ouestfrance/querydsl/postgrest/PostgrestRpcTest.java @@ -2,6 +2,7 @@ import fr.ouestfrance.querydsl.postgrest.app.Post; import fr.ouestfrance.querydsl.postgrest.app.PostRequestWithSelect; +import fr.ouestfrance.querydsl.postgrest.model.exceptions.PostgrestRequestException; import org.apache.commons.lang3.reflect.TypeUtils; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.junit.jupiter.api.BeforeEach; @@ -16,6 +17,7 @@ import static fr.ouestfrance.querydsl.postgrest.TestUtils.jsonResponse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; @MockServerSettings(ports = 8007) class PostgrestRpcTest { @@ -72,4 +74,21 @@ void shouldCallRpcWithCriteriaResultIsList(MockServerClient client) { assertNotNull(result); System.out.println(result); } + + + @Test + void shouldRaiseExceptionOn404(MockServerClient client) { + client.when(HttpRequest.request().withPath("/rpc/testV1")) + .respond(jsonResponse(""" + { + "code":"PGRST202", + "details":"Searched for the function public_repository_depositaire.testV1 with parameters coordinates, type or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache.", + "hint":null, + "message":"Could not find the function public_repository_depositaire.testV1(coordinates, type) in the schema cache" + } + """).withStatusCode(404)); + PostgrestRequestException exception = assertThrows(PostgrestRequestException.class, () -> rpcClient.executeRpc("testV1", Post.class)); + assertNotNull(exception); + assertNotNull(exception.getResponseBody()); + } } diff --git a/querydsl-postgrest-webclient-adapter/src/main/java/fr/ouestfrance/querydsl/postgrest/PostgrestWebClient.java b/querydsl-postgrest-webclient-adapter/src/main/java/fr/ouestfrance/querydsl/postgrest/PostgrestWebClient.java index 8efc41c..4b06a5b 100644 --- a/querydsl-postgrest-webclient-adapter/src/main/java/fr/ouestfrance/querydsl/postgrest/PostgrestWebClient.java +++ b/querydsl-postgrest-webclient-adapter/src/main/java/fr/ouestfrance/querydsl/postgrest/PostgrestWebClient.java @@ -4,6 +4,7 @@ import fr.ouestfrance.querydsl.postgrest.model.CountItem; import fr.ouestfrance.querydsl.postgrest.model.HeaderRange; import fr.ouestfrance.querydsl.postgrest.model.RangeResponse; +import fr.ouestfrance.querydsl.postgrest.model.exceptions.PostgrestRequestException; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import org.springframework.core.ParameterizedTypeReference; @@ -13,7 +14,10 @@ import org.springframework.http.ResponseEntity; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -93,14 +97,14 @@ public V rpc(String rpcName, Map> params, Object body, uriBuilder.queryParams(toMultiMap(params)); return uriBuilder.build(); }); - if(body != null){ - request.bodyValue(body); - } + Optional.ofNullable(body).ifPresent(request::bodyValue); Object result = request .retrieve() .bodyToMono(ParameterizedTypeReference.forType(clazz)) + // On not found raise postgrestRequestException with body + .onErrorMap(WebClientResponseException.NotFound.class, e -> new PostgrestRequestException(rpcName, e.getMessage(), e, e.getResponseBodyAsString())) .block(); - if(result != null) { + if (result != null) { return (V) result; } return null; @@ -152,6 +156,7 @@ public BulkResponse delete(String resource, Map> par /** * Convert map to MultiValueMap + * * @param params map * @return MultiValueMap */ @@ -161,7 +166,8 @@ private static MultiValueMap toMultiMap(Map /** * Safe add headers to httpHeaders - * @param headers headers + * + * @param headers headers * @param httpHeaders httpHeaders */ private static void safeAdd(Map> headers, HttpHeaders httpHeaders) { diff --git a/querydsl-postgrest-webclient-adapter/src/test/java/fr/ouestfrance/querydsl/postgrest/PostgrestWebClientRpcTest.java b/querydsl-postgrest-webclient-adapter/src/test/java/fr/ouestfrance/querydsl/postgrest/PostgrestWebClientRpcTest.java index 037be96..f695838 100644 --- a/querydsl-postgrest-webclient-adapter/src/test/java/fr/ouestfrance/querydsl/postgrest/PostgrestWebClientRpcTest.java +++ b/querydsl-postgrest-webclient-adapter/src/test/java/fr/ouestfrance/querydsl/postgrest/PostgrestWebClientRpcTest.java @@ -2,6 +2,7 @@ import fr.ouestfrance.querydsl.postgrest.app.Post; import fr.ouestfrance.querydsl.postgrest.app.PostRequestWithSelect; +import fr.ouestfrance.querydsl.postgrest.model.exceptions.PostgrestRequestException; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.reflect.TypeUtils; import org.junit.jupiter.api.BeforeEach; @@ -15,6 +16,7 @@ import static fr.ouestfrance.querydsl.postgrest.TestUtils.jsonResponse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; @MockServerSettings(ports = 8007) @Slf4j @@ -70,4 +72,21 @@ void shouldCallRpcWithCriteriaResultIsList(MockServerClient client) { assertNotNull(result); } + + @Test + void shouldRaiseExceptionOn404(MockServerClient client) { + client.when(HttpRequest.request().withPath("/rpc/testV1")) + .respond(jsonResponse(""" + { + "code":"PGRST202", + "details":"Searched for the function public_repository_depositaire.testV1 with parameters coordinates, type or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache.", + "hint":null, + "message":"Could not find the function public_repository_depositaire.testV1(coordinates, type) in the schema cache" + } + """).withStatusCode(404)); + PostgrestRequestException exception = assertThrows(PostgrestRequestException.class, () -> rpcClient.executeRpc("testV1", Post.class)); + assertNotNull(exception); + assertNotNull(exception.getResponseBody()); + } + } diff --git a/querydsl-postgrest/src/main/java/fr/ouestfrance/querydsl/postgrest/model/exceptions/PostgrestRequestException.java b/querydsl-postgrest/src/main/java/fr/ouestfrance/querydsl/postgrest/model/exceptions/PostgrestRequestException.java index 1f9724f..c615709 100644 --- a/querydsl-postgrest/src/main/java/fr/ouestfrance/querydsl/postgrest/model/exceptions/PostgrestRequestException.java +++ b/querydsl-postgrest/src/main/java/fr/ouestfrance/querydsl/postgrest/model/exceptions/PostgrestRequestException.java @@ -1,10 +1,17 @@ package fr.ouestfrance.querydsl.postgrest.model.exceptions; +import lombok.Getter; +import lombok.Setter; + /** * Runtime exception for querying failure */ +@Getter +@Setter public class PostgrestRequestException extends RuntimeException { + private String responseBody; + /** * PostgrestRequestException constructor * @@ -28,6 +35,19 @@ public PostgrestRequestException(String resourceName, String message, Throwable this("Error on querying " + resourceName + " cause by " + message, cause); } + /** + * PostgrestRequestException constructor + * @param resourceName resource name + * @param message cause message + * @param cause exception raised + * @param errorBody error body + */ + public PostgrestRequestException(String resourceName, String message, Throwable cause, String errorBody) { + this("Error on querying " + resourceName + " cause by " + message, cause); + this.responseBody = errorBody; + } + + /** * PostgrestRequestException constructor * @@ -46,4 +66,6 @@ public PostgrestRequestException(String message) { public PostgrestRequestException(String message, Throwable cause) { super(message, cause); } + + }