Don't expose ElasticsearchStatusException.

Original Pull Request #1959 
Closes #1957
This commit is contained in:
Peter-Josef Meisch 2021-10-11 12:38:55 +02:00 committed by GitHub
parent 2450d579e9
commit 4a8e012e04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 133 additions and 51 deletions

View File

@ -44,6 +44,7 @@ Currently this will be a `org
.springframework.data.elasticsearch.core.clients.elasticsearch7.ElasticsearchAggregations` object; later different implementations will be available.
The same change has been done to the `ReactiveSearchOperations.aggregate()` functions, the now return a `Flux<AggregationContainer<?>>`.
Programs using the aggregations need to be changed to cast the returned value to the appropriate class to further proces it.
* methods that might have thrown a `org.elasticsearch.ElasticsearchStatusException` now will throw `org.springframework.data.elasticsearch.RestStatusException` instead.
=== Handling of field and sourceFilter properties of Query

View File

@ -0,0 +1,49 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch;
import org.springframework.dao.DataAccessException;
/**
* Exception class for REST status exceptions independent from the used client/backend.
*
* @author Peter-Josef Meisch
* @since 4.3
*/
public class RestStatusException extends DataAccessException {
// we do not use a dedicated status class from Elasticsearch, OpenSearch, Spring web or webflux here
private final int status;
public RestStatusException(int status, String msg) {
super(msg);
this.status = status;
}
public RestStatusException(int status, String msg, Throwable cause) {
super(msg, cause);
this.status = status;
}
public int getStatus() {
return status;
}
@Override
public String toString() {
return "RestStatusException{" + "status=" + status + "} " + super.toString();
}
}

View File

