diff --git a/app/client/src/actions/pluginActionActions.ts b/app/client/src/actions/pluginActionActions.ts index 63bc11a30d..dd99f7c262 100644 --- a/app/client/src/actions/pluginActionActions.ts +++ b/app/client/src/actions/pluginActionActions.ts @@ -228,11 +228,16 @@ export const executePluginActionRequest = (payload: { id: string }) => ({ payload: payload, }); -export const executePluginActionSuccess = (payload: { +export interface ExecutePluginActionSuccessPayload { id: string; response: ActionResponse; isPageLoad?: boolean; -}) => ({ + isActionCreatedInApp: boolean; +} + +export const executePluginActionSuccess = ( + payload: ExecutePluginActionSuccessPayload, +) => ({ type: ReduxActionTypes.EXECUTE_PLUGIN_ACTION_SUCCESS, payload: payload, }); diff --git a/app/client/src/ce/reducers/entityReducers/actionsReducer.tsx b/app/client/src/ce/reducers/entityReducers/actionsReducer.tsx index 73793fa507..b8b9ce9ac9 100644 --- a/app/client/src/ce/reducers/entityReducers/actionsReducer.tsx +++ b/app/client/src/ce/reducers/entityReducers/actionsReducer.tsx @@ -8,7 +8,10 @@ import type { ActionResponse } from "api/ActionAPI"; import type { ExecuteErrorPayload } from "constants/AppsmithActionConstants/ActionConstants"; import _ from "lodash"; import type { Action } from "entities/Action"; -import type { UpdateActionPropertyActionPayload } from "actions/pluginActionActions"; +import type { + ExecutePluginActionSuccessPayload, + UpdateActionPropertyActionPayload, +} from "actions/pluginActionActions"; export interface ActionData { isLoading: boolean; @@ -173,8 +176,10 @@ export const handlers = { }, [ReduxActionTypes.EXECUTE_PLUGIN_ACTION_SUCCESS]: ( draftMetaState: Array, - action: ReduxAction<{ id: string; response: ActionResponse }>, + action: ReduxAction, ) => { + if (!action.payload.isActionCreatedInApp) return; + const foundAction = draftMetaState.find((stateAction) => { return stateAction.config.id === action.payload.id; }); diff --git a/app/client/src/ce/utils/getIsActionCreatedInApp.ts b/app/client/src/ce/utils/getIsActionCreatedInApp.ts new file mode 100644 index 0000000000..4efe0995f1 --- /dev/null +++ b/app/client/src/ce/utils/getIsActionCreatedInApp.ts @@ -0,0 +1,5 @@ +import type { Action } from "entities/Action"; + +export function getIsActionCreatedInApp(action: Action) { + return !!action; +} diff --git a/app/client/src/components/editorComponents/ApiResponseView.tsx b/app/client/src/components/editorComponents/ApiResponseView.tsx index d597c911f9..db55de5417 100644 --- a/app/client/src/components/editorComponents/ApiResponseView.tsx +++ b/app/client/src/components/editorComponents/ApiResponseView.tsx @@ -1,19 +1,14 @@ import type { PropsWithChildren, RefObject } from "react"; import React, { useCallback, useRef, useState } from "react"; -import { connect, useDispatch, useSelector } from "react-redux"; -import type { RouteComponentProps } from "react-router"; -import { withRouter } from "react-router"; +import { useDispatch, useSelector } from "react-redux"; import ReactJson from "react-json-view"; import styled from "styled-components"; -import type { AppState } from "@appsmith/reducers"; import type { ActionResponse } from "api/ActionAPI"; import { formatBytes } from "utils/helpers"; -import type { APIEditorRouteParams } from "constants/routes"; import type { SourceEntity } from "entities/AppsmithConsole"; import LOG_TYPE from "entities/AppsmithConsole/logtype"; import { ENTITY_TYPE } from "entities/AppsmithConsole"; import ReadOnlyEditor from "components/editorComponents/ReadOnlyEditor"; -import { getActionResponses } from "@appsmith/selectors/entitiesSelector"; import { isArray, isEmpty, isString } from "lodash"; import { CHECK_REQUEST_BODY, @@ -25,7 +20,7 @@ import { DEBUGGER_ERRORS, } from "@appsmith/constants/messages"; import { Text as BlueprintText } from "@blueprintjs/core"; -import type { EditorTheme } from "./CodeEditor/EditorConfig"; +import { EditorTheme } from "./CodeEditor/EditorConfig"; import NoResponseSVG from "assets/images/no-response.svg"; import DebuggerLogs from "./Debugger/DebuggerLogs"; import ErrorLogs from "./Debugger/Errors"; @@ -38,11 +33,11 @@ import EntityBottomTabs from "./EntityBottomTabs"; import { DEBUGGER_TAB_KEYS } from "./Debugger/helpers"; import Table from "pages/Editor/QueryEditor/Table"; import { API_RESPONSE_TYPE_OPTIONS } from "constants/ApiEditorConstants/CommonApiConstants"; -import type { UpdateActionPropertyActionPayload } from "actions/pluginActionActions"; import { setActionResponseDisplayFormat } from "actions/pluginActionActions"; import { isHtml } from "./utils"; import { getDebuggerSelectedTab, + getErrorCount, getResponsePaneHeight, } from "selectors/debuggerSelectors"; import { ActionExecutionResizerHeight } from "pages/Editor/APIEditor/constants"; @@ -179,29 +174,17 @@ const ResponseBodyContainer = styled.div` display: grid; `; -interface ReduxStateProps { - responses: Record; - isRunning: Record; - errorCount: number; +interface Props { + currentActionConfig?: Action; + theme?: EditorTheme; + apiName: string; + disabled?: boolean; + onRunClick: () => void; + responseDataTypes: { key: string; title: string }[]; + responseDisplayFormat: { title: string; value: string }; + actionResponse?: ActionResponse; + isRunning: boolean; } -interface ReduxDispatchProps { - updateActionResponseDisplayFormat: ({ - field, - id, - value, - }: UpdateActionPropertyActionPayload) => void; -} - -type Props = ReduxStateProps & - ReduxDispatchProps & - RouteComponentProps & { - theme?: EditorTheme; - apiName: string; - disabled?: boolean; - onRunClick: () => void; - responseDataTypes: { key: string; title: string }[]; - responseDisplayFormat: { title: string; value: string }; - }; const StatusCodeText = styled(BaseText)>` color: ${(props) => @@ -313,31 +296,21 @@ export const NoResponse = (props: NoResponseProps) => ( function ApiResponseView(props: Props) { const { + actionResponse = EMPTY_RESPONSE, + currentActionConfig, disabled, - match: { - params: { apiId }, - }, + isRunning, responseDataTypes, responseDisplayFormat, - responses, - updateActionResponseDisplayFormat, + theme = EditorTheme.LIGHT, } = props; - let response: ActionResponse = EMPTY_RESPONSE; - let isRunning = false; - let hasFailed = false; - if (apiId && apiId in responses) { - response = responses[apiId] || EMPTY_RESPONSE; - isRunning = props.isRunning[apiId]; - hasFailed = response.statusCode ? response.statusCode[0] !== "2" : false; - } - const actions: Action[] = useSelector((state: AppState) => - state.entities.actions.map((action) => action.config), - ); - const currentActionConfig: Action | undefined = actions.find( - (action) => action.id === apiId, - ); + const hasFailed = actionResponse.statusCode + ? actionResponse.statusCode[0] !== "2" + : false; + const panelRef: RefObject = useRef(null); const dispatch = useDispatch(); + const errorCount = useSelector(getErrorCount); const onDebugClick = useCallback(() => { AnalyticsUtil.logEvent("OPEN_DEBUGGER", { @@ -353,12 +326,12 @@ function ApiResponseView(props: Props) { }); }; - const messages = response?.messages; + const messages = actionResponse?.messages; let responseHeaders = {}; // if no headers are present in the response, use the default body text. - if (response.headers) { - Object.entries(response.headers).forEach(([key, value]) => { + if (actionResponse.headers) { + Object.entries(actionResponse.headers).forEach(([key, value]) => { if (isArray(value) && value.length < 2) return (responseHeaders = { ...responseHeaders, @@ -375,17 +348,19 @@ function ApiResponseView(props: Props) { } const onResponseTabSelect = (tab: string) => { - updateActionResponseDisplayFormat({ - id: apiId ? apiId : "", - field: "responseDisplayFormat", - value: tab, - }); + dispatch( + setActionResponseDisplayFormat({ + id: currentActionConfig?.id || "", + field: "responseDisplayFormat", + value: tab, + }), + ); }; let filteredResponseDataTypes: { key: string; title: string }[] = [ ...responseDataTypes, ]; - if (!!response.body && !isArray(response.body)) { + if (!!actionResponse.body && !isArray(actionResponse.body)) { filteredResponseDataTypes = responseDataTypes.filter( (item) => item.key !== API_RESPONSE_TYPE_OPTIONS.TABLE, ); @@ -403,7 +378,7 @@ function ApiResponseView(props: Props) { title: dataType.title, panelComponent: responseTabComponent( dataType.key, - response?.body, + actionResponse?.body, responsePaneHeight, ), }; @@ -444,12 +419,12 @@ function ApiResponseView(props: Props) { }, []); // get request timestamp formatted to human readable format. - const responseState = getUpdateTimestamp(response.request); + const responseState = getUpdateTimestamp(actionResponse.request); // action source for analytics. const actionSource: SourceEntity = { type: ENTITY_TYPE.ACTION, name: currentActionConfig ? currentActionConfig.name : "API", - id: apiId ? apiId : "", + id: currentActionConfig?.id || "", }; const tabs = [ { @@ -471,16 +446,18 @@ function ApiResponseView(props: Props) { Your API failed to execute - {response.pluginErrorDetails && ":"} + {actionResponse.pluginErrorDetails && ":"} - {response.pluginErrorDetails && ( + {actionResponse.pluginErrorDetails && ( <>
- {response.pluginErrorDetails.downstreamErrorMessage} + {actionResponse.pluginErrorDetails.downstreamErrorMessage}
- {response.pluginErrorDetails.downstreamErrorCode && ( + {actionResponse.pluginErrorDetails.downstreamErrorCode && ( )} @@ -488,11 +465,11 @@ function ApiResponseView(props: Props) {
- {response.request && ( + {actionResponse.request && ( e.stopPropagation()} @@ -503,7 +480,7 @@ function ApiResponseView(props: Props) { ) : ( - {isEmpty(response.statusCode) ? ( + {isEmpty(actionResponse.statusCode) ? ( ) : ( - {isString(response?.body) && isHtml(response?.body) ? ( + {isString(actionResponse?.body) && + isHtml(actionResponse?.body) ? ( ) : responseTabs && @@ -536,7 +514,7 @@ function ApiResponseView(props: Props) { /> {responseTabComponent( selectedControl || segmentedControlOptions[0]?.value, - response?.body, + actionResponse?.body, responsePaneHeight, )} @@ -569,7 +547,7 @@ function ApiResponseView(props: Props) { )} - {isEmpty(response.statusCode) ? ( + {isEmpty(actionResponse.statusCode) ? ( , }, { @@ -624,48 +602,49 @@ function ApiResponseView(props: Props) { snapToHeight={ActionExecutionResizerHeight} /> {isRunning && ( - + )} - {response.statusCode && ( + {actionResponse.statusCode && ( - {response.statusCode && ( + {actionResponse.statusCode && ( Status: - {response.statusCode} + {actionResponse.statusCode} )} - {response.duration && ( + {actionResponse.duration && ( Time: - {response.duration} ms + {actionResponse.duration} ms )} - {response.size && ( + {actionResponse.size && ( Size: - {formatBytes(parseInt(response.size))} - - - )} - {!isEmpty(response?.body) && Array.isArray(response?.body) && ( - - Result: - - {`${response?.body.length} Record${ - response?.body.length > 1 ? "s" : "" - }`} + {formatBytes(parseInt(actionResponse.size))} )} + {!isEmpty(actionResponse?.body) && + Array.isArray(actionResponse?.body) && ( + + Result: + + {`${actionResponse?.body.length} Record${ + actionResponse?.body.length > 1 ? "s" : "" + }`} + + + )} )} @@ -688,25 +667,4 @@ function ApiResponseView(props: Props) { ); } -const mapStateToProps = (state: AppState): ReduxStateProps => { - return { - responses: getActionResponses(state), - isRunning: state.ui.apiPane.isRunning, - errorCount: state.ui.debugger.context.errorCount, - }; -}; - -const mapDispatchToProps = (dispatch: any): ReduxDispatchProps => ({ - updateActionResponseDisplayFormat: ({ - field, - id, - value, - }: UpdateActionPropertyActionPayload) => { - dispatch(setActionResponseDisplayFormat({ id, field, value })); - }, -}); - -export default connect( - mapStateToProps, - mapDispatchToProps, -)(withRouter(ApiResponseView)); +export default ApiResponseView; diff --git a/app/client/src/ee/utils/getIsActionCreatedInApp.ts b/app/client/src/ee/utils/getIsActionCreatedInApp.ts new file mode 100644 index 0000000000..692423e227 --- /dev/null +++ b/app/client/src/ee/utils/getIsActionCreatedInApp.ts @@ -0,0 +1 @@ +export * from "ce/utils/getIsActionCreatedInApp"; diff --git a/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx b/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx index 4fd5deb1fe..77e5da8daa 100644 --- a/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx +++ b/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx @@ -8,7 +8,11 @@ import { GRAPHQL_HTTP_METHOD_OPTIONS } from "constants/ApiEditorConstants/GraphQ import styled from "styled-components"; import FormLabel from "components/editorComponents/FormLabel"; import FormRow from "components/editorComponents/FormRow"; -import type { PaginationField, SuggestedWidget } from "api/ActionAPI"; +import type { + ActionResponse, + PaginationField, + SuggestedWidget, +} from "api/ActionAPI"; import type { Action, PaginationType } from "entities/Action"; import { isGraphqlPlugin } from "entities/Action"; import KeyValueFieldArray from "components/editorComponents/form/fields/KeyValueFieldArray"; @@ -164,6 +168,7 @@ const MainContainer = styled.div` /* padding: var(--ads-v2-spaces-7); */ `; export interface CommonFormProps { + actionResponse?: ActionResponse; pluginId: string; onRunClick: (paginationField?: PaginationField) => void; onDeleteClick: () => void; @@ -508,6 +513,7 @@ function CommonEditorForm(props: CommonFormPropsWithExtraParams) { actionConfigurationHeaders, actionConfigurationParams, actionName, + actionResponse, autoGeneratedActionConfigHeaders, closeEditorLink, currentActionDatasourceId, @@ -729,8 +735,11 @@ function CommonEditorForm(props: CommonFormPropsWithExtraParams) { {showDebugger && ( { - return { - key: data.dataType, - title: data.dataType, - }; - }); - responseDisplayFormat = { - title: actionData.responseDisplayFormat, - value: actionData.responseDisplayFormat, - }; - } else { - responseDataTypes = []; - responseDisplayFormat = { - title: "", - value: "", - }; - } + const { responseDataTypes, responseDisplayFormat } = + actionResponseDisplayDataFormats(actionData); return { actionName, + actionResponse, apiId, httpMethodFromForm, actionConfigurationHeaders, diff --git a/app/client/src/pages/Editor/APIEditor/RapidApiEditorForm.tsx b/app/client/src/pages/Editor/APIEditor/RapidApiEditorForm.tsx index d8389cecf6..3dba7e603b 100644 --- a/app/client/src/pages/Editor/APIEditor/RapidApiEditorForm.tsx +++ b/app/client/src/pages/Editor/APIEditor/RapidApiEditorForm.tsx @@ -1,12 +1,18 @@ import React, { useContext } from "react"; import { connect } from "react-redux"; +import type { RouteComponentProps } from "react-router"; import type { InjectedFormProps } from "redux-form"; import { reduxForm, formValueSelector } from "redux-form"; import { POST_BODY_FORMAT_OPTIONS } from "constants/ApiEditorConstants/CommonApiConstants"; import styled from "styled-components"; import FormLabel from "components/editorComponents/FormLabel"; import FormRow from "components/editorComponents/FormRow"; -import type { PaginationField, BodyFormData, Property } from "api/ActionAPI"; +import type { + PaginationField, + BodyFormData, + Property, + ActionResponse, +} from "api/ActionAPI"; import DynamicTextField from "components/editorComponents/form/fields/DynamicTextField"; import KeyValueFieldArray from "components/editorComponents/form/fields/KeyValueFieldArray"; import ApiResponseView from "components/editorComponents/ApiResponseView"; @@ -18,11 +24,12 @@ import type { PaginationType, Action } from "entities/Action"; import ActionNameEditor from "components/editorComponents/ActionNameEditor"; import { NameWrapper } from "./CommonEditorForm"; import { BaseButton } from "components/designSystems/appsmith/BaseButton"; -import { getActionData } from "@appsmith/selectors/entitiesSelector"; +import { getAction, getActionData } from "@appsmith/selectors/entitiesSelector"; import type { AppState } from "@appsmith/reducers"; import { Icon } from "design-system"; import { showDebuggerFlag } from "selectors/debuggerSelectors"; import { ApiEditorContext } from "./ApiEditorContext"; +import { actionResponseDisplayDataFormats } from "../utils"; const Form = styled.form` display: flex; @@ -96,13 +103,21 @@ const TabbedViewContainer = styled.div` padding-top: 12px; `; -interface APIFormProps { - onRunClick: (paginationField?: PaginationField) => void; - onDeleteClick: () => void; - isRunning: boolean; - isDeleting: boolean; - paginationType: PaginationType; +interface APIFormOwnProps { + apiId: string; + apiName: string; appName: string; + isDeleting: boolean; + isRunning: boolean; + location: RouteComponentProps["location"]; + onDeleteClick: () => void; + onRunClick: (paginationField?: PaginationField) => void; + paginationType: PaginationType; +} + +interface APIFormProps { + actionData?: ActionResponse; + currentActionConfig?: Action; templateId: string; actionConfiguration?: any; actionConfigurationHeaders?: Property[]; @@ -111,18 +126,15 @@ interface APIFormProps { providerImage: string; providerURL: string; providerCredentialSteps: string; - location: { - pathname: string; - }; - apiName: string; - apiId: string; dispatch: any; responseDataTypes: { key: string; title: string }[]; responseDisplayFormat: { title: string; value: string }; showDebugger: boolean; } -type Props = APIFormProps & InjectedFormProps; +type Props = APIFormProps & + InjectedFormProps & + APIFormOwnProps; function RapidApiEditorForm(props: Props) { const { @@ -276,7 +288,10 @@ function RapidApiEditorForm(props: Props) { {showDebugger && ( { +export default connect((state: AppState, ownProps: APIFormOwnProps) => { const displayFormat = selector(state, "displayFormat"); const providerImage = selector(state, "provider.imageUrl"); const providerURL = selector(state, "provider.url"); @@ -317,29 +332,17 @@ export default connect((state: AppState) => { `${actionConfigurationBodyFormData}`, ); } + const currentActionConfig = getAction(state, ownProps.apiId); const actionData = getActionData(state, actionConfiguration.id); - let responseDisplayFormat: { title: string; value: string }; - let responseDataTypes: { key: string; title: string }[]; - if (!!actionData && actionData.responseDisplayFormat) { - responseDataTypes = actionData.dataTypes.map((data) => { - return { - key: data.dataType, - title: data.dataType, - }; - }); - responseDisplayFormat = { - title: actionData.responseDisplayFormat, - value: actionData.responseDisplayFormat, - }; - } else { - responseDataTypes = []; - responseDisplayFormat = { + const { responseDataTypes, responseDisplayFormat } = + actionResponseDisplayDataFormats(actionData, { title: "JSON", value: "JSON", - }; - } + }); return { + actionData, + currentActionConfig, displayFormat, actionConfiguration, actionConfigurationHeaders, @@ -353,7 +356,7 @@ export default connect((state: AppState) => { providerCredentialSteps, }; })( - reduxForm({ + reduxForm({ form: API_EDITOR_FORM_NAME, destroyOnUnmount: false, })(RapidApiEditorForm), diff --git a/app/client/src/pages/Editor/APIEditor/RestAPIForm.tsx b/app/client/src/pages/Editor/APIEditor/RestAPIForm.tsx index b593c4e1f0..9bd36ebc82 100644 --- a/app/client/src/pages/Editor/APIEditor/RestAPIForm.tsx +++ b/app/client/src/pages/Editor/APIEditor/RestAPIForm.tsx @@ -6,7 +6,6 @@ import styled from "styled-components"; import { API_EDITOR_FORM_NAME } from "@appsmith/constants/forms"; import type { Action } from "entities/Action"; import PostBodyData from "./PostBodyData"; -import { EMPTY_RESPONSE } from "components/editorComponents/emptyResponse"; import type { AppState } from "@appsmith/reducers"; import { getApiName } from "selectors/formSelectors"; import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; @@ -25,6 +24,7 @@ import CommonEditorForm from "./CommonEditorForm"; import Pagination from "./Pagination"; import { getCurrentEnvironmentId } from "@appsmith/selectors/environmentSelectors"; import { ApiEditorContext } from "./ApiEditorContext"; +import { actionResponseDisplayDataFormats } from "../utils"; const NoBodyMessage = styled.div` margin-top: 20px; @@ -162,39 +162,23 @@ export default connect((state: AppState, props: { pluginId: string }) => { } const responses = getActionResponses(state); + const actionResponse = responses[apiId]; let hasResponse = false; let suggestedWidgets; - if (apiId && apiId in responses) { - const response = responses[apiId] || EMPTY_RESPONSE; + if (actionResponse) { hasResponse = - !isEmpty(response.statusCode) && response.statusCode[0] === "2"; - suggestedWidgets = response.suggestedWidgets; + !isEmpty(actionResponse.statusCode) && + actionResponse.statusCode[0] === "2"; + suggestedWidgets = actionResponse.suggestedWidgets; } const actionData = getActionData(state, apiId); - let responseDisplayFormat: { title: string; value: string }; - let responseDataTypes: { key: string; title: string }[]; - if (!!actionData && actionData.responseDisplayFormat) { - responseDataTypes = actionData.dataTypes.map((data) => { - return { - key: data.dataType, - title: data.dataType, - }; - }); - responseDisplayFormat = { - title: actionData.responseDisplayFormat, - value: actionData.responseDisplayFormat, - }; - } else { - responseDataTypes = []; - responseDisplayFormat = { - title: "", - value: "", - }; - } + const { responseDataTypes, responseDisplayFormat } = + actionResponseDisplayDataFormats(actionData); return { actionName, + actionResponse, apiId, httpMethodFromForm, actionConfigurationHeaders, diff --git a/app/client/src/pages/Editor/QueryEditor/Editor.tsx b/app/client/src/pages/Editor/QueryEditor/Editor.tsx index 5b91f01e33..7dab744921 100644 --- a/app/client/src/pages/Editor/QueryEditor/Editor.tsx +++ b/app/client/src/pages/Editor/QueryEditor/Editor.tsx @@ -228,10 +228,10 @@ class QueryEditor extends React.Component { return ( props.theme.spaces[17] + 1}px; - top: 9px; - color: var(--ads-v2-color-fg); -`; - const FieldWrapper = styled.div` margin-top: 15px; `; @@ -196,18 +148,6 @@ const SecondaryWrapper = styled.div` overflow: hidden; `; -const HelpSection = styled.div``; - -const ResponseContentWrapper = styled.div<{ isError: boolean }>` - overflow-y: clip; - display: grid; - height: ${(props) => (props.isError ? "" : "100%")}; - - ${HelpSection} { - margin-bottom: 10px; - } -`; - export const StyledFormRow = styled(FormRow)` padding: 0px var(--ads-v2-spaces-7) var(--ads-v2-spaces-5) var(--ads-v2-spaces-7); @@ -338,15 +278,7 @@ interface QueryFormProps { isRunning: boolean; dataSources: Datasource[]; uiComponent: UIComponentTypes; - executedQueryData?: { - body: any; - isExecutionSuccess?: boolean; - messages?: Array; - suggestedWidgets?: SuggestedWidget[]; - readableError?: string; - pluginErrorDetails?: PluginErrorDetails; - request?: ActionApiResponseReq; - }; + actionResponse?: ActionResponse; runErrorMessage: string | undefined; location: { state: any; @@ -357,11 +289,6 @@ interface QueryFormProps { formData: SaaSAction | QueryAction; responseDisplayFormat: { title: string; value: string }; responseDataTypes: { key: string; title: string }[]; - updateActionResponseDisplayFormat: ({ - field, - id, - value, - }: UpdateActionPropertyActionPayload) => void; datasourceId: string; showCloseEditor: boolean; } @@ -382,10 +309,10 @@ type Props = EditorJSONtoFormProps & export function EditorJSONtoForm(props: Props) { const { actionName, + actionResponse, dataSources, documentationLink, editorConfig, - executedQueryData, formName, handleSubmit, isRunning, @@ -397,7 +324,6 @@ export function EditorJSONtoForm(props: Props) { runErrorMessage, settingConfig, uiComponent, - updateActionResponseDisplayFormat, } = props; const { @@ -408,16 +334,8 @@ export function EditorJSONtoForm(props: Props) { saveActionName, } = useContext(QueryEditorContext); - let error = runErrorMessage; - let output: Record[] | null = null; - let hintMessages: Array = []; - const panelRef: RefObject = useRef(null); - const params = useParams<{ apiId?: string; queryId?: string }>(); - // fetch the error count from the store. - const errorCount = useSelector(getErrorCount); - const actions: Action[] = useSelector((state: AppState) => state.entities.actions.map((action) => action.config), ); @@ -426,6 +344,8 @@ export function EditorJSONtoForm(props: Props) { (action) => action.id === params.apiId || action.id === params.queryId, ); const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled); + const [showResponseOnFirstLoad, setShowResponseOnFirstLoad] = + useState(false); const isChangePermitted = getHasManageActionPermission( isFeatureEnabled, @@ -440,9 +360,6 @@ export function EditorJSONtoForm(props: Props) { (state: AppState) => getCurrentAppWorkspace(state).userPermissions ?? [], ); - const [showResponseOnFirstLoad, setShowResponseOnFirstLoad] = - useState(false); - const canCreateDatasource = getHasCreateDatasourcePermission( isFeatureEnabled, userWorkspacePermissions, @@ -470,54 +387,24 @@ export function EditorJSONtoForm(props: Props) { (!actionBody && SQL_DATASOURCES.includes(currentActionPluginName)) || !isExecutePermitted; - // Query is executed even once during the session, show the response data. - if (executedQueryData) { - if (!executedQueryData.isExecutionSuccess) { - // Pass the error to be shown in the error tab - error = executedQueryData.readableError - ? getErrorAsString(executedQueryData.readableError) - : getErrorAsString(executedQueryData.body); - } else if (isString(executedQueryData.body)) { - //reset error. - error = ""; - try { - // Try to parse response as JSON array to be displayed in the Response tab - output = JSON.parse(executedQueryData.body); - } catch (e) { - // In case the string is not a JSON, wrap it in a response object - output = [ - { - response: executedQueryData.body, - }, - ]; - } - } else { - //reset error. - error = ""; - output = executedQueryData.body; - } - if (executedQueryData.messages && executedQueryData.messages.length) { - //reset error. - error = ""; - hintMessages = executedQueryData.messages; - } - } + const dispatch = useDispatch(); // These useEffects are used to open the response tab by default for page load queries // as for page load queries, query response is available and can be shown in response tab useEffect(() => { - // output and responseDisplayFormat is present only when query has response available + // actionResponse and responseDisplayFormat is present only when query has response available if ( responseDisplayFormat && !!responseDisplayFormat?.title && - output && + actionResponse && + actionResponse.isExecutionSuccess && !showResponseOnFirstLoad ) { dispatch(showDebugger(true)); dispatch(setDebuggerSelectedTab(DEBUGGER_TAB_KEYS.RESPONSE_TAB)); setShowResponseOnFirstLoad(true); } - }, [responseDisplayFormat, output, showResponseOnFirstLoad]); + }, [responseDisplayFormat, actionResponse, showResponseOnFirstLoad]); // When multiple page load queries exist, we want to response tab by default for all of them // Hence this useEffect will reset showResponseOnFirstLoad flag used to track whether to show response tab or not @@ -527,8 +414,6 @@ export function EditorJSONtoForm(props: Props) { } }, [currentActionConfig?.id]); - const dispatch = useDispatch(); - const handleDocumentationClick = (e: React.MouseEvent) => { e.stopPropagation(); openDoc(DocsLink.QUERY, plugin?.documentationLink, plugin?.name); @@ -684,63 +569,8 @@ export function EditorJSONtoForm(props: Props) { }); }; - // get the response pane height from the store. - const responsePaneHeight = useSelector(getResponsePaneHeight); - // set the response pane height on resize. - const setQueryResponsePaneHeight = useCallback((height: number) => { - dispatch(setResponsePaneHeight(height)); - }, []); - - const responseBodyTabs = - responseDataTypes && - responseDataTypes.map((dataType, index) => { - return { - index: index, - key: dataType.key, - title: dataType.title, - panelComponent: responseTabComponent( - dataType.key, - output, - responsePaneHeight, - ), - }; - }); - - const segmentedControlOptions = - responseBodyTabs && - responseBodyTabs.map((item) => { - return { value: item.key, label: item.title }; - }); - - const [selectedControl, setSelectedControl] = useState( - segmentedControlOptions[0]?.value, - ); - - const onResponseTabSelect = (tabKey: string) => { - if (tabKey === DEBUGGER_TAB_KEYS.ERROR_TAB) { - AnalyticsUtil.logEvent("OPEN_DEBUGGER", { - source: "QUERY_PANE", - }); - } - updateActionResponseDisplayFormat({ - id: currentActionConfig?.id || "", - field: "responseDisplayFormat", - value: tabKey, - }); - }; - // onResponseTabSelect(selectedControl); - const selectedTabIndex = - responseDataTypes && - responseDataTypes.findIndex( - (dataType) => dataType.title === responseDisplayFormat?.title, - ); - - //Update request timestamp to human readable format. - const responseState = - executedQueryData && getUpdateTimestamp(executedQueryData.request); - // action source for analytics. const actionSource: SourceEntity = { type: SOURCE_ENTITY_TYPE.ACTION, @@ -748,128 +578,6 @@ export function EditorJSONtoForm(props: Props) { id: currentActionConfig ? currentActionConfig.id : "", }; - const responseTabs = [ - { - key: "response", - title: "Response", - panelComponent: ( - - {error && ( - - - - Your query failed to execute - {executedQueryData && - (executedQueryData.pluginErrorDetails || - executedQueryData.body) && - ":"} - - {executedQueryData && - (executedQueryData.pluginErrorDetails ? ( - <> -
- { - executedQueryData.pluginErrorDetails - .downstreamErrorMessage - } -
- {executedQueryData.pluginErrorDetails - .downstreamErrorCode && ( - - )} - - ) : ( - executedQueryData.body && ( -
- {executedQueryData.body} -
- ) - ))} - -
- {executedQueryData && executedQueryData.request && ( - e.stopPropagation()} - > - - - )} -
- )} - {hintMessages && hintMessages.length > 0 && ( - - {hintMessages.map((msg, index) => ( - - {msg} - - ))} - - )} - {currentActionConfig && - output && - responseBodyTabs && - responseBodyTabs.length > 0 && - selectedTabIndex !== -1 && ( - - { - setSelectedControl(value); - onResponseTabSelect(value); - }} - options={segmentedControlOptions} - value={selectedControl} - /> - {responseTabComponent( - selectedControl || segmentedControlOptions[0]?.value, - output, - responsePaneHeight, - )} - - )} - {!output && !error && ( - - )} -
- ), - }, - { - key: DEBUGGER_TAB_KEYS.ERROR_TAB, - title: createMessage(DEBUGGER_ERRORS), - count: errorCount, - panelComponent: , - }, - { - key: DEBUGGER_TAB_KEYS.LOGS_TAB, - title: createMessage(DEBUGGER_LOGS), - panelComponent: , - }, - { - key: DEBUGGER_TAB_KEYS.INSPECT_TAB, - title: createMessage(INSPECT_ENTITY), - panelComponent: , - }, - ]; - const { hasDependencies } = useEntityDependencies(props.actionName); const pluginImages = useSelector(getPluginImages); @@ -907,18 +615,10 @@ export function EditorJSONtoForm(props: Props) { const selectedResponseTab = useSelector(getDebuggerSelectedTab); - const setSelectedResponseTab = useCallback((tabKey: string) => { - dispatch(setDebuggerSelectedTab(tabKey)); - }, []); - - // close the debugger - //TODO: move this to a common place - const onClose = () => dispatch(showDebugger(false)); - // here we check for normal conditions for opening action pane // or if any of the flags are true, We should open the actionpane by default. const shouldOpenActionPaneByDefault = - ((hasDependencies || !!output) && !guidedTourEnabled) || + ((hasDependencies || !!actionResponse) && !guidedTourEnabled) || currentActionPluginName !== PluginName.SMTP; // when switching between different redux forms, make sure this redux form has been initialized before rendering anything. @@ -1068,52 +768,18 @@ export function EditorJSONtoForm(props: Props) { {renderDebugger && selectedResponseTab !== DEBUGGER_TAB_KEYS.HEADER_TAB && ( - - - setQueryResponsePaneHeight(height) - } - openResizer={isRunning} - panelRef={panelRef} - snapToHeight={ActionExecutionResizerHeight} - /> - {isRunning && ( - - )} - - {output && !!output.length && ( - - - Result: - {` ${output.length} Record${ - output.length > 1 ? "s" : "" - }`} - - - )} - - - - + )} @@ -1125,9 +791,9 @@ export function EditorJSONtoForm(props: Props) { context={DatasourceStructureContext.QUERY_EDITOR} datasourceId={props.datasourceId} hasConnections={hasDependencies} - hasResponse={!!output} + hasResponse={!!actionResponse} pluginId={props.pluginId} - suggestedWidgets={executedQueryData?.suggestedWidgets} + suggestedWidgets={actionResponse?.suggestedWidgets} /> diff --git a/app/client/src/pages/Editor/QueryEditor/Form.tsx b/app/client/src/pages/Editor/QueryEditor/Form.tsx index 8585dbca18..d8820bd10a 100644 --- a/app/client/src/pages/Editor/QueryEditor/Form.tsx +++ b/app/client/src/pages/Editor/QueryEditor/Form.tsx @@ -12,6 +12,7 @@ import { import type { EditorJSONtoFormProps } from "./EditorJSONtoForm"; import { EditorJSONtoForm } from "./EditorJSONtoForm"; import { getFormEvaluationState } from "selectors/formSelectors"; +import { actionResponseDisplayDataFormats } from "../utils"; const valueSelector = formValueSelector(QUERY_EDITOR_FORM_NAME); const mapStateToProps = (state: AppState, props: any) => { @@ -20,27 +21,8 @@ const mapStateToProps = (state: AppState, props: any) => { const pluginId = valueSelector(state, "datasource.pluginId"); const selectedDbId = valueSelector(state, "datasource.id"); const actionData = getActionData(state, actionId); - let responseDisplayFormat: { title: string; value: string }; - let responseDataTypes: { key: string; title: string }[]; - - if (actionData && actionData.responseDisplayFormat) { - responseDataTypes = actionData.dataTypes.map((data) => { - return { - key: data.dataType, - title: data.dataType, - }; - }); - responseDisplayFormat = { - title: actionData.responseDisplayFormat, - value: actionData.responseDisplayFormat, - }; - } else { - responseDataTypes = []; - responseDisplayFormat = { - title: "", - value: "", - }; - } + const { responseDataTypes, responseDisplayFormat } = + actionResponseDisplayDataFormats(actionData); const responseTypes = getPluginResponseTypes(state); const documentationLinks = getPluginDocumentationLinks(state); diff --git a/app/client/src/pages/Editor/QueryEditor/QueryResponseView.tsx b/app/client/src/pages/Editor/QueryEditor/QueryResponseView.tsx new file mode 100644 index 0000000000..bf4f687f71 --- /dev/null +++ b/app/client/src/pages/Editor/QueryEditor/QueryResponseView.tsx @@ -0,0 +1,393 @@ +import { + setDebuggerSelectedTab, + setResponsePaneHeight, + showDebugger, +} from "actions/debuggerActions"; +import { CloseDebugger } from "components/editorComponents/Debugger/DebuggerTabs"; +import EntityBottomTabs from "components/editorComponents/EntityBottomTabs"; +import React, { useCallback, useRef, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import styled from "styled-components"; +import { ActionExecutionResizerHeight } from "pages/Editor/APIEditor/constants"; +import { + getDebuggerSelectedTab, + getErrorCount, + getResponsePaneHeight, +} from "selectors/debuggerSelectors"; +import { Text, TextType } from "design-system-old"; +import ActionExecutionInProgressView from "components/editorComponents/ActionExecutionInProgressView"; +import Resizable, { + ResizerCSS, +} from "components/editorComponents/Debugger/Resizer"; +import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; +import { DEBUGGER_TAB_KEYS } from "components/editorComponents/Debugger/helpers"; +import { + DEBUGGER_ERRORS, + DEBUGGER_LOGS, + INSPECT_ENTITY, + createMessage, +} from "@appsmith/constants/messages"; +import DebuggerLogs from "components/editorComponents/Debugger/DebuggerLogs"; +import ErrorLogs from "components/editorComponents/Debugger/Errors"; +import { Callout, SegmentedControl } from "design-system"; +import { + NoResponse, + ResponseTabErrorContainer, + ResponseTabErrorContent, + ResponseTabErrorDefaultMessage, + apiReactJsonProps, + responseTabComponent, +} from "components/editorComponents/ApiResponseView"; +import LogAdditionalInfo from "components/editorComponents/Debugger/ErrorLogs/components/LogAdditionalInfo"; +import LogHelper from "components/editorComponents/Debugger/ErrorLogs/components/LogHelper"; +import { JsonWrapper } from "components/editorComponents/Debugger/ErrorLogs/components/LogCollapseData"; +import ReactJson from "react-json-view"; +import EntityDeps from "components/editorComponents/Debugger/EntityDependecies"; +import type { ActionResponse } from "api/ActionAPI"; +import { getErrorAsString } from "sagas/ActionExecution/errorUtils"; +import { isString } from "lodash"; +import LOG_TYPE from "entities/AppsmithConsole/logtype"; +import type { SourceEntity } from "entities/AppsmithConsole"; +import { getUpdateTimestamp } from "components/editorComponents/Debugger/ErrorLogs/ErrorLogItem"; +import type { Action } from "entities/Action"; +import AnalyticsUtil from "utils/AnalyticsUtil"; +import { setActionResponseDisplayFormat } from "actions/pluginActionActions"; + +const HelpSection = styled.div``; + +const ResponseContentWrapper = styled.div<{ isError: boolean }>` + overflow-y: clip; + display: grid; + height: ${(props) => (props.isError ? "" : "100%")}; + + ${HelpSection} { + margin-bottom: 10px; + } +`; + +const ResultsCount = styled.div` + position: absolute; + right: ${(props) => props.theme.spaces[17] + 1}px; + top: 9px; + color: var(--ads-v2-color-fg); +`; + +export const TabbedViewContainer = styled.div` + ${ResizerCSS}; + height: ${ActionExecutionResizerHeight}px; + // Minimum height of bottom tabs as it can be resized + min-height: 36px; + width: 100%; + background-color: var(--ads-v2-color-bg); + border-top: 1px solid var(--ads-v2-color-border); +`; + +export const SegmentedControlContainer = styled.div` + padding: 0 var(--ads-v2-spaces-7); + padding-top: var(--ads-v2-spaces-4); + display: flex; + flex-direction: column; + gap: var(--ads-v2-spaces-4); + overflow-y: clip; + overflow-x: scroll; +`; + +interface QueryResponseViewProps { + actionSource: SourceEntity; + responseTabOnRunClick: () => void; + currentActionConfig?: Action; + isRunning: boolean; + actionName: string; // Check what and how to get + runErrorMessage?: string; + actionResponse?: ActionResponse; + responseDataTypes: { key: string; title: string }[]; + responseDisplayFormat: { title: string; value: string }; + isExecutePermitted: boolean; +} + +function QueryResponseView({ + actionName, + actionResponse, + actionSource, + currentActionConfig, + isExecutePermitted, + isRunning, + responseDataTypes, + responseDisplayFormat, + responseTabOnRunClick, + runErrorMessage, +}: QueryResponseViewProps) { + let output: Record[] | null = null; + + const responseBodyTabs = + responseDataTypes && + responseDataTypes.map((dataType, index) => { + return { + index: index, + key: dataType.key, + title: dataType.title, + panelComponent: responseTabComponent( + dataType.key, + output, + responsePaneHeight, + ), + }; + }); + + const segmentedControlOptions = + responseBodyTabs && + responseBodyTabs.map((item) => { + return { value: item.key, label: item.title }; + }); + + const panelRef = useRef(null); + const dispatch = useDispatch(); + const [selectedControl, setSelectedControl] = useState( + segmentedControlOptions[0]?.value, + ); + const selectedResponseTab = useSelector(getDebuggerSelectedTab); + const responsePaneHeight = useSelector(getResponsePaneHeight); + const errorCount = useSelector(getErrorCount); + + const onResponseTabSelect = (tabKey: string) => { + if (tabKey === DEBUGGER_TAB_KEYS.ERROR_TAB) { + AnalyticsUtil.logEvent("OPEN_DEBUGGER", { + source: "QUERY_PANE", + }); + } + dispatch( + setActionResponseDisplayFormat({ + id: currentActionConfig?.id || "", + field: "responseDisplayFormat", + value: tabKey, + }), + ); + }; + + let error = runErrorMessage; + let hintMessages: Array = []; + // Update request timestamp to human readable format. + const responseState = + actionResponse && getUpdateTimestamp(actionResponse.request); + + const selectedTabIndex = + responseDataTypes && + responseDataTypes.findIndex( + (dataType) => dataType.title === responseDisplayFormat?.title, + ); + + // Query is executed even once during the session, show the response data. + if (actionResponse) { + if (!actionResponse.isExecutionSuccess) { + // Pass the error to be shown in the error tab + error = actionResponse.readableError + ? getErrorAsString(actionResponse.readableError) + : getErrorAsString(actionResponse.body); + } else if (isString(actionResponse.body)) { + //reset error. + error = ""; + try { + // Try to parse response as JSON array to be displayed in the Response tab + output = JSON.parse(actionResponse.body); + } catch (e) { + // In case the string is not a JSON, wrap it in a response object + output = [ + { + response: actionResponse.body, + }, + ]; + } + } else { + //reset error. + error = ""; + output = actionResponse.body as any; + } + if (actionResponse.messages && actionResponse.messages.length) { + //reset error. + error = ""; + hintMessages = actionResponse.messages; + } + } + + const setQueryResponsePaneHeight = useCallback((height: number) => { + dispatch(setResponsePaneHeight(height)); + }, []); + + const onClose = () => dispatch(showDebugger(false)); + const setSelectedResponseTab = useCallback((tabKey: string) => { + dispatch(setDebuggerSelectedTab(tabKey)); + }, []); + + const responseTabs = [ + { + key: "response", + title: "Response", + panelComponent: ( + + {error && ( + + + + Your query failed to execute + {actionResponse && + (actionResponse.pluginErrorDetails || + actionResponse.body) && + ":"} + + {actionResponse && + (actionResponse.pluginErrorDetails ? ( + <> +
+ { + actionResponse.pluginErrorDetails + .downstreamErrorMessage + } +
+ {actionResponse.pluginErrorDetails + .downstreamErrorCode && ( + + )} + + ) : ( + actionResponse.body && ( +
+ {actionResponse.body} +
+ ) + ))} + +
+ {actionResponse && actionResponse.request && ( + e.stopPropagation()} + > + + + )} +
+ )} + {hintMessages && hintMessages.length > 0 && ( + + {hintMessages.map((msg, index) => ( + + {msg} + + ))} + + )} + {currentActionConfig && + output && + responseBodyTabs && + responseBodyTabs.length > 0 && + selectedTabIndex !== -1 && ( + + { + setSelectedControl(value); + onResponseTabSelect(value); + }} + options={segmentedControlOptions} + value={selectedControl} + /> + {responseTabComponent( + selectedControl || segmentedControlOptions[0]?.value, + output, + responsePaneHeight, + )} + + )} + {!output && !error && ( + + )} +
+ ), + }, + { + key: DEBUGGER_TAB_KEYS.ERROR_TAB, + title: createMessage(DEBUGGER_ERRORS), + count: errorCount, + panelComponent: , + }, + { + key: DEBUGGER_TAB_KEYS.LOGS_TAB, + title: createMessage(DEBUGGER_LOGS), + panelComponent: , + }, + { + key: DEBUGGER_TAB_KEYS.INSPECT_TAB, + title: createMessage(INSPECT_ENTITY), + panelComponent: , + }, + ]; + + return ( + + + setQueryResponsePaneHeight(height) + } + openResizer={isRunning} + panelRef={panelRef} + snapToHeight={ActionExecutionResizerHeight} + /> + {isRunning && ( + + )} + + {output && !!output.length && ( + + + Result: + {` ${output.length} Record${ + output.length > 1 ? "s" : "" + }`} + + + )} + + + + + ); +} + +export default QueryResponseView; diff --git a/app/client/src/pages/Editor/utils.ts b/app/client/src/pages/Editor/utils.ts index 359c629a86..84529bad59 100644 --- a/app/client/src/pages/Editor/utils.ts +++ b/app/client/src/pages/Editor/utils.ts @@ -18,6 +18,7 @@ import type { URLBuilderParams } from "@appsmith/entities/URLRedirect/URLAssembl import { useSelector } from "react-redux"; import { getCurrentPageId } from "selectors/editorSelectors"; import type { WidgetCardProps } from "widgets/BaseWidget"; +import type { ActionResponse } from "api/ActionAPI"; export const draggableElement = ( id: string, @@ -303,3 +304,35 @@ export const groupWidgetCardsByTags = (widgetCards: WidgetCardProps[]) => { export const transformTextToSentenceCase = (s: string) => { return s.slice(0, 1).toUpperCase() + s.slice(1).toLowerCase(); }; + +export const actionResponseDisplayDataFormats = ( + actionData?: ActionResponse, + defaultDisplayFormat: { title: string; value: string } = { + title: "", + value: "", + }, +) => { + let responseDisplayFormat: { title: string; value: string }; + let responseDataTypes: { key: string; title: string }[]; + + if (actionData && actionData.responseDisplayFormat) { + responseDataTypes = actionData.dataTypes.map((data) => { + return { + key: data.dataType, + title: data.dataType, + }; + }); + responseDisplayFormat = { + title: actionData.responseDisplayFormat, + value: actionData.responseDisplayFormat, + }; + } else { + responseDataTypes = []; + responseDisplayFormat = defaultDisplayFormat; + } + + return { + responseDataTypes, + responseDisplayFormat, + }; +}; diff --git a/app/client/src/sagas/ActionExecution/PluginActionSaga.ts b/app/client/src/sagas/ActionExecution/PluginActionSaga.ts index 7682124190..a570663044 100644 --- a/app/client/src/sagas/ActionExecution/PluginActionSaga.ts +++ b/app/client/src/sagas/ActionExecution/PluginActionSaga.ts @@ -161,6 +161,7 @@ import { getCurrentEnvironmentName, } from "@appsmith/selectors/environmentSelectors"; import { EVAL_WORKER_ACTIONS } from "@appsmith/workers/Evaluation/evalWorkerActions"; +import { getIsActionCreatedInApp } from "@appsmith/utils/getIsActionCreatedInApp"; enum ActionResponseDataTypes { BINARY = "BINARY", @@ -560,7 +561,7 @@ export default function* executePluginActionTriggerSaga( }); const executePluginActionResponse: ExecutePluginActionResponse = yield call( executePluginActionSaga, - action.id, + action, pagination, params, ); @@ -733,11 +734,12 @@ interface RunActionError { clientDefinedError?: boolean; } -function* runActionSaga( +export function* runActionSaga( reduxAction: ReduxAction<{ id: string; - paginationField: PaginationField; + paginationField?: PaginationField; skipOpeningDebugger: boolean; + action?: Action; }>, ) { const actionId = reduxAction.payload.id; @@ -754,10 +756,12 @@ function* runActionSaga( const currentEnvDetails: { id: string; name: string } = yield select( getCurrentEnvironmentDetails, ); - const actionObject = shouldBeDefined( - yield select(getAction, actionId), - `action not found for id - ${actionId}`, - ); + const actionObject = + reduxAction.payload.action || + shouldBeDefined( + yield select(getAction, actionId), + `action not found for id - ${actionId}`, + ); const plugin: Plugin = yield select(getPlugin, actionObject?.pluginId); const datasource: Datasource = yield select( getDatasource, @@ -787,7 +791,7 @@ function* runActionSaga( }, }); - const { id, paginationField } = reduxAction.payload; + const { paginationField } = reduxAction.payload; // open response tab in debugger on exection of action. if (!reduxAction.payload.skipOpeningDebugger) { yield call(openDebugger); @@ -802,7 +806,7 @@ function* runActionSaga( try { const executePluginActionResponse: ExecutePluginActionResponse = yield call( executePluginActionSaga, - id, + actionObject, paginationField, {}, true, @@ -1119,7 +1123,7 @@ function* executePageLoadAction(pageAction: PageAction) { try { const executePluginActionResponse: ExecutePluginActionResponse = - yield call(executePluginActionSaga, pageAction); + yield call(executePluginActionSaga, action); payload = executePluginActionResponse.payload; isError = executePluginActionResponse.isError; } catch (e) { @@ -1293,24 +1297,12 @@ interface ExecutePluginActionResponse { * PluginActionExecutionError which needs to be handled by any saga that calls this. * */ function* executePluginActionSaga( - actionOrActionId: PageAction | string, + pluginAction: Action, paginationField?: PaginationField, params?: Record, isUserInitiated?: boolean, ) { - let pluginAction; - let actionId; - if (isString(actionOrActionId)) { - // @ts-expect-error: plugin Action can take many types - pluginAction = yield select(getAction, actionOrActionId); - actionId = actionOrActionId; - } else { - pluginAction = shouldBeDefined( - yield select(getAction, actionOrActionId.id), - `Action not found for id -> ${actionOrActionId.id}`, - ); - actionId = actionOrActionId.id; - } + const actionId = pluginAction.id; if (pluginAction.confirmBeforeExecute) { const modalPayload = { @@ -1392,6 +1384,7 @@ function* executePluginActionSaga( executePluginActionSuccess({ id: actionId, response: payload, + isActionCreatedInApp: getIsActionCreatedInApp(pluginAction), }), ); @@ -1454,6 +1447,7 @@ function* executePluginActionSaga( executePluginActionSuccess({ id: actionId, response: EMPTY_RESPONSE, + isActionCreatedInApp: getIsActionCreatedInApp(pluginAction), }), ); yield put( diff --git a/app/client/src/selectors/apiPaneSelectors.ts b/app/client/src/selectors/apiPaneSelectors.ts index 671a7f1d58..df0dd35505 100644 --- a/app/client/src/selectors/apiPaneSelectors.ts +++ b/app/client/src/selectors/apiPaneSelectors.ts @@ -15,3 +15,6 @@ export const getApiPaneConfigSelectedTabIndex = (state: AppState) => export const getApiRightPaneSelectedTab = (state: AppState) => state.ui.apiPane.selectedRightPaneTab; + +export const getIsRunning = (state: AppState, apiId: string) => + state.ui.apiPane.isRunning[apiId];