diff --git a/site/src/api/queries/externalAuth.ts b/site/src/api/queries/externalAuth.ts new file mode 100644 index 0000000000000..2a0c66458752b --- /dev/null +++ b/site/src/api/queries/externalAuth.ts @@ -0,0 +1,63 @@ +import * as API from "api/api"; +import { ExternalAuth } from "api/typesGenerated"; +import { QueryClient, UseMutationOptions } from "react-query"; + +// Returns all configured external auths for a given user. +export const externalAuths = () => { + return { + queryKey: ["external-auth"], + queryFn: () => API.getUserExternalAuthProviders(), + }; +}; + +export const externalAuthProvider = (providerId: string) => { + return { + queryKey: ["external-auth", providerId], + queryFn: () => API.getExternalAuthProvider(providerId), + }; +}; + +export const externalAuthDevice = (providerId: string) => { + return { + queryFn: () => API.getExternalAuthDevice(providerId), + queryKey: ["external-auth", providerId, "device"], + }; +}; + +export const exchangeExternalAuthDevice = ( + providerId: string, + deviceCode: string, + queryClient: QueryClient, +) => { + return { + queryFn: () => + API.exchangeExternalAuthDevice(providerId, { + device_code: deviceCode, + }), + queryKey: ["external-auth", providerId, "device", deviceCode], + onSuccess: async () => { + // Force a refresh of the Git auth status. + await queryClient.invalidateQueries(["external-auth", providerId]); + }, + }; +}; + +export const validateExternalAuth = ( + queryClient: QueryClient, +): UseMutationOptions => { + return { + mutationFn: API.getExternalAuthProvider, + onSuccess: (data, providerId) => { + queryClient.setQueryData(["external-auth", providerId], data); + }, + }; +}; + +export const unlinkExternalAuths = (queryClient: QueryClient) => { + return { + mutationFn: API.unlinkExternalAuthProvider, + onSuccess: async () => { + await queryClient.invalidateQueries(["external-auth"]); + }, + }; +}; diff --git a/site/src/api/queries/externalauth.ts b/site/src/api/queries/externalauth.ts deleted file mode 100644 index 684135db75d13..0000000000000 --- a/site/src/api/queries/externalauth.ts +++ /dev/null @@ -1,40 +0,0 @@ -import * as API from "api/api"; -import { QueryClient } from "react-query"; - -const getUserExternalAuthsKey = () => ["list", "external-auth"]; - -// listUserExternalAuths returns all configured external auths for a given user. -export const listUserExternalAuths = () => { - return { - queryKey: getUserExternalAuthsKey(), - queryFn: () => API.getUserExternalAuthProviders(), - }; -}; - -const getUserExternalAuthKey = (providerID: string) => [ - providerID, - "get", - "external-auth", -]; - -export const userExternalAuth = (providerID: string) => { - return { - queryKey: getUserExternalAuthKey(providerID), - queryFn: () => API.getExternalAuthProvider(providerID), - }; -}; - -export const validateExternalAuth = (_: QueryClient) => { - return { - mutationFn: API.getExternalAuthProvider, - }; -}; - -export const unlinkExternalAuths = (queryClient: QueryClient) => { - return { - mutationFn: API.unlinkExternalAuthProvider, - onSuccess: async () => { - await queryClient.invalidateQueries(["external-auth"]); - }, - }; -}; diff --git a/site/src/pages/ExternalAuthPage/ExternalAuthPage.tsx b/site/src/pages/ExternalAuthPage/ExternalAuthPage.tsx index 59a9855a1c33e..3b9c81a636d82 100644 --- a/site/src/pages/ExternalAuthPage/ExternalAuthPage.tsx +++ b/site/src/pages/ExternalAuthPage/ExternalAuthPage.tsx @@ -1,9 +1,4 @@ import { useQuery, useQueryClient } from "react-query"; -import { - exchangeExternalAuthDevice, - getExternalAuthDevice, - getExternalAuthProvider, -} from "api/api"; import { usePermissions } from "hooks"; import { type FC } from "react"; import { useParams, useSearchParams } from "react-router-dom"; @@ -13,56 +8,44 @@ import { isAxiosError } from "axios"; import Button from "@mui/material/Button"; import { SignInLayout } from "components/SignInLayout/SignInLayout"; import { Welcome } from "components/Welcome/Welcome"; +import { + externalAuthDevice, + externalAuthProvider, + exchangeExternalAuthDevice, +} from "api/queries/externalAuth"; const ExternalAuthPage: FC = () => { - const { provider } = useParams(); - if (!provider) { - throw new Error("provider must exist"); - } + const { provider } = useParams() as { provider: string }; const [searchParams] = useSearchParams(); const permissions = usePermissions(); const queryClient = useQueryClient(); - const getExternalAuthProviderQuery = useQuery({ - queryKey: ["externalauth", provider], - queryFn: () => getExternalAuthProvider(provider), + const externalAuthProviderOpts = externalAuthProvider(provider); + const externalAuthProviderQuery = useQuery({ + ...externalAuthProviderOpts, refetchOnWindowFocus: true, }); - const getExternalAuthDeviceQuery = useQuery({ + const externalAuthDeviceQuery = useQuery({ + ...externalAuthDevice(provider), enabled: - Boolean(!getExternalAuthProviderQuery.data?.authenticated) && - Boolean(getExternalAuthProviderQuery.data?.device), - queryFn: () => getExternalAuthDevice(provider), - queryKey: ["externalauth", provider, "device"], + Boolean(!externalAuthProviderQuery.data?.authenticated) && + Boolean(externalAuthProviderQuery.data?.device), refetchOnMount: false, }); const exchangeExternalAuthDeviceQuery = useQuery({ - queryFn: () => - exchangeExternalAuthDevice(provider, { - device_code: getExternalAuthDeviceQuery.data?.device_code || "", - }), - queryKey: [ - "externalauth", + ...exchangeExternalAuthDevice( provider, - getExternalAuthDeviceQuery.data?.device_code, - ], - enabled: Boolean(getExternalAuthDeviceQuery.data), - onSuccess: () => { - // Force a refresh of the Git auth status. - queryClient.invalidateQueries(["externalauth", provider]).catch((ex) => { - console.error("invalidate queries", ex); - }); - }, + externalAuthDeviceQuery.data?.device_code ?? "", + queryClient, + ), + enabled: Boolean(externalAuthDeviceQuery.data), retry: true, - retryDelay: (getExternalAuthDeviceQuery.data?.interval || 5) * 1000, + retryDelay: (externalAuthDeviceQuery.data?.interval || 5) * 1000, refetchOnWindowFocus: (query) => query.state.status === "success" ? false : "always", }); - if ( - getExternalAuthProviderQuery.isLoading || - !getExternalAuthProviderQuery.data - ) { + if (externalAuthProviderQuery.isLoading || !externalAuthProviderQuery.data) { return null; } @@ -73,8 +56,8 @@ const ExternalAuthPage: FC = () => { } if ( - !getExternalAuthProviderQuery.data.authenticated && - !getExternalAuthProviderQuery.data.device + !externalAuthProviderQuery.data.authenticated && + !externalAuthProviderQuery.data.device ) { const redirectedParam = searchParams?.get("redirected"); if (redirectedParam && redirectedParam.toLowerCase() === "true") { @@ -111,16 +94,16 @@ const ExternalAuthPage: FC = () => { return ( { - queryClient.setQueryData(["externalauth", provider], { - ...getExternalAuthProviderQuery.data, + queryClient.setQueryData(externalAuthProviderOpts.queryKey, { + ...externalAuthProviderQuery.data, authenticated: false, }); }} viewExternalAuthConfig={permissions.viewExternalAuthConfig} deviceExchangeError={deviceExchangeError} - externalAuthDevice={getExternalAuthDeviceQuery.data} + externalAuthDevice={externalAuthDeviceQuery.data} /> ); }; diff --git a/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPage.tsx b/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPage.tsx index ced75156bd0c5..f577f31389e3d 100644 --- a/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPage.tsx +++ b/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPage.tsx @@ -1,10 +1,10 @@ import { FC, useState } from "react"; import { UserExternalAuthSettingsPageView } from "./UserExternalAuthSettingsPageView"; import { - listUserExternalAuths, + externalAuths, unlinkExternalAuths, validateExternalAuth, -} from "api/queries/externalauth"; +} from "api/queries/externalAuth"; import { Section } from "components/SettingsLayout/Section"; import { DeleteDialog } from "components/Dialogs/DeleteDialog/DeleteDialog"; import { useMutation, useQuery, useQueryClient } from "react-query"; @@ -17,30 +17,17 @@ const UserExternalAuthSettingsPage: FC = () => { // need to be refetched const [unlinked, setUnlinked] = useState(0); - const { - data: externalAuths, - error, - isLoading, - refetch, - } = useQuery(listUserExternalAuths()); - + const externalAuthsQuery = useQuery(externalAuths()); const [appToUnlink, setAppToUnlink] = useState(); - const mutateParams = unlinkExternalAuths(queryClient); - const unlinkAppMutation = useMutation({ - ...mutateParams, - onSuccess: async () => { - await mutateParams.onSuccess(); - }, - }); - + const unlinkAppMutation = useMutation(unlinkExternalAuths(queryClient)); const validateAppMutation = useMutation(validateExternalAuth(queryClient)); return ( -
+
{ setAppToUnlink(providerID); @@ -81,7 +68,7 @@ const UserExternalAuthSettingsPage: FC = () => { // setAppToUnlink closes the modal setAppToUnlink(undefined); // refetch repopulates the external auth data - await refetch(); + await externalAuthsQuery.refetch(); // this tells our child components to refetch their data // as at least 1 provider was unlinked. setUnlinked(unlinked + 1); diff --git a/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPageView.tsx b/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPageView.tsx index 96ef1066f43b7..0d00f4d4d596f 100644 --- a/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPageView.tsx +++ b/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPageView.tsx @@ -24,7 +24,7 @@ import { import { ExternalAuthPollingState } from "pages/CreateWorkspacePage/CreateWorkspacePage"; import { useState, useCallback, useEffect } from "react"; import { useQuery } from "react-query"; -import { userExternalAuth } from "api/queries/externalauth"; +import { externalAuthProvider } from "api/queries/externalAuth"; import { FullScreenLoader } from "components/Loader/FullScreenLoader"; export type UserExternalAuthSettingsPageViewProps = { @@ -61,7 +61,7 @@ export const UserExternalAuthSettingsPageView = ({ Application Link - + @@ -150,7 +150,7 @@ const ExternalAuthRow = ({ message={authenticated ? "Authenticated" : "Click to Login"} externalAuthPollingState={externalAuthPollingState} startPollingExternalAuth={startPollingExternalAuth} - > + /> {(link || externalAuth?.authenticated) && ( @@ -199,7 +199,7 @@ const useExternalAuth = (providerID: string, unlinked: number) => { }, []); const { data: externalAuth, refetch } = useQuery({ - ...userExternalAuth(providerID), + ...externalAuthProvider(providerID), refetchInterval: externalAuthPollingState === "polling" ? 1000 : false, });