diff --git a/app/pages/lookups.ts b/app/pages/lookups.ts
new file mode 100644
index 000000000..97d3a0c93
--- /dev/null
+++ b/app/pages/lookups.ts
@@ -0,0 +1,27 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * Copyright Oxide Computer Company
+ */
+import { redirect, type LoaderFunctionArgs } from 'react-router-dom'
+
+import { apiQueryClient } from '@oxide/api'
+
+import { trigger404 } from 'app/components/ErrorBoundary'
+import { pb } from 'app/util/path-builder'
+
+export async function instanceLookupLoader({ params }: LoaderFunctionArgs) {
+ try {
+ const instance = await apiQueryClient.fetchQuery('instanceView', {
+ path: { instance: params.instance! },
+ })
+ const project = await apiQueryClient.fetchQuery('projectView', {
+ path: { project: instance.projectId },
+ })
+ return redirect(pb.instance({ project: project.name, instance: instance.name }))
+ } catch (_e) {
+ throw trigger404
+ }
+}
diff --git a/app/routes.tsx b/app/routes.tsx
index 55f109532..0da226e92 100644
--- a/app/routes.tsx
+++ b/app/routes.tsx
@@ -43,6 +43,7 @@ import DeviceAuthSuccessPage from './pages/DeviceAuthSuccessPage'
import DeviceAuthVerifyPage from './pages/DeviceAuthVerifyPage'
import { LoginPage } from './pages/LoginPage'
import { LoginPageSaml } from './pages/LoginPageSaml'
+import { instanceLookupLoader } from './pages/lookups'
import {
DisksPage,
ImagesPage,
@@ -229,6 +230,15 @@ export const routes = createRoutesFromElements(
loader={SiloUtilizationPage.loader}
handle={{ crumb: 'Utilization' }}
/>
+
+ {/* let's do both. what could go wrong*/}
+
+
+
}>
{
+ test('404s on existing name', async ({ page }) => {
+ await page.goto('/lookup/i/db1')
+ await expect(page.getByText('Page not found')).toBeVisible()
+ })
+
+ test('404s on empty ID', async ({ page }) => {
+ await page.goto('/lookup/i/')
+ await expect(page.getByText('Page not found')).toBeVisible()
+ })
+
+ test('looks up instance by ID', async ({ page }) => {
+ await page.goto('/lookup/i/935499b3-fd96-432a-9c21-83a3dc1eece4')
+ await expect(page).toHaveURL('/projects/mock-project/instances/db1/storage')
+ })
+})
+
+test.describe('/lookup/instances', () => {
+ test('404s on existing name', async ({ page }) => {
+ await page.goto('/lookup/instances/db1')
+ await expect(page.getByText('Page not found')).toBeVisible()
+ })
+
+ test('404s on empty ID', async ({ page }) => {
+ await page.goto('/lookup/instances/')
+ await expect(page.getByText('Page not found')).toBeVisible()
+ })
+
+ test('looks up instance by ID', async ({ page }) => {
+ await page.goto('/lookup/instances/935499b3-fd96-432a-9c21-83a3dc1eece4')
+ await expect(page).toHaveURL('/projects/mock-project/instances/db1/storage')
+ })
+})