From 2e85276c7c93fb7c35a440ed249962f11b6b904d Mon Sep 17 00:00:00 2001 From: Peter-Josef Meisch Date: Tue, 2 Mar 2021 21:43:08 +0100 Subject: [PATCH] DefaultReactiveElasticsearchClient handle 5xx error with empty body --- .../DefaultReactiveElasticsearchClient.java | 4 ++ ...efaultReactiveElasticsearchClientTest.java | 66 ++++++++++++++----- 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/springframework/data/elasticsearch/client/reactive/DefaultReactiveElasticsearchClient.java b/src/main/java/org/springframework/data/elasticsearch/client/reactive/DefaultReactiveElasticsearchClient.java index ce3ab80c7..c0e5aaedd 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/reactive/DefaultReactiveElasticsearchClient.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/reactive/DefaultReactiveElasticsearchClient.java @@ -866,6 +866,10 @@ private Publisher handleServerError(Request request, ClientResp String mediaType = response.headers().contentType().map(MediaType::toString).orElse(XContentType.JSON.mediaType()); return response.body(BodyExtractors.toMono(byte[].class)) // + .switchIfEmpty(Mono + .error(new ElasticsearchStatusException(String.format("%s request to %s returned error code %s and no body.", + request.getMethod(), request.getEndpoint(), statusCode), status)) + ) .map(bytes -> new String(bytes, StandardCharsets.UTF_8)) // .flatMap(content -> contentOrError(content, mediaType, status)) .flatMap(unused -> Mono diff --git a/src/test/java/org/springframework/data/elasticsearch/client/reactive/DefaultReactiveElasticsearchClientTest.java b/src/test/java/org/springframework/data/elasticsearch/client/reactive/DefaultReactiveElasticsearchClientTest.java index 3fc6d1516..48307af4a 100644 --- a/src/test/java/org/springframework/data/elasticsearch/client/reactive/DefaultReactiveElasticsearchClientTest.java +++ b/src/test/java/org/springframework/data/elasticsearch/client/reactive/DefaultReactiveElasticsearchClientTest.java @@ -22,20 +22,29 @@ import reactor.core.publisher.Mono; import reactor.test.StepVerifier; +import java.net.URI; +import java.util.Optional; import java.util.function.Function; +import org.elasticsearch.ElasticsearchStatusException; +import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.client.Request; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.fetch.subphase.FetchSourceContext; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.HttpStatus; +import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClient.ResponseSpec; +import org.springframework.web.util.UriBuilder; /** * @author Peter-Josef Meisch @@ -46,30 +55,24 @@ class DefaultReactiveElasticsearchClientTest { @Mock private HostProvider hostProvider; @Mock private Function searchRequestConverter; + @Spy private RequestCreator requestCreator; - private DefaultReactiveElasticsearchClient client; - - @BeforeEach - void setUp() { - client = new DefaultReactiveElasticsearchClient(hostProvider, new RequestCreator() { - @Override - public Function search() { - return searchRequestConverter; - } - }) { - @Override - public Mono execute(ReactiveElasticsearchClientCallback callback) { - return Mono.empty(); - } - }; - } + @Mock private WebClient webClient; @Test void shouldSetAppropriateRequestParametersOnCount() { + when(requestCreator.search()).thenReturn(searchRequestConverter); SearchRequest searchRequest = new SearchRequest("someindex") // .source(new SearchSourceBuilder().query(QueryBuilders.matchAllQuery())); + ReactiveElasticsearchClient client = new DefaultReactiveElasticsearchClient(hostProvider, requestCreator) { + @Override + public Mono execute(ReactiveElasticsearchClientCallback callback) { + return Mono.empty(); + } + }; + client.count(searchRequest).as(StepVerifier::create).verifyComplete(); ArgumentCaptor captor = ArgumentCaptor.forClass(SearchRequest.class); @@ -79,4 +82,33 @@ void shouldSetAppropriateRequestParametersOnCount() { assertThat(source.trackTotalHitsUpTo()).isEqualTo(TRACK_TOTAL_HITS_ACCURATE); assertThat(source.fetchSource()).isEqualTo(FetchSourceContext.DO_NOT_FETCH_SOURCE); } + + @Test // #1712 + @DisplayName("should throw ElasticsearchStatusException on server 5xx with empty body") + void shouldThrowElasticsearchStatusExceptionOnServer5xxWithEmptyBody() { + + when(hostProvider.getActive(any())).thenReturn(Mono.just(webClient)); + WebClient.RequestBodyUriSpec requestBodyUriSpec = mock(WebClient.RequestBodyUriSpec.class); + when(requestBodyUriSpec.uri((Function) any())).thenReturn(requestBodyUriSpec); + when(requestBodyUriSpec.attribute(any(), any())).thenReturn(requestBodyUriSpec); + when(requestBodyUriSpec.headers(any())).thenReturn(requestBodyUriSpec); + when(webClient.method(any())).thenReturn(requestBodyUriSpec); + when(requestBodyUriSpec.exchangeToMono(any())).thenAnswer(invocationOnMock -> { + Function> responseHandler = invocationOnMock.getArgument(0); + ClientResponse clientResponse = mock(ClientResponse.class); + when(clientResponse.statusCode()).thenReturn(HttpStatus.SERVICE_UNAVAILABLE); + ClientResponse.Headers headers = mock(ClientResponse.Headers.class); + when(headers.contentType()).thenReturn(Optional.empty()); + when(clientResponse.headers()).thenReturn(headers); + when(clientResponse.body(any())).thenReturn(Mono.empty()); + return responseHandler.apply(clientResponse); + }); + + ReactiveElasticsearchClient client = new DefaultReactiveElasticsearchClient(hostProvider, requestCreator); + + client.get(new GetRequest("42")) // + .as(StepVerifier::create) // + .expectError(ElasticsearchStatusException.class) // + .verify(); // + } }