Skip to content

Commit

Permalink
cleanup: delete unused API helper things (#2577)
Browse files Browse the repository at this point in the history
cut a few lines by cleaning up types and shuffling things
  • Loading branch information
david-crespo authored Nov 23, 2024
1 parent 175d75b commit 653b572
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 68 deletions.
16 changes: 6 additions & 10 deletions app/api/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,21 @@
*
* Copyright Oxide Computer Company
*/
import { QueryClient } from '@tanstack/react-query'
import { QueryClient, useQuery, type UseQueryOptions } from '@tanstack/react-query'

import { Api } from './__generated__/Api'
import { type ApiError } from './errors'
import {
ensurePrefetched,
getApiQueryOptions,
getListQueryOptionsFn,
getUseApiMutation,
getUseApiQueries,
getUseApiQuery,
getUseApiQueryErrorsAllowed,
getUsePrefetchedApiQuery,
wrapQueryClient,
} from './hooks'

export {
ensurePrefetched,
usePrefetchedQuery,
PAGE_SIZE,
type PaginatedQuery,
} from './hooks'

export const api = new Api({
// unit tests run in Node, whose fetch implementation requires a full URL
host: process.env.NODE_ENV === 'test' ? 'http://testhost' : '',
Expand All @@ -42,7 +36,6 @@ export const apiq = getApiQueryOptions(api.methods)
*/
export const getListQFn = getListQueryOptionsFn(api.methods)
export const useApiQuery = getUseApiQuery(api.methods)
export const useApiQueries = getUseApiQueries(api.methods)
/**
* Same as `useApiQuery`, except we use `invariant(data)` to ensure the data is
* already there in the cache at request time, which means it has been
Expand All @@ -53,6 +46,9 @@ export const usePrefetchedApiQuery = getUsePrefetchedApiQuery(api.methods)
export const useApiQueryErrorsAllowed = getUseApiQueryErrorsAllowed(api.methods)
export const useApiMutation = getUseApiMutation(api.methods)

export const usePrefetchedQuery = <TData>(options: UseQueryOptions<TData, ApiError>) =>
ensurePrefetched(useQuery(options), options.queryKey)

// Needs to be defined here instead of in app so we can use it to define
// `apiQueryClient`, which provides API-typed versions of QueryClient methods
export const queryClient = new QueryClient({
Expand Down
70 changes: 15 additions & 55 deletions app/api/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,11 @@ import {
hashKey,
queryOptions,
useMutation,
useQueries,
useQuery,
type DefaultError,
type FetchQueryOptions,
type InvalidateQueryFilters,
type QueryClient,
type QueryKey,
type UndefinedInitialDataOptions,
type UseMutationOptions,
type UseQueryOptions,
type UseQueryResult,
Expand All @@ -29,17 +26,12 @@ import { invariant } from '~/util/invariant'
import type { ApiResult } from './__generated__/Api'
import { processServerError, type ApiError } from './errors'
import { navToLogin } from './nav-to-login'
import { type ResultsPage } from './util'

/* eslint-disable @typescript-eslint/no-explicit-any */
export type Params<F> = F extends (p: infer P) => any ? P : never
export type Result<F> = F extends (p: any) => Promise<ApiResult<infer R>> ? R : never
export type ResultItem<F> =
Result<F> extends { items: (infer R)[] }
? R extends Record<string, unknown>
? R
: never
: never
type Params<F> = F extends (p: infer P) => any ? P : never
type Result<F> = F extends (p: any) => Promise<ApiResult<infer R>> ? R : never

export type ResultsPage<TItem> = { items: TItem[]; nextPage?: string }

type ApiClient = Record<string, (...args: any) => Promise<ApiResult<any>>>
/* eslint-enable @typescript-eslint/no-explicit-any */
Expand Down Expand Up @@ -92,17 +84,17 @@ Error message: ${error.message.replace(/\n/g, '\n' + ' '.repeat('Error message:
* `queryKey` and `queryFn` are always constructed by our helper hooks, so we
* only allow the rest of the options.
*/
type UseQueryOtherOptions<T, E = DefaultError> = Omit<
UndefinedInitialDataOptions<T, E>,
'queryKey' | 'queryFn'
type UseQueryOtherOptions<T> = Omit<
UseQueryOptions<T, ApiError>,
'queryKey' | 'queryFn' | 'initialData'
>

/**
* `queryKey` and `queryFn` are always constructed by our helper hooks, so we
* only allow the rest of the options.
*/
type FetchQueryOtherOptions<T, E = DefaultError> = Omit<
FetchQueryOptions<T, E>,
type FetchQueryOtherOptions<T> = Omit<
FetchQueryOptions<T, ApiError>,
'queryKey' | 'queryFn'
>

Expand All @@ -111,7 +103,7 @@ export const getApiQueryOptions =
<M extends string & keyof A>(
method: M,
params: Params<A[M]>,
options: UseQueryOtherOptions<Result<A[M]>, ApiError> = {}
options: UseQueryOtherOptions<Result<A[M]>> = {}
) =>
queryOptions({
queryKey: [method, params],
Expand Down Expand Up @@ -163,7 +155,7 @@ export const getListQueryOptionsFn =
>(
method: M,
params: Params<A[M]>,
options: UseQueryOtherOptions<Result<A[M]>, ApiError> = {}
options: UseQueryOtherOptions<Result<A[M]>> = {}
): PaginatedQuery<Result<A[M]>> => {
// We pull limit out of the query params rather than passing it in some
// other way so that there is exactly one way of specifying it. If we had
Expand All @@ -190,7 +182,7 @@ export const getUseApiQuery =
<M extends string & keyof A>(
method: M,
params: Params<A[M]>,
options: UseQueryOtherOptions<Result<A[M]>, ApiError> = {}
options: UseQueryOtherOptions<Result<A[M]>> = {}
) =>
useQuery(getApiQueryOptions(api)(method, params, options))

Expand All @@ -199,7 +191,7 @@ export const getUsePrefetchedApiQuery =
<M extends string & keyof A>(
method: M,
params: Params<A[M]>,
options: UseQueryOtherOptions<Result<A[M]>, ApiError> = {}
options: UseQueryOtherOptions<Result<A[M]>> = {}
) => {
const qOptions = getApiQueryOptions(api)(method, params, options)
return ensurePrefetched(useQuery(qOptions), qOptions.queryKey)
Expand Down Expand Up @@ -232,9 +224,6 @@ export function ensurePrefetched<TData, TError>(
return result as SetNonNullable<typeof result, 'data'>
}

export const usePrefetchedQuery = <TData>(options: UseQueryOptions<TData, ApiError>) =>
ensurePrefetched(useQuery(options), options.queryKey)

const ERRORS_ALLOWED = 'errors-allowed'

/** Result that includes both success and error so it can be cached by RQ */
Expand Down Expand Up @@ -289,35 +278,6 @@ export const getUseApiMutation =
...options,
})

/**
* Our version of `useQueries`, but with the key difference that all queries in
* a given call are using the same API method, and therefore all have the same
* request and response (`Params` and `Result`) types. Otherwise the types would
* be (perhaps literally) impossible.
*/
export const getUseApiQueries =
<A extends ApiClient>(api: A) =>
<M extends string & keyof A>(
method: M,
paramsArray: Params<A[M]>[],
options: UseQueryOtherOptions<Result<A[M]>, ApiError> = {}
) => {
return useQueries({
queries: paramsArray.map(
(params) =>
({
queryKey: [method, params],
queryFn: ({ signal }) =>
api[method](params, { signal }).then(handleResult(method)),
throwOnError: (err: ApiError) => err.statusCode === 404,
...options,
// Add params to the result for reassembly after the queries are returned
select: (data) => ({ ...data, params }),
}) satisfies UseQueryOptions<Result<A[M]> & { params: Params<A[M]> }, ApiError>
),
})
}

export const wrapQueryClient = <A extends ApiClient>(api: A, queryClient: QueryClient) => ({
/**
* Note that we only take a single argument, `method`, rather than allowing
Expand All @@ -340,7 +300,7 @@ export const wrapQueryClient = <A extends ApiClient>(api: A, queryClient: QueryC
fetchQuery: <M extends string & keyof A>(
method: M,
params: Params<A[M]>,
options: FetchQueryOtherOptions<Result<A[M]>, ApiError> = {}
options: FetchQueryOtherOptions<Result<A[M]>> = {}
) =>
queryClient.fetchQuery({
queryKey: [method, params],
Expand All @@ -350,7 +310,7 @@ export const wrapQueryClient = <A extends ApiClient>(api: A, queryClient: QueryC
prefetchQuery: <M extends string & keyof A>(
method: M,
params: Params<A[M]>,
options: FetchQueryOtherOptions<Result<A[M]>, ApiError> = {}
options: FetchQueryOtherOptions<Result<A[M]>> = {}
) =>
queryClient.prefetchQuery({
queryKey: [method, params],
Expand Down
2 changes: 1 addition & 1 deletion app/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ export type { ApiTypes }

export * as PathParams from './path-params'

export type { Params, Result, ResultItem } from './hooks'
export { ensurePrefetched, PAGE_SIZE, type PaginatedQuery, type ResultsPage } from './hooks'
export type { ApiError } from './errors'
export { navToLogin } from './nav-to-login'
2 changes: 0 additions & 2 deletions app/api/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ import type {
VpcFirewallRuleUpdate,
} from './__generated__/Api'

export type ResultsPage<TItem> = { items: TItem[]; nextPage?: string }

// API limits encoded in https://github.com/oxidecomputer/omicron/blob/main/nexus/src/app/mod.rs

export const MAX_NICS_PER_INSTANCE = 8
Expand Down

0 comments on commit 653b572

Please sign in to comment.