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

chore: constrain the diff on only affected JS actions #33435

Merged
merged 3 commits into from
May 27, 2024

Conversation

vsvamsi1
Copy link
Contributor

@vsvamsi1 vsvamsi1 commented May 14, 2024

Description

Perform diff of only the affected JS collections not the entire list of JS collections and for non JS updates evaluation we should not perform a diff on any JS collection.. This should improve the p100 of each evaluation.

Fixes #33508

Warning

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

Automation

/ok-to-test tags="@tag.All"

🔍 Cypress test results

Tip

🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
Workflow run: https://github.com/appsmithorg/appsmith/actions/runs/9248167182
Commit: e178485
Cypress dashboard url: Click here!

Communication

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

  • Yes
  • No

@vsvamsi1 vsvamsi1 self-assigned this May 14, 2024
@vsvamsi1 vsvamsi1 marked this pull request as draft May 14, 2024 11:13
Copy link
Contributor

coderabbitai bot commented May 14, 2024

Walkthrough

Walkthrough

The changes optimize the evaluation process for JavaScript (JS) collections by introducing mechanisms to handle only the affected JS objects during evaluations, rather than evaluating the entire list. This involves creating constants, functions, and parameters to identify and process affected JS objects, thereby improving performance.

Changes

Files/Groups Change Summary
evaluationActionsList.ts Consolidated JS action types into a constant array JS_ACTIONS and used it in EVALUATE_REDUX_ACTIONS.
ReduxActionConstants.tsx Added a new interface BufferedReduxAction<T> with an affectedJSObjects field.
EvaluationsSagaUtils.ts & InferAffectedJSObjects.ts (CE & EE) Introduced functions to handle and extract affected JS object IDs from actions.
EvaluationsSaga.ts Added handling for affectedJSObjects in various functions and parameters.
EvaluationsSaga.test.ts Added imports for testing affected JS objects handling.
EvaluationsSagaUtils.test.ts Introduced test cases for getAffectedJSObjectIdsFromAction function.
generateOpimisedUpdates.test.ts Updated import for parseUpdatesAndDeleteUndefinedUpdates to reflect new file structure.
evalTree.ts & evalTrigger.ts Added affectedJSObjects parameter to function calls.
types.ts Added affectedJSObjects property to EvalTreeRequestData interface.
DataTreeEvaluator/index.ts Added affectedJSObjects parameter to DataTreeEvaluator class.
DataTreeEvaluator/utils.test.ts Introduced test suite for getOnlyAffectedJSObjects function.
DataTreeEvaluator/utils.ts Implemented getOnlyAffectedJSObjects function to filter JSActionEntities based on affected IDs.

Sequence Diagram(s) (Beta)

sequenceDiagram
    participant Client
    participant Saga
    participant Evaluator
    Client->>Saga: Dispatch JS Action
    Saga->>Evaluator: Extract affected JS objects
    Evaluator->>Saga: Return affected JS objects
    Saga->>Client: Perform constrained diff and update

Assessment against linked issues

Objective Addressed Explanation
Perform diff of only the affected JS collections (33508)
Skip JS collection diff for non-JS updates (33508)

Tip

New Features and Improvements

Review Settings

Introduced new personality profiles for code reviews. Users can now select between "Chill" and "Assertive" review tones to tailor feedback styles according to their preferences. The "Assertive" profile posts more comments and nitpicks the code more aggressively, while the "Chill" profile is more relaxed and posts fewer comments.

AST-based Instructions

CodeRabbit offers customizing reviews based on the Abstract Syntax Tree (AST) pattern matching. Read more about AST-based instructions in the documentation.

Community-driven AST-based Rules

We are kicking off a community-driven initiative to create and share AST-based rules. Users can now contribute their AST-based rules to detect security vulnerabilities, code smells, and anti-patterns. Please see the ast-grep-essentials repository for more information.

New Static Analysis Tools

We are continually expanding our support for static analysis tools. We have added support for biome, hadolint, and ast-grep. Update the settings in your .coderabbit.yaml file or head over to the settings page to enable or disable the tools you want to use.

Tone Settings

Users can now customize CodeRabbit to review code in the style of their favorite characters or personalities. Here are some of our favorite examples:

  • Mr. T: "You must talk like Mr. T in all your code reviews. I pity the fool who doesn't!"
  • Pirate: "Arr, matey! Ye must talk like a pirate in all yer code reviews. Yarrr!"
  • Snarky: "You must be snarky in all your code reviews. Snark, snark, snark!"

Revamped Settings Page

We have redesigned the settings page for a more intuitive layout, enabling users to find and adjust settings quickly. This change was long overdue; it not only improves the user experience but also allows our development team to add more settings in the future with ease. Going forward, the changes to .coderabbit.yaml will be reflected in the settings page, and vice versa.

Miscellaneous

  • Turn off free summarization: You can switch off free summarization of PRs opened by users not on a paid plan using the enable_free_tier setting.
  • Knowledge-base scope: You can now set the scope of the knowledge base to either the repository (local) or the organization (global) level using the knowledge_base setting. In addition, you can specify Jira project keys and Linear team keys to limit the knowledge base scope for those integrations.
  • High-level summary placement: You can now customize the location of the high-level summary in the PR description using the high_level_summary_placeholder setting (default @coderabbitai summary).
  • Revamped request changes workflow: You can now configure CodeRabbit to auto-approve or request changes on PRs based on the review feedback using the request_changes_workflow setting.

Recent Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between 71e302b and e178485.
Files selected for processing (3)
  • app/client/src/ce/sagas/InferAffectedJSObjects.ts (1 hunks)
  • app/client/src/ee/sagas/InferAffectedJSObjects.ts (1 hunks)
  • app/client/src/sagas/EvaluationsSagaUtils.ts (1 hunks)
Files skipped from review due to trivial changes (1)
  • app/client/src/ee/sagas/InferAffectedJSObjects.ts
Additional Context Used
Biome (3)
app/client/src/sagas/EvaluationsSagaUtils.ts (3)

23-23: Unexpected any. Specify a different type.


23-23: Unexpected any. Specify a different type.


63-63: Reassigning a function parameter is confusing.

Additional comments not posted (4)
app/client/src/ce/sagas/InferAffectedJSObjects.ts (3)

13-47: The function getAffectedJSObjectIdsFromJSAction is well-implemented and aligns with the PR's objectives to optimize JS collection evaluations.


50-66: The function getAffectedJSObjectIdsFromBufferedAction correctly handles the extraction of affected JS objects from buffered actions, supporting the PR's goal of optimizing evaluations.


68-71: The constant AFFECTED_JS_OBJECTS_FNS is correctly defined and includes appropriate functions for handling affected JS objects.

app/client/src/sagas/EvaluationsSagaUtils.ts (1)

48-51: The interface AffectedJSObjects is well-defined and supports the functionality required for handling affected JS objects.


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>.
    • 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 generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @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.

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 as 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 full the 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 help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration 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.

@vsvamsi1 vsvamsi1 added the ok-to-test Required label for CI label May 14, 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: 2

Out of diff range and nitpick comments (1)
app/client/src/workers/common/DataTreeEvaluator/index.ts (1)

478-493: Ensure consistent naming conventions.

The method name getOnlyAffectJsWidgets should be getOnlyAffectedJsWidgets to maintain consistency and clarity.

