Skip to content

Commit 7d73c7d

Browse files
committed
Stricter Typing on Nodes
1 parent d9b0b7a commit 7d73c7d

27 files changed

+237
-205
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: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,12 @@ 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 {
1517
type: "input" | "output";
16-
data: {
17-
label: string;
18-
value?: string;
19-
default?: string;
20-
type?: string;
21-
readOnly?: boolean;
22-
};
18+
data: IONodeData;
2319
selected: boolean;
2420
deletable: boolean;
2521
}
@@ -28,10 +24,9 @@ const IONode = ({ type, data, selected = false }: IONodeProps) => {
2824
const { graphSpec, componentSpec } = useComponentSpec();
2925
const { setContent, clearContent } = useContextPanel();
3026

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

34-
const readOnly = !!data.readOnly;
29+
const isInput = isInputSpec(spec, type);
3530

3631
const handleType = isInput ? "source" : "target";
3732
const handlePosition = isInput ? Position.Right : Position.Left;
@@ -44,13 +39,13 @@ const IONode = ({ type, data, selected = false }: IONodeProps) => {
4439
const borderColor = selected ? selectedColor : defaultColor;
4540

4641
const input = useMemo(
47-
() => componentSpec.inputs?.find((input) => input.name === data.label),
48-
[componentSpec.inputs, data.label],
42+
() => componentSpec.inputs?.find((input) => input.name === spec.name),
43+
[componentSpec.inputs, spec.name],
4944
);
5045

5146
const output = useMemo(
52-
() => componentSpec.outputs?.find((output) => output.name === data.label),
53-
[componentSpec.outputs, data.label],
47+
() => componentSpec.outputs?.find((output) => output.name === spec.name),
48+
[componentSpec.outputs, spec.name],
5449
);
5550

5651
useEffect(() => {
@@ -65,7 +60,7 @@ const IONode = ({ type, data, selected = false }: IONodeProps) => {
6560
);
6661
}
6762

68-
if (output && isOutput) {
63+
if (output && !isInput) {
6964
const outputConnectedDetails = getOutputConnectedDetails(
7065
graphSpec,
7166
output.name,
@@ -88,7 +83,7 @@ const IONode = ({ type, data, selected = false }: IONodeProps) => {
8883
};
8984
}, [input, output, selected, readOnly]);
9085

91-
const connectedOutput = getOutputConnectedDetails(graphSpec, data.label);
86+
const connectedOutput = getOutputConnectedDetails(graphSpec, spec.name);
9287
const outputConnectedValue = connectedOutput.outputName;
9388
const outputConnectedType = connectedOutput.outputType;
9489
const outputConnectedTaskId = connectedOutput.taskId;
@@ -98,30 +93,24 @@ const IONode = ({ type, data, selected = false }: IONodeProps) => {
9893

9994
const handleClassName = isInput ? "translate-x-1.5" : "-translate-x-1.5";
10095

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-
96+
const inputValue = isInput ? (spec.value ?? spec.default ?? null) : null;
11097
const outputValue = outputConnectedValue ?? null;
111-
11298
const value = isInput ? inputValue : outputValue;
11399

100+
const typeValue = isInput
101+
? typeSpecToString(spec.type)
102+
: typeSpecToString(outputConnectedType);
103+
114104
return (
115105
<Card className={cn("border-2 max-w-[300px] p-0", borderColor)}>
116106
<CardHeader className="px-2 py-2.5">
117-
<CardTitle className="break-words">{data.label}</CardTitle>
107+
<CardTitle className="break-words">{spec.name}</CardTitle>
118108
</CardHeader>
119109
<CardContent className="p-2 max-w-[250px]">
120110
<BlockStack gap="2">
121111
{/* type */}
122112
<Paragraph size="xs" font="mono" className="truncate text-slate-700">
123-
<span className="font-bold">Type:</span>{" "}
124-
{outputConnectedType ?? data.type ?? "Any"}
113+
<span className="font-bold">Type:</span> {typeValue}
125114
</Paragraph>
126115

127116
{!!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)