From b4fa983b56e9d346bacac868a53a0150b49c2846 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 6 Dec 2023 16:26:32 +0000 Subject: [PATCH 1/3] Minor queries refactoring --- site/src/api/queries/externalAuth.ts | 63 +++++++++++++++++ site/src/api/queries/externalauth.ts | 40 ----------- .../ExternalAuthPage/ExternalAuthPage.tsx | 69 +++++++------------ .../UserExternalAuthSettingsPage.tsx | 11 +-- .../UserExternalAuthSettingsPageView.tsx | 4 +- 5 files changed, 93 insertions(+), 94 deletions(-) create mode 100644 site/src/api/queries/externalAuth.ts delete mode 100644 site/src/api/queries/externalauth.ts diff --git a/site/src/api/queries/externalAuth.ts b/site/src/api/queries/externalAuth.ts new file mode 100644 index 0000000000000..a4a34e9c0f908 --- /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"; + +// listUserExternalAuths returns all configured external auths for a given user. +export const listUserExternalAuths = () => { + 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..a64ae46d1e64c 100644 --- a/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPage.tsx +++ b/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPage.tsx @@ -4,7 +4,7 @@ import { listUserExternalAuths, 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"; @@ -25,14 +25,7 @@ const UserExternalAuthSettingsPage: FC = () => { } = useQuery(listUserExternalAuths()); 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 ( diff --git a/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPageView.tsx b/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPageView.tsx index 96ef1066f43b7..e1d6b9c690f15 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 = { @@ -199,7 +199,7 @@ const useExternalAuth = (providerID: string, unlinked: number) => { }, []); const { data: externalAuth, refetch } = useQuery({ - ...userExternalAuth(providerID), + ...externalAuthProvider(providerID), refetchInterval: externalAuthPollingState === "polling" ? 1000 : false, }); From 1dff105fb0fcd6af8fdf633c33e8986f1dd11e76 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 6 Dec 2023 16:33:25 +0000 Subject: [PATCH 2/3] Another minor query refactor --- site/src/api/queries/externalAuth.ts | 4 ++-- .../UserExternalAuthSettingsPage.tsx | 18 ++++++------------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/site/src/api/queries/externalAuth.ts b/site/src/api/queries/externalAuth.ts index a4a34e9c0f908..2a0c66458752b 100644 --- a/site/src/api/queries/externalAuth.ts +++ b/site/src/api/queries/externalAuth.ts @@ -2,8 +2,8 @@ import * as API from "api/api"; import { ExternalAuth } from "api/typesGenerated"; import { QueryClient, UseMutationOptions } from "react-query"; -// listUserExternalAuths returns all configured external auths for a given user. -export const listUserExternalAuths = () => { +// Returns all configured external auths for a given user. +export const externalAuths = () => { return { queryKey: ["external-auth"], queryFn: () => API.getUserExternalAuthProviders(), diff --git a/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPage.tsx b/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPage.tsx index a64ae46d1e64c..63ac742a5d63c 100644 --- a/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPage.tsx +++ b/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPage.tsx @@ -1,7 +1,7 @@ import { FC, useState } from "react"; import { UserExternalAuthSettingsPageView } from "./UserExternalAuthSettingsPageView"; import { - listUserExternalAuths, + externalAuths, unlinkExternalAuths, validateExternalAuth, } from "api/queries/externalAuth"; @@ -17,13 +17,7 @@ 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 unlinkAppMutation = useMutation(unlinkExternalAuths(queryClient)); const validateAppMutation = useMutation(validateExternalAuth(queryClient)); @@ -31,9 +25,9 @@ const UserExternalAuthSettingsPage: FC = () => { return (
{ setAppToUnlink(providerID); @@ -74,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); From 6e4d12595edc847d9e18cbb874dfa56e3330686b Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 6 Dec 2023 16:40:27 +0000 Subject: [PATCH 3/3] refactor(site): minor query and visual improvements on external auth --- .../UserExternalAuthSettingsPage.tsx | 2 +- .../UserExternalAuthSettingsPageView.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPage.tsx b/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPage.tsx index 63ac742a5d63c..f577f31389e3d 100644 --- a/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPage.tsx +++ b/site/src/pages/UserExternalAuthSettingsPage/UserExternalAuthSettingsPage.tsx @@ -23,7 +23,7 @@ const UserExternalAuthSettingsPage: FC = () => { const validateAppMutation = useMutation(validateExternalAuth(queryClient)); return ( -
+
Application Link - + @@ -150,7 +150,7 @@ const ExternalAuthRow = ({ message={authenticated ? "Authenticated" : "Click to Login"} externalAuthPollingState={externalAuthPollingState} startPollingExternalAuth={startPollingExternalAuth} - > + /> {(link || externalAuth?.authenticated) && (