@ -101,6 +101,7 @@ import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.suggest.Suggest;
import org.reactivestreams.Publisher;
import org.springframework.data.elasticsearch.RestStatusException;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.ClientLogger;
@ -836,8 +837,7 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch
return Mono.error(BytesRestResponse.errorFromXContent(createParser(mediaType, content)));
} catch (Exception e) {
return Mono
.error(new ElasticsearchStatusException(content, RestStatus.fromCode(response.statusCode().value())));
return Mono.error(new RestStatusException(response.statusCode().value(), content));
}
}
}
@ -870,14 +870,14 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch
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)))
.switchIfEmpty(Mono.error(new RestStatusException(status.getStatus(),
String.format("%s request to %s returned error code %s and no body.", request.getMethod(),
request.getEndpoint(), statusCode))))
.map(bytes -> new String(bytes, StandardCharsets.UTF_8)) //
.flatMap(content -> contentOrError(content, mediaType, status))
.flatMap(unused -> Mono
.error(new ElasticsearchStatusException(String.format("%s request to %s returned error code %s.",
request.getMethod(), request.getEndpoint(), statusCode), status)));
.error(new RestStatusException(status.getStatus(), String.format("%s request to %s returned error code %s.",
request.getMethod(), request.getEndpoint(), statusCode))));
}
private <T> Publisher<? extends T> handleClientError(String logId, ClientResponse response, Class<T> responseType) {
@ -909,7 +909,7 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch
if (exception != null) {
StringBuilder sb = new StringBuilder();
buildExceptionMessages(sb, exception);
return Mono.error(new ElasticsearchStatusException(sb.toString(), status, exception));
return Mono.error(new RestStatusException(status.getStatus(), sb.toString(), exception));
}
return Mono.just(content);

View File

@ -22,13 +22,13 @@ import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.common.ValidationException;
import org.elasticsearch.index.engine.VersionConflictEngineException;
import org.elasticsearch.rest.RestStatus;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.elasticsearch.NoSuchIndexException;
import org.springframework.data.elasticsearch.RestStatusException;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
@ -63,9 +63,28 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
ex);
}
if (elasticsearchException instanceof ElasticsearchStatusException) {
ElasticsearchStatusException restStatusException = (ElasticsearchStatusException) elasticsearchException;
return new RestStatusException(restStatusException.status().getStatus(), restStatusException.getMessage(),
restStatusException.getCause());
}
return new UncategorizedElasticsearchException(ex.getMessage(), ex);
}
if (ex instanceof RestStatusException) {
RestStatusException restStatusException = (RestStatusException) ex;
Throwable cause = restStatusException.getCause();
if (cause instanceof ElasticsearchException) {
ElasticsearchException elasticsearchException = (ElasticsearchException) cause;
if (!indexAvailable(elasticsearchException)) {
return new NoSuchIndexException(ObjectUtils.nullSafeToString(elasticsearchException.getMetadata("es.index")),
ex);
}
}
}
if (ex instanceof ValidationException) {
return new DataIntegrityViolationException(ex.getMessage(), ex);
}
@ -80,13 +99,26 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
private boolean isSeqNoConflict(Exception exception) {
Integer status = null;
String message = null;
if (exception instanceof ElasticsearchStatusException) {
ElasticsearchStatusException statusException = (ElasticsearchStatusException) exception;
status = statusException.status().getStatus();
message = statusException.getMessage();
}
return statusException.status() == RestStatus.CONFLICT && statusException.getMessage() != null
&& statusException.getMessage().contains("type=version_conflict_engine_exception")
&& statusException.getMessage().contains("version conflict, required seqNo");
if (exception instanceof RestStatusException) {
RestStatusException statusException = (RestStatusException) exception;
status = statusException.getStatus();
message = statusException.getMessage();
}
if (status != null && message != null) {
return status == 409 && message.contains("type=version_conflict_engine_exception")
&& message.contains("version conflict, required seqNo");
}
if (exception instanceof VersionConflictEngineException) {

View File

@ -26,7 +26,6 @@ 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;
@ -40,6 +39,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.elasticsearch.RestStatusException;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
@ -84,8 +84,8 @@ class DefaultReactiveElasticsearchClientTest {
}
@Test // #1712
@DisplayName("should throw ElasticsearchStatusException on server 5xx with empty body")
void shouldThrowElasticsearchStatusExceptionOnServer5xxWithEmptyBody() {
@DisplayName("should throw RestStatusException on server 5xx with empty body")
void shouldThrowRestStatusExceptionOnServer5xxWithEmptyBody() {
when(hostProvider.getActive(any())).thenReturn(Mono.just(webClient));
WebClient.RequestBodyUriSpec requestBodyUriSpec = mock(WebClient.RequestBodyUriSpec.class);
@ -108,7 +108,7 @@ class DefaultReactiveElasticsearchClientTest {
client.get(new GetRequest("42")) //
.as(StepVerifier::create) //
.expectError(ElasticsearchStatusException.class) //
.expectError(RestStatusException.class) //
.verify(); //
}
}

View File

@ -30,7 +30,6 @@ import java.util.Map;
import java.util.UUID;
import java.util.stream.IntStream;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
@ -63,6 +62,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.RestStatusException;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ReactiveIndexOperations;
@ -165,7 +165,7 @@ public class ReactiveElasticsearchClientIntegrationTests {
client.get(new GetRequest(INDEX_I, "nonono")) //
.as(StepVerifier::create) //
.expectError(ElasticsearchStatusException.class) //
.expectError(RestStatusException.class) //
.verify();
}
@ -356,7 +356,7 @@ public class ReactiveElasticsearchClientIntegrationTests {
client.index(request) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}
@Test // DATAES-488
@ -405,7 +405,7 @@ public class ReactiveElasticsearchClientIntegrationTests {
client.update(request) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}
@Test // DATAES-488
@ -715,7 +715,7 @@ public class ReactiveElasticsearchClientIntegrationTests {
client.indices().createIndex(request -> request.index(INDEX_I)) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}
@Test // #1658
@ -737,7 +737,7 @@ public class ReactiveElasticsearchClientIntegrationTests {
client.indices().createIndex(new CreateIndexRequest(INDEX_I)) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}
@Test // #1658
@ -757,7 +757,7 @@ public class ReactiveElasticsearchClientIntegrationTests {
operations.indexOps(IndexCoordinates.of(INDEX_I)).create().block();
client.indices().getIndex(new GetIndexRequest(INDEX_II)).as(StepVerifier::create)
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}
@Test // DATAES-569, DATAES-678
@ -782,7 +782,7 @@ public class ReactiveElasticsearchClientIntegrationTests {
client.indices().deleteIndex(request -> request.indices(INDEX_I)) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}
@Test // DATAES-569
@ -800,7 +800,7 @@ public class ReactiveElasticsearchClientIntegrationTests {
client.indices().openIndex(request -> request.indices(INDEX_I)) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}
@Test // DATAES-569
@ -818,7 +818,7 @@ public class ReactiveElasticsearchClientIntegrationTests {
client.indices().closeIndex(request -> request.indices(INDEX_I)) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}
@Test // DATAES-569
@ -836,7 +836,7 @@ public class ReactiveElasticsearchClientIntegrationTests {
client.indices().refreshIndex(request -> request.indices(INDEX_I)) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}
@Test // #1640
@ -869,7 +869,7 @@ public class ReactiveElasticsearchClientIntegrationTests {
client.indices().putMapping(putMappingRequest) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}
@Test // #1640
@ -911,7 +911,7 @@ public class ReactiveElasticsearchClientIntegrationTests {
client.indices().getMapping(getMappingsRequest) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}
@Test // DATAES-569
@ -936,7 +936,7 @@ public class ReactiveElasticsearchClientIntegrationTests {
client.indices().putMapping(request -> request.indices(INDEX_I).source(jsonMap)) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}
@Test // DATAES-569
@ -954,7 +954,7 @@ public class ReactiveElasticsearchClientIntegrationTests {
client.indices().flushIndex(request -> request.indices(INDEX_I)) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}
@Test // DATAES-684
@ -1076,7 +1076,7 @@ public class ReactiveElasticsearchClientIntegrationTests {
void getFieldMappingNonExistingIndex() {
client.indices().getFieldMapping(request -> request.indices(INDEX_I).fields("message1")).as(StepVerifier::create)
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}
@Test // DATAES-796

View File

@ -27,7 +27,6 @@ import java.net.URI;
import java.time.Instant;
import java.util.Collections;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.DocWriteResponse.Result;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
@ -46,6 +45,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.reactivestreams.Publisher;
import org.springframework.data.elasticsearch.RestStatusException;
import org.springframework.data.elasticsearch.client.reactive.ReactiveMockClientTestsUtils.MockDelegatingElasticsearchHostProvider;
import org.springframework.data.elasticsearch.client.reactive.ReactiveMockClientTestsUtils.MockWebClientProvider.Receive;
import org.springframework.http.HttpMethod;
@ -476,9 +476,9 @@ public class ReactiveElasticsearchClientUnitTests {
hostProvider.when(HOST) //
.updateFail();
client.update(new UpdateRequest("twitter", "doc", "1").doc(Collections.singletonMap("user", "cstrobl")))
client.update(new UpdateRequest("twitter", "1").doc(Collections.singletonMap("user", "cstrobl")))
.as(StepVerifier::create) //
.expectError(ElasticsearchStatusException.class) //
.expectError(RestStatusException.class) //
.verify();
}

View File

@ -37,7 +37,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.springframework.dao.DataAccessException;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.IndicesOptions;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
@ -76,14 +76,14 @@ public class ElasticsearchRestTemplateTests extends ElasticsearchTemplateTests {
}
@Test
public void shouldThrowExceptionIfDocumentDoesNotExistWhileDoingPartialUpdate() {
public void shouldThrowDataAccessExceptionIfDocumentDoesNotExistWhileDoingPartialUpdate() {
// when
org.springframework.data.elasticsearch.core.document.Document document = org.springframework.data.elasticsearch.core.document.Document
.create();
UpdateQuery updateQuery = UpdateQuery.builder(nextIdAsString()).withDocument(document).build();
assertThatThrownBy(() -> operations.update(updateQuery, IndexCoordinates.of(indexNameProvider.indexName())))
.isInstanceOf(UncategorizedElasticsearchException.class);
.isInstanceOf(DataAccessException.class);
}
@Test // DATAES-768

View File

@ -40,7 +40,6 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.index.query.IdsQueryBuilder;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
@ -57,6 +56,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.annotation.Id;
@ -64,7 +64,7 @@ import org.springframework.data.annotation.Version;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.springframework.data.elasticsearch.RestStatusException;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
@ -225,7 +225,7 @@ public class ReactiveElasticsearchTemplateIntegrationTests {
operations.get("foo", SampleEntity.class, IndexCoordinates.of("no-such-index")) //
.as(StepVerifier::create) //
.expectError(ElasticsearchStatusException.class);
.expectError(RestStatusException.class);
}
@Test // DATAES-504
@ -337,7 +337,7 @@ public class ReactiveElasticsearchTemplateIntegrationTests {
.search(new CriteriaQuery(Criteria.where("message").is("some message")), SampleEntity.class,
IndexCoordinates.of("no-such-index")) //
.as(StepVerifier::create) //
.expectError(ElasticsearchStatusException.class);
.expectError(RestStatusException.class);
}
@Test // DATAES-504
@ -437,7 +437,7 @@ public class ReactiveElasticsearchTemplateIntegrationTests {
}
@Test // DATAES-595, DATAES-767
public void shouldThrowElasticsearchStatusExceptionWhenInvalidPreferenceForGivenCriteria() {
public void shouldThrowDataAccessExceptionWhenInvalidPreferenceForGivenCriteria() {
SampleEntity sampleEntity1 = randomEntity("test message");
SampleEntity sampleEntity2 = randomEntity("test test");
@ -451,7 +451,7 @@ public class ReactiveElasticsearchTemplateIntegrationTests {
operations.search(queryWithInvalidPreference, SampleEntity.class) //
.as(StepVerifier::create) //
.expectError(UncategorizedElasticsearchException.class).verify();
.expectError(DataAccessException.class).verify();
}
@Test // DATAES-504
@ -533,7 +533,7 @@ public class ReactiveElasticsearchTemplateIntegrationTests {
.aggregate(new CriteriaQuery(Criteria.where("message").is("some message")), SampleEntity.class,
IndexCoordinates.of("no-such-index")) //
.as(StepVerifier::create) //
.expectError(ElasticsearchStatusException.class);
.expectError(RestStatusException.class);
}
@Test // DATAES-519, DATAES-767
@ -541,7 +541,7 @@ public class ReactiveElasticsearchTemplateIntegrationTests {
operations.count(SampleEntity.class) //
.as(StepVerifier::create) //
.expectError(ElasticsearchStatusException.class);
.expectError(RestStatusException.class);
}
@Test // DATAES-504
@ -573,7 +573,7 @@ public class ReactiveElasticsearchTemplateIntegrationTests {
operations.delete("does-not-exists", IndexCoordinates.of("no-such-index")) //
.as(StepVerifier::create)//
.expectError(ElasticsearchStatusException.class);
.expectError(RestStatusException.class);
}
@Test // DATAES-504

View File

@ -31,7 +31,6 @@ import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.elasticsearch.ElasticsearchStatusException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
@ -46,6 +45,7 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.elasticsearch.RestStatusException;
import org.springframework.data.elasticsearch.annotations.CountQuery;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
@ -122,7 +122,7 @@ class SimpleReactiveElasticsearchRepositoryTests {
void findByIdShouldErrorIfIndexDoesNotExist() {
repository.findById("id-two") //
.as(StepVerifier::create) //
.expectError(ElasticsearchStatusException.class);
.expectError(RestStatusException.class);
}
@Test // DATAES-519
@ -268,7 +268,7 @@ class SimpleReactiveElasticsearchRepositoryTests {
void countShouldErrorWhenIndexDoesNotExist() {
repository.count() //
.as(StepVerifier::create) //
.expectError(ElasticsearchStatusException.class);
.expectError(RestStatusException.class);
}
@Test // DATAES-519