@@ -484,6 +500,7 @@ export default class DataTreeEvaluator {
unEvalTree: any,
configTree: ConfigTree,
webworkerTelemetry: Record<string, WebworkerSpanData> = {},
onlyAffectedJSWidgets: string[] | undefined = undefined,
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider adding a JSDoc comment for the new parameter.

Add a JSDoc comment for the onlyAffectedJSWidgets parameter to explain its purpose and usage.

/**
 * @param onlyAffectedJSWidgets - List of affected JS widgets to limit the scope of updates.
 */

Comment on lines 532 to 530
microDiff(
this.getOnlyAffectJsWidgets(
oldUnEvalTreeJSCollections,
onlyAffectedJSWidgets,
),
this.getOnlyAffectJsWidgets(
localUnEvalTreeJSCollection,
onlyAffectedJSWidgets,
),
) || [],
Copy link
Contributor

Choose a reason for hiding this comment

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

Ensure proper error handling.

The getOnlyAffectJsWidgets method calls should include error handling to manage potential issues with undefined or malformed inputs.

const oldUnEvalTreeJSCollections = this.getOnlyAffectJsWidgets(
  oldUnEvalTreeJSCollections,
  onlyAffectedJSWidgets,
);
const localUnEvalTreeJSCollection = this.getOnlyAffectJsWidgets(
  localUnEvalTreeJSCollection,
  onlyAffectedJSWidgets,
);

Consider wrapping these calls in a try-catch block or adding validation checks.

@vsvamsi1 vsvamsi1 changed the title Test if constrained diff working chore: Test if constrained diff working May 15, 2024
@github-actions github-actions bot added the skip-changelog Adding this label to a PR prevents it from being listed in the changelog label May 15, 2024
@vsvamsi1 vsvamsi1 added ok-to-test Required label for CI and removed ok-to-test Required label for CI labels May 15, 2024
@vsvamsi1 vsvamsi1 removed the request for review from ApekshaBhosale May 15, 2024 09:06
@vsvamsi1
Copy link
Contributor Author

/build-deploy-preview skip-tests=true

Copy link

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

Copy link

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

@vsvamsi1
Copy link
Contributor Author

/ci-test-limit

Copy link

@vsvamsi1
Copy link
Contributor Author

/ci-test-limit

@vsvamsi1 vsvamsi1 added ok-to-test Required label for CI and removed ok-to-test Required label for CI labels May 16, 2024
Copy link

Copy link

Workflow run: https://github.com/appsmithorg/appsmith/actions/runs/9113153223.
Cypress dashboard url: Click here!
All cypress tests have passed 🎉🎉🎉

@vsvamsi1 vsvamsi1 added ok-to-test Required label for CI and removed ok-to-test Required label for CI labels May 16, 2024
@vsvamsi1 vsvamsi1 marked this pull request as ready for review May 16, 2024 18:01
@vsvamsi1 vsvamsi1 requested a review from ayushpahwa as a code owner May 16, 2024 18:01
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

Out of diff range and nitpick comments (1)
app/client/cypress/limited-tests.txt (1)

Line range hint 6-6: Add the missing article "the".

- #ci-test-limit uses this file to run minimum of specs. Do not run entire suite with this command.
+ #ci-test-limit uses this file to run the minimum of specs. Do not run the entire suite with this command.

Comment on lines 1170 to 1174
export interface BufferedReduxAction<T> {
affectedJSActionIds?: string[];
type: ReduxActionType | ReduxActionErrorType;
payload: T;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider adding a comment to explain the purpose of the affectedJSActionIds property in the BufferedReduxAction interface.

+ // Optional array of affected JS action IDs for tracking purposes
  affectedJSActionIds?: string[];

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.

Suggested change
export interface BufferedReduxAction<T> {
affectedJSActionIds?: string[];
type: ReduxActionType | ReduxActionErrorType;
payload: T;
}
export interface BufferedReduxAction<T> {
// Optional array of affected JS action IDs for tracking purposes
affectedJSActionIds?: string[];
type: ReduxActionType | ReduxActionErrorType;
payload: T;
}

@vsvamsi1 vsvamsi1 requested review from dvj1988 and removed request for ayushpahwa May 17, 2024 03:28
@vsvamsi1 vsvamsi1 force-pushed the constrainedSetup branch 2 times, most recently from ffcb97e to 910d78d Compare May 22, 2024 13:04
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

Outside diff range and nitpick comments (3)
app/client/src/workers/common/DataTreeEvaluator/utils.test.ts (1)

1-3: Ensure consistent import grouping and ordering.

Consider grouping and ordering imports by their source or relevance to improve readability and maintainability.

app/client/src/sagas/EvaluationSaga.utils.ts (1)

Line range hint 491-505: Optimize the merging of AffectedJSObjects.

The function mergeJSBufferedActions can be optimized by directly returning the new object when isAllAffected is true for either argument. This avoids unnecessary array operations:

- if (prevAffectedJSAction.isAllAffected || newAffectedJSAction.isAllAffected) {
-   return {
-     isAllAffected: true,
-     ids: [],
-   };
- }
+ return (prevAffectedJSAction.isAllAffected || newAffectedJSAction.isAllAffected) ? { isAllAffected: true, ids: [] } : {
+   isAllAffected: false,
+   ids: [...prevAffectedJSAction.ids, ...newAffectedJSAction.ids],
+ };
app/client/src/sagas/EvaluationsSaga.test.ts (1)

Line range hint 1-16: Ensure consistent import grouping and ordering.

Consider grouping and ordering imports by their source or relevance to improve readability and maintainability.

Comment on lines 106 to 110
import type { AffectedJSObjects } from "./EvaluationSaga.utils";
import {
getAffectedJSObjectIdsFromAction,
getAffectedJSObjectIdsFromJSAction,
parseUpdatesAndDeleteUndefinedUpdates,
} from "./EvaluationSaga.utils";
Copy link
Contributor

Choose a reason for hiding this comment

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

Use specific types instead of any.

The use of any type for affectedJSObjects in the import statement reduces type safety. It would be beneficial to define a more specific type structure for affectedJSObjects to ensure better type checking and to avoid potential runtime errors.

@brayn003 brayn003 added ok-to-test Required label for CI and removed ok-to-test Required label for CI labels May 23, 2024
@vsvamsi1 vsvamsi1 added ok-to-test Required label for CI and removed ok-to-test Required label for CI labels May 24, 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: 10

Outside diff range and nitpick comments (12)
app/client/src/workers/common/DataTreeEvaluator/utils.ts (2)

Line range hint 52-52: Remove unnecessary continue statement.

- if (!isJSAction(entity)) continue;
+ if (!isJSAction(entity)) return;

The continue statement is unnecessary here since it's already the last statement in the loop's current iteration.


Line range hint 79-79: Specify a more accurate type instead of any.

- entity: DataTreeEntity,
+ entity: SpecificEntityType,

Using any can lead to potential type safety issues. It's better to specify a more accurate type.

app/client/src/sagas/EvaluationsSaga.ts (1)

Line range hint 255-288: Consider refining the default parameter placement in evaluateTreeSaga.

The default parameter affectedJSObjects should ideally follow the last required parameter or be made a required parameter to avoid confusion and potential errors in function calls.

- affectedJSObjects: AffectedJSObjects = defaultAffectedJSObjects,
+ affectedJSObjects: AffectedJSObjects,
app/client/src/workers/common/DataTreeEvaluator/index.ts (5)

115-119: Consider adding a JSDoc comment for the new imports to explain their purpose and usage.

/**
 * @module utils
 * - getFixedTimeDifference: Calculates the fixed time difference between two timestamps.
 * - getOnlyAffectedJSObjects: Filters JSActionEntities based on affected action IDs.
 * - replaceThisDotParams: Replaces 'this.' with 'params.' in JS object methods.
 */

Line range hint 467-480: Replace forEach with for...of for better performance and readability.

- Object.keys(unEvalJSCollection).forEach((update) => {
+ for (const update of Object.keys(unEvalJSCollection)) {
    const updates = unEvalJSCollection[update];
    if (!!unevalTree[update]) {
-     Object.keys(updates).forEach((key) => {
+     for (const key of Object.keys(updates)) {
        const data = get(unevalTree, `${update}.${key}.data`, undefined);
        if (isJSObjectFunction(unevalTree, update, key, configTree)) {
          set(unevalTree, `${update}.${key}`, new String(updates[key]));
          set(unevalTree, `${update}.${key}.data`, data);
        } else {
          set(unevalTree, `${update}.${key}`, updates[key]);
        }
      }
    }
  }

Also applies to: 470-478


Line range hint 783-785: Replace forEach with for...of in multiple locations for consistency and performance.

- differences.forEach((diff) => {
+ for (const diff of differences) {
    if (!Array.isArray(diff.path) || diff.path.length === 0) continue; // Null check for typescript
    // Apply the changes into the evalTree so that it gets the latest changes
    applyChange(this.evalTree, undefined, diff);
  }

Also applies to: 930-936, 945-951, 969-976, 985-993


Line range hint 3-3: Avoid shadowing the global EvalError property. Consider renaming the imported or locally declared EvalError to avoid confusion and potential bugs.

import type {
  DataTreeEvaluationProps,
  EvaluationError as EvalError, // Renamed to avoid shadowing the global EvalError
} from "utils/DynamicBindingUtils";

Line range hint 1306-1342: This else clause can be omitted because previous branches break early, simplifying the control flow.

- if (condition) {
-   // code
-   break;
- } else {
-   // other code
- }
+ if (condition) {
+   // code
+   break;
+ }
+ // other code
app/client/src/ce/constants/ReduxActionConstants.tsx (4)

19-19: Add a brief comment describing the purpose of AffectedJSObjects.

Adding a comment for the AffectedJSObjects import will help maintainers understand its usage context quickly.


Line range hint 1196-1196: Specify a more appropriate type instead of any for reject.

-  reject: any;
+  reject: (reason?: any) => void;

Using any is generally discouraged due to the lack of type safety. Specifying a function type for reject enhances clarity and type safety.


Line range hint 1197-1197: Specify a more appropriate type instead of any for resolve.

-  resolve: any;
+  resolve: (value?: unknown) => void;

Using any is generally discouraged due to the lack of type safety. Specifying a function type for resolve enhances clarity and type safety.


Line range hint 1289-1289: Specify a more appropriate type instead of any for applications.

-  applications: any[];
+  applications: ApplicationPayload[];

Using any is generally discouraged due to the lack of type safety. Specifying ApplicationPayload[] for applications enhances clarity and type safety.

Comment on lines 106 to 110
import type { AffectedJSObjects } from "./EvaluationSaga.utils";
import {
getAffectedJSObjectIdsFromAction,
parseUpdatesAndDeleteUndefinedUpdates,
} from "./EvaluationSaga.utils";
Copy link
Contributor

Choose a reason for hiding this comment

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

Use specific types instead of any.

The use of any type for affectedJSObjects in the import statement reduces type safety. It would be beneficial to define a more specific type structure for affectedJSObjects to ensure better type checking and to avoid potential runtime errors.

Comment on lines +541 to +547
// extract the affected JS action ids from the action and pass them
// as a part of the buffered action
const affectedJSObjects = getAffectedJSObjectIdsFromAction(action);
collectedAffectedJSObjects = mergeJSBufferedActions(
collectedAffectedJSObjects,
affectedJSObjects,
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Clarify the handling of affectedJSObjects in buffered actions.

The comments and code in the put function of evalQueueBuffer could be enhanced for better clarity. Specifically, the comment about extracting affected JS action IDs could be more descriptive about how and why this is done:

- // extract the affected JS action ids from the action and pass them
- //  as a part of the buffered action
+ // Extract affected JS action IDs from the action to ensure that only relevant JS actions are processed in the buffered action.

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.

Suggested change
// extract the affected JS action ids from the action and pass them
// as a part of the buffered action
const affectedJSObjects = getAffectedJSObjectIdsFromAction(action);
collectedAffectedJSObjects = mergeJSBufferedActions(
collectedAffectedJSObjects,
affectedJSObjects,
);
// Extract affected JS action IDs from the action to ensure that only relevant JS actions are processed in the buffered action.
const affectedJSObjects = getAffectedJSObjectIdsFromAction(action);
collectedAffectedJSObjects = mergeJSBufferedActions(
collectedAffectedJSObjects,
affectedJSObjects,
);

Comment on lines +1172 to +1174
export interface BufferedReduxAction<T> extends ReduxAction<T> {
affectedJSObjects: AffectedJSObjects;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Define the BufferedReduxAction interface more clearly.

- export interface BufferedReduxAction<T> extends ReduxAction<T> {
-   affectedJSObjects: AffectedJSObjects;
- }
+ /**
+  * Represents a Redux action that is buffered and includes information about affected JavaScript objects.
+  * @template T The type of the payload.
+  */
+ export interface BufferedReduxAction<T> extends ReduxAction<T> {
+   affectedJSObjects: AffectedJSObjects;
+ }

Adding documentation comments will improve code readability and maintainability.


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.

Suggested change
export interface BufferedReduxAction<T> extends ReduxAction<T> {
affectedJSObjects: AffectedJSObjects;
}
/**
* Represents a Redux action that is buffered and includes information about affected JavaScript objects.
* @template T The type of the payload.
*/
export interface BufferedReduxAction<T> extends ReduxAction<T> {
affectedJSObjects: AffectedJSObjects;
}

Comment on lines 13 to 48
export function getAffectedJSObjectIdsFromJSAction(
action: ReduxAction<unknown> | BufferedReduxAction<unknown>,
): AffectedJSObjects {
if (!JS_ACTIONS.includes(action.type)) {
return {
ids: [],
isAllAffected: false,
};
}
// only JS actions here
action as ReduxAction<unknown>;
// When fetching JSActions fails, we need to diff all JSObjects because the reducer updates it
// to empty collection
if (
action.type === ReduxActionErrorTypes.FETCH_JS_ACTIONS_ERROR ||
action.type === ReduxActionErrorTypes.FETCH_JS_ACTIONS_VIEW_MODE_ERROR
) {
return {
isAllAffected: true,
ids: [],
};
}

const { payload } = action as ReduxAction<{
data: JSCollection;
}> &
ReduxAction<JSCollection>;
// some actions have within data property of the action payload, we need to extract it from there
const innerData = payload?.data || payload;

const ids = Array.isArray(innerData)
? innerData.map(({ id }) => id)
: [innerData.id];

return { ids, isAllAffected: false };
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Ensure proper type annotations for better type safety.

- action: ReduxAction<unknown> | BufferedReduxAction<unknown>,
+ action: ReduxAction<anySpecificType> | BufferedReduxAction<anySpecificType>,

Consider replacing unknown with more specific types to enhance type safety and code maintainability.

Committable suggestion was skipped due low confidence.

Comment on lines 50 to 66
function getAffectedJSObjectIdsFromBufferedAction(
action: ReduxAction<unknown> | BufferedReduxAction<unknown>,
): AffectedJSObjects {
if (action.type !== ReduxActionTypes.BUFFERED_ACTION) {
return {
ids: [],
isAllAffected: false,
};
}
// only Buffered actions here
return (
(action as BufferedReduxAction<unknown>).affectedJSObjects || {
ids: [],
isAllAffected: false,
}
);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Refactor to avoid duplication and enhance clarity.

- if (action.type !== ReduxActionTypes.BUFFERED_ACTION) {
-   return {
-     ids: [],
-     isAllAffected: false,
-   };
- }
- // only Buffered actions here
- return (
-   (action as BufferedReduxAction<unknown>).affectedJSObjects || {
-     ids: [],
-     isAllAffected: false,
-   }
- );
+ return action.type === ReduxActionTypes.BUFFERED_ACTION
+   ? (action as BufferedReduxAction<unknown>).affectedJSObjects || { ids: [], isAllAffected: false }
+   : { ids: [], isAllAffected: false };

This change reduces the complexity and increases the readability of the function.


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.

Suggested change
function getAffectedJSObjectIdsFromBufferedAction(
action: ReduxAction<unknown> | BufferedReduxAction<unknown>,
): AffectedJSObjects {
if (action.type !== ReduxActionTypes.BUFFERED_ACTION) {
return {
ids: [],
isAllAffected: false,
};
}
// only Buffered actions here
return (
(action as BufferedReduxAction<unknown>).affectedJSObjects || {
ids: [],
isAllAffected: false,
}
);
}
function getAffectedJSObjectIdsFromBufferedAction(
action: ReduxAction<unknown> | BufferedReduxAction<unknown>,
): AffectedJSObjects {
return action.type === ReduxActionTypes.BUFFERED_ACTION
? (action as BufferedReduxAction<unknown>).affectedJSObjects || { ids: [], isAllAffected: false }
: { ids: [], isAllAffected: false };
}

@@ -16,6 +16,7 @@
import type { DataTreeDiff } from "@appsmith/workers/Evaluation/evaluationUtils";
import type { APP_MODE } from "entities/App";
import type { WebworkerSpanData } from "UITelemetry/generateWebWorkerTraces";
import type { AffectedJSObjects } from "sagas/EvaluationSaga.utils";
Copy link
Contributor

Choose a reason for hiding this comment

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

Specify a more accurate type instead of any.

- export type EvalWorkerSyncRequest<T = any> = WorkerRequest<
+ export type EvalWorkerSyncRequest<T = SpecificType> = WorkerRequest<

Using any can lead to potential type safety issues. It's better to specify a more accurate type.

Committable suggestion was skipped due low confidence.

Comment on lines 53 to 73
const mergeAffectedJSObjects = (
action: ReduxAction<unknown> | BufferedReduxAction<unknown>,
) => {
return affectedJSObjectsFns.reduce(
(acc, affectedJSObjectsFn) => {
// when either of the action isAllJSObjectsAffected return true.
// In this case perform diff on all js objects
if (acc.isAllAffected) {
return acc;
}
acc = {
isAllAffected:
acc.isAllAffected || affectedJSObjectsFn(action).isAllAffected,
ids: [...acc.ids, ...affectedJSObjectsFn(action).ids],
};

return acc;
},
{ ids: [], isAllAffected: false } as AffectedJSObjects,
);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider refactoring to avoid reassigning function parameters.

- acc = {
-   isAllAffected:
-     acc.isAllAffected || affectedJSObjectsFn(action).isAllAffected,
-   ids: [...acc.ids, ...affectedJSObjectsFn(action).ids],
- };
+ const result = affectedJSObjectsFn(action);
+ return {
+   isAllAffected: acc.isAllAffected || result.isAllAffected,
+   ids: [...acc.ids, ...result.ids],
+ };

Reassigning function parameters can lead to unexpected behavior and makes the code harder to understand. This refactor avoids direct mutation of the accumulator.


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.

Suggested change
const mergeAffectedJSObjects = (
action: ReduxAction<unknown> | BufferedReduxAction<unknown>,
) => {
return affectedJSObjectsFns.reduce(
(acc, affectedJSObjectsFn) => {
// when either of the action isAllJSObjectsAffected return true.
// In this case perform diff on all js objects
if (acc.isAllAffected) {
return acc;
}
acc = {
isAllAffected:
acc.isAllAffected || affectedJSObjectsFn(action).isAllAffected,
ids: [...acc.ids, ...affectedJSObjectsFn(action).ids],
};
return acc;
},
{ ids: [], isAllAffected: false } as AffectedJSObjects,
);
};
const mergeAffectedJSObjects = (
action: ReduxAction<unknown> | BufferedReduxAction<unknown>,
) => {
return affectedJSObjectsFns.reduce(
(acc, affectedJSObjectsFn) => {
// when either of the action isAllJSObjectsAffected return true.
// In this case perform diff on all js objects
if (acc.isAllAffected) {
return acc;
}
const result = affectedJSObjectsFn(action);
return {
isAllAffected: acc.isAllAffected || result.isAllAffected,
ids: [...acc.ids, ...result.ids],
};
},
{ ids: [], isAllAffected: false } as AffectedJSObjects,
);
};

@vsvamsi1 vsvamsi1 added ok-to-test Required label for CI and removed ok-to-test Required label for CI labels May 24, 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: 5

Outside diff range and nitpick comments (12)
app/client/src/sagas/EvaluationsSaga.ts (2)

Line range hint 255-288: Review the handling of default parameters in function signatures.

The default parameter defaultAffectedJSObjects is used before it is defined in the file. This might lead to runtime errors if the function is called before the variable is initialized. Consider moving the definition of defaultAffectedJSObjects before its usage or initializing it directly in the function signature.

- affectedJSObjects: AffectedJSObjects = defaultAffectedJSObjects,
+ affectedJSObjects: AffectedJSObjects = { isAllAffected: false, ids: [] },

Line range hint 591-591: Review the placement of default parameters in function signatures.

The default parameter affectedJSObjects is placed before required parameters in the evalAndLintingHandler function. This is unconventional and could lead to confusion. Consider rearranging the parameters to follow the typical pattern of required parameters first, followed by optional ones.

- function evalAndLintingHandler(isBlockingCall = true, action: ReduxAction<unknown>, options: Partial<{ shouldReplay: boolean; forceEvaluation: boolean; requiresLogging: boolean; affectedJSObjects: AffectedJSObjects; }>) {
+ function evalAndLintingHandler(isBlockingCall = true, action: ReduxAction<unknown>, affectedJSObjects: AffectedJSObjects, options: Partial<{ shouldReplay: boolean; forceEvaluation: boolean; requiresLogging: boolean; }>) {
app/client/src/workers/common/DataTreeEvaluator/index.ts (7)

Line range hint 3-3: Avoid shadowing the global EvalError property.

Consider renaming the EvalError type imported from "utils/DynamicBindingUtils" to avoid confusion with the global EvalError object in JavaScript.


Line range hint 179-179: Specify explicit types instead of using any.

Using any can lead to potential runtime errors due to lack of type safety. Please specify more explicit types for better type checking and code clarity.

Also applies to: 233-233, 489-489, 733-733, 1047-1047, 1351-1351


Line range hint 467-480: Prefer for...of loops over forEach for better performance and readability.

Using for...of loops can enhance performance in some cases and generally improves readability, especially when dealing with asynchronous operations or needing to break/continue.

Also applies to: 470-478, 783-785, 930-936, 945-951, 969-976, 985-993


Line range hint 469-469: Avoid redundant double-negation.

Simplify the condition by removing the double negation (!!) which can lead to confusion and is generally unnecessary.


Line range hint 1000-1008: Ensure default parameters follow the last required parameter.

Default parameters should be listed after all required parameters in function/method signatures to avoid confusion and potential errors in function calls.


Line range hint 1061-1061: Avoid implicit any types.

Implicit any types can lead to potential runtime errors and lack of clarity in the codebase. Please specify explicit types.


Line range hint 1306-1342: Remove unnecessary else clauses.

Since the preceding branches of the conditional statements end with break or continue, the else clauses are redundant and can be omitted for cleaner and more readable code.

Also applies to: 1417-1419

app/client/src/ce/constants/ReduxActionConstants.tsx (3)

Line range hint 1196-1196: Consider specifying a more appropriate type instead of any for the reject field in PromisePayload.

- reject: any;
+ reject: (reason?: any) => void;

Line range hint 1197-1197: Consider specifying a more appropriate type instead of any for the resolve field in PromisePayload.

- resolve: any;
+ resolve: (value?: unknown) => void;

Line range hint 1289-1289: Consider specifying a more appropriate type instead of any for the applications field in WorkspaceDetails.

- applications: any[];
+ applications: ApplicationPayload[];

Comment on lines +490 to +504
function mergeJSBufferedActions(
prevAffectedJSAction: AffectedJSObjects,
newAffectedJSAction: AffectedJSObjects,
) {
if (prevAffectedJSAction.isAllAffected || newAffectedJSAction.isAllAffected) {
return {
isAllAffected: true,
ids: [],
};
}
return {
isAllAffected: false,
ids: [...prevAffectedJSAction.ids, ...newAffectedJSAction.ids],
};
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Optimize the merging logic for affected JS objects.

The function mergeJSBufferedActions can be optimized by directly returning the new object when isAllAffected is true for either argument. This avoids unnecessary array operations and simplifies the logic.

- if (prevAffectedJSAction.isAllAffected || newAffectedJSAction.isAllAffected) {
-   return {
-     isAllAffected: true,
-     ids: [],
-   };
- }
+ return (prevAffectedJSAction.isAllAffected || newAffectedJSAction.isAllAffected) ? { isAllAffected: true, ids: [] } : {
+   isAllAffected: false,
+   ids: [...prevAffectedJSAction.ids, ...newAffectedJSAction.ids],
+ };

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.

Suggested change
function mergeJSBufferedActions(
prevAffectedJSAction: AffectedJSObjects,
newAffectedJSAction: AffectedJSObjects,
) {
if (prevAffectedJSAction.isAllAffected || newAffectedJSAction.isAllAffected) {
return {
isAllAffected: true,
ids: [],
};
}
return {
isAllAffected: false,
ids: [...prevAffectedJSAction.ids, ...newAffectedJSAction.ids],
};
}
function mergeJSBufferedActions(
prevAffectedJSAction: AffectedJSObjects,
newAffectedJSAction: AffectedJSObjects,
) {
return (prevAffectedJSAction.isAllAffected || newAffectedJSAction.isAllAffected) ? { isAllAffected: true, ids: [] } : {
isAllAffected: false,
ids: [...prevAffectedJSAction.ids, ...newAffectedJSAction.ids],
};
}

Comment on lines 53 to 73
const mergeAffectedJSObjects = (
action: ReduxAction<unknown> | BufferedReduxAction<unknown>,
) => {
return affectedJSObjectsFns.reduce(
(acc, affectedJSObjectsFn) => {
// when either of the action isAllJSObjectsAffected return true.
// In this case perform diff on all js objects
if (acc.isAllAffected) {
return acc;
}
acc = {
isAllAffected:
acc.isAllAffected || affectedJSObjectsFn(action).isAllAffected,
ids: [...acc.ids, ...affectedJSObjectsFn(action).ids],
};

return acc;
},
{ ids: [], isAllAffected: false } as AffectedJSObjects,
);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Refactor to improve clarity in mergeAffectedJSObjects.

- acc = {
+ let newAcc = {

Using a separate variable instead of reassigning the accumulator inside the reduce function enhances clarity and maintainability.


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.

Suggested change
const mergeAffectedJSObjects = (
action: ReduxAction<unknown> | BufferedReduxAction<unknown>,
) => {
return affectedJSObjectsFns.reduce(
(acc, affectedJSObjectsFn) => {
// when either of the action isAllJSObjectsAffected return true.
// In this case perform diff on all js objects
if (acc.isAllAffected) {
return acc;
}
acc = {
isAllAffected:
acc.isAllAffected || affectedJSObjectsFn(action).isAllAffected,
ids: [...acc.ids, ...affectedJSObjectsFn(action).ids],
};
return acc;
},
{ ids: [], isAllAffected: false } as AffectedJSObjects,
);
};
const mergeAffectedJSObjects = (
action: ReduxAction<unknown> | BufferedReduxAction<unknown>,
) => {
return affectedJSObjectsFns.reduce(
(acc, affectedJSObjectsFn) => {
// when either of the action isAllJSObjectsAffected return true.
// In this case perform diff on all js objects
if (acc.isAllAffected) {
return acc;
}
let newAcc = {
isAllAffected:
acc.isAllAffected || affectedJSObjectsFn(action).isAllAffected,
ids: [...acc.ids, ...affectedJSObjectsFn(action).ids],
};
return newAcc;
},
{ ids: [], isAllAffected: false } as AffectedJSObjects,
);
};

} from "@appsmith/entities/DataTree/types";
import { isObject } from "lodash";
import type { AffectedJSObjects } from "sagas/EvaluationsSagaUtils";
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider using template literals for better readability in getFixedTimeDifference.

- return (endTime - startTime).toFixed(2) + " ms";
+ return `${(endTime - startTime).toFixed(2)} ms`;

This change enhances readability by using template literals.

Committable suggestion was skipped due low confidence.

Comment on lines +69 to +95
test.each([
[createJSCollectionSuccess, jsObject1, ["1234"]],
[deleteJSCollectionSuccess, jsObject1, ["1234"]],
[copyJSCollectionSuccess, jsObject1, ["1234"]],
[moveJSCollectionSuccess, jsObject1, ["1234"]],
[updateJSCollectionBodySuccess, { data: jsObject1 }, ["1234"]],
[fetchJSCollectionsForPageSuccess, jsCollection, ["1234", "5678"]],
])(
"should return the correct affected JSObject ids for action %p with input %p and expected to be %p",
(action, input, expected) => {
const result = getAffectedJSObjectIdsFromAction(
action(input as JSCollection & JSCollection[] & { data: JSCollection }),
);
expect(result).toEqual({ ids: expected, isAllAffected: false });
},
);
test("should return isAllAffected to be true when there are JS errors", () => {
[
ReduxActionErrorTypes.FETCH_JS_ACTIONS_ERROR,
ReduxActionErrorTypes.FETCH_JS_ACTIONS_VIEW_MODE_ERROR,
].forEach((actionType) => {
const result = getAffectedJSObjectIdsFromAction({
type: actionType,
} as ReduxAction<unknown>);
expect(result).toEqual({ isAllAffected: true, ids: [] });
});
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider using for...of instead of forEach for better performance and readability in asynchronous contexts.

- [].forEach((actionType) => {
+ for (const actionType of []) {

This change enhances readability and performance, especially in asynchronous contexts.


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.

Suggested change
test.each([
[createJSCollectionSuccess, jsObject1, ["1234"]],
[deleteJSCollectionSuccess, jsObject1, ["1234"]],
[copyJSCollectionSuccess, jsObject1, ["1234"]],
[moveJSCollectionSuccess, jsObject1, ["1234"]],
[updateJSCollectionBodySuccess, { data: jsObject1 }, ["1234"]],
[fetchJSCollectionsForPageSuccess, jsCollection, ["1234", "5678"]],
])(
"should return the correct affected JSObject ids for action %p with input %p and expected to be %p",
(action, input, expected) => {
const result = getAffectedJSObjectIdsFromAction(
action(input as JSCollection & JSCollection[] & { data: JSCollection }),
);
expect(result).toEqual({ ids: expected, isAllAffected: false });
},
);
test("should return isAllAffected to be true when there are JS errors", () => {
[
ReduxActionErrorTypes.FETCH_JS_ACTIONS_ERROR,
ReduxActionErrorTypes.FETCH_JS_ACTIONS_VIEW_MODE_ERROR,
].forEach((actionType) => {
const result = getAffectedJSObjectIdsFromAction({
type: actionType,
} as ReduxAction<unknown>);
expect(result).toEqual({ isAllAffected: true, ids: [] });
});
});
test.each([
[createJSCollectionSuccess, jsObject1, ["1234"]],
[deleteJSCollectionSuccess, jsObject1, ["1234"]],
[copyJSCollectionSuccess, jsObject1, ["1234"]],
[moveJSCollectionSuccess, jsObject1, ["1234"]],
[updateJSCollectionBodySuccess, { data: jsObject1 }, ["1234"]],
[fetchJSCollectionsForPageSuccess, jsCollection, ["1234", "5678"]],
])(
"should return the correct affected JSObject ids for action %p with input %p and expected to be %p",
(action, input, expected) => {
const result = getAffectedJSObjectIdsFromAction(
action(input as JSCollection & JSCollection[] & { data: JSCollection }),
);
expect(result).toEqual({ ids: expected, isAllAffected: false });
},
);
test("should return isAllAffected to be true when there are JS errors", () => {
for (const actionType of [
ReduxActionErrorTypes.FETCH_JS_ACTIONS_ERROR,
ReduxActionErrorTypes.FETCH_JS_ACTIONS_VIEW_MODE_ERROR,
]) {
const result = getAffectedJSObjectIdsFromAction({
type: actionType,
} as ReduxAction<unknown>);
expect(result).toEqual({ isAllAffected: true, ids: [] });
}
});

@vsvamsi1 vsvamsi1 added ok-to-test Required label for CI and removed ok-to-test Required label for CI labels May 24, 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: 7

Outside diff range and nitpick comments (25)
app/client/src/workers/common/DataTreeEvaluator/utils.ts (2)

Line range hint 52-52: Remove unnecessary continue statement.

- if (!isJSAction(entity)) continue;
+ if (!isJSAction(entity)) return;

Replacing continue with return simplifies the control flow and improves readability.


Line range hint 53-60: Simplify the conditional logic by removing the redundant else clause.

-      } else {
-        const reacheableAsyncNodes = dependencyMap.getAllReachableNodes(
-          `${entityName}.${propertyName}`,
-          allAsyncNodes,
-        );
-        reacheableAsyncNodes.length &&
-          allAsyncNodes.push(`${entityName}.${propertyName}`);
-      }

The else clause is unnecessary because the previous branch of the if statement continues the loop, making this code unreachable in that case.

app/client/src/sagas/EvaluationsSaga.test.ts (1)

Line range hint 30-30: Specify a more accurate type instead of any.

- buffer.put(fetchPluginFormConfigsSuccess({} as any));
+ buffer.put(fetchPluginFormConfigsSuccess({} as SpecificType));

Using any can lead to potential type safety issues. It's better to specify a more accurate type.

app/client/src/workers/Evaluation/handlers/evalTree.ts (1)

Line range hint 5-5: Avoid shadowing the global EvalError property.

- import type { DependencyMap, EvalError } from "utils/DynamicBindingUtils";
+ import type { DependencyMap, EvaluationError as EvalError } from "utils/DynamicBindingUtils";

Renaming the imported EvalError to EvaluationError avoids confusion with the global EvalError object and enhances code clarity.

app/client/src/workers/Evaluation/__tests__/generateOpimisedUpdates.test.ts (7)

Line range hint 313-317: Prefer using for...of instead of forEach for better performance and readability.

- parsedUpdates.forEach((v: any) => {
+ for (const v of parsedUpdates) {
    applyChange(draft, undefined, v);
- });
+ }

Line range hint 352-356: Consider replacing forEach with for...of for consistency and potential performance benefits.

- action.payload.forEach((batchedAction) => {
+ for (const batchedAction of action.payload) {
    if (batchedAction.postEvalActions) {
      postEvalActions.push(
        ...(batchedAction.postEvalActions as AnyReduxAction[]),
      );
    }
- });
+ }

Line range hint 357-358: Avoid using the delete operator as it can lead to performance issues and hinder JavaScript engine optimizations.

- delete draft.Table1.pageSize;
- delete draft.Table1.__evaluation__.errors.transientTableData;
+ draft.Table1.pageSize = undefined;
+ draft.Table1.__evaluation__.errors.transientTableData = undefined;

Also applies to: 358-361


Line range hint 405-409: Replace forEach with for...of to enhance clarity and performance.

- parsedUpdates.forEach((v: any) => {
+ for (const v of parsedUpdates) {
    applyChange(draft, undefined, v);
- });
+ }

Line range hint 410-411: Consider avoiding the delete operator to maintain optimal performance.

- delete draft.Table1.pageSize;
- delete draft.Table1.__evaluation__.errors.transientTableData;
+ draft.Table1.pageSize = undefined;
+ draft.Table1.__evaluation__.errors.transientTableData = undefined;

Also applies to: 411-414


Line range hint 458-462: Use for...of instead of forEach for iterating over arrays for better performance.

- action.payload.forEach((batchedAction) => {
+ for (const batchedAction of action.payload) {
    if (batchedAction.postEvalActions) {
      postEvalActions.push(
        ...(batchedAction.postEvalActions as AnyReduxAction[]),
      );
    }
- });
+ }

Line range hint 510-515: Prefer for...of over forEach to improve performance and readability in loops.

- action.payload.forEach((batchedAction) => {
+ for (const batchedAction of action.payload) {
    if (batchedAction.postEvalActions) {
      postEvalActions.push(
        ...(batchedAction.postEvalActions as AnyReduxAction[]),
      );
    }
- });
+ }
app/client/src/sagas/EvaluationsSaga.ts (5)

Line range hint 29-29: Avoid shadowing the global EvalError property. Consider renaming local instances to prevent potential conflicts.

- import type { EvalError, EvaluationError } from "utils/DynamicBindingUtils";
+ import type { EvaluationError } from "utils/DynamicBindingUtils";
+ type AppsmithEvalError = EvalError; // Rename to avoid shadowing global `EvalError`

Line range hint 193-193: Replace forEach with for...of for better performance and readability in array iterations.

- action.payload.forEach((batchedAction) => {
+ for (const batchedAction of action.payload) {
    if (batchedAction.postEvalActions) {
      postEvalActions.push(
        ...(batchedAction.postEvalActions as AnyReduxAction[]),
      );
    }
- });
+ }

Line range hint 232-232: Use optional chaining to safely access nested properties.

- if (action.payload && action.payload.shouldReplay) {
+ if (action.payload?.shouldReplay) {

Line range hint 579-585: Prefer for...of over forEach for iterating over arrays to enhance performance and readability.

- action.payload.forEach((batchedAction) => {
+ for (const batchedAction of action.payload) {
    if (batchedAction.postEvalActions) {
      postEvalActions.push(
        ...(batchedAction.postEvalActions as AnyReduxAction[]),
      );
    }
- });
+ }

Line range hint 591-591: Ensure default parameters follow the last required parameter or are marked as required to maintain clarity in function signatures.

- function* evalAndLintingHandler(
-   isBlockingCall = true,
-   action: ReduxAction<unknown>,
-   options: Partial<{
+ function* evalAndLintingHandler(
+   action: ReduxAction<unknown>,
+   options: Partial<{
+   isBlockingCall = true,
app/client/src/workers/common/DataTreeEvaluator/index.ts (7)

Line range hint 492-600: Ensure proper error handling for the setupUpdateTree method.

try {
  // existing code here
} catch (error) {
  // handle error appropriately
}

Line range hint 467-480: Replace forEach with for...of for better performance and readability.

for (const update of Object.keys(unEvalJSCollection)) {
  const updates = unEvalJSCollection[update];
  if (unevalTree[update]) {
    for (const key of Object.keys(updates)) {
      const data = get(unevalTree, `${update}.${key}.data`, undefined);
      if (isJSObjectFunction(unevalTree, update, key, configTree)) {
        set(unevalTree, `${update}.${key}`, new String(updates[key]));
        set(unevalTree, `${update}.${key}.data`, data);
      } else {
        set(unevalTree, `${update}.${key}`, updates[key]);
      }
    }
  }
}

Also applies to: 469-469, 470-478


Line range hint 783-785: Replace forEach with for...of in multiple locations for consistency and performance.

// Example conversion for one of the forEach loops
for (const diff of differences) {
  translateDiffEventToDataTreeDiffEvent(diff, localUnEvalTree);
}

Also applies to: 930-936, 945-951, 969-976, 985-993


Line range hint 1000-1008: Adjust the default parameter affectedJSObjects to follow the last required parameter.

// Correct parameter order
affectedJSObjects: AffectedJSObjects = { isAllAffected: false, ids: [] },
webworkerTelemetry: Record<string, WebworkerSpanData> = {},

Line range hint 1269-1274: Avoid unsafe usage of 'return' in loops.

// Example fix
if (condition) {
  return value; // Ensure that return is safe and intended
}

Line range hint 1306-1342: Remove unnecessary else clause after early return.

// Before
if (condition) {
  return value;
} else {
  // code
}

// After
if (condition) {
  return value;
}
// code

Line range hint 1351-1351: Specify a more precise type than any for configTree.

// Suggested type change
type ConfigTree = {
  // properties of ConfigTree
};
app/client/src/ce/constants/ReduxActionConstants.tsx (2)

19-19: Add documentation for AffectedJSObjects import.

Consider adding a comment above the import statement to explain why AffectedJSObjects is needed here, especially since it's a new addition and might not be immediately clear to other developers.


Line range hint 1196-1196: Replace any with specific types to enhance type safety.

Using any type can lead to potential runtime errors due to lack of type checking. Consider specifying more appropriate types or using generics if applicable.

-  applications: any[];
+  applications: ApplicationPayload[];

-  reject: any;
-  resolve: any;
+  reject: (reason?: any) => void;
+  resolve: (value: unknown) => void;

-  workspace: Workspace;
+  workspace: WorkspaceDetails;

Also applies to: 1197-1197, 1289-1289

Comment on lines +521 to +530
microDiff(
getOnlyAffectedJSObjects(
oldUnEvalTreeJSCollections,
affectedJSObjects,
),
getOnlyAffectedJSObjects(
localUnEvalTreeJSCollection,
affectedJSObjects,
),
) || [],
Copy link
Contributor

Choose a reason for hiding this comment

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

Optimize the use of microDiff and getOnlyAffectedJSObjects by directly passing the result of getOnlyAffectedJSObjects to microDiff.

const jsDifferences: Diff<
  Record<string, JSActionEntity>,
  Record<string, JSActionEntity>
>[] = profileFn(
  "SetupUpdateTree.Diff1",
  undefined,
  webworkerTelemetry,
  () => convertMicroDiffToDeepDiff(
    microDiff(
      getOnlyAffectedJSObjects(
        oldUnEvalTreeJSCollections,
        affectedJSObjects,
      ),
      getOnlyAffectedJSObjects(
        localUnEvalTreeJSCollection,
        affectedJSObjects,
      ),
    ) || [],
  ),
);

@@ -41,6 +42,7 @@ export interface EvalTreeRequestData {
appMode?: APP_MODE;
widgetsMeta: Record<string, any>;
shouldRespondWithLogs?: boolean;
affectedJSObjects: AffectedJSObjects;
Copy link
Contributor

Choose a reason for hiding this comment

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

Ensure affectedJSObjects is marked as optional to align with TypeScript best practices.

-  affectedJSObjects: AffectedJSObjects;
+  affectedJSObjects?: AffectedJSObjects;

Marking affectedJSObjects as optional will enhance type safety, especially in cases where it might not be provided.


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.

Suggested change
affectedJSObjects: AffectedJSObjects;
affectedJSObjects?: AffectedJSObjects;

@@ -16,6 +16,7 @@
import type { DataTreeDiff } from "@appsmith/workers/Evaluation/evaluationUtils";
import type { APP_MODE } from "entities/App";
import type { WebworkerSpanData } from "UITelemetry/generateWebWorkerTraces";
import type { AffectedJSObjects } from "sagas/EvaluationsSagaUtils";
Copy link
Contributor

Choose a reason for hiding this comment

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

Add missing import for AffectedJSObjects.

+ import type { AffectedJSObjects } from "sagas/EvaluationsSagaUtils";

The AffectedJSObjects type is used in this file but not imported, which could lead to a compilation error.


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.

Suggested change
import type { AffectedJSObjects } from "sagas/EvaluationsSagaUtils";
import type { AffectedJSObjects } from "sagas/EvaluationsSagaUtils";


//delete all undefined properties from the state
const { deleteUpdates, regularUpdates } = parsedUpdates.reduce(
(acc: any, curr: any) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Specify more accurate types instead of any in the reducer function.

- (acc: any, curr: any) => {
+ (acc: DiffWithNewTreeState[], curr: DiffWithNewTreeState) => {

Using more specific types instead of any enhances type safety and code readability.


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.

Suggested change
(acc: any, curr: any) => {
(acc: DiffWithNewTreeState[], curr: DiffWithNewTreeState) => {

@@ -484,6 +489,7 @@
unEvalTree: any,
configTree: ConfigTree,
webworkerTelemetry: Record<string, WebworkerSpanData> = {},
affectedJSObjects: AffectedJSObjects = { isAllAffected: false, ids: [] },
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider specifying a more precise type than any for affectedJSObjects.

// Suggested type change for affectedJSObjects
type AffectedJSObjects = {
  isAllAffected: boolean;
  ids: string[];
};

if (acc.isAllAffected) {
return acc;
}
acc = {
Copy link
Contributor

Choose a reason for hiding this comment

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

Avoid reassigning function parameters.

- acc = {
+ let newAcc = {

Reassigning function parameters can lead to unexpected behavior and makes the code harder to understand. Use a new variable instead.


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.

Suggested change
acc = {
let newAcc = {

Comment on lines +86 to +94
[
ReduxActionErrorTypes.FETCH_JS_ACTIONS_ERROR,
ReduxActionErrorTypes.FETCH_JS_ACTIONS_VIEW_MODE_ERROR,
].forEach((actionType) => {
const result = getAffectedJSObjectIdsFromAction({
type: actionType,
} as ReduxAction<unknown>);
expect(result).toEqual({ isAllAffected: true, ids: [] });
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Prefer for...of instead of forEach for better performance and readability.

- [].forEach((actionType) => {
+ for (const actionType of []) {

Using for...of enhances readability and performance, especially in asynchronous contexts.


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.

Suggested change
[
ReduxActionErrorTypes.FETCH_JS_ACTIONS_ERROR,
ReduxActionErrorTypes.FETCH_JS_ACTIONS_VIEW_MODE_ERROR,
].forEach((actionType) => {
const result = getAffectedJSObjectIdsFromAction({
type: actionType,
} as ReduxAction<unknown>);
expect(result).toEqual({ isAllAffected: true, ids: [] });
});
[
ReduxActionErrorTypes.FETCH_JS_ACTIONS_ERROR,
ReduxActionErrorTypes.FETCH_JS_ACTIONS_VIEW_MODE_ERROR,
].forEach((actionType) => {
+ for (const actionType of [
+ ReduxActionErrorTypes.FETCH_JS_ACTIONS_ERROR,
+ ReduxActionErrorTypes.FETCH_JS_ACTIONS_VIEW_MODE_ERROR,
+ ]) {
const result = getAffectedJSObjectIdsFromAction({
type: actionType,
} as ReduxAction<unknown>);
expect(result).toEqual({ isAllAffected: true, ids: [] });
});

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

Comment on lines +53 to +73
const mergeAffectedJSObjects = (
action: ReduxAction<unknown> | BufferedReduxAction<unknown>,
) => {
return AFFECTED_JS_OBJECTS_FNS.reduce(
(acc, affectedJSObjectsFn) => {
// when either of the action isAllJSObjectsAffected return true.
// In this case perform diff on all js objects
if (acc.isAllAffected) {
return acc;
}
acc = {
isAllAffected:
acc.isAllAffected || affectedJSObjectsFn(action).isAllAffected,
ids: [...acc.ids, ...affectedJSObjectsFn(action).ids],
};

return acc;
},
{ ids: [], isAllAffected: false } as AffectedJSObjects,
);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Avoid reassigning function parameters in mergeAffectedJSObjects to enhance clarity and maintainability.

- acc = {
+ let newAcc = {

Using a separate variable instead of reassigning the accumulator inside the reduce function enhances clarity and maintainability.


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.

Suggested change
const mergeAffectedJSObjects = (
action: ReduxAction<unknown> | BufferedReduxAction<unknown>,
) => {
return AFFECTED_JS_OBJECTS_FNS.reduce(
(acc, affectedJSObjectsFn) => {
// when either of the action isAllJSObjectsAffected return true.
// In this case perform diff on all js objects
if (acc.isAllAffected) {
return acc;
}
acc = {
isAllAffected:
acc.isAllAffected || affectedJSObjectsFn(action).isAllAffected,
ids: [...acc.ids, ...affectedJSObjectsFn(action).ids],
};
return acc;
},
{ ids: [], isAllAffected: false } as AffectedJSObjects,
);
};
const mergeAffectedJSObjects = (
action: ReduxAction<unknown> | BufferedReduxAction<unknown>,
) => {
return AFFECTED_JS_OBJECTS_FNS.reduce(
(acc, affectedJSObjectsFn) => {
// when either of the action isAllJSObjectsAffected return true.
// In this case perform diff on all js objects
if (acc.isAllAffected) {
return acc;
}
let newAcc = {
isAllAffected:
acc.isAllAffected || affectedJSObjectsFn(action).isAllAffected,
ids: [...acc.ids, ...affectedJSObjectsFn(action).ids],
};
return newAcc;
},
{ ids: [], isAllAffected: false } as AffectedJSObjects,
);
};

Comment on lines +9 to +46
export const parseUpdatesAndDeleteUndefinedUpdates = (
updates: string,
): DiffWithNewTreeState[] => {
let parsedUpdates = [];
try {
//Parse updates from a string
parsedUpdates = JSON.parse(updates);
} catch (e) {
log.error("Failed to parse updates", e, updates);
return [];
}

//delete all undefined properties from the state
const { deleteUpdates, regularUpdates } = parsedUpdates.reduce(
(acc: any, curr: any) => {
const { kind, path, rhs } = curr;

if (rhs === undefined) {
//ignore any new undefined updates to the state if the value is undefined
if (kind === "N") {
return acc;
}
//convert undefined updates to delete updates
if (kind === "E") {
acc.deleteUpdates.push({ kind: "D", path });
return acc;
}
}

acc.regularUpdates.push(curr);
return acc;
},
{ regularUpdates: [], deleteUpdates: [] },
);

const consolidatedUpdates = [...regularUpdates, ...deleteUpdates];
return consolidatedUpdates;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider specifying more accurate types instead of any in parseUpdatesAndDeleteUndefinedUpdates to enhance type safety.

- (acc: any, curr: any) => {
+ (acc: DiffWithNewTreeState[], curr: DiffWithNewTreeState) => {

This change improves type safety by avoiding the use of any.


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.

Suggested change
export const parseUpdatesAndDeleteUndefinedUpdates = (
updates: string,
): DiffWithNewTreeState[] => {
let parsedUpdates = [];
try {
//Parse updates from a string
parsedUpdates = JSON.parse(updates);
} catch (e) {
log.error("Failed to parse updates", e, updates);
return [];
}
//delete all undefined properties from the state
const { deleteUpdates, regularUpdates } = parsedUpdates.reduce(
(acc: any, curr: any) => {
const { kind, path, rhs } = curr;
if (rhs === undefined) {
//ignore any new undefined updates to the state if the value is undefined
if (kind === "N") {
return acc;
}
//convert undefined updates to delete updates
if (kind === "E") {
acc.deleteUpdates.push({ kind: "D", path });
return acc;
}
}
acc.regularUpdates.push(curr);
return acc;
},
{ regularUpdates: [], deleteUpdates: [] },
);
const consolidatedUpdates = [...regularUpdates, ...deleteUpdates];
return consolidatedUpdates;
};
export const parseUpdatesAndDeleteUndefinedUpdates = (
updates: string,
): DiffWithNewTreeState[] => {
let parsedUpdates = [];
try {
//Parse updates from a string
parsedUpdates = JSON.parse(updates);
} catch (e) {
log.error("Failed to parse updates", e, updates);
return [];
}
//delete all undefined properties from the state
const { deleteUpdates, regularUpdates } = parsedUpdates.reduce(
(acc: DiffWithNewTreeState[], curr: DiffWithNewTreeState) => {
const { kind, path, rhs } = curr;
if (rhs === undefined) {
//ignore any new undefined updates to the state if the value is undefined
if (kind === "N") {
return acc;
}
//convert undefined updates to delete updates
if (kind === "E") {
acc.deleteUpdates.push({ kind: "D", path });
return acc;
}
}
acc.regularUpdates.push(curr);
return acc;
},
{ regularUpdates: [], deleteUpdates: [] },
);
const consolidatedUpdates = [...regularUpdates, ...deleteUpdates];
return consolidatedUpdates;
};

@vsvamsi1 vsvamsi1 added ok-to-test Required label for CI and removed ok-to-test Required label for CI labels May 27, 2024
Copy link
Contributor

@ankitakinger ankitakinger left a comment

Choose a reason for hiding this comment

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

LGTM

@vsvamsi1 vsvamsi1 merged commit d8f3911 into release May 27, 2024
82 checks passed
@vsvamsi1 vsvamsi1 deleted the constrainedSetup branch May 27, 2024 06:15
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 Performance Pod All things related to Appsmith performance Performance Issues related to performance skip-changelog Adding this label to a PR prevents it from being listed in the changelog Task A simple Todo
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Constrained diff of JS collections
5 participants