Allow json in workflow run's error field (#12762)
We can now inspect errors even if they contain complex data as objects. Only the first line of the error is put in red.  
This commit is contained in:
committed by
GitHub
parent
1e0ee9421d
commit
22e126869c
@ -3,6 +3,7 @@ import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
|||||||
import { WorkflowRun } from '@/workflow/types/Workflow';
|
import { WorkflowRun } from '@/workflow/types/Workflow';
|
||||||
import { workflowRunSchema } from '@/workflow/validation-schemas/workflowSchema';
|
import { workflowRunSchema } from '@/workflow/validation-schemas/workflowSchema';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
export const useWorkflowRun = ({
|
export const useWorkflowRun = ({
|
||||||
workflowRunId,
|
workflowRunId,
|
||||||
@ -14,13 +15,18 @@ export const useWorkflowRun = ({
|
|||||||
objectRecordId: workflowRunId,
|
objectRecordId: workflowRunId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { success, data: record } = useMemo(
|
const {
|
||||||
() => workflowRunSchema.safeParse(rawRecord),
|
success,
|
||||||
[rawRecord],
|
data: record,
|
||||||
);
|
error,
|
||||||
|
} = useMemo(() => workflowRunSchema.safeParse(rawRecord), [rawRecord]);
|
||||||
|
|
||||||
|
if (!isDefined(rawRecord)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
return undefined;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return record;
|
return record;
|
||||||
|
|||||||
@ -271,7 +271,7 @@ export const workflowTriggerSchema = z.discriminatedUnion('type', [
|
|||||||
// Step output schemas
|
// Step output schemas
|
||||||
export const workflowExecutorOutputSchema = z.object({
|
export const workflowExecutorOutputSchema = z.object({
|
||||||
result: z.any().optional(),
|
result: z.any().optional(),
|
||||||
error: z.string().optional(),
|
error: z.any().optional(),
|
||||||
pendingEvent: z.boolean().optional(),
|
pendingEvent: z.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -286,7 +286,7 @@ export const workflowRunOutputSchema = z.object({
|
|||||||
steps: z.array(workflowActionSchema),
|
steps: z.array(workflowActionSchema),
|
||||||
}),
|
}),
|
||||||
stepsOutput: workflowRunOutputStepsOutputSchema.optional(),
|
stepsOutput: workflowRunOutputStepsOutputSchema.optional(),
|
||||||
error: z.string().optional(),
|
error: z.any().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const workflowRunContextSchema = z.record(z.any());
|
export const workflowRunContextSchema = z.record(z.any());
|
||||||
|
|||||||
@ -67,7 +67,13 @@ export const WorkflowRunStepOutputDetail = ({ stepId }: { stepId: string }) => {
|
|||||||
? getTriggerHeaderType(stepDefinition.definition)
|
? getTriggerHeaderType(stepDefinition.definition)
|
||||||
: i18n._(getActionHeaderTypeOrThrow(stepDefinition.definition.type));
|
: i18n._(getActionHeaderTypeOrThrow(stepDefinition.definition.type));
|
||||||
|
|
||||||
const setRedHighlightingForEveryNode: GetJsonNodeHighlighting = () => 'red';
|
const setRedHighlightingForEveryNode: GetJsonNodeHighlighting = (keyPath) => {
|
||||||
|
if (keyPath === 'error') {
|
||||||
|
return 'red';
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -571,6 +571,9 @@ export const RedHighlighting: Story = {
|
|||||||
value: {
|
value: {
|
||||||
name: 'John Doe',
|
name: 'John Doe',
|
||||||
age: 30,
|
age: 30,
|
||||||
|
address: {
|
||||||
|
city: 'Paris',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
getNodeHighlighting: () => 'red',
|
getNodeHighlighting: () => 'red',
|
||||||
},
|
},
|
||||||
|
|||||||
@ -25,8 +25,9 @@ const StyledLabelContainer = styled.div`
|
|||||||
gap: ${({ theme }) => theme.spacing(2)};
|
gap: ${({ theme }) => theme.spacing(2)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledElementsCount = styled.span`
|
const StyledElementsCount = styled.span<{ variant?: 'red' }>`
|
||||||
color: ${({ theme }) => theme.font.color.tertiary};
|
color: ${({ theme, variant }) =>
|
||||||
|
variant === 'red' ? theme.font.color.danger : theme.font.color.tertiary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledJsonList = styled(JsonList)``.withComponent(motion.ul);
|
const StyledJsonList = styled(JsonList)``.withComponent(motion.ul);
|
||||||
@ -118,13 +119,25 @@ export const JsonNestedNode = ({
|
|||||||
<JsonArrow
|
<JsonArrow
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onClick={handleArrowClick}
|
onClick={handleArrowClick}
|
||||||
variant={highlighting === 'partial-blue' ? 'blue' : undefined}
|
variant={
|
||||||
|
highlighting === 'partial-blue'
|
||||||
|
? 'blue'
|
||||||
|
: highlighting === 'red'
|
||||||
|
? highlighting
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<JsonNodeLabel label={label} Icon={Icon} />
|
<JsonNodeLabel
|
||||||
|
label={label}
|
||||||
|
Icon={Icon}
|
||||||
|
highlighting={highlighting === 'red' ? highlighting : undefined}
|
||||||
|
/>
|
||||||
|
|
||||||
{renderElementsCount && (
|
{renderElementsCount && (
|
||||||
<StyledElementsCount>
|
<StyledElementsCount
|
||||||
|
variant={highlighting === 'red' ? 'red' : undefined}
|
||||||
|
>
|
||||||
{renderElementsCount(elements.length)}
|
{renderElementsCount(elements.length)}
|
||||||
</StyledElementsCount>
|
</StyledElementsCount>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -6,16 +6,20 @@ import { useJsonTreeContextOrThrow } from '@ui/json-visualizer/hooks/useJsonTree
|
|||||||
import { ANIMATION } from '@ui/theme';
|
import { ANIMATION } from '@ui/theme';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
const StyledButton = styled(motion.button)`
|
const StyledButton = styled(motion.button)<{ variant?: 'blue' | 'red' }>`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-color: ${({ theme }) => theme.border.color.medium};
|
background-color: ${({ theme, variant }) =>
|
||||||
|
variant === 'red'
|
||||||
|
? theme.background.danger
|
||||||
|
: theme.background.transparent.lighter};
|
||||||
|
border-color: ${({ theme, variant }) =>
|
||||||
|
variant === 'red' ? theme.border.color.danger : theme.border.color.medium};
|
||||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding-inline: ${({ theme }) => theme.spacing(1)};
|
padding-inline: ${({ theme }) => theme.spacing(1)};
|
||||||
background-color: ${({ theme }) => theme.background.transparent.lighter};
|
|
||||||
height: 24px;
|
height: 24px;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -31,7 +35,7 @@ export const JsonArrow = ({
|
|||||||
}: {
|
}: {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
variant?: 'blue';
|
variant?: 'blue' | 'red';
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
@ -39,7 +43,7 @@ export const JsonArrow = ({
|
|||||||
useJsonTreeContextOrThrow();
|
useJsonTreeContextOrThrow();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledButton onClick={onClick}>
|
<StyledButton variant={variant} onClick={onClick}>
|
||||||
<VisibilityHidden>
|
<VisibilityHidden>
|
||||||
{isOpen ? arrowButtonExpandedLabel : arrowButtonCollapsedLabel}
|
{isOpen ? arrowButtonExpandedLabel : arrowButtonCollapsedLabel}
|
||||||
</VisibilityHidden>
|
</VisibilityHidden>
|
||||||
@ -47,7 +51,11 @@ export const JsonArrow = ({
|
|||||||
<MotionIconChevronDown
|
<MotionIconChevronDown
|
||||||
size={theme.icon.size.md}
|
size={theme.icon.size.md}
|
||||||
color={
|
color={
|
||||||
variant === 'blue' ? theme.color.blue : theme.font.color.secondary
|
variant === 'blue'
|
||||||
|
? theme.color.blue
|
||||||
|
: variant === 'red'
|
||||||
|
? theme.font.color.danger
|
||||||
|
: theme.font.color.secondary
|
||||||
}
|
}
|
||||||
initial={false}
|
initial={false}
|
||||||
animate={{ rotate: isOpen ? 0 : -90 }}
|
animate={{ rotate: isOpen ? 0 : -90 }}
|
||||||
|
|||||||
Reference in New Issue
Block a user