diff --git a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaType.java b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaType.java index 2221f1bc97614..0cf1a609e9128 100644 --- a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaType.java +++ b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaType.java @@ -22,8 +22,8 @@ import org.elasticsearch.common.collect.Tuple; import java.util.Collections; +import java.util.List; import java.util.Map; -import java.util.Set; /** * Abstracts a Media Type and a query parameter format. @@ -44,7 +44,7 @@ public interface MediaType { * Returns a set of HeaderValues - allowed media type values on Accept or Content-Type headers * Also defines media type parameters for validation. */ - Set headerValues(); + List headerValues(); /** * A class to represent supported mediaType values i.e. application/json and parameters to be validated. diff --git a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaTypeRegistry.java b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaTypeRegistry.java index 674770944516d..c0ca282b12762 100644 --- a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaTypeRegistry.java +++ b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/MediaTypeRegistry.java @@ -22,7 +22,6 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; -import java.util.Set; import java.util.regex.Pattern; /** @@ -63,12 +62,16 @@ public Map parametersFor(String typeWithSubtype) { public MediaTypeRegistry register(T[] mediaTypes ) { for (T mediaType : mediaTypes) { - Set tuples = mediaType.headerValues(); - for (MediaType.HeaderValue headerValue : tuples) { - queryParamToMediaType.put(mediaType.queryParameter(), mediaType); - typeWithSubtypeToMediaType.put(headerValue.v1(), mediaType); - parametersMap.put(headerValue.v1(), convertPatterns(headerValue.v2())); - } + register(mediaType); + } + return this; + } + + public MediaTypeRegistry register(T mediaType) { + for (MediaType.HeaderValue headerValue : mediaType.headerValues()) { + queryParamToMediaType.put(mediaType.queryParameter(), mediaType); + typeWithSubtypeToMediaType.put(headerValue.v1(), mediaType); + parametersMap.put(headerValue.v1(), convertPatterns(headerValue.v2())); } return this; } diff --git a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/ParsedMediaType.java b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/ParsedMediaType.java index a7db4608bc867..21cf1b29543c2 100644 --- a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/ParsedMediaType.java +++ b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/ParsedMediaType.java @@ -106,8 +106,8 @@ public static ParsedMediaType parseMediaType(String headerValue) { return null; } - public static ParsedMediaType parseMediaType(XContentType requestContentType, Map parameters) { - ParsedMediaType parsedMediaType = parseMediaType(requestContentType.mediaTypeWithoutParameters()); + public static ParsedMediaType parseMediaType(MediaType requestContentType, Map parameters) { + ParsedMediaType parsedMediaType = parseMediaType(requestContentType.headerValues().get(0).v1()); parsedMediaType.parameters.putAll(parameters); return parsedMediaType; } @@ -159,6 +159,10 @@ public String toString() { return originalHeaderValue; } + public String responseContentTypeHeader() { + return responseContentTypeHeader(this.parameters); + } + public String responseContentTypeHeader(Map parameters) { return this.mediaTypeWithoutParameters() + formatParameters(parameters); } diff --git a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentBuilder.java b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentBuilder.java index b19235d3e8060..59d6b320415b4 100644 --- a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentBuilder.java +++ b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentBuilder.java @@ -208,9 +208,7 @@ public XContentBuilder(XContent xContent, OutputStream os, Set includes, } public String getResponseContentTypeString() { - Map parameters = responseContentType != null ? - responseContentType.getParameters() : Collections.emptyMap(); - return responseContentType.responseContentTypeHeader(parameters); + return responseContentType.responseContentTypeHeader(); } public XContentType contentType() { diff --git a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentType.java b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentType.java index f8e0dc23f0233..8660e20c6da12 100644 --- a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentType.java +++ b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentType.java @@ -24,8 +24,8 @@ import org.elasticsearch.common.xcontent.smile.SmileXContent; import org.elasticsearch.common.xcontent.yaml.YamlXContent; +import java.util.List; import java.util.Map; -import java.util.Set; /** * The content type of {@link org.elasticsearch.common.xcontent.XContent}. @@ -57,8 +57,8 @@ public XContent xContent() { } @Override - public Set headerValues() { - return Set.of( + public List headerValues() { + return List.of( new HeaderValue("application/json"), new HeaderValue("application/x-ndjson"), new HeaderValue("application/*")); @@ -84,8 +84,8 @@ public XContent xContent() { } @Override - public Set headerValues() { - return Set.of( + public List headerValues() { + return List.of( new HeaderValue("application/smile")); } }, @@ -109,8 +109,8 @@ public XContent xContent() { } @Override - public Set headerValues() { - return Set.of( + public List headerValues() { + return List.of( new HeaderValue("application/yaml")); } }, @@ -134,8 +134,8 @@ public XContent xContent() { } @Override - public Set headerValues() { - return Set.of( + public List headerValues() { + return List.of( new HeaderValue("application/cbor")); } }, @@ -159,8 +159,8 @@ public XContent xContent() { } @Override - public Set headerValues() { - return Set.of( + public List headerValues() { + return List.of( new HeaderValue(VENDOR_APPLICATION_PREFIX + "json", Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN)), new HeaderValue(VENDOR_APPLICATION_PREFIX + "x-ndjson", @@ -192,8 +192,8 @@ public XContent xContent() { } @Override - public Set headerValues() { - return Set.of( + public List headerValues() { + return List.of( new HeaderValue(VENDOR_APPLICATION_PREFIX + "smile", Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN))); } @@ -223,8 +223,8 @@ public XContent xContent() { } @Override - public Set headerValues() { - return Set.of( + public List headerValues() { + return List.of( new HeaderValue(VENDOR_APPLICATION_PREFIX + "yaml", Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN))); } @@ -254,8 +254,8 @@ public XContent xContent() { } @Override - public Set headerValues() { - return Set.of( + public List headerValues() { + return List.of( new HeaderValue(VENDOR_APPLICATION_PREFIX + "cbor", Map.of(COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN))); } diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/CustomMustacheFactory.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/CustomMustacheFactory.java index f47ab68a940d9..0e29450908a83 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/CustomMustacheFactory.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/CustomMustacheFactory.java @@ -30,10 +30,12 @@ import com.github.mustachejava.codes.DefaultMustache; import com.github.mustachejava.codes.IterableCode; import com.github.mustachejava.codes.WriteCode; -import org.apache.lucene.search.highlight.DefaultEncoder; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.xcontent.MediaType; +import org.elasticsearch.common.xcontent.ParsedMediaType; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.script.MustacheMediaType; import java.io.IOException; import java.io.StringWriter; @@ -51,28 +53,32 @@ import java.util.regex.Pattern; public class CustomMustacheFactory extends DefaultMustacheFactory { + private static final MediaType DEFAULT_MIME_TYPE = XContentType.JSON; - static final String JSON_MIME_TYPE_WITH_CHARSET = "application/json;charset=utf-8"; - static final String JSON_MIME_TYPE = "application/json"; - static final String PLAIN_TEXT_MIME_TYPE = "text/plain"; - static final String X_WWW_FORM_URLENCODED_MIME_TYPE = "application/x-www-form-urlencoded"; - - private static final String DEFAULT_MIME_TYPE = JSON_MIME_TYPE; - - private static final Map> ENCODERS = Map.of( - JSON_MIME_TYPE_WITH_CHARSET, JsonEscapeEncoder::new, - JSON_MIME_TYPE, JsonEscapeEncoder::new, - PLAIN_TEXT_MIME_TYPE, DefaultEncoder::new, - X_WWW_FORM_URLENCODED_MIME_TYPE, UrlEncoder::new); + private static final Map> ENCODERS = Map.of( + XContentType.JSON, JsonEscapeEncoder::new, + MustacheMediaType.PLAIN_TEXT, DefaultEncoder::new, + MustacheMediaType.X_WWW_FORM_URLENCODED, UrlEncoder::new); private final Encoder encoder; - public CustomMustacheFactory(String mimeType) { - super(); + public CustomMustacheFactory(MediaType mimeType) { setObjectHandler(new CustomReflectionObjectHandler()); this.encoder = createEncoder(mimeType); } + public CustomMustacheFactory(String mimeType) { + this(parseMediaType(mimeType)); + } + + private static MediaType parseMediaType(String mimeType) { + MediaType mustacheMediaType = ParsedMediaType.parseMediaType(mimeType).toMediaType(MustacheMediaType.REGISTRY); + if (mustacheMediaType == null) { + throw new IllegalArgumentException("No encoder found for MIME type [" + mimeType + "]"); + } + return mustacheMediaType; + } + public CustomMustacheFactory() { this(DEFAULT_MIME_TYPE); } @@ -86,14 +92,19 @@ public void encode(String value, Writer writer) { } } - static Encoder createEncoder(String mimeType) { - final Supplier supplier = ENCODERS.get(mimeType); + private static Encoder createEncoder(MediaType mustacheMediaType) { + final Supplier supplier = ENCODERS.get(mustacheMediaType); if (supplier == null) { - throw new IllegalArgumentException("No encoder found for MIME type [" + mimeType + "]"); + throw new IllegalArgumentException("No encoder found for MIME type [" + mustacheMediaType + "]"); } return supplier.get(); } + // package scope for testing + Encoder getEncoder() { + return encoder; + } + @Override public MustacheVisitor createMustacheVisitor() { return new CustomMustacheVisitor(this); diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/CustomMustacheFactoryTests.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/CustomMustacheFactoryTests.java index f5465f256c225..c2cfc96138a73 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/CustomMustacheFactoryTests.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/CustomMustacheFactoryTests.java @@ -19,54 +19,74 @@ package org.elasticsearch.script.mustache; +import org.elasticsearch.common.xcontent.ParsedMediaType; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.script.MustacheMediaType; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptEngine; import org.elasticsearch.script.TemplateScript; import org.elasticsearch.test.ESTestCase; +import java.util.Collections; import java.util.Map; -import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; -import static org.elasticsearch.script.mustache.CustomMustacheFactory.JSON_MIME_TYPE; -import static org.elasticsearch.script.mustache.CustomMustacheFactory.PLAIN_TEXT_MIME_TYPE; -import static org.elasticsearch.script.mustache.CustomMustacheFactory.X_WWW_FORM_URLENCODED_MIME_TYPE; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; public class CustomMustacheFactoryTests extends ESTestCase { + String JSON_MIME_TYPE_WITH_CHARSET = ParsedMediaType.parseMediaType(XContentType.JSON, Map.of("charset", "utf-8")) + .responseContentTypeHeader(); + String JSON_MIME_TYPE = ParsedMediaType.parseMediaType(XContentType.JSON, Collections.emptyMap()) + .responseContentTypeHeader(); + String PLAIN_TEXT_MIME_TYPE = ParsedMediaType.parseMediaType(MustacheMediaType.PLAIN_TEXT, Collections.emptyMap()) + .responseContentTypeHeader(); + String X_WWW_FORM_URLENCODED_MIME_TYPE = ParsedMediaType.parseMediaType(MustacheMediaType.X_WWW_FORM_URLENCODED, Collections.emptyMap()) + .responseContentTypeHeader(); public void testCreateEncoder() { { final IllegalArgumentException e = - expectThrows(IllegalArgumentException.class, () -> CustomMustacheFactory.createEncoder("non-existent")); - assertThat(e.getMessage(), equalTo("No encoder found for MIME type [non-existent]")); + expectThrows(IllegalArgumentException.class, () -> new CustomMustacheFactory("non-existent")); + assertThat(e.getMessage(), equalTo("invalid media-type [non-existent]")); } { - final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> CustomMustacheFactory.createEncoder("")); - assertThat(e.getMessage(), equalTo("No encoder found for MIME type []")); + final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new CustomMustacheFactory("")); + assertThat(e.getMessage(), equalTo("invalid media-type []")); } { final IllegalArgumentException e = - expectThrows(IllegalArgumentException.class, () -> CustomMustacheFactory.createEncoder("test")); - assertThat(e.getMessage(), equalTo("No encoder found for MIME type [test]")); + expectThrows(IllegalArgumentException.class, () -> new CustomMustacheFactory("test")); + assertThat(e.getMessage(), equalTo("invalid media-type [test]")); } - assertThat(CustomMustacheFactory.createEncoder(CustomMustacheFactory.JSON_MIME_TYPE_WITH_CHARSET), + String contentType = ParsedMediaType.parseMediaType("application/json ; charset=UTF-8") + .responseContentTypeHeader(); + assertThat(new CustomMustacheFactory(contentType).getEncoder(), + instanceOf(CustomMustacheFactory.JsonEscapeEncoder.class)); + assertThat(new CustomMustacheFactory(JSON_MIME_TYPE_WITH_CHARSET).getEncoder(), + instanceOf(CustomMustacheFactory.JsonEscapeEncoder.class)); + assertThat(new CustomMustacheFactory(JSON_MIME_TYPE).getEncoder(), + instanceOf(CustomMustacheFactory.JsonEscapeEncoder.class)); + + assertThat(new CustomMustacheFactory(PLAIN_TEXT_MIME_TYPE).getEncoder(), + instanceOf(CustomMustacheFactory.DefaultEncoder.class)); + assertThat(new CustomMustacheFactory(X_WWW_FORM_URLENCODED_MIME_TYPE).getEncoder(), + instanceOf(CustomMustacheFactory.UrlEncoder.class)); + + //TODO PG should this be rejected? + contentType = ParsedMediaType.parseMediaType("application/json ; unknown=UTF-8") + .responseContentTypeHeader(); + assertThat(new CustomMustacheFactory(contentType).getEncoder(), instanceOf(CustomMustacheFactory.JsonEscapeEncoder.class)); - assertThat(CustomMustacheFactory.createEncoder(CustomMustacheFactory.JSON_MIME_TYPE), - instanceOf(CustomMustacheFactory.JsonEscapeEncoder.class)); - assertThat(CustomMustacheFactory.createEncoder(CustomMustacheFactory.PLAIN_TEXT_MIME_TYPE), - instanceOf(CustomMustacheFactory.DefaultEncoder.class)); - assertThat(CustomMustacheFactory.createEncoder(CustomMustacheFactory.X_WWW_FORM_URLENCODED_MIME_TYPE), - instanceOf(CustomMustacheFactory.UrlEncoder.class)); } public void testJsonEscapeEncoder() { final ScriptEngine engine = new MustacheScriptEngine(); - final Map params = randomBoolean() ? singletonMap(Script.CONTENT_TYPE_OPTION, JSON_MIME_TYPE) : emptyMap(); + final Map params = randomBoolean() ? singletonMap(Script.CONTENT_TYPE_OPTION, JSON_MIME_TYPE) : + Collections.emptyMap(); TemplateScript.Factory compiled = engine.compile(null, "{\"field\": \"{{value}}\"}", TemplateScript.CONTEXT, params); diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yml index b79f73639da4c..bc4067776a045 100644 --- a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yml +++ b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yml @@ -1,9 +1,5 @@ --- "Verify that we can still find things with the template": - - skip: - version: "all" - reason: "Awaits fix: https://github.com/elastic/elasticsearch/issues/66986" - - do: search_template: rest_total_hits_as_int: true diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml index 16da4c9eb7f0b..33e3e2ede5157 100644 --- a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml +++ b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml @@ -1,8 +1,5 @@ --- "Verify that we can still find things with the template": - - skip: - version: "all" - reason: "Awaits fix: https://github.com/elastic/elasticsearch/issues/66986" - do: search_template: diff --git a/server/src/main/java/org/elasticsearch/script/MustacheMediaType.java b/server/src/main/java/org/elasticsearch/script/MustacheMediaType.java new file mode 100644 index 0000000000000..0344f3e233c88 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/script/MustacheMediaType.java @@ -0,0 +1,69 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.script; + +import org.elasticsearch.common.xcontent.MediaType; +import org.elasticsearch.common.xcontent.MediaTypeRegistry; +import org.elasticsearch.common.xcontent.XContentType; + +import java.util.List; + +public enum MustacheMediaType implements MediaType { + + // JSON { +// @Override +// public String queryParameter() { +// return null; +// } +// +// @Override +// public List headerValues() { +// return List.of( +// new HeaderValue("application/json", +// Map.of("charset", "utf-8"))); +// } +// }, + PLAIN_TEXT { + @Override + public String queryParameter() { + return null; + } + + @Override + public List headerValues() { + return List.of(new HeaderValue("text/plain")); + } + }, + X_WWW_FORM_URLENCODED { + @Override + public String queryParameter() { + return null; + } + + @Override + public List headerValues() { + return List.of(new HeaderValue("application/x-www-form-urlencoded")); + } + }; + + public static final MediaTypeRegistry REGISTRY = new MediaTypeRegistry<>() + .register(MustacheMediaType.values()) + .register(XContentType.JSON); // TODO PG make JSON and ND-JSON separate? then make the headerValues return single instance? +} diff --git a/server/src/main/java/org/elasticsearch/script/Script.java b/server/src/main/java/org/elasticsearch/script/Script.java index 1b5566c5ca1c5..b3f3f3e96f944 100644 --- a/server/src/main/java/org/elasticsearch/script/Script.java +++ b/server/src/main/java/org/elasticsearch/script/Script.java @@ -30,9 +30,11 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.AbstractObjectParser; import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; +import org.elasticsearch.common.xcontent.MediaType; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.ObjectParser.ValueType; +import org.elasticsearch.common.xcontent.ParsedMediaType; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -631,9 +633,10 @@ public XContentBuilder toXContent(XContentBuilder builder, Params builderParams) builder.startObject(); String contentType = options == null ? null : options.get(CONTENT_TYPE_OPTION); - - if (type == ScriptType.INLINE) { - if (contentType != null && builder.contentType().mediaType().equals(contentType)) { + ParsedMediaType parsedMediaType = ParsedMediaType.parseMediaType(contentType); + MediaType type = parsedMediaType != null ? parsedMediaType.toMediaType(MustacheMediaType.REGISTRY) : null; + if (this.type == ScriptType.INLINE) { + if (contentType != null && builder.contentType().equals(type)) { try (InputStream stream = new BytesArray(idOrCode).streamInput()) { builder.rawField(SOURCE_PARSE_FIELD.getPreferredName(), stream); } diff --git a/server/src/test/java/org/elasticsearch/rest/RestControllerTests.java b/server/src/test/java/org/elasticsearch/rest/RestControllerTests.java index 5683352c84d14..b5612724f44a7 100644 --- a/server/src/test/java/org/elasticsearch/rest/RestControllerTests.java +++ b/server/src/test/java/org/elasticsearch/rest/RestControllerTests.java @@ -732,8 +732,8 @@ public Version compatibleWithVersion() { private String randomCompatibleMimeType(byte version) { XContentType type = randomFrom(XContentType.VND_JSON, XContentType.VND_SMILE, XContentType.VND_CBOR, XContentType.VND_YAML); - return ParsedMediaType.parseMediaType(type.mediaType()) - .responseContentTypeHeader(Map.of(MediaType.COMPATIBLE_WITH_PARAMETER_NAME, String.valueOf(version))); + return ParsedMediaType.parseMediaType(type, Map.of(MediaType.COMPATIBLE_WITH_PARAMETER_NAME, String.valueOf(version))) + .responseContentTypeHeader(); } private static final class TestHttpServerTransport extends AbstractLifecycleComponent implements diff --git a/server/src/test/java/org/elasticsearch/script/ScriptTests.java b/server/src/test/java/org/elasticsearch/script/ScriptTests.java index 58560b1d1a85a..d459beaa1998d 100644 --- a/server/src/test/java/org/elasticsearch/script/ScriptTests.java +++ b/server/src/test/java/org/elasticsearch/script/ScriptTests.java @@ -44,7 +44,7 @@ public class ScriptTests extends ESTestCase { public void testScriptParsing() throws IOException { - Script expectedScript = createScript(); + Script expectedScript = createScript(XContentType.JSON.mediaType()); try (XContentBuilder builder = XContentFactory.contentBuilder(randomFrom(XContentType.values()))) { expectedScript.toXContent(builder, ToXContent.EMPTY_PARAMS); try (XContentParser parser = createParser(builder)) { @@ -55,7 +55,17 @@ public void testScriptParsing() throws IOException { } public void testScriptSerialization() throws IOException { - Script expectedScript = createScript(); + Script expectedScript = createScript(XContentType.JSON.mediaType()); + testSerialisation(expectedScript); + + expectedScript = createScript("application/json;charset=utf-8"); + testSerialisation(expectedScript); + + expectedScript = createScript("application/json; charset=UTF-8"); + testSerialisation(expectedScript); + } + + private void testSerialisation(Script expectedScript) throws IOException { try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { expectedScript.writeTo(new OutputStreamStreamOutput(out)); try (ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray())) { @@ -65,7 +75,7 @@ public void testScriptSerialization() throws IOException { } } - private Script createScript() throws IOException { + private Script createScript(String contentType) throws IOException { final Map params = randomBoolean() ? Collections.emptyMap() : Collections.singletonMap("key", "value"); ScriptType scriptType = randomFrom(ScriptType.values()); String script; @@ -84,12 +94,12 @@ private Script createScript() throws IOException { scriptType == ScriptType.STORED ? null : randomFrom("_lang1", "_lang2", "_lang3"), script, scriptType == ScriptType.INLINE ? - Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()) : null, params + Collections.singletonMap(Script.CONTENT_TYPE_OPTION, contentType) : null, params ); } public void testParse() throws IOException { - Script expectedScript = createScript(); + Script expectedScript = createScript(XContentType.JSON.mediaType()); try (XContentBuilder builder = XContentFactory.contentBuilder(randomFrom(XContentType.values()))) { expectedScript.toXContent(builder, ToXContent.EMPTY_PARAMS); try (XContentParser xParser = createParser(builder)) { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TextFormat.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TextFormat.java index 6b771dcfd7ca3..7eb36de7325e9 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TextFormat.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TextFormat.java @@ -26,7 +26,6 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; -import java.util.Set; import java.util.function.Function; import static org.elasticsearch.xpack.sql.action.BasicFormatter.FormatOption.TEXT; @@ -105,8 +104,8 @@ protected String eol() { } @Override - public Set headerValues() { - return Set.of( + public List headerValues() { + return List.of( new HeaderValue(CONTENT_TYPE_TXT, Map.of("header", "present|absent")), new HeaderValue(VENDOR_CONTENT_TYPE_TXT, @@ -229,8 +228,8 @@ boolean hasHeader(RestRequest request) { } @Override - public Set headerValues() { - return Set.of( + public List headerValues() { + return List.of( new HeaderValue(CONTENT_TYPE_CSV, Map.of("header", "present|absent","delimiter", ".+")),// more detailed parsing is in TextFormat.CSV#delimiter new HeaderValue(VENDOR_CONTENT_TYPE_CSV, @@ -288,8 +287,8 @@ String maybeEscape(String value, Character __) { } @Override - public Set headerValues() { - return Set.of( + public List headerValues() { + return List.of( new HeaderValue(CONTENT_TYPE_TSV, Map.of("header", "present|absent")), new HeaderValue(VENDOR_CONTENT_TYPE_TSV, Map.of("header", "present|absent", COMPATIBLE_WITH_PARAMETER_NAME, VERSION_PATTERN)));