Scripting: Remove support for deprecated StoredScript contexts (#31394)

Removes support for storing scripts without the usual json around the
script. So You can no longer do:
```
POST _scripts/<templatename>
{
    "query": {
        "match": {
            "title": "{{query_string}}"
        }
    }
}
```

and must instead do:
```
POST _scripts/<templatename>
{
    "script": {
        "lang": "mustache",
        "source": {
            "query": {
                "match": {
                    "title": "{{query_string}}"
                }
            }
        }
    }
}
```

This improves error reporting when you attempt to store a script but don't
quite get the syntax right. Before, there was a good chance that we'd
think of it as a "raw" template and just store it. Now we won't do that.
Nice.
This commit is contained in:
Sohaib Iftikhar 2018-07-05 15:30:08 +02:00 committed by Nik Everett
parent 894fb97ad7
commit 40b822c878
8 changed files with 141 additions and 271 deletions

View File

@ -75,3 +75,7 @@ will be for such settings to be copied on such operations. To enable users in
`copy_settings` parameter was added on the REST layer. As this behavior will be `copy_settings` parameter was added on the REST layer. As this behavior will be
the only behavior in 8.0.0, this parameter is deprecated in 7.0.0 for removal in the only behavior in 8.0.0, this parameter is deprecated in 7.0.0 for removal in
8.0.0. 8.0.0.
==== The deprecated stored script contexts have now been removed
When putting stored scripts, support for storing them with the deprecated `template` context or without a context is
now removed. Scripts must be stored using the `script` context as mentioned in the documentation.

View File

@ -23,7 +23,6 @@ import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRespo
import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.Plugin;
@ -152,25 +151,22 @@ public class SearchTemplateIT extends ESSingleNodeTestCase {
public void testIndexedTemplateClient() throws Exception { public void testIndexedTemplateClient() throws Exception {
assertAcked(client().admin().cluster().preparePutStoredScript() assertAcked(client().admin().cluster().preparePutStoredScript()
.setId("testTemplate") .setId("testTemplate")
.setContent(new BytesArray("{" + .setContent(
"\"template\":{" + new BytesArray(
" \"query\":{" + "{" +
" \"match\":{" + " \"script\": {" +
" \"theField\" : \"{{fieldParam}}\"}" + " \"lang\": \"mustache\"," +
" }" + " \"source\": {" +
"}" + " \"query\": {" +
"}"), XContentType.JSON)); " \"match\": {" +
" \"theField\": \"{{fieldParam}}\"" +
" }" +
assertAcked(client().admin().cluster().preparePutStoredScript() " }" +
.setId("testTemplate").setContent(new BytesArray("{" + " }" +
"\"template\":{" + " }" +
" \"query\":{" + "}"
" \"match\":{" + ),
" \"theField\" : \"{{fieldParam}}\"}" + XContentType.JSON));
" }" +
"}" +
"}"), XContentType.JSON));
GetStoredScriptResponse getResponse = client().admin().cluster() GetStoredScriptResponse getResponse = client().admin().cluster()
.prepareGetStoredScript("testTemplate").get(); .prepareGetStoredScript("testTemplate").get();
@ -198,41 +194,32 @@ public class SearchTemplateIT extends ESSingleNodeTestCase {
getResponse = client().admin().cluster().prepareGetStoredScript("testTemplate").get(); getResponse = client().admin().cluster().prepareGetStoredScript("testTemplate").get();
assertNull(getResponse.getSource()); assertNull(getResponse.getSource());
assertWarnings("the template context is now deprecated. Specify templates in a \"script\" element.");
} }
public void testIndexedTemplate() throws Exception { public void testIndexedTemplate() throws Exception {
assertAcked(client().admin().cluster().preparePutStoredScript()
.setId("1a") String script =
.setContent(new BytesArray("{" + "{" +
"\"template\":{" + " \"script\": {" +
" \"query\":{" + " \"lang\": \"mustache\"," +
" \"match\":{" + " \"source\": {" +
" \"theField\" : \"{{fieldParam}}\"}" + " \"query\": {" +
" }" + " \"match\": {" +
"}" + " \"theField\": \"{{fieldParam}}\"" +
"}" " }" +
), XContentType.JSON) " }" +
" }" +
" }" +
"}";
assertAcked(
client().admin().cluster().preparePutStoredScript().setId("1a").setContent(new BytesArray(script), XContentType.JSON)
); );
assertAcked(client().admin().cluster().preparePutStoredScript() assertAcked(
.setId("2") client().admin().cluster().preparePutStoredScript().setId("2").setContent(new BytesArray(script), XContentType.JSON)
.setContent(new BytesArray("{" +
"\"template\":{" +
" \"query\":{" +
" \"match\":{" +
" \"theField\" : \"{{fieldParam}}\"}" +
" }" +
"}" +
"}"), XContentType.JSON)
); );
assertAcked(client().admin().cluster().preparePutStoredScript() assertAcked(
.setId("3") client().admin().cluster().preparePutStoredScript().setId("3").setContent(new BytesArray(script), XContentType.JSON)
.setContent(new BytesArray("{" +
"\"template\":{" +
" \"match\":{" +
" \"theField\" : \"{{fieldParam}}\"}" +
" }" +
"}"), XContentType.JSON)
); );
BulkRequestBuilder bulkRequestBuilder = client().prepareBulk(); BulkRequestBuilder bulkRequestBuilder = client().prepareBulk();
@ -268,7 +255,6 @@ public class SearchTemplateIT extends ESSingleNodeTestCase {
.setScript("2").setScriptType(ScriptType.STORED).setScriptParams(templateParams) .setScript("2").setScriptType(ScriptType.STORED).setScriptParams(templateParams)
.get(); .get();
assertHitCount(searchResponse.getResponse(), 1); assertHitCount(searchResponse.getResponse(), 1);
assertWarnings("the template context is now deprecated. Specify templates in a \"script\" element.");
} }
// Relates to #10397 // Relates to #10397
@ -282,13 +268,27 @@ public class SearchTemplateIT extends ESSingleNodeTestCase {
client().admin().indices().prepareRefresh().get(); client().admin().indices().prepareRefresh().get();
int iterations = randomIntBetween(2, 11); int iterations = randomIntBetween(2, 11);
String query =
"{" +
" \"script\": {" +
" \"lang\": \"mustache\"," +
" \"source\": {" +
" \"query\": {" +
" \"match_phrase_prefix\": {" +
" \"searchtext\": {" +
" \"query\": \"{{P_Keyword1}}\"," +
" \"slop\": {{slop}}" +
" }" +
" }" +
" }" +
" }" +
" }" +
"}";
for (int i = 1; i < iterations; i++) { for (int i = 1; i < iterations; i++) {
assertAcked(client().admin().cluster().preparePutStoredScript() assertAcked(client().admin().cluster().preparePutStoredScript()
.setId("git01") .setId("git01")
.setContent(new BytesArray( .setContent(new BytesArray(query.replace("{{slop}}", Integer.toString(-1))), XContentType.JSON)
"{\"template\":{\"query\": {\"match_phrase_prefix\": {\"searchtext\": {\"query\": \"{{P_Keyword1}}\"," );
+ "\"slop\": -1}}}}}"),
XContentType.JSON));
GetStoredScriptResponse getResponse = client().admin().cluster().prepareGetStoredScript("git01").get(); GetStoredScriptResponse getResponse = client().admin().cluster().prepareGetStoredScript("git01").get();
assertNotNull(getResponse.getSource()); assertNotNull(getResponse.getSource());
@ -304,8 +304,8 @@ public class SearchTemplateIT extends ESSingleNodeTestCase {
assertAcked(client().admin().cluster().preparePutStoredScript() assertAcked(client().admin().cluster().preparePutStoredScript()
.setId("git01") .setId("git01")
.setContent(new BytesArray("{\"query\": {\"match_phrase_prefix\": {\"searchtext\": {\"query\": \"{{P_Keyword1}}\"," + .setContent(new BytesArray(query.replace("{{slop}}", Integer.toString(0))), XContentType.JSON)
"\"slop\": 0}}}}"), XContentType.JSON)); );
SearchTemplateResponse searchResponse = new SearchTemplateRequestBuilder(client()) SearchTemplateResponse searchResponse = new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest("testindex").types("test")) .setRequest(new SearchRequest("testindex").types("test"))
@ -313,16 +313,30 @@ public class SearchTemplateIT extends ESSingleNodeTestCase {
.get(); .get();
assertHitCount(searchResponse.getResponse(), 1); assertHitCount(searchResponse.getResponse(), 1);
} }
assertWarnings("the template context is now deprecated. Specify templates in a \"script\" element.");
} }
public void testIndexedTemplateWithArray() throws Exception { public void testIndexedTemplateWithArray() throws Exception {
String multiQuery = "{\"query\":{\"terms\":{\"theField\":[\"{{#fieldParam}}\",\"{{.}}\",\"{{/fieldParam}}\"]}}}"; String multiQuery =
"{\n" +
" \"script\": {\n" +
" \"lang\": \"mustache\",\n" +
" \"source\": {\n" +
" \"query\": {\n" +
" \"terms\": {\n" +
" \"theField\": [\n" +
" \"{{#fieldParam}}\",\n" +
" \"{{.}}\",\n" +
" \"{{/fieldParam}}\"\n" +
" ]\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertAcked( assertAcked(
client().admin().cluster().preparePutStoredScript() client().admin().cluster().preparePutStoredScript()
.setId("4") .setId("4")
.setContent(BytesReference.bytes(jsonBuilder().startObject().field("template", multiQuery).endObject()), .setContent(new BytesArray(multiQuery), XContentType.JSON)
XContentType.JSON)
); );
BulkRequestBuilder bulkRequestBuilder = client().prepareBulk(); BulkRequestBuilder bulkRequestBuilder = client().prepareBulk();
bulkRequestBuilder.add(client().prepareIndex("test", "type", "1").setSource("{\"theField\":\"foo\"}", XContentType.JSON)); bulkRequestBuilder.add(client().prepareIndex("test", "type", "1").setSource("{\"theField\":\"foo\"}", XContentType.JSON));
@ -342,7 +356,6 @@ public class SearchTemplateIT extends ESSingleNodeTestCase {
.setScript("4").setScriptType(ScriptType.STORED).setScriptParams(arrayTemplateParams) .setScript("4").setScriptType(ScriptType.STORED).setScriptParams(arrayTemplateParams)
.get(); .get();
assertHitCount(searchResponse.getResponse(), 5); assertHitCount(searchResponse.getResponse(), 5);
assertWarnings("the template context is now deprecated. Specify templates in a \"script\" element.");
} }
} }

View File

@ -114,11 +114,7 @@ public class GetStoredScriptResponse extends ActionResponse implements StatusToX
super.readFrom(in); super.readFrom(in);
if (in.readBoolean()) { if (in.readBoolean()) {
if (in.getVersion().onOrAfter(Version.V_5_3_0)) { source = new StoredScriptSource(in);
source = new StoredScriptSource(in);
} else {
source = new StoredScriptSource(in.readString());
}
} else { } else {
source = null; source = null;
} }
@ -136,12 +132,7 @@ public class GetStoredScriptResponse extends ActionResponse implements StatusToX
out.writeBoolean(false); out.writeBoolean(false);
} else { } else {
out.writeBoolean(true); out.writeBoolean(true);
source.writeTo(out);
if (out.getVersion().onOrAfter(Version.V_5_3_0)) {
source.writeTo(out);
} else {
out.writeString(source.getSource());
}
} }
if (out.getVersion().onOrAfter(Version.V_6_4_0)) { if (out.getVersion().onOrAfter(Version.V_6_4_0)) {
out.writeString(id); out.writeString(id);

View File

@ -19,15 +19,12 @@
package org.elasticsearch.script; package org.elasticsearch.script;
import org.elasticsearch.Version;
import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptResponse;
import org.elasticsearch.cluster.AbstractDiffable; import org.elasticsearch.cluster.AbstractDiffable;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.Diff; import org.elasticsearch.cluster.Diff;
import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
@ -69,16 +66,6 @@ public class StoredScriptSource extends AbstractDiffable<StoredScriptSource> imp
*/ */
public static final ParseField SCRIPT_PARSE_FIELD = new ParseField("script"); public static final ParseField SCRIPT_PARSE_FIELD = new ParseField("script");
/**
* Standard {@link ParseField} for outer level of stored script source.
*/
public static final ParseField TEMPLATE_PARSE_FIELD = new ParseField("template");
/**
* Standard {@link ParseField} for query on the inner field.
*/
public static final ParseField TEMPLATE_NO_WRAPPER_PARSE_FIELD = new ParseField("query");
/** /**
* Standard {@link ParseField} for lang on the inner level. * Standard {@link ParseField} for lang on the inner level.
*/ */
@ -194,26 +181,6 @@ public class StoredScriptSource extends AbstractDiffable<StoredScriptSource> imp
PARSER.declareField(Builder::setOptions, XContentParser::mapStrings, OPTIONS_PARSE_FIELD, ValueType.OBJECT); PARSER.declareField(Builder::setOptions, XContentParser::mapStrings, OPTIONS_PARSE_FIELD, ValueType.OBJECT);
} }
private static StoredScriptSource parseRemaining(Token token, XContentParser parser) throws IOException {
try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
if (token != Token.START_OBJECT) {
builder.startObject();
builder.copyCurrentStructure(parser);
builder.endObject();
} else {
builder.copyCurrentStructure(parser);
}
String source = Strings.toString(builder);
if (source == null || source.isEmpty()) {
DEPRECATION_LOGGER.deprecated("empty templates should no longer be used");
}
return new StoredScriptSource(Script.DEFAULT_TEMPLATE_LANG, source, Collections.emptyMap());
}
}
/** /**
* This will parse XContent into a {@link StoredScriptSource}. The following formats can be parsed: * This will parse XContent into a {@link StoredScriptSource}. The following formats can be parsed:
* *
@ -271,27 +238,8 @@ public class StoredScriptSource extends AbstractDiffable<StoredScriptSource> imp
* } * }
* } * }
* *
* The simple template format: * Note that the "source" parameter can also handle template parsing including from
* * a complex JSON object.
* {@code
* {
* "query" : ...
* }
* }
*
* The complex template format:
*
* {@code
* {
* "template": {
* "query" : ...
* }
* }
* }
*
* Note that templates can be handled as both strings and complex JSON objects.
* Also templates may be part of the 'source' parameter in a script. The Parser
* can handle this case as well.
* *
* @param content The content from the request to be parsed as described above. * @param content The content from the request to be parsed as described above.
* @return The parsed {@link StoredScriptSource}. * @return The parsed {@link StoredScriptSource}.
@ -316,7 +264,7 @@ public class StoredScriptSource extends AbstractDiffable<StoredScriptSource> imp
if (token != Token.FIELD_NAME) { if (token != Token.FIELD_NAME) {
throw new ParsingException(parser.getTokenLocation(), "unexpected token [" + token + ", expected [" + throw new ParsingException(parser.getTokenLocation(), "unexpected token [" + token + ", expected [" +
SCRIPT_PARSE_FIELD.getPreferredName() + ", " + TEMPLATE_PARSE_FIELD.getPreferredName()); SCRIPT_PARSE_FIELD.getPreferredName() + "]");
} }
String name = parser.currentName(); String name = parser.currentName();
@ -329,28 +277,9 @@ public class StoredScriptSource extends AbstractDiffable<StoredScriptSource> imp
} else { } else {
throw new ParsingException(parser.getTokenLocation(), "unexpected token [" + token + "], expected [{, <source>]"); throw new ParsingException(parser.getTokenLocation(), "unexpected token [" + token + "], expected [{, <source>]");
} }
} else if (TEMPLATE_PARSE_FIELD.getPreferredName().equals(name)) {
DEPRECATION_LOGGER.deprecated("the template context is now deprecated. Specify templates in a \"script\" element.");
token = parser.nextToken();
if (token == Token.VALUE_STRING) {
String source = parser.text();
if (source == null || source.isEmpty()) {
DEPRECATION_LOGGER.deprecated("empty templates should no longer be used");
}
return new StoredScriptSource(Script.DEFAULT_TEMPLATE_LANG, source, Collections.emptyMap());
} else {
return parseRemaining(token, parser);
}
} else if (TEMPLATE_NO_WRAPPER_PARSE_FIELD.getPreferredName().equals(name)) {
DEPRECATION_LOGGER.deprecated("the template context is now deprecated. Specify templates in a \"script\" element.");
return parseRemaining(token, parser);
} else { } else {
DEPRECATION_LOGGER.deprecated("scripts should not be stored without a context. Specify them in a \"script\" element."); throw new ParsingException(parser.getTokenLocation(), "unexpected field [" + name + "], expected [" +
return parseRemaining(token, parser); SCRIPT_PARSE_FIELD.getPreferredName() + "]");
} }
} catch (IOException ioe) { } catch (IOException ioe) {
throw new UncheckedIOException(ioe); throw new UncheckedIOException(ioe);
@ -397,16 +326,6 @@ public class StoredScriptSource extends AbstractDiffable<StoredScriptSource> imp
private final String source; private final String source;
private final Map<String, String> options; private final Map<String, String> options;
/**
* Constructor for use with {@link GetStoredScriptResponse}
* to support the deprecated stored script namespace.
*/
public StoredScriptSource(String source) {
this.lang = null;
this.source = Objects.requireNonNull(source);
this.options = null;
}
/** /**
* Standard StoredScriptSource constructor. * Standard StoredScriptSource constructor.
* @param lang The language to compile the script with. Must not be {@code null}. * @param lang The language to compile the script with. Must not be {@code null}.
@ -426,35 +345,24 @@ public class StoredScriptSource extends AbstractDiffable<StoredScriptSource> imp
* only the source parameter will be read in as a bytes reference. * only the source parameter will be read in as a bytes reference.
*/ */
public StoredScriptSource(StreamInput in) throws IOException { public StoredScriptSource(StreamInput in) throws IOException {
if (in.getVersion().onOrAfter(Version.V_5_3_0)) { this.lang = in.readString();
this.lang = in.readString(); this.source = in.readString();
this.source = in.readString(); @SuppressWarnings("unchecked")
@SuppressWarnings("unchecked") Map<String, String> options = (Map<String, String>)(Map)in.readMap();
Map<String, String> options = (Map<String, String>)(Map)in.readMap(); this.options = options;
this.options = options;
} else {
this.lang = null;
this.source = in.readBytesReference().utf8ToString();
this.options = null;
}
} }
/** /**
* Writes a {@link StoredScriptSource} to a stream. Version 5.3+ will write * Writes a {@link StoredScriptSource} to a stream. Will write
* all of the lang, source, and options parameters. For versions prior to 5.3, * all of the lang, source, and options parameters.
* only the source parameter will be read in as a bytes reference.
*/ */
@Override @Override
public void writeTo(StreamOutput out) throws IOException { public void writeTo(StreamOutput out) throws IOException {
if (out.getVersion().onOrAfter(Version.V_5_3_0)) { out.writeString(lang);
out.writeString(lang); out.writeString(source);
out.writeString(source); @SuppressWarnings("unchecked")
@SuppressWarnings("unchecked") Map<String, Object> options = (Map<String, Object>)(Map)this.options;
Map<String, Object> options = (Map<String, Object>)(Map)this.options; out.writeMap(options);
out.writeMap(options);
} else {
out.writeBytesReference(new BytesArray(source));
}
} }
/** /**

View File

@ -77,14 +77,12 @@ public class ScriptMetaDataTests extends AbstractSerializingTestCase<ScriptMetaD
ScriptMetaData.Builder builder = new ScriptMetaData.Builder(null); ScriptMetaData.Builder builder = new ScriptMetaData.Builder(null);
XContentBuilder sourceBuilder = XContentFactory.jsonBuilder(); XContentBuilder sourceBuilder = XContentFactory.jsonBuilder();
sourceBuilder.startObject().startObject("template").field("field", "value").endObject().endObject(); sourceBuilder.startObject().startObject("script")
builder.storeScript("template", StoredScriptSource.parse(BytesReference.bytes(sourceBuilder), sourceBuilder.contentType())); .field("lang", "_lang")
assertWarnings("the template context is now deprecated. Specify templates in a \"script\" element."); .startObject("source").field("field", "value").endObject()
.endObject().endObject();
sourceBuilder = XContentFactory.jsonBuilder(); builder.storeScript("source_template", StoredScriptSource.parse(BytesReference.bytes(sourceBuilder),
sourceBuilder.startObject().field("template", "value").endObject(); sourceBuilder.contentType()));
builder.storeScript("template_field", StoredScriptSource.parse(BytesReference.bytes(sourceBuilder), sourceBuilder.contentType()));
assertWarnings("the template context is now deprecated. Specify templates in a \"script\" element.");
sourceBuilder = XContentFactory.jsonBuilder(); sourceBuilder = XContentFactory.jsonBuilder();
sourceBuilder.startObject().startObject("script").field("lang", "_lang").field("source", "_source").endObject().endObject(); sourceBuilder.startObject().startObject("script").field("lang", "_lang").field("source", "_source").endObject().endObject();
@ -92,26 +90,25 @@ public class ScriptMetaDataTests extends AbstractSerializingTestCase<ScriptMetaD
ScriptMetaData scriptMetaData = builder.build(); ScriptMetaData scriptMetaData = builder.build();
assertEquals("_source", scriptMetaData.getStoredScript("script").getSource()); assertEquals("_source", scriptMetaData.getStoredScript("script").getSource());
assertEquals("{\"field\":\"value\"}", scriptMetaData.getStoredScript("template").getSource()); assertEquals("{\"field\":\"value\"}", scriptMetaData.getStoredScript("source_template").getSource());
assertEquals("value", scriptMetaData.getStoredScript("template_field").getSource());
} }
public void testDiff() throws Exception { public void testDiff() throws Exception {
ScriptMetaData.Builder builder = new ScriptMetaData.Builder(null); ScriptMetaData.Builder builder = new ScriptMetaData.Builder(null);
builder.storeScript("1", StoredScriptSource.parse(new BytesArray("{\"foo\":\"abc\"}"), XContentType.JSON)); builder.storeScript("1", StoredScriptSource.parse(
assertWarnings("scripts should not be stored without a context. Specify them in a \"script\" element."); new BytesArray("{\"script\":{\"lang\":\"mustache\",\"source\":{\"foo\":\"abc\"}}}"), XContentType.JSON));
builder.storeScript("2", StoredScriptSource.parse(new BytesArray("{\"foo\":\"def\"}"), XContentType.JSON)); builder.storeScript("2", StoredScriptSource.parse(
assertWarnings("scripts should not be stored without a context. Specify them in a \"script\" element."); new BytesArray("{\"script\":{\"lang\":\"mustache\",\"source\":{\"foo\":\"def\"}}}"), XContentType.JSON));
builder.storeScript("3", StoredScriptSource.parse(new BytesArray("{\"foo\":\"ghi\"}"), XContentType.JSON)); builder.storeScript("3", StoredScriptSource.parse(
assertWarnings("scripts should not be stored without a context. Specify them in a \"script\" element."); new BytesArray("{\"script\":{\"lang\":\"mustache\",\"source\":{\"foo\":\"ghi\"}}}"), XContentType.JSON));
ScriptMetaData scriptMetaData1 = builder.build(); ScriptMetaData scriptMetaData1 = builder.build();
builder = new ScriptMetaData.Builder(scriptMetaData1); builder = new ScriptMetaData.Builder(scriptMetaData1);
builder.storeScript("2", StoredScriptSource.parse(new BytesArray("{\"foo\":\"changed\"}"), XContentType.JSON)); builder.storeScript("2", StoredScriptSource.parse(
assertWarnings("scripts should not be stored without a context. Specify them in a \"script\" element."); new BytesArray("{\"script\":{\"lang\":\"mustache\",\"source\":{\"foo\":\"changed\"}}}"), XContentType.JSON));
builder.deleteScript("3"); builder.deleteScript("3");
builder.storeScript("4", StoredScriptSource.parse(new BytesArray("{\"foo\":\"jkl\"}"), XContentType.JSON)); builder.storeScript("4", StoredScriptSource.parse(
assertWarnings("scripts should not be stored without a context. Specify them in a \"script\" element."); new BytesArray("{\"script\":{\"lang\":\"mustache\",\"source\":{\"foo\":\"jkl\"}}}"), XContentType.JSON));
ScriptMetaData scriptMetaData2 = builder.build(); ScriptMetaData scriptMetaData2 = builder.build();
ScriptMetaData.ScriptMetadataDiff diff = (ScriptMetaData.ScriptMetadataDiff) scriptMetaData2.diff(scriptMetaData1); ScriptMetaData.ScriptMetadataDiff diff = (ScriptMetaData.ScriptMetadataDiff) scriptMetaData2.diff(scriptMetaData1);

View File

@ -40,19 +40,21 @@ public class StoredScriptSourceTests extends AbstractSerializingTestCase<StoredS
try { try {
XContentBuilder template = XContentBuilder.builder(xContentType.xContent()); XContentBuilder template = XContentBuilder.builder(xContentType.xContent());
template.startObject(); template.startObject();
template.startObject("query"); template.startObject("script");
template.startObject("match"); {
template.field("title", "{{query_string}}"); template.field("lang", "mustache");
template.endObject(); template.startObject("source");
template.startObject("query").startObject("match").field("title", "{{query_string}}").endObject();
template.endObject();
template.endObject();
}
template.endObject(); template.endObject();
template.endObject(); template.endObject();
Map<String, String> options = new HashMap<>(); Map<String, String> options = new HashMap<>();
if (randomBoolean()) { if (randomBoolean()) {
options.put(Script.CONTENT_TYPE_OPTION, xContentType.mediaType()); options.put(Script.CONTENT_TYPE_OPTION, xContentType.mediaType());
} }
StoredScriptSource source = StoredScriptSource.parse(BytesReference.bytes(template), xContentType); return StoredScriptSource.parse(BytesReference.bytes(template), xContentType);
assertWarnings("the template context is now deprecated. Specify templates in a \"script\" element.");
return source;
} catch (IOException e) { } catch (IOException e) {
throw new AssertionError("Failed to create test instance", e); throw new AssertionError("Failed to create test instance", e);
} }
@ -84,7 +86,7 @@ public class StoredScriptSourceTests extends AbstractSerializingTestCase<StoredS
newTemplate.endObject(); newTemplate.endObject();
newTemplate.endObject(); newTemplate.endObject();
switch (between(0, 3)) { switch (between(0, 2)) {
case 0: case 0:
source = Strings.toString(newTemplate); source = Strings.toString(newTemplate);
break; break;
@ -92,12 +94,9 @@ public class StoredScriptSourceTests extends AbstractSerializingTestCase<StoredS
lang = randomAlphaOfLengthBetween(1, 20); lang = randomAlphaOfLengthBetween(1, 20);
break; break;
case 2: case 2:
default:
options = new HashMap<>(options); options = new HashMap<>(options);
options.put(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20)); options.put(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20));
break;
case 3:
default:
return new StoredScriptSource(Strings.toString(newTemplate));
} }
return new StoredScriptSource(lang, source, options); return new StoredScriptSource(lang, source, options);
} }

View File

@ -20,6 +20,7 @@
package org.elasticsearch.script; package org.elasticsearch.script;
import org.elasticsearch.ResourceNotFoundException; import org.elasticsearch.ResourceNotFoundException;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.io.stream.Writeable;
@ -66,49 +67,6 @@ public class StoredScriptTests extends AbstractSerializingTestCase<StoredScriptS
assertThat(parsed, equalTo(source)); assertThat(parsed, equalTo(source));
} }
// simple template value string
try (XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON)) {
builder.startObject().field("template", "code").endObject();
StoredScriptSource parsed = StoredScriptSource.parse(BytesReference.bytes(builder), XContentType.JSON);
StoredScriptSource source = new StoredScriptSource("mustache", "code", Collections.emptyMap());
assertThat(parsed, equalTo(source));
assertWarnings("the template context is now deprecated. Specify templates in a \"script\" element.");
}
// complex template with wrapper template object
try (XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON)) {
builder.startObject().field("template").startObject().field("query", "code").endObject().endObject();
String code;
try (XContentBuilder cb = XContentFactory.contentBuilder(builder.contentType())) {
code = Strings.toString(cb.startObject().field("query", "code").endObject());
}
StoredScriptSource parsed = StoredScriptSource.parse(BytesReference.bytes(builder), XContentType.JSON);
StoredScriptSource source = new StoredScriptSource("mustache", code, Collections.emptyMap());
assertThat(parsed, equalTo(source));
assertWarnings("the template context is now deprecated. Specify templates in a \"script\" element.");
}
// complex template with no wrapper object
try (XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON)) {
builder.startObject().field("query", "code").endObject();
String code;
try (XContentBuilder cb = XContentFactory.contentBuilder(builder.contentType())) {
code = Strings.toString(cb.startObject().field("query", "code").endObject());
}
StoredScriptSource parsed = StoredScriptSource.parse(BytesReference.bytes(builder), XContentType.JSON);
StoredScriptSource source = new StoredScriptSource("mustache", code, Collections.emptyMap());
assertThat(parsed, equalTo(source));
assertWarnings("the template context is now deprecated. Specify templates in a \"script\" element.");
}
// complex template using script as the field name // complex template using script as the field name
try (XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON)) { try (XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON)) {
builder.startObject().startObject("script").field("lang", "mustache") builder.startObject().startObject("script").field("lang", "mustache")
@ -206,6 +164,15 @@ public class StoredScriptTests extends AbstractSerializingTestCase<StoredScriptS
StoredScriptSource.parse(BytesReference.bytes(builder), XContentType.JSON)); StoredScriptSource.parse(BytesReference.bytes(builder), XContentType.JSON));
assertThat(iae.getMessage(), equalTo("illegal compiler options [{option=option}] specified")); assertThat(iae.getMessage(), equalTo("illegal compiler options [{option=option}] specified"));
} }
// check for unsupported template context
try (XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON)) {
builder.startObject().field("template", "code").endObject();
ParsingException pEx = expectThrows(ParsingException.class, () ->
StoredScriptSource.parse(BytesReference.bytes(builder), XContentType.JSON));
assertThat(pEx.getMessage(), equalTo("unexpected field [template], expected ["+
StoredScriptSource.SCRIPT_PARSE_FIELD.getPreferredName()+ "]"));
}
} }
public void testEmptyTemplateDeprecations() throws IOException { public void testEmptyTemplateDeprecations() throws IOException {
@ -219,19 +186,6 @@ public class StoredScriptTests extends AbstractSerializingTestCase<StoredScriptS
assertWarnings("empty templates should no longer be used"); assertWarnings("empty templates should no longer be used");
} }
try (XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON)) {
builder.startObject().field("template", "").endObject();
StoredScriptSource parsed = StoredScriptSource.parse(BytesReference.bytes(builder), XContentType.JSON);
StoredScriptSource source = new StoredScriptSource(Script.DEFAULT_TEMPLATE_LANG, "", Collections.emptyMap());
assertThat(parsed, equalTo(source));
assertWarnings(
"the template context is now deprecated. Specify templates in a \"script\" element.",
"empty templates should no longer be used"
);
}
try (XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON)) { try (XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON)) {
builder.startObject().field("script").startObject().field("lang", "mustache") builder.startObject().field("script").startObject().field("lang", "mustache")
.field("source", "").endObject().endObject(); .field("source", "").endObject().endObject();

View File

@ -220,7 +220,11 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
SearchSourceBuilder searchSourceBuilder = searchSource().query(matchQuery("level", "a")); SearchSourceBuilder searchSourceBuilder = searchSource().query(matchQuery("level", "a"));
assertAcked(client().admin().cluster().preparePutStoredScript() assertAcked(client().admin().cluster().preparePutStoredScript()
.setId("my-template") .setId("my-template")
.setContent(BytesReference.bytes(jsonBuilder().startObject().field("template").value(searchSourceBuilder).endObject()), .setContent(BytesReference.bytes(
jsonBuilder().startObject().startObject("script")
.field("lang", "mustache")
.field("source").value(searchSourceBuilder)
.endObject().endObject()),
XContentType.JSON) XContentType.JSON)
.get()); .get());