From fd977e22bc41cde5cb671ad1d5964a01886fc5c6 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 22 Apr 2024 14:14:17 -0400 Subject: [PATCH 1/9] chore: reduce requests the dashboard makes from seeded data We already inject all of this content in `index.html`. There was also a bug with displaying a loading indicator when the workspace proxies endpoint 404s. --- site/src/api/queries/appearance.ts | 14 +++++++-- site/src/api/queries/buildInfo.ts | 14 +++++++-- site/src/api/queries/users.ts | 30 ++++++++++++++----- .../modules/dashboard/Navbar/NavbarView.tsx | 7 +++++ 4 files changed, 54 insertions(+), 11 deletions(-) diff --git a/site/src/api/queries/appearance.ts b/site/src/api/queries/appearance.ts index c81e01dbb9337..46fbcb9c7138e 100644 --- a/site/src/api/queries/appearance.ts +++ b/site/src/api/queries/appearance.ts @@ -6,12 +6,22 @@ import { getMetadataAsJSON } from "utils/metadata"; const initialAppearanceData = getMetadataAsJSON("appearance"); const appearanceConfigKey = ["appearance"] as const; -export const appearance = (): UseQueryOptions => { - return { +export const appearance = () => { + const opts: UseQueryOptions = { queryKey: ["appearance"], initialData: initialAppearanceData, queryFn: () => API.getAppearance(), }; + // If we have initial appearance data, we don't want to fetch + // the user again. We already have it! + if (initialAppearanceData) { + opts.cacheTime = Infinity; + opts.staleTime = Infinity; + opts.refetchOnMount = false; + opts.refetchOnReconnect = false; + opts.refetchOnWindowFocus = false; + } + return opts; }; export const updateAppearance = (queryClient: QueryClient) => { diff --git a/site/src/api/queries/buildInfo.ts b/site/src/api/queries/buildInfo.ts index b0761f001d6d6..48d278660c840 100644 --- a/site/src/api/queries/buildInfo.ts +++ b/site/src/api/queries/buildInfo.ts @@ -6,10 +6,20 @@ import { getMetadataAsJSON } from "utils/metadata"; const initialBuildInfoData = getMetadataAsJSON("build-info"); const buildInfoKey = ["buildInfo"] as const; -export const buildInfo = (): UseQueryOptions => { - return { +export const buildInfo = () => { + const opts: UseQueryOptions = { queryKey: buildInfoKey, initialData: initialBuildInfoData, queryFn: () => API.getBuildInfo(), }; + // If we have initial build info data, we don't want to fetch + // the user again. We already have it! + if (initialBuildInfoData) { + opts.cacheTime = Infinity; + opts.staleTime = Infinity; + opts.refetchOnMount = false; + opts.refetchOnReconnect = false; + opts.refetchOnWindowFocus = false; + } + return opts; }; diff --git a/site/src/api/queries/users.ts b/site/src/api/queries/users.ts index 3aa0b009a2527..e454f0101b447 100644 --- a/site/src/api/queries/users.ts +++ b/site/src/api/queries/users.ts @@ -112,6 +112,8 @@ export const updateRoles = (queryClient: QueryClient) => { }; }; +const initialUserData = getMetadataAsJSON("user"); + export const authMethods = () => { return { // Even the endpoint being /users/authmethods we don't want to revalidate it @@ -121,18 +123,26 @@ export const authMethods = () => { }; }; -const initialUserData = getMetadataAsJSON("user"); - const meKey = ["me"]; -export const me = (): UseQueryOptions & { - queryKey: QueryKey; -} => { - return { +export const me = () => { + const opts: UseQueryOptions & { + queryKey: QueryKey; + } = { queryKey: meKey, initialData: initialUserData, queryFn: API.getAuthenticatedUser, }; + // If we have initial user data, we don't want to fetch + // the user again. We already have it! + if (initialUserData) { + opts.cacheTime = Infinity; + opts.staleTime = Infinity; + opts.refetchOnMount = false; + opts.refetchOnReconnect = false; + opts.refetchOnWindowFocus = false; + } + return opts; }; export function apiKey(): UseQueryOptions { @@ -142,8 +152,14 @@ export function apiKey(): UseQueryOptions { }; } -export const hasFirstUser = () => { +export const hasFirstUser = (): UseQueryOptions => { return { + // If there is initial user data, we don't want to make + // this request. It's a waste! + cacheTime: Infinity, + staleTime: Infinity, + initialData: Boolean(initialUserData), + queryKey: ["hasFirstUser"], queryFn: API.hasFirstUser, }; diff --git a/site/src/modules/dashboard/Navbar/NavbarView.tsx b/site/src/modules/dashboard/Navbar/NavbarView.tsx index 65f14e7c1273f..440bdd44f249d 100644 --- a/site/src/modules/dashboard/Navbar/NavbarView.tsx +++ b/site/src/modules/dashboard/Navbar/NavbarView.tsx @@ -235,6 +235,13 @@ const ProxyMenu: FC = ({ proxyContextValue }) => { return proxy.healthy && latency !== undefined && latency.at < refetchDate; }; + // This endpoint returns a 404 when not using enterprise. + // If we don't return null, then it looks like this is + // loading forever! + if (proxyContextValue.error) { + return null; + } + if (isLoading) { return ( Date: Mon, 22 Apr 2024 18:30:26 +0000 Subject: [PATCH 2/9] Fix first user fetch --- site/src/api/queries/appearance.ts | 21 +++++++++----------- site/src/api/queries/buildInfo.ts | 21 +++++++++----------- site/src/api/queries/users.ts | 32 +++++++++++++++--------------- 3 files changed, 34 insertions(+), 40 deletions(-) diff --git a/site/src/api/queries/appearance.ts b/site/src/api/queries/appearance.ts index 46fbcb9c7138e..9aeb4cdf3fc5c 100644 --- a/site/src/api/queries/appearance.ts +++ b/site/src/api/queries/appearance.ts @@ -6,22 +6,19 @@ import { getMetadataAsJSON } from "utils/metadata"; const initialAppearanceData = getMetadataAsJSON("appearance"); const appearanceConfigKey = ["appearance"] as const; -export const appearance = () => { - const opts: UseQueryOptions = { +export const appearance = (): UseQueryOptions => { + return { + // We either have our initial data or should immediately + // fetch and never again! + cacheTime: Infinity, + staleTime: Infinity, + refetchOnMount: false, + refetchOnReconnect: false, + refetchOnWindowFocus: false, queryKey: ["appearance"], initialData: initialAppearanceData, queryFn: () => API.getAppearance(), }; - // If we have initial appearance data, we don't want to fetch - // the user again. We already have it! - if (initialAppearanceData) { - opts.cacheTime = Infinity; - opts.staleTime = Infinity; - opts.refetchOnMount = false; - opts.refetchOnReconnect = false; - opts.refetchOnWindowFocus = false; - } - return opts; }; export const updateAppearance = (queryClient: QueryClient) => { diff --git a/site/src/api/queries/buildInfo.ts b/site/src/api/queries/buildInfo.ts index 48d278660c840..4b3c53e9b6d0e 100644 --- a/site/src/api/queries/buildInfo.ts +++ b/site/src/api/queries/buildInfo.ts @@ -6,20 +6,17 @@ import { getMetadataAsJSON } from "utils/metadata"; const initialBuildInfoData = getMetadataAsJSON("build-info"); const buildInfoKey = ["buildInfo"] as const; -export const buildInfo = () => { - const opts: UseQueryOptions = { +export const buildInfo = (): UseQueryOptions => { + return { + // We either have our initial data or should immediately + // fetch and never again! + cacheTime: Infinity, + staleTime: Infinity, + refetchOnMount: false, + refetchOnReconnect: false, + refetchOnWindowFocus: false, queryKey: buildInfoKey, initialData: initialBuildInfoData, queryFn: () => API.getBuildInfo(), }; - // If we have initial build info data, we don't want to fetch - // the user again. We already have it! - if (initialBuildInfoData) { - opts.cacheTime = Infinity; - opts.staleTime = Infinity; - opts.refetchOnMount = false; - opts.refetchOnReconnect = false; - opts.refetchOnWindowFocus = false; - } - return opts; }; diff --git a/site/src/api/queries/users.ts b/site/src/api/queries/users.ts index e454f0101b447..e76a6698f499b 100644 --- a/site/src/api/queries/users.ts +++ b/site/src/api/queries/users.ts @@ -125,24 +125,21 @@ export const authMethods = () => { const meKey = ["me"]; -export const me = () => { - const opts: UseQueryOptions & { - queryKey: QueryKey; - } = { +export const me = (): UseQueryOptions & { + queryKey: QueryKey; +} => { + return { + // We either have our initial data or should immediately + // fetch and never again! + cacheTime: Infinity, + staleTime: Infinity, + refetchOnMount: false, + refetchOnReconnect: false, + refetchOnWindowFocus: false, queryKey: meKey, initialData: initialUserData, queryFn: API.getAuthenticatedUser, }; - // If we have initial user data, we don't want to fetch - // the user again. We already have it! - if (initialUserData) { - opts.cacheTime = Infinity; - opts.staleTime = Infinity; - opts.refetchOnMount = false; - opts.refetchOnReconnect = false; - opts.refetchOnWindowFocus = false; - } - return opts; }; export function apiKey(): UseQueryOptions { @@ -158,8 +155,11 @@ export const hasFirstUser = (): UseQueryOptions => { // this request. It's a waste! cacheTime: Infinity, staleTime: Infinity, - initialData: Boolean(initialUserData), - + refetchOnMount: false, + refetchOnReconnect: false, + refetchOnWindowFocus: false, + // This cannot be false otherwise it will not fetch! + initialData: typeof initialUserData !== "undefined" ? true : undefined, queryKey: ["hasFirstUser"], queryFn: API.hasFirstUser, }; From 94adf605a63d653f8265256ff9ddd76cb821a246 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 22 Apr 2024 18:34:51 +0000 Subject: [PATCH 3/9] Add util --- site/src/api/queries/appearance.ts | 7 ++----- site/src/api/queries/buildInfo.ts | 7 ++----- site/src/api/queries/users.ts | 13 +++---------- site/src/api/queries/util.ts | 13 +++++++++++++ 4 files changed, 20 insertions(+), 20 deletions(-) create mode 100644 site/src/api/queries/util.ts diff --git a/site/src/api/queries/appearance.ts b/site/src/api/queries/appearance.ts index 9aeb4cdf3fc5c..58433f658d40f 100644 --- a/site/src/api/queries/appearance.ts +++ b/site/src/api/queries/appearance.ts @@ -2,6 +2,7 @@ import type { QueryClient, UseQueryOptions } from "react-query"; import * as API from "api/api"; import type { AppearanceConfig } from "api/typesGenerated"; import { getMetadataAsJSON } from "utils/metadata"; +import { cachedQuery } from "./util"; const initialAppearanceData = getMetadataAsJSON("appearance"); const appearanceConfigKey = ["appearance"] as const; @@ -10,11 +11,7 @@ export const appearance = (): UseQueryOptions => { return { // We either have our initial data or should immediately // fetch and never again! - cacheTime: Infinity, - staleTime: Infinity, - refetchOnMount: false, - refetchOnReconnect: false, - refetchOnWindowFocus: false, + ...cachedQuery, queryKey: ["appearance"], initialData: initialAppearanceData, queryFn: () => API.getAppearance(), diff --git a/site/src/api/queries/buildInfo.ts b/site/src/api/queries/buildInfo.ts index 4b3c53e9b6d0e..23d932eca4bc2 100644 --- a/site/src/api/queries/buildInfo.ts +++ b/site/src/api/queries/buildInfo.ts @@ -2,6 +2,7 @@ import type { UseQueryOptions } from "react-query"; import * as API from "api/api"; import type { BuildInfoResponse } from "api/typesGenerated"; import { getMetadataAsJSON } from "utils/metadata"; +import { cachedQuery } from "./util"; const initialBuildInfoData = getMetadataAsJSON("build-info"); const buildInfoKey = ["buildInfo"] as const; @@ -10,11 +11,7 @@ export const buildInfo = (): UseQueryOptions => { return { // We either have our initial data or should immediately // fetch and never again! - cacheTime: Infinity, - staleTime: Infinity, - refetchOnMount: false, - refetchOnReconnect: false, - refetchOnWindowFocus: false, + ...cachedQuery, queryKey: buildInfoKey, initialData: initialBuildInfoData, queryFn: () => API.getBuildInfo(), diff --git a/site/src/api/queries/users.ts b/site/src/api/queries/users.ts index e76a6698f499b..0d92003acffb6 100644 --- a/site/src/api/queries/users.ts +++ b/site/src/api/queries/users.ts @@ -19,6 +19,7 @@ import type { UsePaginatedQueryOptions } from "hooks/usePaginatedQuery"; import { prepareQuery } from "utils/filters"; import { getMetadataAsJSON } from "utils/metadata"; import { getAuthorizationKey } from "./authCheck"; +import { cachedQuery } from "./util"; export function usersKey(req: UsersRequest) { return ["users", req] as const; @@ -131,11 +132,7 @@ export const me = (): UseQueryOptions & { return { // We either have our initial data or should immediately // fetch and never again! - cacheTime: Infinity, - staleTime: Infinity, - refetchOnMount: false, - refetchOnReconnect: false, - refetchOnWindowFocus: false, + ...cachedQuery, queryKey: meKey, initialData: initialUserData, queryFn: API.getAuthenticatedUser, @@ -153,11 +150,7 @@ export const hasFirstUser = (): UseQueryOptions => { return { // If there is initial user data, we don't want to make // this request. It's a waste! - cacheTime: Infinity, - staleTime: Infinity, - refetchOnMount: false, - refetchOnReconnect: false, - refetchOnWindowFocus: false, + ...cachedQuery, // This cannot be false otherwise it will not fetch! initialData: typeof initialUserData !== "undefined" ? true : undefined, queryKey: ["hasFirstUser"], diff --git a/site/src/api/queries/util.ts b/site/src/api/queries/util.ts new file mode 100644 index 0000000000000..b5002b1d5f293 --- /dev/null +++ b/site/src/api/queries/util.ts @@ -0,0 +1,13 @@ +// cachedQuery allows the caller to only make a request +// a single time, and use `initialData` if it is provided. +// +// This is particularly helpful for passing values injected +// via metadata. We do this for the initial user fetch, buildinfo, +// and a few others to reduce page load time. +export const cachedQuery = { + cacheTime: Infinity, + staleTime: Infinity, + refetchOnMount: false, + refetchOnReconnect: false, + refetchOnWindowFocus: false, +}; From 43cb86bd4375a7456b845c8d983081ff7bf106ff Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 22 Apr 2024 18:40:21 +0000 Subject: [PATCH 4/9] Add cached query for entitlements and experiments --- site/src/api/queries/entitlements.ts | 2 ++ site/src/api/queries/experiments.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/site/src/api/queries/entitlements.ts b/site/src/api/queries/entitlements.ts index d92b81cec6095..cf4440d300240 100644 --- a/site/src/api/queries/entitlements.ts +++ b/site/src/api/queries/entitlements.ts @@ -2,12 +2,14 @@ import type { QueryClient, UseQueryOptions } from "react-query"; import * as API from "api/api"; import type { Entitlements } from "api/typesGenerated"; import { getMetadataAsJSON } from "utils/metadata"; +import { cachedQuery } from "./util" const initialEntitlementsData = getMetadataAsJSON("entitlements"); const ENTITLEMENTS_QUERY_KEY = ["entitlements"] as const; export const entitlements = (): UseQueryOptions => { return { + ...cachedQuery, queryKey: ENTITLEMENTS_QUERY_KEY, queryFn: () => API.getEntitlements(), initialData: initialEntitlementsData, diff --git a/site/src/api/queries/experiments.ts b/site/src/api/queries/experiments.ts index 1f7e04901ae59..92557d79d1d4c 100644 --- a/site/src/api/queries/experiments.ts +++ b/site/src/api/queries/experiments.ts @@ -2,12 +2,14 @@ import type { UseQueryOptions } from "react-query"; import * as API from "api/api"; import type { Experiments } from "api/typesGenerated"; import { getMetadataAsJSON } from "utils/metadata"; +import { cachedQuery } from "./util" const initialExperimentsData = getMetadataAsJSON("experiments"); const experimentsKey = ["experiments"] as const; export const experiments = (): UseQueryOptions => { return { + ...cachedQuery, queryKey: experimentsKey, initialData: initialExperimentsData, queryFn: () => API.getExperiments(), From aa2cb8fec405e37e80143a18cb3d563feeebfa91 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 22 Apr 2024 18:44:33 +0000 Subject: [PATCH 5/9] Fix authmethods unnecessary request --- site/src/contexts/auth/AuthProvider.tsx | 11 +---------- site/src/pages/LoginPage/LoginPage.tsx | 8 +++++--- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/site/src/contexts/auth/AuthProvider.tsx b/site/src/contexts/auth/AuthProvider.tsx index 567c48e8fce38..f581971b1175a 100644 --- a/site/src/contexts/auth/AuthProvider.tsx +++ b/site/src/contexts/auth/AuthProvider.tsx @@ -9,18 +9,13 @@ import { useMutation, useQuery, useQueryClient } from "react-query"; import { isApiError } from "api/errors"; import { checkAuthorization } from "api/queries/authCheck"; import { - authMethods, hasFirstUser, login, logout, me, updateProfile as updateProfileOptions, } from "api/queries/users"; -import type { - AuthMethods, - UpdateUserProfileRequest, - User, -} from "api/typesGenerated"; +import type { UpdateUserProfileRequest, User } from "api/typesGenerated"; import { displaySuccess } from "components/GlobalSnackbar/utils"; import { permissionsToCheck, type Permissions } from "./permissions"; @@ -34,7 +29,6 @@ export type AuthContextValue = { isUpdatingProfile: boolean; user: User | undefined; permissions: Permissions | undefined; - authMethods: AuthMethods | undefined; organizationId: string | undefined; signInError: unknown; updateProfileError: unknown; @@ -51,7 +45,6 @@ export const AuthProvider: FC = ({ children }) => { const queryClient = useQueryClient(); const meOptions = me(); const userQuery = useQuery(meOptions); - const authMethodsQuery = useQuery(authMethods()); const hasFirstUserQuery = useQuery(hasFirstUser()); const permissionsQuery = useQuery({ ...checkAuthorization({ checks: permissionsToCheck }), @@ -77,7 +70,6 @@ export const AuthProvider: FC = ({ children }) => { userQuery.error.response.status === 401; const isSigningOut = logoutMutation.isLoading; const isLoading = - authMethodsQuery.isLoading || userQuery.isLoading || hasFirstUserQuery.isLoading || (userQuery.isSuccess && permissionsQuery.isLoading); @@ -120,7 +112,6 @@ export const AuthProvider: FC = ({ children }) => { updateProfile, user: userQuery.data, permissions: permissionsQuery.data as Permissions | undefined, - authMethods: authMethodsQuery.data, signInError: loginMutation.error, updateProfileError: updateProfileMutation.error, organizationId: userQuery.data?.organization_ids[0], diff --git a/site/src/pages/LoginPage/LoginPage.tsx b/site/src/pages/LoginPage/LoginPage.tsx index 0b1f5fad4b338..88aa450dad2c3 100644 --- a/site/src/pages/LoginPage/LoginPage.tsx +++ b/site/src/pages/LoginPage/LoginPage.tsx @@ -1,6 +1,8 @@ import type { FC } from "react"; import { Helmet } from "react-helmet-async"; +import { useQuery } from "react-query"; import { Navigate, useLocation, useNavigate } from "react-router-dom"; +import { authMethods } from "api/queries/users"; import { useAuthContext } from "contexts/auth/AuthProvider"; import { getApplicationName } from "utils/appearance"; import { retrieveRedirect } from "utils/redirect"; @@ -14,9 +16,9 @@ export const LoginPage: FC = () => { isConfiguringTheFirstUser, signIn, isSigningIn, - authMethods, signInError, } = useAuthContext(); + const authMethodsQuery = useQuery(authMethods()); const redirectTo = retrieveRedirect(location.search); const applicationName = getApplicationName(); const navigate = useNavigate(); @@ -60,9 +62,9 @@ export const LoginPage: FC = () => { Codestin Search App { await signIn(email, password); From 64b770e433ac4ef70af3d38aafec2bbc97394680 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 22 Apr 2024 18:46:32 +0000 Subject: [PATCH 6/9] Fix unnecessary region request --- site/src/contexts/ProxyContext.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/site/src/contexts/ProxyContext.tsx b/site/src/contexts/ProxyContext.tsx index ae6637238e8b9..18deed1c306a2 100644 --- a/site/src/contexts/ProxyContext.tsx +++ b/site/src/contexts/ProxyContext.tsx @@ -9,6 +9,7 @@ import { } from "react"; import { useQuery } from "react-query"; import { getWorkspaceProxies, getWorkspaceProxyRegions } from "api/api"; +import { cachedQuery } from "api/queries/util"; import type { Region, WorkspaceProxy } from "api/typesGenerated"; import { useAuthenticated } from "contexts/auth/RequireAuth"; import { type ProxyLatencyReport, useProxyLatency } from "./useProxyLatency"; @@ -131,6 +132,7 @@ export const ProxyProvider: FC = ({ children }) => { isLoading: proxiesLoading, isFetched: proxiesFetched, } = useQuery({ + ...cachedQuery, queryKey, queryFn: query, staleTime: initialData ? Infinity : undefined, From ebf13d5ac3bde52bf6ce0e1c12050954d1d6c994 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 22 Apr 2024 18:51:53 +0000 Subject: [PATCH 7/9] Fix fmt --- site/src/api/queries/entitlements.ts | 2 +- site/src/api/queries/experiments.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/api/queries/entitlements.ts b/site/src/api/queries/entitlements.ts index cf4440d300240..327097b150e71 100644 --- a/site/src/api/queries/entitlements.ts +++ b/site/src/api/queries/entitlements.ts @@ -2,7 +2,7 @@ import type { QueryClient, UseQueryOptions } from "react-query"; import * as API from "api/api"; import type { Entitlements } from "api/typesGenerated"; import { getMetadataAsJSON } from "utils/metadata"; -import { cachedQuery } from "./util" +import { cachedQuery } from "./util"; const initialEntitlementsData = getMetadataAsJSON("entitlements"); const ENTITLEMENTS_QUERY_KEY = ["entitlements"] as const; diff --git a/site/src/api/queries/experiments.ts b/site/src/api/queries/experiments.ts index 92557d79d1d4c..fd08d8fa5b867 100644 --- a/site/src/api/queries/experiments.ts +++ b/site/src/api/queries/experiments.ts @@ -2,7 +2,7 @@ import type { UseQueryOptions } from "react-query"; import * as API from "api/api"; import type { Experiments } from "api/typesGenerated"; import { getMetadataAsJSON } from "utils/metadata"; -import { cachedQuery } from "./util" +import { cachedQuery } from "./util"; const initialExperimentsData = getMetadataAsJSON("experiments"); const experimentsKey = ["experiments"] as const; From 60fed57a3d767c69607fcbb01c0b7088e3929182 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 22 Apr 2024 19:24:16 +0000 Subject: [PATCH 8/9] Debug --- site/src/api/queries/entitlements.ts | 6 +++--- site/src/utils/metadata.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/site/src/api/queries/entitlements.ts b/site/src/api/queries/entitlements.ts index 327097b150e71..76e411898295a 100644 --- a/site/src/api/queries/entitlements.ts +++ b/site/src/api/queries/entitlements.ts @@ -5,12 +5,12 @@ import { getMetadataAsJSON } from "utils/metadata"; import { cachedQuery } from "./util"; const initialEntitlementsData = getMetadataAsJSON("entitlements"); -const ENTITLEMENTS_QUERY_KEY = ["entitlements"] as const; +const entitlementsQueryKey = ["entitlements"] as const; export const entitlements = (): UseQueryOptions => { return { ...cachedQuery, - queryKey: ENTITLEMENTS_QUERY_KEY, + queryKey: entitlementsQueryKey, queryFn: () => API.getEntitlements(), initialData: initialEntitlementsData, }; @@ -21,7 +21,7 @@ export const refreshEntitlements = (queryClient: QueryClient) => { mutationFn: API.refreshEntitlements, onSuccess: async () => { await queryClient.invalidateQueries({ - queryKey: ENTITLEMENTS_QUERY_KEY, + queryKey: entitlementsQueryKey, }); }, }; diff --git a/site/src/utils/metadata.ts b/site/src/utils/metadata.ts index 8b05747579746..723b2e508395e 100644 --- a/site/src/utils/metadata.ts +++ b/site/src/utils/metadata.ts @@ -1,10 +1,10 @@ export const getMetadataAsJSON = >( property: string, ): T | undefined => { - const appearance = document.querySelector(`meta[property=${property}]`); + const metadata = document.querySelector(`meta[property=${property}]`); - if (appearance) { - const rawContent = appearance.getAttribute("content"); + if (metadata) { + const rawContent = metadata.getAttribute("content"); if (rawContent) { try { From 9f369ac2011e9b7603eb46802e0c496f438e9ddf Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 22 Apr 2024 19:32:21 +0000 Subject: [PATCH 9/9] Fix test --- site/src/api/queries/appearance.ts | 3 +-- site/src/api/queries/buildInfo.ts | 3 +-- site/src/api/queries/entitlements.ts | 3 +-- site/src/api/queries/experiments.ts | 3 +-- site/src/api/queries/users.ts | 10 ++-------- site/src/api/queries/util.ts | 24 +++++++++++++++++------- site/src/contexts/ProxyContext.tsx | 8 +++----- 7 files changed, 26 insertions(+), 28 deletions(-) diff --git a/site/src/api/queries/appearance.ts b/site/src/api/queries/appearance.ts index 58433f658d40f..45a6c618c5dec 100644 --- a/site/src/api/queries/appearance.ts +++ b/site/src/api/queries/appearance.ts @@ -11,9 +11,8 @@ export const appearance = (): UseQueryOptions => { return { // We either have our initial data or should immediately // fetch and never again! - ...cachedQuery, + ...cachedQuery(initialAppearanceData), queryKey: ["appearance"], - initialData: initialAppearanceData, queryFn: () => API.getAppearance(), }; }; diff --git a/site/src/api/queries/buildInfo.ts b/site/src/api/queries/buildInfo.ts index 23d932eca4bc2..aeed3ecd3d02b 100644 --- a/site/src/api/queries/buildInfo.ts +++ b/site/src/api/queries/buildInfo.ts @@ -11,9 +11,8 @@ export const buildInfo = (): UseQueryOptions => { return { // We either have our initial data or should immediately // fetch and never again! - ...cachedQuery, + ...cachedQuery(initialBuildInfoData), queryKey: buildInfoKey, - initialData: initialBuildInfoData, queryFn: () => API.getBuildInfo(), }; }; diff --git a/site/src/api/queries/entitlements.ts b/site/src/api/queries/entitlements.ts index 76e411898295a..1a4c990fe2ad4 100644 --- a/site/src/api/queries/entitlements.ts +++ b/site/src/api/queries/entitlements.ts @@ -9,10 +9,9 @@ const entitlementsQueryKey = ["entitlements"] as const; export const entitlements = (): UseQueryOptions => { return { - ...cachedQuery, + ...cachedQuery(initialEntitlementsData), queryKey: entitlementsQueryKey, queryFn: () => API.getEntitlements(), - initialData: initialEntitlementsData, }; }; diff --git a/site/src/api/queries/experiments.ts b/site/src/api/queries/experiments.ts index fd08d8fa5b867..bd7f436f5b9e6 100644 --- a/site/src/api/queries/experiments.ts +++ b/site/src/api/queries/experiments.ts @@ -9,9 +9,8 @@ const experimentsKey = ["experiments"] as const; export const experiments = (): UseQueryOptions => { return { - ...cachedQuery, + ...cachedQuery(initialExperimentsData), queryKey: experimentsKey, - initialData: initialExperimentsData, queryFn: () => API.getExperiments(), } satisfies UseQueryOptions; }; diff --git a/site/src/api/queries/users.ts b/site/src/api/queries/users.ts index 0d92003acffb6..34609b47f5a20 100644 --- a/site/src/api/queries/users.ts +++ b/site/src/api/queries/users.ts @@ -130,11 +130,8 @@ export const me = (): UseQueryOptions & { queryKey: QueryKey; } => { return { - // We either have our initial data or should immediately - // fetch and never again! - ...cachedQuery, + ...cachedQuery(initialUserData), queryKey: meKey, - initialData: initialUserData, queryFn: API.getAuthenticatedUser, }; }; @@ -148,11 +145,8 @@ export function apiKey(): UseQueryOptions { export const hasFirstUser = (): UseQueryOptions => { return { - // If there is initial user data, we don't want to make - // this request. It's a waste! - ...cachedQuery, // This cannot be false otherwise it will not fetch! - initialData: typeof initialUserData !== "undefined" ? true : undefined, + ...cachedQuery(typeof initialUserData !== "undefined" ? true : undefined), queryKey: ["hasFirstUser"], queryFn: API.hasFirstUser, }; diff --git a/site/src/api/queries/util.ts b/site/src/api/queries/util.ts index b5002b1d5f293..d5eb591c1ffd2 100644 --- a/site/src/api/queries/util.ts +++ b/site/src/api/queries/util.ts @@ -1,13 +1,23 @@ +import type { UseQueryOptions } from "react-query"; + // cachedQuery allows the caller to only make a request // a single time, and use `initialData` if it is provided. // // This is particularly helpful for passing values injected // via metadata. We do this for the initial user fetch, buildinfo, // and a few others to reduce page load time. -export const cachedQuery = { - cacheTime: Infinity, - staleTime: Infinity, - refetchOnMount: false, - refetchOnReconnect: false, - refetchOnWindowFocus: false, -}; +export const cachedQuery = (initialData?: T): Partial> => + // Only do this if there is initial data, + // otherwise it can conflict with tests. + initialData + ? { + cacheTime: Infinity, + staleTime: Infinity, + refetchOnMount: false, + refetchOnReconnect: false, + refetchOnWindowFocus: false, + initialData, + } + : { + initialData, + }; diff --git a/site/src/contexts/ProxyContext.tsx b/site/src/contexts/ProxyContext.tsx index 18deed1c306a2..ff99d7b03c41d 100644 --- a/site/src/contexts/ProxyContext.tsx +++ b/site/src/contexts/ProxyContext.tsx @@ -7,7 +7,7 @@ import { useEffect, useState, } from "react"; -import { useQuery } from "react-query"; +import { type UseQueryOptions, useQuery } from "react-query"; import { getWorkspaceProxies, getWorkspaceProxyRegions } from "api/api"; import { cachedQuery } from "api/queries/util"; import type { Region, WorkspaceProxy } from "api/typesGenerated"; @@ -132,12 +132,10 @@ export const ProxyProvider: FC = ({ children }) => { isLoading: proxiesLoading, isFetched: proxiesFetched, } = useQuery({ - ...cachedQuery, + ...cachedQuery(initialData), queryKey, queryFn: query, - staleTime: initialData ? Infinity : undefined, - initialData, - }); + } as UseQueryOptions); // Every time we get a new proxiesResponse, update the latency check // to each workspace proxy.