From 78a72961baeea3ad0e59ab5bece5f9caa75f06f1 Mon Sep 17 00:00:00 2001 From: Aditya Hegde Date: Thu, 12 Sep 2024 19:03:49 +0530 Subject: [PATCH 01/17] Add basic to and from url methods --- .../features/dashboards/url-state/fromUrl.ts | 251 ++++++++++++++++++ .../features/dashboards/url-state/mappers.ts | 20 ++ .../features/dashboards/url-state/toUrl.ts | 86 ++++++ 3 files changed, 357 insertions(+) create mode 100644 web-common/src/features/dashboards/url-state/fromUrl.ts create mode 100644 web-common/src/features/dashboards/url-state/mappers.ts create mode 100644 web-common/src/features/dashboards/url-state/toUrl.ts diff --git a/web-common/src/features/dashboards/url-state/fromUrl.ts b/web-common/src/features/dashboards/url-state/fromUrl.ts new file mode 100644 index 00000000000..bb0d937e31c --- /dev/null +++ b/web-common/src/features/dashboards/url-state/fromUrl.ts @@ -0,0 +1,251 @@ +import { + PivotChipData, + PivotChipType, + PivotState, +} from "@rilldata/web-common/features/dashboards/pivot/types"; +import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { + TDDChart, + TDDState, +} from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; +import { FromURLParamTimeDimensionMap } from "@rilldata/web-common/features/dashboards/url-state/mappers"; +import { getMapFromArray } from "@rilldata/web-common/lib/arrayUtils"; +import { TIME_GRAIN } from "@rilldata/web-common/lib/time/config"; +import { + DashboardTimeControls, + TimeRangePreset, +} from "@rilldata/web-common/lib/time/types"; +import { DashboardState_LeaderboardSortDirection } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; +import type { + MetricsViewSpecDimensionV2, + MetricsViewSpecMeasureV2, + V1MetricsViewSpec, +} from "@rilldata/web-common/runtime-client"; + +export function getMetricsExplorerFromUrl( + searchParams: URLSearchParams, + metricsView: V1MetricsViewSpec, +) { + // TODO: replace this with V1ExplorePreset once it is available on main + const entity: Partial = {}; + const measures = getMapFromArray(metricsView.measures ?? [], (m) => m.name!); + const dimensions = getMapFromArray( + metricsView.dimensions ?? [], + (d) => d.name!, + ); + + // TODO: filter + + if (searchParams.has("tr")) { + entity.selectedTimeRange = fromTimeRangeUrlParam( + searchParams.get("tr") as string, + ); + } + if (searchParams.has("tz")) { + entity.selectedTimezone = searchParams.get("tz") as string; + } + if (searchParams.has("ctr")) { + entity.selectedComparisonTimeRange = fromTimeRangeUrlParam( + searchParams.get("ctr") as string, + ); + } + if (searchParams.has("cd")) { + const cd = searchParams.get("cd") as string; + if (dimensions.has(cd)) { + entity.selectedComparisonDimension = cd; + } + } + + Object.assign( + entity, + fromOverviewUrlParams(searchParams, measures, dimensions), + ); + + entity.tdd = fromTimeDimensionUrlParams(searchParams, measures); + + entity.pivot = fromPivotUrlParams(searchParams, measures, dimensions); + + return entity; +} + +function fromTimeRangeUrlParam(timeRange: string) { + const selectedTimeRange: DashboardTimeControls = { + name: timeRange, + } as DashboardTimeControls; + // backwards compatibility + if (timeRange in TimeRangePreset) { + selectedTimeRange.name = TimeRangePreset[timeRange]; + } + + return selectedTimeRange; +} + +function fromOverviewUrlParams( + searchParams: URLSearchParams, + measures: Map, + dimensions: Map, +) { + const entity: Partial = {}; + + if (searchParams.has("e.m")) { + const mes = searchParams.get("e.m") as string; + if (mes === "*") { + entity.allMeasuresVisible = true; + entity.visibleMeasureKeys = new Set(measures.keys()); + } else { + entity.allMeasuresVisible = false; + entity.visibleMeasureKeys = new Set( + mes.split(",").filter((m) => measures.has(m)), + ); + } + } + + if (searchParams.has("e.d")) { + const dims = searchParams.get("e.d") as string; + if (dims === "*") { + entity.allDimensionsVisible = true; + entity.visibleDimensionKeys = new Set(dimensions.keys()); + } else { + entity.allDimensionsVisible = false; + entity.visibleDimensionKeys = new Set( + dims.split(",").filter((d) => dimensions.has(d)), + ); + } + } + + if (searchParams.has("e.sb")) { + const sortBy = searchParams.get("e.sb") as string; + if (measures.has(sortBy)) { + entity.leaderboardMeasureName = sortBy; + } + } + if (searchParams.has("e.sd")) { + const sortDir = searchParams.get("e.sd") as string; + entity.sortDirection = + sortDir === "ASC" ? SortDirection.ASCENDING : SortDirection.DESCENDING; + } + + if (searchParams.has("e.ed")) { + const dim = searchParams.get("e.ed") as string; + if (dimensions.has(dim)) { + entity.selectedDimensionName = dim; + } + } + + return entity; +} + +function fromTimeDimensionUrlParams( + searchParams: URLSearchParams, + measures: Map, +) { + let ttdMeasure: string | undefined; + let ttdChartType: TDDChart | undefined; + let ttdPin: number | undefined; + + if (searchParams.has("tdd.m")) { + const mes = searchParams.get("tdd.m") as string; + if (measures.has(mes)) { + ttdMeasure = mes; + } + } + if (searchParams.has("tdd.ct")) { + const ct = searchParams.get("tdd.ct") as string; + if (ct in TDDChart) { + ttdChartType = TDDChart[ct]; + } + } + if (searchParams.has("tdd.p")) { + const pin = Number(searchParams.get("tdd.p") as string); + if (!Number.isNaN(pin)) { + ttdPin = pin; + } + } + + return { + expandedMeasureName: ttdMeasure ?? "", + chartType: ttdChartType ?? TDDChart.DEFAULT, + pinIndex: ttdPin ?? -1, + }; +} + +function fromPivotUrlParams( + searchParams: URLSearchParams, + measures: Map, + dimensions: Map, +): PivotState { + const mapPivotEntry = (entry: string): PivotChipData | undefined => { + if (entry in FromURLParamTimeDimensionMap) { + const grain = FromURLParamTimeDimensionMap[entry]; + return { + id: grain, + title: TIME_GRAIN[grain]?.label, + type: PivotChipType.Time, + }; + } + + if (measures.has(entry)) { + const m = measures.get(entry)!; + return { + id: entry, + title: m.label || m.name || "Unknown", + type: PivotChipType.Measure, + }; + } + + if (dimensions.has(entry)) { + const d = dimensions.get(entry)!; + return { + id: entry, + title: d.label || d.name || "Unknown", + type: PivotChipType.Dimension, + }; + } + + return undefined; + }; + + const rowDimensions: PivotChipData[] = []; + if (searchParams.has("p.r")) { + const pivotRows = searchParams.get("p.r") as string; + pivotRows.split(",").forEach((pivotRow) => { + const chip = mapPivotEntry(pivotRow); + if (!chip) return; + rowDimensions.push(chip); + }); + } + const colMeasures: PivotChipData[] = []; + const colDimensions: PivotChipData[] = []; + if (searchParams.has("p.c")) { + const pivotCols = searchParams.get("p.c") as string; + pivotCols.split(",").forEach((pivotRow) => { + const chip = mapPivotEntry(pivotRow); + if (!chip) return; + if (chip.type === PivotChipType.Measure) { + colMeasures.push(chip); + } else { + colDimensions.push(chip); + } + }); + } + + return { + active: searchParams.get("view") === "pivot", + rows: { + dimension: rowDimensions, + }, + columns: { + measure: colMeasures, + dimension: colDimensions, + }, + // TODO: other fields + expanded: {}, + sorting: [], + columnPage: 1, + rowPage: 1, + enableComparison: false, + activeCell: null, + rowJoinType: "nest", + }; +} diff --git a/web-common/src/features/dashboards/url-state/mappers.ts b/web-common/src/features/dashboards/url-state/mappers.ts new file mode 100644 index 00000000000..7e245eaf630 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/mappers.ts @@ -0,0 +1,20 @@ +import { V1TimeGrain } from "@rilldata/web-common/runtime-client"; + +export const FromURLParamTimeDimensionMap: Record = { + "time.hour": V1TimeGrain.TIME_GRAIN_HOUR, + "time.day": V1TimeGrain.TIME_GRAIN_DAY, + "time.month": V1TimeGrain.TIME_GRAIN_MONTH, +}; +export const ToURLParamTimeDimensionMap = reverseMap( + FromURLParamTimeDimensionMap, +); + +function reverseMap( + map: Partial>, +): Partial> { + const revMap = {} as Partial>; + for (const k in map) { + revMap[map[k] as string | number] = map[k]; + } + return revMap; +} diff --git a/web-common/src/features/dashboards/url-state/toUrl.ts b/web-common/src/features/dashboards/url-state/toUrl.ts new file mode 100644 index 00000000000..c3d73eb9f88 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/toUrl.ts @@ -0,0 +1,86 @@ +import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; +import type { V1MetricsViewSpec } from "@rilldata/web-common/runtime-client"; + +export function getUrlFromMetricsExplorer( + metrics: MetricsExplorerEntity, + searchParams: URLSearchParams, + metricsView: V1MetricsViewSpec, +) { + if (!metrics) return; + + // TODO: filter + + if ( + metrics.selectedTimeRange?.name && + metrics.selectedTimeRange?.name !== metricsView.defaultTimeRange + ) { + searchParams.set("tr", metrics.selectedTimeRange?.name); + } + // TODO: rest of time range + + toOverviewUrl(metrics, searchParams, metricsView); + + toTimeDimensionUrlParams(metrics, searchParams); + + toPivotUrlParams(metrics, searchParams); +} + +function toOverviewUrl( + metrics: MetricsExplorerEntity, + searchParams: URLSearchParams, + metricsView: V1MetricsViewSpec, +) { + if (!metrics.allMeasuresVisible) { + searchParams.set("e.m", [...metrics.visibleMeasureKeys].join(",")); + } + if (!metrics.allDimensionsVisible) { + searchParams.set("e.m", [...metrics.visibleMeasureKeys].join(",")); + } + if (metrics.leaderboardMeasureName !== metricsView.measures?.[0]?.name) { + searchParams.set("e.sb", metrics.leaderboardMeasureName); + } + if (metrics.sortDirection !== SortDirection.DESCENDING) { + searchParams.set("e.sd", "ASC"); + } + if (metrics.selectedDimensionName) { + searchParams.set("e.ed", metrics.selectedDimensionName); + } +} + +function toTimeDimensionUrlParams( + metrics: MetricsExplorerEntity, + searchParams: URLSearchParams, +) { + if (metrics.tdd.expandedMeasureName) { + searchParams.set("tdd.m", metrics.tdd.expandedMeasureName); + } + if (metrics.tdd.pinIndex !== -1) { + searchParams.set("tdd.p", metrics.tdd.pinIndex + ""); + } + if (metrics.tdd.chartType !== TDDChart.DEFAULT) { + searchParams.set("tdd.p", metrics.tdd.chartType); + } +} + +function toPivotUrlParams( + metrics: MetricsExplorerEntity, + searchParams: URLSearchParams, +) { + if (!metrics.pivot.active) return; + + searchParams.set( + "p.r", + metrics.pivot.rows.dimension.map((d) => d.id).join(","), + ); + searchParams.set( + "p.c", + [ + ...metrics.pivot.columns.dimension.map((d) => d.id), + ...metrics.pivot.columns.measure.map((m) => m.id), + ].join(","), + ); + + // TODO: other fields +} From 41bdad158805f890d4ca23e5aa76f6464cbe40a5 Mon Sep 17 00:00:00 2001 From: Aditya Hegde Date: Mon, 16 Sep 2024 15:22:52 +0530 Subject: [PATCH 02/17] Add filters to url --- .../url-state/filters/converters.ts | 79 ++++++++++++++++++- .../url-state/filters/expression.spec.ts | 4 + .../url-state/filters/post-processors.ts | 4 +- .../features/dashboards/url-state/fromUrl.ts | 79 +++++++++++++++---- .../features/dashboards/url-state/mappers.ts | 7 +- .../features/dashboards/url-state/toUrl.ts | 6 ++ .../routes/(viz)/dashboard/[name]/+page.ts | 2 + 7 files changed, 160 insertions(+), 21 deletions(-) diff --git a/web-common/src/features/dashboards/url-state/filters/converters.ts b/web-common/src/features/dashboards/url-state/filters/converters.ts index a21410f3c3f..bd3ddf9a7fb 100644 --- a/web-common/src/features/dashboards/url-state/filters/converters.ts +++ b/web-common/src/features/dashboards/url-state/filters/converters.ts @@ -1,3 +1,5 @@ +import { BinaryOperationReverseMap } from "@rilldata/web-common/features/dashboards/url-state/filters/post-processors"; +import { V1Expression, V1Operation } from "@rilldata/web-common/runtime-client"; import grammar from "./expression.cjs"; import nearley from "nearley"; @@ -5,7 +7,33 @@ const compiledGrammar = nearley.Grammar.fromCompiled(grammar); export function convertFilterParamToExpression(filter: string) { const parser = new nearley.Parser(compiledGrammar); parser.feed(filter); - return parser.results[0]; + return parser.results[0] as V1Expression; +} + +export function convertExpressionToFilterParam(expr: V1Expression): string { + if (!expr) return ""; + + if (expr.val) { + if (typeof expr.val === "string") { + return `'${expr.val}'`; + } + return expr.val + ""; + } + + if (expr.ident) return expr.ident; + + switch (expr.cond?.op) { + case V1Operation.OPERATION_AND: + case V1Operation.OPERATION_OR: + return convertJoinerExpressionToFilterParam(expr); + + case V1Operation.OPERATION_IN: + case V1Operation.OPERATION_NIN: + return convertInExpressionToFilterParam(expr); + + default: + return convertBinaryExpressionToFilterParam(expr); + } } export function stripParserError(err: Error) { @@ -14,3 +42,52 @@ export function stripParserError(err: Error) { err.message.indexOf("Instead, I was expecting") - 1, ); } + +function convertJoinerExpressionToFilterParam(expr: V1Expression) { + const joiner = expr.cond?.op === V1Operation.OPERATION_AND ? "AND" : "OR"; + + const parts = expr.cond?.exprs + ?.map(convertExpressionToFilterParam) + .filter(Boolean); + if (!parts?.length) return ""; + + return `(${parts.join(joiner)})`; +} + +function convertInExpressionToFilterParam(expr: V1Expression) { + if (!expr.cond?.exprs?.length) return ""; + const joiner = expr.cond?.op === V1Operation.OPERATION_IN ? "IN" : "NIN"; + + const column = expr.cond.exprs[0]?.ident; + if (!column) return ""; + + if (expr.cond.exprs[1]?.subquery?.having) { + // TODO: support `NIN ` + const having = convertExpressionToFilterParam( + expr.cond.exprs[1]?.subquery?.having, + ); + if (having) return `${column} having (${having})`; + } + + if (expr.cond.exprs.length > 1) { + const vals = expr.cond.exprs + .slice(1) + .map(convertExpressionToFilterParam) + .filter(Boolean); + return `${column} ${joiner} (${vals.join(",")})`; + } + + return ""; +} + +function convertBinaryExpressionToFilterParam(expr: V1Expression) { + if (!expr.cond?.op || !(expr.cond?.op in BinaryOperationReverseMap)) + return ""; + const op = BinaryOperationReverseMap[expr.cond.op]; + if (!expr.cond?.exprs?.length) return ""; + const left = convertExpressionToFilterParam(expr.cond.exprs[0]); + const right = convertExpressionToFilterParam(expr.cond.exprs[1]); + if (!left || !right) return ""; + + return `${left} ${op} ${right}`; +} diff --git a/web-common/src/features/dashboards/url-state/filters/expression.spec.ts b/web-common/src/features/dashboards/url-state/filters/expression.spec.ts index fe390db00a1..0bf7c3a10e0 100644 --- a/web-common/src/features/dashboards/url-state/filters/expression.spec.ts +++ b/web-common/src/features/dashboards/url-state/filters/expression.spec.ts @@ -17,6 +17,10 @@ import nearley from "nearley"; describe("expression", () => { describe("positive cases", () => { const Cases = [ + { + expr: "country IN ('US','IN')", + expected_expression: createInExpression("country", ["US", "IN"]), + }, { expr: "country IN ('US','IN') and state = 'ABC'", expected_expression: createAndExpression([ diff --git a/web-common/src/features/dashboards/url-state/filters/post-processors.ts b/web-common/src/features/dashboards/url-state/filters/post-processors.ts index a3ac991b18e..62340cf747b 100644 --- a/web-common/src/features/dashboards/url-state/filters/post-processors.ts +++ b/web-common/src/features/dashboards/url-state/filters/post-processors.ts @@ -6,9 +6,10 @@ import { createSubQueryExpression, getAllIdentifiers, } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; +import { reverseMap } from "@rilldata/web-common/features/dashboards/url-state/mappers"; import { V1Operation } from "@rilldata/web-common/runtime-client"; -const BinaryOperationMap = { +const BinaryOperationMap: Record = { "=": V1Operation.OPERATION_EQ, "!=": V1Operation.OPERATION_NEQ, ">": V1Operation.OPERATION_GT, @@ -16,6 +17,7 @@ const BinaryOperationMap = { "<": V1Operation.OPERATION_LT, "<=": V1Operation.OPERATION_LTE, }; +export const BinaryOperationReverseMap = reverseMap(BinaryOperationMap); export const binaryPostprocessor = ([left, _1, op, _2, right]) => createBinaryExpression(left, BinaryOperationMap[op], right); diff --git a/web-common/src/features/dashboards/url-state/fromUrl.ts b/web-common/src/features/dashboards/url-state/fromUrl.ts index bb0d937e31c..e4369eb58e4 100644 --- a/web-common/src/features/dashboards/url-state/fromUrl.ts +++ b/web-common/src/features/dashboards/url-state/fromUrl.ts @@ -1,14 +1,20 @@ +import { splitWhereFilter } from "@rilldata/web-common/features/dashboards/filters/measure-filters/measure-filter-utils"; import { PivotChipData, PivotChipType, PivotState, } from "@rilldata/web-common/features/dashboards/pivot/types"; import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; -import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { createAndExpression } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; +import type { + DimensionThresholdFilter, + MetricsExplorerEntity, +} from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { TDDChart, TDDState, } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; +import { convertFilterParamToExpression } from "@rilldata/web-common/features/dashboards/url-state/filters/converters"; import { FromURLParamTimeDimensionMap } from "@rilldata/web-common/features/dashboards/url-state/mappers"; import { getMapFromArray } from "@rilldata/web-common/lib/arrayUtils"; import { TIME_GRAIN } from "@rilldata/web-common/lib/time/config"; @@ -16,39 +22,55 @@ import { DashboardTimeControls, TimeRangePreset, } from "@rilldata/web-common/lib/time/types"; -import { DashboardState_LeaderboardSortDirection } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; -import type { +import { MetricsViewSpecDimensionV2, MetricsViewSpecMeasureV2, + V1Expression, V1MetricsViewSpec, + V1Operation, } from "@rilldata/web-common/runtime-client"; export function getMetricsExplorerFromUrl( searchParams: URLSearchParams, metricsView: V1MetricsViewSpec, -) { +): { entity: Partial; errors: Error[] } { // TODO: replace this with V1ExplorePreset once it is available on main const entity: Partial = {}; + const errors: Error[] = []; + const measures = getMapFromArray(metricsView.measures ?? [], (m) => m.name!); const dimensions = getMapFromArray( metricsView.dimensions ?? [], (d) => d.name!, ); - // TODO: filter + if (searchParams.has("f")) { + const { + dimensionFilters, + dimensionThresholdFilters, + errors: filterErrors, + } = fromFilterUrlParam(searchParams.get("f") as string); + if (filterErrors) errors.push(...filterErrors); + entity.whereFilter = dimensionFilters; + entity.dimensionThresholdFilters = dimensionThresholdFilters; + } if (searchParams.has("tr")) { - entity.selectedTimeRange = fromTimeRangeUrlParam( + const { timeRange, error } = fromTimeRangeUrlParam( searchParams.get("tr") as string, ); + if (error) errors.push(error); + entity.selectedTimeRange = timeRange; } if (searchParams.has("tz")) { entity.selectedTimezone = searchParams.get("tz") as string; } if (searchParams.has("ctr")) { - entity.selectedComparisonTimeRange = fromTimeRangeUrlParam( + const { timeRange, error } = fromTimeRangeUrlParam( searchParams.get("ctr") as string, ); + if (error) errors.push(error); + entity.selectedComparisonTimeRange = timeRange; } if (searchParams.has("cd")) { const cd = searchParams.get("cd") as string; @@ -66,19 +88,44 @@ export function getMetricsExplorerFromUrl( entity.pivot = fromPivotUrlParams(searchParams, measures, dimensions); - return entity; + return { entity, errors }; } -function fromTimeRangeUrlParam(timeRange: string) { - const selectedTimeRange: DashboardTimeControls = { - name: timeRange, - } as DashboardTimeControls; - // backwards compatibility - if (timeRange in TimeRangePreset) { - selectedTimeRange.name = TimeRangePreset[timeRange]; +function fromFilterUrlParam(filter: string): { + dimensionFilters?: V1Expression; + dimensionThresholdFilters?: DimensionThresholdFilter[]; + errors?: Error[]; +} { + try { + let expr = convertFilterParamToExpression(filter); + // if root is not AND/OR then add AND + if ( + expr?.cond?.op !== V1Operation.OPERATION_AND && + expr?.cond?.op !== V1Operation.OPERATION_OR + ) { + expr = createAndExpression([expr]); + } + return splitWhereFilter(expr); + } catch (e) { + return { errors: [e] }; + } +} + +function fromTimeRangeUrlParam(tr: string): { + timeRange?: DashboardTimeControls; + error?: Error; +} { + if (tr in TimeRangePreset) { + return { + timeRange: { + name: tr, + } as DashboardTimeControls, + }; } - return selectedTimeRange; + return { + error: new Error(`unknown time range: ${tr}`), + }; } function fromOverviewUrlParams( diff --git a/web-common/src/features/dashboards/url-state/mappers.ts b/web-common/src/features/dashboards/url-state/mappers.ts index 7e245eaf630..85bf83d035c 100644 --- a/web-common/src/features/dashboards/url-state/mappers.ts +++ b/web-common/src/features/dashboards/url-state/mappers.ts @@ -9,9 +9,10 @@ export const ToURLParamTimeDimensionMap = reverseMap( FromURLParamTimeDimensionMap, ); -function reverseMap( - map: Partial>, -): Partial> { +export function reverseMap< + K extends string | number, + V extends string | number, +>(map: Partial>): Partial> { const revMap = {} as Partial>; for (const k in map) { revMap[map[k] as string | number] = map[k]; diff --git a/web-common/src/features/dashboards/url-state/toUrl.ts b/web-common/src/features/dashboards/url-state/toUrl.ts index c3d73eb9f88..4cde131ba1c 100644 --- a/web-common/src/features/dashboards/url-state/toUrl.ts +++ b/web-common/src/features/dashboards/url-state/toUrl.ts @@ -1,6 +1,8 @@ +import { mergeMeasureFilters } from "@rilldata/web-common/features/dashboards/filters/measure-filters/measure-filter-utils"; import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; +import { convertExpressionToFilterParam } from "@rilldata/web-common/features/dashboards/url-state/filters/converters"; import type { V1MetricsViewSpec } from "@rilldata/web-common/runtime-client"; export function getUrlFromMetricsExplorer( @@ -11,6 +13,10 @@ export function getUrlFromMetricsExplorer( if (!metrics) return; // TODO: filter + const expr = mergeMeasureFilters(metrics); + if (expr && expr?.cond?.exprs?.length) { + searchParams.set("f", convertExpressionToFilterParam(expr)); + } if ( metrics.selectedTimeRange?.name && diff --git a/web-local/src/routes/(viz)/dashboard/[name]/+page.ts b/web-local/src/routes/(viz)/dashboard/[name]/+page.ts index b1f60a8a9b6..d20a8c34da5 100644 --- a/web-local/src/routes/(viz)/dashboard/[name]/+page.ts +++ b/web-local/src/routes/(viz)/dashboard/[name]/+page.ts @@ -31,6 +31,8 @@ export const load = async ({ params, depends }) => { > = ({ signal }) => runtimeServiceGetResource(instanceId, queryParams, signal); + console.log(params); + try { const response = await queryClient.fetchQuery({ queryFn: queryFunction, From 31a900f9e49199723365ae3e8f5a33e0455891d5 Mon Sep 17 00:00:00 2001 From: Aditya Hegde Date: Tue, 17 Sep 2024 15:06:54 +0530 Subject: [PATCH 03/17] Hookup the new url format --- .../dashboards/stores/dashboard-stores.ts | 29 +++++++++++++++ .../url-state/DashboardURLStateSync.svelte | 36 +++++++++++++++++++ .../features/dashboards/url-state/fromUrl.ts | 5 +-- .../(viz)/dashboard/[name]/+page.svelte | 6 ++-- .../routes/(viz)/dashboard/[name]/+page.ts | 6 ++-- 5 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte diff --git a/web-common/src/features/dashboards/stores/dashboard-stores.ts b/web-common/src/features/dashboards/stores/dashboard-stores.ts index a864fd127bb..1f242b33d8e 100644 --- a/web-common/src/features/dashboards/stores/dashboard-stores.ts +++ b/web-common/src/features/dashboards/stores/dashboard-stores.ts @@ -14,6 +14,7 @@ import { } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; +import { getMetricsExplorerFromUrl } from "@rilldata/web-common/features/dashboards/url-state/fromUrl"; import { getMapFromArray } from "@rilldata/web-common/lib/arrayUtils"; import type { DashboardTimeControls, @@ -208,6 +209,34 @@ const metricViewReducers = { }); }, + syncFromUrlParams( + name: string, + urlParams: URLSearchParams, + metricsView: V1MetricsViewSpec, + ) { + if (!urlParams || !metricsView) return; + // not all data for MetricsExplorerEntity will be filled out here. + // Hence, it is a Partial + const { entity } = getMetricsExplorerFromUrl(urlParams, metricsView); + if (!entity) return; + // TODO: errors + console.log(entity); + + updateMetricsExplorerByName(name, (metricsExplorer) => { + for (const key in entity) { + metricsExplorer[key] = entity[key]; + } + // this hack is needed since what is shown for comparison is not a single source + // TODO: use an enum and get rid of this + if (!entity.showTimeComparison) { + metricsExplorer.showTimeComparison = false; + } + metricsExplorer.dimensionFilterExcludeMode = + includeExcludeModeFromFilters(entity.whereFilter); + AdvancedMeasureCorrector.correct(metricsExplorer, metricsView); + }); + }, + sync(name: string, metricsView: V1MetricsViewSpec) { if (!name || !metricsView || !metricsView.measures) return; updateMetricsExplorerByName(name, (metricsExplorer) => { diff --git a/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte b/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte new file mode 100644 index 00000000000..65146e1ffcc --- /dev/null +++ b/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte @@ -0,0 +1,36 @@ + + + diff --git a/web-common/src/features/dashboards/url-state/fromUrl.ts b/web-common/src/features/dashboards/url-state/fromUrl.ts index e4369eb58e4..c090ffaf5a9 100644 --- a/web-common/src/features/dashboards/url-state/fromUrl.ts +++ b/web-common/src/features/dashboards/url-state/fromUrl.ts @@ -51,8 +51,9 @@ export function getMetricsExplorerFromUrl( errors: filterErrors, } = fromFilterUrlParam(searchParams.get("f") as string); if (filterErrors) errors.push(...filterErrors); - entity.whereFilter = dimensionFilters; - entity.dimensionThresholdFilters = dimensionThresholdFilters; + if (dimensionFilters) entity.whereFilter = dimensionFilters; + if (dimensionThresholdFilters) + entity.dimensionThresholdFilters = dimensionThresholdFilters; } if (searchParams.has("tr")) { diff --git a/web-local/src/routes/(viz)/dashboard/[name]/+page.svelte b/web-local/src/routes/(viz)/dashboard/[name]/+page.svelte index 2afb7795a04..fad62490c14 100644 --- a/web-local/src/routes/(viz)/dashboard/[name]/+page.svelte +++ b/web-local/src/routes/(viz)/dashboard/[name]/+page.svelte @@ -3,7 +3,7 @@ import { Dashboard } from "@rilldata/web-common/features/dashboards"; import DashboardThemeProvider from "@rilldata/web-common/features/dashboards/DashboardThemeProvider.svelte"; import { resetSelectedMockUserAfterNavigate } from "@rilldata/web-common/features/dashboards/granular-access-policies/resetSelectedMockUserAfterNavigate"; - import DashboardURLStateProvider from "@rilldata/web-common/features/dashboards/proto-state/DashboardURLStateProvider.svelte"; + import DashboardURLStateSync from "@rilldata/web-common/features/dashboards/url-state/DashboardURLStateSync.svelte"; import StateManagersProvider from "@rilldata/web-common/features/dashboards/state-managers/StateManagersProvider.svelte"; import DashboardStateProvider from "@rilldata/web-common/features/dashboards/stores/DashboardStateProvider.svelte"; import { useProjectParser } from "@rilldata/web-common/features/entity-management/resource-selectors.js"; @@ -64,11 +64,11 @@ {#key metricsViewName} - + - + {/key} diff --git a/web-local/src/routes/(viz)/dashboard/[name]/+page.ts b/web-local/src/routes/(viz)/dashboard/[name]/+page.ts index d20a8c34da5..0ae95749d26 100644 --- a/web-local/src/routes/(viz)/dashboard/[name]/+page.ts +++ b/web-local/src/routes/(viz)/dashboard/[name]/+page.ts @@ -1,3 +1,4 @@ +import { metricsExplorerStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors.js"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient.js"; import { @@ -9,7 +10,7 @@ import type { QueryFunction } from "@tanstack/svelte-query"; import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import { get } from "svelte/store"; -export const load = async ({ params, depends }) => { +export const load = async ({ params, depends, url }) => { const { instanceId } = get(runtime); const dashboardName = params.name; @@ -31,8 +32,6 @@ export const load = async ({ params, depends }) => { > = ({ signal }) => runtimeServiceGetResource(instanceId, queryParams, signal); - console.log(params); - try { const response = await queryClient.fetchQuery({ queryFn: queryFunction, @@ -47,6 +46,7 @@ export const load = async ({ params, depends }) => { return { metricsView: metricsViewResource, + searchParams: url?.searchParams, }; } catch (e) { console.error(e); From cb151b2bda4f40b0e2576a18576702f53621a182 Mon Sep 17 00:00:00 2001 From: Aditya Hegde Date: Tue, 1 Oct 2024 19:51:37 +0530 Subject: [PATCH 04/17] Use ExplorePreset for defaults --- proto/gen/rill/runtime/v1/resources.pb.go | 1905 +++++++++-------- .../rill/runtime/v1/resources.pb.validate.go | 24 + .../gen/rill/runtime/v1/runtime.swagger.yaml | 40 + proto/rill/runtime/v1/resources.proto | 27 + .../features/dashboards/url-state/fromUrl.ts | 154 +- .../features/dashboards/url-state/toUrl.ts | 151 +- web-common/src/lib/arrayUtils.ts | 14 + .../proto/gen/rill/runtime/v1/resources_pb.ts | 116 + .../src/runtime-client/gen/index.schemas.ts | 195 +- 9 files changed, 1606 insertions(+), 1020 deletions(-) diff --git a/proto/gen/rill/runtime/v1/resources.pb.go b/proto/gen/rill/runtime/v1/resources.pb.go index 3c92c04a3b0..bc7b6734185 100644 --- a/proto/gen/rill/runtime/v1/resources.pb.go +++ b/proto/gen/rill/runtime/v1/resources.pb.go @@ -126,6 +126,58 @@ func (ExploreComparisonMode) EnumDescriptor() ([]byte, []int) { return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{1} } +type ExploreWebView int32 + +const ( + ExploreWebView_EXPLORE_ACTIVE_PAGE_OVERVIEW ExploreWebView = 0 + ExploreWebView_EXPLORE_ACTIVE_PAGE_TIME_DIMENSION ExploreWebView = 1 + ExploreWebView_EXPLORE_ACTIVE_PAGE_PIVOT ExploreWebView = 2 + ExploreWebView_EXPLORE_ACTIVE_PAGE_CANVAS ExploreWebView = 3 +) + +// Enum value maps for ExploreWebView. +var ( + ExploreWebView_name = map[int32]string{ + 0: "EXPLORE_ACTIVE_PAGE_OVERVIEW", + 1: "EXPLORE_ACTIVE_PAGE_TIME_DIMENSION", + 2: "EXPLORE_ACTIVE_PAGE_PIVOT", + 3: "EXPLORE_ACTIVE_PAGE_CANVAS", + } + ExploreWebView_value = map[string]int32{ + "EXPLORE_ACTIVE_PAGE_OVERVIEW": 0, + "EXPLORE_ACTIVE_PAGE_TIME_DIMENSION": 1, + "EXPLORE_ACTIVE_PAGE_PIVOT": 2, + "EXPLORE_ACTIVE_PAGE_CANVAS": 3, + } +) + +func (x ExploreWebView) Enum() *ExploreWebView { + p := new(ExploreWebView) + *p = x + return p +} + +func (x ExploreWebView) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ExploreWebView) Descriptor() protoreflect.EnumDescriptor { + return file_rill_runtime_v1_resources_proto_enumTypes[2].Descriptor() +} + +func (ExploreWebView) Type() protoreflect.EnumType { + return &file_rill_runtime_v1_resources_proto_enumTypes[2] +} + +func (x ExploreWebView) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ExploreWebView.Descriptor instead. +func (ExploreWebView) EnumDescriptor() ([]byte, []int) { + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{2} +} + type AssertionStatus int32 const ( @@ -162,11 +214,11 @@ func (x AssertionStatus) String() string { } func (AssertionStatus) Descriptor() protoreflect.EnumDescriptor { - return file_rill_runtime_v1_resources_proto_enumTypes[2].Descriptor() + return file_rill_runtime_v1_resources_proto_enumTypes[3].Descriptor() } func (AssertionStatus) Type() protoreflect.EnumType { - return &file_rill_runtime_v1_resources_proto_enumTypes[2] + return &file_rill_runtime_v1_resources_proto_enumTypes[3] } func (x AssertionStatus) Number() protoreflect.EnumNumber { @@ -175,7 +227,7 @@ func (x AssertionStatus) Number() protoreflect.EnumNumber { // Deprecated: Use AssertionStatus.Descriptor instead. func (AssertionStatus) EnumDescriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{2} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{3} } // Type of measure query to generate @@ -215,11 +267,11 @@ func (x MetricsViewSpec_MeasureType) String() string { } func (MetricsViewSpec_MeasureType) Descriptor() protoreflect.EnumDescriptor { - return file_rill_runtime_v1_resources_proto_enumTypes[3].Descriptor() + return file_rill_runtime_v1_resources_proto_enumTypes[4].Descriptor() } func (MetricsViewSpec_MeasureType) Type() protoreflect.EnumType { - return &file_rill_runtime_v1_resources_proto_enumTypes[3] + return &file_rill_runtime_v1_resources_proto_enumTypes[4] } func (x MetricsViewSpec_MeasureType) Number() protoreflect.EnumNumber { @@ -269,11 +321,11 @@ func (x MetricsViewSpec_ComparisonMode) String() string { } func (MetricsViewSpec_ComparisonMode) Descriptor() protoreflect.EnumDescriptor { - return file_rill_runtime_v1_resources_proto_enumTypes[4].Descriptor() + return file_rill_runtime_v1_resources_proto_enumTypes[5].Descriptor() } func (MetricsViewSpec_ComparisonMode) Type() protoreflect.EnumType { - return &file_rill_runtime_v1_resources_proto_enumTypes[4] + return &file_rill_runtime_v1_resources_proto_enumTypes[5] } func (x MetricsViewSpec_ComparisonMode) Number() protoreflect.EnumNumber { @@ -318,11 +370,11 @@ func (x BucketExtractPolicy_Strategy) String() string { } func (BucketExtractPolicy_Strategy) Descriptor() protoreflect.EnumDescriptor { - return file_rill_runtime_v1_resources_proto_enumTypes[5].Descriptor() + return file_rill_runtime_v1_resources_proto_enumTypes[6].Descriptor() } func (BucketExtractPolicy_Strategy) Type() protoreflect.EnumType { - return &file_rill_runtime_v1_resources_proto_enumTypes[5] + return &file_rill_runtime_v1_resources_proto_enumTypes[6] } func (x BucketExtractPolicy_Strategy) Number() protoreflect.EnumNumber { @@ -2637,10 +2689,24 @@ type ExplorePreset struct { // It corresponds to the `range` property of the explore's `time_ranges`. // If not found in `time_ranges`, it should be added to the list. TimeRange string `protobuf:"bytes,6,opt,name=time_range,json=timeRange,proto3" json:"time_range,omitempty"` + Timezone string `protobuf:"bytes,11,opt,name=timezone,proto3" json:"timezone,omitempty"` + TimeGrain string `protobuf:"bytes,12,opt,name=time_grain,json=timeGrain,proto3" json:"time_grain,omitempty"` // Comparison mode. - ComparisonMode ExploreComparisonMode `protobuf:"varint,7,opt,name=comparison_mode,json=comparisonMode,proto3,enum=rill.runtime.v1.ExploreComparisonMode" json:"comparison_mode,omitempty"` + ComparisonMode ExploreComparisonMode `protobuf:"varint,7,opt,name=comparison_mode,json=comparisonMode,proto3,enum=rill.runtime.v1.ExploreComparisonMode" json:"comparison_mode,omitempty"` + CompareTimeRange string `protobuf:"bytes,13,opt,name=compare_time_range,json=compareTimeRange,proto3" json:"compare_time_range,omitempty"` // If comparison_mode is EXPLORE_COMPARISON_MODE_DIMENSION, this indicates the dimension to use. - ComparisonDimension string `protobuf:"bytes,8,opt,name=comparison_dimension,json=comparisonDimension,proto3" json:"comparison_dimension,omitempty"` + ComparisonDimension string `protobuf:"bytes,8,opt,name=comparison_dimension,json=comparisonDimension,proto3" json:"comparison_dimension,omitempty"` + View ExploreWebView `protobuf:"varint,14,opt,name=view,proto3,enum=rill.runtime.v1.ExploreWebView" json:"view,omitempty"` + OverviewSortBy string `protobuf:"bytes,15,opt,name=overview_sort_by,json=overviewSortBy,proto3" json:"overview_sort_by,omitempty"` + OverviewSortAsc bool `protobuf:"varint,16,opt,name=overview_sort_asc,json=overviewSortAsc,proto3" json:"overview_sort_asc,omitempty"` + OverviewExpandedDimension string `protobuf:"bytes,17,opt,name=overview_expanded_dimension,json=overviewExpandedDimension,proto3" json:"overview_expanded_dimension,omitempty"` + TimeDimensionMeasure string `protobuf:"bytes,18,opt,name=time_dimension_measure,json=timeDimensionMeasure,proto3" json:"time_dimension_measure,omitempty"` + TimeDimensionChartType string `protobuf:"bytes,19,opt,name=time_dimension_chart_type,json=timeDimensionChartType,proto3" json:"time_dimension_chart_type,omitempty"` + TimeDimensionPin bool `protobuf:"varint,20,opt,name=time_dimension_pin,json=timeDimensionPin,proto3" json:"time_dimension_pin,omitempty"` + PivotRows []string `protobuf:"bytes,21,rep,name=pivot_rows,json=pivotRows,proto3" json:"pivot_rows,omitempty"` + PivotCols []string `protobuf:"bytes,22,rep,name=pivot_cols,json=pivotCols,proto3" json:"pivot_cols,omitempty"` + PivotSortBy string `protobuf:"bytes,23,opt,name=pivot_sort_by,json=pivotSortBy,proto3" json:"pivot_sort_by,omitempty"` + PivotSortAsc bool `protobuf:"varint,24,opt,name=pivot_sort_asc,json=pivotSortAsc,proto3" json:"pivot_sort_asc,omitempty"` } func (x *ExplorePreset) Reset() { @@ -2717,6 +2783,20 @@ func (x *ExplorePreset) GetTimeRange() string { return "" } +func (x *ExplorePreset) GetTimezone() string { + if x != nil { + return x.Timezone + } + return "" +} + +func (x *ExplorePreset) GetTimeGrain() string { + if x != nil { + return x.TimeGrain + } + return "" +} + func (x *ExplorePreset) GetComparisonMode() ExploreComparisonMode { if x != nil { return x.ComparisonMode @@ -2724,6 +2804,13 @@ func (x *ExplorePreset) GetComparisonMode() ExploreComparisonMode { return ExploreComparisonMode_EXPLORE_COMPARISON_MODE_UNSPECIFIED } +func (x *ExplorePreset) GetCompareTimeRange() string { + if x != nil { + return x.CompareTimeRange + } + return "" +} + func (x *ExplorePreset) GetComparisonDimension() string { if x != nil { return x.ComparisonDimension @@ -2731,6 +2818,83 @@ func (x *ExplorePreset) GetComparisonDimension() string { return "" } +func (x *ExplorePreset) GetView() ExploreWebView { + if x != nil { + return x.View + } + return ExploreWebView_EXPLORE_ACTIVE_PAGE_OVERVIEW +} + +func (x *ExplorePreset) GetOverviewSortBy() string { + if x != nil { + return x.OverviewSortBy + } + return "" +} + +func (x *ExplorePreset) GetOverviewSortAsc() bool { + if x != nil { + return x.OverviewSortAsc + } + return false +} + +func (x *ExplorePreset) GetOverviewExpandedDimension() string { + if x != nil { + return x.OverviewExpandedDimension + } + return "" +} + +func (x *ExplorePreset) GetTimeDimensionMeasure() string { + if x != nil { + return x.TimeDimensionMeasure + } + return "" +} + +func (x *ExplorePreset) GetTimeDimensionChartType() string { + if x != nil { + return x.TimeDimensionChartType + } + return "" +} + +func (x *ExplorePreset) GetTimeDimensionPin() bool { + if x != nil { + return x.TimeDimensionPin + } + return false +} + +func (x *ExplorePreset) GetPivotRows() []string { + if x != nil { + return x.PivotRows + } + return nil +} + +func (x *ExplorePreset) GetPivotCols() []string { + if x != nil { + return x.PivotCols + } + return nil +} + +func (x *ExplorePreset) GetPivotSortBy() string { + if x != nil { + return x.PivotSortBy + } + return "" +} + +func (x *ExplorePreset) GetPivotSortAsc() bool { + if x != nil { + return x.PivotSortAsc + } + return false +} + // FieldSelector describes logic for selecting a list of fields. // It is useful for dynamically evaluating fields when the list of potential fields is not known at parse time. type FieldSelector struct { @@ -7087,7 +7251,7 @@ var file_rill_runtime_v1_resources_proto_rawDesc = []byte{ 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xa2, 0x03, 0x0a, 0x0d, 0x45, 0x78, 0x70, 0x6c, + 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xfd, 0x07, 0x0a, 0x0d, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x50, 0x72, 0x65, 0x73, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, @@ -7105,601 +7269,648 @@ var file_rill_runtime_v1_resources_proto_rawDesc = []byte{ 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x10, 0x6d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x73, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, - 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x4f, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x70, - 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x26, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, - 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x63, 0x6f, 0x6d, - 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, - 0x73, 0x6f, 0x6e, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xca, 0x01, 0x0a, - 0x0d, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x16, - 0x0a, 0x06, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, - 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x3a, 0x0a, 0x06, 0x66, 0x69, - 0x65, 0x6c, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x06, - 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x16, 0x0a, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x12, 0x2d, - 0x0a, 0x11, 0x64, 0x75, 0x63, 0x6b, 0x64, 0x62, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x10, 0x64, 0x75, 0x63, - 0x6b, 0x64, 0x62, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x0a, 0x0a, - 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x29, 0x0a, 0x0f, 0x53, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x73, 0x22, 0x76, 0x0a, 0x09, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x32, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x52, - 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x59, 0x0a, 0x0d, - 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x12, 0x1c, 0x0a, - 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x73, - 0x71, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x71, 0x6c, 0x12, 0x18, 0x0a, - 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x2a, 0x0a, 0x0e, 0x4d, 0x69, 0x67, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x22, 0x6d, 0x0a, 0x06, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x2f, 0x0a, - 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, - 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x32, - 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x22, 0xe8, 0x05, 0x0a, 0x0a, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, - 0x63, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x74, - 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, - 0x65, 0x12, 0x44, 0x0a, 0x10, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x73, 0x63, 0x68, - 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, - 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, - 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x0f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, - 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x6f, - 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, - 0x12, 0x1d, 0x0a, 0x0a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x71, 0x75, 0x65, 0x72, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x26, 0x0a, 0x0f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x5f, 0x6a, 0x73, - 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x41, - 0x72, 0x67, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x78, 0x70, 0x6f, 0x72, - 0x74, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x65, - 0x78, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x42, 0x0a, 0x0d, 0x65, 0x78, - 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x52, 0x0c, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x37, - 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x09, 0x6e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x4e, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x72, - 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x77, 0x61, 0x74, 0x65, 0x72, - 0x6d, 0x61, 0x72, 0x6b, 0x5f, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x18, 0x0c, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x10, 0x77, 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x68, - 0x65, 0x72, 0x69, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x73, 0x5f, 0x69, 0x73, 0x6f, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0d, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x49, - 0x73, 0x6f, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0e, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x4c, 0x69, - 0x6d, 0x69, 0x74, 0x12, 0x38, 0x0a, 0x18, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, - 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x75, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x18, - 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x1a, 0x3e, 0x0a, - 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x90, 0x02, - 0x0a, 0x0b, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3a, 0x0a, - 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, - 0x6e, 0x65, 0x78, 0x74, 0x52, 0x75, 0x6e, 0x4f, 0x6e, 0x12, 0x4d, 0x0a, 0x11, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x45, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4d, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, - 0x22, 0x81, 0x02, 0x0a, 0x0f, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, - 0x3b, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x65, 0x64, 0x4f, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x69, 0x73, - 0x68, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, - 0x65, 0x64, 0x4f, 0x6e, 0x22, 0x6a, 0x0a, 0x05, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x12, 0x2e, 0x0a, - 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, - 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, - 0x65, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x31, 0x0a, - 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, - 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, - 0x6c, 0x65, 0x72, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x22, 0xeb, 0x08, 0x0a, 0x09, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x18, - 0x0a, 0x07, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x07, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x44, - 0x0a, 0x10, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, - 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, - 0x75, 0x6c, 0x65, 0x52, 0x0f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x63, 0x68, 0x65, - 0x64, 0x75, 0x6c, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x77, 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, - 0x6b, 0x5f, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x10, 0x77, 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x68, 0x65, 0x72, 0x69, - 0x74, 0x12, 0x34, 0x0a, 0x16, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x69, - 0x73, 0x6f, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x14, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x49, 0x73, 0x6f, 0x44, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, - 0x12, 0x38, 0x0a, 0x18, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x63, 0x68, - 0x65, 0x63, 0x6b, 0x5f, 0x75, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x16, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x43, 0x68, 0x65, - 0x63, 0x6b, 0x55, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x69, - 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, - 0x6e, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x71, 0x75, 0x65, 0x72, 0x79, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x72, 0x67, 0x73, - 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x71, 0x75, 0x65, - 0x72, 0x79, 0x41, 0x72, 0x67, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, - 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, - 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x12, 0x48, 0x0a, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, - 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x17, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, - 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, - 0x12, 0x2b, 0x0a, 0x11, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x75, 0x73, - 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0e, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x46, 0x6f, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x31, 0x0a, - 0x14, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, - 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x11, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x46, 0x6f, 0x72, 0x55, 0x73, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, - 0x12, 0x4b, 0x0a, 0x14, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x48, 0x00, 0x52, 0x12, 0x71, 0x75, 0x65, 0x72, 0x79, - 0x46, 0x6f, 0x72, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2a, 0x0a, - 0x11, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x76, - 0x65, 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, - 0x4f, 0x6e, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x79, 0x5f, 0x6f, 0x6e, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0c, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x12, - 0x26, 0x0a, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, - 0x4f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x79, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x72, 0x65, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, - 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x13, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x14, 0x72, 0x65, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x41, 0x66, 0x74, - 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x09, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, + 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x69, 0x6d, 0x65, + 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, + 0x7a, 0x6f, 0x6e, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x67, 0x72, 0x61, + 0x69, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x47, 0x72, + 0x61, 0x69, 0x6e, 0x12, 0x4f, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, + 0x6e, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x72, + 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, + 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, + 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, + 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x10, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, + 0x67, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, + 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x13, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x44, 0x69, 0x6d, 0x65, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x04, 0x76, 0x69, 0x65, 0x77, 0x18, 0x0e, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x57, 0x65, 0x62, + 0x56, 0x69, 0x65, 0x77, 0x52, 0x04, 0x76, 0x69, 0x65, 0x77, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x76, + 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x0f, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x53, 0x6f, + 0x72, 0x74, 0x42, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, + 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x61, 0x73, 0x63, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0f, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x53, 0x6f, 0x72, 0x74, 0x41, 0x73, 0x63, + 0x12, 0x3e, 0x0a, 0x1b, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x65, 0x78, 0x70, + 0x61, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x19, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x45, + 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x34, 0x0a, 0x16, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, + 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x14, 0x74, 0x69, 0x6d, 0x65, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4d, + 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x12, 0x39, 0x0a, 0x19, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, + 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x74, 0x69, 0x6d, 0x65, 0x44, + 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x72, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x69, 0x6e, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x74, + 0x69, 0x6d, 0x65, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x69, 0x6e, 0x12, + 0x1d, 0x0a, 0x0a, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x15, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x09, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x1d, + 0x0a, 0x0a, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x5f, 0x63, 0x6f, 0x6c, 0x73, 0x18, 0x16, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x09, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x43, 0x6f, 0x6c, 0x73, 0x12, 0x22, 0x0a, + 0x0d, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x17, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x53, 0x6f, 0x72, 0x74, 0x42, + 0x79, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, + 0x61, 0x73, 0x63, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x69, 0x76, 0x6f, 0x74, + 0x53, 0x6f, 0x72, 0x74, 0x41, 0x73, 0x63, 0x22, 0xca, 0x01, 0x0a, 0x0d, 0x46, 0x69, 0x65, 0x6c, + 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x6e, 0x76, + 0x65, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x6e, 0x76, 0x65, 0x72, + 0x74, 0x12, 0x12, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, + 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x3a, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, + 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x73, 0x12, 0x16, 0x0a, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x00, 0x52, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x12, 0x2d, 0x0a, 0x11, 0x64, 0x75, 0x63, + 0x6b, 0x64, 0x62, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x10, 0x64, 0x75, 0x63, 0x6b, 0x64, 0x62, 0x45, 0x78, + 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x0a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x22, 0x29, 0x0a, 0x0f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, + 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, + 0x76, 0x0a, 0x09, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x04, + 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, + 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x69, 0x67, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, + 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x59, 0x0a, 0x0d, 0x4d, 0x69, 0x67, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x71, 0x6c, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x71, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x22, 0x2a, 0x0a, 0x0e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x6d, + 0x0a, 0x06, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x2f, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, + 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x32, 0x0a, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xe8, 0x05, + 0x0a, 0x0a, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x18, 0x0a, 0x07, + 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x74, + 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x44, 0x0a, 0x10, + 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, + 0x65, 0x52, 0x0f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, + 0x6c, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, + 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x74, 0x69, 0x6d, + 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x71, 0x75, 0x65, 0x72, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x71, 0x75, + 0x65, 0x72, 0x79, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x41, 0x72, 0x67, 0x73, 0x4a, 0x73, + 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x6d, + 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, + 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x42, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x5f, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x72, + 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, + 0x78, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x0c, 0x65, 0x78, 0x70, + 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x37, 0x0a, 0x09, 0x6e, 0x6f, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, - 0x72, 0x73, 0x12, 0x4d, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, - 0x70, 0x65, 0x63, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x6f, 0x72, 0x22, 0x61, - 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x37, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, - 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, - 0x73, 0x22, 0xc7, 0x02, 0x0a, 0x0a, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1b, 0x0a, - 0x09, 0x72, 0x65, 0x66, 0x73, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x72, 0x65, 0x66, 0x73, 0x48, 0x61, 0x73, 0x68, 0x12, 0x3a, 0x0a, 0x0b, 0x6e, 0x65, - 0x78, 0x74, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x6e, 0x65, 0x78, - 0x74, 0x52, 0x75, 0x6e, 0x4f, 0x6e, 0x12, 0x4c, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x74, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4c, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x72, 0x73, 0x12, 0x4e, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, + 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x53, 0x70, 0x65, 0x63, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x77, 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x5f, + 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x77, + 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x12, + 0x34, 0x0a, 0x16, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x69, 0x73, 0x6f, + 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x14, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x49, 0x73, 0x6f, 0x44, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, + 0x6c, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x38, + 0x0a, 0x18, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, + 0x6b, 0x5f, 0x75, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x16, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x55, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x90, 0x02, 0x0a, 0x0b, 0x52, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, + 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x6e, 0x65, 0x78, 0x74, 0x52, + 0x75, 0x6e, 0x4f, 0x6e, 0x12, 0x4d, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, + 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x4d, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, + 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x65, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x91, 0x03, 0x0a, 0x0e, - 0x41, 0x6c, 0x65, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, - 0x0a, 0x05, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, - 0x64, 0x68, 0x6f, 0x63, 0x12, 0x38, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x2d, - 0x0a, 0x12, 0x73, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x73, 0x65, 0x6e, 0x74, - 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x41, 0x0a, - 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x0d, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, - 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x4f, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, 0x66, - 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x65, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x81, 0x02, 0x0a, 0x0f, + 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x14, 0x0a, 0x05, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, + 0x61, 0x64, 0x68, 0x6f, 0x63, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3b, 0x0a, 0x0b, 0x72, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x72, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, + 0x4f, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x6f, + 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x4f, 0x6e, 0x22, + 0x6a, 0x0a, 0x05, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, 0x70, + 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x31, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, + 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xeb, 0x08, 0x0a, 0x09, + 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x72, 0x69, + 0x67, 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x74, 0x72, 0x69, 0x67, + 0x67, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x44, 0x0a, 0x10, 0x72, 0x65, 0x66, + 0x72, 0x65, 0x73, 0x68, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x0f, + 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, + 0x2b, 0x0a, 0x11, 0x77, 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x5f, 0x69, 0x6e, 0x68, + 0x65, 0x72, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x77, 0x61, 0x74, 0x65, + 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x12, 0x34, 0x0a, 0x16, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x69, 0x73, 0x6f, 0x5f, 0x64, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x49, 0x73, 0x6f, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x38, 0x0a, 0x18, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x75, + 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x6e, 0x63, + 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, + 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1d, + 0x0a, 0x0a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x71, 0x75, 0x65, 0x72, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, + 0x0f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x41, 0x72, 0x67, + 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, + 0x72, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, + 0x72, 0x12, 0x48, 0x0a, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x72, + 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, + 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x46, + 0x6f, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x14, 0x71, 0x75, 0x65, 0x72, + 0x79, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, + 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x11, 0x71, 0x75, 0x65, 0x72, 0x79, 0x46, + 0x6f, 0x72, 0x55, 0x73, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x4b, 0x0a, 0x14, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x48, 0x00, 0x52, 0x12, 0x71, 0x75, 0x65, 0x72, 0x79, 0x46, 0x6f, 0x72, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6e, 0x6f, 0x74, 0x69, + 0x66, 0x79, 0x5f, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x18, 0x0f, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4f, 0x6e, 0x52, 0x65, 0x63, + 0x6f, 0x76, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x6f, + 0x6e, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6e, 0x6f, + 0x74, 0x69, 0x66, 0x79, 0x4f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x6f, + 0x74, 0x69, 0x66, 0x79, 0x5f, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x11, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4f, 0x6e, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x18, 0x12, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x34, + 0x0a, 0x16, 0x72, 0x65, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, + 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, + 0x72, 0x65, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x41, 0x66, 0x74, 0x65, 0x72, 0x53, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, + 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x52, 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x4d, 0x0a, + 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x14, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x41, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3e, 0x0a, 0x10, + 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0b, 0x0a, 0x09, + 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x6f, 0x72, 0x22, 0x61, 0x0a, 0x08, 0x4e, 0x6f, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x12, 0x37, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xc7, 0x02, 0x0a, + 0x0a, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, + 0x70, 0x65, 0x63, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x73, 0x70, 0x65, 0x63, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x66, 0x73, + 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x66, + 0x73, 0x48, 0x61, 0x73, 0x68, 0x12, 0x3a, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x72, 0x75, + 0x6e, 0x5f, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x6e, 0x65, 0x78, 0x74, 0x52, 0x75, 0x6e, 0x4f, + 0x6e, 0x12, 0x4c, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, + 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x6c, 0x65, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x4c, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, + 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, + 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x65, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x27, 0x0a, + 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x91, 0x03, 0x0a, 0x0e, 0x41, 0x6c, 0x65, 0x72, 0x74, + 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x68, + 0x6f, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x12, + 0x38, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x6e, + 0x74, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x73, 0x65, 0x6e, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x41, 0x0a, 0x0e, 0x65, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x66, 0x69, - 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x4f, 0x6e, 0x12, 0x45, 0x0a, 0x10, 0x73, 0x75, 0x70, 0x70, - 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x5f, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0f, - 0x73, 0x75, 0x70, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x53, 0x69, 0x6e, 0x63, 0x65, 0x22, - 0xa4, 0x01, 0x0a, 0x0f, 0x41, 0x73, 0x73, 0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x12, 0x38, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x32, 0x0a, - 0x08, 0x66, 0x61, 0x69, 0x6c, 0x5f, 0x72, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x07, 0x66, 0x61, 0x69, 0x6c, 0x52, 0x6f, - 0x77, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x7c, 0x0a, 0x0b, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, - 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, - 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x37, 0x0a, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x6c, - 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x22, 0x11, 0x0a, 0x0f, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, - 0x67, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x22, 0x12, 0x0a, 0x10, 0x50, 0x75, 0x6c, 0x6c, 0x54, - 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x0e, - 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x37, - 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, - 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x70, 0x65, - 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x3a, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, - 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, - 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x22, 0x8f, 0x01, 0x0a, 0x12, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, - 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x3b, 0x0a, 0x09, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, - 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x09, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x65, 0x78, + 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x65, 0x64, 0x4f, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, + 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, + 0x64, 0x4f, 0x6e, 0x12, 0x45, 0x0a, 0x10, 0x73, 0x75, 0x70, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, + 0x64, 0x5f, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0f, 0x73, 0x75, 0x70, 0x70, 0x72, + 0x65, 0x73, 0x73, 0x65, 0x64, 0x53, 0x69, 0x6e, 0x63, 0x65, 0x22, 0xa4, 0x01, 0x0a, 0x0f, 0x41, + 0x73, 0x73, 0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x38, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, + 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x41, 0x73, 0x73, 0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x32, 0x0a, 0x08, 0x66, 0x61, 0x69, 0x6c, + 0x5f, 0x72, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x52, 0x07, 0x66, 0x61, 0x69, 0x6c, 0x52, 0x6f, 0x77, 0x12, 0x23, 0x0a, 0x0d, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x22, 0x7c, 0x0a, 0x0b, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, + 0x12, 0x34, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, + 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, + 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x37, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, + 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, + 0x11, 0x0a, 0x0f, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x70, + 0x65, 0x63, 0x22, 0x12, 0x0a, 0x10, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, + 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x66, 0x72, 0x65, + 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x04, 0x73, 0x70, 0x65, + 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, - 0x68, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x52, 0x06, 0x6d, - 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x22, 0x15, 0x0a, 0x13, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, - 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x85, 0x01, 0x0a, - 0x13, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x54, 0x72, 0x69, - 0x67, 0x67, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x75, - 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x66, 0x75, 0x6c, 0x6c, 0x12, 0x16, - 0x0a, 0x06, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, - 0x73, 0x70, 0x6c, 0x69, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x61, 0x6c, 0x6c, 0x5f, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x65, 0x64, 0x5f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x10, 0x61, 0x6c, 0x6c, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x65, 0x64, 0x53, 0x70, - 0x6c, 0x69, 0x74, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x0d, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, - 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x36, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, 0x61, - 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x39, - 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, - 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x60, 0x0a, 0x11, 0x42, 0x75, 0x63, - 0x6b, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x4b, - 0x0a, 0x0e, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, - 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x45, - 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0d, 0x65, 0x78, - 0x74, 0x72, 0x61, 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x2c, 0x0a, 0x12, 0x42, - 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x22, 0xd6, 0x02, 0x0a, 0x13, 0x42, 0x75, - 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x12, 0x52, 0x0a, 0x0d, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, - 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, + 0x65, 0x63, 0x12, 0x3a, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, + 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x8f, + 0x01, 0x0a, 0x12, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, + 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x3b, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4d, 0x6f, 0x64, 0x65, + 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, + 0x22, 0x15, 0x0a, 0x13, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, + 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x66, 0x72, + 0x65, 0x73, 0x68, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, + 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x75, 0x6c, 0x6c, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x04, 0x66, 0x75, 0x6c, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x70, 0x6c, + 0x69, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x73, 0x70, 0x6c, 0x69, 0x74, + 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x61, 0x6c, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x65, 0x64, + 0x5f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x61, + 0x6c, 0x6c, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x65, 0x64, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x73, 0x22, + 0x82, 0x01, 0x0a, 0x0d, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, + 0x72, 0x12, 0x36, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x53, + 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x39, 0x0a, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, - 0x74, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, - 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0c, 0x72, 0x6f, 0x77, 0x73, 0x53, 0x74, 0x72, - 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0e, 0x72, 0x6f, 0x77, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, - 0x54, 0x0a, 0x0e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, - 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, - 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x74, - 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x53, 0x74, 0x72, - 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x65, - 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x4a, 0x0a, 0x08, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, - 0x67, 0x79, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, - 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, - 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x10, 0x01, 0x12, - 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x54, 0x41, 0x49, 0x4c, - 0x10, 0x02, 0x22, 0x6a, 0x0a, 0x05, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x73, - 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, - 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x68, 0x65, 0x6d, - 0x65, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x31, 0x0a, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x68, 0x65, - 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xb9, - 0x01, 0x0a, 0x09, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x40, 0x0a, 0x0d, - 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0c, 0x70, - 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x44, - 0x0a, 0x0f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, - 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x48, - 0x01, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, - 0x72, 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, - 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, - 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x0c, 0x0a, 0x0a, 0x54, 0x68, - 0x65, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x76, 0x0a, 0x09, 0x43, 0x6f, 0x6d, 0x70, - 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x22, 0x60, 0x0a, 0x11, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, + 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x4b, 0x0a, 0x0e, 0x65, 0x78, 0x74, + 0x72, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, + 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0d, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x2c, 0x0a, 0x12, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, + 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, + 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, + 0x67, 0x69, 0x6f, 0x6e, 0x22, 0xd6, 0x02, 0x0a, 0x13, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x45, + 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x52, 0x0a, 0x0d, + 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x74, 0x72, + 0x61, 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, + 0x67, 0x79, 0x52, 0x0c, 0x72, 0x6f, 0x77, 0x73, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, + 0x12, 0x28, 0x0a, 0x10, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x72, 0x6f, 0x77, 0x73, + 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x54, 0x0a, 0x0e, 0x66, 0x69, + 0x6c, 0x65, 0x73, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x74, 0x72, 0x61, + 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, + 0x79, 0x52, 0x0d, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, + 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x4c, 0x69, 0x6d, 0x69, + 0x74, 0x22, 0x4a, 0x0a, 0x08, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x18, 0x0a, + 0x14, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, + 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x52, 0x41, 0x54, + 0x45, 0x47, 0x59, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, + 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x54, 0x41, 0x49, 0x4c, 0x10, 0x02, 0x22, 0x6a, 0x0a, + 0x05, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x70, 0x65, 0x63, + 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x31, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xb9, 0x01, 0x0a, 0x09, 0x54, 0x68, + 0x65, 0x6d, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x40, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6d, 0x61, + 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, + 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x44, 0x0a, 0x0f, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x48, 0x01, 0x52, 0x0e, 0x73, 0x65, + 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x42, + 0x10, 0x0a, 0x0e, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, + 0x72, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, + 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x0c, 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x22, 0x76, 0x0a, 0x09, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, + 0x12, 0x32, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, + 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, + 0x73, 0x70, 0x65, 0x63, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, - 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, - 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x22, 0xc3, 0x03, 0x0a, 0x0d, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x70, - 0x65, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x74, - 0x69, 0x74, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x75, 0x62, 0x74, - 0x69, 0x74, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, - 0x12, 0x48, 0x0a, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, - 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, - 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, - 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, - 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x12, 0x48, 0x0a, 0x13, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, - 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, - 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, - 0x12, 0x38, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xc3, 0x03, 0x0a, 0x0d, + 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x14, 0x0a, + 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, + 0x74, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x75, 0x62, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, + 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x12, 0x48, 0x0a, 0x13, 0x72, + 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, + 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, + 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, + 0x72, 0x12, 0x48, 0x0a, 0x13, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x5f, 0x70, 0x72, + 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, + 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x05, 0x69, + 0x6e, 0x70, 0x75, 0x74, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, + 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, + 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x05, + 0x69, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x3a, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, + 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x73, 0x68, 0x6f, 0x77, 0x12, 0x2a, 0x0a, 0x11, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, + 0x5f, 0x69, 0x6e, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x49, 0x6e, 0x43, 0x61, 0x6e, 0x76, 0x61, + 0x73, 0x22, 0x4f, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x73, 0x70, 0x65, + 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, + 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, + 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x70, + 0x65, 0x63, 0x22, 0x78, 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, + 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x3b, 0x0a, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x6d, 0x0a, 0x06, + 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x12, 0x2f, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, + 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x32, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x89, 0x02, 0x0a, 0x0a, + 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, + 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x07, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x61, + 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x67, 0x61, 0x70, 0x12, 0x40, 0x0a, 0x09, + 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, - 0x62, 0x6c, 0x65, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x3a, 0x0a, 0x06, 0x6f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, - 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x06, - 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x18, 0x0a, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x12, 0x2a, 0x0a, 0x11, 0x64, 0x65, - 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x49, 0x6e, - 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x22, 0x4f, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, - 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, - 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x09, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x53, 0x70, 0x65, 0x63, 0x22, 0x78, 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x6f, - 0x6e, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x12, 0x3b, 0x0a, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x22, 0x6d, 0x0a, 0x06, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x12, 0x2f, 0x0a, 0x04, 0x73, - 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, - 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, - 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x32, 0x0a, 0x05, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x69, - 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, - 0x6e, 0x76, 0x61, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x22, 0x89, 0x02, 0x0a, 0x0a, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, 0x12, - 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x12, - 0x10, 0x0a, 0x03, 0x67, 0x61, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x67, 0x61, - 0x70, 0x12, 0x40, 0x0a, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x05, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, - 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, - 0x6c, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x52, - 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x44, 0x0a, 0x0e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, - 0x74, 0x79, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, + 0x62, 0x6c, 0x65, 0x52, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x31, + 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, + 0x73, 0x12, 0x44, 0x0a, 0x0e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x72, 0x75, + 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, + 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x75, + 0x72, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, + 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x49, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x76, 0x61, + 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, + 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, + 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, + 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x70, + 0x65, 0x63, 0x22, 0xd5, 0x01, 0x0a, 0x0a, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x49, 0x74, 0x65, + 0x6d, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, + 0x2a, 0x0a, 0x11, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x63, 0x61, + 0x6e, 0x76, 0x61, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x65, 0x66, 0x69, + 0x6e, 0x65, 0x64, 0x49, 0x6e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x12, 0x11, 0x0a, 0x01, 0x78, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x01, 0x78, 0x88, 0x01, 0x01, 0x12, 0x11, + 0x0a, 0x01, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x01, 0x52, 0x01, 0x79, 0x88, 0x01, + 0x01, 0x12, 0x19, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, + 0x48, 0x02, 0x52, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x03, 0x52, 0x06, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x42, 0x04, 0x0a, 0x02, 0x5f, 0x78, 0x42, + 0x04, 0x0a, 0x02, 0x5f, 0x79, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x42, + 0x09, 0x0a, 0x07, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x64, 0x0a, 0x03, 0x41, 0x50, + 0x49, 0x12, 0x2c, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x18, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x41, 0x50, 0x49, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, + 0x2f, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x73, - 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x49, 0x0a, 0x0b, - 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0a, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, 0x52, 0x09, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x53, 0x70, 0x65, 0x63, 0x22, 0xd5, 0x01, 0x0a, 0x0a, 0x43, 0x61, 0x6e, 0x76, - 0x61, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, - 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, - 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, - 0x69, 0x6e, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x49, 0x6e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, - 0x12, 0x11, 0x0a, 0x01, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x01, 0x78, - 0x88, 0x01, 0x01, 0x12, 0x11, 0x0a, 0x01, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x01, - 0x52, 0x01, 0x79, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x02, 0x52, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x88, 0x01, - 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0d, 0x48, 0x03, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x42, 0x04, - 0x0a, 0x02, 0x5f, 0x78, 0x42, 0x04, 0x0a, 0x02, 0x5f, 0x79, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x77, - 0x69, 0x64, 0x74, 0x68, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, - 0x64, 0x0a, 0x03, 0x41, 0x50, 0x49, 0x12, 0x2c, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, - 0x73, 0x70, 0x65, 0x63, 0x12, 0x2f, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xb1, 0x02, 0x0a, 0x07, 0x41, 0x50, 0x49, 0x53, 0x70, 0x65, - 0x63, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x12, 0x48, 0x0a, - 0x13, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, - 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, - 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, - 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6f, 0x70, 0x65, 0x6e, 0x61, - 0x70, 0x69, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, - 0x12, 0x46, 0x0a, 0x12, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x70, 0x61, 0x72, 0x61, - 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x11, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x4f, 0x0a, 0x17, 0x6f, 0x70, 0x65, 0x6e, - 0x61, 0x70, 0x69, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, - 0x65, 0x6d, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x52, 0x15, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x0a, 0x0a, 0x08, 0x41, 0x50, 0x49, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x9b, 0x01, 0x0a, 0x08, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, - 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, 0x66, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, - 0x72, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x12, - 0x25, 0x0a, 0x0e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, - 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x5a, - 0x6f, 0x6e, 0x65, 0x22, 0xa5, 0x01, 0x0a, 0x0a, 0x50, 0x61, 0x72, 0x73, 0x65, 0x45, 0x72, 0x72, - 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, - 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x44, 0x0a, 0x0e, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x0d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x22, 0x50, 0x0a, 0x0f, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, - 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x70, - 0x65, 0x72, 0x74, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0c, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x50, 0x61, 0x74, 0x68, 0x22, 0x4b, 0x0a, - 0x0f, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x45, 0x72, 0x72, 0x6f, 0x72, - 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, - 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x2a, 0x0a, 0x0e, 0x45, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x22, 0x0a, 0x0c, 0x43, 0x68, 0x61, 0x72, 0x4c, 0x6f, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0xae, 0x03, 0x0a, 0x0d, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x16, 0x0a, 0x06, - 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x72, - 0x69, 0x76, 0x65, 0x72, 0x12, 0x4e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, - 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, - 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, - 0x74, 0x69, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x13, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, - 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x77, 0x0a, 0x19, 0x70, 0x72, 0x6f, 0x70, 0x65, - 0x72, 0x74, 0x69, 0x65, 0x73, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, - 0x62, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, - 0x72, 0x74, 0x69, 0x65, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, - 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x17, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, - 0x69, 0x65, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, - 0x1a, 0x3d, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, - 0x4a, 0x0a, 0x1c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x46, 0x72, 0x6f, - 0x6d, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x2d, 0x0a, 0x0e, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, - 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, 0x48, 0x61, 0x73, 0x68, 0x22, 0x78, 0x0a, 0x0b, 0x43, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x56, 0x32, 0x12, 0x32, 0x0a, 0x04, 0x73, 0x70, 0x65, - 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x35, 0x0a, - 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, + 0x2e, 0x41, 0x50, 0x49, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x22, 0xb1, 0x02, 0x0a, 0x07, 0x41, 0x50, 0x49, 0x53, 0x70, 0x65, 0x63, 0x12, 0x1a, 0x0a, 0x08, + 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x12, 0x48, 0x0a, 0x13, 0x72, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, + 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, + 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x73, 0x75, + 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x70, 0x65, + 0x6e, 0x61, 0x70, 0x69, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x46, 0x0a, 0x12, 0x6f, + 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x52, 0x11, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, + 0x65, 0x72, 0x73, 0x12, 0x4f, 0x0a, 0x17, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x72, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x15, 0x6f, + 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x22, 0x0a, 0x0a, 0x08, 0x41, 0x50, 0x49, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x22, 0x9b, 0x01, 0x0a, 0x08, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, + 0x0a, 0x72, 0x65, 0x66, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x09, 0x72, 0x65, 0x66, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, + 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x69, + 0x63, 0x6b, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x22, 0xa5, + 0x01, 0x0a, 0x0a, 0x50, 0x61, 0x72, 0x73, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, + 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, + 0x50, 0x61, 0x74, 0x68, 0x12, 0x44, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x2a, 0x8a, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, - 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x45, 0x43, 0x4f, - 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, - 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, - 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, - 0x44, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, - 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, - 0x47, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, - 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, - 0x03, 0x2a, 0xab, 0x01, 0x0a, 0x15, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6d, - 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x27, 0x0a, 0x23, 0x45, - 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, - 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x20, 0x0a, 0x1c, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, - 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, - 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, - 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, - 0x45, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x10, 0x02, 0x12, 0x25, 0x0a, 0x21, 0x45, 0x58, 0x50, 0x4c, - 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, - 0x4f, 0x44, 0x45, 0x5f, 0x44, 0x49, 0x4d, 0x45, 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x2a, - 0x85, 0x01, 0x0a, 0x0f, 0x41, 0x73, 0x73, 0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x1c, 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, + 0x68, 0x61, 0x72, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x78, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x22, 0x50, 0x0a, 0x0f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x5f, + 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x70, + 0x65, 0x72, 0x74, 0x79, 0x50, 0x61, 0x74, 0x68, 0x22, 0x4b, 0x0a, 0x0f, 0x44, 0x65, 0x70, 0x65, + 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, + 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x65, 0x6e, + 0x64, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x2a, 0x0a, 0x0e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x22, 0x22, 0x0a, 0x0c, 0x43, 0x68, 0x61, 0x72, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0xae, 0x03, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x12, + 0x4e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, + 0x70, 0x65, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, + 0x31, 0x0a, 0x14, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, + 0x65, 0x73, 0x12, 0x77, 0x0a, 0x19, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, + 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, + 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x17, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x46, 0x72, + 0x6f, 0x6d, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x50, + 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4a, 0x0a, 0x1c, 0x50, 0x72, + 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x72, 0x69, + 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x2d, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, + 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, + 0x63, 0x48, 0x61, 0x73, 0x68, 0x22, 0x78, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x56, 0x32, 0x12, 0x32, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x70, + 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, + 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2a, + 0x8a, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, - 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, - 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x41, 0x53, 0x53, 0x10, 0x01, - 0x12, 0x19, 0x0a, 0x15, 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, - 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x41, - 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, - 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x42, 0xc1, 0x01, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, - 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x42, - 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, - 0x01, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x69, - 0x6c, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x72, 0x69, 0x6c, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x72, 0x69, 0x6c, 0x6c, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x76, 0x31, 0xa2, - 0x02, 0x03, 0x52, 0x52, 0x58, 0xaa, 0x02, 0x0f, 0x52, 0x69, 0x6c, 0x6c, 0x2e, 0x52, 0x75, 0x6e, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0f, 0x52, 0x69, 0x6c, 0x6c, 0x5c, 0x52, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x1b, 0x52, 0x69, 0x6c, 0x6c, - 0x5c, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x11, 0x52, 0x69, 0x6c, 0x6c, 0x3a, 0x3a, - 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, + 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x01, + 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x54, + 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x1c, + 0x0a, 0x18, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, + 0x55, 0x53, 0x5f, 0x52, 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x03, 0x2a, 0xab, 0x01, 0x0a, + 0x15, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, + 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x27, 0x0a, 0x23, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, + 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, + 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, + 0x20, 0x0a, 0x1c, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, + 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, + 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, + 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x54, 0x49, 0x4d, + 0x45, 0x10, 0x02, 0x12, 0x25, 0x0a, 0x21, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, + 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x44, + 0x49, 0x4d, 0x45, 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x2a, 0x99, 0x01, 0x0a, 0x0e, 0x45, + 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x57, 0x65, 0x62, 0x56, 0x69, 0x65, 0x77, 0x12, 0x20, 0x0a, + 0x1c, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, + 0x50, 0x41, 0x47, 0x45, 0x5f, 0x4f, 0x56, 0x45, 0x52, 0x56, 0x49, 0x45, 0x57, 0x10, 0x00, 0x12, + 0x26, 0x0a, 0x22, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, + 0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x5f, 0x44, 0x49, 0x4d, 0x45, + 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, 0x45, 0x58, 0x50, 0x4c, 0x4f, + 0x52, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x50, + 0x49, 0x56, 0x4f, 0x54, 0x10, 0x02, 0x12, 0x1e, 0x0a, 0x1a, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, + 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x43, 0x41, + 0x4e, 0x56, 0x41, 0x53, 0x10, 0x03, 0x2a, 0x85, 0x01, 0x0a, 0x0f, 0x41, 0x73, 0x73, 0x65, 0x72, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x1c, 0x41, 0x53, + 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, + 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, + 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, + 0x5f, 0x50, 0x41, 0x53, 0x53, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x53, 0x53, 0x45, 0x52, + 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x41, 0x49, 0x4c, + 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, + 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x42, 0xc1, + 0x01, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x69, 0x6c, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x72, 0x69, + 0x6c, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x72, 0x69, 0x6c, + 0x6c, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x52, 0x52, 0x58, 0xaa, 0x02, 0x0f, 0x52, + 0x69, 0x6c, 0x6c, 0x2e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, + 0x0f, 0x52, 0x69, 0x6c, 0x6c, 0x5c, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5c, 0x56, 0x31, + 0xe2, 0x02, 0x1b, 0x52, 0x69, 0x6c, 0x6c, 0x5c, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5c, + 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, + 0x11, 0x52, 0x69, 0x6c, 0x6c, 0x3a, 0x3a, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x3a, 0x3a, + 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -7714,265 +7925,267 @@ func file_rill_runtime_v1_resources_proto_rawDescGZIP() []byte { return file_rill_runtime_v1_resources_proto_rawDescData } -var file_rill_runtime_v1_resources_proto_enumTypes = make([]protoimpl.EnumInfo, 6) +var file_rill_runtime_v1_resources_proto_enumTypes = make([]protoimpl.EnumInfo, 7) var file_rill_runtime_v1_resources_proto_msgTypes = make([]protoimpl.MessageInfo, 84) var file_rill_runtime_v1_resources_proto_goTypes = []any{ (ReconcileStatus)(0), // 0: rill.runtime.v1.ReconcileStatus (ExploreComparisonMode)(0), // 1: rill.runtime.v1.ExploreComparisonMode - (AssertionStatus)(0), // 2: rill.runtime.v1.AssertionStatus - (MetricsViewSpec_MeasureType)(0), // 3: rill.runtime.v1.MetricsViewSpec.MeasureType - (MetricsViewSpec_ComparisonMode)(0), // 4: rill.runtime.v1.MetricsViewSpec.ComparisonMode - (BucketExtractPolicy_Strategy)(0), // 5: rill.runtime.v1.BucketExtractPolicy.Strategy - (*Resource)(nil), // 6: rill.runtime.v1.Resource - (*ResourceMeta)(nil), // 7: rill.runtime.v1.ResourceMeta - (*ResourceName)(nil), // 8: rill.runtime.v1.ResourceName - (*ProjectParser)(nil), // 9: rill.runtime.v1.ProjectParser - (*ProjectParserSpec)(nil), // 10: rill.runtime.v1.ProjectParserSpec - (*ProjectParserState)(nil), // 11: rill.runtime.v1.ProjectParserState - (*SourceV2)(nil), // 12: rill.runtime.v1.SourceV2 - (*SourceSpec)(nil), // 13: rill.runtime.v1.SourceSpec - (*SourceState)(nil), // 14: rill.runtime.v1.SourceState - (*ModelV2)(nil), // 15: rill.runtime.v1.ModelV2 - (*ModelSpec)(nil), // 16: rill.runtime.v1.ModelSpec - (*ModelState)(nil), // 17: rill.runtime.v1.ModelState - (*MetricsViewV2)(nil), // 18: rill.runtime.v1.MetricsViewV2 - (*MetricsViewSpec)(nil), // 19: rill.runtime.v1.MetricsViewSpec - (*SecurityRule)(nil), // 20: rill.runtime.v1.SecurityRule - (*SecurityRuleAccess)(nil), // 21: rill.runtime.v1.SecurityRuleAccess - (*SecurityRuleFieldAccess)(nil), // 22: rill.runtime.v1.SecurityRuleFieldAccess - (*SecurityRuleRowFilter)(nil), // 23: rill.runtime.v1.SecurityRuleRowFilter - (*MetricsViewState)(nil), // 24: rill.runtime.v1.MetricsViewState - (*Explore)(nil), // 25: rill.runtime.v1.Explore - (*ExploreSpec)(nil), // 26: rill.runtime.v1.ExploreSpec - (*ExploreState)(nil), // 27: rill.runtime.v1.ExploreState - (*ExploreTimeRange)(nil), // 28: rill.runtime.v1.ExploreTimeRange - (*ExploreComparisonTimeRange)(nil), // 29: rill.runtime.v1.ExploreComparisonTimeRange - (*ExplorePreset)(nil), // 30: rill.runtime.v1.ExplorePreset - (*FieldSelector)(nil), // 31: rill.runtime.v1.FieldSelector - (*StringListValue)(nil), // 32: rill.runtime.v1.StringListValue - (*Migration)(nil), // 33: rill.runtime.v1.Migration - (*MigrationSpec)(nil), // 34: rill.runtime.v1.MigrationSpec - (*MigrationState)(nil), // 35: rill.runtime.v1.MigrationState - (*Report)(nil), // 36: rill.runtime.v1.Report - (*ReportSpec)(nil), // 37: rill.runtime.v1.ReportSpec - (*ReportState)(nil), // 38: rill.runtime.v1.ReportState - (*ReportExecution)(nil), // 39: rill.runtime.v1.ReportExecution - (*Alert)(nil), // 40: rill.runtime.v1.Alert - (*AlertSpec)(nil), // 41: rill.runtime.v1.AlertSpec - (*Notifier)(nil), // 42: rill.runtime.v1.Notifier - (*AlertState)(nil), // 43: rill.runtime.v1.AlertState - (*AlertExecution)(nil), // 44: rill.runtime.v1.AlertExecution - (*AssertionResult)(nil), // 45: rill.runtime.v1.AssertionResult - (*PullTrigger)(nil), // 46: rill.runtime.v1.PullTrigger - (*PullTriggerSpec)(nil), // 47: rill.runtime.v1.PullTriggerSpec - (*PullTriggerState)(nil), // 48: rill.runtime.v1.PullTriggerState - (*RefreshTrigger)(nil), // 49: rill.runtime.v1.RefreshTrigger - (*RefreshTriggerSpec)(nil), // 50: rill.runtime.v1.RefreshTriggerSpec - (*RefreshTriggerState)(nil), // 51: rill.runtime.v1.RefreshTriggerState - (*RefreshModelTrigger)(nil), // 52: rill.runtime.v1.RefreshModelTrigger - (*BucketPlanner)(nil), // 53: rill.runtime.v1.BucketPlanner - (*BucketPlannerSpec)(nil), // 54: rill.runtime.v1.BucketPlannerSpec - (*BucketPlannerState)(nil), // 55: rill.runtime.v1.BucketPlannerState - (*BucketExtractPolicy)(nil), // 56: rill.runtime.v1.BucketExtractPolicy - (*Theme)(nil), // 57: rill.runtime.v1.Theme - (*ThemeSpec)(nil), // 58: rill.runtime.v1.ThemeSpec - (*ThemeState)(nil), // 59: rill.runtime.v1.ThemeState - (*Component)(nil), // 60: rill.runtime.v1.Component - (*ComponentSpec)(nil), // 61: rill.runtime.v1.ComponentSpec - (*ComponentState)(nil), // 62: rill.runtime.v1.ComponentState - (*ComponentVariable)(nil), // 63: rill.runtime.v1.ComponentVariable - (*Canvas)(nil), // 64: rill.runtime.v1.Canvas - (*CanvasSpec)(nil), // 65: rill.runtime.v1.CanvasSpec - (*CanvasState)(nil), // 66: rill.runtime.v1.CanvasState - (*CanvasItem)(nil), // 67: rill.runtime.v1.CanvasItem - (*API)(nil), // 68: rill.runtime.v1.API - (*APISpec)(nil), // 69: rill.runtime.v1.APISpec - (*APIState)(nil), // 70: rill.runtime.v1.APIState - (*Schedule)(nil), // 71: rill.runtime.v1.Schedule - (*ParseError)(nil), // 72: rill.runtime.v1.ParseError - (*ValidationError)(nil), // 73: rill.runtime.v1.ValidationError - (*DependencyError)(nil), // 74: rill.runtime.v1.DependencyError - (*ExecutionError)(nil), // 75: rill.runtime.v1.ExecutionError - (*CharLocation)(nil), // 76: rill.runtime.v1.CharLocation - (*ConnectorSpec)(nil), // 77: rill.runtime.v1.ConnectorSpec - (*ConnectorState)(nil), // 78: rill.runtime.v1.ConnectorState - (*ConnectorV2)(nil), // 79: rill.runtime.v1.ConnectorV2 - (*MetricsViewSpec_DimensionV2)(nil), // 80: rill.runtime.v1.MetricsViewSpec.DimensionV2 - (*MetricsViewSpec_DimensionSelector)(nil), // 81: rill.runtime.v1.MetricsViewSpec.DimensionSelector - (*MetricsViewSpec_MeasureWindow)(nil), // 82: rill.runtime.v1.MetricsViewSpec.MeasureWindow - (*MetricsViewSpec_MeasureV2)(nil), // 83: rill.runtime.v1.MetricsViewSpec.MeasureV2 - (*MetricsViewSpec_AvailableComparisonOffset)(nil), // 84: rill.runtime.v1.MetricsViewSpec.AvailableComparisonOffset - (*MetricsViewSpec_AvailableTimeRange)(nil), // 85: rill.runtime.v1.MetricsViewSpec.AvailableTimeRange - nil, // 86: rill.runtime.v1.ReportSpec.AnnotationsEntry - nil, // 87: rill.runtime.v1.AlertSpec.AnnotationsEntry - nil, // 88: rill.runtime.v1.ConnectorSpec.PropertiesEntry - nil, // 89: rill.runtime.v1.ConnectorSpec.PropertiesFromVariablesEntry - (*timestamppb.Timestamp)(nil), // 90: google.protobuf.Timestamp - (*structpb.Struct)(nil), // 91: google.protobuf.Struct - (*StructType)(nil), // 92: rill.runtime.v1.StructType - (TimeGrain)(0), // 93: rill.runtime.v1.TimeGrain - (*Expression)(nil), // 94: rill.runtime.v1.Expression - (ExportFormat)(0), // 95: rill.runtime.v1.ExportFormat - (*Color)(nil), // 96: rill.runtime.v1.Color - (*structpb.Value)(nil), // 97: google.protobuf.Value + (ExploreWebView)(0), // 2: rill.runtime.v1.ExploreWebView + (AssertionStatus)(0), // 3: rill.runtime.v1.AssertionStatus + (MetricsViewSpec_MeasureType)(0), // 4: rill.runtime.v1.MetricsViewSpec.MeasureType + (MetricsViewSpec_ComparisonMode)(0), // 5: rill.runtime.v1.MetricsViewSpec.ComparisonMode + (BucketExtractPolicy_Strategy)(0), // 6: rill.runtime.v1.BucketExtractPolicy.Strategy + (*Resource)(nil), // 7: rill.runtime.v1.Resource + (*ResourceMeta)(nil), // 8: rill.runtime.v1.ResourceMeta + (*ResourceName)(nil), // 9: rill.runtime.v1.ResourceName + (*ProjectParser)(nil), // 10: rill.runtime.v1.ProjectParser + (*ProjectParserSpec)(nil), // 11: rill.runtime.v1.ProjectParserSpec + (*ProjectParserState)(nil), // 12: rill.runtime.v1.ProjectParserState + (*SourceV2)(nil), // 13: rill.runtime.v1.SourceV2 + (*SourceSpec)(nil), // 14: rill.runtime.v1.SourceSpec + (*SourceState)(nil), // 15: rill.runtime.v1.SourceState + (*ModelV2)(nil), // 16: rill.runtime.v1.ModelV2 + (*ModelSpec)(nil), // 17: rill.runtime.v1.ModelSpec + (*ModelState)(nil), // 18: rill.runtime.v1.ModelState + (*MetricsViewV2)(nil), // 19: rill.runtime.v1.MetricsViewV2 + (*MetricsViewSpec)(nil), // 20: rill.runtime.v1.MetricsViewSpec + (*SecurityRule)(nil), // 21: rill.runtime.v1.SecurityRule + (*SecurityRuleAccess)(nil), // 22: rill.runtime.v1.SecurityRuleAccess + (*SecurityRuleFieldAccess)(nil), // 23: rill.runtime.v1.SecurityRuleFieldAccess + (*SecurityRuleRowFilter)(nil), // 24: rill.runtime.v1.SecurityRuleRowFilter + (*MetricsViewState)(nil), // 25: rill.runtime.v1.MetricsViewState + (*Explore)(nil), // 26: rill.runtime.v1.Explore + (*ExploreSpec)(nil), // 27: rill.runtime.v1.ExploreSpec + (*ExploreState)(nil), // 28: rill.runtime.v1.ExploreState + (*ExploreTimeRange)(nil), // 29: rill.runtime.v1.ExploreTimeRange + (*ExploreComparisonTimeRange)(nil), // 30: rill.runtime.v1.ExploreComparisonTimeRange + (*ExplorePreset)(nil), // 31: rill.runtime.v1.ExplorePreset + (*FieldSelector)(nil), // 32: rill.runtime.v1.FieldSelector + (*StringListValue)(nil), // 33: rill.runtime.v1.StringListValue + (*Migration)(nil), // 34: rill.runtime.v1.Migration + (*MigrationSpec)(nil), // 35: rill.runtime.v1.MigrationSpec + (*MigrationState)(nil), // 36: rill.runtime.v1.MigrationState + (*Report)(nil), // 37: rill.runtime.v1.Report + (*ReportSpec)(nil), // 38: rill.runtime.v1.ReportSpec + (*ReportState)(nil), // 39: rill.runtime.v1.ReportState + (*ReportExecution)(nil), // 40: rill.runtime.v1.ReportExecution + (*Alert)(nil), // 41: rill.runtime.v1.Alert + (*AlertSpec)(nil), // 42: rill.runtime.v1.AlertSpec + (*Notifier)(nil), // 43: rill.runtime.v1.Notifier + (*AlertState)(nil), // 44: rill.runtime.v1.AlertState + (*AlertExecution)(nil), // 45: rill.runtime.v1.AlertExecution + (*AssertionResult)(nil), // 46: rill.runtime.v1.AssertionResult + (*PullTrigger)(nil), // 47: rill.runtime.v1.PullTrigger + (*PullTriggerSpec)(nil), // 48: rill.runtime.v1.PullTriggerSpec + (*PullTriggerState)(nil), // 49: rill.runtime.v1.PullTriggerState + (*RefreshTrigger)(nil), // 50: rill.runtime.v1.RefreshTrigger + (*RefreshTriggerSpec)(nil), // 51: rill.runtime.v1.RefreshTriggerSpec + (*RefreshTriggerState)(nil), // 52: rill.runtime.v1.RefreshTriggerState + (*RefreshModelTrigger)(nil), // 53: rill.runtime.v1.RefreshModelTrigger + (*BucketPlanner)(nil), // 54: rill.runtime.v1.BucketPlanner + (*BucketPlannerSpec)(nil), // 55: rill.runtime.v1.BucketPlannerSpec + (*BucketPlannerState)(nil), // 56: rill.runtime.v1.BucketPlannerState + (*BucketExtractPolicy)(nil), // 57: rill.runtime.v1.BucketExtractPolicy + (*Theme)(nil), // 58: rill.runtime.v1.Theme + (*ThemeSpec)(nil), // 59: rill.runtime.v1.ThemeSpec + (*ThemeState)(nil), // 60: rill.runtime.v1.ThemeState + (*Component)(nil), // 61: rill.runtime.v1.Component + (*ComponentSpec)(nil), // 62: rill.runtime.v1.ComponentSpec + (*ComponentState)(nil), // 63: rill.runtime.v1.ComponentState + (*ComponentVariable)(nil), // 64: rill.runtime.v1.ComponentVariable + (*Canvas)(nil), // 65: rill.runtime.v1.Canvas + (*CanvasSpec)(nil), // 66: rill.runtime.v1.CanvasSpec + (*CanvasState)(nil), // 67: rill.runtime.v1.CanvasState + (*CanvasItem)(nil), // 68: rill.runtime.v1.CanvasItem + (*API)(nil), // 69: rill.runtime.v1.API + (*APISpec)(nil), // 70: rill.runtime.v1.APISpec + (*APIState)(nil), // 71: rill.runtime.v1.APIState + (*Schedule)(nil), // 72: rill.runtime.v1.Schedule + (*ParseError)(nil), // 73: rill.runtime.v1.ParseError + (*ValidationError)(nil), // 74: rill.runtime.v1.ValidationError + (*DependencyError)(nil), // 75: rill.runtime.v1.DependencyError + (*ExecutionError)(nil), // 76: rill.runtime.v1.ExecutionError + (*CharLocation)(nil), // 77: rill.runtime.v1.CharLocation + (*ConnectorSpec)(nil), // 78: rill.runtime.v1.ConnectorSpec + (*ConnectorState)(nil), // 79: rill.runtime.v1.ConnectorState + (*ConnectorV2)(nil), // 80: rill.runtime.v1.ConnectorV2 + (*MetricsViewSpec_DimensionV2)(nil), // 81: rill.runtime.v1.MetricsViewSpec.DimensionV2 + (*MetricsViewSpec_DimensionSelector)(nil), // 82: rill.runtime.v1.MetricsViewSpec.DimensionSelector + (*MetricsViewSpec_MeasureWindow)(nil), // 83: rill.runtime.v1.MetricsViewSpec.MeasureWindow + (*MetricsViewSpec_MeasureV2)(nil), // 84: rill.runtime.v1.MetricsViewSpec.MeasureV2 + (*MetricsViewSpec_AvailableComparisonOffset)(nil), // 85: rill.runtime.v1.MetricsViewSpec.AvailableComparisonOffset + (*MetricsViewSpec_AvailableTimeRange)(nil), // 86: rill.runtime.v1.MetricsViewSpec.AvailableTimeRange + nil, // 87: rill.runtime.v1.ReportSpec.AnnotationsEntry + nil, // 88: rill.runtime.v1.AlertSpec.AnnotationsEntry + nil, // 89: rill.runtime.v1.ConnectorSpec.PropertiesEntry + nil, // 90: rill.runtime.v1.ConnectorSpec.PropertiesFromVariablesEntry + (*timestamppb.Timestamp)(nil), // 91: google.protobuf.Timestamp + (*structpb.Struct)(nil), // 92: google.protobuf.Struct + (*StructType)(nil), // 93: rill.runtime.v1.StructType + (TimeGrain)(0), // 94: rill.runtime.v1.TimeGrain + (*Expression)(nil), // 95: rill.runtime.v1.Expression + (ExportFormat)(0), // 96: rill.runtime.v1.ExportFormat + (*Color)(nil), // 97: rill.runtime.v1.Color + (*structpb.Value)(nil), // 98: google.protobuf.Value } var file_rill_runtime_v1_resources_proto_depIdxs = []int32{ - 7, // 0: rill.runtime.v1.Resource.meta:type_name -> rill.runtime.v1.ResourceMeta - 9, // 1: rill.runtime.v1.Resource.project_parser:type_name -> rill.runtime.v1.ProjectParser - 12, // 2: rill.runtime.v1.Resource.source:type_name -> rill.runtime.v1.SourceV2 - 15, // 3: rill.runtime.v1.Resource.model:type_name -> rill.runtime.v1.ModelV2 - 18, // 4: rill.runtime.v1.Resource.metrics_view:type_name -> rill.runtime.v1.MetricsViewV2 - 25, // 5: rill.runtime.v1.Resource.explore:type_name -> rill.runtime.v1.Explore - 33, // 6: rill.runtime.v1.Resource.migration:type_name -> rill.runtime.v1.Migration - 36, // 7: rill.runtime.v1.Resource.report:type_name -> rill.runtime.v1.Report - 40, // 8: rill.runtime.v1.Resource.alert:type_name -> rill.runtime.v1.Alert - 46, // 9: rill.runtime.v1.Resource.pull_trigger:type_name -> rill.runtime.v1.PullTrigger - 49, // 10: rill.runtime.v1.Resource.refresh_trigger:type_name -> rill.runtime.v1.RefreshTrigger - 53, // 11: rill.runtime.v1.Resource.bucket_planner:type_name -> rill.runtime.v1.BucketPlanner - 57, // 12: rill.runtime.v1.Resource.theme:type_name -> rill.runtime.v1.Theme - 60, // 13: rill.runtime.v1.Resource.component:type_name -> rill.runtime.v1.Component - 64, // 14: rill.runtime.v1.Resource.canvas:type_name -> rill.runtime.v1.Canvas - 68, // 15: rill.runtime.v1.Resource.api:type_name -> rill.runtime.v1.API - 79, // 16: rill.runtime.v1.Resource.connector:type_name -> rill.runtime.v1.ConnectorV2 - 8, // 17: rill.runtime.v1.ResourceMeta.name:type_name -> rill.runtime.v1.ResourceName - 8, // 18: rill.runtime.v1.ResourceMeta.refs:type_name -> rill.runtime.v1.ResourceName - 8, // 19: rill.runtime.v1.ResourceMeta.owner:type_name -> rill.runtime.v1.ResourceName - 90, // 20: rill.runtime.v1.ResourceMeta.created_on:type_name -> google.protobuf.Timestamp - 90, // 21: rill.runtime.v1.ResourceMeta.spec_updated_on:type_name -> google.protobuf.Timestamp - 90, // 22: rill.runtime.v1.ResourceMeta.state_updated_on:type_name -> google.protobuf.Timestamp - 90, // 23: rill.runtime.v1.ResourceMeta.deleted_on:type_name -> google.protobuf.Timestamp + 8, // 0: rill.runtime.v1.Resource.meta:type_name -> rill.runtime.v1.ResourceMeta + 10, // 1: rill.runtime.v1.Resource.project_parser:type_name -> rill.runtime.v1.ProjectParser + 13, // 2: rill.runtime.v1.Resource.source:type_name -> rill.runtime.v1.SourceV2 + 16, // 3: rill.runtime.v1.Resource.model:type_name -> rill.runtime.v1.ModelV2 + 19, // 4: rill.runtime.v1.Resource.metrics_view:type_name -> rill.runtime.v1.MetricsViewV2 + 26, // 5: rill.runtime.v1.Resource.explore:type_name -> rill.runtime.v1.Explore + 34, // 6: rill.runtime.v1.Resource.migration:type_name -> rill.runtime.v1.Migration + 37, // 7: rill.runtime.v1.Resource.report:type_name -> rill.runtime.v1.Report + 41, // 8: rill.runtime.v1.Resource.alert:type_name -> rill.runtime.v1.Alert + 47, // 9: rill.runtime.v1.Resource.pull_trigger:type_name -> rill.runtime.v1.PullTrigger + 50, // 10: rill.runtime.v1.Resource.refresh_trigger:type_name -> rill.runtime.v1.RefreshTrigger + 54, // 11: rill.runtime.v1.Resource.bucket_planner:type_name -> rill.runtime.v1.BucketPlanner + 58, // 12: rill.runtime.v1.Resource.theme:type_name -> rill.runtime.v1.Theme + 61, // 13: rill.runtime.v1.Resource.component:type_name -> rill.runtime.v1.Component + 65, // 14: rill.runtime.v1.Resource.canvas:type_name -> rill.runtime.v1.Canvas + 69, // 15: rill.runtime.v1.Resource.api:type_name -> rill.runtime.v1.API + 80, // 16: rill.runtime.v1.Resource.connector:type_name -> rill.runtime.v1.ConnectorV2 + 9, // 17: rill.runtime.v1.ResourceMeta.name:type_name -> rill.runtime.v1.ResourceName + 9, // 18: rill.runtime.v1.ResourceMeta.refs:type_name -> rill.runtime.v1.ResourceName + 9, // 19: rill.runtime.v1.ResourceMeta.owner:type_name -> rill.runtime.v1.ResourceName + 91, // 20: rill.runtime.v1.ResourceMeta.created_on:type_name -> google.protobuf.Timestamp + 91, // 21: rill.runtime.v1.ResourceMeta.spec_updated_on:type_name -> google.protobuf.Timestamp + 91, // 22: rill.runtime.v1.ResourceMeta.state_updated_on:type_name -> google.protobuf.Timestamp + 91, // 23: rill.runtime.v1.ResourceMeta.deleted_on:type_name -> google.protobuf.Timestamp 0, // 24: rill.runtime.v1.ResourceMeta.reconcile_status:type_name -> rill.runtime.v1.ReconcileStatus - 90, // 25: rill.runtime.v1.ResourceMeta.reconcile_on:type_name -> google.protobuf.Timestamp - 8, // 26: rill.runtime.v1.ResourceMeta.renamed_from:type_name -> rill.runtime.v1.ResourceName - 10, // 27: rill.runtime.v1.ProjectParser.spec:type_name -> rill.runtime.v1.ProjectParserSpec - 11, // 28: rill.runtime.v1.ProjectParser.state:type_name -> rill.runtime.v1.ProjectParserState - 72, // 29: rill.runtime.v1.ProjectParserState.parse_errors:type_name -> rill.runtime.v1.ParseError - 13, // 30: rill.runtime.v1.SourceV2.spec:type_name -> rill.runtime.v1.SourceSpec - 14, // 31: rill.runtime.v1.SourceV2.state:type_name -> rill.runtime.v1.SourceState - 91, // 32: rill.runtime.v1.SourceSpec.properties:type_name -> google.protobuf.Struct - 71, // 33: rill.runtime.v1.SourceSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule - 90, // 34: rill.runtime.v1.SourceState.refreshed_on:type_name -> google.protobuf.Timestamp - 16, // 35: rill.runtime.v1.ModelV2.spec:type_name -> rill.runtime.v1.ModelSpec - 17, // 36: rill.runtime.v1.ModelV2.state:type_name -> rill.runtime.v1.ModelState - 71, // 37: rill.runtime.v1.ModelSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule - 91, // 38: rill.runtime.v1.ModelSpec.incremental_state_resolver_properties:type_name -> google.protobuf.Struct - 91, // 39: rill.runtime.v1.ModelSpec.splits_resolver_properties:type_name -> google.protobuf.Struct - 91, // 40: rill.runtime.v1.ModelSpec.input_properties:type_name -> google.protobuf.Struct - 91, // 41: rill.runtime.v1.ModelSpec.stage_properties:type_name -> google.protobuf.Struct - 91, // 42: rill.runtime.v1.ModelSpec.output_properties:type_name -> google.protobuf.Struct - 91, // 43: rill.runtime.v1.ModelState.result_properties:type_name -> google.protobuf.Struct - 90, // 44: rill.runtime.v1.ModelState.refreshed_on:type_name -> google.protobuf.Timestamp - 91, // 45: rill.runtime.v1.ModelState.incremental_state:type_name -> google.protobuf.Struct - 92, // 46: rill.runtime.v1.ModelState.incremental_state_schema:type_name -> rill.runtime.v1.StructType - 19, // 47: rill.runtime.v1.MetricsViewV2.spec:type_name -> rill.runtime.v1.MetricsViewSpec - 24, // 48: rill.runtime.v1.MetricsViewV2.state:type_name -> rill.runtime.v1.MetricsViewState - 93, // 49: rill.runtime.v1.MetricsViewSpec.smallest_time_grain:type_name -> rill.runtime.v1.TimeGrain - 80, // 50: rill.runtime.v1.MetricsViewSpec.dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionV2 - 83, // 51: rill.runtime.v1.MetricsViewSpec.measures:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureV2 - 20, // 52: rill.runtime.v1.MetricsViewSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule - 4, // 53: rill.runtime.v1.MetricsViewSpec.default_comparison_mode:type_name -> rill.runtime.v1.MetricsViewSpec.ComparisonMode - 85, // 54: rill.runtime.v1.MetricsViewSpec.available_time_ranges:type_name -> rill.runtime.v1.MetricsViewSpec.AvailableTimeRange - 21, // 55: rill.runtime.v1.SecurityRule.access:type_name -> rill.runtime.v1.SecurityRuleAccess - 22, // 56: rill.runtime.v1.SecurityRule.field_access:type_name -> rill.runtime.v1.SecurityRuleFieldAccess - 23, // 57: rill.runtime.v1.SecurityRule.row_filter:type_name -> rill.runtime.v1.SecurityRuleRowFilter - 94, // 58: rill.runtime.v1.SecurityRuleRowFilter.expression:type_name -> rill.runtime.v1.Expression - 19, // 59: rill.runtime.v1.MetricsViewState.valid_spec:type_name -> rill.runtime.v1.MetricsViewSpec - 26, // 60: rill.runtime.v1.Explore.spec:type_name -> rill.runtime.v1.ExploreSpec - 27, // 61: rill.runtime.v1.Explore.state:type_name -> rill.runtime.v1.ExploreState - 31, // 62: rill.runtime.v1.ExploreSpec.dimensions_selector:type_name -> rill.runtime.v1.FieldSelector - 31, // 63: rill.runtime.v1.ExploreSpec.measures_selector:type_name -> rill.runtime.v1.FieldSelector - 28, // 64: rill.runtime.v1.ExploreSpec.time_ranges:type_name -> rill.runtime.v1.ExploreTimeRange - 30, // 65: rill.runtime.v1.ExploreSpec.presets:type_name -> rill.runtime.v1.ExplorePreset - 20, // 66: rill.runtime.v1.ExploreSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule - 26, // 67: rill.runtime.v1.ExploreState.valid_spec:type_name -> rill.runtime.v1.ExploreSpec - 29, // 68: rill.runtime.v1.ExploreTimeRange.comparison_time_ranges:type_name -> rill.runtime.v1.ExploreComparisonTimeRange - 31, // 69: rill.runtime.v1.ExplorePreset.dimensions_selector:type_name -> rill.runtime.v1.FieldSelector - 31, // 70: rill.runtime.v1.ExplorePreset.measures_selector:type_name -> rill.runtime.v1.FieldSelector + 91, // 25: rill.runtime.v1.ResourceMeta.reconcile_on:type_name -> google.protobuf.Timestamp + 9, // 26: rill.runtime.v1.ResourceMeta.renamed_from:type_name -> rill.runtime.v1.ResourceName + 11, // 27: rill.runtime.v1.ProjectParser.spec:type_name -> rill.runtime.v1.ProjectParserSpec + 12, // 28: rill.runtime.v1.ProjectParser.state:type_name -> rill.runtime.v1.ProjectParserState + 73, // 29: rill.runtime.v1.ProjectParserState.parse_errors:type_name -> rill.runtime.v1.ParseError + 14, // 30: rill.runtime.v1.SourceV2.spec:type_name -> rill.runtime.v1.SourceSpec + 15, // 31: rill.runtime.v1.SourceV2.state:type_name -> rill.runtime.v1.SourceState + 92, // 32: rill.runtime.v1.SourceSpec.properties:type_name -> google.protobuf.Struct + 72, // 33: rill.runtime.v1.SourceSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule + 91, // 34: rill.runtime.v1.SourceState.refreshed_on:type_name -> google.protobuf.Timestamp + 17, // 35: rill.runtime.v1.ModelV2.spec:type_name -> rill.runtime.v1.ModelSpec + 18, // 36: rill.runtime.v1.ModelV2.state:type_name -> rill.runtime.v1.ModelState + 72, // 37: rill.runtime.v1.ModelSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule + 92, // 38: rill.runtime.v1.ModelSpec.incremental_state_resolver_properties:type_name -> google.protobuf.Struct + 92, // 39: rill.runtime.v1.ModelSpec.splits_resolver_properties:type_name -> google.protobuf.Struct + 92, // 40: rill.runtime.v1.ModelSpec.input_properties:type_name -> google.protobuf.Struct + 92, // 41: rill.runtime.v1.ModelSpec.stage_properties:type_name -> google.protobuf.Struct + 92, // 42: rill.runtime.v1.ModelSpec.output_properties:type_name -> google.protobuf.Struct + 92, // 43: rill.runtime.v1.ModelState.result_properties:type_name -> google.protobuf.Struct + 91, // 44: rill.runtime.v1.ModelState.refreshed_on:type_name -> google.protobuf.Timestamp + 92, // 45: rill.runtime.v1.ModelState.incremental_state:type_name -> google.protobuf.Struct + 93, // 46: rill.runtime.v1.ModelState.incremental_state_schema:type_name -> rill.runtime.v1.StructType + 20, // 47: rill.runtime.v1.MetricsViewV2.spec:type_name -> rill.runtime.v1.MetricsViewSpec + 25, // 48: rill.runtime.v1.MetricsViewV2.state:type_name -> rill.runtime.v1.MetricsViewState + 94, // 49: rill.runtime.v1.MetricsViewSpec.smallest_time_grain:type_name -> rill.runtime.v1.TimeGrain + 81, // 50: rill.runtime.v1.MetricsViewSpec.dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionV2 + 84, // 51: rill.runtime.v1.MetricsViewSpec.measures:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureV2 + 21, // 52: rill.runtime.v1.MetricsViewSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule + 5, // 53: rill.runtime.v1.MetricsViewSpec.default_comparison_mode:type_name -> rill.runtime.v1.MetricsViewSpec.ComparisonMode + 86, // 54: rill.runtime.v1.MetricsViewSpec.available_time_ranges:type_name -> rill.runtime.v1.MetricsViewSpec.AvailableTimeRange + 22, // 55: rill.runtime.v1.SecurityRule.access:type_name -> rill.runtime.v1.SecurityRuleAccess + 23, // 56: rill.runtime.v1.SecurityRule.field_access:type_name -> rill.runtime.v1.SecurityRuleFieldAccess + 24, // 57: rill.runtime.v1.SecurityRule.row_filter:type_name -> rill.runtime.v1.SecurityRuleRowFilter + 95, // 58: rill.runtime.v1.SecurityRuleRowFilter.expression:type_name -> rill.runtime.v1.Expression + 20, // 59: rill.runtime.v1.MetricsViewState.valid_spec:type_name -> rill.runtime.v1.MetricsViewSpec + 27, // 60: rill.runtime.v1.Explore.spec:type_name -> rill.runtime.v1.ExploreSpec + 28, // 61: rill.runtime.v1.Explore.state:type_name -> rill.runtime.v1.ExploreState + 32, // 62: rill.runtime.v1.ExploreSpec.dimensions_selector:type_name -> rill.runtime.v1.FieldSelector + 32, // 63: rill.runtime.v1.ExploreSpec.measures_selector:type_name -> rill.runtime.v1.FieldSelector + 29, // 64: rill.runtime.v1.ExploreSpec.time_ranges:type_name -> rill.runtime.v1.ExploreTimeRange + 31, // 65: rill.runtime.v1.ExploreSpec.presets:type_name -> rill.runtime.v1.ExplorePreset + 21, // 66: rill.runtime.v1.ExploreSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule + 27, // 67: rill.runtime.v1.ExploreState.valid_spec:type_name -> rill.runtime.v1.ExploreSpec + 30, // 68: rill.runtime.v1.ExploreTimeRange.comparison_time_ranges:type_name -> rill.runtime.v1.ExploreComparisonTimeRange + 32, // 69: rill.runtime.v1.ExplorePreset.dimensions_selector:type_name -> rill.runtime.v1.FieldSelector + 32, // 70: rill.runtime.v1.ExplorePreset.measures_selector:type_name -> rill.runtime.v1.FieldSelector 1, // 71: rill.runtime.v1.ExplorePreset.comparison_mode:type_name -> rill.runtime.v1.ExploreComparisonMode - 32, // 72: rill.runtime.v1.FieldSelector.fields:type_name -> rill.runtime.v1.StringListValue - 34, // 73: rill.runtime.v1.Migration.spec:type_name -> rill.runtime.v1.MigrationSpec - 35, // 74: rill.runtime.v1.Migration.state:type_name -> rill.runtime.v1.MigrationState - 37, // 75: rill.runtime.v1.Report.spec:type_name -> rill.runtime.v1.ReportSpec - 38, // 76: rill.runtime.v1.Report.state:type_name -> rill.runtime.v1.ReportState - 71, // 77: rill.runtime.v1.ReportSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule - 95, // 78: rill.runtime.v1.ReportSpec.export_format:type_name -> rill.runtime.v1.ExportFormat - 42, // 79: rill.runtime.v1.ReportSpec.notifiers:type_name -> rill.runtime.v1.Notifier - 86, // 80: rill.runtime.v1.ReportSpec.annotations:type_name -> rill.runtime.v1.ReportSpec.AnnotationsEntry - 90, // 81: rill.runtime.v1.ReportState.next_run_on:type_name -> google.protobuf.Timestamp - 39, // 82: rill.runtime.v1.ReportState.current_execution:type_name -> rill.runtime.v1.ReportExecution - 39, // 83: rill.runtime.v1.ReportState.execution_history:type_name -> rill.runtime.v1.ReportExecution - 90, // 84: rill.runtime.v1.ReportExecution.report_time:type_name -> google.protobuf.Timestamp - 90, // 85: rill.runtime.v1.ReportExecution.started_on:type_name -> google.protobuf.Timestamp - 90, // 86: rill.runtime.v1.ReportExecution.finished_on:type_name -> google.protobuf.Timestamp - 41, // 87: rill.runtime.v1.Alert.spec:type_name -> rill.runtime.v1.AlertSpec - 43, // 88: rill.runtime.v1.Alert.state:type_name -> rill.runtime.v1.AlertState - 71, // 89: rill.runtime.v1.AlertSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule - 91, // 90: rill.runtime.v1.AlertSpec.resolver_properties:type_name -> google.protobuf.Struct - 91, // 91: rill.runtime.v1.AlertSpec.query_for_attributes:type_name -> google.protobuf.Struct - 42, // 92: rill.runtime.v1.AlertSpec.notifiers:type_name -> rill.runtime.v1.Notifier - 87, // 93: rill.runtime.v1.AlertSpec.annotations:type_name -> rill.runtime.v1.AlertSpec.AnnotationsEntry - 91, // 94: rill.runtime.v1.Notifier.properties:type_name -> google.protobuf.Struct - 90, // 95: rill.runtime.v1.AlertState.next_run_on:type_name -> google.protobuf.Timestamp - 44, // 96: rill.runtime.v1.AlertState.current_execution:type_name -> rill.runtime.v1.AlertExecution - 44, // 97: rill.runtime.v1.AlertState.execution_history:type_name -> rill.runtime.v1.AlertExecution - 45, // 98: rill.runtime.v1.AlertExecution.result:type_name -> rill.runtime.v1.AssertionResult - 90, // 99: rill.runtime.v1.AlertExecution.execution_time:type_name -> google.protobuf.Timestamp - 90, // 100: rill.runtime.v1.AlertExecution.started_on:type_name -> google.protobuf.Timestamp - 90, // 101: rill.runtime.v1.AlertExecution.finished_on:type_name -> google.protobuf.Timestamp - 90, // 102: rill.runtime.v1.AlertExecution.suppressed_since:type_name -> google.protobuf.Timestamp - 2, // 103: rill.runtime.v1.AssertionResult.status:type_name -> rill.runtime.v1.AssertionStatus - 91, // 104: rill.runtime.v1.AssertionResult.fail_row:type_name -> google.protobuf.Struct - 47, // 105: rill.runtime.v1.PullTrigger.spec:type_name -> rill.runtime.v1.PullTriggerSpec - 48, // 106: rill.runtime.v1.PullTrigger.state:type_name -> rill.runtime.v1.PullTriggerState - 50, // 107: rill.runtime.v1.RefreshTrigger.spec:type_name -> rill.runtime.v1.RefreshTriggerSpec - 51, // 108: rill.runtime.v1.RefreshTrigger.state:type_name -> rill.runtime.v1.RefreshTriggerState - 8, // 109: rill.runtime.v1.RefreshTriggerSpec.resources:type_name -> rill.runtime.v1.ResourceName - 52, // 110: rill.runtime.v1.RefreshTriggerSpec.models:type_name -> rill.runtime.v1.RefreshModelTrigger - 54, // 111: rill.runtime.v1.BucketPlanner.spec:type_name -> rill.runtime.v1.BucketPlannerSpec - 55, // 112: rill.runtime.v1.BucketPlanner.state:type_name -> rill.runtime.v1.BucketPlannerState - 56, // 113: rill.runtime.v1.BucketPlannerSpec.extract_policy:type_name -> rill.runtime.v1.BucketExtractPolicy - 5, // 114: rill.runtime.v1.BucketExtractPolicy.rows_strategy:type_name -> rill.runtime.v1.BucketExtractPolicy.Strategy - 5, // 115: rill.runtime.v1.BucketExtractPolicy.files_strategy:type_name -> rill.runtime.v1.BucketExtractPolicy.Strategy - 58, // 116: rill.runtime.v1.Theme.spec:type_name -> rill.runtime.v1.ThemeSpec - 59, // 117: rill.runtime.v1.Theme.state:type_name -> rill.runtime.v1.ThemeState - 96, // 118: rill.runtime.v1.ThemeSpec.primary_color:type_name -> rill.runtime.v1.Color - 96, // 119: rill.runtime.v1.ThemeSpec.secondary_color:type_name -> rill.runtime.v1.Color - 61, // 120: rill.runtime.v1.Component.spec:type_name -> rill.runtime.v1.ComponentSpec - 62, // 121: rill.runtime.v1.Component.state:type_name -> rill.runtime.v1.ComponentState - 91, // 122: rill.runtime.v1.ComponentSpec.resolver_properties:type_name -> google.protobuf.Struct - 91, // 123: rill.runtime.v1.ComponentSpec.renderer_properties:type_name -> google.protobuf.Struct - 63, // 124: rill.runtime.v1.ComponentSpec.input:type_name -> rill.runtime.v1.ComponentVariable - 63, // 125: rill.runtime.v1.ComponentSpec.output:type_name -> rill.runtime.v1.ComponentVariable - 61, // 126: rill.runtime.v1.ComponentState.valid_spec:type_name -> rill.runtime.v1.ComponentSpec - 97, // 127: rill.runtime.v1.ComponentVariable.default_value:type_name -> google.protobuf.Value - 65, // 128: rill.runtime.v1.Canvas.spec:type_name -> rill.runtime.v1.CanvasSpec - 66, // 129: rill.runtime.v1.Canvas.state:type_name -> rill.runtime.v1.CanvasState - 63, // 130: rill.runtime.v1.CanvasSpec.variables:type_name -> rill.runtime.v1.ComponentVariable - 67, // 131: rill.runtime.v1.CanvasSpec.items:type_name -> rill.runtime.v1.CanvasItem - 20, // 132: rill.runtime.v1.CanvasSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule - 65, // 133: rill.runtime.v1.CanvasState.valid_spec:type_name -> rill.runtime.v1.CanvasSpec - 69, // 134: rill.runtime.v1.API.spec:type_name -> rill.runtime.v1.APISpec - 70, // 135: rill.runtime.v1.API.state:type_name -> rill.runtime.v1.APIState - 91, // 136: rill.runtime.v1.APISpec.resolver_properties:type_name -> google.protobuf.Struct - 91, // 137: rill.runtime.v1.APISpec.openapi_parameters:type_name -> google.protobuf.Struct - 91, // 138: rill.runtime.v1.APISpec.openapi_response_schema:type_name -> google.protobuf.Struct - 76, // 139: rill.runtime.v1.ParseError.start_location:type_name -> rill.runtime.v1.CharLocation - 88, // 140: rill.runtime.v1.ConnectorSpec.properties:type_name -> rill.runtime.v1.ConnectorSpec.PropertiesEntry - 89, // 141: rill.runtime.v1.ConnectorSpec.properties_from_variables:type_name -> rill.runtime.v1.ConnectorSpec.PropertiesFromVariablesEntry - 77, // 142: rill.runtime.v1.ConnectorV2.spec:type_name -> rill.runtime.v1.ConnectorSpec - 78, // 143: rill.runtime.v1.ConnectorV2.state:type_name -> rill.runtime.v1.ConnectorState - 93, // 144: rill.runtime.v1.MetricsViewSpec.DimensionSelector.time_grain:type_name -> rill.runtime.v1.TimeGrain - 81, // 145: rill.runtime.v1.MetricsViewSpec.MeasureWindow.order_by:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector - 3, // 146: rill.runtime.v1.MetricsViewSpec.MeasureV2.type:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureType - 82, // 147: rill.runtime.v1.MetricsViewSpec.MeasureV2.window:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureWindow - 81, // 148: rill.runtime.v1.MetricsViewSpec.MeasureV2.per_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector - 81, // 149: rill.runtime.v1.MetricsViewSpec.MeasureV2.required_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector - 84, // 150: rill.runtime.v1.MetricsViewSpec.AvailableTimeRange.comparison_offsets:type_name -> rill.runtime.v1.MetricsViewSpec.AvailableComparisonOffset - 151, // [151:151] is the sub-list for method output_type - 151, // [151:151] is the sub-list for method input_type - 151, // [151:151] is the sub-list for extension type_name - 151, // [151:151] is the sub-list for extension extendee - 0, // [0:151] is the sub-list for field type_name + 2, // 72: rill.runtime.v1.ExplorePreset.view:type_name -> rill.runtime.v1.ExploreWebView + 33, // 73: rill.runtime.v1.FieldSelector.fields:type_name -> rill.runtime.v1.StringListValue + 35, // 74: rill.runtime.v1.Migration.spec:type_name -> rill.runtime.v1.MigrationSpec + 36, // 75: rill.runtime.v1.Migration.state:type_name -> rill.runtime.v1.MigrationState + 38, // 76: rill.runtime.v1.Report.spec:type_name -> rill.runtime.v1.ReportSpec + 39, // 77: rill.runtime.v1.Report.state:type_name -> rill.runtime.v1.ReportState + 72, // 78: rill.runtime.v1.ReportSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule + 96, // 79: rill.runtime.v1.ReportSpec.export_format:type_name -> rill.runtime.v1.ExportFormat + 43, // 80: rill.runtime.v1.ReportSpec.notifiers:type_name -> rill.runtime.v1.Notifier + 87, // 81: rill.runtime.v1.ReportSpec.annotations:type_name -> rill.runtime.v1.ReportSpec.AnnotationsEntry + 91, // 82: rill.runtime.v1.ReportState.next_run_on:type_name -> google.protobuf.Timestamp + 40, // 83: rill.runtime.v1.ReportState.current_execution:type_name -> rill.runtime.v1.ReportExecution + 40, // 84: rill.runtime.v1.ReportState.execution_history:type_name -> rill.runtime.v1.ReportExecution + 91, // 85: rill.runtime.v1.ReportExecution.report_time:type_name -> google.protobuf.Timestamp + 91, // 86: rill.runtime.v1.ReportExecution.started_on:type_name -> google.protobuf.Timestamp + 91, // 87: rill.runtime.v1.ReportExecution.finished_on:type_name -> google.protobuf.Timestamp + 42, // 88: rill.runtime.v1.Alert.spec:type_name -> rill.runtime.v1.AlertSpec + 44, // 89: rill.runtime.v1.Alert.state:type_name -> rill.runtime.v1.AlertState + 72, // 90: rill.runtime.v1.AlertSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule + 92, // 91: rill.runtime.v1.AlertSpec.resolver_properties:type_name -> google.protobuf.Struct + 92, // 92: rill.runtime.v1.AlertSpec.query_for_attributes:type_name -> google.protobuf.Struct + 43, // 93: rill.runtime.v1.AlertSpec.notifiers:type_name -> rill.runtime.v1.Notifier + 88, // 94: rill.runtime.v1.AlertSpec.annotations:type_name -> rill.runtime.v1.AlertSpec.AnnotationsEntry + 92, // 95: rill.runtime.v1.Notifier.properties:type_name -> google.protobuf.Struct + 91, // 96: rill.runtime.v1.AlertState.next_run_on:type_name -> google.protobuf.Timestamp + 45, // 97: rill.runtime.v1.AlertState.current_execution:type_name -> rill.runtime.v1.AlertExecution + 45, // 98: rill.runtime.v1.AlertState.execution_history:type_name -> rill.runtime.v1.AlertExecution + 46, // 99: rill.runtime.v1.AlertExecution.result:type_name -> rill.runtime.v1.AssertionResult + 91, // 100: rill.runtime.v1.AlertExecution.execution_time:type_name -> google.protobuf.Timestamp + 91, // 101: rill.runtime.v1.AlertExecution.started_on:type_name -> google.protobuf.Timestamp + 91, // 102: rill.runtime.v1.AlertExecution.finished_on:type_name -> google.protobuf.Timestamp + 91, // 103: rill.runtime.v1.AlertExecution.suppressed_since:type_name -> google.protobuf.Timestamp + 3, // 104: rill.runtime.v1.AssertionResult.status:type_name -> rill.runtime.v1.AssertionStatus + 92, // 105: rill.runtime.v1.AssertionResult.fail_row:type_name -> google.protobuf.Struct + 48, // 106: rill.runtime.v1.PullTrigger.spec:type_name -> rill.runtime.v1.PullTriggerSpec + 49, // 107: rill.runtime.v1.PullTrigger.state:type_name -> rill.runtime.v1.PullTriggerState + 51, // 108: rill.runtime.v1.RefreshTrigger.spec:type_name -> rill.runtime.v1.RefreshTriggerSpec + 52, // 109: rill.runtime.v1.RefreshTrigger.state:type_name -> rill.runtime.v1.RefreshTriggerState + 9, // 110: rill.runtime.v1.RefreshTriggerSpec.resources:type_name -> rill.runtime.v1.ResourceName + 53, // 111: rill.runtime.v1.RefreshTriggerSpec.models:type_name -> rill.runtime.v1.RefreshModelTrigger + 55, // 112: rill.runtime.v1.BucketPlanner.spec:type_name -> rill.runtime.v1.BucketPlannerSpec + 56, // 113: rill.runtime.v1.BucketPlanner.state:type_name -> rill.runtime.v1.BucketPlannerState + 57, // 114: rill.runtime.v1.BucketPlannerSpec.extract_policy:type_name -> rill.runtime.v1.BucketExtractPolicy + 6, // 115: rill.runtime.v1.BucketExtractPolicy.rows_strategy:type_name -> rill.runtime.v1.BucketExtractPolicy.Strategy + 6, // 116: rill.runtime.v1.BucketExtractPolicy.files_strategy:type_name -> rill.runtime.v1.BucketExtractPolicy.Strategy + 59, // 117: rill.runtime.v1.Theme.spec:type_name -> rill.runtime.v1.ThemeSpec + 60, // 118: rill.runtime.v1.Theme.state:type_name -> rill.runtime.v1.ThemeState + 97, // 119: rill.runtime.v1.ThemeSpec.primary_color:type_name -> rill.runtime.v1.Color + 97, // 120: rill.runtime.v1.ThemeSpec.secondary_color:type_name -> rill.runtime.v1.Color + 62, // 121: rill.runtime.v1.Component.spec:type_name -> rill.runtime.v1.ComponentSpec + 63, // 122: rill.runtime.v1.Component.state:type_name -> rill.runtime.v1.ComponentState + 92, // 123: rill.runtime.v1.ComponentSpec.resolver_properties:type_name -> google.protobuf.Struct + 92, // 124: rill.runtime.v1.ComponentSpec.renderer_properties:type_name -> google.protobuf.Struct + 64, // 125: rill.runtime.v1.ComponentSpec.input:type_name -> rill.runtime.v1.ComponentVariable + 64, // 126: rill.runtime.v1.ComponentSpec.output:type_name -> rill.runtime.v1.ComponentVariable + 62, // 127: rill.runtime.v1.ComponentState.valid_spec:type_name -> rill.runtime.v1.ComponentSpec + 98, // 128: rill.runtime.v1.ComponentVariable.default_value:type_name -> google.protobuf.Value + 66, // 129: rill.runtime.v1.Canvas.spec:type_name -> rill.runtime.v1.CanvasSpec + 67, // 130: rill.runtime.v1.Canvas.state:type_name -> rill.runtime.v1.CanvasState + 64, // 131: rill.runtime.v1.CanvasSpec.variables:type_name -> rill.runtime.v1.ComponentVariable + 68, // 132: rill.runtime.v1.CanvasSpec.items:type_name -> rill.runtime.v1.CanvasItem + 21, // 133: rill.runtime.v1.CanvasSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule + 66, // 134: rill.runtime.v1.CanvasState.valid_spec:type_name -> rill.runtime.v1.CanvasSpec + 70, // 135: rill.runtime.v1.API.spec:type_name -> rill.runtime.v1.APISpec + 71, // 136: rill.runtime.v1.API.state:type_name -> rill.runtime.v1.APIState + 92, // 137: rill.runtime.v1.APISpec.resolver_properties:type_name -> google.protobuf.Struct + 92, // 138: rill.runtime.v1.APISpec.openapi_parameters:type_name -> google.protobuf.Struct + 92, // 139: rill.runtime.v1.APISpec.openapi_response_schema:type_name -> google.protobuf.Struct + 77, // 140: rill.runtime.v1.ParseError.start_location:type_name -> rill.runtime.v1.CharLocation + 89, // 141: rill.runtime.v1.ConnectorSpec.properties:type_name -> rill.runtime.v1.ConnectorSpec.PropertiesEntry + 90, // 142: rill.runtime.v1.ConnectorSpec.properties_from_variables:type_name -> rill.runtime.v1.ConnectorSpec.PropertiesFromVariablesEntry + 78, // 143: rill.runtime.v1.ConnectorV2.spec:type_name -> rill.runtime.v1.ConnectorSpec + 79, // 144: rill.runtime.v1.ConnectorV2.state:type_name -> rill.runtime.v1.ConnectorState + 94, // 145: rill.runtime.v1.MetricsViewSpec.DimensionSelector.time_grain:type_name -> rill.runtime.v1.TimeGrain + 82, // 146: rill.runtime.v1.MetricsViewSpec.MeasureWindow.order_by:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector + 4, // 147: rill.runtime.v1.MetricsViewSpec.MeasureV2.type:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureType + 83, // 148: rill.runtime.v1.MetricsViewSpec.MeasureV2.window:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureWindow + 82, // 149: rill.runtime.v1.MetricsViewSpec.MeasureV2.per_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector + 82, // 150: rill.runtime.v1.MetricsViewSpec.MeasureV2.required_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector + 85, // 151: rill.runtime.v1.MetricsViewSpec.AvailableTimeRange.comparison_offsets:type_name -> rill.runtime.v1.MetricsViewSpec.AvailableComparisonOffset + 152, // [152:152] is the sub-list for method output_type + 152, // [152:152] is the sub-list for method input_type + 152, // [152:152] is the sub-list for extension type_name + 152, // [152:152] is the sub-list for extension extendee + 0, // [0:152] is the sub-list for field type_name } func init() { file_rill_runtime_v1_resources_proto_init() } @@ -8989,7 +9202,7 @@ func file_rill_runtime_v1_resources_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_rill_runtime_v1_resources_proto_rawDesc, - NumEnums: 6, + NumEnums: 7, NumMessages: 84, NumExtensions: 0, NumServices: 0, diff --git a/proto/gen/rill/runtime/v1/resources.pb.validate.go b/proto/gen/rill/runtime/v1/resources.pb.validate.go index afe0edce5e9..d42883cb058 100644 --- a/proto/gen/rill/runtime/v1/resources.pb.validate.go +++ b/proto/gen/rill/runtime/v1/resources.pb.validate.go @@ -4866,10 +4866,34 @@ func (m *ExplorePreset) validate(all bool) error { // no validation rules for TimeRange + // no validation rules for Timezone + + // no validation rules for TimeGrain + // no validation rules for ComparisonMode + // no validation rules for CompareTimeRange + // no validation rules for ComparisonDimension + // no validation rules for View + + // no validation rules for OverviewSortBy + + // no validation rules for OverviewSortAsc + + // no validation rules for OverviewExpandedDimension + + // no validation rules for TimeDimensionMeasure + + // no validation rules for TimeDimensionChartType + + // no validation rules for TimeDimensionPin + + // no validation rules for PivotSortBy + + // no validation rules for PivotSortAsc + if len(errors) > 0 { return ExplorePresetMultiError(errors) } diff --git a/proto/gen/rill/runtime/v1/runtime.swagger.yaml b/proto/gen/rill/runtime/v1/runtime.swagger.yaml index e98a8105f4a..c2a957bfbbb 100644 --- a/proto/gen/rill/runtime/v1/runtime.swagger.yaml +++ b/proto/gen/rill/runtime/v1/runtime.swagger.yaml @@ -4008,12 +4008,44 @@ definitions: Time range for the explore. It corresponds to the `range` property of the explore's `time_ranges`. If not found in `time_ranges`, it should be added to the list. + timezone: + type: string + timeGrain: + type: string comparisonMode: $ref: '#/definitions/v1ExploreComparisonMode' description: Comparison mode. + compareTimeRange: + type: string comparisonDimension: type: string description: If comparison_mode is EXPLORE_COMPARISON_MODE_DIMENSION, this indicates the dimension to use. + view: + $ref: '#/definitions/v1ExploreWebView' + overviewSortBy: + type: string + overviewSortAsc: + type: boolean + overviewExpandedDimension: + type: string + timeDimensionMeasure: + type: string + timeDimensionChartType: + type: string + timeDimensionPin: + type: boolean + pivotRows: + type: array + items: + type: string + pivotCols: + type: array + items: + type: string + pivotSortBy: + type: string + pivotSortAsc: + type: boolean v1ExploreSpec: type: object properties: @@ -4094,6 +4126,14 @@ definitions: type: object $ref: '#/definitions/v1ExploreComparisonTimeRange' title: Comparison time ranges available for this time range + v1ExploreWebView: + type: string + enum: + - EXPLORE_ACTIVE_PAGE_OVERVIEW + - EXPLORE_ACTIVE_PAGE_TIME_DIMENSION + - EXPLORE_ACTIVE_PAGE_PIVOT + - EXPLORE_ACTIVE_PAGE_CANVAS + default: EXPLORE_ACTIVE_PAGE_OVERVIEW v1ExportFormat: type: string enum: diff --git a/proto/rill/runtime/v1/resources.proto b/proto/rill/runtime/v1/resources.proto index f07fbdf7ded..caf093897d1 100644 --- a/proto/rill/runtime/v1/resources.proto +++ b/proto/rill/runtime/v1/resources.proto @@ -384,14 +384,34 @@ message ExplorePreset { repeated string measures = 4; // Dynamic selector for `measures`. Will be processed during validation, so it will always be empty in `state.valid_spec`. FieldSelector measures_selector = 10; + // Time range for the explore. // It corresponds to the `range` property of the explore's `time_ranges`. // If not found in `time_ranges`, it should be added to the list. string time_range = 6; + string timezone = 11; + string time_grain = 12; + // Comparison mode. ExploreComparisonMode comparison_mode = 7; + optional string compare_time_range = 13; // If comparison_mode is EXPLORE_COMPARISON_MODE_DIMENSION, this indicates the dimension to use. string comparison_dimension = 8; + + ExploreWebView view = 14; + + string overview_sort_by = 15; + bool overview_sort_asc = 16; + string overview_expanded_dimension = 17; + + string time_dimension_measure = 18; + string time_dimension_chart_type = 19; + bool time_dimension_pin = 20; + + repeated string pivot_rows = 21; + repeated string pivot_cols = 22; + string pivot_sort_by = 23; + bool pivot_sort_asc = 24; } enum ExploreComparisonMode { @@ -401,6 +421,13 @@ enum ExploreComparisonMode { EXPLORE_COMPARISON_MODE_DIMENSION = 3; } +enum ExploreWebView { + EXPLORE_ACTIVE_PAGE_OVERVIEW = 0; + EXPLORE_ACTIVE_PAGE_TIME_DIMENSION = 1; + EXPLORE_ACTIVE_PAGE_PIVOT = 2; + EXPLORE_ACTIVE_PAGE_CANVAS = 3; +} + // FieldSelector describes logic for selecting a list of fields. // It is useful for dynamically evaluating fields when the list of potential fields is not known at parse time. message FieldSelector { diff --git a/web-common/src/features/dashboards/url-state/fromUrl.ts b/web-common/src/features/dashboards/url-state/fromUrl.ts index c090ffaf5a9..d24b89a6fa9 100644 --- a/web-common/src/features/dashboards/url-state/fromUrl.ts +++ b/web-common/src/features/dashboards/url-state/fromUrl.ts @@ -25,6 +25,9 @@ import { import { MetricsViewSpecDimensionV2, MetricsViewSpecMeasureV2, + type V1ExplorePreset, + V1ExploreSpec, + V1ExploreWebView, V1Expression, V1MetricsViewSpec, V1Operation, @@ -33,14 +36,23 @@ import { export function getMetricsExplorerFromUrl( searchParams: URLSearchParams, metricsView: V1MetricsViewSpec, + explore: V1ExploreSpec, ): { entity: Partial; errors: Error[] } { // TODO: replace this with V1ExplorePreset once it is available on main const entity: Partial = {}; const errors: Error[] = []; - const measures = getMapFromArray(metricsView.measures ?? [], (m) => m.name!); + const preset = explore.presets?.[0] ?? {}; + + const measures = getMapFromArray( + metricsView.measures?.filter((m) => explore.measures?.includes(m.name!)) ?? + [], + (m) => m.name!, + ); const dimensions = getMapFromArray( - metricsView.dimensions ?? [], + metricsView.dimensions?.filter((d) => + explore.dimensions?.includes(d.name!), + ) ?? [], (d) => d.name!, ); @@ -56,39 +68,59 @@ export function getMetricsExplorerFromUrl( entity.dimensionThresholdFilters = dimensionThresholdFilters; } - if (searchParams.has("tr")) { - const { timeRange, error } = fromTimeRangeUrlParam( - searchParams.get("tr") as string, - ); + const { entity: trEntity, errors: trErrors } = fromTimeRangesParams( + searchParams, + dimensions, + preset, + ); + Object.assign(entity, trEntity); + errors.push(...trErrors); + + Object.assign( + entity, + fromOverviewUrlParams(searchParams, measures, dimensions, explore, preset), + ); + + entity.tdd = fromTimeDimensionUrlParams(searchParams, measures, preset); + + entity.pivot = fromPivotUrlParams(searchParams, measures, dimensions, preset); + + return { entity, errors }; +} + +function fromTimeRangesParams( + searchParams: URLSearchParams, + dimensions: Map, + preset: V1ExplorePreset, +) { + const entity: Partial = {}; + const errors: Error[] = []; + + const timeRange = preset.timeRange || searchParams.get("tr"); + if (timeRange) { + const { timeRange: selectedTimeRange, error } = + fromTimeRangeUrlParam(timeRange); if (error) errors.push(error); - entity.selectedTimeRange = timeRange; + entity.selectedTimeRange = selectedTimeRange; } - if (searchParams.has("tz")) { - entity.selectedTimezone = searchParams.get("tz") as string; + const timeZone = preset.timezone || searchParams.get("tz"); + if (timeZone) { + entity.selectedTimezone = timeZone; } - if (searchParams.has("ctr")) { - const { timeRange, error } = fromTimeRangeUrlParam( - searchParams.get("ctr") as string, - ); + + const comparisonTimeRange = + preset.compareTimeRange || searchParams.get("ctr"); + if (comparisonTimeRange) { + const { timeRange, error } = fromTimeRangeUrlParam(comparisonTimeRange); if (error) errors.push(error); entity.selectedComparisonTimeRange = timeRange; } - if (searchParams.has("cd")) { - const cd = searchParams.get("cd") as string; - if (dimensions.has(cd)) { - entity.selectedComparisonDimension = cd; - } + const comparisonDimension = + preset.comparisonDimension || searchParams.get("cd"); + if (comparisonDimension && dimensions.has(comparisonDimension)) { + entity.selectedComparisonDimension = comparisonDimension; } - Object.assign( - entity, - fromOverviewUrlParams(searchParams, measures, dimensions), - ); - - entity.tdd = fromTimeDimensionUrlParams(searchParams, measures); - - entity.pivot = fromPivotUrlParams(searchParams, measures, dimensions); - return { entity, errors }; } @@ -133,47 +165,56 @@ function fromOverviewUrlParams( searchParams: URLSearchParams, measures: Map, dimensions: Map, + explore: V1ExploreSpec, + preset: V1ExplorePreset, ) { const entity: Partial = {}; + let selectedMeasures = preset.measures ?? explore.measures ?? []; if (searchParams.has("e.m")) { const mes = searchParams.get("e.m") as string; - if (mes === "*") { - entity.allMeasuresVisible = true; - entity.visibleMeasureKeys = new Set(measures.keys()); - } else { - entity.allMeasuresVisible = false; - entity.visibleMeasureKeys = new Set( - mes.split(",").filter((m) => measures.has(m)), - ); + if (mes !== "*") { + selectedMeasures = mes.split(",").filter((m) => measures.has(m)); } } + entity.allMeasuresVisible = + selectedMeasures.length === explore.measures?.length; + entity.visibleMeasureKeys = new Set(selectedMeasures); + let selectedDimensions = preset.dimensions ?? explore.dimensions ?? []; if (searchParams.has("e.d")) { const dims = searchParams.get("e.d") as string; - if (dims === "*") { - entity.allDimensionsVisible = true; - entity.visibleDimensionKeys = new Set(dimensions.keys()); - } else { - entity.allDimensionsVisible = false; - entity.visibleDimensionKeys = new Set( - dims.split(",").filter((d) => dimensions.has(d)), - ); + if (dims !== "*") { + selectedDimensions = dims.split(",").filter((d) => dimensions.has(d)); } } + entity.allDimensionsVisible = + selectedDimensions.length === explore.dimensions?.length; + entity.visibleDimensionKeys = new Set(selectedDimensions); + entity.leaderboardMeasureName = + preset.overviewSortBy ?? explore.measures?.[0]; if (searchParams.has("e.sb")) { const sortBy = searchParams.get("e.sb") as string; if (measures.has(sortBy)) { entity.leaderboardMeasureName = sortBy; } } + + if (preset.overviewSortAsc !== undefined) { + entity.sortDirection = preset.overviewSortAsc + ? SortDirection.ASCENDING + : SortDirection.DESCENDING; + } else { + entity.sortDirection = SortDirection.DESCENDING; + } if (searchParams.has("e.sd")) { const sortDir = searchParams.get("e.sd") as string; entity.sortDirection = sortDir === "ASC" ? SortDirection.ASCENDING : SortDirection.DESCENDING; } + entity.selectedDimensionName = preset.overviewExpandedDimension ?? ""; if (searchParams.has("e.ed")) { const dim = searchParams.get("e.ed") as string; if (dimensions.has(dim)) { @@ -187,10 +228,11 @@ function fromOverviewUrlParams( function fromTimeDimensionUrlParams( searchParams: URLSearchParams, measures: Map, + preset: V1ExplorePreset, ) { - let ttdMeasure: string | undefined; - let ttdChartType: TDDChart | undefined; - let ttdPin: number | undefined; + let ttdMeasure = preset.timeDimensionMeasure; + let ttdChartType = preset.timeDimensionChartType as TDDChart | undefined; + let ttdPin: number | undefined; // TODO if (searchParams.has("tdd.m")) { const mes = searchParams.get("tdd.m") as string; @@ -222,6 +264,7 @@ function fromPivotUrlParams( searchParams: URLSearchParams, measures: Map, dimensions: Map, + preset: V1ExplorePreset, ): PivotState { const mapPivotEntry = (entry: string): PivotChipData | undefined => { if (entry in FromURLParamTimeDimensionMap) { @@ -255,19 +298,26 @@ function fromPivotUrlParams( }; const rowDimensions: PivotChipData[] = []; + let pivotRows = preset.pivotRows; if (searchParams.has("p.r")) { - const pivotRows = searchParams.get("p.r") as string; - pivotRows.split(",").forEach((pivotRow) => { + pivotRows = (searchParams.get("p.r") as string).split(","); + } + if (pivotRows) { + pivotRows.forEach((pivotRow) => { const chip = mapPivotEntry(pivotRow); if (!chip) return; rowDimensions.push(chip); }); } + const colMeasures: PivotChipData[] = []; const colDimensions: PivotChipData[] = []; + let pivotCols = preset.pivotCols; if (searchParams.has("p.c")) { - const pivotCols = searchParams.get("p.c") as string; - pivotCols.split(",").forEach((pivotRow) => { + pivotCols = (searchParams.get("p.c") as string).split(","); + } + if (pivotCols) { + pivotCols.forEach((pivotRow) => { const chip = mapPivotEntry(pivotRow); if (!chip) return; if (chip.type === PivotChipType.Measure) { @@ -279,7 +329,9 @@ function fromPivotUrlParams( } return { - active: searchParams.get("view") === "pivot", + active: + searchParams.get("view") === "pivot" || + preset.view === V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, rows: { dimension: rowDimensions, }, diff --git a/web-common/src/features/dashboards/url-state/toUrl.ts b/web-common/src/features/dashboards/url-state/toUrl.ts index 4cde131ba1c..f82353b71f7 100644 --- a/web-common/src/features/dashboards/url-state/toUrl.ts +++ b/web-common/src/features/dashboards/url-state/toUrl.ts @@ -1,92 +1,167 @@ import { mergeMeasureFilters } from "@rilldata/web-common/features/dashboards/filters/measure-filters/measure-filter-utils"; +import { + PivotChipData, + PivotChipType, +} from "@rilldata/web-common/features/dashboards/pivot/types"; import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; import { convertExpressionToFilterParam } from "@rilldata/web-common/features/dashboards/url-state/filters/converters"; -import type { V1MetricsViewSpec } from "@rilldata/web-common/runtime-client"; +import { ToURLParamTimeDimensionMap } from "@rilldata/web-common/features/dashboards/url-state/mappers"; +import { + arrayOrderedEquals, + arrayUnorderedEquals, +} from "@rilldata/web-common/lib/arrayUtils"; +import type { + V1ExplorePreset, + V1ExploreSpec, +} from "@rilldata/web-common/runtime-client"; export function getUrlFromMetricsExplorer( metrics: MetricsExplorerEntity, searchParams: URLSearchParams, - metricsView: V1MetricsViewSpec, + explore: V1ExploreSpec, + preset: V1ExplorePreset, ) { if (!metrics) return; - // TODO: filter const expr = mergeMeasureFilters(metrics); if (expr && expr?.cond?.exprs?.length) { searchParams.set("f", convertExpressionToFilterParam(expr)); } + toTimeRangesUrl(metrics, searchParams, preset); + + toOverviewUrl(metrics, searchParams, explore, preset); + + toTimeDimensionUrlParams(metrics, searchParams, preset); + + toPivotUrlParams(metrics, searchParams, preset); +} + +function toTimeRangesUrl( + metrics: MetricsExplorerEntity, + searchParams: URLSearchParams, + preset: V1ExplorePreset, +) { if ( metrics.selectedTimeRange?.name && - metrics.selectedTimeRange?.name !== metricsView.defaultTimeRange + metrics.selectedTimeRange?.name !== preset.timeRange ) { - searchParams.set("tr", metrics.selectedTimeRange?.name); + searchParams.set("tr", metrics.selectedTimeRange.name); + } + if (metrics.selectedTimezone !== preset.timezone) { + searchParams.set("tz", metrics.selectedTimezone); } - // TODO: rest of time range - - toOverviewUrl(metrics, searchParams, metricsView); - - toTimeDimensionUrlParams(metrics, searchParams); - toPivotUrlParams(metrics, searchParams); + if (metrics.selectedComparisonTimeRange?.name !== preset.compareTimeRange) { + searchParams.set("ctr", metrics.selectedComparisonTimeRange?.name ?? ""); + } + if (metrics.selectedComparisonDimension !== preset.comparisonDimension) { + searchParams.set("cd", metrics.selectedComparisonDimension ?? ""); + } } function toOverviewUrl( metrics: MetricsExplorerEntity, searchParams: URLSearchParams, - metricsView: V1MetricsViewSpec, + explore: V1ExploreSpec, + preset: V1ExplorePreset, ) { - if (!metrics.allMeasuresVisible) { - searchParams.set("e.m", [...metrics.visibleMeasureKeys].join(",")); + const measures = [...metrics.visibleMeasureKeys]; + const presetMeasures = preset.measures ?? explore.measures ?? []; + if (!arrayUnorderedEquals(measures, presetMeasures)) { + if (metrics.allMeasuresVisible) { + searchParams.set("o.m", "*"); + } else { + searchParams.set("o.m", measures.join(",")); + } } - if (!metrics.allDimensionsVisible) { - searchParams.set("e.m", [...metrics.visibleMeasureKeys].join(",")); + + const dimensions = [...metrics.visibleDimensionKeys]; + const presetDimensions = preset.dimensions ?? explore.dimensions ?? []; + if (!arrayUnorderedEquals(dimensions, presetDimensions)) { + if (metrics.allDimensionsVisible) { + searchParams.set("o.d", "*"); + } else { + searchParams.set("o.d", dimensions.join(",")); + } } - if (metrics.leaderboardMeasureName !== metricsView.measures?.[0]?.name) { - searchParams.set("e.sb", metrics.leaderboardMeasureName); + + if ( + // if sort by is defined then only set param if selected is not the same. + (preset.overviewSortBy && + metrics.leaderboardMeasureName !== preset.overviewSortBy) || + // else the default is the 1st measure in explore, so check that next + metrics.leaderboardMeasureName !== explore.measures?.[0] + ) { + searchParams.set("o.sb", metrics.leaderboardMeasureName); } - if (metrics.sortDirection !== SortDirection.DESCENDING) { - searchParams.set("e.sd", "ASC"); + + const sortAsc = metrics.sortDirection === SortDirection.ASCENDING; + if ( + preset.overviewSortAsc === undefined || + preset.overviewSortAsc !== sortAsc + ) { + searchParams.set("o.sd", sortAsc ? "ASC" : "DESC"); } - if (metrics.selectedDimensionName) { - searchParams.set("e.ed", metrics.selectedDimensionName); + + if ( + metrics.selectedDimensionName && + metrics.selectedDimensionName !== preset.overviewExpandedDimension + ) { + searchParams.set("o.ed", metrics.selectedDimensionName); } } function toTimeDimensionUrlParams( metrics: MetricsExplorerEntity, searchParams: URLSearchParams, + preset: V1ExplorePreset, ) { - if (metrics.tdd.expandedMeasureName) { + if ( + metrics.tdd.expandedMeasureName && + metrics.tdd.expandedMeasureName !== preset.timeDimensionMeasure + ) { searchParams.set("tdd.m", metrics.tdd.expandedMeasureName); } - if (metrics.tdd.pinIndex !== -1) { - searchParams.set("tdd.p", metrics.tdd.pinIndex + ""); - } - if (metrics.tdd.chartType !== TDDChart.DEFAULT) { + + if ( + (preset.timeDimensionChartType !== undefined && + metrics.tdd.chartType !== preset.timeDimensionChartType) || + metrics.tdd.chartType !== TDDChart.DEFAULT + ) { searchParams.set("tdd.p", metrics.tdd.chartType); } + + // TODO: pin } function toPivotUrlParams( metrics: MetricsExplorerEntity, searchParams: URLSearchParams, + preset: V1ExplorePreset, ) { if (!metrics.pivot.active) return; - searchParams.set( - "p.r", - metrics.pivot.rows.dimension.map((d) => d.id).join(","), - ); - searchParams.set( - "p.c", - [ - ...metrics.pivot.columns.dimension.map((d) => d.id), - ...metrics.pivot.columns.measure.map((m) => m.id), - ].join(","), - ); + const mapPivotEntry = (data: PivotChipData) => { + if (data.type === PivotChipType.Time) + return ToURLParamTimeDimensionMap[data.id] as string; + return data.id; + }; + + const rows = metrics.pivot.rows.dimension.map(mapPivotEntry); + if (arrayOrderedEquals(rows, preset.pivotRows ?? [])) { + searchParams.set("p.r", rows.join(",")); + } + + const cols = [ + ...metrics.pivot.columns.dimension.map(mapPivotEntry), + ...metrics.pivot.columns.measure.map(mapPivotEntry), + ]; + if (arrayOrderedEquals(cols, preset.pivotCols ?? [])) { + searchParams.set("p.c", cols.join(",")); + } // TODO: other fields } diff --git a/web-common/src/lib/arrayUtils.ts b/web-common/src/lib/arrayUtils.ts index 28be43dfc5b..089a12e6b27 100644 --- a/web-common/src/lib/arrayUtils.ts +++ b/web-common/src/lib/arrayUtils.ts @@ -26,3 +26,17 @@ export function createBatches(array: T[], batchSize: number): T[][] { } return batches; } + +export function arrayUnorderedEquals(src: T[], tar: T[]) { + if (src.length !== tar.length) return false; + const set = new Set(src); + return tar.every((t) => set.has(t)); +} + +export function arrayOrderedEquals(src: T[], tar: T[]) { + if (src.length !== tar.length) return false; + for (let i = 0; i < src.length; i += 1) { + if (src[i] !== tar[i]) return false; + } + return true; +} diff --git a/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts b/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts index 9f125f4ed9a..22ad8a5b61a 100644 --- a/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts +++ b/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts @@ -75,6 +75,38 @@ proto3.util.setEnumType(ExploreComparisonMode, "rill.runtime.v1.ExploreCompariso { no: 3, name: "EXPLORE_COMPARISON_MODE_DIMENSION" }, ]); +/** + * @generated from enum rill.runtime.v1.ExploreWebView + */ +export enum ExploreWebView { + /** + * @generated from enum value: EXPLORE_ACTIVE_PAGE_OVERVIEW = 0; + */ + EXPLORE_ACTIVE_PAGE_OVERVIEW = 0, + + /** + * @generated from enum value: EXPLORE_ACTIVE_PAGE_TIME_DIMENSION = 1; + */ + EXPLORE_ACTIVE_PAGE_TIME_DIMENSION = 1, + + /** + * @generated from enum value: EXPLORE_ACTIVE_PAGE_PIVOT = 2; + */ + EXPLORE_ACTIVE_PAGE_PIVOT = 2, + + /** + * @generated from enum value: EXPLORE_ACTIVE_PAGE_CANVAS = 3; + */ + EXPLORE_ACTIVE_PAGE_CANVAS = 3, +} +// Retrieve enum metadata with: proto3.getEnumType(ExploreWebView) +proto3.util.setEnumType(ExploreWebView, "rill.runtime.v1.ExploreWebView", [ + { no: 0, name: "EXPLORE_ACTIVE_PAGE_OVERVIEW" }, + { no: 1, name: "EXPLORE_ACTIVE_PAGE_TIME_DIMENSION" }, + { no: 2, name: "EXPLORE_ACTIVE_PAGE_PIVOT" }, + { no: 3, name: "EXPLORE_ACTIVE_PAGE_CANVAS" }, +]); + /** * @generated from enum rill.runtime.v1.AssertionStatus */ @@ -2365,6 +2397,16 @@ export class ExplorePreset extends Message { */ timeRange = ""; + /** + * @generated from field: string timezone = 11; + */ + timezone = ""; + + /** + * @generated from field: string time_grain = 12; + */ + timeGrain = ""; + /** * Comparison mode. * @@ -2372,6 +2414,11 @@ export class ExplorePreset extends Message { */ comparisonMode = ExploreComparisonMode.UNSPECIFIED; + /** + * @generated from field: string compare_time_range = 13; + */ + compareTimeRange = ""; + /** * If comparison_mode is EXPLORE_COMPARISON_MODE_DIMENSION, this indicates the dimension to use. * @@ -2379,6 +2426,61 @@ export class ExplorePreset extends Message { */ comparisonDimension = ""; + /** + * @generated from field: rill.runtime.v1.ExploreWebView view = 14; + */ + view = ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW; + + /** + * @generated from field: string overview_sort_by = 15; + */ + overviewSortBy = ""; + + /** + * @generated from field: bool overview_sort_asc = 16; + */ + overviewSortAsc = false; + + /** + * @generated from field: string overview_expanded_dimension = 17; + */ + overviewExpandedDimension = ""; + + /** + * @generated from field: string time_dimension_measure = 18; + */ + timeDimensionMeasure = ""; + + /** + * @generated from field: string time_dimension_chart_type = 19; + */ + timeDimensionChartType = ""; + + /** + * @generated from field: bool time_dimension_pin = 20; + */ + timeDimensionPin = false; + + /** + * @generated from field: repeated string pivot_rows = 21; + */ + pivotRows: string[] = []; + + /** + * @generated from field: repeated string pivot_cols = 22; + */ + pivotCols: string[] = []; + + /** + * @generated from field: string pivot_sort_by = 23; + */ + pivotSortBy = ""; + + /** + * @generated from field: bool pivot_sort_asc = 24; + */ + pivotSortAsc = false; + constructor(data?: PartialMessage) { super(); proto3.util.initPartial(data, this); @@ -2393,8 +2495,22 @@ export class ExplorePreset extends Message { { no: 4, name: "measures", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, { no: 10, name: "measures_selector", kind: "message", T: FieldSelector }, { no: 6, name: "time_range", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 11, name: "timezone", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 12, name: "time_grain", kind: "scalar", T: 9 /* ScalarType.STRING */ }, { no: 7, name: "comparison_mode", kind: "enum", T: proto3.getEnumType(ExploreComparisonMode) }, + { no: 13, name: "compare_time_range", kind: "scalar", T: 9 /* ScalarType.STRING */ }, { no: 8, name: "comparison_dimension", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 14, name: "view", kind: "enum", T: proto3.getEnumType(ExploreWebView) }, + { no: 15, name: "overview_sort_by", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 16, name: "overview_sort_asc", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, + { no: 17, name: "overview_expanded_dimension", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 18, name: "time_dimension_measure", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 19, name: "time_dimension_chart_type", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 20, name: "time_dimension_pin", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, + { no: 21, name: "pivot_rows", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, + { no: 22, name: "pivot_cols", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, + { no: 23, name: "pivot_sort_by", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 24, name: "pivot_sort_asc", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): ExplorePreset { diff --git a/web-common/src/runtime-client/gen/index.schemas.ts b/web-common/src/runtime-client/gen/index.schemas.ts index 74043e77f5b..b317af0bca2 100644 --- a/web-common/src/runtime-client/gen/index.schemas.ts +++ b/web-common/src/runtime-client/gen/index.schemas.ts @@ -575,6 +575,12 @@ export interface V1TimeSeriesValue { records?: V1TimeSeriesValueRecords; } +export interface V1TimeSeriesTimeRange { + start?: string; + end?: string; + interval?: V1TimeGrain; +} + export interface V1TimeSeriesResponse { results?: V1TimeSeriesValue[]; spark?: V1TimeSeriesValue[]; @@ -603,12 +609,6 @@ export const V1TimeGrain = { TIME_GRAIN_YEAR: "TIME_GRAIN_YEAR", } as const; -export interface V1TimeSeriesTimeRange { - start?: string; - end?: string; - interval?: V1TimeGrain; -} - export interface V1TimeRange { start?: string; end?: string; @@ -820,6 +820,26 @@ export const V1ResourceEvent = { RESOURCE_EVENT_DELETE: "RESOURCE_EVENT_DELETE", } as const; +export interface V1Resource { + meta?: V1ResourceMeta; + projectParser?: V1ProjectParser; + source?: V1SourceV2; + model?: V1ModelV2; + metricsView?: V1MetricsViewV2; + explore?: V1Explore; + migration?: V1Migration; + report?: V1Report; + alert?: V1Alert; + pullTrigger?: V1PullTrigger; + refreshTrigger?: V1RefreshTrigger; + bucketPlanner?: V1BucketPlanner; + theme?: V1Theme; + component?: V1Component; + canvas?: V1Canvas; + api?: V1API; + connector?: V1ConnectorV2; +} + export type V1ResolveComponentResponseRendererProperties = { [key: string]: any; }; @@ -835,6 +855,13 @@ If it resolves to false, the other fields are not set. */ rendererProperties?: V1ResolveComponentResponseRendererProperties; } +export interface V1ReportState { + nextRunOn?: string; + currentExecution?: V1ReportExecution; + executionHistory?: V1ReportExecution[]; + executionCount?: number; +} + export type V1ReportSpecAnnotations = { [key: string]: string }; export interface V1ReportSpec { @@ -863,13 +890,6 @@ export interface V1ReportExecution { finishedOn?: string; } -export interface V1ReportState { - nextRunOn?: string; - currentExecution?: V1ReportExecution; - executionHistory?: V1ReportExecution[]; - executionCount?: number; -} - export interface V1Report { spec?: V1ReportSpec; state?: V1ReportState; @@ -883,31 +903,19 @@ export interface V1RefreshTriggerState { [key: string]: any; } +export interface V1RefreshTriggerSpec { + /** Resources to refresh. The refreshable types are sources, models, alerts, reports, and the project parser. +If a model is specified, a normal incremental refresh is triggered. Use the "models" field to trigger other kinds of model refreshes. */ + resources?: V1ResourceName[]; + /** Models to refresh. These are specified separately to enable more fine-grained configuration. */ + models?: V1RefreshModelTrigger[]; +} + export interface V1RefreshTrigger { spec?: V1RefreshTriggerSpec; state?: V1RefreshTriggerState; } -export interface V1Resource { - meta?: V1ResourceMeta; - projectParser?: V1ProjectParser; - source?: V1SourceV2; - model?: V1ModelV2; - metricsView?: V1MetricsViewV2; - explore?: V1Explore; - migration?: V1Migration; - report?: V1Report; - alert?: V1Alert; - pullTrigger?: V1PullTrigger; - refreshTrigger?: V1RefreshTrigger; - bucketPlanner?: V1BucketPlanner; - theme?: V1Theme; - component?: V1Component; - canvas?: V1Canvas; - api?: V1API; - connector?: V1ConnectorV2; -} - export interface V1RefreshModelTrigger { /** The model to refresh. */ model?: string; @@ -920,14 +928,6 @@ For non-incremental models, this is equivalent to a normal refresh. */ allErroredSplits?: boolean; } -export interface V1RefreshTriggerSpec { - /** Resources to refresh. The refreshable types are sources, models, alerts, reports, and the project parser. -If a model is specified, a normal incremental refresh is triggered. Use the "models" field to trigger other kinds of model refreshes. */ - resources?: V1ResourceName[]; - /** Models to refresh. These are specified separately to enable more fine-grained configuration. */ - models?: V1RefreshModelTrigger[]; -} - export type V1ReconcileStatus = (typeof V1ReconcileStatus)[keyof typeof V1ReconcileStatus]; @@ -1016,12 +1016,6 @@ export interface V1PullTrigger { state?: V1PullTriggerState; } -export interface V1ProjectParserState { - parseErrors?: V1ParseError[]; - currentCommitSha?: string; - watching?: boolean; -} - export interface V1ProjectParserSpec { [key: string]: any; } @@ -1049,6 +1043,12 @@ export interface V1ParseError { external?: boolean; } +export interface V1ProjectParserState { + parseErrors?: V1ParseError[]; + currentCommitSha?: string; + watching?: boolean; +} + export type V1Operation = (typeof V1Operation)[keyof typeof V1Operation]; // eslint-disable-next-line @typescript-eslint/no-redeclare @@ -1113,11 +1113,6 @@ export interface V1Notifier { properties?: V1NotifierProperties; } -export interface V1ModelV2 { - spec?: V1ModelSpec; - state?: V1ModelState; -} - /** * incremental_state contains the result of the most recent invocation of the model's incremental state resolver. */ @@ -1152,6 +1147,11 @@ export interface V1ModelState { splitsHaveErrors?: boolean; } +export interface V1ModelV2 { + spec?: V1ModelSpec; + state?: V1ModelState; +} + export type V1ModelSplitData = { [key: string]: any }; export interface V1ModelSplit { @@ -1243,11 +1243,6 @@ export interface V1MetricsViewToplistResponse { data?: V1MetricsViewToplistResponseDataItem[]; } -export interface V1MetricsViewSort { - name?: string; - ascending?: boolean; -} - export interface V1MetricsViewToplistRequest { instanceId?: string; metricsViewName?: string; @@ -1344,6 +1339,11 @@ It's set to true if the metrics view is based on an externally managed table. */ streaming?: boolean; } +export interface V1MetricsViewSort { + name?: string; + ascending?: boolean; +} + export interface V1MetricsViewSearchResponse { results?: MetricsViewSearchResponseSearchResult[]; } @@ -1354,11 +1354,6 @@ export interface V1MetricsViewSchemaResponse { export type V1MetricsViewRowsResponseDataItem = { [key: string]: any }; -export interface V1MetricsViewRowsResponse { - meta?: V1MetricsViewColumn[]; - data?: V1MetricsViewRowsResponseDataItem[]; -} - export interface V1MetricsViewFilter { include?: MetricsViewFilterCond[]; exclude?: MetricsViewFilterCond[]; @@ -1443,35 +1438,17 @@ export interface V1MetricsViewComparisonMeasureAlias { alias?: string; } -export interface V1MetricsViewComparisonRequest { - instanceId?: string; - metricsViewName?: string; - dimension?: V1MetricsViewAggregationDimension; - measures?: V1MetricsViewAggregationMeasure[]; - comparisonMeasures?: string[]; - sort?: V1MetricsViewComparisonSort[]; - timeRange?: V1TimeRange; - comparisonTimeRange?: V1TimeRange; - where?: V1Expression; - /** Optional. If both where and where_sql are set, both will be applied with an AND between them. */ - whereSql?: string; - having?: V1Expression; - /** Optional. If both having and having_sql are set, both will be applied with an AND between them. */ - havingSql?: string; - aliases?: V1MetricsViewComparisonMeasureAlias[]; - limit?: string; - offset?: string; - priority?: number; - exact?: boolean; - filter?: V1MetricsViewFilter; -} - export interface V1MetricsViewColumn { name?: string; type?: string; nullable?: boolean; } +export interface V1MetricsViewRowsResponse { + meta?: V1MetricsViewColumn[]; + data?: V1MetricsViewRowsResponseDataItem[]; +} + export interface V1MetricsViewAggregationSort { name?: string; desc?: boolean; @@ -1558,6 +1535,29 @@ export interface V1MetricsViewAggregationDimension { alias?: string; } +export interface V1MetricsViewComparisonRequest { + instanceId?: string; + metricsViewName?: string; + dimension?: V1MetricsViewAggregationDimension; + measures?: V1MetricsViewAggregationMeasure[]; + comparisonMeasures?: string[]; + sort?: V1MetricsViewComparisonSort[]; + timeRange?: V1TimeRange; + comparisonTimeRange?: V1TimeRange; + where?: V1Expression; + /** Optional. If both where and where_sql are set, both will be applied with an AND between them. */ + whereSql?: string; + having?: V1Expression; + /** Optional. If both having and having_sql are set, both will be applied with an AND between them. */ + havingSql?: string; + aliases?: V1MetricsViewComparisonMeasureAlias[]; + limit?: string; + offset?: string; + priority?: number; + exact?: boolean; + filter?: V1MetricsViewFilter; +} + export interface V1MapType { keyType?: Runtimev1Type; valueType?: Runtimev1Type; @@ -1807,6 +1807,17 @@ export const V1ExportFormat = { EXPORT_FORMAT_PARQUET: "EXPORT_FORMAT_PARQUET", } as const; +export type V1ExploreWebView = + (typeof V1ExploreWebView)[keyof typeof V1ExploreWebView]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const V1ExploreWebView = { + EXPLORE_ACTIVE_PAGE_OVERVIEW: "EXPLORE_ACTIVE_PAGE_OVERVIEW", + EXPLORE_ACTIVE_PAGE_TIME_DIMENSION: "EXPLORE_ACTIVE_PAGE_TIME_DIMENSION", + EXPLORE_ACTIVE_PAGE_PIVOT: "EXPLORE_ACTIVE_PAGE_PIVOT", + EXPLORE_ACTIVE_PAGE_CANVAS: "EXPLORE_ACTIVE_PAGE_CANVAS", +} as const; + export interface V1ExploreState { validSpec?: V1ExploreSpec; } @@ -1847,9 +1858,23 @@ export interface V1ExplorePreset { It corresponds to the `range` property of the explore's `time_ranges`. If not found in `time_ranges`, it should be added to the list. */ timeRange?: string; + timezone?: string; + timeGrain?: string; comparisonMode?: V1ExploreComparisonMode; + compareTimeRange?: string; /** If comparison_mode is EXPLORE_COMPARISON_MODE_DIMENSION, this indicates the dimension to use. */ comparisonDimension?: string; + view?: V1ExploreWebView; + overviewSortBy?: string; + overviewSortAsc?: boolean; + overviewExpandedDimension?: string; + timeDimensionMeasure?: string; + timeDimensionChartType?: string; + timeDimensionPin?: boolean; + pivotRows?: string[]; + pivotCols?: string[]; + pivotSortBy?: string; + pivotSortAsc?: boolean; } export interface V1ExploreSpec { From 10614fe2e87a773876f74b069645dc12630f5838 Mon Sep 17 00:00:00 2001 From: Aditya Hegde Date: Wed, 2 Oct 2024 17:40:12 +0530 Subject: [PATCH 05/17] Add tests for no presets --- proto/gen/rill/runtime/v1/resources.pb.go | 1274 +++++++++-------- .../rill/runtime/v1/resources.pb.validate.go | 6 +- .../gen/rill/runtime/v1/runtime.swagger.yaml | 3 +- proto/rill/runtime/v1/resources.proto | 9 +- .../stores/dashboard-store-defaults.ts | 46 +- .../dashboards/stores/test-data/data.ts | 6 + .../features/dashboards/url-state/defaults.ts | 6 + .../features/dashboards/url-state/fromUrl.ts | 76 +- .../features/dashboards/url-state/mappers.ts | 40 +- .../features/dashboards/url-state/toUrl.ts | 55 +- .../url-state/url-state-test-data.ts | 40 + .../dashboards/url-state/url-state.spec.ts | 199 +++ .../proto/gen/rill/runtime/v1/resources_pb.ts | 38 +- .../src/runtime-client/gen/index.schemas.ts | 1 + 14 files changed, 1068 insertions(+), 731 deletions(-) create mode 100644 web-common/src/features/dashboards/url-state/defaults.ts create mode 100644 web-common/src/features/dashboards/url-state/url-state-test-data.ts create mode 100644 web-common/src/features/dashboards/url-state/url-state.spec.ts diff --git a/proto/gen/rill/runtime/v1/resources.pb.go b/proto/gen/rill/runtime/v1/resources.pb.go index bc7b6734185..4d7e7877d73 100644 --- a/proto/gen/rill/runtime/v1/resources.pb.go +++ b/proto/gen/rill/runtime/v1/resources.pb.go @@ -129,25 +129,28 @@ func (ExploreComparisonMode) EnumDescriptor() ([]byte, []int) { type ExploreWebView int32 const ( - ExploreWebView_EXPLORE_ACTIVE_PAGE_OVERVIEW ExploreWebView = 0 - ExploreWebView_EXPLORE_ACTIVE_PAGE_TIME_DIMENSION ExploreWebView = 1 - ExploreWebView_EXPLORE_ACTIVE_PAGE_PIVOT ExploreWebView = 2 - ExploreWebView_EXPLORE_ACTIVE_PAGE_CANVAS ExploreWebView = 3 + ExploreWebView_EXPLORE_ACTIVE_PAGE_UNSPECIFIED ExploreWebView = 0 + ExploreWebView_EXPLORE_ACTIVE_PAGE_OVERVIEW ExploreWebView = 1 + ExploreWebView_EXPLORE_ACTIVE_PAGE_TIME_DIMENSION ExploreWebView = 2 + ExploreWebView_EXPLORE_ACTIVE_PAGE_PIVOT ExploreWebView = 3 + ExploreWebView_EXPLORE_ACTIVE_PAGE_CANVAS ExploreWebView = 4 ) // Enum value maps for ExploreWebView. var ( ExploreWebView_name = map[int32]string{ - 0: "EXPLORE_ACTIVE_PAGE_OVERVIEW", - 1: "EXPLORE_ACTIVE_PAGE_TIME_DIMENSION", - 2: "EXPLORE_ACTIVE_PAGE_PIVOT", - 3: "EXPLORE_ACTIVE_PAGE_CANVAS", + 0: "EXPLORE_ACTIVE_PAGE_UNSPECIFIED", + 1: "EXPLORE_ACTIVE_PAGE_OVERVIEW", + 2: "EXPLORE_ACTIVE_PAGE_TIME_DIMENSION", + 3: "EXPLORE_ACTIVE_PAGE_PIVOT", + 4: "EXPLORE_ACTIVE_PAGE_CANVAS", } ExploreWebView_value = map[string]int32{ - "EXPLORE_ACTIVE_PAGE_OVERVIEW": 0, - "EXPLORE_ACTIVE_PAGE_TIME_DIMENSION": 1, - "EXPLORE_ACTIVE_PAGE_PIVOT": 2, - "EXPLORE_ACTIVE_PAGE_CANVAS": 3, + "EXPLORE_ACTIVE_PAGE_UNSPECIFIED": 0, + "EXPLORE_ACTIVE_PAGE_OVERVIEW": 1, + "EXPLORE_ACTIVE_PAGE_TIME_DIMENSION": 2, + "EXPLORE_ACTIVE_PAGE_PIVOT": 3, + "EXPLORE_ACTIVE_PAGE_CANVAS": 4, } ) @@ -2693,7 +2696,7 @@ type ExplorePreset struct { TimeGrain string `protobuf:"bytes,12,opt,name=time_grain,json=timeGrain,proto3" json:"time_grain,omitempty"` // Comparison mode. ComparisonMode ExploreComparisonMode `protobuf:"varint,7,opt,name=comparison_mode,json=comparisonMode,proto3,enum=rill.runtime.v1.ExploreComparisonMode" json:"comparison_mode,omitempty"` - CompareTimeRange string `protobuf:"bytes,13,opt,name=compare_time_range,json=compareTimeRange,proto3" json:"compare_time_range,omitempty"` + CompareTimeRange *string `protobuf:"bytes,13,opt,name=compare_time_range,json=compareTimeRange,proto3,oneof" json:"compare_time_range,omitempty"` // If comparison_mode is EXPLORE_COMPARISON_MODE_DIMENSION, this indicates the dimension to use. ComparisonDimension string `protobuf:"bytes,8,opt,name=comparison_dimension,json=comparisonDimension,proto3" json:"comparison_dimension,omitempty"` View ExploreWebView `protobuf:"varint,14,opt,name=view,proto3,enum=rill.runtime.v1.ExploreWebView" json:"view,omitempty"` @@ -2805,8 +2808,8 @@ func (x *ExplorePreset) GetComparisonMode() ExploreComparisonMode { } func (x *ExplorePreset) GetCompareTimeRange() string { - if x != nil { - return x.CompareTimeRange + if x != nil && x.CompareTimeRange != nil { + return *x.CompareTimeRange } return "" } @@ -2822,7 +2825,7 @@ func (x *ExplorePreset) GetView() ExploreWebView { if x != nil { return x.View } - return ExploreWebView_EXPLORE_ACTIVE_PAGE_OVERVIEW + return ExploreWebView_EXPLORE_ACTIVE_PAGE_UNSPECIFIED } func (x *ExplorePreset) GetOverviewSortBy() string { @@ -7251,7 +7254,7 @@ var file_rill_runtime_v1_resources_proto_rawDesc = []byte{ 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xfd, 0x07, 0x0a, 0x0d, 0x45, 0x78, 0x70, 0x6c, + 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x99, 0x08, 0x0a, 0x0d, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x50, 0x72, 0x65, 0x73, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, @@ -7278,639 +7281,643 @@ var file_rill_runtime_v1_resources_proto_rawDesc = []byte{ 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, - 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, + 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x31, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x10, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, - 0x67, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, - 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x13, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x44, 0x69, 0x6d, 0x65, - 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x04, 0x76, 0x69, 0x65, 0x77, 0x18, 0x0e, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x57, 0x65, 0x62, - 0x56, 0x69, 0x65, 0x77, 0x52, 0x04, 0x76, 0x69, 0x65, 0x77, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x76, - 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x0f, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x53, 0x6f, - 0x72, 0x74, 0x42, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, - 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x61, 0x73, 0x63, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0f, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x53, 0x6f, 0x72, 0x74, 0x41, 0x73, 0x63, - 0x12, 0x3e, 0x0a, 0x1b, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x65, 0x78, 0x70, - 0x61, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x19, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x45, - 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x34, 0x0a, 0x16, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, - 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x14, 0x74, 0x69, 0x6d, 0x65, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4d, - 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x12, 0x39, 0x0a, 0x19, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, - 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x74, 0x69, 0x6d, 0x65, 0x44, - 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x72, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, - 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x69, 0x6e, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x74, - 0x69, 0x6d, 0x65, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x69, 0x6e, 0x12, - 0x1d, 0x0a, 0x0a, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x15, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x09, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x1d, - 0x0a, 0x0a, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x5f, 0x63, 0x6f, 0x6c, 0x73, 0x18, 0x16, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x09, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x43, 0x6f, 0x6c, 0x73, 0x12, 0x22, 0x0a, - 0x0d, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x17, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x53, 0x6f, 0x72, 0x74, 0x42, - 0x79, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, - 0x61, 0x73, 0x63, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x69, 0x76, 0x6f, 0x74, - 0x53, 0x6f, 0x72, 0x74, 0x41, 0x73, 0x63, 0x22, 0xca, 0x01, 0x0a, 0x0d, 0x46, 0x69, 0x65, 0x6c, - 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x6e, 0x76, - 0x65, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x6e, 0x76, 0x65, 0x72, - 0x74, 0x12, 0x12, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, - 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x3a, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, - 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, - 0x73, 0x12, 0x16, 0x0a, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x48, 0x00, 0x52, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x12, 0x2d, 0x0a, 0x11, 0x64, 0x75, 0x63, - 0x6b, 0x64, 0x62, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x10, 0x64, 0x75, 0x63, 0x6b, 0x64, 0x62, 0x45, 0x78, - 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x0a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x22, 0x29, 0x0a, 0x0f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, - 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, - 0x76, 0x0a, 0x09, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x04, - 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x69, 0x67, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, - 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x59, 0x0a, 0x0d, 0x4d, 0x69, 0x67, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x71, 0x6c, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x71, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x22, 0x2a, 0x0a, 0x0e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x6d, - 0x0a, 0x06, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x2f, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, - 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, - 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x32, 0x0a, 0x05, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xe8, 0x05, - 0x0a, 0x0a, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x18, 0x0a, 0x07, - 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x74, - 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x44, 0x0a, 0x10, - 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, - 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, - 0x65, 0x52, 0x0f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, - 0x6c, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, - 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x74, 0x69, 0x6d, - 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x71, 0x75, 0x65, 0x72, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x71, 0x75, - 0x65, 0x72, 0x79, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x41, 0x72, 0x67, 0x73, 0x4a, 0x73, - 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x6d, - 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, - 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x42, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x5f, - 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x72, - 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, - 0x78, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x0c, 0x65, 0x78, 0x70, - 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x37, 0x0a, 0x09, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, - 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, - 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, - 0x72, 0x73, 0x12, 0x4e, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x53, 0x70, 0x65, 0x63, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x77, 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x5f, - 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x77, - 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x12, - 0x34, 0x0a, 0x16, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x69, 0x73, 0x6f, - 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x14, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x49, 0x73, 0x6f, 0x44, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, - 0x6c, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x38, - 0x0a, 0x18, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, - 0x6b, 0x5f, 0x75, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x16, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x55, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, + 0x48, 0x00, 0x52, 0x10, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x52, + 0x61, 0x6e, 0x67, 0x65, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, + 0x6e, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x04, 0x76, 0x69, + 0x65, 0x77, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6c, 0x6f, + 0x72, 0x65, 0x57, 0x65, 0x62, 0x56, 0x69, 0x65, 0x77, 0x52, 0x04, 0x76, 0x69, 0x65, 0x77, 0x12, + 0x28, 0x0a, 0x10, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x73, 0x6f, 0x72, 0x74, + 0x5f, 0x62, 0x79, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x76, 0x65, 0x72, 0x76, + 0x69, 0x65, 0x77, 0x53, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x6f, 0x76, 0x65, + 0x72, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x61, 0x73, 0x63, 0x18, 0x10, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x53, 0x6f, + 0x72, 0x74, 0x41, 0x73, 0x63, 0x12, 0x3e, 0x0a, 0x1b, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, + 0x77, 0x5f, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x19, 0x6f, 0x76, 0x65, 0x72, + 0x76, 0x69, 0x65, 0x77, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x44, 0x69, 0x6d, 0x65, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x16, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, 0x69, + 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x18, + 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x74, 0x69, 0x6d, 0x65, 0x44, 0x69, 0x6d, 0x65, 0x6e, + 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x12, 0x39, 0x0a, 0x19, 0x74, + 0x69, 0x6d, 0x65, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x68, + 0x61, 0x72, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, + 0x74, 0x69, 0x6d, 0x65, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, + 0x72, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, + 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x69, 0x6e, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x10, 0x74, 0x69, 0x6d, 0x65, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, + 0x6e, 0x50, 0x69, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x5f, 0x72, 0x6f, + 0x77, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x52, + 0x6f, 0x77, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x5f, 0x63, 0x6f, 0x6c, + 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x43, 0x6f, + 0x6c, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x5f, 0x73, 0x6f, 0x72, 0x74, + 0x5f, 0x62, 0x79, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x69, 0x76, 0x6f, 0x74, + 0x53, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x5f, + 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x61, 0x73, 0x63, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, + 0x70, 0x69, 0x76, 0x6f, 0x74, 0x53, 0x6f, 0x72, 0x74, 0x41, 0x73, 0x63, 0x42, 0x15, 0x0a, 0x13, + 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, + 0x6e, 0x67, 0x65, 0x22, 0xca, 0x01, 0x0a, 0x0d, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x12, 0x12, 0x0a, + 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x03, 0x61, 0x6c, + 0x6c, 0x12, 0x3a, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x16, 0x0a, + 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, + 0x72, 0x65, 0x67, 0x65, 0x78, 0x12, 0x2d, 0x0a, 0x11, 0x64, 0x75, 0x63, 0x6b, 0x64, 0x62, 0x5f, + 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x00, 0x52, 0x10, 0x64, 0x75, 0x63, 0x6b, 0x64, 0x62, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x0a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x22, 0x29, 0x0a, 0x0f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x76, 0x0a, 0x09, 0x4d, + 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x35, 0x0a, 0x05, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x69, + 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x22, 0x59, 0x0a, 0x0d, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x53, 0x70, 0x65, 0x63, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x71, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x73, 0x71, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x2a, + 0x0a, 0x0e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x6d, 0x0a, 0x06, 0x52, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x12, 0x2f, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, + 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x32, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xe8, 0x05, 0x0a, 0x0a, 0x52, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x72, 0x69, 0x67, + 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x74, 0x72, 0x69, 0x67, 0x67, + 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x44, 0x0a, 0x10, 0x72, 0x65, 0x66, 0x72, + 0x65, 0x73, 0x68, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x0f, 0x72, + 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x27, + 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x71, 0x75, 0x65, 0x72, 0x79, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x71, 0x75, 0x65, + 0x72, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, + 0x61, 0x72, 0x67, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x41, 0x72, 0x67, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x21, + 0x0a, 0x0c, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x6d, 0x69, + 0x74, 0x12, 0x42, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, + 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x46, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x37, 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x52, 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x4e, + 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0a, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, + 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2b, + 0x0a, 0x11, 0x77, 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x5f, 0x69, 0x6e, 0x68, 0x65, + 0x72, 0x69, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x77, 0x61, 0x74, 0x65, 0x72, + 0x6d, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x69, 0x73, 0x6f, 0x5f, 0x64, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x49, 0x73, 0x6f, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6c, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x38, 0x0a, 0x18, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x75, 0x6e, + 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x6e, 0x63, 0x6c, + 0x6f, 0x73, 0x65, 0x64, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x90, 0x02, 0x0a, 0x0b, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x72, 0x75, 0x6e, + 0x5f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x6e, 0x65, 0x78, 0x74, 0x52, 0x75, 0x6e, 0x4f, 0x6e, + 0x12, 0x4d, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x4d, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, + 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x65, 0x78, + 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x27, + 0x0a, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x81, 0x02, 0x0a, 0x0f, 0x52, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x61, + 0x64, 0x68, 0x6f, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x64, 0x68, 0x6f, + 0x63, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3b, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x54, + 0x69, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x6f, + 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x4f, 0x6e, 0x12, 0x3b, + 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x0a, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x4f, 0x6e, 0x22, 0x6a, 0x0a, 0x05, 0x41, + 0x6c, 0x65, 0x72, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, + 0x73, 0x70, 0x65, 0x63, 0x12, 0x31, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xeb, 0x08, 0x0a, 0x09, 0x41, 0x6c, 0x65, 0x72, + 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, + 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x44, 0x0a, 0x10, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, + 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x0f, 0x72, 0x65, 0x66, 0x72, + 0x65, 0x73, 0x68, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x77, + 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x5f, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x77, 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, + 0x6b, 0x49, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x69, 0x73, 0x6f, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x73, 0x49, 0x73, 0x6f, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, + 0x0a, 0x0f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, + 0x6c, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x38, 0x0a, 0x18, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x75, 0x6e, 0x63, 0x6c, 0x6f, + 0x73, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x76, 0x61, 0x6c, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x65, + 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x74, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x71, 0x75, + 0x65, 0x72, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x71, 0x75, 0x65, 0x72, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x71, 0x75, 0x65, + 0x72, 0x79, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x41, 0x72, 0x67, 0x73, 0x4a, 0x73, 0x6f, + 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x18, 0x16, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x12, 0x48, 0x0a, + 0x13, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x69, 0x65, 0x73, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x71, 0x75, 0x65, 0x72, 0x79, + 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x0b, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x00, 0x52, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x46, 0x6f, 0x72, 0x55, 0x73, + 0x65, 0x72, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x14, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x6f, + 0x72, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x0c, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x00, 0x52, 0x11, 0x71, 0x75, 0x65, 0x72, 0x79, 0x46, 0x6f, 0x72, 0x55, 0x73, + 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x4b, 0x0a, 0x14, 0x71, 0x75, 0x65, 0x72, 0x79, + 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, + 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x48, 0x00, + 0x52, 0x12, 0x71, 0x75, 0x65, 0x72, 0x79, 0x46, 0x6f, 0x72, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x6f, + 0x6e, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4f, 0x6e, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x6f, 0x6e, 0x5f, 0x66, 0x61, + 0x69, 0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, + 0x4f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, + 0x5f, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1a, + 0x0a, 0x08, 0x72, 0x65, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x08, 0x72, 0x65, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x72, 0x65, + 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x72, 0x65, 0x6e, 0x6f, + 0x74, 0x69, 0x66, 0x79, 0x41, 0x66, 0x74, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, + 0x12, 0x37, 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x18, 0x15, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x09, + 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x4d, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, + 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, + 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, + 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x90, 0x02, 0x0a, 0x0b, 0x52, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, - 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x6e, 0x65, 0x78, 0x74, 0x52, - 0x75, 0x6e, 0x4f, 0x6e, 0x12, 0x4d, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, - 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x4d, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, - 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x10, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x65, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x81, 0x02, 0x0a, 0x0f, - 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x14, 0x0a, 0x05, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, - 0x61, 0x64, 0x68, 0x6f, 0x63, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3b, 0x0a, 0x0b, 0x72, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x72, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x71, 0x75, 0x65, 0x72, + 0x79, 0x5f, 0x66, 0x6f, 0x72, 0x22, 0x61, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, + 0x37, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0a, 0x70, 0x72, + 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xc7, 0x02, 0x0a, 0x0a, 0x41, 0x6c, 0x65, + 0x72, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, + 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, + 0x48, 0x61, 0x73, 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x66, 0x73, 0x5f, 0x68, 0x61, 0x73, + 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x66, 0x73, 0x48, 0x61, 0x73, + 0x68, 0x12, 0x3a, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x6f, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x52, 0x09, 0x6e, 0x65, 0x78, 0x74, 0x52, 0x75, 0x6e, 0x4f, 0x6e, 0x12, 0x4c, 0x0a, + 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, + 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4c, 0x0a, 0x11, 0x65, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x45, 0x78, + 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x22, 0x91, 0x03, 0x0a, 0x0e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x12, 0x38, 0x0a, 0x06, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x73, + 0x73, 0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x6f, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x11, 0x73, 0x65, 0x6e, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x41, 0x0a, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x4f, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x6f, - 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x4f, 0x6e, 0x22, - 0x6a, 0x0a, 0x05, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, - 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, 0x70, - 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x31, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xeb, 0x08, 0x0a, 0x09, - 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x72, 0x69, - 0x67, 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x74, 0x72, 0x69, 0x67, - 0x67, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x44, 0x0a, 0x10, 0x72, 0x65, 0x66, - 0x72, 0x65, 0x73, 0x68, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x0f, - 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, - 0x2b, 0x0a, 0x11, 0x77, 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x5f, 0x69, 0x6e, 0x68, - 0x65, 0x72, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x77, 0x61, 0x74, 0x65, - 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x12, 0x34, 0x0a, 0x16, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x69, 0x73, 0x6f, 0x5f, 0x64, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x49, 0x73, 0x6f, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, - 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x38, 0x0a, 0x18, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x75, - 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x6e, 0x63, - 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, - 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, - 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1d, - 0x0a, 0x0a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x71, 0x75, 0x65, 0x72, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, - 0x0f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x41, 0x72, 0x67, - 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, - 0x72, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, - 0x72, 0x12, 0x48, 0x0a, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x72, - 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, - 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x46, - 0x6f, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x14, 0x71, 0x75, 0x65, 0x72, - 0x79, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, - 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x11, 0x71, 0x75, 0x65, 0x72, 0x79, 0x46, - 0x6f, 0x72, 0x55, 0x73, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x4b, 0x0a, 0x14, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x48, 0x00, 0x52, 0x12, 0x71, 0x75, 0x65, 0x72, 0x79, 0x46, 0x6f, 0x72, 0x41, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6e, 0x6f, 0x74, 0x69, - 0x66, 0x79, 0x5f, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x18, 0x0f, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4f, 0x6e, 0x52, 0x65, 0x63, - 0x6f, 0x76, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x6f, - 0x6e, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6e, 0x6f, - 0x74, 0x69, 0x66, 0x79, 0x4f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x6f, - 0x74, 0x69, 0x66, 0x79, 0x5f, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x11, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4f, 0x6e, 0x45, 0x72, 0x72, - 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x18, 0x12, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x34, - 0x0a, 0x16, 0x72, 0x65, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, - 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, - 0x72, 0x65, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x41, 0x66, 0x74, 0x65, 0x72, 0x53, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, - 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, - 0x65, 0x72, 0x52, 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x4d, 0x0a, - 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x14, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x41, - 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3e, 0x0a, 0x10, - 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0b, 0x0a, 0x09, - 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x6f, 0x72, 0x22, 0x61, 0x0a, 0x08, 0x4e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x12, 0x37, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xc7, 0x02, 0x0a, - 0x0a, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, - 0x70, 0x65, 0x63, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x73, 0x70, 0x65, 0x63, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x66, 0x73, - 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x66, - 0x73, 0x48, 0x61, 0x73, 0x68, 0x12, 0x3a, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x72, 0x75, - 0x6e, 0x5f, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x6e, 0x65, 0x78, 0x74, 0x52, 0x75, 0x6e, 0x4f, - 0x6e, 0x12, 0x4c, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, - 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, - 0x6c, 0x65, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x4c, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, - 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x65, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x27, 0x0a, - 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x91, 0x03, 0x0a, 0x0e, 0x41, 0x6c, 0x65, 0x72, 0x74, - 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x68, - 0x6f, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x12, - 0x38, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x6e, - 0x74, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x73, 0x65, 0x6e, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x41, 0x0a, 0x0e, 0x65, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x65, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x65, 0x64, 0x4f, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, - 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, - 0x64, 0x4f, 0x6e, 0x12, 0x45, 0x0a, 0x10, 0x73, 0x75, 0x70, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, - 0x64, 0x5f, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0f, 0x73, 0x75, 0x70, 0x70, 0x72, - 0x65, 0x73, 0x73, 0x65, 0x64, 0x53, 0x69, 0x6e, 0x63, 0x65, 0x22, 0xa4, 0x01, 0x0a, 0x0f, 0x41, - 0x73, 0x73, 0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x38, - 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, - 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x41, 0x73, 0x73, 0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x32, 0x0a, 0x08, 0x66, 0x61, 0x69, 0x6c, - 0x5f, 0x72, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, - 0x75, 0x63, 0x74, 0x52, 0x07, 0x66, 0x61, 0x69, 0x6c, 0x52, 0x6f, 0x77, 0x12, 0x23, 0x0a, 0x0d, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x22, 0x7c, 0x0a, 0x0b, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, - 0x12, 0x34, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, + 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x4f, 0x6e, 0x12, + 0x45, 0x0a, 0x10, 0x73, 0x75, 0x70, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x5f, 0x73, 0x69, + 0x6e, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0f, 0x73, 0x75, 0x70, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, + 0x64, 0x53, 0x69, 0x6e, 0x63, 0x65, 0x22, 0xa4, 0x01, 0x0a, 0x0f, 0x41, 0x73, 0x73, 0x65, 0x72, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x38, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, + 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x73, 0x73, + 0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x32, 0x0a, 0x08, 0x66, 0x61, 0x69, 0x6c, 0x5f, 0x72, 0x6f, 0x77, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, + 0x07, 0x66, 0x61, 0x69, 0x6c, 0x52, 0x6f, 0x77, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x7c, 0x0a, + 0x0b, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x04, + 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, + 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x6c, + 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, + 0x65, 0x63, 0x12, 0x37, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x21, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x11, 0x0a, 0x0f, 0x50, + 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x22, 0x12, + 0x0a, 0x10, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, + 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, + 0x67, 0x67, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x3a, + 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, + 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x8f, 0x01, 0x0a, 0x12, 0x52, + 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x70, 0x65, + 0x63, 0x12, 0x3b, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3c, + 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, - 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x37, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, - 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, - 0x11, 0x0a, 0x0f, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x70, - 0x65, 0x63, 0x22, 0x12, 0x0a, 0x10, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, - 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x66, 0x72, 0x65, - 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x04, 0x73, 0x70, 0x65, - 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, - 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, - 0x65, 0x63, 0x12, 0x3a, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, - 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x8f, - 0x01, 0x0a, 0x12, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, - 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x3b, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4d, 0x6f, 0x64, 0x65, - 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, - 0x22, 0x15, 0x0a, 0x13, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, - 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x66, 0x72, - 0x65, 0x73, 0x68, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, - 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x75, 0x6c, 0x6c, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x04, 0x66, 0x75, 0x6c, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x70, 0x6c, - 0x69, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x73, 0x70, 0x6c, 0x69, 0x74, - 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x61, 0x6c, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x65, 0x64, - 0x5f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x61, - 0x6c, 0x6c, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x65, 0x64, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x73, 0x22, - 0x82, 0x01, 0x0a, 0x0d, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, - 0x72, 0x12, 0x36, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x53, - 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x39, 0x0a, 0x05, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, - 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x22, 0x60, 0x0a, 0x11, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, - 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x4b, 0x0a, 0x0e, 0x65, 0x78, 0x74, - 0x72, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, - 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0d, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x2c, 0x0a, 0x12, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, - 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, - 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, - 0x67, 0x69, 0x6f, 0x6e, 0x22, 0xd6, 0x02, 0x0a, 0x13, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x45, - 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x52, 0x0a, 0x0d, - 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x74, 0x72, - 0x61, 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, - 0x67, 0x79, 0x52, 0x0c, 0x72, 0x6f, 0x77, 0x73, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, - 0x12, 0x28, 0x0a, 0x10, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x62, - 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x72, 0x6f, 0x77, 0x73, - 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x54, 0x0a, 0x0e, 0x66, 0x69, - 0x6c, 0x65, 0x73, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x74, 0x72, 0x61, - 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, - 0x79, 0x52, 0x0d, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, - 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x4c, 0x69, 0x6d, 0x69, - 0x74, 0x22, 0x4a, 0x0a, 0x08, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x18, 0x0a, - 0x14, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x52, 0x41, 0x54, - 0x45, 0x47, 0x59, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, - 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x54, 0x41, 0x49, 0x4c, 0x10, 0x02, 0x22, 0x6a, 0x0a, - 0x05, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x70, 0x65, 0x63, - 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x31, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xb9, 0x01, 0x0a, 0x09, 0x54, 0x68, - 0x65, 0x6d, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x40, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6d, 0x61, - 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x54, 0x72, 0x69, + 0x67, 0x67, 0x65, 0x72, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x22, 0x15, 0x0a, 0x13, + 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4d, + 0x6f, 0x64, 0x65, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6d, + 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, + 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x75, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x04, 0x66, 0x75, 0x6c, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x73, 0x12, 0x2c, 0x0a, + 0x12, 0x61, 0x6c, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x65, 0x64, 0x5f, 0x73, 0x70, 0x6c, + 0x69, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x61, 0x6c, 0x6c, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x65, 0x64, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x0d, + 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x36, 0x0a, + 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, + 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, + 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x39, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, 0x61, + 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x22, 0x60, 0x0a, 0x11, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, + 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x4b, 0x0a, 0x0e, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, + 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, + 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x52, 0x0d, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x22, 0x2c, 0x0a, 0x12, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x6e, + 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, + 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, + 0x22, 0xd6, 0x02, 0x0a, 0x13, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x74, 0x72, 0x61, + 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x52, 0x0a, 0x0d, 0x72, 0x6f, 0x77, 0x73, + 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0c, + 0x72, 0x6f, 0x77, 0x73, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x28, 0x0a, 0x10, + 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x72, 0x6f, 0x77, 0x73, 0x4c, 0x69, 0x6d, 0x69, + 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x54, 0x0a, 0x0e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, + 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, - 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x44, 0x0a, 0x0f, 0x73, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x48, 0x01, 0x52, 0x0e, 0x73, 0x65, - 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x42, - 0x10, 0x0a, 0x0e, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, - 0x72, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, - 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x0c, 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x22, 0x76, 0x0a, 0x09, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, - 0x12, 0x32, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, + 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x66, + 0x69, 0x6c, 0x65, 0x73, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x1f, 0x0a, 0x0b, + 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x4a, 0x0a, + 0x08, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x54, 0x52, + 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, + 0x44, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, + 0x48, 0x45, 0x41, 0x44, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, + 0x47, 0x59, 0x5f, 0x54, 0x41, 0x49, 0x4c, 0x10, 0x02, 0x22, 0x6a, 0x0a, 0x05, 0x54, 0x68, 0x65, + 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, + 0x65, 0x63, 0x12, 0x31, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xb9, 0x01, 0x0a, 0x09, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, + 0x70, 0x65, 0x63, 0x12, 0x40, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x63, + 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x69, 0x6c, + 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, + 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, + 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x44, 0x0a, 0x0f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, + 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, - 0x73, 0x70, 0x65, 0x63, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xc3, 0x03, 0x0a, 0x0d, - 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x14, 0x0a, - 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, - 0x74, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x75, 0x62, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, - 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x12, 0x48, 0x0a, 0x13, 0x72, - 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, - 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, - 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, - 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, - 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, - 0x72, 0x12, 0x48, 0x0a, 0x13, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x5f, 0x70, 0x72, - 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, - 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x05, 0x69, - 0x6e, 0x70, 0x75, 0x74, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, + 0x2e, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x48, 0x01, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, + 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x42, 0x12, 0x0a, + 0x10, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, + 0x72, 0x22, 0x0c, 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, + 0x76, 0x0a, 0x09, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x04, + 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, - 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x05, - 0x69, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x3a, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, - 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x73, 0x68, 0x6f, 0x77, 0x12, 0x2a, 0x0a, 0x11, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, - 0x5f, 0x69, 0x6e, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x49, 0x6e, 0x43, 0x61, 0x6e, 0x76, 0x61, - 0x73, 0x22, 0x4f, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x73, 0x70, 0x65, - 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, - 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x70, - 0x65, 0x63, 0x22, 0x78, 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, - 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, - 0x3b, 0x0a, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, - 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x6d, 0x0a, 0x06, - 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x12, 0x2f, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, - 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x32, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, - 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x89, 0x02, 0x0a, 0x0a, - 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, - 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x07, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x61, - 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x67, 0x61, 0x70, 0x12, 0x40, 0x0a, 0x09, - 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, - 0x62, 0x6c, 0x65, 0x52, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x31, - 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, - 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, - 0x73, 0x12, 0x44, 0x0a, 0x0e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x72, 0x75, - 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, - 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x75, - 0x72, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, - 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x49, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x76, 0x61, - 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, - 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, - 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x70, - 0x65, 0x63, 0x22, 0xd5, 0x01, 0x0a, 0x0a, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x49, 0x74, 0x65, - 0x6d, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, - 0x2a, 0x0a, 0x11, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x63, 0x61, - 0x6e, 0x76, 0x61, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x65, 0x66, 0x69, - 0x6e, 0x65, 0x64, 0x49, 0x6e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x12, 0x11, 0x0a, 0x01, 0x78, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x01, 0x78, 0x88, 0x01, 0x01, 0x12, 0x11, - 0x0a, 0x01, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x01, 0x52, 0x01, 0x79, 0x88, 0x01, - 0x01, 0x12, 0x19, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, - 0x48, 0x02, 0x52, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x03, 0x52, 0x06, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x42, 0x04, 0x0a, 0x02, 0x5f, 0x78, 0x42, - 0x04, 0x0a, 0x02, 0x5f, 0x79, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x42, - 0x09, 0x0a, 0x07, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x64, 0x0a, 0x03, 0x41, 0x50, - 0x49, 0x12, 0x2c, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x18, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x41, 0x50, 0x49, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, - 0x2f, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, - 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x41, 0x50, 0x49, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x22, 0xb1, 0x02, 0x0a, 0x07, 0x41, 0x50, 0x49, 0x53, 0x70, 0x65, 0x63, 0x12, 0x1a, 0x0a, 0x08, - 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x12, 0x48, 0x0a, 0x13, 0x72, 0x65, 0x73, 0x6f, - 0x6c, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, - 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, - 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x73, 0x75, - 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x70, 0x65, - 0x6e, 0x61, 0x70, 0x69, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x46, 0x0a, 0x12, 0x6f, - 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x52, 0x11, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x73, 0x12, 0x4f, 0x0a, 0x17, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x72, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x05, + 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, + 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xc3, 0x03, 0x0a, 0x0d, 0x43, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, + 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, + 0x1a, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x73, 0x75, 0x62, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, + 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, + 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x12, 0x48, 0x0a, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x6c, + 0x76, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x15, 0x6f, - 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x53, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x22, 0x0a, 0x0a, 0x08, 0x41, 0x50, 0x49, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x22, 0x9b, 0x01, 0x0a, 0x08, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, - 0x0a, 0x72, 0x65, 0x66, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x09, 0x72, 0x65, 0x66, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, - 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, - 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x69, - 0x63, 0x6b, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, - 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x22, 0xa5, - 0x01, 0x0a, 0x0a, 0x50, 0x61, 0x72, 0x73, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, - 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, - 0x50, 0x61, 0x74, 0x68, 0x12, 0x44, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6c, 0x6f, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, - 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x68, 0x61, 0x72, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x78, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x22, 0x50, 0x0a, 0x0f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, + 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, + 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x12, 0x48, 0x0a, + 0x13, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x50, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, + 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, + 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, + 0x74, 0x12, 0x3a, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, + 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x12, 0x0a, + 0x04, 0x73, 0x68, 0x6f, 0x77, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x68, 0x6f, + 0x77, 0x12, 0x2a, 0x0a, 0x11, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, + 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x65, 0x64, 0x49, 0x6e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x22, 0x4f, 0x0a, + 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x3d, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, + 0x70, 0x65, 0x63, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x70, 0x65, 0x63, 0x22, 0x78, + 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, + 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x3b, 0x0a, 0x0d, 0x64, + 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x64, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x6d, 0x0a, 0x06, 0x43, 0x61, 0x6e, 0x76, + 0x61, 0x73, 0x12, 0x2f, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, + 0x70, 0x65, 0x63, 0x12, 0x32, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x89, 0x02, 0x0a, 0x0a, 0x43, 0x61, 0x6e, 0x76, + 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, + 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x61, 0x70, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x03, 0x67, 0x61, 0x70, 0x12, 0x40, 0x0a, 0x09, 0x76, 0x61, 0x72, 0x69, + 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, + 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, + 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x05, 0x69, 0x74, + 0x65, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, + 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, + 0x61, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x44, 0x0a, + 0x0e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, + 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, + 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x75, + 0x6c, 0x65, 0x73, 0x22, 0x49, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x73, 0x70, 0x65, 0x63, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, + 0x70, 0x65, 0x63, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x70, 0x65, 0x63, 0x22, 0xd5, + 0x01, 0x0a, 0x0a, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1c, 0x0a, + 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x49, + 0x6e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x12, 0x11, 0x0a, 0x01, 0x78, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x48, 0x00, 0x52, 0x01, 0x78, 0x88, 0x01, 0x01, 0x12, 0x11, 0x0a, 0x01, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x01, 0x52, 0x01, 0x79, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, + 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x02, 0x52, 0x05, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x03, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x88, 0x01, 0x01, 0x42, 0x04, 0x0a, 0x02, 0x5f, 0x78, 0x42, 0x04, 0x0a, 0x02, 0x5f, + 0x79, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x42, 0x09, 0x0a, 0x07, 0x5f, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x64, 0x0a, 0x03, 0x41, 0x50, 0x49, 0x12, 0x2c, 0x0a, + 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, + 0x49, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x2f, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, + 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xb1, 0x02, 0x0a, + 0x07, 0x41, 0x50, 0x49, 0x53, 0x70, 0x65, 0x63, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x72, 0x12, 0x48, 0x0a, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x27, + 0x0a, 0x0f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, + 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x46, 0x0a, 0x12, 0x6f, 0x70, 0x65, 0x6e, 0x61, + 0x70, 0x69, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x11, 0x6f, 0x70, + 0x65, 0x6e, 0x61, 0x70, 0x69, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, + 0x4f, 0x0a, 0x17, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x15, 0x6f, 0x70, 0x65, 0x6e, 0x61, + 0x70, 0x69, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x22, 0x0a, 0x0a, 0x08, 0x41, 0x50, 0x49, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x9b, 0x01, 0x0a, + 0x08, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x66, + 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, + 0x65, 0x66, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x69, 0x73, 0x61, + 0x62, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, + 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, + 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x22, 0xa5, 0x01, 0x0a, 0x0a, 0x50, + 0x61, 0x72, 0x73, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x5f, - 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x70, - 0x65, 0x72, 0x74, 0x79, 0x50, 0x61, 0x74, 0x68, 0x22, 0x4b, 0x0a, 0x0f, 0x44, 0x65, 0x70, 0x65, - 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, - 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x65, 0x6e, - 0x64, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x2a, 0x0a, 0x0e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, - 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, + 0x12, 0x44, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x4c, + 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x22, 0x50, 0x0a, 0x0f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, + 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x68, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, + 0x50, 0x61, 0x74, 0x68, 0x22, 0x4b, 0x0a, 0x0f, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, + 0x63, 0x79, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x22, 0x22, 0x0a, 0x0c, 0x43, 0x68, 0x61, 0x72, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0xae, 0x03, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, - 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x12, - 0x4e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, - 0x70, 0x65, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, - 0x31, 0x0a, 0x14, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, - 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, - 0x65, 0x73, 0x12, 0x77, 0x0a, 0x19, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, - 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, - 0x72, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, - 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x17, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x46, 0x72, - 0x6f, 0x6d, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x50, - 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4a, 0x0a, 0x1c, 0x50, 0x72, - 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x72, 0x69, - 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, + 0x79, 0x22, 0x2a, 0x0a, 0x0e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x22, 0x0a, + 0x0c, 0x43, 0x68, 0x61, 0x72, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x6c, 0x69, 0x6e, + 0x65, 0x22, 0xae, 0x03, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, + 0x70, 0x65, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x12, 0x4e, 0x0a, 0x0a, 0x70, + 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x2e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x2e, + 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, + 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x77, + 0x0a, 0x19, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x5f, 0x66, 0x72, 0x6f, + 0x6d, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x3b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, + 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x46, 0x72, 0x6f, 0x6d, + 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x17, + 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, + 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x2d, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, - 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, - 0x63, 0x48, 0x61, 0x73, 0x68, 0x22, 0x78, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x56, 0x32, 0x12, 0x32, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x70, - 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2a, - 0x8a, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, - 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, - 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, - 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x01, - 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x54, - 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x1c, - 0x0a, 0x18, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, - 0x55, 0x53, 0x5f, 0x52, 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x03, 0x2a, 0xab, 0x01, 0x0a, - 0x15, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, - 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x27, 0x0a, 0x23, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, - 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, - 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, - 0x20, 0x0a, 0x1c, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, - 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, - 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, - 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x54, 0x49, 0x4d, - 0x45, 0x10, 0x02, 0x12, 0x25, 0x0a, 0x21, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, - 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x44, - 0x49, 0x4d, 0x45, 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x2a, 0x99, 0x01, 0x0a, 0x0e, 0x45, - 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x57, 0x65, 0x62, 0x56, 0x69, 0x65, 0x77, 0x12, 0x20, 0x0a, - 0x1c, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, - 0x50, 0x41, 0x47, 0x45, 0x5f, 0x4f, 0x56, 0x45, 0x52, 0x56, 0x49, 0x45, 0x57, 0x10, 0x00, 0x12, - 0x26, 0x0a, 0x22, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, - 0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x5f, 0x44, 0x49, 0x4d, 0x45, - 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, 0x45, 0x58, 0x50, 0x4c, 0x4f, - 0x52, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x50, - 0x49, 0x56, 0x4f, 0x54, 0x10, 0x02, 0x12, 0x1e, 0x0a, 0x1a, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, - 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x43, 0x41, - 0x4e, 0x56, 0x41, 0x53, 0x10, 0x03, 0x2a, 0x85, 0x01, 0x0a, 0x0f, 0x41, 0x73, 0x73, 0x65, 0x72, - 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x1c, 0x41, 0x53, - 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, - 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, - 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, - 0x5f, 0x50, 0x41, 0x53, 0x53, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x53, 0x53, 0x45, 0x52, - 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x41, 0x49, 0x4c, - 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, - 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x42, 0xc1, - 0x01, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x69, 0x6c, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x72, 0x69, - 0x6c, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x72, 0x69, 0x6c, - 0x6c, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x72, 0x75, 0x6e, - 0x74, 0x69, 0x6d, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x52, 0x52, 0x58, 0xaa, 0x02, 0x0f, 0x52, - 0x69, 0x6c, 0x6c, 0x2e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, - 0x0f, 0x52, 0x69, 0x6c, 0x6c, 0x5c, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5c, 0x56, 0x31, - 0xe2, 0x02, 0x1b, 0x52, 0x69, 0x6c, 0x6c, 0x5c, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5c, - 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, - 0x11, 0x52, 0x69, 0x6c, 0x6c, 0x3a, 0x3a, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x3a, 0x3a, - 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4a, 0x0a, 0x1c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x69, 0x65, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x22, 0x2d, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x68, 0x61, 0x73, + 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, 0x48, 0x61, 0x73, + 0x68, 0x22, 0x78, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x56, 0x32, + 0x12, 0x32, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, + 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, + 0x73, 0x70, 0x65, 0x63, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2a, 0x8a, 0x01, 0x0a, 0x0f, + 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x20, 0x0a, 0x1c, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, + 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, + 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, + 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, + 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, + 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, + 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x03, 0x2a, 0xab, 0x01, 0x0a, 0x15, 0x45, 0x78, 0x70, + 0x6c, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, 0x6f, + 0x64, 0x65, 0x12, 0x27, 0x0a, 0x23, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, + 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x20, 0x0a, 0x1c, 0x45, + 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, + 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x20, 0x0a, + 0x1c, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, + 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x10, 0x02, 0x12, + 0x25, 0x0a, 0x21, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, + 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x44, 0x49, 0x4d, 0x45, 0x4e, + 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x2a, 0xbe, 0x01, 0x0a, 0x0e, 0x45, 0x78, 0x70, 0x6c, 0x6f, + 0x72, 0x65, 0x57, 0x65, 0x62, 0x56, 0x69, 0x65, 0x77, 0x12, 0x23, 0x0a, 0x1f, 0x45, 0x58, 0x50, + 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, + 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x20, + 0x0a, 0x1c, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, + 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x4f, 0x56, 0x45, 0x52, 0x56, 0x49, 0x45, 0x57, 0x10, 0x01, + 0x12, 0x26, 0x0a, 0x22, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, + 0x56, 0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x5f, 0x44, 0x49, 0x4d, + 0x45, 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, 0x45, 0x58, 0x50, 0x4c, + 0x4f, 0x52, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, + 0x50, 0x49, 0x56, 0x4f, 0x54, 0x10, 0x03, 0x12, 0x1e, 0x0a, 0x1a, 0x45, 0x58, 0x50, 0x4c, 0x4f, + 0x52, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x43, + 0x41, 0x4e, 0x56, 0x41, 0x53, 0x10, 0x04, 0x2a, 0x85, 0x01, 0x0a, 0x0f, 0x41, 0x73, 0x73, 0x65, + 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x1c, 0x41, + 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, + 0x15, 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, + 0x53, 0x5f, 0x50, 0x41, 0x53, 0x53, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x53, 0x53, 0x45, + 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x41, 0x49, + 0x4c, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, + 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x42, + 0xc1, 0x01, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x69, 0x6c, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x72, + 0x69, 0x6c, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x72, 0x69, + 0x6c, 0x6c, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x52, 0x52, 0x58, 0xaa, 0x02, 0x0f, + 0x52, 0x69, 0x6c, 0x6c, 0x2e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x56, 0x31, 0xca, + 0x02, 0x0f, 0x52, 0x69, 0x6c, 0x6c, 0x5c, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5c, 0x56, + 0x31, 0xe2, 0x02, 0x1b, 0x52, 0x69, 0x6c, 0x6c, 0x5c, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, + 0x02, 0x11, 0x52, 0x69, 0x6c, 0x6c, 0x3a, 0x3a, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x3a, + 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -9184,6 +9191,7 @@ func file_rill_runtime_v1_resources_proto_init() { (*SecurityRule_FieldAccess)(nil), (*SecurityRule_RowFilter)(nil), } + file_rill_runtime_v1_resources_proto_msgTypes[24].OneofWrappers = []any{} file_rill_runtime_v1_resources_proto_msgTypes[25].OneofWrappers = []any{ (*FieldSelector_All)(nil), (*FieldSelector_Fields)(nil), diff --git a/proto/gen/rill/runtime/v1/resources.pb.validate.go b/proto/gen/rill/runtime/v1/resources.pb.validate.go index d42883cb058..9f2c9f8b41c 100644 --- a/proto/gen/rill/runtime/v1/resources.pb.validate.go +++ b/proto/gen/rill/runtime/v1/resources.pb.validate.go @@ -4872,8 +4872,6 @@ func (m *ExplorePreset) validate(all bool) error { // no validation rules for ComparisonMode - // no validation rules for CompareTimeRange - // no validation rules for ComparisonDimension // no validation rules for View @@ -4894,6 +4892,10 @@ func (m *ExplorePreset) validate(all bool) error { // no validation rules for PivotSortAsc + if m.CompareTimeRange != nil { + // no validation rules for CompareTimeRange + } + if len(errors) > 0 { return ExplorePresetMultiError(errors) } diff --git a/proto/gen/rill/runtime/v1/runtime.swagger.yaml b/proto/gen/rill/runtime/v1/runtime.swagger.yaml index c2a957bfbbb..d07c8901172 100644 --- a/proto/gen/rill/runtime/v1/runtime.swagger.yaml +++ b/proto/gen/rill/runtime/v1/runtime.swagger.yaml @@ -4129,11 +4129,12 @@ definitions: v1ExploreWebView: type: string enum: + - EXPLORE_ACTIVE_PAGE_UNSPECIFIED - EXPLORE_ACTIVE_PAGE_OVERVIEW - EXPLORE_ACTIVE_PAGE_TIME_DIMENSION - EXPLORE_ACTIVE_PAGE_PIVOT - EXPLORE_ACTIVE_PAGE_CANVAS - default: EXPLORE_ACTIVE_PAGE_OVERVIEW + default: EXPLORE_ACTIVE_PAGE_UNSPECIFIED v1ExportFormat: type: string enum: diff --git a/proto/rill/runtime/v1/resources.proto b/proto/rill/runtime/v1/resources.proto index caf093897d1..4c85d143ac5 100644 --- a/proto/rill/runtime/v1/resources.proto +++ b/proto/rill/runtime/v1/resources.proto @@ -422,10 +422,11 @@ enum ExploreComparisonMode { } enum ExploreWebView { - EXPLORE_ACTIVE_PAGE_OVERVIEW = 0; - EXPLORE_ACTIVE_PAGE_TIME_DIMENSION = 1; - EXPLORE_ACTIVE_PAGE_PIVOT = 2; - EXPLORE_ACTIVE_PAGE_CANVAS = 3; + EXPLORE_ACTIVE_PAGE_UNSPECIFIED = 0; + EXPLORE_ACTIVE_PAGE_OVERVIEW = 1; + EXPLORE_ACTIVE_PAGE_TIME_DIMENSION = 2; + EXPLORE_ACTIVE_PAGE_PIVOT = 3; + EXPLORE_ACTIVE_PAGE_CANVAS = 4; } // FieldSelector describes logic for selecting a list of fields. diff --git a/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts b/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts index 4521e3d2502..131a16ef0d6 100644 --- a/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts +++ b/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts @@ -142,40 +142,26 @@ export function getDefaultMetricsExplorerEntity( explore: V1ExploreSpec, fullTimeRange: V1MetricsViewTimeRangeResponse | undefined, ): MetricsExplorerEntity { - // CAST SAFETY: safe b/c (1) measure.name is a string if defined, - // and (2) we filter out undefined values - const defaultMeasureNames = (metricsView?.measures - ?.map((measure) => measure?.name) - .filter((name) => name !== undefined) ?? []) as string[]; + const preset = explore.presets?.[0]; - // CAST SAFETY: safe b/c (1) measure.name is a string if defined, - // and (2) we filter out undefined values - const defaultDimNames = (metricsView?.dimensions - ?.map((dim) => dim.name) - .filter((name) => name !== undefined) ?? []) as string[]; + const defaultMeasureNames = preset?.measures ?? explore?.measures ?? []; + + const defaultDimNames = preset?.dimensions ?? explore?.dimensions ?? []; const metricsExplorer: MetricsExplorerEntity = { name, - visibleMeasureKeys: metricsView.defaultMeasures?.length - ? new Set( - metricsView.defaultMeasures - .map((dm) => normaliseName(dm, metricsView.measures)) - .filter((dm) => !!dm) as string[], - ) - : new Set(defaultMeasureNames), - allMeasuresVisible: - !metricsView.defaultMeasures?.length || - metricsView.defaultMeasures?.length === defaultMeasureNames.length, - visibleDimensionKeys: metricsView.defaultDimensions?.length - ? new Set( - metricsView.defaultDimensions - .map((dd) => normaliseName(dd, metricsView.dimensions)) - .filter((dd) => !!dd) as string[], - ) - : new Set(defaultDimNames), - allDimensionsVisible: - !metricsView.defaultDimensions?.length || - metricsView.defaultDimensions?.length === defaultDimNames.length, + visibleMeasureKeys: new Set( + defaultMeasureNames + .map((dm) => normaliseName(dm, metricsView.measures)) + .filter((dm) => !!dm) as string[], + ), + allMeasuresVisible: defaultMeasureNames.length === explore.measures?.length, + visibleDimensionKeys: new Set( + defaultDimNames + .map((dd) => normaliseName(dd, metricsView.dimensions)) + .filter((dd) => !!dd) as string[], + ), + allDimensionsVisible: defaultDimNames.length === explore.dimensions?.length, leaderboardMeasureName: defaultMeasureNames[0], whereFilter: createAndExpression([]), havingFilter: createAndExpression([]), diff --git a/web-common/src/features/dashboards/stores/test-data/data.ts b/web-common/src/features/dashboards/stores/test-data/data.ts index e268968ca1a..3ab6921a50f 100644 --- a/web-common/src/features/dashboards/stores/test-data/data.ts +++ b/web-common/src/features/dashboards/stores/test-data/data.ts @@ -181,6 +181,12 @@ export const AD_BIDS_METRICS_WITH_BOOL_DIMENSION: V1MetricsViewSpec = { }, ], }; +export const AD_BIDS_METRICS_3_MEASURES_DIMENSIONS: V1MetricsViewSpec = { + title: AD_BIDS_NAME, + table: AD_BIDS_SOURCE_NAME, + measures: AD_BIDS_THREE_MEASURES, + dimensions: AD_BIDS_THREE_DIMENSIONS, +}; export const AD_BIDS_EXPLORE_INIT: V1ExploreSpec = { title: AD_BIDS_EXPLORE_NAME, diff --git a/web-common/src/features/dashboards/url-state/defaults.ts b/web-common/src/features/dashboards/url-state/defaults.ts new file mode 100644 index 00000000000..bc1930beec5 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/defaults.ts @@ -0,0 +1,6 @@ +import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; +import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; + +export const URLStateDefaultTimezone = "UTC"; +export const URLStateDefaultSortDirection = SortDirection.DESCENDING; +export const URLStateDefaultTDDChartType = TDDChart.DEFAULT; diff --git a/web-common/src/features/dashboards/url-state/fromUrl.ts b/web-common/src/features/dashboards/url-state/fromUrl.ts index d24b89a6fa9..25704023190 100644 --- a/web-common/src/features/dashboards/url-state/fromUrl.ts +++ b/web-common/src/features/dashboards/url-state/fromUrl.ts @@ -14,14 +14,20 @@ import { TDDChart, TDDState, } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; +import { + URLStateDefaultSortDirection, + URLStateDefaultTimezone, +} from "@rilldata/web-common/features/dashboards/url-state/defaults"; import { convertFilterParamToExpression } from "@rilldata/web-common/features/dashboards/url-state/filters/converters"; -import { FromURLParamTimeDimensionMap } from "@rilldata/web-common/features/dashboards/url-state/mappers"; +import { + FromURLParamTDDChartMap, + FromURLParamTimeDimensionMap, + FromURLParamViewMap, + ToActivePageViewMap, +} from "@rilldata/web-common/features/dashboards/url-state/mappers"; import { getMapFromArray } from "@rilldata/web-common/lib/arrayUtils"; import { TIME_GRAIN } from "@rilldata/web-common/lib/time/config"; -import { - DashboardTimeControls, - TimeRangePreset, -} from "@rilldata/web-common/lib/time/types"; +import { DashboardTimeControls } from "@rilldata/web-common/lib/time/types"; import { MetricsViewSpecDimensionV2, MetricsViewSpecMeasureV2, @@ -37,13 +43,12 @@ export function getMetricsExplorerFromUrl( searchParams: URLSearchParams, metricsView: V1MetricsViewSpec, explore: V1ExploreSpec, + preset: V1ExplorePreset, ): { entity: Partial; errors: Error[] } { // TODO: replace this with V1ExplorePreset once it is available on main const entity: Partial = {}; const errors: Error[] = []; - const preset = explore.presets?.[0] ?? {}; - const measures = getMapFromArray( metricsView.measures?.filter((m) => explore.measures?.includes(m.name!)) ?? [], @@ -56,6 +61,14 @@ export function getMetricsExplorerFromUrl( (d) => d.name!, ); + if (searchParams.has("vw")) { + entity.activePage = Number( + ToActivePageViewMap[ + FromURLParamViewMap[searchParams.get("vw") as string] + ] ?? "0", + ); + } + if (searchParams.has("f")) { const { dimensionFilters, @@ -106,6 +119,8 @@ function fromTimeRangesParams( const timeZone = preset.timezone || searchParams.get("tz"); if (timeZone) { entity.selectedTimezone = timeZone; + } else { + entity.selectedTimezone = URLStateDefaultTimezone; } const comparisonTimeRange = @@ -148,17 +163,16 @@ function fromTimeRangeUrlParam(tr: string): { timeRange?: DashboardTimeControls; error?: Error; } { - if (tr in TimeRangePreset) { - return { - timeRange: { - name: tr, - } as DashboardTimeControls, - }; - } - + // TODO: validation return { - error: new Error(`unknown time range: ${tr}`), + timeRange: { + name: tr, + } as DashboardTimeControls, }; + + // return { + // error: new Error(`unknown time range: ${tr}`), + // }; } function fromOverviewUrlParams( @@ -171,8 +185,8 @@ function fromOverviewUrlParams( const entity: Partial = {}; let selectedMeasures = preset.measures ?? explore.measures ?? []; - if (searchParams.has("e.m")) { - const mes = searchParams.get("e.m") as string; + if (searchParams.has("o.m")) { + const mes = searchParams.get("o.m") as string; if (mes !== "*") { selectedMeasures = mes.split(",").filter((m) => measures.has(m)); } @@ -182,8 +196,8 @@ function fromOverviewUrlParams( entity.visibleMeasureKeys = new Set(selectedMeasures); let selectedDimensions = preset.dimensions ?? explore.dimensions ?? []; - if (searchParams.has("e.d")) { - const dims = searchParams.get("e.d") as string; + if (searchParams.has("o.d")) { + const dims = searchParams.get("o.d") as string; if (dims !== "*") { selectedDimensions = dims.split(",").filter((d) => dimensions.has(d)); } @@ -193,9 +207,9 @@ function fromOverviewUrlParams( entity.visibleDimensionKeys = new Set(selectedDimensions); entity.leaderboardMeasureName = - preset.overviewSortBy ?? explore.measures?.[0]; - if (searchParams.has("e.sb")) { - const sortBy = searchParams.get("e.sb") as string; + preset.overviewSortBy ?? preset.measures?.[0] ?? explore.measures?.[0]; + if (searchParams.has("o.sb")) { + const sortBy = searchParams.get("o.sb") as string; if (measures.has(sortBy)) { entity.leaderboardMeasureName = sortBy; } @@ -206,17 +220,17 @@ function fromOverviewUrlParams( ? SortDirection.ASCENDING : SortDirection.DESCENDING; } else { - entity.sortDirection = SortDirection.DESCENDING; + entity.sortDirection = URLStateDefaultSortDirection; } - if (searchParams.has("e.sd")) { - const sortDir = searchParams.get("e.sd") as string; + if (searchParams.has("o.sd")) { + const sortDir = searchParams.get("o.sd") as string; entity.sortDirection = sortDir === "ASC" ? SortDirection.ASCENDING : SortDirection.DESCENDING; } entity.selectedDimensionName = preset.overviewExpandedDimension ?? ""; - if (searchParams.has("e.ed")) { - const dim = searchParams.get("e.ed") as string; + if (searchParams.has("o.ed")) { + const dim = searchParams.get("o.ed") as string; if (dimensions.has(dim)) { entity.selectedDimensionName = dim; } @@ -242,8 +256,8 @@ function fromTimeDimensionUrlParams( } if (searchParams.has("tdd.ct")) { const ct = searchParams.get("tdd.ct") as string; - if (ct in TDDChart) { - ttdChartType = TDDChart[ct]; + if (ct in FromURLParamTDDChartMap) { + ttdChartType = FromURLParamTDDChartMap[ct]; } } if (searchParams.has("tdd.p")) { @@ -330,7 +344,7 @@ function fromPivotUrlParams( return { active: - searchParams.get("view") === "pivot" || + searchParams.get("vw") === "pivot" || preset.view === V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, rows: { dimension: rowDimensions, diff --git a/web-common/src/features/dashboards/url-state/mappers.ts b/web-common/src/features/dashboards/url-state/mappers.ts index 85bf83d035c..532f5db8d0c 100644 --- a/web-common/src/features/dashboards/url-state/mappers.ts +++ b/web-common/src/features/dashboards/url-state/mappers.ts @@ -1,4 +1,32 @@ -import { V1TimeGrain } from "@rilldata/web-common/runtime-client"; +import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; +import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; +import { + V1ExploreWebView, + V1TimeGrain, +} from "@rilldata/web-common/runtime-client"; + +export const FromURLParamViewMap: Record = { + overview: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW, + pivot: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, + time_dimension: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_TIME_DIMENSION, +}; +export const ToURLParamViewMap = reverseMap(FromURLParamViewMap); + +export const FromActivePageMap: Record< + DashboardState_ActivePage, + V1ExploreWebView +> = { + [DashboardState_ActivePage.UNSPECIFIED]: + V1ExploreWebView.EXPLORE_ACTIVE_PAGE_UNSPECIFIED, + [DashboardState_ActivePage.DEFAULT]: + V1ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW, + [DashboardState_ActivePage.PIVOT]: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, + [DashboardState_ActivePage.DIMENSION_TABLE]: + V1ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW, + [DashboardState_ActivePage.TIME_DIMENSIONAL_DETAIL]: + V1ExploreWebView.EXPLORE_ACTIVE_PAGE_TIME_DIMENSION, +}; +export const ToActivePageViewMap = reverseMap(FromActivePageMap); export const FromURLParamTimeDimensionMap: Record = { "time.hour": V1TimeGrain.TIME_GRAIN_HOUR, @@ -9,13 +37,21 @@ export const ToURLParamTimeDimensionMap = reverseMap( FromURLParamTimeDimensionMap, ); +export const FromURLParamTDDChartMap: Record = { + timeseries: TDDChart.DEFAULT, + bar: TDDChart.GROUPED_BAR, + stacked_bar: TDDChart.STACKED_BAR, + stacked_area: TDDChart.STACKED_AREA, +}; +export const ToURLParamTDDChartMap = reverseMap(FromURLParamTDDChartMap); + export function reverseMap< K extends string | number, V extends string | number, >(map: Partial>): Partial> { const revMap = {} as Partial>; for (const k in map) { - revMap[map[k] as string | number] = map[k]; + revMap[map[k] as string | number] = k; } return revMap; } diff --git a/web-common/src/features/dashboards/url-state/toUrl.ts b/web-common/src/features/dashboards/url-state/toUrl.ts index f82353b71f7..71c2ab194a5 100644 --- a/web-common/src/features/dashboards/url-state/toUrl.ts +++ b/web-common/src/features/dashboards/url-state/toUrl.ts @@ -5,16 +5,26 @@ import { } from "@rilldata/web-common/features/dashboards/pivot/types"; import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; -import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; +import { + URLStateDefaultSortDirection, + URLStateDefaultTDDChartType, + URLStateDefaultTimezone, +} from "@rilldata/web-common/features/dashboards/url-state/defaults"; import { convertExpressionToFilterParam } from "@rilldata/web-common/features/dashboards/url-state/filters/converters"; -import { ToURLParamTimeDimensionMap } from "@rilldata/web-common/features/dashboards/url-state/mappers"; +import { + FromActivePageMap, + ToURLParamTDDChartMap, + ToURLParamTimeDimensionMap, + ToURLParamViewMap, +} from "@rilldata/web-common/features/dashboards/url-state/mappers"; import { arrayOrderedEquals, arrayUnorderedEquals, } from "@rilldata/web-common/lib/arrayUtils"; -import type { +import { V1ExplorePreset, V1ExploreSpec, + V1ExploreWebView, } from "@rilldata/web-common/runtime-client"; export function getUrlFromMetricsExplorer( @@ -25,6 +35,14 @@ export function getUrlFromMetricsExplorer( ) { if (!metrics) return; + const currentView = FromActivePageMap[metrics.activePage]; + if ( + (preset.view !== undefined && preset.view !== currentView) || + currentView !== V1ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW + ) { + searchParams.set("vw", ToURLParamViewMap[currentView] as string); + } + const expr = mergeMeasureFilters(metrics); if (expr && expr?.cond?.exprs?.length) { searchParams.set("f", convertExpressionToFilterParam(expr)); @@ -50,7 +68,11 @@ function toTimeRangesUrl( ) { searchParams.set("tr", metrics.selectedTimeRange.name); } - if (metrics.selectedTimezone !== preset.timezone) { + if ( + (preset.timezone !== undefined && + metrics.selectedTimezone !== preset.timezone) || + metrics.selectedTimezone !== URLStateDefaultTimezone + ) { searchParams.set("tz", metrics.selectedTimezone); } @@ -88,20 +110,25 @@ function toOverviewUrl( } } + const defaultLeaderboardMeasure = + preset.measures?.[0] ?? explore.measures?.[0]; if ( // if sort by is defined then only set param if selected is not the same. (preset.overviewSortBy && metrics.leaderboardMeasureName !== preset.overviewSortBy) || - // else the default is the 1st measure in explore, so check that next - metrics.leaderboardMeasureName !== explore.measures?.[0] + // else the default is the 1st measure in preset or explore, so check that next + metrics.leaderboardMeasureName !== defaultLeaderboardMeasure ) { searchParams.set("o.sb", metrics.leaderboardMeasureName); } const sortAsc = metrics.sortDirection === SortDirection.ASCENDING; if ( - preset.overviewSortAsc === undefined || - preset.overviewSortAsc !== sortAsc + // if preset has a sort direction then only set if not the same + (preset.overviewSortAsc !== undefined && + preset.overviewSortAsc !== sortAsc) || + // else if the direction is not the default then set the param + metrics.sortDirection !== URLStateDefaultSortDirection ) { searchParams.set("o.sd", sortAsc ? "ASC" : "DESC"); } @@ -129,12 +156,16 @@ function toTimeDimensionUrlParams( if ( (preset.timeDimensionChartType !== undefined && metrics.tdd.chartType !== preset.timeDimensionChartType) || - metrics.tdd.chartType !== TDDChart.DEFAULT + metrics.tdd.chartType !== URLStateDefaultTDDChartType ) { - searchParams.set("tdd.p", metrics.tdd.chartType); + searchParams.set( + "tdd.ct", + ToURLParamTDDChartMap[metrics.tdd.chartType] ?? "", + ); } // TODO: pin + // TODO: what should be done when chartType is set but expandedMeasureName is not st } function toPivotUrlParams( @@ -151,7 +182,7 @@ function toPivotUrlParams( }; const rows = metrics.pivot.rows.dimension.map(mapPivotEntry); - if (arrayOrderedEquals(rows, preset.pivotRows ?? [])) { + if (!arrayOrderedEquals(rows, preset.pivotRows ?? [])) { searchParams.set("p.r", rows.join(",")); } @@ -159,7 +190,7 @@ function toPivotUrlParams( ...metrics.pivot.columns.dimension.map(mapPivotEntry), ...metrics.pivot.columns.measure.map(mapPivotEntry), ]; - if (arrayOrderedEquals(cols, preset.pivotCols ?? [])) { + if (!arrayOrderedEquals(cols, preset.pivotCols ?? [])) { searchParams.set("p.c", cols.join(",")); } diff --git a/web-common/src/features/dashboards/url-state/url-state-test-data.ts b/web-common/src/features/dashboards/url-state/url-state-test-data.ts new file mode 100644 index 00000000000..39ccccf2a6e --- /dev/null +++ b/web-common/src/features/dashboards/url-state/url-state-test-data.ts @@ -0,0 +1,40 @@ +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; + +export const URLStateTestMetricsExplorerEntity: Partial = + { + visibleMeasureKeys: new Set(["impressions", "bid_price"]), + allMeasuresVisible: true, + visibleDimensionKeys: new Set(["publisher", "domain"]), + allDimensionsVisible: true, + + selectedDimensionName: "", + selectedTimezone: "UTC", + sortDirection: 2, + + leaderboardMeasureName: "impressions", + + pivot: { + active: false, + activeCell: null, + columnPage: 1, + columns: { + dimension: [], + measure: [], + }, + enableComparison: false, + expanded: {}, + rowJoinType: "nest", + rowPage: 1, + rows: { + dimension: [], + }, + sorting: [], + }, + + tdd: { + chartType: TDDChart.DEFAULT, + expandedMeasureName: "", + pinIndex: -1, + }, + }; diff --git a/web-common/src/features/dashboards/url-state/url-state.spec.ts b/web-common/src/features/dashboards/url-state/url-state.spec.ts new file mode 100644 index 00000000000..93ed4dc75e0 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/url-state.spec.ts @@ -0,0 +1,199 @@ +import { PivotChipType } from "@rilldata/web-common/features/dashboards/pivot/types"; +import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; +import { getDefaultMetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; +import { + createAndExpression, + createInExpression, +} from "@rilldata/web-common/features/dashboards/stores/filter-utils"; +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { + AD_BIDS_BID_PRICE_MEASURE, + AD_BIDS_DOMAIN_DIMENSION, + AD_BIDS_EXPLORE_INIT, + AD_BIDS_IMPRESSIONS_MEASURE, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + AD_BIDS_NAME, + AD_BIDS_PUBLISHER_DIMENSION, +} from "@rilldata/web-common/features/dashboards/stores/test-data/data"; +import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; +import { getMetricsExplorerFromUrl } from "@rilldata/web-common/features/dashboards/url-state/fromUrl"; +import { getUrlFromMetricsExplorer } from "@rilldata/web-common/features/dashboards/url-state/toUrl"; +import { URLStateTestMetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/url-state/url-state-test-data"; +import { + DashboardTimeControls, + TimeRangePreset, +} from "@rilldata/web-common/lib/time/types"; +import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; +import { V1TimeGrain } from "@rilldata/web-common/runtime-client"; +import { describe, expect, it } from "vitest"; + +const NoPresetTestCases: { + title: string; + url: string; + entity: Partial; +}[] = [ + { + title: "filter", + url: "http://localhost/?f=%28publisher+IN+%28%27Yahoo%27%29%29", + entity: { + whereFilter: createAndExpression([ + createInExpression(AD_BIDS_PUBLISHER_DIMENSION, ["Yahoo"]), + ]), + dimensionThresholdFilters: [], + }, + }, + { + title: "time ranges", + url: "http://localhost/?tr=P4W&tz=Asia%2FKathmandu", + entity: { + selectedTimeRange: { + name: TimeRangePreset.LAST_4_WEEKS, + } as DashboardTimeControls, + selectedTimezone: "Asia/Kathmandu", + }, + }, + { + title: "partially visible measures/dimensions", + url: "http://localhost/?o.m=impressions&o.d=publisher", + entity: { + visibleMeasureKeys: new Set([AD_BIDS_IMPRESSIONS_MEASURE]), + allMeasuresVisible: false, + visibleDimensionKeys: new Set([AD_BIDS_PUBLISHER_DIMENSION]), + allDimensionsVisible: false, + }, + }, + { + title: "all measures/dimensions visible", + url: "http://localhost/", + entity: { + visibleMeasureKeys: new Set([ + AD_BIDS_IMPRESSIONS_MEASURE, + AD_BIDS_BID_PRICE_MEASURE, + ]), + allMeasuresVisible: true, + visibleDimensionKeys: new Set([ + AD_BIDS_PUBLISHER_DIMENSION, + AD_BIDS_DOMAIN_DIMENSION, + ]), + allDimensionsVisible: true, + }, + }, + { + title: "leaderboard sort measure different than default", + url: "http://localhost/?o.sb=bid_price&o.sd=ASC", + entity: { + leaderboardMeasureName: AD_BIDS_BID_PRICE_MEASURE, + sortDirection: SortDirection.ASCENDING, + }, + }, + { + title: "leaderboard sort measure same as default", + url: "http://localhost/", + entity: { + leaderboardMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, + sortDirection: SortDirection.DESCENDING, + }, + }, + { + title: "time dimensional details", + url: "http://localhost/?vw=time_dimension&tdd.m=impressions&tdd.ct=stacked_bar", + entity: { + activePage: DashboardState_ActivePage.TIME_DIMENSIONAL_DETAIL, + tdd: { + expandedMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, + chartType: TDDChart.STACKED_BAR, + pinIndex: -1, + }, + }, + }, + { + title: "pivot", + url: "http://localhost/?vw=pivot&p.r=publisher%2Ctime.hour&p.c=domain%2Ctime.day%2Cimpressions", + entity: { + activePage: DashboardState_ActivePage.PIVOT, + pivot: { + active: true, + rows: { + dimension: [ + { + id: AD_BIDS_PUBLISHER_DIMENSION, + type: PivotChipType.Dimension, + title: AD_BIDS_PUBLISHER_DIMENSION, + }, + { + id: V1TimeGrain.TIME_GRAIN_HOUR, + type: PivotChipType.Time, + title: "hour", + }, + ], + }, + columns: { + measure: [ + { + id: AD_BIDS_IMPRESSIONS_MEASURE, + type: PivotChipType.Measure, + title: AD_BIDS_IMPRESSIONS_MEASURE, + }, + ], + dimension: [ + { + id: AD_BIDS_DOMAIN_DIMENSION, + type: PivotChipType.Dimension, + title: AD_BIDS_DOMAIN_DIMENSION, + }, + { + id: V1TimeGrain.TIME_GRAIN_DAY, + type: PivotChipType.Time, + title: "day", + }, + ], + }, + expanded: {}, + sorting: [], + columnPage: 1, + rowPage: 1, + enableComparison: false, + activeCell: null, + rowJoinType: "nest", + }, + }, + }, +]; + +describe("Human readable URL state", () => { + describe("No preset", () => { + for (const { title, url, entity } of NoPresetTestCases) { + it(title, () => { + const u = new URL("http://localhost"); + const defaultEntity = { + ...getDefaultMetricsExplorerEntity( + AD_BIDS_NAME, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + AD_BIDS_EXPLORE_INIT, + undefined, + ), + ...entity, + }; + getUrlFromMetricsExplorer( + defaultEntity, + u.searchParams, + AD_BIDS_EXPLORE_INIT, + {}, + ); + + expect(u.toString()).toEqual(url); + + const { entity: actualEntity } = getMetricsExplorerFromUrl( + u.searchParams, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + AD_BIDS_EXPLORE_INIT, + {}, + ); + expect(actualEntity).toEqual({ + ...URLStateTestMetricsExplorerEntity, + ...entity, + }); + }); + } + }); +}); diff --git a/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts b/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts index 22ad8a5b61a..efff111eb16 100644 --- a/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts +++ b/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts @@ -80,31 +80,37 @@ proto3.util.setEnumType(ExploreComparisonMode, "rill.runtime.v1.ExploreCompariso */ export enum ExploreWebView { /** - * @generated from enum value: EXPLORE_ACTIVE_PAGE_OVERVIEW = 0; + * @generated from enum value: EXPLORE_ACTIVE_PAGE_UNSPECIFIED = 0; */ - EXPLORE_ACTIVE_PAGE_OVERVIEW = 0, + EXPLORE_ACTIVE_PAGE_UNSPECIFIED = 0, /** - * @generated from enum value: EXPLORE_ACTIVE_PAGE_TIME_DIMENSION = 1; + * @generated from enum value: EXPLORE_ACTIVE_PAGE_OVERVIEW = 1; */ - EXPLORE_ACTIVE_PAGE_TIME_DIMENSION = 1, + EXPLORE_ACTIVE_PAGE_OVERVIEW = 1, /** - * @generated from enum value: EXPLORE_ACTIVE_PAGE_PIVOT = 2; + * @generated from enum value: EXPLORE_ACTIVE_PAGE_TIME_DIMENSION = 2; */ - EXPLORE_ACTIVE_PAGE_PIVOT = 2, + EXPLORE_ACTIVE_PAGE_TIME_DIMENSION = 2, /** - * @generated from enum value: EXPLORE_ACTIVE_PAGE_CANVAS = 3; + * @generated from enum value: EXPLORE_ACTIVE_PAGE_PIVOT = 3; */ - EXPLORE_ACTIVE_PAGE_CANVAS = 3, + EXPLORE_ACTIVE_PAGE_PIVOT = 3, + + /** + * @generated from enum value: EXPLORE_ACTIVE_PAGE_CANVAS = 4; + */ + EXPLORE_ACTIVE_PAGE_CANVAS = 4, } // Retrieve enum metadata with: proto3.getEnumType(ExploreWebView) proto3.util.setEnumType(ExploreWebView, "rill.runtime.v1.ExploreWebView", [ - { no: 0, name: "EXPLORE_ACTIVE_PAGE_OVERVIEW" }, - { no: 1, name: "EXPLORE_ACTIVE_PAGE_TIME_DIMENSION" }, - { no: 2, name: "EXPLORE_ACTIVE_PAGE_PIVOT" }, - { no: 3, name: "EXPLORE_ACTIVE_PAGE_CANVAS" }, + { no: 0, name: "EXPLORE_ACTIVE_PAGE_UNSPECIFIED" }, + { no: 1, name: "EXPLORE_ACTIVE_PAGE_OVERVIEW" }, + { no: 2, name: "EXPLORE_ACTIVE_PAGE_TIME_DIMENSION" }, + { no: 3, name: "EXPLORE_ACTIVE_PAGE_PIVOT" }, + { no: 4, name: "EXPLORE_ACTIVE_PAGE_CANVAS" }, ]); /** @@ -2415,9 +2421,9 @@ export class ExplorePreset extends Message { comparisonMode = ExploreComparisonMode.UNSPECIFIED; /** - * @generated from field: string compare_time_range = 13; + * @generated from field: optional string compare_time_range = 13; */ - compareTimeRange = ""; + compareTimeRange?: string; /** * If comparison_mode is EXPLORE_COMPARISON_MODE_DIMENSION, this indicates the dimension to use. @@ -2429,7 +2435,7 @@ export class ExplorePreset extends Message { /** * @generated from field: rill.runtime.v1.ExploreWebView view = 14; */ - view = ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW; + view = ExploreWebView.EXPLORE_ACTIVE_PAGE_UNSPECIFIED; /** * @generated from field: string overview_sort_by = 15; @@ -2498,7 +2504,7 @@ export class ExplorePreset extends Message { { no: 11, name: "timezone", kind: "scalar", T: 9 /* ScalarType.STRING */ }, { no: 12, name: "time_grain", kind: "scalar", T: 9 /* ScalarType.STRING */ }, { no: 7, name: "comparison_mode", kind: "enum", T: proto3.getEnumType(ExploreComparisonMode) }, - { no: 13, name: "compare_time_range", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 13, name: "compare_time_range", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, { no: 8, name: "comparison_dimension", kind: "scalar", T: 9 /* ScalarType.STRING */ }, { no: 14, name: "view", kind: "enum", T: proto3.getEnumType(ExploreWebView) }, { no: 15, name: "overview_sort_by", kind: "scalar", T: 9 /* ScalarType.STRING */ }, diff --git a/web-common/src/runtime-client/gen/index.schemas.ts b/web-common/src/runtime-client/gen/index.schemas.ts index b317af0bca2..e4c0c708e69 100644 --- a/web-common/src/runtime-client/gen/index.schemas.ts +++ b/web-common/src/runtime-client/gen/index.schemas.ts @@ -1812,6 +1812,7 @@ export type V1ExploreWebView = // eslint-disable-next-line @typescript-eslint/no-redeclare export const V1ExploreWebView = { + EXPLORE_ACTIVE_PAGE_UNSPECIFIED: "EXPLORE_ACTIVE_PAGE_UNSPECIFIED", EXPLORE_ACTIVE_PAGE_OVERVIEW: "EXPLORE_ACTIVE_PAGE_OVERVIEW", EXPLORE_ACTIVE_PAGE_TIME_DIMENSION: "EXPLORE_ACTIVE_PAGE_TIME_DIMENSION", EXPLORE_ACTIVE_PAGE_PIVOT: "EXPLORE_ACTIVE_PAGE_PIVOT", From 5fbb31a0ef1943085571f9729e88217ce1d1e570 Mon Sep 17 00:00:00 2001 From: Aditya Hegde Date: Tue, 15 Oct 2024 11:06:54 +0530 Subject: [PATCH 06/17] Add tests for presets --- .../url-state/filters/converters.ts | 5 +- .../url-state/filters/expression.spec.ts | 2 +- .../features/dashboards/url-state/fromUrl.ts | 72 ++- .../features/dashboards/url-state/mappers.ts | 2 + .../features/dashboards/url-state/toUrl.ts | 50 +- .../dashboards/url-state/url-state.spec.ts | 510 +++++++++++++----- 6 files changed, 475 insertions(+), 166 deletions(-) diff --git a/web-common/src/features/dashboards/url-state/filters/converters.ts b/web-common/src/features/dashboards/url-state/filters/converters.ts index bd3ddf9a7fb..4e36668bbcd 100644 --- a/web-common/src/features/dashboards/url-state/filters/converters.ts +++ b/web-common/src/features/dashboards/url-state/filters/converters.ts @@ -1,5 +1,8 @@ import { BinaryOperationReverseMap } from "@rilldata/web-common/features/dashboards/url-state/filters/post-processors"; -import { V1Expression, V1Operation } from "@rilldata/web-common/runtime-client"; +import { + type V1Expression, + V1Operation, +} from "@rilldata/web-common/runtime-client"; import grammar from "./expression.cjs"; import nearley from "nearley"; diff --git a/web-common/src/features/dashboards/url-state/filters/expression.spec.ts b/web-common/src/features/dashboards/url-state/filters/expression.spec.ts index 0bf7c3a10e0..945180c3ef7 100644 --- a/web-common/src/features/dashboards/url-state/filters/expression.spec.ts +++ b/web-common/src/features/dashboards/url-state/filters/expression.spec.ts @@ -77,7 +77,7 @@ describe("expression", () => { // assert that there is only match. this ensures unambiguous grammar. expect(parser.results).length(1); - expect(convertFilterParamToExpression(expr)).toEqual( + expect(convertFilterParamToExpression(expr)).to.deep.eq( expected_expression, ); }); diff --git a/web-common/src/features/dashboards/url-state/fromUrl.ts b/web-common/src/features/dashboards/url-state/fromUrl.ts index 25704023190..c1bfe69d4b2 100644 --- a/web-common/src/features/dashboards/url-state/fromUrl.ts +++ b/web-common/src/features/dashboards/url-state/fromUrl.ts @@ -1,8 +1,8 @@ import { splitWhereFilter } from "@rilldata/web-common/features/dashboards/filters/measure-filters/measure-filter-utils"; import { - PivotChipData, + type PivotChipData, PivotChipType, - PivotState, + type PivotState, } from "@rilldata/web-common/features/dashboards/pivot/types"; import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; import { createAndExpression } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; @@ -12,7 +12,7 @@ import type { } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { TDDChart, - TDDState, + type TDDState, } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; import { URLStateDefaultSortDirection, @@ -27,15 +27,16 @@ import { } from "@rilldata/web-common/features/dashboards/url-state/mappers"; import { getMapFromArray } from "@rilldata/web-common/lib/arrayUtils"; import { TIME_GRAIN } from "@rilldata/web-common/lib/time/config"; -import { DashboardTimeControls } from "@rilldata/web-common/lib/time/types"; +import type { DashboardTimeControls } from "@rilldata/web-common/lib/time/types"; +import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; import { - MetricsViewSpecDimensionV2, - MetricsViewSpecMeasureV2, + type MetricsViewSpecDimensionV2, + type MetricsViewSpecMeasureV2, type V1ExplorePreset, - V1ExploreSpec, + type V1ExploreSpec, V1ExploreWebView, - V1Expression, - V1MetricsViewSpec, + type V1Expression, + type V1MetricsViewSpec, V1Operation, } from "@rilldata/web-common/runtime-client"; @@ -61,12 +62,10 @@ export function getMetricsExplorerFromUrl( (d) => d.name!, ); - if (searchParams.has("vw")) { - entity.activePage = Number( - ToActivePageViewMap[ - FromURLParamViewMap[searchParams.get("vw") as string] - ] ?? "0", - ); + const view = + FromURLParamViewMap[searchParams.get("vw") as string] || preset.view; + if (view) { + entity.activePage = Number(ToActivePageViewMap[view] ?? "0"); } if (searchParams.has("f")) { @@ -109,14 +108,14 @@ function fromTimeRangesParams( const entity: Partial = {}; const errors: Error[] = []; - const timeRange = preset.timeRange || searchParams.get("tr"); + const timeRange = searchParams.get("tr") || preset.timeRange; if (timeRange) { const { timeRange: selectedTimeRange, error } = fromTimeRangeUrlParam(timeRange); if (error) errors.push(error); entity.selectedTimeRange = selectedTimeRange; } - const timeZone = preset.timezone || searchParams.get("tz"); + const timeZone = searchParams.get("tz") || preset.timezone; if (timeZone) { entity.selectedTimezone = timeZone; } else { @@ -124,18 +123,20 @@ function fromTimeRangesParams( } const comparisonTimeRange = - preset.compareTimeRange || searchParams.get("ctr"); + searchParams.get("ctr") || preset.compareTimeRange; if (comparisonTimeRange) { const { timeRange, error } = fromTimeRangeUrlParam(comparisonTimeRange); if (error) errors.push(error); entity.selectedComparisonTimeRange = timeRange; } const comparisonDimension = - preset.comparisonDimension || searchParams.get("cd"); + searchParams.get("cd") || preset.comparisonDimension; if (comparisonDimension && dimensions.has(comparisonDimension)) { entity.selectedComparisonDimension = comparisonDimension; } + // TODO: grain + return { entity, errors }; } @@ -189,6 +190,8 @@ function fromOverviewUrlParams( const mes = searchParams.get("o.m") as string; if (mes !== "*") { selectedMeasures = mes.split(",").filter((m) => measures.has(m)); + } else if (explore.measures) { + selectedMeasures = explore.measures; } } entity.allMeasuresVisible = @@ -200,6 +203,8 @@ function fromOverviewUrlParams( const dims = searchParams.get("o.d") as string; if (dims !== "*") { selectedDimensions = dims.split(",").filter((d) => dimensions.has(d)); + } else if (explore.dimensions) { + selectedDimensions = explore.dimensions; } } entity.allDimensionsVisible = @@ -228,12 +233,26 @@ function fromOverviewUrlParams( sortDir === "ASC" ? SortDirection.ASCENDING : SortDirection.DESCENDING; } - entity.selectedDimensionName = preset.overviewExpandedDimension ?? ""; if (searchParams.has("o.ed")) { const dim = searchParams.get("o.ed") as string; - if (dimensions.has(dim)) { + if ( + dimensions.has(dim) || + // we are unsetting from a default preset + dim === "" + ) { entity.selectedDimensionName = dim; + if (dim) { + entity.activePage = DashboardState_ActivePage.DIMENSION_TABLE; + } else { + // we are un-setting the dimension, so change active page as well + entity.activePage = DashboardState_ActivePage.DEFAULT; + } } + } else if (preset.overviewExpandedDimension) { + entity.selectedDimensionName = preset.overviewExpandedDimension; + entity.activePage = DashboardState_ActivePage.DIMENSION_TABLE; + } else { + entity.selectedDimensionName = ""; } return entity; @@ -250,7 +269,11 @@ function fromTimeDimensionUrlParams( if (searchParams.has("tdd.m")) { const mes = searchParams.get("tdd.m") as string; - if (measures.has(mes)) { + if ( + measures.has(mes) || + // we are unsetting from a default preset + mes === "" + ) { ttdMeasure = mes; } } @@ -344,8 +367,9 @@ function fromPivotUrlParams( return { active: - searchParams.get("vw") === "pivot" || - preset.view === V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, + (searchParams.has("vw") && searchParams.get("vw") === "pivot") || + (!searchParams.has("vw") && + preset.view === V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT), rows: { dimension: rowDimensions, }, diff --git a/web-common/src/features/dashboards/url-state/mappers.ts b/web-common/src/features/dashboards/url-state/mappers.ts index 532f5db8d0c..1396ceb1a29 100644 --- a/web-common/src/features/dashboards/url-state/mappers.ts +++ b/web-common/src/features/dashboards/url-state/mappers.ts @@ -27,6 +27,8 @@ export const FromActivePageMap: Record< V1ExploreWebView.EXPLORE_ACTIVE_PAGE_TIME_DIMENSION, }; export const ToActivePageViewMap = reverseMap(FromActivePageMap); +ToActivePageViewMap[V1ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW] = + DashboardState_ActivePage.DEFAULT; export const FromURLParamTimeDimensionMap: Record = { "time.hour": V1TimeGrain.TIME_GRAIN_HOUR, diff --git a/web-common/src/features/dashboards/url-state/toUrl.ts b/web-common/src/features/dashboards/url-state/toUrl.ts index 71c2ab194a5..722f020d010 100644 --- a/web-common/src/features/dashboards/url-state/toUrl.ts +++ b/web-common/src/features/dashboards/url-state/toUrl.ts @@ -1,6 +1,6 @@ import { mergeMeasureFilters } from "@rilldata/web-common/features/dashboards/filters/measure-filters/measure-filter-utils"; import { - PivotChipData, + type PivotChipData, PivotChipType, } from "@rilldata/web-common/features/dashboards/pivot/types"; import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; @@ -22,8 +22,8 @@ import { arrayUnorderedEquals, } from "@rilldata/web-common/lib/arrayUtils"; import { - V1ExplorePreset, - V1ExploreSpec, + type V1ExplorePreset, + type V1ExploreSpec, V1ExploreWebView, } from "@rilldata/web-common/runtime-client"; @@ -38,7 +38,8 @@ export function getUrlFromMetricsExplorer( const currentView = FromActivePageMap[metrics.activePage]; if ( (preset.view !== undefined && preset.view !== currentView) || - currentView !== V1ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW + (preset.view === undefined && + currentView !== V1ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW) ) { searchParams.set("vw", ToURLParamViewMap[currentView] as string); } @@ -69,9 +70,12 @@ function toTimeRangesUrl( searchParams.set("tr", metrics.selectedTimeRange.name); } if ( + // if preset has timezone then only set if selected is not the same (preset.timezone !== undefined && metrics.selectedTimezone !== preset.timezone) || - metrics.selectedTimezone !== URLStateDefaultTimezone + // else if the timezone is not the default then set the param + (preset.timezone === undefined && + metrics.selectedTimezone !== URLStateDefaultTimezone) ) { searchParams.set("tz", metrics.selectedTimezone); } @@ -82,6 +86,8 @@ function toTimeRangesUrl( if (metrics.selectedComparisonDimension !== preset.comparisonDimension) { searchParams.set("cd", metrics.selectedComparisonDimension ?? ""); } + + // TODO: grain } function toOverviewUrl( @@ -113,11 +119,12 @@ function toOverviewUrl( const defaultLeaderboardMeasure = preset.measures?.[0] ?? explore.measures?.[0]; if ( - // if sort by is defined then only set param if selected is not the same. + // if sort by is defined in preset then only set param if selected is not the same. (preset.overviewSortBy && metrics.leaderboardMeasureName !== preset.overviewSortBy) || // else the default is the 1st measure in preset or explore, so check that next - metrics.leaderboardMeasureName !== defaultLeaderboardMeasure + (!preset.overviewSortBy && + metrics.leaderboardMeasureName !== defaultLeaderboardMeasure) ) { searchParams.set("o.sb", metrics.leaderboardMeasureName); } @@ -128,16 +135,19 @@ function toOverviewUrl( (preset.overviewSortAsc !== undefined && preset.overviewSortAsc !== sortAsc) || // else if the direction is not the default then set the param - metrics.sortDirection !== URLStateDefaultSortDirection + (preset.overviewSortAsc === undefined && + metrics.sortDirection !== URLStateDefaultSortDirection) ) { searchParams.set("o.sd", sortAsc ? "ASC" : "DESC"); } if ( - metrics.selectedDimensionName && - metrics.selectedDimensionName !== preset.overviewExpandedDimension + (preset.overviewExpandedDimension !== undefined && + metrics.selectedDimensionName !== preset.overviewExpandedDimension) || + (preset.overviewExpandedDimension === undefined && + metrics.selectedDimensionName) ) { - searchParams.set("o.ed", metrics.selectedDimensionName); + searchParams.set("o.ed", metrics.selectedDimensionName ?? ""); } } @@ -147,16 +157,20 @@ function toTimeDimensionUrlParams( preset: V1ExplorePreset, ) { if ( - metrics.tdd.expandedMeasureName && - metrics.tdd.expandedMeasureName !== preset.timeDimensionMeasure + (preset.timeDimensionMeasure !== undefined && + metrics.tdd.expandedMeasureName !== preset.timeDimensionMeasure) || + (preset.timeDimensionMeasure === undefined && + metrics.tdd.expandedMeasureName) ) { - searchParams.set("tdd.m", metrics.tdd.expandedMeasureName); + searchParams.set("tdd.m", metrics.tdd.expandedMeasureName ?? ""); } if ( (preset.timeDimensionChartType !== undefined && - metrics.tdd.chartType !== preset.timeDimensionChartType) || - metrics.tdd.chartType !== URLStateDefaultTDDChartType + ToURLParamTDDChartMap[metrics.tdd.chartType] !== + preset.timeDimensionChartType) || + (preset.timeDimensionChartType === undefined && + metrics.tdd.chartType !== URLStateDefaultTDDChartType) ) { searchParams.set( "tdd.ct", @@ -165,7 +179,7 @@ function toTimeDimensionUrlParams( } // TODO: pin - // TODO: what should be done when chartType is set but expandedMeasureName is not st + // TODO: what should be done when chartType is set but expandedMeasureName is not } function toPivotUrlParams( @@ -173,8 +187,6 @@ function toPivotUrlParams( searchParams: URLSearchParams, preset: V1ExplorePreset, ) { - if (!metrics.pivot.active) return; - const mapPivotEntry = (data: PivotChipData) => { if (data.type === PivotChipType.Time) return ToURLParamTimeDimensionMap[data.id] as string; diff --git a/web-common/src/features/dashboards/url-state/url-state.spec.ts b/web-common/src/features/dashboards/url-state/url-state.spec.ts index 93ed4dc75e0..c33ee34de1b 100644 --- a/web-common/src/features/dashboards/url-state/url-state.spec.ts +++ b/web-common/src/features/dashboards/url-state/url-state.spec.ts @@ -20,96 +20,304 @@ import { getMetricsExplorerFromUrl } from "@rilldata/web-common/features/dashboa import { getUrlFromMetricsExplorer } from "@rilldata/web-common/features/dashboards/url-state/toUrl"; import { URLStateTestMetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/url-state/url-state-test-data"; import { - DashboardTimeControls, + type DashboardTimeControls, TimeRangePreset, } from "@rilldata/web-common/lib/time/types"; import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; -import { V1TimeGrain } from "@rilldata/web-common/runtime-client"; +import { + type V1ExplorePreset, + type V1ExploreSpec, + V1ExploreWebView, + V1TimeGrain, +} from "@rilldata/web-common/runtime-client"; import { describe, expect, it } from "vitest"; -const NoPresetTestCases: { - title: string; - url: string; - entity: Partial; -}[] = [ - { - title: "filter", - url: "http://localhost/?f=%28publisher+IN+%28%27Yahoo%27%29%29", - entity: { - whereFilter: createAndExpression([ - createInExpression(AD_BIDS_PUBLISHER_DIMENSION, ["Yahoo"]), - ]), - dimensionThresholdFilters: [], - }, - }, - { - title: "time ranges", - url: "http://localhost/?tr=P4W&tz=Asia%2FKathmandu", - entity: { - selectedTimeRange: { - name: TimeRangePreset.LAST_4_WEEKS, - } as DashboardTimeControls, - selectedTimezone: "Asia/Kathmandu", - }, - }, - { - title: "partially visible measures/dimensions", - url: "http://localhost/?o.m=impressions&o.d=publisher", - entity: { - visibleMeasureKeys: new Set([AD_BIDS_IMPRESSIONS_MEASURE]), - allMeasuresVisible: false, - visibleDimensionKeys: new Set([AD_BIDS_PUBLISHER_DIMENSION]), - allDimensionsVisible: false, - }, - }, - { - title: "all measures/dimensions visible", - url: "http://localhost/", - entity: { - visibleMeasureKeys: new Set([ - AD_BIDS_IMPRESSIONS_MEASURE, - AD_BIDS_BID_PRICE_MEASURE, - ]), - allMeasuresVisible: true, - visibleDimensionKeys: new Set([ - AD_BIDS_PUBLISHER_DIMENSION, - AD_BIDS_DOMAIN_DIMENSION, - ]), - allDimensionsVisible: true, - }, - }, - { - title: "leaderboard sort measure different than default", - url: "http://localhost/?o.sb=bid_price&o.sd=ASC", - entity: { - leaderboardMeasureName: AD_BIDS_BID_PRICE_MEASURE, - sortDirection: SortDirection.ASCENDING, - }, - }, - { - title: "leaderboard sort measure same as default", - url: "http://localhost/", - entity: { - leaderboardMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, - sortDirection: SortDirection.DESCENDING, - }, - }, - { - title: "time dimensional details", - url: "http://localhost/?vw=time_dimension&tdd.m=impressions&tdd.ct=stacked_bar", - entity: { - activePage: DashboardState_ActivePage.TIME_DIMENSIONAL_DETAIL, - tdd: { - expandedMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, - chartType: TDDChart.STACKED_BAR, - pinIndex: -1, +describe("Human readable URL state", () => { + it("filter", () => { + testEntity( + { + whereFilter: createAndExpression([ + createInExpression(AD_BIDS_PUBLISHER_DIMENSION, ["Yahoo"]), + ]), + dimensionThresholdFilters: [], }, - }, - }, - { - title: "pivot", - url: "http://localhost/?vw=pivot&p.r=publisher%2Ctime.hour&p.c=domain%2Ctime.day%2Cimpressions", - entity: { + "http://localhost/?f=%28publisher+IN+%28%27Yahoo%27%29%29", + ); + }); + + describe("Time ranges", () => { + it("no preset", () => { + testEntity( + { + selectedTimeRange: { + name: TimeRangePreset.LAST_4_WEEKS, + } as DashboardTimeControls, + selectedTimezone: "Asia/Kathmandu", + }, + "http://localhost/?tr=P4W&tz=Asia%2FKathmandu", + ); + }); + + it("with preset and matching preset", () => { + testEntity( + { + selectedTimeRange: { + name: TimeRangePreset.LAST_7_DAYS, + } as DashboardTimeControls, + selectedTimezone: "Asia/Kathmandu", + }, + "http://localhost/", + { + timeRange: "P7D", + timezone: "Asia/Kathmandu", + }, + ); + }); + + it("with preset and not matching preset", () => { + testEntity( + { + selectedTimeRange: { + name: TimeRangePreset.LAST_4_WEEKS, + } as DashboardTimeControls, + selectedTimezone: "America/Los_Angeles", + }, + "http://localhost/?tr=P4W&tz=America%2FLos_Angeles", + { + timeRange: "P7D", + timezone: "Asia/Kathmandu", + }, + ); + }); + }); + + describe("measures/dimensions visibility", () => { + it("no preset and partially visible measures/dimensions", () => { + testEntity( + { + visibleMeasureKeys: new Set([AD_BIDS_IMPRESSIONS_MEASURE]), + allMeasuresVisible: false, + visibleDimensionKeys: new Set([AD_BIDS_PUBLISHER_DIMENSION]), + allDimensionsVisible: false, + }, + "http://localhost/?o.m=impressions&o.d=publisher", + ); + }); + + it("no preset and all measures/dimensions visible", () => { + testEntity( + { + visibleMeasureKeys: new Set([ + AD_BIDS_IMPRESSIONS_MEASURE, + AD_BIDS_BID_PRICE_MEASURE, + ]), + allMeasuresVisible: true, + visibleDimensionKeys: new Set([ + AD_BIDS_PUBLISHER_DIMENSION, + AD_BIDS_DOMAIN_DIMENSION, + ]), + allDimensionsVisible: true, + }, + "http://localhost/", + ); + }); + + it("with preset and partially visible measures/dimensions, matching preset", () => { + testEntity( + { + visibleMeasureKeys: new Set([AD_BIDS_IMPRESSIONS_MEASURE]), + allMeasuresVisible: false, + visibleDimensionKeys: new Set([AD_BIDS_PUBLISHER_DIMENSION]), + allDimensionsVisible: false, + }, + "http://localhost/", + { + measures: [AD_BIDS_IMPRESSIONS_MEASURE], + dimensions: [AD_BIDS_PUBLISHER_DIMENSION], + }, + ); + }); + + it("with preset and all measures/dimensions visible, not matching preset", () => { + testEntity( + { + visibleMeasureKeys: new Set([ + AD_BIDS_IMPRESSIONS_MEASURE, + AD_BIDS_BID_PRICE_MEASURE, + ]), + allMeasuresVisible: true, + visibleDimensionKeys: new Set([ + AD_BIDS_PUBLISHER_DIMENSION, + AD_BIDS_DOMAIN_DIMENSION, + ]), + allDimensionsVisible: true, + }, + "http://localhost/?o.m=*&o.d=*", + { + measures: [AD_BIDS_IMPRESSIONS_MEASURE], + dimensions: [AD_BIDS_PUBLISHER_DIMENSION], + }, + ); + }); + }); + + describe("leaderboard configs", () => { + it("no preset and leaderboard sort measure different than default", () => { + testEntity( + { + leaderboardMeasureName: AD_BIDS_BID_PRICE_MEASURE, + sortDirection: SortDirection.ASCENDING, + }, + "http://localhost/?o.sb=bid_price&o.sd=ASC", + ); + }); + + it("no preset and leaderboard sort measure same as default", () => { + testEntity( + { + leaderboardMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, + sortDirection: SortDirection.DESCENDING, + }, + "http://localhost/", + ); + }); + + it("with preset and leaderboard sort measure same as preset", () => { + testEntity( + { + leaderboardMeasureName: AD_BIDS_BID_PRICE_MEASURE, + sortDirection: SortDirection.ASCENDING, + }, + "http://localhost/", + { + overviewSortBy: AD_BIDS_BID_PRICE_MEASURE, + overviewSortAsc: true, + }, + ); + }); + + it("with preset and leaderboard sort measure different than preset", () => { + testEntity( + { + leaderboardMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, + sortDirection: SortDirection.DESCENDING, + }, + "http://localhost/?o.sb=impressions&o.sd=DESC", + { + overviewSortBy: AD_BIDS_BID_PRICE_MEASURE, + overviewSortAsc: true, + }, + ); + }); + }); + + describe("dimension table", () => { + it("no preset and with dimension table active", () => { + testEntity( + { + activePage: DashboardState_ActivePage.DIMENSION_TABLE, + selectedDimensionName: AD_BIDS_PUBLISHER_DIMENSION, + }, + "http://localhost/?o.ed=publisher", + ); + }); + + it("with preset and with dimension table same as preset", () => { + testEntity( + { + activePage: DashboardState_ActivePage.DIMENSION_TABLE, + selectedDimensionName: AD_BIDS_DOMAIN_DIMENSION, + }, + "http://localhost/", + { + overviewExpandedDimension: AD_BIDS_DOMAIN_DIMENSION, + }, + ); + }); + + it("with preset and with dimension table different than preset", () => { + testEntity( + { + activePage: DashboardState_ActivePage.DIMENSION_TABLE, + selectedDimensionName: AD_BIDS_PUBLISHER_DIMENSION, + }, + "http://localhost/?o.ed=publisher", + { + overviewExpandedDimension: AD_BIDS_DOMAIN_DIMENSION, + }, + ); + }); + + it("with preset and with no dimension table different than preset", () => { + testEntity( + { + activePage: DashboardState_ActivePage.DEFAULT, + selectedDimensionName: "", + }, + "http://localhost/?o.ed=", + { + overviewExpandedDimension: AD_BIDS_DOMAIN_DIMENSION, + }, + ); + }); + }); + + describe("time dimensional details", () => { + it("no preset and has time dimensional details", () => { + testEntity( + { + activePage: DashboardState_ActivePage.TIME_DIMENSIONAL_DETAIL, + tdd: { + expandedMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, + chartType: TDDChart.STACKED_BAR, + pinIndex: -1, + }, + }, + "http://localhost/?vw=time_dimension&tdd.m=impressions&tdd.ct=stacked_bar", + ); + }); + + it("with preset and has time dimensional details same as presets", () => { + testEntity( + { + activePage: DashboardState_ActivePage.TIME_DIMENSIONAL_DETAIL, + tdd: { + expandedMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, + chartType: TDDChart.STACKED_BAR, + pinIndex: -1, + }, + }, + "http://localhost/", + { + view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_TIME_DIMENSION, + timeDimensionMeasure: AD_BIDS_IMPRESSIONS_MEASURE, + timeDimensionChartType: "stacked_bar", + }, + ); + }); + + it("with preset and has time dimensional details different than presets", () => { + testEntity( + { + activePage: DashboardState_ActivePage.DEFAULT, + tdd: { + expandedMeasureName: "", + chartType: TDDChart.DEFAULT, + pinIndex: -1, + }, + }, + "http://localhost/?vw=overview&tdd.m=&tdd.ct=timeseries", + { + view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_TIME_DIMENSION, + timeDimensionMeasure: AD_BIDS_IMPRESSIONS_MEASURE, + timeDimensionChartType: "stacked_bar", + }, + ); + }); + }); + + describe("pivot", () => { + const PIVOT_ENTITY: Partial = { activePage: DashboardState_ActivePage.PIVOT, pivot: { active: true, @@ -156,44 +364,104 @@ const NoPresetTestCases: { activeCell: null, rowJoinType: "nest", }, - }, - }, -]; + }; -describe("Human readable URL state", () => { - describe("No preset", () => { - for (const { title, url, entity } of NoPresetTestCases) { - it(title, () => { - const u = new URL("http://localhost"); - const defaultEntity = { - ...getDefaultMetricsExplorerEntity( - AD_BIDS_NAME, - AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, - AD_BIDS_EXPLORE_INIT, - undefined, - ), - ...entity, - }; - getUrlFromMetricsExplorer( - defaultEntity, - u.searchParams, - AD_BIDS_EXPLORE_INIT, - {}, - ); - - expect(u.toString()).toEqual(url); - - const { entity: actualEntity } = getMetricsExplorerFromUrl( - u.searchParams, - AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, - AD_BIDS_EXPLORE_INIT, - {}, - ); - expect(actualEntity).toEqual({ - ...URLStateTestMetricsExplorerEntity, - ...entity, - }); + it("no preset with pivot", () => { + testEntity( + PIVOT_ENTITY, + "http://localhost/?vw=pivot&p.r=publisher%2Ctime.hour&p.c=domain%2Ctime.day%2Cimpressions", + ); + }); + + it("with preset and pivot same as preset", () => { + testEntity(PIVOT_ENTITY, "http://localhost/", { + view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, + pivotRows: ["publisher", "time.hour"], + pivotCols: ["domain", "time.day", "impressions"], }); - } + }); + + it("with preset and pivot different as preset", () => { + testEntity( + PIVOT_ENTITY, + "http://localhost/?p.r=publisher%2Ctime.hour&p.c=domain%2Ctime.day%2Cimpressions", + { + view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, + pivotRows: ["domain", "time.day"], + pivotCols: ["impressions"], + }, + ); + }); + + it("with preset and no pivot, different as preset", () => { + testEntity( + { + activePage: DashboardState_ActivePage.DEFAULT, + pivot: { + active: false, + rows: { + dimension: [], + }, + columns: { + measure: [], + dimension: [], + }, + expanded: {}, + sorting: [], + columnPage: 1, + rowPage: 1, + enableComparison: false, + activeCell: null, + rowJoinType: "nest", + }, + }, + "http://localhost/?vw=overview&p.r=&p.c=", + { + view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, + pivotRows: ["domain", "time.day"], + pivotCols: ["impressions"], + }, + ); + }); }); }); + +function testEntity( + entity: Partial, + expectedUrl: string, + preset?: V1ExplorePreset, +) { + const url = new URL("http://localhost"); + const explore: V1ExploreSpec = { + ...AD_BIDS_EXPLORE_INIT, + ...(preset ? { defaultPreset: preset } : {}), + }; + const defaultEntity = { + ...getDefaultMetricsExplorerEntity( + AD_BIDS_NAME, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + explore, + undefined, + ), + ...entity, + }; + getUrlFromMetricsExplorer( + defaultEntity, + url.searchParams, + explore, + preset ?? {}, + ); + + expect(url.toString()).to.eq(expectedUrl); + + const { entity: actualEntity } = getMetricsExplorerFromUrl( + url.searchParams, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + explore, + preset ?? {}, + ); + expect(actualEntity).toEqual({ + ...URLStateTestMetricsExplorerEntity, + ...entity, + }); +} From 79218166d5cc48d324591cdf7b2279ee7c45dd6a Mon Sep 17 00:00:00 2001 From: Aditya Hegde Date: Fri, 18 Oct 2024 20:21:52 +0530 Subject: [PATCH 07/17] Use loader function to parse url to metrics explore state --- .../state-managers/state-managers.ts | 2 ++ .../dashboards/stores/dashboard-stores.ts | 19 +++-------- .../dashboards/stores/syncDashboardState.ts | 2 +- .../url-state/DashboardURLStateSync.svelte | 33 +++++++++++++------ .../features/dashboards/url-state/fromUrl.ts | 8 +++++ .../routes/(viz)/explore/[name]/+page.svelte | 27 ++++++++------- .../src/routes/(viz)/explore/[name]/+page.ts | 22 ++++++++++++- 7 files changed, 75 insertions(+), 38 deletions(-) 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 fa6e38fee59..cce34168762 100644 --- a/web-common/src/features/dashboards/state-managers/state-managers.ts +++ b/web-common/src/features/dashboards/state-managers/state-managers.ts @@ -7,6 +7,7 @@ import { getPersistentDashboardStore, initPersistentDashboardStore, } from "@rilldata/web-common/features/dashboards/stores/persistent-dashboard-state"; +import { initLocalUserPreferenceStore } from "@rilldata/web-common/features/dashboards/user-preferences"; import { type ExploreValidSpecResponse, useExploreValidSpec, @@ -139,6 +140,7 @@ export function createStateManagers({ // TODO: once we move everything from dashboard-stores to here, we can get rid of the global initPersistentDashboardStore((extraKeyPrefix || "") + exploreName); + initLocalUserPreferenceStore(exploreName); const persistentDashboardStore = getPersistentDashboardStore(); return { diff --git a/web-common/src/features/dashboards/stores/dashboard-stores.ts b/web-common/src/features/dashboards/stores/dashboard-stores.ts index c8f29e26420..82a31d810b9 100644 --- a/web-common/src/features/dashboards/stores/dashboard-stores.ts +++ b/web-common/src/features/dashboards/stores/dashboard-stores.ts @@ -14,7 +14,6 @@ import { } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; -import { getMetricsExplorerFromUrl } from "@rilldata/web-common/features/dashboards/url-state/fromUrl"; import type { DashboardTimeControls, ScrubRange, @@ -207,28 +206,20 @@ const metricsViewReducers = { syncFromUrlParams( name: string, - urlParams: URLSearchParams, + partialMetrics: Partial, metricsView: V1MetricsViewSpec, ) { - if (!urlParams || !metricsView) return; - // not all data for MetricsExplorerEntity will be filled out here. - // Hence, it is a Partial - const { entity } = getMetricsExplorerFromUrl(urlParams, metricsView); - if (!entity) return; - // TODO: errors - console.log(entity); - updateMetricsExplorerByName(name, (metricsExplorer) => { - for (const key in entity) { - metricsExplorer[key] = entity[key]; + for (const key in partialMetrics) { + metricsExplorer[key] = partialMetrics[key]; } // this hack is needed since what is shown for comparison is not a single source // TODO: use an enum and get rid of this - if (!entity.showTimeComparison) { + if (!partialMetrics.showTimeComparison) { metricsExplorer.showTimeComparison = false; } metricsExplorer.dimensionFilterExcludeMode = - includeExcludeModeFromFilters(entity.whereFilter); + includeExcludeModeFromFilters(partialMetrics.whereFilter); AdvancedMeasureCorrector.correct(metricsExplorer, metricsView); }); }, diff --git a/web-common/src/features/dashboards/stores/syncDashboardState.ts b/web-common/src/features/dashboards/stores/syncDashboardState.ts index 5dc9ab6cd2d..1977b8ca3a1 100644 --- a/web-common/src/features/dashboards/stores/syncDashboardState.ts +++ b/web-common/src/features/dashboards/stores/syncDashboardState.ts @@ -66,7 +66,7 @@ export function createDashboardStateSync( const exploreName = get(ctx.exploreName); if (exploreName in get(metricsExplorerStore).entities) { // Successive syncs with metrics view spec - metricsExplorerStore.sync(exploreName, explore); + // metricsExplorerStore.sync(exploreName, explore); } else { // Running for the 1st time. Initialise the dashboard store. metricsExplorerStore.init( diff --git a/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte b/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte index 65146e1ffcc..e958d8fa924 100644 --- a/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte +++ b/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte @@ -1,35 +1,48 @@ diff --git a/web-common/src/features/dashboards/url-state/fromUrl.ts b/web-common/src/features/dashboards/url-state/fromUrl.ts index c1bfe69d4b2..7841f31ee21 100644 --- a/web-common/src/features/dashboards/url-state/fromUrl.ts +++ b/web-common/src/features/dashboards/url-state/fromUrl.ts @@ -78,6 +78,9 @@ export function getMetricsExplorerFromUrl( if (dimensionFilters) entity.whereFilter = dimensionFilters; if (dimensionThresholdFilters) entity.dimensionThresholdFilters = dimensionThresholdFilters; + } else { + entity.whereFilter = createAndExpression([]); + entity.dimensionThresholdFilters = []; } const { entity: trEntity, errors: trErrors } = fromTimeRangesParams( @@ -128,11 +131,16 @@ function fromTimeRangesParams( const { timeRange, error } = fromTimeRangeUrlParam(comparisonTimeRange); if (error) errors.push(error); entity.selectedComparisonTimeRange = timeRange; + } else { + entity.selectedComparisonTimeRange = undefined; } + const comparisonDimension = searchParams.get("cd") || preset.comparisonDimension; if (comparisonDimension && dimensions.has(comparisonDimension)) { entity.selectedComparisonDimension = comparisonDimension; + } else { + entity.selectedComparisonDimension = ""; } // TODO: grain diff --git a/web-local/src/routes/(viz)/explore/[name]/+page.svelte b/web-local/src/routes/(viz)/explore/[name]/+page.svelte index 272d933421d..3cc4ed5b9e5 100644 --- a/web-local/src/routes/(viz)/explore/[name]/+page.svelte +++ b/web-local/src/routes/(viz)/explore/[name]/+page.svelte @@ -4,7 +4,7 @@ import DashboardThemeProvider from "@rilldata/web-common/features/dashboards/DashboardThemeProvider.svelte"; import { resetSelectedMockUserAfterNavigate } from "@rilldata/web-common/features/dashboards/granular-access-policies/resetSelectedMockUserAfterNavigate"; import { selectedMockUserStore } from "@rilldata/web-common/features/dashboards/granular-access-policies/stores"; - import DashboardURLStateProvider from "@rilldata/web-common/features/dashboards/proto-state/DashboardURLStateProvider.svelte"; + import DashboardURLStateSync from "@rilldata/web-common/features/dashboards/url-state/DashboardURLStateSync.svelte"; import StateManagersProvider from "@rilldata/web-common/features/dashboards/state-managers/StateManagersProvider.svelte"; import DashboardStateProvider from "@rilldata/web-common/features/dashboards/stores/DashboardStateProvider.svelte"; import { useProjectParser } from "@rilldata/web-common/features/entity-management/resource-selectors"; @@ -16,20 +16,21 @@ const queryClient = useQueryClient(); export let data: PageData; + $: ({ metricsView, explore, partialMetrics } = data); resetSelectedMockUserAfterNavigate(queryClient); - $: exploreName = data.explore.meta?.name?.name as string; - $: metricsViewName = data.metricsView?.meta?.name?.name as string; + $: exploreName = explore.meta?.name?.name as string; + $: metricsViewName = metricsView?.meta?.name?.name as string; $: ({ instanceId } = $runtime); $: filePaths = [ - ...(data.explore.meta?.filePaths ?? []), - ...(data.metricsView.meta?.filePaths ?? []), + ...(explore.meta?.filePaths ?? []), + ...(metricsView.meta?.filePaths ?? []), ]; - $: explore = useExploreValidSpec(instanceId, exploreName); - $: measures = $explore.data?.explore?.measures ?? []; + $: exploreQuery = useExploreValidSpec(instanceId, exploreName); + $: measures = $exploreQuery.data?.explore?.measures ?? []; $: projectParserQuery = useProjectParser(queryClient, instanceId, { enabled: $selectedMockUserStore?.admin, }); @@ -39,7 +40,9 @@ (error) => filePaths.includes(error.filePath as string), ); $: mockUserHasNoAccess = - $selectedMockUserStore && $explore.error?.response?.status === 404; + $selectedMockUserStore && $exploreQuery.error?.response?.status === 404; + + console.log("???"); @@ -48,7 +51,7 @@ {#if measures.length === 0 && $selectedMockUserStore !== null} @@ -60,7 +63,7 @@ /> {:else if mockUserHasNoAccess} @@ -68,11 +71,11 @@ {#key exploreName} - + - + {/key} diff --git a/web-local/src/routes/(viz)/explore/[name]/+page.ts b/web-local/src/routes/(viz)/explore/[name]/+page.ts index dc8cee4e97c..f2eeaa78b06 100644 --- a/web-local/src/routes/(viz)/explore/[name]/+page.ts +++ b/web-local/src/routes/(viz)/explore/[name]/+page.ts @@ -1,3 +1,5 @@ +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { getMetricsExplorerFromUrl } from "@rilldata/web-common/features/dashboards/url-state/fromUrl"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient.js"; import { getRuntimeServiceGetExploreQueryKey, @@ -8,7 +10,7 @@ import type { QueryFunction } from "@tanstack/svelte-query"; import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import { get } from "svelte/store"; -export const load = async ({ params, depends }) => { +export const load = async ({ params, depends, url }) => { const { instanceId } = get(runtime); const exploreName = params.name; @@ -29,6 +31,7 @@ export const load = async ({ params, depends }) => { const response = await queryClient.fetchQuery({ queryFn: queryFunction, queryKey, + staleTime: Infinity, }); const exploreResource = response.explore; @@ -41,9 +44,26 @@ export const load = async ({ params, depends }) => { throw error(404, "Metrics view not found"); } + let partialMetrics: Partial = {}; + if ( + metricsViewResource.metricsView.state?.validSpec && + exploreResource.explore.state?.validSpec && + url + ) { + const { entity, errors } = getMetricsExplorerFromUrl( + url.searchParams, + metricsViewResource.metricsView.state.validSpec, + exploreResource.explore.state.validSpec, + exploreResource.explore.state.validSpec.defaultPreset ?? {}, + ); + partialMetrics = entity; + if (errors.length) console.log(errors); // TODO + } + return { explore: exploreResource, metricsView: metricsViewResource, + partialMetrics, }; } catch (e) { console.error(e); From af1a02980f1637c3104fad0e1e49aba2124ddd03 Mon Sep 17 00:00:00 2001 From: Aditya Hegde Date: Tue, 22 Oct 2024 11:13:22 +0530 Subject: [PATCH 08/17] Converts from and to V1ExplorePreset --- proto/gen/rill/runtime/v1/resources.pb.go | 388 ++++++++-------- .../rill/runtime/v1/resources.pb.validate.go | 91 +++- .../gen/rill/runtime/v1/runtime.swagger.yaml | 2 + proto/rill/runtime/v1/resources.proto | 28 +- runtime/compilers/rillv1/parse_explore.go | 14 +- .../compilers/rillv1/parse_metrics_view.go | 41 +- .../src/features/embeds/ExploreEmbed.svelte | 16 +- .../explore/[dashboard]/+page.svelte | 19 +- .../dashboards/stores/dashboard-stores.ts | 4 +- .../dashboards/stores/syncDashboardState.ts | 15 +- .../url-state/DashboardURLStateSync.svelte | 88 +++- .../convertPresetToMetricsExplore.ts | 413 ++++++++++++++++++ .../url-state/convertURLToExplorePreset.ts | 302 +++++++++++++ .../features/dashboards/url-state/defaults.ts | 13 + .../url-state/error-message-helpers.ts | 10 + .../dashboards/url-state/getBasePreset.ts | 51 +++ .../features/dashboards/url-state/toUrl.ts | 17 +- .../url-state/url-state-test-data.ts | 14 + .../dashboards/url-state/url-state.spec.ts | 16 +- .../features/dashboards/user-preferences.ts | 25 +- web-common/src/lib/arrayUtils.ts | 7 + .../proto/gen/rill/runtime/v1/resources_pb.ts | 84 ++-- .../src/runtime-client/gen/index.schemas.ts | 1 + .../routes/(viz)/explore/[name]/+page.svelte | 15 +- .../src/routes/(viz)/explore/[name]/+page.ts | 23 +- 25 files changed, 1372 insertions(+), 325 deletions(-) create mode 100644 web-common/src/features/dashboards/url-state/convertPresetToMetricsExplore.ts create mode 100644 web-common/src/features/dashboards/url-state/convertURLToExplorePreset.ts create mode 100644 web-common/src/features/dashboards/url-state/error-message-helpers.ts create mode 100644 web-common/src/features/dashboards/url-state/getBasePreset.ts diff --git a/proto/gen/rill/runtime/v1/resources.pb.go b/proto/gen/rill/runtime/v1/resources.pb.go index 86c1cf5cfe3..2b821bea632 100644 --- a/proto/gen/rill/runtime/v1/resources.pb.go +++ b/proto/gen/rill/runtime/v1/resources.pb.go @@ -2685,28 +2685,29 @@ type ExplorePreset struct { Measures []string `protobuf:"bytes,4,rep,name=measures,proto3" json:"measures,omitempty"` // Dynamic selector for `measures`. Will be processed during validation, so it will always be empty in `state.valid_spec`. MeasuresSelector *FieldSelector `protobuf:"bytes,10,opt,name=measures_selector,json=measuresSelector,proto3" json:"measures_selector,omitempty"` + Where *Expression `protobuf:"bytes,25,opt,name=where,proto3,oneof" json:"where,omitempty"` // Time range for the explore. // It corresponds to the `range` property of the explore's `time_ranges`. // If not found in `time_ranges`, it should be added to the list. - TimeRange string `protobuf:"bytes,6,opt,name=time_range,json=timeRange,proto3" json:"time_range,omitempty"` - Timezone string `protobuf:"bytes,11,opt,name=timezone,proto3" json:"timezone,omitempty"` - TimeGrain string `protobuf:"bytes,12,opt,name=time_grain,json=timeGrain,proto3" json:"time_grain,omitempty"` + TimeRange *string `protobuf:"bytes,6,opt,name=time_range,json=timeRange,proto3,oneof" json:"time_range,omitempty"` + Timezone *string `protobuf:"bytes,11,opt,name=timezone,proto3,oneof" json:"timezone,omitempty"` + TimeGrain *string `protobuf:"bytes,12,opt,name=time_grain,json=timeGrain,proto3,oneof" json:"time_grain,omitempty"` // Comparison mode. ComparisonMode ExploreComparisonMode `protobuf:"varint,7,opt,name=comparison_mode,json=comparisonMode,proto3,enum=rill.runtime.v1.ExploreComparisonMode" json:"comparison_mode,omitempty"` CompareTimeRange *string `protobuf:"bytes,13,opt,name=compare_time_range,json=compareTimeRange,proto3,oneof" json:"compare_time_range,omitempty"` // If comparison_mode is EXPLORE_COMPARISON_MODE_DIMENSION, this indicates the dimension to use. - ComparisonDimension string `protobuf:"bytes,8,opt,name=comparison_dimension,json=comparisonDimension,proto3" json:"comparison_dimension,omitempty"` - View ExploreWebView `protobuf:"varint,14,opt,name=view,proto3,enum=rill.runtime.v1.ExploreWebView" json:"view,omitempty"` - OverviewSortBy string `protobuf:"bytes,15,opt,name=overview_sort_by,json=overviewSortBy,proto3" json:"overview_sort_by,omitempty"` - OverviewSortAsc bool `protobuf:"varint,16,opt,name=overview_sort_asc,json=overviewSortAsc,proto3" json:"overview_sort_asc,omitempty"` - OverviewExpandedDimension string `protobuf:"bytes,17,opt,name=overview_expanded_dimension,json=overviewExpandedDimension,proto3" json:"overview_expanded_dimension,omitempty"` - TimeDimensionMeasure string `protobuf:"bytes,18,opt,name=time_dimension_measure,json=timeDimensionMeasure,proto3" json:"time_dimension_measure,omitempty"` - TimeDimensionChartType string `protobuf:"bytes,19,opt,name=time_dimension_chart_type,json=timeDimensionChartType,proto3" json:"time_dimension_chart_type,omitempty"` - TimeDimensionPin bool `protobuf:"varint,20,opt,name=time_dimension_pin,json=timeDimensionPin,proto3" json:"time_dimension_pin,omitempty"` - PivotRows []string `protobuf:"bytes,21,rep,name=pivot_rows,json=pivotRows,proto3" json:"pivot_rows,omitempty"` - PivotCols []string `protobuf:"bytes,22,rep,name=pivot_cols,json=pivotCols,proto3" json:"pivot_cols,omitempty"` - PivotSortBy string `protobuf:"bytes,23,opt,name=pivot_sort_by,json=pivotSortBy,proto3" json:"pivot_sort_by,omitempty"` - PivotSortAsc bool `protobuf:"varint,24,opt,name=pivot_sort_asc,json=pivotSortAsc,proto3" json:"pivot_sort_asc,omitempty"` + ComparisonDimension *string `protobuf:"bytes,8,opt,name=comparison_dimension,json=comparisonDimension,proto3,oneof" json:"comparison_dimension,omitempty"` + View *ExploreWebView `protobuf:"varint,14,opt,name=view,proto3,enum=rill.runtime.v1.ExploreWebView,oneof" json:"view,omitempty"` + OverviewSortBy *string `protobuf:"bytes,15,opt,name=overview_sort_by,json=overviewSortBy,proto3,oneof" json:"overview_sort_by,omitempty"` + OverviewSortAsc *bool `protobuf:"varint,16,opt,name=overview_sort_asc,json=overviewSortAsc,proto3,oneof" json:"overview_sort_asc,omitempty"` + OverviewExpandedDimension *string `protobuf:"bytes,17,opt,name=overview_expanded_dimension,json=overviewExpandedDimension,proto3,oneof" json:"overview_expanded_dimension,omitempty"` + TimeDimensionMeasure *string `protobuf:"bytes,18,opt,name=time_dimension_measure,json=timeDimensionMeasure,proto3,oneof" json:"time_dimension_measure,omitempty"` + TimeDimensionChartType *string `protobuf:"bytes,19,opt,name=time_dimension_chart_type,json=timeDimensionChartType,proto3,oneof" json:"time_dimension_chart_type,omitempty"` + TimeDimensionPin *bool `protobuf:"varint,20,opt,name=time_dimension_pin,json=timeDimensionPin,proto3,oneof" json:"time_dimension_pin,omitempty"` + PivotRows []string `protobuf:"bytes,21,rep,name=pivot_rows,json=pivotRows,proto3" json:"pivot_rows,omitempty"` + PivotCols []string `protobuf:"bytes,22,rep,name=pivot_cols,json=pivotCols,proto3" json:"pivot_cols,omitempty"` + PivotSortBy *string `protobuf:"bytes,23,opt,name=pivot_sort_by,json=pivotSortBy,proto3,oneof" json:"pivot_sort_by,omitempty"` + PivotSortAsc *bool `protobuf:"varint,24,opt,name=pivot_sort_asc,json=pivotSortAsc,proto3,oneof" json:"pivot_sort_asc,omitempty"` } func (x *ExplorePreset) Reset() { @@ -2769,23 +2770,30 @@ func (x *ExplorePreset) GetMeasuresSelector() *FieldSelector { return nil } -func (x *ExplorePreset) GetTimeRange() string { +func (x *ExplorePreset) GetWhere() *Expression { if x != nil { - return x.TimeRange + return x.Where + } + return nil +} + +func (x *ExplorePreset) GetTimeRange() string { + if x != nil && x.TimeRange != nil { + return *x.TimeRange } return "" } func (x *ExplorePreset) GetTimezone() string { - if x != nil { - return x.Timezone + if x != nil && x.Timezone != nil { + return *x.Timezone } return "" } func (x *ExplorePreset) GetTimeGrain() string { - if x != nil { - return x.TimeGrain + if x != nil && x.TimeGrain != nil { + return *x.TimeGrain } return "" } @@ -2805,57 +2813,57 @@ func (x *ExplorePreset) GetCompareTimeRange() string { } func (x *ExplorePreset) GetComparisonDimension() string { - if x != nil { - return x.ComparisonDimension + if x != nil && x.ComparisonDimension != nil { + return *x.ComparisonDimension } return "" } func (x *ExplorePreset) GetView() ExploreWebView { - if x != nil { - return x.View + if x != nil && x.View != nil { + return *x.View } return ExploreWebView_EXPLORE_ACTIVE_PAGE_UNSPECIFIED } func (x *ExplorePreset) GetOverviewSortBy() string { - if x != nil { - return x.OverviewSortBy + if x != nil && x.OverviewSortBy != nil { + return *x.OverviewSortBy } return "" } func (x *ExplorePreset) GetOverviewSortAsc() bool { - if x != nil { - return x.OverviewSortAsc + if x != nil && x.OverviewSortAsc != nil { + return *x.OverviewSortAsc } return false } func (x *ExplorePreset) GetOverviewExpandedDimension() string { - if x != nil { - return x.OverviewExpandedDimension + if x != nil && x.OverviewExpandedDimension != nil { + return *x.OverviewExpandedDimension } return "" } func (x *ExplorePreset) GetTimeDimensionMeasure() string { - if x != nil { - return x.TimeDimensionMeasure + if x != nil && x.TimeDimensionMeasure != nil { + return *x.TimeDimensionMeasure } return "" } func (x *ExplorePreset) GetTimeDimensionChartType() string { - if x != nil { - return x.TimeDimensionChartType + if x != nil && x.TimeDimensionChartType != nil { + return *x.TimeDimensionChartType } return "" } func (x *ExplorePreset) GetTimeDimensionPin() bool { - if x != nil { - return x.TimeDimensionPin + if x != nil && x.TimeDimensionPin != nil { + return *x.TimeDimensionPin } return false } @@ -2875,15 +2883,15 @@ func (x *ExplorePreset) GetPivotCols() []string { } func (x *ExplorePreset) GetPivotSortBy() string { - if x != nil { - return x.PivotSortBy + if x != nil && x.PivotSortBy != nil { + return *x.PivotSortBy } return "" } func (x *ExplorePreset) GetPivotSortAsc() bool { - if x != nil { - return x.PivotSortAsc + if x != nil && x.PivotSortAsc != nil { + return *x.PivotSortAsc } return false } @@ -7245,7 +7253,7 @@ var file_rill_runtime_v1_resources_proto_rawDesc = []byte{ 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, - 0x61, 0x6e, 0x67, 0x65, 0x22, 0x83, 0x08, 0x0a, 0x0d, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, + 0x61, 0x6e, 0x67, 0x65, 0x22, 0x93, 0x0b, 0x0a, 0x0d, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x50, 0x72, 0x65, 0x73, 0x65, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x4f, 0x0a, 0x13, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, @@ -7260,56 +7268,81 @@ var file_rill_runtime_v1_resources_proto_rawDesc = []byte{ 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x10, 0x6d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x73, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, - 0x1a, 0x0a, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, - 0x69, 0x6d, 0x65, 0x5f, 0x67, 0x72, 0x61, 0x69, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x74, 0x69, 0x6d, 0x65, 0x47, 0x72, 0x61, 0x69, 0x6e, 0x12, 0x4f, 0x0a, 0x0f, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6d, - 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x0e, 0x63, 0x6f, 0x6d, - 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x31, 0x0a, 0x12, 0x63, - 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, - 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x10, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x72, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x88, 0x01, 0x01, 0x12, 0x31, - 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x5f, 0x64, 0x69, 0x6d, - 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, - 0x6e, 0x12, 0x33, 0x0a, 0x04, 0x76, 0x69, 0x65, 0x77, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x57, 0x65, 0x62, 0x56, 0x69, 0x65, 0x77, - 0x52, 0x04, 0x76, 0x69, 0x65, 0x77, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, - 0x65, 0x77, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x53, 0x6f, 0x72, 0x74, 0x42, 0x79, - 0x12, 0x2a, 0x0a, 0x11, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x73, 0x6f, 0x72, - 0x74, 0x5f, 0x61, 0x73, 0x63, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6f, 0x76, 0x65, - 0x72, 0x76, 0x69, 0x65, 0x77, 0x53, 0x6f, 0x72, 0x74, 0x41, 0x73, 0x63, 0x12, 0x3e, 0x0a, 0x1b, - 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, - 0x64, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x19, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x45, 0x78, 0x70, 0x61, 0x6e, - 0x64, 0x65, 0x64, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x16, + 0x12, 0x36, 0x0a, 0x05, 0x77, 0x68, 0x65, 0x72, 0x65, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x05, + 0x77, 0x68, 0x65, 0x72, 0x65, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, + 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, + 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, + 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, + 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x67, 0x72, 0x61, 0x69, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x47, 0x72, 0x61, 0x69, 0x6e, 0x88, 0x01, + 0x01, 0x12, 0x4f, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x5f, + 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x72, 0x69, 0x6c, + 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, + 0x6c, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, 0x6f, + 0x64, 0x65, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, 0x6f, + 0x64, 0x65, 0x12, 0x31, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x48, 0x04, + 0x52, 0x10, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, + 0x67, 0x65, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, + 0x73, 0x6f, 0x6e, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x05, 0x52, 0x13, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, + 0x6e, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x38, 0x0a, + 0x04, 0x76, 0x69, 0x65, 0x77, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, + 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x57, 0x65, 0x62, 0x56, 0x69, 0x65, 0x77, 0x48, 0x06, 0x52, 0x04, + 0x76, 0x69, 0x65, 0x77, 0x88, 0x01, 0x01, 0x12, 0x2d, 0x0a, 0x10, 0x6f, 0x76, 0x65, 0x72, 0x76, + 0x69, 0x65, 0x77, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x0f, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x07, 0x52, 0x0e, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x53, 0x6f, 0x72, + 0x74, 0x42, 0x79, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x11, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, + 0x65, 0x77, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x61, 0x73, 0x63, 0x18, 0x10, 0x20, 0x01, 0x28, + 0x08, 0x48, 0x08, 0x52, 0x0f, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x53, 0x6f, 0x72, + 0x74, 0x41, 0x73, 0x63, 0x88, 0x01, 0x01, 0x12, 0x43, 0x0a, 0x1b, 0x6f, 0x76, 0x65, 0x72, 0x76, + 0x69, 0x65, 0x77, 0x5f, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x64, 0x69, 0x6d, + 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x48, 0x09, 0x52, 0x19, + 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, + 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x16, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, - 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x74, 0x69, - 0x6d, 0x65, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x61, 0x73, 0x75, - 0x72, 0x65, 0x12, 0x39, 0x0a, 0x19, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x74, 0x69, 0x6d, 0x65, 0x44, 0x69, 0x6d, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x72, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, - 0x12, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, - 0x70, 0x69, 0x6e, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x74, 0x69, 0x6d, 0x65, 0x44, - 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x69, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x70, - 0x69, 0x76, 0x6f, 0x74, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x09, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x69, - 0x76, 0x6f, 0x74, 0x5f, 0x63, 0x6f, 0x6c, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, - 0x70, 0x69, 0x76, 0x6f, 0x74, 0x43, 0x6f, 0x6c, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x70, 0x69, 0x76, - 0x6f, 0x74, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x53, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x12, 0x24, 0x0a, - 0x0e, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x61, 0x73, 0x63, 0x18, - 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x53, 0x6f, 0x72, 0x74, - 0x41, 0x73, 0x63, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xca, 0x01, 0x0a, 0x0d, 0x46, + 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x48, 0x0a, 0x52, 0x14, + 0x74, 0x69, 0x6d, 0x65, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x61, + 0x73, 0x75, 0x72, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3e, 0x0a, 0x19, 0x74, 0x69, 0x6d, 0x65, 0x5f, + 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x48, 0x0b, 0x52, 0x16, 0x74, 0x69, + 0x6d, 0x65, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x72, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x12, 0x74, 0x69, 0x6d, 0x65, 0x5f, + 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x69, 0x6e, 0x18, 0x14, 0x20, + 0x01, 0x28, 0x08, 0x48, 0x0c, 0x52, 0x10, 0x74, 0x69, 0x6d, 0x65, 0x44, 0x69, 0x6d, 0x65, 0x6e, + 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x69, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x69, + 0x76, 0x6f, 0x74, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, + 0x70, 0x69, 0x76, 0x6f, 0x74, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x69, 0x76, + 0x6f, 0x74, 0x5f, 0x63, 0x6f, 0x6c, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x70, + 0x69, 0x76, 0x6f, 0x74, 0x43, 0x6f, 0x6c, 0x73, 0x12, 0x27, 0x0a, 0x0d, 0x70, 0x69, 0x76, 0x6f, + 0x74, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x0d, 0x52, 0x0b, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x53, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x88, 0x01, + 0x01, 0x12, 0x29, 0x0a, 0x0e, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, + 0x61, 0x73, 0x63, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x48, 0x0e, 0x52, 0x0c, 0x70, 0x69, 0x76, + 0x6f, 0x74, 0x53, 0x6f, 0x72, 0x74, 0x41, 0x73, 0x63, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, + 0x5f, 0x77, 0x68, 0x65, 0x72, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, + 0x72, 0x61, 0x6e, 0x67, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, + 0x6e, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x67, 0x72, 0x61, 0x69, + 0x6e, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, + 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x6f, + 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x42, + 0x14, 0x0a, 0x12, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x73, 0x6f, 0x72, + 0x74, 0x5f, 0x61, 0x73, 0x63, 0x42, 0x1e, 0x0a, 0x1c, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, + 0x65, 0x77, 0x5f, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x64, 0x69, 0x6d, 0x65, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x19, 0x0a, 0x17, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, + 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, + 0x42, 0x1c, 0x0a, 0x1a, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x42, 0x15, + 0x0a, 0x13, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, + 0x6e, 0x5f, 0x70, 0x69, 0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x5f, + 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x70, 0x69, 0x76, 0x6f, + 0x74, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x61, 0x73, 0x63, 0x22, 0xca, 0x01, 0x0a, 0x0d, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, @@ -8097,92 +8130,93 @@ var file_rill_runtime_v1_resources_proto_depIdxs = []int32{ 30, // 68: rill.runtime.v1.ExploreTimeRange.comparison_time_ranges:type_name -> rill.runtime.v1.ExploreComparisonTimeRange 32, // 69: rill.runtime.v1.ExplorePreset.dimensions_selector:type_name -> rill.runtime.v1.FieldSelector 32, // 70: rill.runtime.v1.ExplorePreset.measures_selector:type_name -> rill.runtime.v1.FieldSelector - 1, // 71: rill.runtime.v1.ExplorePreset.comparison_mode:type_name -> rill.runtime.v1.ExploreComparisonMode - 2, // 72: rill.runtime.v1.ExplorePreset.view:type_name -> rill.runtime.v1.ExploreWebView - 33, // 73: rill.runtime.v1.FieldSelector.fields:type_name -> rill.runtime.v1.StringListValue - 35, // 74: rill.runtime.v1.Migration.spec:type_name -> rill.runtime.v1.MigrationSpec - 36, // 75: rill.runtime.v1.Migration.state:type_name -> rill.runtime.v1.MigrationState - 38, // 76: rill.runtime.v1.Report.spec:type_name -> rill.runtime.v1.ReportSpec - 39, // 77: rill.runtime.v1.Report.state:type_name -> rill.runtime.v1.ReportState - 72, // 78: rill.runtime.v1.ReportSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule - 96, // 79: rill.runtime.v1.ReportSpec.export_format:type_name -> rill.runtime.v1.ExportFormat - 43, // 80: rill.runtime.v1.ReportSpec.notifiers:type_name -> rill.runtime.v1.Notifier - 87, // 81: rill.runtime.v1.ReportSpec.annotations:type_name -> rill.runtime.v1.ReportSpec.AnnotationsEntry - 91, // 82: rill.runtime.v1.ReportState.next_run_on:type_name -> google.protobuf.Timestamp - 40, // 83: rill.runtime.v1.ReportState.current_execution:type_name -> rill.runtime.v1.ReportExecution - 40, // 84: rill.runtime.v1.ReportState.execution_history:type_name -> rill.runtime.v1.ReportExecution - 91, // 85: rill.runtime.v1.ReportExecution.report_time:type_name -> google.protobuf.Timestamp - 91, // 86: rill.runtime.v1.ReportExecution.started_on:type_name -> google.protobuf.Timestamp - 91, // 87: rill.runtime.v1.ReportExecution.finished_on:type_name -> google.protobuf.Timestamp - 42, // 88: rill.runtime.v1.Alert.spec:type_name -> rill.runtime.v1.AlertSpec - 44, // 89: rill.runtime.v1.Alert.state:type_name -> rill.runtime.v1.AlertState - 72, // 90: rill.runtime.v1.AlertSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule - 92, // 91: rill.runtime.v1.AlertSpec.resolver_properties:type_name -> google.protobuf.Struct - 92, // 92: rill.runtime.v1.AlertSpec.query_for_attributes:type_name -> google.protobuf.Struct - 43, // 93: rill.runtime.v1.AlertSpec.notifiers:type_name -> rill.runtime.v1.Notifier - 88, // 94: rill.runtime.v1.AlertSpec.annotations:type_name -> rill.runtime.v1.AlertSpec.AnnotationsEntry - 92, // 95: rill.runtime.v1.Notifier.properties:type_name -> google.protobuf.Struct - 91, // 96: rill.runtime.v1.AlertState.next_run_on:type_name -> google.protobuf.Timestamp - 45, // 97: rill.runtime.v1.AlertState.current_execution:type_name -> rill.runtime.v1.AlertExecution - 45, // 98: rill.runtime.v1.AlertState.execution_history:type_name -> rill.runtime.v1.AlertExecution - 46, // 99: rill.runtime.v1.AlertExecution.result:type_name -> rill.runtime.v1.AssertionResult - 91, // 100: rill.runtime.v1.AlertExecution.execution_time:type_name -> google.protobuf.Timestamp - 91, // 101: rill.runtime.v1.AlertExecution.started_on:type_name -> google.protobuf.Timestamp - 91, // 102: rill.runtime.v1.AlertExecution.finished_on:type_name -> google.protobuf.Timestamp - 91, // 103: rill.runtime.v1.AlertExecution.suppressed_since:type_name -> google.protobuf.Timestamp - 3, // 104: rill.runtime.v1.AssertionResult.status:type_name -> rill.runtime.v1.AssertionStatus - 92, // 105: rill.runtime.v1.AssertionResult.fail_row:type_name -> google.protobuf.Struct - 48, // 106: rill.runtime.v1.PullTrigger.spec:type_name -> rill.runtime.v1.PullTriggerSpec - 49, // 107: rill.runtime.v1.PullTrigger.state:type_name -> rill.runtime.v1.PullTriggerState - 51, // 108: rill.runtime.v1.RefreshTrigger.spec:type_name -> rill.runtime.v1.RefreshTriggerSpec - 52, // 109: rill.runtime.v1.RefreshTrigger.state:type_name -> rill.runtime.v1.RefreshTriggerState - 9, // 110: rill.runtime.v1.RefreshTriggerSpec.resources:type_name -> rill.runtime.v1.ResourceName - 53, // 111: rill.runtime.v1.RefreshTriggerSpec.models:type_name -> rill.runtime.v1.RefreshModelTrigger - 55, // 112: rill.runtime.v1.BucketPlanner.spec:type_name -> rill.runtime.v1.BucketPlannerSpec - 56, // 113: rill.runtime.v1.BucketPlanner.state:type_name -> rill.runtime.v1.BucketPlannerState - 57, // 114: rill.runtime.v1.BucketPlannerSpec.extract_policy:type_name -> rill.runtime.v1.BucketExtractPolicy - 6, // 115: rill.runtime.v1.BucketExtractPolicy.rows_strategy:type_name -> rill.runtime.v1.BucketExtractPolicy.Strategy - 6, // 116: rill.runtime.v1.BucketExtractPolicy.files_strategy:type_name -> rill.runtime.v1.BucketExtractPolicy.Strategy - 59, // 117: rill.runtime.v1.Theme.spec:type_name -> rill.runtime.v1.ThemeSpec - 60, // 118: rill.runtime.v1.Theme.state:type_name -> rill.runtime.v1.ThemeState - 97, // 119: rill.runtime.v1.ThemeSpec.primary_color:type_name -> rill.runtime.v1.Color - 97, // 120: rill.runtime.v1.ThemeSpec.secondary_color:type_name -> rill.runtime.v1.Color - 62, // 121: rill.runtime.v1.Component.spec:type_name -> rill.runtime.v1.ComponentSpec - 63, // 122: rill.runtime.v1.Component.state:type_name -> rill.runtime.v1.ComponentState - 92, // 123: rill.runtime.v1.ComponentSpec.resolver_properties:type_name -> google.protobuf.Struct - 92, // 124: rill.runtime.v1.ComponentSpec.renderer_properties:type_name -> google.protobuf.Struct - 64, // 125: rill.runtime.v1.ComponentSpec.input:type_name -> rill.runtime.v1.ComponentVariable - 64, // 126: rill.runtime.v1.ComponentSpec.output:type_name -> rill.runtime.v1.ComponentVariable - 62, // 127: rill.runtime.v1.ComponentState.valid_spec:type_name -> rill.runtime.v1.ComponentSpec - 98, // 128: rill.runtime.v1.ComponentVariable.default_value:type_name -> google.protobuf.Value - 66, // 129: rill.runtime.v1.Canvas.spec:type_name -> rill.runtime.v1.CanvasSpec - 67, // 130: rill.runtime.v1.Canvas.state:type_name -> rill.runtime.v1.CanvasState - 64, // 131: rill.runtime.v1.CanvasSpec.variables:type_name -> rill.runtime.v1.ComponentVariable - 68, // 132: rill.runtime.v1.CanvasSpec.items:type_name -> rill.runtime.v1.CanvasItem - 21, // 133: rill.runtime.v1.CanvasSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule - 66, // 134: rill.runtime.v1.CanvasState.valid_spec:type_name -> rill.runtime.v1.CanvasSpec - 70, // 135: rill.runtime.v1.API.spec:type_name -> rill.runtime.v1.APISpec - 71, // 136: rill.runtime.v1.API.state:type_name -> rill.runtime.v1.APIState - 92, // 137: rill.runtime.v1.APISpec.resolver_properties:type_name -> google.protobuf.Struct - 92, // 138: rill.runtime.v1.APISpec.openapi_parameters:type_name -> google.protobuf.Struct - 92, // 139: rill.runtime.v1.APISpec.openapi_response_schema:type_name -> google.protobuf.Struct - 77, // 140: rill.runtime.v1.ParseError.start_location:type_name -> rill.runtime.v1.CharLocation - 89, // 141: rill.runtime.v1.ConnectorSpec.properties:type_name -> rill.runtime.v1.ConnectorSpec.PropertiesEntry - 90, // 142: rill.runtime.v1.ConnectorSpec.properties_from_variables:type_name -> rill.runtime.v1.ConnectorSpec.PropertiesFromVariablesEntry - 78, // 143: rill.runtime.v1.ConnectorV2.spec:type_name -> rill.runtime.v1.ConnectorSpec - 79, // 144: rill.runtime.v1.ConnectorV2.state:type_name -> rill.runtime.v1.ConnectorState - 94, // 145: rill.runtime.v1.MetricsViewSpec.DimensionSelector.time_grain:type_name -> rill.runtime.v1.TimeGrain - 82, // 146: rill.runtime.v1.MetricsViewSpec.MeasureWindow.order_by:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector - 4, // 147: rill.runtime.v1.MetricsViewSpec.MeasureV2.type:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureType - 83, // 148: rill.runtime.v1.MetricsViewSpec.MeasureV2.window:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureWindow - 82, // 149: rill.runtime.v1.MetricsViewSpec.MeasureV2.per_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector - 82, // 150: rill.runtime.v1.MetricsViewSpec.MeasureV2.required_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector - 85, // 151: rill.runtime.v1.MetricsViewSpec.AvailableTimeRange.comparison_offsets:type_name -> rill.runtime.v1.MetricsViewSpec.AvailableComparisonOffset - 152, // [152:152] is the sub-list for method output_type - 152, // [152:152] is the sub-list for method input_type - 152, // [152:152] is the sub-list for extension type_name - 152, // [152:152] is the sub-list for extension extendee - 0, // [0:152] is the sub-list for field type_name + 95, // 71: rill.runtime.v1.ExplorePreset.where:type_name -> rill.runtime.v1.Expression + 1, // 72: rill.runtime.v1.ExplorePreset.comparison_mode:type_name -> rill.runtime.v1.ExploreComparisonMode + 2, // 73: rill.runtime.v1.ExplorePreset.view:type_name -> rill.runtime.v1.ExploreWebView + 33, // 74: rill.runtime.v1.FieldSelector.fields:type_name -> rill.runtime.v1.StringListValue + 35, // 75: rill.runtime.v1.Migration.spec:type_name -> rill.runtime.v1.MigrationSpec + 36, // 76: rill.runtime.v1.Migration.state:type_name -> rill.runtime.v1.MigrationState + 38, // 77: rill.runtime.v1.Report.spec:type_name -> rill.runtime.v1.ReportSpec + 39, // 78: rill.runtime.v1.Report.state:type_name -> rill.runtime.v1.ReportState + 72, // 79: rill.runtime.v1.ReportSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule + 96, // 80: rill.runtime.v1.ReportSpec.export_format:type_name -> rill.runtime.v1.ExportFormat + 43, // 81: rill.runtime.v1.ReportSpec.notifiers:type_name -> rill.runtime.v1.Notifier + 87, // 82: rill.runtime.v1.ReportSpec.annotations:type_name -> rill.runtime.v1.ReportSpec.AnnotationsEntry + 91, // 83: rill.runtime.v1.ReportState.next_run_on:type_name -> google.protobuf.Timestamp + 40, // 84: rill.runtime.v1.ReportState.current_execution:type_name -> rill.runtime.v1.ReportExecution + 40, // 85: rill.runtime.v1.ReportState.execution_history:type_name -> rill.runtime.v1.ReportExecution + 91, // 86: rill.runtime.v1.ReportExecution.report_time:type_name -> google.protobuf.Timestamp + 91, // 87: rill.runtime.v1.ReportExecution.started_on:type_name -> google.protobuf.Timestamp + 91, // 88: rill.runtime.v1.ReportExecution.finished_on:type_name -> google.protobuf.Timestamp + 42, // 89: rill.runtime.v1.Alert.spec:type_name -> rill.runtime.v1.AlertSpec + 44, // 90: rill.runtime.v1.Alert.state:type_name -> rill.runtime.v1.AlertState + 72, // 91: rill.runtime.v1.AlertSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule + 92, // 92: rill.runtime.v1.AlertSpec.resolver_properties:type_name -> google.protobuf.Struct + 92, // 93: rill.runtime.v1.AlertSpec.query_for_attributes:type_name -> google.protobuf.Struct + 43, // 94: rill.runtime.v1.AlertSpec.notifiers:type_name -> rill.runtime.v1.Notifier + 88, // 95: rill.runtime.v1.AlertSpec.annotations:type_name -> rill.runtime.v1.AlertSpec.AnnotationsEntry + 92, // 96: rill.runtime.v1.Notifier.properties:type_name -> google.protobuf.Struct + 91, // 97: rill.runtime.v1.AlertState.next_run_on:type_name -> google.protobuf.Timestamp + 45, // 98: rill.runtime.v1.AlertState.current_execution:type_name -> rill.runtime.v1.AlertExecution + 45, // 99: rill.runtime.v1.AlertState.execution_history:type_name -> rill.runtime.v1.AlertExecution + 46, // 100: rill.runtime.v1.AlertExecution.result:type_name -> rill.runtime.v1.AssertionResult + 91, // 101: rill.runtime.v1.AlertExecution.execution_time:type_name -> google.protobuf.Timestamp + 91, // 102: rill.runtime.v1.AlertExecution.started_on:type_name -> google.protobuf.Timestamp + 91, // 103: rill.runtime.v1.AlertExecution.finished_on:type_name -> google.protobuf.Timestamp + 91, // 104: rill.runtime.v1.AlertExecution.suppressed_since:type_name -> google.protobuf.Timestamp + 3, // 105: rill.runtime.v1.AssertionResult.status:type_name -> rill.runtime.v1.AssertionStatus + 92, // 106: rill.runtime.v1.AssertionResult.fail_row:type_name -> google.protobuf.Struct + 48, // 107: rill.runtime.v1.PullTrigger.spec:type_name -> rill.runtime.v1.PullTriggerSpec + 49, // 108: rill.runtime.v1.PullTrigger.state:type_name -> rill.runtime.v1.PullTriggerState + 51, // 109: rill.runtime.v1.RefreshTrigger.spec:type_name -> rill.runtime.v1.RefreshTriggerSpec + 52, // 110: rill.runtime.v1.RefreshTrigger.state:type_name -> rill.runtime.v1.RefreshTriggerState + 9, // 111: rill.runtime.v1.RefreshTriggerSpec.resources:type_name -> rill.runtime.v1.ResourceName + 53, // 112: rill.runtime.v1.RefreshTriggerSpec.models:type_name -> rill.runtime.v1.RefreshModelTrigger + 55, // 113: rill.runtime.v1.BucketPlanner.spec:type_name -> rill.runtime.v1.BucketPlannerSpec + 56, // 114: rill.runtime.v1.BucketPlanner.state:type_name -> rill.runtime.v1.BucketPlannerState + 57, // 115: rill.runtime.v1.BucketPlannerSpec.extract_policy:type_name -> rill.runtime.v1.BucketExtractPolicy + 6, // 116: rill.runtime.v1.BucketExtractPolicy.rows_strategy:type_name -> rill.runtime.v1.BucketExtractPolicy.Strategy + 6, // 117: rill.runtime.v1.BucketExtractPolicy.files_strategy:type_name -> rill.runtime.v1.BucketExtractPolicy.Strategy + 59, // 118: rill.runtime.v1.Theme.spec:type_name -> rill.runtime.v1.ThemeSpec + 60, // 119: rill.runtime.v1.Theme.state:type_name -> rill.runtime.v1.ThemeState + 97, // 120: rill.runtime.v1.ThemeSpec.primary_color:type_name -> rill.runtime.v1.Color + 97, // 121: rill.runtime.v1.ThemeSpec.secondary_color:type_name -> rill.runtime.v1.Color + 62, // 122: rill.runtime.v1.Component.spec:type_name -> rill.runtime.v1.ComponentSpec + 63, // 123: rill.runtime.v1.Component.state:type_name -> rill.runtime.v1.ComponentState + 92, // 124: rill.runtime.v1.ComponentSpec.resolver_properties:type_name -> google.protobuf.Struct + 92, // 125: rill.runtime.v1.ComponentSpec.renderer_properties:type_name -> google.protobuf.Struct + 64, // 126: rill.runtime.v1.ComponentSpec.input:type_name -> rill.runtime.v1.ComponentVariable + 64, // 127: rill.runtime.v1.ComponentSpec.output:type_name -> rill.runtime.v1.ComponentVariable + 62, // 128: rill.runtime.v1.ComponentState.valid_spec:type_name -> rill.runtime.v1.ComponentSpec + 98, // 129: rill.runtime.v1.ComponentVariable.default_value:type_name -> google.protobuf.Value + 66, // 130: rill.runtime.v1.Canvas.spec:type_name -> rill.runtime.v1.CanvasSpec + 67, // 131: rill.runtime.v1.Canvas.state:type_name -> rill.runtime.v1.CanvasState + 64, // 132: rill.runtime.v1.CanvasSpec.variables:type_name -> rill.runtime.v1.ComponentVariable + 68, // 133: rill.runtime.v1.CanvasSpec.items:type_name -> rill.runtime.v1.CanvasItem + 21, // 134: rill.runtime.v1.CanvasSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule + 66, // 135: rill.runtime.v1.CanvasState.valid_spec:type_name -> rill.runtime.v1.CanvasSpec + 70, // 136: rill.runtime.v1.API.spec:type_name -> rill.runtime.v1.APISpec + 71, // 137: rill.runtime.v1.API.state:type_name -> rill.runtime.v1.APIState + 92, // 138: rill.runtime.v1.APISpec.resolver_properties:type_name -> google.protobuf.Struct + 92, // 139: rill.runtime.v1.APISpec.openapi_parameters:type_name -> google.protobuf.Struct + 92, // 140: rill.runtime.v1.APISpec.openapi_response_schema:type_name -> google.protobuf.Struct + 77, // 141: rill.runtime.v1.ParseError.start_location:type_name -> rill.runtime.v1.CharLocation + 89, // 142: rill.runtime.v1.ConnectorSpec.properties:type_name -> rill.runtime.v1.ConnectorSpec.PropertiesEntry + 90, // 143: rill.runtime.v1.ConnectorSpec.properties_from_variables:type_name -> rill.runtime.v1.ConnectorSpec.PropertiesFromVariablesEntry + 78, // 144: rill.runtime.v1.ConnectorV2.spec:type_name -> rill.runtime.v1.ConnectorSpec + 79, // 145: rill.runtime.v1.ConnectorV2.state:type_name -> rill.runtime.v1.ConnectorState + 94, // 146: rill.runtime.v1.MetricsViewSpec.DimensionSelector.time_grain:type_name -> rill.runtime.v1.TimeGrain + 82, // 147: rill.runtime.v1.MetricsViewSpec.MeasureWindow.order_by:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector + 4, // 148: rill.runtime.v1.MetricsViewSpec.MeasureV2.type:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureType + 83, // 149: rill.runtime.v1.MetricsViewSpec.MeasureV2.window:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureWindow + 82, // 150: rill.runtime.v1.MetricsViewSpec.MeasureV2.per_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector + 82, // 151: rill.runtime.v1.MetricsViewSpec.MeasureV2.required_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector + 85, // 152: rill.runtime.v1.MetricsViewSpec.AvailableTimeRange.comparison_offsets:type_name -> rill.runtime.v1.MetricsViewSpec.AvailableComparisonOffset + 153, // [153:153] is the sub-list for method output_type + 153, // [153:153] is the sub-list for method input_type + 153, // [153:153] is the sub-list for extension type_name + 153, // [153:153] is the sub-list for extension extendee + 0, // [0:153] is the sub-list for field type_name } func init() { file_rill_runtime_v1_resources_proto_init() } diff --git a/proto/gen/rill/runtime/v1/resources.pb.validate.go b/proto/gen/rill/runtime/v1/resources.pb.validate.go index 41df2efa9f3..e297e6e2b98 100644 --- a/proto/gen/rill/runtime/v1/resources.pb.validate.go +++ b/proto/gen/rill/runtime/v1/resources.pb.validate.go @@ -4857,36 +4857,95 @@ func (m *ExplorePreset) validate(all bool) error { } } - // no validation rules for TimeRange + // no validation rules for ComparisonMode - // no validation rules for Timezone + if m.Where != nil { - // no validation rules for TimeGrain + if all { + switch v := interface{}(m.GetWhere()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, ExplorePresetValidationError{ + field: "Where", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, ExplorePresetValidationError{ + field: "Where", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetWhere()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return ExplorePresetValidationError{ + field: "Where", + reason: "embedded message failed validation", + cause: err, + } + } + } - // no validation rules for ComparisonMode + } + + if m.TimeRange != nil { + // no validation rules for TimeRange + } + + if m.Timezone != nil { + // no validation rules for Timezone + } - // no validation rules for ComparisonDimension + if m.TimeGrain != nil { + // no validation rules for TimeGrain + } + + if m.CompareTimeRange != nil { + // no validation rules for CompareTimeRange + } - // no validation rules for View + if m.ComparisonDimension != nil { + // no validation rules for ComparisonDimension + } - // no validation rules for OverviewSortBy + if m.View != nil { + // no validation rules for View + } - // no validation rules for OverviewSortAsc + if m.OverviewSortBy != nil { + // no validation rules for OverviewSortBy + } - // no validation rules for OverviewExpandedDimension + if m.OverviewSortAsc != nil { + // no validation rules for OverviewSortAsc + } - // no validation rules for TimeDimensionMeasure + if m.OverviewExpandedDimension != nil { + // no validation rules for OverviewExpandedDimension + } - // no validation rules for TimeDimensionChartType + if m.TimeDimensionMeasure != nil { + // no validation rules for TimeDimensionMeasure + } - // no validation rules for TimeDimensionPin + if m.TimeDimensionChartType != nil { + // no validation rules for TimeDimensionChartType + } - // no validation rules for PivotSortBy + if m.TimeDimensionPin != nil { + // no validation rules for TimeDimensionPin + } - // no validation rules for PivotSortAsc + if m.PivotSortBy != nil { + // no validation rules for PivotSortBy + } - if m.CompareTimeRange != nil { - // no validation rules for CompareTimeRange + if m.PivotSortAsc != nil { + // no validation rules for PivotSortAsc } if len(errors) > 0 { diff --git a/proto/gen/rill/runtime/v1/runtime.swagger.yaml b/proto/gen/rill/runtime/v1/runtime.swagger.yaml index 15107d513a5..af72347b0b4 100644 --- a/proto/gen/rill/runtime/v1/runtime.swagger.yaml +++ b/proto/gen/rill/runtime/v1/runtime.swagger.yaml @@ -3999,6 +3999,8 @@ definitions: measuresSelector: $ref: '#/definitions/v1FieldSelector' description: Dynamic selector for `measures`. Will be processed during validation, so it will always be empty in `state.valid_spec`. + where: + $ref: '#/definitions/v1Expression' timeRange: type: string description: |- diff --git a/proto/rill/runtime/v1/resources.proto b/proto/rill/runtime/v1/resources.proto index fa000e0cfa1..9c734719a9c 100644 --- a/proto/rill/runtime/v1/resources.proto +++ b/proto/rill/runtime/v1/resources.proto @@ -382,33 +382,35 @@ message ExplorePreset { // Dynamic selector for `measures`. Will be processed during validation, so it will always be empty in `state.valid_spec`. FieldSelector measures_selector = 10; + optional Expression where = 25; + // Time range for the explore. // It corresponds to the `range` property of the explore's `time_ranges`. // If not found in `time_ranges`, it should be added to the list. - string time_range = 6; - string timezone = 11; - string time_grain = 12; + optional string time_range = 6; + optional string timezone = 11; + optional string time_grain = 12; // Comparison mode. ExploreComparisonMode comparison_mode = 7; optional string compare_time_range = 13; // If comparison_mode is EXPLORE_COMPARISON_MODE_DIMENSION, this indicates the dimension to use. - string comparison_dimension = 8; + optional string comparison_dimension = 8; - ExploreWebView view = 14; + optional ExploreWebView view = 14; - string overview_sort_by = 15; - bool overview_sort_asc = 16; - string overview_expanded_dimension = 17; + optional string overview_sort_by = 15; + optional bool overview_sort_asc = 16; + optional string overview_expanded_dimension = 17; - string time_dimension_measure = 18; - string time_dimension_chart_type = 19; - bool time_dimension_pin = 20; + optional string time_dimension_measure = 18; + optional string time_dimension_chart_type = 19; + optional bool time_dimension_pin = 20; repeated string pivot_rows = 21; repeated string pivot_cols = 22; - string pivot_sort_by = 23; - bool pivot_sort_asc = 24; + optional string pivot_sort_by = 23; + optional bool pivot_sort_asc = 24; } enum ExploreComparisonMode { diff --git a/runtime/compilers/rillv1/parse_explore.go b/runtime/compilers/rillv1/parse_explore.go index 63ad936b0f7..43d3e5a2118 100644 --- a/runtime/compilers/rillv1/parse_explore.go +++ b/runtime/compilers/rillv1/parse_explore.go @@ -12,7 +12,7 @@ import ( ) type ExploreYAML struct { - commonYAML `yaml:",inline"` // Not accessed here, only setting it so we can use KnownFields for YAML parsing + commonYAML `yaml:",inline"` // Not accessed here, only setting it so we can use KnownFields for YAML parsing Title string `yaml:"title"` Description string `yaml:"description"` MetricsView string `yaml:"metrics_view"` @@ -214,14 +214,22 @@ func (p *Parser) parseExplore(node *Node) error { presetMeasuresSelector = tmp.Defaults.Measures.Proto() } + var tr *string + if tmp.Defaults.TimeRange != "" { + tr = &tmp.Defaults.TimeRange + } + var compareDim *string + if tmp.Defaults.ComparisonDimension != "" { + compareDim = &tmp.Defaults.ComparisonDimension + } defaultPreset = &runtimev1.ExplorePreset{ Dimensions: presetDimensions, DimensionsSelector: presetDimensionsSelector, Measures: presetMeasures, MeasuresSelector: presetMeasuresSelector, - TimeRange: tmp.Defaults.TimeRange, + TimeRange: tr, ComparisonMode: mode, - ComparisonDimension: tmp.Defaults.ComparisonDimension, + ComparisonDimension: compareDim, } } diff --git a/runtime/compilers/rillv1/parse_metrics_view.go b/runtime/compilers/rillv1/parse_metrics_view.go index dcdf90ea09b..678b596bef5 100644 --- a/runtime/compilers/rillv1/parse_metrics_view.go +++ b/runtime/compilers/rillv1/parse_metrics_view.go @@ -4,30 +4,29 @@ import ( "fmt" "strings" "time" + // Load IANA time zone data + _ "time/tzdata" runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1" "github.com/rilldata/rill/runtime/pkg/duration" "gopkg.in/yaml.v3" - - // Load IANA time zone data - _ "time/tzdata" ) // MetricsViewYAML is the raw structure of a MetricsView resource defined in YAML type MetricsViewYAML struct { commonYAML `yaml:",inline"` // Not accessed here, only setting it so we can use KnownFields for YAML parsing - Title string `yaml:"title"` - DisplayName string `yaml:"display_name"` // Backwards compatibility - Description string `yaml:"description"` - Model string `yaml:"model"` - Database string `yaml:"database"` - DatabaseSchema string `yaml:"database_schema"` - Table string `yaml:"table"` - TimeDimension string `yaml:"timeseries"` - Watermark string `yaml:"watermark"` - SmallestTimeGrain string `yaml:"smallest_time_grain"` - FirstDayOfWeek uint32 `yaml:"first_day_of_week"` - FirstMonthOfYear uint32 `yaml:"first_month_of_year"` + Title string `yaml:"title"` + DisplayName string `yaml:"display_name"` // Backwards compatibility + Description string `yaml:"description"` + Model string `yaml:"model"` + Database string `yaml:"database"` + DatabaseSchema string `yaml:"database_schema"` + Table string `yaml:"table"` + TimeDimension string `yaml:"timeseries"` + Watermark string `yaml:"watermark"` + SmallestTimeGrain string `yaml:"smallest_time_grain"` + FirstDayOfWeek uint32 `yaml:"first_day_of_week"` + FirstMonthOfYear uint32 `yaml:"first_month_of_year"` Dimensions []*struct { Name string Label string @@ -872,14 +871,22 @@ func (p *Parser) parseMetricsView(node *Node) error { if len(spec.DefaultMeasures) == 0 { presetMeasuresSelector = &runtimev1.FieldSelector{Selector: &runtimev1.FieldSelector_All{All: true}} } + var tr *string + if spec.DefaultTimeRange != "" { + tr = &spec.DefaultTimeRange + } + var compareDim *string + if spec.DefaultComparisonDimension != "" { + compareDim = &spec.DefaultComparisonDimension + } e.ExploreSpec.DefaultPreset = &runtimev1.ExplorePreset{ Dimensions: spec.DefaultDimensions, DimensionsSelector: presetDimensionsSelector, Measures: spec.DefaultMeasures, MeasuresSelector: presetMeasuresSelector, - TimeRange: spec.DefaultTimeRange, + TimeRange: tr, ComparisonMode: exploreComparisonMode, - ComparisonDimension: spec.DefaultComparisonDimension, + ComparisonDimension: compareDim, } return nil diff --git a/web-admin/src/features/embeds/ExploreEmbed.svelte b/web-admin/src/features/embeds/ExploreEmbed.svelte index a3a1a6d0578..3c9f4b6168b 100644 --- a/web-admin/src/features/embeds/ExploreEmbed.svelte +++ b/web-admin/src/features/embeds/ExploreEmbed.svelte @@ -1,9 +1,8 @@ - +{#if schemaError} + +{:else if timeRangeSummaryError} + +{:else if $dashboardStore} + +{/if} diff --git a/web-common/src/features/dashboards/url-state/convertPresetToMetricsExplore.ts b/web-common/src/features/dashboards/url-state/convertPresetToMetricsExplore.ts new file mode 100644 index 00000000000..87a0794d78b --- /dev/null +++ b/web-common/src/features/dashboards/url-state/convertPresetToMetricsExplore.ts @@ -0,0 +1,413 @@ +import { splitWhereFilter } from "@rilldata/web-common/features/dashboards/filters/measure-filters/measure-filter-utils"; +import { + type PivotChipData, + PivotChipType, +} from "@rilldata/web-common/features/dashboards/pivot/types"; +import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; +import { + getMultiFieldError, + getSingleFieldError, +} from "@rilldata/web-common/features/dashboards/url-state/error-message-helpers"; +import { + FromURLParamTDDChartMap, + FromURLParamTimeDimensionMap, + ToActivePageViewMap, +} from "@rilldata/web-common/features/dashboards/url-state/mappers"; +import { + getMapFromArray, + getMissingValues, +} from "@rilldata/web-common/lib/arrayUtils"; +import { TIME_GRAIN } from "@rilldata/web-common/lib/time/config"; +import type { DashboardTimeControls } from "@rilldata/web-common/lib/time/types"; +import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; +import { + type MetricsViewSpecDimensionV2, + type MetricsViewSpecMeasureV2, + V1ExploreComparisonMode, + type V1ExplorePreset, + type V1ExploreSpec, + V1ExploreWebView, + type V1MetricsViewSpec, +} from "@rilldata/web-common/runtime-client"; + +/** + * Converts a V1ExplorePreset to our internal metrics explore state. + * V1ExplorePreset could come from url state, bookmark, alert or report. + */ +export function convertPresetToMetricsExplore( + metricsView: V1MetricsViewSpec, + explore: V1ExploreSpec, + preset: V1ExplorePreset, +) { + const entity: Partial = {}; + const errors: Error[] = []; + + const measures = getMapFromArray( + metricsView.measures?.filter((m) => explore.measures?.includes(m.name!)) ?? + [], + (m) => m.name!, + ); + const dimensions = getMapFromArray( + metricsView.dimensions?.filter((d) => + explore.dimensions?.includes(d.name!), + ) ?? [], + (d) => d.name!, + ); + + if (preset.view) { + entity.activePage = Number(ToActivePageViewMap[preset.view] ?? "0"); + } + + if (preset.where) { + const { dimensionFilters, dimensionThresholdFilters } = splitWhereFilter( + preset.where, + ); + entity.whereFilter = dimensionFilters; + entity.dimensionThresholdFilters = dimensionThresholdFilters; + } + + const { entity: trEntity, errors: trErrors } = fromTimeRangesParams( + preset, + dimensions, + ); + Object.assign(entity, trEntity); + errors.push(...trErrors); + + const { entity: ovEntity, errors: ovErrors } = fromOverviewUrlParams( + measures, + dimensions, + explore, + preset, + ); + Object.assign(entity, ovEntity); + errors.push(...ovErrors); + + const { entity: tddEntity, errors: tddErrors } = fromTimeDimensionUrlParams( + measures, + preset, + ); + Object.assign(entity, tddEntity); + errors.push(...tddErrors); + + const { entity: pivotEntity, errors: pivotErrors } = fromPivotUrlParams( + measures, + dimensions, + preset, + ); + Object.assign(entity, pivotEntity); + errors.push(...pivotErrors); + + return { entity, errors }; +} + +function fromTimeRangesParams( + preset: V1ExplorePreset, + dimensions: Map, +) { + const entity: Partial = {}; + const errors: Error[] = []; + + if (preset.timeRange) { + const { timeRange, error } = fromTimeRangeUrlParam(preset.timeRange); + if (error) errors.push(error); + entity.selectedTimeRange = timeRange; + } + + if (preset.timezone) { + entity.selectedTimezone = preset.timezone; + } + + if (preset.compareTimeRange) { + const { timeRange, error } = fromTimeRangeUrlParam(preset.compareTimeRange); + if (error) errors.push(error); + entity.selectedComparisonTimeRange = timeRange; + entity.showTimeComparison = true; + // unset compare dimension + entity.selectedComparisonDimension = ""; + } + + if (preset.comparisonDimension) { + if (dimensions.has(preset.comparisonDimension)) { + entity.selectedComparisonDimension = preset.comparisonDimension; + // unset compare time ranges + entity.selectedComparisonTimeRange = undefined; + entity.showTimeComparison = false; + } else { + errors.push( + getSingleFieldError("compare dimension", preset.comparisonDimension), + ); + } + } + + if ( + preset.comparisonMode === + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_NONE + ) { + // unset all comparison setting if mode is none + entity.selectedComparisonTimeRange = undefined; + entity.selectedComparisonDimension = ""; + entity.showTimeComparison = false; + } + + // TODO: grain + + return { entity, errors }; +} + +function fromTimeRangeUrlParam(tr: string): { + timeRange?: DashboardTimeControls; + error?: Error; +} { + // TODO: validation + return { + timeRange: { + name: tr, + } as DashboardTimeControls, + }; + + // return { + // error: new Error(`unknown time range: ${tr}`), + // }; +} + +function fromOverviewUrlParams( + measures: Map, + dimensions: Map, + explore: V1ExploreSpec, + preset: V1ExplorePreset, +) { + const entity: Partial = {}; + const errors: Error[] = []; + + if (preset.measures?.length) { + const selectedMeasures = preset.measures.filter((m) => measures.has(m)); + const missingMeasures = getMissingValues(selectedMeasures, preset.measures); + if (missingMeasures.length) { + errors.push(getMultiFieldError("measure", missingMeasures)); + } + + entity.allMeasuresVisible = + selectedMeasures.length === explore.measures?.length; + entity.visibleMeasureKeys = new Set(selectedMeasures); + } + + if (preset.dimensions?.length) { + const selectedDimensions = preset.dimensions.filter((d) => + dimensions.has(d), + ); + const missingDimensions = getMissingValues( + selectedDimensions, + preset.dimensions, + ); + if (missingDimensions.length) { + errors.push(getMultiFieldError("dimension", missingDimensions)); + } + + entity.allDimensionsVisible = + selectedDimensions.length === explore.dimensions?.length; + entity.visibleDimensionKeys = new Set(selectedDimensions); + } + + if (preset.overviewSortBy) { + if (measures.has(preset.overviewSortBy)) { + entity.leaderboardMeasureName = preset.overviewSortBy; + } else { + errors.push( + getSingleFieldError("sort by measure", preset.overviewSortBy), + ); + } + } + + if (preset.overviewSortAsc !== undefined) { + entity.sortDirection = preset.overviewSortAsc + ? SortDirection.ASCENDING + : SortDirection.DESCENDING; + } + + if (preset.overviewExpandedDimension !== undefined) { + if (preset.overviewExpandedDimension === "") { + entity.selectedDimensionName = ""; + // if preset didnt have a view then this is a dimension table unset. + if ( + preset.view === V1ExploreWebView.EXPLORE_ACTIVE_PAGE_UNSPECIFIED || + preset.view === undefined + ) { + entity.activePage = DashboardState_ActivePage.DEFAULT; + } + } else if (dimensions.has(preset.overviewExpandedDimension)) { + entity.selectedDimensionName = preset.overviewExpandedDimension; + if ( + preset.view === V1ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW || + preset.view === V1ExploreWebView.EXPLORE_ACTIVE_PAGE_UNSPECIFIED || + preset.view === undefined + ) { + entity.activePage = DashboardState_ActivePage.DIMENSION_TABLE; + } + } else { + errors.push( + getSingleFieldError( + "expanded dimension", + preset.overviewExpandedDimension, + ), + ); + } + } + + return { entity, errors }; +} + +function fromTimeDimensionUrlParams( + measures: Map, + preset: V1ExplorePreset, +): { + entity: Partial; + errors: Error[]; +} { + if (preset.timeDimensionMeasure === undefined) { + return { + entity: {}, + errors: [], + }; + } + + const errors: Error[] = []; + + let expandedMeasureName = preset.timeDimensionMeasure; + if (expandedMeasureName && !measures.has(expandedMeasureName)) { + expandedMeasureName = ""; + errors.push(getSingleFieldError("expanded measure", expandedMeasureName)); + } + + // unset + if (expandedMeasureName === "") { + return { + entity: { + tdd: { + expandedMeasureName: "", + chartType: TDDChart.DEFAULT, + pinIndex: -1, + }, + }, + errors, + }; + } + + const entity: Partial = { + tdd: { + expandedMeasureName, + chartType: preset.timeDimensionChartType + ? FromURLParamTDDChartMap[preset.timeDimensionChartType] + : TDDChart.DEFAULT, + pinIndex: preset.timeDimensionPin ? Number(preset.timeDimensionPin) : -1, + }, + }; + + return { + entity, + errors, + }; +} + +function fromPivotUrlParams( + measures: Map, + dimensions: Map, + preset: V1ExplorePreset, +): { + entity: Partial; + errors: Error[]; +} { + const errors: Error[] = []; + + const mapPivotEntry = (entry: string): PivotChipData | undefined => { + if (entry in FromURLParamTimeDimensionMap) { + const grain = FromURLParamTimeDimensionMap[entry]; + return { + id: grain, + title: TIME_GRAIN[grain]?.label, + type: PivotChipType.Time, + }; + } + + if (measures.has(entry)) { + const m = measures.get(entry)!; + return { + id: entry, + title: m.label || m.name || "Unknown", + type: PivotChipType.Measure, + }; + } + + if (dimensions.has(entry)) { + const d = dimensions.get(entry)!; + return { + id: entry, + title: d.label || d.name || "Unknown", + type: PivotChipType.Dimension, + }; + } + + errors.push(getSingleFieldError("pivot entry", entry)); + + return undefined; + }; + + let hasSomePivotFields = false; + + const rowDimensions: PivotChipData[] = []; + if (preset.pivotRows) { + preset.pivotRows.forEach((pivotRow) => { + const chip = mapPivotEntry(pivotRow); + if (!chip) return; + rowDimensions.push(chip); + }); + hasSomePivotFields = true; + } + + const colMeasures: PivotChipData[] = []; + const colDimensions: PivotChipData[] = []; + if (preset.pivotCols) { + preset.pivotCols.forEach((pivotRow) => { + const chip = mapPivotEntry(pivotRow); + if (!chip) return; + if (chip.type === PivotChipType.Measure) { + colMeasures.push(chip); + } else { + colDimensions.push(chip); + } + }); + hasSomePivotFields = true; + } + + if (!hasSomePivotFields) { + return { + entity: {}, + errors, + }; + } + + return { + entity: { + pivot: { + active: preset.view === V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, + rows: { + dimension: rowDimensions, + }, + columns: { + measure: colMeasures, + dimension: colDimensions, + }, + // TODO: other fields + expanded: {}, + sorting: [], + columnPage: 1, + rowPage: 1, + enableComparison: false, + activeCell: null, + rowJoinType: "nest", + }, + }, + errors, + }; +} diff --git a/web-common/src/features/dashboards/url-state/convertURLToExplorePreset.ts b/web-common/src/features/dashboards/url-state/convertURLToExplorePreset.ts new file mode 100644 index 00000000000..8080bde3f44 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/convertURLToExplorePreset.ts @@ -0,0 +1,302 @@ +import { createAndExpression } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; +import { + getMultiFieldError, + getSingleFieldError, +} from "@rilldata/web-common/features/dashboards/url-state/error-message-helpers"; +import { convertFilterParamToExpression } from "@rilldata/web-common/features/dashboards/url-state/filters/converters"; +import { + FromURLParamTimeDimensionMap, + FromURLParamViewMap, +} from "@rilldata/web-common/features/dashboards/url-state/mappers"; +import { + getMapFromArray, + getMissingValues, +} from "@rilldata/web-common/lib/arrayUtils"; +import { + type MetricsViewSpecDimensionV2, + type MetricsViewSpecMeasureV2, + V1ExploreComparisonMode, + type V1ExplorePreset, + type V1ExploreSpec, + type V1Expression, + type V1MetricsViewSpec, + V1Operation, +} from "@rilldata/web-common/runtime-client"; + +export function convertURLToExplorePreset( + searchParams: URLSearchParams, + metricsView: V1MetricsViewSpec, + explore: V1ExploreSpec, + basePreset: V1ExplorePreset, +) { + const preset: V1ExplorePreset = { + ...basePreset, + }; + const errors: Error[] = []; + + const measures = getMapFromArray( + metricsView.measures?.filter((m) => explore.measures?.includes(m.name!)) ?? + [], + (m) => m.name!, + ); + const dimensions = getMapFromArray( + metricsView.dimensions?.filter((d) => + explore.dimensions?.includes(d.name!), + ) ?? [], + (d) => d.name!, + ); + + if (searchParams.has("vw")) { + preset.view = FromURLParamViewMap[searchParams.get("vw") as string]; + } + + if (searchParams.has("f")) { + const { expr, errors: filterErrors } = fromFilterUrlParam( + searchParams.get("f") as string, + ); + if (filterErrors) errors.push(...filterErrors); + if (expr) preset.where = expr; + } + + const { preset: trPreset, errors: trErrors } = fromTimeRangesParams( + searchParams, + dimensions, + ); + Object.assign(preset, trPreset); + errors.push(...trErrors); + + const { preset: ovPreset, errors: ovErrors } = fromOverviewUrlParams( + searchParams, + measures, + dimensions, + explore, + ); + Object.assign(preset, ovPreset); + errors.push(...ovErrors); + + const { preset: tddPreset, errors: tddErrors } = fromTimeDimensionUrlParams( + searchParams, + measures, + ); + Object.assign(preset, tddPreset); + errors.push(...tddErrors); + + const { preset: pivotPreset, errors: pivotErrors } = fromPivotUrlParams( + searchParams, + measures, + dimensions, + ); + Object.assign(preset, pivotPreset); + errors.push(...pivotErrors); + + return { preset, errors }; +} + +function fromFilterUrlParam(filter: string): { + expr?: V1Expression; + errors?: Error[]; +} { + try { + let expr = convertFilterParamToExpression(filter); + // if root is not AND/OR then add AND + if ( + expr?.cond?.op !== V1Operation.OPERATION_AND && + expr?.cond?.op !== V1Operation.OPERATION_OR + ) { + expr = createAndExpression([expr]); + } + return { expr }; + } catch (e) { + return { errors: [e] }; + } +} + +function fromTimeRangesParams( + searchParams: URLSearchParams, + dimensions: Map, +) { + const preset: V1ExplorePreset = {}; + const errors: Error[] = []; + + if (searchParams.has("tr")) { + preset.timeRange = searchParams.get("tr") as string; + // TODO: parse and return errors + } + if (searchParams.has("tz")) { + preset.timezone = searchParams.get("tz") as string; + } + + if (searchParams.has("ctr")) { + preset.compareTimeRange = searchParams.get("ctr") as string; + preset.comparisonMode = + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_TIME; + // TODO: parse and return errors + } + if (searchParams.has("cd")) { + const comparisonDimension = searchParams.get("cd") as string; + // unsetting a default from url + if (comparisonDimension === "") { + preset.comparisonDimension = ""; + preset.comparisonMode = + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_NONE; + } else if (dimensions.has(comparisonDimension)) { + preset.comparisonDimension = comparisonDimension; + preset.comparisonMode ??= + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_DIMENSION; + } else { + errors.push( + getSingleFieldError("compare dimension", comparisonDimension), + ); + } + } + + // TODO: grain from time range ? + + return { preset, errors }; +} + +function fromOverviewUrlParams( + searchParams: URLSearchParams, + measures: Map, + dimensions: Map, + explore: V1ExploreSpec, +) { + const preset: V1ExplorePreset = {}; + const errors: Error[] = []; + + if (searchParams.has("o.m")) { + const mes = searchParams.get("o.m") as string; + if (mes === "*") { + preset.measures = explore.measures ?? []; + } else { + const selectedMeasures = mes.split(",").filter((m) => measures.has(m)); + preset.measures = selectedMeasures; + const missingMeasures = getMissingValues( + mes.split(","), + selectedMeasures, + ); + if (missingMeasures.length) { + errors.push(getMultiFieldError("measure", missingMeasures)); + } + } + } + + if (searchParams.has("o.d")) { + const dims = searchParams.get("o.d") as string; + if (dims === "*") { + preset.dimensions = explore.dimensions ?? []; + } else { + const selectedDimensions = dims + .split(",") + .filter((d) => dimensions.has(d)); + preset.dimensions = selectedDimensions; + const missingDimensions = getMissingValues( + dims.split(","), + selectedDimensions, + ); + if (missingDimensions.length) { + errors.push(getMultiFieldError("dimension", missingDimensions)); + } + } + } + + if (searchParams.has("o.sb")) { + const sortBy = searchParams.get("o.sb") as string; + if (measures.has(sortBy)) { + preset.overviewSortBy = sortBy; + } else { + errors.push(getSingleFieldError("sort by measure", sortBy)); + } + } + + if (searchParams.has("o.sd")) { + preset.overviewSortAsc = (searchParams.get("o.sd") as string) === "ASC"; + } + + if (searchParams.has("o.ed")) { + const dim = searchParams.get("o.ed") as string; + if ( + dimensions.has(dim) || + // we are unsetting from a default preset + dim === "" + ) { + preset.overviewExpandedDimension = dim; + } else { + errors.push(getSingleFieldError("expanded dimension", dim)); + } + } + + return { preset, errors }; +} + +function fromTimeDimensionUrlParams( + searchParams: URLSearchParams, + measures: Map, +) { + const preset: V1ExplorePreset = {}; + const errors: Error[] = []; + + if (searchParams.has("tdd.m")) { + const mes = searchParams.get("tdd.m") as string; + if ( + measures.has(mes) || + // we are unsetting from a default preset + mes === "" + ) { + preset.timeDimensionMeasure = mes; + } else { + errors.push(getSingleFieldError("expanded measure", mes)); + } + } + if (searchParams.has("tdd.ct")) { + preset.timeDimensionChartType = searchParams.get("tdd.ct") as string; + } + if (searchParams.has("tdd.p")) { + preset.timeDimensionPin = true; + } + + return { + preset, + errors, + }; +} + +function fromPivotUrlParams( + searchParams: URLSearchParams, + measures: Map, + dimensions: Map, +) { + const preset: V1ExplorePreset = {}; + const errors: Error[] = []; + + if (searchParams.has("p.r")) { + const rows = (searchParams.get("p.r") as string).split(","); + const validRows = rows.filter( + (r) => dimensions.has(r) || r in FromURLParamTimeDimensionMap, + ); + preset.pivotRows = validRows; + const missingRows = getMissingValues(rows, validRows); + if (missingRows.length) { + errors.push(getMultiFieldError("pivot row", missingRows)); + } + } + + if (searchParams.has("p.c")) { + const cols = (searchParams.get("p.c") as string).split(","); + const validCols = cols.filter( + (r) => + dimensions.has(r) || + measures.has(r) || + r in FromURLParamTimeDimensionMap, + ); + preset.pivotCols = validCols; + const missingCols = getMissingValues(cols, validCols); + if (missingCols.length) { + errors.push(getMultiFieldError("pivot column", missingCols)); + } + } + + // TODO: other fields + + return { preset, errors }; +} diff --git a/web-common/src/features/dashboards/url-state/defaults.ts b/web-common/src/features/dashboards/url-state/defaults.ts index bc1930beec5..3d6faca1b1e 100644 --- a/web-common/src/features/dashboards/url-state/defaults.ts +++ b/web-common/src/features/dashboards/url-state/defaults.ts @@ -1,6 +1,19 @@ import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; +import { + V1ExploreComparisonMode, + V1ExploreWebView, +} from "@rilldata/web-common/runtime-client"; +export const URLStateDefaultView = + V1ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW; + +export const URLStateDefaultTimeRange = "inf"; export const URLStateDefaultTimezone = "UTC"; + +export const URLStateDefaultCompareMode = + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_NONE; + export const URLStateDefaultSortDirection = SortDirection.DESCENDING; + export const URLStateDefaultTDDChartType = TDDChart.DEFAULT; diff --git a/web-common/src/features/dashboards/url-state/error-message-helpers.ts b/web-common/src/features/dashboards/url-state/error-message-helpers.ts new file mode 100644 index 00000000000..42cd50308f4 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/error-message-helpers.ts @@ -0,0 +1,10 @@ +export function getSingleFieldError(fieldLabel: string, field: string) { + return new Error(`Select ${fieldLabel}: "${field}" is not valid.`); +} + +export function getMultiFieldError(fieldLabel: string, fields: string[]) { + const plural = fields.length > 1; + return new Error( + `Select ${fieldLabel}${plural ? "s" : ""}: "${fields.join(",")}" ${plural ? "are" : "is"} not valid.`, + ); +} diff --git a/web-common/src/features/dashboards/url-state/getBasePreset.ts b/web-common/src/features/dashboards/url-state/getBasePreset.ts new file mode 100644 index 00000000000..1c54e40a594 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/getBasePreset.ts @@ -0,0 +1,51 @@ +import { createAndExpression } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; +import { + URLStateDefaultTDDChartType, + URLStateDefaultTimeRange, + URLStateDefaultTimezone, +} from "@rilldata/web-common/features/dashboards/url-state/defaults"; +import { ToURLParamTDDChartMap } from "@rilldata/web-common/features/dashboards/url-state/mappers"; +import type { LocalUserPreferences } from "@rilldata/web-common/features/dashboards/user-preferences"; +import { + V1ExploreComparisonMode, + type V1ExplorePreset, + type V1ExploreSpec, + V1ExploreWebView, +} from "@rilldata/web-common/runtime-client"; + +export function getBasePreset( + explore: V1ExploreSpec, + preferences: LocalUserPreferences, +) { + const basePreset: V1ExplorePreset = { + view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW, + where: createAndExpression([]), + + measures: explore.measures, + dimensions: explore.dimensions, + + timeRange: URLStateDefaultTimeRange, + timezone: preferences.timeZone ?? URLStateDefaultTimezone, + timeGrain: "", + comparisonMode: V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_NONE, + compareTimeRange: "", + comparisonDimension: "", + + overviewSortBy: explore.measures?.[0], + overviewSortAsc: false, + overviewExpandedDimension: "", + + timeDimensionMeasure: "", + timeDimensionChartType: ToURLParamTDDChartMap[URLStateDefaultTDDChartType], + timeDimensionPin: false, + + pivotCols: [], + pivotRows: [], + pivotSortBy: "", + pivotSortAsc: false, + + ...(explore.defaultPreset ?? {}), + }; + + return basePreset; +} diff --git a/web-common/src/features/dashboards/url-state/toUrl.ts b/web-common/src/features/dashboards/url-state/toUrl.ts index 722f020d010..cb6ee77b582 100644 --- a/web-common/src/features/dashboards/url-state/toUrl.ts +++ b/web-common/src/features/dashboards/url-state/toUrl.ts @@ -80,10 +80,23 @@ function toTimeRangesUrl( searchParams.set("tz", metrics.selectedTimezone); } - if (metrics.selectedComparisonTimeRange?.name !== preset.compareTimeRange) { + console.log(metrics.showTimeComparison, metrics.selectedComparisonTimeRange); + if ( + (preset.compareTimeRange !== undefined && + metrics.selectedComparisonTimeRange?.name !== preset.compareTimeRange) || + (preset.compareTimeRange === undefined && + !!metrics.selectedComparisonTimeRange?.name) + ) { searchParams.set("ctr", metrics.selectedComparisonTimeRange?.name ?? ""); } - if (metrics.selectedComparisonDimension !== preset.comparisonDimension) { + if ( + // if preset has a compare dimension then only set if selected is not the same + (preset.comparisonDimension !== undefined && + metrics.selectedComparisonDimension !== preset.comparisonDimension) || + // else if there is no default then set if there was a selected compare dimension + (preset.comparisonDimension === undefined && + !!metrics.selectedComparisonDimension) + ) { searchParams.set("cd", metrics.selectedComparisonDimension ?? ""); } diff --git a/web-common/src/features/dashboards/url-state/url-state-test-data.ts b/web-common/src/features/dashboards/url-state/url-state-test-data.ts index 39ccccf2a6e..5865c7a0dd3 100644 --- a/web-common/src/features/dashboards/url-state/url-state-test-data.ts +++ b/web-common/src/features/dashboards/url-state/url-state-test-data.ts @@ -1,16 +1,30 @@ +import { createAndExpression } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; +import type { DashboardTimeControls } from "@rilldata/web-common/lib/time/types"; +import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; export const URLStateTestMetricsExplorerEntity: Partial = { + activePage: DashboardState_ActivePage.DEFAULT, + visibleMeasureKeys: new Set(["impressions", "bid_price"]), allMeasuresVisible: true, visibleDimensionKeys: new Set(["publisher", "domain"]), allDimensionsVisible: true, + whereFilter: createAndExpression([]), + dimensionThresholdFilters: [], + selectedDimensionName: "", selectedTimezone: "UTC", sortDirection: 2, + selectedTimeRange: { + name: "inf", + } as DashboardTimeControls, + selectedComparisonDimension: "", + selectedComparisonTimeRange: undefined, + showTimeComparison: false, leaderboardMeasureName: "impressions", diff --git a/web-common/src/features/dashboards/url-state/url-state.spec.ts b/web-common/src/features/dashboards/url-state/url-state.spec.ts index c33ee34de1b..c8f94155283 100644 --- a/web-common/src/features/dashboards/url-state/url-state.spec.ts +++ b/web-common/src/features/dashboards/url-state/url-state.spec.ts @@ -16,7 +16,9 @@ import { AD_BIDS_PUBLISHER_DIMENSION, } from "@rilldata/web-common/features/dashboards/stores/test-data/data"; import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; -import { getMetricsExplorerFromUrl } from "@rilldata/web-common/features/dashboards/url-state/fromUrl"; +import { convertPresetToMetricsExplore } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToMetricsExplore"; +import { convertURLToExplorePreset } from "@rilldata/web-common/features/dashboards/url-state/convertURLToExplorePreset"; +import { getBasePreset } from "@rilldata/web-common/features/dashboards/url-state/getBasePreset"; import { getUrlFromMetricsExplorer } from "@rilldata/web-common/features/dashboards/url-state/toUrl"; import { URLStateTestMetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/url-state/url-state-test-data"; import { @@ -454,13 +456,19 @@ function testEntity( expect(url.toString()).to.eq(expectedUrl); - const { entity: actualEntity } = getMetricsExplorerFromUrl( + const { preset: presetFromUrl } = convertURLToExplorePreset( url.searchParams, AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, explore, - preset ?? {}, + getBasePreset(explore, {}), ); - expect(actualEntity).toEqual({ + const { entity: entityFromPreset } = convertPresetToMetricsExplore( + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + explore, + presetFromUrl, + ); + + expect(entityFromPreset).toEqual({ ...URLStateTestMetricsExplorerEntity, ...entity, }); diff --git a/web-common/src/features/dashboards/user-preferences.ts b/web-common/src/features/dashboards/user-preferences.ts index 06c20677258..eb33bb2995e 100644 --- a/web-common/src/features/dashboards/user-preferences.ts +++ b/web-common/src/features/dashboards/user-preferences.ts @@ -1,6 +1,6 @@ import { localStorageStore } from "@rilldata/web-common/lib/store-utils"; import { getLocalIANA } from "@rilldata/web-common/lib/time/timezone"; -import type { Readable, Writable } from "svelte/store"; +import { get, type Readable, type Writable } from "svelte/store"; /** * TODO: We should create a single user preference store for all dashboards @@ -13,9 +13,9 @@ export interface LocalUserPreferences { } let localUserPreferences: Writable; -export function initLocalUserPreferenceStore(metricsViewName: string) { +export function initLocalUserPreferenceStore(exploreName: string) { localUserPreferences = localStorageStore( - `${metricsViewName}-userPreference`, + `${exploreName}-userPreference`, { timeZone: getLocalIANA(), }, @@ -40,6 +40,25 @@ function localUserPreferencesActions() { }; } +export function getLocalUserPreferencesState( + exploreName: string, +): LocalUserPreferences { + const localstorageUserPreference = localStorage.getItem( + `${exploreName}-userPreference`, + ); + if (localstorageUserPreference) { + try { + return JSON.parse(localstorageUserPreference); + } catch { + // TODO + } + } + + return { + timeZone: getLocalIANA(), + }; +} + export function getLocalUserPreferences(): Readable & ReturnType { return { diff --git a/web-common/src/lib/arrayUtils.ts b/web-common/src/lib/arrayUtils.ts index 089a12e6b27..0a6caf14ec4 100644 --- a/web-common/src/lib/arrayUtils.ts +++ b/web-common/src/lib/arrayUtils.ts @@ -40,3 +40,10 @@ export function arrayOrderedEquals(src: T[], tar: T[]) { } return true; } + +/** + * Returns values in tar that are missing in src. + */ +export function getMissingValues(src: T[], tar: T[]) { + return tar.filter((v) => !src.includes(v)); +} diff --git a/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts b/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts index dc8d9440d77..0e21138c3c5 100644 --- a/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts +++ b/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts @@ -2386,24 +2386,29 @@ export class ExplorePreset extends Message { */ measuresSelector?: FieldSelector; + /** + * @generated from field: optional rill.runtime.v1.Expression where = 25; + */ + where?: Expression; + /** * Time range for the explore. * It corresponds to the `range` property of the explore's `time_ranges`. * If not found in `time_ranges`, it should be added to the list. * - * @generated from field: string time_range = 6; + * @generated from field: optional string time_range = 6; */ - timeRange = ""; + timeRange?: string; /** - * @generated from field: string timezone = 11; + * @generated from field: optional string timezone = 11; */ - timezone = ""; + timezone?: string; /** - * @generated from field: string time_grain = 12; + * @generated from field: optional string time_grain = 12; */ - timeGrain = ""; + timeGrain?: string; /** * Comparison mode. @@ -2420,44 +2425,44 @@ export class ExplorePreset extends Message { /** * If comparison_mode is EXPLORE_COMPARISON_MODE_DIMENSION, this indicates the dimension to use. * - * @generated from field: string comparison_dimension = 8; + * @generated from field: optional string comparison_dimension = 8; */ - comparisonDimension = ""; + comparisonDimension?: string; /** - * @generated from field: rill.runtime.v1.ExploreWebView view = 14; + * @generated from field: optional rill.runtime.v1.ExploreWebView view = 14; */ - view = ExploreWebView.EXPLORE_ACTIVE_PAGE_UNSPECIFIED; + view?: ExploreWebView; /** - * @generated from field: string overview_sort_by = 15; + * @generated from field: optional string overview_sort_by = 15; */ - overviewSortBy = ""; + overviewSortBy?: string; /** - * @generated from field: bool overview_sort_asc = 16; + * @generated from field: optional bool overview_sort_asc = 16; */ - overviewSortAsc = false; + overviewSortAsc?: boolean; /** - * @generated from field: string overview_expanded_dimension = 17; + * @generated from field: optional string overview_expanded_dimension = 17; */ - overviewExpandedDimension = ""; + overviewExpandedDimension?: string; /** - * @generated from field: string time_dimension_measure = 18; + * @generated from field: optional string time_dimension_measure = 18; */ - timeDimensionMeasure = ""; + timeDimensionMeasure?: string; /** - * @generated from field: string time_dimension_chart_type = 19; + * @generated from field: optional string time_dimension_chart_type = 19; */ - timeDimensionChartType = ""; + timeDimensionChartType?: string; /** - * @generated from field: bool time_dimension_pin = 20; + * @generated from field: optional bool time_dimension_pin = 20; */ - timeDimensionPin = false; + timeDimensionPin?: boolean; /** * @generated from field: repeated string pivot_rows = 21; @@ -2470,14 +2475,14 @@ export class ExplorePreset extends Message { pivotCols: string[] = []; /** - * @generated from field: string pivot_sort_by = 23; + * @generated from field: optional string pivot_sort_by = 23; */ - pivotSortBy = ""; + pivotSortBy?: string; /** - * @generated from field: bool pivot_sort_asc = 24; + * @generated from field: optional bool pivot_sort_asc = 24; */ - pivotSortAsc = false; + pivotSortAsc?: boolean; constructor(data?: PartialMessage) { super(); @@ -2491,23 +2496,24 @@ export class ExplorePreset extends Message { { no: 9, name: "dimensions_selector", kind: "message", T: FieldSelector }, { no: 4, name: "measures", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, { no: 10, name: "measures_selector", kind: "message", T: FieldSelector }, - { no: 6, name: "time_range", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 11, name: "timezone", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 12, name: "time_grain", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 25, name: "where", kind: "message", T: Expression, opt: true }, + { no: 6, name: "time_range", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 11, name: "timezone", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 12, name: "time_grain", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, { no: 7, name: "comparison_mode", kind: "enum", T: proto3.getEnumType(ExploreComparisonMode) }, { no: 13, name: "compare_time_range", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 8, name: "comparison_dimension", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 14, name: "view", kind: "enum", T: proto3.getEnumType(ExploreWebView) }, - { no: 15, name: "overview_sort_by", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 16, name: "overview_sort_asc", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - { no: 17, name: "overview_expanded_dimension", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 18, name: "time_dimension_measure", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 19, name: "time_dimension_chart_type", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 20, name: "time_dimension_pin", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, + { no: 8, name: "comparison_dimension", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 14, name: "view", kind: "enum", T: proto3.getEnumType(ExploreWebView), opt: true }, + { no: 15, name: "overview_sort_by", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 16, name: "overview_sort_asc", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, + { no: 17, name: "overview_expanded_dimension", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 18, name: "time_dimension_measure", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 19, name: "time_dimension_chart_type", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 20, name: "time_dimension_pin", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, { no: 21, name: "pivot_rows", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, { no: 22, name: "pivot_cols", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, - { no: 23, name: "pivot_sort_by", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 24, name: "pivot_sort_asc", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, + { no: 23, name: "pivot_sort_by", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 24, name: "pivot_sort_asc", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): ExplorePreset { diff --git a/web-common/src/runtime-client/gen/index.schemas.ts b/web-common/src/runtime-client/gen/index.schemas.ts index 2742dae2bbb..363bb717e30 100644 --- a/web-common/src/runtime-client/gen/index.schemas.ts +++ b/web-common/src/runtime-client/gen/index.schemas.ts @@ -1854,6 +1854,7 @@ export interface V1ExplorePreset { /** Measures to show. If `measures_selector` is set, this will only be set in `state.valid_spec`. */ measures?: string[]; measuresSelector?: V1FieldSelector; + where?: V1Expression; /** Time range for the explore. It corresponds to the `range` property of the explore's `time_ranges`. If not found in `time_ranges`, it should be added to the list. */ diff --git a/web-local/src/routes/(viz)/explore/[name]/+page.svelte b/web-local/src/routes/(viz)/explore/[name]/+page.svelte index 3cc4ed5b9e5..ab4d4b98de1 100644 --- a/web-local/src/routes/(viz)/explore/[name]/+page.svelte +++ b/web-local/src/routes/(viz)/explore/[name]/+page.svelte @@ -6,7 +6,6 @@ import { selectedMockUserStore } from "@rilldata/web-common/features/dashboards/granular-access-policies/stores"; import DashboardURLStateSync from "@rilldata/web-common/features/dashboards/url-state/DashboardURLStateSync.svelte"; import StateManagersProvider from "@rilldata/web-common/features/dashboards/state-managers/StateManagersProvider.svelte"; - import DashboardStateProvider from "@rilldata/web-common/features/dashboards/stores/DashboardStateProvider.svelte"; import { useProjectParser } from "@rilldata/web-common/features/entity-management/resource-selectors"; import { useExploreValidSpec } from "@rilldata/web-common/features/explores/selectors"; import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; @@ -41,8 +40,6 @@ ); $: mockUserHasNoAccess = $selectedMockUserStore && $exploreQuery.error?.response?.status === 404; - - console.log("???"); @@ -70,13 +67,11 @@ {:else} {#key exploreName} - - - - - - - + + + + + {/key} {/if} diff --git a/web-local/src/routes/(viz)/explore/[name]/+page.ts b/web-local/src/routes/(viz)/explore/[name]/+page.ts index f2eeaa78b06..6783cacd5f5 100644 --- a/web-local/src/routes/(viz)/explore/[name]/+page.ts +++ b/web-local/src/routes/(viz)/explore/[name]/+page.ts @@ -1,5 +1,8 @@ import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; -import { getMetricsExplorerFromUrl } from "@rilldata/web-common/features/dashboards/url-state/fromUrl"; +import { convertPresetToMetricsExplore } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToMetricsExplore"; +import { convertURLToExplorePreset } from "@rilldata/web-common/features/dashboards/url-state/convertURLToExplorePreset"; +import { getBasePreset } from "@rilldata/web-common/features/dashboards/url-state/getBasePreset"; +import { getLocalUserPreferencesState } from "@rilldata/web-common/features/dashboards/user-preferences"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient.js"; import { getRuntimeServiceGetExploreQueryKey, @@ -45,25 +48,37 @@ export const load = async ({ params, depends, url }) => { } let partialMetrics: Partial = {}; + const errors: Error[] = []; if ( metricsViewResource.metricsView.state?.validSpec && exploreResource.explore.state?.validSpec && url ) { - const { entity, errors } = getMetricsExplorerFromUrl( + const { preset, errors: errorsFromPreset } = convertURLToExplorePreset( url.searchParams, metricsViewResource.metricsView.state.validSpec, exploreResource.explore.state.validSpec, - exploreResource.explore.state.validSpec.defaultPreset ?? {}, + getBasePreset( + exploreResource.explore.state.validSpec, + getLocalUserPreferencesState(exploreName), + ), ); + errors.push(...errorsFromPreset); + const { entity, errors: errorsFromEntity } = + convertPresetToMetricsExplore( + metricsViewResource.metricsView.state.validSpec, + exploreResource.explore.state.validSpec, + preset, + ); + errors.push(...errorsFromEntity); partialMetrics = entity; - if (errors.length) console.log(errors); // TODO } return { explore: exploreResource, metricsView: metricsViewResource, partialMetrics, + errors, }; } catch (e) { console.error(e); From c12d20ef1ac712347954a3b75217945b27d9d0c1 Mon Sep 17 00:00:00 2001 From: Aditya Hegde Date: Thu, 24 Oct 2024 16:27:08 +0530 Subject: [PATCH 09/17] Use base preset to generate init explore --- .../stores/dashboard-store-defaults.ts | 72 ++++--------- .../url-state/DashboardURLStateSync.svelte | 1 - .../convertPresetToMetricsExplore.ts | 33 +++++- .../features/dashboards/url-state/fromUrl.ts | 2 +- .../features/dashboards/url-state/toUrl.ts | 9 +- .../url-state/url-state-test-data.ts | 54 ---------- .../dashboards/url-state/url-state.spec.ts | 101 +++++++++++++----- .../features/dashboards/user-preferences.ts | 2 +- .../src/routes/(viz)/explore/[name]/+page.ts | 21 +--- 9 files changed, 134 insertions(+), 161 deletions(-) delete mode 100644 web-common/src/features/dashboards/url-state/url-state-test-data.ts diff --git a/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts b/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts index c4dfcfa2b8e..3edc9d9c671 100644 --- a/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts +++ b/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts @@ -10,7 +10,12 @@ import { } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { getPersistentDashboardState } from "@rilldata/web-common/features/dashboards/stores/persistent-dashboard-state"; import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; -import { getLocalUserPreferences } from "@rilldata/web-common/features/dashboards/user-preferences"; +import { convertPresetToMetricsExplore } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToMetricsExplore"; +import { getBasePreset } from "@rilldata/web-common/features/dashboards/url-state/getBasePreset"; +import { + getLocalUserPreferences, + getLocalUserPreferencesState, +} from "@rilldata/web-common/features/dashboards/user-preferences"; import { getTimeComparisonParametersForComponent } from "@rilldata/web-common/lib/time/comparisons"; import { DEFAULT_TIME_RANGES } from "@rilldata/web-common/lib/time/config"; import { getDefaultTimeGrain } from "@rilldata/web-common/lib/time/grains"; @@ -41,7 +46,8 @@ export function setDefaultTimeRange( !fullTimeRange.timeRangeSummary?.max ) return; - const timeZone = get(getLocalUserPreferences()).timeZone; + const timeZone = + explorePreset?.timezone || get(getLocalUserPreferences()).timeZone; const fullTimeStart = new Date(fullTimeRange.timeRangeSummary.min); const fullTimeEnd = new Date(fullTimeRange.timeRangeSummary.max); const timeRange = isoDurationToFullTimeRange( @@ -140,68 +146,26 @@ export function getDefaultMetricsExplorerEntity( explore: V1ExploreSpec, fullTimeRange: V1MetricsViewTimeRangeResponse | undefined, ): MetricsExplorerEntity { - const defaultMeasureNames = - explore?.defaultPreset?.measures ?? explore?.measures ?? []; - - const defaultDimNames = - explore?.defaultPreset?.dimensions ?? explore?.dimensions ?? []; - - const metricsExplorer: MetricsExplorerEntity = { + const { entity: baseEntity } = convertPresetToMetricsExplore( + metricsView, + explore, + getBasePreset(explore, getLocalUserPreferencesState(name)), + ); + const metricsExplorer = { + // fields filled here are the ones that are not stored and loaded to/from URL name, - visibleMeasureKeys: new Set( - defaultMeasureNames - .map((dm) => normaliseName(dm, metricsView.measures)) - .filter((dm) => !!dm) as string[], - ), - allMeasuresVisible: defaultMeasureNames.length === explore.measures?.length, - visibleDimensionKeys: new Set( - defaultDimNames - .map((dd) => normaliseName(dd, metricsView.dimensions)) - .filter((dd) => !!dd) as string[], - ), - allDimensionsVisible: defaultDimNames.length === explore.dimensions?.length, - leaderboardMeasureName: defaultMeasureNames[0], - whereFilter: createAndExpression([]), havingFilter: createAndExpression([]), - dimensionThresholdFilters: [], dimensionFilterExcludeMode: new Map(), leaderboardContextColumn: LeaderboardContextColumn.HIDDEN, dashboardSortType: SortType.VALUE, sortDirection: SortDirection.DESCENDING, - selectedTimezone: "UTC", - selectedTimeRange: undefined, - activePage: DashboardState_ActivePage.DEFAULT, - selectedComparisonDimension: undefined, - selectedDimensionName: undefined, - - showTimeComparison: false, dimensionSearchText: "", temporaryFilterName: null, - tdd: { - chartType: TDDChart.DEFAULT, - expandedMeasureName: "", - pinIndex: -1, - }, - pivot: { - active: false, - rows: { - dimension: [], - }, - columns: { - dimension: [], - measure: [], - }, - rowJoinType: "nest", - expanded: {}, - sorting: [], - rowPage: 1, - enableComparison: true, - columnPage: 1, - activeCell: null, - }, contextColumnWidths: { ...contextColWidthDefaults }, - }; + + ...baseEntity, + } as MetricsExplorerEntity; // set time range related stuff setDefaultTimeRange(explore?.defaultPreset, metricsExplorer, fullTimeRange); setDefaultComparison(metricsView, explore, metricsExplorer, fullTimeRange); diff --git a/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte b/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte index 96ec3fbfd5d..c5975db1459 100644 --- a/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte +++ b/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte @@ -73,7 +73,6 @@ } } - console.log(partialMetrics); metricsExplorerStore.mergePartialExplorerEntity( $exploreName, partialMetrics, diff --git a/web-common/src/features/dashboards/url-state/convertPresetToMetricsExplore.ts b/web-common/src/features/dashboards/url-state/convertPresetToMetricsExplore.ts index 87a0794d78b..6bc57728506 100644 --- a/web-common/src/features/dashboards/url-state/convertPresetToMetricsExplore.ts +++ b/web-common/src/features/dashboards/url-state/convertPresetToMetricsExplore.ts @@ -6,15 +6,18 @@ import { import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; +import { convertURLToExplorePreset } from "@rilldata/web-common/features/dashboards/url-state/convertURLToExplorePreset"; import { getMultiFieldError, getSingleFieldError, } from "@rilldata/web-common/features/dashboards/url-state/error-message-helpers"; +import { getBasePreset } from "@rilldata/web-common/features/dashboards/url-state/getBasePreset"; import { FromURLParamTDDChartMap, FromURLParamTimeDimensionMap, ToActivePageViewMap, } from "@rilldata/web-common/features/dashboards/url-state/mappers"; +import { getLocalUserPreferencesState } from "@rilldata/web-common/features/dashboards/user-preferences"; import { getMapFromArray, getMissingValues, @@ -32,6 +35,30 @@ import { type V1MetricsViewSpec, } from "@rilldata/web-common/runtime-client"; +export function convertURLToMetricsExplore( + exploreName: string, + searchParams: URLSearchParams, + metricsView: V1MetricsViewSpec, + explore: V1ExploreSpec, +) { + const errors: Error[] = []; + const { preset, errors: errorsFromPreset } = convertURLToExplorePreset( + searchParams, + metricsView, + explore, + // TODO: can we cache this per explore? + getBasePreset(explore, getLocalUserPreferencesState(exploreName)), + ); + errors.push(...errorsFromPreset); + const { entity, errors: errorsFromEntity } = convertPresetToMetricsExplore( + metricsView, + explore, + preset, + ); + errors.push(...errorsFromEntity); + return { entity, errors }; +} + /** * Converts a V1ExplorePreset to our internal metrics explore state. * V1ExplorePreset could come from url state, bookmark, alert or report. @@ -147,7 +174,7 @@ function fromTimeRangesParams( ) { // unset all comparison setting if mode is none entity.selectedComparisonTimeRange = undefined; - entity.selectedComparisonDimension = ""; + entity.selectedComparisonDimension = undefined; entity.showTimeComparison = false; } @@ -228,7 +255,7 @@ function fromOverviewUrlParams( if (preset.overviewExpandedDimension !== undefined) { if (preset.overviewExpandedDimension === "") { - entity.selectedDimensionName = ""; + entity.selectedDimensionName = undefined; // if preset didnt have a view then this is a dimension table unset. if ( preset.view === V1ExploreWebView.EXPLORE_ACTIVE_PAGE_UNSPECIFIED || @@ -403,7 +430,7 @@ function fromPivotUrlParams( sorting: [], columnPage: 1, rowPage: 1, - enableComparison: false, + enableComparison: true, activeCell: null, rowJoinType: "nest", }, diff --git a/web-common/src/features/dashboards/url-state/fromUrl.ts b/web-common/src/features/dashboards/url-state/fromUrl.ts index 7841f31ee21..5508fc77350 100644 --- a/web-common/src/features/dashboards/url-state/fromUrl.ts +++ b/web-common/src/features/dashboards/url-state/fromUrl.ts @@ -390,7 +390,7 @@ function fromPivotUrlParams( sorting: [], columnPage: 1, rowPage: 1, - enableComparison: false, + enableComparison: true, activeCell: null, rowJoinType: "nest", }; diff --git a/web-common/src/features/dashboards/url-state/toUrl.ts b/web-common/src/features/dashboards/url-state/toUrl.ts index cb6ee77b582..e282105d0d4 100644 --- a/web-common/src/features/dashboards/url-state/toUrl.ts +++ b/web-common/src/features/dashboards/url-state/toUrl.ts @@ -8,6 +8,7 @@ import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashbo import { URLStateDefaultSortDirection, URLStateDefaultTDDChartType, + URLStateDefaultTimeRange, URLStateDefaultTimezone, } from "@rilldata/web-common/features/dashboards/url-state/defaults"; import { convertExpressionToFilterParam } from "@rilldata/web-common/features/dashboards/url-state/filters/converters"; @@ -64,10 +65,12 @@ function toTimeRangesUrl( preset: V1ExplorePreset, ) { if ( - metrics.selectedTimeRange?.name && - metrics.selectedTimeRange?.name !== preset.timeRange + (preset.timeRange !== undefined && + metrics.selectedTimeRange?.name !== preset.timeRange) || + (preset.timeRange === undefined && + metrics.selectedTimeRange?.name !== URLStateDefaultTimeRange) ) { - searchParams.set("tr", metrics.selectedTimeRange.name); + searchParams.set("tr", metrics.selectedTimeRange?.name ?? ""); } if ( // if preset has timezone then only set if selected is not the same diff --git a/web-common/src/features/dashboards/url-state/url-state-test-data.ts b/web-common/src/features/dashboards/url-state/url-state-test-data.ts deleted file mode 100644 index 5865c7a0dd3..00000000000 --- a/web-common/src/features/dashboards/url-state/url-state-test-data.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { createAndExpression } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; -import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; -import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; -import type { DashboardTimeControls } from "@rilldata/web-common/lib/time/types"; -import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; - -export const URLStateTestMetricsExplorerEntity: Partial = - { - activePage: DashboardState_ActivePage.DEFAULT, - - visibleMeasureKeys: new Set(["impressions", "bid_price"]), - allMeasuresVisible: true, - visibleDimensionKeys: new Set(["publisher", "domain"]), - allDimensionsVisible: true, - - whereFilter: createAndExpression([]), - dimensionThresholdFilters: [], - - selectedDimensionName: "", - selectedTimezone: "UTC", - sortDirection: 2, - selectedTimeRange: { - name: "inf", - } as DashboardTimeControls, - selectedComparisonDimension: "", - selectedComparisonTimeRange: undefined, - showTimeComparison: false, - - leaderboardMeasureName: "impressions", - - pivot: { - active: false, - activeCell: null, - columnPage: 1, - columns: { - dimension: [], - measure: [], - }, - enableComparison: false, - expanded: {}, - rowJoinType: "nest", - rowPage: 1, - rows: { - dimension: [], - }, - sorting: [], - }, - - tdd: { - chartType: TDDChart.DEFAULT, - expandedMeasureName: "", - pinIndex: -1, - }, - }; diff --git a/web-common/src/features/dashboards/url-state/url-state.spec.ts b/web-common/src/features/dashboards/url-state/url-state.spec.ts index c8f94155283..b4c1bf642ac 100644 --- a/web-common/src/features/dashboards/url-state/url-state.spec.ts +++ b/web-common/src/features/dashboards/url-state/url-state.spec.ts @@ -10,17 +10,19 @@ import { AD_BIDS_BID_PRICE_MEASURE, AD_BIDS_DOMAIN_DIMENSION, AD_BIDS_EXPLORE_INIT, + AD_BIDS_EXPLORE_NAME, AD_BIDS_IMPRESSIONS_MEASURE, AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, - AD_BIDS_NAME, AD_BIDS_PUBLISHER_DIMENSION, + AD_BIDS_TIME_RANGE_SUMMARY, } from "@rilldata/web-common/features/dashboards/stores/test-data/data"; import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; -import { convertPresetToMetricsExplore } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToMetricsExplore"; -import { convertURLToExplorePreset } from "@rilldata/web-common/features/dashboards/url-state/convertURLToExplorePreset"; -import { getBasePreset } from "@rilldata/web-common/features/dashboards/url-state/getBasePreset"; +import { convertURLToMetricsExplore } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToMetricsExplore"; import { getUrlFromMetricsExplorer } from "@rilldata/web-common/features/dashboards/url-state/toUrl"; -import { URLStateTestMetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/url-state/url-state-test-data"; +import { + getLocalUserPreferences, + initLocalUserPreferenceStore, +} from "@rilldata/web-common/features/dashboards/user-preferences"; import { type DashboardTimeControls, TimeRangePreset, @@ -32,9 +34,21 @@ import { V1ExploreWebView, V1TimeGrain, } from "@rilldata/web-common/runtime-client"; -import { describe, expect, it } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Human readable URL state", () => { + beforeAll(() => { + initLocalUserPreferenceStore(AD_BIDS_EXPLORE_NAME); + }); + + beforeEach(() => { + getLocalUserPreferences().updateTimeZone("UTC"); + localStorage.setItem( + `${AD_BIDS_EXPLORE_NAME}-userPreference`, + `{"timezone":"UTC"}`, + ); + }); + it("filter", () => { testEntity( { @@ -254,7 +268,7 @@ describe("Human readable URL state", () => { testEntity( { activePage: DashboardState_ActivePage.DEFAULT, - selectedDimensionName: "", + selectedDimensionName: undefined, }, "http://localhost/?o.ed=", { @@ -362,7 +376,7 @@ describe("Human readable URL state", () => { sorting: [], columnPage: 1, rowPage: 1, - enableComparison: false, + enableComparison: true, activeCell: null, rowJoinType: "nest", }, @@ -412,7 +426,7 @@ describe("Human readable URL state", () => { sorting: [], columnPage: 1, rowPage: 1, - enableComparison: false, + enableComparison: true, activeCell: null, rowJoinType: "nest", }, @@ -438,17 +452,20 @@ function testEntity( ...AD_BIDS_EXPLORE_INIT, ...(preset ? { defaultPreset: preset } : {}), }; - const defaultEntity = { - ...getDefaultMetricsExplorerEntity( - AD_BIDS_NAME, - AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, - explore, - undefined, - ), - ...entity, - }; + const initEntity = getDefaultMetricsExplorerEntity( + AD_BIDS_EXPLORE_NAME, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + explore, + AD_BIDS_TIME_RANGE_SUMMARY, + ); + cleanMetricsExplore(initEntity); + + // load url params with update metrics state getUrlFromMetricsExplorer( - defaultEntity, + { + ...initEntity, + ...entity, + }, url.searchParams, explore, preset ?? {}, @@ -456,20 +473,50 @@ function testEntity( expect(url.toString()).to.eq(expectedUrl); - const { preset: presetFromUrl } = convertURLToExplorePreset( + // get back the entity from url params + const { entity: entityFromUrl } = convertURLToMetricsExplore( + AD_BIDS_EXPLORE_NAME, url.searchParams, AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, explore, - getBasePreset(explore, {}), ); - const { entity: entityFromPreset } = convertPresetToMetricsExplore( + + // assert that the entity we got back matches the original + expect(entityFromUrl).toEqual({ + ...initEntity, + ...entity, + }); + + // go back to default url + const defaultUrl = new URL("http://localhost"); + const { entity: entityFromDefaultUrl } = convertURLToMetricsExplore( + AD_BIDS_EXPLORE_NAME, + defaultUrl.searchParams, AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, explore, - presetFromUrl, ); - expect(entityFromPreset).toEqual({ - ...URLStateTestMetricsExplorerEntity, - ...entity, - }); + // assert that the entity we got back matches the original + expect(entityFromDefaultUrl).toEqual(initEntity); +} + +// cleans up any UI only state from MetricsExplorerEntity +function cleanMetricsExplore( + metricsExplorerEntity: Partial, +) { + delete metricsExplorerEntity.name; + delete metricsExplorerEntity.dimensionFilterExcludeMode; + delete metricsExplorerEntity.havingFilter; + delete metricsExplorerEntity.temporaryFilterName; + delete metricsExplorerEntity.contextColumnWidths; + delete metricsExplorerEntity.dimensionSearchText; + metricsExplorerEntity.selectedTimeRange = { + name: metricsExplorerEntity.selectedTimeRange?.name ?? "inf", + } as DashboardTimeControls; + delete metricsExplorerEntity.lastDefinedScrubRange; + + //TODO + delete metricsExplorerEntity.selectedScrubRange; + delete metricsExplorerEntity.leaderboardContextColumn; + delete metricsExplorerEntity.dashboardSortType; } diff --git a/web-common/src/features/dashboards/user-preferences.ts b/web-common/src/features/dashboards/user-preferences.ts index eb33bb2995e..a34ec679aea 100644 --- a/web-common/src/features/dashboards/user-preferences.ts +++ b/web-common/src/features/dashboards/user-preferences.ts @@ -1,6 +1,6 @@ import { localStorageStore } from "@rilldata/web-common/lib/store-utils"; import { getLocalIANA } from "@rilldata/web-common/lib/time/timezone"; -import { get, type Readable, type Writable } from "svelte/store"; +import { type Readable, type Writable } from "svelte/store"; /** * TODO: We should create a single user preference store for all dashboards diff --git a/web-local/src/routes/(viz)/explore/[name]/+page.ts b/web-local/src/routes/(viz)/explore/[name]/+page.ts index 6783cacd5f5..3203ccb9cbd 100644 --- a/web-local/src/routes/(viz)/explore/[name]/+page.ts +++ b/web-local/src/routes/(viz)/explore/[name]/+page.ts @@ -1,8 +1,5 @@ import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; -import { convertPresetToMetricsExplore } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToMetricsExplore"; -import { convertURLToExplorePreset } from "@rilldata/web-common/features/dashboards/url-state/convertURLToExplorePreset"; -import { getBasePreset } from "@rilldata/web-common/features/dashboards/url-state/getBasePreset"; -import { getLocalUserPreferencesState } from "@rilldata/web-common/features/dashboards/user-preferences"; +import { convertURLToMetricsExplore } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToMetricsExplore"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient.js"; import { getRuntimeServiceGetExploreQueryKey, @@ -54,24 +51,14 @@ export const load = async ({ params, depends, url }) => { exploreResource.explore.state?.validSpec && url ) { - const { preset, errors: errorsFromPreset } = convertURLToExplorePreset( + const { entity, errors: errorsFromConvert } = convertURLToMetricsExplore( + exploreName, url.searchParams, metricsViewResource.metricsView.state.validSpec, exploreResource.explore.state.validSpec, - getBasePreset( - exploreResource.explore.state.validSpec, - getLocalUserPreferencesState(exploreName), - ), ); - errors.push(...errorsFromPreset); - const { entity, errors: errorsFromEntity } = - convertPresetToMetricsExplore( - metricsViewResource.metricsView.state.validSpec, - exploreResource.explore.state.validSpec, - preset, - ); - errors.push(...errorsFromEntity); partialMetrics = entity; + errors.push(...errorsFromConvert); } return { From cab692cdbb7dde31dc1b6e0857fc27099d84a4b0 Mon Sep 17 00:00:00 2001 From: Aditya Hegde Date: Wed, 30 Oct 2024 12:06:17 +0530 Subject: [PATCH 10/17] Add mapping from legacy proto state --- .../dashboards/proto-state/fromProto.ts | 33 +- .../convertLegacyStateToExplorePreset.ts | 338 ++++++++++++++++++ .../url-state/convertURLToExplorePreset.ts | 12 +- .../features/dashboards/url-state/defaults.ts | 1 + .../url-state/error-message-helpers.ts | 2 +- .../dashboards/url-state/legacyMappers.ts | 16 + 6 files changed, 377 insertions(+), 25 deletions(-) create mode 100644 web-common/src/features/dashboards/url-state/convertLegacyStateToExplorePreset.ts create mode 100644 web-common/src/features/dashboards/url-state/legacyMappers.ts diff --git a/web-common/src/features/dashboards/proto-state/fromProto.ts b/web-common/src/features/dashboards/proto-state/fromProto.ts index f49a164ea93..c28b16ef54f 100644 --- a/web-common/src/features/dashboards/proto-state/fromProto.ts +++ b/web-common/src/features/dashboards/proto-state/fromProto.ts @@ -134,7 +134,9 @@ export function getDashboardStateFromProto( dashboard.compareTimeRange, ); // backwards compatibility - correctComparisonTimeRange(entity.selectedComparisonTimeRange); + entity.selectedComparisonTimeRange.name = correctComparisonTimeRange( + entity.selectedComparisonTimeRange.name, + ); } if (dashboard.showTimeComparison !== undefined) { entity.showTimeComparison = Boolean(dashboard.showTimeComparison); @@ -223,7 +225,9 @@ export function base64ToProto(message: string) { return protoBase64.dec(message); } -function fromExpressionProto(expression: Expression): V1Expression | undefined { +export function fromExpressionProto( + expression: Expression, +): V1Expression | undefined { switch (expression.expression.case) { case "ident": return { @@ -422,29 +426,22 @@ function fromPivotProto( }; } -function correctComparisonTimeRange( - comparisonTimeRange: DashboardTimeControls, -) { - switch (comparisonTimeRange.name as string) { +export function correctComparisonTimeRange(name: string) { + switch (name) { case "CONTIGUOUS": - comparisonTimeRange.name = TimeComparisonOption.CONTIGUOUS; - break; + return TimeComparisonOption.CONTIGUOUS; case "P1D": - comparisonTimeRange.name = TimeComparisonOption.DAY; - break; + return TimeComparisonOption.DAY; case "P1W": - comparisonTimeRange.name = TimeComparisonOption.WEEK; - break; + return TimeComparisonOption.WEEK; case "P1M": - comparisonTimeRange.name = TimeComparisonOption.MONTH; - break; + return TimeComparisonOption.MONTH; case "P3M": - comparisonTimeRange.name = TimeComparisonOption.QUARTER; - break; + return TimeComparisonOption.QUARTER; case "P1Y": - comparisonTimeRange.name = TimeComparisonOption.YEAR; - break; + return TimeComparisonOption.YEAR; } + return name; } function chartTypeMap(chartType: string | undefined): TDDChart { diff --git a/web-common/src/features/dashboards/url-state/convertLegacyStateToExplorePreset.ts b/web-common/src/features/dashboards/url-state/convertLegacyStateToExplorePreset.ts new file mode 100644 index 00000000000..4164ef01488 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/convertLegacyStateToExplorePreset.ts @@ -0,0 +1,338 @@ +import { FromProtoTimeGrainMap } from "@rilldata/web-common/features/dashboards/proto-state/enum-maps"; +import { + correctComparisonTimeRange, + fromExpressionProto, +} from "@rilldata/web-common/features/dashboards/proto-state/fromProto"; +import { + createAndExpression, + createSubQueryExpression, + getAllIdentifiers, +} from "@rilldata/web-common/features/dashboards/stores/filter-utils"; +import { + ExplorePresetDefaultChartType, + URLStateDefaultTimezone, +} from "@rilldata/web-common/features/dashboards/url-state/defaults"; +import { + getMultiFieldError, + getSingleFieldError, +} from "@rilldata/web-common/features/dashboards/url-state/error-message-helpers"; +import { mapLegacyChartType } from "@rilldata/web-common/features/dashboards/url-state/legacyMappers"; +import { + FromURLParamTimeDimensionMap, + ToURLParamTimeDimensionMap, +} from "@rilldata/web-common/features/dashboards/url-state/mappers"; +import { + getMapFromArray, + getMissingValues, +} from "@rilldata/web-common/lib/arrayUtils"; +import type { TimeGrain } from "@rilldata/web-common/proto/gen/rill/runtime/v1/time_grain_pb"; +import { + type DashboardState, + DashboardState_LeaderboardSortDirection, + PivotElement, +} from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; +import { + type MetricsViewSpecDimensionV2, + type MetricsViewSpecMeasureV2, + V1ExploreComparisonMode, + type V1ExplorePreset, + type V1ExploreSpec, + type V1Expression, + type V1MetricsViewSpec, +} from "@rilldata/web-common/runtime-client"; + +export function convertLegacyStateToExplorePreset( + legacyState: DashboardState, + metricsView: V1MetricsViewSpec, + explore: V1ExploreSpec, + basePreset: V1ExplorePreset, +) { + const preset: V1ExplorePreset = { + ...basePreset, + }; + const errors: Error[] = []; + + const measures = getMapFromArray( + metricsView.measures?.filter((m) => explore.measures?.includes(m.name!)) ?? + [], + (m) => m.name!, + ); + const dimensions = getMapFromArray( + metricsView.dimensions?.filter((d) => + explore.dimensions?.includes(d.name!), + ) ?? [], + (d) => d.name!, + ); + + if (legacyState.filters) { + // TODO + } else if (legacyState.where) { + preset.where = fromExpressionProto(legacyState.where); + } + if (legacyState.having) { + preset.where ??= createAndExpression([]); + const exprs = preset.where?.cond?.exprs as V1Expression[]; + legacyState.having.forEach((h) => { + const expr = fromExpressionProto(h.filter); + exprs.push( + createSubQueryExpression(h.name, getAllIdentifiers(expr), expr), + ); + }); + } + + const { entity: trEntity, errors: trErrors } = fromLegacyTimeRangeFields( + legacyState, + dimensions, + ); + Object.assign(entity, trEntity); + errors.push(...trErrors); + + const { preset: ovPreset, errors: ovErrors } = fromLegacyOverviewFields( + legacyState, + dimensions, + measures, + explore, + ); + Object.assign(preset, ovPreset); + errors.push(...ovErrors); + + const { preset: tddPreset, errors: tddErrors } = + fromLegacyTimeDimensionFields(legacyState, measures); + Object.assign(preset, tddPreset); + errors.push(...tddErrors); + + const { preset: pivotPreset, errors: pivotErrors } = fromLegacyPivotFields( + legacyState, + measures, + dimensions, + ); + Object.assign(preset, pivotPreset); + errors.push(...pivotErrors); + + return { preset, errors }; +} + +function fromLegacyTimeRangeFields( + legacyState: DashboardState, + dimensions: Map, +) { + const preset: V1ExplorePreset = {}; + const errors: Error[] = []; + + if (legacyState.timeRange?.name) { + preset.timeRange = legacyState.timeRange.name; + // TODO: custom time range + } + if (legacyState.timeGrain) { + // TODO + // preset.timeGrain = legacyState.timeGrain; + } + + if (legacyState.compareTimeRange?.name) { + preset.compareTimeRange = correctComparisonTimeRange( + legacyState.compareTimeRange.name, + ); + // TODO: custom time range + } + if (legacyState.showTimeComparison !== undefined) { + preset.comparisonMode = + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_TIME; + } + + if (legacyState.comparisonDimension) { + if (dimensions.has(legacyState.comparisonDimension)) { + preset.comparisonDimension = legacyState.comparisonDimension; + preset.comparisonMode = + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_DIMENSION; + } else { + errors.push( + getSingleFieldError( + "compare dimension", + legacyState.comparisonDimension, + ), + ); + } + } else { + // older state would unset comparison dimension when empty + preset.comparisonDimension = ""; + } + + if (!preset.comparisonMode) { + // if there was no comparison in legacyState it was an unset to `None` + preset.comparisonMode = + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_NONE; + } + + preset.timezone = legacyState.selectedTimezone ?? URLStateDefaultTimezone; + + // TODO: scrubRange + + return { preset, errors }; +} + +function fromLegacyOverviewFields( + legacyState: DashboardState, + measures: Map, + dimensions: Map, + explore: V1ExploreSpec, +) { + const preset: V1ExplorePreset = {}; + const errors: Error[] = []; + + if (legacyState.allMeasuresVisible) { + preset.measures = explore.measures ?? []; + } else if (legacyState.visibleMeasures?.length) { + preset.measures = legacyState.visibleMeasures.filter((m) => + measures.has(m), + ); + const missingMeasures = getMissingValues( + legacyState.visibleMeasures, + preset.measures, + ); + if (missingMeasures.length) { + errors.push(getMultiFieldError("measure", missingMeasures)); + } + } + + if (legacyState.allDimensionsVisible) { + preset.dimensions = explore.dimensions ?? []; + } else if (legacyState.visibleDimensions?.length) { + preset.dimensions = legacyState.visibleDimensions.filter((d) => + dimensions.has(d), + ); + const missingDimensions = getMissingValues( + legacyState.visibleDimensions, + preset.dimensions, + ); + if (missingDimensions.length) { + errors.push(getMultiFieldError("dimension", missingDimensions)); + } + } + + if (legacyState.leaderboardMeasure !== undefined) { + if (measures.has(legacyState.leaderboardMeasure)) { + preset.overviewSortBy = legacyState.leaderboardMeasure; + } else { + errors.push( + getMultiFieldError("sort by measure", legacyState.leaderboardMeasure), + ); + } + } + + if (legacyState.leaderboardSortDirection) { + preset.overviewSortAsc = + legacyState.leaderboardSortDirection === + DashboardState_LeaderboardSortDirection.ASCENDING; + } + + if (legacyState.leaderboardContextColumn !== undefined) { + // TODO + } + + if (legacyState.leaderboardSortType) { + // TODO + } + + return { preset, errors }; +} + +function fromLegacyTimeDimensionFields( + legacyState: DashboardState, + measures: Map, +) { + const preset: V1ExplorePreset = {}; + const errors: Error[] = []; + + if (!legacyState.expandedMeasure) { + if (dashboard.activePage) { + preset.timeDimensionMeasure = ""; + preset.timeDimensionPin = false; + preset.timeDimensionChartType = ExplorePresetDefaultChartType; + } + return { preset, errors }; + } + + if (!measures.has(legacyState.expandedMeasure)) { + errors.push( + getSingleFieldError("expanded measure", legacyState.expandedMeasure), + ); + return { preset, errors }; + } + + preset.timeDimensionMeasure = legacyState.expandedMeasure; + preset.timeDimensionPin = false; // TODO + preset.timeDimensionChartType = mapLegacyChartType(legacyState.chartType); + + return { preset, errors }; +} + +function fromLegacyPivotFields( + legacyState: DashboardState, + measures: Map, +) { + const preset: V1ExplorePreset = {}; + const errors: Error[] = []; + + const mapTimeDimension = (grain: TimeGrain) => + ToURLParamTimeDimensionMap[FromProtoTimeGrainMap[grain]]; + const mapAllDimension = (dimension: PivotElement) => { + if (dimension?.element.case === "pivotTimeDimension") { + return mapTimeDimension(dimension?.element.value); + } else { + return dimension?.element.value as string; + } + }; + + if ( + legacyState.pivotRowAllDimensions?.length && + legacyState.pivotColumnAllDimensions?.length + ) { + preset.pivotRows = legacyState.pivotRowAllDimensions.map(mapAllDimension); + preset.pivotCols = + legacyState.pivotColumnAllDimensions.map(mapAllDimension); + } else if ( + // backwards compatibility for state + legacyState.pivotRowDimensions?.length || + legacyState.pivotRowTimeDimensions?.length || + legacyState.pivotColumnDimensions?.length || + legacyState.pivotColumnTimeDimensions?.length + ) { + preset.pivotRows = [ + ...legacyState.pivotRowTimeDimensions.map(mapTimeDimension), + ...legacyState.pivotRowDimensions, + ]; + preset.pivotCols = [ + ...legacyState.pivotColumnTimeDimensions.map(mapTimeDimension), + ...legacyState.pivotColumnDimensions, + ]; + } + + if (preset.pivotRows?.length) { + const allValues = preset.pivotRows; + preset.pivotRows = preset.pivotRows.filter( + (r) => dimensions.has(r) || r in FromURLParamTimeDimensionMap, + ); + const missingRows = getMissingValues(allValues, preset.pivotRows); + if (missingRows.length) { + errors.push(getMultiFieldError("pivot row", missingRows)); + } + } + + if (preset.pivotCols?.length) { + const allValues = preset.pivotCols; + preset.pivotCols = preset.pivotCols.filter( + (c) => + dimensions.has(c) || + measures.has(c) || + c in FromURLParamTimeDimensionMap, + ); + const missingCols = getMissingValues(allValues, preset.pivotCols); + if (missingCols.length) { + errors.push(getMultiFieldError("pivot column", missingCols)); + } + } + + // TODO: other fields + + return { preset, errors }; +} diff --git a/web-common/src/features/dashboards/url-state/convertURLToExplorePreset.ts b/web-common/src/features/dashboards/url-state/convertURLToExplorePreset.ts index 8080bde3f44..0d6255ea13e 100644 --- a/web-common/src/features/dashboards/url-state/convertURLToExplorePreset.ts +++ b/web-common/src/features/dashboards/url-state/convertURLToExplorePreset.ts @@ -128,7 +128,7 @@ function fromTimeRangesParams( if (searchParams.has("ctr")) { preset.compareTimeRange = searchParams.get("ctr") as string; - preset.comparisonMode = + preset.comparisonMode ??= V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_TIME; // TODO: parse and return errors } @@ -137,7 +137,7 @@ function fromTimeRangesParams( // unsetting a default from url if (comparisonDimension === "") { preset.comparisonDimension = ""; - preset.comparisonMode = + preset.comparisonMode ??= V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_NONE; } else if (dimensions.has(comparisonDimension)) { preset.comparisonDimension = comparisonDimension; @@ -284,10 +284,10 @@ function fromPivotUrlParams( if (searchParams.has("p.c")) { const cols = (searchParams.get("p.c") as string).split(","); const validCols = cols.filter( - (r) => - dimensions.has(r) || - measures.has(r) || - r in FromURLParamTimeDimensionMap, + (c) => + dimensions.has(c) || + measures.has(c) || + c in FromURLParamTimeDimensionMap, ); preset.pivotCols = validCols; const missingCols = getMissingValues(cols, validCols); diff --git a/web-common/src/features/dashboards/url-state/defaults.ts b/web-common/src/features/dashboards/url-state/defaults.ts index 3d6faca1b1e..3e1ab01026d 100644 --- a/web-common/src/features/dashboards/url-state/defaults.ts +++ b/web-common/src/features/dashboards/url-state/defaults.ts @@ -17,3 +17,4 @@ export const URLStateDefaultCompareMode = export const URLStateDefaultSortDirection = SortDirection.DESCENDING; export const URLStateDefaultTDDChartType = TDDChart.DEFAULT; +export const ExplorePresetDefaultChartType = "timeseries"; diff --git a/web-common/src/features/dashboards/url-state/error-message-helpers.ts b/web-common/src/features/dashboards/url-state/error-message-helpers.ts index 42cd50308f4..2eb45bb6c8c 100644 --- a/web-common/src/features/dashboards/url-state/error-message-helpers.ts +++ b/web-common/src/features/dashboards/url-state/error-message-helpers.ts @@ -5,6 +5,6 @@ export function getSingleFieldError(fieldLabel: string, field: string) { export function getMultiFieldError(fieldLabel: string, fields: string[]) { const plural = fields.length > 1; return new Error( - `Select ${fieldLabel}${plural ? "s" : ""}: "${fields.join(",")}" ${plural ? "are" : "is"} not valid.`, + `Selected ${fieldLabel}${plural ? "s" : ""}: "${fields.join(",")}" ${plural ? "are" : "is"} not valid.`, ); } diff --git a/web-common/src/features/dashboards/url-state/legacyMappers.ts b/web-common/src/features/dashboards/url-state/legacyMappers.ts new file mode 100644 index 00000000000..c2944614102 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/legacyMappers.ts @@ -0,0 +1,16 @@ +import { ExplorePresetDefaultChartType } from "@rilldata/web-common/features/dashboards/url-state/defaults"; + +const LegacyCharTypeToPresetChartType: Record = { + default: ExplorePresetDefaultChartType, + grouped_bar: "bar", + stacked_bar: "stacked_bar", + stacked_area: "stacked_area", +}; +export function mapLegacyChartType(chartType: string | undefined) { + if (!chartType) { + return ExplorePresetDefaultChartType; + } + return ( + LegacyCharTypeToPresetChartType[chartType] ?? ExplorePresetDefaultChartType + ); +} From 90bec55fbcb36c2320c1107f14bcc301af598ddc Mon Sep 17 00:00:00 2001 From: Aditya Hegde Date: Fri, 1 Nov 2024 18:07:36 +0530 Subject: [PATCH 11/17] Add tests for legacy proto parsing --- .../dashboards/stores/test-data/data.ts | 52 ++ .../dashboards/stores/test-data/helpers.ts | 75 +- .../convertLegacyStateToExplorePreset.ts | 71 +- .../convertPresetToMetricsExplore.ts | 4 +- .../url-state/convertURLToExplorePreset.ts | 41 + .../dashboards/url-state/url-state.spec.ts | 869 +++++++++--------- .../src/routes/(viz)/explore/[name]/+page.ts | 2 + 7 files changed, 652 insertions(+), 462 deletions(-) diff --git a/web-common/src/features/dashboards/stores/test-data/data.ts b/web-common/src/features/dashboards/stores/test-data/data.ts index ba49adba441..b3cfeeb5996 100644 --- a/web-common/src/features/dashboards/stores/test-data/data.ts +++ b/web-common/src/features/dashboards/stores/test-data/data.ts @@ -1,7 +1,9 @@ +import { PivotChipType } from "@rilldata/web-common/features/dashboards/pivot/types"; import { createAndExpression, createInExpression, } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { getLocalIANA } from "@rilldata/web-common/lib/time/timezone"; import { getOffset, @@ -13,6 +15,7 @@ import { TimeOffsetType, TimeRangePreset, } from "@rilldata/web-common/lib/time/types"; +import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; import type { MetricsViewSpecDimensionV2, MetricsViewSpecMeasureV2, @@ -316,3 +319,52 @@ export const CUSTOM_TEST_CONTROLS = { start: TestTimeConstants.LAST_18_HOURS, end: TestTimeConstants.LAST_12_HOURS, } as DashboardTimeControls; + +export const AD_BIDS_PIVOT_ENTITY: Partial = { + activePage: DashboardState_ActivePage.PIVOT, + pivot: { + active: true, + rows: { + dimension: [ + { + id: AD_BIDS_PUBLISHER_DIMENSION, + type: PivotChipType.Dimension, + title: AD_BIDS_PUBLISHER_DIMENSION, + }, + { + id: V1TimeGrain.TIME_GRAIN_HOUR, + type: PivotChipType.Time, + title: "hour", + }, + ], + }, + columns: { + measure: [ + { + id: AD_BIDS_IMPRESSIONS_MEASURE, + type: PivotChipType.Measure, + title: AD_BIDS_IMPRESSIONS_MEASURE, + }, + ], + dimension: [ + { + id: AD_BIDS_DOMAIN_DIMENSION, + type: PivotChipType.Dimension, + title: AD_BIDS_DOMAIN_DIMENSION, + }, + { + id: V1TimeGrain.TIME_GRAIN_DAY, + type: PivotChipType.Time, + title: "day", + }, + ], + }, + expanded: {}, + sorting: [], + columnPage: 1, + rowPage: 1, + enableComparison: true, + activeCell: null, + rowJoinType: "nest", + }, +}; 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 5d01bfd39d9..e95668bdec9 100644 --- a/web-common/src/features/dashboards/stores/test-data/helpers.ts +++ b/web-common/src/features/dashboards/stores/test-data/helpers.ts @@ -1,4 +1,5 @@ import { QueryClient } from "@rilldata/svelte-query"; +import { PivotChipType } from "@rilldata/web-common/features/dashboards/pivot/types"; import { createStateManagers } from "@rilldata/web-common/features/dashboards/state-managers/state-managers"; import { getDefaultMetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; import { metricsExplorerStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; @@ -16,11 +17,14 @@ import { AD_BIDS_TIME_RANGE_SUMMARY, } from "@rilldata/web-common/features/dashboards/stores/test-data/data"; import type { ExploreValidSpecResponse } from "@rilldata/web-common/features/explores/selectors"; +import { TIME_GRAIN } from "@rilldata/web-common/lib/time/config"; import type { DashboardTimeControls } from "@rilldata/web-common/lib/time/types"; -import type { - V1ExploreSpec, - V1Expression, - V1MetricsViewSpec, +import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; +import { + type V1ExploreSpec, + type V1Expression, + type V1MetricsViewSpec, + V1TimeGrain, } from "@rilldata/web-common/runtime-client"; import { deepClone } from "@vitest/utils"; import { get } from "svelte/store"; @@ -161,3 +165,66 @@ export function getPartialDashboard( ); return partialDashboard; } + +export function getPivotedPartialDashboard( + pivotRowDimensions: string[], + pivotRowTimeDimensions: V1TimeGrain[], + pivotColumnMeasures: string[], + pivotColumnDimensions: string[], + pivotColumnTimeDimensions: V1TimeGrain[], +): Partial { + const hasPivot = + !!pivotRowDimensions.length || + !!pivotRowTimeDimensions.length || + !!pivotColumnMeasures.length || + !!pivotColumnDimensions.length || + !!pivotColumnTimeDimensions.length; + return { + activePage: hasPivot + ? DashboardState_ActivePage.PIVOT + : DashboardState_ActivePage.DEFAULT, + pivot: { + active: hasPivot, + rows: { + dimension: [ + ...pivotRowDimensions.map((r) => ({ + id: r, + type: PivotChipType.Dimension, + title: r, + })), + ...pivotRowTimeDimensions.map((g) => ({ + id: g, + type: PivotChipType.Time, + title: TIME_GRAIN[g]?.label.toString(), + })), + ], + }, + columns: { + measure: pivotColumnMeasures.map((m) => ({ + id: m, + type: PivotChipType.Measure, + title: m, + })), + dimension: [ + ...pivotColumnDimensions.map((r) => ({ + id: r, + type: PivotChipType.Dimension, + title: r, + })), + ...pivotColumnTimeDimensions.map((g) => ({ + id: g, + type: PivotChipType.Time, + title: TIME_GRAIN[g]?.label.toString(), + })), + ], + }, + expanded: {}, + sorting: [], + columnPage: 1, + rowPage: 1, + enableComparison: true, + activeCell: null, + rowJoinType: "nest", + }, + }; +} diff --git a/web-common/src/features/dashboards/url-state/convertLegacyStateToExplorePreset.ts b/web-common/src/features/dashboards/url-state/convertLegacyStateToExplorePreset.ts index 4164ef01488..2fae6bd39b0 100644 --- a/web-common/src/features/dashboards/url-state/convertLegacyStateToExplorePreset.ts +++ b/web-common/src/features/dashboards/url-state/convertLegacyStateToExplorePreset.ts @@ -1,4 +1,5 @@ import { FromProtoTimeGrainMap } from "@rilldata/web-common/features/dashboards/proto-state/enum-maps"; +import { convertFilterToExpression } from "@rilldata/web-common/features/dashboards/proto-state/filter-converter"; import { correctComparisonTimeRange, fromExpressionProto, @@ -8,6 +9,7 @@ import { createSubQueryExpression, getAllIdentifiers, } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { ExplorePresetDefaultChartType, URLStateDefaultTimezone, @@ -18,6 +20,7 @@ import { } from "@rilldata/web-common/features/dashboards/url-state/error-message-helpers"; import { mapLegacyChartType } from "@rilldata/web-common/features/dashboards/url-state/legacyMappers"; import { + FromActivePageMap, FromURLParamTimeDimensionMap, ToURLParamTimeDimensionMap, } from "@rilldata/web-common/features/dashboards/url-state/mappers"; @@ -28,6 +31,7 @@ import { import type { TimeGrain } from "@rilldata/web-common/proto/gen/rill/runtime/v1/time_grain_pb"; import { type DashboardState, + DashboardState_ActivePage, DashboardState_LeaderboardSortDirection, PivotElement, } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; @@ -37,6 +41,7 @@ import { V1ExploreComparisonMode, type V1ExplorePreset, type V1ExploreSpec, + V1ExploreWebView, type V1Expression, type V1MetricsViewSpec, } from "@rilldata/web-common/runtime-client"; @@ -64,8 +69,14 @@ export function convertLegacyStateToExplorePreset( (d) => d.name!, ); + if (legacyState.activePage !== DashboardState_ActivePage.UNSPECIFIED) { + preset.view = FromActivePageMap[legacyState.activePage]; + } + if (legacyState.filters) { - // TODO + // backwards compatibility for our older filter format + preset.where = convertFilterToExpression(legacyState.filters); + // TODO: correct older values that would have strings for non-strings } else if (legacyState.where) { preset.where = fromExpressionProto(legacyState.where); } @@ -73,6 +84,7 @@ export function convertLegacyStateToExplorePreset( preset.where ??= createAndExpression([]); const exprs = preset.where?.cond?.exprs as V1Expression[]; legacyState.having.forEach((h) => { + if (!h.filter) return; const expr = fromExpressionProto(h.filter); exprs.push( createSubQueryExpression(h.name, getAllIdentifiers(expr), expr), @@ -80,17 +92,17 @@ export function convertLegacyStateToExplorePreset( }); } - const { entity: trEntity, errors: trErrors } = fromLegacyTimeRangeFields( + const { preset: trPreset, errors: trErrors } = fromLegacyTimeRangeFields( legacyState, dimensions, ); - Object.assign(entity, trEntity); + Object.assign(preset, trPreset); errors.push(...trErrors); const { preset: ovPreset, errors: ovErrors } = fromLegacyOverviewFields( legacyState, - dimensions, measures, + dimensions, explore, ); Object.assign(preset, ovPreset); @@ -134,7 +146,7 @@ function fromLegacyTimeRangeFields( ); // TODO: custom time range } - if (legacyState.showTimeComparison !== undefined) { + if (legacyState.showTimeComparison) { preset.comparisonMode = V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_TIME; } @@ -214,7 +226,7 @@ function fromLegacyOverviewFields( preset.overviewSortBy = legacyState.leaderboardMeasure; } else { errors.push( - getMultiFieldError("sort by measure", legacyState.leaderboardMeasure), + getSingleFieldError("sort by measure", legacyState.leaderboardMeasure), ); } } @@ -233,6 +245,23 @@ function fromLegacyOverviewFields( // TODO } + if (legacyState.selectedDimension) { + if (dimensions.has(legacyState.selectedDimension)) { + preset.overviewExpandedDimension = legacyState.selectedDimension; + } else { + errors.push( + getSingleFieldError( + "expanded dimension", + legacyState.selectedDimension, + ), + ); + } + } else if (legacyState.activePage !== DashboardState_ActivePage.UNSPECIFIED) { + // UNSPECIFIED means it was a partial state stored to proto state + // So anything other than that would need to unset this + preset.overviewExpandedDimension = ""; + } + return { preset, errors }; } @@ -244,7 +273,7 @@ function fromLegacyTimeDimensionFields( const errors: Error[] = []; if (!legacyState.expandedMeasure) { - if (dashboard.activePage) { + if (legacyState.activePage) { preset.timeDimensionMeasure = ""; preset.timeDimensionPin = false; preset.timeDimensionChartType = ExplorePresetDefaultChartType; @@ -269,12 +298,13 @@ function fromLegacyTimeDimensionFields( function fromLegacyPivotFields( legacyState: DashboardState, measures: Map, + dimensions: Map, ) { const preset: V1ExplorePreset = {}; const errors: Error[] = []; const mapTimeDimension = (grain: TimeGrain) => - ToURLParamTimeDimensionMap[FromProtoTimeGrainMap[grain]]; + ToURLParamTimeDimensionMap[FromProtoTimeGrainMap[grain]] ?? ""; const mapAllDimension = (dimension: PivotElement) => { if (dimension?.element.case === "pivotTimeDimension") { return mapTimeDimension(dimension?.element.value); @@ -284,7 +314,7 @@ function fromLegacyPivotFields( }; if ( - legacyState.pivotRowAllDimensions?.length && + legacyState.pivotRowAllDimensions?.length || legacyState.pivotColumnAllDimensions?.length ) { preset.pivotRows = legacyState.pivotRowAllDimensions.map(mapAllDimension); @@ -298,15 +328,24 @@ function fromLegacyPivotFields( legacyState.pivotColumnTimeDimensions?.length ) { preset.pivotRows = [ - ...legacyState.pivotRowTimeDimensions.map(mapTimeDimension), + ...legacyState.pivotRowTimeDimensions + .map(mapTimeDimension) + .filter(Boolean), ...legacyState.pivotRowDimensions, ]; preset.pivotCols = [ - ...legacyState.pivotColumnTimeDimensions.map(mapTimeDimension), + ...legacyState.pivotColumnTimeDimensions + .map(mapTimeDimension) + .filter(Boolean), ...legacyState.pivotColumnDimensions, ]; } + if (legacyState.pivotColumnMeasures?.length) { + preset.pivotCols ??= []; + preset.pivotCols.push(...legacyState.pivotColumnMeasures); + } + if (preset.pivotRows?.length) { const allValues = preset.pivotRows; preset.pivotRows = preset.pivotRows.filter( @@ -332,6 +371,16 @@ function fromLegacyPivotFields( } } + if ( + legacyState.activePage !== DashboardState_ActivePage.PIVOT && + // UNSPECIFIED means it was a partial state stored to proto state + legacyState.activePage !== DashboardState_ActivePage.UNSPECIFIED + ) { + // legacy state would unset when active page is not pivot + preset.pivotRows = []; + preset.pivotCols = []; + } + // TODO: other fields return { preset, errors }; diff --git a/web-common/src/features/dashboards/url-state/convertPresetToMetricsExplore.ts b/web-common/src/features/dashboards/url-state/convertPresetToMetricsExplore.ts index 6bc57728506..220c5a85e02 100644 --- a/web-common/src/features/dashboards/url-state/convertPresetToMetricsExplore.ts +++ b/web-common/src/features/dashboards/url-state/convertPresetToMetricsExplore.ts @@ -361,7 +361,7 @@ function fromPivotUrlParams( const m = measures.get(entry)!; return { id: entry, - title: m.label || m.name || "Unknown", + title: m.displayName || m.name || "Unknown", type: PivotChipType.Measure, }; } @@ -370,7 +370,7 @@ function fromPivotUrlParams( const d = dimensions.get(entry)!; return { id: entry, - title: d.label || d.name || "Unknown", + title: d.displayName || d.name || "Unknown", type: PivotChipType.Dimension, }; } diff --git a/web-common/src/features/dashboards/url-state/convertURLToExplorePreset.ts b/web-common/src/features/dashboards/url-state/convertURLToExplorePreset.ts index 0d6255ea13e..6a0a874b504 100644 --- a/web-common/src/features/dashboards/url-state/convertURLToExplorePreset.ts +++ b/web-common/src/features/dashboards/url-state/convertURLToExplorePreset.ts @@ -1,4 +1,6 @@ +import { base64ToProto } from "@rilldata/web-common/features/dashboards/proto-state/fromProto"; import { createAndExpression } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; +import { convertLegacyStateToExplorePreset } from "@rilldata/web-common/features/dashboards/url-state/convertLegacyStateToExplorePreset"; import { getMultiFieldError, getSingleFieldError, @@ -12,6 +14,7 @@ import { getMapFromArray, getMissingValues, } from "@rilldata/web-common/lib/arrayUtils"; +import { DashboardState } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; import { type MetricsViewSpecDimensionV2, type MetricsViewSpecMeasureV2, @@ -46,6 +49,16 @@ export function convertURLToExplorePreset( (d) => d.name!, ); + // Support legacy dashboard param. + // This will be applied 1st so that any newer params added can be applied as well. + if (searchParams.has("state")) { + const legacyState = searchParams.get("state") as string; + const { preset: presetFromLegacyState, errors: errorsFromLegacyState } = + fromLegacyStateUrlParam(legacyState, metricsView, explore, basePreset); + Object.assign(preset, presetFromLegacyState); + errors.push(...errorsFromLegacyState); + } + if (searchParams.has("vw")) { preset.view = FromURLParamViewMap[searchParams.get("vw") as string]; } @@ -92,6 +105,34 @@ export function convertURLToExplorePreset( return { preset, errors }; } +function fromLegacyStateUrlParam( + legacyState: string, + metricsView: V1MetricsViewSpec, + explore: V1ExploreSpec, + basePreset: V1ExplorePreset, +) { + try { + legacyState = legacyState.includes("%") + ? decodeURIComponent(legacyState) + : legacyState; + const legacyDashboardState = DashboardState.fromBinary( + base64ToProto(legacyState), + ); + + return convertLegacyStateToExplorePreset( + legacyDashboardState, + metricsView, + explore, + basePreset, + ); + } catch (e) { + return { + preset: {}, + errors: [e], // TODO: parse and show meaningful error + }; + } +} + function fromFilterUrlParam(filter: string): { expr?: V1Expression; errors?: Error[]; diff --git a/web-common/src/features/dashboards/url-state/url-state.spec.ts b/web-common/src/features/dashboards/url-state/url-state.spec.ts index b4c1bf642ac..750f65eea60 100644 --- a/web-common/src/features/dashboards/url-state/url-state.spec.ts +++ b/web-common/src/features/dashboards/url-state/url-state.spec.ts @@ -1,5 +1,5 @@ -import { PivotChipType } from "@rilldata/web-common/features/dashboards/pivot/types"; import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; +import { getProtoFromDashboardState } from "@rilldata/web-common/features/dashboards/proto-state/toProto"; import { getDefaultMetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; import { createAndExpression, @@ -16,6 +16,7 @@ import { AD_BIDS_PUBLISHER_DIMENSION, AD_BIDS_TIME_RANGE_SUMMARY, } from "@rilldata/web-common/features/dashboards/stores/test-data/data"; +import { getPivotedPartialDashboard } from "@rilldata/web-common/features/dashboards/stores/test-data/helpers"; import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; import { convertURLToMetricsExplore } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToMetricsExplore"; import { getUrlFromMetricsExplorer } from "@rilldata/web-common/features/dashboards/url-state/toUrl"; @@ -36,6 +37,324 @@ import { } from "@rilldata/web-common/runtime-client"; import { beforeAll, beforeEach, describe, expect, it } from "vitest"; +const TestCases: { + title: string; + entity: Partial; + preset?: V1ExplorePreset; + expectedUrl: string; +}[] = [ + { + title: "filter", + entity: { + whereFilter: createAndExpression([ + createInExpression(AD_BIDS_PUBLISHER_DIMENSION, ["Yahoo"]), + ]), + dimensionThresholdFilters: [], + }, + expectedUrl: "http://localhost/?f=%28publisher+IN+%28%27Yahoo%27%29%29", + }, + + { + title: "Time range without preset", + entity: { + selectedTimeRange: { + name: TimeRangePreset.LAST_4_WEEKS, + } as DashboardTimeControls, + selectedTimezone: "Asia/Kathmandu", + }, + expectedUrl: "http://localhost/?tr=P4W&tz=Asia%2FKathmandu", + }, + { + title: "Time range with preset and state matching preset", + entity: { + selectedTimeRange: { + name: TimeRangePreset.LAST_7_DAYS, + } as DashboardTimeControls, + selectedTimezone: "Asia/Kathmandu", + }, + preset: { + timeRange: "P7D", + timezone: "Asia/Kathmandu", + }, + expectedUrl: "http://localhost/", + }, + { + title: "Time range with preset and state not matching preset", + entity: { + selectedTimeRange: { + name: TimeRangePreset.LAST_4_WEEKS, + } as DashboardTimeControls, + selectedTimezone: "America/Los_Angeles", + }, + preset: { + timeRange: "P7D", + timezone: "Asia/Kathmandu", + }, + expectedUrl: "http://localhost/?tr=P4W&tz=America%2FLos_Angeles", + }, + + { + title: + "Measures/dimensions visibility with no preset and partially visible measures/dimensions in state", + entity: { + visibleMeasureKeys: new Set([AD_BIDS_IMPRESSIONS_MEASURE]), + allMeasuresVisible: false, + visibleDimensionKeys: new Set([AD_BIDS_PUBLISHER_DIMENSION]), + allDimensionsVisible: false, + }, + expectedUrl: "http://localhost/?o.m=impressions&o.d=publisher", + }, + { + title: + "Measures/dimensions visibility with no preset and all measures/dimensions visible in state", + entity: { + visibleMeasureKeys: new Set([ + AD_BIDS_IMPRESSIONS_MEASURE, + AD_BIDS_BID_PRICE_MEASURE, + ]), + allMeasuresVisible: true, + visibleDimensionKeys: new Set([ + AD_BIDS_PUBLISHER_DIMENSION, + AD_BIDS_DOMAIN_DIMENSION, + ]), + allDimensionsVisible: true, + }, + expectedUrl: "http://localhost/", + }, + { + title: + "Measures/dimensions visibility with preset and partially visible measures/dimensions in state matching preset", + entity: { + visibleMeasureKeys: new Set([AD_BIDS_IMPRESSIONS_MEASURE]), + allMeasuresVisible: false, + visibleDimensionKeys: new Set([AD_BIDS_PUBLISHER_DIMENSION]), + allDimensionsVisible: false, + }, + preset: { + measures: [AD_BIDS_IMPRESSIONS_MEASURE], + dimensions: [AD_BIDS_PUBLISHER_DIMENSION], + }, + expectedUrl: "http://localhost/", + }, + { + title: + "Measures/dimensions visibility with preset and all measures/dimensions visible in state not matching preset", + entity: { + visibleMeasureKeys: new Set([ + AD_BIDS_IMPRESSIONS_MEASURE, + AD_BIDS_BID_PRICE_MEASURE, + ]), + allMeasuresVisible: true, + visibleDimensionKeys: new Set([ + AD_BIDS_PUBLISHER_DIMENSION, + AD_BIDS_DOMAIN_DIMENSION, + ]), + allDimensionsVisible: true, + }, + preset: { + measures: [AD_BIDS_IMPRESSIONS_MEASURE], + dimensions: [AD_BIDS_PUBLISHER_DIMENSION], + }, + expectedUrl: "http://localhost/?o.m=*&o.d=*", + }, + + { + title: + "Leaderboard configs with no preset and leaderboard sort measure in state different than default", + entity: { + leaderboardMeasureName: AD_BIDS_BID_PRICE_MEASURE, + sortDirection: SortDirection.ASCENDING, + }, + expectedUrl: "http://localhost/?o.sb=bid_price&o.sd=ASC", + }, + { + title: + "Leaderboard configs with no preset and leaderboard sort measure in state same as default", + entity: { + leaderboardMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, + sortDirection: SortDirection.DESCENDING, + }, + expectedUrl: "http://localhost/", + }, + { + title: + "Leaderboard configs with preset and leaderboard sort measure in state same as preset", + entity: { + leaderboardMeasureName: AD_BIDS_BID_PRICE_MEASURE, + sortDirection: SortDirection.ASCENDING, + }, + preset: { + overviewSortBy: AD_BIDS_BID_PRICE_MEASURE, + overviewSortAsc: true, + }, + expectedUrl: "http://localhost/", + }, + { + title: + "Leaderboard configs with preset and leaderboard sort measure in state different than preset", + entity: { + leaderboardMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, + sortDirection: SortDirection.DESCENDING, + }, + preset: { + overviewSortBy: AD_BIDS_BID_PRICE_MEASURE, + overviewSortAsc: true, + }, + expectedUrl: "http://localhost/?o.sb=impressions&o.sd=DESC", + }, + + { + title: "Dimension table with no preset and dimension table active in state", + entity: { + activePage: DashboardState_ActivePage.DIMENSION_TABLE, + selectedDimensionName: AD_BIDS_PUBLISHER_DIMENSION, + }, + expectedUrl: "http://localhost/?o.ed=publisher", + }, + { + title: + "Dimension table with preset and with dimension table in state same as preset", + entity: { + activePage: DashboardState_ActivePage.DIMENSION_TABLE, + selectedDimensionName: AD_BIDS_DOMAIN_DIMENSION, + }, + preset: { + overviewExpandedDimension: AD_BIDS_DOMAIN_DIMENSION, + }, + expectedUrl: "http://localhost/", + }, + { + title: + "Dimension table with preset and with dimension table in state different than preset", + entity: { + activePage: DashboardState_ActivePage.DIMENSION_TABLE, + selectedDimensionName: AD_BIDS_PUBLISHER_DIMENSION, + }, + preset: { + overviewExpandedDimension: AD_BIDS_DOMAIN_DIMENSION, + }, + expectedUrl: "http://localhost/?o.ed=publisher", + }, + { + title: + "Dimension table with preset and with no dimension table in state different than preset", + entity: { + activePage: DashboardState_ActivePage.DEFAULT, + selectedDimensionName: undefined, + }, + preset: { + overviewExpandedDimension: AD_BIDS_DOMAIN_DIMENSION, + }, + expectedUrl: "http://localhost/?o.ed=", + }, + + { + title: + "Time dimensional details with no preset and has time dimensional details in state", + entity: { + activePage: DashboardState_ActivePage.TIME_DIMENSIONAL_DETAIL, + tdd: { + expandedMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, + chartType: TDDChart.STACKED_BAR, + pinIndex: -1, + }, + }, + expectedUrl: + "http://localhost/?vw=time_dimension&tdd.m=impressions&tdd.ct=stacked_bar", + }, + { + title: + "Time dimensional details with preset and has time dimensional details in state same as presets", + entity: { + activePage: DashboardState_ActivePage.TIME_DIMENSIONAL_DETAIL, + tdd: { + expandedMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, + chartType: TDDChart.STACKED_BAR, + pinIndex: -1, + }, + }, + preset: { + view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_TIME_DIMENSION, + timeDimensionMeasure: AD_BIDS_IMPRESSIONS_MEASURE, + timeDimensionChartType: "stacked_bar", + }, + expectedUrl: "http://localhost/", + }, + { + title: + "Time dimensional details with preset and has time dimensional details in state different than presets", + entity: { + activePage: DashboardState_ActivePage.DEFAULT, + tdd: { + expandedMeasureName: "", + chartType: TDDChart.DEFAULT, + pinIndex: -1, + }, + }, + preset: { + view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_TIME_DIMENSION, + timeDimensionMeasure: AD_BIDS_IMPRESSIONS_MEASURE, + timeDimensionChartType: "stacked_bar", + }, + expectedUrl: "http://localhost/?vw=overview&tdd.m=&tdd.ct=timeseries", + }, + + { + title: "Pivot with no preset and has pivot in state", + entity: getPivotedPartialDashboard( + [AD_BIDS_PUBLISHER_DIMENSION], + [V1TimeGrain.TIME_GRAIN_HOUR], + [AD_BIDS_IMPRESSIONS_MEASURE], + [AD_BIDS_DOMAIN_DIMENSION], + [V1TimeGrain.TIME_GRAIN_DAY], + ), + expectedUrl: + "http://localhost/?vw=pivot&p.r=publisher%2Ctime.hour&p.c=domain%2Ctime.day%2Cimpressions", + }, + { + title: "Pivot with preset and has pivot in state same as preset", + entity: getPivotedPartialDashboard( + [AD_BIDS_PUBLISHER_DIMENSION], + [V1TimeGrain.TIME_GRAIN_HOUR], + [AD_BIDS_IMPRESSIONS_MEASURE], + [AD_BIDS_DOMAIN_DIMENSION], + [V1TimeGrain.TIME_GRAIN_DAY], + ), + preset: { + view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, + pivotRows: ["publisher", "time.hour"], + pivotCols: ["domain", "time.day", "impressions"], + }, + expectedUrl: "http://localhost/", + }, + { + title: "Pivot with preset and pivot in state different as preset", + entity: getPivotedPartialDashboard( + [AD_BIDS_DOMAIN_DIMENSION], + [V1TimeGrain.TIME_GRAIN_DAY], + [AD_BIDS_IMPRESSIONS_MEASURE], + [], + [], + ), + preset: { + view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, + pivotRows: ["publisher", "time.hour"], + pivotCols: ["domain", "time.day", "impressions"], + }, + expectedUrl: "http://localhost/?p.r=domain%2Ctime.day&p.c=impressions", + }, + { + title: "Pivot with preset and no pivot in state different as preset", + entity: getPivotedPartialDashboard([], [], [], [], []), + preset: { + view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, + pivotRows: ["publisher", "time.hour"], + pivotCols: ["domain", "time.day", "impressions"], + }, + expectedUrl: "http://localhost/?vw=overview&p.r=&p.c=", + }, +]; + describe("Human readable URL state", () => { beforeAll(() => { initLocalUserPreferenceStore(AD_BIDS_EXPLORE_NAME); @@ -49,457 +368,117 @@ describe("Human readable URL state", () => { ); }); - it("filter", () => { - testEntity( - { - whereFilter: createAndExpression([ - createInExpression(AD_BIDS_PUBLISHER_DIMENSION, ["Yahoo"]), - ]), - dimensionThresholdFilters: [], - }, - "http://localhost/?f=%28publisher+IN+%28%27Yahoo%27%29%29", - ); - }); - - describe("Time ranges", () => { - it("no preset", () => { - testEntity( - { - selectedTimeRange: { - name: TimeRangePreset.LAST_4_WEEKS, - } as DashboardTimeControls, - selectedTimezone: "Asia/Kathmandu", - }, - "http://localhost/?tr=P4W&tz=Asia%2FKathmandu", - ); - }); - - it("with preset and matching preset", () => { - testEntity( - { - selectedTimeRange: { - name: TimeRangePreset.LAST_7_DAYS, - } as DashboardTimeControls, - selectedTimezone: "Asia/Kathmandu", - }, - "http://localhost/", - { - timeRange: "P7D", - timezone: "Asia/Kathmandu", - }, - ); - }); - - it("with preset and not matching preset", () => { - testEntity( - { - selectedTimeRange: { - name: TimeRangePreset.LAST_4_WEEKS, - } as DashboardTimeControls, - selectedTimezone: "America/Los_Angeles", - }, - "http://localhost/?tr=P4W&tz=America%2FLos_Angeles", - { - timeRange: "P7D", - timezone: "Asia/Kathmandu", - }, - ); - }); - }); - - describe("measures/dimensions visibility", () => { - it("no preset and partially visible measures/dimensions", () => { - testEntity( - { - visibleMeasureKeys: new Set([AD_BIDS_IMPRESSIONS_MEASURE]), - allMeasuresVisible: false, - visibleDimensionKeys: new Set([AD_BIDS_PUBLISHER_DIMENSION]), - allDimensionsVisible: false, - }, - "http://localhost/?o.m=impressions&o.d=publisher", - ); - }); - - it("no preset and all measures/dimensions visible", () => { - testEntity( - { - visibleMeasureKeys: new Set([ - AD_BIDS_IMPRESSIONS_MEASURE, - AD_BIDS_BID_PRICE_MEASURE, - ]), - allMeasuresVisible: true, - visibleDimensionKeys: new Set([ - AD_BIDS_PUBLISHER_DIMENSION, - AD_BIDS_DOMAIN_DIMENSION, - ]), - allDimensionsVisible: true, - }, - "http://localhost/", - ); - }); - - it("with preset and partially visible measures/dimensions, matching preset", () => { - testEntity( - { - visibleMeasureKeys: new Set([AD_BIDS_IMPRESSIONS_MEASURE]), - allMeasuresVisible: false, - visibleDimensionKeys: new Set([AD_BIDS_PUBLISHER_DIMENSION]), - allDimensionsVisible: false, - }, - "http://localhost/", - { - measures: [AD_BIDS_IMPRESSIONS_MEASURE], - dimensions: [AD_BIDS_PUBLISHER_DIMENSION], - }, - ); - }); - - it("with preset and all measures/dimensions visible, not matching preset", () => { - testEntity( - { - visibleMeasureKeys: new Set([ - AD_BIDS_IMPRESSIONS_MEASURE, - AD_BIDS_BID_PRICE_MEASURE, - ]), - allMeasuresVisible: true, - visibleDimensionKeys: new Set([ - AD_BIDS_PUBLISHER_DIMENSION, - AD_BIDS_DOMAIN_DIMENSION, - ]), - allDimensionsVisible: true, - }, - "http://localhost/?o.m=*&o.d=*", - { - measures: [AD_BIDS_IMPRESSIONS_MEASURE], - dimensions: [AD_BIDS_PUBLISHER_DIMENSION], - }, - ); - }); - }); - - describe("leaderboard configs", () => { - it("no preset and leaderboard sort measure different than default", () => { - testEntity( - { - leaderboardMeasureName: AD_BIDS_BID_PRICE_MEASURE, - sortDirection: SortDirection.ASCENDING, - }, - "http://localhost/?o.sb=bid_price&o.sd=ASC", - ); - }); - - it("no preset and leaderboard sort measure same as default", () => { - testEntity( - { - leaderboardMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, - sortDirection: SortDirection.DESCENDING, - }, - "http://localhost/", - ); - }); - - it("with preset and leaderboard sort measure same as preset", () => { - testEntity( - { - leaderboardMeasureName: AD_BIDS_BID_PRICE_MEASURE, - sortDirection: SortDirection.ASCENDING, - }, - "http://localhost/", - { - overviewSortBy: AD_BIDS_BID_PRICE_MEASURE, - overviewSortAsc: true, - }, - ); - }); - - it("with preset and leaderboard sort measure different than preset", () => { - testEntity( - { - leaderboardMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, - sortDirection: SortDirection.DESCENDING, - }, - "http://localhost/?o.sb=impressions&o.sd=DESC", - { - overviewSortBy: AD_BIDS_BID_PRICE_MEASURE, - overviewSortAsc: true, - }, - ); - }); - }); - - describe("dimension table", () => { - it("no preset and with dimension table active", () => { - testEntity( - { - activePage: DashboardState_ActivePage.DIMENSION_TABLE, - selectedDimensionName: AD_BIDS_PUBLISHER_DIMENSION, - }, - "http://localhost/?o.ed=publisher", - ); - }); - - it("with preset and with dimension table same as preset", () => { - testEntity( - { - activePage: DashboardState_ActivePage.DIMENSION_TABLE, - selectedDimensionName: AD_BIDS_DOMAIN_DIMENSION, - }, - "http://localhost/", - { - overviewExpandedDimension: AD_BIDS_DOMAIN_DIMENSION, - }, - ); - }); - - it("with preset and with dimension table different than preset", () => { - testEntity( - { - activePage: DashboardState_ActivePage.DIMENSION_TABLE, - selectedDimensionName: AD_BIDS_PUBLISHER_DIMENSION, - }, - "http://localhost/?o.ed=publisher", - { - overviewExpandedDimension: AD_BIDS_DOMAIN_DIMENSION, - }, - ); - }); - - it("with preset and with no dimension table different than preset", () => { - testEntity( - { - activePage: DashboardState_ActivePage.DEFAULT, - selectedDimensionName: undefined, - }, - "http://localhost/?o.ed=", - { - overviewExpandedDimension: AD_BIDS_DOMAIN_DIMENSION, - }, - ); - }); - }); - - describe("time dimensional details", () => { - it("no preset and has time dimensional details", () => { - testEntity( - { - activePage: DashboardState_ActivePage.TIME_DIMENSIONAL_DETAIL, - tdd: { - expandedMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, - chartType: TDDChart.STACKED_BAR, - pinIndex: -1, - }, - }, - "http://localhost/?vw=time_dimension&tdd.m=impressions&tdd.ct=stacked_bar", - ); - }); - - it("with preset and has time dimensional details same as presets", () => { - testEntity( - { - activePage: DashboardState_ActivePage.TIME_DIMENSIONAL_DETAIL, - tdd: { - expandedMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, - chartType: TDDChart.STACKED_BAR, - pinIndex: -1, - }, - }, - "http://localhost/", - { - view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_TIME_DIMENSION, - timeDimensionMeasure: AD_BIDS_IMPRESSIONS_MEASURE, - timeDimensionChartType: "stacked_bar", - }, - ); - }); - - it("with preset and has time dimensional details different than presets", () => { - testEntity( - { - activePage: DashboardState_ActivePage.DEFAULT, - tdd: { - expandedMeasureName: "", - chartType: TDDChart.DEFAULT, - pinIndex: -1, + describe("Should update url state and restore default state on empty params", () => { + for (const { title, entity, preset, expectedUrl } of TestCases) { + it(title, () => { + const url = new URL("http://localhost"); + const explore: V1ExploreSpec = { + ...AD_BIDS_EXPLORE_INIT, + ...(preset ? { defaultPreset: preset } : {}), + }; + const initEntity = getDefaultMetricsExplorerEntity( + AD_BIDS_EXPLORE_NAME, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + explore, + AD_BIDS_TIME_RANGE_SUMMARY, + ); + cleanMetricsExplore(initEntity); + + // load url params with update metrics state + getUrlFromMetricsExplorer( + { + ...initEntity, + ...entity, }, - }, - "http://localhost/?vw=overview&tdd.m=&tdd.ct=timeseries", - { - view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_TIME_DIMENSION, - timeDimensionMeasure: AD_BIDS_IMPRESSIONS_MEASURE, - timeDimensionChartType: "stacked_bar", - }, - ); - }); + url.searchParams, + explore, + preset ?? {}, + ); + + expect(url.toString()).to.eq(expectedUrl); + + // get back the entity from url params + const { entity: entityFromUrl } = convertURLToMetricsExplore( + AD_BIDS_EXPLORE_NAME, + url.searchParams, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + explore, + ); + + // assert that the entity we got back matches the expected entity + expect(entityFromUrl).toEqual({ + ...initEntity, + ...entity, + }); + + // go back to default url + const defaultUrl = new URL("http://localhost"); + const { entity: entityFromDefaultUrl } = convertURLToMetricsExplore( + AD_BIDS_EXPLORE_NAME, + defaultUrl.searchParams, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + explore, + ); + + // assert that the entity we got back matches the original + expect(entityFromDefaultUrl).toEqual(initEntity); + }); + } }); - describe("pivot", () => { - const PIVOT_ENTITY: Partial = { - activePage: DashboardState_ActivePage.PIVOT, - pivot: { - active: true, - rows: { - dimension: [ - { - id: AD_BIDS_PUBLISHER_DIMENSION, - type: PivotChipType.Dimension, - title: AD_BIDS_PUBLISHER_DIMENSION, - }, - { - id: V1TimeGrain.TIME_GRAIN_HOUR, - type: PivotChipType.Time, - title: "hour", - }, - ], - }, - columns: { - measure: [ - { - id: AD_BIDS_IMPRESSIONS_MEASURE, - type: PivotChipType.Measure, - title: AD_BIDS_IMPRESSIONS_MEASURE, - }, - ], - dimension: [ - { - id: AD_BIDS_DOMAIN_DIMENSION, - type: PivotChipType.Dimension, - title: AD_BIDS_DOMAIN_DIMENSION, - }, - { - id: V1TimeGrain.TIME_GRAIN_DAY, - type: PivotChipType.Time, - title: "day", - }, - ], - }, - expanded: {}, - sorting: [], - columnPage: 1, - rowPage: 1, - enableComparison: true, - activeCell: null, - rowJoinType: "nest", - }, - }; - - it("no preset with pivot", () => { - testEntity( - PIVOT_ENTITY, - "http://localhost/?vw=pivot&p.r=publisher%2Ctime.hour&p.c=domain%2Ctime.day%2Cimpressions", - ); - }); - - it("with preset and pivot same as preset", () => { - testEntity(PIVOT_ENTITY, "http://localhost/", { - view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, - pivotRows: ["publisher", "time.hour"], - pivotCols: ["domain", "time.day", "impressions"], + describe("Should set correct state for legacy protobuf state and restore default state on empty params", () => { + for (const { title, entity, preset } of TestCases) { + it(title, () => { + const url = new URL("http://localhost"); + const explore: V1ExploreSpec = { + ...AD_BIDS_EXPLORE_INIT, + ...(preset ? { defaultPreset: preset } : {}), + }; + const initEntity = getDefaultMetricsExplorerEntity( + AD_BIDS_EXPLORE_NAME, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + explore, + AD_BIDS_TIME_RANGE_SUMMARY, + ); + cleanMetricsExplore(initEntity); + // load url with legacy protobuf state + url.searchParams.set( + "state", + getProtoFromDashboardState({ + ...initEntity, + ...entity, + }), + ); + + // get back the entity from url params + const { entity: entityFromUrl } = convertURLToMetricsExplore( + AD_BIDS_EXPLORE_NAME, + url.searchParams, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + explore, + ); + // assert that the entity we got back matches the expected entity + expect(entityFromUrl).toEqual({ + ...initEntity, + ...entity, + }); + + // go back to default url + const defaultUrl = new URL("http://localhost"); + const { entity: entityFromDefaultUrl } = convertURLToMetricsExplore( + AD_BIDS_EXPLORE_NAME, + defaultUrl.searchParams, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + explore, + ); + + // assert that the entity we got back matches the original + expect(entityFromDefaultUrl).toEqual(initEntity); }); - }); - - it("with preset and pivot different as preset", () => { - testEntity( - PIVOT_ENTITY, - "http://localhost/?p.r=publisher%2Ctime.hour&p.c=domain%2Ctime.day%2Cimpressions", - { - view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, - pivotRows: ["domain", "time.day"], - pivotCols: ["impressions"], - }, - ); - }); - - it("with preset and no pivot, different as preset", () => { - testEntity( - { - activePage: DashboardState_ActivePage.DEFAULT, - pivot: { - active: false, - rows: { - dimension: [], - }, - columns: { - measure: [], - dimension: [], - }, - expanded: {}, - sorting: [], - columnPage: 1, - rowPage: 1, - enableComparison: true, - activeCell: null, - rowJoinType: "nest", - }, - }, - "http://localhost/?vw=overview&p.r=&p.c=", - { - view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, - pivotRows: ["domain", "time.day"], - pivotCols: ["impressions"], - }, - ); - }); + } }); }); -function testEntity( - entity: Partial, - expectedUrl: string, - preset?: V1ExplorePreset, -) { - const url = new URL("http://localhost"); - const explore: V1ExploreSpec = { - ...AD_BIDS_EXPLORE_INIT, - ...(preset ? { defaultPreset: preset } : {}), - }; - const initEntity = getDefaultMetricsExplorerEntity( - AD_BIDS_EXPLORE_NAME, - AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, - explore, - AD_BIDS_TIME_RANGE_SUMMARY, - ); - cleanMetricsExplore(initEntity); - - // load url params with update metrics state - getUrlFromMetricsExplorer( - { - ...initEntity, - ...entity, - }, - url.searchParams, - explore, - preset ?? {}, - ); - - expect(url.toString()).to.eq(expectedUrl); - - // get back the entity from url params - const { entity: entityFromUrl } = convertURLToMetricsExplore( - AD_BIDS_EXPLORE_NAME, - url.searchParams, - AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, - explore, - ); - - // assert that the entity we got back matches the original - expect(entityFromUrl).toEqual({ - ...initEntity, - ...entity, - }); - - // go back to default url - const defaultUrl = new URL("http://localhost"); - const { entity: entityFromDefaultUrl } = convertURLToMetricsExplore( - AD_BIDS_EXPLORE_NAME, - defaultUrl.searchParams, - AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, - explore, - ); - - // assert that the entity we got back matches the original - expect(entityFromDefaultUrl).toEqual(initEntity); -} - // cleans up any UI only state from MetricsExplorerEntity function cleanMetricsExplore( metricsExplorerEntity: Partial, diff --git a/web-local/src/routes/(viz)/explore/[name]/+page.ts b/web-local/src/routes/(viz)/explore/[name]/+page.ts index 3203ccb9cbd..fe6c0a2de9e 100644 --- a/web-local/src/routes/(viz)/explore/[name]/+page.ts +++ b/web-local/src/routes/(viz)/explore/[name]/+page.ts @@ -31,6 +31,8 @@ export const load = async ({ params, depends, url }) => { const response = await queryClient.fetchQuery({ queryFn: queryFunction, queryKey, + // this loader function is run for every param change in url. + // so to avoid re-fetching explore everytime we set this so that it hits cache. staleTime: Infinity, }); From 4d98863298d2b377a2423c6ae7a83e4101f531bf Mon Sep 17 00:00:00 2001 From: Aditya Hegde Date: Mon, 4 Nov 2024 13:19:21 +0530 Subject: [PATCH 12/17] Add partial metrics entity support --- .../query-mappers/mapQueryToDashboard.ts | 5 +- .../dashboards/query-mappers/utils.ts | 37 +- .../-/alerts/[alert]/open/+page.svelte | 12 +- .../dashboards/stores/dashboard-stores.ts | 1 - .../dashboards/stores/test-data/data.ts | 2 +- .../url-state/DashboardURLStateSync.svelte | 4 +- ... convertMetricsEntityToURLSearchParams.ts} | 2 +- .../convertMetricsExploreToPreset.ts | 168 ++++++++ .../features/dashboards/url-state/fromUrl.ts | 397 ------------------ .../url-state/sparse-preset.spec.ts | 183 ++++++++ .../dashboards/url-state/url-state.spec.ts | 17 +- 11 files changed, 409 insertions(+), 419 deletions(-) rename web-common/src/features/dashboards/url-state/{toUrl.ts => convertMetricsEntityToURLSearchParams.ts} (99%) create mode 100644 web-common/src/features/dashboards/url-state/convertMetricsExploreToPreset.ts delete mode 100644 web-common/src/features/dashboards/url-state/fromUrl.ts create mode 100644 web-common/src/features/dashboards/url-state/sparse-preset.spec.ts diff --git a/web-admin/src/features/dashboards/query-mappers/mapQueryToDashboard.ts b/web-admin/src/features/dashboards/query-mappers/mapQueryToDashboard.ts index dc743b9f6e5..9e323210718 100644 --- a/web-admin/src/features/dashboards/query-mappers/mapQueryToDashboard.ts +++ b/web-admin/src/features/dashboards/query-mappers/mapQueryToDashboard.ts @@ -5,7 +5,6 @@ import type { QueryRequests, } from "@rilldata/web-admin/features/dashboards/query-mappers/types"; import type { CompoundQueryResult } from "@rilldata/web-common/features/compound-query-result"; -import { getProtoFromDashboardState } from "@rilldata/web-common/features/dashboards/proto-state/toProto"; import { getDefaultMetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { initLocalUserPreferenceStore } from "@rilldata/web-common/features/dashboards/user-preferences"; @@ -20,7 +19,7 @@ import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import { derived, get, readable } from "svelte/store"; type DashboardStateForQuery = { - state?: string; + dashboard?: MetricsExplorerEntity; exploreName?: string; }; @@ -140,7 +139,7 @@ export function mapQueryToDashboard( isFetching: false, error: "", data: { - state: getProtoFromDashboardState(newDashboard), + dashboard: newDashboard, exploreName, }, }); diff --git a/web-admin/src/features/dashboards/query-mappers/utils.ts b/web-admin/src/features/dashboards/query-mappers/utils.ts index 861f607abac..737d60b9d43 100644 --- a/web-admin/src/features/dashboards/query-mappers/utils.ts +++ b/web-admin/src/features/dashboards/query-mappers/utils.ts @@ -9,6 +9,8 @@ import { } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { PreviousCompleteRangeMap } from "@rilldata/web-common/features/dashboards/time-controls/time-range-mappers"; +import { convertMetricsEntityToURLSearchParams } from "@rilldata/web-common/features/dashboards/url-state/convertMetricsEntityToURLSearchParams"; +import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; import { isoDurationToFullTimeRange } from "@rilldata/web-common/lib/time/ranges/iso-ranges"; import { type DashboardTimeControls, @@ -17,13 +19,17 @@ import { } from "@rilldata/web-common/lib/time/types"; import { getQueryServiceMetricsViewAggregationQueryKey, + getRuntimeServiceGetExploreQueryKey, queryServiceMetricsViewAggregation, type QueryServiceMetricsViewAggregationBody, + runtimeServiceGetExplore, type V1Expression, type V1TimeRange, type V1TimeRangeSummary, } from "@rilldata/web-common/runtime-client"; +import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import type { QueryClient } from "@tanstack/svelte-query"; +import { get } from "svelte/store"; // We are manually sending in duration, offset and round to grain for previous complete ranges. // This is to map back that split @@ -209,15 +215,40 @@ export function getExploreName(webOpenPath: string) { return matches[1]; } -export function getExplorePageUrl( +export async function getExplorePageUrl( curPageUrl: URL, organization: string, project: string, exploreName: string, - state: string, + dashboard: MetricsExplorerEntity, ) { + const instanceId = get(runtime).instanceId; + const { explore } = await queryClient.fetchQuery({ + queryFn: ({ signal }) => + runtimeServiceGetExplore( + instanceId, + { + name: exploreName, + }, + signal, + ), + queryKey: getRuntimeServiceGetExploreQueryKey(instanceId, { + name: exploreName, + }), + // this loader function is run for every param change in url. + // so to avoid re-fetching explore everytime we set this so that it hits cache. + staleTime: Infinity, + }); + const url = new URL(`${curPageUrl.protocol}//${curPageUrl.host}`); url.pathname = `/${organization}/${project}/explore/${exploreName}`; - url.searchParams.set("state", state); + + const exploreSpec = explore?.explore?.state?.validSpec; + convertMetricsEntityToURLSearchParams( + dashboard, + url.searchParams, + exploreSpec ?? {}, + exploreSpec?.defaultPreset ?? {}, + ); return url.toString(); } diff --git a/web-admin/src/routes/[organization]/[project]/-/alerts/[alert]/open/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/alerts/[alert]/open/+page.svelte index 5b957edd53a..b2848d8afef 100644 --- a/web-admin/src/routes/[organization]/[project]/-/alerts/[alert]/open/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/-/alerts/[alert]/open/+page.svelte @@ -46,17 +46,21 @@ goto(`/${organization}/${project}/-/alerts/${alertId}`); } - $: if ($dashboardStateForAlert.data) { - void goto( - getExplorePageUrl( + async function gotExplorePage() { + return goto( + await getExplorePageUrl( $page.url, organization, project, $dashboardStateForAlert.data.exploreName, - $dashboardStateForAlert.data.state, + $dashboardStateForAlert.data.dashboard, ), ); } + + $: if ($dashboardStateForAlert.data) { + void gotExplorePage(); + } diff --git a/web-common/src/features/dashboards/stores/dashboard-stores.ts b/web-common/src/features/dashboards/stores/dashboard-stores.ts index 93dab41fcb1..79c91da90e5 100644 --- a/web-common/src/features/dashboards/stores/dashboard-stores.ts +++ b/web-common/src/features/dashboards/stores/dashboard-stores.ts @@ -186,7 +186,6 @@ const metricsViewReducers = { schema, ); if (!partial) return; - console.log("syncFromUrl", partial); updateMetricsExplorerByName(name, (metricsExplorer) => { for (const key in partial) { diff --git a/web-common/src/features/dashboards/stores/test-data/data.ts b/web-common/src/features/dashboards/stores/test-data/data.ts index b3cfeeb5996..0106521c2cf 100644 --- a/web-common/src/features/dashboards/stores/test-data/data.ts +++ b/web-common/src/features/dashboards/stores/test-data/data.ts @@ -184,7 +184,7 @@ export const AD_BIDS_METRICS_WITH_BOOL_DIMENSION: V1MetricsViewSpec = { ], }; export const AD_BIDS_METRICS_3_MEASURES_DIMENSIONS: V1MetricsViewSpec = { - title: AD_BIDS_NAME, + displayName: AD_BIDS_NAME, table: AD_BIDS_SOURCE_NAME, measures: AD_BIDS_THREE_MEASURES, dimensions: AD_BIDS_THREE_DIMENSIONS, diff --git a/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte b/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte index c5975db1459..35b04c59f9b 100644 --- a/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte +++ b/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte @@ -5,7 +5,7 @@ import { getStateManagers } from "@rilldata/web-common/features/dashboards/state-managers/state-managers"; import { metricsExplorerStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; - import { getUrlFromMetricsExplorer } from "@rilldata/web-common/features/dashboards/url-state/toUrl"; + import { convertMetricsEntityToURLSearchParams } from "@rilldata/web-common/features/dashboards/url-state/convertMetricsEntityToURLSearchParams"; import { createQueryServiceMetricsViewSchema } 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"; @@ -40,7 +40,7 @@ const u = new URL( `${$page.url.protocol}//${$page.url.host}${$page.url.pathname}`, ); - getUrlFromMetricsExplorer( + convertMetricsEntityToURLSearchParams( $dashboardStore, u.searchParams, exploreSpec, diff --git a/web-common/src/features/dashboards/url-state/toUrl.ts b/web-common/src/features/dashboards/url-state/convertMetricsEntityToURLSearchParams.ts similarity index 99% rename from web-common/src/features/dashboards/url-state/toUrl.ts rename to web-common/src/features/dashboards/url-state/convertMetricsEntityToURLSearchParams.ts index e282105d0d4..02286a8502e 100644 --- a/web-common/src/features/dashboards/url-state/toUrl.ts +++ b/web-common/src/features/dashboards/url-state/convertMetricsEntityToURLSearchParams.ts @@ -28,7 +28,7 @@ import { V1ExploreWebView, } from "@rilldata/web-common/runtime-client"; -export function getUrlFromMetricsExplorer( +export function convertMetricsEntityToURLSearchParams( metrics: MetricsExplorerEntity, searchParams: URLSearchParams, explore: V1ExploreSpec, diff --git a/web-common/src/features/dashboards/url-state/convertMetricsExploreToPreset.ts b/web-common/src/features/dashboards/url-state/convertMetricsExploreToPreset.ts new file mode 100644 index 00000000000..523ef75bb93 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/convertMetricsExploreToPreset.ts @@ -0,0 +1,168 @@ +import { mergeDimensionAndMeasureFilter } from "@rilldata/web-common/features/dashboards/filters/measure-filters/measure-filter-utils"; +import { + type PivotChipData, + PivotChipType, +} from "@rilldata/web-common/features/dashboards/pivot/types"; +import { createAndExpression } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { + FromActivePageMap, + ToURLParamTDDChartMap, + ToURLParamTimeDimensionMap, +} from "@rilldata/web-common/features/dashboards/url-state/mappers"; +import { DashboardState_LeaderboardSortDirection } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; +import { + V1ExploreComparisonMode, + type V1ExplorePreset, + type V1ExploreSpec, +} from "@rilldata/web-common/runtime-client"; + +export function convertMetricsExploreToPreset( + metrics: Partial, + explore: V1ExploreSpec, +) { + const preset: V1ExplorePreset = {}; + + if (metrics.activePage) { + preset.view = FromActivePageMap[metrics.activePage]; + } + + if (metrics.whereFilter || metrics.dimensionThresholdFilters) { + preset.where = mergeDimensionAndMeasureFilter( + metrics.whereFilter ?? createAndExpression([]), + metrics.dimensionThresholdFilters ?? [], + ); + } + + Object.assign(preset, fromMetricsExploreTimeRangeFields(metrics)); + + Object.assign(preset, fromMetricsExploreOverviewFields(metrics, explore)); + + Object.assign(preset, fromMetricsExploreTimeDimensionFields(metrics)); + + Object.assign(preset, fromMetricsExplorePivotFields(metrics)); + + return preset; +} + +function fromMetricsExploreTimeRangeFields( + metrics: Partial, +) { + const preset: V1ExplorePreset = {}; + + if (metrics.selectedTimeRange?.name) { + preset.timeRange = metrics.selectedTimeRange?.name; + // TODO: custom time range + } + // TODO: grain + + if (metrics.selectedComparisonTimeRange?.name) { + preset.compareTimeRange = metrics.selectedComparisonTimeRange.name; + // TODO: custom time range + } + if (metrics.showTimeComparison) { + preset.comparisonMode = + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_TIME; + } + + if (metrics.selectedComparisonDimension !== undefined) { + preset.comparisonDimension = metrics.selectedComparisonDimension; + if (metrics.selectedComparisonDimension) { + preset.comparisonMode = + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_DIMENSION; + } + } + + if (metrics.selectedTimezone) { + preset.timezone = metrics.selectedTimezone; + } + + // TODO: scrubRange + + return preset; +} + +function fromMetricsExploreOverviewFields( + metrics: Partial, + explore: V1ExploreSpec, +) { + const preset: V1ExplorePreset = {}; + + if (metrics.allMeasuresVisible) { + preset.measures = explore.measures ?? []; + } else if (metrics.visibleMeasureKeys) { + preset.measures = [...metrics.visibleMeasureKeys]; + } + + if (metrics.allDimensionsVisible) { + preset.dimensions = explore.dimensions ?? []; + } else if (metrics.visibleDimensionKeys) { + preset.dimensions = [...metrics.visibleDimensionKeys]; + } + + if (metrics.leaderboardMeasureName !== undefined) { + preset.overviewSortBy = metrics.leaderboardMeasureName; + } + + if (metrics.sortDirection) { + preset.overviewSortAsc = + metrics.sortDirection === + DashboardState_LeaderboardSortDirection.ASCENDING; + } + + if (metrics.leaderboardContextColumn !== undefined) { + // TODO + } + + if (metrics.dashboardSortType) { + // TODO + } + + if (metrics.selectedDimensionName !== undefined) { + preset.overviewExpandedDimension = metrics.selectedDimensionName; + } + + return preset; +} + +function fromMetricsExploreTimeDimensionFields( + metrics: Partial, +) { + const preset: V1ExplorePreset = {}; + + if (!metrics.tdd) { + return preset; + } + + preset.timeDimensionMeasure = metrics.tdd.expandedMeasureName; + preset.timeDimensionPin = false; // TODO + preset.timeDimensionChartType = ToURLParamTDDChartMap[metrics.tdd.chartType]; + + return preset; +} + +function fromMetricsExplorePivotFields( + metrics: Partial, +) { + const preset: V1ExplorePreset = {}; + + if (!metrics.pivot) { + return preset; + } + + const mapPivotEntry = (data: PivotChipData) => { + if (data.type === PivotChipType.Time) + return ToURLParamTimeDimensionMap[data.id] as string; + return data.id; + }; + + preset.pivotRows = metrics.pivot.rows.dimension.map(mapPivotEntry); + preset.pivotCols = [ + ...metrics.pivot.columns.dimension.map(mapPivotEntry), + ...metrics.pivot.columns.measure.map(mapPivotEntry), + ]; + + // TODO: other fields + + return preset; +} diff --git a/web-common/src/features/dashboards/url-state/fromUrl.ts b/web-common/src/features/dashboards/url-state/fromUrl.ts deleted file mode 100644 index 5508fc77350..00000000000 --- a/web-common/src/features/dashboards/url-state/fromUrl.ts +++ /dev/null @@ -1,397 +0,0 @@ -import { splitWhereFilter } from "@rilldata/web-common/features/dashboards/filters/measure-filters/measure-filter-utils"; -import { - type PivotChipData, - PivotChipType, - type PivotState, -} from "@rilldata/web-common/features/dashboards/pivot/types"; -import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; -import { createAndExpression } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; -import type { - DimensionThresholdFilter, - MetricsExplorerEntity, -} from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; -import { - TDDChart, - type TDDState, -} from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; -import { - URLStateDefaultSortDirection, - URLStateDefaultTimezone, -} from "@rilldata/web-common/features/dashboards/url-state/defaults"; -import { convertFilterParamToExpression } from "@rilldata/web-common/features/dashboards/url-state/filters/converters"; -import { - FromURLParamTDDChartMap, - FromURLParamTimeDimensionMap, - FromURLParamViewMap, - ToActivePageViewMap, -} from "@rilldata/web-common/features/dashboards/url-state/mappers"; -import { getMapFromArray } from "@rilldata/web-common/lib/arrayUtils"; -import { TIME_GRAIN } from "@rilldata/web-common/lib/time/config"; -import type { DashboardTimeControls } from "@rilldata/web-common/lib/time/types"; -import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; -import { - type MetricsViewSpecDimensionV2, - type MetricsViewSpecMeasureV2, - type V1ExplorePreset, - type V1ExploreSpec, - V1ExploreWebView, - type V1Expression, - type V1MetricsViewSpec, - V1Operation, -} from "@rilldata/web-common/runtime-client"; - -export function getMetricsExplorerFromUrl( - searchParams: URLSearchParams, - metricsView: V1MetricsViewSpec, - explore: V1ExploreSpec, - preset: V1ExplorePreset, -): { entity: Partial; errors: Error[] } { - // TODO: replace this with V1ExplorePreset once it is available on main - const entity: Partial = {}; - const errors: Error[] = []; - - const measures = getMapFromArray( - metricsView.measures?.filter((m) => explore.measures?.includes(m.name!)) ?? - [], - (m) => m.name!, - ); - const dimensions = getMapFromArray( - metricsView.dimensions?.filter((d) => - explore.dimensions?.includes(d.name!), - ) ?? [], - (d) => d.name!, - ); - - const view = - FromURLParamViewMap[searchParams.get("vw") as string] || preset.view; - if (view) { - entity.activePage = Number(ToActivePageViewMap[view] ?? "0"); - } - - if (searchParams.has("f")) { - const { - dimensionFilters, - dimensionThresholdFilters, - errors: filterErrors, - } = fromFilterUrlParam(searchParams.get("f") as string); - if (filterErrors) errors.push(...filterErrors); - if (dimensionFilters) entity.whereFilter = dimensionFilters; - if (dimensionThresholdFilters) - entity.dimensionThresholdFilters = dimensionThresholdFilters; - } else { - entity.whereFilter = createAndExpression([]); - entity.dimensionThresholdFilters = []; - } - - const { entity: trEntity, errors: trErrors } = fromTimeRangesParams( - searchParams, - dimensions, - preset, - ); - Object.assign(entity, trEntity); - errors.push(...trErrors); - - Object.assign( - entity, - fromOverviewUrlParams(searchParams, measures, dimensions, explore, preset), - ); - - entity.tdd = fromTimeDimensionUrlParams(searchParams, measures, preset); - - entity.pivot = fromPivotUrlParams(searchParams, measures, dimensions, preset); - - return { entity, errors }; -} - -function fromTimeRangesParams( - searchParams: URLSearchParams, - dimensions: Map, - preset: V1ExplorePreset, -) { - const entity: Partial = {}; - const errors: Error[] = []; - - const timeRange = searchParams.get("tr") || preset.timeRange; - if (timeRange) { - const { timeRange: selectedTimeRange, error } = - fromTimeRangeUrlParam(timeRange); - if (error) errors.push(error); - entity.selectedTimeRange = selectedTimeRange; - } - const timeZone = searchParams.get("tz") || preset.timezone; - if (timeZone) { - entity.selectedTimezone = timeZone; - } else { - entity.selectedTimezone = URLStateDefaultTimezone; - } - - const comparisonTimeRange = - searchParams.get("ctr") || preset.compareTimeRange; - if (comparisonTimeRange) { - const { timeRange, error } = fromTimeRangeUrlParam(comparisonTimeRange); - if (error) errors.push(error); - entity.selectedComparisonTimeRange = timeRange; - } else { - entity.selectedComparisonTimeRange = undefined; - } - - const comparisonDimension = - searchParams.get("cd") || preset.comparisonDimension; - if (comparisonDimension && dimensions.has(comparisonDimension)) { - entity.selectedComparisonDimension = comparisonDimension; - } else { - entity.selectedComparisonDimension = ""; - } - - // TODO: grain - - return { entity, errors }; -} - -function fromFilterUrlParam(filter: string): { - dimensionFilters?: V1Expression; - dimensionThresholdFilters?: DimensionThresholdFilter[]; - errors?: Error[]; -} { - try { - let expr = convertFilterParamToExpression(filter); - // if root is not AND/OR then add AND - if ( - expr?.cond?.op !== V1Operation.OPERATION_AND && - expr?.cond?.op !== V1Operation.OPERATION_OR - ) { - expr = createAndExpression([expr]); - } - return splitWhereFilter(expr); - } catch (e) { - return { errors: [e] }; - } -} - -function fromTimeRangeUrlParam(tr: string): { - timeRange?: DashboardTimeControls; - error?: Error; -} { - // TODO: validation - return { - timeRange: { - name: tr, - } as DashboardTimeControls, - }; - - // return { - // error: new Error(`unknown time range: ${tr}`), - // }; -} - -function fromOverviewUrlParams( - searchParams: URLSearchParams, - measures: Map, - dimensions: Map, - explore: V1ExploreSpec, - preset: V1ExplorePreset, -) { - const entity: Partial = {}; - - let selectedMeasures = preset.measures ?? explore.measures ?? []; - if (searchParams.has("o.m")) { - const mes = searchParams.get("o.m") as string; - if (mes !== "*") { - selectedMeasures = mes.split(",").filter((m) => measures.has(m)); - } else if (explore.measures) { - selectedMeasures = explore.measures; - } - } - entity.allMeasuresVisible = - selectedMeasures.length === explore.measures?.length; - entity.visibleMeasureKeys = new Set(selectedMeasures); - - let selectedDimensions = preset.dimensions ?? explore.dimensions ?? []; - if (searchParams.has("o.d")) { - const dims = searchParams.get("o.d") as string; - if (dims !== "*") { - selectedDimensions = dims.split(",").filter((d) => dimensions.has(d)); - } else if (explore.dimensions) { - selectedDimensions = explore.dimensions; - } - } - entity.allDimensionsVisible = - selectedDimensions.length === explore.dimensions?.length; - entity.visibleDimensionKeys = new Set(selectedDimensions); - - entity.leaderboardMeasureName = - preset.overviewSortBy ?? preset.measures?.[0] ?? explore.measures?.[0]; - if (searchParams.has("o.sb")) { - const sortBy = searchParams.get("o.sb") as string; - if (measures.has(sortBy)) { - entity.leaderboardMeasureName = sortBy; - } - } - - if (preset.overviewSortAsc !== undefined) { - entity.sortDirection = preset.overviewSortAsc - ? SortDirection.ASCENDING - : SortDirection.DESCENDING; - } else { - entity.sortDirection = URLStateDefaultSortDirection; - } - if (searchParams.has("o.sd")) { - const sortDir = searchParams.get("o.sd") as string; - entity.sortDirection = - sortDir === "ASC" ? SortDirection.ASCENDING : SortDirection.DESCENDING; - } - - if (searchParams.has("o.ed")) { - const dim = searchParams.get("o.ed") as string; - if ( - dimensions.has(dim) || - // we are unsetting from a default preset - dim === "" - ) { - entity.selectedDimensionName = dim; - if (dim) { - entity.activePage = DashboardState_ActivePage.DIMENSION_TABLE; - } else { - // we are un-setting the dimension, so change active page as well - entity.activePage = DashboardState_ActivePage.DEFAULT; - } - } - } else if (preset.overviewExpandedDimension) { - entity.selectedDimensionName = preset.overviewExpandedDimension; - entity.activePage = DashboardState_ActivePage.DIMENSION_TABLE; - } else { - entity.selectedDimensionName = ""; - } - - return entity; -} - -function fromTimeDimensionUrlParams( - searchParams: URLSearchParams, - measures: Map, - preset: V1ExplorePreset, -) { - let ttdMeasure = preset.timeDimensionMeasure; - let ttdChartType = preset.timeDimensionChartType as TDDChart | undefined; - let ttdPin: number | undefined; // TODO - - if (searchParams.has("tdd.m")) { - const mes = searchParams.get("tdd.m") as string; - if ( - measures.has(mes) || - // we are unsetting from a default preset - mes === "" - ) { - ttdMeasure = mes; - } - } - if (searchParams.has("tdd.ct")) { - const ct = searchParams.get("tdd.ct") as string; - if (ct in FromURLParamTDDChartMap) { - ttdChartType = FromURLParamTDDChartMap[ct]; - } - } - if (searchParams.has("tdd.p")) { - const pin = Number(searchParams.get("tdd.p") as string); - if (!Number.isNaN(pin)) { - ttdPin = pin; - } - } - - return { - expandedMeasureName: ttdMeasure ?? "", - chartType: ttdChartType ?? TDDChart.DEFAULT, - pinIndex: ttdPin ?? -1, - }; -} - -function fromPivotUrlParams( - searchParams: URLSearchParams, - measures: Map, - dimensions: Map, - preset: V1ExplorePreset, -): PivotState { - const mapPivotEntry = (entry: string): PivotChipData | undefined => { - if (entry in FromURLParamTimeDimensionMap) { - const grain = FromURLParamTimeDimensionMap[entry]; - return { - id: grain, - title: TIME_GRAIN[grain]?.label, - type: PivotChipType.Time, - }; - } - - if (measures.has(entry)) { - const m = measures.get(entry)!; - return { - id: entry, - title: m.label || m.name || "Unknown", - type: PivotChipType.Measure, - }; - } - - if (dimensions.has(entry)) { - const d = dimensions.get(entry)!; - return { - id: entry, - title: d.label || d.name || "Unknown", - type: PivotChipType.Dimension, - }; - } - - return undefined; - }; - - const rowDimensions: PivotChipData[] = []; - let pivotRows = preset.pivotRows; - if (searchParams.has("p.r")) { - pivotRows = (searchParams.get("p.r") as string).split(","); - } - if (pivotRows) { - pivotRows.forEach((pivotRow) => { - const chip = mapPivotEntry(pivotRow); - if (!chip) return; - rowDimensions.push(chip); - }); - } - - const colMeasures: PivotChipData[] = []; - const colDimensions: PivotChipData[] = []; - let pivotCols = preset.pivotCols; - if (searchParams.has("p.c")) { - pivotCols = (searchParams.get("p.c") as string).split(","); - } - if (pivotCols) { - pivotCols.forEach((pivotRow) => { - const chip = mapPivotEntry(pivotRow); - if (!chip) return; - if (chip.type === PivotChipType.Measure) { - colMeasures.push(chip); - } else { - colDimensions.push(chip); - } - }); - } - - return { - active: - (searchParams.has("vw") && searchParams.get("vw") === "pivot") || - (!searchParams.has("vw") && - preset.view === V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT), - rows: { - dimension: rowDimensions, - }, - columns: { - measure: colMeasures, - dimension: colDimensions, - }, - // TODO: other fields - expanded: {}, - sorting: [], - columnPage: 1, - rowPage: 1, - enableComparison: true, - activeCell: null, - rowJoinType: "nest", - }; -} diff --git a/web-common/src/features/dashboards/url-state/sparse-preset.spec.ts b/web-common/src/features/dashboards/url-state/sparse-preset.spec.ts new file mode 100644 index 00000000000..e3a5b8b53f9 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/sparse-preset.spec.ts @@ -0,0 +1,183 @@ +import { getDefaultMetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; +import { metricsExplorerStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { + AD_BIDS_EXPLORE_INIT, + AD_BIDS_EXPLORE_NAME, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + AD_BIDS_TIME_RANGE_SUMMARY, +} from "@rilldata/web-common/features/dashboards/stores/test-data/data"; +import { + getPartialDashboard, + resetDashboardStore, +} from "@rilldata/web-common/features/dashboards/stores/test-data/helpers"; +import { + AD_BIDS_APPLY_BP_MEASURE_FILTER, + AD_BIDS_APPLY_DOM_DIMENSION_FILTER, + AD_BIDS_APPLY_IMP_MEASURE_FILTER, + AD_BIDS_APPLY_PUB_DIMENSION_FILTER, + AD_BIDS_OPEN_BP_TDD, + AD_BIDS_OPEN_DOM_BP_PIVOT, + AD_BIDS_OPEN_DOM_DIMENSION_TABLE, + AD_BIDS_OPEN_IMP_TDD, + AD_BIDS_OPEN_PUB_DIMENSION_TABLE, + AD_BIDS_OPEN_PUB_IMP_PIVOT, + AD_BIDS_REMOVE_IMP_MEASURE_FILTER, + AD_BIDS_REMOVE_PUB_DIMENSION_FILTER, + AD_BIDS_SET_P4W_TIME_RANGE_FILTER, + AD_BIDS_SET_P7D_TIME_RANGE_FILTER, + applyMutationsToDashboard, + type TestDashboardMutation, +} from "@rilldata/web-common/features/dashboards/stores/test-data/store-mutations"; +import { convertMetricsEntityToURLSearchParams } from "@rilldata/web-common/features/dashboards/url-state/convertMetricsEntityToURLSearchParams"; +import { convertMetricsExploreToPreset } from "@rilldata/web-common/features/dashboards/url-state/convertMetricsExploreToPreset"; +import { + convertPresetToMetricsExplore, + convertURLToMetricsExplore, +} from "@rilldata/web-common/features/dashboards/url-state/convertPresetToMetricsExplore"; +import { cleanMetricsExplore } from "@rilldata/web-common/features/dashboards/url-state/url-state.spec"; +import { + getLocalUserPreferences, + initLocalUserPreferenceStore, +} from "@rilldata/web-common/features/dashboards/user-preferences"; +import { deepClone } from "@vitest/utils"; +import { get } from "svelte/store"; +import { beforeAll, beforeEach, describe, expect, it } from "vitest"; + +const TestCases: { + title: string; + mutations: TestDashboardMutation[]; + keys: (keyof MetricsExplorerEntity)[]; +}[] = [ + { + title: "filters", + mutations: [ + AD_BIDS_APPLY_PUB_DIMENSION_FILTER, + AD_BIDS_APPLY_IMP_MEASURE_FILTER, + ], + keys: ["whereFilter", "dimensionThresholdFilters"], + }, + { + title: "time range", + mutations: [AD_BIDS_SET_P7D_TIME_RANGE_FILTER], + keys: ["selectedTimeRange", "selectedComparisonTimeRange"], + }, + { + title: "dimension table", + mutations: [AD_BIDS_OPEN_PUB_DIMENSION_TABLE], + keys: ["activePage", "selectedDimensionName"], + }, + { + title: "time dimension details", + mutations: [AD_BIDS_OPEN_IMP_TDD], + keys: ["activePage", "tdd"], + }, + { + title: "pivot", + mutations: [AD_BIDS_OPEN_PUB_IMP_PIVOT], + keys: ["activePage", "pivot"], + }, +]; +// list of mutations that reverts all mutations from the above test cases +const TestCasesOppositeMutations = [ + AD_BIDS_REMOVE_PUB_DIMENSION_FILTER, + AD_BIDS_APPLY_DOM_DIMENSION_FILTER, + AD_BIDS_REMOVE_IMP_MEASURE_FILTER, + AD_BIDS_APPLY_BP_MEASURE_FILTER, + AD_BIDS_SET_P4W_TIME_RANGE_FILTER, + AD_BIDS_OPEN_DOM_DIMENSION_TABLE, + AD_BIDS_OPEN_BP_TDD, + AD_BIDS_OPEN_DOM_BP_PIVOT, +]; + +describe("sparse preset", () => { + beforeAll(() => { + initLocalUserPreferenceStore(AD_BIDS_EXPLORE_NAME); + }); + + beforeEach(() => { + resetDashboardStore(); + getLocalUserPreferences().updateTimeZone("UTC"); + localStorage.setItem( + `${AD_BIDS_EXPLORE_NAME}-userPreference`, + `{"timezone":"UTC"}`, + ); + }); + + describe("should reset dashboard store", () => { + for (const { title, mutations } of TestCases) { + it(`from ${title}`, () => { + const initEntity = getDefaultMetricsExplorerEntity( + AD_BIDS_EXPLORE_NAME, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + AD_BIDS_EXPLORE_INIT, + AD_BIDS_TIME_RANGE_SUMMARY, + ); + cleanMetricsExplore(initEntity); + applyMutationsToDashboard(AD_BIDS_EXPLORE_NAME, mutations); + + const url = new URL("http://localhost"); + // get the entity from no param url + const { entity: entityFromUrl } = convertURLToMetricsExplore( + AD_BIDS_EXPLORE_NAME, + url.searchParams, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + AD_BIDS_EXPLORE_INIT, + ); + + expect(entityFromUrl).toEqual(initEntity); + }); + } + }); + + describe("should reset partial dashboard store", () => { + for (const { title, mutations, keys } of TestCases) { + it(`to ${title}`, () => { + // apply the mutations for the test + applyMutationsToDashboard(AD_BIDS_EXPLORE_NAME, mutations); + + // get and store the partial from the current state + const partialEntity = getPartialDashboard(AD_BIDS_EXPLORE_NAME, keys); + const partialPreset = convertMetricsExploreToPreset( + partialEntity, + AD_BIDS_EXPLORE_INIT, + ); + // convert to partial preset and back to entity + const { entity: partialEntityFromPreset } = + convertPresetToMetricsExplore( + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + AD_BIDS_EXPLORE_INIT, + partialPreset, + ); + // remove parts not in the state + cleanMetricsExplore(partialEntity); + // assert that the partial from entity match the one from state + expect(partialEntityFromPreset).toEqual(partialEntity); + + // apply mutations to reverse the previous mutations + applyMutationsToDashboard( + AD_BIDS_EXPLORE_NAME, + TestCasesOppositeMutations, + ); + + // merge partial entity from preset to current state + metricsExplorerStore.mergePartialExplorerEntity( + AD_BIDS_EXPLORE_NAME, + partialEntityFromPreset, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + ); + + // get and store the current state + const curEntity = deepClone( + get(metricsExplorerStore).entities[AD_BIDS_EXPLORE_NAME], + ); + cleanMetricsExplore(curEntity); + // assert that the current entity is not changed when the partial entity selected is applied to it. + expect(curEntity).toEqual({ + ...curEntity, + ...partialEntity, + }); + }); + } + }); +}); diff --git a/web-common/src/features/dashboards/url-state/url-state.spec.ts b/web-common/src/features/dashboards/url-state/url-state.spec.ts index 750f65eea60..50b47a41ef4 100644 --- a/web-common/src/features/dashboards/url-state/url-state.spec.ts +++ b/web-common/src/features/dashboards/url-state/url-state.spec.ts @@ -19,7 +19,7 @@ import { import { getPivotedPartialDashboard } from "@rilldata/web-common/features/dashboards/stores/test-data/helpers"; import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; import { convertURLToMetricsExplore } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToMetricsExplore"; -import { getUrlFromMetricsExplorer } from "@rilldata/web-common/features/dashboards/url-state/toUrl"; +import { convertMetricsEntityToURLSearchParams } from "@rilldata/web-common/features/dashboards/url-state/convertMetricsEntityToURLSearchParams"; import { getLocalUserPreferences, initLocalUserPreferenceStore, @@ -385,7 +385,7 @@ describe("Human readable URL state", () => { cleanMetricsExplore(initEntity); // load url params with update metrics state - getUrlFromMetricsExplorer( + convertMetricsEntityToURLSearchParams( { ...initEntity, ...entity, @@ -480,7 +480,7 @@ describe("Human readable URL state", () => { }); // cleans up any UI only state from MetricsExplorerEntity -function cleanMetricsExplore( +export function cleanMetricsExplore( metricsExplorerEntity: Partial, ) { delete metricsExplorerEntity.name; @@ -489,12 +489,15 @@ function cleanMetricsExplore( delete metricsExplorerEntity.temporaryFilterName; delete metricsExplorerEntity.contextColumnWidths; delete metricsExplorerEntity.dimensionSearchText; - metricsExplorerEntity.selectedTimeRange = { - name: metricsExplorerEntity.selectedTimeRange?.name ?? "inf", - } as DashboardTimeControls; + if (metricsExplorerEntity.selectedTimeRange) { + metricsExplorerEntity.selectedTimeRange = { + name: metricsExplorerEntity.selectedTimeRange?.name ?? "inf", + // TODO: grain + } as DashboardTimeControls; + } delete metricsExplorerEntity.lastDefinedScrubRange; - //TODO + // TODO delete metricsExplorerEntity.selectedScrubRange; delete metricsExplorerEntity.leaderboardContextColumn; delete metricsExplorerEntity.dashboardSortType; From 7394cd79158107a716d709744e6b68396c0436b2 Mon Sep 17 00:00:00 2001 From: Aditya Hegde Date: Mon, 4 Nov 2024 18:25:49 +0530 Subject: [PATCH 13/17] Incorporate bookmarks --- web-admin/src/features/bookmarks/selectors.ts | 88 +++++++++++++----- web-admin/src/routes/+layout.ts | 5 +- .../[organization]/[project]/+layout.ts | 16 +++- .../[project]/explore/[dashboard]/+layout.ts | 70 +++++++++++++++ .../explore/[dashboard]/+page.svelte | 27 ++---- .../[project]/explore/[dashboard]/+page.ts | 27 ++++++ .../dashboards/stores/test-data/data.ts | 5 ++ .../convertMetricsEntityToURLSearchParams.ts | 1 - .../convertPresetToMetricsExplore.ts | 7 +- .../url-state/sparse-preset.spec.ts | 4 +- .../dashboards/url-state/url-state.spec.ts | 15 +++- .../routes/(viz)/explore/[name]/+layout.ts | 60 +++++++++++++ .../src/routes/(viz)/explore/[name]/+page.ts | 90 +++++-------------- 13 files changed, 289 insertions(+), 126 deletions(-) create mode 100644 web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+layout.ts create mode 100644 web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.ts create mode 100644 web-local/src/routes/(viz)/explore/[name]/+layout.ts diff --git a/web-admin/src/features/bookmarks/selectors.ts b/web-admin/src/features/bookmarks/selectors.ts index 731bb7f65b9..37569f2571b 100644 --- a/web-admin/src/features/bookmarks/selectors.ts +++ b/web-admin/src/features/bookmarks/selectors.ts @@ -1,6 +1,8 @@ import { + adminServiceListBookmarks, createAdminServiceGetCurrentUser, createAdminServiceListBookmarks, + getAdminServiceListBookmarksQueryKey, type V1Bookmark, } from "@rilldata/web-admin/client"; import { useProjectId } from "@rilldata/web-admin/features/projects/selectors"; @@ -8,9 +10,11 @@ import type { CompoundQueryResult } from "@rilldata/web-common/features/compound import { getDashboardStateFromUrl } from "@rilldata/web-common/features/dashboards/proto-state/fromProto"; import { useMetricsViewTimeRange } from "@rilldata/web-common/features/dashboards/selectors"; import { useExploreStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { timeControlStateSelector } from "@rilldata/web-common/features/dashboards/time-controls/time-control-store"; import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors"; import { useExploreValidSpec } from "@rilldata/web-common/features/explores/selectors"; +import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; import { prettyFormatTimeRange } from "@rilldata/web-common/lib/time/ranges"; import { TimeRangePreset } from "@rilldata/web-common/lib/time/types"; import { @@ -25,6 +29,7 @@ import { derived, type Readable } from "svelte/store"; export type BookmarkEntry = { resource: V1Bookmark; + metricsEntity: Partial; filtersOnly: boolean; absoluteTimeRange: boolean; }; @@ -66,29 +71,13 @@ export function getBookmarks( !schemaResp.isFetching && userResp.isSuccess && !!userResp.data.user, - select: (resp) => { - const bookmarks: Bookmarks = { - home: undefined, - personal: [], - shared: [], - }; - resp.bookmarks?.forEach((bookmarkResource) => { - const bookmark = parseBookmarkEntry( - bookmarkResource, - validSpec.data?.metricsView ?? {}, - validSpec.data?.explore ?? {}, - schemaResp.data?.schema ?? {}, - ); - if (bookmarkResource.default) { - bookmarks.home = bookmark; - } else if (bookmarkResource.shared) { - bookmarks.shared.push(bookmark); - } else { - bookmarks.personal.push(bookmark); - } - }); - return bookmarks; - }, + select: (resp) => + categorizeBookmarks( + resp.bookmarks ?? [], + validSpec.data?.metricsView, + validSpec.data?.explore, + schemaResp.data?.schema, + ), queryClient, }, }, @@ -96,6 +85,58 @@ export function getBookmarks( ); } +export async function fetchBookmarks( + projectId: string, + exploreName: string, + metricsSpec: V1MetricsViewSpec | undefined, + exploreSpec: V1ExploreSpec | undefined, +) { + const params = { + projectId, + resourceKind: ResourceKind.Explore, + resourceName: exploreName, + }; + const bookmarksResp = await queryClient.fetchQuery({ + queryKey: getAdminServiceListBookmarksQueryKey(params), + queryFn: ({ signal }) => adminServiceListBookmarks(params, signal), + }); + return categorizeBookmarks( + bookmarksResp.bookmarks ?? [], + metricsSpec, + exploreSpec, + undefined, // TODO + ); +} + +function categorizeBookmarks( + bookmarkResp: V1Bookmark[], + metricsSpec: V1MetricsViewSpec | undefined, + exploreSpec: V1ExploreSpec | undefined, + schema: V1StructType | undefined, +) { + const bookmarks: Bookmarks = { + home: undefined, + personal: [], + shared: [], + }; + bookmarkResp?.forEach((bookmarkResource) => { + const bookmark = parseBookmarkEntry( + bookmarkResource, + metricsSpec ?? {}, + exploreSpec ?? {}, + schema ?? {}, + ); + if (bookmarkResource.default) { + bookmarks.home = bookmark; + } else if (bookmarkResource.shared) { + bookmarks.shared.push(bookmark); + } else { + bookmarks.personal.push(bookmark); + } + }); + return bookmarks; +} + export function searchBookmarks( bookmarks: Bookmarks, searchText: string, @@ -196,6 +237,7 @@ function parseBookmarkEntry( ); return { resource: bookmarkResource, + metricsEntity, absoluteTimeRange: metricsEntity.selectedTimeRange?.name === TimeRangePreset.CUSTOM, filtersOnly: !metricsEntity.pivot, diff --git a/web-admin/src/routes/+layout.ts b/web-admin/src/routes/+layout.ts index 981c6f24514..8dd9b14705a 100644 --- a/web-admin/src/routes/+layout.ts +++ b/web-admin/src/routes/+layout.ts @@ -99,11 +99,14 @@ export const load = async ({ params, url, route }) => { queryKey, }); - const { projectPermissions } = response; + const { projectPermissions, project, prodDeployment, jwt } = response; return { organizationPermissions, projectPermissions, + project, + prodDeployment, + jwt, }; } catch (e) { if (e.response?.status !== 403) { diff --git a/web-admin/src/routes/[organization]/[project]/+layout.ts b/web-admin/src/routes/[organization]/[project]/+layout.ts index fd59452faf1..99c72aa57dd 100644 --- a/web-admin/src/routes/[organization]/[project]/+layout.ts +++ b/web-admin/src/routes/[organization]/[project]/+layout.ts @@ -1,13 +1,27 @@ import { hasBlockerIssues } from "@rilldata/web-admin/features/billing/selectors"; import { fetchAllProjectsHibernating } from "@rilldata/web-admin/features/organizations/selectors"; +import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; +import { fixLocalhostRuntimePort } from "@rilldata/web-common/runtime-client/fix-localhost-runtime-port"; +import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import { error, redirect } from "@sveltejs/kit"; export const load = async ({ params: { organization }, parent }) => { - const { organizationPermissions, issues } = await parent(); + const { organizationPermissions, issues, prodDeployment, jwt } = + await parent(); if (!organizationPermissions.manageOrg) { return; } + if (prodDeployment) { + void runtime.setRuntime( + queryClient, + fixLocalhostRuntimePort(prodDeployment.runtimeHost), + prodDeployment.runtimeInstanceId, + jwt, + "user", // TODO: others + ); + } + let projectHibernating = false; try { diff --git a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+layout.ts b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+layout.ts new file mode 100644 index 00000000000..bf714263e92 --- /dev/null +++ b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+layout.ts @@ -0,0 +1,70 @@ +import type { QueryFunction } from "@rilldata/svelte-query"; +import { + type Bookmarks, + fetchBookmarks, +} from "@rilldata/web-admin/features/bookmarks/selectors"; +import { getBasePreset } from "@rilldata/web-common/features/dashboards/url-state/getBasePreset"; +import { getLocalUserPreferencesState } from "@rilldata/web-common/features/dashboards/user-preferences"; +import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; +import { + getRuntimeServiceGetExploreQueryKey, + runtimeServiceGetExplore, + type V1ExplorePreset, + type V1Resource, +} from "@rilldata/web-common/runtime-client"; + +export const load = async ({ params, depends, parent }) => { + const { project, prodDeployment } = await parent(); + const instanceId = prodDeployment?.runtimeInstanceId; + + const exploreName = params.dashboard; + const queryParams = { + name: exploreName, + }; + + depends(exploreName, "explore"); + + const queryKey = getRuntimeServiceGetExploreQueryKey(instanceId, queryParams); + + const queryFunction: QueryFunction< + Awaited> + > = ({ signal }) => runtimeServiceGetExplore(instanceId, queryParams, signal); + + try { + const response = await queryClient.fetchQuery({ + queryFn: queryFunction, + queryKey, + }); + + const exploreResource = response.explore; + const metricsViewResource = response.metricsView; + + const basePreset = getBasePreset( + exploreResource.explore?.state?.validSpec ?? {}, + getLocalUserPreferencesState(exploreName), + ); + + const bookmarks = await fetchBookmarks( + project.id, + exploreName, + metricsViewResource.metricsView?.state?.validSpec, + exploreResource.explore?.state?.validSpec, + ); + + return { + explore: exploreResource, + metricsView: metricsViewResource, + basePreset, + bookmarks, + }; + } catch { + // error handled in +page.svelte for now + // TODO: move it here + return { + explore: {}, + metricsView: {}, + basePreset: {}, + bookmarks: {}, + }; + } +}; diff --git a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte index 962431c48d9..7b31f69327c 100644 --- a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte @@ -1,8 +1,6 @@ + + + + diff --git a/web-common/src/features/dashboards/url-state/convertLegacyStateToExplorePreset.ts b/web-common/src/features/dashboards/url-state/convertLegacyStateToExplorePreset.ts index 2fae6bd39b0..4478629ccbc 100644 --- a/web-common/src/features/dashboards/url-state/convertLegacyStateToExplorePreset.ts +++ b/web-common/src/features/dashboards/url-state/convertLegacyStateToExplorePreset.ts @@ -9,7 +9,6 @@ import { createSubQueryExpression, getAllIdentifiers, } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; -import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { ExplorePresetDefaultChartType, URLStateDefaultTimezone, @@ -41,7 +40,6 @@ import { V1ExploreComparisonMode, type V1ExplorePreset, type V1ExploreSpec, - V1ExploreWebView, type V1Expression, type V1MetricsViewSpec, } from "@rilldata/web-common/runtime-client"; diff --git a/web-common/src/features/dashboards/url-state/filters/converters.ts b/web-common/src/features/dashboards/url-state/filters/converters.ts index 4e36668bbcd..64e06c2e2e7 100644 --- a/web-common/src/features/dashboards/url-state/filters/converters.ts +++ b/web-common/src/features/dashboards/url-state/filters/converters.ts @@ -13,7 +13,10 @@ export function convertFilterParamToExpression(filter: string) { return parser.results[0] as V1Expression; } -export function convertExpressionToFilterParam(expr: V1Expression): string { +export function convertExpressionToFilterParam( + expr: V1Expression, + depth = 0, +): string { if (!expr) return ""; if (expr.val) { @@ -28,14 +31,14 @@ export function convertExpressionToFilterParam(expr: V1Expression): string { switch (expr.cond?.op) { case V1Operation.OPERATION_AND: case V1Operation.OPERATION_OR: - return convertJoinerExpressionToFilterParam(expr); + return convertJoinerExpressionToFilterParam(expr, depth); case V1Operation.OPERATION_IN: case V1Operation.OPERATION_NIN: - return convertInExpressionToFilterParam(expr); + return convertInExpressionToFilterParam(expr, depth); default: - return convertBinaryExpressionToFilterParam(expr); + return convertBinaryExpressionToFilterParam(expr, depth); } } @@ -46,18 +49,25 @@ export function stripParserError(err: Error) { ); } -function convertJoinerExpressionToFilterParam(expr: V1Expression) { +function convertJoinerExpressionToFilterParam( + expr: V1Expression, + depth: number, +) { const joiner = expr.cond?.op === V1Operation.OPERATION_AND ? "AND" : "OR"; const parts = expr.cond?.exprs - ?.map(convertExpressionToFilterParam) + ?.map((e) => convertExpressionToFilterParam(e, depth + 1)) .filter(Boolean); if (!parts?.length) return ""; + const exprParam = parts.join(joiner); - return `(${parts.join(joiner)})`; + if (depth === 0) { + return exprParam; + } + return `(${exprParam})`; } -function convertInExpressionToFilterParam(expr: V1Expression) { +function convertInExpressionToFilterParam(expr: V1Expression, depth: number) { if (!expr.cond?.exprs?.length) return ""; const joiner = expr.cond?.op === V1Operation.OPERATION_IN ? "IN" : "NIN"; @@ -68,6 +78,7 @@ function convertInExpressionToFilterParam(expr: V1Expression) { // TODO: support `NIN ` const having = convertExpressionToFilterParam( expr.cond.exprs[1]?.subquery?.having, + depth + 1, ); if (having) return `${column} having (${having})`; } @@ -75,7 +86,7 @@ function convertInExpressionToFilterParam(expr: V1Expression) { if (expr.cond.exprs.length > 1) { const vals = expr.cond.exprs .slice(1) - .map(convertExpressionToFilterParam) + .map((e) => convertExpressionToFilterParam(e, depth + 1)) .filter(Boolean); return `${column} ${joiner} (${vals.join(",")})`; } @@ -83,13 +94,16 @@ function convertInExpressionToFilterParam(expr: V1Expression) { return ""; } -function convertBinaryExpressionToFilterParam(expr: V1Expression) { +function convertBinaryExpressionToFilterParam( + expr: V1Expression, + depth: number, +) { if (!expr.cond?.op || !(expr.cond?.op in BinaryOperationReverseMap)) return ""; const op = BinaryOperationReverseMap[expr.cond.op]; if (!expr.cond?.exprs?.length) return ""; - const left = convertExpressionToFilterParam(expr.cond.exprs[0]); - const right = convertExpressionToFilterParam(expr.cond.exprs[1]); + const left = convertExpressionToFilterParam(expr.cond.exprs[0], depth + 1); + const right = convertExpressionToFilterParam(expr.cond.exprs[1], depth + 1); if (!left || !right) return ""; return `${left} ${op} ${right}`; diff --git a/web-local/src/routes/(viz)/explore/[name]/+layout.ts b/web-local/src/routes/(viz)/explore/[name]/+layout.ts index 3543e868aa2..46549841882 100644 --- a/web-local/src/routes/(viz)/explore/[name]/+layout.ts +++ b/web-local/src/routes/(viz)/explore/[name]/+layout.ts @@ -1,11 +1,4 @@ -import type { QueryFunction } from "@rilldata/svelte-query"; -import { getBasePreset } from "@rilldata/web-common/features/dashboards/url-state/getBasePreset"; -import { getLocalUserPreferencesState } from "@rilldata/web-common/features/dashboards/user-preferences"; -import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; -import { - getRuntimeServiceGetExploreQueryKey, - runtimeServiceGetExplore, -} from "@rilldata/web-common/runtime-client"; +import { fetchExploreSpec } from "@rilldata/web-admin/features/dashboards/selectors"; import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import { error } from "@sveltejs/kit"; import { get } from "svelte/store"; @@ -17,40 +10,15 @@ export const load = async ({ params, depends }) => { depends(exploreName, "explore"); - const queryParams = { - name: exploreName, - }; - - const queryKey = getRuntimeServiceGetExploreQueryKey(instanceId, queryParams); - - const queryFunction: QueryFunction< - Awaited> - > = ({ signal }) => runtimeServiceGetExplore(instanceId, queryParams, signal); - try { - const response = await queryClient.fetchQuery({ - queryFn: queryFunction, - queryKey, - }); - - const exploreResource = response.explore; - const metricsViewResource = response.metricsView; - - if (!exploreResource?.explore) { - throw error(404, "Explore not found"); - } - if (!metricsViewResource?.metricsView) { - throw error(404, "Metrics view not found"); - } - - const basePreset = getBasePreset( - exploreResource.explore.state?.validSpec ?? {}, - getLocalUserPreferencesState(exploreName), + const { explore, metricsView, basePreset } = await fetchExploreSpec( + instanceId, + exploreName, ); return { - explore: exploreResource, - metricsView: metricsViewResource, + explore, + metricsView, basePreset, }; } catch (e) { From 10c525df89586b811cd59fb97ac048e87d924f37 Mon Sep 17 00:00:00 2001 From: Aditya Hegde Date: Fri, 8 Nov 2024 12:56:59 +0530 Subject: [PATCH 16/17] Convert bookmarks to links with new urls --- .../src/features/bookmarks/Bookmarks.svelte | 12 ---- .../BookmarksDropdownMenuContent.svelte | 23 ++++++- .../BookmarksDropdownMenuItem.svelte | 11 +--- .../src/features/bookmarks/applyBookmark.ts | 38 ------------ web-admin/src/features/bookmarks/selectors.ts | 61 ++++++++++++++++++- .../-/reports/[report]/open/+page.svelte | 12 ++-- .../[project]/-/share/[token]/+page.svelte | 3 +- .../explore/[dashboard]/+page.svelte | 4 +- .../stores/dashboard-store-defaults.ts | 3 +- .../url-state/DashboardURLStateSync.svelte | 8 ++- .../DashboardURLStateSyncWrapper.svelte | 2 +- .../convertMetricsEntityToURLSearchParams.ts | 6 +- .../convertPresetToMetricsExplore.ts | 4 +- .../dashboards/url-state/url-state.spec.ts | 6 +- .../routes/(viz)/explore/[name]/+page.svelte | 4 +- 15 files changed, 113 insertions(+), 84 deletions(-) delete mode 100644 web-admin/src/features/bookmarks/applyBookmark.ts diff --git a/web-admin/src/features/bookmarks/Bookmarks.svelte b/web-admin/src/features/bookmarks/Bookmarks.svelte index 6033533e159..1319aed869b 100644 --- a/web-admin/src/features/bookmarks/Bookmarks.svelte +++ b/web-admin/src/features/bookmarks/Bookmarks.svelte @@ -6,7 +6,6 @@ } from "@rilldata/web-admin/client"; import BookmarkDialog from "@rilldata/web-admin/features/bookmarks/BookmarkDialog.svelte"; import BookmarksContent from "@rilldata/web-admin/features/bookmarks/BookmarksDropdownMenuContent.svelte"; - import { createBookmarkApplier } from "@rilldata/web-admin/features/bookmarks/applyBookmark"; import { createHomeBookmarkModifier } from "@rilldata/web-admin/features/bookmarks/createOrUpdateHomeBookmark"; import { getBookmarkDataForDashboard } from "@rilldata/web-admin/features/bookmarks/getBookmarkDataForDashboard"; import type { BookmarkEntry } from "@rilldata/web-admin/features/bookmarks/selectors"; @@ -29,12 +28,6 @@ let showDialog = false; let bookmark: BookmarkEntry | null = null; - $: bookmarkApplier = createBookmarkApplier( - $runtime?.instanceId, - metricsViewName, - exploreName, - ); - $: exploreStore = useExploreStore(exploreName); $: projectId = useProjectId($page.params.organization, $page.params.project); @@ -46,10 +39,6 @@ ); const bookmarkDeleter = createAdminServiceRemoveBookmark(); - function selectBookmark(bookmark: BookmarkEntry) { - bookmarkApplier(bookmark.resource); - } - async function createHomeBookmark() { await homeBookmarkModifier(getBookmarkDataForDashboard($exploreStore)); eventBus.emit("notification", { @@ -102,7 +91,6 @@ showDialog = true; bookmark = detail; }} - on:select={({ detail }) => selectBookmark(detail)} {metricsViewName} {exploreName} /> diff --git a/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte b/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte index ffad6412388..f88d38cb35f 100644 --- a/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte +++ b/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte @@ -11,9 +11,14 @@ import BookmarkItem from "@rilldata/web-admin/features/bookmarks/BookmarksDropdownMenuItem.svelte"; import { getBookmarks, + getFilledInBookmarks, searchBookmarks, } from "@rilldata/web-admin/features/bookmarks/selectors"; import { Search } from "@rilldata/web-common/components/search"; + import { useExploreStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; + import { getBasePreset } from "@rilldata/web-common/features/dashboards/url-state/getBasePreset"; + import { getLocalUserPreferencesState } from "@rilldata/web-common/features/dashboards/user-preferences"; + import { useExploreValidSpec } from "@rilldata/web-common/features/explores/selectors"; import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import { useQueryClient } from "@tanstack/svelte-query"; import { BookmarkPlusIcon } from "lucide-svelte"; @@ -28,6 +33,13 @@ $: organization = $page.params.organization; $: project = $page.params.project; + $: dashboard = useExploreStore(exploreName); + $: validExploreSpec = useExploreValidSpec($runtime.instanceId, exploreName); + $: exploreSpec = $validExploreSpec.data.explore ?? {}; + $: basePreset = getBasePreset( + exploreSpec, + getLocalUserPreferencesState(exploreName), + ); let searchText: string; let bookmarks: ReturnType; @@ -39,7 +51,14 @@ metricsViewName, exploreName, ); - $: filteredBookmarks = searchBookmarks($bookmarks.data, searchText); + $: filledInBookmarks = getFilledInBookmarks( + $bookmarks.data, + `/${organization}/${project}/explore/${exploreName}`, + $dashboard, + $validExploreSpec.data.explore ?? {}, + basePreset, + ); + $: filteredBookmarks = searchBookmarks(filledInBookmarks, searchText); $: projectPermissions = getProjectPermissions(organization, project); $: manageProject = $projectPermissions.data?.manageProject; @@ -108,7 +127,6 @@ @@ -119,7 +137,6 @@ diff --git a/web-admin/src/features/bookmarks/BookmarksDropdownMenuItem.svelte b/web-admin/src/features/bookmarks/BookmarksDropdownMenuItem.svelte index 6272118871c..cdbfee95d26 100644 --- a/web-admin/src/features/bookmarks/BookmarksDropdownMenuItem.svelte +++ b/web-admin/src/features/bookmarks/BookmarksDropdownMenuItem.svelte @@ -13,11 +13,6 @@ const dispatch = createEventDispatcher(); - function selectBookmark(e) { - if (e.skipSelection) return; - dispatch("select", bookmark); - } - function editBookmark(e) { e.skipSelection = true; dispatch("edit", bookmark); @@ -34,14 +29,12 @@
e.key === "Enter" && e.currentTarget.click()} on:mouseenter={() => (hovered = true)} on:mouseleave={() => (hovered = false)} role="menuitem" tabindex="-1" > - {/if}
- + {#if !readOnly}
{#if hovered} diff --git a/web-admin/src/features/bookmarks/applyBookmark.ts b/web-admin/src/features/bookmarks/applyBookmark.ts deleted file mode 100644 index a39796f45e7..00000000000 --- a/web-admin/src/features/bookmarks/applyBookmark.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { V1Bookmark } from "@rilldata/web-admin/client"; -import { metricsExplorerStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; -import { useExploreValidSpec } from "@rilldata/web-common/features/explores/selectors"; -import { createQueryServiceMetricsViewSchema } from "@rilldata/web-common/runtime-client"; -import { get } from "svelte/store"; - -export function createBookmarkApplier( - instanceId: string, - metricsViewName: string, - exploreName: string, -) { - const validExploreSpec = useExploreValidSpec(instanceId, exploreName); - const metricsSchema = createQueryServiceMetricsViewSchema( - instanceId, - metricsViewName, - ); - - return (bookmark: V1Bookmark) => { - const validExploreSpecResp = get(validExploreSpec); - const metricsSchemaResp = get(metricsSchema); - if ( - !bookmark.data || - !validExploreSpecResp.data?.metricsView || - !validExploreSpecResp.data?.explore || - !metricsSchemaResp.data?.schema - ) { - return; - } - // TODO: use new methods. update the url directly - metricsExplorerStore.syncFromUrl( - exploreName, - decodeURIComponent(bookmark.data), - validExploreSpecResp.data.metricsView, - validExploreSpecResp.data.explore, - metricsSchemaResp.data.schema, - ); - }; -} diff --git a/web-admin/src/features/bookmarks/selectors.ts b/web-admin/src/features/bookmarks/selectors.ts index 37569f2571b..86f5c1f7013 100644 --- a/web-admin/src/features/bookmarks/selectors.ts +++ b/web-admin/src/features/bookmarks/selectors.ts @@ -12,6 +12,7 @@ import { useMetricsViewTimeRange } from "@rilldata/web-common/features/dashboard import { useExploreStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { timeControlStateSelector } from "@rilldata/web-common/features/dashboards/time-controls/time-control-store"; +import { convertMetricsEntityToURLSearchParams } from "@rilldata/web-common/features/dashboards/url-state/convertMetricsEntityToURLSearchParams"; import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors"; import { useExploreValidSpec } from "@rilldata/web-common/features/explores/selectors"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; @@ -19,6 +20,7 @@ import { prettyFormatTimeRange } from "@rilldata/web-common/lib/time/ranges"; import { TimeRangePreset } from "@rilldata/web-common/lib/time/types"; import { createQueryServiceMetricsViewSchema, + type V1ExplorePreset, type V1ExploreSpec, type V1MetricsViewSpec, type V1StructType, @@ -32,6 +34,7 @@ export type BookmarkEntry = { metricsEntity: Partial; filtersOnly: boolean; absoluteTimeRange: boolean; + url: string; }; export type Bookmarks = { @@ -138,9 +141,9 @@ function categorizeBookmarks( } export function searchBookmarks( - bookmarks: Bookmarks, + bookmarks: Bookmarks | undefined, searchText: string, -): Bookmarks { +): Bookmarks | undefined { if (!searchText || !bookmarks) return bookmarks; searchText = searchText.toLowerCase(); const matchName = (bookmark: BookmarkEntry | undefined) => @@ -223,6 +226,39 @@ export function getPrettySelectedTimeRange( ); } +export function getFilledInBookmarks( + bookmarks: Bookmarks | undefined, + baseUrl: string, + dashboard: MetricsExplorerEntity, + exploreSpec: V1ExploreSpec, + basePreset: V1ExplorePreset, +): Bookmarks | undefined { + if (!bookmarks) return undefined; + + if (!baseUrl.startsWith("http")) { + // handle case where only path is provided + baseUrl = "http://localhost" + baseUrl; + } + + return { + home: bookmarks.home + ? getFilledInBookmark( + bookmarks.home, + baseUrl, + dashboard, + exploreSpec, + basePreset, + ) + : undefined, + personal: bookmarks.personal.map((b) => + getFilledInBookmark(b, baseUrl, dashboard, exploreSpec, basePreset), + ), + shared: bookmarks.shared.map((b) => + getFilledInBookmark(b, baseUrl, dashboard, exploreSpec, basePreset), + ), + }; +} + function parseBookmarkEntry( bookmarkResource: V1Bookmark, metricsViewSpec: V1MetricsViewSpec, @@ -241,5 +277,26 @@ function parseBookmarkEntry( absoluteTimeRange: metricsEntity.selectedTimeRange?.name === TimeRangePreset.CUSTOM, filtersOnly: !metricsEntity.pivot, + url: "", // will be filled in along with existing dashboard + }; +} + +function getFilledInBookmark( + bookmark: BookmarkEntry, + baseUrl: string, + dashboard: MetricsExplorerEntity, + exploreSpec: V1ExploreSpec, + basePreset: V1ExplorePreset, +) { + const url = new URL(baseUrl); + convertMetricsEntityToURLSearchParams( + { ...dashboard, ...bookmark.metricsEntity }, + url.searchParams, + exploreSpec, + basePreset, + ); + return { + ...bookmark, + url: `${url.pathname}${url.search}`, }; } diff --git a/web-admin/src/routes/[organization]/[project]/-/reports/[report]/open/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/reports/[report]/open/+page.svelte index 25cb4aa905a..96c5809743c 100644 --- a/web-admin/src/routes/[organization]/[project]/-/reports/[report]/open/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/-/reports/[report]/open/+page.svelte @@ -34,18 +34,22 @@ $report?.data?.resource?.report?.spec?.annotations ?? {}, ); - $: if ($dashboardStateForReport?.data) { - void goto( - getExplorePageUrl( + async function gotExplorePage() { + return goto( + await getExplorePageUrl( $page.url, organization, project, $dashboardStateForReport.data.exploreName, - $dashboardStateForReport.data.state, + $dashboardStateForReport.data.dashboard, ), ); } + $: if ($dashboardStateForReport?.data) { + void gotExplorePage(); + } + // TODO: error handling diff --git a/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.svelte index a9ad33fcfa4..ba8e8706590 100644 --- a/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.svelte @@ -14,6 +14,7 @@ export let data: PageData; $: ({ + basePreset, partialMetrics, token: { resourceName }, } = data); @@ -54,7 +55,7 @@ metricsViewName={explore.metricsView.meta.name.name} exploreName={resourceName} > - + - + diff --git a/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts b/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts index 3edc9d9c671..9c7bc184eaf 100644 --- a/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts +++ b/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts @@ -145,11 +145,12 @@ export function getDefaultMetricsExplorerEntity( metricsView: V1MetricsViewSpec, explore: V1ExploreSpec, fullTimeRange: V1MetricsViewTimeRangeResponse | undefined, + basePreset = getBasePreset(explore, getLocalUserPreferencesState(name)), ): MetricsExplorerEntity { const { entity: baseEntity } = convertPresetToMetricsExplore( metricsView, explore, - getBasePreset(explore, getLocalUserPreferencesState(name)), + basePreset, ); const metricsExplorer = { // fields filled here are the ones that are not stored and loaded to/from URL diff --git a/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte b/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte index b56dc6061bb..05417334548 100644 --- a/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte +++ b/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte @@ -6,10 +6,14 @@ import { metricsExplorerStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { convertMetricsEntityToURLSearchParams } from "@rilldata/web-common/features/dashboards/url-state/convertMetricsEntityToURLSearchParams"; - import { createQueryServiceMetricsViewSchema } from "@rilldata/web-common/runtime-client"; + import { + createQueryServiceMetricsViewSchema, + type V1ExplorePreset, + } 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"; + export let basePreset: V1ExplorePreset; export let partialMetrics: Partial; const { @@ -42,7 +46,7 @@ $dashboardStore, u.searchParams, exploreSpec, - exploreSpec.defaultPreset ?? {}, + basePreset, ); const newUrl = u.toString(); if (window.location.href !== newUrl) { diff --git a/web-common/src/features/dashboards/url-state/DashboardURLStateSyncWrapper.svelte b/web-common/src/features/dashboards/url-state/DashboardURLStateSyncWrapper.svelte index 052ad3663e6..b295b03c587 100644 --- a/web-common/src/features/dashboards/url-state/DashboardURLStateSyncWrapper.svelte +++ b/web-common/src/features/dashboards/url-state/DashboardURLStateSyncWrapper.svelte @@ -37,6 +37,6 @@ $: parseUrl($page.url); - + diff --git a/web-common/src/features/dashboards/url-state/convertMetricsEntityToURLSearchParams.ts b/web-common/src/features/dashboards/url-state/convertMetricsEntityToURLSearchParams.ts index c1653595ed2..8560e1d4878 100644 --- a/web-common/src/features/dashboards/url-state/convertMetricsEntityToURLSearchParams.ts +++ b/web-common/src/features/dashboards/url-state/convertMetricsEntityToURLSearchParams.ts @@ -66,7 +66,8 @@ function toTimeRangesUrl( ) { if ( (preset.timeRange !== undefined && - metrics.selectedTimeRange?.name !== preset.timeRange) || + metrics.selectedTimeRange !== undefined && + metrics.selectedTimeRange.name !== preset.timeRange) || (preset.timeRange === undefined && metrics.selectedTimeRange?.name !== URLStateDefaultTimeRange) ) { @@ -85,7 +86,8 @@ function toTimeRangesUrl( if ( (preset.compareTimeRange !== undefined && - metrics.selectedComparisonTimeRange?.name !== preset.compareTimeRange) || + metrics.selectedComparisonTimeRange !== undefined && + metrics.selectedComparisonTimeRange.name !== preset.compareTimeRange) || (preset.compareTimeRange === undefined && !!metrics.selectedComparisonTimeRange?.name) ) { diff --git a/web-common/src/features/dashboards/url-state/convertPresetToMetricsExplore.ts b/web-common/src/features/dashboards/url-state/convertPresetToMetricsExplore.ts index eaa7d7a1cbe..6dd562a1437 100644 --- a/web-common/src/features/dashboards/url-state/convertPresetToMetricsExplore.ts +++ b/web-common/src/features/dashboards/url-state/convertPresetToMetricsExplore.ts @@ -171,7 +171,7 @@ function fromTimeRangesParams( ) { // unset all comparison setting if mode is none entity.selectedComparisonTimeRange = undefined; - entity.selectedComparisonDimension = undefined; + entity.selectedComparisonDimension = ""; entity.showTimeComparison = false; } @@ -252,7 +252,7 @@ function fromOverviewUrlParams( if (preset.overviewExpandedDimension !== undefined) { if (preset.overviewExpandedDimension === "") { - entity.selectedDimensionName = undefined; + entity.selectedDimensionName = ""; // if preset didnt have a view then this is a dimension table unset. if ( preset.view === V1ExploreWebView.EXPLORE_ACTIVE_PAGE_UNSPECIFIED || diff --git a/web-common/src/features/dashboards/url-state/url-state.spec.ts b/web-common/src/features/dashboards/url-state/url-state.spec.ts index 078b71fc093..e1cefc06830 100644 --- a/web-common/src/features/dashboards/url-state/url-state.spec.ts +++ b/web-common/src/features/dashboards/url-state/url-state.spec.ts @@ -52,7 +52,7 @@ const TestCases: { ]), dimensionThresholdFilters: [], }, - expectedUrl: "http://localhost/?f=%28publisher+IN+%28%27Yahoo%27%29%29", + expectedUrl: "http://localhost/?f=publisher+IN+%28%27Yahoo%27%29", }, { @@ -241,7 +241,7 @@ const TestCases: { "Dimension table with preset and with no dimension table in state different than preset", entity: { activePage: DashboardState_ActivePage.DEFAULT, - selectedDimensionName: undefined, + selectedDimensionName: "", }, preset: { overviewExpandedDimension: AD_BIDS_DOMAIN_DIMENSION, @@ -396,7 +396,7 @@ describe("Human readable URL state", () => { }, url.searchParams, explore, - preset ?? {}, + basePreset, ); expect(url.toString()).to.eq(expectedUrl); diff --git a/web-local/src/routes/(viz)/explore/[name]/+page.svelte b/web-local/src/routes/(viz)/explore/[name]/+page.svelte index ab4d4b98de1..d9be090e747 100644 --- a/web-local/src/routes/(viz)/explore/[name]/+page.svelte +++ b/web-local/src/routes/(viz)/explore/[name]/+page.svelte @@ -15,7 +15,7 @@ const queryClient = useQueryClient(); export let data: PageData; - $: ({ metricsView, explore, partialMetrics } = data); + $: ({ metricsView, explore, basePreset, partialMetrics } = data); resetSelectedMockUserAfterNavigate(queryClient); @@ -67,7 +67,7 @@ {:else} {#key exploreName} - + From 4d2c6ce4e14956002fa8c7426176c0cdc42f3b13 Mon Sep 17 00:00:00 2001 From: Aditya Hegde Date: Wed, 13 Nov 2024 22:42:17 +0530 Subject: [PATCH 17/17] Add support for free form editing of filters --- .../BookmarksDropdownMenuContent.svelte | 4 +- .../getDashboardFromAggregationRequest.ts | 26 +++++------ .../[project]/-/share/[token]/+page.ts | 16 ++++++- .../explore/[dashboard]/+page.svelte | 15 ++++++- .../[project]/explore/[dashboard]/+page.ts | 1 + .../dashboards/filters/Filters.svelte | 23 ++++++++++ .../dashboards/filters/FiltersInput.svelte | 43 +++++++++++++++++++ .../measure-filters/measure-filter-utils.ts | 17 ++++++-- .../dashboards/leaderboard/Leaderboard.svelte | 19 +++++--- .../actions/dimension-filters.ts | 12 ++++++ .../selectors/dimension-filters.ts | 4 ++ .../dashboards/stores/filter-utils.ts | 33 ++++++++++++++ .../url-state/DashboardURLStateSync.svelte | 2 - .../convertMetricsEntityToURLSearchParams.ts | 32 ++++++++------ .../url-state/convertURLToExplorePreset.ts | 4 +- .../url-state/filters/converters.ts | 4 +- 16 files changed, 205 insertions(+), 50 deletions(-) create mode 100644 web-common/src/features/dashboards/filters/FiltersInput.svelte diff --git a/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte b/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte index f88d38cb35f..39717eff9b4 100644 --- a/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte +++ b/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte @@ -35,7 +35,7 @@ $: project = $page.params.project; $: dashboard = useExploreStore(exploreName); $: validExploreSpec = useExploreValidSpec($runtime.instanceId, exploreName); - $: exploreSpec = $validExploreSpec.data.explore ?? {}; + $: exploreSpec = $validExploreSpec.data?.explore ?? {}; $: basePreset = getBasePreset( exploreSpec, getLocalUserPreferencesState(exploreName), @@ -55,7 +55,7 @@ $bookmarks.data, `/${organization}/${project}/explore/${exploreName}`, $dashboard, - $validExploreSpec.data.explore ?? {}, + exploreSpec, basePreset, ); $: filteredBookmarks = searchBookmarks(filledInBookmarks, searchText); diff --git a/web-admin/src/features/dashboards/query-mappers/getDashboardFromAggregationRequest.ts b/web-admin/src/features/dashboards/query-mappers/getDashboardFromAggregationRequest.ts index c46174a728b..10b6b588c1f 100644 --- a/web-admin/src/features/dashboards/query-mappers/getDashboardFromAggregationRequest.ts +++ b/web-admin/src/features/dashboards/query-mappers/getDashboardFromAggregationRequest.ts @@ -18,7 +18,9 @@ import { import { getDashboardStateFromUrl } from "@rilldata/web-common/features/dashboards/proto-state/fromProto"; import { createAndExpression, + createSubQueryExpression, forEachIdentifier, + getAllIdentifiers, } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; @@ -79,24 +81,18 @@ export async function getDashboardFromAggregationRequest({ exprHasComparison(req.having) || dashboard.dimensionThresholdFilters.length > 0 ) { - const expr = await convertExprToToplist( - queryClient, - instanceId, - dashboard.name, + const extraFilter = createSubQueryExpression( dimension, - req.measures?.[0]?.name ?? "", - req.timeRange, - req.comparisonTimeRange, - executionTime, - req.where, + getAllIdentifiers(req.having), req.having, ); - if (expr) { - dashboard.whereFilter = - mergeFilters( - dashboard.whereFilter ?? createAndExpression([]), - createAndExpression([expr]), - ) ?? createAndExpression([]); + if (dashboard.whereFilter?.cond?.exprs?.length) { + dashboard.whereFilter = createAndExpression([ + dashboard.whereFilter, + extraFilter, + ]); + } else { + dashboard.whereFilter = extraFilter; } } else { dashboard.dimensionThresholdFilters = [ diff --git a/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.ts b/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.ts index 461cce9115c..c9e98e9a674 100644 --- a/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.ts +++ b/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.ts @@ -1,5 +1,7 @@ +import { getDashboardStateFromUrl } from "@rilldata/web-common/features/dashboards/proto-state/fromProto"; import { metricsExplorerStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { convertMetricsEntityToURLSearchParams } from "@rilldata/web-common/features/dashboards/url-state/convertMetricsEntityToURLSearchParams"; import { convertURLToMetricsExplore } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToMetricsExplore"; import { redirect } from "@sveltejs/kit"; import { get } from "svelte/store"; @@ -30,8 +32,20 @@ export const load = async ({ url, parent }) => { ) { // Initial load of the dashboard. // Merge home token state to url if present and there are no params in the url + // convert legacy state to new readable url format + const metricsEntity = getDashboardStateFromUrl( + token.state, + metricsViewSpec, + exploreSpec, + {}, // TODO + ); const newUrl = new URL(url); - newUrl.searchParams.set("state", token.state); + convertMetricsEntityToURLSearchParams( + metricsEntity, + newUrl.searchParams, + exploreSpec, + basePreset, + ); throw redirect(307, `${newUrl.pathname}${newUrl.search}`); } diff --git a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte index ba8827e9967..8869eb26ae8 100644 --- a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte @@ -10,6 +10,7 @@ import DashboardURLStateSync from "@rilldata/web-common/features/dashboards/url-state/DashboardURLStateSync.svelte"; import StateManagersProvider from "@rilldata/web-common/features/dashboards/state-managers/StateManagersProvider.svelte"; import { useExplore } from "@rilldata/web-common/features/explores/selectors"; + import { eventBus } from "@rilldata/web-common/lib/event-bus/event-bus"; import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import type { PageData } from "./$types"; @@ -18,7 +19,19 @@ // const PollIntervalWhenDashboardOk = 60000; // This triggers a layout shift, so removing for now export let data: PageData; - $: ({ basePreset, partialMetrics } = data); + $: ({ basePreset, partialMetrics, errors } = data); + $: if (errors?.length) { + console.log(errors); + setTimeout(() => { + eventBus.emit("notification", { + type: "error", + message: errors[0].message, + options: { + persisted: false, + }, + }); + }, 100); + } $: instanceId = $runtime?.instanceId; $: ({ diff --git a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.ts b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.ts index c8dbc36c17f..197dea0a61a 100644 --- a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.ts +++ b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.ts @@ -22,6 +22,7 @@ export const load = async ({ url, parent, params }) => { ); partialMetrics = entity; errors.push(...errorsFromConvert); + console.log(partialMetrics.whereFilter); } if ( diff --git a/web-common/src/features/dashboards/filters/Filters.svelte b/web-common/src/features/dashboards/filters/Filters.svelte index d60d78bdd76..1910fa550c6 100644 --- a/web-common/src/features/dashboards/filters/Filters.svelte +++ b/web-common/src/features/dashboards/filters/Filters.svelte @@ -2,8 +2,10 @@ import Button from "@rilldata/web-common/components/button/Button.svelte"; import Calendar from "@rilldata/web-common/components/icons/Calendar.svelte"; import Filter from "@rilldata/web-common/components/icons/Filter.svelte"; + import FiltersInput from "@rilldata/web-common/features/dashboards/filters/FiltersInput.svelte"; import type { MeasureFilterEntry } from "@rilldata/web-common/features/dashboards/filters/measure-filters/measure-filter-entry"; import MeasureFilter from "@rilldata/web-common/features/dashboards/filters/measure-filters/MeasureFilter.svelte"; + import { isExpressionUnsupported } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; import { getMapFromArray } from "@rilldata/web-common/lib/arrayUtils"; import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import { flip } from "svelte/animate"; @@ -31,6 +33,7 @@ toggleDimensionValueSelection, removeDimensionFilter, toggleDimensionFilterMode, + setFilters, }, measuresFilter: { setMeasureFilter, removeMeasureFilter }, filters: { clearAllFilters }, @@ -46,6 +49,7 @@ measureFilters: { getMeasureFilterItems, getAllMeasureFilterItems }, pivot: { showPivot }, }, + dashboardStore, } = StateManagers; const timeControlsStore = useTimeControlStore(StateManagers); @@ -87,6 +91,12 @@ $: metricTimeSeries = useModelHasTimeSeries(instanceId, $metricsViewName); $: hasTimeSeries = $metricTimeSeries.data; + $: isComplexFilter = isExpressionUnsupported($dashboardStore.whereFilter); + let isFreeFormEdit = false; + $: if (isComplexFilter) { + isFreeFormEdit = true; + } + function handleMeasureFilterApply( dimension: string, measureName: string, @@ -132,6 +142,11 @@ > No filters selected
+ {:else if isFreeFormEdit} + setFilters(filters)} + /> {:else} {#each allDimensionFilters as { name, label, selectedValues } (name)} {@const dimension = dimensions.find( @@ -176,6 +191,14 @@ {#if hasFilters} {/if} + {#if !isComplexFilter} + + {/if} {/if} diff --git a/web-common/src/features/dashboards/filters/FiltersInput.svelte b/web-common/src/features/dashboards/filters/FiltersInput.svelte new file mode 100644 index 00000000000..458a94e8edd --- /dev/null +++ b/web-common/src/features/dashboards/filters/FiltersInput.svelte @@ -0,0 +1,43 @@ + + +
+ + {#if error} + + + + + + {error} + + + {/if} +
diff --git a/web-common/src/features/dashboards/filters/measure-filters/measure-filter-utils.ts b/web-common/src/features/dashboards/filters/measure-filters/measure-filter-utils.ts index 25c16b48de2..3753d55d232 100644 --- a/web-common/src/features/dashboards/filters/measure-filters/measure-filter-utils.ts +++ b/web-common/src/features/dashboards/filters/measure-filters/measure-filter-utils.ts @@ -7,6 +7,8 @@ import { createAndExpression, createSubQueryExpression, filterExpressions, + isExpressionUnsupported, + isJoinerExpression, } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; import type { DimensionThresholdFilter, @@ -42,17 +44,24 @@ export function mergeDimensionAndMeasureFilter( * Measure filters will be sub-queries */ export function splitWhereFilter(whereFilter: V1Expression | undefined) { + if (whereFilter && isExpressionUnsupported(whereFilter)) { + return { dimensionFilters: whereFilter, dimensionThresholdFilters: [] }; + } + const dimensionFilters = createAndExpression([]); const dimensionThresholdFilters: DimensionThresholdFilter[] = []; whereFilter?.cond?.exprs?.filter((e) => { const subqueryExpr = e.cond?.exprs?.[1]; if (subqueryExpr?.subquery) { + const filters = isJoinerExpression(subqueryExpr.subquery.having) + ? (subqueryExpr.subquery.having?.cond?.exprs + ?.map(mapExprToMeasureFilter) + .filter(Boolean) as MeasureFilterEntry[]) + : [mapExprToMeasureFilter(subqueryExpr.subquery.having)]; + dimensionThresholdFilters.push({ name: subqueryExpr.subquery.dimension ?? "", - filters: - (subqueryExpr.subquery.having?.cond?.exprs - ?.map(mapExprToMeasureFilter) - .filter(Boolean) as MeasureFilterEntry[]) ?? [], + filters: filters.filter(Boolean) as MeasureFilterEntry[], }); return; } diff --git a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte index 009c1164c59..f97250b3dda 100644 --- a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte +++ b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte @@ -27,6 +27,7 @@ import { createAndExpression, createOrExpression, + isExpressionUnsupported, sanitiseExpression, } from "../stores/filter-utils"; import { mergeDimensionAndMeasureFilter } from "../filters/measure-filters/measure-filter-utils"; @@ -100,13 +101,17 @@ uri, } = dimension); - $: where = sanitiseExpression( - mergeDimensionAndMeasureFilter( - getFiltersForOtherDimensions(whereFilter, dimensionName), - dimensionThresholdFilters, - ), - undefined, - ); + $: isComplexFilter = isExpressionUnsupported(whereFilter); + $: where = isComplexFilter + ? whereFilter + : sanitiseExpression( + mergeDimensionAndMeasureFilter( + getFiltersForOtherDimensions(whereFilter, dimensionName), + dimensionThresholdFilters, + ), + undefined, + ); + $: console.log(isComplexFilter, where); $: measures = getIndependentMeasures( metricsView, diff --git a/web-common/src/features/dashboards/state-managers/actions/dimension-filters.ts b/web-common/src/features/dashboards/state-managers/actions/dimension-filters.ts index da2176a70fa..054e748cca5 100644 --- a/web-common/src/features/dashboards/state-managers/actions/dimension-filters.ts +++ b/web-common/src/features/dashboards/state-managers/actions/dimension-filters.ts @@ -1,3 +1,4 @@ +import { splitWhereFilter } from "@rilldata/web-common/features/dashboards/filters/measure-filters/measure-filter-utils"; import { createInExpression, getValueIndexInExpression, @@ -156,6 +157,16 @@ export function deselectItemsInFilter( } } +export function setFilters( + { dashboard }: DashboardMutables, + filter: V1Expression, +) { + const { dimensionFilters, dimensionThresholdFilters } = + splitWhereFilter(filter); + dashboard.whereFilter = dimensionFilters; + dashboard.dimensionThresholdFilters = dimensionThresholdFilters; +} + export const dimensionFilterActions = { /** * Toggles whether the given dimension value is selected in the @@ -170,4 +181,5 @@ export const dimensionFilterActions = { removeDimensionFilter, selectItemsInFilter, deselectItemsInFilter, + setFilters, }; 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 ab53b79055f..4a409f38a40 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 @@ -3,6 +3,7 @@ import { filterItemsSortFunction } from "@rilldata/web-common/features/dashboard import { forEachIdentifier, getValuesInExpression, + isExpressionUnsupported, matchExpressionByName, } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; import type { @@ -17,6 +18,9 @@ export const selectedDimensionValues = ( dashData: AtLeast, ): ((dimName: string) => string[]) => { return (dimName: string) => { + // if it is a complex filter unsupported by UI then no values are selected + if (isExpressionUnsupported(dashData.dashboard.whereFilter)) return []; + // FIXME: it is possible for this way of accessing the filters // to return the same value twice, which would seem to indicate // a bug in the way we're setting the filters / active values. diff --git a/web-common/src/features/dashboards/stores/filter-utils.ts b/web-common/src/features/dashboards/stores/filter-utils.ts index 132b85214a1..a72d5ef4b60 100644 --- a/web-common/src/features/dashboards/stores/filter-utils.ts +++ b/web-common/src/features/dashboards/stores/filter-utils.ts @@ -305,3 +305,36 @@ export function isExpressionIncomplete(expression: V1Expression): boolean { // If the operation is specified and a defined, non-empty val is found, the expression is complete return false; } + +export function isJoinerExpression(expression: V1Expression | undefined) { + return ( + expression?.cond?.op && + (expression.cond.op === V1Operation.OPERATION_AND || + expression.cond.op === V1Operation.OPERATION_OR) + ); +} + +export function isExpressionUnsupported(expression: V1Expression) { + if ( + !expression.cond || + !expression.cond.exprs || + expression.cond?.op !== V1Operation.OPERATION_AND + ) { + return true; + } + + for (const expr of expression.cond.exprs) { + if (expr.cond?.op !== V1Operation.OPERATION_IN) return true; + + const subqueryExpr = expr.cond?.exprs?.[1]; + if ( + subqueryExpr?.subquery?.having?.cond?.exprs?.length && + isJoinerExpression(subqueryExpr.subquery.having) && + subqueryExpr.subquery.having.cond.exprs.length > 1 + ) { + return true; + } + } + + return false; +} diff --git a/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte b/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte index 05417334548..d7c0fbdb190 100644 --- a/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte +++ b/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte @@ -51,8 +51,6 @@ const newUrl = u.toString(); if (window.location.href !== newUrl) { void goto(newUrl); - } else { - console.log("same", newUrl); } } diff --git a/web-common/src/features/dashboards/url-state/convertMetricsEntityToURLSearchParams.ts b/web-common/src/features/dashboards/url-state/convertMetricsEntityToURLSearchParams.ts index 8560e1d4878..31122d0e8ea 100644 --- a/web-common/src/features/dashboards/url-state/convertMetricsEntityToURLSearchParams.ts +++ b/web-common/src/features/dashboards/url-state/convertMetricsEntityToURLSearchParams.ts @@ -113,23 +113,27 @@ function toOverviewUrl( explore: V1ExploreSpec, preset: V1ExplorePreset, ) { - const measures = [...metrics.visibleMeasureKeys]; - const presetMeasures = preset.measures ?? explore.measures ?? []; - if (!arrayUnorderedEquals(measures, presetMeasures)) { - if (metrics.allMeasuresVisible) { - searchParams.set("o.m", "*"); - } else { - searchParams.set("o.m", measures.join(",")); + if (metrics.visibleMeasureKeys) { + const measures = [...metrics.visibleMeasureKeys]; + const presetMeasures = preset.measures ?? explore.measures ?? []; + if (!arrayUnorderedEquals(measures, presetMeasures)) { + if (metrics.allMeasuresVisible) { + searchParams.set("o.m", "*"); + } else { + searchParams.set("o.m", measures.join(",")); + } } } - const dimensions = [...metrics.visibleDimensionKeys]; - const presetDimensions = preset.dimensions ?? explore.dimensions ?? []; - if (!arrayUnorderedEquals(dimensions, presetDimensions)) { - if (metrics.allDimensionsVisible) { - searchParams.set("o.d", "*"); - } else { - searchParams.set("o.d", dimensions.join(",")); + if (metrics.visibleDimensionKeys) { + const dimensions = [...metrics.visibleDimensionKeys]; + const presetDimensions = preset.dimensions ?? explore.dimensions ?? []; + if (!arrayUnorderedEquals(dimensions, presetDimensions)) { + if (metrics.allDimensionsVisible) { + searchParams.set("o.d", "*"); + } else { + searchParams.set("o.d", dimensions.join(",")); + } } } diff --git a/web-common/src/features/dashboards/url-state/convertURLToExplorePreset.ts b/web-common/src/features/dashboards/url-state/convertURLToExplorePreset.ts index 6a0a874b504..700b0be0e5e 100644 --- a/web-common/src/features/dashboards/url-state/convertURLToExplorePreset.ts +++ b/web-common/src/features/dashboards/url-state/convertURLToExplorePreset.ts @@ -316,7 +316,7 @@ function fromPivotUrlParams( (r) => dimensions.has(r) || r in FromURLParamTimeDimensionMap, ); preset.pivotRows = validRows; - const missingRows = getMissingValues(rows, validRows); + const missingRows = getMissingValues(validRows, rows); if (missingRows.length) { errors.push(getMultiFieldError("pivot row", missingRows)); } @@ -331,7 +331,7 @@ function fromPivotUrlParams( c in FromURLParamTimeDimensionMap, ); preset.pivotCols = validCols; - const missingCols = getMissingValues(cols, validCols); + const missingCols = getMissingValues(validCols, cols); if (missingCols.length) { errors.push(getMultiFieldError("pivot column", missingCols)); } diff --git a/web-common/src/features/dashboards/url-state/filters/converters.ts b/web-common/src/features/dashboards/url-state/filters/converters.ts index 64e06c2e2e7..edb5a8da866 100644 --- a/web-common/src/features/dashboards/url-state/filters/converters.ts +++ b/web-common/src/features/dashboards/url-state/filters/converters.ts @@ -53,7 +53,7 @@ function convertJoinerExpressionToFilterParam( expr: V1Expression, depth: number, ) { - const joiner = expr.cond?.op === V1Operation.OPERATION_AND ? "AND" : "OR"; + const joiner = expr.cond?.op === V1Operation.OPERATION_AND ? " AND " : " OR "; const parts = expr.cond?.exprs ?.map((e) => convertExpressionToFilterParam(e, depth + 1)) @@ -78,7 +78,7 @@ function convertInExpressionToFilterParam(expr: V1Expression, depth: number) { // TODO: support `NIN ` const having = convertExpressionToFilterParam( expr.cond.exprs[1]?.subquery?.having, - depth + 1, + 0, ); if (having) return `${column} having (${having})`; }