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

Fix subtyping in dict service #7115

Open
wants to merge 25 commits into
base: staging
Choose a base branch
from

Conversation

Diamekod0221
Copy link
Contributor

@Diamekod0221 Diamekod0221 commented Oct 30, 2024

Describe your changes

DictApiHttpService sometimes returns improper subtypes like Long type Dicts for Integer or won't return proper types like Number type dict for Long.

Issue is caused by bugs in lang3 lib used in CanBeSubclassDeterminer - the lib would sometimes return incorrect false, and our faulty logic in TypeConversionHandler - the code would return true even if no conversion was possible.

Fixed by adding a double check in SubclassDeterminer and fixing the logic in ConversionHandler.

Checklist before merge

  • Related issue ID is placed at the beginning of PR title in [brackets] (can be GH issue or Nu Jira issue)
  • Code is cleaned from temporary changes and commented out lines
  • Parts of the code that are not easy to understand are documented in the code
  • Changes are covered by automated tests
  • Showcase in dev-application.conf added to demonstrate the feature
  • Documentation added or updated
  • Added entry in Changelog.md describing the change from the perspective of a public distribution user
  • Added MigrationGuide.md entry in the appropriate subcategory if introducing a breaking change
  • Verify that PR will be squashed during merge

Summary by CodeRabbit

  • New Features

    • Enhanced type conversion checks across various components, allowing for broader compatibility with numeric and collection types.
    • Introduced new methods for strict and loose type assignability checks in the AssignabilityDeterminer class.
  • Bug Fixes

    • Improved error handling and validation logic in multiple areas, ensuring more accurate type compatibility assessments.
  • Documentation

    • Updated migration guide to reflect changes in version 1.18.0, including configuration and API updates.
  • Tests

    • Added unit tests for the new AssignabilityDeterminer class, covering strict and loose conversion cases.

@Diamekod0221 Diamekod0221 marked this pull request as ready for review October 30, 2024 15:21
@github-actions github-actions bot added client client main fe ui labels Oct 30, 2024
@Diamekod0221 Diamekod0221 force-pushed the fix_subtyping_in_DictService branch 2 times, most recently from 81e1d11 to 05da3cb Compare October 31, 2024 16:25
Copy link

coderabbitai bot commented Nov 20, 2024

📝 Walkthrough
📝 Walkthrough

Walkthrough

The pull request introduces significant changes to the type-checking and conversion logic across various components of the Nussknacker engine. Key modifications include the replacement of the canBeSubclassOf method with canBeConvertedTo in multiple locations, enhancing the flexibility of type compatibility checks. This adjustment affects how numeric types and other data structures are validated, allowing for broader type conversions rather than strict subclass relationships. New methods such as canBeStrictlyConvertedTo have been introduced to provide stricter conversion checks, while existing methods have been renamed or modified to reflect this new logic. Additionally, several test cases have been updated to align with the revised type-checking mechanisms, ensuring that the tests accurately reflect the intended behavior of the type system. Overall, these changes enhance the robustness of type conversion handling within the Nussknacker framework.

Possibly related PRs

Suggested labels

submodules

Suggested reviewers

  • Dzuming
  • arkadius
  • JulianWielga

Warning

Rate limit exceeded

@Diamekod0221 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 16 minutes and 34 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between ce5448a and 4204af0.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between ce5448a and 4204af0.

