[ 
https://issues.apache.org/jira/browse/METRON-1277?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16223580#comment-16223580
 ] 

ASF GitHub Bot commented on METRON-1277:
----------------------------------------

Github user jjmeyer0 commented on a diff in the pull request:

    https://github.com/apache/metron/pull/814#discussion_r147555077
  
    --- Diff: 
metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/MatchTest.java
 ---
    @@ -0,0 +1,202 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one
    + * or more contributor license agreements.  See the NOTICE file
    + * distributed with this work for additional information
    + * regarding copyright ownership.  The ASF licenses this file
    + * to you under the Apache License, Version 2.0 (the
    + * "License"); you may not use this file except in compliance
    + * with the License.  You may obtain a copy of the License at
    + *
    + *     http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.metron.stellar.dsl.functions;
    +
    +import static 
org.apache.metron.stellar.common.utils.StellarProcessorUtils.run;
    +import static 
org.apache.metron.stellar.common.utils.StellarProcessorUtils.runPredicate;
    +
    +import com.google.common.collect.ImmutableMap;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import org.apache.metron.stellar.dsl.DefaultVariableResolver;
    +import org.apache.metron.stellar.dsl.ParseException;
    +import org.junit.Assert;
    +import org.junit.Ignore;
    +import org.junit.Test;
    +
    +public class MatchTest {
    +
    +  @Test
    +  @SuppressWarnings("unchecked")
    +  public void testMatchLambda() {
    +    Assert.assertTrue(runPredicate("match { 1 >= 0 : ()-> true }", new 
HashMap() {{
    +      put("foo", 0);
    +    }}));
    +    Assert.assertTrue(
    +        runPredicate("match { foo == 0 : ()-> true, default : ()-> false 
}", new HashMap() {{
    +          put("foo", 0);
    +        }}));
    +
    +    Assert.assertFalse(
    +        runPredicate("match { foo == 0 : ()-> true, default : ()-> false 
}", new HashMap() {{
    +          put("foo", 1);
    +        }}));
    +
    +    Assert.assertTrue(
    +        runPredicate("match { foo == 0 : ()-> false, foo == 1 : ()-> true, 
default : ()-> false }",
    +            new HashMap() {{
    +              put("foo", 1);
    +            }}));
    +
    +    Assert.assertTrue(runPredicate(
    +        "match { foo == 0 : ()-> bFalse, foo == 1 : ()-> bTrue, default : 
()-> bFalse }",
    +        new HashMap() {{
    +          put("foo", 1);
    +          put("bFalse", false);
    +          put("bTrue", true);
    +        }}));
    +
    +    Assert.assertTrue(runPredicate(
    +        "match { foo == 0 : ()-> bFalse, foo == 1 : ()-> bTrue, default : 
()-> bFalse }",
    +        new HashMap() {{
    +          put("foo", 1);
    +          put("bFalse", false);
    +          put("bTrue", true);
    +        }}));
    +
    +  }
    +
    +  @Test
    +  @SuppressWarnings("unchecked")
    +  @Ignore
    +  public void testMatchMAPEvaluation() {
    +
    +    // NOTE: THIS IS BROKEN RIGHT NOW.
    +
    +    String expr = "match{ var1 :  MAP(['foo', 'bar'], (x) -> TO_UPPER(x)) 
}";
    +
    +    Object o = run(expr, ImmutableMap.of("foo", "foo", "bar", "bar", 
"var1", true));
    +
    +    Assert.assertTrue(o instanceof List);
    +
    +    List<String> result = (List<String>) o;
    +
    +    Assert.assertEquals(2, result.size());
    +    Assert.assertEquals("FOO", result.get(0));
    +    Assert.assertEquals("BAR", result.get(1));
    +}
    +
    +  @Test
    +  @SuppressWarnings("unchecked")
    +  public void testMatchRegexMatch() {
    +    final Map<String, String> variableMap = new HashMap<String, String>() 
{{
    +      put("numbers", "12345");
    +      put("numberPattern", "\\d(\\d)(\\d).*");
    +      put("letters", "abcde");
    +      put("empty", "");
    +    }};
    +
    +    Assert.assertTrue(runPredicate("match{ 
REGEXP_MATCH(numbers,numberPattern): true, default : false}", new 
DefaultVariableResolver(v -> variableMap.get(v),v -> 
variableMap.containsKey(v))));
    +    Assert.assertFalse(runPredicate("match{ 
REGEXP_MATCH(letters,numberPattern) : true, default :false}", new 
DefaultVariableResolver(v -> variableMap.get(v),v -> 
variableMap.containsKey(v))));
    +  }
    +
    +  @Test
    +  @SuppressWarnings("unchecked")
    +  public void testMatchBareStatements() {
    +
    +    Assert.assertTrue(runPredicate("match { foo == 0 : bFalse, foo == 1 : 
bTrue, default : false }",
    +        new HashMap() {{
    +          put("foo", 1);
    +          put("bFalse", false);
    +          put("bTrue", true);
    +        }}));
    +
    +    Assert.assertEquals("warning",
    +        run("match{ threat.triage.level < 10 : 'info', threat.triage.level 
< 20 : 'warning', default : 'critical' }",
    +            new HashMap() {{
    +              put("threat.triage.level", 15);
    +            }}));
    +  }
    +
    +  @Test
    +  @SuppressWarnings("unchecked")
    +  public void testWithFunction() {
    +    Assert.assertEquals("WARNING",
    +        run("match{ threat.triage.level < 10 : 'info', threat.triage.level 
< 20 : TO_UPPER('warning'), default : 'critical' }",
    +            new HashMap() {{
    +              put("threat.triage.level", 15);
    +            }}));
    +  }
    +
    +  @Test
    +  @SuppressWarnings("unchecked")
    +  public void testWithFunctionMultiArgs() {
    +    Assert.assertEquals("false",
    +        run("match{ threat.triage.level < 10 : 'info', threat.triage.level 
< 20 : TO_STRING(IS_ENCODING(other,'BASE32')), default : 'critical' }",
    +            new HashMap() {{
    +              put("threat.triage.level", 15);
    +              put("other", "value");
    +            }}));
    +
    +    Assert.assertEquals(false,
    +        run("match{ threat.triage.level < 10 : 'info', threat.triage.level 
< 20 : IS_ENCODING(other,'BASE32'), default : 'critical' }",
    +            new HashMap() {{
    +              put("threat.triage.level", 15);
    +              put("other", "value");
    +            }}));
    +  }
    +
    +  @Test
    +  @SuppressWarnings("unchecked")
    +  public void testLogical() {
    +
    +    Assert.assertTrue(
    +        runPredicate("match { foo == 0  OR bar == 'yes' : ()-> true, 
default : ()-> false }",
    +            new HashMap() {{
    +              put("foo", 1);
    +              put("bar", "yes");
    +            }}));
    +
    +    Assert.assertTrue(
    +        runPredicate("match { foo == 0  AND bar == 'yes' : ()-> true, 
default : ()-> false }",
    +            new HashMap() {{
    +              put("foo", 0);
    +              put("bar", "yes");
    +            }}));
    +  }
    +
    +  @Test
    +  @SuppressWarnings("unchecked")
    +  public void testMatchErrorNoDefault() {
    +
    +    boolean caught = false;
    --- End diff --
    
    We shouldn't use a boolean flag to determine if an exception was thrown. If 
you just want to expect an exception we should use `@Test(expected = 
ParseException.class)`. For more indepth testing we should use `@Rule public 
ExpectedException thrown = ExpectedException.none();`.


> STELLAR Add Match functionality to language
> -------------------------------------------
>
>                 Key: METRON-1277
>                 URL: https://issues.apache.org/jira/browse/METRON-1277
>             Project: Metron
>          Issue Type: New Feature
>            Reporter: Otto Fowler
>            Assignee: Otto Fowler
>
> From dev list:
> ------------
> Hi All, 
> It's high time that Stellar supports some form of conditional that is 
> beyond if/then/else. Right now, the way to do fall-through conditionals is: 
> if x < 10 then 'info' else if x >= 10 && x <= 20 then 'warn' else 'critical' 
> That becomes non-scalable very quickly. I wanted to facilitate a 
> discussion with the community on the syntax. I'll give a few options and 
> you guys/gals can come up with your own suggestions too, but I wanted to 
> frame teh conversation. 
> *MAP-BASED SWITCH* 
> With the advent of METRON-1254 (https://github.com/apache/metron/pull/801), 
> we could enable (from a language perspective in Stellar) multi-part 
> conditionals or switch/case style statements. To wit: 
> MAP_GET(true, { x < 10 : 'info', x >= 10 && x <= 20 : 'warn', x > 20 : 
> 'critical' }) 
> Or, with a convenience function: 
> CASE( { x < 10 : 'info', x >= 10 && x <= 20 : 'warn', x > 20 : 'critical' } 
> ) 
> The issue with this is that the last true condition wins because we're 
> using a map. 
> *LIST-BASED SWITCH* 
> We could correct this by adding a list of pairs construction to stellar: 
> CASE( [ x < 10 : 'info', x <= 20 : 'warn'], 'critical') 
> This would enable us to allow the first true condition to win, so the 
> second condition can be simpler and we could pass a default return value as 
> the final argument. 
> The downside to this, is that it requires a language enhancement (the list 
> of pairs construction you see there). 
> *LAMBDA FUNCTION-BASED SWITCH* 
> Some of the problems with the previous statements are that every 
> conditional has to be evaluated and there is no opportunity to short 
> circuit. They're all evaluated at parse-time rather than execution time. 
> We could, instead, construct a lambda function approach to this and support 
> short-circuiting in even complex conditionals: 
> CASE( real_variable_name, [ x -> x < 10 ? 'info', x -> x <= 20 ? 'warn' ], 
> 'critical') 
> or 
> CASE( real_variable_name, [ x -> if x < 10 then 'info', x -> if x <= 20 
> then 'warn' ], 'critical') 
> This would require lessening ?: (if/then/else) syntax to support to enable 
> just if without else conditions. This also has the benefit of allowing 
> simplifying the expression due to lambda function variable renaming 
> (real_variable_name can be much more complex (or even an expression) than 
> 'x'. 
> Creative other approaches to this are appreciated! 
> Thanks, 
> Casey 
> ----------------
> and ->
>  
> How about this:
> match(VAR_TO_VAL_ASSIGNMENT+) { BOOLEAN_STATEMENT(VALS) : LAMBDA(VALS), 
> BOOLEAN_STATEMENT(VALS) : LAMBDA(VALS) , LAMBDA(VALS)}
> * match = new keyword
> * match takes variable number of assignments, where the val assigned to is 
> available in the evaluation and the lambdas
> * match {} contains comma separated list of a statement that evaluates to a 
> boolean and a lambda
> * LAMBDA is executed on match, and it’s value is returned
> * no matches returns null or return of optional final statement, which is a 
> LAMBDA without a BOOLEAN_STATEMENT



--
This message was sent by Atlassian JIRA
(v6.4.14#64029)

Reply via email to