chore: reduce dashboard requests from seeded data (#13034)

* 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.

* Fix first user fetch

* Add util

* Add cached query for entitlements and experiments

* Fix authmethods unnecessary request

* Fix unnecessary region request

* Fix fmt

* Debug

* Fix test
This commit is contained in:
Kyle Carberry
2024-04-22 16:07:56 -04:00
committed by GitHub
parent 8d1220e0c8
commit d3f3ace220
11 changed files with 65 additions and 31 deletions
+4 -1
View File
@@ -2,14 +2,17 @@ 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<AppearanceConfig>("appearance");
const appearanceConfigKey = ["appearance"] as const;
export const appearance = (): UseQueryOptions<AppearanceConfig> => {
return {
// We either have our initial data or should immediately
// fetch and never again!
...cachedQuery(initialAppearanceData),
queryKey: ["appearance"],
initialData: initialAppearanceData,
queryFn: () => API.getAppearance(),
};
};
+4 -1
View File
@@ -2,14 +2,17 @@ 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<BuildInfoResponse>("build-info");
const buildInfoKey = ["buildInfo"] as const;
export const buildInfo = (): UseQueryOptions<BuildInfoResponse> => {
return {
// We either have our initial data or should immediately
// fetch and never again!
...cachedQuery(initialBuildInfoData),
queryKey: buildInfoKey,
initialData: initialBuildInfoData,
queryFn: () => API.getBuildInfo(),
};
};
+5 -4
View File
@@ -2,15 +2,16 @@ 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>("entitlements");
const ENTITLEMENTS_QUERY_KEY = ["entitlements"] as const;
const entitlementsQueryKey = ["entitlements"] as const;
export const entitlements = (): UseQueryOptions<Entitlements> => {
return {
queryKey: ENTITLEMENTS_QUERY_KEY,
...cachedQuery(initialEntitlementsData),
queryKey: entitlementsQueryKey,
queryFn: () => API.getEntitlements(),
initialData: initialEntitlementsData,
};
};
@@ -19,7 +20,7 @@ export const refreshEntitlements = (queryClient: QueryClient) => {
mutationFn: API.refreshEntitlements,
onSuccess: async () => {
await queryClient.invalidateQueries({
queryKey: ENTITLEMENTS_QUERY_KEY,
queryKey: entitlementsQueryKey,
});
},
};
+2 -1
View File
@@ -2,14 +2,15 @@ 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>("experiments");
const experimentsKey = ["experiments"] as const;
export const experiments = (): UseQueryOptions<Experiments> => {
return {
...cachedQuery(initialExperimentsData),
queryKey: experimentsKey,
initialData: initialExperimentsData,
queryFn: () => API.getExperiments(),
} satisfies UseQueryOptions<Experiments>;
};
+7 -4
View File
@@ -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;
@@ -112,6 +113,8 @@ export const updateRoles = (queryClient: QueryClient) => {
};
};
const initialUserData = getMetadataAsJSON<User>("user");
export const authMethods = () => {
return {
// Even the endpoint being /users/authmethods we don't want to revalidate it
@@ -121,16 +124,14 @@ export const authMethods = () => {
};
};
const initialUserData = getMetadataAsJSON<User>("user");
const meKey = ["me"];
export const me = (): UseQueryOptions<User> & {
queryKey: QueryKey;
} => {
return {
...cachedQuery(initialUserData),
queryKey: meKey,
initialData: initialUserData,
queryFn: API.getAuthenticatedUser,
};
};
@@ -142,8 +143,10 @@ export function apiKey(): UseQueryOptions<GenerateAPIKeyResponse> {
};
}
export const hasFirstUser = () => {
export const hasFirstUser = (): UseQueryOptions<boolean> => {
return {
// This cannot be false otherwise it will not fetch!
...cachedQuery(typeof initialUserData !== "undefined" ? true : undefined),
queryKey: ["hasFirstUser"],
queryFn: API.hasFirstUser,
};
+23
View File
@@ -0,0 +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 = <T>(initialData?: T): Partial<UseQueryOptions<T>> =>
// 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,
};