Skip to content

Commit b27c731

Browse files
feat(frontend): only have open subscriptions for active workflows
1 parent 685d9e9 commit b27c731

14 files changed

+185
-133
lines changed

frontend/dashboard/src/RelayEnvironment.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ const fetchFn: FetchFunction = async (request, variables) => {
7676
}
7777
};
7878

79-
const wsClient = createClient({
79+
export const wsClient = createClient({
8080
url: WS_ENDPOINT,
8181
connectionParams: async () => {
8282
if (!keycloak.authenticated) {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useEffect, useState } from "react";
2+
import { wsClient } from "./RelayEnvironment";
3+
import { Typography } from "@mui/material";
4+
5+
export function WebSocketDebugger() {
6+
const [status, setStatus] = useState<
7+
"connected" | "closed" | "error" | "unknown"
8+
>("unknown");
9+
10+
useEffect(() => {
11+
const handleConnected = () => {
12+
setStatus("connected");
13+
};
14+
const handleClosed = () => {
15+
setStatus("closed");
16+
};
17+
const handleError = () => {
18+
setStatus("error");
19+
};
20+
21+
wsClient.on("connected", handleConnected);
22+
wsClient.on("closed", handleClosed);
23+
wsClient.on("error", handleError);
24+
25+
return () => {};
26+
}, []);
27+
28+
return <Typography>WebSocket Status: {status}</Typography>;
29+
}

frontend/relay-workflows-lib/lib/components/BaseSingleWorkflowView.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,22 @@ import { TaskInfo } from "workflows-lib/lib/components/workflow/TaskInfo";
44
import { buildTaskTree } from "workflows-lib/lib/utils/tasksFlowUtils";
55
import { Artifact, Task, TaskNode } from "workflows-lib/lib/types";
66
import { useFetchedTasks, useSelectedTasks } from "./workflowRelayUtils";
7-
import WorkflowRelay from "./WorkflowRelay";
87
import WorkflowInfo from "./WorkflowInfo";
98
import { workflowRelaySubscription$data } from "../graphql/__generated__/workflowRelaySubscription.graphql";
10-
import { SingleWorkflowViewProps } from "./SingleWorkflowView";
9+
import WorkflowRelay from "./WorkflowRelay";
1110

12-
interface Props extends SingleWorkflowViewProps {
11+
interface BaseSingleWorkflowViewProps {
1312
data: workflowRelaySubscription$data | null;
13+
tasknames?: string[];
1414
}
1515

1616
export default function BaseSingleWorkflowView({
17-
visit,
18-
workflowName,
1917
tasknames,
2018
data,
21-
}: Props) {
19+
}: BaseSingleWorkflowViewProps) {
2220
const [artifactList, setArtifactList] = useState<Artifact[]>([]);
2321
const [outputTasks, setOutputTasks] = useState<string[]>([]);
24-
const fetchedTasks = useFetchedTasks(data, visit, workflowName);
22+
const fetchedTasks = useFetchedTasks(data);
2523
const [selectedTasks, setSelectedTasks] = useSelectedTasks();
2624
const [filledTaskName, setFilledTaskName] = useState<string | null>(null);
2725

@@ -120,8 +118,7 @@ export default function BaseSingleWorkflowView({
120118
</Box>
121119
{data && (
122120
<WorkflowRelay
123-
workflowName={data.workflow.name}
124-
visit={data.workflow.visit}
121+
data={data}
125122
workflowLink
126123
filledTaskName={filledTaskName}
127124
expanded={true}

frontend/relay-workflows-lib/lib/components/BaseWorkflowRelay.tsx

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from "react";
22
import { ResizableBox } from "react-resizable";
33
import { Box } from "@mui/material";
4-
import { Visit, visitToText } from "@diamondlightsource/sci-react-ui";
4+
import { visitToText } from "@diamondlightsource/sci-react-ui";
55
import {
66
TasksFlow,
77
WorkflowAccordion,
@@ -12,31 +12,27 @@ import { useFetchedTasks, useSelectedTasks } from "./workflowRelayUtils";
1212
import { workflowRelaySubscription$data } from "../graphql/__generated__/workflowRelaySubscription.graphql";
1313
import { useParams, useNavigate } from "react-router-dom";
1414

15-
interface Props {
16-
visit: Visit;
17-
workflowName: string;
15+
interface BaseWorkflowRelayProps {
1816
workflowLink?: boolean;
1917
filledTaskName?: string | null;
2018
expanded?: boolean;
2119
onChange?: () => void;
22-
data: workflowRelaySubscription$data | null;
20+
data: workflowRelaySubscription$data;
2321
}
2422

2523
export default function BaseWorkflowRelay({
26-
visit,
27-
workflowName,
2824
workflowLink,
2925
filledTaskName,
3026
expanded,
3127
onChange,
3228
data,
33-
}: Props) {
29+
}: BaseWorkflowRelayProps) {
3430
const { workflowName: workflowNameURL } = useParams<{
3531
workflowName: string;
3632
}>();
3733
const navigate = useNavigate();
38-
const statusText = data?.workflow.status?.__typename ?? "Unknown";
39-
const fetchedTasks = useFetchedTasks(data, visit, workflowName);
34+
const statusText = data.workflow.status?.__typename ?? "Unknown";
35+
const fetchedTasks = useFetchedTasks(data);
4036
const [selectedTasks, setSelectedTasks] = useSelectedTasks();
4137

4238
const onNavigate = React.useCallback(
@@ -53,19 +49,14 @@ export default function BaseWorkflowRelay({
5349
} else {
5450
updatedTasks = [taskName];
5551
}
56-
if (workflowNameURL !== workflowName) {
57-
void navigate(`/workflows/${visitToText(visit)}/${workflowName}`);
52+
if (workflowNameURL !== data.workflow.name) {
53+
void navigate(
54+
`/workflows/${visitToText(data.workflow.visit)}/${data.workflow.name}`,
55+
);
5856
}
5957
setSelectedTasks(updatedTasks);
6058
},
61-
[
62-
navigate,
63-
selectedTasks,
64-
setSelectedTasks,
65-
visit,
66-
workflowName,
67-
workflowNameURL,
68-
],
59+
[navigate, selectedTasks, setSelectedTasks, workflowNameURL, data],
6960
);
7061

7162
return (
@@ -85,8 +76,8 @@ export default function BaseWorkflowRelay({
8576
>
8677
<WorkflowAccordion
8778
workflow={{
88-
name: workflowName,
89-
instrumentSession: visit,
79+
name: data.workflow.name,
80+
instrumentSession: data.workflow.visit,
9081
status: statusText as WorkflowStatus,
9182
}}
9283
workflowLink={workflowLink}
@@ -111,7 +102,7 @@ export default function BaseWorkflowRelay({
111102
}}
112103
>
113104
<TasksFlow
114-
workflowName={workflowName}
105+
workflowName={data.workflow.name}
115106
tasks={fetchedTasks}
116107
onNavigate={onNavigate}
117108
highlightedTaskNames={selectedTasks}

frontend/relay-workflows-lib/lib/components/LiveSingleWorkflowView.tsx

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,5 @@ export default function LiveWorkflowView({
3737

3838
useSubscription(subscriptionData);
3939

40-
return (
41-
<BaseSingleWorkflowView
42-
visit={visit}
43-
workflowName={workflowName}
44-
tasknames={tasknames}
45-
data={workflowData}
46-
/>
47-
);
40+
return <BaseSingleWorkflowView tasknames={tasknames} data={workflowData} />;
4841
}
Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,67 @@
1-
import { useMemo, useState } from "react";
2-
import { useSubscription } from "react-relay";
1+
import { useEffect, useRef, useState } from "react";
2+
import { requestSubscription, useRelayEnvironment } from "react-relay";
33
import { workflowRelaySubscription } from "../graphql/workflowRelaySubscription";
44
import {
55
workflowRelaySubscription$data,
66
workflowRelaySubscription as WorkflowRelaySubscriptionType,
77
} from "../graphql/__generated__/workflowRelaySubscription.graphql";
88
import BaseWorkflowRelay from "./BaseWorkflowRelay";
99
import { WorkflowRelayProps } from "./WorkflowRelay";
10+
import { isFinished } from "../utils";
1011

11-
export default function LiveWorkflowRelay(props: WorkflowRelayProps) {
12+
interface LiveWorkflowRelayProps extends WorkflowRelayProps {
13+
onNull: () => void;
14+
}
15+
16+
export default function LiveWorkflowRelay(props: LiveWorkflowRelayProps) {
1217
const [workflowData, setWorkflowData] =
1318
useState<workflowRelaySubscription$data | null>(null);
1419

15-
const subscriptionConfig = useMemo(
16-
() => ({
17-
subscription: workflowRelaySubscription,
18-
variables: {
19-
visit: props.visit,
20-
name: props.workflowName,
21-
},
22-
onNext: (response?: workflowRelaySubscription$data | null) => {
23-
if (response) {
24-
setWorkflowData(response);
25-
}
26-
},
27-
onError: (error: unknown) => {
28-
console.error("Subscription error:", error);
29-
},
30-
onCompleted: () => {
31-
console.log("completed");
20+
const subscriptionRef = useRef<{ dispose: () => void } | null>(null);
21+
const environment = useRelayEnvironment();
22+
useEffect(() => {
23+
const subscription = requestSubscription<WorkflowRelaySubscriptionType>(
24+
environment,
25+
{
26+
subscription: workflowRelaySubscription,
27+
variables: {
28+
visit: props.data.workflow.visit,
29+
name: props.data.workflow.name,
30+
},
31+
onNext: (response?: workflowRelaySubscription$data | null) => {
32+
if (response) {
33+
setWorkflowData(response);
34+
35+
if (isFinished(response)) {
36+
console.log("Workflow finished, unsubscribing.");
37+
subscriptionRef.current?.dispose();
38+
}
39+
} else {
40+
props.onNull();
41+
}
42+
},
43+
onError: (error: unknown) => {
44+
console.error("Subscription error:", error);
45+
},
46+
onCompleted: () => {
47+
console.log("completed");
48+
},
3249
},
33-
}),
34-
[props.visit, props.workflowName],
35-
);
50+
);
51+
52+
subscriptionRef.current = subscription;
3653

37-
useSubscription<WorkflowRelaySubscriptionType>(subscriptionConfig);
54+
return () => {
55+
subscriptionRef.current?.dispose();
56+
};
57+
}, [
58+
props.data.workflow.visit,
59+
props.data.workflow.name,
60+
props.onNull,
61+
environment,
62+
]);
3863

39-
return <BaseWorkflowRelay {...props} data={workflowData} />;
64+
return workflowData ? (
65+
<BaseWorkflowRelay {...props} data={workflowData} />
66+
) : null;
4067
}

frontend/relay-workflows-lib/lib/components/MockedSingleWorkflowView.tsx

Lines changed: 0 additions & 28 deletions
This file was deleted.

frontend/relay-workflows-lib/lib/components/MockedWorkflowRelay.tsx

Lines changed: 0 additions & 17 deletions
This file was deleted.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import React from "react";
2+
import { Visit } from "workflows-lib";
3+
import { workflowRelayQuery } from "../graphql/workflowRelayQuery";
4+
import { workflowRelayQuery as WorkflowRelayQueryType } from "../graphql/__generated__/workflowRelayQuery.graphql";
5+
import { useLazyLoadQuery } from "react-relay";
6+
import WorkflowRelay from "./WorkflowRelay";
7+
8+
export interface WorkflowRelayProps {
9+
visit: Visit;
10+
workflowName: string;
11+
workflowLink?: boolean;
12+
filledTaskName?: string | null;
13+
expanded?: boolean;
14+
onChange?: () => void;
15+
}
16+
17+
const QueryWorkflowRelay: React.FC<WorkflowRelayProps> = (props) => {
18+
const queryData = useLazyLoadQuery<WorkflowRelayQueryType>(
19+
workflowRelayQuery,
20+
{
21+
visit: props.visit,
22+
name: props.workflowName,
23+
},
24+
);
25+
26+
return <WorkflowRelay {...props} data={queryData} />;
27+
};
28+
29+
export default QueryWorkflowRelay;

frontend/relay-workflows-lib/lib/components/SingleWorkflowView.tsx

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,30 @@
11
import { Visit } from "workflows-lib";
22
import LiveSingleWorkflowView from "./LiveSingleWorkflowView";
3-
import MockedWorkflowView from "./MockedSingleWorkflowView";
3+
import { useLazyLoadQuery } from "react-relay";
4+
import { workflowRelayQuery } from "../graphql/workflowRelayQuery";
5+
import { workflowRelayQuery as WorkflowRelayQueryType } from "../graphql/__generated__/workflowRelayQuery.graphql";
6+
import { isFinished } from "../utils";
7+
import BaseSingleWorkflowView from "./BaseSingleWorkflowView";
48

5-
const isMocking = import.meta.env.VITE_ENABLE_MOCKING === "true";
69
export interface SingleWorkflowViewProps {
710
visit: Visit;
811
workflowName: string;
912
tasknames?: string[];
1013
}
1114

1215
export default function SingleWorkflowView(props: SingleWorkflowViewProps) {
13-
return isMocking ? (
14-
<MockedWorkflowView {...props} />
16+
const queryData = useLazyLoadQuery<WorkflowRelayQueryType>(
17+
workflowRelayQuery,
18+
{
19+
visit: props.visit,
20+
name: props.workflowName,
21+
},
22+
);
23+
24+
const finished = isFinished(queryData);
25+
26+
return finished ? (
27+
<BaseSingleWorkflowView data={queryData} tasknames={props.tasknames} />
1528
) : (
1629
<LiveSingleWorkflowView {...props} />
1730
);

0 commit comments

Comments
 (0)