chore: Adding no access state for Datasource tab based on permissions given via GAC (#38268)

## Description

- Today, when a user gives only View/Edit access to a Datasource, the
schema tables are still being seen on the Datasource tabs while it
doesn't show on the Datasource Editor page. This has been fixed with
this PR. It should only be seen when Datasource has create action
permissions, hence we show the "We can’t show the schema for this
datasource" screen in this case.
- When the user has not given View access to a Datasource, the UI is
broken in Datasource tab. This has been fixed with this PR. We now show
the No access state in this case.

**BEFORE**:

When view access is not given:
<img width="1147" alt="Screenshot 2024-12-20 at 5 52 58 PM"
src="https://github.com/user-attachments/assets/c1d1fd39-d6d3-4fd8-99bf-895698f61490"
/>

When create action permission is not given but view access is given:
<img width="1138" alt="Screenshot 2024-12-20 at 5 54 10 PM"
src="https://github.com/user-attachments/assets/abf0aa86-e541-4453-b7e4-071d123f7a60"
/>


**AFTER**:

When view access is not given:
<img width="1136" alt="Screenshot 2024-12-20 at 5 58 22 PM"
src="https://github.com/user-attachments/assets/e160250b-963c-457e-81b1-380aef1332a1"
/>


When create action permission is not given but view access is given:
<img width="1139" alt="Screenshot 2024-12-20 at 5 57 53 PM"
src="https://github.com/user-attachments/assets/1967a657-622c-46f7-b6d4-78451b6106f0"
/>


Fixes [#38093](https://github.com/appsmithorg/appsmith/issues/38093)

## Automation

/ok-to-test tags="@tag.Sanity, @tag.Datasource, @tag.IDE, @tag.JS,
@tag.Git"

### 🔍 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/12452839374>
> Commit: ee5bc1774c02b4b29a702c8baefbad35390708c3
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=12452839374&attempt=3"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Sanity, @tag.Datasource, @tag.IDE, @tag.JS, @tag.Git`
> Spec:
> <hr>Mon, 23 Dec 2024 08:29: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

- **New Features**
- Introduced new icons and improved conditional rendering for datasource
components.
  - Added a "not found" message for empty datasource options.
  - Enhanced user feedback with clearer messaging for missing resources.

- **Bug Fixes**
- Adjusted rendering logic to ensure edit buttons only appear when both
conditions are met.

- **Enhancements**
- Improved permission checks and logic for managing datasource
visibility.
- Streamlined component logic for better readability and
maintainability.
  - Enhanced error handling practices in saga functions.

- **Tests**
  - Simplified test structure by removing unnecessary context providers.

- **Chores**
  - Updated import statements and component names for consistency.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Ankita Kinger 2024-12-23 20:27:36 +05:30 committed by GitHub
parent e45cbdfa83
commit 5855115abb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 217 additions and 171 deletions

View File

@ -1,32 +1,30 @@
import React from "react";
import { Flex } from "@appsmith/ads";
import { Flex, Icon } from "@appsmith/ads";
import { getAssetUrl } from "ee/utils/airgapHelpers";
import { EntityIcon } from "pages/Editor/Explorer/ExplorerIcons";
import { useSelector } from "react-redux";
import {
getPluginIdFromDatasourceId,
getPluginImages,
} from "ee/selectors/entitiesSelector";
import { getPluginImages } from "ee/selectors/entitiesSelector";
interface Props {
datasourceId: string;
datasourceName: string;
pluginId: string;
}
const CurrentDataSource = ({ datasourceId, datasourceName }: Props) => {
const { pluginId, pluginImages } = useSelector((state) => ({
pluginId: getPluginIdFromDatasourceId(state, datasourceId),
pluginImages: getPluginImages(state),
}));
const CurrentDataSource = ({ datasourceName, pluginId }: Props) => {
const pluginImages = useSelector((state) => getPluginImages(state));
const datasourceIcon = pluginId ? pluginImages?.[pluginId] : undefined;
return (
<Flex alignItems="center" gap="spaces-2">
<EntityIcon height="16px" width="16px">
{datasourceIcon ? (
<img alt="entityIcon" src={getAssetUrl(datasourceIcon)} />
) : (
<Icon name="datasource-v3" />
)}
</EntityIcon>
{datasourceName}
{datasourceName || "NA"}
</Flex>
);
};

View File

@ -6,9 +6,11 @@ import { useGoToDatasource } from "PluginActionEditor/components/PluginActionRes
const CurrentDataSourceLink = ({
datasourceId,
datasourceName,
pluginId,
}: {
datasourceId: string;
datasourceName: string;
pluginId: string;
}) => {
const { goToDatasource } = useGoToDatasource();
@ -19,10 +21,7 @@ const CurrentDataSourceLink = ({
return (
<Link onClick={handleClick}>
<CurrentDataSource
datasourceId={datasourceId}
datasourceName={datasourceName}
/>
<CurrentDataSource datasourceName={datasourceName} pluginId={pluginId} />
</Link>
);
};

View File

@ -55,7 +55,7 @@ const DatasourceInfo = ({
datasourceName={datasourceName}
plugin={plugin}
/>
{showEditButton && (
{showEditButton && datasourceName && (
<Tooltip content={createMessage(EDIT_DS_CONFIG)} placement="top">
<Button
isIconButton

View File

@ -1,7 +1,11 @@
import React from "react";
import { useSelector } from "react-redux";
import { Flex } from "@appsmith/ads";
import { CREATE_NEW_DATASOURCE, createMessage } from "ee/constants/messages";
import {
CREATE_NEW_DATASOURCE,
createMessage,
NOT_FOUND,
} from "ee/constants/messages";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
import {
@ -66,7 +70,7 @@ export const PluginDatasourceSelector = ({
const userWorkspacePermissions = useSelector(
(state: AppState) => getCurrentAppWorkspace(state).userPermissions ?? [],
);
const isChangePermitted = getHasManageActionPermission(
const isActionChangePermitted = getHasManageActionPermission(
isFeatureEnabled,
currentActionConfig?.userPermissions,
);
@ -108,15 +112,23 @@ export const PluginDatasourceSelector = ({
});
}
if (!showDatasourceSelector || !isChangePermitted) {
if (!showDatasourceSelector || !isActionChangePermitted) {
return (
<CurrentDataSourceLink
datasourceId={datasourceId}
datasourceName={datasourceName}
pluginId={plugin?.id || ""}
/>
);
}
if (DATASOURCES_OPTIONS.length < 1) {
DATASOURCES_OPTIONS.push({
label: createMessage(NOT_FOUND),
value: "not found",
});
}
return (
<Flex>
<MenuField
@ -126,8 +138,8 @@ export const PluginDatasourceSelector = ({
options={DATASOURCES_OPTIONS}
>
<CurrentDataSource
datasourceId={datasourceId}
datasourceName={datasourceName}
pluginId={plugin?.id || ""}
/>
</MenuField>
</Flex>

View File

@ -19,13 +19,12 @@ export interface DatasourceProps {
}
const DatasourceSelector = (props: DatasourceProps) => {
return props.plugin ? (
return props.plugin &&
API_FORM_COMPONENTS.includes(props.plugin.uiComponent) ? (
<ApiDatasourceSelector {...props} formName={API_EDITOR_FORM_NAME} />
) : (
<QueryDatasourceSelector {...props} formName={QUERY_EDITOR_FORM_NAME} />
)
) : null;
);
};
export default DatasourceSelector;

View File

@ -7,6 +7,7 @@ import {
getIsFetchingDatasourceStructure,
getPluginIdFromDatasourceId,
getPluginDatasourceComponentFromId,
getDatasource,
} from "ee/selectors/entitiesSelector";
import type { AppState } from "ee/reducers";
import { fetchDatasourceStructure } from "actions/datasourceActions";
@ -26,6 +27,13 @@ import { useEditorType } from "ee/hooks";
import { useParentEntityInfo } from "ee/hooks/datasourceEditorHooks";
import DatasourceInfo from "./DatasourceInfo";
import { getPlugin } from "ee/selectors/entitiesSelector";
import {
getHasCreateDatasourceActionPermission,
getHasManageDatasourcePermission,
getHasReadDatasourcePermission,
} from "ee/utils/BusinessFeatures/permissionPageHelpers";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
interface Props {
datasourceId: string;
@ -33,17 +41,19 @@ interface Props {
currentActionId: string;
}
const Datasource = (props: Props) => {
const DatasourceTab = (props: Props) => {
const dispatch = useDispatch();
const { datasourceId, datasourceName } = props;
const datasourceStructure = useSelector((state) =>
getDatasourceStructureById(state, props.datasourceId),
getDatasourceStructureById(state, datasourceId),
);
const { responseTabHeight } = useSelector(getPluginActionDebuggerState);
const pluginId = useSelector((state) =>
getPluginIdFromDatasourceId(state, props.datasourceId),
getPluginIdFromDatasourceId(state, datasourceId),
);
const plugin = useSelector((state) => getPlugin(state, pluginId || ""));
@ -54,32 +64,51 @@ const Datasource = (props: Props) => {
const [selectedTable, setSelectedTable] = useState<string>();
const isLoading = useSelector((state: AppState) =>
getIsFetchingDatasourceStructure(state, props.datasourceId),
getIsFetchingDatasourceStructure(state, datasourceId),
);
const pluginDatasourceForm = useSelector((state) =>
getPluginDatasourceComponentFromId(state, pluginId || ""),
);
const datasource = useSelector((state) => getDatasource(state, datasourceId));
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
const canCreateDatasourceActions = getHasCreateDatasourceActionPermission(
isFeatureEnabled,
datasource?.userPermissions || [],
);
const canReadDatasource = getHasReadDatasourcePermission(
isFeatureEnabled,
datasource?.userPermissions || [],
);
const canManageDatasource = getHasManageDatasourcePermission(
isFeatureEnabled,
datasource?.userPermissions || [],
);
useEffect(
function resetSelectedTable() {
setSelectedTable(undefined);
},
[props.datasourceId],
[datasourceId],
);
useEffect(
function fetchDatasourceStructureEffect() {
function fetchStructure() {
if (
props.datasourceId &&
datasourceId &&
datasourceStructure === undefined &&
pluginDatasourceForm !==
DatasourceComponentTypes.RestAPIDatasourceForm
) {
dispatch(
fetchDatasourceStructure(
props.datasourceId,
datasourceId,
true,
DatasourceStructureContext.QUERY_EDITOR,
),
@ -89,7 +118,7 @@ const Datasource = (props: Props) => {
fetchStructure();
},
[props.datasourceId, datasourceStructure, dispatch, pluginDatasourceForm],
[datasourceId, datasourceStructure, dispatch, pluginDatasourceForm],
);
useEffect(
@ -98,7 +127,7 @@ const Datasource = (props: Props) => {
setSelectedTable(datasourceStructure.tables[0].name);
}
},
[selectedTable, props.datasourceId, isLoading, datasourceStructure],
[selectedTable, datasourceId, isLoading, datasourceStructure],
);
// eslint-disable-next-line react-perf/jsx-no-new-function-as-prop
@ -106,14 +135,14 @@ const Datasource = (props: Props) => {
const entryPoint = DatasourceEditEntryPoints.QUERY_EDITOR_DATASOURCE_SCHEMA;
AnalyticsUtil.logEvent("EDIT_DATASOURCE_CLICK", {
datasourceId: props.datasourceId,
datasourceId,
pluginName: "",
entryPoint: entryPoint,
});
const url = datasourcesEditorIdURL({
baseParentEntityId: parentEntityId,
datasourceId: props.datasourceId,
datasourceId,
params: { ...omit(getQueryParams(), "viewMode"), viewMode: false },
generateEditorPath: true,
});
@ -124,7 +153,12 @@ const Datasource = (props: Props) => {
const getStatusState = () => {
if (isLoading) return SchemaDisplayStatus.SCHEMA_LOADING;
if (!datasourceStructure) return SchemaDisplayStatus.NOSCHEMA;
/* When a user doesn't have view access on a datasource */
if (!canReadDatasource) return SchemaDisplayStatus.NOACCESS;
/* When a user doesn't have create new query access but has view access on the datasource */
if (!datasourceStructure || !canCreateDatasourceActions)
return SchemaDisplayStatus.NOSCHEMA;
if (datasourceStructure && "error" in datasourceStructure)
return SchemaDisplayStatus.FAILED;
@ -144,10 +178,10 @@ const Datasource = (props: Props) => {
return (
<Flex flexDirection="column" padding="spaces-3">
<DatasourceInfo
datasourceId={props.datasourceId}
datasourceName={props.datasourceName}
datasourceId={datasourceId}
datasourceName={datasourceName}
plugin={plugin}
showEditButton={!isLoading}
showEditButton={!isLoading && canManageDatasource}
/>
<StatusDisplay
editDatasource={editDatasource}
@ -171,8 +205,8 @@ const Datasource = (props: Props) => {
<Flex h="100%">
<DatasourceTables
currentActionId={props.currentActionId}
datasourceId={props.datasourceId}
datasourceName={props.datasourceName}
datasourceId={datasourceId}
datasourceName={datasourceName}
datasourceStructure={datasourceStructure}
plugin={plugin}
selectedTable={selectedTable}
@ -200,4 +234,4 @@ const Datasource = (props: Props) => {
);
};
export { Datasource };
export { DatasourceTab };

View File

@ -1 +1 @@
export * from "./Datasource";
export * from "./DatasourceTab";

View File

@ -24,7 +24,7 @@ import {
} from "PluginActionEditor/store";
import { doesPluginRequireDatasource } from "ee/entities/Engine/actionHelpers";
import useShowSchema from "PluginActionEditor/components/PluginActionResponse/hooks/useShowSchema";
import { Datasource } from "PluginActionEditor/components/PluginActionResponse/components/DatasourceTab";
import { DatasourceTab } from "PluginActionEditor/components/PluginActionResponse/components/DatasourceTab";
import {
useBlockExecution,
useHandleRunClick,
@ -64,10 +64,10 @@ function usePluginActionResponseTabs() {
key: DEBUGGER_TAB_KEYS.DATASOURCE_TAB,
title: "Datasource",
panelComponent: (
<Datasource
<DatasourceTab
currentActionId={action.id}
datasourceId={datasource?.id || ""}
datasourceName={datasource?.name || ""}
datasourceId={datasource?.id || action.datasource.id || ""}
datasourceName={datasource?.name || action.datasource.name || ""}
/>
),
});
@ -119,10 +119,10 @@ function usePluginActionResponseTabs() {
key: DEBUGGER_TAB_KEYS.DATASOURCE_TAB,
title: "Datasource",
panelComponent: (
<Datasource
<DatasourceTab
currentActionId={action.id}
datasourceId={datasource?.id || ""}
datasourceName={datasource?.name || ""}
datasourceId={datasource?.id || action.datasource.id || ""}
datasourceName={datasource?.name || action.datasource.name || ""}
/>
),
});

View File

@ -394,6 +394,7 @@ export const CREATE_NEW_DATASOURCE_MOST_POPULAR_HEADER = () => "Most popular";
export const CREATE_NEW_DATASOURCE_REST_API = () => "REST API";
export const SAMPLE_DATASOURCES = () => "Sample datasources";
export const EDIT_DS_CONFIG = () => "Edit datasource configuration";
export const NOT_FOUND = () => "Not found";
export const ERROR_EVAL_ERROR_GENERIC = () =>
`Unexpected error occurred while evaluating the application`;

View File

@ -8,7 +8,7 @@ import {
import { getJSTabs, getQueryTabs } from "selectors/ideSelectors";
import type { AppState } from "ee/reducers";
import { identifyEntityFromPath } from "navigation/FocusEntity";
import { getCurrentPageId } from "selectors/editorSelectors";
import { getCurrentBasePageId } from "selectors/editorSelectors";
import { getQueryEntityItemUrl } from "ee/pages/Editor/IDE/EditorPane/Query/utils";
export type EditorSegmentList = Array<{
@ -74,10 +74,10 @@ export const selectQuerySegmentEditorTabs = (state: AppState) => {
export const getLastQueryTab = createSelector(
selectQuerySegmentEditorTabs,
getCurrentPageId,
(tabs, pageId) => {
getCurrentBasePageId,
(tabs, basePageId) => {
if (tabs.length) {
const url = getQueryEntityItemUrl(tabs[tabs.length - 1], pageId);
const url = getQueryEntityItemUrl(tabs[tabs.length - 1], basePageId);
const urlWithoutQueryParams = url.split("?")[0];
return identifyEntityFromPath(urlWithoutQueryParams);

View File

@ -1645,7 +1645,9 @@ export const getQuerySegmentItems = createSelector(
? "AI Queries"
: datasourceIdToNameMap[action.config.datasource.id] ?? "AI Queries";
} else {
group = datasourceIdToNameMap[action.config.datasource.id];
group =
action.config.datasource?.name ??
datasourceIdToNameMap[action.config.datasource?.id];
}
return {

View File

@ -5,6 +5,9 @@ import { hasCreateWorkspacePermission as hasCreateWorkspacePermission_EE } from
import { hasCreateDatasourcePermission as hasCreateDatasourcePermission_CE } from "ce/utils/permissionHelpers";
import { hasCreateDatasourcePermission as hasCreateDatasourcePermission_EE } from "ee/utils/permissionHelpers";
import { hasReadDatasourcePermission as hasReadDatasourcePermission_CE } from "ce/utils/permissionHelpers";
import { hasReadDatasourcePermission as hasReadDatasourcePermission_EE } from "ee/utils/permissionHelpers";
import { hasManageDatasourcePermission as hasManageDatasourcePermission_CE } from "ce/utils/permissionHelpers";
import { hasManageDatasourcePermission as hasManageDatasourcePermission_EE } from "ee/utils/permissionHelpers";
@ -58,6 +61,14 @@ export const getHasCreateDatasourcePermission = (
else return hasCreateDatasourcePermission_CE(permissions);
};
export const getHasReadDatasourcePermission = (
isEnabled: boolean,
permissions?: string[],
) => {
if (isEnabled) return hasReadDatasourcePermission_EE(permissions);
else return hasReadDatasourcePermission_CE(permissions);
};
export const getHasManageDatasourcePermission = (
isEnabled: boolean,
permissions?: string[],

View File

@ -19,6 +19,7 @@ export enum PERMISSION_TYPE {
CREATE_APPLICATION = "create:applications",
/* Datasource permissions */
CREATE_DATASOURCES = "create:datasources",
READ_DATASOURCES = "read:datasources",
EXECUTE_DATASOURCES = "execute:datasources",
CREATE_DATASOURCE_ACTIONS = "create:datasourceActions",
DELETE_DATASOURCES = "delete:datasources",
@ -85,6 +86,8 @@ export const hasCreateWorkspacePermission = (_permissions?: string[]) => true;
export const hasCreateDatasourcePermission = (_permissions?: string[]) => true;
export const hasReadDatasourcePermission = (_permissions?: string[]) => true;
export const hasManageDatasourcePermission = (_permissions?: string[]) => true;
export const hasManageWorkspaceDatasourcePermission = (

View File

@ -14,9 +14,6 @@ import { getIDETestState } from "test/factories/AppIDEFactoryUtils";
import { PageFactory } from "test/factories/PageFactory";
import { screen, waitFor } from "@testing-library/react";
import { GoogleSheetFactory } from "test/factories/Actions/GoogleSheetFactory";
import { PluginActionContextProvider } from "PluginActionEditor";
import { PluginPackageName, PluginType } from "entities/Action";
import { DatasourceComponentTypes, UIComponentTypes } from "api/PluginApi";
const FeatureFlags = {
rollout_side_by_side_enabled: true,
@ -329,17 +326,6 @@ describe("IDE URL rendering of Queries", () => {
});
describe("Postgres Routes", () => {
const mockPlugin = {
id: "plugin_id",
name: "Postgres",
packageName: PluginPackageName.POSTGRES,
type: PluginType.DB,
uiComponent: UIComponentTypes.UQIDbEditorForm,
datasourceComponent: DatasourceComponentTypes.AutoForm,
templates: {},
requiresDatasource: true,
};
it("Renders Postgres routes in Full Screen", async () => {
const page = PageFactory.build();
const anQuery = PostgresFactory.build({
@ -358,9 +344,7 @@ describe("IDE URL rendering of Queries", () => {
const { getAllByText, getByRole, getByTestId } = render(
<Route path={BUILDER_PATH}>
<PluginActionContextProvider action={anQuery} plugin={mockPlugin}>
<IDE />
</PluginActionContextProvider>
</Route>,
{
url: `/app/applicationSlug/pageSlug-${page.basePageId}/edit/queries/${anQuery.baseId}`,
@ -418,9 +402,7 @@ describe("IDE URL rendering of Queries", () => {
const { getAllByText, getByRole, getByTestId } = render(
<Route path={BUILDER_PATH}>
<PluginActionContextProvider action={anQuery} plugin={mockPlugin}>
<IDE />
</PluginActionContextProvider>
</Route>,
{
url: `/app/applicationSlug/pageSlug-${page.basePageId}/edit/queries/${anQuery.baseId}`,
@ -551,17 +533,6 @@ describe("IDE URL rendering of Queries", () => {
});
describe("Google Sheets Routes", () => {
const mockPlugin = {
id: "plugin_id",
name: "Google Sheets",
packageName: PluginPackageName.GOOGLE_SHEETS,
type: PluginType.DB,
uiComponent: UIComponentTypes.GraphQLEditorForm,
datasourceComponent: DatasourceComponentTypes.RestAPIDatasourceForm,
templates: {},
requiresDatasource: false,
};
it("Renders Google Sheets routes in Full Screen", async () => {
const page = PageFactory.build();
const anQuery = GoogleSheetFactory.build({
@ -581,9 +552,7 @@ describe("IDE URL rendering of Queries", () => {
const { getAllByText, getByRole, getByTestId } = render(
<Route path={BUILDER_PATH}>
<PluginActionContextProvider action={anQuery} plugin={mockPlugin}>
<IDE />
</PluginActionContextProvider>
</Route>,
{
url: `/app/applicationSlug/pageSlug-${page.basePageId}/edit/saas/google-sheets-plugin/api/${anQuery.baseId}`,
@ -634,9 +603,7 @@ describe("IDE URL rendering of Queries", () => {
const { container, getAllByText, getByRole, getByTestId } = render(
<Route path={BUILDER_PATH}>
<PluginActionContextProvider action={anQuery} plugin={mockPlugin}>
<IDE />
</PluginActionContextProvider>
</Route>,
{
url: `/app/applicationSlug/pageSlug-${page.basePageId}/edit/saas/google-sheets-plugin/api/${anQuery.baseId}`,

View File

@ -27,6 +27,8 @@ import { closeJSActionTab } from "actions/jsActionActions";
import { closeQueryActionTab } from "actions/pluginActionActions";
import { getCurrentBasePageId } from "selectors/editorSelectors";
import { getCurrentEntityInfo } from "../utils";
import { useEditorType } from "ee/hooks";
import { useParentEntityInfo } from "ee/hooks/datasourceEditorHooks";
export const useCurrentEditorState = () => {
const [selectedSegment, setSelectedSegment] = useState<EditorEntityTab>(
@ -58,7 +60,9 @@ export const useCurrentEditorState = () => {
export const useSegmentNavigation = (): {
onSegmentChange: (value: string) => void;
} => {
const basePageId = useSelector(getCurrentBasePageId);
const editorType = useEditorType(location.pathname);
const { parentEntityId: baseParentEntityId } =
useParentEntityInfo(editorType);
/**
* Callback to handle the segment change
@ -70,17 +74,17 @@ export const useSegmentNavigation = (): {
const onSegmentChange = (value: string) => {
switch (value) {
case EditorEntityTab.QUERIES:
history.push(queryListURL({ basePageId }), {
history.push(queryListURL({ baseParentEntityId }), {
invokedBy: NavigationMethod.SegmentControl,
});
break;
case EditorEntityTab.JS:
history.push(jsCollectionListURL({ basePageId }), {
history.push(jsCollectionListURL({ baseParentEntityId }), {
invokedBy: NavigationMethod.SegmentControl,
});
break;
case EditorEntityTab.UI:
history.push(widgetListURL({ basePageId }), {
history.push(widgetListURL({ baseParentEntityId }), {
invokedBy: NavigationMethod.SegmentControl,
});
break;

View File

@ -12,7 +12,7 @@ import {
} from "ee/constants/messages";
import DebuggerLogs from "components/editorComponents/Debugger/DebuggerLogs";
import ErrorLogs from "components/editorComponents/Debugger/Errors";
import { Datasource } from "PluginActionEditor/components/PluginActionResponse/components/DatasourceTab";
import { DatasourceTab } from "PluginActionEditor/components/PluginActionResponse/components/DatasourceTab";
import type { ActionResponse } from "api/ActionAPI";
import type { SourceEntity } from "entities/AppsmithConsole";
import type { Action } from "entities/Action";
@ -219,10 +219,12 @@ function QueryDebuggerTabs({
key: DEBUGGER_TAB_KEYS.DATASOURCE_TAB,
title: "Datasource",
panelComponent: (
<Datasource
<DatasourceTab
currentActionId={currentActionConfig.id}
datasourceId={currentActionConfig.datasource.id || ""}
datasourceName={datasource?.name || ""}
datasourceName={
datasource?.name || currentActionConfig.datasource.name || ""
}
/>
),
});

View File

@ -1561,16 +1561,19 @@ function* fetchDatasourceStructureSaga(
yield take(ReduxActionTypes.FETCH_ENVIRONMENT_SUCCESS);
}
let errorMessage = "";
let isSuccess = false;
try {
const datasource = shouldBeDefined<Datasource>(
yield select(getDatasource, action.payload.id),
`Datasource not found for id - ${action.payload.id}`,
);
const plugin: Plugin = yield select(getPlugin, datasource?.pluginId);
let errorMessage = "";
let isSuccess = false;
try {
const response: ApiResponse = yield DatasourcesApi.fetchDatasourceStructure(
const response: ApiResponse =
yield DatasourcesApi.fetchDatasourceStructure(
action.payload.id,
action.payload.ignoreCache,
);
@ -1638,6 +1641,7 @@ function* fetchDatasourceStructureSaga(
},
});
}
const currentEnvDetails: { id: string; name: string } = yield select(
getCurrentEnvironmentDetails,
);
@ -1651,6 +1655,16 @@ function* fetchDatasourceStructureSaga(
isSuccess: isSuccess,
source: action.payload.schemaFetchContext,
});
} catch (error) {
yield put({
type: ReduxActionErrorTypes.FETCH_DATASOURCE_STRUCTURE_ERROR,
payload: {
error,
show: false,
datasourceId: action.payload.id,
},
});
}
}
function* addAndFetchDatasourceStructureSaga(