Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
236 changes: 123 additions & 113 deletions app/client/src/entities/DataTree/dataTreeFactory.ts
Original file line number Diff line number Diff line change
@@ -1,88 +1,99 @@
import { generateDataTreeAction } from "ee/entities/DataTree/dataTreeAction";
import { generateDataTreeJSAction } from "ee/entities/DataTree/dataTreeJSAction";
import { generateDataTreeWidget } from "entities/DataTree/dataTreeWidget";
import log from "loglevel";
import type { DependencyMap } from "utils/DynamicBindingUtils";

import {
ENTITY_TYPE,
EvaluationSubstitutionType,
} from "ee/entities/DataTree/types";
import { generateDataTreeModuleInputs } from "ee/entities/DataTree/utils";
import type {
DataTreeSeed,
AppsmithEntity,
EntityTypeValue,
} from "ee/entities/DataTree/types";
import type {
unEvalAndConfigTree,
ConfigTree,
UnEvalTree,
} from "entities/DataTree/dataTreeTypes";
import type { EntityTypeValue } from "ee/entities/DataTree/types";
import type { ConfigTree, UnEvalTree } from "entities/DataTree/dataTreeTypes";
import { isEmpty } from "lodash";
import { generateModuleInstance } from "ee/entities/DataTree/dataTreeModuleInstance";
import {
endSpan,
startNestedSpan,
startRootSpan,
} from "UITelemetry/generateTraces";
import { endSpan, startRootSpan } from "UITelemetry/generateTraces";

import type { LayoutSystemTypes } from "layoutSystems/types";
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 { ActionDataState } from "ee/reducers/entityReducers/actionsReducer";
import type { JSCollectionDataState } from "ee/reducers/entityReducers/jsActionsReducer";
import type { ModuleInputSection } from "ee/constants/ModuleConstants";
import type { ModuleInstance } from "ee/constants/ModuleInstanceConstants";

export class DataTreeFactory {
static create({
actions,
appData,
editorConfigs,
isMobile,
jsActions,
layoutSystemType,
loadingEntities,
metaWidgets,
moduleInputs,
moduleInstanceEntities,
moduleInstances,
pluginDependencyConfig,
theme,
widgets,
widgetsMeta,
}: DataTreeSeed): unEvalAndConfigTree {
static metaWidgets(
metaWidgets: MetaWidgetsReduxState,
widgetsMeta: MetaState,
loadingEntities: LoadingEntitiesState,
) {
const widgetsSpan = startRootSpan("DataTreeFactory.metaWidgets");

const res = Object.values(metaWidgets).reduce(
(acc, widget) => {
const { configEntity, unEvalEntity } = generateDataTreeWidget(
widget,
widgetsMeta[widget.metaWidgetId || widget.widgetId],
loadingEntities,
);

acc.dataTree[widget.widgetName] = unEvalEntity;
acc.configTree[widget.widgetName] = configEntity;

return acc;
},
{ dataTree: {} as UnEvalTree, configTree: {} as ConfigTree },
);

endSpan(widgetsSpan);

return res;
}

static widgets(
widgets: CanvasWidgetsReduxState,
widgetsMeta: MetaState,
loadingEntities: LoadingEntitiesState,
layoutSystemType: LayoutSystemTypes,
isMobile: boolean,
) {
const widgetsSpan = startRootSpan("DataTreeFactory.widgets");

const dataTree: UnEvalTree = {};
const configTree: ConfigTree = {};
const start = performance.now();
const startActions = performance.now();
const rootSpan = startRootSpan("DataTreeFactory.create");
const actionsSpan = startNestedSpan("DataTreeFactory.actions", rootSpan);

actions.forEach((action) => {
const editorConfig = editorConfigs[action.config.pluginId];
const dependencyConfig = pluginDependencyConfig[action.config.pluginId];
const { configEntity, unEvalEntity } = generateDataTreeAction(
action,
editorConfig,
dependencyConfig,

Object.values(widgets).forEach((widget) => {
const { configEntity, unEvalEntity } = generateDataTreeWidget(
widget,
widgetsMeta[widget.metaWidgetId || widget.widgetId],
loadingEntities,
layoutSystemType,
isMobile,
);

dataTree[action.config.name] = unEvalEntity;
configTree[action.config.name] = configEntity;
dataTree[widget.widgetName] = unEvalEntity;
configTree[widget.widgetName] = configEntity;
});
const endActions = performance.now();

endSpan(actionsSpan);

const startJsActions = performance.now();
const jsActionsSpan = startNestedSpan(
"DataTreeFactory.jsActions",
rootSpan,
);

jsActions.forEach((js) => {
const { configEntity, unEvalEntity } = generateDataTreeJSAction(js);
endSpan(widgetsSpan);

dataTree[js.config.name] = unEvalEntity;
configTree[js.config.name] = configEntity;
});
const endJsActions = performance.now();
return { dataTree, configTree };
}

endSpan(jsActionsSpan);
public static moduleComponents(
moduleInputs: ModuleInputSection[],
moduleInstances: Record<string, ModuleInstance> | null,
moduleInstanceEntities: unknown,
) {
const moduleComponentsSpan = startRootSpan(
"DataTreeFactory.moduleComponents",
);

const startWidgets = performance.now();
const widgetsSpan = startNestedSpan("DataTreeFactory.widgets", rootSpan);
const dataTree: UnEvalTree = {};
const configTree: ConfigTree = {};

if (!isEmpty(moduleInputs)) {
const { configEntity, unEvalEntity } =
Expand All @@ -108,66 +119,65 @@ export class DataTreeFactory {
});
}

Object.values(widgets).forEach((widget) => {
const { configEntity, unEvalEntity } = generateDataTreeWidget(
widget,
widgetsMeta[widget.metaWidgetId || widget.widgetId],
loadingEntities,
layoutSystemType,
isMobile,
);
endSpan(moduleComponentsSpan);

dataTree[widget.widgetName] = unEvalEntity;
configTree[widget.widgetName] = configEntity;
});
return {
dataTree,
configTree,
};
}

const endWidgets = performance.now();
static jsActions(jsActions: JSCollectionDataState) {
const actionsSpan = startRootSpan("DataTreeFactory.jsActions");

endSpan(widgetsSpan);
const res = jsActions.reduce(
(acc, js) => {
const { configEntity, unEvalEntity } = generateDataTreeJSAction(js);

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;

const startMetaWidgets = performance.now();
const metaWidgetsSpan = startNestedSpan(
"DataTreeFactory.metaWidgets",
rootSpan,
acc.dataTree[js.config.name] = unEvalEntity;
acc.configTree[js.config.name] = configEntity;

return acc;
},
{
dataTree: {} as UnEvalTree,
configTree: {} as ConfigTree,
},
);

Object.values(metaWidgets).forEach((widget) => {
const { configEntity, unEvalEntity } = generateDataTreeWidget(
widget,
widgetsMeta[widget.metaWidgetId || widget.widgetId],
loadingEntities,
);
endSpan(actionsSpan);

dataTree[widget.widgetName] = unEvalEntity;
configTree[widget.widgetName] = configEntity;
});
const endMetaWidgets = performance.now();
return res;
}

endSpan(metaWidgetsSpan);
endSpan(rootSpan);
public static actions(
actions: ActionDataState,
editorConfigs: Record<string, unknown[]>,
pluginDependencyConfig: Record<string, DependencyMap>,
) {
const actionsSpan = startRootSpan("DataTreeFactory.actions");

const res = actions.reduce(
(acc, action) => {
const editorConfig = editorConfigs[action.config.pluginId];
const dependencyConfig = pluginDependencyConfig[action.config.pluginId];
const { configEntity, unEvalEntity } = generateDataTreeAction(
action,
editorConfig,
dependencyConfig,
);

const end = performance.now();
acc.dataTree[action.config.name] = unEvalEntity;
acc.configTree[action.config.name] = configEntity;

const out = {
total: end - start,
widgets: endWidgets - startWidgets,
actions: endActions - startActions,
jsActions: endJsActions - startJsActions,
metaWidgets: endMetaWidgets - startMetaWidgets,
};
return acc;
},
{ dataTree: {} as UnEvalTree, configTree: {} as ConfigTree },
);

log.debug("### Create unevalTree timing", out);
endSpan(actionsSpan);

return { unEvalTree: dataTree, configTree };
return res;
Comment on lines +153 to +180
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure safe access to editorConfig and dependencyConfig

If editorConfigs or pluginDependencyConfig do not contain the pluginId, accessing them may result in undefined. Consider providing default values to prevent potential runtime errors.

Apply this diff to add default values:

 const editorConfig = editorConfigs[action.config.pluginId] || [];
 const dependencyConfig = pluginDependencyConfig[action.config.pluginId] || {};
 const { configEntity, unEvalEntity } = generateDataTreeAction(
   action,
   editorConfig,
   dependencyConfig,
 );
📝 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. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static actions(
actions: ActionDataState,
editorConfigs: Record<string, unknown[]>,
pluginDependencyConfig: Record<string, DependencyMap>,
) {
const actionsSpan = startRootSpan("DataTreeFactory.actions");
const res = actions.reduce(
(acc, action) => {
const editorConfig = editorConfigs[action.config.pluginId];
const dependencyConfig = pluginDependencyConfig[action.config.pluginId];
const { configEntity, unEvalEntity } = generateDataTreeAction(
action,
editorConfig,
dependencyConfig,
);
const end = performance.now();
acc.dataTree[action.config.name] = unEvalEntity;
acc.configTree[action.config.name] = configEntity;
const out = {
total: end - start,
widgets: endWidgets - startWidgets,
actions: endActions - startActions,
jsActions: endJsActions - startJsActions,
metaWidgets: endMetaWidgets - startMetaWidgets,
};
return acc;
},
{ dataTree: {} as UnEvalTree, configTree: {} as ConfigTree },
);
log.debug("### Create unevalTree timing", out);
endSpan(actionsSpan);
return { unEvalTree: dataTree, configTree };
return res;
public static actions(
actions: ActionDataState,
editorConfigs: Record<string, unknown[]>,
pluginDependencyConfig: Record<string, DependencyMap>,
) {
const actionsSpan = startRootSpan("DataTreeFactory.actions");
const res = actions.reduce(
(acc, action) => {
const editorConfig = editorConfigs[action.config.pluginId] || [];
const dependencyConfig = pluginDependencyConfig[action.config.pluginId] || {};
const { configEntity, unEvalEntity } = generateDataTreeAction(
action,
editorConfig,
dependencyConfig,
);
acc.dataTree[action.config.name] = unEvalEntity;
acc.configTree[action.config.name] = configEntity;
return acc;
},
{ dataTree: {} as UnEvalTree, configTree: {} as ConfigTree },
);
endSpan(actionsSpan);
return res;

}
}

Expand Down
2 changes: 0 additions & 2 deletions app/client/src/entities/DataTree/dataTreeWidget.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,6 @@ describe("generateDataTreeWidget", () => {
parentColumnSpace: 0,
parentRowSpace: 0,
rightColumn: 0,
renderMode: RenderModes.CANVAS,
version: 0,
topRow: 0,
widgetId: "123",
widgetName: "Input1",
Expand Down
9 changes: 7 additions & 2 deletions app/client/src/entities/DataTree/dataTreeWidget.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getAllPathsFromPropertyConfig } from "entities/Widget/utils";
import _, { get, isEmpty } from "lodash";
import _, { get, isEmpty, omit } from "lodash";
import memoize from "micro-memoize";
import type { FlattenedWidgetProps } from "reducers/entityReducers/canvasWidgetsReducer";
import type { DynamicPath } from "utils/DynamicBindingUtils";
Expand All @@ -21,6 +21,7 @@ import WidgetFactory from "WidgetProvider/factory";
import { getComponentDimensions } from "layoutSystems/common/utils/ComponentSizeUtils";
import type { LoadingEntitiesState } from "reducers/evaluationReducers/loadingEntitiesReducer";
import { LayoutSystemTypes } from "layoutSystems/types";
import { WIDGET_PROPS_TO_SKIP_FROM_EVAL } from "constants/WidgetConstants";

/**
*
Expand Down Expand Up @@ -176,13 +177,17 @@ export function getSetterConfig(
// Widget changes only when dynamicBindingPathList changes.
// Only meta properties change very often, for example typing in an input or selecting a table row.
const generateDataTreeWidgetWithoutMeta = (
widget: FlattenedWidgetProps,
pureWidget: FlattenedWidgetProps,
): {
dataTreeWidgetWithoutMetaProps: WidgetEntity;
overridingMetaPropsMap: Record<string, boolean>;
defaultMetaProps: Record<string, unknown>;
entityConfig: WidgetEntityConfig;
} => {
const widget = omit(
pureWidget,
Object.keys(WIDGET_PROPS_TO_SKIP_FROM_EVAL),
) as FlattenedWidgetProps;
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const derivedProps: any = {};
Expand Down
1 change: 1 addition & 0 deletions app/client/src/entities/Widget/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ const getAllPathsFromPropertyConfigWithoutMemo = (

export const getAllPathsFromPropertyConfig = memoize(
getAllPathsFromPropertyConfigWithoutMemo,

{ maxSize: 1000 },
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createReducer } from "utils/ReducerUtils";
import type { ReduxAction } from "ee/constants/ReduxActionConstants";
import { ReduxActionTypes } from "ee/constants/ReduxActionConstants";
import { isEqual } from "lodash";

export type LoadingEntitiesState = Set<string>;

Expand All @@ -10,7 +11,16 @@ const loadingEntitiesReducer = createReducer(initialState, {
[ReduxActionTypes.SET_LOADING_ENTITIES]: (
state: LoadingEntitiesState,
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,
});

Expand Down
Loading