diff --git a/.prettierignore b/.prettierignore index cc0d45a87255..0b8933b6750a 100644 --- a/.prettierignore +++ b/.prettierignore @@ -70,3 +70,6 @@ web-common/src/features/dashboards/url-state/filters/expression.cjs # generated by paraglidejs web-common/src/lib/i18n/gen web-common/src/lib/i18n/project.inlang + +# generated by web-common/scripts/generate-flint-catalog.ts +web-common/src/features/custom-viz/examples/flint-charts-index.json diff --git a/docs/docs/developers/build/dashboards/canvas-widgets/custom-viz.md b/docs/docs/developers/build/dashboards/canvas-widgets/custom-viz.md new file mode 100644 index 000000000000..7428f19a3df1 --- /dev/null +++ b/docs/docs/developers/build/dashboards/canvas-widgets/custom-viz.md @@ -0,0 +1,177 @@ +--- +title: "Custom Viz Components" +sidebar_label: "Custom Viz" +sidebar_position: 15 +--- + +Custom viz components are reusable visualizations defined as standalone `type: component` files. A component declares typed **params** and renders a [custom chart](/reference/project-files/component) built from a Metrics SQL query and a chart spec. Canvas dashboards reference the component by name and bind values to its params, so one visualization can be reused across many dashboards with different metrics views, measures, and dimensions. + +Charts are described declaratively: you state the chart type and which field goes on which channel, and Rill derives the scales, axes, number formats, sorting, stacking, colors, and layout. Rill supplies the semantics of each field from your metrics view — a currency measure formats as currency, the time dimension is treated as temporal at the dashboard's grain — so the spec stays short and the same component adapts when it is bound to a different metrics view. + +Unlike an inline `custom_chart` widget, a custom viz lives in its own file (conventionally under `viz_library/`), has a declared parameter contract that Rill validates, and can be developed in a dedicated editor with a live preview. + +## Creating a custom viz + +In Rill Developer, click **Add → Custom viz → Blank** (or create a file under `viz_library/` by hand): + +```yaml +# viz_library/measure_trend.yaml +type: component +display_name: Measure trend + +params: + - name: metrics_view + type: metrics_view + required: true + - name: measure + type: measure + required: true + - name: time_dim + type: time_dimension + required: true + +custom_chart: + metrics_sql: | + SELECT {{ .params.time_dim }}, {{ .params.measure }} + FROM {{ .params.metrics_view }} + ORDER BY {{ .params.time_dim }} + spec: + chartType: Line Chart + encodings: + x: { field: "{{ .params.time_dim }}" } + y: { field: "{{ .params.measure }}" } +``` + +There is no schema, sizing, mark, or encoding type to write: `chartType` and `encodings` are the whole spec. An encoding accepts only `field`, `type`, `aggregate`, `sortOrder`, `sortBy`, and `scheme` — and `type` and `aggregate` are rarely needed, since Rill already knows that measures are quantitative and pre-aggregated. Presentation tuning that a chart type exposes (`innerRadius`, `stackMode`, `interpolate`, …) goes under an optional `chartProperties` mapping. + +The component editor shows a live preview. Use the **Test values** panel to bind preview-only values to the params; dashboards set their own values. The **Used by** panel lists every dashboard referencing the component. + +## Params + +Params are the component's contract with dashboards. Each param has a `name`, a `type`, and optionally `required`, `default`, `description`, and (for scalars) `options`: + +| Type | Bound value | Notes | +|---|---|---| +| `metrics_view` | A metrics view name | Must be named `metrics_view` or end with `_metrics_view` | +| `measure` | A measure of the bound metrics view | Validated at reconcile time | +| `dimension` | A dimension of the bound metrics view | Validated at reconcile time | +| `time_dimension` | A time dimension of the bound metrics view | The primary time dimension or a time-typed dimension | +| `string`, `number`, `boolean` | A scalar value | Useful for row limits and `chartProperties` | + +Params are substituted into `metrics_sql` and `spec` through templating: `{{ .params.measure }}`. A value that is exactly one placeholder keeps the param's type, so a `number` param bound to `innerRadius: "{{ .params.inner_radius }}"` arrives as a number rather than a string. Partial interpolations such as `LIMIT {{ .params.limit }}` are ordinary strings. + +When evolving a component that dashboards already use, prefer adding params with a `default` (or `optional`) so existing references keep working. Renaming or removing a param surfaces a validation error on every dashboard that binds it. + +## Referencing from a canvas + +In the canvas visual editor, open **Add widget → Your components** and pick the component: Rill pre-selects sensible bindings (a metrics view with a time dimension, its first measure, and so on), and the inspector shows a form generated from the declared params. In YAML, a reference looks like: + +```yaml +# dashboards/overview.yaml +type: canvas +rows: + - items: + - component: measure_trend + params: + metrics_view: bids_metrics + measure: total_bids + time_dim: __time + width: 8 + - component: measure_trend + params: + metrics_view: bids_metrics + measure: avg_bid_price + time_dim: __time + width: 4 +``` + +Bindings are validated when the canvas reconciles: unknown params, missing required params, and fields that don't exist in the bound metrics view are reported as errors on the dashboard. Canvas-level time and dimension filters apply to custom viz like any other widget. + +A time dimension selected by the `metrics_sql` is bucketed at the dashboard's time grain, so a time series re-buckets when the time controls change, just like a native chart. Write `date_trunc('', ) AS ` in the query only when a chart should stay at one fixed bucket regardless of the dashboard; the alias is required, or the column comes out named after the expression and the encodings can no longer reference it. The component editor's preview has no time controls to inherit from and renders at day grain. + +## Starting from a chart type + +**Add → Custom viz** opens a gallery of every chart type Rill can render, grouped by family (bars, lines and areas, distributions, circular, points, tables and maps) and labelled with the fields each one needs — for example a heatmap needs two categorical fields and a measure. Picking one writes a component with a required `metrics_view` param, one typed param per channel named after its chart role (`x_axis`, `color`, …), a templated `metrics_sql`, and a row limit sized to the chart's visual density. + +When the AI assistant is enabled, the chart type is handed to it instead, and it authors the same component against your project's own metrics views so the params come pre-bound to real fields. You're taken to the component immediately and watch an "Importing chart using AI…" screen while it generates; the editor appears once the component reconciles. + +## Ejecting to Vega-Lite + +When a chart needs something the chart spec cannot express, **Eject to Vega-Lite** in the component +editor's header replaces `spec` with the Vega-Lite it currently compiles to, under `vega_spec`. From +there the spec is yours to edit: marks, layers, transforms, interactions, anything Vega-Lite can do. + +Ejecting keeps the component parameterized. The compiled spec has the bound values resolved into it, +so before writing the file Rill turns them back into template references: + +| Resolved value | Written as | +|---|---| +| A field name, wherever it appears (encodings, transforms, expressions) | `{{ .params. }}` | +| A field's display name, in an axis or legend title | `{{ .fields..display_name }}` | +| A measure's number formatter, in `formatType` | `{{ .fields..format_type }}` | + +Nothing about the component's contract changes: the params, the `metrics_sql` query, and every +dashboard that references it keep working, and re-binding a param in a dashboard still re-titles the +axes and re-formats the numbers. + +```yaml +# viz_library/measure_trend.yaml, after ejecting +custom_chart: + metrics_sql: | + SELECT {{ .params.time_dim }}, {{ .params.measure }} + FROM {{ .params.metrics_view }} + ORDER BY {{ .params.time_dim }} + vega_spec: | + { + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "width": "container", + "height": "container", + "autosize": { "type": "fit" }, + "data": { "name": "query1" }, + "mark": "line", + "encoding": { + "x": { + "field": "{{ .params.time_dim }}", + "type": "temporal", + "title": "{{ .fields.time_dim.display_name }}" + }, + "y": { + "field": "{{ .params.measure }}", + "type": "quantitative", + "title": "{{ .fields.measure.display_name }}", + "formatType": "{{ .fields.measure.format_type }}" + } + } + } +``` + +The query result arrives as the `query1` dataset, which is why the spec reads its data from +`{"name": "query1"}` rather than declaring values inline. The container sizing is the same as the +native charts use: `autosize: fit` makes the rendered dimensions the chart's total size, so axes and +legends stay inside the widget instead of growing past it. + +Alongside `display_name` and `format_type`, a field-typed param also exposes `name` (the same value +as `{{ .params. }}`), `format_d3` and `format_preset`, which are useful when you would rather +format with Vega-Lite's own `format` than route through Rill's formatter. + +Ejecting is one-way, and it is a snapshot: the Vega-Lite it writes reflects the data the chart was +compiled against, and Rill stops deriving scales, axes, formats, sorting, stacking and layout, so +the chart no longer adapts when the data changes shape. Eject when you need control that the chart +spec does not offer, not as a matter of course. To get back, undo the edit or revert the file. + +## Limits + +A component issues exactly one Metrics SQL query, so charts that layer several datasets cannot be expressed. Cross-row derivations (running totals, rankings, per-group offsets) are not available in Metrics SQL either — use a chart type that computes them, such as `Waterfall Chart`, `Bump Chart`, or `ECDF Plot`. + +Rows that overflow what an axis can legibly fit are dropped and reported above the chart, so a row limit set far above a chart's density silently truncates rather than crowds. + +Inline `custom_chart` widgets inside a canvas are a separate, Vega-Lite-based widget and continue to use `vega_spec`. A component file authors a chart spec under `spec`, and only carries a `vega_spec` once it has been ejected. + +:::note Feature flag +Custom viz is currently gated behind the `customComponents` feature flag. Enable it in `rill.yaml`: + +```yaml +features: + - customComponents +``` +::: diff --git a/docs/docs/reference/project-files/canvas-dashboards.md b/docs/docs/reference/project-files/canvas-dashboards.md index 6a620ef72089..88138015ed84 100644 --- a/docs/docs/reference/project-files/canvas-dashboards.md +++ b/docs/docs/reference/project-files/canvas-dashboards.md @@ -52,6 +52,8 @@ _[array of object]_ - Refers to all of the rows displayed on the Canvas. Each en - **donut_chart** - Donut or Pie chart to display sums of total + - **`params`** - _[object]_ - Values bound to the referenced component's declared params. Only valid together with `component`. Values must be scalars. + - **`width`** - _[string, integer]_ - Width of the component (can be a number or string with unit) - **`name`** - _[string]_ - Stable identifier for a tab group, used as its deep-link URL key. Defaults to `group-` if omitted. Only used for tab-group entries. diff --git a/docs/docs/reference/project-files/component.md b/docs/docs/reference/project-files/component.md index 8cafb1f2301d..db057e9b51d0 100644 --- a/docs/docs/reference/project-files/component.md +++ b/docs/docs/reference/project-files/component.md @@ -20,6 +20,24 @@ _[string]_ - Refers to the display name for the component _[string]_ - Detailed description of the component's purpose and functionality +### `params` + +_[array of object]_ - List of typed parameters that canvases can bind values to when referencing this component. Bound values are available in the renderer properties' templating as `{{ .params. }}`, and the metrics view metadata of a field-typed param as `{{ .fields..display_name }}`, `{{ .fields..format_d3 }}`, `{{ .fields..format_preset }}` and `{{ .fields..format_type }}`. + + - **`name`** - _[string]_ - Param name. Must be a valid identifier; referenced in the renderer properties' templating as `{{ .params. }}`. _(required)_ + + - **`type`** - _[string]_ - Param type. Params of type `metrics_view` must be named `metrics_view` or end with `_metrics_view`. _(required)_ + + - **`description`** - _[string]_ - Human-facing description of the param + + - **`required`** - _[boolean]_ - If true, a canvas item referencing this component must bind a value for the param. Mutually exclusive with `default`. + + - **`default`** - _[string, number, boolean]_ - Default value used when the param is not bound + + - **`metrics_view`** - _[string]_ - For `measure`, `dimension` and `time_dimension` params, the name of a sibling param of type `metrics_view` whose bound metrics view the field must belong to. May be omitted when exactly one `metrics_view` param is declared. + + - **`options`** - _[array]_ - For scalar params, the allowed values. Renders as a select input in visual editors. + ### `input` _[array of object]_ - List of input variables that can be passed to the component @@ -301,3 +319,33 @@ _[object]_ - (no description) - **`title`** - _[string]_ - Image title - **`description`** - _[string]_ - Image description + +### `custom_chart` + +_[object]_ - A custom visualization: a Metrics SQL query plus a chart spec describing how to draw it. +Rill derives scales, axes, formats, sorting, stacking and layout from the data and from the +metrics view's semantics, so the spec only states the chart type and which field goes on which channel. + + + - **`metrics_sql`** - _[string]_ - Metrics SQL query selecting the dimensions and measures to plot, e.g. + `SELECT {{ .params.dim }}, {{ .params.measure }} FROM {{ .params.metrics_view }} LIMIT 10`. + Measures arrive pre-aggregated; do not wrap them in aggregate functions. + + + - **`spec`** - _[object]_ - (no description) + + - **`chartType`** - _[string]_ - The chart type, e.g. `Line Chart`, `Bar Chart`, `Heatmap`, `Waterfall Chart`. + + - **`encodings`** - _[object]_ - Maps a visual channel (`x`, `y`, `color`, `size`, `column`, `row`, `group`, `angle`, `goal`, ...) + to a field. Each value is either a field name or an encoding object. + + + - **`chartProperties`** - _[object]_ - Per-chart-type presentation tuning, e.g. `innerRadius`, `stackMode`, `interpolate`. + + - **`vega_spec`** - _[string]_ - A Vega-Lite spec, as an alternative to `spec` for a chart Rill cannot describe. + This is what ejecting a chart spec in the component editor produces: the Vega-Lite it + compiled to, with the param bindings turned back into template references. Rill no longer + derives scales, axes, formats, sorting or layout, so the chart stops adapting on its own. + Read the query result from the `query1` dataset, i.e. `"data": {"name": "query1"}`, + and size the chart to its container with `"width": "container"`, `"height": "container"` + and `"autosize": {"type": "fit"}`. Mutually exclusive with `spec`. diff --git a/package-lock.json b/package-lock.json index 6177083b77e2..fea2cbe83dde 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18606,6 +18606,39 @@ "dev": true, "license": "ISC" }, + "node_modules/flint-chart": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/flint-chart/-/flint-chart-0.4.1.tgz", + "integrity": "sha512-4umtCbQpDiY5sRCAqNlBKxIpxRXg0FMRj0f1d2d1n/5p75MzfasiRJDEIkrPtjBdPffjmaXtDeBnSqazrh1opA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "chart.js": "^4.0.0", + "echarts": "^5.0.0 || ^6.0.0", + "plotly.js": "^2.0.0 || ^3.0.0", + "vega": "^5.0.0 || ^6.0.0", + "vega-lite": "^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "chart.js": { + "optional": true + }, + "echarts": { + "optional": true + }, + "plotly.js": { + "optional": true + }, + "vega": { + "optional": true + }, + "vega-lite": { + "optional": true + } + } + }, "node_modules/follow-redirects": { "version": "1.15.11", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", @@ -36183,6 +36216,7 @@ "dependencies": { "@internationalized/date": "^3.10.1", "@tiptap/suggestion": "^3.20.1", + "flint-chart": "0.4.1", "picomatch": "^4.0.3", "vega-embed": "^7.1.0" }, diff --git a/proto/gen/rill/runtime/v1/queries.pb.go b/proto/gen/rill/runtime/v1/queries.pb.go index ba5c9d99b222..14dfe032c3ce 100644 --- a/proto/gen/rill/runtime/v1/queries.pb.go +++ b/proto/gen/rill/runtime/v1/queries.pb.go @@ -5398,6 +5398,8 @@ type ResolveComponentResponse struct { // Renderer properties with templating resolved for the provided args RendererProperties *structpb.Struct `protobuf:"bytes,2,opt,name=renderer_properties,json=rendererProperties,proto3" json:"renderer_properties,omitempty"` + // The effective args used for resolution: the component's declared param defaults merged with the provided args. + ResolvedArgs *structpb.Struct `protobuf:"bytes,3,opt,name=resolved_args,json=resolvedArgs,proto3" json:"resolved_args,omitempty"` } func (x *ResolveComponentResponse) Reset() { @@ -5439,6 +5441,13 @@ func (x *ResolveComponentResponse) GetRendererProperties() *structpb.Struct { return nil } +func (x *ResolveComponentResponse) GetResolvedArgs() *structpb.Struct { + if x != nil { + return x.ResolvedArgs + } + return nil +} + type ResolveTemplatedStringRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -9884,266 +9893,121 @@ var file_rill_runtime_v1_queries_proto_rawDesc = []byte{ 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x61, 0x72, 0x67, 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, 0x04, 0x61, 0x72, - 0x67, 0x73, 0x22, 0x64, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x43, 0x6f, 0x6d, - 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, - 0x0a, 0x13, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 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, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x50, 0x72, - 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xd9, 0x03, 0x0a, 0x1d, 0x52, 0x65, 0x73, - 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x53, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x62, - 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, - 0x2a, 0x0a, 0x11, 0x75, 0x73, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x6f, - 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x75, 0x73, 0x65, 0x46, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x98, 0x01, 0x0a, 0x20, - 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x77, 0x68, 0x65, 0x72, 0x65, - 0x5f, 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, 0x77, - 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x50, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, - 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, - 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, - 0x6c, 0x57, 0x68, 0x65, 0x72, 0x65, 0x42, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, - 0x69, 0x65, 0x77, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x1c, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x61, 0x6c, 0x57, 0x68, 0x65, 0x72, 0x65, 0x42, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x12, 0x4e, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, - 0x65, 0x52, 0x13, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x54, 0x69, 0x6d, - 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x1a, 0x6c, 0x0a, 0x21, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x61, 0x6c, 0x57, 0x68, 0x65, 0x72, 0x65, 0x42, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 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, 0x45, - 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0x34, 0x0a, 0x1e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, + 0x67, 0x73, 0x22, 0xa2, 0x01, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x43, 0x6f, + 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x48, 0x0a, 0x13, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 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, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x50, + 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0d, 0x72, 0x65, 0x73, + 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x61, 0x72, 0x67, 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, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x6c, + 0x76, 0x65, 0x64, 0x41, 0x72, 0x67, 0x73, 0x22, 0xd9, 0x03, 0x0a, 0x1d, 0x52, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, + 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x2a, + 0x0a, 0x11, 0x75, 0x73, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x75, 0x73, 0x65, 0x46, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x98, 0x01, 0x0a, 0x20, 0x61, + 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x77, 0x68, 0x65, 0x72, 0x65, 0x5f, + 0x62, 0x79, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x50, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x8f, 0x02, 0x0a, 0x1b, 0x43, - 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x76, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, + 0x57, 0x68, 0x65, 0x72, 0x65, 0x42, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, + 0x65, 0x77, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x1c, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x61, 0x6c, 0x57, 0x68, 0x65, 0x72, 0x65, 0x42, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x56, 0x69, 0x65, 0x77, 0x12, 0x4e, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, + 0x52, 0x13, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, + 0x52, 0x61, 0x6e, 0x67, 0x65, 0x1a, 0x6c, 0x0a, 0x21, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x61, 0x6c, 0x57, 0x68, 0x65, 0x72, 0x65, 0x42, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x56, 0x69, 0x65, 0x77, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 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, 0x45, 0x78, + 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0x34, 0x0a, 0x1e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x8f, 0x02, 0x0a, 0x1b, 0x43, 0x6f, + 0x6c, 0x75, 0x6d, 0x6e, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, + 0x62, 0x61, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, + 0x62, 0x61, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, + 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, + 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x26, 0x0a, + 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, + 0x02, 0x10, 0x01, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0xb6, 0x01, 0x0a, 0x1c, + 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x05, + 0x73, 0x74, 0x61, 0x72, 0x74, 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, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2c, + 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 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, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x36, 0x0a, 0x08, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, + 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x47, 0x72, 0x61, 0x69, 0x6e, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6c, 0x22, 0xa5, 0x02, 0x0a, 0x11, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, + 0x6f, 0x70, 0x4b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x61, 0x74, - 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x61, 0x74, + 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, - 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x26, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0xb6, 0x01, 0x0a, - 0x1c, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x76, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, - 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 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, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, - 0x2c, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 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, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x36, 0x0a, - 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x47, 0x72, 0x61, 0x69, 0x6e, 0x52, 0x08, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x76, 0x61, 0x6c, 0x22, 0xa5, 0x02, 0x0a, 0x11, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, - 0x54, 0x6f, 0x70, 0x4b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x61, - 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x61, - 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, - 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, - 0x26, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x75, 0x6d, - 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, - 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x67, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x61, 0x67, 0x67, 0x12, 0x0c, 0x0a, 0x01, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, - 0x6b, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x6a, 0x0a, - 0x12, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x6f, 0x70, 0x4b, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x13, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x63, - 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 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, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x75, - 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x12, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x63, - 0x61, 0x6c, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x6e, 0x0a, 0x12, 0x43, 0x61, 0x74, - 0x65, 0x67, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, - 0x2c, 0x0a, 0x05, 0x74, 0x6f, 0x70, 0x5f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, - 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x54, 0x6f, 0x70, 0x4b, 0x48, 0x00, 0x52, 0x04, 0x74, 0x6f, 0x70, 0x4b, 0x12, 0x22, 0x0a, - 0x0b, 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x01, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, - 0x79, 0x42, 0x06, 0x0a, 0x04, 0x63, 0x61, 0x73, 0x65, 0x22, 0x8a, 0x01, 0x0a, 0x04, 0x54, 0x6f, - 0x70, 0x4b, 0x12, 0x35, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x70, 0x4b, 0x2e, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x1a, 0x4b, 0x0a, 0x05, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 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, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x8a, 0x02, 0x0a, 0x16, 0x43, 0x6f, 0x6c, 0x75, 0x6d, - 0x6e, 0x4e, 0x75, 0x6c, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, - 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x26, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, - 0x10, 0x01, 0x52, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, - 0x0b, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x63, 0x6f, 0x6c, - 0x75, 0x6d, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, - 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, - 0x69, 0x74, 0x79, 0x22, 0x2f, 0x0a, 0x17, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x75, 0x6c, - 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x96, 0x02, 0x0a, 0x22, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x44, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x76, 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, - 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x61, - 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x61, - 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, - 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, - 0x26, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x75, 0x6d, - 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, - 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x6f, 0x0a, - 0x23, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x76, 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0f, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x5f, - 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x67, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x67, 0x67, 0x12, 0x0c, 0x0a, 0x01, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, 0x6b, + 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x6a, 0x0a, 0x12, + 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x6f, 0x70, 0x4b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x54, 0x0a, 0x13, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x63, 0x61, + 0x6c, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 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, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x52, 0x12, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x63, 0x61, + 0x6c, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x6e, 0x0a, 0x12, 0x43, 0x61, 0x74, 0x65, + 0x67, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x2c, + 0x0a, 0x05, 0x74, 0x6f, 0x70, 0x5f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x0e, - 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x9b, - 0x02, 0x0a, 0x0e, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, - 0x79, 0x12, 0x5d, 0x0a, 0x16, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x5f, 0x68, 0x69, 0x73, - 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x5f, 0x62, 0x69, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x67, 0x72, 0x61, 0x6d, 0x42, 0x69, 0x6e, 0x73, 0x48, 0x00, 0x52, 0x14, 0x6e, 0x75, 0x6d, 0x65, - 0x72, 0x69, 0x63, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x42, 0x69, 0x6e, 0x73, - 0x12, 0x53, 0x0a, 0x12, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, - 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, - 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, - 0x48, 0x00, 0x52, 0x11, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x53, 0x74, 0x61, 0x74, 0x69, - 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x4d, 0x0a, 0x10, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, - 0x5f, 0x6f, 0x75, 0x74, 0x6c, 0x69, 0x65, 0x72, 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, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x4f, 0x75, 0x74, 0x6c, 0x69, 0x65, 0x72, - 0x73, 0x48, 0x00, 0x52, 0x0f, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x4f, 0x75, 0x74, 0x6c, - 0x69, 0x65, 0x72, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x63, 0x61, 0x73, 0x65, 0x22, 0xcc, 0x01, 0x0a, - 0x14, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, - 0x6d, 0x42, 0x69, 0x6e, 0x73, 0x12, 0x3d, 0x0a, 0x04, 0x62, 0x69, 0x6e, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x42, 0x69, 0x6e, 0x73, 0x2e, 0x42, 0x69, 0x6e, 0x52, 0x04, - 0x62, 0x69, 0x6e, 0x73, 0x1a, 0x75, 0x0a, 0x03, 0x42, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x62, - 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x62, 0x75, 0x63, - 0x6b, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, - 0x52, 0x03, 0x6c, 0x6f, 0x77, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x69, 0x64, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x6d, 0x69, 0x64, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69, 0x67, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x04, 0x68, 0x69, 0x67, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x91, 0x01, 0x0a, 0x11, - 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, - 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, - 0x6d, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, - 0x52, 0x03, 0x6d, 0x61, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65, 0x61, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x04, 0x6d, 0x65, 0x61, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x71, 0x32, 0x35, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x71, 0x32, 0x35, 0x12, 0x10, 0x0a, 0x03, 0x71, - 0x35, 0x30, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x71, 0x35, 0x30, 0x12, 0x10, 0x0a, - 0x03, 0x71, 0x37, 0x35, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x71, 0x37, 0x35, 0x12, - 0x0e, 0x0a, 0x02, 0x73, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x02, 0x73, 0x64, 0x22, - 0xd0, 0x01, 0x0a, 0x0f, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x4f, 0x75, 0x74, 0x6c, 0x69, - 0x65, 0x72, 0x73, 0x12, 0x44, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x6c, 0x69, 0x65, 0x72, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x4f, - 0x75, 0x74, 0x6c, 0x69, 0x65, 0x72, 0x73, 0x2e, 0x4f, 0x75, 0x74, 0x6c, 0x69, 0x65, 0x72, 0x52, - 0x08, 0x6f, 0x75, 0x74, 0x6c, 0x69, 0x65, 0x72, 0x73, 0x1a, 0x77, 0x0a, 0x07, 0x4f, 0x75, 0x74, - 0x6c, 0x69, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, - 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x6c, 0x6f, 0x77, 0x12, 0x12, - 0x0a, 0x04, 0x68, 0x69, 0x67, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, 0x68, 0x69, - 0x67, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x22, 0x8a, 0x02, 0x0a, 0x16, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, - 0x65, 0x47, 0x72, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, - 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1c, - 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, - 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x61, 0x74, 0x61, - 0x62, 0x61, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x12, 0x26, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, - 0x75, 0x6d, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, - 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, - 0x54, 0x0a, 0x17, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x47, 0x72, 0x61, - 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x74, 0x69, - 0x6d, 0x65, 0x5f, 0x67, 0x72, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, - 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x47, 0x72, 0x61, 0x69, 0x6e, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x47, 0x72, 0x61, 0x69, 0x6e, 0x22, 0xde, 0x02, 0x0a, 0x1d, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, - 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, - 0x73, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, - 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x73, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x61, 0x74, - 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x26, 0x0a, 0x0a, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, - 0x01, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x4b, 0x0a, - 0x10, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, - 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, - 0x72, 0x61, 0x6d, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x0f, 0x68, 0x69, 0x73, 0x74, 0x6f, - 0x67, 0x72, 0x61, 0x6d, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, - 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, - 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x6a, 0x0a, 0x1e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, - 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0f, 0x6e, 0x75, 0x6d, 0x65, - 0x72, 0x69, 0x63, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x6d, 0x6d, 0x61, - 0x72, 0x79, 0x52, 0x0e, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x6d, 0x6d, 0x61, - 0x72, 0x79, 0x22, 0x8d, 0x02, 0x0a, 0x19, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x52, 0x75, 0x67, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x54, 0x6f, 0x70, 0x4b, 0x48, 0x00, 0x52, 0x04, 0x74, 0x6f, 0x70, 0x4b, 0x12, 0x22, 0x0a, 0x0b, + 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x01, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, + 0x42, 0x06, 0x0a, 0x04, 0x63, 0x61, 0x73, 0x65, 0x22, 0x8a, 0x01, 0x0a, 0x04, 0x54, 0x6f, 0x70, + 0x4b, 0x12, 0x35, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x70, 0x4b, 0x2e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x1a, 0x4b, 0x0a, 0x05, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 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, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x8a, 0x02, 0x0a, 0x16, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, + 0x4e, 0x75, 0x6c, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x05, @@ -10159,669 +10023,818 @@ var file_rill_runtime_v1_queries_proto_rawDesc = []byte{ 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, - 0x74, 0x79, 0x22, 0x66, 0x0a, 0x1a, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x52, 0x75, 0x67, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x48, 0x0a, 0x0f, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x5f, 0x73, 0x75, 0x6d, 0x6d, - 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, - 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x75, 0x6d, 0x65, - 0x72, 0x69, 0x63, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x0e, 0x6e, 0x75, 0x6d, 0x65, - 0x72, 0x69, 0x63, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x8a, 0x02, 0x0a, 0x16, 0x43, - 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, - 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, - 0x65, 0x6d, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x62, - 0x61, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x26, 0x0a, 0x0a, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, - 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x28, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, - 0x0a, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, - 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, - 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x6a, 0x0a, 0x17, 0x43, 0x6f, 0x6c, 0x75, 0x6d, - 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x12, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, - 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, + 0x74, 0x79, 0x22, 0x2f, 0x0a, 0x17, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x75, 0x6c, 0x6c, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x22, 0x96, 0x02, 0x0a, 0x22, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x44, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x76, 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, + 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x61, 0x74, + 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x61, 0x74, + 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, + 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x26, + 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, + 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x6f, 0x0a, 0x23, + 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x76, + 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0f, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x5f, 0x73, + 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, + 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, + 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x0e, 0x6e, + 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x9b, 0x02, + 0x0a, 0x0e, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, + 0x12, 0x5d, 0x0a, 0x16, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x5f, 0x68, 0x69, 0x73, 0x74, + 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x5f, 0x62, 0x69, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x25, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, + 0x72, 0x61, 0x6d, 0x42, 0x69, 0x6e, 0x73, 0x48, 0x00, 0x52, 0x14, 0x6e, 0x75, 0x6d, 0x65, 0x72, + 0x69, 0x63, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x42, 0x69, 0x6e, 0x73, 0x12, + 0x53, 0x0a, 0x12, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x75, + 0x6d, 0x65, 0x72, 0x69, 0x63, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x48, + 0x00, 0x52, 0x11, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, + 0x74, 0x69, 0x63, 0x73, 0x12, 0x4d, 0x0a, 0x10, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x5f, + 0x6f, 0x75, 0x74, 0x6c, 0x69, 0x65, 0x72, 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, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, - 0x79, 0x52, 0x10, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x6d, 0x6d, - 0x61, 0x72, 0x79, 0x22, 0xa8, 0x01, 0x0a, 0x10, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, - 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x2c, 0x0a, 0x03, 0x6d, 0x69, 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, 0x03, 0x6d, 0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x03, 0x6d, 0x61, 0x78, 0x18, 0x02, 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, - 0x03, 0x6d, 0x61, 0x78, 0x12, 0x38, 0x0a, 0x09, 0x77, 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, - 0x6b, 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, 0x77, 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x22, 0x8c, - 0x02, 0x0a, 0x18, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x43, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, - 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, + 0x2e, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x4f, 0x75, 0x74, 0x6c, 0x69, 0x65, 0x72, 0x73, + 0x48, 0x00, 0x52, 0x0f, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x4f, 0x75, 0x74, 0x6c, 0x69, + 0x65, 0x72, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x63, 0x61, 0x73, 0x65, 0x22, 0xcc, 0x01, 0x0a, 0x14, + 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, + 0x42, 0x69, 0x6e, 0x73, 0x12, 0x3d, 0x0a, 0x04, 0x62, 0x69, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x42, 0x69, 0x6e, 0x73, 0x2e, 0x42, 0x69, 0x6e, 0x52, 0x04, 0x62, + 0x69, 0x6e, 0x73, 0x1a, 0x75, 0x0a, 0x03, 0x42, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, + 0x63, 0x6b, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x03, 0x6c, 0x6f, 0x77, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x69, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x6d, 0x69, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69, 0x67, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, + 0x68, 0x69, 0x67, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x91, 0x01, 0x0a, 0x11, 0x4e, + 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, + 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x6d, + 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x03, 0x6d, 0x61, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65, 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x04, 0x6d, 0x65, 0x61, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x71, 0x32, 0x35, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x71, 0x32, 0x35, 0x12, 0x10, 0x0a, 0x03, 0x71, 0x35, + 0x30, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x71, 0x35, 0x30, 0x12, 0x10, 0x0a, 0x03, + 0x71, 0x37, 0x35, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x71, 0x37, 0x35, 0x12, 0x0e, + 0x0a, 0x02, 0x73, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x02, 0x73, 0x64, 0x22, 0xd0, + 0x01, 0x0a, 0x0f, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x4f, 0x75, 0x74, 0x6c, 0x69, 0x65, + 0x72, 0x73, 0x12, 0x44, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x6c, 0x69, 0x65, 0x72, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x4f, 0x75, + 0x74, 0x6c, 0x69, 0x65, 0x72, 0x73, 0x2e, 0x4f, 0x75, 0x74, 0x6c, 0x69, 0x65, 0x72, 0x52, 0x08, + 0x6f, 0x75, 0x74, 0x6c, 0x69, 0x65, 0x72, 0x73, 0x1a, 0x77, 0x0a, 0x07, 0x4f, 0x75, 0x74, 0x6c, + 0x69, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6c, + 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x6c, 0x6f, 0x77, 0x12, 0x12, 0x0a, + 0x04, 0x68, 0x69, 0x67, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, 0x68, 0x69, 0x67, + 0x68, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x22, 0x8a, 0x02, 0x0a, 0x16, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, + 0x47, 0x72, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, + 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x64, + 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, + 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x62, + 0x61, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x12, 0x26, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x75, + 0x6d, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, + 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x54, + 0x0a, 0x17, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x47, 0x72, 0x61, 0x69, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x74, 0x69, 0x6d, + 0x65, 0x5f, 0x67, 0x72, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, + 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x47, 0x72, 0x61, 0x69, 0x6e, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x47, + 0x72, 0x61, 0x69, 0x6e, 0x22, 0xde, 0x02, 0x0a, 0x1d, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, + 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, + 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, + 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x73, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x61, 0x74, 0x61, + 0x62, 0x61, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x26, 0x0a, 0x0a, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, + 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, + 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x10, + 0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, + 0x61, 0x6d, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x0f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, + 0x72, 0x61, 0x6d, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x6a, 0x0a, 0x1e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, + 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0f, 0x6e, 0x75, 0x6d, 0x65, 0x72, + 0x69, 0x63, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, + 0x79, 0x52, 0x0e, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, + 0x79, 0x22, 0x8d, 0x02, 0x0a, 0x19, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x52, 0x75, 0x67, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, + 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, + 0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x61, + 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x12, 0x26, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, + 0x52, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0b, 0x63, + 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x75, 0x6d, + 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x22, 0x66, 0x0a, 0x1a, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x52, 0x75, 0x67, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x48, 0x0a, 0x0f, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, + 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x75, 0x6d, 0x65, 0x72, + 0x69, 0x63, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x0e, 0x6e, 0x75, 0x6d, 0x65, 0x72, + 0x69, 0x63, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x8a, 0x02, 0x0a, 0x16, 0x43, 0x6f, + 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, + 0x27, 0x0a, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, + 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x26, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, + 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x28, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, + 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, + 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, + 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x6a, 0x0a, 0x17, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, + 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x4f, 0x0a, 0x12, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5f, + 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, + 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, + 0x52, 0x10, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, + 0x72, 0x79, 0x22, 0xa8, 0x01, 0x0a, 0x10, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, + 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x2c, 0x0a, 0x03, 0x6d, 0x69, 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, 0x03, 0x6d, 0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x03, 0x6d, 0x61, 0x78, 0x18, 0x02, 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, 0x03, + 0x6d, 0x61, 0x78, 0x12, 0x38, 0x0a, 0x09, 0x77, 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, 0x6b, + 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, 0x77, 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x22, 0x8c, 0x02, + 0x0a, 0x18, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x43, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, + 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x61, 0x74, + 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x61, 0x74, + 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, + 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x26, + 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, + 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x71, 0x0a, 0x19, + 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x43, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x13, 0x63, 0x61, 0x74, + 0x65, 0x67, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, + 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, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, + 0x69, 0x63, 0x61, 0x6c, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x12, 0x63, 0x61, 0x74, + 0x65, 0x67, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, + 0x8c, 0x05, 0x0a, 0x17, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x61, - 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x61, + 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, - 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x26, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x75, 0x6d, - 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, - 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x71, 0x0a, - 0x19, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x43, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, - 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x13, 0x63, 0x61, - 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, - 0x79, 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, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, - 0x72, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x12, 0x63, 0x61, - 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, - 0x22, 0x8c, 0x05, 0x0a, 0x17, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x53, - 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, + 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x5b, 0x0a, 0x08, 0x6d, 0x65, 0x61, 0x73, 0x75, + 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x72, 0x69, 0x6c, 0x6c, + 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, + 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x2e, 0x42, 0x61, 0x73, 0x69, 0x63, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, + 0x42, 0x08, 0xfa, 0x42, 0x05, 0x92, 0x01, 0x02, 0x08, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x61, 0x73, + 0x75, 0x72, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x15, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x5f, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x13, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x43, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1f, 0x0a, 0x06, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x1a, 0x02, 0x28, 0x00, 0x52, + 0x06, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x12, 0x28, 0x0a, 0x0b, 0x73, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x42, 0x07, 0xfa, 0x42, + 0x04, 0x1a, 0x02, 0x28, 0x00, 0x52, 0x0a, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x69, 0x7a, + 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1b, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x1a, 0x62, 0x0a, 0x0c, 0x42, 0x61, + 0x73, 0x69, 0x63, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x27, 0x0a, 0x0a, 0x65, 0x78, + 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, + 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x71, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x71, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x57, + 0x0a, 0x18, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x06, 0x72, 0x6f, + 0x6c, 0x6c, 0x75, 0x70, 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, 0x54, 0x69, 0x6d, + 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, + 0x06, 0x72, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x22, 0xb7, 0x01, 0x0a, 0x13, 0x54, 0x69, 0x6d, 0x65, + 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, + 0x30, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 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, 0x05, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x12, 0x2c, 0x0a, 0x03, 0x65, 0x6e, 0x64, 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, 0x03, 0x65, 0x6e, 0x64, 0x12, + 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x47, 0x72, 0x61, 0x69, 0x6e, 0x42, 0x08, 0xfa, + 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, + 0x6c, 0x22, 0xa9, 0x01, 0x0a, 0x12, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, + 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x73, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x70, 0x61, 0x72, 0x6b, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x73, 0x70, 0x61, 0x72, 0x6b, 0x12, 0x1f, 0x0a, 0x0b, + 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0a, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x82, 0x01, + 0x0a, 0x0f, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x12, 0x2a, 0x0a, 0x02, 0x74, 0x73, 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, 0x02, 0x74, 0x73, 0x12, 0x10, 0x0a, + 0x03, 0x62, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x62, 0x69, 0x6e, 0x12, + 0x31, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 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, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, + 0x64, 0x73, 0x22, 0xe1, 0x01, 0x0a, 0x17, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x72, 0x64, + 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, + 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, + 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, + 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x61, 0x74, + 0x61, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x12, 0x26, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, + 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, + 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, + 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x3c, 0x0a, 0x18, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, + 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, + 0x6c, 0x69, 0x74, 0x79, 0x22, 0xdd, 0x01, 0x0a, 0x13, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, + 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, - 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, + 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x64, - 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, + 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x62, - 0x61, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, + 0x61, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x26, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x5b, 0x0a, 0x08, 0x6d, 0x65, 0x61, 0x73, - 0x75, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, - 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x2e, 0x42, 0x61, 0x73, 0x69, 0x63, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, - 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x92, 0x01, 0x02, 0x08, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x61, - 0x73, 0x75, 0x72, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x15, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x5f, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x13, 0x74, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x43, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, - 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, - 0x69, 0x65, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x09, 0x74, 0x69, - 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1f, 0x0a, 0x06, 0x70, 0x69, 0x78, 0x65, 0x6c, - 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x1a, 0x02, 0x28, 0x00, - 0x52, 0x06, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x12, 0x28, 0x0a, 0x0b, 0x73, 0x61, 0x6d, 0x70, - 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x42, 0x07, 0xfa, - 0x42, 0x04, 0x1a, 0x02, 0x28, 0x00, 0x52, 0x0a, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x69, - 0x7a, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1b, - 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x1a, 0x62, 0x0a, 0x0c, 0x42, - 0x61, 0x73, 0x69, 0x63, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x27, 0x0a, 0x0a, 0x65, - 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x71, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x71, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x22, - 0x57, 0x0a, 0x18, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, - 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x06, 0x72, - 0x6f, 0x6c, 0x6c, 0x75, 0x70, 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, 0x54, 0x69, - 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x52, 0x06, 0x72, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x22, 0xb7, 0x01, 0x0a, 0x13, 0x54, 0x69, 0x6d, - 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, - 0x12, 0x30, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 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, 0x05, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x12, 0x2c, 0x0a, 0x03, 0x65, 0x6e, 0x64, 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, 0x03, 0x65, 0x6e, 0x64, - 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x47, 0x72, 0x61, 0x69, 0x6e, 0x42, 0x08, - 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, - 0x61, 0x6c, 0x22, 0xa9, 0x01, 0x0a, 0x12, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x72, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, - 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x72, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x70, 0x61, 0x72, 0x6b, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, - 0x73, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x73, 0x70, 0x61, 0x72, 0x6b, 0x12, 0x1f, 0x0a, - 0x0b, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0a, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x82, - 0x01, 0x0a, 0x0f, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x12, 0x2a, 0x0a, 0x02, 0x74, 0x73, 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, 0x02, 0x74, 0x73, 0x12, 0x10, - 0x0a, 0x03, 0x62, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x62, 0x69, 0x6e, - 0x12, 0x31, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 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, 0x07, 0x72, 0x65, 0x63, 0x6f, - 0x72, 0x64, 0x73, 0x22, 0xe1, 0x01, 0x0a, 0x17, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x72, - 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x22, 0x96, 0x02, 0x0a, 0x14, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, + 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, + 0x0f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, + 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x12, 0x6e, 0x0a, 0x13, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x75, 0x6d, + 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x55, 0x6e, 0x73, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x12, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x43, + 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x1a, 0x45, 0x0a, 0x17, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 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, 0x6b, 0x0a, + 0x0d, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 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, 0x32, 0x0a, 0x15, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x73, + 0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x13, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0xf9, 0x01, 0x0a, 0x10, 0x54, + 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, - 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, + 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, - 0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x61, - 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x06, 0x20, + 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x26, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, - 0x52, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, - 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, - 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x3c, 0x0a, 0x18, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x43, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, - 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, - 0x61, 0x6c, 0x69, 0x74, 0x79, 0x22, 0xdd, 0x01, 0x0a, 0x13, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, - 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, - 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1c, - 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, - 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x61, 0x74, 0x61, - 0x62, 0x61, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x12, 0x26, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, - 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, - 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x96, 0x02, 0x0a, 0x14, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, - 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, - 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, - 0x65, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, - 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x12, 0x6e, 0x0a, 0x13, 0x75, 0x6e, 0x73, 0x75, 0x70, - 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x75, - 0x6d, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x55, 0x6e, 0x73, 0x75, - 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x12, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, - 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x1a, 0x45, 0x0a, 0x17, 0x55, 0x6e, 0x73, 0x75, 0x70, - 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 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, 0x6b, - 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 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, 0x32, 0x0a, 0x15, 0x6c, 0x61, 0x72, 0x67, 0x65, - 0x73, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x13, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x53, - 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0xf9, 0x01, 0x0a, 0x10, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, - 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, - 0x1a, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x64, - 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x12, 0x26, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, - 0x01, 0x52, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x05, - 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x42, 0x07, 0xfa, 0x42, 0x04, - 0x1a, 0x02, 0x28, 0x00, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, - 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, - 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x40, 0x0a, 0x11, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x52, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x04, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 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, 0x04, 0x64, 0x61, 0x74, 0x61, 0x2a, 0x70, 0x0a, 0x0e, 0x42, 0x75, 0x69, - 0x6c, 0x74, 0x69, 0x6e, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x42, - 0x55, 0x49, 0x4c, 0x54, 0x49, 0x4e, 0x5f, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x5f, 0x55, - 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, - 0x42, 0x55, 0x49, 0x4c, 0x54, 0x49, 0x4e, 0x5f, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x5f, - 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x01, 0x12, 0x22, 0x0a, 0x1e, 0x42, 0x55, 0x49, 0x4c, 0x54, - 0x49, 0x4e, 0x5f, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, - 0x5f, 0x44, 0x49, 0x53, 0x54, 0x49, 0x4e, 0x43, 0x54, 0x10, 0x02, 0x2a, 0x9e, 0x02, 0x0a, 0x1d, - 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x43, 0x6f, 0x6d, 0x70, 0x61, - 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x53, 0x6f, 0x72, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x31, 0x0a, - 0x2d, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x53, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x43, 0x4f, - 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, - 0x12, 0x30, 0x0a, 0x2c, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x53, 0x5f, 0x56, 0x49, 0x45, 0x57, - 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x4f, 0x52, 0x54, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, - 0x10, 0x01, 0x12, 0x36, 0x0a, 0x32, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x53, 0x5f, 0x56, 0x49, - 0x45, 0x57, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x4f, - 0x52, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, - 0x4f, 0x4e, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x02, 0x12, 0x2f, 0x0a, 0x2b, 0x4d, 0x45, + 0x52, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x05, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x1a, + 0x02, 0x28, 0x00, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, + 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, + 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x40, 0x0a, 0x11, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, + 0x6f, 0x77, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x01, 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, 0x04, 0x64, 0x61, 0x74, 0x61, 0x2a, 0x70, 0x0a, 0x0e, 0x42, 0x75, 0x69, 0x6c, + 0x74, 0x69, 0x6e, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x42, 0x55, + 0x49, 0x4c, 0x54, 0x49, 0x4e, 0x5f, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x5f, 0x55, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x42, + 0x55, 0x49, 0x4c, 0x54, 0x49, 0x4e, 0x5f, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x5f, 0x43, + 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x01, 0x12, 0x22, 0x0a, 0x1e, 0x42, 0x55, 0x49, 0x4c, 0x54, 0x49, + 0x4e, 0x5f, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, + 0x44, 0x49, 0x53, 0x54, 0x49, 0x4e, 0x43, 0x54, 0x10, 0x02, 0x2a, 0x9e, 0x02, 0x0a, 0x1d, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, + 0x69, 0x73, 0x6f, 0x6e, 0x53, 0x6f, 0x72, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x31, 0x0a, 0x2d, + 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x53, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x43, 0x4f, 0x4d, + 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, + 0x30, 0x0a, 0x2c, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x53, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, + 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, + 0x01, 0x12, 0x36, 0x0a, 0x32, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x53, 0x5f, 0x56, 0x49, 0x45, + 0x57, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x4f, 0x52, + 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, + 0x4e, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x02, 0x12, 0x2f, 0x0a, 0x2b, 0x4d, 0x45, 0x54, + 0x52, 0x49, 0x43, 0x53, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, + 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, + 0x42, 0x53, 0x5f, 0x44, 0x45, 0x4c, 0x54, 0x41, 0x10, 0x03, 0x12, 0x2f, 0x0a, 0x2b, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x53, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x41, 0x42, 0x53, 0x5f, 0x44, 0x45, 0x4c, 0x54, 0x41, 0x10, 0x03, 0x12, 0x2f, 0x0a, 0x2b, 0x4d, - 0x45, 0x54, 0x52, 0x49, 0x43, 0x53, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x43, 0x4f, 0x4d, 0x50, - 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x52, 0x45, 0x4c, 0x5f, 0x44, 0x45, 0x4c, 0x54, 0x41, 0x10, 0x04, 0x2a, 0xb0, 0x02, 0x0a, - 0x20, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x43, 0x6f, 0x6d, 0x70, - 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x34, 0x0a, 0x30, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x53, 0x5f, 0x56, 0x49, 0x45, - 0x57, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x45, 0x41, - 0x53, 0x55, 0x52, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x33, 0x0a, 0x2f, 0x4d, 0x45, 0x54, 0x52, 0x49, - 0x43, 0x53, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, - 0x4f, 0x4e, 0x5f, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x42, 0x41, 0x53, 0x45, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x01, 0x12, 0x39, 0x0a, 0x35, - 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x53, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x43, 0x4f, 0x4d, - 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, - 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x02, 0x12, 0x32, 0x0a, 0x2e, 0x4d, 0x45, 0x54, 0x52, 0x49, - 0x43, 0x53, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, - 0x4f, 0x4e, 0x5f, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x41, 0x42, 0x53, 0x5f, 0x44, 0x45, 0x4c, 0x54, 0x41, 0x10, 0x03, 0x12, 0x32, 0x0a, 0x2e, 0x4d, + 0x52, 0x45, 0x4c, 0x5f, 0x44, 0x45, 0x4c, 0x54, 0x41, 0x10, 0x04, 0x2a, 0xb0, 0x02, 0x0a, 0x20, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x43, 0x6f, 0x6d, 0x70, 0x61, + 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x34, 0x0a, 0x30, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x53, 0x5f, 0x56, 0x49, 0x45, 0x57, + 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x45, 0x41, 0x53, + 0x55, 0x52, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x33, 0x0a, 0x2f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, + 0x53, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, + 0x4e, 0x5f, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, + 0x41, 0x53, 0x45, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x01, 0x12, 0x39, 0x0a, 0x35, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x53, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x4c, 0x5f, 0x44, 0x45, 0x4c, 0x54, 0x41, 0x10, 0x04, 0x2a, - 0x6d, 0x0a, 0x0f, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x4d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x12, 0x20, 0x0a, 0x1c, 0x48, 0x49, 0x53, 0x54, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x5f, - 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x48, 0x49, 0x53, 0x54, 0x4f, 0x47, 0x52, 0x41, - 0x4d, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x46, 0x44, 0x10, 0x01, 0x12, 0x1f, 0x0a, - 0x1b, 0x48, 0x49, 0x53, 0x54, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, - 0x44, 0x5f, 0x44, 0x49, 0x41, 0x47, 0x4e, 0x4f, 0x53, 0x54, 0x49, 0x43, 0x10, 0x02, 0x32, 0xba, - 0x2f, 0x0a, 0x0c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, - 0x74, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, + 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x56, + 0x41, 0x4c, 0x55, 0x45, 0x10, 0x02, 0x12, 0x32, 0x0a, 0x2e, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, + 0x53, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, + 0x4e, 0x5f, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, + 0x42, 0x53, 0x5f, 0x44, 0x45, 0x4c, 0x54, 0x41, 0x10, 0x03, 0x12, 0x32, 0x0a, 0x2e, 0x4d, 0x45, + 0x54, 0x52, 0x49, 0x43, 0x53, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, + 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x52, 0x45, 0x4c, 0x5f, 0x44, 0x45, 0x4c, 0x54, 0x41, 0x10, 0x04, 0x2a, 0x6d, + 0x0a, 0x0f, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x4d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x12, 0x20, 0x0a, 0x1c, 0x48, 0x49, 0x53, 0x54, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x5f, 0x4d, + 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, + 0x44, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x48, 0x49, 0x53, 0x54, 0x4f, 0x47, 0x52, 0x41, 0x4d, + 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x46, 0x44, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, + 0x48, 0x49, 0x53, 0x54, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, + 0x5f, 0x44, 0x49, 0x41, 0x47, 0x4e, 0x4f, 0x53, 0x54, 0x49, 0x43, 0x10, 0x02, 0x32, 0xba, 0x2f, + 0x0a, 0x0c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x74, + 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x26, 0x3a, - 0x01, 0x2a, 0x22, 0x21, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, - 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x80, 0x01, 0x0a, 0x06, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, - 0x12, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x35, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x3a, 0x01, 0x2a, 0x22, 0x2a, 0x2f, 0x76, - 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, - 0x73, 0x2f, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x9b, 0x01, 0x0a, 0x0c, 0x45, 0x78, 0x70, - 0x6f, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, - 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6f, - 0x72, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x25, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x38, 0x3a, 0x01, - 0x2a, 0x22, 0x33, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, - 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x2f, 0x7b, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x7d, 0x2f, - 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x9e, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x26, 0x2e, 0x72, 0x69, 0x6c, 0x6c, - 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x27, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, - 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3b, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x35, 0x12, 0x33, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, - 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2d, - 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0xd2, 0x01, 0x0a, 0x16, 0x4d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x2e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, - 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x26, 0x3a, 0x01, + 0x2a, 0x22, 0x21, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, + 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x12, 0x80, 0x01, 0x0a, 0x06, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x12, + 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x35, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x3a, 0x01, 0x2a, 0x22, 0x2a, 0x2f, 0x76, 0x31, + 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x2f, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x9b, 0x01, 0x0a, 0x0c, 0x45, 0x78, 0x70, 0x6f, + 0x72, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, + 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, + 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x38, 0x3a, 0x01, 0x2a, + 0x22, 0x33, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, + 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x73, 0x2f, 0x7b, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x7d, 0x2f, 0x65, + 0x78, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x9e, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x26, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x27, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3b, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x35, 0x12, 0x33, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, + 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, + 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2d, 0x73, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0xd2, 0x01, 0x0a, 0x16, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x2e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x41, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x41, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x57, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x51, 0x3a, 0x01, 0x2a, 0x22, 0x4c, 0x2f, + 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x76, 0x69, 0x65, 0x77, 0x73, + 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x7d, 0x2f, + 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0xc7, 0x01, 0x0a, 0x12, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, 0x6f, 0x70, 0x6c, 0x69, + 0x73, 0x74, 0x12, 0x2a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, - 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x57, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x51, 0x3a, 0x01, 0x2a, 0x22, 0x4c, - 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, - 0x69, 0x65, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x76, 0x69, 0x65, 0x77, - 0x73, 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x7d, - 0x2f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0xc7, 0x01, 0x0a, - 0x12, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, 0x6f, 0x70, 0x6c, - 0x69, 0x73, 0x74, 0x12, 0x2a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, - 0x77, 0x54, 0x6f, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, 0x6f, 0x70, - 0x6c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x58, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x52, 0x3a, 0x01, 0x2a, 0x22, 0x4d, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x6d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x73, 0x2d, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x74, - 0x6f, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x12, 0xd8, 0x01, 0x0a, 0x15, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, - 0x12, 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x43, 0x6f, - 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x54, 0x6f, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, + 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, 0x6f, 0x70, 0x6c, + 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x58, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x52, 0x3a, 0x01, 0x2a, 0x22, 0x4d, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, + 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x2d, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x74, 0x6f, + 0x70, 0x6c, 0x69, 0x73, 0x74, 0x12, 0xd8, 0x01, 0x0a, 0x15, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x56, 0x69, 0x65, 0x77, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x12, + 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x43, 0x6f, 0x6d, - 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x60, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x5a, 0x3a, 0x01, 0x2a, 0x22, 0x55, 0x2f, 0x76, 0x31, 0x2f, - 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, - 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2f, 0x7b, 0x6d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x7d, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x2d, 0x74, 0x6f, 0x70, 0x6c, 0x69, 0x73, - 0x74, 0x12, 0xd3, 0x01, 0x0a, 0x15, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, - 0x77, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x2d, 0x2e, 0x72, 0x69, - 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, - 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x72, 0x69, 0x6c, + 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, + 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x43, 0x6f, 0x6d, 0x70, + 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x60, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x5a, 0x3a, 0x01, 0x2a, 0x22, 0x55, 0x2f, 0x76, 0x31, 0x2f, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2f, 0x7b, 0x6d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, + 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x2d, 0x74, 0x6f, 0x70, 0x6c, 0x69, 0x73, 0x74, + 0x12, 0xd3, 0x01, 0x0a, 0x15, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, + 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5b, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x55, 0x3a, 0x01, 0x2a, 0x22, 0x50, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, - 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x2d, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x73, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0xc3, 0x01, 0x0a, 0x11, 0x4d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x12, 0x29, 0x2e, - 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, 0x6f, 0x74, 0x61, 0x6c, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x57, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x51, 0x3a, 0x01, 0x2a, 0x22, - 0x4c, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, - 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, - 0x72, 0x69, 0x65, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x76, 0x69, 0x65, - 0x77, 0x73, 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, 0x77, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x12, 0xbb, 0x01, - 0x0a, 0x0f, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x52, 0x6f, 0x77, - 0x73, 0x12, 0x27, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x52, - 0x6f, 0x77, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x52, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x55, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4f, 0x3a, 0x01, 0x2a, 0x22, - 0x4a, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, - 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, - 0x72, 0x69, 0x65, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x76, 0x69, 0x65, - 0x77, 0x73, 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, 0x77, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x6f, 0x77, 0x73, 0x12, 0xd8, 0x01, 0x0a, 0x14, - 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, 0x69, 0x6d, 0x65, 0x52, - 0x61, 0x6e, 0x67, 0x65, 0x12, 0x2c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, - 0x65, 0x77, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, - 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x63, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x5d, 0x3a, 0x01, 0x2a, 0x22, 0x58, 0x2f, 0x76, - 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, - 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2f, - 0x7b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x7d, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x2d, 0x73, - 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0xc0, 0x01, 0x0a, 0x11, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x29, 0x2e, 0x72, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, + 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5b, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x55, 0x3a, 0x01, 0x2a, 0x22, 0x50, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, + 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x2d, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, + 0x5f, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0xc3, 0x01, 0x0a, 0x11, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x12, 0x29, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x73, 0x56, 0x69, 0x65, 0x77, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x54, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4e, 0x12, 0x4c, 0x2f, 0x76, 0x31, + 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x57, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x51, 0x3a, 0x01, 0x2a, 0x22, 0x4c, + 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, + 0x69, 0x65, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x76, 0x69, 0x65, 0x77, + 0x73, 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x12, 0xbb, 0x01, 0x0a, + 0x0f, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x52, 0x6f, 0x77, 0x73, + 0x12, 0x27, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x52, 0x6f, + 0x77, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x72, 0x69, 0x6c, 0x6c, + 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x52, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x55, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4f, 0x3a, 0x01, 0x2a, 0x22, 0x4a, + 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, + 0x69, 0x65, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x76, 0x69, 0x65, 0x77, + 0x73, 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x6f, 0x77, 0x73, 0x12, 0xd8, 0x01, 0x0a, 0x14, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, + 0x6e, 0x67, 0x65, 0x12, 0x2c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, + 0x77, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, + 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x63, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x5d, 0x3a, 0x01, 0x2a, 0x22, 0x58, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x7d, 0x2f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0xc3, 0x01, 0x0a, 0x11, 0x4d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, - 0x29, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x53, 0x65, 0x61, - 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x57, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x51, 0x3a, 0x01, - 0x2a, 0x22, 0x4c, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, - 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, - 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x76, - 0x69, 0x65, 0x77, 0x73, 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, - 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, - 0xd4, 0x01, 0x0a, 0x15, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, - 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, + 0x65, 0x7d, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x2d, 0x73, 0x75, + 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0xc0, 0x01, 0x0a, 0x11, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x56, 0x69, 0x65, 0x77, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x29, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, + 0x56, 0x69, 0x65, 0x77, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x54, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4e, 0x12, 0x4c, 0x2f, 0x76, 0x31, 0x2f, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, + 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2f, 0x7b, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x7d, 0x2f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0xc3, 0x01, 0x0a, 0x11, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x29, + 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x53, 0x65, 0x61, 0x72, + 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x57, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x51, 0x3a, 0x01, 0x2a, + 0x22, 0x4c, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, + 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, + 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x76, 0x69, + 0x65, 0x77, 0x73, 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, + 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0xd4, + 0x01, 0x0a, 0x15, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, 0x69, + 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x56, - 0x3a, 0x01, 0x2a, 0x22, 0x51, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, - 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, - 0x2d, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, - 0x76, 0x69, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x2d, - 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0xd7, 0x01, 0x0a, 0x16, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 0x2e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x41, - 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x41, - 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x5c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x56, 0x3a, 0x01, 0x2a, 0x22, 0x51, 0x2f, - 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, - 0x65, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x76, 0x69, 0x65, 0x77, 0x73, - 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0xd8, 0x01, 0x0a, 0x1d, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x45, 0x78, 0x70, 0x72, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x53, - 0x51, 0x4c, 0x12, 0x35, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x45, 0x78, 0x70, 0x72, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x53, - 0x51, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x72, 0x69, 0x6c, 0x6c, - 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x74, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x53, 0x51, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x48, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x42, 0x3a, 0x01, 0x2a, 0x22, 0x3d, 0x2f, 0x76, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, + 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x56, 0x69, 0x65, 0x77, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x56, 0x3a, + 0x01, 0x2a, 0x22, 0x51, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, + 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, + 0x76, 0x69, 0x65, 0x77, 0x73, 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, + 0x69, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x2d, 0x72, + 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0xd7, 0x01, 0x0a, 0x16, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x56, 0x69, 0x65, 0x77, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x2e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x41, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x41, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x5c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x56, 0x3a, 0x01, 0x2a, 0x22, 0x51, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, - 0x73, 0x2f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2d, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x12, 0xa8, 0x01, 0x0a, 0x0d, - 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x12, 0x25, 0x2e, - 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x43, 0x61, - 0x6e, 0x76, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x48, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x42, 0x3a, 0x01, 0x2a, 0x22, 0x3d, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x63, 0x61, 0x6e, - 0x76, 0x61, 0x73, 0x65, 0x73, 0x2f, 0x7b, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x7d, 0x2f, 0x72, - 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x12, 0xb6, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x6c, - 0x76, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x28, 0x2e, 0x72, 0x69, - 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x43, - 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x4d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x47, 0x3a, 0x01, 0x2a, 0x22, 0x42, 0x2f, 0x76, 0x31, + 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2f, + 0x7b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x7d, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0xd8, 0x01, 0x0a, 0x1d, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x45, 0x78, 0x70, 0x72, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x53, 0x51, + 0x4c, 0x12, 0x35, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x45, 0x78, 0x70, 0x72, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x53, 0x51, + 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, + 0x72, 0x74, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x4d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x73, 0x53, 0x51, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x48, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x42, 0x3a, 0x01, 0x2a, 0x22, 0x3d, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, - 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x63, 0x6f, 0x6d, - 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x7d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x12, - 0xc2, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x2e, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, - 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x53, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, - 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x53, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x47, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x41, 0x3a, 0x01, 0x2a, 0x22, 0x3c, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, + 0x2f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2d, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x12, 0xa8, 0x01, 0x0a, 0x0d, 0x52, + 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x12, 0x25, 0x2e, 0x72, + 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x43, 0x61, 0x6e, + 0x76, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x48, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x42, 0x3a, 0x01, 0x2a, 0x22, 0x3d, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, - 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x72, 0x65, 0x73, 0x6f, - 0x6c, 0x76, 0x65, 0x2d, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x2d, 0x73, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0x12, 0xc7, 0x01, 0x0a, 0x14, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x52, - 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2c, 0x2e, - 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x72, 0x69, - 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, - 0x6c, 0x75, 0x6d, 0x6e, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, - 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x52, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x4c, 0x3a, 0x01, 0x2a, 0x22, 0x47, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x63, 0x61, 0x6e, 0x76, + 0x61, 0x73, 0x65, 0x73, 0x2f, 0x7b, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x7d, 0x2f, 0x72, 0x65, + 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x12, 0xb6, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, + 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x28, 0x2e, 0x72, 0x69, 0x6c, + 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x6c, 0x76, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x43, 0x6f, + 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x4d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x47, 0x3a, 0x01, 0x2a, 0x22, 0x42, 0x2f, 0x76, 0x31, 0x2f, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, + 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x63, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x7d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x12, 0xc2, + 0x01, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x2e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, + 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, + 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x47, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x41, 0x3a, 0x01, 0x2a, 0x22, 0x3c, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, - 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x72, 0x6f, 0x6c, 0x6c, 0x75, - 0x70, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x73, 0x2f, 0x7b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0x9e, - 0x01, 0x0a, 0x0a, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x6f, 0x70, 0x4b, 0x12, 0x22, 0x2e, - 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x6f, 0x70, 0x4b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x23, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x6f, 0x70, 0x4b, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x47, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x41, 0x3a, 0x01, - 0x2a, 0x22, 0x3c, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, - 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, - 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x74, 0x6f, 0x70, 0x6b, 0x2f, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, - 0xb0, 0x01, 0x0a, 0x0f, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x75, 0x6c, 0x6c, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x27, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x75, 0x6c, 0x6c, - 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x72, + 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x6c, + 0x76, 0x65, 0x2d, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x2d, 0x73, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x12, 0xc7, 0x01, 0x0a, 0x14, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x52, 0x6f, + 0x6c, 0x6c, 0x75, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x75, 0x6c, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x44, 0x12, 0x42, - 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, - 0x69, 0x65, 0x73, 0x2f, 0x6e, 0x75, 0x6c, 0x6c, 0x2d, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2f, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x7d, 0x12, 0xe0, 0x01, 0x0a, 0x1b, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x44, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x76, 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, - 0x63, 0x73, 0x12, 0x33, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x44, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x76, 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, - 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x76, 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, - 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x56, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x50, 0x12, 0x4e, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, - 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x76, 0x65, 0x2d, 0x73, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, - 0x73, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0xb9, 0x01, 0x0a, 0x0f, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, - 0x54, 0x69, 0x6d, 0x65, 0x47, 0x72, 0x61, 0x69, 0x6e, 0x12, 0x27, 0x2e, 0x72, 0x69, 0x6c, 0x6c, - 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, - 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x47, 0x72, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x47, - 0x72, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x53, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x4d, 0x12, 0x4b, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x76, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x72, 0x69, 0x6c, + 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, + 0x75, 0x6d, 0x6e, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, + 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x52, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x4c, 0x3a, 0x01, 0x2a, 0x22, 0x47, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x65, - 0x73, 0x74, 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x2d, 0x67, 0x72, 0x61, 0x69, 0x6e, 0x2f, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x7d, 0x12, 0xcc, 0x01, 0x0a, 0x16, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x75, 0x6d, 0x65, - 0x72, 0x69, 0x63, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x12, 0x2e, 0x2e, 0x72, - 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x72, + 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x72, 0x6f, 0x6c, 0x6c, 0x75, 0x70, + 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, + 0x2f, 0x7b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0x9e, 0x01, + 0x0a, 0x0a, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x6f, 0x70, 0x4b, 0x12, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x51, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x4b, 0x12, 0x49, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, - 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x6e, 0x75, 0x6d, 0x65, 0x72, - 0x69, 0x63, 0x2d, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x2f, 0x74, 0x61, 0x62, + 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x6f, 0x70, 0x4b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x23, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x6f, 0x70, 0x4b, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x47, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x41, 0x3a, 0x01, 0x2a, + 0x22, 0x3c, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, + 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, + 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x74, 0x6f, 0x70, 0x6b, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x73, 0x2f, 0x7b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0xb0, + 0x01, 0x0a, 0x0f, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x75, 0x6c, 0x6c, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x27, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x75, 0x6c, 0x6c, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, + 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x75, 0x6c, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x44, 0x12, 0x42, 0x2f, + 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x2f, 0x6e, 0x75, 0x6c, 0x6c, 0x2d, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2f, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x7d, 0x12, 0xe0, 0x01, 0x0a, 0x1b, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x44, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x76, 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, + 0x73, 0x12, 0x33, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x76, 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x44, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x76, 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, + 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x56, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x50, 0x12, 0x4e, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, + 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x76, 0x65, 0x2d, 0x73, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, + 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x7d, 0x12, 0xb9, 0x01, 0x0a, 0x0f, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, + 0x69, 0x6d, 0x65, 0x47, 0x72, 0x61, 0x69, 0x6e, 0x12, 0x27, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, + 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x47, 0x72, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x28, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x47, 0x72, + 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x53, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x4d, 0x12, 0x4b, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, + 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x65, 0x73, + 0x74, 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x2d, 0x67, 0x72, 0x61, 0x69, 0x6e, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, - 0x12, 0xbc, 0x01, 0x0a, 0x12, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x52, 0x75, 0x67, 0x48, 0x69, - 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x12, 0x2a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, - 0x52, 0x75, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x52, 0x75, 0x67, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x4d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x47, 0x12, 0x45, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x72, 0x75, - 0x67, 0x2d, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x2f, 0x74, 0x61, 0x62, 0x6c, + 0x12, 0xcc, 0x01, 0x0a, 0x16, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x75, 0x6d, 0x65, 0x72, + 0x69, 0x63, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x12, 0x2e, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, + 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x67, 0x72, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, + 0x6c, 0x75, 0x6d, 0x6e, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x67, 0x72, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x51, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x4b, 0x12, 0x49, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, + 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, + 0x63, 0x2d, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, - 0xb8, 0x01, 0x0a, 0x0f, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, - 0x6e, 0x67, 0x65, 0x12, 0x27, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, - 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x72, - 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, + 0xbc, 0x01, 0x0a, 0x12, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x52, 0x75, 0x67, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x12, 0x2a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x52, + 0x75, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x52, 0x75, 0x67, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x4d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x47, 0x12, 0x45, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x72, 0x75, 0x67, + 0x2d, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x73, 0x2f, 0x7b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0xb8, + 0x01, 0x0a, 0x0f, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, + 0x67, 0x65, 0x12, 0x27, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x52, + 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, + 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x52, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4c, 0x12, 0x4a, 0x2f, + 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x2d, 0x73, 0x75, + 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0xbe, 0x01, 0x0a, 0x11, 0x43, 0x6f, + 0x6c, 0x75, 0x6d, 0x6e, 0x43, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x12, + 0x29, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x43, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, + 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x72, 0x69, 0x6c, + 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, + 0x75, 0x6d, 0x6e, 0x43, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x52, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4c, 0x12, 0x4a, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, - 0x69, 0x65, 0x73, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x2d, 0x73, - 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0xbe, 0x01, 0x0a, 0x11, 0x43, - 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x43, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, - 0x12, 0x29, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x43, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, - 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x72, 0x69, - 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, - 0x6c, 0x75, 0x6d, 0x6e, 0x43, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x52, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4c, 0x12, - 0x4a, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, + 0x69, 0x65, 0x73, 0x2f, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x2d, 0x63, 0x61, 0x72, 0x64, 0x69, + 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0xb6, 0x01, 0x0a, 0x10, 0x43, + 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, + 0x28, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x72, 0x69, 0x6c, 0x6c, + 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, + 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x47, 0x3a, 0x01, 0x2a, 0x22, + 0x42, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, - 0x72, 0x69, 0x65, 0x73, 0x2f, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x2d, 0x63, 0x61, 0x72, 0x64, - 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x2f, 0x7b, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0xb6, 0x01, 0x0a, 0x10, - 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, - 0x12, 0x28, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, - 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, - 0x75, 0x6d, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x47, 0x3a, 0x01, 0x2a, - 0x22, 0x42, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, - 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, - 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, - 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x7d, 0x12, 0xba, 0x01, 0x0a, 0x10, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, - 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x28, 0x2e, 0x72, 0x69, 0x6c, 0x6c, - 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x43, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x72, 0x64, 0x69, - 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x51, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4b, 0x12, 0x49, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, - 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x2d, 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2f, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x7d, 0x12, 0xac, 0x01, 0x0a, 0x0c, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x75, 0x6d, - 0x6e, 0x73, 0x12, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x72, 0x69, 0x65, 0x73, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x7d, 0x12, 0xba, 0x01, 0x0a, 0x10, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x72, + 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x28, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x4f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x49, 0x22, 0x47, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x63, 0x6f, 0x6c, - 0x75, 0x6d, 0x6e, 0x73, 0x2d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2f, 0x74, 0x61, 0x62, + 0x43, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x72, 0x64, 0x69, 0x6e, + 0x61, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x51, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x4b, 0x12, 0x49, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, + 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x2d, 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, - 0x12, 0x98, 0x01, 0x0a, 0x09, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x21, - 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3e, 0x12, 0x3c, 0x2f, - 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, - 0x65, 0x73, 0x2f, 0x72, 0x6f, 0x77, 0x73, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x2f, 0x7b, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x42, 0xbf, 0x01, 0x0a, 0x13, - 0x63, 0x6f, 0x6d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x51, 0x75, 0x65, 0x72, 0x69, 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, + 0x12, 0xac, 0x01, 0x0a, 0x0c, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, + 0x73, 0x12, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, + 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, + 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4f, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x49, 0x22, 0x47, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, + 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x63, 0x6f, 0x6c, 0x75, + 0x6d, 0x6e, 0x73, 0x2d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2f, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, + 0x98, 0x01, 0x0a, 0x09, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x21, 0x2e, + 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3e, 0x12, 0x3c, 0x2f, 0x76, + 0x31, 0x2f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, + 0x73, 0x2f, 0x72, 0x6f, 0x77, 0x73, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x42, 0xbf, 0x01, 0x0a, 0x13, 0x63, + 0x6f, 0x6d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x76, 0x31, 0x42, 0x0c, 0x51, 0x75, 0x65, 0x72, 0x69, 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 ( @@ -11111,120 +11124,121 @@ var file_rill_runtime_v1_queries_proto_depIdxs = []int32{ 109, // 142: rill.runtime.v1.ResolveCanvasResponse.referenced_metrics_views:type_name -> rill.runtime.v1.ResolveCanvasResponse.ReferencedMetricsViewsEntry 118, // 143: rill.runtime.v1.ResolveComponentRequest.args:type_name -> google.protobuf.Struct 118, // 144: rill.runtime.v1.ResolveComponentResponse.renderer_properties:type_name -> google.protobuf.Struct - 110, // 145: rill.runtime.v1.ResolveTemplatedStringRequest.additional_where_by_metrics_view:type_name -> rill.runtime.v1.ResolveTemplatedStringRequest.AdditionalWhereByMetricsViewEntry - 33, // 146: rill.runtime.v1.ResolveTemplatedStringRequest.additional_time_range:type_name -> rill.runtime.v1.TimeRange - 121, // 147: rill.runtime.v1.ColumnRollupIntervalResponse.start:type_name -> google.protobuf.Timestamp - 121, // 148: rill.runtime.v1.ColumnRollupIntervalResponse.end:type_name -> google.protobuf.Timestamp - 123, // 149: rill.runtime.v1.ColumnRollupIntervalResponse.interval:type_name -> rill.runtime.v1.TimeGrain - 71, // 150: rill.runtime.v1.ColumnTopKResponse.categorical_summary:type_name -> rill.runtime.v1.CategoricalSummary - 72, // 151: rill.runtime.v1.CategoricalSummary.top_k:type_name -> rill.runtime.v1.TopK - 111, // 152: rill.runtime.v1.TopK.entries:type_name -> rill.runtime.v1.TopK.Entry - 77, // 153: rill.runtime.v1.ColumnDescriptiveStatisticsResponse.numeric_summary:type_name -> rill.runtime.v1.NumericSummary - 78, // 154: rill.runtime.v1.NumericSummary.numeric_histogram_bins:type_name -> rill.runtime.v1.NumericHistogramBins - 79, // 155: rill.runtime.v1.NumericSummary.numeric_statistics:type_name -> rill.runtime.v1.NumericStatistics - 80, // 156: rill.runtime.v1.NumericSummary.numeric_outliers:type_name -> rill.runtime.v1.NumericOutliers - 112, // 157: rill.runtime.v1.NumericHistogramBins.bins:type_name -> rill.runtime.v1.NumericHistogramBins.Bin - 113, // 158: rill.runtime.v1.NumericOutliers.outliers:type_name -> rill.runtime.v1.NumericOutliers.Outlier - 123, // 159: rill.runtime.v1.ColumnTimeGrainResponse.time_grain:type_name -> rill.runtime.v1.TimeGrain - 3, // 160: rill.runtime.v1.ColumnNumericHistogramRequest.histogram_method:type_name -> rill.runtime.v1.HistogramMethod - 77, // 161: rill.runtime.v1.ColumnNumericHistogramResponse.numeric_summary:type_name -> rill.runtime.v1.NumericSummary - 77, // 162: rill.runtime.v1.ColumnRugHistogramResponse.numeric_summary:type_name -> rill.runtime.v1.NumericSummary - 89, // 163: rill.runtime.v1.ColumnTimeRangeResponse.time_range_summary:type_name -> rill.runtime.v1.TimeRangeSummary - 121, // 164: rill.runtime.v1.TimeRangeSummary.min:type_name -> google.protobuf.Timestamp - 121, // 165: rill.runtime.v1.TimeRangeSummary.max:type_name -> google.protobuf.Timestamp - 121, // 166: rill.runtime.v1.TimeRangeSummary.watermark:type_name -> google.protobuf.Timestamp - 71, // 167: rill.runtime.v1.ColumnCardinalityResponse.categorical_summary:type_name -> rill.runtime.v1.CategoricalSummary - 114, // 168: rill.runtime.v1.ColumnTimeSeriesRequest.measures:type_name -> rill.runtime.v1.ColumnTimeSeriesRequest.BasicMeasure - 94, // 169: rill.runtime.v1.ColumnTimeSeriesRequest.time_range:type_name -> rill.runtime.v1.TimeSeriesTimeRange - 95, // 170: rill.runtime.v1.ColumnTimeSeriesResponse.rollup:type_name -> rill.runtime.v1.TimeSeriesResponse - 121, // 171: rill.runtime.v1.TimeSeriesTimeRange.start:type_name -> google.protobuf.Timestamp - 121, // 172: rill.runtime.v1.TimeSeriesTimeRange.end:type_name -> google.protobuf.Timestamp - 123, // 173: rill.runtime.v1.TimeSeriesTimeRange.interval:type_name -> rill.runtime.v1.TimeGrain - 96, // 174: rill.runtime.v1.TimeSeriesResponse.results:type_name -> rill.runtime.v1.TimeSeriesValue - 96, // 175: rill.runtime.v1.TimeSeriesResponse.spark:type_name -> rill.runtime.v1.TimeSeriesValue - 121, // 176: rill.runtime.v1.TimeSeriesValue.ts:type_name -> google.protobuf.Timestamp - 118, // 177: rill.runtime.v1.TimeSeriesValue.records:type_name -> google.protobuf.Struct - 101, // 178: rill.runtime.v1.TableColumnsResponse.profile_columns:type_name -> rill.runtime.v1.ProfileColumn - 115, // 179: rill.runtime.v1.TableColumnsResponse.unsupported_columns:type_name -> rill.runtime.v1.TableColumnsResponse.UnsupportedColumnsEntry - 118, // 180: rill.runtime.v1.TableRowsResponse.data:type_name -> google.protobuf.Struct - 116, // 181: rill.runtime.v1.MetricsViewFilter.Cond.in:type_name -> google.protobuf.Value - 116, // 182: rill.runtime.v1.MetricsViewSearchResponse.SearchResult.value:type_name -> google.protobuf.Value - 121, // 183: rill.runtime.v1.MetricsViewAnnotationsResponse.Annotation.time:type_name -> google.protobuf.Timestamp - 121, // 184: rill.runtime.v1.MetricsViewAnnotationsResponse.Annotation.time_end:type_name -> google.protobuf.Timestamp - 118, // 185: rill.runtime.v1.MetricsViewAnnotationsResponse.Annotation.additional_fields:type_name -> google.protobuf.Struct - 124, // 186: rill.runtime.v1.ResolveCanvasResponse.ResolvedComponentsEntry.value:type_name -> rill.runtime.v1.Resource - 124, // 187: rill.runtime.v1.ResolveCanvasResponse.ReferencedMetricsViewsEntry.value:type_name -> rill.runtime.v1.Resource - 122, // 188: rill.runtime.v1.ResolveTemplatedStringRequest.AdditionalWhereByMetricsViewEntry.value:type_name -> rill.runtime.v1.Expression - 116, // 189: rill.runtime.v1.TopK.Entry.value:type_name -> google.protobuf.Value - 4, // 190: rill.runtime.v1.QueryService.Query:input_type -> rill.runtime.v1.QueryRequest - 6, // 191: rill.runtime.v1.QueryService.Export:input_type -> rill.runtime.v1.ExportRequest - 8, // 192: rill.runtime.v1.QueryService.ExportReport:input_type -> rill.runtime.v1.ExportReportRequest - 13, // 193: rill.runtime.v1.QueryService.ProjectStorage:input_type -> rill.runtime.v1.ProjectStorageRequest - 16, // 194: rill.runtime.v1.QueryService.MetricsViewAggregation:input_type -> rill.runtime.v1.MetricsViewAggregationRequest - 29, // 195: rill.runtime.v1.QueryService.MetricsViewToplist:input_type -> rill.runtime.v1.MetricsViewToplistRequest - 31, // 196: rill.runtime.v1.QueryService.MetricsViewComparison:input_type -> rill.runtime.v1.MetricsViewComparisonRequest - 38, // 197: rill.runtime.v1.QueryService.MetricsViewTimeSeries:input_type -> rill.runtime.v1.MetricsViewTimeSeriesRequest - 40, // 198: rill.runtime.v1.QueryService.MetricsViewTotals:input_type -> rill.runtime.v1.MetricsViewTotalsRequest - 42, // 199: rill.runtime.v1.QueryService.MetricsViewRows:input_type -> rill.runtime.v1.MetricsViewRowsRequest - 48, // 200: rill.runtime.v1.QueryService.MetricsViewTimeRange:input_type -> rill.runtime.v1.MetricsViewTimeRangeRequest - 50, // 201: rill.runtime.v1.QueryService.MetricsViewSchema:input_type -> rill.runtime.v1.MetricsViewSchemaRequest - 52, // 202: rill.runtime.v1.QueryService.MetricsViewSearch:input_type -> rill.runtime.v1.MetricsViewSearchRequest - 54, // 203: rill.runtime.v1.QueryService.MetricsViewTimeRanges:input_type -> rill.runtime.v1.MetricsViewTimeRangesRequest - 57, // 204: rill.runtime.v1.QueryService.MetricsViewAnnotations:input_type -> rill.runtime.v1.MetricsViewAnnotationsRequest - 59, // 205: rill.runtime.v1.QueryService.ConvertExpressionToMetricsSQL:input_type -> rill.runtime.v1.ConvertExpressionToMetricsSQLRequest - 61, // 206: rill.runtime.v1.QueryService.ResolveCanvas:input_type -> rill.runtime.v1.ResolveCanvasRequest - 63, // 207: rill.runtime.v1.QueryService.ResolveComponent:input_type -> rill.runtime.v1.ResolveComponentRequest - 65, // 208: rill.runtime.v1.QueryService.ResolveTemplatedString:input_type -> rill.runtime.v1.ResolveTemplatedStringRequest - 67, // 209: rill.runtime.v1.QueryService.ColumnRollupInterval:input_type -> rill.runtime.v1.ColumnRollupIntervalRequest - 69, // 210: rill.runtime.v1.QueryService.ColumnTopK:input_type -> rill.runtime.v1.ColumnTopKRequest - 73, // 211: rill.runtime.v1.QueryService.ColumnNullCount:input_type -> rill.runtime.v1.ColumnNullCountRequest - 75, // 212: rill.runtime.v1.QueryService.ColumnDescriptiveStatistics:input_type -> rill.runtime.v1.ColumnDescriptiveStatisticsRequest - 81, // 213: rill.runtime.v1.QueryService.ColumnTimeGrain:input_type -> rill.runtime.v1.ColumnTimeGrainRequest - 83, // 214: rill.runtime.v1.QueryService.ColumnNumericHistogram:input_type -> rill.runtime.v1.ColumnNumericHistogramRequest - 85, // 215: rill.runtime.v1.QueryService.ColumnRugHistogram:input_type -> rill.runtime.v1.ColumnRugHistogramRequest - 87, // 216: rill.runtime.v1.QueryService.ColumnTimeRange:input_type -> rill.runtime.v1.ColumnTimeRangeRequest - 90, // 217: rill.runtime.v1.QueryService.ColumnCardinality:input_type -> rill.runtime.v1.ColumnCardinalityRequest - 92, // 218: rill.runtime.v1.QueryService.ColumnTimeSeries:input_type -> rill.runtime.v1.ColumnTimeSeriesRequest - 97, // 219: rill.runtime.v1.QueryService.TableCardinality:input_type -> rill.runtime.v1.TableCardinalityRequest - 99, // 220: rill.runtime.v1.QueryService.TableColumns:input_type -> rill.runtime.v1.TableColumnsRequest - 102, // 221: rill.runtime.v1.QueryService.TableRows:input_type -> rill.runtime.v1.TableRowsRequest - 5, // 222: rill.runtime.v1.QueryService.Query:output_type -> rill.runtime.v1.QueryResponse - 7, // 223: rill.runtime.v1.QueryService.Export:output_type -> rill.runtime.v1.ExportResponse - 9, // 224: rill.runtime.v1.QueryService.ExportReport:output_type -> rill.runtime.v1.ExportReportResponse - 14, // 225: rill.runtime.v1.QueryService.ProjectStorage:output_type -> rill.runtime.v1.ProjectStorageResponse - 17, // 226: rill.runtime.v1.QueryService.MetricsViewAggregation:output_type -> rill.runtime.v1.MetricsViewAggregationResponse - 30, // 227: rill.runtime.v1.QueryService.MetricsViewToplist:output_type -> rill.runtime.v1.MetricsViewToplistResponse - 32, // 228: rill.runtime.v1.QueryService.MetricsViewComparison:output_type -> rill.runtime.v1.MetricsViewComparisonResponse - 39, // 229: rill.runtime.v1.QueryService.MetricsViewTimeSeries:output_type -> rill.runtime.v1.MetricsViewTimeSeriesResponse - 41, // 230: rill.runtime.v1.QueryService.MetricsViewTotals:output_type -> rill.runtime.v1.MetricsViewTotalsResponse - 43, // 231: rill.runtime.v1.QueryService.MetricsViewRows:output_type -> rill.runtime.v1.MetricsViewRowsResponse - 49, // 232: rill.runtime.v1.QueryService.MetricsViewTimeRange:output_type -> rill.runtime.v1.MetricsViewTimeRangeResponse - 51, // 233: rill.runtime.v1.QueryService.MetricsViewSchema:output_type -> rill.runtime.v1.MetricsViewSchemaResponse - 53, // 234: rill.runtime.v1.QueryService.MetricsViewSearch:output_type -> rill.runtime.v1.MetricsViewSearchResponse - 55, // 235: rill.runtime.v1.QueryService.MetricsViewTimeRanges:output_type -> rill.runtime.v1.MetricsViewTimeRangesResponse - 58, // 236: rill.runtime.v1.QueryService.MetricsViewAnnotations:output_type -> rill.runtime.v1.MetricsViewAnnotationsResponse - 60, // 237: rill.runtime.v1.QueryService.ConvertExpressionToMetricsSQL:output_type -> rill.runtime.v1.ConvertExpressionToMetricsSQLResponse - 62, // 238: rill.runtime.v1.QueryService.ResolveCanvas:output_type -> rill.runtime.v1.ResolveCanvasResponse - 64, // 239: rill.runtime.v1.QueryService.ResolveComponent:output_type -> rill.runtime.v1.ResolveComponentResponse - 66, // 240: rill.runtime.v1.QueryService.ResolveTemplatedString:output_type -> rill.runtime.v1.ResolveTemplatedStringResponse - 68, // 241: rill.runtime.v1.QueryService.ColumnRollupInterval:output_type -> rill.runtime.v1.ColumnRollupIntervalResponse - 70, // 242: rill.runtime.v1.QueryService.ColumnTopK:output_type -> rill.runtime.v1.ColumnTopKResponse - 74, // 243: rill.runtime.v1.QueryService.ColumnNullCount:output_type -> rill.runtime.v1.ColumnNullCountResponse - 76, // 244: rill.runtime.v1.QueryService.ColumnDescriptiveStatistics:output_type -> rill.runtime.v1.ColumnDescriptiveStatisticsResponse - 82, // 245: rill.runtime.v1.QueryService.ColumnTimeGrain:output_type -> rill.runtime.v1.ColumnTimeGrainResponse - 84, // 246: rill.runtime.v1.QueryService.ColumnNumericHistogram:output_type -> rill.runtime.v1.ColumnNumericHistogramResponse - 86, // 247: rill.runtime.v1.QueryService.ColumnRugHistogram:output_type -> rill.runtime.v1.ColumnRugHistogramResponse - 88, // 248: rill.runtime.v1.QueryService.ColumnTimeRange:output_type -> rill.runtime.v1.ColumnTimeRangeResponse - 91, // 249: rill.runtime.v1.QueryService.ColumnCardinality:output_type -> rill.runtime.v1.ColumnCardinalityResponse - 93, // 250: rill.runtime.v1.QueryService.ColumnTimeSeries:output_type -> rill.runtime.v1.ColumnTimeSeriesResponse - 98, // 251: rill.runtime.v1.QueryService.TableCardinality:output_type -> rill.runtime.v1.TableCardinalityResponse - 100, // 252: rill.runtime.v1.QueryService.TableColumns:output_type -> rill.runtime.v1.TableColumnsResponse - 103, // 253: rill.runtime.v1.QueryService.TableRows:output_type -> rill.runtime.v1.TableRowsResponse - 222, // [222:254] is the sub-list for method output_type - 190, // [190:222] is the sub-list for method input_type - 190, // [190:190] is the sub-list for extension type_name - 190, // [190:190] is the sub-list for extension extendee - 0, // [0:190] is the sub-list for field type_name + 118, // 145: rill.runtime.v1.ResolveComponentResponse.resolved_args:type_name -> google.protobuf.Struct + 110, // 146: rill.runtime.v1.ResolveTemplatedStringRequest.additional_where_by_metrics_view:type_name -> rill.runtime.v1.ResolveTemplatedStringRequest.AdditionalWhereByMetricsViewEntry + 33, // 147: rill.runtime.v1.ResolveTemplatedStringRequest.additional_time_range:type_name -> rill.runtime.v1.TimeRange + 121, // 148: rill.runtime.v1.ColumnRollupIntervalResponse.start:type_name -> google.protobuf.Timestamp + 121, // 149: rill.runtime.v1.ColumnRollupIntervalResponse.end:type_name -> google.protobuf.Timestamp + 123, // 150: rill.runtime.v1.ColumnRollupIntervalResponse.interval:type_name -> rill.runtime.v1.TimeGrain + 71, // 151: rill.runtime.v1.ColumnTopKResponse.categorical_summary:type_name -> rill.runtime.v1.CategoricalSummary + 72, // 152: rill.runtime.v1.CategoricalSummary.top_k:type_name -> rill.runtime.v1.TopK + 111, // 153: rill.runtime.v1.TopK.entries:type_name -> rill.runtime.v1.TopK.Entry + 77, // 154: rill.runtime.v1.ColumnDescriptiveStatisticsResponse.numeric_summary:type_name -> rill.runtime.v1.NumericSummary + 78, // 155: rill.runtime.v1.NumericSummary.numeric_histogram_bins:type_name -> rill.runtime.v1.NumericHistogramBins + 79, // 156: rill.runtime.v1.NumericSummary.numeric_statistics:type_name -> rill.runtime.v1.NumericStatistics + 80, // 157: rill.runtime.v1.NumericSummary.numeric_outliers:type_name -> rill.runtime.v1.NumericOutliers + 112, // 158: rill.runtime.v1.NumericHistogramBins.bins:type_name -> rill.runtime.v1.NumericHistogramBins.Bin + 113, // 159: rill.runtime.v1.NumericOutliers.outliers:type_name -> rill.runtime.v1.NumericOutliers.Outlier + 123, // 160: rill.runtime.v1.ColumnTimeGrainResponse.time_grain:type_name -> rill.runtime.v1.TimeGrain + 3, // 161: rill.runtime.v1.ColumnNumericHistogramRequest.histogram_method:type_name -> rill.runtime.v1.HistogramMethod + 77, // 162: rill.runtime.v1.ColumnNumericHistogramResponse.numeric_summary:type_name -> rill.runtime.v1.NumericSummary + 77, // 163: rill.runtime.v1.ColumnRugHistogramResponse.numeric_summary:type_name -> rill.runtime.v1.NumericSummary + 89, // 164: rill.runtime.v1.ColumnTimeRangeResponse.time_range_summary:type_name -> rill.runtime.v1.TimeRangeSummary + 121, // 165: rill.runtime.v1.TimeRangeSummary.min:type_name -> google.protobuf.Timestamp + 121, // 166: rill.runtime.v1.TimeRangeSummary.max:type_name -> google.protobuf.Timestamp + 121, // 167: rill.runtime.v1.TimeRangeSummary.watermark:type_name -> google.protobuf.Timestamp + 71, // 168: rill.runtime.v1.ColumnCardinalityResponse.categorical_summary:type_name -> rill.runtime.v1.CategoricalSummary + 114, // 169: rill.runtime.v1.ColumnTimeSeriesRequest.measures:type_name -> rill.runtime.v1.ColumnTimeSeriesRequest.BasicMeasure + 94, // 170: rill.runtime.v1.ColumnTimeSeriesRequest.time_range:type_name -> rill.runtime.v1.TimeSeriesTimeRange + 95, // 171: rill.runtime.v1.ColumnTimeSeriesResponse.rollup:type_name -> rill.runtime.v1.TimeSeriesResponse + 121, // 172: rill.runtime.v1.TimeSeriesTimeRange.start:type_name -> google.protobuf.Timestamp + 121, // 173: rill.runtime.v1.TimeSeriesTimeRange.end:type_name -> google.protobuf.Timestamp + 123, // 174: rill.runtime.v1.TimeSeriesTimeRange.interval:type_name -> rill.runtime.v1.TimeGrain + 96, // 175: rill.runtime.v1.TimeSeriesResponse.results:type_name -> rill.runtime.v1.TimeSeriesValue + 96, // 176: rill.runtime.v1.TimeSeriesResponse.spark:type_name -> rill.runtime.v1.TimeSeriesValue + 121, // 177: rill.runtime.v1.TimeSeriesValue.ts:type_name -> google.protobuf.Timestamp + 118, // 178: rill.runtime.v1.TimeSeriesValue.records:type_name -> google.protobuf.Struct + 101, // 179: rill.runtime.v1.TableColumnsResponse.profile_columns:type_name -> rill.runtime.v1.ProfileColumn + 115, // 180: rill.runtime.v1.TableColumnsResponse.unsupported_columns:type_name -> rill.runtime.v1.TableColumnsResponse.UnsupportedColumnsEntry + 118, // 181: rill.runtime.v1.TableRowsResponse.data:type_name -> google.protobuf.Struct + 116, // 182: rill.runtime.v1.MetricsViewFilter.Cond.in:type_name -> google.protobuf.Value + 116, // 183: rill.runtime.v1.MetricsViewSearchResponse.SearchResult.value:type_name -> google.protobuf.Value + 121, // 184: rill.runtime.v1.MetricsViewAnnotationsResponse.Annotation.time:type_name -> google.protobuf.Timestamp + 121, // 185: rill.runtime.v1.MetricsViewAnnotationsResponse.Annotation.time_end:type_name -> google.protobuf.Timestamp + 118, // 186: rill.runtime.v1.MetricsViewAnnotationsResponse.Annotation.additional_fields:type_name -> google.protobuf.Struct + 124, // 187: rill.runtime.v1.ResolveCanvasResponse.ResolvedComponentsEntry.value:type_name -> rill.runtime.v1.Resource + 124, // 188: rill.runtime.v1.ResolveCanvasResponse.ReferencedMetricsViewsEntry.value:type_name -> rill.runtime.v1.Resource + 122, // 189: rill.runtime.v1.ResolveTemplatedStringRequest.AdditionalWhereByMetricsViewEntry.value:type_name -> rill.runtime.v1.Expression + 116, // 190: rill.runtime.v1.TopK.Entry.value:type_name -> google.protobuf.Value + 4, // 191: rill.runtime.v1.QueryService.Query:input_type -> rill.runtime.v1.QueryRequest + 6, // 192: rill.runtime.v1.QueryService.Export:input_type -> rill.runtime.v1.ExportRequest + 8, // 193: rill.runtime.v1.QueryService.ExportReport:input_type -> rill.runtime.v1.ExportReportRequest + 13, // 194: rill.runtime.v1.QueryService.ProjectStorage:input_type -> rill.runtime.v1.ProjectStorageRequest + 16, // 195: rill.runtime.v1.QueryService.MetricsViewAggregation:input_type -> rill.runtime.v1.MetricsViewAggregationRequest + 29, // 196: rill.runtime.v1.QueryService.MetricsViewToplist:input_type -> rill.runtime.v1.MetricsViewToplistRequest + 31, // 197: rill.runtime.v1.QueryService.MetricsViewComparison:input_type -> rill.runtime.v1.MetricsViewComparisonRequest + 38, // 198: rill.runtime.v1.QueryService.MetricsViewTimeSeries:input_type -> rill.runtime.v1.MetricsViewTimeSeriesRequest + 40, // 199: rill.runtime.v1.QueryService.MetricsViewTotals:input_type -> rill.runtime.v1.MetricsViewTotalsRequest + 42, // 200: rill.runtime.v1.QueryService.MetricsViewRows:input_type -> rill.runtime.v1.MetricsViewRowsRequest + 48, // 201: rill.runtime.v1.QueryService.MetricsViewTimeRange:input_type -> rill.runtime.v1.MetricsViewTimeRangeRequest + 50, // 202: rill.runtime.v1.QueryService.MetricsViewSchema:input_type -> rill.runtime.v1.MetricsViewSchemaRequest + 52, // 203: rill.runtime.v1.QueryService.MetricsViewSearch:input_type -> rill.runtime.v1.MetricsViewSearchRequest + 54, // 204: rill.runtime.v1.QueryService.MetricsViewTimeRanges:input_type -> rill.runtime.v1.MetricsViewTimeRangesRequest + 57, // 205: rill.runtime.v1.QueryService.MetricsViewAnnotations:input_type -> rill.runtime.v1.MetricsViewAnnotationsRequest + 59, // 206: rill.runtime.v1.QueryService.ConvertExpressionToMetricsSQL:input_type -> rill.runtime.v1.ConvertExpressionToMetricsSQLRequest + 61, // 207: rill.runtime.v1.QueryService.ResolveCanvas:input_type -> rill.runtime.v1.ResolveCanvasRequest + 63, // 208: rill.runtime.v1.QueryService.ResolveComponent:input_type -> rill.runtime.v1.ResolveComponentRequest + 65, // 209: rill.runtime.v1.QueryService.ResolveTemplatedString:input_type -> rill.runtime.v1.ResolveTemplatedStringRequest + 67, // 210: rill.runtime.v1.QueryService.ColumnRollupInterval:input_type -> rill.runtime.v1.ColumnRollupIntervalRequest + 69, // 211: rill.runtime.v1.QueryService.ColumnTopK:input_type -> rill.runtime.v1.ColumnTopKRequest + 73, // 212: rill.runtime.v1.QueryService.ColumnNullCount:input_type -> rill.runtime.v1.ColumnNullCountRequest + 75, // 213: rill.runtime.v1.QueryService.ColumnDescriptiveStatistics:input_type -> rill.runtime.v1.ColumnDescriptiveStatisticsRequest + 81, // 214: rill.runtime.v1.QueryService.ColumnTimeGrain:input_type -> rill.runtime.v1.ColumnTimeGrainRequest + 83, // 215: rill.runtime.v1.QueryService.ColumnNumericHistogram:input_type -> rill.runtime.v1.ColumnNumericHistogramRequest + 85, // 216: rill.runtime.v1.QueryService.ColumnRugHistogram:input_type -> rill.runtime.v1.ColumnRugHistogramRequest + 87, // 217: rill.runtime.v1.QueryService.ColumnTimeRange:input_type -> rill.runtime.v1.ColumnTimeRangeRequest + 90, // 218: rill.runtime.v1.QueryService.ColumnCardinality:input_type -> rill.runtime.v1.ColumnCardinalityRequest + 92, // 219: rill.runtime.v1.QueryService.ColumnTimeSeries:input_type -> rill.runtime.v1.ColumnTimeSeriesRequest + 97, // 220: rill.runtime.v1.QueryService.TableCardinality:input_type -> rill.runtime.v1.TableCardinalityRequest + 99, // 221: rill.runtime.v1.QueryService.TableColumns:input_type -> rill.runtime.v1.TableColumnsRequest + 102, // 222: rill.runtime.v1.QueryService.TableRows:input_type -> rill.runtime.v1.TableRowsRequest + 5, // 223: rill.runtime.v1.QueryService.Query:output_type -> rill.runtime.v1.QueryResponse + 7, // 224: rill.runtime.v1.QueryService.Export:output_type -> rill.runtime.v1.ExportResponse + 9, // 225: rill.runtime.v1.QueryService.ExportReport:output_type -> rill.runtime.v1.ExportReportResponse + 14, // 226: rill.runtime.v1.QueryService.ProjectStorage:output_type -> rill.runtime.v1.ProjectStorageResponse + 17, // 227: rill.runtime.v1.QueryService.MetricsViewAggregation:output_type -> rill.runtime.v1.MetricsViewAggregationResponse + 30, // 228: rill.runtime.v1.QueryService.MetricsViewToplist:output_type -> rill.runtime.v1.MetricsViewToplistResponse + 32, // 229: rill.runtime.v1.QueryService.MetricsViewComparison:output_type -> rill.runtime.v1.MetricsViewComparisonResponse + 39, // 230: rill.runtime.v1.QueryService.MetricsViewTimeSeries:output_type -> rill.runtime.v1.MetricsViewTimeSeriesResponse + 41, // 231: rill.runtime.v1.QueryService.MetricsViewTotals:output_type -> rill.runtime.v1.MetricsViewTotalsResponse + 43, // 232: rill.runtime.v1.QueryService.MetricsViewRows:output_type -> rill.runtime.v1.MetricsViewRowsResponse + 49, // 233: rill.runtime.v1.QueryService.MetricsViewTimeRange:output_type -> rill.runtime.v1.MetricsViewTimeRangeResponse + 51, // 234: rill.runtime.v1.QueryService.MetricsViewSchema:output_type -> rill.runtime.v1.MetricsViewSchemaResponse + 53, // 235: rill.runtime.v1.QueryService.MetricsViewSearch:output_type -> rill.runtime.v1.MetricsViewSearchResponse + 55, // 236: rill.runtime.v1.QueryService.MetricsViewTimeRanges:output_type -> rill.runtime.v1.MetricsViewTimeRangesResponse + 58, // 237: rill.runtime.v1.QueryService.MetricsViewAnnotations:output_type -> rill.runtime.v1.MetricsViewAnnotationsResponse + 60, // 238: rill.runtime.v1.QueryService.ConvertExpressionToMetricsSQL:output_type -> rill.runtime.v1.ConvertExpressionToMetricsSQLResponse + 62, // 239: rill.runtime.v1.QueryService.ResolveCanvas:output_type -> rill.runtime.v1.ResolveCanvasResponse + 64, // 240: rill.runtime.v1.QueryService.ResolveComponent:output_type -> rill.runtime.v1.ResolveComponentResponse + 66, // 241: rill.runtime.v1.QueryService.ResolveTemplatedString:output_type -> rill.runtime.v1.ResolveTemplatedStringResponse + 68, // 242: rill.runtime.v1.QueryService.ColumnRollupInterval:output_type -> rill.runtime.v1.ColumnRollupIntervalResponse + 70, // 243: rill.runtime.v1.QueryService.ColumnTopK:output_type -> rill.runtime.v1.ColumnTopKResponse + 74, // 244: rill.runtime.v1.QueryService.ColumnNullCount:output_type -> rill.runtime.v1.ColumnNullCountResponse + 76, // 245: rill.runtime.v1.QueryService.ColumnDescriptiveStatistics:output_type -> rill.runtime.v1.ColumnDescriptiveStatisticsResponse + 82, // 246: rill.runtime.v1.QueryService.ColumnTimeGrain:output_type -> rill.runtime.v1.ColumnTimeGrainResponse + 84, // 247: rill.runtime.v1.QueryService.ColumnNumericHistogram:output_type -> rill.runtime.v1.ColumnNumericHistogramResponse + 86, // 248: rill.runtime.v1.QueryService.ColumnRugHistogram:output_type -> rill.runtime.v1.ColumnRugHistogramResponse + 88, // 249: rill.runtime.v1.QueryService.ColumnTimeRange:output_type -> rill.runtime.v1.ColumnTimeRangeResponse + 91, // 250: rill.runtime.v1.QueryService.ColumnCardinality:output_type -> rill.runtime.v1.ColumnCardinalityResponse + 93, // 251: rill.runtime.v1.QueryService.ColumnTimeSeries:output_type -> rill.runtime.v1.ColumnTimeSeriesResponse + 98, // 252: rill.runtime.v1.QueryService.TableCardinality:output_type -> rill.runtime.v1.TableCardinalityResponse + 100, // 253: rill.runtime.v1.QueryService.TableColumns:output_type -> rill.runtime.v1.TableColumnsResponse + 103, // 254: rill.runtime.v1.QueryService.TableRows:output_type -> rill.runtime.v1.TableRowsResponse + 223, // [223:255] is the sub-list for method output_type + 191, // [191:223] is the sub-list for method input_type + 191, // [191:191] is the sub-list for extension type_name + 191, // [191:191] is the sub-list for extension extendee + 0, // [0:191] is the sub-list for field type_name } func init() { file_rill_runtime_v1_queries_proto_init() } diff --git a/proto/gen/rill/runtime/v1/queries.pb.validate.go b/proto/gen/rill/runtime/v1/queries.pb.validate.go index 6d5ca64cd039..014945f831f1 100644 --- a/proto/gen/rill/runtime/v1/queries.pb.validate.go +++ b/proto/gen/rill/runtime/v1/queries.pb.validate.go @@ -11297,6 +11297,35 @@ func (m *ResolveComponentResponse) validate(all bool) error { } } + if all { + switch v := interface{}(m.GetResolvedArgs()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, ResolveComponentResponseValidationError{ + field: "ResolvedArgs", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, ResolveComponentResponseValidationError{ + field: "ResolvedArgs", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetResolvedArgs()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return ResolveComponentResponseValidationError{ + field: "ResolvedArgs", + reason: "embedded message failed validation", + cause: err, + } + } + } + if len(errors) > 0 { return ResolveComponentResponseMultiError(errors) } diff --git a/proto/gen/rill/runtime/v1/resources.pb.go b/proto/gen/rill/runtime/v1/resources.pb.go index c7dccdb7e403..013e8bde7025 100644 --- a/proto/gen/rill/runtime/v1/resources.pb.go +++ b/proto/gen/rill/runtime/v1/resources.pb.go @@ -5404,13 +5404,16 @@ type ComponentSpec struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - DisplayName string `protobuf:"bytes,1,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` - Description string `protobuf:"bytes,7,opt,name=description,proto3" json:"description,omitempty"` - Renderer string `protobuf:"bytes,4,opt,name=renderer,proto3" json:"renderer,omitempty"` - RendererProperties *structpb.Struct `protobuf:"bytes,5,opt,name=renderer_properties,json=rendererProperties,proto3" json:"renderer_properties,omitempty"` - Input []*ComponentVariable `protobuf:"bytes,8,rep,name=input,proto3" json:"input,omitempty"` - Output *ComponentVariable `protobuf:"bytes,9,opt,name=output,proto3" json:"output,omitempty"` - DefinedInCanvas bool `protobuf:"varint,6,opt,name=defined_in_canvas,json=definedInCanvas,proto3" json:"defined_in_canvas,omitempty"` + DisplayName string `protobuf:"bytes,1,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` + Description string `protobuf:"bytes,7,opt,name=description,proto3" json:"description,omitempty"` + Renderer string `protobuf:"bytes,4,opt,name=renderer,proto3" json:"renderer,omitempty"` + RendererProperties *structpb.Struct `protobuf:"bytes,5,opt,name=renderer_properties,json=rendererProperties,proto3" json:"renderer_properties,omitempty"` + // Declared parameters that canvases can bind values to when referencing this component. + // Bound values are available in the renderer properties' templating as {{ .params. }}. + Params []*ComponentParam `protobuf:"bytes,10,rep,name=params,proto3" json:"params,omitempty"` + Input []*ComponentVariable `protobuf:"bytes,8,rep,name=input,proto3" json:"input,omitempty"` + Output *ComponentVariable `protobuf:"bytes,9,opt,name=output,proto3" json:"output,omitempty"` + DefinedInCanvas bool `protobuf:"varint,6,opt,name=defined_in_canvas,json=definedInCanvas,proto3" json:"defined_in_canvas,omitempty"` } func (x *ComponentSpec) Reset() { @@ -5473,6 +5476,13 @@ func (x *ComponentSpec) GetRendererProperties() *structpb.Struct { return nil } +func (x *ComponentSpec) GetParams() []*ComponentParam { + if x != nil { + return x.Params + } + return nil +} + func (x *ComponentSpec) GetInput() []*ComponentVariable { if x != nil { return x.Input @@ -5615,6 +5625,111 @@ func (x *ComponentVariable) GetDefaultValue() *structpb.Value { return nil } +// ComponentParam declares a typed, validated parameter of a component. +type ComponentParam struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Param name. Must be a valid identifier; referenced in templates as {{ .params. }}. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Param type. One of: "string", "number", "boolean", "metrics_view", "measure", "dimension", "time_dimension". + Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + // Human-facing description of the param. + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + // If true, a canvas item referencing this component must bind a value for the param. + Required bool `protobuf:"varint,4,opt,name=required,proto3" json:"required,omitempty"` + // Default value used when the param is not bound. Mutually exclusive with required=true. + Default *structpb.Value `protobuf:"bytes,5,opt,name=default,proto3" json:"default,omitempty"` + // For "measure", "dimension" and "time_dimension" params: + // the name of a sibling param of type "metrics_view" whose bound metrics view the field must belong to. + // May be omitted when exactly one "metrics_view" param is declared, in which case the parser fills it in. + MetricsViewParam string `protobuf:"bytes,6,opt,name=metrics_view_param,json=metricsViewParam,proto3" json:"metrics_view_param,omitempty"` + // For scalar params: allowed values. Renders as a select input in visual editors. + Options []*structpb.Value `protobuf:"bytes,7,rep,name=options,proto3" json:"options,omitempty"` +} + +func (x *ComponentParam) Reset() { + *x = ComponentParam{} + if protoimpl.UnsafeEnabled { + mi := &file_rill_runtime_v1_resources_proto_msgTypes[54] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ComponentParam) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ComponentParam) ProtoMessage() {} + +func (x *ComponentParam) ProtoReflect() protoreflect.Message { + mi := &file_rill_runtime_v1_resources_proto_msgTypes[54] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ComponentParam.ProtoReflect.Descriptor instead. +func (*ComponentParam) Descriptor() ([]byte, []int) { + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{54} +} + +func (x *ComponentParam) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ComponentParam) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *ComponentParam) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *ComponentParam) GetRequired() bool { + if x != nil { + return x.Required + } + return false +} + +func (x *ComponentParam) GetDefault() *structpb.Value { + if x != nil { + return x.Default + } + return nil +} + +func (x *ComponentParam) GetMetricsViewParam() string { + if x != nil { + return x.MetricsViewParam + } + return "" +} + +func (x *ComponentParam) GetOptions() []*structpb.Value { + if x != nil { + return x.Options + } + return nil +} + type Canvas struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -5627,7 +5742,7 @@ type Canvas struct { func (x *Canvas) Reset() { *x = Canvas{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[54] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5640,7 +5755,7 @@ func (x *Canvas) String() string { func (*Canvas) ProtoMessage() {} func (x *Canvas) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[54] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5653,7 +5768,7 @@ func (x *Canvas) ProtoReflect() protoreflect.Message { // Deprecated: Use Canvas.ProtoReflect.Descriptor instead. func (*Canvas) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{54} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{55} } func (x *Canvas) GetSpec() *CanvasSpec { @@ -5721,7 +5836,7 @@ type CanvasSpec struct { func (x *CanvasSpec) Reset() { *x = CanvasSpec{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[55] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5734,7 +5849,7 @@ func (x *CanvasSpec) String() string { func (*CanvasSpec) ProtoMessage() {} func (x *CanvasSpec) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[55] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5747,7 +5862,7 @@ func (x *CanvasSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use CanvasSpec.ProtoReflect.Descriptor instead. func (*CanvasSpec) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{55} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{56} } func (x *CanvasSpec) GetDisplayName() string { @@ -5891,7 +6006,7 @@ type CanvasState struct { func (x *CanvasState) Reset() { *x = CanvasState{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[56] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5904,7 +6019,7 @@ func (x *CanvasState) String() string { func (*CanvasState) ProtoMessage() {} func (x *CanvasState) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[56] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5917,7 +6032,7 @@ func (x *CanvasState) ProtoReflect() protoreflect.Message { // Deprecated: Use CanvasState.ProtoReflect.Descriptor instead. func (*CanvasState) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{56} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{57} } func (x *CanvasState) GetValidSpec() *CanvasSpec { @@ -5953,7 +6068,7 @@ type CanvasRow struct { func (x *CanvasRow) Reset() { *x = CanvasRow{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[57] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5966,7 +6081,7 @@ func (x *CanvasRow) String() string { func (*CanvasRow) ProtoMessage() {} func (x *CanvasRow) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[57] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5979,7 +6094,7 @@ func (x *CanvasRow) ProtoReflect() protoreflect.Message { // Deprecated: Use CanvasRow.ProtoReflect.Descriptor instead. func (*CanvasRow) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{57} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{58} } func (x *CanvasRow) GetHeight() uint32 { @@ -6025,7 +6140,7 @@ type CanvasTabGroup struct { func (x *CanvasTabGroup) Reset() { *x = CanvasTabGroup{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[58] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6038,7 +6153,7 @@ func (x *CanvasTabGroup) String() string { func (*CanvasTabGroup) ProtoMessage() {} func (x *CanvasTabGroup) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[58] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6051,7 +6166,7 @@ func (x *CanvasTabGroup) ProtoReflect() protoreflect.Message { // Deprecated: Use CanvasTabGroup.ProtoReflect.Descriptor instead. func (*CanvasTabGroup) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{58} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{59} } func (x *CanvasTabGroup) GetName() string { @@ -6085,7 +6200,7 @@ type CanvasTab struct { func (x *CanvasTab) Reset() { *x = CanvasTab{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[59] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6098,7 +6213,7 @@ func (x *CanvasTab) String() string { func (*CanvasTab) ProtoMessage() {} func (x *CanvasTab) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[59] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6111,7 +6226,7 @@ func (x *CanvasTab) ProtoReflect() protoreflect.Message { // Deprecated: Use CanvasTab.ProtoReflect.Descriptor instead. func (*CanvasTab) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{59} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{60} } func (x *CanvasTab) GetName() string { @@ -6144,6 +6259,9 @@ type CanvasItem struct { Component string `protobuf:"bytes,1,opt,name=component,proto3" json:"component,omitempty"` // Indicates if the component was defined inline as part of the canvas YAML. DefinedInCanvas bool `protobuf:"varint,8,opt,name=defined_in_canvas,json=definedInCanvas,proto3" json:"defined_in_canvas,omitempty"` + // Values bound to the referenced component's declared params. + // Only set for items that reference an externally defined component. + Params *structpb.Struct `protobuf:"bytes,11,opt,name=params,proto3" json:"params,omitempty"` // Width of the item. The unit is given in width_unit. Width *uint32 `protobuf:"varint,9,opt,name=width,proto3,oneof" json:"width,omitempty"` // Unit of the width. Current possible values: empty string. @@ -6153,7 +6271,7 @@ type CanvasItem struct { func (x *CanvasItem) Reset() { *x = CanvasItem{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[60] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6166,7 +6284,7 @@ func (x *CanvasItem) String() string { func (*CanvasItem) ProtoMessage() {} func (x *CanvasItem) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[60] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6179,7 +6297,7 @@ func (x *CanvasItem) ProtoReflect() protoreflect.Message { // Deprecated: Use CanvasItem.ProtoReflect.Descriptor instead. func (*CanvasItem) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{60} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{61} } func (x *CanvasItem) GetComponent() string { @@ -6196,6 +6314,13 @@ func (x *CanvasItem) GetDefinedInCanvas() bool { return false } +func (x *CanvasItem) GetParams() *structpb.Struct { + if x != nil { + return x.Params + } + return nil +} + func (x *CanvasItem) GetWidth() uint32 { if x != nil && x.Width != nil { return *x.Width @@ -6233,7 +6358,7 @@ type CanvasPreset struct { func (x *CanvasPreset) Reset() { *x = CanvasPreset{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[61] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6246,7 +6371,7 @@ func (x *CanvasPreset) String() string { func (*CanvasPreset) ProtoMessage() {} func (x *CanvasPreset) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[61] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[62] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6259,7 +6384,7 @@ func (x *CanvasPreset) ProtoReflect() protoreflect.Message { // Deprecated: Use CanvasPreset.ProtoReflect.Descriptor instead. func (*CanvasPreset) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{61} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{62} } func (x *CanvasPreset) GetTimeRange() string { @@ -6301,7 +6426,7 @@ type DefaultMetricsSQLFilter struct { func (x *DefaultMetricsSQLFilter) Reset() { *x = DefaultMetricsSQLFilter{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[62] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6314,7 +6439,7 @@ func (x *DefaultMetricsSQLFilter) String() string { func (*DefaultMetricsSQLFilter) ProtoMessage() {} func (x *DefaultMetricsSQLFilter) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[62] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[63] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6327,7 +6452,7 @@ func (x *DefaultMetricsSQLFilter) ProtoReflect() protoreflect.Message { // Deprecated: Use DefaultMetricsSQLFilter.ProtoReflect.Descriptor instead. func (*DefaultMetricsSQLFilter) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{62} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{63} } func (x *DefaultMetricsSQLFilter) GetExpression() *Expression { @@ -6350,7 +6475,7 @@ type API struct { func (x *API) Reset() { *x = API{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[63] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6363,7 +6488,7 @@ func (x *API) String() string { func (*API) ProtoMessage() {} func (x *API) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[63] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[64] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6376,7 +6501,7 @@ func (x *API) ProtoReflect() protoreflect.Message { // Deprecated: Use API.ProtoReflect.Descriptor instead. func (*API) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{63} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{64} } func (x *API) GetSpec() *APISpec { @@ -6412,7 +6537,7 @@ type APISpec struct { func (x *APISpec) Reset() { *x = APISpec{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[64] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6425,7 +6550,7 @@ func (x *APISpec) String() string { func (*APISpec) ProtoMessage() {} func (x *APISpec) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[64] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[65] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6438,7 +6563,7 @@ func (x *APISpec) ProtoReflect() protoreflect.Message { // Deprecated: Use APISpec.ProtoReflect.Descriptor instead. func (*APISpec) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{64} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{65} } func (x *APISpec) GetResolver() string { @@ -6513,7 +6638,7 @@ type APIState struct { func (x *APIState) Reset() { *x = APIState{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[65] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6526,7 +6651,7 @@ func (x *APIState) String() string { func (*APIState) ProtoMessage() {} func (x *APIState) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[65] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[66] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6539,7 +6664,7 @@ func (x *APIState) ProtoReflect() protoreflect.Message { // Deprecated: Use APIState.ProtoReflect.Descriptor instead. func (*APIState) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{65} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{66} } type Schedule struct { @@ -6557,7 +6682,7 @@ type Schedule struct { func (x *Schedule) Reset() { *x = Schedule{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[66] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6570,7 +6695,7 @@ func (x *Schedule) String() string { func (*Schedule) ProtoMessage() {} func (x *Schedule) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[66] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6583,7 +6708,7 @@ func (x *Schedule) ProtoReflect() protoreflect.Message { // Deprecated: Use Schedule.ProtoReflect.Descriptor instead. func (*Schedule) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{66} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{67} } func (x *Schedule) GetRefUpdate() bool { @@ -6636,7 +6761,7 @@ type ParseError struct { func (x *ParseError) Reset() { *x = ParseError{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[67] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6649,7 +6774,7 @@ func (x *ParseError) String() string { func (*ParseError) ProtoMessage() {} func (x *ParseError) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[67] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[68] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6662,7 +6787,7 @@ func (x *ParseError) ProtoReflect() protoreflect.Message { // Deprecated: Use ParseError.ProtoReflect.Descriptor instead. func (*ParseError) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{67} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{68} } func (x *ParseError) GetMessage() string { @@ -6712,7 +6837,7 @@ type ValidationError struct { func (x *ValidationError) Reset() { *x = ValidationError{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[68] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6725,7 +6850,7 @@ func (x *ValidationError) String() string { func (*ValidationError) ProtoMessage() {} func (x *ValidationError) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[68] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[69] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6738,7 +6863,7 @@ func (x *ValidationError) ProtoReflect() protoreflect.Message { // Deprecated: Use ValidationError.ProtoReflect.Descriptor instead. func (*ValidationError) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{68} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{69} } func (x *ValidationError) GetMessage() string { @@ -6767,7 +6892,7 @@ type DependencyError struct { func (x *DependencyError) Reset() { *x = DependencyError{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[69] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6780,7 +6905,7 @@ func (x *DependencyError) String() string { func (*DependencyError) ProtoMessage() {} func (x *DependencyError) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[69] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[70] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6793,7 +6918,7 @@ func (x *DependencyError) ProtoReflect() protoreflect.Message { // Deprecated: Use DependencyError.ProtoReflect.Descriptor instead. func (*DependencyError) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{69} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{70} } func (x *DependencyError) GetMessage() string { @@ -6821,7 +6946,7 @@ type ExecutionError struct { func (x *ExecutionError) Reset() { *x = ExecutionError{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[70] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6834,7 +6959,7 @@ func (x *ExecutionError) String() string { func (*ExecutionError) ProtoMessage() {} func (x *ExecutionError) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[70] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[71] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6847,7 +6972,7 @@ func (x *ExecutionError) ProtoReflect() protoreflect.Message { // Deprecated: Use ExecutionError.ProtoReflect.Descriptor instead. func (*ExecutionError) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{70} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{71} } func (x *ExecutionError) GetMessage() string { @@ -6868,7 +6993,7 @@ type CharLocation struct { func (x *CharLocation) Reset() { *x = CharLocation{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[71] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6881,7 +7006,7 @@ func (x *CharLocation) String() string { func (*CharLocation) ProtoMessage() {} func (x *CharLocation) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[71] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[72] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6894,7 +7019,7 @@ func (x *CharLocation) ProtoReflect() protoreflect.Message { // Deprecated: Use CharLocation.ProtoReflect.Descriptor instead. func (*CharLocation) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{71} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{72} } func (x *CharLocation) GetLine() uint32 { @@ -6916,7 +7041,7 @@ type ConnectorV2 struct { func (x *ConnectorV2) Reset() { *x = ConnectorV2{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[72] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6929,7 +7054,7 @@ func (x *ConnectorV2) String() string { func (*ConnectorV2) ProtoMessage() {} func (x *ConnectorV2) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[72] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[73] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6942,7 +7067,7 @@ func (x *ConnectorV2) ProtoReflect() protoreflect.Message { // Deprecated: Use ConnectorV2.ProtoReflect.Descriptor instead. func (*ConnectorV2) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{72} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{73} } func (x *ConnectorV2) GetSpec() *ConnectorSpec { @@ -6974,7 +7099,7 @@ type ConnectorSpec struct { func (x *ConnectorSpec) Reset() { *x = ConnectorSpec{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[73] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6987,7 +7112,7 @@ func (x *ConnectorSpec) String() string { func (*ConnectorSpec) ProtoMessage() {} func (x *ConnectorSpec) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[73] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[74] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7000,7 +7125,7 @@ func (x *ConnectorSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use ConnectorSpec.ProtoReflect.Descriptor instead. func (*ConnectorSpec) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{73} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{74} } func (x *ConnectorSpec) GetDriver() string { @@ -7049,7 +7174,7 @@ type ConnectorState struct { func (x *ConnectorState) Reset() { *x = ConnectorState{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[74] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7062,7 +7187,7 @@ func (x *ConnectorState) String() string { func (*ConnectorState) ProtoMessage() {} func (x *ConnectorState) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[74] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[75] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7075,7 +7200,7 @@ func (x *ConnectorState) ProtoReflect() protoreflect.Message { // Deprecated: Use ConnectorState.ProtoReflect.Descriptor instead. func (*ConnectorState) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{74} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{75} } func (x *ConnectorState) GetSpecHash() string { @@ -7115,7 +7240,7 @@ type MetricsViewSpec_Dimension struct { func (x *MetricsViewSpec_Dimension) Reset() { *x = MetricsViewSpec_Dimension{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[75] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7128,7 +7253,7 @@ func (x *MetricsViewSpec_Dimension) String() string { func (*MetricsViewSpec_Dimension) ProtoMessage() {} func (x *MetricsViewSpec_Dimension) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[75] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[76] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7263,7 +7388,7 @@ type MetricsViewSpec_DimensionSelector struct { func (x *MetricsViewSpec_DimensionSelector) Reset() { *x = MetricsViewSpec_DimensionSelector{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[76] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7276,7 +7401,7 @@ func (x *MetricsViewSpec_DimensionSelector) String() string { func (*MetricsViewSpec_DimensionSelector) ProtoMessage() {} func (x *MetricsViewSpec_DimensionSelector) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[76] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[77] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7330,7 +7455,7 @@ type MetricsViewSpec_MeasureWindow struct { func (x *MetricsViewSpec_MeasureWindow) Reset() { *x = MetricsViewSpec_MeasureWindow{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[77] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7343,7 +7468,7 @@ func (x *MetricsViewSpec_MeasureWindow) String() string { func (*MetricsViewSpec_MeasureWindow) ProtoMessage() {} func (x *MetricsViewSpec_MeasureWindow) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[77] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[78] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7410,7 +7535,7 @@ type MetricsViewSpec_Measure struct { func (x *MetricsViewSpec_Measure) Reset() { *x = MetricsViewSpec_Measure{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[78] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7423,7 +7548,7 @@ func (x *MetricsViewSpec_Measure) String() string { func (*MetricsViewSpec_Measure) ProtoMessage() {} func (x *MetricsViewSpec_Measure) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[78] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[79] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7591,7 +7716,7 @@ type MetricsViewSpec_Annotation struct { func (x *MetricsViewSpec_Annotation) Reset() { *x = MetricsViewSpec_Annotation{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[79] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7604,7 +7729,7 @@ func (x *MetricsViewSpec_Annotation) String() string { func (*MetricsViewSpec_Annotation) ProtoMessage() {} func (x *MetricsViewSpec_Annotation) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[79] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[80] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7721,7 +7846,7 @@ type MetricsViewSpec_Rollup struct { func (x *MetricsViewSpec_Rollup) Reset() { *x = MetricsViewSpec_Rollup{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[80] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7734,7 +7859,7 @@ func (x *MetricsViewSpec_Rollup) String() string { func (*MetricsViewSpec_Rollup) ProtoMessage() {} func (x *MetricsViewSpec_Rollup) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[80] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[81] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9169,7 +9294,7 @@ var file_rill_runtime_v1_resources_proto_rawDesc = []byte{ 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, 0xdc, 0x02, 0x0a, 0x0d, 0x43, + 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x95, 0x03, 0x0a, 0x0d, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, @@ -9181,350 +9306,373 @@ var file_rill_runtime_v1_resources_proto_rawDesc = []byte{ 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, 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, 0x97, 0x01, 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, 0x12, 0x46, 0x0a, 0x11, 0x64, - 0x61, 0x74, 0x61, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x6f, 0x6e, - 0x18, 0x02, 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, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x65, - 0x64, 0x4f, 0x6e, 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, 0x8a, 0x07, 0x0a, - 0x0a, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, 0x12, 0x21, 0x0a, 0x0c, 0x64, - 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x77, 0x69, - 0x64, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x57, 0x69, - 0x64, 0x74, 0x68, 0x12, 0x13, 0x0a, 0x05, 0x67, 0x61, 0x70, 0x5f, 0x78, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x04, 0x67, 0x61, 0x70, 0x58, 0x12, 0x13, 0x0a, 0x05, 0x67, 0x61, 0x70, 0x5f, - 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x67, 0x61, 0x70, 0x59, 0x12, 0x14, 0x0a, - 0x05, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x68, - 0x65, 0x6d, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x65, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x5f, - 0x74, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x08, 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, 0x0d, 0x65, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, - 0x64, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x42, 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, - 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x72, 0x69, - 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, - 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0a, - 0x74, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x35, 0x0a, 0x17, 0x61, 0x6c, - 0x6c, 0x6f, 0x77, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, - 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x61, 0x6c, 0x6c, - 0x6f, 0x77, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, - 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x73, 0x18, - 0x0c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x73, - 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x66, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x44, 0x0a, 0x0e, 0x64, 0x65, 0x66, - 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x73, 0x65, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x50, 0x72, 0x65, 0x73, 0x65, 0x74, - 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x72, 0x65, 0x73, 0x65, 0x74, 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, 0x2e, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x18, 0x0a, 0x20, 0x03, 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, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 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, 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, 0x97, 0x01, 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, 0x12, 0x46, 0x0a, 0x11, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, 0x65, 0x66, + 0x72, 0x65, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x02, 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, 0x64, 0x61, 0x74, + 0x61, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x65, 0x64, 0x4f, 0x6e, 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, 0x88, 0x02, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, + 0x6e, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 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, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, + 0x30, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x05, 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, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, + 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, + 0x77, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, 0x77, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x12, + 0x30, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 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, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 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, 0x8a, 0x07, 0x0a, 0x0a, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, 0x12, + 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x18, 0x11, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, + 0x78, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6d, + 0x61, 0x78, 0x57, 0x69, 0x64, 0x74, 0x68, 0x12, 0x13, 0x0a, 0x05, 0x67, 0x61, 0x70, 0x5f, 0x78, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x67, 0x61, 0x70, 0x58, 0x12, 0x13, 0x0a, 0x05, + 0x67, 0x61, 0x70, 0x5f, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x67, 0x61, 0x70, + 0x59, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x65, 0x6d, 0x62, 0x65, 0x64, + 0x64, 0x65, 0x64, 0x5f, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, - 0x73, 0x12, 0x44, 0x0a, 0x0e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x72, 0x75, + 0x31, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x70, 0x65, 0x63, 0x52, 0x0d, 0x65, 0x6d, 0x62, + 0x65, 0x64, 0x64, 0x65, 0x64, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x42, 0x0a, 0x0b, 0x74, 0x69, + 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x21, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, + 0x67, 0x65, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x35, + 0x0a, 0x17, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x14, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x54, 0x69, 0x6d, 0x65, + 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, + 0x6e, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5a, + 0x6f, 0x6e, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x5f, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x66, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x44, 0x0a, + 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x73, 0x65, 0x74, 0x18, + 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x50, 0x72, + 0x65, 0x73, 0x65, 0x74, 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x72, 0x65, + 0x73, 0x65, 0x74, 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, 0x2e, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x12, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x52, 0x6f, 0x77, 0x52, + 0x04, 0x72, 0x6f, 0x77, 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, 0x12, 0x25, 0x0a, 0x0e, 0x70, + 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x10, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, + 0x72, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x66, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x12, 0x4e, 0x0a, + 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x14, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2c, 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, 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, 0x91, 0x01, + 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, 0x12, 0x46, 0x0a, 0x11, 0x64, 0x61, 0x74, + 0x61, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x02, + 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, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x65, 0x64, 0x4f, + 0x6e, 0x22, 0xc5, 0x01, 0x0a, 0x09, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x52, 0x6f, 0x77, 0x12, + 0x1b, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, + 0x00, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x0b, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x55, 0x6e, 0x69, 0x74, 0x12, 0x31, 0x0a, + 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x03, 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, 0x3c, 0x0a, 0x09, 0x74, 0x61, 0x62, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 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, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x54, 0x61, 0x62, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x52, 0x08, 0x74, 0x61, 0x62, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x09, + 0x0a, 0x07, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x54, 0x0a, 0x0e, 0x43, 0x61, 0x6e, + 0x76, 0x61, 0x73, 0x54, 0x61, 0x62, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x2e, 0x0a, 0x04, 0x74, 0x61, 0x62, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x54, 0x61, 0x62, 0x52, 0x04, 0x74, 0x61, 0x62, 0x73, 0x22, + 0x72, 0x0a, 0x09, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x54, 0x61, 0x62, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, + 0x6f, 0x77, 0x73, 0x22, 0xcb, 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, 0x2f, 0x0a, 0x06, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x0b, 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, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x19, 0x0a, + 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x05, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x0a, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x55, 0x6e, 0x69, 0x74, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x22, 0x9c, 0x03, 0x0a, 0x0c, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x50, 0x72, 0x65, 0x73, + 0x65, 0x74, 0x12, 0x22, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x61, + 0x6e, 0x67, 0x65, 0x88, 0x01, 0x01, 0x12, 0x4f, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, + 0x69, 0x73, 0x6f, 0x6e, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x02, 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, 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, 0x01, 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, + 0x4e, 0x0a, 0x0b, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x18, 0x13, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x50, 0x72, 0x65, + 0x73, 0x65, 0x74, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x45, 0x78, 0x70, 0x72, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x45, 0x78, 0x70, 0x72, 0x1a, + 0x67, 0x0a, 0x0f, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x45, 0x78, 0x70, 0x72, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x73, 0x53, 0x51, 0x4c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 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, + 0x22, 0x56, 0x0a, 0x17, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x73, 0x53, 0x51, 0x4c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x3b, 0x0a, 0x0a, 0x65, + 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 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, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x65, 0x78, + 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 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, 0xf8, + 0x03, 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, 0x36, 0x0a, 0x17, 0x6f, 0x70, 0x65, + 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x5f, + 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x6f, 0x70, 0x65, 0x6e, + 0x61, 0x70, 0x69, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x4a, 0x73, 0x6f, + 0x6e, 0x12, 0x3d, 0x0a, 0x1b, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x18, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4a, 0x73, 0x6f, 0x6e, + 0x12, 0x3f, 0x0a, 0x1c, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x72, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x19, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4a, 0x73, 0x6f, + 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x64, 0x65, 0x66, + 0x73, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, + 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x44, 0x65, 0x66, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, + 0x78, 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, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x69, 0x6e, 0x6e, 0x65, - 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0d, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x12, 0x29, - 0x0a, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, - 0x65, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x12, 0x4e, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, - 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, - 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, 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, 0x91, 0x01, 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, 0x12, 0x46, 0x0a, 0x11, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, 0x65, - 0x66, 0x72, 0x65, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x02, 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, 0x64, 0x61, - 0x74, 0x61, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x65, 0x64, 0x4f, 0x6e, 0x22, 0xc5, 0x01, - 0x0a, 0x09, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x52, 0x6f, 0x77, 0x12, 0x1b, 0x0a, 0x06, 0x68, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x06, 0x68, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x65, 0x69, 0x67, - 0x68, 0x74, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x68, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x55, 0x6e, 0x69, 0x74, 0x12, 0x31, 0x0a, 0x05, 0x69, 0x74, 0x65, - 0x6d, 0x73, 0x18, 0x03, 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, 0x3c, 0x0a, 0x09, - 0x74, 0x61, 0x62, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 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, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x54, 0x61, 0x62, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x52, 0x08, 0x74, 0x61, 0x62, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x54, 0x0a, 0x0e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x54, - 0x61, 0x62, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x74, - 0x61, 0x62, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, - 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, - 0x61, 0x73, 0x54, 0x61, 0x62, 0x52, 0x04, 0x74, 0x61, 0x62, 0x73, 0x22, 0x72, 0x0a, 0x09, 0x43, - 0x61, 0x6e, 0x76, 0x61, 0x73, 0x54, 0x61, 0x62, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, - 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x2e, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x22, - 0x9a, 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, 0x19, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, - 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, - 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x0a, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x75, 0x6e, 0x69, - 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x69, 0x64, 0x74, 0x68, 0x55, 0x6e, - 0x69, 0x74, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x9c, 0x03, 0x0a, - 0x0c, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x50, 0x72, 0x65, 0x73, 0x65, 0x74, 0x12, 0x22, 0x0a, - 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x48, 0x00, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x88, 0x01, - 0x01, 0x12, 0x4f, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x5f, - 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x02, 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, 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, 0x01, 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, 0x4e, 0x0a, 0x0b, 0x66, 0x69, - 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x50, 0x72, 0x65, 0x73, 0x65, 0x74, 0x2e, 0x46, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x45, 0x78, 0x70, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, - 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x45, 0x78, 0x70, 0x72, 0x1a, 0x67, 0x0a, 0x0f, 0x46, 0x69, - 0x6c, 0x74, 0x65, 0x72, 0x45, 0x78, 0x70, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x3e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, - 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x53, - 0x51, 0x4c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 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, 0x22, 0x56, 0x0a, 0x17, 0x44, - 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x53, 0x51, 0x4c, - 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x3b, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 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, 0x45, 0x78, 0x70, - 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 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, 0xf8, 0x03, 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, 0x36, 0x0a, 0x17, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x1b, - 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, - 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x18, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x1c, 0x6f, - 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, - 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x19, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x13, - 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x64, 0x65, 0x66, 0x73, 0x5f, 0x70, 0x72, 0x65, - 0x66, 0x69, 0x78, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x6f, 0x70, 0x65, 0x6e, 0x61, - 0x70, 0x69, 0x44, 0x65, 0x66, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 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, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x6e, 0x65, 0x73, 0x74, 0x65, - 0x64, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x12, 0x73, 0x6b, 0x69, 0x70, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x53, 0x65, 0x63, 0x75, - 0x72, 0x69, 0x74, 0x79, 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, 0xbf, - 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, 0x12, 0x18, 0x0a, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, - 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, - 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, - 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, 0x22, 0xf1, 0x01, 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, 0x37, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, - 0x73, 0x18, 0x07, 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, 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, - 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, - 0x0e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, - 0x06, 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, 0x0d, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x67, 0x73, 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, 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, 0x8c, 0x01, 0x0a, 0x0f, 0x4d, 0x6f, - 0x64, 0x65, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, - 0x1d, 0x4d, 0x4f, 0x44, 0x45, 0x4c, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x5f, 0x4d, 0x4f, - 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, + 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x6b, 0x69, 0x70, 0x5f, + 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x73, 0x6b, 0x69, 0x70, 0x4e, 0x65, 0x73, 0x74, 0x65, + 0x64, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 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, 0xbf, 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, 0x12, 0x18, 0x0a, 0x07, 0x77, + 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x77, 0x61, + 0x72, 0x6e, 0x69, 0x6e, 0x67, 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, 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, 0x22, 0xf1, + 0x01, 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, 0x37, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, + 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x07, 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, 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, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x61, 0x72, 0x67, 0x73, 0x18, 0x06, 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, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x72, + 0x67, 0x73, 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, 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, 0x8c, + 0x01, 0x0a, 0x0f, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x6f, + 0x64, 0x65, 0x12, 0x21, 0x0a, 0x1d, 0x4d, 0x4f, 0x44, 0x45, 0x4c, 0x5f, 0x43, 0x48, 0x41, 0x4e, + 0x47, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, + 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x4f, 0x44, 0x45, 0x4c, 0x5f, 0x43, + 0x48, 0x41, 0x4e, 0x47, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x45, 0x54, + 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x4d, 0x4f, 0x44, 0x45, 0x4c, 0x5f, 0x43, 0x48, 0x41, 0x4e, + 0x47, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x4d, 0x41, 0x4e, 0x55, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x4f, 0x44, 0x45, 0x4c, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, - 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x45, 0x54, 0x10, 0x01, 0x12, 0x1c, 0x0a, - 0x18, 0x4d, 0x4f, 0x44, 0x45, 0x4c, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x5f, 0x4d, 0x4f, - 0x44, 0x45, 0x5f, 0x4d, 0x41, 0x4e, 0x55, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x4d, - 0x4f, 0x44, 0x45, 0x4c, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45, - 0x5f, 0x50, 0x41, 0x54, 0x43, 0x48, 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, 0xae, 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, 0x57, 0x45, 0x42, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x55, 0x4e, - 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x45, - 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x57, 0x45, 0x42, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, - 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x10, 0x01, 0x12, 0x23, 0x0a, 0x1f, 0x45, 0x58, 0x50, - 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x57, 0x45, 0x42, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x54, 0x49, - 0x4d, 0x45, 0x5f, 0x44, 0x49, 0x4d, 0x45, 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x1a, - 0x0a, 0x16, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x57, 0x45, 0x42, 0x5f, 0x56, 0x49, - 0x45, 0x57, 0x5f, 0x50, 0x49, 0x56, 0x4f, 0x54, 0x10, 0x03, 0x12, 0x1b, 0x0a, 0x17, 0x45, 0x58, - 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x57, 0x45, 0x42, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x43, - 0x41, 0x4e, 0x56, 0x41, 0x53, 0x10, 0x04, 0x2a, 0xdc, 0x01, 0x0a, 0x0f, 0x45, 0x78, 0x70, 0x6c, - 0x6f, 0x72, 0x65, 0x53, 0x6f, 0x72, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x1d, 0x45, - 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, - 0x0a, 0x17, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, 0x45, - 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x50, 0x45, 0x52, 0x43, 0x45, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x23, 0x0a, 0x1f, 0x45, 0x58, + 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x41, 0x54, 0x43, 0x48, 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, 0xae, 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, 0x57, 0x45, 0x42, 0x5f, 0x56, 0x49, + 0x45, 0x57, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, + 0x12, 0x1c, 0x0a, 0x18, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x57, 0x45, 0x42, 0x5f, + 0x56, 0x49, 0x45, 0x57, 0x5f, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x10, 0x01, 0x12, 0x23, + 0x0a, 0x1f, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x57, 0x45, 0x42, 0x5f, 0x56, 0x49, + 0x45, 0x57, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x5f, 0x44, 0x49, 0x4d, 0x45, 0x4e, 0x53, 0x49, 0x4f, + 0x4e, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x57, + 0x45, 0x42, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x50, 0x49, 0x56, 0x4f, 0x54, 0x10, 0x03, 0x12, + 0x1b, 0x0a, 0x17, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x57, 0x45, 0x42, 0x5f, 0x56, + 0x49, 0x45, 0x57, 0x5f, 0x43, 0x41, 0x4e, 0x56, 0x41, 0x53, 0x10, 0x04, 0x2a, 0xdc, 0x01, 0x0a, + 0x0f, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x53, 0x6f, 0x72, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x21, 0x0a, 0x1d, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x4f, 0x52, 0x54, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, + 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x53, + 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x01, + 0x12, 0x1d, 0x0a, 0x19, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x4f, 0x52, 0x54, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x45, 0x52, 0x43, 0x45, 0x4e, 0x54, 0x10, 0x02, 0x12, + 0x23, 0x0a, 0x1f, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x4c, 0x54, 0x41, 0x5f, 0x50, 0x45, 0x52, 0x43, 0x45, + 0x4e, 0x54, 0x10, 0x03, 0x12, 0x24, 0x0a, 0x20, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, + 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x4c, 0x54, 0x41, 0x5f, + 0x41, 0x42, 0x53, 0x4f, 0x4c, 0x55, 0x54, 0x45, 0x10, 0x04, 0x12, 0x1f, 0x0a, 0x1b, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x44, 0x45, 0x4c, 0x54, 0x41, 0x5f, 0x50, 0x45, 0x52, 0x43, 0x45, 0x4e, 0x54, 0x10, 0x03, 0x12, - 0x24, 0x0a, 0x20, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x4c, 0x54, 0x41, 0x5f, 0x41, 0x42, 0x53, 0x4f, 0x4c, - 0x55, 0x54, 0x45, 0x10, 0x04, 0x12, 0x1f, 0x0a, 0x1b, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, - 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x49, 0x4d, 0x45, 0x4e, - 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x05, 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, + 0x44, 0x49, 0x4d, 0x45, 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x05, 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, 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, + 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 ( @@ -9540,7 +9688,7 @@ func file_rill_runtime_v1_resources_proto_rawDescGZIP() []byte { } var file_rill_runtime_v1_resources_proto_enumTypes = make([]protoimpl.EnumInfo, 8) -var file_rill_runtime_v1_resources_proto_msgTypes = make([]protoimpl.MessageInfo, 87) +var file_rill_runtime_v1_resources_proto_msgTypes = make([]protoimpl.MessageInfo, 88) var file_rill_runtime_v1_resources_proto_goTypes = []any{ (ReconcileStatus)(0), // 0: rill.runtime.v1.ReconcileStatus (ModelChangeMode)(0), // 1: rill.runtime.v1.ModelChangeMode @@ -9604,48 +9752,49 @@ var file_rill_runtime_v1_resources_proto_goTypes = []any{ (*ComponentSpec)(nil), // 59: rill.runtime.v1.ComponentSpec (*ComponentState)(nil), // 60: rill.runtime.v1.ComponentState (*ComponentVariable)(nil), // 61: rill.runtime.v1.ComponentVariable - (*Canvas)(nil), // 62: rill.runtime.v1.Canvas - (*CanvasSpec)(nil), // 63: rill.runtime.v1.CanvasSpec - (*CanvasState)(nil), // 64: rill.runtime.v1.CanvasState - (*CanvasRow)(nil), // 65: rill.runtime.v1.CanvasRow - (*CanvasTabGroup)(nil), // 66: rill.runtime.v1.CanvasTabGroup - (*CanvasTab)(nil), // 67: rill.runtime.v1.CanvasTab - (*CanvasItem)(nil), // 68: rill.runtime.v1.CanvasItem - (*CanvasPreset)(nil), // 69: rill.runtime.v1.CanvasPreset - (*DefaultMetricsSQLFilter)(nil), // 70: rill.runtime.v1.DefaultMetricsSQLFilter - (*API)(nil), // 71: rill.runtime.v1.API - (*APISpec)(nil), // 72: rill.runtime.v1.APISpec - (*APIState)(nil), // 73: rill.runtime.v1.APIState - (*Schedule)(nil), // 74: rill.runtime.v1.Schedule - (*ParseError)(nil), // 75: rill.runtime.v1.ParseError - (*ValidationError)(nil), // 76: rill.runtime.v1.ValidationError - (*DependencyError)(nil), // 77: rill.runtime.v1.DependencyError - (*ExecutionError)(nil), // 78: rill.runtime.v1.ExecutionError - (*CharLocation)(nil), // 79: rill.runtime.v1.CharLocation - (*ConnectorV2)(nil), // 80: rill.runtime.v1.ConnectorV2 - (*ConnectorSpec)(nil), // 81: rill.runtime.v1.ConnectorSpec - (*ConnectorState)(nil), // 82: rill.runtime.v1.ConnectorState - (*MetricsViewSpec_Dimension)(nil), // 83: rill.runtime.v1.MetricsViewSpec.Dimension - (*MetricsViewSpec_DimensionSelector)(nil), // 84: rill.runtime.v1.MetricsViewSpec.DimensionSelector - (*MetricsViewSpec_MeasureWindow)(nil), // 85: rill.runtime.v1.MetricsViewSpec.MeasureWindow - (*MetricsViewSpec_Measure)(nil), // 86: rill.runtime.v1.MetricsViewSpec.Measure - (*MetricsViewSpec_Annotation)(nil), // 87: rill.runtime.v1.MetricsViewSpec.Annotation - (*MetricsViewSpec_Rollup)(nil), // 88: rill.runtime.v1.MetricsViewSpec.Rollup - nil, // 89: rill.runtime.v1.MetricsViewSpec.QueryAttributesEntry - nil, // 90: rill.runtime.v1.ReportSpec.AnnotationsEntry - nil, // 91: rill.runtime.v1.AlertSpec.AnnotationsEntry - nil, // 92: rill.runtime.v1.ThemeColors.VariablesEntry - nil, // 93: rill.runtime.v1.CanvasSpec.AnnotationsEntry - nil, // 94: rill.runtime.v1.CanvasPreset.FilterExprEntry - (*timestamppb.Timestamp)(nil), // 95: google.protobuf.Timestamp - (*structpb.Struct)(nil), // 96: google.protobuf.Struct - (*StructType)(nil), // 97: rill.runtime.v1.StructType - (TimeGrain)(0), // 98: rill.runtime.v1.TimeGrain - (*Expression)(nil), // 99: rill.runtime.v1.Expression - (ExportFormat)(0), // 100: rill.runtime.v1.ExportFormat - (*Color)(nil), // 101: rill.runtime.v1.Color - (*structpb.Value)(nil), // 102: google.protobuf.Value - (*Type)(nil), // 103: rill.runtime.v1.Type + (*ComponentParam)(nil), // 62: rill.runtime.v1.ComponentParam + (*Canvas)(nil), // 63: rill.runtime.v1.Canvas + (*CanvasSpec)(nil), // 64: rill.runtime.v1.CanvasSpec + (*CanvasState)(nil), // 65: rill.runtime.v1.CanvasState + (*CanvasRow)(nil), // 66: rill.runtime.v1.CanvasRow + (*CanvasTabGroup)(nil), // 67: rill.runtime.v1.CanvasTabGroup + (*CanvasTab)(nil), // 68: rill.runtime.v1.CanvasTab + (*CanvasItem)(nil), // 69: rill.runtime.v1.CanvasItem + (*CanvasPreset)(nil), // 70: rill.runtime.v1.CanvasPreset + (*DefaultMetricsSQLFilter)(nil), // 71: rill.runtime.v1.DefaultMetricsSQLFilter + (*API)(nil), // 72: rill.runtime.v1.API + (*APISpec)(nil), // 73: rill.runtime.v1.APISpec + (*APIState)(nil), // 74: rill.runtime.v1.APIState + (*Schedule)(nil), // 75: rill.runtime.v1.Schedule + (*ParseError)(nil), // 76: rill.runtime.v1.ParseError + (*ValidationError)(nil), // 77: rill.runtime.v1.ValidationError + (*DependencyError)(nil), // 78: rill.runtime.v1.DependencyError + (*ExecutionError)(nil), // 79: rill.runtime.v1.ExecutionError + (*CharLocation)(nil), // 80: rill.runtime.v1.CharLocation + (*ConnectorV2)(nil), // 81: rill.runtime.v1.ConnectorV2 + (*ConnectorSpec)(nil), // 82: rill.runtime.v1.ConnectorSpec + (*ConnectorState)(nil), // 83: rill.runtime.v1.ConnectorState + (*MetricsViewSpec_Dimension)(nil), // 84: rill.runtime.v1.MetricsViewSpec.Dimension + (*MetricsViewSpec_DimensionSelector)(nil), // 85: rill.runtime.v1.MetricsViewSpec.DimensionSelector + (*MetricsViewSpec_MeasureWindow)(nil), // 86: rill.runtime.v1.MetricsViewSpec.MeasureWindow + (*MetricsViewSpec_Measure)(nil), // 87: rill.runtime.v1.MetricsViewSpec.Measure + (*MetricsViewSpec_Annotation)(nil), // 88: rill.runtime.v1.MetricsViewSpec.Annotation + (*MetricsViewSpec_Rollup)(nil), // 89: rill.runtime.v1.MetricsViewSpec.Rollup + nil, // 90: rill.runtime.v1.MetricsViewSpec.QueryAttributesEntry + nil, // 91: rill.runtime.v1.ReportSpec.AnnotationsEntry + nil, // 92: rill.runtime.v1.AlertSpec.AnnotationsEntry + nil, // 93: rill.runtime.v1.ThemeColors.VariablesEntry + nil, // 94: rill.runtime.v1.CanvasSpec.AnnotationsEntry + nil, // 95: rill.runtime.v1.CanvasPreset.FilterExprEntry + (*timestamppb.Timestamp)(nil), // 96: google.protobuf.Timestamp + (*structpb.Struct)(nil), // 97: google.protobuf.Struct + (*StructType)(nil), // 98: rill.runtime.v1.StructType + (TimeGrain)(0), // 99: rill.runtime.v1.TimeGrain + (*Expression)(nil), // 100: rill.runtime.v1.Expression + (ExportFormat)(0), // 101: rill.runtime.v1.ExportFormat + (*Color)(nil), // 102: rill.runtime.v1.Color + (*structpb.Value)(nil), // 103: google.protobuf.Value + (*Type)(nil), // 104: rill.runtime.v1.Type } var file_rill_runtime_v1_resources_proto_depIdxs = []int32{ 9, // 0: rill.runtime.v1.Resource.meta:type_name -> rill.runtime.v1.ResourceMeta @@ -9660,54 +9809,54 @@ var file_rill_runtime_v1_resources_proto_depIdxs = []int32{ 50, // 9: rill.runtime.v1.Resource.refresh_trigger:type_name -> rill.runtime.v1.RefreshTrigger 54, // 10: rill.runtime.v1.Resource.theme:type_name -> rill.runtime.v1.Theme 58, // 11: rill.runtime.v1.Resource.component:type_name -> rill.runtime.v1.Component - 62, // 12: rill.runtime.v1.Resource.canvas:type_name -> rill.runtime.v1.Canvas - 71, // 13: rill.runtime.v1.Resource.api:type_name -> rill.runtime.v1.API - 80, // 14: rill.runtime.v1.Resource.connector:type_name -> rill.runtime.v1.ConnectorV2 + 63, // 12: rill.runtime.v1.Resource.canvas:type_name -> rill.runtime.v1.Canvas + 72, // 13: rill.runtime.v1.Resource.api:type_name -> rill.runtime.v1.API + 81, // 14: rill.runtime.v1.Resource.connector:type_name -> rill.runtime.v1.ConnectorV2 10, // 15: rill.runtime.v1.ResourceMeta.name:type_name -> rill.runtime.v1.ResourceName 10, // 16: rill.runtime.v1.ResourceMeta.refs:type_name -> rill.runtime.v1.ResourceName 10, // 17: rill.runtime.v1.ResourceMeta.owner:type_name -> rill.runtime.v1.ResourceName - 95, // 18: rill.runtime.v1.ResourceMeta.created_on:type_name -> google.protobuf.Timestamp - 95, // 19: rill.runtime.v1.ResourceMeta.spec_updated_on:type_name -> google.protobuf.Timestamp - 95, // 20: rill.runtime.v1.ResourceMeta.state_updated_on:type_name -> google.protobuf.Timestamp - 95, // 21: rill.runtime.v1.ResourceMeta.deleted_on:type_name -> google.protobuf.Timestamp + 96, // 18: rill.runtime.v1.ResourceMeta.created_on:type_name -> google.protobuf.Timestamp + 96, // 19: rill.runtime.v1.ResourceMeta.spec_updated_on:type_name -> google.protobuf.Timestamp + 96, // 20: rill.runtime.v1.ResourceMeta.state_updated_on:type_name -> google.protobuf.Timestamp + 96, // 21: rill.runtime.v1.ResourceMeta.deleted_on:type_name -> google.protobuf.Timestamp 0, // 22: rill.runtime.v1.ResourceMeta.reconcile_status:type_name -> rill.runtime.v1.ReconcileStatus - 95, // 23: rill.runtime.v1.ResourceMeta.reconcile_on:type_name -> google.protobuf.Timestamp + 96, // 23: rill.runtime.v1.ResourceMeta.reconcile_on:type_name -> google.protobuf.Timestamp 10, // 24: rill.runtime.v1.ResourceMeta.renamed_from:type_name -> rill.runtime.v1.ResourceName 12, // 25: rill.runtime.v1.ProjectParser.spec:type_name -> rill.runtime.v1.ProjectParserSpec 13, // 26: rill.runtime.v1.ProjectParser.state:type_name -> rill.runtime.v1.ProjectParserState - 75, // 27: rill.runtime.v1.ProjectParserState.parse_errors:type_name -> rill.runtime.v1.ParseError - 95, // 28: rill.runtime.v1.ProjectParserState.current_commit_on:type_name -> google.protobuf.Timestamp + 76, // 27: rill.runtime.v1.ProjectParserState.parse_errors:type_name -> rill.runtime.v1.ParseError + 96, // 28: rill.runtime.v1.ProjectParserState.current_commit_on:type_name -> google.protobuf.Timestamp 15, // 29: rill.runtime.v1.Source.spec:type_name -> rill.runtime.v1.SourceSpec 16, // 30: rill.runtime.v1.Source.state:type_name -> rill.runtime.v1.SourceState - 96, // 31: rill.runtime.v1.SourceSpec.properties:type_name -> google.protobuf.Struct - 74, // 32: rill.runtime.v1.SourceSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule - 95, // 33: rill.runtime.v1.SourceState.refreshed_on:type_name -> google.protobuf.Timestamp + 97, // 31: rill.runtime.v1.SourceSpec.properties:type_name -> google.protobuf.Struct + 75, // 32: rill.runtime.v1.SourceSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule + 96, // 33: rill.runtime.v1.SourceState.refreshed_on:type_name -> google.protobuf.Timestamp 18, // 34: rill.runtime.v1.Model.spec:type_name -> rill.runtime.v1.ModelSpec 19, // 35: rill.runtime.v1.Model.state:type_name -> rill.runtime.v1.ModelState - 74, // 36: rill.runtime.v1.ModelSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule - 96, // 37: rill.runtime.v1.ModelSpec.incremental_state_resolver_properties:type_name -> google.protobuf.Struct - 96, // 38: rill.runtime.v1.ModelSpec.partitions_resolver_properties:type_name -> google.protobuf.Struct - 96, // 39: rill.runtime.v1.ModelSpec.input_properties:type_name -> google.protobuf.Struct - 96, // 40: rill.runtime.v1.ModelSpec.stage_properties:type_name -> google.protobuf.Struct - 96, // 41: rill.runtime.v1.ModelSpec.output_properties:type_name -> google.protobuf.Struct + 75, // 36: rill.runtime.v1.ModelSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule + 97, // 37: rill.runtime.v1.ModelSpec.incremental_state_resolver_properties:type_name -> google.protobuf.Struct + 97, // 38: rill.runtime.v1.ModelSpec.partitions_resolver_properties:type_name -> google.protobuf.Struct + 97, // 39: rill.runtime.v1.ModelSpec.input_properties:type_name -> google.protobuf.Struct + 97, // 40: rill.runtime.v1.ModelSpec.stage_properties:type_name -> google.protobuf.Struct + 97, // 41: rill.runtime.v1.ModelSpec.output_properties:type_name -> google.protobuf.Struct 1, // 42: rill.runtime.v1.ModelSpec.change_mode:type_name -> rill.runtime.v1.ModelChangeMode 20, // 43: rill.runtime.v1.ModelSpec.tests:type_name -> rill.runtime.v1.ModelTest - 96, // 44: rill.runtime.v1.ModelState.result_properties:type_name -> google.protobuf.Struct - 95, // 45: rill.runtime.v1.ModelState.refreshed_on:type_name -> google.protobuf.Timestamp - 96, // 46: rill.runtime.v1.ModelState.incremental_state:type_name -> google.protobuf.Struct - 97, // 47: rill.runtime.v1.ModelState.incremental_state_schema:type_name -> rill.runtime.v1.StructType - 96, // 48: rill.runtime.v1.ModelTest.resolver_properties:type_name -> google.protobuf.Struct + 97, // 44: rill.runtime.v1.ModelState.result_properties:type_name -> google.protobuf.Struct + 96, // 45: rill.runtime.v1.ModelState.refreshed_on:type_name -> google.protobuf.Timestamp + 97, // 46: rill.runtime.v1.ModelState.incremental_state:type_name -> google.protobuf.Struct + 98, // 47: rill.runtime.v1.ModelState.incremental_state_schema:type_name -> rill.runtime.v1.StructType + 97, // 48: rill.runtime.v1.ModelTest.resolver_properties:type_name -> google.protobuf.Struct 22, // 49: rill.runtime.v1.MetricsView.spec:type_name -> rill.runtime.v1.MetricsViewSpec 28, // 50: rill.runtime.v1.MetricsView.state:type_name -> rill.runtime.v1.MetricsViewState - 98, // 51: rill.runtime.v1.MetricsViewSpec.smallest_time_grain:type_name -> rill.runtime.v1.TimeGrain - 83, // 52: rill.runtime.v1.MetricsViewSpec.dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.Dimension - 86, // 53: rill.runtime.v1.MetricsViewSpec.measures:type_name -> rill.runtime.v1.MetricsViewSpec.Measure + 99, // 51: rill.runtime.v1.MetricsViewSpec.smallest_time_grain:type_name -> rill.runtime.v1.TimeGrain + 84, // 52: rill.runtime.v1.MetricsViewSpec.dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.Dimension + 87, // 53: rill.runtime.v1.MetricsViewSpec.measures:type_name -> rill.runtime.v1.MetricsViewSpec.Measure 35, // 54: rill.runtime.v1.MetricsViewSpec.parent_dimensions:type_name -> rill.runtime.v1.FieldSelector 35, // 55: rill.runtime.v1.MetricsViewSpec.parent_measures:type_name -> rill.runtime.v1.FieldSelector - 87, // 56: rill.runtime.v1.MetricsViewSpec.annotations:type_name -> rill.runtime.v1.MetricsViewSpec.Annotation + 88, // 56: rill.runtime.v1.MetricsViewSpec.annotations:type_name -> rill.runtime.v1.MetricsViewSpec.Annotation 23, // 57: rill.runtime.v1.MetricsViewSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule - 89, // 58: rill.runtime.v1.MetricsViewSpec.query_attributes:type_name -> rill.runtime.v1.MetricsViewSpec.QueryAttributesEntry - 88, // 59: rill.runtime.v1.MetricsViewSpec.rollups:type_name -> rill.runtime.v1.MetricsViewSpec.Rollup + 90, // 58: rill.runtime.v1.MetricsViewSpec.query_attributes:type_name -> rill.runtime.v1.MetricsViewSpec.QueryAttributesEntry + 89, // 59: rill.runtime.v1.MetricsViewSpec.rollups:type_name -> rill.runtime.v1.MetricsViewSpec.Rollup 24, // 60: rill.runtime.v1.SecurityRule.access:type_name -> rill.runtime.v1.SecurityRuleAccess 25, // 61: rill.runtime.v1.SecurityRule.field_access:type_name -> rill.runtime.v1.SecurityRuleFieldAccess 26, // 62: rill.runtime.v1.SecurityRule.row_filter:type_name -> rill.runtime.v1.SecurityRuleRowFilter @@ -9715,10 +9864,10 @@ var file_rill_runtime_v1_resources_proto_depIdxs = []int32{ 10, // 64: rill.runtime.v1.SecurityRuleAccess.condition_resources:type_name -> rill.runtime.v1.ResourceName 10, // 65: rill.runtime.v1.SecurityRuleFieldAccess.condition_resources:type_name -> rill.runtime.v1.ResourceName 10, // 66: rill.runtime.v1.SecurityRuleRowFilter.condition_resources:type_name -> rill.runtime.v1.ResourceName - 99, // 67: rill.runtime.v1.SecurityRuleRowFilter.expression:type_name -> rill.runtime.v1.Expression + 100, // 67: rill.runtime.v1.SecurityRuleRowFilter.expression:type_name -> rill.runtime.v1.Expression 10, // 68: rill.runtime.v1.SecurityRuleTransitiveAccess.resource:type_name -> rill.runtime.v1.ResourceName 22, // 69: rill.runtime.v1.MetricsViewState.valid_spec:type_name -> rill.runtime.v1.MetricsViewSpec - 95, // 70: rill.runtime.v1.MetricsViewState.data_refreshed_on:type_name -> google.protobuf.Timestamp + 96, // 70: rill.runtime.v1.MetricsViewState.data_refreshed_on:type_name -> google.protobuf.Timestamp 30, // 71: rill.runtime.v1.Explore.spec:type_name -> rill.runtime.v1.ExploreSpec 31, // 72: rill.runtime.v1.Explore.state:type_name -> rill.runtime.v1.ExploreState 35, // 73: rill.runtime.v1.ExploreSpec.dimensions_selector:type_name -> rill.runtime.v1.FieldSelector @@ -9728,11 +9877,11 @@ var file_rill_runtime_v1_resources_proto_depIdxs = []int32{ 34, // 77: rill.runtime.v1.ExploreSpec.default_preset:type_name -> rill.runtime.v1.ExplorePreset 23, // 78: rill.runtime.v1.ExploreSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule 30, // 79: rill.runtime.v1.ExploreState.valid_spec:type_name -> rill.runtime.v1.ExploreSpec - 95, // 80: rill.runtime.v1.ExploreState.data_refreshed_on:type_name -> google.protobuf.Timestamp + 96, // 80: rill.runtime.v1.ExploreState.data_refreshed_on:type_name -> google.protobuf.Timestamp 33, // 81: rill.runtime.v1.ExploreTimeRange.comparison_time_ranges:type_name -> rill.runtime.v1.ExploreComparisonTimeRange 35, // 82: rill.runtime.v1.ExplorePreset.dimensions_selector:type_name -> rill.runtime.v1.FieldSelector 35, // 83: rill.runtime.v1.ExplorePreset.measures_selector:type_name -> rill.runtime.v1.FieldSelector - 99, // 84: rill.runtime.v1.ExplorePreset.where:type_name -> rill.runtime.v1.Expression + 100, // 84: rill.runtime.v1.ExplorePreset.where:type_name -> rill.runtime.v1.Expression 2, // 85: rill.runtime.v1.ExplorePreset.comparison_mode:type_name -> rill.runtime.v1.ExploreComparisonMode 3, // 86: rill.runtime.v1.ExplorePreset.view:type_name -> rill.runtime.v1.ExploreWebView 4, // 87: rill.runtime.v1.ExplorePreset.explore_sort_type:type_name -> rill.runtime.v1.ExploreSortType @@ -9741,102 +9890,106 @@ var file_rill_runtime_v1_resources_proto_depIdxs = []int32{ 39, // 90: rill.runtime.v1.Migration.state:type_name -> rill.runtime.v1.MigrationState 41, // 91: rill.runtime.v1.Report.spec:type_name -> rill.runtime.v1.ReportSpec 42, // 92: rill.runtime.v1.Report.state:type_name -> rill.runtime.v1.ReportState - 74, // 93: rill.runtime.v1.ReportSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule - 96, // 94: rill.runtime.v1.ReportSpec.resolver_properties:type_name -> google.protobuf.Struct - 100, // 95: rill.runtime.v1.ReportSpec.export_format:type_name -> rill.runtime.v1.ExportFormat + 75, // 93: rill.runtime.v1.ReportSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule + 97, // 94: rill.runtime.v1.ReportSpec.resolver_properties:type_name -> google.protobuf.Struct + 101, // 95: rill.runtime.v1.ReportSpec.export_format:type_name -> rill.runtime.v1.ExportFormat 46, // 96: rill.runtime.v1.ReportSpec.notifiers:type_name -> rill.runtime.v1.Notifier - 90, // 97: rill.runtime.v1.ReportSpec.annotations:type_name -> rill.runtime.v1.ReportSpec.AnnotationsEntry - 95, // 98: rill.runtime.v1.ReportState.next_run_on:type_name -> google.protobuf.Timestamp + 91, // 97: rill.runtime.v1.ReportSpec.annotations:type_name -> rill.runtime.v1.ReportSpec.AnnotationsEntry + 96, // 98: rill.runtime.v1.ReportState.next_run_on:type_name -> google.protobuf.Timestamp 43, // 99: rill.runtime.v1.ReportState.current_execution:type_name -> rill.runtime.v1.ReportExecution 43, // 100: rill.runtime.v1.ReportState.execution_history:type_name -> rill.runtime.v1.ReportExecution - 95, // 101: rill.runtime.v1.ReportExecution.report_time:type_name -> google.protobuf.Timestamp - 95, // 102: rill.runtime.v1.ReportExecution.started_on:type_name -> google.protobuf.Timestamp - 95, // 103: rill.runtime.v1.ReportExecution.finished_on:type_name -> google.protobuf.Timestamp + 96, // 101: rill.runtime.v1.ReportExecution.report_time:type_name -> google.protobuf.Timestamp + 96, // 102: rill.runtime.v1.ReportExecution.started_on:type_name -> google.protobuf.Timestamp + 96, // 103: rill.runtime.v1.ReportExecution.finished_on:type_name -> google.protobuf.Timestamp 45, // 104: rill.runtime.v1.Alert.spec:type_name -> rill.runtime.v1.AlertSpec 47, // 105: rill.runtime.v1.Alert.state:type_name -> rill.runtime.v1.AlertState - 74, // 106: rill.runtime.v1.AlertSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule - 96, // 107: rill.runtime.v1.AlertSpec.resolver_properties:type_name -> google.protobuf.Struct - 96, // 108: rill.runtime.v1.AlertSpec.query_for_attributes:type_name -> google.protobuf.Struct + 75, // 106: rill.runtime.v1.AlertSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule + 97, // 107: rill.runtime.v1.AlertSpec.resolver_properties:type_name -> google.protobuf.Struct + 97, // 108: rill.runtime.v1.AlertSpec.query_for_attributes:type_name -> google.protobuf.Struct 46, // 109: rill.runtime.v1.AlertSpec.notifiers:type_name -> rill.runtime.v1.Notifier - 91, // 110: rill.runtime.v1.AlertSpec.annotations:type_name -> rill.runtime.v1.AlertSpec.AnnotationsEntry - 96, // 111: rill.runtime.v1.Notifier.properties:type_name -> google.protobuf.Struct - 95, // 112: rill.runtime.v1.AlertState.next_run_on:type_name -> google.protobuf.Timestamp + 92, // 110: rill.runtime.v1.AlertSpec.annotations:type_name -> rill.runtime.v1.AlertSpec.AnnotationsEntry + 97, // 111: rill.runtime.v1.Notifier.properties:type_name -> google.protobuf.Struct + 96, // 112: rill.runtime.v1.AlertState.next_run_on:type_name -> google.protobuf.Timestamp 48, // 113: rill.runtime.v1.AlertState.current_execution:type_name -> rill.runtime.v1.AlertExecution 48, // 114: rill.runtime.v1.AlertState.execution_history:type_name -> rill.runtime.v1.AlertExecution 49, // 115: rill.runtime.v1.AlertExecution.result:type_name -> rill.runtime.v1.AssertionResult - 95, // 116: rill.runtime.v1.AlertExecution.execution_time:type_name -> google.protobuf.Timestamp - 95, // 117: rill.runtime.v1.AlertExecution.started_on:type_name -> google.protobuf.Timestamp - 95, // 118: rill.runtime.v1.AlertExecution.finished_on:type_name -> google.protobuf.Timestamp - 95, // 119: rill.runtime.v1.AlertExecution.suppressed_since:type_name -> google.protobuf.Timestamp + 96, // 116: rill.runtime.v1.AlertExecution.execution_time:type_name -> google.protobuf.Timestamp + 96, // 117: rill.runtime.v1.AlertExecution.started_on:type_name -> google.protobuf.Timestamp + 96, // 118: rill.runtime.v1.AlertExecution.finished_on:type_name -> google.protobuf.Timestamp + 96, // 119: rill.runtime.v1.AlertExecution.suppressed_since:type_name -> google.protobuf.Timestamp 5, // 120: rill.runtime.v1.AssertionResult.status:type_name -> rill.runtime.v1.AssertionStatus - 96, // 121: rill.runtime.v1.AssertionResult.fail_row:type_name -> google.protobuf.Struct + 97, // 121: rill.runtime.v1.AssertionResult.fail_row:type_name -> google.protobuf.Struct 51, // 122: rill.runtime.v1.RefreshTrigger.spec:type_name -> rill.runtime.v1.RefreshTriggerSpec 52, // 123: rill.runtime.v1.RefreshTrigger.state:type_name -> rill.runtime.v1.RefreshTriggerState 10, // 124: rill.runtime.v1.RefreshTriggerSpec.resources:type_name -> rill.runtime.v1.ResourceName 53, // 125: rill.runtime.v1.RefreshTriggerSpec.models:type_name -> rill.runtime.v1.RefreshModelTrigger 55, // 126: rill.runtime.v1.Theme.spec:type_name -> rill.runtime.v1.ThemeSpec 56, // 127: rill.runtime.v1.Theme.state:type_name -> rill.runtime.v1.ThemeState - 101, // 128: rill.runtime.v1.ThemeSpec.primary_color:type_name -> rill.runtime.v1.Color - 101, // 129: rill.runtime.v1.ThemeSpec.secondary_color:type_name -> rill.runtime.v1.Color + 102, // 128: rill.runtime.v1.ThemeSpec.primary_color:type_name -> rill.runtime.v1.Color + 102, // 129: rill.runtime.v1.ThemeSpec.secondary_color:type_name -> rill.runtime.v1.Color 57, // 130: rill.runtime.v1.ThemeSpec.light:type_name -> rill.runtime.v1.ThemeColors 57, // 131: rill.runtime.v1.ThemeSpec.dark:type_name -> rill.runtime.v1.ThemeColors - 92, // 132: rill.runtime.v1.ThemeColors.variables:type_name -> rill.runtime.v1.ThemeColors.VariablesEntry + 93, // 132: rill.runtime.v1.ThemeColors.variables:type_name -> rill.runtime.v1.ThemeColors.VariablesEntry 59, // 133: rill.runtime.v1.Component.spec:type_name -> rill.runtime.v1.ComponentSpec 60, // 134: rill.runtime.v1.Component.state:type_name -> rill.runtime.v1.ComponentState - 96, // 135: rill.runtime.v1.ComponentSpec.renderer_properties:type_name -> google.protobuf.Struct - 61, // 136: rill.runtime.v1.ComponentSpec.input:type_name -> rill.runtime.v1.ComponentVariable - 61, // 137: rill.runtime.v1.ComponentSpec.output:type_name -> rill.runtime.v1.ComponentVariable - 59, // 138: rill.runtime.v1.ComponentState.valid_spec:type_name -> rill.runtime.v1.ComponentSpec - 95, // 139: rill.runtime.v1.ComponentState.data_refreshed_on:type_name -> google.protobuf.Timestamp - 102, // 140: rill.runtime.v1.ComponentVariable.default_value:type_name -> google.protobuf.Value - 63, // 141: rill.runtime.v1.Canvas.spec:type_name -> rill.runtime.v1.CanvasSpec - 64, // 142: rill.runtime.v1.Canvas.state:type_name -> rill.runtime.v1.CanvasState - 55, // 143: rill.runtime.v1.CanvasSpec.embedded_theme:type_name -> rill.runtime.v1.ThemeSpec - 32, // 144: rill.runtime.v1.CanvasSpec.time_ranges:type_name -> rill.runtime.v1.ExploreTimeRange - 69, // 145: rill.runtime.v1.CanvasSpec.default_preset:type_name -> rill.runtime.v1.CanvasPreset - 61, // 146: rill.runtime.v1.CanvasSpec.variables:type_name -> rill.runtime.v1.ComponentVariable - 65, // 147: rill.runtime.v1.CanvasSpec.rows:type_name -> rill.runtime.v1.CanvasRow - 23, // 148: rill.runtime.v1.CanvasSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule - 93, // 149: rill.runtime.v1.CanvasSpec.annotations:type_name -> rill.runtime.v1.CanvasSpec.AnnotationsEntry - 63, // 150: rill.runtime.v1.CanvasState.valid_spec:type_name -> rill.runtime.v1.CanvasSpec - 95, // 151: rill.runtime.v1.CanvasState.data_refreshed_on:type_name -> google.protobuf.Timestamp - 68, // 152: rill.runtime.v1.CanvasRow.items:type_name -> rill.runtime.v1.CanvasItem - 66, // 153: rill.runtime.v1.CanvasRow.tab_group:type_name -> rill.runtime.v1.CanvasTabGroup - 67, // 154: rill.runtime.v1.CanvasTabGroup.tabs:type_name -> rill.runtime.v1.CanvasTab - 65, // 155: rill.runtime.v1.CanvasTab.rows:type_name -> rill.runtime.v1.CanvasRow - 2, // 156: rill.runtime.v1.CanvasPreset.comparison_mode:type_name -> rill.runtime.v1.ExploreComparisonMode - 94, // 157: rill.runtime.v1.CanvasPreset.filter_expr:type_name -> rill.runtime.v1.CanvasPreset.FilterExprEntry - 99, // 158: rill.runtime.v1.DefaultMetricsSQLFilter.expression:type_name -> rill.runtime.v1.Expression - 72, // 159: rill.runtime.v1.API.spec:type_name -> rill.runtime.v1.APISpec - 73, // 160: rill.runtime.v1.API.state:type_name -> rill.runtime.v1.APIState - 96, // 161: rill.runtime.v1.APISpec.resolver_properties:type_name -> google.protobuf.Struct - 23, // 162: rill.runtime.v1.APISpec.security_rules:type_name -> rill.runtime.v1.SecurityRule - 79, // 163: rill.runtime.v1.ParseError.start_location:type_name -> rill.runtime.v1.CharLocation - 81, // 164: rill.runtime.v1.ConnectorV2.spec:type_name -> rill.runtime.v1.ConnectorSpec - 82, // 165: rill.runtime.v1.ConnectorV2.state:type_name -> rill.runtime.v1.ConnectorState - 96, // 166: rill.runtime.v1.ConnectorSpec.properties:type_name -> google.protobuf.Struct - 96, // 167: rill.runtime.v1.ConnectorSpec.provision_args:type_name -> google.protobuf.Struct - 6, // 168: rill.runtime.v1.MetricsViewSpec.Dimension.type:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionType - 98, // 169: rill.runtime.v1.MetricsViewSpec.Dimension.smallest_time_grain:type_name -> rill.runtime.v1.TimeGrain - 103, // 170: rill.runtime.v1.MetricsViewSpec.Dimension.data_type:type_name -> rill.runtime.v1.Type - 98, // 171: rill.runtime.v1.MetricsViewSpec.DimensionSelector.time_grain:type_name -> rill.runtime.v1.TimeGrain - 84, // 172: rill.runtime.v1.MetricsViewSpec.MeasureWindow.order_by:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector - 7, // 173: rill.runtime.v1.MetricsViewSpec.Measure.type:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureType - 85, // 174: rill.runtime.v1.MetricsViewSpec.Measure.window:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureWindow - 84, // 175: rill.runtime.v1.MetricsViewSpec.Measure.per_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector - 84, // 176: rill.runtime.v1.MetricsViewSpec.Measure.required_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector - 96, // 177: rill.runtime.v1.MetricsViewSpec.Measure.format_d3_locale:type_name -> google.protobuf.Struct - 103, // 178: rill.runtime.v1.MetricsViewSpec.Measure.data_type:type_name -> rill.runtime.v1.Type - 35, // 179: rill.runtime.v1.MetricsViewSpec.Annotation.measures_selector:type_name -> rill.runtime.v1.FieldSelector - 98, // 180: rill.runtime.v1.MetricsViewSpec.Rollup.time_grain:type_name -> rill.runtime.v1.TimeGrain - 35, // 181: rill.runtime.v1.MetricsViewSpec.Rollup.dimensions_selector:type_name -> rill.runtime.v1.FieldSelector - 35, // 182: rill.runtime.v1.MetricsViewSpec.Rollup.measures_selector:type_name -> rill.runtime.v1.FieldSelector - 70, // 183: rill.runtime.v1.CanvasPreset.FilterExprEntry.value:type_name -> rill.runtime.v1.DefaultMetricsSQLFilter - 184, // [184:184] is the sub-list for method output_type - 184, // [184:184] is the sub-list for method input_type - 184, // [184:184] is the sub-list for extension type_name - 184, // [184:184] is the sub-list for extension extendee - 0, // [0:184] is the sub-list for field type_name + 97, // 135: rill.runtime.v1.ComponentSpec.renderer_properties:type_name -> google.protobuf.Struct + 62, // 136: rill.runtime.v1.ComponentSpec.params:type_name -> rill.runtime.v1.ComponentParam + 61, // 137: rill.runtime.v1.ComponentSpec.input:type_name -> rill.runtime.v1.ComponentVariable + 61, // 138: rill.runtime.v1.ComponentSpec.output:type_name -> rill.runtime.v1.ComponentVariable + 59, // 139: rill.runtime.v1.ComponentState.valid_spec:type_name -> rill.runtime.v1.ComponentSpec + 96, // 140: rill.runtime.v1.ComponentState.data_refreshed_on:type_name -> google.protobuf.Timestamp + 103, // 141: rill.runtime.v1.ComponentVariable.default_value:type_name -> google.protobuf.Value + 103, // 142: rill.runtime.v1.ComponentParam.default:type_name -> google.protobuf.Value + 103, // 143: rill.runtime.v1.ComponentParam.options:type_name -> google.protobuf.Value + 64, // 144: rill.runtime.v1.Canvas.spec:type_name -> rill.runtime.v1.CanvasSpec + 65, // 145: rill.runtime.v1.Canvas.state:type_name -> rill.runtime.v1.CanvasState + 55, // 146: rill.runtime.v1.CanvasSpec.embedded_theme:type_name -> rill.runtime.v1.ThemeSpec + 32, // 147: rill.runtime.v1.CanvasSpec.time_ranges:type_name -> rill.runtime.v1.ExploreTimeRange + 70, // 148: rill.runtime.v1.CanvasSpec.default_preset:type_name -> rill.runtime.v1.CanvasPreset + 61, // 149: rill.runtime.v1.CanvasSpec.variables:type_name -> rill.runtime.v1.ComponentVariable + 66, // 150: rill.runtime.v1.CanvasSpec.rows:type_name -> rill.runtime.v1.CanvasRow + 23, // 151: rill.runtime.v1.CanvasSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule + 94, // 152: rill.runtime.v1.CanvasSpec.annotations:type_name -> rill.runtime.v1.CanvasSpec.AnnotationsEntry + 64, // 153: rill.runtime.v1.CanvasState.valid_spec:type_name -> rill.runtime.v1.CanvasSpec + 96, // 154: rill.runtime.v1.CanvasState.data_refreshed_on:type_name -> google.protobuf.Timestamp + 69, // 155: rill.runtime.v1.CanvasRow.items:type_name -> rill.runtime.v1.CanvasItem + 67, // 156: rill.runtime.v1.CanvasRow.tab_group:type_name -> rill.runtime.v1.CanvasTabGroup + 68, // 157: rill.runtime.v1.CanvasTabGroup.tabs:type_name -> rill.runtime.v1.CanvasTab + 66, // 158: rill.runtime.v1.CanvasTab.rows:type_name -> rill.runtime.v1.CanvasRow + 97, // 159: rill.runtime.v1.CanvasItem.params:type_name -> google.protobuf.Struct + 2, // 160: rill.runtime.v1.CanvasPreset.comparison_mode:type_name -> rill.runtime.v1.ExploreComparisonMode + 95, // 161: rill.runtime.v1.CanvasPreset.filter_expr:type_name -> rill.runtime.v1.CanvasPreset.FilterExprEntry + 100, // 162: rill.runtime.v1.DefaultMetricsSQLFilter.expression:type_name -> rill.runtime.v1.Expression + 73, // 163: rill.runtime.v1.API.spec:type_name -> rill.runtime.v1.APISpec + 74, // 164: rill.runtime.v1.API.state:type_name -> rill.runtime.v1.APIState + 97, // 165: rill.runtime.v1.APISpec.resolver_properties:type_name -> google.protobuf.Struct + 23, // 166: rill.runtime.v1.APISpec.security_rules:type_name -> rill.runtime.v1.SecurityRule + 80, // 167: rill.runtime.v1.ParseError.start_location:type_name -> rill.runtime.v1.CharLocation + 82, // 168: rill.runtime.v1.ConnectorV2.spec:type_name -> rill.runtime.v1.ConnectorSpec + 83, // 169: rill.runtime.v1.ConnectorV2.state:type_name -> rill.runtime.v1.ConnectorState + 97, // 170: rill.runtime.v1.ConnectorSpec.properties:type_name -> google.protobuf.Struct + 97, // 171: rill.runtime.v1.ConnectorSpec.provision_args:type_name -> google.protobuf.Struct + 6, // 172: rill.runtime.v1.MetricsViewSpec.Dimension.type:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionType + 99, // 173: rill.runtime.v1.MetricsViewSpec.Dimension.smallest_time_grain:type_name -> rill.runtime.v1.TimeGrain + 104, // 174: rill.runtime.v1.MetricsViewSpec.Dimension.data_type:type_name -> rill.runtime.v1.Type + 99, // 175: rill.runtime.v1.MetricsViewSpec.DimensionSelector.time_grain:type_name -> rill.runtime.v1.TimeGrain + 85, // 176: rill.runtime.v1.MetricsViewSpec.MeasureWindow.order_by:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector + 7, // 177: rill.runtime.v1.MetricsViewSpec.Measure.type:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureType + 86, // 178: rill.runtime.v1.MetricsViewSpec.Measure.window:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureWindow + 85, // 179: rill.runtime.v1.MetricsViewSpec.Measure.per_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector + 85, // 180: rill.runtime.v1.MetricsViewSpec.Measure.required_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector + 97, // 181: rill.runtime.v1.MetricsViewSpec.Measure.format_d3_locale:type_name -> google.protobuf.Struct + 104, // 182: rill.runtime.v1.MetricsViewSpec.Measure.data_type:type_name -> rill.runtime.v1.Type + 35, // 183: rill.runtime.v1.MetricsViewSpec.Annotation.measures_selector:type_name -> rill.runtime.v1.FieldSelector + 99, // 184: rill.runtime.v1.MetricsViewSpec.Rollup.time_grain:type_name -> rill.runtime.v1.TimeGrain + 35, // 185: rill.runtime.v1.MetricsViewSpec.Rollup.dimensions_selector:type_name -> rill.runtime.v1.FieldSelector + 35, // 186: rill.runtime.v1.MetricsViewSpec.Rollup.measures_selector:type_name -> rill.runtime.v1.FieldSelector + 71, // 187: rill.runtime.v1.CanvasPreset.FilterExprEntry.value:type_name -> rill.runtime.v1.DefaultMetricsSQLFilter + 188, // [188:188] is the sub-list for method output_type + 188, // [188:188] is the sub-list for method input_type + 188, // [188:188] is the sub-list for extension type_name + 188, // [188:188] is the sub-list for extension extendee + 0, // [0:188] is the sub-list for field type_name } func init() { file_rill_runtime_v1_resources_proto_init() } @@ -10499,7 +10652,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[54].Exporter = func(v any, i int) any { - switch v := v.(*Canvas); i { + switch v := v.(*ComponentParam); i { case 0: return &v.state case 1: @@ -10511,7 +10664,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[55].Exporter = func(v any, i int) any { - switch v := v.(*CanvasSpec); i { + switch v := v.(*Canvas); i { case 0: return &v.state case 1: @@ -10523,7 +10676,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[56].Exporter = func(v any, i int) any { - switch v := v.(*CanvasState); i { + switch v := v.(*CanvasSpec); i { case 0: return &v.state case 1: @@ -10535,7 +10688,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[57].Exporter = func(v any, i int) any { - switch v := v.(*CanvasRow); i { + switch v := v.(*CanvasState); i { case 0: return &v.state case 1: @@ -10547,7 +10700,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[58].Exporter = func(v any, i int) any { - switch v := v.(*CanvasTabGroup); i { + switch v := v.(*CanvasRow); i { case 0: return &v.state case 1: @@ -10559,7 +10712,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[59].Exporter = func(v any, i int) any { - switch v := v.(*CanvasTab); i { + switch v := v.(*CanvasTabGroup); i { case 0: return &v.state case 1: @@ -10571,7 +10724,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[60].Exporter = func(v any, i int) any { - switch v := v.(*CanvasItem); i { + switch v := v.(*CanvasTab); i { case 0: return &v.state case 1: @@ -10583,7 +10736,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[61].Exporter = func(v any, i int) any { - switch v := v.(*CanvasPreset); i { + switch v := v.(*CanvasItem); i { case 0: return &v.state case 1: @@ -10595,7 +10748,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[62].Exporter = func(v any, i int) any { - switch v := v.(*DefaultMetricsSQLFilter); i { + switch v := v.(*CanvasPreset); i { case 0: return &v.state case 1: @@ -10607,7 +10760,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[63].Exporter = func(v any, i int) any { - switch v := v.(*API); i { + switch v := v.(*DefaultMetricsSQLFilter); i { case 0: return &v.state case 1: @@ -10619,7 +10772,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[64].Exporter = func(v any, i int) any { - switch v := v.(*APISpec); i { + switch v := v.(*API); i { case 0: return &v.state case 1: @@ -10631,7 +10784,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[65].Exporter = func(v any, i int) any { - switch v := v.(*APIState); i { + switch v := v.(*APISpec); i { case 0: return &v.state case 1: @@ -10643,7 +10796,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[66].Exporter = func(v any, i int) any { - switch v := v.(*Schedule); i { + switch v := v.(*APIState); i { case 0: return &v.state case 1: @@ -10655,7 +10808,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[67].Exporter = func(v any, i int) any { - switch v := v.(*ParseError); i { + switch v := v.(*Schedule); i { case 0: return &v.state case 1: @@ -10667,7 +10820,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[68].Exporter = func(v any, i int) any { - switch v := v.(*ValidationError); i { + switch v := v.(*ParseError); i { case 0: return &v.state case 1: @@ -10679,7 +10832,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[69].Exporter = func(v any, i int) any { - switch v := v.(*DependencyError); i { + switch v := v.(*ValidationError); i { case 0: return &v.state case 1: @@ -10691,7 +10844,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[70].Exporter = func(v any, i int) any { - switch v := v.(*ExecutionError); i { + switch v := v.(*DependencyError); i { case 0: return &v.state case 1: @@ -10703,7 +10856,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[71].Exporter = func(v any, i int) any { - switch v := v.(*CharLocation); i { + switch v := v.(*ExecutionError); i { case 0: return &v.state case 1: @@ -10715,7 +10868,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[72].Exporter = func(v any, i int) any { - switch v := v.(*ConnectorV2); i { + switch v := v.(*CharLocation); i { case 0: return &v.state case 1: @@ -10727,7 +10880,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[73].Exporter = func(v any, i int) any { - switch v := v.(*ConnectorSpec); i { + switch v := v.(*ConnectorV2); i { case 0: return &v.state case 1: @@ -10739,7 +10892,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[74].Exporter = func(v any, i int) any { - switch v := v.(*ConnectorState); i { + switch v := v.(*ConnectorSpec); i { case 0: return &v.state case 1: @@ -10751,7 +10904,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[75].Exporter = func(v any, i int) any { - switch v := v.(*MetricsViewSpec_Dimension); i { + switch v := v.(*ConnectorState); i { case 0: return &v.state case 1: @@ -10763,7 +10916,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[76].Exporter = func(v any, i int) any { - switch v := v.(*MetricsViewSpec_DimensionSelector); i { + switch v := v.(*MetricsViewSpec_Dimension); i { case 0: return &v.state case 1: @@ -10775,7 +10928,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[77].Exporter = func(v any, i int) any { - switch v := v.(*MetricsViewSpec_MeasureWindow); i { + switch v := v.(*MetricsViewSpec_DimensionSelector); i { case 0: return &v.state case 1: @@ -10787,7 +10940,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[78].Exporter = func(v any, i int) any { - switch v := v.(*MetricsViewSpec_Measure); i { + switch v := v.(*MetricsViewSpec_MeasureWindow); i { case 0: return &v.state case 1: @@ -10799,7 +10952,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[79].Exporter = func(v any, i int) any { - switch v := v.(*MetricsViewSpec_Annotation); i { + switch v := v.(*MetricsViewSpec_Measure); i { case 0: return &v.state case 1: @@ -10811,6 +10964,18 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[80].Exporter = func(v any, i int) any { + switch v := v.(*MetricsViewSpec_Annotation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rill_runtime_v1_resources_proto_msgTypes[81].Exporter = func(v any, i int) any { switch v := v.(*MetricsViewSpec_Rollup); i { case 0: return &v.state @@ -10861,16 +11026,16 @@ func file_rill_runtime_v1_resources_proto_init() { (*AlertSpec_QueryForAttributes)(nil), } file_rill_runtime_v1_resources_proto_msgTypes[47].OneofWrappers = []any{} - file_rill_runtime_v1_resources_proto_msgTypes[57].OneofWrappers = []any{} - file_rill_runtime_v1_resources_proto_msgTypes[60].OneofWrappers = []any{} + file_rill_runtime_v1_resources_proto_msgTypes[58].OneofWrappers = []any{} file_rill_runtime_v1_resources_proto_msgTypes[61].OneofWrappers = []any{} + file_rill_runtime_v1_resources_proto_msgTypes[62].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_rill_runtime_v1_resources_proto_rawDesc, NumEnums: 8, - NumMessages: 87, + NumMessages: 88, 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 deb4bc8f013a..f84c5daabcda 100644 --- a/proto/gen/rill/runtime/v1/resources.pb.validate.go +++ b/proto/gen/rill/runtime/v1/resources.pb.validate.go @@ -9604,6 +9604,40 @@ func (m *ComponentSpec) validate(all bool) error { } } + for idx, item := range m.GetParams() { + _, _ = idx, item + + if all { + switch v := interface{}(item).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, ComponentSpecValidationError{ + field: fmt.Sprintf("Params[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, ComponentSpecValidationError{ + field: fmt.Sprintf("Params[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(item).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return ComponentSpecValidationError{ + field: fmt.Sprintf("Params[%v]", idx), + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + for idx, item := range m.GetInput() { _, _ = idx, item @@ -10040,6 +10074,179 @@ var _ interface { ErrorName() string } = ComponentVariableValidationError{} +// Validate checks the field values on ComponentParam with the rules defined in +// the proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *ComponentParam) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on ComponentParam with the rules defined +// in the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in ComponentParamMultiError, +// or nil if none found. +func (m *ComponentParam) ValidateAll() error { + return m.validate(true) +} + +func (m *ComponentParam) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for Name + + // no validation rules for Type + + // no validation rules for Description + + // no validation rules for Required + + if all { + switch v := interface{}(m.GetDefault()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, ComponentParamValidationError{ + field: "Default", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, ComponentParamValidationError{ + field: "Default", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetDefault()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return ComponentParamValidationError{ + field: "Default", + reason: "embedded message failed validation", + cause: err, + } + } + } + + // no validation rules for MetricsViewParam + + for idx, item := range m.GetOptions() { + _, _ = idx, item + + if all { + switch v := interface{}(item).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, ComponentParamValidationError{ + field: fmt.Sprintf("Options[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, ComponentParamValidationError{ + field: fmt.Sprintf("Options[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(item).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return ComponentParamValidationError{ + field: fmt.Sprintf("Options[%v]", idx), + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + + if len(errors) > 0 { + return ComponentParamMultiError(errors) + } + + return nil +} + +// ComponentParamMultiError is an error wrapping multiple validation errors +// returned by ComponentParam.ValidateAll() if the designated constraints +// aren't met. +type ComponentParamMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m ComponentParamMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m ComponentParamMultiError) AllErrors() []error { return m } + +// ComponentParamValidationError is the validation error returned by +// ComponentParam.Validate if the designated constraints aren't met. +type ComponentParamValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e ComponentParamValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e ComponentParamValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e ComponentParamValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e ComponentParamValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e ComponentParamValidationError) ErrorName() string { return "ComponentParamValidationError" } + +// Error satisfies the builtin error interface +func (e ComponentParamValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sComponentParam.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = ComponentParamValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = ComponentParamValidationError{} + // Validate checks the field values on Canvas with the rules defined in the // proto definition for this message. If any rules are violated, the first // error encountered is returned, or nil if there are no violations. @@ -11131,6 +11338,35 @@ func (m *CanvasItem) validate(all bool) error { // no validation rules for DefinedInCanvas + if all { + switch v := interface{}(m.GetParams()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, CanvasItemValidationError{ + field: "Params", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, CanvasItemValidationError{ + field: "Params", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetParams()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return CanvasItemValidationError{ + field: "Params", + reason: "embedded message failed validation", + cause: err, + } + } + } + // no validation rules for WidthUnit if m.Width != nil { diff --git a/proto/gen/rill/runtime/v1/runtime.swagger.yaml b/proto/gen/rill/runtime/v1/runtime.swagger.yaml index 425a0a899042..5de2e681c1f3 100644 --- a/proto/gen/rill/runtime/v1/runtime.swagger.yaml +++ b/proto/gen/rill/runtime/v1/runtime.swagger.yaml @@ -4444,6 +4444,11 @@ definitions: definedInCanvas: type: boolean description: Indicates if the component was defined inline as part of the canvas YAML. + params: + type: object + description: |- + Values bound to the referenced component's declared params. + Only set for items that reference an externally defined component. width: type: integer format: int64 @@ -4969,6 +4974,34 @@ definitions: $ref: '#/definitions/v1ComponentSpec' state: $ref: '#/definitions/v1ComponentState' + v1ComponentParam: + type: object + properties: + name: + type: string + description: Param name. Must be a valid identifier; referenced in templates as {{ .params. }}. + type: + type: string + description: 'Param type. One of: "string", "number", "boolean", "metrics_view", "measure", "dimension", "time_dimension".' + description: + type: string + description: Human-facing description of the param. + required: + type: boolean + description: If true, a canvas item referencing this component must bind a value for the param. + default: + description: Default value used when the param is not bound. Mutually exclusive with required=true. + metricsViewParam: + type: string + description: |- + For "measure", "dimension" and "time_dimension" params: + the name of a sibling param of type "metrics_view" whose bound metrics view the field must belong to. + May be omitted when exactly one "metrics_view" param is declared, in which case the parser fills it in. + options: + type: array + items: {} + description: 'For scalar params: allowed values. Renders as a select input in visual editors.' + description: ComponentParam declares a typed, validated parameter of a component. v1ComponentSpec: type: object properties: @@ -4980,6 +5013,14 @@ definitions: type: string rendererProperties: type: object + params: + type: array + items: + type: object + $ref: '#/definitions/v1ComponentParam' + description: |- + Declared parameters that canvases can bind values to when referencing this component. + Bound values are available in the renderer properties' templating as {{ .params. }}. input: type: array items: @@ -7764,6 +7805,9 @@ definitions: rendererProperties: type: object title: Renderer properties with templating resolved for the provided args + resolvedArgs: + type: object + description: 'The effective args used for resolution: the component''s declared param defaults merged with the provided args.' v1ResolveTemplatedStringResponse: type: object properties: diff --git a/proto/rill/runtime/v1/queries.proto b/proto/rill/runtime/v1/queries.proto index e19fb32c6ba8..cbff554baef6 100644 --- a/proto/rill/runtime/v1/queries.proto +++ b/proto/rill/runtime/v1/queries.proto @@ -1047,6 +1047,8 @@ message ResolveComponentRequest { message ResolveComponentResponse { // Renderer properties with templating resolved for the provided args google.protobuf.Struct renderer_properties = 2; + // The effective args used for resolution: the component's declared param defaults merged with the provided args. + google.protobuf.Struct resolved_args = 3; } message ResolveTemplatedStringRequest { diff --git a/proto/rill/runtime/v1/resources.proto b/proto/rill/runtime/v1/resources.proto index 50e2547384b2..d04d116d1ad4 100644 --- a/proto/rill/runtime/v1/resources.proto +++ b/proto/rill/runtime/v1/resources.proto @@ -826,6 +826,9 @@ message ComponentSpec { string description = 7; string renderer = 4; google.protobuf.Struct renderer_properties = 5; + // Declared parameters that canvases can bind values to when referencing this component. + // Bound values are available in the renderer properties' templating as {{ .params. }}. + repeated ComponentParam params = 10; repeated ComponentVariable input = 8; ComponentVariable output = 9; bool defined_in_canvas = 6; @@ -845,6 +848,26 @@ message ComponentVariable { google.protobuf.Value default_value = 3; } +// ComponentParam declares a typed, validated parameter of a component. +message ComponentParam { + // Param name. Must be a valid identifier; referenced in templates as {{ .params. }}. + string name = 1; + // Param type. One of: "string", "number", "boolean", "metrics_view", "measure", "dimension", "time_dimension". + string type = 2; + // Human-facing description of the param. + string description = 3; + // If true, a canvas item referencing this component must bind a value for the param. + bool required = 4; + // Default value used when the param is not bound. Mutually exclusive with required=true. + google.protobuf.Value default = 5; + // For "measure", "dimension" and "time_dimension" params: + // the name of a sibling param of type "metrics_view" whose bound metrics view the field must belong to. + // May be omitted when exactly one "metrics_view" param is declared, in which case the parser fills it in. + string metrics_view_param = 6; + // For scalar params: allowed values. Renders as a select input in visual editors. + repeated google.protobuf.Value options = 7; +} + message Canvas { CanvasSpec spec = 1; CanvasState state = 2; @@ -937,6 +960,9 @@ message CanvasItem { string component = 1; // Indicates if the component was defined inline as part of the canvas YAML. bool defined_in_canvas = 8; + // Values bound to the referenced component's declared params. + // Only set for items that reference an externally defined component. + google.protobuf.Struct params = 11; // Width of the item. The unit is given in width_unit. optional uint32 width = 9; // Unit of the width. Current possible values: empty string. diff --git a/runtime/ai/develop_file.go b/runtime/ai/develop_file.go index 160a8a6e38d8..a21959f20f17 100644 --- a/runtime/ai/develop_file.go +++ b/runtime/ai/develop_file.go @@ -23,7 +23,7 @@ var _ Tool[*DevelopFileArgs, *DevelopFileResult] = (*DevelopFile)(nil) type DevelopFileArgs struct { Path string `json:"path" jsonschema:"The path of a .yaml or .sql file to create, update or delete."` - Type string `json:"type,omitempty" jsonschema:"Type of Rill file to develop (optional, but recommended if known). Options: rill.yaml, .env, connector, model, metrics_view, explore, canvas, theme, api, alert, report."` + Type string `json:"type,omitempty" jsonschema:"Type of Rill file to develop (optional, but recommended if known). Options: rill.yaml, .env, connector, model, metrics_view, explore, canvas, component, theme, api, alert, report."` Prompt string `json:"prompt" jsonschema:"A detailed description of how to develop the file. Include any relevant details assuming no prior context except the path's current content and status (if any)."` } @@ -78,7 +78,7 @@ func (t *DevelopFile) Handler(ctx context.Context, args *DevelopFileArgs) (*Deve if err != nil { return nil, fmt.Errorf("failed to load developer agent resource-specific system prompt: %w", err) } - case "connector", "model", "metrics_view", "explore", "canvas", "theme": + case "connector", "model", "metrics_view", "explore", "canvas", "component", "theme": resourceInstructions, err = instructions.Load(fmt.Sprintf("resources/%s.md", args.Type), instructions.Options{}) if err != nil { return nil, fmt.Errorf("failed to load developer agent resource-specific system prompt: %w", err) diff --git a/runtime/ai/instructions/data/resources/canvas.md b/runtime/ai/instructions/data/resources/canvas.md index 5f0fbbefbd62..b457810d44a5 100644 --- a/runtime/ai/instructions/data/resources/canvas.md +++ b/runtime/ai/instructions/data/resources/canvas.md @@ -873,6 +873,19 @@ image: Build fully custom visualizations using Metrics SQL queries and Vega-Lite specifications. Use this when the built-in chart types are insufficient and you need complete control over the visualization. +**Prefer a standalone component over an inline block.** Instead of embedding the `custom_chart` block in the canvas, create a `type: component` file under `viz_library/` that declares typed params, and reference it from the canvas item with `component: ` plus a `params:` map. This keeps canvas files small and makes the visualization reusable across dashboards. Note that component files describe charts with a chart spec under `spec` rather than with `vega_spec`, switching to `vega_spec` only when the chart spec cannot express the chart: the format below applies to inline blocks, and to an ejected component with the addition of param templating. See the component resource instructions for the file format, param types, and templating conventions. Use an inline `custom_chart` block only for trivial one-off charts. Example reference: + +```yaml +rows: + - items: + - component: measure_trend + params: + metrics_view: bids_metrics + measure: total_bids + time_dim: __time + width: 8 +``` + Custom charts use `metrics_sql` to query data from metrics views and `vega_spec` to define the Vega-Lite visualization. The data from each query is available in the Vega-Lite spec as named datasets: `query1`, `query2`, etc. #### metrics_sql Query Language diff --git a/runtime/ai/instructions/data/resources/component.md b/runtime/ai/instructions/data/resources/component.md new file mode 100644 index 000000000000..221b287dfb8b --- /dev/null +++ b/runtime/ai/instructions/data/resources/component.md @@ -0,0 +1,305 @@ +--- +description: Detailed instructions and examples for developing component (custom viz) resources in Rill +--- + +# Instructions for developing a component in Rill + +## Introduction + +Components are reusable visualizations ("custom viz") defined as standalone `type: component` files, conventionally under `viz_library/`. A component declares typed **params** and a **renderer**; canvas dashboards reference the component by name and bind values to its params. This makes one visualization reusable across many dashboards with different metrics views, measures, and dimensions. + +Charts are described with a declarative chart spec: you state the chart type and which field goes on which channel, and Rill derives the scales, axes, number formats, sorting, stacking, colors and layout from the data and from the metrics view's semantics. **In a chart spec, do not set axis titles, tick counts, colors, fonts, widths or heights anywhere**; they are computed. Reach for Vega-Lite only when the chart spec cannot express what was asked, and then only as described in "Going beyond the chart spec" below. + +## Anatomy + +A component file's only top-level keys are `type`, `display_name`, `description` (optional), `params`, and one renderer block such as `custom_chart` (which contains `metrics_sql` and `spec`). **Never place `metrics_sql` or `spec` at the top level of the YAML, and never wrap the renderer under a `renderer:` key.** A file without a proper renderer block parses as an empty draft and renders nothing: it reports no error, so a misplaced block is silent. + +```yaml +# viz_library/measure_trend.yaml +type: component +display_name: Measure trend +description: Line chart of any measure over a time dimension. + +params: + - name: metrics_view + type: metrics_view + required: true + - name: measure + type: measure + required: true + - name: time_dim + type: time_dimension + required: true + - name: limit + type: number + default: 500 + +custom_chart: + metrics_sql: | + SELECT {{ .params.time_dim }}, {{ .params.measure }} + FROM {{ .params.metrics_view }} + ORDER BY {{ .params.time_dim }} + LIMIT {{ .params.limit }} + spec: + chartType: Line Chart + encodings: + x: { field: "{{ .params.time_dim }}" } + y: { field: "{{ .params.measure }}" } +``` + +A canvas references it and binds values: + +```yaml +# dashboards/overview.yaml +type: canvas +rows: + - items: + - component: measure_trend + params: + metrics_view: bids_metrics + measure: total_bids + time_dim: __time + width: 8 +``` + +## Params + +Each param has: + +- `name` (required): a valid identifier, referenced in templates as `{{ .params. }}`. The names `title`, `description`, `datum`, `item`, `event`, and `parent` are reserved. +- `type` (required): one of `string`, `number`, `boolean`, `metrics_view`, `measure`, `dimension`, `time_dimension`. +- `required`: if true, every canvas item referencing the component must bind a value. Mutually exclusive with `default`. +- `default`: the value used when the param is not bound. Prefer defaults over `required` when evolving an existing component, so existing dashboards don't break. +- `description`: shown in the visual editor. +- `options`: for scalar params, the allowed values (renders as a select input). +- `metrics_view`: for `measure`/`dimension`/`time_dimension` params, the name of the sibling `metrics_view` param whose bound metrics view the field must belong to. Auto-inferred when exactly one `metrics_view` param is declared. + +Constraints enforced by the parser: + +- Params of type `metrics_view` must be named `metrics_view` or end with `_metrics_view`. +- Every `{{ .params.X }}` reference in the renderer properties must be a declared param. +- Canvas bindings are validated at reconcile time: unknown params, missing required params, type mismatches, and fields that don't exist in the bound metrics view are all errors. + +A value that consists of exactly one `{{ .params.X }}` placeholder keeps the param's type, so a `number` param used as `innerRadius: "{{ .params.inner_radius }}"` arrives as a number, not a string. Partial interpolations such as `LIMIT {{ .params.limit }}` are ordinary strings. + +## Creating a new component + +A new component is written from a chart type and a set of channels, without being told which data to use: + +- Pick the metrics view in the project that best fits the chart's shape, and **bind every field param to a field that metrics view actually declares** — never invent field names, and confirm them by reading the metrics view rather than guessing from the component's own naming. +- **Declare a required `metrics_view` param, plus one required param per channel the chart binds, typed `dimension`, `measure` or `time_dimension`.** Field params are never `string` and never have a default. +- Bind only the channels the chart type calls for; an unused channel left in the spec renders empty. + +## The chart spec + +`spec` is a YAML mapping with three keys: + +- `chartType` (required): one of the chart types listed below. The name must match exactly. +- `encodings` (required): maps a visual channel to a field. Each value is either a field name or a mapping. +- `chartProperties` (optional): per-chart-type presentation tuning. Properties that don't apply to the chart type are ignored. + +**An encoding mapping supports exactly these six keys, and nothing else:** + +| Key | Purpose | +| --- | --- | +| `field` | The column to bind. Usually `"{{ .params. }}"`. | +| `type` | `quantitative`, `nominal`, `ordinal` or `temporal`. Rarely needed: Rill supplies each field's semantics from the metrics view, so the chart already knows measures are quantitative, dimensions categorical, and the time dimension temporal. | +| `aggregate` | `count`, `sum`, `average` or `mean`. Rarely needed: measures selected from a metrics view are already aggregated. | +| `sortOrder` | `ascending` or `descending`. | +| `sortBy` | Channel or field to sort this channel's values by, e.g. `x` or `y`. | +| `scheme` | Color scheme name for the `color` channel, e.g. `viridis`, `tableau10`. | + +The Vega-Lite keys `bin`, `stack`, `timeUnit`, `axis`, `legend`, `format`, `scale`, `mark` and `config` do not exist here. Binning, stacking, time bucketing, axis and legend configuration, and number formatting are all derived; a chart type that needs one applies it itself. + +Common channels: `x`, `y`, `color`, `size`, `shape`, `opacity`, `column` and `row` (small multiples), `group` (dodge/cluster category, distinct from `color`), `detail`, `order`, `angle`, `goal` (bullet target). Which channels a chart type accepts varies; bind only the ones it uses. + +There is no `title` in a chart spec. Set `display_name` on the component instead. + +### Chart types + +Points: `Scatter Plot`, `Regression`, `Connected Scatter Plot`, `Ranged Dot Plot`, `Strip Plot` + +Bars: `Bar Chart`, `Grouped Bar Chart`, `Stacked Bar Chart`, `Lollipop Chart`, `Waterfall Chart`, `Gantt Chart`, `Bullet Chart` + +Distributions: `Histogram`, `Density Plot`, `ECDF Plot`, `Violin Plot`, `Boxplot`, `Pyramid Chart`, `Candlestick Chart` + +Lines and areas: `Line Chart`, `Sparkline`, `Bump Chart`, `Slope Chart`, `Area Chart`, `Streamgraph`, `Range Area Chart` + +Circular: `Pie Chart`, `Donut Chart`, `Rose Chart`, `Radar Chart` + +Tables: `Heatmap`, `Bar Table` + +Single numbers are not a chart type here: a canvas has a native KPI widget for those. + +### Examples + +A ranked bar chart. Metrics SQL picks the top rows; the chart orders the bars: + +```yaml +custom_chart: + metrics_sql: | + SELECT {{ .params.dimension }}, {{ .params.measure }} + FROM {{ .params.metrics_view }} + ORDER BY {{ .params.measure }} DESC + LIMIT {{ .params.limit }} + spec: + chartType: Bar Chart + encodings: + y: { field: "{{ .params.dimension }}", sortBy: x, sortOrder: descending } + x: { field: "{{ .params.measure }}" } +``` + +A donut, with the hole exposed as a param: + +```yaml + spec: + chartType: Pie Chart + encodings: + size: { field: "{{ .params.measure }}" } + color: { field: "{{ .params.dimension }}" } + chartProperties: + innerRadius: "{{ .params.inner_radius }}" +``` + +Small multiples: one panel per value of a dimension: + +```yaml + spec: + chartType: Line Chart + encodings: + column: { field: "{{ .params.facet }}" } + x: { field: "{{ .params.time_dim }}" } + y: { field: "{{ .params.measure }}" } + color: { field: "{{ .params.series }}" } +``` + +## Going beyond the chart spec: ejecting to Vega-Lite + +The chart spec is deliberately narrow: a chart type, six encoding keys, and `chartProperties`. When a +request needs something it cannot reach — a mark it has no chart type for, layered or dual-axis +composition, a data transform, an interaction, or specific control over an axis, legend, scale or +number format — the component **ejects**: `spec` is replaced by `vega_spec`, a Vega-Lite v5 spec +written as a string. The two are mutually exclusive; a component that declares both fails to parse. + +Try the chart spec first. Ejecting is a one-way trade: Rill stops deriving scales, axes, formats, +sorting, stacking and layout, so the chart no longer adapts to its data, and everything the chart +spec used to compute becomes yours to write and maintain. Eject when the request genuinely cannot be +met otherwise, not to gain incidental control. If a listed chart type does what was asked, use it. +Say plainly in your summary that the component was ejected and what it gave up. + +Nothing else about the component changes: keep the `params` and the `metrics_sql` exactly as they +were, so every dashboard already referencing the component keeps working. Only the renderer's chart +description changes. + +### Keeping an ejected component parameterized + +A component is reusable because its bindings are templated, and an ejected spec must stay that way. +Never write a field name, an axis title or a number format as a literal — bind each one back to the +param it came from: + +| What you need | Write | +| --- | --- | +| A field name, anywhere: encodings, transforms, `datum[...]` expressions, tooltips | `{{ .params. }}` | +| A field's label, for an axis, legend or tooltip title | `{{ .fields..display_name }}` | +| A measure's number format | `formatType: "{{ .fields..format_type }}"` | + +`.fields.` exposes the metrics view metadata of a **field-typed** param (`measure`, +`dimension`, `time_dimension`) and is an error on any other param. Its properties are `name` (the +same value as `{{ .params. }}`), `display_name`, `format_d3`, `format_preset` and +`format_type`. A mistyped property is rejected at parse time. + +Prefer `formatType` with `format_type` over a raw `format` string: it routes values through the +formatter Rill registers for that measure, so currency, percent and duration presets render the way +they do everywhere else in the dashboard. Use `format: "{{ .fields..format_d3 }}"` only when +the spec has to be portable outside Rill. + +Scalar params (`string`, `number`, `boolean`) are additionally injected as native Vega-Lite params, +so a spec can reference one as a signal by name in an expression, predicate or extent — in addition +to substituting it textually as `{{ .params. }}`. + +### Vega-Lite rules for an ejected component + +- Write a valid Vega-Lite v5 spec as a YAML block scalar (`vega_spec: |`). +- Read the query result from `{"data": {"name": "query1"}}`. A component issues exactly one query, so + there is never a `query2`. Never inline `{"values": [...]}`. +- Set `"width": "container"`, `"height": "container"` and `"autosize": {"type": "fit"}` at the top + level, so the chart fills its widget instead of overflowing it. `autosize` is not optional: without + it only the width is fitted and axis and legend decorations run past the bottom of the container. +- Set `type` on every encoding (`quantitative`, `nominal`, `ordinal`, `temporal`). Nothing is derived + any more, and an unset type silently renders wrong. +- A time dimension selected plainly by the `metrics_sql` is still bucketed at the dashboard's grain, + so encode it as `"type": "temporal"` and let the query drive the bucket. +- Include a tooltip covering the encoded fields, titled with `display_name` and formatted with + `format_type`. + +```yaml +custom_chart: + metrics_sql: | + SELECT {{ .params.time_dim }}, {{ .params.measure }} + FROM {{ .params.metrics_view }} + ORDER BY {{ .params.time_dim }} + LIMIT {{ .params.limit }} + vega_spec: | + { + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "width": "container", + "height": "container", + "autosize": {"type": "fit"}, + "data": {"name": "query1"}, + "mark": {"type": "line", "interpolate": "monotone", "point": true}, + "encoding": { + "x": { + "field": "{{ .params.time_dim }}", + "type": "temporal", + "title": "{{ .fields.time_dim.display_name }}" + }, + "y": { + "field": "{{ .params.measure }}", + "type": "quantitative", + "title": "{{ .fields.measure.display_name }}", + "axis": {"formatType": "{{ .fields.measure.format_type }}"} + }, + "tooltip": [ + { + "field": "{{ .params.time_dim }}", + "type": "temporal", + "title": "{{ .fields.time_dim.display_name }}" + }, + { + "field": "{{ .params.measure }}", + "type": "quantitative", + "title": "{{ .fields.measure.display_name }}", + "formatType": "{{ .fields.measure.format_type }}" + } + ] + } + } +``` + +The component editor also has an **Eject to Vega-Lite** action, which compiles the current chart spec +and writes the equivalent parameterized Vega-Lite for the user. It is a good starting point for +someone editing by hand, but it is not available to you: write the `vega_spec` directly. + +## metrics_sql rules + +`metrics_sql` queries metrics views as virtual tables, and it is a deliberately restricted dialect — treat it as "pick columns", not general SQL: + +- Keep queries trivial: `SELECT FROM ` with an `ORDER BY` and `LIMIT`. For parameterized components that means `SELECT {{ .params. }}, {{ .params. }} FROM {{ .params.metrics_view }}`. +- Order a time series by its time dimension ascending, and anything else by the main measure descending so the `LIMIT` keeps the rows the chart should show. To let a dashboard choose the sort instead, declare a required `string` param named `order_by` and write `ORDER BY {{ .params.order_by }} DESC`: the visual editor treats `order_by` specially by name, rendering a dropdown of the field values bound to the component's other params and defaulting it to the measure. +- Declare a `number` param named `limit` (not required, with a default) and end the SQL with `LIMIT {{ .params.limit }}` so dashboards can tune the row count. Choose the default from the chart's visual density: around 10 where each row is a large mark (bars, arcs, text), up to 100 for charts that stay readable with many small marks (scatterplots, heatmaps, strip plots), and up to 2000 for time series. Rows beyond what the axis can legibly fit are dropped and reported as a warning, so an oversized limit silently truncates the chart. +- **Every field the `spec` encodings or the `ORDER BY` reference must also appear in the `SELECT` list.** Ordering by an unselected field fails at query time, and encodings reading unselected fields render empty. +- NOT supported: CTEs (`WITH`), JOINs, subqueries, window functions, `CASE` expressions, and aggregate functions. Measures are already aggregated; never wrap them in `SUM()`/`AVG()`/`COUNT()`. Grouping by the selected dimensions is implicit. +- Renaming a dimension or measure with `AS` is rejected: result columns keep the metrics view field names, so reference `{{ .params. }}` in the encodings and `ORDER BY` the field itself, never an alias. +- **Select a time dimension plainly; do not bucket it with `date_trunc`.** A selected time dimension is bucketed automatically at the grain of whatever time controls the chart is placed under, so a plain `SELECT {{ .params. }}` is what lets a dashboard drive the chart. Reach for `date_trunc('', )` only when the chart is meant to stay at one fixed bucket regardless of the dashboard, and then alias it back to the field's own name — `date_trunc('month', {{ .params. }}) AS {{ .params. }}` — because an unaliased `date_trunc` produces a column named after the expression, which no encoding can reference. +- A component issues exactly one query. Charts that need several datasets cannot be expressed. + +If a visualization needs cross-row derivations (running sums, per-group offsets, rankings), they cannot be expressed here: pick a chart type that computes what you need (`Waterfall Chart`, `Bump Chart`, `ECDF Plot`) or simplify the chart to direct field encodings. + +## Tips + +- Give the component a clear `display_name` and `description`; they are shown in the visual editor's widget picker. +- Name params after their role in the chart (`x_axis`, `y_axis`, `color`, `size`, ...) rather than after the underlying column names, and set a `description` on each param. +- **Do not set `type` or `aggregate` on an encoding** unless the derived behaviour is actually wrong; the semantics Rill supplies are usually right, and hardcoding them is what breaks a component when it is rebound to a different metrics view. A dimension typed `quantitative` renders a blank chart. +- New params on an already-used component should be optional or have a default, otherwise existing dashboards break. +- Components are validated leniently while they contain unresolved `{{ .params.* }}` placeholders; the fully-bound instance is validated in each canvas that references it. +- Inline `custom_chart` blocks inside a canvas are a different, Vega-Lite-based widget and always use `vega_spec`; they never accept `spec`. A component file authors `spec` by default and switches to `vega_spec` only when ejected (see "Going beyond the chart spec"). diff --git a/runtime/canvas/component.go b/runtime/canvas/component.go index 090a31f11dd7..18a29d471ca7 100644 --- a/runtime/canvas/component.go +++ b/runtime/canvas/component.go @@ -1,6 +1,7 @@ package canvas import ( + "encoding/json" "errors" "fmt" "strings" @@ -13,49 +14,182 @@ import ( // The provided metricsViews should contain every valid metrics view referenced by the component (as determined in the parser). // If the renderer properties reference a metrics view not in metricsViews, assume the metrics view is invalid or does not exist (don't look it up separately in the catalog). // +// allowTemplated should be true when the component declares params. In that case, properties may contain +// unresolved template placeholders (e.g. {{ .params.measure }}), which makes field-membership validation +// impossible for the templated values; those membership checks are skipped and the bound values are +// validated by the canvas reconciler (ValidateParamBindings) instead. All other checks still run: +// required keys must be present, values must have the right types, and enum-valued and non-templated +// field properties are validated as usual. +// // Note: metrics views referenced through markdown content cannot be validated here. // This is because the upstream parser can't extract refs from templates, so the metrics views cannot be passed through to here. // Warning: if you try to fix this, note that the refs must be added in the parser, not looked up dynamically here; // a dynamic lookup will have a race condition where the metrics view may not have been reconciled yet. -func ValidateRendererProperties(renderer string, props map[string]any, metricsViews map[string]*runtimev1.MetricsViewSpec) error { +func ValidateRendererProperties(renderer string, props map[string]any, metricsViews map[string]*runtimev1.MetricsViewSpec, allowTemplated bool) error { + v := rendererValidator{ + metricsViews: metricsViews, + lenient: allowTemplated && hasTemplatedString(props), + } switch renderer { case "line_chart", "bar_chart", "area_chart", "stacked_bar", "stacked_bar_normalized": - return validateCartesianChart(props, metricsViews) + return v.validateCartesianChart(props) case "donut_chart", "pie_chart": - return validateCircularChart(props, metricsViews) + return v.validateCircularChart(props) case "scatter_plot": - return validateScatterPlot(props, metricsViews) + return v.validateScatterPlot(props) case "funnel_chart": - return validateFunnelChart(props, metricsViews) + return v.validateFunnelChart(props) case "heatmap": - return validateHeatmap(props, metricsViews) + return v.validateHeatmap(props) case "combo_chart": - return validateComboChart(props, metricsViews) + return v.validateComboChart(props) case "markdown": return validateMarkdown(props) case "image": return validateImage(props) case "kpi": - return validateKPI(props, metricsViews) + return v.validateKPI(props) case "kpi_grid": - return validateKPIGrid(props, metricsViews) + return v.validateKPIGrid(props) case "table": - return validateTable(props, metricsViews) + return v.validateTable(props) case "pivot": - return validatePivot(props, metricsViews) + return v.validatePivot(props) case "leaderboard": - return validateLeaderboard(props, metricsViews) + return v.validateLeaderboard(props) case "custom_chart": - // TODO: Implement - return nil + return validateCustomChart(props) default: return fmt.Errorf("unsupported renderer %q", renderer) } } +// rendererValidator holds the context for validating renderer properties. +// When lenient is true, the properties may contain unresolved template placeholders: +// field-membership checks are skipped for templated values (and for all fields when the +// metrics view name itself is templated), while structural checks still run. +type rendererValidator struct { + metricsViews map[string]*runtimev1.MetricsViewSpec + lenient bool +} + +// validateCustomChart validates properties for custom_chart. +// It only rejects malformed values, not incomplete ones: the visual editor persists draft custom +// charts with empty properties, so completeness is enforced at render time instead. +// metrics_sql queries are validated at query time; vega_spec (inline canvas charts) is validated as +// JSON only when it contains no template placeholders; spec (Flint, component files) is checked +// structurally. Which of the two is permitted is enforced at parse time, not here. +func validateCustomChart(props map[string]any) error { + if raw, ok := pathutil.GetPath(props, "metrics_sql"); ok { + switch v := raw.(type) { + case string: + // Nothing to check. + case []any: + for i, e := range v { + if _, ok := e.(string); !ok { + return fmt.Errorf("renderer property 'metrics_sql' entry at index %d must be a string", i) + } + } + default: + return errors.New("renderer property 'metrics_sql' must be a string or an array of strings") + } + } + + vegaSpec, ok, err := getOptionalPathString(props, "vega_spec") + if err != nil { + return err + } + if ok && strings.TrimSpace(vegaSpec) != "" && !strings.Contains(vegaSpec, "{{") { + var m map[string]any + if err := json.Unmarshal([]byte(vegaSpec), &m); err != nil { + return fmt.Errorf("renderer property 'vega_spec' is not a valid JSON object: %w", err) + } + } + + if raw, ok := props["spec"]; ok { + if err := validateFlintSpec(raw); err != nil { + return err + } + } + + return nil +} + +// validateFlintSpec structurally checks a Flint chart spec. It deliberately does not check chartType +// against Flint's registry: the registry lives in the JS package, so the frontend validates that. +func validateFlintSpec(raw any) error { + spec, ok := raw.(map[string]any) + if !ok { + return errors.New("renderer property 'spec' must be a mapping") + } + + if v, ok := spec["chartType"]; ok { + s, ok := v.(string) + if !ok { + return errors.New("renderer property 'spec.chartType' must be a string") + } + if strings.TrimSpace(s) == "" { + return errors.New("renderer property 'spec.chartType' must not be empty") + } + } + + raw, ok = spec["encodings"] + if !ok { + return nil + } + encodings, ok := raw.(map[string]any) + if !ok { + return errors.New("renderer property 'spec.encodings' must be a mapping") + } + for channel, raw := range encodings { + // A channel is either a field-name shorthand, an encoding object, or an array of either. + entries, ok := raw.([]any) + if !ok { + entries = []any{raw} + } + for _, entry := range entries { + switch e := entry.(type) { + case string: + // Field-name shorthand; nothing to check. + case map[string]any: + if v, ok := e["field"]; ok { + if _, ok := v.(string); !ok { + return fmt.Errorf("renderer property 'spec.encodings.%s.field' must be a string", channel) + } + } + default: + return fmt.Errorf("renderer property 'spec.encodings.%s' must be a field name or an encoding mapping", channel) + } + } + } + + return nil +} + +// hasTemplatedString reports whether any string nested in the value contains template placeholders. +func hasTemplatedString(val any) bool { + switch val := val.(type) { + case string: + return strings.Contains(val, "{{") + case map[string]any: + for _, v := range val { + if hasTemplatedString(v) { + return true + } + } + case []any: + for _, v := range val { + if hasTemplatedString(v) { + return true + } + } + } + return false +} + // validateCartesianChart validates properties for line_chart, bar_chart, area_chart, stacked_bar, and stacked_bar_normalized. -func validateCartesianChart(props map[string]any, metricsViews map[string]*runtimev1.MetricsViewSpec) error { - mvn, mv, err := requireMetricsView(props, metricsViews) +func (v rendererValidator) validateCartesianChart(props map[string]any) error { + mvn, mv, err := v.requireMetricsView(props) if err != nil { return err } @@ -64,7 +198,7 @@ func validateCartesianChart(props map[string]any, metricsViews map[string]*runti if !ok { return errors.New("renderer properties must include a string 'x.field' property") } - if !metricsViewHasDimension(mv, xField) { + if !v.hasDimension(mv, xField) { return fmt.Errorf("referenced x.field %q is not a dimension in metrics view %q", xField, mvn) } @@ -72,7 +206,7 @@ func validateCartesianChart(props map[string]any, metricsViews map[string]*runti if !ok { return errors.New("renderer properties must include a string 'y.field' property") } - if !metricsViewHasMeasure(mv, yField) { + if !v.hasMeasure(mv, yField) { return fmt.Errorf("referenced y.field %q is not a measure in metrics view %q", yField, mvn) } @@ -82,18 +216,18 @@ func validateCartesianChart(props map[string]any, metricsViews map[string]*runti return err } for _, f := range yFields { - if !metricsViewHasMeasure(mv, f) { + if !v.hasMeasure(mv, f) { return fmt.Errorf("referenced y.fields value %q is not a measure in metrics view %q", f, mvn) } } // Validate optional color field: can be a plain string (skip) or a map with a "field" key (validate as dimension) - return validateOptionalColorDimensionField(mv, mvn, props) + return v.validateOptionalColorDimensionField(mv, mvn, props) } // validateCircularChart validates properties for donut_chart and pie_chart. -func validateCircularChart(props map[string]any, metricsViews map[string]*runtimev1.MetricsViewSpec) error { - mvn, mv, err := requireMetricsView(props, metricsViews) +func (v rendererValidator) validateCircularChart(props map[string]any) error { + mvn, mv, err := v.requireMetricsView(props) if err != nil { return err } @@ -102,16 +236,16 @@ func validateCircularChart(props map[string]any, metricsViews map[string]*runtim if !ok { return errors.New("renderer properties must include a string 'measure.field' property") } - if !metricsViewHasMeasure(mv, measureField) { + if !v.hasMeasure(mv, measureField) { return fmt.Errorf("referenced measure.field %q is not a measure in metrics view %q", measureField, mvn) } - return validateOptionalDimensionField(mv, mvn, props, "color.field") + return v.validateOptionalDimensionField(mv, mvn, props, "color.field") } // validateScatterPlot validates properties for scatter_plot. -func validateScatterPlot(props map[string]any, metricsViews map[string]*runtimev1.MetricsViewSpec) error { - mvn, mv, err := requireMetricsView(props, metricsViews) +func (v rendererValidator) validateScatterPlot(props map[string]any) error { + mvn, mv, err := v.requireMetricsView(props) if err != nil { return err } @@ -120,7 +254,7 @@ func validateScatterPlot(props map[string]any, metricsViews map[string]*runtimev if !ok { return errors.New("renderer properties must include a string 'x.field' property") } - if !metricsViewHasMeasure(mv, xField) { + if !v.hasMeasure(mv, xField) { return fmt.Errorf("referenced x.field %q is not a measure in metrics view %q", xField, mvn) } @@ -128,30 +262,30 @@ func validateScatterPlot(props map[string]any, metricsViews map[string]*runtimev if !ok { return errors.New("renderer properties must include a string 'y.field' property") } - if !metricsViewHasMeasure(mv, yField) { + if !v.hasMeasure(mv, yField) { return fmt.Errorf("referenced y.field %q is not a measure in metrics view %q", yField, mvn) } - if err := validateOptionalDimensionField(mv, mvn, props, "dimension.field"); err != nil { + if err := v.validateOptionalDimensionField(mv, mvn, props, "dimension.field"); err != nil { return err } - if err := validateOptionalMeasureField(mv, mvn, props, "size.field"); err != nil { + if err := v.validateOptionalMeasureField(mv, mvn, props, "size.field"); err != nil { return err } // Color can be a plain string or a map with a "field" key - return validateOptionalColorDimensionField(mv, mvn, props) + return v.validateOptionalColorDimensionField(mv, mvn, props) } // validateFunnelChart validates properties for funnel_chart. -func validateFunnelChart(props map[string]any, metricsViews map[string]*runtimev1.MetricsViewSpec) error { - mvn, mv, err := requireMetricsView(props, metricsViews) +func (v rendererValidator) validateFunnelChart(props map[string]any) error { + mvn, mv, err := v.requireMetricsView(props) if err != nil { return err } - if err := validateOptionalMeasureField(mv, mvn, props, "measure.field"); err != nil { + if err := v.validateOptionalMeasureField(mv, mvn, props, "measure.field"); err != nil { return err } @@ -161,69 +295,69 @@ func validateFunnelChart(props map[string]any, metricsViews map[string]*runtimev return err } for _, f := range fields { - if !metricsViewHasMeasure(mv, f) { + if !v.hasMeasure(mv, f) { return fmt.Errorf("referenced measure.fields value %q is not a measure in metrics view %q", f, mvn) } } - if err := validateOptionalDimensionField(mv, mvn, props, "stage.field"); err != nil { + if err := v.validateOptionalDimensionField(mv, mvn, props, "stage.field"); err != nil { return err } // Optional enum-valued funnel fields. The renderer falls back to defaults for // unknown values, but we reject typos here so authors get a clear error. - if err := validateOptionalStringEnum(props, "breakdownMode", []string{"dimension", "measures"}); err != nil { + if err := v.validateOptionalStringEnum(props, "breakdownMode", []string{"dimension", "measures"}); err != nil { return err } - if err := validateOptionalStringEnum(props, "mode", []string{"width", "order"}); err != nil { + if err := v.validateOptionalStringEnum(props, "mode", []string{"width", "order"}); err != nil { return err } - if err := validateOptionalStringEnum(props, "color", []string{"stage", "measure", "name", "value"}); err != nil { + if err := v.validateOptionalStringEnum(props, "color", []string{"stage", "measure", "name", "value"}); err != nil { return err } - return validateOptionalStringEnum(props, "percentMode", []string{"top", "previous"}) + return v.validateOptionalStringEnum(props, "percentMode", []string{"top", "previous"}) } // validateHeatmap validates properties for heatmap. -func validateHeatmap(props map[string]any, metricsViews map[string]*runtimev1.MetricsViewSpec) error { - mvn, mv, err := requireMetricsView(props, metricsViews) +func (v rendererValidator) validateHeatmap(props map[string]any) error { + mvn, mv, err := v.requireMetricsView(props) if err != nil { return err } - if err := validateOptionalDimensionField(mv, mvn, props, "x.field"); err != nil { + if err := v.validateOptionalDimensionField(mv, mvn, props, "x.field"); err != nil { return err } - if err := validateOptionalDimensionField(mv, mvn, props, "y.field"); err != nil { + if err := v.validateOptionalDimensionField(mv, mvn, props, "y.field"); err != nil { return err } // Note: for heatmap, color is a measure (not a dimension like other charts) - return validateOptionalMeasureField(mv, mvn, props, "color.field") + return v.validateOptionalMeasureField(mv, mvn, props, "color.field") } // validateComboChart validates properties for combo_chart. -func validateComboChart(props map[string]any, metricsViews map[string]*runtimev1.MetricsViewSpec) error { - mvn, mv, err := requireMetricsView(props, metricsViews) +func (v rendererValidator) validateComboChart(props map[string]any) error { + mvn, mv, err := v.requireMetricsView(props) if err != nil { return err } - if err := validateOptionalDimensionField(mv, mvn, props, "x.field"); err != nil { + if err := v.validateOptionalDimensionField(mv, mvn, props, "x.field"); err != nil { return err } - if err := validateOptionalMeasureField(mv, mvn, props, "y1.field"); err != nil { + if err := v.validateOptionalMeasureField(mv, mvn, props, "y1.field"); err != nil { return err } - if err := validateOptionalMeasureField(mv, mvn, props, "y2.field"); err != nil { + if err := v.validateOptionalMeasureField(mv, mvn, props, "y2.field"); err != nil { return err } // Combo chart color is typically {field: "measures", type: "value"} for dual-axis mode - return validateOptionalColorDimensionField(mv, mvn, props) + return v.validateOptionalColorDimensionField(mv, mvn, props) } // validateMarkdown validates properties for markdown. @@ -245,8 +379,8 @@ func validateImage(props map[string]any) error { } // validateKPI validates properties for kpi. -func validateKPI(props map[string]any, metricsViews map[string]*runtimev1.MetricsViewSpec) error { - mvn, mv, err := requireMetricsView(props, metricsViews) +func (v rendererValidator) validateKPI(props map[string]any) error { + mvn, mv, err := v.requireMetricsView(props) if err != nil { return err } @@ -256,7 +390,7 @@ func validateKPI(props map[string]any, metricsViews map[string]*runtimev1.Metric if !ok { return errors.New("renderer properties for kpi must include a string 'measure' property") } - if !metricsViewHasMeasure(mv, measure) { + if !v.hasMeasure(mv, measure) { return fmt.Errorf("referenced measure %q is not a measure in metrics view %q", measure, mvn) } @@ -264,8 +398,8 @@ func validateKPI(props map[string]any, metricsViews map[string]*runtimev1.Metric } // validateKPIGrid validates properties for kpi_grid. -func validateKPIGrid(props map[string]any, metricsViews map[string]*runtimev1.MetricsViewSpec) error { - mvn, mv, err := requireMetricsView(props, metricsViews) +func (v rendererValidator) validateKPIGrid(props map[string]any) error { + mvn, mv, err := v.requireMetricsView(props) if err != nil { return err } @@ -278,7 +412,7 @@ func validateKPIGrid(props map[string]any, metricsViews map[string]*runtimev1.Me return errors.New("renderer properties for kpi_grid must include a non-empty 'measures' array of strings") } for _, m := range measures { - if !metricsViewHasMeasure(mv, m) { + if !v.hasMeasure(mv, m) { return fmt.Errorf("referenced measures value %q is not a measure in metrics view %q", m, mvn) } } @@ -287,8 +421,8 @@ func validateKPIGrid(props map[string]any, metricsViews map[string]*runtimev1.Me } // validateTable validates properties for table. -func validateTable(props map[string]any, metricsViews map[string]*runtimev1.MetricsViewSpec) error { - mvn, mv, err := requireMetricsView(props, metricsViews) +func (v rendererValidator) validateTable(props map[string]any) error { + mvn, mv, err := v.requireMetricsView(props) if err != nil { return err } @@ -301,7 +435,7 @@ func validateTable(props map[string]any, metricsViews map[string]*runtimev1.Metr return errors.New("renderer properties for table must include a non-empty 'columns' array of strings") } for _, col := range columns { - if !metricsViewHasDimension(mv, col) && !metricsViewHasMeasure(mv, col) && !isEncodedTimeDimension(mv, col) { + if !v.hasDimension(mv, col) && !v.hasMeasure(mv, col) && !isEncodedTimeDimension(mv, col) { return fmt.Errorf("referenced columns value %q is not a dimension or measure in metrics view %q", col, mvn) } } @@ -310,8 +444,8 @@ func validateTable(props map[string]any, metricsViews map[string]*runtimev1.Metr } // validatePivot validates properties for pivot. -func validatePivot(props map[string]any, metricsViews map[string]*runtimev1.MetricsViewSpec) error { - mvn, mv, err := requireMetricsView(props, metricsViews) +func (v rendererValidator) validatePivot(props map[string]any) error { + mvn, mv, err := v.requireMetricsView(props) if err != nil { return err } @@ -334,17 +468,17 @@ func validatePivot(props map[string]any, metricsViews map[string]*runtimev1.Metr } for _, m := range measures { - if !metricsViewHasMeasure(mv, m) { + if !v.hasMeasure(mv, m) { return fmt.Errorf("referenced measures value %q is not a measure in metrics view %q", m, mvn) } } for _, d := range rowDims { - if !metricsViewHasDimension(mv, d) && !isEncodedTimeDimension(mv, d) { + if !v.hasDimension(mv, d) && !isEncodedTimeDimension(mv, d) { return fmt.Errorf("referenced row_dimensions value %q is not a dimension in metrics view %q", d, mvn) } } for _, d := range colDims { - if !metricsViewHasDimension(mv, d) && !isEncodedTimeDimension(mv, d) { + if !v.hasDimension(mv, d) && !isEncodedTimeDimension(mv, d) { return fmt.Errorf("referenced col_dimensions value %q is not a dimension in metrics view %q", d, mvn) } } @@ -353,8 +487,8 @@ func validatePivot(props map[string]any, metricsViews map[string]*runtimev1.Metr } // validateLeaderboard validates properties for leaderboard. -func validateLeaderboard(props map[string]any, metricsViews map[string]*runtimev1.MetricsViewSpec) error { - mvn, mv, err := requireMetricsView(props, metricsViews) +func (v rendererValidator) validateLeaderboard(props map[string]any) error { + mvn, mv, err := v.requireMetricsView(props) if err != nil { return err } @@ -373,12 +507,12 @@ func validateLeaderboard(props map[string]any, metricsViews map[string]*runtimev } for _, m := range measures { - if !metricsViewHasMeasure(mv, m) { + if !v.hasMeasure(mv, m) { return fmt.Errorf("referenced measures value %q is not a measure in metrics view %q", m, mvn) } } for _, d := range dimensions { - if !metricsViewHasDimension(mv, d) { + if !v.hasDimension(mv, d) { return fmt.Errorf("referenced dimensions value %q is not a dimension in metrics view %q", d, mvn) } } @@ -388,20 +522,44 @@ func validateLeaderboard(props map[string]any, metricsViews map[string]*runtimev // requireMetricsView extracts and validates the "metrics_view" property from renderer props. // It returns the metrics view name, spec, and nil error on success. -func requireMetricsView(props map[string]any, metricsViews map[string]*runtimev1.MetricsViewSpec) (string, *runtimev1.MetricsViewSpec, error) { +// In lenient mode, a templated metrics view name returns a nil spec without error; +// the membership helpers skip their checks when the spec is nil. +func (v rendererValidator) requireMetricsView(props map[string]any) (string, *runtimev1.MetricsViewSpec, error) { mvn, ok := pathutil.GetPathString(props, "metrics_view") if !ok { return "", nil, errors.New("renderer properties must include a string 'metrics_view' property") } - mv := metricsViews[mvn] + if v.lenient && strings.Contains(mvn, "{{") { + return mvn, nil, nil + } + mv := v.metricsViews[mvn] if mv == nil { return "", nil, fmt.Errorf("referenced metrics view %q is invalid", mvn) } return mvn, mv, nil } +// hasDimension reports whether the metrics view has a dimension with the given name. +// In lenient mode the check passes when it cannot run: the metrics view is unresolved +// (templated name, mv is nil) or the field name itself is templated. +func (v rendererValidator) hasDimension(mv *runtimev1.MetricsViewSpec, fieldName string) bool { + if v.lenient && (mv == nil || strings.Contains(fieldName, "{{")) { + return true + } + return metricsViewHasDimension(mv, fieldName) +} + +// hasMeasure reports whether the metrics view has a measure with the given name. +// In lenient mode the check passes when it cannot run; see hasDimension. +func (v rendererValidator) hasMeasure(mv *runtimev1.MetricsViewSpec, fieldName string) bool { + if v.lenient && (mv == nil || strings.Contains(fieldName, "{{")) { + return true + } + return metricsViewHasMeasure(mv, fieldName) +} + // validateOptionalDimensionField validates that a field at the given path, if present, is a dimension in the metrics view. -func validateOptionalDimensionField(mv *runtimev1.MetricsViewSpec, mvName string, props map[string]any, path string) error { +func (v rendererValidator) validateOptionalDimensionField(mv *runtimev1.MetricsViewSpec, mvName string, props map[string]any, path string) error { field, ok, err := getOptionalPathString(props, path) if err != nil { return err @@ -409,14 +567,14 @@ func validateOptionalDimensionField(mv *runtimev1.MetricsViewSpec, mvName string if !ok { return nil } - if !metricsViewHasDimension(mv, field) { + if !v.hasDimension(mv, field) { return fmt.Errorf("referenced %s %q is not a dimension in metrics view %q", path, field, mvName) } return nil } // validateOptionalMeasureField validates that a field at the given path, if present, is a measure in the metrics view. -func validateOptionalMeasureField(mv *runtimev1.MetricsViewSpec, mvName string, props map[string]any, path string) error { +func (v rendererValidator) validateOptionalMeasureField(mv *runtimev1.MetricsViewSpec, mvName string, props map[string]any, path string) error { field, ok, err := getOptionalPathString(props, path) if err != nil { return err @@ -424,7 +582,7 @@ func validateOptionalMeasureField(mv *runtimev1.MetricsViewSpec, mvName string, if !ok { return nil } - if !metricsViewHasMeasure(mv, field) { + if !v.hasMeasure(mv, field) { return fmt.Errorf("referenced %s %q is not a measure in metrics view %q", path, field, mvName) } return nil @@ -436,7 +594,7 @@ func validateOptionalMeasureField(mv *runtimev1.MetricsViewSpec, mvName string, // - a map with a "field" key: validate color.field as a dimension // // This pattern is used by cartesian charts, scatter plots, and combo charts. -func validateOptionalColorDimensionField(mv *runtimev1.MetricsViewSpec, mvName string, props map[string]any) error { +func (v rendererValidator) validateOptionalColorDimensionField(mv *runtimev1.MetricsViewSpec, mvName string, props map[string]any) error { raw, ok := pathutil.GetPath(props, "color") if !ok { return nil @@ -450,7 +608,7 @@ func validateOptionalColorDimensionField(mv *runtimev1.MetricsViewSpec, mvName s return nil } // Otherwise validate color.field as a dimension - return validateOptionalDimensionField(mv, mvName, props, "color.field") + return v.validateOptionalDimensionField(mv, mvName, props, "color.field") } // getPathStringSlice extracts a []string from a nested path in the props map. @@ -493,7 +651,8 @@ func getOptionalPathString(props map[string]any, path string) (string, bool, err // validateOptionalStringEnum validates that an optional string field at the given // path, if present, equals one of the allowed values. -func validateOptionalStringEnum(props map[string]any, path string, allowed []string) error { +// In lenient mode, templated values are skipped since they only resolve at render time. +func (v rendererValidator) validateOptionalStringEnum(props map[string]any, path string, allowed []string) error { value, ok, err := getOptionalPathString(props, path) if err != nil { return err @@ -501,6 +660,9 @@ func validateOptionalStringEnum(props map[string]any, path string, allowed []str if !ok { return nil } + if v.lenient && strings.Contains(value, "{{") { + return nil + } for _, a := range allowed { if value == a { return nil diff --git a/runtime/canvas/component_test.go b/runtime/canvas/component_test.go index 91e21d1e3109..cc7dff7891d9 100644 --- a/runtime/canvas/component_test.go +++ b/runtime/canvas/component_test.go @@ -966,3 +966,297 @@ leaderboard: testruntime.RequireReconcileState(t, rt, id, 4, 1, 0) testruntime.RequireReconcileErrorContains(t, rt, id, runtime.ResourceKindComponent, "c1", "is not a dimension") } + +func TestValidateCustomChart(t *testing.T) { + rt, id := testruntime.NewInstanceWithOptions(t, testruntime.InstanceOptions{ + Files: metricsViewFiles(), + }) + + // Valid: static metrics_sql string and a Flint spec. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: component +custom_chart: + metrics_sql: SELECT foo, y FROM mv1 + spec: + chartType: Bar Chart + encodings: + x: {field: foo} + y: {field: y} +`}) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 4, 0, 0) + + // Valid: channels may bind a bare field name instead of an encoding mapping. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: component +custom_chart: + metrics_sql: SELECT foo, y FROM mv1 + spec: + chartType: Bar Chart + encodings: + x: foo + y: [y] +`}) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 4, 0, 0) + + // Valid: incomplete drafts are allowed; the visual editor persists custom charts with + // missing or empty properties while the user is still building them. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: component +custom_chart: + metrics_sql: SELECT foo, y FROM mv1 +`}) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 4, 0, 0) + + // Invalid: metrics_sql of the wrong type. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: component +custom_chart: + metrics_sql: 42 + spec: + chartType: Bar Chart +`}) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 4, 1, 0) + testruntime.RequireReconcileErrorContains(t, rt, id, runtime.ResourceKindComponent, "c1", "must be a string or an array of strings") + + // Valid: an ejected component draws a Vega-Lite spec instead of a chart spec. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: component +custom_chart: + metrics_sql: SELECT foo, y FROM mv1 + vega_spec: '{"mark": "bar"}' +`}) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 4, 0, 0) + + // Invalid: a component draws one or the other, not both. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: component +custom_chart: + metrics_sql: SELECT foo, y FROM mv1 + vega_spec: '{"mark": "bar"}' + spec: + chartType: Bar Chart +`}) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 3, 1, 1) + testruntime.RequireParseErrors(t, rt, id, map[string]string{ + "/c1.yaml": `"spec" and "vega_spec" are mutually exclusive`, + }) + + // Invalid: vega_spec is a JSON string, not a mapping. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: component +custom_chart: + metrics_sql: SELECT foo, y FROM mv1 + vega_spec: + mark: bar +`}) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 3, 1, 1) + testruntime.RequireParseErrors(t, rt, id, map[string]string{ + "/c1.yaml": `"vega_spec" must be a string`, + }) + + // Invalid: spec must be a mapping, not a JSON string. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: component +custom_chart: + metrics_sql: SELECT foo, y FROM mv1 + spec: '{"chartType": "Bar Chart"}' +`}) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 3, 1, 1) + testruntime.RequireParseErrors(t, rt, id, map[string]string{ + "/c1.yaml": `"spec" must be a mapping`, + }) + + // Invalid: empty chartType. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: component +custom_chart: + metrics_sql: SELECT foo, y FROM mv1 + spec: + chartType: "" +`}) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 4, 1, 0) + testruntime.RequireReconcileErrorContains(t, rt, id, runtime.ResourceKindComponent, "c1", "'spec.chartType' must not be empty") + + // Invalid: an encoding channel that is neither a field name nor an encoding mapping. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: component +custom_chart: + metrics_sql: SELECT foo, y FROM mv1 + spec: + chartType: Bar Chart + encodings: + x: 42 +`}) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 4, 1, 0) + testruntime.RequireReconcileErrorContains(t, rt, id, runtime.ResourceKindComponent, "c1", "must be a field name or an encoding mapping") + + // Valid: a parameterized component with templated properties reconciles. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: component +params: + - name: metrics_view + type: metrics_view + required: true + - name: measure + type: measure + required: true +custom_chart: + metrics_sql: SELECT foo, {{ .params.measure }} AS value FROM {{ .params.metrics_view }} + spec: + chartType: Bar Chart + encodings: + x: {field: foo} + y: {field: "{{ .params.measure }}"} +`}) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 4, 0, 0) + + // Invalid: a parameterized component still gets structural checks despite templated properties. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: component +params: + - name: metrics_view + type: metrics_view + required: true +custom_chart: + metrics_sql: 42 + spec: + chartType: Bar Chart + encodings: + x: {field: "{{ .params.metrics_view }}"} +`}) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 4, 1, 0) + testruntime.RequireReconcileErrorContains(t, rt, id, runtime.ResourceKindComponent, "c1", "must be a string or an array of strings") +} + +func TestValidateParameterizedRenderer(t *testing.T) { + rt, id := testruntime.NewInstanceWithOptions(t, testruntime.InstanceOptions{ + Files: metricsViewFiles(), + }) + + // Valid: all fields templated; membership checks are deferred to param binding. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: component +params: + - name: metrics_view + type: metrics_view + required: true + - name: measure + type: measure + required: true +kpi: + metrics_view: "{{ .params.metrics_view }}" + measure: "{{ .params.measure }}" +`}) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 4, 0, 0) + + // Invalid: templated properties do not exempt the component from structural checks; + // the required 'measure' property is missing. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: component +params: + - name: metrics_view + type: metrics_view + required: true +kpi: + metrics_view: "{{ .params.metrics_view }}" +`}) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 4, 1, 0) + testruntime.RequireReconcileErrorContains(t, rt, id, runtime.ResourceKindComponent, "c1", "'measure' property") + + // Valid: static metrics view with a templated field; the static x.field is still validated. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: component +params: + - name: measure + type: measure + required: true + metrics_view: metrics_view + - name: metrics_view + type: metrics_view + default: mv1 +line_chart: + metrics_view: mv1 + x: + field: foo + y: + field: "{{ .params.measure }}" +`}) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 4, 0, 0) + + // Invalid: static x.field is a measure, caught even though other fields are templated. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: component +params: + - name: measure + type: measure + required: true + metrics_view: metrics_view + - name: metrics_view + type: metrics_view + default: mv1 +line_chart: + metrics_view: mv1 + x: + field: y + y: + field: "{{ .params.measure }}" +`}) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 4, 1, 0) + testruntime.RequireReconcileErrorContains(t, rt, id, runtime.ResourceKindComponent, "c1", "is not a dimension") +} + +func TestValidateUnknownRendererWithParams(t *testing.T) { + rt, id := testruntime.NewInstanceWithOptions(t, testruntime.InstanceOptions{ + Files: metricsViewFiles(), + }) + + // A parameterized component skips field validation, but an unknown renderer + // (e.g. an erroneous `renderer:` wrapper around the real block) must still fail. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: component +params: + - name: metrics_view + type: metrics_view + required: true +renderer: + custom_chart: + metrics_sql: SELECT foo, y FROM {{ .params.metrics_view }} + vega_spec: '{"mark": "bar"}' +`}) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 4, 1, 0) + testruntime.RequireReconcileErrorContains(t, rt, id, runtime.ResourceKindComponent, "c1", `unsupported renderer "renderer"`) +} diff --git a/runtime/canvas/fields.go b/runtime/canvas/fields.go new file mode 100644 index 000000000000..72210ecd8790 --- /dev/null +++ b/runtime/canvas/fields.go @@ -0,0 +1,141 @@ +package canvas + +import ( + "strconv" + "strings" + + runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1" +) + +// timeDimensionDisplayName labels the metrics view's primary time dimension. +// A metrics view carries no meaningful display name for its timestamp column, +// so charts would otherwise label axes and tooltips with the raw column name. +// Keep in sync with TIME_DISPLAY_NAME in web-common/src/features/custom-viz/flint/semantic-types.ts. +const timeDimensionDisplayName = "Time" + +// FieldTemplateData builds the ".fields" template namespace: the metrics view metadata of every +// field-typed param, keyed by param name. +// +// Params substitute a field's *name* through ".params.", which is all a query needs. +// A hand-authored Vega-Lite spec also needs the field's presentation metadata (what to title an +// axis, how to format a value), and that lives in the metrics view rather than in the binding. +// Exposing it per param rather than per field name keeps the lookup unambiguous when a component +// declares several metrics_view params: each field-typed param names the one it resolves against. +// +// The exposed keys are: +// +// name: the bound field name, same as .params. +// display_name: the field's display name ("Time" for the primary time dimension) +// type: the param's declared type ("measure", "dimension" or "time_dimension") +// format_d3: the measure's d3 format string, if it declares one +// format_preset: the measure's format preset, if it declares one +// format_type: the name of the Vega expression formatter Rill registers for the measure, +// for use as "formatType" in a vega_spec +// +// Every field-typed param gets an entry, even one whose metrics view or field cannot be resolved: +// its keys are then empty strings, so a reference degrades to nothing rather than to Go's +// "" placeholder, which would reach the renderer as a field or formatter name. +func FieldTemplateData(params []*runtimev1.ComponentParam, args map[string]any, metricsViews map[string]*runtimev1.MetricsViewSpec) map[string]any { + res := make(map[string]any) + for _, p := range params { + switch p.Type { + case "measure", "dimension", "time_dimension": + default: + continue + } + + field, ok := args[p.Name].(string) + if !ok || isTemplated(field) { + field = "" + } + + var mv *runtimev1.MetricsViewSpec + for _, mvp := range params { + if mvp.Name != p.MetricsViewParam { + continue + } + if name, ok := args[mvp.Name].(string); ok { + mv = metricsViews[name] + } + break + } + + res[p.Name] = fieldMetadata(mv, p.Type, field) + } + return res +} + +// FieldMetadataKeys are the keys ".fields." exposes. +// The parser validates references against them, since a mistyped key resolves to Go's +// "" placeholder rather than to an error. +var FieldMetadataKeys = []string{"name", "display_name", "type", "format_d3", "format_preset", "format_type"} + +// fieldMetadata resolves one field's metadata from the metrics view it belongs to. +func fieldMetadata(mv *runtimev1.MetricsViewSpec, paramType, field string) map[string]any { + res := map[string]any{ + "name": field, + "display_name": field, + "type": paramType, + "format_d3": "", + "format_preset": "", + "format_type": "", + } + if mv == nil || field == "" { + return res + } + + for _, m := range mv.Measures { + if m.Name != field { + continue + } + if m.DisplayName != "" { + res["display_name"] = m.DisplayName + } + res["format_d3"] = m.FormatD3 + res["format_preset"] = m.FormatPreset + res["format_type"] = VegaFormatType(field) + return res + } + + // The primary time dimension is not always present in the dimensions list, and the display name + // it does carry defaults to the column name, so it is replaced rather than used. + if mv.TimeDimension == field { + res["display_name"] = timeDimensionDisplayName + return res + } + + for _, d := range mv.Dimensions { + if d.Name == field { + if d.DisplayName != "" { + res["display_name"] = d.DisplayName + } + return res + } + } + + return res +} + +// VegaFormatType returns the name of the Vega expression function that Rill registers to format +// values of the given measure. Vega-Lite compiles a custom "formatType" into an expression function +// call, so the name is reduced to a JavaScript identifier-safe subset and measure names with spaces +// or operators stay usable. +// +// Keep in sync with sanitizeFieldName in web-common/src/components/vega/util.ts. +func VegaFormatType(measure string) string { + var b strings.Builder + for _, r := range measure { + switch { + case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', r >= '0' && r <= '9', r == '_', r == '$': + b.WriteRune(r) + default: + b.WriteString("_u") + b.WriteString(strings.ToLower(strconv.FormatInt(int64(r), 16))) + b.WriteString("_") + } + } + if b.Len() == 0 { + return "rill_field" + } + return "rill_" + b.String() +} diff --git a/runtime/canvas/fields_test.go b/runtime/canvas/fields_test.go new file mode 100644 index 000000000000..401541b394fb --- /dev/null +++ b/runtime/canvas/fields_test.go @@ -0,0 +1,115 @@ +package canvas + +import ( + "testing" + + runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1" + "github.com/stretchr/testify/require" +) + +func TestFieldTemplateData(t *testing.T) { + mv := &runtimev1.MetricsViewSpec{ + TimeDimension: "timestamp", + Measures: []*runtimev1.MetricsViewSpec_Measure{ + {Name: "bid price sum", DisplayName: "Sum of Bid Price", FormatPreset: "currency_usd"}, + {Name: "total_records", DisplayName: "Total records", FormatD3: ",.0f"}, + }, + Dimensions: []*runtimev1.MetricsViewSpec_Dimension{ + {Name: "publisher", DisplayName: "Publisher"}, + {Name: "created_at", DisplayName: "Created At", Type: runtimev1.MetricsViewSpec_DIMENSION_TYPE_TIME}, + }, + } + metricsViews := map[string]*runtimev1.MetricsViewSpec{"mv1": mv} + + params := []*runtimev1.ComponentParam{ + {Name: "metrics_view", Type: "metrics_view"}, + {Name: "measure", Type: "measure", MetricsViewParam: "metrics_view"}, + {Name: "dim", Type: "dimension", MetricsViewParam: "metrics_view"}, + {Name: "time_dim", Type: "time_dimension", MetricsViewParam: "metrics_view"}, + {Name: "limit", Type: "number"}, + } + + fields := FieldTemplateData(params, map[string]any{ + "metrics_view": "mv1", + "measure": "bid price sum", + "dim": "publisher", + "time_dim": "timestamp", + "limit": 100, + }, metricsViews) + + require.Equal(t, map[string]any{ + "name": "bid price sum", + "display_name": "Sum of Bid Price", + "type": "measure", + "format_d3": "", + "format_preset": "currency_usd", + // The measure name is reduced to a Vega identifier: " " is U+0020. + "format_type": "rill_bid_u20_price_u20_sum", + }, fields["measure"]) + + require.Equal(t, "Publisher", fields["dim"].(map[string]any)["display_name"]) + // The primary time dimension is labelled "Time" rather than by its column name. + require.Equal(t, "Time", fields["time_dim"].(map[string]any)["display_name"]) + // Only field-typed params carry metrics view metadata. + require.NotContains(t, fields, "limit") + require.NotContains(t, fields, "metrics_view") + + t.Run("secondary time dimension keeps its display name", func(t *testing.T) { + fields := FieldTemplateData(params, map[string]any{ + "metrics_view": "mv1", + "time_dim": "created_at", + }, metricsViews) + require.Equal(t, "Created At", fields["time_dim"].(map[string]any)["display_name"]) + }) + + t.Run("unresolvable bindings blank out rather than going missing", func(t *testing.T) { + // A reference to a missing key resolves to Go's "" placeholder, which would then + // reach the renderer as a field or formatter name, so every field-typed param gets an entry. + fields := FieldTemplateData(params, map[string]any{ + // No metrics view bound, so no field metadata can be resolved. + "measure": "total_records", + // Templated and unbound values resolve at render time, not here. + "dim": "{{ .env.dim }}", + }, metricsViews) + + require.Equal(t, map[string]any{ + "name": "total_records", + "display_name": "total_records", + "type": "measure", + "format_d3": "", + "format_preset": "", + "format_type": "", + }, fields["measure"]) + require.Equal(t, map[string]any{ + "name": "", + "display_name": "", + "type": "dimension", + "format_d3": "", + "format_preset": "", + "format_type": "", + }, fields["dim"]) + // Unbound entirely, and still present. + require.Contains(t, fields, "time_dim") + + for name, meta := range fields { + for _, key := range FieldMetadataKeys { + require.Contains(t, meta, key, "param %q is missing %q", name, key) + } + } + }) + + t.Run("unknown field falls back to its name", func(t *testing.T) { + fields := FieldTemplateData(params, map[string]any{ + "metrics_view": "mv1", + "dim": "not_a_dimension", + }, metricsViews) + require.Equal(t, "not_a_dimension", fields["dim"].(map[string]any)["display_name"]) + }) +} + +func TestVegaFormatType(t *testing.T) { + require.Equal(t, "rill_total_records", VegaFormatType("total_records")) + require.Equal(t, "rill_bid_u20_price", VegaFormatType("bid price")) + require.Equal(t, "rill_a_u25_b", VegaFormatType("a%b")) + require.Equal(t, "rill_field", VegaFormatType("")) +} diff --git a/runtime/canvas/params.go b/runtime/canvas/params.go new file mode 100644 index 000000000000..2c82dd022d1a --- /dev/null +++ b/runtime/canvas/params.go @@ -0,0 +1,309 @@ +package canvas + +import ( + "encoding/json" + "fmt" + "regexp" + "strings" + + runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/structpb" +) + +// EffectiveArgs merges a component's declared param defaults with the provided args. +// Provided args take precedence over declared param defaults, +// which in turn take precedence over legacy input variable defaults. +func EffectiveArgs(spec *runtimev1.ComponentSpec, args map[string]any) map[string]any { + res := make(map[string]any, len(args)+len(spec.Params)+len(spec.Input)) + for _, v := range spec.Input { + if v.DefaultValue != nil { + res[v.Name] = v.DefaultValue.AsInterface() + } + } + for _, p := range spec.Params { + if p.Default != nil { + res[p.Name] = p.Default.AsInterface() + } + } + for k, v := range args { + res[k] = v + } + return res +} + +// ValidateParamBindings validates values bound to a component's declared params. +// It checks unknown keys, missing required params, scalar types and option membership. +// When metricsViews is non-nil, it additionally checks that params of type "metrics_view" name a valid metrics view +// and that field-typed params reference fields of their bound metrics view. +// The provided metricsViews should contain every valid metrics view bound to a param (as determined by refs in the parser); +// a bound metrics view missing from the map is reported as invalid (don't look it up separately in the catalog). +// Pass nil metricsViews to skip the metrics view checks (e.g. at resolve time, where they already ran at reconcile time). +// +// Bound string values containing template placeholders (e.g. {{ .env.name }}) are not validated; +// they resolve at render time. +func ValidateParamBindings(params []*runtimev1.ComponentParam, bound map[string]any, metricsViews map[string]*runtimev1.MetricsViewSpec) error { + declared := make(map[string]*runtimev1.ComponentParam, len(params)) + for _, p := range params { + declared[p.Name] = p + } + + for k := range bound { + if declared[k] == nil { + return fmt.Errorf("unknown param %q: it is not declared by the component", k) + } + } + + for _, p := range params { + v, err := effectiveParamValue(p, bound) + if err != nil { + return err + } + if v == nil { + // Optional and unbound with no default. + continue + } + if isTemplated(v) { + continue + } + + if err := validateParamValueType(p, v); err != nil { + return err + } + + if len(p.Options) > 0 { + val, err := structpb.NewValue(v) + if err != nil { + return fmt.Errorf("invalid value for param %q: %w", p.Name, err) + } + found := false + for _, opt := range p.Options { + if proto.Equal(val, opt) { + found = true + break + } + } + if !found { + return fmt.Errorf("value %v for param %q is not one of its options", v, p.Name) + } + } + + if metricsViews == nil { + continue + } + + switch p.Type { + case "metrics_view": + if metricsViews[v.(string)] == nil { + return fmt.Errorf("metrics view %q bound to param %q is invalid or does not exist", v, p.Name) + } + case "measure", "dimension", "time_dimension": + mvn, err := effectiveParamValue(declared[p.MetricsViewParam], bound) + if err != nil || mvn == nil || isTemplated(mvn) { + // The metrics view param is itself invalid or unresolvable; it reports its own error. + continue + } + mv := metricsViews[mvn.(string)] + if mv == nil { + continue + } + field := v.(string) + switch p.Type { + case "measure": + if !metricsViewHasMeasure(mv, field) { + return fmt.Errorf("value %q for param %q is not a measure in metrics view %q", field, p.Name, mvn) + } + case "dimension": + if !metricsViewHasDimension(mv, field) { + return fmt.Errorf("value %q for param %q is not a dimension in metrics view %q", field, p.Name, mvn) + } + case "time_dimension": + if !metricsViewHasTimeDimension(mv, field) { + return fmt.Errorf("value %q for param %q is not a time dimension in metrics view %q", field, p.Name, mvn) + } + } + } + } + + return nil +} + +// InjectVegaParams injects the values of a component's scalar params as native Vega-Lite params +// into a Vega-Lite spec (a JSON object), making them usable in Vega expressions, predicates and extents. +// If the spec already declares a top-level param with the same name, only its "value" is overridden +// so author-supplied properties like "bind" and "expr" are preserved. +// Field-typed params are not injected since Vega-Lite cannot parameterize field names; +// they are substituted through templating instead. +// Returns the spec unchanged if there is nothing to inject. +func InjectVegaParams(vegaSpec string, params []*runtimev1.ComponentParam, args map[string]any) (string, error) { + var names []string + values := make(map[string]any) + for _, p := range params { + switch p.Type { + case "string", "number", "boolean": + default: + continue + } + v, ok := args[p.Name] + if !ok || v == nil || isTemplated(v) { + continue + } + names = append(names, p.Name) + values[p.Name] = v + } + if len(names) == 0 || strings.TrimSpace(vegaSpec) == "" { + return vegaSpec, nil + } + + var spec map[string]any + if err := json.Unmarshal([]byte(vegaSpec), &spec); err != nil { + return "", fmt.Errorf("vega_spec is not a valid JSON object: %w", err) + } + + existing, _ := spec["params"].([]any) + for _, name := range names { + found := false + for _, e := range existing { + if m, ok := e.(map[string]any); ok && m["name"] == name { + m["value"] = values[name] + found = true + break + } + } + if !found { + existing = append(existing, map[string]any{"name": name, "value": values[name]}) + } + } + spec["params"] = existing + + out, err := json.Marshal(spec) + if err != nil { + return "", err + } + return string(out), nil +} + +// wholeStringParamRef matches a string consisting of exactly one param placeholder and nothing else. +var wholeStringParamRef = regexp.MustCompile(`^\s*\{\{\s*\.(?:params|args)\.(\w+)\s*\}\}\s*$`) + +// CoerceScalarParams substitutes numeric and boolean param values into renderer properties +// with their original type, rather than as strings. +// +// Template resolution always yields strings, so a structured renderer property such as a Flint +// spec's "chartProperties: {innerRadius: {{ .params.inner_radius }}}" would otherwise reach the +// renderer as "60" instead of 60 and silently degrade. This only applies to values that consist of +// exactly one placeholder: partial interpolations like "LIMIT {{ .params.limit }}" are strings by +// nature and are left to normal templating. +// +// It returns a copy; the input is not modified. +func CoerceScalarParams(props map[string]any, params []*runtimev1.ComponentParam, args map[string]any) map[string]any { + values := make(map[string]any) + for _, p := range params { + switch p.Type { + case "number", "boolean": + default: + continue + } + v, ok := args[p.Name] + if !ok || v == nil || isTemplated(v) { + continue + } + values[p.Name] = v + } + if len(values) == 0 { + return props + } + + res, _ := coerceScalarParams(props, values).(map[string]any) + return res +} + +func coerceScalarParams(val any, values map[string]any) any { + switch val := val.(type) { + case string: + m := wholeStringParamRef.FindStringSubmatch(val) + if m == nil { + return val + } + v, ok := values[m[1]] + if !ok { + return val + } + return v + case map[string]any: + res := make(map[string]any, len(val)) + for k, v := range val { + res[k] = coerceScalarParams(v, values) + } + return res + case []any: + res := make([]any, len(val)) + for i, v := range val { + res[i] = coerceScalarParams(v, values) + } + return res + default: + return val + } +} + +// effectiveParamValue returns the value bound to a param, falling back to its default. +// It returns nil if the param is unbound and has no default, or an error if it is required and unbound. +func effectiveParamValue(p *runtimev1.ComponentParam, bound map[string]any) (any, error) { + if p == nil { + return nil, nil + } + if v, ok := bound[p.Name]; ok && v != nil { + return v, nil + } + if p.Default != nil { + return p.Default.AsInterface(), nil + } + if p.Required { + return nil, fmt.Errorf("missing value for required param %q", p.Name) + } + return nil, nil +} + +// validateParamValueType checks that a bound value conforms to the param's declared type. +func validateParamValueType(p *runtimev1.ComponentParam, v any) error { + switch p.Type { + case "number": + switch v.(type) { + case int, int32, int64, uint, uint32, uint64, float32, float64: + return nil + } + return fmt.Errorf("value for param %q must be a number, got %v", p.Name, v) + case "boolean": + if _, ok := v.(bool); !ok { + return fmt.Errorf("value for param %q must be a boolean, got %v", p.Name, v) + } + return nil + default: + // "string" and the metrics view field types hold string values. + if _, ok := v.(string); !ok { + return fmt.Errorf("value for param %q must be a string, got %v", p.Name, v) + } + return nil + } +} + +// metricsViewHasTimeDimension returns true if fieldName is the metrics view's primary time dimension +// or a declared dimension of the time type. +func metricsViewHasTimeDimension(mv *runtimev1.MetricsViewSpec, fieldName string) bool { + if mv.TimeDimension == fieldName { + return true + } + for _, d := range mv.Dimensions { + if d.Name == fieldName { + return d.Type == runtimev1.MetricsViewSpec_DIMENSION_TYPE_TIME + } + } + return false +} + +// isTemplated returns true if the value is a string containing template placeholders. +func isTemplated(v any) bool { + s, ok := v.(string) + return ok && strings.Contains(s, "{{") +} diff --git a/runtime/canvas/params_test.go b/runtime/canvas/params_test.go new file mode 100644 index 000000000000..69f1d78a8e3d --- /dev/null +++ b/runtime/canvas/params_test.go @@ -0,0 +1,198 @@ +package canvas_test + +import ( + "testing" + + runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1" + "github.com/rilldata/rill/runtime/canvas" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/structpb" +) + +func testParams(t *testing.T) []*runtimev1.ComponentParam { + t.Helper() + return []*runtimev1.ComponentParam{ + {Name: "metrics_view", Type: "metrics_view", Required: true}, + {Name: "measure", Type: "measure", Required: true, MetricsViewParam: "metrics_view"}, + {Name: "dim", Type: "dimension", MetricsViewParam: "metrics_view"}, + {Name: "time_dim", Type: "time_dimension", MetricsViewParam: "metrics_view"}, + {Name: "limit", Type: "number", Default: structpb.NewNumberValue(500)}, + {Name: "shape", Type: "string", Default: structpb.NewStringValue("line"), Options: []*structpb.Value{structpb.NewStringValue("line"), structpb.NewStringValue("area")}}, + {Name: "smooth", Type: "boolean"}, + } +} + +func testMetricsViews(t *testing.T) map[string]*runtimev1.MetricsViewSpec { + t.Helper() + return map[string]*runtimev1.MetricsViewSpec{ + "mv1": { + TimeDimension: "ts", + Dimensions: []*runtimev1.MetricsViewSpec_Dimension{ + {Name: "country", Type: runtimev1.MetricsViewSpec_DIMENSION_TYPE_CATEGORICAL}, + {Name: "updated_at", Type: runtimev1.MetricsViewSpec_DIMENSION_TYPE_TIME}, + }, + Measures: []*runtimev1.MetricsViewSpec_Measure{ + {Name: "total"}, + }, + }, + } +} + +func TestEffectiveArgs(t *testing.T) { + spec := &runtimev1.ComponentSpec{ + Params: testParams(t), + Input: []*runtimev1.ComponentVariable{ + {Name: "legacy", DefaultValue: structpb.NewStringValue("x")}, + {Name: "limit", DefaultValue: structpb.NewNumberValue(1)}, // Overridden by the param default + }, + } + + args := canvas.EffectiveArgs(spec, map[string]any{"metrics_view": "mv1", "shape": "area"}) + require.Equal(t, map[string]any{ + "metrics_view": "mv1", + "limit": float64(500), + "shape": "area", + "legacy": "x", + }, args) +} + +func TestValidateParamBindings(t *testing.T) { + params := testParams(t) + mvs := testMetricsViews(t) + + valid := map[string]any{ + "metrics_view": "mv1", + "measure": "total", + "dim": "country", + "time_dim": "ts", + "limit": 100, + "shape": "area", + "smooth": true, + } + + cases := []struct { + name string + mutate func(m map[string]any) + mvs map[string]*runtimev1.MetricsViewSpec + wantErr string + }{ + {name: "valid", mutate: func(m map[string]any) {}}, + {name: "valid without metrics views", mutate: func(m map[string]any) { m["measure"] = "unknown" }, mvs: nil}, + {name: "unknown param", mutate: func(m map[string]any) { m["nope"] = 1 }, wantErr: `unknown param "nope"`}, + {name: "missing required", mutate: func(m map[string]any) { delete(m, "measure") }, wantErr: `missing value for required param "measure"`}, + {name: "type mismatch number", mutate: func(m map[string]any) { m["limit"] = "abc" }, wantErr: `param "limit" must be a number`}, + {name: "type mismatch boolean", mutate: func(m map[string]any) { m["smooth"] = "yes" }, wantErr: `param "smooth" must be a boolean`}, + {name: "type mismatch string", mutate: func(m map[string]any) { m["measure"] = 1 }, wantErr: `param "measure" must be a string`}, + {name: "value not in options", mutate: func(m map[string]any) { m["shape"] = "donut" }, wantErr: "not one of its options"}, + {name: "invalid metrics view", mutate: func(m map[string]any) { m["metrics_view"] = "mv2" }, wantErr: `metrics view "mv2" bound to param "metrics_view" is invalid or does not exist`}, + {name: "invalid measure", mutate: func(m map[string]any) { m["measure"] = "country" }, wantErr: `not a measure in metrics view "mv1"`}, + {name: "invalid dimension", mutate: func(m map[string]any) { m["dim"] = "total" }, wantErr: `not a dimension in metrics view "mv1"`}, + {name: "time dimension accepts time-typed dimension", mutate: func(m map[string]any) { m["time_dim"] = "updated_at" }}, + {name: "invalid time dimension", mutate: func(m map[string]any) { m["time_dim"] = "country" }, wantErr: `not a time dimension in metrics view "mv1"`}, + {name: "templated value skips validation", mutate: func(m map[string]any) { m["measure"] = "{{ .env.measure }}" }}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + bound := make(map[string]any, len(valid)) + for k, v := range valid { + bound[k] = v + } + tc.mutate(bound) + + effectiveMVs := mvs + if tc.mvs == nil && tc.name == "valid without metrics views" { + effectiveMVs = nil + } + + err := canvas.ValidateParamBindings(params, bound, effectiveMVs) + if tc.wantErr == "" { + require.NoError(t, err) + } else { + require.ErrorContains(t, err, tc.wantErr) + } + }) + } + + // Optional params with no default can be omitted entirely. + err := canvas.ValidateParamBindings(params, map[string]any{"metrics_view": "mv1", "measure": "total"}, mvs) + require.NoError(t, err) + + // Defaults are validated too: a field param with a default referencing a missing field errors without a binding. + withDefault := []*runtimev1.ComponentParam{ + {Name: "metrics_view", Type: "metrics_view", Default: structpb.NewStringValue("mv1")}, + {Name: "measure", Type: "measure", MetricsViewParam: "metrics_view", Default: structpb.NewStringValue("missing")}, + } + err = canvas.ValidateParamBindings(withDefault, nil, mvs) + require.ErrorContains(t, err, `not a measure in metrics view "mv1"`) +} + +func TestCoerceScalarParams(t *testing.T) { + params := testParams(t) + args := map[string]any{ + "metrics_view": "mv1", + "measure": "total", + "limit": 10.0, + "smooth": true, + "shape": "area", + } + + props := map[string]any{ + // Partial interpolation: stays a string, resolved by normal templating. + "metrics_sql": "SELECT {{ .params.measure }} FROM {{ .params.metrics_view }} LIMIT {{ .params.limit }}", + "spec": map[string]any{ + "chartType": "Line Chart", + "encodings": map[string]any{ + // Field params are strings either way. + "y": map[string]any{"field": "{{ .params.measure }}"}, + }, + "chartProperties": map[string]any{ + "pointCount": "{{ .params.limit }}", + "interpolate": "{{ .params.smooth }}", + // String params are left alone: templating already yields the right type. + "mark": "{{ .params.shape }}", + // Whitespace and the legacy .args alias are both accepted. + "padded": "{{ .args.limit }}", + // Not a param reference. + "label": "Top {{ .params.limit }} rows", + }, + "nested": []any{"{{ .params.limit }}", "keep"}, + }, + } + + got := canvas.CoerceScalarParams(props, params, args) + + spec := got["spec"].(map[string]any) + chartProps := spec["chartProperties"].(map[string]any) + + require.Equal(t, 10.0, chartProps["pointCount"]) + require.Equal(t, true, chartProps["interpolate"]) + require.Equal(t, "{{ .params.shape }}", chartProps["mark"]) + require.Equal(t, 10.0, chartProps["padded"]) + require.Equal(t, "Top {{ .params.limit }} rows", chartProps["label"]) + require.Equal(t, []any{10.0, "keep"}, spec["nested"]) + + // Untouched: not a whole-string param reference. + require.Equal(t, props["metrics_sql"], got["metrics_sql"]) + require.Equal(t, "{{ .params.measure }}", spec["encodings"].(map[string]any)["y"].(map[string]any)["field"]) + + // The input is not modified. + require.Equal(t, "{{ .params.limit }}", props["spec"].(map[string]any)["chartProperties"].(map[string]any)["pointCount"]) +} + +func TestCoerceScalarParamsNoScalarArgs(t *testing.T) { + props := map[string]any{"spec": map[string]any{"chartType": "Line Chart"}} + args := map[string]any{"metrics_view": "mv1"} + + // Returns the props unchanged when there is nothing to coerce. + require.Equal(t, props, canvas.CoerceScalarParams(props, testParams(t), args)) +} + +func TestCoerceScalarParamsSkipsTemplatedArgs(t *testing.T) { + params := testParams(t) + // An arg that is itself still templated must not be substituted. + args := map[string]any{"limit": "{{ .params.outer }}"} + props := map[string]any{"n": "{{ .params.limit }}"} + + require.Equal(t, props, canvas.CoerceScalarParams(props, params, args)) +} diff --git a/runtime/canvases.go b/runtime/canvases.go index e55ee01f4e41..a100d44b6b66 100644 --- a/runtime/canvases.go +++ b/runtime/canvases.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "strings" runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1" "github.com/rilldata/rill/runtime/drivers" @@ -25,6 +26,19 @@ func CollectCanvasComponentNames(rows []*runtimev1.CanvasRow, out map[string]boo } } +// CollectCanvasItems collects all items in the given rows, +// descending into tab groups (one level deep, since tabs cannot be nested). +func CollectCanvasItems(rows []*runtimev1.CanvasRow, out *[]*runtimev1.CanvasItem) { + for _, row := range rows { + *out = append(*out, row.Items...) + if tg := row.GetTabGroup(); tg != nil { + for _, tab := range tg.Tabs { + CollectCanvasItems(tab.Rows, out) + } + } + } +} + type ResolveCanvasResult struct { Canvas *runtimev1.Resource ResolvedComponents map[string]*runtimev1.Resource @@ -99,7 +113,9 @@ func (r *Runtime) ResolveCanvas(ctx context.Context, instanceID, canvas string, for k, v := range validSpec.RendererProperties.Fields { switch k { case "metrics_view": - if name := v.GetStringValue(); name != "" { + // Skip templated values (e.g. {{ .params.metrics_view }}): + // metrics views bound to params are collected from the canvas items below. + if name := v.GetStringValue(); name != "" && !strings.Contains(name, "{{") { metricsViews[name] = true } case "metrics_sql": @@ -146,6 +162,39 @@ func (r *Runtime) ResolveCanvas(ctx context.Context, instanceID, canvas string, } } + // Extract metrics views bound to component params by canvas items. + var items []*runtimev1.CanvasItem + CollectCanvasItems(spec.Rows, &items) + for _, item := range items { + cmp := components[item.Component] + if cmp == nil { + continue + } + validSpec := cmp.GetComponent().State.ValidSpec + if validSpec == nil && unsafe { + validSpec = cmp.GetComponent().Spec + } + if validSpec == nil { + continue + } + var bound map[string]any + if item.Params != nil { + bound = item.Params.AsMap() + } + for _, p := range validSpec.Params { + if p.Type != "metrics_view" { + continue + } + v, ok := bound[p.Name] + if !ok && p.Default != nil { + v = p.Default.AsInterface() + } + if name, ok := v.(string); ok && name != "" && !strings.Contains(name, "{{") { + metricsViews[name] = true + } + } + } + // Lookup metrics view resources referencedMetricsViews := make(map[string]*runtimev1.Resource) for mvName := range metricsViews { diff --git a/runtime/feature_flags.go b/runtime/feature_flags.go index b153d1fc07db..1745e1acee33 100644 --- a/runtime/feature_flags.go +++ b/runtime/feature_flags.go @@ -66,6 +66,8 @@ var defaultFeatureFlags = map[string]string{ "cloud_editing": "false", // Controls visibility of the custom chart option in canvas dashboards "custom_charts": "false", + // Controls visibility of standalone custom viz components (creation, canvas references, extraction, examples gallery) + "custom_components": "false", // Controls visibility of the personal canvas feature (per-user owner-only canvases stored as virtual files) "personal_canvases": "false", } diff --git a/runtime/feature_flags_test.go b/runtime/feature_flags_test.go index 33d2f38ff074..f48bd0fb54cb 100644 --- a/runtime/feature_flags_test.go +++ b/runtime/feature_flags_test.go @@ -45,6 +45,7 @@ func Test_ResolveFeatureFlags(t *testing.T) { "stickyDashboardState": false, "cloudEditing": false, "customCharts": false, + "customComponents": false, "personalCanvases": false, "disablePersistentDashboardState": false, }, @@ -73,6 +74,7 @@ func Test_ResolveFeatureFlags(t *testing.T) { "stickyDashboardState": false, "cloudEditing": false, "customCharts": false, + "customComponents": false, "personalCanvases": false, "disablePersistentDashboardState": false, }, @@ -101,6 +103,7 @@ func Test_ResolveFeatureFlags(t *testing.T) { "stickyDashboardState": false, "cloudEditing": false, "customCharts": false, + "customComponents": false, "personalCanvases": false, "disablePersistentDashboardState": false, }, @@ -129,6 +132,7 @@ func Test_ResolveFeatureFlags(t *testing.T) { "stickyDashboardState": false, "cloudEditing": false, "customCharts": false, + "customComponents": false, "personalCanvases": false, "disablePersistentDashboardState": false, }, diff --git a/runtime/parser/parse_canvas.go b/runtime/parser/parse_canvas.go index 30b27b17044f..7d2a673c01f5 100644 --- a/runtime/parser/parse_canvas.go +++ b/runtime/parser/parse_canvas.go @@ -15,6 +15,7 @@ import ( "github.com/rilldata/rill/runtime/pkg/rilltime" "github.com/rilldata/rill/runtime/pkg/urlutils" "golang.org/x/exp/maps" + "google.golang.org/protobuf/types/known/structpb" "gopkg.in/yaml.v3" ) @@ -70,6 +71,7 @@ type canvasTabYAML struct { type canvasItemYAML struct { Width *string `yaml:"width"` Component string `yaml:"component"` // Name of an externally defined component + Params map[string]any `yaml:"params"` // Values bound to the referenced component's declared params InlineComponent map[string]yaml.Node `yaml:",inline"` // Any other properties are considered an inline component definition } @@ -326,6 +328,38 @@ func (p *Parser) parseCanvasRows(node *Node, rows []*canvasRowYAML, allowTabs bo return nil, fmt.Errorf("item %d in row %d has properties incompatible with 'component'", j, i) } + // Validate and convert param bindings. Bindings are only valid for items that reference + // an externally defined component; inline components can hardcode values directly. + var params *structpb.Struct + if len(item.Params) > 0 { + if len(item.InlineComponent) > 0 { + return nil, fmt.Errorf("item %d in row %d cannot pass 'params' to an inline component", j, i) + } + for k, v := range item.Params { + if !componentParamNameRegex.MatchString(k) { + return nil, fmt.Errorf("invalid param name %q for item %d in row %d", k, j, i) + } + switch v.(type) { + case string, bool, int, int32, int64, uint, uint32, uint64, float32, float64: + default: + return nil, fmt.Errorf("invalid value for param %q for item %d in row %d: only scalar values are supported", k, j, i) + } + // Register metrics views bound to params as refs of the canvas. + // The component parser enforces that params of type "metrics_view" are named + // "metrics_view" or end with "_metrics_view", which makes this extraction complete. + if k == "metrics_view" || strings.HasSuffix(k, "_metrics_view") { + if name, ok := v.(string); ok && name != "" && !strings.Contains(name, "{{") { + node.Refs = append(node.Refs, ResourceName{Kind: ResourceKindMetricsView, Name: name}) + } + } + } + var err error + params, err = structpb.NewStruct(item.Params) + if err != nil { + return nil, fmt.Errorf("invalid params for item %d in row %d: %w", j, i, err) + } + } + // Parse inline component definition if present and assign into item.Component var definedInCanvs bool if len(item.InlineComponent) > 0 { @@ -342,6 +376,7 @@ func (p *Parser) parseCanvasRows(node *Node, rows []*canvasRowYAML, allowTabs bo items = append(items, &runtimev1.CanvasItem{ Component: item.Component, DefinedInCanvas: definedInCanvs, + Params: params, Width: width, WidthUnit: widthUnit, }) @@ -444,11 +479,16 @@ func (p *Parser) parseCanvasInlineComponent(canvasName, posKey string, props map return "", nil, err } - spec, refs, err := p.parseComponentYAML(tmp) + spec, refs, err := p.parseComponentYAML(tmp, true) if err != nil { return "", nil, err } + // Param declarations only make sense on externally defined components, where canvas items can bind values to them. + if len(spec.Params) > 0 { + return "", nil, errors.New("inline components cannot declare params") + } + spec.DefinedInCanvas = true name := fmt.Sprintf("%s--component-%s", canvasName, posKey) diff --git a/runtime/parser/parse_component.go b/runtime/parser/parse_component.go index ae30e389a706..1ce6d93811fc 100644 --- a/runtime/parser/parse_component.go +++ b/runtime/parser/parse_component.go @@ -3,8 +3,12 @@ package parser import ( "errors" "fmt" + "regexp" + "slices" + "strings" runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1" + "github.com/rilldata/rill/runtime/canvas" "github.com/rilldata/rill/runtime/pkg/pbutil" "google.golang.org/protobuf/types/known/structpb" ) @@ -15,11 +19,45 @@ type ComponentYAML struct { Title string `yaml:"title"` // Deprecated: use display_name Description string `yaml:"description"` Subtitle string `yaml:"subtitle"` // Deprecated: use description + Params []*ComponentParamYAML `yaml:"params"` Input []*ComponentVariableYAML `yaml:"input"` Output *ComponentVariableYAML `yaml:"output"` Other map[string]map[string]any `yaml:",inline" mapstructure:",remain"` // Generic renderer: can only have one key } +// ComponentParamYAML declares a typed parameter of a component. +// Canvases bind values to params when referencing the component; +// bound values are available in the renderer properties' templating as {{ .params. }}, +// and the metrics view metadata of a field-typed param as {{ .fields.. }} +// (see canvas.FieldTemplateData). +type ComponentParamYAML struct { + Name string `yaml:"name"` + Type string `yaml:"type"` + Description string `yaml:"description"` + Required bool `yaml:"required"` + Default any `yaml:"default"` + MetricsView string `yaml:"metrics_view"` // For field-typed params: back-reference to a sibling param of type "metrics_view" + Options []any `yaml:"options"` // For scalar params: allowed values +} + +// componentParamScalarTypes are the param types that hold plain values. +// Scalar params may declare options and are injected as native Vega-Lite params at resolve time. +var componentParamScalarTypes = []string{"string", "number", "boolean"} + +// componentParamFieldTypes are the param types that reference a field of a metrics view. +// They resolve their metrics view through a sibling param of type "metrics_view". +var componentParamFieldTypes = []string{"measure", "dimension", "time_dimension"} + +// componentParamNameRegex restricts param names to valid identifiers, +// since they are referenced as {{ .params. }} in Go templates and as signal names in Vega expressions. +var componentParamNameRegex = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`) + +// componentParamReservedNames are names that cannot be used for params: +// "title" and "description" collide with common component properties in visual editors, +// and "datum", "item", "event", "parent" are reserved in Vega expressions +// (scalar params are injected as native Vega-Lite params at resolve time). +var componentParamReservedNames = []string{"title", "description", "datum", "item", "event", "parent"} + type ComponentVariableYAML struct { Name string `yaml:"name"` Type string `yaml:"type"` @@ -58,7 +96,7 @@ func (p *Parser) parseComponent(node *Node) error { } // Parse into a ComponentSpec - spec, refs, err := p.parseComponentYAML(tmp) + spec, refs, err := p.parseComponentYAML(tmp, false) if err != nil { return err } @@ -81,7 +119,10 @@ func (p *Parser) parseComponent(node *Node) error { // parseComponentYAML parses and validates a ComponentYAML. // It is separated from parseComponent to allow inline creation of components from a canvas YAML file. -func (p *Parser) parseComponentYAML(tmp *ComponentYAML) (*runtimev1.ComponentSpec, []ResourceName, error) { +// The inline flag selects the custom chart spec format: inline canvas charts author Vega-Lite under +// "vega_spec", while standalone component files author a chart spec under "spec" and only carry a +// "vega_spec" once that chart spec has been ejected. +func (p *Parser) parseComponentYAML(tmp *ComponentYAML, inline bool) (*runtimev1.ComponentSpec, []ResourceName, error) { // Display name backwards compatibility if tmp.Title != "" && tmp.DisplayName == "" { tmp.DisplayName = tmp.Title @@ -95,10 +136,10 @@ func (p *Parser) parseComponentYAML(tmp *ComponentYAML) (*runtimev1.ComponentSpe // Discover and validate the renderer n := 0 var renderer string + var props map[string]any var rendererProps *structpb.Struct if len(tmp.Other) == 1 { n++ - var props map[string]any for renderer, props = range tmp.Other { break } @@ -121,6 +162,35 @@ func (p *Parser) parseComponentYAML(tmp *ComponentYAML) (*runtimev1.ComponentSpe return nil, nil, errors.New(`multiple renderers are not allowed`) } + // Enforce the custom chart spec format for this context. Presence is not required: + // the visual editor persists drafts with empty renderer properties. + if renderer == "custom_chart" { + if inline { + if _, ok := props["spec"]; ok { + return nil, nil, errors.New(`renderer property "spec" is not supported in an inline canvas chart: use "vega_spec", or define a component file to author a chart spec`) + } + } else { + _, hasSpec := props["spec"] + _, hasVegaSpec := props["vega_spec"] + // A component file authors a chart spec under "spec". It may instead carry a Vega-Lite spec + // under "vega_spec", which is what ejecting a chart spec produces; the two describe the same + // chart at different levels, so exactly one of them draws it. + if hasSpec && hasVegaSpec { + return nil, nil, errors.New(`renderer properties "spec" and "vega_spec" are mutually exclusive: a component draws either a chart spec or a Vega-Lite spec`) + } + if hasSpec { + if _, ok := props["spec"].(map[string]any); !ok { + return nil, nil, errors.New(`renderer property "spec" must be a mapping`) + } + } + if hasVegaSpec { + if _, ok := props["vega_spec"].(string); !ok { + return nil, nil, errors.New(`renderer property "vega_spec" must be a string containing a Vega-Lite spec`) + } + } + } + } + // We generally treat the renderer props as untyped, but since "metrics_view" is a very common field, // and adding it to refs generally makes for nicer error messages, we specifically search for and link it here. var refs []ResourceName @@ -128,7 +198,8 @@ func (p *Parser) parseComponentYAML(tmp *ComponentYAML) (*runtimev1.ComponentSpe for k, v := range rendererProps.Fields { if k == "metrics_view" { name := v.GetStringValue() - if name != "" { + // Skip templated values (e.g. {{ .params.metrics_view }}): refs must be static resource names. + if name != "" && !strings.Contains(name, "{{") { refs = append(refs, ResourceName{Kind: ResourceKindMetricsView, Name: name}) } break @@ -136,6 +207,68 @@ func (p *Parser) parseComponentYAML(tmp *ComponentYAML) (*runtimev1.ComponentSpe } } + // Parse declared params + params, err := parseComponentParams(tmp.Params) + if err != nil { + return nil, nil, err + } + + // Link metrics views set as defaults of metrics_view params: + // a component rendered with its defaults depends on the default metrics view + // even when no canvas binds the param, so it needs a ref for DAG ordering and invalidation. + for _, param := range params { + if param.Type != "metrics_view" || param.Default == nil { + continue + } + if name, ok := param.Default.AsInterface().(string); ok && name != "" && !strings.Contains(name, "{{") { + refs = append(refs, ResourceName{Kind: ResourceKindMetricsView, Name: name}) + } + } + + // When params are declared, require that all template references to .params (or its .args alias) + // and to .fields in the renderer properties refer to declared params. This catches typos at parse time. + if len(params) > 0 { + declared := make(map[string]bool, len(params)) + fieldTyped := make(map[string]bool, len(params)) + for _, param := range params { + declared[param.Name] = true + fieldTyped[param.Name] = slices.Contains(componentParamFieldTypes, param.Type) + } + vars := make(map[string]string) + if err := AnalyzeTemplateRecursively(props, vars); err != nil { + return nil, nil, fmt.Errorf("failed to analyze templating in renderer properties: %w", err) + } + for v := range vars { + if rest, ok := strings.CutPrefix(v, "fields."); ok { + name, key, _ := strings.Cut(rest, ".") + if !declared[name] { + return nil, nil, fmt.Errorf("renderer properties reference undeclared param %q", name) + } + // ".fields" exposes metrics view metadata, which only a field-typed param resolves against. + if !fieldTyped[name] { + return nil, nil, fmt.Errorf(`renderer properties reference ".fields.%s", but param %q is not of type %s`, name, name, strings.Join(componentParamFieldTypes, ", ")) + } + // A mistyped key resolves to Go's "" placeholder rather than to an error, + // which would then reach the renderer as a field or formatter name. + if !slices.Contains(canvas.FieldMetadataKeys, key) { + return nil, nil, fmt.Errorf(`renderer properties reference ".fields.%s.%s", which is not a field property (options: %s)`, name, key, strings.Join(canvas.FieldMetadataKeys, ", ")) + } + continue + } + rest, ok := strings.CutPrefix(v, "params.") + if !ok { + rest, ok = strings.CutPrefix(v, "args.") + } + if !ok { + continue + } + name, _, _ := strings.Cut(rest, ".") + if !declared[name] { + return nil, nil, fmt.Errorf("renderer properties reference undeclared param %q", name) + } + } + } + // Parse input variables var input []*runtimev1.ComponentVariable if len(tmp.Input) > 0 { @@ -165,9 +298,184 @@ func (p *Parser) parseComponentYAML(tmp *ComponentYAML) (*runtimev1.ComponentSpe Description: tmp.Description, Renderer: renderer, RendererProperties: rendererProps, + Params: params, Input: input, Output: output, } return spec, refs, nil } + +// parseComponentParams validates a component's declared params and converts them to protos. +func parseComponentParams(params []*ComponentParamYAML) ([]*runtimev1.ComponentParam, error) { + if len(params) == 0 { + return nil, nil + } + + // First pass: validate names and collect params of type "metrics_view" for back-reference resolution. + var mvParams []string + seen := make(map[string]bool, len(params)) + for i, param := range params { + if param == nil { + return nil, fmt.Errorf("param at index %d is empty", i) + } + if !componentParamNameRegex.MatchString(param.Name) { + return nil, fmt.Errorf("invalid param name %q: must be a valid identifier", param.Name) + } + if slices.Contains(componentParamReservedNames, param.Name) { + return nil, fmt.Errorf("param name %q is reserved", param.Name) + } + if seen[param.Name] { + return nil, fmt.Errorf("duplicate param name %q", param.Name) + } + seen[param.Name] = true + + if param.Type == "metrics_view" { + // The naming convention enables the canvas parser to extract metrics view refs from + // param bindings by key pattern without access to the component's declarations. + if param.Name != "metrics_view" && !strings.HasSuffix(param.Name, "_metrics_view") { + return nil, fmt.Errorf(`param %q of type "metrics_view" must be named "metrics_view" or end with "_metrics_view"`, param.Name) + } + mvParams = append(mvParams, param.Name) + } + } + + // Second pass: validate types, defaults, options and back-references, and convert to protos. + res := make([]*runtimev1.ComponentParam, len(params)) + for i, param := range params { + isScalar := slices.Contains(componentParamScalarTypes, param.Type) + isField := slices.Contains(componentParamFieldTypes, param.Type) + if !isScalar && !isField && param.Type != "metrics_view" { + return nil, fmt.Errorf("param %q has invalid type %q (options: %s)", param.Name, param.Type, strings.Join(slices.Concat(componentParamScalarTypes, []string{"metrics_view"}, componentParamFieldTypes), ", ")) + } + + if param.Required && param.Default != nil { + return nil, fmt.Errorf("param %q cannot both be required and have a default", param.Name) + } + if param.Default != nil { + if err := validateComponentParamValue(param.Type, param.Default); err != nil { + return nil, fmt.Errorf("invalid default for param %q: %w", param.Name, err) + } + } + + if len(param.Options) > 0 { + if !isScalar { + return nil, fmt.Errorf("param %q cannot have options: options are only supported for scalar types (%s)", param.Name, strings.Join(componentParamScalarTypes, ", ")) + } + defaultInOptions := param.Default == nil + for j, opt := range param.Options { + if err := validateComponentParamValue(param.Type, opt); err != nil { + return nil, fmt.Errorf("invalid option at index %d for param %q: %w", j, param.Name, err) + } + if param.Default != nil && scalarsEqual(param.Default, opt) { + defaultInOptions = true + } + } + if !defaultInOptions { + return nil, fmt.Errorf("default for param %q is not one of its options", param.Name) + } + } + + mvParam := param.MetricsView + if isField { + if mvParam == "" { + switch len(mvParams) { + case 1: + mvParam = mvParams[0] + case 0: + return nil, fmt.Errorf(`param %q of type %q requires a param of type "metrics_view" to be declared`, param.Name, param.Type) + default: + return nil, fmt.Errorf(`param %q of type %q must set "metrics_view" to disambiguate between multiple metrics_view params`, param.Name, param.Type) + } + } else if !slices.Contains(mvParams, mvParam) { + return nil, fmt.Errorf(`param %q references %q, which is not a declared param of type "metrics_view"`, param.Name, mvParam) + } + } else if mvParam != "" { + return nil, fmt.Errorf(`param %q of type %q cannot set "metrics_view"`, param.Name, param.Type) + } + + var defaultVal *structpb.Value + if param.Default != nil { + var err error + defaultVal, err = structpb.NewValue(param.Default) + if err != nil { + return nil, fmt.Errorf("invalid default for param %q: %w", param.Name, err) + } + } + var options []*structpb.Value + for j, opt := range param.Options { + val, err := structpb.NewValue(opt) + if err != nil { + return nil, fmt.Errorf("invalid option at index %d for param %q: %w", j, param.Name, err) + } + options = append(options, val) + } + + res[i] = &runtimev1.ComponentParam{ + Name: param.Name, + Type: param.Type, + Description: param.Description, + Required: param.Required, + Default: defaultVal, + MetricsViewParam: mvParam, + Options: options, + } + } + + return res, nil +} + +// validateComponentParamValue checks that a param's default or option value conforms to its declared type. +func validateComponentParamValue(typ string, v any) error { + switch typ { + case "number": + switch v.(type) { + case int, int32, int64, uint, uint32, uint64, float32, float64: + return nil + } + return fmt.Errorf("expected a number, got %v", v) + case "boolean": + if _, ok := v.(bool); !ok { + return fmt.Errorf("expected a boolean, got %v", v) + } + return nil + default: + // "string" and all metrics view field types hold string values. + if _, ok := v.(string); !ok { + return fmt.Errorf("expected a string, got %v", v) + } + return nil + } +} + +// scalarsEqual compares two scalar values, treating all numeric types as equal when their values match. +func scalarsEqual(a, b any) bool { + af, aok := asFloat(a) + bf, bok := asFloat(b) + if aok && bok { + return af == bf + } + return a == b +} + +func asFloat(v any) (float64, bool) { + switch v := v.(type) { + case int: + return float64(v), true + case int32: + return float64(v), true + case int64: + return float64(v), true + case uint: + return float64(v), true + case uint32: + return float64(v), true + case uint64: + return float64(v), true + case float32: + return float64(v), true + case float64: + return v, true + } + return 0, false +} diff --git a/runtime/parser/parser_test.go b/runtime/parser/parser_test.go index d8b28163a898..6ac7e9ee90ef 100644 --- a/runtime/parser/parser_test.go +++ b/runtime/parser/parser_test.go @@ -2019,6 +2019,320 @@ input: requireResourcesAndErrors(t, p, resources, nil) } +func TestComponentParams(t *testing.T) { + ctx := context.Background() + repo := makeRepo(t, map[string]string{ + `rill.yaml`: ``, + `components/trend.yaml`: ` +type: component +params: + - name: metrics_view + type: metrics_view + required: true + - name: measure + type: measure + required: true + - name: time_dim + type: time_dimension + required: true + - name: limit + type: number + default: 500 + - name: shape + type: string + default: line + options: [line, area] +custom_chart: + metrics_sql: "SELECT {{ .params.time_dim }} AS ts, {{ .params.measure }} AS value FROM {{ .params.metrics_view }} LIMIT {{ .params.limit }}" + spec: + chartType: Line Chart + encodings: + x: {field: "{{ .params.time_dim }}"} + y: {field: "{{ .params.measure }}"} +`, + // Component with a templated metrics_view renderer property: it must not produce a ref. + `components/tpl.yaml`: ` +type: component +params: + - name: metrics_view + type: metrics_view + required: true +kpi: + metrics_view: "{{ .params.metrics_view }}" +`, + // Component with a defaulted metrics_view param: the default must produce a ref, + // since the component depends on it even when no canvas binds the param. + `components/defaulted.yaml`: ` +type: component +params: + - name: metrics_view + type: metrics_view + default: mv_default +kpi: + metrics_view: "{{ .params.metrics_view }}" +`, + `canvases/d1.yaml`: ` +type: canvas +rows: +- items: + - component: trend + params: + metrics_view: mv1 + measure: total + time_dim: __time + limit: 100 + width: 6 +`, + }) + + resources := []*Resource{ + { + Name: ResourceName{Kind: ResourceKindComponent, Name: "trend"}, + Paths: []string{"/components/trend.yaml"}, + ComponentSpec: &runtimev1.ComponentSpec{ + DisplayName: "Trend", + Renderer: "custom_chart", + RendererProperties: must(structpb.NewStruct(map[string]any{ + "metrics_sql": "SELECT {{ .params.time_dim }} AS ts, {{ .params.measure }} AS value FROM {{ .params.metrics_view }} LIMIT {{ .params.limit }}", + "spec": map[string]any{ + "chartType": "Line Chart", + "encodings": map[string]any{ + "x": map[string]any{"field": "{{ .params.time_dim }}"}, + "y": map[string]any{"field": "{{ .params.measure }}"}, + }, + }, + })), + Params: []*runtimev1.ComponentParam{ + {Name: "metrics_view", Type: "metrics_view", Required: true}, + {Name: "measure", Type: "measure", Required: true, MetricsViewParam: "metrics_view"}, + {Name: "time_dim", Type: "time_dimension", Required: true, MetricsViewParam: "metrics_view"}, + {Name: "limit", Type: "number", Default: structpb.NewNumberValue(500)}, + {Name: "shape", Type: "string", Default: structpb.NewStringValue("line"), Options: []*structpb.Value{structpb.NewStringValue("line"), structpb.NewStringValue("area")}}, + }, + }, + }, + { + Name: ResourceName{Kind: ResourceKindComponent, Name: "tpl"}, + Paths: []string{"/components/tpl.yaml"}, + ComponentSpec: &runtimev1.ComponentSpec{ + DisplayName: "Tpl", + Renderer: "kpi", + RendererProperties: must(structpb.NewStruct(map[string]any{"metrics_view": "{{ .params.metrics_view }}"})), + Params: []*runtimev1.ComponentParam{ + {Name: "metrics_view", Type: "metrics_view", Required: true}, + }, + }, + }, + { + Name: ResourceName{Kind: ResourceKindComponent, Name: "defaulted"}, + Paths: []string{"/components/defaulted.yaml"}, + Refs: []ResourceName{{Kind: ResourceKindMetricsView, Name: "mv_default"}}, + ComponentSpec: &runtimev1.ComponentSpec{ + DisplayName: "Defaulted", + Renderer: "kpi", + RendererProperties: must(structpb.NewStruct(map[string]any{"metrics_view": "{{ .params.metrics_view }}"})), + Params: []*runtimev1.ComponentParam{ + {Name: "metrics_view", Type: "metrics_view", Default: structpb.NewStringValue("mv_default")}, + }, + }, + }, + { + Name: ResourceName{Kind: ResourceKindCanvas, Name: "d1"}, + Paths: []string{"/canvases/d1.yaml"}, + Refs: []ResourceName{ + {Kind: ResourceKindComponent, Name: "trend"}, + {Kind: ResourceKindMetricsView, Name: "mv1"}, + }, + CanvasSpec: &runtimev1.CanvasSpec{ + DisplayName: "D1", + AllowCustomTimeRange: true, + FiltersEnabled: true, + Rows: []*runtimev1.CanvasRow{ + { + Items: []*runtimev1.CanvasItem{ + { + Component: "trend", + Params: must(structpb.NewStruct(map[string]any{ + "metrics_view": "mv1", + "measure": "total", + "time_dim": "__time", + "limit": 100, + })), + Width: asPtr(uint32(6)), + }, + }, + }, + }, + }, + }, + } + + p, err := Parse(ctx, repo, "", "", "duckdb", true) + require.NoError(t, err) + requireResourcesAndErrors(t, p, resources, nil) +} + +func TestComponentParamErrors(t *testing.T) { + ctx := context.Background() + + component := func(params string) string { + return fmt.Sprintf("type: component\nparams:\n%s\nkpi:\n metrics_view: foo\n", params) + } + + cases := []struct { + name string + files map[string]string + wantErr string + }{ + { + name: "duplicate param name", + files: map[string]string{`components/c.yaml`: component(" - name: a\n type: string\n - name: a\n type: number")}, + wantErr: `duplicate param name "a"`, + }, + { + name: "invalid type", + files: map[string]string{`components/c.yaml`: component(" - name: a\n type: widget")}, + wantErr: `invalid type "widget"`, + }, + { + name: "invalid name", + files: map[string]string{`components/c.yaml`: component(" - name: 1a\n type: string")}, + wantErr: `invalid param name "1a"`, + }, + { + name: "reserved name", + files: map[string]string{`components/c.yaml`: component(" - name: datum\n type: string")}, + wantErr: `param name "datum" is reserved`, + }, + { + name: "required with default", + files: map[string]string{`components/c.yaml`: component(" - name: a\n type: string\n required: true\n default: x")}, + wantErr: "cannot both be required and have a default", + }, + { + name: "default type mismatch", + files: map[string]string{`components/c.yaml`: component(" - name: a\n type: number\n default: x")}, + wantErr: "expected a number", + }, + { + name: "field param without metrics_view param", + files: map[string]string{`components/c.yaml`: component(" - name: a\n type: measure")}, + wantErr: `requires a param of type "metrics_view"`, + }, + { + name: "metrics_view param naming convention", + files: map[string]string{`components/c.yaml`: component(" - name: mv\n type: metrics_view")}, + wantErr: `must be named "metrics_view" or end with "_metrics_view"`, + }, + { + name: "back-reference to non-metrics_view param", + files: map[string]string{`components/c.yaml`: component(" - name: metrics_view\n type: metrics_view\n - name: a\n type: string\n - name: b\n type: measure\n metrics_view: a")}, + wantErr: `not a declared param of type "metrics_view"`, + }, + { + name: "options on field param", + files: map[string]string{`components/c.yaml`: component(" - name: metrics_view\n type: metrics_view\n - name: a\n type: measure\n options: [x, y]")}, + wantErr: "options are only supported for scalar types", + }, + { + name: "default not in options", + files: map[string]string{`components/c.yaml`: component(" - name: a\n type: string\n default: z\n options: [x, y]")}, + wantErr: "not one of its options", + }, + { + name: "undeclared param reference in renderer properties", + files: map[string]string{`components/c.yaml`: ` +type: component +params: + - name: a + type: string +kpi: + metrics_view: foo + measure: "{{ .params.b }}" +`}, + wantErr: `reference undeclared param "b"`, + }, + { + name: "undeclared param reference in a .fields reference", + files: map[string]string{`components/c.yaml`: ` +type: component +params: + - name: metrics_view + type: metrics_view + - name: a + type: measure +custom_chart: + vega_spec: '{"title": "{{ .fields.b.display_name }}"}' +`}, + wantErr: `reference undeclared param "b"`, + }, + { + name: "field metadata reference to a scalar param", + files: map[string]string{`components/c.yaml`: ` +type: component +params: + - name: a + type: string +custom_chart: + vega_spec: '{"title": "{{ .fields.a.display_name }}"}' +`}, + wantErr: `param "a" is not of type measure, dimension, time_dimension`, + }, + { + name: "unknown field metadata property", + files: map[string]string{`components/c.yaml`: ` +type: component +params: + - name: metrics_view + type: metrics_view + - name: a + type: measure +custom_chart: + vega_spec: '{"title": "{{ .fields.a.displayName }}"}' +`}, + wantErr: `".fields.a.displayName", which is not a field property`, + }, + { + name: "params on inline component", + files: map[string]string{`canvases/d.yaml`: ` +type: canvas +rows: +- items: + - markdown: + content: Foo + params: + a: 1 +`}, + wantErr: "cannot pass 'params' to an inline component", + }, + { + name: "non-scalar param binding", + files: map[string]string{`canvases/d.yaml`: ` +type: canvas +rows: +- items: + - component: c + params: + a: [1, 2] +`}, + wantErr: "only scalar values are supported", + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + files := map[string]string{`rill.yaml`: ``} + for k, v := range tc.files { + files[k] = v + } + p, err := Parse(ctx, makeRepo(t, files), "", "", "duckdb", true) + require.NoError(t, err) + require.Len(t, p.Errors, 1) + require.Contains(t, p.Errors[0].Message, tc.wantErr) + }) + } +} + func TestCanvasTabGroups(t *testing.T) { ctx := context.Background() repo := makeRepo(t, map[string]string{ diff --git a/runtime/parser/schema/project.schema.yaml b/runtime/parser/schema/project.schema.yaml index fa8f5c1aba0e..a58b1fe29276 100644 --- a/runtime/parser/schema/project.schema.yaml +++ b/runtime/parser/schema/project.schema.yaml @@ -2406,6 +2406,14 @@ definitions: - **table** - Similar to Pivot table, add dimensions and measures to visualize your data - **heatmap** - Heat Map chart to visualize distribution of data - **donut_chart** - Donut or Pie chart to display sums of total + params: + type: object + description: Values bound to the referenced component's declared params. Only valid together with `component`. Values must be scalars. + additionalProperties: + type: + - string + - number + - boolean width: type: - string @@ -3157,6 +3165,11 @@ definitions: description: type: string description: Detailed description of the component's purpose and functionality + params: + type: array + description: List of typed parameters that canvases can bind values to when referencing this component. Bound values are available in the renderer properties' templating as `{{ .params. }}`, and the metrics view metadata of a field-typed param as `{{ .fields..display_name }}`, `{{ .fields..format_d3 }}`, `{{ .fields..format_preset }}` and `{{ .fields..format_type }}`. + items: + $ref: '#/definitions/component_param_properties' input: type: array description: List of input variables that can be passed to the component @@ -3180,10 +3193,94 @@ definitions: $ref: '#/definitions/markdown_properties' image: $ref: '#/definitions/image_properties' + custom_chart: + $ref: '#/definitions/custom_chart_properties' required: - type additionalProperties: false + # Custom Chart Properties (Flint) + custom_chart_properties: + type: object + description: | + A custom visualization: a Metrics SQL query plus a chart spec describing how to draw it. + Rill derives scales, axes, formats, sorting, stacking and layout from the data and from the + metrics view's semantics, so the spec only states the chart type and which field goes on which channel. + properties: + metrics_sql: + type: string + description: | + Metrics SQL query selecting the dimensions and measures to plot, e.g. + `SELECT {{ .params.dim }}, {{ .params.measure }} FROM {{ .params.metrics_view }} LIMIT 10`. + Measures arrive pre-aggregated; do not wrap them in aggregate functions. + spec: + $ref: '#/definitions/flint_chart_spec' + vega_spec: + type: string + description: | + A Vega-Lite spec, as an alternative to `spec` for a chart Rill cannot describe. + This is what ejecting a chart spec in the component editor produces: the Vega-Lite it + compiled to, with the param bindings turned back into template references. Rill no longer + derives scales, axes, formats, sorting or layout, so the chart stops adapting on its own. + Read the query result from the `query1` dataset, i.e. `"data": {"name": "query1"}`, + and size the chart to its container with `"width": "container"`, `"height": "container"` + and `"autosize": {"type": "fit"}`. Mutually exclusive with `spec`. + additionalProperties: false + + # Flint Chart Spec + flint_chart_spec: + type: object + properties: + chartType: + type: string + description: The chart type, e.g. `Line Chart`, `Bar Chart`, `Heatmap`, `Waterfall Chart`. + encodings: + type: object + description: | + Maps a visual channel (`x`, `y`, `color`, `size`, `column`, `row`, `group`, `angle`, `goal`, ...) + to a field. Each value is either a field name or an encoding object. + additionalProperties: + oneOf: + - type: string + - $ref: '#/definitions/flint_chart_encoding' + - type: array + items: + oneOf: + - type: string + - $ref: '#/definitions/flint_chart_encoding' + chartProperties: + type: object + description: Per-chart-type presentation tuning, e.g. `innerRadius`, `stackMode`, `interpolate`. + additionalProperties: false + + # Flint Chart Encoding + flint_chart_encoding: + type: object + properties: + field: + type: string + description: Name of the column to bind to this channel. + type: + type: string + enum: [quantitative, nominal, ordinal, temporal] + description: Overrides the type derived from the field's semantics. Rarely needed. + aggregate: + type: string + enum: [count, sum, average, mean] + description: | + Aggregates rows within the chart. Rarely needed in Rill: measures selected from a metrics view + are already aggregated. + sortOrder: + type: string + enum: [ascending, descending] + sortBy: + type: string + description: Channel or field to sort this channel's values by, e.g. `y`. + scheme: + type: string + description: Color scheme name for the `color` channel, e.g. `viridis`, `tableau10`. + additionalProperties: false + # Chart Properties chart_properties: type: object @@ -3397,6 +3494,50 @@ definitions: description: Overrides any properties in production environment. + component_param_properties: + type: object + properties: + name: + type: string + description: Param name. Must be a valid identifier; referenced in the renderer properties' templating as `{{ .params. }}`. + type: + type: string + enum: + - string + - number + - boolean + - metrics_view + - measure + - dimension + - time_dimension + description: Param type. Params of type `metrics_view` must be named `metrics_view` or end with `_metrics_view`. + description: + type: string + description: Human-facing description of the param + required: + type: boolean + description: If true, a canvas item referencing this component must bind a value for the param. Mutually exclusive with `default`. + default: + description: Default value used when the param is not bound + type: + - string + - number + - boolean + metrics_view: + type: string + description: For `measure`, `dimension` and `time_dimension` params, the name of a sibling param of type `metrics_view` whose bound metrics view the field must belong to. May be omitted when exactly one `metrics_view` param is declared. + options: + type: array + description: For scalar params, the allowed values. Renders as a select input in visual editors. + items: + type: + - string + - number + - boolean + required: + - name + - type + additionalProperties: false component_variable_properties: type: object properties: diff --git a/runtime/reconcilers/canvas.go b/runtime/reconcilers/canvas.go index 93eb37cf3baf..08376ee350ef 100644 --- a/runtime/reconcilers/canvas.go +++ b/runtime/reconcilers/canvas.go @@ -4,9 +4,11 @@ import ( "context" "errors" "fmt" + "strings" runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1" "github.com/rilldata/rill/runtime" + "github.com/rilldata/rill/runtime/canvas" "github.com/rilldata/rill/runtime/drivers" "github.com/rilldata/rill/runtime/pkg/pathutil" "google.golang.org/protobuf/types/known/timestamppb" @@ -115,6 +117,9 @@ func (r *CanvasReconciler) Reconcile(ctx context.Context, n *runtimev1.ResourceN if validateErr == nil { validateErr = r.validateRequiredFilters(ctx, c.Spec, components) } + if validateErr == nil { + validateErr = r.validateItemParamBindings(ctx, c.Spec, components) + } // Capture the valid spec in the state if validateErr == nil { @@ -200,15 +205,34 @@ func canvasTransitiveConditionResources(ctx context.Context, c *runtime.Controll // explicitly allow access to the canvas itself conditionResources = append(conditionResources, res.Meta.Name) - // Collect all component names referenced by the canvas (including those nested in tab groups) - componentNames := make(map[string]bool) - runtime.CollectCanvasComponentNames(spec.Rows, componentNames) + // Collect all items referenced by the canvas (including those nested in tab groups) + var items []*runtimev1.CanvasItem + runtime.CollectCanvasItems(spec.Rows, &items) + + // Process each item + seenComponents := make(map[string]bool, len(items)) + for _, item := range items { + // Track metrics views bound to the item's params. + // The component parser enforces that params of type "metrics_view" are named + // "metrics_view" or end with "_metrics_view", which makes this extraction complete. + if item.Params != nil { + for k, v := range item.Params.AsMap() { + if k == "metrics_view" || strings.HasSuffix(k, "_metrics_view") { + if name, ok := v.(string); ok && name != "" && !strings.Contains(name, "{{") { + refs.metricsViews[name] = true + } + } + } + } + + if seenComponents[item.Component] { + continue + } + seenComponents[item.Component] = true - // Process each component - for componentName := range componentNames { componentRef := &runtimev1.ResourceName{ Kind: runtime.ResourceKindComponent, - Name: componentName, + Name: item.Component, } // Allow access to the component itself conditionResources = append(conditionResources, componentRef) @@ -225,6 +249,16 @@ func canvasTransitiveConditionResources(ctx context.Context, c *runtime.Controll if componentSpec == nil { componentSpec = componentRes.GetComponent().Spec } + + // Track metrics views set as defaults of the component's declared metrics_view params. + for _, p := range componentSpec.Params { + if p.Type == "metrics_view" && p.Default != nil { + if name, ok := p.Default.AsInterface().(string); ok && name != "" && !strings.Contains(name, "{{") { + refs.metricsViews[name] = true + } + } + } + if componentSpec.RendererProperties == nil { continue } @@ -364,6 +398,74 @@ func (r *CanvasReconciler) validateRequiredFilters(ctx context.Context, spec *ru return nil } +// validateItemParamBindings validates the param values that canvas items bind to their referenced +// components' declared params, including that field-typed params reference fields that exist in +// the bound metrics views. +func (r *CanvasReconciler) validateItemParamBindings(ctx context.Context, spec *runtimev1.CanvasSpec, components map[string]*runtimev1.Resource) error { + var items []*runtimev1.CanvasItem + runtime.CollectCanvasItems(spec.Rows, &items) + + // Cache of valid metrics view specs bound to params, shared across items. + mvs := make(map[string]*runtimev1.MetricsViewSpec) + + for _, item := range items { + cmp := components[item.Component] + if cmp == nil { + // Missing components are reported by checkRefs. + continue + } + componentSpec := cmp.GetComponent().State.ValidSpec + if componentSpec == nil { + componentSpec = cmp.GetComponent().Spec + } + + var bound map[string]any + if item.Params != nil { + bound = item.Params.AsMap() + } + if len(bound) == 0 && len(componentSpec.Params) == 0 { + continue + } + if len(bound) > 0 && len(componentSpec.Params) == 0 { + return fmt.Errorf("item passes params to component %q, which does not declare any params", item.Component) + } + + // Fetch the specs of the metrics views bound to params. + // It is safe to fetch them here because the parser added them as refs of the canvas, so DAG ordering holds. + for _, p := range componentSpec.Params { + if p.Type != "metrics_view" { + continue + } + v, ok := bound[p.Name] + if !ok && p.Default != nil { + v = p.Default.AsInterface() + } + name, ok := v.(string) + if !ok || name == "" || strings.Contains(name, "{{") { + continue + } + if _, ok := mvs[name]; ok { + continue + } + res, err := r.C.Get(ctx, &runtimev1.ResourceName{Kind: runtime.ResourceKindMetricsView, Name: name}, false) + if err != nil { + // Not found or invalid; ValidateParamBindings reports it as an invalid metrics view. + continue + } + if mvSpec := res.GetMetricsView().State.ValidSpec; mvSpec != nil { + mvs[name] = mvSpec + } + } + + err := canvas.ValidateParamBindings(componentSpec.Params, bound, mvs) + if err != nil { + return fmt.Errorf("invalid params for component %q: %w", item.Component, err) + } + } + + return nil +} + // rendererRefs tracks all metrics views found in canvas component renderer properties. // It currently only tracks metrics views, but in the future we may want to add an option to also track metrics view fields and filters. // We did that previously, but removed it since such granular security was considered too strict (it also impacts ability to filter by fields not present on the canvas). @@ -407,9 +509,13 @@ func (r *rendererRefs) populateRendererRefs(ctx context.Context, renderer string } // metricsView registers a metrics view reference. +// Templated values (e.g. {{ .params.metrics_view }}) are skipped; the metrics views bound to +// params are tracked from the canvas items' params instead. func (r *rendererRefs) metricsView(mv any) error { if mv, ok := mv.(string); ok { - r.metricsViews[mv] = true + if !strings.Contains(mv, "{{") { + r.metricsViews[mv] = true + } return nil } return fmt.Errorf("metrics view field is not a string") @@ -449,13 +555,31 @@ func (r *rendererRefs) text(ctx context.Context, content any) error { return nil } -// metricsSQL parses and registers metrics view references found in a metrics SQL string. +// metricsSQL parses and registers metrics view references found in a metrics_sql property, +// which holds either a single query string or a list of query strings. func (r *rendererRefs) metricsSQL(ctx context.Context, sql any) error { - sqlStr, ok := sql.(string) - if !ok { - return fmt.Errorf("metrics_sql field is not a string") + switch v := sql.(type) { + case string: + return r.metricsSQLString(ctx, v) + case []any: + // Multi-query custom charts: register refs per query, best-effort, + // so one malformed query doesn't drop refs from the others. + for _, e := range v { + if s, ok := e.(string); ok { + _ = r.metricsSQLString(ctx, s) + if ctx.Err() != nil { + return ctx.Err() + } + } + } + return nil + default: + return fmt.Errorf("metrics_sql field is not a string or a list of strings") } +} +// metricsSQLString parses and registers metrics view references found in a single metrics SQL query. +func (r *rendererRefs) metricsSQLString(ctx context.Context, sqlStr string) error { initializer, ok := runtime.ResolverInitializers["metrics_sql"] if !ok { return fmt.Errorf("metrics_sql resolver not registered") diff --git a/runtime/reconcilers/canvas_test.go b/runtime/reconcilers/canvas_test.go index f5cb9af1f876..d4da23a2f7d3 100644 --- a/runtime/reconcilers/canvas_test.go +++ b/runtime/reconcilers/canvas_test.go @@ -308,6 +308,7 @@ func TestCanvasResolveTransitiveAccess(t *testing.T) { "m1.sql": `SELECT 'foo' as foo, 1 as x`, "m2.sql": `SELECT 'bar' as bar, 2 as y`, "m3.sql": `SELECT 'baz' as baz, 3 as z`, + "m4.sql": `SELECT 'qux' as qux, 4 as w`, "mv1.yaml": ` version: 1 type: metrics_view @@ -337,6 +338,16 @@ dimensions: measures: - name: z expression: sum(z) +`, + "mv4.yaml": ` +version: 1 +type: metrics_view +model: m4 +dimensions: +- column: qux +measures: +- name: w + expression: sum(w) `, "c1.yaml": ` type: canvas @@ -349,13 +360,18 @@ rows: - items: - custom_chart: metrics_sql: "SELECT bar, y FROM mv2" + - items: + - custom_chart: + metrics_sql: + - "SELECT bar, y FROM mv2" + - "SELECT qux, w FROM mv4" - items: - markdown: content: 'Total z: {{ metrics_sql "SELECT z FROM mv3" }}' `, }) testruntime.ReconcileParserAndWait(t, rt, id) - testruntime.RequireReconcileState(t, rt, id, 11, 0, 0) + testruntime.RequireReconcileState(t, rt, id, 14, 0, 0) // Build claims with a transitive access rule on the canvas ctx := t.Context() @@ -391,4 +407,167 @@ rows: sec, err = rt.ResolveSecurity(ctx, id, claims, mv3) require.NoError(t, err) require.True(t, sec.CanAccess()) + + // Resolve security for mv4 (referenced via a metrics_sql list in a multi-query custom chart); should be accessible + mv4 := testruntime.GetResource(t, rt, id, runtime.ResourceKindMetricsView, "mv4") + sec, err = rt.ResolveSecurity(ctx, id, claims, mv4) + require.NoError(t, err) + require.True(t, sec.CanAccess()) +} + +func TestCanvasItemParamBindings(t *testing.T) { + rt, id := testruntime.NewInstanceWithOptions(t, testruntime.InstanceOptions{ + Files: map[string]string{"rill.yaml": ""}, + }) + + // Create a model, a metrics view, a parameterized component, and a canvas binding values to its params. + testruntime.PutFiles(t, rt, id, map[string]string{ + "m1.sql": `SELECT '2025-01-01T00:00:00Z'::TIMESTAMP AS ts, 'foo' as foo, 1 as x`, + "mv1.yaml": ` +version: 1 +type: metrics_view +model: m1 +timeseries: ts +dimensions: +- column: foo +measures: +- name: x + expression: sum(x) +`, + "trend.yaml": ` +type: component +params: + - name: metrics_view + type: metrics_view + required: true + - name: measure + type: measure + required: true + - name: time_dim + type: time_dimension + required: true + - name: limit + type: number + default: 500 +custom_chart: + metrics_sql: SELECT {{ .params.time_dim }} AS ts, {{ .params.measure }} AS value FROM {{ .params.metrics_view }} LIMIT {{ .params.limit }} + spec: + chartType: Line Chart + encodings: + x: {field: ts} + y: {field: value} +`, + "c1.yaml": ` +type: canvas +rows: + - items: + - component: trend + params: + metrics_view: mv1 + measure: x + time_dim: ts +`, + }) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 5, 0, 0) + + // The canvas should be valid and have a parser-added ref to the param-bound metrics view. + c1 := testruntime.GetResource(t, rt, id, runtime.ResourceKindCanvas, "c1") + require.NotNil(t, c1.GetCanvas().State.ValidSpec) + var hasMVRef bool + for _, ref := range c1.Meta.Refs { + if ref.Kind == runtime.ResourceKindMetricsView && ref.Name == "mv1" { + hasMVRef = true + } + } + require.True(t, hasMVRef, "expected canvas to have a ref to the param-bound metrics view") + + // The param-bound metrics view should be accessible through transitive access on the canvas. + claims := &runtime.SecurityClaims{ + AdditionalRules: []*runtimev1.SecurityRule{ + { + Rule: &runtimev1.SecurityRule_TransitiveAccess{ + TransitiveAccess: &runtimev1.SecurityRuleTransitiveAccess{ + Resource: &runtimev1.ResourceName{Kind: runtime.ResourceKindCanvas, Name: "c1"}, + }, + }, + }, + }, + } + mv1 := testruntime.GetResource(t, rt, id, runtime.ResourceKindMetricsView, "mv1") + sec, err := rt.ResolveSecurity(t.Context(), id, claims, mv1) + require.NoError(t, err) + require.True(t, sec.CanAccess()) + + // Invalid: a bound measure that doesn't exist in the metrics view. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: canvas +rows: + - items: + - component: trend + params: + metrics_view: mv1 + measure: nonexistent + time_dim: ts +`, + }) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 5, 1, 0) + testruntime.RequireReconcileErrorContains(t, rt, id, runtime.ResourceKindCanvas, "c1", `not a measure in metrics view "mv1"`) + + // Invalid: a missing required param. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: canvas +rows: + - items: + - component: trend + params: + metrics_view: mv1 + time_dim: ts +`, + }) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 5, 1, 0) + testruntime.RequireReconcileErrorContains(t, rt, id, runtime.ResourceKindCanvas, "c1", `missing value for required param "measure"`) + + // Invalid: passing params to a component that doesn't declare any. + testruntime.PutFiles(t, rt, id, map[string]string{ + "plain.yaml": ` +type: component +kpi: + metrics_view: mv1 + measure: x +`, + "c1.yaml": ` +type: canvas +rows: + - items: + - component: plain + params: + foo: 1 +`, + }) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 6, 1, 0) + testruntime.RequireReconcileErrorContains(t, rt, id, runtime.ResourceKindCanvas, "c1", "does not declare any params") + + // Valid again: fix the canvas and check it recovers. + testruntime.PutFiles(t, rt, id, map[string]string{ + "c1.yaml": ` +type: canvas +rows: + - items: + - component: trend + params: + metrics_view: mv1 + measure: x + time_dim: ts + limit: 100 + - component: plain +`, + }) + testruntime.ReconcileParserAndWait(t, rt, id) + testruntime.RequireReconcileState(t, rt, id, 6, 0, 0) } diff --git a/runtime/reconcilers/component.go b/runtime/reconcilers/component.go index 9ade4bf8484e..243557eeee45 100644 --- a/runtime/reconcilers/component.go +++ b/runtime/reconcilers/component.go @@ -87,7 +87,7 @@ func (r *ComponentReconciler) Reconcile(ctx context.Context, n *runtimev1.Resour // Validate the renderer properties (only if all metrics view refs have a ValidSpec). var rendererErr error if allMetricsValid { - rendererErr = canvas.ValidateRendererProperties(c.Spec.Renderer, c.Spec.RendererProperties.AsMap(), mvs) + rendererErr = canvas.ValidateRendererProperties(c.Spec.Renderer, c.Spec.RendererProperties.AsMap(), mvs, len(c.Spec.Params) > 0) } else { rendererErr = errors.New("one or more referenced metrics views are invalid") } diff --git a/runtime/resolvers/metrics_sql.go b/runtime/resolvers/metrics_sql.go index 66f2e9aa8be8..2285bda2737b 100644 --- a/runtime/resolvers/metrics_sql.go +++ b/runtime/resolvers/metrics_sql.go @@ -3,6 +3,7 @@ package resolvers import ( "context" "errors" + "fmt" "github.com/mitchellh/mapstructure" runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1" @@ -30,6 +31,9 @@ type metricsSQLProps struct { AdditionalWhereByMetricsView map[string]*metricsview.Expression `mapstructure:"additional_where_by_metrics_view"` // AdditionalTimeRange is a time range filter to apply to the metrics SQL. AdditionalTimeRange *metricsview.TimeRange `mapstructure:"additional_time_range"` + // AdditionalTimeGrain buckets the time dimensions selected by the metrics SQL at this grain. + // Dimensions that already declare a grain, i.e. an explicit date_trunc in the SQL, are left alone. + AdditionalTimeGrain metricsview.TimeGrain `mapstructure:"additional_time_grain"` } type metricsSQLArgs struct { @@ -48,6 +52,9 @@ func newMetricsSQL(ctx context.Context, opts *runtime.ResolverOptions) (runtime. if props.SQL == "" { return nil, errors.New(`metrics SQL: missing required property "sql"`) } + if !props.AdditionalTimeGrain.Valid() { + return nil, fmt.Errorf("metrics SQL: invalid time grain %q", props.AdditionalTimeGrain) + } span := trace.SpanFromContext(ctx) if span.SpanContext().IsValid() { @@ -56,6 +63,7 @@ func newMetricsSQL(ctx context.Context, opts *runtime.ResolverOptions) (runtime. attribute.String("time_zone", props.TimeZone), attribute.Bool("has_additional_where", props.AdditionalWhere != nil || len(props.AdditionalWhereByMetricsView) > 0), attribute.Bool("has_additional_time_range", props.AdditionalTimeRange != nil), + attribute.String("additional_time_grain", string(props.AdditionalTimeGrain)), ) } @@ -79,22 +87,24 @@ func newMetricsSQL(ctx context.Context, opts *runtime.ResolverOptions) (runtime. return nil, err } + getMetricsView := func(ctx context.Context, name string) (*runtimev1.Resource, error) { + mv, err := ctrl.Get(ctx, &runtimev1.ResourceName{Kind: runtime.ResourceKindMetricsView, Name: name}, false) + if err != nil { + return nil, err + } + sec, err := opts.Runtime.ResolveSecurity(ctx, ctrl.InstanceID, opts.Claims, mv) + if err != nil { + return nil, err + } + if !sec.CanAccess() { + return nil, runtime.ErrForbidden + } + return mv, nil + } + // Create a metrics SQL parser compiler := metricssql.New(&metricssql.CompilerOptions{ - GetMetricsView: func(ctx context.Context, name string) (*runtimev1.Resource, error) { - mv, err := ctrl.Get(ctx, &runtimev1.ResourceName{Kind: runtime.ResourceKindMetricsView, Name: name}, false) - if err != nil { - return nil, err - } - sec, err := opts.Runtime.ResolveSecurity(ctx, ctrl.InstanceID, opts.Claims, mv) - if err != nil { - return nil, err - } - if !sec.CanAccess() { - return nil, runtime.ErrForbidden - } - return mv, nil - }, + GetMetricsView: getMetricsView, GetTimestamps: func(ctx context.Context, mv *runtimev1.Resource, timeDim string) (metricsview.TimestampsResult, error) { sec, err := opts.Runtime.ResolveSecurity(ctx, ctrl.InstanceID, opts.Claims, mv) if err != nil { @@ -127,6 +137,15 @@ func newMetricsSQL(ctx context.Context, opts *runtime.ResolverOptions) (runtime. // Inject the additional time range if provided query.TimeRange = applyAdditionalTimeRange(query.TimeRange, props.AdditionalTimeRange) + // Bucket the query's time dimensions if a grain was provided + if props.AdditionalTimeGrain != metricsview.TimeGrainUnspecified { + mv, err := getMetricsView(ctx, query.MetricsView) + if err != nil { + return nil, err + } + applyAdditionalTimeGrain(query, mv.GetMetricsView().State.ValidSpec, props.AdditionalTimeGrain) + } + // Set the additional timezone if provided if props.TimeZone != "" { query.TimeZone = props.TimeZone @@ -169,6 +188,41 @@ func applyAdditionalWhere(current, additional *metricsview.Expression) *metricsv } } +// applyAdditionalTimeGrain floors the query's time dimensions at the given grain. +// A dimension that already computes a time floor came from an explicit date_trunc in the metrics SQL, +// so it keeps the bucket its author asked for. +// The dimension's name is left untouched, so the result columns are the same as without a grain. +func applyAdditionalTimeGrain(query *metricsview.Query, spec *runtimev1.MetricsViewSpec, grain metricsview.TimeGrain) { + if spec == nil { + return + } + + for i, dim := range query.Dimensions { + if dim.Compute != nil { + continue + } + + // The primary time dimension is not necessarily declared in the dimensions list, so check it separately. + isTime := dim.Name == spec.TimeDimension + for _, d := range spec.Dimensions { + if d.Name == dim.Name { + isTime = d.Type == runtimev1.MetricsViewSpec_DIMENSION_TYPE_TIME + break + } + } + if !isTime { + continue + } + + query.Dimensions[i].Compute = &metricsview.DimensionCompute{ + TimeFloor: &metricsview.DimensionComputeTimeFloor{ + Dimension: dim.Name, + Grain: grain, + }, + } + } +} + // applyAdditionalTimeRange merges the existing time range with the additional time range func applyAdditionalTimeRange(current, additional *metricsview.TimeRange) *metricsview.TimeRange { if current == nil { diff --git a/runtime/resolvers/metrics_sql_test.go b/runtime/resolvers/metrics_sql_test.go index e98195f62081..02e920d31630 100644 --- a/runtime/resolvers/metrics_sql_test.go +++ b/runtime/resolvers/metrics_sql_test.go @@ -281,3 +281,70 @@ func TestMetricsSQLWithAdditionalTimeZone(t *testing.T) { require.Error(t, err, "Should error for invalid timezone") } + +func TestMetricsSQLWithAdditionalTimeGrain(t *testing.T) { + rt, instanceID := testruntime.NewInstanceForProject(t, "ad_bids") + + testruntime.RequireParseErrors(t, rt, instanceID, nil) + + resolve := func(props map[string]any) []map[string]any { + res, _, err := rt.Resolve(context.Background(), &runtime.ResolveOptions{ + InstanceID: instanceID, + Resolver: "metrics_sql", + ResolverProperties: props, + Args: nil, + Claims: &runtime.SecurityClaims{}, + }) + require.NoError(t, err) + defer res.Close() + + var rows []map[string]any + require.NoError(t, json.Unmarshal(must(res.MarshalJSON()), &rows)) + require.Greater(t, len(rows), 0) + return rows + } + + // Timestamps of the rows, requiring the time dimension to keep its name in the result. + timestamps := func(rows []map[string]any) []time.Time { + var res []time.Time + for _, row := range rows { + raw, ok := row["timestamp"].(string) + require.True(t, ok, "expected a timestamp column, got %v", row) + ts, err := time.Parse(time.RFC3339, raw) + require.NoError(t, err) + res = append(res, ts) + } + return res + } + + const sql = "SELECT timestamp, bid_price FROM ad_bids_metrics ORDER BY timestamp LIMIT 500" + + // Without a grain, the time dimension is selected at its native resolution. + ungrouped := timestamps(resolve(map[string]any{"sql": sql})) + + // With a grain, the same SQL is bucketed. + daily := timestamps(resolve(map[string]any{"sql": sql, "additional_time_grain": "day"})) + require.Less(t, len(daily), len(ungrouped)) + for _, ts := range daily { + require.Equal(t, ts.Truncate(24*time.Hour), ts, "expected a day-floored timestamp") + } + + // A grain declared in the SQL is the author's choice and is not overridden. + monthly := timestamps(resolve(map[string]any{ + "sql": "SELECT date_trunc('month', timestamp) AS timestamp, bid_price FROM ad_bids_metrics ORDER BY timestamp LIMIT 500", + "additional_time_grain": "day", + })) + require.Less(t, len(monthly), len(daily)) + for _, ts := range monthly { + require.Equal(t, 1, ts.Day(), "expected a month-floored timestamp") + } + + _, _, err := rt.Resolve(context.Background(), &runtime.ResolveOptions{ + InstanceID: instanceID, + Resolver: "metrics_sql", + ResolverProperties: map[string]any{"sql": sql, "additional_time_grain": "fortnight"}, + Args: nil, + Claims: &runtime.SecurityClaims{}, + }) + require.Error(t, err, "should error for an invalid time grain") +} diff --git a/runtime/server/canvases.go b/runtime/server/canvases.go index 0b8677974d0a..7aa0204afdf8 100644 --- a/runtime/server/canvases.go +++ b/runtime/server/canvases.go @@ -2,9 +2,12 @@ package server import ( "context" + "errors" runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1" "github.com/rilldata/rill/runtime" + "github.com/rilldata/rill/runtime/canvas" + "github.com/rilldata/rill/runtime/drivers" "github.com/rilldata/rill/runtime/parser" "github.com/rilldata/rill/runtime/pkg/observability" "github.com/rilldata/rill/runtime/server/auth" @@ -74,23 +77,46 @@ func (s *Server) ResolveComponent(ctx context.Context, req *runtimev1.ResolveCom return nil, err } - // Parse args + // Parse args and validate them against the component's declared params. + // Components without declared params accept arbitrary args for backwards compatibility. args := req.Args.AsMap() + if len(spec.Params) > 0 { + if err := canvas.ValidateParamBindings(spec.Params, args, nil); err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + } + + // Merge the declared param defaults (and legacy input variable defaults) under the provided args. + effectiveArgs := canvas.EffectiveArgs(spec, args) + + // Resolve the metrics view metadata of the bound fields, so a spec can title an axis with a + // measure's display name or format it with the measure's formatter. See canvas.FieldTemplateData. + metricsViews, err := s.boundMetricsViews(ctx, req.InstanceId, claims, spec.Params, effectiveArgs) + if err != nil { + return nil, err + } - // Setup templating data + // Setup templating data. + // The effective args are exposed both as .params (canonical, matching the YAML key) and .args (legacy alias). td := parser.TemplateData{ Environment: inst.Environment, User: claims.UserAttributes, Variables: inst.ResolveVariables(false), ExtraProps: map[string]any{ - "args": args, + "args": effectiveArgs, + "params": effectiveArgs, + "fields": canvas.FieldTemplateData(spec.Params, effectiveArgs, metricsViews), }, } - // Resolve templating in the renderer properties + // Resolve templating in the renderer properties. + // Numeric and boolean params are substituted before templating so they keep their type; + // see canvas.CoerceScalarParams. var rendererProps *structpb.Struct if spec.RendererProperties != nil { - v, err := parser.ResolveTemplateRecursively(spec.RendererProperties.AsMap(), td, false) + props := canvas.CoerceScalarParams(spec.RendererProperties.AsMap(), spec.Params, effectiveArgs) + + v, err := parser.ResolveTemplateRecursively(props, td, false) if err != nil { return nil, status.Errorf(codes.InvalidArgument, "failed to resolve renderer properties: %s", err.Error()) } @@ -100,14 +126,70 @@ func (s *Server) ResolveComponent(ctx context.Context, req *runtimev1.ResolveCom return nil, status.Errorf(codes.Internal, "failed to convert resolved renderer properties to map: %v", v) } + // For custom charts, inject scalar param values as native Vega-Lite params into the vega_spec. + if spec.Renderer == "custom_chart" { + if vegaSpec, ok := props["vega_spec"].(string); ok { + injected, err := canvas.InjectVegaParams(vegaSpec, spec.Params, effectiveArgs) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "failed to inject params into vega_spec: %s", err.Error()) + } + props["vega_spec"] = injected + } + } + rendererProps, err = structpb.NewStruct(props) if err != nil { return nil, status.Errorf(codes.Internal, "failed to convert renderer properties to struct: %s", err.Error()) } } + resolvedArgs, err := structpb.NewStruct(effectiveArgs) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to convert resolved args to struct: %s", err.Error()) + } + // Return the response return &runtimev1.ResolveComponentResponse{ RendererProperties: rendererProps, + ResolvedArgs: resolvedArgs, }, nil } + +// boundMetricsViews loads the valid specs of the metrics views bound to a component's +// metrics_view params, with the caller's security policy applied. +// Metrics views that don't exist or that the caller cannot access are omitted. +func (s *Server) boundMetricsViews(ctx context.Context, instanceID string, claims *runtime.SecurityClaims, params []*runtimev1.ComponentParam, args map[string]any) (map[string]*runtimev1.MetricsViewSpec, error) { + ctrl, err := s.runtime.Controller(ctx, instanceID) + if err != nil { + return nil, err + } + + res := make(map[string]*runtimev1.MetricsViewSpec) + for _, p := range params { + if p.Type != "metrics_view" { + continue + } + name, ok := args[p.Name].(string) + if !ok || name == "" || res[name] != nil { + continue + } + mvRes, err := ctrl.Get(ctx, &runtimev1.ResourceName{Kind: runtime.ResourceKindMetricsView, Name: name}, false) + if err != nil { + if errors.Is(err, drivers.ErrResourceNotFound) { + continue + } + return nil, err + } + mvRes, access, err := s.runtime.ApplySecurityPolicy(ctx, instanceID, claims, mvRes) + if err != nil { + return nil, err + } + if !access { + continue + } + if spec := mvRes.GetMetricsView().State.ValidSpec; spec != nil { + res[name] = spec + } + } + return res, nil +} diff --git a/runtime/server/canvases_test.go b/runtime/server/canvases_test.go index 7bcdb7bbb5a6..06c434bfb2d4 100644 --- a/runtime/server/canvases_test.go +++ b/runtime/server/canvases_test.go @@ -2,6 +2,7 @@ package server_test import ( "context" + "encoding/json" "testing" runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1" @@ -13,6 +14,8 @@ import ( "github.com/rilldata/rill/runtime/testruntime" "github.com/stretchr/testify/require" "go.uber.org/zap" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/structpb" ) @@ -666,3 +669,417 @@ func must[T any](v T, err error) T { } return v } + +func TestResolveComponentWithParams(t *testing.T) { + rt, instanceID := testruntime.NewInstanceWithOptions(t, testruntime.InstanceOptions{ + Files: map[string]string{ + "rill.yaml": "", + "m1.sql": `SELECT 'US' AS country`, + "mv1.yaml": ` +type: metrics_view +version: 1 +model: m1 +dimensions: +- column: country +measures: +- name: count + expression: COUNT(*) +`, + "trend.yaml": ` +type: component +params: + - name: metrics_view + type: metrics_view + required: true + - name: measure + type: measure + required: true + - name: limit + type: number + default: 500 + - name: smooth + type: boolean + default: false +custom_chart: + metrics_sql: "SELECT country, {{ .params.measure }} AS value FROM {{ .params.metrics_view }} LIMIT {{ .params.limit }}" + spec: + chartType: Line Chart + encodings: + x: {field: country} + y: {field: "{{ .params.measure }}"} + chartProperties: + pointCount: "{{ .params.limit }}" + showPoints: "{{ .params.smooth }}" + label: "Top {{ .params.limit }}" +`, + }, + }) + testruntime.RequireReconcileState(t, rt, instanceID, 4, 0, 0) + + server, err := server.NewServer(context.Background(), &server.Options{}, rt, zap.NewNop(), ratelimit.NewNoop(), activity.NewNoopClient()) + require.NoError(t, err) + + args, err := structpb.NewStruct(map[string]any{ + "metrics_view": "mv1", + "measure": "count", + "smooth": false, + }) + require.NoError(t, err) + + res, err := server.ResolveComponent(testCtx(), &runtimev1.ResolveComponentRequest{ + InstanceId: instanceID, + Component: "trend", + Args: args, + }) + require.NoError(t, err) + + // Templating should resolve provided args and declared defaults. + props := res.RendererProperties.AsMap() + require.Equal(t, "SELECT country, count AS value FROM mv1 LIMIT 500", props["metrics_sql"]) + + // Field params are templated into the spec as strings. + spec := props["spec"].(map[string]any) + encodings := spec["encodings"].(map[string]any) + require.Equal(t, "count", encodings["y"].(map[string]any)["field"]) + + // Numeric and boolean params keep their type when a value is exactly one placeholder, + // so Flint receives 500 and false rather than "500" and "false". + chartProps := spec["chartProperties"].(map[string]any) + require.Equal(t, float64(500), chartProps["pointCount"]) + require.Equal(t, false, chartProps["showPoints"]) + + // Partial interpolations remain strings. + require.Equal(t, "Top 500", chartProps["label"]) + + // The resolved args should contain the merged defaults and provided args. + require.Equal(t, map[string]any{ + "metrics_view": "mv1", + "measure": "count", + "limit": float64(500), + "smooth": false, + }, res.ResolvedArgs.AsMap()) + + // Missing required arg should return InvalidArgument. + args, err = structpb.NewStruct(map[string]any{"metrics_view": "mv1"}) + require.NoError(t, err) + _, err = server.ResolveComponent(testCtx(), &runtimev1.ResolveComponentRequest{ + InstanceId: instanceID, + Component: "trend", + Args: args, + }) + require.Error(t, err) + require.Equal(t, codes.InvalidArgument, status.Code(err)) + require.ErrorContains(t, err, `missing value for required param "measure"`) + + // Unknown arg should return InvalidArgument. + args, err = structpb.NewStruct(map[string]any{"metrics_view": "mv1", "measure": "count", "nope": 1}) + require.NoError(t, err) + _, err = server.ResolveComponent(testCtx(), &runtimev1.ResolveComponentRequest{ + InstanceId: instanceID, + Component: "trend", + Args: args, + }) + require.Error(t, err) + require.Equal(t, codes.InvalidArgument, status.Code(err)) + require.ErrorContains(t, err, `unknown param "nope"`) +} + +func TestResolveEjectedComponent(t *testing.T) { + rt, instanceID := testruntime.NewInstanceWithOptions(t, testruntime.InstanceOptions{ + Files: map[string]string{ + "rill.yaml": "", + "m1.sql": `SELECT 'US' AS country, 1 AS amount`, + "mv1.yaml": ` +type: metrics_view +version: 1 +model: m1 +dimensions: +- column: country + display_name: Country +measures: +- name: revenue + display_name: Total Revenue + expression: SUM(amount) + format_preset: currency_usd +`, + // What ejecting a chart spec produces: the Vega-Lite it compiled to, with the bound + // values turned back into references to the params and their field metadata. + "trend.yaml": ` +type: component +params: + - name: metrics_view + type: metrics_view + required: true + - name: dim + type: dimension + required: true + - name: measure + type: measure + required: true +custom_chart: + metrics_sql: "SELECT {{ .params.dim }}, {{ .params.measure }} FROM {{ .params.metrics_view }}" + vega_spec: | + { + "mark": "bar", + "encoding": { + "x": {"field": "{{ .params.dim }}", "title": "{{ .fields.dim.display_name }}"}, + "y": { + "field": "{{ .params.measure }}", + "title": "{{ .fields.measure.display_name }}", + "formatType": "{{ .fields.measure.format_type }}", + "format": "{{ .fields.measure.format_d3 }}" + } + }, + "data": {"name": "query1"} + } +`, + }, + }) + testruntime.RequireReconcileState(t, rt, instanceID, 4, 0, 0) + + server, err := server.NewServer(context.Background(), &server.Options{}, rt, zap.NewNop(), ratelimit.NewNoop(), activity.NewNoopClient()) + require.NoError(t, err) + + args, err := structpb.NewStruct(map[string]any{ + "metrics_view": "mv1", + "dim": "country", + "measure": "revenue", + }) + require.NoError(t, err) + + res, err := server.ResolveComponent(testCtx(), &runtimev1.ResolveComponentRequest{ + InstanceId: instanceID, + Component: "trend", + Args: args, + }) + require.NoError(t, err) + + props := res.RendererProperties.AsMap() + require.Equal(t, "SELECT country, revenue FROM mv1", props["metrics_sql"]) + + var vegaSpec struct { + Encoding struct { + X struct { + Field string `json:"field"` + Title string `json:"title"` + } `json:"x"` + Y struct { + Field string `json:"field"` + Title string `json:"title"` + FormatType string `json:"formatType"` + Format string `json:"format"` + } `json:"y"` + } `json:"encoding"` + } + require.NoError(t, json.Unmarshal([]byte(props["vega_spec"].(string)), &vegaSpec)) + + require.Equal(t, "country", vegaSpec.Encoding.X.Field) + require.Equal(t, "Country", vegaSpec.Encoding.X.Title) + require.Equal(t, "revenue", vegaSpec.Encoding.Y.Field) + require.Equal(t, "Total Revenue", vegaSpec.Encoding.Y.Title) + require.Equal(t, "rill_revenue", vegaSpec.Encoding.Y.FormatType) + // The measure formats from a preset rather than a d3 string, so there is nothing to substitute. + require.Empty(t, vegaSpec.Encoding.Y.Format) +} + +// TestResolveEjectedComponentFromAgentInstructions runs the exact spec shape that the component +// authoring instructions tell the developer agent to write when it ejects a chart spec +// (runtime/ai/instructions/data/resources/component.md), so the two cannot drift apart. +func TestResolveEjectedComponentFromAgentInstructions(t *testing.T) { + rt, instanceID := testruntime.NewInstanceWithOptions(t, testruntime.InstanceOptions{ + Files: map[string]string{ + "rill.yaml": "", + "m1.sql": `SELECT NOW() AS ts, 1 AS amount`, + "mv1.yaml": ` +type: metrics_view +version: 1 +model: m1 +timeseries: ts +measures: +- name: revenue + display_name: Total Revenue + expression: SUM(amount) + format_preset: currency_usd +`, + "trend.yaml": ` +type: component +display_name: Measure trend +params: + - name: metrics_view + type: metrics_view + required: true + - name: measure + type: measure + required: true + - name: time_dim + type: time_dimension + required: true + - name: limit + type: number + default: 500 +custom_chart: + metrics_sql: | + SELECT {{ .params.time_dim }}, {{ .params.measure }} + FROM {{ .params.metrics_view }} + ORDER BY {{ .params.time_dim }} + LIMIT {{ .params.limit }} + vega_spec: | + { + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "width": "container", + "height": "container", + "autosize": {"type": "fit"}, + "data": {"name": "query1"}, + "mark": {"type": "line", "interpolate": "monotone", "point": true}, + "encoding": { + "x": { + "field": "{{ .params.time_dim }}", + "type": "temporal", + "title": "{{ .fields.time_dim.display_name }}" + }, + "y": { + "field": "{{ .params.measure }}", + "type": "quantitative", + "title": "{{ .fields.measure.display_name }}", + "axis": {"formatType": "{{ .fields.measure.format_type }}"} + }, + "tooltip": [ + { + "field": "{{ .params.measure }}", + "type": "quantitative", + "title": "{{ .fields.measure.display_name }}", + "formatType": "{{ .fields.measure.format_type }}" + } + ] + } + } +`, + }, + }) + testruntime.RequireReconcileState(t, rt, instanceID, 4, 0, 0) + + server, err := server.NewServer(context.Background(), &server.Options{}, rt, zap.NewNop(), ratelimit.NewNoop(), activity.NewNoopClient()) + require.NoError(t, err) + + args, err := structpb.NewStruct(map[string]any{ + "metrics_view": "mv1", + "measure": "revenue", + "time_dim": "ts", + }) + require.NoError(t, err) + + res, err := server.ResolveComponent(testCtx(), &runtimev1.ResolveComponentRequest{ + InstanceId: instanceID, + Component: "trend", + Args: args, + }) + require.NoError(t, err) + + props := res.RendererProperties.AsMap() + vegaSpec := props["vega_spec"].(string) + + // Nothing may be left for Vega to choke on: an unresolved placeholder reaches it as a field + // name or inside a tooltip expression. + require.NotContains(t, vegaSpec, "{{") + + var spec map[string]any + require.NoError(t, json.Unmarshal([]byte(vegaSpec), &spec)) + require.Equal(t, "container", spec["width"]) + require.Equal(t, "container", spec["height"]) + require.Equal(t, map[string]any{"type": "fit"}, spec["autosize"]) + require.Equal(t, map[string]any{"name": "query1"}, spec["data"]) + + encoding := spec["encoding"].(map[string]any) + x := encoding["x"].(map[string]any) + y := encoding["y"].(map[string]any) + require.Equal(t, "ts", x["field"]) + require.Equal(t, "Time", x["title"]) + require.Equal(t, "revenue", y["field"]) + require.Equal(t, "Total Revenue", y["title"]) + require.Equal(t, "rill_revenue", y["axis"].(map[string]any)["formatType"]) + + // The scalar param is injected as a native Vega-Lite param, as the instructions state. + params := spec["params"].([]any) + require.Contains(t, params, map[string]any{"name": "limit", "value": float64(500)}) +} + +func TestResolveCanvasWithParamBoundMetricsView(t *testing.T) { + rt, instanceID := testruntime.NewInstanceWithOptions(t, testruntime.InstanceOptions{ + Files: map[string]string{ + "rill.yaml": "", + "m1.sql": `SELECT 'US' AS country`, + "mv1.yaml": ` +type: metrics_view +version: 1 +model: m1 +dimensions: +- column: country +measures: +- name: count + expression: COUNT(*) +`, + "trend.yaml": ` +type: component +params: + - name: metrics_view + type: metrics_view + required: true + - name: measure + type: measure + required: true +custom_chart: + metrics_sql: "SELECT country, {{ .params.measure }} AS value FROM {{ .params.metrics_view }}" + spec: + chartType: Line Chart + encodings: + x: {field: ts} + y: {field: value} +`, + // Built-in renderer with a templated metrics_view: ResolveCanvas must not + // treat the raw template string as a metrics view resource name. + "tplkpi.yaml": ` +type: component +params: + - name: metrics_view + type: metrics_view + required: true +kpi: + metrics_view: "{{ .params.metrics_view }}" +`, + "c1.yaml": ` +type: canvas +rows: +- items: + - component: trend + params: + metrics_view: mv1 + measure: count + - component: tplkpi + params: + metrics_view: mv1 +`, + }, + }) + testruntime.RequireReconcileState(t, rt, instanceID, 6, 0, 0) + + server, err := server.NewServer(context.Background(), &server.Options{}, rt, zap.NewNop(), ratelimit.NewNoop(), activity.NewNoopClient()) + require.NoError(t, err) + + res, err := server.ResolveCanvas(testCtx(), &runtimev1.ResolveCanvasRequest{ + InstanceId: instanceID, + Canvas: "c1", + }) + require.NoError(t, err) + + // The referenced components should be returned, and the param-bound metrics view + // should be included in the referenced metrics views. + require.Contains(t, res.ResolvedComponents, "trend") + require.Contains(t, res.ResolvedComponents, "tplkpi") + require.Contains(t, res.ReferencedMetricsViews, "mv1") + require.Len(t, res.ReferencedMetricsViews, 1) + + // The canvas items should carry the param bindings. + items := res.Canvas.GetCanvas().State.ValidSpec.Rows[0].Items + require.Len(t, items, 2) + require.Equal(t, map[string]any{"metrics_view": "mv1", "measure": "count"}, items[0].Params.AsMap()) +} diff --git a/web-common/package.json b/web-common/package.json index bb6c1aafe2e7..77a0fbca4b5d 100644 --- a/web-common/package.json +++ b/web-common/package.json @@ -110,6 +110,7 @@ "dependencies": { "@internationalized/date": "^3.10.1", "@tiptap/suggestion": "^3.20.1", + "flint-chart": "0.4.1", "picomatch": "^4.0.3", "vega-embed": "^7.1.0" } diff --git a/web-common/scripts/generate-flint-catalog.ts b/web-common/scripts/generate-flint-catalog.ts new file mode 100644 index 000000000000..6a56056eb162 --- /dev/null +++ b/web-common/scripts/generate-flint-catalog.ts @@ -0,0 +1,552 @@ +/** + * Generates the custom viz gallery from Flint's own chart registry and example set. + * + * Run manually after bumping the flint-chart dependency: + * + * npx tsx web-common/scripts/generate-flint-catalog.ts + * + * The gallery used to be scraped from the Vega-Lite examples site. It is now derived from the + * installed package, so it cannot drift from the version we render with. + * + * Flint's example set is its rendering test suite: alongside the curated examples behind its own + * gallery (https://microsoft.github.io/flint-chart/#/gallery/vegalite) it carries a combinatorial + * matrix (the same chart at 20/100/1800 points, with and without color) and deliberately adversarial + * cases (axis overflow, degenerate single-series, warning paths). A picker wants neither, so this + * takes the curated examples first and drops anything tagged adversarial; see pickExamples. + * + * Each example is compiled and rendered to an SVG thumbnail here, at build time, so the gallery + * grid stays instant and needs no chart engine. The SVGs are inlined into the index as data URIs + * rather than written as sibling files: web-common is a library the apps alias in from outside their + * own root ("@rilldata/web-common": "/../web-common/src" in web-local/vite.config.ts, with + * server.fs.allow scoped to "."), so an asset URL Vite resolves against web-common 404s when + * web-local or web-admin serves the dialog. A data URI belongs to the module, so it just works. + * + * Field roles are resolved from each example's declared semantic types via Flint's registry, then + * mapped onto Rill's param types (measure / dimension / time_dimension). + */ + +import { createRequire } from "node:module"; +import { readFileSync, rmSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; +import * as vega from "vega"; +import { compile } from "vega-lite"; +import { + assembleVegaLite, + vlAllTemplateDefs, + vlGetTemplateDef, + vlTemplateDefs, +} from "flint-chart/vegalite"; +import { getRegistryEntry, SemanticTypes } from "flint-chart/core"; +import { TEST_GENERATORS } from "flint-chart/test-data"; + +const require = createRequire(import.meta.url); + +const EXAMPLES_DIR = join( + import.meta.dirname, + "../src/features/custom-viz/examples", +); +const OUT_PATH = join(EXAMPLES_DIR, "flint-charts-index.json"); + +/** Thumbnail render box. Small enough that Flint drops dense examples down to a readable summary. */ +const THUMBNAIL_SIZE = { width: 240, height: 150 }; + +/** Keeps one chart type from flooding the grid, and bounds the vendored SVG payload. */ +const MAX_PER_CHART_TYPE = 3; + +/** + * Tags marking an example as a rendering test rather than a starter: it either exceeds a layout + * budget on purpose, exercises a degenerate shape, or asserts a warning fires. Showing one in the + * picker would offer a chart that looks broken as a starting point. + */ +const ADVERSARIAL_TAGS = new Set([ + "stress", + "overflow", + "cutoff", + "overstretch", + "crowded", + "very-large", + "edge", + "edge-case", + "degenerate", + "single-row", + "warning", + "dtype-conversion", + "sparse", + "ambiguous", + "high-cardinality", + "redundant-color", + "redundant-group", + "dodge-global", + "dodge-local", +]); + +/** + * Titles from the internal suite that name a matrix cell rather than a chart, e.g. "N(4)×Q + * +color(N,3) (12 pts)" or "Strip ▸ X ×20". Deprioritized behind prose titles: the derived title + * carries the card, but the original still shows in tooltips and feeds search. + */ +const SHORTHAND_TITLE = /[×▸]\s*\d|^[A-Z]\(|^(?:EC|Phase \d):|\(\d+ pts/; + +/** Skip a thumbnail that would bloat the index; the example is dropped rather than shipped blank. */ +const MAX_THUMBNAIL_BYTES = 60_000; + +/** + * MIT requires the copyright and permission notice to travel with substantial portions of the + * software. The example titles, descriptions and channel layouts here, and the thumbnails rendered + * from the sample data, are all derived from flint-chart, so the notice is recorded in the + * generated index alongside them. + */ +const LICENSE_NOTICE = + "Chart examples and thumbnails derived from flint-chart (https://github.com/microsoft/flint-chart), MIT License, Copyright (c) Microsoft Corporation."; +const SOURCE_URL = "https://github.com/microsoft/flint-chart"; + +type Role = "measure" | "dimension" | "time_dimension"; + +/** Chart types kept out of the gallery, each with the reason reported in the skip log. */ +const EXCLUDED_CHART_TYPES = new Map([ + // Rill never derives the semantics these need: our metrics view mapping emits Category for every + // non-temporal dimension, so geographic channels would silently degrade. + ["Map", "needs semantics Rill never derives"], + ["Choropleth", "needs semantics Rill never derives"], + ["KPI Card", "Rill has a native KPI widget"], +]); + +/** Semantic types Rill cannot produce; any example using one is skipped. */ +const UNSUPPORTED_SEMANTIC_TYPES = new Set(["Latitude", "Longitude"]); + +/** Param names by channel, describing the field's role in the chart. */ +const CHANNEL_PARAM_NAMES: Record = { + x: "x_axis", + y: "y_axis", + x2: "x_axis_end", + y2: "y_axis_end", + angle: "angle", + radius: "radius", + color: "color", + size: "size", + shape: "shape", + opacity: "opacity", + detail: "detail", + order: "order", + group: "group", + row: "row", + column: "column", + metric: "metric", + value: "value", + goal: "goal", + id: "region", + open: "open", + high: "high", + low: "low", + close: "close", +}; + +/** + * Row-count default by how much space each mark needs. Flint drops rows that overflow the axis + * budget and reports it as a warning, so these stay under it. + */ +const DENSE_CHARTS = new Set([ + "Scatter Plot", + "Regression", + "Connected Scatter Plot", + "Strip Plot", + "Heatmap", + "Histogram", + "Density Plot", + "ECDF Plot", + "Boxplot", + "Violin Plot", +]); +const TIME_SERIES_CHARTS = new Set([ + "Line Chart", + "Area Chart", + "Streamgraph", + "Sparkline", + "Range Area Chart", + "Bump Chart", + "Candlestick Chart", +]); + +interface FlintExample { + title: string; + description?: string; + tags?: string[]; + chartType: string; + data: Record[]; + metadata: Record; + encodingMap: Record; + chartProperties?: Record; +} + +interface GalleryExample { + name: string; + /** Slug of the chart type, used as the generated component's file name. */ + chartSlug: string; + chartType: string; + category: string; + /** Flint's own label for the example. Not shown on the card; it feeds search and the tooltip. */ + title: string; + description: string; + tags: string[]; + bindings: { channel: string; param: string; role: Role }[]; + chartProperties?: Record; + /** The rendered SVG as a data URI, ready to drop into an img src. */ + thumbnail: string; + defaultLimit: number; +} + +const registeredSemanticTypes = new Set(Object.keys(SemanticTypes)); + +/** + * Maps an example field onto a Rill param type. + * + * Flint's registry is the source of truth where it knows the type, but its example set uses several + * domain-flavoured names (GDP, Team, Value, ...) that are not registered and would otherwise all + * degrade to categorical. For those, the field's own data type decides. + */ +function roleFor(semanticType: string, dataType: string): Role | null { + if (UNSUPPORTED_SEMANTIC_TYPES.has(semanticType)) return null; + + if (!registeredSemanticTypes.has(semanticType)) { + return dataType === "number" ? "measure" : "dimension"; + } + + const entry = getRegistryEntry(semanticType) as + | { t0?: string; aggRole?: string } + | undefined; + if (!entry) return dataType === "number" ? "measure" : "dimension"; + + // A temporal field is an axis unless it is a quantity of time (Duration is additive). + if (entry.t0 === "Temporal") { + return entry.aggRole === "dimension" ? "time_dimension" : "measure"; + } + if (entry.t0 === "Measure") return "measure"; + // Score is intensive and numeric; Rank is an ordinal label. + if (entry.aggRole === "intensive" || entry.aggRole === "additive") { + return "measure"; + } + return "dimension"; +} + +function categoryOf(chart: string): string { + for (const [category, defs] of Object.entries( + vlTemplateDefs as unknown as Record, + )) { + if (defs.some((def) => def.chart === chart)) return category; + } + return "Other"; +} + +function defaultLimit(chartType: string): number { + if (TIME_SERIES_CHARTS.has(chartType)) return 2000; + if (DENSE_CHARTS.has(chartType)) return 100; + return 10; +} + +function slugify(value: string): string { + return value + .toLowerCase() + .replace(/[^a-z0-9]+/g, "_") + .replace(/^_|_$/g, ""); +} + +/** + * Ranks an example by how well it works as a starter. Flint's curated gallery examples come first, + * then the ones built on named real-world datasets (Palmer Penguins, the Keeling Curve), then + * anything else it pins to a gallery, then the remaining synthetic shapes. + */ +function starterRank(example: FlintExample, fromGalleryGenerator: boolean): number { + const tags = example.tags ?? []; + if (fromGalleryGenerator) return 0; + if (tags.includes("real")) return 1; + if (tags.includes("gallery") || tags.includes("gallery-pin")) return 2; + return SHORTHAND_TITLE.test(example.title) ? 4 : 3; +} + +/** + * Picks the examples to ship for one chart type: drops the adversarial cases and the ones encoding a + * channel this type does not declare, dedupes examples registered under more than one generator key, + * then keeps the best-ranked few. + */ +function pickExamples( + candidates: { example: FlintExample; fromGalleryGenerator: boolean }[], + declaredChannels: string[], +): { example: FlintExample; fromGalleryGenerator: boolean }[] { + const seenTitles = new Set(); + return candidates + .filter( + ({ example }) => + !(example.tags ?? []).some((tag) => ADVERSARIAL_TAGS.has(tag)), + ) + // Some examples in the cross-backend suites encode a channel this chart type does not declare + // (a grouped bar with both color and group). Flint's renderer drops it, so binding it would + // declare a param that never reaches the chart. Filtering here lets a valid example take the + // slot instead of leaving the chart type one card short. + .filter(({ example }) => + Object.keys(example.encodingMap).every((channel) => + declaredChannels.includes(channel), + ), + ) + .sort( + (a, b) => + starterRank(a.example, a.fromGalleryGenerator) - + starterRank(b.example, b.fromGalleryGenerator), + ) + .filter(({ example }) => { + if (seenTitles.has(example.title)) return false; + seenTitles.add(example.title); + return true; + }) + .slice(0, MAX_PER_CHART_TYPE); +} + +/** Compiles an example to Vega-Lite and rasterizes it to a standalone SVG string. */ +async function renderThumbnail( + example: FlintExample, + encodings: Record, +): Promise { + const semantic_types = Object.fromEntries( + Object.entries(example.metadata).map(([field, meta]) => [ + field, + meta.semanticType, + ]), + ); + + const spec = assembleVegaLite({ + data: { values: example.data }, + semantic_types, + chart_spec: { + chartType: example.chartType, + encodings, + baseSize: THUMBNAIL_SIZE, + canvasSize: THUMBNAIL_SIZE, + ...(example.chartProperties + ? { chartProperties: example.chartProperties } + : {}), + }, + }) as Record; + + // Flint's bookkeeping keys are not valid Vega-Lite. + for (const key of Object.keys(spec)) { + if (key.startsWith("_")) delete spec[key]; + } + + // A thumbnail reads as a shape, not a chart you can look values up in. Suppressing tick labels, + // titles and legends keeps the mark geometry legible at 240x150 and drops the text elements that + // otherwise dominate the SVG. + spec.config = { + ...((spec.config as Record) ?? {}), + axis: { labels: false, title: null, ticks: false, domainColor: "#d0d5dd" }, + legend: { disable: true }, + // Faceted examples (violin grids, small multiples) label each panel separately. + header: { labels: false, title: null }, + }; + // Flint sets some axis titles on the encoding itself, which wins over config. + stripEncodingTitles(spec); + + const view = new vega.View(vega.parse(compile(spec as never).spec), { + renderer: "none", + }); + return minifySvg(await view.toSVG()); +} + +/** Nulls out per-encoding axis titles so a thumbnail carries no text of its own. */ +function stripEncodingTitles(node: unknown): void { + if (Array.isArray(node)) { + node.forEach(stripEncodingTitles); + return; + } + if (!node || typeof node !== "object") return; + const record = node as Record; + if (typeof record.field === "string" && "title" in record) { + record.title = null; + } + Object.values(record).forEach(stripEncodingTitles); +} + +/** + * Vega emits full float precision, which dominates the size of a dense thumbnail: a 500-point + * scatter is mostly digits past the decimal point that no one can see at 240x150. Rounding to two + * places roughly halves the payload and is visually lossless at this scale. + */ +function minifySvg(svg: string): string { + return svg.replace(/-?\d+\.\d{3,}/g, (match) => + String(Math.round(Number(match) * 100) / 100), + ); +} + +/** + * Wraps an SVG as a data URI for an img src. + * + * Percent-encoding only the characters that would terminate the URI or the surrounding attribute + * keeps the markup readable and roughly a third smaller than base64, which inflates by 4/3 and + * defeats gzip on text this repetitive. + */ +function svgDataURI(svg: string): string { + const escaped = svg + .replace(/%/g, "%25") + .replace(/#/g, "%23") + .replace(/"/g, "%22") + .replace(/&/g, "%26") + .replace(//g, "%3E") + // A raw newline is legal in an attribute but not in a URI. + .replace(/\s*\n\s*/g, " "); + return `data:image/svg+xml,${escaped}`; +} + +async function main() { + // flint-chart does not export ./package.json, so resolve it via a subpath we can import. + const { version: flintVersion } = JSON.parse( + readFileSync( + join(require.resolve("flint-chart/vegalite"), "../../../package.json"), + "utf8", + ), + ) as { version: string }; + + // Drop the sibling SVG directory earlier revisions emitted; the thumbnails are inlined now. + rmSync(join(EXAMPLES_DIR, "thumbnails"), { recursive: true, force: true }); + + const chartTypes = new Set( + (vlAllTemplateDefs as { chart: string }[]).map((def) => def.chart), + ); + + const examples: GalleryExample[] = []; + const skipped: string[] = []; + const usedNames = new Set(); + let thumbnailBytes = 0; + + // Group every generated example by its own chart type rather than by generator key. Flint's + // curated examples live under "Gallery: Bar"-style keys that are not chart type names, so keying + // off the generator would drop exactly the ones a picker wants. + const candidates = new Map< + string, + { example: FlintExample; fromGalleryGenerator: boolean }[] + >(); + + for (const [key, generate] of Object.entries(TEST_GENERATORS)) { + let generated: FlintExample[]; + try { + generated = (generate as () => FlintExample[])(); + } catch (error) { + skipped.push(`${key} — generator threw: ${String(error)}`); + continue; + } + + for (const example of generated) { + // Other backends and internal layout suites. + if (!chartTypes.has(example.chartType)) continue; + if (EXCLUDED_CHART_TYPES.has(example.chartType)) continue; + + const list = candidates.get(example.chartType) ?? []; + list.push({ example, fromGalleryGenerator: key.startsWith("Gallery:") }); + candidates.set(example.chartType, list); + } + } + + for (const [chartType, reason] of EXCLUDED_CHART_TYPES) { + skipped.push(`${chartType} — excluded: ${reason}`); + } + + for (const [chartType, forChartType] of candidates) { + const category = categoryOf(chartType); + const { channels: declaredChannels } = vlGetTemplateDef(chartType) as { + channels: string[]; + }; + + for (const { example } of pickExamples(forChartType, declaredChannels)) { + // Resolve every encoded channel to a Rill param role; skip the example if any field + // uses semantics Rill cannot produce, rather than shipping a dead-end starter. + const bindings: GalleryExample["bindings"] = []; + const encodings: Record = {}; + let usable = true; + + for (const [channel, encoding] of Object.entries(example.encodingMap)) { + // A channel bound to several fields (static-series fold) has no single param. + if (Array.isArray(encoding)) { + usable = false; + break; + } + const field = encoding.fieldID; + const meta = example.metadata[field]; + if (!meta) { + usable = false; + break; + } + const role = roleFor(meta.semanticType, meta.type); + if (!role) { + usable = false; + break; + } + bindings.push({ + channel, + param: CHANNEL_PARAM_NAMES[channel] ?? channel, + role, + }); + encodings[channel] = { field }; + } + + if (!usable || bindings.length === 0) continue; + + let svg: string; + try { + svg = await renderThumbnail(example, encodings); + } catch (error) { + skipped.push( + `${chartType} / ${example.title} — render failed: ${String(error)}`, + ); + continue; + } + if (svg.length > MAX_THUMBNAIL_BYTES) { + skipped.push( + `${chartType} / ${example.title} — thumbnail too large (${svg.length} bytes)`, + ); + continue; + } + + let name = `${slugify(chartType)}__${slugify(example.title)}`; + for (let i = 2; usedNames.has(name); i++) { + name = `${slugify(chartType)}__${slugify(example.title)}_${i}`; + } + usedNames.add(name); + + thumbnailBytes += svg.length; + + examples.push({ + name, + chartSlug: slugify(chartType), + chartType, + category, + title: example.title, + description: example.description ?? "", + tags: example.tags ?? [], + bindings, + ...(example.chartProperties + ? { chartProperties: example.chartProperties } + : {}), + thumbnail: svgDataURI(svg), + defaultLimit: defaultLimit(chartType), + }); + } + } + + writeFileSync( + OUT_PATH, + `${JSON.stringify({ license: LICENSE_NOTICE, source: SOURCE_URL, flintVersion, examples }, null, 2)}\n`, + ); + + const byChartType = new Set(examples.map((e) => e.chartType)); + console.log( + `Wrote ${examples.length} examples across ${byChartType.size} chart types to ${OUT_PATH}`, + ); + console.log( + `Thumbnails: ${examples.length} SVGs inlined, ${(thumbnailBytes / 1024).toFixed(0)} KB total`, + ); + if (skipped.length) { + console.log(`Skipped ${skipped.length}:`); + for (const entry of skipped.slice(0, 20)) console.log(` - ${entry}`); + if (skipped.length > 20) console.log(` ... and ${skipped.length - 20} more`); + } +} + +await main(); diff --git a/web-common/src/components/vega/vega-config.ts b/web-common/src/components/vega/vega-config.ts index e6d1a3c3b256..e607d785d0e7 100644 --- a/web-common/src/components/vega/vega-config.ts +++ b/web-common/src/components/vega/vega-config.ts @@ -102,6 +102,7 @@ export const getRillTheme: ( bar: { fill: barColor, }, + text: { fill: axisLabelColor }, line: { stroke: lineColor, strokeWidth: 1.5, strokeOpacity: 1 }, path: { stroke: lineColor }, rect: { fill: lineColor }, diff --git a/web-common/src/features/canvas/AddComponentDropdown.svelte b/web-common/src/features/canvas/AddComponentDropdown.svelte index 449ff7d2ca6a..af002759413f 100644 --- a/web-common/src/features/canvas/AddComponentDropdown.svelte +++ b/web-common/src/features/canvas/AddComponentDropdown.svelte @@ -4,8 +4,13 @@ CHART_CONFIG, VISIBLE_CHART_TYPES, } from "@rilldata/web-common/features/components/charts/config"; + import { + ResourceKind, + useFilteredResources, + } from "@rilldata/web-common/features/entity-management/resource-selectors"; import { featureFlags } from "@rilldata/web-common/features/feature-flags"; import { m } from "@rilldata/web-common/lib/i18n/gen/messages"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { Layers, Plus, PlusCircle } from "lucide-svelte"; import type { ComponentType, SvelteComponent } from "svelte"; import type { ChartType } from "../components/charts/types"; @@ -40,13 +45,28 @@ export let rowIndex: number | undefined = undefined; export let columnIndex: number | undefined = undefined; export let onItemClick: (type: CanvasComponentType) => void; + // When provided (and the customComponents flag is on), the menu lists the project's + // standalone components (custom viz) so they can be added as references. + export let onAddComponentRef: ((componentName: string) => void) | undefined = + undefined; export let onMouseEnter: () => void = () => {}; export let onOpenChange: (isOpen: boolean) => void = () => {}; // When provided, the menu offers "Tab group" as a final item. Only passed at the // top level (tab groups cannot be nested inside a tab or a column). export let onAddTabGroup: (() => void) | undefined = undefined; - const { customCharts } = featureFlags; + const { customCharts, customComponents } = featureFlags; + + const client = useRuntimeClient(); + + $: componentsQuery = useFilteredResources(client, ResourceKind.Component); + // Standalone (custom viz) components only: inline canvas components are excluded. + $: customVizComponents = ($componentsQuery?.data ?? []).filter( + (res) => + !res.component?.spec?.definedInCanvas && + !!res.meta?.name?.name && + !res.meta.name.name.includes("--component-"), + ); const ADD_DROPDOWN_CHART_TYPES = VISIBLE_CHART_TYPES.filter((type) => { return type !== "stacked_bar" && type !== "stacked_bar_normalized"; @@ -162,6 +182,31 @@ {/if} {/each} + {#if $customComponents && onAddComponentRef} + + + + + {m.canvas_your_components()} + + + {#each customVizComponents as res (res.meta?.name?.name)} + onAddComponentRef?.(res.meta?.name?.name ?? "")} + > + + {res.component?.spec?.displayName || res.meta?.name?.name} + + {:else} +
+ {m.canvas_no_components_yet()} +
+ {/each} +
+
+ {/if} + {#if onAddTabGroup} !!res.meta?.name?.name) + .map((res) => [res.meta!.name!.name as string, res]), + ); + $: ({ editorContent, updateEditorContent } = fileArtifact); $: contents = parseDocument($editorContent ?? ""); @@ -200,7 +217,9 @@ dragComponent = component; const id = component.id; - const element = document.querySelector("#" + id); + // getElementById, not querySelector: instance ids of referenced components + // contain "::", which is not valid in a CSS selector. + const element = document.getElementById(id); if (!element) return; const width = element.clientWidth; @@ -349,14 +368,17 @@ defaultMetrics, resolvedComponents, transaction, + componentResources: componentResourceMap, }); if (selectedId) { - const idsAtPositions = specRows.map((row) => { + // Selection ids are per-position instance ids (which for items that + // reference an external component differ from the item's component name). + const idsAtPositions = specRows.map((row, rowIdx) => { return { - items: row.items?.map((item) => { - return item.component ?? ""; - }), + items: row.items?.map((item, colIdx) => + canvasItemInstanceId(item, rowIdx, colIdx, namePrefix), + ), }; }); @@ -371,7 +393,9 @@ const colIndex = ids[rowIndex]?.items?.indexOf(selectedId); if (colIndex !== undefined && colIndex !== -1 && rowIndex !== -1) { - const newIdOfSelected = getId(rowIndex, colIndex, namePrefix); + const newIdOfSelected = selectedId.includes("::") + ? `${componentNameFromInstanceId(selectedId)}::${namePrefix}${rowIndex}-${colIndex}` + : getId(rowIndex, colIndex, namePrefix); if (selectedId && newIdOfSelected) setSelectedComponent(newIdOfSelected); @@ -386,16 +410,21 @@ function addItems( position: { row: number; column: number }, - items: CanvasComponentType[], + items: AddableItem[], target?: EditTarget, ) { if (!defaultMetrics) return; performTransaction( { - operations: items.map((type, i) => ({ + operations: items.map((item, i) => ({ type: "add", - componentType: type, + ...(typeof item === "string" + ? { componentType: item } + : { + componentType: "component_ref" as const, + componentName: item.componentName, + }), destination: { row: position.row, col: position.column + i, @@ -407,12 +436,31 @@ ); const { namePrefix } = resolveTarget(target); - const id = getId(position.row, position.column, namePrefix); + const id = getInstanceId( + items[0], + position.row, + position.column, + namePrefix, + ); setSelectedComponent(id); openSidebar(); } + // The client-side instance id an added item will get: the generated resource name + // for inline components, or the positional reference id for component references. + function getInstanceId( + item: AddableItem | undefined, + row: number, + column: number, + namePrefix = "", + ) { + if (item && typeof item !== "string") { + return `${item.componentName}::${namePrefix}${row}-${column}`; + } + return getId(row, column, namePrefix); + } + function updateContents() { const newContent = contents.toString(); if (newContent === $editorContent) { @@ -502,15 +550,11 @@ if (convertRowToTabGroup(contents, rowIndex)) updateContents(); } - function initializeRow( - row: number, - type: CanvasComponentType, - target?: EditTarget, - ) { + function initializeRow(row: number, item: AddableItem, target?: EditTarget) { if (!defaultMetrics) return; const { namePrefix } = resolveTarget(target); - const id = getId(row, 0, namePrefix); + const id = getInstanceId(item, row, 0, namePrefix); performTransaction( { @@ -518,7 +562,12 @@ { type: "add", insertRow: true, - componentType: type, + ...(typeof item === "string" + ? { componentType: item } + : { + componentType: "component_ref" as const, + componentName: item.componentName, + }), destination: { row, col: 0, @@ -865,6 +914,9 @@ onItemClick={(type) => { initializeRow(specCanvasRows.length, type); }} + onAddComponentRef={(componentName) => { + initializeRow(specCanvasRows.length, { componentName }); + }} onAddTabGroup={addTabGroupAction} /> {:else if canvasData} @@ -884,6 +936,10 @@ initializeRow(specCanvasRows.length, type); setTimeout(() => scrollToBottom(), 500); }} + onAddComponentRef={(componentName) => { + initializeRow(specCanvasRows.length, { componentName }); + setTimeout(() => scrollToBottom(), 500); + }} onAddTabGroup={addTabGroupAction} /> {/if} diff --git a/web-common/src/features/canvas/EditableCanvasRow.svelte b/web-common/src/features/canvas/EditableCanvasRow.svelte index 420d02a8a913..53d60209e121 100644 --- a/web-common/src/features/canvas/EditableCanvasRow.svelte +++ b/web-common/src/features/canvas/EditableCanvasRow.svelte @@ -6,7 +6,6 @@ import CanvasComponent from "./CanvasComponent.svelte"; import type { BaseCanvasComponent } from "./components/BaseCanvasComponent"; import DropZone from "./components/DropZone.svelte"; - import type { CanvasComponentType } from "./components/types"; import ElementDivider from "./ElementDivider.svelte"; import ItemWrapper from "./ItemWrapper.svelte"; import { @@ -15,6 +14,7 @@ MIN_WIDTH, mousePosition, normalizeSizeArray, + type AddableItem, } from "./layout-util"; import RowDropZone from "./RowDropZone.svelte"; import RowWrapper from "./RowWrapper.svelte"; @@ -33,7 +33,7 @@ export let selectedComponent: Writable; export let addItems: ( position: { row: number; column: number }, - items: CanvasComponentType[], + items: AddableItem[], ) => void; export let spreadEvenly: (index: number) => void; export let onComponentMouseDown: (params: { @@ -47,7 +47,7 @@ // Optional: insert a tab group at a given top-level index (top-level rows only). export let onAddTabGroup: ((index: number) => void) | undefined = undefined; export let onDrop: (row: number, column: number | null) => void; - export let initializeRow: (row: number, type: CanvasComponentType) => void; + export let initializeRow: (row: number, type: AddableItem) => void; export let updateRowHeight: (newHeight: number, index: number) => void; export let updateComponentWidths: ( index: number, diff --git a/web-common/src/features/canvas/EditableCanvasTabGroup.svelte b/web-common/src/features/canvas/EditableCanvasTabGroup.svelte index 12c01fbd8fd5..11d5e297d131 100644 --- a/web-common/src/features/canvas/EditableCanvasTabGroup.svelte +++ b/web-common/src/features/canvas/EditableCanvasTabGroup.svelte @@ -5,10 +5,9 @@ import AddComponentDropdown from "./AddComponentDropdown.svelte"; import CanvasTabStrip from "./CanvasTabStrip.svelte"; import type { BaseCanvasComponent } from "./components/BaseCanvasComponent"; - import type { CanvasComponentType } from "./components/types"; import EditableCanvasRow from "./EditableCanvasRow.svelte"; import ItemWrapper from "./ItemWrapper.svelte"; - import type { EditTarget } from "./layout-util"; + import type { AddableItem, EditTarget } from "./layout-util"; import RowDropZone from "./RowDropZone.svelte"; import RowWrapper from "./RowWrapper.svelte"; import type { TabGroup } from "./stores/tab-group"; @@ -39,13 +38,13 @@ ) => void; export let addItems: ( position: { row: number; column: number }, - items: CanvasComponentType[], + items: AddableItem[], target?: EditTarget, ) => void; export let spreadEvenly: (index: number, target?: EditTarget) => void; export let initializeRow: ( row: number, - type: CanvasComponentType, + type: AddableItem, target?: EditTarget, ) => void; export let updateRowHeight: ( @@ -186,6 +185,8 @@ label={m.canvas_add_widget_to_tab()} onItemClick={(type) => initializeRow($grid.length, type, target)} + onAddComponentRef={(componentName) => + initializeRow($grid.length, { componentName }, target)} /> @@ -235,6 +236,8 @@ componentForm label={m.canvas_add_widget_below_tabs()} onItemClick={(type) => initializeRow(blockIndex + 1, type)} + onAddComponentRef={(componentName) => + initializeRow(blockIndex + 1, { componentName })} onAddTabGroup={() => onAddTabGroup(blockIndex + 1)} /> diff --git a/web-common/src/features/canvas/ElementDivider.svelte b/web-common/src/features/canvas/ElementDivider.svelte index 4b3e3f535699..5b4b8cc2bb02 100644 --- a/web-common/src/features/canvas/ElementDivider.svelte +++ b/web-common/src/features/canvas/ElementDivider.svelte @@ -3,6 +3,7 @@ import { ArrowLeftRight } from "lucide-svelte"; import AddComponentDropdown from "./AddComponentDropdown.svelte"; import type { CanvasComponentType } from "./components/types"; + import type { AddableItem } from "./layout-util"; import { dropZone, activeDivider } from "./stores/ui-stores"; import Tooltip from "@rilldata/web-common/components/tooltip/Tooltip.svelte"; import TooltipContent from "@rilldata/web-common/components/tooltip/TooltipContent.svelte"; @@ -18,7 +19,7 @@ export let dragging: boolean; export let addItems: ( position: { row: number; column: number }, - item: CanvasComponentType[], + item: AddableItem[], ) => void; export let onColumnResizeStart: ((columnIndex: number) => void) | undefined = undefined; @@ -56,6 +57,12 @@ addItems({ row: rowIndex, column: addIndex }, [type]); } } + + function onAddComponentRef(componentName: string) { + if (componentName) { + addItems({ row: rowIndex, column: addIndex }, [{ componentName }]); + } + }
{ if (!isOpen) { activeDivider.set(null); diff --git a/web-common/src/features/canvas/LocalFiltersHeader.svelte b/web-common/src/features/canvas/LocalFiltersHeader.svelte index 46df3e8bbeae..40caaf495f16 100644 --- a/web-common/src/features/canvas/LocalFiltersHeader.svelte +++ b/web-common/src/features/canvas/LocalFiltersHeader.svelte @@ -25,7 +25,9 @@ } = component); $: metricsViewName = - "metrics_view" in $specStore ? ($specStore.metrics_view ?? null) : null; + "metrics_view" in $specStore + ? (($specStore.metrics_view as string | undefined) ?? null) + : null; $: if (metricsViewName) { measures = getMeasuresForMetricView(metricsViewName); @@ -93,9 +95,7 @@ import AddComponentDropdown from "./AddComponentDropdown.svelte"; - import type { CanvasComponentType } from "./components/types"; + import type { AddableItem } from "./layout-util"; import Divider from "./Divider.svelte"; import { activeDivider, dropZone } from "./stores/ui-stores"; @@ -11,7 +11,7 @@ export let position: "top" | "bottom" | undefined = undefined; export let onDrop: (row: number, column: number | null) => void; export let onRowResizeStart: (e: MouseEvent) => void = () => {}; - export let addItem: (type: CanvasComponentType) => void; + export let addItem: (item: AddableItem) => void; // When provided, the add menu offers inserting a tab group at this position. export let onAddTabGroup: (() => void) | undefined = undefined; @@ -93,6 +93,7 @@ } }} onItemClick={addItem} + onAddComponentRef={(componentName) => addItem({ componentName })} {onAddTabGroup} /> diff --git a/web-common/src/features/canvas/components/BaseCanvasComponent.ts b/web-common/src/features/canvas/components/BaseCanvasComponent.ts index bc4b67fbc4eb..09d4ddd262e4 100644 --- a/web-common/src/features/canvas/components/BaseCanvasComponent.ts +++ b/web-common/src/features/canvas/components/BaseCanvasComponent.ts @@ -10,6 +10,7 @@ import type { import { getFiltersFromText } from "@rilldata/web-common/features/dashboards/filters/dimension-filters/dimension-search-text-utils"; import type { ExploreState } from "@rilldata/web-common/features/dashboards/stores/explore-state"; import type { + V1CanvasItem, V1Expression, V1Resource, V1TimeRange, @@ -169,7 +170,10 @@ export abstract class BaseCanvasComponent { this.unsubscribeSpec?.(); } - update(resource: V1Resource, path: ComponentPath) { + // item is the canvas item this instance renders; only used by components whose + // editable state lives on the item rather than the resource (see ComponentRefComponent). + update(resource: V1Resource, path: ComponentPath, item?: V1CanvasItem) { + void item; const yamlSpec = (resource.component?.state?.validSpec ?.rendererProperties ?? (this.parent.allowUnvalidatedSpec diff --git a/web-common/src/features/canvas/components/component-ref/CanvasComponentRef.svelte b/web-common/src/features/canvas/components/component-ref/CanvasComponentRef.svelte new file mode 100644 index 000000000000..cd4a2ce33d50 --- /dev/null +++ b/web-common/src/features/canvas/components/component-ref/CanvasComponentRef.svelte @@ -0,0 +1,124 @@ + + +{#if !componentSpec} + +{:else if renderer !== "custom_chart"} + +{:else if $resolvedQuery.error && !optimisticProps} + +{:else if !resolvedProps} + +
+ +
+{:else} + +{/if} diff --git a/web-common/src/features/canvas/components/component-ref/index.ts b/web-common/src/features/canvas/components/component-ref/index.ts new file mode 100644 index 000000000000..1f3a976b14ff --- /dev/null +++ b/web-common/src/features/canvas/components/component-ref/index.ts @@ -0,0 +1,216 @@ +import { BaseCanvasComponent } from "@rilldata/web-common/features/canvas/components/BaseCanvasComponent"; +import type { CanvasComponentType } from "@rilldata/web-common/features/canvas/components/types"; +import type { + AllKeys, + InputParams, +} from "@rilldata/web-common/features/canvas/inspector/types"; +import type { + CanvasEntity, + ComponentPath, +} from "@rilldata/web-common/features/canvas/stores/canvas-entity"; +import { + getDeclaredParams, + isOrderByParam, + orderByOptions, + paramToInputParam, + prettyParamLabel, + reconcileOrderByArg, +} from "@rilldata/web-common/features/custom-viz/params"; +import type { + V1CanvasItem, + V1ComponentParam, + V1Resource, +} from "@rilldata/web-common/runtime-client"; +import { get } from "svelte/store"; +import CanvasComponentRef from "./CanvasComponentRef.svelte"; + +/** + * The flattened spec of a canvas item that references an externally defined component: + * the component name plus the values bound to its declared params spread at the top + * level, so the generated inspector inputs can bind to them like ordinary spec keys. + * Param bindings are scalars only (enforced by the canvas parser), which also keeps + * indexed access on the ComponentSpec union usable by the shared inspector widgets. + */ +export interface ComponentRefSpec { + component: string; + [param: string]: string | number | boolean | undefined; +} + +/** + * Canvas component for items that reference an externally defined component + * (`component: ` + `params:` in the canvas YAML). Unlike other canvas + * components, its editable state is the canvas item's param bindings, not the + * component's renderer properties, and its inspector inputs are generated from + * the referenced component's declared params. + */ +export class ComponentRefComponent extends BaseCanvasComponent { + minSize = { width: 4, height: 4 }; + defaultSize = { width: 6, height: 4 }; + resetParams = []; + type: CanvasComponentType = "component_ref"; + component = CanvasComponentRef; + + componentName: string; + + constructor( + resource: V1Resource, + parent: CanvasEntity, + path: ComponentPath, + item?: V1CanvasItem, + ) { + super(resource, parent, path, { component: item?.component ?? "" }); + this.componentName = item?.component ?? ""; + this.specStore.set(this.specFromItem(item)); + this.syncMetricsViewName(); + } + + override update( + resource: V1Resource, + path: ComponentPath, + item?: V1CanvasItem, + ) { + super.update(resource, path); + if (item?.component) this.componentName = item.component; + this.specStore.set(this.specFromItem(item)); + this.syncMetricsViewName(); + } + + /** The referenced component's declared params. */ + get declaredParams(): V1ComponentParam[] { + return getDeclaredParams( + get(this.resource), + this.parent.allowUnvalidatedSpec, + ); + } + + /** + * The values currently bound to the declared params. + * Filters to declared names, since the spec may carry derived keys + * (e.g. the metrics_view surfaced for the canvas filter system). + */ + args(spec: ComponentRefSpec = get(this.specStore)): Record { + const declared = new Set(this.declaredParams.map((p) => p.name)); + return Object.fromEntries( + Object.entries(spec).filter( + ([key, value]) => declared.has(key) && value !== undefined, + ), + ); + } + + isValid(spec: ComponentRefSpec): boolean { + return !!spec.component; + } + + inputParams(): InputParams { + const options: Record> = {}; + const args = this.args(); + for (const param of this.declaredParams) { + if (!param.name) continue; + // The by-convention order_by param selects among the currently bound + // field values instead of a free-text input. + if (isOrderByParam(param)) { + options[param.name] = { + type: "select", + label: prettyParamLabel(param.name), + optional: !param.required, + description: param.description, + meta: { options: orderByOptions(this.declaredParams, args) }, + }; + continue; + } + options[param.name] = paramToInputParam(param); + } + return { options, filter: {} }; + } + + override updateProperty( + key: AllKeys, + value: ComponentRefSpec[AllKeys], + ) { + const currentSpec = get(this.specStore); + const newSpec = { ...currentSpec, [key]: value }; + if (value === undefined || value === "") { + delete newSpec[key]; + } + + // When the bound metrics view changes, clear the bound field params that + // depend on it: they referenced fields of the previous metrics view. + for (const param of this.declaredParams) { + if (param.type === "metrics_view" && param.name === key) { + for (const dependent of this.declaredParams) { + if (dependent.metricsViewParam === key && dependent.name) { + delete newSpec[dependent.name]; + } + } + } + } + + // Keep the by-convention order_by binding in step: it follows a re-bound + // field it pointed at, and is re-seeded from the measure when it no longer + // matches any bound field (e.g. after a metrics view change). + reconcileOrderByArg(this.declaredParams, newSpec, { + name: String(key), + previousValue: currentSpec[key], + }); + + this.writeParamsToYAML(this.args(newSpec)); + this.specStore.set(newSpec); + this.syncMetricsViewName(); + } + + override setSpec(newSpec: ComponentRefSpec) { + this.writeParamsToYAML(this.args(newSpec)); + this.specStore.set(newSpec); + this.syncMetricsViewName(); + } + + static newComponentSpec(): ComponentRefSpec { + return { component: "" }; + } + + private specFromItem(item: V1CanvasItem | undefined): ComponentRefSpec { + return { + component: item?.component ?? this.componentName, + ...(item?.params ?? {}), + }; + } + + /** + * Persists the param bindings to the canvas YAML at the item's `params:` key. + * Only the params map is written; the item's other keys (component, width) stay untouched. + */ + private writeParamsToYAML(params: Record) { + if (!this.parent.fileArtifact) return; + const parsedDocument = get(this.parent.parsedContent); + const { updateEditorContent } = this.parent.fileArtifact; + + // pathInYAML ends with the component type segment; the item map is its parent. + const itemPath = this.pathInYAML.slice(0, -1); + if (Object.keys(params).length > 0) { + parsedDocument.setIn([...itemPath, "params"], params); + } else { + parsedDocument.deleteIn([...itemPath, "params"]); + } + + updateEditorContent(parsedDocument.toString(), false, true); + } + + /** + * Tracks the metrics view bound to the first metrics_view-typed param so the + * canvas filter and time systems know which metrics view this item uses. + */ + private syncMetricsViewName() { + const spec = get(this.specStore); + const mvParam = this.declaredParams.find((p) => p.type === "metrics_view"); + const name = mvParam?.name + ? (spec[mvParam.name] ?? mvParam.default) + : undefined; + if (typeof name !== "string" || !name || name === this.metricsViewName) { + return; + } + this.metricsViewName = name; + this.localFilters = this.parent.filterManager.createLocalFilterStore(name); + // Surface the metrics view under the key the shared canvas systems read. + this.specStore.update((s) => ({ ...s, metrics_view: name })); + } +} diff --git a/web-common/src/features/canvas/components/types.ts b/web-common/src/features/canvas/components/types.ts index feca61fb4c93..5fec7aebc97d 100644 --- a/web-common/src/features/canvas/components/types.ts +++ b/web-common/src/features/canvas/components/types.ts @@ -3,6 +3,7 @@ import type { CircularCanvasChartSpec } from "@rilldata/web-common/features/canv import type { ScatterPlotCanvasChartSpec } from "@rilldata/web-common/features/canvas/components/charts/variants/ScatterPlotChart"; import type { KPIGridSpec } from "@rilldata/web-common/features/canvas/components/kpi-grid"; import type { ChartType } from "../../components/charts/types"; +import type { ComponentRefSpec } from "./component-ref"; import type { ImageSpec } from "./image"; import type { KPISpec } from "./kpi"; import type { LeaderboardSpec } from "./leaderboard"; @@ -21,7 +22,11 @@ export type ComponentWithMetricsView = | LeaderboardSpec | CustomChart; -export type ComponentSpec = ComponentWithMetricsView | ImageSpec | MarkdownSpec; +export type ComponentSpec = + | ComponentWithMetricsView + | ImageSpec + | MarkdownSpec + | ComponentRefSpec; export interface ComponentCommonProperties { title?: string; @@ -59,7 +64,8 @@ export type CanvasComponentType = | "pivot" | "table" | "leaderboard" - | "custom_chart"; + | "custom_chart" + | "component_ref"; interface LineChart { line_chart: CartesianCanvasChartSpec; diff --git a/web-common/src/features/canvas/components/util.ts b/web-common/src/features/canvas/components/util.ts index 688659310e16..33b9ce0e2803 100644 --- a/web-common/src/features/canvas/components/util.ts +++ b/web-common/src/features/canvas/components/util.ts @@ -11,6 +11,7 @@ import type { FilterInputTypes, } from "@rilldata/web-common/features/canvas/inspector/types"; import { + type V1CanvasItem, type V1ComponentSpec, type V1MetricsViewSpec, type V1ResolveCanvasResponseResolvedComponents, @@ -18,6 +19,7 @@ import { } from "@rilldata/web-common/runtime-client"; import type { CanvasEntity, ComponentPath } from "../stores/canvas-entity"; import type { BaseCanvasComponent } from "./BaseCanvasComponent"; +import { ComponentRefComponent } from "./component-ref"; import { ImageComponent } from "./image"; import { LeaderboardComponent } from "./leaderboard"; import { MarkdownCanvasComponent } from "./markdown"; @@ -120,6 +122,7 @@ const NON_CHART_TYPES = [ "pivot", "leaderboard", "custom_chart", + "component_ref", ] as const; const ALL_COMPONENT_TYPES = [...CHART_TYPES, ...NON_CHART_TYPES] as const; @@ -152,6 +155,7 @@ const baseComponentMap = { table: PivotCanvasComponent, pivot: PivotCanvasComponent, custom_chart: CustomChartComponent, + component_ref: ComponentRefComponent, } as const; const IconMap = { markdown: TextIcon, @@ -178,6 +182,7 @@ const baseDisplayMap = { image: "Image", leaderboard: "Leaderboard", custom_chart: "Custom Chart", + component_ref: "Custom viz", } as const; const chartDisplayMap = Object.fromEntries( @@ -193,8 +198,19 @@ export function createComponent( resource: V1Resource, parent: CanvasEntity, path: ComponentPath, + item?: V1CanvasItem, + instanceId?: string, ): BaseCanvasComponent { - const type = resource.component?.spec?.renderer as CanvasComponentType; + const type = getComponentInstanceType(resource, item); + if (type === "component_ref") { + const component = new ComponentRefComponent(resource, parent, path, item); + // Items referencing an external component share one resource, so the + // resource name can't identify the instance: use the positional instance id + // (also the components-store key and the selection id) so two references to + // the same component get distinct DOM ids and selection/cleanup state. + if (instanceId) component.id = instanceId; + return component; + } const ComponentClass = COMPONENT_CLASS_MAP[type as keyof typeof COMPONENT_CLASS_MAP]; if (ComponentClass) { @@ -203,6 +219,26 @@ export function createComponent( return new CartesianChartComponent(resource, parent, path); } +/** + * Determines the component class type for a canvas item. + * Items that reference an externally defined component render through the + * component_ref wrapper regardless of the referenced component's renderer; + * everything else keys off the renderer. + */ +export function getComponentInstanceType( + resource: V1Resource | undefined, + item?: V1CanvasItem, + allowUnvalidatedSpec = true, +): CanvasComponentType { + if (item && !item.definedInCanvas && item.component) { + return "component_ref"; + } + return (resource?.component?.state?.validSpec?.renderer ?? + (allowUnvalidatedSpec + ? resource?.component?.spec?.renderer + : undefined)) as CanvasComponentType; +} + export function isCanvasComponentType( value: string | undefined, ): value is CanvasComponentType { diff --git a/web-common/src/features/canvas/inspector/ComponentsEditor.svelte b/web-common/src/features/canvas/inspector/ComponentsEditor.svelte index aa83dd6129c7..17075bc9d1ce 100644 --- a/web-common/src/features/canvas/inspector/ComponentsEditor.svelte +++ b/web-common/src/features/canvas/inspector/ComponentsEditor.svelte @@ -1,11 +1,15 @@ {#if componentType} @@ -41,6 +53,23 @@ {/if} + {#if isRef} +
+ {#if refFilePath} + + {m.canvas_edit_component()} + + + {/if} + + {m.canvas_component_ref_edit_note()} + +
+ {/if} + {#if componentType && component && rendererProperties && currentTab} {#if currentTab === "options"} {#key component} @@ -57,7 +86,7 @@
{:else}
- Unknown Component {type} + {m.canvas_inspector_unknown_component({ type })}
{/if} diff --git a/web-common/src/features/canvas/inspector/MetricSelectorDropdown.svelte b/web-common/src/features/canvas/inspector/MetricSelectorDropdown.svelte index 02bdee014957..41a6b3481231 100644 --- a/web-common/src/features/canvas/inspector/MetricSelectorDropdown.svelte +++ b/web-common/src/features/canvas/inspector/MetricSelectorDropdown.svelte @@ -29,7 +29,10 @@ .filter(isString); $: metricsView = - "metrics_view" in $spec ? $spec.metrics_view : metricsViewNames[0]; + (key in $spec ? ($spec[key] as string | undefined) : undefined) ?? + ("metrics_view" in $spec + ? ($spec.metrics_view as string | undefined) + : metricsViewNames[0]); ({ value: name, label: name }))} + onChange={(next) => onChange(next)} + /> +{:else if param.type === "measure" || param.type === "dimension" || param.type === "time_dimension"} + onChange(next)} + /> +{:else if param.options?.length} + +{/if} diff --git a/web-common/src/features/custom-viz/workspace/TestBindingsPanel.svelte b/web-common/src/features/custom-viz/workspace/TestBindingsPanel.svelte new file mode 100644 index 000000000000..aabbd9b0a9dd --- /dev/null +++ b/web-common/src/features/custom-viz/workspace/TestBindingsPanel.svelte @@ -0,0 +1,137 @@ + + +
+ {#if params.length === 0} +
+ {m.component_no_params()} +
+ {:else} +
+ {m.component_test_values()} +
+
+ {m.component_test_values_note()} +
+ {#each params as param (param.name)} +
+ setArg(param.name, value)} + /> + {#if param.description} +
+ {param.description} +
+ {/if} +
+ {/each} + {/if} +
diff --git a/web-common/src/features/custom-viz/workspace/UsedByPanel.svelte b/web-common/src/features/custom-viz/workspace/UsedByPanel.svelte new file mode 100644 index 000000000000..a7f5ef701327 --- /dev/null +++ b/web-common/src/features/custom-viz/workspace/UsedByPanel.svelte @@ -0,0 +1,95 @@ + + +
+
+ {m.component_used_by()} +
+ {#if usedBy.length === 0} +
+ {m.component_not_used()} +
+ {:else} + {#each usedBy as { resource, items } (resource.meta?.name?.name)} + {@const canvasName = resource.meta?.name?.name} + {@const filePath = resource.meta?.filePaths?.[0]} +
+
+ + {resource.canvas?.state?.validSpec?.displayName || canvasName} + + {#if items.length > 1} + ×{items.length} + {/if} +
+ {#each items as item, i (i)} + {#if item.params && Object.keys(item.params).length > 0} + + {/if} + {/each} +
+ {/each} + {/if} +
diff --git a/web-common/src/features/custom-viz/workspace/preview-state.ts b/web-common/src/features/custom-viz/workspace/preview-state.ts new file mode 100644 index 000000000000..ce9468b28e67 --- /dev/null +++ b/web-common/src/features/custom-viz/workspace/preview-state.ts @@ -0,0 +1,19 @@ +import { writable, type Writable } from "svelte/store"; + +/** + * Session-scoped test bindings for the component workspace preview, keyed by file path. + * These are dev-time values used to render the preview; they are never persisted to + * the component's YAML. + */ +const stores = new Map>>(); + +export function getPreviewArgsStore( + filePath: string, +): Writable> { + let store = stores.get(filePath); + if (!store) { + store = writable({}); + stores.set(filePath, store); + } + return store; +} diff --git a/web-common/src/features/entity-management/add/AddAssetButton.svelte b/web-common/src/features/entity-management/add/AddAssetButton.svelte index cb13eab6e678..c13aba8d5712 100644 --- a/web-common/src/features/entity-management/add/AddAssetButton.svelte +++ b/web-common/src/features/entity-management/add/AddAssetButton.svelte @@ -27,6 +27,7 @@ import { useRuntimeClient } from "../../../runtime-client/v2"; import { useIsModelingSupportedForDefaultOlapDriverOLAP as useIsModelingSupportedForDefaultOlapDriver } from "../../connectors/selectors.ts"; import { directoryState } from "../../file-explorer/directory-store.ts"; + import ChartTypesDialog from "@rilldata/web-common/features/custom-viz/examples/ChartTypesDialog.svelte"; import { createResourceAndNavigate } from "./new-files.ts"; import AddAiConnectorDialog from "../../connectors/ai/AddAiConnectorDialog.svelte"; import CreateExploreDialog from "./CreateExploreDialog.svelte"; @@ -46,6 +47,7 @@ let showExploreDialog = false; let generateDataDialog = false; let showAiConnectorDialog = false; + let showChartTypesDialog = false; let addDataModalOpen = false; let addDataConnector = ""; let addDataTargetResource: ResourceKind | undefined; @@ -57,7 +59,7 @@ const createFile = createRuntimeServicePutFileMutation(runtimeClient); const createFolder = createRuntimeServiceCreateDirectoryMutation(runtimeClient); - const { developerChat } = featureFlags; + const { developerChat, customComponents } = featureFlags; $: currentFile = $page.params.file; $: currentDirectory = currentFile @@ -243,6 +245,18 @@
+ {#if $customComponents} + (showChartTypesDialog = true)} + > + + Custom viz + + {/if} More @@ -324,6 +338,10 @@ +{#if $customComponents} + +{/if} + + import { goto } from "$app/navigation"; + import { customYAMLwithJSONandSQL } from "@rilldata/web-common/components/editor/presets/yamlWithJsonAndSql"; + import Editor from "@rilldata/web-common/features/editor/Editor.svelte"; + import type { EditorView } from "@codemirror/view"; + import { handleEntityRename } from "@rilldata/web-common/features/entity-management/actions/ui-actions.ts"; + import { getNameFromFile } from "@rilldata/web-common/features/entity-management/entity-mappers"; + import type { FileArtifact } from "@rilldata/web-common/features/entity-management/file-artifact"; + import { + ResourceKind, + useFilteredResources, + } from "@rilldata/web-common/features/entity-management/resource-selectors"; + import ReconcileWarningPanel from "@rilldata/web-common/features/entity-management/ReconcileWarningPanel.svelte"; + import { sendComponentFilePrompt } from "@rilldata/web-common/features/custom-viz/component-ai-agent"; + import ComponentPreview from "@rilldata/web-common/features/custom-viz/workspace/ComponentPreview.svelte"; + import EjectButton from "@rilldata/web-common/features/custom-viz/workspace/EjectButton.svelte"; + import TestBindingsPanel from "@rilldata/web-common/features/custom-viz/workspace/TestBindingsPanel.svelte"; + import UsedByPanel from "@rilldata/web-common/features/custom-viz/workspace/UsedByPanel.svelte"; + import { featureFlags } from "@rilldata/web-common/features/feature-flags"; + import { getPreviewArgsStore } from "@rilldata/web-common/features/custom-viz/workspace/preview-state"; + import { getDeclaredParams } from "@rilldata/web-common/features/custom-viz/params"; + import SidebarWrapper from "@rilldata/web-common/features/visual-editing/SidebarWrapper.svelte"; + import { + Inspector, + WorkspaceContainer, + WorkspaceHeader, + } from "@rilldata/web-common/layout/workspace"; + import { workspaces } from "@rilldata/web-common/layout/workspace/workspace-stores"; + import WorkspaceEditorContainer from "@rilldata/web-common/layout/workspace/WorkspaceEditorContainer.svelte"; + import { m } from "@rilldata/web-common/lib/i18n/gen/messages"; + import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; + import type { V1ComponentParam } from "@rilldata/web-common/runtime-client"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; + + export let fileArtifact: FileArtifact; + + const runtimeClient = useRuntimeClient(); + + const { developerChat } = featureFlags; + + let aiPrompt = ""; + let editor: EditorView | null = null; + + function askAI() { + const prompt = aiPrompt.trim(); + if (!prompt) return; + sendComponentFilePrompt(runtimeClient, filePath, prompt); + aiPrompt = ""; + } + + $: ({ + autoSave, + path: filePath, + fileName, + getResource, + remoteContent, + hasUnsavedChanges, + } = fileArtifact); + + $: resourceQuery = getResource(queryClient); + $: ({ data: resource } = $resourceQuery); + + $: componentName = getNameFromFile(filePath); + + $: workspace = workspaces.get(filePath); + $: selectedViewStore = workspace.view; + $: selectedView = $selectedViewStore ?? "code"; + + $: parseErrorQuery = fileArtifact.getParseError(queryClient); + $: parseError = $parseErrorQuery; + + $: argsStore = getPreviewArgsStore(filePath); + + // Breaking-change warning: the component is used by dashboards, and the draft + // spec removes, retypes, or adds a required param compared to the valid spec. + $: canvasesQuery = useFilteredResources(runtimeClient, ResourceKind.Canvas); + $: usedByCount = ($canvasesQuery?.data ?? []).filter((res) => + (res.meta?.refs ?? []).some( + (ref) => + ref.kind === ResourceKind.Component && ref.name === componentName, + ), + ).length; + $: validParams = resource?.component?.state?.validSpec?.params ?? []; + $: draftParams = getDeclaredParams(resource, true); + $: showBreakingWarning = + usedByCount > 0 && + validParams.length > 0 && + isBreakingChange(validParams, draftParams); + + function isBreakingChange( + valid: V1ComponentParam[], + draft: V1ComponentParam[], + ): boolean { + const draftByName = new Map(draft.map((p) => [p.name, p])); + for (const param of valid) { + const draftParam = draftByName.get(param.name); + if (!draftParam || draftParam.type !== param.type) return true; + } + for (const param of draft) { + const isNew = !valid.some((v) => v.name === param.name); + if (isNew && param.required) return true; + } + return false; + } + + async function onChangeCallback(newTitle: string) { + const newRoute = await handleEntityRename( + runtimeClient, + newTitle, + filePath, + fileName, + ); + if (newRoute) await goto(newRoute); + } + + + + + {#snippet cta()} + + {/snippet} + + + +
+ {#if showBreakingWarning} +
+ {m.component_breaking_warning({ count: String(usedByCount) })} +
+ {/if} +
+ + + {#if selectedView === "code"} + + {:else} + + {/if} + +
+ +
+
+ + + + + {#if $developerChat} +
+ +
+ {/if} + + +
+
+
+
diff --git a/web-common/src/features/workspaces/WorkspaceDispatcher.svelte b/web-common/src/features/workspaces/WorkspaceDispatcher.svelte index b625f965687c..93c2c7de416f 100644 --- a/web-common/src/features/workspaces/WorkspaceDispatcher.svelte +++ b/web-common/src/features/workspaces/WorkspaceDispatcher.svelte @@ -4,13 +4,16 @@ import { customYAMLwithJSONandSQL } from "@rilldata/web-common/components/editor/presets/yamlWithJsonAndSql"; import { GeneratingMessage } from "@rilldata/web-common/components/generating-message"; import { generatingCanvasFilePath } from "@rilldata/web-common/features/canvas/ai-generation/generateCanvas"; + import { generatingComponentFilePath } from "@rilldata/web-common/features/custom-viz/examples/import-with-ai"; import Editor from "@rilldata/web-common/features/editor/Editor.svelte"; import FileWorkspaceHeader from "@rilldata/web-common/features/editor/FileWorkspaceHeader.svelte"; import { getExtensionsForFile } from "@rilldata/web-common/features/editor/getExtensionsForFile"; import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors"; import { directoryState } from "@rilldata/web-common/features/file-explorer/directory-store"; import type { FileArtifact } from "@rilldata/web-common/features/entity-management/file-artifact"; + import { featureFlags } from "@rilldata/web-common/features/feature-flags"; import CanvasWorkspace from "@rilldata/web-common/features/workspaces/CanvasWorkspace.svelte"; + import ComponentWorkspace from "@rilldata/web-common/features/workspaces/ComponentWorkspace.svelte"; import ExploreWorkspace from "@rilldata/web-common/features/workspaces/ExploreWorkspace.svelte"; import MetricsWorkspace from "@rilldata/web-common/features/workspaces/MetricsWorkspace.svelte"; import ModelWorkspace from "@rilldata/web-common/features/workspaces/ModelWorkspace.svelte"; @@ -52,8 +55,13 @@ let resourceKind = $derived($resourceName?.kind as ResourceKind | undefined); + const { customComponents } = featureFlags; + let WorkspaceComponent = $derived( - workspaces.get(resourceKind ?? $inferredResourceKind), + (resourceKind ?? $inferredResourceKind) === ResourceKind.Component && + $customComponents + ? ComponentWorkspace + : workspaces.get(resourceKind ?? $inferredResourceKind), ); let resourceQuery = $derived(getResource(queryClient)); @@ -69,6 +77,9 @@ let parseError = $derived($parseErrorQuery); let isGeneratingThisFile = $derived($generatingCanvasFilePath === path); + let isImportingThisComponent = $derived( + $generatingComponentFilePath === path, + ); onMount(() => { expandDirectory(path); @@ -92,6 +103,8 @@
{#if isGeneratingThisFile} + {:else if isImportingThisComponent} + {:else if fileArtifact.isPreviewableDataFile} {:else if WorkspaceComponent} diff --git a/web-common/src/layout/workspace/WorkspaceEditorContainer.svelte b/web-common/src/layout/workspace/WorkspaceEditorContainer.svelte index adc00456d95a..672d0820aaa5 100644 --- a/web-common/src/layout/workspace/WorkspaceEditorContainer.svelte +++ b/web-common/src/layout/workspace/WorkspaceEditorContainer.svelte @@ -56,10 +56,12 @@ >
-
- {effectiveError} +
+ {effectiveError} {#if filePath} - + + + {/if}
diff --git a/web-common/src/lib/i18n/messages/en.json b/web-common/src/lib/i18n/messages/en.json index 814248eab124..7d0c4df3e39d 100644 --- a/web-common/src/lib/i18n/messages/en.json +++ b/web-common/src/lib/i18n/messages/en.json @@ -378,10 +378,43 @@ "canvas_column_dimensions_label": "Column dimensions", "canvas_columns_label": "Columns", "canvas_comparison_values_label": "Comparison values", + "component_ai_placeholder": "Describe a change and press Enter to edit with AI…", + "component_example_imported": "Created {path}", + "component_examples_all": "All chart types", + "component_examples_description": "Pick a chart type to start from, then bind it to your data in the component editor.", + "component_examples_empty": "No chart types match your search", + "component_examples_start_blank": "Start blank", + "component_examples_title": "Chart types", + "component_breaking_warning": "Used by {count} dashboard(s) — removing, retyping, or adding required params will break their bindings.", + "component_fix_with_ai": "Fix with AI", + "component_eject": "Eject to Vega-Lite", + "component_eject_tooltip": "Replace this chart spec with the Vega-Lite it compiles to, so you can edit it directly", + "component_eject_confirm_title": "Eject to Vega-Lite?", + "component_eject_confirm_description": "This replaces the chart spec with the Vega-Lite it currently compiles to. Params keep working, but Rill stops deriving scales, sorting and layout from the data, so the chart no longer adapts on its own. There is no way back other than undoing the edit.", + "component_eject_confirm_action": "Eject", + "component_eject_in_progress": "Ejecting\u2026", + "component_eject_success": "Ejected to a Vega-Lite spec", + "component_eject_failed": "Could not eject this component: {error}", + "component_copy_bindings": "Use this dashboard's values", + "component_no_params": "Declare params to make this component reusable across dashboards.", + "component_not_used": "Not used by any dashboards yet", + "component_preview_no_spec": "Add a renderer (e.g. custom_chart) to preview this component", + "component_preview_title": "Preview", + "component_test_values": "Test values", + "component_test_values_note": "Preview-only bindings; dashboards set their own values.", + "component_used_by": "Used by", + "canvas_component_ref_edit_note": "Edits affect all dashboards using this component.", + "canvas_component_ref_missing": "Component \"{name}\" not found", + "canvas_inspector_unknown_component": "Unknown component \"{type}\"", + "canvas_component_ref_unsupported_renderer": "Components with renderer \"{renderer}\" cannot be referenced on a canvas yet", "canvas_config": "Config", "canvas_configuration": "{label} Configuration", "canvas_configurations": "Canvas configurations", "canvas_custom_chart": "Custom Chart", + "canvas_custom_viz": "Custom viz", + "canvas_edit_component": "Edit component", + "canvas_no_components_yet": "No custom viz in this project yet", + "canvas_your_components": "Your components", "canvas_data_labels_label": "Data labels", "canvas_delete": "Delete", "canvas_delete_dashboard_desc": "The dashboard \"{name}\" will be permanently deleted. This action cannot be undone.", diff --git a/web-common/src/lib/i18n/messages/es.json b/web-common/src/lib/i18n/messages/es.json index f924d2871eb5..d166bc49cb4c 100644 --- a/web-common/src/lib/i18n/messages/es.json +++ b/web-common/src/lib/i18n/messages/es.json @@ -378,10 +378,43 @@ "canvas_column_dimensions_label": "Dimensiones de columna", "canvas_columns_label": "Columnas", "canvas_comparison_values_label": "Valores de comparación", + "component_ai_placeholder": "Describe un cambio y pulsa Enter para editar con IA…", + "component_example_imported": "Se creó {path}", + "component_examples_all": "Todos los tipos de gráfico", + "component_examples_description": "Elige un tipo de gráfico para empezar y luego vincúblo a tus datos en el editor de componentes.", + "component_examples_empty": "Ningún tipo de gráfico coincide con tu búsqueda", + "component_examples_start_blank": "Empezar en blanco", + "component_examples_title": "Tipos de gráfico", + "component_breaking_warning": "Usado por {count} dashboard(s) — eliminar, cambiar el tipo o añadir params obligatorios romperá sus vinculaciones.", + "component_fix_with_ai": "Corregir con IA", + "component_eject": "Exportar a Vega-Lite", + "component_eject_tooltip": "Sustituye esta especificación de gráfico por el Vega-Lite al que se compila, para poder editarlo directamente", + "component_eject_confirm_title": "¿Exportar a Vega-Lite?", + "component_eject_confirm_description": "Esto sustituye la especificación del gráfico por el Vega-Lite al que se compila actualmente. Los params siguen funcionando, pero Rill deja de derivar escalas, ordenación y disposición a partir de los datos, así que el gráfico ya no se adapta por sí solo. No hay vuelta atrás salvo deshacer el cambio.", + "component_eject_confirm_action": "Exportar", + "component_eject_in_progress": "Exportando\u2026", + "component_eject_success": "Exportado a una especificación de Vega-Lite", + "component_eject_failed": "No se pudo exportar este componente: {error}", + "component_copy_bindings": "Usar los valores de este dashboard", + "component_no_params": "Declara params para que este componente sea reutilizable en dashboards.", + "component_not_used": "Aún no se usa en ningún dashboard", + "component_preview_no_spec": "Añade un renderizador (p. ej. custom_chart) para previsualizar este componente", + "component_preview_title": "Vista previa", + "component_test_values": "Valores de prueba", + "component_test_values_note": "Vinculaciones solo para la vista previa; los dashboards definen sus propios valores.", + "component_used_by": "Usado por", + "canvas_component_ref_edit_note": "Los cambios afectan a todos los dashboards que usan este componente.", + "canvas_component_ref_missing": "Componente \"{name}\" no encontrado", + "canvas_inspector_unknown_component": "Componente \"{type}\" desconocido", + "canvas_component_ref_unsupported_renderer": "Los componentes con renderizador \"{renderer}\" aún no se pueden referenciar en un canvas", "canvas_config": "Configuración", "canvas_configuration": "Configuración de {label}", "canvas_configurations": "Configuraciones del canvas", "canvas_custom_chart": "Gráfico personalizado", + "canvas_custom_viz": "Visualización personalizada", + "canvas_edit_component": "Editar componente", + "canvas_no_components_yet": "Todavía no hay visualizaciones personalizadas en este proyecto", + "canvas_your_components": "Tus componentes", "canvas_data_labels_label": "Etiquetas de datos", "canvas_delete": "Eliminar", "canvas_delete_dashboard_desc": "El dashboard \"{name}\" será eliminado permanentemente. Esta acción no se puede deshacer.", diff --git a/web-common/src/proto/gen/rill/runtime/v1/queries_pb.ts b/web-common/src/proto/gen/rill/runtime/v1/queries_pb.ts index 2f0d7234ed4c..39470eceac7e 100644 --- a/web-common/src/proto/gen/rill/runtime/v1/queries_pb.ts +++ b/web-common/src/proto/gen/rill/runtime/v1/queries_pb.ts @@ -4445,6 +4445,13 @@ export class ResolveComponentResponse extends Message */ rendererProperties?: Struct; + /** + * The effective args used for resolution: the component's declared param defaults merged with the provided args. + * + * @generated from field: google.protobuf.Struct resolved_args = 3; + */ + resolvedArgs?: Struct; + constructor(data?: PartialMessage) { super(); proto3.util.initPartial(data, this); @@ -4454,6 +4461,7 @@ export class ResolveComponentResponse extends Message static readonly typeName = "rill.runtime.v1.ResolveComponentResponse"; static readonly fields: FieldList = proto3.util.newFieldList(() => [ { no: 2, name: "renderer_properties", kind: "message", T: Struct }, + { no: 3, name: "resolved_args", kind: "message", T: Struct }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): ResolveComponentResponse { 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 f184c32bbdad..6c7bb6caae85 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 @@ -4786,6 +4786,14 @@ export class ComponentSpec extends Message { */ rendererProperties?: Struct; + /** + * Declared parameters that canvases can bind values to when referencing this component. + * Bound values are available in the renderer properties' templating as {{ .params. }}. + * + * @generated from field: repeated rill.runtime.v1.ComponentParam params = 10; + */ + params: ComponentParam[] = []; + /** * @generated from field: repeated rill.runtime.v1.ComponentVariable input = 8; */ @@ -4813,6 +4821,7 @@ export class ComponentSpec extends Message { { no: 7, name: "description", kind: "scalar", T: 9 /* ScalarType.STRING */ }, { no: 4, name: "renderer", kind: "scalar", T: 9 /* ScalarType.STRING */ }, { no: 5, name: "renderer_properties", kind: "message", T: Struct }, + { no: 10, name: "params", kind: "message", T: ComponentParam, repeated: true }, { no: 8, name: "input", kind: "message", T: ComponentVariable, repeated: true }, { no: 9, name: "output", kind: "message", T: ComponentVariable }, { no: 6, name: "defined_in_canvas", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, @@ -4932,6 +4941,97 @@ export class ComponentVariable extends Message { } } +/** + * ComponentParam declares a typed, validated parameter of a component. + * + * @generated from message rill.runtime.v1.ComponentParam + */ +export class ComponentParam extends Message { + /** + * Param name. Must be a valid identifier; referenced in templates as {{ .params. }}. + * + * @generated from field: string name = 1; + */ + name = ""; + + /** + * Param type. One of: "string", "number", "boolean", "metrics_view", "measure", "dimension", "time_dimension". + * + * @generated from field: string type = 2; + */ + type = ""; + + /** + * Human-facing description of the param. + * + * @generated from field: string description = 3; + */ + description = ""; + + /** + * If true, a canvas item referencing this component must bind a value for the param. + * + * @generated from field: bool required = 4; + */ + required = false; + + /** + * Default value used when the param is not bound. Mutually exclusive with required=true. + * + * @generated from field: google.protobuf.Value default = 5; + */ + default?: Value; + + /** + * For "measure", "dimension" and "time_dimension" params: + * the name of a sibling param of type "metrics_view" whose bound metrics view the field must belong to. + * May be omitted when exactly one "metrics_view" param is declared, in which case the parser fills it in. + * + * @generated from field: string metrics_view_param = 6; + */ + metricsViewParam = ""; + + /** + * For scalar params: allowed values. Renders as a select input in visual editors. + * + * @generated from field: repeated google.protobuf.Value options = 7; + */ + options: Value[] = []; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "rill.runtime.v1.ComponentParam"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "type", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 3, name: "description", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 4, name: "required", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, + { no: 5, name: "default", kind: "message", T: Value }, + { no: 6, name: "metrics_view_param", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 7, name: "options", kind: "message", T: Value, repeated: true }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): ComponentParam { + return new ComponentParam().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): ComponentParam { + return new ComponentParam().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): ComponentParam { + return new ComponentParam().fromJsonString(jsonString, options); + } + + static equals(a: ComponentParam | PlainMessage | undefined, b: ComponentParam | PlainMessage | undefined): boolean { + return proto3.util.equals(ComponentParam, a, b); + } +} + /** * @generated from message rill.runtime.v1.Canvas */ @@ -5389,6 +5489,14 @@ export class CanvasItem extends Message { */ definedInCanvas = false; + /** + * Values bound to the referenced component's declared params. + * Only set for items that reference an externally defined component. + * + * @generated from field: google.protobuf.Struct params = 11; + */ + params?: Struct; + /** * Width of the item. The unit is given in width_unit. * @@ -5413,6 +5521,7 @@ export class CanvasItem extends Message { static readonly fields: FieldList = proto3.util.newFieldList(() => [ { no: 1, name: "component", kind: "scalar", T: 9 /* ScalarType.STRING */ }, { no: 8, name: "defined_in_canvas", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, + { no: 11, name: "params", kind: "message", T: Struct }, { no: 9, name: "width", kind: "scalar", T: 13 /* ScalarType.UINT32 */, opt: true }, { no: 10, name: "width_unit", 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 5e8b648db936..d19c7a057d70 100644 --- a/web-common/src/runtime-client/gen/index.schemas.ts +++ b/web-common/src/runtime-client/gen/index.schemas.ts @@ -414,11 +414,16 @@ export interface V1Canvas { state?: V1CanvasState; } +export type V1CanvasItemParams = { [key: string]: unknown }; + export interface V1CanvasItem { /** Name of the component to render. */ component?: string; /** Indicates if the component was defined inline as part of the canvas YAML. */ definedInCanvas?: boolean; + /** Values bound to the referenced component's declared params. +Only set for items that reference an externally defined component. */ + params?: V1CanvasItemParams; /** Width of the item. The unit is given in width_unit. */ width?: number; /** Unit of the width. Current possible values: empty string. */ @@ -699,11 +704,31 @@ export interface V1ComponentSpec { description?: string; renderer?: string; rendererProperties?: V1ComponentSpecRendererProperties; + /** Declared parameters that canvases can bind values to when referencing this component. */ + params?: V1ComponentParam[]; input?: V1ComponentVariable[]; output?: V1ComponentVariable; definedInCanvas?: boolean; } +/** ComponentParam declares a typed, validated parameter of a component. */ +export interface V1ComponentParam { + /** Param name. Must be a valid identifier; referenced in templates as {{ .params. }}. */ + name?: string; + /** Param type. One of: "string", "number", "boolean", "metrics_view", "measure", "dimension", "time_dimension". */ + type?: string; + /** Human-facing description of the param. */ + description?: string; + /** If true, a canvas item referencing this component must bind a value. */ + required?: boolean; + /** Default value used when the param is not bound. Mutually exclusive with required=true. */ + default?: unknown; + /** For field-typed params: the name of a sibling param of type "metrics_view" whose bound metrics view the field must belong to. */ + metricsViewParam?: string; + /** For scalar params: allowed values. Renders as a select input in visual editors. */ + options?: unknown[]; +} + export interface V1ComponentState { validSpec?: V1ComponentSpec; /** The last time any underlying metrics view(s)'s data was refreshed. @@ -2306,8 +2331,14 @@ export type V1ResolveComponentResponseRendererProperties = { [key: string]: unknown; }; +export type V1ResolveComponentResponseResolvedArgs = { + [key: string]: unknown; +}; + export interface V1ResolveComponentResponse { rendererProperties?: V1ResolveComponentResponseRendererProperties; + /** The effective args used for resolution: the component's declared param defaults merged with the provided args. */ + resolvedArgs?: V1ResolveComponentResponseResolvedArgs; } export interface V1ResolveTemplatedStringResponse { diff --git a/web-common/tests/projects/AdBidsComponents/.gitignore b/web-common/tests/projects/AdBidsComponents/.gitignore new file mode 100644 index 000000000000..b527f7f86b09 --- /dev/null +++ b/web-common/tests/projects/AdBidsComponents/.gitignore @@ -0,0 +1,5 @@ +.DS_Store + +# Rill +.env +tmp diff --git a/web-common/tests/projects/AdBidsComponents/dashboards/AdBids_metrics_canvas.yaml b/web-common/tests/projects/AdBidsComponents/dashboards/AdBids_metrics_canvas.yaml new file mode 100644 index 000000000000..c5b199c7ed56 --- /dev/null +++ b/web-common/tests/projects/AdBidsComponents/dashboards/AdBids_metrics_canvas.yaml @@ -0,0 +1,65 @@ +# Explore YAML +# Reference documentation: https://docs.rilldata.com/reference/project-files/canvas-dashboards + +type: canvas +display_name: "Adbids Canvas Dashboard" +defaults: + time_range: PT24H + comparison_mode: time +rows: + - items: + - kpi_grid: + metrics_view: AdBids_metrics + measures: + - total_records + - bid_price_sum + comparison: + - delta + - percent_change + width: 12 + height: 128px + - items: + - stacked_bar: + metrics_view: AdBids_metrics + x: + type: temporal + field: timestamp + sort: -y + limit: 20 + y: + type: quantitative + field: total_records + zeroBasedOrigin: true + color: hsl(240,100%,67%) + width: 12 + height: 320px + - items: + - stacked_bar: + metrics_view: AdBids_metrics + title: "" + description: "" + color: + field: domain + type: nominal + x: + field: publisher + limit: 20 + sort: -y + type: nominal + y: + field: bid_price_sum + type: quantitative + zeroBasedOrigin: true + width: 12 + height: 394px + - items: + - table: + metrics_view: AdBids_metrics + columns: + - publisher + - domain + - total_records + - bid_price_sum + title: Table component + width: 12 + height: 300px diff --git a/web-common/tests/projects/AdBidsComponents/dashboards/AdBids_metrics_explore.yaml b/web-common/tests/projects/AdBidsComponents/dashboards/AdBids_metrics_explore.yaml new file mode 100644 index 000000000000..34369cf3bf91 --- /dev/null +++ b/web-common/tests/projects/AdBidsComponents/dashboards/AdBids_metrics_explore.yaml @@ -0,0 +1,29 @@ +# Explore YAML +# Reference documentation: https://docs.rilldata.com/reference/project-files/explore-dashboards + +type: explore + +title: "Adbids dashboard" +metrics_view: AdBids_metrics + +dimensions: '*' +measures: '*' + +time_ranges: + - PT6H + - PT24H + - P7D + - P14D + - P4W + - P12M + - rill-TD + - rill-WTD + - rill-MTD + - rill-QTD + - rill-YTD + - rill-PDC + - rill-PWC + - rill-PMC + - rill-PQC + - rill-PYC + - inf \ No newline at end of file diff --git a/web-common/tests/projects/AdBidsComponents/data/AdBids.csv.gz b/web-common/tests/projects/AdBidsComponents/data/AdBids.csv.gz new file mode 100644 index 000000000000..da5ba5845b1e Binary files /dev/null and b/web-common/tests/projects/AdBidsComponents/data/AdBids.csv.gz differ diff --git a/web-common/tests/projects/AdBidsComponents/metrics/AdBids_metrics.yaml b/web-common/tests/projects/AdBidsComponents/metrics/AdBids_metrics.yaml new file mode 100644 index 000000000000..9f33fe700bfe --- /dev/null +++ b/web-common/tests/projects/AdBidsComponents/metrics/AdBids_metrics.yaml @@ -0,0 +1,37 @@ +# Metrics view YAML +# Reference documentation: https://docs.rilldata.com/reference/project-files/metrics-views + +version: 1 +type: metrics_view + +display_name: Adbids +table: AdBids_model +timeseries: timestamp + +dimensions: + - name: publisher + display_name: Publisher + column: publisher + - name: domain + display_name: Domain + column: domain + - name: timestamp + display_name: Timestamp + column: timestamp + type: time + - name: offset_timestamp + display_name: Offset Timestamp + column: offset_timestamp + type: time + +measures: + - name: total_records + display_name: Total records + expression: COUNT(*) + description: "" + format_preset: humanize + - name: bid_price_sum + display_name: Sum of Bid Price + expression: SUM(bid_price) + description: "" + format_preset: humanize diff --git a/web-common/tests/projects/AdBidsComponents/models/AdBids_model.sql b/web-common/tests/projects/AdBidsComponents/models/AdBids_model.sql new file mode 100644 index 000000000000..48acb6a05777 --- /dev/null +++ b/web-common/tests/projects/AdBidsComponents/models/AdBids_model.sql @@ -0,0 +1,5 @@ +select + *, + -- Add an offset timestamp that is 7 days earlier than the primary timestamp + "timestamp" - INTERVAL '7 days' as offset_timestamp +from AdBids diff --git a/web-common/tests/projects/AdBidsComponents/rill.yaml b/web-common/tests/projects/AdBidsComponents/rill.yaml new file mode 100644 index 000000000000..5db3a395b884 --- /dev/null +++ b/web-common/tests/projects/AdBidsComponents/rill.yaml @@ -0,0 +1,7 @@ +compiler: rillv1 + +display_name: "Untitled Rill Project" + +features: + chat: false + customComponents: true diff --git a/web-common/tests/projects/AdBidsComponents/sources/AdBids.yaml b/web-common/tests/projects/AdBidsComponents/sources/AdBids.yaml new file mode 100644 index 000000000000..070e7d310c09 --- /dev/null +++ b/web-common/tests/projects/AdBidsComponents/sources/AdBids.yaml @@ -0,0 +1,8 @@ +# Model YAML +# Reference documentation: https://docs.rilldata.com/reference/project-files/models + +type: model +materialize: true + +connector: "duckdb" +sql: "select * from read_csv('data/AdBids.csv.gz', auto_detect=true, ignore_errors=1, header=true)" \ No newline at end of file diff --git a/web-common/tests/projects/AdBidsComponents/viz_library/measure_trend.yaml b/web-common/tests/projects/AdBidsComponents/viz_library/measure_trend.yaml new file mode 100644 index 000000000000..357ff6ef918f --- /dev/null +++ b/web-common/tests/projects/AdBidsComponents/viz_library/measure_trend.yaml @@ -0,0 +1,25 @@ +type: component +display_name: Measure trend +description: Line chart of any measure over a time dimension. + +params: + - name: metrics_view + type: metrics_view + required: true + - name: measure + type: measure + required: true + - name: time_dim + type: time_dimension + required: true + +custom_chart: + metrics_sql: | + SELECT {{ .params.time_dim }}, {{ .params.measure }} + FROM {{ .params.metrics_view }} + ORDER BY {{ .params.time_dim }} + spec: + chartType: Line Chart + encodings: + x: { field: "{{ .params.time_dim }}" } + y: { field: "{{ .params.measure }}" } diff --git a/web-common/tests/projects/AdBidsComponents/viz_library/share_donut.yaml b/web-common/tests/projects/AdBidsComponents/viz_library/share_donut.yaml new file mode 100644 index 000000000000..e38adeaf0b71 --- /dev/null +++ b/web-common/tests/projects/AdBidsComponents/viz_library/share_donut.yaml @@ -0,0 +1,35 @@ +type: component +display_name: Share donut +description: Donut chart showing how a measure is split across a dimension. + +params: + - name: metrics_view + type: metrics_view + required: true + - name: dimension + type: dimension + required: true + - name: measure + type: measure + required: true + - name: inner_radius + type: number + default: 60 + description: Size of the hole in the middle. Set to 0 for a full pie. + - name: limit + type: number + default: 10 + +custom_chart: + metrics_sql: | + SELECT {{ .params.dimension }}, {{ .params.measure }} + FROM {{ .params.metrics_view }} + ORDER BY {{ .params.measure }} DESC + LIMIT {{ .params.limit }} + spec: + chartType: Pie Chart + encodings: + size: { field: "{{ .params.measure }}" } + color: { field: "{{ .params.dimension }}" } + chartProperties: + innerRadius: "{{ .params.inner_radius }}" diff --git a/web-common/tests/projects/AdBidsComponents/viz_library/top_n_bar.yaml b/web-common/tests/projects/AdBidsComponents/viz_library/top_n_bar.yaml new file mode 100644 index 000000000000..1a9416b5d368 --- /dev/null +++ b/web-common/tests/projects/AdBidsComponents/viz_library/top_n_bar.yaml @@ -0,0 +1,29 @@ +type: component +display_name: Top N bar +description: Ranked horizontal bar chart of a measure by a dimension. + +params: + - name: metrics_view + type: metrics_view + required: true + - name: dimension + type: dimension + required: true + - name: measure + type: measure + required: true + - name: limit + type: number + default: 10 + +custom_chart: + metrics_sql: | + SELECT {{ .params.dimension }}, {{ .params.measure }} + FROM {{ .params.metrics_view }} + ORDER BY {{ .params.measure }} DESC + LIMIT {{ .params.limit }} + spec: + chartType: Bar Chart + encodings: + y: { field: "{{ .params.dimension }}", sortBy: x, sortOrder: descending } + x: { field: "{{ .params.measure }}" } diff --git a/web-local/tests/components/custom-viz.spec.ts b/web-local/tests/components/custom-viz.spec.ts new file mode 100644 index 000000000000..ccb1406f648f --- /dev/null +++ b/web-local/tests/components/custom-viz.spec.ts @@ -0,0 +1,141 @@ +import { expect } from "@playwright/test"; +import { test } from "../setup/base"; + +test.describe("custom viz components", () => { + test.use({ project: "AdBidsComponents" }); + + test("can create a blank custom viz with a workspace and test bindings", async ({ + page, + }) => { + await page.getByLabel("Add asset").waitFor({ state: "visible" }); + await page.getByLabel("Add asset").click(); + + // "Custom viz" opens the examples gallery directly; "Start blank" sits in + // its header for the from-scratch path. + await page.getByRole("menuitem", { name: "Custom viz" }).click(); + await page.getByRole("button", { name: "Start blank" }).click(); + + // Lands on the new component file in the component workspace. + await expect(page).toHaveURL(/viz_library\/component\.yaml/); + + // The inspector shows the declared params as test bindings. + await expect(page.getByText("Test values")).toBeVisible(); + await expect(page.getByText("Metrics View", { exact: true })).toBeVisible(); + await expect(page.getByText("Measure", { exact: true })).toBeVisible(); + await expect(page.getByText("Dimension", { exact: true })).toBeVisible(); + + // The pre-existing component is not used by any dashboards yet. + await expect(page.getByText("Used by", { exact: true })).toBeVisible(); + + // The workspace opens in the visual view by default; the preview renders a + // chart against the auto-selected metrics view and test bindings. + await expect( + page.locator(".vega-embed svg, .vega-embed canvas"), + ).toBeVisible({ timeout: 15_000 }); + + // Switching to the code view shows the starter YAML. + await page.getByLabel("Switch to code editor").click(); + await expect(page.getByText("type: component")).toBeVisible(); + }); + + test("can add a custom viz to a canvas with generated param inputs", async ({ + page, + }) => { + await page.getByLabel("Add asset").waitFor({ state: "visible" }); + await page.getByLabel("Add asset").click(); + await page.getByRole("menuitem", { name: "Canvas dashboard" }).click(); + + await page + .getByRole("button", { name: "Add widget" }) + .waitFor({ state: "visible" }); + await page.getByRole("button", { name: "Add widget" }).click(); + + // The project's standalone components are listed in their own section. + await page.getByRole("menuitem", { name: "Your components" }).hover(); + await page.getByRole("menuitem", { name: "Measure trend" }).click(); + + // The inspector shows the generated param form with smart defaults bound. + await expect(page.getByText("Metrics View", { exact: true })).toBeVisible(); + await expect(page.getByText("Measure", { exact: true })).toBeVisible(); + await expect(page.getByText("Time Dim", { exact: true })).toBeVisible(); + + // The referenced component's header links to editing the component file. + await expect(page.getByText("Edit component")).toBeVisible(); + await expect( + page.getByText("Edits affect all dashboards using this component."), + ).toBeVisible(); + + // The referenced chart renders. + await expect( + page.locator(".vega-embed svg, .vega-embed canvas").first(), + ).toBeVisible({ timeout: 15_000 }); + }); + + test("can reference the same component twice with different bindings", async ({ + page, + }) => { + await page.getByLabel("Add asset").waitFor({ state: "visible" }); + await page.getByLabel("Add asset").click(); + await page.getByRole("menuitem", { name: "Canvas dashboard" }).click(); + + await page + .getByRole("button", { name: "Add widget" }) + .waitFor({ state: "visible" }); + await page.getByRole("button", { name: "Add widget" }).click(); + await page.getByRole("menuitem", { name: "Your components" }).hover(); + await page.getByRole("menuitem", { name: "Measure trend" }).click(); + + // Wait for the first instance to render before inserting the second. + await expect(page.locator(".vega-embed").first()).toBeVisible({ + timeout: 15_000, + }); + + await page + .getByRole("button", { name: "Resize row 1 column 1" }) + .hover({ force: true }); + await page + .getByRole("button", { + name: "Insert widget in row 1 at column 2", + exact: true, + }) + .click(); + await page.getByRole("menuitem", { name: "Your components" }).click(); + await page + .getByRole("menuitem", { name: "Measure trend" }) + .click({ timeout: 10_000 }); + + // Both instances render independently, each with a positional instance id + // as its DOM id (never the shared component resource name, which would + // produce duplicate ids and confuse drag geometry and selection). + await expect(page.locator(".vega-embed")).toHaveCount(2, { + timeout: 15_000, + }); + await expect(page.locator('[id="measure_trend"]')).toHaveCount(0); + await expect(page.locator('[id^="measure_trend::"]')).toHaveCount(2); + }); +}); + +test.describe("custom viz flag off", () => { + test.use({ project: "AdBids" }); + + test("menus do not offer custom viz when the flag is off", async ({ + page, + }) => { + await page.getByLabel("Add asset").waitFor({ state: "visible" }); + await page.getByLabel("Add asset").click(); + await expect( + page.getByRole("menuitem", { name: "Custom viz" }), + ).toHaveCount(0); + await page.keyboard.press("Escape"); + + await page.getByLabel("Add asset").click(); + await page.getByRole("menuitem", { name: "Canvas dashboard" }).click(); + await page + .getByRole("button", { name: "Add widget" }) + .waitFor({ state: "visible" }); + await page.getByRole("button", { name: "Add widget" }).click(); + await expect( + page.getByRole("menuitem", { name: "Your components" }), + ).toHaveCount(0); + }); +});