From aae6ff834f05063f69c4ab2d2c26a71eed99a5b0 Mon Sep 17 00:00:00 2001 From: uboness Date: Wed, 25 Feb 2015 22:05:11 +0200 Subject: [PATCH] [client] reorganized tests and added alert source builder The `AlertSourceBuilder` along with a set of source builder for all the different constructs that make an alert (condition, input, transform and action), provides a structured approach for building an alert from the client side (instead of forcing the clients to use xcontent directory) - fixed some of the tests to already use these builders (I reckon there are still quite a few that need to be converted.. but we'll do that over time). - moved all integration tests under `test/integration` package. - changed the `AlertsTests` to **not** be an integration test... it randomizes the alert structure and makes sure that it can serialize & deserialize itself to/from xcontent. - fixed small bugs found by the tests Original commit: elastic/x-pack-elasticsearch@94b76b6fc76324170928f9c4db079733050f4531 --- .../java/org/elasticsearch/alerts/Alert.java | 81 ++++- .../org/elasticsearch/alerts/Payload.java | 18 +- .../elasticsearch/alerts/actions/Action.java | 9 +- .../alerts/actions/ActionBuilders.java | 36 +++ .../elasticsearch/alerts/actions/Actions.java | 20 ++ .../alerts/actions/email/EmailAction.java | 62 ++++ .../alerts/actions/index/IndexAction.java | 25 ++ .../alerts/actions/webhook/WebhookAction.java | 53 +++- .../alerts/client/AlertSourceBuilder.java | 129 ++++++++ .../alerts/condition/Condition.java | 6 + .../alerts/condition/ConditionBuilders.java | 31 ++ .../alerts/condition/ConditionRegistry.java | 5 + .../condition/script/ScriptCondition.java | 57 ++++ .../simple/AlwaysFalseCondition.java | 25 ++ .../condition/simple/AlwaysTrueCondition.java | 22 ++ .../alerts/history/HistoryStore.java | 4 +- .../org/elasticsearch/alerts/input/Input.java | 6 + .../alerts/input/InputBuilders.java | 39 +++ .../elasticsearch/alerts/input/NoneInput.java | 128 ++++++++ .../alerts/input/search/SearchInput.java | 70 +++-- .../alerts/input/simple/SimpleInput.java | 47 +++ .../rest/action/RestPutAlertAction.java | 2 +- .../scheduler/schedule/ScheduleRegistry.java | 5 + .../alerts/support/AlertUtils.java | 20 +- .../elasticsearch/alerts/support/Script.java | 2 +- .../support/SearchRequestEquivalence.java | 54 ++++ .../support/template/XContentTemplate.java | 2 + .../alerts/transform/ChainTransform.java | 36 ++- .../alerts/transform/ScriptTransform.java | 31 +- .../alerts/transform/SearchTransform.java | 23 +- .../alerts/transform/Transform.java | 7 +- .../alerts/transform/TransformBuilders.java | 39 +++ .../alerts/transform/TransformRegistry.java | 2 +- .../actions/put/PutAlertRequest.java | 14 +- .../actions/put/PutAlertRequestBuilder.java | 16 +- .../alerts/AlertSerializationTest.java | 75 ----- .../org/elasticsearch/alerts/AlertTests.java | 281 ++++++++++++++++++ .../alerts/actions/ActionsTest.java | 101 ------- ...on.java => AlwaysFalseConditionTests.java} | 6 +- ...ion.java => AlwaysTrueConditionTests.java} | 6 +- ...redAlertTest.java => FiredAlertTests.java} | 10 +- ...exNameTest.java => HistoryStoreTests.java} | 4 +- .../alerts/input/search/SearchInputTests.java | 24 +- .../schedule/ScheduleRegistryTests.java | 3 - .../support/template/ScriptTemplateTests.java | 1 - .../AbstractAlertsIntegrationTests.java} | 83 +----- .../alerts/test/AlertsTestUtils.java | 107 +++++++ .../integration/AlertMetadataTests.java} | 19 +- .../integration}/AlertStatsTests.java | 37 ++- .../integration}/AlertThrottleTests.java | 88 ++---- .../integration/BasicAlertsTests.java} | 108 ++++--- .../integration/BootStrapTests.java} | 31 +- .../integration}/NoMasterNodeTests.java | 21 +- .../integration/TransformSearchTests.java} | 56 ++-- .../transform/ScriptTransformTests.java | 6 +- 55 files changed, 1662 insertions(+), 531 deletions(-) create mode 100644 src/main/java/org/elasticsearch/alerts/actions/ActionBuilders.java create mode 100644 src/main/java/org/elasticsearch/alerts/client/AlertSourceBuilder.java create mode 100644 src/main/java/org/elasticsearch/alerts/condition/ConditionBuilders.java create mode 100644 src/main/java/org/elasticsearch/alerts/input/InputBuilders.java create mode 100644 src/main/java/org/elasticsearch/alerts/input/NoneInput.java create mode 100644 src/main/java/org/elasticsearch/alerts/support/SearchRequestEquivalence.java create mode 100644 src/main/java/org/elasticsearch/alerts/transform/TransformBuilders.java delete mode 100644 src/test/java/org/elasticsearch/alerts/AlertSerializationTest.java create mode 100644 src/test/java/org/elasticsearch/alerts/AlertTests.java delete mode 100644 src/test/java/org/elasticsearch/alerts/actions/ActionsTest.java rename src/test/java/org/elasticsearch/alerts/condition/simple/{TestAlwaysTrueCondition.java => AlwaysFalseConditionTests.java} (97%) rename src/test/java/org/elasticsearch/alerts/condition/simple/{TestAlwaysFalseCondition.java => AlwaysTrueConditionTests.java} (98%) rename src/test/java/org/elasticsearch/alerts/history/{FiredAlertTest.java => FiredAlertTests.java} (88%) rename src/test/java/org/elasticsearch/alerts/history/{ActionHistoryIndexNameTest.java => HistoryStoreTests.java} (90%) rename src/test/java/org/elasticsearch/alerts/{AbstractAlertingTests.java => test/AbstractAlertsIntegrationTests.java} (82%) create mode 100644 src/test/java/org/elasticsearch/alerts/test/AlertsTestUtils.java rename src/test/java/org/elasticsearch/alerts/{AlertMetadataTest.java => test/integration/AlertMetadataTests.java} (64%) rename src/test/java/org/elasticsearch/alerts/{actions => test/integration}/AlertStatsTests.java (61%) rename src/test/java/org/elasticsearch/alerts/{ => test/integration}/AlertThrottleTests.java (70%) rename src/test/java/org/elasticsearch/alerts/{BasicAlertingTest.java => test/integration/BasicAlertsTests.java} (70%) rename src/test/java/org/elasticsearch/alerts/{BootStrapTest.java => test/integration/BootStrapTests.java} (88%) rename src/test/java/org/elasticsearch/alerts/{ => test/integration}/NoMasterNodeTests.java (88%) rename src/test/java/org/elasticsearch/alerts/{TransformSearchTest.java => test/integration/TransformSearchTests.java} (51%) diff --git a/src/main/java/org/elasticsearch/alerts/Alert.java b/src/main/java/org/elasticsearch/alerts/Alert.java index dc1c54bf7a9..27008807cff 100644 --- a/src/main/java/org/elasticsearch/alerts/Alert.java +++ b/src/main/java/org/elasticsearch/alerts/Alert.java @@ -7,11 +7,13 @@ package org.elasticsearch.alerts; import org.elasticsearch.alerts.actions.ActionRegistry; import org.elasticsearch.alerts.actions.Actions; -import org.elasticsearch.alerts.scheduler.Scheduler; import org.elasticsearch.alerts.condition.Condition; import org.elasticsearch.alerts.condition.ConditionRegistry; +import org.elasticsearch.alerts.condition.simple.AlwaysTrueCondition; import org.elasticsearch.alerts.input.Input; import org.elasticsearch.alerts.input.InputRegistry; +import org.elasticsearch.alerts.input.NoneInput; +import org.elasticsearch.alerts.scheduler.Scheduler; import org.elasticsearch.alerts.scheduler.schedule.Schedule; import org.elasticsearch.alerts.scheduler.schedule.ScheduleRegistry; import org.elasticsearch.alerts.throttle.AlertThrottler; @@ -27,6 +29,7 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Streamable; import org.elasticsearch.common.joda.time.DateTime; +import org.elasticsearch.common.joda.time.DateTimeZone; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.ToXContent; @@ -57,7 +60,7 @@ public class Alert implements Scheduler.Job, ToXContent { @Nullable private final Transform transform; - public Alert(String name, Schedule schedule, Input input, Condition condition, Transform transform, Actions actions, Map metadata, Status status, TimeValue throttlePeriod) { + public Alert(String name, Schedule schedule, Input input, Condition condition, @Nullable Transform transform, Actions actions, @Nullable Map metadata, @Nullable TimeValue throttlePeriod, @Nullable Status status) { this.name = name; this.schedule = schedule; this.input = input; @@ -67,7 +70,6 @@ public class Alert implements Scheduler.Job, ToXContent { this.throttlePeriod = throttlePeriod; this.metadata = metadata; this.transform = transform != null ? transform : Transform.NOOP; - throttler = new AlertThrottler(throttlePeriod); } @@ -174,6 +176,9 @@ public class Alert implements Scheduler.Job, ToXContent { private final ActionRegistry actionRegistry; private final InputRegistry inputRegistry; + private final Input defaultInput; + private final Condition defaultCondition; + @Inject public Parser(Settings settings, ConditionRegistry conditionRegistry, ScheduleRegistry scheduleRegistry, TransformRegistry transformRegistry, ActionRegistry actionRegistry, @@ -185,6 +190,9 @@ public class Alert implements Scheduler.Job, ToXContent { this.transformRegistry = transformRegistry; this.actionRegistry = actionRegistry; this.inputRegistry = inputRegistry; + + this.defaultInput = new NoneInput(logger); + this.defaultCondition = new AlwaysTrueCondition(logger); } public Alert parse(String name, boolean includeStatus, BytesReference source) { @@ -200,8 +208,8 @@ public class Alert implements Scheduler.Job, ToXContent { public Alert parse(String name, boolean includeStatus, XContentParser parser) throws IOException { Schedule schedule = null; - Input input = null; - Condition condition = null; + Input input = defaultInput; + Condition condition = defaultCondition; Actions actions = null; Transform transform = null; Map metatdata = null; @@ -244,30 +252,24 @@ public class Alert implements Scheduler.Job, ToXContent { if (schedule == null) { throw new AlertsSettingsException("could not parse alert [" + name + "]. missing alert schedule"); } - if (input == null) { - throw new AlertsSettingsException("could not parse alert [" + name + "]. missing alert input"); - } - if (condition == null) { - throw new AlertsSettingsException("could not parse alert [" + name + "]. missing alert condition"); - } if (actions == null) { throw new AlertsSettingsException("could not parse alert [" + name + "]. missing alert actions"); } - return new Alert(name, schedule, input, condition, transform, actions, metatdata, status, throttlePeriod); + return new Alert(name, schedule, input, condition, transform, actions, metatdata, throttlePeriod, status); } } public static class Status implements ToXContent, Streamable { - public static final ParseField TIMESTAMP_FIELD = new ParseField("last_throttled"); public static final ParseField LAST_CHECKED_FIELD = new ParseField("last_checked"); public static final ParseField LAST_MET_CONDITION_FIELD = new ParseField("last_met_condition"); public static final ParseField LAST_THROTTLED_FIELD = new ParseField("last_throttled"); public static final ParseField LAST_EXECUTED_FIELD = new ParseField("last_executed"); public static final ParseField ACK_FIELD = new ParseField("ack"); public static final ParseField STATE_FIELD = new ParseField("state"); + public static final ParseField TIMESTAMP_FIELD = new ParseField("timestamp"); public static final ParseField REASON_FIELD = new ParseField("reason"); private transient long version; @@ -335,6 +337,38 @@ public class Alert implements Scheduler.Job, ToXContent { return ackStatus; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Status status = (Status) o; + + if (version != status.version) return false; + if (!ackStatus.equals(status.ackStatus)) return false; + if (lastChecked != null ? !lastChecked.equals(status.lastChecked) : status.lastChecked != null) + return false; + if (lastExecuted != null ? !lastExecuted.equals(status.lastExecuted) : status.lastExecuted != null) + return false; + if (lastMetCondition != null ? !lastMetCondition.equals(status.lastMetCondition) : status.lastMetCondition != null) + return false; + if (lastThrottle != null ? !lastThrottle.equals(status.lastThrottle) : status.lastThrottle != null) + return false; + + return true; + } + + @Override + public int hashCode() { + int result = (int) (version ^ (version >>> 32)); + result = 31 * result + (lastChecked != null ? lastChecked.hashCode() : 0); + result = 31 * result + (lastMetCondition != null ? lastMetCondition.hashCode() : 0); + result = 31 * result + (lastThrottle != null ? lastThrottle.hashCode() : 0); + result = 31 * result + (lastExecuted != null ? lastExecuted.hashCode() : 0); + result = 31 * result + ackStatus.hashCode(); + return result; + } + /** * Called whenever an alert is checked, ie. the condition of the alert is evaluated to see if * the alert should be executed. @@ -534,7 +568,7 @@ public class Alert implements Scheduler.Job, ToXContent { private final DateTime timestamp; public AckStatus() { - this(State.AWAITS_EXECUTION, new DateTime()); + this(State.AWAITS_EXECUTION, new DateTime(DateTimeZone.UTC)); } public AckStatus(State state, DateTime timestamp) { @@ -550,6 +584,25 @@ public class Alert implements Scheduler.Job, ToXContent { return timestamp; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AckStatus ackStatus = (AckStatus) o; + + if (state != ackStatus.state) return false; + if (!timestamp.equals(ackStatus.timestamp)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = state.hashCode(); + result = 31 * result + timestamp.hashCode(); + return result; + } } public static class Throttle { diff --git a/src/main/java/org/elasticsearch/alerts/Payload.java b/src/main/java/org/elasticsearch/alerts/Payload.java index 06cab302210..4541497a591 100644 --- a/src/main/java/org/elasticsearch/alerts/Payload.java +++ b/src/main/java/org/elasticsearch/alerts/Payload.java @@ -43,6 +43,23 @@ public interface Payload extends ToXContent { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { return builder.value(data); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Simple simple = (Simple) o; + + if (!data.equals(simple.data)) return false; + + return true; + } + + @Override + public int hashCode() { + return data.hashCode(); + } } static class ActionResponse extends Simple { @@ -50,7 +67,6 @@ public interface Payload extends ToXContent { public ActionResponse(org.elasticsearch.action.ActionResponse response) { super(responseToData(response)); } - } static class XContent extends Simple { diff --git a/src/main/java/org/elasticsearch/alerts/actions/Action.java b/src/main/java/org/elasticsearch/alerts/actions/Action.java index 63b83a22be7..b3f79537f27 100644 --- a/src/main/java/org/elasticsearch/alerts/actions/Action.java +++ b/src/main/java/org/elasticsearch/alerts/actions/Action.java @@ -48,7 +48,7 @@ public abstract class Action implements ToXContent { /** * Parses xcontent to a concrete action of the same type. */ - protected static interface Parser> { + public static interface Parser> { /** * @return The type of the action @@ -92,5 +92,12 @@ public abstract class Action implements ToXContent { } protected abstract XContentBuilder xContentBody(XContentBuilder builder, Params params) throws IOException; + + } + + public static interface SourceBuilder extends ToXContent { + + String type(); + } } diff --git a/src/main/java/org/elasticsearch/alerts/actions/ActionBuilders.java b/src/main/java/org/elasticsearch/alerts/actions/ActionBuilders.java new file mode 100644 index 00000000000..c106ba359f9 --- /dev/null +++ b/src/main/java/org/elasticsearch/alerts/actions/ActionBuilders.java @@ -0,0 +1,36 @@ +/* + * 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.actions; + +import org.elasticsearch.alerts.actions.email.EmailAction; +import org.elasticsearch.alerts.actions.index.IndexAction; +import org.elasticsearch.alerts.actions.webhook.WebhookAction; +import org.elasticsearch.alerts.support.Script; + +/** + * + */ +public final class ActionBuilders { + + private ActionBuilders() { + } + + public static EmailAction.SourceBuilder emailAction() { + return new EmailAction.SourceBuilder(); + } + + public static IndexAction.SourceBuilder indexAction(String index, String type) { + return new IndexAction.SourceBuilder(index, type); + } + + public static WebhookAction.SourceBuilder webhookAction(String url) { + return new WebhookAction.SourceBuilder(url); + } + + public static WebhookAction.SourceBuilder webhookAction(Script url) { + return new WebhookAction.SourceBuilder(url); + } +} diff --git a/src/main/java/org/elasticsearch/alerts/actions/Actions.java b/src/main/java/org/elasticsearch/alerts/actions/Actions.java index fa3a5f95968..39bad55426c 100644 --- a/src/main/java/org/elasticsearch/alerts/actions/Actions.java +++ b/src/main/java/org/elasticsearch/alerts/actions/Actions.java @@ -23,6 +23,10 @@ public class Actions implements Iterable, ToXContent { this.actions = actions; } + public int count() { + return actions.size(); + } + @Override public Iterator iterator() { return actions.iterator(); @@ -38,4 +42,20 @@ public class Actions implements Iterable, ToXContent { return builder; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Actions actions1 = (Actions) o; + + if (!actions.equals(actions1.actions)) return false; + + return true; + } + + @Override + public int hashCode() { + return actions.hashCode(); + } } diff --git a/src/main/java/org/elasticsearch/alerts/actions/email/EmailAction.java b/src/main/java/org/elasticsearch/alerts/actions/email/EmailAction.java index 2aa704a3036..6b4edb4be0a 100644 --- a/src/main/java/org/elasticsearch/alerts/actions/email/EmailAction.java +++ b/src/main/java/org/elasticsearch/alerts/actions/email/EmailAction.java @@ -358,6 +358,68 @@ public class EmailAction extends Action { } } + public static class SourceBuilder implements Action.SourceBuilder { + private Email.Address from; + private Email.AddressList replyTo; + private Email.AddressList to; + private Email.AddressList cc; + private Email.AddressList bcc; + private Authentication auth = null; + private Profile profile = null; + private String account = null; + private Template subject; + private Template textBody; + private Template htmlBody; + private Boolean attachPayload; + + @Override + public String type() { + return TYPE; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + if (from != null) { + builder.field(Email.FROM_FIELD.getPreferredName(), from); + } + if (replyTo != null && replyTo.size() != 0) { + builder.field(Email.REPLY_TO_FIELD.getPreferredName(), (ToXContent) replyTo); + } + if (to != null && to.size() != 0) { + builder.field(Email.TO_FIELD.getPreferredName(), (ToXContent) to); + } + if (cc != null && cc.size() != 0) { + builder.field(Email.CC_FIELD.getPreferredName(), (ToXContent) cc); + } + if (bcc != null && bcc.size() != 0) { + builder.field(Email.BCC_FIELD.getPreferredName(), (ToXContent) bcc); + } + if (auth != null) { + builder.field(Parser.USER_FIELD.getPreferredName(), auth.user()); + builder.field(Parser.PASSWORD_FIELD.getPreferredName(), auth.password()); + } + if (profile != null) { + builder.field(Parser.PROFILE_FIELD.getPreferredName(), profile); + } + if (account != null) { + builder.field(Parser.ACCOUNT_FIELD.getPreferredName(), account); + } + if (subject != null) { + builder.field(Email.SUBJECT_FIELD.getPreferredName(), subject); + } + if (textBody != null) { + builder.field(Email.TEXT_BODY_FIELD.getPreferredName(), textBody); + } + if (htmlBody != null) { + builder.field(Email.HTML_BODY_FIELD.getPreferredName(), htmlBody); + } + if (attachPayload != null) { + builder.field(Parser.ATTACH_PAYLOAD_FIELD.getPreferredName(), attachPayload); + } + return builder.endObject(); + } + } } diff --git a/src/main/java/org/elasticsearch/alerts/actions/index/IndexAction.java b/src/main/java/org/elasticsearch/alerts/actions/index/IndexAction.java index c3821e919c1..0c775aae2a6 100644 --- a/src/main/java/org/elasticsearch/alerts/actions/index/IndexAction.java +++ b/src/main/java/org/elasticsearch/alerts/actions/index/IndexAction.java @@ -233,7 +233,32 @@ public class IndexAction extends Action { } return builder; } + } + public static class SourceBuilder implements Action.SourceBuilder { + + private final String index; + private final String type; + + public SourceBuilder(String index, String type) { + this.index = index; + this.type = type; + } + + @Override + public String type() { + return TYPE; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.startObject() + .field(Parser.INDEX_FIELD.getPreferredName(), index) + .field(Parser.TYPE_FIELD.getPreferredName(), type) + .endObject(); + } } } + + diff --git a/src/main/java/org/elasticsearch/alerts/actions/webhook/WebhookAction.java b/src/main/java/org/elasticsearch/alerts/actions/webhook/WebhookAction.java index 885a474b728..1dc7c5c0366 100644 --- a/src/main/java/org/elasticsearch/alerts/actions/webhook/WebhookAction.java +++ b/src/main/java/org/elasticsearch/alerts/actions/webhook/WebhookAction.java @@ -11,6 +11,7 @@ import org.elasticsearch.alerts.Payload; import org.elasticsearch.alerts.actions.Action; import org.elasticsearch.alerts.actions.ActionException; import org.elasticsearch.alerts.actions.ActionSettingsException; +import org.elasticsearch.alerts.support.Script; import org.elasticsearch.alerts.support.template.Template; import org.elasticsearch.alerts.support.template.XContentTemplate; import org.elasticsearch.common.Nullable; @@ -25,6 +26,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; +import java.util.Locale; /** */ @@ -75,13 +77,12 @@ public class WebhookAction extends Action { logger.error("failed to connect to [{}] for alert [{}]", ioe, urlText, ctx.alert().name()); return new Result.Failure("failed to send http request. " + ioe.getMessage()); } - } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); - builder.field(Parser.METHOD_FIELD.getPreferredName(), method.getName()); + builder.field(Parser.METHOD_FIELD.getPreferredName(), method.getName().toLowerCase(Locale.ROOT)); builder.field(Parser.URL_FIELD.getPreferredName(), url); if (body != null) { builder.field(Parser.BODY_FIELD.getPreferredName(), body); @@ -96,7 +97,7 @@ public class WebhookAction extends Action { WebhookAction that = (WebhookAction) o; - if (!body.equals(that.body)) return false; + if (body != null ? !body.equals(that.body) : that.body != null) return false; if (!method.equals(that.method)) return false; if (!url.equals(that.url)) return false; @@ -107,7 +108,7 @@ public class WebhookAction extends Action { public int hashCode() { int result = method.hashCode(); result = 31 * result + url.hashCode(); - result = 31 * result + body.hashCode(); + result = 31 * result + (body != null ? body.hashCode() : 0); return result; } @@ -205,7 +206,7 @@ public class WebhookAction extends Action { currentFieldName = parser.currentName(); } else if ((token.isValue() || token == XContentParser.Token.START_OBJECT) && currentFieldName != null ) { if (METHOD_FIELD.match(currentFieldName)) { - method = HttpMethod.valueOf(parser.text()); + method = HttpMethod.valueOf(parser.text().toUpperCase(Locale.ROOT)); if (method != HttpMethod.POST && method != HttpMethod.GET && method != HttpMethod.PUT) { throw new ActionSettingsException("could not parse webhook action. unsupported http method [" + method.getName() + "]"); } @@ -279,4 +280,46 @@ public class WebhookAction extends Action { } } + public static class SourceBuilder implements Action.SourceBuilder { + + private final Script url; + private HttpMethod method; + private Script body = null; + + public SourceBuilder(String url) { + this(new Script(url)); + } + + public SourceBuilder(Script url) { + this.url = url; + } + + public SourceBuilder method(HttpMethod method) { + this.method = method; + return this; + } + + public SourceBuilder body(Script body) { + this.body = body; + return this; + } + + @Override + public String type() { + return TYPE; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field(Parser.URL_FIELD.getPreferredName(), url); + if (method != null) { + builder.field(Parser.METHOD_FIELD.getPreferredName(), method.getName().toLowerCase(Locale.ROOT)); + } + if (body != null) { + builder.field(Parser.BODY_FIELD.getPreferredName(), body); + } + return builder.endObject(); + } + } } diff --git a/src/main/java/org/elasticsearch/alerts/client/AlertSourceBuilder.java b/src/main/java/org/elasticsearch/alerts/client/AlertSourceBuilder.java new file mode 100644 index 00000000000..bf298ff8773 --- /dev/null +++ b/src/main/java/org/elasticsearch/alerts/client/AlertSourceBuilder.java @@ -0,0 +1,129 @@ +/* + * 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.client; + +import org.elasticsearch.alerts.Alert; +import org.elasticsearch.alerts.actions.Action; +import org.elasticsearch.alerts.condition.Condition; +import org.elasticsearch.alerts.condition.ConditionBuilders; +import org.elasticsearch.alerts.input.Input; +import org.elasticsearch.alerts.input.NoneInput; +import org.elasticsearch.alerts.scheduler.schedule.Schedule; +import org.elasticsearch.alerts.transform.Transform; +import org.elasticsearch.common.bytes.BytesReference; +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.XContentType; +import org.elasticsearch.search.builder.SearchSourceBuilderException; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * + */ +public class AlertSourceBuilder implements ToXContent { + + public static AlertSourceBuilder alertSourceBuilder() { + return new AlertSourceBuilder(); + } + + private Schedule schedule; + private Input.SourceBuilder input = NoneInput.SourceBuilder.INSTANCE; + private Condition.SourceBuilder condition = ConditionBuilders.alwaysTrueCondition(); + private Transform.SourceBuilder transform = null; + private Set actions = new HashSet<>(); + private TimeValue throttlePeriod = null; + private Map metadata; + + public AlertSourceBuilder schedule(Schedule schedule) { + this.schedule = schedule; + return this; + } + + public AlertSourceBuilder input(Input.SourceBuilder input) { + this.input = input; + return this; + } + + public AlertSourceBuilder condition(Condition.SourceBuilder condition) { + this.condition = condition; + return this; + } + + public AlertSourceBuilder transform(Transform.SourceBuilder transform) { + this.transform = transform; + return this; + } + + public AlertSourceBuilder throttlePeriod(TimeValue throttlePeriod) { + this.throttlePeriod = throttlePeriod; + return this; + } + + public AlertSourceBuilder addAction(Action.SourceBuilder action) { + actions.add(action); + return this; + } + + public AlertSourceBuilder metadata(Map metadata) { + this.metadata = metadata; + return this; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + + builder.startObject(Alert.Parser.SCHEDULE_FIELD.getPreferredName()) + .field(schedule.type(), schedule) + .endObject(); + + builder.startObject(Alert.Parser.INPUT_FIELD.getPreferredName()) + .field(input.type(), input) + .endObject(); + + builder.startObject(Alert.Parser.CONDITION_FIELD.getPreferredName()) + .field(condition.type(), condition) + .endObject(); + + if (transform != null) { + builder.startObject(Alert.Parser.TRANSFORM_FIELD.getPreferredName()) + .field(transform.type(), transform) + .endObject(); + } + + if (throttlePeriod != null) { + builder.field(Alert.Parser.THROTTLE_PERIOD_FIELD.getPreferredName(), throttlePeriod.getMillis()); + } + + builder.startArray(Alert.Parser.ACTIONS_FIELD.getPreferredName()); + for (Action.SourceBuilder action : actions) { + builder.startObject().field(action.type(), action).endObject(); + } + builder.endArray(); + + if (metadata != null) { + builder.field(Alert.Parser.META_FIELD.getPreferredName(), metadata); + } + + return builder.endObject(); + } + + public BytesReference buildAsBytes(XContentType contentType) throws SearchSourceBuilderException { + try { + XContentBuilder builder = XContentFactory.contentBuilder(contentType); + toXContent(builder, ToXContent.EMPTY_PARAMS); + return builder.bytes(); + } catch (Exception e) { + throw new SearchSourceBuilderException("Failed to build search source", e); + } + } +} diff --git a/src/main/java/org/elasticsearch/alerts/condition/Condition.java b/src/main/java/org/elasticsearch/alerts/condition/Condition.java index 4b8397910cc..27bfdfbea8a 100644 --- a/src/main/java/org/elasticsearch/alerts/condition/Condition.java +++ b/src/main/java/org/elasticsearch/alerts/condition/Condition.java @@ -75,4 +75,10 @@ public abstract class Condition implements ToXConten public boolean met() { return met; } } + + public static interface SourceBuilder extends ToXContent { + + public String type(); + + } } diff --git a/src/main/java/org/elasticsearch/alerts/condition/ConditionBuilders.java b/src/main/java/org/elasticsearch/alerts/condition/ConditionBuilders.java new file mode 100644 index 00000000000..0b27a73b2ae --- /dev/null +++ b/src/main/java/org/elasticsearch/alerts/condition/ConditionBuilders.java @@ -0,0 +1,31 @@ +/* + * 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.condition; + +import org.elasticsearch.alerts.condition.script.ScriptCondition; +import org.elasticsearch.alerts.condition.simple.AlwaysTrueCondition; + +/** + * + */ +public final class ConditionBuilders { + + private ConditionBuilders() { + } + + public static AlwaysTrueCondition.SourceBuilder alwaysTrueCondition() { + return AlwaysTrueCondition.SourceBuilder.INSTANCE; + } + + public static ScriptCondition.SourceBuilder scriptCondition() { + return new ScriptCondition.SourceBuilder(); + } + + public static ScriptCondition.SourceBuilder scriptCondition(String script) { + return new ScriptCondition.SourceBuilder().script(script); + } + +} diff --git a/src/main/java/org/elasticsearch/alerts/condition/ConditionRegistry.java b/src/main/java/org/elasticsearch/alerts/condition/ConditionRegistry.java index f0e3af172fe..5a05ec77bd6 100644 --- a/src/main/java/org/elasticsearch/alerts/condition/ConditionRegistry.java +++ b/src/main/java/org/elasticsearch/alerts/condition/ConditionRegistry.java @@ -11,6 +11,7 @@ import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; import java.util.Map; +import java.util.Set; /** * @@ -24,6 +25,10 @@ public class ConditionRegistry { this.parsers = ImmutableMap.copyOf(parsers); } + public Set types() { + return parsers.keySet(); + } + /** * Reads the contents of parser to create the correct Condition * diff --git a/src/main/java/org/elasticsearch/alerts/condition/script/ScriptCondition.java b/src/main/java/org/elasticsearch/alerts/condition/script/ScriptCondition.java index 300a8551ae7..86e6d6badc6 100644 --- a/src/main/java/org/elasticsearch/alerts/condition/script/ScriptCondition.java +++ b/src/main/java/org/elasticsearch/alerts/condition/script/ScriptCondition.java @@ -18,8 +18,11 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.script.ExecutableScript; +import org.elasticsearch.script.ScriptService; import java.io.IOException; +import java.util.Collections; +import java.util.Map; /** * This class executes a script against the ctx payload and returns a boolean @@ -61,6 +64,23 @@ public class ScriptCondition extends Condition { return script.toXContent(builder, params); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ScriptCondition that = (ScriptCondition) o; + + if (!script.equals(that.script)) return false; + + return true; + } + + @Override + public int hashCode() { + return script.hashCode(); + } + public static class Parser extends AbstractComponent implements Condition.Parser { private final ScriptServiceProxy scriptService; @@ -131,6 +151,43 @@ public class ScriptCondition extends Condition { .field(MET_FIELD.getPreferredName(), met()) .endObject(); } + } + public static class SourceBuilder implements Condition.SourceBuilder { + + private String script; + private String lang = ScriptService.DEFAULT_LANG; + private ScriptService.ScriptType type = ScriptService.ScriptType.INLINE; + private Map params = Collections.emptyMap(); + + public SourceBuilder script(String script) { + this.script = script; + return this; + } + + public SourceBuilder lang(String lang) { + this.lang = lang; + return this; + } + + public SourceBuilder type(ScriptService.ScriptType type) { + this.type = type; + return this; + } + + public SourceBuilder type(Map params) { + this.params = params; + return this; + } + + @Override + public String type() { + return TYPE; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return new Script(script, type, lang, this.params).toXContent(builder, params); + } } } diff --git a/src/main/java/org/elasticsearch/alerts/condition/simple/AlwaysFalseCondition.java b/src/main/java/org/elasticsearch/alerts/condition/simple/AlwaysFalseCondition.java index 5c80e09f900..c6fbe266239 100644 --- a/src/main/java/org/elasticsearch/alerts/condition/simple/AlwaysFalseCondition.java +++ b/src/main/java/org/elasticsearch/alerts/condition/simple/AlwaysFalseCondition.java @@ -12,6 +12,7 @@ import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; @@ -22,6 +23,7 @@ import java.io.IOException; public class AlwaysFalseCondition extends Condition { public static final String TYPE = "always_false"; + public static final Result RESULT = new Result(TYPE, false) { @Override @@ -50,6 +52,11 @@ public class AlwaysFalseCondition extends Condition { return builder.startObject().endObject(); } + @Override + public boolean equals(Object obj) { + return obj instanceof AlwaysFalseCondition; + } + public static class Parser extends AbstractComponent implements Condition.Parser { @Inject @@ -80,4 +87,22 @@ public class AlwaysFalseCondition extends Condition { } } + public static class SourceBuilder implements Condition.SourceBuilder { + + public static final SourceBuilder INSTANCE = new SourceBuilder(); + + private SourceBuilder() { + } + + @Override + public String type() { + return TYPE; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.startObject().endObject(); + } + } + } diff --git a/src/main/java/org/elasticsearch/alerts/condition/simple/AlwaysTrueCondition.java b/src/main/java/org/elasticsearch/alerts/condition/simple/AlwaysTrueCondition.java index f734d23d938..6c29229d40c 100644 --- a/src/main/java/org/elasticsearch/alerts/condition/simple/AlwaysTrueCondition.java +++ b/src/main/java/org/elasticsearch/alerts/condition/simple/AlwaysTrueCondition.java @@ -22,6 +22,7 @@ import java.io.IOException; public class AlwaysTrueCondition extends Condition { public static final String TYPE = "always_true"; + public static final Result RESULT = new Result(TYPE, true) { @Override @@ -49,6 +50,10 @@ public class AlwaysTrueCondition extends Condition { return builder.startObject().endObject(); } + @Override + public boolean equals(Object obj) { + return obj instanceof AlwaysTrueCondition; + } public static class Parser extends AbstractComponent implements Condition.Parser { @@ -87,4 +92,21 @@ public class AlwaysTrueCondition extends Condition { } } + public static class SourceBuilder implements Condition.SourceBuilder { + + public static final SourceBuilder INSTANCE = new SourceBuilder(); + + private SourceBuilder() { + } + + @Override + public String type() { + return TYPE; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.startObject().endObject(); + } + } } diff --git a/src/main/java/org/elasticsearch/alerts/history/HistoryStore.java b/src/main/java/org/elasticsearch/alerts/history/HistoryStore.java index 45f76ff238c..24ef2828dca 100644 --- a/src/main/java/org/elasticsearch/alerts/history/HistoryStore.java +++ b/src/main/java/org/elasticsearch/alerts/history/HistoryStore.java @@ -16,6 +16,7 @@ import org.elasticsearch.alerts.support.TemplateUtils; import org.elasticsearch.alerts.support.init.proxy.ClientProxy; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.joda.time.DateTime; @@ -74,8 +75,9 @@ public class HistoryStore extends AbstractComponent { public void update(FiredAlert firedAlert) throws HistoryException { logger.debug("updating fired alert [{}]", firedAlert); try { + BytesReference bytes = XContentFactory.jsonBuilder().value(firedAlert).bytes(); IndexResponse response = client.prepareIndex(getAlertHistoryIndexNameForTime(firedAlert.scheduledTime()), ALERT_HISTORY_TYPE, firedAlert.id()) - .setSource(XContentFactory.jsonBuilder().value(firedAlert)) + .setSource(bytes) .setVersion(firedAlert.version()) .get(); firedAlert.version(response.getVersion()); diff --git a/src/main/java/org/elasticsearch/alerts/input/Input.java b/src/main/java/org/elasticsearch/alerts/input/Input.java index d3dcf2ae80c..a34c09de20c 100644 --- a/src/main/java/org/elasticsearch/alerts/input/Input.java +++ b/src/main/java/org/elasticsearch/alerts/input/Input.java @@ -88,4 +88,10 @@ public abstract class Input implements ToXContent { protected abstract XContentBuilder toXContentBody(XContentBuilder builder, Params params) throws IOException; } + + public static interface SourceBuilder extends ToXContent { + + String type(); + + } } diff --git a/src/main/java/org/elasticsearch/alerts/input/InputBuilders.java b/src/main/java/org/elasticsearch/alerts/input/InputBuilders.java new file mode 100644 index 00000000000..d7215c703d8 --- /dev/null +++ b/src/main/java/org/elasticsearch/alerts/input/InputBuilders.java @@ -0,0 +1,39 @@ +/* + * 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.input; + +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchRequestBuilder; +import org.elasticsearch.alerts.input.search.SearchInput; +import org.elasticsearch.alerts.input.simple.SimpleInput; + +import java.util.HashMap; +import java.util.Map; + +/** + * + */ +public final class InputBuilders { + + private InputBuilders() { + } + + public static SearchInput.SourceBuilder searchInput(SearchRequest request) { + return new SearchInput.SourceBuilder(request); + } + + public static SearchInput.SourceBuilder searchInput(SearchRequestBuilder builder) { + return searchInput(builder.request()); + } + + public static SimpleInput.SourceBuilder simpleInput() { + return simpleInput(new HashMap()); + } + + public static SimpleInput.SourceBuilder simpleInput(Map data) { + return new SimpleInput.SourceBuilder(data); + } +} diff --git a/src/main/java/org/elasticsearch/alerts/input/NoneInput.java b/src/main/java/org/elasticsearch/alerts/input/NoneInput.java new file mode 100644 index 00000000000..47407734e7d --- /dev/null +++ b/src/main/java/org/elasticsearch/alerts/input/NoneInput.java @@ -0,0 +1,128 @@ +/* + * 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.input; + +import org.elasticsearch.alerts.ExecutionContext; +import org.elasticsearch.alerts.Payload; +import org.elasticsearch.common.collect.ImmutableMap; +import org.elasticsearch.common.component.AbstractComponent; +import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Map; + +/** + * + */ +public class NoneInput extends Input { + + public static final String TYPE = "none"; + + private static final Payload EMPTY_PAYLOAD = new Payload() { + @Override + public Map data() { + return ImmutableMap.of(); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.startObject().endObject(); + } + }; + + public NoneInput(ESLogger logger) { + super(logger); + } + + @Override + public String type() { + return TYPE; + } + + @Override + public Result execute(ExecutionContext ctx) throws IOException { + return Result.INSTANCE; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.startObject().endObject(); + } + + public static class Result extends Input.Result { + + static final Result INSTANCE = new Result(); + + private Result() { + super(TYPE, EMPTY_PAYLOAD); + } + + @Override + protected XContentBuilder toXContentBody(XContentBuilder builder, Params params) throws IOException { + return builder.startObject().endObject(); + } + } + + public static class Parser extends AbstractComponent implements Input.Parser { + + private final NoneInput input; + + public Parser(Settings settings) { + super(settings); + this.input = new NoneInput(logger); + } + + @Override + public String type() { + return TYPE; + } + + @Override + public NoneInput parse(XContentParser parser) throws IOException { + if (parser.currentToken() != XContentParser.Token.START_OBJECT) { + + } + parser.nextToken(); + if (parser.currentToken() != XContentParser.Token.END_OBJECT) { + + } + return input; + } + + @Override + public Result parseResult(XContentParser parser) throws IOException { + if (parser.currentToken() != XContentParser.Token.START_OBJECT) { + + } + parser.nextToken(); + if (parser.currentToken() != XContentParser.Token.END_OBJECT) { + + } + return Result.INSTANCE; + } + } + + public static class SourceBuilder implements Input.SourceBuilder { + + public static final SourceBuilder INSTANCE = new SourceBuilder(); + + private SourceBuilder() { + } + + @Override + public String type() { + return TYPE; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.startObject().endObject(); + } + } +} 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 279dfb16a1d..29f4b9df89b 100644 --- a/src/main/java/org/elasticsearch/alerts/input/search/SearchInput.java +++ b/src/main/java/org/elasticsearch/alerts/input/search/SearchInput.java @@ -13,6 +13,7 @@ import org.elasticsearch.alerts.Payload; import org.elasticsearch.alerts.input.Input; import org.elasticsearch.alerts.input.InputException; import org.elasticsearch.alerts.support.AlertUtils; +import org.elasticsearch.alerts.support.SearchRequestEquivalence; import org.elasticsearch.alerts.support.Variables; import org.elasticsearch.alerts.support.init.proxy.ClientProxy; import org.elasticsearch.alerts.support.init.proxy.ScriptServiceProxy; @@ -44,6 +45,7 @@ import static org.elasticsearch.alerts.support.AlertsDateUtils.formatDate; public class SearchInput extends Input { public static final String TYPE = "search"; + public static final SearchType DEFAULT_SEARCH_TYPE = SearchType.COUNT; private final SearchRequest searchRequest; @@ -86,12 +88,25 @@ public class SearchInput extends Input { @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(); - builder.field(Parser.REQUEST_FIELD.getPreferredName()); - AlertUtils.writeSearchRequest(searchRequest, builder, params); - return builder.endObject(); + return AlertUtils.writeSearchRequest(searchRequest, builder, params); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SearchInput that = (SearchInput) o; + + if (!SearchRequestEquivalence.INSTANCE.equivalent(searchRequest, that.searchRequest)) return false; + + return true; + } + + @Override + public int hashCode() { + return SearchRequestEquivalence.INSTANCE.hash(searchRequest); + } /** * Creates a new search request applying the scheduledFireTime and fireTime to the original request @@ -136,11 +151,8 @@ public class SearchInput extends Input { @Override protected XContentBuilder toXContentBody(XContentBuilder builder, Params params) throws IOException { - if (request != null) { - builder.field(Parser.REQUEST_FIELD.getPreferredName()); - AlertUtils.writeSearchRequest(request, builder, params); - } - return builder; + builder.field(Parser.REQUEST_FIELD.getPreferredName()); + return AlertUtils.writeSearchRequest(request, builder, params); } } @@ -164,27 +176,10 @@ public class SearchInput extends Input { @Override public SearchInput parse(XContentParser parser) throws IOException { - SearchRequest request = null; - - String currentFieldName = null; - XContentParser.Token token; - - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.START_OBJECT && currentFieldName != null) { - if (REQUEST_FIELD.match(currentFieldName)) { - request = AlertUtils.readSearchRequest(parser, DEFAULT_SEARCH_TYPE); - } else { - throw new InputException("unable to parse [" + TYPE + "] input. unexpected field [" + currentFieldName + "]"); - } - } - } - + SearchRequest request = AlertUtils.readSearchRequest(parser, DEFAULT_SEARCH_TYPE); if (request == null) { - throw new InputException("search request is missing or null for [" + TYPE + "] input"); + throw new InputException("could not parse [search] input. search request is missing or null."); } - return new SearchInput(logger, scriptService, client, request); } @@ -222,4 +217,23 @@ public class SearchInput extends Input { return new Result(TYPE, payload, request); } } + + public static class SourceBuilder implements Input.SourceBuilder { + + private final SearchRequest request; + + public SourceBuilder(SearchRequest request) { + this.request = request; + } + + @Override + public String type() { + return TYPE; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return AlertUtils.writeSearchRequest(request, builder, params); + } + } } diff --git a/src/main/java/org/elasticsearch/alerts/input/simple/SimpleInput.java b/src/main/java/org/elasticsearch/alerts/input/simple/SimpleInput.java index 3f0b4182bd6..d6fc921c23c 100644 --- a/src/main/java/org/elasticsearch/alerts/input/simple/SimpleInput.java +++ b/src/main/java/org/elasticsearch/alerts/input/simple/SimpleInput.java @@ -13,10 +13,14 @@ import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; /** * This class just defines a simple xcontent map as an input @@ -24,6 +28,7 @@ import java.io.IOException; public class SimpleInput extends Input { public static final String TYPE = "simple"; + private final Payload payload; public SimpleInput(ESLogger logger, Payload payload) { @@ -48,6 +53,23 @@ public class SimpleInput extends Input { return builder.endObject(); } + @Override + public int hashCode() { + return Objects.hash(payload); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final SimpleInput other = (SimpleInput) obj; + return Objects.equals(this.payload.data(), other.payload.data()); + } + public static class Result extends Input.Result { public Result(String type, Payload payload) { @@ -122,4 +144,29 @@ public class SimpleInput extends Input { return new Result(TYPE, payload); } } + + public static class SourceBuilder implements Input.SourceBuilder { + + private Map data; + + public SourceBuilder(Map data) { + this.data = data; + } + + public Input.SourceBuilder put(String key, Object value) { + data.put(key, value); + return this; + } + + @Override + public String type() { + return TYPE; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.map(data); + + } + } } diff --git a/src/main/java/org/elasticsearch/alerts/rest/action/RestPutAlertAction.java b/src/main/java/org/elasticsearch/alerts/rest/action/RestPutAlertAction.java index ae52f7ddfee..54c403bacbf 100644 --- a/src/main/java/org/elasticsearch/alerts/rest/action/RestPutAlertAction.java +++ b/src/main/java/org/elasticsearch/alerts/rest/action/RestPutAlertAction.java @@ -40,7 +40,7 @@ public class RestPutAlertAction extends BaseRestHandler { protected void handleRequest(RestRequest request, RestChannel channel, Client client) throws Exception { PutAlertRequest putAlertRequest = new PutAlertRequest(); putAlertRequest.setAlertName(request.param("name")); - putAlertRequest.setAlertSource(request.content(), request.contentUnsafe()); + putAlertRequest.source(request.content(), request.contentUnsafe()); alertsClient.putAlert(putAlertRequest, new RestBuilderListener(channel) { @Override public RestResponse buildResponse(PutAlertResponse response, XContentBuilder builder) throws Exception { diff --git a/src/main/java/org/elasticsearch/alerts/scheduler/schedule/ScheduleRegistry.java b/src/main/java/org/elasticsearch/alerts/scheduler/schedule/ScheduleRegistry.java index db8d8246001..abde920456c 100644 --- a/src/main/java/org/elasticsearch/alerts/scheduler/schedule/ScheduleRegistry.java +++ b/src/main/java/org/elasticsearch/alerts/scheduler/schedule/ScheduleRegistry.java @@ -12,6 +12,7 @@ import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; import java.util.Map; +import java.util.Set; /** * @@ -25,6 +26,10 @@ public class ScheduleRegistry { this.parsers = ImmutableMap.copyOf(parsers); } + public Set types() { + return parsers.keySet(); + } + public Schedule parse(XContentParser parser) throws IOException { String type = null; XContentParser.Token token; diff --git a/src/main/java/org/elasticsearch/alerts/support/AlertUtils.java b/src/main/java/org/elasticsearch/alerts/support/AlertUtils.java index 037e007a10b..11904070466 100644 --- a/src/main/java/org/elasticsearch/alerts/support/AlertUtils.java +++ b/src/main/java/org/elasticsearch/alerts/support/AlertUtils.java @@ -155,13 +155,19 @@ public final class AlertUtils { /** * Writes the searchRequest to the specified builder. */ - public static void writeSearchRequest(SearchRequest searchRequest, XContentBuilder builder, ToXContent.Params params) throws IOException { + public static XContentBuilder writeSearchRequest(SearchRequest searchRequest, XContentBuilder builder, ToXContent.Params params) throws IOException { if (searchRequest == null) { builder.nullValue(); - return; + return builder; } builder.startObject(); + if (searchRequest.searchType() != null) { + builder.field("search_type", searchRequest.searchType().toString().toLowerCase(Locale.ENGLISH)); + } + if (searchRequest.indices() != null) { + builder.array("indices", searchRequest.indices()); + } if (Strings.hasLength(searchRequest.source())) { XContentHelper.writeRawField("body", searchRequest.source(), builder, params); } @@ -171,11 +177,6 @@ public final class AlertUtils { if (searchRequest.templateType() != null) { builder.field("template_type", searchRequest.templateType().name().toLowerCase(Locale.ROOT)); } - builder.startArray("indices"); - for (String index : searchRequest.indices()) { - builder.value(index); - } - builder.endArray(); if (searchRequest.indicesOptions() != DEFAULT_INDICES_OPTIONS) { IndicesOptions options = searchRequest.indicesOptions(); builder.startObject("indices_options"); @@ -194,10 +195,7 @@ public final class AlertUtils { builder.field("allow_no_indices", options.allowNoIndices()); builder.endObject(); } - if (searchRequest.searchType() != null) { - builder.field("search_type", searchRequest.searchType().toString().toLowerCase(Locale.ENGLISH)); - } - builder.endObject(); + return builder.endObject(); } } diff --git a/src/main/java/org/elasticsearch/alerts/support/Script.java b/src/main/java/org/elasticsearch/alerts/support/Script.java index f26509fb078..12c156e3156 100644 --- a/src/main/java/org/elasticsearch/alerts/support/Script.java +++ b/src/main/java/org/elasticsearch/alerts/support/Script.java @@ -91,7 +91,7 @@ public class Script implements ToXContent { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { return builder.startObject() .field(SCRIPT_FIELD.getPreferredName(), script) - .field(TYPE_FIELD.getPreferredName(), type) + .field(TYPE_FIELD.getPreferredName(), type.name().toLowerCase(Locale.ROOT)) .field(LANG_FIELD.getPreferredName(), lang) .field(PARAMS_FIELD.getPreferredName(), this.params) .endObject(); diff --git a/src/main/java/org/elasticsearch/alerts/support/SearchRequestEquivalence.java b/src/main/java/org/elasticsearch/alerts/support/SearchRequestEquivalence.java new file mode 100644 index 00000000000..5f41048597f --- /dev/null +++ b/src/main/java/org/elasticsearch/alerts/support/SearchRequestEquivalence.java @@ -0,0 +1,54 @@ +/* + * 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.action.search.SearchRequest; +import org.elasticsearch.alerts.AlertsException; +import org.elasticsearch.common.base.Equivalence; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.io.stream.BytesStreamOutput; +import java.io.IOException; +import java.util.Arrays; + +/** + * The only true way today to compare search request object (outside of core) is to + * serialize it and compare the serialized output. this is heavy obviously, but luckily we + * don't compare search requests in normal runtime... we only do it in the tests. The is here basically + * due to the lack of equals/hashcode support in SearchRequest in core. + */ +public final class SearchRequestEquivalence extends Equivalence { + + public static final SearchRequestEquivalence INSTANCE = new SearchRequestEquivalence(); + + private SearchRequestEquivalence() { + } + + @Override + protected boolean doEquivalent(SearchRequest r1, SearchRequest r2) { + try { + BytesStreamOutput output1 = new BytesStreamOutput(); + r1.writeTo(output1); + BytesReference bytes1 = output1.bytes(); + BytesStreamOutput output2 = new BytesStreamOutput(bytes1.length()); + r2.writeTo(output2); + BytesReference bytes2 = output2.bytes(); + return Arrays.equals(bytes1.toBytes(), bytes2.toBytes()); + } catch (IOException ioe) { + throw new AlertsException("could not compare search requests", ioe); + } + } + + @Override + protected int doHash(SearchRequest request) { + try { + BytesStreamOutput output = new BytesStreamOutput(); + request.writeTo(output); + return Arrays.hashCode(output.bytes().toBytes()); + } catch (IOException ioe) { + throw new AlertsException("could not compute hashcode for search request", ioe); + } + } +} diff --git a/src/main/java/org/elasticsearch/alerts/support/template/XContentTemplate.java b/src/main/java/org/elasticsearch/alerts/support/template/XContentTemplate.java index 96474e20292..d7e438832f6 100644 --- a/src/main/java/org/elasticsearch/alerts/support/template/XContentTemplate.java +++ b/src/main/java/org/elasticsearch/alerts/support/template/XContentTemplate.java @@ -7,6 +7,7 @@ package org.elasticsearch.alerts.support.template; import org.elasticsearch.common.xcontent.XContent; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.common.xcontent.yaml.YamlXContent; import java.io.IOException; @@ -18,6 +19,7 @@ import java.util.Map; public class XContentTemplate implements Template { public static XContentTemplate YAML = new XContentTemplate(YamlXContent.yamlXContent); + public static XContentTemplate JSON = new XContentTemplate(JsonXContent.jsonXContent); private final XContent xContent; diff --git a/src/main/java/org/elasticsearch/alerts/transform/ChainTransform.java b/src/main/java/org/elasticsearch/alerts/transform/ChainTransform.java index 0304f851cda..949a4447530 100644 --- a/src/main/java/org/elasticsearch/alerts/transform/ChainTransform.java +++ b/src/main/java/org/elasticsearch/alerts/transform/ChainTransform.java @@ -72,7 +72,11 @@ public class ChainTransform extends Transform { @Override public void init(Injector injector) { - this.registry = injector.getInstance(TransformRegistry.class); + init(injector.getInstance(TransformRegistry.class)); + } + + public void init(TransformRegistry registry) { + this.registry = registry; } @Override @@ -106,8 +110,36 @@ public class ChainTransform extends Transform { } return new ChainTransform(builder.build()); } - } + public static class SourceBuilder implements Transform.SourceBuilder { + + private final ImmutableList.Builder builders = ImmutableList.builder(); + + @Override + public String type() { + return TYPE; + } + + public SourceBuilder(Transform.SourceBuilder... builders) { + this.builders.add(builders); + } + + public SourceBuilder add(Transform.SourceBuilder builder) { + builders.add(builder); + return this; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startArray(); + for (Transform.SourceBuilder transBuilder : builders.build()) { + builder.startObject() + .field(TYPE, transBuilder) + .endObject(); + } + return builder.endArray(); + } + } } diff --git a/src/main/java/org/elasticsearch/alerts/transform/ScriptTransform.java b/src/main/java/org/elasticsearch/alerts/transform/ScriptTransform.java index 59d1d10547d..0e12edde561 100644 --- a/src/main/java/org/elasticsearch/alerts/transform/ScriptTransform.java +++ b/src/main/java/org/elasticsearch/alerts/transform/ScriptTransform.java @@ -26,12 +26,12 @@ public class ScriptTransform extends Transform { public static final String TYPE = "script"; - private final Script script; private final ScriptServiceProxy scriptService; + private final Script script; - public ScriptTransform(Script script, ScriptServiceProxy scriptService) { - this.script = script; + public ScriptTransform(ScriptServiceProxy scriptService, Script script) { this.scriptService = scriptService; + this.script = script; } @Override @@ -83,7 +83,30 @@ public class ScriptTransform extends Transform { } catch (Script.ParseException pe) { throw new AlertsSettingsException("could not parse [script] transform", pe); } - return new ScriptTransform(script, scriptService); + return new ScriptTransform(scriptService, script); + } + } + + public static class SourceBuilder implements Transform.SourceBuilder { + + private final Script script; + + public SourceBuilder(String script) { + this(new Script(script)); + } + + public SourceBuilder(Script script) { + this.script = script; + } + + @Override + public String type() { + return TYPE; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return script.toXContent(builder, params); } } } diff --git a/src/main/java/org/elasticsearch/alerts/transform/SearchTransform.java b/src/main/java/org/elasticsearch/alerts/transform/SearchTransform.java index 234f22c98d1..2fc37290c19 100644 --- a/src/main/java/org/elasticsearch/alerts/transform/SearchTransform.java +++ b/src/main/java/org/elasticsearch/alerts/transform/SearchTransform.java @@ -48,6 +48,7 @@ public class SearchTransform extends Transform { protected final ESLogger logger; protected final ScriptServiceProxy scriptService; protected final ClientProxy client; + protected final SearchRequest request; public SearchTransform(ESLogger logger, ScriptServiceProxy scriptService, ClientProxy client, SearchRequest request) { @@ -71,8 +72,7 @@ public class SearchTransform extends Transform { @Override public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { - AlertUtils.writeSearchRequest(request, builder, params); - return builder; + return AlertUtils.writeSearchRequest(request, builder, params); } public SearchRequest createRequest(SearchRequest requestPrototype, ExecutionContext ctx, Payload payload) throws IOException { @@ -160,4 +160,23 @@ public class SearchTransform extends Transform { } } + public static class SourceBuilder implements Transform.SourceBuilder { + + private final SearchRequest request; + + public SourceBuilder(SearchRequest request) { + this.request = request; + } + + @Override + public String type() { + return TYPE; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return AlertUtils.writeSearchRequest(request, builder, params); + } + } + } diff --git a/src/main/java/org/elasticsearch/alerts/transform/Transform.java b/src/main/java/org/elasticsearch/alerts/transform/Transform.java index 2cdf04c1ecf..a84e7db2d7f 100644 --- a/src/main/java/org/elasticsearch/alerts/transform/Transform.java +++ b/src/main/java/org/elasticsearch/alerts/transform/Transform.java @@ -69,7 +69,7 @@ public abstract class Transform implements ToXContent { } } - static interface Parser { + public static interface Parser { String type(); @@ -77,4 +77,9 @@ public abstract class Transform implements ToXContent { } + public static interface SourceBuilder extends ToXContent { + + String type(); + } + } diff --git a/src/main/java/org/elasticsearch/alerts/transform/TransformBuilders.java b/src/main/java/org/elasticsearch/alerts/transform/TransformBuilders.java new file mode 100644 index 00000000000..a688b0bb446 --- /dev/null +++ b/src/main/java/org/elasticsearch/alerts/transform/TransformBuilders.java @@ -0,0 +1,39 @@ +/* + * 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.transform; + +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.alerts.support.Script; +import org.elasticsearch.alerts.transform.ChainTransform; +import org.elasticsearch.alerts.transform.ScriptTransform; +import org.elasticsearch.alerts.transform.SearchTransform; +import org.elasticsearch.alerts.transform.Transform; + +/** + * + */ +public final class TransformBuilders { + + private TransformBuilders() { + } + + public static SearchTransform.SourceBuilder searchTransform(SearchRequest request) { + return new SearchTransform.SourceBuilder(request); + } + + public static ScriptTransform.SourceBuilder scriptTransform(String script) { + return new ScriptTransform.SourceBuilder(script); + } + + public static ScriptTransform.SourceBuilder scriptTransform(Script script) { + return new ScriptTransform.SourceBuilder(script); + } + + public static ChainTransform.SourceBuilder chainTransform(Transform.SourceBuilder... transforms) { + return new ChainTransform.SourceBuilder(transforms); + } + +} diff --git a/src/main/java/org/elasticsearch/alerts/transform/TransformRegistry.java b/src/main/java/org/elasticsearch/alerts/transform/TransformRegistry.java index 99851e380b4..5fb3db2a748 100644 --- a/src/main/java/org/elasticsearch/alerts/transform/TransformRegistry.java +++ b/src/main/java/org/elasticsearch/alerts/transform/TransformRegistry.java @@ -32,7 +32,7 @@ public class TransformRegistry { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { type = parser.currentName(); - } else if (token == XContentParser.Token.START_OBJECT && type != null) { + } else if (type != null) { Transform.Parser transformParser = parsers.get(type); if (transformParser == null) { throw new AlertsSettingsException("unknown transform type [" + type + "]"); diff --git a/src/main/java/org/elasticsearch/alerts/transport/actions/put/PutAlertRequest.java b/src/main/java/org/elasticsearch/alerts/transport/actions/put/PutAlertRequest.java index 286d16cf2bc..f836fd9a90d 100644 --- a/src/main/java/org/elasticsearch/alerts/transport/actions/put/PutAlertRequest.java +++ b/src/main/java/org/elasticsearch/alerts/transport/actions/put/PutAlertRequest.java @@ -9,9 +9,12 @@ package org.elasticsearch.alerts.transport.actions.put; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.ValidateActions; import org.elasticsearch.action.support.master.MasterNodeOperationRequest; +import org.elasticsearch.alerts.client.AlertSourceBuilder; +import org.elasticsearch.client.Requests; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.XContentType; import java.io.IOException; @@ -59,7 +62,14 @@ public class PutAlertRequest extends MasterNodeOperationRequest /** * Set the source of the alert */ - public void setAlertSource(BytesReference alertSource) { + public void source(AlertSourceBuilder source) { + source(source.buildAsBytes(XContentType.JSON)); + } + + /** + * Set the source of the alert + */ + public void source(BytesReference alertSource) { this.alertSource = alertSource; this.alertSourceUnsafe = false; } @@ -67,7 +77,7 @@ public class PutAlertRequest extends MasterNodeOperationRequest /** * Set the source of the alert with boolean to control source safety */ - public void setAlertSource(BytesReference alertSource, boolean alertSourceUnsafe) { + public void source(BytesReference alertSource, boolean alertSourceUnsafe) { this.alertSource = alertSource; this.alertSourceUnsafe = alertSourceUnsafe; } diff --git a/src/main/java/org/elasticsearch/alerts/transport/actions/put/PutAlertRequestBuilder.java b/src/main/java/org/elasticsearch/alerts/transport/actions/put/PutAlertRequestBuilder.java index 05251846f06..7e0f57baf46 100644 --- a/src/main/java/org/elasticsearch/alerts/transport/actions/put/PutAlertRequestBuilder.java +++ b/src/main/java/org/elasticsearch/alerts/transport/actions/put/PutAlertRequestBuilder.java @@ -7,6 +7,7 @@ package org.elasticsearch.alerts.transport.actions.put; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.master.MasterNodeOperationRequestBuilder; +import org.elasticsearch.alerts.client.AlertSourceBuilder; import org.elasticsearch.alerts.client.AlertsClient; import org.elasticsearch.client.Client; import org.elasticsearch.common.bytes.BytesReference; @@ -28,19 +29,26 @@ public class PutAlertRequestBuilder extends MasterNodeOperationRequestBuilder listener) { diff --git a/src/test/java/org/elasticsearch/alerts/AlertSerializationTest.java b/src/test/java/org/elasticsearch/alerts/AlertSerializationTest.java deleted file mode 100644 index f8bc75a0a6a..00000000000 --- a/src/test/java/org/elasticsearch/alerts/AlertSerializationTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.alerts; - -import org.elasticsearch.alerts.actions.Action; -import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.junit.Test; - -import java.io.IOException; - -public class AlertSerializationTest extends AbstractAlertingTests { - - @Test - public void testAlertSerialization() throws Exception { - - Alert alert = createTestAlert("test-serialization"); - - - XContentBuilder jsonBuilder = XContentFactory.jsonBuilder(); - alert.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS); - - final Alert.Parser alertParser = - internalTestCluster().getInstance(Alert.Parser.class, internalTestCluster().getMasterName()); - - Alert parsedAlert = alertParser.parse("test-serialization", true, jsonBuilder.bytes()); - - long parsedActionCount = 0; - - long alertActionCount = 0; - for (Action action : parsedAlert.actions()) { - boolean found = false; - ++parsedActionCount; - alertActionCount = 0; - for (Action action1 : alert.actions()) { - ++alertActionCount; - if (action.type().equals(action1.type())) { - assertEqualByGeneratedXContent(action, action1); - found = true; - } - } - assertTrue(found); - } - assertEquals(parsedActionCount, alertActionCount); - - assertEquals(parsedAlert,alert); - - assertEquals(parsedAlert.status().version(), alert.status().version()); - if (parsedAlert.status().lastExecuted() == null ) { - assertNull(alert.status().lastExecuted()); - } else { - assertEquals(parsedAlert.status().lastExecuted().getMillis(), alert.status().lastExecuted().getMillis()); - } - assertEqualByGeneratedXContent(parsedAlert.schedule(), alert.schedule()); - assertEqualByGeneratedXContent(parsedAlert.condition(), alert.condition()); - assertEquals(parsedAlert.throttlePeriod().getMillis(), alert.throttlePeriod().getMillis()); - assertEquals(parsedAlert.status().ackStatus().state(), alert.status().ackStatus().state()); - assertEquals(parsedAlert.metadata().get("foo"), "bar"); - } - - - private void assertEqualByGeneratedXContent(ToXContent xCon1, ToXContent xCon2) throws IOException { - XContentBuilder builder1 = XContentFactory.jsonBuilder(); - XContentBuilder builder2 = XContentFactory.jsonBuilder(); - xCon1.toXContent(builder1, ToXContent.EMPTY_PARAMS); - xCon2.toXContent(builder2, ToXContent.EMPTY_PARAMS); - assertEquals(builder1.bytes().toUtf8(), builder2.bytes().toUtf8()); - - } - -} diff --git a/src/test/java/org/elasticsearch/alerts/AlertTests.java b/src/test/java/org/elasticsearch/alerts/AlertTests.java new file mode 100644 index 00000000000..7dda15cdd4e --- /dev/null +++ b/src/test/java/org/elasticsearch/alerts/AlertTests.java @@ -0,0 +1,281 @@ +/* + * 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; + +import com.carrotsearch.randomizedtesting.annotations.Repeat; +import org.elasticsearch.alerts.actions.Action; +import org.elasticsearch.alerts.actions.ActionRegistry; +import org.elasticsearch.alerts.actions.Actions; +import org.elasticsearch.alerts.actions.email.EmailAction; +import org.elasticsearch.alerts.actions.email.service.Email; +import org.elasticsearch.alerts.actions.email.service.EmailService; +import org.elasticsearch.alerts.actions.email.service.Profile; +import org.elasticsearch.alerts.actions.index.IndexAction; +import org.elasticsearch.alerts.actions.webhook.HttpClient; +import org.elasticsearch.alerts.actions.webhook.WebhookAction; +import org.elasticsearch.alerts.condition.Condition; +import org.elasticsearch.alerts.condition.ConditionRegistry; +import org.elasticsearch.alerts.condition.script.ScriptCondition; +import org.elasticsearch.alerts.condition.simple.AlwaysTrueCondition; +import org.elasticsearch.alerts.input.Input; +import org.elasticsearch.alerts.input.InputRegistry; +import org.elasticsearch.alerts.input.search.SearchInput; +import org.elasticsearch.alerts.input.simple.SimpleInput; +import org.elasticsearch.alerts.scheduler.schedule.*; +import org.elasticsearch.alerts.scheduler.schedule.support.*; +import org.elasticsearch.alerts.support.Script; +import org.elasticsearch.alerts.support.init.proxy.ClientProxy; +import org.elasticsearch.alerts.support.init.proxy.ScriptServiceProxy; +import org.elasticsearch.alerts.support.template.ScriptTemplate; +import org.elasticsearch.alerts.support.template.Template; +import org.elasticsearch.alerts.test.AlertsTestUtils; +import org.elasticsearch.alerts.transform.*; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.collect.ImmutableList; +import org.elasticsearch.common.collect.ImmutableMap; +import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.common.logging.Loggers; +import org.elasticsearch.common.netty.handler.codec.http.HttpMethod; +import org.elasticsearch.common.settings.ImmutableSettings; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.test.ElasticsearchTestCase; +import org.junit.Before; +import org.junit.Test; + +import java.util.Map; + +import static org.elasticsearch.alerts.test.AlertsTestUtils.matchAllRequest; +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.Mockito.mock; + +public class AlertTests extends ElasticsearchTestCase { + + private ScriptServiceProxy scriptService; + private ClientProxy client; + private HttpClient httpClient; + private EmailService emailService; + private Template.Parser templateParser; + private ESLogger logger; + private Settings settings = ImmutableSettings.EMPTY; + + @Before + public void init() throws Exception { + scriptService = mock(ScriptServiceProxy.class); + client = mock(ClientProxy.class); + httpClient = mock(HttpClient.class); + emailService = mock(EmailService.class); + templateParser = new ScriptTemplate.Parser(settings, scriptService); + logger = Loggers.getLogger(AlertTests.class); + } + + @Test @Repeat(iterations = 20) + public void testParser_SelfGenerated() throws Exception { + + Schedule schedule = randomSchedule(); + ScheduleRegistry scheduleRegistry = registry(schedule); + + Input input = randomInput(); + InputRegistry inputRegistry = registry(input); + + Condition condition = randomCondition(); + ConditionRegistry conditionRegistry = registry(condition); + + Transform transform = randomTransform(); + TransformRegistry transformRegistry = registry(transform); + + Actions actions = randomActions(); + ActionRegistry actionRegistry = registry(actions); + + Map metadata = ImmutableMap.of("_key", "_val"); + + Alert.Status status = new Alert.Status(); + + TimeValue throttlePeriod = randomBoolean() ? null : TimeValue.timeValueSeconds(randomIntBetween(5, 10)); + + Alert alert = new Alert("_name", schedule, input, condition, transform, actions, metadata, throttlePeriod, status); + + BytesReference bytes = XContentFactory.jsonBuilder().value(alert).bytes(); + logger.info(bytes.toUtf8()); + Alert.Parser alertParser = new Alert.Parser(settings, conditionRegistry, scheduleRegistry, transformRegistry, actionRegistry, inputRegistry); + + boolean includeStatus = randomBoolean(); + Alert parsedAlert = alertParser.parse("_name", includeStatus, bytes); + + if (includeStatus) { + assertThat(parsedAlert.status(), equalTo(status)); + } + assertThat(parsedAlert.schedule(), equalTo(schedule)); + assertThat(parsedAlert.input(), equalTo(input)); + assertThat(parsedAlert.condition(), equalTo(condition)); + if (throttlePeriod != null) { + assertThat(parsedAlert.throttlePeriod().millis(), equalTo(throttlePeriod.millis())); + } + assertThat(parsedAlert.metadata(), equalTo(metadata)); + assertThat(parsedAlert.actions(), equalTo(actions)); + } + + private static Schedule randomSchedule() { + String type = randomFrom(CronSchedule.TYPE, HourlySchedule.TYPE, DailySchedule.TYPE, WeeklySchedule.TYPE, MonthlySchedule.TYPE, YearlySchedule.TYPE, IntervalSchedule.TYPE); + switch (type) { + case CronSchedule.TYPE: + return new CronSchedule("0/5 * * * * ? *"); + case HourlySchedule.TYPE: + return HourlySchedule.builder().minutes(30).build(); + case DailySchedule.TYPE: + return DailySchedule.builder().atNoon().build(); + case WeeklySchedule.TYPE: + return WeeklySchedule.builder().time(WeekTimes.builder().on(DayOfWeek.FRIDAY).atMidnight()).build(); + case MonthlySchedule.TYPE: + return MonthlySchedule.builder().time(MonthTimes.builder().on(1).atNoon()).build(); + case YearlySchedule.TYPE: + return YearlySchedule.builder().time(YearTimes.builder().in(Month.JANUARY).on(1).atMidnight()).build(); + default: + return new IntervalSchedule(IntervalSchedule.Interval.seconds(5)); + } + } + + private static ScheduleRegistry registry(Schedule schedule) { + ImmutableMap.Builder parsers = ImmutableMap.builder(); + switch (schedule.type()) { + case CronSchedule.TYPE: + parsers.put(CronSchedule.TYPE, new CronSchedule.Parser()); + return new ScheduleRegistry(parsers.build()); + case HourlySchedule.TYPE: + parsers.put(HourlySchedule.TYPE, new HourlySchedule.Parser()); + return new ScheduleRegistry(parsers.build()); + case DailySchedule.TYPE: + parsers.put(DailySchedule.TYPE, new DailySchedule.Parser()); + return new ScheduleRegistry(parsers.build()); + case WeeklySchedule.TYPE: + parsers.put(WeeklySchedule.TYPE, new WeeklySchedule.Parser()); + return new ScheduleRegistry(parsers.build()); + case MonthlySchedule.TYPE: + parsers.put(MonthlySchedule.TYPE, new MonthlySchedule.Parser()); + return new ScheduleRegistry(parsers.build()); + case YearlySchedule.TYPE: + parsers.put(YearlySchedule.TYPE, new YearlySchedule.Parser()); + return new ScheduleRegistry(parsers.build()); + case IntervalSchedule.TYPE: + parsers.put(IntervalSchedule.TYPE, new IntervalSchedule.Parser()); + return new ScheduleRegistry(parsers.build()); + default: + throw new IllegalArgumentException("unknown schedule [" + schedule + "]"); + } + } + + private Input randomInput() { + String type = randomFrom(SearchInput.TYPE, SimpleInput.TYPE); + switch (type) { + case SearchInput.TYPE: + return new SearchInput(logger, scriptService, client, AlertsTestUtils.newInputSearchRequest("idx")); + default: + return new SimpleInput(logger, new Payload.Simple(ImmutableMap.builder().put("_key", "_val").build())); + } + } + + private InputRegistry registry(Input input) { + ImmutableMap.Builder parsers = ImmutableMap.builder(); + switch (input.type()) { + case SearchInput.TYPE: + parsers.put(SearchInput.TYPE, new SearchInput.Parser(settings, scriptService, client)); + return new InputRegistry(parsers.build()); + default: + parsers.put(SimpleInput.TYPE, new SimpleInput.Parser(settings)); + return new InputRegistry(parsers.build()); + } + } + + private Condition randomCondition() { + String type = randomFrom(ScriptCondition.TYPE, AlwaysTrueCondition.TYPE); + switch (type) { + case ScriptCondition.TYPE: + return new ScriptCondition(logger, scriptService, new Script("_script")); + default: + return new AlwaysTrueCondition(logger); + } + } + + private ConditionRegistry registry(Condition condition) { + ImmutableMap.Builder parsers = ImmutableMap.builder(); + switch (condition.type()) { + case ScriptCondition.TYPE: + parsers.put(ScriptCondition.TYPE, new ScriptCondition.Parser(settings, scriptService)); + return new ConditionRegistry(parsers.build()); + default: + parsers.put(AlwaysTrueCondition.TYPE, new AlwaysTrueCondition.Parser(settings)); + return new ConditionRegistry(parsers.build()); + } + } + + private Transform randomTransform() { + String type = randomFrom(ScriptTransform.TYPE, SearchTransform.TYPE, ChainTransform.TYPE); + switch (type) { + case ScriptTransform.TYPE: + return new ScriptTransform(scriptService, new Script("_script")); + case SearchTransform.TYPE: + return new SearchTransform(logger, scriptService, client, matchAllRequest()); + default: // chain + return new ChainTransform(ImmutableList.of( + new SearchTransform(logger, scriptService, client, matchAllRequest()), + new ScriptTransform(scriptService, new Script("_script")))); + } + } + + private TransformRegistry registry(Transform transform) { + ImmutableMap.Builder parsers = ImmutableMap.builder(); + switch (transform.type()) { + case ScriptTransform.TYPE: + parsers.put(ScriptTransform.TYPE, new ScriptTransform.Parser(scriptService)); + return new TransformRegistry(parsers.build()); + case SearchTransform.TYPE: + parsers.put(SearchTransform.TYPE, new SearchTransform.Parser(settings, scriptService, client)); + return new TransformRegistry(parsers.build()); + default: + ChainTransform.Parser parser = new ChainTransform.Parser(); + parsers.put(ChainTransform.TYPE, parser); + parsers.put(ScriptTransform.TYPE, new ScriptTransform.Parser(scriptService)); + parsers.put(SearchTransform.TYPE, new SearchTransform.Parser(settings, scriptService, client)); + TransformRegistry registry = new TransformRegistry(parsers.build()); + parser.init(registry); + return registry; + } + } + + private Actions randomActions() { + ImmutableList.Builder list = ImmutableList.builder(); + if (randomBoolean()) { + list.add(new EmailAction(logger, emailService, Email.builder().id("prototype").build(), null, Profile.STANDARD, null, null, null, null, randomBoolean())); + } + if (randomBoolean()) { + list.add(new IndexAction(logger, client, "_index", "_type")); + } + if (randomBoolean()) { + list.add(new WebhookAction(logger, httpClient, randomFrom(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT), new ScriptTemplate(scriptService, "_url"), null)); + } + return new Actions(list.build()); + } + + private ActionRegistry registry(Actions actions) { + ImmutableMap.Builder parsers = ImmutableMap.builder(); + for (Action action : actions) { + switch (action.type()) { + case EmailAction.TYPE: + parsers.put(EmailAction.TYPE, new EmailAction.Parser(settings, emailService, templateParser)); + break; + case IndexAction.TYPE: + parsers.put(IndexAction.TYPE, new IndexAction.Parser(settings, client)); + break; + case WebhookAction.TYPE: + parsers.put(WebhookAction.TYPE, new WebhookAction.Parser(settings, templateParser, httpClient)); + break; + } + } + return new ActionRegistry(parsers.build()); + } + +} diff --git a/src/test/java/org/elasticsearch/alerts/actions/ActionsTest.java b/src/test/java/org/elasticsearch/alerts/actions/ActionsTest.java deleted file mode 100644 index 12f597d8a7f..00000000000 --- a/src/test/java/org/elasticsearch/alerts/actions/ActionsTest.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.alerts.actions; - - -import org.elasticsearch.alerts.AbstractAlertingTests; -import org.elasticsearch.alerts.Alert; -import org.elasticsearch.alerts.actions.index.IndexAction; -import org.elasticsearch.alerts.condition.Condition; -import org.elasticsearch.alerts.condition.script.ScriptCondition; -import org.elasticsearch.alerts.input.Input; -import org.elasticsearch.alerts.input.search.SearchInput; -import org.elasticsearch.alerts.scheduler.schedule.CronSchedule; -import org.elasticsearch.alerts.support.Script; -import org.elasticsearch.alerts.support.init.proxy.ClientProxy; -import org.elasticsearch.alerts.transform.SearchTransform; -import org.elasticsearch.alerts.transport.actions.delete.DeleteAlertRequest; -import org.elasticsearch.alerts.transport.actions.delete.DeleteAlertResponse; -import org.elasticsearch.alerts.transport.actions.get.GetAlertRequest; -import org.elasticsearch.alerts.transport.actions.get.GetAlertResponse; -import org.elasticsearch.alerts.transport.actions.put.PutAlertRequest; -import org.elasticsearch.alerts.transport.actions.put.PutAlertResponse; -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.script.ScriptService; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; - -/** - */ -public class ActionsTest extends AbstractAlertingTests { - - @Test - public void testAlertActions() throws Exception { - //TODO: Consider deleting this test or making it do something useful - createIndex("my-index"); - - ensureGreen("my-index"); - - client().preparePutIndexedScript() - .setScriptLang("mustache") - .setId("query") - .setSource(jsonBuilder().startObject().startObject("template").startObject("match_all").endObject().endObject().endObject()) - .get(); - - ensureAlertingStarted(); - - final Action alertAction = new IndexAction(logger, ClientProxy.of(client()), "testindex", "testtype"); - final List actionList = new ArrayList<>(); - actionList.add(alertAction); - - Input alertInput = new SearchInput(logger, scriptService(), ClientProxy.of(client()), - createConditionSearchRequest()); - Condition alertCondition = new ScriptCondition(logger, scriptService(), new Script("return true")); - - - - Alert alert = new Alert( - "my-first-alert", - new CronSchedule("0/5 * * * * ? *"), - alertInput, - alertCondition, - new SearchTransform(logger, scriptService(), ClientProxy.of(client()), createConditionSearchRequest()), - new Actions(actionList), null, new Alert.Status(), new TimeValue(0) - ); - - - XContentBuilder jsonBuilder = XContentFactory.jsonBuilder(); - alert.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS); - - PutAlertRequest alertRequest = alertClient().preparePutAlert().setAlertName("my-first-alert").setAlertSource(jsonBuilder.bytes()).request(); - PutAlertResponse alertsResponse = alertClient().putAlert(alertRequest).actionGet(); - assertNotNull(alertsResponse.indexResponse()); - assertTrue(alertsResponse.indexResponse().isCreated()); - - GetAlertRequest getAlertRequest = new GetAlertRequest(alert.name()); - GetAlertResponse getAlertResponse = alertClient().getAlert(getAlertRequest).actionGet(); - assertTrue(getAlertResponse.getResponse().isExists()); - assertEquals(((Map)getAlertResponse.getResponse().getSourceAsMap().get("schedule")).get("cron").toString(), "0/5 * * * * ? *"); - - DeleteAlertRequest deleteAlertRequest = new DeleteAlertRequest(alert.name()); - DeleteAlertResponse deleteAlertResponse = alertClient().deleteAlert(deleteAlertRequest).actionGet(); - assertNotNull(deleteAlertResponse.deleteResponse()); - assertTrue(deleteAlertResponse.deleteResponse().isFound()); - - getAlertResponse = alertClient().getAlert(getAlertRequest).actionGet(); - assertFalse(getAlertResponse.getResponse().isExists()); - - } - -} diff --git a/src/test/java/org/elasticsearch/alerts/condition/simple/TestAlwaysTrueCondition.java b/src/test/java/org/elasticsearch/alerts/condition/simple/AlwaysFalseConditionTests.java similarity index 97% rename from src/test/java/org/elasticsearch/alerts/condition/simple/TestAlwaysTrueCondition.java rename to src/test/java/org/elasticsearch/alerts/condition/simple/AlwaysFalseConditionTests.java index 296a7bd849e..648cb79a815 100644 --- a/src/test/java/org/elasticsearch/alerts/condition/simple/TestAlwaysTrueCondition.java +++ b/src/test/java/org/elasticsearch/alerts/condition/simple/AlwaysFalseConditionTests.java @@ -18,7 +18,8 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; /** */ -public class TestAlwaysTrueCondition extends ElasticsearchTestCase { +public class AlwaysFalseConditionTests extends ElasticsearchTestCase { + @Test public void testExecute() throws Exception { @@ -34,6 +35,7 @@ public class TestAlwaysTrueCondition extends ElasticsearchTestCase { builder.endObject(); XContentParser xp = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes()); xp.nextToken(); + Condition alwaysTrue = p.parse(xp); assertTrue(alwaysTrue.execute(null).met()); } @@ -47,6 +49,7 @@ public class TestAlwaysTrueCondition extends ElasticsearchTestCase { builder.endObject(); XContentParser xp = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes()); xp.nextToken(); + p.parse(xp); fail("expected a condition exception trying to parse an invalid condition XContent, [" + AlwaysTrueCondition.TYPE + "] condition should not parse with a body"); @@ -80,5 +83,4 @@ public class TestAlwaysTrueCondition extends ElasticsearchTestCase { fail("expected a condition exception trying to parse an invalid condition result XContent, [" + AlwaysTrueCondition.TYPE + "] condition result should not parse with a [met] field"); } - } diff --git a/src/test/java/org/elasticsearch/alerts/condition/simple/TestAlwaysFalseCondition.java b/src/test/java/org/elasticsearch/alerts/condition/simple/AlwaysTrueConditionTests.java similarity index 98% rename from src/test/java/org/elasticsearch/alerts/condition/simple/TestAlwaysFalseCondition.java rename to src/test/java/org/elasticsearch/alerts/condition/simple/AlwaysTrueConditionTests.java index e9c551eb96b..6436aebb89c 100644 --- a/src/test/java/org/elasticsearch/alerts/condition/simple/TestAlwaysFalseCondition.java +++ b/src/test/java/org/elasticsearch/alerts/condition/simple/AlwaysTrueConditionTests.java @@ -18,8 +18,7 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; /** */ -public class TestAlwaysFalseCondition extends ElasticsearchTestCase { - +public class AlwaysTrueConditionTests extends ElasticsearchTestCase { @Test public void testExecute() throws Exception { @@ -35,7 +34,6 @@ public class TestAlwaysFalseCondition extends ElasticsearchTestCase { builder.endObject(); XContentParser xp = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes()); xp.nextToken(); - Condition alwaysTrue = p.parse(xp); assertTrue(alwaysTrue.execute(null).met()); } @@ -49,7 +47,6 @@ public class TestAlwaysFalseCondition extends ElasticsearchTestCase { builder.endObject(); XContentParser xp = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes()); xp.nextToken(); - p.parse(xp); fail("expected a condition exception trying to parse an invalid condition XContent, [" + AlwaysTrueCondition.TYPE + "] condition should not parse with a body"); @@ -83,4 +80,5 @@ public class TestAlwaysFalseCondition extends ElasticsearchTestCase { fail("expected a condition exception trying to parse an invalid condition result XContent, [" + AlwaysTrueCondition.TYPE + "] condition result should not parse with a [met] field"); } + } diff --git a/src/test/java/org/elasticsearch/alerts/history/FiredAlertTest.java b/src/test/java/org/elasticsearch/alerts/history/FiredAlertTests.java similarity index 88% rename from src/test/java/org/elasticsearch/alerts/history/FiredAlertTest.java rename to src/test/java/org/elasticsearch/alerts/history/FiredAlertTests.java index 437a48e91f9..5cf093bde1c 100644 --- a/src/test/java/org/elasticsearch/alerts/history/FiredAlertTest.java +++ b/src/test/java/org/elasticsearch/alerts/history/FiredAlertTests.java @@ -13,6 +13,8 @@ import org.elasticsearch.alerts.condition.simple.AlwaysFalseCondition; import org.elasticsearch.alerts.condition.simple.AlwaysTrueCondition; import org.elasticsearch.alerts.input.Input; import org.elasticsearch.alerts.input.simple.SimpleInput; +import org.elasticsearch.alerts.test.AbstractAlertsIntegrationTests; +import org.elasticsearch.alerts.test.AlertsTestUtils; import org.elasticsearch.alerts.throttle.Throttler; import org.elasticsearch.common.joda.time.DateTime; import org.elasticsearch.common.xcontent.ToXContent; @@ -22,12 +24,12 @@ import org.junit.Test; /** */ -public class FiredAlertTest extends AbstractAlertingTests { +public class FiredAlertTests extends AbstractAlertsIntegrationTests { @Test public void testParser() throws Exception { - Alert alert = createTestAlert("fired_test"); + Alert alert = AlertsTestUtils.createTestAlert("fired_test", scriptService(), httpClient(), noopEmailService(), logger); FiredAlert firedAlert = new FiredAlert(alert, new DateTime(), new DateTime()); XContentBuilder jsonBuilder = XContentFactory.jsonBuilder(); firedAlert.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS); @@ -43,7 +45,7 @@ public class FiredAlertTest extends AbstractAlertingTests { @Test public void testParser_WithSealedFiredAlert() throws Exception { - Alert alert = createTestAlert("fired_test"); + Alert alert = AlertsTestUtils.createTestAlert("fired_test", scriptService(), httpClient(), noopEmailService(), logger); FiredAlert firedAlert = new FiredAlert(alert, new DateTime(), new DateTime()); ExecutionContext ctx = new ExecutionContext(firedAlert.id(), alert, new DateTime(), new DateTime()); ctx.onActionResult(new EmailAction.Result.Failure("failed to send because blah")); @@ -65,7 +67,7 @@ public class FiredAlertTest extends AbstractAlertingTests { @Test public void testParser_WithSealedFiredAlert_WithScriptSearchCondition() throws Exception { - Alert alert = createTestAlert("fired_test"); + Alert alert = AlertsTestUtils.createTestAlert("fired_test", scriptService(), httpClient(), noopEmailService(), logger); FiredAlert firedAlert = new FiredAlert(alert, new DateTime(), new DateTime()); ExecutionContext ctx = new ExecutionContext(firedAlert.id(), alert, new DateTime(), new DateTime()); ctx.onActionResult(new EmailAction.Result.Failure("failed to send because blah")); diff --git a/src/test/java/org/elasticsearch/alerts/history/ActionHistoryIndexNameTest.java b/src/test/java/org/elasticsearch/alerts/history/HistoryStoreTests.java similarity index 90% rename from src/test/java/org/elasticsearch/alerts/history/ActionHistoryIndexNameTest.java rename to src/test/java/org/elasticsearch/alerts/history/HistoryStoreTests.java index e3e251d30ed..2a5211809f6 100644 --- a/src/test/java/org/elasticsearch/alerts/history/ActionHistoryIndexNameTest.java +++ b/src/test/java/org/elasticsearch/alerts/history/HistoryStoreTests.java @@ -14,10 +14,10 @@ import static org.hamcrest.core.IsEqual.equalTo; /** */ -public class ActionHistoryIndexNameTest extends ElasticsearchTestCase { +public class HistoryStoreTests extends ElasticsearchTestCase { @Test - public void testActionHistoryNameTest() { + public void testIndexNameGeneration() { assertThat(HistoryStore.getAlertHistoryIndexNameForTime(new DateTime(0, DateTimeZone.UTC)), equalTo(".alert_history_1970-01-01")); assertThat(HistoryStore.getAlertHistoryIndexNameForTime(new DateTime(100000000000L, DateTimeZone.UTC)), equalTo(".alert_history_1973-03-03")); assertThat(HistoryStore.getAlertHistoryIndexNameForTime(new DateTime(1416582852000L, DateTimeZone.UTC)), equalTo(".alert_history_2014-11-21")); diff --git a/src/test/java/org/elasticsearch/alerts/input/search/SearchInputTests.java b/src/test/java/org/elasticsearch/alerts/input/search/SearchInputTests.java index 9ada09c3d66..d665f7d44c9 100644 --- a/src/test/java/org/elasticsearch/alerts/input/search/SearchInputTests.java +++ b/src/test/java/org/elasticsearch/alerts/input/search/SearchInputTests.java @@ -19,6 +19,8 @@ import org.elasticsearch.common.settings.ImmutableSettings; 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.json.JsonXContent; import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.builder.SearchSourceBuilder; @@ -70,27 +72,21 @@ public class SearchInputTests extends ElasticsearchIntegrationTest { @Test public void testParser_Valid() throws Exception { - SearchSourceBuilder searchSourceBuilder = searchSource().query( - filteredQuery(matchQuery("event_type", "a"), rangeFilter("_timestamp").from("{{" + Variables.SCHEDULED_FIRE_TIME + "}}||-30s").to("{{" + Variables.SCHEDULED_FIRE_TIME + "}}")) - ); - SearchRequest request = client() - .prepareSearch() + SearchRequest request = client().prepareSearch() .setSearchType(SearchInput.DEFAULT_SEARCH_TYPE) .request() - .source(searchSourceBuilder); + .source(searchSource() + .query(filteredQuery(matchQuery("event_type", "a"), rangeFilter("_timestamp").from("{{" + Variables.SCHEDULED_FIRE_TIME + "}}||-30s").to("{{" + Variables.SCHEDULED_FIRE_TIME + "}}")))); - XContentBuilder jsonBuilder = jsonBuilder(); + XContentBuilder builder = AlertUtils.writeSearchRequest(request, jsonBuilder(), ToXContent.EMPTY_PARAMS); + XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes()); + parser.nextToken(); - jsonBuilder.startObject(); - jsonBuilder.field(SearchInput.Parser.REQUEST_FIELD.getPreferredName()); - AlertUtils.writeSearchRequest(request, jsonBuilder, ToXContent.EMPTY_PARAMS); - jsonBuilder.endObject(); - - Input.Parser searchInputParser = new SearchInput.Parser(ImmutableSettings.settingsBuilder().build(), + Input.Parser searchInputParser = new SearchInput.Parser(ImmutableSettings.EMPTY, ScriptServiceProxy.of(internalCluster().getInstance(ScriptService.class)), ClientProxy.of(client())); - Input searchInput = searchInputParser.parse(XContentFactory.xContent(jsonBuilder.bytes()).createParser(jsonBuilder.bytes())); + Input searchInput = searchInputParser.parse(parser); assertEquals(SearchInput.TYPE, searchInput.type()); } diff --git a/src/test/java/org/elasticsearch/alerts/scheduler/schedule/ScheduleRegistryTests.java b/src/test/java/org/elasticsearch/alerts/scheduler/schedule/ScheduleRegistryTests.java index a913ae0545c..b99526f1d41 100644 --- a/src/test/java/org/elasticsearch/alerts/scheduler/schedule/ScheduleRegistryTests.java +++ b/src/test/java/org/elasticsearch/alerts/scheduler/schedule/ScheduleRegistryTests.java @@ -97,7 +97,6 @@ public class ScheduleRegistryTests extends ScheduleTestCase { .field(DailySchedule.TYPE, daily) .endObject(); BytesReference bytes = builder.bytes(); - System.out.println(bytes.toUtf8()); XContentParser parser = JsonXContent.jsonXContent.createParser(bytes); parser.nextToken(); Schedule schedule = registry.parse(parser); @@ -114,7 +113,6 @@ public class ScheduleRegistryTests extends ScheduleTestCase { .field(WeeklySchedule.TYPE, weekly) .endObject(); BytesReference bytes = builder.bytes(); - System.out.println(bytes.toUtf8()); XContentParser parser = JsonXContent.jsonXContent.createParser(bytes); parser.nextToken(); Schedule schedule = registry.parse(parser); @@ -131,7 +129,6 @@ public class ScheduleRegistryTests extends ScheduleTestCase { .field(MonthlySchedule.TYPE, monthly) .endObject(); BytesReference bytes = builder.bytes(); - System.out.println(bytes.toUtf8()); XContentParser parser = JsonXContent.jsonXContent.createParser(bytes); parser.nextToken(); Schedule schedule = registry.parse(parser); diff --git a/src/test/java/org/elasticsearch/alerts/support/template/ScriptTemplateTests.java b/src/test/java/org/elasticsearch/alerts/support/template/ScriptTemplateTests.java index b4bb487c341..d276030fa37 100644 --- a/src/test/java/org/elasticsearch/alerts/support/template/ScriptTemplateTests.java +++ b/src/test/java/org/elasticsearch/alerts/support/template/ScriptTemplateTests.java @@ -116,7 +116,6 @@ public class ScriptTemplateTests extends ElasticsearchTestCase { XContentBuilder builder = jsonBuilder().value(template); BytesReference bytes = builder.bytes(); - System.out.println(bytes.toUtf8()); XContentParser parser = JsonXContent.jsonXContent.createParser(bytes); parser.nextToken(); ScriptTemplate parsed = templateParser.parse(parser); diff --git a/src/test/java/org/elasticsearch/alerts/AbstractAlertingTests.java b/src/test/java/org/elasticsearch/alerts/test/AbstractAlertsIntegrationTests.java similarity index 82% rename from src/test/java/org/elasticsearch/alerts/AbstractAlertingTests.java rename to src/test/java/org/elasticsearch/alerts/test/AbstractAlertsIntegrationTests.java index 55be8fbb1a5..a32188a94eb 100644 --- a/src/test/java/org/elasticsearch/alerts/AbstractAlertingTests.java +++ b/src/test/java/org/elasticsearch/alerts/test/AbstractAlertsIntegrationTests.java @@ -3,33 +3,24 @@ * 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; +package org.elasticsearch.alerts.test; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.support.IndicesOptions; -import org.elasticsearch.alerts.actions.Action; -import org.elasticsearch.alerts.actions.Actions; -import org.elasticsearch.alerts.actions.email.EmailAction; +import org.elasticsearch.alerts.AlertsPlugin; +import org.elasticsearch.alerts.AlertsService; import org.elasticsearch.alerts.actions.email.service.Authentication; import org.elasticsearch.alerts.actions.email.service.Email; import org.elasticsearch.alerts.actions.email.service.EmailService; import org.elasticsearch.alerts.actions.email.service.Profile; import org.elasticsearch.alerts.actions.webhook.HttpClient; -import org.elasticsearch.alerts.actions.webhook.WebhookAction; import org.elasticsearch.alerts.client.AlertsClient; -import org.elasticsearch.alerts.condition.script.ScriptCondition; import org.elasticsearch.alerts.history.FiredAlert; import org.elasticsearch.alerts.history.HistoryStore; -import org.elasticsearch.alerts.input.search.SearchInput; -import org.elasticsearch.alerts.scheduler.schedule.CronSchedule; import org.elasticsearch.alerts.support.AlertUtils; -import org.elasticsearch.alerts.support.Script; -import org.elasticsearch.alerts.support.init.proxy.ClientProxy; import org.elasticsearch.alerts.support.init.proxy.ScriptServiceProxy; -import org.elasticsearch.alerts.support.template.ScriptTemplate; import org.elasticsearch.alerts.support.template.Template; -import org.elasticsearch.alerts.transform.SearchTransform; import org.elasticsearch.alerts.transport.actions.stats.AlertsStatsResponse; import org.elasticsearch.client.Client; import org.elasticsearch.cluster.ClusterState; @@ -37,36 +28,34 @@ import org.elasticsearch.cluster.metadata.IndexTemplateMetaData; import org.elasticsearch.cluster.routing.IndexRoutingTable; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.hppc.cursors.ObjectObjectCursor; -import org.elasticsearch.common.netty.handler.codec.http.HttpMethod; import org.elasticsearch.common.settings.ImmutableSettings; 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.support.XContentMapValues; -import org.elasticsearch.script.ScriptService; import org.elasticsearch.test.ElasticsearchIntegrationTest; +import org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope; import org.elasticsearch.test.InternalTestCluster; import org.elasticsearch.test.TestCluster; import org.junit.After; import org.junit.Before; -import javax.mail.internet.AddressException; import java.io.IOException; import java.net.InetSocketAddress; import java.util.*; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; -import static org.elasticsearch.index.query.QueryBuilders.*; -import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource; +import static org.elasticsearch.index.query.QueryBuilders.boolQuery; +import static org.elasticsearch.index.query.QueryBuilders.matchQuery; +import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.SUITE; import static org.hamcrest.Matchers.*; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNot.not; /** */ -@ElasticsearchIntegrationTest.ClusterScope(scope = ElasticsearchIntegrationTest.Scope.SUITE, numClientNodes = 0, transportClientRatio = 0, randomDynamicTemplates = false) -public abstract class AbstractAlertingTests extends ElasticsearchIntegrationTest { +@ClusterScope(scope = SUITE, numClientNodes = 0, transportClientRatio = 0, randomDynamicTemplates = false) +public abstract class AbstractAlertsIntegrationTests extends ElasticsearchIntegrationTest { @Override protected Settings nodeSettings(int nodeOrdinal) { @@ -129,10 +118,8 @@ public abstract class AbstractAlertingTests extends ElasticsearchIntegrationTest builder.startObject("input"); { - builder.startObject("search"); - builder.field("request"); + builder.field("search"); AlertUtils.writeSearchRequest(conditionRequest, builder, ToXContent.EMPTY_PARAMS); - builder.endObject(); } builder.endObject(); @@ -163,56 +150,6 @@ public abstract class AbstractAlertingTests extends ElasticsearchIntegrationTest return builder.bytes(); } - public static SearchRequest createConditionSearchRequest(String... indices) { - SearchRequest request = new SearchRequest(indices); - request.indicesOptions(AlertUtils.DEFAULT_INDICES_OPTIONS); - request.searchType(SearchInput.DEFAULT_SEARCH_TYPE); - return request; - } - - protected Alert createTestAlert(String alertName) throws AddressException { - SearchRequest conditionRequest = createConditionSearchRequest("my-condition-index").source(searchSource().query(matchAllQuery())); - SearchRequest transformRequest = createConditionSearchRequest("my-payload-index").source(searchSource().query(matchAllQuery())); - transformRequest.searchType(SearchTransform.DEFAULT_SEARCH_TYPE); - conditionRequest.searchType(SearchInput.DEFAULT_SEARCH_TYPE); - - List actions = new ArrayList<>(); - - Template url = new ScriptTemplate(scriptService(), "http://localhost/foobarbaz/{{alert_name}}"); - Template body = new ScriptTemplate(scriptService(), "{{alert_name}} executed with {{response.hits.total}} hits"); - - actions.add(new WebhookAction(logger, httpClient(), HttpMethod.GET, url, body)); - - Email.Address from = new Email.Address("from@test.com"); - List emailAddressList = new ArrayList<>(); - emailAddressList.add(new Email.Address("to@test.com")); - Email.AddressList to = new Email.AddressList(emailAddressList); - - - Email.Builder emailBuilder = Email.builder().id("prototype"); - emailBuilder.from(from); - emailBuilder.to(to); - - - EmailAction emailAction = new EmailAction(logger, noopEmailService(), emailBuilder.build(), - new Authentication("testname", "testpassword"), Profile.STANDARD, "testaccount", body, body, null, true); - - actions.add(emailAction); - - Map metadata = new LinkedHashMap<>(); - metadata.put("foo", "bar"); - - return new Alert( - alertName, - new CronSchedule("0/5 * * * * ? *"), - new SearchInput(logger, scriptService(), ClientProxy.of(client()), - conditionRequest), - new ScriptCondition(logger, scriptService(), new Script("return true")), - new SearchTransform(logger, scriptService(), ClientProxy.of(client()), transformRequest), new Actions(actions), metadata, new Alert.Status(), new TimeValue(0) - ); - } - - protected AlertsClient alertClient() { return internalTestCluster().getInstance(AlertsClient.class); } diff --git a/src/test/java/org/elasticsearch/alerts/test/AlertsTestUtils.java b/src/test/java/org/elasticsearch/alerts/test/AlertsTestUtils.java new file mode 100644 index 00000000000..8c089ab57fa --- /dev/null +++ b/src/test/java/org/elasticsearch/alerts/test/AlertsTestUtils.java @@ -0,0 +1,107 @@ +/* + * 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.test; + +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.alerts.Alert; +import org.elasticsearch.alerts.actions.Action; +import org.elasticsearch.alerts.actions.Actions; +import org.elasticsearch.alerts.actions.email.EmailAction; +import org.elasticsearch.alerts.actions.email.service.Authentication; +import org.elasticsearch.alerts.actions.email.service.Email; +import org.elasticsearch.alerts.actions.email.service.EmailService; +import org.elasticsearch.alerts.actions.email.service.Profile; +import org.elasticsearch.alerts.actions.webhook.HttpClient; +import org.elasticsearch.alerts.actions.webhook.WebhookAction; +import org.elasticsearch.alerts.condition.script.ScriptCondition; +import org.elasticsearch.alerts.input.search.SearchInput; +import org.elasticsearch.alerts.scheduler.schedule.CronSchedule; +import org.elasticsearch.alerts.support.AlertUtils; +import org.elasticsearch.alerts.support.Script; +import org.elasticsearch.alerts.support.init.proxy.ClientProxy; +import org.elasticsearch.alerts.support.init.proxy.ScriptServiceProxy; +import org.elasticsearch.alerts.support.template.ScriptTemplate; +import org.elasticsearch.alerts.support.template.Template; +import org.elasticsearch.alerts.transform.SearchTransform; +import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.common.netty.handler.codec.http.HttpMethod; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.test.ElasticsearchIntegrationTest; + +import javax.mail.internet.AddressException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; +import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource; + +/** + * + */ +public final class AlertsTestUtils { + + private AlertsTestUtils() { + } + + public static SearchRequest newInputSearchRequest(String... indices) { + SearchRequest request = new SearchRequest(indices); + request.indicesOptions(AlertUtils.DEFAULT_INDICES_OPTIONS); + request.searchType(SearchInput.DEFAULT_SEARCH_TYPE); + return request; + } + + public static SearchRequest matchAllRequest() { + return new SearchRequest().source(SearchSourceBuilder.searchSource().query(matchAllQuery())); + } + + public static Alert createTestAlert(String alertName, ScriptServiceProxy scriptService, HttpClient httpClient, EmailService emailService, ESLogger logger) throws AddressException { + SearchRequest conditionRequest = newInputSearchRequest("my-condition-index").source(searchSource().query(matchAllQuery())); + SearchRequest transformRequest = newInputSearchRequest("my-payload-index").source(searchSource().query(matchAllQuery())); + transformRequest.searchType(SearchTransform.DEFAULT_SEARCH_TYPE); + conditionRequest.searchType(SearchInput.DEFAULT_SEARCH_TYPE); + + List actions = new ArrayList<>(); + + Template url = new ScriptTemplate(scriptService, "http://localhost/foobarbaz/{{alert_name}}"); + Template body = new ScriptTemplate(scriptService, "{{alert_name}} executed with {{response.hits.total}} hits"); + + actions.add(new WebhookAction(logger, httpClient, HttpMethod.GET, url, body)); + + Email.Address from = new Email.Address("from@test.com"); + List emailAddressList = new ArrayList<>(); + emailAddressList.add(new Email.Address("to@test.com")); + Email.AddressList to = new Email.AddressList(emailAddressList); + + + Email.Builder emailBuilder = Email.builder().id("prototype"); + emailBuilder.from(from); + emailBuilder.to(to); + + + EmailAction emailAction = new EmailAction(logger, emailService, emailBuilder.build(), + new Authentication("testname", "testpassword"), Profile.STANDARD, "testaccount", body, body, null, true); + + actions.add(emailAction); + + Map metadata = new LinkedHashMap<>(); + metadata.put("foo", "bar"); + + return new Alert( + alertName, + new CronSchedule("0/5 * * * * ? *"), + new SearchInput(logger, scriptService, ClientProxy.of(ElasticsearchIntegrationTest.client()), + conditionRequest), + new ScriptCondition(logger, scriptService, new Script("return true")), + new SearchTransform(logger, scriptService, ClientProxy.of(ElasticsearchIntegrationTest.client()), transformRequest), + new Actions(actions), + metadata, + new TimeValue(0), + new Alert.Status()); + } +} diff --git a/src/test/java/org/elasticsearch/alerts/AlertMetadataTest.java b/src/test/java/org/elasticsearch/alerts/test/integration/AlertMetadataTests.java similarity index 64% rename from src/test/java/org/elasticsearch/alerts/AlertMetadataTest.java rename to src/test/java/org/elasticsearch/alerts/test/integration/AlertMetadataTests.java index e4a011946be..d912fc9a66c 100644 --- a/src/test/java/org/elasticsearch/alerts/AlertMetadataTest.java +++ b/src/test/java/org/elasticsearch/alerts/test/integration/AlertMetadataTests.java @@ -3,11 +3,12 @@ * 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; +package org.elasticsearch.alerts.test.integration; -import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.alerts.history.HistoryStore; +import org.elasticsearch.alerts.test.AbstractAlertsIntegrationTests; +import org.elasticsearch.alerts.test.AlertsTestUtils; import org.junit.Test; import java.util.ArrayList; @@ -15,14 +16,19 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static org.elasticsearch.alerts.client.AlertSourceBuilder.alertSourceBuilder; +import static org.elasticsearch.alerts.condition.ConditionBuilders.scriptCondition; +import static org.elasticsearch.alerts.input.InputBuilders.searchInput; +import static org.elasticsearch.alerts.scheduler.schedule.Schedules.cron; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource; import static org.hamcrest.Matchers.greaterThan; /** + * */ -public class AlertMetadataTest extends AbstractAlertingTests { +public class AlertMetadataTests extends AbstractAlertsIntegrationTests { @Test public void testAlertMetadata() throws Exception { @@ -35,9 +41,12 @@ public class AlertMetadataTest extends AbstractAlertingTests { metaList.add("test"); metadata.put("baz", metaList); - SearchRequest conditionRequest = createConditionSearchRequest("my-index").source(searchSource().query(matchAllQuery())); alertClient().preparePutAlert("1") - .setAlertSource(createAlertSource("0/5 * * * * ? *", conditionRequest, "hits.total == 1", metadata)) + .source(alertSourceBuilder() + .schedule(cron("0/5 * * * * ? *")) + .input(searchInput(AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(matchAllQuery())))) + .condition(scriptCondition("hits.total == 1")) + .metadata(metadata)) .get(); // Wait for a no action entry to be added. (the condition search request will not match, because there are no docs in my-index) assertAlertWithNoActionNeeded("1", 1); diff --git a/src/test/java/org/elasticsearch/alerts/actions/AlertStatsTests.java b/src/test/java/org/elasticsearch/alerts/test/integration/AlertStatsTests.java similarity index 61% rename from src/test/java/org/elasticsearch/alerts/actions/AlertStatsTests.java rename to src/test/java/org/elasticsearch/alerts/test/integration/AlertStatsTests.java index 121df746b86..8002e32f174 100644 --- a/src/test/java/org/elasticsearch/alerts/actions/AlertStatsTests.java +++ b/src/test/java/org/elasticsearch/alerts/test/integration/AlertStatsTests.java @@ -3,42 +3,47 @@ * 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.actions; +package org.elasticsearch.alerts.test.integration; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.alerts.*; import org.elasticsearch.alerts.client.AlertsClient; +import org.elasticsearch.alerts.test.AbstractAlertsIntegrationTests; +import org.elasticsearch.alerts.test.AlertsTestUtils; import org.elasticsearch.alerts.transport.actions.stats.AlertsStatsRequest; import org.elasticsearch.alerts.transport.actions.stats.AlertsStatsResponse; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.test.ElasticsearchIntegrationTest; +import org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope; import org.junit.Test; import java.util.concurrent.TimeUnit; import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource; +import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST; +import static org.hamcrest.Matchers.is; import static org.hamcrest.core.IsEqual.equalTo; /** */ -@ElasticsearchIntegrationTest.ClusterScope(scope = ElasticsearchIntegrationTest.Scope.TEST, numClientNodes = 0, transportClientRatio = 0, randomDynamicTemplates = false) -public class AlertStatsTests extends AbstractAlertingTests { +@ClusterScope(scope = TEST, numClientNodes = 0, transportClientRatio = 0, randomDynamicTemplates = false) +public class AlertStatsTests extends AbstractAlertsIntegrationTests { @Test public void testStartedStats() throws Exception { AlertsStatsRequest alertsStatsRequest = alertClient().prepareAlertsStats().request(); AlertsStatsResponse response = alertClient().alertsStats(alertsStatsRequest).actionGet(); - assertTrue(response.isAlertActionManagerStarted()); - assertThat(response.getAlertManagerStarted(), equalTo(AlertsService.State.STARTED)); - assertThat(response.getAlertActionManagerQueueSize(), equalTo(0L)); - assertThat(response.getNumberOfRegisteredAlerts(), equalTo(0L)); - assertThat(response.getAlertActionManagerLargestQueueSize(), equalTo(0L)); - assertThat(response.getVersion(), equalTo(AlertsVersion.CURRENT)); - assertThat(response.getBuild(), equalTo(AlertsBuild.CURRENT)); + assertThat(response.isAlertActionManagerStarted(), is(true)); + assertThat(response.getAlertManagerStarted(), is(AlertsService.State.STARTED)); + assertThat(response.getAlertActionManagerQueueSize(), is(0L)); + assertThat(response.getNumberOfRegisteredAlerts(), is(0L)); + assertThat(response.getAlertActionManagerLargestQueueSize(), is(0L)); + assertThat(response.getVersion(), is(AlertsVersion.CURRENT)); + assertThat(response.getBuild(), is(AlertsBuild.CURRENT)); } @Test @@ -48,13 +53,13 @@ public class AlertStatsTests extends AbstractAlertingTests { AlertsStatsRequest alertsStatsRequest = alertsClient.prepareAlertsStats().request(); AlertsStatsResponse response = alertsClient.alertsStats(alertsStatsRequest).actionGet(); - assertTrue(response.isAlertActionManagerStarted()); + assertThat(response.isAlertActionManagerStarted(), is(true)); assertThat(response.getAlertManagerStarted(), equalTo(AlertsService.State.STARTED)); - SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); + SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); BytesReference alertSource = createAlertSource("* * * * * ? *", searchRequest, "hits.total == 1"); alertClient().preparePutAlert("testAlert") - .setAlertSource(alertSource) + .source(alertSource) .get(); response = alertClient().alertsStats(alertsStatsRequest).actionGet(); @@ -63,9 +68,9 @@ public class AlertStatsTests extends AbstractAlertingTests { TimeValue waitTime = new TimeValue(30, TimeUnit.SECONDS); Thread.sleep(waitTime.getMillis()); - assertTrue(response.isAlertActionManagerStarted()); - assertThat(response.getAlertManagerStarted(), equalTo(AlertsService.State.STARTED)); - assertThat(response.getNumberOfRegisteredAlerts(), equalTo(1L)); + assertThat(response.isAlertActionManagerStarted(), is(true)); + assertThat(response.getAlertManagerStarted(), is(AlertsService.State.STARTED)); + assertThat(response.getNumberOfRegisteredAlerts(), is(1L)); //assertThat(response.getAlertActionManagerLargestQueueSize(), greaterThan(0L)); } } diff --git a/src/test/java/org/elasticsearch/alerts/AlertThrottleTests.java b/src/test/java/org/elasticsearch/alerts/test/integration/AlertThrottleTests.java similarity index 70% rename from src/test/java/org/elasticsearch/alerts/AlertThrottleTests.java rename to src/test/java/org/elasticsearch/alerts/test/integration/AlertThrottleTests.java index 53e9e441b62..14230cfba95 100644 --- a/src/test/java/org/elasticsearch/alerts/AlertThrottleTests.java +++ b/src/test/java/org/elasticsearch/alerts/test/integration/AlertThrottleTests.java @@ -3,41 +3,37 @@ * 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; +package org.elasticsearch.alerts.test.integration; import org.elasticsearch.action.count.CountResponse; import org.elasticsearch.action.delete.DeleteResponse; import org.elasticsearch.action.index.IndexResponse; -import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchType; -import org.elasticsearch.alerts.actions.Action; -import org.elasticsearch.alerts.actions.Actions; -import org.elasticsearch.alerts.actions.index.IndexAction; +import org.elasticsearch.alerts.Alert; +import org.elasticsearch.alerts.actions.ActionBuilders; import org.elasticsearch.alerts.client.AlertsClient; -import org.elasticsearch.alerts.condition.script.ScriptCondition; import org.elasticsearch.alerts.history.FiredAlert; import org.elasticsearch.alerts.history.HistoryStore; -import org.elasticsearch.alerts.input.search.SearchInput; -import org.elasticsearch.alerts.scheduler.schedule.CronSchedule; -import org.elasticsearch.alerts.support.Script; -import org.elasticsearch.alerts.support.init.proxy.ClientProxy; -import org.elasticsearch.alerts.transform.SearchTransform; +import org.elasticsearch.alerts.test.AbstractAlertsIntegrationTests; import org.elasticsearch.alerts.transport.actions.ack.AckAlertResponse; import org.elasticsearch.alerts.transport.actions.get.GetAlertResponse; import org.elasticsearch.alerts.transport.actions.put.PutAlertResponse; 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.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; +import org.junit.Assert; import org.junit.Test; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.TimeUnit; +import static org.elasticsearch.alerts.client.AlertSourceBuilder.alertSourceBuilder; +import static org.elasticsearch.alerts.condition.ConditionBuilders.scriptCondition; +import static org.elasticsearch.alerts.input.InputBuilders.searchInput; +import static org.elasticsearch.alerts.transform.TransformBuilders.searchTransform; +import static org.elasticsearch.alerts.scheduler.schedule.Schedules.cron; +import static org.elasticsearch.alerts.test.AlertsTestUtils.matchAllRequest; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource; import static org.hamcrest.Matchers.*; @@ -45,7 +41,7 @@ import static org.hamcrest.core.IsEqual.equalTo; /** */ -public class AlertThrottleTests extends AbstractAlertingTests { +public class AlertThrottleTests extends AbstractAlertsIntegrationTests { @Test public void testAckThrottle() throws Exception{ @@ -57,32 +53,22 @@ public class AlertThrottleTests extends AbstractAlertingTests { assertTrue(dummyEventIndexResponse.isCreated()); refresh(); + PutAlertResponse putAlertResponse = alertsClient.preparePutAlert() + .alertName("throttled-alert") + .source(alertSourceBuilder() + .schedule(cron("0/5 * * * * ? *")) + .input(searchInput(matchAllRequest().indices("test-index"))) + .condition(scriptCondition("hits.total > 0")) + .transform(searchTransform(matchAllRequest().indices("test-index"))) + .addAction(ActionBuilders.indexAction("action-index", "action-type")) + .throttlePeriod(TimeValue.timeValueMillis(0))) + .get(); - SearchRequest request = createConditionSearchRequest("test-index").source(searchSource().query(matchAllQuery())); - - List actions = new ArrayList<>(); - - actions.add(new IndexAction(logger, ClientProxy.of(client()), "action-index", "action-type")); - - Alert alert = new Alert( - "test-ack-throttle", - new CronSchedule("0/5 * * * * ? *"), - new SearchInput(logger, scriptService(), ClientProxy.of(client()), - request), - new ScriptCondition(logger, scriptService(), new Script("hits.total > 0")), - new SearchTransform(logger, scriptService(), ClientProxy.of(client()), request), new Actions(actions), null, new Alert.Status(), new TimeValue(0) - ); - - - XContentBuilder jsonBuilder = XContentFactory.jsonBuilder(); - alert.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS); - - PutAlertResponse putAlertResponse = alertsClient.preparePutAlert().setAlertName("throttled-alert").setAlertSource(jsonBuilder.bytes()).get(); assertTrue(putAlertResponse.indexResponse().isCreated()); Thread.sleep(20000); AckAlertResponse ackResponse = alertsClient.prepareAckAlert("throttled-alert").get(); - assertEquals(Alert.Status.AckStatus.State.ACKED, ackResponse.getStatus().ackStatus().state()); + Assert.assertEquals(Alert.Status.AckStatus.State.ACKED, ackResponse.getStatus().ackStatus().state()); refresh(); SearchResponse searchResponse = client() @@ -138,24 +124,16 @@ public class AlertThrottleTests extends AbstractAlertingTests { assertTrue(dummyEventIndexResponse.isCreated()); refresh(); - SearchRequest request = createConditionSearchRequest("test-index").source(searchSource().query(matchAllQuery())); - - List actions = new ArrayList<>(); - - actions.add(new IndexAction(logger, ClientProxy.of(client()), "action-index", "action-type")); - - Alert alert = new Alert( - "test-time-throttle", - new CronSchedule("0/5 * * * * ? *"), - new SearchInput(logger, scriptService(), ClientProxy.of(client()), request), - new ScriptCondition(logger, scriptService(), new Script("hits.total > 0")), - new SearchTransform(logger, scriptService(), ClientProxy.of(client()), request), - new Actions(actions), null, new Alert.Status(), new TimeValue(10, TimeUnit.SECONDS)); - - XContentBuilder jsonBuilder = XContentFactory.jsonBuilder(); - alert.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS); - - PutAlertResponse putAlertResponse = alertsClient.preparePutAlert().setAlertName("throttled-alert").setAlertSource(jsonBuilder.bytes()).get(); + PutAlertResponse putAlertResponse = alertsClient.preparePutAlert() + .alertName("throttled-alert") + .source(alertSourceBuilder() + .schedule(cron("0/5 * * * * ? *")) + .input(searchInput(matchAllRequest().indices("test-index"))) + .condition(scriptCondition("hits.total > 0")) + .transform(searchTransform(matchAllRequest().indices("test-index"))) + .addAction(ActionBuilders.indexAction("action-index", "action-type")) + .throttlePeriod(TimeValue.timeValueSeconds(10))) + .get(); assertTrue(putAlertResponse.indexResponse().isCreated()); forceFullSleepTime(new TimeValue(5, TimeUnit.SECONDS)); diff --git a/src/test/java/org/elasticsearch/alerts/BasicAlertingTest.java b/src/test/java/org/elasticsearch/alerts/test/integration/BasicAlertsTests.java similarity index 70% rename from src/test/java/org/elasticsearch/alerts/BasicAlertingTest.java rename to src/test/java/org/elasticsearch/alerts/test/integration/BasicAlertsTests.java index 987a2998dfc..da8c8a80db9 100644 --- a/src/test/java/org/elasticsearch/alerts/BasicAlertingTest.java +++ b/src/test/java/org/elasticsearch/alerts/test/integration/BasicAlertsTests.java @@ -3,18 +3,23 @@ * 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; +package org.elasticsearch.alerts.test.integration; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchType; +import org.elasticsearch.alerts.AlertsException; +import org.elasticsearch.alerts.AlertsStore; +import org.elasticsearch.alerts.client.AlertSourceBuilder; import org.elasticsearch.alerts.client.AlertsClient; +import org.elasticsearch.alerts.scheduler.schedule.IntervalSchedule; import org.elasticsearch.alerts.support.AlertUtils; import org.elasticsearch.alerts.support.Variables; +import org.elasticsearch.alerts.test.AbstractAlertsIntegrationTests; +import org.elasticsearch.alerts.test.AlertsTestUtils; import org.elasticsearch.alerts.transport.actions.delete.DeleteAlertRequest; import org.elasticsearch.alerts.transport.actions.delete.DeleteAlertResponse; import org.elasticsearch.alerts.transport.actions.get.GetAlertResponse; import org.elasticsearch.alerts.transport.actions.put.PutAlertResponse; -import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.query.FilterBuilders; @@ -28,6 +33,12 @@ import org.junit.Test; import java.util.Locale; +import static org.elasticsearch.alerts.actions.ActionBuilders.indexAction; +import static org.elasticsearch.alerts.client.AlertSourceBuilder.alertSourceBuilder; +import static org.elasticsearch.alerts.condition.ConditionBuilders.scriptCondition; +import static org.elasticsearch.alerts.input.InputBuilders.searchInput; +import static org.elasticsearch.alerts.scheduler.schedule.Schedules.cron; +import static org.elasticsearch.alerts.scheduler.schedule.Schedules.interval; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.index.query.FilterBuilders.rangeFilter; import static org.elasticsearch.index.query.QueryBuilders.*; @@ -39,7 +50,7 @@ import static org.hamcrest.Matchers.is; /** */ -public class BasicAlertingTest extends AbstractAlertingTests { +public class BasicAlertsTests extends AbstractAlertsIntegrationTests { @Test public void testIndexAlert() throws Exception { @@ -47,10 +58,12 @@ public class BasicAlertingTest extends AbstractAlertingTests { createIndex("my-index"); // Have a sample document in the index, the alert is going to evaluate client().prepareIndex("my-index", "my-type").setSource("field", "value").get(); - SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); - BytesReference alertSource = createAlertSource("0/5 * * * * ? *", searchRequest, "hits.total == 1"); + SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); alertsClient.preparePutAlert("my-first-alert") - .setAlertSource(alertSource) + .source(alertSourceBuilder() + .schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS)) + .input(searchInput(searchRequest)) + .condition(scriptCondition("hits.total == 1"))) .get(); assertAlertWithMinimumPerformedActionsCount("my-first-alert", 1); @@ -62,10 +75,12 @@ public class BasicAlertingTest extends AbstractAlertingTests { @Test public void testIndexAlert_registerAlertBeforeTargetIndex() throws Exception { AlertsClient alertsClient = alertClient(); - SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); - BytesReference alertSource = createAlertSource("0/5 * * * * ? *", searchRequest, "hits.total == 1"); + SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); alertsClient.preparePutAlert("my-first-alert") - .setAlertSource(alertSource) + .source(alertSourceBuilder() + .schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS)) + .input(searchInput(searchRequest)) + .condition(scriptCondition("hits.total == 1"))) .get(); // The alert's condition won't meet because there is no data that matches with the query @@ -82,10 +97,12 @@ public class BasicAlertingTest extends AbstractAlertingTests { createIndex("my-index"); // Have a sample document in the index, the alert is going to evaluate client().prepareIndex("my-index", "my-type").setSource("field", "value").get(); - SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(matchAllQuery())); - BytesReference alertSource = createAlertSource("0/5 * * * * ? *", searchRequest, "hits.total == 1"); + SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(matchAllQuery())); PutAlertResponse indexResponse = alertsClient.preparePutAlert("my-first-alert") - .setAlertSource(alertSource) + .source(alertSourceBuilder() + .schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS)) + .input(searchInput(searchRequest)) + .condition(scriptCondition("hits.total == 1"))) .get(); assertThat(indexResponse.indexResponse().isCreated(), is(true)); @@ -117,13 +134,13 @@ public class BasicAlertingTest extends AbstractAlertingTests { alertSource.startObject("schedule").field("cron", "0/5 * * * * ? *").endObject(); alertSource.startObject("condition").startObject("script").field("script", "return true").field("request"); - AlertUtils.writeSearchRequest(createConditionSearchRequest(), alertSource, ToXContent.EMPTY_PARAMS); + AlertUtils.writeSearchRequest(AlertsTestUtils.newInputSearchRequest(), alertSource, ToXContent.EMPTY_PARAMS); alertSource.endObject(); alertSource.endObject(); try { alertsClient.preparePutAlert("my-first-alert") - .setAlertSource(alertSource.bytes()) + .source(alertSource.bytes()) .get(); fail(); } catch (AlertsException e) { @@ -145,12 +162,14 @@ public class BasicAlertingTest extends AbstractAlertingTests { createIndex("my-index"); // Have a sample document in the index, the alert is going to evaluate client().prepareIndex("my-index", "my-type").setSource("field", "value").get(); - SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(matchAllQuery())); + SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(matchAllQuery())); searchRequest.searchType(SearchType.QUERY_THEN_FETCH); // By accessing the actual hit we know that the fetch phase has been performed - BytesReference alertSource = createAlertSource("0/5 * * * * ? *", searchRequest, "hits?.hits[0]._score == 1.0"); PutAlertResponse indexResponse = alertsClient.preparePutAlert("my-first-alert") - .setAlertSource(alertSource) + .source(alertSourceBuilder() + .schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS)) + .input(searchInput(searchRequest)) + .condition(scriptCondition("hits?.hits[0]._score == 1.0"))) .get(); assertThat(indexResponse.indexResponse().isCreated(), is(true)); assertAlertWithMinimumPerformedActionsCount("my-first-alert", 1); @@ -158,19 +177,27 @@ public class BasicAlertingTest extends AbstractAlertingTests { @Test public void testModifyAlerts() throws Exception { - SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(matchAllQuery())); + SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index") + .source(searchSource().query(matchAllQuery())); + + AlertSourceBuilder source = alertSourceBuilder() + .schedule(cron("0/5 * * * * ? *")) + .input(searchInput(searchRequest)) + .addAction(indexAction("my-index", "trail")); + + alertClient().preparePutAlert("1") - .setAlertSource(createAlertSource("0/5 * * * * ? *", searchRequest, "hits.total == 1")) + .source(source.condition(scriptCondition("hits.total == 1"))) .get(); assertAlertWithMinimumPerformedActionsCount("1", 0, false); alertClient().preparePutAlert("1") - .setAlertSource(createAlertSource("0/5 * * * * ? *", searchRequest, "hits.total == 0")) + .source(source.condition(scriptCondition("hits.total == 0"))) .get(); assertAlertWithMinimumPerformedActionsCount("1", 1, false); alertClient().preparePutAlert("1") - .setAlertSource(createAlertSource("0/5 * * * * ? 2020", searchRequest, "hits.total == 0")) + .source(source.schedule(cron("0/5 * * * * ? 2020")).condition(scriptCondition("hits.total == 0"))) .get(); Thread.sleep(5000); @@ -181,12 +208,13 @@ public class BasicAlertingTest extends AbstractAlertingTests { @Test public void testAggregations() throws Exception { - class R implements Runnable { + + class Indexer extends Thread { private final long sleepTime; private final long totalTime; - R(long sleepTime, long totalTime) { + Indexer(long sleepTime, long totalTime) { this.sleepTime = sleepTime; this.totalTime = totalTime; } @@ -207,24 +235,30 @@ public class BasicAlertingTest extends AbstractAlertingTests { } assertAcked(prepareCreate("my-index").addMapping("my-type", "_timestamp", "enabled=true")); - SearchRequest searchRequest = createConditionSearchRequest("my-index").source( + SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source( searchSource() .query(QueryBuilders.constantScoreQuery(FilterBuilders.rangeFilter("_timestamp").from("{{scheduled_fire_time}}||-1m").to("{{scheduled_fire_time}}"))) - .aggregation(AggregationBuilders.dateHistogram("rate").field("_timestamp").interval(DateHistogram.Interval.SECOND).order(Histogram.Order.COUNT_DESC)) - ); - BytesReference reference = createAlertSource("* 0/1 * * * ? *", searchRequest, "aggregations.rate.buckets[0]?.doc_count > 5"); - alertClient().preparePutAlert("rate-alert").setAlertSource(reference).get(); + .aggregation(AggregationBuilders.dateHistogram("rate").field("_timestamp").interval(DateHistogram.Interval.SECOND).order(Histogram.Order.COUNT_DESC))); +// BytesReference reference = createAlertSource("* 0/1 * * * ? *", searchRequest, "aggregations.rate.buckets[0]?.doc_count > 5"); + alertClient().preparePutAlert("rate-alert") + .source(alertSourceBuilder() + .schedule(cron("* 0/1 * * * ? *")) + .input(searchInput(searchRequest)) + .condition(scriptCondition("aggregations.rate.buckets[0]?.doc_count > 5")) + .addAction(indexAction("my-index", "trail"))) + .get(); - Thread indexThread = new Thread(new R(500, 60000)); - indexThread.start(); - indexThread.join(); + Indexer indexer = new Indexer(500, 60000); + indexer.start(); + indexer.join(); assertAlertWithExactPerformedActionsCount("rate-alert", 0); assertAlertWithNoActionNeeded("rate-alert", 1); - indexThread = new Thread(new R(100, 60000)); - indexThread.start(); - indexThread.join(); + indexer = new Indexer(100, 60000); + indexer.start(); + indexer.join(); + assertAlertWithMinimumPerformedActionsCount("rate-alert", 1); } @@ -235,7 +269,7 @@ public class BasicAlertingTest extends AbstractAlertingTests { @Test public void testConditionSearchWithSource() throws Exception { testConditionSearch( - createConditionSearchRequest("my-index").source(searchSourceBuilder) + AlertsTestUtils.newInputSearchRequest("my-index").source(searchSourceBuilder) ); } @@ -246,7 +280,7 @@ public class BasicAlertingTest extends AbstractAlertingTests { .setId("my-template") .setSource(jsonBuilder().startObject().field("template").value(searchSourceBuilder).endObject()) .get(); - SearchRequest searchRequest = createConditionSearchRequest("my-index"); + SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index"); searchRequest.templateName("my-template"); searchRequest.templateType(ScriptService.ScriptType.INDEXED); testConditionSearch(searchRequest); @@ -259,7 +293,7 @@ public class BasicAlertingTest extends AbstractAlertingTests { alertClient().prepareDeleteAlert(alertName).get(); alertClient().preparePutAlert(alertName) - .setAlertSource(createAlertSource(String.format(Locale.ROOT, "0/%s * * * * ? *", (scheduleTimeInMs / 1000)), request, "return hits.total >= 3")) + .source(createAlertSource(String.format(Locale.ROOT, "0/%s * * * * ? *", (scheduleTimeInMs / 1000)), request, "return hits.total >= 3")) .get(); long time1 = System.currentTimeMillis(); diff --git a/src/test/java/org/elasticsearch/alerts/BootStrapTest.java b/src/test/java/org/elasticsearch/alerts/test/integration/BootStrapTests.java similarity index 88% rename from src/test/java/org/elasticsearch/alerts/BootStrapTest.java rename to src/test/java/org/elasticsearch/alerts/test/integration/BootStrapTests.java index ed3deb26585..710285c5a3f 100644 --- a/src/test/java/org/elasticsearch/alerts/BootStrapTest.java +++ b/src/test/java/org/elasticsearch/alerts/test/integration/BootStrapTests.java @@ -3,12 +3,15 @@ * 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; +package org.elasticsearch.alerts.test.integration; import org.elasticsearch.action.WriteConsistencyLevel; import org.elasticsearch.action.count.CountResponse; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.alerts.Alert; +import org.elasticsearch.alerts.AlertsService; +import org.elasticsearch.alerts.AlertsStore; import org.elasticsearch.alerts.actions.Action; import org.elasticsearch.alerts.actions.Actions; import org.elasticsearch.alerts.condition.script.ScriptCondition; @@ -18,6 +21,8 @@ import org.elasticsearch.alerts.input.search.SearchInput; import org.elasticsearch.alerts.scheduler.schedule.CronSchedule; import org.elasticsearch.alerts.support.Script; import org.elasticsearch.alerts.support.init.proxy.ClientProxy; +import org.elasticsearch.alerts.test.AbstractAlertsIntegrationTests; +import org.elasticsearch.alerts.test.AlertsTestUtils; import org.elasticsearch.alerts.transform.SearchTransform; import org.elasticsearch.alerts.transport.actions.put.PutAlertResponse; import org.elasticsearch.alerts.transport.actions.stats.AlertsStatsResponse; @@ -43,13 +48,13 @@ import static org.hamcrest.core.IsEqual.equalTo; /** */ -public class BootStrapTest extends AbstractAlertingTests { +public class BootStrapTests extends AbstractAlertsIntegrationTests { @Test public void testBootStrapAlerts() throws Exception { ensureAlertingStarted(); - SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); + SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); BytesReference alertSource = createAlertSource("0 0/5 * * * ? *", searchRequest, "hits.total == 1"); client().prepareIndex(AlertsStore.ALERT_INDEX, AlertsStore.ALERT_TYPE, "my-first-alert") .setSource(alertSource) @@ -76,16 +81,17 @@ public class BootStrapTest extends AbstractAlertingTests { assertThat(response.getAlertManagerStarted(), equalTo(AlertsService.State.STARTED)); assertThat(response.getNumberOfRegisteredAlerts(), equalTo(0L)); - SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); + SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); Alert alert = new Alert( "test-serialization", new CronSchedule("0/5 * * * * ? 2035"), //Set this into the future so we don't get any extra runs new SearchInput(logger, scriptService(), ClientProxy.of(client()), searchRequest), new ScriptCondition(logger, scriptService(), new Script("return true")), new SearchTransform(logger, scriptService(), ClientProxy.of(client()), searchRequest), - new Actions(new ArrayList()), null, new Alert.Status(), new TimeValue(0) - - ); + new Actions(new ArrayList()), + null, // metadata + new TimeValue(0), + new Alert.Status()); XContentBuilder builder = jsonBuilder().value(alert); IndexResponse indexResponse = client().prepareIndex(AlertsStore.ALERT_INDEX, AlertsStore.ALERT_TYPE, alert.name()) @@ -124,7 +130,7 @@ public class BootStrapTest extends AbstractAlertingTests { DateTime now = new DateTime(DateTimeZone.UTC); long numberOfAlertHistoryIndices = randomIntBetween(2,8); long numberOfAlertHistoryEntriesPerIndex = randomIntBetween(5,10); - SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); + SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); for (int i = 0; i < numberOfAlertHistoryIndices; i++) { DateTime historyIndexDate = now.minus((new TimeValue(i, TimeUnit.DAYS)).getMillis()); @@ -143,14 +149,13 @@ public class BootStrapTest extends AbstractAlertingTests { new ScriptCondition(logger, scriptService(), new Script("return true")), new SearchTransform(logger, scriptService(), ClientProxy.of(client()), searchRequest), new Actions(new ArrayList()), - null, - new Alert.Status(), - new TimeValue(0) - ); + null, // metatdata + new TimeValue(0), + new Alert.Status()); XContentBuilder jsonBuilder = jsonBuilder(); alert.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS); - PutAlertResponse putAlertResponse = alertClient().preparePutAlert(alert.name()).setAlertSource(jsonBuilder.bytes()).get(); + PutAlertResponse putAlertResponse = alertClient().preparePutAlert(alert.name()).source(jsonBuilder.bytes()).get(); assertTrue(putAlertResponse.indexResponse().isCreated()); FiredAlert firedAlert = new FiredAlert(alert, historyIndexDate, historyIndexDate); diff --git a/src/test/java/org/elasticsearch/alerts/NoMasterNodeTests.java b/src/test/java/org/elasticsearch/alerts/test/integration/NoMasterNodeTests.java similarity index 88% rename from src/test/java/org/elasticsearch/alerts/NoMasterNodeTests.java rename to src/test/java/org/elasticsearch/alerts/test/integration/NoMasterNodeTests.java index f69288f6044..2b918b32501 100644 --- a/src/test/java/org/elasticsearch/alerts/NoMasterNodeTests.java +++ b/src/test/java/org/elasticsearch/alerts/test/integration/NoMasterNodeTests.java @@ -3,10 +3,13 @@ * 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; +package org.elasticsearch.alerts.test.integration; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.alerts.AlertsService; +import org.elasticsearch.alerts.test.AbstractAlertsIntegrationTests; +import org.elasticsearch.alerts.test.AlertsTestUtils; import org.elasticsearch.alerts.transport.actions.delete.DeleteAlertResponse; import org.elasticsearch.alerts.transport.actions.stats.AlertsStatsResponse; import org.elasticsearch.client.Client; @@ -20,6 +23,7 @@ import org.elasticsearch.discovery.DiscoverySettings; import org.elasticsearch.discovery.MasterNotDiscoveredException; import org.elasticsearch.discovery.zen.elect.ElectMasterService; import org.elasticsearch.test.ElasticsearchIntegrationTest; +import org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope; import org.elasticsearch.test.discovery.ClusterDiscoveryConfiguration; import org.elasticsearch.test.junit.annotations.TestLogging; import org.junit.Test; @@ -28,14 +32,15 @@ import java.util.concurrent.TimeUnit; import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource; +import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.core.Is.is; /** */ -@ElasticsearchIntegrationTest.ClusterScope(scope = ElasticsearchIntegrationTest.Scope.TEST, numClientNodes = 0, transportClientRatio = 0, randomDynamicTemplates = false, numDataNodes = 0) -public class NoMasterNodeTests extends AbstractAlertingTests { +@ClusterScope(scope = TEST, numClientNodes = 0, transportClientRatio = 0, randomDynamicTemplates = false, numDataNodes = 0) +public class NoMasterNodeTests extends AbstractAlertsIntegrationTests { private ClusterDiscoveryConfiguration.UnicastZen config; @@ -60,10 +65,10 @@ public class NoMasterNodeTests extends AbstractAlertingTests { // Have a sample document in the index, the alert is going to evaluate client().prepareIndex("my-index", "my-type").setSource("field", "value").get(); - SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); + SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); BytesReference alertSource = createAlertSource("0/5 * * * * ? *", searchRequest, "hits.total == 1"); alertClient().preparePutAlert("my-first-alert") - .setAlertSource(alertSource) + .source(alertSource) .get(); assertAlertWithMinimumPerformedActionsCount("my-first-alert", 1); @@ -88,7 +93,7 @@ public class NoMasterNodeTests extends AbstractAlertingTests { // Add a new alert and wait for its condition to be met alertClient().preparePutAlert("my-second-alert") - .setAlertSource(alertSource) + .source(alertSource) .get(); assertAlertWithMinimumPerformedActionsCount("my-second-alert", 1); } @@ -108,10 +113,10 @@ public class NoMasterNodeTests extends AbstractAlertingTests { ensureAlertingStarted(); for (int i = 1; i <= numberOfAlerts; i++) { String alertName = "alert" + i; - SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); + SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); BytesReference alertSource = createAlertSource("0/5 * * * * ? *", searchRequest, "hits.total == 1"); alertClient().preparePutAlert(alertName) - .setAlertSource(alertSource) + .source(alertSource) .get(); } ensureGreen(); diff --git a/src/test/java/org/elasticsearch/alerts/TransformSearchTest.java b/src/test/java/org/elasticsearch/alerts/test/integration/TransformSearchTests.java similarity index 51% rename from src/test/java/org/elasticsearch/alerts/TransformSearchTest.java rename to src/test/java/org/elasticsearch/alerts/test/integration/TransformSearchTests.java index 36171492b3a..9c88bf9d7be 100644 --- a/src/test/java/org/elasticsearch/alerts/TransformSearchTest.java +++ b/src/test/java/org/elasticsearch/alerts/test/integration/TransformSearchTests.java @@ -3,40 +3,34 @@ * 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; +package org.elasticsearch.alerts.test.integration; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.alerts.actions.Action; -import org.elasticsearch.alerts.actions.Actions; -import org.elasticsearch.alerts.actions.index.IndexAction; -import org.elasticsearch.alerts.condition.script.ScriptCondition; -import org.elasticsearch.alerts.input.search.SearchInput; -import org.elasticsearch.alerts.scheduler.schedule.CronSchedule; -import org.elasticsearch.alerts.support.Script; -import org.elasticsearch.alerts.support.init.proxy.ClientProxy; +import org.elasticsearch.alerts.scheduler.schedule.IntervalSchedule.Interval; +import org.elasticsearch.alerts.test.AbstractAlertsIntegrationTests; +import org.elasticsearch.alerts.test.AlertsTestUtils; import org.elasticsearch.alerts.transform.SearchTransform; import org.elasticsearch.alerts.transport.actions.put.PutAlertResponse; 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.script.ScriptService; import org.elasticsearch.search.SearchHit; import org.junit.Test; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; +import static org.elasticsearch.alerts.actions.ActionBuilders.indexAction; +import static org.elasticsearch.alerts.client.AlertSourceBuilder.alertSourceBuilder; +import static org.elasticsearch.alerts.input.InputBuilders.searchInput; +import static org.elasticsearch.alerts.transform.TransformBuilders.searchTransform; +import static org.elasticsearch.alerts.scheduler.schedule.Schedules.interval; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource; import static org.hamcrest.Matchers.greaterThan; /** */ -public class TransformSearchTest extends AbstractAlertingTests { +public class TransformSearchTests extends AbstractAlertsIntegrationTests { @Test public void testTransformSearchRequest() throws Exception { @@ -46,31 +40,23 @@ public class TransformSearchTest extends AbstractAlertingTests { index("my-payload-index","payload", "mytestresult"); refresh(); - SearchRequest conditionRequest = createConditionSearchRequest("my-condition-index").source(searchSource().query(matchAllQuery())); - SearchRequest transformRequest = createConditionSearchRequest("my-payload-index").source(searchSource().query(matchAllQuery())); + SearchRequest inputRequest = AlertsTestUtils.newInputSearchRequest("my-condition-index").source(searchSource().query(matchAllQuery())); + SearchRequest transformRequest = AlertsTestUtils.newInputSearchRequest("my-payload-index").source(searchSource().query(matchAllQuery())); transformRequest.searchType(SearchTransform.DEFAULT_SEARCH_TYPE); - List actions = new ArrayList<>(); - actions.add(new IndexAction(logger, ClientProxy.of(client()), "my-payload-output","result")); - Map metadata = new HashMap<>(); metadata.put("foo", "bar"); metadata.put("list", "baz"); - Alert alert = new Alert( - "test-serialization", - new CronSchedule("0/5 * * * * ? *"), - new SearchInput(logger, scriptService(), ClientProxy.of(client()), - conditionRequest), - new ScriptCondition(logger, scriptService(), new Script("return true")), - new SearchTransform(logger, scriptService(), ClientProxy.of(client()), transformRequest), - new Actions(actions), metadata, new Alert.Status(), new TimeValue(0) - ); - - XContentBuilder jsonBuilder = XContentFactory.jsonBuilder(); - alert.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS); - - PutAlertResponse putAlertResponse = alertClient().preparePutAlert("test-payload").setAlertSource(jsonBuilder.bytes()).get(); + PutAlertResponse putAlertResponse = alertClient().preparePutAlert("test-payload") + .source(alertSourceBuilder() + .schedule(interval(5, Interval.Unit.SECONDS)) + .input(searchInput(inputRequest)) + .transform(searchTransform(transformRequest)) + .addAction(indexAction("my-payload-output", "result")) + .metadata(metadata) + .throttlePeriod(TimeValue.timeValueSeconds(0))) + .get(); assertTrue(putAlertResponse.indexResponse().isCreated()); assertAlertWithMinimumPerformedActionsCount("test-payload", 1, false); diff --git a/src/test/java/org/elasticsearch/alerts/transform/ScriptTransformTests.java b/src/test/java/org/elasticsearch/alerts/transform/ScriptTransformTests.java index 87b0f47e0d2..cf66e734d1b 100644 --- a/src/test/java/org/elasticsearch/alerts/transform/ScriptTransformTests.java +++ b/src/test/java/org/elasticsearch/alerts/transform/ScriptTransformTests.java @@ -24,9 +24,7 @@ import java.util.Collections; import java.util.Map; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -41,7 +39,7 @@ public class ScriptTransformTests extends ElasticsearchTestCase { ScriptService.ScriptType type = randomFrom(ScriptService.ScriptType.values()); Map params = Collections.emptyMap(); Script script = new Script("_script", type, "_lang", params); - ScriptTransform transform = new ScriptTransform(script, service); + ScriptTransform transform = new ScriptTransform(service, script); DateTime now = new DateTime(); ExecutionContext ctx = mock(ExecutionContext.class);