fix: add websocket close handling (#17548)

resolves #17508 

Display an error in the UI that the websocket closed if the user is
still interacting with the dynamic parameters form

<img width="795" alt="Screenshot 2025-04-23 at 17 57 25"
src="https://github.com/user-attachments/assets/15362ddb-fe01-462e-8537-a48302c5c621"
/>
This commit is contained in:
Jaayden Halko
2025-04-23 19:19:25 -04:00
committed by GitHub
parent 3567d455a7
commit ef6e6e41ff
3 changed files with 57 additions and 2 deletions
+6
View File
@@ -1015,9 +1015,11 @@ class ApiMethods {
{
onMessage,
onError,
onClose,
}: {
onMessage: (response: TypesGen.DynamicParametersResponse) => void;
onError: (error: Error) => void;
onClose: () => void;
},
): WebSocket => {
const socket = createWebSocket(
@@ -1033,6 +1035,10 @@ class ApiMethods {
socket.close();
});
socket.addEventListener("close", () => {
onClose();
});
return socket;
};
@@ -1,4 +1,4 @@
import type { ApiErrorResponse } from "api/errors";
import { type ApiErrorResponse, DetailedError } from "api/errors";
import { checkAuthorization } from "api/queries/authCheck";
import {
templateByName,
@@ -107,6 +107,15 @@ const CreateWorkspacePageExperimental: FC = () => {
onError: (error) => {
setWsError(error);
},
onClose: () => {
// There is no reason for the websocket to close while a user is on the page
setWsError(
new DetailedError(
"Websocket connection for dynamic parameters unexpectedly closed.",
"Refresh the page to reset the form.",
),
);
},
},
);
@@ -141,7 +150,7 @@ const CreateWorkspacePageExperimental: FC = () => {
} = useExternalAuth(realizedVersionId);
const isLoadingFormData =
ws.current?.readyState !== WebSocket.OPEN ||
ws.current?.readyState === WebSocket.CONNECTING ||
templateQuery.isLoading ||
permissionsQuery.isLoading;
const loadFormDataError = templateQuery.error ?? permissionsQuery.error;
@@ -0,0 +1,40 @@
import type { Meta, StoryObj } from "@storybook/react";
import { DetailedError } from "api/errors";
import { chromatic } from "testHelpers/chromatic";
import { MockTemplate, MockUser } from "testHelpers/entities";
import { CreateWorkspacePageViewExperimental } from "./CreateWorkspacePageViewExperimental";
const meta: Meta<typeof CreateWorkspacePageViewExperimental> = {
title: "Pages/CreateWorkspacePageViewExperimental",
parameters: { chromatic },
component: CreateWorkspacePageViewExperimental,
args: {
autofillParameters: [],
diagnostics: [],
defaultName: "",
defaultOwner: MockUser,
externalAuth: [],
externalAuthPollingState: "idle",
hasAllRequiredExternalAuth: true,
mode: "form",
parameters: [],
permissions: {
createWorkspaceForAny: true,
},
presets: [],
sendMessage: () => {},
template: MockTemplate,
},
};
export default meta;
type Story = StoryObj<typeof CreateWorkspacePageViewExperimental>;
export const WebsocketError: Story = {
args: {
error: new DetailedError(
"Websocket connection for dynamic parameters unexpectedly closed.",
"Refresh the page to reset the form.",
),
},
};