Skip to content

Commit 3bc7030

Browse files
committed
Merge branch 'main' into truncate_copyable_ip_address
2 parents c736082 + 1dd25f6 commit 3bc7030

Some content is hidden

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

66 files changed

+1671
-1049
lines changed

.github/workflows/lintBuildTest.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
id: cache-node-modules
2121
with:
2222
path: node_modules
23-
key: modules-${{ hashFiles('package-lock.json') }}
23+
key: modules-${{ hashFiles('package-lock.json', 'patches/**') }}
2424
- name: npm install
2525
if: steps.cache-node-modules.outputs.cache-hit != 'true'
2626
run: npm install
@@ -39,7 +39,7 @@ jobs:
3939
id: cache-node-modules
4040
with:
4141
path: node_modules
42-
key: modules-${{ hashFiles('package-lock.json') }}
42+
key: modules-${{ hashFiles('package-lock.json', 'patches/**') }}
4343
- name: Typecheck
4444
run: npx tsc
4545
- name: Lint
@@ -67,7 +67,7 @@ jobs:
6767
uses: actions/cache@v4
6868
with:
6969
path: node_modules
70-
key: modules-${{ hashFiles('package-lock.json') }}
70+
key: modules-${{ hashFiles('package-lock.json', 'patches/**') }}
7171
- name: Set env.PLAYWRIGHT_VERSION
7272
run: |
7373
PLAYWRIGHT_VERSION=$(npm ls --json @playwright/test | jq --raw-output '.dependencies["@playwright/test"].version')

app/api/client.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55
*
66
* Copyright Oxide Computer Company
77
*/
8-
import { QueryClient, useQuery, type UseQueryOptions } from '@tanstack/react-query'
8+
import {
9+
QueryClient as QueryClientOrig,
10+
useQuery,
11+
type UseQueryOptions,
12+
} from '@tanstack/react-query'
913

1014
import { Api } from './__generated__/Api'
1115
import { type ApiError } from './errors'
@@ -49,6 +53,24 @@ export const useApiMutation = getUseApiMutation(api.methods)
4953
export const usePrefetchedQuery = <TData>(options: UseQueryOptions<TData, ApiError>) =>
5054
ensurePrefetched(useQuery(options), options.queryKey)
5155

56+
/**
57+
* Extends React Query's `QueryClient` with a couple of API-specific methods.
58+
* Existing methods are never modified.
59+
*/
60+
class QueryClient extends QueryClientOrig {
61+
/**
62+
* Invalidate all cached queries for a given endpoint.
63+
*
64+
* Note that we only take a single argument, `method`, rather than allowing
65+
* the full query key `[query, params]` to be specified. This is to avoid
66+
* accidentally overspecifying and therefore failing to match the desired query.
67+
* The params argument can be added in if we ever have a use case for it.
68+
*/
69+
invalidateEndpoint(method: keyof typeof api.methods) {
70+
this.invalidateQueries({ queryKey: [method] })
71+
}
72+
}
73+
5274
// Needs to be defined here instead of in app so we can use it to define
5375
// `apiQueryClient`, which provides API-typed versions of QueryClient methods
5476
export const queryClient = new QueryClient({

app/api/hooks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export const getApiQueryOptions =
108108
queryOptions({
109109
queryKey: [method, params],
110110
// no catch, let unexpected errors bubble up
111-
queryFn: ({ signal }) => api[method](params, { signal }).then(handleResult(method)),
111+
queryFn: () => api[method](params).then(handleResult(method)),
112112
// In the case of 404s, let the error bubble up to the error boundary so
113113
// we can say Not Found. If you need to allow a 404 and want it to show
114114
// up as `error` state instead, pass `useErrorBoundary: false` as an

app/api/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ export * from './__generated__/Api'
1919

2020
export type { ApiTypes }
2121

22-
export * as PathParams from './path-params'
23-
2422
export { ensurePrefetched, PAGE_SIZE, type PaginatedQuery, type ResultsPage } from './hooks'
2523
export type { ApiError } from './errors'
2624
export { navToLogin } from './nav-to-login'

app/api/path-params.ts

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

app/api/selectors.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
5+
*
6+
* Copyright Oxide Computer Company
7+
*/
8+
import type { Merge } from 'type-fest'
9+
10+
export type Project = Readonly<{ project?: string }>
11+
export type Instance = Readonly<Merge<Project, { instance?: string }>>
12+
export type Disk = Readonly<Merge<Project, { disk?: string }>>
13+
export type Image = Readonly<Merge<Project, { image?: string }>>
14+
export type SiloImage = Readonly<{ image?: string }>
15+
export type NetworkInterface = Readonly<Merge<Instance, { interface?: string }>>
16+
export type Snapshot = Readonly<Merge<Project, { snapshot?: string }>>
17+
export type Vpc = Readonly<Merge<Project, { vpc?: string }>>
18+
export type VpcRouter = Readonly<Merge<Vpc, { router?: string }>>
19+
export type InternetGateway = Readonly<Merge<Vpc, { gateway?: string }>>
20+
export type InternetGatewayIpAddress = Readonly<
21+
Merge<InternetGateway, { address?: string }>
22+
>
23+
export type InternetGatewayIpPool = Merge<InternetGateway, { pool?: string }>
24+
export type VpcRouterRoute = Readonly<Merge<VpcRouter, { route?: string }>>
25+
export type VpcSubnet = Readonly<Merge<Vpc, { subnet?: string }>>
26+
export type FirewallRule = Readonly<Merge<Vpc, { rule?: string }>>
27+
export type Silo = Readonly<{ silo?: string }>
28+
export type IdentityProvider = Readonly<Merge<Silo, { provider: string }>>
29+
export type SystemUpdate = Readonly<{ version: string }>
30+
export type SshKey = Readonly<{ sshKey: string }>
31+
export type Sled = Readonly<{ sledId?: string }>
32+
export type IpPool = Readonly<{ pool?: string }>
33+
export type FloatingIp = Readonly<Merge<Project, { floatingIp?: string }>>
34+
35+
export type Id = Readonly<{ id: string }>

app/components/ErrorBoundary.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export const trigger404 = { type: 'error', statusCode: 404 }
1717
type Props = { error: Error | ApiError }
1818

1919
function ErrorFallback({ error }: Props) {
20+
console.error(error)
21+
2022
if ('statusCode' in error && error.statusCode === 404) {
2123
return <NotFound />
2224
}
@@ -38,6 +40,5 @@ export const ErrorBoundary = (props: { children: React.ReactNode }) => (
3840
export function RouterDataErrorBoundary() {
3941
// TODO: validate this unknown at runtime _before_ passing to ErrorFallback
4042
const error = useRouteError() as Props['error']
41-
console.error(error)
4243
return <ErrorFallback error={error} />
4344
}

app/components/ExternalIps.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@ import { useApiQuery } from '@oxide/api'
1111
import { EmptyCell, SkeletonCell } from '~/table/cells/EmptyCell'
1212
import { CopyableIp } from '~/ui/lib/CopyableIp'
1313
import { intersperse } from '~/util/array'
14+
import type * as PP from '~/util/path-params'
1415

15-
type InstanceSelector = { project: string; instance: string }
16-
17-
export function ExternalIps({ project, instance }: InstanceSelector) {
16+
export function ExternalIps({ project, instance }: PP.Instance) {
1817
const { data, isPending } = useApiQuery('instanceExternalIpList', {
1918
path: { instance },
2019
query: { project },

app/forms/firewall-rules-common.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,9 +323,9 @@ export const CommonFields = ({ control, nameTaken, error }: CommonFieldsProps) =
323323
target="_blank"
324324
rel="noreferrer"
325325
>
326-
guest networking guide
326+
Networking
327327
</a>{' '}
328-
and{' '}
328+
guide and the{' '}
329329
<a
330330
href="https://docs.oxide.computer/api/vpc_firewall_rules_update"
331331
// don't need color and hover color because message text is already color-info anyway

app/forms/floating-ip-edit.tsx

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,27 @@
88
import { useForm } from 'react-hook-form'
99
import { useNavigate, type LoaderFunctionArgs } from 'react-router-dom'
1010

11-
import {
12-
apiQueryClient,
13-
useApiMutation,
14-
useApiQueryClient,
15-
usePrefetchedApiQuery,
16-
} from '@oxide/api'
11+
import { apiq, queryClient, useApiMutation, usePrefetchedApiQuery } from '@oxide/api'
1712

1813
import { DescriptionField } from '~/components/form/fields/DescriptionField'
1914
import { NameField } from '~/components/form/fields/NameField'
2015
import { SideModalForm } from '~/components/form/SideModalForm'
2116
import { HL } from '~/components/HL'
2217
import { getFloatingIpSelector, useFloatingIpSelector } from '~/hooks/use-params'
2318
import { addToast } from '~/stores/toast'
19+
import type * as PP from '~/util/path-params'
2420
import { pb } from 'app/util/path-builder'
2521

22+
const floatingIpView = ({ project, floatingIp }: PP.FloatingIp) =>
23+
apiq('floatingIpView', { path: { floatingIp }, query: { project } })
24+
2625
EditFloatingIpSideModalForm.loader = async ({ params }: LoaderFunctionArgs) => {
27-
const { floatingIp, project } = getFloatingIpSelector(params)
28-
await apiQueryClient.prefetchQuery('floatingIpView', {
29-
path: { floatingIp },
30-
query: { project },
31-
})
26+
const selector = getFloatingIpSelector(params)
27+
await queryClient.prefetchQuery(floatingIpView(selector))
3228
return null
3329
}
3430

3531
export function EditFloatingIpSideModalForm() {
36-
const queryClient = useApiQueryClient()
3732
const navigate = useNavigate()
3833

3934
const floatingIpSelector = useFloatingIpSelector()
@@ -47,7 +42,7 @@ export function EditFloatingIpSideModalForm() {
4742

4843
const editFloatingIp = useApiMutation('floatingIpUpdate', {
4944
onSuccess(_floatingIp) {
50-
queryClient.invalidateQueries('floatingIpList')
45+
queryClient.invalidateEndpoint('floatingIpList')
5146
addToast(<>Floating IP <HL>{_floatingIp.name}</HL> updated</>) // prettier-ignore
5247
onDismiss()
5348
},

0 commit comments

Comments
 (0)