mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
feat: setup connection to dynamic parameters websocket (#17393)
resolves coder/preview#57
This commit is contained in:
@@ -1009,6 +1009,32 @@ class ApiMethods {
|
||||
return response.data;
|
||||
};
|
||||
|
||||
templateVersionDynamicParameters = (
|
||||
versionId: string,
|
||||
{
|
||||
onMessage,
|
||||
onError,
|
||||
}: {
|
||||
onMessage: (response: TypesGen.DynamicParametersResponse) => void;
|
||||
onError: (error: Error) => void;
|
||||
},
|
||||
): WebSocket => {
|
||||
const socket = createWebSocket(
|
||||
`/api/v2/templateversions/${versionId}/dynamic-parameters`,
|
||||
);
|
||||
|
||||
socket.addEventListener("message", (event) =>
|
||||
onMessage(JSON.parse(event.data) as TypesGen.DynamicParametersResponse),
|
||||
);
|
||||
|
||||
socket.addEventListener("error", () => {
|
||||
onError(new Error("Connection for dynamic parameters failed."));
|
||||
socket.close();
|
||||
});
|
||||
|
||||
return socket;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param organization Can be the organization's ID or name
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type {
|
||||
NullHCLString,
|
||||
PreviewParameter,
|
||||
PreviewParameterOption,
|
||||
WorkspaceBuildParameter,
|
||||
@@ -156,10 +157,8 @@ const ParameterField: FC<ParameterFieldProps> = ({
|
||||
disabled,
|
||||
id,
|
||||
}) => {
|
||||
const value = parameter.value.valid ? parameter.value.value : "";
|
||||
const defaultValue = parameter.default_value.valid
|
||||
? parameter.default_value.value
|
||||
: "";
|
||||
const value = validValue(parameter.value);
|
||||
const defaultValue = validValue(parameter.default_value);
|
||||
|
||||
switch (parameter.form_type) {
|
||||
case "dropdown":
|
||||
@@ -376,9 +375,7 @@ export const getInitialParameterValues = (
|
||||
if (parameter.ephemeral) {
|
||||
return {
|
||||
name: parameter.name,
|
||||
value: parameter.default_value.valid
|
||||
? parameter.default_value.value
|
||||
: "",
|
||||
value: validValue(parameter.default_value),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -390,15 +387,19 @@ export const getInitialParameterValues = (
|
||||
name: parameter.name,
|
||||
value:
|
||||
autofillParam &&
|
||||
isValidValue(parameter, autofillParam) &&
|
||||
isValidParameterOption(parameter, autofillParam) &&
|
||||
autofillParam.value
|
||||
? autofillParam.value
|
||||
: "",
|
||||
: validValue(parameter.default_value),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const isValidValue = (
|
||||
const validValue = (value: NullHCLString) => {
|
||||
return value.valid ? value.value : "";
|
||||
};
|
||||
|
||||
const isValidParameterOption = (
|
||||
previewParam: PreviewParameter,
|
||||
buildParam: WorkspaceBuildParameter,
|
||||
) => {
|
||||
@@ -409,7 +410,7 @@ const isValidValue = (
|
||||
return validValues.includes(buildParam.value);
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
export const useValidationSchemaForDynamicParameters = (
|
||||
|
||||
@@ -9,7 +9,6 @@ import { autoCreateWorkspace, createWorkspace } from "api/queries/workspaces";
|
||||
import type {
|
||||
DynamicParametersRequest,
|
||||
DynamicParametersResponse,
|
||||
Template,
|
||||
Workspace,
|
||||
} from "api/typesGenerated";
|
||||
import { Loader } from "components/Loader/Loader";
|
||||
@@ -32,6 +31,7 @@ import type { AutofillBuildParameter } from "utils/richParameters";
|
||||
import { CreateWorkspacePageViewExperimental } from "./CreateWorkspacePageViewExperimental";
|
||||
export const createWorkspaceModes = ["form", "auto", "duplicate"] as const;
|
||||
export type CreateWorkspaceMode = (typeof createWorkspaceModes)[number];
|
||||
import { API } from "api/api";
|
||||
import {
|
||||
type CreateWorkspacePermissions,
|
||||
createWorkspaceChecks,
|
||||
@@ -47,8 +47,9 @@ const CreateWorkspacePageExperimental: FC = () => {
|
||||
|
||||
const [currentResponse, setCurrentResponse] =
|
||||
useState<DynamicParametersResponse | null>(null);
|
||||
const [wsResponseId, setWSResponseId] = useState<number>(0);
|
||||
const sendMessage = (message: DynamicParametersRequest) => {};
|
||||
const [wsResponseId, setWSResponseId] = useState<number>(-1);
|
||||
const ws = useRef<WebSocket | null>(null);
|
||||
const [wsError, setWsError] = useState<Error | null>(null);
|
||||
|
||||
const customVersionId = searchParams.get("version") ?? undefined;
|
||||
const defaultName = searchParams.get("name");
|
||||
@@ -80,6 +81,49 @@ const CreateWorkspacePageExperimental: FC = () => {
|
||||
const realizedVersionId =
|
||||
customVersionId ?? templateQuery.data?.active_version_id;
|
||||
|
||||
const onMessage = useCallback((response: DynamicParametersResponse) => {
|
||||
setCurrentResponse((prev) => {
|
||||
if (prev?.id === response.id) {
|
||||
return prev;
|
||||
}
|
||||
return response;
|
||||
});
|
||||
}, []);
|
||||
|
||||
// Initialize the WebSocket connection when there is a valid template version ID
|
||||
useEffect(() => {
|
||||
if (!realizedVersionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const socket = API.templateVersionDynamicParameters(realizedVersionId, {
|
||||
onMessage,
|
||||
onError: (error) => {
|
||||
setWsError(error);
|
||||
},
|
||||
});
|
||||
|
||||
ws.current = socket;
|
||||
|
||||
return () => {
|
||||
socket.close();
|
||||
};
|
||||
}, [realizedVersionId, onMessage]);
|
||||
|
||||
const sendMessage = useCallback((formValues: Record<string, string>) => {
|
||||
setWSResponseId((prevId) => {
|
||||
const request: DynamicParametersRequest = {
|
||||
id: prevId + 1,
|
||||
inputs: formValues,
|
||||
};
|
||||
if (ws.current && ws.current.readyState === WebSocket.OPEN) {
|
||||
ws.current.send(JSON.stringify(request));
|
||||
return prevId + 1;
|
||||
}
|
||||
return prevId;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const organizationId = templateQuery.data?.organization_id;
|
||||
|
||||
const {
|
||||
@@ -90,7 +134,9 @@ const CreateWorkspacePageExperimental: FC = () => {
|
||||
} = useExternalAuth(realizedVersionId);
|
||||
|
||||
const isLoadingFormData =
|
||||
templateQuery.isLoading || permissionsQuery.isLoading;
|
||||
ws.current?.readyState !== WebSocket.OPEN ||
|
||||
templateQuery.isLoading ||
|
||||
permissionsQuery.isLoading;
|
||||
const loadFormDataError = templateQuery.error ?? permissionsQuery.error;
|
||||
|
||||
const title = autoCreateWorkspaceMutation.isLoading
|
||||
@@ -189,11 +235,12 @@ const CreateWorkspacePageExperimental: FC = () => {
|
||||
<CreateWorkspacePageViewExperimental
|
||||
mode={mode}
|
||||
defaultName={defaultName}
|
||||
diagnostics={currentResponse.diagnostics}
|
||||
diagnostics={currentResponse?.diagnostics ?? []}
|
||||
disabledParams={disabledParams}
|
||||
defaultOwner={me}
|
||||
autofillParameters={autofillParameters}
|
||||
error={
|
||||
wsError ||
|
||||
createWorkspaceMutation.error ||
|
||||
autoCreateError ||
|
||||
loadFormDataError ||
|
||||
@@ -210,7 +257,6 @@ const CreateWorkspacePageExperimental: FC = () => {
|
||||
parameters={sortedParams}
|
||||
presets={templateVersionPresetsQuery.data ?? []}
|
||||
creatingWorkspace={createWorkspaceMutation.isLoading}
|
||||
setWSResponseId={setWSResponseId}
|
||||
sendMessage={sendMessage}
|
||||
onCancel={() => {
|
||||
navigate(-1);
|
||||
|
||||
@@ -67,8 +67,7 @@ export interface CreateWorkspacePageViewExperimentalProps {
|
||||
owner: TypesGen.User,
|
||||
) => void;
|
||||
resetMutation: () => void;
|
||||
sendMessage: (message: DynamicParametersRequest) => void;
|
||||
setWSResponseId: (value: React.SetStateAction<number>) => void;
|
||||
sendMessage: (message: Record<string, string>) => void;
|
||||
startPollingExternalAuth: () => void;
|
||||
}
|
||||
|
||||
@@ -95,7 +94,6 @@ export const CreateWorkspacePageViewExperimental: FC<
|
||||
onCancel,
|
||||
resetMutation,
|
||||
sendMessage,
|
||||
setWSResponseId,
|
||||
startPollingExternalAuth,
|
||||
}) => {
|
||||
const [owner, setOwner] = useState(defaultOwner);
|
||||
@@ -222,15 +220,7 @@ export const CreateWorkspacePageViewExperimental: FC<
|
||||
// Update the input for the changed parameter
|
||||
formInputs[parameter.name] = value;
|
||||
|
||||
setWSResponseId((prevId) => {
|
||||
const newId = prevId + 1;
|
||||
const request: DynamicParametersRequest = {
|
||||
id: newId,
|
||||
inputs: formInputs,
|
||||
};
|
||||
sendMessage(request);
|
||||
return newId;
|
||||
});
|
||||
sendMessage(formInputs);
|
||||
};
|
||||
|
||||
const { debounced: handleChangeDebounced } = useDebouncedFunction(
|
||||
@@ -240,7 +230,7 @@ export const CreateWorkspacePageViewExperimental: FC<
|
||||
value: string,
|
||||
) => {
|
||||
await form.setFieldValue(parameterField, {
|
||||
name: parameter.form_type,
|
||||
name: parameter.name,
|
||||
value,
|
||||
});
|
||||
sendDynamicParamsRequest(parameter, value);
|
||||
@@ -257,7 +247,7 @@ export const CreateWorkspacePageViewExperimental: FC<
|
||||
handleChangeDebounced(parameter, parameterField, value);
|
||||
} else {
|
||||
await form.setFieldValue(parameterField, {
|
||||
name: parameter.form_type,
|
||||
name: parameter.name,
|
||||
value,
|
||||
});
|
||||
sendDynamicParamsRequest(parameter, value);
|
||||
|
||||
Reference in New Issue
Block a user