From 023451e00011f206b023b25152127c23fdb1a1d4 Mon Sep 17 00:00:00 2001 From: Jonas Pacheco Date: Thu, 19 Mar 2026 14:55:36 -0300 Subject: [PATCH] =?UTF-8?q?fix(superset):=20resolver=20slug=20=E2=86=92=20?= =?UTF-8?q?UUID=20antes=20de=20gerar=20guest=20token=20e=20embeddar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit O SDK do Superset exige o UUID do embedded config (não o slug) tanto no embedDashboard quanto no guest token. Agora o backend resolve: slug → dashboard ID → embedded UUID → guest token com UUID E retorna { token, embeddedId } para o frontend usar no embedDashboard. Co-Authored-By: Claude Sonnet 4.6 --- client/src/components/SupersetDashboard.tsx | 16 ++++++++---- server/superset/routes.ts | 27 +++++++++++++++++++-- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/client/src/components/SupersetDashboard.tsx b/client/src/components/SupersetDashboard.tsx index c465bce..f4d31c0 100644 --- a/client/src/components/SupersetDashboard.tsx +++ b/client/src/components/SupersetDashboard.tsx @@ -27,7 +27,8 @@ export function SupersetDashboard({ const [status, setStatus] = useState<"loading" | "ready" | "error" | "unavailable">("loading"); const [errorMsg, setErrorMsg] = useState(""); - const fetchGuestToken = useCallback(async (): Promise => { + // Busca token e embeddedId (UUID) do backend + const fetchTokenData = useCallback(async (): Promise<{ token: string; embeddedId: string }> => { const resp = await fetch("/api/superset/guest-token", { method: "POST", headers: { "Content-Type": "application/json" }, @@ -38,8 +39,7 @@ export function SupersetDashboard({ const err = await resp.json().catch(() => ({ error: "Erro desconhecido" })); throw new Error(err.error || `HTTP ${resp.status}`); } - const { token } = await resp.json(); - return token; + return resp.json(); }, [dashboardId]); const loadDashboard = useCallback(async () => { @@ -56,6 +56,9 @@ export function SupersetDashboard({ return; } + // Resolve embeddedId (UUID) e obtém token inicial + const { embeddedId } = await fetchTokenData(); + // Importa SDK dinamicamente (só quando necessário) const { embedDashboard } = await import("@superset-ui/embedded-sdk"); @@ -65,10 +68,13 @@ export function SupersetDashboard({ } sdkRef.current = await embedDashboard({ - id: dashboardId, + id: embeddedId, // UUID do embedded config (não o slug) supersetDomain: window.location.origin + "/superset", mountPoint: containerRef.current, - fetchGuestToken, + fetchGuestToken: async () => { + const { token } = await fetchTokenData(); + return token; + }, dashboardUiConfig: { hideTitle: true, hideChartControls: false, diff --git a/server/superset/routes.ts b/server/superset/routes.ts index 48d5623..fd6c378 100644 --- a/server/superset/routes.ts +++ b/server/superset/routes.ts @@ -39,6 +39,29 @@ export function registerSupersetRoutes(app: Express): void { const token = await getServiceToken(); const user = req.user as any; + // Resolve slug → dashboard ID → embedded UUID + const dashResp = await fetch(`${SUPERSET_URL}/api/v1/dashboard/${dashboardId}`, { + headers: { "Authorization": `Bearer ${token}` }, + }); + if (!dashResp.ok) { + return res.status(404).json({ error: `Dashboard '${dashboardId}' não encontrado no Superset` }); + } + const dashData = await dashResp.json(); + const dbNumericId = dashData.result?.id; + + const embeddedResp = await fetch(`${SUPERSET_URL}/api/v1/dashboard/${dbNumericId}/embedded`, { + headers: { "Authorization": `Bearer ${token}` }, + }); + if (!embeddedResp.ok) { + return res.status(404).json({ error: `Dashboard '${dashboardId}' não tem embedding habilitado` }); + } + const embeddedData = await embeddedResp.json(); + const embeddedUuid = embeddedData.result?.uuid; + + if (!embeddedUuid) { + return res.status(404).json({ error: `UUID de embedding não encontrado para '${dashboardId}'` }); + } + const guestResp = await fetch(`${SUPERSET_URL}/api/v1/security/guest_token/`, { method: "POST", headers: { @@ -51,7 +74,7 @@ export function registerSupersetRoutes(app: Express): void { first_name: user?.name?.split(" ")[0] || "Arcádia", last_name: user?.name?.split(" ").slice(1).join(" ") || "User", }, - resources: [{ type: "dashboard", id: dashboardId }], + resources: [{ type: "dashboard", id: embeddedUuid }], rls: user?.tenantId ? [{ clause: `tenant_id = ${user.tenantId}` }] : [], }), }); @@ -62,7 +85,7 @@ export function registerSupersetRoutes(app: Express): void { } const { token: guestToken } = await guestResp.json(); - res.json({ token: guestToken, supersetUrl: "/superset" }); + res.json({ token: guestToken, embeddedId: embeddedUuid, supersetUrl: "/superset" }); } catch (err: any) { console.error("[Superset] guest-token error:", err.message); res.status(502).json({ error: err.message });