diff --git a/README.md b/README.md index 9b84718..a6c4b6a 100644 --- a/README.md +++ b/README.md @@ -154,8 +154,8 @@ actual backslash character. A backslash escaping any character other than asteri Anything-but matching does what the name says: matches anything *except* what's provided in the rule. -Anything-but works with single string and numeric values or lists, which have to contain entirely strings or -entirely numerics. It also may be applied to a prefix match. +Anything-but works with single string and numeric values or lists, which have to contain entirely strings or entirely +numerics. It also may be applied to a prefix, suffix, or equals-ignore-case match of a string or a list of strings. Single anything-but (string, then numeric): ```javascript @@ -198,6 +198,15 @@ Anything-but prefix: } ``` +Anything-but prefix list (strings): +```javascript +{ + "detail": { + "state": [ { "anything-but": { "prefix": [ "init", "error" ] } } ] + } +} +``` + Anything-but suffix: ```javascript { @@ -207,6 +216,25 @@ Anything-but suffix: } ``` +Anything-but suffix list (strings): +```javascript +{ + "detail": { + "instance-id": [ { "anything-but": { "suffix": [ "1234", "6789" ] } } ] + } +} +``` + +Anything-but-ignore-case: +```javascript +{ + "detail": { + "state": [ { "anything-but": {"equals-ignore-case": "Stopped" } } ] + } +} + +``` + Anything-but-ignore-case list (strings): ```javascript { @@ -717,7 +745,15 @@ public static ValuePatterns suffixEqualsIgnoreCaseMatch(final String suffix); public static ValuePatterns equalsIgnoreCaseMatch(final String value); public static ValuePatterns wildcardMatch(final String value); public static AnythingBut anythingButMatch(final String anythingBut); -public static AnythingBut anythingButPrefix(final String prefix); +public static AnythingBut anythingButMatch(final Set anythingButs); +public static AnythingBut anythingButMatch(final double anythingBut); +public static AnythingBut anythingButNumberMatch(final Set anythingButs); +public static AnythingButValuesSet anythingButPrefix(final String prefix); +public static AnythingButValuesSet anythingButPrefix(final Set anythingButs); +public static AnythingButValuesSet anythingButSuffix(final String suffix); +public static AnythingButValuesSet anythingButSuffix(final Set anythingButs); +public static AnythingButValuesSet anythingButIgnoreCaseMatch(final String anythingBut); +public static AnythingButValuesSet anythingButIgnoreCaseMatch(final Set anythingButs); public static ValuePatterns numericEquals(final double val); public static Range lessThan(final double val); public static Range lessThanOrEqualTo(final double val); diff --git a/pom.xml b/pom.xml index d7d6098..a6470ed 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ software.amazon.event.ruler event-ruler Event Ruler - 1.7.2 + 1.7.3 Event Ruler is a Java library that allows matching Rules to Events. An event is a list of fields, which may be given as name/value pairs or as a JSON object. A rule associates event field names with lists of possible values. There are two reasons to use Ruler: 1/ It's fast; the time it takes to match Events doesn't diff --git a/src/main/software/amazon/event/ruler/AnythingButEqualsIgnoreCase.java b/src/main/software/amazon/event/ruler/AnythingButValuesSet.java similarity index 70% rename from src/main/software/amazon/event/ruler/AnythingButEqualsIgnoreCase.java rename to src/main/software/amazon/event/ruler/AnythingButValuesSet.java index 52a61cd..edb8ed3 100755 --- a/src/main/software/amazon/event/ruler/AnythingButEqualsIgnoreCase.java +++ b/src/main/software/amazon/event/ruler/AnythingButValuesSet.java @@ -5,16 +5,16 @@ import java.util.Set; /** - * Represents denylist like rule: any value matches if it's *not* in the anything-but/ignore-case list. + * Represents denylist like rule: any value matches if it's *not* in the anything-but set. * It supports lists whose members must be all strings. - * Matching is case-insensitive + * This can be used for anything-but-equals-ignore-case, anything-but-prefix, and anything-but-suffix. */ -public class AnythingButEqualsIgnoreCase extends Patterns { +public class AnythingButValuesSet extends Patterns { private final Set values; - AnythingButEqualsIgnoreCase(final Set values) { - super(MatchType.ANYTHING_BUT_IGNORE_CASE); + AnythingButValuesSet(final MatchType matchType, final Set values) { + super(matchType); this.values = Collections.unmodifiableSet(values); } @@ -34,7 +34,7 @@ public boolean equals(Object o) { return false; } - AnythingButEqualsIgnoreCase that = (AnythingButEqualsIgnoreCase) o; + AnythingButValuesSet that = (AnythingButValuesSet) o; return (Objects.equals(values, that.values)); } @@ -48,6 +48,6 @@ public int hashCode() { @Override public String toString() { - return "ABIC:"+ values + ", (" + super.toString() + ")"; + return "ABVS:"+ values + ", (" + super.toString() + ")"; } } diff --git a/src/main/software/amazon/event/ruler/ByteMachine.java b/src/main/software/amazon/event/ruler/ByteMachine.java index 8df0f28..9de1314 100644 --- a/src/main/software/amazon/event/ruler/ByteMachine.java +++ b/src/main/software/amazon/event/ruler/ByteMachine.java @@ -147,8 +147,10 @@ void deletePattern(final Patterns pattern) { deleteAnythingButPattern((AnythingBut) pattern); break; case ANYTHING_BUT_IGNORE_CASE: - assert pattern instanceof AnythingButEqualsIgnoreCase; - deleteAnythingButEqualsIgnoreCasePattern((AnythingButEqualsIgnoreCase) pattern); + case ANYTHING_BUT_PREFIX: + case ANYTHING_BUT_SUFFIX: + assert pattern instanceof AnythingButValuesSet; + deleteAnythingButValuesSetPattern((AnythingButValuesSet) pattern); break; case EXACT: case NUMERIC_EQ: @@ -156,8 +158,6 @@ void deletePattern(final Patterns pattern) { case PREFIX_EQUALS_IGNORE_CASE: case SUFFIX: case SUFFIX_EQUALS_IGNORE_CASE: - case ANYTHING_BUT_PREFIX: - case ANYTHING_BUT_SUFFIX: case EQUALS_IGNORE_CASE: case WILDCARD: assert pattern instanceof ValuePatterns; @@ -181,7 +181,7 @@ private void deleteAnythingButPattern(AnythingBut pattern) { deleteMatchStep(startState, 0, pattern, getParser().parse(pattern.type(), value))); } - private void deleteAnythingButEqualsIgnoreCasePattern(AnythingButEqualsIgnoreCase pattern) { + private void deleteAnythingButValuesSetPattern(AnythingButValuesSet pattern) { pattern.getValues().forEach(value -> deleteMatchStep(startState, 0, pattern, getParser().parse(pattern.type(), value))); } @@ -687,11 +687,11 @@ NameState addPattern(final Patterns pattern, final NameState nameState) { assert pattern instanceof AnythingBut; return addAnythingButPattern((AnythingBut) pattern, nameState); case ANYTHING_BUT_IGNORE_CASE: - assert pattern instanceof AnythingButEqualsIgnoreCase; - return addAnythingButEqualsIgnoreCasePattern((AnythingButEqualsIgnoreCase) pattern, nameState); - case ANYTHING_BUT_SUFFIX: case ANYTHING_BUT_PREFIX: + assert pattern instanceof AnythingButValuesSet; + return addAnythingButValuesSetPattern((AnythingButValuesSet) pattern, nameState); + case EXACT: case NUMERIC_EQ: case PREFIX: @@ -731,7 +731,7 @@ private NameState addAnythingButPattern(AnythingBut pattern, NameState nameState return nameStateToBeReturned; } - private NameState addAnythingButEqualsIgnoreCasePattern(AnythingButEqualsIgnoreCase pattern, NameState nameState) { + private NameState addAnythingButValuesSetPattern(AnythingButValuesSet pattern, NameState nameState) { NameState nameStateToBeReturned = nameState; NameState nameStateChecker = null; @@ -937,16 +937,16 @@ NameState findPattern(final Patterns pattern) { assert pattern instanceof AnythingBut; return findAnythingButPattern((AnythingBut) pattern); case ANYTHING_BUT_IGNORE_CASE: - assert pattern instanceof AnythingButEqualsIgnoreCase; - return findAnythingButEqualsIgnoreCasePattern((AnythingButEqualsIgnoreCase) pattern); + case ANYTHING_BUT_SUFFIX: + case ANYTHING_BUT_PREFIX: + assert pattern instanceof AnythingButValuesSet; + return findAnythingButValuesSetPattern((AnythingButValuesSet) pattern); case EXACT: case NUMERIC_EQ: case PREFIX: case PREFIX_EQUALS_IGNORE_CASE: case SUFFIX: case SUFFIX_EQUALS_IGNORE_CASE: - case ANYTHING_BUT_SUFFIX: - case ANYTHING_BUT_PREFIX: case EQUALS_IGNORE_CASE: case WILDCARD: assert pattern instanceof ValuePatterns; @@ -974,7 +974,7 @@ private NameState findAnythingButPattern(AnythingBut pattern) { return null; } - private NameState findAnythingButEqualsIgnoreCasePattern(AnythingButEqualsIgnoreCase pattern) { + private NameState findAnythingButValuesSetPattern(AnythingButValuesSet pattern) { Set nextNameStates = new HashSet<>(pattern.getValues().size()); for (String value : pattern.getValues()) { diff --git a/src/main/software/amazon/event/ruler/JsonRuleCompiler.java b/src/main/software/amazon/event/ruler/JsonRuleCompiler.java index 1e97048..05ed3d6 100644 --- a/src/main/software/amazon/event/ruler/JsonRuleCompiler.java +++ b/src/main/software/amazon/event/ruler/JsonRuleCompiler.java @@ -407,7 +407,7 @@ private static Patterns processMatchExpression(final JsonParser parser) throws I return range; } else if (Constants.ANYTHING_BUT_MATCH.equals(matchTypeName)) { - boolean isIgnoreCase = false; + MatchType matchType = MatchType.ANYTHING_BUT; JsonToken anythingButExpressionToken = parser.nextToken(); if (anythingButExpressionToken == JsonToken.START_OBJECT) { @@ -417,36 +417,22 @@ private static Patterns processMatchExpression(final JsonParser parser) throws I barf(parser, "Anything-But expression name not found"); } final String anythingButObjectOp = parser.getCurrentName(); - final boolean isPrefix = Constants.PREFIX_MATCH.equals(anythingButObjectOp); - final boolean isSuffix = Constants.SUFFIX_MATCH.equals(anythingButObjectOp); - isIgnoreCase = Constants.EQUALS_IGNORE_CASE.equals(anythingButObjectOp); - if(!isIgnoreCase) { - if (!isPrefix && !isSuffix) { + switch (anythingButObjectOp) { + case Constants.EQUALS_IGNORE_CASE: + matchType = MatchType.ANYTHING_BUT_IGNORE_CASE; + break; + case Constants.PREFIX_MATCH: + matchType = MatchType.ANYTHING_BUT_PREFIX; + break; + case Constants.SUFFIX_MATCH: + matchType = MatchType.ANYTHING_BUT_SUFFIX; + break; + default: barf(parser, "Unsupported anything-but pattern: " + anythingButObjectOp); - } - final JsonToken anythingButParamType = parser.nextToken(); - if (anythingButParamType != JsonToken.VALUE_STRING) { - barf(parser, "prefix/suffix match pattern must be a string"); - } - final String text = parser.getText(); - if (text.isEmpty()) { - barf(parser, "Null prefix/suffix not allowed"); - } - if (parser.nextToken() != JsonToken.END_OBJECT) { - barf(parser, "Only one key allowed in match expression"); - } - if (parser.nextToken() != JsonToken.END_OBJECT) { - barf(parser, "Only one key allowed in match expression"); - } - if(isPrefix) { - return Patterns.anythingButPrefix('"' + text); // note no trailing quote - } else { - return Patterns.anythingButSuffix(text + '"'); // note no leading quote - } - } else { - // Step into anything-but's equals-ignore-case - anythingButExpressionToken = parser.nextToken(); } + + // Step into anything-but's equals-ignore-case/prefix/suffix + anythingButExpressionToken = parser.nextToken(); } if (anythingButExpressionToken != JsonToken.START_ARRAY && @@ -459,24 +445,24 @@ private static Patterns processMatchExpression(final JsonParser parser) throws I Patterns anythingBut; if (anythingButExpressionToken == JsonToken.START_ARRAY) { - if(isIgnoreCase) { - anythingBut = processAnythingButEqualsIgnoreCaseListMatchExpression(parser); - } else { - anythingBut = processAnythingButListMatchExpression(parser); - } + if (matchType == MatchType.ANYTHING_BUT) { + anythingBut = processAnythingButListMatchExpression(parser); + } else { + anythingBut = processAnythingButValuesSetMatchExpression(parser, matchType); + } } else { - if(isIgnoreCase) { - anythingBut = processAnythingButEqualsIgnoreCaseMatchExpression(parser, anythingButExpressionToken); - } else { - anythingBut = processAnythingButMatchExpression(parser, anythingButExpressionToken); - } + if (matchType == MatchType.ANYTHING_BUT) { + anythingBut = processAnythingButMatchExpression(parser, anythingButExpressionToken); + } else { + anythingBut = processAnythingButValuesSetSingleValueMatchExpression(parser, anythingButExpressionToken, matchType); + } } if (parser.nextToken() != JsonToken.END_OBJECT) { tooManyElements(parser); } - // Complete the object closure for equals-ignore-case - if (isIgnoreCase && parser.nextToken() != JsonToken.END_OBJECT) { + // Complete the object closure when a set is present + if (matchType != MatchType.ANYTHING_BUT && parser.nextToken() != JsonToken.END_OBJECT) { tooManyElements(parser); } @@ -608,25 +594,39 @@ private static Patterns processAnythingButListMatchExpression(JsonParser parser) return AnythingBut.anythingButMatch(values, hasNumber); } - private static Patterns processAnythingButEqualsIgnoreCaseListMatchExpression(JsonParser parser) throws JsonParseException { + private static Patterns processAnythingButValuesSetMatchExpression(JsonParser parser, MatchType matchType) + throws JsonParseException { JsonToken token; Set values = new HashSet<>(); - boolean hasNumber = false; try { while ((token = parser.nextToken()) != JsonToken.END_ARRAY) { switch (token) { case VALUE_STRING: - values.add('"' + parser.getText() + '"'); + String text = parser.getText(); + if (matchType != MatchType.ANYTHING_BUT_IGNORE_CASE && text.isEmpty()) { + barf(parser, "Null prefix/suffix not allowed"); + } + values.add(generateValueBasedOnMatchType(text, matchType)); break; default: - barf(parser, "Inside anything-but/equals-ignore-case list, number|start|null|boolean is not supported."); + if (matchType == MatchType.ANYTHING_BUT_IGNORE_CASE) { + barf(parser, "Inside anything-but/equals-ignore-case list, number|start|null|boolean is not supported."); + } else { + barf(parser, "prefix/suffix match pattern must be a string"); + } } } } catch (IllegalArgumentException | IOException e) { barf(parser, e.getMessage()); } - return AnythingButEqualsIgnoreCase.anythingButIgnoreCaseMatch(values); + switch (matchType) { + case ANYTHING_BUT_IGNORE_CASE: return Patterns.anythingButIgnoreCaseMatch(values); + case ANYTHING_BUT_PREFIX: return Patterns.anythingButPrefix(values); + case ANYTHING_BUT_SUFFIX: return Patterns.anythingButSuffix(values); + // Not barfing as this is a code bug rather than bad JSON. + default: throw new IllegalArgumentException("processAnythingButValuesSetMatchExpression received invalid matchType of " + matchType); + } } private static Patterns processAnythingButMatchExpression(JsonParser parser, @@ -648,17 +648,47 @@ private static Patterns processAnythingButMatchExpression(JsonParser parser, return AnythingBut.anythingButMatch(values, hasNumber); } - private static Patterns processAnythingButEqualsIgnoreCaseMatchExpression(JsonParser parser, - JsonToken anythingButExpressionToken) throws IOException { + private static Patterns processAnythingButValuesSetSingleValueMatchExpression(JsonParser parser, + JsonToken anythingButExpressionToken, MatchType matchType) throws IOException { Set values = new HashSet<>(); switch (anythingButExpressionToken) { case VALUE_STRING: - values.add('"' + parser.getText() + '"'); + String text = parser.getText(); + if (matchType != MatchType.ANYTHING_BUT_IGNORE_CASE && text.isEmpty()) { + barf(parser, "Null prefix/suffix not allowed"); + } + values.add(generateValueBasedOnMatchType(text, matchType)); break; default: - barf(parser, "Inside anything-but/equals-ignore-case list, number|start|null|boolean is not supported."); + if (matchType == MatchType.ANYTHING_BUT_IGNORE_CASE) { + barf(parser, "Inside anything-but/equals-ignore-case list, number|start|null|boolean is not supported."); + } else { + barf(parser, "prefix/suffix match pattern must be a string"); + } + } + + switch (matchType) { + case ANYTHING_BUT_IGNORE_CASE: return Patterns.anythingButIgnoreCaseMatch(values); + case ANYTHING_BUT_PREFIX: return Patterns.anythingButPrefix(values); + case ANYTHING_BUT_SUFFIX: return Patterns.anythingButSuffix(values); + // Not barfing as this is a code bug rather than bad JSON. + default: throw new IllegalArgumentException("processAnythingButValuesSetSingleValueMatchExpression received invalid matchType of " + matchType); + } + } + + private static String generateValueBasedOnMatchType(String text, MatchType matchType) { + switch (matchType) { + case PREFIX: + case PREFIX_EQUALS_IGNORE_CASE: + case ANYTHING_BUT_PREFIX: + return '"' + text; + case SUFFIX: + case SUFFIX_EQUALS_IGNORE_CASE: + case ANYTHING_BUT_SUFFIX: + return text + '"'; + default: + return '"' + text + '"'; } - return AnythingButEqualsIgnoreCase.anythingButIgnoreCaseMatch(values); } private static Patterns processNumericMatchExpression(final JsonParser parser) throws IOException { diff --git a/src/main/software/amazon/event/ruler/Patterns.java b/src/main/software/amazon/event/ruler/Patterns.java index 42d46b3..5421b8e 100644 --- a/src/main/software/amazon/event/ruler/Patterns.java +++ b/src/main/software/amazon/event/ruler/Patterns.java @@ -3,6 +3,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; +import java.util.stream.Collectors; /** * The Patterns deal pre-processing of rules for the eventual matching against events. @@ -69,12 +70,12 @@ public static AnythingBut anythingButMatch(final Set anythingButs) { return new AnythingBut(anythingButs, false); } - public static AnythingButEqualsIgnoreCase anythingButIgnoreCaseMatch(final String anythingBut) { - return new AnythingButEqualsIgnoreCase(Collections.singleton(anythingBut)); + public static AnythingButValuesSet anythingButIgnoreCaseMatch(final String anythingBut) { + return new AnythingButValuesSet(MatchType.ANYTHING_BUT_IGNORE_CASE, Collections.singleton(anythingBut)); } - public static AnythingButEqualsIgnoreCase anythingButIgnoreCaseMatch(final Set anythingButs) { - return new AnythingButEqualsIgnoreCase(anythingButs); + public static AnythingButValuesSet anythingButIgnoreCaseMatch(final Set anythingButs) { + return new AnythingButValuesSet(MatchType.ANYTHING_BUT_IGNORE_CASE, anythingButs); } public static AnythingBut anythingButNumberMatch(final Set anythingButs) { @@ -85,12 +86,22 @@ public static AnythingBut anythingButNumberMatch(final Set anythingButs) return new AnythingBut(normalizedNumbers, true); } - public static ValuePatterns anythingButPrefix(final String prefix) { - return new ValuePatterns(MatchType.ANYTHING_BUT_PREFIX, prefix); + public static AnythingButValuesSet anythingButPrefix(final String prefix) { + return new AnythingButValuesSet(MatchType.ANYTHING_BUT_PREFIX, Collections.singleton(prefix)); } - public static ValuePatterns anythingButSuffix(final String suffix) { - return new ValuePatterns(MatchType.ANYTHING_BUT_SUFFIX, new StringBuilder(suffix).reverse().toString()); + public static AnythingButValuesSet anythingButPrefix(final Set anythingButs) { + return new AnythingButValuesSet(MatchType.ANYTHING_BUT_PREFIX, anythingButs); + } + + public static AnythingButValuesSet anythingButSuffix(final String suffix) { + return new AnythingButValuesSet(MatchType.ANYTHING_BUT_SUFFIX, + Collections.singleton(new StringBuilder(suffix).reverse().toString())); + } + + public static AnythingButValuesSet anythingButSuffix(final Set anythingButs) { + return new AnythingButValuesSet(MatchType.ANYTHING_BUT_SUFFIX, + anythingButs.stream().map(s -> new StringBuilder(s).reverse().toString()).collect(Collectors.toSet())); } public static ValuePatterns numericEquals(final double val) { diff --git a/src/main/software/amazon/event/ruler/RuleCompiler.java b/src/main/software/amazon/event/ruler/RuleCompiler.java index 742a97b..809f6c5 100644 --- a/src/main/software/amazon/event/ruler/RuleCompiler.java +++ b/src/main/software/amazon/event/ruler/RuleCompiler.java @@ -524,7 +524,7 @@ private static Patterns processAnythingButEqualsIgnoreCaseListMatchExpression(Js barf(parser, e.getMessage()); } - return AnythingButEqualsIgnoreCase.anythingButIgnoreCaseMatch(values); + return AnythingButValuesSet.anythingButIgnoreCaseMatch(values); } @@ -557,7 +557,7 @@ private static Patterns processAnythingButEqualsIgnoreCaseMatchExpression(JsonPa default: barf(parser, "Inside anything-but/ignore-case list, number|start|null|boolean is not supported."); } - return AnythingButEqualsIgnoreCase.anythingButIgnoreCaseMatch(values); + return AnythingButValuesSet.anythingButIgnoreCaseMatch(values); } private static Patterns processNumericMatchExpression(final JsonParser parser) throws IOException { diff --git a/src/main/software/amazon/event/ruler/Ruler.java b/src/main/software/amazon/event/ruler/Ruler.java index c5fa9eb..49e1f49 100644 --- a/src/main/software/amazon/event/ruler/Ruler.java +++ b/src/main/software/amazon/event/ruler/Ruler.java @@ -137,19 +137,27 @@ private static boolean matches(final JsonNode val, final Patterns pattern) { } return false; case ANYTHING_BUT_IGNORE_CASE: - assert (pattern instanceof AnythingButEqualsIgnoreCase); - AnythingButEqualsIgnoreCase anythingButIgnoreCasePattern = (AnythingButEqualsIgnoreCase) pattern; + assert (pattern instanceof AnythingButValuesSet); + AnythingButValuesSet anythingButIgnoreCasePattern = (AnythingButValuesSet) pattern; if (val.isTextual()) { return anythingButIgnoreCasePattern.getValues().stream().noneMatch(v -> v.equalsIgnoreCase('"' + val.asText() + '"')); } return false; case ANYTHING_BUT_SUFFIX: - valuePattern = (ValuePatterns) pattern; - return !(val.isTextual() && (val.asText() + '"').startsWith(valuePattern.pattern())); + AnythingButValuesSet anythingButSuffixPattern = (AnythingButValuesSet) pattern; + String valueForSuffix = val.asText() + '"'; + if (val.isTextual()) { + return anythingButSuffixPattern.getValues().stream().noneMatch(v -> valueForSuffix.startsWith(v)); + } + return false; case ANYTHING_BUT_PREFIX: - valuePattern = (ValuePatterns) pattern; - return !(val.isTextual() && ('"' + val.asText()).startsWith(valuePattern.pattern())); + AnythingButValuesSet anythingButPrefixPattern = (AnythingButValuesSet) pattern; + String valueForPrefix = '"' + val.asText() + '"'; + if (val.isTextual()) { + return anythingButPrefixPattern.getValues().stream().noneMatch(v -> valueForPrefix.startsWith(v)); + } + return false; case NUMERIC_EQ: valuePattern = (ValuePatterns) pattern; diff --git a/src/test/software/amazon/event/ruler/ACMachineTest.java b/src/test/software/amazon/event/ruler/ACMachineTest.java index e599b64..450f743 100644 --- a/src/test/software/amazon/event/ruler/ACMachineTest.java +++ b/src/test/software/amazon/event/ruler/ACMachineTest.java @@ -1434,8 +1434,76 @@ public void testAnythingButDeletion() throws Exception { } @Test - public void testAnythingButSuffix() throws Exception { + public void testAnythingButPrefix() throws Exception { + String rule = "{\n" + + "\"a\": [ { \"anything-but\": {\"prefix\": \"$\"} } ]\n" + + "}"; + + Machine machine = new Machine(); + machine.addRule("r1", rule); + + String event1 = "{" + + " \"a\": \"value$\"\n" + + "}\n"; + + String event2 = "{" + + " \"a\": \"notvalue\"\n" + + "}\n"; + + String event3 = "{" + + " \"a\": \"$notvalue\"\n" + + "}\n"; + + assertEquals(1, machine.rulesForJSONEvent(event1).size()); + assertEquals(1, machine.rulesForJSONEvent(event2).size()); + assertEquals(0, machine.rulesForJSONEvent(event3).size()); + + machine.deleteRule("r1", rule); + + assertTrue(machine.isEmpty()); + assertEquals(0, machine.rulesForJSONEvent(event2).size()); + assertEquals(0, machine.rulesForJSONEvent(event3).size()); + } + + @Test + public void testAnythingButPrefixSet() throws Exception { + String rule = "{\n" + + "\"a\": [ { \"anything-but\": {\"prefix\": [\"$\", \"%\"] } } ]\n" + + "}"; + + Machine machine = new Machine(); + machine.addRule("r1", rule); + + String event1 = "{" + + " \"a\": \"value$\"\n" + + "}\n"; + + String event2 = "{" + + " \"a\": \"notvalue%\"\n" + + "}\n"; + + String event3 = "{" + + " \"a\": \"$notvalue\"\n" + + "}\n"; + + String event4 = "{" + + " \"a\": \"%notvalue\"\n" + + "}\n"; + + assertEquals(1, machine.rulesForJSONEvent(event1).size()); + assertEquals(1, machine.rulesForJSONEvent(event2).size()); + assertEquals(0, machine.rulesForJSONEvent(event3).size()); + assertEquals(0, machine.rulesForJSONEvent(event4).size()); + machine.deleteRule("r1", rule); + + assertTrue(machine.isEmpty()); + assertEquals(0, machine.rulesForJSONEvent(event3).size()); + assertEquals(0, machine.rulesForJSONEvent(event4).size()); + } + + @Test + public void testAnythingButSuffix() throws Exception { String rule = "{\n" + "\"a\": [ { \"anything-but\": {\"suffix\": \"$\"} } ]\n" + "}"; @@ -1459,6 +1527,48 @@ public void testAnythingButSuffix() throws Exception { assertEquals(1, machine.rulesForJSONEvent(event2).size()); assertEquals(1, machine.rulesForJSONEvent(event3).size()); + machine.deleteRule("r1", rule); + + assertTrue(machine.isEmpty()); + assertEquals(0, machine.rulesForJSONEvent(event2).size()); + assertEquals(0, machine.rulesForJSONEvent(event3).size()); + } + + @Test + public void testAnythingButSuffixSet() throws Exception { + String rule = "{\n" + + "\"a\": [ { \"anything-but\": {\"suffix\": [\"$\", \"%\"] } } ]\n" + + "}"; + + Machine machine = new Machine(); + machine.addRule("r1", rule); + + String event1 = "{" + + " \"a\": \"value$\"\n" + + "}\n"; + + String event2 = "{" + + " \"a\": \"notvalue%\"\n" + + "}\n"; + + String event3 = "{" + + " \"a\": \"$notvalue\"\n" + + "}\n"; + + String event4 = "{" + + " \"a\": \"%notvalue\"\n" + + "}\n"; + + assertEquals(0, machine.rulesForJSONEvent(event1).size()); + assertEquals(0, machine.rulesForJSONEvent(event2).size()); + assertEquals(1, machine.rulesForJSONEvent(event3).size()); + assertEquals(1, machine.rulesForJSONEvent(event4).size()); + + machine.deleteRule("r1", rule); + + assertTrue(machine.isEmpty()); + assertEquals(0, machine.rulesForJSONEvent(event3).size()); + assertEquals(0, machine.rulesForJSONEvent(event4).size()); } @Test @@ -1527,6 +1637,10 @@ public void testAnythingButEqualsIgnoreCase() throws Exception { assertEquals(0, machine.rulesForJSONEvent(event8).size()); assertEquals(0, machine.rulesForJSONEvent(event9).size()); + machine.deleteRule("r1", rule); + + assertTrue(machine.isEmpty()); + assertEquals(0, machine.rulesForJSONEvent(event1).size()); } @Test diff --git a/src/test/software/amazon/event/ruler/JsonRuleCompilerTest.java b/src/test/software/amazon/event/ruler/JsonRuleCompilerTest.java index 22e55f9..744d16f 100644 --- a/src/test/software/amazon/event/ruler/JsonRuleCompilerTest.java +++ b/src/test/software/amazon/event/ruler/JsonRuleCompilerTest.java @@ -129,15 +129,26 @@ public void testCompile() throws Exception { j = "{\"a\": [ { \"anything-but\": { \"prefix\": \"foo\" } } ] }"; assertNull("Good anything-but should parse", JsonRuleCompiler.check(j)); + j = "{\"a\": [ { \"anything-but\": { \"prefix\": [\"abc\", \"123\"] } } ] }"; + assertNull("Good anything-but should parse", JsonRuleCompiler.check(j)); + j = "{\"a\": [ { \"anything-but\": { \"suffix\": \"foo\" } } ] }"; assertNull("Good anything-but should parse", JsonRuleCompiler.check(j)); + j = "{\"a\": [ { \"anything-but\": { \"suffix\": [\"abc\", \"123\"] } } ] }"; + assertNull("Good anything-but should parse", JsonRuleCompiler.check(j)); + j = "{\"a\": [ { \"anything-but\": {\"equals-ignore-case\": \"rule\" } } ] }"; assertNull("Good anything-but/ignore-case should parse", JsonRuleCompiler.check(j)); + j = "{\"a\": [ { \"anything-but\": {\"equals-ignore-case\": \"\" } } ] }"; + assertNull("Good anything-but/ignore-case should parse", JsonRuleCompiler.check(j)); + j = "{\"a\": [ { \"anything-but\": {\"equals-ignore-case\": [\"abc\", \"123\"] } } ] }"; assertNull("Good anything-but/ignore-case should parse", JsonRuleCompiler.check(j)); + j = "{\"a\": [ { \"anything-but\": {\"equals-ignore-case\": [\"abc\", \"\"] } } ] }"; + assertNull("Good anything-but/ignore-case should parse", JsonRuleCompiler.check(j)); j = "{\"a\": [ { \"exactly\": \"child\" } ] }"; assertNull("Good exact-match should parse", JsonRuleCompiler.check(j)); @@ -177,12 +188,24 @@ public void testCompile() throws Exception { "{\"a\": [ { \"anything-but\": { \"prefix\": \"\" } } ] }", "{\"a\": [ { \"anything-but\": { \"prefix\": \"foo\", \"a\":1 } } ] }", "{\"a\": [ { \"anything-but\": { \"prefix\": \"foo\" }, \"x\": 1 } ] }", + "{\"a\": [ { \"anything-but\": { \"prefix\": [\"1\", \"2\" \"3\"] } } ] }", // missing , + "{\"a\": [ { \"anything-but\": { \"prefix\": [\"1\", \"\"] } } ] }", // no empty string + "{\"a\": [ { \"anything-but\": { \"prefix\": [1, 2, 3] } } ] }", // no numbers + "{\"a\": [ { \"anything-but\": { \"prefix\": [\"1\", \"2\" } } ] }", // missing ] + "{\"a\": [ { \"anything-but\": { \"prefix\": [\"1\", \"2\" ] } ] }", // missing } "{\"a\": [ { \"anything-but\": { \"suffix\": 27 } } ] }", "{\"a\": [ { \"anything-but\": { \"suffix\": \"\" } } ] }", "{\"a\": [ { \"anything-but\": { \"suffix\": \"foo\", \"a\":1 } } ] }", "{\"a\": [ { \"anything-but\": { \"suffix\": \"foo\" }, \"x\": 1 } ] }", - "{\"a\": [ { \"anything-but\" : { \"equals-ignore-case\": [1, 2 3] } } ] }", - "{\"a\": [ { \"anything-but\": {\"equals-ignore-case\": [1, 2, 3] } } ] }", // no numbers + "{\"a\": [ { \"anything-but\": { \"suffix\": [\"1\", \"2\" \"3\"] } } ] }", // missing , + "{\"a\": [ { \"anything-but\": { \"suffix\": [\"1\", \"\"] } } ] }", // no empty string + "{\"a\": [ { \"anything-but\": { \"suffix\": [1, 2, 3] } } ] }", // no numbers + "{\"a\": [ { \"anything-but\": { \"suffix\": [\"1\", \"2\" } } ] }", // missing ] + "{\"a\": [ { \"anything-but\": { \"suffix\": [\"1\", \"2\" ] } ] }", // missing } + "{\"a\": [ { \"anything-but\": { \"equals-ignore-case\": [\"1\", \"2\" \"3\"] } } ] }", // missing , + "{\"a\": [ { \"anything-but\": { \"equals-ignore-case\": [1, 2, 3] } } ] }", // no numbers + "{\"a\": [ { \"anything-but\": { \"equals-ignore-case\": [\"1\", \"2\" } } ] }", // missing ] + "{\"a\": [ { \"anything-but\": { \"equals-ignore-case\": [\"1\", \"2\" ] } ] }", // missing } "{\"a\": [ { \"equals-ignore-case\": 5 } ] }", "{\"a\": [ { \"equals-ignore-case\": [ \"abc\" ] } ] }", "{\"a\": [ { \"prefix\": { \"invalid-expression\": [ \"abc\" ] } } ] }",