Added `compare` condition

A simple `condition` that compares a path into the model in the execution context to a value. The comparison is based on the following possible operators: `eq`, `not_eq`, `lt`, `lte`, `gt`, `gte`.

The following example shows a `compare` condition that checks if the total hits in the payload is greater or equal to 5.

```
{
	"compare" : {
		"ctx.payload.hits.total" : { "gte" :  "5" }
	}
}
```

Original commit: elastic/x-pack-elasticsearch@6d4f2bbf10
This commit is contained in:
uboness 2015-05-18 22:06:48 +02:00
parent 9e5fa64e03
commit 9aef7bb52b
17 changed files with 1123 additions and 41 deletions

View File

@ -9,6 +9,8 @@ import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.inject.multibindings.MapBinder;
import org.elasticsearch.watcher.condition.always.AlwaysCondition;
import org.elasticsearch.watcher.condition.always.AlwaysConditionFactory;
import org.elasticsearch.watcher.condition.compare.CompareCondition;
import org.elasticsearch.watcher.condition.compare.CompareConditionFactory;
import org.elasticsearch.watcher.condition.never.NeverCondition;
import org.elasticsearch.watcher.condition.never.NeverConditionFactory;
import org.elasticsearch.watcher.condition.script.ScriptCondition;
@ -42,6 +44,9 @@ public class ConditionModule extends AbstractModule {
bind(AlwaysConditionFactory.class).asEagerSingleton();
factoriesBinder.addBinding(AlwaysCondition.TYPE).to(AlwaysConditionFactory.class);
bind(CompareConditionFactory.class).asEagerSingleton();
factoriesBinder.addBinding(CompareCondition.TYPE).to(CompareConditionFactory.class);
for (Map.Entry<String, Class<? extends ConditionFactory>> entry : factories.entrySet()) {
bind(entry.getValue()).asEagerSingleton();
factoriesBinder.addBinding(entry.getKey()).to(entry.getValue());

View File

@ -0,0 +1,349 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.condition.compare;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.condition.Condition;
import org.elasticsearch.watcher.support.WatcherDateUtils;
import org.elasticsearch.watcher.support.xcontent.WatcherXContentUtils;
import java.io.IOException;
import java.util.Locale;
import java.util.Objects;
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
/**
*
*/
public class CompareCondition implements Condition {
public static final String TYPE = "compare";
private String path;
private Op op;
private Object value;
public CompareCondition(String path, Op op, Object value) {
this.path = path;
this.op = op;
this.value = value;
}
@Override
public final String type() {
return TYPE;
}
public String getPath() {
return path;
}
public Op getOp() {
return op;
}
public Object getValue() {
return value;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CompareCondition condition = (CompareCondition) o;
if (!path.equals(condition.path)) return false;
if (op != condition.op) return false;
return !(value != null ? !value.equals(condition.value) : condition.value != null);
}
@Override
public int hashCode() {
int result = path.hashCode();
result = 31 * result + op.hashCode();
result = 31 * result + (value != null ? value.hashCode() : 0);
return result;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject()
.startObject(path)
.field(op.id(), value)
.endObject()
.endObject();
}
public static CompareCondition parse(String watchId, XContentParser parser) throws IOException {
if (parser.currentToken() != XContentParser.Token.START_OBJECT) {
throw new CompareConditionException("could not parse [{}] condition for watch [{}]. expected an object but found [{}] instead", TYPE, watchId, parser.currentToken());
}
String path = null;
Object value = null;
Op op = null;
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
path = parser.currentName();
} else if (path == null) {
throw new CompareConditionException("could not parse [{}] condition for watch [{}]. expected a field indicating the compared path, but found [{}] instead", TYPE, watchId, token);
} else if (token == XContentParser.Token.START_OBJECT) {
token = parser.nextToken();
if (token != XContentParser.Token.FIELD_NAME) {
throw new CompareConditionException("could not parse [{}] condition for watch [{}]. expected a field indicating the comparison operator, but found [{}] instead", TYPE, watchId, token);
}
try {
op = Op.resolve(parser.currentName());
} catch (IllegalArgumentException iae) {
throw new CompareConditionException("could not parse [{}] condition for watch [{}]. unknown comparison operator [{}]", TYPE, watchId, parser.currentName());
}
token = parser.nextToken();
if (!op.supportsStructures() && !token.isValue() && token != XContentParser.Token.VALUE_NULL) {
throw new CompareConditionException("could not parse [{}] condition for watch [{}]. compared value for [{}] with operation [{}] must either be a numeric, string, boolean or null value, but found [{}] instead", TYPE, watchId, path, op.name().toLowerCase(Locale.ROOT), token);
}
value = WatcherXContentUtils.readValue(parser, token);
token = parser.nextToken();
if (token != XContentParser.Token.END_OBJECT) {
throw new CompareConditionException("could not parse [{}] condition for watch [{}]. expected end of path object, but found [{}] instead", TYPE, watchId, token);
}
} else {
throw new CompareConditionException("could not parse [{}] condition for watch [{}]. expected an object for field [{}] but found [{}] instead", TYPE, watchId, path, token);
}
}
return new CompareCondition(path, op, value);
}
public static class Result extends Condition.Result {
private final Object resolveValue;
Result(Object resolveValue, boolean met) {
super(TYPE, met);
this.resolveValue = resolveValue;
}
public Object getResolveValue() {
return resolveValue;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject()
.field(Field.MET.getPreferredName(), met)
.field(Field.RESOLVED_VALUE.getPreferredName(), resolveValue)
.endObject();
}
public static Result parse(String watchId, XContentParser parser) throws IOException {
Object resolvedValue = null;
boolean foundResolvedValue = false;
Boolean met = 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 (currentFieldName == null) {
throw new CompareConditionException("could not parse condition result [{}] for watcher [{}]. expected a field but found [{}] instead", TYPE, watchId, token);
} else if (token == XContentParser.Token.VALUE_BOOLEAN) {
if (Field.MET.match(currentFieldName)) {
met = parser.booleanValue();
} else {
throw new CompareConditionException("could not parse condition result [{}] for watcher [{}]. unexpected boolean field [{}]", TYPE, watchId, currentFieldName);
}
} else if (Field.RESOLVED_VALUE.match(currentFieldName)) {
resolvedValue = WatcherXContentUtils.readValue(parser, token);
foundResolvedValue = true;
} else {
throw new CompareConditionException("could not parse condition result [{}] for watcher [{}]. unexpected field [{}]", TYPE, watchId, currentFieldName);
}
}
if (!foundResolvedValue) {
throw new CompareConditionException("could not parse condition result [{}] for watcher [{}]. missing required field [{}]", TYPE, watchId, Field.RESOLVED_VALUE.getPreferredName());
}
return new Result(resolvedValue, met);
}
}
public enum Op {
EQ() {
@Override
public boolean eval(Object v1, Object v2) {
Integer compVal = compare(v1, v2);
return compVal != null && compVal == 0;
}
@Override
public boolean supportsStructures() {
return true;
}
},
NOT_EQ() {
@Override
public boolean eval(Object v1, Object v2) {
Integer compVal = compare(v1, v2);
return compVal == null || compVal != 0;
}
@Override
public boolean supportsStructures() {
return true;
}
},
LT() {
@Override
public boolean eval(Object v1, Object v2) {
Integer compVal = compare(v1, v2);
return compVal != null && compVal < 0;
}
},
LTE() {
@Override
public boolean eval(Object v1, Object v2) {
Integer compVal = compare(v1, v2);
return compVal != null && compVal <= 0;
}
},
GT() {
@Override
public boolean eval(Object v1, Object v2) {
Integer compVal = compare(v1, v2);
return compVal != null && compVal > 0;
}
},
GTE() {
@Override
public boolean eval(Object v1, Object v2) {
Integer compVal = compare(v1, v2);
return compVal != null && compVal >= 0;
}
};
public abstract boolean eval(Object v1, Object v2);
public boolean supportsStructures() {
return false;
}
// this method performs lenient comparison, potentially between different types. The second argument
// type (v2) determines the type of comparison (this is because the second argument is configured by the
// user while the first argument is the dynamic path that is evaluated at runtime. That is, if the user configures
// a number, it expects a number, therefore the comparison will be based on numeric comparison). If the
// comparison is numeric, other types (e.g. strings) will converted to numbers if possible, if not, the comparison
// will fail and `false` will be returned.
//
// may return `null` indicating v1 simply doesn't equal v2 (without any order association)
static Integer compare(Object v1, Object v2) {
if (Objects.equals(v1, v2)) {
return 0;
}
if (v1 == null || v2 == null) {
return null;
}
// special case for numbers. If v1 is not a number, we'll try to convert it to a number
if (v2 instanceof Number) {
if (!(v1 instanceof Number)) {
try {
v1 = Double.valueOf(String.valueOf(v1));
} catch (NumberFormatException nfe) {
// could not convert to number
return null;
}
}
return ((Number) v1).doubleValue() > ((Number) v2).doubleValue() ? 1 :
((Number) v1).doubleValue() < ((Number) v2).doubleValue() ? -1 : 0;
}
// special case for strings. If v1 is not a string, we'll convert it to a string
if (v2 instanceof String) {
v1 = String.valueOf(v1);
return ((String) v1).compareTo((String) v2);
}
// special case for date/times. If v1 is not a dateTime, we'll try to convert it to a datetime
if (v2 instanceof DateTime) {
if (v1 instanceof DateTime) {
return ((DateTime) v1).compareTo((DateTime) v2);
}
if (v1 instanceof String) {
try {
v1 = WatcherDateUtils.parseDate((String) v1);
} catch (Exception e) {
return null;
}
} else if (v1 instanceof Number){
v1 = new DateTime(((Number) v1).longValue(), UTC);
} else {
// cannot convert to date...
return null;
}
return ((DateTime) v1).compareTo((DateTime) v2);
}
if (v1.getClass() != v2.getClass() || Comparable.class.isAssignableFrom(v1.getClass())) {
return null;
}
try {
return ((Comparable) v1).compareTo(v2);
} catch (Exception e) {
return null;
}
}
public String id() {
return name().toLowerCase(Locale.ROOT);
}
public static Op resolve(String id) {
return Op.valueOf(id.toUpperCase(Locale.ROOT));
}
}
public static class Builder implements Condition.Builder<CompareCondition> {
private String path;
private Op op;
private Object value;
public Builder(String path, Op op, Object value) {
this.path = path;
this.op = op;
this.value = value;
}
public CompareCondition build() {
return new CompareCondition(path, op, value);
}
}
public static class EvaluationException extends CompareConditionException {
public EvaluationException(String msg, Object... args) {
super(msg, args);
}
public EvaluationException(String msg, Throwable cause, Object... args) {
super(msg, cause, args);
}
}
interface Field extends Condition.Field {
ParseField RESOLVED_VALUE = new ParseField("resolved_value");
}
}

