Skip to content

Commit e9f9f00

Browse files
authored
SQL: add pretty printing to JSON format (#43756) (#44220)
(cherry picked from commit cbd9d4c)
1 parent c091b6c commit e9f9f00

File tree

5 files changed

+111
-2
lines changed

5 files changed

+111
-2
lines changed

server/src/main/java/org/elasticsearch/rest/AbstractRestChannel.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,22 @@ public XContentBuilder newErrorBuilder() throws IOException {
8484
*/
8585
@Override
8686
public XContentBuilder newBuilder(@Nullable XContentType requestContentType, boolean useFiltering) throws IOException {
87+
return newBuilder(requestContentType, null, useFiltering);
88+
}
89+
90+
/**
91+
* Creates a new {@link XContentBuilder} for a response to be sent using this channel. The builder's type can be sent as a parameter,
92+
* through {@code responseContentType} or it can fallback to {@link #newBuilder(XContentType, boolean)} logic if the sent type value
93+
* is {@code null}.
94+
*/
95+
@Override
96+
public XContentBuilder newBuilder(@Nullable XContentType requestContentType, @Nullable XContentType responseContentType,
97+
boolean useFiltering) throws IOException {
98+
if (responseContentType == null) {
99+
responseContentType = XContentType.fromMediaTypeOrFormat(format);
100+
}
87101
// try to determine the response content type from the media type or the format query string parameter, with the format parameter
88102
// taking precedence over the Accept header
89-
XContentType responseContentType = XContentType.fromMediaTypeOrFormat(format);
90103
if (responseContentType == null) {
91104
if (requestContentType != null) {
92105
// if there was a parsed content-type for the incoming request use that since no format was specified using the query

server/src/main/java/org/elasticsearch/rest/RestChannel.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ public interface RestChannel {
3636
XContentBuilder newErrorBuilder() throws IOException;
3737

3838
XContentBuilder newBuilder(@Nullable XContentType xContentType, boolean useFiltering) throws IOException;
39+
40+
XContentBuilder newBuilder(@Nullable XContentType xContentType, @Nullable XContentType responseContentType,
41+
boolean useFiltering) throws IOException;
3942

4043
BytesStreamOutput bytesOutput();
4144

server/src/main/java/org/elasticsearch/rest/RestController.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,12 @@ public XContentBuilder newBuilder(@Nullable XContentType xContentType, boolean u
509509
return delegate.newBuilder(xContentType, useFiltering);
510510
}
511511

512+
@Override
513+
public XContentBuilder newBuilder(XContentType xContentType, XContentType responseContentType, boolean useFiltering)
514+
throws IOException {
515+
return delegate.newBuilder(xContentType, responseContentType, useFiltering);
516+
}
517+
512518
@Override
513519
public BytesStreamOutput bytesOutput() {
514520
return delegate.bytesOutput();

x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package org.elasticsearch.xpack.sql.qa.rest;
77

88
import com.fasterxml.jackson.core.io.JsonStringEncoder;
9+
910
import org.apache.http.HttpEntity;
1011
import org.apache.http.entity.ContentType;
1112
import org.apache.http.entity.StringEntity;
@@ -25,6 +26,7 @@
2526
import org.elasticsearch.xpack.sql.qa.ErrorsTestCase;
2627
import org.hamcrest.Matcher;
2728

29+
import java.io.ByteArrayOutputStream;
2830
import java.io.IOException;
2931
import java.io.InputStream;
3032
import java.io.InputStreamReader;
@@ -414,6 +416,91 @@ protected Map<String, Object> runSql(HttpEntity sql, String suffix) throws IOExc
414416
}
415417
}
416418

419+
public void testPrettyPrintingEnabled() throws IOException {
420+
boolean columnar = randomBoolean();
421+
String expected = "";
422+
if (columnar) {
423+
expected = "{\n" +
424+
" \"columns\" : [\n" +
425+
" {\n" +
426+
" \"name\" : \"test1\",\n" +
427+
" \"type\" : \"text\"\n" +
428+
" }\n" +
429+
" ],\n" +
430+
" \"values\" : [\n" +
431+
" [\n" +
432+
" \"test1\",\n" +
433+
" \"test2\"\n" +
434+
" ]\n" +
435+
" ]\n" +
436+
"}\n";
437+
} else {
438+
expected = "{\n" +
439+
" \"columns\" : [\n" +
440+
" {\n" +
441+
" \"name\" : \"test1\",\n" +
442+
" \"type\" : \"text\"\n" +
443+
" }\n" +
444+
" ],\n" +
445+
" \"rows\" : [\n" +
446+
" [\n" +
447+
" \"test1\"\n" +
448+
" ],\n" +
449+
" [\n" +
450+
" \"test2\"\n" +
451+
" ]\n" +
452+
" ]\n" +
453+
"}\n";
454+
}
455+
executeAndAssertPrettyPrinting(expected, "true", columnar);
456+
}
457+
458+
public void testPrettyPrintingDisabled() throws IOException {
459+
boolean columnar = randomBoolean();
460+
String expected = "";
461+
if (columnar) {
462+
expected = "{\"columns\":[{\"name\":\"test1\",\"type\":\"text\"}],\"values\":[[\"test1\",\"test2\"]]}";
463+
} else {
464+
expected = "{\"columns\":[{\"name\":\"test1\",\"type\":\"text\"}],\"rows\":[[\"test1\"],[\"test2\"]]}";
465+
}
466+
executeAndAssertPrettyPrinting(expected, randomFrom("false", null), columnar);
467+
}
468+
469+
private void executeAndAssertPrettyPrinting(String expectedJson, String prettyParameter, boolean columnar)
470+
throws IOException {
471+
index("{\"test1\":\"test1\"}",
472+
"{\"test1\":\"test2\"}");
473+
474+
Request request = new Request("POST", SQL_QUERY_REST_ENDPOINT);
475+
if (prettyParameter != null) {
476+
request.addParameter("pretty", prettyParameter);
477+
}
478+
if (randomBoolean()) {
479+
// We default to JSON but we force it randomly for extra coverage
480+
request.addParameter("format", "json");
481+
}
482+
if (randomBoolean()) {
483+
// JSON is the default but randomly set it sometime for extra coverage
484+
RequestOptions.Builder options = request.getOptions().toBuilder();
485+
options.addHeader("Accept", randomFrom("*/*", "application/json"));
486+
request.setOptions(options);
487+
}
488+
request.setEntity(new StringEntity("{\"query\":\"SELECT * FROM test\"" + mode("plain") + columnarParameter(columnar) + "}",
489+
ContentType.APPLICATION_JSON));
490+
491+
Response response = client().performRequest(request);
492+
try (InputStream content = response.getEntity().getContent()) {
493+
ByteArrayOutputStream result = new ByteArrayOutputStream();
494+
byte[] buffer = new byte[1024];
495+
int length;
496+
while ((length = content.read(buffer)) != -1) {
497+
result.write(buffer, 0, length);
498+
}
499+
String actualJson = result.toString("UTF-8");
500+
assertEquals(expectedJson, actualJson);
501+
}
502+
}
503+
417504
public void testBasicTranslateQuery() throws IOException {
418505
index("{\"test\":\"test\"}", "{\"test\":\"test\"}");
419506

x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/RestSqlQueryAction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient cli
9595
return channel -> client.execute(SqlQueryAction.INSTANCE, sqlRequest, new RestResponseListener<SqlQueryResponse>(channel) {
9696
@Override
9797
public RestResponse buildResponse(SqlQueryResponse response) throws Exception {
98-
XContentBuilder builder = XContentBuilder.builder(xContentType.xContent());
98+
XContentBuilder builder = channel.newBuilder(request.getXContentType(), xContentType, true);
9999
response.toXContent(builder, request);
100100
return new BytesRestResponse(RestStatus.OK, builder);
101101
}

0 commit comments

Comments
 (0)