diff --git a/app/client/src/ce/constants/messages.ts b/app/client/src/ce/constants/messages.ts index caef7eaad6c4..ce9dd82b126d 100644 --- a/app/client/src/ce/constants/messages.ts +++ b/app/client/src/ce/constants/messages.ts @@ -2503,3 +2503,9 @@ export const EMPTY_DATASOURCE_TOOLTIP_SIDEBUTTON = () => "Create a datasource to power your app with data."; export const FIELD_REQUIRED_MESSAGE = () => `This field is required`; + +export const PREPARED_STATEMENT_WARNING = { + MESSAGE: () => + "Prepared Statements are currently enabled, which may be causing the query error. Turn them off and try running the query again", + LINK: () => "Open settings", +}; diff --git a/app/client/src/pages/Editor/QueryEditor/QueryResponseTab.test.tsx b/app/client/src/pages/Editor/QueryEditor/QueryResponseTab.test.tsx new file mode 100644 index 000000000000..6615ebbaf390 --- /dev/null +++ b/app/client/src/pages/Editor/QueryEditor/QueryResponseTab.test.tsx @@ -0,0 +1,235 @@ +import React from "react"; +import { render } from "@testing-library/react"; +import { Provider } from "react-redux"; +import configureStore from "redux-mock-store"; +import QueryResponseTab from "./QueryResponseTab"; +import { ENTITY_TYPE } from "ee/entities/AppsmithConsole/utils"; +import type { Action } from "entities/Action"; +import { unitTestBaseMockStore } from "layoutSystems/common/dropTarget/unitTestUtils"; +import { EditorViewMode } from "ee/entities/IDE/constants"; +import { lightTheme } from "selectors/themeSelectors"; +import { ThemeProvider } from "styled-components"; +import { BrowserRouter as Router } from "react-router-dom"; + +// Mock store +const mockStore = configureStore([]); + +const defaultProps = { + actionName: "Test Action", + actionSource: { + name: "test source", + id: "test-source-id", + type: ENTITY_TYPE.ACTION, + }, + currentActionConfig: { + id: "test-action-id", + name: "Test Action", + actionConfiguration: { pluginSpecifiedTemplates: [{ value: true }] }, + userPermissions: ["execute"], + } as Action, + isRunning: false, + onRunClick: jest.fn(), + runErrorMessage: "", +}; + +const storeData = { + ...unitTestBaseMockStore, + evaluations: { + tree: {}, + }, + entities: { + plugins: { + list: [], + }, + datasources: { + structure: {}, + }, + }, + ui: { + ...unitTestBaseMockStore.ui, + users: { + featureFlag: { + data: {}, + overriddenFlags: {}, + }, + }, + ide: { + view: EditorViewMode.FullScreen, + }, + debugger: { + context: { + errorCount: 0, + }, + }, + queryPane: { + debugger: { + open: true, + responseTabHeight: 200, + selectedTab: "response", + }, + }, + }, +}; + +describe("QueryResponseTab", () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let store: any; + + beforeEach(() => { + store = mockStore(storeData); + }); + + /** Test use prepared statement warning **/ + it("1. Prepared statement warning should not be showing", () => { + const { container } = render( + + + + + + + , + ); + + // Check if the prepared statement warning is not showing + expect( + container.querySelector("[data-testid='t--prepared-statement-warning']"), + ).toBeNull(); + }); + + it("2. Check if prepared statement warning is not showing while running the query", () => { + const { container } = render( + + + + + + + , + ); + + // Check if the prepared statement warning is showing + expect( + container.querySelector("[data-testid='t--prepared-statement-warning']"), + ).toBeNull(); + }); + + it("3. Check if prepared statement warning is not showing when run is successful", () => { + store = mockStore({ + ...storeData, + entities: { + ...storeData.entities, + actions: [ + { + config: { + id: "test-action-id", + name: "Test Action", + }, + isLoading: false, + data: { + body: [{ key: "value" }], + isExecutionSuccess: true, + }, + }, + ], + }, + }); + + const { container } = render( + + + + + + + , + ); + + // Check if the prepared statement warning is showing + expect( + container.querySelector("[data-testid='t--prepared-statement-warning']"), + ).toBeNull(); + }); + + it("4. Check if prepared statement warning is showing when run is failed", () => { + store = mockStore({ + ...storeData, + entities: { + ...storeData.entities, + actions: [ + { + config: { + id: "test-action-id", + name: "Test Action", + }, + isLoading: false, + data: { + body: "ERROR: relation 'userssss' does not exist Position: 15", + isExecutionSuccess: false, + }, + }, + ], + }, + }); + + const { container } = render( + + + + + + + , + ); + + // Check if the prepared statement warning is showing + expect( + container.querySelector("[data-testid='t--prepared-statement-warning']"), + ).not.toBeNull(); + }); + + it("5. Check if prepared statement warning is not showing when prepared statement is turned off", () => { + store = mockStore({ + ...storeData, + entities: { + ...storeData.entities, + actions: [ + { + config: { + id: "test-action-id", + name: "Test Action", + }, + isLoading: false, + data: { + body: "ERROR: relation 'userssss' does not exist Position: 15", + isExecutionSuccess: false, + }, + }, + ], + }, + }); + + const props = { + ...defaultProps, + currentActionConfig: { + ...defaultProps.currentActionConfig, + actionConfiguration: { pluginSpecifiedTemplates: [{ value: false }] }, + } as Action, + }; + + const { container } = render( + + + + + + + , + ); + + // Check if the prepared statement warning is showing + expect( + container.querySelector("[data-testid='t--prepared-statement-warning']"), + ).toBeNull(); + }); +}); diff --git a/app/client/src/pages/Editor/QueryEditor/QueryResponseTab.tsx b/app/client/src/pages/Editor/QueryEditor/QueryResponseTab.tsx index c2d5295e65b1..a95c0cc0277b 100644 --- a/app/client/src/pages/Editor/QueryEditor/QueryResponseTab.tsx +++ b/app/client/src/pages/Editor/QueryEditor/QueryResponseTab.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useCallback, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import ReactJson from "react-json-view"; import { @@ -13,7 +13,12 @@ import LogAdditionalInfo from "components/editorComponents/Debugger/ErrorLogs/co import LogHelper from "components/editorComponents/Debugger/ErrorLogs/components/LogHelper"; import LOG_TYPE from "entities/AppsmithConsole/logtype"; import { JsonWrapper } from "components/editorComponents/Debugger/ErrorLogs/components/LogCollapseData"; -import { Callout, Flex, SegmentedControl } from "@appsmith/ads"; +import { + Callout, + Flex, + SegmentedControl, + type CalloutLinkProps, +} from "@appsmith/ads"; import styled from "styled-components"; import { DEBUGGER_TAB_KEYS } from "components/editorComponents/Debugger/helpers"; import AnalyticsUtil from "ee/utils/AnalyticsUtil"; @@ -32,6 +37,12 @@ import ActionExecutionInProgressView from "components/editorComponents/ActionExe import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; import BindDataButton from "./BindDataButton"; import { getQueryPaneDebuggerState } from "selectors/queryPaneSelectors"; +import { setQueryPaneConfigSelectedTabIndex } from "actions/queryPaneActions"; +import { EDITOR_TABS } from "constants/QueryEditorConstants"; +import { + createMessage, + PREPARED_STATEMENT_WARNING, +} from "ee/constants/messages"; const HelpSection = styled.div``; @@ -151,6 +162,7 @@ const QueryResponseTab = (props: Props) => { let error = runErrorMessage; let hintMessages: Array = []; + let showPreparedStatementWarning = false; // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any let output: Record[] | null = null; @@ -189,8 +201,31 @@ const QueryResponseTab = (props: Props) => { error = ""; hintMessages = actionResponse.messages; } + + const { pluginSpecifiedTemplates } = + currentActionConfig.actionConfiguration; + + if ( + error && + pluginSpecifiedTemplates && + pluginSpecifiedTemplates.length > 0 && + pluginSpecifiedTemplates[0].value === true + ) { + showPreparedStatementWarning = true; + } } + const navigateToSettings = useCallback(() => { + dispatch(setQueryPaneConfigSelectedTabIndex(EDITOR_TABS.SETTINGS)); + }, []); + + const preparedStatementCalloutLinks: CalloutLinkProps[] = [ + { + onClick: navigateToSettings, + children: createMessage(PREPARED_STATEMENT_WARNING.LINK), + }, + ]; + if (isRunning) { return ( { return ( + {showPreparedStatementWarning && ( + + {createMessage(PREPARED_STATEMENT_WARNING.MESSAGE)} + + )} {error && (