refactor: poll for git auth updates when creating a workspace (#9804)

This commit is contained in:
Kayla Washburn
2023-09-26 11:39:07 -06:00
committed by GitHub
parent 4c3b579f58
commit 6f0e2a7968
9 changed files with 212 additions and 117 deletions
+13
View File
@@ -120,3 +120,16 @@ export const updateActiveTemplateVersion = (
},
};
};
export const templateVersionGitAuthKey = (versionId: string) => [
"templateVersion",
versionId,
"gitAuth",
];
export const templateVersionGitAuth = (versionId: string) => {
return {
queryKey: templateVersionGitAuthKey(versionId),
queryFn: () => API.getTemplateVersionGitAuth(versionId),
};
};
@@ -7,11 +7,13 @@ import {
MockWorkspace,
MockWorkspaceQuota,
MockWorkspaceRequest,
MockWorkspaceRichParametersRequest,
MockTemplateVersionParameter1,
MockTemplateVersionParameter2,
MockTemplateVersionParameter3,
MockTemplateVersionGitAuth,
MockOrganization,
MockTemplateVersionGitAuthAuthenticated,
} from "testHelpers/entities";
import {
renderWithAuth,
@@ -31,17 +33,6 @@ const renderCreateWorkspacePage = () => {
});
};
Object.defineProperty(window, "BroadcastChannel", {
value: class {
addEventListener() {
// noop
}
close() {
// noop
}
},
});
describe("CreateWorkspacePage", () => {
it("succeeds with default owner", async () => {
jest
@@ -71,9 +62,9 @@ describe("CreateWorkspacePage", () => {
expect(API.createWorkspace).toBeCalledWith(
MockUser.organization_ids[0],
MockUser.id,
{
...MockWorkspaceRequest,
},
expect.objectContaining({
...MockWorkspaceRichParametersRequest,
}),
),
);
});
@@ -165,6 +156,50 @@ describe("CreateWorkspacePage", () => {
expect(validationError).toBeInTheDocument();
});
it("gitauth authenticates and succeeds", async () => {
jest
.spyOn(API, "getWorkspaceQuota")
.mockResolvedValueOnce(MockWorkspaceQuota);
jest
.spyOn(API, "getUsers")
.mockResolvedValueOnce({ users: [MockUser], count: 1 });
jest.spyOn(API, "createWorkspace").mockResolvedValueOnce(MockWorkspace);
jest
.spyOn(API, "getTemplateVersionGitAuth")
.mockResolvedValue([MockTemplateVersionGitAuth]);
renderCreateWorkspacePage();
await waitForLoaderToBeRemoved();
const nameField = await screen.findByLabelText(nameLabelText);
// have to use fireEvent b/c userEvent isn't cleaning up properly between tests
fireEvent.change(nameField, {
target: { value: "test" },
});
const githubButton = await screen.findByText("Login with GitHub");
await userEvent.click(githubButton);
jest
.spyOn(API, "getTemplateVersionGitAuth")
.mockResolvedValue([MockTemplateVersionGitAuthAuthenticated]);
await screen.findByText("Authenticated with GitHub");
const submitButton = screen.getByText(createWorkspaceText);
await userEvent.click(submitButton);
await waitFor(() =>
expect(API.createWorkspace).toBeCalledWith(
MockUser.organization_ids[0],
MockUser.id,
expect.objectContaining({
...MockWorkspaceRequest,
}),
),
);
});
it("gitauth: errors if unauthenticated and submits", async () => {
jest
.spyOn(API, "getTemplateVersionGitAuth")
@@ -210,4 +245,29 @@ describe("CreateWorkspacePage", () => {
);
});
});
it("auto create a workspace if uses mode=auto and version=version-id", async () => {
const param = "first_parameter";
const paramValue = "It works!";
const createWorkspaceSpy = jest.spyOn(API, "createWorkspace");
renderWithAuth(<CreateWorkspacePage />, {
route:
"/templates/" +
MockTemplate.name +
`/workspace?param.${param}=${paramValue}&mode=auto&version=test-template-version`,
path: "/templates/:template/workspace",
});
await waitFor(() => {
expect(createWorkspaceSpy).toBeCalledWith(
MockOrganization.id,
"me",
expect.objectContaining({
template_version_id: MockTemplate.active_version_id,
rich_parameter_values: [{ name: param, value: paramValue }],
}),
);
});
});
});
@@ -1,13 +1,11 @@
import { useMachine } from "@xstate/react";
import {
Template,
TemplateVersionGitAuth,
TemplateVersionParameter,
WorkspaceBuildParameter,
} from "api/typesGenerated";
import { useMe } from "hooks/useMe";
import { useOrganizationId } from "hooks/useOrganizationId";
import { FC } from "react";
import { type FC, useCallback, useState, useEffect } from "react";
import { Helmet } from "react-helmet-async";
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
import { pageTitle } from "utils/page";
@@ -25,6 +23,10 @@ import {
colors,
NumberDictionary,
} from "unique-names-generator";
import { useQuery } from "@tanstack/react-query";
import { templateVersionGitAuth } from "api/queries/templates";
export type GitAuthPollingState = "idle" | "polling" | "abandoned";
const CreateWorkspacePage: FC = () => {
const organizationId = useOrganizationId();
@@ -50,18 +52,49 @@ const CreateWorkspacePage: FC = () => {
},
},
});
const {
template,
error,
parameters,
permissions,
gitAuth,
defaultName,
versionId,
} = createWorkspaceState.context;
const { template, parameters, permissions, defaultName, versionId } =
createWorkspaceState.context;
const title = createWorkspaceState.matches("autoCreating")
? "Creating workspace..."
: "Create Workspace";
: "Create workspace";
const [gitAuthPollingState, setGitAuthPollingState] =
useState<GitAuthPollingState>("idle");
const startPollingGitAuth = useCallback(() => {
setGitAuthPollingState("polling");
}, []);
const { data: gitAuth, error } = useQuery(
versionId
? {
...templateVersionGitAuth(versionId),
refetchInterval: gitAuthPollingState === "polling" ? 1000 : false,
}
: { enabled: false },
);
const allSignedIn = gitAuth?.every((it) => it.authenticated);
useEffect(() => {
if (allSignedIn) {
setGitAuthPollingState("idle");
return;
}
if (gitAuthPollingState !== "polling") {
return;
}
// Poll for a maximum of one minute
const quitPolling = setTimeout(
() => setGitAuthPollingState("abandoned"),
60_000,
);
return () => {
clearTimeout(quitPolling);
};
}, [gitAuthPollingState, allSignedIn]);
return (
<>
@@ -81,11 +114,13 @@ const CreateWorkspacePage: FC = () => {
defaultOwner={me}
defaultBuildParameters={defaultBuildParameters}
error={error}
template={template as Template}
template={template!}
versionId={versionId}
gitAuth={gitAuth as TemplateVersionGitAuth[]}
gitAuth={gitAuth ?? []}
gitAuthPollingState={gitAuthPollingState}
startPollingGitAuth={startPollingGitAuth}
permissions={permissions as CreateWSPermissions}
parameters={parameters as TemplateVersionParameter[]}
parameters={parameters!}
creatingWorkspace={createWorkspaceState.matches("creatingWorkspace")}
onCancel={() => {
navigate(-1);
@@ -30,6 +30,7 @@ import { CreateWSPermissions } from "xServices/createWorkspace/createWorkspaceXS
import { GitAuth } from "./GitAuth";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Stack } from "components/Stack/Stack";
import { type GitAuthPollingState } from "./CreateWorkspacePage";
export interface CreateWorkspacePageViewProps {
error: unknown;
@@ -38,6 +39,8 @@ export interface CreateWorkspacePageViewProps {
template: TypesGen.Template;
versionId?: string;
gitAuth: TypesGen.TemplateVersionGitAuth[];
gitAuthPollingState: GitAuthPollingState;
startPollingGitAuth: () => void;
parameters: TypesGen.TemplateVersionParameter[];
defaultBuildParameters: TypesGen.WorkspaceBuildParameter[];
permissions: CreateWSPermissions;
@@ -56,6 +59,8 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
template,
versionId,
gitAuth,
gitAuthPollingState,
startPollingGitAuth,
parameters,
defaultBuildParameters,
permissions,
@@ -113,7 +118,7 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
>
<FormFields>
<SelectedTemplate template={template} />
{versionId && (
{versionId && versionId !== template.active_version_id && (
<Stack spacing={1} className={styles.hasDescription}>
<TextField
disabled
@@ -161,11 +166,13 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
description="This template requires authentication to automatically perform Git operations on create."
>
<FormFields>
{gitAuth.map((auth, index) => (
{gitAuth.map((auth) => (
<GitAuth
key={index}
key={auth.id}
authenticateURL={auth.authenticate_url}
authenticated={auth.authenticated}
gitAuthPollingState={gitAuthPollingState}
startPollingGitAuth={startPollingGitAuth}
type={auth.type}
error={gitAuthErrors[auth.id]}
/>
@@ -229,12 +236,8 @@ type GitAuthErrors = Record<string, string>;
const useGitAuthVerification = (gitAuth: TypesGen.TemplateVersionGitAuth[]) => {
const [gitAuthErrors, setGitAuthErrors] = useState<GitAuthErrors>({});
// Clear errors when gitAuth is refreshed
useEffect(() => {
// templateGitAuth is refreshed automatically using a BroadcastChannel
// which may change the `authenticated` property.
//
// If the provider becomes authenticated, we want the error message
// to disappear.
setGitAuthErrors({});
}, [gitAuth]);
+30 -15
View File
@@ -9,20 +9,30 @@ import { BitbucketIcon } from "components/Icons/BitbucketIcon";
import { GitlabIcon } from "components/Icons/GitlabIcon";
import { FC } from "react";
import { makeStyles } from "@mui/styles";
import { type GitAuthPollingState } from "./CreateWorkspacePage";
import { Stack } from "components/Stack/Stack";
import ReplayIcon from "@mui/icons-material/Replay";
import { LoadingButton } from "components/LoadingButton/LoadingButton";
export interface GitAuthProps {
type: TypesGen.GitProvider;
authenticated: boolean;
authenticateURL: string;
gitAuthPollingState: GitAuthPollingState;
startPollingGitAuth: () => void;
error?: string;
}
export const GitAuth: FC<GitAuthProps> = ({
type,
authenticated,
authenticateURL,
error,
}) => {
export const GitAuth: FC<GitAuthProps> = (props) => {
const {
type,
authenticated,
authenticateURL,
gitAuthPollingState,
startPollingGitAuth,
error,
} = props;
const styles = useStyles({
error: typeof error !== "undefined",
});
@@ -52,12 +62,11 @@ export const GitAuth: FC<GitAuthProps> = ({
return (
<Tooltip
title={
authenticated ? "You're already authenticated! No action needed." : ``
}
title={authenticated && `${prettyName} has already been connected.`}
>
<div>
<Button
<Stack alignItems="center" spacing={1}>
<LoadingButton
loading={gitAuthPollingState === "polling"}
href={authenticateURL}
variant="contained"
size="large"
@@ -73,15 +82,21 @@ export const GitAuth: FC<GitAuthProps> = ({
return;
}
window.open(authenticateURL, "_blank", "width=900,height=600");
startPollingGitAuth();
}}
>
{authenticated
? `You're authenticated with ${prettyName}!`
: `Click to login with ${prettyName}!`}
</Button>
? `Authenticated with ${prettyName}`
: `Login with ${prettyName}`}
</LoadingButton>
{gitAuthPollingState === "abandoned" && (
<Button variant="text" onClick={startPollingGitAuth}>
<ReplayIcon /> Check again
</Button>
)}
{error && <FormHelperText error>{error}</FormHelperText>}
</div>
</Stack>
</Tooltip>
);
};
+1 -13
View File
@@ -5,12 +5,11 @@ import {
getGitAuthProvider,
} from "api/api";
import { usePermissions } from "hooks";
import { FC, useEffect } from "react";
import { type FC } from "react";
import { useParams } from "react-router-dom";
import GitAuthPageView from "./GitAuthPageView";
import { ApiErrorResponse } from "api/errors";
import { isAxiosError } from "axios";
import { REFRESH_GITAUTH_BROADCAST_CHANNEL } from "utils/gitAuth";
const GitAuthPage: FC = () => {
const { provider } = useParams();
@@ -52,17 +51,6 @@ const GitAuthPage: FC = () => {
query.state.status === "success" ? false : "always",
});
useEffect(() => {
if (!getGitAuthProviderQuery.data?.authenticated) {
return;
}
// This is used to notify the parent window that the Git auth token has been refreshed.
// It's critical in the create workspace flow!
const bc = new BroadcastChannel(REFRESH_GITAUTH_BROADCAST_CHANNEL);
// The message doesn't matter, any message refreshes the page!
bc.postMessage("noop");
}, [getGitAuthProviderQuery.data?.authenticated]);
if (getGitAuthProviderQuery.isLoading || !getGitAuthProviderQuery.data) {
return null;
}
+2 -15
View File
@@ -11,8 +11,7 @@ import { Avatar } from "components/Avatar/Avatar";
import { CopyButton } from "components/CopyButton/CopyButton";
import { SignInLayout } from "components/SignInLayout/SignInLayout";
import { Welcome } from "components/Welcome/Welcome";
import { FC, useEffect } from "react";
import { REFRESH_GITAUTH_BROADCAST_CHANNEL } from "utils/gitAuth";
import { type FC } from "react";
export interface GitAuthPageViewProps {
gitAuth: GitAuth;
@@ -33,18 +32,6 @@ const GitAuthPageView: FC<GitAuthPageViewProps> = ({
}) => {
const styles = useStyles();
useEffect(() => {
if (!gitAuth.authenticated) {
return;
}
// This is used to notify the parent window that the Git auth token has been refreshed.
// It's critical in the create workspace flow!
// eslint-disable-next-line compat/compat -- It actually is supported... not sure why it's complaining.
const bc = new BroadcastChannel(REFRESH_GITAUTH_BROADCAST_CHANNEL);
// The message doesn't matter, any message refreshes the page!
bc.postMessage("noop");
}, [gitAuth.authenticated]);
if (!gitAuth.authenticated) {
return (
<SignInLayout>
@@ -76,7 +63,7 @@ const GitAuthPageView: FC<GitAuthPageViewProps> = ({
<SignInLayout>
<Welcome message={`You've authenticated with ${gitAuth.type}!`} />
<p className={styles.text}>
Hey @{gitAuth.user?.login} 👋!{" "}
Hey @{gitAuth.user?.login}! 👋{" "}
{(!gitAuth.app_installable || gitAuth.installations.length > 0) &&
"You are now authenticated with Git. Feel free to close this window!"}
</p>
+22 -8
View File
@@ -1190,18 +1190,24 @@ export const MockTemplateVersionVariable5: TypesGen.TemplateVersionVariable = {
sensitive: false,
};
// requests the MockWorkspace
export const MockWorkspaceRequest: TypesGen.CreateWorkspaceRequest = {
name: "test",
template_id: "test-template",
rich_parameter_values: [
{
name: MockTemplateVersionParameter1.name,
value: MockTemplateVersionParameter1.default_value,
},
],
template_version_id: "test-template-version",
rich_parameter_values: [],
};
export const MockWorkspaceRichParametersRequest: TypesGen.CreateWorkspaceRequest =
{
name: "test",
template_version_id: "test-template-version",
rich_parameter_values: [
{
name: MockTemplateVersionParameter1.name,
value: MockTemplateVersionParameter1.default_value,
},
],
};
export const MockUserAgent = {
browser: "Chrome 99.0.4844",
device: "Other",
@@ -2184,6 +2190,14 @@ export const MockTemplateVersionGitAuth: TypesGen.TemplateVersionGitAuth = {
authenticated: false,
};
export const MockTemplateVersionGitAuthAuthenticated: TypesGen.TemplateVersionGitAuth =
{
id: "github",
type: "github",
authenticate_url: "https://example.com/gitauth/github",
authenticated: true,
};
export const MockDeploymentStats: TypesGen.DeploymentStats = {
aggregated_from: "2023-03-06T19:08:55.211625Z",
collected_at: "2023-03-06T19:12:55.211625Z",
@@ -2,13 +2,11 @@ import {
checkAuthorization,
createWorkspace,
getTemplateByName,
getTemplateVersionGitAuth,
getTemplateVersionRichParameters,
} from "api/api";
import {
CreateWorkspaceRequest,
Template,
TemplateVersionGitAuth,
TemplateVersionParameter,
User,
Workspace,
@@ -16,7 +14,6 @@ import {
} from "api/typesGenerated";
import { assign, createMachine } from "xstate";
import { paramsUsedToCreateWorkspace } from "utils/workspace";
import { REFRESH_GITAUTH_BROADCAST_CHANNEL } from "utils/gitAuth";
export type CreateWorkspaceMode = "form" | "auto";
@@ -33,7 +30,6 @@ type CreateWorkspaceContext = {
template?: Template;
parameters?: TemplateVersionParameter[];
permissions?: Record<string, boolean>;
gitAuth?: TemplateVersionGitAuth[];
// Used on auto-create
defaultBuildParameters?: WorkspaceBuildParameter[];
};
@@ -49,7 +45,7 @@ type RefreshGitAuthEvent = {
};
export const createWorkspaceMachine =
/** @xstate-layout N4IgpgJg5mDOIC5QGMBOYCGAXMB1A9qgNawAOGyYAyltmAHQxZYCWAdlACpgC2pANnVgBiCPjYN2AN3xEGaTDgLEyFarRyMwzdl14ChCafmTYW4gNoAGALrWbiUKXywWrcY5AAPRABYATAA0IACeiACMvgCs9ADssQBsVgDM-v5JyQnJUQkAvrnBCnTKJOSUNHRaOhzcfII4ImIS9MZy9EVKhKVqFZpMrDX69XBGbDKm7mz2FuEOSCDOrpOePgjJAJzr9AAcVr4J677bCeFWB3vBYQgBvvQJB-5R6+GxVulPUfmF6MVdquUaBj9XS1AwNYRgVCoQj0MEAM0IPHaP06KjK6kqwMGdUMxgm5imtnsnkWbgJK0QGy2u32h2Op3OvkuiH8vis9HCDxO0XC2z5CX8XxAHTwf3RvSB2gGehxOCoyAAFrwMKJxJIxrJ5CjRWieoCqtLQcN5UqeBhRuMzJYibYSS4yR55qtTv52bFeYlfPEor4XuFmddwlscqz1mdfMlfZlfEKRSV-hi+lKQUM6CblRCoTD4YjkYodd0AZjk9iwdRFcqLSYrYS7Lb5qTlk6Im83R6El7Yj6-QH4gl6M8ctsoht7skrJ8CsLtfHxfqsTKywAFDCoDA8bSQxpqloatpxsV64vVRfDFdrjc4VCwKv4611uZOe1N0DOgXhOKRjZf-axR4Btl2XWWJngSKJtgCHJwnSWMZ0PIskxPI06HPddN2vTNoVQWF6gRVAkQPXUEMlJDUxwVDLy3W8a2mesnyWclmwQTl-A-WIv3WH8Ej-KIAyyW4Em2Z5XV5fx1m48JYPzWcj0Qw0yLAABxNwAEEAFcsAVVVmlaLVpPgxMSPk2UlNUjSFWoyZaMfBZn0Y18WSDfsfUEvlfEHViA2SbZ-HoDZwPSe4omCwUp0IwtDINFMTOUrB1M0zDs1w3NwoTCUotLYZYviiy8Rom0bMbezvEc8T6BcvkII8-1Qj8dZfP2R4fXEziUliKTfiIyKK2QIhdCXSEeBYWBXHEbcdL3eQlV6gb8OG0a2FgYkGzsx0HIQfwfJiKJXmSZJoO88IRwAqwP22aD3OeLshP2DrUQi9Ker6jhZqGkaCRESEsJw7A8II6aiFe+aPuW+iHTYCkNqiKxtgHVi+XWYK2SZWqNr5HZslAiNoJSSdvn0rr0rhFh+H4frV3XEQAGEACUAFEVM4OmAH1cAAeRpgBpKglxUqm6dB2yGLWkq0cecrdv2-xDuO1GXluRH9s5H1wKObi7oLNL9WJ0nyYvEQqDpgAZOmqc4Zm2dwAA5OmacFoqRdWcd0asSWkZuoJUbSftpc2vZ6rZDsXg1mTiLzMwOFDsBtPVGR9zgwn9Q6XQo8sglrLtYWIaYzbxZ2lIpZl5IA3O+hLvWeleSiVj6pDgzHpRFODMS7Cc3w8P7q1ypk8jgy0-ve3Vuz9bc+2yWDvO2WrjE2HMcybZYiOHJYm2fIpzYfAIDgTxUrnOhM-ByGAFoEgDE-6CsS+3n8d0nl9aW8enAmHvnEtTyEA+X1F4cOWryM0k5JyTiAZWS3DSKBdIC9zjZDronY8xkyzpjNJ-YqqxXjORXuEfaEYAh9gAtLO4aQNgenqkdSMsCX7wOisuCmlFrwoMdhETazl9hCQ2NLKwFd1gnRiCkMM51og8k2BQruclqFZTMppBhw9RZBldvQTa0FzgdnuF5Y4ZdgrumyI8JyC8RF700E9fqg1gZjWkZDUBWwDgjjElgnawE1GxAUUJPYglwIjjeDGMKCdKGaB1mTF6tD4ArSzpDfabwdiXxApseqtjT6o1SE4txrVXTpHSF4-GnVfF6QjlAKO5ic5RCOn5LsbwjpHWeJyAMZVThHUgvcCSvp9GyRyTgCABT1rhN8rsV2MTYmgQDC6BR7xuznByCcZpYcvqEA6aLSxdxFa2OyCBWIAYimwxXvwoMrp3GrzXkAA */
/** @xstate-layout N4IgpgJg5mDOIC5QGMBOYCGAXMB1A9qgNawAOGyYAyltmAHTIAWYyRAlgHZQCy+EYAMQBtAAwBdRKFL5Y7LO3ycpIAB6IATABYAnPR2iDWgOwBmAGynRo4wFZbAGhABPRDoAc9d+8OnTARlF-Uy0NUQ0AXwinNEwcAmIyCmpaHEYWNi5efiFhf0kkEBk5BSUVdQQNU1svEPcq9wsDU3ctJ1dKv3pRd3NzHVNjf1sjHX8omPQ6BJJySho6egwAVyx8AGEphW5BCCUGLgA3fCIGWOnCWeSFtJW1zbishCP8ZGxFTjFxL5Vi+Q-yoh7MZ9MZjLoQqItN5jDp2ogALT+PSWWwaDR9dzGDTeXTuCYgc7xS5JeapBh3DZbLKCMCoVCEeikAA22AAZoQALaMLZ4ElzFKLSkPd7cZ6cY5vUqfCQ-Qp-aWAhAtPQtWxDaE+OxQ4zwhD+dw1US2cw4-zmLQ9WyicwEol8xICm4MZn4DAQLIAMS5ABFsBhdvt6C9Tjy4g6rmTFq73V7ff7xZL3kovnLpLJ-mVChVzNb6P5tDoMei7P5-G0XIgQqZ6BjbFrWuZGrC7byZqTBWkYx7uN7UJy-bRafTGSz2VywxdHddyfRu3H+4OMInXsmZd8JL8M4rs4hejWCzYLOYDSfjOY9SENPpgmYy+bgjodLbooS2-yZ4t2BBmUJ1gAlABRABBAAVQCAH1cAAeX-ABpKgAAVgPWQC0yKbcAV3BBLHMWs61EQZjQ0ZF3D1ewa36S0QlhasQlbcN2ydWciSyJjkkDTgDglE4znfacozSVjuHYygVylD5U03eVMKzUAc1MPRGh0cEtBMJ9Wj1MxPFsKwmg0DwwmGBip0jTs+MeESP0oYcGVQJlWSwDl+0nYkBPM1y2OssBxLXKSCnTEosPkxBQn8eh7FaewBjRU1THIwIb2CLQ+nrXN1SiV9OByeBCntUTzK3IK5LUKstFrWE4uNGxwQxPUkRsfNqnRfxzybE1+hMtyzOddJWA4bg+AEIrM2UbD6gq58qmqsFQgvSsEGfehLEMExWp0LRSK6iMO164VqW4EadxC5Ui2W0wNDBDVekfLTrx8cIAnBKxgVsbaCt6+de3jWgjuC0qcIsZbfBtPE-F1BaGn0AzIWxGK-HxV98u83rv1-P6SpzSx6EUtT+kIsYT38PUtFscKDTUw1zGxMmy3elGWIOqACoxsaTtNPCLs2-oGlNVq9SJ2tQcCS0eZS+n3N6+0IFZpV3A2-MtBadx1uRcIyIWuxPDsforEM6x7AlnrZ27QCR1QWXxthHHLrBJ8nxtJsSd0ehsQsJWNHrYYi0yiIgA */
createMachine(
{
id: "createWorkspaceState",
@@ -64,7 +60,6 @@ export const createWorkspaceMachine =
template: Template;
permissions: CreateWSPermissions;
parameters: TemplateVersionParameter[];
gitAuth: TemplateVersionGitAuth[];
};
};
createWorkspace: {
@@ -112,16 +107,6 @@ export const createWorkspaceMachine =
},
},
idle: {
invoke: [
{
src: () => (callback) => {
const channel = watchGitAuthRefresh(() => {
callback("REFRESH_GITAUTH");
});
return () => channel.close();
},
},
],
on: {
CREATE_WORKSPACE: {
target: "creatingWorkspace",
@@ -189,23 +174,24 @@ export const createWorkspaceMachine =
rich_parameter_values: defaultBuildParameters,
});
},
loadFormData: async ({ templateName, organizationId }) => {
loadFormData: async ({ templateName, organizationId, versionId }) => {
const [template, permissions] = await Promise.all([
getTemplateByName(organizationId, templateName),
checkCreateWSPermissions(organizationId),
]);
const [parameters, gitAuth] = await Promise.all([
getTemplateVersionRichParameters(template.active_version_id).then(
(p) => p.filter(paramsUsedToCreateWorkspace),
const realizedVersionId = versionId ?? template.active_version_id;
const [parameters] = await Promise.all([
getTemplateVersionRichParameters(realizedVersionId).then((p) =>
p.filter(paramsUsedToCreateWorkspace),
),
getTemplateVersionGitAuth(template.active_version_id),
]);
return {
template,
permissions,
parameters,
gitAuth,
versionId: realizedVersionId,
};
},
},
@@ -246,12 +232,6 @@ const checkCreateWSPermissions = async (organizationId: string) => {
}) as Promise<Record<keyof typeof permissionsToCheck, boolean>>;
};
export const watchGitAuthRefresh = (callback: () => void) => {
const bc = new BroadcastChannel(REFRESH_GITAUTH_BROADCAST_CHANNEL);
bc.addEventListener("message", callback);
return bc;
};
export type CreateWSPermissions = Awaited<
ReturnType<typeof checkCreateWSPermissions>
>;