From e199cbb8b32994fb75a9b7a8a51cc0f9a6e35de4 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 20 Sep 2023 21:13:14 +0000 Subject: [PATCH 01/11] chore: xstate layout --- site/src/xServices/createWorkspace/createWorkspaceXService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/xServices/createWorkspace/createWorkspaceXService.ts b/site/src/xServices/createWorkspace/createWorkspaceXService.ts index 24d65608ad730..f78819285c2d2 100644 --- a/site/src/xServices/createWorkspace/createWorkspaceXService.ts +++ b/site/src/xServices/createWorkspace/createWorkspaceXService.ts @@ -49,7 +49,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+EYAMQBtAAwBdRKFL5Y7LO3ycpIAB6IATAGYN9UQBYArDsP6A7Pq1WNAGhABPRAE4AHPUOHRG7xoCMxswA2Mw0AX1C7NEwcAmIyCmpaHEYWNi5efiFhX0kkEBk5BSUVdQQ-M3onM19fUV8nfRdvJ0M7RzKdejMPVw0mpyctJ0DwyPQ6WJJySho6egwAVyx8AGFxhW5BCCUGLgA3fCIGKInCKYTZ5MXltej0hH38ZGxFTjFxd5UC+VeSxA8KlULIN9AYXC4zE42ogALT1eiBLSGbyBQIQvoufSuUYgE4xM7xGZJBjXVbrdKCMCoVCEeikAA22AAZoQALaMdZ4AnTRJzUm3F7cB6cA7PIpvCSfPLfcV-BBaFxOegKwzVRouUTdAxmaEIXwuQx6QyBPq+QKgg2iEYRXGcyaE3nJen4DAQdIAMTZABFsBgtjt6I8jhzoly4jzLgxna6Pd7fcLRS8lO8pdJZD9inlSsEtF0qvoNI0rUEBrqtJZ6BpAtqFaIvE4cXiw+ciXNo27uJ7UKyfbRKdTaQzmWyQ6dwxdifR27Hu72MAmnkmJR8JF907Ks4hFYEjZiLKJDL4jIWyxWqwZ9Poj74LBDG3buRO5uwIPShCsAEoAUQAggAVL8AH1cAAeQ-ABpKgAAUfxWL9U3yddfk3BBAkMJVFScOtvExcwml1VVDS8dVAkGFx6mNe9Q3tCNJzxdIaISf1OF2EVDmOB9x1bZJ6O4RjKAXMVXhTVdpSQzNQGzRF3F8bxLAPJxCw0VoHEQSFRARKpyyMJEWiMKixxbR0OLuPjH0ofsaVQOlGSwFlu1HfEuOMxyGPMsBBKXETcjTQpkMkxAtGrSpyytFxAjNPxvAI0RcycXwtE1boISw9DDHCG1OEyeA8ibfjjLXPyJLUWEEq6Uj9DRZSNAPdCIV1OFNXoBLkQ0QZzUxUQGxtPL3MjFJWA4bg+AEQqM2UFDy0rRLjBvcwGlBXxdWGZVsKCtry1MYwDKcoz+v5cluDGjcAvlRSER0MwrqPcKEqhVSEBCegNS8LQ5pmwidubB1+unTs41oY7-JK1DgtNDVPCutL9F1bQ3EU0ErSqiH0p6zi9snF83yB4rswhPRUWGIJ9SvFpdSMXxmshfxIvQxEwjR6i+row6oHynGJtO80lQGVFgnIsxwS8XVzR3bpC2GaszXMXwvvy-qmwgDm5VIndvCsOtIUqmpbAexVdESiwC1VJEEv0OXmbbF0IC-AdUGVlD4t0KsPHe+o6h1B6jA0zVYuNPotCCSEMtCIA */ createMachine( { id: "createWorkspaceState", From ed72bf52dbd35e9a87a7fdc86716ec1c70648f98 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Thu, 21 Sep 2023 00:14:19 +0000 Subject: [PATCH 02/11] refactor: poll for git auth updates when connecting a provider --- site/src/api/queries/templates.ts | 13 ++++ .../CreateWorkspacePage.tsx | 63 ++++++++++++++----- .../CreateWorkspacePageView.tsx | 13 +++- .../src/pages/CreateWorkspacePage/GitAuth.tsx | 45 ++++++++----- .../src/pages/GitAuthPage/GitAuthPageView.tsx | 2 +- .../createWorkspaceXService.ts | 33 +++------- 6 files changed, 109 insertions(+), 60 deletions(-) diff --git a/site/src/api/queries/templates.ts b/site/src/api/queries/templates.ts index 7bb34b74df124..b01cb2d31a3e9 100644 --- a/site/src/api/queries/templates.ts +++ b/site/src/api/queries/templates.ts @@ -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), + }; +}; diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx index 60932170a5a2f..9673d975ac3ea 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx @@ -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,45 @@ 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("idle"); + + useEffect(() => { + if (gitAuthPollingState !== "polling") { + return; + } + + // Poll for a maximum of one minute + const quitPolling = setTimeout( + () => setGitAuthPollingState("abandoned"), + 60_000, + ); + return () => { + clearTimeout(quitPolling); + }; + }, [gitAuthPollingState]); + + const startPollingGitAuth = useCallback(() => { + if (gitAuthPollingState === "polling") { + return; + } + setGitAuthPollingState("polling"); + }, [gitAuthPollingState]); + + const { data: gitAuth, error } = useQuery( + versionId + ? { + ...templateVersionGitAuth(versionId), + refetchInterval: gitAuthPollingState === "polling" ? 1000 : false, + } + : { enabled: false }, + ); return ( <> @@ -81,11 +110,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); diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index 77d078ea0c939..34a50b537eb86 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -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 = ({ template, versionId, gitAuth, + gitAuthPollingState, + startPollingGitAuth, parameters, defaultBuildParameters, permissions, @@ -113,7 +118,7 @@ export const CreateWorkspacePageView: FC = ({ > - {versionId && ( + {versionId !== template.active_version_id && ( = ({ description="This template requires authentication to automatically perform Git operations on create." > - {gitAuth.map((auth, index) => ( + {gitAuth.map((auth) => ( diff --git a/site/src/pages/CreateWorkspacePage/GitAuth.tsx b/site/src/pages/CreateWorkspacePage/GitAuth.tsx index 69315899b539c..bc81c3a73a1c9 100644 --- a/site/src/pages/CreateWorkspacePage/GitAuth.tsx +++ b/site/src/pages/CreateWorkspacePage/GitAuth.tsx @@ -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 = ({ - type, - authenticated, - authenticateURL, - error, -}) => { +export const GitAuth: FC = (props) => { + const { + type, + authenticated, + authenticateURL, + gitAuthPollingState, + startPollingGitAuth, + error, + } = props; + const styles = useStyles({ error: typeof error !== "undefined", }); @@ -52,12 +62,11 @@ export const GitAuth: FC = ({ return ( -
- + ? `Authenticated with ${prettyName}` + : `Login with ${prettyName}`} + + {gitAuthPollingState === "abandoned" && ( + + )} {error && {error}} -
+
); }; diff --git a/site/src/pages/GitAuthPage/GitAuthPageView.tsx b/site/src/pages/GitAuthPage/GitAuthPageView.tsx index 5d15a677ea344..d880239e613b0 100644 --- a/site/src/pages/GitAuthPage/GitAuthPageView.tsx +++ b/site/src/pages/GitAuthPage/GitAuthPageView.tsx @@ -76,7 +76,7 @@ const GitAuthPageView: FC = ({

- 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!"}

diff --git a/site/src/xServices/createWorkspace/createWorkspaceXService.ts b/site/src/xServices/createWorkspace/createWorkspaceXService.ts index f78819285c2d2..e87f2c1573392 100644 --- a/site/src/xServices/createWorkspace/createWorkspaceXService.ts +++ b/site/src/xServices/createWorkspace/createWorkspaceXService.ts @@ -33,7 +33,6 @@ type CreateWorkspaceContext = { template?: Template; parameters?: TemplateVersionParameter[]; permissions?: Record; - gitAuth?: TemplateVersionGitAuth[]; // Used on auto-create defaultBuildParameters?: WorkspaceBuildParameter[]; }; @@ -49,7 +48,7 @@ type RefreshGitAuthEvent = { }; export const createWorkspaceMachine = - /** @xstate-layout N4IgpgJg5mDOIC5QGMBOYCGAXMB1A9qgNawAOGyYAyltmAHTIAWYyRAlgHZQCy+EYAMQBtAAwBdRKFL5Y7LO3ycpIAB6IATAGYN9UQBYArDsP6A7Pq1WNAGhABPRAE4AHPUOHRG7xoCMxswA2Mw0AX1C7NEwcAmIyCmpaHEYWNi5efiFhX0kkEBk5BSUVdQQ-M3onM19fUV8nfRdvJ0M7RzKdejMPVw0mpyctJ0DwyPQ6WJJySho6egwAVyx8AGFxhW5BCCUGLgA3fCIGKInCKYTZ5MXltej0hH38ZGxFTjFxd5UC+VeSxA8KlULIN9AYXC4zE42ogALT1eiBLSGbyBQIQvoufSuUYgE4xM7xGZJBjXVbrdKCMCoVCEeikAA22AAZoQALaMdZ4AnTRJzUm3F7cB6cA7PIpvCSfPLfcV-BBaFxOegKwzVRouUTdAxmaEIXwuQx6QyBPq+QKgg2iEYRXGcyaE3nJen4DAQdIAMTZABFsBgtjt6I8jhzoly4jzLgxna6Pd7fcLRS8lO8pdJZD9inlSsEtF0qvoNI0rUEBrqtJZ6BpAtqFaIvE4cXiw+ciXNo27uJ7UKyfbRKdTaQzmWyQ6dwxdifR27Hu72MAmnkmJR8JF907Ks4hFYEjZiLKJDL4jIWyxWqwZ9Poj74LBDG3buRO5uwIPShCsAEoAUQAggAVL8AH1cAAeQ-ABpKgAAUfxWL9U3yddfk3BBAkMJVFScOtvExcwml1VVDS8dVAkGFx6mNe9Q3tCNJzxdIaISf1OF2EVDmOB9x1bZJ6O4RjKAXMVXhTVdpSQzNQGzRF3F8bxLAPJxCw0VoHEQSFRARKpyyMJEWiMKixxbR0OLuPjH0ofsaVQOlGSwFlu1HfEuOMxyGPMsBBKXETcjTQpkMkxAtGrSpyytFxAjNPxvAI0RcycXwtE1boISw9DDHCG1OEyeA8ibfjjLXPyJLUWEEq6Uj9DRZSNAPdCIV1OFNXoBLkQ0QZzUxUQGxtPL3MjFJWA4bg+AEQqM2UFDy0rRLjBvcwGlBXxdWGZVsKCtry1MYwDKcoz+v5cluDGjcAvlRSER0MwrqPcKEqhVSEBCegNS8LQ5pmwidubB1+unTs41oY7-JK1DgtNDVPCutL9F1bQ3EU0ErSqiH0p6zi9snF83yB4rswhPRUWGIJ9SvFpdSMXxmshfxIvQxEwjR6i+row6oHynGJtO80lQGVFgnIsxwS8XVzR3bpC2GaszXMXwvvy-qmwgDm5VIndvCsOtIUqmpbAexVdESiwC1VJEEv0OXmbbF0IC-AdUGVlD4t0KsPHe+o6h1B6jA0zVYuNPotCCSEMtCIA */ + /** @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 +63,6 @@ export const createWorkspaceMachine = template: Template; permissions: CreateWSPermissions; parameters: TemplateVersionParameter[]; - gitAuth: TemplateVersionGitAuth[]; }; }; createWorkspace: { @@ -112,16 +110,6 @@ export const createWorkspaceMachine = }, }, idle: { - invoke: [ - { - src: () => (callback) => { - const channel = watchGitAuthRefresh(() => { - callback("REFRESH_GITAUTH"); - }); - return () => channel.close(); - }, - }, - ], on: { CREATE_WORKSPACE: { target: "creatingWorkspace", @@ -189,23 +177,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 +235,6 @@ const checkCreateWSPermissions = async (organizationId: string) => { }) as Promise>; }; -export const watchGitAuthRefresh = (callback: () => void) => { - const bc = new BroadcastChannel(REFRESH_GITAUTH_BROADCAST_CHANNEL); - bc.addEventListener("message", callback); - return bc; -}; - export type CreateWSPermissions = Awaited< ReturnType >; From e0f72ef4483dd6663562a0e92bfe5cdcf60aeaa5 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 22 Sep 2023 21:58:04 +0000 Subject: [PATCH 03/11] =?UTF-8?q?=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx index 9673d975ac3ea..8685ce8342607 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx @@ -77,11 +77,8 @@ const CreateWorkspacePage: FC = () => { }, [gitAuthPollingState]); const startPollingGitAuth = useCallback(() => { - if (gitAuthPollingState === "polling") { - return; - } setGitAuthPollingState("polling"); - }, [gitAuthPollingState]); + }, []); const { data: gitAuth, error } = useQuery( versionId From cd75e5c0aabd5e2eed2a4969b96de95bc1e5114c Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 22 Sep 2023 22:05:26 +0000 Subject: [PATCH 04/11] quit polling when authenticated --- .../CreateWorkspacePage.tsx | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx index 8685ce8342607..f7739a2acbed6 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx @@ -61,7 +61,27 @@ const CreateWorkspacePage: FC = () => { const [gitAuthPollingState, setGitAuthPollingState] = useState("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; } @@ -74,20 +94,7 @@ const CreateWorkspacePage: FC = () => { return () => { clearTimeout(quitPolling); }; - }, [gitAuthPollingState]); - - const startPollingGitAuth = useCallback(() => { - setGitAuthPollingState("polling"); - }, []); - - const { data: gitAuth, error } = useQuery( - versionId - ? { - ...templateVersionGitAuth(versionId), - refetchInterval: gitAuthPollingState === "polling" ? 1000 : false, - } - : { enabled: false }, - ); + }, [gitAuthPollingState, allSignedIn]); return ( <> From e4d67b9b67b9e9d4114aad9bb9a3e45a02abd4b4 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 22 Sep 2023 22:09:17 +0000 Subject: [PATCH 05/11] remove `BroadcastChannel` stuff --- .../CreateWorkspacePage.test.tsx | 11 ----------- .../CreateWorkspacePageView.tsx | 6 +----- site/src/pages/GitAuthPage/GitAuthPage.tsx | 14 +------------- site/src/pages/GitAuthPage/GitAuthPageView.tsx | 12 ------------ 4 files changed, 2 insertions(+), 41 deletions(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx index 7ca03313c5248..c7b00fa69acad 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx @@ -31,17 +31,6 @@ const renderCreateWorkspacePage = () => { }); }; -Object.defineProperty(window, "BroadcastChannel", { - value: class { - addEventListener() { - // noop - } - close() { - // noop - } - }, -}); - describe("CreateWorkspacePage", () => { it("succeeds with default owner", async () => { jest diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index 34a50b537eb86..ab7b1f1f66167 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -236,12 +236,8 @@ type GitAuthErrors = Record; const useGitAuthVerification = (gitAuth: TypesGen.TemplateVersionGitAuth[]) => { const [gitAuthErrors, setGitAuthErrors] = useState({}); + // 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]); diff --git a/site/src/pages/GitAuthPage/GitAuthPage.tsx b/site/src/pages/GitAuthPage/GitAuthPage.tsx index 0c64cad44d5e9..f0ebfa22f34ef 100644 --- a/site/src/pages/GitAuthPage/GitAuthPage.tsx +++ b/site/src/pages/GitAuthPage/GitAuthPage.tsx @@ -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; } diff --git a/site/src/pages/GitAuthPage/GitAuthPageView.tsx b/site/src/pages/GitAuthPage/GitAuthPageView.tsx index d880239e613b0..49cf79f925727 100644 --- a/site/src/pages/GitAuthPage/GitAuthPageView.tsx +++ b/site/src/pages/GitAuthPage/GitAuthPageView.tsx @@ -33,18 +33,6 @@ const GitAuthPageView: FC = ({ }) => { 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 ( From 8669a5fe00af6953ef2933f6d37a89eaaf14e73a Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 22 Sep 2023 22:20:41 +0000 Subject: [PATCH 06/11] =?UTF-8?q?=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/src/pages/GitAuthPage/GitAuthPageView.tsx | 3 +-- site/src/xServices/createWorkspace/createWorkspaceXService.ts | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/site/src/pages/GitAuthPage/GitAuthPageView.tsx b/site/src/pages/GitAuthPage/GitAuthPageView.tsx index 49cf79f925727..23ec8fd98ee0d 100644 --- a/site/src/pages/GitAuthPage/GitAuthPageView.tsx +++ b/site/src/pages/GitAuthPage/GitAuthPageView.tsx @@ -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; diff --git a/site/src/xServices/createWorkspace/createWorkspaceXService.ts b/site/src/xServices/createWorkspace/createWorkspaceXService.ts index e87f2c1573392..79dc44701430f 100644 --- a/site/src/xServices/createWorkspace/createWorkspaceXService.ts +++ b/site/src/xServices/createWorkspace/createWorkspaceXService.ts @@ -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"; From 479a790690299506f4e2bbea927e641dd7de7841 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 22 Sep 2023 22:30:24 +0000 Subject: [PATCH 07/11] fix tests --- site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx | 2 +- site/src/testHelpers/entities.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx index c7b00fa69acad..113a623844d92 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx @@ -193,7 +193,7 @@ describe("CreateWorkspacePage", () => { MockOrganization.id, "me", expect.objectContaining({ - template_id: MockTemplate.id, + template_version_id: MockTemplate.active_version_id, rich_parameter_values: [{ name: param, value: paramValue }], }), ); diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index ace4333128026..f16f08d575828 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -1154,7 +1154,7 @@ export const MockTemplateVersionVariable5: TypesGen.TemplateVersionVariable = { // requests the MockWorkspace export const MockWorkspaceRequest: TypesGen.CreateWorkspaceRequest = { name: "test", - template_id: "test-template", + template_version_id: "test-template-version", rich_parameter_values: [ { name: MockTemplateVersionParameter1.name, From 91665c6ee2b56c78b44790501e371bc7f24ef48d Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 22 Sep 2023 22:48:14 +0000 Subject: [PATCH 08/11] fix tests pt. 2 --- .../CreateWorkspacePage.test.tsx | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx index 113a623844d92..b79fb0da4995b 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx @@ -188,6 +188,31 @@ describe("CreateWorkspacePage", () => { path: "/templates/:template/workspace", }); + await waitFor(() => { + expect(createWorkspaceSpy).toBeCalledWith( + MockOrganization.id, + "me", + expect.objectContaining({ + template_id: MockTemplate.id, + rich_parameter_values: [{ name: param, value: paramValue }], + }), + ); + }); + }); + + 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(, { + 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, From f5d0b7e3f3fed35b94f7570dd698b8459bceb293 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Mon, 25 Sep 2023 15:29:36 +0000 Subject: [PATCH 09/11] tweak `versionId` logic a touch --- site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index ab7b1f1f66167..f46b5c42e26ff 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -118,7 +118,7 @@ export const CreateWorkspacePageView: FC = ({ > - {versionId !== template.active_version_id && ( + {versionId && versionId !== template.active_version_id && ( Date: Tue, 26 Sep 2023 17:07:24 +0000 Subject: [PATCH 10/11] add a test --- .../CreateWorkspacePage.test.tsx | 55 ++++++++++++++++++- site/src/testHelpers/entities.ts | 28 +++++++--- 2 files changed, 73 insertions(+), 10 deletions(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx index b79fb0da4995b..aaa7e4d5db467 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx @@ -7,17 +7,20 @@ import { MockWorkspace, MockWorkspaceQuota, MockWorkspaceRequest, + MockWorkspaceRichParametersRequest, MockTemplateVersionParameter1, MockTemplateVersionParameter2, MockTemplateVersionParameter3, MockTemplateVersionGitAuth, MockOrganization, + MockTemplateVersionGitAuthAuthenticated, } from "testHelpers/entities"; import { renderWithAuth, waitForLoaderToBeRemoved, } from "testHelpers/renderHelpers"; import CreateWorkspacePage from "./CreateWorkspacePage"; +import { delay } from "utils/delay"; const nameLabelText = "Workspace Name"; const createWorkspaceText = "Create Workspace"; @@ -60,9 +63,9 @@ describe("CreateWorkspacePage", () => { expect(API.createWorkspace).toBeCalledWith( MockUser.organization_ids[0], MockUser.id, - { - ...MockWorkspaceRequest, - }, + expect.objectContaining({ + ...MockWorkspaceRichParametersRequest, + }), ), ); }); @@ -154,6 +157,52 @@ 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"); + userEvent.click(githubButton); + + jest + .spyOn(API, "getTemplateVersionGitAuth") + .mockResolvedValue([MockTemplateVersionGitAuthAuthenticated]); + + // Wait for gitAuth to be refetched + await delay(1500); + 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") diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 6fc7f8cf5ae44..b150f00f466cb 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -1172,18 +1172,24 @@ export const MockTemplateVersionVariable5: TypesGen.TemplateVersionVariable = { sensitive: false, }; -// requests the MockWorkspace export const MockWorkspaceRequest: TypesGen.CreateWorkspaceRequest = { name: "test", template_version_id: "test-template-version", - rich_parameter_values: [ - { - name: MockTemplateVersionParameter1.name, - value: MockTemplateVersionParameter1.default_value, - }, - ], + 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", @@ -2166,6 +2172,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", From e81800eb4e8516d5d6993ea71c144b4b10807783 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 26 Sep 2023 17:19:37 +0000 Subject: [PATCH 11/11] oh --- .../pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx index aaa7e4d5db467..84036b0392921 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx @@ -20,7 +20,6 @@ import { waitForLoaderToBeRemoved, } from "testHelpers/renderHelpers"; import CreateWorkspacePage from "./CreateWorkspacePage"; -import { delay } from "utils/delay"; const nameLabelText = "Workspace Name"; const createWorkspaceText = "Create Workspace"; @@ -179,14 +178,12 @@ describe("CreateWorkspacePage", () => { }); const githubButton = await screen.findByText("Login with GitHub"); - userEvent.click(githubButton); + await userEvent.click(githubButton); jest .spyOn(API, "getTemplateVersionGitAuth") .mockResolvedValue([MockTemplateVersionGitAuthAuthenticated]); - // Wait for gitAuth to be refetched - await delay(1500); await screen.findByText("Authenticated with GitHub"); const submitButton = screen.getByText(createWorkspaceText);