Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ describe("generateDataTreeJSAction", () => {
myVar1: "SMART_SUBSTITUTE",
myVar2: "SMART_SUBSTITUTE",
},
actionNames: ["myFun2", "myFun1"],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

The order of action names in tests doesn't match the implementation

The implementation in dataTreeJSAction.ts shows that action names are derived directly from the actions array using actions.map((action) => action.name), but the test expectations have a reversed order ["myFun2", "myFun1"] compared to how they appear in the test's actions array. This inconsistency should be fixed.

  • In dataTreeJSAction.test.ts: Update the expected actionNames array to match the actual order of actions in the input data.
🔗 Analysis chain

Verify action names order consistency

The actionNames array maintains the same order ["myFun2", "myFun1"] in both test cases. Ensure this order matches the intended evaluation order in the actual implementation.

Also applies to: 393-393

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the order of action names in the implementation
# Expected: The order of actions in generateDataTreeJSAction should match the test expectations

# Search for the implementation of generateDataTreeJSAction
ast-grep --pattern 'function generateDataTreeJSAction($_) {
  $$$
}'

Length of output: 70


Script:

#!/bin/bash
# Let's try a different approach to find the implementation
# First, let's find the file containing the implementation
rg -l "generateDataTreeJSAction" --type ts

# Then let's look at the implementation with context
rg "generateDataTreeJSAction" --type ts -B 5 -A 20

# Also search for any related types or interfaces
rg "interface.*JSActionConfig" --type ts -A 5

Length of output: 13478


Script:

#!/bin/bash
# Let's look at the actions array in the test file to verify the order
rg -A 10 "actions: \[" app/client/src/ce/entities/DataTree/dataTreeJSAction.test.ts

# And check if there's any logic in the implementation that determines the action order
rg -B 5 -A 10 "actionNames" app/client/src/ce/entities/DataTree/dataTreeJSAction.ts

Length of output: 1244

};
const resultData = generateDataTreeJSAction(jsCollection);

Expand Down Expand Up @@ -389,6 +390,7 @@ describe("generateDataTreeJSAction", () => {
myVar1: "SMART_SUBSTITUTE",
myVar2: "SMART_SUBSTITUTE",
},
actionNames: ["myFun2", "myFun1"],
};

const result = generateDataTreeJSAction(jsCollection);
Expand Down
1 change: 1 addition & 0 deletions app/client/src/ce/entities/DataTree/dataTreeJSAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export const generateDataTreeJSAction = (
dynamicBindingPathList: dynamicBindingPathList,
variables: listVariables,
dependencyMap: dependencyMap,
actionNames: actions.map((action) => action.name),
},
};
};
1 change: 1 addition & 0 deletions app/client/src/ce/entities/DataTree/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export interface JSActionEntityConfig extends EntityConfig {
moduleId?: string;
moduleInstanceId?: string;
isPublic?: boolean;
actionNames: string[];
}

export interface JSActionEntity {
Expand Down
2 changes: 2 additions & 0 deletions app/client/src/ce/entities/FeatureFlag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const FEATURE_FLAG = {
"release_table_custom_loading_state_enabled",
release_custom_widget_ai_builder: "release_custom_widget_ai_builder",
ab_request_new_integration_enabled: "ab_request_new_integration_enabled",
release_evaluation_scope_cache: "release_evaluation_scope_cache",
release_table_html_column_type_enabled:
"release_table_html_column_type_enabled",
} as const;
Expand Down Expand Up @@ -83,6 +84,7 @@ export const DEFAULT_FEATURE_FLAG_VALUE: FeatureFlags = {
release_table_custom_loading_state_enabled: false,
release_custom_widget_ai_builder: false,
ab_request_new_integration_enabled: false,
release_evaluation_scope_cache: false,
release_table_html_column_type_enabled: false,
};

Expand Down
19 changes: 13 additions & 6 deletions app/client/src/ce/workers/Evaluation/Actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ export enum ExecutionType {
/**
* This method returns new dataTree with entity function and platform function
*/
export const addDataTreeToContext = (args: {
EVAL_CONTEXT: EvalContext;
export const getDataTreeContext = (args: {
dataTree: Readonly<DataTree>;
removeEntityFunctions?: boolean;
isTriggerBased: boolean;
Expand All @@ -50,10 +49,11 @@ export const addDataTreeToContext = (args: {
const {
configTree,
dataTree,
EVAL_CONTEXT,
isTriggerBased,
removeEntityFunctions = false,
} = args;
const EVAL_CONTEXT: EvalContext = {};

const dataTreeEntries = Object.entries(dataTree);
const entityFunctionCollection: Record<string, Record<string, Function>> = {};

Expand Down Expand Up @@ -95,16 +95,23 @@ export const addDataTreeToContext = (args: {
);
}

if (removeEntityFunctions)
return removeEntityFunctionsFromEvalContext(
if (removeEntityFunctions) {
removeEntityFunctionsFromEvalContext(
entityFunctionCollection,
EVAL_CONTEXT,
);

if (!isTriggerBased) return;
return EVAL_CONTEXT;
}

if (!isTriggerBased) {
return EVAL_CONTEXT;
}

// if eval is not trigger based i.e., sync eval then we skip adding entity function to evalContext
addEntityFunctionsToEvalContext(EVAL_CONTEXT, entityFunctionCollection);

return EVAL_CONTEXT;
};

export const addEntityFunctionsToEvalContext = (
Expand Down
13 changes: 13 additions & 0 deletions app/client/src/ce/workers/Evaluation/evaluationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,19 @@ export const isNotEntity = (entity: DataTreeEntity) => {
export const isEntityAction = (entity: DataTreeEntity) => {
return isAction(entity);
};

export const isPropertyAnEntityAction = (
entity: DataTreeEntity,
propertyPath: string,
entityConfig: DataTreeEntityConfig,
) => {
if (!isJSAction(entity)) return false;

const { actionNames } = entityConfig as JSActionEntityConfig;

return actionNames.includes(propertyPath);
};

export const convertMicroDiffToDeepDiff = (
microDiffDifferences: Difference[],
): Diff<unknown, unknown>[] =>
Expand Down
9 changes: 0 additions & 9 deletions app/client/src/entities/Engine/AppViewerEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import {
waitForSegmentInit,
waitForFetchUserSuccess,
} from "ee/sagas/userSagas";
import { waitForFetchEnvironments } from "ee/sagas/EnvironmentSagas";
import { fetchJSCollectionsForView } from "actions/jsActionActions";
import {
fetchAppThemesAction,
Expand Down Expand Up @@ -154,14 +153,6 @@ export default class AppViewerEngine extends AppEngine {
yield call(waitForSegmentInit, true);
endSpan(waitForSegmentSpan);

const waitForEnvironmentsSpan = startNestedSpan(
"AppViewerEngine.waitForFetchEnvironments",
rootSpan,
);

yield call(waitForFetchEnvironments);
endSpan(waitForEnvironmentsSpan);

yield put(fetchAllPageEntityCompletion([executePageLoadActions()]));

endSpan(loadAppEntitiesSpan);
Expand Down
2 changes: 2 additions & 0 deletions app/client/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// This file must be executed as early as possible to ensure the preloads are triggered ASAP
import "./preload-route-chunks";
// Initialise eval worker instance
import "utils/workerInstances";

import React from "react";
import "./wdyr";
Expand Down
3 changes: 2 additions & 1 deletion app/client/src/sagas/ActionExecution/PluginActionSaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ import log from "loglevel";
import { EMPTY_RESPONSE } from "components/editorComponents/emptyResponse";
import type { AppState } from "ee/reducers";
import { DEFAULT_EXECUTE_ACTION_TIMEOUT_MS } from "ee/constants/ApiConstants";
import { evaluateActionBindings, evalWorker } from "sagas/EvaluationsSaga";
import { evaluateActionBindings } from "sagas/EvaluationsSaga";
import { evalWorker } from "utils/workerInstances";
import { isBlobUrl, parseBlobUrl } from "utils/AppsmithUtils";
import { getType, Types } from "utils/TypeHelpers";
import { matchPath } from "react-router";
Expand Down
2 changes: 1 addition & 1 deletion app/client/src/sagas/ActionExecution/geolocationSaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { showToastOnExecutionError } from "sagas/ActionExecution/errorUtils";
import { setUserCurrentGeoLocation } from "actions/browserRequestActions";
import type { Channel } from "redux-saga";
import { channel } from "redux-saga";
import { evalWorker } from "sagas/EvaluationsSaga";
import { evalWorker } from "utils/workerInstances";
import type {
TGetGeoLocationDescription,
TWatchGeoLocationDescription,
Expand Down
2 changes: 1 addition & 1 deletion app/client/src/sagas/EvalWorkerActionSagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import type { TMessage } from "utils/MessageUtil";
import { MessageType } from "utils/MessageUtil";
import type { ResponsePayload } from "../sagas/EvaluationsSaga";
import {
evalWorker,
executeTriggerRequestSaga,
updateDataTreeHandler,
} from "../sagas/EvaluationsSaga";
import { evalWorker } from "utils/workerInstances";
import { handleStoreOperations } from "./ActionExecution/StoreActionSaga";
import type { EvalTreeResponseData } from "workers/Evaluation/types";
import isEmpty from "lodash/isEmpty";
Expand Down
2 changes: 1 addition & 1 deletion app/client/src/sagas/EvaluationsSaga.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import {
defaultAffectedJSObjects,
evalQueueBuffer,
evaluateTreeSaga,
evalWorker,
} from "./EvaluationsSaga";
import { evalWorker } from "utils/workerInstances";
import { expectSaga } from "redux-saga-test-plan";
import { EVAL_WORKER_ACTIONS } from "ee/workers/Evaluation/evalWorkerActions";
import { select } from "redux-saga/effects";
Expand Down
16 changes: 1 addition & 15 deletions app/client/src/sagas/EvaluationsSaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
import { getMetaWidgets, getWidgets, getWidgetsMeta } from "sagas/selectors";
import type { WidgetTypeConfigMap } from "WidgetProvider/factory";
import WidgetFactory from "WidgetProvider/factory";
import { GracefulWorkerService } from "utils/WorkerUtil";
import { evalWorker } from "utils/workerInstances";
import type { EvalError, EvaluationError } from "utils/DynamicBindingUtils";
import { PropertyEvaluationErrorType } from "utils/DynamicBindingUtils";
import { EVAL_WORKER_ACTIONS } from "ee/workers/Evaluation/evalWorkerActions";
Expand Down Expand Up @@ -120,18 +120,6 @@ import { getInstanceId } from "ee/selectors/tenantSelectors";

const APPSMITH_CONFIGS = getAppsmithConfigs();

export const evalWorker = new GracefulWorkerService(
new Worker(
new URL("../workers/Evaluation/evaluation.worker.ts", import.meta.url),
{
type: "module",
// Note: the `Worker` part of the name is slightly important – LinkRelPreload_spec.js
// relies on it to find workers in the list of all requests.
name: "evalWorker",
},
),
);

let widgetTypeConfigMap: WidgetTypeConfigMap;

export function* updateDataTreeHandler(
Expand Down Expand Up @@ -902,5 +890,3 @@ export default function* evaluationSagaListeners() {
}
}
}

export { evalWorker as EvalWorker };
2 changes: 1 addition & 1 deletion app/client/src/sagas/JSLibrarySaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { getCurrentApplicationId } from "selectors/editorSelectors";
import CodemirrorTernService from "utils/autocomplete/CodemirrorTernService";
import { EVAL_WORKER_ACTIONS } from "ee/workers/Evaluation/evalWorkerActions";
import { validateResponse } from "./ErrorSagas";
import { EvalWorker } from "./EvaluationsSaga";
import { evalWorker as EvalWorker } from "utils/workerInstances";
import log from "loglevel";
import { APP_MODE } from "entities/App";
import { getAppMode } from "ee/selectors/applicationSelectors";
Expand Down
4 changes: 4 additions & 0 deletions app/client/src/sagas/PostEvaluationSagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import type { EvalTreeResponseData } from "workers/Evaluation/types";
import { endSpan, startRootSpan } from "UITelemetry/generateTraces";
import { getJSActionPathNameToDisplay } from "ee/utils/actionExecutionUtils";
import { showToastOnExecutionError } from "./ActionExecution/errorUtils";
import { waitForFetchEnvironments } from "ee/sagas/EnvironmentSagas";

let successfulBindingsMap: SuccessfulBindingMap | undefined;

Expand Down Expand Up @@ -190,6 +191,9 @@ export function* logSuccessfulBindings(
}

export function* postEvalActionDispatcher(actions: Array<AnyReduxAction>) {
// Wait for environments api fetch before dispatching actions
yield call(waitForFetchEnvironments);

for (const action of actions) {
yield put(action);
}
Expand Down
13 changes: 13 additions & 0 deletions app/client/src/utils/workerInstances.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { GracefulWorkerService } from "./WorkerUtil";

export const evalWorker = new GracefulWorkerService(
new Worker(
new URL("../workers/Evaluation/evaluation.worker.ts", import.meta.url),
{
type: "module",
// Note: the `Worker` part of the name is slightly important – LinkRelPreload_spec.js
// relies on it to find workers in the list of all requests.
name: "evalWorker",
},
),
);
2 changes: 1 addition & 1 deletion app/client/src/workers/Evaluation/JSObject/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { EvalErrorTypes, getEvalValuePath } from "utils/DynamicBindingUtils";
import type { JSUpdate, ParsedJSSubAction } from "utils/JSPaneUtils";
import { parseJSObject, isJSFunctionProperty } from "@shared/ast";
import type DataTreeEvaluator from "workers/common/DataTreeEvaluator";
import evaluateSync from "workers/Evaluation/evaluate";
import { evaluateSync } from "workers/Evaluation/evaluate";
import type { DataTreeDiff } from "ee/workers/Evaluation/evaluationUtils";
import {
DataTreeDiffEvent,
Expand Down
4 changes: 1 addition & 3 deletions app/client/src/workers/Evaluation/JSObject/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,6 @@ describe("saveResolvedFunctionsAndJSUpdates", function () {
{
key: "myFun2",
},
{
key: "myFun2",
},
],
bindingPaths: {
body: "SMART_SUBSTITUTE",
Expand All @@ -216,6 +213,7 @@ describe("saveResolvedFunctionsAndJSUpdates", function () {
pluginType: "JS",
name: "JSObject1",
actionId: "64013546b956c26882acc587",
actionNames: ["myFun1", "myFun2"],
} as JSActionEntityConfig,
};
const entityName = "JSObject1";
Expand Down
7 changes: 4 additions & 3 deletions app/client/src/workers/Evaluation/__tests__/Actions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { EvalContext } from "workers/Evaluation/evaluate";
import { createEvaluationContext } from "workers/Evaluation/evaluate";
import { MessageType } from "utils/MessageUtil";
import {
addDataTreeToContext,
getDataTreeContext,
addPlatformFunctionsToEvalContext,
} from "ee/workers/Evaluation/Actions";
import TriggerEmitter, { BatchKey } from "../fns/utils/TriggerEmitter";
Expand Down Expand Up @@ -548,12 +548,13 @@ describe("Test addDataTreeToContext method", () => {
const evalContext: EvalContext = {};

beforeAll(() => {
addDataTreeToContext({
EVAL_CONTEXT: evalContext,
const EVAL_CONTEXT = getDataTreeContext({
dataTree: dataTree as unknown as DataTree,
configTree,
isTriggerBased: true,
});

Object.assign(evalContext, EVAL_CONTEXT);
addPlatformFunctionsToEvalContext(evalContext);
});

Expand Down
Loading