diff --git a/frontend/src/AppBuilder/_stores/slices/eventsSlice.js b/frontend/src/AppBuilder/_stores/slices/eventsSlice.js index 995050d023..077e0cadd4 100644 --- a/frontend/src/AppBuilder/_stores/slices/eventsSlice.js +++ b/frontend/src/AppBuilder/_stores/slices/eventsSlice.js @@ -444,7 +444,7 @@ export const createEventsSlice = (set, get) => ({ component: `[Page ${pageName}] [Component ${componentName}] [Event ${event?.eventId}] [Action ${event.actionId}]`, page: `[Page ${pageName}] [Event ${event.eventId}] [Action ${event.actionId}]`, query: `[Query ${getQueryName()}] [Event ${event.eventId}] [Action ${event.actionId}]`, - customLog: `${event.description}`, + customLog: `${event.key}`, }; return headerMap[source] || ''; @@ -457,7 +457,7 @@ export const createEventsSlice = (set, get) => ({ page: 'Event Errors with page', component: 'Component Event', query: 'Event Errors with query', - customLog: 'Custom Log', + customLog: 'Queries', }; return errorTargetMap[source]; @@ -470,8 +470,9 @@ export const createEventsSlice = (set, get) => ({ error: { message: error.message, description: JSON.stringify(error.message, null, 2), - ...(event.component && componentId && { componentId: componentId }), + ...(event.component === 'component' && componentId && { componentId: componentId }), }, + description: event?.description, errorTarget: constructErrorTarget(), options: options, strace: 'app_level', @@ -1128,30 +1129,51 @@ export const createEventsSlice = (set, get) => ({ return executeAction(event, mode, {}); }; - const logInfo = (log) => { + const logInfo = (log, isFromTransformation) => { + const query = dataQuery.queries.modules['canvas'].find((query) => query.id == queryId); const error = new Error(); - const stackLine = error.stack.split('\n')[2]; + const stackLine = error.stack.split('\n')[isFromTransformation ? 3 : 2]; const lineNumberMatch = stackLine.match(/:(\d+):\d+\)$/); const lineNumber = lineNumberMatch ? lineNumberMatch[1] : 'unknown'; - const event = { actionId: 'log-info', description: `${log}, Line ${lineNumber - 2}`, eventType: 'customLog' }; + const event = { + actionId: 'log-info', + key: `${query.name}${isFromTransformation ? ', transformation' : ''}, line ${lineNumber - 2}`, + description: log, + eventType: 'customLog', + query, + }; return executeAction(event, mode, {}); }; - const logError = (log) => { + const logError = (log, isFromTransformation = false) => { + const query = dataQuery.queries.modules['canvas'].find((query) => query.id == queryId); const error = new Error(); - const stackLine = error.stack.split('\n')[2]; + const stackLine = error.stack.split('\n')[isFromTransformation ? 3 : 2]; const lineNumberMatch = stackLine.match(/:(\d+):\d+\)$/); const lineNumber = lineNumberMatch ? lineNumberMatch[1] : 'unknown'; - const event = { actionId: 'log-error', description: `${log}, Line ${lineNumber - 2}`, eventType: 'customLog' }; + const event = { + actionId: 'log-error', + key: `${query.name}${isFromTransformation ? ', transformation' : ''}, line ${lineNumber - 2}`, + description: log, + eventType: 'customLog', + query, + }; return executeAction(event, mode, {}); }; - const log = (log) => { + const log = (log, isFromTransformation = false) => { + const query = dataQuery.queries.modules['canvas'].find((query) => query.id == queryId); const error = new Error(); - const stackLine = error.stack.split('\n')[2]; + const stackLine = error.stack.split('\n')[isFromTransformation ? 3 : 2]; const lineNumberMatch = stackLine.match(/:(\d+):\d+\)$/); const lineNumber = lineNumberMatch ? lineNumberMatch[1] : 'unknown'; - const event = { actionId: 'log', description: `${log}, Line ${lineNumber - 2}`, eventType: 'customLog' }; + const event = { + actionId: 'log', + key: `${query.name}${isFromTransformation ? ', transformation' : ''}, line ${lineNumber - 2}`, + description: log, + eventType: 'customLog', + query, + }; return executeAction(event, mode, {}); }; diff --git a/frontend/src/AppBuilder/_stores/slices/queryPanelSlice.js b/frontend/src/AppBuilder/_stores/slices/queryPanelSlice.js index fbfb931130..811ebfc958 100644 --- a/frontend/src/AppBuilder/_stores/slices/queryPanelSlice.js +++ b/frontend/src/AppBuilder/_stores/slices/queryPanelSlice.js @@ -446,7 +446,7 @@ export const createQueryPanelSlice = (set, get) => ({ query, 'edit' ); - if (finalData.status === 'failed') { + if (finalData?.status === 'failed') { setResolvedQuery(queryId, { isLoading: false, }); @@ -650,7 +650,7 @@ export const createQueryPanelSlice = (set, get) => ({ query, 'edit' ); - if (finalData.status === 'failed') { + if (finalData?.status === 'failed') { onEvent('onDataQueryFailure', queryEvents); setPreviewLoading(false); setIsPreviewQueryLoading(false); @@ -778,18 +778,30 @@ export const createQueryPanelSlice = (set, get) => ({ runTransformation: async (rawData, transformation, transformationLanguage = 'javascript', query, mode = 'edit') => { const data = rawData; const { - queryPanel: { runPythonTransformation }, + queryPanel: { runPythonTransformation, createProxy }, getResolvedState, } = get(); - let result = []; + let result = {}; const currentState = getResolvedState(); if (transformationLanguage === 'python') { result = await runPythonTransformation(currentState, data, transformation, query, mode); } else if (transformationLanguage === 'javascript') { try { + const { eventsSlice } = get(); + const { generateAppActions } = eventsSlice; + const queriesInResolvedState = deepClone(currentState.queries); + const actions = generateAppActions(query?.id, mode); + + const proxiedComponents = createProxy(currentState?.components, 'components'); + const proxiedGlobals = createProxy(currentState?.globals, 'globals'); + const proxiedConstants = createProxy(currentState?.constants, 'constants'); + const proxiedVariables = createProxy(currentState?.variables, 'variables'); + const proxiedPage = createProxy(deepClone(currentState?.page, 'page')); + const proxiedQueriesInResolvedState = createProxy(queriesInResolvedState, 'queries'); + const evalFunction = Function( - ['data', 'moment', '_', 'components', 'queries', 'globals', 'variables', 'page', 'constants'], + ['data', 'moment', '_', 'components', 'queries', 'globals', 'variables', 'page', 'constants', 'actions'], transformation ); @@ -797,32 +809,51 @@ export const createQueryPanelSlice = (set, get) => ({ data, moment, _, - currentState.components, - currentState.queries, - currentState.globals, - currentState.variables, - currentState.page, - currentState.constants + proxiedComponents, + proxiedQueriesInResolvedState, + proxiedGlobals, + proxiedVariables, + proxiedPage, + proxiedConstants, + { + logError: function (log) { + return actions.logError.call(actions, log, true); + }, + logInfo: function (log) { + return actions.logInfo.call(actions, log, true); + }, + log: function (log) { + return actions.log.call(actions, log, true); + }, + } ); } catch (err) { - result = { - message: err.stack.split('\n')[0], - status: 'failed', - data: data, - }; + const stackLines = err.stack.split('\n'); + const errorLocation = + stackLines[2]?.match(/:(\d+):(\d+)/) ?? stackLines[1]?.match(/:(\d+):(\d+)/); + + let lineNumber = null; + + if (errorLocation) { + lineNumber = errorLocation[1] - 2; + } + + console.log('JS execution failed: ', err); + let error = err.message || err.stack.split('\n')[0] || 'JS execution failed'; + result = { status: 'failed', data: { message: error, description: error, lineNumber } }; + get().debugger.log({ + logLevel: result?.status === 'failed' ? 'error' : 'success', + type: 'transformation', + kind: query.kind, + key: `${query.name}, transformation, line ${result?.data?.lineNumber}`, + message: result?.message, + error: result?.data, + isTransformation: true, + isQuerySuccessLog: result?.status === 'failed' ? false : true, + errorTarget: 'Queries', + }); } } - get().debugger.log({ - logLevel: result?.status === 'failed' ? 'error' : 'success', - type: 'transformation', - kind: query.kind, - key: query.name, - message: result?.message, - error: result, - isTransformation: true, - isQuerySuccessLog: result?.status === 'failed' ? false : true, - errorTarget: 'Queries', - }); return result; }, @@ -890,12 +921,13 @@ export const createQueryPanelSlice = (set, get) => ({ createProxy: (obj, path = '') => { const { queryPanel } = get(); const { createProxy } = queryPanel; + return new Proxy(obj, { get(target, prop) { const fullPath = path ? `${path}.${prop}` : prop; if (!(prop in target)) { - throw new Error(`Property "${fullPath}" is not defined`); + throw new Error(`ReferenceError: ${fullPath} is not defined`); } const value = target[prop]; @@ -984,13 +1016,16 @@ export const createQueryPanelSlice = (set, get) => ({ //Proxy Func required to get current execution line number from stack to log in debugger - const proxiedComponents = createProxy(resolvedState?.components); - const proxiedGlobals = createProxy(resolvedState?.globals); - const proxiedConstants = createProxy(resolvedState?.constants); - const proxiedVariables = createProxy(resolvedState?.variables); - const proxiedPage = createProxy(deepClone(resolvedState?.page)); - const proxiedQueriesInResolvedState = createProxy(queriesInResolvedState); - const proxiedFormattedParams = createProxy(!_.isEmpty(proxiedFormattedParams) ? [proxiedFormattedParams] : []); + const proxiedComponents = createProxy(deepClone(resolvedState?.components), 'components'); + const proxiedGlobals = createProxy(deepClone(resolvedState?.globals), 'globals'); + const proxiedConstants = createProxy(deepClone(resolvedState?.constants), 'constants'); + const proxiedVariables = createProxy(deepClone(resolvedState?.variables), 'variables'); + const proxiedPage = createProxy(deepClone(resolvedState?.page, 'page')); + const proxiedQueriesInResolvedState = createProxy(deepClone(queriesInResolvedState), 'queries'); + const proxiedFormattedParams = createProxy( + !_.isEmpty(proxiedFormattedParams) ? [proxiedFormattedParams] : [], + 'params' + ); const fnParams = [ 'moment', diff --git a/frontend/src/Editor/LeftSidebar/SidebarDebugger/Logs.jsx b/frontend/src/Editor/LeftSidebar/SidebarDebugger/Logs.jsx index aba2ca6af1..a30585bf42 100644 --- a/frontend/src/Editor/LeftSidebar/SidebarDebugger/Logs.jsx +++ b/frontend/src/Editor/LeftSidebar/SidebarDebugger/Logs.jsx @@ -27,7 +27,7 @@ function Logs({ logProps, idx }) { logProps?.description || (isString(logProps?.message) && logProps?.message) || (isString(logProps?.error?.description) && logProps?.error?.description) || //added string check since description can be an object. eg: runpy - logProps?.error?.message.trim() + logProps?.error?.message }`; const defaultStyles = { @@ -87,12 +87,12 @@ function Logs({ logProps, idx }) {

{ - setOpen((prev) => !prev); + logProps?.type !== 'Custom Log' && setOpen((prev) => !prev); }} style={{ pointerEvents: logProps?.isQuerySuccessLog ? 'none' : 'default', position: 'relative' }} > - + {logProps?.type !== 'Custom Log' && } {logProps.type === 'navToDisablePage' ? ( @@ -103,23 +103,32 @@ function Logs({ logProps, idx }) {

{logProps?.errorTarget}
{moment(logProps?.timestamp).fromNow()} -
+ {logProps?.type === 'Custom Log' && ( +
+ Custom Log +
+ )} +
+ {logProps?.type == 'Custom Log' &&
{message}
} - {message} - {logProps?.error?.lineNumber ? `, Line ${logProps.error.lineNumber}` : ''} + {logProps?.type !== 'Custom Log' && message} )} diff --git a/frontend/src/_styles/left-sidebar.scss b/frontend/src/_styles/left-sidebar.scss index 82b6762b54..1883213fa8 100644 --- a/frontend/src/_styles/left-sidebar.scss +++ b/frontend/src/_styles/left-sidebar.scss @@ -195,6 +195,7 @@ .text-tomato-9 { color: var(--tomato9); + font-weight: 500; } .text-slate-10 { @@ -252,12 +253,25 @@ background-color: var(--slate3); } - .error-target { + .error-target, .error-target-custom-log { background-color: var(--interactive-overlays-fill-hover) !important; padding: 4px 7px; border-radius: 7px; color: var(--slate10) } + + .error-target-custom-log { + display: flex; + align-items: center; + margin-top: 5px; + background: var(--purple5) !important; + color: var(--purple11); + width: fit-content; + + svg { + margin-right: 1px; + } + } } diff --git a/frontend/src/_ui/Icon/solidIcons/Code.jsx b/frontend/src/_ui/Icon/solidIcons/Code.jsx new file mode 100644 index 0000000000..d4022f94d0 --- /dev/null +++ b/frontend/src/_ui/Icon/solidIcons/Code.jsx @@ -0,0 +1,21 @@ +import React from 'react'; + +const Code = ({ fill = 'var(--purple11)', width = '25', className = '', viewBox = '0 0 25 25' }) => ( + + + +); + +export default Code; diff --git a/frontend/src/_ui/Icon/solidIcons/index.js b/frontend/src/_ui/Icon/solidIcons/index.js index 34a2410e1d..02ef072f4a 100644 --- a/frontend/src/_ui/Icon/solidIcons/index.js +++ b/frontend/src/_ui/Icon/solidIcons/index.js @@ -229,6 +229,7 @@ import CalendarSmall from './CalendarSmall.jsx'; import UserGroupsGrey from './UserGroupsGrey.jsx'; import AppLimitSvg from './AppLimitSvg.jsx'; import NewTabSmall from './NewTabSmall.jsx'; +import Code from './Code.jsx'; const Icon = (props) => { switch (props.name) { @@ -308,6 +309,8 @@ const Icon = (props) => { return ; case 'clearrectangle': return ; + case 'code': + return ; case 'clock': return ; case 'column':