fix: Updating the permission check for run button in response pane (#36893)

## Description

Updating the permission check for run button in response pane to fix the
button being disabled even though the user has execute permissions.

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

## Automation

/ok-to-test tags="@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/11359075861>
> Commit: 4bc6d8a650cf28b2a26e84851c55a6d232daaf90
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=11359075861&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Sanity`
> Spec:
> <hr>Wed, 16 Oct 2024 05:26:33 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 the `isRunDisabled` property to enhance the
`QueryResponseTab` component, improving execution control based on user
permissions.
- Updated several components to reflect the new `isRunDisabled` prop,
streamlining execution permission handling.

- **Bug Fixes**
- Renamed `disabled` prop to `isRunDisabled` for clarity and consistency
across components.

- **Documentation**
- Enhanced clarity in the UI regarding execution permissions and action
contexts.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Ankita Kinger 2024-10-16 12:49:37 +05:30 committed by GitHub
parent 00c16e81b3
commit 4f7fd1270e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 54 additions and 57 deletions

View File

@ -149,6 +149,7 @@ function usePluginActionResponseTabs() {
actionName={action.name}
actionSource={actionSource}
currentActionConfig={action}
isRunDisabled={blockExecution}
isRunning={isRunning}
onRunClick={onRunClick}
runErrorMessage={""} // TODO

View File

@ -81,7 +81,7 @@ describe("ApiResponseView", () => {
<Router>
<ApiResponseView
currentActionConfig={Api1}
disabled={false}
isRunDisabled={false}
isRunning={false}
onRunClick={noop}
/>

View File

@ -33,7 +33,7 @@ import { ApiResponseHeaders } from "PluginActionEditor/components/PluginActionRe
interface Props {
currentActionConfig: Action;
theme?: EditorTheme;
disabled: boolean;
isRunDisabled: boolean;
onRunClick: () => void;
actionResponse?: ActionResponse;
isRunning: boolean;
@ -43,7 +43,7 @@ function ApiResponseView(props: Props) {
const {
actionResponse = EMPTY_RESPONSE,
currentActionConfig,
disabled,
isRunDisabled = false,
isRunning,
theme = EditorTheme.LIGHT,
} = props;
@ -99,7 +99,7 @@ function ApiResponseView(props: Props) {
<ApiResponse
action={currentActionConfig}
actionResponse={actionResponse}
isRunDisabled={disabled}
isRunDisabled={isRunDisabled}
isRunning={isRunning}
onRunClick={onRunClick}
responseTabHeight={responseTabHeight}
@ -113,7 +113,7 @@ function ApiResponseView(props: Props) {
panelComponent: (
<ApiResponseHeaders
actionResponse={actionResponse}
isRunDisabled={disabled}
isRunDisabled={isRunDisabled}
isRunning={isRunning}
onDebugClick={onDebugClick}
onRunClick={onRunClick}

View File

@ -341,7 +341,7 @@ function CommonEditorForm(props: CommonFormPropsWithExtraParams) {
<ApiResponseView
actionResponse={actionResponse}
currentActionConfig={currentActionConfig}
disabled={!isExecutePermitted}
isRunDisabled={blockExecution}
isRunning={isRunning}
onRunClick={onRunClick}
theme={theme}

View File

@ -21,7 +21,7 @@ import ActionRightPane from "components/editorComponents/ActionRightPane";
import type { ActionResponse } from "api/ActionAPI";
import type { Plugin } from "api/PluginApi";
import type { UIComponentTypes } from "api/PluginApi";
import { EDITOR_TABS } from "constants/QueryEditorConstants";
import { EDITOR_TABS, SQL_DATASOURCES } from "constants/QueryEditorConstants";
import type { FormEvalOutput } from "reducers/evaluationReducers/formEvaluationReducer";
import {
getPluginActionConfigSelectedTab,
@ -37,6 +37,10 @@ import { doesPluginRequireDatasource } from "ee/entities/Engine/actionHelpers";
import FormRender from "./FormRender";
import QueryEditorHeader from "./QueryEditorHeader";
import RunHistory from "ee/components/RunHistory";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
import { getHasExecuteActionPermission } from "ee/utils/BusinessFeatures/permissionPageHelpers";
import { getPluginNameFromId } from "ee/selectors/entitiesSelector";
const QueryFormContainer = styled.form`
flex: 1;
@ -241,6 +245,35 @@ export function EditorJSONtoForm(props: Props) {
[dispatch],
);
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
const isExecutePermitted = getHasExecuteActionPermission(
isFeatureEnabled,
currentActionConfig?.userPermissions,
);
// get the current action's plugin name
const currentActionPluginName = useSelector((state: AppState) =>
getPluginNameFromId(state, currentActionConfig?.pluginId || ""),
);
let actionBody = "";
if (!!currentActionConfig?.actionConfiguration) {
if ("formData" in currentActionConfig?.actionConfiguration) {
// if the action has a formData (the action is postUQI e.g. Oracle)
actionBody =
currentActionConfig.actionConfiguration.formData?.body?.data || "";
} else {
// if the action is pre UQI, the path is different e.g. mySQL
actionBody = currentActionConfig.actionConfiguration?.body || "";
}
}
// if (the body is empty and the action is an sql datasource) or the user does not have permission, block action execution.
const blockExecution =
(!actionBody && SQL_DATASOURCES.includes(currentActionPluginName)) ||
!isExecutePermitted;
// when switching between different redux forms, make sure this redux form has been initialized before rendering anything.
// the initialized prop below comes from redux-form.
if (!props.initialized) {
@ -252,6 +285,7 @@ export function EditorJSONtoForm(props: Props) {
<QueryEditorHeader
dataSources={dataSources}
formName={formName}
isRunDisabled={blockExecution}
isRunning={isRunning}
onCreateDatasourceClick={onCreateDatasourceClick}
onRunClick={onRunClick}
@ -334,6 +368,7 @@ export function EditorJSONtoForm(props: Props) {
actionResponse={actionResponse}
actionSource={actionSource}
currentActionConfig={currentActionConfig}
isRunDisabled={blockExecution}
isRunning={isRunning}
onRunClick={onRunClick}
runErrorMessage={runErrorMessage}

View File

@ -46,6 +46,7 @@ const ResultsCount = styled.div`
interface QueryDebuggerTabsProps {
actionSource: SourceEntity;
currentActionConfig?: Action;
isRunDisabled?: boolean;
isRunning: boolean;
actionName: string; // Check what and how to get
runErrorMessage?: string;
@ -59,6 +60,7 @@ function QueryDebuggerTabs({
actionResponse,
actionSource,
currentActionConfig,
isRunDisabled = false,
isRunning,
onRunClick,
runErrorMessage,
@ -233,6 +235,7 @@ function QueryDebuggerTabs({
actionName={actionName}
actionSource={actionSource}
currentActionConfig={currentActionConfig}
isRunDisabled={isRunDisabled}
isRunning={isRunning}
onRunClick={onRunClick}
runErrorMessage={runErrorMessage}

View File

@ -5,22 +5,14 @@ import { StyledFormRow } from "./EditorJSONtoForm";
import styled from "styled-components";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
import {
getHasExecuteActionPermission,
getHasManageActionPermission,
} from "ee/utils/BusinessFeatures/permissionPageHelpers";
import { getHasManageActionPermission } from "ee/utils/BusinessFeatures/permissionPageHelpers";
import { useActiveActionBaseId } from "ee/pages/Editor/Explorer/hooks";
import { useSelector } from "react-redux";
import {
getActionByBaseId,
getPlugin,
getPluginNameFromId,
} from "ee/selectors/entitiesSelector";
import { getActionByBaseId, getPlugin } from "ee/selectors/entitiesSelector";
import { QueryEditorContext } from "./QueryEditorContext";
import type { Plugin } from "api/PluginApi";
import type { Datasource } from "entities/Datasource";
import type { AppState } from "ee/reducers";
import { SQL_DATASOURCES } from "constants/QueryEditorConstants";
import DatasourceSelector from "./DatasourceSelector";
import { getSavingStatusForActionName } from "selectors/actionSelectors";
import { getAssetUrl } from "ee/utils/airgapHelpers";
@ -51,6 +43,7 @@ interface Props {
formName: string;
dataSources: Datasource[];
onCreateDatasourceClick: () => void;
isRunDisabled?: boolean;
isRunning: boolean;
onRunClick: () => void;
}
@ -59,6 +52,7 @@ const QueryEditorHeader = (props: Props) => {
const {
dataSources,
formName,
isRunDisabled = false,
isRunning,
onCreateDatasourceClick,
onRunClick,
@ -78,11 +72,6 @@ const QueryEditorHeader = (props: Props) => {
currentActionConfig?.userPermissions,
);
const isExecutePermitted = getHasExecuteActionPermission(
isFeatureEnabled,
currentActionConfig?.userPermissions,
);
const currentPlugin = useSelector((state: AppState) =>
getPlugin(state, currentActionConfig?.pluginId || ""),
);
@ -95,29 +84,6 @@ const QueryEditorHeader = (props: Props) => {
const icon = ActionUrlIcon(iconUrl);
// get the current action's plugin name
const currentActionPluginName = useSelector((state: AppState) =>
getPluginNameFromId(state, currentActionConfig?.pluginId || ""),
);
let actionBody = "";
if (!!currentActionConfig?.actionConfiguration) {
if ("formData" in currentActionConfig?.actionConfiguration) {
// if the action has a formData (the action is postUQI e.g. Oracle)
actionBody =
currentActionConfig.actionConfiguration.formData?.body?.data || "";
} else {
// if the action is pre UQI, the path is different e.g. mySQL
actionBody = currentActionConfig.actionConfiguration?.body || "";
}
}
// if (the body is empty and the action is an sql datasource) or the user does not have permission, block action execution.
const blockExecution =
(!actionBody && SQL_DATASOURCES.includes(currentActionPluginName)) ||
!isExecutePermitted;
return (
<StyledFormRow>
<NameWrapper>
@ -141,7 +107,7 @@ const QueryEditorHeader = (props: Props) => {
<Button
className="t--run-query"
data-guided-tour-iid="run-query"
isDisabled={blockExecution}
isDisabled={isRunDisabled}
isLoading={isRunning}
onClick={onRunClick}
size="md"

View File

@ -28,9 +28,6 @@ import type { SourceEntity } from "entities/AppsmithConsole";
import type { Action } from "entities/Action";
import { getActionData } from "ee/selectors/entitiesSelector";
import { actionResponseDisplayDataFormats } from "../utils";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
import { getHasExecuteActionPermission } from "ee/utils/BusinessFeatures/permissionPageHelpers";
import { getErrorAsString } from "sagas/ActionExecution/errorUtils";
import { isString } from "lodash";
import ActionExecutionInProgressView from "components/editorComponents/ActionExecutionInProgressView";
@ -72,6 +69,7 @@ const ResponseContentWrapper = styled.div<{ isError: boolean }>`
interface Props {
actionSource: SourceEntity;
isRunDisabled?: boolean;
isRunning: boolean;
onRunClick: () => void;
currentActionConfig: Action;
@ -84,19 +82,13 @@ const QueryResponseTab = (props: Props) => {
actionName,
actionSource,
currentActionConfig,
isRunDisabled = false,
isRunning,
onRunClick,
runErrorMessage,
} = props;
const dispatch = useDispatch();
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
const isExecutePermitted = getHasExecuteActionPermission(
isFeatureEnabled,
currentActionConfig?.userPermissions,
);
const actionResponse = useSelector((state) =>
getActionData(state, currentActionConfig.id),
);
@ -341,7 +333,7 @@ const QueryResponseTab = (props: Props) => {
)}
{!output && !error && (
<NoResponse
isRunDisabled={!isExecutePermitted}
isRunDisabled={isRunDisabled}
isRunning={isRunning}
onRunClick={responseTabOnRunClick}
/>