Skip to content

Commit 601cdfa

Browse files
authored
chore: get rid of usePrefetchedApiQuery, apiQueryClient, and useApiQueryClient (#2967)
* use apiq for prefetched queries * delete the now unused stuff
1 parent 1f2019c commit 601cdfa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+517
-698
lines changed

app/api/client.ts

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ import {
1919
getApiQueryOptionsErrorsAllowed,
2020
getListQueryOptionsFn,
2121
getUseApiMutation,
22-
getUsePrefetchedApiQuery,
23-
wrapQueryClient,
2422
} from './hooks'
2523

2624
export const api = new Api({
@@ -55,15 +53,14 @@ export const apiqErrorsAllowed = getApiQueryOptionsErrorsAllowed(api.methods)
5553
* `useQueryTable`.
5654
*/
5755
export const getListQFn = getListQueryOptionsFn(api.methods)
56+
export const useApiMutation = getUseApiMutation(api.methods)
57+
5858
/**
59-
* Same as `useApiQuery`, except we use `invariant(data)` to ensure the data is
59+
* Same as `useQuery`, except we use `invariant(data)` to ensure the data is
6060
* already there in the cache at request time, which means it has been
6161
* prefetched in a loader. Whenever this hook is used, there should be an e2e
6262
* test loading the page to exercise the invariant in CI.
6363
*/
64-
export const usePrefetchedApiQuery = getUsePrefetchedApiQuery(api.methods)
65-
export const useApiMutation = getUseApiMutation(api.methods)
66-
6764
export const usePrefetchedQuery = <TData>(options: UseQueryOptions<TData, ApiError>) =>
6865
ensurePrefetched(useQuery(options), options.queryKey)
6966

@@ -96,11 +93,3 @@ export const queryClient = new QueryClient({
9693
},
9794
},
9895
})
99-
100-
// to be used in loaders, which are outside the component tree and therefore
101-
// don't have access to context
102-
export const apiQueryClient = wrapQueryClient(api.methods, queryClient)
103-
104-
// used to retrieve the typed query client in components. doesn't need to exist:
105-
// we could import apiQueryClient directly everywhere, but the change is noisy
106-
export const useApiQueryClient = () => apiQueryClient

app/api/hooks.ts

Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@ import {
99
hashKey,
1010
queryOptions,
1111
useMutation,
12-
useQuery,
13-
type FetchQueryOptions,
14-
type InvalidateQueryFilters,
15-
type QueryClient,
1612
type QueryKey,
1713
type UseMutationOptions,
1814
type UseQueryOptions,
@@ -89,15 +85,6 @@ type UseQueryOtherOptions<T> = Omit<
8985
'queryKey' | 'queryFn' | 'initialData'
9086
>
9187

92-
/**
93-
* `queryKey` and `queryFn` are always constructed by our helper hooks, so we
94-
* only allow the rest of the options.
95-
*/
96-
type FetchQueryOtherOptions<T> = Omit<
97-
FetchQueryOptions<T, ApiError>,
98-
'queryKey' | 'queryFn'
99-
>
100-
10188
export const getApiQueryOptions =
10289
<A extends ApiClient>(api: A) =>
10390
<M extends string & keyof A>(
@@ -177,17 +164,6 @@ export const getListQueryOptionsFn =
177164
}
178165
}
179166

180-
export const getUsePrefetchedApiQuery =
181-
<A extends ApiClient>(api: A) =>
182-
<M extends string & keyof A>(
183-
method: M,
184-
params: Params<A[M]>,
185-
options: UseQueryOtherOptions<Result<A[M]>> = {}
186-
) => {
187-
const qOptions = getApiQueryOptions(api)(method, params, options)
188-
return ensurePrefetched(useQuery(qOptions), qOptions.queryKey)
189-
}
190-
191167
const prefetchError = (key?: QueryKey) =>
192168
`Expected query to be prefetched.
193169
Key: ${key ? hashKey(key) : '<unknown>'}
@@ -257,47 +233,6 @@ export const getUseApiMutation =
257233
...options,
258234
})
259235

260-
export const wrapQueryClient = <A extends ApiClient>(api: A, queryClient: QueryClient) => ({
261-
/**
262-
* Note that we only take a single argument, `method`, rather than allowing
263-
* the full query key `[query, params]` to be specified. This is to avoid
264-
* accidentally overspecifying and therefore failing to match the desired
265-
* query. The params argument can be added back in if we ever have a use case
266-
* for it.
267-
*
268-
* Passing no arguments will invalidate all queries.
269-
*/
270-
invalidateQueries: <M extends keyof A>(method?: M, filters?: InvalidateQueryFilters) =>
271-
queryClient.invalidateQueries(method ? { queryKey: [method], ...filters } : undefined),
272-
setQueryData: <M extends keyof A>(method: M, params: Params<A[M]>, data: Result<A[M]>) =>
273-
queryClient.setQueryData([method, params], data),
274-
setQueryDataErrorsAllowed: <M extends keyof A>(
275-
method: M,
276-
params: Params<A[M]>,
277-
data: ErrorsAllowed<Result<A[M]>, ApiError>
278-
) => queryClient.setQueryData([method, params, ERRORS_ALLOWED], data),
279-
fetchQuery: <M extends string & keyof A>(
280-
method: M,
281-
params: Params<A[M]>,
282-
options: FetchQueryOtherOptions<Result<A[M]>> = {}
283-
) =>
284-
queryClient.fetchQuery({
285-
queryKey: [method, params],
286-
queryFn: () => api[method](params).then(handleResult(method)),
287-
...options,
288-
}),
289-
prefetchQuery: <M extends string & keyof A>(
290-
method: M,
291-
params: Params<A[M]>,
292-
options: FetchQueryOtherOptions<Result<A[M]>> = {}
293-
) =>
294-
queryClient.prefetchQuery({
295-
queryKey: [method, params],
296-
queryFn: () => api[method](params).then(handleResult(method)),
297-
...options,
298-
}),
299-
})
300-
301236
/*
302237
1. what's up with [method, params]?
303238

app/api/roles.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { useMemo } from 'react'
1515
import * as R from 'remeda'
1616

1717
import type { FleetRole, IdentityType, ProjectRole, SiloRole } from './__generated__/Api'
18-
import { usePrefetchedApiQuery } from './client'
18+
import { apiq, usePrefetchedQuery } from './client'
1919

2020
/**
2121
* Union of all the specific roles, which are all the same, which makes making
@@ -98,8 +98,8 @@ export function useUserRows(
9898
): UserAccessRow[] {
9999
// HACK: because the policy has no names, we are fetching ~all the users,
100100
// putting them in a dictionary, and adding the names to the rows
101-
const { data: users } = usePrefetchedApiQuery('userList', {})
102-
const { data: groups } = usePrefetchedApiQuery('groupList', {})
101+
const { data: users } = usePrefetchedQuery(apiq('userList', {}))
102+
const { data: groups } = usePrefetchedQuery(apiq('groupList', {}))
103103
return useMemo(() => {
104104
const userItems = users?.items || []
105105
const groupItems = groups?.items || []
@@ -137,8 +137,8 @@ export type Actor = {
137137
* the given policy.
138138
*/
139139
export function useActorsNotInPolicy(policy: Policy): Actor[] {
140-
const { data: users } = usePrefetchedApiQuery('userList', {})
141-
const { data: groups } = usePrefetchedApiQuery('groupList', {})
140+
const { data: users } = usePrefetchedQuery(apiq('userList', {}))
141+
const { data: groups } = usePrefetchedQuery(apiq('groupList', {}))
142142
return useMemo(() => {
143143
// IDs are UUIDs, so no need to include identity type in set value to disambiguate
144144
const actorsInPolicy = new Set(policy?.roleAssignments.map((ra) => ra.identityId) || [])

app/components/AttachEphemeralIpModal.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import { useMemo } from 'react'
1010
import { useForm } from 'react-hook-form'
1111

12-
import { useApiMutation, useApiQueryClient, usePrefetchedApiQuery } from '~/api'
12+
import { apiq, queryClient, useApiMutation, usePrefetchedQuery } from '~/api'
1313
import { ListboxField } from '~/components/form/fields/ListboxField'
1414
import { HL } from '~/components/HL'
1515
import { useInstanceSelector } from '~/hooks/use-params'
@@ -20,18 +20,17 @@ import { ALL_ISH } from '~/util/consts'
2020
import { toIpPoolItem } from './form/fields/ip-pool-item'
2121

2222
export const AttachEphemeralIpModal = ({ onDismiss }: { onDismiss: () => void }) => {
23-
const queryClient = useApiQueryClient()
2423
const { project, instance } = useInstanceSelector()
25-
const { data: siloPools } = usePrefetchedApiQuery('projectIpPoolList', {
26-
query: { limit: ALL_ISH },
27-
})
24+
const { data: siloPools } = usePrefetchedQuery(
25+
apiq('projectIpPoolList', { query: { limit: ALL_ISH } })
26+
)
2827
const defaultPool = useMemo(
2928
() => siloPools?.items.find((pool) => pool.isDefault),
3029
[siloPools]
3130
)
3231
const instanceEphemeralIpAttach = useApiMutation('instanceEphemeralIpAttach', {
3332
onSuccess(ephemeralIp) {
34-
queryClient.invalidateQueries('instanceExternalIpList')
33+
queryClient.invalidateEndpoint('instanceExternalIpList')
3534
addToast(<>IP <HL>{ephemeralIp.ip}</HL> attached</>) // prettier-ignore
3635
onDismiss()
3736
},

app/components/AttachFloatingIpModal.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import { useForm } from 'react-hook-form'
1111

1212
import {
1313
apiqErrorsAllowed,
14+
queryClient,
1415
useApiMutation,
15-
useApiQueryClient,
1616
type FloatingIp,
1717
type Instance,
1818
} from '~/api'
@@ -67,11 +67,10 @@ export const AttachFloatingIpModal = ({
6767
instance: Instance
6868
onDismiss: () => void
6969
}) => {
70-
const queryClient = useApiQueryClient()
7170
const floatingIpAttach = useApiMutation('floatingIpAttach', {
7271
onSuccess(floatingIp) {
73-
queryClient.invalidateQueries('floatingIpList')
74-
queryClient.invalidateQueries('instanceExternalIpList')
72+
queryClient.invalidateEndpoint('floatingIpList')
73+
queryClient.invalidateEndpoint('instanceExternalIpList')
7574
addToast(<>IP <HL>{floatingIp.name}</HL> attached</>) // prettier-ignore
7675
onDismiss()
7776
},

app/components/form/fields/SshKeysField.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import { useState } from 'react'
99
import { useController, type Control } from 'react-hook-form'
1010

11-
import { usePrefetchedApiQuery } from '@oxide/api'
11+
import { apiq, usePrefetchedQuery } from '@oxide/api'
1212
import { Key16Icon } from '@oxide/design-system/icons/react'
1313

1414
import type { InstanceCreateInput } from '~/forms/instance-create'
@@ -55,7 +55,7 @@ export function SshKeysField({
5555
control: Control<InstanceCreateInput>
5656
isSubmitting: boolean
5757
}) {
58-
const keys = usePrefetchedApiQuery('currentUserSshKeyList', {}).data?.items || []
58+
const keys = usePrefetchedQuery(apiq('currentUserSshKeyList', {})).data?.items || []
5959
const [showAddSshKey, setShowAddSshKey] = useState(false)
6060

6161
const {

app/forms/disk-create.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import { useController, useForm, type Control } from 'react-hook-form'
1212

1313
import {
1414
apiq,
15+
queryClient,
1516
useApiMutation,
16-
useApiQueryClient,
1717
type BlockSize,
1818
type Disk,
1919
type DiskCreate,
@@ -70,11 +70,9 @@ export function CreateDiskSideModalForm({
7070
onDismiss,
7171
unavailableDiskNames = [],
7272
}: CreateSideModalFormProps) {
73-
const queryClient = useApiQueryClient()
74-
7573
const createDisk = useApiMutation('diskCreate', {
7674
onSuccess(data) {
77-
queryClient.invalidateQueries('diskList')
75+
queryClient.invalidateEndpoint('diskList')
7876
addToast(<>Disk <HL>{data.name}</HL> created</>) // prettier-ignore
7977
onSuccess?.(data)
8078
onDismiss()

app/forms/firewall-rules-common.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import { useController, useForm, type Control } from 'react-hook-form'
1212
import { Badge } from '@oxide/design-system/ui'
1313

1414
import {
15-
usePrefetchedApiQuery,
15+
apiq,
16+
usePrefetchedQuery,
1617
type ApiError,
1718
type Instance,
1819
type Vpc,
@@ -109,13 +110,13 @@ const TargetAndHostFilterSubform = ({
109110
// prefetchedApiQueries below are prefetched in firewall-rules-create and -edit
110111
const {
111112
data: { items: instances },
112-
} = usePrefetchedApiQuery('instanceList', { query: { project, limit: ALL_ISH } })
113+
} = usePrefetchedQuery(apiq('instanceList', { query: { project, limit: ALL_ISH } }))
113114
const {
114115
data: { items: vpcs },
115-
} = usePrefetchedApiQuery('vpcList', { query: { project, limit: ALL_ISH } })
116+
} = usePrefetchedQuery(apiq('vpcList', { query: { project, limit: ALL_ISH } }))
116117
const {
117118
data: { items: vpcSubnets },
118-
} = usePrefetchedApiQuery('vpcSubnetList', { query: { project, vpc, limit: ALL_ISH } })
119+
} = usePrefetchedQuery(apiq('vpcSubnetList', { query: { project, vpc, limit: ALL_ISH } }))
119120

120121
const subform = useForm({ defaultValues: targetAndHostDefaultValues })
121122
const field = useController({ name: `${sectionType}s`, control }).field

app/forms/firewall-rules-create.tsx

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import { useForm } from 'react-hook-form'
99
import { useNavigate, useParams, type LoaderFunctionArgs } from 'react-router'
1010

1111
import {
12-
apiQueryClient,
12+
apiq,
1313
firewallRuleGetToPut,
14+
queryClient,
1415
useApiMutation,
15-
useApiQueryClient,
16-
usePrefetchedApiQuery,
16+
usePrefetchedQuery,
1717
type VpcFirewallRule,
1818
} from '@oxide/api'
1919

@@ -61,34 +61,33 @@ const ruleToValues = (rule: VpcFirewallRule): FirewallRuleValues => ({
6161
export async function clientLoader({ params }: LoaderFunctionArgs) {
6262
const { project, vpc } = getVpcSelector(params)
6363
await Promise.all([
64-
apiQueryClient.prefetchQuery('vpcFirewallRulesView', { query: { project, vpc } }),
65-
apiQueryClient.prefetchQuery('instanceList', { query: { project, limit: ALL_ISH } }),
66-
apiQueryClient.prefetchQuery('vpcList', { query: { project, limit: ALL_ISH } }),
67-
apiQueryClient.prefetchQuery('vpcSubnetList', {
68-
query: { project, vpc, limit: ALL_ISH },
69-
}),
64+
queryClient.prefetchQuery(apiq('vpcFirewallRulesView', { query: { project, vpc } })),
65+
queryClient.prefetchQuery(apiq('instanceList', { query: { project, limit: ALL_ISH } })),
66+
queryClient.prefetchQuery(apiq('vpcList', { query: { project, limit: ALL_ISH } })),
67+
queryClient.prefetchQuery(
68+
apiq('vpcSubnetList', { query: { project, vpc, limit: ALL_ISH } })
69+
),
7070
])
7171

7272
return null
7373
}
7474

7575
export default function CreateFirewallRuleForm() {
7676
const vpcSelector = useVpcSelector()
77-
const queryClient = useApiQueryClient()
7877

7978
const navigate = useNavigate()
8079
const onDismiss = () => navigate(pb.vpcFirewallRules(vpcSelector))
8180

8281
const updateRules = useApiMutation('vpcFirewallRulesUpdate', {
8382
onSuccess(updatedRules) {
8483
const newRule = updatedRules.rules[updatedRules.rules.length - 1]
85-
queryClient.invalidateQueries('vpcFirewallRulesView')
84+
queryClient.invalidateEndpoint('vpcFirewallRulesView')
8685
addToast(<>Firewall rule <HL>{newRule.name}</HL> created</>) // prettier-ignore
8786
navigate(pb.vpcFirewallRules(vpcSelector))
8887
},
8988
})
9089

91-
const { data } = usePrefetchedApiQuery('vpcFirewallRulesView', { query: vpcSelector })
90+
const { data } = usePrefetchedQuery(apiq('vpcFirewallRulesView', { query: vpcSelector }))
9291
const existingRules = data.rules
9392

9493
// The :rule path param is optional. If it is present, we are creating a

0 commit comments

Comments
 (0)