chore: Debugger Split states (#31043)

## Description

Creates local states for the debugger for Query Pane, Api Pane and JS
Pane and separates it from the main Canvas Debugger state. This is done
so that in Split pane, the states of Action Pane debugger can be
different from the Canvas Debugger state. To keep handling the
Fullscreen Debugger experience, a new hook `useDebuggerTriggerClick` is
introduced which opens the correct debugger based on the IDE state.

Also removes the Error and Logs from the Query / Api / JS Debuggers when
in split screen mode for a cleaner debugging experience

##### This change removes the expectation of having a common debugger
state that follows around as the user navigates in the IDE. Instead it
create a new debugger state per entity item. The tests have been updated
to reflect this



#### PR fixes following issue(s)
Fixes #30836
Fixes #30342


#### Media


#### Type of change
- Breaking change (fix or feature that would cause existing
functionality to not work as expected)

## Testing

#### How Has This Been Tested?

- [ ] Manual
- [ ] JUnit
- [ ] Jest
- [ ] Cypress
>
>
#### Test Plan
> Add Testsmith test cases links that relate to this PR
>
>
#### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
>
>
>
## Checklist:
#### Dev activity
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag


#### QA activity:
- [ ] [Speedbreak
features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-)
have been covered
- [ ] Test plan covers all impacted features and [areas of
interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-)
- [ ] Test plan has been peer reviewed by project stakeholders and other
QA members
- [ ] Manually tested functionality on DP
- [ ] We had an implementation alignment call with stakeholders post QA
Round 2
- [ ] Cypress test cases have been added and approved by SDET/manual QA
- [ ] Added `Test Plan Approved` label after Cypress tests were reviewed
- [ ] Added `Test Plan Approved` label after JUnit tests were reviewed
This commit is contained in:
Hetu Nandu 2024-02-29 11:53:57 +05:30 committed by GitHub
parent 5c5ae36275
commit 1d4198048c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 696 additions and 484 deletions

View File

@ -31,7 +31,6 @@ describe(
debuggerHelper.ClicklogEntityLink();
agHelper.AssertElementVisibility(apiPage._nextCursorValue);
debuggerHelper.CloseBottomBar();
entityExplorer.ActionContextMenuByEntityName({
entityNameinLeftSidebar: "Api1",
entityType: entityItems.Api,
@ -51,11 +50,9 @@ describe(
apiPage.SelectPaneTab("Headers");
EditorNavigation.ShowCanvas();
debuggerHelper.AssertErrorCount(1);
debuggerHelper.ClickDebuggerIcon();
debuggerHelper.ClicklogEntityLink();
agHelper.AssertElementVisibility(apiPage._bodyValue(0));
debuggerHelper.CloseBottomBar();
entityExplorer.ActionContextMenuByEntityName({
entityNameinLeftSidebar: "Api2",
entityType: entityItems.Api,
@ -69,12 +66,11 @@ describe(
apiPage.SelectPaneTab("Pagination");
EditorNavigation.ShowCanvas();
debuggerHelper.ClickDebuggerIcon();
// The Debugger should already be open here as it was opened before
debuggerHelper.ClickLogsTab();
debuggerHelper.ClicklogEntityLink(true);
agHelper.AssertElementVisibility(dataSources._queryTimeout);
debuggerHelper.CloseBottomBar();
entityExplorer.ActionContextMenuByEntityName({
entityNameinLeftSidebar: "Api3",
entityType: entityItems.Api,

View File

@ -2,7 +2,7 @@ import * as _ from "../../../../support/Objects/ObjectsCore";
import { PageType } from "../../../../support/Pages/DebuggerHelper";
import EditorNavigation from "../../../../support/Pages/EditorNavigation";
describe("Entity bottom bar", () => {
describe("Entity bottom bar", { tags: ["@tag.Debugger"] }, () => {
it("1. Debugger should be closable", () => {
//Verify if bottom bar is closed.
_.debuggerHelper.AssertClosed();
@ -32,17 +32,14 @@ describe("Entity bottom bar", () => {
_.debuggerHelper.AssertSelectedTab("Response");
//verify if bottom bar is closed on switching to canvas page.
EditorNavigation.ShowCanvas();
_.debuggerHelper.AssertSelectedTab("Errors");
_.debuggerHelper.AssertClosed();
});
it("3. Api bottom pane should be collapsable", () => {
_.apiPage.CreateAndFillApi(
_.dataManager.dsValues[_.dataManager.defaultEnviorment].mockApiUrl,
);
//Verify that the errors tab is still open.
_.debuggerHelper.AssertSelectedTab("Errors");
//Verify if bottom bar is closed on clicking close icon in API page.
_.debuggerHelper.CloseBottomBar();
//Verify that the errors tab is still closed.
_.debuggerHelper.AssertClosed();
//Verify if bottom bar opens on clicking debugger icon in api page.
_.debuggerHelper.ClickDebuggerIcon();
@ -60,12 +57,11 @@ describe("Entity bottom bar", () => {
it("4. Bottom bar in Datasource", () => {
//Verify if bottom bar remain open on shifting to create new datasource page.
_.dataSources.NavigateToDSCreateNew();
//Expecting errors tab to be closed as previous selected tab was response.
//And response tab is not part of datasource page.
//Expecting errors tab to be closed as this is now a datasource
_.debuggerHelper.AssertClosed();
//Verify if bottom bar opens on clicking debugger icon in datasource page.
_.debuggerHelper.ClickDebuggerIcon();
_.debuggerHelper.AssertClosed();
_.debuggerHelper.AssertOpen(PageType.DataSources);
});
it(

View File

@ -14,7 +14,7 @@ const generateTestLogString = () => {
return logString;
};
describe("Debugger logs", function () {
describe("Debugger logs", { tags: ["@tag.Debugger"] }, function () {
this.beforeEach(() => {
logString = generateTestLogString();
});
@ -285,7 +285,8 @@ describe("Debugger logs", function () {
});
EditorNavigation.SelectEntityByName("Page1", EntityType.Page);
_.agHelper.GetNClick(_.locators._errorTab);
_.agHelper.AssertElementVisibility(".t--debugger-count");
_.debuggerHelper.ClickDebuggerIcon();
_.debuggerHelper.ClicklogEntityLink();

View File

@ -2,6 +2,7 @@ import type { ReduxAction } from "@appsmith/constants/ReduxActionConstants";
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
import type { EventLocation } from "@appsmith/utils/analyticsUtilTypes";
import type { SlashCommandPayload } from "entities/Action";
import type { ApiPaneDebuggerState } from "@appsmith/reducers/uiReducers/apiPaneReducer";
export const changeApi = (
id: string,
@ -14,40 +15,6 @@ export const changeApi = (
};
};
export const initApiPane = (urlId?: string): ReduxAction<{ id?: string }> => {
return {
type: ReduxActionTypes.INIT_API_PANE,
payload: { id: urlId },
};
};
export const setCurrentCategory = (
category: string,
): ReduxAction<{ category: string }> => {
return {
type: ReduxActionTypes.SET_CURRENT_CATEGORY,
payload: { category },
};
};
export const setLastUsedEditorPage = (
path: string,
): ReduxAction<{ path: string }> => {
return {
type: ReduxActionTypes.SET_LAST_USED_EDITOR_PAGE,
payload: { path },
};
};
export const setLastSelectedPage = (
selectedPageId: string,
): ReduxAction<{ selectedPageId: string }> => {
return {
type: ReduxActionTypes.SET_LAST_SELECTED_PAGE_PAGE,
payload: { selectedPageId },
};
};
export const createNewApiAction = (
pageId: string,
from: EventLocation,
@ -109,3 +76,10 @@ export const setApiRightPaneSelectedTab: (
type: ReduxActionTypes.SET_API_RIGHT_PANE_SELECTED_TAB,
payload: { selectedTab: payload },
});
export const setApiPaneDebuggerState = (
payload: Partial<ApiPaneDebuggerState>,
) => ({
type: ReduxActionTypes.SET_API_PANE_DEBUGGER_STATE,
payload,
});

View File

@ -1,7 +1,10 @@
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
import type { Log, Message, SourceEntity } from "entities/AppsmithConsole";
import type { ENTITY_TYPE } from "@appsmith/entities/AppsmithConsole/utils";
import type { DebuggerContext } from "reducers/uiReducers/debuggerReducer";
import type {
CanvasDebuggerState,
DebuggerContext,
} from "reducers/uiReducers/debuggerReducer";
import type { EventName } from "@appsmith/utils/analyticsUtilTypes";
import type { APP_MODE } from "entities/App";
@ -126,3 +129,12 @@ export const setDebuggerContext = (context: DebuggerContext) => {
payload: { context },
};
};
export const setCanvasDebuggerState = (
payload: Partial<CanvasDebuggerState>,
) => {
return {
type: ReduxActionTypes.SET_CANVAS_DEBUGGER_STATE,
payload,
};
};

View File

@ -6,7 +6,10 @@ import type {
SetFunctionPropertyPayload,
} from "@appsmith/api/JSActionAPI";
import type { EventLocation } from "@appsmith/utils/analyticsUtilTypes";
import type { JSEditorTab } from "reducers/uiReducers/jsPaneReducer";
import type {
JSEditorTab,
JSPaneDebuggerState,
} from "reducers/uiReducers/jsPaneReducer";
export const createNewJSCollection = (
pageId: string,
@ -111,3 +114,10 @@ export const setJsPaneConfigSelectedTab: (
type: ReduxActionTypes.SET_JS_PANE_CONFIG_SELECTED_TAB,
payload: { selectedTab: payload },
});
export const setJsPaneDebuggerState = (
payload: Partial<JSPaneDebuggerState>,
) => ({
type: ReduxActionTypes.SET_JS_PANE_DEBUGGER_STATE,
payload,
});

View File

@ -1,6 +1,7 @@
import type { ReduxAction } from "@appsmith/constants/ReduxActionConstants";
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
import type { Action } from "entities/Action";
import type { QueryPaneDebuggerState } from "@appsmith/reducers/uiReducers/queryPaneReducer";
export interface ChangeQueryPayload {
id: string;
@ -26,3 +27,12 @@ export const setQueryPaneConfigSelectedTabIndex: (
type: ReduxActionTypes.SET_QUERY_PANE_CONFIG_SELECTED_TAB,
payload: { selectedTabIndex: payload },
});
export const setQueryPaneDebuggerState = (
payload: Partial<QueryPaneDebuggerState>,
) => {
return {
type: ReduxActionTypes.SET_QUERY_PANE_DEBUGGER_STATE,
payload,
};
};

View File

@ -463,7 +463,6 @@ const ActionTypes = {
FETCH_ALL_PUBLISHED_PAGES: "FETCH_ALL_PUBLISHED_PAGES",
CREATE_NEW_API_ACTION: "CREATE_NEW_API_ACTION",
CREATE_NEW_QUERY_ACTION: "CREATE_NEW_QUERY_ACTION",
SET_CURRENT_CATEGORY: "SET_CURRENT_CATEGORY",
SET_LAST_USED_EDITOR_PAGE: "SET_LAST_USED_EDITOR_PAGE",
SET_LAST_SELECTED_PAGE_PAGE: "SET_LAST_SELECTED_PAGE_PAGE",
FETCH_PROVIDER_DETAILS_BY_PROVIDER_ID_INIT:
@ -911,6 +910,10 @@ const ActionTypes = {
END_CONSOLIDATED_PAGE_LOAD: "END_CONSOLIDATED_PAGE_LOAD",
FETCH_ENVIRONMENT_SUCCESS: "FETCH_ENVIRONMENT_SUCCESS",
SET_SHOW_CREATE_NEW_MODAL: "SET_SHOW_CREATE_NEW_MODAL",
SET_QUERY_PANE_DEBUGGER_STATE: "SET_QUERY_PANE_DEBUGGER_STATE",
SET_API_PANE_DEBUGGER_STATE: "SET_API_PANE_DEBUGGER_STATE",
SET_JS_PANE_DEBUGGER_STATE: "SET_JS_PANE_DEBUGGER_STATE",
SET_CANVAS_DEBUGGER_STATE: "SET_CANVAS_DEBUGGER_STATE",
};
export const ReduxActionTypes = {

View File

@ -1,5 +1,6 @@
import {
setApiPaneConfigSelectedTabIndex,
setApiPaneDebuggerState,
setApiRightPaneSelectedTab,
} from "actions/apiPaneActions";
import {
@ -13,6 +14,7 @@ import {
} from "actions/editorContextActions";
import {
getApiPaneConfigSelectedTabIndex,
getApiPaneDebuggerState,
getApiRightPaneSelectedTab,
} from "selectors/apiPaneSelectors";
import {
@ -33,14 +35,20 @@ import {
import { setDatasourceViewMode } from "actions/datasourceActions";
import { updateExplorerWidthAction } from "actions/explorerActions";
import { setJsPaneConfigSelectedTab } from "actions/jsPaneActions";
import {
setJsPaneConfigSelectedTab,
setJsPaneDebuggerState,
} from "actions/jsPaneActions";
import {
setAllPropertySectionState,
setFocusablePropertyPaneField,
setPropertyPaneWidthAction,
setSelectedPropertyPanels,
} from "actions/propertyPaneActions";
import { setQueryPaneConfigSelectedTabIndex } from "actions/queryPaneActions";
import {
setQueryPaneConfigSelectedTabIndex,
setQueryPaneDebuggerState,
} from "actions/queryPaneActions";
import { selectWidgetInitAction } from "actions/widgetSelectionActions";
import {
DEFAULT_ENTITY_EXPLORER_WIDTH,
@ -53,6 +61,7 @@ import { getExplorerWidth } from "selectors/explorerSelector";
import {
getFirstJSObject,
getJSPaneConfigSelectedTab,
getJsPaneDebuggerState,
} from "selectors/jsPaneSelectors";
import {
getFocusablePropertyPaneField,
@ -62,6 +71,7 @@ import {
import {
getFirstQuery,
getQueryPaneConfigSelectedTabIndex,
getQueryPaneDebuggerState,
} from "selectors/queryPaneSelectors";
import { getDebuggerContext } from "selectors/debuggerSelectors";
import { setDebuggerContext } from "actions/debuggerActions";
@ -83,6 +93,7 @@ import { FocusElement, FocusElementConfigType } from "navigation/FocusElements";
import type { FocusElementsConfigList } from "sagas/FocusRetentionSaga";
import { getJSTabs, getQueryTabs } from "selectors/ideSelectors";
import { setJSTabs, setQueryTabs } from "actions/ideActions";
import { ActionExecutionResizerHeight } from "pages/Editor/APIEditor/constants";
export const AppIDEFocusElements: FocusElementsConfigList = {
[FocusEntity.DATASOURCE_LIST]: [
@ -117,6 +128,17 @@ export const AppIDEFocusElements: FocusElementsConfigList = {
setter: setJsPaneConfigSelectedTab,
defaultValue: JSEditorTab.CODE,
},
{
type: FocusElementConfigType.Redux,
name: FocusElement.JSDebugger,
selector: getJsPaneDebuggerState,
setter: setJsPaneDebuggerState,
defaultValue: {
open: false,
responseTabHeight: ActionExecutionResizerHeight,
selectedTab: undefined,
},
},
],
[FocusEntity.QUERY]: [
{
@ -156,6 +178,28 @@ export const AppIDEFocusElements: FocusElementsConfigList = {
selector: getApiRightPaneSelectedTab,
setter: setApiRightPaneSelectedTab,
},
{
type: FocusElementConfigType.Redux,
name: FocusElement.QueryDebugger,
selector: getQueryPaneDebuggerState,
setter: setQueryPaneDebuggerState,
defaultValue: {
open: false,
responseTabHeight: ActionExecutionResizerHeight,
selectedTab: undefined,
},
},
{
type: FocusElementConfigType.Redux,
name: FocusElement.ApiDebugger,
selector: getApiPaneDebuggerState,
setter: setApiPaneDebuggerState,
defaultValue: {
open: false,
responseTabHeight: ActionExecutionResizerHeight,
selectedTab: undefined,
},
},
],
[FocusEntity.PROPERTY_PANE]: [
{

View File

@ -6,32 +6,38 @@ import {
} from "@appsmith/constants/ReduxActionConstants";
import type { Action } from "entities/Action";
import type { UpdateActionPropertyActionPayload } from "actions/pluginActionActions";
import { ActionExecutionResizerHeight } from "pages/Editor/APIEditor/constants";
export const initialState: ApiPaneReduxState = {
isCreating: false,
isFetching: false,
isRunning: {},
isSaving: {},
isDeleting: {},
isDirty: {},
currentCategory: "",
extraformData: {},
selectedConfigTabIndex: 0,
selectedResponseTab: "",
debugger: {
open: false,
responseTabHeight: ActionExecutionResizerHeight,
},
};
export interface ApiPaneDebuggerState {
open: boolean;
responseTabHeight: number;
selectedTab?: string;
}
export interface ApiPaneReduxState {
isCreating: boolean; // RR
isFetching: boolean; // RR
isRunning: Record<string, boolean>;
isSaving: Record<string, boolean>; // RN
isDeleting: Record<string, boolean>;
isDirty: Record<string, boolean>;
currentCategory: string;
extraformData: Record<string, any>;
selectedConfigTabIndex: number;
selectedResponseTab: string;
selectedRightPaneTab?: string;
debugger: ApiPaneDebuggerState;
}
export const handlers = {
@ -74,6 +80,10 @@ export const handlers = {
...state.isRunning,
[action.payload.id]: true,
},
debugger: {
...state.debugger,
open: true,
},
}),
[ReduxActionTypes.RUN_ACTION_SUCCESS]: (
state: ApiPaneReduxState,
@ -183,14 +193,6 @@ export const handlers = {
},
}),
[ReduxActionTypes.SET_CURRENT_CATEGORY]: (
state: ApiPaneReduxState,
action: ReduxAction<{ category: string }>,
) => ({
...state,
currentCategory: action.payload.category,
}),
/**
* This redux action sets the extraformData field for an action. This is used to select the
* appropriate body type tab selection in the Rest API plugin based on the content-type.
@ -233,6 +235,18 @@ export const handlers = {
selectedRightPaneTab: selectedTab,
};
},
[ReduxActionTypes.SET_API_PANE_DEBUGGER_STATE]: (
state: ApiPaneReduxState,
action: ReduxAction<Partial<ApiPaneDebuggerState>>,
) => {
return {
...state,
debugger: {
...state.debugger,
...action.payload,
},
};
},
};
const apiPaneReducer = createReducer(initialState, handlers);

View File

@ -8,29 +8,33 @@ import { omit } from "lodash";
import type { Action } from "entities/Action";
import type { ActionResponse } from "api/ActionAPI";
import { ActionExecutionResizerHeight } from "pages/Editor/APIEditor/constants";
import { DEBUGGER_TAB_KEYS } from "../../../components/editorComponents/Debugger/helpers";
export const initialState: QueryPaneReduxState = {
isFetching: false,
isCreating: false,
isRunning: {},
isSaving: {},
isDeleting: {},
runErrorMessage: {},
lastUsed: "", // NR
responseTabHeight: ActionExecutionResizerHeight,
debugger: {
open: false,
responseTabHeight: ActionExecutionResizerHeight,
},
selectedConfigTabIndex: "0",
};
export interface QueryPaneDebuggerState {
open: boolean;
responseTabHeight: number;
selectedTab?: string;
}
export interface QueryPaneReduxState {
isFetching: boolean; // RR
isRunning: Record<string, boolean>;
isSaving: Record<string, boolean>; // RR
isDeleting: Record<string, boolean>;
runErrorMessage: Record<string, string>;
lastUsed: string; // NR
isCreating: boolean; // RR
selectedConfigTabIndex: string;
responseTabHeight: number;
debugger: QueryPaneDebuggerState;
}
export const handlers = {
@ -122,13 +126,18 @@ export const handlers = {
[ReduxActionTypes.RUN_ACTION_REQUEST]: (
state: any,
action: ReduxAction<{ id: string }>,
) => {
): QueryPaneReduxState => {
return {
...state,
isRunning: {
...state.isRunning,
[action.payload.id]: true,
},
debugger: {
...state.debugger,
selectedTab: DEBUGGER_TAB_KEYS.RESPONSE_TAB,
open: true,
},
};
},
@ -187,6 +196,18 @@ export const handlers = {
selectedConfigTabIndex: selectedTabIndex,
};
},
[ReduxActionTypes.SET_QUERY_PANE_DEBUGGER_STATE]: (
state: QueryPaneReduxState,
action: ReduxAction<Partial<QueryPaneDebuggerState>>,
) => {
return {
...state,
debugger: {
...state.debugger,
...action.payload,
},
};
},
};
const queryPaneReducer = createReducer(initialState, handlers);

View File

@ -13,11 +13,10 @@ import { isArray, isEmpty, isString } from "lodash";
import {
CHECK_REQUEST_BODY,
createMessage,
DEBUGGER_ERRORS,
DEBUGGER_LOGS,
EMPTY_RESPONSE_FIRST_HALF,
EMPTY_RESPONSE_LAST_HALF,
INSPECT_ENTITY,
DEBUGGER_ERRORS,
} from "@appsmith/constants/messages";
import { Text as BlueprintText } from "@blueprintjs/core";
import { EditorTheme } from "./CodeEditor/EditorConfig";
@ -26,26 +25,17 @@ import DebuggerLogs from "./Debugger/DebuggerLogs";
import ErrorLogs from "./Debugger/Errors";
import Resizer, { ResizerCSS } from "./Debugger/Resizer";
import AnalyticsUtil from "utils/AnalyticsUtil";
import EntityDeps from "./Debugger/EntityDependecies";
import { Classes, TAB_MIN_HEIGHT, Text, TextType } from "design-system-old";
import { Button, Callout, Flex, SegmentedControl } from "design-system";
import type { BottomTab } from "./EntityBottomTabs";
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 { setActionResponseDisplayFormat } from "actions/pluginActionActions";
import { isHtml } from "./utils";
import {
getDebuggerSelectedTab,
getErrorCount,
getResponsePaneHeight,
} from "selectors/debuggerSelectors";
import { getErrorCount } from "selectors/debuggerSelectors";
import { ActionExecutionResizerHeight } from "pages/Editor/APIEditor/constants";
import {
setDebuggerSelectedTab,
setResponsePaneHeight,
showDebugger,
} from "actions/debuggerActions";
import LogAdditionalInfo from "./Debugger/ErrorLogs/components/LogAdditionalInfo";
import {
JsonWrapper,
@ -59,6 +49,10 @@ import ActionExecutionInProgressView from "./ActionExecutionInProgressView";
import { CloseDebugger } from "./Debugger/DebuggerTabs";
import { EMPTY_RESPONSE } from "./emptyResponse";
import BindDataButton from "../../pages/Editor/QueryEditor/BindDataButton";
import { setApiPaneDebuggerState } from "actions/apiPaneActions";
import { getApiPaneDebuggerState } from "selectors/apiPaneSelectors";
import { getIDEViewMode } from "selectors/ideSelectors";
import { EditorViewMode } from "@appsmith/entities/IDE/constants";
interface TextStyleProps {
accent: "primary" | "secondary" | "error";
@ -312,12 +306,19 @@ function ApiResponseView(props: Props) {
const panelRef: RefObject<HTMLDivElement> = useRef(null);
const dispatch = useDispatch();
const errorCount = useSelector(getErrorCount);
const { open, responseTabHeight, selectedTab } = useSelector(
getApiPaneDebuggerState,
);
const ideViewMode = useSelector(getIDEViewMode);
const onDebugClick = useCallback(() => {
AnalyticsUtil.logEvent("OPEN_DEBUGGER", {
source: "API",
});
dispatch(setDebuggerSelectedTab(DEBUGGER_TAB_KEYS.ERROR_TAB));
dispatch(
setApiPaneDebuggerState({ selectedTab: DEBUGGER_TAB_KEYS.ERROR_TAB }),
);
}, []);
const onRunClick = () => {
@ -380,7 +381,7 @@ function ApiResponseView(props: Props) {
panelComponent: responseTabComponent(
dataType.key,
actionResponse?.body,
responsePaneHeight,
responseTabHeight,
),
};
});
@ -401,8 +402,6 @@ function ApiResponseView(props: Props) {
(dataType) => dataType.title === responseDisplayFormat?.title,
);
// get the selected tab in the response pane.
const selectedResponseTab = useSelector(getDebuggerSelectedTab);
// update the selected tab in the response pane.
const updateSelectedResponseTab = useCallback((tabKey: string) => {
if (tabKey === DEBUGGER_TAB_KEYS.ERROR_TAB) {
@ -410,13 +409,12 @@ function ApiResponseView(props: Props) {
source: "API_PANE",
});
}
dispatch(setDebuggerSelectedTab(tabKey));
dispatch(setApiPaneDebuggerState({ selectedTab: tabKey }));
}, []);
// get the height of the response pane.
const responsePaneHeight = useSelector(getResponsePaneHeight);
// update the height of the response pane on resize.
const updateResponsePaneHeight = useCallback((height: number) => {
dispatch(setResponsePaneHeight(height));
dispatch(setApiPaneDebuggerState({ responseTabHeight: height }));
}, []);
// get request timestamp formatted to human readable format.
@ -427,7 +425,7 @@ function ApiResponseView(props: Props) {
name: currentActionConfig ? currentActionConfig.name : "API",
id: currentActionConfig?.id || "",
};
const tabs = [
const tabs: BottomTab[] = [
{
key: "response",
title: "Response",
@ -526,7 +524,7 @@ function ApiResponseView(props: Props) {
{responseTabComponent(
selectedControl || segmentedControlOptions[0]?.value,
actionResponse?.body,
responsePaneHeight,
responseTabHeight,
)}
</SegmentedControlContainer>
) : null}
@ -579,32 +577,34 @@ function ApiResponseView(props: Props) {
</ResponseTabWrapper>
),
},
{
key: DEBUGGER_TAB_KEYS.ERROR_TAB,
title: createMessage(DEBUGGER_ERRORS),
count: errorCount,
panelComponent: <ErrorLogs />,
},
{
key: DEBUGGER_TAB_KEYS.LOGS_TAB,
title: createMessage(DEBUGGER_LOGS),
panelComponent: <DebuggerLogs searchQuery={props.apiName} />,
},
{
key: DEBUGGER_TAB_KEYS.INSPECT_TAB,
title: createMessage(INSPECT_ENTITY),
panelComponent: <EntityDeps />,
},
];
if (ideViewMode === EditorViewMode.FullScreen) {
tabs.push(
{
key: DEBUGGER_TAB_KEYS.ERROR_TAB,
title: createMessage(DEBUGGER_ERRORS),
count: errorCount,
panelComponent: <ErrorLogs />,
},
{
key: DEBUGGER_TAB_KEYS.LOGS_TAB,
title: createMessage(DEBUGGER_LOGS),
panelComponent: <DebuggerLogs searchQuery={props.apiName} />,
},
);
}
// close the debugger
//TODO: move this to a common place
const onClose = () => dispatch(showDebugger(false));
const onClose = () => dispatch(setApiPaneDebuggerState({ open: false }));
if (!open) return null;
return (
<ResponseContainer className="t--api-bottom-pane-container" ref={panelRef}>
<Resizer
initialHeight={responsePaneHeight}
initialHeight={responseTabHeight}
onResizeComplete={(height: number) => {
updateResponsePaneHeight(height);
}}
@ -659,7 +659,7 @@ function ApiResponseView(props: Props) {
<EntityBottomTabs
expandedHeight={`${ActionExecutionResizerHeight}px`}
onSelect={updateSelectedResponseTab}
selectedTabKey={selectedResponseTab}
selectedTabKey={selectedTab || ""}
tabs={tabs}
/>
<CloseDebugger

View File

@ -8,11 +8,11 @@ import {
getDatasourceStructureById,
getIsFetchingDatasourceStructure,
} from "@appsmith/selectors/entitiesSelector";
import { getResponsePaneHeight } from "selectors/debuggerSelectors";
import DatasourceField from "pages/Editor/DatasourceInfo/DatasourceField";
import { find } from "lodash";
import type { AppState } from "@appsmith/reducers";
import RenderInterimDataState from "pages/Editor/DatasourceInfo/RenderInterimDataState";
import { getQueryPaneDebuggerState } from "selectors/queryPaneSelectors";
interface Props {
datasourceId: string;
@ -24,7 +24,7 @@ const Schema = (props: Props) => {
const datasourceStructure = useSelector((state) =>
getDatasourceStructureById(state, props.datasourceId),
);
const responsePaneHeight = useSelector(getResponsePaneHeight);
const { responseTabHeight } = useSelector(getQueryPaneDebuggerState);
const [selectedTable, setSelectedTable] = useState<string>();
const selectedTableItems = find(datasourceStructure?.tables, [
@ -69,7 +69,7 @@ const Schema = (props: Props) => {
<Flex
flexDirection="row"
gap="spaces-2"
height={`${responsePaneHeight - 45}px`}
height={`${responseTabHeight - 45}px`}
maxWidth="70rem"
overflow="hidden"
>
@ -94,7 +94,7 @@ const Schema = (props: Props) => {
borderLeft="1px solid var(--ads-v2-color-border)"
flex="1"
flexDirection="column"
height={`${responsePaneHeight - 45}px`}
height={`${responseTabHeight - 45}px`}
justifyContent={
isLoading || columns.length === 0 ? "center" : "flex-start"
}

View File

@ -0,0 +1,96 @@
import { useLocation } from "react-router";
import { DEBUGGER_TAB_KEYS } from "../helpers";
import { setCanvasDebuggerState } from "actions/debuggerActions";
import AnalyticsUtil from "utils/AnalyticsUtil";
import type { FocusEntityInfo } from "navigation/FocusEntity";
import { FocusEntity, identifyEntityFromPath } from "navigation/FocusEntity";
import { setJsPaneDebuggerState } from "actions/jsPaneActions";
import { setApiPaneDebuggerState } from "actions/apiPaneActions";
import { setQueryPaneDebuggerState } from "actions/queryPaneActions";
import { getJsPaneDebuggerState } from "selectors/jsPaneSelectors";
import { getApiPaneDebuggerState } from "selectors/apiPaneSelectors";
import { getQueryPaneDebuggerState } from "selectors/queryPaneSelectors";
import { getCanvasDebuggerState } from "selectors/debuggerSelectors";
import { getIDEViewMode } from "selectors/ideSelectors";
import { useDispatch, useSelector } from "react-redux";
import { EditorViewMode } from "@appsmith/entities/IDE/constants";
import type { ReduxAction } from "@appsmith/constants/ReduxActionConstants";
import type { CanvasDebuggerState } from "reducers/uiReducers/debuggerReducer";
import type { AppState } from "@appsmith/reducers";
interface Config {
set: (
payload: Partial<CanvasDebuggerState>,
) => ReduxAction<Partial<CanvasDebuggerState>>;
get: (state: AppState) => CanvasDebuggerState;
}
const canvasDebuggerConfig: Config = {
set: setCanvasDebuggerState,
get: getCanvasDebuggerState,
};
const queryDebuggerConfig: Config = {
set: setQueryPaneDebuggerState,
get: getQueryPaneDebuggerState,
};
const getConfig = (focusInfo: FocusEntityInfo): Config => {
switch (focusInfo.entity) {
case FocusEntity.QUERY:
if (focusInfo.params.apiId) {
if (focusInfo.params.pluginPackageName) {
return queryDebuggerConfig;
}
return {
set: setApiPaneDebuggerState,
get: getApiPaneDebuggerState,
};
}
return queryDebuggerConfig;
case FocusEntity.JS_OBJECT:
return {
set: setJsPaneDebuggerState,
get: getJsPaneDebuggerState,
};
default:
return canvasDebuggerConfig;
}
};
const useDebuggerTriggerClick = () => {
const location = useLocation();
const currentFocus = identifyEntityFromPath(location.pathname);
const ideState = useSelector(getIDEViewMode);
const dispatch = useDispatch();
const config =
ideState === EditorViewMode.FullScreen
? getConfig(currentFocus)
: canvasDebuggerConfig;
const state = useSelector(config.get);
return () => {
// If debugger is already open and selected tab is error tab then we will close debugger.
if (state.open && state.selectedTab === DEBUGGER_TAB_KEYS.ERROR_TAB) {
dispatch(config.set({ open: false }));
} else {
// If debugger is not open then we will open debugger and show error tab.
if (!state.open) {
dispatch(
config.set({ open: true, selectedTab: DEBUGGER_TAB_KEYS.ERROR_TAB }),
);
}
// Select error tab if debugger is open and selected tab is not error tab.
// And also when we are opening debugger.
dispatch(config.set({ selectedTab: DEBUGGER_TAB_KEYS.ERROR_TAB }));
}
if (!state.open) {
AnalyticsUtil.logEvent("OPEN_DEBUGGER", {
source: "TRIGGER",
});
}
};
};
export default useDebuggerTriggerClick;

View File

@ -1,20 +1,10 @@
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import DebuggerTabs from "./DebuggerTabs";
import {
setDebuggerSelectedTab,
setErrorCount,
showDebugger as showDebuggerAction,
} from "actions/debuggerActions";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { stopEventPropagation } from "utils/AppsmithUtils";
import {
getDebuggerSelectedTab,
getMessageCount,
showDebuggerFlag,
} from "selectors/debuggerSelectors";
import { DEBUGGER_TAB_KEYS } from "./helpers";
import { setErrorCount } from "actions/debuggerActions";
import { getMessageCount, showDebuggerFlag } from "selectors/debuggerSelectors";
import { Button, Tooltip } from "design-system";
import useDebuggerTriggerClick from "./hooks/useDebuggerTriggerClick";
function Debugger() {
// Debugger render flag
@ -25,33 +15,13 @@ function Debugger() {
export function DebuggerTrigger() {
const dispatch = useDispatch();
const showDebugger = useSelector(showDebuggerFlag);
const selectedTab = useSelector(getDebuggerSelectedTab);
const messageCounters = useSelector(getMessageCount);
useEffect(() => {
dispatch(setErrorCount(messageCounters.errors));
});
const onClick = (e: any) => {
// If debugger is already open and selected tab is error tab then we will close debugger.
if (showDebugger && selectedTab === DEBUGGER_TAB_KEYS.ERROR_TAB) {
dispatch(showDebuggerAction(false));
} else {
// If debugger is not open then we will open debugger and show error tab.
if (!showDebugger) {
dispatch(showDebuggerAction(true));
}
// Select error tab if debugger is open and selected tab is not error tab.
// And also when we are opening debugger.
dispatch(setDebuggerSelectedTab(DEBUGGER_TAB_KEYS.ERROR_TAB));
}
if (!showDebugger)
AnalyticsUtil.logEvent("OPEN_DEBUGGER", {
source: "CANVAS",
});
stopEventPropagation(e);
};
const onClick = useDebuggerTriggerClick();
//tooltip will always show error count as we are opening error tab on click of debugger.
const tooltipContent =

View File

@ -1,5 +1,5 @@
import type { RefObject } from "react";
import React, { useEffect, useRef, useCallback, useState } from "react";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { connect, useDispatch, useSelector } from "react-redux";
import type { RouteComponentProps } from "react-router";
import { withRouter } from "react-router";
@ -9,15 +9,14 @@ import type { AppState } from "@appsmith/reducers";
import type { JSEditorRouteParams } from "constants/routes";
import {
createMessage,
DEBUGGER_LOGS,
DEBUGGER_ERRORS,
DEBUGGER_LOGS,
EXECUTING_FUNCTION,
NO_JS_FUNCTION_RETURN_VALUE,
UPDATING_JS_COLLECTION,
} from "@appsmith/constants/messages";
import type { EditorTheme } from "./CodeEditor/EditorConfig";
import DebuggerLogs from "./Debugger/DebuggerLogs";
import ErrorLogs from "./Debugger/Errors";
import Resizer, { ResizerCSS } from "./Debugger/Resizer";
import type { JSAction } from "entities/JSCollection";
import ReadOnlyEditor from "components/editorComponents/ReadOnlyEditor";
@ -26,22 +25,14 @@ import LoadingOverlayScreen from "components/editorComponents/LoadingOverlayScre
import type { JSCollectionData } from "@appsmith/reducers/entityReducers/jsActionsReducer";
import type { EvaluationError } from "utils/DynamicBindingUtils";
import { DEBUGGER_TAB_KEYS } from "./Debugger/helpers";
import type { BottomTab } from "./EntityBottomTabs";
import EntityBottomTabs from "./EntityBottomTabs";
import { TAB_MIN_HEIGHT } from "design-system-old";
import { CodeEditorWithGutterStyles } from "pages/Editor/JSEditor/constants";
import { getIsSavingEntity } from "selectors/editorSelectors";
import { getJSResponseViewState } from "./utils";
import {
getDebuggerSelectedTab,
getFilteredErrors,
getResponsePaneHeight,
} from "selectors/debuggerSelectors";
import { getFilteredErrors } from "selectors/debuggerSelectors";
import { ActionExecutionResizerHeight } from "pages/Editor/APIEditor/constants";
import {
setDebuggerSelectedTab,
setResponsePaneHeight,
showDebugger,
} from "actions/debuggerActions";
import {
NoResponse,
ResponseTabErrorContainer,
@ -52,6 +43,11 @@ import LOG_TYPE from "entities/AppsmithConsole/logtype";
import type { SourceEntity, Log } from "entities/AppsmithConsole";
import { ENTITY_TYPE } from "@appsmith/entities/AppsmithConsole/utils";
import { CloseDebugger } from "./Debugger/DebuggerTabs";
import { getJsPaneDebuggerState } from "selectors/jsPaneSelectors";
import { setJsPaneDebuggerState } from "actions/jsPaneActions";
import { getIDEViewMode } from "selectors/ideSelectors";
import { EditorViewMode } from "@appsmith/entities/IDE/constants";
import ErrorLogs from "./Debugger/Errors";
const ResponseContainer = styled.div`
${ResizerCSS};
@ -210,7 +206,10 @@ function JSResponseView(props: Props) {
}
}
} catch (e) {}
const tabs = [
const ideViewMode = useSelector(getIDEViewMode);
const tabs: BottomTab[] = [
{
key: "response",
title: "Response",
@ -279,12 +278,6 @@ function JSResponseView(props: Props) {
</>
),
},
{
key: DEBUGGER_TAB_KEYS.ERROR_TAB,
title: createMessage(DEBUGGER_ERRORS),
count: errorCount,
panelComponent: <ErrorLogs />,
},
{
key: DEBUGGER_TAB_KEYS.LOGS_TAB,
title: createMessage(DEBUGGER_LOGS),
@ -292,24 +285,34 @@ function JSResponseView(props: Props) {
},
];
if (ideViewMode === EditorViewMode.FullScreen) {
tabs.push({
key: DEBUGGER_TAB_KEYS.ERROR_TAB,
title: createMessage(DEBUGGER_ERRORS),
count: errorCount,
panelComponent: <ErrorLogs />,
});
}
// get the selected tab from the store.
const selectedResponseTab = useSelector(getDebuggerSelectedTab);
const { open, responseTabHeight, selectedTab } = useSelector(
getJsPaneDebuggerState,
);
// set the selected tab in the store.
const setSelectedResponseTab = useCallback((selectedTab: string) => {
dispatch(setDebuggerSelectedTab(selectedTab));
dispatch(setJsPaneDebuggerState({ selectedTab }));
}, []);
// get the height of the response pane.
const responseTabHeight = useSelector(getResponsePaneHeight);
// set the height of the response pane on resize.
const setResponseHeight = useCallback((height: number) => {
dispatch(setResponsePaneHeight(height));
dispatch(setJsPaneDebuggerState({ responseTabHeight: height }));
}, []);
// close the debugger
const onClose = () => dispatch(showDebugger(false));
const onClose = () => dispatch(setJsPaneDebuggerState({ open: false }));
// Do not render if header tab is selected in the bottom bar.
return !(selectedResponseTab === DEBUGGER_TAB_KEYS.HEADER_TAB) ? (
return open && selectedTab ? (
<ResponseContainer
className="t--js-editor-bottom-pane-container"
ref={panelRef}
@ -323,7 +326,7 @@ function JSResponseView(props: Props) {
<EntityBottomTabs
expandedHeight={`${ActionExecutionResizerHeight}px`}
onSelect={setSelectedResponseTab}
selectedTabKey={selectedResponseTab}
selectedTabKey={selectedTab}
tabs={tabs}
/>

View File

@ -26,6 +26,9 @@ export enum FocusElement {
SelectedJSObject = "SelectedJSObject",
SelectedSegment = "SelectedSegment",
IDETabs = "IDETabs",
QueryDebugger = "QueryDebugger",
ApiDebugger = "ApiDebugger",
JSDebugger = "JSDebugger",
}
export enum FocusElementConfigType {

View File

@ -50,7 +50,6 @@ import { replayHighlightClass } from "globalStyles/portals";
import { getPlugin } from "@appsmith/selectors/entitiesSelector";
import { setApiPaneConfigSelectedTabIndex } from "actions/apiPaneActions";
import type { AutoGeneratedHeader } from "./helpers";
import { showDebuggerFlag } from "selectors/debuggerSelectors";
import { getApiPaneConfigSelectedTabIndex } from "selectors/apiPaneSelectors";
import { noop } from "lodash";
import { DEFAULT_DATASOURCE_NAME } from "constants/ApiEditorConstants/ApiEditorConstants";
@ -573,9 +572,6 @@ function CommonEditorForm(props: CommonFormPropsWithExtraParams) {
actionDatasourceName === DEFAULT_DATASOURCE_NAME) ||
!isExecutePermitted;
// Debugger render flag
const showDebugger = useSelector(showDebuggerFlag);
const isGraphql = isGraphqlPlugin(plugin);
const theme = EditorTheme.LIGHT;
@ -732,19 +728,17 @@ function CommonEditorForm(props: CommonFormPropsWithExtraParams) {
</StyledTabPanel>
</Tabs>
</TabbedViewContainer>
{showDebugger && (
<ApiResponseView
actionResponse={actionResponse}
apiName={actionName}
currentActionConfig={currentActionConfig}
disabled={!isExecutePermitted}
isRunning={isRunning}
onRunClick={onRunClick}
responseDataTypes={responseDataTypes}
responseDisplayFormat={responseDisplayFormat}
theme={theme}
/>
)}
<ApiResponseView
actionResponse={actionResponse}
apiName={actionName}
currentActionConfig={currentActionConfig}
disabled={!isExecutePermitted}
isRunning={isRunning}
onRunClick={onRunClick}
responseDataTypes={responseDataTypes}
responseDisplayFormat={responseDisplayFormat}
theme={theme}
/>
</SecondaryWrapper>
<ActionRightPane
actionRightPaneBackLink={actionRightPaneBackLink}

View File

@ -1,6 +1,6 @@
import { executeDatasourceQuery } from "actions/datasourceActions";
import type { Datasource, QueryTemplate } from "entities/Datasource";
import { useState, useCallback } from "react";
import { useCallback, useState } from "react";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { useDispatch, useSelector } from "react-redux";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";

View File

@ -1,4 +1,4 @@
import React, { memo, useEffect, useMemo, useRef, useState } from "react";
import React, { memo, useMemo, useRef, useState } from "react";
import Entity, { EntityClassNames } from "../Explorer/Entity";
import { datasourceTableIcon } from "../Explorer/ExplorerIcons";
import QueryTemplates from "./QueryTemplates";
@ -22,7 +22,6 @@ import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
import { hasCreateDSActionPermissionInApp } from "@appsmith/utils/BusinessFeatures/permissionPageHelpers";
import { useEditorType } from "@appsmith/hooks";
import history from "utils/history";
import ResizeObserver from "resize-observer-polyfill";
interface DatasourceStructureItemProps {
dbStructure: DatasourceTable;
@ -45,6 +44,7 @@ const StructureWrapper = styled.div`
display: flex;
flex-direction: column;
flex-grow: 1;
height: 100%;
`;
const DatasourceStructureItem = memo((props: DatasourceStructureItemProps) => {
@ -160,33 +160,6 @@ type DatasourceStructureProps = Partial<DatasourceStructureItemProps> & {
};
const DatasourceStructure = (props: DatasourceStructureProps) => {
const [containerHeight, setContainerHeight] = useState<number>();
const containerRef = useRef<HTMLDivElement>(null);
const updateContainerHeight = () => {
if (containerRef.current?.offsetHeight) {
setContainerHeight(containerRef.current?.offsetHeight);
}
};
const resizeObserver = useRef(
new ResizeObserver(() => {
updateContainerHeight();
}),
);
useEffect(() => {
updateContainerHeight();
if (containerRef.current) {
resizeObserver.current.observe(containerRef.current);
}
return () => {
if (containerRef.current) {
resizeObserver.current.unobserve(containerRef.current);
}
};
}, []);
const Row = (index: number) => {
const structure = props.tables[index];
@ -201,15 +174,12 @@ const DatasourceStructure = (props: DatasourceStructureProps) => {
};
return (
<StructureWrapper ref={containerRef}>
{containerHeight && (
<Virtuoso
className="t--schema-virtuoso-container"
itemContent={Row}
style={{ height: `${containerHeight}px` }}
totalCount={props.tables.length}
/>
)}
<StructureWrapper>
<Virtuoso
className="t--schema-virtuoso-container"
itemContent={Row}
totalCount={props.tables.length}
/>
</StructureWrapper>
);
};

View File

@ -2,8 +2,6 @@ import React, { useEffect, useRef, useState } from "react";
import styled from "styled-components";
import { ResizerCSS } from "components/editorComponents/Debugger/Resizer";
import Resizable from "components/editorComponents/Debugger/Resizer";
import { useSelector } from "react-redux";
import { getResponsePaneHeight } from "selectors/debuggerSelectors";
import { ActionExecutionResizerHeight } from "pages/Editor/APIEditor/constants";
import type { BottomTab } from "components/editorComponents/EntityBottomTabs";
import EntityBottomTabs from "components/editorComponents/EntityBottomTabs";
@ -23,11 +21,11 @@ interface Props {
onResizeComplete: (height: number) => void;
isRunning: boolean;
tabs: BottomTab[];
height?: number;
}
const ActionDrawer = (props: Props) => {
const panelRef = useRef<HTMLDivElement>(null);
const responsePaneHeight = useSelector(getResponsePaneHeight);
const [selectedTab, setSelectedTab] = useState<string>("");
useEffect(() => {
if (props.tabs.length) {
@ -38,7 +36,7 @@ const ActionDrawer = (props: Props) => {
return (
<Container ref={panelRef}>
<Resizable
initialHeight={responsePaneHeight}
initialHeight={props.height || ActionExecutionResizerHeight}
onResizeComplete={props.onResizeComplete}
openResizer={props.isRunning}
panelRef={panelRef}

View File

@ -2,7 +2,6 @@ import { Flex } from "design-system";
import React, { useCallback, useState } from "react";
import ActionToolbar from "./ActionToolbar";
import ActionDrawer from "./ActionDrawer";
import { setResponsePaneHeight } from "actions/debuggerActions";
import { useDispatch } from "react-redux";
import type { BottomTab } from "components/editorComponents/EntityBottomTabs";
@ -14,12 +13,16 @@ interface Props {
tabs: BottomTab[];
runOptionsSelector?: React.ReactNode;
settingsRender: React.ReactNode;
height?: number;
onSetHeight?: (height: number) => void;
}
const ActionEditor = (props: Props) => {
const dispatch = useDispatch();
const setDrawerHeight = useCallback((height: number) => {
dispatch(setResponsePaneHeight(height));
if (props.onSetHeight) {
dispatch(props.onSetHeight(height));
}
}, []);
const [settingsOpen, setSettingsOpen] = useState(false);
@ -42,6 +45,7 @@ const ActionEditor = (props: Props) => {
/>
</Flex>
<ActionDrawer
height={props.height}
isRunning={props.isRunning}
onResizeComplete={setDrawerHeight}
tabs={props.tabs}

View File

@ -24,7 +24,7 @@ import NewApiScreen from "./NewApi";
import NewQueryScreen from "./NewQuery";
import { isAirgapped } from "@appsmith/utils/airgapHelpers";
import history from "utils/history";
import { showDebuggerFlag } from "../../../selectors/debuggerSelectors";
import { showDebuggerFlag } from "selectors/debuggerSelectors";
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
import {
createMessage,
@ -40,6 +40,7 @@ import {
import { useEditorType } from "@appsmith/hooks";
import { useParentEntityInfo } from "@appsmith/hooks/datasourceEditorHooks";
import AIDataSources from "./AIDataSources";
import Debugger from "../DataSourceEditor/Debugger";
const NewIntegrationsContainer = styled.div`
${thinScrollbar};
@ -280,6 +281,7 @@ class CreateNewDatasourceTab extends React.Component<
isEnabledForCreateNew,
isOnboardingScreen,
pageId,
showDebugger,
} = this.props;
if (!canCreateDatasource) return null;
const mockDataSection =
@ -290,68 +292,72 @@ class CreateNewDatasourceTab extends React.Component<
/>
) : null;
return (
<NewIntegrationsContainer className="p-4" id="new-integrations-wrapper">
{dataSources.length === 0 && <AddDatasourceSecurely />}
{dataSources.length === 0 && this.props.mockDatasources.length > 0 && (
<>
{mockDataSection}
<StyledDivider />
</>
)}
{isEnabledForCreateNew && (
<>
<CreateNewDatasource
active={false}
history={history}
isCreating={isCreating}
isOnboardingScreen={!!isOnboardingScreen}
location={location}
pageId={pageId}
showMostPopularPlugins
showUnsupportedPluginDialog={this.showUnsupportedPluginDialog}
/>
<StyledDivider />
</>
)}
<CreateNewAPI
active={false}
history={history}
isCreating={isCreating}
isOnboardingScreen={!!isOnboardingScreen}
location={location}
pageId={pageId}
showUnsupportedPluginDialog={this.showUnsupportedPluginDialog}
/>
<StyledDivider />
<CreateNewDatasource
active={false}
history={history}
isCreating={isCreating}
location={location}
pageId={pageId}
showUnsupportedPluginDialog={this.showUnsupportedPluginDialog}
/>
<CreateNewSaasIntegration
active={false}
history={history}
isCreating={isCreating}
location={location}
pageId={pageId}
showUnsupportedPluginDialog={this.showUnsupportedPluginDialog}
/>
<CreateNewAIIntegration
history={history}
isCreating={isCreating}
pageId={pageId}
showUnsupportedPluginDialog={this.showUnsupportedPluginDialog}
/>
{dataSources.length > 0 && this.props.mockDatasources.length > 0 && (
<>
<StyledDivider />
{mockDataSection}
</>
)}
</NewIntegrationsContainer>
<>
<NewIntegrationsContainer className="p-4" id="new-integrations-wrapper">
{dataSources.length === 0 && <AddDatasourceSecurely />}
{dataSources.length === 0 &&
this.props.mockDatasources.length > 0 && (
<>
{mockDataSection}
<StyledDivider />
</>
)}
{isEnabledForCreateNew && (
<>
<CreateNewDatasource
active={false}
history={history}
isCreating={isCreating}
isOnboardingScreen={!!isOnboardingScreen}
location={location}
pageId={pageId}
showMostPopularPlugins
showUnsupportedPluginDialog={this.showUnsupportedPluginDialog}
/>
<StyledDivider />
</>
)}
<CreateNewAPI
active={false}
history={history}
isCreating={isCreating}
isOnboardingScreen={!!isOnboardingScreen}
location={location}
pageId={pageId}
showUnsupportedPluginDialog={this.showUnsupportedPluginDialog}
/>
<StyledDivider />
<CreateNewDatasource
active={false}
history={history}
isCreating={isCreating}
location={location}
pageId={pageId}
showUnsupportedPluginDialog={this.showUnsupportedPluginDialog}
/>
<CreateNewSaasIntegration
active={false}
history={history}
isCreating={isCreating}
location={location}
pageId={pageId}
showUnsupportedPluginDialog={this.showUnsupportedPluginDialog}
/>
<CreateNewAIIntegration
history={history}
isCreating={isCreating}
pageId={pageId}
showUnsupportedPluginDialog={this.showUnsupportedPluginDialog}
/>
{dataSources.length > 0 && this.props.mockDatasources.length > 0 && (
<>
<StyledDivider />
{mockDataSection}
</>
)}
</NewIntegrationsContainer>
{showDebugger && <Debugger />}
</>
);
}
}

View File

@ -14,6 +14,7 @@ import JSObjectNameEditor from "./JSObjectNameEditor";
import {
setActiveJSAction,
setJsPaneConfigSelectedTab,
setJsPaneDebuggerState,
startExecutingJSFunction,
updateJSCollectionBody,
} from "actions/jsPaneActions";
@ -60,7 +61,6 @@ import history from "utils/history";
import { CursorPositionOrigin } from "@appsmith/reducers/uiReducers/editorContextReducer";
import LazyCodeEditor from "components/editorComponents/LazyCodeEditor";
import styled from "styled-components";
import { showDebuggerFlag } from "selectors/debuggerSelectors";
import { Tab, TabPanel, Tabs, TabsList } from "design-system";
import { JSEditorTab } from "reducers/uiReducers/jsPaneReducer";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
@ -70,6 +70,7 @@ import {
getHasManageActionPermission,
} from "@appsmith/utils/BusinessFeatures/permissionPageHelpers";
import type { JSCollectionData } from "@appsmith/reducers/entityReducers/jsActionsReducer";
import { DEBUGGER_TAB_KEYS } from "../../../components/editorComponents/Debugger/helpers";
interface JSFormProps {
jsCollectionData: JSCollectionData;
@ -202,6 +203,12 @@ function JSEditorForm({
// Executes JS action
const executeJSAction = (jsAction: JSAction, from: EventLocation) => {
dispatch(
setJsPaneDebuggerState({
open: true,
selectedTab: DEBUGGER_TAB_KEYS.RESPONSE_TAB,
}),
);
setActiveResponse(jsAction);
if (jsAction.id !== selectedJSActionOption.data?.id)
setSelectedJSActionOption(convertJSActionToDropdownOption(jsAction));
@ -303,9 +310,6 @@ function JSEditorForm({
const selectedConfigTab = useSelector(getJSPaneConfigSelectedTab);
// Debugger render flag
const showDebugger = useSelector(showDebuggerFlag);
const setSelectedConfigTab = useCallback((selectedTab: JSEditorTab) => {
dispatch(setJsPaneConfigSelectedTab(selectedTab));
}, []);
@ -418,23 +422,21 @@ function JSEditorForm({
)}
</Tabs>
</TabbedViewContainer>
{showDebugger ? (
<JSResponseView
currentFunction={activeResponse}
disabled={disableRunFunctionality || !isExecutePermitted}
errors={parseErrors}
isLoading={isExecutingCurrentJSAction}
jsCollectionData={jsCollectionData}
onButtonClick={(
event:
| React.MouseEvent<HTMLElement, MouseEvent>
| KeyboardEvent,
) => {
handleRunAction(event, "JS_OBJECT_RESPONSE_RUN_BUTTON");
}}
theme={theme}
/>
) : null}
<JSResponseView
currentFunction={activeResponse}
disabled={disableRunFunctionality || !isExecutePermitted}
errors={parseErrors}
isLoading={isExecutingCurrentJSAction}
jsCollectionData={jsCollectionData}
onButtonClick={(
event:
| React.MouseEvent<HTMLElement, MouseEvent>
| KeyboardEvent,
) => {
handleRunAction(event, "JS_OBJECT_RESPONSE_RUN_BUTTON");
}}
theme={theme}
/>
</SecondaryWrapper>
</div>
</Wrapper>

View File

@ -1,4 +1,4 @@
import { useContext, useEffect, useState } from "react";
import { useContext } from "react";
import React, { useCallback } from "react";
import type { InjectedFormProps } from "redux-form";
import { noop } from "lodash";
@ -15,7 +15,6 @@ import ActionSettings from "pages/Editor/ActionSettings";
import { Button, Tab, TabPanel, Tabs, TabsList, Tooltip } from "design-system";
import styled from "styled-components";
import FormRow from "components/editorComponents/FormRow";
import { ResizerCSS } from "components/editorComponents/Debugger/Resizer";
import {
createMessage,
DOCUMENTATION,
@ -30,16 +29,10 @@ import ActionRightPane, {
import type { ActionResponse } from "api/ActionAPI";
import type { Plugin } from "api/PluginApi";
import type { UIComponentTypes } from "api/PluginApi";
import { DEBUGGER_TAB_KEYS } from "components/editorComponents/Debugger/helpers";
import { EDITOR_TABS } from "constants/QueryEditorConstants";
import type { FormEvalOutput } from "reducers/evaluationReducers/formEvaluationReducer";
import { getQueryPaneConfigSelectedTabIndex } from "selectors/queryPaneSelectors";
import { setQueryPaneConfigSelectedTabIndex } from "actions/queryPaneActions";
import { ActionExecutionResizerHeight } from "pages/Editor/APIEditor/constants";
import {
getDebuggerSelectedTab,
showDebuggerFlag,
} from "selectors/debuggerSelectors";
import type { SourceEntity } from "entities/AppsmithConsole";
import { ENTITY_TYPE as SOURCE_ENTITY_TYPE } from "@appsmith/entities/AppsmithConsole/utils";
import { DocsLink, openDoc } from "../../../constants/DocumentationLinks";
@ -47,7 +40,6 @@ import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
import { QueryEditorContext } from "./QueryEditorContext";
import QueryDebuggerTabs from "./QueryDebuggerTabs";
import { setDebuggerSelectedTab, showDebugger } from "actions/debuggerActions";
import useShowSchema from "components/editorComponents/ActionRightPane/useShowSchema";
import { doesPluginRequireDatasource } from "@appsmith/entities/Engine/actionHelpers";
import FormRender from "./FormRender";
@ -79,16 +71,6 @@ const QueryFormContainer = styled.form`
}
`;
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);
`;
const SettingsWrapper = styled.div`
${thinScrollbar};
height: 100%;
@ -221,7 +203,6 @@ export function EditorJSONtoForm(props: Props) {
onCreateDatasourceClick,
onRunClick,
plugin,
responseDisplayFormat,
runErrorMessage,
settingConfig,
uiComponent,
@ -242,8 +223,6 @@ export function EditorJSONtoForm(props: Props) {
const currentActionConfig: Action | undefined = actions.find(
(action) => action.id === params.apiId || action.id === params.queryId,
);
const [showResponseOnFirstLoad, setShowResponseOnFirstLoad] =
useState<boolean>(false);
const pluginRequireDatasource = doesPluginRequireDatasource(plugin);
@ -264,38 +243,6 @@ export function EditorJSONtoForm(props: Props) {
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(() => {
// actionResponse and responseDisplayFormat is present only when query has response available
if (
responseDisplayFormat &&
!!responseDisplayFormat?.title &&
actionResponse &&
actionResponse.isExecutionSuccess &&
!showResponseOnFirstLoad
) {
dispatch(showDebugger(true));
dispatch(setDebuggerSelectedTab(DEBUGGER_TAB_KEYS.RESPONSE_TAB));
setShowResponseOnFirstLoad(true);
}
}, [responseDisplayFormat, actionResponse, showResponseOnFirstLoad]);
useEffect(() => {
if (showSchema) {
dispatch(showDebugger(true));
dispatch(setDebuggerSelectedTab(DEBUGGER_TAB_KEYS.SCHEMA_TAB));
}
}, [showSchema]);
// 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
useEffect(() => {
if (!!currentActionConfig?.id) {
setShowResponseOnFirstLoad(false);
}
}, [currentActionConfig?.id]);
const handleDocumentationClick = () => {
openDoc(DocsLink.QUERY, plugin?.documentationLink, plugin?.name);
};
@ -311,15 +258,10 @@ export function EditorJSONtoForm(props: Props) {
const selectedConfigTab = useSelector(getQueryPaneConfigSelectedTabIndex);
// Debugger render flag
const renderDebugger = useSelector(showDebuggerFlag);
const setSelectedConfigTab = useCallback((selectedIndex: string) => {
dispatch(setQueryPaneConfigSelectedTabIndex(selectedIndex));
}, []);
const selectedResponseTab = useSelector(getDebuggerSelectedTab);
// 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 =
@ -465,19 +407,16 @@ export function EditorJSONtoForm(props: Props) {
</Tooltip>
)}
</TabContainerView>
{renderDebugger &&
selectedResponseTab !== DEBUGGER_TAB_KEYS.HEADER_TAB && (
<QueryDebuggerTabs
actionName={actionName}
actionResponse={actionResponse}
actionSource={actionSource}
currentActionConfig={currentActionConfig}
isRunning={isRunning}
onRunClick={onRunClick}
runErrorMessage={runErrorMessage}
showSchema={showSchema}
/>
)}
<QueryDebuggerTabs
actionName={actionName}
actionResponse={actionResponse}
actionSource={actionSource}
currentActionConfig={currentActionConfig}
isRunning={isRunning}
onRunClick={onRunClick}
runErrorMessage={runErrorMessage}
showSchema={showSchema}
/>
</SecondaryWrapper>
</div>
{showRightPane && (

View File

@ -1,20 +1,11 @@
import {
setDebuggerSelectedTab,
setResponsePaneHeight,
showDebugger,
} from "actions/debuggerActions";
import { CloseDebugger } from "components/editorComponents/Debugger/DebuggerTabs";
import type { BottomTab } from "components/editorComponents/EntityBottomTabs";
import EntityBottomTabs from "components/editorComponents/EntityBottomTabs";
import React, { useCallback, useEffect, useRef } from "react";
import React, { useCallback, useEffect, 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 { getErrorCount } from "selectors/debuggerSelectors";
import { Text, TextType } from "design-system-old";
import Resizable, {
ResizerCSS,
@ -23,12 +14,10 @@ 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 EntityDeps from "components/editorComponents/Debugger/EntityDependecies";
import Schema from "components/editorComponents/Debugger/Schema";
import type { ActionResponse } from "api/ActionAPI";
import { isString } from "lodash";
@ -42,6 +31,11 @@ import {
import { DatasourceComponentTypes } from "api/PluginApi";
import { fetchDatasourceStructure } from "actions/datasourceActions";
import { DatasourceStructureContext } from "entities/Datasource";
import { getQueryPaneDebuggerState } from "selectors/queryPaneSelectors";
import { setQueryPaneDebuggerState } from "actions/queryPaneActions";
import { actionResponseDisplayDataFormats } from "../utils";
import { getIDEViewMode } from "selectors/ideSelectors";
import { EditorViewMode } from "@appsmith/entities/IDE/constants";
const ResultsCount = styled.div`
position: absolute;
@ -86,8 +80,16 @@ function QueryDebuggerTabs({
const panelRef = useRef<HTMLDivElement>(null);
const dispatch = useDispatch();
const selectedResponseTab = useSelector(getDebuggerSelectedTab);
const responsePaneHeight = useSelector(getResponsePaneHeight);
const { open, responseTabHeight, selectedTab } = useSelector(
getQueryPaneDebuggerState,
);
const { responseDisplayFormat } =
actionResponseDisplayDataFormats(actionResponse);
const [showResponseOnFirstLoad, setShowResponseOnFirstLoad] =
useState<boolean>(false);
const errorCount = useSelector(getErrorCount);
const pluginDatasourceForm = useSelector((state) =>
@ -120,6 +122,46 @@ function QueryDebuggerTabs({
}
}, []);
// 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(() => {
// actionResponse and responseDisplayFormat is present only when query has response available
if (
responseDisplayFormat &&
!!responseDisplayFormat?.title &&
actionResponse &&
actionResponse.isExecutionSuccess &&
!showResponseOnFirstLoad
) {
dispatch(
setQueryPaneDebuggerState({
open: true,
selectedTab: DEBUGGER_TAB_KEYS.RESPONSE_TAB,
}),
);
setShowResponseOnFirstLoad(true);
}
}, [responseDisplayFormat, actionResponse, showResponseOnFirstLoad]);
useEffect(() => {
if (showSchema && !selectedTab) {
dispatch(
setQueryPaneDebuggerState({
open: true,
selectedTab: DEBUGGER_TAB_KEYS.SCHEMA_TAB,
}),
);
}
}, [showSchema, currentActionConfig?.id, selectedTab]);
// 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
useEffect(() => {
if (!!currentActionConfig?.id) {
setShowResponseOnFirstLoad(false);
}
}, [currentActionConfig?.id]);
// Query is executed even once during the session, show the response data.
if (actionResponse) {
if (isString(actionResponse.body)) {
@ -140,32 +182,36 @@ function QueryDebuggerTabs({
}
const setQueryResponsePaneHeight = useCallback((height: number) => {
dispatch(setResponsePaneHeight(height));
dispatch(setQueryPaneDebuggerState({ responseTabHeight: height }));
}, []);
const onClose = useCallback(() => {
dispatch(setQueryPaneDebuggerState({ open: false }));
}, []);
const onClose = () => dispatch(showDebugger(false));
const setSelectedResponseTab = useCallback((tabKey: string) => {
dispatch(setDebuggerSelectedTab(tabKey));
dispatch(setQueryPaneDebuggerState({ selectedTab: tabKey }));
}, []);
const responseTabs: BottomTab[] = [
{
key: DEBUGGER_TAB_KEYS.ERROR_TAB,
title: createMessage(DEBUGGER_ERRORS),
count: errorCount,
panelComponent: <ErrorLogs />,
},
{
key: DEBUGGER_TAB_KEYS.LOGS_TAB,
title: createMessage(DEBUGGER_LOGS),
panelComponent: <DebuggerLogs searchQuery={actionName} />,
},
{
key: DEBUGGER_TAB_KEYS.INSPECT_TAB,
title: createMessage(INSPECT_ENTITY),
panelComponent: <EntityDeps />,
},
];
const ideViewMode = useSelector(getIDEViewMode);
const responseTabs: BottomTab[] = [];
if (ideViewMode === EditorViewMode.FullScreen) {
responseTabs.push(
{
key: DEBUGGER_TAB_KEYS.ERROR_TAB,
title: createMessage(DEBUGGER_ERRORS),
count: errorCount,
panelComponent: <ErrorLogs />,
},
{
key: DEBUGGER_TAB_KEYS.LOGS_TAB,
title: createMessage(DEBUGGER_LOGS),
panelComponent: <DebuggerLogs searchQuery={actionName} />,
},
);
}
if (currentActionConfig) {
responseTabs.unshift({
@ -197,13 +243,15 @@ function QueryDebuggerTabs({
});
}
if (!open) return null;
return (
<TabbedViewContainer
className="t--query-bottom-pane-container"
ref={panelRef}
>
<Resizable
initialHeight={responsePaneHeight}
initialHeight={responseTabHeight}
onResizeComplete={(height: number) =>
setQueryResponsePaneHeight(height)
}
@ -226,7 +274,7 @@ function QueryDebuggerTabs({
<EntityBottomTabs
expandedHeight={`${ActionExecutionResizerHeight}px`}
onSelect={setSelectedResponseTab}
selectedTabKey={selectedResponseTab}
selectedTabKey={selectedTab || ""}
tabs={responseTabs}
/>
<CloseDebugger

View File

@ -26,12 +26,12 @@ import { actionResponseDisplayDataFormats } from "../utils";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
import { getHasExecuteActionPermission } from "@appsmith/utils/BusinessFeatures/permissionPageHelpers";
import { getResponsePaneHeight } from "selectors/debuggerSelectors";
import { getErrorAsString } from "sagas/ActionExecution/errorUtils";
import { isString } from "lodash";
import ActionExecutionInProgressView from "components/editorComponents/ActionExecutionInProgressView";
import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig";
import BindDataButton from "./BindDataButton";
import { getQueryPaneDebuggerState } from "selectors/queryPaneSelectors";
const HelpSection = styled.div``;
@ -84,7 +84,7 @@ const QueryResponseTab = (props: Props) => {
const actionResponse = useSelector((state) =>
getActionData(state, currentActionConfig.id),
);
const responsePaneHeight = useSelector(getResponsePaneHeight);
const { responseTabHeight } = useSelector(getQueryPaneDebuggerState);
const { responseDataTypes, responseDisplayFormat } =
actionResponseDisplayDataFormats(actionResponse);
@ -99,7 +99,7 @@ const QueryResponseTab = (props: Props) => {
panelComponent: responseTabComponent(
dataType.key,
output,
responsePaneHeight,
responseTabHeight,
),
};
});
@ -278,7 +278,7 @@ const QueryResponseTab = (props: Props) => {
{responseTabComponent(
selectedControl || segmentedControlOptions[0]?.value,
output,
responsePaneHeight,
responseTabHeight,
)}
</ResponseDataContainer>
)}

View File

@ -160,6 +160,26 @@ const debuggerReducer = createImmerReducer(initialState, {
) => {
state.context = action.context;
},
[ReduxActionTypes.SET_CANVAS_DEBUGGER_STATE]: (
state: DebuggerReduxState,
action: { payload: Partial<CanvasDebuggerState> },
): DebuggerReduxState => {
return {
...state,
isOpen: "open" in action.payload ? !!action.payload.open : state.isOpen,
context: {
...state.context,
responseTabHeight:
"responseTabHeight" in action.payload
? Number(action.payload.responseTabHeight)
: state.context.responseTabHeight,
selectedDebuggerTab:
"selectedTab" in action.payload
? String(action.payload.selectedTab)
: state.context.selectedDebuggerTab,
},
};
},
// Resetting debugger state after env switch
[ReduxActionTypes.SWITCH_ENVIRONMENT_SUCCESS]: () => {
return klona(initialState);
@ -183,4 +203,10 @@ export interface DebuggerContext {
selectedDebuggerFilter: string;
}
export interface CanvasDebuggerState {
open: boolean;
responseTabHeight: number;
selectedTab?: string;
}
export default debuggerReducer;

View File

@ -12,24 +12,31 @@ export enum JSEditorTab {
SETTINGS = "SETTINGS",
}
export interface JSPaneDebuggerState {
open: boolean;
responseTabHeight: number;
selectedTab?: string;
}
export interface JsPaneReduxState {
isCreating: boolean;
isFetching: boolean;
isSaving: Record<string, boolean>;
isDeleting: Record<string, boolean>;
isDirty: Record<string, boolean>;
selectedConfigTab: JSEditorTab;
responseTabHeight: number;
debugger: JSPaneDebuggerState;
}
const initialState: JsPaneReduxState = {
isCreating: false,
isFetching: false,
isSaving: {},
isDeleting: {},
isDirty: {},
responseTabHeight: ActionExecutionResizerHeight,
selectedConfigTab: JSEditorTab.CODE,
debugger: {
open: false,
responseTabHeight: ActionExecutionResizerHeight,
},
};
const jsPaneReducer = createReducer(initialState, {
@ -184,6 +191,18 @@ const jsPaneReducer = createReducer(initialState, {
selectedConfigTab: selectedTab,
};
},
[ReduxActionTypes.SET_JS_PANE_DEBUGGER_STATE]: (
state: JsPaneReduxState,
action: ReduxAction<Partial<JSPaneDebuggerState>>,
) => {
return {
...state,
debugger: {
...state.debugger,
...action.payload,
},
};
},
});
export default jsPaneReducer;

View File

@ -18,9 +18,11 @@ import {
updateAction,
updateActionData,
} from "actions/pluginActionActions";
import { makeUpdateJSCollection } from "sagas/JSPaneSagas";
import {
handleExecuteJSFunctionSaga,
makeUpdateJSCollection,
} from "sagas/JSPaneSagas";
import { setDebuggerSelectedTab, showDebugger } from "actions/debuggerActions";
import type {
ApplicationPayload,
ReduxAction,
@ -38,12 +40,13 @@ import type {
import ActionAPI from "api/ActionAPI";
import {
getAction,
getCurrentActions,
getCurrentPageNameByActionId,
getDatasource,
getJSCollectionFromAllEntities,
getPlugin,
isActionDirty,
isActionSaving,
getDatasource,
getJSCollectionFromAllEntities,
} from "@appsmith/selectors/entitiesSelector";
import { getIsGitSyncModalOpen } from "selectors/gitSyncSelectors";
import {
@ -51,15 +54,15 @@ import {
getCurrentApplication,
} from "@appsmith/selectors/applicationSelectors";
import {
find,
flatten,
get,
isArray,
isString,
set,
find,
isNil,
flatten,
isArrayBuffer,
isEmpty,
isNil,
isString,
set,
unset,
} from "lodash";
import AppsmithConsole from "utils/AppsmithConsole";
@ -77,12 +80,12 @@ import type { Action } from "entities/Action";
import { PluginType } from "entities/Action";
import LOG_TYPE from "entities/AppsmithConsole/logtype";
import {
ACTION_EXECUTION_CANCELLED,
ACTION_EXECUTION_FAILED,
createMessage,
ERROR_ACTION_EXECUTE_FAIL,
ERROR_FAIL_ON_PAGE_LOAD_ACTIONS,
ERROR_PLUGIN_ACTION_EXECUTE,
ACTION_EXECUTION_CANCELLED,
ACTION_EXECUTION_FAILED,
SWITCH_ENVIRONMENT_SUCCESS,
} from "@appsmith/constants/messages";
import type {
@ -107,7 +110,7 @@ import * as log from "loglevel";
import { EMPTY_RESPONSE } from "components/editorComponents/emptyResponse";
import type { AppState } from "@appsmith/reducers";
import { DEFAULT_EXECUTE_ACTION_TIMEOUT_MS } from "@appsmith/constants/ApiConstants";
import { evalWorker, evaluateActionBindings } from "sagas/EvaluationsSaga";
import { evaluateActionBindings, evalWorker } from "sagas/EvaluationsSaga";
import { isBlobUrl, parseBlobUrl } from "utils/AppsmithUtils";
import { getType, Types } from "utils/TypeHelpers";
import { matchPath } from "react-router";
@ -115,11 +118,11 @@ import {
API_EDITOR_BASE_PATH,
API_EDITOR_ID_PATH,
API_EDITOR_PATH_WITH_SELECTED_PAGE_ID,
CURL_IMPORT_PAGE_PATH,
INTEGRATION_EDITOR_PATH,
matchQueryBuilderPath,
QUERIES_EDITOR_BASE_PATH,
QUERIES_EDITOR_ID_PATH,
CURL_IMPORT_PAGE_PATH,
matchQueryBuilderPath,
} from "constants/routes";
import { SAAS_EDITOR_API_ID_PATH } from "pages/Editor/SaaSEditor/constants";
import { APP_MODE } from "entities/App";
@ -141,10 +144,9 @@ import { submitCurlImportForm } from "actions/importActions";
import type { curlImportFormValues } from "pages/Editor/APIEditor/helpers";
import { matchBasePath } from "@appsmith/pages/Editor/Explorer/helpers";
import {
isTrueObject,
findDatatype,
isTrueObject,
} from "@appsmith/workers/Evaluation/evaluationUtils";
import { handleExecuteJSFunctionSaga } from "sagas/JSPaneSagas";
import type { Plugin } from "api/PluginApi";
import { setDefaultActionDisplayFormat } from "./PluginActionSagaUtils";
import { checkAndLogErrorsIfCyclicDependency } from "sagas/helper";
@ -152,13 +154,15 @@ import { toast } from "design-system";
import type { TRunDescription } from "workers/Evaluation/fns/actionFns";
import { DEBUGGER_TAB_KEYS } from "components/editorComponents/Debugger/helpers";
import { FILE_SIZE_LIMIT_FOR_BLOBS } from "constants/WidgetConstants";
import { getCurrentActions } from "@appsmith/selectors/entitiesSelector";
import type { ActionData } from "@appsmith/reducers/entityReducers/actionsReducer";
import { handleStoreOperations } from "./StoreActionSaga";
import { fetchPage } from "actions/pageActions";
import type { Datasource } from "entities/Datasource";
import { softRefreshDatasourceStructure } from "actions/datasourceActions";
import { changeQuery } from "actions/queryPaneActions";
import {
changeQuery,
setQueryPaneDebuggerState,
} from "actions/queryPaneActions";
import {
getCurrentEnvironmentDetails,
getCurrentEnvironmentName,
@ -179,6 +183,7 @@ import {
} from "@appsmith/utils/actionExecutionUtils";
import type { JSAction, JSCollection } from "entities/JSCollection";
import { getAllowedActionAnalyticsKeys } from "constants/AppsmithActionConstants/formConfig/ActionAnalyticsConfig";
import { setApiPaneDebuggerState } from "../../actions/apiPaneActions";
enum ActionResponseDataTypes {
BINARY = "BINARY",
@ -792,7 +797,7 @@ export function* runActionSaga(
const { paginationField } = reduxAction.payload;
// open response tab in debugger on exection of action.
if (!reduxAction.payload.skipOpeningDebugger) {
yield call(openDebugger);
yield call(openDebugger, plugin.type);
}
let payload = EMPTY_RESPONSE;
@ -1170,7 +1175,7 @@ function* executePageLoadAction(pageAction: PageAction, span?: OtlpSpan) {
// open response tab in debugger on exection of action on page load.
// Only if current page is the page on which the action is executed.
if (window.location.pathname.includes(pageAction.id))
yield call(openDebugger);
yield call(openDebugger, plugin.type);
if (isError) {
AppsmithConsole.addErrors([
@ -1563,9 +1568,20 @@ function triggerFileUploadInstrumentation(
}
//Open debugger with response tab selected.
function* openDebugger() {
yield put(showDebugger(true));
yield put(setDebuggerSelectedTab(DEBUGGER_TAB_KEYS.RESPONSE_TAB));
function* openDebugger(pluginType: PluginType) {
if (pluginType === PluginType.API) {
yield put(
setApiPaneDebuggerState({
open: true,
selectedTab: DEBUGGER_TAB_KEYS.RESPONSE_TAB,
}),
);
} else {
setQueryPaneDebuggerState({
open: true,
selectedTab: DEBUGGER_TAB_KEYS.RESPONSE_TAB,
});
}
}
// Function to clear the action responses for the actions which are not executeOnLoad.

View File

@ -53,6 +53,7 @@ import {
updateJSCollectionBodySuccess,
updateJSFunction,
executeJSFunctionInit,
setJsPaneDebuggerState,
} from "actions/jsPaneActions";
import { getCurrentWorkspaceId } from "@appsmith/selectors/selectedWorkspaceSelectors";
import { getPluginIdOfPackageName } from "sagas/selectors";
@ -91,14 +92,13 @@ import type { EventLocation } from "@appsmith/utils/analyticsUtilTypes";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { checkAndLogErrorsIfCyclicDependency } from "./helper";
import { toast } from "design-system";
import { setDebuggerSelectedTab, showDebugger } from "actions/debuggerActions";
import { DEBUGGER_TAB_KEYS } from "components/editorComponents/Debugger/helpers";
import { getDebuggerSelectedTab } from "selectors/debuggerSelectors";
import { getIsServerDSLMigrationsEnabled } from "selectors/pageSelectors";
import {
getJSActionNameToDisplay,
getJSActionPathNameToDisplay,
} from "@appsmith/utils/actionExecutionUtils";
import { getJsPaneDebuggerState } from "selectors/jsPaneSelectors";
export interface GenerateDefaultJSObjectProps {
name: string;
@ -434,15 +434,16 @@ export function* handleExecuteJSFunctionSaga(data: {
// open response tab in debugger on runnning or page load js action.
if (doesURLPathContainCollectionId || openDebugger) {
yield put(showDebugger(true));
yield put(setJsPaneDebuggerState({ open: true }));
const debuggerSelectedTab: ReturnType<typeof getDebuggerSelectedTab> =
yield select(getDebuggerSelectedTab);
const { selectedTab: debuggerSelectedTab } = yield select(
getJsPaneDebuggerState,
);
yield put(
setDebuggerSelectedTab(
debuggerSelectedTab || DEBUGGER_TAB_KEYS.RESPONSE_TAB,
),
setJsPaneDebuggerState({
selectedTab: debuggerSelectedTab || DEBUGGER_TAB_KEYS.RESPONSE_TAB,
}),
);
}
yield put({
@ -481,8 +482,12 @@ export function* handleExecuteJSFunctionSaga(data: {
} catch (error) {
// open response tab in debugger on runnning js action.
if (doesURLPathContainCollectionId) {
yield put(showDebugger(true));
yield put(setDebuggerSelectedTab(DEBUGGER_TAB_KEYS.RESPONSE_TAB));
yield put(
setJsPaneDebuggerState({
open: true,
selectedTab: DEBUGGER_TAB_KEYS.RESPONSE_TAB,
}),
);
}
AppsmithConsole.addErrors([
{

View File

@ -1,4 +1,6 @@
import type { AppState } from "@appsmith/reducers";
import { createSelector } from "reselect";
import { combinedPreviewModeSelector } from "./editorSelectors";
type GetFormData = (
state: AppState,
@ -18,3 +20,12 @@ export const getApiRightPaneSelectedTab = (state: AppState) =>
export const getIsRunning = (state: AppState, apiId: string) =>
state.ui.apiPane.isRunning[apiId];
export const getApiPaneDebuggerState = (state: AppState) =>
state.ui.apiPane.debugger;
export const showApiPaneDebugger = createSelector(
(state) => state.ui.apiPane.debugger.open,
combinedPreviewModeSelector,
(isOpen, isPreview) => isOpen && !isPreview,
);

View File

@ -12,6 +12,7 @@ import {
} from "@appsmith/workers/Evaluation/evaluationUtils";
import { getDataTree } from "./dataTreeSelectors";
import { combinedPreviewModeSelector } from "./editorSelectors";
import type { CanvasDebuggerState } from "reducers/uiReducers/debuggerReducer";
interface ErrorObejct {
[k: string]: Log;
@ -148,8 +149,22 @@ export const getScrollPosition = (state: AppState) =>
export const getDebuggerContext = (state: AppState) =>
state.ui.debugger.context;
export const getDebuggerOpen = (state: AppState) => state.ui.debugger.isOpen;
export const showDebuggerFlag = createSelector(
(state) => state.ui.debugger.isOpen,
getDebuggerOpen,
combinedPreviewModeSelector,
(isOpen, isPreview) => isOpen && !isPreview,
);
export const getCanvasDebuggerState = createSelector(
showDebuggerFlag,
getDebuggerContext,
(openState, context): CanvasDebuggerState => {
return {
open: openState,
selectedTab: context.selectedDebuggerTab,
responseTabHeight: context.responseTabHeight,
};
},
);

View File

@ -21,3 +21,6 @@ export const getFirstJSObject = (
return identifyEntityFromPath(urlWithoutQueryParams);
}
};
export const getJsPaneDebuggerState = (state: AppState) =>
state.ui.jsPane.debugger;

View File

@ -18,6 +18,9 @@ export const getFirstQuery = (state: AppState): FocusEntityInfo | undefined => {
}
};
export const getQueryPaneDebuggerState = (state: AppState) =>
state.ui.queryPane.debugger;
export const getQueryRunErrorMessage = (state: AppState, id: string) => {
const { runErrorMessage } = state.ui.queryPane;
return runErrorMessage[id];