diff --git a/app/client/src/actions/debuggerActions.ts b/app/client/src/actions/debuggerActions.ts index ce7beb04cd..d78b600239 100644 --- a/app/client/src/actions/debuggerActions.ts +++ b/app/client/src/actions/debuggerActions.ts @@ -1,5 +1,5 @@ import { ReduxActionTypes } from "constants/ReduxActionConstants"; -import { Message, ENTITY_TYPE } from "entities/AppsmithConsole"; +import { ENTITY_TYPE, Log, Message } from "entities/AppsmithConsole"; import { EventName } from "utils/AnalyticsUtil"; export interface LogDebuggerErrorAnalyticsPayload { @@ -8,15 +8,18 @@ export interface LogDebuggerErrorAnalyticsPayload { entityType: ENTITY_TYPE; eventName: EventName; propertyPath: string; - errorMessages: { message: string }[]; + errorMessages?: Message[]; + errorMessage?: Message["message"]; + errorType?: Message["type"]; + analytics?: Log["analytics"]; } -export const debuggerLogInit = (payload: Message) => ({ +export const debuggerLogInit = (payload: Log) => ({ type: ReduxActionTypes.DEBUGGER_LOG_INIT, payload, }); -export const debuggerLog = (payload: Message) => ({ +export const debuggerLog = (payload: Log) => ({ type: ReduxActionTypes.DEBUGGER_LOG, payload, }); @@ -30,16 +33,33 @@ export const showDebugger = (payload?: boolean) => ({ payload, }); -export const errorLog = (payload: Message) => ({ - type: ReduxActionTypes.DEBUGGER_ERROR_LOG, +// Add an error +export const addErrorLogInit = (payload: Log) => ({ + type: ReduxActionTypes.DEBUGGER_ADD_ERROR_LOG_INIT, payload, }); -export const updateErrorLog = (payload: Message) => ({ - type: ReduxActionTypes.DEBUGGER_UPDATE_ERROR_LOG, +export const addErrorLog = (payload: Log) => ({ + type: ReduxActionTypes.DEBUGGER_ADD_ERROR_LOG, payload, }); +export const deleteErrorLogInit = ( + id: string, + analytics?: Log["analytics"], +) => ({ + type: ReduxActionTypes.DEBUGGER_DELETE_ERROR_LOG_INIT, + payload: { + id, + analytics, + }, +}); + +export const deleteErrorLog = (id: string) => ({ + type: ReduxActionTypes.DEBUGGER_DELETE_ERROR_LOG, + payload: id, +}); + // Only used for analytics export const logDebuggerErrorAnalytics = ( payload: LogDebuggerErrorAnalyticsPayload, diff --git a/app/client/src/components/designSystems/appsmith/help/DocumentationSearch.tsx b/app/client/src/components/designSystems/appsmith/help/DocumentationSearch.tsx index 6183a29946..9266b308d7 100644 --- a/app/client/src/components/designSystems/appsmith/help/DocumentationSearch.tsx +++ b/app/client/src/components/designSystems/appsmith/help/DocumentationSearch.tsx @@ -341,17 +341,6 @@ if (intercomAppID) { }); } -export function bootIntercom(intercomAppID: string, user?: User) { - if (intercomAppID && window.Intercom) { - window.Intercom("boot", { - app_id: intercomAppID, - user_id: user?.username, - name: user?.name, - email: user?.email, - }); - } -} - class DocumentationSearch extends React.Component { constructor(props: Props) { super(props); diff --git a/app/client/src/components/designSystems/appsmith/help/HelpModal.tsx b/app/client/src/components/designSystems/appsmith/help/HelpModal.tsx index 86e6105faf..9af6b399e3 100644 --- a/app/client/src/components/designSystems/appsmith/help/HelpModal.tsx +++ b/app/client/src/components/designSystems/appsmith/help/HelpModal.tsx @@ -1,7 +1,5 @@ import React, { SyntheticEvent } from "react"; -import DocumentationSearch, { - bootIntercom, -} from "components/designSystems/appsmith/help/DocumentationSearch"; +import DocumentationSearch from "components/designSystems/appsmith/help/DocumentationSearch"; import { getHelpModalOpen } from "selectors/helpSelectors"; import { setHelpDefaultRefinement, @@ -19,7 +17,7 @@ import AnalyticsUtil from "utils/AnalyticsUtil"; import { HELP_MODAL_HEIGHT, HELP_MODAL_WIDTH } from "constants/HelpConstants"; import { getCurrentUser } from "selectors/usersSelectors"; import { User } from "constants/userConstants"; -const { intercomAppID } = getAppsmithConfigs(); +import { bootIntercom } from "utils/helpers"; const { algolia } = getAppsmithConfigs(); const HelpButton = styled.button<{ @@ -70,12 +68,12 @@ class HelpModal extends React.Component { static contextType = LayersContext; componentDidMount() { const { user } = this.props; - bootIntercom(intercomAppID, user); + bootIntercom(user); } componentDidUpdate(prevProps: Props) { const { user } = this.props; if (user?.email && prevProps.user?.email !== user?.email) { - bootIntercom(intercomAppID, user); + bootIntercom(user); } } /** diff --git a/app/client/src/components/editorComponents/Debugger/DebuggerLogs.tsx b/app/client/src/components/editorComponents/Debugger/DebuggerLogs.tsx index cbc2dcf921..b43afffc25 100644 --- a/app/client/src/components/editorComponents/Debugger/DebuggerLogs.tsx +++ b/app/client/src/components/editorComponents/Debugger/DebuggerLogs.tsx @@ -7,6 +7,9 @@ import { BlankState } from "./helpers"; import LogItem, { getLogItemProps } from "./LogItem"; import { usePagination, useFilteredLogs } from "./hooks"; import { createMessage, NO_LOGS } from "constants/messages"; +import { useSelector } from "react-redux"; +import { getCurrentUser } from "selectors/usersSelectors"; +import { bootIntercom } from "utils/helpers"; const LIST_HEADER_HEIGHT = "38px"; @@ -45,6 +48,11 @@ function DebbuggerLogs(props: Props) { () => LOGS_FILTER_OPTIONS.find((option) => option.value === filter), [filter], ); + const currentUser = useSelector(getCurrentUser); + + useEffect(() => { + bootIntercom(currentUser); + }, [currentUser?.email]); const handleScroll = (e: Event) => { if ((e.target as HTMLDivElement).scrollTop === 0) { diff --git a/app/client/src/components/editorComponents/Debugger/Errors.tsx b/app/client/src/components/editorComponents/Debugger/Errors.tsx index e4da955f80..9464249ff4 100644 --- a/app/client/src/components/editorComponents/Debugger/Errors.tsx +++ b/app/client/src/components/editorComponents/Debugger/Errors.tsx @@ -1,10 +1,13 @@ -import React from "react"; +import React, { useEffect } from "react"; import { useSelector } from "react-redux"; import styled from "styled-components"; import { getDebuggerErrors } from "selectors/debuggerSelectors"; import LogItem, { getLogItemProps } from "./LogItem"; import { BlankState } from "./helpers"; import { createMessage, NO_ERRORS } from "constants/messages"; +import { getCurrentUser } from "selectors/usersSelectors"; +import { AppState } from "reducers"; +import { bootIntercom } from "utils/helpers"; const ContainerWrapper = styled.div` overflow: hidden; @@ -18,7 +21,12 @@ const ListWrapper = styled.div` function Errors(props: { hasShortCut?: boolean }) { const errors = useSelector(getDebuggerErrors); - const expandId = useSelector((state: any) => state.ui.debugger.expandId); + const expandId = useSelector((state: AppState) => state.ui.debugger.expandId); + const currentUser = useSelector(getCurrentUser); + + useEffect(() => { + bootIntercom(currentUser); + }, [currentUser?.email]); return ( diff --git a/app/client/src/components/editorComponents/Debugger/LogItem.tsx b/app/client/src/components/editorComponents/Debugger/LogItem.tsx index ba57bef865..d09bc036b0 100644 --- a/app/client/src/components/editorComponents/Debugger/LogItem.tsx +++ b/app/client/src/components/editorComponents/Debugger/LogItem.tsx @@ -1,7 +1,7 @@ import { Collapse, Position } from "@blueprintjs/core"; import { Classes } from "components/ads/common"; import Icon, { IconName, IconSize } from "components/ads/Icon"; -import { Message, Severity, SourceEntity } from "entities/AppsmithConsole"; +import { Log, Message, Severity, SourceEntity } from "entities/AppsmithConsole"; import React, { useCallback, useState } from "react"; import ReactJson from "react-json-view"; import styled from "styled-components"; @@ -16,9 +16,16 @@ import Text, { TextType } from "components/ads/Text"; import { getTypographyByKey } from "constants/DefaultTheme"; import AnalyticsUtil from "utils/AnalyticsUtil"; import TooltipComponent from "components/ads/Tooltip"; -import { createMessage, TROUBLESHOOT_ISSUE } from "constants/messages"; +import { + createMessage, + DEBUGGER_INTERCOM_TEXT, + TROUBLESHOOT_ISSUE, +} from "constants/messages"; +import { PropertyEvaluationErrorType } from "utils/DynamicBindingUtils"; +import { getAppsmithConfigs } from "configs"; +const { intercomAppID } = getAppsmithConfigs(); -const Log = styled.div<{ collapsed: boolean }>` +const Wrapper = styled.div<{ collapsed: boolean }>` padding: 9px 30px; display: flex; @@ -142,7 +149,7 @@ const MessageWrapper = styled.div` padding-top: ${(props) => props.theme.spaces[1]}px; `; -export const getLogItemProps = (e: Message) => { +export const getLogItemProps = (e: Log) => { return { icon: SeverityIcon[e.severity] as IconName, iconColor: SeverityIconColor[e.severity], @@ -170,7 +177,7 @@ type LogItemProps = { id?: string; source?: SourceEntity; expand?: boolean; - messages: Message["messages"]; + messages?: Message[]; }; function LogItem(props: LogItemProps) { @@ -188,21 +195,47 @@ function LogItem(props: LogItemProps) { const showToggleIcon = props.state || props.messages; const dispatch = useDispatch(); - const openHelpModal = useCallback((e, message?: string) => { + const onLogClick = useCallback((e, error?: Message) => { e.stopPropagation(); - const text = message || props.text; - AnalyticsUtil.logEvent("OPEN_OMNIBAR", { - source: "DEBUGGER", - searchTerm: text, - }); - dispatch(setGlobalSearchQuery(text || "")); - dispatch(toggleShowGlobalSearchModal()); + // If the error message was clicked we use that, else if the wand icon is clicked + // we use the first error "Message" in the list + // This is of type Message { message: string; type?: ErrorType; } + const focusedError = + error || + (props.messages && props.messages.length ? props.messages[0] : undefined); + const text = focusedError?.message || props.text; + + switch (focusedError?.type) { + case PropertyEvaluationErrorType.PARSE: + case PropertyEvaluationErrorType.LINT: + // Search google for the error message + window.open("http://google.com/search?q=" + text); + break; + case PropertyEvaluationErrorType.VALIDATION: + // Search through the omnibar + AnalyticsUtil.logEvent("OPEN_OMNIBAR", { + source: "DEBUGGER", + searchTerm: text, + errorType: PropertyEvaluationErrorType.VALIDATION, + }); + dispatch(setGlobalSearchQuery(text || "")); + dispatch(toggleShowGlobalSearchModal()); + break; + default: + // Prefill the error in intercom + if (intercomAppID && window.Intercom) { + window.Intercom( + "showNewMessage", + createMessage(DEBUGGER_INTERCOM_TEXT, text), + ); + } + } }, []); const messages = props.messages || []; return ( - setIsOpen(!isOpen)} @@ -244,7 +277,7 @@ function LogItem(props: LogItemProps) { className={Classes.ICON} fillColor={props.iconColor} name={"wand"} - onClick={openHelpModal} + onClick={onLogClick} size={IconSize.MEDIUM} /> @@ -257,7 +290,7 @@ function LogItem(props: LogItemProps) { openHelpModal(event, e.message)} + onClick={(event) => onLogClick(event, e)} > {e.message} @@ -283,7 +316,7 @@ function LogItem(props: LogItemProps) { uiComponent={DebuggerLinkUI.ENTITY_NAME} /> )} - + ); } diff --git a/app/client/src/components/editorComponents/Debugger/helpers.tsx b/app/client/src/components/editorComponents/Debugger/helpers.tsx index aa2981b6be..33b03b7fa3 100644 --- a/app/client/src/components/editorComponents/Debugger/helpers.tsx +++ b/app/client/src/components/editorComponents/Debugger/helpers.tsx @@ -1,4 +1,4 @@ -import { Message, Severity } from "entities/AppsmithConsole"; +import { Log, Severity } from "entities/AppsmithConsole"; import React from "react"; import styled from "styled-components"; import { getTypographyByKey } from "constants/DefaultTheme"; @@ -125,7 +125,7 @@ export function getDependencyChain( export const doesEntityHaveErrors = ( entityId: string, - debuggerErrors: Record, + debuggerErrors: Record, ) => { const ids = Object.keys(debuggerErrors); diff --git a/app/client/src/components/editorComponents/Debugger/hooks.ts b/app/client/src/components/editorComponents/Debugger/hooks.ts index c15b0d3a2a..7d29b5d86e 100644 --- a/app/client/src/components/editorComponents/Debugger/hooks.ts +++ b/app/client/src/components/editorComponents/Debugger/hooks.ts @@ -1,7 +1,7 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { useSelector } from "react-redux"; import { useParams } from "react-router"; -import { ENTITY_TYPE, Message } from "entities/AppsmithConsole"; +import { ENTITY_TYPE, Log } from "entities/AppsmithConsole"; import { AppState } from "reducers"; import { getActionConfig } from "pages/Editor/Explorer/Actions/helpers"; import { useNavigateToWidget } from "pages/Editor/Explorer/Widgets/useNavigateToWidget"; @@ -33,11 +33,11 @@ export const useFilteredLogs = (query: string, filter?: any) => { let logs = useSelector((state: AppState) => state.ui.debugger.logs); if (filter) { - logs = logs.filter((log: Message) => log.severity === filter); + logs = logs.filter((log) => log.severity === filter); } if (query) { - logs = logs.filter((log: Message) => { + logs = logs.filter((log) => { if (log.source?.name) return ( log.source?.name.toUpperCase().indexOf(query.toUpperCase()) !== -1 @@ -48,9 +48,9 @@ export const useFilteredLogs = (query: string, filter?: any) => { return logs; }; -export const usePagination = (data: Message[], itemsPerPage = 50) => { +export const usePagination = (data: Log[], itemsPerPage = 50) => { const [currentPage, setCurrentPage] = useState(1); - const [paginatedData, setPaginatedData] = useState([]); + const [paginatedData, setPaginatedData] = useState([]); const maxPage = Math.ceil(data.length / itemsPerPage); useEffect(() => { @@ -158,9 +158,7 @@ export const useEntityLink = () => { export const useGetEntityInfo = (name: string) => { const entity = useSelector((state: AppState) => state.evaluations.tree[name]); - const debuggerErrors: Record = useSelector( - getDebuggerErrors, - ); + const debuggerErrors = useSelector(getDebuggerErrors); const action = useSelector((state: AppState) => isAction(entity) ? getAction(state, entity.actionId) : undefined, ); diff --git a/app/client/src/constants/ReduxActionConstants.tsx b/app/client/src/constants/ReduxActionConstants.tsx index 4b1356fe1f..39ff214bc9 100644 --- a/app/client/src/constants/ReduxActionConstants.tsx +++ b/app/client/src/constants/ReduxActionConstants.tsx @@ -133,10 +133,11 @@ export const ReduxActionTypes = { PUBLISH: "PUBLISH", DEBUGGER_LOG: "DEBUGGER_LOG", DEBUGGER_LOG_INIT: "DEBUGGER_LOG_INIT", - DEBUGGER_ERROR_LOG: "DEBUGGER_ERROR_LOG", - DEBUGGER_UPDATE_ERROR_LOG: "DEBUGGER_UPDATE_ERROR_LOG", - DEBUGGER_UPDATE_ERROR_LOGS: "DEBUGGER_UPDATE_ERROR_LOGS", DEBUGGER_ERROR_ANALYTICS: "DEBUGGER_ERROR_ANALYTICS", + DEBUGGER_ADD_ERROR_LOG: "DEBUGGER_ADD_ERROR_LOG", + DEBUGGER_DELETE_ERROR_LOG: "DEBUGGER_DELETE_ERROR_LOG", + DEBUGGER_ADD_ERROR_LOG_INIT: "DEBUGGER_ADD_ERROR_LOG_INIT", + DEBUGGER_DELETE_ERROR_LOG_INIT: "DEBUGGER_DELETE_ERROR_LOG_INIT", CLEAR_DEBUGGER_LOGS: "CLEAR_DEBUGGER_LOGS", SHOW_DEBUGGER: "SHOW_DEBUGGER", SET_ACTION_TABS_INITIAL_INDEX: "SET_ACTION_TABS_INITIAL_INDEX", diff --git a/app/client/src/constants/messages.ts b/app/client/src/constants/messages.ts index 119cc14ba2..6806387df2 100644 --- a/app/client/src/constants/messages.ts +++ b/app/client/src/constants/messages.ts @@ -349,11 +349,15 @@ export const DEBUGGER_ERRORS = () => "Errors"; export const DEBUGGER_LOGS = () => "Logs"; export const INSPECT_ENTITY = () => "Inspect Entity"; export const INSPECT_ENTITY_BLANK_STATE = () => "Select an entity to inspect"; +export const VALUE_IS_INVALID = (propertyPath: string) => + `The value at ${propertyPath} is invalid`; export const ACTION_CONFIGURATION_UPDATED = () => "Configuration updated"; export const WIDGET_PROPERTIES_UPDATED = () => "Widget properties were updated"; export const EMPTY_RESPONSE_FIRST_HALF = () => "🙌 Click on"; export const EMPTY_RESPONSE_LAST_HALF = () => "to get a response"; export const INVALID_EMAIL = () => "Please enter a valid email"; +export const DEBUGGER_INTERCOM_TEXT = (text: string) => + `Hi, \nI'm facing the following error on appsmith, can you please help? \n\n${text}`; export const TROUBLESHOOT_ISSUE = () => "Troubleshoot issue"; diff --git a/app/client/src/entities/AppsmithConsole/action.ts b/app/client/src/entities/AppsmithConsole/action.ts index b41f7bb5d3..6dae43aa2d 100644 --- a/app/client/src/entities/AppsmithConsole/action.ts +++ b/app/client/src/entities/AppsmithConsole/action.ts @@ -1,10 +1,3 @@ -import { ActionableError } from "entities/AppsmithConsole"; - export enum ActionError { EXECUTION_TIMEOUT = "action:execution:timeout", } - -export interface TimeoutError extends ActionableError { - type: ActionError.EXECUTION_TIMEOUT; - timeoutMs: number; -} diff --git a/app/client/src/entities/AppsmithConsole/binding.ts b/app/client/src/entities/AppsmithConsole/binding.ts index 6544b88a21..64d1584be0 100644 --- a/app/client/src/entities/AppsmithConsole/binding.ts +++ b/app/client/src/entities/AppsmithConsole/binding.ts @@ -1,26 +1,5 @@ -import { ActionableError } from "entities/AppsmithConsole"; - export enum BindingError { SYNTAX = "binding:syntax", UNKNOWN_VARIABLE = "binding:unknown_variable", DISALLOWED_FUNCTION = "binding:disallowed_function", } - -interface BaseBindingError extends ActionableError { - lineNumber: number; - position: number; -} - -export interface SyntaxError extends BaseBindingError { - type: BindingError.SYNTAX; -} - -export interface UnknownVariableError extends BaseBindingError { - type: BindingError.UNKNOWN_VARIABLE; - variableName: string; -} - -export interface DisallowedFunctionError extends BaseBindingError { - type: BindingError.DISALLOWED_FUNCTION; - functionName: string; -} diff --git a/app/client/src/entities/AppsmithConsole/eval.ts b/app/client/src/entities/AppsmithConsole/eval.ts index bee17c56be..877ed33a94 100644 --- a/app/client/src/entities/AppsmithConsole/eval.ts +++ b/app/client/src/entities/AppsmithConsole/eval.ts @@ -1,10 +1,3 @@ -import { ActionableError, SourceEntity } from "entities/AppsmithConsole"; - export enum EvalError { CYCLIC = "eval:cyclic", } - -export interface CyclicDependencyError extends ActionableError { - type: EvalError.CYCLIC; - entities: SourceEntity[]; -} diff --git a/app/client/src/entities/AppsmithConsole/index.ts b/app/client/src/entities/AppsmithConsole/index.ts index 0ae20d2dee..b37a1179fc 100644 --- a/app/client/src/entities/AppsmithConsole/index.ts +++ b/app/client/src/entities/AppsmithConsole/index.ts @@ -1,9 +1,6 @@ import { ReduxAction } from "constants/ReduxActionConstants"; -import { BindingError } from "entities/AppsmithConsole/binding"; -import { ActionError } from "entities/AppsmithConsole/action"; -import { WidgetError } from "entities/AppsmithConsole/widget"; -import { EvalError } from "entities/AppsmithConsole/eval"; import LOG_TYPE from "./logtype"; +import { PropertyEvaluationErrorType } from "utils/DynamicBindingUtils"; export enum ENTITY_TYPE { ACTION = "ACTION", @@ -11,7 +8,11 @@ export enum ENTITY_TYPE { WIDGET = "WIDGET", } -export type ErrorType = BindingError | ActionError | WidgetError | EvalError; +export enum PLATFORM_ERROR { + PLUGIN_EXECUTION = "PLUGIN_EXECUTION", +} + +export type ErrorType = PropertyEvaluationErrorType | PLATFORM_ERROR; export enum Severity { // Everything, irrespective of what the user should see or not @@ -47,17 +48,13 @@ export interface SourceEntity { } export interface LogActionPayload { + // Log id, used for updating or deleting + id?: string; // What is the log about. Is it a datasource update, widget update, eval error etc. logType?: LOG_TYPE; text: string; + messages?: Array; // Time taken for the event to complete - messages?: Array<{ - // More contextual message than `text` - message: string; - // The section of code being referred to - // codeSegment?: string; - }>; - timeTaken?: string; // "where" source entity and propertyPsath. source?: SourceEntity; @@ -67,7 +64,15 @@ export interface LogActionPayload { analytics?: Record; } -export interface Message extends LogActionPayload { +export interface Message { + // More contextual message than `text` + message: string; + type?: ErrorType; + // The section of code being referred to + // codeSegment?: string; +} + +export interface Log extends LogActionPayload { severity: Severity; // "when" did this event happen timestamp: string; @@ -100,12 +105,3 @@ export interface Message extends LogActionPayload { * ] * } */ -export interface ActionableError extends Message { - // Error type of the event. - type: ErrorType; - - severity: Severity.ERROR; - - // Actions a user can take to resolve this issue - userActions: Array; -} diff --git a/app/client/src/pages/Editor/HelpButton.tsx b/app/client/src/pages/Editor/HelpButton.tsx index 7001fb0153..e7db88979e 100644 --- a/app/client/src/pages/Editor/HelpButton.tsx +++ b/app/client/src/pages/Editor/HelpButton.tsx @@ -2,17 +2,15 @@ import React, { useEffect } from "react"; import styled, { createGlobalStyle, withTheme } from "styled-components"; import { Popover, Position } from "@blueprintjs/core"; -import DocumentationSearch, { - bootIntercom, -} from "components/designSystems/appsmith/help/DocumentationSearch"; +import DocumentationSearch from "components/designSystems/appsmith/help/DocumentationSearch"; import Icon, { IconSize } from "components/ads/Icon"; import { HELP_MODAL_WIDTH } from "constants/HelpConstants"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { Theme } from "constants/DefaultTheme"; -import { getAppsmithConfigs } from "../../configs"; import { getCurrentUser } from "../../selectors/usersSelectors"; import { useSelector } from "react-redux"; +import { bootIntercom } from "utils/helpers"; const HelpPopoverStyle = createGlobalStyle` .bp3-popover.bp3-minimal.navbar-help-popover { @@ -47,12 +45,11 @@ const onOpened = () => { AnalyticsUtil.logEvent("OPEN_HELP", { page: "Editor" }); }; -const { intercomAppID } = getAppsmithConfigs(); function HelpButton() { const user = useSelector(getCurrentUser); useEffect(() => { - bootIntercom(intercomAppID, user); + bootIntercom(user); }, [user?.email]); return ( diff --git a/app/client/src/pages/Editor/PropertyPane/PropertyPaneConnections.tsx b/app/client/src/pages/Editor/PropertyPane/PropertyPaneConnections.tsx index dcea8bf149..8612393780 100644 --- a/app/client/src/pages/Editor/PropertyPane/PropertyPaneConnections.tsx +++ b/app/client/src/pages/Editor/PropertyPane/PropertyPaneConnections.tsx @@ -21,7 +21,7 @@ import { getDependenciesFromInverseDependencies, } from "components/editorComponents/Debugger/helpers"; import { getDebuggerErrors } from "selectors/debuggerSelectors"; -import { ENTITY_TYPE, Message } from "entities/AppsmithConsole"; +import { ENTITY_TYPE, Log } from "entities/AppsmithConsole"; import { DebugButton } from "components/editorComponents/Debugger/DebugCTA"; import { showDebugger } from "actions/debuggerActions"; import { setActionTabsInitialIndex } from "actions/actionActions"; @@ -179,7 +179,7 @@ type TriggerNodeProps = DefaultDropDownValueNodeProps & { const doConnectionsHaveErrors = ( options: DropdownOption[], - debuggerErrors: Record, + debuggerErrors: Record, ) => { return options.some((option) => doesEntityHaveErrors(option.value as string, debuggerErrors), diff --git a/app/client/src/reducers/uiReducers/debuggerReducer.ts b/app/client/src/reducers/uiReducers/debuggerReducer.ts index 0a685b642a..451f7c30a5 100644 --- a/app/client/src/reducers/uiReducers/debuggerReducer.ts +++ b/app/client/src/reducers/uiReducers/debuggerReducer.ts @@ -1,8 +1,7 @@ import { createReducer } from "utils/AppsmithUtils"; -import { Message, Severity } from "entities/AppsmithConsole"; +import { Log, Severity } from "entities/AppsmithConsole"; import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants"; -import { get, merge, isEmpty, omit, isUndefined } from "lodash"; -import LOG_TYPE from "entities/AppsmithConsole/logtype"; +import { omit, isUndefined } from "lodash"; const initialState: DebuggerReduxState = { logs: [], @@ -15,7 +14,7 @@ const initialState: DebuggerReduxState = { const debuggerReducer = createReducer(initialState, { [ReduxActionTypes.DEBUGGER_LOG]: ( state: DebuggerReduxState, - action: ReduxAction, + action: ReduxAction, ) => { const isError = action.payload.severity === Severity.ERROR; @@ -41,70 +40,28 @@ const debuggerReducer = createReducer(initialState, { isOpen: isUndefined(action.payload) ? !state.isOpen : action.payload, }; }, - [ReduxActionTypes.DEBUGGER_ERROR_LOG]: ( + [ReduxActionTypes.DEBUGGER_ADD_ERROR_LOG]: ( state: DebuggerReduxState, - action: ReduxAction, + action: ReduxAction, ) => { - if (!action.payload.source) return state; - - const entityId = action.payload.source.id; - const id = - action.payload.logType === LOG_TYPE.WIDGET_PROPERTY_VALIDATION_ERROR || - action.payload.logType === LOG_TYPE.EVAL_ERROR - ? `${entityId}-${action.payload.source.propertyPath}` - : entityId; - const previousState = get(state.errors, id, {}); + if (!action.payload.id) return state; return { ...state, errors: { ...state.errors, - [id]: { - ...merge(previousState, action.payload), - }, + [action.payload.id]: action.payload, }, - expandId: id, + expandId: action.payload.id, }; }, - [ReduxActionTypes.DEBUGGER_UPDATE_ERROR_LOG]: ( + [ReduxActionTypes.DEBUGGER_DELETE_ERROR_LOG]: ( state: DebuggerReduxState, - action: ReduxAction, - ) => { - if (!action.payload.source) return state; - - const entityId = action.payload.source.id; - const isWidgetErrorLog = - action.payload.logType === LOG_TYPE.WIDGET_PROPERTY_VALIDATION_ERROR || - action.payload.logType === LOG_TYPE.EVAL_ERROR; - const id = isWidgetErrorLog - ? `${entityId}-${action.payload.source.propertyPath}` - : entityId; - - if (isEmpty(action.payload.state)) { - return { - ...state, - errors: omit(state.errors, id), - }; - } - - return { - ...state, - errors: { - ...state.errors, - [id]: { - ...action.payload, - }, - }, - expandId: id, - }; - }, - [ReduxActionTypes.DEBUGGER_UPDATE_ERROR_LOGS]: ( - state: DebuggerReduxState, - action: ReduxAction, + action: ReduxAction, ) => { return { ...state, - errors: { ...action.payload }, + errors: omit(state.errors, action.payload), }; }, [ReduxActionTypes.INIT_CANVAS_LAYOUT]: () => { @@ -115,10 +72,10 @@ const debuggerReducer = createReducer(initialState, { }); export interface DebuggerReduxState { - logs: Message[]; + logs: Log[]; errorCount: number; isOpen: boolean; - errors: Record; + errors: Record; expandId: string; } diff --git a/app/client/src/sagas/ActionExecutionSagas.ts b/app/client/src/sagas/ActionExecutionSagas.ts index daf03d3675..0c3646c499 100644 --- a/app/client/src/sagas/ActionExecutionSagas.ts +++ b/app/client/src/sagas/ActionExecutionSagas.ts @@ -115,7 +115,7 @@ import { resetWidgetMetaProperty, } from "actions/metaActions"; import AppsmithConsole from "utils/AppsmithConsole"; -import { ENTITY_TYPE } from "entities/AppsmithConsole"; +import { ENTITY_TYPE, PLATFORM_ERROR } from "entities/AppsmithConsole"; import LOG_TYPE from "entities/AppsmithConsole/logtype"; import { matchPath } from "react-router"; import { setDataUrl } from "./PageSagas"; @@ -565,7 +565,8 @@ export function* executeActionSaga( }), ); if (isErrorResponse(response)) { - AppsmithConsole.error({ + AppsmithConsole.addError({ + id: actionId, logType: LOG_TYPE.ACTION_EXECUTION_ERROR, text: `Execution failed with status ${response.data.statusCode}`, source: { @@ -574,7 +575,12 @@ export function* executeActionSaga( id: actionId, }, state: response.data?.request ?? null, - messages: [{ message: payload.body as string }], + messages: [ + { + message: payload.body as string, + type: PLATFORM_ERROR.PLUGIN_EXECUTION, + }, + ], }); PerformanceTracker.stopAsyncTracking( PerformanceTransactionName.EXECUTE_ACTION, @@ -902,7 +908,8 @@ function* runActionSaga( }, }); } else { - AppsmithConsole.error({ + AppsmithConsole.addError({ + id: actionId, logType: LOG_TYPE.ACTION_EXECUTION_ERROR, text: `Execution failed with status ${response.data.statusCode}`, source: { @@ -915,6 +922,7 @@ function* runActionSaga( message: !isString(payload.body) ? JSON.stringify(payload.body) : payload.body, + type: PLATFORM_ERROR.PLUGIN_EXECUTION, }, ], state: response.data?.request ?? null, @@ -931,7 +939,8 @@ function* runActionSaga( error = response.data.body.toString(); } - AppsmithConsole.error({ + AppsmithConsole.addError({ + id: actionId, logType: LOG_TYPE.ACTION_EXECUTION_ERROR, text: `Execution failed with status ${response.data.statusCode} `, source: { @@ -1016,7 +1025,8 @@ function* executePageLoadAction(pageAction: PageAction) { message += `\nERROR: "${body}"`; } - AppsmithConsole.error({ + AppsmithConsole.addError({ + id: pageAction.id, logType: LOG_TYPE.ACTION_EXECUTION_ERROR, text: `Execution failed with status ${response.data.statusCode}`, source: { @@ -1025,7 +1035,12 @@ function* executePageLoadAction(pageAction: PageAction) { id: pageAction.id, }, state: response.data?.request ?? null, - messages: [{ message: JSON.stringify(body) }], + messages: [ + { + message: JSON.stringify(body), + type: PLATFORM_ERROR.PLUGIN_EXECUTION, + }, + ], }); yield put( diff --git a/app/client/src/sagas/DebuggerSagas.ts b/app/client/src/sagas/DebuggerSagas.ts index 07672ab185..5cc352b486 100644 --- a/app/client/src/sagas/DebuggerSagas.ts +++ b/app/client/src/sagas/DebuggerSagas.ts @@ -1,16 +1,12 @@ import { + addErrorLog, debuggerLog, - errorLog, - logDebuggerErrorAnalytics, + debuggerLogInit, + deleteErrorLog, LogDebuggerErrorAnalyticsPayload, - updateErrorLog, } from "actions/debuggerActions"; import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants"; -import { - ENTITY_TYPE, - LogActionPayload, - Message, -} from "entities/AppsmithConsole"; +import { ENTITY_TYPE, Log, LogActionPayload } from "entities/AppsmithConsole"; import { all, call, @@ -20,13 +16,9 @@ import { take, takeEvery, } from "redux-saga/effects"; -import { get, set } from "lodash"; +import { findIndex, get, isMatch, set } from "lodash"; import { getDebuggerErrors } from "selectors/debuggerSelectors"; -import { - getAction, - getPlugin, - getPluginNameFromId, -} from "selectors/entitiesSelector"; +import { getAction, getPlugin } from "selectors/entitiesSelector"; import { Action, PluginType } from "entities/Action"; import LOG_TYPE from "entities/AppsmithConsole/logtype"; import { DataTree } from "entities/DataTree/dataTreeFactory"; @@ -50,6 +42,7 @@ import { Plugin } from "api/PluginApi"; import { getCurrentPageId } from "selectors/editorSelectors"; import { getWidget } from "./selectors"; import { WidgetProps } from "widgets/BaseWidget"; +import AppsmithConsole from "utils/AppsmithConsole"; // Saga to format action request values to be shown in the debugger function* formatActionRequestSaga( @@ -103,62 +96,29 @@ function* formatActionRequestSaga( } } -function* onEntityDeleteSaga(payload: Message) { +function* onEntityDeleteSaga(payload: Log) { const source = payload.source; if (!source) { yield put(debuggerLog(payload)); return; } - const currentPageId = yield select(getCurrentPageId); - let pluginName: string = yield select( - getPluginNameFromId, - payload?.analytics?.pluginId, - ); - const errors: Record = yield select(getDebuggerErrors); + const errors: Record = yield select(getDebuggerErrors); const errorIds = Object.keys(errors); - const updatedErrors: any = {}; errorIds.map((e) => { const includes = e.includes(source.id); - if (!includes) { - updatedErrors[e] = errors[e]; - } else { - // If the error is being removed here - // need to send an analytics event for the same - const error = errors[e]; - pluginName = pluginName.replace(/ /g, ""); - - if (source.type === ENTITY_TYPE.ACTION) { - AnalyticsUtil.logEvent("DEBUGGER_RESOLVED_ERROR", { - entityType: pluginName, - propertyPath: `${pluginName}.${error.source?.propertyPath ?? ""}`, - errorMessages: error.messages, - pageId: currentPageId, - }); - } else if (source.type === ENTITY_TYPE.WIDGET) { - const widgetType = error?.analytics?.widgetType; - - AnalyticsUtil.logEvent("DEBUGGER_RESOLVED_ERROR", { - entityType: widgetType, - propertyPath: `${widgetType}.${error.source?.propertyPath ?? ""}`, - errorMessages: error.messages, - pageId: currentPageId, - }); - } + if (includes) { + AppsmithConsole.deleteError(e, payload.analytics); } }); - yield put({ - type: ReduxActionTypes.DEBUGGER_UPDATE_ERROR_LOGS, - payload: updatedErrors, - }); yield put(debuggerLog(payload)); } -function* logDependentEntityProperties(payload: Message) { +function* logDependentEntityProperties(payload: Log) { const { source, state } = payload; if (!state || !source) return; @@ -207,11 +167,8 @@ function* logDependentEntityProperties(payload: Message) { ); } -function* debuggerLogSaga(action: ReduxAction) { +function* debuggerLogSaga(action: ReduxAction) { const { payload } = action; - const debuggerErrors: Record = yield select( - getDebuggerErrors, - ); switch (payload.logType) { case LOG_TYPE.WIDGET_UPDATE: @@ -226,9 +183,7 @@ function* debuggerLogSaga(action: ReduxAction) { case LOG_TYPE.WIDGET_PROPERTY_VALIDATION_ERROR: if (payload.source && payload.source.propertyPath) { if (payload.text) { - yield put(errorLog(payload)); - - yield put(debuggerLog(payload)); + yield put(addErrorLog(payload)); } } break; @@ -239,20 +194,7 @@ function* debuggerLogSaga(action: ReduxAction) { payload, "state", ); - if (!((payload.source?.id as string) in debuggerErrors)) { - yield put( - logDebuggerErrorAnalytics({ - eventName: "DEBUGGER_NEW_ERROR", - errorMessages: payload.messages ?? [], - entityType: ENTITY_TYPE.ACTION, - entityId: payload.source?.id ?? "", - entityName: payload.source?.name ?? "", - propertyPath: "", - }), - ); - } - - yield put(errorLog(formattedLog)); + yield put(addErrorLog(formattedLog)); yield put(debuggerLog(formattedLog)); } break; @@ -264,25 +206,7 @@ function* debuggerLogSaga(action: ReduxAction) { "state.request", ); - if ((payload.source?.id as string) in debuggerErrors) { - yield put( - logDebuggerErrorAnalytics({ - eventName: "DEBUGGER_RESOLVED_ERROR", - errorMessages: - debuggerErrors[payload.source?.id ?? ""].messages ?? [], - entityType: ENTITY_TYPE.ACTION, - entityId: payload.source?.id ?? "", - entityName: payload.source?.name ?? "", - propertyPath: "", - }), - ); - } - yield put( - updateErrorLog({ - ...payload, - state: {}, - }), - ); + AppsmithConsole.deleteError(payload.source?.id ?? ""); yield put(debuggerLog(formattedLog)); } @@ -304,8 +228,11 @@ function* logDebuggerErrorAnalyticsSaga( const currentPageId = yield select(getCurrentPageId); if (payload.entityType === ENTITY_TYPE.WIDGET) { - const widget: WidgetProps = yield select(getWidget, payload.entityId); - const widgetType = widget.type; + const widget: WidgetProps | undefined = yield select( + getWidget, + payload.entityId, + ); + const widgetType = widget?.type || payload?.analytics?.widgetType || ""; const propertyPath = `${widgetType}.${payload.propertyPath}`; // Sending widget type for widgets @@ -314,10 +241,16 @@ function* logDebuggerErrorAnalyticsSaga( propertyPath, errorMessages: payload.errorMessages, pageId: currentPageId, + errorMessage: payload.errorMessage, + errorType: payload.errorType, }); } else if (payload.entityType === ENTITY_TYPE.ACTION) { - const action: Action = yield select(getAction, payload.entityId); - const plugin: Plugin = yield select(getPlugin, action.pluginId); + const action: Action | undefined = yield select( + getAction, + payload.entityId, + ); + const pluginId = action?.pluginId || payload?.analytics?.pluginId || ""; + const plugin: Plugin = yield select(getPlugin, pluginId); const pluginName = plugin.name.replace(/ /g, ""); let propertyPath = `${pluginName}`; @@ -331,6 +264,8 @@ function* logDebuggerErrorAnalyticsSaga( propertyPath, errorMessages: payload.errorMessages, pageId: currentPageId, + errorMessage: payload.errorMessage, + errorType: payload.errorType, }); } } catch (e) { @@ -338,6 +273,147 @@ function* logDebuggerErrorAnalyticsSaga( } } +function* addDebuggerErrorLogSaga(action: ReduxAction) { + const payload = action.payload; + const errors: Record = yield select(getDebuggerErrors); + + yield put(debuggerLogInit(payload)); + + if (!payload.source || !payload.id) return; + + const analyticsPayload = { + entityName: payload.source.name, + entityType: payload.source.type, + entityId: payload.source.id, + propertyPath: payload.source.propertyPath ?? "", + }; + + // If this is a new error + if (!(payload.id in errors)) { + const errorMessages = payload.messages ?? []; + + yield put({ + type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS, + payload: { + ...analyticsPayload, + eventName: "DEBUGGER_NEW_ERROR", + errorMessages: payload.messages, + }, + }); + + // Log analytics for new error messages + if (errorMessages.length && payload) { + yield all( + errorMessages.map((errorMessage) => + put({ + type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS, + payload: { + ...analyticsPayload, + eventName: "DEBUGGER_NEW_ERROR_MESSAGE", + errorMessage: errorMessage.message, + errorType: errorMessage.type, + }, + }), + ), + ); + } + } else { + const updatedErrorMessages = payload.messages ?? []; + const existingErrorMessages = errors[payload.id].messages ?? []; + // Log new error messages + yield all( + updatedErrorMessages.map((updatedErrorMessage) => { + const exists = findIndex( + existingErrorMessages, + (existingErrorMessage) => { + return isMatch(existingErrorMessage, updatedErrorMessage); + }, + ); + + if (exists < 0) { + return put({ + type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS, + payload: { + ...analyticsPayload, + eventName: "DEBUGGER_NEW_ERROR_MESSAGE", + errorMessage: updatedErrorMessage.message, + errorType: updatedErrorMessage.type, + }, + }); + } + }), + ); + // Log resolved error messages + yield all( + existingErrorMessages.map((existingErrorMessage) => { + const exists = findIndex( + updatedErrorMessages, + (updatedErrorMessage) => { + return isMatch(updatedErrorMessage, existingErrorMessage); + }, + ); + + if (exists < 0) { + return put({ + type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS, + payload: { + ...analyticsPayload, + eventName: "DEBUGGER_RESOLVED_ERROR_MESSAGE", + errorMessage: existingErrorMessage.message, + errorType: existingErrorMessage.type, + }, + }); + } + }), + ); + } +} + +function* deleteDebuggerErrorLogSaga( + action: ReduxAction<{ id: string; analytics: Log["analytics"] }>, +) { + const errors: Record = yield select(getDebuggerErrors); + const error = errors[action.payload.id]; + + if (!error.source) return; + + const analyticsPayload = { + entityName: error.source.name, + entityType: error.source.type, + entityId: error.source.id, + propertyPath: error.source.propertyPath ?? "", + analytics: action.payload.analytics, + }; + const errorMessages = error.messages; + + yield put({ + type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS, + payload: { + ...analyticsPayload, + eventName: "DEBUGGER_RESOLVED_ERROR", + errorMessages, + }, + }); + + if (errorMessages) { + yield all( + errorMessages.map((errorMessage) => { + return put({ + type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS, + payload: { + ...analyticsPayload, + eventName: "DEBUGGER_RESOLVED_ERROR_MESSAGE", + errorMessage: errorMessage.message, + errorType: errorMessage.type, + }, + }); + }), + ); + } + + yield put(deleteErrorLog(action.payload.id)); +} + export default function* debuggerSagasListeners() { yield all([ takeEvery(ReduxActionTypes.DEBUGGER_LOG_INIT, debuggerLogSaga), @@ -345,5 +421,13 @@ export default function* debuggerSagasListeners() { ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS, logDebuggerErrorAnalyticsSaga, ), + takeEvery( + ReduxActionTypes.DEBUGGER_ADD_ERROR_LOG_INIT, + addDebuggerErrorLogSaga, + ), + takeEvery( + ReduxActionTypes.DEBUGGER_DELETE_ERROR_LOG_INIT, + deleteDebuggerErrorLogSaga, + ), ]); } diff --git a/app/client/src/sagas/PostEvaluationSagas.ts b/app/client/src/sagas/PostEvaluationSagas.ts index a7d302860e..846a9c720d 100644 --- a/app/client/src/sagas/PostEvaluationSagas.ts +++ b/app/client/src/sagas/PostEvaluationSagas.ts @@ -1,4 +1,4 @@ -import { ENTITY_TYPE, Message } from "entities/AppsmithConsole"; +import { ENTITY_TYPE, Log } from "entities/AppsmithConsole"; import { DataTree } from "entities/DataTree/dataTreeFactory"; import { DataTreeDiff, @@ -13,16 +13,13 @@ import { EvaluationError, getEvalErrorPath, getEvalValuePath, - PropertyEvalErrorTypeDebugMessage, PropertyEvaluationErrorType, } from "utils/DynamicBindingUtils"; -import _ from "lodash"; +import { find, get, some } from "lodash"; import LOG_TYPE from "../entities/AppsmithConsole/logtype"; -import moment from "moment/moment"; import { put, select } from "redux-saga/effects"; import { ReduxAction, - ReduxActionTypes, ReduxActionWithoutPayload, } from "constants/ReduxActionConstants"; import { Toaster } from "components/ads/Toast"; @@ -34,6 +31,7 @@ import { createMessage, ERROR_EVAL_ERROR_GENERIC, ERROR_EVAL_TRIGGER, + VALUE_IS_INVALID, } from "constants/messages"; import log from "loglevel"; import { AppState } from "reducers"; @@ -41,17 +39,15 @@ import { getAppMode } from "selectors/applicationSelectors"; import { APP_MODE } from "entities/App"; import { dataTreeTypeDefCreator } from "utils/autocomplete/dataTreeTypeDefCreator"; import TernServer from "utils/autocomplete/TernServer"; -import { logDebuggerErrorAnalytics } from "actions/debuggerActions"; -import store from "../store"; const getDebuggerErrors = (state: AppState) => state.ui.debugger.errors; -function getLatestEvalPropertyErrors( - currentDebuggerErrors: Record, +function logLatestEvalPropertyErrors( + currentDebuggerErrors: Record, dataTree: DataTree, evaluationOrder: Array, ) { - const updatedDebuggerErrors: Record = { + const updatedDebuggerErrors: Record = { ...currentDebuggerErrors, }; @@ -64,12 +60,12 @@ function getLatestEvalPropertyErrors( if (propertyPath in entity.logBlackList) { continue; } - const allEvalErrors: EvaluationError[] = _.get( + const allEvalErrors: EvaluationError[] = get( entity, getEvalErrorPath(evaluatedPath, false), [], ); - const evaluatedValue = _.get( + const evaluatedValue = get( entity, getEvalValuePath(evaluatedPath, false), ); @@ -89,23 +85,17 @@ function getLatestEvalPropertyErrors( if (evalErrors.length) { // TODO Rank and set the most critical error - const error = evalErrors[0]; - const errorMessages = evalErrors.map((e) => ({ - message: e.errorMessage, - })); + // const error = evalErrors[0]; + // Reformatting eval errors here to a format usable by the debugger + const errorMessages = evalErrors.map((e) => { + // Error format required for the debugger + const formattedError = { + message: e.errorMessage, + type: e.errorType, + }; - if (!(debuggerKey in updatedDebuggerErrors)) { - store.dispatch( - logDebuggerErrorAnalytics({ - eventName: "DEBUGGER_NEW_ERROR", - entityId: idField, - entityName: nameField, - entityType, - propertyPath, - errorMessages, - }), - ); - } + return formattedError; + }); const analyticsData = isWidget(entity) ? { @@ -114,14 +104,13 @@ function getLatestEvalPropertyErrors( : {}; // Add or update - updatedDebuggerErrors[debuggerKey] = { + AppsmithConsole.addError({ + id: debuggerKey, logType: LOG_TYPE.EVAL_ERROR, - text: PropertyEvalErrorTypeDebugMessage[error.errorType]( - propertyPath, - ), + // Unless the intention is to change the message shown in the debugger please do not + // change the text shown here + text: createMessage(VALUE_IS_INVALID, propertyPath), messages: errorMessages, - severity: error.severity, - timestamp: moment().format("hh:mm:ss"), source: { id: idField, name: nameField, @@ -132,25 +121,12 @@ function getLatestEvalPropertyErrors( [propertyPath]: evaluatedValue, }, analytics: analyticsData, - }; + }); } else if (debuggerKey in updatedDebuggerErrors) { - store.dispatch( - logDebuggerErrorAnalytics({ - eventName: "DEBUGGER_RESOLVED_ERROR", - entityId: idField, - entityName: nameField, - entityType, - propertyPath: - updatedDebuggerErrors[debuggerKey].source?.propertyPath ?? "", - errorMessages: updatedDebuggerErrors[debuggerKey].messages ?? [], - }), - ); - // Remove - delete updatedDebuggerErrors[debuggerKey]; + AppsmithConsole.deleteError(debuggerKey); } } } - return updatedDebuggerErrors; } export function* evalErrorHandler( @@ -159,19 +135,15 @@ export function* evalErrorHandler( evaluationOrder?: Array, ): any { if (dataTree && evaluationOrder) { - const currentDebuggerErrors: Record = yield select( + const currentDebuggerErrors: Record = yield select( getDebuggerErrors, ); - const evalPropertyErrors = getLatestEvalPropertyErrors( + // Update latest errors to the debugger + logLatestEvalPropertyErrors( currentDebuggerErrors, dataTree, evaluationOrder, ); - - yield put({ - type: ReduxActionTypes.DEBUGGER_UPDATE_ERROR_LOGS, - payload: evalPropertyErrors, - }); } errors.forEach((error) => { @@ -260,13 +232,13 @@ export function* logSuccessfulBindings( ); const entity = dataTree[entityName]; if (isAction(entity) || isWidget(entity)) { - const unevalValue = _.get(unEvalTree, evaluatedPath); + const unevalValue = get(unEvalTree, evaluatedPath); const entityType = isAction(entity) ? entity.pluginType : entity.type; - const isABinding = _.find(entity.dynamicBindingPathList, { + const isABinding = find(entity.dynamicBindingPathList, { key: propertyPath, }); const logBlackList = entity.logBlackList; - const errors: EvaluationError[] = _.get( + const errors: EvaluationError[] = get( dataTree, getEvalErrorPath(evaluatedPath), [], @@ -310,7 +282,7 @@ export function* updateTernDefinitions( shouldUpdate = false; } else { // Only when new field is added or deleted, we want to re create the def - shouldUpdate = _.some(updates, (update) => { + shouldUpdate = some(updates, (update) => { return ( update.event === DataTreeDiffEvent.NEW || update.event === DataTreeDiffEvent.DELETE diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 2706928eef..69d0b7e162 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -545,6 +545,9 @@ export function* deleteAllSelectedWidgetsSaga( type: ENTITY_TYPE.WIDGET, id: widget.widgetId, }, + analytics: { + widgetType: widget.type, + }, }); }); } @@ -666,6 +669,9 @@ export function* deleteSaga(deleteAction: ReduxAction) { type: ENTITY_TYPE.WIDGET, id: widget.widgetId, }, + analytics: { + widgetType: widget.type, + }, }); }); } diff --git a/app/client/src/utils/AnalyticsUtil.tsx b/app/client/src/utils/AnalyticsUtil.tsx index 8b0d3ffc20..ce5d5ed029 100644 --- a/app/client/src/utils/AnalyticsUtil.tsx +++ b/app/client/src/utils/AnalyticsUtil.tsx @@ -129,6 +129,8 @@ export type EventName = | "SLASH_COMMAND" | "DEBUGGER_NEW_ERROR" | "DEBUGGER_RESOLVED_ERROR" + | "DEBUGGER_NEW_ERROR_MESSAGE" + | "DEBUGGER_RESOLVED_ERROR_MESSAGE" | "ADD_MOCK_DATASOURCE_CLICK" | "CREATE_DATA_SOURCE_AUTH_API_CLICK" | "GEN_CRUD_PAGE_CREATE_NEW_DATASOURCE" diff --git a/app/client/src/utils/AppsmithConsole.ts b/app/client/src/utils/AppsmithConsole.ts index bfa1f4fd70..5fb5d1b339 100644 --- a/app/client/src/utils/AppsmithConsole.ts +++ b/app/client/src/utils/AppsmithConsole.ts @@ -1,10 +1,19 @@ -import { debuggerLogInit } from "actions/debuggerActions"; -import { Message, Severity, LogActionPayload } from "entities/AppsmithConsole"; +import { + addErrorLogInit, + debuggerLogInit, + deleteErrorLogInit, +} from "actions/debuggerActions"; +import { ReduxAction } from "constants/ReduxActionConstants"; +import { Severity, LogActionPayload, Log } from "entities/AppsmithConsole"; import moment from "moment"; import store from "store"; -function log(ev: Message) { - store.dispatch(debuggerLogInit(ev)); +function dispatchAction(action: ReduxAction) { + store.dispatch(action); +} + +function log(ev: Log) { + dispatchAction(debuggerLogInit(ev)); } function getTimeStamp() { @@ -27,6 +36,9 @@ function warning(ev: LogActionPayload) { }); } +// This is used to show a log as an error +// NOTE: These logs won't appear in the errors tab +// To add errors to the errors tab use the addError method. function error(ev: LogActionPayload) { log({ ...ev, @@ -35,8 +47,26 @@ function error(ev: LogActionPayload) { }); } +// This is used to add an error to the errors tab +function addError(payload: LogActionPayload) { + dispatchAction( + addErrorLogInit({ + ...payload, + severity: Severity.ERROR, + timestamp: getTimeStamp(), + }), + ); +} + +// This is used to remove an error from the errors tab +function deleteError(id: string, analytics?: Log["analytics"]) { + dispatchAction(deleteErrorLogInit(id, analytics)); +} + export default { info, warning, error, + addError, + deleteError, }; diff --git a/app/client/src/utils/helpers.tsx b/app/client/src/utils/helpers.tsx index 6a65d8f9ee..d61b67d4b9 100644 --- a/app/client/src/utils/helpers.tsx +++ b/app/client/src/utils/helpers.tsx @@ -13,6 +13,10 @@ import { isPermitted, PERMISSION_TYPE, } from "pages/Applications/permissionHelpers"; +import { User } from "constants/userConstants"; +import { getAppsmithConfigs } from "configs"; + +const { intercomAppID } = getAppsmithConfigs(); export const snapToGrid = ( columnWidth: number, @@ -397,3 +401,14 @@ export const getIsSafeRedirectURL = (redirectURL: string) => { return false; } }; + +export function bootIntercom(user?: User) { + if (intercomAppID && window.Intercom) { + window.Intercom("boot", { + app_id: intercomAppID, + user_id: user?.username, + name: user?.name, + email: user?.email, + }); + } +}