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:
parent
00c16e81b3
commit
4f7fd1270e
|
|
@ -149,6 +149,7 @@ function usePluginActionResponseTabs() {
|
||||||
actionName={action.name}
|
actionName={action.name}
|
||||||
actionSource={actionSource}
|
actionSource={actionSource}
|
||||||
currentActionConfig={action}
|
currentActionConfig={action}
|
||||||
|
isRunDisabled={blockExecution}
|
||||||
isRunning={isRunning}
|
isRunning={isRunning}
|
||||||
onRunClick={onRunClick}
|
onRunClick={onRunClick}
|
||||||
runErrorMessage={""} // TODO
|
runErrorMessage={""} // TODO
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ describe("ApiResponseView", () => {
|
||||||
<Router>
|
<Router>
|
||||||
<ApiResponseView
|
<ApiResponseView
|
||||||
currentActionConfig={Api1}
|
currentActionConfig={Api1}
|
||||||
disabled={false}
|
isRunDisabled={false}
|
||||||
isRunning={false}
|
isRunning={false}
|
||||||
onRunClick={noop}
|
onRunClick={noop}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import { ApiResponseHeaders } from "PluginActionEditor/components/PluginActionRe
|
||||||
interface Props {
|
interface Props {
|
||||||
currentActionConfig: Action;
|
currentActionConfig: Action;
|
||||||
theme?: EditorTheme;
|
theme?: EditorTheme;
|
||||||
disabled: boolean;
|
isRunDisabled: boolean;
|
||||||
onRunClick: () => void;
|
onRunClick: () => void;
|
||||||
actionResponse?: ActionResponse;
|
actionResponse?: ActionResponse;
|
||||||
isRunning: boolean;
|
isRunning: boolean;
|
||||||
|
|
@ -43,7 +43,7 @@ function ApiResponseView(props: Props) {
|
||||||
const {
|
const {
|
||||||
actionResponse = EMPTY_RESPONSE,
|
actionResponse = EMPTY_RESPONSE,
|
||||||
currentActionConfig,
|
currentActionConfig,
|
||||||
disabled,
|
isRunDisabled = false,
|
||||||
isRunning,
|
isRunning,
|
||||||
theme = EditorTheme.LIGHT,
|
theme = EditorTheme.LIGHT,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
@ -99,7 +99,7 @@ function ApiResponseView(props: Props) {
|
||||||
<ApiResponse
|
<ApiResponse
|
||||||
action={currentActionConfig}
|
action={currentActionConfig}
|
||||||
actionResponse={actionResponse}
|
actionResponse={actionResponse}
|
||||||
isRunDisabled={disabled}
|
isRunDisabled={isRunDisabled}
|
||||||
isRunning={isRunning}
|
isRunning={isRunning}
|
||||||
onRunClick={onRunClick}
|
onRunClick={onRunClick}
|
||||||
responseTabHeight={responseTabHeight}
|
responseTabHeight={responseTabHeight}
|
||||||
|
|
@ -113,7 +113,7 @@ function ApiResponseView(props: Props) {
|
||||||
panelComponent: (
|
panelComponent: (
|
||||||
<ApiResponseHeaders
|
<ApiResponseHeaders
|
||||||
actionResponse={actionResponse}
|
actionResponse={actionResponse}
|
||||||
isRunDisabled={disabled}
|
isRunDisabled={isRunDisabled}
|
||||||
isRunning={isRunning}
|
isRunning={isRunning}
|
||||||
onDebugClick={onDebugClick}
|
onDebugClick={onDebugClick}
|
||||||
onRunClick={onRunClick}
|
onRunClick={onRunClick}
|
||||||
|
|
|
||||||
|
|
@ -341,7 +341,7 @@ function CommonEditorForm(props: CommonFormPropsWithExtraParams) {
|
||||||
<ApiResponseView
|
<ApiResponseView
|
||||||
actionResponse={actionResponse}
|
actionResponse={actionResponse}
|
||||||
currentActionConfig={currentActionConfig}
|
currentActionConfig={currentActionConfig}
|
||||||
disabled={!isExecutePermitted}
|
isRunDisabled={blockExecution}
|
||||||
isRunning={isRunning}
|
isRunning={isRunning}
|
||||||
onRunClick={onRunClick}
|
onRunClick={onRunClick}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import ActionRightPane from "components/editorComponents/ActionRightPane";
|
||||||
import type { ActionResponse } from "api/ActionAPI";
|
import type { ActionResponse } from "api/ActionAPI";
|
||||||
import type { Plugin } from "api/PluginApi";
|
import type { Plugin } from "api/PluginApi";
|
||||||
import type { UIComponentTypes } 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 type { FormEvalOutput } from "reducers/evaluationReducers/formEvaluationReducer";
|
||||||
import {
|
import {
|
||||||
getPluginActionConfigSelectedTab,
|
getPluginActionConfigSelectedTab,
|
||||||
|
|
@ -37,6 +37,10 @@ import { doesPluginRequireDatasource } from "ee/entities/Engine/actionHelpers";
|
||||||
import FormRender from "./FormRender";
|
import FormRender from "./FormRender";
|
||||||
import QueryEditorHeader from "./QueryEditorHeader";
|
import QueryEditorHeader from "./QueryEditorHeader";
|
||||||
import RunHistory from "ee/components/RunHistory";
|
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`
|
const QueryFormContainer = styled.form`
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
@ -241,6 +245,35 @@ export function EditorJSONtoForm(props: Props) {
|
||||||
[dispatch],
|
[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.
|
// 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.
|
// the initialized prop below comes from redux-form.
|
||||||
if (!props.initialized) {
|
if (!props.initialized) {
|
||||||
|
|
@ -252,6 +285,7 @@ export function EditorJSONtoForm(props: Props) {
|
||||||
<QueryEditorHeader
|
<QueryEditorHeader
|
||||||
dataSources={dataSources}
|
dataSources={dataSources}
|
||||||
formName={formName}
|
formName={formName}
|
||||||
|
isRunDisabled={blockExecution}
|
||||||
isRunning={isRunning}
|
isRunning={isRunning}
|
||||||
onCreateDatasourceClick={onCreateDatasourceClick}
|
onCreateDatasourceClick={onCreateDatasourceClick}
|
||||||
onRunClick={onRunClick}
|
onRunClick={onRunClick}
|
||||||
|
|
@ -334,6 +368,7 @@ export function EditorJSONtoForm(props: Props) {
|
||||||
actionResponse={actionResponse}
|
actionResponse={actionResponse}
|
||||||
actionSource={actionSource}
|
actionSource={actionSource}
|
||||||
currentActionConfig={currentActionConfig}
|
currentActionConfig={currentActionConfig}
|
||||||
|
isRunDisabled={blockExecution}
|
||||||
isRunning={isRunning}
|
isRunning={isRunning}
|
||||||
onRunClick={onRunClick}
|
onRunClick={onRunClick}
|
||||||
runErrorMessage={runErrorMessage}
|
runErrorMessage={runErrorMessage}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ const ResultsCount = styled.div`
|
||||||
interface QueryDebuggerTabsProps {
|
interface QueryDebuggerTabsProps {
|
||||||
actionSource: SourceEntity;
|
actionSource: SourceEntity;
|
||||||
currentActionConfig?: Action;
|
currentActionConfig?: Action;
|
||||||
|
isRunDisabled?: boolean;
|
||||||
isRunning: boolean;
|
isRunning: boolean;
|
||||||
actionName: string; // Check what and how to get
|
actionName: string; // Check what and how to get
|
||||||
runErrorMessage?: string;
|
runErrorMessage?: string;
|
||||||
|
|
@ -59,6 +60,7 @@ function QueryDebuggerTabs({
|
||||||
actionResponse,
|
actionResponse,
|
||||||
actionSource,
|
actionSource,
|
||||||
currentActionConfig,
|
currentActionConfig,
|
||||||
|
isRunDisabled = false,
|
||||||
isRunning,
|
isRunning,
|
||||||
onRunClick,
|
onRunClick,
|
||||||
runErrorMessage,
|
runErrorMessage,
|
||||||
|
|
@ -233,6 +235,7 @@ function QueryDebuggerTabs({
|
||||||
actionName={actionName}
|
actionName={actionName}
|
||||||
actionSource={actionSource}
|
actionSource={actionSource}
|
||||||
currentActionConfig={currentActionConfig}
|
currentActionConfig={currentActionConfig}
|
||||||
|
isRunDisabled={isRunDisabled}
|
||||||
isRunning={isRunning}
|
isRunning={isRunning}
|
||||||
onRunClick={onRunClick}
|
onRunClick={onRunClick}
|
||||||
runErrorMessage={runErrorMessage}
|
runErrorMessage={runErrorMessage}
|
||||||
|
|
|
||||||
|
|
@ -5,22 +5,14 @@ import { StyledFormRow } from "./EditorJSONtoForm";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
|
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
|
||||||
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
|
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
|
||||||
import {
|
import { getHasManageActionPermission } from "ee/utils/BusinessFeatures/permissionPageHelpers";
|
||||||
getHasExecuteActionPermission,
|
|
||||||
getHasManageActionPermission,
|
|
||||||
} from "ee/utils/BusinessFeatures/permissionPageHelpers";
|
|
||||||
import { useActiveActionBaseId } from "ee/pages/Editor/Explorer/hooks";
|
import { useActiveActionBaseId } from "ee/pages/Editor/Explorer/hooks";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import {
|
import { getActionByBaseId, getPlugin } from "ee/selectors/entitiesSelector";
|
||||||
getActionByBaseId,
|
|
||||||
getPlugin,
|
|
||||||
getPluginNameFromId,
|
|
||||||
} from "ee/selectors/entitiesSelector";
|
|
||||||
import { QueryEditorContext } from "./QueryEditorContext";
|
import { QueryEditorContext } from "./QueryEditorContext";
|
||||||
import type { Plugin } from "api/PluginApi";
|
import type { Plugin } from "api/PluginApi";
|
||||||
import type { Datasource } from "entities/Datasource";
|
import type { Datasource } from "entities/Datasource";
|
||||||
import type { AppState } from "ee/reducers";
|
import type { AppState } from "ee/reducers";
|
||||||
import { SQL_DATASOURCES } from "constants/QueryEditorConstants";
|
|
||||||
import DatasourceSelector from "./DatasourceSelector";
|
import DatasourceSelector from "./DatasourceSelector";
|
||||||
import { getSavingStatusForActionName } from "selectors/actionSelectors";
|
import { getSavingStatusForActionName } from "selectors/actionSelectors";
|
||||||
import { getAssetUrl } from "ee/utils/airgapHelpers";
|
import { getAssetUrl } from "ee/utils/airgapHelpers";
|
||||||
|
|
@ -51,6 +43,7 @@ interface Props {
|
||||||
formName: string;
|
formName: string;
|
||||||
dataSources: Datasource[];
|
dataSources: Datasource[];
|
||||||
onCreateDatasourceClick: () => void;
|
onCreateDatasourceClick: () => void;
|
||||||
|
isRunDisabled?: boolean;
|
||||||
isRunning: boolean;
|
isRunning: boolean;
|
||||||
onRunClick: () => void;
|
onRunClick: () => void;
|
||||||
}
|
}
|
||||||
|
|
@ -59,6 +52,7 @@ const QueryEditorHeader = (props: Props) => {
|
||||||
const {
|
const {
|
||||||
dataSources,
|
dataSources,
|
||||||
formName,
|
formName,
|
||||||
|
isRunDisabled = false,
|
||||||
isRunning,
|
isRunning,
|
||||||
onCreateDatasourceClick,
|
onCreateDatasourceClick,
|
||||||
onRunClick,
|
onRunClick,
|
||||||
|
|
@ -78,11 +72,6 @@ const QueryEditorHeader = (props: Props) => {
|
||||||
currentActionConfig?.userPermissions,
|
currentActionConfig?.userPermissions,
|
||||||
);
|
);
|
||||||
|
|
||||||
const isExecutePermitted = getHasExecuteActionPermission(
|
|
||||||
isFeatureEnabled,
|
|
||||||
currentActionConfig?.userPermissions,
|
|
||||||
);
|
|
||||||
|
|
||||||
const currentPlugin = useSelector((state: AppState) =>
|
const currentPlugin = useSelector((state: AppState) =>
|
||||||
getPlugin(state, currentActionConfig?.pluginId || ""),
|
getPlugin(state, currentActionConfig?.pluginId || ""),
|
||||||
);
|
);
|
||||||
|
|
@ -95,29 +84,6 @@ const QueryEditorHeader = (props: Props) => {
|
||||||
|
|
||||||
const icon = ActionUrlIcon(iconUrl);
|
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 (
|
return (
|
||||||
<StyledFormRow>
|
<StyledFormRow>
|
||||||
<NameWrapper>
|
<NameWrapper>
|
||||||
|
|
@ -141,7 +107,7 @@ const QueryEditorHeader = (props: Props) => {
|
||||||
<Button
|
<Button
|
||||||
className="t--run-query"
|
className="t--run-query"
|
||||||
data-guided-tour-iid="run-query"
|
data-guided-tour-iid="run-query"
|
||||||
isDisabled={blockExecution}
|
isDisabled={isRunDisabled}
|
||||||
isLoading={isRunning}
|
isLoading={isRunning}
|
||||||
onClick={onRunClick}
|
onClick={onRunClick}
|
||||||
size="md"
|
size="md"
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,6 @@ import type { SourceEntity } from "entities/AppsmithConsole";
|
||||||
import type { Action } from "entities/Action";
|
import type { Action } from "entities/Action";
|
||||||
import { getActionData } from "ee/selectors/entitiesSelector";
|
import { getActionData } from "ee/selectors/entitiesSelector";
|
||||||
import { actionResponseDisplayDataFormats } from "../utils";
|
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 { getErrorAsString } from "sagas/ActionExecution/errorUtils";
|
||||||
import { isString } from "lodash";
|
import { isString } from "lodash";
|
||||||
import ActionExecutionInProgressView from "components/editorComponents/ActionExecutionInProgressView";
|
import ActionExecutionInProgressView from "components/editorComponents/ActionExecutionInProgressView";
|
||||||
|
|
@ -72,6 +69,7 @@ const ResponseContentWrapper = styled.div<{ isError: boolean }>`
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
actionSource: SourceEntity;
|
actionSource: SourceEntity;
|
||||||
|
isRunDisabled?: boolean;
|
||||||
isRunning: boolean;
|
isRunning: boolean;
|
||||||
onRunClick: () => void;
|
onRunClick: () => void;
|
||||||
currentActionConfig: Action;
|
currentActionConfig: Action;
|
||||||
|
|
@ -84,19 +82,13 @@ const QueryResponseTab = (props: Props) => {
|
||||||
actionName,
|
actionName,
|
||||||
actionSource,
|
actionSource,
|
||||||
currentActionConfig,
|
currentActionConfig,
|
||||||
|
isRunDisabled = false,
|
||||||
isRunning,
|
isRunning,
|
||||||
onRunClick,
|
onRunClick,
|
||||||
runErrorMessage,
|
runErrorMessage,
|
||||||
} = props;
|
} = props;
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
|
|
||||||
|
|
||||||
const isExecutePermitted = getHasExecuteActionPermission(
|
|
||||||
isFeatureEnabled,
|
|
||||||
currentActionConfig?.userPermissions,
|
|
||||||
);
|
|
||||||
|
|
||||||
const actionResponse = useSelector((state) =>
|
const actionResponse = useSelector((state) =>
|
||||||
getActionData(state, currentActionConfig.id),
|
getActionData(state, currentActionConfig.id),
|
||||||
);
|
);
|
||||||
|
|
@ -341,7 +333,7 @@ const QueryResponseTab = (props: Props) => {
|
||||||
)}
|
)}
|
||||||
{!output && !error && (
|
{!output && !error && (
|
||||||
<NoResponse
|
<NoResponse
|
||||||
isRunDisabled={!isExecutePermitted}
|
isRunDisabled={isRunDisabled}
|
||||||
isRunning={isRunning}
|
isRunning={isRunning}
|
||||||
onRunClick={responseTabOnRunClick}
|
onRunClick={responseTabOnRunClick}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user