watcher: Removed custom xmustache script and use mustache script engine in lang-mustache module
The lang-mustache module has been extended to meet Watcher's needs: * The ability to refer the specific slots in arrays. * An `content_type` option controls whether json string escaping is used. Otherwise there is no escaping. Closes elastic/elasticsearch#1116 Other changes: * I changed tests that were just using mustache just because it was around to not use mustache * I moved tests to `test-xpack-with-mustache` module that were testing mustache with Watcher * added smoke test for watcher and mustache * moved some tests around * instead of using DefaultTextTemplateEngine in watcher tests use MockTextTemplateEngine * added a mock mustache script engine * Cleanup some messy tests to not rely on mustache and move them back into xpack module * moved array access test to smoke test watcher with mustache module * test: simplified the condition search test to take the time component out of it, while still simulation a condition * removed the mustache dependency in the messy-test-watcher-with-groovy module Original commit: elastic/x-pack-elasticsearch@6a2a4e885f
This commit is contained in:
parent
29fbaf233f
commit
e617d8365f
|
@ -9,6 +9,4 @@ apply plugin: 'elasticsearch.messy-test'
|
|||
dependencies {
|
||||
testCompile project(path: ':x-plugins:elasticsearch:x-pack', configuration: 'testArtifacts')
|
||||
testCompile project(path: ':modules:lang-groovy', configuration: 'runtime')
|
||||
// some tests depend on both groovy and mustache! this is really bad!
|
||||
testCompile project(path: ':modules:lang-mustache', configuration: 'runtime')
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import static org.elasticsearch.watcher.input.InputBuilders.simpleInput;
|
|||
import static org.elasticsearch.watcher.transform.TransformBuilders.scriptTransform;
|
||||
import static org.elasticsearch.watcher.trigger.TriggerBuilders.schedule;
|
||||
import static org.elasticsearch.watcher.trigger.schedule.Schedules.cron;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
@ -57,12 +58,12 @@ public class ExecutionVarsIntegrationTests extends AbstractWatcherIntegrationTes
|
|||
.transform(scriptTransform("ctx.vars.watch_transform_value = ctx.vars.condition_value + 5; return ctx.payload;"))
|
||||
.addAction(
|
||||
"a1",
|
||||
scriptTransform("ctx.vars.a1_transform_value = ctx.vars.watch_transform_value + 10; return ctx.payload;"),
|
||||
loggingAction("condition_value={{ctx.vars.condition_value}}, watch_transform_value={{ctx.vars.watch_transform_value}}, a1_transform_value={{ctx.vars.a1_transform_value}}"))
|
||||
scriptTransform("ctx.vars.a1_transform_value = ctx.vars.watch_transform_value + 10; ctx.payload.a1_transformed_value = ctx.vars.a1_transform_value; return ctx.payload;"),
|
||||
loggingAction("_text"))
|
||||
.addAction(
|
||||
"a2",
|
||||
scriptTransform("ctx.vars.a2_transform_value = ctx.vars.watch_transform_value + 20; return ctx.payload;"),
|
||||
loggingAction("condition_value={{ctx.vars.condition_value}}, watch_transform_value={{ctx.vars.watch_transform_value}}, a2_transform_value={{ctx.vars.a2_transform_value}}")))
|
||||
scriptTransform("ctx.vars.a2_transform_value = ctx.vars.watch_transform_value + 20; ctx.payload.a2_transformed_value = ctx.vars.a2_transform_value; return ctx.payload;"),
|
||||
loggingAction("_text")))
|
||||
.get();
|
||||
|
||||
assertThat(putWatchResponse.isCreated(), is(true));
|
||||
|
@ -99,12 +100,12 @@ public class ExecutionVarsIntegrationTests extends AbstractWatcherIntegrationTes
|
|||
case "a1":
|
||||
assertValue(action, "status", is("success"));
|
||||
assertValue(action, "transform.status", is("success"));
|
||||
assertValue(action, "logging.logged_text", is("condition_value=10, watch_transform_value=15, a1_transform_value=25"));
|
||||
assertValue(action, "transform.payload.a1_transformed_value", equalTo(25));
|
||||
break;
|
||||
case "a2":
|
||||
assertValue(action, "status", is("success"));
|
||||
assertValue(action, "transform.status", is("success"));
|
||||
assertValue(action, "logging.logged_text", is("condition_value=10, watch_transform_value=15, a2_transform_value=35"));
|
||||
assertValue(action, "transform.payload.a2_transformed_value", equalTo(35));
|
||||
break;
|
||||
default:
|
||||
fail("there should not be an action result for action with an id other than a1 or a2");
|
||||
|
@ -122,12 +123,12 @@ public class ExecutionVarsIntegrationTests extends AbstractWatcherIntegrationTes
|
|||
.transform(scriptTransform("ctx.vars.watch_transform_value = ctx.vars.condition_value + 5; return ctx.payload;"))
|
||||
.addAction(
|
||||
"a1",
|
||||
scriptTransform("ctx.vars.a1_transform_value = ctx.vars.watch_transform_value + 10; return ctx.payload;"),
|
||||
loggingAction("condition_value={{ctx.vars.condition_value}}, watch_transform_value={{ctx.vars.watch_transform_value}}, a1_transform_value={{ctx.vars.a1_transform_value}}"))
|
||||
scriptTransform("ctx.vars.a1_transform_value = ctx.vars.watch_transform_value + 10; ctx.payload.a1_transformed_value = ctx.vars.a1_transform_value; return ctx.payload;"),
|
||||
loggingAction("_text"))
|
||||
.addAction(
|
||||
"a2",
|
||||
scriptTransform("ctx.vars.a2_transform_value = ctx.vars.watch_transform_value + 20; return ctx.payload;"),
|
||||
loggingAction("condition_value={{ctx.vars.condition_value}}, watch_transform_value={{ctx.vars.watch_transform_value}}, a2_transform_value={{ctx.vars.a2_transform_value}}")))
|
||||
scriptTransform("ctx.vars.a2_transform_value = ctx.vars.watch_transform_value + 20; ctx.payload.a2_transformed_value = ctx.vars.a2_transform_value; return ctx.payload;"),
|
||||
loggingAction("_text")))
|
||||
.get();
|
||||
|
||||
assertThat(putWatchResponse.isCreated(), is(true));
|
||||
|
@ -161,12 +162,12 @@ public class ExecutionVarsIntegrationTests extends AbstractWatcherIntegrationTes
|
|||
case "a1":
|
||||
assertValue(action, "status", is("success"));
|
||||
assertValue(action, "transform.status", is("success"));
|
||||
assertValue(action, "logging.logged_text", is("condition_value=10, watch_transform_value=15, a1_transform_value=25"));
|
||||
assertValue(action, "transform.payload.a1_transformed_value", equalTo(25));
|
||||
break;
|
||||
case "a2":
|
||||
assertValue(action, "status", is("success"));
|
||||
assertValue(action, "transform.status", is("success"));
|
||||
assertValue(action, "logging.logged_text", is("condition_value=10, watch_transform_value=15, a2_transform_value=35"));
|
||||
assertValue(action, "transform.payload.a2_transformed_value", equalTo(35));
|
||||
break;
|
||||
default:
|
||||
fail("there should not be an action result for action with an id other than a1 or a2");
|
||||
|
|
|
@ -10,7 +10,6 @@ import org.elasticsearch.action.search.SearchResponse;
|
|||
import org.elasticsearch.action.search.SearchType;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.script.groovy.GroovyPlugin;
|
||||
import org.elasticsearch.script.mustache.MustachePlugin;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
|
||||
import org.elasticsearch.search.sort.SortOrder;
|
||||
|
@ -49,7 +48,6 @@ public class IndexActionIntegrationTests extends AbstractWatcherIntegrationTestC
|
|||
protected List<Class<? extends Plugin>> pluginTypes() {
|
||||
List<Class<? extends Plugin>> types = super.pluginTypes();
|
||||
types.add(GroovyPlugin.class);
|
||||
types.add(MustachePlugin.class);
|
||||
return types;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ import org.elasticsearch.script.groovy.GroovyScriptEngineService;
|
|||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||
import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
|
||||
import org.elasticsearch.watcher.support.text.xmustache.XMustacheScriptEngineService;
|
||||
import org.junit.Ignore;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
@ -34,15 +33,12 @@ public final class MessyTestUtils {
|
|||
.put("script.indexed", "true")
|
||||
.put("path.home", LuceneTestCase.createTempDir())
|
||||
.build();
|
||||
XMustacheScriptEngineService mustacheScriptEngineService = new XMustacheScriptEngineService(settings);
|
||||
GroovyScriptEngineService groovyScriptEngineService = new GroovyScriptEngineService(settings);
|
||||
Set<ScriptEngineService> engineServiceSet = new HashSet<>();
|
||||
engineServiceSet.add(mustacheScriptEngineService);
|
||||
engineServiceSet.add(groovyScriptEngineService);
|
||||
ScriptEngineRegistry scriptEngineRegistry = new ScriptEngineRegistry(
|
||||
Arrays.asList(
|
||||
new ScriptEngineRegistry.ScriptEngineRegistration(GroovyScriptEngineService.class, GroovyScriptEngineService.TYPES),
|
||||
new ScriptEngineRegistry.ScriptEngineRegistration(XMustacheScriptEngineService.class, XMustacheScriptEngineService.TYPES)
|
||||
new ScriptEngineRegistry.ScriptEngineRegistration(GroovyScriptEngineService.class, GroovyScriptEngineService.TYPES)
|
||||
)
|
||||
);
|
||||
ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Arrays.asList(ScriptServiceProxy.INSTANCE));
|
||||
|
|
|
@ -12,7 +12,6 @@ import org.elasticsearch.common.io.Streams;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.script.groovy.GroovyPlugin;
|
||||
import org.elasticsearch.script.mustache.MustachePlugin;
|
||||
import org.elasticsearch.watcher.support.Script;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTestCase;
|
||||
import org.elasticsearch.watcher.test.WatcherTestUtils;
|
||||
|
@ -52,7 +51,6 @@ public class TransformIntegrationTests extends AbstractWatcherIntegrationTestCas
|
|||
protected List<Class<? extends Plugin>> pluginTypes() {
|
||||
List<Class<? extends Plugin>> types = super.pluginTypes();
|
||||
types.add(GroovyPlugin.class);
|
||||
types.add(MustachePlugin.class);
|
||||
return types;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
/*
|
||||
* 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.messy.tests;
|
||||
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.script.mustache.MustachePlugin;
|
||||
import org.elasticsearch.test.ESIntegTestCase;
|
||||
import org.elasticsearch.test.junit.annotations.TestLogging;
|
||||
import org.elasticsearch.watcher.actions.email.service.EmailTemplate;
|
||||
import org.elasticsearch.watcher.actions.email.service.support.EmailServer;
|
||||
import org.elasticsearch.watcher.client.WatcherClient;
|
||||
import org.elasticsearch.watcher.condition.compare.CompareCondition;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTestCase;
|
||||
import org.elasticsearch.watcher.trigger.schedule.IntervalSchedule;
|
||||
import org.junit.After;
|
||||
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
|
||||
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
|
||||
import static org.elasticsearch.test.ESIntegTestCase.Scope.SUITE;
|
||||
import static org.elasticsearch.watcher.actions.ActionBuilders.emailAction;
|
||||
import static org.elasticsearch.watcher.client.WatchSourceBuilders.watchBuilder;
|
||||
import static org.elasticsearch.watcher.condition.ConditionBuilders.compareCondition;
|
||||
import static org.elasticsearch.watcher.input.InputBuilders.searchInput;
|
||||
import static org.elasticsearch.watcher.test.WatcherTestUtils.newInputSearchRequest;
|
||||
import static org.elasticsearch.watcher.trigger.TriggerBuilders.schedule;
|
||||
import static org.elasticsearch.watcher.trigger.schedule.Schedules.interval;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
@TestLogging("subethamail:TRACE,watcher:TRACE")
|
||||
@ESIntegTestCase.ClusterScope(scope = SUITE, numClientNodes = 0, transportClientRatio = 0, randomDynamicTemplates = false, numDataNodes = 1)
|
||||
public class EmailActionIntegrationTests extends AbstractWatcherIntegrationTestCase {
|
||||
static final String USERNAME = "_user";
|
||||
static final String PASSWORD = "_passwd";
|
||||
|
||||
private EmailServer server;
|
||||
|
||||
@After
|
||||
public void cleanup() throws Exception {
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Class<? extends Plugin>> pluginTypes() {
|
||||
List<Class<? extends Plugin>> types = new ArrayList<>();
|
||||
types.addAll(super.pluginTypes());
|
||||
types.add(MustachePlugin.class);
|
||||
return types;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Settings nodeSettings(int nodeOrdinal) {
|
||||
if(server == null) {
|
||||
//Need to construct the Email Server here as this happens before init()
|
||||
server = EmailServer.localhost("2500-2600", USERNAME, PASSWORD, logger);
|
||||
}
|
||||
return Settings.builder()
|
||||
.put(super.nodeSettings(nodeOrdinal))
|
||||
.put("watcher.actions.email.service.account.test.smtp.auth", true)
|
||||
.put("watcher.actions.email.service.account.test.smtp.user", USERNAME)
|
||||
.put("watcher.actions.email.service.account.test.smtp.password", PASSWORD)
|
||||
.put("watcher.actions.email.service.account.test.smtp.port", server.port())
|
||||
.put("watcher.actions.email.service.account.test.smtp.host", "localhost")
|
||||
.build();
|
||||
}
|
||||
|
||||
public void testArrayAccess() throws Exception {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
server.addListener(new EmailServer.Listener() {
|
||||
@Override
|
||||
public void on(MimeMessage message) throws Exception {
|
||||
assertThat(message.getSubject(), equalTo("value"));
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
WatcherClient watcherClient = watcherClient();
|
||||
createIndex("idx");
|
||||
// Have a sample document in the index, the watch is going to evaluate
|
||||
client().prepareIndex("idx", "type").setSource("field", "value").get();
|
||||
refresh();
|
||||
SearchRequest searchRequest = newInputSearchRequest("idx").source(searchSource().query(termQuery("field", "value")));
|
||||
watcherClient.preparePutWatch("_id")
|
||||
.setSource(watchBuilder()
|
||||
.trigger(schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS)))
|
||||
.input(searchInput(searchRequest))
|
||||
.condition(compareCondition("ctx.payload.hits.total", CompareCondition.Op.GT, 0L))
|
||||
.addAction("_email", emailAction(EmailTemplate.builder().from("_from").to("_to")
|
||||
.subject("{{ctx.payload.hits.hits.0._source.field}}")).setAuthentication(USERNAME, PASSWORD.toCharArray())))
|
||||
.get();
|
||||
|
||||
if (timeWarped()) {
|
||||
timeWarp().scheduler().trigger("_id");
|
||||
refresh();
|
||||
}
|
||||
|
||||
assertWatchWithMinimumPerformedActionsCount("_id", 1);
|
||||
|
||||
if (!latch.await(5, TimeUnit.SECONDS)) {
|
||||
fail("waited too long for email to be received");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,12 +21,8 @@
|
|||
*/
|
||||
|
||||
// renames that took place:
|
||||
// renamed: x-pack/watcher/src/test/java/org/elasticsearch/watcher/test/integration/BasicWatcherTests.java -> qa/messy-test-xpack-with-mustache/src/test/java/org/elasticsearch/messy/tests/BasicWatcherTests.java
|
||||
// renamed: x-pack/watcher/src/test/java/org/elasticsearch/watcher/actions/email/EmailActionIntegrationTests.java -> qa/messy-test-xpack-with-mustache/src/test/java/org/elasticsearch/messy/tests/EmailActionIntegrationTests.java
|
||||
// renamed: x-pack/watcher/src/test/java/org/elasticsearch/watcher/history/HistoryTemplateSearchInputMappingsTests.java -> qa/messy-test-xpack-with-mustache/src/test/java/org/elasticsearch/messy/tests/HistoryTemplateSearchInputMappingsTests.java
|
||||
// renamed: x-pack/watcher/src/test/java/org/elasticsearch/watcher/input/search/SearchInputTests.java -> qa/messy-test-xpack-with-mustache/src/test/java/org/elasticsearch/messy/tests/SearchInputTests.java
|
||||
// renamed: x-pack/watcher/src/test/java/org/elasticsearch/watcher/transform/search/SearchTransformTests.java -> qa/messy-test-xpack-with-mustache/src/test/java/org/elasticsearch/messy/tests/SearchTransformTests.java
|
||||
// renamed: x-pack/shield/src/test/java/org/elasticsearch/integration/ShieldCachePermissionTests.java -> qa/messy-test-xpack-with-mustache/src/test/java/org/elasticsearch/messy/tests/ShieldCachePermissionTests.java
|
||||
// renamed: x-pack/watcher/src/test/java/org/elasticsearch/watcher/actions/TimeThrottleIntegrationTests.java -> qa/messy-test-xpack-with-mustache/src/test/java/org/elasticsearch/messy/tests/TimeThrottleIntegrationTests.java
|
||||
|
||||
package org.elasticsearch.messy.tests;
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
apply plugin: 'elasticsearch.rest-test'
|
||||
|
||||
dependencies {
|
||||
testCompile project(path: ':x-plugins:elasticsearch:x-pack', configuration: 'runtime')
|
||||
testCompile project(path: ':modules:lang-mustache', configuration: 'runtime')
|
||||
}
|
||||
|
||||
integTest {
|
||||
cluster {
|
||||
plugin 'x-pack', project(':x-plugins:elasticsearch:x-pack')
|
||||
systemProperty 'es.shield.enabled', 'false'
|
||||
systemProperty 'es.marvel.enabled', 'false'
|
||||
systemProperty 'es.http.port', '9400'
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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.smoketest;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Name;
|
||||
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
|
||||
import org.elasticsearch.test.rest.ESRestTestCase;
|
||||
import org.elasticsearch.test.rest.RestTestCandidate;
|
||||
import org.elasticsearch.test.rest.parser.RestTestParseException;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
|
||||
public abstract class WatcherRestTestCase extends ESRestTestCase {
|
||||
|
||||
public WatcherRestTestCase(@Name("yaml") RestTestCandidate testCandidate) {
|
||||
super(testCandidate);
|
||||
}
|
||||
|
||||
@ParametersFactory
|
||||
public static Iterable<Object[]> parameters() throws IOException, RestTestParseException {
|
||||
return ESRestTestCase.createParameters(0, 1);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void startWatcher() throws Exception {
|
||||
try(CloseableHttpClient client = HttpClients.createMinimal(new BasicHttpClientConnectionManager())) {
|
||||
URL url = getClusterUrls()[0];
|
||||
HttpPut request = new HttpPut(new URI("http", null, url.getHost(), url.getPort(), "/_watcher/_start", null, null));
|
||||
client.execute(request);
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopWatcher() throws Exception {
|
||||
try(CloseableHttpClient client = HttpClients.createMinimal(new BasicHttpClientConnectionManager())) {
|
||||
URL url = getClusterUrls()[0];
|
||||
HttpPut request = new HttpPut(new URI("http", null, url.getHost(), url.getPort(), "/_watcher/_stop", null, null));
|
||||
client.execute(request);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* 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.smoketest;
|
||||
|
||||
import com.fasterxml.jackson.core.io.JsonStringEncoder;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.script.ScriptContextRegistry;
|
||||
import org.elasticsearch.script.ScriptEngineRegistry;
|
||||
import org.elasticsearch.script.ScriptEngineService;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.script.ScriptSettings;
|
||||
import org.elasticsearch.script.mustache.MustacheScriptEngineService;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||
import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
|
||||
import org.elasticsearch.watcher.support.text.DefaultTextTemplateEngine;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplate;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplateEngine;
|
||||
import org.junit.Before;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
public class WatcherTemplateIT extends ESTestCase {
|
||||
|
||||
private TextTemplateEngine engine;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
Settings setting = Settings.builder().put(ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING, true).build();
|
||||
Environment environment = Mockito.mock(Environment.class);
|
||||
Set<ScriptEngineService> engines = Collections.singleton(new MustacheScriptEngineService(setting));
|
||||
ResourceWatcherService resourceWatcherService = Mockito.mock(ResourceWatcherService.class);
|
||||
ScriptContextRegistry registry = new ScriptContextRegistry(Collections.singletonList(ScriptServiceProxy.INSTANCE));
|
||||
|
||||
ScriptEngineRegistry scriptEngineRegistry = new ScriptEngineRegistry(
|
||||
Arrays.asList(
|
||||
new ScriptEngineRegistry.ScriptEngineRegistration(MustacheScriptEngineService.class, MustacheScriptEngineService.TYPES)
|
||||
)
|
||||
);
|
||||
ScriptSettings scriptSettings = new ScriptSettings(scriptEngineRegistry, registry);
|
||||
ScriptService scriptService = new ScriptService(setting, environment, engines, resourceWatcherService, scriptEngineRegistry, registry, scriptSettings);
|
||||
engine = new DefaultTextTemplateEngine(Settings.EMPTY, ScriptServiceProxy.of(scriptService));
|
||||
}
|
||||
|
||||
public void testEscaping() throws Exception {
|
||||
XContentType contentType = randomFrom(XContentType.values());
|
||||
if (rarely()) {
|
||||
contentType = null;
|
||||
}
|
||||
Character[] specialChars = new Character[]{'\f', '\n', '\r', '"', '\\', (char) 11, '\t', '\b' };
|
||||
int iters = scaledRandomIntBetween(100, 1000);
|
||||
for (int i = 0; i < iters; i++) {
|
||||
int rounds = scaledRandomIntBetween(1, 20);
|
||||
StringWriter escaped = new StringWriter(); //This will be escaped as it is constructed
|
||||
StringWriter unescaped = new StringWriter(); //This will be escaped at the end
|
||||
|
||||
for (int j = 0; j < rounds; j++) {
|
||||
String s = getChars();
|
||||
unescaped.write(s);
|
||||
if (contentType == XContentType.JSON) {
|
||||
escaped.write(JsonStringEncoder.getInstance().quoteAsString(s));
|
||||
} else {
|
||||
escaped.write(s);
|
||||
}
|
||||
|
||||
char c = randomFrom(specialChars);
|
||||
unescaped.append(c);
|
||||
|
||||
if (contentType == XContentType.JSON) {
|
||||
escaped.write(JsonStringEncoder.getInstance().quoteAsString("" + c));
|
||||
} else {
|
||||
escaped.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (contentType == XContentType.JSON) {
|
||||
assertThat(escaped.toString(), equalTo(new String(JsonStringEncoder.getInstance().quoteAsString(unescaped.toString()))));
|
||||
}
|
||||
else {
|
||||
assertThat(escaped.toString(), equalTo(unescaped.toString()));
|
||||
}
|
||||
|
||||
String template = prepareTemplate("{{data}}", contentType);
|
||||
|
||||
Map<String, Object> dataMap = new HashMap<>();
|
||||
dataMap.put("data", unescaped.toString());
|
||||
String renderedTemplate = engine.render(new TextTemplate(template), dataMap);
|
||||
assertThat(renderedTemplate, notNullValue());
|
||||
|
||||
if (contentType == XContentType.JSON) {
|
||||
if (!escaped.toString().equals(renderedTemplate)) {
|
||||
String escapedString = escaped.toString();
|
||||
for (int l = 0; l < renderedTemplate.length() && l < escapedString.length(); ++l) {
|
||||
if (renderedTemplate.charAt(l) != escapedString.charAt(l)) {
|
||||
logger.error("at [{}] expected [{}] but got [{}]", l, renderedTemplate.charAt(l), escapedString.charAt(l));
|
||||
}
|
||||
}
|
||||
}
|
||||
assertThat(escaped.toString(), equalTo(renderedTemplate));
|
||||
} else {
|
||||
assertThat(unescaped.toString(), equalTo(renderedTemplate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testSimpleParameterReplace() {
|
||||
{
|
||||
String template = "__json__::GET _search {\"query\": " + "{\"boosting\": {" + "\"positive\": {\"match\": {\"body\": \"gift\"}},"
|
||||
+ "\"negative\": {\"term\": {\"body\": {\"value\": \"solr\"}" + "}}, \"negative_boost\": {{boost_val}} } }}";
|
||||
Map<String, Object> vars = new HashMap<>();
|
||||
vars.put("boost_val", "0.3");
|
||||
String result = engine.render(new TextTemplate(template), vars);
|
||||
assertEquals("GET _search {\"query\": {\"boosting\": {\"positive\": {\"match\": {\"body\": \"gift\"}},"
|
||||
+ "\"negative\": {\"term\": {\"body\": {\"value\": \"solr\"}}}, \"negative_boost\": 0.3 } }}",
|
||||
result);
|
||||
}
|
||||
{
|
||||
String template = "__json__::GET _search {\"query\": " + "{\"boosting\": {" + "\"positive\": {\"match\": {\"body\": \"gift\"}},"
|
||||
+ "\"negative\": {\"term\": {\"body\": {\"value\": \"{{body_val}}\"}" + "}}, \"negative_boost\": {{boost_val}} } }}";
|
||||
Map<String, Object> vars = new HashMap<>();
|
||||
vars.put("boost_val", "0.3");
|
||||
vars.put("body_val", "\"quick brown\"");
|
||||
String result = engine.render(new TextTemplate(template), vars);
|
||||
assertEquals("GET _search {\"query\": {\"boosting\": {\"positive\": {\"match\": {\"body\": \"gift\"}},"
|
||||
+ "\"negative\": {\"term\": {\"body\": {\"value\": \"\\\"quick brown\\\"\"}}}, \"negative_boost\": 0.3 } }}",
|
||||
result);
|
||||
}
|
||||
}
|
||||
|
||||
public void testInvalidPrefixes() throws Exception {
|
||||
String[] specialStrings = new String[]{"\f", "\n", "\r", "\"", "\\", "\t", "\b", "__::", "__" };
|
||||
String prefix = randomFrom("", "__", "____::", "___::", "____", "::", "++json__::", "__json__", "+_json__::", "__json__:");
|
||||
String template = prefix + " {{test_var1}} {{test_var2}}";
|
||||
Map<String, Object> vars = new HashMap<>();
|
||||
Writer var1Writer = new StringWriter();
|
||||
Writer var2Writer = new StringWriter();
|
||||
|
||||
for(int i = 0; i < scaledRandomIntBetween(10,1000); ++i) {
|
||||
var1Writer.write(randomRealisticUnicodeOfCodepointLengthBetween(0, 10));
|
||||
var2Writer.write(randomRealisticUnicodeOfCodepointLengthBetween(0, 10));
|
||||
var1Writer.append(randomFrom(specialStrings));
|
||||
var2Writer.append(randomFrom(specialStrings));
|
||||
}
|
||||
|
||||
vars.put("test_var1", var1Writer.toString());
|
||||
vars.put("test_var2", var2Writer.toString());
|
||||
String s1 = engine.render(new TextTemplate(template), vars);
|
||||
String s2 = prefix + " " + var1Writer.toString() + " " + var2Writer.toString();
|
||||
assertThat(s1, equalTo(s2));
|
||||
}
|
||||
|
||||
static String getChars() throws IOException {
|
||||
return randomRealisticUnicodeOfCodepointLengthBetween(0, 10);
|
||||
}
|
||||
|
||||
static String prepareTemplate(String template, @Nullable XContentType contentType) {
|
||||
if (contentType == null) {
|
||||
return template;
|
||||
}
|
||||
return new StringBuilder("__")
|
||||
.append(contentType.shortName().toLowerCase(Locale.ROOT))
|
||||
.append("__::")
|
||||
.append(template)
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.smoketest;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Name;
|
||||
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
|
||||
import org.elasticsearch.test.rest.ESRestTestCase;
|
||||
import org.elasticsearch.test.rest.RestTestCandidate;
|
||||
import org.elasticsearch.test.rest.parser.RestTestParseException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/** Runs rest tests against external cluster */
|
||||
public class WatcherWithMustacheIT extends WatcherRestTestCase {
|
||||
|
||||
public WatcherWithMustacheIT(@Name("yaml") RestTestCandidate testCandidate) {
|
||||
super(testCandidate);
|
||||
}
|
||||
|
||||
@ParametersFactory
|
||||
public static Iterable<Object[]> parameters() throws IOException, RestTestParseException {
|
||||
return ESRestTestCase.createParameters(0, 1);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
"Test webhook action with mustache integration":
|
||||
- do:
|
||||
cluster.health:
|
||||
wait_for_status: yellow
|
||||
|
||||
|
||||
- do: {watcher.stats:{}}
|
||||
- match: { "watcher_state": "started" }
|
||||
- match: { "watch_count": 0 }
|
||||
|
||||
- do:
|
||||
watcher.put_watch:
|
||||
id: "test_watch"
|
||||
body: >
|
||||
{
|
||||
"trigger": {
|
||||
"schedule": {
|
||||
"interval": "1s"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"simple" : {
|
||||
"key" : "value"
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"always" : {}
|
||||
},
|
||||
"actions": {
|
||||
"output": {
|
||||
"webhook" : {
|
||||
"method" : "PUT",
|
||||
"host" : "localhost",
|
||||
"port" : 9400,
|
||||
"path": "/my_index/my_type/{{ctx.watch_id}}",
|
||||
"body" : {
|
||||
"inline": {
|
||||
"watch_id" : "{{ctx.watch_id}}"
|
||||
},
|
||||
"params": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
- match: { _id: "test_watch" }
|
||||
- match: { created: true }
|
||||
|
||||
- do: {watcher.stats:{}}
|
||||
- match: { "watch_count": 1 }
|
||||
|
||||
# Simulate a Thread.sleep()
|
||||
- do:
|
||||
catch: request_timeout
|
||||
cluster.health:
|
||||
wait_for_nodes: 99
|
||||
timeout: 10s
|
||||
- match: { "timed_out": true }
|
||||
|
||||
- do:
|
||||
get:
|
||||
index: my_index
|
||||
type: my_type
|
||||
id: test_watch
|
||||
|
||||
- match: { _source: { watch_id: "test_watch" } }
|
|
@ -0,0 +1,62 @@
|
|||
---
|
||||
"Test array access":
|
||||
- do:
|
||||
cluster.health:
|
||||
wait_for_status: yellow
|
||||
|
||||
|
||||
- do: {watcher.stats:{}}
|
||||
- match: { "watcher_state": "started" }
|
||||
- match: { "watch_count": 0 }
|
||||
|
||||
- do:
|
||||
watcher.put_watch:
|
||||
id: "test_watch"
|
||||
body: >
|
||||
{
|
||||
"trigger": {
|
||||
"schedule": {
|
||||
"interval": "1s"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"simple" : {
|
||||
"objects" : [
|
||||
{
|
||||
"field": "value1"
|
||||
},
|
||||
{
|
||||
"field": "value2"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"always" : {}
|
||||
},
|
||||
"actions": {
|
||||
"output": {
|
||||
"logging" : {
|
||||
"text" : "{{ctx.payload.objects.0.field}} {{ctx.payload.objects.1.field}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
- match: { _id: "test_watch" }
|
||||
- match: { created: true }
|
||||
|
||||
- do: {watcher.stats:{}}
|
||||
- match: { "watch_count": 1 }
|
||||
|
||||
# Simulate a Thread.sleep()
|
||||
- do:
|
||||
catch: request_timeout
|
||||
cluster.health:
|
||||
wait_for_nodes: 99
|
||||
timeout: 10s
|
||||
- match: { "timed_out": true }
|
||||
|
||||
- do:
|
||||
search:
|
||||
index: .watch_history-*
|
||||
- match: { hits.hits.0._source.result.actions.0.logging.logged_text: "value1 value2" }
|
|
@ -59,7 +59,6 @@ import org.elasticsearch.watcher.support.init.InitializingService;
|
|||
import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
|
||||
import org.elasticsearch.watcher.support.secret.SecretModule;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplateModule;
|
||||
import org.elasticsearch.watcher.support.text.xmustache.XMustacheScriptEngineService;
|
||||
import org.elasticsearch.watcher.support.validation.WatcherSettingsValidation;
|
||||
import org.elasticsearch.watcher.transform.TransformModule;
|
||||
import org.elasticsearch.watcher.transport.actions.ack.AckWatchAction;
|
||||
|
@ -182,9 +181,6 @@ public class WatcherPlugin extends Plugin {
|
|||
|
||||
public void onModule(ScriptModule module) {
|
||||
module.registerScriptContext(ScriptServiceProxy.INSTANCE);
|
||||
if (enabled && !transportClient) {
|
||||
module.addScriptEngine(new ScriptEngineRegistry.ScriptEngineRegistration(XMustacheScriptEngineService.class, XMustacheScriptEngineService.TYPES));
|
||||
}
|
||||
}
|
||||
|
||||
public void onModule(SettingsModule module) {
|
||||
|
|
|
@ -45,12 +45,12 @@ public class ScriptServiceProxy implements InitializingService.Initializable {
|
|||
|
||||
public CompiledScript compile(Script script) {
|
||||
return securityContext.executeAs(XPackUser.INSTANCE, () ->
|
||||
compile(new org.elasticsearch.script.Script(script.script(), script.type(), script.lang(), script.params())));
|
||||
compile(new org.elasticsearch.script.Script(script.script(), script.type(), script.lang(), script.params()), Collections.emptyMap()));
|
||||
}
|
||||
|
||||
public CompiledScript compile(org.elasticsearch.script.Script script) {
|
||||
public CompiledScript compile(org.elasticsearch.script.Script script, Map<String, String> compileParams) {
|
||||
return securityContext.executeAs(XPackUser.INSTANCE, () ->
|
||||
service.compile(script, WatcherScriptContext.CTX, Collections.emptyMap()));
|
||||
service.compile(script, WatcherScriptContext.CTX, compileParams));
|
||||
}
|
||||
|
||||
public ExecutableScript executable(CompiledScript compiledScript, Map<String, Object> vars) {
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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.support.text;
|
||||
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.script.CompiledScript;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.Template;
|
||||
import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class DefaultTextTemplateEngine extends AbstractComponent implements TextTemplateEngine {
|
||||
|
||||
private final ScriptServiceProxy service;
|
||||
|
||||
@Inject
|
||||
public DefaultTextTemplateEngine(Settings settings, ScriptServiceProxy service) {
|
||||
super(settings);
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String render(TextTemplate template, Map<String, Object> model) {
|
||||
XContentType contentType = detectContentType(template);
|
||||
Map<String, String> compileParams = compileParams(contentType);
|
||||
template = trimContentType(template);
|
||||
|
||||
CompiledScript compiledScript = service.compile(convert(template, model), compileParams);
|
||||
ExecutableScript executable = service.executable(compiledScript, model);
|
||||
Object result = executable.run();
|
||||
if (result instanceof BytesReference) {
|
||||
return ((BytesReference) result).toUtf8();
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private TextTemplate trimContentType(TextTemplate textTemplate) {
|
||||
String template = textTemplate.getTemplate();
|
||||
if (!template.startsWith("__")){
|
||||
return textTemplate; //Doesn't even start with __ so can't have a content type
|
||||
}
|
||||
int index = template.indexOf("__::", 3); //There must be a __<content_type__:: prefix so the minimum length before detecting '__::' is 3
|
||||
if (index >= 0 && index < 12) { //Assume that the content type name is less than 10 characters long otherwise we may falsely detect strings that start with '__ and have '__::' somewhere in the content
|
||||
if (template.length() == 6) {
|
||||
template = "";
|
||||
} else {
|
||||
template = template.substring(index + 4);
|
||||
}
|
||||
}
|
||||
return new TextTemplate(template, textTemplate.getContentType(), textTemplate.getType(), textTemplate.getParams());
|
||||
}
|
||||
|
||||
private XContentType detectContentType(TextTemplate textTemplate) {
|
||||
String template = textTemplate.getTemplate();
|
||||
if (template.startsWith("__")) {
|
||||
int endOfContentName = template.indexOf("__::", 3); //There must be a __<content_type__:: prefix so the minimum length before detecting '__::' is 3
|
||||
if (endOfContentName != -1) {
|
||||
return XContentType.fromRestContentType(template.substring(2, endOfContentName));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Template convert(TextTemplate textTemplate, Map<String, Object> model) {
|
||||
Map<String, Object> mergedModel = new HashMap<>();
|
||||
mergedModel.putAll(textTemplate.getParams());
|
||||
mergedModel.putAll(model);
|
||||
return new Template(textTemplate.getTemplate(), textTemplate.getType(), "mustache", textTemplate.getContentType(), mergedModel);
|
||||
}
|
||||
|
||||
private Map<String, String> compileParams(XContentType contentType) {
|
||||
if (contentType == XContentType.JSON) {
|
||||
return Collections.singletonMap("content_type", "application/json");
|
||||
} else {
|
||||
return Collections.singletonMap("content_type", "text/plain");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,11 +31,11 @@ public class TextTemplate implements ToXContent {
|
|||
private final @Nullable ScriptType type;
|
||||
private final @Nullable Map<String, Object> params;
|
||||
|
||||
TextTemplate(String template) {
|
||||
public TextTemplate(String template) {
|
||||
this(template, null, null, null);
|
||||
}
|
||||
|
||||
TextTemplate(String template, @Nullable XContentType contentType, @Nullable ScriptType type, @Nullable Map<String, Object> params) {
|
||||
public TextTemplate(String template, @Nullable XContentType contentType, @Nullable ScriptType type, @Nullable Map<String, Object> params) {
|
||||
this.template = template;
|
||||
this.contentType = contentType;
|
||||
this.type = type;
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
package org.elasticsearch.watcher.support.text;
|
||||
|
||||
import org.elasticsearch.common.inject.AbstractModule;
|
||||
import org.elasticsearch.watcher.support.text.xmustache.XMustacheTextTemplateEngine;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -15,7 +14,7 @@ public class TextTemplateModule extends AbstractModule {
|
|||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(XMustacheTextTemplateEngine.class).asEagerSingleton();
|
||||
bind(TextTemplateEngine.class).to(XMustacheTextTemplateEngine.class);
|
||||
bind(DefaultTextTemplateEngine.class).asEagerSingleton();
|
||||
bind(TextTemplateEngine.class).to(DefaultTextTemplateEngine.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,164 +0,0 @@
|
|||
/*
|
||||
* 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.support.text.xmustache;
|
||||
|
||||
import com.fasterxml.jackson.core.io.JsonStringEncoder;
|
||||
import com.github.mustachejava.DefaultMustacheFactory;
|
||||
import com.github.mustachejava.MustacheException;
|
||||
import com.github.mustachejava.reflect.ReflectionObjectHandler;
|
||||
import org.elasticsearch.common.util.iterable.Iterables;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.watcher.support.ArrayObjectIterator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An extension to elasticsearch's {@code JsonEscapingMustacheFactory} that on top of applying json
|
||||
* escapes it also enables support for navigating arrays using `array.X` notation (where `X` is the index
|
||||
* of the element in the array).
|
||||
*/
|
||||
public class XMustacheFactory extends DefaultMustacheFactory {
|
||||
|
||||
final XContentType contentType;
|
||||
|
||||
public XMustacheFactory(XContentType contentType) {
|
||||
this.contentType = contentType;
|
||||
setObjectHandler(new ReflectionObjectHandler() {
|
||||
@Override
|
||||
public Object coerce(Object object) {
|
||||
if (object != null) {
|
||||
if (object.getClass().isArray()) {
|
||||
return new ArrayMap(object);
|
||||
} else if (object instanceof Collection) {
|
||||
return new CollectionMap((Collection) object);
|
||||
}
|
||||
}
|
||||
return super.coerce(object);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(String value, Writer writer) {
|
||||
try {
|
||||
if (contentType == XContentType.JSON) {
|
||||
writer.write(JsonStringEncoder.getInstance().quoteAsString(value));
|
||||
} else {
|
||||
writer.write(value);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new MustacheException("Failed to encode value: " + value);
|
||||
}
|
||||
}
|
||||
|
||||
static class ArrayMap extends AbstractMap<Object, Object> implements Iterable<Object> {
|
||||
|
||||
private final Object array;
|
||||
|
||||
public ArrayMap(Object array) {
|
||||
this.array = array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(Object key) {
|
||||
if (key instanceof Number) {
|
||||
return Array.get(array, ((Number) key).intValue());
|
||||
}
|
||||
try {
|
||||
int index = Integer.parseInt(key.toString());
|
||||
return Array.get(array, index);
|
||||
} catch (NumberFormatException nfe) {
|
||||
// if it's not a number it is as if the key doesn't exist
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return get(key) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<Object, Object>> entrySet() {
|
||||
int length = Array.getLength(array);
|
||||
Map<Object, Object> map = new HashMap<>(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
map.put(i, Array.get(array, i));
|
||||
}
|
||||
return map.entrySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over a set of elements of type T.
|
||||
*
|
||||
* @return an Iterator.
|
||||
*/
|
||||
@Override
|
||||
public Iterator<Object> iterator() {
|
||||
return new ArrayObjectIterator(array);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
static class CollectionMap extends AbstractMap<Object, Object> implements Iterable<Object> {
|
||||
|
||||
private final Collection col;
|
||||
|
||||
public CollectionMap(Collection col) {
|
||||
this.col = col;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(Object key) {
|
||||
if (key instanceof Number) {
|
||||
return Iterables.get(col, ((Number) key).intValue());
|
||||
}
|
||||
try {
|
||||
int index = Integer.parseInt(key.toString());
|
||||
return Iterables.get(col, index);
|
||||
} catch (NumberFormatException nfe) {
|
||||
// if it's not a number it is as if the key doesn't exist
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return get(key) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<Object, Object>> entrySet() {
|
||||
Map<Object, Object> map = new HashMap<>(col.size());
|
||||
int i = 0;
|
||||
for (Object item : col) {
|
||||
map.put(i++, item);
|
||||
}
|
||||
return map.entrySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over a set of elements of type T.
|
||||
*
|
||||
* @return an Iterator.
|
||||
*/
|
||||
@Override
|
||||
public Iterator<Object> iterator() {
|
||||
return col.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,200 +0,0 @@
|
|||
/*
|
||||
* 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.support.text.xmustache;
|
||||
|
||||
import com.github.mustachejava.Mustache;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.io.FastStringReader;
|
||||
import org.elasticsearch.common.io.UTF8StreamWriter;
|
||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.script.CompiledScript;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.ScriptEngineService;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
import org.elasticsearch.search.lookup.SearchLookup;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class XMustacheScriptEngineService extends AbstractComponent implements ScriptEngineService {
|
||||
|
||||
public static final String NAME = "xmustache";
|
||||
|
||||
public static final List<String> TYPES = Collections.singletonList(NAME);
|
||||
|
||||
/**
|
||||
* @param settings automatically wired by Guice.
|
||||
* */
|
||||
@Inject
|
||||
public XMustacheScriptEngineService(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a template string to (in this case) a Mustache object than can
|
||||
* later be re-used for execution to fill in missing parameter values.
|
||||
*
|
||||
* @param template
|
||||
* a string representing the template to compile.
|
||||
* @return a compiled template object for later execution.
|
||||
* */
|
||||
@Override
|
||||
public Object compile(String template, Map<String, String> params) {
|
||||
/** Factory to generate Mustache objects from. */
|
||||
XContentType xContentType = detectContentType(template);
|
||||
template = trimContentType(template);
|
||||
return (new XMustacheFactory(xContentType)).compile(new FastStringReader(template), "query-template");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTypes() {
|
||||
return TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getExtensions() {
|
||||
return TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSandboxed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutableScript executable(CompiledScript compiledScript,
|
||||
@Nullable Map<String, Object> vars) {
|
||||
return new MustacheExecutableScript((Mustache) compiledScript.compiled(), vars);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchScript search(CompiledScript compiledScript, SearchLookup lookup,
|
||||
@Nullable Map<String, Object> vars) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// Nothing to do here
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scriptRemoved(CompiledScript script) {
|
||||
// Nothing to do here
|
||||
}
|
||||
|
||||
public static String prepareTemplate(String template, @Nullable XContentType contentType) {
|
||||
if (contentType == null) {
|
||||
return template;
|
||||
}
|
||||
return new StringBuilder("__")
|
||||
.append(contentType.shortName().toLowerCase(Locale.ROOT))
|
||||
.append("__::")
|
||||
.append(template)
|
||||
.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used at query execution time by script service in order to execute a query template.
|
||||
* */
|
||||
private class MustacheExecutableScript implements ExecutableScript {
|
||||
/** Compiled template object. */
|
||||
private Mustache mustache;
|
||||
/** Parameters to fill above object with. */
|
||||
private Map<String, Object> vars;
|
||||
|
||||
/**
|
||||
* @param mustache the compiled template object
|
||||
* @param vars the parameters to fill above object with
|
||||
**/
|
||||
public MustacheExecutableScript(Mustache mustache,
|
||||
Map<String, Object> vars) {
|
||||
this.mustache = mustache;
|
||||
this.vars = vars == null ? Collections.<String, Object>emptyMap() : vars;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNextVar(String name, Object value) {
|
||||
this.vars.put(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object run() {
|
||||
BytesStreamOutput result = new BytesStreamOutput();
|
||||
UTF8StreamWriter writer = utf8StreamWriter().setOutput(result);
|
||||
mustache.execute(writer, vars);
|
||||
try {
|
||||
writer.flush();
|
||||
} catch (IOException e) {
|
||||
logger.error("Could not execute query template (failed to flush writer): ", e);
|
||||
} finally {
|
||||
try {
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
logger.error("Could not execute query template (failed to close writer): ", e);
|
||||
}
|
||||
}
|
||||
return result.bytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object unwrap(Object value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/** Thread local UTF8StreamWriter to store template execution results in, thread local to save object creation.*/
|
||||
private static ThreadLocal<SoftReference<UTF8StreamWriter>> utf8StreamWriter = new ThreadLocal<>();
|
||||
|
||||
/** If exists, reset and return, otherwise create, reset and return a writer.*/
|
||||
private static UTF8StreamWriter utf8StreamWriter() {
|
||||
SoftReference<UTF8StreamWriter> ref = utf8StreamWriter.get();
|
||||
UTF8StreamWriter writer = (ref == null) ? null : ref.get();
|
||||
if (writer == null) {
|
||||
writer = new UTF8StreamWriter(1024 * 4);
|
||||
utf8StreamWriter.set(new SoftReference<>(writer));
|
||||
}
|
||||
writer.reset();
|
||||
return writer;
|
||||
}
|
||||
|
||||
private String trimContentType(String template) {
|
||||
if (!template.startsWith("__")){
|
||||
return template; //Doesn't even start with __ so can't have a content type
|
||||
}
|
||||
int index = template.indexOf("__::", 3); //There must be a __<content_type__:: prefix so the minimum length before detecting '__::' is 3
|
||||
if (index >= 0 && index < 12) { //Assume that the content type name is less than 10 characters long otherwise we may falsely detect strings that start with '__ and have '__::' somewhere in the content
|
||||
if (template.length() == 6) {
|
||||
template = "";
|
||||
} else {
|
||||
template = template.substring(index + 4);
|
||||
}
|
||||
}
|
||||
return template;
|
||||
}
|
||||
|
||||
private XContentType detectContentType(String template) {
|
||||
if (template.startsWith("__")) {
|
||||
int endOfContentName = template.indexOf("__::", 3); //There must be a __<content_type__:: prefix so the minimum length before detecting '__::' is 3
|
||||
if (endOfContentName != -1) {
|
||||
return XContentType.fromRestContentType(template.substring(2, endOfContentName));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* 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.support.text.xmustache;
|
||||
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplate;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplateEngine;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class XMustacheTextTemplateEngine extends AbstractComponent implements TextTemplateEngine {
|
||||
|
||||
private final ScriptServiceProxy service;
|
||||
|
||||
@Inject
|
||||
public XMustacheTextTemplateEngine(Settings settings, ScriptServiceProxy service) {
|
||||
super(settings);
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String render(TextTemplate template, Map<String, Object> model) {
|
||||
Map<String, Object> mergedModel = new HashMap<>();
|
||||
mergedModel.putAll(template.getParams());
|
||||
mergedModel.putAll(model);
|
||||
ExecutableScript executable = service.executable(new org.elasticsearch.script.Template(template.getTemplate(), template.getType(), XMustacheScriptEngineService.NAME, template.getContentType(), mergedModel));
|
||||
Object result = executable.run();
|
||||
if (result instanceof BytesReference) {
|
||||
return ((BytesReference) result).toUtf8();
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.script;
|
||||
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.search.lookup.SearchLookup;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A mock script engine that registers itself under the 'mustache' name so that {@link org.elasticsearch.watcher.support.text.DefaultTextTemplateEngine}
|
||||
* uses it and adds validation that watcher tests don't rely on mustache templating/
|
||||
*/
|
||||
public class MockMustacheScriptEngine extends MockScriptEngine {
|
||||
|
||||
public static final String NAME = "mustache";
|
||||
|
||||
public static class TestPlugin extends MockScriptEngine.TestPlugin {
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
public void onModule(ScriptModule module) {
|
||||
module.addScriptEngine(new ScriptEngineRegistry.ScriptEngineRegistration(MockMustacheScriptEngine.class, Collections.singletonList(NAME)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTypes() {
|
||||
return Collections.singletonList(NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getExtensions() {
|
||||
return getTypes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object compile(String script, Map<String, String> params) {
|
||||
if (script.contains("{{") && script.contains("}}")) {
|
||||
throw new IllegalArgumentException("Fix your test to not rely on mustache");
|
||||
}
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
}
|
|
@ -3,13 +3,10 @@
|
|||
* 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.messy.tests;
|
||||
|
||||
package org.elasticsearch.watcher.actions;
|
||||
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.script.mustache.MustachePlugin;
|
||||
import org.elasticsearch.watcher.client.WatcherClient;
|
||||
import org.elasticsearch.watcher.condition.compare.CompareCondition;
|
||||
import org.elasticsearch.watcher.execution.ExecutionState;
|
||||
|
@ -20,8 +17,6 @@ import org.elasticsearch.watcher.transport.actions.put.PutWatchResponse;
|
|||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
|
||||
|
@ -41,14 +36,6 @@ import static org.hamcrest.Matchers.is;
|
|||
*/
|
||||
public class TimeThrottleIntegrationTests extends AbstractWatcherIntegrationTestCase {
|
||||
|
||||
@Override
|
||||
protected List<Class<? extends Plugin>> pluginTypes() {
|
||||
List<Class<? extends Plugin>> types = new ArrayList<>();
|
||||
types.addAll(super.pluginTypes());
|
||||
types.add(MustachePlugin.class);
|
||||
return types;
|
||||
}
|
||||
|
||||
private IndexResponse indexTestDoc() {
|
||||
createIndex("actions", "events");
|
||||
ensureGreen("actions", "events");
|
|
@ -35,7 +35,6 @@ import org.elasticsearch.watcher.execution.Wid;
|
|||
import org.elasticsearch.watcher.support.http.HttpClient;
|
||||
import org.elasticsearch.watcher.support.http.HttpRequest;
|
||||
import org.elasticsearch.watcher.support.http.HttpRequestTemplate;
|
||||
import org.elasticsearch.watcher.support.http.HttpRequestTemplateTests;
|
||||
import org.elasticsearch.watcher.support.http.HttpResponse;
|
||||
import org.elasticsearch.watcher.support.http.auth.HttpAuthRegistry;
|
||||
import org.elasticsearch.watcher.support.http.auth.basic.BasicAuthFactory;
|
||||
|
@ -45,6 +44,7 @@ import org.elasticsearch.watcher.support.text.TextTemplate;
|
|||
import org.elasticsearch.watcher.support.text.TextTemplateEngine;
|
||||
import org.elasticsearch.watcher.support.xcontent.WatcherParams;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTestCase;
|
||||
import org.elasticsearch.watcher.test.MockTextTemplateEngine;
|
||||
import org.elasticsearch.watcher.watch.Payload;
|
||||
import org.jboss.netty.handler.codec.http.HttpHeaders;
|
||||
import org.joda.time.DateTime;
|
||||
|
@ -90,7 +90,7 @@ public class EmailActionTests extends ESTestCase {
|
|||
|
||||
@Before
|
||||
public void addEmailAttachmentParsers() {
|
||||
emailAttachmentParsers.put(HttpEmailAttachementParser.TYPE, new HttpEmailAttachementParser(httpClient, new HttpRequestTemplate.Parser(registry), new HttpRequestTemplateTests.MockTextTemplateEngine()));
|
||||
emailAttachmentParsers.put(HttpEmailAttachementParser.TYPE, new HttpEmailAttachementParser(httpClient, new HttpRequestTemplate.Parser(registry), new MockTextTemplateEngine()));
|
||||
emailAttachmentParsers.put(DataAttachmentParser.TYPE, new DataAttachmentParser());
|
||||
emailAttachmentParser = new EmailAttachmentsParser(emailAttachmentParsers);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* 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.messy.tests;
|
||||
package org.elasticsearch.watcher.actions.email;
|
||||
|
||||
import com.squareup.okhttp.mockwebserver.MockResponse;
|
||||
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
||||
|
@ -14,8 +14,6 @@ import org.elasticsearch.common.io.Streams;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.script.mustache.MustachePlugin;
|
||||
import org.elasticsearch.watcher.actions.email.service.EmailTemplate;
|
||||
import org.elasticsearch.watcher.actions.email.service.attachment.DataAttachment;
|
||||
import org.elasticsearch.watcher.actions.email.service.attachment.EmailAttachmentParser;
|
||||
|
@ -87,14 +85,6 @@ public class EmailAttachmentTests extends AbstractWatcherIntegrationTestCase {
|
|||
webServer.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Class<? extends Plugin>> pluginTypes() {
|
||||
List<Class<? extends Plugin>> types = new ArrayList<>();
|
||||
types.addAll(super.pluginTypes());
|
||||
types.add(MustachePlugin.class);
|
||||
return types;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Settings nodeSettings(int nodeOrdinal) {
|
||||
if(server == null) {
|
|
@ -13,12 +13,12 @@ import org.elasticsearch.test.ESTestCase;
|
|||
import org.elasticsearch.watcher.support.http.HttpClient;
|
||||
import org.elasticsearch.watcher.support.http.HttpRequest;
|
||||
import org.elasticsearch.watcher.support.http.HttpRequestTemplate;
|
||||
import org.elasticsearch.watcher.support.http.HttpRequestTemplateTests;
|
||||
import org.elasticsearch.watcher.support.http.HttpResponse;
|
||||
import org.elasticsearch.watcher.support.http.auth.HttpAuthRegistry;
|
||||
import org.elasticsearch.watcher.support.http.auth.basic.BasicAuth;
|
||||
import org.elasticsearch.watcher.support.http.auth.basic.BasicAuthFactory;
|
||||
import org.elasticsearch.watcher.support.secret.SecretService;
|
||||
import org.elasticsearch.watcher.test.MockTextTemplateEngine;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
@ -54,7 +54,7 @@ public class HttpEmailAttachementParserTests extends ESTestCase {
|
|||
|
||||
public void testSerializationWorks() throws Exception {
|
||||
Map<String, EmailAttachmentParser> attachmentParsers = new HashMap<>();
|
||||
attachmentParsers.put(HttpEmailAttachementParser.TYPE, new HttpEmailAttachementParser(httpClient, httpRequestTemplateParser, new HttpRequestTemplateTests.MockTextTemplateEngine()));
|
||||
attachmentParsers.put(HttpEmailAttachementParser.TYPE, new HttpEmailAttachementParser(httpClient, httpRequestTemplateParser, new MockTextTemplateEngine()));
|
||||
EmailAttachmentsParser emailAttachmentsParser = new EmailAttachmentsParser(attachmentParsers);
|
||||
|
||||
String id = "some-id";
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.elasticsearch.common.xcontent.json.JsonXContent;
|
|||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplate;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplateEngine;
|
||||
import org.elasticsearch.watcher.test.MockTextTemplateEngine;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
@ -441,12 +442,7 @@ public class SlackMessageTests extends ESTestCase {
|
|||
}
|
||||
|
||||
// relies on the fact that all the templates we use are inline templates without param place holders
|
||||
TextTemplateEngine engine = new TextTemplateEngine() {
|
||||
@Override
|
||||
public String render(TextTemplate template, Map<String, Object> model) {
|
||||
return template.getTemplate();
|
||||
}
|
||||
};
|
||||
TextTemplateEngine engine = new MockTextTemplateEngine();
|
||||
|
||||
SlackMessage.Template template = templateBuilder.build();
|
||||
|
||||
|
|
|
@ -385,7 +385,7 @@ public class ActionThrottleTests extends AbstractWatcherIntegrationTestCase {
|
|||
LOGGING {
|
||||
@Override
|
||||
public Action.Builder action() throws Exception {
|
||||
TextTemplate.Builder templateBuilder = new TextTemplate.Builder.Inline("{{ctx.watch_id}}");
|
||||
TextTemplate.Builder templateBuilder = new TextTemplate.Builder.Inline("_logging");
|
||||
return LoggingAction.builder(templateBuilder.build());
|
||||
}
|
||||
|
||||
|
|
|
@ -37,8 +37,9 @@ import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
|
|||
import org.elasticsearch.watcher.support.secret.SecretService;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplate;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplateEngine;
|
||||
import org.elasticsearch.watcher.support.text.xmustache.XMustacheTextTemplateEngine;
|
||||
import org.elasticsearch.watcher.support.text.DefaultTextTemplateEngine;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTestCase;
|
||||
import org.elasticsearch.watcher.test.MockTextTemplateEngine;
|
||||
import org.elasticsearch.watcher.test.WatcherTestUtils;
|
||||
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
|
||||
import org.elasticsearch.watcher.watch.Payload;
|
||||
|
@ -50,7 +51,6 @@ import org.junit.Before;
|
|||
|
||||
import javax.mail.internet.AddressException;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Collections.singletonMap;
|
||||
|
@ -78,8 +78,6 @@ public class WebhookActionTests extends ESTestCase {
|
|||
static final String TEST_HOST = "test.com";
|
||||
static final int TEST_PORT = 8089;
|
||||
|
||||
private ThreadPool tp = null;
|
||||
private ScriptServiceProxy scriptService;
|
||||
private TextTemplateEngine templateEngine;
|
||||
private HttpAuthRegistry authRegistry;
|
||||
private TextTemplate testBody;
|
||||
|
@ -91,21 +89,13 @@ public class WebhookActionTests extends ESTestCase {
|
|||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
tp = new ThreadPool(ThreadPool.Names.SAME);
|
||||
Settings settings = Settings.EMPTY;
|
||||
scriptService = WatcherTestUtils.getScriptServiceProxy(tp);
|
||||
templateEngine = new XMustacheTextTemplateEngine(settings, scriptService);
|
||||
templateEngine = new MockTextTemplateEngine();
|
||||
SecretService secretService = mock(SecretService.class);
|
||||
testBody = TextTemplate.inline(TEST_BODY_STRING).build();
|
||||
testPath = TextTemplate.inline(TEST_PATH_STRING).build();
|
||||
authRegistry = new HttpAuthRegistry(singletonMap("basic", new BasicAuthFactory(secretService)));
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() {
|
||||
tp.shutdownNow();
|
||||
}
|
||||
|
||||
public void testExecute() throws Exception {
|
||||
ExecuteScenario scenario = randomFrom(ExecuteScenario.Success, ExecuteScenario.ErrorCode);
|
||||
|
||||
|
@ -236,56 +226,6 @@ public class WebhookActionTests extends ESTestCase {
|
|||
return new WebhookActionFactory(Settings.EMPTY, client, new HttpRequestTemplate.Parser(authRegistry), templateEngine);
|
||||
}
|
||||
|
||||
public void testTemplatedHttpRequest() throws Exception {
|
||||
HttpClient httpClient = ExecuteScenario.Success.client();
|
||||
|
||||
String body = "{{ctx.watch_id}}";
|
||||
String host = "testhost";
|
||||
String path = randomFrom("{{ctx.execution_time}}", "{{ctx.trigger.scheduled_time}}", "{{ctx.trigger.triggered_time}}");
|
||||
|
||||
Map<String, TextTemplate> params = new HashMap<>();
|
||||
params.put("foo", TextTemplate.inline(randomFrom("{{ctx.execution_time}}", "{{ctx.trigger.scheduled_time}}", "{{ctx.trigger.triggered_time}}")).build());
|
||||
HttpMethod method = randomFrom(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT);
|
||||
|
||||
HttpRequestTemplate request = getHttpRequestTemplate(method, host, TEST_PORT, TextTemplate.inline(path).build(), TextTemplate.inline(body).build(), params);
|
||||
|
||||
String watchId = "_watch";
|
||||
String actionId = randomAsciiOfLength(5);
|
||||
|
||||
WebhookAction action = WebhookAction.builder(request).build();
|
||||
|
||||
ExecutableWebhookAction webhookAction = new ExecutableWebhookAction(action, logger, httpClient, templateEngine);
|
||||
|
||||
DateTime time = new DateTime(UTC);
|
||||
Watch watch = createWatch(watchId, mock(ClientProxy.class), "account1");
|
||||
WatchExecutionContext ctx = new TriggeredExecutionContext(watch, time, new ScheduleTriggerEvent(watchId, time, time), timeValueSeconds(5));
|
||||
Action.Result result = webhookAction.execute(actionId, ctx, Payload.EMPTY);
|
||||
|
||||
assertThat(result, Matchers.instanceOf(WebhookAction.Result.Success.class));
|
||||
WebhookAction.Result.Success success = (WebhookAction.Result.Success) result;
|
||||
assertThat(success.request().body(), equalTo(watchId));
|
||||
assertThat(success.request().path(), equalTo(time.toString()));
|
||||
assertThat(success.request().params().get("foo"), equalTo(time.toString()));
|
||||
|
||||
}
|
||||
|
||||
public void testValidUrls() throws Exception {
|
||||
HttpClient httpClient = ExecuteScenario.Success.client();
|
||||
HttpMethod method = HttpMethod.POST;
|
||||
TextTemplate path = TextTemplate.defaultType("/test_{{ctx.watch_id}}").build();
|
||||
String host = "test.host";
|
||||
HttpRequestTemplate requestTemplate = getHttpRequestTemplate(method, host, TEST_PORT, path, testBody, null);
|
||||
WebhookAction action = new WebhookAction(requestTemplate);
|
||||
|
||||
ExecutableWebhookAction executable = new ExecutableWebhookAction(action, logger, httpClient, templateEngine);
|
||||
|
||||
String watchId = "test_url_encode" + randomAsciiOfLength(10);
|
||||
Watch watch = createWatch(watchId, mock(ClientProxy.class), "account1");
|
||||
WatchExecutionContext ctx = new TriggeredExecutionContext(watch, new DateTime(UTC), new ScheduleTriggerEvent(watchId, new DateTime(UTC), new DateTime(UTC)), timeValueSeconds(5));
|
||||
Action.Result result = executable.execute("_id", ctx, new Payload.Simple());
|
||||
assertThat(result, Matchers.instanceOf(WebhookAction.Result.Success.class));
|
||||
}
|
||||
|
||||
public void testThatSelectingProxyWorks() throws Exception {
|
||||
Environment environment = new Environment(Settings.builder().put("path.home", createTempDir()).build());
|
||||
HttpClient httpClient = new HttpClient(Settings.EMPTY, authRegistry, environment).start();
|
||||
|
@ -311,10 +251,30 @@ public class WebhookActionTests extends ESTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testValidUrls() throws Exception {
|
||||
HttpClient client = mock(HttpClient.class);
|
||||
when(client.execute(any(HttpRequest.class)))
|
||||
.thenReturn(new HttpResponse(randomIntBetween(200, 399)));
|
||||
String watchId = "test_url_encode" + randomAsciiOfLength(10);
|
||||
|
||||
HttpMethod method = HttpMethod.POST;
|
||||
TextTemplate path = TextTemplate.defaultType("/test_" + watchId).build();
|
||||
String host = "test.host";
|
||||
TextTemplate testBody = TextTemplate.inline("ERROR HAPPENED").build();
|
||||
HttpRequestTemplate requestTemplate = getHttpRequestTemplate(method, host, TEST_PORT, path, testBody, null);
|
||||
WebhookAction action = new WebhookAction(requestTemplate);
|
||||
|
||||
ExecutableWebhookAction executable = new ExecutableWebhookAction(action, logger, client, templateEngine);
|
||||
|
||||
Watch watch = createWatch(watchId, mock(ClientProxy.class), "account1");
|
||||
WatchExecutionContext ctx = new TriggeredExecutionContext(watch, new DateTime(UTC), new ScheduleTriggerEvent(watchId, new DateTime(UTC), new DateTime(UTC)), timeValueSeconds(5));
|
||||
Action.Result result = executable.execute("_id", ctx, new Payload.Simple());
|
||||
assertThat(result, Matchers.instanceOf(WebhookAction.Result.Success.class));
|
||||
}
|
||||
|
||||
private Watch createWatch(String watchId, ClientProxy client, final String account) throws AddressException, IOException {
|
||||
return WatcherTestUtils.createTestWatch(watchId,
|
||||
client,
|
||||
scriptService,
|
||||
ExecuteScenario.Success.client(),
|
||||
new AbstractWatcherIntegrationTestCase.NoopEmailService() {
|
||||
@Override
|
||||
|
|
|
@ -83,8 +83,8 @@ public class WebhookHttpsIntegrationTests extends AbstractWatcherIntegrationTest
|
|||
webServer.enqueue(new MockResponse().setResponseCode(200).setBody("body"));
|
||||
HttpRequestTemplate.Builder builder = HttpRequestTemplate.builder("localhost", webPort)
|
||||
.scheme(Scheme.HTTPS)
|
||||
.path(TextTemplate.inline("/test/{{ctx.watch_id}}").build())
|
||||
.body(TextTemplate.inline("{{ctx.payload}}").build());
|
||||
.path(TextTemplate.inline("/test/_id").build())
|
||||
.body(TextTemplate.inline("{key=value}").build());
|
||||
|
||||
watcherClient().preparePutWatch("_id")
|
||||
.setSource(watchBuilder()
|
||||
|
@ -126,8 +126,8 @@ public class WebhookHttpsIntegrationTests extends AbstractWatcherIntegrationTest
|
|||
HttpRequestTemplate.Builder builder = HttpRequestTemplate.builder("localhost", webPort)
|
||||
.scheme(Scheme.HTTPS)
|
||||
.auth(new BasicAuth("_username", "_password".toCharArray()))
|
||||
.path(TextTemplate.inline("/test/{{ctx.watch_id}}").build())
|
||||
.body(TextTemplate.inline("{{ctx.payload}}").build());
|
||||
.path(TextTemplate.inline("/test/_id").build())
|
||||
.body(TextTemplate.inline("{key=value}").build());
|
||||
|
||||
watcherClient().preparePutWatch("_id")
|
||||
.setSource(watchBuilder()
|
||||
|
|
|
@ -70,10 +70,10 @@ public class WebhookIntegrationTests extends AbstractWatcherIntegrationTestCase
|
|||
public void testWebhook() throws Exception {
|
||||
webServer.enqueue(new MockResponse().setResponseCode(200).setBody("body"));
|
||||
HttpRequestTemplate.Builder builder = HttpRequestTemplate.builder("localhost", webPort)
|
||||
.path(TextTemplate.inline("/test/{{ctx.watch_id}}"))
|
||||
.path(TextTemplate.inline("/test/_id"))
|
||||
.putParam("param1", TextTemplate.inline("value1"))
|
||||
.putParam("watch_id", TextTemplate.inline("{{ctx.watch_id}}"))
|
||||
.body(TextTemplate.inline("{{ctx.payload}}"));
|
||||
.putParam("watch_id", TextTemplate.inline("_id"))
|
||||
.body(TextTemplate.inline("_body"));
|
||||
|
||||
watcherClient().preparePutWatch("_id")
|
||||
.setSource(watchBuilder()
|
||||
|
@ -91,7 +91,7 @@ public class WebhookIntegrationTests extends AbstractWatcherIntegrationTestCase
|
|||
assertWatchWithMinimumPerformedActionsCount("_id", 1, false);
|
||||
RecordedRequest recordedRequest = webServer.takeRequest();
|
||||
assertThat(recordedRequest.getPath(), anyOf(equalTo("/test/_id?watch_id=_id¶m1=value1"), equalTo("/test/_id?param1=value1&watch_id=_id")));
|
||||
assertThat(recordedRequest.getBody().readUtf8Line(), equalTo("{key=value}"));
|
||||
assertThat(recordedRequest.getBody().readUtf8Line(), equalTo("_body"));
|
||||
|
||||
SearchResponse response = searchWatchRecords(new Callback<SearchRequestBuilder>() {
|
||||
@Override
|
||||
|
@ -114,10 +114,10 @@ public class WebhookIntegrationTests extends AbstractWatcherIntegrationTestCase
|
|||
webServer.enqueue(new MockResponse().setResponseCode(200).setBody("body"));
|
||||
HttpRequestTemplate.Builder builder = HttpRequestTemplate.builder("localhost", webPort)
|
||||
.auth(new BasicAuth("_username", "_password".toCharArray()))
|
||||
.path(TextTemplate.inline("/test/{{ctx.watch_id}}").build())
|
||||
.path(TextTemplate.inline("/test/_id").build())
|
||||
.putParam("param1", TextTemplate.inline("value1").build())
|
||||
.putParam("watch_id", TextTemplate.inline("{{ctx.watch_id}}").build())
|
||||
.body(TextTemplate.inline("{{ctx.payload}}").build());
|
||||
.putParam("watch_id", TextTemplate.inline("_id").build())
|
||||
.body(TextTemplate.inline("_body").build());
|
||||
|
||||
watcherClient().preparePutWatch("_id")
|
||||
.setSource(watchBuilder()
|
||||
|
@ -135,7 +135,7 @@ public class WebhookIntegrationTests extends AbstractWatcherIntegrationTestCase
|
|||
assertWatchWithMinimumPerformedActionsCount("_id", 1, false);
|
||||
RecordedRequest recordedRequest = webServer.takeRequest();
|
||||
assertThat(recordedRequest.getPath(), anyOf(equalTo("/test/_id?watch_id=_id¶m1=value1"), equalTo("/test/_id?param1=value1&watch_id=_id")));
|
||||
assertThat(recordedRequest.getBody().readUtf8Line(), equalTo("{key=value}"));
|
||||
assertThat(recordedRequest.getBody().readUtf8Line(), equalTo("_body"));
|
||||
assertThat(recordedRequest.getHeader("Authorization"), equalTo("Basic X3VzZXJuYW1lOl9wYXNzd29yZA=="));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import static org.hamcrest.Matchers.equalTo;
|
|||
*/
|
||||
public class TriggeredWatchTests extends AbstractWatcherIntegrationTestCase {
|
||||
public void testParser() throws Exception {
|
||||
Watch watch = WatcherTestUtils.createTestWatch("fired_test", scriptService(), watcherHttpClient(), noopEmailService(), logger);
|
||||
Watch watch = WatcherTestUtils.createTestWatch("fired_test", watcherHttpClient(), noopEmailService(), logger);
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent(watch.id(), DateTime.now(DateTimeZone.UTC), DateTime.now(DateTimeZone.UTC));
|
||||
Wid wid = new Wid("_record", randomLong(), DateTime.now(DateTimeZone.UTC));
|
||||
TriggeredWatch triggeredWatch = new TriggeredWatch(wid, event);
|
||||
|
|
|
@ -3,23 +3,17 @@
|
|||
* 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.messy.tests;
|
||||
package org.elasticsearch.watcher.history;
|
||||
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.SearchType;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.script.mustache.MustachePlugin;
|
||||
import org.elasticsearch.search.aggregations.Aggregations;
|
||||
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
|
||||
import org.elasticsearch.watcher.execution.ExecutionState;
|
||||
import org.elasticsearch.watcher.history.HistoryStore;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTestCase;
|
||||
import org.elasticsearch.watcher.transport.actions.put.PutWatchResponse;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
|
||||
import static org.elasticsearch.search.aggregations.AggregationBuilders.terms;
|
||||
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
|
||||
|
@ -38,14 +32,6 @@ import static org.hamcrest.Matchers.notNullValue;
|
|||
*/
|
||||
public class HistoryTemplateSearchInputMappingsTests extends AbstractWatcherIntegrationTestCase {
|
||||
|
||||
@Override
|
||||
protected List<Class<? extends Plugin>> pluginTypes() {
|
||||
List<Class<? extends Plugin>> types = new ArrayList<>();
|
||||
types.addAll(super.pluginTypes());
|
||||
types.add(MustachePlugin.class);
|
||||
return types;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean timeWarped() {
|
||||
return true; // just to have better control over the triggers
|
|
@ -47,7 +47,7 @@ public class ChainIntegrationTests extends AbstractWatcherIntegrationTestCase {
|
|||
|
||||
InetSocketAddress address = internalCluster().httpAddresses()[0];
|
||||
HttpInput.Builder httpInputBuilder = httpInput(HttpRequestTemplate.builder(address.getHostString(), address.getPort())
|
||||
.path("{{ctx.payload.first.url}}")
|
||||
.path("/" + index + "/_search")
|
||||
.body(jsonBuilder().startObject().field("size", 1).endObject())
|
||||
.auth(shieldEnabled() ? new BasicAuth("test", "changeme".toCharArray()) : null));
|
||||
|
||||
|
|
|
@ -5,27 +5,22 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.support.http;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.search.aggregations.support.format.ValueParser;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.watcher.support.http.auth.HttpAuthRegistry;
|
||||
import org.elasticsearch.watcher.support.http.auth.basic.BasicAuth;
|
||||
import org.elasticsearch.watcher.support.http.auth.basic.BasicAuthFactory;
|
||||
import org.elasticsearch.watcher.support.secret.SecretService;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplate;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplateEngine;
|
||||
import org.elasticsearch.watcher.test.MockTextTemplateEngine;
|
||||
import org.jboss.netty.handler.codec.http.HttpHeaders;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.singletonMap;
|
||||
|
@ -66,6 +61,21 @@ public class HttpRequestTemplateTests extends ESTestCase {
|
|||
assertThat(request.proxy().getPort(), is(8080));
|
||||
}
|
||||
|
||||
public void testRender() {
|
||||
HttpRequestTemplate template = HttpRequestTemplate.builder("_host", 1234)
|
||||
.body(TextTemplate.inline("_body"))
|
||||
.path(TextTemplate.inline("_path"))
|
||||
.putParam("_key1", TextTemplate.inline("_value1"))
|
||||
.putHeader("_key2", TextTemplate.inline("_value2"))
|
||||
.build();
|
||||
|
||||
HttpRequest result = template.render(new MockTextTemplateEngine(), Collections.emptyMap());
|
||||
assertThat(result.body(), equalTo("_body"));
|
||||
assertThat(result.path(), equalTo("_path"));
|
||||
assertThat(result.params(), equalTo(Collections.singletonMap("_key1", "_value1")));
|
||||
assertThat(result.headers(), equalTo(Collections.singletonMap("_key2", "_value2")));
|
||||
}
|
||||
|
||||
public void testProxyParsing() throws Exception {
|
||||
HttpRequestTemplate.Builder builder = HttpRequestTemplate.builder("_host", 1234);
|
||||
builder.path("/path");
|
||||
|
@ -169,10 +179,4 @@ public class HttpRequestTemplateTests extends ESTestCase {
|
|||
assertThat(parsedTemplate, is(urlParsedTemplate));
|
||||
}
|
||||
|
||||
public static class MockTextTemplateEngine implements TextTemplateEngine {
|
||||
@Override
|
||||
public String render(TextTemplate template, Map<String, Object> model) {
|
||||
return template.getTemplate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,13 +12,18 @@ import org.elasticsearch.common.settings.Settings;
|
|||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
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.ScriptType;
|
||||
import org.elasticsearch.script.Template;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
|
||||
import org.elasticsearch.watcher.support.text.xmustache.XMustacheTextTemplateEngine;
|
||||
import org.elasticsearch.watcher.support.text.DefaultTextTemplateEngine;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplate;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplateEngine;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -32,20 +37,18 @@ import static org.hamcrest.Matchers.notNullValue;
|
|||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class TextTemplateTests extends ESTestCase {
|
||||
|
||||
private ScriptServiceProxy proxy;
|
||||
private TextTemplateEngine engine;
|
||||
private ExecutableScript script;
|
||||
private final String lang = "xmustache";
|
||||
private final String lang = "mustache";
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
proxy = mock(ScriptServiceProxy.class);
|
||||
script = mock(ExecutableScript.class);
|
||||
engine = new XMustacheTextTemplateEngine(Settings.EMPTY, proxy);
|
||||
engine = new DefaultTextTemplateEngine(Settings.EMPTY, proxy);
|
||||
}
|
||||
|
||||
public void testRender() throws Exception {
|
||||
|
@ -57,7 +60,9 @@ public class TextTemplateTests extends ESTestCase {
|
|||
merged = unmodifiableMap(merged);
|
||||
ScriptType type = randomFrom(ScriptType.values());
|
||||
|
||||
when(proxy.executable(new org.elasticsearch.script.Template(templateText, type, lang, null, merged))).thenReturn(script);
|
||||
CompiledScript compiledScript = mock(CompiledScript.class);
|
||||
when(proxy.compile(new Template(templateText, type, lang, null, merged), Collections.singletonMap("content_type", "text/plain"))).thenReturn(compiledScript);
|
||||
when(proxy.executable(compiledScript, model)).thenReturn(script);
|
||||
when(script.run()).thenReturn("rendered_text");
|
||||
|
||||
TextTemplate template = templateBuilder(type, templateText).params(params).build();
|
||||
|
@ -70,7 +75,9 @@ public class TextTemplateTests extends ESTestCase {
|
|||
Map<String, Object> model = singletonMap("key", "model_val");
|
||||
ScriptType scriptType = randomFrom(ScriptType.values());
|
||||
|
||||
when(proxy.executable(new org.elasticsearch.script.Template(templateText, scriptType, lang, null, model))).thenReturn(script);
|
||||
CompiledScript compiledScript = mock(CompiledScript.class);
|
||||
when(proxy.compile(new Template(templateText, scriptType, lang, null, model), Collections.singletonMap("content_type", "text/plain"))).thenReturn(compiledScript);
|
||||
when(proxy.executable(compiledScript, model)).thenReturn(script);
|
||||
when(script.run()).thenReturn("rendered_text");
|
||||
|
||||
TextTemplate template = templateBuilder(scriptType, templateText).params(params).build();
|
||||
|
@ -81,7 +88,9 @@ public class TextTemplateTests extends ESTestCase {
|
|||
String templateText = "_template";
|
||||
Map<String, Object> model = singletonMap("key", "model_val");
|
||||
|
||||
when(proxy.executable(new org.elasticsearch.script.Template(templateText, ScriptType.INLINE, lang, null, model))).thenReturn(script);
|
||||
CompiledScript compiledScript = mock(CompiledScript.class);
|
||||
when(proxy.compile(new Template(templateText, ScriptType.INLINE, lang, null, model), Collections.singletonMap("content_type", "text/plain"))).thenReturn(compiledScript);
|
||||
when(proxy.executable(compiledScript, model)).thenReturn(script);
|
||||
when(script.run()).thenReturn("rendered_text");
|
||||
|
||||
TextTemplate template = new TextTemplate(templateText);
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* 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.support.text.xmustache;
|
||||
|
||||
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.script.CompiledScript;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class XMustacheScriptEngineTests extends ESTestCase {
|
||||
private XMustacheScriptEngineService engine;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
engine = new XMustacheScriptEngineService(Settings.Builder.EMPTY_SETTINGS);
|
||||
}
|
||||
|
||||
public void testSimpleParameterReplace() {
|
||||
{
|
||||
String template = "__json__::GET _search {\"query\": " + "{\"boosting\": {" + "\"positive\": {\"match\": {\"body\": \"gift\"}},"
|
||||
+ "\"negative\": {\"term\": {\"body\": {\"value\": \"solr\"}" + "}}, \"negative_boost\": {{boost_val}} } }}";
|
||||
Map<String, Object> vars = new HashMap<>();
|
||||
vars.put("boost_val", "0.3");
|
||||
CompiledScript compiledScript = new CompiledScript(ScriptService.ScriptType.INLINE, "inline", "mustache", engine.compile(template, Collections.emptyMap()));
|
||||
BytesReference o = (BytesReference) engine.executable(compiledScript, vars).run();
|
||||
assertEquals("GET _search {\"query\": {\"boosting\": {\"positive\": {\"match\": {\"body\": \"gift\"}},"
|
||||
+ "\"negative\": {\"term\": {\"body\": {\"value\": \"solr\"}}}, \"negative_boost\": 0.3 } }}",
|
||||
new String(o.toBytes(), Charset.forName("UTF-8")));
|
||||
}
|
||||
{
|
||||
String template = "__json__::GET _search {\"query\": " + "{\"boosting\": {" + "\"positive\": {\"match\": {\"body\": \"gift\"}},"
|
||||
+ "\"negative\": {\"term\": {\"body\": {\"value\": \"{{body_val}}\"}" + "}}, \"negative_boost\": {{boost_val}} } }}";
|
||||
Map<String, Object> vars = new HashMap<>();
|
||||
vars.put("boost_val", "0.3");
|
||||
vars.put("body_val", "\"quick brown\"");
|
||||
CompiledScript compiledScript = new CompiledScript(ScriptService.ScriptType.INLINE, "inline", "mustache", engine.compile(template, Collections.emptyMap()));
|
||||
BytesReference o = (BytesReference) engine.executable(compiledScript, vars).run();
|
||||
assertEquals("GET _search {\"query\": {\"boosting\": {\"positive\": {\"match\": {\"body\": \"gift\"}},"
|
||||
+ "\"negative\": {\"term\": {\"body\": {\"value\": \"\\\"quick brown\\\"\"}}}, \"negative_boost\": 0.3 } }}",
|
||||
new String(o.toBytes(), Charset.forName("UTF-8")));
|
||||
}
|
||||
}
|
||||
|
||||
public void testInvalidPrefixes() throws Exception {
|
||||
String[] specialStrings = new String[]{"\f", "\n", "\r", "\"", "\\", "\t", "\b", "__::", "__" };
|
||||
String prefix = randomFrom("", "__", "____::", "___::", "____", "::", "++json__::", "__json__", "+_json__::", "__json__:");
|
||||
String template = prefix + " {{test_var1}} {{test_var2}}";
|
||||
Map<String, Object> vars = new HashMap<>();
|
||||
Writer var1Writer = new StringWriter();
|
||||
Writer var2Writer = new StringWriter();
|
||||
|
||||
for(int i = 0; i < scaledRandomIntBetween(10,1000); ++i) {
|
||||
var1Writer.write(randomRealisticUnicodeOfCodepointLengthBetween(0, 10));
|
||||
var2Writer.write(randomRealisticUnicodeOfCodepointLengthBetween(0, 10));
|
||||
var1Writer.append(randomFrom(specialStrings));
|
||||
var2Writer.append(randomFrom(specialStrings));
|
||||
}
|
||||
|
||||
vars.put("test_var1", var1Writer.toString());
|
||||
vars.put("test_var2", var2Writer.toString());
|
||||
CompiledScript compiledScript = new CompiledScript(ScriptService.ScriptType.INLINE, "inline", "mustache", engine.compile(template, Collections.emptyMap()));
|
||||
BytesReference o = (BytesReference) engine.executable(compiledScript, vars).run();
|
||||
String s1 = o.toUtf8();
|
||||
String s2 = prefix + " " + var1Writer.toString() + " " + var2Writer.toString();
|
||||
assertEquals(s1, s2);
|
||||
}
|
||||
}
|
|
@ -1,185 +0,0 @@
|
|||
/*
|
||||
* 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.support.text.xmustache;
|
||||
|
||||
import com.fasterxml.jackson.core.io.JsonStringEncoder;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.script.CompiledScript;
|
||||
import org.elasticsearch.script.ScriptEngineService;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Collections.singleton;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.hamcrest.Matchers.both;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class XMustacheTests extends ESTestCase {
|
||||
private ScriptEngineService engine;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
engine = new XMustacheScriptEngineService(Settings.EMPTY);
|
||||
}
|
||||
|
||||
public void testArrayAccess() throws Exception {
|
||||
String template = "{{data.0}} {{data.1}}";
|
||||
CompiledScript mustache = new CompiledScript(ScriptService.ScriptType.INLINE, "inline", "mustache", engine.compile(template, Collections.emptyMap()));
|
||||
Map<String, Object> vars = new HashMap<>();
|
||||
Object data = randomFrom(
|
||||
new String[] { "foo", "bar" },
|
||||
Arrays.asList("foo", "bar"));
|
||||
vars.put("data", data);
|
||||
Object output = engine.executable(mustache, vars).run();
|
||||
assertThat(output, notNullValue());
|
||||
assertThat(output, instanceOf(BytesReference.class));
|
||||
BytesReference bytes = (BytesReference) output;
|
||||
assertThat(bytes.toUtf8(), equalTo("foo bar"));
|
||||
|
||||
// Sets can come out in any order
|
||||
Set<String> setData = new HashSet<>();
|
||||
setData.add("foo");
|
||||
setData.add("bar");
|
||||
vars.put("data", setData);
|
||||
output = engine.executable(mustache, vars).run();
|
||||
assertThat(output, notNullValue());
|
||||
assertThat(output, instanceOf(BytesReference.class));
|
||||
bytes = (BytesReference) output;
|
||||
assertThat(bytes.toUtf8(), both(containsString("foo")).and(containsString("bar")));
|
||||
}
|
||||
|
||||
public void testArrayInArrayAccess() throws Exception {
|
||||
String template = "{{data.0.0}} {{data.0.1}}";
|
||||
CompiledScript mustache = new CompiledScript(ScriptService.ScriptType.INLINE, "inline", "mustache", engine.compile(template, Collections.emptyMap()));
|
||||
Map<String, Object> vars = new HashMap<>();
|
||||
Object data = randomFrom(
|
||||
new String[][] { new String[] { "foo", "bar" }},
|
||||
Collections.singletonList(new String[] { "foo", "bar" }),
|
||||
singleton(new String[] { "foo", "bar" })
|
||||
);
|
||||
vars.put("data", data);
|
||||
Object output = engine.executable(mustache, vars).run();
|
||||
assertThat(output, notNullValue());
|
||||
assertThat(output, instanceOf(BytesReference.class));
|
||||
BytesReference bytes = (BytesReference) output;
|
||||
assertThat(bytes.toUtf8(), equalTo("foo bar"));
|
||||
}
|
||||
|
||||
public void testMapInArrayAccess() throws Exception {
|
||||
String template = "{{data.0.key}} {{data.1.key}}";
|
||||
CompiledScript mustache = new CompiledScript(ScriptService.ScriptType.INLINE, "inline", "mustache", engine.compile(template, Collections.emptyMap()));
|
||||
Map<String, Object> vars = new HashMap<>();
|
||||
Object data = randomFrom(
|
||||
new Map[] { singletonMap("key", "foo"), singletonMap("key", "bar") },
|
||||
Arrays.asList(singletonMap("key", "foo"), singletonMap("key", "bar")));
|
||||
vars.put("data", data);
|
||||
Object output = engine.executable(mustache, vars).run();
|
||||
assertThat(output, notNullValue());
|
||||
assertThat(output, instanceOf(BytesReference.class));
|
||||
BytesReference bytes = (BytesReference) output;
|
||||
assertThat(bytes.toUtf8(), equalTo("foo bar"));
|
||||
|
||||
// HashSet iteration order isn't fixed
|
||||
Set<Object> setData = new HashSet<>();
|
||||
setData.add(singletonMap("key", "foo"));
|
||||
setData.add(singletonMap("key", "bar"));
|
||||
vars.put("data", setData);
|
||||
output = engine.executable(mustache, vars).run();
|
||||
assertThat(output, notNullValue());
|
||||
assertThat(output, instanceOf(BytesReference.class));
|
||||
bytes = (BytesReference) output;
|
||||
assertThat(bytes.toUtf8(), both(containsString("foo")).and(containsString("bar")));
|
||||
|
||||
}
|
||||
|
||||
public void testEscaping() throws Exception {
|
||||
XContentType contentType = randomFrom(XContentType.values());
|
||||
if (rarely()) {
|
||||
contentType = null;
|
||||
}
|
||||
Character[] specialChars = new Character[]{'\f', '\n', '\r', '"', '\\', (char) 11, '\t', '\b' };
|
||||
int iters = scaledRandomIntBetween(100, 1000);
|
||||
for (int i = 0; i < iters; i++) {
|
||||
int rounds = scaledRandomIntBetween(1, 20);
|
||||
StringWriter escaped = new StringWriter(); //This will be escaped as it is constructed
|
||||
StringWriter unescaped = new StringWriter(); //This will be escaped at the end
|
||||
|
||||
for (int j = 0; j < rounds; j++) {
|
||||
String s = getChars();
|
||||
unescaped.write(s);
|
||||
if (contentType == XContentType.JSON) {
|
||||
escaped.write(JsonStringEncoder.getInstance().quoteAsString(s));
|
||||
} else {
|
||||
escaped.write(s);
|
||||
}
|
||||
|
||||
char c = randomFrom(specialChars);
|
||||
unescaped.append(c);
|
||||
|
||||
if (contentType == XContentType.JSON) {
|
||||
escaped.write(JsonStringEncoder.getInstance().quoteAsString("" + c));
|
||||
} else {
|
||||
escaped.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (contentType == XContentType.JSON) {
|
||||
assertThat(escaped.toString(), equalTo(new String(JsonStringEncoder.getInstance().quoteAsString(unescaped.toString()))));
|
||||
}
|
||||
else {
|
||||
assertThat(escaped.toString(), equalTo(unescaped.toString()));
|
||||
}
|
||||
|
||||
String template = XMustacheScriptEngineService.prepareTemplate("{{data}}", contentType);
|
||||
|
||||
Map<String, Object> dataMap = new HashMap<>();
|
||||
dataMap.put("data", unescaped.toString());
|
||||
CompiledScript mustache = new CompiledScript(ScriptService.ScriptType.INLINE, "inline", "mustache", engine.compile(template, Collections.emptyMap()));
|
||||
Object output = engine.executable(mustache, dataMap).run();
|
||||
|
||||
assertThat(output, notNullValue());
|
||||
assertThat(output, instanceOf(BytesReference.class));
|
||||
BytesReference bytes = (BytesReference) output;
|
||||
String renderedTemplate = bytes.toUtf8();
|
||||
|
||||
if (contentType == XContentType.JSON) {
|
||||
if (!escaped.toString().equals(renderedTemplate)) {
|
||||
String escapedString = escaped.toString();
|
||||
for (int l = 0; l < renderedTemplate.length() && l < escapedString.length(); ++l) {
|
||||
if (renderedTemplate.charAt(l) != escapedString.charAt(l)) {
|
||||
logger.error("at [{}] expected [{}] but got [{}]", l, renderedTemplate.charAt(l), escapedString.charAt(l));
|
||||
}
|
||||
}
|
||||
}
|
||||
assertThat(escaped.toString(), equalTo(renderedTemplate));
|
||||
} else {
|
||||
assertThat(unescaped.toString(), equalTo(renderedTemplate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getChars() throws IOException {
|
||||
return randomRealisticUnicodeOfCodepointLengthBetween(0, 10);
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
|||
import org.elasticsearch.index.IndexModule;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.script.MockMustacheScriptEngine;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.shield.ShieldPlugin;
|
||||
|
@ -152,6 +153,7 @@ public abstract class AbstractWatcherIntegrationTestCase extends ESIntegTestCase
|
|||
plugins.remove(MockTransportService.TestPlugin.class); // shield has its own transport service
|
||||
plugins.remove(AssertingLocalTransport.TestPlugin.class); // shield has its own transport
|
||||
plugins.add(MockFSIndexStore.TestPlugin.class); // we have to explicitly add it otherwise we will fail to set the check_index_on_close setting
|
||||
plugins.add(MockMustacheScriptEngine.TestPlugin.class);
|
||||
return plugins;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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.test;
|
||||
|
||||
import org.elasticsearch.watcher.support.text.TextTemplate;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplateEngine;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class MockTextTemplateEngine implements TextTemplateEngine {
|
||||
@Override
|
||||
public String render(TextTemplate template, Map<String, Object> model) {
|
||||
return template.getTemplate();
|
||||
}
|
||||
}
|
|
@ -56,8 +56,7 @@ import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
|
|||
import org.elasticsearch.watcher.support.secret.Secret;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplate;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplateEngine;
|
||||
import org.elasticsearch.watcher.support.text.xmustache.XMustacheScriptEngineService;
|
||||
import org.elasticsearch.watcher.support.text.xmustache.XMustacheTextTemplateEngine;
|
||||
import org.elasticsearch.watcher.support.text.DefaultTextTemplateEngine;
|
||||
import org.elasticsearch.watcher.support.xcontent.ObjectPath;
|
||||
import org.elasticsearch.watcher.support.xcontent.XContentSource;
|
||||
import org.elasticsearch.watcher.transform.search.ExecutableSearchTransform;
|
||||
|
@ -175,12 +174,12 @@ public final class WatcherTestUtils {
|
|||
}
|
||||
|
||||
|
||||
public static Watch createTestWatch(String watchName, ScriptServiceProxy scriptService, HttpClient httpClient, EmailService emailService, ESLogger logger) throws AddressException {
|
||||
return createTestWatch(watchName, ClientProxy.of(ESIntegTestCase.client()), scriptService, httpClient, emailService, logger);
|
||||
public static Watch createTestWatch(String watchName, HttpClient httpClient, EmailService emailService, ESLogger logger) throws AddressException {
|
||||
return createTestWatch(watchName, ClientProxy.of(ESIntegTestCase.client()), httpClient, emailService, logger);
|
||||
}
|
||||
|
||||
|
||||
public static Watch createTestWatch(String watchName, ClientProxy client, ScriptServiceProxy scriptService, HttpClient httpClient, EmailService emailService, ESLogger logger) throws AddressException {
|
||||
public static Watch createTestWatch(String watchName, ClientProxy client, HttpClient httpClient, EmailService emailService, ESLogger logger) throws AddressException {
|
||||
|
||||
SearchRequest conditionRequest = newInputSearchRequest("my-condition-index").source(searchSource().query(matchAllQuery()));
|
||||
SearchRequest transformRequest = newInputSearchRequest("my-payload-index").source(searchSource().query(matchAllQuery()));
|
||||
|
@ -197,7 +196,7 @@ public final class WatcherTestUtils {
|
|||
TextTemplate body = TextTemplate.inline("{{ctx.watch_id}} executed with {{ctx.payload.response.hits.total_hits}} hits").build();
|
||||
httpRequest.body(body);
|
||||
|
||||
TextTemplateEngine engine = new XMustacheTextTemplateEngine(Settings.EMPTY, scriptService);
|
||||
TextTemplateEngine engine = new MockTextTemplateEngine();
|
||||
|
||||
actions.add(new ActionWrapper("_webhook", new ExecutableWebhookAction(new WebhookAction(httpRequest.build()), logger, httpClient, engine)));
|
||||
|
||||
|
@ -209,12 +208,10 @@ public final class WatcherTestUtils {
|
|||
.to(to)
|
||||
.build();
|
||||
|
||||
TextTemplateEngine templateEngine = new XMustacheTextTemplateEngine(Settings.EMPTY, scriptService);
|
||||
|
||||
Authentication auth = new Authentication("testname", new Secret("testpassword".toCharArray()));
|
||||
|
||||
EmailAction action = new EmailAction(email, "testaccount", auth, Profile.STANDARD, null, null);
|
||||
ExecutableEmailAction executale = new ExecutableEmailAction(action, logger, emailService, templateEngine, new HtmlSanitizer(Settings.EMPTY), Collections.emptyMap());
|
||||
ExecutableEmailAction executale = new ExecutableEmailAction(action, logger, emailService, engine, new HtmlSanitizer(Settings.EMPTY), Collections.emptyMap());
|
||||
|
||||
actions.add(new ActionWrapper("_email", executale));
|
||||
|
||||
|
@ -246,15 +243,12 @@ public final class WatcherTestUtils {
|
|||
.put("script.indexed", "true")
|
||||
.put("path.home", createTempDir())
|
||||
.build();
|
||||
XMustacheScriptEngineService mustacheScriptEngineService = new XMustacheScriptEngineService(settings);
|
||||
Set<ScriptEngineService> engineServiceSet = new HashSet<>();
|
||||
engineServiceSet.add(mustacheScriptEngineService);
|
||||
ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Arrays.asList(ScriptServiceProxy.INSTANCE));
|
||||
|
||||
ScriptEngineRegistry scriptEngineRegistry =
|
||||
new ScriptEngineRegistry(Collections.singletonList(new ScriptEngineRegistry.ScriptEngineRegistration(XMustacheScriptEngineService.class, XMustacheScriptEngineService.TYPES)));
|
||||
new ScriptEngineRegistry(Collections.emptyList());
|
||||
ScriptSettings scriptSettings = new ScriptSettings(scriptEngineRegistry, scriptContextRegistry);
|
||||
return ScriptServiceProxy.of(new ScriptService(settings, new Environment(settings), engineServiceSet, new ResourceWatcherService(settings, tp), scriptEngineRegistry, scriptContextRegistry, scriptSettings));
|
||||
return ScriptServiceProxy.of(new ScriptService(settings, new Environment(settings), Collections.emptySet(), new ResourceWatcherService(settings, tp), scriptEngineRegistry, scriptContextRegistry, scriptSettings));
|
||||
}
|
||||
|
||||
public static SearchType getRandomSupportedSearchType() {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* 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.messy.tests;
|
||||
package org.elasticsearch.watcher.test.integration;
|
||||
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
|
@ -13,10 +13,8 @@ import org.elasticsearch.common.unit.TimeValue;
|
|||
import org.elasticsearch.common.util.Callback;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.script.ScriptService.ScriptType;
|
||||
import org.elasticsearch.script.Template;
|
||||
import org.elasticsearch.script.mustache.MustachePlugin;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.watcher.client.WatchSourceBuilder;
|
||||
import org.elasticsearch.watcher.client.WatcherClient;
|
||||
|
@ -34,14 +32,9 @@ import org.elasticsearch.watcher.trigger.schedule.support.MonthTimes;
|
|||
import org.elasticsearch.watcher.trigger.schedule.support.WeekTimes;
|
||||
import org.elasticsearch.watcher.watch.WatchStore;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
|
||||
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||
|
@ -56,6 +49,7 @@ import static org.elasticsearch.watcher.input.InputBuilders.simpleInput;
|
|||
import static org.elasticsearch.watcher.test.WatcherTestUtils.newInputSearchRequest;
|
||||
import static org.elasticsearch.watcher.test.WatcherTestUtils.xContentSource;
|
||||
import static org.elasticsearch.watcher.trigger.TriggerBuilders.schedule;
|
||||
import static org.elasticsearch.watcher.trigger.schedule.Schedules.cron;
|
||||
import static org.elasticsearch.watcher.trigger.schedule.Schedules.daily;
|
||||
import static org.elasticsearch.watcher.trigger.schedule.Schedules.hourly;
|
||||
import static org.elasticsearch.watcher.trigger.schedule.Schedules.interval;
|
||||
|
@ -70,13 +64,10 @@ import static org.hamcrest.Matchers.notNullValue;
|
|||
public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
||||
|
||||
@Override
|
||||
protected List<Class<? extends Plugin>> pluginTypes() {
|
||||
List<Class<? extends Plugin>> types = new ArrayList<>();
|
||||
types.addAll(super.pluginTypes());
|
||||
types.add(MustachePlugin.class);
|
||||
return types;
|
||||
protected boolean timeWarped() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public void testIndexWatch() throws Exception {
|
||||
WatcherClient watcherClient = watcherClient();
|
||||
createIndex("idx");
|
||||
|
@ -89,17 +80,10 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
|||
.trigger(schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS)))
|
||||
.input(searchInput(searchRequest))
|
||||
.condition(compareCondition("ctx.payload.hits.total", CompareCondition.Op.EQ, 1L))
|
||||
.addAction("_logger", loggingAction("\n\n************\n" +
|
||||
"total hits: {{ctx.payload.hits.total}}\n" +
|
||||
"************\n")
|
||||
.addAction("_logger", loggingAction("_logging")
|
||||
.setCategory("_category")))
|
||||
.get();
|
||||
|
||||
if (timeWarped()) {
|
||||
timeWarp().scheduler().trigger("_name");
|
||||
refresh();
|
||||
}
|
||||
|
||||
timeWarp().scheduler().trigger("_name");
|
||||
assertWatchWithMinimumPerformedActionsCount("_name", 1);
|
||||
|
||||
GetWatchResponse getWatchResponse = watcherClient().prepareGetWatch().setId("_name").get();
|
||||
|
@ -116,12 +100,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
|||
.input(searchInput(searchRequest))
|
||||
.condition(compareCondition("ctx.payload.hits.total", CompareCondition.Op.EQ, 1L)))
|
||||
.get();
|
||||
|
||||
if (timeWarped()) {
|
||||
timeWarp().scheduler().trigger("_name");
|
||||
refresh();
|
||||
}
|
||||
|
||||
timeWarp().scheduler().trigger("_name");
|
||||
// The watch's condition won't meet because there is no data that matches with the query
|
||||
assertWatchWithNoActionNeeded("_name", 1);
|
||||
|
||||
|
@ -143,20 +122,11 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
|||
SearchRequest searchRequest = newInputSearchRequest("idx").source(searchSource().query(matchAllQuery()));
|
||||
PutWatchResponse indexResponse = watcherClient.preparePutWatch("_name")
|
||||
.setSource(watchBuilder()
|
||||
.trigger(schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS)))
|
||||
.trigger(schedule(cron("0/1 * * * * ? 2020")))
|
||||
.input(searchInput(searchRequest))
|
||||
.condition(compareCondition("ctx.payload.hits.total", CompareCondition.Op.EQ, 1L)))
|
||||
.get();
|
||||
assertThat(indexResponse.isCreated(), is(true));
|
||||
|
||||
if (!timeWarped()) {
|
||||
// Although there is no added benefit in this test for waiting for the watch to fire, however
|
||||
// we need to wait here because of a test timing issue. When we tear down a test we delete the watch and delete all
|
||||
// indices, but there may still be inflight fired watches, which may trigger the watch history to be created again, before
|
||||
// we finished the tear down phase.
|
||||
assertWatchWithNoActionNeeded("_name", 1);
|
||||
}
|
||||
|
||||
DeleteWatchResponse deleteWatchResponse = watcherClient.prepareDeleteWatch("_name").get();
|
||||
assertThat(deleteWatchResponse, notNullValue());
|
||||
assertThat(deleteWatchResponse.isFound(), is(true));
|
||||
|
@ -217,22 +187,17 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
|||
.setSource(source.condition(compareCondition("ctx.payload.hits.total", CompareCondition.Op.EQ, 1L)))
|
||||
.get();
|
||||
|
||||
if (timeWarped()) {
|
||||
timeWarp().clock().fastForwardSeconds(5);
|
||||
timeWarp().scheduler().trigger("_name");
|
||||
refresh();
|
||||
}
|
||||
timeWarp().clock().fastForwardSeconds(5);
|
||||
timeWarp().scheduler().trigger("_name");
|
||||
assertWatchWithMinimumPerformedActionsCount("_name", 0, false);
|
||||
|
||||
watcherClient().preparePutWatch("_name")
|
||||
.setSource(source.condition(compareCondition("ctx.payload.hits.total", CompareCondition.Op.EQ, 0L)))
|
||||
.get();
|
||||
|
||||
if (timeWarped()) {
|
||||
timeWarp().clock().fastForwardSeconds(5);
|
||||
timeWarp().scheduler().trigger("_name");
|
||||
refresh();
|
||||
}
|
||||
timeWarp().clock().fastForwardSeconds(5);
|
||||
timeWarp().scheduler().trigger("_name");
|
||||
refresh();
|
||||
assertWatchWithMinimumPerformedActionsCount("_name", 1, false);
|
||||
|
||||
watcherClient().preparePutWatch("_name")
|
||||
|
@ -241,24 +206,12 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
|||
.condition(compareCondition("ctx.payload.hits.total", CompareCondition.Op.EQ, 0L)))
|
||||
.get();
|
||||
|
||||
if (timeWarped()) {
|
||||
timeWarp().clock().fastForwardSeconds(5);
|
||||
timeWarp().scheduler().trigger("_name");
|
||||
refresh();
|
||||
} else {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
|
||||
timeWarp().clock().fastForwardSeconds(5);
|
||||
timeWarp().scheduler().trigger("_name");
|
||||
long count = findNumberOfPerformedActions("_name");
|
||||
|
||||
if (timeWarped()) {
|
||||
timeWarp().clock().fastForwardSeconds(5);
|
||||
timeWarp().scheduler().trigger("_name");
|
||||
refresh();
|
||||
} else {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
|
||||
timeWarp().clock().fastForwardSeconds(5);
|
||||
timeWarp().scheduler().trigger("_name");
|
||||
assertThat(count, equalTo(findNumberOfPerformedActions("_name")));
|
||||
}
|
||||
|
||||
|
@ -272,7 +225,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
|||
.trigger(schedule(interval("1s")))
|
||||
.input(simpleInput("key", "value"))
|
||||
.defaultThrottlePeriod(TimeValue.timeValueSeconds(0))
|
||||
.addAction("_id", loggingAction("hello {{ctx.watcher_id}}!"));
|
||||
.addAction("_id", loggingAction("_logging"));
|
||||
watcherClient().preparePutWatch("_name")
|
||||
.setSource(source)
|
||||
.get();
|
||||
|
@ -284,7 +237,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
|||
.trigger(schedule(interval("100s")))
|
||||
.defaultThrottlePeriod(TimeValue.timeValueSeconds(0))
|
||||
.input(simpleInput("key", "value"))
|
||||
.addAction("_id", loggingAction("hello {{ctx.watcher_id}}!"));
|
||||
.addAction("_id", loggingAction("_logging"));
|
||||
watcherClient().preparePutWatch("_name")
|
||||
.setSource(source)
|
||||
.get();
|
||||
|
@ -297,24 +250,12 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
|||
}
|
||||
|
||||
public void testConditionSearchWithSource() throws Exception {
|
||||
String variable = randomFrom("ctx.execution_time", "ctx.trigger.scheduled_time", "ctx.trigger.triggered_time");
|
||||
SearchSourceBuilder searchSourceBuilder = searchSource().query(boolQuery()
|
||||
.must(matchQuery("level", "a"))
|
||||
.must(rangeQuery("_timestamp")
|
||||
.from("{{" + variable + "}}||-30s")
|
||||
.to("{{" + variable + "}}")));
|
||||
|
||||
SearchSourceBuilder searchSourceBuilder = searchSource().query(matchQuery("level", "a"));
|
||||
testConditionSearch(newInputSearchRequest("events").source(searchSourceBuilder));
|
||||
}
|
||||
|
||||
public void testConditionSearchWithIndexedTemplate() throws Exception {
|
||||
String variable = randomFrom("ctx.execution_time", "ctx.trigger.scheduled_time", "ctx.trigger.triggered_time");
|
||||
SearchSourceBuilder searchSourceBuilder = searchSource().query(boolQuery()
|
||||
.must(matchQuery("level", "a"))
|
||||
.must(rangeQuery("_timestamp")
|
||||
.from("{{" + variable + "}}||-30s")
|
||||
.to("{{" + variable + "}}")));
|
||||
|
||||
SearchSourceBuilder searchSourceBuilder = searchSource().query(matchQuery("level", "a"));
|
||||
client().preparePutIndexedScript()
|
||||
.setScriptLang("mustache")
|
||||
.setId("my-template")
|
||||
|
@ -350,14 +291,8 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
|||
.condition(compareCondition("ctx.payload.hits.max_score", CompareCondition.Op.GTE, 0L)))
|
||||
.get();
|
||||
|
||||
if (timeWarped()) {
|
||||
timeWarp().scheduler().trigger("_name1");
|
||||
timeWarp().scheduler().trigger("_name2");
|
||||
refresh();
|
||||
} else {
|
||||
Thread.sleep(5000);
|
||||
}
|
||||
|
||||
timeWarp().scheduler().trigger("_name1");
|
||||
timeWarp().scheduler().trigger("_name2");
|
||||
assertWatchWithMinimumPerformedActionsCount("_name1", 1);
|
||||
assertWatchWithNoActionNeeded("_name2", 1);
|
||||
|
||||
|
@ -442,10 +377,8 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
|||
}
|
||||
|
||||
private void testConditionSearch(SearchRequest request) throws Exception {
|
||||
if (timeWarped()) {
|
||||
// reset, so we don't miss event docs when we filter over the _timestamp field.
|
||||
timeWarp().clock().setTime(SystemClock.INSTANCE.nowUTC());
|
||||
}
|
||||
// reset, so we don't miss event docs when we filter over the _timestamp field.
|
||||
timeWarp().clock().setTime(SystemClock.INSTANCE.nowUTC());
|
||||
|
||||
String watchName = "_name";
|
||||
assertAcked(prepareCreate("events").addMapping("event", "_timestamp", "enabled=true", "level", "type=string"));
|
||||
|
@ -469,14 +402,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
|||
.get();
|
||||
|
||||
refresh();
|
||||
|
||||
if (timeWarped()) {
|
||||
timeWarp().clock().fastForwardSeconds(5);
|
||||
timeWarp().scheduler().trigger(watchName);
|
||||
refresh();
|
||||
} else {
|
||||
Thread.sleep(5000);
|
||||
}
|
||||
timeWarp().scheduler().trigger(watchName);
|
||||
assertWatchWithNoActionNeeded(watchName, 1);
|
||||
|
||||
client().prepareIndex("events", "event")
|
||||
|
@ -484,13 +410,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
|||
.setSource("level", "b")
|
||||
.get();
|
||||
refresh();
|
||||
if (timeWarped()) {
|
||||
timeWarp().clock().fastForwardSeconds(5);
|
||||
timeWarp().scheduler().trigger(watchName);
|
||||
refresh();
|
||||
} else {
|
||||
Thread.sleep(5000);
|
||||
}
|
||||
timeWarp().scheduler().trigger(watchName);
|
||||
assertWatchWithNoActionNeeded(watchName, 2);
|
||||
|
||||
client().prepareIndex("events", "event")
|
||||
|
@ -498,13 +418,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
|||
.setSource("level", "a")
|
||||
.get();
|
||||
refresh();
|
||||
if (timeWarped()) {
|
||||
timeWarp().clock().fastForwardSeconds(5);
|
||||
timeWarp().scheduler().trigger(watchName);
|
||||
refresh();
|
||||
} else {
|
||||
Thread.sleep(5000);
|
||||
}
|
||||
timeWarp().scheduler().trigger(watchName);
|
||||
assertWatchWithMinimumPerformedActionsCount(watchName, 1);
|
||||
}
|
||||
}
|
|
@ -81,7 +81,7 @@ public class WatchMetadataTests extends AbstractWatcherIntegrationTestCase {
|
|||
metadata.put("foo", "bar");
|
||||
metadata.put("logtext", "This is a test");
|
||||
|
||||
LoggingAction.Builder loggingAction = loggingAction(TextTemplate.inline("{{ctx.metadata.logtext}}"))
|
||||
LoggingAction.Builder loggingAction = loggingAction(TextTemplate.inline("_logging"))
|
||||
.setLevel(LoggingLevel.DEBUG)
|
||||
.setCategory("test");
|
||||
|
||||
|
@ -102,6 +102,6 @@ public class WatchMetadataTests extends AbstractWatcherIntegrationTestCase {
|
|||
|
||||
assertThat(ObjectPath.<String>eval("metadata.foo", result), equalTo("bar"));
|
||||
assertThat(ObjectPath.<String>eval("result.actions.0.id", result), equalTo("testLogger"));
|
||||
assertThat(ObjectPath.<String>eval("result.actions.0.logging.logged_text", result), equalTo("This is a test"));
|
||||
assertThat(ObjectPath.<String>eval("result.actions.0.logging.logged_text", result), equalTo("_logging"));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue