Skip to content

Commit 24dc844

Browse files
committed
clean up gateway routes fetch logic by extracting shared hook
1 parent 49cfa15 commit 24dc844

File tree

2 files changed

+75
-89
lines changed

2 files changed

+75
-89
lines changed

app/pages/project/vpcs/VpcPage/tabs/VpcGatewaysTab.tsx

Lines changed: 14 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import { ALL_ISH } from '~/util/consts'
2424
import { pb } from '~/util/path-builder'
2525
import type * as PP from '~/util/path-params'
2626

27+
import { useGatewayRoutes } from '../../internet-gateway-edit'
28+
2729
const gatewayList = ({ project, vpc }: PP.Vpc) =>
2830
getListQFn('internetGatewayList', { query: { project, vpc, limit: ALL_ISH } })
2931
const routerList = ({ project, vpc }: PP.Vpc) =>
@@ -48,41 +50,20 @@ const InternetGatewayIpPoolCell = ({ gatewayId }: { gatewayId: string }) => {
4850
return <IpPoolCell ipPoolId={gateways.items[0].ipPoolId} />
4951
}
5052

51-
// called by InternetGatewayAttachedRoutesCell to get the routes per router
52-
// we need to have this in its own function because useQuery cannot be called inside a loop
53-
const InternetGatewayRoutes = ({
54-
project,
55-
vpc,
56-
gateway,
57-
router,
58-
}: PP.VpcInternetGateway & { router: string }) => {
59-
const { data: routes } = useQuery(routeList({ project, vpc, router }).optionsFn())
60-
if (!routes || routes.items.length < 1) return null
61-
return routes.items
62-
.filter((r) => r.target.type === 'internet_gateway' && r.target.value === gateway)
63-
.map((route) => (
64-
<Link
65-
key={route.name}
66-
to={pb.vpcRouterRouteEdit({ project, vpc, router, route: route.name })}
67-
className="link-with-underline text-sans-md"
68-
>
53+
const GatewayRoutes = ({ project, vpc, gateway }: PP.VpcInternetGateway) => {
54+
const matchingRoutes = useGatewayRoutes({ project, vpc, gateway })
55+
56+
if (!matchingRoutes?.length) return <EmptyCell />
57+
58+
return matchingRoutes.map(([router, route]) => {
59+
const to = pb.vpcRouterRouteEdit({ project, vpc, router, route: route.name })
60+
const key = `${router}-${route.name}`
61+
return (
62+
<Link key={key} to={to} className="link-with-underline text-sans-md">
6963
{route.name}
7064
</Link>
71-
))
72-
}
73-
74-
const InternetGatewayAttachedRoutesCell = ({
75-
project,
76-
vpc,
77-
gateway,
78-
}: PP.VpcInternetGateway) => {
79-
const { data: routers } = useQuery(routerList({ project, vpc }).optionsFn())
80-
const matchingRoutes = routers?.items.map((router) => {
81-
const props = { project, vpc, gateway, router: router.name }
82-
return <InternetGatewayRoutes key={router.name} {...props} />
65+
)
8366
})
84-
if (!matchingRoutes?.length) return <EmptyCell />
85-
return <div className="space-x-2">{matchingRoutes}</div>
8667
}
8768

8869
const colHelper = createColumnHelper<InternetGateway>()
@@ -152,11 +133,7 @@ export function VpcInternetGatewaysTab() {
152133
id: 'routes',
153134
header: 'Routes',
154135
cell: (info) => (
155-
<InternetGatewayAttachedRoutesCell
156-
project={project}
157-
vpc={vpc}
158-
gateway={info.getValue()}
159-
/>
136+
<GatewayRoutes project={project} vpc={vpc} gateway={info.getValue()} />
160137
),
161138
}),
162139
colHelper.accessor('timeCreated', Columns.timeCreated),

app/pages/project/vpcs/internet-gateway-edit.tsx

Lines changed: 61 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,20 @@
66
* Copyright Oxide Computer Company
77
*/
88

9-
import { useQuery } from '@tanstack/react-query'
9+
import { useQueries, useQuery } from '@tanstack/react-query'
1010
import { useForm } from 'react-hook-form'
1111
import { Link, useNavigate, type LoaderFunctionArgs } from 'react-router-dom'
12+
import * as R from 'remeda'
1213

1314
import { Gateway16Icon } from '@oxide/design-system/icons/react'
1415

15-
import { apiQueryClient, getListQFn, queryClient, usePrefetchedApiQuery } from '~/api'
16+
import {
17+
apiQueryClient,
18+
getListQFn,
19+
queryClient,
20+
usePrefetchedApiQuery,
21+
usePrefetchedQuery,
22+
} from '~/api'
1623
import { SideModalForm } from '~/components/form/SideModalForm'
1724
import { getInternetGatewaySelector, useInternetGatewaySelector } from '~/hooks/use-params'
1825
import { DescriptionCell } from '~/table/cells/DescriptionCell'
@@ -28,54 +35,55 @@ import { links } from '~/util/links'
2835
import { pb } from '~/util/path-builder'
2936
import type * as PP from '~/util/path-params'
3037

31-
const RouterRow = ({
32-
project,
33-
vpc,
34-
gateway,
35-
router,
36-
}: PP.VpcInternetGateway & { router: string }) => {
37-
const matchingRoutes: JSX.Element[] = []
38-
const { data: routes } = useQuery(routeList({ project, vpc, router }).optionsFn())
39-
if (!routes || routes.items.length < 1) return
40-
routes.items.forEach((route) => {
41-
if (route.target.type === 'internet_gateway' && route.target.value === gateway) {
42-
matchingRoutes.push(
43-
<Table.Row key={`${router}-${route.name}`}>
44-
<Table.Cell className="!bg-raise">{router}</Table.Cell>
45-
<Table.Cell className="bg-raise">
46-
<Link
47-
to={pb.vpcRouterRouteEdit({
48-
project,
49-
vpc,
50-
router,
51-
route: route.name,
52-
})}
53-
className="link-with-underline text-sans-md"
54-
>
55-
{route.name}
56-
</Link>
57-
</Table.Cell>
58-
</Table.Row>
59-
)
60-
}
38+
const RoutesEmpty = () => (
39+
<Table.Row>
40+
<Table.Cell colSpan={2} className="bg-secondary">
41+
No VPC router routes target this gateway.
42+
</Table.Cell>
43+
</Table.Row>
44+
)
45+
46+
/**
47+
* For a given gateway, return a list of [router name, RouterRoute] pairs
48+
*/
49+
export function useGatewayRoutes({ project, vpc, gateway }: PP.VpcInternetGateway) {
50+
const { data: routers } = usePrefetchedQuery(routerList({ project, vpc }).optionsFn())
51+
const routerNames = routers.items.map((r) => r.name)
52+
53+
const routesQueries = useQueries({
54+
queries: routerNames.map((router) => routeList({ project, vpc, router }).optionsFn()),
6155
})
62-
return matchingRoutes
63-
}
56+
const loadedRoutesLists = routesQueries.filter((q) => !!q.data).map((q) => q.data.items)
57+
58+
// loading. should never happen because of prefetches
59+
if (loadedRoutesLists.length < routers.items.length) return null
6460

65-
const RouterRows = ({ project, vpc, gateway }: PP.VpcInternetGateway) => {
66-
const { data: routers } = useQuery(routerList({ project, vpc }).optionsFn())
67-
const matchingRoutes = routers?.items.flatMap((router) =>
68-
RouterRow({ project, vpc, gateway, router: router.name })
61+
return R.pipe(
62+
R.zip(routerNames, loadedRoutesLists),
63+
R.flatMap(([router, routes]) => routes.map((route) => [router, route] as const)),
64+
R.filter(([_, r]) => r.target.type === 'internet_gateway' && r.target.value === gateway)
6965
)
70-
return matchingRoutes?.length ? (
71-
matchingRoutes
72-
) : (
73-
<Table.Row>
74-
<Table.Cell colSpan={2} className="bg-secondary">
75-
No VPC routes target this gateway.
66+
}
67+
68+
function RouteRows({ project, vpc, gateway }: PP.VpcInternetGateway) {
69+
const matchingRoutes = useGatewayRoutes({ project, vpc, gateway })
70+
71+
if (!matchingRoutes) return null
72+
if (matchingRoutes.length === 0) return <RoutesEmpty />
73+
74+
return matchingRoutes.map(([router, route]) => (
75+
<Table.Row key={route.id}>
76+
<Table.Cell className="!bg-raise">{router}</Table.Cell>
77+
<Table.Cell className="bg-raise">
78+
<Link
79+
to={pb.vpcRouterRouteEdit({ project, vpc, router, route: route.name })}
80+
className="link-with-underline text-sans-md"
81+
>
82+
{route.name}
83+
</Link>
7684
</Table.Cell>
7785
</Table.Row>
78-
)
86+
))
7987
}
8088

8189
const gatewayIpPoolList = ({ project, vpc, gateway }: PP.VpcInternetGateway) =>
@@ -100,14 +108,13 @@ EditInternetGatewayForm.loader = async function ({ params }: LoaderFunctionArgs)
100108
}),
101109
queryClient.prefetchQuery(gatewayIpPoolList({ project, vpc, gateway }).optionsFn()),
102110
queryClient.prefetchQuery(gatewayIpAddressList({ project, vpc, gateway }).optionsFn()),
103-
(await queryClient.fetchQuery(routerList({ project, vpc }).optionsFn())).items.map(
104-
(router) => {
111+
...(await queryClient.fetchQuery(routerList({ project, vpc }).optionsFn())).items.map(
112+
(router) =>
105113
queryClient.prefetchQuery(
106114
routeList({ project, vpc, router: router.name }).optionsFn()
107115
)
108-
}
109116
),
110-
])
117+
] satisfies Promise<unknown>[])
111118
return null
112119
}
113120

@@ -235,11 +242,13 @@ export function EditInternetGatewayForm() {
235242
</SideModal.Heading>
236243
<Table>
237244
<Table.Header>
238-
<Table.HeadCell>Router</Table.HeadCell>
239-
<Table.HeadCell>Route</Table.HeadCell>
245+
<Table.HeaderRow>
246+
<Table.HeadCell>Router</Table.HeadCell>
247+
<Table.HeadCell>Route</Table.HeadCell>
248+
</Table.HeaderRow>
240249
</Table.Header>
241250
<Table.Body>
242-
<RouterRows project={project} vpc={vpc} gateway={gateway} />
251+
<RouteRows project={project} vpc={vpc} gateway={gateway} />
243252
</Table.Body>
244253
</Table>
245254
</div>

0 commit comments

Comments
 (0)