ownOperator = an operator working rather similar to the ‘list contains’-Operator from FEEL (Can’t use the ‘list operator’ here instead for several reasons)
AnotherList = a parameter (as List) within the OneList
I expect my rule to evaluate to true for this input. And it does. But still FeelEngine gives me a warning saying:
ValError(no variable found for name ‘item’)
I only see this warning because in my ownOperator I have implemented a check for ValErrors.
It looks like my ownOperator is called two times, first time with that ValError, second time without.
With debugging I came up to ScalaFeelEngine where it calls feelEngine.evalUnaryTests(expression, context);, but then I don’t know what exactly happens in FeelEngine and why my ownOperator is called twice.
When I change the rule to count[...] = 2, it evaluates to false with the above given input, as expected, so I assume the evaluation of the expression itself works correctly.
I hope I have explained my problem in an understandable way.
It evaluates to true — just as expected.
How did you implement your own operator? As a FEEL expression or using the Function Provider SPI?
Can you share the implementation?
thanks for your answer.
Unfortunately i was not able to make any progress on this issue on my own so far.
I implemented my operator whitin a CustomFunctionProvider which implements the FeelCustomFunctionProvider.
But I don’t think the implementation of the operator is the problem because its input when we get there the fist time already is: ValError, “XYZ”
The only reason, why I see that ValError at all is because I am checking for ValErrors in the beginning of the operator-implementation and log a warning if there are some.
Also, when we get there the second time without the ValError, my operator also evaluates to true, just as ‘list contains’ is doing.
So still I think something happening in the FeelEngine is the problem causing to give ValError in the first place but then calling my operator a second time without that ValError.
I’m still trying to replicate the error.
Do you use your expression as a literal expression in a DMN? And do you reference this decision via a business rule task?
So far, I tried replicating it using Feel-Scala. Using the following two classes, everything runs without errors:
package org.camunda.forum;
import org.camunda.feel.context.JavaFunction;
import org.camunda.feel.context.JavaFunctionProvider;
import org.camunda.feel.syntaxtree.ValBoolean;
import org.camunda.feel.syntaxtree.ValError;
import org.camunda.feel.syntaxtree.ValList;
import org.camunda.feel.syntaxtree.ValString;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class CustomFunctionProvider extends JavaFunctionProvider {
private static final Map<String, JavaFunction> functions = new HashMap<>();
static {
final JavaFunction function = new JavaFunction(Arrays.asList("l", "v"), args -> {
try {
final ValList list = (ValList) args.get(0);
final ValString value = (ValString) args.get(1);
args.stream().filter(v -> v instanceof ValError).forEach(err -> System.out.println("Error " + err));
return new ValBoolean(list.items().contains(value));
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
return new ValBoolean(false);
});
functions.put("customContains", function);
}
@Override
public Optional<JavaFunction> resolveFunction(String functionName) {
return Optional.ofNullable(functions.get(functionName));
}
@Override
public Collection<String> getFunctionNames() {
return functions.keySet();
}
}
package org.camunda.forum;
import org.camunda.feel.FeelEngine;
import scala.util.Either;
import java.util.Arrays;
import java.util.Map;
public class TestApp {
public static void main(String[] args) {
FeelEngine engine = new FeelEngine.Builder()
.functionProvider(new CustomFunctionProvider())
.build();
final Map<String, Object> variables = Map
.of("MyInput", Map
.of("OneList", Arrays.asList(
Map.of("AnotherList", Arrays.asList("ABC", "XYZ")),
Map.of("AnotherList", Arrays.asList("XYZ"))
)));
Either<FeelEngine.Failure, Object> result = engine.evalExpression(
"(count(MyInput.OneList[customContains(item.AnotherList, \"XYZ\")]) = 2)",
variables);
if (result.isRight()) {
final Object value = result.right().get();
System.out.println("result is " + value);
} else {
final FeelEngine.Failure failure = result.left().get();
throw new RuntimeException(failure.message());
}
}
}
Camunda Platform 7.14.0 uses FEEL in version 1.12.2.
You can change the version but it depends on how you use the Camunda Platform (i.e. embedded, run, etc.). If you embed the engine then it should be possible to change the version by depending on a newer version of the FEEL engine (see the pom).
BTW: you should update your Camunda Platform version
However, I don’t see a Camunda version that uses FEEL 1.15 already.