chore: Refactor DataSidePane to accept selector as prop to show usage messages in EE (#32870)
## Description Adds a couple of props to alter the data displayed in the datasources left pane to show the right count of entities using datasource. This props are going to be used in Packages and Workflow editors datasources page. PR for https://github.com/appsmithorg/appsmith-ee/pull/4026 ## Automation /ok-to-test tags="@tag.IDE, @tag.Datasource, @tag.Sanity" ### 🔍 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/8843287060> > Commit: 66a5282be88c27fab11316bd136d88bc74de5d5b > Cypress dashboard url: <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=8843287060&attempt=1" target="_blank">Click here!</a> <!-- 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 the `DataSidePane` to display the name and count of actions, improving the informational context for users. - Added a new selector function to calculate and display the count of queries for each datasource in the app. - Introduced a `DatasourceFactory` for generating mock datasource objects for testing purposes. - Expanded the list of plugin package names in the `MockPluginsState` to include additional plugins like `MY_SQL`, `S3`, `SNOWFLAKE`, `FIRESTORE`, `GRAPHQL`, `APPSMITH_AI`, `MS_SQL`, `ORACLE`, and `WORKFLOW`. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
85fa8cb452
commit
a72ae578bf
|
|
@ -20,7 +20,7 @@ import {
|
|||
PluginPackageName,
|
||||
PluginType,
|
||||
} from "entities/Action";
|
||||
import { find, get, groupBy, keyBy, sortBy } from "lodash";
|
||||
import { countBy, find, get, groupBy, keyBy, sortBy } from "lodash";
|
||||
import ImageAlt from "assets/images/placeholder-image.svg";
|
||||
import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
|
||||
import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants";
|
||||
|
|
@ -1540,3 +1540,22 @@ export const getJSSegmentItems = createSelector(
|
|||
|
||||
export const getSelectedTableName = (state: AppState) =>
|
||||
state.ui.datasourcePane.selectedTableName;
|
||||
|
||||
export const getDatasourceUsageCountForApp = createSelector(
|
||||
getActions,
|
||||
getDatasources,
|
||||
(state: AppState, editorType: string) => editorType,
|
||||
(actions, datasources, editorType) => {
|
||||
const actionCount = countBy(actions, "config.datasource.id");
|
||||
const actionDsMap: Record<string, string> = {};
|
||||
|
||||
datasources.forEach((ds) => {
|
||||
actionDsMap[ds.id] = `No queries in this ${editorType}`;
|
||||
});
|
||||
Object.keys(actionCount).forEach((dsId) => {
|
||||
actionDsMap[dsId] = `${actionCount[dsId]} queries in this ${editorType}`;
|
||||
});
|
||||
|
||||
return actionDsMap;
|
||||
},
|
||||
);
|
||||
|
|
|
|||
102
app/client/src/pages/Editor/IDE/LeftPane/DataSidePane.test.tsx
Normal file
102
app/client/src/pages/Editor/IDE/LeftPane/DataSidePane.test.tsx
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
import React from "react";
|
||||
import "@testing-library/jest-dom";
|
||||
import { screen } from "@testing-library/react";
|
||||
import DataSidePane from "./DataSidePane";
|
||||
import { datasourceFactory } from "test/factories/DatasourceFactory";
|
||||
import { getIDETestState } from "test/factories/AppIDEFactoryUtils";
|
||||
import { PostgresFactory } from "test/factories/Actions/Postgres";
|
||||
import type { AppState } from "@appsmith/reducers";
|
||||
import { render } from "test/testUtils";
|
||||
|
||||
const productsDS = datasourceFactory().build({
|
||||
name: "Products",
|
||||
id: "products-ds-id",
|
||||
});
|
||||
const usersDS = datasourceFactory().build({ name: "Users", id: "users-ds-id" });
|
||||
const ordersDS = datasourceFactory().build({
|
||||
name: "Orders",
|
||||
id: "orders-ds-id",
|
||||
});
|
||||
const usersAction1 = PostgresFactory.build({
|
||||
datasource: {
|
||||
id: usersDS.id,
|
||||
},
|
||||
});
|
||||
const usersAction2 = PostgresFactory.build({
|
||||
datasource: {
|
||||
id: usersDS.id,
|
||||
},
|
||||
});
|
||||
const ordersAction1 = PostgresFactory.build({
|
||||
datasource: {
|
||||
id: ordersDS.id,
|
||||
},
|
||||
});
|
||||
|
||||
describe("DataSidePane", () => {
|
||||
it("renders the ds and count by using the default selector if dsUsageSelector not passed as a props", () => {
|
||||
const state = getIDETestState({
|
||||
actions: [usersAction1, usersAction2, ordersAction1],
|
||||
datasources: [productsDS, usersDS, ordersDS],
|
||||
}) as AppState;
|
||||
render(<DataSidePane />, {
|
||||
url: "app/untitled-application-1/page1/edit/datasource/users-ds-id",
|
||||
initialState: state,
|
||||
});
|
||||
|
||||
expect(screen.getByText("Databases")).toBeInTheDocument();
|
||||
expect(screen.getByText("Products")).toBeInTheDocument();
|
||||
expect(screen.getByText("Users")).toBeInTheDocument();
|
||||
|
||||
const usersDSParentElement =
|
||||
screen.getByText("Users").parentElement?.parentElement;
|
||||
|
||||
expect(usersDSParentElement).toHaveTextContent("2 queries in this app");
|
||||
|
||||
const productsDSParentElement =
|
||||
screen.getByText("Products").parentElement?.parentElement;
|
||||
|
||||
expect(productsDSParentElement).toHaveTextContent("No queries in this app");
|
||||
|
||||
const ortdersDSParentElement =
|
||||
screen.getByText("Orders").parentElement?.parentElement;
|
||||
|
||||
expect(ortdersDSParentElement).toHaveTextContent("1 queries in this app");
|
||||
});
|
||||
|
||||
it("it uses the selector dsUsageSelector passed as prop", () => {
|
||||
const state = getIDETestState({
|
||||
datasources: [productsDS, usersDS, ordersDS],
|
||||
}) as AppState;
|
||||
|
||||
const usageSelector = () => {
|
||||
return {
|
||||
[usersDS.id]: "Rendering description for users",
|
||||
[productsDS.id]: "Rendering description for products",
|
||||
};
|
||||
};
|
||||
|
||||
render(<DataSidePane dsUsageSelector={usageSelector} />, {
|
||||
url: "app/untitled-application-1/page1/edit/datasource/users-ds-id",
|
||||
initialState: state,
|
||||
});
|
||||
|
||||
expect(screen.getByText("Databases")).toBeInTheDocument();
|
||||
expect(screen.getByText("Products")).toBeInTheDocument();
|
||||
expect(screen.getByText("Users")).toBeInTheDocument();
|
||||
|
||||
const usersDSParentElement =
|
||||
screen.getByText("Users").parentElement?.parentElement;
|
||||
|
||||
expect(usersDSParentElement).toHaveTextContent(
|
||||
"Rendering description for users",
|
||||
);
|
||||
|
||||
const productsDSParentElement =
|
||||
screen.getByText("Products").parentElement?.parentElement;
|
||||
|
||||
expect(productsDSParentElement).toHaveTextContent(
|
||||
"Rendering description for products",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -3,8 +3,8 @@ import styled from "styled-components";
|
|||
import { Flex, List, Text } from "design-system";
|
||||
import { useSelector } from "react-redux";
|
||||
import {
|
||||
getActions,
|
||||
getCurrentPageId,
|
||||
getDatasourceUsageCountForApp,
|
||||
getDatasources,
|
||||
getDatasourcesGroupedByPluginCategory,
|
||||
getPlugins,
|
||||
|
|
@ -15,7 +15,7 @@ import {
|
|||
integrationEditorURL,
|
||||
} from "@appsmith/RouteBuilder";
|
||||
import { getSelectedDatasourceId } from "@appsmith/navigation/FocusSelectors";
|
||||
import { countBy, keyBy } from "lodash";
|
||||
import { get, keyBy } from "lodash";
|
||||
import CreateDatasourcePopover from "./CreateDatasourcePopover";
|
||||
import { useLocation } from "react-router";
|
||||
import {
|
||||
|
|
@ -54,7 +54,12 @@ const StyledList = styled(List)`
|
|||
gap: 0;
|
||||
`;
|
||||
|
||||
const DataSidePane = () => {
|
||||
interface DataSidePaneProps {
|
||||
dsUsageSelector?: (...args: any[]) => Record<string, string>;
|
||||
}
|
||||
|
||||
const DataSidePane = (props: DataSidePaneProps) => {
|
||||
const { dsUsageSelector = getDatasourceUsageCountForApp } = props;
|
||||
const editorType = useEditorType(history.location.pathname);
|
||||
const pageId = useSelector(getCurrentPageId) as string;
|
||||
const [currentSelectedDatasource, setCurrentSelectedDatasource] = useState<
|
||||
|
|
@ -64,8 +69,7 @@ const DataSidePane = () => {
|
|||
const groupedDatasources = useSelector(getDatasourcesGroupedByPluginCategory);
|
||||
const plugins = useSelector(getPlugins);
|
||||
const groupedPlugins = keyBy(plugins, "id");
|
||||
const actions = useSelector(getActions);
|
||||
const actionCount = countBy(actions, "config.datasource.id");
|
||||
const dsUsageMap = useSelector((state) => dsUsageSelector(state, editorType));
|
||||
const goToDatasource = useCallback((id: string) => {
|
||||
history.push(datasourcesEditorIdURL({ datasourceId: id }));
|
||||
}, []);
|
||||
|
|
@ -135,9 +139,7 @@ const DataSidePane = () => {
|
|||
className: "t--datasource",
|
||||
title: data.name,
|
||||
onClick: () => goToDatasource(data.id),
|
||||
description: `${
|
||||
actionCount[data.id] || "No"
|
||||
} queries in this ${editorType}`,
|
||||
description: get(dsUsageMap, data.id, ""),
|
||||
descriptionType: "block",
|
||||
isSelected: currentSelectedDatasource === data.id,
|
||||
startIcon: (
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import type { IDETabs } from "reducers/uiReducers/ideReducer";
|
|||
import { IDETabsDefaultValue } from "reducers/uiReducers/ideReducer";
|
||||
import type { JSCollection } from "entities/JSCollection";
|
||||
import type { FocusHistory } from "reducers/uiReducers/focusHistoryReducer";
|
||||
import type { Datasource } from "entities/Datasource";
|
||||
|
||||
interface IDEStateArgs {
|
||||
ideView?: EditorViewMode;
|
||||
|
|
@ -17,11 +18,13 @@ interface IDEStateArgs {
|
|||
tabs?: IDETabs;
|
||||
branch?: string;
|
||||
focusHistory?: FocusHistory;
|
||||
datasources?: Datasource[];
|
||||
}
|
||||
|
||||
export const getIDETestState = ({
|
||||
actions = [],
|
||||
branch,
|
||||
datasources = [],
|
||||
focusHistory = {},
|
||||
ideView = EditorViewMode.FullScreen,
|
||||
js = [],
|
||||
|
|
@ -47,6 +50,10 @@ export const getIDETestState = ({
|
|||
...initialState,
|
||||
entities: {
|
||||
...initialState.entities,
|
||||
datasources: {
|
||||
...initialState.entities.datasources,
|
||||
list: datasources,
|
||||
},
|
||||
plugins: MockPluginsState,
|
||||
pageList: pageList,
|
||||
actions: actionData,
|
||||
|
|
|
|||
52
app/client/test/factories/DatasourceFactory.ts
Normal file
52
app/client/test/factories/DatasourceFactory.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import * as Factory from "factory.ts";
|
||||
import { PluginPackageName } from "entities/Action";
|
||||
import { PluginIDs } from "test/factories/MockPluginsState";
|
||||
import { DatasourceConnectionMode, type Datasource } from "entities/Datasource";
|
||||
import { SSLType } from "entities/Datasource/RestAPIForm";
|
||||
|
||||
interface DatasourceFactory extends Datasource {
|
||||
pluginPackageName?: PluginPackageName;
|
||||
}
|
||||
|
||||
export const datasourceFactory = (pluginPackageName?: PluginPackageName) =>
|
||||
Factory.Sync.makeFactory<DatasourceFactory>({
|
||||
id: "ds-id",
|
||||
userPermissions: [
|
||||
"create:datasourceActions",
|
||||
"execute:datasources",
|
||||
"delete:datasources",
|
||||
"manage:datasources",
|
||||
"read:datasources",
|
||||
],
|
||||
name: "Mock_DB",
|
||||
pluginId: PluginIDs[pluginPackageName || PluginPackageName.POSTGRES],
|
||||
workspaceId: "workspace-id",
|
||||
datasourceStorages: {
|
||||
"65fc11feb48e3e52a6d91d34": {
|
||||
datasourceId: "65fc124fb48e3e52a6d91d44",
|
||||
environmentId: "65fc11feb48e3e52a6d91d34",
|
||||
datasourceConfiguration: {
|
||||
url: "mockdb.internal.appsmith.com",
|
||||
connection: {
|
||||
mode: DatasourceConnectionMode.READ_ONLY,
|
||||
ssl: {
|
||||
authType: SSLType.DEFAULT,
|
||||
authTypeControl: false,
|
||||
certificateFile: {
|
||||
name: "",
|
||||
base64Content: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
authentication: {
|
||||
authenticationType: "dbAuth",
|
||||
username: "mockdb",
|
||||
},
|
||||
},
|
||||
isConfigured: true,
|
||||
isValid: true,
|
||||
},
|
||||
},
|
||||
invalids: [],
|
||||
messages: [],
|
||||
});
|
||||
|
|
@ -1,12 +1,21 @@
|
|||
import type { PluginDataState } from "reducers/entityReducers/pluginsReducer";
|
||||
import { PluginPackageName } from "entities/Action";
|
||||
|
||||
export const PluginIDs = {
|
||||
export const PluginIDs: Record<PluginPackageName, string> = {
|
||||
[PluginPackageName.POSTGRES]: "65e58df196506a506bd7069c",
|
||||
[PluginPackageName.REST_API]: "65e58df196506a506bd7069d",
|
||||
[PluginPackageName.MONGO]: "65e58df196506a506bd7069e",
|
||||
[PluginPackageName.GOOGLE_SHEETS]: "65e58df296506a506bd706a9",
|
||||
[PluginPackageName.JS]: "65e58df296506a506bd706ad",
|
||||
[PluginPackageName.MY_SQL]: "65e58df296506a506bd7069f",
|
||||
[PluginPackageName.S3]: "65e58df296506a506bd706a8",
|
||||
[PluginPackageName.SNOWFLAKE]: "65e58df296506a506bd706ab",
|
||||
[PluginPackageName.FIRESTORE]: "65e58df296506a506bd706a6",
|
||||
[PluginPackageName.GRAPHQL]: "65e58df396506a506bd706be",
|
||||
[PluginPackageName.APPSMITH_AI]: "65e58fe2225bee69e71c536a",
|
||||
[PluginPackageName.MS_SQL]: "65e58df296506a506bd706a5",
|
||||
[PluginPackageName.ORACLE]: "65e58df396506a506bd706bf",
|
||||
[PluginPackageName.WORKFLOW]: "<replace-me-with-default-plugin-id>", // this is added for the typing of PluginIDs to pass
|
||||
};
|
||||
|
||||
export default {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user