Watcher: Remove usage of SearchRequest's template support

Template support is going to be removed from the Search API to its own Search Template API in the lang-mustache module (see elastic/elasticsearch#17906, elastic/elasticsearch#18765). This commit changes Watcher's SearchInput and SearchTransform classes so that it now uses a WatcherSearchTemplateRequest that contains both the search request and the template. Search request and template are rendered using WatcherSearchTemplateRequestService before being executed.

Original commit: elastic/x-pack-elasticsearch@bfa16ab80f
This commit is contained in:
Tanguy Leroux 2016-06-13 16:46:52 +02:00
parent f6abf979ce
commit 99ade96091
24 changed files with 850 additions and 590 deletions

View File

@ -12,31 +12,40 @@ import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.settings.Settings;
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.common.xcontent.support.XContentMapValues;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.Template;
import org.elasticsearch.plugins.ScriptPlugin;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.mustache.MustachePlugin;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.suggest.Suggesters;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
import org.elasticsearch.xpack.common.ScriptServiceProxy;
import org.elasticsearch.xpack.common.text.TextTemplate;
import org.elasticsearch.xpack.watcher.actions.ActionWrapper;
import org.elasticsearch.xpack.watcher.actions.ExecutableActions;
import org.elasticsearch.xpack.watcher.condition.always.ExecutableAlwaysCondition;
import org.elasticsearch.xpack.watcher.execution.TriggeredExecutionContext;
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.watcher.input.Input;
import org.elasticsearch.xpack.watcher.input.search.ExecutableSearchInput;
import org.elasticsearch.xpack.watcher.input.search.SearchInput;
import org.elasticsearch.xpack.watcher.input.search.SearchInputFactory;
import org.elasticsearch.xpack.watcher.input.simple.ExecutableSimpleInput;
import org.elasticsearch.xpack.watcher.input.simple.SimpleInput;
import org.elasticsearch.xpack.watcher.support.Script;
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource;
import org.elasticsearch.xpack.watcher.trigger.schedule.IntervalSchedule;
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTrigger;
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTriggerEvent;
@ -67,7 +76,6 @@ import static org.elasticsearch.test.ESIntegTestCase.Scope.SUITE;
import static org.elasticsearch.xpack.watcher.test.WatcherTestUtils.getRandomSupportedSearchType;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.joda.time.DateTimeZone.UTC;
/**
@ -81,10 +89,11 @@ public class SearchInputIT extends ESIntegTestCase {
Collection<Class<? extends Plugin>> types = new ArrayList<>();
types.addAll(super.nodePlugins());
types.add(MustachePlugin.class);
types.add(CustomScriptContextPlugin.class);
return types;
}
private final static String TEMPLATE_QUERY = "{\"query\":{\"filtered\":{\"query\":{\"match\":{\"event_type\":{\"query\":\"a\"," +
private final static String TEMPLATE_QUERY = "{\"query\":{\"bool\":{\"must\":{\"match\":{\"event_type\":{\"query\":\"a\"," +
"\"type\":\"boolean\"}}},\"filter\":{\"range\":{\"_timestamp\":" +
"{\"from\":\"{{ctx.trigger.scheduled_time}}||-{{seconds_param}}\",\"to\":\"{{ctx.trigger.scheduled_time}}\"," +
"\"include_lower\":true,\"include_upper\":true}}}}}}";
@ -100,7 +109,6 @@ public class SearchInputIT extends ESIntegTestCase {
throw new RuntimeException("failed to create config dir");
}
String path = "/org/elasticsearch/xpack/watcher/input/search/config/scripts/test_disk_template.mustache";
try (InputStream stream = SearchInputIT.class.getResourceAsStream("/org/elasticsearch/xpack/watcher/input/search/config/scripts" +
"/test_disk_template.mustache");
OutputStream out = Files.newOutputStream(scriptPath.resolve("test_disk_template.mustache"))) {
@ -126,14 +134,15 @@ public class SearchInputIT extends ESIntegTestCase {
SearchSourceBuilder searchSourceBuilder = searchSource().query(
boolQuery().must(matchQuery("event_type", "a")).must(rangeQuery("_timestamp")
.from("{{ctx.trigger.scheduled_time}}||-30s").to("{{ctx.trigger.triggered_time}}")));
SearchRequest request = client()
SearchRequest searchRequest = client()
.prepareSearch()
.setSearchType(ExecutableSearchInput.DEFAULT_SEARCH_TYPE)
.request()
.source(searchSourceBuilder);
WatcherSearchTemplateRequest request = new WatcherSearchTemplateRequest(searchRequest);
ExecutableSearchInput searchInput = new ExecutableSearchInput(new SearchInput(request, null, null, null), logger,
WatcherClientProxy.of(client()), null);
WatcherClientProxy.of(client()), watcherSearchTemplateService(), null);
WatchExecutionContext ctx = new TriggeredExecutionContext(
new Watch("test-watch",
new ScheduleTrigger(new IntervalSchedule(new IntervalSchedule.Interval(1, IntervalSchedule.Interval.Unit.MINUTES))),
@ -149,21 +158,21 @@ public class SearchInputIT extends ESIntegTestCase {
timeValueSeconds(5));
SearchInput.Result result = searchInput.execute(ctx, new Payload.Simple());
assertThat((Integer) XContentMapValues.extractValue("hits.total", result.payload().data()), equalTo(0));
assertThat(XContentMapValues.extractValue("hits.total", result.payload().data()), equalTo(0));
assertNotNull(result.executedRequest());
assertEquals(result.executedRequest().searchType(), request.searchType());
assertArrayEquals(result.executedRequest().indices(), request.indices());
assertEquals(result.executedRequest().indicesOptions(), request.indicesOptions());
assertThat(result.status(), is(Input.Result.Status.SUCCESS));
assertEquals(result.executedRequest().searchType(), request.getRequest().searchType());
assertArrayEquals(result.executedRequest().indices(), request.getRequest().indices());
assertEquals(result.executedRequest().indicesOptions(), request.getRequest().indicesOptions());
XContentSource source = toXContentSource(result);
assertThat(source.getValue("query.bool.must.1.range._timestamp.from"), equalTo("1970-01-01T00:00:00.000Z||-30s"));
assertThat(source.getValue("query.bool.must.1.range._timestamp.to"), equalTo("1970-01-01T00:00:00.000Z"));
}
public void testSearchInlineTemplate() throws Exception {
WatchExecutionContext ctx = createContext();
final String expectedTemplateString = "{\"query\":{\"filtered\":{\"query\":{\"match\":{\"event_type\":{\"query\":\"a\","
+ "\"type\":\"boolean\"}}},\"filter\":{\"range\":{\"_timestamp\":"
+ "{\"from\":\"{{ctx.trigger.scheduled_time}}||-{{seconds_param}}\",\"to\":\"{{ctx.trigger.scheduled_time}}\","
+ "\"include_lower\":true,\"include_upper\":true}}}}}}";
Map<String, Object> triggerParams = new HashMap<String, Object>();
triggerParams.put("triggered_time", new DateTime(1970, 01, 01, 00, 01, 00, 000, ISOChronology.getInstanceUTC()));
triggerParams.put("scheduled_time", new DateTime(1970, 01, 01, 00, 01, 00, 000, ISOChronology.getInstanceUTC()));
@ -178,17 +187,26 @@ public class SearchInputIT extends ESIntegTestCase {
Map<String, Object> expectedParams = new HashMap<String, Object>();
expectedParams.put("seconds_param", "30s");
expectedParams.put("ctx", ctxParams);
Template expectedTemplate = new Template(expectedTemplateString, ScriptType.INLINE, null, XContentType.JSON, expectedParams);
Map<String, Object> params = new HashMap<>();
params.put("seconds_param", "30s");
Template template = new Template(TEMPLATE_QUERY, ScriptType.INLINE, null, XContentType.JSON, params);
Script template = Script.inline(TEMPLATE_QUERY).lang("mustache").params(params).build();
SearchRequest request = client().prepareSearch().setSearchType(ExecutableSearchInput.DEFAULT_SEARCH_TYPE)
.setIndices("test-search-index").setTemplate(template).request();
SearchRequest request = client().prepareSearch()
.setSearchType(ExecutableSearchInput.DEFAULT_SEARCH_TYPE)
.setIndices("test-search-index").request();
SearchInput.Result executedResult = executeSearchInput(request, ctx);
assertThat(executedResult.executedRequest().template(), equalTo(expectedTemplate));
SearchInput.Result executedResult = executeSearchInput(request, template, ctx);
assertNotNull(executedResult.executedRequest());
assertThat(executedResult.status(), is(Input.Result.Status.SUCCESS));
assertEquals(executedResult.executedRequest().searchType(), request.searchType());
assertArrayEquals(executedResult.executedRequest().indices(), request.indices());
assertEquals(executedResult.executedRequest().indicesOptions(), request.indicesOptions());
XContentSource source = toXContentSource(executedResult);
assertThat(source.getValue("query.bool.filter.0.range._timestamp.from"), equalTo("1970-01-01T00:01:00.000Z||-30s"));
assertThat(source.getValue("query.bool.filter.0.range._timestamp.to"), equalTo("1970-01-01T00:01:00.000Z"));
}
public void testSearchIndexedTemplate() throws Exception {
@ -204,17 +222,24 @@ public class SearchInputIT extends ESIntegTestCase {
Map<String, Object> params = new HashMap<>();
params.put("seconds_param", "30s");
Template template = new Template("test-template", ScriptType.STORED, null, null, params);
Script template = Script.indexed("test-template").lang("mustache").params(params).build();
jsonBuilder().value(TextTemplate.indexed("test-template").params(params).build()).bytes();
SearchRequest request = client().prepareSearch().setSearchType(ExecutableSearchInput.DEFAULT_SEARCH_TYPE)
.setIndices("test-search-index").setTemplate(template).request();
.setIndices("test-search-index").request();
SearchInput.Result executedResult = executeSearchInput(request, template, ctx);
assertNotNull(executedResult.executedRequest());
assertThat(executedResult.status(), is(Input.Result.Status.SUCCESS));
assertEquals(executedResult.executedRequest().searchType(), request.searchType());
assertArrayEquals(executedResult.executedRequest().indices(), request.indices());
assertEquals(executedResult.executedRequest().indicesOptions(), request.indicesOptions());
XContentSource source = toXContentSource(executedResult);
assertThat(source.getValue("query.bool.filter.0.range._timestamp.from"), equalTo("1970-01-01T00:01:00.000Z||-30s"));
assertThat(source.getValue("query.bool.filter.0.range._timestamp.to"), equalTo("1970-01-01T00:01:00.000Z"));
SearchInput.Result executedResult = executeSearchInput(request, ctx);
Template resultTemplate = executedResult.executedRequest().template();
assertThat(resultTemplate, notNullValue());
assertThat(resultTemplate.getScript(), equalTo("test-template"));
assertThat(resultTemplate.getType(), equalTo(ScriptType.STORED));
}
public void testSearchOnDiskTemplate() throws Exception {
@ -223,15 +248,16 @@ public class SearchInputIT extends ESIntegTestCase {
Map<String, Object> params = new HashMap<>();
params.put("seconds_param", "30s");
Template template = new Template("test_disk_template", ScriptType.FILE, null, null, params);
Script template = Script.file("test_disk_template").lang("mustache").params(params).build();
SearchRequest request = client().prepareSearch().setSearchType(ExecutableSearchInput.DEFAULT_SEARCH_TYPE)
.setIndices("test-search-index").setTemplate(template).request();
.setIndices("test-search-index").request();
SearchInput.Result executedResult = executeSearchInput(request, ctx);
Template resultTemplate = executedResult.executedRequest().template();
assertThat(resultTemplate, notNullValue());
assertThat(resultTemplate.getScript(), equalTo("test_disk_template"));
assertThat(resultTemplate.getType(), equalTo(ScriptType.FILE));
SearchInput.Result executedResult = executeSearchInput(request, template, ctx);
assertNotNull(executedResult.executedRequest());
assertThat(executedResult.status(), is(Input.Result.Status.SUCCESS));
assertArrayEquals(executedResult.executedRequest().indices(), request.indices());
assertEquals(executedResult.executedRequest().indicesOptions(), request.indicesOptions());
}
public void testDifferentSearchType() throws Exception {
@ -241,14 +267,16 @@ public class SearchInputIT extends ESIntegTestCase {
);
SearchType searchType = getRandomSupportedSearchType();
SearchRequest request = client()
SearchRequest searchRequest = client()
.prepareSearch()
.setSearchType(searchType)
.request()
.source(searchSourceBuilder);
WatcherSearchTemplateRequest request = new WatcherSearchTemplateRequest(searchRequest);
ExecutableSearchInput searchInput = new ExecutableSearchInput(new SearchInput(request, null, null, null), logger,
WatcherClientProxy.of(client()), null);
WatcherClientProxy.of(client()), watcherSearchTemplateService(), null);
WatchExecutionContext ctx = new TriggeredExecutionContext(
new Watch("test-watch",
new ScheduleTrigger(new IntervalSchedule(new IntervalSchedule.Interval(1, IntervalSchedule.Interval.Unit.MINUTES))),
@ -264,15 +292,20 @@ public class SearchInputIT extends ESIntegTestCase {
timeValueSeconds(5));
SearchInput.Result result = searchInput.execute(ctx, new Payload.Simple());
assertThat((Integer) XContentMapValues.extractValue("hits.total", result.payload().data()), equalTo(0));
assertThat(XContentMapValues.extractValue("hits.total", result.payload().data()), equalTo(0));
assertNotNull(result.executedRequest());
assertThat(result.status(), is(Input.Result.Status.SUCCESS));
assertEquals(result.executedRequest().searchType(), searchType);
assertArrayEquals(result.executedRequest().indices(), request.indices());
assertEquals(result.executedRequest().indicesOptions(), request.indicesOptions());
assertArrayEquals(result.executedRequest().indices(), searchRequest.indices());
assertEquals(result.executedRequest().indicesOptions(), searchRequest.indicesOptions());
XContentSource source = toXContentSource(result);
assertThat(source.getValue("query.bool.must.1.range._timestamp.from"), equalTo("1970-01-01T00:00:00.000Z||-30s"));
assertThat(source.getValue("query.bool.must.1.range._timestamp.to"), equalTo("1970-01-01T00:00:00.000Z"));
}
public void testParserValid() throws Exception {
SearchRequest request = client().prepareSearch()
SearchRequest searchRequest = client().prepareSearch()
.setSearchType(ExecutableSearchInput.DEFAULT_SEARCH_TYPE)
.request()
.source(searchSource()
@ -280,13 +313,13 @@ public class SearchInputIT extends ESIntegTestCase {
.from("{{ctx.trigger.scheduled_time}}||-30s").to("{{ctx.trigger.triggered_time}}"))));
TimeValue timeout = randomBoolean() ? TimeValue.timeValueSeconds(randomInt(10)) : null;
XContentBuilder builder = jsonBuilder().value(new SearchInput(request, null, timeout, null));
XContentBuilder builder = jsonBuilder().value(new SearchInput(new WatcherSearchTemplateRequest(searchRequest), null, timeout, null));
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
parser.nextToken();
IndicesQueriesRegistry indicesQueryRegistry = internalCluster().getInstance(IndicesQueriesRegistry.class);
SearchInputFactory factory = new SearchInputFactory(Settings.EMPTY, WatcherClientProxy.of(client()), indicesQueryRegistry,
null, null);
null, null, scriptService());
SearchInput searchInput = factory.parseInput("_id", parser);
assertEquals(SearchInput.TYPE, searchInput.type());
@ -309,15 +342,47 @@ public class SearchInputIT extends ESIntegTestCase {
timeValueSeconds(5));
}
private SearchInput.Result executeSearchInput(SearchRequest request, WatchExecutionContext ctx) throws IOException {
private SearchInput.Result executeSearchInput(SearchRequest request, Script template, WatchExecutionContext ctx) throws IOException {
createIndex("test-search-index");
ensureGreen("test-search-index");
SearchInput.Builder siBuilder = SearchInput.builder(request);
SearchInput.Builder siBuilder = SearchInput.builder(new WatcherSearchTemplateRequest(request, template));
SearchInput si = siBuilder.build();
ExecutableSearchInput searchInput = new ExecutableSearchInput(si, logger, WatcherClientProxy.of(client()), null);
ExecutableSearchInput searchInput = new ExecutableSearchInput(si, logger, WatcherClientProxy.of(client()),
watcherSearchTemplateService(), null);
return searchInput.execute(ctx, new Payload.Simple());
}
protected WatcherSearchTemplateService watcherSearchTemplateService() {
String master = internalCluster().getMasterName();
return new WatcherSearchTemplateService(internalCluster().clusterService(master).getSettings(),
ScriptServiceProxy.of(internalCluster().getInstance(ScriptService.class, master), internalCluster().clusterService(master)),
internalCluster().getInstance(IndicesQueriesRegistry.class, master),
internalCluster().getInstance(AggregatorParsers.class, master),
internalCluster().getInstance(Suggesters.class, master)
);
}
protected ScriptServiceProxy scriptService() {
return ScriptServiceProxy.of(internalCluster().getInstance(ScriptService.class), internalCluster().clusterService());
}
private XContentSource toXContentSource(SearchInput.Result result) throws IOException {
try (XContentBuilder builder = jsonBuilder()) {
result.executedRequest().source().toXContent(builder, ToXContent.EMPTY_PARAMS);
return new XContentSource(builder);
}
}
/**
* Custom plugin that registers XPack script context.
*/
public static class CustomScriptContextPlugin extends Plugin implements ScriptPlugin {
@Override
public ScriptContext.Plugin getCustomScriptContexts() {
return ScriptServiceProxy.INSTANCE;
}
}
}

