From f14d5dfcd4aa0fed33ab326776f607475aa597f4 Mon Sep 17 00:00:00 2001 From: Piotr Rudnicki Date: Wed, 13 Nov 2024 12:54:42 +0100 Subject: [PATCH] [NU-7142] Lift TypingResult for dicts --- docs/Changelog.md | 14 ++++++- .../DictKeyWithLabelExpressionParser.scala | 21 ++++++++-- ...DictKeyWithLabelExpressionParserTest.scala | 42 +++++++++++++++++++ 3 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 scenario-compiler/src/test/scala/pl/touk/nussknacker/engine/language/dictWithLabel/DictKeyWithLabelExpressionParserTest.scala diff --git a/docs/Changelog.md b/docs/Changelog.md index 80992227237..8bd7218c4b3 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -1,5 +1,17 @@ # Changelog +## 1.19 + +#### Highlights + +##### End-user + +##### Administrator + +### 1.19.0 (Not released yet) + +* [#7145](https://github.com/TouK/nussknacker/pull/7145) Lift TypingResult information for dictionaries + ## 1.18 #### Highlights @@ -20,7 +32,7 @@ ### 1.18.0 (Not released yet) -* [6944](https://github.com/TouK/nussknacker/pull/6944) Changes around adhoc testing feature +* [#6944](https://github.com/TouK/nussknacker/pull/6944) Changes around adhoc testing feature * `test-with-form` button was renamed to `adhoc-testing` * Improved form validators inside adhoc tests (validation was moved to backend) * Moved `testInfo/*` endpoints to `scenarioTesting/` path and rewrite then using Tapir diff --git a/scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/language/dictWithLabel/DictKeyWithLabelExpressionParser.scala b/scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/language/dictWithLabel/DictKeyWithLabelExpressionParser.scala index c95b3d41437..2a2eb13bf82 100644 --- a/scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/language/dictWithLabel/DictKeyWithLabelExpressionParser.scala +++ b/scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/language/dictWithLabel/DictKeyWithLabelExpressionParser.scala @@ -9,7 +9,7 @@ import pl.touk.nussknacker.engine.api.definition.{AdditionalVariable => _} import pl.touk.nussknacker.engine.api.expression.ExpressionTypingInfo import pl.touk.nussknacker.engine.api.generics.ExpressionParseError import pl.touk.nussknacker.engine.api.typed.typing -import pl.touk.nussknacker.engine.api.typed.typing.{Typed, TypingResult} +import pl.touk.nussknacker.engine.api.typed.typing.{Typed, TypedClass, TypedObjectWithValue, TypingResult} import pl.touk.nussknacker.engine.expression.NullExpression import pl.touk.nussknacker.engine.expression.parse.{CompiledExpression, ExpressionParser, TypedExpression} import pl.touk.nussknacker.engine.graph.expression.Expression.Language @@ -17,8 +17,23 @@ import pl.touk.nussknacker.engine.graph.expression.{DictKeyWithLabelExpression, import pl.touk.nussknacker.engine.spel.SpelExpressionParseError.KeyWithLabelExpressionParsingError import pl.touk.nussknacker.engine.spel.SpelExpressionParser -case class DictKeyWithLabelExpressionTypingInfo(key: String, label: Option[String], typingResult: TypingResult) - extends ExpressionTypingInfo +import scala.util.Try + +case class DictKeyWithLabelExpressionTypingInfo(key: String, label: Option[String], expectedType: TypingResult) + extends ExpressionTypingInfo { + + // We should support at least types defined in FragmentParameterValidator#permittedTypesForEditors + override def typingResult: TypingResult = expectedType match { + case clazz: TypedClass if clazz.canBeSubclassOf(Typed[Long]) && Try(key.toLong).toOption.isDefined => + TypedObjectWithValue(clazz.runtimeObjType, key.toLong) + case clazz: TypedClass if clazz.canBeSubclassOf(Typed[Boolean]) && Try(key.toBoolean).toOption.isDefined => + TypedObjectWithValue(clazz.runtimeObjType, key.toBoolean) + case clazz: TypedClass if clazz.canBeSubclassOf(Typed[String]) => + TypedObjectWithValue(clazz.runtimeObjType, key) + case _ => expectedType + } + +} object DictKeyWithLabelExpressionParser extends ExpressionParser { diff --git a/scenario-compiler/src/test/scala/pl/touk/nussknacker/engine/language/dictWithLabel/DictKeyWithLabelExpressionParserTest.scala b/scenario-compiler/src/test/scala/pl/touk/nussknacker/engine/language/dictWithLabel/DictKeyWithLabelExpressionParserTest.scala new file mode 100644 index 00000000000..93574d7c8ee --- /dev/null +++ b/scenario-compiler/src/test/scala/pl/touk/nussknacker/engine/language/dictWithLabel/DictKeyWithLabelExpressionParserTest.scala @@ -0,0 +1,42 @@ +package pl.touk.nussknacker.engine.language.dictWithLabel + +import org.scalatest.Inside.inside +import org.scalatest.OptionValues +import org.scalatest.funsuite.AnyFunSuite +import org.scalatest.matchers.should.Matchers +import pl.touk.nussknacker.engine.api.context.ValidationContext +import pl.touk.nussknacker.engine.api.typed.typing.{Typed, TypedObjectWithValue} + +import scala.reflect.ClassTag + +class DictKeyWithLabelExpressionParserTest extends AnyFunSuite with Matchers with OptionValues { + private val dictKeyWithLabelExpressionParser = DictKeyWithLabelExpressionParser + + test("should parse dict key with label String expression and lift typing information") { + checkForDictKeyWithLabelExpressionTypingInfo[String]("'pizza'", "pizza") + } + + test("should parse dict key with label Long expression and lift typing information") { + checkForDictKeyWithLabelExpressionTypingInfo[Long](42, "number") + } + + test("should parse dict key with label Boolean expression and lift typing information") { + checkForDictKeyWithLabelExpressionTypingInfo[Boolean](true, "judge") + } + + private def checkForDictKeyWithLabelExpressionTypingInfo[T: ClassTag](key: T, label: String) = { + val jsonString = s"""{"key":"$key","label":"$label"}""" + val parsedTypingResult = dictKeyWithLabelExpressionParser + .parse(jsonString, ValidationContext.empty, Typed.typedClass[T]) + .toOption + .value + .typingInfo + .typingResult + + inside(parsedTypingResult) { case typedObjectWithValue: TypedObjectWithValue => + typedObjectWithValue.underlying shouldBe Typed.typedClass[T] + typedObjectWithValue.valueOpt shouldBe Some(key) + } + } + +}