diff --git a/app/client/src/PluginActionEditor/PluginActionContext.tsx b/app/client/src/PluginActionEditor/PluginActionContext.tsx index 42c0d4e67458..e37882c7d294 100644 --- a/app/client/src/PluginActionEditor/PluginActionContext.tsx +++ b/app/client/src/PluginActionEditor/PluginActionContext.tsx @@ -28,19 +28,27 @@ interface ChildrenProps { export const PluginActionContextProvider = ( props: ChildrenProps & PluginActionContextType, ) => { - const { action, children, datasource, editorConfig, plugin, settingsConfig } = - props; + const { + action, + actionResponse, + children, + datasource, + editorConfig, + plugin, + settingsConfig, + } = props; // using useMemo to avoid unnecessary renders const contextValue = useMemo( () => ({ action, + actionResponse, datasource, editorConfig, plugin, settingsConfig, }), - [action, datasource, editorConfig, plugin, settingsConfig], + [action, actionResponse, datasource, editorConfig, plugin, settingsConfig], ); return ( diff --git a/app/client/src/PluginActionEditor/components/PluginActionToolbar.tsx b/app/client/src/PluginActionEditor/components/PluginActionToolbar.tsx index 34e5b0fdfe98..5c0362be3190 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionToolbar.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionToolbar.tsx @@ -1,13 +1,16 @@ -import React from "react"; +import React, { useCallback } from "react"; import { IDEToolbar } from "IDE"; import { Button, Menu, MenuContent, MenuTrigger, Tooltip } from "@appsmith/ads"; import { modText } from "utils/helpers"; import { usePluginActionContext } from "../PluginActionContext"; import { + useBlockExecution, useHandleRunClick, useAnalyticsOnRunClick, } from "PluginActionEditor/hooks"; import { useToggle } from "@mantine/hooks"; +import { useSelector } from "react-redux"; +import { isActionRunning } from "PluginActionEditor/store"; interface PluginActionToolbarProps { runOptions?: React.ReactNode; @@ -20,11 +23,13 @@ const PluginActionToolbar = (props: PluginActionToolbarProps) => { const { handleRunClick } = useHandleRunClick(); const { callRunActionAnalytics } = useAnalyticsOnRunClick(); const [isMenuOpen, toggleMenuOpen] = useToggle([false, true]); + const blockExecution = useBlockExecution(); + const isRunning = useSelector(isActionRunning(action.id)); - const onRunClick = () => { + const onRunClick = useCallback(() => { callRunActionAnalytics(); handleRunClick(); - }; + }, [callRunActionAnalytics, handleRunClick]); return ( @@ -36,7 +41,13 @@ const PluginActionToolbar = (props: PluginActionToolbarProps) => { placement="topRight" showArrow={false} > - diff --git a/app/client/src/PluginActionEditor/hooks/index.ts b/app/client/src/PluginActionEditor/hooks/index.ts index c412903be5f8..c61530a08d8c 100644 --- a/app/client/src/PluginActionEditor/hooks/index.ts +++ b/app/client/src/PluginActionEditor/hooks/index.ts @@ -1,4 +1,5 @@ export { useActionSettingsConfig } from "ee/PluginActionEditor/hooks/useActionSettingsConfig"; export { useHandleDeleteClick } from "ee/PluginActionEditor/hooks/useHandleDeleteClick"; export { useHandleRunClick } from "ee/PluginActionEditor/hooks/useHandleRunClick"; +export { useBlockExecution } from "ee/PluginActionEditor/hooks/useBlockExecution"; export { useAnalyticsOnRunClick } from "ee/PluginActionEditor/hooks/useAnalyticsOnRunClick"; diff --git a/app/client/src/ce/PluginActionEditor/components/PluginActionResponse/hooks/usePluginActionResponseTabs.tsx b/app/client/src/ce/PluginActionEditor/components/PluginActionResponse/hooks/usePluginActionResponseTabs.tsx index d3d599fc81b0..4b8f04d18a50 100644 --- a/app/client/src/ce/PluginActionEditor/components/PluginActionResponse/hooks/usePluginActionResponseTabs.tsx +++ b/app/client/src/ce/PluginActionEditor/components/PluginActionResponse/hooks/usePluginActionResponseTabs.tsx @@ -17,10 +17,12 @@ import DebuggerLogs from "components/editorComponents/Debugger/DebuggerLogs"; import { PluginType } from "entities/Action"; import { ApiResponse } from "PluginActionEditor/components/PluginActionResponse/components/ApiResponse"; import { ApiResponseHeaders } from "PluginActionEditor/components/PluginActionResponse/components/ApiResponseHeaders"; -import { noop } from "lodash"; import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; import { getErrorCount } from "selectors/debuggerSelectors"; -import { getPluginActionDebuggerState } from "PluginActionEditor/store"; +import { + getPluginActionDebuggerState, + isActionRunning, +} from "PluginActionEditor/store"; import { doesPluginRequireDatasource } from "ee/entities/Engine/actionHelpers"; import useShowSchema from "components/editorComponents/ActionRightPane/useShowSchema"; import Schema from "components/editorComponents/Debugger/Schema"; @@ -28,9 +30,11 @@ import QueryResponseTab from "pages/Editor/QueryEditor/QueryResponseTab"; import type { SourceEntity } from "entities/AppsmithConsole"; import { ENTITY_TYPE as SOURCE_ENTITY_TYPE } from "ee/entities/AppsmithConsole/utils"; import { + useBlockExecution, useHandleRunClick, useAnalyticsOnRunClick, } from "PluginActionEditor/hooks"; +import useDebuggerTriggerClick from "components/editorComponents/Debugger/hooks/useDebuggerTriggerClick"; function usePluginActionResponseTabs() { const { action, actionResponse, datasource, plugin } = @@ -46,6 +50,10 @@ function usePluginActionResponseTabs() { const { responseTabHeight } = useSelector(getPluginActionDebuggerState); + const onDebugClick = useDebuggerTriggerClick(); + const isRunning = useSelector(isActionRunning(action.id)); + const blockExecution = useBlockExecution(); + const tabs: BottomTab[] = []; const onRunClick = () => { @@ -78,8 +86,8 @@ function usePluginActionResponseTabs() { ), @@ -141,7 +149,7 @@ function usePluginActionResponseTabs() { actionName={action.name} actionSource={actionSource} currentActionConfig={action} - isRunning={false} + isRunning={isRunning} onRunClick={onRunClick} runErrorMessage={""} // TODO /> diff --git a/app/client/src/ce/PluginActionEditor/hooks/useBlockExecution.ts b/app/client/src/ce/PluginActionEditor/hooks/useBlockExecution.ts new file mode 100644 index 000000000000..e02bcc7231d0 --- /dev/null +++ b/app/client/src/ce/PluginActionEditor/hooks/useBlockExecution.ts @@ -0,0 +1,63 @@ +import { getHasExecuteActionPermission } from "ee/utils/BusinessFeatures/permissionPageHelpers"; +import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; +import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; +import { DEFAULT_DATASOURCE_NAME } from "constants/ApiEditorConstants/ApiEditorConstants"; +import { UIComponentTypes } from "api/PluginApi"; +import { SQL_DATASOURCES } from "constants/QueryEditorConstants"; +import { usePluginActionContext } from "PluginActionEditor/PluginActionContext"; + +const useBlockExecution = () => { + const { action, plugin } = usePluginActionContext(); + const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled); + const isExecutePermitted = getHasExecuteActionPermission( + isFeatureEnabled, + action?.userPermissions, + ); + + let actionBody = ""; + let blockExecution = false; + + // API Editor Constants + // this gets the url of the current action's datasource + const actionDatasourceUrl = + action.datasource.datasourceConfiguration?.url || ""; + const actionDatasourceUrlPath = action.actionConfiguration.path || ""; + // this gets the name of the current action's datasource + const actionDatasourceName = action.datasource.name || ""; + + // Query Editor Constants + if (!!action.actionConfiguration) { + if ("formData" in action.actionConfiguration) { + // if the action has a formData (the action is postUQI e.g. Oracle) + actionBody = action.actionConfiguration.formData?.body?.data || ""; + } else { + // if the action is pre UQI, the path is different e.g. mySQL + actionBody = action.actionConfiguration?.body || ""; + } + } + + if ( + [ + UIComponentTypes.ApiEditorForm, + UIComponentTypes.GraphQLEditorForm, + ].includes(plugin.uiComponent) + ) { + // if the url is empty and the action's datasource name is the default datasource name (this means the api does not have a datasource attached) + // or the user does not have permission, + // we block action execution. + blockExecution = + (!actionDatasourceUrl && + !actionDatasourceUrlPath && + actionDatasourceName === DEFAULT_DATASOURCE_NAME) || + !isExecutePermitted; + } else { + // if (the body is empty and the action is an sql datasource) or the user does not have permission, block action execution. + blockExecution = + (!actionBody && SQL_DATASOURCES.includes(plugin.name)) || + !isExecutePermitted; + } + + return blockExecution; +}; + +export { useBlockExecution }; diff --git a/app/client/src/ee/PluginActionEditor/hooks/useBlockExecution.ts b/app/client/src/ee/PluginActionEditor/hooks/useBlockExecution.ts new file mode 100644 index 000000000000..d95708182c37 --- /dev/null +++ b/app/client/src/ee/PluginActionEditor/hooks/useBlockExecution.ts @@ -0,0 +1 @@ +export * from "ce/PluginActionEditor/hooks/useBlockExecution";