PromucFlow_constructor/app/client/src/selectors/dataTreeSelectors.ts
Rishabh Rathod 8a16903f31
feat: Add currentPage, workspace, application name to appsmith context (#38114)
## Description

Add currentPageName, AppName and workspace name to appsmith context
object

Fixes https://github.com/appsmithorg/appsmith/issues/38046

## Automation

/ok-to-test tags="@tag.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/12462985063>
> Commit: eec4292d3bc1db86bdcf089061a6d2c7d8fa2205
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=12462985063&attempt=2"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.JS`
> Spec:
> <hr>Mon, 23 Dec 2024 09:14:06 UTC
<!-- 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 `AppsmithEntity` interface with new properties:
`currentPageName`, `workspaceName`, and `appName`.
- Updated `getUnevaluatedDataTree` selector to accept additional
parameters, providing more contextual information about the current
application state.
- Introduced a new internal selector to retrieve the current page's
name.

- **Bug Fixes**
- Adjusted expected hint text in autocomplete tests for JavaScript
objects to align with updated behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: “sneha122” <“sneha@appsmith.com”>
2024-12-23 16:33:50 +05:30

240 lines
7.3 KiB
TypeScript

import { createSelector } from "reselect";
import {
getCurrentActions,
getAppData,
getPluginDependencyConfig,
getPluginEditorConfigs,
getCurrentJSCollections,
getInputsForModule,
getModuleInstances,
getModuleInstanceEntities,
getCurrentModuleActions,
getCurrentModuleJSCollections,
} from "ee/selectors/entitiesSelector";
import type { AppsmithEntity, WidgetEntity } from "ee/entities/DataTree/types";
import type {
ConfigTree,
DataTree,
UnEvalTree,
} from "entities/DataTree/dataTreeTypes";
import {
DataTreeFactory,
ENTITY_TYPE,
} from "entities/DataTree/dataTreeFactory";
import {
getIsMobileBreakPoint,
getMetaWidgets,
getWidgets,
getWidgetsMeta,
} from "sagas/selectors";
import "url-search-params-polyfill";
import type { AppState } from "ee/reducers";
import { getSelectedAppThemeProperties } from "./appThemingSelectors";
import type { LoadingEntitiesState } from "reducers/evaluationReducers/loadingEntitiesReducer";
import _, { get } from "lodash";
import type { EvaluationError } from "utils/DynamicBindingUtils";
import { getEvalErrorPath } from "utils/DynamicBindingUtils";
import ConfigTreeActions from "utils/configTree";
import { DATATREE_INTERNAL_KEYWORDS } from "constants/WidgetValidation";
import { getLayoutSystemType } from "./layoutSystemSelectors";
import {
getCurrentWorkflowActions,
getCurrentWorkflowJSActions,
} from "ee/selectors/workflowSelectors";
import { getCurrentApplication } from "ee/selectors/applicationSelectors";
import { getCurrentAppWorkspace } from "ee/selectors/selectedWorkspaceSelectors";
import type { PageListReduxState } from "reducers/entityReducers/pageListReducer";
export const getLoadingEntities = (state: AppState) =>
state.evaluations.loadingEntities;
/**
* This selector is created to combine a couple of data points required by getUnevaluatedDataTree selector.
* Current version of reselect package only allows upto 12 arguments. Hence, this workaround.
* TODO: Figure out a better way to do this in a separate task. Or update the package if possible.
*/
const getLayoutSystemPayload = createSelector(
getLayoutSystemType,
getIsMobileBreakPoint,
(layoutSystemType, isMobile) => {
return {
layoutSystemType,
isMobile,
};
},
);
const getCurrentActionsEntities = createSelector(
getCurrentActions,
getCurrentModuleActions,
getCurrentWorkflowActions,
(actions, moduleActions, workflowActions) => {
return [...actions, ...moduleActions, ...workflowActions];
},
);
const getCurrentJSActionsEntities = createSelector(
getCurrentJSCollections,
getCurrentModuleJSCollections,
getCurrentWorkflowJSActions,
(jsActions, moduleJSActions, workflowJsActions) => {
return [...jsActions, ...moduleJSActions, ...workflowJsActions];
},
);
const getModulesData = createSelector(
getInputsForModule,
getModuleInstances,
getModuleInstanceEntities,
(moduleInputs, moduleInstances, moduleInstanceEntities) => {
return {
moduleInputs: moduleInputs,
moduleInstances: moduleInstances,
moduleInstanceEntities: moduleInstanceEntities,
};
},
);
const getActionsFromUnevaluatedDataTree = createSelector(
getCurrentActionsEntities,
getPluginEditorConfigs,
getPluginDependencyConfig,
(actions, editorConfigs, pluginDependencyConfig) =>
DataTreeFactory.actions(actions, editorConfigs, pluginDependencyConfig),
);
const getJSActionsFromUnevaluatedDataTree = createSelector(
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,
widgetsMeta,
loadingEntities,
layoutSystemPayload.layoutSystemType,
layoutSystemPayload.isMobile,
),
);
const getMetaWidgetsFromUnevaluatedDataTree = createSelector(
getMetaWidgets,
getWidgetsMeta,
getLoadingEntities,
(metaWidgets, widgetsMeta, loadingEntities) =>
DataTreeFactory.metaWidgets(metaWidgets, widgetsMeta, loadingEntities),
);
// * This is only for internal use to avoid cyclic dependency issue
const getPageListState = (state: AppState) => state.entities.pageList;
const getCurrentPageName = createSelector(
getPageListState,
(pageList: PageListReduxState) =>
pageList.pages.find((page) => page.pageId === pageList.currentPageId)
?.pageName,
);
export const getUnevaluatedDataTree = createSelector(
getActionsFromUnevaluatedDataTree,
getJSActionsFromUnevaluatedDataTree,
getWidgetsFromUnevaluatedDataTree,
getMetaWidgetsFromUnevaluatedDataTree,
getAppData,
getSelectedAppThemeProperties,
getCurrentAppWorkspace,
getCurrentApplication,
getCurrentPageName,
(
actions,
jsActions,
widgets,
metaWidgets,
appData,
theme,
currentWorkspace,
currentApplication,
getCurrentPageName,
) => {
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,
currentPageName: getCurrentPageName,
workspaceName: currentWorkspace.name,
appName: currentApplication?.name,
} as AppsmithEntity;
(dataTree.appsmith as AppsmithEntity).ENTITY_TYPE = ENTITY_TYPE.APPSMITH;
dataTree = { ...dataTree, ...metaWidgets.dataTree };
configTree = { ...configTree, ...metaWidgets.configTree };
return { unEvalTree: dataTree, configTree };
},
);
export const getEvaluationInverseDependencyMap = (state: AppState) =>
state.evaluations.dependencies.inverseDependencyMap;
export const getIsWidgetLoading = createSelector(
[getLoadingEntities, (_state: AppState, widgetName: string) => widgetName],
(loadingEntities: LoadingEntitiesState, widgetName: string) =>
loadingEntities.has(widgetName),
);
/**
* returns evaluation tree object
*
* @param state
*/
export const getDataTree = (state: AppState): DataTree =>
state.evaluations.tree;
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const getConfigTree = (): any => {
return ConfigTreeActions.getConfigTree();
};
export const getWidgetEvalValues = createSelector(
[getDataTree, (_state: AppState, widgetName: string) => widgetName],
(tree: DataTree, widgetName: string) => tree[widgetName] as WidgetEntity,
);
// For autocomplete. Use actions cached responses if
// there isn't a response already
export const getDataTreeForAutocomplete = createSelector(
getDataTree,
(tree: DataTree) => {
return _.omit(tree, Object.keys(DATATREE_INTERNAL_KEYWORDS));
},
);
export const getPathEvalErrors = createSelector(
[
getDataTreeForAutocomplete,
(_: unknown, dataTreePath: string) => dataTreePath,
],
(dataTree: DataTree, dataTreePath: string) =>
get(dataTree, getEvalErrorPath(dataTreePath), []) as EvaluationError[],
);