Skip to content

Commit 1f2019c

Browse files
authored
chore: Get rid of useApiQuery (#2966)
get rid of useApiQuery
1 parent 170bf35 commit 1f2019c

File tree

19 files changed

+120
-133
lines changed

19 files changed

+120
-133
lines changed

app/api/__tests__/hooks.spec.tsx

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@
55
*
66
* Copyright Oxide Computer Company
77
*/
8-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
8+
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query'
99
import { act, render, renderHook, waitFor } from '@testing-library/react'
1010
import { describe, expect, it, vi } from 'vitest'
1111

1212
import { project } from '@oxide/api-mocks'
1313

14-
import { useApiMutation, useApiQuery } from '..'
14+
import { apiq, useApiMutation } from '..'
1515
import type { DiskCreate } from '../__generated__/Api'
1616
import { overrideOnce } from '../../../test/unit/server'
1717

18+
// TODO: rethink whether these tests need to exist when they are so well-covered
19+
// by playwright tests
20+
1821
// because useApiQuery and useApiMutation are almost entirely typed wrappers
1922
// around React Query's useQuery and useMutation, these tests are mostly about
2023
// testing the one bit of real logic in there: error parsing
@@ -32,12 +35,12 @@ export function Wrapper({ children }: { children: React.ReactNode }) {
3235

3336
const config = { wrapper: Wrapper }
3437

35-
const renderProjectList = () => renderHook(() => useApiQuery('projectList', {}), config)
38+
const renderProjectList = () => renderHook(() => useQuery(apiq('projectList', {})), config)
3639

3740
// 503 is a special key in the MSW server that returns a 503
3841
const renderGetProject503 = () =>
3942
renderHook(
40-
() => useApiQuery('projectView', { path: { project: 'project-error-503' } }),
43+
() => useQuery(apiq('projectView', { path: { project: 'project-error-503' } })),
4144
config
4245
)
4346

@@ -117,7 +120,7 @@ describe('useApiQuery', () => {
117120
function BadApiCall() {
118121
try {
119122
// oxlint-disable-next-line react-hooks/rules-of-hooks
120-
useApiQuery('projectView', { path: { project: 'nonexistent' } })
123+
useQuery(apiq('projectView', { path: { project: 'nonexistent' } }))
121124
} catch (e) {
122125
onError(e)
123126
}
@@ -139,10 +142,12 @@ describe('useApiQuery', () => {
139142
it('default throw behavior can be overridden to use query error state', async () => {
140143
const { result } = renderHook(
141144
() =>
142-
useApiQuery(
143-
'projectView',
144-
{ path: { project: 'nonexistent' } },
145-
{ throwOnError: false } // <----- the point
145+
useQuery(
146+
apiq(
147+
'projectView',
148+
{ path: { project: 'nonexistent' } },
149+
{ throwOnError: false }
150+
)
146151
),
147152
config
148153
)

app/api/client.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import {
1919
getApiQueryOptionsErrorsAllowed,
2020
getListQueryOptionsFn,
2121
getUseApiMutation,
22-
getUseApiQuery,
2322
getUsePrefetchedApiQuery,
2423
wrapQueryClient,
2524
} from './hooks'
@@ -56,7 +55,6 @@ export const apiqErrorsAllowed = getApiQueryOptionsErrorsAllowed(api.methods)
5655
* `useQueryTable`.
5756
*/
5857
export const getListQFn = getListQueryOptionsFn(api.methods)
59-
export const useApiQuery = getUseApiQuery(api.methods)
6058
/**
6159
* Same as `useApiQuery`, except we use `invariant(data)` to ensure the data is
6260
* already there in the cache at request time, which means it has been

app/api/hooks.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -177,15 +177,6 @@ export const getListQueryOptionsFn =
177177
}
178178
}
179179

180-
export const getUseApiQuery =
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-
useQuery(getApiQueryOptions(api)(method, params, options))
188-
189180
export const getUsePrefetchedApiQuery =
190181
<A extends ApiClient>(api: A) =>
191182
<M extends string & keyof A>(

app/components/ExternalIps.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
* Copyright Oxide Computer Company
77
*/
88

9+
import { useQuery } from '@tanstack/react-query'
910
import { Link } from 'react-router'
1011
import * as R from 'remeda'
1112

12-
import { useApiQuery, type ExternalIp } from '@oxide/api'
13+
import { apiq, type ExternalIp } from '@oxide/api'
1314

1415
import { EmptyCell, SkeletonCell } from '~/table/cells/EmptyCell'
1516
import { CopyableIp } from '~/ui/lib/CopyableIp'
@@ -23,10 +24,9 @@ const IP_ORDER = { floating: 0, ephemeral: 1, snat: 2 } as const
2324
export const orderIps = (ips: ExternalIp[]) => R.sortBy(ips, (a) => IP_ORDER[a.kind])
2425

2526
export function ExternalIps({ project, instance }: PP.Instance) {
26-
const { data, isPending } = useApiQuery('instanceExternalIpList', {
27-
path: { instance },
28-
query: { project },
29-
})
27+
const { data, isPending } = useQuery(
28+
apiq('instanceExternalIpList', { path: { instance }, query: { project } })
29+
)
3030
if (isPending) return <SkeletonCell />
3131

3232
// Exclude SNAT IPs from the properties table because they are rarely going

app/components/SystemMetric.tsx

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,10 @@
55
*
66
* Copyright Oxide Computer Company
77
*/
8+
import { useQuery } from '@tanstack/react-query'
89
import { useMemo, useRef } from 'react'
910

10-
import {
11-
synthesizeData,
12-
useApiQuery,
13-
type ChartDatum,
14-
type SystemMetricName,
15-
} from '@oxide/api'
11+
import { apiq, synthesizeData, type ChartDatum, type SystemMetricName } from '@oxide/api'
1612

1713
import { ChartContainer, ChartHeader, TimeSeriesChart } from './TimeSeriesChart'
1814

@@ -53,23 +49,21 @@ export function SiloMetric({
5349
}: SiloMetricProps) {
5450
// TODO: we're only pulling the first page. Should we bump the cap to 10k?
5551
// Fetch multiple pages if 10k is not enough? That's a bit much.
56-
const inRange = useApiQuery(
57-
'siloMetric',
58-
{
59-
path: { metricName },
60-
query: { project, startTime, endTime, limit: 3000 },
61-
},
62-
{ placeholderData: (x) => x }
52+
const inRange = useQuery(
53+
apiq(
54+
'siloMetric',
55+
{ path: { metricName }, query: { project, startTime, endTime, limit: 3000 } },
56+
{ placeholderData: (x) => x }
57+
)
6358
)
6459

6560
// get last point before startTime to use as first point in graph
66-
const beforeStart = useApiQuery(
67-
'siloMetric',
68-
{
69-
path: { metricName },
70-
query: { project, endTime: startTime, ...staticParams },
71-
},
72-
{ placeholderData: (x) => x }
61+
const beforeStart = useQuery(
62+
apiq(
63+
'siloMetric',
64+
{ path: { metricName }, query: { project, endTime: startTime, ...staticParams } },
65+
{ placeholderData: (x) => x }
66+
)
7367
)
7468

7569
const ref = useRef<ChartDatum[] | undefined>(undefined)
@@ -124,23 +118,21 @@ export function SystemMetric({
124118
}: SystemMetricProps) {
125119
// TODO: we're only pulling the first page. Should we bump the cap to 10k?
126120
// Fetch multiple pages if 10k is not enough? That's a bit much.
127-
const inRange = useApiQuery(
128-
'systemMetric',
129-
{
130-
path: { metricName },
131-
query: { silo, startTime, endTime, limit: 3000 },
132-
},
133-
{ placeholderData: (x) => x }
121+
const inRange = useQuery(
122+
apiq(
123+
'systemMetric',
124+
{ path: { metricName }, query: { silo, startTime, endTime, limit: 3000 } },
125+
{ placeholderData: (x) => x }
126+
)
134127
)
135128

136129
// get last point before startTime to use as first point in graph
137-
const beforeStart = useApiQuery(
138-
'systemMetric',
139-
{
140-
path: { metricName },
141-
query: { silo, endTime: startTime, ...staticParams },
142-
},
143-
{ placeholderData: (x) => x }
130+
const beforeStart = useQuery(
131+
apiq(
132+
'systemMetric',
133+
{ path: { metricName }, query: { silo, endTime: startTime, ...staticParams } },
134+
{ placeholderData: (x) => x }
135+
)
144136
)
145137

146138
const ref = useRef<ChartDatum[] | undefined>(undefined)

app/components/form/fields/SubnetListbox.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
*
66
* Copyright Oxide Computer Company
77
*/
8+
import { useQuery } from '@tanstack/react-query'
89
import { useWatch, type FieldPath, type FieldValues } from 'react-hook-form'
910

10-
import { useApiQuery } from '@oxide/api'
11+
import { apiq } from '@oxide/api'
1112

1213
import { useProjectSelector } from '~/hooks/use-params'
1314

@@ -40,10 +41,12 @@ export function SubnetListbox<
4041

4142
// TODO: error handling other than fallback to empty list?
4243
const subnets =
43-
useApiQuery(
44-
'vpcSubnetList',
45-
{ query: { ...projectSelector, vpc: vpcName } },
46-
{ enabled: vpcExists, throwOnError: false }
44+
useQuery(
45+
apiq(
46+
'vpcSubnetList',
47+
{ query: { ...projectSelector, vpc: vpcName } },
48+
{ enabled: vpcExists, throwOnError: false }
49+
)
4750
).data?.items || []
4851

4952
return (

app/components/form/fields/useItemsList.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
* Copyright Oxide Computer Company
77
*/
88

9+
import { useQuery } from '@tanstack/react-query'
910
import { useMemo } from 'react'
1011

11-
import { useApiQuery } from '~/api'
12+
import { apiq } from '@oxide/api'
13+
1214
import { useVpcSelector } from '~/hooks/use-params'
1315

1416
/**
@@ -30,7 +32,7 @@ export function customRouterDataToForm(value: string | undefined | null): string
3032

3133
export const useCustomRouterItems = () => {
3234
const vpcSelector = useVpcSelector()
33-
const { data, isLoading } = useApiQuery('vpcRouterList', { query: vpcSelector })
35+
const { data, isLoading } = useQuery(apiq('vpcRouterList', { query: vpcSelector }))
3436

3537
const routerItems = useMemo(() => {
3638
const items = (data?.items || [])

app/forms/disk-attach.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
*
66
* Copyright Oxide Computer Company
77
*/
8+
import { useQuery } from '@tanstack/react-query'
89
import { useMemo } from 'react'
910
import { useForm } from 'react-hook-form'
1011

11-
import { useApiQuery, type ApiError } from '@oxide/api'
12+
import { apiq, type ApiError } from '@oxide/api'
1213

1314
import { ComboboxField } from '~/components/form/fields/ComboboxField'
1415
import { ModalForm } from '~/components/form/ModalForm'
@@ -40,9 +41,9 @@ export function AttachDiskModalForm({
4041
}: AttachDiskProps) {
4142
const { project } = useProjectSelector()
4243

43-
const { data, isPending } = useApiQuery('diskList', {
44-
query: { project, limit: ALL_ISH },
45-
})
44+
const { data, isPending } = useQuery(
45+
apiq('diskList', { query: { project, limit: ALL_ISH } })
46+
)
4647
const detachedDisks = useMemo(
4748
() =>
4849
toComboboxItems(

app/forms/disk-create.tsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
*
66
* Copyright Oxide Computer Company
77
*/
8+
import { useQuery } from '@tanstack/react-query'
89
import { filesize } from 'filesize'
910
import { useMemo } from 'react'
1011
import { useController, useForm, type Control } from 'react-hook-form'
1112

1213
import {
14+
apiq,
1315
useApiMutation,
14-
useApiQuery,
1516
useApiQueryClient,
1617
type BlockSize,
1718
type Disk,
@@ -82,8 +83,8 @@ export function CreateDiskSideModalForm({
8283

8384
const form = useForm({ defaultValues })
8485
const { project } = useProjectSelector()
85-
const projectImages = useApiQuery('imageList', { query: { project } })
86-
const siloImages = useApiQuery('imageList', {})
86+
const projectImages = useQuery(apiq('imageList', { query: { project } }))
87+
const siloImages = useQuery(apiq('imageList', {}))
8788

8889
// put project images first because if there are any, there probably aren't
8990
// very many and they're probably relevant
@@ -93,7 +94,7 @@ export function CreateDiskSideModalForm({
9394
)
9495
const areImagesLoading = projectImages.isPending || siloImages.isPending
9596

96-
const snapshotsQuery = useApiQuery('snapshotList', { query: { project } })
97+
const snapshotsQuery = useQuery(apiq('snapshotList', { query: { project } }))
9798
const snapshots = snapshotsQuery.data?.items || []
9899

99100
// validate disk source size
@@ -235,11 +236,8 @@ const DiskSourceField = ({
235236
}
236237

237238
const DiskNameFromId = ({ disk }: { disk: string }) => {
238-
const { data, isPending, isError } = useApiQuery(
239-
'diskView',
240-
{ path: { disk } },
241-
// this can 404 if the source disk has been deleted, and that's fine
242-
{ throwOnError: false }
239+
const { data, isPending, isError } = useQuery(
240+
apiq('diskView', { path: { disk } }, { throwOnError: false })
243241
)
244242

245243
if (isPending || isError) return null
@@ -248,7 +246,7 @@ const DiskNameFromId = ({ disk }: { disk: string }) => {
248246

249247
const SnapshotSelectField = ({ control }: { control: Control<DiskCreate> }) => {
250248
const { project } = useProjectSelector()
251-
const snapshotsQuery = useApiQuery('snapshotList', { query: { project } })
249+
const snapshotsQuery = useQuery(apiq('snapshotList', { query: { project } }))
252250

253251
const snapshots = snapshotsQuery.data?.items || []
254252
const diskSizeField = useController({ control, name: 'size' }).field

app/forms/floating-ip-create.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,12 @@
66
* Copyright Oxide Computer Company
77
*/
88
import * as Accordion from '@radix-ui/react-accordion'
9+
import { useQuery } from '@tanstack/react-query'
910
import { useState } from 'react'
1011
import { useForm } from 'react-hook-form'
1112
import { useNavigate } from 'react-router'
1213

13-
import {
14-
useApiMutation,
15-
useApiQuery,
16-
useApiQueryClient,
17-
type FloatingIpCreate,
18-
} from '@oxide/api'
14+
import { apiq, useApiMutation, useApiQueryClient, type FloatingIpCreate } from '@oxide/api'
1915

2016
import { AccordionItem } from '~/components/AccordionItem'
2117
import { DescriptionField } from '~/components/form/fields/DescriptionField'
@@ -42,7 +38,9 @@ export const handle = titleCrumb('New Floating IP')
4238
export default function CreateFloatingIpSideModalForm() {
4339
// Fetch 1000 to we can be sure to get them all. Don't bother prefetching
4440
// because the list is hidden under the Advanced accordion.
45-
const { data: allPools } = useApiQuery('projectIpPoolList', { query: { limit: ALL_ISH } })
41+
const { data: allPools } = useQuery(
42+
apiq('projectIpPoolList', { query: { limit: ALL_ISH } })
43+
)
4644

4745
const queryClient = useApiQueryClient()
4846
const projectSelector = useProjectSelector()

0 commit comments

Comments
 (0)