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:
parent
bc165cf827
commit
c8943509a0
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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/**/**/*
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"];
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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 = {};
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 || {},
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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 };
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user