Scripting: Add watcher script contexts (#34059)

This commit removes the use of ExecutableScript from watcher in favor of
custom script contexts for both watcher condition scripts and transform
scripts.
This commit is contained in:
Ryan Ernst 2018-09-28 07:58:17 -07:00 committed by GitHub
parent 0c3846d3d5
commit 95977f4db9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 324 additions and 129 deletions

View File

@ -19,11 +19,12 @@
package org.elasticsearch.script;
import org.apache.logging.log4j.LogManager;
import org.elasticsearch.common.logging.DeprecationLogger;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.elasticsearch.common.logging.DeprecationLogger;
public final class ParameterMap implements Map<String, Object> {
@ -34,7 +35,7 @@ public final class ParameterMap implements Map<String, Object> {
private final Map<String, String> deprecations;
ParameterMap(Map<String, Object> params, Map<String, String> deprecations) {
public ParameterMap(Map<String, Object> params, Map<String, String> deprecations) {
this.params = params;
this.deprecations = deprecations;
}

View File

@ -48,7 +48,6 @@ import org.elasticsearch.plugins.ReloadablePlugin;
import org.elasticsearch.plugins.ScriptPlugin;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestHandler;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.SearchScript;
@ -106,6 +105,7 @@ import org.elasticsearch.xpack.watcher.condition.CompareCondition;
import org.elasticsearch.xpack.watcher.condition.InternalAlwaysCondition;
import org.elasticsearch.xpack.watcher.condition.NeverCondition;
import org.elasticsearch.xpack.watcher.condition.ScriptCondition;
import org.elasticsearch.xpack.watcher.condition.WatcherConditionScript;
import org.elasticsearch.xpack.watcher.execution.AsyncTriggerEventConsumer;
import org.elasticsearch.xpack.watcher.execution.ExecutionService;
import org.elasticsearch.xpack.watcher.execution.InternalWatchExecutor;
@ -152,6 +152,7 @@ import org.elasticsearch.xpack.watcher.support.WatcherIndexTemplateRegistry;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import org.elasticsearch.xpack.watcher.transform.script.ScriptTransform;
import org.elasticsearch.xpack.watcher.transform.script.ScriptTransformFactory;
import org.elasticsearch.xpack.watcher.transform.script.WatcherTransformScript;
import org.elasticsearch.xpack.watcher.transform.search.SearchTransform;
import org.elasticsearch.xpack.watcher.transform.search.SearchTransformFactory;
import org.elasticsearch.xpack.watcher.transport.actions.ack.TransportAckWatchAction;
@ -225,9 +226,6 @@ public class Watcher extends Plugin implements ActionPlugin, ScriptPlugin, Reloa
public static final ScriptContext<SearchScript.Factory> SCRIPT_SEARCH_CONTEXT =
new ScriptContext<>("xpack", SearchScript.Factory.class);
// TODO: remove this context when each xpack script use case has their own contexts
public static final ScriptContext<ExecutableScript.Factory> SCRIPT_EXECUTABLE_CONTEXT
= new ScriptContext<>("xpack_executable", ExecutableScript.Factory.class);
public static final ScriptContext<TemplateScript.Factory> SCRIPT_TEMPLATE_CONTEXT
= new ScriptContext<>("xpack_template", TemplateScript.Factory.class);
@ -673,7 +671,8 @@ public class Watcher extends Plugin implements ActionPlugin, ScriptPlugin, Reloa
@Override
public List<ScriptContext<?>> getContexts() {
return Arrays.asList(Watcher.SCRIPT_SEARCH_CONTEXT, Watcher.SCRIPT_EXECUTABLE_CONTEXT, Watcher.SCRIPT_TEMPLATE_CONTEXT);
return Arrays.asList(Watcher.SCRIPT_SEARCH_CONTEXT, WatcherTransformScript.CONTEXT,
WatcherConditionScript.CONTEXT, Watcher.SCRIPT_TEMPLATE_CONTEXT);
}
@Override

View File

@ -41,7 +41,7 @@ public class ExecutableEmailAction extends ExecutableAction<EmailAction> {
}
public Action.Result execute(String actionId, WatchExecutionContext ctx, Payload payload) throws Exception {
Map<String, Object> model = Variables.createCtxModel(ctx, payload);
Map<String, Object> model = Variables.createCtxParamsMap(ctx, payload);
Map<String, Attachment> attachments = new HashMap<>();
DataAttachment dataAttachment = action.getDataAttachment();

View File

@ -39,7 +39,7 @@ public class ExecutableHipChatAction extends ExecutableAction<HipChatAction> {
// watch/action were created.
account.validateParsedTemplate(ctx.id().watchId(), actionId, action.message);
Map<String, Object> model = Variables.createCtxModel(ctx, payload);
Map<String, Object> model = Variables.createCtxParamsMap(ctx, payload);
HipChatMessage message = account.render(ctx.id().watchId(), actionId, templateEngine, action.message, model);
if (ctx.simulateAction(actionId)) {

View File

@ -42,7 +42,7 @@ public class ExecutableJiraAction extends ExecutableAction<JiraAction> {
throw new IllegalStateException("account [" + action.account + "] was not found. perhaps it was deleted");
}
final Function<String, String> render = s -> engine.render(new TextTemplate(s), Variables.createCtxModel(ctx, payload));
final Function<String, String> render = s -> engine.render(new TextTemplate(s), Variables.createCtxParamsMap(ctx, payload));
Map<String, Object> fields = new HashMap<>();
// Apply action fields

View File

@ -40,7 +40,7 @@ public class ExecutableLoggingAction extends ExecutableAction<LoggingAction> {
@Override
public Action.Result execute(String actionId, WatchExecutionContext ctx, Payload payload) throws Exception {
Map<String, Object> model = Variables.createCtxModel(ctx, payload);
Map<String, Object> model = Variables.createCtxParamsMap(ctx, payload);
String loggedText = templateEngine.render(action.text, model);
if (ctx.simulateAction(actionId)) {

View File

@ -40,7 +40,7 @@ public class ExecutablePagerDutyAction extends ExecutableAction<PagerDutyAction>
throw new IllegalStateException("account [" + action.event.account + "] was not found. perhaps it was deleted");
}
Map<String, Object> model = Variables.createCtxModel(ctx, payload);
Map<String, Object> model = Variables.createCtxParamsMap(ctx, payload);
IncidentEvent event = action.event.render(ctx.watch().id(), actionId, templateEngine, model, account.getDefaults());
if (ctx.simulateAction(actionId)) {

View File

@ -40,7 +40,7 @@ public class ExecutableSlackAction extends ExecutableAction<SlackAction> {
throw new IllegalStateException("account [" + action.account + "] was not found. perhaps it was deleted");
}
Map<String, Object> model = Variables.createCtxModel(ctx, payload);
Map<String, Object> model = Variables.createCtxParamsMap(ctx, payload);
SlackMessage message = action.message.render(ctx.id().watchId(), actionId, templateEngine, model, account.getMessageDefaults());
if (ctx.simulateAction(actionId)) {

View File

@ -31,7 +31,7 @@ public class ExecutableWebhookAction extends ExecutableAction<WebhookAction> {
@Override
public Action.Result execute(String actionId, WatchExecutionContext ctx, Payload payload) throws Exception {
Map<String, Object> model = Variables.createCtxModel(ctx, payload);
Map<String, Object> model = Variables.createCtxParamsMap(ctx, payload);
HttpRequest request = action.requestTemplate.render(templateEngine, model);

View File

@ -36,7 +36,7 @@ abstract class AbstractCompareCondition implements ExecutableCondition {
@Override
public final Result execute(WatchExecutionContext ctx) {
Map<String, Object> resolvedValues = new HashMap<>();
Map<String, Object> model = Variables.createCtxModel(ctx, ctx.payload());
Map<String, Object> model = Variables.createCtxParamsMap(ctx, ctx.payload());
return doExecute(model, resolvedValues);
}

View File

@ -8,19 +8,15 @@ package org.elasticsearch.xpack.watcher.condition;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.xpack.core.watcher.condition.ExecutableCondition;
import org.elasticsearch.xpack.core.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.watcher.Watcher;
import org.elasticsearch.xpack.watcher.support.Variables;
import java.io.IOException;
import java.util.Map;
import static org.elasticsearch.xpack.core.watcher.support.Exceptions.illegalState;
/**
* This class executes a script against the ctx payload and returns a boolean
*/
@ -30,16 +26,16 @@ public final class ScriptCondition implements ExecutableCondition {
private static final Result UNMET = new Result(null, TYPE, false);
private final Script script;
private final ExecutableScript.Factory scriptFactory;
private final WatcherConditionScript.Factory scriptFactory;
public ScriptCondition(Script script) {
this.script = script;
scriptFactory = null;
this.scriptFactory = null;
}
ScriptCondition(Script script, ExecutableScript.Factory scriptFactory) {
ScriptCondition(Script script, ScriptService scriptService) {
this.script = script;
this.scriptFactory = scriptFactory;
this.scriptFactory = scriptService.compile(script, WatcherConditionScript.CONTEXT);
}
public Script getScript() {
@ -49,7 +45,7 @@ public final class ScriptCondition implements ExecutableCondition {
public static ScriptCondition parse(ScriptService scriptService, String watchId, XContentParser parser) throws IOException {
try {
Script script = Script.parse(parser);
return new ScriptCondition(script, scriptService.compile(script, Watcher.SCRIPT_EXECUTABLE_CONTEXT));
return new ScriptCondition(script, scriptService);
} catch (ElasticsearchParseException pe) {
throw new ElasticsearchParseException("could not parse [{}] condition for watch [{}]. failed to parse script", pe, TYPE,
watchId);
@ -62,17 +58,12 @@ public final class ScriptCondition implements ExecutableCondition {
}
public Result doExecute(WatchExecutionContext ctx) {
Map<String, Object> parameters = Variables.createCtxModel(ctx, ctx.payload());
Map<String, Object> parameters = Variables.createCtxParamsMap(ctx, ctx.payload());
if (script.getParams() != null && !script.getParams().isEmpty()) {
parameters.putAll(script.getParams());
}
ExecutableScript executable = scriptFactory.newInstance(parameters);
Object value = executable.run();
if (value instanceof Boolean) {
return (Boolean) value ? MET : UNMET;
}
throw illegalState("condition [{}] must return a boolean value (true|false) but instead returned [{}]", type(), ctx.watch().id(),
script, value);
WatcherConditionScript conditionScript = scriptFactory.newInstance(script.getParams(), ctx);
return conditionScript.execute() ? MET : UNMET;
}
@Override

View File

@ -0,0 +1,62 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.watcher.condition;
import org.elasticsearch.script.ParameterMap;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.xpack.core.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.watcher.support.Variables;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* A script to determine whether a watch should be run.
*/
public abstract class WatcherConditionScript {
public static final String[] PARAMETERS = {};
private static final Map<String, String> DEPRECATIONS;
static {
Map<String, String> deprecations = new HashMap<>();
deprecations.put(
"ctx",
"Accessing variable [ctx] via [params.ctx] from within a watcher_condition script " +
"is deprecated in favor of directly accessing [ctx]."
);
DEPRECATIONS = Collections.unmodifiableMap(deprecations);
}
private final Map<String, Object> params;
// TODO: ctx should have its members extracted into execute parameters, but it needs to be a member for bwc access in params
private final Map<String, Object> ctx;
public WatcherConditionScript(Map<String, Object> params, WatchExecutionContext watcherContext) {
Map<String, Object> paramsWithCtx = new HashMap<>(params);
Map<String, Object> ctx = Variables.createCtx(watcherContext, watcherContext.payload());
paramsWithCtx.put("ctx", ctx);
this.params = new ParameterMap(Collections.unmodifiableMap(paramsWithCtx), DEPRECATIONS);
this.ctx = ctx;
}
public abstract boolean execute();
public Map<String, Object> getParams() {
return params;
}
public Map<String, Object> getCtx() {
return ctx;
}
public interface Factory {
WatcherConditionScript newInstance(Map<String, Object> params, WatchExecutionContext watcherContext);
}
public static ScriptContext<Factory> CONTEXT = new ScriptContext<>("watcher_condition", Factory.class);
}

View File

@ -44,7 +44,7 @@ public class ExecutableHttpInput extends ExecutableInput<HttpInput, HttpInput.Re
public HttpInput.Result execute(WatchExecutionContext ctx, Payload payload) {
HttpRequest request = null;
try {
Map<String, Object> model = Variables.createCtxModel(ctx, payload);
Map<String, Object> model = Variables.createCtxParamsMap(ctx, payload);
request = input.getRequest().render(templateEngine, model);
return doExecute(ctx, request);
} catch (Exception e) {

View File

@ -57,7 +57,7 @@ public class DataAttachmentParser implements EmailAttachmentParser<DataAttachmen
@Override
public Attachment toAttachment(WatchExecutionContext ctx, Payload payload, DataAttachment attachment) throws IOException {
Map<String, Object> model = Variables.createCtxModel(ctx, payload);
Map<String, Object> model = Variables.createCtxParamsMap(ctx, payload);
return attachment.getDataAttachment().create(attachment.id(), model);
}
}

View File

@ -79,7 +79,7 @@ public class HttpEmailAttachementParser implements EmailAttachmentParser<HttpReq
@Override
public Attachment toAttachment(WatchExecutionContext context, Payload payload,
HttpRequestAttachment attachment) throws IOException {
Map<String, Object> model = Variables.createCtxModel(context, payload);
Map<String, Object> model = Variables.createCtxParamsMap(context, payload);
HttpRequest httpRequest = attachment.getRequestTemplate().render(templateEngine, model);
HttpResponse response = httpClient.execute(httpRequest);

View File

@ -91,7 +91,7 @@ public class ReportingAttachmentParser implements EmailAttachmentParser<Reportin
@Override
public Attachment toAttachment(WatchExecutionContext context, Payload payload, ReportingAttachment attachment) throws IOException {
Map<String, Object> model = Variables.createCtxModel(context, payload);
Map<String, Object> model = Variables.createCtxParamsMap(context, payload);
String initialUrl = templateEngine.render(new TextTemplate(attachment.url()), model);

View File

@ -22,7 +22,15 @@ public final class Variables {
public static final String METADATA = "metadata";
public static final String VARS = "vars";
public static Map<String, Object> createCtxModel(WatchExecutionContext ctx, Payload payload) {
/** Creates a ctx map and puts it into the returned map as "ctx". */
public static Map<String, Object> createCtxParamsMap(WatchExecutionContext ctx, Payload payload) {
Map<String, Object> model = new HashMap<>();
model.put(CTX, createCtx(ctx, payload));
return model;
}
/** Creates a ctx map. */
public static Map<String, Object> createCtx(WatchExecutionContext ctx, Payload payload) {
Map<String, Object> ctxModel = new HashMap<>();
ctxModel.put(ID, ctx.id().value());
ctxModel.put(WATCH_ID, ctx.id().watchId());
@ -33,10 +41,6 @@ public final class Variables {
}
ctxModel.put(METADATA, ctx.watch().metadata());
ctxModel.put(VARS, ctx.vars());
Map<String, Object> model = new HashMap<>();
model.put(CTX, ctxModel);
return model;
return ctxModel;
}
}

View File

@ -45,7 +45,7 @@ public class WatcherSearchTemplateService extends AbstractComponent {
public String renderTemplate(Script source, WatchExecutionContext ctx, Payload payload) throws IOException {
// Due the inconsistency with templates in ES 1.x, we maintain our own template format.
// This template format we use now, will become the template structure in ES 2.0
Map<String, Object> watcherContextParams = Variables.createCtxModel(ctx, payload);
Map<String, Object> watcherContextParams = Variables.createCtxParamsMap(ctx, payload);
// Here we convert watcher template into a ES core templates. Due to the different format we use, we
// convert to the template format used in ES core
if (source.getParams() != null) {

View File

@ -8,19 +8,16 @@ package org.elasticsearch.xpack.watcher.transform.script;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.util.Supplier;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.xpack.core.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.core.watcher.transform.ExecutableTransform;
import org.elasticsearch.xpack.core.watcher.watch.Payload;
import org.elasticsearch.xpack.watcher.Watcher;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.elasticsearch.xpack.watcher.support.Variables.createCtxModel;
import static org.elasticsearch.xpack.watcher.transform.script.ScriptTransform.TYPE;
public class ExecutableScriptTransform extends ExecutableTransform<ScriptTransform, ScriptTransform.Result> {
@ -32,7 +29,7 @@ public class ExecutableScriptTransform extends ExecutableTransform<ScriptTransfo
this.scriptService = scriptService;
Script script = transform.getScript();
// try to compile so we catch syntax errors early
scriptService.compile(script, Watcher.SCRIPT_EXECUTABLE_CONTEXT);
scriptService.compile(script, WatcherTransformScript.CONTEXT);
}
@Override
@ -47,14 +44,9 @@ public class ExecutableScriptTransform extends ExecutableTransform<ScriptTransfo
ScriptTransform.Result doExecute(WatchExecutionContext ctx, Payload payload) throws IOException {
Script script = transform.getScript();
Map<String, Object> model = new HashMap<>();
if (script.getParams() != null) {
model.putAll(script.getParams());
}
model.putAll(createCtxModel(ctx, payload));
ExecutableScript.Factory factory = scriptService.compile(script, Watcher.SCRIPT_EXECUTABLE_CONTEXT);
ExecutableScript executable = factory.newInstance(model);
Object value = executable.run();
WatcherTransformScript.Factory factory = scriptService.compile(script, WatcherTransformScript.CONTEXT);
WatcherTransformScript transformScript = factory.newInstance(script.getParams(), ctx, payload);
Object value = transformScript.execute();
// TODO: deprecate one of these styles (returning a map or returning an opaque value below)
if (value instanceof Map) {
return new ScriptTransform.Result(new Payload.Simple((Map<String, Object>) value));

View File

@ -0,0 +1,63 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.watcher.transform.script;
import org.elasticsearch.script.ParameterMap;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.xpack.core.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.core.watcher.watch.Payload;
import org.elasticsearch.xpack.watcher.support.Variables;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* A script to transform the results of a watch execution.
*/
public abstract class WatcherTransformScript {
public static final String[] PARAMETERS = {};
private static final Map<String, String> DEPRECATIONS;
static {
Map<String, String> deprecations = new HashMap<>();
deprecations.put(
"ctx",
"Accessing variable [ctx] via [params.ctx] from within a watcher_transform script " +
"is deprecated in favor of directly accessing [ctx]."
);
DEPRECATIONS = Collections.unmodifiableMap(deprecations);
}
private final Map<String, Object> params;
// TODO: ctx should have its members extracted into execute parameters, but it needs to be a member bwc access in params
private final Map<String, Object> ctx;
public WatcherTransformScript(Map<String, Object> params, WatchExecutionContext watcherContext, Payload payload) {
Map<String, Object> paramsWithCtx = new HashMap<>(params);
Map<String, Object> ctx = Variables.createCtx(watcherContext, payload);
paramsWithCtx.put("ctx", ctx);
this.params = new ParameterMap(Collections.unmodifiableMap(paramsWithCtx), DEPRECATIONS);
this.ctx = ctx;
}
public abstract Object execute();
public Map<String, Object> getParams() {
return params;
}
public Map<String, Object> getCtx() {
return ctx;
}
public interface Factory {
WatcherTransformScript newInstance(Map<String, Object> params, WatchExecutionContext watcherContext, Payload payload);
}
public static ScriptContext<Factory> CONTEXT = new ScriptContext<>("watcher_transform", Factory.class);
}

View File

@ -12,7 +12,6 @@ import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.core.watcher.condition.ExecutableCondition;
import org.elasticsearch.xpack.watcher.Watcher;
import java.time.Clock;
@ -56,7 +55,7 @@ public class AlwaysConditionTests extends ESTestCase {
switch (type) {
case ScriptCondition.TYPE:
Script mockScript = mockScript("_script");
return new ScriptCondition(mockScript, scriptService.compile(mockScript, Watcher.SCRIPT_EXECUTABLE_CONTEXT));
return new ScriptCondition(mockScript, scriptService);
case CompareCondition.TYPE:
return new CompareCondition("_path", randomFrom(CompareCondition.Op.values()), randomFrom(5, "3"),
Clock.systemUTC());

View File

@ -13,15 +13,12 @@ import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.script.GeneralScriptException;
import org.elasticsearch.script.MockScriptEngine;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptEngine;
import org.elasticsearch.script.ScriptException;
import org.elasticsearch.script.ScriptMetaData;
import org.elasticsearch.script.ScriptService;
@ -30,9 +27,12 @@ import org.elasticsearch.search.internal.InternalSearchResponse;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.core.watcher.condition.ExecutableCondition;
import org.elasticsearch.xpack.core.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.core.watcher.execution.Wid;
import org.elasticsearch.xpack.core.watcher.trigger.TriggerEvent;
import org.elasticsearch.xpack.core.watcher.watch.Payload;
import org.elasticsearch.xpack.watcher.Watcher;
import org.elasticsearch.xpack.core.watcher.watch.Watch;
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
import org.elasticsearch.xpack.watcher.test.WatcherMockScriptPlugin;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.Before;
@ -51,6 +51,8 @@ import static org.elasticsearch.xpack.core.watcher.support.Exceptions.illegalArg
import static org.elasticsearch.xpack.watcher.test.WatcherTestUtils.mockExecutionContext;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class ScriptConditionTests extends ESTestCase {
@ -77,15 +79,13 @@ public class ScriptConditionTests extends ESTestCase {
return total > 1;
});
scripts.put("ctx.payload.hits.total > threshold", vars -> {
scripts.put("ctx.payload.hits.total > params.threshold", vars -> {
int total = (int) XContentMapValues.extractValue("ctx.payload.hits.total", vars);
int threshold = (int) XContentMapValues.extractValue("threshold", vars);
int threshold = (int) XContentMapValues.extractValue("params.threshold", vars);
return total > threshold;
});
ScriptEngine engine = new MockScriptEngine(MockScriptEngine.NAME, scripts, Collections.emptyMap());
scriptService = new ScriptService(Settings.EMPTY, Collections.singletonMap(engine.getType(), engine),
Collections.singletonMap(Watcher.SCRIPT_EXECUTABLE_CONTEXT.name, Watcher.SCRIPT_EXECUTABLE_CONTEXT));
scriptService = WatcherMockScriptPlugin.newMockScriptService(scripts);
ClusterState.Builder clusterState = new ClusterState.Builder(new ClusterName("_name"));
clusterState.metaData(MetaData.builder().putCustom(ScriptMetaData.TYPE, new ScriptMetaData.Builder(null).build()));
@ -94,8 +94,7 @@ public class ScriptConditionTests extends ESTestCase {
}
public void testExecute() throws Exception {
Script script = mockScript("ctx.payload.hits.total > 1");
ScriptCondition condition = new ScriptCondition(script, scriptService.compile(script, Watcher.SCRIPT_EXECUTABLE_CONTEXT));
ScriptCondition condition = new ScriptCondition(mockScript("ctx.payload.hits.total > 1"), scriptService);
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 0, 500L, ShardSearchFailure.EMPTY_ARRAY,
SearchResponse.Clusters.EMPTY);
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
@ -103,8 +102,9 @@ public class ScriptConditionTests extends ESTestCase {
}
public void testExecuteMergedParams() throws Exception {
Script script = new Script(ScriptType.INLINE, "mockscript", "ctx.payload.hits.total > threshold", singletonMap("threshold", 1));
ScriptCondition executable = new ScriptCondition(script, scriptService.compile(script, Watcher.SCRIPT_EXECUTABLE_CONTEXT));
Script script = new Script(ScriptType.INLINE, "mockscript",
"ctx.payload.hits.total > params.threshold", singletonMap("threshold", 1));
ScriptCondition executable = new ScriptCondition(script, scriptService);
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 0, 500L, ShardSearchFailure.EMPTY_ARRAY,
SearchResponse.Clusters.EMPTY);
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
@ -182,9 +182,8 @@ public class ScriptConditionTests extends ESTestCase {
}
public void testScriptConditionThrowException() throws Exception {
Script script = mockScript("null.foo");
ScriptCondition condition = new ScriptCondition(
script, scriptService.compile(script, Watcher.SCRIPT_EXECUTABLE_CONTEXT));
mockScript("null.foo"), scriptService);
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 0, 500L, ShardSearchFailure.EMPTY_ARRAY,
SearchResponse.Clusters.EMPTY);
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
@ -192,20 +191,9 @@ public class ScriptConditionTests extends ESTestCase {
assertThat(exception.getMessage(), containsString("Error evaluating null.foo"));
}
public void testScriptConditionReturnObjectThrowsException() throws Exception {
Script script = mockScript("return new Object()");
ScriptCondition condition = new ScriptCondition(script, scriptService.compile(script, Watcher.SCRIPT_EXECUTABLE_CONTEXT));
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 0, 500L, ShardSearchFailure.EMPTY_ARRAY,
SearchResponse.Clusters.EMPTY);
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
Exception exception = expectThrows(IllegalStateException.class, () -> condition.execute(ctx));
assertThat(exception.getMessage(),
containsString("condition [script] must return a boolean value (true|false) but instead returned [_name]"));
}
public void testScriptConditionAccessCtx() throws Exception {
Script script = mockScript("ctx.trigger.scheduled_time.getMillis() < new Date().time");
ScriptCondition condition = new ScriptCondition(script, scriptService.compile(script, Watcher.SCRIPT_EXECUTABLE_CONTEXT));
ScriptCondition condition = new ScriptCondition(mockScript("ctx.trigger.scheduled_time.getMillis() < new Date().time"),
scriptService);
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 0, 500L, ShardSearchFailure.EMPTY_ARRAY,
SearchResponse.Clusters.EMPTY);
WatchExecutionContext ctx = mockExecutionContext("_name", new DateTime(DateTimeZone.UTC), new Payload.XContent(response));
@ -213,6 +201,23 @@ public class ScriptConditionTests extends ESTestCase {
assertThat(condition.execute(ctx).met(), is(true));
}
public void testParamsCtxDeprecated() throws Exception {
WatchExecutionContext watcherContext = mock(WatchExecutionContext.class);
when(watcherContext.id()).thenReturn(mock(Wid.class));
when(watcherContext.watch()).thenReturn(mock(Watch.class));
when(watcherContext.triggerEvent()).thenReturn(mock(TriggerEvent.class));
WatcherConditionScript watcherScript = new WatcherConditionScript(Collections.emptyMap(), watcherContext) {
@Override
public boolean execute() {
assertThat(getParams().get("ctx"), is(getCtx()));
return true;
}
};
watcherScript.execute();
assertWarnings("Accessing variable [ctx] via [params.ctx] from within a watcher_condition script " +
"is deprecated in favor of directly accessing [ctx].");
}
private static XContentBuilder createConditionContent(String script, String scriptLang, ScriptType scriptType) throws IOException {
XContentBuilder builder = jsonBuilder();
if (scriptType == null) {

View File

@ -24,6 +24,7 @@ import org.elasticsearch.xpack.watcher.condition.InternalAlwaysCondition;
import org.elasticsearch.xpack.watcher.condition.NeverCondition;
import org.elasticsearch.xpack.watcher.condition.ScriptCondition;
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
import org.elasticsearch.xpack.watcher.test.WatcherMockScriptPlugin;
import java.util.ArrayList;
import java.util.Arrays;
@ -66,7 +67,7 @@ public class HistoryActionConditionTests extends AbstractWatcherIntegrationTestC
return types;
}
public static class CustomScriptPlugin extends MockScriptPlugin {
public static class CustomScriptPlugin extends WatcherMockScriptPlugin {
@Override
protected Map<String, Function<Map<String, Object>, Object>> pluginScripts() {

View File

@ -13,8 +13,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.script.MockScriptEngine;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptEngine;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.test.ESTestCase;
@ -25,7 +23,7 @@ import org.elasticsearch.xpack.core.watcher.transform.ExecutableTransform;
import org.elasticsearch.xpack.core.watcher.transform.TransformFactory;
import org.elasticsearch.xpack.core.watcher.transform.TransformRegistry;
import org.elasticsearch.xpack.core.watcher.watch.Payload;
import org.elasticsearch.xpack.watcher.Watcher;
import org.elasticsearch.xpack.watcher.test.WatcherMockScriptPlugin;
import org.elasticsearch.xpack.watcher.test.WatcherTestUtils;
import org.elasticsearch.xpack.watcher.transform.script.ExecutableScriptTransform;
import org.elasticsearch.xpack.watcher.transform.script.ScriptTransform;
@ -33,7 +31,6 @@ import org.elasticsearch.xpack.watcher.transform.script.ScriptTransformFactory;
import org.junit.Before;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
@ -46,14 +43,7 @@ public class TransformInputTests extends ESTestCase {
@Before
public void setupScriptService() {
Map<String, ScriptEngine> engines = new HashMap<>();
engines.put(MockScriptEngine.NAME,
new MockScriptEngine(MockScriptEngine.NAME, Collections.singletonMap("1", s -> "2"), Collections.emptyMap()));
Map<String, ScriptContext<?>> contexts = new HashMap<>();
contexts.put(Watcher.SCRIPT_TEMPLATE_CONTEXT.name, Watcher.SCRIPT_TEMPLATE_CONTEXT);
contexts.put(Watcher.SCRIPT_SEARCH_CONTEXT.name, Watcher.SCRIPT_SEARCH_CONTEXT);
contexts.put(Watcher.SCRIPT_EXECUTABLE_CONTEXT.name, Watcher.SCRIPT_EXECUTABLE_CONTEXT);
scriptService = new ScriptService(Settings.EMPTY, engines, contexts);
scriptService = WatcherMockScriptPlugin.newMockScriptService(Collections.singletonMap("1", s -> "2"));
}
public void testExecute() {

View File

@ -40,7 +40,7 @@ public class VariablesTests extends ESTestCase {
.metadata(metatdata)
.buildMock();
Map<String, Object> model = Variables.createCtxModel(ctx, payload);
Map<String, Object> model = Variables.createCtxParamsMap(ctx, payload);
assertThat(model, notNullValue());
assertThat(model.size(), is(1));

View File

@ -0,0 +1,74 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.watcher.test;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.script.MockScriptEngine;
import org.elasticsearch.script.MockScriptPlugin;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptEngine;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.xpack.watcher.Watcher;
import org.elasticsearch.xpack.watcher.condition.WatcherConditionScript;
import org.elasticsearch.xpack.watcher.transform.script.WatcherTransformScript;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Provides a mock script engine with mock versions of watcher scripts.
*/
public abstract class WatcherMockScriptPlugin extends MockScriptPlugin {
public static final Map<ScriptContext<?>, MockScriptEngine.ContextCompiler> CONTEXT_COMPILERS;
static {
Map<ScriptContext<?>, MockScriptEngine.ContextCompiler> compilers = new HashMap<>();
compilers.put(WatcherConditionScript.CONTEXT, (script, options) ->
(WatcherConditionScript.Factory) (params, watcherContext) ->
new WatcherConditionScript(params, watcherContext) {
@Override
public boolean execute() {
Map<String, Object> vars = new HashMap<>();
vars.put("params", getParams());
vars.put("ctx", getCtx());
return (boolean) script.apply(vars);
}
});
compilers.put(WatcherTransformScript.CONTEXT, (script, options) ->
(WatcherTransformScript.Factory) (params, watcherContext, payload) ->
new WatcherTransformScript(params, watcherContext, payload) {
@Override
public Object execute() {
Map<String, Object> vars = new HashMap<>();
vars.put("params", getParams());
vars.put("ctx", getCtx());
return script.apply(vars);
}
});
CONTEXT_COMPILERS = Collections.unmodifiableMap(compilers);
}
public static final List<ScriptContext<?>> CONTEXTS = Collections.unmodifiableList(Arrays.asList(
WatcherConditionScript.CONTEXT, WatcherTransformScript.CONTEXT, Watcher.SCRIPT_TEMPLATE_CONTEXT, Watcher.SCRIPT_SEARCH_CONTEXT
));
@Override
protected Map<ScriptContext<?>, MockScriptEngine.ContextCompiler> pluginContextCompilers() {
return CONTEXT_COMPILERS;
}
public static ScriptService newMockScriptService(Map<String, Function<Map<String, Object>, Object>> scripts) {
Map<String, ScriptEngine> engines = new HashMap<>();
engines.put(MockScriptEngine.NAME,
new MockScriptEngine(MockScriptEngine.NAME, scripts, CONTEXT_COMPILERS));
Map<String, ScriptContext<?>> contexts = CONTEXTS.stream().collect(Collectors.toMap(o -> o.name, Function.identity()));
return new ScriptService(Settings.EMPTY, engines, contexts);
}
}

View File

@ -9,13 +9,13 @@ import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.protocol.xpack.watcher.PutWatchResponse;
import org.elasticsearch.script.MockScriptPlugin;
import org.elasticsearch.xpack.core.watcher.client.WatcherClient;
import org.elasticsearch.xpack.core.watcher.support.xcontent.ObjectPath;
import org.elasticsearch.xpack.core.watcher.support.xcontent.XContentSource;
import org.elasticsearch.xpack.core.watcher.transport.actions.execute.ExecuteWatchResponse;
import org.elasticsearch.xpack.watcher.condition.ScriptCondition;
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
import org.elasticsearch.xpack.watcher.test.WatcherMockScriptPlugin;
import org.hamcrest.Matcher;
import java.util.HashMap;
@ -46,7 +46,7 @@ public class ExecutionVarsIntegrationTests extends AbstractWatcherIntegrationTes
return types;
}
public static class CustomScriptPlugin extends MockScriptPlugin {
public static class CustomScriptPlugin extends WatcherMockScriptPlugin {
@Override
@SuppressWarnings("unchecked")

View File

@ -42,6 +42,7 @@ import org.elasticsearch.xpack.watcher.input.search.SearchInputFactory;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import org.elasticsearch.xpack.watcher.test.WatcherTestUtils;
import org.elasticsearch.xpack.watcher.transform.script.WatcherTransformScript;
import org.junit.Before;
import org.mockito.ArgumentCaptor;
@ -76,7 +77,7 @@ public class SearchInputTests extends ESTestCase {
Map<String, ScriptContext<?>> contexts = new HashMap<>();
contexts.put(Watcher.SCRIPT_TEMPLATE_CONTEXT.name, Watcher.SCRIPT_TEMPLATE_CONTEXT);
contexts.put(Watcher.SCRIPT_SEARCH_CONTEXT.name, Watcher.SCRIPT_SEARCH_CONTEXT);
contexts.put(Watcher.SCRIPT_EXECUTABLE_CONTEXT.name, Watcher.SCRIPT_EXECUTABLE_CONTEXT);
contexts.put(WatcherTransformScript.CONTEXT.name, WatcherTransformScript.CONTEXT);
scriptService = new ScriptService(Settings.EMPTY, engines, contexts);
ThreadPool threadPool = mock(ThreadPool.class);

View File

@ -18,6 +18,7 @@ import org.elasticsearch.script.ScriptType;
import org.elasticsearch.xpack.watcher.condition.InternalAlwaysCondition;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
import org.elasticsearch.xpack.watcher.test.WatcherMockScriptPlugin;
import java.io.IOException;
import java.io.UncheckedIOException;
@ -78,7 +79,7 @@ public class TransformIntegrationTests extends AbstractWatcherIntegrationTestCas
return config;
}
public static class CustomScriptPlugin extends MockScriptPlugin {
public static class CustomScriptPlugin extends WatcherMockScriptPlugin {
@Override
protected Map<String, Function<Map<String, Object>, Object>> pluginScripts() {

View File

@ -8,7 +8,6 @@ package org.elasticsearch.xpack.watcher.transform.script;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptException;
@ -17,10 +16,12 @@ import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.core.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.core.watcher.execution.Wid;
import org.elasticsearch.xpack.core.watcher.transform.Transform;
import org.elasticsearch.xpack.core.watcher.trigger.TriggerEvent;
import org.elasticsearch.xpack.core.watcher.watch.Payload;
import org.elasticsearch.xpack.core.watcher.watch.Watch;
import org.elasticsearch.xpack.watcher.Watcher;
import org.elasticsearch.xpack.watcher.support.Variables;
import java.util.Collections;
import java.util.HashMap;
@ -49,21 +50,19 @@ public class ScriptTransformTests extends ESTestCase {
ScriptType type = randomFrom(ScriptType.values());
Map<String, Object> params = Collections.emptyMap();
Script script = new Script(type, type == ScriptType.STORED ? null : "_lang", "_script", params);
ExecutableScript.Factory factory = mock(ExecutableScript.Factory.class);
when(service.compile(script, Watcher.SCRIPT_EXECUTABLE_CONTEXT)).thenReturn(factory);
WatcherTransformScript.Factory factory = mock(WatcherTransformScript.Factory.class);
when(service.compile(script, WatcherTransformScript.CONTEXT)).thenReturn(factory);
ExecutableScriptTransform transform = new ExecutableScriptTransform(new ScriptTransform(script), logger, service);
WatchExecutionContext ctx = mockExecutionContext("_name", Payload.EMPTY);
Payload payload = new Payload.Simple("key", "value");
Map<String, Object> model = Variables.createCtxModel(ctx, payload);
Map<String, Object> transformed = singletonMap("key", "value");
ExecutableScript executable = mock(ExecutableScript.class);
when(executable.run()).thenReturn(transformed);
when(factory.newInstance(model)).thenReturn(executable);
WatcherTransformScript executable = mock(WatcherTransformScript.class);
when(executable.execute()).thenReturn(transformed);
when(factory.newInstance(params, ctx, payload)).thenReturn(executable);
Transform.Result result = transform.execute(ctx, payload);
assertThat(result, notNullValue());
@ -77,19 +76,17 @@ public class ScriptTransformTests extends ESTestCase {
ScriptType type = randomFrom(ScriptType.values());
Map<String, Object> params = Collections.emptyMap();
Script script = new Script(type, type == ScriptType.STORED ? null : "_lang", "_script", params);
ExecutableScript.Factory factory = mock(ExecutableScript.Factory.class);
when(service.compile(script, Watcher.SCRIPT_EXECUTABLE_CONTEXT)).thenReturn(factory);
WatcherTransformScript.Factory factory = mock(WatcherTransformScript.Factory.class);
when(service.compile(script, WatcherTransformScript.CONTEXT)).thenReturn(factory);
ExecutableScriptTransform transform = new ExecutableScriptTransform(new ScriptTransform(script), logger, service);
WatchExecutionContext ctx = mockExecutionContext("_name", Payload.EMPTY);
Payload payload = new Payload.Simple("key", "value");
Map<String, Object> model = Variables.createCtxModel(ctx, payload);
ExecutableScript executable = mock(ExecutableScript.class);
when(executable.run()).thenThrow(new RuntimeException("_error"));
when(factory.newInstance(model)).thenReturn(executable);
WatcherTransformScript executable = mock(WatcherTransformScript.class);
when(executable.execute()).thenThrow(new RuntimeException("_error"));
when(factory.newInstance(params, ctx, payload)).thenReturn(executable);
Transform.Result result = transform.execute(ctx, payload);
assertThat(result, notNullValue());
@ -103,20 +100,18 @@ public class ScriptTransformTests extends ESTestCase {
ScriptType type = randomFrom(ScriptType.values());
Map<String, Object> params = Collections.emptyMap();
Script script = new Script(type, type == ScriptType.STORED ? null : "_lang", "_script", params);
ExecutableScript.Factory factory = mock(ExecutableScript.Factory.class);
when(service.compile(script, Watcher.SCRIPT_EXECUTABLE_CONTEXT)).thenReturn(factory);
WatcherTransformScript.Factory factory = mock(WatcherTransformScript.Factory.class);
when(service.compile(script, WatcherTransformScript.CONTEXT)).thenReturn(factory);
ExecutableScriptTransform transform = new ExecutableScriptTransform(new ScriptTransform(script), logger, service);
WatchExecutionContext ctx = mockExecutionContext("_name", Payload.EMPTY);
Payload payload = new Payload.Simple("key", "value");
Map<String, Object> model = Variables.createCtxModel(ctx, payload);
ExecutableScript executable = mock(ExecutableScript.class);
WatcherTransformScript executable = mock(WatcherTransformScript.class);
Object value = randomFrom("value", 1, new String[] { "value" }, Collections.singletonList("value"), singleton("value"));
when(executable.run()).thenReturn(value);
when(factory.newInstance(model)).thenReturn(executable);
when(executable.execute()).thenReturn(value);
when(factory.newInstance(params, ctx, payload)).thenReturn(executable);
Transform.Result result = transform.execute(ctx, payload);
assertThat(result, notNullValue());
@ -158,7 +153,7 @@ public class ScriptTransformTests extends ESTestCase {
String errorMessage = "expected error message";
ScriptException scriptException = new ScriptException(errorMessage, new RuntimeException("foo"),
Collections.emptyList(), "whatever", "whatever");
when(scriptService.compile(anyObject(), eq(Watcher.SCRIPT_EXECUTABLE_CONTEXT))).thenThrow(scriptException);
when(scriptService.compile(anyObject(), eq(WatcherTransformScript.CONTEXT))).thenThrow(scriptException);
ScriptTransformFactory transformFactory = new ScriptTransformFactory(Settings.builder().build(), scriptService);
@ -191,6 +186,23 @@ public class ScriptTransformTests extends ESTestCase {
assertThat(e.getMessage(), containsString("script_lang not supported [not_a_valid_lang]"));
}
public void testParamsCtxDeprecated() throws Exception {
WatchExecutionContext watcherContext = mock(WatchExecutionContext.class);
when(watcherContext.id()).thenReturn(mock(Wid.class));
when(watcherContext.watch()).thenReturn(mock(Watch.class));
when(watcherContext.triggerEvent()).thenReturn(mock(TriggerEvent.class));
Payload payload = mock(Payload.class);
WatcherTransformScript watcherScript = new WatcherTransformScript(Collections.emptyMap(), watcherContext, payload) {
@Override
public Object execute() {
return getParams().get("ctx");
}
};
assertThat(watcherScript.execute(), is(watcherScript.getCtx()));
assertWarnings("Accessing variable [ctx] via [params.ctx] from within a watcher_transform script " +
"is deprecated in favor of directly accessing [ctx].");
}
static String scriptTypeField(ScriptType type) {
switch (type) {
case INLINE: return "source";
@ -205,7 +217,7 @@ public class ScriptTransformTests extends ESTestCase {
.put("path.home", createTempDir())
.build();
Map<String, ScriptContext> contexts = new HashMap<>(ScriptModule.CORE_CONTEXTS);
contexts.put(Watcher.SCRIPT_EXECUTABLE_CONTEXT.name, Watcher.SCRIPT_EXECUTABLE_CONTEXT);
contexts.put(WatcherTransformScript.CONTEXT.name, WatcherTransformScript.CONTEXT);
contexts.put(Watcher.SCRIPT_TEMPLATE_CONTEXT.name, Watcher.SCRIPT_TEMPLATE_CONTEXT);
return new ScriptService(settings, Collections.emptyMap(), Collections.emptyMap());
}