camunda / feel-scala

FEEL parser and interpreter written in Scala
https://camunda.github.io/feel-scala/
Apache License 2.0
119 stars 46 forks source link

A list property gets overridden by variable within a filter expression #765

Closed saig0 closed 6 months ago

saig0 commented 7 months ago

Describe the bug

A filter expression can access the properties of a list item by the property name. In the following example, the list is filtered by the property loanId. The variable id is provided by the context.

sum({"loans" : [
  {"loanId" : "AAA001", "amount" : 10},
  {"loanId" : "AAA002", "amount" : 20},
  {"loanId" : "AAA001", "amount" : 50}
]}.loans[loanId = id].amount)

Since version 1.16.2, the FEEL engine evaluates the expression differently if the context contains a variable loanId. The property loanId gets overridden by the variable loanId. It seems to be an issue with the variable resolution.

This issue happens only if the context is provided by a custom context (i.e. not a static context). This is the case with the integration in Zeebe.

To Reproduce Use the following test case of the FEEL engine to reproduce the issue:

  "A filter expression" should "access an item property" in {
    evaluateExpression(
      expression =
        """sum({"loans" : [
                           {"loanId" : "AAA001", "amount" : 10},
                           {"loanId" : "AAA002", "amount" : 20},
                           {"loanId" : "AAA001", "amount" : 50}
                         ]}.loans[loanId = id].amount)""",
      context = new MyContext                                 // --> this is failing
      // variables = Map("id" -> "AAA002", "loanId" -> "AAA002") --> this is working
    ) should returnResult(20)
  }

  class MyContext extends CustomContext {

    val vars = Map("id" -> "AAA002", "loanId" -> "AAA002")

    override def variableProvider: VariableProvider = new VariableProvider {
      override def getVariable(name: String): Option[Any] = vars.get(name)

      override def keys: Iterable[String] = vars.keys
    }
  }

Expected behavior In a list filter expression, the properties of a list item are not overridden by the context.

Environment

saig0 commented 7 months ago

Workaround

Via context Avoid exposing variables that have the same name as list properties (i.e. no variable loanId).

Via expression Instead of accessing the list properties directly, use the filter variable item to access the properties. See the following example.

sum({"loans" : [
  {"loanId" : "AAA001", "amount" : 10},
  {"loanId" : "AAA002", "amount" : 20},
  {"loanId" : "AAA001", "amount" : 50}
]}.loans[item.loanId = id].amount)