Thanks to visit codestin.com
Credit goes to github.com

Skip to content

chore: reduce dashboard requests from seeded data #13034

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion site/src/api/queries/appearance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
};
};
Expand Down
5 changes: 4 additions & 1 deletion site/src/api/queries/buildInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
};
};
9 changes: 5 additions & 4 deletions site/src/api/queries/entitlements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
};

Expand All @@ -19,7 +20,7 @@ export const refreshEntitlements = (queryClient: QueryClient) => {
mutationFn: API.refreshEntitlements,
onSuccess: async () => {
await queryClient.invalidateQueries({
queryKey: ENTITLEMENTS_QUERY_KEY,
queryKey: entitlementsQueryKey,
});
},
};
Expand Down
3 changes: 2 additions & 1 deletion site/src/api/queries/experiments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
};
Expand Down
11 changes: 7 additions & 4 deletions site/src/api/queries/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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,
};
};
Expand All @@ -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,
};
Expand Down
23 changes: 23 additions & 0 deletions site/src/api/queries/util.ts
Original file line number Diff line number Diff line change
@@ -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,
};
8 changes: 4 additions & 4 deletions site/src/contexts/ProxyContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ 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";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { type ProxyLatencyReport, useProxyLatency } from "./useProxyLatency";
Expand Down Expand Up @@ -131,11 +132,10 @@ export const ProxyProvider: FC<PropsWithChildren> = ({ children }) => {
isLoading: proxiesLoading,
isFetched: proxiesFetched,
} = useQuery({
...cachedQuery(initialData),
queryKey,
queryFn: query,
staleTime: initialData ? Infinity : undefined,
initialData,
});
} as UseQueryOptions<readonly Region[]>);

// Every time we get a new proxiesResponse, update the latency check
// to each workspace proxy.
Expand Down
11 changes: 1 addition & 10 deletions site/src/contexts/auth/AuthProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -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;
Expand All @@ -51,7 +45,6 @@ export const AuthProvider: FC<PropsWithChildren> = ({ 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 }),
Expand All @@ -77,7 +70,6 @@ export const AuthProvider: FC<PropsWithChildren> = ({ children }) => {
userQuery.error.response.status === 401;
const isSigningOut = logoutMutation.isLoading;
const isLoading =
authMethodsQuery.isLoading ||
userQuery.isLoading ||
hasFirstUserQuery.isLoading ||
(userQuery.isSuccess && permissionsQuery.isLoading);
Expand Down Expand Up @@ -120,7 +112,6 @@ export const AuthProvider: FC<PropsWithChildren> = ({ 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],
Expand Down
7 changes: 7 additions & 0 deletions site/src/modules/dashboard/Navbar/NavbarView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,13 @@ const ProxyMenu: FC<ProxyMenuProps> = ({ 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 (
<Skeleton
Expand Down
8 changes: 5 additions & 3 deletions site/src/pages/LoginPage/LoginPage.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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();
Expand Down Expand Up @@ -60,9 +62,9 @@ export const LoginPage: FC = () => {
<title>Sign in to {applicationName}</title>
</Helmet>
<LoginPageView
authMethods={authMethods}
authMethods={authMethodsQuery.data}
error={signInError}
isLoading={isLoading}
isLoading={isLoading || authMethodsQuery.isLoading}
isSigningIn={isSigningIn}
onSignIn={async ({ email, password }) => {
await signIn(email, password);
Expand Down
6 changes: 3 additions & 3 deletions site/src/utils/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
export const getMetadataAsJSON = <T extends NonNullable<unknown>>(
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 {
Expand Down