Skip to content

Commit fbab826

Browse files
committed
Stricter Typing on Nodes
1 parent c426542 commit fbab826

27 files changed

+228
-207
lines changed

src/components/Editor/PipelineDetails.tsx

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { useContextPanel } from "@/providers/ContextPanelProvider";
1616
import {
1717
type InputSpec,
1818
type OutputSpec,
19-
type TypeSpecType,
19+
typeSpecToString,
2020
} from "@/utils/componentSpec";
2121
import { getComponentFileFromList } from "@/utils/componentStore";
2222
import { USER_PIPELINES_LIST_NAME } from "@/utils/constants";
@@ -36,17 +36,6 @@ const PipelineDetails = () => {
3636

3737
const [isYamlOpen, setIsYamlOpen] = useState(false);
3838

39-
// Utility function to convert TypeSpecType to string
40-
const typeSpecToString = (typeSpec?: TypeSpecType): string => {
41-
if (typeSpec === undefined) {
42-
return "Any";
43-
}
44-
if (typeof typeSpec === "string") {
45-
return typeSpec;
46-
}
47-
return JSON.stringify(typeSpec);
48-
};
49-
5039
// State for file metadata
5140
const [fileMeta, setFileMeta] = useState<{
5241
creationTime?: Date;

src/components/shared/ComponentEditor/usePreviewTaskNodeData.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { useQuery } from "@tanstack/react-query";
22

33
import { hydrateComponentReference } from "@/services/componentService";
4-
import type { TaskNodeData } from "@/types/taskNode";
4+
import type { TaskNodeData } from "@/types/nodes";
55
import type { HydratedComponentReference } from "@/utils/componentSpec";
66
import { generateTaskSpec } from "@/utils/nodes/generateTaskSpec";
7+
import { createEmptyTaskCallbacks } from "@/utils/nodes/taskCallbackUtils";
78

89
export const usePreviewTaskNodeData = (componentText: string) => {
910
const { data: componentRef, isLoading } = useQuery({
@@ -30,5 +31,8 @@ const generatePreviewTaskNodeData = (
3031
taskId: previewTaskId,
3132
isGhost: false,
3233
readOnly: true,
34+
connectable: false,
35+
highlighted: false,
36+
callbacks: createEmptyTaskCallbacks(),
3337
};
3438
};

src/components/shared/ReactFlow/FlowCanvas/FlowCanvas.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ const useScheduleExecutionOnceWhenConditionMet = (
9797
};
9898

9999
const FlowCanvas = ({
100-
readOnly,
100+
readOnly = false,
101101
nodesConnectable,
102102
children,
103103
...rest
@@ -307,7 +307,7 @@ const FlowCanvas = ({
307307
() => ({
308308
connectable: !readOnly && !!nodesConnectable,
309309
readOnly,
310-
nodeCallbacks,
310+
callbacks: nodeCallbacks,
311311
}),
312312
[readOnly, nodesConnectable, nodeCallbacks],
313313
);
@@ -494,14 +494,14 @@ const FlowCanvas = ({
494494
return;
495495
}
496496

497-
const { taskSpec: droppedTask, taskType } = getTaskFromEvent(event);
497+
const { taskSpec: droppedTask, nodeType } = getTaskFromEvent(event);
498498

499-
if (!taskType) {
499+
if (!nodeType) {
500500
console.error("Dropped task type not identified.");
501501
return;
502502
}
503503

504-
if (!droppedTask && taskType === "task") {
504+
if (!droppedTask && nodeType === "task") {
505505
console.error("Unable to find dropped task.");
506506
return;
507507
}
@@ -559,7 +559,7 @@ const FlowCanvas = ({
559559
const position = getPositionFromEvent(event, reactFlowInstance);
560560

561561
const newComponentSpec = addTask(
562-
taskType,
562+
nodeType,
563563
droppedTask,
564564
position,
565565
componentSpec,

src/components/shared/ReactFlow/FlowCanvas/GhostNode/GhostNode.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { memo, useMemo } from "react";
33

44
import { cn } from "@/lib/utils";
55
import { TaskNodeProvider } from "@/providers/TaskNodeProvider";
6-
import type { TaskNodeData } from "@/types/taskNode";
6+
import type { TaskNodeData } from "@/types/nodes";
77
import type { ComponentReference } from "@/utils/componentSpec";
88
import { generateTaskSpec } from "@/utils/nodes/generateTaskSpec";
9+
import { createEmptyTaskCallbacks } from "@/utils/nodes/taskCallbackUtils";
910

1011
import { TaskNodeCard } from "../TaskNode/TaskNodeCard";
1112

@@ -74,5 +75,9 @@ const generateGhostTaskNodeData = (
7475
taskSpec,
7576
taskId: ghostTaskId,
7677
isGhost: true,
78+
readOnly: true,
79+
highlighted: false,
80+
connectable: false,
81+
callbacks: createEmptyTaskCallbacks(),
7782
};
7883
};

src/components/shared/ReactFlow/FlowCanvas/GhostNode/HintNode.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { type NodeProps, useReactFlow } from "@xyflow/react";
22
import { memo, type PropsWithChildren, useMemo } from "react";
33

44
import { cn } from "@/lib/utils";
5-
import type { HintNodeData } from "@/types/hintNode";
5+
import type { HintNodeData } from "@/types/nodes";
66

77
const HintNode = ({ data }: NodeProps) => {
88
const { getZoom } = useReactFlow();

src/components/shared/ReactFlow/FlowCanvas/IONode/IONode.tsx

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,22 @@ import { Paragraph } from "@/components/ui/typography";
1010
import { cn } from "@/lib/utils";
1111
import { useComponentSpec } from "@/providers/ComponentSpecProvider";
1212
import { useContextPanel } from "@/providers/ContextPanelProvider";
13+
import type { IONodeData } from "@/types/nodes";
14+
import { isInputSpec, typeSpecToString } from "@/utils/componentSpec";
1315

1416
interface IONodeProps {
15-
type: "input" | "output";
16-
data: {
17-
label: string;
18-
value?: string;
19-
default?: string;
20-
type?: string;
21-
readOnly?: boolean;
22-
};
17+
data: IONodeData;
2318
selected: boolean;
2419
deletable: boolean;
2520
}
2621

27-
const IONode = ({ type, data, selected = false }: IONodeProps) => {
22+
const IONode = ({ data, selected = false }: IONodeProps) => {
2823
const { graphSpec, componentSpec } = useComponentSpec();
2924
const { setContent, clearContent } = useContextPanel();
3025

31-
const isInput = type === "input";
32-
const isOutput = type === "output";
26+
const { spec, readOnly } = data;
3327

34-
const readOnly = !!data.readOnly;
28+
const isInput = isInputSpec(spec);
3529

3630
const handleType = isInput ? "source" : "target";
3731
const handlePosition = isInput ? Position.Right : Position.Left;
@@ -44,13 +38,13 @@ const IONode = ({ type, data, selected = false }: IONodeProps) => {
4438
const borderColor = selected ? selectedColor : defaultColor;
4539

4640
const input = useMemo(
47-
() => componentSpec.inputs?.find((input) => input.name === data.label),
48-
[componentSpec.inputs, data.label],
41+
() => componentSpec.inputs?.find((input) => input.name === spec.name),
42+
[componentSpec.inputs, spec.name],
4943
);
5044

5145
const output = useMemo(
52-
() => componentSpec.outputs?.find((output) => output.name === data.label),
53-
[componentSpec.outputs, data.label],
46+
() => componentSpec.outputs?.find((output) => output.name === spec.name),
47+
[componentSpec.outputs, spec.name],
5448
);
5549

5650
useEffect(() => {
@@ -65,7 +59,7 @@ const IONode = ({ type, data, selected = false }: IONodeProps) => {
6559
);
6660
}
6761

68-
if (output && isOutput) {
62+
if (output && !isInput) {
6963
const outputConnectedDetails = getOutputConnectedDetails(
7064
graphSpec,
7165
output.name,
@@ -88,7 +82,7 @@ const IONode = ({ type, data, selected = false }: IONodeProps) => {
8882
};
8983
}, [input, output, selected, readOnly]);
9084

91-
const connectedOutput = getOutputConnectedDetails(graphSpec, data.label);
85+
const connectedOutput = getOutputConnectedDetails(graphSpec, spec.name);
9286
const outputConnectedValue = connectedOutput.outputName;
9387
const outputConnectedType = connectedOutput.outputType;
9488
const outputConnectedTaskId = connectedOutput.taskId;
@@ -98,30 +92,24 @@ const IONode = ({ type, data, selected = false }: IONodeProps) => {
9892

9993
const handleClassName = isInput ? "translate-x-1.5" : "-translate-x-1.5";
10094

101-
const hasDataValue = !!data.value;
102-
const hasDataDefault = !!data.default;
103-
104-
const inputValue = hasDataValue
105-
? data.value
106-
: hasDataDefault
107-
? data.default
108-
: null;
109-
95+
const inputValue = isInput ? (spec.value ?? spec.default ?? null) : null;
11096
const outputValue = outputConnectedValue ?? null;
111-
11297
const value = isInput ? inputValue : outputValue;
11398

99+
const typeValue = isInput
100+
? typeSpecToString(spec.type)
101+
: typeSpecToString(outputConnectedType);
102+
114103
return (
115104
<Card className={cn("border-2 max-w-[300px] p-0", borderColor)}>
116105
<CardHeader className="px-2 py-2.5">
117-
<CardTitle className="break-words">{data.label}</CardTitle>
106+
<CardTitle className="break-words">{spec.name}</CardTitle>
118107
</CardHeader>
119108
<CardContent className="p-2 max-w-[250px]">
120109
<BlockStack gap="2">
121110
{/* type */}
122111
<Paragraph size="xs" font="mono" className="truncate text-slate-700">
123-
<span className="font-bold">Type:</span>{" "}
124-
{outputConnectedType ?? data.type ?? "Any"}
112+
<span className="font-bold">Type:</span> {typeValue}
125113
</Paragraph>
126114

127115
{!!outputConnectedTaskId && (

src/components/shared/ReactFlow/FlowCanvas/TaskNode/ArgumentsEditor/ArgumentInputDialog.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { MultilineTextInputDialog } from "@/components/shared/Dialogs/MultilineTextInputDialog";
22
import type { ArgumentInput } from "@/types/arguments";
3+
import { typeSpecToString } from "@/utils/componentSpec";
34

4-
import { getInputValue, typeSpecToString } from "./utils";
5+
import { getInputValue } from "./utils";
56

67
export const ArgumentInputDialog = ({
78
argument,

src/components/shared/ReactFlow/FlowCanvas/TaskNode/ArgumentsEditor/ArgumentInputField.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,10 @@ import {
3131
import useToastNotification from "@/hooks/useToastNotification";
3232
import { cn } from "@/lib/utils";
3333
import type { ArgumentInput } from "@/types/arguments";
34+
import { typeSpecToString } from "@/utils/componentSpec";
3435

3536
import { ArgumentInputDialog } from "./ArgumentInputDialog";
36-
import {
37-
getDefaultValue,
38-
getInputValue,
39-
getPlaceholder,
40-
typeSpecToString,
41-
} from "./utils";
37+
import { getDefaultValue, getInputValue, getPlaceholder } from "./utils";
4238

4339
export const ArgumentInputField = ({
4440
argument,

src/components/shared/ReactFlow/FlowCanvas/TaskNode/ArgumentsEditor/utils.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import type { ArgumentInput } from "@/types/arguments";
2-
import type {
3-
ArgumentType,
4-
TaskSpec,
5-
TypeSpecType,
6-
} from "@/utils/componentSpec";
2+
import type { ArgumentType, TaskSpec } from "@/utils/componentSpec";
73

84
export const getArgumentInputs = (taskSpec: TaskSpec) => {
95
const componentSpec = taskSpec.componentRef.spec;
@@ -40,16 +36,6 @@ export const getArgumentInputs = (taskSpec: TaskSpec) => {
4036
return argumentInputs;
4137
};
4238

43-
export const typeSpecToString = (typeSpec?: TypeSpecType): string => {
44-
if (typeSpec === undefined) {
45-
return "Any";
46-
}
47-
if (typeof typeSpec === "string") {
48-
return typeSpec;
49-
}
50-
return JSON.stringify(typeSpec);
51-
};
52-
5339
export const getPlaceholder = (argument: ArgumentType) => {
5440
if (typeof argument === "string" || !argument) {
5541
return null;

src/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskNode.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { memo, useMemo } from "react";
44
import type { ContainerExecutionStatus } from "@/api/types.gen";
55
import { useComponentSpec } from "@/providers/ComponentSpecProvider";
66
import { TaskNodeProvider } from "@/providers/TaskNodeProvider";
7-
import type { TaskNodeData } from "@/types/taskNode";
7+
import type { TaskNodeData } from "@/types/nodes";
88

99
import { StatusIndicator } from "./StatusIndicator";
1010
import { TaskNodeCard } from "./TaskNodeCard";

0 commit comments

Comments
 (0)