PromucFlow_constructor/app/client/src/workers/Evaluation/evalTreeWithChanges.ts
Rishabh Rathod cb3ddf1cb3
chore: Add evalTreeWithDiff to evalWorkerAction (#34403)
## Description

These changes add evalTreeWithDiff to evalWorkerAction. Using this
method it will be possible to send the unevalTree and configTree updates
from mainThread to trigger evaluation.
- This will skip diffing complete unEvaltree

### Next steps after this PR
- [ ] Rename EvalTreeWithChanges to evalTreeWithDiff
- [ ] Generate Diff instead of updatedValuePaths for the current
evalTreeWithChanges references.
- [ ] Handle evalTreeWithDiff for
	- [ ] JSObject updates 
	- [ ] updateDependencyMap

Fixes #

## Automation

/test js

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/9684706533>
> Commit: 3de988af5da9900f4e589a008d28296d05d17f25
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=9684706533&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.JS`

<!-- end of auto-generated comment: Cypress test results  -->


















## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [x] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Enhanced widget evaluation logic and improved handling of dynamic
bindings and dependencies.

- **Bug Fixes**
- Corrected type handling in various evaluation functions to improve
stability and accuracy.

- **Tests**
- Updated test cases for widget property setters to include additional
tags for better categorization.

- **Refactor**
- Streamlined function signatures and improved type safety across
evaluation utilities.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-06-27 12:08:53 +05:30

175 lines
5.1 KiB
TypeScript

import { dataTreeEvaluator } from "./handlers/evalTree";
import type { EvalMetaUpdates } from "@appsmith/workers/common/DataTreeEvaluator/types";
import { makeEntityConfigsAsObjProperties } from "@appsmith/workers/Evaluation/dataTreeUtils";
import type {
EvalTreeResponseData,
EvalWorkerSyncRequest,
UpdateTreeResponse,
} from "./types";
import { MessageType, sendMessage } from "utils/MessageUtil";
import { MAIN_THREAD_ACTION } from "@appsmith/workers/Evaluation/evalWorkerActions";
import type { UpdateDataTreeMessageData } from "sagas/EvalWorkerActionSagas";
import {
generateOptimisedUpdatesAndSetPrevState,
getNewDataTreeUpdates,
uniqueOrderUpdatePaths,
} from "./helpers";
import type { DataTreeDiff } from "@appsmith/workers/Evaluation/evaluationUtils";
import type DataTreeEvaluator from "workers/common/DataTreeEvaluator";
const getDefaultEvalResponse = (): EvalTreeResponseData => ({
updates: "[]",
dependencies: {},
errors: [],
evalMetaUpdates: [],
evaluationOrder: [],
jsUpdates: {},
logs: [],
unEvalUpdates: [],
isCreateFirstTree: false,
staleMetaIds: [],
removedPaths: [],
isNewWidgetAdded: false,
undefinedEvalValuesMap: {},
jsVarsCreatedEvent: [],
});
export function evalTreeWithChanges(
request: EvalWorkerSyncRequest<{
metaUpdates?: EvalMetaUpdates;
updatedValuePaths: string[][];
}>,
) {
const { data } = request;
const { metaUpdates = [], updatedValuePaths } = data;
const pathsToSkipFromEval = updatedValuePaths.map((path) => path.join("."));
let setupUpdateTreeResponse = {} as UpdateTreeResponse;
if (dataTreeEvaluator) {
setupUpdateTreeResponse = dataTreeEvaluator.setupUpdateTreeWithDifferences(
updatedValuePaths,
pathsToSkipFromEval,
);
}
evaluateAndPushResponse(
dataTreeEvaluator,
setupUpdateTreeResponse,
metaUpdates,
pathsToSkipFromEval,
);
}
export const getAffectedNodesInTheDataTree = (
unEvalUpdates: DataTreeDiff[],
evalOrder: string[],
) => {
const allUnevalUpdates = unEvalUpdates.map(
(update) => update.payload.propertyPath,
);
// merge unevalUpdate paths and evalOrder paths
return uniqueOrderUpdatePaths([...allUnevalUpdates, ...evalOrder]);
};
export const evaluateAndPushResponse = (
dataTreeEvaluator: DataTreeEvaluator | undefined,
setupUpdateTreeResponse: UpdateTreeResponse,
metaUpdates: EvalMetaUpdates,
additionalPathsAddedAsUpdates: string[],
) => {
const response = evaluateAndGenerateResponse(
dataTreeEvaluator,
setupUpdateTreeResponse,
metaUpdates,
additionalPathsAddedAsUpdates,
);
return pushResponseToMainThread(response);
};
export const evaluateAndGenerateResponse = (
dataTreeEvaluator: DataTreeEvaluator | undefined,
setupUpdateTreeResponse: UpdateTreeResponse,
metaUpdates: EvalMetaUpdates,
additionalPathsAddedAsUpdates: string[],
): UpdateDataTreeMessageData => {
// generate default response first and later add updates to it
const defaultResponse = getDefaultEvalResponse();
if (!dataTreeEvaluator) {
const updates = generateOptimisedUpdatesAndSetPrevState(
{},
dataTreeEvaluator,
[],
);
defaultResponse.updates = updates;
defaultResponse.evalMetaUpdates = [...(metaUpdates || [])];
return {
workerResponse: defaultResponse,
unevalTree: {},
};
}
const { evalOrder, jsUpdates, unEvalUpdates } = setupUpdateTreeResponse;
defaultResponse.evaluationOrder = evalOrder;
defaultResponse.unEvalUpdates = unEvalUpdates;
defaultResponse.jsUpdates = jsUpdates;
const updateResponse = dataTreeEvaluator.evalAndValidateSubTree(
evalOrder,
dataTreeEvaluator.oldConfigTree,
unEvalUpdates,
);
const dataTree = makeEntityConfigsAsObjProperties(
dataTreeEvaluator.evalTree,
{
evalProps: dataTreeEvaluator.evalProps,
},
);
/** Make sure evalMetaUpdates is sanitized to prevent postMessage failure */
defaultResponse.evalMetaUpdates = JSON.parse(
JSON.stringify([...(metaUpdates || []), ...updateResponse.evalMetaUpdates]),
);
defaultResponse.staleMetaIds = updateResponse.staleMetaIds;
const unevalTree = dataTreeEvaluator.getOldUnevalTree();
// when additional paths are required to be added as updates, we extract the updates from the data tree using these paths.
const additionalUpdates = getNewDataTreeUpdates(
additionalPathsAddedAsUpdates,
dataTree,
);
// the affected paths is a combination of the eval order and the uneval updates
// we use this collection to limit the diff between the old and new data tree
const affectedNodePaths = getAffectedNodesInTheDataTree(
unEvalUpdates,
evalOrder,
);
defaultResponse.updates = generateOptimisedUpdatesAndSetPrevState(
dataTree,
dataTreeEvaluator,
affectedNodePaths,
additionalUpdates,
);
dataTreeEvaluator.undefinedEvalValuesMap =
dataTreeEvaluator.undefinedEvalValuesMap || {};
return {
workerResponse: defaultResponse,
unevalTree,
};
};
export const pushResponseToMainThread = (data: UpdateDataTreeMessageData) => {
sendMessage.call(self, {
messageType: MessageType.DEFAULT,
body: {
data,
method: MAIN_THREAD_ACTION.UPDATE_DATATREE,
},
});
};