Now input is separate from condition and condition just contains the decision logic.
``` "input": { "search": { "request": { "body": { "query": { "match_all": {} } } } } }, "condition": { "script": { "script": "return true" } }, ``` The result of this in the `alert_execution` looks like : ``` "input_result": { "search": { "payload": { "hits": { "total": 1, "hits": [ { "_type": "my-type", "_source": { "field": "value" }, "_id": "AUujS61M4FTW2U3Ztz5U", "_index": "my-index", "_score": 0.30685282 } ], "max_score": 0.30685282 }, "_shards": { "total": 5, "failed": 0, "successful": 5 }, "timed_out": false, "took": 1823 }, "request": { "body": { "query": { "match_all": {} } } } } } "condition_result": { "script": { "met": true } } ``` There are two Inputs currently the `SearchInput` as shown above and a `SimpleInput` that just contains a payload that will be returned in the result. There are three conditions, the `ScriptCondition` as shown above and an `AlwaysTrueCondition` and AlwaysFalseCondition` condition. Original commit: elastic/x-pack-elasticsearch@0d8ac24c5a
This commit is contained in:
parent
fa02c150b4
commit
46cefe261a
|
@ -8,14 +8,16 @@ 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.input.Input;
|
||||
import org.elasticsearch.alerts.input.InputRegistry;
|
||||
import org.elasticsearch.alerts.scheduler.schedule.Schedule;
|
||||
import org.elasticsearch.alerts.scheduler.schedule.ScheduleRegistry;
|
||||
import org.elasticsearch.alerts.throttle.AlertThrottler;
|
||||
import org.elasticsearch.alerts.throttle.Throttler;
|
||||
import org.elasticsearch.alerts.transform.Transform;
|
||||
import org.elasticsearch.alerts.transform.TransformRegistry;
|
||||
import org.elasticsearch.alerts.condition.Condition;
|
||||
import org.elasticsearch.alerts.condition.ConditionRegistry;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
|
@ -42,6 +44,7 @@ public class Alert implements Scheduler.Job, ToXContent {
|
|||
|
||||
private final String name;
|
||||
private final Schedule schedule;
|
||||
private final Input input;
|
||||
private final Condition condition;
|
||||
private final Actions actions;
|
||||
private final Throttler throttler;
|
||||
|
@ -54,9 +57,10 @@ public class Alert implements Scheduler.Job, ToXContent {
|
|||
@Nullable
|
||||
private final Transform transform;
|
||||
|
||||
public Alert(String name, Schedule schedule, Condition condition, Transform transform, TimeValue throttlePeriod, Actions actions, Map<String, Object> metadata, Status status) {
|
||||
public Alert(String name, Schedule schedule, Input input, Condition condition, Transform transform, Actions actions, Map<String, Object> metadata, Status status, TimeValue throttlePeriod) {
|
||||
this.name = name;
|
||||
this.schedule = schedule;
|
||||
this.input = input;
|
||||
this.condition = condition;
|
||||
this.actions = actions;
|
||||
this.status = status != null ? status : new Status();
|
||||
|
@ -75,6 +79,8 @@ public class Alert implements Scheduler.Job, ToXContent {
|
|||
return schedule;
|
||||
}
|
||||
|
||||
public Input input() { return input;}
|
||||
|
||||
public Condition condition() {
|
||||
return condition;
|
||||
}
|
||||
|
@ -134,6 +140,7 @@ public class Alert implements Scheduler.Job, ToXContent {
|
|||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(Parser.SCHEDULE_FIELD.getPreferredName()).startObject().field(schedule.type(), schedule).endObject();
|
||||
builder.field(Parser.INPUT_FIELD.getPreferredName()).startObject().field(input.type(), input).endObject();
|
||||
builder.field(Parser.CONDITION_FIELD.getPreferredName()).startObject().field(condition.type(), condition).endObject();
|
||||
if (transform != Transform.NOOP) {
|
||||
builder.field(Parser.TRANSFORM_FIELD.getPreferredName()).startObject().field(transform.type(), transform).endObject();
|
||||
|
@ -153,6 +160,7 @@ public class Alert implements Scheduler.Job, ToXContent {
|
|||
public static class Parser extends AbstractComponent {
|
||||
|
||||
public static final ParseField SCHEDULE_FIELD = new ParseField("schedule");
|
||||
public static final ParseField INPUT_FIELD = new ParseField("input");
|
||||
public static final ParseField CONDITION_FIELD = new ParseField("condition");
|
||||
public static final ParseField ACTIONS_FIELD = new ParseField("actions");
|
||||
public static final ParseField TRANSFORM_FIELD = new ParseField("transform");
|
||||
|
@ -164,16 +172,19 @@ public class Alert implements Scheduler.Job, ToXContent {
|
|||
private final ScheduleRegistry scheduleRegistry;
|
||||
private final TransformRegistry transformRegistry;
|
||||
private final ActionRegistry actionRegistry;
|
||||
private final InputRegistry inputRegistry;
|
||||
|
||||
@Inject
|
||||
public Parser(Settings settings, ConditionRegistry conditionRegistry, ScheduleRegistry scheduleRegistry,
|
||||
TransformRegistry transformRegistry, ActionRegistry actionRegistry) {
|
||||
TransformRegistry transformRegistry, ActionRegistry actionRegistry,
|
||||
InputRegistry inputRegistry) {
|
||||
|
||||
super(settings);
|
||||
this.conditionRegistry = conditionRegistry;
|
||||
this.scheduleRegistry = scheduleRegistry;
|
||||
this.transformRegistry = transformRegistry;
|
||||
this.actionRegistry = actionRegistry;
|
||||
this.inputRegistry = inputRegistry;
|
||||
}
|
||||
|
||||
public Alert parse(String name, boolean includeStatus, BytesReference source) {
|
||||
|
@ -189,6 +200,7 @@ 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;
|
||||
Actions actions = null;
|
||||
Transform transform = null;
|
||||
|
@ -206,6 +218,8 @@ public class Alert implements Scheduler.Job, ToXContent {
|
|||
} else if ((token.isValue() || token == XContentParser.Token.START_OBJECT || token == XContentParser.Token.START_ARRAY) && currentFieldName !=null ) {
|
||||
if (SCHEDULE_FIELD.match(currentFieldName)) {
|
||||
schedule = scheduleRegistry.parse(parser);
|
||||
} else if (INPUT_FIELD.match(currentFieldName)) {
|
||||
input = inputRegistry.parse(parser);
|
||||
} else if (CONDITION_FIELD.match(currentFieldName)) {
|
||||
condition = conditionRegistry.parse(parser);
|
||||
} else if (ACTIONS_FIELD.match(currentFieldName)) {
|
||||
|
@ -230,6 +244,9 @@ 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");
|
||||
}
|
||||
|
@ -237,7 +254,7 @@ public class Alert implements Scheduler.Job, ToXContent {
|
|||
throw new AlertsSettingsException("could not parse alert [" + name + "]. missing alert actions");
|
||||
}
|
||||
|
||||
return new Alert(name, schedule, condition, transform, throttlePeriod, actions, metatdata, status);
|
||||
return new Alert(name, schedule, input, condition, transform, actions, metatdata, status, throttlePeriod);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import org.elasticsearch.alerts.actions.Action;
|
|||
import org.elasticsearch.alerts.actions.ActionRegistry;
|
||||
import org.elasticsearch.alerts.condition.Condition;
|
||||
import org.elasticsearch.alerts.condition.ConditionRegistry;
|
||||
import org.elasticsearch.alerts.input.Input;
|
||||
import org.elasticsearch.alerts.input.InputRegistry;
|
||||
import org.elasticsearch.alerts.throttle.Throttler;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
|
@ -24,16 +26,18 @@ import java.util.Map;
|
|||
*/
|
||||
public class AlertExecution implements ToXContent {
|
||||
|
||||
private final Input.Result inputResult;
|
||||
private final Condition.Result conditionResult;
|
||||
private final Throttler.Result throttleResult;
|
||||
private final Map<String, Action.Result> actionsResults;
|
||||
private final Payload payload;
|
||||
|
||||
public AlertExecution(ExecutionContext context) {
|
||||
this(context.conditionResult(), context.throttleResult(), context.actionsResults(), context.payload());
|
||||
this(context.inputResult(), context.conditionResult(), context.throttleResult(), context.actionsResults(), context.payload());
|
||||
}
|
||||
|
||||
AlertExecution(Condition.Result conditionResult, Throttler.Result throttleResult, Map<String, Action.Result> actionsResults, Payload payload) {
|
||||
AlertExecution(Input.Result inputResult, Condition.Result conditionResult, Throttler.Result throttleResult, Map<String, Action.Result> actionsResults, Payload payload) {
|
||||
this.inputResult = inputResult;
|
||||
this.conditionResult = conditionResult;
|
||||
this.throttleResult = throttleResult;
|
||||
this.actionsResults = actionsResults;
|
||||
|
@ -59,6 +63,9 @@ public class AlertExecution implements ToXContent {
|
|||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
if (inputResult != null) {
|
||||
builder.startObject(Parser.INPUT_RESULT.getPreferredName()).field(inputResult.type(), inputResult).endObject();
|
||||
}
|
||||
if (conditionResult != null) {
|
||||
builder.startObject(Parser.CONDITION_RESULT.getPreferredName()).field(conditionResult.type(), conditionResult).endObject();
|
||||
}
|
||||
|
@ -82,16 +89,19 @@ public class AlertExecution implements ToXContent {
|
|||
|
||||
public static class Parser {
|
||||
|
||||
public static final ParseField INPUT_RESULT = new ParseField("input_result");
|
||||
public static final ParseField CONDITION_RESULT = new ParseField("condition_result");
|
||||
public static final ParseField PAYLOAD = new ParseField("payload");
|
||||
public static final ParseField ACTIONS_RESULTS = new ParseField("actions_results");
|
||||
public static final ParseField THROTTLED = new ParseField("throttled");
|
||||
public static final ParseField THROTTLE_REASON = new ParseField("throttle_reason");
|
||||
|
||||
public static AlertExecution parse(XContentParser parser, ConditionRegistry conditionRegistry, ActionRegistry actionRegistry) throws IOException {
|
||||
public static AlertExecution parse(XContentParser parser, ConditionRegistry conditionRegistry, ActionRegistry actionRegistry,
|
||||
InputRegistry inputRegistry) throws IOException {
|
||||
boolean throttled = false;
|
||||
String throttleReason = null;
|
||||
Map<String, Action.Result> actionResults = new HashMap<>();
|
||||
Input.Result inputResult = null;
|
||||
Condition.Result conditionResult = null;
|
||||
Payload payload = null;
|
||||
|
||||
|
@ -109,7 +119,9 @@ public class AlertExecution implements ToXContent {
|
|||
throw new AlertsException("unable to parse alert run. unexpected field [" + currentFieldName + "]");
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
if (CONDITION_RESULT.match(currentFieldName)) {
|
||||
if (INPUT_RESULT.match(currentFieldName)) {
|
||||
inputResult = inputRegistry.parseResult(parser);
|
||||
} else if (CONDITION_RESULT.match(currentFieldName)) {
|
||||
conditionResult = conditionRegistry.parseResult(parser);
|
||||
} else if (PAYLOAD.match(currentFieldName)) {
|
||||
payload = new Payload.XContent(parser);
|
||||
|
@ -128,7 +140,7 @@ public class AlertExecution implements ToXContent {
|
|||
}
|
||||
|
||||
Throttler.Result throttleResult = throttled ? Throttler.Result.throttle(throttleReason) : Throttler.Result.NO;
|
||||
return new AlertExecution(conditionResult, throttleResult, actionResults, payload );
|
||||
return new AlertExecution(inputResult, conditionResult, throttleResult, actionResults, payload );
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,9 @@ package org.elasticsearch.alerts;
|
|||
|
||||
import org.elasticsearch.alerts.actions.ActionModule;
|
||||
import org.elasticsearch.alerts.client.AlertsClientModule;
|
||||
import org.elasticsearch.alerts.condition.ConditionModule;
|
||||
import org.elasticsearch.alerts.history.HistoryModule;
|
||||
import org.elasticsearch.alerts.input.InputModule;
|
||||
import org.elasticsearch.alerts.rest.AlertsRestModule;
|
||||
import org.elasticsearch.alerts.scheduler.SchedulerModule;
|
||||
import org.elasticsearch.alerts.support.TemplateUtils;
|
||||
|
@ -16,7 +18,6 @@ import org.elasticsearch.alerts.support.init.InitializingModule;
|
|||
import org.elasticsearch.alerts.support.template.TemplateModule;
|
||||
import org.elasticsearch.alerts.transform.TransformModule;
|
||||
import org.elasticsearch.alerts.transport.AlertsTransportModule;
|
||||
import org.elasticsearch.alerts.condition.ConditionModule;
|
||||
import org.elasticsearch.common.collect.ImmutableList;
|
||||
import org.elasticsearch.common.inject.AbstractModule;
|
||||
import org.elasticsearch.common.inject.Module;
|
||||
|
@ -36,6 +37,7 @@ public class AlertsModule extends AbstractModule implements SpawnModules {
|
|||
new SchedulerModule(),
|
||||
new AlertsTransportModule(),
|
||||
new ConditionModule(),
|
||||
new InputModule(),
|
||||
new ActionModule(),
|
||||
new HistoryModule());
|
||||
}
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
package org.elasticsearch.alerts;
|
||||
|
||||
import org.elasticsearch.alerts.actions.Action;
|
||||
import org.elasticsearch.alerts.condition.Condition;
|
||||
import org.elasticsearch.alerts.input.Input;
|
||||
import org.elasticsearch.alerts.throttle.Throttler;
|
||||
import org.elasticsearch.alerts.transform.Transform;
|
||||
import org.elasticsearch.alerts.condition.Condition;
|
||||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
@ -24,6 +25,7 @@ public class ExecutionContext {
|
|||
private final DateTime fireTime;
|
||||
private final DateTime scheduledTime;
|
||||
|
||||
private Input.Result inputResult;
|
||||
private Condition.Result conditionResult;
|
||||
private Throttler.Result throttleResult;
|
||||
private Transform.Result transformResult;
|
||||
|
@ -58,10 +60,18 @@ public class ExecutionContext {
|
|||
return payload;
|
||||
}
|
||||
|
||||
public void onInputResult(Input.Result inputResult) {
|
||||
this.inputResult = inputResult;
|
||||
this.payload = inputResult.payload();
|
||||
}
|
||||
|
||||
public Input.Result inputResult() {
|
||||
return inputResult;
|
||||
}
|
||||
|
||||
public void onConditionResult(Condition.Result conditionResult) {
|
||||
this.conditionResult = conditionResult;
|
||||
this.payload = conditionResult.payload();
|
||||
alert.status().onCheck(conditionResult.met(), fireTime);
|
||||
this.conditionResult = conditionResult;
|
||||
}
|
||||
|
||||
public Condition.Result conditionResult() {
|
||||
|
|
|
@ -6,11 +6,9 @@
|
|||
package org.elasticsearch.alerts.condition;
|
||||
|
||||
import org.elasticsearch.alerts.ExecutionContext;
|
||||
import org.elasticsearch.alerts.Payload;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -20,6 +18,8 @@ import java.io.IOException;
|
|||
*/
|
||||
public abstract class Condition<R extends Condition.Result> implements ToXContent {
|
||||
|
||||
protected static final ParseField MET_FIELD = new ParseField("met");
|
||||
|
||||
protected final ESLogger logger;
|
||||
|
||||
protected Condition(ESLogger logger) {
|
||||
|
@ -60,39 +60,19 @@ public abstract class Condition<R extends Condition.Result> implements ToXConten
|
|||
|
||||
public abstract static class Result implements ToXContent {
|
||||
|
||||
public static final ParseField MET_FIELD = new ParseField("met");
|
||||
public static final ParseField PAYLOAD_FIELD = new ParseField("payload");
|
||||
|
||||
private final String type;
|
||||
private final boolean met;
|
||||
private final Payload payload;
|
||||
|
||||
public Result(String type, boolean met, Payload payload) {
|
||||
public Result(String type, boolean met) {
|
||||
this.type = type;
|
||||
this.met = met;
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public String type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean met() {
|
||||
return met;
|
||||
}
|
||||
public boolean met() { return met; }
|
||||
|
||||
public Payload payload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject()
|
||||
.field(MET_FIELD.getPreferredName(), met)
|
||||
.field(PAYLOAD_FIELD.getPreferredName(), payload);
|
||||
return toXContentBody(builder, params).endObject();
|
||||
}
|
||||
|
||||
protected abstract XContentBuilder toXContentBody(XContentBuilder builder, Params params) throws IOException;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
*/
|
||||
package org.elasticsearch.alerts.condition;
|
||||
|
||||
import org.elasticsearch.alerts.condition.search.ScriptSearchCondition;
|
||||
import org.elasticsearch.alerts.condition.simple.SimpleCondition;
|
||||
import org.elasticsearch.alerts.condition.script.ScriptCondition;
|
||||
import org.elasticsearch.alerts.condition.simple.AlwaysFalseCondition;
|
||||
import org.elasticsearch.alerts.condition.simple.AlwaysTrueCondition;
|
||||
import org.elasticsearch.common.inject.AbstractModule;
|
||||
import org.elasticsearch.common.inject.multibindings.MapBinder;
|
||||
|
||||
|
@ -28,10 +29,12 @@ public class ConditionModule extends AbstractModule {
|
|||
protected void configure() {
|
||||
|
||||
MapBinder<String, Condition.Parser> parsersBinder = MapBinder.newMapBinder(binder(), String.class, Condition.Parser.class);
|
||||
bind(ScriptSearchCondition.Parser.class).asEagerSingleton();
|
||||
parsersBinder.addBinding(ScriptSearchCondition.TYPE).to(ScriptSearchCondition.Parser.class);
|
||||
bind(SimpleCondition.Parser.class).asEagerSingleton();
|
||||
parsersBinder.addBinding(SimpleCondition.TYPE).to(SimpleCondition.Parser.class);
|
||||
bind(ScriptCondition.Parser.class).asEagerSingleton();
|
||||
parsersBinder.addBinding(ScriptCondition.TYPE).to(ScriptCondition.Parser.class);
|
||||
bind(AlwaysFalseCondition.Parser.class).asEagerSingleton();
|
||||
parsersBinder.addBinding(AlwaysFalseCondition.TYPE).to(AlwaysFalseCondition.Parser.class);
|
||||
bind(AlwaysTrueCondition.Parser.class).asEagerSingleton();
|
||||
parsersBinder.addBinding(AlwaysTrueCondition.TYPE).to(AlwaysTrueCondition.Parser.class);
|
||||
|
||||
for (Map.Entry<String, Class<? extends Condition.Parser>> entry : parsers.entrySet()) {
|
||||
bind(entry.getValue()).asEagerSingleton();
|
||||
|
|
|
@ -49,6 +49,13 @@ public class ConditionRegistry {
|
|||
return condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the contents of parser to create the correct Condition.Result
|
||||
*
|
||||
* @param parser The parser containing the condition result definition
|
||||
* @return A new condition result instance from the parser
|
||||
* @throws IOException
|
||||
*/
|
||||
public Condition.Result parseResult(XContentParser parser) throws IOException {
|
||||
String type = null;
|
||||
XContentParser.Token token;
|
||||
|
|
|
@ -3,33 +3,30 @@
|
|||
* 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.search;
|
||||
package org.elasticsearch.alerts.condition.script;
|
||||
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.alerts.ExecutionContext;
|
||||
import org.elasticsearch.alerts.Payload;
|
||||
import org.elasticsearch.alerts.condition.Condition;
|
||||
import org.elasticsearch.alerts.condition.ConditionException;
|
||||
import org.elasticsearch.alerts.support.AlertUtils;
|
||||
import org.elasticsearch.alerts.support.init.proxy.ClientProxy;
|
||||
import org.elasticsearch.alerts.support.init.proxy.ScriptServiceProxy;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
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 org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
*
|
||||
* This class executes a script against the ctx payload and returns a boolean
|
||||
*/
|
||||
public class ScriptSearchCondition extends SearchCondition {
|
||||
public class ScriptCondition extends Condition<ScriptCondition.Result> {
|
||||
|
||||
public static final String TYPE = "script";
|
||||
|
||||
|
@ -37,13 +34,14 @@ public class ScriptSearchCondition extends SearchCondition {
|
|||
private final ScriptService.ScriptType scriptType;
|
||||
private final String scriptLang;
|
||||
|
||||
public ScriptSearchCondition(ESLogger logger, ScriptServiceProxy scriptService, ClientProxy client,
|
||||
SearchRequest request, String script, ScriptService.ScriptType scriptType,
|
||||
String scriptLang) {
|
||||
super(logger, scriptService, client, request);
|
||||
private final ScriptServiceProxy scriptService;
|
||||
|
||||
public ScriptCondition(ESLogger logger, ScriptServiceProxy scriptService, String script, ScriptService.ScriptType scriptType, String scriptLang) {
|
||||
super(logger);
|
||||
this.script = script;
|
||||
this.scriptType = scriptType;
|
||||
this.scriptLang = scriptLang;
|
||||
this.scriptService = scriptService;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -52,40 +50,38 @@ public class ScriptSearchCondition extends SearchCondition {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Result processSearchResponse(SearchResponse response) {
|
||||
Payload payload = new Payload.ActionResponse(response);
|
||||
public Result execute(ExecutionContext ctx) throws IOException {
|
||||
return processPayload(ctx.payload());
|
||||
}
|
||||
|
||||
protected Result processPayload(Payload payload) {
|
||||
ExecutableScript executable = scriptService.executable(scriptLang, script, scriptType, payload.data());
|
||||
Object value = executable.run();
|
||||
if (value instanceof Boolean) {
|
||||
return new Result(TYPE, (Boolean) value, request, payload);
|
||||
return (Boolean) value ? Result.MET : Result.UNMET;
|
||||
}
|
||||
throw new ConditionException("condition script [" + script + "] did not return a boolean value");
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(Parser.REQUEST_FIELD.getPreferredName());
|
||||
AlertUtils.writeSearchRequest(request, builder, params);
|
||||
builder.field(ScriptService.SCRIPT_INLINE.getPreferredName(), script);
|
||||
builder.field(Parser.SCRIPT_TYPE_FIELD.getPreferredName(), scriptType);
|
||||
builder.field(ScriptService.SCRIPT_LANG.getPreferredName(), scriptLang);
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
public static class Parser extends AbstractComponent implements SearchCondition.Parser<Result, ScriptSearchCondition> {
|
||||
public static class Parser extends AbstractComponent implements Condition.Parser<Result, ScriptCondition> {
|
||||
|
||||
public static ParseField REQUEST_FIELD = new ParseField("request");
|
||||
public static ParseField SCRIPT_TYPE_FIELD = new ParseField("script_type");
|
||||
|
||||
private final ClientProxy client;
|
||||
private final ScriptServiceProxy scriptService;
|
||||
|
||||
@Inject
|
||||
public Parser(Settings settings, ClientProxy client, ScriptServiceProxy scriptService) {
|
||||
public Parser(Settings settings, ScriptServiceProxy service) {
|
||||
super(settings);
|
||||
this.client = client;
|
||||
this.scriptService = scriptService;
|
||||
scriptService = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -94,9 +90,7 @@ public class ScriptSearchCondition extends SearchCondition {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ScriptSearchCondition parse(XContentParser parser) throws IOException {
|
||||
|
||||
SearchRequest request = null;
|
||||
public ScriptCondition parse(XContentParser parser) throws IOException {
|
||||
String scriptLang = null;
|
||||
String script = null;
|
||||
ScriptService.ScriptType scriptType = ScriptService.ScriptType.INLINE;
|
||||
|
@ -107,67 +101,77 @@ public class ScriptSearchCondition extends SearchCondition {
|
|||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if ((token.isValue() || token == XContentParser.Token.START_OBJECT) && currentFieldName != null) {
|
||||
if (REQUEST_FIELD.match(currentFieldName)) {
|
||||
request = AlertUtils.readSearchRequest(parser, DEFAULT_SEARCH_TYPE);
|
||||
} else if (ScriptService.SCRIPT_ID.match(currentFieldName)) {
|
||||
if (ScriptService.SCRIPT_ID.match(currentFieldName)) {
|
||||
script = parser.text();
|
||||
scriptType = ScriptService.ScriptType.INDEXED;
|
||||
} else if (ScriptService.SCRIPT_INLINE.match(currentFieldName)) {
|
||||
script = parser.text();
|
||||
} else if (SCRIPT_TYPE_FIELD.match(currentFieldName)) {
|
||||
scriptType = ScriptService.ScriptType.valueOf(parser.text());
|
||||
String value = parser.text();
|
||||
try {
|
||||
scriptType = ScriptService.ScriptType.valueOf(value.toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new ConditionException("could not parse [script] condition. unknown script type [" + value + "]");
|
||||
}
|
||||
} else if (ScriptService.SCRIPT_LANG.match(currentFieldName)) {
|
||||
scriptLang = parser.text();
|
||||
} else {
|
||||
throw new ConditionException("could not parse script condition. unexpected field [" + currentFieldName + "]");
|
||||
throw new ConditionException("could not parse [script] condition. unexpected field [" + currentFieldName + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (request == null) {
|
||||
throw new ConditionException("could not parse script condition. missing required search request");
|
||||
}
|
||||
|
||||
if (script == null) {
|
||||
throw new ConditionException("could not parse script condition. either [script] or [script_id] must be provided");
|
||||
throw new ConditionException("could not parse [script] condition. either [script] or [script_id] must be provided");
|
||||
}
|
||||
|
||||
return new ScriptSearchCondition(logger, scriptService, client, request, script, scriptType, scriptLang);
|
||||
return new ScriptCondition(logger, scriptService, script, scriptType, scriptLang);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptSearchCondition.Result parseResult(XContentParser parser) throws IOException {
|
||||
public Result parseResult(XContentParser parser) throws IOException {
|
||||
Boolean met = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
boolean met = false;
|
||||
Payload payload = null;
|
||||
SearchRequest request = null;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token.isValue()) {
|
||||
if (token == XContentParser.Token.VALUE_BOOLEAN) {
|
||||
if (Condition.Result.MET_FIELD.match(currentFieldName)) {
|
||||
if (Condition.MET_FIELD.match(currentFieldName)) {
|
||||
met = parser.booleanValue();
|
||||
} else {
|
||||
throw new ConditionException("unable to parse condition result. unexpected field [" + currentFieldName + "]");
|
||||
throw new ConditionException("unable to parse [script] condition result. expected a boolean, got [" + parser.text() + "]");
|
||||
}
|
||||
} else {
|
||||
throw new ConditionException("unable to parse condition result. unexpected field [" + currentFieldName + "]");
|
||||
throw new ConditionException("unable to parse [script] condition result. unexpected field [" + currentFieldName + "]");
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
if (Condition.Result.PAYLOAD_FIELD.match(currentFieldName)) {
|
||||
payload = new Payload.Simple(parser.map()); ///@TODO FIXME
|
||||
} else if (REQUEST_FIELD.match(currentFieldName)) {
|
||||
request = AlertUtils.readSearchRequest(parser, DEFAULT_SEARCH_TYPE);
|
||||
} else {
|
||||
throw new ConditionException("unable to parse condition result. unexpected field [" + currentFieldName + "]");
|
||||
}
|
||||
} else {
|
||||
throw new ConditionException("unable to parse condition result. unexpected token [" + token + "]");
|
||||
}
|
||||
}
|
||||
return new Result(TYPE, met, request, payload);
|
||||
|
||||
if (met == null) {
|
||||
throw new ConditionException("could not parse [script] condition result. [met] is a required field");
|
||||
}
|
||||
|
||||
return met ? Result.MET : Result.UNMET;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Result extends Condition.Result {
|
||||
|
||||
static final Result MET = new Result(true);
|
||||
static final Result UNMET = new Result(false);
|
||||
|
||||
private Result(boolean met) {
|
||||
super(TYPE, met);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
return builder.startObject()
|
||||
.field(MET_FIELD.getPreferredName(), met())
|
||||
.endObject();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,128 +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.condition.search;
|
||||
|
||||
import org.elasticsearch.ElasticsearchIllegalStateException;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.SearchType;
|
||||
import org.elasticsearch.alerts.ExecutionContext;
|
||||
import org.elasticsearch.alerts.Payload;
|
||||
import org.elasticsearch.alerts.support.AlertUtils;
|
||||
import org.elasticsearch.alerts.support.Variables;
|
||||
import org.elasticsearch.alerts.support.init.proxy.ClientProxy;
|
||||
import org.elasticsearch.alerts.support.init.proxy.ScriptServiceProxy;
|
||||
import org.elasticsearch.alerts.condition.Condition;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.collect.MapBuilder;
|
||||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.alerts.support.AlertsDateUtils.formatDate;
|
||||
|
||||
public abstract class SearchCondition extends Condition<SearchCondition.Result> {
|
||||
|
||||
public static final SearchType DEFAULT_SEARCH_TYPE = SearchType.COUNT;
|
||||
|
||||
protected final ScriptServiceProxy scriptService;
|
||||
protected final ClientProxy client;
|
||||
protected final SearchRequest request;
|
||||
|
||||
public SearchCondition(ESLogger logger, ScriptServiceProxy scriptService, ClientProxy client, SearchRequest request) {
|
||||
super(logger);
|
||||
this.scriptService = scriptService;
|
||||
this.client = client;
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result execute(ExecutionContext ctx) throws IOException {
|
||||
SearchRequest request = createSearchRequestWithTimes(this.request, ctx.scheduledTime(), ctx.fireTime(), scriptService);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("running query for [{}]", ctx.alert().name(), XContentHelper.convertToJson(request.source(), false, true));
|
||||
}
|
||||
|
||||
// actionGet deals properly with InterruptedException
|
||||
SearchResponse response = client.search(request).actionGet();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("got [{}] hits", ctx.alert().name(), response.getHits().getTotalHits());
|
||||
for (SearchHit hit : response.getHits()) {
|
||||
logger.debug("hit [{}]", XContentHelper.toString(hit));
|
||||
}
|
||||
|
||||
}
|
||||
return processSearchResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the search response and returns the appropriate condition result
|
||||
*/
|
||||
protected abstract Result processSearchResponse(SearchResponse response);
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new search request applying the scheduledFireTime and fireTime to the original request
|
||||
*/
|
||||
public static SearchRequest createSearchRequestWithTimes(SearchRequest requestPrototype, DateTime scheduledFireTime, DateTime fireTime, ScriptServiceProxy scriptService) throws IOException {
|
||||
SearchRequest request = new SearchRequest(requestPrototype)
|
||||
.indicesOptions(requestPrototype.indicesOptions())
|
||||
.indices(requestPrototype.indices());
|
||||
if (Strings.hasLength(requestPrototype.source())) {
|
||||
Map<String, String> templateParams = new HashMap<>();
|
||||
templateParams.put(Variables.SCHEDULED_FIRE_TIME, formatDate(scheduledFireTime));
|
||||
templateParams.put(Variables.FIRE_TIME, formatDate(fireTime));
|
||||
String requestSource = XContentHelper.convertToJson(requestPrototype.source(), false);
|
||||
ExecutableScript script = scriptService.executable("mustache", requestSource, ScriptService.ScriptType.INLINE, templateParams);
|
||||
request.source((BytesReference) script.unwrap(script.run()), false);
|
||||
} else if (requestPrototype.templateName() != null) {
|
||||
MapBuilder<String, String> templateParams = MapBuilder.newMapBuilder(requestPrototype.templateParams())
|
||||
.put(Variables.SCHEDULED_FIRE_TIME, formatDate(scheduledFireTime))
|
||||
.put(Variables.FIRE_TIME, formatDate(fireTime));
|
||||
request.templateParams(templateParams.map());
|
||||
request.templateName(requestPrototype.templateName());
|
||||
request.templateType(requestPrototype.templateType());
|
||||
} else {
|
||||
throw new ElasticsearchIllegalStateException("Search requests needs either source or template name");
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
public static class Result extends Condition.Result {
|
||||
|
||||
public static final ParseField REQUEST_FIELD = new ParseField("request");
|
||||
|
||||
private final SearchRequest request;
|
||||
|
||||
public Result(String type, boolean met, SearchRequest request, Payload payload) {
|
||||
super(type, met, payload);
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
public SearchRequest request() {
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContentBody(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.field(REQUEST_FIELD.getPreferredName());
|
||||
AlertUtils.writeSearchRequest(request(), builder, params);
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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.simple;
|
||||
|
||||
import org.elasticsearch.alerts.ExecutionContext;
|
||||
import org.elasticsearch.alerts.condition.Condition;
|
||||
import org.elasticsearch.alerts.condition.ConditionException;
|
||||
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.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class AlwaysFalseCondition extends Condition<Condition.Result> {
|
||||
|
||||
public static final String TYPE = "always_false";
|
||||
public static final Result RESULT = new Result(TYPE, false) {
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
return builder.startObject().endObject();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public AlwaysFalseCondition(ESLogger logger) {
|
||||
super(logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result execute(ExecutionContext ctx) throws IOException {
|
||||
return RESULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
return builder.startObject().endObject();
|
||||
}
|
||||
|
||||
public static class Parser extends AbstractComponent implements Condition.Parser<Result, AlwaysFalseCondition> {
|
||||
|
||||
@Inject
|
||||
public Parser(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlwaysFalseCondition parse(XContentParser parser) throws IOException {
|
||||
return new AlwaysFalseCondition(logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result parseResult(XContentParser parser) throws IOException {
|
||||
if (parser.currentToken() != XContentParser.Token.START_OBJECT){
|
||||
throw new ConditionException("unable to parse [" + TYPE + "] condition result. expected a start object token, found [" + parser.currentToken() + "]");
|
||||
}
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
if (token != XContentParser.Token.END_OBJECT) {
|
||||
throw new ConditionException("unable to parse [" + TYPE + "] condition result. expected an empty object, but found an object with [" + token + "]");
|
||||
}
|
||||
return RESULT;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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.simple;
|
||||
|
||||
import org.elasticsearch.alerts.ExecutionContext;
|
||||
import org.elasticsearch.alerts.condition.Condition;
|
||||
import org.elasticsearch.alerts.condition.ConditionException;
|
||||
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.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class AlwaysTrueCondition extends Condition<Condition.Result> {
|
||||
|
||||
public static final String TYPE = "always_true";
|
||||
public static final Result RESULT = new Result(TYPE, true) {
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
return builder.startObject().endObject();
|
||||
}
|
||||
};
|
||||
|
||||
public AlwaysTrueCondition(ESLogger logger) {
|
||||
super(logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result execute(ExecutionContext ctx) throws IOException {
|
||||
return RESULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
return builder.startObject().endObject();
|
||||
}
|
||||
|
||||
|
||||
public static class Parser extends AbstractComponent implements Condition.Parser<Result, AlwaysTrueCondition> {
|
||||
|
||||
@Inject
|
||||
public Parser(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlwaysTrueCondition parse(XContentParser parser) throws IOException {
|
||||
if (parser.currentToken() != XContentParser.Token.START_OBJECT){
|
||||
throw new ConditionException("unable to parse [" + TYPE + "] condition. expected a start object token, found [" + parser.currentToken() + "]");
|
||||
}
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
if (token != XContentParser.Token.END_OBJECT) {
|
||||
throw new ConditionException("unable to parse [" + TYPE + "] condition. expected an empty object, but found an object with [" + token + "]");
|
||||
}
|
||||
return new AlwaysTrueCondition(logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result parseResult(XContentParser parser) throws IOException {
|
||||
if (parser.currentToken() != XContentParser.Token.START_OBJECT){
|
||||
throw new ConditionException("unable to parse [" + TYPE + "] condition result. expected a start object token, found [" + parser.currentToken() + "]");
|
||||
}
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
if (token != XContentParser.Token.END_OBJECT) {
|
||||
throw new ConditionException("unable to parse [" + TYPE + "] condition. expected an empty object, but found an object with [" + token + "]");
|
||||
}
|
||||
return RESULT;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -6,7 +6,6 @@
|
|||
package org.elasticsearch.alerts.history;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.ElasticsearchIllegalArgumentException;
|
||||
import org.elasticsearch.alerts.Alert;
|
||||
import org.elasticsearch.alerts.AlertExecution;
|
||||
import org.elasticsearch.alerts.AlertsException;
|
||||
|
@ -14,6 +13,8 @@ import org.elasticsearch.alerts.AlertsSettingsException;
|
|||
import org.elasticsearch.alerts.actions.ActionRegistry;
|
||||
import org.elasticsearch.alerts.condition.Condition;
|
||||
import org.elasticsearch.alerts.condition.ConditionRegistry;
|
||||
import org.elasticsearch.alerts.input.Input;
|
||||
import org.elasticsearch.alerts.input.InputRegistry;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
|
@ -37,6 +38,7 @@ public class FiredAlert implements ToXContent {
|
|||
private String name;
|
||||
private DateTime fireTime;
|
||||
private DateTime scheduledTime;
|
||||
private Input input;
|
||||
private Condition condition;
|
||||
private State state;
|
||||
private AlertExecution execution;
|
||||
|
@ -58,6 +60,7 @@ public class FiredAlert implements ToXContent {
|
|||
this.fireTime = fireTime;
|
||||
this.scheduledTime = scheduledTime;
|
||||
this.condition = alert.condition();
|
||||
this.input = alert.input();
|
||||
this.state = State.AWAITS_EXECUTION;
|
||||
this.metadata = alert.metadata();
|
||||
this.version = 1;
|
||||
|
@ -79,6 +82,8 @@ public class FiredAlert implements ToXContent {
|
|||
return fireTime;
|
||||
}
|
||||
|
||||
public Input input() { return input; }
|
||||
|
||||
public Condition condition() {
|
||||
return condition;
|
||||
}
|
||||
|
@ -206,12 +211,15 @@ public class FiredAlert implements ToXContent {
|
|||
|
||||
private final ConditionRegistry conditionRegistry;
|
||||
private final ActionRegistry actionRegistry;
|
||||
private final InputRegistry inputRegistry;
|
||||
|
||||
@Inject
|
||||
public Parser(Settings settings, ConditionRegistry conditionRegistry, ActionRegistry actionRegistry) {
|
||||
public Parser(Settings settings, ConditionRegistry conditionRegistry, ActionRegistry actionRegistry,
|
||||
InputRegistry inputRegistry) {
|
||||
super(settings);
|
||||
this.conditionRegistry = conditionRegistry;
|
||||
this.actionRegistry = actionRegistry;
|
||||
this.inputRegistry = inputRegistry;
|
||||
}
|
||||
|
||||
public FiredAlert parse(BytesReference source, String historyId, long version) {
|
||||
|
@ -234,12 +242,15 @@ public class FiredAlert implements ToXContent {
|
|||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
if (Alert.Parser.CONDITION_FIELD.match(currentFieldName)) {
|
||||
if (Alert.Parser.INPUT_FIELD.match(currentFieldName)) {
|
||||
alert.input = inputRegistry.parse(parser);
|
||||
} else if (Alert.Parser.CONDITION_FIELD.match(currentFieldName)) {
|
||||
alert.condition = conditionRegistry.parse(parser);
|
||||
} else if (METADATA_FIELD.match(currentFieldName)) {
|
||||
alert.metadata = parser.map();
|
||||
} else if (ALERT_EXECUTION_FIELD.match(currentFieldName)) {
|
||||
alert.execution = AlertExecution.Parser.parse(parser, conditionRegistry, actionRegistry);
|
||||
alert.execution = AlertExecution.Parser.parse(parser, conditionRegistry, actionRegistry,
|
||||
inputRegistry);
|
||||
} else {
|
||||
throw new AlertsException("unable to parse fired alert. unexpected field [" + currentFieldName + "]");
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.elasticsearch.ElasticsearchIllegalStateException;
|
|||
import org.elasticsearch.alerts.*;
|
||||
import org.elasticsearch.alerts.actions.Action;
|
||||
import org.elasticsearch.alerts.condition.Condition;
|
||||
import org.elasticsearch.alerts.input.Input;
|
||||
import org.elasticsearch.alerts.scheduler.Scheduler;
|
||||
import org.elasticsearch.alerts.support.Callback;
|
||||
import org.elasticsearch.alerts.throttle.Throttler;
|
||||
|
@ -252,6 +253,9 @@ public class HistoryService extends AbstractComponent {
|
|||
*/
|
||||
|
||||
AlertExecution execute(ExecutionContext ctx) throws IOException {
|
||||
|
||||
Input.Result inputResult = alert.input().execute(ctx);
|
||||
ctx.onInputResult(inputResult);
|
||||
Condition.Result conditionResult = alert.condition().execute(ctx);
|
||||
ctx.onConditionResult(conditionResult);
|
||||
|
||||
|
@ -260,7 +264,7 @@ public class HistoryService extends AbstractComponent {
|
|||
ctx.onThrottleResult(throttleResult);
|
||||
|
||||
if (!throttleResult.throttle()) {
|
||||
Transform.Result result = alert.transform().apply(ctx, conditionResult.payload());
|
||||
Transform.Result result = alert.transform().apply(ctx, inputResult.payload());
|
||||
ctx.onTransformResult(result);
|
||||
for (Action action : alert.actions()) {
|
||||
Action.Result actionResult = action.execute(ctx, result.payload());
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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.ParseField;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public abstract class Input<R extends Input.Result> implements ToXContent {
|
||||
|
||||
protected final ESLogger logger;
|
||||
|
||||
protected Input(ESLogger logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the type of this input
|
||||
*/
|
||||
public abstract String type();
|
||||
|
||||
/**
|
||||
* Executes this input
|
||||
*/
|
||||
public abstract R execute(ExecutionContext ctx) throws IOException;
|
||||
|
||||
|
||||
/**
|
||||
* Parses xcontent to a concrete input of the same type.
|
||||
*/
|
||||
public static interface Parser<R extends Input.Result, I extends Input<R>> {
|
||||
|
||||
/**
|
||||
* @return The type of the input
|
||||
*/
|
||||
String type();
|
||||
|
||||
/**
|
||||
* Parses the given xcontent and creates a concrete input
|
||||
*/
|
||||
I parse(XContentParser parser) throws IOException;
|
||||
|
||||
/**
|
||||
* Parses the given xContent and creates a concrete result
|
||||
*/
|
||||
R parseResult(XContentParser parser) throws IOException;
|
||||
}
|
||||
|
||||
public abstract static class Result implements ToXContent {
|
||||
|
||||
public static final ParseField PAYLOAD_FIELD = new ParseField("payload");
|
||||
|
||||
private final String type;
|
||||
private final Payload payload;
|
||||
|
||||
public Result(String type, Payload payload) {
|
||||
this.type = type;
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public String type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
public Payload payload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject()
|
||||
.field(PAYLOAD_FIELD.getPreferredName(), payload);
|
||||
return toXContentBody(builder, params).endObject();
|
||||
}
|
||||
|
||||
protected abstract XContentBuilder toXContentBody(XContentBuilder builder, Params params) throws IOException;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.AlertsException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class InputException extends AlertsException {
|
||||
|
||||
public InputException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public InputException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.input.search.SearchInput;
|
||||
import org.elasticsearch.alerts.input.simple.SimpleInput;
|
||||
import org.elasticsearch.common.inject.AbstractModule;
|
||||
import org.elasticsearch.common.inject.multibindings.MapBinder;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class InputModule extends AbstractModule {
|
||||
|
||||
private final Map<String, Class<? extends Input.Parser>> parsers = new HashMap<>();
|
||||
|
||||
public void registerInput(String type, Class<? extends Input.Parser> parserType) {
|
||||
parsers.put(type, parserType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
|
||||
MapBinder<String, Input.Parser> parsersBinder = MapBinder.newMapBinder(binder(), String.class, Input.Parser.class);
|
||||
bind(SearchInput.Parser.class).asEagerSingleton();
|
||||
parsersBinder.addBinding(SearchInput.TYPE).to(SearchInput.Parser.class);
|
||||
bind(SimpleInput.Parser.class).asEagerSingleton();
|
||||
parsersBinder.addBinding(SimpleInput.TYPE).to(SimpleInput.Parser.class);
|
||||
|
||||
for (Map.Entry<String, Class<? extends Input.Parser>> entry : parsers.entrySet()) {
|
||||
bind(entry.getValue()).asEagerSingleton();
|
||||
parsersBinder.addBinding(entry.getKey()).to(entry.getValue());
|
||||
}
|
||||
|
||||
bind(InputRegistry.class).asEagerSingleton();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class InputRegistry {
|
||||
|
||||
private final ImmutableMap<String, Input.Parser> parsers;
|
||||
|
||||
@Inject
|
||||
public InputRegistry(Map<String, Input.Parser> parsers) {
|
||||
this.parsers = ImmutableMap.copyOf(parsers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the contents of parser to create the correct Input
|
||||
*
|
||||
* @param parser The parser containing the input definition
|
||||
* @return A new input instance from the parser
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public Input parse(XContentParser parser) throws IOException {
|
||||
String type = null;
|
||||
XContentParser.Token token;
|
||||
Input input = null;
|
||||
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) {
|
||||
Input.Parser inputParser = parsers.get(type);
|
||||
if (inputParser == null) {
|
||||
throw new InputException("unknown input type [" + type + "]");
|
||||
}
|
||||
input = inputParser.parse(parser);
|
||||
}
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
public Input.Result parseResult(XContentParser parser) throws IOException {
|
||||
String type = null;
|
||||
XContentParser.Token token;
|
||||
Input.Result inputResult = null;
|
||||
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) {
|
||||
Input.Parser inputParser = parsers.get(type);
|
||||
if (inputParser == null) {
|
||||
throw new InputException("unknown input type [" + type + "]");
|
||||
}
|
||||
inputResult = inputParser.parseResult(parser);
|
||||
}
|
||||
}
|
||||
return inputResult;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* 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.search;
|
||||
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.SearchType;
|
||||
import org.elasticsearch.alerts.ExecutionContext;
|
||||
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.Variables;
|
||||
import org.elasticsearch.alerts.support.init.proxy.ClientProxy;
|
||||
import org.elasticsearch.alerts.support.init.proxy.ScriptServiceProxy;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.collect.MapBuilder;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.alerts.support.AlertsDateUtils.formatDate;
|
||||
|
||||
/**
|
||||
* This class just defines an input that runs a search
|
||||
*/
|
||||
public class SearchInput extends Input<SearchInput.Result> {
|
||||
|
||||
public static final String TYPE = "search";
|
||||
public static final SearchType DEFAULT_SEARCH_TYPE = SearchType.COUNT;
|
||||
|
||||
private final SearchRequest searchRequest;
|
||||
|
||||
private final ScriptServiceProxy scriptService;
|
||||
private final ClientProxy client;
|
||||
|
||||
public SearchInput(ESLogger logger, ScriptServiceProxy scriptService, ClientProxy client, SearchRequest searchRequest) {
|
||||
super(logger);
|
||||
this.searchRequest = searchRequest;
|
||||
this.scriptService = scriptService;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result execute(ExecutionContext ctx) throws IOException {
|
||||
|
||||
SearchRequest request = createSearchRequestWithTimes(this.searchRequest, ctx.scheduledTime(), ctx.fireTime(), scriptService);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("[{}] running query for [{}] [{}]", ctx.id(), ctx.alert().name(), XContentHelper.convertToJson(request.source(), false, true));
|
||||
}
|
||||
|
||||
// actionGet deals properly with InterruptedException
|
||||
SearchResponse response = client.search(request).actionGet();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("[{}] found [{}] hits", ctx.id(), ctx.alert().name(), response.getHits().getTotalHits());
|
||||
for (SearchHit hit : response.getHits()) {
|
||||
logger.debug("[{}] hit [{}]", ctx.id(), XContentHelper.toString(hit));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new Result(TYPE, new Payload.ActionResponse(response), request);
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new search request applying the scheduledFireTime and fireTime to the original request
|
||||
*/
|
||||
public static SearchRequest createSearchRequestWithTimes(SearchRequest requestPrototype, DateTime scheduledFireTime, DateTime fireTime, ScriptServiceProxy scriptService) throws IOException {
|
||||
SearchRequest request = new SearchRequest(requestPrototype)
|
||||
.indicesOptions(requestPrototype.indicesOptions())
|
||||
.searchType(requestPrototype.searchType())
|
||||
.indices(requestPrototype.indices());
|
||||
if (Strings.hasLength(requestPrototype.source())) {
|
||||
Map<String, String> templateParams = new HashMap<>();
|
||||
templateParams.put(Variables.SCHEDULED_FIRE_TIME, formatDate(scheduledFireTime));
|
||||
templateParams.put(Variables.FIRE_TIME, formatDate(fireTime));
|
||||
String requestSource = XContentHelper.convertToJson(requestPrototype.source(), false);
|
||||
ExecutableScript script = scriptService.executable("mustache", requestSource, ScriptService.ScriptType.INLINE, templateParams);
|
||||
request.source((BytesReference) script.unwrap(script.run()), false);
|
||||
} else if (requestPrototype.templateName() != null) {
|
||||
MapBuilder<String, String> templateParams = MapBuilder.newMapBuilder(requestPrototype.templateParams())
|
||||
.put(Variables.SCHEDULED_FIRE_TIME, formatDate(scheduledFireTime))
|
||||
.put(Variables.FIRE_TIME, formatDate(fireTime));
|
||||
request.templateParams(templateParams.map());
|
||||
request.templateName(requestPrototype.templateName());
|
||||
request.templateType(requestPrototype.templateType());
|
||||
} else {
|
||||
throw new InputException("search requests needs either source or template name");
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
public static class Result extends Input.Result {
|
||||
|
||||
private final SearchRequest request;
|
||||
|
||||
public Result(String type, Payload payload, SearchRequest request) {
|
||||
super(type, payload);
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
public SearchRequest request() {
|
||||
return request;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Parser extends AbstractComponent implements Input.Parser<Result,SearchInput> {
|
||||
|
||||
public static ParseField REQUEST_FIELD = new ParseField("request");
|
||||
|
||||
private final ScriptServiceProxy scriptService;
|
||||
private final ClientProxy client;
|
||||
|
||||
@Inject
|
||||
public Parser(Settings settings, ScriptServiceProxy scriptService, ClientProxy client) {
|
||||
super(settings);
|
||||
this.scriptService = scriptService;
|
||||
this.client = client;
|
||||
}
|
||||
@Override
|
||||
public String type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@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 + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (request == null) {
|
||||
throw new InputException("search request is missing or null for [" + TYPE + "] input");
|
||||
}
|
||||
|
||||
return new SearchInput(logger, scriptService, client, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result parseResult(XContentParser parser) throws IOException {
|
||||
Payload payload = null;
|
||||
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 (Input.Result.PAYLOAD_FIELD.match(currentFieldName)) {
|
||||
payload = new Payload.XContent(parser);
|
||||
} else if (REQUEST_FIELD.match(currentFieldName)) {
|
||||
request = AlertUtils.readSearchRequest(parser, DEFAULT_SEARCH_TYPE);
|
||||
} else {
|
||||
throw new InputException("unable to parse [" + TYPE + "] input result. unexpected field [" + currentFieldName + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (payload == null) {
|
||||
throw new InputException("unable to parse [" + TYPE + "] input result ["
|
||||
+ Input.Result.PAYLOAD_FIELD.getPreferredName() + "] is required");
|
||||
}
|
||||
|
||||
if (request == null) {
|
||||
throw new InputException("unable to parse [" + TYPE + "] input result, ["
|
||||
+ REQUEST_FIELD.getPreferredName() + "] is required");
|
||||
}
|
||||
|
||||
return new Result(TYPE, payload, request);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,12 +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.condition.simple;
|
||||
package org.elasticsearch.alerts.input.simple;
|
||||
|
||||
import org.elasticsearch.alerts.ExecutionContext;
|
||||
import org.elasticsearch.alerts.Payload;
|
||||
import org.elasticsearch.alerts.condition.Condition;
|
||||
import org.elasticsearch.alerts.condition.ConditionException;
|
||||
import org.elasticsearch.alerts.input.Input;
|
||||
import org.elasticsearch.alerts.input.InputException;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
|
@ -19,15 +19,14 @@ import org.elasticsearch.common.xcontent.XContentParser;
|
|||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A condition that is always met and returns a static/fixed payload
|
||||
* This class just defines a simple xcontent map as an input
|
||||
*/
|
||||
public class SimpleCondition extends Condition<SimpleCondition.Result> {
|
||||
public class SimpleInput extends Input<SimpleInput.Result> {
|
||||
|
||||
public static final String TYPE = "simple";
|
||||
|
||||
private final Payload payload;
|
||||
|
||||
public SimpleCondition(ESLogger logger, Payload payload) {
|
||||
public SimpleInput(ESLogger logger, Payload payload) {
|
||||
super(logger);
|
||||
this.payload = payload;
|
||||
}
|
||||
|
@ -39,18 +38,20 @@ public class SimpleCondition extends Condition<SimpleCondition.Result> {
|
|||
|
||||
@Override
|
||||
public Result execute(ExecutionContext ctx) throws IOException {
|
||||
return new Result(payload);
|
||||
return new Result(TYPE, payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
return payload.toXContent(builder, params);
|
||||
builder.startObject();
|
||||
builder.field(Input.Result.PAYLOAD_FIELD.getPreferredName(), payload);
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
public static class Result extends Condition.Result {
|
||||
public static class Result extends Input.Result {
|
||||
|
||||
public Result(Payload payload) {
|
||||
super(TYPE, true, payload);
|
||||
public Result(String type, Payload payload) {
|
||||
super(type, payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -59,7 +60,7 @@ public class SimpleCondition extends Condition<SimpleCondition.Result> {
|
|||
}
|
||||
}
|
||||
|
||||
public static class Parser extends AbstractComponent implements Condition.Parser<Result, SimpleCondition> {
|
||||
public static class Parser extends AbstractComponent implements Input.Parser<Result,SimpleInput> {
|
||||
|
||||
@Inject
|
||||
public Parser(Settings settings) {
|
||||
|
@ -72,45 +73,53 @@ public class SimpleCondition extends Condition<SimpleCondition.Result> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public SimpleCondition parse(XContentParser parser) throws IOException {
|
||||
return new SimpleCondition(logger, new Payload.XContent(parser));
|
||||
public SimpleInput parse(XContentParser parser) throws IOException {
|
||||
Payload payload = 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 (Input.Result.PAYLOAD_FIELD.match(currentFieldName)) {
|
||||
payload = new Payload.XContent(parser);
|
||||
} else {
|
||||
throw new InputException("unable to parse [" + TYPE + "] input. unexpected field [" + currentFieldName + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (payload == null) {
|
||||
throw new InputException("unable to parse [" + TYPE + "] input [payload] is a required field");
|
||||
}
|
||||
|
||||
return new SimpleInput(logger, payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result parseResult(XContentParser parser) throws IOException {
|
||||
Payload payload = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
Payload payload = null;
|
||||
boolean met = false;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token.isValue()) {
|
||||
if (token == XContentParser.Token.VALUE_BOOLEAN) {
|
||||
if (Condition.Result.MET_FIELD.match(currentFieldName)) {
|
||||
met = parser.booleanValue();
|
||||
} else {
|
||||
throw new ConditionException("unable to parse simple condition result. unexpected field [" + currentFieldName + "]");
|
||||
}
|
||||
} else {
|
||||
throw new ConditionException("unable to parse simple condition result. unexpected field [" + currentFieldName + "]");
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
if (Condition.Result.PAYLOAD_FIELD.match(currentFieldName)) {
|
||||
} else if (token == XContentParser.Token.START_OBJECT && currentFieldName != null) {
|
||||
if (Input.Result.PAYLOAD_FIELD.match(currentFieldName)) {
|
||||
payload = new Payload.XContent(parser);
|
||||
} else {
|
||||
throw new ConditionException("unable to parse simple condition result. unexpected field [" + currentFieldName + "]");
|
||||
throw new InputException("unable to parse [" + TYPE + "] input result. unexpected field [" + currentFieldName + "]");
|
||||
}
|
||||
} else {
|
||||
throw new ConditionException("unable to parse simple condition result. unexpected token [" + token + "]");
|
||||
}
|
||||
}
|
||||
|
||||
if (!met) {
|
||||
throw new ConditionException("unable to parse simple condition result. simple condition always matches, yet [met] field is either missing or set to [false]");
|
||||
if (payload == null) {
|
||||
throw new InputException("unable to parse [" + TYPE + "] input result [payload] is a required field");
|
||||
}
|
||||
|
||||
return new Result(payload);
|
||||
return new Result(TYPE, payload);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,6 +16,16 @@
|
|||
"enabled" : false,
|
||||
"dynamic" : true
|
||||
},
|
||||
"input": {
|
||||
"type": "object",
|
||||
"enabled" : false,
|
||||
"dynamic" : true
|
||||
},
|
||||
"condition": {
|
||||
"type": "object",
|
||||
"enabled" : false,
|
||||
"dynamic" : true
|
||||
},
|
||||
"status": {
|
||||
"type": "object",
|
||||
"enabled" : false,
|
||||
|
@ -24,17 +34,12 @@
|
|||
"throttle_period": {
|
||||
"type": "string"
|
||||
},
|
||||
"condition": {
|
||||
"type": "object",
|
||||
"enabled" : false,
|
||||
"dynamic" : true
|
||||
},
|
||||
"actions": {
|
||||
"transform": {
|
||||
"type" : "object",
|
||||
"enabled" : false,
|
||||
"dynamic" : true
|
||||
},
|
||||
"transform": {
|
||||
"actions": {
|
||||
"type" : "object",
|
||||
"enabled" : false,
|
||||
"dynamic" : true
|
||||
|
|
|
@ -18,10 +18,10 @@ 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.search.ScriptSearchCondition;
|
||||
import org.elasticsearch.alerts.condition.search.SearchCondition;
|
||||
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.init.proxy.ClientProxy;
|
||||
|
@ -117,32 +117,46 @@ public abstract class AbstractAlertingTests extends ElasticsearchIntegrationTest
|
|||
protected BytesReference createAlertSource(String cron, SearchRequest conditionRequest, String conditionScript, Map<String,Object> metadata) throws IOException {
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject();
|
||||
{
|
||||
builder.startObject("schedule")
|
||||
.field("cron", cron)
|
||||
.endObject();
|
||||
|
||||
builder.startObject("schedule")
|
||||
.field("cron", cron)
|
||||
.endObject();
|
||||
if (metadata != null) {
|
||||
builder.field("meta", metadata);
|
||||
}
|
||||
|
||||
if (metadata != null) {
|
||||
builder.field("meta", metadata);
|
||||
builder.startObject("input");
|
||||
{
|
||||
builder.startObject("search");
|
||||
builder.field("request");
|
||||
AlertUtils.writeSearchRequest(conditionRequest, builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.endObject();
|
||||
}
|
||||
builder.endObject();
|
||||
|
||||
builder.startObject("condition");
|
||||
{
|
||||
builder.startObject("script");
|
||||
builder.field("script", conditionScript);
|
||||
builder.endObject();
|
||||
}
|
||||
builder.endObject();
|
||||
|
||||
|
||||
builder.startArray("actions");
|
||||
{
|
||||
builder.startObject();
|
||||
{
|
||||
builder.startObject("index");
|
||||
builder.field("index", "my-index");
|
||||
builder.field("type", "trail");
|
||||
builder.endObject();
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
builder.endArray();
|
||||
}
|
||||
|
||||
builder.startObject("condition");
|
||||
builder.startObject("script");
|
||||
builder.field("request");
|
||||
AlertUtils.writeSearchRequest(conditionRequest, builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.field("script", conditionScript);
|
||||
builder.endObject();
|
||||
builder.endObject();
|
||||
|
||||
builder.startArray("actions");
|
||||
builder.startObject();
|
||||
builder.startObject("index");
|
||||
builder.field("index", "my-index");
|
||||
builder.field("type", "trail");
|
||||
builder.endObject();
|
||||
builder.endObject();
|
||||
builder.endArray();
|
||||
|
||||
builder.endObject();
|
||||
|
||||
return builder.bytes();
|
||||
|
@ -151,7 +165,7 @@ public abstract class AbstractAlertingTests extends ElasticsearchIntegrationTest
|
|||
public static SearchRequest createConditionSearchRequest(String... indices) {
|
||||
SearchRequest request = new SearchRequest(indices);
|
||||
request.indicesOptions(AlertUtils.DEFAULT_INDICES_OPTIONS);
|
||||
request.searchType(SearchCondition.DEFAULT_SEARCH_TYPE);
|
||||
request.searchType(SearchInput.DEFAULT_SEARCH_TYPE);
|
||||
return request;
|
||||
}
|
||||
|
||||
|
@ -159,7 +173,7 @@ public abstract class AbstractAlertingTests extends ElasticsearchIntegrationTest
|
|||
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(SearchCondition.DEFAULT_SEARCH_TYPE);
|
||||
conditionRequest.searchType(SearchInput.DEFAULT_SEARCH_TYPE);
|
||||
|
||||
List<Action> actions = new ArrayList<>();
|
||||
|
||||
|
@ -190,13 +204,10 @@ public abstract class AbstractAlertingTests extends ElasticsearchIntegrationTest
|
|||
return new Alert(
|
||||
alertName,
|
||||
new CronSchedule("0/5 * * * * ? *"),
|
||||
new ScriptSearchCondition(logger, scriptService(), ClientProxy.of(client()),
|
||||
conditionRequest,"return true", ScriptService.ScriptType.INLINE, "groovy"),
|
||||
new SearchTransform(logger, scriptService(), ClientProxy.of(client()), transformRequest),
|
||||
new TimeValue(0),
|
||||
new Actions(actions),
|
||||
metadata,
|
||||
new Alert.Status()
|
||||
new SearchInput(logger, scriptService(), ClientProxy.of(client()),
|
||||
conditionRequest),
|
||||
new ScriptCondition(logger, scriptService(), "return true", ScriptService.ScriptType.INLINE, "groovy"),
|
||||
new SearchTransform(logger, scriptService(), ClientProxy.of(client()), transformRequest), new Actions(actions), metadata, new Alert.Status(), new TimeValue(0)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -266,7 +277,7 @@ public abstract class AbstractAlertingTests extends ElasticsearchIntegrationTest
|
|||
.get();
|
||||
assertThat(searchResponse.getHits().getTotalHits(), greaterThanOrEqualTo(minimumExpectedAlertActionsWithActionPerformed));
|
||||
if (assertConditionMet) {
|
||||
assertThat((Integer) XContentMapValues.extractValue("alert_execution.condition_result.script.payload.hits.total", searchResponse.getHits().getAt(0).sourceAsMap()), greaterThanOrEqualTo(1));
|
||||
assertThat((Integer) XContentMapValues.extractValue("alert_execution.input_result.search.payload.hits.total", searchResponse.getHits().getAt(0).sourceAsMap()), greaterThanOrEqualTo(1));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -15,9 +15,10 @@ import org.elasticsearch.alerts.actions.Action;
|
|||
import org.elasticsearch.alerts.actions.Actions;
|
||||
import org.elasticsearch.alerts.actions.index.IndexAction;
|
||||
import org.elasticsearch.alerts.client.AlertsClient;
|
||||
import org.elasticsearch.alerts.condition.search.ScriptSearchCondition;
|
||||
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.init.proxy.ClientProxy;
|
||||
import org.elasticsearch.alerts.transform.SearchTransform;
|
||||
|
@ -64,15 +65,12 @@ public class AlertThrottleTests extends AbstractAlertingTests {
|
|||
actions.add(new IndexAction(logger, ClientProxy.of(client()), "action-index", "action-type"));
|
||||
|
||||
Alert alert = new Alert(
|
||||
"test-serialization",
|
||||
"test-ack-throttle",
|
||||
new CronSchedule("0/5 * * * * ? *"),
|
||||
new ScriptSearchCondition(logger, scriptService(), ClientProxy.of(client()),
|
||||
request, "hits.total > 0", ScriptService.ScriptType.INLINE, "groovy"),
|
||||
new SearchTransform(logger, scriptService(), ClientProxy.of(client()), request),
|
||||
new TimeValue(0),
|
||||
new Actions(actions),
|
||||
null,
|
||||
new Alert.Status()
|
||||
new SearchInput(logger, scriptService(), ClientProxy.of(client()),
|
||||
request),
|
||||
new ScriptCondition(logger, scriptService(), "hits.total > 0", ScriptService.ScriptType.INLINE, "groovy"),
|
||||
new SearchTransform(logger, scriptService(), ClientProxy.of(client()), request), new Actions(actions), null, new Alert.Status(), new TimeValue(0)
|
||||
);
|
||||
|
||||
|
||||
|
@ -149,13 +147,10 @@ public class AlertThrottleTests extends AbstractAlertingTests {
|
|||
Alert alert = new Alert(
|
||||
"test-time-throttle",
|
||||
new CronSchedule("0/5 * * * * ? *"),
|
||||
new ScriptSearchCondition(logger, scriptService(), ClientProxy.of(client()),
|
||||
request, "hits.total > 0", ScriptService.ScriptType.INLINE, "groovy"),
|
||||
new SearchInput(logger, scriptService(), ClientProxy.of(client()), request),
|
||||
new ScriptCondition(logger, scriptService(), "hits.total > 0", ScriptService.ScriptType.INLINE, "groovy"),
|
||||
new SearchTransform(logger, scriptService(), ClientProxy.of(client()), request),
|
||||
new TimeValue(10, TimeUnit.SECONDS),
|
||||
new Actions(actions),
|
||||
null,
|
||||
new Alert.Status()
|
||||
new Actions(actions), null, new Alert.Status(), new TimeValue(10, TimeUnit.SECONDS)
|
||||
);
|
||||
|
||||
|
||||
|
@ -198,7 +193,6 @@ public class AlertThrottleTests extends AbstractAlertingTests {
|
|||
assertThat(countResponse.getCount(), lessThanOrEqualTo(4L));
|
||||
|
||||
|
||||
|
||||
CountResponse countOfThrottledActions = client()
|
||||
.prepareCount(HistoryStore.ALERT_HISTORY_INDEX_PREFIX + "*")
|
||||
.setQuery(QueryBuilders.matchQuery(FiredAlert.Parser.STATE_FIELD.getPreferredName(), FiredAlert.State.THROTTLED.id()))
|
||||
|
|
|
@ -11,9 +11,10 @@ import org.elasticsearch.action.index.IndexResponse;
|
|||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.alerts.actions.Action;
|
||||
import org.elasticsearch.alerts.actions.Actions;
|
||||
import org.elasticsearch.alerts.condition.search.ScriptSearchCondition;
|
||||
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.init.proxy.ClientProxy;
|
||||
import org.elasticsearch.alerts.transform.SearchTransform;
|
||||
|
@ -25,7 +26,6 @@ import org.elasticsearch.common.joda.time.DateTimeZone;
|
|||
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.script.ScriptService;
|
||||
import org.elasticsearch.test.junit.annotations.TestLogging;
|
||||
|
@ -78,14 +78,12 @@ public class BootStrapTest extends AbstractAlertingTests {
|
|||
SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(termQuery("field", "value")));
|
||||
Alert alert = new Alert(
|
||||
"test-serialization",
|
||||
new CronSchedule("0/5 * * * * ? 2035"),
|
||||
new ScriptSearchCondition(logger, scriptService(), ClientProxy.of(client()),
|
||||
searchRequest, "return true", ScriptService.ScriptType.INLINE, "groovy"),
|
||||
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(), "return true", ScriptService.ScriptType.INLINE, "groovy"),
|
||||
new SearchTransform(logger, scriptService(), ClientProxy.of(client()), searchRequest),
|
||||
new TimeValue(0),
|
||||
new Actions(new ArrayList<Action>()),
|
||||
null,
|
||||
new Alert.Status()
|
||||
new Actions(new ArrayList<Action>()), null, new Alert.Status(), new TimeValue(0)
|
||||
|
||||
);
|
||||
|
||||
XContentBuilder builder = jsonBuilder().value(alert);
|
||||
|
@ -139,13 +137,14 @@ public class BootStrapTest extends AbstractAlertingTests {
|
|||
Alert alert = new Alert(
|
||||
"action-test-"+ i + " " + j,
|
||||
new CronSchedule("0/5 * * * * ? 2035"), //Set a cron schedule far into the future so this alert is never scheduled
|
||||
new ScriptSearchCondition(logger, scriptService(), ClientProxy.of(client()),
|
||||
searchRequest, "return true", ScriptService.ScriptType.INLINE, "groovy"),
|
||||
new SearchInput(logger, scriptService(), ClientProxy.of(client()),
|
||||
searchRequest),
|
||||
new ScriptCondition(logger, scriptService(), "return true", ScriptService.ScriptType.INLINE, "groovy"),
|
||||
new SearchTransform(logger, scriptService(), ClientProxy.of(client()), searchRequest),
|
||||
new TimeValue(0),
|
||||
new Actions(new ArrayList<Action>()),
|
||||
null,
|
||||
new Alert.Status()
|
||||
new Alert.Status(),
|
||||
new TimeValue(0)
|
||||
);
|
||||
XContentBuilder jsonBuilder = jsonBuilder();
|
||||
alert.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS);
|
||||
|
@ -154,9 +153,13 @@ public class BootStrapTest extends AbstractAlertingTests {
|
|||
assertTrue(putAlertResponse.indexResponse().isCreated());
|
||||
|
||||
FiredAlert firedAlert = new FiredAlert(alert, historyIndexDate, historyIndexDate);
|
||||
|
||||
XContentBuilder jsonBuilder2 = jsonBuilder();
|
||||
firedAlert.toXContent(jsonBuilder2, ToXContent.EMPTY_PARAMS);
|
||||
|
||||
IndexResponse indexResponse = client().prepareIndex(actionHistoryIndex, HistoryStore.ALERT_HISTORY_TYPE, firedAlert.id())
|
||||
.setConsistencyLevel(WriteConsistencyLevel.ALL)
|
||||
.setSource(XContentFactory.jsonBuilder().value(firedAlert))
|
||||
.setSource(jsonBuilder2.bytes())
|
||||
.get();
|
||||
assertTrue(indexResponse.isCreated());
|
||||
}
|
||||
|
|
|
@ -10,7 +10,8 @@ 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.search.ScriptSearchCondition;
|
||||
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.init.proxy.ClientProxy;
|
||||
import org.elasticsearch.alerts.transform.SearchTransform;
|
||||
|
@ -58,13 +59,11 @@ public class TransformSearchTest extends AbstractAlertingTests {
|
|||
Alert alert = new Alert(
|
||||
"test-serialization",
|
||||
new CronSchedule("0/5 * * * * ? *"),
|
||||
new ScriptSearchCondition(logger, scriptService(), ClientProxy.of(client()),
|
||||
conditionRequest,"return true", ScriptService.ScriptType.INLINE, "groovy"),
|
||||
new SearchInput(logger, scriptService(), ClientProxy.of(client()),
|
||||
conditionRequest),
|
||||
new ScriptCondition(logger, scriptService(), "return true", ScriptService.ScriptType.INLINE, "groovy"),
|
||||
new SearchTransform(logger, scriptService(), ClientProxy.of(client()), transformRequest),
|
||||
new TimeValue(0),
|
||||
new Actions(actions),
|
||||
metadata,
|
||||
new Alert.Status()
|
||||
new Actions(actions), metadata, new Alert.Status(), new TimeValue(0)
|
||||
);
|
||||
|
||||
XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
|
||||
|
|
|
@ -10,7 +10,9 @@ 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.search.ScriptSearchCondition;
|
||||
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.init.proxy.ClientProxy;
|
||||
import org.elasticsearch.alerts.transform.SearchTransform;
|
||||
|
@ -56,19 +58,21 @@ public class ActionsTest extends AbstractAlertingTests {
|
|||
final List<Action> actionList = new ArrayList<>();
|
||||
actionList.add(alertAction);
|
||||
|
||||
Condition alertCondition = new ScriptSearchCondition(logger, scriptService(),
|
||||
ClientProxy.of(client()), createConditionSearchRequest(), "return true", ScriptService.ScriptType.INLINE, "groovy");
|
||||
Input alertInput = new SearchInput(logger, scriptService(), ClientProxy.of(client()),
|
||||
createConditionSearchRequest());
|
||||
Condition alertCondition = new ScriptCondition(logger, scriptService(), "return true", ScriptService.ScriptType.INLINE, "groovy");
|
||||
|
||||
|
||||
|
||||
Alert alert = new Alert(
|
||||
"my-first-alert",
|
||||
new CronSchedule("0/5 * * * * ? *"),
|
||||
alertInput,
|
||||
alertCondition,
|
||||
new SearchTransform(logger, scriptService(), ClientProxy.of(client()), createConditionSearchRequest()),
|
||||
new TimeValue(0),
|
||||
new Actions(actionList),
|
||||
null,
|
||||
new Alert.Status());
|
||||
new Actions(actionList), null, new Alert.Status(), new TimeValue(0)
|
||||
);
|
||||
|
||||
|
||||
XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
|
||||
alert.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS);
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* 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.script;
|
||||
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.ShardSearchFailure;
|
||||
import org.elasticsearch.alerts.Payload;
|
||||
import org.elasticsearch.alerts.condition.ConditionException;
|
||||
import org.elasticsearch.alerts.support.init.proxy.ScriptServiceProxy;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.script.ScriptEngineService;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.script.groovy.GroovyScriptEngineService;
|
||||
import org.elasticsearch.search.internal.InternalSearchResponse;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class ScriptConditionTests extends ElasticsearchTestCase {
|
||||
|
||||
ThreadPool tp = null;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
tp = new ThreadPool(ThreadPool.Names.SAME);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() {
|
||||
tp.shutdownNow();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testExecute() throws Exception {
|
||||
ScriptServiceProxy scriptService = getScriptServiceProxy(tp);
|
||||
ScriptCondition condition = new ScriptCondition(logger, scriptService, "hits.total > 1", ScriptService.ScriptType.INLINE, "groovy");
|
||||
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 500l, new ShardSearchFailure[0]);
|
||||
assertFalse(condition.processPayload(new Payload.ActionResponse(response)).met());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParser_Valid() throws Exception {
|
||||
ScriptCondition.Parser conditionParser = new ScriptCondition.Parser(ImmutableSettings.settingsBuilder().build(), getScriptServiceProxy(tp));
|
||||
|
||||
XContentBuilder builder = createConditionContent("hits.total > 1", null, null);
|
||||
XContentParser parser = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
|
||||
ScriptCondition condition = conditionParser.parse(parser);
|
||||
|
||||
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 500l, new ShardSearchFailure[0]);
|
||||
|
||||
assertFalse(condition.processPayload(new Payload.ActionResponse(response)).met());
|
||||
|
||||
|
||||
builder = createConditionContent("return true", null, null);
|
||||
parser = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
|
||||
condition = conditionParser.parse(parser);
|
||||
|
||||
assertTrue(condition.processPayload(new Payload.ActionResponse(response)).met());
|
||||
}
|
||||
|
||||
@Test(expected = ConditionException.class)
|
||||
public void testParser_InValid() throws Exception {
|
||||
ScriptCondition.Parser conditionParser = new ScriptCondition.Parser(ImmutableSettings.settingsBuilder().build(), getScriptServiceProxy(tp));
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject().endObject();
|
||||
XContentParser parser = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
|
||||
try {
|
||||
conditionParser.parse(parser);
|
||||
} catch (Throwable t) {
|
||||
throw t;
|
||||
}
|
||||
fail("expected a condition exception trying to parse an invalid condition XContent");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testScriptResultParser_Valid() throws Exception {
|
||||
ScriptCondition.Parser conditionParser = new ScriptCondition.Parser(ImmutableSettings.settingsBuilder().build(), getScriptServiceProxy(tp));
|
||||
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.field("met", true );
|
||||
builder.endObject();
|
||||
|
||||
ScriptCondition.Result scriptResult = conditionParser.parseResult(XContentFactory.xContent(builder.bytes()).createParser(builder.bytes()));
|
||||
assertTrue(scriptResult.met());
|
||||
|
||||
builder = jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.field("met", false );
|
||||
builder.endObject();
|
||||
|
||||
scriptResult = conditionParser.parseResult(XContentFactory.xContent(builder.bytes()).createParser(builder.bytes()));
|
||||
assertFalse(scriptResult.met());
|
||||
}
|
||||
|
||||
@Test(expected = ConditionException.class)
|
||||
public void testScriptResultParser_Invalid() throws Exception {
|
||||
ScriptCondition.Parser conditionParser = new ScriptCondition.Parser(ImmutableSettings.settingsBuilder().build(), getScriptServiceProxy(tp));
|
||||
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject().endObject();
|
||||
|
||||
try {
|
||||
conditionParser.parseResult(XContentFactory.xContent(builder.bytes()).createParser(builder.bytes()));
|
||||
} catch (Throwable t) {
|
||||
throw t;
|
||||
}
|
||||
fail("expected a condition exception trying to parse an invalid condition XContent");
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static ScriptServiceProxy getScriptServiceProxy(ThreadPool tp) {
|
||||
Settings settings = ImmutableSettings.settingsBuilder().build();
|
||||
GroovyScriptEngineService groovyScriptEngineService = new GroovyScriptEngineService(settings);
|
||||
Set<ScriptEngineService> engineServiceSet = new HashSet<>();
|
||||
engineServiceSet.add(groovyScriptEngineService);
|
||||
return ScriptServiceProxy.of(new ScriptService(settings, new Environment(), engineServiceSet, new ResourceWatcherService(settings, tp)));
|
||||
}
|
||||
|
||||
private static XContentBuilder createConditionContent(String script, String scriptLang, ScriptService.ScriptType scriptType) throws IOException {
|
||||
XContentBuilder jsonBuilder = jsonBuilder();
|
||||
jsonBuilder.startObject();
|
||||
jsonBuilder.field("script");
|
||||
jsonBuilder.startObject();
|
||||
jsonBuilder.field("script", script);
|
||||
if (scriptLang != null) {
|
||||
jsonBuilder.field("script_lang", scriptLang);
|
||||
}
|
||||
if (scriptType != null) {
|
||||
jsonBuilder.field("script_type", scriptType.toString());
|
||||
}
|
||||
jsonBuilder.endObject();
|
||||
jsonBuilder.endObject();
|
||||
return jsonBuilder;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,96 +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.condition.search;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.ShardSearchFailure;
|
||||
import org.elasticsearch.alerts.AbstractAlertingTests;
|
||||
import org.elasticsearch.alerts.support.AlertUtils;
|
||||
import org.elasticsearch.alerts.support.init.proxy.ScriptServiceProxy;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.script.ScriptEngineService;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.script.groovy.GroovyScriptEngineService;
|
||||
import org.elasticsearch.search.internal.InternalSearchResponse;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
|
||||
|
||||
public class SearchConditionTests extends ElasticsearchTestCase {
|
||||
|
||||
private XContentBuilder createConditionContent(String script, String scriptLang, ScriptService.ScriptType scriptType, SearchRequest request) throws IOException {
|
||||
XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
|
||||
jsonBuilder.startObject();
|
||||
jsonBuilder.field("script");
|
||||
jsonBuilder.startObject();
|
||||
jsonBuilder.field("script", script);
|
||||
if (scriptLang != null) {
|
||||
jsonBuilder.field("script_lang", scriptLang);
|
||||
}
|
||||
if (scriptType != null) {
|
||||
jsonBuilder.field("script_type", scriptType.toString());
|
||||
}
|
||||
jsonBuilder.field(ScriptSearchCondition.Parser.REQUEST_FIELD.getPreferredName());
|
||||
AlertUtils.writeSearchRequest(request, jsonBuilder, ToXContent.EMPTY_PARAMS);
|
||||
jsonBuilder.endObject();
|
||||
jsonBuilder.endObject();
|
||||
return jsonBuilder;
|
||||
}
|
||||
|
||||
public void testInlineScriptConditions() throws Exception {
|
||||
|
||||
Settings settings = ImmutableSettings.settingsBuilder().build();
|
||||
GroovyScriptEngineService groovyScriptEngineService = new GroovyScriptEngineService(settings);
|
||||
ThreadPool tp = new ThreadPool(ThreadPool.Names.SAME);
|
||||
Set<ScriptEngineService> engineServiceSet = new HashSet<>();
|
||||
engineServiceSet.add(groovyScriptEngineService);
|
||||
|
||||
ScriptService scriptService = new ScriptService(settings, new Environment(), engineServiceSet, new ResourceWatcherService(settings, tp));
|
||||
ScriptSearchCondition.Parser conditionParser = new ScriptSearchCondition.Parser(settings, null, ScriptServiceProxy.of(scriptService));
|
||||
|
||||
try {
|
||||
XContentBuilder builder = createConditionContent("hits.total > 1", null, null, AbstractAlertingTests.createConditionSearchRequest());
|
||||
XContentParser parser = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
|
||||
ScriptSearchCondition condition = conditionParser.parse(parser);
|
||||
|
||||
SearchRequest request = new SearchRequest();
|
||||
request.indices("my-index");
|
||||
request.types("my-type");
|
||||
|
||||
|
||||
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 500l, new ShardSearchFailure[0]);
|
||||
XContentBuilder responseBuilder = jsonBuilder().startObject().value(response).endObject();
|
||||
assertFalse(condition.processSearchResponse(response).met());
|
||||
|
||||
|
||||
builder = createConditionContent("return true", null, null, AbstractAlertingTests.createConditionSearchRequest());
|
||||
parser = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
|
||||
condition = conditionParser.parse(parser);
|
||||
|
||||
assertTrue(condition.processSearchResponse(response).met());
|
||||
|
||||
tp.shutdownNow();
|
||||
} catch (IOException ioe) {
|
||||
throw new ElasticsearchException("Failed to construct the condition", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* 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.simple;
|
||||
|
||||
import org.elasticsearch.alerts.condition.Condition;
|
||||
import org.elasticsearch.alerts.condition.ConditionException;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class TestAlwaysFalseCondition extends ElasticsearchTestCase {
|
||||
|
||||
|
||||
@Test
|
||||
public void testExecute() throws Exception {
|
||||
Condition alwaysTrue = new AlwaysTrueCondition(logger);
|
||||
assertTrue(alwaysTrue.execute(null).met());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParser_Valid() throws Exception {
|
||||
Condition.Parser p = new AlwaysTrueCondition.Parser(ImmutableSettings.settingsBuilder().build());
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.endObject();
|
||||
XContentParser xp = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
|
||||
xp.nextToken();
|
||||
|
||||
Condition alwaysTrue = p.parse(xp);
|
||||
assertTrue(alwaysTrue.execute(null).met());
|
||||
}
|
||||
|
||||
@Test(expected = ConditionException.class)
|
||||
public void testParser_Invalid() throws Exception {
|
||||
Condition.Parser p = new AlwaysTrueCondition.Parser(ImmutableSettings.settingsBuilder().build());
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.field("foo", "bar");
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testResultParser_Valid() throws Exception {
|
||||
Condition.Parser p = new AlwaysTrueCondition.Parser(ImmutableSettings.settingsBuilder().build());
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.endObject();
|
||||
XContentParser xp = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
|
||||
xp.nextToken();
|
||||
|
||||
Condition.Result alwaysTrueResult = p.parseResult(xp);
|
||||
assertTrue(alwaysTrueResult.met());
|
||||
}
|
||||
|
||||
@Test(expected = ConditionException.class)
|
||||
public void testResultParser_Invalid() throws Exception {
|
||||
Condition.Parser p = new AlwaysTrueCondition.Parser(ImmutableSettings.settingsBuilder().build());
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.field("met", false );
|
||||
builder.endObject();
|
||||
XContentParser xp = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
|
||||
xp.nextToken();
|
||||
|
||||
p.parseResult(xp);
|
||||
fail("expected a condition exception trying to parse an invalid condition result XContent, ["
|
||||
+ AlwaysTrueCondition.TYPE + "] condition result should not parse with a [met] field");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* 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.simple;
|
||||
|
||||
import org.elasticsearch.alerts.condition.Condition;
|
||||
import org.elasticsearch.alerts.condition.ConditionException;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class TestAlwaysTrueCondition extends ElasticsearchTestCase {
|
||||
|
||||
@Test
|
||||
public void testExecute() throws Exception {
|
||||
Condition alwaysTrue = new AlwaysTrueCondition(logger);
|
||||
assertTrue(alwaysTrue.execute(null).met());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParser_Valid() throws Exception {
|
||||
Condition.Parser p = new AlwaysTrueCondition.Parser(ImmutableSettings.settingsBuilder().build());
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.endObject();
|
||||
XContentParser xp = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
|
||||
xp.nextToken();
|
||||
Condition alwaysTrue = p.parse(xp);
|
||||
assertTrue(alwaysTrue.execute(null).met());
|
||||
}
|
||||
|
||||
@Test(expected = ConditionException.class)
|
||||
public void testParser_Invalid() throws Exception {
|
||||
Condition.Parser p = new AlwaysTrueCondition.Parser(ImmutableSettings.settingsBuilder().build());
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.field("foo", "bar");
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testResultParser_Valid() throws Exception {
|
||||
Condition.Parser p = new AlwaysTrueCondition.Parser(ImmutableSettings.settingsBuilder().build());
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.endObject();
|
||||
XContentParser xp = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
|
||||
xp.nextToken();
|
||||
|
||||
Condition.Result alwaysTrueResult = p.parseResult(xp);
|
||||
assertTrue(alwaysTrueResult.met());
|
||||
}
|
||||
|
||||
@Test(expected = ConditionException.class)
|
||||
public void testResultParser_Invalid() throws Exception {
|
||||
Condition.Parser p = new AlwaysTrueCondition.Parser(ImmutableSettings.settingsBuilder().build());
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.field("met", false );
|
||||
builder.endObject();
|
||||
XContentParser xp = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
|
||||
xp.nextToken();
|
||||
|
||||
p.parseResult(xp);
|
||||
fail("expected a condition exception trying to parse an invalid condition result XContent, ["
|
||||
+ AlwaysTrueCondition.TYPE + "] condition result should not parse with a [met] field");
|
||||
}
|
||||
|
||||
}
|
|
@ -8,11 +8,12 @@ package org.elasticsearch.alerts.history;
|
|||
import org.elasticsearch.alerts.*;
|
||||
import org.elasticsearch.alerts.actions.email.EmailAction;
|
||||
import org.elasticsearch.alerts.actions.webhook.WebhookAction;
|
||||
import org.elasticsearch.alerts.throttle.Throttler;
|
||||
import org.elasticsearch.alerts.condition.Condition;
|
||||
import org.elasticsearch.alerts.condition.search.ScriptSearchCondition;
|
||||
import org.elasticsearch.alerts.condition.search.SearchCondition;
|
||||
import org.elasticsearch.alerts.condition.simple.SimpleCondition;
|
||||
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.throttle.Throttler;
|
||||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
@ -47,8 +48,10 @@ public class FiredAlertTest extends AbstractAlertingTests {
|
|||
ExecutionContext ctx = new ExecutionContext(firedAlert.id(), alert, new DateTime(), new DateTime());
|
||||
ctx.onActionResult(new EmailAction.Result.Failure("failed to send because blah"));
|
||||
ctx.onActionResult(new WebhookAction.Result.Executed(300, "http://localhost:8000/alertfoo", "{'awesome' : 'us'}"));
|
||||
Condition.Result conditionResult = new SimpleCondition.Result(new Payload.Simple());
|
||||
Input.Result inputResult = new SimpleInput.Result(SimpleInput.TYPE, new Payload.Simple());
|
||||
Condition.Result conditionResult = AlwaysTrueCondition.RESULT;
|
||||
ctx.onThrottleResult(Throttler.NO_THROTTLE.throttle(ctx));
|
||||
ctx.onInputResult(inputResult);
|
||||
ctx.onConditionResult(conditionResult);
|
||||
firedAlert.update(new AlertExecution(ctx));
|
||||
|
||||
|
@ -67,8 +70,10 @@ public class FiredAlertTest extends AbstractAlertingTests {
|
|||
ExecutionContext ctx = new ExecutionContext(firedAlert.id(), alert, new DateTime(), new DateTime());
|
||||
ctx.onActionResult(new EmailAction.Result.Failure("failed to send because blah"));
|
||||
ctx.onActionResult(new WebhookAction.Result.Executed(300, "http://localhost:8000/alertfoo", "{'awesome' : 'us'}"));
|
||||
Condition.Result conditionResult = new SearchCondition.Result(ScriptSearchCondition.TYPE, true, createConditionSearchRequest(), new Payload.Simple());
|
||||
Input.Result inputResult = new SimpleInput.Result(SimpleInput.TYPE, new Payload.Simple());
|
||||
Condition.Result conditionResult = AlwaysFalseCondition.RESULT;
|
||||
ctx.onThrottleResult(Throttler.NO_THROTTLE.throttle(ctx));
|
||||
ctx.onInputResult(inputResult);
|
||||
ctx.onConditionResult(conditionResult);
|
||||
firedAlert.update(new AlertExecution(ctx));
|
||||
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* 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.search;
|
||||
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.alerts.ExecutionContext;
|
||||
import org.elasticsearch.alerts.input.Input;
|
||||
import org.elasticsearch.alerts.input.InputException;
|
||||
import org.elasticsearch.alerts.support.AlertUtils;
|
||||
import org.elasticsearch.alerts.support.Variables;
|
||||
import org.elasticsearch.alerts.support.init.proxy.ClientProxy;
|
||||
import org.elasticsearch.alerts.support.init.proxy.ScriptServiceProxy;
|
||||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
import org.elasticsearch.common.joda.time.DateTimeZone;
|
||||
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.support.XContentMapValues;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.test.ElasticsearchIntegrationTest;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.index.query.FilterBuilders.rangeFilter;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.filteredQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
|
||||
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
public class SearchInputTests extends ElasticsearchIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void testExecute() 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()
|
||||
.setSearchType(SearchInput.DEFAULT_SEARCH_TYPE)
|
||||
.request()
|
||||
.source(searchSourceBuilder);
|
||||
|
||||
SearchInput searchInput = new SearchInput(logger,
|
||||
ScriptServiceProxy.of(internalCluster().getInstance(ScriptService.class)),
|
||||
ClientProxy.of(client()), request);
|
||||
ExecutionContext ctx = new ExecutionContext("test-alert", null,
|
||||
new DateTime(0, DateTimeZone.UTC), new DateTime(0, DateTimeZone.UTC));
|
||||
SearchInput.Result result = searchInput.execute(ctx);
|
||||
|
||||
assertThat((Integer) XContentMapValues.extractValue("hits.total", result.payload().data()), equalTo(0));
|
||||
assertNotNull(result.request());
|
||||
assertEquals(result.request().searchType(),request.searchType());
|
||||
assertArrayEquals(result.request().indices(), request.indices());
|
||||
assertEquals(result.request().indicesOptions(), request.indicesOptions());
|
||||
}
|
||||
|
||||
@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()
|
||||
.setSearchType(SearchInput.DEFAULT_SEARCH_TYPE)
|
||||
.request()
|
||||
.source(searchSourceBuilder);
|
||||
|
||||
XContentBuilder jsonBuilder = jsonBuilder();
|
||||
|
||||
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(),
|
||||
ScriptServiceProxy.of(internalCluster().getInstance(ScriptService.class)),
|
||||
ClientProxy.of(client()));
|
||||
|
||||
Input searchInput = searchInputParser.parse(XContentFactory.xContent(jsonBuilder.bytes()).createParser(jsonBuilder.bytes()));
|
||||
assertEquals(SearchInput.TYPE, searchInput.type());
|
||||
}
|
||||
|
||||
@Test(expected = InputException.class)
|
||||
public void testParser_Invalid() throws Exception {
|
||||
Input.Parser searchInputParser = new SearchInput.Parser(ImmutableSettings.settingsBuilder().build(),
|
||||
ScriptServiceProxy.of(internalCluster().getInstance(ScriptService.class)),
|
||||
ClientProxy.of(client()));
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("foo", "bar");
|
||||
data.put("baz", new ArrayList<String>() );
|
||||
|
||||
XContentBuilder jsonBuilder = jsonBuilder();
|
||||
jsonBuilder.startObject();
|
||||
jsonBuilder.field(Input.Result.PAYLOAD_FIELD.getPreferredName(), data);
|
||||
jsonBuilder.endObject();
|
||||
|
||||
searchInputParser.parseResult(XContentFactory.xContent(jsonBuilder.bytes()).createParser(jsonBuilder.bytes()));
|
||||
fail("result parsing should fail if payload is provided but request is missing");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResultParser() throws Exception {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("foo", "bar");
|
||||
data.put("baz", new ArrayList<String>() );
|
||||
|
||||
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()
|
||||
.setSearchType(SearchInput.DEFAULT_SEARCH_TYPE)
|
||||
.request()
|
||||
.source(searchSourceBuilder);
|
||||
|
||||
XContentBuilder jsonBuilder = jsonBuilder();
|
||||
jsonBuilder.startObject();
|
||||
jsonBuilder.field(Input.Result.PAYLOAD_FIELD.getPreferredName(), data);
|
||||
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(),
|
||||
ScriptServiceProxy.of(internalCluster().getInstance(ScriptService.class)),
|
||||
ClientProxy.of(client()));
|
||||
|
||||
Input.Result result = searchInputParser.parseResult(XContentFactory.xContent(jsonBuilder.bytes()).createParser(jsonBuilder.bytes()));
|
||||
|
||||
assertEquals(SearchInput.TYPE, result.type());
|
||||
assertEquals(result.payload().data().get("foo"), "bar");
|
||||
List baz = (List)result.payload().data().get("baz");
|
||||
assertTrue(baz.isEmpty());
|
||||
|
||||
assertTrue(result instanceof SearchInput.Result);
|
||||
SearchInput.Result searchInputResult = (SearchInput.Result) result;
|
||||
assertNotNull(searchInputResult.request());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* 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.simple;
|
||||
|
||||
import org.elasticsearch.alerts.Payload;
|
||||
import org.elasticsearch.alerts.input.Input;
|
||||
import org.elasticsearch.alerts.input.InputException;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class SimpleInputTests extends ElasticsearchTestCase {
|
||||
|
||||
@Test
|
||||
public void textExecute() throws Exception {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("foo", "bar");
|
||||
data.put("baz", new ArrayList<String>() );
|
||||
Input staticInput = new SimpleInput(logger, new Payload.Simple(data));
|
||||
|
||||
Input.Result staticResult = staticInput.execute(null);
|
||||
assertEquals(staticResult.payload().data().get("foo"), "bar");
|
||||
List baz = (List)staticResult.payload().data().get("baz");
|
||||
assertTrue(baz.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testParser_Valid() throws Exception {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("foo", "bar");
|
||||
data.put("baz", new ArrayList<String>() );
|
||||
|
||||
XContentBuilder jsonBuilder = jsonBuilder();
|
||||
jsonBuilder.startObject();
|
||||
jsonBuilder.field(Input.Result.PAYLOAD_FIELD.getPreferredName(), data);
|
||||
jsonBuilder.endObject();
|
||||
|
||||
Input.Parser parser = new SimpleInput.Parser(ImmutableSettings.builder().build());
|
||||
Input input = parser.parse(XContentFactory.xContent(jsonBuilder.bytes()).createParser(jsonBuilder.bytes()));
|
||||
assertEquals(input.type(), SimpleInput.TYPE);
|
||||
|
||||
|
||||
Input.Result staticResult = input.execute(null);
|
||||
assertEquals(staticResult.payload().data().get("foo"), "bar");
|
||||
List baz = (List)staticResult.payload().data().get("baz");
|
||||
assertTrue(baz.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = InputException.class)
|
||||
public void testParser_Invalid() throws Exception {
|
||||
|
||||
XContentBuilder jsonBuilder = jsonBuilder();
|
||||
jsonBuilder.startObject();
|
||||
jsonBuilder.endObject();
|
||||
|
||||
Input.Parser parser = new SimpleInput.Parser(ImmutableSettings.builder().build());
|
||||
parser.parse(XContentFactory.xContent(jsonBuilder.bytes()).createParser(jsonBuilder.bytes()));
|
||||
fail("[simple] input parse should fail with an InputException for an empty json object");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testResultParser_Valid() throws Exception {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("foo", "bar");
|
||||
data.put("baz", new ArrayList<String>() );
|
||||
|
||||
XContentBuilder jsonBuilder = jsonBuilder();
|
||||
jsonBuilder.startObject();
|
||||
jsonBuilder.field(Input.Result.PAYLOAD_FIELD.getPreferredName(), data);
|
||||
jsonBuilder.endObject();
|
||||
|
||||
Input.Parser parser = new SimpleInput.Parser(ImmutableSettings.builder().build());
|
||||
Input.Result staticResult = parser.parseResult(XContentFactory.xContent(jsonBuilder.bytes()).createParser(jsonBuilder.bytes()));
|
||||
assertEquals(staticResult.type(), SimpleInput.TYPE);
|
||||
|
||||
assertEquals(staticResult.payload().data().get("foo"), "bar");
|
||||
List baz = (List)staticResult.payload().data().get("baz");
|
||||
assertTrue(baz.isEmpty());
|
||||
}
|
||||
|
||||
@Test(expected = InputException.class)
|
||||
public void testResultParser_Invalid() throws Exception {
|
||||
XContentBuilder jsonBuilder = jsonBuilder();
|
||||
jsonBuilder.startObject();
|
||||
jsonBuilder.endObject();
|
||||
|
||||
Input.Parser parser = new SimpleInput.Parser(ImmutableSettings.builder().build());
|
||||
parser.parseResult(XContentFactory.xContent(jsonBuilder.bytes()).createParser(jsonBuilder.bytes()));
|
||||
fail("[simple] input result parse should fail with an InputException for an empty json object");
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue