Skip to content

chore: Dynamic class based projections for nested fields in jsonb column#36988

Merged
abhvsn merged 6 commits intopgfrom
dynamic-projections
Oct 22, 2024
Merged

chore: Dynamic class based projections for nested fields in jsonb column#36988
abhvsn merged 6 commits intopgfrom
dynamic-projections

Conversation

@abhvsn
Copy link
Contributor

@abhvsn abhvsn commented Oct 21, 2024

Description

Fixes #Issue Number
or
Fixes Issue URL

Warning

If no issue exists, please create an issue first, and check with the maintainers if the issue is valid.

/test Sanity

🔍 Cypress test results

Tip

🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
Workflow run: https://github.com/appsmithorg/appsmith/actions/runs/11452551547
Commit: 63fb7bf
Cypress dashboard.
Tags: @tag.Sanity
Spec:


Tue, 22 Oct 2024 03:51:40 UTC

Communication

Should the DevRel and Marketing teams inform users about this change?

  • Yes
  • No

Summary by CodeRabbit

  • New Features

    • Introduced a new FieldInfo record for better representation of field metadata.
    • Added utility methods in AppsmithClassUtils for class type identification.
    • Enhanced ReflectionHelpers with new methods for extracting field paths and improved tuple mapping.
    • Made the keyToExpression method public in BridgeQuery for broader access.
    • Updated query execution logic in BaseAppsmithRepositoryCEImpl to dynamically handle field paths.
    • Modified UserRecentlyUsedEntitiesProjection to a class structure with enhanced constructor logic.
    • Improved method implementations in CustomUserDataRepositoryCEImpl for accessing and modifying recently used entities.
  • Bug Fixes

    • Improved error handling in tuple mapping and field path extraction processes.

@abhvsn abhvsn changed the base branch from release to pg October 21, 2024 07:47
@github-actions github-actions bot added the skip-changelog Adding this label to a PR prevents it from being listed in the changelog label Oct 21, 2024
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 21, 2024

Walkthrough

The changes introduce new functionalities and enhancements across several classes in the Appsmith server. A new record FieldInfo is created to represent field metadata. The AppsmithClassUtils class is added for class type identification. Modifications to the ReflectionHelpers class improve tuple mapping and field path extraction capabilities. The BridgeQuery class's keyToExpression method's visibility is increased to public. Lastly, the BaseAppsmithRepositoryCEImpl class is updated to utilize dynamic field path extraction and improved query handling.

Changes

File Path Change Summary
app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/FieldInfo.java - New record added: FieldInfo(String fullPath, Class<?> type)
app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/AppsmithClassUtils.java - Class added: public class AppsmithClassUtils
- Method added: public static boolean isAppsmithProjections(Class<?> clazz)
- Method added: public static boolean isAppsmithDefinedClass(Class<?> clazz)
app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/ReflectionHelpers.java - Method signature updates for map methods
- New methods added: public static List<FieldInfo> extractFieldPaths(Class<?> projectionClass), private static List<Class<?>> fetchAllFieldTypes(Class<?> clazz), private static boolean isCollectionType(Class<?> clazz)
app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/bridge/BridgeQuery.java - Method visibility updated: public static <R> Expression<R> keyToExpression(...)
app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/BaseAppsmithRepositoryCEImpl.java - Method added: List<FieldInfo> extractFieldPaths(Class<P> projectionClass)
- Method added: Expression<?> keyToExpression(Class<?> type, Root<?> root, CriteriaBuilder cb, String fullPath)
app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/RecentlyUsedEntityDTO.java - Annotation removed: @NoArgsConstructor
app/server/appsmith-server/src/main/java/com/appsmith/server/projections/UserRecentlyUsedEntitiesProjection.java - Class modified: Converted from record to class
- Constructor added: public UserRecentlyUsedEntitiesProjection(List<Object> recentlyUsedEntityIds)
app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomUserDataRepositoryCEImpl.java - Method signature updated: Logic changes in fetchMostRecentlyUsedWorkspaceId and removeEntitiesFromRecentlyUsedList

Suggested labels

Task, ok-to-test, DB Infrastructure Pod

Suggested reviewers

  • sharat87
  • pratapaprasanna
  • ApekshaBhosale

Poem

In the land of code where records grow,
FieldInfo blooms, a data show.
With paths extracted, queries refined,
A helper class for types aligned.
From bridges to repositories, all in sync,
Appsmith's magic makes us think! ✨


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.

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
Contributor

@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: 3

🧹 Outside diff range and nitpick comments (2)
app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/AppsmithClassUtils.java (2)

3-3: Add a private constructor to prevent instantiation.

As this is a utility class with only static methods, consider adding a private constructor to prevent instantiation.

public class AppsmithClassUtils {
+    private AppsmithClassUtils() {
+        // Prevent instantiation
+    }

    // ... existing methods
}

5-7: Consider using a more specific regex pattern.

The current regex might match unintended package names. A more specific pattern could ensure better accuracy.

-    return clazz.getPackageName().matches(".*appsmith.*projections");
+    return clazz.getPackageName().matches(".*appsmith\\..*\\.projections");

This change ensures "appsmith" and "projections" are separate path components.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 4b0592a and c0b7010.

📒 Files selected for processing (5)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/FieldInfo.java (1 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/AppsmithClassUtils.java (1 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/ReflectionHelpers.java (2 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/bridge/BridgeQuery.java (1 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/BaseAppsmithRepositoryCEImpl.java (3 hunks)
🧰 Additional context used
🔇 Additional comments (5)
app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/FieldInfo.java (1)

1-3: LGTM! Excellent use of Java record for FieldInfo.

The introduction of the FieldInfo record is a clean and efficient way to represent field metadata. Using a record here provides automatic implementations of equals(), hashCode(), and toString(), which is perfect for this use case.

app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/AppsmithClassUtils.java (1)

9-11: LGTM!

The method is well-named and correctly implemented.

app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/bridge/BridgeQuery.java (1)

240-240: Visibility change: keyToExpression method is now public

The visibility of the keyToExpression method has been changed from private to public. This change allows external access to the method, which could potentially impact how it's used across the application.

Please ensure that:

  1. This change is intentional and necessary.
  2. The method's documentation (if any) is updated to reflect its public nature.
  3. Any security implications of exposing this method have been considered.
✅ Verification successful

Visibility change: keyToExpression method is now public

The keyToExpression method has been made public to allow external access, as evidenced by its usage in BaseAppsmithRepositoryCEImpl.java.

Please ensure that:

  1. This change is intentional and necessary for the application's functionality.
  2. The method's documentation has been updated to reflect its public accessibility.
  3. Any potential security implications of exposing this method have been reviewed and addressed.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify the usage of keyToExpression method outside the BridgeQuery class
rg "BridgeQuery\.keyToExpression" --type java

Length of output: 239

app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/BaseAppsmithRepositoryCEImpl.java (2)

11-11: Import statements are appropriate

The added imports for FieldInfo, extractFieldPaths, and map are necessary and correctly included.

Also applies to: 66-66, 68-68


303-309: Dynamic field path extraction implemented correctly

The use of extractFieldPaths and keyToExpression to handle nested fields in projections is well-integrated.

Copy link
Contributor

@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: 3

🧹 Outside diff range and nitpick comments (1)
app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/BaseAppsmithRepositoryCEImpl.java (1)

303-309: LGTM: Improved dynamic field handling for projections.

The changes enhance the flexibility of query construction by dynamically extracting field paths and using FieldInfo objects. This approach better supports nested fields and complex projections.

Consider adding a comment explaining the purpose of the extractFieldPaths method for better code documentation.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between c0b7010 and c242dae.

📒 Files selected for processing (3)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/AppsmithClassUtils.java (1 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/ReflectionHelpers.java (1 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/BaseAppsmithRepositoryCEImpl.java (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/AppsmithClassUtils.java
🧰 Additional context used
🔇 Additional comments (4)
app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/BaseAppsmithRepositoryCEImpl.java (2)

11-11: LGTM: New imports for improved field handling.

The new imports for FieldInfo, extractFieldPaths, and the modified import for keyToExpression are appropriate for the changes implemented in the class.

Also applies to: 66-66, 68-68


Line range hint 1-824: Overall assessment: Improved flexibility and robustness.

The changes in this file significantly enhance the handling of dynamic field paths and projections in the repository implementation. The use of FieldInfo and dynamic field path extraction improves support for nested fields and complex projections. While there are minor opportunities for improvement in documentation and code structure, the overall changes are well-implemented and consistent.

app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/ReflectionHelpers.java (2)

160-161: Correct Logical Condition in extractFieldPathsRecursively

The condition may not work as intended. Use logical AND (&&) instead of OR (||) to proceed when the field type is neither an Appsmith-defined class nor an Appsmith projection.


152-175: Add Safeguard Against StackOverflowError

Consider implementing a recursion depth limit or tracking visited classes in extractFieldPathsRecursively to prevent a potential StackOverflowError.

@github-actions
Copy link

Failed server tests

  • com.appsmith.server.repositories.CustomUserDataRepositoryTest#fetchMostRecentlyUsedWorkspaceId_withAndWithoutRecentlyUsedIds_success

Copy link
Contributor

@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 (1)
app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/ReflectionHelpers.java (1)

146-186: Improve cyclic dependency error message

The cyclic dependency detection is a good safeguard. Consider improving the error message to include the field name that caused the cycle.

Suggested improvement:

-            throw new RuntimeException("Cyclical dependency detected for: " + cyclicChain);
+            throw new RuntimeException("Cyclical dependency detected at field '" + fieldName + "' in chain: " + cyclicChain);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between c242dae and 0765c47.

📒 Files selected for processing (3)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/FieldInfo.java (1 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/AppsmithClassUtils.java (1 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/ReflectionHelpers.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/FieldInfo.java
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/AppsmithClassUtils.java
🧰 Additional context used
🔇 Additional comments (5)
app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/ReflectionHelpers.java (5)

3-18: LGTM: Import statements and class declaration

The new imports and static imports are appropriate for the added functionality and improve code readability.


81-83: LGTM: Updated map method

The changes are consistent with the modifications in the private map method and improve type safety.


94-102: LGTM: Optimized map method for multiple records

The changes improve performance by fetching field types only once for all records. The implementation is consistent with other modifications.


111-127: LGTM: New fetchAllFieldTypes method

The method correctly traverses the class hierarchy and handles private fields, providing a robust way to fetch all field types.


135-138: LGTM: New isCollectionType method

The method correctly checks for Collection and Map types, providing a useful utility for type checking.

@github-actions
Copy link

Failed server tests

  • com.appsmith.server.repositories.CustomUserDataRepositoryTest#fetchMostRecentlyUsedWorkspaceId_withAndWithoutRecentlyUsedIds_success

@abhvsn
Copy link
Contributor Author

abhvsn commented Oct 22, 2024

/build-deploy-preview skip-tests=true

@github-actions
Copy link

Deploying Your Preview: https://github.com/appsmithorg/appsmith/actions/runs/11452370160.
Workflow: On demand build Docker image and deploy preview.
skip-tests: true.
env: ``.
PR: 36988.
recreate: .

Copy link
Contributor

@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

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 0765c47 and 2629b66.

📒 Files selected for processing (4)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/RecentlyUsedEntityDTO.java (0 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/ReflectionHelpers.java (1 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/projections/UserRecentlyUsedEntitiesProjection.java (1 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomUserDataRepositoryCEImpl.java (1 hunks)
💤 Files with no reviewable changes (1)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/RecentlyUsedEntityDTO.java
🧰 Additional context used
🔇 Additional comments (9)
app/server/appsmith-server/src/main/java/com/appsmith/server/projections/UserRecentlyUsedEntitiesProjection.java (3)

10-11: Approved: Change from record to class enhances flexibility.

The transition from a record to a class with @Getter annotation maintains the immutability while allowing for custom constructor logic.


12-12: LGTM: Proper field initialization.

Good use of interface for declaration and concrete class for initialization.


14-23: Consider exception handling and future refactoring.

The constructor logic looks sound, but consider these points:

  1. Handle potential exceptions from ObjectMapper conversion.
  2. The TODO comment suggests a need for refactoring. Consider creating a ticket for this.

To ensure all usages are updated, run:

app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/ReflectionHelpers.java (5)

3-23: LGTM: Import statements and class setup look good.

The new imports and static final ObjectMapper are appropriate for the added functionality.


81-83: LGTM: Consistent with private map method changes.

The use of ArrayList aligns with the modifications in the private map method.


94-103: LGTM: Good performance optimization.

Fetching field types once for multiple records is an excellent optimization, especially for large datasets.


111-127: LGTM: Comprehensive field type fetching.

The method correctly handles the class hierarchy and private fields.


135-138: LGTM: Correct implementation for checking collection types.

The method accurately identifies Collection and Map types.

app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CustomUserDataRepositoryCEImpl.java (1)

73-75: Code change looks good

The method correctly retrieves the most recently used workspace ID.

Comment on lines +146 to +186
public static List<FieldInfo> extractFieldPaths(Class<?> projectionClass) {
List<FieldInfo> fieldPaths = new ArrayList<>();
List<Class<?>> visitedClasses = new ArrayList<>();
extractFieldPathsRecursively(projectionClass, "", fieldPaths, visitedClasses);
return fieldPaths;
}

private static void extractFieldPathsRecursively(
Class<?> clazz, String parentPath, List<FieldInfo> fieldPaths, List<Class<?>> visitedClasses) {
// Check if the class has already been visited to prevent cyclic dependencies
if (visitedClasses.contains(clazz)) {
String cyclicChain = String.join(
" -> ", visitedClasses.stream().map(Class::getName).toArray(String[]::new));
throw new RuntimeException("Cyclical dependency detected for: " + cyclicChain);
}
// Process the class and its superclasses
while (clazz != null && clazz != Object.class) {
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true); // Ensure access to private fields
String fieldName = field.getName();
String fullPath = parentPath.isEmpty() ? fieldName : dotted(parentPath, fieldName);
Class<?> fieldType = field.getType();

if (isAppsmithProjections(fieldType)) {
visitedClasses.add(fieldType);
extractFieldPathsRecursively(field.getType(), fullPath, fieldPaths, visitedClasses);
} else {
// Check if the field type is part of JdbcType.getDdlTypeCode if not assign the Object as the type
if (isPrimitiveWrapper(field.getType()) || String.class.equals(field.getType())) {
fieldPaths.add(new FieldInfo(fullPath, field.getType()));
} else {
fieldPaths.add(new FieldInfo(fullPath, Object.class));
}
}
}
// Move to superclass (if any)
clazz = clazz.getSuperclass();
}
// Remove the class from the visited set to allow revisiting in different branches
visitedClasses.remove(clazz);
}
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 22, 2024

Choose a reason for hiding this comment

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

⚠️ Potential issue

Consider more graceful handling of cyclic dependencies

While the cyclic dependency detection is good, throwing a runtime exception might be too severe. Consider logging a warning and skipping the cyclic path instead.

Suggested modification:

-            throw new RuntimeException("Cyclical dependency detected for: " + cyclicChain);
+            System.err.println("Warning: Cyclical dependency detected for: " + cyclicChain);
+            return;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static List<FieldInfo> extractFieldPaths(Class<?> projectionClass) {
List<FieldInfo> fieldPaths = new ArrayList<>();
List<Class<?>> visitedClasses = new ArrayList<>();
extractFieldPathsRecursively(projectionClass, "", fieldPaths, visitedClasses);
return fieldPaths;
}
private static void extractFieldPathsRecursively(
Class<?> clazz, String parentPath, List<FieldInfo> fieldPaths, List<Class<?>> visitedClasses) {
// Check if the class has already been visited to prevent cyclic dependencies
if (visitedClasses.contains(clazz)) {
String cyclicChain = String.join(
" -> ", visitedClasses.stream().map(Class::getName).toArray(String[]::new));
throw new RuntimeException("Cyclical dependency detected for: " + cyclicChain);
}
// Process the class and its superclasses
while (clazz != null && clazz != Object.class) {
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true); // Ensure access to private fields
String fieldName = field.getName();
String fullPath = parentPath.isEmpty() ? fieldName : dotted(parentPath, fieldName);
Class<?> fieldType = field.getType();
if (isAppsmithProjections(fieldType)) {
visitedClasses.add(fieldType);
extractFieldPathsRecursively(field.getType(), fullPath, fieldPaths, visitedClasses);
} else {
// Check if the field type is part of JdbcType.getDdlTypeCode if not assign the Object as the type
if (isPrimitiveWrapper(field.getType()) || String.class.equals(field.getType())) {
fieldPaths.add(new FieldInfo(fullPath, field.getType()));
} else {
fieldPaths.add(new FieldInfo(fullPath, Object.class));
}
}
}
// Move to superclass (if any)
clazz = clazz.getSuperclass();
}
// Remove the class from the visited set to allow revisiting in different branches
visitedClasses.remove(clazz);
}
public static List<FieldInfo> extractFieldPaths(Class<?> projectionClass) {
List<FieldInfo> fieldPaths = new ArrayList<>();
List<Class<?>> visitedClasses = new ArrayList<>();
extractFieldPathsRecursively(projectionClass, "", fieldPaths, visitedClasses);
return fieldPaths;
}
private static void extractFieldPathsRecursively(
Class<?> clazz, String parentPath, List<FieldInfo> fieldPaths, List<Class<?>> visitedClasses) {
// Check if the class has already been visited to prevent cyclic dependencies
if (visitedClasses.contains(clazz)) {
String cyclicChain = String.join(
" -> ", visitedClasses.stream().map(Class::getName).toArray(String[]::new));
System.err.println("Warning: Cyclical dependency detected for: " + cyclicChain);
return;
}
// Process the class and its superclasses
while (clazz != null && clazz != Object.class) {
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true); // Ensure access to private fields
String fieldName = field.getName();
String fullPath = parentPath.isEmpty() ? fieldName : dotted(parentPath, fieldName);
Class<?> fieldType = field.getType();
if (isAppsmithProjections(fieldType)) {
visitedClasses.add(fieldType);
extractFieldPathsRecursively(field.getType(), fullPath, fieldPaths, visitedClasses);
} else {
// Check if the field type is part of JdbcType.getDdlTypeCode if not assign the Object as the type
if (isPrimitiveWrapper(field.getType()) || String.class.equals(field.getType())) {
fieldPaths.add(new FieldInfo(fullPath, field.getType()));
} else {
fieldPaths.add(new FieldInfo(fullPath, Object.class));
}
}
}
// Move to superclass (if any)
clazz = clazz.getSuperclass();
}
// Remove the class from the visited set to allow revisiting in different branches
visitedClasses.remove(clazz);
}

Copy link
Contributor Author

@abhvsn abhvsn Oct 22, 2024

Choose a reason for hiding this comment

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

It's better to fail early in such scenario so that devs can come up with better datastructure.

Copy link
Contributor

Choose a reason for hiding this comment

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

@abhvsn, understood. Failing early in such scenarios makes sense to encourage better data structures. Thanks for clarifying.

(_/)
( -_-) thinking...


✏️ Learnings added
Learnt from: abhvsn
PR: appsmithorg/appsmith#36988
File: app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/ReflectionHelpers.java:149-189
Timestamp: 2024-10-22T03:15:41.280Z
Learning: In methods like `extractFieldPathsRecursively` in `ReflectionHelpers.java`, when detecting cyclic dependencies, we prefer to fail early by throwing a `RuntimeException` to encourage developers to design better data structures.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@github-actions
Copy link

Deploy-Preview-URL: https://ce-36988.dp.appsmith.com

@abhvsn abhvsn added the ok-to-test Required label for CI label Oct 22, 2024
Copy link
Contributor

@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: 0

🧹 Outside diff range and nitpick comments (2)
app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/ReflectionHelpers.java (2)

35-70: LGTM: Improved object mapping logic.

The new implementation handles Appsmith projections and performs deep copying, enhancing flexibility. Consider using List<Object> instead of ArrayList<Object> in the method signature for better abstraction.


149-189: LGTM: New methods for extracting field paths.

The extractFieldPaths and extractFieldPathsRecursively methods provide crucial functionality for working with projection classes. However, consider logging a warning instead of throwing an exception for cyclic dependencies to allow for more graceful handling.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 2629b66 and 63fb7bf.

📒 Files selected for processing (1)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/ReflectionHelpers.java (1 hunks)
🧰 Additional context used
🔇 Additional comments (5)
app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/ReflectionHelpers.java (5)

3-4: LGTM: New imports added for enhanced functionality.

The new imports support the added features in the class.

Also applies to: 10-11, 13-18


84-86: LGTM: Updated to use new mapping logic.

The changes are consistent with the updates to the main map method.


97-105: LGTM: Performance improvement in mapping multiple records.

Fetching field types once for all records reduces redundant operations. Good optimization!


114-130: LGTM: New method for fetching field types.

The fetchAllFieldTypes method correctly handles class hierarchy, supporting the improved mapping logic.


138-141: LGTM: Utility method for checking collection types.

The isCollectionType method provides a clean way to identify collection types, supporting the mapping logic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ok-to-test Required label for CI skip-changelog Adding this label to a PR prevents it from being listed in the changelog

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant