chore: improved performance of getUnevaluatedDataTree (#37189)

## Description
Refactored getUnevaluatedDataTree selector to be more resilient to state
changes thereby reselect cache gets triggered less often. Identified an
action which was firing the selectors unnecessarily, fixed that as well.
For our customer during page load it used to take about 800ms
cumulatively, it has now dropped to about 160ms by about 80%. This is
also an impactful selector which cumulatively takes about 50,000 seconds
for all our client systems per week, hence we focussed our optimisation
here

## Automation

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

### 🔍 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/11658078700>
> Commit: 557172d47b2232800355e1dc78c921d9cb56c725
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=11658078700&attempt=2"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.All`
> Spec:
> <hr>Mon, 04 Nov 2024 06:00:06 UTC
<!-- end of auto-generated comment: Cypress test results  -->


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


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

## Summary by CodeRabbit

- **Bug Fixes**
- Improved state management by preventing unnecessary updates to loading
entities, enhancing app performance.

- **Refactor**
- Updated the `loadingEntitiesReducer` to include an equality check for
loading entities before state updates.
- Enhanced date handling capabilities in the DatePicker widget tests and
commands.
- Restructured the `DataTreeFactory` class for improved modularity and
clarity in data tree creation.
- Simplified selector functions for better readability and
maintainability in data tree selectors.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Vemparala Surya Vamsi 2024-11-04 16:03:03 +05:30 committed by GitHub
parent bc165cf827
commit c8943509a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 217 additions and 169 deletions

View File

@ -30,13 +30,13 @@ describe(
formWidgetsPage.datepickerWidget, formWidgetsPage.datepickerWidget,
widgetsPage.widgetNameSpan, widgetsPage.widgetNameSpan,
); );
// change the date to next day // change the date to next day
cy.get(formWidgetsPage.defaultDate).click(); cy.get(formWidgetsPage.defaultDate).click();
/** /**
* setDate--> is a Command to select the date in the date picker * setDate--> is a Command to select the date in the date picker
*/ */
cy.setDate(1); cy.setDate(1);
const nextDay = dayjs().add(1, "days").format("DD/MM/YYYY"); const nextDay = dayjs().add(1, "days").format("DD/MM/YYYY");
cy.log(nextDay); cy.log(nextDay);

View File

@ -1,6 +1,5 @@
# To run only limited tests - give the spec names in below format: # To run only limited tests - give the spec names in below format:
cypress/e2e/Regression/ClientSide/Templates/Fork_Template_spec.js cypress/e2e/Regression/ClientSide/Templates/Fork_Template_spec.js
# For running all specs - uncomment below: # For running all specs - uncomment below:
#cypress/e2e/**/**/* #cypress/e2e/**/**/*

View File

@ -2,6 +2,7 @@
/* eslint-disable cypress/no-assigning-return-values */ /* eslint-disable cypress/no-assigning-return-values */
/* This file is used to maintain comman methods across tests , refer other *.js files for adding common methods */ /* This file is used to maintain comman methods across tests , refer other *.js files for adding common methods */
import { ANVIL_EDITOR_TEST } from "./Constants.js"; import { ANVIL_EDITOR_TEST } from "./Constants.js";
import advancedFormat from "dayjs/plugin/advancedFormat";
import EditorNavigation, { import EditorNavigation, {
EntityType, EntityType,
@ -18,6 +19,7 @@ import { v4 as uuidv4 } from "uuid";
const dayjs = require("dayjs"); const dayjs = require("dayjs");
const loginPage = require("../locators/LoginPage.json"); const loginPage = require("../locators/LoginPage.json");
import homePage from "../locators/HomePage"; import homePage from "../locators/HomePage";
dayjs.extend(advancedFormat);
const commonlocators = require("../locators/commonlocators.json"); const commonlocators = require("../locators/commonlocators.json");
const widgetsPage = require("../locators/Widgets.json"); const widgetsPage = require("../locators/Widgets.json");
@ -525,7 +527,7 @@ Cypress.Commands.add("getDate", (date, dateFormate) => {
}); });
Cypress.Commands.add("setDate", (date) => { Cypress.Commands.add("setDate", (date) => {
const expDate = dayjs().add(date, "days").format("dddd, MMMM DD"); const expDate = dayjs().add(date, "days").format("dddd, MMMM Do, YYYY");
cy.get(`.react-datepicker__day[aria-label^="Choose ${expDate}"]`).click(); cy.get(`.react-datepicker__day[aria-label^="Choose ${expDate}"]`).click();
}); });

View File

@ -199,7 +199,6 @@ export interface DataTreeSeed {
pluginDependencyConfig: Record<string, DependencyMap>; pluginDependencyConfig: Record<string, DependencyMap>;
widgets: CanvasWidgetsReduxState; widgets: CanvasWidgetsReduxState;
widgetsMeta: MetaState; widgetsMeta: MetaState;
pageList: Page[];
appData: AppDataState; appData: AppDataState;
jsActions: JSCollectionDataState; jsActions: JSCollectionDataState;
theme: AppTheme["properties"]; theme: AppTheme["properties"];

View File

@ -1,88 +1,71 @@
import { generateDataTreeAction } from "ee/entities/DataTree/dataTreeAction"; import { generateDataTreeAction } from "ee/entities/DataTree/dataTreeAction";
import { generateDataTreeJSAction } from "ee/entities/DataTree/dataTreeJSAction"; import { generateDataTreeJSAction } from "ee/entities/DataTree/dataTreeJSAction";
import { generateDataTreeWidget } from "entities/DataTree/dataTreeWidget"; import { generateDataTreeWidget } from "entities/DataTree/dataTreeWidget";
import log from "loglevel";
import { import {
ENTITY_TYPE, ENTITY_TYPE,
EvaluationSubstitutionType, EvaluationSubstitutionType,
} from "ee/entities/DataTree/types"; } from "ee/entities/DataTree/types";
import { generateDataTreeModuleInputs } from "ee/entities/DataTree/utils"; import { generateDataTreeModuleInputs } from "ee/entities/DataTree/utils";
import type { import type { EntityTypeValue } from "ee/entities/DataTree/types";
DataTreeSeed, import type { ConfigTree, UnEvalTree } from "entities/DataTree/dataTreeTypes";
AppsmithEntity,
EntityTypeValue,
} from "ee/entities/DataTree/types";
import type {
unEvalAndConfigTree,
ConfigTree,
UnEvalTree,
} from "entities/DataTree/dataTreeTypes";
import { isEmpty } from "lodash"; import { isEmpty } from "lodash";
import { generateModuleInstance } from "ee/entities/DataTree/dataTreeModuleInstance"; import { generateModuleInstance } from "ee/entities/DataTree/dataTreeModuleInstance";
import { import { endSpan, startRootSpan } from "UITelemetry/generateTraces";
endSpan, import type { ActionDataState } from "ee/reducers/entityReducers/actionsReducer";
startNestedSpan, import type { JSCollectionDataState } from "ee/reducers/entityReducers/jsActionsReducer";
startRootSpan, import type { LayoutSystemTypes } from "layoutSystems/types";
} from "UITelemetry/generateTraces"; import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
import type { MetaState } from "reducers/entityReducers/metaReducer";
import type { LoadingEntitiesState } from "reducers/evaluationReducers/loadingEntitiesReducer";
import type { MetaWidgetsReduxState } from "reducers/entityReducers/metaWidgetsReducer";
import type { Module } from "ee/constants/ModuleConstants";
import type { ModuleInstance } from "ee/constants/ModuleInstanceConstants";
import type {
DependencyMap,
FormEditorConfigs,
} from "utils/DynamicBindingUtils";
export class DataTreeFactory { export class DataTreeFactory {
static create({ public static metaWidgets(
actions, metaWidgets: MetaWidgetsReduxState,
appData, widgetsMeta: MetaState,
editorConfigs, loadingEntities: LoadingEntitiesState,
isMobile, ) {
jsActions,
layoutSystemType,
loadingEntities,
metaWidgets,
moduleInputs,
moduleInstanceEntities,
moduleInstances,
pluginDependencyConfig,
theme,
widgets,
widgetsMeta,
}: DataTreeSeed): unEvalAndConfigTree {
const dataTree: UnEvalTree = {}; const dataTree: UnEvalTree = {};
const configTree: ConfigTree = {}; const configTree: ConfigTree = {};
const start = performance.now(); const metaWidgetsSpan = startRootSpan("DataTreeFactory.metaWidgets");
const startActions = performance.now();
const rootSpan = startRootSpan("DataTreeFactory.create");
const actionsSpan = startNestedSpan("DataTreeFactory.actions", rootSpan);
actions.forEach((action) => { Object.values(metaWidgets).forEach((widget) => {
const editorConfig = editorConfigs[action.config.pluginId]; const { configEntity, unEvalEntity } = generateDataTreeWidget(
const dependencyConfig = pluginDependencyConfig[action.config.pluginId]; widget,
const { configEntity, unEvalEntity } = generateDataTreeAction( widgetsMeta[widget.metaWidgetId || widget.widgetId],
action, loadingEntities,
editorConfig,
dependencyConfig,
); );
dataTree[action.config.name] = unEvalEntity; dataTree[widget.widgetName] = unEvalEntity;
configTree[action.config.name] = configEntity; configTree[widget.widgetName] = configEntity;
}); });
const endActions = performance.now(); endSpan(metaWidgetsSpan);
endSpan(actionsSpan); return {
dataTree,
configTree,
};
}
const startJsActions = performance.now(); // eslint-disable-next-line @typescript-eslint/no-explicit-any
const jsActionsSpan = startNestedSpan( public static widgets(
"DataTreeFactory.jsActions", moduleInputs: Module["inputsForm"],
rootSpan, moduleInstances: Record<string, ModuleInstance> | null,
); moduleInstanceEntities: null,
widgets: CanvasWidgetsReduxState,
jsActions.forEach((js) => { widgetsMeta: MetaState,
const { configEntity, unEvalEntity } = generateDataTreeJSAction(js); loadingEntities: LoadingEntitiesState,
layoutSystemType: LayoutSystemTypes,
dataTree[js.config.name] = unEvalEntity; isMobile: boolean,
configTree[js.config.name] = configEntity; ) {
}); const dataTree: UnEvalTree = {};
const endJsActions = performance.now(); const configTree: ConfigTree = {};
const widgetsSpan = startRootSpan("DataTreeFactory.widgets");
endSpan(jsActionsSpan);
const startWidgets = performance.now();
const widgetsSpan = startNestedSpan("DataTreeFactory.widgets", rootSpan);
if (!isEmpty(moduleInputs)) { if (!isEmpty(moduleInputs)) {
const { configEntity, unEvalEntity } = const { configEntity, unEvalEntity } =
@ -120,54 +103,60 @@ export class DataTreeFactory {
dataTree[widget.widgetName] = unEvalEntity; dataTree[widget.widgetName] = unEvalEntity;
configTree[widget.widgetName] = configEntity; configTree[widget.widgetName] = configEntity;
}); });
const endWidgets = performance.now();
endSpan(widgetsSpan); endSpan(widgetsSpan);
dataTree.appsmith = { return {
...appData, dataTree,
// combine both persistent and transient state with the transient state configTree,
// taking precedence in case the key is the same
store: appData.store,
theme,
} as AppsmithEntity;
(dataTree.appsmith as AppsmithEntity).ENTITY_TYPE = ENTITY_TYPE.APPSMITH;
const startMetaWidgets = performance.now();
const metaWidgetsSpan = startNestedSpan(
"DataTreeFactory.metaWidgets",
rootSpan,
);
Object.values(metaWidgets).forEach((widget) => {
const { configEntity, unEvalEntity } = generateDataTreeWidget(
widget,
widgetsMeta[widget.metaWidgetId || widget.widgetId],
loadingEntities,
);
dataTree[widget.widgetName] = unEvalEntity;
configTree[widget.widgetName] = configEntity;
});
const endMetaWidgets = performance.now();
endSpan(metaWidgetsSpan);
endSpan(rootSpan);
const end = performance.now();
const out = {
total: end - start,
widgets: endWidgets - startWidgets,
actions: endActions - startActions,
jsActions: endJsActions - startJsActions,
metaWidgets: endMetaWidgets - startMetaWidgets,
}; };
}
log.debug("### Create unevalTree timing", out); public static jsActions(jsActions: JSCollectionDataState) {
const dataTree: UnEvalTree = {};
const configTree: ConfigTree = {};
const actionsSpan = startRootSpan("DataTreeFactory.jsActions");
return { unEvalTree: dataTree, configTree }; jsActions.forEach((js) => {
const { configEntity, unEvalEntity } = generateDataTreeJSAction(js);
dataTree[js.config.name] = unEvalEntity;
configTree[js.config.name] = configEntity;
});
endSpan(actionsSpan);
return {
dataTree,
configTree,
};
}
public static actions(
actions: ActionDataState,
editorConfigs: FormEditorConfigs,
pluginDependencyConfig: Record<string, DependencyMap>,
) {
const dataTree: UnEvalTree = {};
const configTree: ConfigTree = {};
const actionsSpan = startRootSpan("DataTreeFactory.actions");
actions.forEach((action) => {
const editorConfig = editorConfigs[action.config.pluginId];
const dependencyConfig = pluginDependencyConfig[action.config.pluginId];
const { configEntity, unEvalEntity } = generateDataTreeAction(
action,
editorConfig,
dependencyConfig,
);
dataTree[action.config.name] = unEvalEntity;
configTree[action.config.name] = configEntity;
});
endSpan(actionsSpan);
return {
dataTree,
configTree,
};
} }
} }

View File

@ -225,8 +225,6 @@ describe("generateDataTreeWidget", () => {
parentColumnSpace: 0, parentColumnSpace: 0,
parentRowSpace: 0, parentRowSpace: 0,
rightColumn: 0, rightColumn: 0,
renderMode: RenderModes.CANVAS,
version: 0,
topRow: 0, topRow: 0,
widgetId: "123", widgetId: "123",
widgetName: "Input1", widgetName: "Input1",

View File

@ -1,5 +1,5 @@
import { getAllPathsFromPropertyConfig } from "entities/Widget/utils"; import { getAllPathsFromPropertyConfig } from "entities/Widget/utils";
import _, { get, isEmpty } from "lodash"; import _, { get, isEmpty, omit } from "lodash";
import memoize from "micro-memoize"; import memoize from "micro-memoize";
import type { FlattenedWidgetProps } from "reducers/entityReducers/canvasWidgetsReducer"; import type { FlattenedWidgetProps } from "reducers/entityReducers/canvasWidgetsReducer";
import type { DynamicPath } from "utils/DynamicBindingUtils"; import type { DynamicPath } from "utils/DynamicBindingUtils";
@ -21,6 +21,7 @@ import WidgetFactory from "WidgetProvider/factory";
import { getComponentDimensions } from "layoutSystems/common/utils/ComponentSizeUtils"; import { getComponentDimensions } from "layoutSystems/common/utils/ComponentSizeUtils";
import type { LoadingEntitiesState } from "reducers/evaluationReducers/loadingEntitiesReducer"; import type { LoadingEntitiesState } from "reducers/evaluationReducers/loadingEntitiesReducer";
import { LayoutSystemTypes } from "layoutSystems/types"; import { LayoutSystemTypes } from "layoutSystems/types";
import { WIDGET_PROPS_TO_SKIP_FROM_EVAL } from "constants/WidgetConstants";
/** /**
* *
@ -176,13 +177,17 @@ export function getSetterConfig(
// Widget changes only when dynamicBindingPathList changes. // Widget changes only when dynamicBindingPathList changes.
// Only meta properties change very often, for example typing in an input or selecting a table row. // Only meta properties change very often, for example typing in an input or selecting a table row.
const generateDataTreeWidgetWithoutMeta = ( const generateDataTreeWidgetWithoutMeta = (
widget: FlattenedWidgetProps, widgetWithEval: FlattenedWidgetProps,
): { ): {
dataTreeWidgetWithoutMetaProps: WidgetEntity; dataTreeWidgetWithoutMetaProps: WidgetEntity;
overridingMetaPropsMap: Record<string, boolean>; overridingMetaPropsMap: Record<string, boolean>;
defaultMetaProps: Record<string, unknown>; defaultMetaProps: Record<string, unknown>;
entityConfig: WidgetEntityConfig; entityConfig: WidgetEntityConfig;
} => { } => {
const widget = omit(
widgetWithEval,
Object.keys(WIDGET_PROPS_TO_SKIP_FROM_EVAL),
) as FlattenedWidgetProps;
// TODO: Fix this the next time the file is edited // TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const derivedProps: any = {}; const derivedProps: any = {};

View File

@ -1,6 +1,7 @@
import { createReducer } from "utils/ReducerUtils"; import { createReducer } from "utils/ReducerUtils";
import type { ReduxAction } from "ee/constants/ReduxActionConstants"; import type { ReduxAction } from "ee/constants/ReduxActionConstants";
import { ReduxActionTypes } from "ee/constants/ReduxActionConstants"; import { ReduxActionTypes } from "ee/constants/ReduxActionConstants";
import { isEqual } from "lodash";
export type LoadingEntitiesState = Set<string>; export type LoadingEntitiesState = Set<string>;
@ -10,7 +11,16 @@ const loadingEntitiesReducer = createReducer(initialState, {
[ReduxActionTypes.SET_LOADING_ENTITIES]: ( [ReduxActionTypes.SET_LOADING_ENTITIES]: (
state: LoadingEntitiesState, state: LoadingEntitiesState,
action: ReduxAction<Set<string>>, action: ReduxAction<Set<string>>,
): LoadingEntitiesState => action.payload, ): LoadingEntitiesState => {
const newLoadingEntities = action.payload;
// its just a set with string properties time complexity of equal is not too bad
if (isEqual(state, newLoadingEntities)) {
return state;
}
return newLoadingEntities;
},
[ReduxActionTypes.FETCH_PAGE_INIT]: () => initialState, [ReduxActionTypes.FETCH_PAGE_INIT]: () => initialState,
}); });

View File

@ -23,6 +23,7 @@ import { sortJSExecutionDataByCollectionId } from "workers/Evaluation/JSObject/u
import type { LintTreeSagaRequestData } from "plugins/Linting/types"; import type { LintTreeSagaRequestData } from "plugins/Linting/types";
import { evalErrorHandler } from "./EvalErrorHandler"; import { evalErrorHandler } from "./EvalErrorHandler";
import { getUnevaluatedDataTree } from "selectors/dataTreeSelectors"; import { getUnevaluatedDataTree } from "selectors/dataTreeSelectors";
import { endSpan, startRootSpan } from "UITelemetry/generateTraces";
export interface UpdateDataTreeMessageData { export interface UpdateDataTreeMessageData {
workerResponse: EvalTreeResponseData; workerResponse: EvalTreeResponseData;
@ -166,9 +167,13 @@ export function* handleEvalWorkerMessage(message: TMessage<any>) {
} }
case MAIN_THREAD_ACTION.UPDATE_DATATREE: { case MAIN_THREAD_ACTION.UPDATE_DATATREE: {
const { workerResponse } = data as UpdateDataTreeMessageData; const { workerResponse } = data as UpdateDataTreeMessageData;
const rootSpan = startRootSpan("DataTreeFactory.create");
const unEvalAndConfigTree: ReturnType<typeof getUnevaluatedDataTree> = const unEvalAndConfigTree: ReturnType<typeof getUnevaluatedDataTree> =
yield select(getUnevaluatedDataTree); yield select(getUnevaluatedDataTree);
endSpan(rootSpan);
yield call(updateDataTreeHandler, { yield call(updateDataTreeHandler, {
evalTreeResponse: workerResponse as EvalTreeResponseData, evalTreeResponse: workerResponse as EvalTreeResponseData,
unevalTree: unEvalAndConfigTree.unEvalTree || {}, unevalTree: unEvalAndConfigTree.unEvalTree || {},

View File

@ -350,10 +350,14 @@ export function* evaluateAndExecuteDynamicTrigger(
callbackData?: Array<any>, callbackData?: Array<any>,
globalContext?: Record<string, unknown>, globalContext?: Record<string, unknown>,
) { ) {
const rootSpan = startRootSpan("DataTreeFactory.create");
const unEvalTree: ReturnType<typeof getUnevaluatedDataTree> = yield select( const unEvalTree: ReturnType<typeof getUnevaluatedDataTree> = yield select(
getUnevaluatedDataTree, getUnevaluatedDataTree,
); );
endSpan(rootSpan);
log.debug({ execute: dynamicTrigger }); log.debug({ execute: dynamicTrigger });
const response: { errors: EvaluationError[]; result: unknown } = yield call( const response: { errors: EvaluationError[]; result: unknown } = yield call(
evalWorker.request, evalWorker.request,
@ -497,8 +501,12 @@ export function* executeJSFunction(
export // TODO: Fix this the next time the file is edited export // TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
function* validateProperty(property: string, value: any, props: WidgetProps) { function* validateProperty(property: string, value: any, props: WidgetProps) {
const rootSpan = startRootSpan("DataTreeFactory.create");
const unEvalAndConfigTree: ReturnType<typeof getUnevaluatedDataTree> = const unEvalAndConfigTree: ReturnType<typeof getUnevaluatedDataTree> =
yield select(getUnevaluatedDataTree); yield select(getUnevaluatedDataTree);
endSpan(rootSpan);
const configTree = unEvalAndConfigTree.configTree; const configTree = unEvalAndConfigTree.configTree;
const entityConfig = configTree[props.widgetName] as WidgetEntityConfig; const entityConfig = configTree[props.widgetName] as WidgetEntityConfig;
const validation = entityConfig?.validationPaths[property]; const validation = entityConfig?.validationPaths[property];
@ -661,9 +669,14 @@ function* evalAndLintingHandler(
return; return;
} }
const rootSpan = startRootSpan("DataTreeFactory.create");
// Generate all the data needed for both eval and linting // Generate all the data needed for both eval and linting
const unEvalAndConfigTree: ReturnType<typeof getUnevaluatedDataTree> = const unEvalAndConfigTree: ReturnType<typeof getUnevaluatedDataTree> =
yield select(getUnevaluatedDataTree); yield select(getUnevaluatedDataTree);
endSpan(rootSpan);
const postEvalActions = getPostEvalActions(action); const postEvalActions = getPostEvalActions(action);
const fn: (...args: unknown[]) => CallEffect<unknown> | ForkEffect<unknown> = const fn: (...args: unknown[]) => CallEffect<unknown> | ForkEffect<unknown> =
isBlockingCall ? call : fork; isBlockingCall ? call : fork;

View File

@ -11,17 +11,23 @@ import {
getCurrentModuleActions, getCurrentModuleActions,
getCurrentModuleJSCollections, getCurrentModuleJSCollections,
} from "ee/selectors/entitiesSelector"; } from "ee/selectors/entitiesSelector";
import type { WidgetEntity } from "ee/entities/DataTree/types"; import type { AppsmithEntity, WidgetEntity } from "ee/entities/DataTree/types";
import type { DataTree } from "entities/DataTree/dataTreeTypes"; import type {
import { DataTreeFactory } from "entities/DataTree/dataTreeFactory"; ConfigTree,
DataTree,
UnEvalTree,
} from "entities/DataTree/dataTreeTypes";
import {
DataTreeFactory,
ENTITY_TYPE,
} from "entities/DataTree/dataTreeFactory";
import { import {
getIsMobileBreakPoint, getIsMobileBreakPoint,
getMetaWidgets, getMetaWidgets,
getWidgetsForEval, getWidgets,
getWidgetsMeta, getWidgetsMeta,
} from "sagas/selectors"; } from "sagas/selectors";
import "url-search-params-polyfill"; import "url-search-params-polyfill";
import { getPageList } from "./appViewSelectors";
import type { AppState } from "ee/reducers"; import type { AppState } from "ee/reducers";
import { getSelectedAppThemeProperties } from "./appThemingSelectors"; import { getSelectedAppThemeProperties } from "./appThemingSelectors";
import type { LoadingEntitiesState } from "reducers/evaluationReducers/loadingEntitiesReducer"; import type { LoadingEntitiesState } from "reducers/evaluationReducers/loadingEntitiesReducer";
@ -55,25 +61,20 @@ const getLayoutSystemPayload = createSelector(
}, },
); );
const getCurrentActionEntities = createSelector( const getCurrentActionsEntities = createSelector(
getCurrentActions, getCurrentActions,
getCurrentModuleActions, getCurrentModuleActions,
getCurrentWorkflowActions, getCurrentWorkflowActions,
(actions, moduleActions, workflowActions) => {
return [...actions, ...moduleActions, ...workflowActions];
},
);
const getCurrentJSActionsEntities = createSelector(
getCurrentJSCollections, getCurrentJSCollections,
getCurrentModuleJSCollections, getCurrentModuleJSCollections,
getCurrentWorkflowJSActions, getCurrentWorkflowJSActions,
( (jsActions, moduleJSActions, workflowJsActions) => {
actions, return [...jsActions, ...moduleJSActions, ...workflowJsActions];
moduleActions,
workflowActions,
jsActions,
moduleJSActions,
workflowJsActions,
) => {
return {
actions: [...actions, ...moduleActions, ...workflowActions],
jsActions: [...jsActions, ...moduleJSActions, ...workflowJsActions],
};
}, },
); );
@ -90,49 +91,76 @@ const getModulesData = createSelector(
}, },
); );
export const getUnevaluatedDataTree = createSelector( const getActionsFromUnevaluatedDataTree = createSelector(
getCurrentActionEntities, getCurrentActionsEntities,
getWidgetsForEval,
getWidgetsMeta,
getPageList,
getAppData,
getPluginEditorConfigs, getPluginEditorConfigs,
getPluginDependencyConfig, getPluginDependencyConfig,
getSelectedAppThemeProperties, (actions, editorConfigs, pluginDependencyConfig) =>
getMetaWidgets, DataTreeFactory.actions(actions, editorConfigs, pluginDependencyConfig),
getLayoutSystemPayload, );
getLoadingEntities,
getModulesData,
(
currentActionEntities,
widgets,
widgetsMeta,
pageListPayload,
appData,
editorConfigs,
pluginDependencyConfig,
selectedAppThemeProperty,
metaWidgets,
layoutSystemPayload,
loadingEntities,
modulesData,
) => {
const pageList = pageListPayload || [];
return DataTreeFactory.create({ const getJSActionsFromUnevaluatedDataTree = createSelector(
...currentActionEntities, getCurrentJSActionsEntities,
(jsActions) => DataTreeFactory.jsActions(jsActions),
);
const getWidgetsFromUnevaluatedDataTree = createSelector(
getModulesData,
getWidgets,
getWidgetsMeta,
getLoadingEntities,
getLayoutSystemPayload,
(moduleData, widgets, widgetsMeta, loadingEntities, layoutSystemPayload) =>
DataTreeFactory.widgets(
moduleData.moduleInputs,
moduleData.moduleInstances,
moduleData.moduleInstanceEntities,
widgets, widgets,
widgetsMeta, widgetsMeta,
pageList,
appData,
editorConfigs,
pluginDependencyConfig,
theme: selectedAppThemeProperty,
metaWidgets,
loadingEntities, loadingEntities,
...layoutSystemPayload, layoutSystemPayload.layoutSystemType,
...modulesData, layoutSystemPayload.isMobile,
}); ),
);
const getMetaWidgetsFromUnevaluatedDataTree = createSelector(
getMetaWidgets,
getWidgetsMeta,
getLoadingEntities,
(metaWidgets, widgetsMeta, loadingEntities) =>
DataTreeFactory.metaWidgets(metaWidgets, widgetsMeta, loadingEntities),
);
export const getUnevaluatedDataTree = createSelector(
getActionsFromUnevaluatedDataTree,
getJSActionsFromUnevaluatedDataTree,
getWidgetsFromUnevaluatedDataTree,
getMetaWidgetsFromUnevaluatedDataTree,
getAppData,
getSelectedAppThemeProperties,
(actions, jsActions, widgets, metaWidgets, appData, theme) => {
let dataTree: UnEvalTree = {
...actions.dataTree,
...jsActions.dataTree,
...widgets.dataTree,
};
let configTree: ConfigTree = {
...actions.configTree,
...jsActions.configTree,
...widgets.configTree,
};
dataTree.appsmith = {
...appData,
// combine both persistent and transient state with the transient state
// taking precedence in case the key is the same
store: appData.store,
theme,
} as AppsmithEntity;
(dataTree.appsmith as AppsmithEntity).ENTITY_TYPE = ENTITY_TYPE.APPSMITH;
dataTree = { ...dataTree, ...metaWidgets.dataTree };
configTree = { ...configTree, ...metaWidgets.configTree };
return { unEvalTree: dataTree, configTree };
}, },
); );