Skip to content

Commit

Permalink
Merge pull request #838 from camunda/backport-833-to-1.17
Browse files Browse the repository at this point in the history
[Backport 1.17] fix: `string()` function can handle a context with custom value types
  • Loading branch information
mustafadagher authored Apr 24, 2024
2 parents 852e1ad + e8dafb9 commit 012c071
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,17 @@
package org.camunda.feel.impl.builtin

import org.camunda.feel.impl.builtin.BuiltinFunction.builtinFunction
import org.camunda.feel.syntaxtree.{
Val,
ValBoolean,
ValDate,
ValDateTime,
ValDayTimeDuration,
ValError,
ValLocalDateTime,
ValLocalTime,
ValNull,
ValNumber,
ValString,
ValTime,
ValYearMonthDuration,
ZonedTime
}
import org.camunda.feel.syntaxtree._
import org.camunda.feel.valuemapper.ValueMapper
import org.camunda.feel.{
Date,
YearMonthDuration,
dateFormatter,
dateTimeFormatter,
isDayTimeDuration,
isLocalDateTime,
isOffsetDateTime,
isOffsetTime,
isValidDate,
isYearMonthDuration,
localDateTimeFormatter,
localTimeFormatter,
stringToDate,
stringToDateTime,
stringToDayTimeDuration,
Expand All @@ -56,11 +38,10 @@ import org.camunda.feel.{
}

import java.math.BigDecimal
import java.time.{LocalDate, LocalTime, Period, ZoneId, ZoneOffset}
import java.util.regex.Pattern
import java.time._
import scala.util.Try

object ConversionBuiltinFunctions {
class ConversionBuiltinFunctions(valueMapper: ValueMapper) {

def functions = Map(
"date" -> List(dateFunction, dateFunction3),
Expand Down Expand Up @@ -234,10 +215,28 @@ object ConversionBuiltinFunctions {
invoke = {
case List(ValNull) => ValNull
case List(from: ValString) => from
case List(from) => ValString(from.toString)
case List(from) => ValString(toString(from))
}
)

private def toString(from: Val): String = {
from match {
case ValContext(context) =>
context.variableProvider.getVariables
.map { case (key, value) =>
val asVal = valueMapper.toVal(value)
val asString = toString(asVal)
s"$key:$asString"
}
.mkString(start = "{", sep = ", ", end = "}")
case ValList(items) =>
items
.map(toString)
.mkString(start = "[", sep = ", ", end = "]")
case from => from.toString
}
}

private def durationFunction =
builtinFunction(
params = List("from"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class BuiltinFunctions(clock: FeelEngineClock, valueMapper: ValueMapper) extends
override def functionNames: Iterable[String] = functions.keys

val functions: Map[String, List[ValFunction]] =
ConversionBuiltinFunctions.functions ++
new ConversionBuiltinFunctions(valueMapper).functions ++
BooleanBuiltinFunctions.functions ++
StringBuiltinFunctions.functions ++
ListBuiltinFunctions.functions ++
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@
*/
package org.camunda.feel.impl.builtin

import org.scalatest.matchers.should.Matchers
import org.scalatest.flatspec.AnyFlatSpec
import org.camunda.feel.api.FeelEngineBuilder
import org.camunda.feel.impl.interpreter.MyCustomContext
import org.camunda.feel.impl.{EvaluationResultMatchers, FeelEngineTest}
import org.camunda.feel.syntaxtree._
import org.camunda.feel._
import org.camunda.feel.impl.{EvaluationResultMatchers, FeelEngineTest, FeelIntegrationTest}
import org.camunda.feel.valuemapper.CustomValueMapper
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

import java.time.{Duration, LocalDate, LocalDateTime, LocalTime, OffsetTime, Period, ZonedDateTime}
import scala.math.BigDecimal.double2bigDecimal
import java.time._
import scala.math.BigDecimal.int2bigDecimal

/** @author
Expand Down Expand Up @@ -311,6 +312,90 @@ class BuiltinConversionFunctionsTest
evaluateExpression(" string({a:1,b:2}) ") should returnResult("{a:1, b:2}")
}

case class CustomValue(value: Int)

class MyCustomValueMapper extends CustomValueMapper {
def toVal(x: Any, innerValueMapper: Any => Val): Option[Val] = x match {
case CustomValue(value) => Some(ValNumber(value))
case _ => None
}

override def unpackVal(value: Val, innerValueMapper: Val => Any): Option[Any] = None
}

it should "convert a custom context" in {

val engine = FeelEngineBuilder()
.withCustomValueMapper(new MyCustomValueMapper())
.build()

engine.evaluateExpression(
expression = " string(context) ",
variables = Map("context" -> Map("a" -> CustomValue(1)))
) should returnResult("{a:1}")

engine.evaluateExpression(
expression = " string(context) ",
variables = Map("context" -> ValContext(new MyCustomContext(Map("a" -> CustomValue(1)))))
) should returnResult(
"{a:1}"
)
}

it should "convert a list containing a custom context" in {

val engine = FeelEngineBuilder()
.withCustomValueMapper(new MyCustomValueMapper())
.build()

engine.evaluateExpression(
expression = " string(list) ",
variables = Map("list" -> List(Map("a" -> CustomValue(1)), CustomValue(2)))
) should returnResult("[{a:1}, 2]")

engine.evaluateExpression(
expression = " string(list) ",
variables =
Map("list" -> List(Map("l" -> List(CustomValue(1), CustomValue(2))), CustomValue(3)))
) should returnResult("[{l:[1, 2]}, 3]")

engine.evaluateExpression(
expression = " string(list) ",
variables = Map(
"list" -> List(ValContext(new MyCustomContext(Map("a" -> CustomValue(1)))), CustomValue(2))
)
) should returnResult("[{a:1}, 2]")

engine.evaluateExpression(
expression = " string(list) ",
variables = Map(
"list" -> List(
ValContext(new MyCustomContext(Map("l" -> List(CustomValue(1), CustomValue(2))))),
CustomValue(3)
)
)
) should returnResult("[{l:[1, 2]}, 3]")
}

it should "convert a nested custom context" in {

val engine = FeelEngineBuilder()
.withCustomValueMapper(new MyCustomValueMapper())
.build()

engine.evaluateExpression(
expression = " string(context) ",
variables = Map(
"context" ->
ValContext(
new MyCustomContext(
Map("ctx" -> ValContext(new MyCustomContext(Map("a" -> CustomValue(1)))))
)
)
)
) should returnResult("{ctx:{a:1}}")
}

"A duration() function" should "convert day-time-String" in {

evaluateExpression(""" duration(x) """, Map("x" -> "P2DT20H14M")) should returnResult(
Expand Down

0 comments on commit 012c071

Please sign in to comment.