diff --git a/web-admin/src/features/dashboards/listing/selectors.ts b/web-admin/src/features/dashboards/listing/selectors.ts index d59facf3f19..74f8654e8dd 100644 --- a/web-admin/src/features/dashboards/listing/selectors.ts +++ b/web-admin/src/features/dashboards/listing/selectors.ts @@ -42,6 +42,7 @@ export function useDashboards( select: (data) => { return data.resources.filter((res) => res.canvas || res.explore); }, + enabled: !!instanceId, refetchInterval: createSmartRefetchInterval, }, }); diff --git a/web-admin/src/routes/+layout.ts b/web-admin/src/routes/+layout.ts index 539ea2d20a7..ef9d4f9f761 100644 --- a/web-admin/src/routes/+layout.ts +++ b/web-admin/src/routes/+layout.ts @@ -23,7 +23,6 @@ import { fetchProjectDeploymentDetails } from "@rilldata/web-admin/features/proj import { getOrgWithBearerToken } from "@rilldata/web-admin/features/public-urls/get-org-with-bearer-token"; import { initPosthog } from "@rilldata/web-common/lib/analytics/posthog"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient.js"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import { error, redirect, type Page } from "@sveltejs/kit"; import { isAxiosError } from "axios"; import { Settings } from "luxon"; @@ -136,14 +135,6 @@ export const load = async ({ params, url, route, depends }) => { runtime: runtimeData, } = await fetchProjectDeploymentDetails(organization, project, token); - await runtime.setRuntime( - queryClient, - runtimeData.host ?? "", - runtimeData.instanceId, - runtimeData.jwt?.token, - runtimeData.jwt?.authContext, - ); - return { user, organizationPermissions, diff --git a/web-admin/src/routes/-/embed/+layout.svelte b/web-admin/src/routes/-/embed/+layout.svelte index 86636872ca7..6af876fc44c 100644 --- a/web-admin/src/routes/-/embed/+layout.svelte +++ b/web-admin/src/routes/-/embed/+layout.svelte @@ -19,13 +19,15 @@ } from "@rilldata/web-common/lib/rpc"; import ErrorPage from "@rilldata/web-common/components/ErrorPage.svelte"; import type { PageData } from "./$types"; + import RuntimeProvider from "@rilldata/web-common/runtime-client/RuntimeProvider.svelte"; export let data: PageData; const { instanceId, - missingRequireParams, navigationEnabled, + runtimeHost, + accessToken, } = data; const { dashboardChat } = featureFlags; @@ -109,25 +111,32 @@ fatal /> {:else} - {#if showTopBar} -
- -
- {/if} + + {#if showTopBar} +
+ +
+ {/if} -
-
- +
+
+ +
+ {#if $dashboardChat && activeResource?.kind === ResourceKind.Explore} + + {/if}
- {#if $dashboardChat && activeResource?.kind === ResourceKind.Explore} - - {/if} -
+ {/if} diff --git a/web-admin/src/routes/-/embed/+layout.ts b/web-admin/src/routes/-/embed/+layout.ts index 58f66473381..e7e440f7586 100644 --- a/web-admin/src/routes/-/embed/+layout.ts +++ b/web-admin/src/routes/-/embed/+layout.ts @@ -2,8 +2,6 @@ import { EmbedStore } from "@rilldata/web-common/features/embeds/embed-store.ts" import { removeEmbedParams } from "@rilldata/web-admin/features/embeds/init-embed-public-api.ts"; import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors.ts"; import { redirect } from "@sveltejs/kit"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store.ts"; -import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient.ts"; export const load = async ({ url }) => { const embedStore = EmbedStore.getInstance(); @@ -36,14 +34,6 @@ export const load = async ({ url }) => { visibleExplores, } = embedStore; - await runtime.setRuntime( - queryClient, - runtimeHost, - instanceId, - accessToken, - "embed", - ); - return { instanceId, runtimeHost, diff --git a/web-admin/src/routes/-/embed/canvas/[name]/+page.svelte b/web-admin/src/routes/-/embed/canvas/[name]/+page.svelte index a2ca814dd0b..ceffe8243ce 100644 --- a/web-admin/src/routes/-/embed/canvas/[name]/+page.svelte +++ b/web-admin/src/routes/-/embed/canvas/[name]/+page.svelte @@ -8,6 +8,8 @@ $: ({ canvasName, instanceId, navigationEnabled } = data); - - - +{#key instanceId} + + + +{/key} diff --git a/web-admin/src/routes/[organization]/[project]/-/ai/+layout.ts b/web-admin/src/routes/[organization]/[project]/-/ai/+layout.ts index 93caf27dbb2..d06fb2b190a 100644 --- a/web-admin/src/routes/[organization]/[project]/-/ai/+layout.ts +++ b/web-admin/src/routes/[organization]/[project]/-/ai/+layout.ts @@ -2,9 +2,8 @@ import { getLastConversationId, setLastConversationId, } from "@rilldata/web-common/features/chat/layouts/fullpage/fullpage-store"; -import { featureFlags } from "@rilldata/web-common/features/feature-flags.js"; +import { getFeatureFlags } from "@rilldata/web-common/features/feature-flags.js"; import { redirect } from "@sveltejs/kit"; -import { get } from "svelte/store"; export const load = async ({ params: { organization, project, conversationId }, @@ -13,15 +12,13 @@ export const load = async ({ parent, }) => { // Wait for the feature flags to load - await parent(); + const { runtime } = await parent(); - // There is a potential race condition where feature flags from instance is not loaded yet. - // So wait until it is ready before checking for "chat" - await featureFlags.ready; + const fetchedFeatureFlags = await getFeatureFlags(runtime); // Redirect to `/-/dashboards` if chat feature is disabled // NOTE: In the future, we'll use user-level `ai` permissions for more granular access control - const chatEnabled = get(featureFlags.chat); + const chatEnabled = Boolean(fetchedFeatureFlags.chat); if (!chatEnabled) { throw redirect(307, `/${organization}/${project}/-/dashboards`); } diff --git a/web-admin/src/routes/[organization]/[project]/-/alerts/[alert]/+layout.ts b/web-admin/src/routes/[organization]/[project]/-/alerts/[alert]/+layout.ts index e5b659c85e5..921436f721e 100644 --- a/web-admin/src/routes/[organization]/[project]/-/alerts/[alert]/+layout.ts +++ b/web-admin/src/routes/[organization]/[project]/-/alerts/[alert]/+layout.ts @@ -9,7 +9,7 @@ export async function load({ params, parent }) { const alertData = await queryClient .fetchQuery( - getRuntimeServiceGetResourceQueryOptions(runtime.instanceId, { + getRuntimeServiceGetResourceQueryOptions(runtime, { "name.kind": ResourceKind.Alert, "name.name": params.alert, }), diff --git a/web-admin/src/routes/[organization]/[project]/-/open-query/+page.ts b/web-admin/src/routes/[organization]/[project]/-/open-query/+page.ts index e686e560d6c..114286d7d87 100644 --- a/web-admin/src/routes/[organization]/[project]/-/open-query/+page.ts +++ b/web-admin/src/routes/[organization]/[project]/-/open-query/+page.ts @@ -3,12 +3,13 @@ import type { PageLoad } from "./$types"; export const load: PageLoad = async ({ params, url, parent }) => { // Only proceed once the runtime in parent is ready - await parent(); + const parentData = await parent(); // Get the organization and project from the URL const organization = params.organization; const project = params.project; + const runtime = parentData.runtime; // Open the query (this'll redirect to the relevant Explore page) - await openQuery({ url, organization, project }); + await openQuery({ url, organization, project, runtime }); }; diff --git a/web-admin/src/routes/[organization]/[project]/-/reports/[report]/+layout.ts b/web-admin/src/routes/[organization]/[project]/-/reports/[report]/+layout.ts index 84239f1d323..80f70692a77 100644 --- a/web-admin/src/routes/[organization]/[project]/-/reports/[report]/+layout.ts +++ b/web-admin/src/routes/[organization]/[project]/-/reports/[report]/+layout.ts @@ -9,7 +9,7 @@ export async function load({ params, parent }) { const reportData = await queryClient .fetchQuery( - getRuntimeServiceGetResourceQueryOptions(runtime.instanceId, { + getRuntimeServiceGetResourceQueryOptions(runtime, { "name.kind": ResourceKind.Report, "name.name": params.report, }), diff --git a/web-admin/src/routes/[organization]/[project]/canvas/[dashboard]/+page.svelte b/web-admin/src/routes/[organization]/[project]/canvas/[dashboard]/+page.svelte index 0d51586e43a..6342cd40fc4 100644 --- a/web-admin/src/routes/[organization]/[project]/canvas/[dashboard]/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/canvas/[dashboard]/+page.svelte @@ -13,6 +13,8 @@ $: ({ instanceId } = $runtime); - - - +{#key instanceId} + + + +{/key} diff --git a/web-admin/tests/README.md b/web-admin/tests/README.md index 43058531dbf..555554d68d0 100644 --- a/web-admin/tests/README.md +++ b/web-admin/tests/README.md @@ -11,7 +11,7 @@ Playwright recommends that global setup takes place in a dedicated Playwright "p 1. Starts a fresh instance of Rill Cloud 2. Logs-in via the e2e-admin@rilldata.com user (which has been pre-populated in our Auth0 staging database) 3. Creates an organization named `e2e` -4. Deploys the OpenRTB project +4. Deploys the OpenRTB and AdBids projects 5. Waits for data ingestion and asserts when the primary dashboard is ready-to-go ```bash diff --git a/web-admin/tests/canvas.spec.ts b/web-admin/tests/canvas.spec.ts index fae68017dc5..63c50d1c6ca 100644 --- a/web-admin/tests/canvas.spec.ts +++ b/web-admin/tests/canvas.spec.ts @@ -15,4 +15,23 @@ test.describe("Canvases", () => { page.getByText("Advertising Spend Overall $3,900"), ).toBeVisible(); }); + + test("can switch projects from canvas", async ({ page }) => { + // Navigate to canvas + await page.goto("/e2e/openrtb/-/dashboards"); + await page + .getByRole("link", { name: "Bids Canvas Dashboard" }) + .first() + .click(); + + // navigate via breadcrumbs to another project + await page + .getByRole("button", { name: "Breadcrumb dropdown" }) + .first() + .click(); + + await page.getByRole("link", { name: "AdBids" }).click(); + + await expect(page.getByText("Adbids Canvas Dashboard")).toBeVisible(); + }); }); diff --git a/web-admin/tests/setup/setup.ts b/web-admin/tests/setup/setup.ts index 09f68cbdb7f..ce2ddd7dd07 100644 --- a/web-admin/tests/setup/setup.ts +++ b/web-admin/tests/setup/setup.ts @@ -261,4 +261,68 @@ setup.describe("global setup", () => { }), ).toContainText("Last refreshed", { timeout: 15_000 }); }); + + setup("should deploy the AdBids project", async ({ adminPage }) => { + // increase project quota for the organization + const { stdout: quotaUpdateStdout } = await execAsync( + `rill sudo quota set --org ${RILL_ORG_NAME} --projects 10`, + ); + expect(quotaUpdateStdout).toContain(`Projects: 10`); + + // Deploy the AdBids project + const { match } = await spawnAndMatch( + "rill", + [ + "deploy", + "--path", + "../web-common/tests/projects/AdBids", + "--project", + "AdBids", + "--archive", + "--interactive=false", + ], + /https?:\/\/[^\s]+/, + ); + + // Navigate to the project URL and expect to see the successful deployment + const url = match[0]; + await adminPage.goto(url); + await expect( + adminPage.getByRole("link", { name: RILL_ORG_NAME }), + ).toBeVisible(); // Organization breadcrumb + await expect( + adminPage.getByRole("link", { name: "AdBids", exact: true }), + ).toBeVisible(); // Project breadcrumb + + // Expect to land on the project home page + await adminPage.waitForURL(`/${RILL_ORG_NAME}/AdBids`); + // Temporary fix to wait for the project to be ready. + // TODO: add a refetch to the project API + await expect + .poll( + async () => { + await adminPage.reload(); + return adminPage.getByLabel("Project title").textContent(); + }, + { intervals: Array(4).fill(30_000), timeout: 120_000 }, + ) + .toContain(`Welcome to Untitled Rill Project`); + + // Navigate to the dashboards page to validate the deployment + await adminPage.getByRole("link", { name: "Dashboards" }).click(); + await adminPage.waitForURL("**/-/dashboards"); + + // Wait for the project to be ready + await expect(adminPage.getByLabel("Container title")).toHaveText( + "Project dashboards", + ); + + // Check that the dashboards are listed + await expect( + adminPage.getByRole("link", { name: "Adbids Canvas Dashboard" }).first(), + ).toBeVisible(); + await expect( + adminPage.getByRole("link", { name: "Adbids dashboard" }), + ).toBeVisible(); + }); }); diff --git a/web-common/src/features/canvas/CanvasInitialization.svelte b/web-common/src/features/canvas/CanvasInitialization.svelte index a03bc7d2fbd..5b44bca537e 100644 --- a/web-common/src/features/canvas/CanvasInitialization.svelte +++ b/web-common/src/features/canvas/CanvasInitialization.svelte @@ -85,6 +85,7 @@ fetchedCanvas, isReconciling, existingStore, + instanceId, ); $: ready = !!resolvedStore; @@ -146,6 +147,7 @@ fetchedCanvas: V1ResolveCanvasResponse | undefined, isReconciling: boolean, existingStore: CanvasStore | undefined, + instanceId: string, ) { if (fetchedCanvas && !isReconciling) { const metricsViews: Record = {}; diff --git a/web-common/src/features/canvas/selector.ts b/web-common/src/features/canvas/selector.ts index ca581c5d792..b42eac3c4e1 100644 --- a/web-common/src/features/canvas/selector.ts +++ b/web-common/src/features/canvas/selector.ts @@ -125,7 +125,7 @@ export function useCanvas( }; }, - enabled: !!canvasName, + enabled: !!canvasName && !!instanceId, ...queryOptions, }, }, diff --git a/web-common/src/features/explore-mappers/open-query.ts b/web-common/src/features/explore-mappers/open-query.ts index 5818e5eccf8..c8c8b4bdde2 100644 --- a/web-common/src/features/explore-mappers/open-query.ts +++ b/web-common/src/features/explore-mappers/open-query.ts @@ -1,25 +1,35 @@ import type { ExploreState } from "@rilldata/web-common/features/dashboards/stores/explore-state"; import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors.js"; -import { generateExploreLink } from "@rilldata/web-common/features/explore-mappers/generate-explore-link"; +import { getUrlForExplore } from "@rilldata/web-common/features/explore-mappers/generate-explore-link"; import { mapMetricsResolverQueryToDashboard } from "@rilldata/web-common/features/explore-mappers/map-metrics-resolver-query-to-dashboard.ts"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; import { + getQueryServiceMetricsViewTimeRangeQueryKey, getRuntimeServiceGetExploreQueryKey, getRuntimeServiceListResourcesQueryKey, - runtimeServiceGetExplore, - runtimeServiceListResources, + type V1ExploreSpec, + type V1GetExploreResponse, + type V1ListResourcesResponse, + type V1MetricsViewSpec, + type V1MetricsViewTimeRangeResponse, } from "@rilldata/web-common/runtime-client"; import type { Schema as MetricsResolverQuery } from "@rilldata/web-common/runtime-client/gen/resolvers/metrics/schema.ts"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import { error, redirect } from "@sveltejs/kit"; -import { get } from "svelte/store"; +import type { Runtime } from "@rilldata/web-common/runtime-client/runtime-store.ts"; +import httpClient from "@rilldata/web-common/runtime-client/http-client.ts"; +import { getTimeControlState } from "@rilldata/web-common/features/dashboards/time-controls/time-control-store.ts"; +import { convertPartialExploreStateToUrlParams } from "@rilldata/web-common/features/dashboards/url-state/convert-partial-explore-state-to-url-params.ts"; +import { createLinkError } from "@rilldata/web-common/features/explore-mappers/explore-validation.ts"; +import { ExploreLinkErrorType } from "@rilldata/web-common/features/explore-mappers/types.ts"; export async function openQuery({ url, organization, project, + runtime, }: { url: URL; + runtime: Runtime; organization?: string; project?: string; }) { @@ -49,14 +59,29 @@ export async function openQuery({ } // Find an explore dashboard that uses this metrics view - const exploreName = await findExploreForMetricsView(metricsViewName); + const exploreName = await findExploreForMetricsView( + runtime, + metricsViewName, + ); + + const { metricsViewSpec, exploreSpec } = await getExploreSpecs( + runtime, + exploreName, + ); // Convert query to ExploreState - const exploreState = await convertQueryToExploreState(query, exploreName); + const exploreState = mapMetricsResolverQueryToDashboard( + metricsViewSpec, + exploreSpec, + query, + ); // Generate the final explore URL exploreURL = await generateExploreLink( + runtime, exploreState, + metricsViewSpec, + exploreSpec, exploreName, organization, project, @@ -81,21 +106,27 @@ export async function openQuery({ * TODO: try to find an explore that has as many measures/dimensions in the query */ async function findExploreForMetricsView( + runtime: Runtime, metricsViewName: string, ): Promise { - const instanceId = get(runtime).instanceId; - // List all explore resources const exploreResources = await queryClient.fetchQuery({ - queryKey: getRuntimeServiceListResourcesQueryKey(instanceId, { + queryKey: getRuntimeServiceListResourcesQueryKey(runtime.instanceId, { kind: ResourceKind.Explore, }), queryFn: ({ signal }) => - runtimeServiceListResources( - instanceId, - { kind: ResourceKind.Explore }, + httpClient({ + url: `/v1/instances/${runtime.instanceId}/resources`, + method: "GET", + params: { kind: ResourceKind.Explore }, signal, - ), + baseUrl: runtime.host, + headers: runtime.jwt + ? { + Authorization: `Bearer ${runtime.jwt?.token}`, + } + : undefined, + }), }); // Look for an explore that references this metrics view @@ -113,28 +144,25 @@ async function findExploreForMetricsView( ); } -/** - * Convert Query directly to ExploreState without going through URL parameters. - */ -async function convertQueryToExploreState( - query: MetricsResolverQuery, - exploreName: string, -): Promise> { - const instanceId = get(runtime).instanceId; - +async function getExploreSpecs(runtime: Runtime, exploreName: string) { // Get explore and metrics view specs const getExploreResponse = await queryClient.fetchQuery({ - queryKey: getRuntimeServiceGetExploreQueryKey(instanceId, { + queryKey: getRuntimeServiceGetExploreQueryKey(runtime.instanceId, { name: exploreName, }), queryFn: ({ signal }) => - runtimeServiceGetExplore( - instanceId, - { - name: exploreName, - }, + httpClient({ + url: `/v1/instances/${runtime.instanceId}/resources/explore`, + method: "GET", + params: { name: exploreName }, signal, - ), + baseUrl: runtime.host, + headers: runtime.jwt + ? { + Authorization: `Bearer ${runtime.jwt?.token}`, + } + : undefined, + }), }); const exploreResource = getExploreResponse.explore; const metricsViewResource = getExploreResponse.metricsView; @@ -150,8 +178,79 @@ async function convertQueryToExploreState( const metricsViewSpec = metricsViewResource?.metricsView?.state?.validSpec; const exploreSpec = exploreResource.explore.state.validSpec; - const partialExploreState: Partial = - mapMetricsResolverQueryToDashboard(metricsViewSpec, exploreSpec, query); + return { metricsViewSpec, exploreSpec }; +} + +/** + * Generates the explore page URL with proper search parameters + */ +async function generateExploreLink( + runtime: Runtime, + exploreState: Partial, + metricsViewSpec: V1MetricsViewSpec, + exploreSpec: V1ExploreSpec, + exploreName: string, + organization?: string | undefined, + project?: string | undefined, +): Promise { + try { + // Build base URL + const url = getUrlForExplore(exploreName, organization, project); + + const metricsViewName = exploreSpec.metricsView; + let fullTimeRange: V1MetricsViewTimeRangeResponse | undefined; + if (metricsViewSpec.timeDimension && metricsViewName) { + fullTimeRange = await queryClient.fetchQuery({ + queryFn: ({ signal }) => + httpClient({ + url: `/v1/instances/${runtime.instanceId}/queries/metrics-views/${metricsViewName}/time-range-summary`, + method: "POST", + headers: { + "Content-Type": "application/json", + ...(runtime.jwt + ? { + Authorization: `Bearer ${runtime.jwt?.token}`, + } + : {}), + }, + data: {}, + signal, + baseUrl: runtime.host, + }), + queryKey: getQueryServiceMetricsViewTimeRangeQueryKey( + runtime.instanceId, + metricsViewName, + {}, + ), + staleTime: Infinity, + gcTime: Infinity, + }); + } - return partialExploreState; + // This is just for an initial redirect. + // DashboardStateDataLoader will handle compression etc. during init + // So no need to use getCleanedUrlParamsForGoto + const searchParams = convertPartialExploreStateToUrlParams( + exploreSpec, + exploreState, + getTimeControlState( + metricsViewSpec, + exploreSpec, + fullTimeRange?.timeRangeSummary, + exploreState, + ), + ); + + searchParams.forEach((value, key) => { + url.searchParams.set(key, value); + }); + + return url.toString(); + } catch (error) { + throw createLinkError( + ExploreLinkErrorType.TRANSFORMATION_ERROR, + `Failed to generate explore link: ${error.message}`, + error, + ); + } } diff --git a/web-common/src/features/feature-flags.ts b/web-common/src/features/feature-flags.ts index 9d736fcea3a..a838158aef6 100644 --- a/web-common/src/features/feature-flags.ts +++ b/web-common/src/features/feature-flags.ts @@ -2,9 +2,12 @@ import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryCl import { writable } from "svelte/store"; import { createRuntimeServiceGetInstance, + getRuntimeServiceGetInstanceQueryKey, + type V1GetInstanceResponse, type V1InstanceFeatureFlags, } from "../runtime-client"; -import { runtime } from "../runtime-client/runtime-store"; +import { type Runtime, runtime } from "../runtime-client/runtime-store"; +import httpClient from "@rilldata/web-common/runtime-client/http-client.ts"; class FeatureFlag { private _internal = false; @@ -123,3 +126,25 @@ class FeatureFlags { } export const featureFlags = new FeatureFlags(); + +export async function getFeatureFlags(runtime: Runtime) { + const instanceResp = await queryClient.fetchQuery({ + queryKey: getRuntimeServiceGetInstanceQueryKey( + runtime.instanceId, + undefined, + ), + queryFn: () => + httpClient({ + url: `/v1/instances/${runtime.instanceId}`, + method: "GET", + baseUrl: runtime.host, + headers: runtime.jwt + ? { + Authorization: `Bearer ${runtime.jwt?.token}`, + } + : undefined, + }), + }); + + return instanceResp.instance?.featureFlags ?? {}; +} diff --git a/web-common/src/features/workspaces/CanvasWorkspace.svelte b/web-common/src/features/workspaces/CanvasWorkspace.svelte index 42459b480d5..2dfdaac1a9d 100644 --- a/web-common/src/features/workspaces/CanvasWorkspace.svelte +++ b/web-common/src/features/workspaces/CanvasWorkspace.svelte @@ -121,20 +121,18 @@ {lineBasedRuntimeErrors} /> {:else if selectedView === "viz"} - {#if ready} - - - - {/if} + + + {/if} diff --git a/web-common/src/runtime-client/http-client.ts b/web-common/src/runtime-client/http-client.ts index b51c75331ea..6a5fcb1aa6e 100644 --- a/web-common/src/runtime-client/http-client.ts +++ b/web-common/src/runtime-client/http-client.ts @@ -23,9 +23,10 @@ export const httpClient = async ( ): Promise => { // Naive request interceptors + const interceptedConfig = { ...config }; // Set host const host = get(runtime).host; - const interceptedConfig = { ...config, baseUrl: host }; + if (host) interceptedConfig.baseUrl = host; // Set JWT let jwt = get(runtime).jwt; diff --git a/web-common/src/runtime-client/http-request-queue/HttpRequestQueue.ts b/web-common/src/runtime-client/http-request-queue/HttpRequestQueue.ts index 8b2a9fe4850..5158ecb3ab8 100644 --- a/web-common/src/runtime-client/http-request-queue/HttpRequestQueue.ts +++ b/web-common/src/runtime-client/http-request-queue/HttpRequestQueue.ts @@ -51,7 +51,7 @@ export class HttpRequestQueue { public add(requestOptions: FetchWrapperOptions) { // prepend after parsing to make parsing faster - requestOptions.url = `${requestOptions?.baseUrl}${requestOptions.url}`; + requestOptions.url = `${requestOptions?.baseUrl ?? ""}${requestOptions.url}`; const urlMatch = UrlExtractorRegex.exec(requestOptions.url); diff --git a/web-common/src/runtime-client/query-options.ts b/web-common/src/runtime-client/query-options.ts index baab2c7e692..bbc7a29cd45 100644 --- a/web-common/src/runtime-client/query-options.ts +++ b/web-common/src/runtime-client/query-options.ts @@ -1,18 +1,31 @@ import type { FetchQueryOptions } from "@tanstack/svelte-query"; -import type { RuntimeServiceGetResourceParams } from "./gen/index.schemas"; +import type { + RuntimeServiceGetResourceParams, + V1GetResourceResponse, +} from "./gen/index.schemas"; import type { RuntimeServiceGetResourceQueryResult } from "./gen/runtime-service/runtime-service"; -import { - getRuntimeServiceGetResourceQueryKey, - runtimeServiceGetResource, -} from "./gen/runtime-service/runtime-service"; +import { getRuntimeServiceGetResourceQueryKey } from "./gen/runtime-service/runtime-service"; +import httpClient from "@rilldata/web-common/runtime-client/http-client.ts"; +import type { Runtime } from "@rilldata/web-common/runtime-client/runtime-store.ts"; export function getRuntimeServiceGetResourceQueryOptions( - instanceId: string, + runtime: Runtime, params: RuntimeServiceGetResourceParams, ) { return >{ - queryKey: getRuntimeServiceGetResourceQueryKey(instanceId, params), - queryFn: () => runtimeServiceGetResource(instanceId, params), + queryKey: getRuntimeServiceGetResourceQueryKey(runtime.instanceId, params), + queryFn: () => + httpClient({ + url: `/v1/instances/${runtime.instanceId}/resource`, + method: "GET", + params, + baseUrl: runtime.host, + headers: runtime.jwt + ? { + Authorization: `Bearer ${runtime.jwt?.token}`, + } + : undefined, + }), staleTime: Infinity, }; } diff --git a/web-local/src/routes/(viz)/-/open-query/+page.ts b/web-local/src/routes/(viz)/-/open-query/+page.ts index 7b927a008af..41913ceaffa 100644 --- a/web-local/src/routes/(viz)/-/open-query/+page.ts +++ b/web-local/src/routes/(viz)/-/open-query/+page.ts @@ -1,6 +1,10 @@ import { openQuery } from "@rilldata/web-common/features/explore-mappers/open-query"; +import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; +import { get } from "svelte/store"; export async function load({ url }) { + const rt = get(runtime); + // Open the query (this'll redirect to the relevant Explore page) - await openQuery({ url }); + await openQuery({ url, runtime: rt }); } diff --git a/web-local/src/routes/(viz)/canvas/[name]/+page.svelte b/web-local/src/routes/(viz)/canvas/[name]/+page.svelte index ea60dfe8220..83f6c69c117 100644 --- a/web-local/src/routes/(viz)/canvas/[name]/+page.svelte +++ b/web-local/src/routes/(viz)/canvas/[name]/+page.svelte @@ -10,6 +10,8 @@ $: ({ canvasName } = data); - - - +{#key instanceId} + + + +{/key}