View File

@ -11,25 +11,28 @@ import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.client.Requests;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.Template;
import org.elasticsearch.plugins.ScriptPlugin;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.mustache.MustachePlugin;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.suggest.Suggesters;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
import org.elasticsearch.xpack.common.ScriptServiceProxy;
import org.elasticsearch.xpack.common.text.TextTemplate;
import org.elasticsearch.xpack.watcher.actions.ExecutableActions;
import org.elasticsearch.xpack.watcher.condition.always.ExecutableAlwaysCondition;
@ -37,7 +40,11 @@ import org.elasticsearch.xpack.watcher.execution.TriggeredExecutionContext;
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.watcher.input.simple.ExecutableSimpleInput;
import org.elasticsearch.xpack.watcher.input.simple.SimpleInput;
import org.elasticsearch.xpack.watcher.support.Script;
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource;
import org.elasticsearch.xpack.watcher.transform.Transform;
import org.elasticsearch.xpack.watcher.transform.TransformBuilders;
import org.elasticsearch.xpack.watcher.transform.search.ExecutableSearchTransform;
@ -100,6 +107,7 @@ public class SearchTransformIT extends ESIntegTestCase {
Collection<Class<? extends Plugin>> types = new ArrayList<>();
types.addAll(super.nodePlugins());
types.add(MustachePlugin.class);
types.add(CustomScriptContextPlugin.class);
return types;
}
@ -154,7 +162,8 @@ public class SearchTransformIT extends ESIntegTestCase {
SearchRequest request = Requests.searchRequest("idx").source(new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()));
SearchTransform searchTransform = TransformBuilders.searchTransform(request).build();
ExecutableSearchTransform transform = new ExecutableSearchTransform(searchTransform, logger, WatcherClientProxy.of(client()), null);
ExecutableSearchTransform transform = new ExecutableSearchTransform(searchTransform, logger, WatcherClientProxy.of(client()),
watcherSearchTemplateService(), null);
WatchExecutionContext ctx = mockExecutionContext("_name", EMPTY_PAYLOAD);
@ -188,7 +197,8 @@ public class SearchTransformIT extends ESIntegTestCase {
new SearchSourceBuilder().query(QueryBuilders.wrapperQuery(jsonBuilder().startObject()
.startObject("_unknown_query_").endObject().endObject().bytes())));
SearchTransform searchTransform = TransformBuilders.searchTransform(request).build();
ExecutableSearchTransform transform = new ExecutableSearchTransform(searchTransform, logger, WatcherClientProxy.of(client()), null);
ExecutableSearchTransform transform = new ExecutableSearchTransform(searchTransform, logger, WatcherClientProxy.of(client()),
watcherSearchTemplateService(), null);
WatchExecutionContext ctx = mockExecutionContext("_name", EMPTY_PAYLOAD);
@ -200,23 +210,27 @@ public class SearchTransformIT extends ESIntegTestCase {
assertThat(result.reason(), containsString("no [query] registered for [_unknown_query_]"));
// extract the base64 encoded query from the template script, path is: query -> wrapper -> query
String jsonQuery = result.executedRequest().template().getScript();
Map<String, Object> map = XContentFactory.xContent(jsonQuery).createParser(jsonQuery).map();
try (XContentBuilder builder = jsonBuilder()) {
result.executedRequest().source().toXContent(builder, ToXContent.EMPTY_PARAMS);
assertThat(map, hasKey("query"));
assertThat(map.get("query"), instanceOf(Map.class));
String jsonQuery = builder.string();
Map<String, Object> map = XContentFactory.xContent(jsonQuery).createParser(jsonQuery).map();
map = (Map<String, Object>) map.get("query");
assertThat(map, hasKey("wrapper"));
assertThat(map.get("wrapper"), instanceOf(Map.class));
assertThat(map, hasKey("query"));
assertThat(map.get("query"), instanceOf(Map.class));
map = (Map<String, Object>) map.get("wrapper");
assertThat(map, hasKey("query"));
assertThat(map.get("query"), instanceOf(String.class));
map = (Map<String, Object>) map.get("query");
assertThat(map, hasKey("wrapper"));
assertThat(map.get("wrapper"), instanceOf(Map.class));
String queryAsBase64 = (String) map.get("query");
String decodedQuery = new String(Base64.getDecoder().decode(queryAsBase64), StandardCharsets.UTF_8);
assertThat(decodedQuery, containsString("_unknown_query_"));
map = (Map<String, Object>) map.get("wrapper");
assertThat(map, hasKey("query"));
assertThat(map.get("query"), instanceOf(String.class));
String queryAsBase64 = (String) map.get("query");
String decodedQuery = new String(Base64.getDecoder().decode(queryAsBase64), StandardCharsets.UTF_8);
assertThat(decodedQuery, containsString("_unknown_query_"));
}
}
public void testExecuteMustacheTemplate() throws Exception {
@ -252,7 +266,8 @@ public class SearchTransformIT extends ESIntegTestCase {
.must(termQuery("value", "{{ctx.payload.value}}"))));
SearchTransform searchTransform = TransformBuilders.searchTransform(request).build();
ExecutableSearchTransform transform = new ExecutableSearchTransform(searchTransform, logger, WatcherClientProxy.of(client()), null);
ExecutableSearchTransform transform = new ExecutableSearchTransform(searchTransform, logger, WatcherClientProxy.of(client()),
watcherSearchTemplateService(), null);
ScheduleTriggerEvent event = new ScheduleTriggerEvent("_name", parseDate("2015-01-04T00:00:00", UTC),
parseDate("2015-01-01T00:00:00", UTC));
@ -319,24 +334,24 @@ public class SearchTransformIT extends ESIntegTestCase {
IndicesQueriesRegistry indicesQueryRegistry = internalCluster().getInstance(IndicesQueriesRegistry.class);
SearchTransformFactory transformFactory = new SearchTransformFactory(Settings.EMPTY, WatcherClientProxy.of(client()),
indicesQueryRegistry, null, null);
indicesQueryRegistry, null, null, scriptService());
ExecutableSearchTransform executable = transformFactory.parseExecutable("_id", parser);
assertThat(executable, notNullValue());
assertThat(executable.type(), is(SearchTransform.TYPE));
assertThat(executable.transform().getRequest(), notNullValue());
if (indices != null) {
assertThat(executable.transform().getRequest().indices(), arrayContainingInAnyOrder(indices));
assertThat(executable.transform().getRequest().getRequest().indices(), arrayContainingInAnyOrder(indices));
}
if (searchType != null) {
assertThat(executable.transform().getRequest().searchType(), is(searchType));
assertThat(executable.transform().getRequest().getRequest().searchType(), is(searchType));
}
if (templateName != null) {
assertThat(executable.transform().getRequest().template(),
equalTo(new Template("template1", ScriptType.FILE, null, null, null)));
assertThat(executable.transform().getRequest().getTemplate(),
equalTo(Script.file("template1").build()));
}
SearchSourceBuilder source = new SearchSourceBuilder().query(QueryBuilders.matchAllQuery());
assertThat(executable.transform().getRequest().source(), equalTo(source));
assertThat(executable.transform().getRequest().getRequest().source(), equalTo(source));
assertThat(executable.transform().getTimeout(), equalTo(readTimeout));
}
@ -348,11 +363,6 @@ public class SearchTransformIT extends ESIntegTestCase {
"{\"from\":\"{{ctx.trigger.scheduled_time}}||-{{seconds_param}}\",\"to\":\"{{ctx.trigger.scheduled_time}}\"," +
"\"include_lower\":true,\"include_upper\":true}}}]}}}";
final String expectedTemplateString = "{\"query\":{\"bool\":{\"must\":[{\"match\":{\"event_type\":{\"query\":\"a\","
+ "\"type\":\"boolean\"}}},{\"range\":{\"_timestamp\":"
+ "{\"from\":\"{{ctx.trigger.scheduled_time}}||-{{seconds_param}}\",\"to\":\"{{ctx.trigger.scheduled_time}}\","
+ "\"include_lower\":true,\"include_upper\":true}}}]}}}";
Map<String, Object> triggerParams = new HashMap<String, Object>();
triggerParams.put("triggered_time", new DateTime(1970, 01, 01, 00, 01, 00, 000, ISOChronology.getInstanceUTC()));
triggerParams.put("scheduled_time", new DateTime(1970, 01, 01, 00, 01, 00, 000, ISOChronology.getInstanceUTC()));
@ -367,18 +377,24 @@ public class SearchTransformIT extends ESIntegTestCase {
Map<String, Object> expectedParams = new HashMap<String, Object>();
expectedParams.put("seconds_param", "30s");
expectedParams.put("ctx", ctxParams);
Template expectedTemplate = new Template(expectedTemplateString, ScriptType.INLINE, null, XContentType.JSON, expectedParams);
Map<String, Object> params = new HashMap<>();
params.put("seconds_param", "30s");
Template template = new Template(templateQuery, ScriptType.INLINE, null, XContentType.JSON, params);
Script template = Script.inline(templateQuery).lang("mustache").params(params).build();
SearchRequest request = client().prepareSearch().setSearchType(ExecutableSearchTransform.DEFAULT_SEARCH_TYPE)
.setIndices("test-search-index").setTemplate(template).request();
.setIndices("test-search-index").request();
SearchTransform.Result executedResult = executeSearchTransform(request, ctx);
SearchTransform.Result executedResult = executeSearchTransform(request, template, ctx);
assertThat(executedResult.executedRequest().template(), equalTo(expectedTemplate));
assertThat(executedResult.status(), is(Transform.Result.Status.SUCCESS));
assertEquals(executedResult.executedRequest().searchType(), request.searchType());
assertArrayEquals(executedResult.executedRequest().indices(), request.indices());
assertEquals(executedResult.executedRequest().indicesOptions(), request.indicesOptions());
XContentSource source = toXContentSource(executedResult);
assertThat(source.getValue("query.bool.must.1.range._timestamp.from"), equalTo("1970-01-01T00:01:00.000Z||-30s"));
assertThat(source.getValue("query.bool.must.1.range._timestamp.to"), equalTo("1970-01-01T00:01:00.000Z"));
}
public void testSearchIndexedTemplate() throws Exception {
@ -399,24 +415,24 @@ public class SearchTransformIT extends ESIntegTestCase {
Map<String, Object> params = new HashMap<>();
params.put("seconds_param", "30s");
BytesReference templateSource = jsonBuilder()
.value(TextTemplate.indexed("test-script").params(params).build())
.bytes();
Template template = new Template("test-script", ScriptType.STORED, null, null, null);
Script template = Script.indexed("test-script").lang("mustache").params(params).build();
SearchRequest request = client()
.prepareSearch()
.setSearchType(ExecutableSearchTransform.DEFAULT_SEARCH_TYPE)
.setIndices("test-search-index")
.setTemplate(template)
.request();
SearchTransform.Result result = executeSearchTransform(request, ctx);
SearchTransform.Result result = executeSearchTransform(request, template, ctx);
assertNotNull(result.executedRequest());
Template resultTemplate = result.executedRequest().template();
assertThat(resultTemplate, notNullValue());
assertThat(resultTemplate.getScript(), equalTo("test-script"));
assertThat(resultTemplate.getType(), equalTo(ScriptType.STORED));
assertThat(result.status(), is(Transform.Result.Status.SUCCESS));
assertArrayEquals(result.executedRequest().indices(), request.indices());
assertEquals(result.executedRequest().indicesOptions(), request.indicesOptions());
XContentSource source = toXContentSource(result);
assertThat(source.getValue("query.bool.must.1.range._timestamp.from"), equalTo("1970-01-01T00:01:00.000Z||-30s"));
assertThat(source.getValue("query.bool.must.1.range._timestamp.to"), equalTo("1970-01-01T00:01:00.000Z"));
}
public void testSearchOnDiskTemplate() throws Exception {
@ -425,16 +441,20 @@ public class SearchTransformIT extends ESIntegTestCase {
Map<String, Object> params = new HashMap<>();
params.put("seconds_param", "30s");
Template template = new Template("test_disk_template", ScriptType.FILE, null, null, null);
Script template = Script.file("test_disk_template").lang("mustache").params(params).build();
SearchRequest request = client().prepareSearch().setSearchType(ExecutableSearchTransform.DEFAULT_SEARCH_TYPE)
.setIndices("test-search-index").setTemplate(template).request();
.setIndices("test-search-index").request();
SearchTransform.Result result = executeSearchTransform(request, template, ctx);
SearchTransform.Result result = executeSearchTransform(request, ctx);
assertNotNull(result.executedRequest());
Template resultTemplate = result.executedRequest().template();
assertThat(resultTemplate, notNullValue());
assertThat(resultTemplate.getScript(), equalTo("test_disk_template"));
assertThat(resultTemplate.getType(), equalTo(ScriptType.FILE));
assertThat(result.status(), is(Transform.Result.Status.SUCCESS));
assertArrayEquals(result.executedRequest().indices(), request.indices());
assertEquals(result.executedRequest().indicesOptions(), request.indicesOptions());
XContentSource source = toXContentSource(result);
assertThat(source.getValue("query.bool.must.1.range._timestamp.from"), equalTo("1970-01-01T00:01:00.000Z||-30s"));
assertThat(source.getValue("query.bool.must.1.range._timestamp.to"), equalTo("1970-01-01T00:01:00.000Z"));
}
public void testDifferentSearchType() throws Exception {
@ -453,13 +473,18 @@ public class SearchTransformIT extends ESIntegTestCase {
.request()
.source(searchSourceBuilder);
SearchTransform.Result result = executeSearchTransform(request, ctx);
SearchTransform.Result result = executeSearchTransform(request, null, ctx);
assertThat((Integer) XContentMapValues.extractValue("hits.total", result.payload().data()), equalTo(0));
assertThat(XContentMapValues.extractValue("hits.total", result.payload().data()), equalTo(0));
assertThat(result.executedRequest(), notNullValue());
assertThat(result.status(), is(Transform.Result.Status.SUCCESS));
assertThat(result.executedRequest().searchType(), is(searchType));
assertThat(result.executedRequest().indices(), arrayContainingInAnyOrder(request.indices()));
assertThat(result.executedRequest().indicesOptions(), equalTo(request.indicesOptions()));
XContentSource source = toXContentSource(result);
assertThat(source.getValue("query.bool.must.1.range._timestamp.from"), equalTo("1970-01-01T00:01:00.000Z||-30s"));
assertThat(source.getValue("query.bool.must.1.range._timestamp.to"), equalTo("1970-01-01T00:01:00.000Z"));
}
private WatchExecutionContext createContext() {
@ -479,17 +504,31 @@ public class SearchTransformIT extends ESIntegTestCase {
timeValueSeconds(5));
}
private SearchTransform.Result executeSearchTransform(SearchRequest request, WatchExecutionContext ctx) throws IOException {
private SearchTransform.Result executeSearchTransform(SearchRequest request, Script template, WatchExecutionContext ctx)
throws IOException {
createIndex("test-search-index");
ensureGreen("test-search-index");
SearchTransform searchTransform = TransformBuilders.searchTransform(request).build();
SearchTransform searchTransform = TransformBuilders.searchTransform(new WatcherSearchTemplateRequest(request, template)).build();
ExecutableSearchTransform executableSearchTransform = new ExecutableSearchTransform(searchTransform, logger,
WatcherClientProxy.of(client()), null);
WatcherClientProxy.of(client()), watcherSearchTemplateService(), null);
return executableSearchTransform.execute(ctx, Payload.Simple.EMPTY);
}
protected WatcherSearchTemplateService watcherSearchTemplateService() {
String master = internalCluster().getMasterName();
return new WatcherSearchTemplateService(internalCluster().clusterService(master).getSettings(),
ScriptServiceProxy.of(internalCluster().getInstance(ScriptService.class, master), internalCluster().clusterService(master)),
internalCluster().getInstance(IndicesQueriesRegistry.class, master),
internalCluster().getInstance(AggregatorParsers.class, master),
internalCluster().getInstance(Suggesters.class, master)
);
}
protected ScriptServiceProxy scriptService() {
return ScriptServiceProxy.of(internalCluster().getInstance(ScriptService.class), internalCluster().clusterService());
}
private static Map<String, Object> doc(String date, String value) {
Map<String, Object> doc = new HashMap<>();
@ -498,4 +537,21 @@ public class SearchTransformIT extends ESIntegTestCase {
return doc;
}
private XContentSource toXContentSource(SearchTransform.Result result) throws IOException {
try (XContentBuilder builder = jsonBuilder()) {
result.executedRequest().source().toXContent(builder, ToXContent.EMPTY_PARAMS);
return new XContentSource(builder);
}
}
/**
* Custom plugin that registers XPack script context.
*/
public static class CustomScriptContextPlugin extends Plugin implements ScriptPlugin {
@Override
public ScriptContext.Plugin getCustomScriptContexts() {
return ScriptServiceProxy.INSTANCE;
}
}
}

View File

@ -5,32 +5,32 @@
*/
package org.elasticsearch.messy.tests;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.Version;
import org.elasticsearch.action.search.SearchPhaseExecutionException;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.indices.TermsLookup;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.Template;
import org.elasticsearch.script.mustache.MustachePlugin;
import org.elasticsearch.script.mustache.MustacheScriptEngineService;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.junit.Before;
import org.junit.BeforeClass;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import static java.util.Collections.singletonMap;
import static org.elasticsearch.script.ScriptService.ScriptType.INLINE;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
@SecurityIntegTestCase.AwaitsFix(bugUrl = "clean up test to not use mustache templates, otherwise needs many resources here")
//@SecurityIntegTestCase.AwaitsFix(bugUrl = "clean up test to not use mustache templates, otherwise needs many resources here")
public class SecurityCachePermissionIT extends SecurityIntegTestCase {
static final String READ_ONE_IDX_USER = "read_user";
@ -72,18 +72,6 @@ public class SecurityCachePermissionIT extends SecurityIntegTestCase {
public void loadData() {
index("data", "a", "1", "{ \"name\": \"John\", \"token\": \"token1\" }");
index("tokens", "tokens", "1", "{ \"group\": \"1\", \"tokens\": [\"token1\", \"token2\"] }");
client().admin().cluster().preparePutStoredScript().setSource(new BytesArray("{\n" +
"\"template\": {\n" +
" \"query\": {\n" +
" \"exists\": {\n" +
" \"field\": \"{{name}}\"\n" +
" }\n" +
" }\n" +
" }\n" +
"}"))
.setScriptLang("mustache")
.setId("testTemplate")
.execute().actionGet();
refresh();
}
@ -96,38 +84,44 @@ public class SecurityCachePermissionIT extends SecurityIntegTestCase {
// Repeat with unauthorized user!!!!
try {
response = client().filterWithHeader(Collections.singletonMap("Authorization", basicAuthHeaderValue(READ_ONE_IDX_USER,
response = client().filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(READ_ONE_IDX_USER,
new SecuredString("changeme".toCharArray()))))
.prepareSearch("data").setTypes("a").setQuery(QueryBuilders.constantScoreQuery(
QueryBuilders.termsLookupQuery("token", new TermsLookup("tokens", "tokens", "1", "tokens"))))
.execute().actionGet();
fail("search phase exception should have been thrown! response was:\n" + response.toString());
} catch (SearchPhaseExecutionException e) {
} catch (ElasticsearchSecurityException e) {
assertThat(e.toString(), containsString("ElasticsearchSecurityException[action"));
assertThat(e.toString(), containsString("unauthorized"));
}
}
public void testThatScriptServiceDoesntLeakData() {
String source = "{\n" +
"\"template\": {\n" +
" \"query\": {\n" +
" \"exists\": {\n" +
" \"field\": \"{{name}}\"\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
//Template template = new Template(source, INLINE, MustacheScriptEngineService.NAME, null, singletonMap("name", "token"));
SearchResponse response = client().prepareSearch("data").setTypes("a")
.setTemplate(new Template("testTemplate", ScriptService.ScriptType.STORED, MustacheScriptEngineService.NAME, null,
Collections.<String, Object>singletonMap("name", "token")))
.setQuery(QueryBuilders.templateQuery(source, singletonMap("name", "token")))
.execute().actionGet();
assertThat(response.isTimedOut(), is(false));
assertThat(response.getHits().hits().length, is(1));
// Repeat with unauthorized user!!!!
try {
response = client().filterWithHeader(Collections.singletonMap("Authorization", basicAuthHeaderValue(READ_ONE_IDX_USER,
new SecuredString("changeme".toCharArray()))))
ElasticsearchSecurityException e = expectThrows(ElasticsearchSecurityException.class, () -> client()
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(READ_ONE_IDX_USER,
new SecuredString("changeme".toCharArray()))))
.prepareSearch("data").setTypes("a")
.setTemplate(new Template("testTemplate", ScriptService.ScriptType.STORED, MustacheScriptEngineService.NAME, null,
Collections.<String, Object>singletonMap("name", "token")))
.execute().actionGet();
fail("search phase exception should have been thrown! response was:\n" + response.toString());
} catch (SearchPhaseExecutionException e) {
assertThat(e.toString(), containsString("ElasticsearchSecurityException[action"));
assertThat(e.toString(), containsString("unauthorized"));
}
.setQuery(QueryBuilders.templateQuery(source, singletonMap("name", "token")))
.execute().actionGet());
assertThat(e.toString(), containsString("ElasticsearchSecurityException[action"));
assertThat(e.toString(), containsString("unauthorized"));
}
}

View File

@ -1,4 +1,3 @@
cluster:admin/render/template/search
cluster:admin/repository/delete
cluster:admin/repository/get
cluster:admin/repository/put

View File

@ -1,4 +1,3 @@
cluster:admin/render/template/search
cluster:admin/snapshot/status[nodes]
cluster:admin/snapshot/status[nodes][n]
cluster:admin/tasks/cancel[n]

View File

@ -31,7 +31,6 @@ import org.elasticsearch.xpack.watcher.execution.InternalWatchExecutor;
import org.elasticsearch.xpack.watcher.history.HistoryModule;
import org.elasticsearch.xpack.watcher.history.HistoryStore;
import org.elasticsearch.xpack.watcher.input.InputModule;
import org.elasticsearch.xpack.watcher.input.chain.ChainInputFactory;
import org.elasticsearch.xpack.watcher.rest.action.RestAckWatchAction;
import org.elasticsearch.xpack.watcher.rest.action.RestActivateWatchAction;
import org.elasticsearch.xpack.watcher.rest.action.RestDeleteWatchAction;
@ -266,5 +265,5 @@ public class Watcher {
"[.watcher-history-YYYY.MM.dd] are allowed to be created", value);
}
}

View File

@ -6,14 +6,14 @@
package org.elasticsearch.xpack.watcher.input;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.xpack.common.http.HttpRequestTemplate;
import org.elasticsearch.xpack.watcher.input.chain.ChainInput;
import org.elasticsearch.xpack.watcher.input.http.HttpInput;
import org.elasticsearch.xpack.watcher.input.none.NoneInput;
import org.elasticsearch.xpack.watcher.input.search.SearchInput;
import org.elasticsearch.xpack.watcher.input.simple.SimpleInput;
import org.elasticsearch.xpack.common.http.HttpRequestTemplate;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.watch.Payload;
import java.util.HashMap;
@ -31,12 +31,12 @@ public final class InputBuilders {
return NoneInput.builder();
}
public static SearchInput.Builder searchInput(SearchRequest request) {
public static SearchInput.Builder searchInput(WatcherSearchTemplateRequest request) {
return SearchInput.builder(request);
}
public static SearchInput.Builder searchInput(SearchRequestBuilder builder) {
return searchInput(builder.request());
public static SearchInput.Builder searchInput(SearchRequest request) {
return searchInput(new WatcherSearchTemplateRequest(request));
}
public static SimpleInput.Builder simpleInput() {

View File

@ -11,16 +11,15 @@ import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.watcher.input.ExecutableInput;
import org.elasticsearch.xpack.watcher.support.WatcherUtils;
import org.elasticsearch.xpack.watcher.support.XContentFilterKeysUtils;
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import org.elasticsearch.xpack.watcher.watch.Payload;
import java.util.Map;
@ -35,11 +34,14 @@ public class ExecutableSearchInput extends ExecutableInput<SearchInput, SearchIn
public static final SearchType DEFAULT_SEARCH_TYPE = SearchType.QUERY_THEN_FETCH;
private final WatcherClientProxy client;
private final WatcherSearchTemplateService searchTemplateService;
private final @Nullable TimeValue timeout;
public ExecutableSearchInput(SearchInput input, ESLogger logger, WatcherClientProxy client, @Nullable TimeValue defaultTimeout) {
public ExecutableSearchInput(SearchInput input, ESLogger logger, WatcherClientProxy client,
WatcherSearchTemplateService searchTemplateService, @Nullable TimeValue defaultTimeout) {
super(input, logger);
this.client = client;
this.searchTemplateService = searchTemplateService;
this.timeout = input.getTimeout() != null ? input.getTimeout() : defaultTimeout;
}
@ -47,7 +49,7 @@ public class ExecutableSearchInput extends ExecutableInput<SearchInput, SearchIn
public SearchInput.Result execute(WatchExecutionContext ctx, Payload payload) {
SearchRequest request = null;
try {
request = WatcherUtils.createSearchRequestFromPrototype(input.getSearchRequest(), ctx, payload);
request = searchTemplateService.createSearchRequestFromPrototype(input.getRequest(), ctx, payload);
return doExecute(ctx, request);
} catch (Exception e) {
logger.error("failed to execute [{}] input for [{}]", e, SearchInput.TYPE, ctx.watch());
@ -57,8 +59,7 @@ public class ExecutableSearchInput extends ExecutableInput<SearchInput, SearchIn
SearchInput.Result doExecute(WatchExecutionContext ctx, SearchRequest request) throws Exception {
if (logger.isTraceEnabled()) {
ToXContent source = request.source() != null ? request.source() : request.template();
logger.trace("[{}] running query for [{}] [{}]", ctx.id(), ctx.watch().id(), XContentHelper.toString(source));
logger.trace("[{}] running query for [{}] [{}]", ctx.id(), ctx.watch().id(), XContentHelper.toString(request.source()));
}
SearchResponse response = client.search(request, timeout);

View File

@ -17,9 +17,8 @@ import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.suggest.Suggesters;
import org.elasticsearch.xpack.watcher.input.Input;
import org.elasticsearch.xpack.watcher.support.SearchRequestEquivalence;
import org.elasticsearch.xpack.watcher.support.WatcherDateTimeUtils;
import org.elasticsearch.xpack.watcher.support.WatcherUtils;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.watch.Payload;
import org.joda.time.DateTimeZone;
@ -38,14 +37,14 @@ public class SearchInput implements Input {
public static final String TYPE = "search";
private final SearchRequest searchRequest;
private final WatcherSearchTemplateRequest request;
private final @Nullable Set<String> extractKeys;
private final @Nullable TimeValue timeout;
private final @Nullable DateTimeZone dynamicNameTimeZone;
public SearchInput(SearchRequest searchRequest, @Nullable Set<String> extractKeys,
public SearchInput(WatcherSearchTemplateRequest request, @Nullable Set<String> extractKeys,
@Nullable TimeValue timeout, @Nullable DateTimeZone dynamicNameTimeZone) {
this.searchRequest = searchRequest;
this.request = request;
this.extractKeys = extractKeys;
this.timeout = timeout;
this.dynamicNameTimeZone = dynamicNameTimeZone;
@ -63,7 +62,7 @@ public class SearchInput implements Input {
SearchInput that = (SearchInput) o;
if (!SearchRequestEquivalence.INSTANCE.equivalent(searchRequest, this.searchRequest)) return false;
if (request != null ? !request.equals(that.request) : that.request != null) return false;
if (extractKeys != null ? !extractKeys.equals(that.extractKeys) : that.extractKeys != null) return false;
if (timeout != null ? !timeout.equals(that.timeout) : that.timeout != null) return false;
return !(dynamicNameTimeZone != null ? !dynamicNameTimeZone.equals(that.dynamicNameTimeZone) : that.dynamicNameTimeZone != null);
@ -71,15 +70,15 @@ public class SearchInput implements Input {
@Override
public int hashCode() {
int result = searchRequest.hashCode();
int result = request != null ? request.hashCode() : 0;
result = 31 * result + (extractKeys != null ? extractKeys.hashCode() : 0);
result = 31 * result + (timeout != null ? timeout.hashCode() : 0);
result = 31 * result + (dynamicNameTimeZone != null ? dynamicNameTimeZone.hashCode() : 0);
return result;
}
public SearchRequest getSearchRequest() {
return searchRequest;
public WatcherSearchTemplateRequest getRequest() {
return request;
}
public Set<String> getExtractKeys() {
@ -97,8 +96,9 @@ public class SearchInput implements Input {
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(Field.REQUEST.getPreferredName());
builder = WatcherUtils.writeSearchRequest(searchRequest, builder, params);
if (request != null) {
builder.field(Field.REQUEST.getPreferredName(), request);
}
if (extractKeys != null) {
builder.field(Field.EXTRACT.getPreferredName(), extractKeys);
}
@ -115,7 +115,7 @@ public class SearchInput implements Input {
public static SearchInput parse(String watchId, XContentParser parser, QueryParseContext context,
AggregatorParsers aggParsers, Suggesters suggesters)
throws IOException {
SearchRequest request = null;
WatcherSearchTemplateRequest request = null;
Set<String> extract = null;
TimeValue timeout = null;
DateTimeZone dynamicNameTimeZone = null;
@ -127,7 +127,7 @@ public class SearchInput implements Input {
currentFieldName = parser.currentName();
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, Field.REQUEST)) {
try {
request = WatcherUtils.readSearchRequest(parser, ExecutableSearchInput.DEFAULT_SEARCH_TYPE, context,
request = WatcherSearchTemplateRequest.fromXContent(parser, ExecutableSearchInput.DEFAULT_SEARCH_TYPE, context,
aggParsers, suggesters);
} catch (ElasticsearchParseException srpe) {
throw new ElasticsearchParseException("could not parse [{}] input for watch [{}]. failed to parse [{}]", srpe, TYPE,
@ -170,7 +170,7 @@ public class SearchInput implements Input {
return new SearchInput(request, extract, timeout, dynamicNameTimeZone);
}
public static Builder builder(SearchRequest request) {
public static Builder builder(WatcherSearchTemplateRequest request) {
return new Builder(request);
}
@ -198,20 +198,19 @@ public class SearchInput implements Input {
return builder;
}
builder.startObject(type);
builder.field(Field.REQUEST.getPreferredName());
WatcherUtils.writeSearchRequest(request, builder, params);
builder.field(Field.REQUEST.getPreferredName(), new WatcherSearchTemplateRequest(request));
return builder.endObject();
}
}
public static class Builder implements Input.Builder<SearchInput> {
private final SearchRequest request;
private final WatcherSearchTemplateRequest request;
private final Set<String> extractKeys = new HashSet<>();
private TimeValue timeout;
private DateTimeZone dynamicNameTimeZone;
private Builder(SearchRequest request) {
private Builder(WatcherSearchTemplateRequest request) {
this.request = request;
}

View File

@ -5,8 +5,6 @@
*/
package org.elasticsearch.xpack.watcher.input.search;
import java.io.IOException;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.logging.Loggers;
@ -17,10 +15,14 @@ import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.suggest.Suggesters;
import org.elasticsearch.xpack.common.ScriptServiceProxy;
import org.elasticsearch.xpack.security.InternalClient;
import org.elasticsearch.xpack.watcher.input.InputFactory;
import org.elasticsearch.xpack.watcher.input.simple.ExecutableSimpleInput;
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import java.io.IOException;
/**
*
@ -33,15 +35,16 @@ public class SearchInputFactory extends InputFactory<SearchInput, SearchInput.Re
private final AggregatorParsers aggParsers;
private final Suggesters suggesters;
private final ParseFieldMatcher parseFieldMatcher;
private final WatcherSearchTemplateService searchTemplateService;
@Inject
public SearchInputFactory(Settings settings, InternalClient client, IndicesQueriesRegistry queryRegistry,
AggregatorParsers aggParsers, Suggesters suggesters) {
this(settings, new WatcherClientProxy(settings, client), queryRegistry, aggParsers, suggesters);
AggregatorParsers aggParsers, Suggesters suggesters, ScriptServiceProxy scriptService) {
this(settings, new WatcherClientProxy(settings, client), queryRegistry, aggParsers, suggesters, scriptService);
}
public SearchInputFactory(Settings settings, WatcherClientProxy client, IndicesQueriesRegistry queryRegistry,
AggregatorParsers aggParsers, Suggesters suggesters) {
AggregatorParsers aggParsers, Suggesters suggesters, ScriptServiceProxy scriptService) {
super(Loggers.getLogger(ExecutableSimpleInput.class, settings));
this.parseFieldMatcher = new ParseFieldMatcher(settings);
this.client = client;
@ -49,6 +52,7 @@ public class SearchInputFactory extends InputFactory<SearchInput, SearchInput.Re
this.aggParsers = aggParsers;
this.suggesters = suggesters;
this.defaultTimeout = settings.getAsTime("xpack.watcher.input.search.default_timeout", null);
this.searchTemplateService = new WatcherSearchTemplateService(settings, scriptService, queryRegistry, aggParsers, suggesters);
}
@Override
@ -64,6 +68,6 @@ public class SearchInputFactory extends InputFactory<SearchInput, SearchInput.Re
@Override
public ExecutableSearchInput createExecutable(SearchInput input) {
return new ExecutableSearchInput(input, inputLogger, client, defaultTimeout);
return new ExecutableSearchInput(input, inputLogger, client, searchTemplateService, defaultTimeout);
}
}

View File

@ -5,54 +5,22 @@
*/
package org.elasticsearch.xpack.watcher.support;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.xpack.watcher.support.WatcherDateTimeUtils.formatDate;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.Template;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.suggest.Suggesters;
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.watcher.watch.Payload;
import org.joda.time.DateTime;
/**
*/
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.Map;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.xpack.watcher.support.WatcherDateTimeUtils.formatDate;
public final class WatcherUtils {
static final ParseField INDICES_FIELD = new ParseField("indices");
static final ParseField TYPES_FIELD = new ParseField("types");
static final ParseField BODY_FIELD = new ParseField("body");
static final ParseField SEARCH_TYPE_FIELD = new ParseField("search_type");
static final ParseField INDICES_OPTIONS_FIELD = new ParseField("indices_options");
static final ParseField EXPAND_WILDCARDS_FIELD = new ParseField("expand_wildcards");
static final ParseField IGNORE_UNAVAILABLE_FIELD = new ParseField("ignore_unavailable");
static final ParseField ALLOW_NO_INDICES_FIELD = new ParseField("allow_no_indices");
static final ParseField TEMPLATE_FIELD = new ParseField("template");
public final static IndicesOptions DEFAULT_INDICES_OPTIONS = IndicesOptions.lenientExpandOpen();
private WatcherUtils() {
}
@ -62,214 +30,6 @@ public final class WatcherUtils {
return XContentHelper.convertToMap(builder.bytes(), false).v2();
}
public static SearchRequest createSearchRequestFromPrototype(SearchRequest requestPrototype, WatchExecutionContext ctx,
Payload payload) throws IOException {
SearchRequest request = new SearchRequest()
.indicesOptions(requestPrototype.indicesOptions())
.searchType(requestPrototype.searchType())
.indices(requestPrototype.indices())
.types(requestPrototype.types());
// Due the inconsistency with templates in ES 1.x, we maintain our own template format.
// This template format we use now, will become the template structure in ES 2.0
Map<String, Object> watcherContextParams = Variables.createCtxModel(ctx, payload);
if (requestPrototype.source() != null) {
// Here we convert a watch search request body into an inline search template,
// this way if any Watcher related context variables are used, they will get resolved,
// by ES search template support
XContentBuilder builder = jsonBuilder();
requestPrototype.source().toXContent(builder, ToXContent.EMPTY_PARAMS);
Template template = new Template(builder.string(), ScriptType.INLINE, null, builder.contentType(), watcherContextParams);
request.template(template);
} else if (requestPrototype.template() != null) {
// Here we convert watcher template into a ES core templates. Due to the different format we use, we
// convert to the template format used in ES core
Template template = requestPrototype.template();
if (template.getParams() != null) {
watcherContextParams.putAll(template.getParams());
}
template = new Template(template.getScript(), template.getType(), template.getLang(), template.getContentType(),
watcherContextParams);
request.template(template);
// }
}
// falling back to an empty body
return request;
}
/**
* Reads a new search request instance for the specified parser.
*/
public static SearchRequest readSearchRequest(XContentParser parser, SearchType searchType, QueryParseContext context,
AggregatorParsers aggParsers, Suggesters suggesters)
throws IOException {
IndicesOptions indicesOptions = DEFAULT_INDICES_OPTIONS;
SearchRequest searchRequest = new SearchRequest();
XContentParser.Token token;
String currentFieldName = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
if (ParseFieldMatcher.STRICT.match(currentFieldName, BODY_FIELD)) {
searchRequest.source(SearchSourceBuilder.fromXContent(context, aggParsers, suggesters));
}
} else if (token == XContentParser.Token.START_ARRAY) {
if (ParseFieldMatcher.STRICT.match(currentFieldName, INDICES_FIELD)) {
List<String> indices = new ArrayList<>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
if (token == XContentParser.Token.VALUE_STRING) {
indices.add(parser.textOrNull());
} else {
throw new ElasticsearchParseException("could not read search request. expected string values in [" +
currentFieldName + "] field, but instead found [" + token + "]");
}
}
searchRequest.indices(indices.toArray(new String[indices.size()]));
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, TYPES_FIELD)) {
List<String> types = new ArrayList<>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
if (token == XContentParser.Token.VALUE_STRING) {
types.add(parser.textOrNull());
} else {
throw new ElasticsearchParseException("could not read search request. expected string values in [" +
currentFieldName + "] field, but instead found [" + token + "]");
}
}
searchRequest.types(types.toArray(new String[types.size()]));
} else {
throw new ElasticsearchParseException("could not read search request. unexpected array field [" +
currentFieldName + "]");
}
} else if (token == XContentParser.Token.START_OBJECT) {
if (ParseFieldMatcher.STRICT.match(currentFieldName, INDICES_OPTIONS_FIELD)) {
boolean expandOpen = DEFAULT_INDICES_OPTIONS.expandWildcardsOpen();
boolean expandClosed = DEFAULT_INDICES_OPTIONS.expandWildcardsClosed();
boolean allowNoIndices = DEFAULT_INDICES_OPTIONS.allowNoIndices();
boolean ignoreUnavailable = DEFAULT_INDICES_OPTIONS.ignoreUnavailable();
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token.isValue()) {
if (ParseFieldMatcher.STRICT.match(currentFieldName, EXPAND_WILDCARDS_FIELD)) {
switch (parser.text()) {
case "all":
expandOpen = true;
expandClosed = true;
break;
case "open":
expandOpen = true;
expandClosed = false;
break;
case "closed":
expandOpen = false;
expandClosed = true;
break;
case "none":
expandOpen = false;
expandClosed = false;
break;
default:
throw new ElasticsearchParseException("could not read search request. unknown value [" +
parser.text() + "] for [" + currentFieldName + "] field ");
}
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, IGNORE_UNAVAILABLE_FIELD)) {
ignoreUnavailable = parser.booleanValue();
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, ALLOW_NO_INDICES_FIELD)) {
allowNoIndices = parser.booleanValue();
} else {
throw new ElasticsearchParseException("could not read search request. unexpected index option [" +
currentFieldName + "]");
}
} else {
throw new ElasticsearchParseException("could not read search request. unexpected object field [" +
currentFieldName + "]");
}
}
indicesOptions = IndicesOptions.fromOptions(ignoreUnavailable, allowNoIndices, expandOpen, expandClosed,
DEFAULT_INDICES_OPTIONS);
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, TEMPLATE_FIELD)) {
Template template = Template.parse(parser, ParseFieldMatcher.STRICT);
searchRequest.template(template);
} else {
throw new ElasticsearchParseException("could not read search request. unexpected object field [" +
currentFieldName + "]");
}
} else if (token == XContentParser.Token.VALUE_STRING) {
if (ParseFieldMatcher.STRICT.match(currentFieldName, INDICES_FIELD)) {
String indicesStr = parser.text();
searchRequest.indices(Strings.delimitedListToStringArray(indicesStr, ",", " \t"));
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, TYPES_FIELD)) {
String typesStr = parser.text();
searchRequest.types(Strings.delimitedListToStringArray(typesStr, ",", " \t"));
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, SEARCH_TYPE_FIELD)) {
searchType = SearchType.fromString(parser.text().toLowerCase(Locale.ROOT), ParseFieldMatcher.EMPTY);
} else {
throw new ElasticsearchParseException("could not read search request. unexpected string field [" +
currentFieldName + "]");
}
} else {
throw new ElasticsearchParseException("could not read search request. unexpected token [" + token + "]");
}
}
if (searchRequest.indices() == null) {
searchRequest.indices(Strings.EMPTY_ARRAY);
}
searchRequest.searchType(searchType);
searchRequest.indicesOptions(indicesOptions);
return searchRequest;
}
/**
* Writes the searchRequest to the specified builder.
*/
public static XContentBuilder writeSearchRequest(SearchRequest searchRequest, XContentBuilder builder,
ToXContent.Params params) throws IOException {
if (searchRequest == null) {
builder.nullValue();
return builder;
}
builder.startObject();
if (searchRequest.searchType() != null) {
builder.field(SEARCH_TYPE_FIELD.getPreferredName(), searchRequest.searchType().toString().toLowerCase(Locale.ENGLISH));
}
if (searchRequest.indices() != null) {
builder.array(INDICES_FIELD.getPreferredName(), searchRequest.indices());
}
if (searchRequest.types() != null) {
builder.array(TYPES_FIELD.getPreferredName(), searchRequest.types());
}
if (searchRequest.source() != null) {
builder.field(BODY_FIELD.getPreferredName(), searchRequest.source());
}
if (searchRequest.template() != null) {
builder.field(TEMPLATE_FIELD.getPreferredName(), searchRequest.template());
}
if (searchRequest.indicesOptions() != DEFAULT_INDICES_OPTIONS) {
IndicesOptions options = searchRequest.indicesOptions();
builder.startObject(INDICES_OPTIONS_FIELD.getPreferredName());
String value;
if (options.expandWildcardsClosed() && options.expandWildcardsOpen()) {
value = "all";
} else if (options.expandWildcardsOpen()) {
value = "open";
} else if (options.expandWildcardsClosed()) {
value = "closed";
} else {
value = "none";
}
builder.field(EXPAND_WILDCARDS_FIELD.getPreferredName(), value);
builder.field(IGNORE_UNAVAILABLE_FIELD.getPreferredName(), options.ignoreUnavailable());
builder.field(ALLOW_NO_INDICES_FIELD.getPreferredName(), options.allowNoIndices());
builder.endObject();
}
return builder.endObject();
}
public static Map<String, Object> flattenModel(Map<String, Object> map) {
Map<String, Object> result = new HashMap<>();
flattenModel("", map, result);

View File

@ -0,0 +1,254 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.watcher.support.search;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.suggest.Suggesters;
import org.elasticsearch.xpack.watcher.support.Script;
import org.elasticsearch.xpack.watcher.support.SearchRequestEquivalence;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
/**
* A {@link WatcherSearchTemplateRequest} contains the search request and the eventual template that will
* be rendered as a script by {@link WatcherSearchTemplateService} before being executed.
*/
public class WatcherSearchTemplateRequest implements ToXContent {
private final SearchRequest request;
private final @Nullable Script template;
public WatcherSearchTemplateRequest(SearchRequest searchRequest, @Nullable Script template) {
this.request = Objects.requireNonNull(searchRequest);
this.template = template;
}
public WatcherSearchTemplateRequest(SearchRequest request) {
this(request, null);
}
public SearchRequest getRequest() {
return request;
}
public Script getTemplate() {
return template;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
if (request != null) {
if (request.searchType() != null) {
builder.field(SEARCH_TYPE_FIELD.getPreferredName(), request.searchType().toString().toLowerCase(Locale.ENGLISH));
}
if (request.indices() != null) {
builder.array(INDICES_FIELD.getPreferredName(), request.indices());
}
if (request.types() != null) {
builder.array(TYPES_FIELD.getPreferredName(), request.types());
}
if (request.source() != null) {
builder.field(BODY_FIELD.getPreferredName(), request.source());
}
if (request.indicesOptions() != DEFAULT_INDICES_OPTIONS) {
IndicesOptions options = request.indicesOptions();
builder.startObject(INDICES_OPTIONS_FIELD.getPreferredName());
String value;
if (options.expandWildcardsClosed() && options.expandWildcardsOpen()) {
value = "all";
} else if (options.expandWildcardsOpen()) {
value = "open";
} else if (options.expandWildcardsClosed()) {
value = "closed";
} else {
value = "none";
}
builder.field(EXPAND_WILDCARDS_FIELD.getPreferredName(), value);
builder.field(IGNORE_UNAVAILABLE_FIELD.getPreferredName(), options.ignoreUnavailable());
builder.field(ALLOW_NO_INDICES_FIELD.getPreferredName(), options.allowNoIndices());
builder.endObject();
}
}
if (template != null) {
builder.field(TEMPLATE_FIELD.getPreferredName(), template);
}
return builder.endObject();
}
/**
* Reads a new watcher search request instance for the specified parser.
*/
public static WatcherSearchTemplateRequest fromXContent(XContentParser parser, SearchType searchType, QueryParseContext context,
AggregatorParsers aggParsers, Suggesters suggesters)
throws IOException {
IndicesOptions indicesOptions = DEFAULT_INDICES_OPTIONS;
SearchRequest searchRequest = new SearchRequest();
Script template = null;
XContentParser.Token token;
String currentFieldName = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
if (ParseFieldMatcher.STRICT.match(currentFieldName, BODY_FIELD)) {
searchRequest.source(SearchSourceBuilder.fromXContent(context, aggParsers, suggesters));
}
} else if (token == XContentParser.Token.START_ARRAY) {
if (ParseFieldMatcher.STRICT.match(currentFieldName, INDICES_FIELD)) {
List<String> indices = new ArrayList<>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
if (token == XContentParser.Token.VALUE_STRING) {
indices.add(parser.textOrNull());
} else {
throw new ElasticsearchParseException("could not read search request. expected string values in [" +
currentFieldName + "] field, but instead found [" + token + "]");
}
}
searchRequest.indices(indices.toArray(new String[indices.size()]));
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, TYPES_FIELD)) {
List<String> types = new ArrayList<>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
if (token == XContentParser.Token.VALUE_STRING) {
types.add(parser.textOrNull());
} else {
throw new ElasticsearchParseException("could not read search request. expected string values in [" +
currentFieldName + "] field, but instead found [" + token + "]");
}
}
searchRequest.types(types.toArray(new String[types.size()]));
} else {
throw new ElasticsearchParseException("could not read search request. unexpected array field [" +
currentFieldName + "]");
}
} else if (token == XContentParser.Token.START_OBJECT) {
if (ParseFieldMatcher.STRICT.match(currentFieldName, INDICES_OPTIONS_FIELD)) {
boolean expandOpen = DEFAULT_INDICES_OPTIONS.expandWildcardsOpen();
boolean expandClosed = DEFAULT_INDICES_OPTIONS.expandWildcardsClosed();
boolean allowNoIndices = DEFAULT_INDICES_OPTIONS.allowNoIndices();
boolean ignoreUnavailable = DEFAULT_INDICES_OPTIONS.ignoreUnavailable();
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token.isValue()) {
if (ParseFieldMatcher.STRICT.match(currentFieldName, EXPAND_WILDCARDS_FIELD)) {
switch (parser.text()) {
case "all":
expandOpen = true;
expandClosed = true;
break;
case "open":
expandOpen = true;
expandClosed = false;
break;
case "closed":
expandOpen = false;
expandClosed = true;
break;
case "none":
expandOpen = false;
expandClosed = false;
break;
default:
throw new ElasticsearchParseException("could not read search request. unknown value [" +
parser.text() + "] for [" + currentFieldName + "] field ");
}
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, IGNORE_UNAVAILABLE_FIELD)) {
ignoreUnavailable = parser.booleanValue();
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, ALLOW_NO_INDICES_FIELD)) {
allowNoIndices = parser.booleanValue();
} else {
throw new ElasticsearchParseException("could not read search request. unexpected index option [" +
currentFieldName + "]");
}
} else {
throw new ElasticsearchParseException("could not read search request. unexpected object field [" +
currentFieldName + "]");
}
}
indicesOptions = IndicesOptions.fromOptions(ignoreUnavailable, allowNoIndices, expandOpen, expandClosed,
DEFAULT_INDICES_OPTIONS);
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, TEMPLATE_FIELD)) {
template = Script.parse(parser);
} else {
throw new ElasticsearchParseException("could not read search request. unexpected object field [" +
currentFieldName + "]");
}
} else if (token == XContentParser.Token.VALUE_STRING) {
if (ParseFieldMatcher.STRICT.match(currentFieldName, INDICES_FIELD)) {
String indicesStr = parser.text();
searchRequest.indices(Strings.delimitedListToStringArray(indicesStr, ",", " \t"));
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, TYPES_FIELD)) {
String typesStr = parser.text();
searchRequest.types(Strings.delimitedListToStringArray(typesStr, ",", " \t"));
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, SEARCH_TYPE_FIELD)) {
searchType = SearchType.fromString(parser.text().toLowerCase(Locale.ROOT), ParseFieldMatcher.EMPTY);
} else {
throw new ElasticsearchParseException("could not read search request. unexpected string field [" +
currentFieldName + "]");
}
} else {
throw new ElasticsearchParseException("could not read search request. unexpected token [" + token + "]");
}
}
if (searchRequest.indices() == null) {
searchRequest.indices(Strings.EMPTY_ARRAY);
}
searchRequest.searchType(searchType);
searchRequest.indicesOptions(indicesOptions);
return new WatcherSearchTemplateRequest(searchRequest, template);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
WatcherSearchTemplateRequest that = (WatcherSearchTemplateRequest) o;
if (!SearchRequestEquivalence.INSTANCE.equivalent(request, that.request)) return false;
return template != null ? template.equals(that.template) : that.template == null;
}
@Override
public int hashCode() {
int result = request != null ? request.hashCode() : 0;
result = 31 * result + (template != null ? template.hashCode() : 0);
return result;
}
static final ParseField INDICES_FIELD = new ParseField("indices");
static final ParseField TYPES_FIELD = new ParseField("types");
static final ParseField BODY_FIELD = new ParseField("body");
static final ParseField SEARCH_TYPE_FIELD = new ParseField("search_type");
static final ParseField INDICES_OPTIONS_FIELD = new ParseField("indices_options");
static final ParseField EXPAND_WILDCARDS_FIELD = new ParseField("expand_wildcards");
static final ParseField IGNORE_UNAVAILABLE_FIELD = new ParseField("ignore_unavailable");
static final ParseField ALLOW_NO_INDICES_FIELD = new ParseField("allow_no_indices");
static final ParseField TEMPLATE_FIELD = new ParseField("template");
public final static IndicesOptions DEFAULT_INDICES_OPTIONS = IndicesOptions.lenientExpandOpen();
}

View File

@ -0,0 +1,125 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.watcher.support.search;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.common.ParseFieldMatcher;
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.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.suggest.Suggesters;
import org.elasticsearch.xpack.common.ScriptServiceProxy;
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.watcher.support.Script;
import org.elasticsearch.xpack.watcher.support.Variables;
import org.elasticsearch.xpack.watcher.watch.Payload;
import java.io.IOException;
import java.util.Map;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
/**
* {@link WatcherSearchTemplateService} renders {@link WatcherSearchTemplateRequest} before their execution.
*/
public class WatcherSearchTemplateService extends AbstractComponent {
private static final String DEFAULT_LANG = "mustache";
private final ScriptServiceProxy scriptService;
private final ParseFieldMatcher parseFieldMatcher;
private final IndicesQueriesRegistry queryRegistry;
private final AggregatorParsers aggsParsers;
private final Suggesters suggesters;
@Inject
public WatcherSearchTemplateService(Settings settings, ScriptServiceProxy scriptServiceProxy,
IndicesQueriesRegistry queryRegistry, AggregatorParsers aggregatorParsers, Suggesters suggesters) {
super(settings);
this.scriptService = scriptServiceProxy;
this.queryRegistry = queryRegistry;
this.aggsParsers = aggregatorParsers;
this.suggesters = suggesters;
this.parseFieldMatcher = new ParseFieldMatcher(settings);
}
public SearchRequest createSearchRequestFromPrototype(WatcherSearchTemplateRequest prototype, WatchExecutionContext ctx,
Payload payload) throws IOException {
SearchRequest request = new SearchRequest()
.indicesOptions(prototype.getRequest().indicesOptions())
.searchType(prototype.getRequest().searchType())
.indices(prototype.getRequest().indices())
.types(prototype.getRequest().types());
Script template = null;
// Due the inconsistency with templates in ES 1.x, we maintain our own template format.
// This template format we use now, will become the template structure in ES 2.0
Map<String, Object> watcherContextParams = Variables.createCtxModel(ctx, payload);
// Here we convert a watch search request body into an inline search template,
// this way if any Watcher related context variables are used, they will get resolved.
if (prototype.getRequest().source() != null) {
try (XContentBuilder builder = jsonBuilder()) {
prototype.getRequest().source().toXContent(builder, ToXContent.EMPTY_PARAMS);
template = Script.inline(builder.string()).lang(DEFAULT_LANG).params(watcherContextParams).build();
}
} else if (prototype.getTemplate() != null) {
// Here we convert watcher template into a ES core templates. Due to the different format we use, we
// convert to the template format used in ES core
Script templatePrototype = prototype.getTemplate();
if (templatePrototype.params() != null) {
watcherContextParams.putAll(templatePrototype.params());
}
Script.Builder builder;
if (templatePrototype.type() == ScriptService.ScriptType.INLINE) {
builder = Script.inline(templatePrototype.script());
} else if (templatePrototype.type() == ScriptService.ScriptType.FILE) {
builder = Script.file(templatePrototype.script());
} else if (templatePrototype.type() == ScriptService.ScriptType.STORED) {
builder = Script.indexed(templatePrototype.script());
} else {
builder = Script.defaultType(templatePrototype.script());
}
template = builder.lang(templatePrototype.lang()).params(watcherContextParams).build();
}
request.source(convert(template));
return request;
}
/**
* Converts a {@link Script} to a {@link org.elasticsearch.search.builder.SearchSourceBuilder}
*/
private SearchSourceBuilder convert(Script template) throws IOException {
SearchSourceBuilder sourceBuilder = SearchSourceBuilder.searchSource();
if (template == null) {
// falling back to an empty body
return sourceBuilder;
}
BytesReference source = (BytesReference) scriptService.executable(scriptService.compile(template), template.params()).run();
if (source != null && source.length() > 0) {
try (XContentParser parser = XContentFactory.xContent(source).createParser(source)) {
sourceBuilder.parseXContent(new QueryParseContext(queryRegistry, parser, parseFieldMatcher), aggsParsers, suggesters);
}
}
return sourceBuilder;
}
}

View File

@ -6,8 +6,8 @@
package org.elasticsearch.xpack.watcher.transform;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.xpack.watcher.support.Script;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.transform.chain.ChainTransform;
import org.elasticsearch.xpack.watcher.transform.script.ScriptTransform;
import org.elasticsearch.xpack.watcher.transform.search.SearchTransform;
@ -20,12 +20,12 @@ public final class TransformBuilders {
private TransformBuilders() {
}
public static SearchTransform.Builder searchTransform(SearchRequest request) {
public static SearchTransform.Builder searchTransform(WatcherSearchTemplateRequest request) {
return SearchTransform.builder(request);
}
public static SearchTransform.Builder searchTransform(SearchRequestBuilder request) {
return searchTransform(request.request());
public static SearchTransform.Builder searchTransform(SearchRequest request) {
return searchTransform(new WatcherSearchTemplateRequest(request));
}
public static ScriptTransform.Builder scriptTransform(String script) {

View File

@ -12,8 +12,8 @@ import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.watcher.support.WatcherUtils;
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import org.elasticsearch.xpack.watcher.transform.ExecutableTransform;
import org.elasticsearch.xpack.watcher.watch.Payload;
@ -25,12 +25,14 @@ public class ExecutableSearchTransform extends ExecutableTransform<SearchTransfo
public static final SearchType DEFAULT_SEARCH_TYPE = SearchType.QUERY_THEN_FETCH;
protected final WatcherClientProxy client;
private final WatcherSearchTemplateService searchTemplateService;
protected final @Nullable TimeValue timeout;
public ExecutableSearchTransform(SearchTransform transform, ESLogger logger, WatcherClientProxy client,
@Nullable TimeValue defaultTimeout) {
WatcherSearchTemplateService searchTemplateService, @Nullable TimeValue defaultTimeout) {
super(transform, logger);
this.client = client;
this.searchTemplateService = searchTemplateService;
this.timeout = transform.getTimeout() != null ? transform.getTimeout() : defaultTimeout;
}
@ -38,7 +40,7 @@ public class ExecutableSearchTransform extends ExecutableTransform<SearchTransfo
public SearchTransform.Result execute(WatchExecutionContext ctx, Payload payload) {
SearchRequest request = null;
try {
request = WatcherUtils.createSearchRequestFromPrototype(transform.getRequest(), ctx, payload);
request = searchTemplateService.createSearchRequestFromPrototype(transform.getRequest(), ctx, payload);
SearchResponse resp = client.search(request, timeout);
return new SearchTransform.Result(request, new Payload.XContent(resp));
} catch (Exception e) {

View File

@ -16,9 +16,8 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.suggest.Suggesters;
import org.elasticsearch.xpack.watcher.support.SearchRequestEquivalence;
import org.elasticsearch.xpack.watcher.support.WatcherDateTimeUtils;
import org.elasticsearch.xpack.watcher.support.WatcherUtils;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.transform.Transform;
import org.elasticsearch.xpack.watcher.watch.Payload;
import org.joda.time.DateTimeZone;
@ -32,11 +31,11 @@ public class SearchTransform implements Transform {
public static final String TYPE = "search";
private final SearchRequest request;
private final WatcherSearchTemplateRequest request;
private final @Nullable TimeValue timeout;
private final @Nullable DateTimeZone dynamicNameTimeZone;
public SearchTransform(SearchRequest request, @Nullable TimeValue timeout, @Nullable DateTimeZone dynamicNameTimeZone) {
public SearchTransform(WatcherSearchTemplateRequest request, @Nullable TimeValue timeout, @Nullable DateTimeZone dynamicNameTimeZone) {
this.request = request;
this.timeout = timeout;
this.dynamicNameTimeZone = dynamicNameTimeZone;
@ -47,7 +46,7 @@ public class SearchTransform implements Transform {
return TYPE;
}
public SearchRequest getRequest() {
public WatcherSearchTemplateRequest getRequest() {
return request;
}
@ -66,14 +65,14 @@ public class SearchTransform implements Transform {
SearchTransform that = (SearchTransform) o;
if (!SearchRequestEquivalence.INSTANCE.equivalent(request, this.request)) return false;
if (request != null ? !request.equals(that.request) : that.request != null) return false;
if (timeout != null ? !timeout.equals(that.timeout) : that.timeout != null) return false;
return !(dynamicNameTimeZone != null ? !dynamicNameTimeZone.equals(that.dynamicNameTimeZone) : that.dynamicNameTimeZone != null);
}
@Override
public int hashCode() {
int result = request.hashCode();
int result = request != null ? request.hashCode() : 0;
result = 31 * result + (timeout != null ? timeout.hashCode() : 0);
result = 31 * result + (dynamicNameTimeZone != null ? dynamicNameTimeZone.hashCode() : 0);
return result;
@ -82,8 +81,9 @@ public class SearchTransform implements Transform {
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(Field.REQUEST.getPreferredName());
builder = WatcherUtils.writeSearchRequest(request, builder, params);
if (request != null) {
builder.field(Field.REQUEST.getPreferredName(), request);
}
if (timeout != null) {
builder.field(Field.TIMEOUT.getPreferredName(), timeout);
}
@ -97,7 +97,7 @@ public class SearchTransform implements Transform {
public static SearchTransform parse(String watchId, XContentParser parser, QueryParseContext context,
AggregatorParsers aggParsers, Suggesters suggesters)
throws IOException {
SearchRequest request = null;
WatcherSearchTemplateRequest request = null;
TimeValue timeout = null;
DateTimeZone dynamicNameTimeZone = null;
@ -108,7 +108,7 @@ public class SearchTransform implements Transform {
currentFieldName = parser.currentName();
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, Field.REQUEST)) {
try {
request = WatcherUtils.readSearchRequest(parser, ExecutableSearchTransform.DEFAULT_SEARCH_TYPE, context,
request = WatcherSearchTemplateRequest.fromXContent(parser, ExecutableSearchTransform.DEFAULT_SEARCH_TYPE, context,
aggParsers, suggesters);
} catch (ElasticsearchParseException srpe) {
throw new ElasticsearchParseException("could not parse [{}] transform for watch [{}]. failed to parse [{}]", srpe,
@ -136,7 +136,7 @@ public class SearchTransform implements Transform {
return new SearchTransform(request, timeout, dynamicNameTimeZone);
}
public static Builder builder(SearchRequest request) {
public static Builder builder(WatcherSearchTemplateRequest request) {
return new Builder(request);
}
@ -162,8 +162,7 @@ public class SearchTransform implements Transform {
protected XContentBuilder typeXContent(XContentBuilder builder, Params params) throws IOException {
if (request != null) {
builder.startObject(type);
builder.field(Field.REQUEST.getPreferredName());
WatcherUtils.writeSearchRequest(request, builder, params);
builder.field(Field.REQUEST.getPreferredName(), new WatcherSearchTemplateRequest(request));
builder.endObject();
}
return builder;
@ -172,11 +171,11 @@ public class SearchTransform implements Transform {
public static class Builder implements Transform.Builder<SearchTransform> {
private final SearchRequest request;
private final WatcherSearchTemplateRequest request;
private TimeValue timeout;
private DateTimeZone dynamicNameTimeZone;
public Builder(SearchRequest request) {
public Builder(WatcherSearchTemplateRequest request) {
this.request = request;
}

View File

@ -18,7 +18,9 @@ import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.suggest.Suggesters;
import org.elasticsearch.xpack.security.InternalClient;
import org.elasticsearch.xpack.common.ScriptServiceProxy;
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import org.elasticsearch.xpack.watcher.transform.TransformFactory;
/**
@ -32,14 +34,15 @@ public class SearchTransformFactory extends TransformFactory<SearchTransform, Se
private final AggregatorParsers aggParsers;
private final Suggesters suggesters;
private final ParseFieldMatcher parseFieldMatcher;
private final WatcherSearchTemplateService searchTemplateService;
@Inject
public SearchTransformFactory(Settings settings, InternalClient client, IndicesQueriesRegistry queryRegistry,
AggregatorParsers aggParsers, Suggesters suggesters) {
this(settings, new WatcherClientProxy(settings, client), queryRegistry, aggParsers, suggesters);
AggregatorParsers aggParsers, Suggesters suggesters, ScriptServiceProxy scriptService) {
this(settings, new WatcherClientProxy(settings, client), queryRegistry, aggParsers, suggesters, scriptService);
}
public SearchTransformFactory(Settings settings, WatcherClientProxy client, IndicesQueriesRegistry queryRegistry,
AggregatorParsers aggParsers, Suggesters suggesters) {
AggregatorParsers aggParsers, Suggesters suggesters, ScriptServiceProxy scriptService) {
super(Loggers.getLogger(ExecutableSearchTransform.class, settings));
this.client = client;
this.parseFieldMatcher = new ParseFieldMatcher(settings);
@ -47,6 +50,7 @@ public class SearchTransformFactory extends TransformFactory<SearchTransform, Se
this.aggParsers = aggParsers;
this.suggesters = suggesters;
this.defaultTimeout = settings.getAsTime("xpack.watcher.transform.search.default_timeout", null);
this.searchTemplateService = new WatcherSearchTemplateService(settings, scriptService, queryRegistry, aggParsers, suggesters);
}
@Override
@ -62,6 +66,6 @@ public class SearchTransformFactory extends TransformFactory<SearchTransform, Se
@Override
public ExecutableSearchTransform createExecutable(SearchTransform transform) {
return new ExecutableSearchTransform(transform, transformLogger, client, defaultTimeout);
return new ExecutableSearchTransform(transform, transformLogger, client, searchTemplateService, defaultTimeout);
}
}

View File

@ -38,7 +38,7 @@ public class HistoryIntegrationTests extends AbstractWatcherIntegrationTestCase
SearchRequestBuilder searchRequestBuilder = client().prepareSearch("foo").addSort(SortBuilders.fieldSort("inner.date").order
(SortOrder.DESC));
builder.input(InputBuilders.chainInput().add("first", InputBuilders.searchInput(searchRequestBuilder)));
builder.input(InputBuilders.chainInput().add("first", InputBuilders.searchInput(searchRequestBuilder.request())));
PutWatchResponse response = watcherClient().preparePutWatch("test_watch").setSource(builder).get();
assertThat(response.isCreated(), is(true));

View File

@ -31,6 +31,7 @@ import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
import org.elasticsearch.xpack.common.secret.SecretService;
import org.elasticsearch.xpack.common.text.TextTemplate;
import org.elasticsearch.xpack.common.text.TextTemplateEngine;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
import org.elasticsearch.xpack.watcher.test.MockTextTemplateEngine;
import org.elasticsearch.xpack.watcher.test.WatcherTestUtils;
@ -238,7 +239,7 @@ public class WebhookActionTests extends ESTestCase {
ExecutableWebhookAction executable = new ExecutableWebhookAction(action, logger, httpClient, templateEngine);
String watchId = "test_url_encode" + randomAsciiOfLength(10);
Watch watch = createWatch(watchId, mock(WatcherClientProxy.class), "account1");
Watch watch = createWatch(watchId, "account1");
WatchExecutionContext ctx = new TriggeredExecutionContext(watch, new DateTime(UTC),
new ScheduleTriggerEvent(watchId, new DateTime(UTC), new DateTime(UTC)), timeValueSeconds(5));
executable.execute("_id", ctx, new Payload.Simple());
@ -264,16 +265,16 @@ public class WebhookActionTests extends ESTestCase {
ExecutableWebhookAction executable = new ExecutableWebhookAction(action, logger, client, templateEngine);
Watch watch = createWatch(watchId, mock(WatcherClientProxy.class), "account1");
Watch watch = createWatch(watchId, "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, WatcherClientProxy client, final String account) throws AddressException, IOException {
private Watch createWatch(String watchId, final String account) throws AddressException, IOException {
return WatcherTestUtils.createTestWatch(watchId,
client,
mock(WatcherClientProxy.class),
ExecuteScenario.Success.client(),
new AbstractWatcherIntegrationTestCase.NoopEmailService() {
@Override
@ -286,6 +287,7 @@ public class WebhookActionTests extends ESTestCase {
return new EmailSent(account, email);
}
},
mock(WatcherSearchTemplateService.class),
logger);
};

View File

@ -8,6 +8,7 @@ package org.elasticsearch.xpack.watcher.execution;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
import org.elasticsearch.xpack.watcher.test.WatcherTestUtils;
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTriggerEvent;
@ -21,7 +22,8 @@ import static org.hamcrest.Matchers.equalTo;
*/
public class TriggeredWatchTests extends AbstractWatcherIntegrationTestCase {
public void testParser() throws Exception {
Watch watch = WatcherTestUtils.createTestWatch("fired_test", watcherHttpClient(), noopEmailService(), logger);
Watch watch = WatcherTestUtils.createTestWatch("fired_test", watcherHttpClient(), noopEmailService(),
watcherSearchTemplateService(), 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);
@ -38,4 +40,8 @@ public class TriggeredWatchTests extends AbstractWatcherIntegrationTestCase {
private TriggeredWatch.Parser triggeredWatchParser() {
return internalCluster().getInstance(TriggeredWatch.Parser.class);
}
protected WatcherSearchTemplateService watcherSearchTemplateService() {
return internalCluster().getInstance(WatcherSearchTemplateService.class);
}
}

View File

@ -6,23 +6,6 @@
package org.elasticsearch.xpack.watcher.support;
import static java.util.Collections.singletonMap;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.xpack.watcher.support.WatcherDateTimeUtils.formatDate;
import static org.elasticsearch.xpack.watcher.support.WatcherUtils.DEFAULT_INDICES_OPTIONS;
import static org.elasticsearch.xpack.watcher.support.WatcherUtils.flattenModel;
import static org.elasticsearch.xpack.watcher.test.WatcherTestUtils.getRandomSupportedSearchType;
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.is;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.support.IndicesOptions;
@ -40,15 +23,30 @@ import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.index.query.QueryParser;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.Template;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.watcher.input.search.ExecutableSearchInput;
import org.elasticsearch.xpack.support.clock.SystemClock;
import org.elasticsearch.xpack.common.text.TextTemplate;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.joda.time.DateTime;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import static java.util.Collections.singletonMap;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.xpack.watcher.input.search.ExecutableSearchInput.DEFAULT_SEARCH_TYPE;
import static org.elasticsearch.xpack.watcher.support.WatcherDateTimeUtils.formatDate;
import static org.elasticsearch.xpack.watcher.support.WatcherUtils.flattenModel;
import static org.elasticsearch.xpack.watcher.test.WatcherTestUtils.getRandomSupportedSearchType;
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
/**
*
*/
@ -100,6 +98,7 @@ public class WatcherUtilsTests extends ESTestCase {
public void testSerializeSearchRequest() throws Exception {
String[] randomIndices = generateRandomStringArray(5, 5, false);
SearchRequest expectedRequest = new SearchRequest(randomIndices);
Script expectedTemplate = null;
if (randomBoolean()) {
String[] randomTypes = generateRandomStringArray(2, 5, false);
@ -107,7 +106,7 @@ public class WatcherUtilsTests extends ESTestCase {
}
expectedRequest.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), randomBoolean(),
randomBoolean(), WatcherUtils.DEFAULT_INDICES_OPTIONS));
randomBoolean(), WatcherSearchTemplateRequest.DEFAULT_INDICES_OPTIONS));
expectedRequest.searchType(getRandomSupportedSearchType());
SearchSourceBuilder searchSourceBuilder = SearchSourceBuilder.searchSource().query(QueryBuilders.matchAllQuery()).size(11);
@ -122,30 +121,29 @@ public class WatcherUtilsTests extends ESTestCase {
}
}
String text = randomAsciiOfLengthBetween(1, 5);
Template template = randomFrom(
new Template(text, ScriptType.INLINE, null, null, params),
new Template(text, ScriptType.FILE, null, null, params),
new Template(text, ScriptType.STORED, null, null, params)
);
expectedRequest.template(template);
expectedTemplate = randomFrom(Script.inline(text), Script.file(text), Script.indexed(text)).params(params).build();
}
WatcherSearchTemplateRequest request = new WatcherSearchTemplateRequest(expectedRequest, expectedTemplate);
XContentBuilder builder = jsonBuilder();
builder = WatcherUtils.writeSearchRequest(expectedRequest, builder, ToXContent.EMPTY_PARAMS);
request.toXContent(builder, ToXContent.EMPTY_PARAMS);
XContentParser parser = XContentHelper.createParser(builder.bytes());
assertThat(parser.nextToken(), equalTo(XContentParser.Token.START_OBJECT));
IndicesQueriesRegistry registry = new IndicesQueriesRegistry();
QueryParser<MatchAllQueryBuilder> queryParser = MatchAllQueryBuilder::fromXContent;
registry.register(queryParser, MatchAllQueryBuilder.QUERY_NAME_FIELD);
QueryParseContext context = new QueryParseContext(registry, parser, ParseFieldMatcher.STRICT);
SearchRequest result = WatcherUtils.readSearchRequest(parser, ExecutableSearchInput.DEFAULT_SEARCH_TYPE, context, null, null);
WatcherSearchTemplateRequest result = WatcherSearchTemplateRequest.fromXContent(parser, DEFAULT_SEARCH_TYPE, context, null, null);
assertThat(result.indices(), arrayContainingInAnyOrder(expectedRequest.indices()));
assertThat(result.types(), arrayContainingInAnyOrder(expectedRequest.types()));
assertThat(result.indicesOptions(), equalTo(expectedRequest.indicesOptions()));
assertThat(result.searchType(), equalTo(expectedRequest.searchType()));
assertThat(result.source(), equalTo(searchSourceBuilder));
assertThat(result.template(), equalTo(expectedRequest.template()));
assertThat(result.getRequest(), is(notNullValue()));
assertThat(result.getRequest().indices(), arrayContainingInAnyOrder(expectedRequest.indices()));
assertThat(result.getRequest().types(), arrayContainingInAnyOrder(expectedRequest.types()));
assertThat(result.getRequest().indicesOptions(), equalTo(expectedRequest.indicesOptions()));
assertThat(result.getRequest().searchType(), equalTo(expectedRequest.searchType()));
assertThat(result.getRequest().source(), equalTo(searchSourceBuilder));
assertThat(result.getTemplate(), equalTo(expectedTemplate));
}
public void testDeserializeSearchRequest() throws Exception {
@ -172,10 +170,10 @@ public class WatcherUtilsTests extends ESTestCase {
}
}
IndicesOptions indicesOptions = DEFAULT_INDICES_OPTIONS;
IndicesOptions indicesOptions = WatcherSearchTemplateRequest.DEFAULT_INDICES_OPTIONS;
if (randomBoolean()) {
indicesOptions = IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), randomBoolean(),
randomBoolean(), WatcherUtils.DEFAULT_INDICES_OPTIONS);
randomBoolean(), WatcherSearchTemplateRequest.DEFAULT_INDICES_OPTIONS);
builder.startObject("indices_options")
.field("allow_no_indices", indicesOptions.allowNoIndices())
.field("expand_wildcards", indicesOptions.expandWildcardsClosed() && indicesOptions.expandWildcardsOpen() ? "all" :
@ -201,7 +199,7 @@ public class WatcherUtilsTests extends ESTestCase {
source = searchSourceBuilder.buildAsBytes(XContentType.JSON);
builder.rawField("body", source);
}
Template templateSource = null;
Script template = null;
if (randomBoolean()) {
Map<String, Object> params = new HashMap<>();
if (randomBoolean()) {
@ -211,14 +209,8 @@ public class WatcherUtilsTests extends ESTestCase {
}
}
String text = randomAsciiOfLengthBetween(1, 5);
TextTemplate template = randomFrom(
TextTemplate.inline(text).params(params).build(),
TextTemplate.file(text).params(params).build(),
TextTemplate.indexed(text).params(params).build()
);
template = randomFrom(Script.inline(text), Script.file(text), Script.indexed(text)) .params(params).build();
builder.field("template", template);
templateSource = new Template(template.getTemplate(), template.getType(), null, template.getContentType(),
template.getParams());
}
builder.endObject();
@ -228,14 +220,15 @@ public class WatcherUtilsTests extends ESTestCase {
QueryParser<MatchAllQueryBuilder> queryParser = MatchAllQueryBuilder::fromXContent;
registry.register(queryParser, MatchAllQueryBuilder.QUERY_NAME_FIELD);
QueryParseContext context = new QueryParseContext(registry, parser, ParseFieldMatcher.STRICT);
SearchRequest result = WatcherUtils.readSearchRequest(parser, ExecutableSearchInput.DEFAULT_SEARCH_TYPE, context, null, null);
WatcherSearchTemplateRequest result = WatcherSearchTemplateRequest.fromXContent(parser, DEFAULT_SEARCH_TYPE, context, null, null);
assertThat(result.indices(), arrayContainingInAnyOrder(indices));
assertThat(result.types(), arrayContainingInAnyOrder(types));
assertThat(result.indicesOptions(), equalTo(indicesOptions));
assertThat(result.searchType(), equalTo(searchType));
assertThat(result.source(), equalTo(searchSourceBuilder));
assertThat(result.template(), equalTo(templateSource));
assertThat(result.getRequest(), is(notNullValue()));
assertThat(result.getRequest().indices(), arrayContainingInAnyOrder(indices));
assertThat(result.getRequest().types(), arrayContainingInAnyOrder(types));
assertThat(result.getRequest().indicesOptions(), equalTo(indicesOptions));
assertThat(result.getRequest().searchType(), equalTo(searchType));
assertThat(result.getRequest().source(), equalTo(searchSourceBuilder));
assertThat(result.getTemplate(), equalTo(template));
}
}

View File

@ -31,6 +31,13 @@ import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.common.ScriptServiceProxy;
import org.elasticsearch.xpack.common.http.HttpClient;
import org.elasticsearch.xpack.common.http.HttpMethod;
import org.elasticsearch.xpack.common.http.HttpRequestTemplate;
import org.elasticsearch.xpack.common.secret.Secret;
import org.elasticsearch.xpack.common.text.TextTemplate;
import org.elasticsearch.xpack.common.text.TextTemplateEngine;
import org.elasticsearch.xpack.notification.email.Authentication;
import org.elasticsearch.xpack.notification.email.EmailService;
import org.elasticsearch.xpack.notification.email.EmailTemplate;
@ -49,15 +56,9 @@ import org.elasticsearch.xpack.watcher.execution.Wid;
import org.elasticsearch.xpack.watcher.input.search.ExecutableSearchInput;
import org.elasticsearch.xpack.watcher.input.simple.ExecutableSimpleInput;
import org.elasticsearch.xpack.watcher.input.simple.SimpleInput;
import org.elasticsearch.xpack.common.ScriptServiceProxy;
import org.elasticsearch.xpack.watcher.support.WatcherUtils;
import org.elasticsearch.xpack.common.http.HttpClient;
import org.elasticsearch.xpack.common.http.HttpMethod;
import org.elasticsearch.xpack.common.http.HttpRequestTemplate;
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
import org.elasticsearch.xpack.common.secret.Secret;
import org.elasticsearch.xpack.common.text.TextTemplate;
import org.elasticsearch.xpack.common.text.TextTemplateEngine;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import org.elasticsearch.xpack.watcher.support.xcontent.ObjectPath;
import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource;
import org.elasticsearch.xpack.watcher.transform.search.ExecutableSearchTransform;
@ -76,7 +77,6 @@ import javax.mail.internet.AddressException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
@ -122,7 +122,7 @@ public final class WatcherTestUtils {
public static SearchRequest newInputSearchRequest(String... indices) {
SearchRequest request = new SearchRequest();
request.indices(indices);
request.indicesOptions(WatcherUtils.DEFAULT_INDICES_OPTIONS);
request.indicesOptions(WatcherSearchTemplateRequest.DEFAULT_INDICES_OPTIONS);
request.searchType(ExecutableSearchInput.DEFAULT_SEARCH_TYPE);
return request;
}
@ -175,13 +175,14 @@ public final class WatcherTestUtils {
public static Watch createTestWatch(String watchName, HttpClient httpClient, EmailService emailService,
ESLogger logger) throws AddressException {
return createTestWatch(watchName, WatcherClientProxy.of(ESIntegTestCase.client()), httpClient, emailService, logger);
WatcherSearchTemplateService searchTemplateService, ESLogger logger) throws AddressException {
WatcherClientProxy client = WatcherClientProxy.of(ESIntegTestCase.client());
return createTestWatch(watchName, client, httpClient, emailService, searchTemplateService, logger);
}
public static Watch createTestWatch(String watchName, WatcherClientProxy client, HttpClient httpClient, EmailService emailService,
ESLogger logger) throws AddressException {
WatcherSearchTemplateService searchTemplateService, ESLogger logger) throws AddressException {
SearchRequest conditionRequest = newInputSearchRequest("my-condition-index").source(searchSource().query(matchAllQuery()));
SearchRequest transformRequest = newInputSearchRequest("my-payload-index").source(searchSource().query(matchAllQuery()));
@ -229,12 +230,15 @@ public final class WatcherTestUtils {
Map<String, ActionStatus> statuses = new HashMap<>();
statuses.put("_webhook", new ActionStatus(now));
statuses.put("_email", new ActionStatus(now));
SearchTransform searchTransform = new SearchTransform(new WatcherSearchTemplateRequest(transformRequest), null, null);
return new Watch(
watchName,
new ScheduleTrigger(new CronSchedule("0/5 * * * * ? *")),
new ExecutableSimpleInput(new SimpleInput(new Payload.Simple(inputData)), logger),
new ExecutableAlwaysCondition(logger),
new ExecutableSearchTransform(new SearchTransform(transformRequest, null, null), logger, client, null),
new ExecutableSearchTransform(searchTransform, logger, client, searchTemplateService, null),
new TimeValue(0),
new ExecutableActions(actions),
metadata,
@ -247,7 +251,7 @@ public final class WatcherTestUtils {
.put("script.indexed", "true")
.put("path.home", createTempDir())
.build();
ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Arrays.asList(ScriptServiceProxy.INSTANCE));
ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Collections.singletonList(ScriptServiceProxy.INSTANCE));
ScriptEngineRegistry scriptEngineRegistry =
new ScriptEngineRegistry(Collections.emptyList());

View File

@ -7,20 +7,16 @@ package org.elasticsearch.xpack.watcher.test.integration;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
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.script.ScriptService.ScriptType;
import org.elasticsearch.script.Template;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.xpack.support.clock.SystemClock;
import org.elasticsearch.xpack.watcher.client.WatchSourceBuilder;
import org.elasticsearch.xpack.watcher.client.WatcherClient;
import org.elasticsearch.xpack.watcher.condition.compare.CompareCondition;
import org.elasticsearch.xpack.watcher.support.WatcherUtils;
import org.elasticsearch.xpack.support.clock.SystemClock;
import org.elasticsearch.xpack.watcher.support.Script;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource;
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
import org.elasticsearch.xpack.watcher.transport.actions.delete.DeleteWatchResponse;
@ -151,8 +147,8 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
watchSource.field("unknown_field", "x");
watchSource.startObject("schedule").field("cron", "0/5 * * * * ? *").endObject();
watchSource.startObject("condition").startObject("script").field("script", "return true").field("request");
WatcherUtils.writeSearchRequest(newInputSearchRequest(), watchSource, ToXContent.EMPTY_PARAMS);
watchSource.startObject("condition").startObject("script").field("script", "return true");
watchSource.field("request", new WatcherSearchTemplateRequest(newInputSearchRequest()));
watchSource.endObject().endObject();
watchSource.endObject();
@ -252,22 +248,20 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
public void testConditionSearchWithSource() throws Exception {
SearchSourceBuilder searchSourceBuilder = searchSource().query(matchQuery("level", "a"));
testConditionSearch(newInputSearchRequest("events").source(searchSourceBuilder));
testConditionSearch(newInputSearchRequest("events").source(searchSourceBuilder), null);
}
public void testConditionSearchWithIndexedTemplate() throws Exception {
SearchSourceBuilder searchSourceBuilder = searchSource().query(matchQuery("level", "a"));
client().admin().cluster().preparePutStoredScript()
assertAcked(client().admin().cluster().preparePutStoredScript()
.setScriptLang("mustache")
.setId("my-template")
.setSource(jsonBuilder().startObject().field("template").value(searchSourceBuilder).endObject().bytes())
.get();
.get());
Template template = new Template("my-template", ScriptType.STORED, null, null, null);
Script template = Script.indexed("my-template").lang("mustache").build();
SearchRequest searchRequest = newInputSearchRequest("events");
// TODO (2.0 upgrade): move back to BytesReference instead of coverting to a string
searchRequest.template(template);
testConditionSearch(searchRequest);
testConditionSearch(searchRequest, template);
}
public void testInputFiltering() throws Exception {
@ -298,12 +292,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
// Check that the input result payload has been filtered
refresh();
SearchResponse searchResponse = searchWatchRecords(new Callback<SearchRequestBuilder>() {
@Override
public void handle(SearchRequestBuilder builder) {
builder.setQuery(matchQuery("watch_id", "_name1"));
}
});
SearchResponse searchResponse = searchWatchRecords(builder -> builder.setQuery(matchQuery("watch_id", "_name1")));
assertHitCount(searchResponse, 1);
XContentSource source = xContentSource(searchResponse.getHits().getAt(0).getSourceRef());
assertThat(source.getValue("result.input.payload.hits.total"), equalTo((Object) 1));
@ -379,7 +368,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
}
}
private void testConditionSearch(SearchRequest request) throws Exception {
private void testConditionSearch(SearchRequest request, Script template) throws Exception {
// reset, so we don't miss event docs when we filter over the _timestamp field.
timeWarp().clock().setTime(SystemClock.INSTANCE.nowUTC());
@ -389,7 +378,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
watcherClient().preparePutWatch(watchName)
.setSource(watchBuilder()
.trigger(schedule(interval("5s")))
.input(searchInput(request))
.input(searchInput(new WatcherSearchTemplateRequest(request, template)))
.condition(compareCondition("ctx.payload.hits.total", CompareCondition.Op.GTE, 3L)))
.get();

View File

@ -5,14 +5,6 @@
*/
package org.elasticsearch.xpack.watcher.watch;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.logging.ESLogger;
@ -25,12 +17,31 @@ import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryParser;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.common.ScriptServiceProxy;
import org.elasticsearch.xpack.common.http.HttpClient;
import org.elasticsearch.xpack.common.http.HttpMethod;
import org.elasticsearch.xpack.common.http.HttpRequestTemplate;
import org.elasticsearch.xpack.common.http.auth.HttpAuthRegistry;
import org.elasticsearch.xpack.common.http.auth.basic.BasicAuthFactory;
import org.elasticsearch.xpack.common.secret.SecretService;
import org.elasticsearch.xpack.common.text.TextTemplate;
import org.elasticsearch.xpack.common.text.TextTemplateEngine;
import org.elasticsearch.xpack.notification.email.DataAttachment;
import org.elasticsearch.xpack.notification.email.EmailService;
import org.elasticsearch.xpack.notification.email.EmailTemplate;
import org.elasticsearch.xpack.notification.email.HtmlSanitizer;
import org.elasticsearch.xpack.notification.email.Profile;
import org.elasticsearch.xpack.notification.email.attachment.EmailAttachments;
import org.elasticsearch.xpack.notification.email.attachment.EmailAttachmentsParser;
import org.elasticsearch.xpack.support.clock.Clock;
import org.elasticsearch.xpack.support.clock.ClockMock;
import org.elasticsearch.xpack.support.clock.SystemClock;
import org.elasticsearch.xpack.watcher.WatcherLicensee;
import org.elasticsearch.xpack.watcher.actions.ActionFactory;
import org.elasticsearch.xpack.watcher.actions.ActionRegistry;
import org.elasticsearch.xpack.watcher.actions.ActionStatus;
import org.elasticsearch.xpack.watcher.actions.ActionWrapper;
import org.elasticsearch.xpack.watcher.actions.ExecutableActions;
import org.elasticsearch.xpack.notification.email.DataAttachment;
import org.elasticsearch.xpack.watcher.actions.email.EmailAction;
import org.elasticsearch.xpack.watcher.actions.email.EmailActionFactory;
import org.elasticsearch.xpack.watcher.actions.email.ExecutableEmailAction;
@ -68,28 +79,15 @@ import org.elasticsearch.xpack.watcher.input.search.SearchInputFactory;
import org.elasticsearch.xpack.watcher.input.simple.ExecutableSimpleInput;
import org.elasticsearch.xpack.watcher.input.simple.SimpleInput;
import org.elasticsearch.xpack.watcher.input.simple.SimpleInputFactory;
import org.elasticsearch.xpack.watcher.WatcherLicensee;
import org.elasticsearch.xpack.watcher.support.Script;
import org.elasticsearch.xpack.watcher.support.WatcherUtils;
import org.elasticsearch.xpack.support.clock.Clock;
import org.elasticsearch.xpack.support.clock.ClockMock;
import org.elasticsearch.xpack.support.clock.SystemClock;
import org.elasticsearch.xpack.common.http.HttpClient;
import org.elasticsearch.xpack.common.http.HttpMethod;
import org.elasticsearch.xpack.common.http.HttpRequestTemplate;
import org.elasticsearch.xpack.common.http.auth.HttpAuthRegistry;
import org.elasticsearch.xpack.common.http.auth.basic.BasicAuthFactory;
import org.elasticsearch.xpack.common.ScriptServiceProxy;
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
import org.elasticsearch.xpack.common.secret.SecretService;
import org.elasticsearch.xpack.common.text.TextTemplate;
import org.elasticsearch.xpack.common.text.TextTemplateEngine;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import org.elasticsearch.xpack.watcher.test.WatcherTestUtils;
import org.elasticsearch.xpack.watcher.transform.ExecutableTransform;
import org.elasticsearch.xpack.watcher.transform.TransformFactory;
import org.elasticsearch.xpack.watcher.transform.TransformRegistry;
import org.elasticsearch.xpack.watcher.transform.chain.ChainTransform;
import org.elasticsearch.xpack.watcher.transform.chain.ChainTransformFactory;
import org.elasticsearch.xpack.watcher.transform.chain.ExecutableChainTransform;
import org.elasticsearch.xpack.watcher.transform.script.ExecutableScriptTransform;
import org.elasticsearch.xpack.watcher.transform.script.ScriptTransform;
@ -116,21 +114,23 @@ import org.elasticsearch.xpack.watcher.trigger.schedule.support.Month;
import org.elasticsearch.xpack.watcher.trigger.schedule.support.MonthTimes;
import org.elasticsearch.xpack.watcher.trigger.schedule.support.WeekTimes;
import org.elasticsearch.xpack.watcher.trigger.schedule.support.YearTimes;
import org.elasticsearch.xpack.notification.email.EmailService;
import org.elasticsearch.xpack.notification.email.EmailTemplate;
import org.elasticsearch.xpack.notification.email.HtmlSanitizer;
import org.elasticsearch.xpack.notification.email.Profile;
import org.elasticsearch.xpack.notification.email.attachment.EmailAttachments;
import org.elasticsearch.xpack.notification.email.attachment.EmailAttachmentsParser;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.Before;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonMap;
import static java.util.Collections.unmodifiableMap;
import static org.elasticsearch.xpack.watcher.input.InputBuilders.searchInput;
import static org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest.DEFAULT_INDICES_OPTIONS;
import static org.elasticsearch.xpack.watcher.test.WatcherTestUtils.matchAllRequest;
import static org.elasticsearch.xpack.watcher.trigger.TriggerBuilders.schedule;
import static org.hamcrest.Matchers.equalTo;
@ -153,6 +153,7 @@ public class WatchTests extends ESTestCase {
private WatcherLicensee watcherLicensee;
private ESLogger logger;
private Settings settings = Settings.EMPTY;
private WatcherSearchTemplateService searchTemplateService;
@Before
public void init() throws Exception {
@ -166,6 +167,7 @@ public class WatchTests extends ESTestCase {
watcherLicensee = mock(WatcherLicensee.class);
authRegistry = new HttpAuthRegistry(singletonMap("basic", new BasicAuthFactory(secretService)));
logger = Loggers.getLogger(WatchTests.class);
searchTemplateService = mock(WatcherSearchTemplateService.class);
}
public void testParserSelfGenerated() throws Exception {
@ -341,7 +343,7 @@ public class WatchTests extends ESTestCase {
switch (type) {
case SearchInput.TYPE:
SearchInput searchInput = searchInput(WatcherTestUtils.newInputSearchRequest("idx")).build();
return new ExecutableSearchInput(searchInput, logger, client, null);
return new ExecutableSearchInput(searchInput, logger, client, searchTemplateService, null);
default:
SimpleInput simpleInput = InputBuilders.simpleInput(singletonMap("_key", "_val")).build();
return new ExecutableSimpleInput(simpleInput, logger);
@ -355,7 +357,7 @@ public class WatchTests extends ESTestCase {
IndicesQueriesRegistry queryRegistry = new IndicesQueriesRegistry();
QueryParser<MatchAllQueryBuilder> queryParser = MatchAllQueryBuilder::fromXContent;
queryRegistry.register(queryParser, MatchAllQueryBuilder.QUERY_NAME_FIELD);
parsers.put(SearchInput.TYPE, new SearchInputFactory(settings, client, queryRegistry, null, null));
parsers.put(SearchInput.TYPE, new SearchInputFactory(settings, client, queryRegistry, null, null, scriptService));
return new InputRegistry(Settings.EMPTY, parsers);
default:
parsers.put(SimpleInput.TYPE, new SimpleInputFactory(settings));
@ -406,15 +408,19 @@ public class WatchTests extends ESTestCase {
case ScriptTransform.TYPE:
return new ExecutableScriptTransform(new ScriptTransform(Script.inline("_script").build()), logger, scriptService);
case SearchTransform.TYPE:
return new ExecutableSearchTransform(new SearchTransform(
matchAllRequest(WatcherUtils.DEFAULT_INDICES_OPTIONS), timeout, timeZone), logger, client, null);
SearchTransform transform = new SearchTransform(
new WatcherSearchTemplateRequest(matchAllRequest(DEFAULT_INDICES_OPTIONS), null), timeout, timeZone);
return new ExecutableSearchTransform(transform, logger, client, searchTemplateService, null);
default: // chain
ChainTransform chainTransform = new ChainTransform(Arrays.asList(
new SearchTransform(matchAllRequest(WatcherUtils.DEFAULT_INDICES_OPTIONS), timeout, timeZone),
new ScriptTransform(Script.inline("_script").build())));
SearchTransform searchTransform = new SearchTransform(
new WatcherSearchTemplateRequest(matchAllRequest(DEFAULT_INDICES_OPTIONS), null), timeout, timeZone);
ScriptTransform scriptTransform = new ScriptTransform(Script.inline("_script").build());
ChainTransform chainTransform = new ChainTransform(Arrays.asList(searchTransform, scriptTransform));
return new ExecutableChainTransform(chainTransform, logger, Arrays.<ExecutableTransform>asList(
new ExecutableSearchTransform(new SearchTransform(
matchAllRequest(WatcherUtils.DEFAULT_INDICES_OPTIONS), timeout, timeZone), logger, client, null),
new WatcherSearchTemplateRequest(matchAllRequest(DEFAULT_INDICES_OPTIONS), null), timeout, timeZone),
logger, client, searchTemplateService, null),
new ExecutableScriptTransform(new ScriptTransform(Script.inline("_script").build()), logger, scriptService)));
}
}
@ -425,7 +431,7 @@ public class WatchTests extends ESTestCase {
queryRegistry.register(queryParser, MatchAllQueryBuilder.QUERY_NAME_FIELD);
Map<String, TransformFactory> factories = new HashMap<>();
factories.put(ScriptTransform.TYPE, new ScriptTransformFactory(settings, scriptService));
factories.put(SearchTransform.TYPE, new SearchTransformFactory(settings, client, queryRegistry, null, null));
factories.put(SearchTransform.TYPE, new SearchTransformFactory(settings, client, queryRegistry, null, null, scriptService));
TransformRegistry registry = new TransformRegistry(Settings.EMPTY, unmodifiableMap(factories));
return registry;
}