diff --git a/Makefile b/Makefile index d268f0d5e34..35827f4c95c 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))" \ @@ -65,5 +66,6 @@ proto.generate: scripts/convert-openapi-v2-to-v3/convert.go --force --public-only \ proto/gen/rill/admin/v1/admin.swagger.yaml proto/gen/rill/admin/v1/public.openapi.yaml npm run generate:runtime-client -w web-common + npm run generate:query-hooks -w web-common npm run generate:client -w web-admin \ No newline at end of file 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/bookmarks/ExploreBookmarks.svelte b/web-admin/src/features/bookmarks/ExploreBookmarks.svelte index e7c311d3caf..ea994fef3fb 100644 --- a/web-admin/src/features/bookmarks/ExploreBookmarks.svelte +++ b/web-admin/src/features/bookmarks/ExploreBookmarks.svelte @@ -10,9 +10,12 @@ import { createUrlForExploreYAMLDefaultState } from "@rilldata/web-common/features/dashboards/stores/get-explore-state-from-yaml-config.ts"; import { createRillDefaultExploreUrlParamsV2 } from "@rilldata/web-common/features/dashboards/url-state/get-rill-default-explore-url-params.ts"; import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors.ts"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { createQuery } from "@tanstack/svelte-query"; import { writable } from "svelte/store"; + const runtimeClient = useRuntimeClient(); + export let organization: string; export let project: string; export let metricsViewName: string; @@ -26,17 +29,21 @@ // Get url params for the explore based on yaml defaults. // This is used for home bookmarks button when there is no explicit home bookmark created. - const urlForExploreYAMLDefaultState = - createUrlForExploreYAMLDefaultState(exploreNameStore); + const urlForExploreYAMLDefaultState = createUrlForExploreYAMLDefaultState( + runtimeClient, + exploreNameStore, + ); // Rill opinionated url params that are removed from url to keep the url short. // To keep bookmarks exhaustive, these are added on top of current url params while creating bookmarks. - const rillDefaultExploreURLParams = - createRillDefaultExploreUrlParamsV2(exploreNameStore); + const rillDefaultExploreURLParams = createRillDefaultExploreUrlParamsV2( + runtimeClient, + exploreNameStore, + ); // Transformer for legacy bookmark data that was stored in proto format. const exploreBookmarkLegacyDataTransformer = - createExploreBookmarkLegacyDataTransformer(exploreNameStore); + createExploreBookmarkLegacyDataTransformer(runtimeClient, exploreNameStore); // Stable query object for bookmarks for this explore const bookmarksQuery = createQuery( diff --git a/web-admin/src/features/bookmarks/explore-bookmark-legacy-data-transformer.ts b/web-admin/src/features/bookmarks/explore-bookmark-legacy-data-transformer.ts index 25c53719e85..3ed06291a64 100644 --- a/web-admin/src/features/bookmarks/explore-bookmark-legacy-data-transformer.ts +++ b/web-admin/src/features/bookmarks/explore-bookmark-legacy-data-transformer.ts @@ -3,6 +3,7 @@ import { getMetricsViewTimeRangeFromExploreQueryOptions } from "@rilldata/web-co 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 { getExploreValidSpecQueryOptions } from "@rilldata/web-common/features/explores/selectors.ts"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import type { V1ExploreSpec, V1MetricsViewSpec, @@ -12,13 +13,14 @@ import { createQuery } from "@tanstack/svelte-query"; import { derived, type Readable } from "svelte/store"; export function createExploreBookmarkLegacyDataTransformer( + 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-admin/src/features/projects/download-report.ts b/web-admin/src/features/projects/download-report.ts index 6e8c38fed67..f6506526309 100644 --- a/web-admin/src/features/projects/download-report.ts +++ b/web-admin/src/features/projects/download-report.ts @@ -4,7 +4,6 @@ import { createQueryServiceExportReport, type RpcStatus, } from "@rilldata/web-common/runtime-client"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import { createMutation, type CreateMutationOptions, @@ -17,6 +16,7 @@ export type DownloadReportRequest = { reportId: string; executionTime: string; originBaseUrl: string; + host: string; }; export function createDownloadReportMutation< @@ -48,7 +48,7 @@ export function createDownloadReportMutation< 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/selectors.ts b/web-admin/src/features/projects/selectors.ts index e3e870be916..08414e86f7c 100644 --- a/web-admin/src/features/projects/selectors.ts +++ b/web-admin/src/features/projects/selectors.ts @@ -20,7 +20,6 @@ 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 { derived, type Readable } from "svelte/store"; export function getProjectPermissions(orgName: string, projName: string) { @@ -137,13 +136,16 @@ 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, }, }; } diff --git a/web-admin/src/routes/[organization]/[project]/+layout.svelte b/web-admin/src/routes/[organization]/[project]/+layout.svelte index c983cb6b533..46f07e16c6e 100644 --- a/web-admin/src/routes/[organization]/[project]/+layout.svelte +++ b/web-admin/src/routes/[organization]/[project]/+layout.svelte @@ -51,10 +51,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 { 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 +205,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 +261,15 @@ : "There was an error deploying your project. Please contact support."} /> {:else if isProjectAvailable} - - - + {#key `${effectiveHost}::${effectiveInstanceId}`} + + + + {/key} {/if} {/if} 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..cab71cf2217 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 @@ -26,6 +26,7 @@ reportId, executionTime, originBaseUrl: window.location.origin, + host: $runtime.host, }, }); } diff --git a/web-common/package.json b/web-common/package.json index 9b89aa91a61..877ccad2a26 100644 --- a/web-common/package.json +++ b/web-common/package.json @@ -6,6 +6,7 @@ "lint": "prettier --check . && eslint .", "format": "prettier --write .", "generate:runtime-client": "svelte-kit sync && orval --config ./orval.config.ts", + "generate:query-hooks": "tsx scripts/generate-query-hooks.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", diff --git a/web-common/scripts/generate-query-hooks-config.ts b/web-common/scripts/generate-query-hooks-config.ts new file mode 100644 index 00000000000..9d933431938 --- /dev/null +++ b/web-common/scripts/generate-query-hooks-config.ts @@ -0,0 +1,81 @@ +/** + * Classification config for the query hooks code generator. + * Determines whether each RPC method produces a query or mutation hook, + * or should be skipped entirely (streaming methods). + */ + +export type MethodClassification = "query" | "mutation" | "skip"; + +/** + * Per-service overrides. Methods not listed here fall through to the + * service-level default classifier. + */ +export const methodOverrides: Record< + string, + Record +> = { + QueryService: { + // Semantically write operations + export: "mutation", + exportReport: "mutation", + query: "mutation", + // Streaming + queryBatch: "skip", + }, + RuntimeService: { + // Streaming + watchFiles: "skip", + watchLogs: "skip", + watchResources: "skip", + completeStreaming: "skip", + // Explicitly classified as queries (despite not matching Get/List prefix) + issueDevJWT: "query", + analyzeConnectors: "query", + analyzeVariables: "query", + queryResolver: "query", + }, + ConnectorService: { + // All methods are queries (no overrides needed) + }, +}; + +/** + * Default classifier for methods not in the override map. + * RuntimeService uses prefix-based classification; other services default to query. + */ +export function classifyMethod( + serviceName: string, + methodName: string, +): MethodClassification { + // Check overrides first + const overrides = methodOverrides[serviceName]; + if (overrides && methodName in overrides) { + return overrides[methodName]; + } + + // Service-specific defaults + if (serviceName === "RuntimeService") { + const queryPrefixes = [ + "get", + "list", + "ping", + "health", + "instanceHealth", + ]; + const lowerMethod = methodName.charAt(0).toLowerCase() + methodName.slice(1); + if (queryPrefixes.some((p) => lowerMethod.startsWith(p))) { + return "query"; + } + return "mutation"; + } + + if (serviceName === "QueryService") { + return "query"; + } + + if (serviceName === "ConnectorService") { + return "query"; + } + + return "query"; +} diff --git a/web-common/scripts/generate-query-hooks.ts b/web-common/scripts/generate-query-hooks.ts new file mode 100644 index 00000000000..be1e95c5879 --- /dev/null +++ b/web-common/scripts/generate-query-hooks.ts @@ -0,0 +1,480 @@ +/** + * Code generator: reads ConnectRPC *_connect.ts service descriptors and + * produces TanStack Query hooks for Svelte. + * + * Usage: tsx scripts/generate-query-hooks.ts + * + * Output: src/runtime-client/v2/gen/{query-service,runtime-service,connector-service}.ts + */ + +import * as fs from "node:fs"; +import * as path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +import { MethodKind } from "@bufbuild/protobuf"; +import { classifyMethod, type MethodClassification } from "./generate-query-hooks-config"; + +// --- Service descriptors --- + +import { QueryService } from "../src/proto/gen/rill/runtime/v1/queries_connect"; +import { RuntimeService } from "../src/proto/gen/rill/runtime/v1/api_connect"; +import { ConnectorService } from "../src/proto/gen/rill/runtime/v1/connectors_connect"; + +interface ServiceDef { + typeName: string; + methods: Record< + string, + { + name: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + I: { typeName: string; new (): any }; + O: { typeName: string }; + kind: MethodKind; + } + >; +} + +interface MethodInfo { + /** camelCase method name as it appears in the service descriptor (e.g. "metricsViewAggregation") */ + methodKey: string; + /** PascalCase method name from the proto (e.g. "MetricsViewAggregation") */ + methodName: string; + /** Request message type name (e.g. "MetricsViewAggregationRequest") */ + inputType: string; + /** Response message type name (e.g. "MetricsViewAggregationResponse") */ + outputType: string; + classification: MethodClassification; + /** Whether the request type has an instanceId field */ + hasInstanceId: boolean; +} + +// --- Helpers --- + +function pascalCase(s: string): string { + return s.charAt(0).toUpperCase() + s.slice(1); +} + +function extractShortName(typeName: string): string { + // "rill.runtime.v1.MetricsViewAggregationRequest" -> "MetricsViewAggregationRequest" + const parts = typeName.split("."); + return parts[parts.length - 1]; +} + +function getServiceShortName(typeName: string): string { + // "rill.runtime.v1.QueryService" -> "QueryService" + return extractShortName(typeName); +} + +function getServiceClientProp(serviceName: string): string { + // "QueryService" -> "queryService", "RuntimeService" -> "runtimeService" + return serviceName.charAt(0).toLowerCase() + serviceName.slice(1); +} + +/** kebab-case for output filename */ +function toKebabCase(s: string): string { + return s + .replace(/([a-z])([A-Z])/g, "$1-$2") + .replace(/([A-Z])([A-Z][a-z])/g, "$1-$2") + .toLowerCase(); +} + +/** Derive the _pb.ts module path that exports the request/response types */ +function getProtoImportPath(serviceName: string): string { + const fileMap: Record = { + QueryService: "queries_pb", + RuntimeService: "api_pb", + ConnectorService: "connectors_pb", + }; + return `../../../proto/gen/rill/runtime/v1/${fileMap[serviceName]}`; +} + +// --- JSON bridge: Orval type helpers --- + +/** Read Orval schemas to discover available V1 types at generation time */ +const orvalSchemaPath = path.resolve( + __dirname, + "../src/runtime-client/gen/index.schemas.ts", +); +const orvalSchemaContent = fs.readFileSync(orvalSchemaPath, "utf-8"); +const availableOrvalTypes = new Set(); +for (const match of orvalSchemaContent.matchAll( + /^export (?:type|interface) (\w+)/gm, +)) { + availableOrvalTypes.add(match[1]); +} + +const ORVAL_IMPORT_PATH = "../../gen/index.schemas"; + +function orvalTypeName(protoTypeName: string): string { + return `V1${protoTypeName}`; +} + +function hasOrvalType(protoTypeName: string): boolean { + return availableOrvalTypes.has(orvalTypeName(protoTypeName)); +} + +/** Get the public-facing type for a request or response */ +function publicType(protoTypeName: string): string { + return hasOrvalType(protoTypeName) + ? orvalTypeName(protoTypeName) + : `PartialMessage<${protoTypeName}>`; +} + +// --- Method extraction --- + +function extractMethods(service: ServiceDef): MethodInfo[] { + const serviceName = getServiceShortName(service.typeName); + const methods: MethodInfo[] = []; + + for (const [key, method] of Object.entries(service.methods)) { + if (method.kind !== MethodKind.Unary) { + // Skip streaming methods automatically + continue; + } + + const classification = classifyMethod(serviceName, key); + if (classification === "skip") continue; + + const hasInstanceId = "instanceId" in new method.I(); + + methods.push({ + methodKey: key, + methodName: method.name, + inputType: extractShortName(method.I.typeName), + outputType: extractShortName(method.O.typeName), + classification, + hasInstanceId, + }); + } + + return methods; +} + +// --- Code generation --- + +function generateServiceFile(service: ServiceDef): string { + const serviceName = getServiceShortName(service.typeName); + const serviceClientProp = getServiceClientProp(serviceName); + const protoImportPath = getProtoImportPath(serviceName); + const methods = extractMethods(service); + + const queryMethods = methods.filter((m) => m.classification === "query"); + const mutationMethods = methods.filter( + (m) => m.classification === "mutation", + ); + + // Collect proto types (value imports; needed for fromJson/toJson calls) + const protoTypes = new Set(); + // Collect Orval types (type-only imports; used in public API signatures) + const orvalTypes = new Set(); + // Track whether PartialMessage is needed (for request types without V1 counterparts) + let needsPartialMessage = false; + + for (const m of methods) { + protoTypes.add(m.inputType); + + if (hasOrvalType(m.inputType)) { + orvalTypes.add(orvalTypeName(m.inputType)); + } else { + needsPartialMessage = true; + // Still need proto type for PartialMessage<> reference + } + + if (hasOrvalType(m.outputType)) { + orvalTypes.add(orvalTypeName(m.outputType)); + } + } + + const lines: string[] = []; + + // Header + lines.push(`// Generated by generate-query-hooks.ts — DO NOT EDIT`); + lines.push(``); + lines.push(`import type { RuntimeClient } from "../runtime-client";`); + + // @bufbuild/protobuf imports (JsonValue always needed; PartialMessage conditional) + const bufImports: string[] = ["JsonValue"]; + if (needsPartialMessage) bufImports.unshift("PartialMessage"); + lines.push( + `import type { ${bufImports.join(", ")} } from "@bufbuild/protobuf";`, + ); + + lines.push( + `import {`, + ` createQuery,`, + ` createMutation,`, + ` type CreateQueryOptions,`, + ` type CreateQueryResult,`, + ` type QueryClient,`, + ` type QueryFunction,`, + ` type QueryKey,`, + ` type CreateMutationOptions,`, + ` type CreateMutationResult,`, + `} from "@tanstack/svelte-query";`, + ); + + // Orval type imports (type-only; for public API signatures) + if (orvalTypes.size > 0) { + const sortedOrval = [...orvalTypes].sort(); + lines.push( + `import type {`, + ...sortedOrval.map( + (t, i) => ` ${t}${i < sortedOrval.length - 1 ? "," : ""}`, + ), + `} from "${ORVAL_IMPORT_PATH}";`, + ); + } + + // Proto type imports (value imports; needed for fromJson calls) + const sortedProto = [...protoTypes].sort(); + lines.push( + `import {`, + ...sortedProto.map( + (t, i) => ` ${t}${i < sortedProto.length - 1 ? "," : ""}`, + ), + `} from "${protoImportPath}";`, + ); + + // Utility: strip undefined values before passing to proto fromJson + // (proto fromJson rejects undefined; Orval's HTTP client silently omitted them) + lines.push( + ``, + `/** Deep-strip undefined values — proto fromJson rejects them */`, + `// eslint-disable-next-line @typescript-eslint/no-explicit-any`, + `function stripUndefined(obj: Record): Record {`, + ` const result: Record = {};`, + ` for (const [key, value] of Object.entries(obj)) {`, + ` if (value === undefined) continue;`, + ` if (Array.isArray(value)) {`, + ` result[key] = value.map((item) =>`, + ` item && typeof item === "object" && !Array.isArray(item)`, + ` ? stripUndefined(item)`, + ` : item,`, + ` );`, + ` } else if (value && typeof value === "object" && !(value instanceof Date)) {`, + ` result[key] = stripUndefined(value);`, + ` } else {`, + ` result[key] = value;`, + ` }`, + ` }`, + ` return result;`, + `}`, + ); + + lines.push(``); + + // --- Query methods --- + for (const m of queryMethods) { + const fullName = `${serviceClientProp}${pascalCase(m.methodKey)}`; + const keyFnName = `get${pascalCase(serviceClientProp)}${pascalCase(m.methodKey)}QueryKey`; + const optsFnName = `get${pascalCase(serviceClientProp)}${pascalCase(m.methodKey)}QueryOptions`; + const hookName = `create${pascalCase(serviceClientProp)}${pascalCase(m.methodKey)}`; + + // Public-facing request type (V1 Orval when available, PartialMessage fallback) + const inputPublic = publicType(m.inputType); + const requestType = m.hasInstanceId + ? `Omit<${inputPublic}, "instanceId">` + : inputPublic; + const requestSpread = m.hasInstanceId + ? `{ instanceId: client.instanceId, ...request }` + : `request`; + + // Public-facing response type (always V1 Orval) + const responseType = publicType(m.outputType); + + // Tier 1: Raw function (JSON bridge: fromJson on input, toJson on output) + lines.push( + `/**`, + ` * Raw RPC call: ${serviceName}.${m.methodName}`, + ` */`, + `export function ${fullName}(`, + ` client: RuntimeClient,`, + ` request: ${requestType},`, + ` options?: { signal?: AbortSignal },`, + `): Promise<${responseType}> {`, + ` return client.${serviceClientProp}.${m.methodKey}(`, + ` ${m.inputType}.fromJson(stripUndefined(${requestSpread}) as unknown as JsonValue),`, + ` { signal: options?.signal },`, + ` ).then(r => r.toJson({ emitDefaultValues: true }) as unknown as ${responseType});`, + `}`, + ``, + ); + + // Tier 2: Query key + lines.push( + `export function ${keyFnName}(`, + ` instanceId: string,`, + ` request?: ${requestType},`, + `): QueryKey {`, + ` return ["${serviceName}", "${m.methodKey}", instanceId, request ?? {}] as const;`, + `}`, + ``, + ); + + // Tier 3: Query options (generic TData for select support) + lines.push( + `export function ${optsFnName}(`, + ` client: RuntimeClient,`, + ` request: ${requestType},`, + ` options?: {`, + ` query?: Partial>;`, + ` },`, + `): CreateQueryOptions<${responseType}, Error, TData> & { queryKey: QueryKey } {`, + ` const queryKey = ${keyFnName}(client.instanceId, request);`, + ` const queryFn: QueryFunction<${responseType}> = ({ signal }) =>`, + ` ${fullName}(client, request, { signal });`, + ` return {`, + ` queryKey,`, + ` queryFn,`, + ` enabled: !!client.instanceId,`, + ` ...options?.query,`, + ` } as CreateQueryOptions<${responseType}, Error, TData> & { queryKey: QueryKey };`, + `}`, + ``, + ); + + // Tier 4: Convenience hook (generic TData for select support) + lines.push( + `export function ${hookName}(`, + ` client: RuntimeClient,`, + ` request: ${requestType},`, + ` options?: {`, + ` query?: Partial>;`, + ` },`, + ` queryClient?: QueryClient,`, + `): CreateQueryResult {`, + ` const queryOptions = ${optsFnName}(client, request, options);`, + ` return createQuery(queryOptions, queryClient);`, + `}`, + ``, + ); + } + + // --- Mutation methods --- + for (const m of mutationMethods) { + const fullName = `${serviceClientProp}${pascalCase(m.methodKey)}`; + const mutOptsFnName = `get${pascalCase(serviceClientProp)}${pascalCase(m.methodKey)}MutationOptions`; + const mutHookName = `create${pascalCase(serviceClientProp)}${pascalCase(m.methodKey)}Mutation`; + + // Public-facing request type (V1 Orval when available, PartialMessage fallback) + const inputPublic = publicType(m.inputType); + const requestType = m.hasInstanceId + ? `Omit<${inputPublic}, "instanceId">` + : inputPublic; + const requestSpread = m.hasInstanceId + ? `{ instanceId: client.instanceId, ...request }` + : `request`; + + // Public-facing response type (always V1 Orval) + const responseType = publicType(m.outputType); + + // Raw function (JSON bridge: fromJson on input, toJson on output) + lines.push( + `/**`, + ` * Raw RPC call (mutation): ${serviceName}.${m.methodName}`, + ` */`, + `export function ${fullName}(`, + ` client: RuntimeClient,`, + ` request: ${requestType},`, + `): Promise<${responseType}> {`, + ` return client.${serviceClientProp}.${m.methodKey}(`, + ` ${m.inputType}.fromJson(stripUndefined(${requestSpread}) as unknown as JsonValue),`, + ` ).then(r => r.toJson({ emitDefaultValues: true }) as unknown as ${responseType});`, + `}`, + ``, + ); + + // Mutation options + lines.push( + `export function ${mutOptsFnName}(`, + ` client: RuntimeClient,`, + ` options?: Partial>,`, + `): CreateMutationOptions<${responseType}, unknown, ${requestType}> {`, + ` return {`, + ` mutationFn: (request) => ${fullName}(client, request),`, + ` ...options,`, + ` };`, + `}`, + ``, + ); + + // Mutation hook + lines.push( + `export function ${mutHookName}(`, + ` client: RuntimeClient,`, + ` options?: Partial>,`, + ` queryClient?: QueryClient,`, + `): CreateMutationResult<${responseType}, unknown, ${requestType}> {`, + ` const mutationOptions = ${mutOptsFnName}(client, options);`, + ` return createMutation(mutationOptions, queryClient);`, + `}`, + ``, + ); + } + + return lines.join("\n"); +} + +function generateIndex(serviceNames: string[]): string { + const lines = [`// Generated by generate-query-hooks.ts — DO NOT EDIT`, ``]; + for (const name of serviceNames) { + lines.push(`export * from "./${toKebabCase(name)}";`); + } + lines.push(``); + return lines.join("\n"); +} + +// --- Main --- + +const outDir = path.resolve( + __dirname, + "../src/runtime-client/v2/gen", +); + +fs.mkdirSync(outDir, { recursive: true }); + +const services: { descriptor: ServiceDef; name: string }[] = [ + { descriptor: QueryService as unknown as ServiceDef, name: "QueryService" }, + { + descriptor: RuntimeService as unknown as ServiceDef, + name: "RuntimeService", + }, + { + descriptor: ConnectorService as unknown as ServiceDef, + name: "ConnectorService", + }, +]; + +let totalQueries = 0; +let totalMutations = 0; + +for (const { descriptor, name } of services) { + const code = generateServiceFile(descriptor); + const fileName = `${toKebabCase(name)}.ts`; + const filePath = path.join(outDir, fileName); + fs.writeFileSync(filePath, code); + + const methods = extractMethods(descriptor); + const queries = methods.filter((m) => m.classification === "query").length; + const mutations = methods.filter( + (m) => m.classification === "mutation", + ).length; + const skipped = Object.keys(descriptor.methods).length - methods.length; + totalQueries += queries; + totalMutations += mutations; + + console.log( + ` ${fileName}: ${queries} queries, ${mutations} mutations, ${skipped} skipped`, + ); +} + +// Generate barrel index +const indexCode = generateIndex(services.map((s) => s.name)); +fs.writeFileSync(path.join(outDir, "index.ts"), indexCode); + +console.log( + `\nTotal: ${totalQueries} queries, ${totalMutations} mutations`, +); +console.log(`Output: ${outDir}`); diff --git a/web-common/src/components/preview-table/ConnectedPreviewTable.svelte b/web-common/src/components/preview-table/ConnectedPreviewTable.svelte index cff98a5f205..ed2ac1e0d94 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,7 +21,7 @@ let columns: VirtualizedTableColumns[] | undefined; let rows: V1TableRowsResponseDataItem[] | undefined; - $: ({ instanceId } = $runtime); + $: ({ instanceId } = runtimeClient); $: columnsQuery = createQueryServiceTableColumns(instanceId, table, { connector, 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/canvas/CanvasBuilder.svelte b/web-common/src/features/canvas/CanvasBuilder.svelte index a121bf6a4b1..c3f57950eb3 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,7 +68,7 @@ $: layoutRows = $_rows; - $: ({ instanceId } = $runtime); + $: ({ instanceId } = runtimeClient); $: components = $componentsStore; 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/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..ecd092e461d 100644 --- a/web-common/src/features/canvas/selector.ts +++ b/web-common/src/features/canvas/selector.ts @@ -4,7 +4,6 @@ import { } from "@rilldata/web-common/features/entity-management/resource-selectors"; import { createQueryServiceResolveCanvas, - getQueryServiceResolveCanvasQueryOptions, type RpcStatus, type V1CanvasSpec, type V1MetricsView, @@ -12,14 +11,11 @@ import { 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 { 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 @@ -132,35 +128,3 @@ export function useCanvas( 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..4434a04fde7 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); @@ -70,7 +69,7 @@ export function setCanvasStore( const canvasEntity = new CanvasEntity(canvasName, instanceId, response); const store: CanvasStore = { - runtime: runtime, + runtimeClient: runtimeClient!, canvasEntity, queryClient, }; diff --git a/web-common/src/features/canvas/stores/time-state.ts b/web-common/src/features/canvas/stores/time-state.ts index 81f71ed2b93..437c1556c8e 100644 --- a/web-common/src/features/canvas/stores/time-state.ts +++ b/web-common/src/features/canvas/stores/time-state.ts @@ -156,7 +156,12 @@ export class TimeState { } const promises = metricsViewsWithTimeSeries.map((mvName) => { - return deriveInterval(range, mvName, timeZone); + return deriveInterval( + range, + this.parent.instanceId, + mvName, + timeZone, + ); }); Promise.all(promises) 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/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..5c9d46c54eb 100644 --- a/web-common/src/features/chat/core/context/metadata.ts +++ b/web-common/src/features/chat/core/context/metadata.ts @@ -1,7 +1,7 @@ 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, @@ -14,7 +14,7 @@ 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,20 +34,22 @@ 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; + const instanceId = client.instanceId; return derived( [metricsViewsQuery, canvasResourcesQuery], 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..b66f5555de3 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,21 @@ 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; + const instanceId = client.instanceId; return derived( [canvasResourcesQuery, lastUsedCanvasNameStore, activeCanvasNameStore], @@ -133,9 +134,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..ceba9c6aaa2 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,19 @@ 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 instanceId = client.instanceId; const models = modelResourcesResp.data ?? []; const modelPickerItems: PickerItem[] = []; const modelQueryOptions: ReturnType< 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..8937ae816ff 100644 --- a/web-common/src/features/chat/core/conversation-manager.ts +++ b/web-common/src/features/chat/core/conversation-manager.ts @@ -4,6 +4,7 @@ import { type RpcStatus, type V1ListConversationsResponse, } from "@rilldata/web-common/runtime-client"; +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; @@ -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..437e814e50a 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"; @@ -95,7 +93,7 @@ function mockForkEmptyResponse() { } function createConversation(conversationId: string = ORIGINAL_CONVERSATION_ID) { - return new Conversation(INSTANCE_ID, conversationId); + return new Conversation(mockRuntimeClient, conversationId); } async function sendMessageAndIgnoreStreamError( diff --git a/web-common/src/features/chat/core/conversation.ts b/web-common/src/features/chat/core/conversation.ts index fec4fa10153..a2a5d3c6bad 100644 --- a/web-common/src/features/chat/core/conversation.ts +++ b/web-common/src/features/chat/core/conversation.ts @@ -11,7 +11,7 @@ import { type V1GetConversationResponse, type V1Message, } 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 { SSEFetchClient, SSEHttpError, @@ -80,6 +80,14 @@ export class Conversation { // 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, ) { @@ -334,7 +342,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 +361,7 @@ export class Conversation { await this.sseClient!.start(baseUrl, { method: "POST", body: requestBody, + getJwt: () => this.client.getJwt(), }); } diff --git a/web-common/src/features/chat/core/messages/Messages.svelte b/web-common/src/features/chat/core/messages/Messages.svelte index 877603d5a1d..3db9cfd3808 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/connectors/code-utils.spec.ts b/web-common/src/features/connectors/code-utils.spec.ts index 0f9f840b39f..1062b04fcbb 100644 --- a/web-common/src/features/connectors/code-utils.spec.ts +++ b/web-common/src/features/connectors/code-utils.spec.ts @@ -842,6 +842,7 @@ describe("updateDotEnvWithSecrets", () => { sql: "SELECT 1", }; const { newBlob } = await updateDotEnvWithSecrets( + "test-instance-id", mockQueryClient as any, connector, formValues, @@ -865,6 +866,7 @@ describe("updateDotEnvWithSecrets", () => { }, }; const { newBlob } = await updateDotEnvWithSecrets( + "test-instance-id", mockQueryClient as any, connector, formValues, @@ -881,6 +883,7 @@ describe("updateDotEnvWithSecrets", () => { const connector: V1ConnectorDriver = { name: "clickhouse" }; const formValues: Record = { password: "new_pw" }; const { newBlob, originalBlob } = await updateDotEnvWithSecrets( + "test-instance-id", mockQueryClient as any, connector, formValues, @@ -898,6 +901,7 @@ describe("updateDotEnvWithSecrets", () => { const connector: V1ConnectorDriver = { name: "clickhouse" }; const formValues: Record = { password: "new_value" }; const { newBlob } = await updateDotEnvWithSecrets( + "test-instance-id", mockQueryClient as any, connector, formValues, @@ -915,6 +919,7 @@ describe("updateDotEnvWithSecrets", () => { dsn: undefined, }; const { newBlob } = await updateDotEnvWithSecrets( + "test-instance-id", mockQueryClient as any, connector, formValues, @@ -932,6 +937,7 @@ describe("updateDotEnvWithSecrets", () => { ], }; const { newBlob } = await updateDotEnvWithSecrets( + "test-instance-id", mockQueryClient as any, connector, formValues, @@ -949,6 +955,7 @@ describe("updateDotEnvWithSecrets", () => { headers: [{ key: "Authorization", value: "Bearer abc123" }], }; const { newBlob } = await updateDotEnvWithSecrets( + "test-instance-id", mockQueryClient as any, connector, formValues, @@ -965,6 +972,7 @@ describe("updateDotEnvWithSecrets", () => { headers: [{ key: "X-API-Key", value: "raw_api_key_value" }], }; const { newBlob } = await updateDotEnvWithSecrets( + "test-instance-id", mockQueryClient as any, connector, formValues, @@ -980,6 +988,7 @@ describe("updateDotEnvWithSecrets", () => { headers: [{ key: "Authorization", value: "Token secret_tok" }], }; const { newBlob } = await updateDotEnvWithSecrets( + "test-instance-id", mockQueryClient as any, connector, formValues, @@ -999,6 +1008,7 @@ describe("updateDotEnvWithSecrets", () => { ], }; const { newBlob } = await updateDotEnvWithSecrets( + "test-instance-id", mockQueryClient as any, connector, formValues, @@ -1010,6 +1020,7 @@ describe("updateDotEnvWithSecrets", () => { it("should invalidate cache before reading .env", async () => { const connector: V1ConnectorDriver = { name: "clickhouse" }; await updateDotEnvWithSecrets( + "test-instance-id", mockQueryClient as any, connector, { password: "pw" }, @@ -1030,6 +1041,7 @@ describe("updateDotEnvWithSecrets", () => { const connector: V1ConnectorDriver = { name: "clickhouse" }; const { newBlob, originalBlob } = await updateDotEnvWithSecrets( + "test-instance-id", mockQueryClient as any, connector, { password: "pw" }, @@ -1047,6 +1059,7 @@ describe("updateDotEnvWithSecrets", () => { const connector: V1ConnectorDriver = { name: "clickhouse" }; await expect( updateDotEnvWithSecrets( + "test-instance-id", mockQueryClient as any, connector, { password: "pw" }, @@ -1069,6 +1082,7 @@ describe("updateDotEnvWithSecrets", () => { dsn: "clickhouse://...", }; const { newBlob } = await updateDotEnvWithSecrets( + "test-instance-id", 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 8a90f10aa27..88bd6b2f81b 100644 --- a/web-common/src/features/connectors/code-utils.ts +++ b/web-common/src/features/connectors/code-utils.ts @@ -1,12 +1,10 @@ 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 { fileArtifacts } from "@rilldata/web-common/features/entity-management/file-artifacts"; import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors"; import { eventBus } from "@rilldata/web-common/lib/event-bus/event-bus"; @@ -256,6 +254,7 @@ driver: ${driverName}`; } export async function updateDotEnvWithSecrets( + instanceId: string, queryClient: QueryClient, connector: V1ConnectorDriver, formValues: Record, @@ -264,8 +263,6 @@ export async function updateDotEnvWithSecrets( schema?: { properties?: Record }; }, ): Promise<{ newBlob: string; originalBlob: string }> { - 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({ @@ -495,11 +492,11 @@ export function makeEnvVarKey( } export async function updateRillYAMLWithOlapConnector( + instanceId: string, queryClient: QueryClient, newConnector: string, ): Promise { // Get the existing rill.yaml file - const instanceId = get(runtime).instanceId; const file = await queryClient.fetchQuery({ queryKey: getRuntimeServiceGetFileQueryKey(instanceId, { path: "rill.yaml", @@ -530,14 +527,13 @@ export function replaceOlapConnectorInYAML( } export async function createYamlModelFromTable( + instanceId: string, 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); @@ -601,6 +597,7 @@ export async function createYamlModelFromTable( } export async function createSqlModelFromTable( + instanceId: string, queryClient: QueryClient, connector: string, database: string, @@ -608,8 +605,6 @@ export async function createSqlModelFromTable( table: string, addDevLimit: boolean = true, ): Promise<[string, string]> { - const instanceId = get(runtime).instanceId; - // Get driver name const analyzeConnectorsQueryKey = getRuntimeServiceAnalyzeConnectorsQueryKey(instanceId); diff --git a/web-common/src/features/connectors/explorer/ConnectorEntry.svelte b/web-common/src/features/connectors/explorer/ConnectorEntry.svelte index 56214e50d8b..496d34ab8dd 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..e3b277492c0 100644 --- a/web-common/src/features/connectors/explorer/DatabaseEntry.svelte +++ b/web-common/src/features/connectors/explorer/DatabaseEntry.svelte @@ -4,21 +4,23 @@ import CaretDownIcon from "../../../components/icons/CaretDownIcon.svelte"; 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, ); @@ -60,7 +62,6 @@ {:else} {#each data as schema (schema)} {#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..763a1aaccc7 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.instanceId, 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..d60c462a6f3 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( + instanceId, queryClient, connector, database, @@ -62,6 +64,7 @@ } else if (showGenerateModel) { await handleCreateModel(() => createYamlModelFromTable( + instanceId, 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..4dac3cbe492 100644 --- a/web-common/src/features/connectors/explorer/TableSchema.svelte +++ b/web-common/src/features/connectors/explorer/TableSchema.svelte @@ -2,16 +2,17 @@ import Tooltip from "../../../components/tooltip/Tooltip.svelte"; import TooltipContent from "../../../components/tooltip/TooltipContent.svelte"; import { useGetTable } from "../selectors"; - import { runtime } from "../../../runtime-client/runtime-store"; + import { useRuntimeClient } from "../../../runtime-client/v2"; export let connector: string; export let database: string = ""; // The backend interprets an empty string as the default database export let databaseSchema: string = ""; // The backend interprets an empty string as the default schema export let table: string; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); + $: newTableQuery = useGetTable( - instanceId, + client, connector, database, databaseSchema, diff --git a/web-common/src/features/connectors/explorer/TableWorkspaceHeader.svelte b/web-common/src/features/connectors/explorer/TableWorkspaceHeader.svelte index a5bf922344c..3a3d1426c5c 100644 --- a/web-common/src/features/connectors/explorer/TableWorkspaceHeader.svelte +++ b/web-common/src/features/connectors/explorer/TableWorkspaceHeader.svelte @@ -9,7 +9,7 @@ import { WorkspaceHeader } from "../../../layout/workspace"; import { BehaviourEventMedium } from "../../../metrics/service/BehaviourEventTypes"; import { MetricsEventSpace } from "../../../metrics/service/MetricsTypes"; - import { runtime } from "../../../runtime-client/runtime-store"; + import { useRuntimeClient } from "../../../runtime-client/v2"; import { ResourceKind } from "../../entity-management/resource-selectors"; import { featureFlags } from "../../feature-flags"; import { useCreateMetricsViewFromTableUIAction } from "../../metrics-views/ai-generation/generateMetricsView"; @@ -21,10 +21,11 @@ const { ai } = featureFlags; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); $: createMetricsViewFromTable = useCreateMetricsViewFromTableUIAction( - instanceId, + client, + client.instanceId, connector, database, databaseSchema, diff --git a/web-common/src/features/connectors/olap/TableInspector.svelte b/web-common/src/features/connectors/olap/TableInspector.svelte index c7f6c94e7aa..2fa7214fdcf 100644 --- a/web-common/src/features/connectors/olap/TableInspector.svelte +++ b/web-common/src/features/connectors/olap/TableInspector.svelte @@ -8,8 +8,8 @@ import { createQueryServiceTableCardinality, createQueryServiceTableColumns, - } from "@rilldata/web-common/runtime-client"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + } from "@rilldata/web-common/runtime-client/v2/gen"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { slide } from "svelte/transition"; export let connector: string; @@ -21,15 +21,17 @@ let isReconciling = false; let hasUnsavedChanges = false; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); - $: cardinalityQuery = createQueryServiceTableCardinality(instanceId, table, { + $: cardinalityQuery = createQueryServiceTableCardinality(client, { + tableName: table, connector, database, databaseSchema, }); - $: profileColumnsQuery = createQueryServiceTableColumns(instanceId, table, { + $: profileColumnsQuery = createQueryServiceTableColumns(client, { + tableName: table, connector, database, databaseSchema, diff --git a/web-common/src/features/connectors/selectors.ts b/web-common/src/features/connectors/selectors.ts index ae0aaeed023..c35aee26612 100644 --- a/web-common/src/features/connectors/selectors.ts +++ b/web-common/src/features/connectors/selectors.ts @@ -3,15 +3,18 @@ import { createInfiniteQuery } from "@tanstack/svelte-query"; import { derived } from "svelte/store"; import { type V1TableInfo, - createConnectorServiceListDatabaseSchemas, - createConnectorServiceGetTable, createRuntimeServiceGetInstance, type V1GetResourceResponse, getRuntimeServiceGetResourceQueryKey, runtimeServiceGetResource, type RpcStatus, } from "../../runtime-client"; -import { connectorServiceListTables } from "../../runtime-client/gen/connector-service/connector-service"; +import type { RuntimeClient } from "../../runtime-client/v2"; +import { + createConnectorServiceListDatabaseSchemas, + createConnectorServiceGetTable, + connectorServiceListTables, +} from "../../runtime-client/v2/gen"; import { ResourceKind } from "../entity-management/resource-selectors"; import type { ErrorType } from "@rilldata/web-common/runtime-client/http-client"; @@ -104,19 +107,19 @@ export function useIsModelingSupportedForDefaultOlapDriverOLAP( * The backend returns all schemas across databases; filtering is applied client-side. */ export function useListDatabaseSchemas( - instanceId: string, + client: RuntimeClient, connector: string, database?: string, enabled: boolean = true, ) { return createConnectorServiceListDatabaseSchemas( + client, { - instanceId, connector, }, { query: { - enabled: !!instanceId && !!connector && enabled, + enabled: !!client.instanceId && !!connector && enabled, select: (data) => { const allSchemas = data.databaseSchemas ?? []; @@ -152,7 +155,7 @@ export function useListDatabaseSchemas( * Infinite tables loader using pageToken cursor */ export function useInfiniteListTables( - instanceId: string, + client: RuntimeClient, connector: string, database: string, databaseSchema: string, @@ -162,11 +165,17 @@ export function useInfiniteListTables( return createInfiniteQuery({ queryKey: [ "/v1/connectors/tables", - { instanceId, connector, database, databaseSchema, pageSize }, + { + instanceId: client.instanceId, + connector, + database, + databaseSchema, + pageSize, + }, ], enabled: enabled && - !!instanceId && + !!client.instanceId && !!connector && (!!database || database === "") && databaseSchema !== undefined, @@ -175,15 +184,15 @@ export function useInfiniteListTables( lastPage?.nextPageToken || undefined, queryFn: ({ pageParam, signal }) => connectorServiceListTables( + client, { - instanceId, connector, database, databaseSchema, pageSize, pageToken: pageParam, }, - signal, + { signal }, ), select: (data: any) => ({ tables: data.pages.flatMap( @@ -202,15 +211,15 @@ export function useInfiniteListTables( * Called when a table is selected/expanded */ export function useGetTable( - instanceId: string, + client: RuntimeClient, connector: string, database: string, databaseSchema: string, table: string, ) { return createConnectorServiceGetTable( + client, { - instanceId, connector, database, databaseSchema, @@ -219,7 +228,7 @@ export function useGetTable( { query: { enabled: - !!instanceId && + !!client.instanceId && !!connector && !!table && database !== undefined && diff --git a/web-common/src/features/dashboards/aggregation-request-builder.spec.ts b/web-common/src/features/dashboards/aggregation-request-builder.spec.ts index bd634aa8ef1..f1e0fe35295 100644 --- a/web-common/src/features/dashboards/aggregation-request-builder.spec.ts +++ b/web-common/src/features/dashboards/aggregation-request-builder.spec.ts @@ -211,6 +211,7 @@ describe("Report rows and columns", () => { it(testCase.title, async () => { await runTest(testCase, (exploreState, timeControlState) => getDimensionTableAggregationRequestForTime({ + instanceId: "", metricsViewName: AD_BIDS_METRICS_NAME, exploreState, timeRange: { @@ -369,6 +370,7 @@ describe("Report rows and columns", () => { testCase, (exploreState, timeControlState) => getTDDAggregationRequest({ + instanceId: "", metricsViewName: AD_BIDS_METRICS_NAME, exploreState, timeControlState, @@ -504,6 +506,7 @@ describe("Report rows and columns", () => { testCase, (exploreState, timeControlState) => getPivotAggregationRequest({ + instanceId: "", metricsViewName: AD_BIDS_METRICS_NAME, timeDimension: AD_BIDS_METRICS_3_MEASURES_DIMENSIONS_WITH_TIME.timeDimension!, diff --git a/web-common/src/features/dashboards/big-number/MeasuresContainer.svelte b/web-common/src/features/dashboards/big-number/MeasuresContainer.svelte index 3243b08d5d0..f2fa4dd9a36 100644 --- a/web-common/src/features/dashboards/big-number/MeasuresContainer.svelte +++ b/web-common/src/features/dashboards/big-number/MeasuresContainer.svelte @@ -4,7 +4,7 @@ import { useTimeControlStore } from "@rilldata/web-common/features/dashboards/time-controls/time-control-store"; import { EntityStatus } from "@rilldata/web-common/features/entity-management/types"; import { createQueryServiceMetricsViewAggregation } from "@rilldata/web-common/runtime-client"; - import { runtime } from "../../../runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { MEASURE_CONFIG } from "../config"; import MeasureBigNumber from "./MeasureBigNumber.svelte"; import DashboardVisibilityDropdown from "@rilldata/web-common/components/menu/DashboardVisibilityDropdown.svelte"; @@ -41,7 +41,8 @@ }, } = getStateManagers(); - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); + const { instanceId } = client; const timeControlsStore = useTimeControlStore(getStateManagers()); diff --git a/web-common/src/features/dashboards/chat-context.ts b/web-common/src/features/dashboards/chat-context.ts index d63d436ce7c..bba6ad8b1bb 100644 --- a/web-common/src/features/dashboards/chat-context.ts +++ b/web-common/src/features/dashboards/chat-context.ts @@ -10,31 +10,35 @@ import type { RuntimeServiceCompleteBody, V1AnalystAgentContext, } from "@rilldata/web-common/runtime-client"; +import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { derived, type Readable } from "svelte/store"; -const activeExploreContextStore = getActiveExploreContext(); - -export const dashboardChatConfig = { - agent: ToolName.ANALYST_AGENT, - additionalContextStoreGetter: () => activeExploreContextStore, // TODO: add canvas context as well - emptyChatLabel: "Happy to help explore your data", - placeholder: - "Type a question, or press @ to insert a metric, dimension, or measure.", - minChatHeight: "min-h-[4rem]", -} satisfies ChatConfig; +export function createDashboardChatConfig(client: RuntimeClient): ChatConfig { + const activeExploreContextStore = getActiveExploreContext(client); + return { + agent: ToolName.ANALYST_AGENT, + additionalContextStoreGetter: () => activeExploreContextStore, + emptyChatLabel: "Happy to help explore your data", + placeholder: + "Type a question, or press @ to insert a metric, dimension, or measure.", + minChatHeight: "min-h-[4rem]", + }; +} /** * Creates a store that contains the active explore context sent to the Complete API. * It returns RuntimeServiceCompleteBody with V1AnalystAgentContext that is passed to the API. */ -function getActiveExploreContext(): Readable< - Partial -> { +function getActiveExploreContext( + client: RuntimeClient, +): Readable> { const exploreNameStore = getExploreNameStore(); const exploreState = useStableExploreState(exploreNameStore); - const timeControlsStore = - createStableTimeControlStoreFromName(exploreNameStore); + const timeControlsStore = createStableTimeControlStoreFromName( + client, + exploreNameStore, + ); return derived( [exploreNameStore, exploreState, timeControlsStore], diff --git a/web-common/src/features/dashboards/dashboard-fetch-mocks.ts b/web-common/src/features/dashboards/dashboard-fetch-mocks.ts index 95d4dc1bc53..e5e63c90c9b 100644 --- a/web-common/src/features/dashboards/dashboard-fetch-mocks.ts +++ b/web-common/src/features/dashboards/dashboard-fetch-mocks.ts @@ -123,8 +123,17 @@ export class DashboardFetchMocks { ); } - private async fetchMock(url: string, body: string | undefined) { + private async fetchMock(url: string, body: string | Uint8Array | undefined) { const u = new URL(url); + + // ConnectRPC routes: POST to /rill.runtime.v1.{Service}/{Method} + const connectMatch = u.pathname.match( + /^\/rill\.runtime\.v1\.(\w+)\/(\w+)$/, + ); + if (connectMatch) { + return this.handleConnectRequest(connectMatch[1], connectMatch[2], body); + } + const [, , , , type, ...parts] = u.pathname.split("/"); let key: string; @@ -162,8 +171,9 @@ export class DashboardFetchMocks { ok: true, json: () => { if (body && parts[2] === "aggregation") { + const bodyText = typeof body === "string" ? body : ""; for (const { regex, response } of this.aggregationRequestMocks) { - if (!regex.test(body)) continue; + if (!regex.test(bodyText)) continue; return response; } } @@ -172,4 +182,69 @@ export class DashboardFetchMocks { headers: new Map([["content-type", "application/json"]]), }; } + + private async handleConnectRequest( + service: string, + method: string, + body: string | Uint8Array | undefined, + ) { + await asyncWait(1); + + // ConnectRPC may send body as Uint8Array or other typed array + let bodyStr: string; + if (typeof body === "string") { + bodyStr = body; + } else if (body && typeof body === "object") { + bodyStr = new TextDecoder().decode(body as ArrayBufferView); + } else { + bodyStr = ""; + } + const parsed = bodyStr ? JSON.parse(bodyStr) : {}; + let responseData: unknown; + + if (service === "RuntimeService" && method === "GetExplore") { + responseData = this.responses.get(`resources__explore__${parsed.name}`); + } else if ( + service === "QueryService" && + method === "MetricsViewTimeRange" + ) { + const stored = this.responses.get( + `queries__metrics-views__time-range-summary__${parsed.metricsViewName}`, + ); + if (stored?.timeRangeSummary) { + // Convert short dates to RFC 3339 for proto Timestamp compatibility + const s = stored.timeRangeSummary; + responseData = { + timeRangeSummary: { + ...s, + min: s.min && !s.min.includes("T") ? s.min + "T00:00:00Z" : s.min, + max: s.max && !s.max.includes("T") ? s.max + "T00:00:00Z" : s.max, + }, + }; + } else { + responseData = stored; + } + } else if ( + service === "QueryService" && + method === "MetricsViewAggregation" + ) { + for (const { regex, response } of this.aggregationRequestMocks) { + if (regex.test(bodyStr)) { + responseData = response; + break; + } + } + } + + // ConnectRPC expects status, ok, headers (Headers), json(), arrayBuffer() + const jsonBody = responseData ?? {}; + return { + status: 200, + ok: true, + headers: new Headers({ "content-type": "application/json" }), + json: async () => jsonBody, + arrayBuffer: async () => + new TextEncoder().encode(JSON.stringify(jsonBody)).buffer, + }; + } } diff --git a/web-common/src/features/dashboards/dimension-search/GlobalDimensionSearchResults.svelte b/web-common/src/features/dashboards/dimension-search/GlobalDimensionSearchResults.svelte index 21e7b59f6ba..89eaaee13be 100644 --- a/web-common/src/features/dashboards/dimension-search/GlobalDimensionSearchResults.svelte +++ b/web-common/src/features/dashboards/dimension-search/GlobalDimensionSearchResults.svelte @@ -6,7 +6,7 @@ useDimensionSearchResults, } from "@rilldata/web-common/features/dashboards/dimension-search/useDimensionSearchResults"; 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 { DropdownMenu, DropdownMenuContent, @@ -26,7 +26,8 @@ validSpecStore, } = getStateManagers(); - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); + const { instanceId } = client; let results: ReturnType; $: if ( diff --git a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte index 068a6809be3..d74150f4b86 100644 --- a/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte +++ b/web-common/src/features/dashboards/dimension-table/DimensionDisplay.svelte @@ -16,7 +16,7 @@ type V1MetricsViewAggregationMeasure, type 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 { getComparisonRequestMeasures } from "../dashboard-utils"; import { mergeDimensionAndMeasureFilters } from "../filters/measure-filters/measure-filter-utils"; import { getSort } from "../leaderboard/leaderboard-utils"; @@ -64,12 +64,13 @@ $: metricsViewSpec = $validSpecStore.data?.metricsView ?? {}; - $: ({ name: dimensionName = "" } = dimension); + const client = useRuntimeClient(); + const { instanceId } = client; - $: ({ instanceId } = $runtime); + $: ({ name: dimensionName = "" } = dimension); $: selectedValues = selectedDimensionValues( - $runtime.instanceId, + client.instanceId, [metricsViewName], $dashboardStore.whereFilter, dimensionName, diff --git a/web-common/src/features/dashboards/dimension-table/dimension-table-export.ts b/web-common/src/features/dashboards/dimension-table/dimension-table-export.ts index 5a08a66f914..2f0bed2b820 100644 --- a/web-common/src/features/dashboards/dimension-table/dimension-table-export.ts +++ b/web-common/src/features/dashboards/dimension-table/dimension-table-export.ts @@ -18,7 +18,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"; @@ -69,6 +68,7 @@ export function getDimensionTableExportQuery( const query: V1Query = { metricsViewAggregationRequest: getDimensionTableAggregationRequestForTime({ + instanceId: ctx.runtimeClient.instanceId, metricsViewName, exploreState, timeRange, @@ -81,12 +81,14 @@ export function getDimensionTableExportQuery( } export function getDimensionTableAggregationRequestForTime({ + instanceId, metricsViewName, exploreState, timeRange, comparisonTimeRange, dimensionSearchText, }: { + instanceId: string; metricsViewName: string; exploreState: ExploreState; timeRange: V1TimeRange; @@ -128,7 +130,7 @@ export function getDimensionTableAggregationRequestForTime({ ); return { - instanceId: get(runtime).instanceId, + instanceId, metricsView: metricsViewName, dimensions: [ { diff --git a/web-common/src/features/dashboards/filters/ExploreFilterChipsReadOnly.svelte b/web-common/src/features/dashboards/filters/ExploreFilterChipsReadOnly.svelte index 72a2c500544..7f7772b4e5d 100644 --- a/web-common/src/features/dashboards/filters/ExploreFilterChipsReadOnly.svelte +++ b/web-common/src/features/dashboards/filters/ExploreFilterChipsReadOnly.svelte @@ -6,6 +6,7 @@ V1Expression, V1TimeRange, } from "@rilldata/web-common/runtime-client"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { writable } from "svelte/store"; export let metricsViewNames: string[]; @@ -19,11 +20,16 @@ export let queryTimeStart: string | undefined = undefined; export let queryTimeEnd: string | undefined = undefined; + const runtimeClient = useRuntimeClient(); + const metricsViewNamesStore = writable([] as string[]); $: metricsViewNamesStore.set(metricsViewNames); const combinedMeasuresAndDimensions = - getCombinedMeasuresAndDimensionsForMetricsViews(metricsViewNamesStore); + getCombinedMeasuresAndDimensionsForMetricsViews( + runtimeClient, + metricsViewNamesStore, + ); $: ({ measures, dimensions } = $combinedMeasuresAndDimensions); diff --git a/web-common/src/features/dashboards/filters/Filters.svelte b/web-common/src/features/dashboards/filters/Filters.svelte index 349089f4129..45abbfcb8c6 100644 --- a/web-common/src/features/dashboards/filters/Filters.svelte +++ b/web-common/src/features/dashboards/filters/Filters.svelte @@ -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,7 +101,8 @@ let showDefaultItem = false; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); + const { instanceId } = client; $: timeRangeQuery = useMetricsViewTimeRange(instanceId, metricsViewName); @@ -234,6 +235,7 @@ const { interval, grain } = await deriveInterval( timeRangeName, + instanceId, metricsViewName, activeTimeZone, column, @@ -296,6 +298,7 @@ const { interval, grain } = await deriveInterval( alias, + instanceId, 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..1f092429409 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,8 @@ : []; let curPinned = filterData.pinned; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); + const { instanceId } = client; $: ({ 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..22882de39af 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,8 @@ export let timeEnd: string | undefined; export let pinned = false; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); + const { instanceId } = client; $: effectiveLabel = isInclude ? label : `Exclude ${label}`; $: sanitisedSearchText = inputText?.replace(/^%/, "").replace(/%$/, ""); 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..af799bfa41d 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,7 +18,8 @@ let viewAsMenuOpen = false; let open = false; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); + const { instanceId } = client; $: mockUsers = useMockUsers(instanceId); @@ -44,7 +45,7 @@ active={viewAsMenuOpen} removeTooltipText="Clear view" onRemove={() => { - updateDevJWT(queryClient, null); + updateDevJWT(queryClient, instanceId, null, client); }} >
@@ -61,7 +62,7 @@ {#each $mockUsers.data as user (user?.email)} { - updateDevJWT(queryClient, user); + updateDevJWT(queryClient, instanceId, user, client); }} 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..a32027c8390 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,11 @@ 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, + instanceId: string, + runtimeClient?: RuntimeClient, +) { beforeNavigate(({ to, from }) => { if (!to?.params || !from?.params) return; @@ -21,7 +26,9 @@ export function resetSelectedMockUserAfterNavigate(queryClient: QueryClient) { from.params.name !== to.params.name && get(selectedMockUserStore) !== null ) { - updateDevJWT(queryClient, null).catch(console.error); + updateDevJWT(queryClient, instanceId, null, runtimeClient).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..e520327be6f 100644 --- a/web-common/src/features/dashboards/granular-access-policies/updateDevJWT.ts +++ b/web-common/src/features/dashboards/granular-access-policies/updateDevJWT.ts @@ -6,17 +6,21 @@ import type { MockUser } from "@rilldata/web-common/features/dashboards/granular 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, + instanceId: string, mockUser: MockUser | null, + runtimeClient?: RuntimeClient, ) { selectedMockUserStore.set(mockUser); if (mockUser === null) { selectedMockUserJWT.set(null); + runtimeClient?.updateJwt(undefined, "user"); + // BRIDGE (temporary): keep global store in sync for unmigrated consumers runtime.update((runtimeState) => { runtimeState.jwt = undefined; return runtimeState; @@ -36,7 +40,8 @@ export async function updateDevJWT( if (!jwt) throw new Error("No JWT returned"); selectedMockUserJWT.set(jwt); - + runtimeClient?.updateJwt(jwt, "mock"); + // BRIDGE (temporary): keep global store in sync for unmigrated consumers runtime.update((runtimeState) => { runtimeState.jwt = { token: jwt, @@ -50,5 +55,5 @@ export async function updateDevJWT( } } - return invalidateAllMetricsViews(queryClient, get(runtime).instanceId); + return invalidateAllMetricsViews(queryClient, instanceId); } diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardDisplay.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardDisplay.svelte index 488c2bc9685..62f27dbb32a 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,10 @@ dashboardStore, } = StateManagers; - let parentElement: HTMLDivElement; + const client = useRuntimeClient(); + const { instanceId } = client; - $: ({ instanceId } = $runtime); + let parentElement: HTMLDivElement; // Reset column widths when the measure changes $: if ($leaderboardSortByMeasureName) { @@ -98,7 +99,7 @@ {parentElement} {timeControlsReady} selectedValues={selectedDimensionValues( - $runtime.instanceId, + client.instanceId, [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..e2a68c27113 100644 --- a/web-common/src/features/dashboards/pivot/pivot-data-store.ts +++ b/web-common/src/features/dashboards/pivot/pivot-data-store.ts @@ -724,6 +724,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-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..6bc0105970c 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 { createQueryServiceMetricsViewAggregation } from "@rilldata/web-common/runtime-client/v2/gen/query-service"; import { type CreateQueryResult, keepPreviousData, @@ -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/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..3787fd3511b 100644 --- a/web-common/src/features/dashboards/rows-viewer/RowsViewer.svelte +++ b/web-common/src/features/dashboards/rows-viewer/RowsViewer.svelte @@ -7,7 +7,7 @@ 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 { writable } from "svelte/store"; import { useExploreState } from "web-common/src/features/dashboards/stores/dashboard-stores"; import { PreviewTable } from "../../../components/preview-table"; @@ -19,7 +19,8 @@ export let filters: V1Expression | undefined; export let timeRange: TimeRangeString; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); + const { instanceId } = client; const SAMPLE_SIZE = 10000; const FALLBACK_SAMPLE_SIZE = 1000; diff --git a/web-common/src/features/dashboards/rows-viewer/RowsViewerAccordion.svelte b/web-common/src/features/dashboards/rows-viewer/RowsViewerAccordion.svelte index da6a5e427db..87150ff1f75 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,8 @@ }, } = stateManagers; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); + const { instanceId } = client; $: exploreState = useExploreState(exploreName); $: ({ whereFilter, dimensionThresholdFilters } = $exploreState); @@ -131,7 +131,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..1e6238ae361 100644 --- a/web-common/src/features/dashboards/selectors.ts +++ b/web-common/src/features/dashboards/selectors.ts @@ -15,7 +15,6 @@ import { import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; import { createRuntimeServiceListResources, - getQueryServiceMetricsViewSchemaQueryOptions, getQueryServiceMetricsViewTimeRangeQueryOptions, getRuntimeServiceListResourcesQueryOptions, type RpcStatus, @@ -25,7 +24,7 @@ import { type V1MetricsViewTimeRangeResponse, type V1Resource, } 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 { createQuery, type CreateQueryOptions, @@ -53,23 +52,19 @@ export function useMetricsView( ); } -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.instanceId, + { + kind: ResourceKind.MetricsView, + }, + { + query: { + select: (data) => + data?.resources?.filter((res) => !!res.metricsView?.state?.validSpec), }, - ); - }); + }, + ); } export function useValidExplores(instanceId: string) { @@ -79,23 +74,6 @@ export function useValidExplores(instanceId: string) { ); } -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) => data?.resources?.filter((res) => !!res.canvas?.state?.validSpec), @@ -170,32 +148,30 @@ 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.instanceId, + metricsViewName, + {}, + { + query: { + enabled: !!metricsViewSpec.timeDimension, }, - ); - }, - ); + }, + ); + }); } export function hasValidMetricsViewTimeRange( @@ -232,33 +208,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, diff --git a/web-common/src/features/dashboards/selectors/index.ts b/web-common/src/features/dashboards/selectors/index.ts index dc434615b70..069a951ff98 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/v2/gen/query-service"; 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/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..e14d178c360 100644 --- a/web-common/src/features/dashboards/state-managers/loaders/DashboardStateManager.svelte +++ b/web-common/src/features/dashboards/state-managers/loaders/DashboardStateManager.svelte @@ -16,7 +16,7 @@ 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 { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { onDestroy } from "svelte"; export let exploreName: string; @@ -26,7 +26,9 @@ | undefined = undefined; export let disableMostRecentDashboardState: boolean = false; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); + const { instanceId } = client; + $: exploreSpecQuery = useExploreValidSpec(instanceId, exploreName); $: exploreSpec = $exploreSpecQuery.data?.explore ?? {}; $: metricsViewName = exploreSpec?.metricsView ?? ""; 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..6a1184b4d74 100644 --- a/web-common/src/features/dashboards/state-managers/loaders/DashboardStateSync.ts +++ b/web-common/src/features/dashboards/state-managers/loaders/DashboardStateSync.ts @@ -51,7 +51,7 @@ export class DashboardStateSync { } public constructor( - instanceId: string, + private readonly instanceId: string, metricsViewName: string, private readonly exploreName: string, private readonly extraPrefix: string | undefined, @@ -150,6 +150,7 @@ export class DashboardStateSync { initExploreState.selectedTimeRange, // initExploreState.selectedComparisonTimeRange, ] = await resolveTimeRanges( + this.instanceId, exploreSpec, [ initExploreState.selectedTimeRange, @@ -245,6 +246,7 @@ export class DashboardStateSync { partialExplore.selectedTimeRange, // partialExplore.selectedComparisonTimeRange, ] = await resolveTimeRanges( + this.instanceId, 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/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..879434458e5 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 @@ -85,10 +85,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.instanceId, [metricsViewName], exploreState.whereFilter, exploreState.selectedComparisonDimension ?? "", 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..7e1fbfea013 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/v2/gen/runtime-service"; +import { createQueryServiceMetricsViewTimeRange } from "@rilldata/web-common/runtime-client/v2/gen/query-service"; 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/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/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..a07b420820c 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 @@ -165,9 +165,16 @@ class MetricsTimeControls { private _subrange = new IntervalStore(); private _comparisonRange = new IntervalStore(); private _showComparison: Writable = writable(false); + private _instanceId: string; private _metricsViewName: string; - constructor(maxStart: DateTime, maxEnd: DateTime, metricsViewName: string) { + constructor( + maxStart: DateTime, + maxEnd: DateTime, + instanceId: string, + metricsViewName: string, + ) { + this._instanceId = instanceId; this._metricsViewName = metricsViewName; const maxInterval = Interval.fromDateTimes( maxStart.setZone("UTC"), @@ -188,7 +195,7 @@ class MetricsTimeControls { if (rightAnchor) { const interval = await deriveInterval( iso, - + this._instanceId, this._metricsViewName, get(this._zone).name, ); @@ -204,7 +211,7 @@ class MetricsTimeControls { if (rightAnchor) { const interval = await deriveInterval( name, - + this._instanceId, this._metricsViewName, get(this._zone).name, ); @@ -284,11 +291,21 @@ class MetricsTimeControls { class TimeControls { private _timeControls = new Map(); - get(metricsViewName: string, maxStart?: DateTime, maxEnd?: DateTime) { + get( + metricsViewName: string, + instanceId?: string, + maxStart?: DateTime, + maxEnd?: DateTime, + ) { let store = this._timeControls.get(metricsViewName); - if (!store && maxStart && maxEnd) { - store = new MetricsTimeControls(maxStart, maxEnd, metricsViewName); + if (!store && maxStart && maxEnd && instanceId) { + store = new MetricsTimeControls( + maxStart, + maxEnd, + instanceId, + metricsViewName, + ); this._timeControls.set(metricsViewName, store); } else if (!store) { throw new Error("TimeControls.get() called without maxStart and maxEnd"); @@ -312,7 +329,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 +347,7 @@ import { getDefaultRangeBuckets } from "@rilldata/web-common/lib/time/defaults"; export async function deriveInterval( name: RillPeriodToDate | RillPreviousPeriod | ISODurationString | string, + instanceId: string, metricsViewName: string, activeTimeZone: string, timeDimension?: string, @@ -352,7 +369,6 @@ 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({ 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..a2446e11ec4 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,7 +1,5 @@ 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, @@ -10,6 +8,7 @@ import { import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; export async function resolveTimeRanges( + instanceId: string, exploreSpec: V1ExploreSpec, timeRanges: (DashboardTimeControls | undefined)[], timeZone: string | undefined, @@ -40,7 +39,6 @@ export async function resolveTimeRanges( if (rillTimes.length === 0) return timeRangesToReturn; - const instanceId = get(runtime).instanceId; const metricsViewName = exploreSpec.metricsView!; try { 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..79e57ebdd7c 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, @@ -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 dd1e0457b23..7ea077047e7 100644 --- a/web-common/src/features/dashboards/time-series/MetricsTimeSeriesCharts.svelte +++ b/web-common/src/features/dashboards/time-series/MetricsTimeSeriesCharts.svelte @@ -40,7 +40,7 @@ type AvailableTimeGrain, } from "@rilldata/web-common/lib/time/types"; import { type MetricsViewSpecMeasure } from "@rilldata/web-common/runtime-client/gen/index.schemas"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store.ts"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { DateTime } from "luxon"; import { Button } from "../../../components/button"; import Pivot from "../../../components/icons/Pivot.svelte"; @@ -95,7 +95,8 @@ } = $timeControlsStore); $: activeTimeGrain = selectedTimeRange?.interval; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); + const { instanceId } = client; let scrubStart; let scrubEnd; 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 b3a534e9d96..3f62d9cd944 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/v2/gen/query-service"; 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.instanceId, [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..64a70342b07 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/v2/gen/query-service"; 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..c447850f633 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/v2/gen/query-service"; 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/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 32bde3d3b31..347ba0aad2f 100644 --- a/web-common/src/features/dashboards/workspace/Dashboard.svelte +++ b/web-common/src/features/dashboards/workspace/Dashboard.svelte @@ -12,7 +12,7 @@ import { readable, type Readable } from "svelte/store"; import { useExploreState } from "web-common/src/features/dashboards/stores/dashboard-stores"; import { DashboardState_ActivePage } from "../../../proto/gen/rill/ui/v1/dashboard_pb"; - import { runtime } from "../../../runtime-client/runtime-store"; + import { useRuntimeClient } from "../../../runtime-client/v2"; import { activeDashboardTheme } from "../../themes/active-dashboard-theme"; import { createResolvedThemeStore } from "../../themes/selectors"; import MeasuresContainer from "../big-number/MeasuresContainer.svelte"; @@ -52,7 +52,8 @@ let metricsWidth = DEFAULT_TIMESERIES_WIDTH; let resizing = false; - $: ({ instanceId } = $runtime); + const client = useRuntimeClient(); + const { instanceId } = client; $: ({ whereFilter, dimensionThresholdFilters, selectedTimeDimension } = $dashboardStore); diff --git a/web-common/src/features/editor/FileWorkspaceHeader.svelte b/web-common/src/features/editor/FileWorkspaceHeader.svelte index 74544c99511..a4f1c596170 100644 --- a/web-common/src/features/editor/FileWorkspaceHeader.svelte +++ b/web-common/src/features/editor/FileWorkspaceHeader.svelte @@ -2,7 +2,9 @@ import { goto } from "$app/navigation"; import { splitFolderAndFileName } from "@rilldata/web-common/features/entity-management/file-path-utils"; import { handleEntityRename } from "@rilldata/web-common/features/entity-management/ui-actions"; - import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; + + const runtimeClient = useRuntimeClient(); import { WorkspaceHeader } from "../../layout/workspace"; import type { ResourceKind } from "../entity-management/resource-selectors"; import { PROTECTED_FILES } from "../file-explorer/protected-paths"; @@ -18,7 +20,7 @@ $: [, fileName] = splitFolderAndFileName(filePath); $: isProtectedFile = PROTECTED_FILES.includes(filePath); - $: ({ instanceId } = $runtime); + $: ({ instanceId } = runtimeClient); const onChangeCallback = async (newTitle: string) => { const route = await handleEntityRename( diff --git a/web-common/src/features/entity-management/AddAssetButton.svelte b/web-common/src/features/entity-management/AddAssetButton.svelte index be170347e06..e23b139d8ed 100644 --- a/web-common/src/features/entity-management/AddAssetButton.svelte +++ b/web-common/src/features/entity-management/AddAssetButton.svelte @@ -17,7 +17,7 @@ createRuntimeServiceCreateDirectory, createRuntimeServicePutFile, } from "../../runtime-client"; - import { runtime } from "../../runtime-client/runtime-store"; + import { useRuntimeClient } from "../../runtime-client/v2"; import { useIsModelingSupportedForDefaultOlapDriverOLAP as useIsModelingSupportedForDefaultOlapDriver } from "../connectors/selectors"; import { directoryState } from "../file-explorer/directory-store"; import { createResourceAndNavigate } from "../file-explorer/new-files"; @@ -42,7 +42,8 @@ const createFile = createRuntimeServicePutFile(); const createFolder = createRuntimeServiceCreateDirectory(); - $: ({ instanceId } = $runtime); + const runtimeClient = useRuntimeClient(); + $: ({ instanceId } = runtimeClient); const { developerChat } = featureFlags; $: currentFile = $page.params.file; @@ -173,7 +174,8 @@ aria-label="Add Model" class="flex gap-x-2" disabled={!isModelingSupported} - on:click={() => createResourceAndNavigate(ResourceKind.Model)} + on:click={() => + createResourceAndNavigate(runtimeClient, ResourceKind.Model)} > createResourceAndNavigate(ResourceKind.MetricsView)} + on:click={() => + createResourceAndNavigate(runtimeClient, ResourceKind.MetricsView)} > { if (metricsViews.length === 1) { void createResourceAndNavigate( + runtimeClient, ResourceKind.Explore, metricsViews.pop(), ); @@ -233,7 +237,8 @@ createResourceAndNavigate(ResourceKind.Canvas)} + on:click={() => + createResourceAndNavigate(runtimeClient, ResourceKind.Canvas)} disabled={metricsViews.length === 0} >
@@ -273,7 +278,8 @@ createResourceAndNavigate(ResourceKind.API)} + on:click={() => + createResourceAndNavigate(runtimeClient, ResourceKind.API)} > createResourceAndNavigate(ResourceKind.Theme)} + on:click={() => + createResourceAndNavigate(runtimeClient, ResourceKind.Theme)} > -