diff --git a/src/main/java/org/elasticsearch/watcher/WatcherService.java b/src/main/java/org/elasticsearch/watcher/WatcherService.java index 5a898a31fbb..8f4d0c32110 100644 --- a/src/main/java/org/elasticsearch/watcher/WatcherService.java +++ b/src/main/java/org/elasticsearch/watcher/WatcherService.java @@ -102,6 +102,7 @@ public class WatcherService extends AbstractComponent { } return result.indexResponse(); } catch (Exception e) { + logger.warn("failed to put watch [{}]", e, id); throw new WatcherException("failed to put watch [{}]", e, id); } finally { lock.release(); diff --git a/src/main/java/org/elasticsearch/watcher/condition/script/ExecutableScriptCondition.java b/src/main/java/org/elasticsearch/watcher/condition/script/ExecutableScriptCondition.java index a0ec9483c45..366dbccb55e 100644 --- a/src/main/java/org/elasticsearch/watcher/condition/script/ExecutableScriptCondition.java +++ b/src/main/java/org/elasticsearch/watcher/condition/script/ExecutableScriptCondition.java @@ -6,6 +6,7 @@ package org.elasticsearch.watcher.condition.script; import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.script.CompiledScript; import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.groovy.GroovyScriptExecutionException; import org.elasticsearch.watcher.condition.ExecutableCondition; @@ -14,6 +15,7 @@ import org.elasticsearch.watcher.support.Variables; import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy; import java.io.IOException; +import java.util.Map; /** * This class executes a script against the ctx payload and returns a boolean @@ -21,16 +23,26 @@ import java.io.IOException; public class ExecutableScriptCondition extends ExecutableCondition { private final ScriptServiceProxy scriptService; + private final CompiledScript compiledScript; public ExecutableScriptCondition(ScriptCondition condition, ESLogger logger, ScriptServiceProxy scriptService) { super(condition, logger); this.scriptService = scriptService; + try { + compiledScript = scriptService.compile(condition.script); + } catch (Exception e) { + throw new ScriptConditionValidationException("failed to compile script [{}] with lang [{}] of type [{}]", e, condition.script.script(), condition.script.lang(), condition.script.type(), e); + } } @Override public ScriptCondition.Result execute(WatchExecutionContext ctx) throws IOException { try { - ExecutableScript executable = scriptService.executable(condition.script, Variables.createCtxModel(ctx, ctx.payload())); + Map parameters = Variables.createCtxModel(ctx, ctx.payload()); + if (condition.script.params() != null && !condition.script.params().isEmpty()) { + parameters.putAll(condition.script.params()); + } + ExecutableScript executable = scriptService.executable(compiledScript, parameters); Object value = executable.run(); if (value instanceof Boolean) { return (Boolean) value ? ScriptCondition.Result.MET : ScriptCondition.Result.UNMET; diff --git a/src/main/java/org/elasticsearch/watcher/condition/script/ScriptConditionValidationException.java b/src/main/java/org/elasticsearch/watcher/condition/script/ScriptConditionValidationException.java new file mode 100644 index 00000000000..4c01dce60c2 --- /dev/null +++ b/src/main/java/org/elasticsearch/watcher/condition/script/ScriptConditionValidationException.java @@ -0,0 +1,19 @@ +/* + * 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.watcher.condition.script; + +/** + */ +public class ScriptConditionValidationException extends ScriptConditionException { + + public ScriptConditionValidationException(String msg, Object... args) { + super(msg, args); + } + + public ScriptConditionValidationException(String msg, Throwable cause, Object... args) { + super(msg, cause, args); + } +} diff --git a/src/main/java/org/elasticsearch/watcher/support/init/proxy/ScriptServiceProxy.java b/src/main/java/org/elasticsearch/watcher/support/init/proxy/ScriptServiceProxy.java index 18d38207f1a..09300c9d977 100644 --- a/src/main/java/org/elasticsearch/watcher/support/init/proxy/ScriptServiceProxy.java +++ b/src/main/java/org/elasticsearch/watcher/support/init/proxy/ScriptServiceProxy.java @@ -7,12 +7,12 @@ package org.elasticsearch.watcher.support.init.proxy; import org.elasticsearch.common.collect.ImmutableMap; import org.elasticsearch.common.inject.Injector; +import org.elasticsearch.script.CompiledScript; import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.SearchScript; import org.elasticsearch.search.lookup.SearchLookup; import org.elasticsearch.watcher.support.Script; -import org.elasticsearch.watcher.support.Variables; import org.elasticsearch.watcher.support.init.InitializingService; import java.util.Map; @@ -39,6 +39,18 @@ public class ScriptServiceProxy implements InitializingService.Initializable { this.service = injector.getInstance(ScriptService.class); } + public CompiledScript compile(String lang, String script, ScriptService.ScriptType scriptType) { + return service.compile(lang, script, scriptType); + } + + public CompiledScript compile(Script script) { + return compile(script.lang(), script.script(), script.type()); + } + + public ExecutableScript executable(CompiledScript compiledScript, Map vars) { + return service.executable(compiledScript, vars); + } + public ExecutableScript executable(Script script, Map vars) { if (script.params() != null && !script.params().isEmpty()) { vars = ImmutableMap.builder() diff --git a/src/main/java/org/elasticsearch/watcher/transform/script/ExecutableScriptTransform.java b/src/main/java/org/elasticsearch/watcher/transform/script/ExecutableScriptTransform.java index 83ffaacb3a6..2ee71202a58 100644 --- a/src/main/java/org/elasticsearch/watcher/transform/script/ExecutableScriptTransform.java +++ b/src/main/java/org/elasticsearch/watcher/transform/script/ExecutableScriptTransform.java @@ -6,6 +6,7 @@ package org.elasticsearch.watcher.transform.script; import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.script.CompiledScript; import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.watcher.execution.WatchExecutionContext; import org.elasticsearch.watcher.support.Script; @@ -25,10 +26,17 @@ import static org.elasticsearch.watcher.support.Variables.createCtxModel; public class ExecutableScriptTransform extends ExecutableTransform { private final ScriptServiceProxy scriptService; + private final CompiledScript compiledScript; public ExecutableScriptTransform(ScriptTransform transform, ESLogger logger, ScriptServiceProxy scriptService) { super(transform, logger); this.scriptService = scriptService; + Script script = transform.getScript(); + try { + compiledScript = scriptService.compile(script); + } catch (Exception e) { + throw new ScriptTransformValidationException("failed to compile script [{}] with lang [{}] of type [{}]", e, script.script(), script.lang(), script.type(), e); + } } @Override @@ -37,7 +45,7 @@ public class ExecutableScriptTransform extends ExecutableTransform model = new HashMap<>(); model.putAll(script.params()); model.putAll(createCtxModel(ctx, payload)); - ExecutableScript executable = scriptService.executable(script.lang(), script.script(), script.type(), model); + ExecutableScript executable = scriptService.executable(compiledScript, model); Object value = executable.run(); if (value instanceof Map) { return new ScriptTransform.Result(new Payload.Simple((Map) value)); diff --git a/src/main/java/org/elasticsearch/watcher/transform/script/ScriptTransformValidationException.java b/src/main/java/org/elasticsearch/watcher/transform/script/ScriptTransformValidationException.java new file mode 100644 index 00000000000..e2c1c0de04b --- /dev/null +++ b/src/main/java/org/elasticsearch/watcher/transform/script/ScriptTransformValidationException.java @@ -0,0 +1,19 @@ +/* + * 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.watcher.transform.script; + +/** + */ +public class ScriptTransformValidationException extends ScriptTransformException { + + public ScriptTransformValidationException(String msg, Object... args) { + super(msg, args); + } + + public ScriptTransformValidationException(String msg, Throwable cause, Object... args) { + super(msg, cause, args); + } +} diff --git a/src/test/java/org/elasticsearch/watcher/actions/webhook/WebhookActionTests.java b/src/test/java/org/elasticsearch/watcher/actions/webhook/WebhookActionTests.java index 74bf8bce91b..e7c8ae4a57e 100644 --- a/src/test/java/org/elasticsearch/watcher/actions/webhook/WebhookActionTests.java +++ b/src/test/java/org/elasticsearch/watcher/actions/webhook/WebhookActionTests.java @@ -16,13 +16,8 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.json.JsonXContent; -import org.elasticsearch.env.Environment; -import org.elasticsearch.node.settings.NodeSettingsService; -import org.elasticsearch.script.ScriptEngineService; -import org.elasticsearch.script.ScriptService; import org.elasticsearch.test.ElasticsearchTestCase; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.watcher.actions.ActionException; import org.elasticsearch.watcher.actions.email.service.*; import org.elasticsearch.watcher.execution.TriggeredExecutionContext; @@ -37,7 +32,6 @@ import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy; import org.elasticsearch.watcher.support.secret.SecretService; import org.elasticsearch.watcher.support.template.Template; import org.elasticsearch.watcher.support.template.TemplateEngine; -import org.elasticsearch.watcher.support.template.xmustache.XMustacheScriptEngineService; import org.elasticsearch.watcher.support.template.xmustache.XMustacheTemplateEngine; import org.elasticsearch.watcher.test.WatcherTestUtils; import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent; @@ -51,9 +45,7 @@ import org.junit.Test; import javax.mail.internet.AddressException; import java.io.IOException; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; import static org.elasticsearch.common.joda.time.DateTimeZone.UTC; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; @@ -89,10 +81,7 @@ public class WebhookActionTests extends ElasticsearchTestCase { public void init() throws IOException { tp = new ThreadPool(ThreadPool.Names.SAME); Settings settings = ImmutableSettings.EMPTY; - XMustacheScriptEngineService mustacheScriptEngineService = new XMustacheScriptEngineService(settings); - Set engineServiceSet = new HashSet<>(); - engineServiceSet.add(mustacheScriptEngineService); - scriptService = ScriptServiceProxy.of(new ScriptService(settings, new Environment(), engineServiceSet, new ResourceWatcherService(settings, tp), new NodeSettingsService(settings))); + scriptService = WatcherTestUtils.getScriptServiceProxy(tp); templateEngine = new XMustacheTemplateEngine(settings, scriptService); secretService = mock(SecretService.class); testBody = new Template(TEST_BODY_STRING ); diff --git a/src/test/java/org/elasticsearch/watcher/condition/script/ScriptConditionTests.java b/src/test/java/org/elasticsearch/watcher/condition/script/ScriptConditionTests.java index f14297cda02..851a9f505b2 100644 --- a/src/test/java/org/elasticsearch/watcher/condition/script/ScriptConditionTests.java +++ b/src/test/java/org/elasticsearch/watcher/condition/script/ScriptConditionTests.java @@ -11,20 +11,14 @@ import org.elasticsearch.action.search.ShardSearchFailure; import org.elasticsearch.common.collect.ImmutableMap; import org.elasticsearch.common.joda.time.DateTime; import org.elasticsearch.common.settings.ImmutableSettings; -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.json.JsonXContent; -import org.elasticsearch.env.Environment; -import org.elasticsearch.node.settings.NodeSettingsService; -import org.elasticsearch.script.ScriptEngineService; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.script.groovy.GroovyScriptEngineService; import org.elasticsearch.search.internal.InternalSearchResponse; import org.elasticsearch.test.ElasticsearchTestCase; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.watcher.execution.WatchExecutionContext; import org.elasticsearch.watcher.support.Script; import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy; @@ -34,11 +28,10 @@ import org.junit.Before; import org.junit.Test; import java.io.IOException; -import java.util.HashSet; -import java.util.Set; import static org.elasticsearch.common.joda.time.DateTimeZone.UTC; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.elasticsearch.watcher.test.WatcherTestUtils.getScriptServiceProxy; import static org.elasticsearch.watcher.test.WatcherTestUtils.mockExecutionContext; import static org.hamcrest.Matchers.is; @@ -165,6 +158,42 @@ public class ScriptConditionTests extends ElasticsearchTestCase { fail("expected a condition exception trying to parse an invalid condition XContent"); } + @Test(expected = ScriptConditionValidationException.class) + @Repeat(iterations = 3) + public void testScriptConditionParser_badScript() throws Exception { + ScriptConditionFactory conditionParser = new ScriptConditionFactory(ImmutableSettings.settingsBuilder().build(), getScriptServiceProxy(tp)); + ScriptService.ScriptType scriptType = randomFrom(ScriptService.ScriptType.values()); + String script; + switch (scriptType) { + case INDEXED: + case FILE: + script = "nonExisting_script"; + break; + case INLINE: + default: + script = "foo = = 1"; + } + XContentBuilder builder = createConditionContent(script, "groovy", scriptType); + XContentParser parser = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes()); + parser.nextToken(); + ScriptCondition scriptCondition = conditionParser.parseCondition("_watch", parser); + conditionParser.createExecutable(scriptCondition); + fail("expected a condition validation exception trying to create an executable with a bad or missing script"); + } + + @Test(expected = ScriptConditionValidationException.class) + public void testScriptConditionParser_badLang() throws Exception { + ScriptConditionFactory conditionParser = new ScriptConditionFactory(ImmutableSettings.settingsBuilder().build(), getScriptServiceProxy(tp)); + ScriptService.ScriptType scriptType = ScriptService.ScriptType.INLINE; + String script = "return true"; + XContentBuilder builder = createConditionContent(script, "not_a_valid_lang", scriptType); + XContentParser parser = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes()); + parser.nextToken(); + ScriptCondition scriptCondition = conditionParser.parseCondition("_watch", parser); + conditionParser.createExecutable(scriptCondition); + fail("expected a condition validation exception trying to create an executable with an invalid language"); + } + @Test(expected = ScriptConditionException.class) public void testScriptCondition_throwException() throws Exception { ScriptServiceProxy scriptService = getScriptServiceProxy(tp); @@ -172,7 +201,7 @@ public class ScriptConditionTests extends ElasticsearchTestCase { SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 500l, new ShardSearchFailure[0]); WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response)); condition.execute(ctx); - fail(); + fail("expected a ScriptConditionException trying to execute a script that throws an exception"); } @Test(expected = ScriptConditionException.class) @@ -183,6 +212,7 @@ public class ScriptConditionTests extends ElasticsearchTestCase { WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response)); condition.execute(ctx); fail(); + fail("expected a ScriptConditionException trying to execute a script that returns an object"); } @Test @@ -195,18 +225,6 @@ public class ScriptConditionTests extends ElasticsearchTestCase { assertThat(condition.execute(ctx).met(), is(true)); } - - private static ScriptServiceProxy getScriptServiceProxy(ThreadPool tp) throws IOException { - Settings settings = ImmutableSettings.settingsBuilder() - .put(ScriptService.DISABLE_DYNAMIC_SCRIPTING_SETTING, "none") - .build(); - GroovyScriptEngineService groovyScriptEngineService = new GroovyScriptEngineService(settings); - Set engineServiceSet = new HashSet<>(); - engineServiceSet.add(groovyScriptEngineService); - NodeSettingsService nodeSettingsService = new NodeSettingsService(settings); - return ScriptServiceProxy.of(new ScriptService(settings, new Environment(), engineServiceSet, new ResourceWatcherService(settings, tp), nodeSettingsService)); - } - private static XContentBuilder createConditionContent(String script, String scriptLang, ScriptService.ScriptType scriptType) throws IOException { XContentBuilder builder = jsonBuilder().startObject(); builder.field("script", script); @@ -219,5 +237,4 @@ public class ScriptConditionTests extends ElasticsearchTestCase { return builder.endObject(); } - } diff --git a/src/test/java/org/elasticsearch/watcher/test/WatcherTestUtils.java b/src/test/java/org/elasticsearch/watcher/test/WatcherTestUtils.java index 3d39c0cd772..74256c0a12e 100644 --- a/src/test/java/org/elasticsearch/watcher/test/WatcherTestUtils.java +++ b/src/test/java/org/elasticsearch/watcher/test/WatcherTestUtils.java @@ -12,10 +12,18 @@ import org.elasticsearch.common.collect.ImmutableMap; import org.elasticsearch.common.joda.time.DateTime; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.settings.ImmutableSettings; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.env.Environment; +import org.elasticsearch.node.settings.NodeSettingsService; +import org.elasticsearch.script.ScriptEngineService; +import org.elasticsearch.script.ScriptService; +import org.elasticsearch.script.groovy.GroovyScriptEngineService; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.test.ElasticsearchIntegrationTest; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.watcher.actions.ActionWrapper; import org.elasticsearch.watcher.actions.ExecutableActions; import org.elasticsearch.watcher.actions.email.EmailAction; @@ -42,9 +50,10 @@ import org.elasticsearch.watcher.support.http.HttpRequestTemplate; import org.elasticsearch.watcher.support.init.proxy.ClientProxy; import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy; import org.elasticsearch.watcher.support.secret.Secret; -import org.elasticsearch.watcher.support.template.xmustache.XMustacheTemplateEngine; import org.elasticsearch.watcher.support.template.Template; import org.elasticsearch.watcher.support.template.TemplateEngine; +import org.elasticsearch.watcher.support.template.xmustache.XMustacheScriptEngineService; +import org.elasticsearch.watcher.support.template.xmustache.XMustacheTemplateEngine; import org.elasticsearch.watcher.transform.search.ExecutableSearchTransform; import org.elasticsearch.watcher.transform.search.SearchTransform; import org.elasticsearch.watcher.trigger.TriggerEvent; @@ -54,10 +63,8 @@ import org.elasticsearch.watcher.watch.Payload; import org.elasticsearch.watcher.watch.Watch; import javax.mail.internet.AddressException; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.io.IOException; +import java.util.*; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource; @@ -190,4 +197,18 @@ public final class WatcherTestUtils { new Watch.Status()); } + + public static ScriptServiceProxy getScriptServiceProxy(ThreadPool tp) throws IOException { + Settings settings = ImmutableSettings.settingsBuilder() + .put(ScriptService.DISABLE_DYNAMIC_SCRIPTING_SETTING, "none") + .build(); + GroovyScriptEngineService groovyScriptEngineService = new GroovyScriptEngineService(settings); + XMustacheScriptEngineService mustacheScriptEngineService = new XMustacheScriptEngineService(settings); + Set engineServiceSet = new HashSet<>(); + engineServiceSet.add(mustacheScriptEngineService); + engineServiceSet.add(groovyScriptEngineService); + NodeSettingsService nodeSettingsService = new NodeSettingsService(settings); + return ScriptServiceProxy.of(new ScriptService(settings, new Environment(), engineServiceSet, new ResourceWatcherService(settings, tp), nodeSettingsService)); + } + } diff --git a/src/test/java/org/elasticsearch/watcher/transform/ScriptTransformTests.java b/src/test/java/org/elasticsearch/watcher/transform/ScriptTransformTests.java index 7bd10b73808..23b7c71fa74 100644 --- a/src/test/java/org/elasticsearch/watcher/transform/ScriptTransformTests.java +++ b/src/test/java/org/elasticsearch/watcher/transform/ScriptTransformTests.java @@ -7,29 +7,36 @@ package org.elasticsearch.watcher.transform; import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.collect.ImmutableList; import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.collect.ImmutableSet; -import org.elasticsearch.common.settings.ImmutableSettings; -import org.elasticsearch.watcher.execution.WatchExecutionContext; -import org.elasticsearch.watcher.transform.script.ExecutableScriptTransform; -import org.elasticsearch.watcher.transform.script.ScriptTransform; -import org.elasticsearch.watcher.transform.script.ScriptTransformFactory; -import org.elasticsearch.watcher.watch.Payload; -import org.elasticsearch.watcher.support.Script; -import org.elasticsearch.watcher.support.Variables; -import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy; +import com.carrotsearch.randomizedtesting.annotations.Repeat; import org.elasticsearch.common.collect.ImmutableMap; +import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.json.JsonXContent; +import org.elasticsearch.script.CompiledScript; import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.ScriptService; import org.elasticsearch.test.ElasticsearchTestCase; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.watcher.execution.WatchExecutionContext; +import org.elasticsearch.watcher.support.Script; +import org.elasticsearch.watcher.support.Variables; +import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy; +import org.elasticsearch.watcher.transform.script.ExecutableScriptTransform; +import org.elasticsearch.watcher.transform.script.ScriptTransform; +import org.elasticsearch.watcher.transform.script.ScriptTransformFactory; +import org.elasticsearch.watcher.transform.script.ScriptTransformValidationException; +import org.elasticsearch.watcher.watch.Payload; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import java.util.Collections; import java.util.Map; -import static org.elasticsearch.watcher.test.WatcherTestUtils.*; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.elasticsearch.watcher.test.WatcherTestUtils.*; import static org.hamcrest.Matchers.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -39,12 +46,27 @@ import static org.mockito.Mockito.when; */ public class ScriptTransformTests extends ElasticsearchTestCase { + ThreadPool tp = null; + + @Before + public void init() { + tp = new ThreadPool(ThreadPool.Names.SAME); + } + + @After + public void cleanup() { + tp.shutdownNow(); + } + + @Test public void testApply_MapValue() throws Exception { ScriptServiceProxy service = mock(ScriptServiceProxy.class); ScriptService.ScriptType type = randomFrom(ScriptService.ScriptType.values()); Map params = Collections.emptyMap(); Script script = new Script("_script", type, "_lang", params); + CompiledScript compiledScript = mock(CompiledScript.class); + when(service.compile(script)).thenReturn(compiledScript); ExecutableScriptTransform transform = new ExecutableScriptTransform(new ScriptTransform(script), logger, service); WatchExecutionContext ctx = mockExecutionContext("_name", EMPTY_PAYLOAD); @@ -59,7 +81,7 @@ public class ScriptTransformTests extends ElasticsearchTestCase { ExecutableScript executable = mock(ExecutableScript.class); when(executable.run()).thenReturn(transformed); - when(service.executable("_lang", "_script", type, model)).thenReturn(executable); + when(service.executable(compiledScript, model)).thenReturn(executable); Transform.Result result = transform.execute(ctx, payload); assertThat(result, notNullValue()); @@ -70,9 +92,12 @@ public class ScriptTransformTests extends ElasticsearchTestCase { @Test public void testApply_NonMapValue() throws Exception { ScriptServiceProxy service = mock(ScriptServiceProxy.class); + ScriptService.ScriptType type = randomFrom(ScriptService.ScriptType.values()); Map params = Collections.emptyMap(); Script script = new Script("_script", type, "_lang", params); + CompiledScript compiledScript = mock(CompiledScript.class); + when(service.compile(script)).thenReturn(compiledScript); ExecutableScriptTransform transform = new ExecutableScriptTransform(new ScriptTransform(script), logger, service); WatchExecutionContext ctx = mockExecutionContext("_name", EMPTY_PAYLOAD); @@ -82,9 +107,9 @@ public class ScriptTransformTests extends ElasticsearchTestCase { Map model = Variables.createCtxModel(ctx, payload); ExecutableScript executable = mock(ExecutableScript.class); - Object value = randomFrom("value", 1, new String[] { "value" }, ImmutableList.of("value"), ImmutableSet.of("value")); + Object value = randomFrom("value", 1, new String[]{"value"}, ImmutableList.of("value"), ImmutableSet.of("value")); when(executable.run()).thenReturn(value); - when(service.executable("_lang", "_script", type, model)).thenReturn(executable); + when(service.executable(compiledScript, model)).thenReturn(executable); Transform.Result result = transform.execute(ctx, payload); assertThat(result, notNullValue()); @@ -120,4 +145,56 @@ public class ScriptTransformTests extends ElasticsearchTestCase { ExecutableScriptTransform transform = new ScriptTransformFactory(ImmutableSettings.EMPTY, service).parseExecutable("_id", parser); assertThat(transform.transform().getScript(), equalTo(new Script("_script", ScriptService.ScriptType.INLINE, ScriptService.DEFAULT_LANG, ImmutableMap.of()))); } + + + @Test(expected = ScriptTransformValidationException.class) + @Repeat(iterations = 3) + public void testScriptConditionParser_badScript() throws Exception { + ScriptTransformFactory transformFactory = new ScriptTransformFactory(ImmutableSettings.settingsBuilder().build(), getScriptServiceProxy(tp)); + ScriptService.ScriptType scriptType = randomFrom(ScriptService.ScriptType.values()); + String script; + switch (scriptType) { + case INDEXED: + case FILE: + script = "nonExisting_script"; + break; + case INLINE: + default: + script = "foo = = 1"; + } + + XContentBuilder builder = jsonBuilder().startObject() + .field("script", script) + .field("lang", "groovy") + .field("type", scriptType.name()) + .startObject("params").field("key", "value").endObject() + .endObject(); + + XContentParser parser = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes()); + parser.nextToken(); + ScriptTransform scriptTransform = transformFactory.parseTransform("_watch", parser); + transformFactory.createExecutable(scriptTransform); + fail("expected a transform validation exception trying to create an executable with a bad or missing script"); + } + + @Test(expected = ScriptTransformValidationException.class) + public void testScriptConditionParser_badLang() throws Exception { + ScriptTransformFactory transformFactory = new ScriptTransformFactory(ImmutableSettings.settingsBuilder().build(), getScriptServiceProxy(tp)); + ScriptService.ScriptType scriptType = ScriptService.ScriptType.INLINE; + String script = "return true"; + XContentBuilder builder = jsonBuilder().startObject() + .field("script", script) + .field("lang", "not_a_valid_lang") + .field("type", scriptType.name()) + .startObject("params").field("key", "value").endObject() + .endObject(); + + + XContentParser parser = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes()); + parser.nextToken(); + ScriptTransform scriptCondition = transformFactory.parseTransform("_watch", parser); + transformFactory.createExecutable(scriptCondition); + fail("expected a transform validation exception trying to create an executable with an invalid language"); + } + }