diff --git a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/ApiError_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/ApiError_spec.ts index 622e3eee2419..f489e2757d07 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/ApiError_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/ApiError_spec.ts @@ -12,12 +12,12 @@ describe( // Create api that causes an error _.apiPage.CreateAndFillApi("https://fakeapi/user"); }); - it("it shows error message", () => { + it("it shows error message in response tab", () => { _.apiPage.RunAPI(false); _.debuggerHelper.AssertOpen(PageType.API); _.apiPage.ResponseStatusCheck("PE-RST-5000"); }); - it("it shows debug button and navigates", () => { + it("it shows error messages in error tab", () => { _.apiPage.DebugError(); _.debuggerHelper.AssertSelectedTab( Cypress.env("MESSAGES").DEBUGGER_ERRORS(), diff --git a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/EntityBottomBar_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/EntityBottomBar_spec.ts index 806170b30ff9..b60cf975d629 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/EntityBottomBar_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/EntityBottomBar_spec.ts @@ -102,13 +102,13 @@ describe( //Create and run query. _.dataSources.EnterQuery( - "SELECT * FROM users ORDER BY id LIMIT 10;", + "SELECT * FROM public.users ORDER BY id LIMIT 10;", 1000, ); _.dataSources.RunQuery(); //Verify if bottom bar is open on executing query. _.debuggerHelper.AssertOpen(PageType.Query); - //Verify if response atb is selected on executing query. + //Verify if response tab is selected on executing query. _.debuggerHelper.AssertSelectedTab( Cypress.env("MESSAGES").DEBUGGER_RESPONSE(), ); @@ -140,13 +140,13 @@ describe( _.debuggerHelper.AssertClosed(); //Create and run query. _.dataSources.EnterQuery( - "SELECT * FROM users ORDER BY id LIMIT 10;", + "SELECT * FROM public.users ORDER BY id LIMIT 10;", 1000, ); _.dataSources.RunQuery(); //Verify if bottom bar is open on executing query. _.debuggerHelper.AssertOpen(PageType.Query); - //Verify if response atb is selected on executing query. + //Verify if response tab is selected on executing query. _.debuggerHelper.AssertSelectedTab( Cypress.env("MESSAGES").DEBUGGER_RESPONSE(), ); diff --git a/app/client/cypress/e2e/Regression/ServerSide/ApiTests/API_All_Verb_spec.js b/app/client/cypress/e2e/Regression/ServerSide/ApiTests/API_All_Verb_spec.js index 8c4cfa434edc..a33910b4c4c7 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/ApiTests/API_All_Verb_spec.js +++ b/app/client/cypress/e2e/Regression/ServerSide/ApiTests/API_All_Verb_spec.js @@ -18,7 +18,7 @@ describe( "API Panel Test Functionality", { tags: ["@tag.Datasource", "@tag.Git", "@tag.AccessControl"] }, function () { - const successMsg = "Executed successfully from user request"; + const successMsg = "Successfully executed in "; afterEach(function () { agHelper.ActionContextMenuWithInPane({ action: "Delete", diff --git a/app/client/cypress/e2e/Sanity/Datasources/MySQLNoiseTest_spec.js b/app/client/cypress/e2e/Sanity/Datasources/MySQLNoiseTest_spec.js index bd4acb0b9e5d..90e8718c2f6a 100644 --- a/app/client/cypress/e2e/Sanity/Datasources/MySQLNoiseTest_spec.js +++ b/app/client/cypress/e2e/Sanity/Datasources/MySQLNoiseTest_spec.js @@ -9,6 +9,7 @@ import { dataSources, debuggerHelper, } from "../../../support/Objects/ObjectsCore"; + const commonlocators = require("../../../locators/commonlocators.json"); describe( @@ -70,7 +71,7 @@ describe( debuggerHelper.OpenDebugger(); debuggerHelper.ClickLogsTab(); debuggerHelper.DoesConsoleLogExist( - "Execution failed with status PE-STC-5000", + "Failed execution", true, "NoiseTestQuery", ); diff --git a/app/client/cypress/support/Pages/ApiPage.ts b/app/client/cypress/support/Pages/ApiPage.ts index 7af9d454d130..3c98073e5fbb 100644 --- a/app/client/cypress/support/Pages/ApiPage.ts +++ b/app/client/cypress/support/Pages/ApiPage.ts @@ -83,8 +83,7 @@ export class ApiPage { private _paginationTypeLabels = ".t--apiFormPaginationType label"; _saveAsDS = ".t--store-as-datasource"; _responseStatus = ".t--response-status-code"; - public _responseTabHeader = "[data-testid=t--tab-HEADERS_TAB]"; - public _headersTabContent = ".t--headers-tab"; + public _debugger = ".t--debugger-count"; public _autoGeneratedHeaderInfoIcon = (key: string) => `.t--auto-generated-${key}-info`; _nextCursorValue = ".t--apiFormPaginationNextCursorValue"; @@ -471,8 +470,7 @@ export class ApiPage { } DebugError() { - this.agHelper.GetNClick(this._responseTabHeader); - cy.get(this._headersTabContent).contains("Debug").click(); + this.agHelper.GetNClick(this._debugger); } public FillCurlNImport(value: string) { diff --git a/app/client/packages/design-system/ads/src/Icon/Icon.provider.tsx b/app/client/packages/design-system/ads/src/Icon/Icon.provider.tsx index 5a5ff05eb1e0..51d1d16667fd 100644 --- a/app/client/packages/design-system/ads/src/Icon/Icon.provider.tsx +++ b/app/client/packages/design-system/ads/src/Icon/Icon.provider.tsx @@ -1072,6 +1072,14 @@ const InputCursorMoveIcon = importSvg( async () => import("../__assets__/icons/ads/input-cursor-move.svg"), ); +const DebugIcon = importSvg( + async () => import("../__assets__/icons/ads/debug.svg"), +); + +const ClearIcon = importSvg( + async () => import("../__assets__/icons/ads/clear.svg"), +); + const ContentTypeTable = importSvg( async () => import("../__assets__/icons/ads/content-type-table.svg"), ); @@ -1149,6 +1157,7 @@ const ICON_LOOKUP = { "check-line": CheckLineIcon, "chevron-left": ChevronLeft, "chevron-right": ChevronRight, + clear: ClearIcon, "close-circle": CloseCircleIcon, "close-circle-control": CloseCircleIcon, "close-circle-line": CloseCircleLineIcon, @@ -1176,6 +1185,7 @@ const ICON_LOOKUP = { "datasource-v3": DatasourceV3Icon, "datasources-2": Datasources2, "decrease-control": DecreaseIcon, + debug: DebugIcon, "delete-bin-line": DeleteBinLineIcon, "delete-blank": DeleteBin7, "delete-column": DeleteColumnIcon, diff --git a/app/client/packages/design-system/ads/src/__assets__/icons/ads/clear.svg b/app/client/packages/design-system/ads/src/__assets__/icons/ads/clear.svg new file mode 100644 index 000000000000..a0e12ee85fae --- /dev/null +++ b/app/client/packages/design-system/ads/src/__assets__/icons/ads/clear.svg @@ -0,0 +1,7 @@ + + + + diff --git a/app/client/packages/design-system/ads/src/__assets__/icons/ads/debug.svg b/app/client/packages/design-system/ads/src/__assets__/icons/ads/debug.svg new file mode 100644 index 000000000000..d6cee9364754 --- /dev/null +++ b/app/client/packages/design-system/ads/src/__assets__/icons/ads/debug.svg @@ -0,0 +1,5 @@ + + + diff --git a/app/client/src/PluginActionEditor/components/PluginActionResponse/PluginActionResponse.tsx b/app/client/src/PluginActionEditor/components/PluginActionResponse/PluginActionResponse.tsx index 45244f48010c..4dcc504442e3 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionResponse/PluginActionResponse.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionResponse/PluginActionResponse.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect } from "react"; +import React, { useCallback, useEffect, useMemo } from "react"; import { IDEBottomView, ViewHideBehaviour } from "IDE"; import { ActionExecutionResizerHeight } from "./constants"; import EntityBottomTabs from "components/editorComponents/EntityBottomTabs"; @@ -12,6 +12,8 @@ import { usePluginActionContext } from "../../PluginActionContext"; import { doesPluginRequireDatasource } from "ee/entities/Engine/actionHelpers"; import useShowSchema from "./hooks/useShowSchema"; import { actionResponseDisplayDataFormats } from "pages/Editor/utils"; +import { PluginType } from "entities/Action"; +import { hasFailed } from "./utils"; function PluginActionResponse() { const dispatch = useDispatch(); @@ -30,6 +32,11 @@ function PluginActionResponse() { const { responseDisplayFormat } = actionResponseDisplayDataFormats(actionResponse); + const executionFailed = useMemo( + () => (actionResponse ? hasFailed(actionResponse) : false), + [actionResponse], + ); + // 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( @@ -55,7 +62,21 @@ function PluginActionResponse() { ); useEffect( - function openSchemaTabWhenNoTabIsSelected() { + function openResponseTabOnError() { + if (executionFailed) { + dispatch( + setPluginActionEditorDebuggerState({ + open: true, + selectedTab: DEBUGGER_TAB_KEYS.RESPONSE_TAB, + }), + ); + } + }, + [executionFailed, dispatch], + ); + + useEffect( + function openDefaultTabWhenNoTabIsSelected() { if (showSchema && !selectedTab) { dispatch( setPluginActionEditorDebuggerState({ @@ -63,9 +84,16 @@ function PluginActionResponse() { selectedTab: DEBUGGER_TAB_KEYS.SCHEMA_TAB, }), ); + } else if (plugin.type === PluginType.API && !selectedTab) { + dispatch( + setPluginActionEditorDebuggerState({ + open: true, + selectedTab: DEBUGGER_TAB_KEYS.RESPONSE_TAB, + }), + ); } }, - [showSchema, selectedTab, dispatch], + [showSchema, selectedTab, dispatch, plugin.type], ); const toggleHide = useCallback( diff --git a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/ApiResponse.tsx b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/ApiResponse.tsx index f5be06f401d2..db11345aa3b9 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/ApiResponse.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/ApiResponse.tsx @@ -21,6 +21,8 @@ import { getUpdateTimestamp } from "components/editorComponents/Debugger/ErrorLo import { ENTITY_TYPE } from "ee/entities/AppsmithConsole/utils"; import ApiFormatSegmentedResponse from "./ApiFormatSegmentedResponse"; import { NoResponse } from "./NoResponse"; +import { useSelector } from "react-redux"; +import { getFilteredErrors } from "selectors/debuggerSelectors"; const HelpSection = styled.div` padding-bottom: 5px; @@ -46,22 +48,20 @@ export const ResponseTabErrorContainer = styled.div` height: fit-content; background: var(--ads-v2-color-bg-error); border-bottom: 1px solid var(--ads-v2-color-border); + font-size: 12px; + line-height: 16px; `; export const ResponseTabErrorContent = styled.div` display: flex; align-items: flex-start; gap: 4px; - font-size: 12px; - line-height: 16px; `; export const ResponseTabErrorDefaultMessage = styled.div` flex-shrink: 0; `; -export const apiReactJsonProps = { ...reactJsonProps, collapsed: 0 }; - export function ApiResponse(props: { action: Action; actionResponse?: ActionResponse; @@ -71,7 +71,19 @@ export function ApiResponse(props: { onRunClick: () => void; responseTabHeight: number; }) { - const { id, name } = props.action; + const { + action, + actionResponse, + isRunDisabled, + isRunning, + onRunClick, + responseTabHeight, + theme, + } = props; + const { id, name } = action; + + const errors = useSelector(getFilteredErrors); + const actionSource: SourceEntity = useMemo( () => ({ type: ENTITY_TYPE.ACTION, @@ -89,29 +101,33 @@ export function ApiResponse(props: { ); } - if (!props.actionResponse) { + if (!actionResponse) { return ( ); } - const { messages, pluginErrorDetails, request } = props.actionResponse; + const { body, messages, pluginErrorDetails, request } = actionResponse; - const runHasFailed = hasFailed(props.actionResponse); - const requestWithTimestamp = getUpdateTimestamp(request); + const runHasFailed = hasFailed(actionResponse); + const requestWithTimestamp = { + error: + actionResponse.readableError || + actionResponse.pluginErrorDetails?.downstreamErrorMessage || + actionResponse.body || + "An unexpected error occurred", + request: getUpdateTimestamp(request), + }; return ( - + {Array.isArray(messages) && messages.length > 0 && ( {messages.map((message, i) => ( @@ -121,25 +137,32 @@ export function ApiResponse(props: { ))} )} - {runHasFailed && !props.isRunning ? ( + {runHasFailed && !isRunning ? ( Your API failed to execute - {pluginErrorDetails && ":"} + {actionResponse && (pluginErrorDetails || body) && ":"} - {pluginErrorDetails && ( - <> -
- {pluginErrorDetails.downstreamErrorMessage} -
- {pluginErrorDetails.downstreamErrorCode && ( - - )} - - )} + {actionResponse && + (pluginErrorDetails ? ( + <> +
+ {pluginErrorDetails.downstreamErrorMessage} +
+ {pluginErrorDetails.downstreamErrorCode && ( + + )} + + ) : ( + errors?.[action.id]?.messages?.[0].message.message && ( +
+ {errors?.[action.id]?.messages?.[0].message.message} +
+ ) + ))} {requestWithTimestamp && ( - + )}
) : ( - {isEmpty(props.actionResponse.statusCode) ? ( + {isEmpty(actionResponse.statusCode) ? ( ) : ( )} diff --git a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/ApiResponseHeaders.tsx b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/ApiResponseHeaders.tsx index 7f055d5bc4f3..01abd8c20838 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/ApiResponseHeaders.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/ApiResponseHeaders.tsx @@ -55,17 +55,6 @@ export function ApiResponseHeaders(props: { return headersTransformer(props.actionResponse?.headers); }, [props.actionResponse?.headers]); - const errorCalloutLinks = useMemo(() => { - return [ - { - children: "Debug", - endIcon: "bug", - onClick: props.onDebugClick, - to: "", - }, - ]; - }, [props.onDebugClick]); - const headersInput = useMemo(() => { return { value: !isEmpty(responseHeaders) @@ -91,21 +80,21 @@ export function ApiResponseHeaders(props: { return ( {runHasFailed && !props.isRunning && ( - - {createMessage(CHECK_REQUEST_BODY)} - + {createMessage(CHECK_REQUEST_BODY)} + )} + {!runHasFailed && ( + + {isEmpty(props.actionResponse.statusCode) ? ( + + ) : ( + + )} + )} - - {isEmpty(props.actionResponse.statusCode) ? ( - - ) : ( - - )} - ); } diff --git a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/QueryResponseTab.tsx b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/QueryResponseTab.tsx index b080dd85e052..546f7ef48142 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/QueryResponseTab.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/QueryResponseTab.tsx @@ -2,7 +2,6 @@ import React, { useCallback, useMemo, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import ReactJson from "react-json-view"; import { - apiReactJsonProps, ResponseTabErrorContainer, ResponseTabErrorContent, ResponseTabErrorDefaultMessage, @@ -12,7 +11,10 @@ import { NoResponse } from "../NoResponse"; import LogAdditionalInfo from "components/editorComponents/Debugger/ErrorLogs/components/LogAdditionalInfo"; 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 { + JsonWrapper, + reactJsonProps, +} from "components/editorComponents/Debugger/ErrorLogs/components/LogCollapseData"; import { Callout, Menu, @@ -190,8 +192,10 @@ export const QueryResponseTab = (props: Props) => { const currentContentType = selectedContentType || firstContentTypeOption?.value; - const responseState = - actionResponse && getUpdateTimestamp(actionResponse.request); + const responseState = { + error: errorMessage, + request: actionResponse && getUpdateTimestamp(actionResponse.request), + }; const selectedTabIndex = responseDataTypes && @@ -366,7 +370,7 @@ export const QueryResponseTab = (props: Props) => { className="t--debugger-log-state" onClick={handleJsonWrapperClick} > - + )} diff --git a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/styles.ts b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/styles.ts index a94fde58c5e1..9a132cdcd609 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/styles.ts +++ b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/styles.ts @@ -33,6 +33,7 @@ export const StatusBar = styled.div` padding: 8px; border-bottom: 1px solid var(--ads-v2-color-border); z-index: var(--ads-v2-z-index-1); + background: var(--ads-v2-color-bg); `; export const StatusBarInfo = styled.div` diff --git a/app/client/src/ce/PluginActionEditor/components/PluginActionResponse/hooks/usePluginActionResponseTabs.tsx b/app/client/src/ce/PluginActionEditor/components/PluginActionResponse/hooks/usePluginActionResponseTabs.tsx index 35a87e5341a1..838832716e57 100644 --- a/app/client/src/ce/PluginActionEditor/components/PluginActionResponse/hooks/usePluginActionResponseTabs.tsx +++ b/app/client/src/ce/PluginActionEditor/components/PluginActionResponse/hooks/usePluginActionResponseTabs.tsx @@ -142,17 +142,17 @@ function usePluginActionResponseTabs() { if (IDEViewMode === EditorViewMode.FullScreen) { tabs.push( + { + key: DEBUGGER_TAB_KEYS.LOGS_TAB, + title: createMessage(DEBUGGER_LOGS), + panelComponent: , + }, { key: DEBUGGER_TAB_KEYS.ERROR_TAB, title: createMessage(DEBUGGER_ERRORS), count: errorCount, panelComponent: , }, - { - key: DEBUGGER_TAB_KEYS.LOGS_TAB, - title: createMessage(DEBUGGER_LOGS), - panelComponent: , - }, ); } diff --git a/app/client/src/ce/constants/messages.ts b/app/client/src/ce/constants/messages.ts index 02ae2a652d9c..2ec08868dd23 100644 --- a/app/client/src/ce/constants/messages.ts +++ b/app/client/src/ce/constants/messages.ts @@ -556,7 +556,7 @@ export const LOGS_FILTER_OPTION_CONSOLE = () => "Console logs"; export const LOGS_FILTER_OPTION_SYSTEM = () => "System logs"; export const NO_LOGS = () => "No logs to show"; export const NO_ERRORS = () => "No signs of trouble here!"; -export const DEBUGGER_ERRORS = () => "Errors"; +export const DEBUGGER_ERRORS = () => "Linter"; export const DEBUGGER_RESPONSE = () => "Response"; export const DEBUGGER_HEADERS = () => "Headers"; export const DEBUGGER_LOGS = () => "Logs"; @@ -629,8 +629,9 @@ export const EXPORT_DEFAULT_BEGINNING = () => `Start object with export default`; export const ACTION_EXECUTION_FAILED = (actionName: string) => `The action "${actionName}" has failed.`; -export const JS_EXECUTION_SUCCESS = () => "JS Function executed successfully"; -export const JS_EXECUTION_FAILURE = () => "JS Function execution failed"; +export const JS_EXECUTION_TRIGGERED = () => "Function triggered"; +export const JS_EXECUTION_SUCCESS = () => "Function executed"; +export const JS_EXECUTION_FAILURE = () => "Function execution failed"; export const JS_EXECUTION_FAILURE_TOASTER = () => "There was an error while executing function"; export const JS_SETTINGS_ONPAGELOAD = () => "Run function on page load (Beta)"; diff --git a/app/client/src/ce/entities/AppsmithConsole/utils.ts b/app/client/src/ce/entities/AppsmithConsole/utils.ts index 63a5104d27a4..025e0a960abe 100644 --- a/app/client/src/ce/entities/AppsmithConsole/utils.ts +++ b/app/client/src/ce/entities/AppsmithConsole/utils.ts @@ -1,5 +1,7 @@ import type { DataTreeEntity } from "entities/DataTree/dataTreeTypes"; import type { DataTreeEntityConfig } from "../DataTree/types"; +import type { TriggerMeta } from "../../sagas/ActionExecution/ActionExecutionSagas"; +import type { SourceEntity } from "../../../entities/AppsmithConsole"; export enum ENTITY_TYPE { ACTION = "ACTION", @@ -25,3 +27,16 @@ export const getModuleInstanceInvalidErrors = ( ) => { return []; }; + +export const getSourceFromTriggerMeta = ( + triggerMeta?: TriggerMeta, +): SourceEntity => { + const type = + (triggerMeta?.source?.entityType as ENTITY_TYPE) || ENTITY_TYPE.JSACTION; + const name = + triggerMeta?.source?.name || triggerMeta?.triggerPropertyName || ""; + const propertyPath = triggerMeta?.triggerPropertyName || ""; + const id = triggerMeta?.source?.id || ""; + + return { type, name, id, propertyPath }; +}; diff --git a/app/client/src/ce/sagas/ActionExecution/ActionExecutionSagas.ts b/app/client/src/ce/sagas/ActionExecution/ActionExecutionSagas.ts index 9fe7e8246485..8766c199f46f 100644 --- a/app/client/src/ce/sagas/ActionExecution/ActionExecutionSagas.ts +++ b/app/client/src/ce/sagas/ActionExecution/ActionExecutionSagas.ts @@ -44,6 +44,7 @@ import { postMessageSaga } from "sagas/ActionExecution/PostMessageSaga"; import type { ActionDescription } from "ee/workers/Evaluation/fns"; import type { AppState } from "ee/reducers"; import { getAction } from "ee/selectors/entitiesSelector"; +import { getSourceFromTriggerMeta } from "ee/entities/AppsmithConsole/utils"; export interface TriggerMeta { source?: TriggerSource; @@ -67,6 +68,7 @@ export function* executeActionTriggers( ): any { // when called via a promise, a trigger can return some value to be used in .then let response: unknown[] = []; + const source = getSourceFromTriggerMeta(triggerMeta); switch (trigger.type) { case "RUN_PLUGIN_ACTION": @@ -92,25 +94,25 @@ export function* executeActionTriggers( break; case "NAVIGATE_TO": - yield call(navigateActionSaga, trigger); + yield call(navigateActionSaga, trigger, source); break; case "SHOW_ALERT": - yield call(showAlertSaga, trigger); + yield call(showAlertSaga, trigger, source); break; case "SHOW_MODAL_BY_NAME": - yield call(openModalSaga, trigger); + yield call(openModalSaga, trigger, source); break; case "CLOSE_MODAL": - yield call(closeModalSaga, trigger); + yield call(closeModalSaga, trigger, source); break; case "DOWNLOAD": - yield call(downloadSaga, trigger); + yield call(downloadSaga, trigger, source); break; case "COPY_TO_CLIPBOARD": - yield call(copySaga, trigger); + yield call(copySaga, trigger, source); break; case "RESET_WIDGET_META_RECURSIVE_BY_NAME": - yield call(resetWidgetActionSaga, trigger); + yield call(resetWidgetActionSaga, trigger, source); break; case "GET_CURRENT_LOCATION": response = yield call(getCurrentLocationSaga, trigger); @@ -130,7 +132,7 @@ export function* executeActionTriggers( yield call(postMessageSaga, trigger); break; default: - log.error("Trigger type unknown", trigger); + log.error("Trigger type unknown", trigger, source); throw Error("Trigger type unknown"); } diff --git a/app/client/src/components/editorComponents/ApiResponseView.tsx b/app/client/src/components/editorComponents/ApiResponseView.tsx index ef01cbd7d9ce..b956d0fa095d 100644 --- a/app/client/src/components/editorComponents/ApiResponseView.tsx +++ b/app/client/src/components/editorComponents/ApiResponseView.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from "react"; +import React, { useCallback, useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import type { ActionResponse } from "api/ActionAPI"; import { @@ -17,7 +17,7 @@ import EntityBottomTabs from "./EntityBottomTabs"; import { DEBUGGER_TAB_KEYS } from "./Debugger/constants"; import { getErrorCount } from "selectors/debuggerSelectors"; import { ActionExecutionResizerHeight } from "PluginActionEditor/components/PluginActionResponse/constants"; -import type { Action } from "entities/Action"; +import { PluginType, type Action } from "entities/Action"; import { EMPTY_RESPONSE } from "./emptyResponse"; import { getPluginActionDebuggerState, @@ -58,6 +58,20 @@ function ApiResponseView(props: Props) { const onDebugClick = useDebuggerTriggerClick(); + useEffect( + function openDefaultTabWhenNoTabIsSelected() { + if (currentActionConfig.pluginType === PluginType.API && !selectedTab) { + dispatch( + setPluginActionEditorDebuggerState({ + open: true, + selectedTab: DEBUGGER_TAB_KEYS.RESPONSE_TAB, + }), + ); + } + }, + [selectedTab, dispatch, currentActionConfig.pluginType], + ); + const onRunClick = () => { props.onRunClick(); AnalyticsUtil.logEvent("RESPONSE_TAB_RUN_ACTION_CLICK", { @@ -124,17 +138,17 @@ function ApiResponseView(props: Props) { if (ideViewMode === EditorViewMode.FullScreen) { tabs.push( + { + key: DEBUGGER_TAB_KEYS.LOGS_TAB, + title: createMessage(DEBUGGER_LOGS), + panelComponent: , + }, { key: DEBUGGER_TAB_KEYS.ERROR_TAB, title: createMessage(DEBUGGER_ERRORS), count: errorCount, panelComponent: , }, - { - key: DEBUGGER_TAB_KEYS.LOGS_TAB, - title: createMessage(DEBUGGER_LOGS), - panelComponent: , - }, ); } diff --git a/app/client/src/components/editorComponents/Debugger/DebuggerEntityLink.tsx b/app/client/src/components/editorComponents/Debugger/DebuggerEntityLink.tsx index 222f549ee5c1..744dd8e3cce2 100644 --- a/app/client/src/components/editorComponents/Debugger/DebuggerEntityLink.tsx +++ b/app/client/src/components/editorComponents/Debugger/DebuggerEntityLink.tsx @@ -4,12 +4,30 @@ import type LOG_TYPE from "entities/AppsmithConsole/logtype"; import type { Plugin } from "api/PluginApi"; import { Link } from "@appsmith/ads"; import type { ENTITY_TYPE } from "ee/entities/AppsmithConsole/utils"; +import styled from "styled-components"; +import { getTypographyByKey } from "@appsmith/ads-old"; export enum DebuggerLinkUI { ENTITY_TYPE, ENTITY_NAME, } +const Wrapper = styled.div` + .debugger-entity-link { + ${getTypographyByKey("h6")} + letter-spacing: -0.195px; + color: var(--ads-v2-color-fg-emphasis); + cursor: pointer; + text-decoration-line: underline; + flex-shrink: 0; + width: max-content; + + > span { + ${getTypographyByKey("h6")} + } + } +`; + export type EntityLinkProps = { uiComponent: DebuggerLinkUI; plugin?: Plugin; @@ -48,12 +66,14 @@ export function DebuggerEntityLink(props: { ); case DebuggerLinkUI.ENTITY_NAME: return ( - - {props.name} - + + + {props.name}: + + ); default: return null; diff --git a/app/client/src/components/editorComponents/Debugger/DebuggerLogs.tsx b/app/client/src/components/editorComponents/Debugger/DebuggerLogs.tsx index a6e6d6d2100c..2fd597951b30 100644 --- a/app/client/src/components/editorComponents/Debugger/DebuggerLogs.tsx +++ b/app/client/src/components/editorComponents/Debugger/DebuggerLogs.tsx @@ -5,7 +5,7 @@ import { get, isUndefined } from "lodash"; import { LOG_CATEGORY, Severity } from "entities/AppsmithConsole"; import FilterHeader from "./FilterHeader"; import { BlankState } from "./helpers"; -import LogItem, { getLogItemProps } from "./LogItem"; +import { LogItem, getLogItemProps } from "./LogItem"; import { usePagination, useFilteredLogs } from "./hooks/debuggerHooks"; import { createMessage, @@ -24,13 +24,14 @@ import type { IconName } from "@blueprintjs/core"; import AnalyticsUtil from "ee/utils/AnalyticsUtil"; import { getDebuggerSelectedFilter } from "selectors/debuggerSelectors"; import { setDebuggerSelectedFilter } from "actions/debuggerActions"; +import { BOTTOM_BAR_HEIGHT } from "components/BottomBar/constants"; -export const LIST_HEADER_HEIGHT = "38px"; +export const LIST_HEADER_HEIGHT = "52px"; export const FOOTER_MARGIN = "40px"; const ContainerWrapper = styled.div` overflow: hidden; - height: 100%; + height: calc(100% - ${BOTTOM_BAR_HEIGHT}px); `; export const ListWrapper = styled.div` @@ -38,7 +39,6 @@ export const ListWrapper = styled.div` overflow: auto; height: calc(100% - ${LIST_HEADER_HEIGHT}); ${thinScrollbar}; - padding-bottom: 37px; `; interface Props { diff --git a/app/client/src/components/editorComponents/Debugger/DebuggerTabs.tsx b/app/client/src/components/editorComponents/Debugger/DebuggerTabs.tsx index 60115c31c370..9c3af9b3c288 100644 --- a/app/client/src/components/editorComponents/Debugger/DebuggerTabs.tsx +++ b/app/client/src/components/editorComponents/Debugger/DebuggerTabs.tsx @@ -13,12 +13,10 @@ import { } from "selectors/debuggerSelectors"; import AnalyticsUtil from "ee/utils/AnalyticsUtil"; import Errors from "./Errors"; -import EntityDeps from "./EntityDependecies"; import { createMessage, DEBUGGER_ERRORS, DEBUGGER_LOGS, - INSPECT_ENTITY, } from "ee/constants/messages"; import { DEBUGGER_TAB_KEYS } from "./constants"; import EntityBottomTabs from "../EntityBottomTabs"; @@ -48,21 +46,16 @@ function DebuggerTabs() { const onClose = () => dispatch(showDebugger(false)); const DEBUGGER_TABS = [ - { - 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: , + key: DEBUGGER_TAB_KEYS.ERROR_TAB, + title: createMessage(DEBUGGER_ERRORS), + count: errorCount, + panelComponent: , }, ]; diff --git a/app/client/src/components/editorComponents/Debugger/ErrorLogs/ErrorLog.tsx b/app/client/src/components/editorComponents/Debugger/ErrorLogs/ErrorLog.tsx index b1f4e68839a7..7813d54b95a2 100644 --- a/app/client/src/components/editorComponents/Debugger/ErrorLogs/ErrorLog.tsx +++ b/app/client/src/components/editorComponents/Debugger/ErrorLogs/ErrorLog.tsx @@ -10,10 +10,11 @@ import type { Log } from "entities/AppsmithConsole"; import { setResponsePaneScrollPosition } from "actions/debuggerActions"; import { useDispatch, useSelector } from "react-redux"; import { getScrollPosition } from "selectors/debuggerSelectors"; +import { BOTTOM_BAR_HEIGHT } from "components/BottomBar/constants"; const ContainerWrapper = styled.div` overflow: hidden; - height: 100%; + height: calc(100% - ${BOTTOM_BAR_HEIGHT}px); `; const ListWrapper = styled.div` @@ -21,7 +22,6 @@ const ListWrapper = styled.div` overflow: auto; ${thinScrollbar}; height: 100%; - padding-bottom: 37px; `; // This component is used to render the error logs in the debugger. diff --git a/app/client/src/components/editorComponents/Debugger/ErrorLogs/ErrorLogItem.tsx b/app/client/src/components/editorComponents/Debugger/ErrorLogs/ErrorLogItem.tsx index 96679de82524..2d0eadeeff60 100644 --- a/app/client/src/components/editorComponents/Debugger/ErrorLogs/ErrorLogItem.tsx +++ b/app/client/src/components/editorComponents/Debugger/ErrorLogs/ErrorLogItem.tsx @@ -9,7 +9,6 @@ import type { PluginErrorDetails } from "api/ActionAPI"; import LogCollapseData from "./components/LogCollapseData"; import LogAdditionalInfo from "./components/LogAdditionalInfo"; import LogEntityLink from "./components/LogEntityLink"; -import LogTimeStamp from "./components/LogTimeStamp"; import { getLogIcon } from "../helpers"; import AnalyticsUtil from "ee/utils/AnalyticsUtil"; import moment from "moment"; @@ -49,6 +48,7 @@ const Wrapper = styled.div<{ collapsed: boolean }>` ? `transform: rotate(-90deg);` : `transform: rotate(0deg); `}; } + .debugger-time { ${getTypographyByKey("h6")} letter-spacing: -0.24px; @@ -61,6 +61,7 @@ const Wrapper = styled.div<{ collapsed: boolean }>` .debugger-error-type { ${getTypographyByKey("h6")} + font-weight: var(--ads-v2-font-weight-normal); letter-spacing: -0.24px; color: var(--ads-v2-color-fg); flex-shrink: 0; @@ -85,6 +86,7 @@ const Wrapper = styled.div<{ collapsed: boolean }>` -ms-user-select: all; /* No support yet */ user-select: all; /* Likely future */ } + .debugger-entity { color: var(--ads-v2-color-fg); ${getTypographyByKey("h6")} @@ -98,21 +100,6 @@ const Wrapper = styled.div<{ collapsed: boolean }>` } } } - - .debugger-entity-link { - // TODO: unclear why this file and LogItem.tsx have different styles when they look so similar - ${getTypographyByKey("h6")} - font-weight: 400; - letter-spacing: -0.195px; - color: var(--ads-v2-color-fg-emphasis); - cursor: pointer; - text-decoration-line: underline; - flex-shrink: 0; - width: max-content; - > span { - font-size: 12px; - } - } `; const FlexWrapper = styled.div` @@ -215,6 +202,12 @@ const ErrorLogItem = (props: LogItemProps) => { const { collapsable } = props; + const errorType = props.messages && props.messages[0].message.name; + + const errorTitle = props.pluginErrorDetails + ? props.pluginErrorDetails.title + : props.messages && props.messages[0].message.message; + return ( { name={props.icon} size="md" /> - - {props.logType && - props.logType !== LOG_TYPE.LINT_ERROR && - props.messages && - props.messages[0].message.name !== "SyntaxError" && ( - - )} {collapsable && props.logType !== LOG_TYPE.LINT_ERROR && ( ); } diff --git a/app/client/src/components/editorComponents/Debugger/styles.ts b/app/client/src/components/editorComponents/Debugger/styles.ts deleted file mode 100644 index 52721305f4f8..000000000000 --- a/app/client/src/components/editorComponents/Debugger/styles.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Button } from "@appsmith/ads"; -import styled from "styled-components"; - -export const DebuggerTriggerButton = styled(Button)` - /* Override the min-width of the button for debugger trigger only */ - - .ads-v2-button__content { - min-width: unset; - } -`; diff --git a/app/client/src/components/editorComponents/JSResponseView.tsx b/app/client/src/components/editorComponents/JSResponseView.tsx index 96d5fbbf006d..1f01151fb09d 100644 --- a/app/client/src/components/editorComponents/JSResponseView.tsx +++ b/app/client/src/components/editorComponents/JSResponseView.tsx @@ -3,7 +3,6 @@ import { connect, useDispatch, useSelector } from "react-redux"; import type { RouteComponentProps } from "react-router"; import { withRouter } from "react-router"; import styled from "styled-components"; -import { every, includes } from "lodash"; import type { AppState } from "ee/reducers"; import type { JSEditorRouteParams } from "constants/routes"; import { @@ -28,16 +27,11 @@ import type { BottomTab } from "./EntityBottomTabs"; import EntityBottomTabs from "./EntityBottomTabs"; import { getIsSavingEntity } from "selectors/editorSelectors"; import { getJSResponseViewState, JSResponseState } from "./utils"; -import { getFilteredErrors } from "selectors/debuggerSelectors"; import { NoResponse } from "PluginActionEditor/components/PluginActionResponse/components/NoResponse"; import { ResponseTabErrorContainer, ResponseTabErrorContent, } from "PluginActionEditor/components/PluginActionResponse/components/ApiResponse"; -import LogHelper from "./Debugger/ErrorLogs/components/LogHelper"; -import LOG_TYPE from "entities/AppsmithConsole/logtype"; -import type { Log, SourceEntity } from "entities/AppsmithConsole"; -import { ENTITY_TYPE } from "ee/entities/AppsmithConsole/utils"; import { getJsPaneDebuggerState } from "selectors/jsPaneSelectors"; import { setJsPaneDebuggerState } from "actions/jsPaneActions"; import { getIDEViewMode } from "selectors/ideSelectors"; @@ -80,13 +74,11 @@ type Props = ReduxStateProps & isLoading: boolean; onButtonClick: (e: React.MouseEvent) => void; jsCollectionData: JSCollectionData | undefined; - debuggerLogsDefaultName?: string; }; function JSResponseView(props: Props) { const { currentFunction, - debuggerLogsDefaultName, disabled, errorCount, errors, @@ -97,7 +89,6 @@ function JSResponseView(props: Props) { const [responseStatus, setResponseStatus] = useState( JSResponseState.NoResponse, ); - const jsObject = jsCollectionData?.config; const responses = (jsCollectionData && jsCollectionData.data) || {}; const isDirty = (jsCollectionData && jsCollectionData.isDirty) || {}; const isExecuting = (jsCollectionData && jsCollectionData.isExecuting) || {}; @@ -124,10 +115,6 @@ function JSResponseView(props: Props) { ); }, [responses, isExecuting, currentFunction, isSaving, isDirty]); - const filteredErrors = useSelector(getFilteredErrors); - let errorMessage: string | undefined; - let errorType = "ValidationError"; - const localExecutionAllowed = useMemo(() => { return isBrowserExecutionAllowed( jsCollectionData?.config, @@ -135,58 +122,6 @@ function JSResponseView(props: Props) { ); }, [jsCollectionData?.config, currentFunction]); - // action source for analytics. - let actionSource: SourceEntity = { - type: ENTITY_TYPE.JSACTION, - name: "", - id: "", - }; - - try { - let errorObject: Log | undefined; - - //get JS execution error from redux store. - if ( - jsCollectionData && - jsCollectionData.config && - jsCollectionData.activeJSActionId - ) { - every(filteredErrors, (error) => { - if ( - includes( - error.id, - jsCollectionData?.config.id + - "-" + - jsCollectionData?.activeJSActionId, - ) - ) { - errorObject = error; - - return false; - } - - return true; - }); - } - - // update error message. - if (errorObject) { - if (errorObject.source) { - // update action source. - actionSource = errorObject.source; - } - - if (errorObject.messages) { - // update error message. - errorMessage = - errorObject.messages[0].message.name + - ": " + - errorObject.messages[0].message.message; - errorType = errorObject.messages[0].message.name; - } - } - } catch (e) {} - const ideViewMode = useSelector(getIDEViewMode); const tabs: BottomTab[] = [ @@ -196,19 +131,12 @@ function JSResponseView(props: Props) { panelComponent: ( <> {localExecutionAllowed && - (hasExecutionParseErrors || - (hasJSObjectParseError && errorMessage)) && ( + (hasExecutionParseErrors || hasJSObjectParseError) && (
- {errorMessage} + Function failed to execute. Check logs for more information.
- -
)} @@ -271,9 +199,7 @@ function JSResponseView(props: Props) { { key: DEBUGGER_TAB_KEYS.LOGS_TAB, title: createMessage(DEBUGGER_LOGS), - panelComponent: ( - - ), + panelComponent: , }, ]; diff --git a/app/client/src/entities/AppsmithConsole/logtype.ts b/app/client/src/entities/AppsmithConsole/logtype.ts index b810a24f2f8d..b81f013242bf 100644 --- a/app/client/src/entities/AppsmithConsole/logtype.ts +++ b/app/client/src/entities/AppsmithConsole/logtype.ts @@ -11,6 +11,7 @@ enum LOG_TYPE { JS_ACTION_UPDATE, JS_PARSE_ERROR, JS_PARSE_SUCCESS, + JS_EXECUTION_ERROR, CYCLIC_DEPENDENCY_ERROR, LINT_ERROR, MISSING_MODULE, diff --git a/app/client/src/pages/Editor/DataSourceEditor/Debugger.tsx b/app/client/src/pages/Editor/DataSourceEditor/Debugger.tsx index 943bf78b5adb..708334ef5365 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/Debugger.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/Debugger.tsx @@ -5,7 +5,6 @@ import { createMessage, DEBUGGER_ERRORS, DEBUGGER_LOGS, - INSPECT_ENTITY, } from "ee/constants/messages"; import { setDebuggerSelectedTab, @@ -16,7 +15,6 @@ import EntityBottomTabs from "components/editorComponents/EntityBottomTabs"; import { DEBUGGER_TAB_KEYS } from "components/editorComponents/Debugger/constants"; import Errors from "components/editorComponents/Debugger/Errors"; import DebuggerLogs from "components/editorComponents/Debugger/DebuggerLogs"; -import EntityDeps from "components/editorComponents/Debugger/EntityDependecies"; import { getDebuggerSelectedTab, getErrorCount, @@ -89,21 +87,16 @@ export default function Debugger() { // define the tabs for the debugger const DEBUGGER_TABS = [ - { - 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: , + key: DEBUGGER_TAB_KEYS.ERROR_TAB, + title: createMessage(DEBUGGER_ERRORS), + count: errorCount, + panelComponent: , }, ]; diff --git a/app/client/src/pages/Editor/IDE/EditorPane/Query/QueryRender.test.tsx b/app/client/src/pages/Editor/IDE/EditorPane/Query/QueryRender.test.tsx index 65314ae7a727..28c400face79 100644 --- a/app/client/src/pages/Editor/IDE/EditorPane/Query/QueryRender.test.tsx +++ b/app/client/src/pages/Editor/IDE/EditorPane/Query/QueryRender.test.tsx @@ -144,7 +144,7 @@ describe("IDE URL rendering of Queries", () => { }, }); - const { getAllByText, getByRole, getByTestId } = render( + const { getAllByText, getByRole, getByTestId, queryAllByRole } = render( , @@ -179,7 +179,7 @@ describe("IDE URL rendering of Queries", () => { // Check if the params tabs is visible getByRole("tab", { name: /params/i }); // Check if run button is visible - getByRole("button", { name: /run/i }); + expect(queryAllByRole("button", { name: /run/i }).length).toBe(2); // Check if the Add new button is shown getByTestId("t--add-item"); }); @@ -201,7 +201,7 @@ describe("IDE URL rendering of Queries", () => { ideView: EditorViewMode.SplitScreen, }); - const { getAllByText, getByRole, getByTestId } = render( + const { getAllByText, getByTestId, queryAllByRole } = render( , @@ -225,7 +225,7 @@ describe("IDE URL rendering of Queries", () => { // Check if the form is rendered getByTestId("t--action-form-API"); // Check if run button is visible - getByRole("button", { name: /run/i }); + expect(queryAllByRole("button", { name: /run/i }).length).toBe(2); // Check if the Add new button is shown getByTestId("t--ide-tabs-add-button"); }); diff --git a/app/client/src/pages/Editor/QueryEditor/QueryDebuggerTabs.tsx b/app/client/src/pages/Editor/QueryEditor/QueryDebuggerTabs.tsx index eb5171787a16..ce53314a5433 100644 --- a/app/client/src/pages/Editor/QueryEditor/QueryDebuggerTabs.tsx +++ b/app/client/src/pages/Editor/QueryEditor/QueryDebuggerTabs.tsx @@ -183,17 +183,17 @@ function QueryDebuggerTabs({ if (ideViewMode === EditorViewMode.FullScreen) { responseTabs.push( + { + key: DEBUGGER_TAB_KEYS.LOGS_TAB, + title: createMessage(DEBUGGER_LOGS), + panelComponent: , + }, { key: DEBUGGER_TAB_KEYS.ERROR_TAB, title: createMessage(DEBUGGER_ERRORS), count: errorCount, panelComponent: , }, - { - key: DEBUGGER_TAB_KEYS.LOGS_TAB, - title: createMessage(DEBUGGER_LOGS), - panelComponent: , - }, ); } diff --git a/app/client/src/sagas/ActionExecution/CopyActionSaga.ts b/app/client/src/sagas/ActionExecution/CopyActionSaga.ts index 1783b724cc4f..2dd161cf7ffb 100644 --- a/app/client/src/sagas/ActionExecution/CopyActionSaga.ts +++ b/app/client/src/sagas/ActionExecution/CopyActionSaga.ts @@ -3,8 +3,12 @@ import AppsmithConsole from "utils/AppsmithConsole"; import { ActionValidationError } from "sagas/ActionExecution/errorUtils"; import { getType, Types } from "utils/TypeHelpers"; import type { TCopyToClipboardDescription } from "workers/Evaluation/fns/copyToClipboard"; +import type { SourceEntity } from "../../entities/AppsmithConsole"; -export default function copySaga(action: TCopyToClipboardDescription) { +export default function copySaga( + action: TCopyToClipboardDescription, + source?: SourceEntity, +) { const { payload } = action; if (typeof payload.data !== "string") { @@ -20,7 +24,12 @@ export default function copySaga(action: TCopyToClipboardDescription) { if (result) { AppsmithConsole.info({ - text: `copyToClipboard('${payload.data}') was triggered`, + source, + text: `copyToClipboard triggered`, + state: { + data: payload.data, + options: payload.options, + }, }); } } diff --git a/app/client/src/sagas/ActionExecution/DownloadActionSaga.ts b/app/client/src/sagas/ActionExecution/DownloadActionSaga.ts index e336992fda19..892bfed7b579 100644 --- a/app/client/src/sagas/ActionExecution/DownloadActionSaga.ts +++ b/app/client/src/sagas/ActionExecution/DownloadActionSaga.ts @@ -6,6 +6,7 @@ import { TriggerFailureError } from "sagas/ActionExecution/errorUtils"; import { isBase64String, isUrlString } from "./downloadActionUtils"; import { isBlobUrl } from "utils/AppsmithUtils"; import type { TDownloadDescription } from "workers/Evaluation/fns/download"; +import type { SourceEntity } from "../../entities/AppsmithConsole"; function downloadBlobURL(url: string, name: string) { const ele = document.createElement("a"); @@ -21,7 +22,10 @@ function downloadBlobURL(url: string, name: string) { }); } -export default async function downloadSaga(action: TDownloadDescription) { +export default async function downloadSaga( + action: TDownloadDescription, + source?: SourceEntity, +) { const { payload } = action; const { data, name, type } = payload; @@ -32,7 +36,13 @@ export default async function downloadSaga(action: TDownloadDescription) { if (isBlobUrl(data)) { downloadBlobURL(data, name); AppsmithConsole.info({ - text: `download('${data}', '${name}', '${type}') was triggered`, + source, + text: `download triggered`, + state: { + data: "blob", + name, + type, + }, }); return; @@ -45,7 +55,13 @@ export default async function downloadSaga(action: TDownloadDescription) { downloadjs(jsonString, name, type); AppsmithConsole.info({ - text: `download('${jsonString}', '${name}', '${type}') was triggered`, + source, + text: `download triggered`, + state: { + data: jsonString, + name, + type, + }, }); } else if (isUrlString(data)) { // In the event that a url string is supplied, we need to fetch the image with the response type arraybuffer. @@ -53,7 +69,13 @@ export default async function downloadSaga(action: TDownloadDescription) { Axios.get(data, { responseType: "arraybuffer" }).then((res) => { downloadjs(res.data, name, type); AppsmithConsole.info({ - text: `download('${data}', '${name}', '${type}') was triggered`, + source, + text: `download triggered`, + state: { + data: "file", + name, + type, + }, }); }); } else if (isBase64String(data)) { @@ -62,13 +84,25 @@ export default async function downloadSaga(action: TDownloadDescription) { }).then((res) => { downloadjs(res.data, name, type); AppsmithConsole.info({ - text: `download('${data}', '${name}', '${type}') was triggered`, + source, + text: `download triggered`, + state: { + data: "file", + name, + type, + }, }); }); } else { downloadjs(data, name, type); AppsmithConsole.info({ - text: `download('${data}', '${name}', '${type}') was triggered`, + source, + text: `download triggered`, + state: { + data, + name, + type, + }, }); } } diff --git a/app/client/src/sagas/ActionExecution/ModalSagas.ts b/app/client/src/sagas/ActionExecution/ModalSagas.ts index 18870fc90309..62cb2081de41 100644 --- a/app/client/src/sagas/ActionExecution/ModalSagas.ts +++ b/app/client/src/sagas/ActionExecution/ModalSagas.ts @@ -6,8 +6,12 @@ import type { TCloseModalDescription, TShowModalDescription, } from "workers/Evaluation/fns/modalFns"; +import type { SourceEntity } from "entities/AppsmithConsole"; -export function* openModalSaga(action: TShowModalDescription) { +export function* openModalSaga( + action: TShowModalDescription, + source: SourceEntity, +) { const { modalName } = action.payload; if (typeof modalName !== "string") { @@ -21,11 +25,18 @@ export function* openModalSaga(action: TShowModalDescription) { yield put(action); AppsmithConsole.info({ - text: `showModal('${modalName ?? ""}') was triggered`, + source, + text: `showModal triggered`, + state: { + modalName, + }, }); } -export function* closeModalSaga(action: TCloseModalDescription) { +export function* closeModalSaga( + action: TCloseModalDescription, + source: SourceEntity, +) { const { modalName } = action.payload; if (typeof modalName !== "string") { @@ -39,6 +50,10 @@ export function* closeModalSaga(action: TCloseModalDescription) { yield put(action); AppsmithConsole.info({ - text: `closeModal(${modalName}) was triggered`, + source, + text: `closeModal triggered`, + state: { + modalName, + }, }); } diff --git a/app/client/src/sagas/ActionExecution/NavigateActionSaga.ts b/app/client/src/sagas/ActionExecution/NavigateActionSaga.ts index 1d828e65791f..7c4df1d906ac 100644 --- a/app/client/src/sagas/ActionExecution/NavigateActionSaga.ts +++ b/app/client/src/sagas/ActionExecution/NavigateActionSaga.ts @@ -15,6 +15,7 @@ import { TriggerFailureError } from "./errorUtils"; import { isValidURL, matchesURLPattern } from "utils/URLUtils"; import type { TNavigateToDescription } from "workers/Evaluation/fns/navigateTo"; import { NavigationTargetType } from "workers/Evaluation/fns/navigateTo"; +import type { SourceEntity } from "entities/AppsmithConsole"; export enum NavigationTargetType_Dep { SAME_WINDOW = "SAME_WINDOW", @@ -28,7 +29,10 @@ const isValidPageName = ( return _.find(pageList, (page: Page) => page.pageName === pageNameOrUrl); }; -export default function* navigateActionSaga(action: TNavigateToDescription) { +export default function* navigateActionSaga( + action: TNavigateToDescription, + source?: SourceEntity, +) { const { payload } = action; const pageList: Page[] = yield select(getPageList); const { pageNameOrUrl, params, target } = payload; @@ -69,8 +73,10 @@ export default function* navigateActionSaga(action: TNavigateToDescription) { } AppsmithConsole.info({ - text: `navigateTo('${page.pageName}') was triggered`, + source, + text: `navigateTo triggered`, state: { + page, params, }, }); diff --git a/app/client/src/sagas/ActionExecution/PluginActionSaga.ts b/app/client/src/sagas/ActionExecution/PluginActionSaga.ts index 59c312ba893d..1eeaed39a8aa 100644 --- a/app/client/src/sagas/ActionExecution/PluginActionSaga.ts +++ b/app/client/src/sagas/ActionExecution/PluginActionSaga.ts @@ -62,6 +62,7 @@ import { isString, set, unset, + zipObject, } from "lodash"; import AppsmithConsole from "utils/AppsmithConsole"; import { ENTITY_TYPE, PLATFORM_ERROR } from "ee/entities/AppsmithConsole/utils"; @@ -172,6 +173,7 @@ import { isActionSaving, setPluginActionEditorDebuggerState, } from "PluginActionEditor/store"; +import { objectKeys } from "@appsmith/utils"; enum ActionResponseDataTypes { BINARY = "BINARY", @@ -284,6 +286,10 @@ function* readBlob(blobUrl: string): any { * - binds dataype to payload * * @param value + * @param executeActionRequest + * @param index + * @param isArray + * @param arrDatatype */ function* resolvingBlobUrls( @@ -308,7 +314,7 @@ function* resolvingBlobUrls( if (isTrueObject(value)) { const blobUrlPaths: string[] = []; - Object.keys(value).forEach((propertyName) => { + objectKeys(value).forEach((propertyName) => { if (isBlobUrl(value[propertyName])) { blobUrlPaths.push(propertyName); } @@ -392,6 +398,9 @@ function updateBlobDataFromUrls( * { key: "this.params.age", value: 26 }, * ] * @param bindings + * @param formData + * @param executeActionRequest + * @param filePickerInstrumentation * @param executionParams */ function* evaluateActionParams( @@ -415,15 +424,14 @@ function* evaluateActionParams( const bindingsMap: Record = {}; const bindingBlob = []; + const evaluatedParams = zipObject(bindings, values); // Maintain a blob data map to resolve blob urls of large files as array buffer const blobDataMap: Record = {}; - let recordFilePickerInstrumentation = false; - // if json bindings have filepicker reference, we need to init the instrumentation object // which we will send post execution - recordFilePickerInstrumentation = bindings.some((binding) => + const recordFilePickerInstrumentation = bindings.some((binding) => binding.includes(".files"), ); @@ -466,6 +474,7 @@ function* evaluateActionParams( ); useBlobMaps = true; unset(newVal, "blobUrlPaths"); + evaluatedParams[key] = "blob"; } tempArr.push(newVal); @@ -477,6 +486,7 @@ function* evaluateActionParams( filePickerInstrumentation["totalSize"] += size; filePickerInstrumentation["fileSizes"].push(size); filePickerInstrumentation["fileTypes"].push(type); + evaluatedParams[key] = "file"; } if ((j + 1) % BATCH_CHUNK_SIZE === 0) { @@ -499,6 +509,7 @@ function* evaluateActionParams( filePickerInstrumentation["totalSize"] += value.size; filePickerInstrumentation["fileSizes"].push(value.size); filePickerInstrumentation["fileTypes"].push(value.type); + evaluatedParams[key] = "file"; } } @@ -508,9 +519,11 @@ function* evaluateActionParams( if (!!value && value.hasOwnProperty("blobUrlPaths")) { updateBlobDataFromUrls(value.blobUrlPaths, value, blobMap, blobDataMap); unset(value, "blobUrlPaths"); + evaluatedParams[key] = "blob"; } value = JSON.stringify(value); + evaluatedParams[key] = value; } // If there are no blob urls in the value, we can directly add it to the formData @@ -543,6 +556,8 @@ function* evaluateActionParams( formData.append(path, blobData), ); } + + return evaluatedParams; } export default function* executePluginActionTriggerSaga( @@ -598,15 +613,6 @@ export default function* executePluginActionTriggerSaga( ? "PREV" : undefined; - AppsmithConsole.info({ - text: "Execution started from widget request", - source: { - type: ENTITY_TYPE.ACTION, - name: pluginActionNameToDisplay, - id: actionId, - }, - state: action.actionConfiguration, - }); const executePluginActionResponse: ExecutePluginActionResponse = yield call( executePluginActionSaga, action, @@ -624,7 +630,7 @@ export default function* executePluginActionTriggerSaga( id: actionId, iconId: action.pluginId, logType: LOG_TYPE.ACTION_EXECUTION_ERROR, - text: `Execution failed with status ${payload.statusCode}`, + text: `Failed execution in ${payload.duration}(ms)`, environmentName: currentEnvDetails.name, source: { type: ENTITY_TYPE.ACTION, @@ -633,21 +639,12 @@ export default function* executePluginActionTriggerSaga( httpMethod: action?.actionConfiguration?.httpMethod, pluginType: action.pluginType, }, - state: payload.request, - messages: [ - { - // Need to stringify cause this gets rendered directly - // and rendering objects can crash the app - message: { - name: "PluginExecutionError", - message: !isString(payload.body) - ? JSON.stringify(payload.body) - : payload.body, - }, - type: PLATFORM_ERROR.PLUGIN_EXECUTION, - subType: payload.errorType, - }, - ], + state: { + error: !isString(payload.body) + ? JSON.stringify(payload.body) + : payload.body, + request: payload.request, + }, pluginErrorDetails: payload.pluginErrorDetails, }, }, @@ -672,8 +669,7 @@ export default function* executePluginActionTriggerSaga( AnalyticsUtil.logEvent("EXECUTE_ACTION_SUCCESS", actionExecutionAnalytics); AppsmithConsole.info({ logType: LOG_TYPE.ACTION_EXECUTION_SUCCESS, - text: "Executed successfully from widget request", - timeTaken: payload.duration, + text: `Successfully executed in ${payload.duration}(ms)`, source: { type: ENTITY_TYPE.ACTION, name: pluginActionNameToDisplay, @@ -784,28 +780,6 @@ export function* runActionSaga( ); const pageName: string = yield select(getCurrentPageNameByActionId, actionId); - const datasourceUrl = get( - actionObject, - "datasource.datasourceConfiguration.url", - ); - - AppsmithConsole.info({ - text: "Execution started from user request", - source: { - type: ENTITY_TYPE.ACTION, - name: actionObject.name, - id: actionId, - }, - state: { - ...actionObject.actionConfiguration, - ...(datasourceUrl - ? { - url: datasourceUrl, - } - : null), - }, - }); - const { paginationField } = reduxAction.payload; // open response tab in debugger on exection of action. @@ -953,9 +927,7 @@ export function* runActionSaga( iconId: actionObject.pluginId, logType: LOG_TYPE.ACTION_EXECUTION_ERROR, environmentName: currentEnvDetails.name, - text: `Execution failed${ - payload.statusCode ? ` with status ${payload.statusCode}` : "" - }`, + text: `Failed execution in ${payload.duration}(ms)`, source: { type: ENTITY_TYPE.ACTION, name: pluginActionNameToDisplay, @@ -963,8 +935,10 @@ export function* runActionSaga( httpMethod: actionObject?.actionConfiguration?.httpMethod, pluginType: actionObject.pluginType, }, - messages: appsmithConsoleErrorMessageList, - state: payload?.request, + state: { + error: error.message, + request: payload.request, + }, pluginErrorDetails: payload?.pluginErrorDetails, }, }, @@ -1019,8 +993,7 @@ export function* runActionSaga( if (payload.isExecutionSuccess) { AppsmithConsole.info({ logType: LOG_TYPE.ACTION_EXECUTION_SUCCESS, - text: "Executed successfully from user request", - timeTaken: payload.duration, + text: `Successfully executed in ${payload.duration}(ms)`, source: { type: ENTITY_TYPE.ACTION, name: pluginActionNameToDisplay, @@ -1222,7 +1195,7 @@ function* executePageLoadAction( iconId: action.pluginId, logType: LOG_TYPE.ACTION_EXECUTION_ERROR, environmentName: currentEnvDetails.name, - text: `Execution failed with status ${payload.statusCode}`, + text: `Failed execution in ${payload.duration}(ms)`, source: { type: ENTITY_TYPE.ACTION, name: actionName, @@ -1230,14 +1203,12 @@ function* executePageLoadAction( httpMethod: action?.actionConfiguration?.httpMethod, pluginType: action.pluginType, }, - state: payload.request, - messages: [ - { - message: error, - type: PLATFORM_ERROR.PLUGIN_EXECUTION, - subType: payload.errorType, - }, - ], + state: { + error: + payload.pluginErrorDetails?.downstreamErrorMessage || + error.message, + request: payload.request, + }, pluginErrorDetails: payload.pluginErrorDetails, }, }, @@ -1423,7 +1394,7 @@ function* executePluginActionSaga( fileSizes: [], }; - yield call( + const evaluatedBindings: Record = yield call( evaluateActionParams, pluginAction.jsonPathKeys, formData, @@ -1432,6 +1403,16 @@ function* executePluginActionSaga( params, ); + AppsmithConsole.info({ + text: "Began execution", + source: { + type: ENTITY_TYPE.ACTION, + name: pluginAction.name, + id: actionId, + }, + state: { requestParams: { ...params, ...evaluatedBindings } }, + }); + let payload = EMPTY_RESPONSE; let response: ActionExecutionResponse; diff --git a/app/client/src/sagas/ActionExecution/ResetWidgetActionSaga.ts b/app/client/src/sagas/ActionExecution/ResetWidgetActionSaga.ts index 8b577f24e9ee..549868c88043 100644 --- a/app/client/src/sagas/ActionExecution/ResetWidgetActionSaga.ts +++ b/app/client/src/sagas/ActionExecution/ResetWidgetActionSaga.ts @@ -11,12 +11,14 @@ import type { FlattenedWidgetProps } from "WidgetProvider/constants"; import { ReduxActionTypes } from "ee/constants/ReduxActionConstants"; import type { TResetWidgetDescription } from "workers/Evaluation/fns/resetWidget"; import AppsmithConsole from "utils/AppsmithConsole"; +import type { SourceEntity } from "../../entities/AppsmithConsole"; export default function* resetWidgetActionSaga( action: TResetWidgetDescription, + source?: SourceEntity, ) { const { payload } = action; - const { metaUpdates, widgetName } = payload; + const { metaUpdates, resetChildren, widgetName } = payload; if (getType(widgetName) !== Types.STRING) { throw new ActionValidationError( @@ -39,7 +41,13 @@ export default function* resetWidgetActionSaga( yield put(resetWidgetMetaUpdates(metaUpdates)); yield take(ReduxActionTypes.RESET_WIDGET_META_EVALUATED); + AppsmithConsole.info({ - text: `resetWidget('${payload.widgetName}', ${payload.resetChildren}) was triggered`, + source, + text: `resetWidget triggered`, + state: { + widgetName, + resetChildren, + }, }); } diff --git a/app/client/src/sagas/ActionExecution/ShowAlertActionSaga.ts b/app/client/src/sagas/ActionExecution/ShowAlertActionSaga.ts index c2011f5c7845..bd848a5ceebe 100644 --- a/app/client/src/sagas/ActionExecution/ShowAlertActionSaga.ts +++ b/app/client/src/sagas/ActionExecution/ShowAlertActionSaga.ts @@ -6,8 +6,12 @@ import type { TShowAlertDescription } from "workers/Evaluation/fns/showAlert"; import { call } from "redux-saga/effects"; import showToast from "sagas/ToastSagas"; import { uniqueId } from "lodash"; +import type { SourceEntity } from "entities/AppsmithConsole"; -export default function* showAlertSaga(action: TShowAlertDescription) { +export default function* showAlertSaga( + action: TShowAlertDescription, + source?: SourceEntity, +) { const { payload } = action; if (typeof payload.message !== "string") { @@ -30,8 +34,11 @@ export default function* showAlertSaga(action: TShowAlertDescription) { { forceDisplay: true }, ); AppsmithConsole.info({ - text: payload.style - ? `showAlert('${payload.message}', '${payload.style}') was triggered` - : `showAlert('${payload.message}') was triggered`, + source: source, + text: "showAlert triggered", + state: { + message: payload.message, + style: payload.style, + }, }); } diff --git a/app/client/src/sagas/ActionExecution/StoreActionSaga.ts b/app/client/src/sagas/ActionExecution/StoreActionSaga.ts index 330faf673c5b..607869552e02 100644 --- a/app/client/src/sagas/ActionExecution/StoreActionSaga.ts +++ b/app/client/src/sagas/ActionExecution/StoreActionSaga.ts @@ -8,7 +8,6 @@ import { getCurrentGitBranch } from "selectors/gitSyncSelectors"; import { getCurrentApplicationId } from "selectors/editorSelectors"; import type { AppStoreState } from "reducers/entityReducers/appReducer"; import { Severity, LOG_CATEGORY } from "entities/AppsmithConsole"; -import moment from "moment"; import type { TClearStoreDescription, TRemoveValueDescription, @@ -63,7 +62,7 @@ export function* handleStoreOperations(triggers: StoreOperation[]) { text, severity: Severity.INFO, category: LOG_CATEGORY.PLATFORM_GENERATED, - timestamp: moment().format("HH:mm:ss"), + timestamp: Date.now().toString(), isExpanded: false, })), ); diff --git a/app/client/src/sagas/ActionExecution/errorUtils.ts b/app/client/src/sagas/ActionExecution/errorUtils.ts index 822c6e5d0228..48196b1ab4ab 100644 --- a/app/client/src/sagas/ActionExecution/errorUtils.ts +++ b/app/client/src/sagas/ActionExecution/errorUtils.ts @@ -64,7 +64,7 @@ export function* showToastOnExecutionError( source: "TOAST", }); store.dispatch(showDebugger(true)); - store.dispatch(setDebuggerSelectedTab(DEBUGGER_TAB_KEYS.ERROR_TAB)); + store.dispatch(setDebuggerSelectedTab(DEBUGGER_TAB_KEYS.LOGS_TAB)); } const action = showCTA diff --git a/app/client/src/sagas/DebuggerSagas.ts b/app/client/src/sagas/DebuggerSagas.ts index 09dbae21871c..d131b02674e5 100644 --- a/app/client/src/sagas/DebuggerSagas.ts +++ b/app/client/src/sagas/DebuggerSagas.ts @@ -15,7 +15,7 @@ import type { LogActionPayload, LogObject, } from "entities/AppsmithConsole"; -import { LOG_CATEGORY } from "entities/AppsmithConsole"; +import { LOG_CATEGORY, Severity } from "entities/AppsmithConsole"; import { ENTITY_TYPE } from "ee/entities/AppsmithConsole/utils"; import { all, @@ -284,10 +284,17 @@ function* debuggerLogSaga(action: ReduxAction) { allFormatedLogs.push(formattedLog); } - yield put(addErrorLogs(allFormatedLogs)); yield put(debuggerLog(allFormatedLogs)); } break; + case LOG_TYPE.JS_EXECUTION_ERROR: { + const filteredLogs = payload.filter( + (log) => log.source && log.source.propertyPath && log.text, + ); + + yield put(debuggerLog(filteredLogs)); + break; + } case LOG_TYPE.ACTION_EXECUTION_SUCCESS: { const allFormatedLogs: Log[] = []; @@ -675,17 +682,25 @@ function* deleteDebuggerErrorLogsSaga( // takes a log object array and stores it in the redux store export function* storeLogs(logs: LogObject[]) { AppsmithConsole.addLogs( - logs.map((log: LogObject) => { - return { - text: createLogTitleString(log.data), - logData: log.data, - source: log.source, - severity: log.severity, - timestamp: log.timestamp, - category: LOG_CATEGORY.USER_GENERATED, - isExpanded: false, - }; - }), + logs + .filter((log) => { + if (log.severity === Severity.ERROR) { + return log.source; + } + + return true; + }) + .map((log: LogObject) => { + return { + text: `console.${log.method}(${createLogTitleString(log.data)})`, + logData: log.data, + source: log.source, + severity: log.severity, + timestamp: log.timestamp, + category: LOG_CATEGORY.USER_GENERATED, + isExpanded: false, + }; + }), ); } diff --git a/app/client/src/sagas/EvaluationsSaga.ts b/app/client/src/sagas/EvaluationsSaga.ts index c7b0115ae0eb..eb624fee5c8a 100644 --- a/app/client/src/sagas/EvaluationsSaga.ts +++ b/app/client/src/sagas/EvaluationsSaga.ts @@ -495,7 +495,7 @@ export function* executeJSFunction( // After every function execution, log execution errors if present yield call(handleJSFunctionExecutionErrorLog, action, collection, errors); - return { result, isDirty }; + return { result, isDirty, errors }; } export // TODO: Fix this the next time the file is edited @@ -547,6 +547,7 @@ export const defaultAffectedJSObjects: AffectedJSObjects = { isAllAffected: false, ids: [], }; + export function evalQueueBuffer() { let canTake = false; // TODO: Fix this the next time the file is edited diff --git a/app/client/src/sagas/JSPaneSagas.ts b/app/client/src/sagas/JSPaneSagas.ts index 5d7fcec2b86c..6f4f1f7a2290 100644 --- a/app/client/src/sagas/JSPaneSagas.ts +++ b/app/client/src/sagas/JSPaneSagas.ts @@ -70,6 +70,7 @@ import { JS_EXECUTION_FAILURE, JS_FUNCTION_CREATE_SUCCESS, JS_FUNCTION_DELETE_SUCCESS, + JS_EXECUTION_TRIGGERED, } from "ee/constants/messages"; import { validateResponse } from "./ErrorSagas"; import AppsmithConsole from "utils/AppsmithConsole"; @@ -462,17 +463,27 @@ export function* handleExecuteJSFunctionSaga(data: { collection, ); + AppsmithConsole.info({ + text: createMessage(JS_EXECUTION_TRIGGERED), + source: { + type: ENTITY_TYPE.JSACTION, + name: jsActionPathNameToDisplay, + id: collectionId, + }, + }); + try { const localExecutionAllowed = isBrowserExecutionAllowed(collection, action); let isDirty = false; // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any let result: any = null; + let errors = []; if (localExecutionAllowed) { // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any - const response: { isDirty: false; result: any } = yield call( + const response: { isDirty: false; result: any; errors: any } = yield call( executeJSFunction, action, collection, @@ -481,6 +492,7 @@ export function* handleExecuteJSFunctionSaga(data: { result = response.result; isDirty = response.isDirty; + errors = response.errors; } // open response tab in debugger on runnning or page load js action. @@ -511,15 +523,17 @@ export function* handleExecuteJSFunctionSaga(data: { }); if (localExecutionAllowed) { - AppsmithConsole.info({ - text: createMessage(JS_EXECUTION_SUCCESS), - source: { - type: ENTITY_TYPE.JSACTION, - name: jsActionPathNameToDisplay, - id: collectionId, - }, - state: { response: result }, - }); + if (!errors.length) { + AppsmithConsole.info({ + text: createMessage(JS_EXECUTION_SUCCESS), + source: { + type: ENTITY_TYPE.JSACTION, + name: jsActionPathNameToDisplay, + id: collectionId, + }, + state: { response: result }, + }); + } } else { yield put({ type: ReduxActionTypes.JS_ACTION_REMOTE_EXECUTION_INIT, @@ -546,7 +560,7 @@ export function* handleExecuteJSFunctionSaga(data: { { payload: { id: actionId, - logType: LOG_TYPE.ACTION_EXECUTION_ERROR, + logType: LOG_TYPE.JS_EXECUTION_ERROR, text: createMessage(JS_EXECUTION_FAILURE), source: { type: ENTITY_TYPE.JSACTION, diff --git a/app/client/src/sagas/PostEvaluationSagas.ts b/app/client/src/sagas/PostEvaluationSagas.ts index 1467bc7582f7..7c7f804fdcad 100644 --- a/app/client/src/sagas/PostEvaluationSagas.ts +++ b/app/client/src/sagas/PostEvaluationSagas.ts @@ -39,7 +39,7 @@ import { getCurrentWorkspaceId } from "ee/selectors/selectedWorkspaceSelectors"; import { getInstanceId } from "ee/selectors/tenantSelectors"; import type { EvalTreeResponseData } from "workers/Evaluation/types"; import { endSpan, startRootSpan } from "UITelemetry/generateTraces"; -import { getCollectionNameToDisplay } from "ee/utils/actionExecutionUtils"; +import { getJSActionPathNameToDisplay } from "ee/utils/actionExecutionUtils"; import { showToastOnExecutionError } from "./ActionExecution/errorUtils"; let successfulBindingsMap: SuccessfulBindingMap | undefined; @@ -73,12 +73,8 @@ export function* showExecutionErrors(errors: EvaluationError[]) { appMode === APP_MODE.EDIT, ); - // Add it to the logs tab when in edit mode - if (appMode === APP_MODE.EDIT) { - AppsmithConsole.error({ - text: errorMessage, - }); - } + // We are not logging this in the debugger because these errors do not have a source + // attached to it. } } @@ -261,11 +257,11 @@ export function* handleJSFunctionExecutionErrorLog( // eslint-disable-next-line @typescript-eslint/no-explicit-any errors: any[], ) { - const { id: collectionId, name: collectionName } = collection; + const { id: collectionId } = collection; - const collectionNameToDisplay = getCollectionNameToDisplay( + const collectionNameToDisplay = getJSActionPathNameToDisplay( action, - collectionName, + collection, ); errors.length @@ -273,10 +269,8 @@ export function* handleJSFunctionExecutionErrorLog( { payload: { id: `${collectionId}-${action.id}`, - logType: LOG_TYPE.EVAL_ERROR, - text: `${createMessage( - JS_EXECUTION_FAILURE, - )}: ${collectionNameToDisplay}.${action.name}`, + logType: LOG_TYPE.JS_EXECUTION_ERROR, + text: createMessage(JS_EXECUTION_FAILURE), messages: errors.map((error) => { // TODO: Remove this check once we address uncaught promise errors let errorMessage = error.errorMessage; diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 312fadd18387..2d1f8151b251 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -217,7 +217,7 @@ abstract class BaseWidget< actionPayload.triggerPropertyName && AppsmithConsole.info({ - text: `${actionPayload.triggerPropertyName} triggered`, + text: `Event ${actionPayload.triggerPropertyName} fired`, source: { type: ENTITY_TYPE.WIDGET, id: this.props.widgetId, @@ -535,10 +535,12 @@ export const WIDGET_DISPLAY_PROPS = { isDisabled: true, backgroundColor: true, }; + export interface WidgetError extends Error { type: "property" | "configuration" | "other"; path?: string; } + export interface WidgetErrorProps { errors?: WidgetError[]; } diff --git a/app/client/src/widgets/MetaHOC.tsx b/app/client/src/widgets/MetaHOC.tsx index 6934099367dc..dfc2338f3eab 100644 --- a/app/client/src/widgets/MetaHOC.tsx +++ b/app/client/src/widgets/MetaHOC.tsx @@ -12,6 +12,7 @@ import type { AppState } from "ee/reducers"; import { error } from "loglevel"; import WidgetFactory from "WidgetProvider/factory"; import type BaseWidget from "./BaseWidget"; + export type pushAction = ( propertyName: string | batchUpdateWidgetMetaPropertyType, propertyValue?: unknown, @@ -43,6 +44,7 @@ export interface WithMeta { interface WidgetMetaProps { metaState: Record; } + type metaHOCProps = WidgetProps & WidgetMetaProps; function withMeta(WrappedWidget: typeof BaseWidget) { @@ -54,6 +56,7 @@ function withMeta(WrappedWidget: typeof BaseWidget) { actionsToExecute: Record; batchMetaUpdates: batchUpdateWidgetMetaPropertyType; updatedProperties: Record; + constructor(props: metaHOCProps) { super(props); const metaProperties = WidgetFactory.getWidgetMetaPropertiesMap( @@ -99,6 +102,7 @@ function withMeta(WrappedWidget: typeof BaseWidget) { source: { id: this.props.widgetId, name: this.props.widgetName, + entityType: ENTITY_TYPE.WIDGET, }, }); @@ -153,8 +157,8 @@ function withMeta(WrappedWidget: typeof BaseWidget) { ); }; /** - This function pushes meta updates that can be commited later. - If there are multiple updates, use this function to batch those updates together. + This function pushes meta updates that can be commited later. + If there are multiple updates, use this function to batch those updates together. */ pushBatchMetaUpdates: pushAction = (firstArgument, ...restArgs) => { //if first argument is an array its a batch lets push it @@ -182,7 +186,7 @@ function withMeta(WrappedWidget: typeof BaseWidget) { error("unknown args ", allArgs); }; /** - This function commits all batched updates in one go. + This function commits all batched updates in one go. */ commitBatchMetaUpdates = () => { //ignore commit if batch array is empty diff --git a/app/client/src/workers/Evaluation/fns/overrides/console.ts b/app/client/src/workers/Evaluation/fns/overrides/console.ts index 44379be028ec..2f88b1d4c857 100644 --- a/app/client/src/workers/Evaluation/fns/overrides/console.ts +++ b/app/client/src/workers/Evaluation/fns/overrides/console.ts @@ -7,7 +7,6 @@ import type { import { Severity } from "entities/AppsmithConsole"; import { ENTITY_TYPE } from "ee/entities/AppsmithConsole/utils"; import { klona } from "klona/lite"; -import moment from "moment"; import type { TriggerMeta } from "ee/sagas/ActionExecution/ActionExecutionSagas"; import TriggerEmitter from "../utils/TriggerEmitter"; import type { EventEmitter } from "events"; @@ -15,12 +14,15 @@ import ExecutionMetaData from "../utils/ExecutionMetaData"; class UserLog { private isEnabled = true; + enable() { this.isEnabled = true; } + disable() { this.isEnabled = false; } + private emitter?: EventEmitter; // TODO: Fix this the next time the file is edited @@ -87,6 +89,7 @@ class UserLog { }, }; } + // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any private replaceFunctionWithNamesFromObjects(data: any) { @@ -107,6 +110,7 @@ class UserLog { return acc; }, acc); } + // iterates over the data and if data is object/array, then it will remove any functions from it // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -132,12 +136,10 @@ class UserLog { }; // parses the incoming log and converts it to the log object - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private parseLogs(method: Methods, data: any[]): LogObject { + private parseLogs(method: Methods, data: unknown[]): LogObject { // Create an ID const id = uuid4(); - const timestamp = moment().format("HH:mm:ss"); + const timestamp = Date.now().toString(); // Parse the methods let output = data; // For logs UI we only keep 3 levels of severity, info, warn, error @@ -146,7 +148,7 @@ class UserLog { if (method === "error") { severity = Severity.ERROR; output = data.map((error) => { - return error?.stack || error; + return (error as Error).stack || error; }); } else if (method === "warn") { severity = Severity.WARNING;