diff --git a/Makefile b/Makefile index d268f0d5e34..0e046eba401 100644 --- a/Makefile +++ b/Makefile @@ -56,6 +56,7 @@ proto.generate: cd proto && buf generate --exclude-path rill/ui cd proto && buf generate --template buf.gen.openapi-admin.yaml --path rill/admin cd proto && buf generate --template buf.gen.openapi-runtime.yaml --path rill/runtime + cd proto && buf generate --template buf.gen.runtime.yaml --path rill/runtime cd proto && buf generate --template buf.gen.local.yaml --path rill/local cd proto && buf generate --template buf.gen.ui.yaml go run -ldflags="-X main.Version=$(shell git describe --tags $(shell git rev-list --tags --max-count=1))" \ diff --git a/package-lock.json b/package-lock.json index 4ca4868f733..b15719f2e57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46870,7 +46870,6 @@ "match-sorter": "^6.3.1", "memoizee": "^0.4.17", "nearley": "^2.20.1", - "orval": "^7.8.0", "posthog-js": "^1.188.0", "prismjs": "^1.30.0", "regular-table": "^0.5.9", diff --git a/proto/buf.gen.runtime.yaml b/proto/buf.gen.runtime.yaml new file mode 100644 index 00000000000..fe6bc5ba89b --- /dev/null +++ b/proto/buf.gen.runtime.yaml @@ -0,0 +1,16 @@ +version: v2 +managed: + enabled: true + override: + - file_option: go_package_prefix + value: github.com/rilldata/rill/proto/gen + disable: + - module: buf.build/googleapis/googleapis + file_option: go_package_prefix + - module: buf.build/envoyproxy/protoc-gen-validate + file_option: go_package_prefix +plugins: + - remote: buf.build/connectrpc/es:v1.4.0 + out: ../web-common/src/proto/gen + opt: + - target=ts diff --git a/web-admin/src/features/alerts/CreateAlert.svelte b/web-admin/src/features/alerts/CreateAlert.svelte index acbfde64eea..3a48edfbff7 100644 --- a/web-admin/src/features/alerts/CreateAlert.svelte +++ b/web-admin/src/features/alerts/CreateAlert.svelte @@ -10,7 +10,7 @@ import AlertForm from "@rilldata/web-common/features/alerts/AlertForm.svelte"; import { useMetricsViewValidSpec } from "@rilldata/web-common/features/dashboards/selectors"; import { getStateManagers } from "@rilldata/web-common/features/dashboards/state-managers/state-managers"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { BellPlusIcon } from "lucide-svelte"; const { @@ -22,9 +22,9 @@ dashboardStore, } = getStateManagers(); - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); - $: metricsView = useMetricsViewValidSpec(instanceId, $metricsViewName); + $: metricsView = useMetricsViewValidSpec(runtimeClient, $metricsViewName); $: hasTimeDimension = !!$metricsView?.data?.timeDimension; let open = false; diff --git a/web-admin/src/features/alerts/history/AlertHistoryTable.svelte b/web-admin/src/features/alerts/history/AlertHistoryTable.svelte index 0fdc9a7cedc..4b5fd28a996 100644 --- a/web-admin/src/features/alerts/history/AlertHistoryTable.svelte +++ b/web-admin/src/features/alerts/history/AlertHistoryTable.svelte @@ -4,15 +4,15 @@ import { useAlert } from "@rilldata/web-admin/features/alerts/selectors"; import ResourceList from "@rilldata/web-admin/features/resources/ResourceList.svelte"; import type { V1AlertExecution } from "@rilldata/web-common/runtime-client/gen/index.schemas"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import type { ColumnDef } from "@tanstack/svelte-table"; import { flexRender } from "@tanstack/svelte-table"; export let alert: string; - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); - $: alertQuery = useAlert(instanceId, alert); + $: alertQuery = useAlert(runtimeClient, alert); /** * Table column definitions. diff --git a/web-admin/src/features/alerts/metadata/AlertFilters.svelte b/web-admin/src/features/alerts/metadata/AlertFilters.svelte index eed7b62db95..f709fb6642a 100644 --- a/web-admin/src/features/alerts/metadata/AlertFilters.svelte +++ b/web-admin/src/features/alerts/metadata/AlertFilters.svelte @@ -13,7 +13,7 @@ V1Expression, V1TimeRange, } from "@rilldata/web-common/runtime-client"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { flip } from "svelte/animate"; import { fly } from "svelte/transition"; @@ -36,9 +36,9 @@ whereFilter = dimensionFilters; havingFilter = dimensionThresholdFilters; } - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); - $: metricsView = useMetricsView(instanceId, metricsViewName); + $: metricsView = useMetricsView(runtimeClient, metricsViewName); $: dimensionIdMap = getMapFromArray( $metricsView.data?.metricsView?.state?.validSpec?.dimensions ?? [], (dimension) => dimension.name, diff --git a/web-admin/src/features/alerts/metadata/AlertMetadata.svelte b/web-admin/src/features/alerts/metadata/AlertMetadata.svelte index f02acf32984..ab43a19a811 100644 --- a/web-admin/src/features/alerts/metadata/AlertMetadata.svelte +++ b/web-admin/src/features/alerts/metadata/AlertMetadata.svelte @@ -1,5 +1,6 @@ diff --git a/web-admin/src/features/projects/download-report.ts b/web-admin/src/features/projects/download-report.ts index 6e8c38fed67..3755193b844 100644 --- a/web-admin/src/features/projects/download-report.ts +++ b/web-admin/src/features/projects/download-report.ts @@ -1,54 +1,52 @@ // TODO: move this file once other parts are merged +import { Timestamp } from "@bufbuild/protobuf"; import { - createQueryServiceExportReport, + queryServiceExportReport, type RpcStatus, } from "@rilldata/web-common/runtime-client"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { createMutation, type CreateMutationOptions, } from "@tanstack/svelte-query"; import type { MutationFunction } from "@tanstack/svelte-query"; -import { get } from "svelte/store"; export type DownloadReportRequest = { - instanceId: string; reportId: string; executionTime: string; originBaseUrl: string; + host: string; }; export function createDownloadReportMutation< TError = { response: { data: RpcStatus } }, TContext = unknown, ->(options?: { - mutation?: CreateMutationOptions< - Awaited>, - TError, - { data: DownloadReportRequest }, - TContext - >; -}) { +>( + client: RuntimeClient, + options?: { + mutation?: CreateMutationOptions< + Awaited>, + TError, + { data: DownloadReportRequest }, + TContext + >; + }, +) { const { mutation: mutationOptions } = options ?? {}; - const exporter = createQueryServiceExportReport(); const mutationFn: MutationFunction< Awaited>, { data: DownloadReportRequest } > = async (props) => { const { data } = props ?? {}; - if (!data.instanceId) throw new Error("Missing instanceId"); - const exportResp = await get(exporter).mutateAsync({ - instanceId: data.instanceId, + const exportResp = await queryServiceExportReport(client, { report: data.reportId, - data: { - executionTime: data.executionTime, - originBaseUrl: data.originBaseUrl, - }, + executionTime: Timestamp.fromJson(data.executionTime), + originBaseUrl: data.originBaseUrl, }); - const downloadUrl = `${get(runtime).host}${exportResp.downloadUrlPath}`; + const downloadUrl = `${data.host}${exportResp.downloadUrlPath}`; window.open(downloadUrl, "_self"); }; diff --git a/web-admin/src/features/projects/github/ProjectGithubConnection.svelte b/web-admin/src/features/projects/github/ProjectGithubConnection.svelte index 5df014200de..151e62dc312 100644 --- a/web-admin/src/features/projects/github/ProjectGithubConnection.svelte +++ b/web-admin/src/features/projects/github/ProjectGithubConnection.svelte @@ -7,12 +7,12 @@ getRepoNameFromGitRemote, getGitUrlFromRemote, } from "@rilldata/web-common/features/project/deploy/github-utils"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; export let organization: string; export let project: string; - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); $: proj = createAdminServiceGetProject(organization, project); $: ({ @@ -22,9 +22,9 @@ $: isGithubConnected = !!gitRemote; $: isManagedGit = !!managedGitId; $: repoName = getRepoNameFromGitRemote(gitRemote); - $: githubLastSynced = useGithubLastSynced(instanceId); + $: githubLastSynced = useGithubLastSynced(runtimeClient); $: dashboardsLastUpdated = useDashboardsLastUpdated( - instanceId, + runtimeClient, organization, project, ); diff --git a/web-admin/src/features/projects/selectors.ts b/web-admin/src/features/projects/selectors.ts index e3e870be916..cff2ecbe642 100644 --- a/web-admin/src/features/projects/selectors.ts +++ b/web-admin/src/features/projects/selectors.ts @@ -20,7 +20,7 @@ import { useResourceV2, } from "@rilldata/web-common/features/entity-management/resource-selectors"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; -import type { Runtime } from "@rilldata/web-common/runtime-client/runtime-store"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { derived, type Readable } from "svelte/store"; export function getProjectPermissions(orgName: string, projName: string) { @@ -137,20 +137,23 @@ export async function fetchProjectDeploymentDetails( return { projectPermissions: projResp.projectPermissions, project: projResp.project, - runtime: { - host: projResp.deployment?.runtimeHost, - instanceId: projResp.deployment?.runtimeInstanceId, - jwt: { - token: projResp.jwt, - authContext: token ? "magic" : "user", - }, + runtime: { + host: projResp.deployment?.runtimeHost ?? "", + instanceId: projResp.deployment?.runtimeInstanceId ?? "", + jwt: projResp.jwt + ? { + token: projResp.jwt, + receivedAt: Date.now(), + authContext: (token ? "magic" : "user") as string, + } + : undefined, }, }; } -export function useGithubLastSynced(instanceId: string) { +export function useGithubLastSynced(client: RuntimeClient) { return useResourceV2( - instanceId, + client, SingletonProjectParserName, ResourceKind.ProjectParser, { diff --git a/web-admin/src/features/projects/status/logs/ProjectLogsPage.svelte b/web-admin/src/features/projects/status/logs/ProjectLogsPage.svelte index 072418e1b78..3822a381fa4 100644 --- a/web-admin/src/features/projects/status/logs/ProjectLogsPage.svelte +++ b/web-admin/src/features/projects/status/logs/ProjectLogsPage.svelte @@ -5,7 +5,7 @@ SSEConnectionManager, ConnectionStatus, } from "@rilldata/web-common/runtime-client/sse-connection-manager"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { V1LogLevel, type V1Log } from "@rilldata/web-common/runtime-client"; import Search from "@rilldata/web-common/components/search/Search.svelte"; import * as DropdownMenu from "@rilldata/web-common/components/dropdown-menu"; @@ -17,6 +17,8 @@ parseStringParam, } from "../url-filter-sync"; + const runtimeClient = useRuntimeClient(); + const MAX_LOGS = 500; const REPLAY_LIMIT = 100; @@ -97,7 +99,7 @@ onMount(() => { mounted = true; - const { host, instanceId } = $runtime; + const { host, instanceId } = runtimeClient; if (!host || !instanceId) return; const url = `${host}/v1/instances/${instanceId}/sse?events=log&logs_replay=true&logs_replay_limit=${REPLAY_LIMIT}`; @@ -108,7 +110,9 @@ logsConnection.on("open", handleOpen), ]; - logsConnection.start(url); + logsConnection.start(url, { + getJwt: () => runtimeClient.getJwt(), + }); }); onDestroy(() => { @@ -152,12 +156,14 @@ } function retryConnection() { - const { host, instanceId } = $runtime; + const { host, instanceId } = runtimeClient; if (!host || !instanceId) return; connectionError = null; const url = `${host}/v1/instances/${instanceId}/sse?events=log&logs_replay=true&logs_replay_limit=${REPLAY_LIMIT}`; - logsConnection.start(url); + logsConnection.start(url, { + getJwt: () => runtimeClient.getJwt(), + }); } function getLevelClass(level: V1LogLevel | undefined): string { diff --git a/web-admin/src/features/projects/status/overview/DeploymentSection.svelte b/web-admin/src/features/projects/status/overview/DeploymentSection.svelte index 2d8c852367d..c7c45aa5872 100644 --- a/web-admin/src/features/projects/status/overview/DeploymentSection.svelte +++ b/web-admin/src/features/projects/status/overview/DeploymentSection.svelte @@ -6,7 +6,7 @@ import { useDashboardsLastUpdated } from "@rilldata/web-admin/features/dashboards/listing/selectors"; import { useGithubLastSynced } from "@rilldata/web-admin/features/projects/selectors"; import { createRuntimeServiceGetInstance } from "@rilldata/web-common/runtime-client"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { useProjectDeployment, useRuntimeVersion } from "../selectors"; import { formatEnvironmentName, @@ -21,7 +21,7 @@ export let organization: string; export let project: string; - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); // Deployment $: projectDeployment = useProjectDeployment(organization, project); @@ -34,20 +34,20 @@ $: projectData = $proj.data?.project; $: primaryBranch = projectData?.primaryBranch; // Last synced - $: githubLastSynced = useGithubLastSynced(instanceId); + $: githubLastSynced = useGithubLastSynced(runtimeClient); $: dashboardsLastUpdated = useDashboardsLastUpdated( - instanceId, + runtimeClient, organization, project, ); $: lastUpdated = $githubLastSynced.data ?? $dashboardsLastUpdated; // Runtime - $: runtimeVersionQuery = useRuntimeVersion(); + $: runtimeVersionQuery = useRuntimeVersion(runtimeClient); $: version = $runtimeVersionQuery.data?.version?.match(/v[\d.]+/)?.[0] ?? ""; // Connectors — sensitive: true is needed to read projectConnectors (OLAP/AI connector types) - $: instanceQuery = createRuntimeServiceGetInstance(instanceId, { + $: instanceQuery = createRuntimeServiceGetInstance(runtimeClient, { sensitive: true, }); $: instance = $instanceQuery.data?.instance; diff --git a/web-admin/src/features/projects/status/overview/ErrorsSection.svelte b/web-admin/src/features/projects/status/overview/ErrorsSection.svelte index bea557b17b9..8eb03ea4a3b 100644 --- a/web-admin/src/features/projects/status/overview/ErrorsSection.svelte +++ b/web-admin/src/features/projects/status/overview/ErrorsSection.svelte @@ -10,20 +10,22 @@ createRuntimeServiceGetResource, type V1Resource, } from "@rilldata/web-common/runtime-client"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { useResources } from "../selectors"; import AlertCircleOutline from "@rilldata/web-common/components/icons/AlertCircleOutline.svelte"; import { groupErrorsByKind, pluralizeKind } from "./overview-utils"; - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); $: basePage = `/${$page.params.organization}/${$page.params.project}/-/status`; // Parse errors $: projectParserQuery = createRuntimeServiceGetResource( - instanceId, + runtimeClient, { - "name.kind": ResourceKind.ProjectParser, - "name.name": SingletonProjectParserName, + name: { + kind: ResourceKind.ProjectParser, + name: SingletonProjectParserName, + }, }, { query: { refetchOnMount: true, refetchOnWindowFocus: true } }, ); @@ -31,7 +33,7 @@ $projectParserQuery.data?.resource?.projectParser?.state?.parseErrors ?? []; // Resource errors grouped by kind - $: resourcesQuery = useResources(instanceId); + $: resourcesQuery = useResources(runtimeClient); $: allResources = ($resourcesQuery.data?.resources ?? []) as V1Resource[]; $: erroredResources = allResources.filter((r) => !!r.meta?.reconcileError); diff --git a/web-admin/src/features/projects/status/overview/ProjectGlobalStatusIndicator.svelte b/web-admin/src/features/projects/status/overview/ProjectGlobalStatusIndicator.svelte index 33a32a2824a..2ef898c7ccc 100644 --- a/web-admin/src/features/projects/status/overview/ProjectGlobalStatusIndicator.svelte +++ b/web-admin/src/features/projects/status/overview/ProjectGlobalStatusIndicator.svelte @@ -5,7 +5,7 @@ import LoadingSpinner from "@rilldata/web-common/components/icons/LoadingSpinner.svelte"; import { useProjectParser } from "@rilldata/web-common/features/entity-management/resource-selectors"; import { createRuntimeServiceListResources } from "@rilldata/web-common/runtime-client"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { runtimeClientStore } from "@rilldata/web-common/runtime-client/v2"; import { useQueryClient } from "@tanstack/svelte-query"; import { useProjectDeployment } from "../selectors"; @@ -14,44 +14,57 @@ export let organization: string; export let project: string; - $: ({ instanceId } = $runtime); + $: runtimeClient = $runtimeClientStore; $: projectDeployment = useProjectDeployment(organization, project); $: ({ data: deployment } = $projectDeployment); $: isDeploymentNotOk = deployment?.status !== V1DeploymentStatus.DEPLOYMENT_STATUS_RUNNING; - $: hasResourceErrorsQuery = createRuntimeServiceListResources( - instanceId, - undefined, - { - query: { - select: (data) => { - return ( - data.resources.filter((resource) => !!resource.meta.reconcileError) - .length > 0 - ); + $: hasResourceErrorsQuery = runtimeClient + ? createRuntimeServiceListResources( + runtimeClient, + {}, + { + query: { + select: (data) => { + return ( + data.resources.filter( + (resource) => !!resource.meta.reconcileError, + ).length > 0 + ); + }, + refetchOnMount: true, + refetchOnWindowFocus: true, + }, }, - refetchOnMount: true, - refetchOnWindowFocus: true, - }, - }, - ); + ) + : null; $: ({ data: hasResourceErrors, error: hasResourceErrorsError, isLoading: hasResourceErrorsLoading, - } = $hasResourceErrorsQuery); - - $: projectParserQuery = useProjectParser(queryClient, instanceId, { - refetchOnMount: true, - refetchOnWindowFocus: true, + } = $hasResourceErrorsQuery ?? { + data: undefined, + error: undefined, + isLoading: true, }); + + $: projectParserQuery = runtimeClient + ? useProjectParser(queryClient, runtimeClient, { + refetchOnMount: true, + refetchOnWindowFocus: true, + }) + : null; $: ({ data: projectParserData, error: projectParserError, isLoading: projectParserLoading, - } = $projectParserQuery); + } = $projectParserQuery ?? { + data: undefined, + error: undefined, + isLoading: true, + }); $: hasParseErrors = projectParserData?.projectParser.state.parseErrors.length > 0; diff --git a/web-admin/src/features/projects/status/overview/ResourcesSection.svelte b/web-admin/src/features/projects/status/overview/ResourcesSection.svelte index 57b47dc34d1..d9545d5e528 100644 --- a/web-admin/src/features/projects/status/overview/ResourcesSection.svelte +++ b/web-admin/src/features/projects/status/overview/ResourcesSection.svelte @@ -1,15 +1,15 @@ diff --git a/web-admin/src/features/projects/status/overview/TablesSection.svelte b/web-admin/src/features/projects/status/overview/TablesSection.svelte index fbc45f8c834..e8c84545a22 100644 --- a/web-admin/src/features/projects/status/overview/TablesSection.svelte +++ b/web-admin/src/features/projects/status/overview/TablesSection.svelte @@ -1,17 +1,17 @@ diff --git a/web-admin/src/features/projects/status/resource-table/ProjectResourcesTable.svelte b/web-admin/src/features/projects/status/resource-table/ProjectResourcesTable.svelte index 151a7a4c13d..295692795ea 100644 --- a/web-admin/src/features/projects/status/resource-table/ProjectResourcesTable.svelte +++ b/web-admin/src/features/projects/status/resource-table/ProjectResourcesTable.svelte @@ -3,12 +3,12 @@ import ResourceTypeBadge from "@rilldata/web-common/features/entity-management/ResourceTypeBadge.svelte"; import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors"; import { - createRuntimeServiceCreateTrigger, + createRuntimeServiceCreateTriggerMutation, getRuntimeServiceListResourcesQueryKey, V1ReconcileStatus, type V1Resource, } from "@rilldata/web-common/runtime-client"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { goto } from "$app/navigation"; import { page } from "$app/stores"; import { useQueryClient } from "@tanstack/svelte-query"; @@ -39,7 +39,9 @@ let openDropdownResourceKey = ""; - const createTrigger = createRuntimeServiceCreateTrigger(); + const runtimeClient = useRuntimeClient(); + const createTrigger = + createRuntimeServiceCreateTriggerMutation(runtimeClient); const queryClient = useQueryClient(); const openRefreshDialog = ( @@ -83,17 +85,14 @@ const handleRefreshErroredPartitions = async () => { await $createTrigger.mutateAsync({ - instanceId: $runtime.instanceId, - data: { - models: [ - { model: erroredPartitionsModelName, allErroredPartitions: true }, - ], - }, + models: [ + { model: erroredPartitionsModelName, allErroredPartitions: true }, + ], }); await queryClient.invalidateQueries({ queryKey: getRuntimeServiceListResourcesQueryKey( - $runtime.instanceId, + runtimeClient.instanceId, undefined, ), }); @@ -107,28 +106,22 @@ const handleRefresh = async () => { if (dialogResourceKind === ResourceKind.Model) { await $createTrigger.mutateAsync({ - instanceId: $runtime.instanceId, - data: { - models: [ - { - model: dialogResourceName, - full: dialogRefreshType === "full", - }, - ], - }, + models: [ + { + model: dialogResourceName, + full: dialogRefreshType === "full", + }, + ], }); } else { await $createTrigger.mutateAsync({ - instanceId: $runtime.instanceId, - data: { - resources: [{ kind: dialogResourceKind, name: dialogResourceName }], - }, + resources: [{ kind: dialogResourceKind, name: dialogResourceName }], }); } await queryClient.invalidateQueries({ queryKey: getRuntimeServiceListResourcesQueryKey( - $runtime.instanceId, + runtimeClient.instanceId, undefined, ), }); diff --git a/web-admin/src/features/projects/status/selectors.ts b/web-admin/src/features/projects/status/selectors.ts index 198da316fdb..30297c7df59 100644 --- a/web-admin/src/features/projects/status/selectors.ts +++ b/web-admin/src/features/projects/status/selectors.ts @@ -9,7 +9,8 @@ import { type V1OlapTableInfo, type V1Resource, } from "@rilldata/web-common/runtime-client"; -import { connectorServiceOLAPListTables } from "@rilldata/web-common/runtime-client/gen/connector-service/connector-service"; +import { connectorServiceOLAPListTables } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { createInfiniteQuery } from "@tanstack/svelte-query"; import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors"; import { derived, type Readable } from "svelte/store"; @@ -49,9 +50,9 @@ export function filterResourcesForDisplay( ); } -export function useResources(instanceId: string) { +export function useResources(client: RuntimeClient) { return createRuntimeServiceListResources( - instanceId, + client, {}, { query: { @@ -81,7 +82,7 @@ export function useResources(instanceId: string) { */ export function useInfiniteTablesList( params: Readable<{ - instanceId: string; + client: RuntimeClient; connector: string; searchPattern?: string; }>, @@ -90,18 +91,17 @@ export function useInfiniteTablesList( queryKey: [ "/v1/olap/tables-infinite", { - instanceId: $p.instanceId, + instanceId: $p.client.instanceId, connector: $p.connector, searchPattern: $p.searchPattern, }, ], - enabled: !!$p.instanceId && !!$p.connector, + enabled: !!$p.client.instanceId && !!$p.connector, initialPageParam: undefined as string | undefined, getNextPageParam: (lastPage: { nextPageToken?: string }) => lastPage?.nextPageToken || undefined, queryFn: ({ pageParam }: { pageParam?: string }) => - connectorServiceOLAPListTables({ - instanceId: $p.instanceId, + connectorServiceOLAPListTables($p.client, { connector: $p.connector, searchPattern: $p.searchPattern, pageToken: pageParam, @@ -142,24 +142,28 @@ export function buildModelResourcesMap( /** * Fetches model resources and maps them by their result table name. */ -export function useModelResources(instanceId: string) { +export function useModelResources(client: RuntimeClient) { return createRuntimeServiceListResources( - instanceId, + client, { kind: ResourceKind.Model }, { query: { select: (data: V1ListResourcesResponse) => buildModelResourcesMap(data.resources), - enabled: !!instanceId, + enabled: !!client.instanceId, }, }, ); } -export function useRuntimeVersion() { - return createRuntimeServicePing({ - query: { - staleTime: 60000, // Cache for 1 minute +export function useRuntimeVersion(client: RuntimeClient) { + return createRuntimeServicePing( + client, + {}, + { + query: { + staleTime: 60000, // Cache for 1 minute + }, }, - }); + ); } diff --git a/web-admin/src/features/projects/status/tables/ProjectTables.svelte b/web-admin/src/features/projects/status/tables/ProjectTables.svelte index cc93453f3bf..453dd0b30bb 100644 --- a/web-admin/src/features/projects/status/tables/ProjectTables.svelte +++ b/web-admin/src/features/projects/status/tables/ProjectTables.svelte @@ -7,9 +7,9 @@ import CaretUpIcon from "@rilldata/web-common/components/icons/CaretUpIcon.svelte"; import { goto } from "$app/navigation"; import { page } from "$app/stores"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { - createRuntimeServiceCreateTrigger, + createRuntimeServiceCreateTriggerMutation, createRuntimeServiceGetInstance, getRuntimeServiceListResourcesQueryKey, type V1Resource, @@ -36,10 +36,10 @@ } from "../url-filter-sync"; import { onMount } from "svelte"; - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); // OLAP connector info - $: instanceQuery = createRuntimeServiceGetInstance(instanceId, { + $: instanceQuery = createRuntimeServiceGetInstance(runtimeClient, { sensitive: true, }); $: instance = $instanceQuery.data?.instance; @@ -67,12 +67,12 @@ // Use a writable store so createInfiniteQuery is called once during init; // parameter changes flow reactively through the store. const tablesParams = writable({ - instanceId: "", + client: runtimeClient, connector: "", searchPattern: undefined as string | undefined, }); $: tablesParams.set({ - instanceId, + client: runtimeClient, connector: connectorName, searchPattern, }); @@ -84,7 +84,7 @@ // TODO: populate from OLAPGetTable responses when per-table metadata is available let isViewMap = new Map(); // createQuery (unlike createInfiniteQuery) handles re-creation in $: blocks safely - $: modelResourcesQuery = useModelResources(instanceId); + $: modelResourcesQuery = useModelResources(runtimeClient); $: modelResources = $modelResourcesQuery.data ?? new Map(); let typeFilter: (typeof typeValues)[number] = parseEnumParam( $page.url.searchParams.get("type"), @@ -145,7 +145,8 @@ let selectedResource: V1Resource | null = null; let selectedModelName = ""; - const createTrigger = createRuntimeServiceCreateTrigger(); + const createTrigger = + createRuntimeServiceCreateTriggerMutation(runtimeClient); const queryClient = useQueryClient(); // Handlers @@ -190,14 +191,14 @@ try { await $createTrigger.mutateAsync({ - instanceId, - data: { - models: [{ model: selectedModelName, ...opts }], - }, + models: [{ model: selectedModelName, ...opts }], }); await queryClient.invalidateQueries({ - queryKey: getRuntimeServiceListResourcesQueryKey(instanceId, undefined), + queryKey: getRuntimeServiceListResourcesQueryKey( + runtimeClient.instanceId, + undefined, + ), }); } catch (error) { console.error("Failed to refresh model:", error); diff --git a/web-admin/src/features/public-urls/CreatePublicURLForm.svelte b/web-admin/src/features/public-urls/CreatePublicURLForm.svelte index eb3633a9783..6959c4ca998 100644 --- a/web-admin/src/features/public-urls/CreatePublicURLForm.svelte +++ b/web-admin/src/features/public-urls/CreatePublicURLForm.svelte @@ -21,7 +21,7 @@ import { useTimeControlStore } from "@rilldata/web-common/features/dashboards/time-controls/time-control-store"; import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors"; import { copyToClipboard } from "@rilldata/web-common/lib/actions/copy-to-clipboard"; - import type { HTTPError } from "@rilldata/web-common/runtime-client/fetchWrapper"; + import type { HTTPError } from "@rilldata/web-common/lib/errors"; import { useQueryClient } from "@tanstack/svelte-query"; import { Pencil } from "lucide-svelte"; import { DateTime } from "luxon"; diff --git a/web-admin/src/features/scheduled-reports/history/ReportHistoryTable.svelte b/web-admin/src/features/scheduled-reports/history/ReportHistoryTable.svelte index 1f42c23a69c..08f71b6771b 100644 --- a/web-admin/src/features/scheduled-reports/history/ReportHistoryTable.svelte +++ b/web-admin/src/features/scheduled-reports/history/ReportHistoryTable.svelte @@ -1,7 +1,7 @@ - + diff --git a/web-admin/src/routes/[organization]/[project]/+layout.svelte b/web-admin/src/routes/[organization]/[project]/+layout.svelte index c983cb6b533..4fab1a67bec 100644 --- a/web-admin/src/routes/[organization]/[project]/+layout.svelte +++ b/web-admin/src/routes/[organization]/[project]/+layout.svelte @@ -8,7 +8,8 @@ > = { gcTime: Math.min(RUNTIME_ACCESS_TOKEN_DEFAULT_TTL, 1000 * 60 * 5), // Make sure we don't keep a stale JWT in the cache refetchInterval: (query) => { - switch (query.state.data?.deployment?.status) { + const status = query.state.data?.deployment?.status; + switch (status) { case V1DeploymentStatus.DEPLOYMENT_STATUS_PENDING: case V1DeploymentStatus.DEPLOYMENT_STATUS_UPDATING: return PollTimeWhenProjectDeploymentPending; @@ -20,6 +21,7 @@ return false; } }, + refetchIntervalInBackground: true, // Keep polling while the tab is hidden (e.g. deploy loader) refetchOnMount: true, refetchOnReconnect: true, refetchOnWindowFocus: true, @@ -51,10 +53,10 @@ import { viewAsUserStore } from "@rilldata/web-admin/features/view-as-user/viewAsUserStore"; import ErrorPage from "@rilldata/web-common/components/ErrorPage.svelte"; import { metricsService } from "@rilldata/web-common/metrics/initMetrics"; - import RuntimeProvider from "@rilldata/web-common/runtime-client/RuntimeProvider.svelte"; + import RuntimeProvider from "@rilldata/web-common/runtime-client/v2/RuntimeProvider.svelte"; import { RUNTIME_ACCESS_TOKEN_DEFAULT_TTL } from "@rilldata/web-common/runtime-client/constants"; - import type { HTTPError } from "@rilldata/web-common/runtime-client/fetchWrapper"; - import type { AuthContext } from "@rilldata/web-common/runtime-client/runtime-store"; + import type { HTTPError } from "@rilldata/web-common/lib/errors"; + import type { AuthContext } from "@rilldata/web-common/runtime-client/v2/runtime-client"; import type { CreateQueryOptions } from "@tanstack/svelte-query"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient.ts"; import { getRuntimeServiceListResourcesQueryKey } from "@rilldata/web-common/runtime-client"; @@ -205,6 +207,20 @@ : "user" ) as AuthContext; + // Derive effective runtime connection props + $: effectiveHost = + mockedUserId && mockedUserDeploymentCredentials + ? mockedUserDeploymentCredentials.runtimeHost + : projectData?.deployment?.runtimeHost; + $: effectiveInstanceId = + mockedUserId && mockedUserDeploymentCredentials + ? mockedUserDeploymentCredentials.instanceId + : projectData?.deployment?.runtimeInstanceId; + $: effectiveJwt = + mockedUserId && mockedUserDeploymentCredentials + ? mockedUserDeploymentCredentials.accessToken + : projectData?.jwt; + // Load telemetry client with relevant context $: if (project && $user.data?.user?.id) { metricsService?.loadCloudFields({ @@ -247,19 +263,19 @@ : "There was an error deploying your project. Please contact support."} /> {:else if isProjectAvailable} - - - + {#if effectiveHost != null && effectiveInstanceId} + {#key `${effectiveHost}::${effectiveInstanceId}`} + + + + {/key} + {:else} + + {/if} {/if} {/if} diff --git a/web-admin/src/routes/[organization]/[project]/+page.svelte b/web-admin/src/routes/[organization]/[project]/+page.svelte index 46177f6510b..124aec73b06 100644 --- a/web-admin/src/routes/[organization]/[project]/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/+page.svelte @@ -6,18 +6,18 @@ import DelayedContent from "@rilldata/web-common/features/entity-management/DelayedContent.svelte"; import { featureFlags } from "@rilldata/web-common/features/feature-flags"; import { createRuntimeServiceGetInstance } from "@rilldata/web-common/runtime-client"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; const { chat } = featureFlags; + const runtimeClient = useRuntimeClient(); + $: ({ params: { project }, } = $page); - $: ({ instanceId } = $runtime); - // Query the instance to get the project display name - $: instanceQuery = createRuntimeServiceGetInstance(instanceId); + $: instanceQuery = createRuntimeServiceGetInstance(runtimeClient, {}); $: projectDisplayName = $instanceQuery.data?.instance?.projectDisplayName || project; $: isLoadingDisplayName = $instanceQuery.isLoading; diff --git a/web-admin/src/routes/[organization]/[project]/-/ai/+layout.ts b/web-admin/src/routes/[organization]/[project]/-/ai/+layout.ts index 0a9805d4254..5530a8ea6ad 100644 --- a/web-admin/src/routes/[organization]/[project]/-/ai/+layout.ts +++ b/web-admin/src/routes/[organization]/[project]/-/ai/+layout.ts @@ -3,6 +3,7 @@ import { setLastConversationId, } from "@rilldata/web-common/features/chat/layouts/fullpage/fullpage-store"; import { getFeatureFlags } from "@rilldata/web-common/features/feature-flags.js"; +import { getCloudRuntimeClient } from "@rilldata/web-admin/lib/runtime-client"; import { redirect } from "@sveltejs/kit"; export const load = async ({ @@ -11,10 +12,10 @@ export const load = async ({ url, parent, }) => { - // Wait for the feature flags to load const { runtime } = await parent(); + const client = getCloudRuntimeClient(runtime); - const fetchedFeatureFlags = await getFeatureFlags(runtime); + const fetchedFeatureFlags = await getFeatureFlags(client); // Redirect to `/-/dashboards` if chat feature is disabled // NOTE: In the future, we'll use user-level `ai` permissions for more granular access control diff --git a/web-admin/src/routes/[organization]/[project]/-/ai/[conversationId]/message/[messageId]/-/open/+page.ts b/web-admin/src/routes/[organization]/[project]/-/ai/[conversationId]/message/[messageId]/-/open/+page.ts index f0d6fded58a..8ff3118f8e1 100644 --- a/web-admin/src/routes/[organization]/[project]/-/ai/[conversationId]/message/[messageId]/-/open/+page.ts +++ b/web-admin/src/routes/[organization]/[project]/-/ai/[conversationId]/message/[messageId]/-/open/+page.ts @@ -1,14 +1,16 @@ import { maybeGetMetricsResolverQueryFromMessage } from "@rilldata/web-common/features/chat/core/citation-url-utils.ts"; import { openQuery } from "@rilldata/web-common/features/explore-mappers/open-query.ts"; +import { getCloudRuntimeClient } from "@rilldata/web-admin/lib/runtime-client"; export async function load({ parent, params: { organization, project } }) { const { runtime, message } = await parent(); + const client = getCloudRuntimeClient(runtime); const query = maybeGetMetricsResolverQueryFromMessage(message); await openQuery({ query, - runtime, + client, organization, project, }); diff --git a/web-admin/src/routes/[organization]/[project]/-/alerts/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/alerts/+page.svelte index 7e5414a813f..56872be4d6f 100644 --- a/web-admin/src/routes/[organization]/[project]/-/alerts/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/-/alerts/+page.svelte @@ -3,15 +3,15 @@ import AlertsTable from "@rilldata/web-admin/features/alerts/listing/AlertsTable.svelte"; import { useAlerts } from "@rilldata/web-admin/features/alerts/selectors"; import ProjectPage from "@rilldata/web-admin/features/projects/ProjectPage.svelte"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); $: ({ params: { organization, project }, } = $page); - $: query = useAlerts(instanceId); + $: query = useAlerts(runtimeClient); $: ({ data } = $query); 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 921436f721e..63be050cb72 100644 --- a/web-admin/src/routes/[organization]/[project]/-/alerts/[alert]/+layout.ts +++ b/web-admin/src/routes/[organization]/[project]/-/alerts/[alert]/+layout.ts @@ -1,24 +1,24 @@ import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors.js"; +import { connectCodeToHTTPStatus } from "@rilldata/web-common/lib/errors"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient.js"; -import { isHTTPError } from "@rilldata/web-common/runtime-client/fetchWrapper.js"; -import { getRuntimeServiceGetResourceQueryOptions } from "@rilldata/web-common/runtime-client/query-options"; +import { getRuntimeServiceGetResourceQueryOptions } from "@rilldata/web-common/runtime-client"; import { error } from "@sveltejs/kit"; +import { ConnectError } from "@connectrpc/connect"; +import { getCloudRuntimeClient } from "@rilldata/web-admin/lib/runtime-client"; export async function load({ params, parent }) { const { runtime } = await parent(); + const client = getCloudRuntimeClient(runtime); const alertData = await queryClient .fetchQuery( - getRuntimeServiceGetResourceQueryOptions(runtime, { - "name.kind": ResourceKind.Alert, - "name.name": params.alert, + getRuntimeServiceGetResourceQueryOptions(client, { + name: { kind: ResourceKind.Alert, name: params.alert }, }), ) .catch((e) => { - if (!isHTTPError(e)) { - throw error(500, "Error fetching alert"); - } - throw error(e.response.status, e.response.data.message); + const ce = ConnectError.from(e); + throw error(connectCodeToHTTPStatus(ce.code), ce.rawMessage); }); return { diff --git a/web-admin/src/routes/[organization]/[project]/-/alerts/[alert]/open/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/alerts/[alert]/open/+page.svelte index 943f54edfdc..26dc34c1fb6 100644 --- a/web-admin/src/routes/[organization]/[project]/-/alerts/[alert]/open/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/-/alerts/[alert]/open/+page.svelte @@ -8,10 +8,13 @@ import { EntityStatus } from "@rilldata/web-common/features/entity-management/types"; import { mapQueryToDashboard } from "@rilldata/web-common/features/explore-mappers/map-to-explore"; import { getExplorePageUrlSearchParams } from "@rilldata/web-common/features/explore-mappers/utils"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import type { PageData } from "./$types"; export let data: PageData; + const runtimeClient = useRuntimeClient(); + $: ({ alert: alertResource, organization, @@ -34,6 +37,7 @@ alertSpec?.queryArgsJson ?? ""; $: dashboardStateForAlert = mapQueryToDashboard( + runtimeClient, { exploreName, queryName, @@ -59,6 +63,7 @@ async function gotoExplorePage() { const exploreStateParams = await getExplorePageUrlSearchParams( + runtimeClient, $dashboardStateForAlert.data.exploreName, $dashboardStateForAlert.data.exploreState, ); diff --git a/web-admin/src/routes/[organization]/[project]/-/deploying/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/deploying/+page.svelte index c9fd8c2b6c9..1d80acbe29d 100644 --- a/web-admin/src/routes/[organization]/[project]/-/deploying/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/-/deploying/+page.svelte @@ -4,17 +4,17 @@ import { useDeployingDashboards } from "@rilldata/web-admin/features/dashboards/listing/deploying-dashboards.ts"; import { eventBus } from "@rilldata/web-common/lib/event-bus/event-bus.ts"; import type { PageData } from "./$types"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store.ts"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; export let data: PageData; const { organization, project, deployingDashboard } = data; - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); // Make this reactive so that it fires once params are ready. // During a first deploy, runtime might not be available when deployment is still being created in the backend. $: deployingDashboardResp = useDeployingDashboards( - instanceId, + runtimeClient, organization.name, project.name, deployingDashboard, 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 5d3f0906696..11bc0dd7d8c 100644 --- a/web-admin/src/routes/[organization]/[project]/-/open-query/+page.ts +++ b/web-admin/src/routes/[organization]/[project]/-/open-query/+page.ts @@ -1,18 +1,18 @@ import { openQuery } from "@rilldata/web-common/features/explore-mappers/open-query"; +import { getCloudRuntimeClient } from "@rilldata/web-admin/lib/runtime-client"; import type { PageLoad } from "./$types"; import { getQueryFromUrl } from "@rilldata/web-common/features/chat/core/citation-url-utils.ts"; export const load: PageLoad = async ({ params, url, parent }) => { - // Only proceed once the runtime in parent is ready - const parentData = await parent(); - - // Get the organization and project from the URL - const organization = params.organization; - const project = params.project; - const runtime = parentData.runtime; + const { runtime } = await parent(); + const client = getCloudRuntimeClient(runtime!); const query = getQueryFromUrl(url); - // Open the query (this'll redirect to the relevant Explore page) - await openQuery({ query, organization, project, runtime }); + await openQuery({ + query, + organization: params.organization, + project: params.project, + client, + }); }; diff --git a/web-admin/src/routes/[organization]/[project]/-/reports/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/reports/+page.svelte index 8486c399e3f..96be78fbe82 100644 --- a/web-admin/src/routes/[organization]/[project]/-/reports/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/-/reports/+page.svelte @@ -3,15 +3,15 @@ import ProjectPage from "@rilldata/web-admin/features/projects/ProjectPage.svelte"; import ReportsTable from "@rilldata/web-admin/features/scheduled-reports/listing/ReportsTable.svelte"; import { useReports } from "@rilldata/web-admin/features/scheduled-reports/selectors"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); $: ({ params: { organization, project }, } = $page); - $: query = useReports(instanceId); + $: query = useReports(runtimeClient); $: ({ data } = $query); 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 80f70692a77..06aa470aadc 100644 --- a/web-admin/src/routes/[organization]/[project]/-/reports/[report]/+layout.ts +++ b/web-admin/src/routes/[organization]/[project]/-/reports/[report]/+layout.ts @@ -1,24 +1,24 @@ import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors.js"; +import { connectCodeToHTTPStatus } from "@rilldata/web-common/lib/errors"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient.js"; -import { isHTTPError } from "@rilldata/web-common/runtime-client/fetchWrapper.js"; -import { getRuntimeServiceGetResourceQueryOptions } from "@rilldata/web-common/runtime-client/query-options"; +import { getRuntimeServiceGetResourceQueryOptions } from "@rilldata/web-common/runtime-client"; import { error } from "@sveltejs/kit"; +import { ConnectError } from "@connectrpc/connect"; +import { getCloudRuntimeClient } from "@rilldata/web-admin/lib/runtime-client"; export async function load({ params, parent }) { const { runtime } = await parent(); + const client = getCloudRuntimeClient(runtime); const reportData = await queryClient .fetchQuery( - getRuntimeServiceGetResourceQueryOptions(runtime, { - "name.kind": ResourceKind.Report, - "name.name": params.report, + getRuntimeServiceGetResourceQueryOptions(client, { + name: { kind: ResourceKind.Report, name: params.report }, }), ) .catch((e) => { - if (!isHTTPError(e)) { - throw error(500, "Error fetching report"); - } - throw error(e.response.status, e.response.data.message); + const ce = ConnectError.from(e); + throw error(connectCodeToHTTPStatus(ce.code), ce.rawMessage); }); return { diff --git a/web-admin/src/routes/[organization]/[project]/-/reports/[report]/export/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/reports/[report]/export/+page.svelte index e65a6c005d6..b587ed9f460 100644 --- a/web-admin/src/routes/[organization]/[project]/-/reports/[report]/export/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/-/reports/[report]/export/+page.svelte @@ -5,16 +5,17 @@ import CtaContentContainer from "@rilldata/web-common/components/calls-to-action/CTAContentContainer.svelte"; import CtaLayoutContainer from "@rilldata/web-common/components/calls-to-action/CTALayoutContainer.svelte"; import CtaMessage from "@rilldata/web-common/components/calls-to-action/CTAMessage.svelte"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; + + const runtimeClient = useRuntimeClient(); - $: ({ instanceId } = $runtime); $: organization = $page.params.organization; $: project = $page.params.project; $: reportId = $page.params.report; $: executionTime = $page.url.searchParams.get("execution_time"); $: token = $page.url.searchParams.get("token"); - const downloadReportMutation = createDownloadReportMutation(); + const downloadReportMutation = createDownloadReportMutation(runtimeClient); let downloadOnce = false; async function triggerDownload() { @@ -22,15 +23,15 @@ downloadOnce = true; await $downloadReportMutation.mutateAsync({ data: { - instanceId, reportId, executionTime, originBaseUrl: window.location.origin, + host: runtimeClient.host, }, }); } - $: if (reportId && $runtime) { + $: if (reportId && runtimeClient) { triggerDownload(); } diff --git a/web-admin/src/routes/[organization]/[project]/-/reports/[report]/open/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/reports/[report]/open/+page.svelte index 898fbbe6b42..40d37547ab8 100644 --- a/web-admin/src/routes/[organization]/[project]/-/reports/[report]/open/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/-/reports/[report]/open/+page.svelte @@ -9,10 +9,13 @@ import { EntityStatus } from "@rilldata/web-common/features/entity-management/types"; import { mapQueryToDashboard } from "@rilldata/web-common/features/explore-mappers/map-to-explore"; import { getExplorePageUrlSearchParams } from "@rilldata/web-common/features/explore-mappers/utils"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import type { PageData } from "./$types"; export let data: PageData; + const runtimeClient = useRuntimeClient(); + $: ({ report: reportResource, organization, @@ -35,6 +38,7 @@ let dashboardStateForReport: ReturnType; $: dashboardStateForReport = mapQueryToDashboard( + runtimeClient, { exploreName, queryName, @@ -64,6 +68,7 @@ exploreState: ExploreState, ) { const exploreStateParams = await getExplorePageUrlSearchParams( + runtimeClient, exploreName, exploreState, ); diff --git a/web-admin/src/routes/[organization]/[project]/-/settings/public-urls/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/settings/public-urls/+page.svelte index a607a623c66..0e979121522 100644 --- a/web-admin/src/routes/[organization]/[project]/-/settings/public-urls/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/-/settings/public-urls/+page.svelte @@ -8,11 +8,11 @@ import { useDashboards } from "@rilldata/web-admin/features/dashboards/listing/selectors"; import PublicURLsResourceTable from "@rilldata/web-admin/features/public-urls/PublicURLsResourceTable.svelte"; import DelayedSpinner from "@rilldata/web-common/features/entity-management/DelayedSpinner.svelte"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { eventBus } from "@rilldata/web-common/lib/event-bus/event-bus"; import { useQueryClient } from "@tanstack/svelte-query"; - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); $: organization = $page.params.organization; $: project = $page.params.project; @@ -40,7 +40,7 @@ (page) => page.tokens ?? [], ) ?? []; - $: dashboards = useDashboards(instanceId); + $: dashboards = useDashboards(runtimeClient); $: allRowsWithDashboardTitle = allRows.map((token) => { const dashboard = $dashboards.data?.find( diff --git a/web-admin/src/routes/[organization]/[project]/-/share/[token]/explore/[dashboard]/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/share/[token]/explore/[dashboard]/+page.svelte index b7bdee3fa8d..f00814cf55a 100644 --- a/web-admin/src/routes/[organization]/[project]/-/share/[token]/explore/[dashboard]/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/-/share/[token]/explore/[dashboard]/+page.svelte @@ -11,9 +11,9 @@ import DashboardStateManager from "@rilldata/web-common/features/dashboards/state-managers/loaders/DashboardStateManager.svelte"; import { eventBus } from "@rilldata/web-common/lib/event-bus/event-bus"; import { createRuntimeServiceGetExplore } from "@rilldata/web-common/runtime-client"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); $: ({ organization, project, dashboard: exploreName } = $page.params); @@ -34,7 +34,7 @@ } // Call `GetExplore` to get the Explore's metrics view - $: exploreQuery = createRuntimeServiceGetExplore(instanceId, { + $: exploreQuery = createRuntimeServiceGetExplore(runtimeClient, { name: exploreName, }); $: ({ data: explore } = $exploreQuery); 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 6342cd40fc4..2af842a964a 100644 --- a/web-admin/src/routes/[organization]/[project]/canvas/[dashboard]/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/canvas/[dashboard]/+page.svelte @@ -1,7 +1,7 @@ -{#key instanceId} - +{#key runtimeClient.instanceId} + {/key} diff --git a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte index 3a163422d99..afc8701920d 100644 --- a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte @@ -15,7 +15,8 @@ import { useExplore } from "@rilldata/web-common/features/explores/selectors"; import { eventBus } from "@rilldata/web-common/lib/event-bus/event-bus"; import type { V1GetExploreResponse } from "@rilldata/web-common/runtime-client"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { isNotFoundError } from "@rilldata/web-common/lib/errors"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import type { PageData } from "./$types"; const PollIntervalWhenDashboardFirstReconciling = 1000; @@ -24,14 +25,14 @@ export let data: PageData; $: ({ project } = data); - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); $: ({ organization: orgName, project: projectName, dashboard: exploreName, } = $page.params); - $: explore = useExplore(instanceId, exploreName, { + $: explore = useExplore(runtimeClient, exploreName, { refetchInterval: (query) => { if (!query.state.data) return false; if (isExploreReconcilingForFirstTime(query.state.data)) @@ -43,9 +44,7 @@ }); $: isDashboardNotFound = - !$explore.data && - $explore.isError && - $explore.error?.response?.status === 404; + !$explore.data && $explore.isError && isNotFoundError($explore.error); $: exploreTitle = $explore.data?.explore?.explore?.state?.validSpec?.displayName; $: metricsViewName = $explore.data?.metricsView?.meta?.name?.name; @@ -75,7 +74,7 @@ $: bookmarkExploreStateQuery = getHomeBookmarkExploreState( project?.id, - instanceId, + runtimeClient, metricsViewName, exploreName, ); diff --git a/web-admin/tests/setup/setup.ts b/web-admin/tests/setup/setup.ts index ce2ddd7dd07..c598f633588 100644 --- a/web-admin/tests/setup/setup.ts +++ b/web-admin/tests/setup/setup.ts @@ -202,6 +202,7 @@ setup.describe("global setup", () => { // 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 }), @@ -212,7 +213,8 @@ setup.describe("global setup", () => { // Expect to land on the project home page await adminPage.waitForURL(`/${RILL_ORG_NAME}/${RILL_PROJECT_NAME}`); - // Temporary fix to wait for the project to be ready. + // Poll with page reloads until the deployment is ready and the project title appears. + // Each reload triggers a fresh GetProject API call via the SvelteKit load function. // TODO: add a refetch to the project API await expect .poll( diff --git a/web-common/orval.config.ts b/web-common/orval.config.ts deleted file mode 100644 index da1d04e55b3..00000000000 --- a/web-common/orval.config.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { defineConfig } from "orval"; - -export default defineConfig({ - api: { - input: "../proto/gen/rill/runtime/v1/runtime.swagger.yaml", - output: { - workspace: "./src/runtime-client/", - target: "gen/index.ts", - client: "svelte-query", - mode: "tags-split", - mock: false, - prettier: true, - override: { - mutator: { - path: "http-client.ts", // Relative to workspace path set above - name: "httpClient", - }, - - // Override queries and mutations here - operations: { - // Turn MetricsViewMeta into a query even though it's a POST request - QueryService_MetricsViewMeta: { - query: { - useQuery: true, - useMutation: false, - }, - }, - QueryService_ColumnRollupInterval: { - query: { - useQuery: true, - useMutation: false, - }, - }, - QueryService_ColumnTopK: { - query: { - useQuery: true, - useMutation: false, - }, - }, - QueryService_ColumnTimeSeries: { - query: { - useQuery: true, - useMutation: false, - }, - }, - QueryService_TableColumns: { - query: { - useQuery: true, - useMutation: false, - }, - }, - QueryService_MetricsViewAggregation: { - query: { - useQuery: true, - useMutation: false, - }, - }, - QueryService_MetricsViewTotals: { - query: { - useQuery: true, - useMutation: false, - }, - }, - QueryService_MetricsViewTimeSeries: { - query: { - useQuery: true, - useMutation: false, - }, - }, - QueryService_MetricsViewToplist: { - query: { - useQuery: true, - useMutation: false, - }, - }, - QueryService_MetricsViewComparison: { - query: { - useQuery: true, - useMutation: false, - }, - }, - QueryService_MetricsViewRows: { - query: { - useQuery: true, - useMutation: false, - }, - }, - QueryService_MetricsViewTimeRange: { - query: { - useQuery: true, - useMutation: false, - }, - }, - QueryService_MetricsViewSearch: { - query: { - useQuery: true, - useMutation: false, - }, - }, - QueryService_MetricsViewTimeRanges: { - query: { - useQuery: true, - useMutation: false, - }, - }, - QueryService_MetricsViewAnnotations: { - query: { - useQuery: true, - useMutation: false, - }, - }, - QueryService_ResolveComponent: { - query: { - useQuery: true, - useMutation: false, - }, - }, - QueryService_ResolveCanvas: { - query: { - useQuery: true, - useMutation: false, - }, - }, - QueryService_ResolveTemplatedString: { - query: { - useQuery: true, - useMutation: false, - }, - }, - RuntimeService_IssueDevJWT: { - query: { - useQuery: true, - useMutation: false, - }, - }, - RuntimeService_GetModelPartitions: { - query: { - useInfinite: true, - useInfiniteQueryParam: "pageToken", - }, - }, - }, - }, - }, - }, -}); diff --git a/web-common/package.json b/web-common/package.json index 9b89aa91a61..1c12545dac9 100644 --- a/web-common/package.json +++ b/web-common/package.json @@ -5,7 +5,7 @@ "scripts": { "lint": "prettier --check . && eslint .", "format": "prettier --write .", - "generate:runtime-client": "svelte-kit sync && orval --config ./orval.config.ts", + "generate:runtime-client": "tsx src/runtime-client/v2/codegen/run.ts", "generate:sveltekit": "svelte-kit sync", "generate:template-schema": "ts-json-schema-generator --path 'src/features/canvas/components/types.ts' --type TemplateSpec -o ../runtime/parser/data/component-template-v1.json", "test": "vitest run", @@ -94,7 +94,6 @@ "match-sorter": "^6.3.1", "memoizee": "^0.4.17", "nearley": "^2.20.1", - "orval": "^7.8.0", "posthog-js": "^1.188.0", "prismjs": "^1.30.0", "regular-table": "^0.5.9", diff --git a/web-common/src/components/preview-table/ConnectedPreviewTable.svelte b/web-common/src/components/preview-table/ConnectedPreviewTable.svelte index cff98a5f205..e2b41212a1a 100644 --- a/web-common/src/components/preview-table/ConnectedPreviewTable.svelte +++ b/web-common/src/components/preview-table/ConnectedPreviewTable.svelte @@ -5,7 +5,9 @@ createQueryServiceTableColumns, createQueryServiceTableRows, } from "@rilldata/web-common/runtime-client"; - import { runtime } from "../../runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; + + const runtimeClient = useRuntimeClient(); import WorkspaceError from "../WorkspaceError.svelte"; import type { VirtualizedTableColumns } from "../virtualized-table/types"; import PreviewTable from "./PreviewTable.svelte"; @@ -19,9 +21,8 @@ let columns: VirtualizedTableColumns[] | undefined; let rows: V1TableRowsResponseDataItem[] | undefined; - $: ({ instanceId } = $runtime); - - $: columnsQuery = createQueryServiceTableColumns(instanceId, table, { + $: columnsQuery = createQueryServiceTableColumns(runtimeClient, { + tableName: table, connector, database, databaseSchema, @@ -32,7 +33,8 @@ error: columnsError, } = $columnsQuery); - $: rowsQuery = createQueryServiceTableRows(instanceId, table, { + $: rowsQuery = createQueryServiceTableRows(runtimeClient, { + tableName: table, connector, database, databaseSchema, @@ -53,7 +55,7 @@ {:else if rowsError || columnsError} {:else if rows && columns} diff --git a/web-common/src/components/vega/VegaLiteRenderer.svelte b/web-common/src/components/vega/VegaLiteRenderer.svelte index 52e774a2e92..f15d68a9039 100644 --- a/web-common/src/components/vega/VegaLiteRenderer.svelte +++ b/web-common/src/components/vega/VegaLiteRenderer.svelte @@ -1,6 +1,7 @@
diff --git a/web-common/src/features/alerts/delivery-tab/notifiers-utils.ts b/web-common/src/features/alerts/delivery-tab/notifiers-utils.ts index be0b4699c5a..96f695d2feb 100644 --- a/web-common/src/features/alerts/delivery-tab/notifiers-utils.ts +++ b/web-common/src/features/alerts/delivery-tab/notifiers-utils.ts @@ -1,9 +1,14 @@ import { createRuntimeServiceListNotifierConnectors } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; -export function getHasSlackConnection(runtimeId: string) { - return createRuntimeServiceListNotifierConnectors(runtimeId, { - query: { - select: (data) => !!data.connectors?.some((c) => c.name === "slack"), +export function getHasSlackConnection(client: RuntimeClient) { + return createRuntimeServiceListNotifierConnectors( + client, + {}, + { + query: { + select: (data) => !!data.connectors?.some((c) => c.name === "slack"), + }, }, - }); + ); } diff --git a/web-common/src/features/canvas/CanvasBuilder.svelte b/web-common/src/features/canvas/CanvasBuilder.svelte index a121bf6a4b1..3ac45874ee1 100644 --- a/web-common/src/features/canvas/CanvasBuilder.svelte +++ b/web-common/src/features/canvas/CanvasBuilder.svelte @@ -6,7 +6,7 @@ type V1CanvasRow, type V1Resource, } from "@rilldata/web-common/runtime-client"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { onDestroy } from "svelte"; import { get, writable } from "svelte/store"; import { parseDocument } from "yaml"; @@ -40,6 +40,8 @@ export let canvasName: string; export let openSidebar: () => void; + const runtimeClient = useRuntimeClient(); + let initialMousePosition: { x: number; y: number } | null = null; let clientWidth: number; let offset = { x: 0, y: 0 }; @@ -66,14 +68,17 @@ $: layoutRows = $_rows; - $: ({ instanceId } = $runtime); + $: ({ instanceId } = runtimeClient); $: components = $componentsStore; $: canvasData = $specStore.data; $: metricsViews = Object.entries(canvasData?.metricsViews ?? {}); - $: metricsViewQuery = useDefaultMetrics(instanceId, metricsViews?.[0]?.[0]); + $: metricsViewQuery = useDefaultMetrics( + runtimeClient, + metricsViews?.[0]?.[0], + ); $: ({ editorContent, updateEditorContent } = fileArtifact); $: contents = parseDocument($editorContent ?? ""); diff --git a/web-common/src/features/canvas/CanvasDashboardEmbed.svelte b/web-common/src/features/canvas/CanvasDashboardEmbed.svelte index db4fcf52060..386e63c04a2 100644 --- a/web-common/src/features/canvas/CanvasDashboardEmbed.svelte +++ b/web-common/src/features/canvas/CanvasDashboardEmbed.svelte @@ -2,7 +2,7 @@ import CanvasDashboardWrapper from "./CanvasDashboardWrapper.svelte"; import { getCanvasStore } from "./state-managers/state-managers"; import StaticCanvasRow from "./StaticCanvasRow.svelte"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import Spinner from "../entity-management/Spinner.svelte"; import { EntityStatus } from "../entity-management/types"; import { derived } from "svelte/store"; @@ -14,7 +14,9 @@ export let canvasName: string; export let navigationEnabled: boolean = true; - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); + + $: ({ instanceId } = runtimeClient); $: ({ canvasEntity: { diff --git a/web-common/src/features/canvas/CanvasDashboardWrapper.svelte b/web-common/src/features/canvas/CanvasDashboardWrapper.svelte index b370455dd9c..5415d6c82f8 100644 --- a/web-common/src/features/canvas/CanvasDashboardWrapper.svelte +++ b/web-common/src/features/canvas/CanvasDashboardWrapper.svelte @@ -1,11 +1,13 @@ diff --git a/web-common/src/features/canvas/components/markdown/util.ts b/web-common/src/features/canvas/components/markdown/util.ts index b3d234d6747..0af9508634a 100644 --- a/web-common/src/features/canvas/components/markdown/util.ts +++ b/web-common/src/features/canvas/components/markdown/util.ts @@ -10,7 +10,7 @@ import type { QueryServiceResolveTemplatedStringBody, } from "@rilldata/web-common/runtime-client"; import { getQueryServiceResolveTemplatedStringQueryOptions } from "@rilldata/web-common/runtime-client"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { derived } from "svelte/store"; import type { ParsedFilters } from "../../stores/filter-state"; import type { Readable } from "svelte/motion"; @@ -128,6 +128,7 @@ function buildRequestBody(params: { export function getResolveTemplatedStringQueryOptions( component: MarkdownCanvasComponent, + client: RuntimeClient, ): Readable< ReturnType > { @@ -139,7 +140,6 @@ export function getResolveTemplatedStringQueryOptions( component.specStore, component.timeAndFilterStore, component.parent?.specStore ?? null, - runtime, component.parent.timeManager.hasTimeSeriesStore, ...Array.from(metricsViewFilters.values()).map((f) => f.parsed), ], @@ -147,14 +147,12 @@ export function getResolveTemplatedStringQueryOptions( spec, timeAndFilters, parentSpec, - runtimeState, hasTimeSeries, ...parsedMetricsViewFilters ]) => { const content = spec?.content ?? ""; const applyFormatting = spec?.apply_formatting === true; const needsTemplating = hasTemplatingSyntax(content); - const instanceId = runtimeState?.instanceId ?? ""; const metricsViews = parentSpec?.data?.metricsViews ?? {}; @@ -169,7 +167,7 @@ export function getResolveTemplatedStringQueryOptions( const enabled = !!needsTemplating && !!content && - !!instanceId && + !!client.instanceId && !!requestBody && (!hasTimeSeries || !!timeAndFilters?.timeRange); @@ -180,9 +178,12 @@ export function getResolveTemplatedStringQueryOptions( const queryEnabled = enabled && !!requestBody; + // Cast needed: QueryServiceResolveTemplatedStringBody uses string + // timestamps while the v2 proto request expects Timestamp objects; + // the v2 fromJson layer handles the conversion at runtime. return getQueryServiceResolveTemplatedStringQueryOptions( - instanceId, - body, + client, + body as any, { query: { enabled: queryEnabled, diff --git a/web-common/src/features/canvas/components/pivot/util.ts b/web-common/src/features/canvas/components/pivot/util.ts index ce81a4969f6..385e3c26103 100644 --- a/web-common/src/features/canvas/components/pivot/util.ts +++ b/web-common/src/features/canvas/components/pivot/util.ts @@ -268,6 +268,7 @@ export const usePivotForCanvas = ( pivotConfig: Readable, ) => { const pivotDashboardContext: PivotDashboardContext = { + runtimeClient: canvas.client, metricsViewName: metricsViewStore, queryClient: queryClient, enabled: !!canvas, diff --git a/web-common/src/features/canvas/explore-link/ExploreLink.svelte b/web-common/src/features/canvas/explore-link/ExploreLink.svelte index 2e0740867dc..afa6f99bb4f 100644 --- a/web-common/src/features/canvas/explore-link/ExploreLink.svelte +++ b/web-common/src/features/canvas/explore-link/ExploreLink.svelte @@ -3,15 +3,16 @@ import type { BaseCanvasComponent } from "@rilldata/web-common/features/canvas/components/BaseCanvasComponent"; import type { ComponentWithMetricsView } from "@rilldata/web-common/features/canvas/components/types"; import { useExploreAvailability } from "@rilldata/web-common/features/explore-mappers/explore-validation"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { derived } from "svelte/store"; import { useTransformCanvasToExploreState } from "./canvas-explore-transformer"; import ExploreLink from "@rilldata/web-common/features/explores/explore-link/ExploreLink.svelte"; + const client = useRuntimeClient(); + export let component: BaseCanvasComponent; export let mode: "inline" | "dropdown-item" | "icon-button" = "inline"; - $: ({ instanceId } = $runtime); $: organization = $page.params.organization; $: project = $page.params.project; @@ -19,7 +20,7 @@ $: metricsViewName = $spec?.metrics_view; // Check if component can be linked to explore - $: exploreAvailability = useExploreAvailability(instanceId, metricsViewName); + $: exploreAvailability = useExploreAvailability(client, metricsViewName); $: context = derived( [exploreAvailability, component.timeAndFilterStore], diff --git a/web-common/src/features/canvas/filters/CanvasFilters.svelte b/web-common/src/features/canvas/filters/CanvasFilters.svelte index c1633b7bc96..1145513b714 100644 --- a/web-common/src/features/canvas/filters/CanvasFilters.svelte +++ b/web-common/src/features/canvas/filters/CanvasFilters.svelte @@ -8,7 +8,7 @@ import MeasureFilter from "@rilldata/web-common/features/dashboards/filters/measure-filters/MeasureFilter.svelte"; import { getPanRangeForTimeRange } from "@rilldata/web-common/features/dashboards/state-managers/selectors/charts"; import SuperPill from "@rilldata/web-common/features/dashboards/time-controls/super-pill/SuperPill.svelte"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import CanvasComparisonPill from "./CanvasComparisonPill.svelte"; import CanvasFilterButton from "../../dashboards/filters/CanvasFilterButton.svelte"; import { Tooltip } from "bits-ui"; @@ -19,12 +19,14 @@ export let builder = false; export let canvasName: string; + const runtimeClient = useRuntimeClient(); + /** the height of a row of chips */ const ROW_HEIGHT = "26px"; let showDefaultItem = false; - $: ({ instanceId } = $runtime); + $: ({ instanceId } = runtimeClient); $: ({ canvasEntity: { filterManager: { diff --git a/web-common/src/features/canvas/inspector/DefaultFilterDisplay.svelte b/web-common/src/features/canvas/inspector/DefaultFilterDisplay.svelte index 2962d0001dd..edae0c35984 100644 --- a/web-common/src/features/canvas/inspector/DefaultFilterDisplay.svelte +++ b/web-common/src/features/canvas/inspector/DefaultFilterDisplay.svelte @@ -1,5 +1,5 @@ diff --git a/web-common/src/features/canvas/inspector/fields/MultiPositionalFieldsInput.svelte b/web-common/src/features/canvas/inspector/fields/MultiPositionalFieldsInput.svelte index f4e48219be1..0f5d69f99e5 100644 --- a/web-common/src/features/canvas/inspector/fields/MultiPositionalFieldsInput.svelte +++ b/web-common/src/features/canvas/inspector/fields/MultiPositionalFieldsInput.svelte @@ -1,7 +1,7 @@ diff --git a/web-common/src/features/canvas/inspector/fields/SingleFieldInput.svelte b/web-common/src/features/canvas/inspector/fields/SingleFieldInput.svelte index ff4a96f6912..1a1427b14ed 100644 --- a/web-common/src/features/canvas/inspector/fields/SingleFieldInput.svelte +++ b/web-common/src/features/canvas/inspector/fields/SingleFieldInput.svelte @@ -4,9 +4,11 @@ import InputLabel from "@rilldata/web-common/components/forms/InputLabel.svelte"; import Search from "@rilldata/web-common/components/search/Search.svelte"; import { getCanvasStore } from "@rilldata/web-common/features/canvas/state-managers/state-managers"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { useMetricFieldData } from "../selectors"; + const client = useRuntimeClient(); + export let metricName: string; export let label: string | undefined = undefined; export let id: string; @@ -23,9 +25,7 @@ let open = false; let searchValue = ""; - $: ({ instanceId } = $runtime); - - $: ctx = getCanvasStore(canvasName, instanceId); + $: ctx = getCanvasStore(canvasName, client.instanceId); $: ({ getTimeDimensionForMetricView } = ctx.canvasEntity.metricsView); $: timeDimension = getTimeDimensionForMetricView(metricName); diff --git a/web-common/src/features/canvas/inspector/filters/DimensionFiltersInput.svelte b/web-common/src/features/canvas/inspector/filters/DimensionFiltersInput.svelte index c527c235313..6430743afc8 100644 --- a/web-common/src/features/canvas/inspector/filters/DimensionFiltersInput.svelte +++ b/web-common/src/features/canvas/inspector/filters/DimensionFiltersInput.svelte @@ -4,7 +4,7 @@ import { getCanvasStore } from "@rilldata/web-common/features/canvas/state-managers/state-managers"; import DimensionFilter from "@rilldata/web-common/features/dashboards/filters/dimension-filters/DimensionFilter.svelte"; import MeasureFilter from "@rilldata/web-common/features/dashboards/filters/measure-filters/MeasureFilter.svelte"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import CanvasFilterButton from "@rilldata/web-common/features/dashboards/filters/CanvasFilterButton.svelte"; import type { FilterState } from "../../stores/filter-state"; import Button from "@rilldata/web-common/components/button/Button.svelte"; @@ -17,9 +17,11 @@ export let excludedDimensions: Set; export let updateLocalFilterString: (newFilterString: string) => void; + const runtimeClient = useRuntimeClient(); + let localFiltersEnabledOverride = false; - $: ({ instanceId } = $runtime); + $: ({ instanceId } = runtimeClient); $: ({ canvasEntity: { diff --git a/web-common/src/features/canvas/inspector/filters/TimeFiltersInput.svelte b/web-common/src/features/canvas/inspector/filters/TimeFiltersInput.svelte index cc90fec0932..9448ef85dc1 100644 --- a/web-common/src/features/canvas/inspector/filters/TimeFiltersInput.svelte +++ b/web-common/src/features/canvas/inspector/filters/TimeFiltersInput.svelte @@ -4,7 +4,7 @@ import CanvasComparisonPill from "@rilldata/web-common/features/canvas/filters/CanvasComparisonPill.svelte"; import { getCanvasStore } from "@rilldata/web-common/features/canvas/state-managers/state-managers"; import SuperPill from "@rilldata/web-common/features/dashboards/time-controls/super-pill/SuperPill.svelte"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import type { TimeState } from "../../stores/time-state"; import { ALL_TIME_RANGE_ALIAS } from "@rilldata/web-common/features/dashboards/time-controls/new-time-controls"; @@ -15,7 +15,9 @@ export let canvasName: string; export let metricsView: string | null; - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); + + $: ({ instanceId } = runtimeClient); $: ({ canvasEntity: { diff --git a/web-common/src/features/canvas/selector.ts b/web-common/src/features/canvas/selector.ts index b42eac3c4e1..25cd85af0c1 100644 --- a/web-common/src/features/canvas/selector.ts +++ b/web-common/src/features/canvas/selector.ts @@ -3,23 +3,19 @@ import { useFilteredResources, } from "@rilldata/web-common/features/entity-management/resource-selectors"; import { - createQueryServiceResolveCanvas, - getQueryServiceResolveCanvasQueryOptions, - type RpcStatus, type V1CanvasSpec, type V1MetricsView, type V1ResolveCanvasResponse, type V1ResolveCanvasResponseResolvedComponents, } from "@rilldata/web-common/runtime-client"; -import type { ErrorType } from "@rilldata/web-common/runtime-client/http-client"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store.ts"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; +import { createQueryServiceResolveCanvas } from "@rilldata/web-common/runtime-client"; +import type { ConnectError } from "@connectrpc/connect"; import type { CreateQueryOptions, CreateQueryResult, QueryClient, } from "@tanstack/svelte-query"; -import { derived, type Readable } from "svelte/store"; - /** * Returns the default metrics view for a given instance, prioritizing in order: * 1. A specific metrics view by name if provided @@ -27,10 +23,10 @@ import { derived, type Readable } from "svelte/store"; * 3. The first available metrics view */ export function useDefaultMetrics( - instanceId: string, + client: RuntimeClient, metricsViewName?: string, ) { - return useFilteredResources(instanceId, ResourceKind.MetricsView, (data) => { + return useFilteredResources(client, ResourceKind.MetricsView, (data) => { const validMetricsViews = data?.resources?.filter( (res) => !!res.metricsView?.state?.validSpec, ); @@ -91,21 +87,16 @@ export interface CanvasResponse { } export function useCanvas( - instanceId: string, + client: RuntimeClient, canvasName: string, queryOptions?: Partial< - CreateQueryOptions< - V1ResolveCanvasResponse, - ErrorType, - CanvasResponse - > + CreateQueryOptions >, queryClient?: QueryClient, -): CreateQueryResult> { +): CreateQueryResult { return createQueryServiceResolveCanvas( - instanceId, - canvasName, - {}, + client, + { canvas: canvasName }, { query: { select: (data) => { @@ -125,42 +116,10 @@ export function useCanvas( }; }, - enabled: !!canvasName && !!instanceId, + enabled: !!canvasName && !!client.instanceId, ...queryOptions, }, }, queryClient, ); } - -export function getCanvasQueryOptions(canvasNameStore: Readable) { - return derived([runtime, canvasNameStore], ([{ instanceId }, canvasName]) => - getQueryServiceResolveCanvasQueryOptions( - instanceId, - canvasName, - {}, - { - query: { - select: (data) => { - const metricsViews: Record = {}; - const refMetricsViews = data?.referencedMetricsViews; - if (refMetricsViews) { - Object.keys(refMetricsViews).forEach((key) => { - metricsViews[key] = refMetricsViews?.[key]?.metricsView; - }); - } - - return { - canvas: data.canvas?.canvas?.state?.validSpec, - components: data.resolvedComponents, - metricsViews, - filePath: data.canvas?.meta?.filePaths?.[0], - }; - }, - - enabled: !!canvasName, - }, - }, - ), - ); -} diff --git a/web-common/src/features/canvas/state-managers/state-managers.ts b/web-common/src/features/canvas/state-managers/state-managers.ts index f2035f05a6c..cb45af80ca7 100644 --- a/web-common/src/features/canvas/state-managers/state-managers.ts +++ b/web-common/src/features/canvas/state-managers/state-managers.ts @@ -1,13 +1,11 @@ -import type { Runtime } from "@rilldata/web-common/runtime-client/runtime-store"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import type { QueryClient } from "@tanstack/svelte-query"; -import { type Writable } from "svelte/store"; import { CanvasEntity } from "../stores/canvas-entity"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; import type { CanvasResponse } from "../selector"; export type CanvasStore = { - runtime: Writable; + runtimeClient: RuntimeClient; canvasEntity: CanvasEntity; queryClient: QueryClient; }; @@ -58,6 +56,7 @@ export function setCanvasStore( canvasName: string, instanceId: string, response: CanvasResponse, + runtimeClient?: RuntimeClient, ): CanvasStore { const id = makeCanvasId(canvasName, instanceId); @@ -68,9 +67,14 @@ export function setCanvasStore( return canvasRegistry.get(id)!; } - const canvasEntity = new CanvasEntity(canvasName, instanceId, response); + const canvasEntity = new CanvasEntity( + canvasName, + instanceId, + response, + runtimeClient!, + ); const store: CanvasStore = { - runtime: runtime, + runtimeClient: runtimeClient!, canvasEntity, queryClient, }; diff --git a/web-common/src/features/canvas/stores/canvas-entity.ts b/web-common/src/features/canvas/stores/canvas-entity.ts index f7016c2285f..0a4446daa47 100644 --- a/web-common/src/features/canvas/stores/canvas-entity.ts +++ b/web-common/src/features/canvas/stores/canvas-entity.ts @@ -7,7 +7,6 @@ import { import type { CanvasSpecResponseStore } from "@rilldata/web-common/features/canvas/types"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; import { - queryServiceConvertExpressionToMetricsSQL, V1ExploreComparisonMode, type V1CanvasPreset, type V1CanvasSpec, @@ -47,6 +46,8 @@ import { createResolvedThemeStore } from "../../themes/selectors"; import { ExploreStateURLParams } from "../../dashboards/url-state/url-params"; import { DEFAULT_DASHBOARD_WIDTH } from "../layout-util"; import { createCustomMapStore } from "@rilldata/web-common/lib/custom-map-store"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; +import { queryServiceConvertExpressionToMetricsSQL } from "@rilldata/web-common/runtime-client"; export const lastVisitedState = new Map(); @@ -107,8 +108,9 @@ export class CanvasEntity { public name: string, public instanceId: string, private spec: CanvasResponse, + readonly client: RuntimeClient, ) { - this.specStore = useCanvas(instanceId, name, {}, queryClient); + this.specStore = useCanvas(client, name, {}, queryClient); // This will be deprecated soon - bgh const searchParamsStore: SearchParamsStore = (() => { @@ -150,7 +152,7 @@ export class CanvasEntity { this.theme = createResolvedThemeStore( this.themeName, this.specStore, - this.instanceId, + this.client, ); this.timeManager = new TimeManager(searchParamsStore, this); @@ -164,7 +166,7 @@ export class CanvasEntity { this.processSpec(this.spec); this.metricsView = new MetricsViewSelectors( - this.instanceId, + this.client, this._metricsViews, ); @@ -398,8 +400,8 @@ export class CanvasEntity { parsed.where, ], queryFn: () => - queryServiceConvertExpressionToMetricsSQL(this.instanceId, { - expression: parsed.where, + queryServiceConvertExpressionToMetricsSQL(this.client, { + expression: parsed.where as any, }), }); }); diff --git a/web-common/src/features/canvas/stores/time-state.ts b/web-common/src/features/canvas/stores/time-state.ts index 81f71ed2b93..ed6370b970c 100644 --- a/web-common/src/features/canvas/stores/time-state.ts +++ b/web-common/src/features/canvas/stores/time-state.ts @@ -156,7 +156,7 @@ export class TimeState { } const promises = metricsViewsWithTimeSeries.map((mvName) => { - return deriveInterval(range, mvName, timeZone); + return deriveInterval(range, this.parent.client, mvName, timeZone); }); Promise.all(promises) diff --git a/web-common/src/features/canvas/types.ts b/web-common/src/features/canvas/types.ts index c500bca5f06..780e1bf3d74 100644 --- a/web-common/src/features/canvas/types.ts +++ b/web-common/src/features/canvas/types.ts @@ -1,11 +1,11 @@ import type { QueryObserverResult } from "@tanstack/svelte-query"; import type { CanvasResponse } from "@rilldata/web-common/features/canvas/selector"; -import type { RpcStatus } from "@rilldata/web-common/runtime-client"; +import type { ConnectError } from "@connectrpc/connect"; import type { Readable } from "svelte/store"; import type { GridItemHTMLElement, GridStackNode } from "gridstack"; export type CanvasSpecResponseStore = Readable< - QueryObserverResult + QueryObserverResult >; export type GridstackDispatchEvents = { diff --git a/web-common/src/features/chat/DashboardChat.svelte b/web-common/src/features/chat/DashboardChat.svelte index 56766ed426f..d2cb5a949af 100644 --- a/web-common/src/features/chat/DashboardChat.svelte +++ b/web-common/src/features/chat/DashboardChat.svelte @@ -2,17 +2,22 @@ import { featureFlags } from "../feature-flags"; import SidebarChat from "./layouts/sidebar/SidebarChat.svelte"; import { chatOpen } from "./layouts/sidebar/sidebar-store"; - import { dashboardChatConfig } from "@rilldata/web-common/features/dashboards/chat-context.ts"; + import { createDashboardChatConfig } from "@rilldata/web-common/features/dashboards/chat-context.ts"; import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors.ts"; - import { canvasChatConfig } from "@rilldata/web-common/features/canvas/chat-context.ts"; + import { createCanvasChatConfig } from "@rilldata/web-common/features/canvas/chat-context.ts"; import ThemeProvider from "@rilldata/web-common/features/dashboards/ThemeProvider.svelte"; import { activeDashboardTheme } from "@rilldata/web-common/features/themes/active-dashboard-theme"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; + + const runtimeClient = useRuntimeClient(); export let kind: ResourceKind.Explore | ResourceKind.Canvas = ResourceKind.Explore; $: chatConfig = - kind === ResourceKind.Explore ? dashboardChatConfig : canvasChatConfig; + kind === ResourceKind.Explore + ? createDashboardChatConfig(runtimeClient) + : createCanvasChatConfig(runtimeClient); const { dashboardChat } = featureFlags; diff --git a/web-common/src/features/chat/core/citation-url-utils.ts b/web-common/src/features/chat/core/citation-url-utils.ts index 45cb594997b..637c615693d 100644 --- a/web-common/src/features/chat/core/citation-url-utils.ts +++ b/web-common/src/features/chat/core/citation-url-utils.ts @@ -1,6 +1,4 @@ -import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient.ts"; import { - getRuntimeServiceGetAIMessageQueryKey, type V1GetAIMessageResponse, type V1Message, } from "@rilldata/web-common/runtime-client"; @@ -10,34 +8,32 @@ import { ToolName, } from "@rilldata/web-common/features/chat/core/types.ts"; import { error } from "@sveltejs/kit"; -import type { Runtime } from "@rilldata/web-common/runtime-client/runtime-store.ts"; -import httpClient from "@rilldata/web-common/runtime-client/http-client.ts"; + +interface RuntimeInfo { + host: string; + instanceId: string; + jwt?: { token: string } | undefined; +} export async function fetchMessage( - runtime: Runtime, + runtime: RuntimeInfo, conversationId: string, messageId: string, ) { try { - const toolCallResp = await queryClient.fetchQuery({ - queryKey: getRuntimeServiceGetAIMessageQueryKey( - runtime.instanceId, - conversationId, - messageId, - ), - queryFn: ({ signal }) => - httpClient({ - url: `/v1/instances/${runtime.instanceId}/ai/conversations/${conversationId}/messages/${messageId}`, - method: "GET", - baseUrl: runtime.host, - headers: runtime.jwt - ? { - Authorization: `Bearer ${runtime.jwt?.token}`, - } - : undefined, - signal, - }), - }); + const headers: Record = {}; + if (runtime.jwt) { + headers["Authorization"] = `Bearer ${runtime.jwt.token}`; + } + const resp = await fetch( + `${runtime.host}/v1/instances/${runtime.instanceId}/ai/conversations/${conversationId}/messages/${messageId}`, + { headers }, + ); + if (!resp.ok) { + const data = await resp.json().catch(() => ({})); + throw { response: { status: resp.status, data } }; + } + const toolCallResp = (await resp.json()) as V1GetAIMessageResponse; // 200 response should always have a message. return toolCallResp.message!; diff --git a/web-common/src/features/chat/core/context/InlineContext.svelte b/web-common/src/features/chat/core/context/InlineContext.svelte index cb5b61e45ca..73c8f68ea62 100644 --- a/web-common/src/features/chat/core/context/InlineContext.svelte +++ b/web-common/src/features/chat/core/context/InlineContext.svelte @@ -6,6 +6,7 @@ import { type InlineContext } from "@rilldata/web-common/features/chat/core/context/inline-context.ts"; import InlineContextPicker from "@rilldata/web-common/features/chat/core/context/picker/InlineContextPicker.svelte"; import { InlineContextConfig } from "@rilldata/web-common/features/chat/core/context/config.ts"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; type InlineContextReadonlyProps = { mode: "readonly"; @@ -25,7 +26,8 @@ let open = false; let tooltipOpen = false; - const contextMetadataStore = getInlineChatContextMetadata(); + const runtimeClient = useRuntimeClient(); + const contextMetadataStore = getInlineChatContextMetadata(runtimeClient); $: typeConfig = selectedChatContext.type ? InlineContextConfig[selectedChatContext.type] diff --git a/web-common/src/features/chat/core/context/metadata.ts b/web-common/src/features/chat/core/context/metadata.ts index 65d5261bc6b..93fb5b7bdce 100644 --- a/web-common/src/features/chat/core/context/metadata.ts +++ b/web-common/src/features/chat/core/context/metadata.ts @@ -1,20 +1,20 @@ import { createQuery } from "@tanstack/svelte-query"; import { getValidMetricsViewsQueryOptions } from "@rilldata/web-common/features/dashboards/selectors.ts"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient.ts"; -import { derived, get, type Readable } from "svelte/store"; +import { derived, type Readable } from "svelte/store"; import { - createQueryServiceResolveCanvas, type MetricsViewSpecDimension, type MetricsViewSpecMeasure, type V1CanvasSpec, type V1ComponentSpec, type V1MetricsViewSpec, } from "@rilldata/web-common/runtime-client"; +import { createQueryServiceResolveCanvas } from "@rilldata/web-common/runtime-client"; import { getClientFilteredResourcesQueryOptions, ResourceKind, } from "@rilldata/web-common/features/entity-management/resource-selectors.ts"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store.ts"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; /** * Metadata used to map a value to a label. @@ -34,21 +34,21 @@ export type MetricsViewMetadata = { * Creates a store that contains a map of metrics view names to their metadata. * Each metrics view metadata has a reference to its spec, and a map of for measure and dimension spec by their names. */ -export function getInlineChatContextMetadata(): Readable { +export function getInlineChatContextMetadata( + client: RuntimeClient, +): Readable { const metricsViewsQuery = createQuery( - getValidMetricsViewsQueryOptions(), + getValidMetricsViewsQueryOptions(client), queryClient, ); const canvasResourcesQuery = createQuery( - getClientFilteredResourcesQueryOptions(ResourceKind.Canvas, (res) => + getClientFilteredResourcesQueryOptions(client, ResourceKind.Canvas, (res) => Boolean(res.canvas?.state?.validSpec), ), queryClient, ); - const instanceId = get(runtime).instanceId; - return derived( [metricsViewsQuery, canvasResourcesQuery], ([metricsViewsResp, canvasResourcesResp], set) => { @@ -87,9 +87,8 @@ export function getInlineChatContextMetadata(): Readable const canvasQueries = canvasResources.map((r) => createQueryServiceResolveCanvas( - instanceId, - r.meta?.name?.name ?? "", - {}, + client, + { canvas: r.meta?.name?.name ?? "" }, undefined, queryClient, ), diff --git a/web-common/src/features/chat/core/context/picker/ExpandableOption.svelte b/web-common/src/features/chat/core/context/picker/ExpandableOption.svelte index cb95a5d6a49..2c47b08ce1c 100644 --- a/web-common/src/features/chat/core/context/picker/ExpandableOption.svelte +++ b/web-common/src/features/chat/core/context/picker/ExpandableOption.svelte @@ -15,6 +15,7 @@ import DelayedSpinner from "@rilldata/web-common/features/entity-management/DelayedSpinner.svelte"; import SimpleOption from "@rilldata/web-common/features/chat/core/context/picker/SimpleOption.svelte"; import { getInlineChatContextMetadata } from "@rilldata/web-common/features/chat/core/context/metadata.ts"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; export let node: PickerTreeNode; export let selectedChatContext: InlineContext | null; @@ -27,9 +28,11 @@ const item = node.item; const context = item.context; + const runtimeClient = useRuntimeClient(); + const typeConfig = InlineContextConfig[context.type]; const typeLabel = typeConfig.typeLabel; - const contextMetadataStore = getInlineChatContextMetadata(); + const contextMetadataStore = getInlineChatContextMetadata(runtimeClient); $: icon = typeConfig?.getIcon?.(context, $contextMetadataStore); const selectedItemId = selectedChatContext ? getIdForContext(selectedChatContext) diff --git a/web-common/src/features/chat/core/context/picker/InlineContextPicker.svelte b/web-common/src/features/chat/core/context/picker/InlineContextPicker.svelte index 248d8f44027..4319a37280e 100644 --- a/web-common/src/features/chat/core/context/picker/InlineContextPicker.svelte +++ b/web-common/src/features/chat/core/context/picker/InlineContextPicker.svelte @@ -21,6 +21,7 @@ import { KeyboardNavigationManager } from "@rilldata/web-common/features/chat/core/context/picker/keyboard-navigation.ts"; import ExpandableOption from "@rilldata/web-common/features/chat/core/context/picker/ExpandableOption.svelte"; import SimpleOption from "@rilldata/web-common/features/chat/core/context/picker/SimpleOption.svelte"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; export let selectedChatContext: InlineContext | null = null; export let searchText: string = ""; @@ -32,12 +33,18 @@ ? getIdForContext(selectedChatContext) : null; + const runtimeClient = useRuntimeClient(); + const searchTextStore = writable(""); $: searchTextStore.set(searchText.replace(/^@/, "")); const uiState = new ContextPickerUIState(); const expandedParentsStore = uiState.expandedParentsStore; - const filteredOptions = getFilteredPickerItems(uiState, searchTextStore); + const filteredOptions = getFilteredPickerItems( + runtimeClient, + uiState, + searchTextStore, + ); $: pickerTree = buildPickerTree($filteredOptions); const keyboardNavigationManager = new KeyboardNavigationManager(uiState); diff --git a/web-common/src/features/chat/core/context/picker/SimpleOption.svelte b/web-common/src/features/chat/core/context/picker/SimpleOption.svelte index a4bc78d4c4b..c025dd479a7 100644 --- a/web-common/src/features/chat/core/context/picker/SimpleOption.svelte +++ b/web-common/src/features/chat/core/context/picker/SimpleOption.svelte @@ -8,14 +8,17 @@ import { CheckIcon } from "lucide-svelte"; import type { PickerItem } from "@rilldata/web-common/features/chat/core/context/picker/picker-tree.ts"; import { getInlineChatContextMetadata } from "@rilldata/web-common/features/chat/core/context/metadata.ts"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; export let item: PickerItem; export let selectedChatContext: InlineContext | null; export let keyboardNavigationManager: KeyboardNavigationManager; export let onSelect: (ctx: InlineContext) => void; + const runtimeClient = useRuntimeClient(); + const typeConfig = InlineContextConfig[item.context.type]; - const contextMetadataStore = getInlineChatContextMetadata(); + const contextMetadataStore = getInlineChatContextMetadata(runtimeClient); $: icon = typeConfig?.getIcon?.(item.context, $contextMetadataStore); const selectedItemId = selectedChatContext ? getIdForContext(selectedChatContext) diff --git a/web-common/src/features/chat/core/context/picker/data/canvases.ts b/web-common/src/features/chat/core/context/picker/data/canvases.ts index dad80361200..f15a9b9b77d 100644 --- a/web-common/src/features/chat/core/context/picker/data/canvases.ts +++ b/web-common/src/features/chat/core/context/picker/data/canvases.ts @@ -1,5 +1,5 @@ import type { PickerItem } from "@rilldata/web-common/features/chat/core/context/picker/picker-tree.ts"; -import { derived, get, type Readable } from "svelte/store"; +import { derived, type Readable } from "svelte/store"; import { getClientFilteredResourcesQueryOptions, ResourceKind, @@ -7,7 +7,6 @@ import { import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient.ts"; import { getCanvasNameStore } from "@rilldata/web-common/features/dashboards/nav-utils.ts"; import { createQuery } from "@tanstack/svelte-query"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store.ts"; import { getQueryServiceResolveCanvasQueryOptions } from "@rilldata/web-common/runtime-client"; import { getIdForContext, @@ -17,19 +16,20 @@ import { import { ContextPickerUIState } from "@rilldata/web-common/features/chat/core/context/picker/ui-state.ts"; import { getLatestConversationQueryOptions } from "@rilldata/web-common/features/chat/core/utils.ts"; import { MessageType } from "@rilldata/web-common/features/chat/core/types.ts"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; export function getCanvasesPickerOptions( + client: RuntimeClient, uiState: ContextPickerUIState, ): Readable { const canvasResourcesQuery = createQuery( - getClientFilteredResourcesQueryOptions(ResourceKind.Canvas, (res) => + getClientFilteredResourcesQueryOptions(client, ResourceKind.Canvas, (res) => Boolean(res.canvas?.state?.validSpec), ), queryClient, ); - const lastUsedCanvasNameStore = getLastUsedCanvasNameStore(); + const lastUsedCanvasNameStore = getLastUsedCanvasNameStore(client); const activeCanvasNameStore = getCanvasNameStore(); - const instanceId = get(runtime).instanceId; return derived( [canvasResourcesQuery, lastUsedCanvasNameStore, activeCanvasNameStore], @@ -57,7 +57,7 @@ export function getCanvasesPickerOptions( } satisfies PickerItem; const childrenQueryOptions = getCanvasComponentsQueryOptions( - instanceId, + client, canvasPickerItem, uiState.getExpandedStore(canvasPickerItem.id), ); @@ -85,16 +85,15 @@ export function getCanvasesPickerOptions( } function getCanvasComponentsQueryOptions( - instanceId: string, + client: RuntimeClient, canvasPickerItem: PickerItem, enabledStore: Readable, ) { const canvas = canvasPickerItem.context.canvas!; return derived(enabledStore, (enabled) => getQueryServiceResolveCanvasQueryOptions( - instanceId, - canvas, - {}, + client, + { canvas }, { query: { select: (data): PickerItem[] => { @@ -133,9 +132,9 @@ function getCanvasComponentsQueryOptions( /** * Looks at the last conversation and returns the canvas used in the last message or tool call. */ -function getLastUsedCanvasNameStore() { +function getLastUsedCanvasNameStore(client: RuntimeClient) { const lastConversationQuery = createQuery( - getLatestConversationQueryOptions(), + getLatestConversationQueryOptions(client), queryClient, ); diff --git a/web-common/src/features/chat/core/context/picker/data/index.ts b/web-common/src/features/chat/core/context/picker/data/index.ts index e4ef37500a2..e6b895d86f3 100644 --- a/web-common/src/features/chat/core/context/picker/data/index.ts +++ b/web-common/src/features/chat/core/context/picker/data/index.ts @@ -7,6 +7,7 @@ import type { PickerItem } from "@rilldata/web-common/features/chat/core/context import { getCanvasesPickerOptions } from "@rilldata/web-common/features/chat/core/context/picker/data/canvases.ts"; import { getInlineChatContextMetadata } from "@rilldata/web-common/features/chat/core/context/metadata.ts"; import { InlineContextConfig } from "@rilldata/web-common/features/chat/core/context/config.ts"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; /** * Creates a store that contains a list of options for each valid metrics view, canvases and sources/models. @@ -17,16 +18,17 @@ import { InlineContextConfig } from "@rilldata/web-common/features/chat/core/con * The list contains parents immediately followed by their children. */ export function getPickerOptions( + client: RuntimeClient, uiState: ContextPickerUIState, ): Readable { const isRillDev = !get(featureFlags.adminServer); return derived( [ - getInlineChatContextMetadata(), - getMetricsViewPickerOptions(), - getCanvasesPickerOptions(uiState), - isRillDev ? getModelsPickerOptions(uiState) : readable(null), + getInlineChatContextMetadata(client), + getMetricsViewPickerOptions(client), + getCanvasesPickerOptions(client, uiState), + isRillDev ? getModelsPickerOptions(client, uiState) : readable(null), uiState.expandedParentsStore, ], ([metadata, metricsViewOptions, canvasOptions, filesOption]) => { diff --git a/web-common/src/features/chat/core/context/picker/data/metrics-views.ts b/web-common/src/features/chat/core/context/picker/data/metrics-views.ts index d8d5f58d89a..f24348f5860 100644 --- a/web-common/src/features/chat/core/context/picker/data/metrics-views.ts +++ b/web-common/src/features/chat/core/context/picker/data/metrics-views.ts @@ -1,6 +1,7 @@ import { getActiveMetricsViewNameStore } from "@rilldata/web-common/features/dashboards/nav-utils.ts"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient.ts"; import { getValidMetricsViewsQueryOptions } from "@rilldata/web-common/features/dashboards/selectors.ts"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { derived, type Readable } from "svelte/store"; import { createQuery } from "@tanstack/svelte-query"; import { @@ -17,14 +18,16 @@ import { getLatestConversationQueryOptions } from "@rilldata/web-common/features * 1st level: metrics view context options * 2nd level: measures and dimensions options for each metrics view */ -export function getMetricsViewPickerOptions(): Readable { +export function getMetricsViewPickerOptions( + client: RuntimeClient, +): Readable { const metricsViewsQuery = createQuery( - getValidMetricsViewsQueryOptions(), + getValidMetricsViewsQueryOptions(client), queryClient, ); - const lastUsedMetricsViewStore = getLastUsedMetricsViewNameStore(); - const activeMetricsViewStore = getActiveMetricsViewNameStore(); + const lastUsedMetricsViewStore = getLastUsedMetricsViewNameStore(client); + const activeMetricsViewStore = getActiveMetricsViewNameStore(client); return derived( [metricsViewsQuery, lastUsedMetricsViewStore, activeMetricsViewStore], @@ -85,9 +88,9 @@ export function getMetricsViewPickerOptions(): Readable { /** * Looks at the last conversation and returns the metrics view used in the last message or tool call. */ -function getLastUsedMetricsViewNameStore() { +function getLastUsedMetricsViewNameStore(client: RuntimeClient) { const lastConversationQuery = createQuery( - getLatestConversationQueryOptions(), + getLatestConversationQueryOptions(client), queryClient, ); diff --git a/web-common/src/features/chat/core/context/picker/data/models.ts b/web-common/src/features/chat/core/context/picker/data/models.ts index 736912ce714..d2baeebab8e 100644 --- a/web-common/src/features/chat/core/context/picker/data/models.ts +++ b/web-common/src/features/chat/core/context/picker/data/models.ts @@ -14,10 +14,10 @@ import { } from "@rilldata/web-common/features/entity-management/resource-selectors.ts"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient.ts"; import { derived, type Readable } from "svelte/store"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store.ts"; import { createQuery } from "@tanstack/svelte-query"; import { ContextPickerUIState } from "@rilldata/web-common/features/chat/core/context/picker/ui-state.ts"; import type { PickerItem } from "@rilldata/web-common/features/chat/core/context/picker/picker-tree.ts"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; /** * Creates a store that contains a 2-level list of sources/model resources. @@ -26,17 +26,18 @@ import type { PickerItem } from "@rilldata/web-common/features/chat/core/context * NOTE: this only lists resources that are parsed as sources/models. Any parse errors will exclude the file. */ export function getModelsPickerOptions( + client: RuntimeClient, uiState: ContextPickerUIState, ): Readable { const modelResourcesQuery = createQuery( - getClientFilteredResourcesQueryOptions(ResourceKind.Model), + getClientFilteredResourcesQueryOptions(client, ResourceKind.Model), queryClient, ); const activeResourceStore = getActiveResourceStore(); return derived( - [runtime, modelResourcesQuery, activeResourceStore], - ([{ instanceId }, modelResourcesResp, activeResource], set) => { + [modelResourcesQuery, activeResourceStore], + ([modelResourcesResp, activeResource], set) => { const models = modelResourcesResp.data ?? []; const modelPickerItems: PickerItem[] = []; const modelQueryOptions: ReturnType< @@ -62,7 +63,7 @@ export function getModelsPickerOptions( } satisfies PickerItem; const childrenQueryOptions = getModelColumnsQueryOptions( - instanceId, + client, res, modelPickerItem, uiState.getExpandedStore(modelPickerItem.id), @@ -91,7 +92,7 @@ export function getModelsPickerOptions( } function getModelColumnsQueryOptions( - instanceId: string, + client: RuntimeClient, modelRes: V1Resource | undefined, modelPickerItem: PickerItem, enabledStore: Readable, @@ -100,9 +101,9 @@ function getModelColumnsQueryOptions( const table = modelRes?.model?.state?.resultTable ?? ""; return derived(enabledStore, (enabled) => getQueryServiceTableColumnsQueryOptions( - instanceId, - table, + client, { + tableName: table, connector, }, { diff --git a/web-common/src/features/chat/core/context/picker/filters.ts b/web-common/src/features/chat/core/context/picker/filters.ts index 18e34c57af0..299811a2388 100644 --- a/web-common/src/features/chat/core/context/picker/filters.ts +++ b/web-common/src/features/chat/core/context/picker/filters.ts @@ -2,6 +2,7 @@ import { ContextPickerUIState } from "@rilldata/web-common/features/chat/core/co import { derived, type Readable } from "svelte/store"; import { getPickerOptions } from "@rilldata/web-common/features/chat/core/context/picker/data"; import type { PickerItem } from "@rilldata/web-common/features/chat/core/context/picker/picker-tree.ts"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; /** * Creates a store that contains a list of options that match the search text. @@ -10,11 +11,16 @@ import type { PickerItem } from "@rilldata/web-common/features/chat/core/context * 3. If any child options are present, retains the parent option as well. */ export function getFilteredPickerItems( + client: RuntimeClient, uiState: ContextPickerUIState, searchTextStore: Readable, ) { return derived( - [getPickerOptions(uiState), searchTextStore, uiState.expandedParentsStore], + [ + getPickerOptions(client, uiState), + searchTextStore, + uiState.expandedParentsStore, + ], ([options, searchText, expandedParents]) => { const filterFunction = (label: string, value: string) => searchText.length === 0 || diff --git a/web-common/src/features/chat/core/conversation-manager.ts b/web-common/src/features/chat/core/conversation-manager.ts index 39aeb65e8e9..2692a48d7d7 100644 --- a/web-common/src/features/chat/core/conversation-manager.ts +++ b/web-common/src/features/chat/core/conversation-manager.ts @@ -1,9 +1,10 @@ import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; import { getRuntimeServiceListConversationsQueryOptions, - type RpcStatus, type V1ListConversationsResponse, } from "@rilldata/web-common/runtime-client"; +import type { ConnectError } from "@connectrpc/connect"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { createQuery, type CreateQueryResult } from "@tanstack/svelte-query"; import { derived, get, type Readable } from "svelte/store"; import { Conversation } from "./conversation"; @@ -51,8 +52,12 @@ export class ConversationManager { private conversationSelector: ConversationSelector; private readonly agent?: string; + public get instanceId(): string { + return this.client.instanceId; + } + constructor( - public readonly instanceId: string, + private readonly client: RuntimeClient, options: ConversationManagerOptions, ) { this.agent = options.agent; @@ -79,10 +84,10 @@ export class ConversationManager { */ public listConversationsQuery(): CreateQueryResult< V1ListConversationsResponse, - RpcStatus + ConnectError > { return createQuery( - getRuntimeServiceListConversationsQueryOptions(this.instanceId, { + getRuntimeServiceListConversationsQueryOptions(this.client, { // Filter to only show Rill client conversations, excluding MCP conversations userAgentPattern: "rill%", }), @@ -110,7 +115,7 @@ export class ConversationManager { // Otherwise, create a conversation instance and store it const conversation = new Conversation( - this.instanceId, + this.client, $conversationId, this.agent, ); @@ -159,7 +164,7 @@ export class ConversationManager { private createNewConversation() { this.newConversationUnsub?.(); this.newConversation = new Conversation( - this.instanceId, + this.client, NEW_CONVERSATION_ID, this.agent, ); @@ -278,21 +283,21 @@ function getConversationManagerKey(instanceId: string, agent?: string): string { } /** - * Get or create a ConversationManager instance for the given instanceId and agent + * Get or create a ConversationManager instance for the given client and agent * - * @param instanceId - The project/instance identifier + * @param client - The RuntimeClient instance * @param options - Configuration options for the conversation manager instance * @returns The ConversationManager instance for this project and agent */ export function getConversationManager( - instanceId: string, + client: RuntimeClient, options: ConversationManagerOptions, ): ConversationManager { - const key = getConversationManagerKey(instanceId, options.agent); + const key = getConversationManagerKey(client.instanceId, options.agent); if (!conversationManagerInstances.has(key)) { conversationManagerInstances.set( key, - new ConversationManager(instanceId, options), + new ConversationManager(client, options), ); } return conversationManagerInstances.get(key)!; diff --git a/web-common/src/features/chat/core/conversation.spec.ts b/web-common/src/features/chat/core/conversation.spec.ts index 3841e844e5b..b354cf9d34e 100644 --- a/web-common/src/features/chat/core/conversation.spec.ts +++ b/web-common/src/features/chat/core/conversation.spec.ts @@ -4,6 +4,7 @@ import { type V1GetConversationResponse, type V1Message, } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { get } from "svelte/store"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { Conversation } from "./conversation"; @@ -24,15 +25,6 @@ vi.mock("@rilldata/web-common/runtime-client", async (importOriginal) => { }; }); -vi.mock("@rilldata/web-common/runtime-client/runtime-store", () => ({ - runtime: { - subscribe: (fn: (value: { host: string }) => void) => { - fn({ host: "http://localhost:9009" }); - return () => {}; - }, - }, -})); - import { runtimeServiceForkConversation } from "@rilldata/web-common/runtime-client"; // ============================================================================= @@ -40,6 +32,12 @@ import { runtimeServiceForkConversation } from "@rilldata/web-common/runtime-cli // ============================================================================= const INSTANCE_ID = "test-instance"; + +const mockRuntimeClient = { + host: "http://localhost:9009", + instanceId: INSTANCE_ID, + getJwt: () => undefined, +} as unknown as RuntimeClient; const ORIGINAL_CONVERSATION_ID = "original-conv-123"; const FORKED_CONVERSATION_ID = "forked-conv-456"; @@ -48,7 +46,9 @@ const FORKED_CONVERSATION_ID = "forked-conv-456"; // ============================================================================= function getCacheKey(conversationId: string) { - return getRuntimeServiceGetConversationQueryKey(INSTANCE_ID, conversationId); + return getRuntimeServiceGetConversationQueryKey(INSTANCE_ID, { + conversationId, + }); } function getCachedData(conversationId: string) { @@ -95,7 +95,7 @@ function mockForkEmptyResponse() { } function createConversation(conversationId: string = ORIGINAL_CONVERSATION_ID) { - return new Conversation(INSTANCE_ID, conversationId); + return new Conversation(mockRuntimeClient, conversationId); } async function sendMessageAndIgnoreStreamError( @@ -145,9 +145,8 @@ describe("Conversation", () => { // Assert: fork API called correctly expect(runtimeServiceForkConversation).toHaveBeenCalledWith( - INSTANCE_ID, - ORIGINAL_CONVERSATION_ID, - {}, + mockRuntimeClient, + { conversationId: ORIGINAL_CONVERSATION_ID }, ); // Assert: cache updated with forked conversation diff --git a/web-common/src/features/chat/core/conversation.ts b/web-common/src/features/chat/core/conversation.ts index fec4fa10153..37091642871 100644 --- a/web-common/src/features/chat/core/conversation.ts +++ b/web-common/src/features/chat/core/conversation.ts @@ -5,13 +5,13 @@ import { getRuntimeServiceGetConversationQueryKey, getRuntimeServiceGetConversationQueryOptions, runtimeServiceForkConversation, - type RpcStatus, type RuntimeServiceCompleteBody, type V1CompleteStreamingResponse, type V1GetConversationResponse, type V1Message, } from "@rilldata/web-common/runtime-client"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; +import type { ConnectError } from "@connectrpc/connect"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { SSEFetchClient, SSEHttpError, @@ -25,7 +25,7 @@ import { type Readable, type Writable, } from "svelte/store"; -import type { HTTPError } from "../../../runtime-client/fetchWrapper"; +import { extractErrorMessage } from "../../../lib/errors"; import type { FeedbackCategory, FeedbackSentiment, @@ -73,13 +73,21 @@ export class Conversation { private hasReceivedFirstMessage = false; private readonly conversationQuery: CreateQueryResult< V1GetConversationResponse, - RpcStatus + ConnectError >; private readonly messageById = new Map(); // Reactive store for conversationId - enables query to auto-update when ID changes private readonly conversationIdStore: Writable; + private get instanceId(): string { + return this.client.instanceId; + } + + public get runtimeClient(): RuntimeClient { + return this.client; + } + public get conversationId(): string { return get(this.conversationIdStore); } @@ -89,7 +97,7 @@ export class Conversation { } constructor( - private readonly instanceId: string, + private readonly client: RuntimeClient, initialConversationId: string, private readonly agent: string = ToolName.ANALYST_AGENT, ) { @@ -100,8 +108,8 @@ export class Conversation { this.conversationIdStore, ($conversationId) => getRuntimeServiceGetConversationQueryOptions( - this.instanceId, - $conversationId, + this.client, + { conversationId: $conversationId }, { query: { enabled: $conversationId !== NEW_CONVERSATION_ID, @@ -135,7 +143,7 @@ export class Conversation { */ public getConversationQuery(): CreateQueryResult< V1GetConversationResponse, - RpcStatus + ConnectError > { return this.conversationQuery; } @@ -144,11 +152,10 @@ export class Conversation { * Get a reactive store for conversation query errors */ public getConversationQueryError(): Readable { - return derived( - this.getConversationQuery(), - ($getConversationQuery) => - ($getConversationQuery.error as HTTPError)?.response?.data?.message ?? - null, + return derived(this.getConversationQuery(), ($getConversationQuery) => + $getConversationQuery.error + ? extractErrorMessage($getConversationQuery.error) + : null, ); } @@ -334,7 +341,7 @@ export class Conversation { this.ensureSSEClient(); this.sseClient!.stop(); - const baseUrl = `${get(runtime).host}/v1/instances/${this.instanceId}/ai/complete/stream?stream=messages`; + const baseUrl = `${this.client.host}/v1/instances/${this.instanceId}/ai/complete/stream?stream=messages`; const requestBody = { instanceId: this.instanceId, @@ -353,6 +360,7 @@ export class Conversation { await this.sseClient!.start(baseUrl, { method: "POST", body: requestBody, + getJwt: () => this.client.getJwt(), }); } @@ -464,11 +472,9 @@ export class Conversation { private async forkConversation(): Promise { const originalConversationId = this.conversationId; - const response = await runtimeServiceForkConversation( - this.instanceId, - this.conversationId, - {}, - ); + const response = await runtimeServiceForkConversation(this.client, { + conversationId: this.conversationId, + }); if (!response.conversationId) { throw new Error("Fork response missing conversation ID"); @@ -480,11 +486,11 @@ export class Conversation { // This ensures the UI shows the conversation history immediately const originalCacheKey = getRuntimeServiceGetConversationQueryKey( this.instanceId, - originalConversationId, + { conversationId: originalConversationId }, ); const forkedCacheKey = getRuntimeServiceGetConversationQueryKey( this.instanceId, - forkedConversationId, + { conversationId: forkedConversationId }, ); const originalData = queryClient.getQueryData(originalCacheKey); @@ -513,12 +519,12 @@ export class Conversation { private transitionToRealConversation(realConversationId: string): void { const oldCacheKey = getRuntimeServiceGetConversationQueryKey( this.instanceId, - this.conversationId, // This is still "new" + { conversationId: this.conversationId }, // This is still "new" ); const newCacheKey = getRuntimeServiceGetConversationQueryKey( this.instanceId, - realConversationId, + { conversationId: realConversationId }, ); // Get existing data from "new" conversation cache @@ -571,10 +577,9 @@ export class Conversation { * Add message to TanStack Query cache */ private addMessageToCache(message: V1Message): void { - const cacheKey = getRuntimeServiceGetConversationQueryKey( - this.instanceId, - this.conversationId, - ); + const cacheKey = getRuntimeServiceGetConversationQueryKey(this.instanceId, { + conversationId: this.conversationId, + }); queryClient.setQueryData(cacheKey, (old) => { if (!old?.conversation) { // Create initial conversation structure if it doesn't exist @@ -606,10 +611,9 @@ export class Conversation { * Remove message from TanStack Query cache (for rollback) */ private removeMessageFromCache(messageId: string): void { - const cacheKey = getRuntimeServiceGetConversationQueryKey( - this.instanceId, - this.conversationId, - ); + const cacheKey = getRuntimeServiceGetConversationQueryKey(this.instanceId, { + conversationId: this.conversationId, + }); queryClient.setQueryData(cacheKey, (old) => { if (!old?.conversation) return old; diff --git a/web-common/src/features/chat/core/messages/Messages.svelte b/web-common/src/features/chat/core/messages/Messages.svelte index 877603d5a1d..a9ea5e3e2ef 100644 --- a/web-common/src/features/chat/core/messages/Messages.svelte +++ b/web-common/src/features/chat/core/messages/Messages.svelte @@ -1,9 +1,8 @@ diff --git a/web-common/src/features/column-profile/column-types/VarcharProfile.svelte b/web-common/src/features/column-profile/column-types/VarcharProfile.svelte index c4ee7004195..2f072d9c725 100644 --- a/web-common/src/features/column-profile/column-types/VarcharProfile.svelte +++ b/web-common/src/features/column-profile/column-types/VarcharProfile.svelte @@ -1,7 +1,6 @@ diff --git a/web-common/src/features/connectors/code-utils.spec.ts b/web-common/src/features/connectors/code-utils.spec.ts index 0f9f840b39f..6dddbc03865 100644 --- a/web-common/src/features/connectors/code-utils.spec.ts +++ b/web-common/src/features/connectors/code-utils.spec.ts @@ -1,5 +1,6 @@ import { describe, expect, it, vi, beforeEach } from "vitest"; import type { V1ConnectorDriver } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { replaceOlapConnectorInYAML, replaceOrAddEnvVariable, @@ -12,19 +13,6 @@ import { updateDotEnvWithSecrets, } from "./code-utils"; -// Mock runtime store and API -vi.mock("../../runtime-client/runtime-store", () => ({ - runtime: { subscribe: vi.fn() }, -})); - -vi.mock("svelte/store", async (importOriginal) => { - const actual = await importOriginal(); - return { - ...actual, - get: () => ({ instanceId: "test-instance" }), - }; -}); - // Import the template for testing const YAML_MODEL_TEMPLATE = `type: model materialize: true\n @@ -818,6 +806,10 @@ describe("compileConnectorYAML", () => { }); describe("updateDotEnvWithSecrets", () => { + const mockClient = { + instanceId: "test-instance-id", + } as unknown as RuntimeClient; + // Track fetchQuery calls so tests can inspect them let mockEnvBlob = ""; const mockQueryClient = { @@ -842,6 +834,7 @@ describe("updateDotEnvWithSecrets", () => { sql: "SELECT 1", }; const { newBlob } = await updateDotEnvWithSecrets( + mockClient, mockQueryClient as any, connector, formValues, @@ -865,6 +858,7 @@ describe("updateDotEnvWithSecrets", () => { }, }; const { newBlob } = await updateDotEnvWithSecrets( + mockClient, mockQueryClient as any, connector, formValues, @@ -881,6 +875,7 @@ describe("updateDotEnvWithSecrets", () => { const connector: V1ConnectorDriver = { name: "clickhouse" }; const formValues: Record = { password: "new_pw" }; const { newBlob, originalBlob } = await updateDotEnvWithSecrets( + mockClient, mockQueryClient as any, connector, formValues, @@ -898,6 +893,7 @@ describe("updateDotEnvWithSecrets", () => { const connector: V1ConnectorDriver = { name: "clickhouse" }; const formValues: Record = { password: "new_value" }; const { newBlob } = await updateDotEnvWithSecrets( + mockClient, mockQueryClient as any, connector, formValues, @@ -915,6 +911,7 @@ describe("updateDotEnvWithSecrets", () => { dsn: undefined, }; const { newBlob } = await updateDotEnvWithSecrets( + mockClient, mockQueryClient as any, connector, formValues, @@ -932,6 +929,7 @@ describe("updateDotEnvWithSecrets", () => { ], }; const { newBlob } = await updateDotEnvWithSecrets( + mockClient, mockQueryClient as any, connector, formValues, @@ -949,6 +947,7 @@ describe("updateDotEnvWithSecrets", () => { headers: [{ key: "Authorization", value: "Bearer abc123" }], }; const { newBlob } = await updateDotEnvWithSecrets( + mockClient, mockQueryClient as any, connector, formValues, @@ -965,6 +964,7 @@ describe("updateDotEnvWithSecrets", () => { headers: [{ key: "X-API-Key", value: "raw_api_key_value" }], }; const { newBlob } = await updateDotEnvWithSecrets( + mockClient, mockQueryClient as any, connector, formValues, @@ -980,6 +980,7 @@ describe("updateDotEnvWithSecrets", () => { headers: [{ key: "Authorization", value: "Token secret_tok" }], }; const { newBlob } = await updateDotEnvWithSecrets( + mockClient, mockQueryClient as any, connector, formValues, @@ -999,6 +1000,7 @@ describe("updateDotEnvWithSecrets", () => { ], }; const { newBlob } = await updateDotEnvWithSecrets( + mockClient, mockQueryClient as any, connector, formValues, @@ -1010,6 +1012,7 @@ describe("updateDotEnvWithSecrets", () => { it("should invalidate cache before reading .env", async () => { const connector: V1ConnectorDriver = { name: "clickhouse" }; await updateDotEnvWithSecrets( + mockClient, mockQueryClient as any, connector, { password: "pw" }, @@ -1030,6 +1033,7 @@ describe("updateDotEnvWithSecrets", () => { const connector: V1ConnectorDriver = { name: "clickhouse" }; const { newBlob, originalBlob } = await updateDotEnvWithSecrets( + mockClient, mockQueryClient as any, connector, { password: "pw" }, @@ -1047,6 +1051,7 @@ describe("updateDotEnvWithSecrets", () => { const connector: V1ConnectorDriver = { name: "clickhouse" }; await expect( updateDotEnvWithSecrets( + mockClient, mockQueryClient as any, connector, { password: "pw" }, @@ -1069,6 +1074,7 @@ describe("updateDotEnvWithSecrets", () => { dsn: "clickhouse://...", }; const { newBlob } = await updateDotEnvWithSecrets( + mockClient, mockQueryClient as any, connector, formValues, diff --git a/web-common/src/features/connectors/code-utils.ts b/web-common/src/features/connectors/code-utils.ts index 6fb5f9319ca..52fbd288743 100644 --- a/web-common/src/features/connectors/code-utils.ts +++ b/web-common/src/features/connectors/code-utils.ts @@ -1,14 +1,14 @@ import { QueryClient } from "@tanstack/svelte-query"; -import { get } from "svelte/store"; import { type V1ConnectorDriver, type ConnectorDriverProperty, getRuntimeServiceGetFileQueryKey, runtimeServiceGetFile, } from "../../runtime-client"; -import { runtime } from "../../runtime-client/runtime-store"; +import type { RuntimeClient } from "../../runtime-client/v2"; import { fileArtifacts } from "@rilldata/web-common/features/entity-management/file-artifacts"; import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors"; +import { extractErrorMessage } from "@rilldata/web-common/lib/errors"; import { eventBus } from "@rilldata/web-common/lib/event-bus/event-bus"; import { getName, @@ -256,6 +256,7 @@ driver: ${driverName}`; } export async function updateDotEnvWithSecrets( + client: RuntimeClient, queryClient: QueryClient, connector: V1ConnectorDriver, formValues: Record, @@ -276,27 +277,27 @@ export async function updateDotEnvWithSecrets( blob = opts.existingEnvBlob; originalBlob = opts.existingEnvBlob; } else { - const instanceId = get(runtime).instanceId; - // Invalidate the cache to ensure we get fresh .env content // This prevents overwriting credentials added by a previous step await queryClient.invalidateQueries({ - queryKey: getRuntimeServiceGetFileQueryKey(instanceId, { path: ".env" }), + queryKey: getRuntimeServiceGetFileQueryKey(client.instanceId, { + path: ".env", + }), }); // Get the existing .env file with fresh data try { const file = await queryClient.fetchQuery({ - queryKey: getRuntimeServiceGetFileQueryKey(instanceId, { + queryKey: getRuntimeServiceGetFileQueryKey(client.instanceId, { path: ".env", }), - queryFn: () => runtimeServiceGetFile(instanceId, { path: ".env" }), + queryFn: () => runtimeServiceGetFile(client, { path: ".env" }), }); blob = file.blob || ""; originalBlob = blob; // Keep original for conflict detection } catch (error) { // Handle the case where the .env file does not exist - if (error?.response?.data?.message?.includes("no such file")) { + if (extractErrorMessage(error).includes("no such file")) { blob = ""; originalBlob = ""; } else { @@ -508,16 +509,16 @@ export function makeEnvVarKey( } export async function updateRillYAMLWithOlapConnector( + client: RuntimeClient, queryClient: QueryClient, newConnector: string, ): Promise { // Get the existing rill.yaml file - const instanceId = get(runtime).instanceId; const file = await queryClient.fetchQuery({ - queryKey: getRuntimeServiceGetFileQueryKey(instanceId, { + queryKey: getRuntimeServiceGetFileQueryKey(client.instanceId, { path: "rill.yaml", }), - queryFn: () => runtimeServiceGetFile(instanceId, { path: "rill.yaml" }), + queryFn: () => runtimeServiceGetFile(client, { path: "rill.yaml" }), }); const blob = file.blob || ""; @@ -543,19 +544,20 @@ export function replaceOlapConnectorInYAML( } export async function createYamlModelFromTable( + client: RuntimeClient, queryClient: QueryClient, connector: string, database: string, databaseSchema: string, table: string, ): Promise<[string, string]> { - const instanceId = get(runtime).instanceId; - // Get driver name for makeSufficientlyQualifiedTableName - const analyzeConnectorsQueryKey = - getRuntimeServiceAnalyzeConnectorsQueryKey(instanceId); + const analyzeConnectorsQueryKey = getRuntimeServiceAnalyzeConnectorsQueryKey( + client.instanceId, + {}, + ); const analyzeConnectorsQueryFn = async () => - runtimeServiceAnalyzeConnectors(instanceId); + runtimeServiceAnalyzeConnectors(client, {}); const connectors = await queryClient.fetchQuery({ queryKey: analyzeConnectorsQueryKey, queryFn: analyzeConnectorsQueryFn, @@ -599,7 +601,7 @@ export async function createYamlModelFromTable( .replace("{{ dev_section }}", devSection); // Write the YAML file - await runtimeServicePutFile(instanceId, { + await runtimeServicePutFile(client, { path: newModelPath, blob: yamlContent, createOnly: true, @@ -607,13 +609,14 @@ export async function createYamlModelFromTable( // Invalidate relevant queries await queryClient.invalidateQueries({ - queryKey: ["runtimeServiceListFiles", instanceId], + queryKey: ["runtimeServiceListFiles", client.instanceId], }); return ["/" + newModelPath, newModelName]; } export async function createSqlModelFromTable( + client: RuntimeClient, queryClient: QueryClient, connector: string, database: string, @@ -621,13 +624,13 @@ export async function createSqlModelFromTable( table: string, addDevLimit: boolean = true, ): Promise<[string, string]> { - const instanceId = get(runtime).instanceId; - // Get driver name - const analyzeConnectorsQueryKey = - getRuntimeServiceAnalyzeConnectorsQueryKey(instanceId); + const analyzeConnectorsQueryKey = getRuntimeServiceAnalyzeConnectorsQueryKey( + client.instanceId, + {}, + ); const analyzeConnectorsQueryFn = async () => - runtimeServiceAnalyzeConnectors(instanceId); + runtimeServiceAnalyzeConnectors(client, {}); const connectors = await queryClient.fetchQuery({ queryKey: analyzeConnectorsQueryKey, queryFn: analyzeConnectorsQueryFn, @@ -641,16 +644,18 @@ export async function createSqlModelFromTable( const driverName = analyzedConnector.driver?.name as string; // Determine whether the connector is the default OLAP connector - const runtimeInstanceQueryKey = - getRuntimeServiceGetInstanceQueryKey(instanceId); + const runtimeInstanceQueryKey = getRuntimeServiceGetInstanceQueryKey( + client.instanceId, + {}, + ); const runtimeInstanceQueryFn = async () => - runtimeServiceGetInstance(instanceId, { sensitive: true }); + runtimeServiceGetInstance(client, { sensitive: true }); const runtimeInstance = await queryClient.fetchQuery({ queryKey: runtimeInstanceQueryKey, queryFn: runtimeInstanceQueryFn, }); if (!runtimeInstance) { - throw new Error(`Could not find runtime instance ${instanceId}`); + throw new Error(`Could not find runtime instance ${client.instanceId}`); } const isDefaultOLAPConnector = runtimeInstance?.instance?.olapConnector === connector; @@ -693,7 +698,7 @@ export async function createSqlModelFromTable( modelSQL += `\n${devLimit}`; } - await runtimeServicePutFile(instanceId, { + await runtimeServicePutFile(client, { path: newModelPath, blob: modelSQL, createOnly: true, diff --git a/web-common/src/features/connectors/explorer/ConnectorEntry.svelte b/web-common/src/features/connectors/explorer/ConnectorEntry.svelte index 56214e50d8b..8c4d8a93826 100644 --- a/web-common/src/features/connectors/explorer/ConnectorEntry.svelte +++ b/web-common/src/features/connectors/explorer/ConnectorEntry.svelte @@ -1,11 +1,9 @@ diff --git a/web-common/src/features/connectors/explorer/DatabaseEntry.svelte b/web-common/src/features/connectors/explorer/DatabaseEntry.svelte index 5a60f9708c1..f390b85833a 100644 --- a/web-common/src/features/connectors/explorer/DatabaseEntry.svelte +++ b/web-common/src/features/connectors/explorer/DatabaseEntry.svelte @@ -2,23 +2,26 @@ import { Database } from "lucide-svelte"; import { slide } from "svelte/transition"; import CaretDownIcon from "../../../components/icons/CaretDownIcon.svelte"; + import { extractErrorMessage } from "../../../lib/errors"; import { LIST_SLIDE_DURATION as duration } from "../../../layout/config"; import type { V1AnalyzedConnector } from "../../../runtime-client"; + import { useRuntimeClient } from "../../../runtime-client/v2"; import DatabaseSchemaEntry from "./DatabaseSchemaEntry.svelte"; import { useListDatabaseSchemas } from "../selectors"; import type { ConnectorExplorerStore } from "./connector-explorer-store"; - export let instanceId: string; export let connector: V1AnalyzedConnector; export let database: string; export let store: ConnectorExplorerStore; + const client = useRuntimeClient(); + $: connectorName = connector?.name as string; $: expandedStore = store.getItem(connectorName, database); $: expanded = $expandedStore; $: databaseSchemasQuery = useListDatabaseSchemas( - instanceId, + client, connectorName, database, ); @@ -49,9 +52,7 @@
    {#if expanded} {#if error} - Error: {error.message || error.response?.data?.message} + Error: {extractErrorMessage(error)} {:else if isLoading} Loading schemas... {:else if data} @@ -60,7 +61,6 @@ {:else} {#each data as schema (schema)} import { slide } from "svelte/transition"; + import { extractErrorMessage } from "../../../lib/errors"; import { LIST_SLIDE_DURATION as duration } from "../../../layout/config"; import type { V1AnalyzedConnector } from "../../../runtime-client"; + import { useRuntimeClient } from "../../../runtime-client/v2"; import DatabaseEntry from "./DatabaseEntry.svelte"; import { useListDatabaseSchemas } from "../selectors"; import type { ConnectorExplorerStore } from "./connector-explorer-store"; - export let instanceId: string; export let connector: V1AnalyzedConnector; export let store: ConnectorExplorerStore; + const client = useRuntimeClient(); + $: connectorName = connector?.name as string; $: hasError = !!connector?.errorMessage; $: queryEnabled = !hasError; $: databaseSchemasQuery = useListDatabaseSchemas( - instanceId, + client, connectorName, undefined, queryEnabled, @@ -34,16 +37,14 @@ {:else if isLoading && queryEnabled} Loading tables... {:else if error && queryEnabled} - Error: {error.message || error.response?.data?.message} + Error: {extractErrorMessage(error)} {:else if data} {#if data.length === 0} No tables found {:else}
      {#each data as database (database)} - + {/each}
    {/if} diff --git a/web-common/src/features/connectors/explorer/DatabaseSchemaEntry.svelte b/web-common/src/features/connectors/explorer/DatabaseSchemaEntry.svelte index 0992754eb7f..d3996e2b3fb 100644 --- a/web-common/src/features/connectors/explorer/DatabaseSchemaEntry.svelte +++ b/web-common/src/features/connectors/explorer/DatabaseSchemaEntry.svelte @@ -5,25 +5,27 @@ V1AnalyzedConnector, V1TableInfo, } from "../../../runtime-client"; + import { useRuntimeClient } from "../../../runtime-client/v2"; import TableEntry from "./TableEntry.svelte"; import { useInfiniteListTables } from "../selectors"; import Button from "../../../components/button/Button.svelte"; import type { ConnectorExplorerStore } from "./connector-explorer-store"; import { onMount } from "svelte"; - export let instanceId: string; export let connector: V1AnalyzedConnector; export let database: string; export let databaseSchema: string; export let store: ConnectorExplorerStore; + const client = useRuntimeClient(); + $: connectorName = connector?.name as string; $: expandedStore = store.getItem(connectorName, database, databaseSchema); $: expanded = $expandedStore; $: tablesQuery = useInfiniteListTables( - instanceId, + client, connectorName, database, databaseSchema, diff --git a/web-common/src/features/connectors/explorer/TableEntry.svelte b/web-common/src/features/connectors/explorer/TableEntry.svelte index 5a9edd94756..f0621c2d1bc 100644 --- a/web-common/src/features/connectors/explorer/TableEntry.svelte +++ b/web-common/src/features/connectors/explorer/TableEntry.svelte @@ -8,7 +8,7 @@ import TableMenuItems from "./TableMenuItems.svelte"; import TableSchema from "./TableSchema.svelte"; import { useIsModelingSupportedForConnectorOLAP as useIsModelingSupportedForConnector } from "../selectors"; - import { runtime } from "../../../runtime-client/runtime-store"; + import { useRuntimeClient } from "../../../runtime-client/v2"; import type { ConnectorExplorerStore } from "./connector-explorer-store"; import { makeFullyQualifiedTableName, @@ -27,14 +27,15 @@ let contextMenuOpen = false; + const client = useRuntimeClient(); + $: expandedStore = store.getItem(connector, database, databaseSchema, table); $: showSchema = $expandedStore; const { allowContextMenu, allowNavigateToTable, allowShowSchema } = store; - $: ({ instanceId: runtimeInstanceId } = $runtime); $: isModelingSupportedForConnector = useIsModelingSupportedForConnector( - runtimeInstanceId, + client, connector, ); $: isModelingSupported = $isModelingSupportedForConnector.data; diff --git a/web-common/src/features/connectors/explorer/TableMenuItems.svelte b/web-common/src/features/connectors/explorer/TableMenuItems.svelte index f3de0cc180d..5a8bf3e3bf0 100644 --- a/web-common/src/features/connectors/explorer/TableMenuItems.svelte +++ b/web-common/src/features/connectors/explorer/TableMenuItems.svelte @@ -10,7 +10,7 @@ MetricsEventScreenName, MetricsEventSpace, } from "@rilldata/web-common/metrics/service/MetricsTypes"; - import { runtime } from "../../../runtime-client/runtime-store"; + import { useRuntimeClient } from "../../../runtime-client/v2"; import { generateMetricsFromTable } from "../../metrics-views/ai-generation/generateMetricsView"; import { createSqlModelFromTable, @@ -27,7 +27,8 @@ export let isModelingSupported: boolean | undefined = false; export let isOlapConnector: boolean = false; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); + $: ({ instanceId } = client); async function handleCreateModel( modelCreationFn: () => Promise<[string, string]>, @@ -52,6 +53,7 @@ if (isModelingSupported) { await handleCreateModel(() => createSqlModelFromTable( + client, queryClient, connector, database, @@ -62,6 +64,7 @@ } else if (showGenerateModel) { await handleCreateModel(() => createYamlModelFromTable( + client, queryClient, connector, database, @@ -74,6 +77,7 @@ async function handleGenerateMetrics() { await generateMetricsFromTable( + client, instanceId, connector, database, @@ -86,6 +90,7 @@ async function handleGenerateDashboard() { await generateMetricsFromTable( + client, instanceId, connector, database, diff --git a/web-common/src/features/connectors/explorer/TableSchema.svelte b/web-common/src/features/connectors/explorer/TableSchema.svelte index d4d42c8251d..1b96278185a 100644 --- a/web-common/src/features/connectors/explorer/TableSchema.svelte +++ b/web-common/src/features/connectors/explorer/TableSchema.svelte @@ -1,17 +1,19 @@ diff --git a/web-common/src/features/dashboards/filters/Filters.svelte b/web-common/src/features/dashboards/filters/Filters.svelte index 349089f4129..0bb5ada2bb3 100644 --- a/web-common/src/features/dashboards/filters/Filters.svelte +++ b/web-common/src/features/dashboards/filters/Filters.svelte @@ -21,7 +21,7 @@ V1TimeGrain, type V1ExploreTimeRange, } from "@rilldata/web-common/runtime-client"; - import { isMetricsViewQuery } from "@rilldata/web-common/runtime-client/invalidation.ts"; + import { invalidationForMetricsViewData } from "@rilldata/web-common/runtime-client/invalidation.ts"; import { DateTime, Interval } from "luxon"; import { flip } from "svelte/animate"; import { fly } from "svelte/transition"; @@ -48,7 +48,7 @@ import Metadata from "../time-controls/super-pill/components/Metadata.svelte"; import { getValidComparisonOption } from "../time-controls/time-range-store"; import { getPinnedTimeZones } from "../url-state/getDefaultExplorePreset"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; const { rillTime } = featureFlags; @@ -101,9 +101,9 @@ let showDefaultItem = false; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); - $: timeRangeQuery = useMetricsViewTimeRange(instanceId, metricsViewName); + $: timeRangeQuery = useMetricsViewTimeRange(client, metricsViewName); $: timeRangeSummary = $timeRangeQuery.data?.timeRangeSummary; @@ -224,7 +224,7 @@ await queryClient.cancelQueries({ predicate: (query) => - isMetricsViewQuery(query.queryHash, metricsViewName), + invalidationForMetricsViewData(query, metricsViewName), }); metricsExplorerStore.setTimeDimension($exploreName, column); @@ -234,6 +234,7 @@ const { interval, grain } = await deriveInterval( timeRangeName, + client, metricsViewName, activeTimeZone, column, @@ -291,11 +292,12 @@ await queryClient.cancelQueries({ predicate: (query) => - isMetricsViewQuery(query.queryHash, metricsViewName), + invalidationForMetricsViewData(query, metricsViewName), }); const { interval, grain } = await deriveInterval( alias, + client, metricsViewName, tz, selectedTimeDimension, diff --git a/web-common/src/features/dashboards/filters/dimension-filters/DimensionFilter.svelte b/web-common/src/features/dashboards/filters/dimension-filters/DimensionFilter.svelte index 95745a1a663..d62d3be0ded 100644 --- a/web-common/src/features/dashboards/filters/dimension-filters/DimensionFilter.svelte +++ b/web-common/src/features/dashboards/filters/dimension-filters/DimensionFilter.svelte @@ -21,7 +21,7 @@ import DimensionFilterFooter from "@rilldata/web-common/features/dashboards/filters/dimension-filters/DimensionFilterFooter.svelte"; import DimensionFilterModeSelector from "@rilldata/web-common/features/dashboards/filters/dimension-filters/DimensionFilterModeSelector.svelte"; import type { V1Expression } from "@rilldata/web-common/runtime-client"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { fly } from "svelte/transition"; import { useAllSearchResultsCount, @@ -67,7 +67,7 @@ : []; let curPinned = filterData.pinned; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); $: ({ name, @@ -104,24 +104,19 @@ (curMode === DimensionFilterMode.InList && searchedBulkValues.length > 0)); - $: searchResultsQuery = useDimensionSearch( - instanceId, - metricsViewNames, - name, - { - mode: curMode, - values: - curMode === DimensionFilterMode.Select - ? selectedValues - : searchedBulkValues, - searchText: curSearchText, - timeStart, - timeEnd, - timeDimension, - enabled: enableSearchQuery, - metricsViewWheres: expressionMap, - }, - ); + $: searchResultsQuery = useDimensionSearch(client, metricsViewNames, name, { + mode: curMode, + values: + curMode === DimensionFilterMode.Select + ? selectedValues + : searchedBulkValues, + searchText: curSearchText, + timeStart, + timeEnd, + timeDimension, + enabled: enableSearchQuery, + metricsViewWheres: expressionMap, + }); $: ({ data: searchResults, error: errorFromSearchResults, @@ -136,7 +131,7 @@ searchedBulkValues.length > 0)); $: allSearchResultsCountQuery = useAllSearchResultsCount( - instanceId, + client, metricsViewNames, name, { diff --git a/web-common/src/features/dashboards/filters/dimension-filters/DimensionFilterReadOnlyChip.svelte b/web-common/src/features/dashboards/filters/dimension-filters/DimensionFilterReadOnlyChip.svelte index b3c3751cf4f..591bf95444f 100644 --- a/web-common/src/features/dashboards/filters/dimension-filters/DimensionFilterReadOnlyChip.svelte +++ b/web-common/src/features/dashboards/filters/dimension-filters/DimensionFilterReadOnlyChip.svelte @@ -6,7 +6,7 @@ useDimensionSearch, } from "@rilldata/web-common/features/dashboards/filters/dimension-filters/dimension-filter-values"; import DimensionFilterChipBody from "@rilldata/web-common/features/dashboards/filters/dimension-filters/DimensionFilterChipBody.svelte"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; export let name: string; export let metricsViewNames: string[]; @@ -19,7 +19,7 @@ export let timeEnd: string | undefined; export let pinned = false; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); $: effectiveLabel = isInclude ? label : `Exclude ${label}`; $: sanitisedSearchText = inputText?.replace(/^%/, "").replace(/%$/, ""); @@ -28,19 +28,14 @@ mode === DimensionFilterMode.Select || (mode === DimensionFilterMode.Contains && !!inputText) || (mode === DimensionFilterMode.InList && values.length > 0); - $: searchResultsQuery = useDimensionSearch( - instanceId, - metricsViewNames, - name, - { - mode, - values, - searchText: inputText ?? "", - timeStart, - timeEnd, - enabled: enableSearchQuery, - }, - ); + $: searchResultsQuery = useDimensionSearch(client, metricsViewNames, name, { + mode, + values, + searchText: inputText ?? "", + timeStart, + timeEnd, + enabled: enableSearchQuery, + }); $: ({ data: searchResults, isFetching: isFetchingFromSearchResults } = $searchResultsQuery); $: correctedSearchResults = enableSearchQuery ? searchResults : []; @@ -48,7 +43,7 @@ (mode === DimensionFilterMode.Contains && !!inputText) || (mode === DimensionFilterMode.InList && values.length > 0); $: allSearchResultsCountQuery = useAllSearchResultsCount( - instanceId, + client, metricsViewNames, name, { diff --git a/web-common/src/features/dashboards/filters/dimension-filters/dimension-filter-values.ts b/web-common/src/features/dashboards/filters/dimension-filters/dimension-filter-values.ts index 0530c4088fd..dde8b1356f5 100644 --- a/web-common/src/features/dashboards/filters/dimension-filters/dimension-filter-values.ts +++ b/web-common/src/features/dashboards/filters/dimension-filters/dimension-filter-values.ts @@ -13,6 +13,7 @@ import { V1BuiltinMeasure, } from "@rilldata/web-common/runtime-client"; import type { V1Expression } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { mergeDimensionAndMeasureFilters } from "../measure-filters/measure-filter-utils"; import { getFiltersForOtherDimensions } from "../../selectors"; @@ -37,7 +38,7 @@ type DimensionSearchArgs = { * even if they're not in the top 250. */ export function useDimensionSearch( - instanceId: string, + client: RuntimeClient, metricsViewNames: string[], dimensionName: string, { @@ -72,9 +73,9 @@ export function useDimensionSearch( }); return createQueryServiceMetricsViewAggregation( - instanceId, - mvName, + client, { + metricsView: mvName, dimensions: [{ name: dimensionName }], timeRange: { start: timeStart, end: timeEnd, timeDimension }, limit: "250", @@ -128,7 +129,7 @@ export function useDimensionSearch( * 3. For Contains mode, it returns the count of values matching the search text. */ export function useAllSearchResultsCount( - instanceId: string, + client: RuntimeClient, metricsViewNames: string[], dimensionName: string, { @@ -160,9 +161,9 @@ export function useAllSearchResultsCount( }); return createQueryServiceMetricsViewAggregation( - instanceId, - mvName, + client, { + metricsView: mvName, measures: [ { name: dimensionName + "__distinct_count", diff --git a/web-common/src/features/dashboards/filters/test/render-filter-component.ts b/web-common/src/features/dashboards/filters/test/render-filter-component.ts index 04eb383c890..afec7c2c3fc 100644 --- a/web-common/src/features/dashboards/filters/test/render-filter-component.ts +++ b/web-common/src/features/dashboards/filters/test/render-filter-component.ts @@ -2,6 +2,10 @@ import Filters from "@rilldata/web-common/features/dashboards/filters/Filters.sv import { DEFAULT_STORE_KEY } from "@rilldata/web-common/features/dashboards/state-managers/state-managers"; import { AD_BIDS_METRICS_NAME } from "@rilldata/web-common/features/dashboards/stores/test-data/data"; import { initStateManagers } from "@rilldata/web-common/features/dashboards/stores/test-data/helpers"; +import { + RUNTIME_CONTEXT_KEY, + RuntimeClient, +} from "@rilldata/web-common/runtime-client/v2"; import { render } from "@testing-library/svelte"; export function renderFilterComponent(hasTimeSeries = false) { @@ -13,9 +17,13 @@ export function renderFilterComponent(hasTimeSeries = false) { metricsViewName: AD_BIDS_METRICS_NAME, hasTimeSeries, }, - context: new Map([ + context: new Map([ [DEFAULT_STORE_KEY as unknown as string, stateManagers as unknown], ["$$_queryClient", queryClient as unknown], + [ + RUNTIME_CONTEXT_KEY, + new RuntimeClient({ host: "http://localhost", instanceId: "test" }), + ], ]), }); diff --git a/web-common/src/features/dashboards/granular-access-policies/ViewAsButton.svelte b/web-common/src/features/dashboards/granular-access-policies/ViewAsButton.svelte index fc50ec6e70e..582e787b95b 100644 --- a/web-common/src/features/dashboards/granular-access-policies/ViewAsButton.svelte +++ b/web-common/src/features/dashboards/granular-access-policies/ViewAsButton.svelte @@ -8,7 +8,7 @@ import Check from "../../../components/icons/Check.svelte"; import EyeIcon from "../../../components/icons/EyeIcon.svelte"; import Spacer from "../../../components/icons/Spacer.svelte"; - import { runtime } from "../../../runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { selectedMockUserStore } from "./stores"; import { useMockUsers } from "./useMockUsers"; import * as DropdownMenu from "@rilldata/web-common/components/dropdown-menu"; @@ -18,9 +18,9 @@ let viewAsMenuOpen = false; let open = false; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); - $: mockUsers = useMockUsers(instanceId); + $: mockUsers = useMockUsers(client); @@ -44,7 +44,7 @@ active={viewAsMenuOpen} removeTooltipText="Clear view" onRemove={() => { - updateDevJWT(queryClient, null); + updateDevJWT(queryClient, client, null); }} >
    @@ -61,7 +61,7 @@ {#each $mockUsers.data as user (user?.email)} { - updateDevJWT(queryClient, user); + updateDevJWT(queryClient, client, user); }} class="flex gap-x-2 items-center" > diff --git a/web-common/src/features/dashboards/granular-access-policies/resetSelectedMockUserAfterNavigate.ts b/web-common/src/features/dashboards/granular-access-policies/resetSelectedMockUserAfterNavigate.ts index 7db87032c0e..23099d4543e 100644 --- a/web-common/src/features/dashboards/granular-access-policies/resetSelectedMockUserAfterNavigate.ts +++ b/web-common/src/features/dashboards/granular-access-policies/resetSelectedMockUserAfterNavigate.ts @@ -1,6 +1,7 @@ import { beforeNavigate } from "$app/navigation"; import { selectedMockUserStore } from "@rilldata/web-common/features/dashboards/granular-access-policies/stores"; import { updateDevJWT } from "@rilldata/web-common/features/dashboards/granular-access-policies/updateDevJWT"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import type { QueryClient } from "@tanstack/svelte-query"; import { get } from "svelte/store"; @@ -13,7 +14,10 @@ import { get } from "svelte/store"; * under this scenario, the catalog entry returns a 404, and it's required to enter the top-level * `Dashboard.svelte` component. */ -export function resetSelectedMockUserAfterNavigate(queryClient: QueryClient) { +export function resetSelectedMockUserAfterNavigate( + queryClient: QueryClient, + client: RuntimeClient, +) { beforeNavigate(({ to, from }) => { if (!to?.params || !from?.params) return; @@ -21,7 +25,7 @@ export function resetSelectedMockUserAfterNavigate(queryClient: QueryClient) { from.params.name !== to.params.name && get(selectedMockUserStore) !== null ) { - updateDevJWT(queryClient, null).catch(console.error); + updateDevJWT(queryClient, client, null).catch(console.error); } }); } diff --git a/web-common/src/features/dashboards/granular-access-policies/updateDevJWT.ts b/web-common/src/features/dashboards/granular-access-policies/updateDevJWT.ts index da058a6a9a4..9b05b73d016 100644 --- a/web-common/src/features/dashboards/granular-access-policies/updateDevJWT.ts +++ b/web-common/src/features/dashboards/granular-access-policies/updateDevJWT.ts @@ -5,27 +5,24 @@ import { import type { MockUser } from "@rilldata/web-common/features/dashboards/granular-access-policies/useMockUsers"; import { runtimeServiceIssueDevJWT } from "@rilldata/web-common/runtime-client"; import { invalidateAllMetricsViews } from "@rilldata/web-common/runtime-client/invalidation"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import type { QueryClient } from "@tanstack/svelte-query"; -import { get } from "svelte/store"; export async function updateDevJWT( queryClient: QueryClient, + client: RuntimeClient, mockUser: MockUser | null, ) { selectedMockUserStore.set(mockUser); if (mockUser === null) { selectedMockUserJWT.set(null); - runtime.update((runtimeState) => { - runtimeState.jwt = undefined; - return runtimeState; - }); + client.updateJwt(undefined, "user"); } else { try { const { name, email, groups, admin, ...customAttributes } = mockUser; - const { jwt } = await runtimeServiceIssueDevJWT({ + const { jwt } = await runtimeServiceIssueDevJWT(client, { email, name: name || "Mock User", admin: !!admin, @@ -36,19 +33,11 @@ export async function updateDevJWT( if (!jwt) throw new Error("No JWT returned"); selectedMockUserJWT.set(jwt); - - runtime.update((runtimeState) => { - runtimeState.jwt = { - token: jwt, - receivedAt: Date.now(), - authContext: "mock", - }; - return runtimeState; - }); + client.updateJwt(jwt, "mock"); } catch { // no-op } } - return invalidateAllMetricsViews(queryClient, get(runtime).instanceId); + return invalidateAllMetricsViews(queryClient, client.instanceId); } diff --git a/web-common/src/features/dashboards/granular-access-policies/useMockUsers.ts b/web-common/src/features/dashboards/granular-access-policies/useMockUsers.ts index c72620dab36..897d9fc294e 100644 --- a/web-common/src/features/dashboards/granular-access-policies/useMockUsers.ts +++ b/web-common/src/features/dashboards/granular-access-policies/useMockUsers.ts @@ -1,5 +1,6 @@ import { parseDocument } from "yaml"; import { createRuntimeServiceGetFile } from "../../../runtime-client"; +import type { RuntimeClient } from "../../../runtime-client/v2"; export interface MockUser { email?: string; @@ -9,9 +10,9 @@ export interface MockUser { attributes?: { [key: string]: any }; } -export function useMockUsers(instanceId: string) { +export function useMockUsers(client: RuntimeClient) { return createRuntimeServiceGetFile( - instanceId, + client, { path: `rill.yaml` }, { query: { diff --git a/web-common/src/features/dashboards/granular-access-policies/useSecurityPolicyCheck.ts b/web-common/src/features/dashboards/granular-access-policies/useSecurityPolicyCheck.ts index 7284797e565..074f2ccdfdc 100644 --- a/web-common/src/features/dashboards/granular-access-policies/useSecurityPolicyCheck.ts +++ b/web-common/src/features/dashboards/granular-access-policies/useSecurityPolicyCheck.ts @@ -1,14 +1,19 @@ import { parse } from "yaml"; import { createRuntimeServiceGetFile } from "../../../runtime-client"; +import type { RuntimeClient } from "../../../runtime-client/v2"; -export function useDashboardPolicyCheck(instanceId: string, filePath: string) { +export function useDashboardPolicyCheck( + client: RuntimeClient, + filePath: string, +) { return createRuntimeServiceGetFile( - instanceId, + client, { path: filePath, }, { query: { + enabled: !!filePath, select: (data) => { if (!data.blob) return false; const yamlObj = parse(data.blob); @@ -20,9 +25,9 @@ export function useDashboardPolicyCheck(instanceId: string, filePath: string) { ); } -export function useRillYamlPolicyCheck(instanceId: string) { +export function useRillYamlPolicyCheck(client: RuntimeClient) { return createRuntimeServiceGetFile( - instanceId, + client, { path: "rill.yaml", }, diff --git a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte index 175ec27e91e..6eef745a851 100644 --- a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte +++ b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte @@ -13,6 +13,7 @@ createQueryServiceMetricsViewAggregation, V1Operation, } from "@rilldata/web-common/runtime-client"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { onMount } from "svelte"; import { getComparisonRequestMeasures, @@ -42,13 +43,13 @@ import type { selectedDimensionValues } from "../state-managers/selectors/dimension-filters"; import { getMeasuresForDimensionOrLeaderboardDisplay } from "../state-managers/selectors/dashboard-queries"; + const runtimeClient = useRuntimeClient(); const gutterWidth = 24; export let dimension: MetricsViewSpecDimension; export let timeRange: V1TimeRange; export let comparisonTimeRange: V1TimeRange | undefined; export let selectedValues: ReturnType; - export let instanceId: string; export let whereFilter: V1Expression; export let dimensionThresholdFilters: DimensionThresholdFilter[]; export let leaderboardSortByMeasureName: string; @@ -165,9 +166,9 @@ ); $: sortedQuery = createQueryServiceMetricsViewAggregation( - instanceId, - metricsViewName, + runtimeClient, { + metricsView: metricsViewName, dimensions: [{ name: dimensionName }], measures, timeRange, @@ -185,9 +186,9 @@ ); $: totalsQuery = createQueryServiceMetricsViewAggregation( - instanceId, - metricsViewName, + runtimeClient, { + metricsView: metricsViewName, measures: leaderboardMeasureNames.map((name) => ({ name })), where, timeRange, @@ -223,9 +224,9 @@ $: belowTheFoldDataLimit = maxValuesToShow - aboveTheFold.length; $: belowTheFoldDataQuery = createQueryServiceMetricsViewAggregation( - instanceId, - metricsViewName, + runtimeClient, { + metricsView: metricsViewName, dimensions: [{ name: dimensionName }], where: sanitiseExpression( createAndExpression( diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardDisplay.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardDisplay.svelte index 488c2bc9685..f4962712cee 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardDisplay.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardDisplay.svelte @@ -5,7 +5,7 @@ V1Expression, V1TimeRange, } from "@rilldata/web-common/runtime-client"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import type { DimensionThresholdFilter } from "web-common/src/features/dashboards/stores/explore-state"; import Leaderboard from "./Leaderboard.svelte"; import LeaderboardControls from "./LeaderboardControls.svelte"; @@ -43,9 +43,9 @@ dashboardStore, } = StateManagers; - let parentElement: HTMLDivElement; + const client = useRuntimeClient(); - $: ({ instanceId } = $runtime); + let parentElement: HTMLDivElement; // Reset column widths when the measure changes $: if ($leaderboardSortByMeasureName) { @@ -86,7 +86,6 @@ leaderboardShowContextForAllMeasures={$leaderboardShowContextForAllMeasures} {whereFilter} {dimensionThresholdFilters} - {instanceId} {tableWidth} {timeRange} {dimensionColumnWidth} @@ -98,7 +97,7 @@ {parentElement} {timeControlsReady} selectedValues={selectedDimensionValues( - $runtime.instanceId, + client, [metricsViewName], $dashboardStore.whereFilter, dimension.name, diff --git a/web-common/src/features/dashboards/nav-utils.ts b/web-common/src/features/dashboards/nav-utils.ts index 1fa05aa7775..c19aea7ff8c 100644 --- a/web-common/src/features/dashboards/nav-utils.ts +++ b/web-common/src/features/dashboards/nav-utils.ts @@ -3,6 +3,7 @@ import { ResourceKind } from "@rilldata/web-common/features/entity-management/re import type { Page } from "@sveltejs/kit"; import { derived } from "svelte/store"; import { getExploreValidSpecQueryOptions } from "@rilldata/web-common/features/explores/selectors.ts"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { createQuery } from "@tanstack/svelte-query"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient.ts"; @@ -53,10 +54,10 @@ export function getCanvasNameStore() { }); } -export function getActiveMetricsViewNameStore() { +export function getActiveMetricsViewNameStore(client: RuntimeClient) { const exploreNameStore = getExploreNameStore(); const validSpecQuery = createQuery( - getExploreValidSpecQueryOptions(exploreNameStore), + getExploreValidSpecQueryOptions(client, exploreNameStore), queryClient, ); diff --git a/web-common/src/features/dashboards/pivot/pivot-data-store.ts b/web-common/src/features/dashboards/pivot/pivot-data-store.ts index 142f293fbac..275e63cc73a 100644 --- a/web-common/src/features/dashboards/pivot/pivot-data-store.ts +++ b/web-common/src/features/dashboards/pivot/pivot-data-store.ts @@ -14,7 +14,7 @@ import type { V1MetricsViewAggregationResponse, V1MetricsViewAggregationSort, } from "@rilldata/web-common/runtime-client"; -import type { HTTPError } from "@rilldata/web-common/runtime-client/fetchWrapper"; +import type { ConnectError } from "@connectrpc/connect"; import type { CreateQueryResult } from "@tanstack/svelte-query"; import type { ColumnDef } from "@tanstack/svelte-table"; import { type Readable, derived, readable } from "svelte/store"; @@ -340,11 +340,11 @@ export function createPivotDataStore( let globalTotalsQuery: | Readable - | CreateQueryResult = + | CreateQueryResult = readable(null); let totalsRowQuery: | Readable - | CreateQueryResult = + | CreateQueryResult = readable(null); if (rowDimensionNames.length && measureNames.length) { globalTotalsQuery = createPivotAggregationRowQuery( @@ -484,8 +484,10 @@ export function createPivotDataStore( let tableCellQuery: | Readable - | CreateQueryResult = - readable(null); + | CreateQueryResult< + V1MetricsViewAggregationResponse, + ConnectError + > = readable(null); let columnDef: ColumnDef[] = []; if ( @@ -724,6 +726,7 @@ export const usePivotForExplore = memoizeMetricsStore( (ctx: StateManagers) => { const pivotConfig = getPivotConfig(ctx); const pivotDashboardContext: PivotDashboardContext = { + runtimeClient: ctx.runtimeClient, metricsViewName: ctx.metricsViewName, queryClient: ctx.queryClient, enabled: !!ctx.dashboardStore, diff --git a/web-common/src/features/dashboards/pivot/pivot-expansion.ts b/web-common/src/features/dashboards/pivot/pivot-expansion.ts index b73e7047a29..61badbb69a3 100644 --- a/web-common/src/features/dashboards/pivot/pivot-expansion.ts +++ b/web-common/src/features/dashboards/pivot/pivot-expansion.ts @@ -15,7 +15,7 @@ import type { V1MetricsViewAggregationResponse, V1MetricsViewAggregationResponseDataItem, } from "@rilldata/web-common/runtime-client"; -import type { HTTPError } from "@rilldata/web-common/runtime-client/fetchWrapper"; +import type { ConnectError } from "@connectrpc/connect"; import type { CreateQueryResult } from "@tanstack/svelte-query"; import { type Readable, derived, readable, writable } from "svelte/store"; import { @@ -356,8 +356,10 @@ export function queryExpandedRowMeasureValues( let subTableQuery: | Readable - | CreateQueryResult = - readable(null); + | CreateQueryResult< + V1MetricsViewAggregationResponse, + ConnectError + > = readable(null); if (config.colDimensionNames.length) { subTableQuery = createSubTableCellQuery( diff --git a/web-common/src/features/dashboards/pivot/pivot-export.ts b/web-common/src/features/dashboards/pivot/pivot-export.ts index b34e9aba28c..fb10e427b0a 100644 --- a/web-common/src/features/dashboards/pivot/pivot-export.ts +++ b/web-common/src/features/dashboards/pivot/pivot-export.ts @@ -12,7 +12,6 @@ import { type V1TimeRange, } from "@rilldata/web-common/runtime-client"; import { get } from "svelte/store"; -import { runtime } from "../../../runtime-client/runtime-store"; import type { StateManagers } from "../state-managers/state-managers"; import { getPivotConfig } from "./pivot-data-config"; import { prepareMeasureForComparison } from "./pivot-utils"; @@ -62,6 +61,7 @@ export function getPivotExportQuery(ctx: StateManagers, isScheduled: boolean) { const query: V1Query = { metricsViewAggregationRequest: getPivotAggregationRequest({ + instanceId: ctx.runtimeClient.instanceId, metricsViewName, timeDimension: exploreState.selectedTimeDimension || @@ -82,6 +82,7 @@ export function getPivotExportQuery(ctx: StateManagers, isScheduled: boolean) { } export function getPivotAggregationRequest({ + instanceId, metricsViewName, timeDimension, exploreState, @@ -93,6 +94,7 @@ export function getPivotAggregationRequest({ isFlat, pivotState, }: { + instanceId: string; metricsViewName: string; timeDimension: string; exploreState: ExploreState; @@ -179,7 +181,7 @@ export function getPivotAggregationRequest({ } return { - instanceId: get(runtime).instanceId, + instanceId, metricsView: metricsViewName, timeRange, comparisonTimeRange: comparisonTime, diff --git a/web-common/src/features/dashboards/pivot/pivot-queries.ts b/web-common/src/features/dashboards/pivot/pivot-queries.ts index 8d53d410431..51ebfa9c303 100644 --- a/web-common/src/features/dashboards/pivot/pivot-queries.ts +++ b/web-common/src/features/dashboards/pivot/pivot-queries.ts @@ -11,10 +11,9 @@ import { type V1MetricsViewAggregationResponse, type V1MetricsViewAggregationResponseDataItem, type V1MetricsViewAggregationSort, - createQueryServiceMetricsViewAggregation, } from "@rilldata/web-common/runtime-client"; -import type { HTTPError } from "@rilldata/web-common/runtime-client/fetchWrapper"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; +import type { ConnectError } from "@connectrpc/connect"; +import { createQueryServiceMetricsViewAggregation } from "@rilldata/web-common/runtime-client"; import { type CreateQueryResult, keepPreviousData, @@ -50,7 +49,7 @@ export function createPivotAggregationRowQuery( limit = "100", offset = "0", timeRange: TimeRangeString | undefined = undefined, -): CreateQueryResult { +): CreateQueryResult { if (!sort.length) { sort = [ { @@ -72,41 +71,39 @@ export function createPivotAggregationRowQuery( hasComparison = true; } - return derived( - [runtime, ctx.metricsViewName], - ([$runtime, metricsViewName], set) => - createQueryServiceMetricsViewAggregation( - $runtime.instanceId, - metricsViewName, - { - measures: prepareMeasureForComparison(measures), - dimensions, - where: sanitiseExpression(whereFilter, undefined), - timeRange: { - start: timeRange?.start ? timeRange.start : config.time.timeStart, - end: timeRange?.end ? timeRange.end : config.time.timeEnd, - timeDimension: config.time.timeDimension, - }, - comparisonTimeRange: - hasComparison && comparisonTime - ? { - start: comparisonTime.start, - end: comparisonTime.end, - timeDimension: config.time.timeDimension, - } - : undefined, - sort, - limit, - offset, + return derived([ctx.metricsViewName], ([metricsViewName], set) => + createQueryServiceMetricsViewAggregation( + ctx.runtimeClient, + { + metricsView: metricsViewName, + measures: prepareMeasureForComparison(measures), + dimensions, + where: sanitiseExpression(whereFilter, undefined), + timeRange: { + start: timeRange?.start ? timeRange.start : config.time.timeStart, + end: timeRange?.end ? timeRange.end : config.time.timeEnd, + timeDimension: config.time.timeDimension, }, - { - query: { - enabled: ctx.enabled, - placeholderData: keepPreviousData, - }, + comparisonTimeRange: + hasComparison && comparisonTime + ? { + start: comparisonTime.start, + end: comparisonTime.end, + timeDimension: config.time.timeDimension, + } + : undefined, + sort, + limit, + offset, + }, + { + query: { + enabled: ctx.enabled, + placeholderData: keepPreviousData, }, - ctx.queryClient, - ).subscribe(set), + }, + ctx.queryClient, + ).subscribe(set), ); } diff --git a/web-common/src/features/dashboards/pivot/pivot-utils.ts b/web-common/src/features/dashboards/pivot/pivot-utils.ts index 21c29083b78..fa6e23866fc 100644 --- a/web-common/src/features/dashboards/pivot/pivot-utils.ts +++ b/web-common/src/features/dashboards/pivot/pivot-utils.ts @@ -17,7 +17,8 @@ import type { V1MetricsViewAggregationResponse, V1MetricsViewAggregationSort, } from "@rilldata/web-common/runtime-client"; -import type { HTTPError } from "@rilldata/web-common/runtime-client/fetchWrapper"; +import { connectCodeToHTTPStatus } from "@rilldata/web-common/lib/errors"; +import type { ConnectError } from "@connectrpc/connect"; import type { QueryObserverResult } from "@tanstack/svelte-query"; import type { Row } from "@tanstack/svelte-table"; import { SHOW_MORE_BUTTON } from "./pivot-constants"; @@ -640,19 +641,21 @@ export function getFiltersForCell( } export function getErrorFromResponse( - queryResult: QueryObserverResult, + queryResult: QueryObserverResult< + V1MetricsViewAggregationResponse, + ConnectError + >, ): PivotQueryError { - return { - statusCode: queryResult?.error?.response?.status || null, - message: queryResult?.error?.response?.data?.message, - traceId: queryResult?.error?.traceId, - }; + const err = queryResult?.error; + const statusCode = err ? connectCodeToHTTPStatus(err.code) : null; + const message = err?.rawMessage || err?.message; + return { statusCode, message }; } export function getErrorFromResponses( queryResults: (QueryObserverResult< V1MetricsViewAggregationResponse, - HTTPError + ConnectError > | null)[], ): PivotQueryError[] { return queryResults diff --git a/web-common/src/features/dashboards/pivot/types.ts b/web-common/src/features/dashboards/pivot/types.ts index 2e619a2ef69..1766f624c09 100644 --- a/web-common/src/features/dashboards/pivot/types.ts +++ b/web-common/src/features/dashboards/pivot/types.ts @@ -6,6 +6,7 @@ import type { V1MetricsViewAggregationResponseDataItem, V1TimeGrain, } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import type { QueryClient } from "@tanstack/svelte-query"; import type { ColumnDef, @@ -40,6 +41,7 @@ export interface PivotCell { } export interface PivotDashboardContext { + runtimeClient: RuntimeClient; metricsViewName: Readable; queryClient: QueryClient; enabled: boolean; diff --git a/web-common/src/features/dashboards/rows-viewer/RowsViewer.svelte b/web-common/src/features/dashboards/rows-viewer/RowsViewer.svelte index 58e4113ad0f..39436bd05b1 100644 --- a/web-common/src/features/dashboards/rows-viewer/RowsViewer.svelte +++ b/web-common/src/features/dashboards/rows-viewer/RowsViewer.svelte @@ -7,7 +7,8 @@ createQueryServiceMetricsViewRows, type V1Expression, } from "@rilldata/web-common/runtime-client"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; + import { isErrorCode } from "@rilldata/web-common/lib/errors"; import { writable } from "svelte/store"; import { useExploreState } from "web-common/src/features/dashboards/stores/dashboard-stores"; import { PreviewTable } from "../../../components/preview-table"; @@ -19,7 +20,7 @@ export let filters: V1Expression | undefined; export let timeRange: TimeRangeString; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); const SAMPLE_SIZE = 10000; const FALLBACK_SAMPLE_SIZE = 1000; @@ -32,9 +33,9 @@ $: timeDimension = $exploreState?.selectedTimeDimension; $: tableQuery = createQueryServiceMetricsViewRows( - instanceId, - metricsViewName, + client, { + metricsViewName, limit: $limit, where: filters, timeStart: timeRange.start, @@ -49,11 +50,7 @@ ); // If too much date is requested, limit the query to 1000 rows - $: if ( - // @ts-ignore - $tableQuery?.error?.response?.data?.code === 8 && - $limit > FALLBACK_SAMPLE_SIZE - ) { + $: if (isErrorCode($tableQuery?.error, 8) && $limit > FALLBACK_SAMPLE_SIZE) { // SK: Have to set the limit on the next tick or the tableQuery does not update. Not sure why, seems like a svelte-query issue. setTimeout(() => { limit.set(FALLBACK_SAMPLE_SIZE); diff --git a/web-common/src/features/dashboards/rows-viewer/RowsViewerAccordion.svelte b/web-common/src/features/dashboards/rows-viewer/RowsViewerAccordion.svelte index da6a5e427db..a9ae413070a 100644 --- a/web-common/src/features/dashboards/rows-viewer/RowsViewerAccordion.svelte +++ b/web-common/src/features/dashboards/rows-viewer/RowsViewerAccordion.svelte @@ -7,8 +7,7 @@ import Resizer from "@rilldata/web-common/layout/Resizer.svelte"; import { formatCompactInteger } from "@rilldata/web-common/lib/formatters"; import { createQueryServiceMetricsViewAggregation } from "@rilldata/web-common/runtime-client"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; - import { get } from "svelte/store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { useExploreState } from "web-common/src/features/dashboards/stores/dashboard-stores"; import ExportMenu from "../../exports/ExportMenu.svelte"; import { featureFlags } from "../../feature-flags"; @@ -42,7 +41,7 @@ }, } = stateManagers; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); $: exploreState = useExploreState(exploreName); $: ({ whereFilter, dimensionThresholdFilters } = $exploreState); @@ -71,9 +70,9 @@ ); $: filteredTotalsQuery = createQueryServiceMetricsViewAggregation( - instanceId, - metricsViewName, + client, { + metricsView: metricsViewName, measures: [{ name: "count", builtinMeasure: "BUILTIN_MEASURE_COUNT" }], timeRange, where: filters, @@ -94,9 +93,9 @@ ); $: totalsQuery = createQueryServiceMetricsViewAggregation( - instanceId, - metricsViewName, + client, { + metricsView: metricsViewName, measures: [{ name: "count", builtinMeasure: "BUILTIN_MEASURE_COUNT" }], }, { @@ -131,7 +130,7 @@ function getExportQuery() { return { metricsViewRowsRequest: { - instanceId: get(runtime).instanceId, + instanceId: client.instanceId, metricsViewName, timeStart: timeRange.start, timeEnd: timeRange.end, diff --git a/web-common/src/features/dashboards/selectors.ts b/web-common/src/features/dashboards/selectors.ts index df41c6c2489..f2e76b6cb5a 100644 --- a/web-common/src/features/dashboards/selectors.ts +++ b/web-common/src/features/dashboards/selectors.ts @@ -15,17 +15,16 @@ import { import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; import { createRuntimeServiceListResources, - getQueryServiceMetricsViewSchemaQueryOptions, getQueryServiceMetricsViewTimeRangeQueryOptions, getRuntimeServiceListResourcesQueryOptions, - type RpcStatus, type V1Expression, type V1GetResourceResponse, type V1MetricsViewSpec, type V1MetricsViewTimeRangeResponse, type V1Resource, } from "@rilldata/web-common/runtime-client"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; +import type { ConnectError } from "@connectrpc/connect"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { createQuery, type CreateQueryOptions, @@ -34,78 +33,56 @@ import { } from "@tanstack/svelte-query"; import { derived, type Readable } from "svelte/store"; import type { DimensionThresholdFilter } from "web-common/src/features/dashboards/stores/explore-state"; -import type { ErrorType } from "../../runtime-client/http-client"; export function useMetricsView( - instanceId: string, + client: RuntimeClient, metricsViewName: string, queryOptions?: CreateQueryOptions< V1GetResourceResponse, - ErrorType, + ConnectError, V1Resource >, ) { return useResource( - instanceId, + client, metricsViewName, ResourceKind.MetricsView, queryOptions, ); } -export function getValidMetricsViewsQueryOptions() { - return derived(runtime, ({ instanceId }) => { - return getRuntimeServiceListResourcesQueryOptions( - instanceId, - { - kind: ResourceKind.MetricsView, - }, - { - query: { - select: (data) => - data?.resources?.filter( - (res) => !!res.metricsView?.state?.validSpec, - ), - }, +export function getValidMetricsViewsQueryOptions(client: RuntimeClient) { + return getRuntimeServiceListResourcesQueryOptions( + client, + { + kind: ResourceKind.MetricsView, + }, + { + query: { + select: (data) => + data?.resources?.filter((res) => !!res.metricsView?.state?.validSpec), }, - ); - }); + }, + ); } -export function useValidExplores(instanceId: string) { +export function useValidExplores(client: RuntimeClient) { // This is used in cloud as well so do not use "useClientFilteredResources" - return useFilteredResources(instanceId, ResourceKind.Explore, (data) => + return useFilteredResources(client, ResourceKind.Explore, (data) => data?.resources?.filter((res) => !!res.explore?.state?.validSpec), ); } -export function getValidDashboardsQueryOptions() { - return derived(runtime, ({ instanceId }) => { - return getRuntimeServiceListResourcesQueryOptions( - instanceId, - { - kind: ResourceKind.Explore, - }, - { - query: { - select: (data) => - data?.resources?.filter((res) => !!res.explore?.state?.validSpec), - }, - }, - ); - }); -} - -export function useValidCanvases(instanceId: string) { - return useFilteredResources(instanceId, ResourceKind.Canvas, (data) => +export function useValidCanvases(client: RuntimeClient) { + return useFilteredResources(client, ResourceKind.Canvas, (data) => data?.resources?.filter((res) => !!res.canvas?.state?.validSpec), ); } -export function useValidDashboards(instanceId: string) { +export function useValidDashboards(client: RuntimeClient) { return createRuntimeServiceListResources( - instanceId, - undefined, // TODO: it'd be nice if we could provide multiple kinds here + client, + {}, // TODO: it'd be nice if we could provide multiple kinds here { query: { select: (data) => { @@ -125,11 +102,11 @@ export function useValidDashboards(instanceId: string) { * Use {@link useDashboard} in the metrics view editor and other use cases. */ export const useMetricsViewValidSpec = ( - instanceId: string, + client: RuntimeClient, metricsViewName: string, selector?: (meta: V1MetricsViewSpec) => T, ) => { - return useResource(instanceId, metricsViewName, ResourceKind.MetricsView, { + return useResource(client, metricsViewName, ResourceKind.MetricsView, { select: (data) => selector ? selector(data.resource?.metricsView?.state?.validSpec ?? {}) @@ -138,7 +115,7 @@ export const useMetricsViewValidSpec = ( }; export function useMetricsViewTimeRange( - instanceId: string, + client: RuntimeClient, metricsViewName: string, options?: { query?: CreateQueryOptions; @@ -148,14 +125,13 @@ export function useMetricsViewTimeRange( const { query: queryOptions } = options ?? {}; const fullTimeRangeQueryOptionsStore = derived( - useMetricsViewValidSpec(instanceId, metricsViewName), + useMetricsViewValidSpec(client, metricsViewName), (validSpecResp) => { const metricsViewSpec = validSpecResp.data ?? {}; return getQueryServiceMetricsViewTimeRangeQueryOptions( - instanceId, - metricsViewName, - {}, + client, + { metricsViewName }, { query: { ...queryOptions, @@ -170,49 +146,45 @@ export function useMetricsViewTimeRange( } export function getMetricsViewTimeRangeFromExploreQueryOptions( + client: RuntimeClient, exploreNameStore: Readable, ) { const validSpecQuery = createQuery( - getExploreValidSpecQueryOptions(exploreNameStore), + getExploreValidSpecQueryOptions(client, exploreNameStore), queryClient, ); - return derived( - [runtime, validSpecQuery], - ([{ instanceId }, validSpecResp]) => { - const metricsViewSpec = validSpecResp.data?.metricsViewSpec ?? {}; - const exploreSpec = validSpecResp.data?.exploreSpec ?? {}; - const metricsViewName = exploreSpec.metricsView ?? ""; + return derived([validSpecQuery], ([validSpecResp]) => { + const metricsViewSpec = validSpecResp.data?.metricsViewSpec ?? {}; + const exploreSpec = validSpecResp.data?.exploreSpec ?? {}; + const metricsViewName = exploreSpec.metricsView ?? ""; - return getQueryServiceMetricsViewTimeRangeQueryOptions( - instanceId, - metricsViewName, - {}, - { - query: { - enabled: !!metricsViewSpec.timeDimension, - }, + return getQueryServiceMetricsViewTimeRangeQueryOptions( + client, + { metricsViewName }, + { + query: { + enabled: !!metricsViewSpec.timeDimension, }, - ); - }, - ); + }, + ); + }); } export function hasValidMetricsViewTimeRange( - instanceId: string, + client: RuntimeClient, exploreName: string, ) { const fullTimeRangeQueryOptionsStore = derived( - useExploreValidSpec(instanceId, exploreName), + useExploreValidSpec(client, exploreName), (validSpecResp) => { const metricsViewSpec = validSpecResp.data?.metricsView ?? {}; const exploreSpec = validSpecResp.data?.explore ?? {}; const metricsViewName = exploreSpec.metricsView ?? ""; return getQueryServiceMetricsViewTimeRangeQueryOptions( - instanceId, - metricsViewName, - {}, + client, + { metricsViewName }, { query: { enabled: Boolean(metricsViewName && metricsViewSpec.timeDimension), @@ -232,33 +204,6 @@ export function hasValidMetricsViewTimeRange( ); } -export function getMetricsViewSchemaOptions( - exploreNameStore: Readable, -) { - const validSpecQuery = createQuery( - getExploreValidSpecQueryOptions(exploreNameStore), - ); - - return derived( - [runtime, validSpecQuery], - ([{ instanceId }, validSpecResp]) => { - const exploreSpec = validSpecResp.data?.exploreSpec ?? {}; - const metricsViewName = exploreSpec.metricsView ?? ""; - - return getQueryServiceMetricsViewSchemaQueryOptions( - instanceId, - metricsViewName, - {}, - { - query: { - enabled: Boolean(metricsViewName), - }, - }, - ); - }, - ); -} - export function getFiltersForOtherDimensions( whereFilter: V1Expression, dimName: string, @@ -291,11 +236,11 @@ export function additionalMeasures( } export const useGetMetricsViewsForModel = ( - instanceId: string, + client: RuntimeClient, modelName: string, ) => { return useClientFilteredResources( - instanceId, + client, ResourceKind.MetricsView, (res) => res.metricsView?.spec?.model === modelName || @@ -304,11 +249,11 @@ export const useGetMetricsViewsForModel = ( }; export const useGetExploresForMetricsView = ( - instanceId: string, + client: RuntimeClient, metricsViewName: string, ) => { return useClientFilteredResources( - instanceId, + client, ResourceKind.Explore, (res) => res.explore?.spec?.metricsView === metricsViewName, ); diff --git a/web-common/src/features/dashboards/selectors/index.ts b/web-common/src/features/dashboards/selectors/index.ts index dc434615b70..1bde6b72f70 100644 --- a/web-common/src/features/dashboards/selectors/index.ts +++ b/web-common/src/features/dashboards/selectors/index.ts @@ -1,22 +1,20 @@ -import { - type V1MetricsViewTimeRangeResponse, - createQueryServiceMetricsViewTimeRange, -} from "@rilldata/web-common/runtime-client"; +import type { V1MetricsViewTimeRangeResponse } from "@rilldata/web-common/runtime-client"; +import { createQueryServiceMetricsViewTimeRange } from "@rilldata/web-common/runtime-client"; import type { CreateQueryResult } from "@tanstack/svelte-query"; import { derived } from "svelte/store"; import type { StateManagers } from "../state-managers/state-managers"; -import type { HTTPError } from "@rilldata/web-common/runtime-client/fetchWrapper"; export function createTimeRangeSummary( ctx: StateManagers, -): CreateQueryResult { +): CreateQueryResult { return derived( - [ctx.runtime, ctx.metricsViewName, ctx.validSpecStore], - ([runtime, metricsViewName, validSpec], set) => + [ctx.metricsViewName, ctx.validSpecStore], + ([metricsViewName, validSpec], set) => createQueryServiceMetricsViewTimeRange( - runtime.instanceId, - metricsViewName, - {}, + ctx.runtimeClient, + { + metricsViewName, + }, { query: { enabled: diff --git a/web-common/src/features/dashboards/state-managers/StateManagersProvider.svelte b/web-common/src/features/dashboards/state-managers/StateManagersProvider.svelte index bf6f8ca789d..d446c88b0a4 100644 --- a/web-common/src/features/dashboards/state-managers/StateManagersProvider.svelte +++ b/web-common/src/features/dashboards/state-managers/StateManagersProvider.svelte @@ -3,16 +3,20 @@ import { createStateManagers, DEFAULT_STORE_KEY } from "./state-managers"; import { useExploreState } from "../stores/dashboard-stores"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; export let metricsViewName: string | undefined; export let exploreName: string; export let visualEditing = false; + const client = useRuntimeClient(); + const stateManagers = metricsViewName ? createStateManagers({ queryClient, metricsViewName, exploreName, + runtimeClient: client, }) : undefined; diff --git a/web-common/src/features/dashboards/state-managers/loaders/DashboardStateDataLoader.ts b/web-common/src/features/dashboards/state-managers/loaders/DashboardStateDataLoader.ts index 9f329af8c36..ccbdf99cf1e 100644 --- a/web-common/src/features/dashboards/state-managers/loaders/DashboardStateDataLoader.ts +++ b/web-common/src/features/dashboards/state-managers/loaders/DashboardStateDataLoader.ts @@ -19,6 +19,7 @@ import { type V1MetricsViewSpec, type V1MetricsViewTimeRangeResponse, } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import type { AfterNavigate } from "@sveltejs/kit"; import { createQuery, type QueryClient } from "@tanstack/svelte-query"; import { Settings } from "luxon"; @@ -55,7 +56,7 @@ export class DashboardStateDataLoader { >; public constructor( - instanceId: string, + private readonly client: RuntimeClient, private readonly exploreName: string, private readonly storageNamespacePrefix: string | undefined, private readonly bookmarkOrTokenExploreState: @@ -63,9 +64,9 @@ export class DashboardStateDataLoader { | undefined, public readonly disableMostRecentDashboardState: boolean, ) { - this.validSpecQuery = useExploreValidSpec(instanceId, exploreName); + this.validSpecQuery = useExploreValidSpec(client, exploreName); this.fullTimeRangeQuery = this.useFullTimeRangeQuery( - instanceId, + client, this.validSpecQuery, ); @@ -197,7 +198,7 @@ export class DashboardStateDataLoader { * Does an additional validation where null min and max returned throws an error instead. */ private useFullTimeRangeQuery( - instanceId: string, + client: RuntimeClient, validSpecQuery: ReturnType, queryClient?: QueryClient, ): CompoundQueryResult { @@ -216,9 +217,8 @@ export class DashboardStateDataLoader { }; return getQueryServiceMetricsViewTimeRangeQueryOptions( - instanceId, - metricsViewName, - {}, + client, + { metricsViewName }, { query: { enabled: Boolean(metricsViewSpec.timeDimension), @@ -250,8 +250,8 @@ export class DashboardStateDataLoader { } if ( - fullTimeRange.data?.timeRangeSummary?.min === null && - fullTimeRange.data?.timeRangeSummary?.max === null + fullTimeRange.data?.timeRangeSummary?.min == null && + fullTimeRange.data?.timeRangeSummary?.max == null ) { // The timeRangeSummary is null when there are 0 rows of data. // Notably, this happens when a security policy fully restricts a user from reading any data. diff --git a/web-common/src/features/dashboards/state-managers/loaders/DashboardStateManager.spec.ts b/web-common/src/features/dashboards/state-managers/loaders/DashboardStateManager.spec.ts index 88daa28dc92..244f7df0be9 100644 --- a/web-common/src/features/dashboards/state-managers/loaders/DashboardStateManager.spec.ts +++ b/web-common/src/features/dashboards/state-managers/loaders/DashboardStateManager.spec.ts @@ -40,6 +40,10 @@ import { V1ExploreComparisonMode, V1TimeGrain, } from "@rilldata/web-common/runtime-client"; +import { + RUNTIME_CONTEXT_KEY, + RuntimeClient, +} from "@rilldata/web-common/runtime-client/v2"; import { render, screen, waitFor } from "@testing-library/svelte"; import { readable } from "svelte/store"; import { beforeEach, describe, expect, it, vi } from "vitest"; @@ -485,7 +489,13 @@ function renderDashboardStateManager( }, // TODO: we need to make sure every single query uses an explicit queryClient instead of the global one // only then we can use a fresh client here. - context: new Map([["$$_queryClient", queryClient]]), + context: new Map([ + ["$$_queryClient", queryClient], + [ + RUNTIME_CONTEXT_KEY, + new RuntimeClient({ host: "http://localhost", instanceId: "test" }), + ], + ]), }); return { queryClient, renderResults }; diff --git a/web-common/src/features/dashboards/state-managers/loaders/DashboardStateManager.svelte b/web-common/src/features/dashboards/state-managers/loaders/DashboardStateManager.svelte index eb8a1daf600..59a77ef064e 100644 --- a/web-common/src/features/dashboards/state-managers/loaders/DashboardStateManager.svelte +++ b/web-common/src/features/dashboards/state-managers/loaders/DashboardStateManager.svelte @@ -15,8 +15,11 @@ import { isUrlTooLong } from "@rilldata/web-common/features/dashboards/url-state/url-length-limits"; import { useExploreValidSpec } from "@rilldata/web-common/features/explores/selectors"; import { eventBus } from "@rilldata/web-common/lib/event-bus/event-bus"; - import type { HTTPError } from "@rilldata/web-common/runtime-client/fetchWrapper"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { + extractErrorMessage, + extractErrorStatusCode, + } from "@rilldata/web-common/lib/errors"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { onDestroy } from "svelte"; export let exploreName: string; @@ -26,14 +29,15 @@ | undefined = undefined; export let disableMostRecentDashboardState: boolean = false; - $: ({ instanceId } = $runtime); - $: exploreSpecQuery = useExploreValidSpec(instanceId, exploreName); + const client = useRuntimeClient(); + + $: exploreSpecQuery = useExploreValidSpec(client, exploreName); $: exploreSpec = $exploreSpecQuery.data?.explore ?? {}; $: metricsViewName = exploreSpec?.metricsView ?? ""; $: exploreStore = useExploreState(exploreName); $: dataLoader = new DashboardStateDataLoader( - instanceId, + client, exploreName, storageNamespacePrefix, bookmarkOrTokenExploreState, @@ -44,7 +48,7 @@ $: if (dataLoader) { stateSync?.teardown(); stateSync = new DashboardStateSync( - instanceId, + client, metricsViewName, exploreName, storageNamespacePrefix, @@ -57,12 +61,12 @@ | undefined; $: if (dataLoader) ({ initExploreState } = dataLoader); - let error: HTTPError | null; + let error: Error | null; let isLoading: boolean; $: if (initExploreState) { ({ isLoading, error } = $initExploreState as { isLoading: boolean; - error: HTTPError | null; + error: Error | null; }); } @@ -107,9 +111,9 @@ {:else if error} {:else if $exploreStore} diff --git a/web-common/src/features/dashboards/state-managers/loaders/DashboardStateSync.ts b/web-common/src/features/dashboards/state-managers/loaders/DashboardStateSync.ts index 9937c619c7e..1980c550af7 100644 --- a/web-common/src/features/dashboards/state-managers/loaders/DashboardStateSync.ts +++ b/web-common/src/features/dashboards/state-managers/loaders/DashboardStateSync.ts @@ -15,6 +15,7 @@ import { import { updateExploreSessionStore } from "@rilldata/web-common/features/dashboards/state-managers/loaders/explore-web-view-store"; import { getCleanedUrlParamsForGoto } from "@rilldata/web-common/features/dashboards/url-state/convert-partial-explore-state-to-url-params"; import { createRillDefaultExploreUrlParams } from "@rilldata/web-common/features/dashboards/url-state/get-rill-default-explore-url-params"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import type { AfterNavigate } from "@sveltejs/kit"; import { getContext, setContext } from "svelte"; import { derived, get, type Readable } from "svelte/store"; @@ -51,7 +52,7 @@ export class DashboardStateSync { } public constructor( - instanceId: string, + private readonly client: RuntimeClient, metricsViewName: string, private readonly exploreName: string, private readonly extraPrefix: string | undefined, @@ -59,7 +60,7 @@ export class DashboardStateSync { ) { this.exploreStore = useExploreState(exploreName); this.timeControlStore = createTimeControlStoreFromName( - instanceId, + client, metricsViewName, exploreName, ); @@ -150,6 +151,7 @@ export class DashboardStateSync { initExploreState.selectedTimeRange, // initExploreState.selectedComparisonTimeRange, ] = await resolveTimeRanges( + this.client, exploreSpec, [ initExploreState.selectedTimeRange, @@ -245,6 +247,7 @@ export class DashboardStateSync { partialExplore.selectedTimeRange, // partialExplore.selectedComparisonTimeRange, ] = await resolveTimeRanges( + this.client, exploreSpec, [ partialExplore.selectedTimeRange, diff --git a/web-common/src/features/dashboards/state-managers/loaders/explore-web-view-store.spec.ts b/web-common/src/features/dashboards/state-managers/loaders/explore-web-view-store.spec.ts index 39a125ac3a3..48b1a7f678f 100644 --- a/web-common/src/features/dashboards/state-managers/loaders/explore-web-view-store.spec.ts +++ b/web-common/src/features/dashboards/state-managers/loaders/explore-web-view-store.spec.ts @@ -30,6 +30,10 @@ import { import DashboardStateManagerTest from "@rilldata/web-common/features/dashboards/state-managers/loaders/test/DashboardStateManagerTest.svelte"; import { getCleanMetricsExploreForAssertion } from "@rilldata/web-common/features/dashboards/url-state/url-state-variations.spec"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; +import { + RUNTIME_CONTEXT_KEY, + RuntimeClient, +} from "@rilldata/web-common/runtime-client/v2"; import { render, screen, waitFor } from "@testing-library/svelte"; import { beforeEach, describe, expect, it, vi } from "vitest"; @@ -252,7 +256,13 @@ function renderDashboardStateManager() { }, // TODO: we need to make sure every single query uses an explicit queryClient instead of the global one // only then we can use a fresh client here. - context: new Map([["$$_queryClient", queryClient]]), + context: new Map([ + ["$$_queryClient", queryClient], + [ + RUNTIME_CONTEXT_KEY, + new RuntimeClient({ host: "http://localhost", instanceId: "test" }), + ], + ]), }); return { queryClient, renderResults }; diff --git a/web-common/src/features/dashboards/state-managers/loaders/test/PageMockForExploreTests.ts b/web-common/src/features/dashboards/state-managers/loaders/test/PageMockForExploreTests.ts index 89b88113590..c3fa4142c4f 100644 --- a/web-common/src/features/dashboards/state-managers/loaders/test/PageMockForExploreTests.ts +++ b/web-common/src/features/dashboards/state-managers/loaders/test/PageMockForExploreTests.ts @@ -54,6 +54,7 @@ export class PageMockForExploreTests { const { update, subscribe } = writable({ url: new URL(`http://localhost/explore/${this.exploreName}`), params: { name: "AdBids_explore" }, + route: { id: "/explore/[name]" }, } as any); this.update = update; diff --git a/web-common/src/features/dashboards/state-managers/most-recent-explore-state.spec.ts b/web-common/src/features/dashboards/state-managers/most-recent-explore-state.spec.ts index e4397b5f93d..bb7a6432511 100644 --- a/web-common/src/features/dashboards/state-managers/most-recent-explore-state.spec.ts +++ b/web-common/src/features/dashboards/state-managers/most-recent-explore-state.spec.ts @@ -31,6 +31,10 @@ import { DashboardState_LeaderboardSortDirection, DashboardState_LeaderboardSortType, } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; +import { + RUNTIME_CONTEXT_KEY, + RuntimeClient, +} from "@rilldata/web-common/runtime-client/v2"; import { render, screen, waitFor } from "@testing-library/svelte"; import { beforeEach, describe, expect, it, vi } from "vitest"; @@ -179,7 +183,13 @@ function renderDashboardStateManager() { }, // TODO: we need to make sure every single query uses an explicit queryClient instead of the global one // only then we can use a fresh client here. - context: new Map([["$$_queryClient", queryClient]]), + context: new Map([ + ["$$_queryClient", queryClient], + [ + RUNTIME_CONTEXT_KEY, + new RuntimeClient({ host: "http://localhost", instanceId: "test" }), + ], + ]), }); return { queryClient, renderResults }; diff --git a/web-common/src/features/dashboards/state-managers/selectors/dimension-filters.ts b/web-common/src/features/dashboards/state-managers/selectors/dimension-filters.ts index be2072fa79a..34faede12e6 100644 --- a/web-common/src/features/dashboards/state-managers/selectors/dimension-filters.ts +++ b/web-common/src/features/dashboards/state-managers/selectors/dimension-filters.ts @@ -14,12 +14,13 @@ import type { V1Expression, } from "@rilldata/web-common/runtime-client"; import { V1Operation } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { derived, readable } from "svelte/store"; import type { AtLeast } from "../types"; import type { DashboardDataSources } from "./types"; export const selectedDimensionValues = ( - instanceId: string, + client: RuntimeClient, metricsViewNames: string[], whereFilter: V1Expression | undefined, dimensionName: string, @@ -65,7 +66,7 @@ export const selectedDimensionValues = ( dimExpr.cond.op === V1Operation.OPERATION_LIKE || dimExpr.cond.op === V1Operation.OPERATION_NLIKE ) { - return useDimensionSearch(instanceId, metricsViewNames, dimensionName, { + return useDimensionSearch(client, metricsViewNames, dimensionName, { mode: DimensionFilterMode.Contains, searchText: (dimExpr.cond?.exprs?.[1]?.val as string) ?? "", values: [], @@ -85,10 +86,10 @@ export const selectedDimensionValues = ( export const useSelectedValuesForCompareDimension = (ctx: StateManagers) => { return derived( - [ctx.runtime, ctx.metricsViewName, ctx.dashboardStore], - ([runtime, metricsViewName, exploreState], set) => + [ctx.metricsViewName, ctx.dashboardStore], + ([metricsViewName, exploreState], set) => selectedDimensionValues( - runtime.instanceId, + ctx.runtimeClient, [metricsViewName], exploreState.whereFilter, exploreState.selectedComparisonDimension ?? "", diff --git a/web-common/src/features/dashboards/state-managers/selectors/dimension-table.ts b/web-common/src/features/dashboards/state-managers/selectors/dimension-table.ts index 3716eb892f0..f10dc7387d3 100644 --- a/web-common/src/features/dashboards/state-managers/selectors/dimension-table.ts +++ b/web-common/src/features/dashboards/state-managers/selectors/dimension-table.ts @@ -1,9 +1,9 @@ import type { VirtualizedTableColumns } from "@rilldata/web-common/components/virtualized-table/types"; import type { MetricsViewSpecDimension, - RpcStatus, V1MetricsViewAggregationResponse, } from "@rilldata/web-common/runtime-client"; +import type { ConnectError } from "@connectrpc/connect"; import type { QueryObserverResult } from "@tanstack/svelte-query"; import type { DimensionTableRow } from "../../dimension-table/dimension-table-types"; import { @@ -74,7 +74,7 @@ export const prepareDimTableRows = ): (( sortedQuery: QueryObserverResult< V1MetricsViewAggregationResponse, - RpcStatus + ConnectError >, unfilteredTotal: number | { [key: string]: number }, ) => DimensionTableRow[]) => diff --git a/web-common/src/features/dashboards/state-managers/selectors/index.ts b/web-common/src/features/dashboards/state-managers/selectors/index.ts index 684250e980f..719bb68492d 100644 --- a/web-common/src/features/dashboards/state-managers/selectors/index.ts +++ b/web-common/src/features/dashboards/state-managers/selectors/index.ts @@ -1,10 +1,7 @@ import { chartSelectors } from "@rilldata/web-common/features/dashboards/state-managers/selectors/charts"; import { measureFilterSelectors } from "@rilldata/web-common/features/dashboards/state-managers/selectors/measure-filters"; import type { ExploreValidSpecResponse } from "@rilldata/web-common/features/explores/selectors"; -import type { - RpcStatus, - V1MetricsViewTimeRangeResponse, -} from "@rilldata/web-common/runtime-client"; +import type { V1MetricsViewTimeRangeResponse } from "@rilldata/web-common/runtime-client"; import type { QueryClient, QueryObserverResult } from "@tanstack/svelte-query"; import { derived, type Readable } from "svelte/store"; import type { ExploreState } from "web-common/src/features/dashboards/stores/explore-state"; @@ -25,7 +22,7 @@ import { leaderboardSelectors } from "./leaderboard"; export type DashboardDataReadables = { dashboardStore: Readable; validSpecStore: Readable< - QueryObserverResult + QueryObserverResult >; timeRangeSummaryStore: Readable< QueryObserverResult diff --git a/web-common/src/features/dashboards/state-managers/state-managers.ts b/web-common/src/features/dashboards/state-managers/state-managers.ts index b88a58c21b8..f9f9036cd15 100644 --- a/web-common/src/features/dashboards/state-managers/state-managers.ts +++ b/web-common/src/features/dashboards/state-managers/state-managers.ts @@ -1,17 +1,13 @@ import { type ExploreState } from "@rilldata/web-common/features/dashboards/stores/explore-state"; import { getDefaultExplorePreset } from "@rilldata/web-common/features/dashboards/url-state/getDefaultExplorePreset"; +import { type ExploreValidSpecResponse } from "@rilldata/web-common/features/explores/selectors"; import { - type ExploreValidSpecResponse, - useExploreValidSpec, -} from "@rilldata/web-common/features/explores/selectors"; -import { - type RpcStatus, type V1ExplorePreset, type V1MetricsViewTimeRangeResponse, - createQueryServiceMetricsViewTimeRange, } from "@rilldata/web-common/runtime-client"; -import type { Runtime } from "@rilldata/web-common/runtime-client/runtime-store"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; +import { createRuntimeServiceGetExplore } from "@rilldata/web-common/runtime-client"; +import { createQueryServiceMetricsViewTimeRange } from "@rilldata/web-common/runtime-client"; import type { QueryClient, QueryObserverResult } from "@tanstack/svelte-query"; import { getContext } from "svelte"; import { @@ -39,7 +35,7 @@ import { } from "../leaderboard-context-column"; export type StateManagers = { - runtime: Writable; + runtimeClient: RuntimeClient; metricsViewName: Writable; exploreName: Writable; metricsStore: Readable; @@ -49,7 +45,7 @@ export type StateManagers = { QueryObserverResult >; validSpecStore: Readable< - QueryObserverResult + QueryObserverResult >; queryClient: QueryClient; updateDashboard: DashboardCallbackExecutor; @@ -81,10 +77,12 @@ export function createStateManagers({ queryClient, metricsViewName, exploreName, + runtimeClient, }: { queryClient: QueryClient; metricsViewName: string; exploreName: string; + runtimeClient: RuntimeClient; }): StateManagers { const metricsViewNameStore = writable(metricsViewName); const exploreNameStore = writable(exploreName); @@ -99,12 +97,21 @@ export function createStateManagers({ ); const validSpecStore: Readable< - QueryObserverResult - > = derived([runtime, exploreNameStore], ([r, exploreName], set) => - useExploreValidSpec( - r.instanceId, - exploreName, - undefined, + QueryObserverResult + > = derived([exploreNameStore], ([exploreName], set) => + createRuntimeServiceGetExplore( + runtimeClient, + { name: exploreName }, + { + query: { + select: (data) => + { + explore: data.explore?.explore?.state?.validSpec, + metricsView: data.metricsView?.metricsView?.state?.validSpec, + }, + enabled: !!exploreName, + }, + }, queryClient, ).subscribe(set), ); @@ -112,12 +119,12 @@ export function createStateManagers({ const timeRangeSummaryStore: Readable< QueryObserverResult > = derived( - [runtime, metricsViewNameStore, validSpecStore, dashboardStore], - ([runtime, mvName, validSpec, $dashboardStore], set) => + [metricsViewNameStore, validSpecStore, dashboardStore], + ([mvName, validSpec, $dashboardStore], set) => createQueryServiceMetricsViewTimeRange( - runtime.instanceId, - mvName, + runtimeClient, { + metricsViewName: mvName, timeDimension: $dashboardStore?.selectedTimeDimension, }, { @@ -157,7 +164,7 @@ export function createStateManagers({ ); return { - runtime: runtime, + runtimeClient, metricsViewName: metricsViewNameStore, exploreName: exploreNameStore, metricsStore: metricsExplorerStore, diff --git a/web-common/src/features/dashboards/stores/ExploreMetricsViewMetadata.ts b/web-common/src/features/dashboards/stores/ExploreMetricsViewMetadata.ts index fb15cf97fb8..0673c80aa8c 100644 --- a/web-common/src/features/dashboards/stores/ExploreMetricsViewMetadata.ts +++ b/web-common/src/features/dashboards/stores/ExploreMetricsViewMetadata.ts @@ -7,6 +7,7 @@ import { type MetricsViewSpecDimension, type MetricsViewSpecMeasure, } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { derived, type Readable } from "svelte/store"; export class ExploreMetricsViewMetadata { @@ -21,18 +22,18 @@ export class ExploreMetricsViewMetadata { public readonly measureNameMap: Readable>; public constructor( - instanceId: string, + client: RuntimeClient, public readonly metricsViewName: string, exploreName: string, ) { this.validSpecQuery = useExploreValidSpec( - instanceId, + client, exploreName, undefined, queryClient, ); this.timeRangeSummary = useMetricsViewTimeRange( - instanceId, + client, metricsViewName, undefined, queryClient, diff --git a/web-common/src/features/dashboards/stores/get-explore-state-from-yaml-config.ts b/web-common/src/features/dashboards/stores/get-explore-state-from-yaml-config.ts index a436ad9db1e..6a6c9add412 100644 --- a/web-common/src/features/dashboards/stores/get-explore-state-from-yaml-config.ts +++ b/web-common/src/features/dashboards/stores/get-explore-state-from-yaml-config.ts @@ -7,6 +7,7 @@ import { getValidComparisonOption } from "@rilldata/web-common/features/dashboar import { convertPartialExploreStateToUrlParams } from "@rilldata/web-common/features/dashboards/url-state/convert-partial-explore-state-to-url-params"; import { ToLegacySortTypeMap } from "@rilldata/web-common/features/dashboards/url-state/legacyMappers"; import { getExploreValidSpecQueryOptions } from "@rilldata/web-common/features/explores/selectors"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { arrayUnorderedEquals } from "@rilldata/web-common/lib/arrayUtils"; import { ISODurationToTimePreset } from "@rilldata/web-common/lib/time/ranges"; import { isoDurationToFullTimeRange } from "@rilldata/web-common/lib/time/ranges/iso-ranges"; @@ -47,13 +48,14 @@ export function getExploreStateFromYAMLConfig( } export function createUrlForExploreYAMLDefaultState( + client: RuntimeClient, exploreNameStore: Readable, ) { const validSpecQuery = createQuery( - getExploreValidSpecQueryOptions(exploreNameStore), + getExploreValidSpecQueryOptions(client, exploreNameStore), ); const timeRangeQuery = createQuery( - getMetricsViewTimeRangeFromExploreQueryOptions(exploreNameStore), + getMetricsViewTimeRangeFromExploreQueryOptions(client, exploreNameStore), ); return derived( diff --git a/web-common/src/features/dashboards/stores/test-data/helpers.ts b/web-common/src/features/dashboards/stores/test-data/helpers.ts index c1587dbf3fa..11d19973f58 100644 --- a/web-common/src/features/dashboards/stores/test-data/helpers.ts +++ b/web-common/src/features/dashboards/stores/test-data/helpers.ts @@ -1,5 +1,6 @@ import { QueryClient } from "@tanstack/svelte-query"; import { createStateManagers } from "@rilldata/web-common/features/dashboards/state-managers/state-managers"; +import { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { metricsExplorerStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; import { createAndExpression } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; import type { ExploreState } from "@rilldata/web-common/features/dashboards/stores/explore-state"; @@ -120,10 +121,15 @@ export function initStateManagers(metricsViewName?: string) { }, }, }); + const runtimeClient = new RuntimeClient({ + host: "http://localhost", + instanceId: "test", + }); const stateManagers = createStateManagers({ queryClient, metricsViewName, exploreName, + runtimeClient, }); return { stateManagers, queryClient }; diff --git a/web-common/src/features/dashboards/stores/test-data/query-mocks.ts b/web-common/src/features/dashboards/stores/test-data/query-mocks.ts index 09816883be1..a40395c5f0c 100644 --- a/web-common/src/features/dashboards/stores/test-data/query-mocks.ts +++ b/web-common/src/features/dashboards/stores/test-data/query-mocks.ts @@ -9,20 +9,20 @@ import type { ExploreValidSpecResponse } from "@rilldata/web-common/features/exp import type { MetricsViewSpecDimension, MetricsViewSpecMeasure, - RpcStatus, V1MetricsViewSpec, } from "@rilldata/web-common/runtime-client"; +import type { ConnectError } from "@connectrpc/connect"; import type { QueryObserverResult } from "@tanstack/query-core"; import { writable } from "svelte/store"; export function createMetricsMetaQueryMock( shouldInit = true, -): CreateQueryResult & { +): CreateQueryResult & { setMeasures: (measures: Array) => void; setDimensions: (dimensions: Array) => void; } { const { update, subscribe } = writable< - QueryObserverResult + QueryObserverResult >({ data: undefined, isSuccess: false, @@ -65,12 +65,12 @@ export function createValidSpecQueryMock( metricsView = AD_BIDS_NAME, shouldInit = true, initMetrics: V1MetricsViewSpec = AD_BIDS_METRICS_INIT, -): CreateQueryResult & { +): CreateQueryResult & { setMeasures: (measures: Array) => void; setDimensions: (dimensions: Array) => void; } { const { update, subscribe } = writable< - QueryObserverResult + QueryObserverResult >({ data: undefined, isSuccess: false, diff --git a/web-common/src/features/dashboards/stores/utils.ts b/web-common/src/features/dashboards/stores/utils.ts deleted file mode 100644 index 2a946dd165e..00000000000 --- a/web-common/src/features/dashboards/stores/utils.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { getMetricsViewTimeRangeFromExploreQueryOptions } from "@rilldata/web-common/features/dashboards/selectors.ts"; -import { metricsExplorerStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores.ts"; -import { createAndExpression } from "@rilldata/web-common/features/dashboards/stores/filter-utils.ts"; -import type { FiltersState } from "@rilldata/web-common/features/dashboards/stores/Filters.ts"; -import type { TimeControlState } from "@rilldata/web-common/features/dashboards/stores/TimeControls.ts"; -import { getTimeControlState } from "@rilldata/web-common/features/dashboards/time-controls/time-control-store.ts"; -import { getExploreValidSpecQueryOptions } from "@rilldata/web-common/features/explores/selectors.ts"; -import { createQuery } from "@tanstack/svelte-query"; -import { derived, type Readable } from "svelte/store"; - -export function getFilterStateFromNameStore( - exploreNameStore: Readable, -): Readable { - return derived( - [metricsExplorerStore, exploreNameStore], - ([metricsExplorerState, exploreName]) => { - const exploreState = metricsExplorerState.entities[exploreName]; - const filtersState: FiltersState = { - whereFilter: exploreState?.whereFilter ?? createAndExpression([]), - dimensionThresholdFilters: - exploreState?.dimensionThresholdFilters ?? [], - dimensionsWithInlistFilter: - exploreState?.dimensionsWithInlistFilter ?? [], - dimensionFilterExcludeMode: - exploreState?.dimensionFilterExcludeMode ?? new Map(), - }; - - return filtersState; - }, - ); -} - -export function getTimeControlsStateFromNameStore( - exploreNameStore: Readable, -) { - const validSpecQuery = createQuery( - getExploreValidSpecQueryOptions(exploreNameStore), - ); - const metricsViewTimeRangeQuery = createQuery( - getMetricsViewTimeRangeFromExploreQueryOptions(exploreNameStore), - ); - - return derived( - [ - metricsExplorerStore, - exploreNameStore, - validSpecQuery, - metricsViewTimeRangeQuery, - ], - ([metricsExplorerState, exploreName, validSpecResp, timeRangeResp]) => { - const exploreState = metricsExplorerState.entities[exploreName]; - const metricsViewSpec = validSpecResp.data?.metricsViewSpec ?? {}; - const exploreSpec = validSpecResp.data?.exploreSpec ?? {}; - const timeRangeSummary = timeRangeResp.data?.timeRangeSummary; - - const exploreTimeControlState = exploreState - ? getTimeControlState( - metricsViewSpec, - exploreSpec, - timeRangeSummary, - exploreState, - ) - : undefined; - const timeControlState = { - selectedTimeRange: exploreTimeControlState?.selectedTimeRange, - selectedComparisonTimeRange: - exploreTimeControlState?.selectedComparisonTimeRange, - showTimeComparison: exploreTimeControlState?.showTimeComparison, - selectedTimezone: exploreState?.selectedTimezone, - }; - - return timeControlState; - }, - ); -} diff --git a/web-common/src/features/dashboards/time-controls/new-time-controls.ts b/web-common/src/features/dashboards/time-controls/new-time-controls.ts index c13883bdd1e..a6622fc7fb8 100644 --- a/web-common/src/features/dashboards/time-controls/new-time-controls.ts +++ b/web-common/src/features/dashboards/time-controls/new-time-controls.ts @@ -15,6 +15,7 @@ import type { V1TimeRangeSummary, } from "@rilldata/web-common/runtime-client"; import { V1TimeGrain } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { DateTime, type DateTimeUnit, @@ -165,9 +166,16 @@ class MetricsTimeControls { private _subrange = new IntervalStore(); private _comparisonRange = new IntervalStore(); private _showComparison: Writable = writable(false); + private _client: RuntimeClient; private _metricsViewName: string; - constructor(maxStart: DateTime, maxEnd: DateTime, metricsViewName: string) { + constructor( + maxStart: DateTime, + maxEnd: DateTime, + client: RuntimeClient, + metricsViewName: string, + ) { + this._client = client; this._metricsViewName = metricsViewName; const maxInterval = Interval.fromDateTimes( maxStart.setZone("UTC"), @@ -188,7 +196,7 @@ class MetricsTimeControls { if (rightAnchor) { const interval = await deriveInterval( iso, - + this._client, this._metricsViewName, get(this._zone).name, ); @@ -204,7 +212,7 @@ class MetricsTimeControls { if (rightAnchor) { const interval = await deriveInterval( name, - + this._client, this._metricsViewName, get(this._zone).name, ); @@ -284,11 +292,21 @@ class MetricsTimeControls { class TimeControls { private _timeControls = new Map(); - get(metricsViewName: string, maxStart?: DateTime, maxEnd?: DateTime) { + get( + metricsViewName: string, + client?: RuntimeClient, + maxStart?: DateTime, + maxEnd?: DateTime, + ) { let store = this._timeControls.get(metricsViewName); - if (!store && maxStart && maxEnd) { - store = new MetricsTimeControls(maxStart, maxEnd, metricsViewName); + if (!store && maxStart && maxEnd && client) { + store = new MetricsTimeControls( + maxStart, + maxEnd, + client, + metricsViewName, + ); this._timeControls.set(metricsViewName, store); } else if (!store) { throw new Error("TimeControls.get() called without maxStart and maxEnd"); @@ -312,7 +330,6 @@ export function isRillPeriodToDate(value: string): value is RillPeriodToDate { return RILL_PERIOD_TO_DATE.includes(value as RillPeriodToDate); } -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import { getAllowedGrains, GrainAliasToV1TimeGrain, @@ -331,6 +348,7 @@ import { getDefaultRangeBuckets } from "@rilldata/web-common/lib/time/defaults"; export async function deriveInterval( name: RillPeriodToDate | RillPreviousPeriod | ISODurationString | string, + client: RuntimeClient, metricsViewName: string, activeTimeZone: string, timeDimension?: string, @@ -352,11 +370,10 @@ export async function deriveInterval( const parsed = parseRillTime(name); // We have a RillTime string - const instanceId = get(runtime).instanceId; const cacheBust = name.includes("now"); const response = await fetchTimeRanges({ - instanceId, + client, metricsViewName, rillTimes: [name], timeZone: activeTimeZone, diff --git a/web-common/src/features/dashboards/time-controls/rill-time-ranges.ts b/web-common/src/features/dashboards/time-controls/rill-time-ranges.ts index fe867c28821..4b99ad9dc99 100644 --- a/web-common/src/features/dashboards/time-controls/rill-time-ranges.ts +++ b/web-common/src/features/dashboards/time-controls/rill-time-ranges.ts @@ -1,15 +1,15 @@ import { validateRillTime } from "@rilldata/web-common/features/dashboards/url-state/time-ranges/parser"; import type { DashboardTimeControls } from "@rilldata/web-common/lib/time/types"; -import { get } from "svelte/store"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import { getQueryServiceMetricsViewTimeRangesQueryKey, queryServiceMetricsViewTimeRanges, type V1ExploreSpec, } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; export async function resolveTimeRanges( + client: RuntimeClient, exploreSpec: V1ExploreSpec, timeRanges: (DashboardTimeControls | undefined)[], timeZone: string | undefined, @@ -40,12 +40,11 @@ export async function resolveTimeRanges( if (rillTimes.length === 0) return timeRangesToReturn; - const instanceId = get(runtime).instanceId; const metricsViewName = exploreSpec.metricsView!; try { const timeRangesResp = await fetchTimeRanges({ - instanceId, + client, metricsViewName, rillTimes, timeZone, @@ -63,7 +62,7 @@ export async function resolveTimeRanges( return timeRangesToReturn; } catch (error) { console.error( - `Failed to resolve time ranges for metrics view ${metricsViewName} in instance ${instanceId}`, + `Failed to resolve time ranges for metrics view ${metricsViewName} in instance ${client.instanceId}`, error, ); return timeRangesToReturn; @@ -71,7 +70,7 @@ export async function resolveTimeRanges( } export async function fetchTimeRanges({ - instanceId, + client, metricsViewName, rillTimes, timeZone, @@ -79,7 +78,7 @@ export async function fetchTimeRanges({ executionTime, cacheBust = false, }: { - instanceId: string; + client: RuntimeClient; metricsViewName: string; rillTimes: string[]; timeDimension?: string | undefined; @@ -88,16 +87,16 @@ export async function fetchTimeRanges({ cacheBust?: boolean; }) { const requestBody = { + metricsViewName, expressions: rillTimes, timeZone, - executionTime, + executionTime: executionTime as any, priority: 100, timeDimension, }; const queryKey = getQueryServiceMetricsViewTimeRangesQueryKey( - instanceId, - metricsViewName, + client.instanceId, requestBody, ); @@ -109,12 +108,7 @@ export async function fetchTimeRanges({ const response = await queryClient.fetchQuery({ queryKey: queryKey, - queryFn: () => - queryServiceMetricsViewTimeRanges( - instanceId, - metricsViewName, - requestBody, - ), + queryFn: () => queryServiceMetricsViewTimeRanges(client, requestBody), staleTime: 60, }); diff --git a/web-common/src/features/dashboards/time-controls/time-control-store.ts b/web-common/src/features/dashboards/time-controls/time-control-store.ts index ef3070db980..e9e39ca5362 100644 --- a/web-common/src/features/dashboards/time-controls/time-control-store.ts +++ b/web-common/src/features/dashboards/time-controls/time-control-store.ts @@ -14,6 +14,7 @@ import { getExploreValidSpecQueryOptions, useExploreValidSpec, } from "@rilldata/web-common/features/explores/selectors"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; import { getComparionRangeForScrub, @@ -245,14 +246,14 @@ export function createTimeControlStore(ctx: StateManagers) { } export function createTimeControlStoreFromName( - instanceId: string, + client: RuntimeClient, metricsViewName: string, exploreName: string, ) { return derived( [ - useExploreValidSpec(instanceId, exploreName, undefined, queryClient), - useMetricsViewTimeRange(instanceId, metricsViewName, {}, queryClient), + useExploreValidSpec(client, exploreName, undefined, queryClient), + useMetricsViewTimeRange(client, metricsViewName, {}, queryClient), useExploreState(exploreName), ], ([validSpecResp, timeRangeSummaryResp, dashboardStore]) => @@ -266,14 +267,15 @@ export function createTimeControlStoreFromName( } export function createStableTimeControlStoreFromName( + client: RuntimeClient, exploreNameStore: Readable, ) { const validSpecQuery = createQuery( - getExploreValidSpecQueryOptions(exploreNameStore), + getExploreValidSpecQueryOptions(client, exploreNameStore), queryClient, ); const metricsViewTimeRangeQuery = createQuery( - getMetricsViewTimeRangeFromExploreQueryOptions(exploreNameStore), + getMetricsViewTimeRangeFromExploreQueryOptions(client, exploreNameStore), queryClient, ); const exploreStore = useStableExploreState(exploreNameStore); diff --git a/web-common/src/features/dashboards/time-dimension-details/tdd-export.ts b/web-common/src/features/dashboards/time-dimension-details/tdd-export.ts index 3152cfb04f8..0a3404497ba 100644 --- a/web-common/src/features/dashboards/time-dimension-details/tdd-export.ts +++ b/web-common/src/features/dashboards/time-dimension-details/tdd-export.ts @@ -14,7 +14,6 @@ import type { V1Query, V1TimeRange, } from "@rilldata/web-common/runtime-client"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import { get } from "svelte/store"; import { buildWhereParamForDimensionTableAndTDDExports } from "../../exports/export-filters"; import { dimensionSearchText as dimensionSearchTextStore } from "../stores/dashboard-stores"; @@ -31,6 +30,7 @@ export function getTDDExportQuery( const query: V1Query = { metricsViewAggregationRequest: getTDDAggregationRequest({ + instanceId: ctx.runtimeClient.instanceId, metricsViewName, exploreState, timeControlState, @@ -45,6 +45,7 @@ export function getTDDExportQuery( } export function getTDDAggregationRequest({ + instanceId, metricsViewName, exploreState, timeControlState, @@ -53,6 +54,7 @@ export function getTDDAggregationRequest({ dimensionSearchText, isScheduled, }: { + instanceId: string; metricsViewName: string; exploreState: ExploreState; timeControlState: TimeControlState; @@ -94,7 +96,7 @@ export function getTDDAggregationRequest({ exploreState.selectedTimeDimension || metricsViewSpec.timeDimension || ""; return { - instanceId: get(runtime).instanceId, + instanceId, metricsView: metricsViewName, dimensions: [ { name: dimensionName }, diff --git a/web-common/src/features/dashboards/time-series/MetricsTimeSeriesCharts.svelte b/web-common/src/features/dashboards/time-series/MetricsTimeSeriesCharts.svelte index b7fe1a107ca..17c85a9a867 100644 --- a/web-common/src/features/dashboards/time-series/MetricsTimeSeriesCharts.svelte +++ b/web-common/src/features/dashboards/time-series/MetricsTimeSeriesCharts.svelte @@ -26,6 +26,7 @@ type DashboardTimeControls, } from "@rilldata/web-common/lib/time/types"; import { type MetricsViewSpecMeasure } from "@rilldata/web-common/runtime-client/gen/index.schemas"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { Button } from "../../../components/button"; import Pivot from "../../../components/icons/Pivot.svelte"; import { TIME_GRAIN } from "../../../lib/time/config"; @@ -42,7 +43,6 @@ import { mergeDimensionAndMeasureFilters } from "@rilldata/web-common/features/dashboards/filters/measure-filters/measure-filter-utils"; import { sanitiseExpression } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; import { DateTime, Interval } from "luxon"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import MoreHorizontal from "@rilldata/web-common/components/icons/MoreHorizontal.svelte"; import IconButton from "@rilldata/web-common/components/button/IconButton.svelte"; import Switch from "@rilldata/web-common/components/forms/Switch.svelte"; @@ -81,7 +81,7 @@ let connectNulls = true; let chartSettingsOpen = false; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); $: ({ selectedTimeRange, @@ -182,7 +182,7 @@ $: chartReady = !!ready; // Check if annotations are enabled for this explore - $: exploreValidSpec = useExploreValidSpec(instanceId, exploreName); + $: exploreValidSpec = useExploreValidSpec(client, exploreName); $: annotationsEnabled = !!$exploreValidSpec.data?.metricsView?.annotations?.length; @@ -390,7 +390,6 @@ {measure} isMeasureExpanded={showTimeDimensionDetail} {showComparison} - {instanceId} metricsViewName={chartMetricsViewName} where={chartWhere} {timeDimension} @@ -409,7 +408,6 @@ tddChartType={showTimeDimensionDetail ? (tddChartType ?? TDDChart.DEFAULT) : TDDChart.DEFAULT} - {instanceId} metricsViewName={chartMetricsViewName} where={chartWhere} {timeDimension} diff --git a/web-common/src/features/dashboards/time-series/annotations-selectors.ts b/web-common/src/features/dashboards/time-series/annotations-selectors.ts index eda6b70ad75..59bfe859e95 100644 --- a/web-common/src/features/dashboards/time-series/annotations-selectors.ts +++ b/web-common/src/features/dashboards/time-series/annotations-selectors.ts @@ -6,6 +6,7 @@ import { type V1MetricsViewAnnotationsResponseAnnotation, V1TimeGrain, } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { DateTime, Interval } from "luxon"; import { TIME_GRAIN } from "@rilldata/web-common/lib/time/config.ts"; import { keepPreviousData } from "@tanstack/svelte-query"; @@ -15,7 +16,7 @@ import { keepPreviousData } from "@tanstack/svelte-query"; * the raw response rows into sorted Annotation objects via `select`. */ export function createAnnotationsQuery( - instanceId: string, + client: RuntimeClient, metricsViewName: string, measureName: string, timeDimension: string | undefined, @@ -29,11 +30,15 @@ export function createAnnotationsQuery( const grain = timeGranularity ?? V1TimeGrain.TIME_GRAIN_UNSPECIFIED; return createQueryServiceMetricsViewAnnotations( - instanceId, - metricsViewName, + client, { - timeRange: { start: timeStart, end: timeEnd, timeDimension }, - timeGrain: timeGranularity, + metricsViewName, + timeRange: { + start: timeStart as any, + end: timeEnd as any, + timeDimension, + }, + timeGrain: timeGranularity as any, measures: [measureName], }, { diff --git a/web-common/src/features/dashboards/time-series/measure-chart/MeasureChart.svelte b/web-common/src/features/dashboards/time-series/measure-chart/MeasureChart.svelte index e862ce6b9f9..c1db7d2717d 100644 --- a/web-common/src/features/dashboards/time-series/measure-chart/MeasureChart.svelte +++ b/web-common/src/features/dashboards/time-series/measure-chart/MeasureChart.svelte @@ -12,7 +12,7 @@ createQueryServiceMetricsViewTimeSeries, type V1Expression, } from "@rilldata/web-common/runtime-client"; - import type { HTTPError } from "@rilldata/web-common/runtime-client/fetchWrapper"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { keepPreviousData } from "@tanstack/svelte-query"; import Spinner from "@rilldata/web-common/features/entity-management/Spinner.svelte"; import { EntityStatus } from "@rilldata/web-common/features/entity-management/types"; @@ -30,7 +30,6 @@ const VISIBILITY_ROOT_MARGIN = "120px"; export let measure: MetricsViewSpecMeasure; - export let instanceId: string; export let metricsViewName: string; export let where: V1Expression | undefined = undefined; export let timeDimension: string | undefined = undefined; @@ -62,6 +61,7 @@ export let scrubController: ScrubController; export let connectNulls: boolean = true; + const client = useRuntimeClient(); const { visible, observe } = createVisibilityObserver(VISIBILITY_ROOT_MARGIN); let container: HTMLDivElement; @@ -79,17 +79,18 @@ $: measureName = measure.name ?? ""; $: height = showTimeDimensionDetail ? 245 : 145; - // Extract ISO strings for API calls - $: timeStart = interval?.start?.toISO() ?? undefined; - $: timeEnd = interval?.end?.toISO() ?? undefined; - $: comparisonTimeStart = comparisonInterval?.start?.toISO() ?? undefined; - $: comparisonTimeEnd = comparisonInterval?.end?.toISO() ?? undefined; + // Extract ISO strings for API calls (must be UTC for protobuf Timestamp parsing) + $: timeStart = interval?.start?.toUTC().toISO() ?? undefined; + $: timeEnd = interval?.end?.toUTC().toISO() ?? undefined; + $: comparisonTimeStart = + comparisonInterval?.start?.toUTC().toISO() ?? undefined; + $: comparisonTimeEnd = comparisonInterval?.end?.toUTC().toISO() ?? undefined; // Time series queries $: timeSeriesQuery = createQueryServiceMetricsViewTimeSeries( - instanceId, - metricsViewName, + client, { + metricsViewName, measureNames: [measureName], where, timeDimension, @@ -108,9 +109,9 @@ ); $: comparisonTimeSeriesQuery = createQueryServiceMetricsViewTimeSeries( - instanceId, - metricsViewName, + client, { + metricsViewName, measureNames: [measureName], where, timeDimension, @@ -145,8 +146,7 @@ ); $: isError = $timeSeriesQuery.isError; - $: error = ($timeSeriesQuery.error as HTTPError | undefined)?.response?.data - ?.message; + $: error = $timeSeriesQuery.error?.message; // Dimension comparison data $: hasDimensionComparison = @@ -154,7 +154,7 @@ $: dimAggQuery = hasDimensionComparison ? createDimensionAggregationQuery( - instanceId, + client, metricsViewName, measureName, comparisonDimension!, @@ -172,7 +172,7 @@ $: dimCompAggQuery = hasDimensionComparison && showComparison && !!comparisonTimeStart ? createDimensionAggregationQuery( - instanceId, + client, metricsViewName, measureName, comparisonDimension!, @@ -216,7 +216,7 @@ // Annotations query $: annotationsQuery = createAnnotationsQuery( - instanceId, + client, metricsViewName, measureName, timeDimension, diff --git a/web-common/src/features/dashboards/time-series/measure-chart/use-dimension-data.ts b/web-common/src/features/dashboards/time-series/measure-chart/use-dimension-data.ts index 5f286f119d5..9cb54dc80be 100644 --- a/web-common/src/features/dashboards/time-series/measure-chart/use-dimension-data.ts +++ b/web-common/src/features/dashboards/time-series/measure-chart/use-dimension-data.ts @@ -11,7 +11,8 @@ import { type V1TimeGrain, type V1TimeSeriesValue, } from "@rilldata/web-common/runtime-client"; -import type { HTTPError } from "@rilldata/web-common/runtime-client/fetchWrapper"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; +import type { ConnectError } from "@connectrpc/connect"; import { keepPreviousData, type CreateQueryResult, @@ -29,7 +30,7 @@ import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryCl * Used by MeasureChart to fetch per-dimension-value time series. */ export function createDimensionAggregationQuery( - instanceId: string, + client: RuntimeClient, metricsViewName: string, measureName: string, dimensionName: string, @@ -41,7 +42,7 @@ export function createDimensionAggregationQuery( timeGranularity: V1TimeGrain, timeZone: string, enabled: boolean, -): CreateQueryResult { +): CreateQueryResult { const baseFilter = where ? (filterExpressions(where, () => true) ?? createAndExpression([])) : createAndExpression([]); @@ -51,18 +52,18 @@ export function createDimensionAggregationQuery( ]); return createQueryServiceMetricsViewAggregation( - instanceId, - metricsViewName, + client, { + metricsView: metricsViewName, measures: [{ name: measureName }], dimensions: [ { name: dimensionName }, - { name: timeDimension, timeGrain: timeGranularity, timeZone }, + { name: timeDimension, timeGrain: timeGranularity as any, timeZone }, ], where: sanitiseExpression(updatedFilter, undefined), timeRange: { - start: timeStart, - end: timeEnd, + start: timeStart as any, + end: timeEnd as any, timeDimension, }, sort: [ diff --git a/web-common/src/features/dashboards/time-series/multiple-dimension-queries.ts b/web-common/src/features/dashboards/time-series/multiple-dimension-queries.ts index f18161e9235..b7feafbddd8 100644 --- a/web-common/src/features/dashboards/time-series/multiple-dimension-queries.ts +++ b/web-common/src/features/dashboards/time-series/multiple-dimension-queries.ts @@ -26,9 +26,8 @@ import { type V1MetricsViewAggregationResponse, V1TimeGrain, type V1TimeSeriesValue, - createQueryServiceMetricsViewAggregation, } from "@rilldata/web-common/runtime-client"; -import type { HTTPError } from "@rilldata/web-common/runtime-client/fetchWrapper"; +import { createQueryServiceMetricsViewAggregation } from "@rilldata/web-common/runtime-client"; import { type CreateQueryResult, keepPreviousData, @@ -77,13 +76,12 @@ export function getDimensionValuesForComparison( ): Readable { return derived( [ - ctx.runtime, ctx.metricsViewName, ctx.dashboardStore, useTimeControlStore(ctx), dimensionSearchText, ], - ([runtime, name, dashboardStore, timeControls, searchText], set) => { + ([name, dashboardStore, timeControls, searchText], set) => { const isValidMeasureList = measures?.length > 0 && measures?.every((m) => m !== undefined); @@ -99,7 +97,7 @@ export function getDimensionValuesForComparison( let comparisonValues: (string | null)[] = []; if (surface === "chart") { return selectedDimensionValues( - runtime.instanceId, + ctx.runtimeClient, [name], dashboardStore?.whereFilter, dimensionName, @@ -126,9 +124,9 @@ export function getDimensionValuesForComparison( return derived( createQueryServiceMetricsViewAggregation( - runtime.instanceId, - name, + ctx.runtimeClient, { + metricsView: name, measures: measures.map((measure) => ({ name: measure })), dimensions: [{ name: dimensionName }], where: sanitiseExpression( @@ -234,15 +232,10 @@ function getAggregationQueryForTopList( measures: string[], dimensionValues: DimensionTopList, isTimeComparison: boolean = false, -): CreateQueryResult { +): CreateQueryResult { return derived( - [ - ctx.runtime, - ctx.metricsViewName, - ctx.dashboardStore, - useTimeControlStore(ctx), - ], - ([runtime, metricsViewName, dashboardStore, timeStore], set) => { + [ctx.metricsViewName, ctx.dashboardStore, useTimeControlStore(ctx)], + ([metricsViewName, dashboardStore, timeStore], set) => { const dimensionName = dashboardStore?.selectedComparisonDimension; const timeGrain = timeStore?.selectedTimeRange?.interval || V1TimeGrain.TIME_GRAIN_DAY; @@ -273,9 +266,9 @@ function getAggregationQueryForTopList( }; return createQueryServiceMetricsViewAggregation( - runtime.instanceId, - metricsViewName, + ctx.runtimeClient, { + metricsView: metricsViewName, measures: measures.map((measure) => ({ name: measure })), dimensions: [ { name: dimensionName }, diff --git a/web-common/src/features/dashboards/time-series/timeseries-data-store.ts b/web-common/src/features/dashboards/time-series/timeseries-data-store.ts index 46a3cbe8319..41faf475bd9 100644 --- a/web-common/src/features/dashboards/time-series/timeseries-data-store.ts +++ b/web-common/src/features/dashboards/time-series/timeseries-data-store.ts @@ -13,9 +13,8 @@ import { type V1MetricsViewAggregationResponse, type V1MetricsViewAggregationResponseDataItem, type V1MetricsViewTimeSeriesResponse, - createQueryServiceMetricsViewTimeSeries, } from "@rilldata/web-common/runtime-client"; -import type { HTTPError } from "@rilldata/web-common/runtime-client/fetchWrapper"; +import { createQueryServiceMetricsViewTimeSeries } from "@rilldata/web-common/runtime-client"; import { keepPreviousData, type CreateQueryResult, @@ -55,21 +54,16 @@ export function createMetricsViewTimeSeries( ctx: StateManagers, measures: string[], isComparison = false, -): CreateQueryResult { +): CreateQueryResult { return derived( - [ - ctx.runtime, - ctx.metricsViewName, - ctx.dashboardStore, - useTimeControlStore(ctx), - ], - ([runtime, metricsViewName, dashboardStore, timeControls], set) => { + [ctx.metricsViewName, ctx.dashboardStore, useTimeControlStore(ctx)], + ([metricsViewName, dashboardStore, timeControls], set) => { const timeGrain = timeControls.selectedTimeRange?.interval; return createQueryServiceMetricsViewTimeSeries( - runtime.instanceId, - metricsViewName, + ctx.runtimeClient, { + metricsViewName, measureNames: measures, where: sanitiseExpression( mergeDimensionAndMeasureFilters( @@ -178,7 +172,7 @@ export function createTimeSeriesDataStore( }); let unfilteredTotals: - | CreateQueryResult + | CreateQueryResult | Writable = writable(null); if (dashboardStore?.selectedComparisonDimension) { @@ -189,10 +183,10 @@ export function createTimeSeriesDataStore( ); } let comparisonTimeSeries: - | CreateQueryResult + | CreateQueryResult | Writable = writable(null); let comparisonTotals: - | CreateQueryResult + | CreateQueryResult | Writable = writable(null); if (showComparison) { comparisonTimeSeries = createMetricsViewTimeSeries( @@ -248,13 +242,11 @@ export function createTimeSeriesDataStore( const error = {}; if (primary.error) { isError = true; - error["timeseries"] = ( - primary.error as HTTPError - ).response?.data?.message; + error["timeseries"] = (primary.error as Error).message; } if (primaryTotal.error) { isError = true; - error["totals"] = primaryTotal.error.response?.data?.message; + error["totals"] = (primaryTotal.error as Error).message; } const primaryIsFetching = primary.isFetching; const primaryTotalIsFetching = primaryTotal.isFetching; diff --git a/web-common/src/features/dashboards/time-series/totals-data-store.ts b/web-common/src/features/dashboards/time-series/totals-data-store.ts index be22d38801c..5576201e5ad 100644 --- a/web-common/src/features/dashboards/time-series/totals-data-store.ts +++ b/web-common/src/features/dashboards/time-series/totals-data-store.ts @@ -7,11 +7,8 @@ import { sanitiseExpression, } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; import { useTimeControlStore } from "@rilldata/web-common/features/dashboards/time-controls/time-control-store"; -import { - createQueryServiceMetricsViewAggregation, - type V1MetricsViewAggregationResponse, -} from "@rilldata/web-common/runtime-client"; -import { type HTTPError } from "@rilldata/web-common/runtime-client/fetchWrapper"; +import type { V1MetricsViewAggregationResponse } from "@rilldata/web-common/runtime-client"; +import { createQueryServiceMetricsViewAggregation } from "@rilldata/web-common/runtime-client"; import type { CreateQueryResult } from "@tanstack/svelte-query"; import { derived } from "svelte/store"; @@ -19,19 +16,14 @@ export function createTotalsForMeasure( ctx: StateManagers, measures: string[], isComparison = false, -): CreateQueryResult { +): CreateQueryResult { return derived( - [ - ctx.runtime, - ctx.metricsViewName, - useTimeControlStore(ctx), - ctx.dashboardStore, - ], - ([runtime, metricsViewName, timeControls, dashboard], set) => + [ctx.metricsViewName, useTimeControlStore(ctx), ctx.dashboardStore], + ([metricsViewName, timeControls, dashboard], set) => createQueryServiceMetricsViewAggregation( - runtime.instanceId, - metricsViewName, + ctx.runtimeClient, { + metricsView: metricsViewName, measures: measures.map((measure) => ({ name: measure })), where: sanitiseExpression( mergeDimensionAndMeasureFilters( @@ -65,15 +57,10 @@ export function createUnfilteredTotalsForMeasure( ctx: StateManagers, measures: string[], dimensionName: string, -): CreateQueryResult { +): CreateQueryResult { return derived( - [ - ctx.runtime, - ctx.metricsViewName, - useTimeControlStore(ctx), - ctx.dashboardStore, - ], - ([runtime, metricsViewName, timeControls, dashboard], set) => { + [ctx.metricsViewName, useTimeControlStore(ctx), ctx.dashboardStore], + ([metricsViewName, timeControls, dashboard], set) => { const filter = sanitiseExpression( mergeDimensionAndMeasureFilters( dashboard.whereFilter, @@ -88,9 +75,9 @@ export function createUnfilteredTotalsForMeasure( ); createQueryServiceMetricsViewAggregation( - runtime.instanceId, - metricsViewName, + ctx.runtimeClient, { + metricsView: metricsViewName, measures: measures.map((measure) => ({ name: measure })), where: updatedFilter, timeRange: { diff --git a/web-common/src/features/dashboards/url-state/ExploreStateTestComponent.svelte b/web-common/src/features/dashboards/url-state/ExploreStateTestComponent.svelte index 0325e508c80..a4d98642891 100644 --- a/web-common/src/features/dashboards/url-state/ExploreStateTestComponent.svelte +++ b/web-common/src/features/dashboards/url-state/ExploreStateTestComponent.svelte @@ -1,11 +1,11 @@ diff --git a/web-common/src/features/dashboards/url-state/get-rill-default-explore-url-params.ts b/web-common/src/features/dashboards/url-state/get-rill-default-explore-url-params.ts index 5da95a9b39d..5e19521484b 100644 --- a/web-common/src/features/dashboards/url-state/get-rill-default-explore-url-params.ts +++ b/web-common/src/features/dashboards/url-state/get-rill-default-explore-url-params.ts @@ -10,6 +10,7 @@ import { getExploreValidSpecQueryOptions, useExploreValidSpec, } from "@rilldata/web-common/features/explores/selectors"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { type V1ExploreSpec, type V1MetricsViewSpec, @@ -80,13 +81,14 @@ export function createRillDefaultExploreUrlParams( * TODO: replace {@link createRillDefaultExploreUrlParams} with this */ export function createRillDefaultExploreUrlParamsV2( + client: RuntimeClient, exploreNameStore: Readable, ) { const validSpecQuery = createQuery( - getExploreValidSpecQueryOptions(exploreNameStore), + getExploreValidSpecQueryOptions(client, exploreNameStore), ); const timeRangeQuery = createQuery( - getMetricsViewTimeRangeFromExploreQueryOptions(exploreNameStore), + getMetricsViewTimeRangeFromExploreQueryOptions(client, exploreNameStore), ); return derived( diff --git a/web-common/src/features/dashboards/workspace/Dashboard.svelte b/web-common/src/features/dashboards/workspace/Dashboard.svelte index ff9b721f691..e513e3409c4 100644 --- a/web-common/src/features/dashboards/workspace/Dashboard.svelte +++ b/web-common/src/features/dashboards/workspace/Dashboard.svelte @@ -1,6 +1,10 @@ = 3) { // After 6 seconds, assume reconcile failure throw new Error( @@ -101,7 +103,7 @@ export async function waitForResourceReconciliation( * Returns true if file was found, false if cancelled. */ export async function pollForFileCreation( - instanceId: string, + client: RuntimeClient, filePath: string, abortSignal: AbortSignal, pollIntervalMs: number = 1000, @@ -110,7 +112,7 @@ export async function pollForFileCreation( await new Promise((resolve) => setTimeout(resolve, pollIntervalMs)); try { - await runtimeServiceGetFile(instanceId, { path: filePath }); + await runtimeServiceGetFile(client, { path: filePath }); return true; // success, file exists } catch { // 404 error, file is not ready yet, continue polling @@ -120,7 +122,7 @@ export async function pollForFileCreation( } export async function renameFileArtifact( - instanceId: string, + client: RuntimeClient, fromPath: string, toPath: string, ) { @@ -145,12 +147,12 @@ export async function renameFileArtifact( } try { - await runtimeServiceRenameFile(instanceId, { + await runtimeServiceRenameFile(client, { fromPath, toPath, }); - httpRequestQueue.removeByName(fromName); + client.requestQueue.removeByName(fromName); const topLevelFromFolder = getTopLevelFolder(addLeadingSlash(fromPath)); const topLevelToFolder = getTopLevelFolder(addLeadingSlash(toPath)); @@ -167,13 +169,13 @@ export async function renameFileArtifact( } } catch (err) { eventBus.emit("notification", { - message: `Failed to rename ${fromName} to ${toName}: ${extractMessage(err.response?.data?.message ?? err.message)}`, + message: `Failed to rename ${fromName} to ${toName}: ${extractMessage(err.rawMessage ?? err.response?.data?.message ?? err.message)}`, }); } } export async function duplicateFileArtifact( - instanceId: string, + client: RuntimeClient, filePath: string, ): Promise { // Get new file path @@ -187,7 +189,7 @@ export async function duplicateFileArtifact( const fileContent = get(fileArtifact.remoteContent); // Create new file - await runtimeServicePutFile(instanceId, { + await runtimeServicePutFile(client, { path: newFilePath, blob: fileContent ?? "", }); @@ -197,21 +199,21 @@ export async function duplicateFileArtifact( } export async function deleteFileArtifact( - instanceId: string, + client: RuntimeClient, filePath: string, force = false, ) { const name = extractFileName(filePath); try { - await runtimeServiceDeleteFile(instanceId, { + await runtimeServiceDeleteFile(client, { path: filePath, force, }); - httpRequestQueue.removeByName(name); + client.requestQueue.removeByName(name); } catch (err) { eventBus.emit("notification", { - message: `Failed to delete ${name}: ${extractMessage(err.response?.data?.message ?? err.message)}`, + message: `Failed to delete ${name}: ${extractMessage(err.rawMessage ?? err.response?.data?.message ?? err.message)}`, }); } } diff --git a/web-common/src/features/entity-management/dag-utils.ts b/web-common/src/features/entity-management/dag-utils.ts index 005e1e16368..1053f7891d0 100644 --- a/web-common/src/features/entity-management/dag-utils.ts +++ b/web-common/src/features/entity-management/dag-utils.ts @@ -3,12 +3,16 @@ import { runtimeServiceListResources, type V1Resource, } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { queryClient } from "../../lib/svelte-query/globalQueryClient"; -export async function isLeafResource(resource: V1Resource, instanceId: string) { +export async function isLeafResource( + resource: V1Resource, + client: RuntimeClient, +) { const allResources = await queryClient.fetchQuery({ - queryKey: getRuntimeServiceListResourcesQueryKey(instanceId, undefined), - queryFn: () => runtimeServiceListResources(instanceId, undefined), + queryKey: getRuntimeServiceListResourcesQueryKey(client.instanceId, {}), + queryFn: () => runtimeServiceListResources(client, {}), }); if (!allResources || !allResources.resources) return false; diff --git a/web-common/src/features/entity-management/error-utils.ts b/web-common/src/features/entity-management/error-utils.ts index 243774eec48..01c0acc4bfc 100644 --- a/web-common/src/features/entity-management/error-utils.ts +++ b/web-common/src/features/entity-management/error-utils.ts @@ -1,5 +1,6 @@ import type { V1Resource } from "../../runtime-client"; import { createRuntimeServiceListResources } from "../../runtime-client"; +import type { RuntimeClient } from "../../runtime-client/v2"; import { resourceNameToId } from "./resource-utils"; /** @@ -11,13 +12,13 @@ import { resourceNameToId } from "./resource-utils"; * Cloud viewers, who have no access to the project's resource graph. */ export function createRootCauseErrorQuery( - instanceId: string, + client: RuntimeClient, resource: V1Resource | undefined, errorMessage: string | undefined, ) { return createRuntimeServiceListResources( - instanceId, - undefined, + client, + {}, { query: { enabled: !!errorMessage && !!resource, diff --git a/web-common/src/features/entity-management/file-and-resource-watcher.ts b/web-common/src/features/entity-management/file-and-resource-watcher.ts index 5742865f91c..0e277095f07 100644 --- a/web-common/src/features/entity-management/file-and-resource-watcher.ts +++ b/web-common/src/features/entity-management/file-and-resource-watcher.ts @@ -25,10 +25,9 @@ import { invalidateMetricsViewData, invalidateProfilingQueries, } from "@rilldata/web-common/runtime-client/invalidation"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; -import { get } from "svelte/store"; import { connectorExplorerStore } from "../connectors/explorer/connector-explorer-store"; import { sourceIngestionTracker } from "../sources/sources-store"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { isLeafResource } from "./dag-utils"; import { eventBus } from "@rilldata/web-common/lib/event-bus/event-bus"; import { SSEConnectionManager } from "@rilldata/web-common/runtime-client/sse-connection-manager"; @@ -134,15 +133,37 @@ export class FileAndResourceWatcher { }); } + private _instanceId = ""; + private _runtimeClient: RuntimeClient | undefined; + + public setInstanceId(instanceId: string) { + this._instanceId = instanceId; + } + + public setRuntimeClient(client: RuntimeClient) { + this._runtimeClient = client; + this._instanceId = client.instanceId; + } + private get instanceId() { - return get(runtime).instanceId; + return this._instanceId; } private invalidateAll() { // Invalidate all runtime queries on reconnect return queryClient.invalidateQueries({ - predicate: (query) => - query.queryHash.includes(`v1/instances/${this.instanceId}`), + predicate: (query) => { + const key = query.queryKey; + if (key.length >= 3 && key[2] === this.instanceId) { + const svc = key[0]; + return ( + svc === "QueryService" || + svc === "RuntimeService" || + svc === "ConnectorService" + ); + } + return false; + }, }); } @@ -172,7 +193,7 @@ export class FileAndResourceWatcher { if (res.path === "/rill.yaml") { // If it's a rill.yaml file, invalidate the dev JWT queries void queryClient.invalidateQueries({ - queryKey: getRuntimeServiceIssueDevJWTQueryKey({}), + queryKey: getRuntimeServiceIssueDevJWTQueryKey(this.instanceId), }); await invalidate("init"); @@ -227,16 +248,14 @@ export class FileAndResourceWatcher { resource: V1Resource | undefined; }>( getRuntimeServiceGetResourceQueryKey(this.instanceId, { - "name.name": res.name.name, - "name.kind": res.name.kind, + name: { name: res.name.name, kind: res.name.kind }, }), )?.resource; // Set the updated resource in the query cache queryClient.setQueryData( getRuntimeServiceGetResourceQueryKey(this.instanceId, { - "name.name": res.name.name, - "name.kind": res.name.kind, + name: { name: res.name.name, kind: res.name.kind }, }), { resource: res?.resource, @@ -301,10 +320,10 @@ export class FileAndResourceWatcher { // Invalidate the connector's list of tables void queryClient.invalidateQueries({ - queryKey: getConnectorServiceOLAPListTablesQueryKey({ - instanceId: this.instanceId, - connector: res.name.name, - }), + queryKey: getConnectorServiceOLAPListTablesQueryKey( + this.instanceId, + { connector: res.name.name }, + ), }); // Done @@ -335,10 +354,10 @@ export class FileAndResourceWatcher { ); for (const connector of connectorsToInvalidate) { void queryClient.invalidateQueries({ - queryKey: getConnectorServiceOLAPListTablesQueryKey({ - instanceId: this.instanceId, - connector: connector, - }), + queryKey: getConnectorServiceOLAPListTablesQueryKey( + this.instanceId, + { connector }, + ), }); } } @@ -371,7 +390,8 @@ export class FileAndResourceWatcher { sourceIngestionTracker.isPending(filePath) && res.resource.meta.specVersion === "1" && // First file version res.resource.meta.stateVersion === "2" && // First ingest is complete - (await isLeafResource(res.resource, this.instanceId)); // Protects against existing projects reconciling anew + this._runtimeClient && + (await isLeafResource(res.resource, this._runtimeClient)); // Protects against existing projects reconciling anew if (isNewSource) { sourceIngestionTracker.trackIngested(filePath); } @@ -381,7 +401,7 @@ export class FileAndResourceWatcher { void queryClient.invalidateQueries({ queryKey: getRuntimeServiceGetModelPartitionsQueryKey( this.instanceId, - res.name.name, + { model: res.name.name }, ), }); } @@ -423,11 +443,9 @@ export class FileAndResourceWatcher { case ResourceKind.Canvas: { void queryClient.refetchQueries({ - queryKey: getQueryServiceResolveCanvasQueryKey( - this.instanceId, - res.name.name, - {}, - ), + queryKey: getQueryServiceResolveCanvasQueryKey(this.instanceId, { + canvas: res.name.name, + }), }); return; } @@ -490,10 +508,10 @@ export class FileAndResourceWatcher { // Invalidate the connector's list of tables void queryClient.invalidateQueries({ - queryKey: getConnectorServiceOLAPListTablesQueryKey({ - instanceId: this.instanceId, - connector: connectorName, - }), + queryKey: getConnectorServiceOLAPListTablesQueryKey( + this.instanceId, + { connector: connectorName }, + ), }); // Done diff --git a/web-common/src/features/entity-management/file-artifact.ts b/web-common/src/features/entity-management/file-artifact.ts index ae269f10da9..fa7f579df05 100644 --- a/web-common/src/features/entity-management/file-artifact.ts +++ b/web-common/src/features/entity-management/file-artifact.ts @@ -18,7 +18,7 @@ import { type V1Resource, type V1ResourceName, } from "@rilldata/web-common/runtime-client"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import type { QueryClient, QueryFunction } from "@tanstack/svelte-query"; import { derived, @@ -91,15 +91,17 @@ export class FileArtifact { }> = writable({ scroll: undefined, selection: undefined }); private editorCallback: (content: string) => void = () => {}; + private readonly client: RuntimeClient; // Last time the state of the resource `kind/name` was updated. // This is updated in watch-resources and is used there to avoid // unnecessary calls to GetResource API. lastStateUpdatedOn: string | undefined; - constructor(filePath: string) { + constructor(client: RuntimeClient, filePath: string) { const [folderName, fileName] = splitFolderAndFileName(filePath); + this.client = client; this.path = filePath; this.folderName = folderName; this.fileName = fileName; @@ -119,7 +121,7 @@ export class FileArtifact { } fetchContent = async (invalidate = false) => { - const instanceId = get(runtime).instanceId; + const instanceId = this.client.instanceId; const queryParams = { path: this.path, }; @@ -129,7 +131,8 @@ export class FileArtifact { const queryFn: QueryFunction< Awaited> - > = ({ signal }) => runtimeServiceGetFile(instanceId, queryParams, signal); + > = ({ signal }) => + runtimeServiceGetFile(this.client, queryParams, { signal }); let fetchedContent: string | undefined = undefined; @@ -222,7 +225,7 @@ export class FileArtifact { }; private saveContent = async (blob: string) => { - const instanceId = get(runtime).instanceId; + const instanceId = this.client.instanceId; // Optimistically update the query queryClient.setQueryData( @@ -237,7 +240,7 @@ export class FileArtifact { try { const fileSavePromise = this.saveState.initiateSave(); - await runtimeServicePutFile(instanceId, { + await runtimeServicePutFile(this.client, { path: this.path, blob, }); @@ -301,10 +304,10 @@ export class FileArtifact { this.lastStateUpdatedOn = undefined; } - getResource = (queryClient: QueryClient, instanceId: string) => { + getResource = (queryClient: QueryClient) => { return derived(this.resourceName, (name, set) => useResource( - instanceId, + this.client, name?.name as string, name?.kind as ResourceKind, undefined, @@ -315,10 +318,9 @@ export class FileArtifact { getParseError = ( queryClient: QueryClient, - instanceId: string, ): Readable => { const store = derived( - useProjectParser(queryClient, instanceId), + useProjectParser(queryClient, this.client), (projectParser) => { if (projectParser.isFetching) { return get(store); @@ -332,14 +334,11 @@ export class FileArtifact { return store; }; - getAllErrors = ( - queryClient: QueryClient, - instanceId: string, - ): Readable => { + getAllErrors = (queryClient: QueryClient): Readable => { const store = derived( [ - useProjectParser(queryClient, instanceId), - this.getResource(queryClient, instanceId), + useProjectParser(queryClient, this.client), + this.getResource(queryClient), ], ([projectParser, resource]) => { if (projectParser.isFetching || resource.isFetching) { @@ -366,9 +365,9 @@ export class FileArtifact { return store; }; - getHasErrors(queryClient: QueryClient, instanceId: string) { + getHasErrors(queryClient: QueryClient) { return derived( - this.getAllErrors(queryClient, instanceId), + this.getAllErrors(queryClient), (errors) => errors.length > 0, ); } diff --git a/web-common/src/features/entity-management/file-artifacts.ts b/web-common/src/features/entity-management/file-artifacts.ts index 0be89adcdc1..f9338c3503e 100644 --- a/web-common/src/features/entity-management/file-artifacts.ts +++ b/web-common/src/features/entity-management/file-artifacts.ts @@ -7,6 +7,7 @@ import { type V1Resource, type V1ResourceName, } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import type { QueryClient } from "@tanstack/svelte-query"; import { derived, get, writable } from "svelte/store"; import { FileArtifact } from "./file-artifact"; @@ -36,9 +37,19 @@ class UnsavedFilesStore { export class FileArtifacts { private readonly artifacts: Map = new Map(); readonly unsavedFiles = new UnsavedFilesStore(); + private client!: RuntimeClient; - async init(queryClient: QueryClient, instanceId: string) { - const resources = await fetchResources(queryClient, instanceId); + /** + * Must be called synchronously (in the script block, not onMount) + * so that child components can access the client during initial render. + */ + setClient(client: RuntimeClient) { + this.client = client; + } + + async init(client: RuntimeClient, queryClient: QueryClient) { + this.client = client; + const resources = await fetchResources(queryClient, client); for (const resource of resources) { switch (resource.meta?.name?.kind) { case ResourceKind.Connector: @@ -52,9 +63,11 @@ export class FileArtifacts { case ResourceKind.API: // set query data for GetResource to avoid refetching data we already have queryClient.setQueryData( - getRuntimeServiceGetResourceQueryKey(instanceId, { - "name.name": resource.meta?.name?.name, - "name.kind": resource.meta?.name?.kind, + getRuntimeServiceGetResourceQueryKey(client.instanceId, { + name: { + name: resource.meta?.name?.name, + kind: resource.meta?.name?.kind, + }, }), { resource, @@ -90,7 +103,7 @@ export class FileArtifacts { let artifact = this.artifacts.get(filePath); if (!artifact) { - artifact = new FileArtifact(filePath); + artifact = new FileArtifact(this.client, filePath); this.artifacts.set(filePath, artifact); } @@ -158,15 +171,14 @@ export class FileArtifacts { */ async checkFileErrors( queryClient: QueryClient, - instanceId: string, filePath: string, ): Promise { const fileArtifact = this.getFileArtifact(filePath); - const hasErrorsStore = fileArtifact.getHasErrors(queryClient, instanceId); + const hasErrorsStore = fileArtifact.getHasErrors(queryClient); const hasErrors = get(hasErrorsStore); if (hasErrors) { - const errors = get(fileArtifact.getAllErrors(queryClient, instanceId)); + const errors = get(fileArtifact.getAllErrors(queryClient)); return errors[0]?.message ?? null; } return null; diff --git a/web-common/src/features/entity-management/file-selectors.ts b/web-common/src/features/entity-management/file-selectors.ts index f42e116be9d..1b1e61676c5 100644 --- a/web-common/src/features/entity-management/file-selectors.ts +++ b/web-common/src/features/entity-management/file-selectors.ts @@ -4,12 +4,16 @@ import { runtimeServiceListFiles, type V1ListFilesResponse, } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import type { QueryClient } from "@tanstack/svelte-query"; -export function useAllFileNames(queryClient: QueryClient, instanceId: string) { +export function useAllFileNames( + queryClient: QueryClient, + client: RuntimeClient, +) { return createRuntimeServiceListFiles( - instanceId, - undefined, + client, + {}, { query: { select: (data) => @@ -31,7 +35,7 @@ export function fileIsMainEntity(filePath: string) { } export function useFileNamesInDirectory( - instanceId: string, + client: RuntimeClient, directoryPath: string, ) { // Ensure the directory path starts with a slash @@ -39,18 +43,22 @@ export function useFileNamesInDirectory( directoryPath = `/${directoryPath}`; } - return createRuntimeServiceListFiles(instanceId, undefined, { - query: { - select: (data) => { - return useFileNamesInDirectorySelector(data, directoryPath); + return createRuntimeServiceListFiles( + client, + {}, + { + query: { + select: (data) => { + return useFileNamesInDirectorySelector(data, directoryPath); + }, }, }, - }); + ); } export async function getFileNamesInDirectory( queryClient: QueryClient, - instanceId: string, + client: RuntimeClient, directoryPath: string, ) { // Ensure the directory path starts with a slash @@ -61,9 +69,8 @@ export async function getFileNamesInDirectory( // Fetch all files in the project // (For now, we fetch all files at once, rather than individual requests for each directory.) const allFiles = await queryClient.fetchQuery({ - queryKey: getRuntimeServiceListFilesQueryKey(instanceId, undefined), - queryFn: ({ signal }) => - runtimeServiceListFiles(instanceId, undefined, signal), + queryKey: getRuntimeServiceListFilesQueryKey(client.instanceId, {}), + queryFn: ({ signal }) => runtimeServiceListFiles(client, {}, { signal }), }); // Get the file names in the given directory @@ -112,7 +119,7 @@ export function useFileNamesInDirectorySelector( } export function useDirectoryNamesInDirectory( - instanceId: string, + client: RuntimeClient, directoryPath: string, ) { // Ensure the directory path starts with a slash @@ -120,13 +127,17 @@ export function useDirectoryNamesInDirectory( directoryPath = `/${directoryPath}`; } - return createRuntimeServiceListFiles(instanceId, undefined, { - query: { - select: (data) => { - return useDirectoryNamesInDirectorySelector(data, directoryPath); + return createRuntimeServiceListFiles( + client, + {}, + { + query: { + select: (data) => { + return useDirectoryNamesInDirectorySelector(data, directoryPath); + }, }, }, - }); + ); } export function useDirectoryNamesInDirectorySelector( diff --git a/web-common/src/features/entity-management/project-parser.ts b/web-common/src/features/entity-management/project-parser.ts index ea5f78ae3f9..688347e5289 100644 --- a/web-common/src/features/entity-management/project-parser.ts +++ b/web-common/src/features/entity-management/project-parser.ts @@ -8,8 +8,10 @@ import { ResourceKind, SingletonProjectParserName } from "./resource-selectors"; export function getProjectParserVersion(instanceId: string) { const projectParserQuery = queryClient.getQueryData( getRuntimeServiceGetResourceQueryKey(instanceId, { - "name.kind": ResourceKind.ProjectParser, - "name.name": SingletonProjectParserName, + name: { + kind: ResourceKind.ProjectParser, + name: SingletonProjectParserName, + }, }), ); @@ -29,8 +31,10 @@ export async function waitForProjectParserVersion( while (currentVersion < version) { const projectParserQuery = queryClient.getQueryData( getRuntimeServiceGetResourceQueryKey(instanceId, { - "name.kind": ResourceKind.ProjectParser, - "name.name": SingletonProjectParserName, + name: { + kind: ResourceKind.ProjectParser, + name: SingletonProjectParserName, + }, }), ); diff --git a/web-common/src/features/entity-management/resource-invalidations.ts b/web-common/src/features/entity-management/resource-invalidations.ts index 6c3cce99623..240231eb0f3 100644 --- a/web-common/src/features/entity-management/resource-invalidations.ts +++ b/web-common/src/features/entity-management/resource-invalidations.ts @@ -11,8 +11,7 @@ export function refreshResource( ) { return queryClient.setQueryData( getRuntimeServiceGetResourceQueryKey(instanceId, { - "name.name": res.meta?.name?.name, - "name.kind": res.meta?.name?.kind, + name: { name: res.meta?.name?.name, kind: res.meta?.name?.kind }, }), { resource: res, diff --git a/web-common/src/features/entity-management/resource-selectors.ts b/web-common/src/features/entity-management/resource-selectors.ts index 0a97b1f9218..5832b12f558 100644 --- a/web-common/src/features/entity-management/resource-selectors.ts +++ b/web-common/src/features/entity-management/resource-selectors.ts @@ -4,7 +4,6 @@ import { getRuntimeServiceGetResourceQueryKey, getRuntimeServiceListResourcesQueryKey, getRuntimeServiceListResourcesQueryOptions, - type RpcStatus, runtimeServiceGetResource, runtimeServiceListResources, type V1ExploreSpec, @@ -14,11 +13,10 @@ import { V1ReconcileStatus, type V1Resource, } from "@rilldata/web-common/runtime-client"; +import type { ConnectError } from "@connectrpc/connect"; import type { CreateQueryOptions, QueryClient } from "@tanstack/svelte-query"; -import type { ErrorType } from "../../runtime-client/http-client"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; -import { derived } from "svelte/store"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store.ts"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; export enum ResourceKind { ProjectParser = "rill.runtime.v1.ProjectParser", @@ -143,28 +141,27 @@ export function coerceResourceKind(res: V1Resource): ResourceKind | undefined { } export function useResource( - instanceId: string, + client: RuntimeClient, name: string, kind: ResourceKind, queryOptions?: Partial< CreateQueryOptions< V1GetResourceResponse, - ErrorType, + ConnectError, T // T is the return type of the `select` function > >, queryClient?: QueryClient, ) { return createRuntimeServiceGetResource( - instanceId, + client, { - "name.kind": kind, - "name.name": name, + name: { kind, name }, }, { query: { select: (data) => data?.resource as T, - enabled: !!instanceId && !!name && !!kind, + enabled: !!client.instanceId && !!name && !!kind, ...queryOptions, }, }, @@ -178,28 +175,27 @@ export function useResource( * any `queryOptions`, not just `select` and `queryClient`. */ export function useResourceV2( - instanceId: string, + client: RuntimeClient, name: string, kind: ResourceKind, queryOptions?: Partial< CreateQueryOptions< V1GetResourceResponse, - ErrorType, + ConnectError, T // T is the return type of the `select` function > >, queryClient?: QueryClient, ) { return createRuntimeServiceGetResource( - instanceId, + client, { - "name.kind": kind, - "name.name": name, + name: { kind, name }, }, { query: { select: (data) => data?.resource as T, - enabled: !!instanceId && !!name && !!kind, + enabled: !!client.instanceId && !!name && !!kind, ...queryOptions, }, }, @@ -209,13 +205,13 @@ export function useResourceV2( export function useProjectParser( queryClient: QueryClient, - instanceId: string, + client: RuntimeClient, queryOptions?: Partial< - CreateQueryOptions, V1Resource> + CreateQueryOptions >, ) { return useResource( - instanceId, + client, SingletonProjectParserName, ResourceKind.ProjectParser, queryOptions, @@ -224,13 +220,13 @@ export function useProjectParser( } export function useFilteredResources>( - instanceId: string, + client: RuntimeClient, kind: ResourceKind, selector: (data: V1ListResourcesResponse) => T = (data) => data.resources as T, ) { return createRuntimeServiceListResources( - instanceId, + client, { kind: kind, }, @@ -248,36 +244,43 @@ export function useFilteredResources>( * This is to improve network requests since we need the full list all the time as well. */ export function useClientFilteredResources( - instanceId: string, + client: RuntimeClient, kind: ResourceKind, filter: (res: V1Resource) => boolean = () => true, ) { - return createRuntimeServiceListResources(instanceId, undefined, { - query: { - select: (data) => - data.resources?.filter( - (res) => res.meta?.name?.kind === kind && filter(res), - ) ?? [], + return createRuntimeServiceListResources( + client, + {}, + { + query: { + select: (data) => + data.resources?.filter( + (res) => res.meta?.name?.kind === kind && filter(res), + ) ?? [], + }, }, - }); + ); } /** * Query options version of {@link useClientFilteredResources}. */ export function getClientFilteredResourcesQueryOptions( + client: RuntimeClient, kind: ResourceKind, filter: (res: V1Resource) => boolean = () => true, ) { - return derived(runtime, ({ instanceId }) => - getRuntimeServiceListResourcesQueryOptions(instanceId, undefined, { + return getRuntimeServiceListResourcesQueryOptions( + client, + {}, + { query: { select: (data) => data.resources?.filter( (res) => res.meta?.name?.kind === kind && filter(res), ) ?? [], }, - }), + }, ); } @@ -290,19 +293,17 @@ export function resourceIsLoading(resource?: V1Resource) { export async function fetchResource( queryClient: QueryClient, - instanceId: string, + client: RuntimeClient, name: string, kind: ResourceKind, ) { const resp = await queryClient.fetchQuery({ - queryKey: getRuntimeServiceGetResourceQueryKey(instanceId, { - "name.name": name, - "name.kind": kind, + queryKey: getRuntimeServiceGetResourceQueryKey(client.instanceId, { + name: { name, kind }, }), queryFn: () => - runtimeServiceGetResource(instanceId, { - "name.name": name, - "name.kind": kind, + runtimeServiceGetResource(client, { + name: { name, kind }, }), }); return resp.resource; @@ -310,11 +311,11 @@ export async function fetchResource( export async function fetchResources( queryClient: QueryClient, - instanceId: string, + client: RuntimeClient, ) { const resp = await queryClient.fetchQuery({ - queryKey: getRuntimeServiceListResourcesQueryKey(instanceId), - queryFn: () => runtimeServiceListResources(instanceId, {}), + queryKey: getRuntimeServiceListResourcesQueryKey(client.instanceId, {}), + queryFn: () => runtimeServiceListResources(client, {}), }); return resp.resources ?? []; } @@ -324,9 +325,13 @@ export type MetricsViewAndExploreSpecs = { exploreSpecsMap: Map; exploreForMetricViewsMap: Map; }; -export function getMetricsViewAndExploreSpecsQueryOptions() { - return derived(runtime, ({ instanceId }) => - getRuntimeServiceListResourcesQueryOptions(instanceId, undefined, { +export function getMetricsViewAndExploreSpecsQueryOptions( + client: RuntimeClient, +) { + return getRuntimeServiceListResourcesQueryOptions( + client, + {}, + { query: { select: (data) => { const metricsViewSpecsMap = new Map(); @@ -355,6 +360,6 @@ export function getMetricsViewAndExploreSpecsQueryOptions() { } satisfies MetricsViewAndExploreSpecs; }, }, - }), + }, ); } diff --git a/web-common/src/features/entity-management/ui-actions.ts b/web-common/src/features/entity-management/ui-actions.ts index 47e4423bda3..804d78ceeab 100644 --- a/web-common/src/features/entity-management/ui-actions.ts +++ b/web-common/src/features/entity-management/ui-actions.ts @@ -6,12 +6,14 @@ import { isDuplicateName, VALID_NAME_PATTERN, } from "@rilldata/web-common/features/entity-management/name-utils"; +import { extractErrorMessage } from "@rilldata/web-common/lib/errors"; import { eventBus } from "@rilldata/web-common/lib/event-bus/event-bus"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { queryClient } from "../../lib/svelte-query/globalQueryClient"; import { getFileNamesInDirectory } from "./file-selectors"; export async function handleEntityRename( - instanceId: string, + client: RuntimeClient, newName: string, existingPath: string, existingName: string, @@ -30,7 +32,7 @@ export async function handleEntityRename( // Check if the new name is already in use const fileNamesInDirectory = await getFileNamesInDirectory( queryClient, - instanceId, + client, folder, ); if (isDuplicateName(newName, existingName, fileNamesInDirectory)) { @@ -45,10 +47,10 @@ export async function handleEntityRename( try { const newFilePath = (folder ? `${folder}/` : "/") + newName; - await renameFileArtifact(instanceId, existingPath, newFilePath); + await renameFileArtifact(client, existingPath, newFilePath); return `/files/${removeLeadingSlash(newFilePath)}`; } catch (err) { - console.error(err.response?.data?.message ?? err); + console.error(extractErrorMessage(err)); } } diff --git a/web-common/src/features/explore-mappers/explore-validation.ts b/web-common/src/features/explore-mappers/explore-validation.ts index 0c7d0767959..09453ed1d2f 100644 --- a/web-common/src/features/explore-mappers/explore-validation.ts +++ b/web-common/src/features/explore-mappers/explore-validation.ts @@ -1,5 +1,6 @@ import { useGetExploresForMetricsView } from "@rilldata/web-common/features/dashboards/selectors"; import type { V1Resource } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { derived, type Readable } from "svelte/store"; import type { DashboardSelectionCriteria, @@ -11,13 +12,10 @@ import type { * Validates if explore dashboards are available for the given metrics view */ export function useExploreAvailability( - instanceId: string, + client: RuntimeClient, metricsViewName: string, ): Readable { - const exploresQuery = useGetExploresForMetricsView( - instanceId, - metricsViewName, - ); + const exploresQuery = useGetExploresForMetricsView(client, metricsViewName); return derived(exploresQuery, (data) => { if (data.error) { diff --git a/web-common/src/features/explore-mappers/generate-explore-link.ts b/web-common/src/features/explore-mappers/generate-explore-link.ts index 006403f0849..65946b1c325 100644 --- a/web-common/src/features/explore-mappers/generate-explore-link.ts +++ b/web-common/src/features/explore-mappers/generate-explore-link.ts @@ -2,12 +2,14 @@ import type { ExploreState } from "@rilldata/web-common/features/dashboards/stor import { createLinkError } from "@rilldata/web-common/features/explore-mappers/explore-validation"; import { ExploreLinkErrorType } from "@rilldata/web-common/features/explore-mappers/types"; import { getExplorePageUrlSearchParams } from "@rilldata/web-common/features/explore-mappers/utils"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { EmbedStore } from "@rilldata/web-common/features/embeds/embed-store.ts"; /** * Generates the explore page URL with proper search parameters */ export async function generateExploreLink( + client: RuntimeClient, exploreState: Partial, exploreName: string, organization?: string | undefined, @@ -19,6 +21,7 @@ export async function generateExploreLink( // Generate search parameters from explore state const searchParams = await getExplorePageUrlSearchParams( + client, exploreName, exploreState, ); diff --git a/web-common/src/features/explore-mappers/get-dashboard-from-aggregation-request.spec.ts b/web-common/src/features/explore-mappers/get-dashboard-from-aggregation-request.spec.ts index 66622417b15..0a5775533c5 100644 --- a/web-common/src/features/explore-mappers/get-dashboard-from-aggregation-request.spec.ts +++ b/web-common/src/features/explore-mappers/get-dashboard-from-aggregation-request.spec.ts @@ -32,6 +32,7 @@ import { V1Operation, V1TimeGrain, } from "@rilldata/web-common/runtime-client"; +import { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { beforeEach, describe, expect, it } from "vitest"; describe("getDashboardFromAggregationRequest", () => { @@ -339,7 +340,12 @@ async function runTest({ ignoreFilters: boolean; forceOpenPivot: boolean; }) { + const mockClient = new RuntimeClient({ + host: "http://localhost:9009", + instanceId: "default", + }); const mapQueryStore = mapQueryToDashboard( + mockClient, { exploreName: AD_BIDS_EXPLORE_NAME, queryName: "MetricsViewAggregation", diff --git a/web-common/src/features/explore-mappers/get-dashboard-from-aggregation-request.ts b/web-common/src/features/explore-mappers/get-dashboard-from-aggregation-request.ts index 7e1cb9cee10..8cb4e50cdbf 100644 --- a/web-common/src/features/explore-mappers/get-dashboard-from-aggregation-request.ts +++ b/web-common/src/features/explore-mappers/get-dashboard-from-aggregation-request.ts @@ -51,7 +51,7 @@ import type { SortingState } from "@tanstack/svelte-table"; export async function getDashboardFromAggregationRequest({ queryClient, - instanceId, + client, req, dashboard, timeRangeSummary, @@ -66,7 +66,6 @@ export async function getDashboardFromAggregationRequest({ if (exploreProtoState) { await mergeDashboardFromUrlState( queryClient, - instanceId, dashboard, metricsView, explore, @@ -76,6 +75,7 @@ export async function getDashboardFromAggregationRequest({ } await fillTimeRange( + client, explore, dashboard, req.timeRange, @@ -104,7 +104,7 @@ export async function getDashboardFromAggregationRequest({ // We do not support comparison based dimension threshold filter in dashboards right now. // So convert it to a toplist and add `in` filter. const expr = await convertQueryFilterToToplistQuery( - instanceId, + client, explore.metricsView ?? "", req, dimension, @@ -212,7 +212,6 @@ function exprHasComparison(expr: V1Expression) { async function mergeDashboardFromUrlState( queryClient: QueryClient, - instanceId: string, exploreState: ExploreState, metricsViewSpec: V1MetricsViewSpec, exploreSpec: V1ExploreSpec, diff --git a/web-common/src/features/explore-mappers/get-dashboard-from-comparison-request.ts b/web-common/src/features/explore-mappers/get-dashboard-from-comparison-request.ts index 75814882508..1d242fc77c8 100644 --- a/web-common/src/features/explore-mappers/get-dashboard-from-comparison-request.ts +++ b/web-common/src/features/explore-mappers/get-dashboard-from-comparison-request.ts @@ -9,6 +9,7 @@ import { } from "@rilldata/web-common/runtime-client"; export async function getDashboardFromComparisonRequest({ + client, req, dashboard, metricsView, @@ -19,6 +20,7 @@ export async function getDashboardFromComparisonRequest({ if (req.where) dashboard.whereFilter = req.where; await fillTimeRange( + client, explore, dashboard, req.timeRange, diff --git a/web-common/src/features/explore-mappers/get-mapped-explore-url.ts b/web-common/src/features/explore-mappers/get-mapped-explore-url.ts index c0b6c2a1d41..183ee3e3c2f 100644 --- a/web-common/src/features/explore-mappers/get-mapped-explore-url.ts +++ b/web-common/src/features/explore-mappers/get-mapped-explore-url.ts @@ -8,11 +8,12 @@ import { mapQueryToDashboard, } from "@rilldata/web-common/features/explore-mappers/map-to-explore.ts"; import { useExploreValidSpec } from "@rilldata/web-common/features/explores/selectors.ts"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; import { derived, readable } from "svelte/store"; export type MapExploreUrlContext = { - instanceId: string; + client: RuntimeClient; organization: string; project: string; token?: string; @@ -26,7 +27,7 @@ export type MapExploreUrlContext = { export function getMappedExploreUrl( req: MapQueryRequest, // Request object passed directly to mapQueryToDashboard opts: MapQueryStateOptions, // Map options passed directly to mapQueryToDashboard - { instanceId, organization, project, token }: MapExploreUrlContext, + { client, organization, project, token }: MapExploreUrlContext, ) { if (!req.queryArgsJson) return readable(""); const queryRequestProperties = JSON.parse(req.queryArgsJson); @@ -37,14 +38,9 @@ export function getMappedExploreUrl( return derived( [ - useExploreValidSpec(instanceId, req.exploreName, undefined, queryClient), - useMetricsViewTimeRange( - instanceId, - metricsViewName, - undefined, - queryClient, - ), - mapQueryToDashboard(req, opts), + useExploreValidSpec(client, req.exploreName, undefined, queryClient), + useMetricsViewTimeRange(client, metricsViewName, undefined, queryClient), + mapQueryToDashboard(client, req, opts), page, ], ([validSpecResp, timeRangeSummaryResp, dashboardState, pageState]) => { diff --git a/web-common/src/features/explore-mappers/map-to-explore.ts b/web-common/src/features/explore-mappers/map-to-explore.ts index 64c2847e69b..89b85350471 100644 --- a/web-common/src/features/explore-mappers/map-to-explore.ts +++ b/web-common/src/features/explore-mappers/map-to-explore.ts @@ -16,8 +16,8 @@ import { type V1MetricsViewAggregationRequest, type V1MetricsViewComparisonRequest, } from "@rilldata/web-common/runtime-client"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; -import { derived, get, readable, type Readable } from "svelte/store"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; +import { derived, readable, type Readable } from "svelte/store"; export type MapQueryRequest = { exploreName: string; @@ -44,6 +44,7 @@ export type MapQueryResponse = { * Used to show the relevant dashboard for a report/alert. */ export function mapQueryToDashboard( + client: RuntimeClient, { exploreName, queryName, queryArgsJson, executionTime }: MapQueryRequest, { exploreProtoState, @@ -103,16 +104,13 @@ export function mapQueryToDashboard( // backwards compatibility for older alerts created on metrics explore directly if (!exploreName) exploreName = metricsViewName; - const instanceId = get(runtime).instanceId; - return derived( [ - useExploreValidSpec(instanceId, exploreName, undefined, queryClient), + useExploreValidSpec(client, exploreName, undefined, queryClient), // TODO: handle non-timestamp dashboards createQueryServiceMetricsViewTimeRange( - get(runtime).instanceId, - metricsViewName, - {}, + client, + { metricsViewName }, undefined, queryClient, ), @@ -132,8 +130,7 @@ export function mapQueryToDashboard( isFetching: false, isLoading: false, error: new Error( - validSpecResp.error?.response?.data?.message ?? - timeRangeSummary.error?.response?.data?.message, + validSpecResp.error?.message ?? timeRangeSummary.error?.message, ), }); return; @@ -181,7 +178,7 @@ export function mapQueryToDashboard( }; getDashboardState({ queryClient, - instanceId, + client, dashboard: defaultExploreState, req: queryRequestProperties, metricsView, diff --git a/web-common/src/features/explore-mappers/open-query.ts b/web-common/src/features/explore-mappers/open-query.ts index b795d56bee2..5543633ff69 100644 --- a/web-common/src/features/explore-mappers/open-query.ts +++ b/web-common/src/features/explore-mappers/open-query.ts @@ -7,16 +7,16 @@ import { getQueryServiceMetricsViewTimeRangeQueryKey, getRuntimeServiceGetExploreQueryKey, getRuntimeServiceListResourcesQueryKey, + queryServiceMetricsViewTimeRange, + runtimeServiceGetExplore, + runtimeServiceListResources, type V1ExploreSpec, - type V1GetExploreResponse, - type V1ListResourcesResponse, type V1MetricsViewSpec, type V1MetricsViewTimeRangeResponse, } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import type { Schema as MetricsResolverQuery } from "@rilldata/web-common/runtime-client/gen/resolvers/metrics/schema.ts"; import { error, redirect } from "@sveltejs/kit"; -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"; @@ -26,10 +26,10 @@ export async function openQuery({ query, organization, project, - runtime, + client, }: { query: MetricsResolverQuery; - runtime: Runtime; + client: RuntimeClient; organization?: string; project?: string; }) { @@ -44,12 +44,12 @@ export async function openQuery({ // Find an explore dashboard that uses this metrics view const exploreName = await findExploreForMetricsView( - runtime, + client, metricsViewName, ); const { metricsViewSpec, exploreSpec } = await getExploreSpecs( - runtime, + client, exploreName, ); @@ -62,7 +62,7 @@ export async function openQuery({ // Generate the final explore URL exploreURL = await generateExploreLink( - runtime, + client, exploreState, metricsViewSpec, exploreSpec, @@ -90,30 +90,19 @@ export async function openQuery({ * TODO: try to find an explore that has as many measures/dimensions in the query */ async function findExploreForMetricsView( - runtime: Runtime, + client: RuntimeClient, metricsViewName: string, ): Promise { - // List all explore resources + const request = { kind: ResourceKind.Explore }; const exploreResources = await queryClient.fetchQuery({ - queryKey: getRuntimeServiceListResourcesQueryKey(runtime.instanceId, { - kind: ResourceKind.Explore, - }), + queryKey: getRuntimeServiceListResourcesQueryKey( + client.instanceId, + request, + ), queryFn: ({ signal }) => - 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, - }), + runtimeServiceListResources(client, request, { signal }), }); - // Look for an explore that references this metrics view if (exploreResources.resources) { for (const resource of exploreResources.resources) { if (resource.explore?.state?.validSpec?.metricsView === metricsViewName) { @@ -122,31 +111,17 @@ async function findExploreForMetricsView( } } - // If no explore found, throw an error throw new Error( `No explore dashboard found for metrics view: ${metricsViewName}`, ); } -async function getExploreSpecs(runtime: Runtime, exploreName: string) { - // Get explore and metrics view specs +async function getExploreSpecs(client: RuntimeClient, exploreName: string) { + const request = { name: exploreName }; const getExploreResponse = await queryClient.fetchQuery({ - queryKey: getRuntimeServiceGetExploreQueryKey(runtime.instanceId, { - name: exploreName, - }), + queryKey: getRuntimeServiceGetExploreQueryKey(client.instanceId, request), queryFn: ({ signal }) => - 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, - }), + runtimeServiceGetExplore(client, request, { signal }), }); const exploreResource = getExploreResponse.explore; const metricsViewResource = getExploreResponse.metricsView; @@ -169,7 +144,7 @@ async function getExploreSpecs(runtime: Runtime, exploreName: string) { * Generates the explore page URL with proper search parameters */ async function generateExploreLink( - runtime: Runtime, + client: RuntimeClient, exploreState: Partial, metricsViewSpec: V1MetricsViewSpec, exploreSpec: V1ExploreSpec, @@ -178,42 +153,24 @@ async function generateExploreLink( 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) { + const request = { 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, - {}, + client.instanceId, + request, ), + queryFn: ({ signal }) => + queryServiceMetricsViewTimeRange(client, request, { signal }), staleTime: Infinity, gcTime: Infinity, }); } - // This is just for an initial redirect. - // DashboardStateDataLoader will handle compression etc. during init - // So no need to use getCleanedUrlParamsForGoto const searchParams = convertPartialExploreStateToUrlParams( exploreSpec, metricsViewSpec, diff --git a/web-common/src/features/explore-mappers/types.ts b/web-common/src/features/explore-mappers/types.ts index 44278008df3..2d3c5871d80 100644 --- a/web-common/src/features/explore-mappers/types.ts +++ b/web-common/src/features/explore-mappers/types.ts @@ -9,6 +9,7 @@ import type { V1MetricsViewToplistRequest, V1TimeRangeSummary, } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import type { QueryClient } from "@tanstack/svelte-query"; export type QueryRequests = @@ -22,7 +23,7 @@ export type TransformerProperties = QueryRequests; export type TransformerArgs = { queryClient: QueryClient; - instanceId: string; + client: RuntimeClient; dashboard: ExploreState; req: R; metricsView: V1MetricsViewSpec; diff --git a/web-common/src/features/explore-mappers/utils.ts b/web-common/src/features/explore-mappers/utils.ts index d40bd5f389c..ffb4fbdd847 100644 --- a/web-common/src/features/explore-mappers/utils.ts +++ b/web-common/src/features/explore-mappers/utils.ts @@ -22,7 +22,6 @@ import { getQueryServiceMetricsViewTimeRangeQueryKey, getRuntimeServiceGetExploreQueryKey, queryServiceMetricsViewAggregation, - type QueryServiceMetricsViewAggregationBody, queryServiceMetricsViewTimeRange, runtimeServiceGetExplore, type V1ExploreSpec, @@ -32,9 +31,7 @@ import { type V1TimeRange, type V1TimeRangeSummary, } from "@rilldata/web-common/runtime-client"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; -import { get } from "svelte/store"; - +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; // We are manually sending in duration, offset and round to grain for previous complete ranges. // This is to map back that split const PreviousCompleteRangeReverseMap: Record = {}; @@ -46,6 +43,7 @@ for (const preset in PreviousCompleteRangeMap) { } export async function fillTimeRange( + client: RuntimeClient, exploreSpec: V1ExploreSpec, exploreState: ExploreState, reqTimeRange: V1TimeRange | undefined, @@ -102,6 +100,7 @@ export async function fillTimeRange( // Resolve time range overriding ref to `executionTime` and set to custom. // This keeps the time range consistent regardless of when the link is opened. [exploreState.selectedTimeRange] = await resolveTimeRanges( + client, exploreSpec, [exploreState.selectedTimeRange], exploreState.selectedTimezone, @@ -123,22 +122,21 @@ export function getExploreName(webOpenPath: string) { } export async function convertQueryFilterToToplistQuery( - instanceId: string, + client: RuntimeClient, metricsView: string, req: V1MetricsViewAggregationRequest, dimension: string, ) { - const params = { + const params = { + metricsViewName: metricsView, ...req, }; const toplist = await queryClient.fetchQuery({ queryKey: getQueryServiceMetricsViewAggregationQueryKey( - instanceId, - metricsView, + client.instanceId, params, ), - queryFn: () => - queryServiceMetricsViewAggregation(instanceId, metricsView, params), + queryFn: () => queryServiceMetricsViewAggregation(client, params), }); return createInExpression( dimension, @@ -147,20 +145,20 @@ export async function convertQueryFilterToToplistQuery( } export async function getExplorePageUrlSearchParams( + client: RuntimeClient, exploreName: string, exploreState: Partial, ): Promise { - const instanceId = get(runtime).instanceId; const { explore, metricsView } = await queryClient.fetchQuery({ queryFn: ({ signal }) => runtimeServiceGetExplore( - instanceId, + client, { name: exploreName, }, - signal, + { signal }, ), - queryKey: getRuntimeServiceGetExploreQueryKey(instanceId, { + queryKey: getRuntimeServiceGetExploreQueryKey(client.instanceId, { name: exploreName, }), // this loader function is run for every param change in url. @@ -179,12 +177,10 @@ export async function getExplorePageUrlSearchParams( ) { fullTimeRange = await queryClient.fetchQuery({ queryFn: () => - queryServiceMetricsViewTimeRange(instanceId, metricsViewName, {}), - queryKey: getQueryServiceMetricsViewTimeRangeQueryKey( - instanceId, + queryServiceMetricsViewTimeRange(client, { metricsViewName }), + queryKey: getQueryServiceMetricsViewTimeRangeQueryKey(client.instanceId, { metricsViewName, - {}, - ), + }), staleTime: Infinity, gcTime: Infinity, }); @@ -216,19 +212,17 @@ export async function getExplorePageUrlSearchParams( * @param exploreSpec */ export function maybeGetExplorePageUrlSearchParams( + instanceId: string, exploreState: Partial, metricsViewSpec: V1MetricsViewSpec, exploreSpec: V1ExploreSpec, ) { - const instanceId = get(runtime).instanceId; const metricsViewName = exploreSpec.metricsView ?? ""; const metricsViewTimeRangeQueryKey = - getQueryServiceMetricsViewTimeRangeQueryKey( - instanceId, + getQueryServiceMetricsViewTimeRangeQueryKey(instanceId, { metricsViewName, - {}, - ); + }); // Get time range query from cache if present, else we will go to `/-/open-query` to fetch it. const queryResp = queryClient.getQueryData( diff --git a/web-common/src/features/explores/ExploreMenuItems.svelte b/web-common/src/features/explores/ExploreMenuItems.svelte index 0cf9582a7db..658e5f4c504 100644 --- a/web-common/src/features/explores/ExploreMenuItems.svelte +++ b/web-common/src/features/explores/ExploreMenuItems.svelte @@ -1,7 +1,6 @@ diff --git a/web-common/src/features/explores/explore-link/ExploreLink.svelte b/web-common/src/features/explores/explore-link/ExploreLink.svelte index 6dd2b6f2170..e95c351dc63 100644 --- a/web-common/src/features/explores/explore-link/ExploreLink.svelte +++ b/web-common/src/features/explores/explore-link/ExploreLink.svelte @@ -12,6 +12,9 @@ } from "@rilldata/web-common/features/explore-mappers/types"; import { getErrorMessage } from "@rilldata/web-common/features/explore-mappers/utils"; import type { ExploreState } from "@rilldata/web-common/features/dashboards/stores/explore-state"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; + + const runtimeClient = useRuntimeClient(); export let exploreName: string; export let displayName: string | undefined = undefined; @@ -32,6 +35,7 @@ try { const exploreURL = await generateExploreLink( + runtimeClient, exploreState, exploreName, organization, diff --git a/web-common/src/features/explores/selectors.ts b/web-common/src/features/explores/selectors.ts index b1dc56c2e35..549f5b0a0be 100644 --- a/web-common/src/features/explores/selectors.ts +++ b/web-common/src/features/explores/selectors.ts @@ -1,4 +1,4 @@ -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store.ts"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { type CreateQueryOptions, type QueryFunction, @@ -9,30 +9,25 @@ import { createRuntimeServiceGetExplore, getRuntimeServiceGetExploreQueryKey, runtimeServiceGetExplore, - type RpcStatus, type V1ExploreSpec, type V1GetExploreResponse, type V1MetricsViewSpec, getRuntimeServiceGetExploreQueryOptions, } from "@rilldata/web-common/runtime-client"; -import type { ErrorType } from "@rilldata/web-common/runtime-client/http-client"; +import type { ConnectError } from "@connectrpc/connect"; import { error } from "@sveltejs/kit"; import { derived, type Readable } from "svelte/store"; export function useExplore( - instanceId: string, + client: RuntimeClient, exploreName: string, queryOptions?: Partial< - CreateQueryOptions< - V1GetExploreResponse, - ErrorType, - V1GetExploreResponse - > + CreateQueryOptions >, queryClient?: QueryClient, ) { return createRuntimeServiceGetExplore( - instanceId, + client, { name: exploreName }, { query: queryOptions, @@ -46,19 +41,19 @@ export type ExploreValidSpecResponse = { metricsView: V1MetricsViewSpec | undefined; }; export function useExploreValidSpec( - instanceId: string, + client: RuntimeClient, exploreName: string, queryOptions?: Partial< CreateQueryOptions< V1GetExploreResponse, - ErrorType, + ConnectError, ExploreValidSpecResponse > >, queryClient?: QueryClient, ) { return createRuntimeServiceGetExplore( - instanceId, + client, { name: exploreName }, { query: { @@ -77,11 +72,12 @@ export function useExploreValidSpec( } export function getExploreValidSpecQueryOptions( + client: RuntimeClient, exploreNameStore: Readable, ) { - return derived([runtime, exploreNameStore], ([{ instanceId }, exploreName]) => + return derived([exploreNameStore], ([exploreName]) => getRuntimeServiceGetExploreQueryOptions( - instanceId, + client, { name: exploreName, }, @@ -99,16 +95,19 @@ export function getExploreValidSpecQueryOptions( } export async function fetchExploreSpec( - instanceId: string, + client: RuntimeClient, exploreName: string, ) { const queryParams = { name: exploreName, }; - const queryKey = getRuntimeServiceGetExploreQueryKey(instanceId, queryParams); + const queryKey = getRuntimeServiceGetExploreQueryKey( + client.instanceId, + queryParams, + ); const queryFunction: QueryFunction< Awaited> - > = ({ signal }) => runtimeServiceGetExplore(instanceId, queryParams, signal); + > = ({ signal }) => runtimeServiceGetExplore(client, queryParams, { signal }); const response = await queryClient.fetchQuery({ queryFn: queryFunction, diff --git a/web-common/src/features/exports/ExportMenu.svelte b/web-common/src/features/exports/ExportMenu.svelte index 336fa54510e..00ba3b4598e 100644 --- a/web-common/src/features/exports/ExportMenu.svelte +++ b/web-common/src/features/exports/ExportMenu.svelte @@ -7,16 +7,17 @@ import TooltipContent from "@rilldata/web-common/components/tooltip/TooltipContent.svelte"; import { featureFlags } from "@rilldata/web-common/features/feature-flags"; import { - createQueryServiceExport, + createQueryServiceExportMutation, V1ExportFormat, type V1Query, } from "@rilldata/web-common/runtime-client"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { onMount } from "svelte"; - import { get } from "svelte/store"; - import { runtime } from "../../runtime-client/runtime-store"; import type TScheduledReportDialog from "../scheduled-reports/ScheduledReportDialog.svelte"; import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors"; + const runtimeClient = useRuntimeClient(); + export let disabled: boolean = false; export let workspace = false; export let label: string; @@ -37,7 +38,7 @@ scheduledReportQuery = getQuery(true); } - const exportDash = createQueryServiceExport(); + const exportDash = createQueryServiceExportMutation(runtimeClient); const { reports, adminServer, exportHeader } = featureFlags; async function handleExport(options: { @@ -46,20 +47,17 @@ }) { const { format, includeHeader = false } = options; const result = await $exportDash.mutateAsync({ - instanceId: get(runtime).instanceId, - data: { - query: exportQuery, - format, - includeHeader, - // Include metadata for CSV/XLSX exports in Cloud context. - ...(includeHeader && - $adminServer && { - originDashboard: { name: exploreName, kind: ResourceKind.Explore }, - origin_url: window.location.href, - }), - }, + query: exportQuery as any, + format: format as any, + includeHeader, + // Include metadata for CSV/XLSX exports in Cloud context. + ...(includeHeader && + $adminServer && { + originDashboard: { name: exploreName, kind: ResourceKind.Explore }, + originUrl: window.location.href, + }), }); - const downloadUrl = `${get(runtime).host}${result.downloadUrlPath}`; + const downloadUrl = `${runtimeClient.host}${result.downloadUrlPath}`; window.open(downloadUrl, "_self"); } diff --git a/web-common/src/features/feature-flags.ts b/web-common/src/features/feature-flags.ts index 584e1e8282c..4d0b5a2f06d 100644 --- a/web-common/src/features/feature-flags.ts +++ b/web-common/src/features/feature-flags.ts @@ -2,12 +2,10 @@ import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryCl import { writable } from "svelte/store"; import { createRuntimeServiceGetInstance, - getRuntimeServiceGetInstanceQueryKey, - type V1GetInstanceResponse, + runtimeServiceGetInstance, type V1InstanceFeatureFlags, } from "../runtime-client"; -import { type Runtime, runtime } from "../runtime-client/runtime-store"; -import httpClient from "@rilldata/web-common/runtime-client/http-client.ts"; +import type { RuntimeClient } from "../runtime-client/v2"; class FeatureFlag { private _internal = false; @@ -67,52 +65,58 @@ class FeatureFlags { deploy = new FeatureFlag("user", true); stickyDashboardState = new FeatureFlag("user", false); + private flagsUnsub?: () => void; + constructor() { this.ready = new Promise((resolve) => { this._resolveReady = resolve; }); + } - const updateFlags = (userFlags: V1InstanceFeatureFlags) => { - this._resolveReady(); - - // First, reset all user flags to their defaults - const userFlagKeys = Object.keys(this).filter((key) => { - const flag = this[key]; - return flag instanceof FeatureFlag && !flag.internalOnly; - }); + clearRuntimeClient() { + this.flagsUnsub?.(); + this.flagsUnsub = undefined; + } - for (const key of userFlagKeys) { - const flag = this[key] as FeatureFlag; - flag.resetToDefault(); - } - - // Then apply project-specific overrides - for (const key in userFlags) { - const flag = this[key] as FeatureFlag | undefined; - if (!flag || flag.internalOnly) continue; - flag.set(userFlags[key]); - } - }; + setRuntimeClient(client: RuntimeClient) { + this.flagsUnsub?.(); - // Responsively update flags based rill.yaml - runtime.subscribe((runtime) => { - if (!runtime?.instanceId) return; - - createRuntimeServiceGetInstance( - runtime.instanceId, - undefined, - { - query: { - select: (data) => data?.instance?.featureFlags, - }, + this.flagsUnsub = createRuntimeServiceGetInstance( + client, + {}, + { + query: { + select: (data) => data?.instance?.featureFlags, }, - queryClient, - ).subscribe((features) => { - if (features.data) updateFlags(features.data); - }); + }, + queryClient, + ).subscribe((features) => { + if (features.data) this.updateFlags(features.data); }); } + private updateFlags(userFlags: V1InstanceFeatureFlags) { + this._resolveReady(); + + // First, reset all user flags to their defaults + const userFlagKeys = Object.keys(this).filter((key) => { + const flag = this[key]; + return flag instanceof FeatureFlag && !flag.internalOnly; + }); + + for (const key of userFlagKeys) { + const flag = this[key] as FeatureFlag; + flag.resetToDefault(); + } + + // Then apply project-specific overrides + for (const key in userFlags) { + const flag = this[key] as FeatureFlag | undefined; + if (!flag || flag.internalOnly) continue; + flag.set(userFlags[key]); + } + } + get set() { return (bool: boolean, ...toggleFlags: FeatureFlagKey[]) => { toggleFlags.forEach((n) => { @@ -126,24 +130,13 @@ 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 ?? {}; +export async function getFeatureFlags( + client: RuntimeClient, +): Promise { + try { + const data = await runtimeServiceGetInstance(client, {}); + return data.instance?.featureFlags ?? {}; + } catch { + return {}; + } } diff --git a/web-common/src/features/file-explorer/FileExplorer.svelte b/web-common/src/features/file-explorer/FileExplorer.svelte index 73f1543af99..215154593ce 100644 --- a/web-common/src/features/file-explorer/FileExplorer.svelte +++ b/web-common/src/features/file-explorer/FileExplorer.svelte @@ -18,7 +18,7 @@ import { PROTECTED_DIRECTORIES } from "@rilldata/web-common/features/file-explorer/protected-paths"; import { isCurrentActivePage } from "@rilldata/web-common/features/file-explorer/utils"; import { createRuntimeServiceListFiles } from "@rilldata/web-common/runtime-client"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { eventBus } from "../../lib/event-bus/event-bus"; import { fileArtifacts } from "../entity-management/file-artifacts"; import NavDirectory from "./NavDirectory.svelte"; @@ -27,36 +27,41 @@ export let hasUnsaved: boolean; - $: ({ instanceId } = $runtime); - $: getFileTree = createRuntimeServiceListFiles(instanceId, undefined, { - query: { - select: (data) => { - if (!data || !data.files?.length) return; - - const files = data.files - // Sort alphabetically case-insensitive - .sort( - (a, b) => - a.path?.localeCompare(b.path ?? "", undefined, { - sensitivity: "base", - }) ?? 0, - ) - // Hide dot directories - .filter( - (file) => - !( - file.isDir && - // Check both the top-level directory and subdirectories - (file.path?.startsWith(".") || file.path?.includes("/.")) - ), - ) - // Hide the `tmp` directory - .filter((file) => !file.path?.startsWith("/tmp")); - - return transformFileList(files); + const runtimeClient = useRuntimeClient(); + + $: getFileTree = createRuntimeServiceListFiles( + runtimeClient, + {}, + { + query: { + select: (data) => { + if (!data || !data.files?.length) return; + + const files = data.files + // Sort alphabetically case-insensitive + .sort( + (a, b) => + a.path?.localeCompare(b.path ?? "", undefined, { + sensitivity: "base", + }) ?? 0, + ) + // Hide dot directories + .filter( + (file) => + !( + file.isDir && + // Check both the top-level directory and subdirectories + (file.path?.startsWith(".") || file.path?.includes("/.")) + ), + ) + // Hide the `tmp` directory + .filter((file) => !file.path?.startsWith("/tmp")); + + return transformFileList(files); + }, }, }, - }); + ); $: ({ data: fileTree } = $getFileTree); @@ -76,7 +81,7 @@ } try { - const newFilePath = await duplicateFileArtifact(instanceId, filePath); + const newFilePath = await duplicateFileArtifact(runtimeClient, filePath); await goto(`/files${newFilePath}`); } catch { eventBus.emit("notification", { @@ -99,14 +104,14 @@ return; } } - await deleteFileArtifact(instanceId, filePath); + await deleteFileArtifact(runtimeClient, filePath); if (isCurrentActivePage(filePath, isDir)) { await goto("/"); } } async function onForceDelete() { - await deleteFileArtifact(instanceId, forceDeletePath, true); + await deleteFileArtifact(runtimeClient, forceDeletePath, true); // onForceDelete is only called on folders, so isDir is always true if (isCurrentActivePage(forceDeletePath, true)) { await goto("/"); @@ -130,7 +135,7 @@ }); return; } - await renameFileArtifact(instanceId, fromPath, newFilePath); + await renameFileArtifact(runtimeClient, fromPath, newFilePath); if (isCurrentFile) { await goto(`/files${newFilePath}`); diff --git a/web-common/src/features/file-explorer/NavDirectoryEntry.svelte b/web-common/src/features/file-explorer/NavDirectoryEntry.svelte index 931dd9f1151..e1f0396ec14 100644 --- a/web-common/src/features/file-explorer/NavDirectoryEntry.svelte +++ b/web-common/src/features/file-explorer/NavDirectoryEntry.svelte @@ -15,8 +15,8 @@ import NavigationMenuItem from "@rilldata/web-common/layout/navigation/NavigationMenuItem.svelte"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; import { Folder } from "lucide-svelte"; - import { createRuntimeServiceCreateDirectory } from "../../runtime-client"; - import { runtime } from "../../runtime-client/runtime-store"; + import { createRuntimeServiceCreateDirectoryMutation } from "../../runtime-client"; + import { useRuntimeClient } from "../../runtime-client/v2"; import { removeLeadingSlash } from "../entity-management/entity-mappers"; import { getTopLevelFolder } from "../entity-management/file-path-utils"; import { useDirectoryNamesInDirectory } from "../entity-management/file-selectors"; @@ -28,21 +28,24 @@ export let onDelete: (filePath: string, isDir: boolean) => void; export let onMouseDown: (e: MouseEvent, dragData: NavDragData) => void; + const runtimeClient = useRuntimeClient(); + let contextMenuOpen = false; - const createFolder = createRuntimeServiceCreateDirectory(); + const createFolder = + createRuntimeServiceCreateDirectoryMutation(runtimeClient); $: id = `${dir.path}-nav-entry`; $: expanded = $directoryState[dir.path]; $: padding = getPaddingFromPath(dir.path); - $: ({ instanceId } = $runtime); + $: ({ instanceId } = runtimeClient); $: topLevelFolder = getTopLevelFolder(dir.path); $: isProtectedDirectory = PROTECTED_DIRECTORIES.includes(topLevelFolder); $: hasErrors = getDirectoryHasErrors(queryClient, instanceId, dir); $: currentDirectoryDirectoryNamesQuery = useDirectoryNamesInDirectory( - instanceId, + runtimeClient, dir.path, ); @@ -65,10 +68,7 @@ : nextFolderName; await $createFolder.mutateAsync({ - instanceId: instanceId, - data: { - path: path, - }, + path: path, }); // Expand the directory to show the new folder diff --git a/web-common/src/features/file-explorer/NavFile.svelte b/web-common/src/features/file-explorer/NavFile.svelte index 22fd13b2aab..9ef9f63d998 100644 --- a/web-common/src/features/file-explorer/NavFile.svelte +++ b/web-common/src/features/file-explorer/NavFile.svelte @@ -22,7 +22,6 @@ ResourceKindToScreenMap, } from "@rilldata/web-common/metrics/service/MetricsTypes"; import type { V1ResourceName } from "@rilldata/web-common/runtime-client"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import { Save } from "lucide-svelte"; import type { Readable } from "svelte/store"; import CopyIcon from "../../components/icons/CopyIcon.svelte"; @@ -61,8 +60,6 @@ saveState: { saving, error }, } = fileArtifact); - $: ({ instanceId } = $runtime); - $: resourceKind = ($resourceName?.kind ?? $inferredResourceKind) as ResourceKind; $: padding = getPaddingFromPath(filePath); @@ -71,7 +68,7 @@ $: isDotFile = fileName && fileName.startsWith("."); $: isProtectedFile = PROTECTED_FILES.includes(filePath); - $: hasErrors = fileArtifact.getHasErrors(queryClient, instanceId); + $: hasErrors = fileArtifact.getHasErrors(queryClient); function fireTelemetry() { const previousScreenName = getScreenNameFromPage(); diff --git a/web-common/src/features/file-explorer/new-files.ts b/web-common/src/features/file-explorer/new-files.ts index e19b748b08c..1d300dd8b0b 100644 --- a/web-common/src/features/file-explorer/new-files.ts +++ b/web-common/src/features/file-explorer/new-files.ts @@ -9,8 +9,7 @@ import { runtimeServicePutFile, type V1Resource, } from "@rilldata/web-common/runtime-client"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; -import { get } from "svelte/store"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { getScreenNameFromPage } from "@rilldata/web-common/features/file-explorer/telemetry.ts"; import { behaviourEvent } from "@rilldata/web-common/metrics/initMetrics.ts"; import { @@ -20,10 +19,11 @@ import { import { MetricsEventSpace } from "@rilldata/web-common/metrics/service/MetricsTypes.ts"; export async function createResourceAndNavigate( + client: RuntimeClient, kind: ResourceKind, baseResource?: V1Resource, ) { - const filePath = await createResourceFile(kind, baseResource); + const filePath = await createResourceFile(client, kind, baseResource); if (!filePath) return; const previousScreenName = getScreenNameFromPage(); @@ -37,6 +37,7 @@ export async function createResourceAndNavigate( } export async function createResourceFile( + client: RuntimeClient, kind: ResourceKind, baseResource?: V1Resource, ): Promise { @@ -45,9 +46,7 @@ export async function createResourceFile( } const newPath = getPathForNewResourceFile(kind, baseResource); - const instanceId = get(runtime).instanceId; - - await runtimeServicePutFile(instanceId, { + await runtimeServicePutFile(client, { path: newPath, blob: generateBlobForNewResourceFile(kind, baseResource), create: true, diff --git a/web-common/src/features/file-explorer/transform-file-list.ts b/web-common/src/features/file-explorer/transform-file-list.ts index 1b9b1da8ffa..44fbfe4b385 100644 --- a/web-common/src/features/file-explorer/transform-file-list.ts +++ b/web-common/src/features/file-explorer/transform-file-list.ts @@ -82,9 +82,7 @@ export function getDirectoryHasErrors( ) { return derived( flattenDirectory(dir).map((filePath) => - fileArtifacts - .getFileArtifact(filePath) - .getAllErrors(queryClient, instanceId), + fileArtifacts.getFileArtifact(filePath).getAllErrors(queryClient), ), (filesErrors) => filesErrors.some((fileErrors) => fileErrors.length > 0), ); diff --git a/web-common/src/features/metrics-views/GoToDashboardButton.svelte b/web-common/src/features/metrics-views/GoToDashboardButton.svelte index ce26c634ed5..14efc0fd93a 100644 --- a/web-common/src/features/metrics-views/GoToDashboardButton.svelte +++ b/web-common/src/features/metrics-views/GoToDashboardButton.svelte @@ -7,7 +7,7 @@ import { featureFlags } from "@rilldata/web-common/features/feature-flags"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; import type { V1Resource } from "@rilldata/web-common/runtime-client"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { useGetExploresForMetricsView } from "../dashboards/selectors"; import { allowPrimary } from "../dashboards/workspace/DeployProjectCTA.svelte"; import { @@ -19,11 +19,12 @@ export let resource: V1Resource | undefined; + const runtimeClient = useRuntimeClient(); const { ai, developerChat } = featureFlags; - $: ({ instanceId } = $runtime); + $: ({ instanceId } = runtimeClient); $: dashboardsQuery = useGetExploresForMetricsView( - instanceId, + runtimeClient, resource?.meta?.name?.name ?? "", ); $: dashboards = $dashboardsQuery.data ?? []; @@ -39,12 +40,12 @@ // Use developer agent if enabled, otherwise fall back to RPC if ($developerChat) { createCanvasDashboardFromMetricsViewWithAgent( - instanceId, + runtimeClient, resource.meta.name.name, ); } else { await createCanvasDashboardFromMetricsView( - instanceId, + runtimeClient, resource.meta.name.name, ); } @@ -58,7 +59,12 @@ disabled={!resource} onClick={async () => { if (resource) - await createAndPreviewExplore(queryClient, instanceId, resource); + await createAndPreviewExplore( + runtimeClient, + queryClient, + instanceId, + resource, + ); }} > Generate Explore Dashboard{$ai ? " with AI" : ""} @@ -90,12 +96,12 @@ // Use developer agent if enabled, otherwise fall back to RPC if ($developerChat) { createCanvasDashboardFromMetricsViewWithAgent( - instanceId, + runtimeClient, resource.meta.name.name, ); } else { await createCanvasDashboardFromMetricsView( - instanceId, + runtimeClient, resource.meta.name.name, ); } @@ -108,7 +114,12 @@ { if (resource) - await createAndPreviewExplore(queryClient, instanceId, resource); + await createAndPreviewExplore( + runtimeClient, + queryClient, + instanceId, + resource, + ); }} > diff --git a/web-common/src/features/metrics-views/MetricsInspector.svelte b/web-common/src/features/metrics-views/MetricsInspector.svelte index 71385c18f19..2eb579ff71f 100644 --- a/web-common/src/features/metrics-views/MetricsInspector.svelte +++ b/web-common/src/features/metrics-views/MetricsInspector.svelte @@ -1,5 +1,6 @@ @@ -118,7 +122,12 @@ {#if resource} - createAndPreviewExplore(queryClient, instanceId, resource)} + createAndPreviewExplore( + runtimeClient, + queryClient, + instanceId, + resource, + )} >
    diff --git a/web-common/src/features/metrics-views/ai-generation/generateMetricsView.ts b/web-common/src/features/metrics-views/ai-generation/generateMetricsView.ts index b8a85e40508..09919966bf1 100644 --- a/web-common/src/features/metrics-views/ai-generation/generateMetricsView.ts +++ b/web-common/src/features/metrics-views/ai-generation/generateMetricsView.ts @@ -7,7 +7,9 @@ import { resourceIsLoading, } from "@rilldata/web-common/features/entity-management/resource-selectors"; import { createResourceFile } from "@rilldata/web-common/features/file-explorer/new-files"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { getScreenNameFromPage } from "@rilldata/web-common/features/file-explorer/telemetry"; +import { extractErrorMessage } from "@rilldata/web-common/lib/errors"; import { eventBus } from "@rilldata/web-common/lib/event-bus/event-bus"; import type { QueryClient } from "@tanstack/svelte-query"; import { get } from "svelte/store"; @@ -20,14 +22,15 @@ import { MetricsEventScreenName, MetricsEventSpace, } from "../../../metrics/service/MetricsTypes"; +import type { + RuntimeServiceGenerateMetricsViewFileBody, + V1GenerateMetricsViewFileResponse, + V1Resource, +} from "../../../runtime-client"; import { runtimeServiceGenerateMetricsViewFile, runtimeServiceGetFile, - type RuntimeServiceGenerateMetricsViewFileBody, - type V1GenerateMetricsViewFileResponse, - type V1Resource, -} from "../../../runtime-client"; -import httpClient from "../../../runtime-client/http-client"; +} from "@rilldata/web-common/runtime-client"; import { createYamlModelFromTable } from "../../connectors/code-utils"; import { getName } from "../../entity-management/name-utils"; import { featureFlags } from "../../feature-flags"; @@ -40,17 +43,22 @@ import OptionToCancelAIGeneration from "./OptionToCancelAIGeneration.svelte"; * AbortSignal, which we can use to cancel the request. */ const runtimeServiceGenerateMetricsViewFileWithSignal = ( - instanceId: string, - runtimeServiceGenerateMetricsViewFileBody: RuntimeServiceGenerateMetricsViewFileBody, + client: RuntimeClient, + body: RuntimeServiceGenerateMetricsViewFileBody, signal: AbortSignal, -) => { - return httpClient({ - url: `/v1/instances/${instanceId}/files/generate-metrics-view`, - method: "post", - headers: { "Content-Type": "application/json" }, - data: runtimeServiceGenerateMetricsViewFileBody, +): Promise => { + const url = `${client.host}/v1/instances/${client.instanceId}/files/generate-metrics-view`; + const headers: Record = { + "Content-Type": "application/json", + }; + const jwt = client.getJwt(); + if (jwt) headers["Authorization"] = `Bearer ${jwt}`; + return fetch(url, { + method: "POST", + headers, + body: JSON.stringify(body), signal, - }); + }).then((r) => r.json()); }; /** @@ -59,6 +67,7 @@ const runtimeServiceGenerateMetricsViewFileWithSignal = ( * This function is to be called from all `Generate dashboard with AI` CTAs *outside* of the Metrics Editor. */ export function useCreateMetricsViewFromTableUIAction( + client: RuntimeClient, instanceId: string, connector: string, database: string, @@ -96,7 +105,7 @@ export function useCreateMetricsViewFromTableUIAction( try { // First, request an AI-generated metrics view void runtimeServiceGenerateMetricsViewFileWithSignal( - instanceId, + client, { connector: connector, database: database, @@ -110,14 +119,14 @@ export function useCreateMetricsViewFromTableUIAction( // Poll until file creation is complete or canceled const fileCreated = await pollForFileCreation( - instanceId, + client, newMetricsViewFilePath, abortController.signal, ); // If the user canceled the AI request, submit another request with `useAi=false` if (!fileCreated) { - await runtimeServiceGenerateMetricsViewFile(instanceId, { + await runtimeServiceGenerateMetricsViewFile(client, { connector: connector, database: database, databaseSchema: databaseSchema, @@ -148,7 +157,7 @@ export function useCreateMetricsViewFromTableUIAction( // Get the Metrics View to use as a base for the Explore const metricsViewResource = fileArtifacts .getFileArtifact(newMetricsViewFilePath) - .getResource(queryClient, instanceId); + .getResource(queryClient); await waitUntil(() => get(metricsViewResource).data !== undefined, 5000); @@ -158,13 +167,13 @@ export function useCreateMetricsViewFromTableUIAction( } // Create the Explore file, and navigate to it - await createAndPreviewExplore(queryClient, instanceId, resource); + await createAndPreviewExplore(client, queryClient, instanceId, resource); } catch (err) { eventBus.emit("notification", { message: `Failed to create ${createExplore ? "a dashboard" : "metrics"} for ` + tableName, - detail: err.response?.data?.message ?? err.message, + detail: extractErrorMessage(err), }); } @@ -179,7 +188,7 @@ export function useCreateMetricsViewFromTableUIAction( * This function is to be called from the `Generate dashboard with AI` CTA *inside* of the Metrics Editor. */ export async function createDashboardFromTableInMetricsEditor( - instanceId: string, + client: RuntimeClient, modelName: string, filePath: string, ) { @@ -205,7 +214,7 @@ export async function createDashboardFromTableInMetricsEditor( try { // First, request an AI-generated dashboard void runtimeServiceGenerateMetricsViewFileWithSignal( - instanceId, + client, { table: tableName, path: filePath, @@ -219,7 +228,7 @@ export async function createDashboardFromTableInMetricsEditor( await new Promise((resolve) => setTimeout(resolve, 1000)); try { - const file = await runtimeServiceGetFile(instanceId, { + const file = await runtimeServiceGetFile(client, { path: filePath, }); if (file.blob !== "") { @@ -233,7 +242,7 @@ export async function createDashboardFromTableInMetricsEditor( // If the user canceled the AI request, submit another request with `useAi=false` if (isAICancelled) { - await runtimeServiceGenerateMetricsViewFile(instanceId, { + await runtimeServiceGenerateMetricsViewFile(client, { table: tableName, path: filePath, useAi: false, @@ -242,7 +251,7 @@ export async function createDashboardFromTableInMetricsEditor( } catch (err) { eventBus.emit("notification", { message: "Failed to create a dashboard for " + tableName, - detail: err.response?.data?.message ?? err.message, + detail: extractErrorMessage(err), }); } @@ -255,6 +264,7 @@ export async function createDashboardFromTableInMetricsEditor( * Handles both OLAP and non-OLAP connectors with appropriate logic for each case. */ export async function generateMetricsFromTable( + client: RuntimeClient, instanceId: string, connector: string, database: string, @@ -268,6 +278,7 @@ export async function generateMetricsFromTable( if (isOlapConnector) { // For OLAP connectors, use direct metrics view generation const createMetricsViewFromTable = useCreateMetricsViewFromTableUIAction( + client, instanceId, connector, database, @@ -281,6 +292,7 @@ export async function generateMetricsFromTable( } else { // For non-OLAP connectors, follow Rill architecture: Model → Metrics → (Optional) Explore await createModelAndMetricsAndExplore( + client, instanceId, connector, database, @@ -299,6 +311,7 @@ export async function generateMetricsFromTable( * 3. Optionally create explore dashboard (on top of metrics view) */ export async function createModelAndMetricsAndExplore( + client: RuntimeClient, instanceId: string, connector: string, database: string, @@ -339,6 +352,7 @@ export async function createModelAndMetricsAndExplore( }); const [, modelName] = await createYamlModelFromTable( + client, queryClient, connector, database, @@ -349,7 +363,7 @@ export async function createModelAndMetricsAndExplore( // Step 2: Wait for model to be ready const modelResource = fileArtifacts .getFileArtifact(`/models/${modelName}.yaml`) - .getResource(queryClient, instanceId); + .getResource(queryClient); await waitUntil(() => get(modelResource).data !== undefined, 10000); @@ -378,22 +392,18 @@ export async function createModelAndMetricsAndExplore( }); // Use the backend function with the model name instead of table name - void runtimeServiceGenerateMetricsViewFile( - instanceId, - { - model: modelName, // Use model name instead of table - path: metricsViewFilePath, - useAi: get(featureFlags.ai), - }, - abortController.signal, - ); + void runtimeServiceGenerateMetricsViewFile(client, { + model: modelName, // Use model name instead of table + path: metricsViewFilePath, + useAi: get(featureFlags.ai), + }); // Poll every second until the AI generation is complete or canceled while (!isAICancelled) { await new Promise((resolve) => setTimeout(resolve, 1000)); try { - await runtimeServiceGetFile(instanceId, { + await runtimeServiceGetFile(client, { path: metricsViewFilePath, }); @@ -406,7 +416,7 @@ export async function createModelAndMetricsAndExplore( // If the user canceled the AI request, submit another request with `useAi=false` if (isAICancelled) { - await runtimeServiceGenerateMetricsViewFile(instanceId, { + await runtimeServiceGenerateMetricsViewFile(client, { model: modelName, path: metricsViewFilePath, useAi: false, @@ -416,7 +426,7 @@ export async function createModelAndMetricsAndExplore( // Step 4: Wait for metrics view to be ready const metricsViewResource = fileArtifacts .getFileArtifact(metricsViewFilePath) - .getResource(queryClient, instanceId); + .getResource(queryClient); await waitUntil(() => get(metricsViewResource).data !== undefined, 10000); @@ -458,7 +468,7 @@ export async function createModelAndMetricsAndExplore( }); // Step 5: Create explore dashboard - await createAndPreviewExplore(queryClient, instanceId, resource); + await createAndPreviewExplore(client, queryClient, instanceId, resource); } catch (err) { console.error("Failed to create model and metrics view:", err); throw err; @@ -473,13 +483,12 @@ export async function createModelAndMetricsAndExplore( * Reconciliation is complete when the status is IDLE. */ async function waitForMetricsViewReconciliation( - instanceId: string, metricsViewFilePath: string, timeoutMs: number = 10000, ): Promise { const metricsViewResource = fileArtifacts .getFileArtifact(metricsViewFilePath) - .getResource(queryClient, instanceId); + .getResource(queryClient); // Wait for the resource to be fully reconciled await waitUntil(() => { @@ -493,7 +502,7 @@ async function waitForMetricsViewReconciliation( * Returns the metrics view resource after creation. */ async function createMetricsViewFromTable( - instanceId: string, + client: RuntimeClient, connector: string, database: string, databaseSchema: string, @@ -510,7 +519,7 @@ async function createMetricsViewFromTable( // Request an AI-generated metrics view void runtimeServiceGenerateMetricsViewFileWithSignal( - instanceId, + client, { connector: connector, database: database, @@ -524,14 +533,14 @@ async function createMetricsViewFromTable( // Poll until file creation is complete or canceled const fileCreated = await pollForFileCreation( - instanceId, + client, newMetricsViewFilePath, abortController.signal, ); // If the user canceled the AI request, submit another request with `useAi=false` if (!fileCreated) { - await runtimeServiceGenerateMetricsViewFile(instanceId, { + await runtimeServiceGenerateMetricsViewFile(client, { connector: connector, database: database, databaseSchema: databaseSchema, @@ -544,7 +553,7 @@ async function createMetricsViewFromTable( // Wait for Metrics View resource to be ready const metricsViewResource = fileArtifacts .getFileArtifact(newMetricsViewFilePath) - .getResource(queryClient, instanceId); + .getResource(queryClient); await waitUntil(() => get(metricsViewResource).data !== undefined, 5000); @@ -554,7 +563,7 @@ async function createMetricsViewFromTable( } // Wait for the metrics view to finish reconciling before returning - await waitForMetricsViewReconciliation(instanceId, newMetricsViewFilePath); + await waitForMetricsViewReconciliation(newMetricsViewFilePath); return resource; } @@ -564,19 +573,21 @@ async function createMetricsViewFromTable( * Returns the file path of the created explore. */ export async function createExploreWithoutNavigation( + client: RuntimeClient, queryClient: QueryClient, instanceId: string, metricsViewResource: V1Resource, ): Promise { // Create the Explore file const filePath = await createResourceFile( + client, ResourceKind.Explore, metricsViewResource, ); // Wait until the Explore resource is ready const fileArtifact = fileArtifacts.getFileArtifact(filePath); - const resource = fileArtifact.getResource(queryClient, instanceId); + const resource = fileArtifact.getResource(queryClient); await waitUntil(() => { return get(resource).data !== undefined; @@ -596,6 +607,7 @@ export async function createExploreWithoutNavigation( * to create Canvas dashboard only (without Explore). */ export function useCreateMetricsViewWithCanvasUIAction( + client: RuntimeClient, instanceId: string, connector: string, database: string, @@ -624,7 +636,7 @@ export function useCreateMetricsViewWithCanvasUIAction( try { // Step 1: Create metrics view const resource = await createMetricsViewFromTable( - instanceId, + client, connector, database, databaseSchema, @@ -667,7 +679,7 @@ export function useCreateMetricsViewWithCanvasUIAction( }); const canvasFilePath = await createCanvasDashboardWithoutNavigation( - instanceId, + client, metricsViewName, ); @@ -687,7 +699,7 @@ export function useCreateMetricsViewWithCanvasUIAction( } catch (err) { eventBus.emit("notification", { message: "Failed to create Canvas dashboard for " + tableName, - detail: err.response?.data?.message ?? err.message, + detail: extractErrorMessage(err), }); } @@ -703,6 +715,7 @@ export function useCreateMetricsViewWithCanvasUIAction( * This function is to be called from "Generate dashboard" CTA when canvas feature is enabled. */ export function useCreateMetricsViewWithCanvasAndExploreUIAction( + client: RuntimeClient, instanceId: string, connector: string, database: string, @@ -736,7 +749,7 @@ export function useCreateMetricsViewWithCanvasAndExploreUIAction( try { // Step 1: Create metrics view const resource = await createMetricsViewFromTable( - instanceId, + client, connector, database, databaseSchema, @@ -779,6 +792,7 @@ export function useCreateMetricsViewWithCanvasAndExploreUIAction( }); exploreFilePath = await createExploreWithoutNavigation( + client, queryClient, instanceId, resource, @@ -798,7 +812,7 @@ export function useCreateMetricsViewWithCanvasAndExploreUIAction( }); canvasFilePath = await createCanvasDashboardWithoutNavigation( - instanceId, + client, metricsViewName, ); @@ -828,7 +842,7 @@ export function useCreateMetricsViewWithCanvasAndExploreUIAction( } catch (err) { eventBus.emit("notification", { message: "Failed to create dashboards for " + tableName, - detail: err.response?.data?.message ?? err.message, + detail: extractErrorMessage(err), }); // If we have an explore path but canvas failed, navigate to explore diff --git a/web-common/src/features/metrics-views/create-and-preview-explore.ts b/web-common/src/features/metrics-views/create-and-preview-explore.ts index 0a5c2d1f99f..e9e6b887474 100644 --- a/web-common/src/features/metrics-views/create-and-preview-explore.ts +++ b/web-common/src/features/metrics-views/create-and-preview-explore.ts @@ -3,24 +3,27 @@ import type { QueryClient } from "@tanstack/svelte-query"; import { get } from "svelte/store"; import { waitUntil } from "../../lib/waitUtils"; import type { V1Resource } from "../../runtime-client"; +import type { RuntimeClient } from "../../runtime-client/v2"; import { fileArtifacts } from "../entity-management/file-artifacts"; import { ResourceKind } from "../entity-management/resource-selectors"; import { createResourceFile } from "../file-explorer/new-files"; export async function createAndPreviewExplore( + client: RuntimeClient, queryClient: QueryClient, instanceId: string, metricsViewResource: V1Resource, ) { // Create the Explore file const filePath = await createResourceFile( + client, ResourceKind.Explore, metricsViewResource, ); // Wait until the Explore resource is ready const fileArtifact = fileArtifacts.getFileArtifact(filePath); - const resource = fileArtifact.getResource(queryClient, instanceId); + const resource = fileArtifact.getResource(queryClient); await waitUntil(() => { return get(resource).data !== undefined; diff --git a/web-common/src/features/metrics-views/editor/Placeholder.svelte b/web-common/src/features/metrics-views/editor/Placeholder.svelte index 0a26e9c45a8..88480ebc68d 100644 --- a/web-common/src/features/metrics-views/editor/Placeholder.svelte +++ b/web-common/src/features/metrics-views/editor/Placeholder.svelte @@ -7,7 +7,7 @@ type V1Resource, runtimeServicePutFile, } from "@rilldata/web-common/runtime-client"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { useIsModelingSupportedForDefaultOlapDriverOLAP as useIsModelingSupportedForDefaultOlapDriver } from "../../connectors/selectors"; import { createDashboardFromTableInMetricsEditor } from "../ai-generation/generateMetricsView"; @@ -15,19 +15,19 @@ export let filePath: string; export let view: EditorView | undefined = undefined; - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); $: isModelingSupportedForDefaultOlapDriver = - useIsModelingSupportedForDefaultOlapDriver(instanceId); + useIsModelingSupportedForDefaultOlapDriver(runtimeClient); $: isModelingSupported = $isModelingSupportedForDefaultOlapDriver.data; - $: models = useModels(instanceId); + $: models = useModels(runtimeClient); const buttonClasses = "inline hover:font-semibold underline underline-offset-2"; async function onAutogenerateConfigFromModel(modelRes: V1Resource) { await createDashboardFromTableInMetricsEditor( - instanceId, + runtimeClient, modelRes?.model?.state?.resultTable ?? "", filePath, ); @@ -37,7 +37,7 @@ async function onCreateSkeletonMetricsConfig() { const yaml = initBlankDashboardYAML(metricsName); - await runtimeServicePutFile(instanceId, { + await runtimeServicePutFile(runtimeClient, { path: filePath, blob: yaml, create: true, diff --git a/web-common/src/features/metrics-views/get-combined-measures-and-dimensions-for-metrics-views.ts b/web-common/src/features/metrics-views/get-combined-measures-and-dimensions-for-metrics-views.ts index f8b9c8dbda9..d0b3ccee1e5 100644 --- a/web-common/src/features/metrics-views/get-combined-measures-and-dimensions-for-metrics-views.ts +++ b/web-common/src/features/metrics-views/get-combined-measures-and-dimensions-for-metrics-views.ts @@ -4,20 +4,20 @@ import { type MetricsViewSpecDimension, type MetricsViewSpecMeasure, } from "@rilldata/web-common/runtime-client"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store.ts"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { createQueries } from "@tanstack/svelte-query"; import { derived, type Readable } from "svelte/store"; export function getCombinedMeasuresAndDimensionsForMetricsViews( + client: RuntimeClient, metricsViewNamesStore: Readable, ) { const metricsViewQueryOptions = derived( - [runtime, metricsViewNamesStore], - ([{ instanceId }, metricsViewNames]) => + metricsViewNamesStore, + (metricsViewNames) => metricsViewNames.map((metricsViewName) => - getRuntimeServiceGetResourceQueryOptions(instanceId, { - "name.kind": ResourceKind.MetricsView, - "name.name": metricsViewName, + getRuntimeServiceGetResourceQueryOptions(client, { + name: { kind: ResourceKind.MetricsView, name: metricsViewName }, }), ), ); diff --git a/web-common/src/features/metrics-views/metrics-view-selectors.ts b/web-common/src/features/metrics-views/metrics-view-selectors.ts index c1487254e51..3ba20ebf940 100644 --- a/web-common/src/features/metrics-views/metrics-view-selectors.ts +++ b/web-common/src/features/metrics-views/metrics-view-selectors.ts @@ -7,6 +7,7 @@ import { type V1MetricsViewSpec, type V1Resource, } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { derived, get, type Readable } from "svelte/store"; import { ResourceKind, @@ -62,9 +63,9 @@ export class MetricsViewSelectors { typeof useFilteredResources> >; - constructor(instanceId: string, metricsViewsData?: MetricsViewsData) { + constructor(client: RuntimeClient, metricsViewsData?: MetricsViewsData) { this.allMetricsViews = useFilteredResources( - instanceId, + client, ResourceKind.MetricsView, ); diff --git a/web-common/src/features/models/incremental/ModelRefreshButton.svelte b/web-common/src/features/models/incremental/ModelRefreshButton.svelte index a6dee7e8636..650f402abdc 100644 --- a/web-common/src/features/models/incremental/ModelRefreshButton.svelte +++ b/web-common/src/features/models/incremental/ModelRefreshButton.svelte @@ -7,26 +7,23 @@ import { V1ReconcileStatus, type V1Resource, - createRuntimeServiceCreateTrigger, + createRuntimeServiceCreateTriggerMutation, } from "@rilldata/web-common/runtime-client"; - import { runtime } from "../../../runtime-client/runtime-store"; + import { useRuntimeClient } from "../../../runtime-client/v2"; export let resource: V1Resource | undefined; export let hasUnsavedChanges: boolean; - const triggerMutation = createRuntimeServiceCreateTrigger(); - - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); + const triggerMutation = + createRuntimeServiceCreateTriggerMutation(runtimeClient); $: isIncrementalModel = resource?.model?.spec?.incremental; $: isModelIdle = resource?.meta?.reconcileStatus === V1ReconcileStatus.RECONCILE_STATUS_IDLE; function refreshModel(full: boolean) { void $triggerMutation.mutateAsync({ - instanceId, - data: { - models: [{ model: resource?.meta?.name?.name, full: full }], - }, + models: [{ model: resource?.meta?.name?.name, full: full }], }); } diff --git a/web-common/src/features/models/inspector/References.svelte b/web-common/src/features/models/inspector/References.svelte index 7c0b2f2bf31..574bdd41d0f 100644 --- a/web-common/src/features/models/inspector/References.svelte +++ b/web-common/src/features/models/inspector/References.svelte @@ -4,29 +4,34 @@ import CollapsibleSectionTitle from "@rilldata/web-common/layout/CollapsibleSectionTitle.svelte"; import { LIST_SLIDE_DURATION } from "@rilldata/web-common/layout/config"; import { formatCompactInteger } from "@rilldata/web-common/lib/formatters"; + import type { V1ResourceName } from "@rilldata/web-common/runtime-client"; import { - type V1ResourceName, - createQueryServiceTableCardinality, createRuntimeServiceGetResource, + createQueryServiceTableCardinality, } from "@rilldata/web-common/runtime-client"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { derived } from "svelte/store"; import { slide } from "svelte/transition"; - import { runtime } from "../../../runtime-client/runtime-store"; import { removeLeadingSlash } from "../../entity-management/entity-mappers"; import WithModelResultTooltip from "./WithModelResultTooltip.svelte"; export let refs: V1ResourceName[]; export let modelHasError: boolean; + export let connector: string; + export let database: string; + export let databaseSchema: string; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); let showReferences = true; $: referencedResourcesStore = derived( refs.map((ref) => { - return createRuntimeServiceGetResource(instanceId, { - "name.name": ref.name as string, - "name.kind": ref.kind as string, + return createRuntimeServiceGetResource(client, { + name: { + name: ref.name as string, + kind: ref.kind as string, + }, }); }), (refs) => refs.map((ref) => ref.data), @@ -35,18 +40,14 @@ $: referencedResourceCardinalitiesStore = derived( refs.map((ref) => { - return createQueryServiceTableCardinality( - instanceId, - ref.name as string, - {}, - { - query: { - select: (data) => +(data.cardinality ?? 0), - }, - }, - ); + return createQueryServiceTableCardinality(client, { + tableName: ref.name as string, + connector, + database, + databaseSchema, + }); }), - (refs) => refs.map((ref) => ref.data), + (refs) => refs.map((ref) => Number(ref.data?.cardinality ?? 0)), ); $: referencedResourcesCardinalities = $referencedResourceCardinalitiesStore; diff --git a/web-common/src/features/models/inspector/WorkspaceInspector.svelte b/web-common/src/features/models/inspector/WorkspaceInspector.svelte index 9b9f406a83e..2c3be3b8c4a 100644 --- a/web-common/src/features/models/inspector/WorkspaceInspector.svelte +++ b/web-common/src/features/models/inspector/WorkspaceInspector.svelte @@ -15,8 +15,9 @@ formatBigNumberPercentage, formatInteger, } from "@rilldata/web-common/lib/formatters"; + import type { V1Resource } from "@rilldata/web-common/runtime-client"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { - type V1Resource, createQueryServiceTableCardinality, createQueryServiceTableColumns, } from "@rilldata/web-common/runtime-client"; @@ -25,7 +26,6 @@ import { slide } from "svelte/transition"; import { LIST_SLIDE_DURATION } from "../../../layout/config"; import InspectorHeaderGrid from "../../../layout/inspector/InspectorHeaderGrid.svelte"; - import { runtime } from "../../../runtime-client/runtime-store"; import IncrementalProcessing from "../incremental/IncrementalProcessing.svelte"; import PartitionsBrowser from "../partitions/PartitionsBrowser.svelte"; import References from "./References.svelte"; @@ -44,27 +44,25 @@ let showColumns = true; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); + $: source = resource?.source; $: model = resource?.model; $: connectorType = source && formatConnectorType(source); $: fileExtension = source && getFileExtension(source); - $: cardinalityQuery = createQueryServiceTableCardinality( - instanceId, + $: cardinalityQuery = createQueryServiceTableCardinality(client, { tableName, - { - connector, - database, - databaseSchema, - }, - ); + connector, + database, + databaseSchema, + }); $: profileColumnsQuery = createQueryServiceTableColumns( - instanceId, - tableName, + client, { + tableName, connector, database, databaseSchema, @@ -88,7 +86,7 @@ $: columnCount = `${formatInteger(profileColumnsCount)} columns`; $: summaries = getSummaries( - instanceId, + client, connector, database, databaseSchema, @@ -112,9 +110,9 @@ $: cardinalityQueries = resourceRefs.map((ref) => { return createQueryServiceTableCardinality( - instanceId, - ref.name as string, + client, { + tableName: ref.name as string, connector, database, databaseSchema, @@ -130,9 +128,9 @@ $: sourceProfileColumns = resourceRefs.map((ref) => { return createQueryServiceTableColumns( - instanceId, - ref.name as string, + client, { + tableName: ref.name as string, connector, database, databaseSchema, @@ -259,7 +257,13 @@
    - +
    diff --git a/web-common/src/features/models/navigation/ModelMenuItems.svelte b/web-common/src/features/models/navigation/ModelMenuItems.svelte index 1c0c117563a..7e3fe221dfb 100644 --- a/web-common/src/features/models/navigation/ModelMenuItems.svelte +++ b/web-common/src/features/models/navigation/ModelMenuItems.svelte @@ -17,7 +17,7 @@ import Model from "../../../components/icons/Model.svelte"; import { behaviourEvent } from "../../../metrics/initMetrics"; import { V1ReconcileStatus } from "../../../runtime-client"; - import { runtime } from "../../../runtime-client/runtime-store"; + import { useRuntimeClient } from "../../../runtime-client/v2"; import { createSqlModelFromTable } from "../../connectors/code-utils"; import { getScreenNameFromPage } from "../../file-explorer/telemetry"; import { @@ -26,17 +26,18 @@ useCreateMetricsViewWithCanvasUIAction, } from "../../metrics-views/ai-generation/generateMetricsView"; + const runtimeClient = useRuntimeClient(); const { ai, developerChat } = featureFlags; const queryClient = useQueryClient(); export let filePath: string; - $: ({ instanceId } = $runtime); + $: ({ instanceId } = runtimeClient); $: fileArtifact = fileArtifacts.getFileArtifact(filePath); - $: modelHasError = fileArtifact.getHasErrors(queryClient, instanceId); - $: modelQuery = fileArtifact.getResource(queryClient, instanceId); + $: modelHasError = fileArtifact.getHasErrors(queryClient); + $: modelQuery = fileArtifact.getResource(queryClient); $: modelResource = $modelQuery.data; $: connector = modelResource?.model?.spec?.outputConnector; $: modelIsIdle = @@ -60,6 +61,7 @@ const previousActiveEntity = getScreenNameFromPage(); const addDevLimit = false; // Typically, the `dev` limit would be applied on the Source itself const [newModelPath, newModelName] = await createSqlModelFromTable( + runtimeClient, queryClient, connector as string, "", @@ -83,6 +85,7 @@ } $: createMetricsViewFromTable = useCreateMetricsViewFromTableUIAction( + runtimeClient, instanceId, connector as string, "", @@ -94,6 +97,7 @@ ); $: createExploreFromTable = useCreateMetricsViewFromTableUIAction( + runtimeClient, instanceId, connector as string, "", @@ -105,6 +109,7 @@ ); $: createCanvasDashboardFromTable = useCreateMetricsViewWithCanvasUIAction( + runtimeClient, instanceId, connector as string, "", @@ -118,7 +123,7 @@ // Use developer agent if enabled, otherwise fall back to RPC if ($developerChat) { await createCanvasDashboardFromTableWithAgent( - instanceId, + runtimeClient, connector as string, "", "", diff --git a/web-common/src/features/models/partitions/PartitionsTable.svelte b/web-common/src/features/models/partitions/PartitionsTable.svelte index bcd8b2f7629..1c57e363b80 100644 --- a/web-common/src/features/models/partitions/PartitionsTable.svelte +++ b/web-common/src/features/models/partitions/PartitionsTable.svelte @@ -13,10 +13,9 @@ import { type V1ModelPartition, type V1Resource, - createRuntimeServiceGetModelPartitionsInfinite, } from "../../../runtime-client"; - - import { runtime } from "../../../runtime-client/runtime-store"; + import { useRuntimeClient } from "../../../runtime-client/v2"; + import { createRuntimeServiceGetModelPartitionsInfinite } from "../../../runtime-client"; import DataCell from "./DataCell.svelte"; import ErrorCell from "./ErrorCell.svelte"; import TriggerPartition from "./TriggerPartition.svelte"; @@ -25,8 +24,9 @@ export let whereErrored: boolean; export let wherePending: boolean; + const runtimeClient = useRuntimeClient(); + $: modelName = resource?.meta?.name?.name as string; - $: ({ instanceId } = $runtime); // ========================== // Infinite Query @@ -36,19 +36,10 @@ ...(wherePending ? { pending: true } : {}), }; $: query = createRuntimeServiceGetModelPartitionsInfinite( - instanceId, - modelName, - { - ...baseParams, - }, + runtimeClient, + { model: modelName, ...baseParams }, { query: { - getNextPageParam: (lastPage) => { - if (lastPage.nextPageToken !== "") { - return lastPage.nextPageToken; - } - return undefined; - }, enabled: !!modelName, refetchOnMount: true, }, @@ -195,7 +186,7 @@ setOptions({ count: $query.hasNextPage ? allRows.length + 1 : allRows.length, }); - const [lastItem] = [...virtualRows].reverse(); + const lastItem = virtualRows[virtualRows.length - 1]; if ( lastItem && lastItem.index > allRows.length - 1 && @@ -249,7 +240,7 @@ Error: {error.response.data.message}Error: {error.message} @@ -261,7 +252,7 @@ {:else} - {#each getVirtualItems() as virtualRow (virtualRow.index)} + {#each virtualRows as virtualRow (virtualRow.index)} {#each rows[virtualRow.index]?.getVisibleCells() ?? [] as cell (cell.id)} diff --git a/web-common/src/features/models/partitions/TriggerPartition.svelte b/web-common/src/features/models/partitions/TriggerPartition.svelte index 8f9c448f63a..96a4c1a83c0 100644 --- a/web-common/src/features/models/partitions/TriggerPartition.svelte +++ b/web-common/src/features/models/partitions/TriggerPartition.svelte @@ -5,16 +5,18 @@ import { Button } from "../../../components/button"; import { V1ReconcileStatus, - createRuntimeServiceCreateTrigger, + createRuntimeServiceCreateTriggerMutation, getRuntimeServiceGetModelPartitionsQueryKey, type V1Resource, } from "../../../runtime-client"; - import { runtime } from "../../../runtime-client/runtime-store"; + import { useRuntimeClient } from "../../../runtime-client/v2"; import { addLeadingSlash } from "../../entity-management/entity-mappers"; import { fileArtifacts } from "../../entity-management/file-artifacts"; + const runtimeClient = useRuntimeClient(); const queryClient = useQueryClient(); - const triggerMutation = createRuntimeServiceCreateTrigger(); + const triggerMutation = + createRuntimeServiceCreateTriggerMutation(runtimeClient); export let partitionKey: string; export let resource: V1Resource | undefined = undefined; @@ -38,24 +40,20 @@ } await $triggerMutation.mutateAsync({ - instanceId, - data: { - models: [ - { - model: modelName, - partitions: [partitionKey], - }, - ], - }, + models: [ + { + model: modelName, + partitions: [partitionKey], + }, + ], }); // Poll for updates since partition execution happens asynchronously const invalidate = () => queryClient.invalidateQueries({ - queryKey: getRuntimeServiceGetModelPartitionsQueryKey( - instanceId, - modelName, - ), + queryKey: getRuntimeServiceGetModelPartitionsQueryKey(instanceId, { + model: modelName, + }), }); await invalidate(); @@ -75,7 +73,7 @@ }, 2000); } - $: ({ instanceId } = $runtime); + $: ({ instanceId } = runtimeClient); $: isLoading = $triggerMutation.isPending; // If resource is passed as prop, use it directly; otherwise derive from URL params (web-local) @@ -84,7 +82,7 @@ params.file !== undefined ? fileArtifacts.getFileArtifact(addLeadingSlash(params.file)) : undefined; - $: resourceQuery = fileArtifact?.getResource(queryClient, instanceId); + $: resourceQuery = fileArtifact?.getResource(queryClient); $: resolvedResource = resource ?? $resourceQuery?.data; diff --git a/web-common/src/features/models/selectors.ts b/web-common/src/features/models/selectors.ts index 7437cea10b1..0dbf7b746b9 100644 --- a/web-common/src/features/models/selectors.ts +++ b/web-common/src/features/models/selectors.ts @@ -3,6 +3,7 @@ import { useClientFilteredResources, useResource, } from "@rilldata/web-common/features/entity-management/resource-selectors"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import type { QueryClient } from "@tanstack/query-core"; import type { Readable } from "svelte/motion"; import { derived } from "svelte/store"; @@ -11,9 +12,9 @@ import { type TableColumnsWithName, } from "../sources/selectors"; -export function useModels(instanceId: string) { +export function useModels(client: RuntimeClient) { return useClientFilteredResources( - instanceId, + client, ResourceKind.Model, (res) => res.meta?.name?.name === res.model?.state?.resultTable && @@ -21,15 +22,15 @@ export function useModels(instanceId: string) { ); } -export function useModel(instanceId: string, name: string) { - return useResource(instanceId, name, ResourceKind.Model); +export function useModel(client: RuntimeClient, name: string) { + return useResource(client, name, ResourceKind.Model); } export function useAllModelColumns( queryClient: QueryClient, - instanceId: string, + client: RuntimeClient, ): Readable> { - return derived([useModels(instanceId)], ([allModels], set) => { + return derived([useModels(client)], ([allModels], set) => { if (!allModels.data?.length) { set([]); return; @@ -39,7 +40,7 @@ export function useAllModelColumns( allModels.data.map((r) => createTableColumnsWithName( queryClient, - instanceId, + client, r.model?.state?.resultConnector ?? "", "", "", diff --git a/web-common/src/features/models/workspace/CreateDashboardButton.svelte b/web-common/src/features/models/workspace/CreateDashboardButton.svelte index c90c8b967cf..829db39b397 100644 --- a/web-common/src/features/models/workspace/CreateDashboardButton.svelte +++ b/web-common/src/features/models/workspace/CreateDashboardButton.svelte @@ -8,7 +8,7 @@ import { MetricsEventSpace } from "@rilldata/web-common/metrics/service/MetricsTypes"; import { Wand } from "lucide-svelte"; import { V1ReconcileStatus } from "../../../runtime-client"; - import { runtime } from "../../../runtime-client/runtime-store"; + import { useRuntimeClient } from "../../../runtime-client/v2"; import { allowPrimary } from "../../dashboards/workspace/DeployProjectCTA.svelte"; import { featureFlags } from "../../feature-flags"; import { useCreateMetricsViewFromTableUIAction } from "../../metrics-views/ai-generation/generateMetricsView"; @@ -18,17 +18,19 @@ export let hasResultTable = false; export let collapse = false; + const runtimeClient = useRuntimeClient(); const { ai } = featureFlags; - $: ({ instanceId } = $runtime); + $: ({ instanceId } = runtimeClient); - $: modelQuery = useModel(instanceId, modelName); + $: modelQuery = useModel(runtimeClient, modelName); $: connector = $modelQuery.data?.model?.spec?.outputConnector; $: modelIsIdle = $modelQuery.data?.meta?.reconcileStatus === V1ReconcileStatus.RECONCILE_STATUS_IDLE; $: createMetricsViewFromModel = useCreateMetricsViewFromTableUIAction( + runtimeClient, instanceId, connector as string, "", diff --git a/web-common/src/features/models/workspace/ModelEditor.svelte b/web-common/src/features/models/workspace/ModelEditor.svelte index b9707003863..dcb6630c2d6 100644 --- a/web-common/src/features/models/workspace/ModelEditor.svelte +++ b/web-common/src/features/models/workspace/ModelEditor.svelte @@ -8,7 +8,7 @@ import { EditorView } from "@codemirror/view"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; import { DuckDBSQL } from "../../../components/editor/presets/duckDBDialect"; - import { runtime } from "../../../runtime-client/runtime-store"; + import { useRuntimeClient } from "../../../runtime-client/v2"; import Editor from "../../editor/Editor.svelte"; import { getExtensionsForFile } from "../../editor/getExtensionsForFile"; import { FileArtifact } from "../../entity-management/file-artifact"; @@ -21,7 +21,7 @@ export let fileArtifact: FileArtifact; export let onSave: (content: string) => void = () => {}; - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); $: ({ remoteContent } = fileArtifact); @@ -29,7 +29,7 @@ let autocompleteCompartment = new Compartment(); // Autocomplete: source tables - $: allSourceColumns = useAllSourceColumns(queryClient, instanceId); + $: allSourceColumns = useAllSourceColumns(queryClient, runtimeClient); $: if ($allSourceColumns?.length) { for (const sourceTable of $allSourceColumns) { const sourceIdentifier = sourceTable?.tableName; @@ -41,7 +41,7 @@ } // Autocomplete: model tables - $: allModelColumns = useAllModelColumns(queryClient, instanceId); + $: allModelColumns = useAllModelColumns(queryClient, runtimeClient); $: if ($allModelColumns?.length) { for (const modelTable of $allModelColumns) { const modelIdentifier = modelTable?.tableName; diff --git a/web-common/src/features/models/workspace/ModelWorkspaceCTAs.svelte b/web-common/src/features/models/workspace/ModelWorkspaceCTAs.svelte index 4d0b83d53bb..ce6aa05050a 100644 --- a/web-common/src/features/models/workspace/ModelWorkspaceCTAs.svelte +++ b/web-common/src/features/models/workspace/ModelWorkspaceCTAs.svelte @@ -10,7 +10,7 @@ V1ReconcileStatus, type V1Resource, } from "@rilldata/web-common/runtime-client"; - import { runtime } from "../../../runtime-client/runtime-store"; + import { useRuntimeClient } from "../../../runtime-client/v2"; import { useGetMetricsViewsForModel } from "../../dashboards/selectors"; import ExportMenu from "../../exports/ExportMenu.svelte"; import { useCreateMetricsViewFromTableUIAction } from "../../metrics-views/ai-generation/generateMetricsView"; @@ -25,15 +25,18 @@ export let hasUnsavedChanges: boolean; export let connector: string; - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); + + $: ({ instanceId } = runtimeClient); $: isModelIdle = resource?.meta?.reconcileStatus === V1ReconcileStatus.RECONCILE_STATUS_IDLE; - $: metricsViewsQuery = useGetMetricsViewsForModel(instanceId, modelName); + $: metricsViewsQuery = useGetMetricsViewsForModel(runtimeClient, modelName); $: availableMetricsViews = $metricsViewsQuery.data ?? []; $: createMetricsViewFromTable = useCreateMetricsViewFromTableUIAction( + runtimeClient, instanceId, connector, "", diff --git a/web-common/src/features/onboarding/OnboardingWorkspace.svelte b/web-common/src/features/onboarding/OnboardingWorkspace.svelte index 62ee0c4cd8a..3dd4b62d812 100644 --- a/web-common/src/features/onboarding/OnboardingWorkspace.svelte +++ b/web-common/src/features/onboarding/OnboardingWorkspace.svelte @@ -2,8 +2,8 @@ import { goto } from "$app/navigation"; import * as DropdownMenu from "@rilldata/web-common/components/dropdown-menu"; import Button from "../../components/button/Button.svelte"; - import { createRuntimeServiceUnpackExample } from "../../runtime-client"; - import { runtime } from "../../runtime-client/runtime-store"; + import { createRuntimeServiceUnpackExampleMutation } from "../../runtime-client"; + import { useRuntimeClient } from "../../runtime-client/v2"; import { addSourceModal } from "../sources/modal/add-source-visibility"; import ImportData from "@rilldata/web-common/components/icons/ImportData.svelte"; import GenerateSampleData from "@rilldata/web-common/features/sample-data/GenerateSampleData.svelte"; @@ -21,9 +21,10 @@ import { waitUntil } from "@rilldata/web-common/lib/waitUtils.ts"; import { fileArtifacts } from "@rilldata/web-common/features/entity-management/file-artifacts.ts"; - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); - const unpackExampleProject = createRuntimeServiceUnpackExample(); + const unpackExampleProject = + createRuntimeServiceUnpackExampleMutation(runtimeClient); async function unpackProject(example: (typeof EXAMPLES)[number]) { await behaviourEvent?.fireSplashEvent( @@ -37,11 +38,8 @@ try { await $unpackExampleProject.mutateAsync({ - instanceId, - data: { - name: example.name, - force: true, - }, + name: example.name, + force: true, }); await waitUntil(() => fileArtifacts.hasFileArtifact(example.firstFile)); @@ -74,7 +72,8 @@