Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create new assert function #704

Merged
merged 9 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,53 @@ get or default(null, "default")
get or default(null, null)
// null
```

## assert(value, condition)

<MarkerCamundaExtension></MarkerCamundaExtension>

Verify that the given condition is met. If the condition is `true`, the function returns the value.
Otherwise, the evaluation fails with an error.

**Function signature**

```js
assert(value: Any, condition: Any)
```

**Examples**

```js
assert(x, x != null)
// "value" - if x is "value"
// error - if x is null or doesn't exist

assert(x, x >= 0)
// 4 - if x is 4
// error - if x is less than zero
```

## assert(value, condition, cause)

<MarkerCamundaExtension></MarkerCamundaExtension>

Verify that the given condition is met. If the condition is `true`, the function returns the value.
Otherwise, the evaluation fails with an error containing the given message.

**Function signature**

```js
assert(value: Any, condition: Any, cause: String)
```

**Examples**

```js
assert(x, x != null, "'x' should not be null")
// "value" - if x is "value"
// error('x' should not be null) - if x is null or doesn't exist

assert(x, x >= 0, "'x' should be positive")
// 4 - if x is 4
// error('x' should be positive) - if x is less than zero
```
22 changes: 21 additions & 1 deletion src/main/scala/org/camunda/feel/FeelEngine.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package org.camunda.feel

import fastparse.Parsed
import org.camunda.feel.FeelEngine.{Configuration, EvalExpressionResult, EvalUnaryTestsResult, Failure}
import org.camunda.feel.api.{EvaluationResult, FailedEvaluationResult, SuccessfulEvaluationResult}
import org.camunda.feel.api.{EvaluationFailure, EvaluationFailureType, EvaluationResult, FailedEvaluationResult, SuccessfulEvaluationResult}
import org.camunda.feel.context.{Context, FunctionProvider}
import org.camunda.feel.impl.interpreter.{BuiltinFunctions, EvalContext, FeelInterpreter}
import org.camunda.feel.impl.parser.{ExpressionValidator, FeelParser}
Expand Down Expand Up @@ -156,6 +156,13 @@ class FeelEngine(
private def eval(exp: ParsedExpression,
context: EvalContext): EvaluationResult = {
interpreter.eval(exp.expression)(context) match {
case _ if containsAssertionError(context) => {
val failureMessage = getAssertErrorMessage(context)
FailedEvaluationResult(
failure = Failure(s"Assertion failure on evaluate the expression '${exp.text}': ${failureMessage}'"),
suppressedFailures = context.failureCollector.failures
)
}
case ValError(cause) =>
FailedEvaluationResult(
failure = Failure(s"failed to evaluate expression '${exp.text}': $cause"),
Expand All @@ -169,6 +176,19 @@ class FeelEngine(
}
}

/**
* Check if an {@link EvaluationFailureType.ASSERT_FAILURE} error is raised during the evaluation of an expression
* @param context the context of the evaluation
* @return true if an an {@link EvaluationFailureType.ASSERT_FAILURE} is raised, false otherwise
*/
private def containsAssertionError(context: EvalContext): Boolean = {
context.failureCollector.failures.exists(_.failureType == EvaluationFailureType.ASSERT_FAILURE);
}

private def getAssertErrorMessage(context: EvalContext): String = {
context.failureCollector.failures.find(_.failureType == EvaluationFailureType.ASSERT_FAILURE).get.failureMessage
}

// ============ public API ============

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ object EvaluationFailureType {

case object FUNCTION_INVOCATION_FAILURE extends EvaluationFailureType

case object ASSERT_FAILURE extends EvaluationFailureType

}


Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@
package org.camunda.feel.impl.builtin

import org.camunda.feel.impl.builtin.BuiltinFunction.builtinFunction
import org.camunda.feel.syntaxtree.{Val, ValBoolean, ValError, ValNull}
import org.camunda.feel.syntaxtree.{Val, ValBoolean, ValError, ValNull, ValString}

object BooleanBuiltinFunctions {

def functions = Map(
"not" -> List(notFunction),
"is defined" -> List(isDefinedFunction),
"get or else" -> List(getOrElse)
"get or else" -> List(getOrElse),
"assert" -> List(assertFunction, assertFunction2)
)

private def notFunction =
Expand All @@ -49,4 +50,21 @@ object BooleanBuiltinFunctions {
case List(value, _) => value
}
)

private def assertFunction = builtinFunction(
params = List("value", "condition"),
invoke = {
case List(value, ValBoolean(true)) => value
case _ => ValError("The condition is not fulfilled")
}
)

private def assertFunction2 = builtinFunction(
params = List("value", "condition", "cause"),
invoke = {
case List(value, ValBoolean(true), _) => value
case List(_, _, ValString(cause)) => ValError(cause)
}
)

}
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ class FeelInterpreter {
case FunctionInvocation(name, params) =>
withFunction(findFunction(context, name, params),
f => invokeFunction(f, params) match {
case ValError(failure) if name == "assert" =>
error(EvaluationFailureType.ASSERT_FAILURE, failure)
ValError(failure)
nicpuppa marked this conversation as resolved.
Show resolved Hide resolved
case ValError(failure) =>
error(EvaluationFailureType.FUNCTION_INVOCATION_FAILURE, s"Failed to invoke function '$name': $failure")
ValNull
Expand Down

This file was deleted.

Loading