From 221494eec9b29b7c104509dbdb796a4bcfcdc527 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 01:52:29 +0000 Subject: [PATCH 1/3] Initial plan From 003aca61b43075f6a4e9f633bcc346c5dbcdf791 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 02:15:41 +0000 Subject: [PATCH 2/3] Fix MSW metadata handlers to use dynamic :type routes matching client URLs The @objectstack/client sends metadata requests using singular type names (e.g. GET /api/v1/meta/app, /meta/object) but the MSW handlers only registered plural routes (/meta/apps, /meta/objects), causing all metadata fetches to fail with 500 errors on the demo site. Replace hardcoded plural-only handlers with dynamic /meta/:type and /metadata/:type/:name routes that match both singular and plural forms. Also fix RootRedirect to show an error message instead of an infinite loading screen when no apps are available. Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- apps/console/src/App.tsx | 10 +++- apps/console/src/mocks/handlers.ts | 86 +++++++----------------------- 2 files changed, 26 insertions(+), 70 deletions(-) diff --git a/apps/console/src/App.tsx b/apps/console/src/App.tsx index 3e7766a71..f107638e0 100644 --- a/apps/console/src/App.tsx +++ b/apps/console/src/App.tsx @@ -368,7 +368,7 @@ function findFirstRoute(items: any[]): string { // Redirect root to default app function RootRedirect() { - const { apps, loading } = useMetadata(); + const { apps, loading, error } = useMetadata(); const activeApps = apps.filter((a: any) => a.active !== false); const defaultApp = activeApps.find((a: any) => a.isDefault === true) || activeApps[0]; @@ -376,7 +376,13 @@ function RootRedirect() { if (defaultApp) { return ; } - return ; + return ( +
+ + {error ? 'Failed to Load Configuration' : 'No Apps Configured'} + +
+ ); } export function App() { diff --git a/apps/console/src/mocks/handlers.ts b/apps/console/src/mocks/handlers.ts index d0c04698c..6afe54ae8 100644 --- a/apps/console/src/mocks/handlers.ts +++ b/apps/console/src/mocks/handlers.ts @@ -48,98 +48,48 @@ export function createHandlers(baseUrl: string, kernel: ObjectKernel, driver: In return HttpResponse.json(response, { status: 200 }); }), - // ── Metadata: list objects ─────────────────────────────────────────── - http.get(`${prefix}${baseUrl}/meta/objects`, async () => { - const response = await protocol.getMetaItems({ type: 'object' }); + // ── Metadata: list items by type ──────────────────────────────────── + // The client sends GET /meta/ where is singular (e.g. app, + // object, dashboard, report, page). We also keep the legacy plural + // routes (/meta/apps, /meta/objects, …) for backward compatibility. + // A single dynamic handler covers both forms. + http.get(`${prefix}${baseUrl}/meta/:type`, async ({ params }) => { + const raw = params.type as string; + const response = await protocol.getMetaItems({ type: raw }); return HttpResponse.json(response, { status: 200 }); }), - http.get(`${prefix}${baseUrl}/metadata/objects`, async () => { - const response = await protocol.getMetaItems({ type: 'object' }); + http.get(`${prefix}${baseUrl}/metadata/:type`, async ({ params }) => { + const raw = params.type as string; + const response = await protocol.getMetaItems({ type: raw }); return HttpResponse.json(response, { status: 200 }); }), - // ── Metadata: single object (legacy /meta/objects/:name) ───────────── - http.get(`${prefix}${baseUrl}/meta/objects/:objectName`, async ({ params }) => { + // ── Metadata: single item by type + name ───────────────────────────── + http.get(`${prefix}${baseUrl}/meta/:type/:name`, async ({ params }) => { try { const response = await protocol.getMetaItem({ - type: 'object', - name: params.objectName as string - }); - return HttpResponse.json(response || { error: 'Not found' }, { status: response ? 200 : 404 }); - } catch (e) { - return HttpResponse.json({ error: String(e) }, { status: 500 }); - } - }), - - // ── Metadata: single object (/meta/object/:name & /metadata/object/:name) - http.get(`${prefix}${baseUrl}/meta/object/:objectName`, async ({ params }) => { - try { - const response = await protocol.getMetaItem({ - type: 'object', - name: params.objectName as string + type: params.type as string, + name: params.name as string }); const payload = (response && response.item) ? response.item : response; return HttpResponse.json(payload || { error: 'Not found' }, { status: payload ? 200 : 404 }); } catch (e) { - console.error('[MSW] error getting meta item', e); return HttpResponse.json({ error: String(e) }, { status: 500 }); } }), - - http.get(`${prefix}${baseUrl}/metadata/object/:objectName`, async ({ params }) => { + http.get(`${prefix}${baseUrl}/metadata/:type/:name`, async ({ params }) => { try { const response = await protocol.getMetaItem({ - type: 'object', - name: params.objectName as string + type: params.type as string, + name: params.name as string }); const payload = (response && response.item) ? response.item : response; return HttpResponse.json(payload || { error: 'Not found' }, { status: payload ? 200 : 404 }); } catch (e) { - console.error('[MSW] error getting meta item', e); return HttpResponse.json({ error: String(e) }, { status: 500 }); } }), - // ── Metadata: apps ────────────────────────────────────────────────── - http.get(`${prefix}${baseUrl}/meta/apps`, async () => { - const response = await protocol.getMetaItems({ type: 'app' }); - return HttpResponse.json(response, { status: 200 }); - }), - http.get(`${prefix}${baseUrl}/metadata/apps`, async () => { - const response = await protocol.getMetaItems({ type: 'app' }); - return HttpResponse.json(response, { status: 200 }); - }), - - // ── Metadata: dashboards ──────────────────────────────────────────── - http.get(`${prefix}${baseUrl}/meta/dashboards`, async () => { - const response = await protocol.getMetaItems({ type: 'dashboard' }); - return HttpResponse.json(response, { status: 200 }); - }), - http.get(`${prefix}${baseUrl}/metadata/dashboards`, async () => { - const response = await protocol.getMetaItems({ type: 'dashboard' }); - return HttpResponse.json(response, { status: 200 }); - }), - - // ── Metadata: reports ─────────────────────────────────────────────── - http.get(`${prefix}${baseUrl}/meta/reports`, async () => { - const response = await protocol.getMetaItems({ type: 'report' }); - return HttpResponse.json(response, { status: 200 }); - }), - http.get(`${prefix}${baseUrl}/metadata/reports`, async () => { - const response = await protocol.getMetaItems({ type: 'report' }); - return HttpResponse.json(response, { status: 200 }); - }), - - // ── Metadata: pages ───────────────────────────────────────────────── - http.get(`${prefix}${baseUrl}/meta/pages`, async () => { - const response = await protocol.getMetaItems({ type: 'page' }); - return HttpResponse.json(response, { status: 200 }); - }), - http.get(`${prefix}${baseUrl}/metadata/pages`, async () => { - const response = await protocol.getMetaItems({ type: 'page' }); - return HttpResponse.json(response, { status: 200 }); - }), - // ── Data: find all ────────────────────────────────────────────────── http.get(`${prefix}${baseUrl}/data/:objectName`, async ({ params, request }) => { const url = new URL(request.url); From 7628557ac1037728de91d300f4a624a7a110dcb7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 02:18:39 +0000 Subject: [PATCH 3/3] Address code review: improve variable naming and error messages Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- apps/console/src/App.tsx | 5 ++++- apps/console/src/mocks/handlers.ts | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/console/src/App.tsx b/apps/console/src/App.tsx index f107638e0..25468ddaf 100644 --- a/apps/console/src/App.tsx +++ b/apps/console/src/App.tsx @@ -1,7 +1,7 @@ import { BrowserRouter, Routes, Route, Navigate, useNavigate, useLocation, useSearchParams } from 'react-router-dom'; import { useState, useEffect, lazy, Suspense, useMemo, type ReactNode } from 'react'; import { ObjectForm } from '@object-ui/plugin-form'; -import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Empty, EmptyTitle } from '@object-ui/components'; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Empty, EmptyTitle, EmptyDescription } from '@object-ui/components'; import { toast } from 'sonner'; import { SchemaRendererProvider } from '@object-ui/react'; import type { ConnectionState } from './dataSource'; @@ -380,6 +380,9 @@ function RootRedirect() {
{error ? 'Failed to Load Configuration' : 'No Apps Configured'} + + {error ? error.message : 'No applications have been registered. Check your ObjectStack configuration.'} +
); diff --git a/apps/console/src/mocks/handlers.ts b/apps/console/src/mocks/handlers.ts index 6afe54ae8..88d11c2f5 100644 --- a/apps/console/src/mocks/handlers.ts +++ b/apps/console/src/mocks/handlers.ts @@ -54,13 +54,13 @@ export function createHandlers(baseUrl: string, kernel: ObjectKernel, driver: In // routes (/meta/apps, /meta/objects, …) for backward compatibility. // A single dynamic handler covers both forms. http.get(`${prefix}${baseUrl}/meta/:type`, async ({ params }) => { - const raw = params.type as string; - const response = await protocol.getMetaItems({ type: raw }); + const metadataType = params.type as string; + const response = await protocol.getMetaItems({ type: metadataType }); return HttpResponse.json(response, { status: 200 }); }), http.get(`${prefix}${baseUrl}/metadata/:type`, async ({ params }) => { - const raw = params.type as string; - const response = await protocol.getMetaItems({ type: raw }); + const metadataType = params.type as string; + const response = await protocol.getMetaItems({ type: metadataType }); return HttpResponse.json(response, { status: 200 }); }),