Skip to content

Commit

Permalink
clean up gateway routes fetch logic by extracting shared hook
Browse files Browse the repository at this point in the history
  • Loading branch information
david-crespo committed Dec 12, 2024
1 parent 49cfa15 commit 24dc844
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 89 deletions.
51 changes: 14 additions & 37 deletions app/pages/project/vpcs/VpcPage/tabs/VpcGatewaysTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import { ALL_ISH } from '~/util/consts'
import { pb } from '~/util/path-builder'
import type * as PP from '~/util/path-params'

import { useGatewayRoutes } from '../../internet-gateway-edit'

const gatewayList = ({ project, vpc }: PP.Vpc) =>
getListQFn('internetGatewayList', { query: { project, vpc, limit: ALL_ISH } })
const routerList = ({ project, vpc }: PP.Vpc) =>
Expand All @@ -48,41 +50,20 @@ const InternetGatewayIpPoolCell = ({ gatewayId }: { gatewayId: string }) => {
return <IpPoolCell ipPoolId={gateways.items[0].ipPoolId} />
}

// called by InternetGatewayAttachedRoutesCell to get the routes per router
// we need to have this in its own function because useQuery cannot be called inside a loop
const InternetGatewayRoutes = ({
project,
vpc,
gateway,
router,
}: PP.VpcInternetGateway & { router: string }) => {
const { data: routes } = useQuery(routeList({ project, vpc, router }).optionsFn())
if (!routes || routes.items.length < 1) return null
return routes.items
.filter((r) => r.target.type === 'internet_gateway' && r.target.value === gateway)
.map((route) => (
<Link
key={route.name}
to={pb.vpcRouterRouteEdit({ project, vpc, router, route: route.name })}
className="link-with-underline text-sans-md"
>
const GatewayRoutes = ({ project, vpc, gateway }: PP.VpcInternetGateway) => {
const matchingRoutes = useGatewayRoutes({ project, vpc, gateway })

if (!matchingRoutes?.length) return <EmptyCell />

return matchingRoutes.map(([router, route]) => {
const to = pb.vpcRouterRouteEdit({ project, vpc, router, route: route.name })
const key = `${router}-${route.name}`
return (
<Link key={key} to={to} className="link-with-underline text-sans-md">
{route.name}
</Link>
))
}

const InternetGatewayAttachedRoutesCell = ({
project,
vpc,
gateway,
}: PP.VpcInternetGateway) => {
const { data: routers } = useQuery(routerList({ project, vpc }).optionsFn())
const matchingRoutes = routers?.items.map((router) => {
const props = { project, vpc, gateway, router: router.name }
return <InternetGatewayRoutes key={router.name} {...props} />
)
})
if (!matchingRoutes?.length) return <EmptyCell />
return <div className="space-x-2">{matchingRoutes}</div>
}

const colHelper = createColumnHelper<InternetGateway>()
Expand Down Expand Up @@ -152,11 +133,7 @@ export function VpcInternetGatewaysTab() {
id: 'routes',
header: 'Routes',
cell: (info) => (
<InternetGatewayAttachedRoutesCell
project={project}
vpc={vpc}
gateway={info.getValue()}
/>
<GatewayRoutes project={project} vpc={vpc} gateway={info.getValue()} />
),
}),
colHelper.accessor('timeCreated', Columns.timeCreated),
Expand Down
113 changes: 61 additions & 52 deletions app/pages/project/vpcs/internet-gateway-edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,20 @@
* Copyright Oxide Computer Company
*/

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

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

import { apiQueryClient, getListQFn, queryClient, usePrefetchedApiQuery } from '~/api'
import {
apiQueryClient,
getListQFn,
queryClient,
usePrefetchedApiQuery,
usePrefetchedQuery,
} from '~/api'
import { SideModalForm } from '~/components/form/SideModalForm'
import { getInternetGatewaySelector, useInternetGatewaySelector } from '~/hooks/use-params'
import { DescriptionCell } from '~/table/cells/DescriptionCell'
Expand All @@ -28,54 +35,55 @@ import { links } from '~/util/links'
import { pb } from '~/util/path-builder'
import type * as PP from '~/util/path-params'

const RouterRow = ({
project,
vpc,
gateway,
router,
}: PP.VpcInternetGateway & { router: string }) => {
const matchingRoutes: JSX.Element[] = []
const { data: routes } = useQuery(routeList({ project, vpc, router }).optionsFn())
if (!routes || routes.items.length < 1) return
routes.items.forEach((route) => {
if (route.target.type === 'internet_gateway' && route.target.value === gateway) {
matchingRoutes.push(
<Table.Row key={`${router}-${route.name}`}>
<Table.Cell className="!bg-raise">{router}</Table.Cell>
<Table.Cell className="bg-raise">
<Link
to={pb.vpcRouterRouteEdit({
project,
vpc,
router,
route: route.name,
})}
className="link-with-underline text-sans-md"
>
{route.name}
</Link>
</Table.Cell>
</Table.Row>
)
}
const RoutesEmpty = () => (
<Table.Row>
<Table.Cell colSpan={2} className="bg-secondary">
No VPC router routes target this gateway.
</Table.Cell>
</Table.Row>
)

/**
* For a given gateway, return a list of [router name, RouterRoute] pairs
*/
export function useGatewayRoutes({ project, vpc, gateway }: PP.VpcInternetGateway) {
const { data: routers } = usePrefetchedQuery(routerList({ project, vpc }).optionsFn())
const routerNames = routers.items.map((r) => r.name)

const routesQueries = useQueries({
queries: routerNames.map((router) => routeList({ project, vpc, router }).optionsFn()),
})
return matchingRoutes
}
const loadedRoutesLists = routesQueries.filter((q) => !!q.data).map((q) => q.data.items)

// loading. should never happen because of prefetches
if (loadedRoutesLists.length < routers.items.length) return null

const RouterRows = ({ project, vpc, gateway }: PP.VpcInternetGateway) => {
const { data: routers } = useQuery(routerList({ project, vpc }).optionsFn())
const matchingRoutes = routers?.items.flatMap((router) =>
RouterRow({ project, vpc, gateway, router: router.name })
return R.pipe(
R.zip(routerNames, loadedRoutesLists),
R.flatMap(([router, routes]) => routes.map((route) => [router, route] as const)),
R.filter(([_, r]) => r.target.type === 'internet_gateway' && r.target.value === gateway)
)
return matchingRoutes?.length ? (
matchingRoutes
) : (
<Table.Row>
<Table.Cell colSpan={2} className="bg-secondary">
No VPC routes target this gateway.
}

function RouteRows({ project, vpc, gateway }: PP.VpcInternetGateway) {
const matchingRoutes = useGatewayRoutes({ project, vpc, gateway })

if (!matchingRoutes) return null
if (matchingRoutes.length === 0) return <RoutesEmpty />

return matchingRoutes.map(([router, route]) => (
<Table.Row key={route.id}>
<Table.Cell className="!bg-raise">{router}</Table.Cell>
<Table.Cell className="bg-raise">
<Link
to={pb.vpcRouterRouteEdit({ project, vpc, router, route: route.name })}
className="link-with-underline text-sans-md"
>
{route.name}
</Link>
</Table.Cell>
</Table.Row>
)
))
}

const gatewayIpPoolList = ({ project, vpc, gateway }: PP.VpcInternetGateway) =>
Expand All @@ -100,14 +108,13 @@ EditInternetGatewayForm.loader = async function ({ params }: LoaderFunctionArgs)
}),
queryClient.prefetchQuery(gatewayIpPoolList({ project, vpc, gateway }).optionsFn()),
queryClient.prefetchQuery(gatewayIpAddressList({ project, vpc, gateway }).optionsFn()),
(await queryClient.fetchQuery(routerList({ project, vpc }).optionsFn())).items.map(
(router) => {
...(await queryClient.fetchQuery(routerList({ project, vpc }).optionsFn())).items.map(
(router) =>
queryClient.prefetchQuery(
routeList({ project, vpc, router: router.name }).optionsFn()
)
}
),
])
] satisfies Promise<unknown>[])
return null
}

Expand Down Expand Up @@ -235,11 +242,13 @@ export function EditInternetGatewayForm() {
</SideModal.Heading>
<Table>
<Table.Header>
<Table.HeadCell>Router</Table.HeadCell>
<Table.HeadCell>Route</Table.HeadCell>
<Table.HeaderRow>
<Table.HeadCell>Router</Table.HeadCell>
<Table.HeadCell>Route</Table.HeadCell>
</Table.HeaderRow>
</Table.Header>
<Table.Body>
<RouterRows project={project} vpc={vpc} gateway={gateway} />
<RouteRows project={project} vpc={vpc} gateway={gateway} />
</Table.Body>
</Table>
</div>
Expand Down

0 comments on commit 24dc844

Please sign in to comment.