Skip to content

Commit c7000fc

Browse files
authored
Extend BulkFailureException.failedDocuments return type to show more details about failure.
Original Pull Request #2633 Related tickets #2619
1 parent 341518d commit c7000fc

File tree

7 files changed

+111
-7
lines changed

7 files changed

+111
-7
lines changed

src/main/asciidoc/reference/elasticsearch-migration-guide-5.1-5.2.adoc

+8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ This section describes breaking changes from version 5.1.x to 5.2.x and how remo
66
[[elasticsearch-migration-guide-5.1-5.2.breaking-changes]]
77
== Breaking Changes
88

9+
In the `org.springframework.data.elasticsearch.BulkFailureException` class, the return type of the `getFailedDocuments` is changed from `Map<String, String>`
10+
to `Map<String, FailureDetails>`, which allows to get additional details about failure reasons.
11+
12+
The definition of the `FailureDetails` class (inner to `BulkFailureException`):
13+
[source,java]
14+
public record FailureDetails(Integer status, String errorMessage) {
15+
}
16+
917
[[elasticsearch-migration-guide-5.1-5.2.deprecations]]
1018
== Deprecations
1119

src/main/java/org/springframework/data/elasticsearch/BulkFailureException.java

+13-3
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,27 @@
2121

2222
/**
2323
* @author Peter-Josef Meisch
24+
* @author Illia Ulianov
2425
* @since 4.1
2526
*/
2627
public class BulkFailureException extends DataRetrievalFailureException {
27-
private final Map<String, String> failedDocuments;
28+
private final Map<String, FailureDetails> failedDocuments;
2829

29-
public BulkFailureException(String msg, Map<String, String> failedDocuments) {
30+
public BulkFailureException(String msg, Map<String, FailureDetails> failedDocuments) {
3031
super(msg);
3132
this.failedDocuments = failedDocuments;
3233
}
3334

34-
public Map<String, String> getFailedDocuments() {
35+
public Map<String, FailureDetails> getFailedDocuments() {
3536
return failedDocuments;
3637
}
38+
39+
/**
40+
* Details about a document saving failure.
41+
*
42+
* @author Illia Ulianov
43+
* @since 5.2
44+
*/
45+
public record FailureDetails(Integer status, String errorMessage) {
46+
}
3747
}

src/main/java/org/springframework/data/elasticsearch/client/elc/ElasticsearchTemplate.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
*
5656
* @author Peter-Josef Meisch
5757
* @author Hamid Rahimi
58+
* @author Illia Ulianov
5859
* @since 4.4
5960
*/
6061
public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
@@ -637,11 +638,11 @@ public BaseQueryBuilder queryBuilderWithIds(List<String> ids) {
637638
protected List<IndexedObjectInformation> checkForBulkOperationFailure(BulkResponse bulkResponse) {
638639

639640
if (bulkResponse.errors()) {
640-
Map<String, String> failedDocuments = new HashMap<>();
641+
Map<String, BulkFailureException.FailureDetails> failedDocuments = new HashMap<>();
641642
for (BulkResponseItem item : bulkResponse.items()) {
642643

643644
if (item.error() != null) {
644-
failedDocuments.put(item.id(), item.error().reason());
645+
failedDocuments.put(item.id(), new BulkFailureException.FailureDetails(item.status(), item.error().reason()));
645646
}
646647
}
647648
throw new BulkFailureException(

src/main/java/org/springframework/data/elasticsearch/client/elc/ReactiveElasticsearchTemplate.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
* Elasticsearch client.
6969
*
7070
* @author Peter-Josef Meisch
71+
* @author Illia Ulianov
7172
* @since 4.4
7273
*/
7374
public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearchTemplate {
@@ -250,12 +251,12 @@ private Flux<BulkResponseItem> doBulkOperation(List<?> queries, BulkOptions bulk
250251
private Mono<BulkResponse> checkForBulkOperationFailure(BulkResponse bulkResponse) {
251252

252253
if (bulkResponse.errors()) {
253-
Map<String, String> failedDocuments = new HashMap<>();
254+
Map<String, BulkFailureException.FailureDetails> failedDocuments = new HashMap<>();
254255

255256
for (BulkResponseItem item : bulkResponse.items()) {
256257

257258
if (item.error() != null) {
258-
failedDocuments.put(item.id(), item.error().reason());
259+
failedDocuments.put(item.id(), new BulkFailureException.FailureDetails(item.status(), item.error().reason()));
259260
}
260261
}
261262
BulkFailureException exception = new BulkFailureException(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.elasticsearch;
17+
18+
import static org.assertj.core.api.Assertions.*;
19+
20+
import java.util.Map;
21+
22+
import org.junit.jupiter.api.Test;
23+
24+
/**
25+
* @author Illia Ulianov
26+
*/
27+
class BulkFailureExceptionTest {
28+
29+
@Test // #2619
30+
void shouldCreateBulkException() {
31+
String documentId = "id1";
32+
var failureDetails = new BulkFailureException.FailureDetails(409, "conflict");
33+
var exception = new BulkFailureException("Test message", Map.of(documentId, failureDetails));
34+
assertThat(exception.getFailedDocuments()).containsEntry(documentId, failureDetails);
35+
}
36+
}

src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchIntegrationTests.java

+24
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.util.stream.Collectors;
3434
import java.util.stream.IntStream;
3535

36+
import org.assertj.core.api.InstanceOfAssertFactories;
3637
import org.assertj.core.api.SoftAssertions;
3738
import org.junit.jupiter.api.BeforeEach;
3839
import org.junit.jupiter.api.DisplayName;
@@ -48,6 +49,7 @@
4849
import org.springframework.data.domain.PageRequest;
4950
import org.springframework.data.domain.Pageable;
5051
import org.springframework.data.domain.Sort;
52+
import org.springframework.data.elasticsearch.BulkFailureException;
5153
import org.springframework.data.elasticsearch.annotations.*;
5254
import org.springframework.data.elasticsearch.annotations.Field;
5355
import org.springframework.data.elasticsearch.annotations.ScriptedField;
@@ -98,6 +100,7 @@
98100
* @author Haibo Liu
99101
* @author scoobyzhang
100102
* @author Hamid Rahimi
103+
* @author Illia Ulianov
101104
*/
102105
@SpringIntegrationTest
103106
public abstract class ElasticsearchIntegrationTests {
@@ -3616,6 +3619,27 @@ void shouldNotErrorOnSortWithUnmappedFieldAndUnmappedTypeSettings() {
36163619
operations.search(query, SampleEntity.class);
36173620
}
36183621

3622+
@Test // #2619
3623+
void shouldFailWithConflictOnAttemptToSaveWithSameVersion() {
3624+
var entity1 = new VersionedEntity();
3625+
entity1.setId("id1");
3626+
entity1.setVersion(1L);
3627+
var entity2 = new VersionedEntity();
3628+
entity2.setId("id2");
3629+
entity2.setVersion(1L);
3630+
operations.save(entity1, entity2);
3631+
3632+
entity1.setVersion(2L);
3633+
assertThatThrownBy(() -> operations.save(entity1, entity2))
3634+
.asInstanceOf(InstanceOfAssertFactories.type(BulkFailureException.class))
3635+
.extracting(BulkFailureException::getFailedDocuments)
3636+
.asInstanceOf(InstanceOfAssertFactories.map(String.class, BulkFailureException.FailureDetails.class))
3637+
.containsOnlyKeys("id2")
3638+
.extracting(Map::values)
3639+
.asInstanceOf(InstanceOfAssertFactories.collection(BulkFailureException.FailureDetails.class))
3640+
.allMatch(failureStatus -> failureStatus.status().equals(409));
3641+
}
3642+
36193643
// region entities
36203644
@Document(indexName = "#{@indexNameProvider.indexName()}")
36213645
@Setting(shards = 1, replicas = 0, refreshInterval = "-1")

src/test/java/org/springframework/data/elasticsearch/core/ReactiveElasticsearchIntegrationTests.java

+24
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
2121
import static org.springframework.data.elasticsearch.core.query.StringQuery.MATCH_ALL;
2222

23+
import org.assertj.core.api.InstanceOfAssertFactories;
24+
import org.springframework.data.elasticsearch.BulkFailureException;
2325
import reactor.core.publisher.Flux;
2426
import reactor.core.publisher.Mono;
2527
import reactor.test.StepVerifier;
@@ -89,6 +91,7 @@
8991
* @author Roman Puchkovskiy
9092
* @author George Popides
9193
* @author Sijia Liu
94+
* @author Illia Ulianov
9295
*/
9396
@SuppressWarnings("SpringJavaAutowiredMembersInspection")
9497
@SpringIntegrationTest
@@ -1192,6 +1195,27 @@ void shouldSaveDataFromFluxAndReturnSavedDataInAFlux() {
11921195
}) //
11931196
.verifyComplete();
11941197
}
1198+
1199+
@Test // #2619
1200+
void shouldFailWithConflictOnAttemptToSaveWithSameVersion() {
1201+
var entity1 = new VersionedEntity();
1202+
entity1.setId("id1");
1203+
entity1.setVersion(1L);
1204+
var entity2 = new VersionedEntity();
1205+
entity2.setId("id2");
1206+
entity2.setVersion(1L);
1207+
operations.saveAll(Arrays.asList(entity1, entity2), VersionedEntity.class).blockLast();
1208+
1209+
entity1.setVersion(2L);
1210+
assertThatThrownBy(() -> operations.saveAll(Arrays.asList(entity1, entity2), VersionedEntity.class).blockLast())
1211+
.asInstanceOf(InstanceOfAssertFactories.type(BulkFailureException.class))
1212+
.extracting(BulkFailureException::getFailedDocuments)
1213+
.asInstanceOf(InstanceOfAssertFactories.map(String.class, BulkFailureException.FailureDetails.class))
1214+
.containsOnlyKeys("id2").extracting(Map::values)
1215+
.asInstanceOf(InstanceOfAssertFactories.collection(BulkFailureException.FailureDetails.class))
1216+
.allMatch(failureStatus -> failureStatus.status().equals(409));
1217+
}
1218+
11951219
// endregion
11961220

11971221
// region Helper functions

0 commit comments

Comments
 (0)