diff --git a/src/main/java/org/elasticsearch/alerts/input/search/SearchInput.java b/src/main/java/org/elasticsearch/alerts/input/search/SearchInput.java index 29f4b9df89b..af89103051e 100644 --- a/src/main/java/org/elasticsearch/alerts/input/search/SearchInput.java +++ b/src/main/java/org/elasticsearch/alerts/input/search/SearchInput.java @@ -130,9 +130,8 @@ public class SearchInput extends Input { request.templateParams(templateParams.map()); request.templateName(requestPrototype.templateName()); request.templateType(requestPrototype.templateType()); - } else { - throw new InputException("search requests needs either source or template name"); } + // falling back to an empty body return request; } diff --git a/src/main/java/org/elasticsearch/alerts/support/AlertUtils.java b/src/main/java/org/elasticsearch/alerts/support/AlertUtils.java index 11904070466..c37ad29ffc6 100644 --- a/src/main/java/org/elasticsearch/alerts/support/AlertUtils.java +++ b/src/main/java/org/elasticsearch/alerts/support/AlertUtils.java @@ -11,7 +11,10 @@ import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchType; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.alerts.AlertsException; +import org.elasticsearch.alerts.AlertsSettingsException; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.joda.time.DateTime; +import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentHelper; @@ -19,11 +22,10 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.script.ScriptService; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Map; +import java.lang.reflect.Array; +import java.util.*; +import static org.elasticsearch.alerts.support.AlertsDateUtils.formatDate; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; /** @@ -50,14 +52,13 @@ public final class AlertUtils { public static SearchRequest readSearchRequest(XContentParser parser, SearchType searchType) throws IOException { SearchRequest searchRequest = new SearchRequest(); IndicesOptions indicesOptions = DEFAULT_INDICES_OPTIONS; - XContentParser.Token token; - String searchRequestFieldName = null; + String currentFieldName = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { - searchRequestFieldName = parser.currentName(); + currentFieldName = parser.currentName(); } else if (token == XContentParser.Token.START_ARRAY) { - switch (searchRequestFieldName) { + switch (currentFieldName) { case "indices": List indices = new ArrayList<>(); while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { @@ -70,11 +71,11 @@ public final class AlertUtils { searchRequest.indices(indices.toArray(new String[indices.size()])); break; default: - throw new ElasticsearchIllegalArgumentException("Unexpected field [" + searchRequestFieldName + "]"); + throw new ElasticsearchIllegalArgumentException("Unexpected field [" + currentFieldName + "]"); } } else if (token == XContentParser.Token.START_OBJECT) { XContentBuilder builder; - switch (searchRequestFieldName) { + switch (currentFieldName) { case "body": builder = XContentBuilder.builder(parser.contentType().xContent()); builder.copyCurrentStructure(parser); @@ -125,25 +126,43 @@ public final class AlertUtils { } indicesOptions = IndicesOptions.fromOptions(ignoreUnavailable, allowNoIndices, expandOpen, expandClosed); break; + case "template": + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token == XContentParser.Token.VALUE_STRING) { + switch (currentFieldName) { + case "name": + searchRequest.templateName(parser.textOrNull()); + break; + case "type": + try { + searchRequest.templateType(ScriptService.ScriptType.valueOf(parser.text().toUpperCase(Locale.ROOT))); + } catch (IllegalArgumentException iae) { + throw new AlertsSettingsException("could not parse search request. unknown template type [" + parser.text() + "]"); + } + } + } else if (token == XContentParser.Token.START_OBJECT) { + if ("params".equals(currentFieldName)) { + searchRequest.templateParams(flattenModel(parser.map())); + } + } + } + break; + default: - throw new ElasticsearchIllegalArgumentException("Unexpected field [" + searchRequestFieldName + "]"); + throw new ElasticsearchIllegalArgumentException("Unexpected field [" + currentFieldName + "]"); } } else if (token.isValue()) { - switch (searchRequestFieldName) { - case "template_name": - searchRequest.templateName(parser.textOrNull()); - break; - case "template_type": - searchRequest.templateType(ScriptService.ScriptType.valueOf(parser.text().toUpperCase(Locale.ROOT))); - break; + switch (currentFieldName) { case "search_type": searchType = SearchType.fromString(parser.text().toLowerCase(Locale.ROOT)); break; default: - throw new ElasticsearchIllegalArgumentException("Unexpected field [" + searchRequestFieldName + "]"); + throw new ElasticsearchIllegalArgumentException("Unexpected field [" + currentFieldName + "]"); } } else { - throw new ElasticsearchIllegalArgumentException("Unexpected field [" + searchRequestFieldName + "]"); + throw new ElasticsearchIllegalArgumentException("Unexpected field [" + currentFieldName + "]"); } } @@ -172,11 +191,17 @@ public final class AlertUtils { XContentHelper.writeRawField("body", searchRequest.source(), builder, params); } if (searchRequest.templateName() != null) { - builder.field("template_name", searchRequest.templateName()); - } - if (searchRequest.templateType() != null) { - builder.field("template_type", searchRequest.templateType().name().toLowerCase(Locale.ROOT)); + builder.startObject("template") + .field("name", searchRequest.templateName()); + if (searchRequest.templateType() != null) { + builder.field("type", searchRequest.templateType().name().toLowerCase(Locale.ROOT)); + } + if (searchRequest.templateParams() != null && !searchRequest.templateParams().isEmpty()) { + builder.field("params", searchRequest.templateParams()); + } + builder.endObject(); } + if (searchRequest.indicesOptions() != DEFAULT_INDICES_OPTIONS) { IndicesOptions options = searchRequest.indicesOptions(); builder.startObject("indices_options"); @@ -198,4 +223,44 @@ public final class AlertUtils { return builder.endObject(); } + public static Map flattenModel(Map map) { + Map result = new HashMap<>(); + flattenModel("", map, result); + return result; + } + + private static void flattenModel(String key, Object value, Map result) { + if (value instanceof Map) { + for (Map.Entry entry : ((Map) value).entrySet()) { + if ("".equals(key)) { + flattenModel(entry.getKey(), entry.getValue(), result); + } else { + flattenModel(key + "." + entry.getKey(), entry.getValue(), result); + } + } + return; + } + if (value instanceof Iterable) { + int i = 0; + for (Object item : (Iterable) value) { + flattenModel(key + "." + i++, item, result); + } + return; + } + if (value.getClass().isArray()) { + for (int i = 0; i < Array.getLength(value); i++) { + flattenModel(key + "." + i, Array.get(value, i), result); + } + return; + } + if (value instanceof DateTime) { + result.put(key, formatDate((DateTime) value)); + return; + } + if (value instanceof TimeValue) { + result.put(key, String.valueOf(((TimeValue) value).getMillis())); + return; + } + result.put(key, String.valueOf(value)); + } } diff --git a/src/main/java/org/elasticsearch/alerts/transform/SearchTransform.java b/src/main/java/org/elasticsearch/alerts/transform/SearchTransform.java index 6200c908d75..6890113b925 100644 --- a/src/main/java/org/elasticsearch/alerts/transform/SearchTransform.java +++ b/src/main/java/org/elasticsearch/alerts/transform/SearchTransform.java @@ -18,10 +18,8 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.joda.time.DateTime; import org.elasticsearch.common.logging.ESLogger; 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.XContentHelper; @@ -30,11 +28,8 @@ import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.ScriptService; import java.io.IOException; -import java.lang.reflect.Array; -import java.util.HashMap; -import java.util.Map; -import static org.elasticsearch.alerts.support.AlertsDateUtils.formatDate; +import static org.elasticsearch.alerts.support.AlertUtils.flattenModel; import static org.elasticsearch.alerts.support.Variables.createCtxModel; /** @@ -86,7 +81,7 @@ public class SearchTransform extends Transform { request.source((BytesReference) script.unwrap(script.run()), false); } else if (requestPrototype.templateName() != null) { MapBuilder templateParams = MapBuilder.newMapBuilder(requestPrototype.templateParams()) - .putAll(flatten(createCtxModel(ctx, payload))); + .putAll(flattenModel(createCtxModel(ctx, payload))); request.templateParams(templateParams.map()); request.templateName(requestPrototype.templateName()); request.templateType(requestPrototype.templateType()); @@ -96,47 +91,6 @@ public class SearchTransform extends Transform { return request; } - static Map flatten(Map map) { - Map result = new HashMap<>(); - flatten("", map, result); - return result; - } - - private static void flatten(String key, Object value, Map result) { - if (value instanceof Map) { - for (Map.Entry entry : ((Map) value).entrySet()) { - if ("".equals(key)) { - flatten(entry.getKey(), entry.getValue(), result); - } else { - flatten(key + "." + entry.getKey(), entry.getValue(), result); - } - } - return; - } - if (value instanceof Iterable) { - int i = 0; - for (Object item : (Iterable) value) { - flatten(key + "." + i++, item, result); - } - return; - } - if (value.getClass().isArray()) { - for (int i = 0; i < Array.getLength(value); i++) { - flatten(key + "." + i, Array.get(value, i), result); - } - return; - } - if (value instanceof DateTime) { - result.put(key, formatDate((DateTime) value)); - return; - } - if (value instanceof TimeValue) { - result.put(key, String.valueOf(((TimeValue) value).getMillis())); - return; - } - result.put(key, String.valueOf(value)); - } - public static class Parser extends AbstractComponent implements Transform.Parser { protected final ScriptServiceProxy scriptService; diff --git a/src/test/java/org/elasticsearch/alerts/support/AlertUtilsTests.java b/src/test/java/org/elasticsearch/alerts/support/AlertUtilsTests.java new file mode 100644 index 00000000000..3c8ff7e03b3 --- /dev/null +++ b/src/test/java/org/elasticsearch/alerts/support/AlertUtilsTests.java @@ -0,0 +1,49 @@ +/* + * 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.alerts.support; + +import org.elasticsearch.common.collect.ImmutableList; +import org.elasticsearch.common.collect.ImmutableMap; +import org.elasticsearch.common.joda.time.DateTime; +import org.elasticsearch.common.unit.TimeValue; +import org.junit.Test; + +import java.util.Map; + +import static org.elasticsearch.alerts.support.AlertUtils.flattenModel; +import static org.elasticsearch.alerts.support.AlertsDateUtils.formatDate; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.is; + +/** + * + */ +public class AlertUtilsTests { + + @Test + public void testFlattenModel() throws Exception { + DateTime now = new DateTime(); + Map map = ImmutableMap.builder() + .put("a", ImmutableMap.builder().put("a1", new int[] { 0, 1, 2 }).build()) + .put("b", new String[] { "b0", "b1", "b2" }) + .put("c", ImmutableList.of(TimeValue.timeValueSeconds(0), TimeValue.timeValueSeconds(1))) + .put("d", now) + .build(); + + Map result = flattenModel(map); + assertThat(result.size(), is(9)); + assertThat(result, hasEntry("a.a1.0", "0")); + assertThat(result, hasEntry("a.a1.1", "1")); + assertThat(result, hasEntry("a.a1.2", "2")); + assertThat(result, hasEntry("b.0", "b0")); + assertThat(result, hasEntry("b.1", "b1")); + assertThat(result, hasEntry("b.2", "b2")); + assertThat(result, hasEntry("c.0", "0")); + assertThat(result, hasEntry("c.1", "1000")); + assertThat(result, hasEntry("d", formatDate(now))); + } +} diff --git a/src/test/java/org/elasticsearch/alerts/transform/SearchTransformTests.java b/src/test/java/org/elasticsearch/alerts/transform/SearchTransformTests.java index 6ef11a16614..5aefee1623a 100644 --- a/src/test/java/org/elasticsearch/alerts/transform/SearchTransformTests.java +++ b/src/test/java/org/elasticsearch/alerts/transform/SearchTransformTests.java @@ -16,11 +16,9 @@ import org.elasticsearch.alerts.support.init.proxy.ClientProxy; import org.elasticsearch.alerts.test.AbstractAlertsSingleNodeTests; import org.elasticsearch.client.Requests; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.collect.ImmutableList; import org.elasticsearch.common.collect.ImmutableMap; import org.elasticsearch.common.joda.time.DateTime; import org.elasticsearch.common.settings.ImmutableSettings; -import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.json.JsonXContent; @@ -30,7 +28,6 @@ import org.junit.Test; import java.util.HashMap; import java.util.Map; -import static org.elasticsearch.alerts.support.AlertsDateUtils.formatDate; import static org.elasticsearch.alerts.support.AlertsDateUtils.parseDate; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.index.query.FilterBuilders.*; @@ -172,10 +169,12 @@ public class SearchTransformTests extends AbstractAlertsSingleNodeTests { builder.field("search_type", searchType.name()); } if (templateName != null) { - builder.field("template_name", templateName); - } - if (templateType != null) { - builder.field("template_type", templateType); + builder.startObject("template") + .field("name", templateName); + if (templateType != null) { + builder.field("type", templateType); + } + builder.endObject(); } XContentBuilder sourceBuilder = jsonBuilder().startObject() @@ -215,29 +214,6 @@ public class SearchTransformTests extends AbstractAlertsSingleNodeTests { assertThat(transform.request.source().toBytes(), equalTo(source.toBytes())); } - @Test - public void testFlatten() throws Exception { - DateTime now = new DateTime(); - Map map = ImmutableMap.builder() - .put("a", ImmutableMap.builder().put("a1", new int[] { 0, 1, 2 }).build()) - .put("b", new String[] { "b0", "b1", "b2" }) - .put("c", ImmutableList.of( TimeValue.timeValueSeconds(0), TimeValue.timeValueSeconds(1))) - .put("d", now) - .build(); - - Map result = SearchTransform.flatten(map); - assertThat(result.size(), is(9)); - assertThat(result, hasEntry("a.a1.0", "0")); - assertThat(result, hasEntry("a.a1.1", "1")); - assertThat(result, hasEntry("a.a1.2", "2")); - assertThat(result, hasEntry("b.0", "b0")); - assertThat(result, hasEntry("b.1", "b1")); - assertThat(result, hasEntry("b.2", "b2")); - assertThat(result, hasEntry("c.0", "0")); - assertThat(result, hasEntry("c.1", "1000")); - assertThat(result, hasEntry("d", formatDate(now))); - } - private static Map doc(String date, String value) { Map doc = new HashMap<>(); doc.put("date", parseDate(date));