@@ -350,21 +350,21 @@ public void testTextMode() throws IOException {
350
350
int count = randomIntBetween (0 , 100 );
351
351
bulkLoadTestData (count );
352
352
var builder = requestObjectBuilder ().query (fromIndex () + " | keep keyword, integer | sort integer asc | limit 100" );
353
- assertEquals (expectedTextBody ("txt" , count , null ), runEsqlAsTextWithFormat (builder , "txt" , null ));
353
+ assertEquals (expectedTextBody ("txt" , count , null ), runEsqlAsTextWithFormat (builder , "txt" , null , mode ));
354
354
}
355
355
356
356
public void testCSVMode () throws IOException {
357
357
int count = randomIntBetween (0 , 100 );
358
358
bulkLoadTestData (count );
359
359
var builder = requestObjectBuilder ().query (fromIndex () + " | keep keyword, integer | sort integer asc | limit 100" );
360
- assertEquals (expectedTextBody ("csv" , count , '|' ), runEsqlAsTextWithFormat (builder , "csv" , '|' ));
360
+ assertEquals (expectedTextBody ("csv" , count , '|' ), runEsqlAsTextWithFormat (builder , "csv" , '|' , mode ));
361
361
}
362
362
363
363
public void testTSVMode () throws IOException {
364
364
int count = randomIntBetween (0 , 100 );
365
365
bulkLoadTestData (count );
366
366
var builder = requestObjectBuilder ().query (fromIndex () + " | keep keyword, integer | sort integer asc | limit 100" );
367
- assertEquals (expectedTextBody ("tsv" , count , null ), runEsqlAsTextWithFormat (builder , "tsv" , null ));
367
+ assertEquals (expectedTextBody ("tsv" , count , null ), runEsqlAsTextWithFormat (builder , "tsv" , null , mode ));
368
368
}
369
369
370
370
public void testCSVNoHeaderMode () throws IOException {
@@ -1003,53 +1003,35 @@ public static Map<String, Object> runEsqlSync(RequestObjectBuilder requestObject
1003
1003
}
1004
1004
1005
1005
public static Map <String , Object > runEsqlAsync (RequestObjectBuilder requestObject ) throws IOException {
1006
- return runEsqlAsync (requestObject , new AssertWarnings .NoWarnings ());
1006
+ return runEsqlAsync (requestObject , randomBoolean (), new AssertWarnings .NoWarnings ());
1007
1007
}
1008
1008
1009
1009
static Map <String , Object > runEsql (RequestObjectBuilder requestObject , AssertWarnings assertWarnings , Mode mode ) throws IOException {
1010
1010
if (mode == ASYNC ) {
1011
- return runEsqlAsync (requestObject , assertWarnings );
1011
+ return runEsqlAsync (requestObject , randomBoolean (), assertWarnings );
1012
1012
} else {
1013
1013
return runEsqlSync (requestObject , assertWarnings );
1014
1014
}
1015
1015
}
1016
1016
1017
1017
public static Map <String , Object > runEsqlSync (RequestObjectBuilder requestObject , AssertWarnings assertWarnings ) throws IOException {
1018
- requestObject .build ();
1019
- Request request = prepareRequest (SYNC );
1020
- String mediaType = attachBody (requestObject , request );
1021
-
1022
- RequestOptions .Builder options = request .getOptions ().toBuilder ();
1023
- options .setWarningsHandler (WarningsHandler .PERMISSIVE ); // We assert the warnings ourselves
1024
- options .addHeader ("Content-Type" , mediaType );
1025
-
1026
- if (randomBoolean ()) {
1027
- options .addHeader ("Accept" , mediaType );
1028
- } else {
1029
- request .addParameter ("format" , requestObject .contentType ().queryParameter ());
1030
- }
1031
- request .setOptions (options );
1018
+ Request request = prepareRequestWithOptions (requestObject , SYNC );
1032
1019
1033
1020
HttpEntity entity = performRequest (request , assertWarnings );
1034
1021
return entityToMap (entity , requestObject .contentType ());
1035
1022
}
1036
1023
1037
1024
public static Map <String , Object > runEsqlAsync (RequestObjectBuilder requestObject , AssertWarnings assertWarnings ) throws IOException {
1038
- addAsyncParameters (requestObject );
1039
- requestObject .build ();
1040
- Request request = prepareRequest (ASYNC );
1041
- String mediaType = attachBody (requestObject , request );
1042
-
1043
- RequestOptions .Builder options = request .getOptions ().toBuilder ();
1044
- options .setWarningsHandler (WarningsHandler .PERMISSIVE ); // We assert the warnings ourselves
1045
- options .addHeader ("Content-Type" , mediaType );
1025
+ return runEsqlAsync (requestObject , randomBoolean (), assertWarnings );
1026
+ }
1046
1027
1047
- if (randomBoolean ()) {
1048
- options .addHeader ("Accept" , mediaType );
1049
- } else {
1050
- request .addParameter ("format" , requestObject .contentType ().queryParameter ());
1051
- }
1052
- request .setOptions (options );
1028
+ public static Map <String , Object > runEsqlAsync (
1029
+ RequestObjectBuilder requestObject ,
1030
+ boolean keepOnCompletion ,
1031
+ AssertWarnings assertWarnings
1032
+ ) throws IOException {
1033
+ addAsyncParameters (requestObject , keepOnCompletion );
1034
+ Request request = prepareRequestWithOptions (requestObject , ASYNC );
1053
1035
1054
1036
if (shouldLog ()) {
1055
1037
LOGGER .info ("REQUEST={}" , request );
@@ -1061,7 +1043,7 @@ public static Map<String, Object> runEsqlAsync(RequestObjectBuilder requestObjec
1061
1043
Object initialColumns = null ;
1062
1044
Object initialValues = null ;
1063
1045
var json = entityToMap (entity , requestObject .contentType ());
1064
- checkKeepOnCompletion (requestObject , json );
1046
+ checkKeepOnCompletion (requestObject , json , keepOnCompletion );
1065
1047
String id = (String ) json .get ("id" );
1066
1048
1067
1049
var supportsAsyncHeaders = clusterHasCapability ("POST" , "/_query" , List .of (), List .of ("async_query_status_headers" )).orElse (false );
@@ -1101,7 +1083,7 @@ public static Map<String, Object> runEsqlAsync(RequestObjectBuilder requestObjec
1101
1083
1102
1084
// issue a second request to "async get" the results
1103
1085
Request getRequest = prepareAsyncGetRequest (id );
1104
- getRequest .setOptions (options );
1086
+ getRequest .setOptions (request . getOptions () );
1105
1087
response = performRequest (getRequest );
1106
1088
entity = response .getEntity ();
1107
1089
}
@@ -1119,6 +1101,66 @@ public static Map<String, Object> runEsqlAsync(RequestObjectBuilder requestObjec
1119
1101
return removeAsyncProperties (result );
1120
1102
}
1121
1103
1104
+ public void testAsyncGetWithoutContentType () throws IOException {
1105
+ int count = randomIntBetween (0 , 100 );
1106
+ bulkLoadTestData (count );
1107
+ var requestObject = requestObjectBuilder ().query (fromIndex () + " | keep keyword, integer | sort integer asc | limit 100" );
1108
+
1109
+ addAsyncParameters (requestObject , true );
1110
+ Request request = prepareRequestWithOptions (requestObject , ASYNC );
1111
+
1112
+ if (shouldLog ()) {
1113
+ LOGGER .info ("REQUEST={}" , request );
1114
+ }
1115
+
1116
+ Response response = performRequest (request );
1117
+ HttpEntity entity = response .getEntity ();
1118
+
1119
+ var json = entityToMap (entity , requestObject .contentType ());
1120
+ checkKeepOnCompletion (requestObject , json , true );
1121
+ String id = (String ) json .get ("id" );
1122
+ // results won't be returned since keepOnCompletion is true
1123
+ assertThat (id , is (not (emptyOrNullString ())));
1124
+
1125
+ // issue an "async get" request with no Content-Type
1126
+ Request getRequest = prepareAsyncGetRequest (id );
1127
+ response = performRequest (getRequest );
1128
+ entity = response .getEntity ();
1129
+ var result = entityToMap (entity , XContentType .JSON );
1130
+
1131
+ ListMatcher values = matchesList ();
1132
+ for (int i = 0 ; i < count ; i ++) {
1133
+ values = values .item (matchesList ().item ("keyword" + i ).item (i ));
1134
+ }
1135
+ assertMap (
1136
+ result ,
1137
+ matchesMap ().entry (
1138
+ "columns" ,
1139
+ matchesList ().item (matchesMap ().entry ("name" , "keyword" ).entry ("type" , "keyword" ))
1140
+ .item (matchesMap ().entry ("name" , "integer" ).entry ("type" , "integer" ))
1141
+ ).entry ("values" , values ).entry ("took" , greaterThanOrEqualTo (0 )).entry ("id" , id ).entry ("is_running" , false )
1142
+ );
1143
+
1144
+ }
1145
+
1146
+ static Request prepareRequestWithOptions (RequestObjectBuilder requestObject , Mode mode ) throws IOException {
1147
+ requestObject .build ();
1148
+ Request request = prepareRequest (mode );
1149
+ String mediaType = attachBody (requestObject , request );
1150
+
1151
+ RequestOptions .Builder options = request .getOptions ().toBuilder ();
1152
+ options .setWarningsHandler (WarningsHandler .PERMISSIVE ); // We assert the warnings ourselves
1153
+ options .addHeader ("Content-Type" , mediaType );
1154
+
1155
+ if (randomBoolean ()) {
1156
+ options .addHeader ("Accept" , mediaType );
1157
+ } else {
1158
+ request .addParameter ("format" , requestObject .contentType ().queryParameter ());
1159
+ }
1160
+ request .setOptions (options );
1161
+ return request ;
1162
+ }
1163
+
1122
1164
// Removes async properties, otherwise consuming assertions would need to handle sync and async differences
1123
1165
static Map <String , Object > removeAsyncProperties (Map <String , Object > map ) {
1124
1166
Map <String , Object > copy = new HashMap <>(map );
@@ -1139,17 +1181,20 @@ protected static Map<String, Object> entityToMap(HttpEntity entity, XContentType
1139
1181
}
1140
1182
}
1141
1183
1142
- static void addAsyncParameters (RequestObjectBuilder requestObject ) throws IOException {
1184
+ static void addAsyncParameters (RequestObjectBuilder requestObject , boolean keepOnCompletion ) throws IOException {
1143
1185
// deliberately short in order to frequently trigger return without results
1144
1186
requestObject .waitForCompletion (TimeValue .timeValueNanos (randomIntBetween (1 , 100 )));
1145
- requestObject .keepOnCompletion (randomBoolean () );
1187
+ requestObject .keepOnCompletion (keepOnCompletion );
1146
1188
requestObject .keepAlive (TimeValue .timeValueDays (randomIntBetween (1 , 10 )));
1147
1189
}
1148
1190
1149
1191
// If keep_on_completion is set then an id must always be present, regardless of the value of any other property.
1150
- static void checkKeepOnCompletion (RequestObjectBuilder requestObject , Map <String , Object > json ) {
1192
+ static void checkKeepOnCompletion (RequestObjectBuilder requestObject , Map <String , Object > json , boolean keepOnCompletion ) {
1151
1193
if (requestObject .keepOnCompletion ()) {
1194
+ assertTrue (keepOnCompletion );
1152
1195
assertThat ((String ) json .get ("id" ), not (emptyOrNullString ()));
1196
+ } else {
1197
+ assertFalse (keepOnCompletion );
1153
1198
}
1154
1199
}
1155
1200
@@ -1167,14 +1212,19 @@ static void deleteNonExistent(Request request) throws IOException {
1167
1212
assertEquals (404 , response .getStatusLine ().getStatusCode ());
1168
1213
}
1169
1214
1170
- static String runEsqlAsTextWithFormat (RequestObjectBuilder builder , String format , @ Nullable Character delimiter ) throws IOException {
1171
- Request request = prepareRequest (SYNC );
1215
+ static String runEsqlAsTextWithFormat (RequestObjectBuilder builder , String format , @ Nullable Character delimiter , Mode mode )
1216
+ throws IOException {
1217
+ Request request = prepareRequest (mode );
1218
+ if (mode == ASYNC ) {
1219
+ addAsyncParameters (builder , randomBoolean ());
1220
+ }
1172
1221
String mediaType = attachBody (builder .build (), request );
1173
1222
1174
1223
RequestOptions .Builder options = request .getOptions ().toBuilder ();
1175
1224
options .addHeader ("Content-Type" , mediaType );
1176
1225
1177
- if (randomBoolean ()) {
1226
+ boolean addParam = randomBoolean ();
1227
+ if (addParam ) {
1178
1228
request .addParameter ("format" , format );
1179
1229
} else {
1180
1230
switch (format ) {
@@ -1188,8 +1238,75 @@ static String runEsqlAsTextWithFormat(RequestObjectBuilder builder, String forma
1188
1238
}
1189
1239
request .setOptions (options );
1190
1240
1191
- HttpEntity entity = performRequest (request , new AssertWarnings .NoWarnings ());
1192
- return Streams .copyToString (new InputStreamReader (entity .getContent (), StandardCharsets .UTF_8 ));
1241
+ if (shouldLog ()) {
1242
+ LOGGER .info ("REQUEST={}" , request );
1243
+ }
1244
+
1245
+ Response response = performRequest (request );
1246
+ HttpEntity entity = assertWarnings (response , new AssertWarnings .NoWarnings ());
1247
+
1248
+ // get the content, it could be empty because the request might have not completed
1249
+ String initialValue = Streams .copyToString (new InputStreamReader (entity .getContent (), StandardCharsets .UTF_8 ));
1250
+ String id = response .getHeader ("X-Elasticsearch-Async-Id" );
1251
+
1252
+ if (mode == SYNC ) {
1253
+ assertThat (id , is (emptyOrNullString ()));
1254
+ return initialValue ;
1255
+ }
1256
+
1257
+ if (id == null ) {
1258
+ // no id returned from an async call, must have completed immediately and without keep_on_completion
1259
+ assertThat (builder .keepOnCompletion (), either (nullValue ()).or (is (false )));
1260
+ assertNull (response .getHeader ("is_running" ));
1261
+ // the content cant be empty
1262
+ assertThat (initialValue , not (emptyOrNullString ()));
1263
+ return initialValue ;
1264
+ } else {
1265
+ // async may not return results immediately, so may need an async get
1266
+ assertThat (id , is (not (emptyOrNullString ())));
1267
+ String isRunning = response .getHeader ("X-Elasticsearch-Async-Is-Running" );
1268
+ if ("?0" .equals (isRunning )) {
1269
+ // must have completed immediately so keep_on_completion must be true
1270
+ assertThat (builder .keepOnCompletion (), is (true ));
1271
+ } else {
1272
+ // did not return results immediately, so we will need an async get
1273
+ // Also, different format modes return different results.
1274
+ switch (format ) {
1275
+ case "txt" -> assertThat (initialValue , emptyOrNullString ());
1276
+ case "csv" -> {
1277
+ assertEquals (initialValue , "\r \n " );
1278
+ initialValue = "" ;
1279
+ }
1280
+ case "tsv" -> {
1281
+ assertEquals (initialValue , "\n " );
1282
+ initialValue = "" ;
1283
+ }
1284
+ }
1285
+ }
1286
+ // issue a second request to "async get" the results
1287
+ Request getRequest = prepareAsyncGetRequest (id );
1288
+ if (delimiter != null ) {
1289
+ getRequest .addParameter ("delimiter" , String .valueOf (delimiter ));
1290
+ }
1291
+ // If the `format` parameter is not added, the GET request will return a response
1292
+ // with the `Content-Type` type due to the lack of an `Accept` header.
1293
+ if (addParam ) {
1294
+ getRequest .addParameter ("format" , format );
1295
+ }
1296
+ // if `addParam` is false, `options` will already have an `Accept` header
1297
+ getRequest .setOptions (options );
1298
+ response = performRequest (getRequest );
1299
+ entity = assertWarnings (response , new AssertWarnings .NoWarnings ());
1300
+ }
1301
+ String newValue = Streams .copyToString (new InputStreamReader (entity .getContent (), StandardCharsets .UTF_8 ));
1302
+
1303
+ // assert initial contents, if any, are the same as async get contents
1304
+ if (initialValue != null && initialValue .isEmpty () == false ) {
1305
+ assertEquals (initialValue , newValue );
1306
+ }
1307
+
1308
+ assertDeletable (id );
1309
+ return newValue ;
1193
1310
}
1194
1311
1195
1312
private static Request prepareRequest (Mode mode ) {
0 commit comments