Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions web-admin/src/features/dashboards/listing/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export function useDashboards(
select: (data) => {
return data.resources.filter((res) => res.canvas || res.explore);
},
enabled: !!instanceId,
refetchInterval: createSmartRefetchInterval,
},
});
Expand Down
9 changes: 0 additions & 9 deletions web-admin/src/routes/+layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -136,14 +135,6 @@ export const load = async ({ params, url, route, depends }) => {
runtime: runtimeData,
} = await fetchProjectDeploymentDetails(organization, project, token);

await runtime.setRuntime(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will cause issues in other places that make a call to runtime in loader function. We should either replace all instances of such calls or keep this and revert back to how things worked before #8391

Copy link
Contributor Author

@briangregoryholmes briangregoryholmes Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the main point of this PR. There shouldn't be any calls/subscriptions to the runtime store in the loader functions. I just checked and really only found a call to openQuery, which I've refactored slightly. Please let me know if there are any others you're aware of. #8391 did not change anything meaningfully related to this behavior, it simply exposed that there was an underlying issue.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated a few more routes. Also fixed openQuery, it was passing jwt and host.

queryClient,
runtimeData.host ?? "",
runtimeData.instanceId,
runtimeData.jwt?.token,
runtimeData.jwt?.authContext,
);

return {
user,
organizationPermissions,
Expand Down
49 changes: 29 additions & 20 deletions web-admin/src/routes/-/embed/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -109,25 +111,32 @@
fatal
/>
{:else}
{#if showTopBar}
<div
class="flex items-center w-full pr-4 py-1 min-h-[2.5rem]"
class:border-b={!onProjectPage}
>
<TopNavigationBarEmbed
{instanceId}
{activeResource}
{navigationEnabled}
/>
</div>
{/if}
<RuntimeProvider
{instanceId}
host={runtimeHost}
jwt={accessToken}
authContext="embed"
>
{#if showTopBar}
<div
class="flex items-center w-full pr-4 py-1 min-h-[2.5rem]"
class:border-b={!onProjectPage}
>
<TopNavigationBarEmbed
{instanceId}
{activeResource}
{navigationEnabled}
/>
</div>
{/if}

<div class="flex h-full overflow-hidden">
<div class="flex-1 overflow-hidden">
<slot />
<div class="flex h-full overflow-hidden">
<div class="flex-1 overflow-hidden">
<slot />
</div>
{#if $dashboardChat && activeResource?.kind === ResourceKind.Explore}
<ExploreChat />
{/if}
</div>
{#if $dashboardChat && activeResource?.kind === ResourceKind.Explore}
<ExploreChat />
{/if}
</div>
</RuntimeProvider>
{/if}
10 changes: 0 additions & 10 deletions web-admin/src/routes/-/embed/+layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -36,14 +34,6 @@ export const load = async ({ url }) => {
visibleExplores,
} = embedStore;

await runtime.setRuntime(
queryClient,
runtimeHost,
instanceId,
accessToken,
"embed",
);

return {
instanceId,
runtimeHost,
Expand Down
8 changes: 5 additions & 3 deletions web-admin/src/routes/-/embed/canvas/[name]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
$: ({ canvasName, instanceId, navigationEnabled } = data);
</script>

<CanvasProvider {canvasName} {instanceId} showBanner>
<CanvasDashboardEmbed {canvasName} {navigationEnabled} />
</CanvasProvider>
{#key instanceId}
<CanvasProvider {canvasName} {instanceId} showBanner>
<CanvasDashboardEmbed {canvasName} {navigationEnabled} />
</CanvasProvider>
{/key}
11 changes: 4 additions & 7 deletions web-admin/src/routes/[organization]/[project]/-/ai/+layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand All @@ -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`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
};
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
$: ({ instanceId } = $runtime);
</script>

<CanvasProvider {canvasName} {instanceId} {projectId} showBanner>
<CanvasDashboardEmbed {canvasName} />
</CanvasProvider>
{#key instanceId}
<CanvasProvider {canvasName} {instanceId} {projectId} showBanner>
<CanvasDashboardEmbed {canvasName} />
</CanvasProvider>
{/key}
2 changes: 1 addition & 1 deletion web-admin/tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 19 additions & 0 deletions web-admin/tests/canvas.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
});
64 changes: 64 additions & 0 deletions web-admin/tests/setup/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
});
2 changes: 2 additions & 0 deletions web-common/src/features/canvas/CanvasInitialization.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
fetchedCanvas,
isReconciling,
existingStore,
instanceId,
);

$: ready = !!resolvedStore;
Expand Down Expand Up @@ -146,6 +147,7 @@
fetchedCanvas: V1ResolveCanvasResponse | undefined,
isReconciling: boolean,
existingStore: CanvasStore | undefined,
instanceId: string,
) {
if (fetchedCanvas && !isReconciling) {
const metricsViews: Record<string, V1MetricsView | undefined> = {};
Expand Down
2 changes: 1 addition & 1 deletion web-common/src/features/canvas/selector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export function useCanvas(
};
},

enabled: !!canvasName,
enabled: !!canvasName && !!instanceId,
...queryOptions,
},
},
Expand Down
Loading
Loading