View File

@ -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.watcher.condition.compare;
import org.elasticsearch.watcher.condition.ConditionException;
/**
*
*/
public class CompareConditionException extends ConditionException {
public CompareConditionException(String msg, Object... args) {
super(msg, args);
}
public CompareConditionException(String msg, Throwable cause, Object... args) {
super(msg, cause, args);
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.condition.compare;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.joda.DateMathParser;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.condition.ConditionFactory;
import org.elasticsearch.watcher.support.WatcherDateUtils;
import org.elasticsearch.watcher.support.clock.Clock;
import java.io.IOException;
/**
*
*/
public class CompareConditionFactory extends ConditionFactory<CompareCondition, CompareCondition.Result, ExecutableCompareCondition> {
private final Clock clock;
@Inject
public CompareConditionFactory(Settings settings, Clock clock) {
super(Loggers.getLogger(ExecutableCompareCondition.class, settings));
this.clock = clock;
}
@Override
public String type() {
return CompareCondition.TYPE;
}
@Override
public CompareCondition parseCondition(String watchId, XContentParser parser) throws IOException {
return CompareCondition.parse(watchId, parser);
}
@Override
public CompareCondition.Result parseResult(String watchId, XContentParser parser) throws IOException {
return CompareCondition.Result.parse(watchId, parser);
}
@Override
public ExecutableCompareCondition createExecutable(CompareCondition condition) {
return new ExecutableCompareCondition(condition, conditionLogger, clock);
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.condition.compare;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.watcher.condition.ExecutableCondition;
import org.elasticsearch.watcher.execution.WatchExecutionContext;
import org.elasticsearch.watcher.support.Variables;
import org.elasticsearch.watcher.support.WatcherDateUtils;
import org.elasticsearch.watcher.support.clock.Clock;
import org.elasticsearch.watcher.support.xcontent.MapPath;
import java.io.IOException;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
/**
*
*/
public class ExecutableCompareCondition extends ExecutableCondition<CompareCondition, CompareCondition.Result> {
static final Pattern DATE_MATH_PATTERN = Pattern.compile("<\\{(.+)\\}>");
static final Pattern PATH_PATTERN = Pattern.compile("\\{\\{(.+)\\}\\}");
private final Clock clock;
public ExecutableCompareCondition(CompareCondition condition, ESLogger logger, Clock clock) {
super(condition, logger);
this.clock = clock;
}
@Override
public CompareCondition.Result execute(WatchExecutionContext ctx) throws IOException {
Map<String, Object> model = Variables.createCtxModel(ctx, ctx.payload());
Object configuredValue = condition.getValue();
if (configuredValue instanceof String) {
// checking if the given value is a date math expression
Matcher matcher = DATE_MATH_PATTERN.matcher((String) configuredValue);
if (matcher.matches()) {
String dateMath = matcher.group(1);
configuredValue = WatcherDateUtils.parseDateMath(dateMath, UTC, clock);
} else {
// checking if the given value is a path expression
matcher = PATH_PATTERN.matcher((String) configuredValue);
if (matcher.matches()) {
String configuredPath = matcher.group(1);
configuredValue = MapPath.eval(configuredPath, model);
}
}
}
Object resolvedValue = MapPath.eval(condition.getPath(), model);
return new CompareCondition.Result(resolvedValue, condition.getOp().eval(resolvedValue, configuredValue));
}
}

View File

@ -0,0 +1,67 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.support.xcontent;
import org.elasticsearch.common.Strings;
import java.lang.reflect.Array;
import java.util.List;
import java.util.Map;
/**
*
*/
public class MapPath {
private MapPath() {
}
public static <T> T eval(String path, Map<String, Object> map) {
return (T) eval(path, (Object) map);
}
private static Object eval(String path, Object ctx) {
String[] parts = Strings.splitStringToArray(path, '.');
StringBuilder resolved = new StringBuilder();
for (String part : parts) {
if (ctx == null) {
return null;
}
if (ctx instanceof Map) {
ctx = ((Map) ctx).get(part);
if (resolved.length() != 0) {
resolved.append(".");
}
resolved.append(part);
} else if (ctx instanceof List) {
try {
int index = Integer.parseInt(part);
ctx = ((List) ctx).get(index);
if (resolved.length() != 0) {
resolved.append(".");
}
resolved.append(part);
} catch (NumberFormatException nfe) {
return null;
}
} else if (ctx.getClass().isArray()) {
try {
int index = Integer.parseInt(part);
ctx = Array.get(ctx, index);
if (resolved.length() != 0) {
resolved.append(".");
}
resolved.append(part);
} catch (NumberFormatException nfe) {
return null;
}
} else {
return null;
}
}
return ctx;
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.support.xcontent;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
*
*/
public class WatcherXContentUtils {
private WatcherXContentUtils() {
}
// TODO open this up in core
public static List<Object> readList(XContentParser parser, XContentParser.Token token) throws IOException {
ArrayList<Object> list = new ArrayList<>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
list.add(readValue(parser, token));
}
return list;
}
// TODO open this up in core
public static Object readValue(XContentParser parser, XContentParser.Token token) throws IOException {
if (token == XContentParser.Token.VALUE_NULL) {
return null;
} else if (token == XContentParser.Token.VALUE_STRING) {
return parser.text();
} else if (token == XContentParser.Token.VALUE_NUMBER) {
XContentParser.NumberType numberType = parser.numberType();
if (numberType == XContentParser.NumberType.INT) {
return parser.intValue();
} else if (numberType == XContentParser.NumberType.LONG) {
return parser.longValue();
} else if (numberType == XContentParser.NumberType.FLOAT) {
return parser.floatValue();
} else if (numberType == XContentParser.NumberType.DOUBLE) {
return parser.doubleValue();
}
} else if (token == XContentParser.Token.VALUE_BOOLEAN) {
return parser.booleanValue();
} else if (token == XContentParser.Token.START_OBJECT) {
return parser.map();
} else if (token == XContentParser.Token.START_ARRAY) {
return readList(parser, token);
} else if (token == XContentParser.Token.VALUE_EMBEDDED_OBJECT) {
return parser.binaryValue();
}
return null;
}
}

View File

@ -11,7 +11,6 @@ import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.*;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import java.io.IOException;
import java.util.Map;
@ -59,7 +58,7 @@ public class XContentSource implements ToXContent {
* @return The extracted value or {@code null} if no value is associated with the given path
*/
public <T> T getValue(String path) {
return (T) XContentMapValues.extractValue(path, getAsMap());
return (T) MapPath.eval(path, getAsMap());
}
@Override

View File

@ -128,6 +128,20 @@
}
}
},
"condition" : {
"type" : "object",
"dynamic" : true,
"properties" : {
"compare" : {
"type" : "object",
"enabled" : false
},
"script" : {
"type" : "object",
"enabled" : false
}
}
},
"transform" : {
"type" : "object",
"dynamic" : true,

View File

@ -0,0 +1,82 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.condition.compare;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.ShardSearchFailure;
import org.elasticsearch.common.text.StringText;
import org.elasticsearch.search.SearchShardTarget;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogram;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
import org.elasticsearch.search.internal.InternalSearchHit;
import org.elasticsearch.search.internal.InternalSearchHits;
import org.elasticsearch.search.internal.InternalSearchResponse;
import org.elasticsearch.watcher.execution.WatchExecutionContext;
import org.elasticsearch.watcher.support.clock.SystemClock;
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
import org.elasticsearch.watcher.watch.Payload;
import org.junit.Test;
import static org.elasticsearch.watcher.test.WatcherTestUtils.mockExecutionContext;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.when;
/**
*/
public class CompareConditionSearchTests extends AbstractWatcherIntegrationTests {
@Test
public void testExecute_withAggs() throws Exception {
client().admin().indices().prepareCreate("my-index")
.addMapping("my-type", "_timestamp", "enabled=true")
.get();
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:00").setSource("{}").get();
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:10").setSource("{}").get();
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:20").setSource("{}").get();
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:30").setSource("{}").get();
refresh();
SearchResponse response = client().prepareSearch("my-index")
.addAggregation(AggregationBuilders.dateHistogram("rate").field("_timestamp").interval(DateHistogram.Interval.HOUR).order(Histogram.Order.COUNT_DESC))
.get();
ExecutableCompareCondition condition = new ExecutableCompareCondition(new CompareCondition("ctx.payload.aggregations.rate.buckets.0.doc_count", CompareCondition.Op.GTE, 5), logger, SystemClock.INSTANCE);
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
assertThat(condition.execute(ctx).met(), is(false));
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:40").setSource("{}").get();
refresh();
response = client().prepareSearch("my-index")
.addAggregation(AggregationBuilders.dateHistogram("rate").field("_timestamp").interval(DateHistogram.Interval.HOUR).order(Histogram.Order.COUNT_DESC))
.get();
ctx = mockExecutionContext("_name", new Payload.XContent(response));
assertThat(condition.execute(ctx).met(), is(true));
}
@Test
public void testExecute_accessHits() throws Exception {
ExecutableCompareCondition condition = new ExecutableCompareCondition(new CompareCondition("ctx.payload.hits.hits.0._score", CompareCondition.Op.EQ, 1), logger, SystemClock.INSTANCE);
InternalSearchHit hit = new InternalSearchHit(0, "1", new StringText("type"), null);
hit.score(1f);
hit.shard(new SearchShardTarget("a", "a", 0));
InternalSearchResponse internalSearchResponse = new InternalSearchResponse(new InternalSearchHits(new InternalSearchHit[]{hit}, 1l, 1f), null, null, null, false, null);
SearchResponse response = new SearchResponse(internalSearchResponse, "", 3, 3, 500l, new ShardSearchFailure[0]);
WatchExecutionContext ctx = mockExecutionContext("_watch_name", new Payload.XContent(response));
assertThat(condition.execute(ctx).met(), is(true));
hit.score(2f);
when(ctx.payload()).thenReturn(new Payload.XContent(response));
assertThat(condition.execute(ctx).met(), is(false));
}
}

View File

@ -0,0 +1,286 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.condition.compare;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import com.carrotsearch.randomizedtesting.annotations.Seed;
import org.elasticsearch.common.collect.ImmutableList;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.watcher.condition.compare.CompareCondition.Op;
import org.elasticsearch.watcher.execution.WatchExecutionContext;
import org.elasticsearch.watcher.support.clock.ClockMock;
import org.elasticsearch.watcher.support.clock.SystemClock;
import org.elasticsearch.watcher.watch.Payload;
import org.junit.Test;
import java.util.Locale;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.watcher.test.WatcherTestUtils.mockExecutionContext;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
/**
*/
@Seed("9E70A915296AA3F2:FAA20587D7DCA86B")
public class CompareConditionTests extends ElasticsearchTestCase {
@Test
public void testOpEval_EQ() throws Exception {
assertThat(Op.EQ.eval(null, null), is(true));
assertThat(Op.EQ.eval(4, 3.0), is(false));
assertThat(Op.EQ.eval(3, 3.0), is(true));
assertThat(Op.EQ.eval(2, new Float(3.0)), is(false));
assertThat(Op.EQ.eval(3, null), is(false));
assertThat(Op.EQ.eval(2, "2"), is(true)); // comparing as strings
assertThat(Op.EQ.eval(3, "4"), is(false)); // comparing as strings
assertThat(Op.EQ.eval(3, "a"), is(false)); // comparing as strings
assertThat(Op.EQ.eval("3", 3), is(true)); // comparing as numbers
assertThat(Op.EQ.eval("a", "aa"), is(false));
assertThat(Op.EQ.eval("a", "a"), is(true));
assertThat(Op.EQ.eval("aa", "ab"), is(false));
assertThat(Op.EQ.eval(ImmutableMap.of("k", "v"), ImmutableMap.of("k", "v")), is(true));
assertThat(Op.EQ.eval(ImmutableMap.of("k", "v"), ImmutableMap.of("k1", "v1")), is(false));
assertThat(Op.EQ.eval(ImmutableList.of("k", "v"), ImmutableList.of("k", "v")), is(true));
assertThat(Op.EQ.eval(ImmutableList.of("k", "v"), ImmutableList.of("k1", "v1")), is(false));
}
@Test
public void testOpEval_NOT_EQ() throws Exception {
assertThat(Op.NOT_EQ.eval(null, null), is(false));
assertThat(Op.NOT_EQ.eval(4, 3.0), is(true));
assertThat(Op.NOT_EQ.eval(3, 3.0), is(false));
assertThat(Op.NOT_EQ.eval(2, new Float(3.0)), is(true));
assertThat(Op.NOT_EQ.eval(3, null), is(true));
assertThat(Op.NOT_EQ.eval(2, "2"), is(false)); // comparing as strings
assertThat(Op.NOT_EQ.eval(3, "4"), is(true)); // comparing as strings
assertThat(Op.NOT_EQ.eval(3, "a"), is(true)); // comparing as strings
assertThat(Op.NOT_EQ.eval("3", 3), is(false)); // comparing as numbers
assertThat(Op.NOT_EQ.eval("a", "aa"), is(true));
assertThat(Op.NOT_EQ.eval("a", "a"), is(false));
assertThat(Op.NOT_EQ.eval("aa", "ab"), is(true));
assertThat(Op.NOT_EQ.eval(ImmutableMap.of("k", "v"), ImmutableMap.of("k", "v")), is(false));
assertThat(Op.NOT_EQ.eval(ImmutableMap.of("k", "v"), ImmutableMap.of("k1", "v1")), is(true));
assertThat(Op.NOT_EQ.eval(ImmutableList.of("k", "v"), ImmutableList.of("k", "v")), is(false));
assertThat(Op.NOT_EQ.eval(ImmutableList.of("k", "v"), ImmutableList.of("k1", "v1")), is(true));
}
@Test
public void testOpEval_GTE() throws Exception {
assertThat(Op.GTE.eval(4, 3.0), is(true));
assertThat(Op.GTE.eval(3, 3.0), is(true));
assertThat(Op.GTE.eval(2, new Float(3.0)), is(false));
assertThat(Op.GTE.eval(3, null), is(false));
assertThat(Op.GTE.eval(3, "2"), is(true)); // comparing as strings
assertThat(Op.GTE.eval(3, "4"), is(false)); // comparing as strings
assertThat(Op.GTE.eval(3, "a"), is(false)); // comparing as strings
assertThat(Op.GTE.eval("4", 3), is(true)); // comparing as numbers
assertThat(Op.GTE.eval("a", "aa"), is(false));
assertThat(Op.GTE.eval("a", "a"), is(true));
assertThat(Op.GTE.eval("aa", "ab"), is(false));
}
@Test
public void testOpEval_GT() throws Exception {
assertThat(Op.GT.eval(4, 3.0), is(true));
assertThat(Op.GT.eval(3, 3.0), is(false));
assertThat(Op.GT.eval(2, new Float(3.0)), is(false));
assertThat(Op.GT.eval(3, null), is(false));
assertThat(Op.GT.eval(3, "2"), is(true)); // comparing as strings
assertThat(Op.GT.eval(3, "4"), is(false)); // comparing as strings
assertThat(Op.GT.eval(3, "a"), is(false)); // comparing as strings
assertThat(Op.GT.eval("4", 3), is(true)); // comparing as numbers
assertThat(Op.GT.eval("a", "aa"), is(false));
assertThat(Op.GT.eval("a", "a"), is(false));
assertThat(Op.GT.eval("aa", "ab"), is(false));
}
@Test
public void testOpEval_LTE() throws Exception {
assertThat(Op.LTE.eval(4, 3.0), is(false));
assertThat(Op.LTE.eval(3, 3.0), is(true));
assertThat(Op.LTE.eval(2, new Float(3.0)), is(true));
assertThat(Op.LTE.eval(3, null), is(false));
assertThat(Op.LTE.eval(3, "2"), is(false)); // comparing as strings
assertThat(Op.LTE.eval(3, "4"), is(true)); // comparing as strings
assertThat(Op.LTE.eval(3, "a"), is(true)); // comparing as strings
assertThat(Op.LTE.eval("4", 3), is(false)); // comparing as numbers
assertThat(Op.LTE.eval("a", "aa"), is(true));
assertThat(Op.LTE.eval("a", "a"), is(true));
assertThat(Op.LTE.eval("aa", "ab"), is(true));
}
@Test
public void testOpEval_LT() throws Exception {
assertThat(Op.LT.eval(4, 3.0), is(false));
assertThat(Op.LT.eval(3, 3.0), is(false));
assertThat(Op.LT.eval(2, new Float(3.0)), is(true));
assertThat(Op.LT.eval(3, null), is(false));
assertThat(Op.LT.eval(3, "2"), is(false)); // comparing as strings
assertThat(Op.LT.eval(3, "4"), is(true)); // comparing as strings
assertThat(Op.LT.eval(3, "a"), is(true)); // comparing as strings
assertThat(Op.LT.eval("4", 3), is(false)); // comparing as numbers
assertThat(Op.LT.eval("a", "aa"), is(true));
assertThat(Op.LT.eval("a", "a"), is(false));
assertThat(Op.LT.eval("aa", "ab"), is(true));
}
@Test @Repeat(iterations = 10)
public void testExecute() throws Exception {
Op op = randomFrom(Op.values());
int value = randomInt(10);
int payloadValue = randomInt(10);
boolean met = op.eval(payloadValue, value);
ExecutableCompareCondition condition = new ExecutableCompareCondition(new CompareCondition("ctx.payload.value", op, value), logger, SystemClock.INSTANCE);
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.Simple("value", payloadValue));
assertThat(condition.execute(ctx).met(), is(met));
}
@Test @Repeat(iterations = 10)
public void testExecute_DateMath() throws Exception {
ClockMock clock = new ClockMock();
boolean met = randomBoolean();
Op op = met ? randomFrom(Op.GT, Op.GTE, Op.NOT_EQ) : randomFrom(Op.LT, Op.LTE, Op.EQ);
String value = "<{now-1d}>";
DateTime payloadValue = clock.now();
ExecutableCompareCondition condition = new ExecutableCompareCondition(new CompareCondition("ctx.payload.value", op, value), logger, clock);
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.Simple("value", payloadValue));
assertThat(condition.execute(ctx).met(), is(met));
}
@Test @Repeat(iterations = 5)
public void testExecute_Path() throws Exception {
ClockMock clock = new ClockMock();
boolean met = randomBoolean();
Op op = met ? Op.EQ : Op.NOT_EQ;
String value = "{{ctx.payload.value}}";
Object payloadValue = new Object();
ExecutableCompareCondition condition = new ExecutableCompareCondition(new CompareCondition("ctx.payload.value", op, value), logger, clock);
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.Simple("value", payloadValue));
assertThat(condition.execute(ctx).met(), is(met));
}
@Test @Repeat(iterations = 10)
public void testParse_Valid() throws Exception {
Op op = randomFrom(Op.values());
Object value = randomFrom("value", 1, null);
CompareConditionFactory factory = new CompareConditionFactory(ImmutableSettings.EMPTY, SystemClock.INSTANCE);
XContentBuilder builder = jsonBuilder();
builder.startObject();
builder.startObject("key1.key2");
builder.field(op.name().toLowerCase(Locale.ROOT), value);
builder.endObject();
builder.endObject();
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
parser.nextToken();
CompareCondition condition = factory.parseCondition("_id", parser);
assertThat(condition, notNullValue());
assertThat(condition.getPath(), is("key1.key2"));
assertThat(condition.getOp(), is(op));
assertThat(condition.getValue(), is(value));
}
@Test(expected = CompareConditionException.class)
public void testParse_InValid_NoOperationBody() throws Exception {
CompareConditionFactory factory = new CompareConditionFactory(ImmutableSettings.EMPTY, SystemClock.INSTANCE);
XContentBuilder builder = jsonBuilder();
builder.startObject();
builder.startObject("key1.key2");
builder.endObject();
builder.endObject();
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
factory.parseCondition("_id", parser);
}
@Test(expected = CompareConditionException.class)
public void testParse_InValid_UnknownOp() throws Exception {
Object value = randomFrom("value", 1, null);
CompareConditionFactory factory = new CompareConditionFactory(ImmutableSettings.EMPTY, SystemClock.INSTANCE);
XContentBuilder builder = jsonBuilder();
builder.startObject();
builder.startObject("key1.key2");
builder.field("foobar", value);
builder.endObject();
builder.endObject();
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
parser.nextToken();
factory.parseCondition("_id", parser);
}
@Test(expected = CompareConditionException.class) @Repeat(iterations = 10)
public void testParse_InValid_WrongValueForOp() throws Exception {
Object value = randomFrom(ImmutableList.of("1", "2"), ImmutableMap.of("key", "value"));
String op = randomFrom("lt", "lte", "gt", "gte");
CompareConditionFactory factory = new CompareConditionFactory(ImmutableSettings.EMPTY, SystemClock.INSTANCE);
XContentBuilder builder = jsonBuilder();
builder.startObject();
builder.startObject("key1.key2");
builder.field(op, value);
builder.endObject();
builder.endObject();
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
parser.nextToken();
factory.parseCondition("_id", parser);
}
@Test @Repeat(iterations = 10)
public void testParse_Result_Valid() throws Exception {
CompareConditionFactory factory = new CompareConditionFactory(ImmutableSettings.EMPTY, SystemClock.INSTANCE);
boolean met = randomBoolean();
Object resolvedValue = randomFrom("1", 5, null, ImmutableList.of("1", "2"), ImmutableMap.of("key", "value"));
XContentBuilder builder = jsonBuilder();
builder.startObject();
builder.field("met", met);
builder.field("resolved_value", resolvedValue);
builder.endObject();
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
parser.nextToken();
CompareCondition.Result result = factory.parseResult("_id", parser);
assertThat(result, notNullValue());
assertThat(result.met(), is(met));
assertThat(result.getResolveValue(), is(resolvedValue));
}
@Test(expected = CompareConditionException.class)
public void testParse_Result_Invalid_MissingResolvedValue() throws Exception {
CompareConditionFactory factory = new CompareConditionFactory(ImmutableSettings.EMPTY, SystemClock.INSTANCE);
boolean met = randomBoolean();
XContentBuilder builder = jsonBuilder();
builder.startObject();
builder.field("met", met);
builder.endObject();
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
parser.nextToken();
factory.parseResult("_id", parser);
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.support.xcontent;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import org.elasticsearch.common.collect.ImmutableList;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.Test;
import java.util.List;
import java.util.Map;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.Matchers.is;
/**
*
*/
public class MapPathTests extends ElasticsearchTestCase {
@Test
public void testEval() throws Exception {
Map<String, Object> map = ImmutableMap.<String, Object>builder()
.put("key", "value")
.build();
assertThat(MapPath.eval("key", map), is((Object) "value"));
assertThat(MapPath.eval("key1", map), nullValue());
}
@Test @Repeat(iterations = 5)
public void testEval_List() throws Exception {
List list = ImmutableList.of(1, 2, 3, 4);
Map<String, Object> map = ImmutableMap.<String, Object>builder()
.put("key", list)
.build();
int index = randomInt(3);
assertThat(MapPath.eval("key." + index, map), is(list.get(index)));
}
@Test @Repeat(iterations = 5)
public void testEval_Array() throws Exception {
int[] array = new int[] { 1, 2, 3, 4 };
Map<String, Object> map = ImmutableMap.<String, Object>builder()
.put("key", array)
.build();
int index = randomInt(3);
assertThat(((Number) MapPath.eval("key." + index, map)).intValue(), is(array[index]));
}
@Test
public void testEval_Map() throws Exception {
Map<String, Object> map = ImmutableMap.<String, Object>builder()
.put("a", ImmutableMap.of("b", "val"))
.build();
assertThat(MapPath.eval("a.b", map), is((Object) "val"));
}
@Test
public void testEval_Mixed() throws Exception {
Map<String, Object> map = ImmutableMap.<String, Object>builder()
.put("a", ImmutableMap.builder()
.put("b", ImmutableList.builder()
.add(ImmutableList.builder()
.add(ImmutableMap.builder()
.put("c", "val")
.build())
.build())
.build())
.build())
.build();
assertThat(MapPath.eval("", map), is((Object) map));
assertThat(MapPath.eval("a.b.0.0.c", map), is((Object) "val"));
assertThat(MapPath.eval("a.b.0.0.c.d", map), nullValue());
assertThat(MapPath.eval("a.b.0.0.d", map), nullValue());
assertThat(MapPath.eval("a.b.c", map), nullValue());
}
}

View File

@ -144,11 +144,8 @@ public class EmailSecretsIntegrationTests extends AbstractWatcherIntegrationTest
.get();
assertThat(executeResponse, notNullValue());
contentSource = executeResponse.getSource();
value = contentSource.getValue("execution_result.actions.email.success");
assertThat(value, instanceOf(List.class));
List<Boolean> values = (List<Boolean>) value;
assertThat(values, hasSize(1));
assertThat(values, hasItem(Boolean.TRUE));
value = contentSource.getValue("execution_result.actions.0.email.success");
assertThat((Boolean) value, is(true));
if (!latch.await(5, TimeUnit.SECONDS)) {
fail("waiting too long for the email to be sent");

View File

@ -32,7 +32,6 @@ import org.junit.Before;
import org.junit.Test;
import java.net.BindException;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
@ -226,24 +225,18 @@ public class HttpSecretsIntegrationTests extends AbstractWatcherIntegrationTests
assertThat(executeResponse, notNullValue());
contentSource = executeResponse.getSource();
value = contentSource.getValue("execution_result.actions.webhook.response.status");
assertThat(value, instanceOf(List.class));
value = contentSource.getValue("execution_result.actions.0.webhook.response.status");
assertThat(value, notNullValue());
List<Number> values = (List<Number>) value;
assertThat(values, hasSize(1));
assertThat(values, hasItem(200));
assertThat(value, instanceOf(Number.class));
assertThat(((Number) value).intValue(), is(200));
value = contentSource.getValue("execution_result.actions.webhook.request.auth.username");
value = contentSource.getValue("execution_result.actions.0.webhook.request.auth.username");
assertThat(value, notNullValue());
assertThat(value, instanceOf(List.class));
values = (List<Number>) value;
assertThat(values, hasSize(1)); // the auth username exists
assertThat(value, instanceOf(String.class));
assertThat((String) value, is(USERNAME)); // the auth username exists
value = contentSource.getValue("execution_result.actions.webhook.request.auth.password");
assertThat(value, notNullValue());
assertThat(value, instanceOf(List.class));
values = (List<Number>) value;
assertThat(values, hasSize(0)); // but the auth password was filtered out
value = contentSource.getValue("execution_result.actions.0.webhook.request.auth.password");
assertThat(value, nullValue()); // but the auth password was filtered out
RecordedRequest request = webServer.takeRequest();
assertThat(request.getHeader("Authorization"), equalTo(ApplicableBasicAuth.headerValue(USERNAME, PASSWORD.toCharArray())));

View File

@ -33,7 +33,6 @@ import java.net.BindException;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
import static org.elasticsearch.watcher.client.WatchSourceBuilders.watchBuilder;
@ -120,15 +119,13 @@ public class WebhookHttpsIntegrationTests extends AbstractWatcherIntegrationTest
.get();
assertNoFailures(response);
XContentSource source = new XContentSource(response.getHits().getAt(0).sourceRef());
List<String> bodies = source.getValue("execution_result.actions.webhook.response.body");
assertThat(bodies, notNullValue());
assertThat(bodies, hasSize(1));
assertThat(bodies, hasItem("body"));
String body = source.getValue("execution_result.actions.0.webhook.response.body");
assertThat(body, notNullValue());
assertThat(body, is("body"));
List<Number> statuses = source.getValue("execution_result.actions.webhook.response.status");
assertThat(statuses, notNullValue());
assertThat(statuses, hasSize(1));
assertThat(statuses, hasItem(200));
Number status = source.getValue("execution_result.actions.0.webhook.response.status");
assertThat(status, notNullValue());
assertThat(status.intValue(), is(200));
}
@Test

View File

@ -26,7 +26,6 @@ import org.junit.Before;
import org.junit.Test;
import java.net.BindException;
import java.util.List;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
import static org.elasticsearch.watcher.client.WatchSourceBuilders.watchBuilder;
@ -98,12 +97,12 @@ public class WebhookIntegrationTests extends AbstractWatcherIntegrationTests {
assertNoFailures(response);
XContentSource source = new XContentSource(response.getHits().getAt(0).getSourceRef());
List<String> bodies = source.getValue("execution_result.actions.webhook.response.body");
assertThat(bodies, notNullValue());
assertThat(bodies, hasItem("body"));
List<Number> statuses = source.getValue("execution_result.actions.webhook.response.status");
assertThat(statuses, notNullValue());
assertThat(statuses, hasItem(200));
String body = source.getValue("execution_result.actions.0.webhook.response.body");
assertThat(body, notNullValue());
assertThat(body, is("body"));
Number status = source.getValue("execution_result.actions.0.webhook.response.status");
assertThat(status, notNullValue());
assertThat(status.intValue(), is(200));
}
@Test

View File

@ -6,7 +6,6 @@
package org.elasticsearch.watcher.watch;
import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.collect.ImmutableSet;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.ImmutableList;
import org.elasticsearch.common.collect.ImmutableMap;
@ -42,6 +41,10 @@ import org.elasticsearch.watcher.condition.ExecutableCondition;
import org.elasticsearch.watcher.condition.always.AlwaysCondition;
import org.elasticsearch.watcher.condition.always.AlwaysConditionFactory;
import org.elasticsearch.watcher.condition.always.ExecutableAlwaysCondition;
import org.elasticsearch.watcher.condition.compare.CompareCondition;
import org.elasticsearch.watcher.condition.compare.CompareCondition.Op;
import org.elasticsearch.watcher.condition.compare.CompareConditionFactory;
import org.elasticsearch.watcher.condition.compare.ExecutableCompareCondition;
import org.elasticsearch.watcher.condition.script.ExecutableScriptCondition;
import org.elasticsearch.watcher.condition.script.ScriptCondition;
import org.elasticsearch.watcher.condition.script.ScriptConditionFactory;
@ -127,7 +130,7 @@ public class WatchTests extends ElasticsearchTestCase {
logger = Loggers.getLogger(WatchTests.class);
}
@Test @Repeat(iterations = 20)
@Test //@Repeat(iterations = 20)
public void testParser_SelfGenerated() throws Exception {
TransformRegistry transformRegistry = transformRegistry();
@ -314,10 +317,12 @@ public class WatchTests extends ElasticsearchTestCase {
}
private ExecutableCondition randomCondition() {
String type = randomFrom(ScriptCondition.TYPE, AlwaysCondition.TYPE);
String type = randomFrom(ScriptCondition.TYPE, AlwaysCondition.TYPE, CompareCondition.TYPE);
switch (type) {
case ScriptCondition.TYPE:
return new ExecutableScriptCondition(new ScriptCondition(Script.inline("_script").build()), logger, scriptService);
case CompareCondition.TYPE:
return new ExecutableCompareCondition(new CompareCondition("_path", randomFrom(Op.values()), randomFrom(5, "3")), logger, SystemClock.INSTANCE);
default:
return new ExecutableAlwaysCondition(logger);
}
@ -329,6 +334,9 @@ public class WatchTests extends ElasticsearchTestCase {
case ScriptCondition.TYPE:
parsers.put(ScriptCondition.TYPE, new ScriptConditionFactory(settings, scriptService));
return new ConditionRegistry(parsers.build());
case CompareCondition.TYPE:
parsers.put(CompareCondition.TYPE, new CompareConditionFactory(settings, SystemClock.INSTANCE));
return new ConditionRegistry(parsers.build());
default:
parsers.put(AlwaysCondition.TYPE, new AlwaysConditionFactory(settings));
return new ConditionRegistry(parsers.build());