mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-22 08:28:35 +00:00
Merge pull request #12233 from ToolJet/fix/custom-debug-bugs
Fix: custom debug and transformation entities bugs
This commit is contained in:
commit
42f7dd856f
6 changed files with 162 additions and 58 deletions
|
|
@ -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, {});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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(/<anonymous>:(\d+):(\d+)/) ?? stackLines[1]?.match(/<anonymous>:(\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',
|
||||
|
|
|
|||
|
|
@ -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 }) {
|
|||
<p
|
||||
className="m-0 d-flex"
|
||||
onClick={(e) => {
|
||||
setOpen((prev) => !prev);
|
||||
logProps?.type !== 'Custom Log' && setOpen((prev) => !prev);
|
||||
}}
|
||||
style={{ pointerEvents: logProps?.isQuerySuccessLog ? 'none' : 'default', position: 'relative' }}
|
||||
>
|
||||
<span className={cx('position-absolute')} style={defaultStyles}>
|
||||
<SolidIcon name="rightarrrow" fill={`var(--icons-strong)`} width="16" />
|
||||
{logProps?.type !== 'Custom Log' && <SolidIcon name="rightarrrow" fill={`var(--icons-strong)`} width="16" />}
|
||||
</span>
|
||||
<span className="w-100" style={{ paddingTop: '8px', paddingBottom: '8px', paddingLeft: '20px' }}>
|
||||
{logProps.type === 'navToDisablePage' ? (
|
||||
|
|
@ -103,23 +103,32 @@ function Logs({ logProps, idx }) {
|
|||
<div className="error-target cursor-pointer">{logProps?.errorTarget}</div>
|
||||
<small className="text-slate-10 text-right ">{moment(logProps?.timestamp).fromNow()}</small>
|
||||
</div>
|
||||
<div className={`d-flex justify-content-between align-items-center ${!open && 'text-truncate'}`}>
|
||||
{logProps?.type === 'Custom Log' && (
|
||||
<div className="error-target-custom-log cursor-pointer">
|
||||
<SolidIcon name="code" fill={`var(--purple11)`} width="15" /> Custom Log
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={`d-flex justify-content-between align-items-center ${
|
||||
!open && logProps?.type !== 'Custom Log' && 'text-truncate'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={` cursor-pointer debugger-error-title ${!open && 'text-truncate'} ${
|
||||
logProps?.errorTarget == 'Custom Log' && logProps?.logLevel == 'error' && 'text-tomato-9'
|
||||
}`}
|
||||
className={` cursor-pointer debugger-error-title font-weight-500 ${
|
||||
!open && logProps?.type !== 'Custom Log' && 'text-truncate'
|
||||
} ${logProps?.type == 'Custom Log' && logProps?.logLevel == 'error' && 'text-tomato-9'}`}
|
||||
>
|
||||
<HighlightSecondWord text={title} />
|
||||
</span>
|
||||
</div>
|
||||
{logProps?.type == 'Custom Log' && <div className="font-weight-500">{message}</div>}
|
||||
<span
|
||||
className={cx('mx-1', {
|
||||
className={cx('font-weight-500', {
|
||||
'text-tomato-9': !logProps?.isQuerySuccessLog,
|
||||
'color-light-green': logProps?.isQuerySuccessLog,
|
||||
})}
|
||||
>
|
||||
{message}
|
||||
{logProps?.error?.lineNumber ? `, Line ${logProps.error.lineNumber}` : ''}
|
||||
{logProps?.type !== 'Custom Log' && message}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
21
frontend/src/_ui/Icon/solidIcons/Code.jsx
Normal file
21
frontend/src/_ui/Icon/solidIcons/Code.jsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import React from 'react';
|
||||
|
||||
const Code = ({ fill = 'var(--purple11)', width = '25', className = '', viewBox = '0 0 25 25' }) => (
|
||||
<svg
|
||||
width={width}
|
||||
height={width}
|
||||
viewBox={viewBox}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M4.05619 2.62763C4.45804 2.22576 5.0031 2 5.57142 2H14.1428C14.3323 2 14.514 2.07525 14.6479 2.20921L20.3623 7.9235C20.4961 8.05744 20.5714 8.23913 20.5714 8.42857V19.8571C20.5714 20.4254 20.3457 20.9706 19.9438 21.3724C19.542 21.7743 18.9968 22 18.4286 22H5.57142C5.00309 22 4.45804 21.7743 4.05619 21.3724C3.65433 20.9706 3.42856 20.4254 3.42856 19.8571V4.14286C3.42856 3.57454 3.65433 3.02949 4.05619 2.62763ZM10.6147 10.5281C11.0332 10.9465 11.0332 11.6249 10.6147 12.0433L8.51521 14.1429L10.6147 16.2424C11.0332 16.6609 11.0332 17.3391 10.6147 17.7576C10.1963 18.176 9.51793 18.176 9.09951 17.7576L6.24237 14.9005C5.82396 14.4821 5.82396 13.8037 6.24237 13.3852L9.09951 10.5281C9.51793 10.1097 10.1963 10.1097 10.6147 10.5281ZM13.3852 12.0433C12.9668 11.6249 12.9668 10.9465 13.3852 10.5281C13.8036 10.1097 14.482 10.1097 14.9005 10.5281L17.7576 13.3852C18.176 13.8037 18.176 14.4821 17.7576 14.9005L14.9005 17.7576C14.482 18.176 13.8036 18.176 13.3852 17.7576C12.9668 17.3391 12.9668 16.6609 13.3852 16.2424L15.4848 14.1429L13.3852 12.0433Z"
|
||||
fill={fill}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default Code;
|
||||
|
|
@ -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 <CircularToggleEnabled {...props} />;
|
||||
case 'clearrectangle':
|
||||
return <ClearRectangle {...props} />;
|
||||
case 'code':
|
||||
return <Code {...props} />;
|
||||
case 'clock':
|
||||
return <Clock {...props} />;
|
||||
case 'column':
|
||||
|
|
|
|||
Loading…
Reference in a new issue