Skip to content

Commit d130ce5

Browse files
swallezl-trotta
andauthored
Parse ES|QL response body header more leniently (#903)
Co-authored-by: Laura Trotta <153528055+l-trotta@users.noreply.github.com>
1 parent 7001d64 commit d130ce5

File tree

3 files changed

+58
-14
lines changed

3 files changed

+58
-14
lines changed

java-client/src/main/java/co/elastic/clients/elasticsearch/_helpers/esql/EsqlAdapterBase.java

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,26 +34,40 @@ public abstract class EsqlAdapterBase<T> implements EsqlAdapter<T> {
3434
* The caller can then read row arrays until finding an end array that closes the top-level array.
3535
*/
3636
public static EsqlMetadata readHeader(JsonParser parser, JsonpMapper mapper) {
37+
EsqlMetadata result = new EsqlMetadata();
38+
3739
JsonpUtils.expectNextEvent(parser, JsonParser.Event.START_OBJECT);
38-
JsonpUtils.expectNextEvent(parser, JsonParser.Event.KEY_NAME);
3940

40-
if (!"columns".equals(parser.getString())) {
41-
throw new JsonpMappingException("Expecting a 'columns' property, but found '" + parser.getString() + "'", parser.getLocation());
41+
parse: while (JsonpUtils.expectNextEvent(parser, JsonParser.Event.KEY_NAME) != null) {
42+
switch (parser.getString()) {
43+
case "values": {
44+
// We're done parsing header information
45+
break parse;
46+
}
47+
case "columns": {
48+
result.columns = JsonpDeserializer
49+
.arrayDeserializer(EsqlMetadata.EsqlColumn._DESERIALIZER)
50+
.deserialize(parser, mapper);
51+
break;
52+
}
53+
case "took": {
54+
JsonpUtils.expectNextEvent(parser, JsonParser.Event.VALUE_NUMBER);
55+
result.took = parser.getLong();
56+
break;
57+
}
58+
default: {
59+
// Ignore everything else
60+
JsonpUtils.skipValue(parser);
61+
break;
62+
}
63+
}
4264
}
4365

44-
List<EsqlMetadata.EsqlColumn> columns = JsonpDeserializer
45-
.arrayDeserializer(EsqlMetadata.EsqlColumn._DESERIALIZER)
46-
.deserialize(parser, mapper);
47-
48-
EsqlMetadata result = new EsqlMetadata();
49-
result.columns = columns;
50-
51-
JsonpUtils.expectNextEvent(parser, JsonParser.Event.KEY_NAME);
52-
53-
if (!"values".equals(parser.getString())) {
54-
throw new JsonpMappingException("Expecting a 'values' property, but found '" + parser.getString() + "'", parser.getLocation());
66+
if (result.columns == null) {
67+
throw new JsonpMappingException("Expecting a 'columns' property before 'values'.", parser.getLocation());
5568
}
5669

70+
// Beginning of the `values` property
5771
JsonpUtils.expectNextEvent(parser, JsonParser.Event.START_ARRAY);
5872

5973
return result;

java-client/src/main/java/co/elastic/clients/elasticsearch/_helpers/esql/EsqlMetadata.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import co.elastic.clients.util.ObjectBuilder;
2727
import co.elastic.clients.util.ObjectBuilderBase;
2828

29+
import javax.annotation.Nullable;
2930
import java.util.List;
3031

3132
public class EsqlMetadata {
@@ -74,4 +75,7 @@ protected static void setupEsqlColumnDeserializer(ObjectDeserializer<EsqlColumn.
7475
}
7576

7677
public List<EsqlColumn> columns;
78+
79+
@Nullable
80+
public Long took;
7781
}

java-client/src/test/java/co/elastic/clients/elasticsearch/_helpers/esql/EsqlAdapterTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import co.elastic.clients.elasticsearch._helpers.esql.jdbc.ResultSetEsqlAdapter;
2424
import co.elastic.clients.elasticsearch._helpers.esql.objects.ObjectsEsqlAdapter;
2525
import co.elastic.clients.elasticsearch.esql.query.EsqlFormat;
26+
import co.elastic.clients.json.JsonpMappingException;
2627
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
2728
import co.elastic.clients.testkit.MockHttpClient;
2829
import co.elastic.clients.transport.endpoints.BinaryResponse;
@@ -36,6 +37,7 @@
3637
public class EsqlAdapterTest extends Assertions {
3738

3839
String json = "{\n" +
40+
" \"took\": 10," +
3941
" \"columns\": [\n" +
4042
"\t{\"name\": \"avg_salary\", \"type\": \"double\"},\n" +
4143
"\t{\"name\": \"lang\", \t\"type\": \"keyword\"}\n" +
@@ -56,6 +58,30 @@ public static class Data {
5658
public String lang;
5759
}
5860

61+
@Test
62+
public void testMissingColumns() throws IOException {
63+
String badJson = "{\n" +
64+
" \"took\": 10," +
65+
" \"values\": [\n" +
66+
"\t[43760.0, \"Spanish\"],\n" +
67+
"\t[48644.0, \"French\"],\n" +
68+
"\t[48832.0, \"German\"]\n" +
69+
" ]\n" +
70+
"}\n";
71+
72+
ElasticsearchClient esClient = new MockHttpClient()
73+
.add("/_query", "application/json", badJson)
74+
.client(new JacksonJsonpMapper());
75+
76+
JsonpMappingException jsonMappingException = assertThrows(JsonpMappingException.class, () -> {
77+
esClient.esql().query(
78+
ResultSetEsqlAdapter.INSTANCE,
79+
"FROM employees | STATS avg_salary = AVG(salary) by country"
80+
);
81+
});
82+
assertTrue(jsonMappingException.getMessage().contains("Expecting a 'columns' property"));
83+
}
84+
5985
@Test
6086
public void testObjectDeserializer() throws IOException {
6187

0 commit comments

Comments
 (0)