Skip to content

Commit 390418f

Browse files
committed
wip: switch codebase to use metadata hook
1 parent 81f2cd9 commit 390418f

File tree

10 files changed

+107
-92
lines changed

10 files changed

+107
-92
lines changed

site/src/api/queries/appearance.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
import type { QueryClient, UseQueryOptions } from "react-query";
1+
import type { QueryClient } from "react-query";
22
import * as API from "api/api";
33
import type { AppearanceConfig } from "api/typesGenerated";
4-
import { getMetadataAsJSON } from "utils/metadata";
4+
import type { MetadataState } from "hooks/useEmbeddedMetadata";
55
import { cachedQuery } from "./util";
66

7-
const initialAppearanceData = getMetadataAsJSON<AppearanceConfig>("appearance");
87
const appearanceConfigKey = ["appearance"] as const;
98

10-
export const appearance = (): UseQueryOptions<AppearanceConfig> => {
11-
// We either have our initial data or should immediately fetch and never again!
9+
export const appearance = (metadata: MetadataState<AppearanceConfig>) => {
1210
return cachedQuery({
13-
initialData: initialAppearanceData,
11+
metadata,
1412
queryKey: ["appearance"],
1513
queryFn: () => API.getAppearance(),
1614
});

site/src/api/queries/buildInfo.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
import type { UseQueryOptions } from "react-query";
21
import * as API from "api/api";
32
import type { BuildInfoResponse } from "api/typesGenerated";
4-
import { getMetadataAsJSON } from "utils/metadata";
3+
import type { MetadataState } from "hooks/useEmbeddedMetadata";
54
import { cachedQuery } from "./util";
65

7-
const initialBuildInfoData = getMetadataAsJSON<BuildInfoResponse>("build-info");
86
const buildInfoKey = ["buildInfo"] as const;
97

10-
export const buildInfo = (): UseQueryOptions<BuildInfoResponse> => {
8+
export const buildInfo = (metadata: MetadataState<BuildInfoResponse>) => {
119
// The version of the app can't change without reloading the page.
1210
return cachedQuery({
13-
initialData: initialBuildInfoData,
11+
metadata,
1412
queryKey: buildInfoKey,
1513
queryFn: () => API.getBuildInfo(),
1614
});

site/src/api/queries/entitlements.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
import type { QueryClient, UseQueryOptions } from "react-query";
1+
import type { QueryClient } from "react-query";
22
import * as API from "api/api";
33
import type { Entitlements } from "api/typesGenerated";
4-
import { getMetadataAsJSON } from "utils/metadata";
4+
import type { MetadataState } from "hooks/useEmbeddedMetadata";
55
import { cachedQuery } from "./util";
66

7-
const initialEntitlementsData = getMetadataAsJSON<Entitlements>("entitlements");
87
const entitlementsQueryKey = ["entitlements"] as const;
98

10-
export const entitlements = (): UseQueryOptions<Entitlements> => {
9+
export const entitlements = (metadata: MetadataState<Entitlements>) => {
1110
return cachedQuery({
12-
initialData: initialEntitlementsData,
11+
metadata,
1312
queryKey: entitlementsQueryKey,
1413
queryFn: () => API.getEntitlements(),
1514
});

site/src/api/queries/experiments.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
import type { UseQueryOptions } from "react-query";
21
import * as API from "api/api";
32
import type { Experiments } from "api/typesGenerated";
4-
import { getMetadataAsJSON } from "utils/metadata";
3+
import type { MetadataState } from "hooks/useEmbeddedMetadata";
54
import { cachedQuery } from "./util";
65

7-
const initialExperimentsData = getMetadataAsJSON<Experiments>("experiments");
86
const experimentsKey = ["experiments"] as const;
97

10-
export const experiments = (): UseQueryOptions<Experiments> => {
8+
export const experiments = (metadata: MetadataState<Experiments>) => {
119
return cachedQuery({
12-
initialData: initialExperimentsData,
10+
metadata,
1311
queryKey: experimentsKey,
1412
queryFn: () => API.getExperiments(),
1513
});

site/src/api/queries/users.ts

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type {
22
QueryClient,
3-
QueryKey,
43
UseMutationOptions,
54
UseQueryOptions,
65
} from "react-query";
@@ -15,9 +14,9 @@ import type {
1514
User,
1615
GenerateAPIKeyResponse,
1716
} from "api/typesGenerated";
17+
import type { MetadataState } from "hooks/useEmbeddedMetadata";
1818
import type { UsePaginatedQueryOptions } from "hooks/usePaginatedQuery";
1919
import { prepareQuery } from "utils/filters";
20-
import { getMetadataAsJSON } from "utils/metadata";
2120
import { getAuthorizationKey } from "./authCheck";
2221
import { cachedQuery } from "./util";
2322

@@ -113,8 +112,6 @@ export const updateRoles = (queryClient: QueryClient) => {
113112
};
114113
};
115114

116-
const initialUserData = getMetadataAsJSON<User>("user");
117-
118115
export const authMethods = () => {
119116
return {
120117
// Even the endpoint being /users/authmethods we don't want to revalidate it
@@ -126,11 +123,9 @@ export const authMethods = () => {
126123

127124
const meKey = ["me"];
128125

129-
export const me = (): UseQueryOptions<User> & {
130-
queryKey: QueryKey;
131-
} => {
126+
export const me = (metadata: MetadataState<User>) => {
132127
return cachedQuery({
133-
initialData: initialUserData,
128+
metadata,
134129
queryKey: meKey,
135130
queryFn: API.getAuthenticatedUser,
136131
});
@@ -143,10 +138,9 @@ export function apiKey(): UseQueryOptions<GenerateAPIKeyResponse> {
143138
};
144139
}
145140

146-
export const hasFirstUser = (): UseQueryOptions<boolean> => {
141+
export const hasFirstUser = (userMetadata: MetadataState<User>) => {
147142
return cachedQuery({
148-
// This cannot be false otherwise it will not fetch!
149-
initialData: Boolean(initialUserData) || undefined,
143+
metadata: userMetadata,
150144
queryKey: ["hasFirstUser"],
151145
queryFn: API.hasFirstUser,
152146
});

site/src/api/queries/util.ts

Lines changed: 65 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,73 @@
1-
import type { UseQueryOptions } from "react-query";
1+
import type { UseQueryOptions, QueryKey } from "react-query";
2+
import type { MetadataState, MetadataValue } from "hooks/useEmbeddedMetadata";
3+
4+
const disabledFetchOptions = {
5+
cacheTime: Infinity,
6+
staleTime: Infinity,
7+
refetchOnMount: false,
8+
refetchOnReconnect: false,
9+
refetchOnWindowFocus: false,
10+
} as const satisfies UseQueryOptions;
11+
12+
type UseQueryOptionsWithMetadata<
13+
TMetadata extends MetadataValue = MetadataValue,
14+
TQueryFnData = unknown,
15+
TError = unknown,
16+
TData = TQueryFnData,
17+
TQueryKey extends QueryKey = QueryKey,
18+
> = Omit<
19+
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
20+
"initialData"
21+
> & {
22+
metadata: MetadataState<TMetadata>;
23+
};
24+
25+
type FormattedQueryOptionsResult<
26+
TQueryFnData = unknown,
27+
TError = unknown,
28+
TData = TQueryFnData,
29+
TQueryKey extends QueryKey = QueryKey,
30+
> = Omit<
31+
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
32+
"initialData"
33+
> & {
34+
queryKey: NonNullable<TQueryKey>;
35+
};
236

337
/**
438
* cachedQuery allows the caller to only make a request a single time, and use
539
* `initialData` if it is provided. This is particularly helpful for passing
640
* values injected via metadata. We do this for the initial user fetch,
741
* buildinfo, and a few others to reduce page load time.
842
*/
9-
export const cachedQuery = <
10-
TQueryOptions extends UseQueryOptions<TData>,
11-
TData,
43+
export function cachedQuery<
44+
TMetadata extends MetadataValue = MetadataValue,
45+
TQueryFnData = unknown,
46+
TError = unknown,
47+
TData = TQueryFnData,
48+
TQueryKey extends QueryKey = QueryKey,
1249
>(
13-
options: TQueryOptions,
14-
): TQueryOptions =>
15-
// Only do this if there is initial data, otherwise it can conflict with tests.
16-
({
17-
...(options.initialData
18-
? {
19-
cacheTime: Infinity,
20-
staleTime: Infinity,
21-
refetchOnMount: false,
22-
refetchOnReconnect: false,
23-
refetchOnWindowFocus: false,
24-
}
25-
: {}),
26-
...options,
27-
});
50+
options: UseQueryOptionsWithMetadata<
51+
TMetadata,
52+
TQueryFnData,
53+
TError,
54+
TData,
55+
TQueryKey
56+
>,
57+
): FormattedQueryOptionsResult<TQueryFnData, TError, TData, TQueryKey> {
58+
const { metadata, ...delegatedOptions } = options;
59+
const metadataIsAvailable = metadata.status === "loaded";
60+
61+
const newOptions = {
62+
...delegatedOptions,
63+
...(metadataIsAvailable ? disabledFetchOptions : {}),
64+
initialData: metadataIsAvailable ? metadata.value : undefined,
65+
};
66+
67+
return newOptions as FormattedQueryOptionsResult<
68+
TQueryFnData,
69+
TError,
70+
TData,
71+
TQueryKey
72+
>;
73+
}

site/src/contexts/auth/AuthProvider.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
} from "api/queries/users";
1818
import type { UpdateUserProfileRequest, User } from "api/typesGenerated";
1919
import { displaySuccess } from "components/GlobalSnackbar/utils";
20+
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
2021
import { permissionsToCheck, type Permissions } from "./permissions";
2122

2223
export type AuthContextValue = {
@@ -42,19 +43,28 @@ export const AuthContext = createContext<AuthContextValue | undefined>(
4243
);
4344

4445
export const AuthProvider: FC<PropsWithChildren> = ({ children }) => {
45-
const queryClient = useQueryClient();
46-
const meOptions = me();
46+
const { metadata, clearMetadataByKey } = useEmbeddedMetadata();
47+
const userMetadataState = metadata.user;
48+
49+
const meOptions = me(userMetadataState);
4750
const userQuery = useQuery(meOptions);
48-
const hasFirstUserQuery = useQuery(hasFirstUser());
51+
const hasFirstUserQuery = useQuery(hasFirstUser(userMetadataState));
52+
4953
const permissionsQuery = useQuery({
5054
...checkAuthorization({ checks: permissionsToCheck }),
5155
enabled: userQuery.data !== undefined,
5256
});
5357

58+
const queryClient = useQueryClient();
5459
const loginMutation = useMutation(
5560
login({ checks: permissionsToCheck }, queryClient),
5661
);
57-
const logoutMutation = useMutation(logout(queryClient));
62+
63+
const logoutMutation = useMutation({
64+
...logout(queryClient),
65+
onSuccess: () => clearMetadataByKey("user"),
66+
});
67+
5868
const updateProfileMutation = useMutation({
5969
...updateProfileOptions("me"),
6070

site/src/hooks/useEmbeddedMetadata.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ type AvailableMetadata = Readonly<{
2626
}>;
2727

2828
type MetadataKey = keyof AvailableMetadata;
29-
type MetadataValue = AvailableMetadata[MetadataKey];
29+
export type MetadataValue = AvailableMetadata[MetadataKey];
3030

3131
export type MetadataState<T extends MetadataValue> = Readonly<{
3232
// undefined chosen to signify missing value because unlike null, it isn't a
3333
// valid JSON-serializable value. It's impossible to be returned by the API
3434
value: T | undefined;
35-
status: "missing" | "loadedOnMount" | "deleted";
35+
status: "missing" | "loaded" | "deleted";
3636
}>;
3737

3838
export type RuntimeHtmlMetadata = Readonly<{
@@ -91,15 +91,9 @@ export class MetadataManager implements MetadataManagerApi {
9191

9292
let newEntry: MetadataState<T>;
9393
if (!node || value === undefined) {
94-
newEntry = {
95-
value: undefined,
96-
status: "missing",
97-
};
94+
newEntry = { value: undefined, status: "missing" };
9895
} else {
99-
newEntry = {
100-
value,
101-
status: "loadedOnMount",
102-
};
96+
newEntry = { value, status: "loaded" };
10397
}
10498

10599
this.trackedMetadataNodes.set(key, node);

site/src/modules/dashboard/DashboardProvider.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type {
1616
} from "api/typesGenerated";
1717
import { displayError } from "components/GlobalSnackbar/utils";
1818
import { Loader } from "components/Loader/Loader";
19+
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
1920
import { hslToHex, isHexColor, isHslColor } from "utils/colors";
2021

2122
interface Appearance {
@@ -35,9 +36,10 @@ export const DashboardContext = createContext<DashboardValue | undefined>(
3536
);
3637

3738
export const DashboardProvider: FC<PropsWithChildren> = ({ children }) => {
38-
const entitlementsQuery = useQuery(entitlements());
39-
const experimentsQuery = useQuery(experiments());
40-
const appearanceQuery = useQuery(appearance());
39+
const { metadata } = useEmbeddedMetadata();
40+
const entitlementsQuery = useQuery(entitlements(metadata.entitlements));
41+
const experimentsQuery = useQuery(experiments(metadata.experiments));
42+
const appearanceQuery = useQuery(appearance(metadata.appearance));
4143

4244
const isLoading =
4345
!entitlementsQuery.data || !appearanceQuery.data || !experimentsQuery.data;

site/src/utils/metadata.ts

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)