📒 Files selected for processing (2)
  • components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/AssignabilityDeterminer.scala (1 hunks)
  • docs/MigrationGuide.md (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/AssignabilityDeterminer.scala
🧰 Additional context used
🪛 Markdownlint (0.35.0)
docs/MigrationGuide.md

69-69: Expected: 2; Actual: 4
Unordered list indentation

(MD007, ul-indent)

🔇 Additional comments (2)
docs/MigrationGuide.md (2)

47-51: LGTM! Well documented API changes for type handling

The changes from canBeSubclassOf to isAssignableLoose/canBeConvertedTo are properly documented, making it clear for users how to migrate their code.


68-69: LGTM! Clear documentation of DictApiEndpoints changes

The change in DictListRequestDto expected type from TypingResultInJson to Json is well documented.

🧰 Tools
🪛 Markdownlint (0.35.0)

69-69: Expected: 2; Actual: 4
Unordered list indentation

(MD007, ul-indent)


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (38)
components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/NumberTypeUtils.scala (1)

18-20: Consider enhancing fallback strategy documentation

While the comment about Double being "quite safe" is helpful, consider adding more detailed documentation about the fallback strategy to help future maintainers understand the reasoning behind the order of checks.

     // in case of some unions
     else if (typ.canBeConvertedTo(Typed[java.lang.Integer])) java.lang.Integer.valueOf(0)
-    // double is quite safe - it can be converted to any Number
+    // Fall back to Double as it safely handles both integral and floating-point numbers
+    // while maintaining sufficient precision for most use cases
     else if (typ.canBeConvertedTo(Typed[Number])) java.lang.Double.valueOf(0)
scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/spel/SpelExpressionValidator.scala (1)

21-21: Consider documenting the SpelExpressionRepr special case.

The || expectedType == Typed[SpelExpressionRepr] condition appears to be a special case in the type checking logic. Consider adding a brief comment explaining why this bypass of conversion checking is necessary.

+  // SpelExpressionRepr is allowed as an expected type without conversion checking
+  // because [explain the reason here]
   case a: TypingResult if a.canBeConvertedTo(expectedType) || expectedType == Typed[SpelExpressionRepr] =>
engine/flink/components/table/src/main/scala/pl/touk/nussknacker/engine/flink/table/sink/TableTypeOutputValidator.scala (1)

Line range hint 24-26: Consider documenting the LogicalType.toString decision

The comment suggests uncertainty about using LogicalType.toString. Consider documenting the rationale for using expectedTypingResult.display instead, or if the comment is no longer relevant, remove it.

components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/SubclassDeterminerSpec.scala (2)

9-17: Add documentation explaining assignability rules

While the test cases are comprehensive, adding comments explaining why certain type conversions are allowed (true) or disallowed (false) would improve maintainability and help other developers understand the business rules.

Example improvement:

 test("Should validate assignability for decimal types") {
+  // Long cannot be assigned to Integer as it may cause data loss
   StrictConversionDeterminer.isAssignable(classOf[java.lang.Long], classOf[java.lang.Integer]) shouldBe false
+  // Integer can be safely assigned to Long as there's no data loss risk
   StrictConversionDeterminer.isAssignable(classOf[Integer], classOf[java.lang.Long]) shouldBe true

27-39: Consider creating a tracking issue for lang3 library bug

The test effectively documents the known issues with lang3's ClassUtils.isAssignable. To ensure future cleanup when the library bug is fixed, consider:

  1. Creating a tracking issue referencing the upstream lang3 bug
  2. Adding the issue number to the test comment
  3. Setting up automated checks to verify if newer versions of lang3 fix this issue

This will help maintain the workaround in SubclassDeterminer.isAssignable appropriately.

Would you like me to help create a tracking issue for this?

components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/TypingResultErrorMessagesSpec.scala (2)

Line range hint 20-40: LGTM with enhancement suggestion for error messages

The test case correctly validates type conversion scenarios with clear error messages. Consider enhancing the error messages to include type conversion suggestions when applicable, which could help developers understand valid conversion paths.


Line range hint 41-51: Consider improving nested structure error messages

While the test case correctly validates nested type conversions, the error message could be more specific about which part of the nested structure is incompatible. This would help developers quickly identify the exact point of type mismatch in complex structures.

designer/server/src/main/scala/pl/touk/nussknacker/ui/api/DictApiHttpService.scala (1)

65-67: Excellent fix for the subtyping issues!

The transition from canBeSubclassOf to canBeStrictlyConvertedTo directly addresses the core issue of incorrect type returns (e.g., Long for Integer requests). This stricter type checking should prevent type mismatches and ensure more accurate dictionary filtering.

Consider adding logging for cases where type conversion is rejected to help with debugging and monitoring of the type conversion behavior in production.

scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/definition/clazz/MethodTypeInfoSubclassChecker.scala (2)

15-15: LGTM! Consider updating related error messages.

The change from canBeSubclassOf to canBeConvertedTo aligns with the PR objectives and should help fix the subtyping issues. However, the error messages in the NotSubclassVarArgument case class still reference "subclass" terminology, which might be misleading given the new conversion-based approach.

Consider updating the error message to reflect the new semantics:

-    s"vararg argument has illegal type: ${found.display} cannot be subclass of ${expected.display}"
+    s"vararg argument has illegal type: ${found.display} cannot be converted to ${expected.display}"

Line range hint 1-90: Consider documenting the type conversion rules.

The transition from subclass checking to type conversion checking is a significant architectural change. Consider adding documentation that clearly outlines:

  1. The rules for valid type conversions
  2. The precedence of different conversion paths
  3. Examples of supported and unsupported conversions

This will help maintainers understand the expected behavior and make future modifications with confidence.

scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/language/dictWithLabel/DictKeyWithLabelExpressionParser.scala (1)

Line range hint 79-81: Update error message to reflect type conversion approach.

The error message still refers to "subclass" despite the implementation now using type conversion checks. Consider updating the message to:

-          s"DictKeyExpression expected type: ${expectedType.display} is unsupported. It must be a subclass of Long, Boolean or String"
+          s"DictKeyExpression expected type: ${expectedType.display} is unsupported. It must be convertible to Long, Boolean or String"
designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/DictApiEndpoints.scala (2)

19-20: Consider using explicit imports instead of wildcard import

While the wildcard import sttp.tapir.json.circe._ works, explicit imports would make it clearer which specific JSON functionalities are being used. This improves code maintainability and makes dependencies more transparent.


Line range hint 26-98: Good architectural approach to type handling refactoring

The endpoint definitions maintain a clean separation of concerns while introducing more flexible type handling. The error handling structure, particularly the inclusion of MalformedTypingResult, provides good coverage for potential issues with the new Json-based type representation.

A few architectural considerations:

  1. The endpoint responses use appropriate HTTP status codes
  2. Security is properly maintained through the auth parameter
  3. Error cases are well-defined and documented
scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/definition/clazz/MethodDefinition.scala (1)

Line range hint 99-107: Consider using a more graceful error handling approach.

While the type validation logic is correct, throwing an AssertionError might be too aggressive for runtime validation. Consider returning a ValidationNel instead, consistent with the method's return type.

-      if (!typesFromStaticMethodInfo.exists(calculated.canBeConvertedTo)) {
-        val expectedTypesString = typesFromStaticMethodInfo.map(_.display).mkString("(", ", ", ")")
-        val argumentsString     = arguments.map(_.display).mkString("(", ", ", ")")
-        throw new AssertionError(
-          s"Generic function $name returned type ${calculated.display} that does not match any of declared types $expectedTypesString when called with arguments $argumentsString"
-        )
-      }
+      if (!typesFromStaticMethodInfo.exists(calculated.canBeConvertedTo)) {
+        val expectedTypesString = typesFromStaticMethodInfo.map(_.display).mkString("(", ", ", ")")
+        val argumentsString     = arguments.map(_.display).mkString("(", ", ", ")")
+        convertError(
+          GenericFunctionTypingError.ReturnTypeError(
+            s"Generic function $name returned type ${calculated.display} that does not match any of declared types $expectedTypesString when called with arguments $argumentsString"
+          ),
+          arguments
+        ).invalidNel
+      } else {
+        calculated.validNel
+      }
components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/TypedFromInstanceTest.scala (1)

85-85: Consider renaming the function to match new type checking approach.

While the change to canBeConvertedTo is correct, the function name checkNotASubclassOfOtherParamTypingResult still refers to the old subclass-based approach. Consider renaming it to something like checkNotConvertibleToOtherParamTypingResult for better clarity and consistency.

engine/flink/management/dev-model/src/main/scala/pl/touk/nussknacker/engine/management/sample/global/DocumentationFunctions.scala (1)

113-113: LGTM! Consider enhancing the error message.

The change to use type conversion checking is appropriate for field access validation.

Consider making the error message more specific by including the actual type:

-        OtherError("Expected string with known value").invalidNel
+        OtherError(s"Expected string with known value but got: ${x.display}").invalidNel
engine/flink/components/table/src/main/scala/pl/touk/nussknacker/engine/flink/table/utils/ToTableTypeSchemaBasedEncoder.scala (3)

85-86: LGTM! Consider improving readability.

The double-check mechanism (Number compatibility + target type compatibility) is a robust solution. However, the condition could be more readable.

Consider reformatting for better readability:

-      if typ
-        .canBeConvertedTo(Typed[Number]) && typ.canBeConvertedTo(targetType.toTypingResult) =>
+      if typ.canBeConvertedTo(Typed[Number]) && 
+         typ.canBeConvertedTo(targetType.toTypingResult) =>

108-108: LGTM! Consider extracting common condition.

The changes correctly ensure that multiset count values can be converted to Int. The implementation is consistent across both cases.

Consider extracting the common condition to reduce duplication:

private val canBeUsedAsCount = (valueType: TypingResult) => 
  valueType.canBeConvertedTo(Typed[Int])

Also applies to: 111-111


Line range hint 29-111: Consider adding documentation for the type conversion strategy.

The changes successfully implement a more flexible type conversion approach, replacing subclass checks with conversion checks throughout the file. This is a significant architectural change that would benefit from documentation.

Consider adding:

  1. Comments explaining the type conversion strategy
  2. Examples of supported type conversions
  3. Documentation of edge cases and limitations
designer/server/src/test/scala/pl/touk/nussknacker/ui/api/DictApiHttpServiceSpec.scala (1)

82-95: Consider improving test data isolation.

While the test correctly validates Long type handling, consider extracting the dictionary ID "long_dict" as a constant to improve maintainability and make the test's dependencies more explicit.

+ private val LongDictId = "long_dict"
  // Then use it in the test
-    "id" : "long_dict",
+    "id" : LongDictId,
utils/schemed-kafka-components-utils/src/main/scala/pl/touk/nussknacker/engine/schemedkafka/encode/AvroSchemaOutputValidator.scala (1)

155-159: Consider documenting the type validation strategy

The codebase now uses both canBeSubclassOf and canBeConvertedTo for different validation scenarios. Consider adding documentation to clarify:

  • When to use canBeSubclassOf vs canBeConvertedTo
  • The rationale behind using conversion checks specifically for map keys
  • Any performance implications of the different validation approaches

This will help maintain consistency as the codebase evolves and guide future modifications to type validation logic.

utils/json-utils/src/main/scala/pl/touk/nussknacker/engine/json/encode/JsonSchemaOutputValidator.scala (1)

373-373: Consider adding a comment explaining type conversion behavior.

Since this is a critical change in type validation logic, consider adding a comment explaining which type conversions are allowed and any potential precision considerations. This will help future maintainers understand the implications of using canBeConvertedTo.

+  // We use canBeConvertedTo instead of canBeSubclassOf to allow valid type conversions
+  // (e.g., Integer → Long) while still maintaining type safety. This ensures correct
+  // handling of numeric types in JSON schema validation.
   condNel(
     typingResult.canBeConvertedTo(schemaAsTypedResult),
     (),
     OutputValidatorTypeError(path, typingResult, JsonSchemaExpected(schema, rootSchema))
   )
engine/flink/components/base-unbounded/src/main/scala/pl/touk/nussknacker/engine/flink/util/transformer/aggregate/aggregates.scala (2)

356-358: Improve readability of the condition

While the type check change is correct, the multi-line condition could be more readable. Consider reformatting:

-            if inputFields.keySet == scalaFields.keySet && klass.canBeConvertedTo(
-              Typed[java.util.Map[String, _]]
-            ) =>
+            if inputFields.keySet == scalaFields.keySet && 
+               klass.canBeConvertedTo(Typed[java.util.Map[String, _]]) =>

442-442: LGTM! Consider documenting type conversion behavior

The change to canBeConvertedTo in MathAggregator trait affects all implementing aggregators. Consider adding documentation about the expected type conversion behavior to help future maintainers understand the implications of this change.

components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/TypingResultSpec.scala (3)

79-82: Consider adding more union type conversion test cases.

While the current tests cover basic union type conversions, consider adding test cases for:

  • Union types with different type parameters
  • Nested union types
  • Union types with incompatible types

133-141: Consider adding array covariance test cases.

While the current array type conversion tests are good, consider adding test cases that specifically verify array covariance behavior with:

  • Primitive type arrays
  • Object arrays with inheritance
  • Multi-dimensional arrays

349-353: Consider adding more date/time format test cases.

While the current tests cover basic date/time string conversions, consider adding test cases for:

  • Different date/time formats
  • Invalid format handling
  • Timezone handling
components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/StrictConversionDeterminer.scala (3)

51-51: Typo in variable name givenSuperclas

The variable givenSuperclas seems to be misspelled. Consider renaming it to givenSuperclass for consistency and readability.


75-83: Consider renaming isAssignable to isAssignableFrom for clarity

The method isAssignable checks if an instance of from can be assigned to to, similar to the Java Class.isAssignableFrom(Class) method. Renaming it to isAssignableFrom would enhance clarity and align with conventional naming.


64-67: Clarify error message in isStrictSubclass method

The error message "${givenClass.display} and ${givenSuperclass.display} are not the same" may be unclear. Consider rephrasing it to provide more context, such as "Expected ${givenSuperclass.display}, but found ${givenClass.display}".

components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/TypeConversionHandler.scala (3)

23-24: Correct grammatical errors in documentation comments

The documentation comments contain grammatical errors that may reduce clarity. Consider rephrasing them for better readability.

Apply this diff to improve the comments:

- * Given to this we cannot be sure if conversion is safe or not based on type (without scale knowledge).
- * So we have two options: force user to convert to some type without floating point (e.g. BigInteger) or be loose in this point.
+ * Given this, we cannot be sure if the conversion is safe based on type alone (without scale knowledge).
+ * Therefore, we have two options: force the user to convert to a non-floating-point type (e.g., BigInteger) or be lenient in this aspect.

96-98: Address the TODO regarding strict number precision checking

The TODO comment indicates a pending decision about adding a feature flag for strict number precision checking.

Would you like assistance in implementing this feature flag? This could enhance configurability and allow users to opt-in for strict precision checks as needed.


100-103: Optimize helper methods within handleStrictNumberConversions

The helper methods isFloating and isDecimalNumber encapsulate type checks.

Consider moving these methods to a shared utility class if they are used elsewhere, or refining them to improve clarity. For example, you might inline them if they're only used once, to streamline the code.

components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/ImplicitConversionDeterminer.scala (5)

24-26: Update documentation to reflect method functionality change

The method documentation still references "subclass" terminology, but the method now handles type conversion rather than subclass checking. Please update the comments to accurately describe the new functionality.

Here is the suggested change:

 /**
- * This method checks if `givenType` can by subclass of `superclassCandidate`
- * It will return true if `givenType` is equals to `superclassCandidate` or `givenType` "extends" `superclassCandidate`
+ * This method checks if `givenType` can be converted to `superclassCandidate`.
+ * It will return true if `givenType` is equal to `superclassCandidate` or if a valid type conversion exists.
  */

49-50: Clarify comment regarding type compatibility checks

The comment references a "double exists" approach but could be clearer regarding the trade-offs made for type safety versus user intentions. Consider refining the comment for better readability and understanding.

Suggested improvement:

- // "double exists" looks like a good tradeoff
+ // Using "double exists" allows flexibility for user intentions, accepting possible types rather than restricting expressions that could fail at runtime

166-167: Rename variable for consistency with method names

The variable canBeSubclass does not align with the updated method names focused on conversion. Rename it to canBeConverted to maintain consistency and improve code readability.

Apply this diff to rename the variable:

 val canBeConverted = equalClassesOrCanAssign andThen (_ => typeParametersMatches(givenClass, givenSuperclass))
-canBeSubclass orElse canBeConvertedTo(givenType, givenSuperclass)
+canBeConverted orElse canBeConvertedTo(givenType, givenSuperclass)

170-170: Update TODO comment to reflect renamed methods

The TODO comment references the old method canBeSubclassOf. Update it to canBeConvertedTo to reflect the method's new name and functionality.

Suggested change:

 // TODO: Conversions should be checked during typing, not during generic usage of TypingResult.canBeConvertedTo(...)

199-203: Correct error message formatting and grammar

The error message in the condNel call has minor grammatical issues. Improve the message for clarity.

Suggested correction:

 s"Key types of Maps ${givenKeyParam.display} and ${superclassKeyParam.display} are not equal"
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 58f8185 and e4724a2.

📒 Files selected for processing (37)
  • components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/ImplicitConversionDeterminer.scala (6 hunks)
  • components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/NumberTypeUtils.scala (1 hunks)
  • components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/StrictConversionDeterminer.scala (1 hunks)
  • components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/TypeConversionHandler.scala (3 hunks)
  • components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/typing.scala (2 hunks)
  • components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/SubclassDeterminerSpec.scala (1 hunks)
  • components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/TypedFromInstanceTest.scala (2 hunks)
  • components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/TypingResultErrorMessagesSpec.scala (3 hunks)
  • components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/TypingResultSpec.scala (7 hunks)
  • designer/client/src/http/HttpService.ts (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/DictApiHttpService.scala (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/DictApiEndpoints.scala (2 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/process/test/ScenarioTestService.scala (2 hunks)
  • designer/server/src/test/scala/pl/touk/nussknacker/ui/api/DictApiHttpServiceSpec.scala (2 hunks)
  • designer/server/src/test/scala/pl/touk/nussknacker/ui/validation/UIProcessValidatorSpec.scala (1 hunks)
  • docs-internal/api/nu-designer-openapi.yaml (1 hunks)
  • engine/flink/components/base-tests/src/test/scala/pl/touk/nussknacker/engine/flink/util/transformer/aggregate/AggregatesSpec.scala (1 hunks)
  • engine/flink/components/base-unbounded/src/main/scala/pl/touk/nussknacker/engine/flink/util/transformer/aggregate/aggregates.scala (4 hunks)
  • engine/flink/components/base/src/main/scala/pl/touk/nussknacker/engine/flink/util/transformer/ForEachTransformer.scala (1 hunks)
  • engine/flink/components/table/src/main/scala/pl/touk/nussknacker/engine/flink/table/sink/TableTypeOutputValidator.scala (1 hunks)
  • engine/flink/components/table/src/main/scala/pl/touk/nussknacker/engine/flink/table/utils/ToTableTypeSchemaBasedEncoder.scala (3 hunks)
  • engine/flink/management/dev-model/src/main/scala/pl/touk/nussknacker/engine/management/sample/global/DocumentationFunctions.scala (2 hunks)
  • engine/flink/management/dev-model/src/main/scala/pl/touk/nussknacker/engine/management/sample/global/ExampleFunctions.scala (1 hunks)
  • engine/lite/components/base/src/main/scala/pl/touk/nussknacker/engine/lite/components/ForEachTransformer.scala (1 hunks)
  • scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/compile/nodecompilation/FragmentParameterValidator.scala (1 hunks)
  • scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/definition/clazz/ClassDefinitionExtractor.scala (2 hunks)
  • scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/definition/clazz/MethodDefinition.scala (2 hunks)
  • scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/definition/clazz/MethodTypeInfoSubclassChecker.scala (2 hunks)
  • scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/language/dictWithLabel/DictKeyWithLabelExpressionParser.scala (1 hunks)
  • scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/spel/SpelExpressionSuggester.scala (1 hunks)
  • scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/spel/SpelExpressionValidator.scala (1 hunks)
  • scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/spel/Typer.scala (11 hunks)
  • scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/spel/typer/MethodReferenceTyper.scala (1 hunks)
  • utils/default-helpers/src/main/scala/pl/touk/nussknacker/engine/util/functions/collection.scala (1 hunks)
  • utils/default-helpers/src/main/scala/pl/touk/nussknacker/engine/util/functions/numeric.scala (1 hunks)
  • utils/json-utils/src/main/scala/pl/touk/nussknacker/engine/json/encode/JsonSchemaOutputValidator.scala (1 hunks)
  • utils/schemed-kafka-components-utils/src/main/scala/pl/touk/nussknacker/engine/schemedkafka/encode/AvroSchemaOutputValidator.scala (1 hunks)
🔇 Additional comments (67)
components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/NumberTypeUtils.scala (1)

18-18: LGTM! Type conversion checks improve type safety

The transition from subclass checks to conversion checks aligns well with the PR objectives and provides more accurate type compatibility verification. The fallback strategy is logically sound:

  1. First attempting Integer conversion for whole numbers
  2. Then falling back to Double for broader number types

Also applies to: 20-20

scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/spel/SpelExpressionValidator.scala (1)

21-21: LGTM! Verify type conversion behavior.

The change from subclass checking to type conversion checking aligns well with the PR objectives and should fix the incorrect subtype handling issues. The implementation maintains proper error handling through the Invalid case.

Please ensure that all edge cases are covered in tests, particularly:

  • Long to Integer conversions
  • Number type handling for Long requests
  • Cases where type conversion might result in data loss
engine/flink/components/table/src/main/scala/pl/touk/nussknacker/engine/flink/table/sink/TableTypeOutputValidator.scala (1)

17-17: LGTM! Verify type conversion behavior

The change from canBeSubclassOf to canBeConvertedTo aligns with the PR objective of fixing subtyping issues. This should properly handle cases where type conversion is possible without requiring strict subclass relationships.

engine/lite/components/base/src/main/scala/pl/touk/nussknacker/engine/lite/components/ForEachTransformer.scala (1)

45-47: LGTM! The type conversion change aligns with the PR objectives.

The switch to canBeConvertedTo from canBeSubclassOf is consistent with the PR's goal to fix subtyping issues in the dict service. This should help resolve the incorrect type handling problems mentioned in the PR description.

Please ensure that the test suite covers edge cases where:

  • Collections with various element types are processed
  • Non-collection types are handled (should return Unknown)
  • Generic type parameters are properly preserved
components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/SubclassDeterminerSpec.scala (1)

1-7: LGTM! Well-structured test file setup

The file structure follows Scala best practices with clear package declaration and appropriate testing framework imports.

engine/flink/components/base/src/main/scala/pl/touk/nussknacker/engine/flink/util/transformer/ForEachTransformer.scala (1)

48-50: LGTM! The type conversion check aligns with the PR objectives.

The transition from subclass checking to type conversion checking is appropriate here and aligns with the broader goal of fixing subtyping issues. Please verify that this change correctly handles all relevant collection types (e.g., List, Set, Queue) to ensure consistent behavior across different collection implementations.

components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/TypingResultErrorMessagesSpec.scala (2)

16-16: LGTM: Import change aligns with the new type conversion approach

The change from subclass checks to conversion checks is consistent with the PR's objective to fix subtyping issues.


52-75: LGTM: Comprehensive coverage of special cases

The test cases thoroughly validate various edge cases and special scenarios, with clear and specific error messages for each case. The null handling is particularly well-implemented, ensuring type safety.

designer/server/src/main/scala/pl/touk/nussknacker/ui/api/DictApiHttpService.scala (1)

59-59: Verify the decoder behavior with the full expectedType object.

The change to pass the complete expectedType object instead of just its value looks correct, as it allows for more comprehensive type information during decoding.

scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/definition/clazz/MethodTypeInfoSubclassChecker.scala (2)

41-43: LGTM! Verify type conversion behavior.

The change to use canBeConvertedTo is consistent with the PR objectives and should fix the incorrect subtype handling. The surrounding logic properly maintains parameter position tracking for error reporting.

Please ensure that all expected type conversions (especially edge cases like Long to Integer) are covered by the test suite.


49-49: LGTM! Ensure comprehensive return type testing.

The change to canBeConvertedTo completes the consistent pattern of changes across all type checks. This should properly handle return type compatibility checks.

Consider adding test cases that specifically verify:

  1. Number type dictionary returns for Long requests
  2. Long type dictionary returns for Integer requests
  3. Edge cases where type conversion should be rejected
scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/language/dictWithLabel/DictKeyWithLabelExpressionParser.scala (1)

73-77: LGTM! Type conversion checks properly implemented.

The transition from canBeSubclassOf to canBeConvertedTo aligns with the PR objectives to fix subtyping issues. The implementation correctly handles type conversion for Long, Boolean, and String types.

designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/DictApiEndpoints.scala (2)

90-90: Verify the implications of using raw Json type

The change from TypingResultInJson to Json for expectedType makes the type handling more flexible, which aligns with fixing the subtyping issues. However, please ensure that:

  1. Proper validation is implemented where this DTO is processed
  2. Error handling adequately covers potential JSON parsing/validation failures
  3. Type conversion logic in the service layer correctly handles the raw Json input

98-100: LGTM! Well-structured schema definitions

The schema definitions are properly implemented with:

  • Appropriate use of lazy evaluation
  • Correct implicit declarations
  • Clean usage of Schema.derived
scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/definition/clazz/MethodDefinition.scala (2)

38-41: LGTM! Verify array-to-list conversion edge cases.

The updated type conversion logic correctly handles both direct type conversion and array-to-list conversion scenarios. This change aligns well with the PR's objective of fixing subtyping issues.

Consider adding test cases for edge cases like:

  • Empty arrays
  • Arrays with null elements
  • Nested arrays that could be converted to nested lists

46-46: LGTM! Clean and consistent type conversion check.

The vararg parameter type checking has been properly updated to use type conversion instead of subclass checking, maintaining consistency with the new type system approach.

components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/TypedFromInstanceTest.scala (3)

63-64: LGTM! Test updated to use new type conversion checks.

The change correctly implements the transition from subclass checks to type conversion checks for empty lists, maintaining coverage for both Scala and Java collections.


71-71: LGTM! Helper function updated to use new type conversion mechanism.

The change correctly updates the type checking mechanism in the helper function while maintaining its original validation purpose.


76-76: LGTM! Parameter type checking updated consistently.

The change correctly implements type conversion checks for generic type parameters, maintaining consistency with the new approach.

engine/flink/management/dev-model/src/main/scala/pl/touk/nussknacker/engine/management/sample/global/DocumentationFunctions.scala (1)

65-65: LGTM! Verify test coverage for type conversion scenarios.

The change from subclass checking to type conversion checking aligns well with the PR objectives and provides more accurate type compatibility validation for the plus operation.

Consider adding test cases to verify:

  • Mixed numeric type operations (e.g., Int + Long, Float + Double)
  • Edge cases with custom Number implementations
engine/flink/management/dev-model/src/main/scala/pl/touk/nussknacker/engine/management/sample/global/ExampleFunctions.scala (2)

150-150: LGTM! Change aligns with PR objectives.

The transition from canBeSubclassOf to canBeConvertedTo aligns with the PR's goal of fixing subtyping issues in the dict service. This change makes the type checking more flexible by allowing type conversions instead of strict subclass relationships.


150-150: Verify numeric type conversion safety.

While the change to canBeConvertedTo improves type flexibility, please verify:

  1. All possible numeric type conversions are safe and won't cause precision loss
  2. Error messages and validation behavior remain appropriate for all numeric types
  3. Edge cases (e.g., very large Long values being converted) are handled correctly
scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/spel/typer/MethodReferenceTyper.scala (1)

65-65: LGTM! The change aligns with the broader type-checking improvements.

The modification from canBeSubclassOf to canBeConvertedTo is consistent with the PR's objective of fixing subtyping issues. This change is part of a systematic refactoring across the codebase to improve type conversion handling, particularly in cases where the previous subclass checks were returning incorrect results.

Please ensure that all test cases cover edge cases where the previous subclass-based type checking might have failed, particularly with Number types and their conversions.

engine/flink/components/table/src/main/scala/pl/touk/nussknacker/engine/flink/table/utils/ToTableTypeSchemaBasedEncoder.scala (1)

29-29: LGTM! Verify number type conversion scenarios.

The change from subclass checking to conversion checking aligns well with the PR objectives and should fix the incorrect number type returns. The implementation is safe as it's followed by proper NumberUtils conversion.

Consider adding test cases for edge cases like:

  • Converting between different number types (Long → Integer, Double → Long, etc.)
  • Handling overflow scenarios
utils/default-helpers/src/main/scala/pl/touk/nussknacker/engine/util/functions/numeric.scala (1)

132-132: LGTM! The change improves type conversion handling.

The switch from canBeSubclassOf to canBeConvertedTo aligns well with the PR's objective of fixing subtype handling issues. This change makes the type checking more flexible while maintaining type safety, particularly in cases where strict subclass relationships were causing false negatives.

Please ensure that the test suite covers edge cases where the previous subclass-based check was failing, particularly with:

  • Long type dictionaries for Integer requests
  • Number type dictionaries for Long requests
designer/server/src/main/scala/pl/touk/nussknacker/ui/process/test/ScenarioTestService.scala (2)

10-10: LGTM! Import change aligns with the PR objectives.

The transition from CanBeSubclassDeterminer to ImplicitConversionDeterminer correctly implements the architectural shift from subclass checks to type conversion checks.


Line range hint 136-140: LGTM! Verify UI behavior with edge cases.

The switch to canBeConvertedTo improves type compatibility checking for UI parameters. This change aligns with the PR's goal of fixing incorrect type handling.

Please verify that the UI correctly handles edge cases, such as:

  • Complex types that might have string representations
  • Custom types with implicit conversions to string
  • Parameters with null values
designer/server/src/test/scala/pl/touk/nussknacker/ui/api/DictApiHttpServiceSpec.scala (2)

20-39: LGTM! Consider adding edge cases.

The test case effectively validates the fix for incorrect subtype handling, ensuring Integer requests don't return Long dictionaries.

Consider adding edge cases with different numeric types (e.g., Short, Byte) to ensure comprehensive subtype handling validation.


97-105: ⚠️ Potential issue

Resolve inconsistency between test name and implementation.

The test name mentions "BigDecimal" but the implementation uses "BigInteger". This inconsistency could lead to confusion. Either:

  1. Rename the test to match the implementation, or
  2. Update the implementation to actually test BigDecimal if that was the intention.

Additionally, verify if returning the same dictionary ("long_dict") for both Long and BigInteger types is intentional as part of the type conversion testing.

scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/compile/nodecompilation/FragmentParameterValidator.scala (1)

157-157: LGTM! The type compatibility check has been updated correctly.

The change from canBeSubclassOf to canBeConvertedTo aligns with the PR objectives to fix subtyping issues in the dict service. This modification should properly handle cases where dictionary value types need to be converted to fragment parameter types.

Please ensure that test coverage includes edge cases such as:

  • Converting between different numeric types (Long to Integer, etc.)
  • Handling null values
  • Cases where type conversion should fail
engine/flink/components/base-tests/src/test/scala/pl/touk/nussknacker/engine/flink/util/transformer/aggregate/AggregatesSpec.scala (1)

77-80: LGTM! The type checking improvement looks good.

The change from canBeSubclassOf to canBeConvertedTo aligns well with the PR objectives to fix subtyping issues. This should properly handle cases where type conversion is possible even without direct inheritance.

Consider adding test cases for edge cases such as:

  • Numeric type conversions (e.g., Long to Integer)
  • Complex object type conversions
  • Cases where conversion should explicitly fail
utils/schemed-kafka-components-utils/src/main/scala/pl/touk/nussknacker/engine/schemedkafka/encode/AvroSchemaOutputValidator.scala (1)

155-159: LGTM! Verify map key type conversion behavior

The change from canBeSubclassOf to canBeConvertedTo aligns with the PR objectives to fix subtyping issues. This modification should properly handle cases where map keys can be converted to strings, rather than strictly checking for subclass relationships.

Please verify that this change correctly handles all expected map key types, particularly:

  • String-like types (e.g., CharSequence)
  • Numeric types that should be convertible to strings
  • Custom types with string conversion methods
utils/json-utils/src/main/scala/pl/touk/nussknacker/engine/json/encode/JsonSchemaOutputValidator.scala (1)

373-373: LGTM! Change aligns with PR objectives.

The switch from canBeSubclassOf to canBeConvertedTo correctly addresses the issue of improper subtype handling mentioned in the PR objectives. This change will allow valid type conversions (e.g., Integer → Long) that were previously being incorrectly rejected.

engine/flink/components/base-unbounded/src/main/scala/pl/touk/nussknacker/engine/flink/util/transformer/aggregate/aggregates.scala (2)

193-193: LGTM! Verify Boolean type conversion handling

The change from canBeSubclassOf to canBeConvertedTo improves type safety by ensuring proper type conversion capability. Please verify that all expected Boolean-convertible types are still accepted correctly.


242-242: LGTM! Verify numeric type conversion handling

The change to canBeConvertedTo ensures proper type conversion capability for numeric types. Please verify that all numeric type conversions work correctly, especially with the ForLargeFloatingNumbersOperation promotion strategy.

components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/typing.scala (2)

30-41: LGTM! Clear distinction between conversion types.

The introduction of two separate methods with clear documentation helps prevent precision loss issues. The implementation correctly delegates to appropriate determiners.

Please ensure that:

  1. All existing callers of the old canBeSubclassOf method have been updated to use the appropriate new method based on their precision requirements
  2. The determiners (ImplicitConversionDeterminer and StrictConversionDeterminer) handle all edge cases correctly

472-474: LGTM! Consistent usage of new conversion method.

The update to use canBeConvertedTo aligns with the broader refactoring from subclass checks to conversion checks.

scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/spel/SpelExpressionSuggester.scala (1)

414-416: LGTM! The change from subclass to conversion checks aligns with PR objectives.

The modification to use canBeConvertedTo instead of canBeSubclassOf should correctly fix the subtyping issues mentioned in the PR objectives, particularly the cases where incorrect types (e.g., Long for Integer requests) were being returned.

Please ensure that the test suite includes cases that verify:

  1. Collection type conversions (e.g., List to Collection)
  2. Map type conversions
  3. Edge cases where type conversion should fail
components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/TypingResultSpec.scala (5)

35-61: LGTM! Basic type conversion tests are comprehensive.

The test cases effectively cover various scenarios for record type conversions, including:

  • Subset/superset field relationships
  • Type compatibility checks
  • List type conversions
  • Map type conversions

86-93: LGTM! Unknown type conversion tests are well-structured.

The test cases properly verify the behavior of Unknown type conversions with various target types.


145-150: LGTM! Numeric type conversion tests are thorough.

The test cases effectively cover various numeric type conversions and their relationships.


306-314: LGTM! Tagged value type conversion tests are comprehensive.

The test cases properly verify tagged value type conversions, including:

  • Same tag, same type
  • Different tags
  • Tagged to untagged conversions

452-452: LGTM! Property-based tests provide good coverage.

The property-based tests effectively verify type conversion properties:

  • Reflexivity: A type can be converted to itself
  • Transitivity: If A can be converted to B and B to C, then A can be converted to C

Also applies to: 466-470, 486-491

utils/default-helpers/src/main/scala/pl/touk/nussknacker/engine/util/functions/collection.scala (1)

359-361: LGTM! Verify numeric type conversion behavior.

The change from canBeSubclassOf to canBeConvertedTo aligns with the PR's objective to fix subtyping issues. This modification should correctly handle cases where numeric types need conversion rather than strict subtype relationships.

Please verify that:

  1. Long type dictionaries are now correctly handled for Integer requests
  2. Number type dictionaries are properly returned for Long requests
  3. Edge cases with different numeric type combinations work as expected
designer/client/src/http/HttpService.ts (1)

857-857: LGTM! Simplified payload structure.

The simplified structure of expectedType in the request payload aligns with the broader changes transitioning from subclass checks to conversion checks.

Please ensure that this payload structure change is compatible with the corresponding backend API changes.

docs-internal/api/nu-designer-openapi.yaml (3)

Line range hint 1119-1127: LGTM! Dict schema properly supports type system

The Dict schema is well-structured with proper type definitions that support the subtyping system through SingleTypingResult.


Line range hint 1128-1201: Verify dictionary type compatibility after schema fix

The endpoint implementation looks correct, but its type compatibility checking will only work properly after fixing the DictListRequestDto schema. Please verify the endpoint's behavior with various type combinations once the schema is updated.


Line range hint 5084-5087: LGTM! Security scheme is well-implemented

The security implementation using HTTP Basic Auth is properly defined and consistently applied across endpoints, with good support for user impersonation.

components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/StrictConversionDeterminer.scala (1)

56-58: Address TODO: Improve TypedNull handling for typed objects

There's a TODO comment indicating that TypedNull should not be considered a subclass of TypedObjectWithValue when all values are assigned. Addressing this is important to prevent potential type system inconsistencies when dealing with null values.

Would you like assistance in implementing the necessary logic to handle this case?

components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/TypeConversionHandler.scala (2)

71-75: Ensure proper integration of canBeStrictlyConvertedTo method

The new method canBeStrictlyConvertedTo adds stricter conversion checks, which is beneficial for precise type handling.

Please verify that:

  • All calls to type conversion checks are updated appropriately where strict conversions are required.
  • The method complements existing functionality without introducing regressions.

92-117: 🛠️ Refactor suggestion

Review logic in handleStrictNumberConversions for completeness

The handleStrictNumberConversions method introduces stricter rules for number conversions. Ensure that the logic correctly handles all relevant number types and edge cases.

Consider the following:

  • Floating-Point Numbers: Confirm that the check for floating-point numbers includes all relevant types and that conversions are handled appropriately.
  • Decimal Numbers: Verify that the use of StrictConversionDeterminer.isAssignable correctly enforces strict conversions for decimal numbers.
  • Default Case Handling: Ensure the default case (case _ => false) properly addresses types that should not be converted, preventing unintended conversions.

If there are known limitations or exceptions, consider documenting them within the code comments for clarity.

components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/ImplicitConversionDeterminer.scala (4)

145-150: Review the logic of isStrictSubclass method

The isStrictSubclass method may not accurately reflect the new conversion-based logic. It still uses subclass terminology and checks for class equality or assignability, which may not align with type conversion semantics. Verify if this method needs adjustment or renaming.


182-183: Simplify condition in typeParametersMatches

The condition within typeParametersMatches uses a logical OR (||) between canBeConvertedTo in both directions. Considering the method's goal, verify if both checks are necessary or if the logic can be simplified to improve performance and readability.


217-219: Assess compatibility of ClassUtils.isAssignable with JDK versions

The comment mentions using an explicit autoboxing flag due to compatibility issues with JDK 11. Verify that this approach is the most effective solution and consider updating the commons-lang3 library if feasible.


218-219: ⚠️ Potential issue

Consider thread safety for shared utility methods

The isAssignable method is a shared utility that may be used concurrently. Ensure that the ClassUtils.isAssignable method is thread-safe or consider synchronizing access if necessary.

scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/definition/clazz/ClassDefinitionExtractor.scala (1)

144-144: Ensure method deduplication aligns with new type conversion logic

The change from canBeSubclassOf to canBeConvertedTo alters the criteria for method selection during deduplication. Please verify that this adjustment maintains the intended behavior when selecting the most specific method among those with generic return types.

scenario-compiler/src/main/scala/pl/touk/nussknacker/engine/spel/Typer.scala (10)

149-150: Verify the replacement of canBeSubclassOf with canBeConvertedTo

The method now uses canBeConvertedTo instead of canBeSubclassOf when checking child types. Ensure that this change aligns with the intended type-checking logic and does not introduce unintended type acceptance.


201-201: Check dynamic property access for string keys

The condition now checks if indexKey can be converted to Typed[String]. Please verify that this change correctly handles dynamic property access when dynamicPropertyAccessAllowed is true or false.


Line range hint 359-368: Validate arithmetic operation type checks

The conditions for arithmetic operations now use canBeConvertedTo(Typed[Number]). Confirm that this change correctly handles all numeric types and maintains proper error handling for non-numeric types.


398-409: Ensure correct handling of string concatenation and numeric addition

In the OpPlus operator, the logic now checks for type conversion to Typed[String] or Typed[Number]. Please verify that this correctly differentiates between string concatenation and numeric addition without introducing type coercion issues.


451-451: Confirm boolean evaluation in selection expressions

The condition checks if result can be converted to Typed[Boolean]. Ensure that this modification correctly evaluates selection predicates and does not affect the selection mechanism.


462-462: Validate ternary operator condition type conversion

The ternary operator now uses canBeConvertedTo(Typed[Boolean]) for its condition. Please confirm that this change correctly enforces boolean conditions and handles type conversions appropriately.


523-526: Review iterative type extraction logic with type conversion

The method extractIterativeType replaces canBeSubclassOf with canBeConvertedTo when handling collections and arrays. Verify that this change maintains correct type extraction for iterable types.


578-578: Ensure arithmetic operations handle numeric conversions properly

In checkTwoOperandsArithmeticOperation, both operands are checked with canBeConvertedTo(Typed[Number]). Confirm that this allows for proper numeric promotions and that invalid conversions are correctly handled.


597-597: Check single-operand arithmetic operation type validation

For unary operations like increment and decrement, the operand is now validated with canBeConvertedTo(Typed[Number]). Please ensure this change correctly restricts the operation to numeric types.


693-696: Validate type conversion in projection and selection

The conditions in extractIterativeType now use canBeConvertedTo for collections and maps. Verify that this change accurately identifies iterable types and does not introduce errors in projections or selections.

designer/server/src/test/scala/pl/touk/nussknacker/ui/validation/UIProcessValidatorSpec.scala (1)

1953-1953: Ensure consistent parameter types across tests

In this line, the parameter type for P1 has been changed to Integer. However, in other tests, such as the one starting at line 1969, P1 is still using Short. Consider updating all relevant tests to use the same parameter type to maintain consistency and avoid potential confusion.

…yped/SubclassDeterminerSpec.scala

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@github-actions github-actions bot added the docs label Nov 21, 2024
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (12)
components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/StrictConversionDeterminer.scala (4)

32-35: Consider elevating the type safety tradeoff explanation to documentation.

The comment explains an important design decision about type safety vs usability tradeoff. Consider moving this explanation to scaladoc to make it more visible and maintainable.

+/**
+ * This implementation chooses usability over strict type safety. Instead of requiring
+ * givenTypes.forAll(... superclassCandidates.exists ...) which would be safer but could
+ * block valid use cases, it uses "double exists" as a practical tradeoff. This allows
+ * scenarios where the user knows only compatible types will appear at runtime.
+ */
 def canBeConvertedTo(

46-54: Add documentation for singleCanBeConvertedTo method.

This method plays a crucial role in type conversion determination. Consider adding scaladoc to explain its purpose and relationship with isStrictSubclass.

+/**
+ * Determines if a single type can be converted to another by comparing their runtime
+ * object types using strict subclass rules.
+ *
+ * @param givenType The type to check for conversion
+ * @param superclassCandidate The target type to convert to
+ * @return Validation result indicating if conversion is possible
+ */
 def singleCanBeConvertedTo(

57-58: Address TODO for Null handling with TypedMap.

The TODO indicates that Null handling for typed maps with assigned values needs implementation. This aligns with the overall goal of improving type safety.

Would you like help implementing the TypedMap null check or should I create an issue to track this?


62-84: Consider architectural improvements for conversion handling.

As suggested in the previous review, consider:

  1. Creating a single ImplicitConversionDeterminer object with only two public methods: canBeConvertedTo and canBeStrictlyConvertedTo
  2. Making all helper methods private to prevent uncontrolled reuse
  3. Consolidating the number type conversion logic in TypeConversionHandler

This would improve maintainability and ensure consistent behavior across the type system.

components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/ImplicitConversionDeterminer.scala (4)

Line range hint 16-23: Update class documentation to reflect the new conversion-focused approach

The class documentation still refers to subclass determination while the implementation has been refactored to focus on type conversion. Consider updating it to reflect this change and explain the new conversion-based approach.


50-68: Consider reducing code duplication with canBeConvertedTo

The method shares significant logic with canBeConvertedTo. Consider extracting the common pattern matching logic into a private helper method that takes a function parameter for the different conversion strategies (strict vs. non-strict).

+ private def handleTypeConversion(
+     givenType: TypingResult,
+     superclassCandidate: TypingResult,
+     singleTypeHandler: (SingleTypingResult, SingleTypingResult) => ValidatedNel[String, Unit]
+ ): ValidatedNel[String, Unit] = {
+   (givenType, superclassCandidate) match {
+     case (_, Unknown)       => ().validNel
+     case (Unknown, _)       => ().validNel
+     case (TypedNull, other) => canNullBeConvertedTo(other)
+     case (_, TypedNull)     => s"No type can be subclass of ${TypedNull.display}".invalidNel
+     case (given: SingleTypingResult, superclass: TypedUnion) =>
+       canBeConvertedTo(NonEmptyList.one(given), superclass.possibleTypes)
+     case (given: TypedUnion, superclass: SingleTypingResult) =>
+       canBeConvertedTo(given.possibleTypes, NonEmptyList.one(superclass))
+     case (given: SingleTypingResult, superclass: SingleTypingResult) =>
+       singleTypeHandler(given, superclass)
+     case (given: TypedUnion, superclass: TypedUnion) =>
+       canBeConvertedTo(given.possibleTypes, superclass.possibleTypes)
+   }
+ }

  def canBeConvertedTo(...): ValidatedNel[String, Unit] = 
-   (givenType, superclassCandidate) match { ... }
+   handleTypeConversion(givenType, superclassCandidate, singleCanBeConvertedTo)

  def canBeStrictlyConvertedTo(...): ValidatedNel[String, Unit] =
-   (givenType, superclassCandidate) match { ... }
+   handleTypeConversion(givenType, superclassCandidate, singleCanBeStrictlyConvertedTo)

221-221: Track architectural improvement for type conversion checks

The TODO comment indicates an important architectural improvement to move type conversion checks to the typing phase. This would help prevent type-related issues earlier in the process.

Would you like me to create a GitHub issue to track this architectural improvement? The issue would cover moving type conversion checks from runtime to the typing phase.


Line range hint 239-243: Consider extracting array type conversion logic

As noted in the comment, array type conversions should be handled separately from the generic type parameter matching logic. This would improve code organization and make the type system more maintainable.

Consider creating a separate handler for array type conversions that can be composed with the generic type parameter matching logic.

docs/MigrationGuide.md (4)

608-609: Fix formatting and improve clarity of DictApiEndpoints changes

The changes for DictApiEndpoints are not properly formatted and lack context. Consider expanding this section to:

* [#7115](https://github.com/TouK/nussknacker/pull/7115) Changes in DictApiEndpoints:
  * Changed `DictListRequestDto.expectedType` type from `TypingResultInJson` to `Json`
  * This change affects dictionary API request format

Line range hint 1-24: Consider adding version compatibility matrix

The migration guide would benefit from having a compatibility matrix at the top showing:

  • Supported Scala versions
  • Supported Flink versions
  • Supported Java versions
  • Required database schema versions

This would help users quickly determine if they can upgrade and what dependencies need to be updated.


Line range hint 25-50: Improve organization of configuration changes section

The configuration changes section could be better organized by grouping related changes together, for example:

  • Authentication/Security changes
  • Logging changes
  • Deployment changes
  • UI/Frontend changes

This would make it easier for users to find relevant changes for their use case.


Based on the gathered information, I can now generate the final response:

Add verification steps for critical API changes

The migration guide should include the following verification steps after upgrade:

  • Verify DeploymentManager API changes:
    • Check if custom DeploymentManager implementations are updated to use the new DeploymentUpdateStrategy instead of savepointPath
    • Verify state restoration works correctly after deployment
  • Verify API endpoints:
    • Test the new /api/scenarioTesting/{scenarioName}/adhoc/validate endpoint
    • Confirm PUT /api/processes/{processName} works with the new scenarioLabels field
    • Verify GET /api/processDefinitionData/* with the new enrichedWithUiConfig parameter
🔗 Analysis chain

Line range hint 51-100: Add migration verification steps

For critical API changes like DeploymentManager modifications, it would be helpful to add verification steps that users can follow to ensure their upgrade was successful. Consider adding a "Verification" subsection.


Let me gather more information about the API changes and migration steps.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for usage of deprecated APIs
rg -l "DeploymentManagerDependencies" 

Length of output: 3066


Script:

#!/bin/bash
# Check for recent changes in DeploymentManager API
rg -A 5 "DeploymentManagerDependencies" designer/deployment-manager-api/src/main/scala/pl/touk/nussknacker/engine/DeploymentManagerDependencies.scala

# Look for existing verification steps in migration guide
rg -i "verif|check|test" docs/MigrationGuide.md

Length of output: 17093

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e4724a2 and 07af990.

📒 Files selected for processing (4)
  • components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/ImplicitConversionDeterminer.scala (6 hunks)
  • components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/StrictConversionDeterminer.scala (1 hunks)
  • components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/SubclassDeterminerSpec.scala (1 hunks)
  • docs/MigrationGuide.md (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/SubclassDeterminerSpec.scala
🔇 Additional comments (3)
components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/StrictConversionDeterminer.scala (1)

75-82: Verify fix for incorrect subtype handling.

The implementation includes special handling for number types, which should address the issues mentioned in the PR objectives. Let's verify this fixes the specific cases.

✅ Verification successful

Based on the search results and the implementation shown in the review comment, I can now generate the final response:

Implementation correctly handles number type conversions

The implementation properly addresses number type conversions through:

  1. Primary check using ClassUtils.isAssignable for strict assignability
  2. Secondary check specifically for number types using AllNumbers index comparison, which ensures proper handling of widening conversions (e.g., Int to Long)

The test results show various type conversion scenarios being tested across the codebase, including numeric conversions in NumericUtilsSpec and type conversion validations in TypingResultSpec, confirming the robustness of the implementation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for test cases verifying number type conversions
rg -A 5 "test.*Long.*Integer|test.*Number.*Long" --type scala

# Search for related dict service tests
rg -A 5 "DictApiHttpService.*test" --type scala

Length of output: 113


Script:

#!/bin/bash
# Search for test files related to StrictConversionDeterminer
fd "StrictConversionDeterminer.*Test.*\.scala$"

# Search for test cases involving number type conversions in the test files
rg -A 10 "StrictConversionDeterminer" --type scala

# Look for the actual implementation to understand the number type handling
ast-grep --pattern 'object AllNumbers {
  $$$
}'

Length of output: 5792


Script:

#!/bin/bash
# Search for test files in the entire codebase
fd "Test.*\.scala$" --exec rg -l "StrictConversionDeterminer|number.*conversion|type.*conversion" {}

# Look for the AllNumbers definition and its usage
rg -A 5 "object AllNumbers" --type scala

# Search for any related conversion test cases
rg -A 10 "test.*conversion" --type scala

Length of output: 26120

components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/ImplicitConversionDeterminer.scala (2)

34-48: LGTM! Comprehensive type conversion handling

The implementation properly handles all type cases with exhaustive pattern matching and clear conversion logic.


Line range hint 34-270: Verify the fix addresses the original dict service issues

The implementation looks solid and should address the subtyping issues mentioned in the PR objectives. However, let's verify that this fixes the specific cases mentioned in the PR description.

✅ Verification successful

Implementation correctly handles dict type conversions

Based on the DictApiHttpServiceSpec tests, the implementation properly handles type conversions for dictionaries, including:

  • Subtyping checks for Integer types (empty list case)
  • Long type dictionary handling (returns long_dict)
  • Numeric type hierarchy (BigInteger can access long_dict)
  • String type dictionaries (rgb, bc, dict)

The tests verify that the type conversion logic in ImplicitConversionDeterminer works correctly for the dict service use cases, particularly for numeric type hierarchies which was a key concern.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for dict service tests that verify Long/Integer type handling
rg -l "DictApiHttpService.*Long.*Integer" --type scala

# Search for related test cases
ast-grep --pattern 'test("$_") { $$$
  $$$
  DictApiHttpService
  $$$
}'

Length of output: 131


Script:

#!/bin/bash
# Let's try a broader search for dict-related test files and type conversion tests
rg -l "DictApiHttpService" --type scala

# Also search for any test cases involving type conversion or subtyping
rg -l "TypeConversionHandler|canBeConvertedTo|isSubclassOf" --type scala --type test

# Look for dict-related type conversion tests
rg "Dict.*Type.*test" --type scala -A 5 -B 5

Length of output: 466


Script:

#!/bin/bash
# Let's examine the DictApiHttpServiceSpec for type conversion tests
cat designer/server/src/test/scala/pl/touk/nussknacker/ui/api/DictApiHttpServiceSpec.scala

# Also search for type conversion tests in the codebase
rg -l "TypeConversionHandler|canBeConvertedTo|isSubclassOf" --type scala

# Look for tests related to ImplicitConversionDeterminer
rg "ImplicitConversionDeterminer.*test|test.*ImplicitConversionDeterminer" --type scala -A 5 -B 5

Length of output: 10763

* simplify conversion check

* changes to TypeConversionHandler

* test fixes

---------

Co-authored-by: Marcel Philipiak <[email protected]>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (6)
components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/typing.scala (2)

37-41: Consider enhancing documentation with examples

The implementation looks good and provides a strict type conversion check. Consider adding examples to the documentation to illustrate the difference between strict and loose conversions (e.g., Int→Long vs Long→Int).

     /**
-     * Checks if the conversion to a given typingResult can be made without loss of precision
+     * Checks if the conversion to a given typingResult can be made without loss of precision.
+     * 
+     * Examples:
+     * - Int → Long: true (no precision loss)
+     * - Long → Int: false (potential precision loss)
+     * - Float → Double: true (no precision loss)
+     * - Double → Float: false (potential precision loss)
      */

30-41: Consider type conversion hierarchy documentation

The introduction of both loose and strict conversion checks creates a hierarchy of type compatibility. Consider documenting this relationship in a central location (e.g., package documentation) to help developers choose the appropriate method for their use case. This could include:

  • When to use canBeConvertedTo vs canBeStrictlyConvertedTo
  • Impact on pattern matching
  • Examples of type hierarchies and their conversion relationships
components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/TypeConversionHandler.scala (1)

24-24: Typo in comment: Correct grammatical error

Please correct the grammatical error in the comment for clarity:

- * Given to this we cannot be sure if conversion is safe or not based on type (without scale knowledge).
+ * Given this, we cannot be sure if conversion is safe or not based on type (without scale knowledge).
components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/AssignabilityDeterminer.scala (3)

10-16: Correct grammatical errors in the class documentation comments.

The class documentation contains grammatical errors that affect readability. Consider updating the comments as follows:

- * This class determine if type can be subclass of other type. It basically based on fact that TypingResults are
+ * This class determines if a type can be a subclass of another type. It is basically based on the fact that TypingResults are

37-38: Fix the typo in the method name 'isNullAsignableTo'.

The method name isNullAsignableTo contains a typo. It should be isNullAssignableTo to reflect the correct spelling.

Apply this diff to correct the method name:

- private def isNullAsignableTo(to: TypingResult): ValidatedNel[String, Unit] = to match {
+ private def isNullAssignableTo(to: TypingResult): ValidatedNel[String, Unit] = to match {

And update the method calls accordingly:

- case (TypedNull, other) => isNullAsignableTo(other)
+ case (TypedNull, other) => isNullAssignableTo(other)

Also applies to: 50-50


51-51: Address the TODO regarding null assignability to typed maps.

There's a TODO comment indicating that Null should not be a subclass of typed maps with all values assigned. Implementing this logic will improve type safety and prevent potential runtime errors.

Would you like assistance in implementing the necessary checks for this case?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 07af990 and 6716c23.

📒 Files selected for processing (5)
  • components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/AssignabilityDeterminer.scala (1 hunks)
  • components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/TypeConversionHandler.scala (4 hunks)
  • components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/typing.scala (2 hunks)
  • components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/TypingResultErrorMessagesSpec.scala (4 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/process/test/ScenarioTestService.scala (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/TypingResultErrorMessagesSpec.scala
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/process/test/ScenarioTestService.scala
🔇 Additional comments (4)
components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/typing.scala (2)

30-35: Well-documented type conversion method!

The implementation correctly delegates to AssignabilityDeterminer and the documentation clearly explains that this method allows for precision loss during conversion.


472-474: Verify impact of looser type matching

The change from canBeSubclassOf to canBeConvertedTo allows for more flexible type matching but could potentially introduce precision loss during pattern matching. We should verify all usages of CastTypedValue to ensure this change doesn't lead to unexpected behavior.

✅ Verification successful

Let me gather more information about the type conversion behavior to better understand the impact.


Based on the gathered information, I can now provide the final response:

Change from canBeSubclassOf to canBeConvertedTo is safe

The change is actually an improvement in type safety. Looking at the implementations:

  • canBeConvertedTo uses AssignabilityDeterminer.isAssignableLoose which is the correct behavior for pattern matching
  • The only usage of CastTypedValue is in Typer.scala for binary operations where looser type matching is desirable
  • There's a separate canBeStrictlyConvertedTo method available when strict type conversion is needed

The change aligns with type system best practices where pattern matching should be more permissive than direct assignments, and the implementation properly handles type conversion safety through AssignabilityDeterminer.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all usages of CastTypedValue pattern matching to assess impact

# Search for CastTypedValue pattern matching usage
echo "=== CastTypedValue pattern matching usage ==="
rg -A 5 "CastTypedValue" 

# Search for specific pattern matching cases that might be affected
echo -e "\n=== Numeric type pattern matching ==="
rg "case\s+(\w+:\s*)?(Long|Int|Double|Float)\s*=>" 

Length of output: 4836


Script:

#!/bin/bash
# Search for canBeConvertedTo and canBeSubclassOf implementations and usages
echo "=== Type conversion implementations ==="
rg -A 10 "def canBeConvertedTo|def canBeSubclassOf" 

# Search for TypedValue usage in pattern matching contexts
echo -e "\n=== TypedValue pattern matching ==="
ast-grep --pattern 'case $_ @ TypedValue($$$) =>'

Length of output: 3562

components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/TypeConversionHandler.scala (2)

6-6: Approved: Importing AllNumbers for strict number comparison

The import of AllNumbers is necessary for the updated logic in strict number conversions.


97-103: Verify the correctness of new strict conversion methods

The introduction of canBeStrictlyConvertedTo and handleStrictConversion enhances type checking. Please ensure that:

  • All necessary cases are thoroughly handled in these methods.
  • Unit tests are updated or added to cover the new logic.
  • There are no regressions or unintended side effects.

I can help review the test coverage or suggest additional test cases if needed.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (3)
components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/AssignabilityDeterminerSpec.scala (3)

9-9: Remove redundant import

The import pl.touk.nussknacker.engine.api.typed._ is redundant as it's importing from the same package where this file is located.

-import pl.touk.nussknacker.engine.api.typed._

14-22: Consider adding more edge cases to the test suite

The current test cases cover basic scenarios well. Consider enhancing the test coverage by adding:

  • Null type handling cases
  • Array type conversion cases
  • Custom type hierarchy cases
  • Generic type invariance cases

Example additions:

// Add these cases to strictConversionCases
(typing.Typed[Null], typing.Typed[AnyRef], Valid(()), Valid(())),            // Null handling
(typing.Typed[Array[Int]], typing.Typed[Array[Number]], Valid(()), Valid()), // Array variance
(typing.Typed[Dog], typing.Typed[Animal], Valid(()), Valid(()))              // Custom hierarchy

48-52: Enhance test assertions with more specific matchers

The current pattern matching with generic succeed/fail could be replaced with more specific ScalaTest matchers for better error reporting and clearer intent:

// Replace the pattern matching blocks with:
result should matchPattern {
  case Valid(_) if expectedStrict.isValid => 
  case Invalid(NonEmptyList(msg, _)) if expectedStrict.isInvalid => 
    msg should not be empty
    msg should include("Cannot")
}

This would:

  • Provide more specific failure messages
  • Validate error message content
  • Make the test intentions clearer

Also applies to: 58-63

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 6716c23 and 6f7187c.

📒 Files selected for processing (1)
  • components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/AssignabilityDeterminerSpec.scala (1 hunks)
🔇 Additional comments (1)
components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/AssignabilityDeterminerSpec.scala (1)

1-67: Verify integration with modified components

While the tests are well-structured, let's verify the integration with the modified components mentioned in the PR objectives:

✅ Verification successful

Tests are well-integrated with the type conversion system

The test suite in AssignabilityDeterminerSpec is properly integrated with the type conversion system and covers the essential scenarios:

  • The test cases align with TypeConversionHandler's implementation, covering both strict and loose conversions
  • Numeric type conversions are tested according to the NumberTypesPromotionStrategy rules
  • The test suite verifies primitive type widening, generic type variance, and collection type compatibility

The implementation shows that:

  • TypeConversionHandler is the core component handling conversion logic
  • AssignabilityDeterminer tests cover both strict and loose conversion paths
  • The test cases match the actual implementation's behavior for number type promotions and conversions
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for related changes in TypeConversionHandler and CanBeSubclassDeterminer
echo "Checking for related component changes..."
rg -l "TypeConversionHandler|CanBeSubclassDeterminer" --type scala

# Verify test coverage for the specific bug fixes mentioned in PR
echo "Verifying test coverage for mentioned bugs..."
rg -l "lang3.*canBeSubclassOf|TypeConversionHandler.*true" --type scala

Length of output: 831


Script:

#!/bin/bash
# Let's check the content of AssignabilityDeterminer to understand its relationship with TypeConversionHandler
echo "Checking AssignabilityDeterminer implementation..."
rg -A 10 "class AssignabilityDeterminer" --type scala

# Let's also check for any test files related to TypeConversionHandler
echo "Checking for TypeConversionHandler tests..."
fd -e scala -e java TypeConversionHandler.*Test
fd -e scala -e java TypeConversionHandler.*Spec

# Let's examine the actual implementation of TypeConversionHandler
echo "Checking TypeConversionHandler implementation..."
cat components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/TypeConversionHandler.scala

Length of output: 8137

val boxedSuperclassCandidate = ClassUtils.primitiveToWrapper(superclassCandidate.klass)
// TODO: This is probably wrong - relying on index of AllNumbers
(boxedGivenClass, boxedSuperclassCandidate) match {
case (f, t) if ClassUtils.isAssignable(f, t, true) => true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont think we should do this check here. We check if its a subclass in AssignabilityDeterminer.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (4)
components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/TypeConversionHandler.scala (1)

68-77: Add documentation for conversion methods.

The method renaming and new strict conversion method improve clarity, but they would benefit from ScalaDoc comments explaining:

  • The difference between loose and strict conversions
  • When to use each method
  • Examples of valid and invalid conversions

Example documentation:

/**
 * Checks if a type can be loosely converted to another type.
 * Loose conversion allows for potential precision loss, e.g., Long to Int.
 *
 * @param from the source type
 * @param to the target type
 * @return true if conversion is possible
 */
def canBeLooselyConvertedTo(from: SingleTypingResult, to: TypedClass): Boolean
components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/AssignabilityDeterminer.scala (3)

9-18: Consider enhancing documentation with examples

While the documentation clearly explains the two conversion modes, adding concrete examples would make it even more helpful. For instance, demonstrate:

  • A case of loose conversion (e.g., Integer to Long)
  • A case of strict conversion (e.g., Int to Long but not vice versa)
  • A case where loose conversion allows but strict conversion prevents type conversion

35-50: Consider pattern matching optimization

The current pattern matching implementation could be optimized by reordering the cases. Consider moving the most common cases (SingleTypingResult matches) before the TypedUnion cases to potentially reduce the number of pattern matches needed in typical scenarios.


216-216: Architectural consideration: Conversion timing

The TODO suggests that type conversions should be checked during typing rather than during runtime usage. This is an important architectural consideration as it affects both performance and type safety.

Consider moving the conversion checks to the typing phase to catch potential issues earlier in the development cycle. This would:

  1. Improve performance by reducing runtime checks
  2. Provide better developer feedback through compile-time errors
  3. Enhance type safety by preventing runtime type conversion failures
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 6f7187c and 1118186.

📒 Files selected for processing (3)
  • components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/AssignabilityDeterminer.scala (1 hunks)
  • components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/TypeConversionHandler.scala (5 hunks)
  • components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/AssignabilityDeterminerSpec.scala (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/AssignabilityDeterminerSpec.scala
🔇 Additional comments (5)
components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/TypeConversionHandler.scala (3)

6-9: LGTM! Import changes align with new functionality.

The added imports support the new strict number conversion logic and BigInteger handling.


79-87: LGTM! Clean separation of concerns.

The method properly handles primitive boxing and cleanly delegates to appropriate conversion handlers based on the strict flag.


117-125: LGTM! Improved parameter naming.

The parameter renaming improves code readability while maintaining the same functionality.

components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/AssignabilityDeterminer.scala (2)

81-81: The error message order issue remains unfixed

The order of expected and actual types in the error message is still reversed.


226-248: Verify conversion behavior with test cases

The implementation of strict and loose conversion checkers looks correct, but we should verify the behavior with comprehensive test cases.

✅ Verification successful

Conversion behavior is well-tested and correctly implemented

The implementation is verified through comprehensive test coverage and proper implementation:

  • AssignabilityDeterminerSpec covers both strict and loose conversions with extensive test cases for:

    • Widening conversions (e.g., Int to Double)
    • Narrowing conversions (e.g., Long to Int)
    • Collection type conversions
    • Generic type handling
  • TypeConversionHandler implements well-defined conversion rules:

    • Strict conversions only allow safe numeric promotions (e.g., Int to Long)
    • Loose conversions handle additional cases like decimal number conversions
    • String conversions for various types (DateTime, UUID, etc.)

The implementation correctly fulfills its purpose with proper test coverage.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find test files related to type conversion and assignability

# Look for test files
echo "Looking for related test files..."
rg -l "AssignabilityDeterminer|TypeConversionHandler" --type scala --glob "*Spec.scala" --glob "*Test.scala"

# Look for specific test cases covering conversion scenarios
echo -e "\nLooking for test cases..."
rg "test.*((strict|loose).*convert|convert.*(strict|loose))" --type scala --glob "*Spec.scala" --glob "*Test.scala"

Length of output: 576


Script:

#!/bin/bash
# Let's examine the test content to verify the conversion behavior

# First, let's look at the AssignabilityDeterminerSpec content
echo "=== AssignabilityDeterminerSpec content ==="
cat "components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/AssignabilityDeterminerSpec.scala"

# Then, let's look for any conversion-related tests in TypingResultErrorMessagesSpec
echo -e "\n=== TypingResultErrorMessagesSpec content ==="
cat "components-api/src/test/scala/pl/touk/nussknacker/engine/api/typed/TypingResultErrorMessagesSpec.scala"

# Let's also check for TypeConversionHandler implementation
echo -e "\n=== Looking for TypeConversionHandler implementation ==="
rg -p "TypeConversionHandler" --type scala -g "!*Test.scala" -g "!*Spec.scala"

Length of output: 7500


Script:

#!/bin/bash
# Let's examine the TypeConversionHandler implementation to verify the conversion logic
echo "=== TypeConversionHandler implementation ==="
cat "components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/TypeConversionHandler.scala"

Length of output: 5286

* are allowed ( Int -> Long). For other types it should work the same as a loose conversion.
*
*/
object AssignabilityDeterminer {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets add an entry about adding this to Migration Guide with and explicitly note that if you used CanBeSubclassDeterminer.canBeSubclassOf you should use AssignabilityDeterminer.isAssignableLoose

* Checks if there exists a conversion to a given typingResult, with possible loss of precision, e.g. long to int.
* If you need to retain conversion precision, use canBeStrictlyConvertedTo
*/
final def canBeConvertedTo(typingResult: TypingResult): Boolean =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets add an entry to Migration Guide canBeSubclassOf -> canBeConvertedTo

Diamekod0221 and others added 2 commits November 26, 2024 22:00
…ApiHttpServiceSpec.scala

Co-authored-by: Mateusz Słabek <[email protected]>
…yped/AssignabilityDeterminer.scala

Co-authored-by: Mateusz Słabek <[email protected]>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (3)
components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/AssignabilityDeterminer.scala (3)

9-18: Documentation needs minor improvements

The class documentation should be enhanced to better explain the conversion modes and their implications. Consider adding examples to illustrate the difference between loose and strict conversions.

Apply this diff to improve the documentation:

 /**
-  * This class determine whether we can assign one type to another type - that is if its the same class, a subclass or can be converted to another type. We provide two modes of conversion -
+  * This class determines whether one type can be assigned to another type - that is if it's the same class, a subclass, or can be converted to another type. We provide two modes of conversion:
  * 1. Loose conversion is based on the fact that TypingResults are
  * sets of possible supertypes with some additional restrictions (like TypedObjectTypingResult). It is basically how SpEL
  * can convert things. Like CommonSupertypeFinder it's in the spirit of "Be type safe as much as possible, but also provide some helpful
  * conversion for types not in the same jvm class hierarchy like boxed Integer to boxed Long and so on".
- * 2. Strict conversion checks whether we can convert the type without losing precision. Eg only widening numerical types
- * are allowed ( Int -> Long). For other types it should work the same as a loose conversion.
+ * 2. Strict conversion allows only widening conversions (e.g., Int -> Long, Float -> Double). For non-numeric types,
+ * it behaves the same as loose conversion.
+ *
+ * Examples:
+ * - Loose: Integer -> Long, Long -> Integer
+ * - Strict: Integer -> Long (allowed), Long -> Integer (not allowed)
  */

142-178: Document type parameter variance rules

The type parameter matching logic handles variance rules for different collection types, but the rules aren't explicitly documented. This could make maintenance more difficult.

Add documentation to clarify variance rules:

   def typeParametersMatches(givenClass: TypedClass, superclassCandidate: TypedClass) = {
+    /**
+     * Variance rules for collection types:
+     * - Lists: Covariant in their type parameter (List[A] is a subtype of List[B] if A is a subtype of B)
+     * - Arrays: Invariant (Array[A] is not a subtype of Array[B] even if A is a subtype of B)
+     * - Maps: Invariant in keys, covariant in values
+     */
     def canBeSubOrSuperclass(givenClassParam: TypingResult, superclassParam: TypingResult) =

216-216: Consider moving type conversions to typing phase

The TODO suggests an architectural improvement that could enhance type safety by performing conversions during the typing phase rather than during runtime checks.

This change would:

  1. Improve type safety by catching conversion issues earlier
  2. Reduce runtime overhead
  3. Make the type system more predictable

Would you like me to help create a design document for this architectural improvement?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 1118186 and ce5448a.

📒 Files selected for processing (2)
  • components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/AssignabilityDeterminer.scala (1 hunks)
  • designer/server/src/test/scala/pl/touk/nussknacker/ui/api/DictApiHttpServiceSpec.scala (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • designer/server/src/test/scala/pl/touk/nussknacker/ui/api/DictApiHttpServiceSpec.scala
🔇 Additional comments (1)
components-api/src/main/scala/pl/touk/nussknacker/engine/api/typed/AssignabilityDeterminer.scala (1)

35-50: Consider adding validation for recursive types

The isAssignable method handles various type combinations well, but it might need additional validation for recursive types to prevent potential stack overflow errors.

Let's verify if there are any recursive types in the codebase:

}

private def isNullAsignableTo(to: TypingResult): ValidatedNel[String, Unit] = to match {
// TODO: Null should not be subclass of typed map that has all values assigned.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Address TODO: Null handling in typed maps

The TODO comment indicates a potential type safety issue. This should be addressed to prevent type confusion and potential runtime errors.

Would you like me to help create a GitHub issue to track this security concern and propose a solution for proper null handling in typed maps?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
client client main fe docs ui
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants