Skip to content

Commit 2a673dc

Browse files
committed
Fix error handling and deserialization
1 parent 282aaa9 commit 2a673dc

File tree

6 files changed

+154
-28
lines changed

6 files changed

+154
-28
lines changed

java-client/src/main/java/co/elastic/clients/base/ApiException.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ public class ApiException extends IOException {
2626
// Subclasses of java.lang.Throwable cannot have generic parameters
2727
// https://docs.oracle.com/javase/specs/jls/se10/html/jls-8.html#jls-8.1.2-310
2828

29-
public final Object error;
29+
private final Object error;
30+
31+
public Object error() {
32+
return this.error;
33+
}
3034

3135
public ApiException(Object error) {
3236
this.error = error;

java-client/src/main/java/co/elastic/clients/base/ElasticsearchError.java

Lines changed: 94 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,50 +19,120 @@
1919

2020
package co.elastic.clients.base;
2121

22+
import co.elastic.clients.json.DelegatingDeserializer;
23+
import co.elastic.clients.json.JsonpDeserializable;
2224
import co.elastic.clients.json.JsonpDeserializer;
23-
import co.elastic.clients.json.ObjectDeserializer;
25+
import co.elastic.clients.json.JsonpMapper;
26+
import co.elastic.clients.json.JsonpSerializable;
27+
import co.elastic.clients.json.ObjectBuilderDeserializer;
28+
import co.elastic.clients.util.ObjectBuilder;
29+
import jakarta.json.JsonValue;
30+
import jakarta.json.stream.JsonGenerator;
31+
import java.util.Objects;
32+
import java.util.function.Function;
2433

25-
public class ElasticsearchError {
34+
@JsonpDeserializable
35+
public final class ElasticsearchError implements JsonpSerializable {
36+
private final int status;
2637

27-
// TODO: add fields for the detailed representation of errors
28-
// See org.elasticsearch.rest.BytesRestResponse.build() and ElasticsearchException
38+
private final JsonValue error;
2939

30-
private int status;
31-
private String error;
40+
// ---------------------------------------------------------------------------------------------
3241

33-
private ElasticsearchError() {}
42+
public ElasticsearchError(ElasticsearchError.Builder builder) {
3443

35-
public ElasticsearchError(int status, String error) {
36-
this.status = status;
37-
this.error = error;
44+
this.status = Objects.requireNonNull(builder.status, "status");
45+
this.error = Objects.requireNonNull(builder.error, "error");
46+
47+
}
48+
49+
public ElasticsearchError(Function<ElasticsearchError.Builder, ElasticsearchError.Builder> fn) {
50+
this(fn.apply(new ElasticsearchError.Builder()));
3851
}
3952

53+
/**
54+
* API name: {@code status}
55+
*/
4056
public int status() {
4157
return this.status;
4258
}
4359

44-
private void status(int status) {
45-
this.status = status;
60+
/**
61+
* API name: {@code error}
62+
*/
63+
public JsonValue error() {
64+
return this.error;
4665
}
4766

48-
public String error() {
49-
return this.error;
67+
/**
68+
* Serialize this object to JSON.
69+
*/
70+
public void serialize(JsonGenerator generator, JsonpMapper mapper) {
71+
generator.writeStartObject();
72+
serializeInternal(generator, mapper);
73+
generator.writeEnd();
5074
}
5175

52-
private void error(String error) {
53-
this.error = error;
76+
protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) {
77+
78+
generator.writeKey("status");
79+
generator.write(this.status);
80+
81+
generator.writeKey("error");
82+
generator.write(this.error);
83+
5484
}
5585

56-
public static final JsonpDeserializer<ElasticsearchError> PARSER;
86+
// ---------------------------------------------------------------------------------------------
87+
88+
/**
89+
* Builder for {@link ElasticsearchError}.
90+
*/
91+
public static class Builder implements ObjectBuilder<ElasticsearchError> {
92+
private Integer status;
93+
94+
private JsonValue error;
95+
96+
/**
97+
* API name: {@code status}
98+
*/
99+
public ElasticsearchError.Builder status(int value) {
100+
this.status = value;
101+
return this;
102+
}
103+
104+
/**
105+
* API name: {@code error}
106+
*/
107+
public ElasticsearchError.Builder error(JsonValue value) {
108+
this.error = value;
109+
return this;
110+
}
111+
112+
/**
113+
* Builds a {@link ElasticsearchError}.
114+
*
115+
* @throws NullPointerException
116+
* if some of the required fields are null.
117+
*/
118+
public ElasticsearchError build() {
119+
120+
return new ElasticsearchError(this);
121+
}
122+
}
123+
124+
// ---------------------------------------------------------------------------------------------
125+
126+
/**
127+
* Json deserializer for {@link ElasticsearchError}
128+
*/
129+
public static final JsonpDeserializer<ElasticsearchError> _DESERIALIZER = ObjectBuilderDeserializer
130+
.lazy(ElasticsearchError.Builder::new, ElasticsearchError::setupElasticsearchErrorDeserializer, ElasticsearchError.Builder::build);
57131

58-
static {
59-
ObjectDeserializer<ElasticsearchError> op = new ObjectDeserializer<>(
60-
ElasticsearchError::new
61-
);
132+
protected static void setupElasticsearchErrorDeserializer(DelegatingDeserializer<ElasticsearchError.Builder> op) {
62133

63-
op.add(ElasticsearchError::status, "status");
64-
op.add(ElasticsearchError::error, JsonpDeserializer.stringDeserializer(), "error");
134+
op.add(Builder::status, JsonpDeserializer.integerDeserializer(), "status");
135+
op.add(ElasticsearchError.Builder::error, JsonpDeserializer.jsonValueDeserializer(), "error");
65136

66-
PARSER = op;
67137
}
68138
}

java-client/src/main/java/co/elastic/clients/base/RestClientTransport.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ private <RequestT> org.elasticsearch.client.Request prepareLowLevelRequest(
167167

168168
clientReq.setEntity(new ByteArrayEntity(baos.toByteArray(), ContentType.APPLICATION_JSON));
169169
}
170-
170+
// Request parameter intercepted by LLRC
171+
clientReq.addParameter("ignore", "400,401,403,404,405");
171172
return clientReq;
172173
}
173174

java-client/src/main/java/co/elastic/clients/base/SimpleEndpoint.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public boolean isError(int statusCode) {
100100

101101
@Override
102102
public JsonpDeserializer<ElasticsearchError> errorParser(int statusCode) {
103-
return ElasticsearchError.PARSER;
103+
return ElasticsearchError._DESERIALIZER;
104104
}
105105

106106
public <NewResponseT> SimpleEndpoint<RequestT, NewResponseT> withResponseDeserializer(

java-client/src/test/java/co/elastic/clients/elasticsearch/end_to_end/RequestTest.java

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919

2020
package co.elastic.clients.elasticsearch.end_to_end;
2121

22-
22+
import co.elastic.clients.base.ApiException;
2323
import co.elastic.clients.base.BooleanResponse;
24+
import co.elastic.clients.base.ElasticsearchError;
2425
import co.elastic.clients.base.RestClientTransport;
2526
import co.elastic.clients.base.Transport;
2627
import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient;
@@ -51,6 +52,7 @@
5152
import java.time.Duration;
5253
import java.util.Map;
5354
import java.util.concurrent.CompletableFuture;
55+
import java.util.concurrent.ExecutionException;
5456
import java.util.concurrent.TimeUnit;
5557

5658
public class RequestTest extends Assert {
@@ -231,6 +233,38 @@ public void testRefresh() throws IOException {
231233
assertEquals("1", ir.id());
232234
}
233235

236+
@Test public void errorResponse() throws Exception {
237+
238+
RestClient restClient = RestClient.builder(new HttpHost("localhost", container.getMappedPort(9200))).build();
239+
Transport transport = new RestClientTransport(restClient, mapper);
240+
ElasticsearchClient client = new ElasticsearchClient(transport);
241+
242+
BooleanResponse exists = client.exists(_0 -> _0.index("doesnotexist").id("reallynot"));
243+
assertFalse(exists.value());
244+
245+
try {
246+
co.elastic.clients.elasticsearch._core.GetResponse<String> response = client.get(
247+
_0 -> _0.index("doesnotexist").id("reallynot"), String.class
248+
);
249+
} catch(ApiException apie) {
250+
ElasticsearchError error = (ElasticsearchError) apie.error();
251+
assertEquals(404, error.status());
252+
assertEquals("index_not_found_exception", error.error().asJsonObject().getString("type"));
253+
}
254+
255+
try {
256+
ElasticsearchAsyncClient aClient = new ElasticsearchAsyncClient(transport);
257+
co.elastic.clients.elasticsearch._core.GetResponse<String> response = aClient.get(
258+
_0 -> _0.index("doesnotexist").id("reallynot"), String.class
259+
).get();
260+
} catch(ExecutionException ee) {
261+
ApiException apie = ((ApiException) ee.getCause());
262+
ElasticsearchError error = (ElasticsearchError) apie.error();
263+
assertEquals(404, error.status());
264+
assertEquals("index_not_found_exception", error.error().asJsonObject().getString("type"));
265+
}
266+
}
267+
234268
public static class AppData {
235269
private int intValue;
236270
private String msg;

java-client/src/test/java/co/elastic/clients/elasticsearch/model/BuiltinTypesTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
package co.elastic.clients.elasticsearch.model;
2121

22+
import co.elastic.clients.base.ElasticsearchError;
2223
import co.elastic.clients.elasticsearch._core.SearchRequest;
2324
import co.elastic.clients.elasticsearch._types.query_dsl.SpanGapQuery;
2425
import co.elastic.clients.elasticsearch.indices.IndexSettings;
@@ -59,4 +60,20 @@ public void testSpanGapQuery() {
5960
assertEquals("a-field", q.field());
6061
assertEquals(12, q.spanWidth());
6162
}
63+
64+
@Test
65+
public void testErrorResponse() {
66+
String json = "{\"error\":{\"root_cause\":[{\"type\":\"index_not_found_exception\",\"reason\":\"no such index [doesnotexist]\"," +
67+
"\"resource.type\":\"index_expression\",\"resource.id\":\"doesnotexist\",\"index_uuid\":\"_na_\"," +
68+
"\"index\":\"doesnotexist\"}],\"type\":\"index_not_found_exception\",\"reason\":\"no such index [doesnotexist]\",\"resource" +
69+
".type\":\"index_expression\",\"resource.id\":\"doesnotexist\",\"index_uuid\":\"_na_\",\"index\":\"doesnotexist\"}," +
70+
"\"status\":404}";
71+
72+
System.out.println(json);
73+
74+
ElasticsearchError error = fromJson(json, ElasticsearchError.class);
75+
76+
assertEquals(404, error.status());
77+
assertEquals("index_not_found_exception", error.error().asJsonObject().getString("type"));
78+
}
6279
}

0 commit comments

Comments
 (0)