Convert script/template objects to json format
Elasticsearch accepts multiple content-type formats, hence scripts can be stored/provided in json, yaml, cbor or smile. Yet the format that should be used internally is json. This is a problem mainly around search templates, as they only support json out of the four content-types, so instead of maintaining the content-type of the request we should rather convert the scripts/templates to json. Binary formats were not previously supported. If you stored a template in yaml format, you'd get back an error "No encoder found for MIME type [application/yaml]" when trying to execute it. With this commit the request content-type is independent from the template, which always gets converted to json internally. That is transparent to users and doesn't affect the content type of the response obtained when executing the template.
This commit is contained in:
parent
9391c6ffa9
commit
f2acf466aa
|
@ -169,9 +169,10 @@ public final class Script implements ToXContentObject, Writeable {
|
||||||
type = ScriptType.INLINE;
|
type = ScriptType.INLINE;
|
||||||
|
|
||||||
if (parser.currentToken() == Token.START_OBJECT) {
|
if (parser.currentToken() == Token.START_OBJECT) {
|
||||||
XContentBuilder builder = XContentFactory.contentBuilder(parser.contentType());
|
//this is really for search templates, that need to be converted to json format
|
||||||
idOrCode = builder.copyCurrentStructure(parser).bytes().utf8ToString();
|
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||||
options.put(CONTENT_TYPE_OPTION, parser.contentType().mediaType());
|
idOrCode = builder.copyCurrentStructure(parser).string();
|
||||||
|
options.put(CONTENT_TYPE_OPTION, XContentType.JSON.mediaType());
|
||||||
} else {
|
} else {
|
||||||
idOrCode = parser.text();
|
idOrCode = parser.text();
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,6 @@ import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
|
||||||
import org.elasticsearch.common.xcontent.ToXContent;
|
import org.elasticsearch.common.xcontent.ToXContent;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser.Token;
|
import org.elasticsearch.common.xcontent.XContentParser.Token;
|
||||||
import org.elasticsearch.common.xcontent.XContentType;
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
|
@ -107,9 +106,10 @@ public class StoredScriptSource extends AbstractDiffable<StoredScriptSource> imp
|
||||||
private void setCode(XContentParser parser) {
|
private void setCode(XContentParser parser) {
|
||||||
try {
|
try {
|
||||||
if (parser.currentToken() == Token.START_OBJECT) {
|
if (parser.currentToken() == Token.START_OBJECT) {
|
||||||
XContentBuilder builder = XContentFactory.contentBuilder(parser.contentType());
|
//this is really for search templates, that need to be converted to json format
|
||||||
code = builder.copyCurrentStructure(parser).bytes().utf8ToString();
|
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||||
options.put(Script.CONTENT_TYPE_OPTION, parser.contentType().mediaType());
|
code = builder.copyCurrentStructure(parser).string();
|
||||||
|
options.put(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType());
|
||||||
} else {
|
} else {
|
||||||
code = parser.text();
|
code = parser.text();
|
||||||
}
|
}
|
||||||
|
@ -263,11 +263,11 @@ public class StoredScriptSource extends AbstractDiffable<StoredScriptSource> imp
|
||||||
if (lang == null) {
|
if (lang == null) {
|
||||||
return PARSER.apply(parser, null).build();
|
return PARSER.apply(parser, null).build();
|
||||||
} else {
|
} else {
|
||||||
try (XContentBuilder builder = XContentFactory.contentBuilder(parser.contentType())) {
|
//this is really for search templates, that need to be converted to json format
|
||||||
|
try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
|
||||||
builder.copyCurrentStructure(parser);
|
builder.copyCurrentStructure(parser);
|
||||||
|
|
||||||
return new StoredScriptSource(lang, builder.string(),
|
return new StoredScriptSource(lang, builder.string(),
|
||||||
Collections.singletonMap(Script.CONTENT_TYPE_OPTION, parser.contentType().mediaType()));
|
Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,11 +284,11 @@ public class StoredScriptSource extends AbstractDiffable<StoredScriptSource> imp
|
||||||
|
|
||||||
if (token == Token.VALUE_STRING) {
|
if (token == Token.VALUE_STRING) {
|
||||||
return new StoredScriptSource(lang, parser.text(),
|
return new StoredScriptSource(lang, parser.text(),
|
||||||
Collections.singletonMap(Script.CONTENT_TYPE_OPTION, parser.contentType().mediaType()));
|
Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try (XContentBuilder builder = XContentFactory.contentBuilder(parser.contentType())) {
|
try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
|
||||||
if (token != Token.START_OBJECT) {
|
if (token != Token.START_OBJECT) {
|
||||||
builder.startObject();
|
builder.startObject();
|
||||||
builder.copyCurrentStructure(parser);
|
builder.copyCurrentStructure(parser);
|
||||||
|
@ -298,7 +298,7 @@ public class StoredScriptSource extends AbstractDiffable<StoredScriptSource> imp
|
||||||
}
|
}
|
||||||
|
|
||||||
return new StoredScriptSource(lang, builder.string(),
|
return new StoredScriptSource(lang, builder.string(),
|
||||||
Collections.singletonMap(Script.CONTENT_TYPE_OPTION, parser.contentType().mediaType()));
|
Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
|
|
|
@ -22,8 +22,8 @@ package org.elasticsearch.script;
|
||||||
import org.elasticsearch.common.io.stream.InputStreamStreamInput;
|
import org.elasticsearch.common.io.stream.InputStreamStreamInput;
|
||||||
import org.elasticsearch.common.io.stream.OutputStreamStreamOutput;
|
import org.elasticsearch.common.io.stream.OutputStreamStreamOutput;
|
||||||
import org.elasticsearch.common.xcontent.ToXContent;
|
import org.elasticsearch.common.xcontent.ToXContent;
|
||||||
import org.elasticsearch.common.xcontent.XContent;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.common.xcontent.XContentType;
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
@ -39,9 +39,8 @@ import static org.hamcrest.Matchers.equalTo;
|
||||||
public class ScriptTests extends ESTestCase {
|
public class ScriptTests extends ESTestCase {
|
||||||
|
|
||||||
public void testScriptParsing() throws IOException {
|
public void testScriptParsing() throws IOException {
|
||||||
XContent xContent = randomFrom(XContentType.JSON, XContentType.YAML).xContent();
|
Script expectedScript = createScript();
|
||||||
Script expectedScript = createScript(xContent);
|
try (XContentBuilder builder = XContentFactory.contentBuilder(randomFrom(XContentType.values()))) {
|
||||||
try (XContentBuilder builder = XContentBuilder.builder(xContent)) {
|
|
||||||
expectedScript.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
expectedScript.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||||
try (XContentParser parser = createParser(builder)) {
|
try (XContentParser parser = createParser(builder)) {
|
||||||
Script actualScript = Script.parse(parser);
|
Script actualScript = Script.parse(parser);
|
||||||
|
@ -51,8 +50,7 @@ public class ScriptTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testScriptSerialization() throws IOException {
|
public void testScriptSerialization() throws IOException {
|
||||||
XContent xContent = randomFrom(XContentType.JSON, XContentType.YAML).xContent();
|
Script expectedScript = createScript();
|
||||||
Script expectedScript = createScript(xContent);
|
|
||||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||||
expectedScript.writeTo(new OutputStreamStreamOutput(out));
|
expectedScript.writeTo(new OutputStreamStreamOutput(out));
|
||||||
try (ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray())) {
|
try (ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray())) {
|
||||||
|
@ -62,12 +60,12 @@ public class ScriptTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Script createScript(XContent xContent) throws IOException {
|
private Script createScript() throws IOException {
|
||||||
final Map<String, Object> params = randomBoolean() ? Collections.emptyMap() : Collections.singletonMap("key", "value");
|
final Map<String, Object> params = randomBoolean() ? Collections.emptyMap() : Collections.singletonMap("key", "value");
|
||||||
ScriptType scriptType = randomFrom(ScriptType.values());
|
ScriptType scriptType = randomFrom(ScriptType.values());
|
||||||
String script;
|
String script;
|
||||||
if (scriptType == ScriptType.INLINE) {
|
if (scriptType == ScriptType.INLINE) {
|
||||||
try (XContentBuilder builder = XContentBuilder.builder(xContent)) {
|
try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
|
||||||
builder.startObject();
|
builder.startObject();
|
||||||
builder.field("field", randomAsciiOfLengthBetween(1, 5));
|
builder.field("field", randomAsciiOfLengthBetween(1, 5));
|
||||||
builder.endObject();
|
builder.endObject();
|
||||||
|
@ -80,8 +78,8 @@ public class ScriptTests extends ESTestCase {
|
||||||
scriptType,
|
scriptType,
|
||||||
scriptType == ScriptType.STORED ? null : randomFrom("_lang1", "_lang2", "_lang3"),
|
scriptType == ScriptType.STORED ? null : randomFrom("_lang1", "_lang2", "_lang3"),
|
||||||
script,
|
script,
|
||||||
scriptType == ScriptType.INLINE ? Collections.singletonMap(Script.CONTENT_TYPE_OPTION, xContent.type().mediaType()) : null,
|
scriptType == ScriptType.INLINE ?
|
||||||
params
|
Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()) : null, params
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,8 +66,9 @@ public class RestSearchTemplateAction extends BaseRestHandler {
|
||||||
PARSER.declareField((parser, request, value) -> {
|
PARSER.declareField((parser, request, value) -> {
|
||||||
request.setScriptType(ScriptType.INLINE);
|
request.setScriptType(ScriptType.INLINE);
|
||||||
if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
|
if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
|
||||||
try (XContentBuilder builder = XContentFactory.contentBuilder(parser.contentType())) {
|
//convert the template to json which is the only supported XContentType (see CustomMustacheFactory#createEncoder)
|
||||||
request.setScript(builder.copyCurrentStructure(parser).bytes().utf8ToString());
|
try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
|
||||||
|
request.setScript(builder.copyCurrentStructure(parser).string());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new ParsingException(parser.getTokenLocation(), "Could not parse inline template", e);
|
throw new ParsingException(parser.getTokenLocation(), "Could not parse inline template", e);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue