diff --git a/proto/gen/rill/runtime/v1/resources.pb.go b/proto/gen/rill/runtime/v1/resources.pb.go index f07fd4c9576..f105ee58921 100644 --- a/proto/gen/rill/runtime/v1/resources.pb.go +++ b/proto/gen/rill/runtime/v1/resources.pb.go @@ -126,6 +126,61 @@ func (ExploreComparisonMode) EnumDescriptor() ([]byte, []int) { return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{1} } +type ExploreWebView int32 + +const ( + ExploreWebView_EXPLORE_ACTIVE_PAGE_UNSPECIFIED ExploreWebView = 0 + ExploreWebView_EXPLORE_ACTIVE_PAGE_OVERVIEW ExploreWebView = 1 + ExploreWebView_EXPLORE_ACTIVE_PAGE_TIME_DIMENSION ExploreWebView = 2 + ExploreWebView_EXPLORE_ACTIVE_PAGE_PIVOT ExploreWebView = 3 + ExploreWebView_EXPLORE_ACTIVE_PAGE_CANVAS ExploreWebView = 4 +) + +// Enum value maps for ExploreWebView. +var ( + ExploreWebView_name = map[int32]string{ + 0: "EXPLORE_ACTIVE_PAGE_UNSPECIFIED", + 1: "EXPLORE_ACTIVE_PAGE_OVERVIEW", + 2: "EXPLORE_ACTIVE_PAGE_TIME_DIMENSION", + 3: "EXPLORE_ACTIVE_PAGE_PIVOT", + 4: "EXPLORE_ACTIVE_PAGE_CANVAS", + } + ExploreWebView_value = map[string]int32{ + "EXPLORE_ACTIVE_PAGE_UNSPECIFIED": 0, + "EXPLORE_ACTIVE_PAGE_OVERVIEW": 1, + "EXPLORE_ACTIVE_PAGE_TIME_DIMENSION": 2, + "EXPLORE_ACTIVE_PAGE_PIVOT": 3, + "EXPLORE_ACTIVE_PAGE_CANVAS": 4, + } +) + +func (x ExploreWebView) Enum() *ExploreWebView { + p := new(ExploreWebView) + *p = x + return p +} + +func (x ExploreWebView) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ExploreWebView) Descriptor() protoreflect.EnumDescriptor { + return file_rill_runtime_v1_resources_proto_enumTypes[2].Descriptor() +} + +func (ExploreWebView) Type() protoreflect.EnumType { + return &file_rill_runtime_v1_resources_proto_enumTypes[2] +} + +func (x ExploreWebView) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ExploreWebView.Descriptor instead. +func (ExploreWebView) EnumDescriptor() ([]byte, []int) { + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{2} +} + type AssertionStatus int32 const ( @@ -162,11 +217,11 @@ func (x AssertionStatus) String() string { } func (AssertionStatus) Descriptor() protoreflect.EnumDescriptor { - return file_rill_runtime_v1_resources_proto_enumTypes[2].Descriptor() + return file_rill_runtime_v1_resources_proto_enumTypes[3].Descriptor() } func (AssertionStatus) Type() protoreflect.EnumType { - return &file_rill_runtime_v1_resources_proto_enumTypes[2] + return &file_rill_runtime_v1_resources_proto_enumTypes[3] } func (x AssertionStatus) Number() protoreflect.EnumNumber { @@ -175,7 +230,7 @@ func (x AssertionStatus) Number() protoreflect.EnumNumber { // Deprecated: Use AssertionStatus.Descriptor instead. func (AssertionStatus) EnumDescriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{2} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{3} } // Type of measure query to generate @@ -215,11 +270,11 @@ func (x MetricsViewSpec_MeasureType) String() string { } func (MetricsViewSpec_MeasureType) Descriptor() protoreflect.EnumDescriptor { - return file_rill_runtime_v1_resources_proto_enumTypes[3].Descriptor() + return file_rill_runtime_v1_resources_proto_enumTypes[4].Descriptor() } func (MetricsViewSpec_MeasureType) Type() protoreflect.EnumType { - return &file_rill_runtime_v1_resources_proto_enumTypes[3] + return &file_rill_runtime_v1_resources_proto_enumTypes[4] } func (x MetricsViewSpec_MeasureType) Number() protoreflect.EnumNumber { @@ -269,11 +324,11 @@ func (x MetricsViewSpec_ComparisonMode) String() string { } func (MetricsViewSpec_ComparisonMode) Descriptor() protoreflect.EnumDescriptor { - return file_rill_runtime_v1_resources_proto_enumTypes[4].Descriptor() + return file_rill_runtime_v1_resources_proto_enumTypes[5].Descriptor() } func (MetricsViewSpec_ComparisonMode) Type() protoreflect.EnumType { - return &file_rill_runtime_v1_resources_proto_enumTypes[4] + return &file_rill_runtime_v1_resources_proto_enumTypes[5] } func (x MetricsViewSpec_ComparisonMode) Number() protoreflect.EnumNumber { @@ -318,11 +373,11 @@ func (x BucketExtractPolicy_Strategy) String() string { } func (BucketExtractPolicy_Strategy) Descriptor() protoreflect.EnumDescriptor { - return file_rill_runtime_v1_resources_proto_enumTypes[5].Descriptor() + return file_rill_runtime_v1_resources_proto_enumTypes[6].Descriptor() } func (BucketExtractPolicy_Strategy) Type() protoreflect.EnumType { - return &file_rill_runtime_v1_resources_proto_enumTypes[5] + return &file_rill_runtime_v1_resources_proto_enumTypes[6] } func (x BucketExtractPolicy_Strategy) Number() protoreflect.EnumNumber { @@ -2647,14 +2702,29 @@ type ExplorePreset struct { Measures []string `protobuf:"bytes,4,rep,name=measures,proto3" json:"measures,omitempty"` // Dynamic selector for `measures`. Will be processed during validation, so it will always be empty in `state.valid_spec`. MeasuresSelector *FieldSelector `protobuf:"bytes,10,opt,name=measures_selector,json=measuresSelector,proto3" json:"measures_selector,omitempty"` + Where *Expression `protobuf:"bytes,25,opt,name=where,proto3,oneof" json:"where,omitempty"` // Time range for the explore. // It corresponds to the `range` property of the explore's `time_ranges`. // If not found in `time_ranges`, it should be added to the list. - TimeRange string `protobuf:"bytes,6,opt,name=time_range,json=timeRange,proto3" json:"time_range,omitempty"` + TimeRange *string `protobuf:"bytes,6,opt,name=time_range,json=timeRange,proto3,oneof" json:"time_range,omitempty"` + Timezone *string `protobuf:"bytes,11,opt,name=timezone,proto3,oneof" json:"timezone,omitempty"` + TimeGrain *string `protobuf:"bytes,12,opt,name=time_grain,json=timeGrain,proto3,oneof" json:"time_grain,omitempty"` // Comparison mode. - ComparisonMode ExploreComparisonMode `protobuf:"varint,7,opt,name=comparison_mode,json=comparisonMode,proto3,enum=rill.runtime.v1.ExploreComparisonMode" json:"comparison_mode,omitempty"` + ComparisonMode ExploreComparisonMode `protobuf:"varint,7,opt,name=comparison_mode,json=comparisonMode,proto3,enum=rill.runtime.v1.ExploreComparisonMode" json:"comparison_mode,omitempty"` + CompareTimeRange *string `protobuf:"bytes,13,opt,name=compare_time_range,json=compareTimeRange,proto3,oneof" json:"compare_time_range,omitempty"` // If comparison_mode is EXPLORE_COMPARISON_MODE_DIMENSION, this indicates the dimension to use. - ComparisonDimension string `protobuf:"bytes,8,opt,name=comparison_dimension,json=comparisonDimension,proto3" json:"comparison_dimension,omitempty"` + ComparisonDimension *string `protobuf:"bytes,8,opt,name=comparison_dimension,json=comparisonDimension,proto3,oneof" json:"comparison_dimension,omitempty"` + View *ExploreWebView `protobuf:"varint,14,opt,name=view,proto3,enum=rill.runtime.v1.ExploreWebView,oneof" json:"view,omitempty"` + OverviewSortBy *string `protobuf:"bytes,15,opt,name=overview_sort_by,json=overviewSortBy,proto3,oneof" json:"overview_sort_by,omitempty"` + OverviewSortAsc *bool `protobuf:"varint,16,opt,name=overview_sort_asc,json=overviewSortAsc,proto3,oneof" json:"overview_sort_asc,omitempty"` + OverviewExpandedDimension *string `protobuf:"bytes,17,opt,name=overview_expanded_dimension,json=overviewExpandedDimension,proto3,oneof" json:"overview_expanded_dimension,omitempty"` + TimeDimensionMeasure *string `protobuf:"bytes,18,opt,name=time_dimension_measure,json=timeDimensionMeasure,proto3,oneof" json:"time_dimension_measure,omitempty"` + TimeDimensionChartType *string `protobuf:"bytes,19,opt,name=time_dimension_chart_type,json=timeDimensionChartType,proto3,oneof" json:"time_dimension_chart_type,omitempty"` + TimeDimensionPin *bool `protobuf:"varint,20,opt,name=time_dimension_pin,json=timeDimensionPin,proto3,oneof" json:"time_dimension_pin,omitempty"` + PivotRows []string `protobuf:"bytes,21,rep,name=pivot_rows,json=pivotRows,proto3" json:"pivot_rows,omitempty"` + PivotCols []string `protobuf:"bytes,22,rep,name=pivot_cols,json=pivotCols,proto3" json:"pivot_cols,omitempty"` + PivotSortBy *string `protobuf:"bytes,23,opt,name=pivot_sort_by,json=pivotSortBy,proto3,oneof" json:"pivot_sort_by,omitempty"` + PivotSortAsc *bool `protobuf:"varint,24,opt,name=pivot_sort_asc,json=pivotSortAsc,proto3,oneof" json:"pivot_sort_asc,omitempty"` } func (x *ExplorePreset) Reset() { @@ -2717,9 +2787,30 @@ func (x *ExplorePreset) GetMeasuresSelector() *FieldSelector { return nil } -func (x *ExplorePreset) GetTimeRange() string { +func (x *ExplorePreset) GetWhere() *Expression { if x != nil { - return x.TimeRange + return x.Where + } + return nil +} + +func (x *ExplorePreset) GetTimeRange() string { + if x != nil && x.TimeRange != nil { + return *x.TimeRange + } + return "" +} + +func (x *ExplorePreset) GetTimezone() string { + if x != nil && x.Timezone != nil { + return *x.Timezone + } + return "" +} + +func (x *ExplorePreset) GetTimeGrain() string { + if x != nil && x.TimeGrain != nil { + return *x.TimeGrain } return "" } @@ -2731,13 +2822,97 @@ func (x *ExplorePreset) GetComparisonMode() ExploreComparisonMode { return ExploreComparisonMode_EXPLORE_COMPARISON_MODE_UNSPECIFIED } +func (x *ExplorePreset) GetCompareTimeRange() string { + if x != nil && x.CompareTimeRange != nil { + return *x.CompareTimeRange + } + return "" +} + func (x *ExplorePreset) GetComparisonDimension() string { + if x != nil && x.ComparisonDimension != nil { + return *x.ComparisonDimension + } + return "" +} + +func (x *ExplorePreset) GetView() ExploreWebView { + if x != nil && x.View != nil { + return *x.View + } + return ExploreWebView_EXPLORE_ACTIVE_PAGE_UNSPECIFIED +} + +func (x *ExplorePreset) GetOverviewSortBy() string { + if x != nil && x.OverviewSortBy != nil { + return *x.OverviewSortBy + } + return "" +} + +func (x *ExplorePreset) GetOverviewSortAsc() bool { + if x != nil && x.OverviewSortAsc != nil { + return *x.OverviewSortAsc + } + return false +} + +func (x *ExplorePreset) GetOverviewExpandedDimension() string { + if x != nil && x.OverviewExpandedDimension != nil { + return *x.OverviewExpandedDimension + } + return "" +} + +func (x *ExplorePreset) GetTimeDimensionMeasure() string { + if x != nil && x.TimeDimensionMeasure != nil { + return *x.TimeDimensionMeasure + } + return "" +} + +func (x *ExplorePreset) GetTimeDimensionChartType() string { + if x != nil && x.TimeDimensionChartType != nil { + return *x.TimeDimensionChartType + } + return "" +} + +func (x *ExplorePreset) GetTimeDimensionPin() bool { + if x != nil && x.TimeDimensionPin != nil { + return *x.TimeDimensionPin + } + return false +} + +func (x *ExplorePreset) GetPivotRows() []string { + if x != nil { + return x.PivotRows + } + return nil +} + +func (x *ExplorePreset) GetPivotCols() []string { if x != nil { - return x.ComparisonDimension + return x.PivotCols + } + return nil +} + +func (x *ExplorePreset) GetPivotSortBy() string { + if x != nil && x.PivotSortBy != nil { + return *x.PivotSortBy } return "" } +func (x *ExplorePreset) GetPivotSortAsc() bool { + if x != nil && x.PivotSortAsc != nil { + return *x.PivotSortAsc + } + return false +} + // FieldSelector describes logic for selecting a list of fields. // It is useful for dynamically evaluating fields when the list of potential fields is not known at parse time. type FieldSelector struct { @@ -7124,7 +7299,7 @@ var file_rill_runtime_v1_resources_proto_rawDesc = []byte{ 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x8c, 0x03, 0x0a, 0x0d, 0x45, 0x78, 0x70, 0x6c, + 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x93, 0x0b, 0x0a, 0x0d, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x50, 0x72, 0x65, 0x73, 0x65, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x4f, 0x0a, 0x13, 0x64, 0x69, 0x6d, @@ -7139,613 +7314,689 @@ var file_rill_runtime_v1_resources_proto_rawDesc = []byte{ 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x10, 0x6d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x73, 0x53, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, - 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, - 0x67, 0x65, 0x12, 0x4f, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, - 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x72, 0x69, - 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, - 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, - 0x6f, 0x64, 0x65, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, - 0x6f, 0x64, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, - 0x6e, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x13, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x44, 0x69, 0x6d, - 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xca, 0x01, 0x0a, 0x0d, 0x46, 0x69, 0x65, 0x6c, 0x64, - 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x6e, 0x76, 0x65, - 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, - 0x12, 0x12, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, - 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x3a, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, - 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, - 0x12, 0x16, 0x0a, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, - 0x00, 0x52, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x12, 0x2d, 0x0a, 0x11, 0x64, 0x75, 0x63, 0x6b, - 0x64, 0x62, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x10, 0x64, 0x75, 0x63, 0x6b, 0x64, 0x62, 0x45, 0x78, 0x70, - 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x0a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x22, 0x29, 0x0a, 0x0f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, - 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x76, - 0x0a, 0x09, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x04, 0x73, - 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, - 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x69, 0x67, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, - 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, + 0x74, 0x6f, 0x72, 0x12, 0x36, 0x0a, 0x05, 0x77, 0x68, 0x65, 0x72, 0x65, 0x18, 0x19, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x48, + 0x00, 0x52, 0x05, 0x77, 0x68, 0x65, 0x72, 0x65, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, 0x74, + 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x88, 0x01, 0x01, 0x12, + 0x1f, 0x0a, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x02, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x88, 0x01, 0x01, + 0x12, 0x22, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x67, 0x72, 0x61, 0x69, 0x6e, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x47, 0x72, 0x61, 0x69, + 0x6e, 0x88, 0x01, 0x01, 0x12, 0x4f, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, + 0x6f, 0x6e, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, + 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, + 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, + 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x31, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x04, 0x52, 0x10, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x54, 0x69, 0x6d, 0x65, + 0x52, 0x61, 0x6e, 0x67, 0x65, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x05, 0x52, 0x13, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, + 0x69, 0x73, 0x6f, 0x6e, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, + 0x12, 0x38, 0x0a, 0x04, 0x76, 0x69, 0x65, 0x77, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, - 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x59, 0x0a, 0x0d, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x71, 0x6c, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x73, 0x71, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x22, 0x2a, 0x0a, 0x0e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x6d, 0x0a, - 0x06, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x2f, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x70, - 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x32, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xf5, 0x05, 0x0a, - 0x0a, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 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, 0x18, - 0x0a, 0x07, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x07, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x10, 0x72, 0x65, 0x66, 0x72, - 0x65, 0x73, 0x68, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x0f, 0x72, - 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x27, - 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, - 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, - 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x71, 0x75, 0x65, 0x72, 0x79, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x71, 0x75, 0x65, - 0x72, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, - 0x61, 0x72, 0x67, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x41, 0x72, 0x67, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x21, - 0x0a, 0x0c, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x6d, 0x69, - 0x74, 0x12, 0x42, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x6d, - 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, - 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x46, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x37, 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, - 0x72, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, - 0x69, 0x65, 0x72, 0x52, 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x4e, - 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0a, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, - 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2b, - 0x0a, 0x11, 0x77, 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x5f, 0x69, 0x6e, 0x68, 0x65, - 0x72, 0x69, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x77, 0x61, 0x74, 0x65, 0x72, - 0x6d, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x69, 0x73, 0x6f, 0x5f, 0x64, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x49, 0x73, 0x6f, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x38, 0x0a, 0x18, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x75, 0x6e, - 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x6e, 0x63, 0x6c, - 0x6f, 0x73, 0x65, 0x64, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0x90, 0x02, 0x0a, 0x0b, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x72, 0x75, 0x6e, - 0x5f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x6e, 0x65, 0x78, 0x74, 0x52, 0x75, 0x6e, 0x4f, 0x6e, - 0x12, 0x4d, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, - 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x4d, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, + 0x2e, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x57, 0x65, 0x62, 0x56, 0x69, 0x65, 0x77, 0x48, + 0x06, 0x52, 0x04, 0x76, 0x69, 0x65, 0x77, 0x88, 0x01, 0x01, 0x12, 0x2d, 0x0a, 0x10, 0x6f, 0x76, + 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x0f, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x07, 0x52, 0x0e, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, + 0x53, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x11, 0x6f, 0x76, 0x65, + 0x72, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x61, 0x73, 0x63, 0x18, 0x10, + 0x20, 0x01, 0x28, 0x08, 0x48, 0x08, 0x52, 0x0f, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, + 0x53, 0x6f, 0x72, 0x74, 0x41, 0x73, 0x63, 0x88, 0x01, 0x01, 0x12, 0x43, 0x0a, 0x1b, 0x6f, 0x76, + 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x5f, + 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x09, 0x52, 0x19, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x45, 0x78, 0x70, 0x61, 0x6e, + 0x64, 0x65, 0x64, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, + 0x39, 0x0a, 0x16, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, + 0x6e, 0x5f, 0x6d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x0a, 0x52, 0x14, 0x74, 0x69, 0x6d, 0x65, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, + 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3e, 0x0a, 0x19, 0x74, 0x69, + 0x6d, 0x65, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x68, 0x61, + 0x72, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x48, 0x0b, 0x52, + 0x16, 0x74, 0x69, 0x6d, 0x65, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x68, + 0x61, 0x72, 0x74, 0x54, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x12, 0x74, 0x69, + 0x6d, 0x65, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x69, 0x6e, + 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x48, 0x0c, 0x52, 0x10, 0x74, 0x69, 0x6d, 0x65, 0x44, 0x69, + 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x69, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, + 0x0a, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x09, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x1d, 0x0a, 0x0a, + 0x70, 0x69, 0x76, 0x6f, 0x74, 0x5f, 0x63, 0x6f, 0x6c, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x09, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x43, 0x6f, 0x6c, 0x73, 0x12, 0x27, 0x0a, 0x0d, 0x70, + 0x69, 0x76, 0x6f, 0x74, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x17, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x0d, 0x52, 0x0b, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x53, 0x6f, 0x72, 0x74, 0x42, + 0x79, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x0e, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x5f, 0x73, 0x6f, + 0x72, 0x74, 0x5f, 0x61, 0x73, 0x63, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x48, 0x0e, 0x52, 0x0c, + 0x70, 0x69, 0x76, 0x6f, 0x74, 0x53, 0x6f, 0x72, 0x74, 0x41, 0x73, 0x63, 0x88, 0x01, 0x01, 0x42, + 0x08, 0x0a, 0x06, 0x5f, 0x77, 0x68, 0x65, 0x72, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x67, + 0x72, 0x61, 0x69, 0x6e, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x42, 0x17, 0x0a, 0x15, 0x5f, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, + 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x42, 0x13, 0x0a, + 0x11, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, + 0x62, 0x79, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x5f, + 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x61, 0x73, 0x63, 0x42, 0x1e, 0x0a, 0x1c, 0x5f, 0x6f, 0x76, 0x65, + 0x72, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x64, + 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x19, 0x0a, 0x17, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x61, 0x73, + 0x75, 0x72, 0x65, 0x42, 0x1c, 0x0a, 0x1a, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, 0x69, 0x6d, + 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, + 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x69, 0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x70, 0x69, 0x76, + 0x6f, 0x74, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x70, + 0x69, 0x76, 0x6f, 0x74, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x61, 0x73, 0x63, 0x22, 0xca, 0x01, + 0x0a, 0x0d, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, + 0x16, 0x0a, 0x06, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x06, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x3a, 0x0a, 0x06, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, + 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x16, 0x0a, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x12, + 0x2d, 0x0a, 0x11, 0x64, 0x75, 0x63, 0x6b, 0x64, 0x62, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x10, 0x64, 0x75, + 0x63, 0x6b, 0x64, 0x62, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x0a, + 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x29, 0x0a, 0x0f, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x76, 0x0a, 0x09, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, + 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x59, 0x0a, + 0x0d, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x12, 0x1c, + 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, + 0x73, 0x71, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x71, 0x6c, 0x12, 0x18, + 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x2a, 0x0a, 0x0e, 0x4d, 0x69, 0x67, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x6d, 0x0a, 0x06, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x2f, + 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, + 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, + 0x32, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, + 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x22, 0xf5, 0x05, 0x0a, 0x0a, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x70, + 0x65, 0x63, 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, 0x18, 0x0a, 0x07, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, + 0x44, 0x0a, 0x10, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, + 0x75, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, + 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x68, 0x65, + 0x64, 0x75, 0x6c, 0x65, 0x52, 0x0f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x63, 0x68, + 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, + 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1d, + 0x0a, 0x0a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x71, 0x75, 0x65, 0x72, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, + 0x0f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x41, 0x72, 0x67, + 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x5f, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x65, 0x78, 0x70, + 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x42, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x6f, + 0x72, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x0c, + 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x37, 0x0a, 0x09, + 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x09, 0x6e, 0x6f, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x4e, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x65, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x27, - 0x0a, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, - 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x81, 0x02, 0x0a, 0x0f, 0x52, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x61, - 0x64, 0x68, 0x6f, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x64, 0x68, 0x6f, - 0x63, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3b, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x77, 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, + 0x72, 0x6b, 0x5f, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x10, 0x77, 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x68, 0x65, 0x72, + 0x69, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, + 0x69, 0x73, 0x6f, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x14, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x49, 0x73, 0x6f, + 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x4c, 0x69, 0x6d, 0x69, + 0x74, 0x12, 0x38, 0x0a, 0x18, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x63, + 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x75, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x18, 0x0f, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x16, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x55, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x1a, 0x3e, 0x0a, 0x10, 0x41, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x90, 0x02, 0x0a, 0x0b, + 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0b, 0x6e, + 0x65, 0x78, 0x74, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x6e, 0x65, + 0x78, 0x74, 0x52, 0x75, 0x6e, 0x4f, 0x6e, 0x12, 0x4d, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x74, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4d, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, + 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x81, + 0x02, 0x0a, 0x0f, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x05, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3b, 0x0a, + 0x0b, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, + 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x65, 0x64, 0x4f, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, + 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, + 0x4f, 0x6e, 0x22, 0x6a, 0x0a, 0x05, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x73, + 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, + 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, + 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x31, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, + 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, + 0x72, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xf8, + 0x08, 0x0a, 0x09, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 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, + 0x18, 0x0a, 0x07, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x07, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x10, 0x72, 0x65, 0x66, + 0x72, 0x65, 0x73, 0x68, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x0f, + 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, + 0x2b, 0x0a, 0x11, 0x77, 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x5f, 0x69, 0x6e, 0x68, + 0x65, 0x72, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x77, 0x61, 0x74, 0x65, + 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x12, 0x34, 0x0a, 0x16, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x69, 0x73, 0x6f, 0x5f, 0x64, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x49, 0x73, 0x6f, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x38, 0x0a, 0x18, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x75, + 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x6e, 0x63, + 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, + 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1d, + 0x0a, 0x0a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x71, 0x75, 0x65, 0x72, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, + 0x0f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x41, 0x72, 0x67, + 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, + 0x72, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, + 0x72, 0x12, 0x48, 0x0a, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x72, + 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, + 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x46, + 0x6f, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x14, 0x71, 0x75, 0x65, 0x72, + 0x79, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, + 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x11, 0x71, 0x75, 0x65, 0x72, 0x79, 0x46, + 0x6f, 0x72, 0x55, 0x73, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x4b, 0x0a, 0x14, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x48, 0x00, 0x52, 0x12, 0x71, 0x75, 0x65, 0x72, 0x79, 0x46, 0x6f, 0x72, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6e, 0x6f, 0x74, 0x69, + 0x66, 0x79, 0x5f, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x18, 0x0f, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4f, 0x6e, 0x52, 0x65, 0x63, + 0x6f, 0x76, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x6f, + 0x6e, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6e, 0x6f, + 0x74, 0x69, 0x66, 0x79, 0x4f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x6f, + 0x74, 0x69, 0x66, 0x79, 0x5f, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x11, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4f, 0x6e, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x18, 0x12, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x34, + 0x0a, 0x16, 0x72, 0x65, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, + 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, + 0x72, 0x65, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x41, 0x66, 0x74, 0x65, 0x72, 0x53, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, + 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x52, 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x4d, 0x0a, + 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x14, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x41, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3e, 0x0a, 0x10, + 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0b, 0x0a, 0x09, + 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x6f, 0x72, 0x22, 0x61, 0x0a, 0x08, 0x4e, 0x6f, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x12, 0x37, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xc7, 0x02, 0x0a, + 0x0a, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, + 0x70, 0x65, 0x63, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x73, 0x70, 0x65, 0x63, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x66, 0x73, + 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x66, + 0x73, 0x48, 0x61, 0x73, 0x68, 0x12, 0x3a, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x72, 0x75, + 0x6e, 0x5f, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x6e, 0x65, 0x78, 0x74, 0x52, 0x75, 0x6e, 0x4f, + 0x6e, 0x12, 0x4c, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, + 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x6c, 0x65, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x4c, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, + 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, + 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x65, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x27, 0x0a, + 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x91, 0x03, 0x0a, 0x0e, 0x41, 0x6c, 0x65, 0x72, 0x74, + 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x68, + 0x6f, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x12, + 0x38, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x6e, + 0x74, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x73, 0x65, 0x6e, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x41, 0x0a, 0x0e, 0x65, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x65, 0x78, + 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x65, 0x64, 0x4f, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, + 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x54, - 0x69, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x6f, - 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x4f, 0x6e, 0x12, 0x3b, - 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x0a, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x4f, 0x6e, 0x22, 0x6a, 0x0a, 0x05, 0x41, - 0x6c, 0x65, 0x72, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, - 0x73, 0x70, 0x65, 0x63, 0x12, 0x31, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xf8, 0x08, 0x0a, 0x09, 0x41, 0x6c, 0x65, 0x72, - 0x74, 0x53, 0x70, 0x65, 0x63, 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, 0x18, 0x0a, 0x07, 0x74, 0x72, 0x69, 0x67, - 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x74, 0x72, 0x69, 0x67, 0x67, - 0x65, 0x72, 0x12, 0x44, 0x0a, 0x10, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x73, 0x63, - 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, - 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x0f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, - 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x77, 0x61, 0x74, 0x65, - 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x5f, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x10, 0x77, 0x61, 0x74, 0x65, 0x72, 0x6d, 0x61, 0x72, 0x6b, 0x49, 0x6e, - 0x68, 0x65, 0x72, 0x69, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, - 0x6c, 0x73, 0x5f, 0x69, 0x73, 0x6f, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, - 0x49, 0x73, 0x6f, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x4c, - 0x69, 0x6d, 0x69, 0x74, 0x12, 0x38, 0x0a, 0x18, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x75, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x12, 0x27, - 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, - 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, - 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x71, 0x75, 0x65, 0x72, 0x79, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x71, 0x75, 0x65, - 0x72, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, - 0x61, 0x72, 0x67, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x41, 0x72, 0x67, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x1a, - 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, + 0x64, 0x4f, 0x6e, 0x12, 0x45, 0x0a, 0x10, 0x73, 0x75, 0x70, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, + 0x64, 0x5f, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0f, 0x73, 0x75, 0x70, 0x70, 0x72, + 0x65, 0x73, 0x73, 0x65, 0x64, 0x53, 0x69, 0x6e, 0x63, 0x65, 0x22, 0xa4, 0x01, 0x0a, 0x0f, 0x41, + 0x73, 0x73, 0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x38, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, + 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x41, 0x73, 0x73, 0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x32, 0x0a, 0x08, 0x66, 0x61, 0x69, 0x6c, + 0x5f, 0x72, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x52, 0x07, 0x66, 0x61, 0x69, 0x6c, 0x52, 0x6f, 0x77, 0x12, 0x23, 0x0a, 0x0d, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x22, 0x7c, 0x0a, 0x0b, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, + 0x12, 0x34, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, + 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, + 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x37, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, + 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, + 0x11, 0x0a, 0x0f, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x70, + 0x65, 0x63, 0x22, 0x12, 0x0a, 0x10, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, + 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x66, 0x72, 0x65, + 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x04, 0x73, 0x70, 0x65, + 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, + 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, + 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, + 0x65, 0x63, 0x12, 0x3a, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, + 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x8f, + 0x01, 0x0a, 0x12, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, + 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x3b, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4d, 0x6f, 0x64, 0x65, + 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, + 0x22, 0x15, 0x0a, 0x13, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, + 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x95, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x66, 0x72, + 0x65, 0x73, 0x68, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, + 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x75, 0x6c, 0x6c, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x04, 0x66, 0x75, 0x6c, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x61, 0x72, + 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x70, + 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x61, 0x6c, 0x6c, + 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x61, 0x6c, 0x6c, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x65, 0x64, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, + 0x82, 0x01, 0x0a, 0x0d, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, + 0x72, 0x12, 0x36, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x53, + 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x39, 0x0a, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, + 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x22, 0x60, 0x0a, 0x11, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, + 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x4b, 0x0a, 0x0e, 0x65, 0x78, 0x74, + 0x72, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, + 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0d, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x2c, 0x0a, 0x12, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, + 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, + 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, + 0x67, 0x69, 0x6f, 0x6e, 0x22, 0xd6, 0x02, 0x0a, 0x13, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x45, + 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x52, 0x0a, 0x0d, + 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x74, 0x72, + 0x61, 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, + 0x67, 0x79, 0x52, 0x0c, 0x72, 0x6f, 0x77, 0x73, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, + 0x12, 0x28, 0x0a, 0x10, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x72, 0x6f, 0x77, 0x73, + 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x54, 0x0a, 0x0e, 0x66, 0x69, + 0x6c, 0x65, 0x73, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x74, 0x72, 0x61, + 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, + 0x79, 0x52, 0x0d, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, + 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x4c, 0x69, 0x6d, 0x69, + 0x74, 0x22, 0x4a, 0x0a, 0x08, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x18, 0x0a, + 0x14, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, + 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x52, 0x41, 0x54, + 0x45, 0x47, 0x59, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, + 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x54, 0x41, 0x49, 0x4c, 0x10, 0x02, 0x22, 0x6a, 0x0a, + 0x05, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x70, 0x65, 0x63, + 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x31, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x95, 0x02, 0x0a, 0x09, 0x54, 0x68, + 0x65, 0x6d, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x40, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6d, 0x61, + 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, + 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x44, 0x0a, 0x0f, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x48, 0x01, 0x52, 0x0e, 0x73, 0x65, + 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, + 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, + 0x5f, 0x72, 0x61, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x69, 0x6d, + 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x52, 0x61, 0x77, 0x12, 0x2e, 0x0a, 0x13, 0x73, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, + 0x61, 0x77, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x52, 0x61, 0x77, 0x42, 0x10, 0x0a, 0x0e, 0x5f, + 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x42, 0x12, 0x0a, + 0x10, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, + 0x72, 0x22, 0x0c, 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, + 0x76, 0x0a, 0x09, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x04, + 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, + 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, + 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, + 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xd6, 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, 0x20, 0x0a, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, + 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x12, 0x48, 0x0a, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, - 0x73, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, - 0x74, 0x69, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x6f, - 0x72, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x48, - 0x00, 0x52, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x46, 0x6f, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, - 0x64, 0x12, 0x31, 0x0a, 0x14, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x75, - 0x73, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x48, - 0x00, 0x52, 0x11, 0x71, 0x75, 0x65, 0x72, 0x79, 0x46, 0x6f, 0x72, 0x55, 0x73, 0x65, 0x72, 0x45, - 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x4b, 0x0a, 0x14, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x6f, - 0x72, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x48, 0x00, 0x52, 0x12, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x46, 0x6f, 0x72, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x6f, 0x6e, 0x5f, 0x72, - 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6e, 0x6f, - 0x74, 0x69, 0x66, 0x79, 0x4f, 0x6e, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x12, 0x24, 0x0a, - 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x6f, 0x6e, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x18, - 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4f, 0x6e, 0x46, - 0x61, 0x69, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x6f, 0x6e, - 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x6f, - 0x74, 0x69, 0x66, 0x79, 0x4f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x72, - 0x65, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, - 0x65, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x72, 0x65, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x79, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, - 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x72, 0x65, 0x6e, 0x6f, 0x74, 0x69, 0x66, - 0x79, 0x41, 0x66, 0x74, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x37, 0x0a, - 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x09, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x4d, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x72, 0x69, - 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, - 0x65, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, - 0x6f, 0x72, 0x22, 0x61, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1c, - 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x37, 0x0a, 0x0a, - 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x74, 0x69, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, + 0x12, 0x48, 0x0a, 0x13, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, + 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x05, 0x69, 0x6e, + 0x70, 0x75, 0x74, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, + 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x05, 0x69, + 0x6e, 0x70, 0x75, 0x74, 0x12, 0x3a, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, + 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x73, 0x68, 0x6f, 0x77, 0x12, 0x2a, 0x0a, 0x11, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, + 0x69, 0x6e, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x49, 0x6e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, + 0x22, 0x4f, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x73, 0x70, 0x65, 0x63, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, + 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x70, 0x65, + 0x63, 0x22, 0x78, 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, 0x61, + 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x3b, + 0x0a, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x64, + 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x6d, 0x0a, 0x06, 0x43, + 0x61, 0x6e, 0x76, 0x61, 0x73, 0x12, 0x2f, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, + 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x32, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x96, 0x02, 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, 0x18, 0x0a, 0x07, + 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, + 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x61, 0x70, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x03, 0x67, 0x61, 0x70, 0x12, 0x40, 0x0a, 0x09, 0x76, 0x61, 0x72, 0x69, + 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, + 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, + 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x05, 0x69, 0x74, + 0x65, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, + 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, + 0x61, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x44, 0x0a, + 0x0e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, + 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, + 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x75, + 0x6c, 0x65, 0x73, 0x22, 0x49, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x73, 0x70, 0x65, 0x63, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, + 0x70, 0x65, 0x63, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x70, 0x65, 0x63, 0x22, 0xd5, + 0x01, 0x0a, 0x0a, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1c, 0x0a, + 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x49, + 0x6e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x12, 0x11, 0x0a, 0x01, 0x78, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x48, 0x00, 0x52, 0x01, 0x78, 0x88, 0x01, 0x01, 0x12, 0x11, 0x0a, 0x01, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x01, 0x52, 0x01, 0x79, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, + 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x02, 0x52, 0x05, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x03, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x88, 0x01, 0x01, 0x42, 0x04, 0x0a, 0x02, 0x5f, 0x78, 0x42, 0x04, 0x0a, 0x02, 0x5f, + 0x79, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x42, 0x09, 0x0a, 0x07, 0x5f, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x64, 0x0a, 0x03, 0x41, 0x50, 0x49, 0x12, 0x2c, 0x0a, + 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, + 0x49, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x2f, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, + 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xb1, 0x02, 0x0a, + 0x07, 0x41, 0x50, 0x49, 0x53, 0x70, 0x65, 0x63, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x72, 0x12, 0x48, 0x0a, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x27, + 0x0a, 0x0f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, + 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x46, 0x0a, 0x12, 0x6f, 0x70, 0x65, 0x6e, 0x61, + 0x70, 0x69, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x11, 0x6f, 0x70, + 0x65, 0x6e, 0x61, 0x70, 0x69, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, + 0x4f, 0x0a, 0x17, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, - 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xc7, 0x02, 0x0a, 0x0a, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x53, + 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x15, 0x6f, 0x70, 0x65, 0x6e, 0x61, + 0x70, 0x69, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x22, 0x0a, 0x0a, 0x08, 0x41, 0x50, 0x49, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x9b, 0x01, 0x0a, + 0x08, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x66, + 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, + 0x65, 0x66, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x69, 0x73, 0x61, + 0x62, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, + 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, + 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x22, 0xa5, 0x01, 0x0a, 0x0a, 0x50, + 0x61, 0x72, 0x73, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, + 0x12, 0x44, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x4c, + 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x22, 0x50, 0x0a, 0x0f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, + 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x68, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, + 0x50, 0x61, 0x74, 0x68, 0x22, 0x4b, 0x0a, 0x0f, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, + 0x63, 0x79, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, + 0x79, 0x22, 0x2a, 0x0a, 0x0e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x22, 0x0a, + 0x0c, 0x43, 0x68, 0x61, 0x72, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x6c, 0x69, 0x6e, + 0x65, 0x22, 0xae, 0x03, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, + 0x70, 0x65, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x12, 0x4e, 0x0a, 0x0a, 0x70, + 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x2e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x2e, + 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, + 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x77, + 0x0a, 0x19, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x5f, 0x66, 0x72, 0x6f, + 0x6d, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x3b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, + 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x46, 0x72, 0x6f, 0x6d, + 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x17, + 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, + 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4a, 0x0a, 0x1c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x69, 0x65, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x22, 0x2d, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, 0x48, 0x61, 0x73, - 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x66, 0x73, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x66, 0x73, 0x48, 0x61, 0x73, 0x68, 0x12, 0x3a, - 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x6f, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x09, 0x6e, 0x65, 0x78, 0x74, 0x52, 0x75, 0x6e, 0x4f, 0x6e, 0x12, 0x4c, 0x0a, 0x11, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x45, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4c, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x05, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, - 0x91, 0x03, 0x0a, 0x0e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x05, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x12, 0x38, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x72, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, - 0x73, 0x65, 0x6e, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 0x41, 0x0a, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x54, 0x69, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, - 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x4f, 0x6e, 0x12, - 0x3b, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x0a, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x4f, 0x6e, 0x12, 0x45, 0x0a, 0x10, - 0x73, 0x75, 0x70, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x5f, 0x73, 0x69, 0x6e, 0x63, 0x65, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x52, 0x0f, 0x73, 0x75, 0x70, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x53, 0x69, - 0x6e, 0x63, 0x65, 0x22, 0xa4, 0x01, 0x0a, 0x0f, 0x41, 0x73, 0x73, 0x65, 0x72, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x38, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x72, 0x74, - 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x32, 0x0a, 0x08, 0x66, 0x61, 0x69, 0x6c, 0x5f, 0x72, 0x6f, 0x77, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x07, 0x66, 0x61, - 0x69, 0x6c, 0x52, 0x6f, 0x77, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x7c, 0x0a, 0x0b, 0x50, 0x75, - 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x04, 0x73, 0x70, 0x65, - 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, - 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, - 0x37, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, - 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x11, 0x0a, 0x0f, 0x50, 0x75, 0x6c, 0x6c, - 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x22, 0x12, 0x0a, 0x10, 0x50, - 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, - 0x85, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, - 0x65, 0x72, 0x12, 0x37, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x23, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, - 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x3a, 0x0a, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, - 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x8f, 0x01, 0x0a, 0x12, 0x52, 0x65, 0x66, 0x72, - 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x3b, - 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x06, 0x6d, - 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x69, - 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x66, 0x72, 0x65, 0x73, 0x68, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, - 0x72, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x22, 0x15, 0x0a, 0x13, 0x52, 0x65, 0x66, - 0x72, 0x65, 0x73, 0x68, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x22, 0x95, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4d, 0x6f, 0x64, 0x65, - 0x6c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, - 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x12, - 0x0a, 0x04, 0x66, 0x75, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x66, 0x75, - 0x6c, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x61, 0x6c, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x65, - 0x64, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x14, 0x61, 0x6c, 0x6c, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x65, 0x64, 0x50, 0x61, - 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x0d, 0x42, 0x75, 0x63, - 0x6b, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x36, 0x0a, 0x04, 0x73, 0x70, - 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, - 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, - 0x65, 0x63, 0x12, 0x39, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, - 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x60, 0x0a, - 0x11, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x70, - 0x65, 0x63, 0x12, 0x4b, 0x0a, 0x0e, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, - 0x6b, 0x65, 0x74, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x52, 0x0d, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, - 0x2c, 0x0a, 0x12, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x72, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x22, 0xd6, 0x02, - 0x0a, 0x13, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x52, 0x0a, 0x0d, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x73, 0x74, - 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x72, - 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, - 0x75, 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x2e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0c, 0x72, 0x6f, 0x77, - 0x73, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x6f, 0x77, - 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x0e, 0x72, 0x6f, 0x77, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x79, - 0x74, 0x65, 0x73, 0x12, 0x54, 0x0a, 0x0e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x73, 0x74, 0x72, - 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x72, 0x69, - 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, - 0x63, 0x6b, 0x65, 0x74, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x66, 0x69, 0x6c, 0x65, - 0x73, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x6c, - 0x65, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, - 0x66, 0x69, 0x6c, 0x65, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x4a, 0x0a, 0x08, 0x53, 0x74, - 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, - 0x47, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, - 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x48, 0x45, 0x41, - 0x44, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, - 0x54, 0x41, 0x49, 0x4c, 0x10, 0x02, 0x22, 0x6a, 0x0a, 0x05, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x12, - 0x2e, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, - 0x31, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, + 0x68, 0x22, 0x78, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x56, 0x32, + 0x12, 0x32, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x22, 0x95, 0x02, 0x0a, 0x09, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x70, 0x65, 0x63, - 0x12, 0x40, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, - 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x48, - 0x00, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x88, - 0x01, 0x01, 0x12, 0x44, 0x0a, 0x0f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, - 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x69, - 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, - 0x6c, 0x6f, 0x72, 0x48, 0x01, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, - 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x69, 0x6d, - 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x61, 0x77, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, - 0x72, 0x52, 0x61, 0x77, 0x12, 0x2e, 0x0a, 0x13, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, - 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x61, 0x77, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x11, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, - 0x72, 0x52, 0x61, 0x77, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, - 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, - 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x0c, 0x0a, 0x0a, 0x54, 0x68, - 0x65, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x76, 0x0a, 0x09, 0x43, 0x6f, 0x6d, 0x70, - 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, - 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, - 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x22, 0xd6, 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, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, - 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, - 0x76, 0x65, 0x72, 0x12, 0x48, 0x0a, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x5f, - 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x6c, - 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1a, 0x0a, - 0x08, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x12, 0x48, 0x0a, 0x13, 0x72, 0x65, 0x6e, - 0x64, 0x65, 0x72, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, - 0x12, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, - 0x69, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x08, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, 0x61, - 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x3a, 0x0a, - 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, - 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, - 0x65, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x68, 0x6f, - 0x77, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x12, 0x2a, 0x0a, - 0x11, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x63, 0x61, 0x6e, 0x76, - 0x61, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, - 0x64, 0x49, 0x6e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x22, 0x4f, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, - 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, - 0x09, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x70, 0x65, 0x63, 0x22, 0x78, 0x0a, 0x11, 0x43, 0x6f, - 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x3b, 0x0a, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, - 0x6c, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x22, 0x6d, 0x0a, 0x06, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x12, 0x2f, - 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, - 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, - 0x32, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, - 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x22, 0x96, 0x02, 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, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x12, - 0x10, 0x0a, 0x03, 0x67, 0x61, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x67, 0x61, - 0x70, 0x12, 0x40, 0x0a, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x05, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, - 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, - 0x6c, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x52, - 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x44, 0x0a, 0x0e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, - 0x74, 0x79, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, - 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x73, - 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x49, 0x0a, 0x0b, - 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0a, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, 0x52, 0x09, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x53, 0x70, 0x65, 0x63, 0x22, 0xd5, 0x01, 0x0a, 0x0a, 0x43, 0x61, 0x6e, 0x76, - 0x61, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, - 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, - 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, - 0x69, 0x6e, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x49, 0x6e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, - 0x12, 0x11, 0x0a, 0x01, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x01, 0x78, - 0x88, 0x01, 0x01, 0x12, 0x11, 0x0a, 0x01, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x01, - 0x52, 0x01, 0x79, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x02, 0x52, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x88, 0x01, - 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0d, 0x48, 0x03, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x42, 0x04, - 0x0a, 0x02, 0x5f, 0x78, 0x42, 0x04, 0x0a, 0x02, 0x5f, 0x79, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x77, - 0x69, 0x64, 0x74, 0x68, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, - 0x64, 0x0a, 0x03, 0x41, 0x50, 0x49, 0x12, 0x2c, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, - 0x73, 0x70, 0x65, 0x63, 0x12, 0x2f, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xb1, 0x02, 0x0a, 0x07, 0x41, 0x50, 0x49, 0x53, 0x70, 0x65, - 0x63, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x12, 0x48, 0x0a, - 0x13, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, - 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, - 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, - 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6f, 0x70, 0x65, 0x6e, 0x61, - 0x70, 0x69, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, - 0x12, 0x46, 0x0a, 0x12, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x70, 0x61, 0x72, 0x61, - 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x11, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x4f, 0x0a, 0x17, 0x6f, 0x70, 0x65, 0x6e, - 0x61, 0x70, 0x69, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, - 0x65, 0x6d, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x52, 0x15, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x0a, 0x0a, 0x08, 0x41, 0x50, 0x49, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x9b, 0x01, 0x0a, 0x08, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, - 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, 0x66, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, - 0x72, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x12, - 0x25, 0x0a, 0x0e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, - 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x5a, - 0x6f, 0x6e, 0x65, 0x22, 0xa5, 0x01, 0x0a, 0x0a, 0x50, 0x61, 0x72, 0x73, 0x65, 0x45, 0x72, 0x72, - 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, - 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x44, 0x0a, 0x0e, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x0d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x22, 0x50, 0x0a, 0x0f, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, - 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x70, - 0x65, 0x72, 0x74, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0c, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x50, 0x61, 0x74, 0x68, 0x22, 0x4b, 0x0a, - 0x0f, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x45, 0x72, 0x72, 0x6f, 0x72, - 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, - 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x2a, 0x0a, 0x0e, 0x45, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x22, 0x0a, 0x0c, 0x43, 0x68, 0x61, 0x72, 0x4c, 0x6f, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0xae, 0x03, 0x0a, 0x0d, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x16, 0x0a, 0x06, - 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x72, - 0x69, 0x76, 0x65, 0x72, 0x12, 0x4e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, - 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, - 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, - 0x74, 0x69, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x13, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, - 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x77, 0x0a, 0x19, 0x70, 0x72, 0x6f, 0x70, 0x65, - 0x72, 0x74, 0x69, 0x65, 0x73, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, - 0x62, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, - 0x72, 0x74, 0x69, 0x65, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, - 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x17, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, - 0x69, 0x65, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, - 0x1a, 0x3d, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, - 0x4a, 0x0a, 0x1c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x46, 0x72, 0x6f, - 0x6d, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x2d, 0x0a, 0x0e, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, - 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, 0x48, 0x61, 0x73, 0x68, 0x22, 0x78, 0x0a, 0x0b, 0x43, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x56, 0x32, 0x12, 0x32, 0x0a, 0x04, 0x73, 0x70, 0x65, - 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x35, 0x0a, - 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, - 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x2a, 0x8a, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, - 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x45, 0x43, 0x4f, - 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, - 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, - 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, - 0x44, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, - 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, - 0x47, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, - 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, - 0x03, 0x2a, 0xab, 0x01, 0x0a, 0x15, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6d, - 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x27, 0x0a, 0x23, 0x45, + 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, + 0x73, 0x70, 0x65, 0x63, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2a, 0x8a, 0x01, 0x0a, 0x0f, + 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x20, 0x0a, 0x1c, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, + 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, + 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, + 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, + 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, + 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, + 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x03, 0x2a, 0xab, 0x01, 0x0a, 0x15, 0x45, 0x78, 0x70, + 0x6c, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, 0x6f, + 0x64, 0x65, 0x12, 0x27, 0x0a, 0x23, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, + 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x20, 0x0a, 0x1c, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, - 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x20, 0x0a, 0x1c, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, - 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, - 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, - 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, - 0x45, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x10, 0x02, 0x12, 0x25, 0x0a, 0x21, 0x45, 0x58, 0x50, 0x4c, - 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, - 0x4f, 0x44, 0x45, 0x5f, 0x44, 0x49, 0x4d, 0x45, 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x2a, - 0x85, 0x01, 0x0a, 0x0f, 0x41, 0x73, 0x73, 0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x1c, 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, - 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, - 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, - 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x41, 0x53, 0x53, 0x10, 0x01, - 0x12, 0x19, 0x0a, 0x15, 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, - 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x41, + 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x20, 0x0a, + 0x1c, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, + 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x10, 0x02, 0x12, + 0x25, 0x0a, 0x21, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, + 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x44, 0x49, 0x4d, 0x45, 0x4e, + 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x2a, 0xbe, 0x01, 0x0a, 0x0e, 0x45, 0x78, 0x70, 0x6c, 0x6f, + 0x72, 0x65, 0x57, 0x65, 0x62, 0x56, 0x69, 0x65, 0x77, 0x12, 0x23, 0x0a, 0x1f, 0x45, 0x58, 0x50, + 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, + 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x20, + 0x0a, 0x1c, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, + 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x4f, 0x56, 0x45, 0x52, 0x56, 0x49, 0x45, 0x57, 0x10, 0x01, + 0x12, 0x26, 0x0a, 0x22, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, + 0x56, 0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x5f, 0x44, 0x49, 0x4d, + 0x45, 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, 0x45, 0x58, 0x50, 0x4c, + 0x4f, 0x52, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, + 0x50, 0x49, 0x56, 0x4f, 0x54, 0x10, 0x03, 0x12, 0x1e, 0x0a, 0x1a, 0x45, 0x58, 0x50, 0x4c, 0x4f, + 0x52, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x43, + 0x41, 0x4e, 0x56, 0x41, 0x53, 0x10, 0x04, 0x2a, 0x85, 0x01, 0x0a, 0x0f, 0x41, 0x73, 0x73, 0x65, + 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x1c, 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, - 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, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, + 0x15, 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, + 0x53, 0x5f, 0x50, 0x41, 0x53, 0x53, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x53, 0x53, 0x45, + 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x41, 0x49, + 0x4c, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, + 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x42, + 0xc1, 0x01, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x69, 0x6c, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x72, + 0x69, 0x6c, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x72, 0x69, + 0x6c, 0x6c, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x52, 0x52, 0x58, 0xaa, 0x02, 0x0f, + 0x52, 0x69, 0x6c, 0x6c, 0x2e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x56, 0x31, 0xca, + 0x02, 0x0f, 0x52, 0x69, 0x6c, 0x6c, 0x5c, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5c, 0x56, + 0x31, 0xe2, 0x02, 0x1b, 0x52, 0x69, 0x6c, 0x6c, 0x5c, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, + 0x02, 0x11, 0x52, 0x69, 0x6c, 0x6c, 0x3a, 0x3a, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x3a, + 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -7760,266 +8011,269 @@ func file_rill_runtime_v1_resources_proto_rawDescGZIP() []byte { return file_rill_runtime_v1_resources_proto_rawDescData } -var file_rill_runtime_v1_resources_proto_enumTypes = make([]protoimpl.EnumInfo, 6) +var file_rill_runtime_v1_resources_proto_enumTypes = make([]protoimpl.EnumInfo, 7) var file_rill_runtime_v1_resources_proto_msgTypes = make([]protoimpl.MessageInfo, 84) var file_rill_runtime_v1_resources_proto_goTypes = []any{ (ReconcileStatus)(0), // 0: rill.runtime.v1.ReconcileStatus (ExploreComparisonMode)(0), // 1: rill.runtime.v1.ExploreComparisonMode - (AssertionStatus)(0), // 2: rill.runtime.v1.AssertionStatus - (MetricsViewSpec_MeasureType)(0), // 3: rill.runtime.v1.MetricsViewSpec.MeasureType - (MetricsViewSpec_ComparisonMode)(0), // 4: rill.runtime.v1.MetricsViewSpec.ComparisonMode - (BucketExtractPolicy_Strategy)(0), // 5: rill.runtime.v1.BucketExtractPolicy.Strategy - (*Resource)(nil), // 6: rill.runtime.v1.Resource - (*ResourceMeta)(nil), // 7: rill.runtime.v1.ResourceMeta - (*ResourceName)(nil), // 8: rill.runtime.v1.ResourceName - (*ProjectParser)(nil), // 9: rill.runtime.v1.ProjectParser - (*ProjectParserSpec)(nil), // 10: rill.runtime.v1.ProjectParserSpec - (*ProjectParserState)(nil), // 11: rill.runtime.v1.ProjectParserState - (*SourceV2)(nil), // 12: rill.runtime.v1.SourceV2 - (*SourceSpec)(nil), // 13: rill.runtime.v1.SourceSpec - (*SourceState)(nil), // 14: rill.runtime.v1.SourceState - (*ModelV2)(nil), // 15: rill.runtime.v1.ModelV2 - (*ModelSpec)(nil), // 16: rill.runtime.v1.ModelSpec - (*ModelState)(nil), // 17: rill.runtime.v1.ModelState - (*MetricsViewV2)(nil), // 18: rill.runtime.v1.MetricsViewV2 - (*MetricsViewSpec)(nil), // 19: rill.runtime.v1.MetricsViewSpec - (*SecurityRule)(nil), // 20: rill.runtime.v1.SecurityRule - (*SecurityRuleAccess)(nil), // 21: rill.runtime.v1.SecurityRuleAccess - (*SecurityRuleFieldAccess)(nil), // 22: rill.runtime.v1.SecurityRuleFieldAccess - (*SecurityRuleRowFilter)(nil), // 23: rill.runtime.v1.SecurityRuleRowFilter - (*MetricsViewState)(nil), // 24: rill.runtime.v1.MetricsViewState - (*Explore)(nil), // 25: rill.runtime.v1.Explore - (*ExploreSpec)(nil), // 26: rill.runtime.v1.ExploreSpec - (*ExploreState)(nil), // 27: rill.runtime.v1.ExploreState - (*ExploreTimeRange)(nil), // 28: rill.runtime.v1.ExploreTimeRange - (*ExploreComparisonTimeRange)(nil), // 29: rill.runtime.v1.ExploreComparisonTimeRange - (*ExplorePreset)(nil), // 30: rill.runtime.v1.ExplorePreset - (*FieldSelector)(nil), // 31: rill.runtime.v1.FieldSelector - (*StringListValue)(nil), // 32: rill.runtime.v1.StringListValue - (*Migration)(nil), // 33: rill.runtime.v1.Migration - (*MigrationSpec)(nil), // 34: rill.runtime.v1.MigrationSpec - (*MigrationState)(nil), // 35: rill.runtime.v1.MigrationState - (*Report)(nil), // 36: rill.runtime.v1.Report - (*ReportSpec)(nil), // 37: rill.runtime.v1.ReportSpec - (*ReportState)(nil), // 38: rill.runtime.v1.ReportState - (*ReportExecution)(nil), // 39: rill.runtime.v1.ReportExecution - (*Alert)(nil), // 40: rill.runtime.v1.Alert - (*AlertSpec)(nil), // 41: rill.runtime.v1.AlertSpec - (*Notifier)(nil), // 42: rill.runtime.v1.Notifier - (*AlertState)(nil), // 43: rill.runtime.v1.AlertState - (*AlertExecution)(nil), // 44: rill.runtime.v1.AlertExecution - (*AssertionResult)(nil), // 45: rill.runtime.v1.AssertionResult - (*PullTrigger)(nil), // 46: rill.runtime.v1.PullTrigger - (*PullTriggerSpec)(nil), // 47: rill.runtime.v1.PullTriggerSpec - (*PullTriggerState)(nil), // 48: rill.runtime.v1.PullTriggerState - (*RefreshTrigger)(nil), // 49: rill.runtime.v1.RefreshTrigger - (*RefreshTriggerSpec)(nil), // 50: rill.runtime.v1.RefreshTriggerSpec - (*RefreshTriggerState)(nil), // 51: rill.runtime.v1.RefreshTriggerState - (*RefreshModelTrigger)(nil), // 52: rill.runtime.v1.RefreshModelTrigger - (*BucketPlanner)(nil), // 53: rill.runtime.v1.BucketPlanner - (*BucketPlannerSpec)(nil), // 54: rill.runtime.v1.BucketPlannerSpec - (*BucketPlannerState)(nil), // 55: rill.runtime.v1.BucketPlannerState - (*BucketExtractPolicy)(nil), // 56: rill.runtime.v1.BucketExtractPolicy - (*Theme)(nil), // 57: rill.runtime.v1.Theme - (*ThemeSpec)(nil), // 58: rill.runtime.v1.ThemeSpec - (*ThemeState)(nil), // 59: rill.runtime.v1.ThemeState - (*Component)(nil), // 60: rill.runtime.v1.Component - (*ComponentSpec)(nil), // 61: rill.runtime.v1.ComponentSpec - (*ComponentState)(nil), // 62: rill.runtime.v1.ComponentState - (*ComponentVariable)(nil), // 63: rill.runtime.v1.ComponentVariable - (*Canvas)(nil), // 64: rill.runtime.v1.Canvas - (*CanvasSpec)(nil), // 65: rill.runtime.v1.CanvasSpec - (*CanvasState)(nil), // 66: rill.runtime.v1.CanvasState - (*CanvasItem)(nil), // 67: rill.runtime.v1.CanvasItem - (*API)(nil), // 68: rill.runtime.v1.API - (*APISpec)(nil), // 69: rill.runtime.v1.APISpec - (*APIState)(nil), // 70: rill.runtime.v1.APIState - (*Schedule)(nil), // 71: rill.runtime.v1.Schedule - (*ParseError)(nil), // 72: rill.runtime.v1.ParseError - (*ValidationError)(nil), // 73: rill.runtime.v1.ValidationError - (*DependencyError)(nil), // 74: rill.runtime.v1.DependencyError - (*ExecutionError)(nil), // 75: rill.runtime.v1.ExecutionError - (*CharLocation)(nil), // 76: rill.runtime.v1.CharLocation - (*ConnectorSpec)(nil), // 77: rill.runtime.v1.ConnectorSpec - (*ConnectorState)(nil), // 78: rill.runtime.v1.ConnectorState - (*ConnectorV2)(nil), // 79: rill.runtime.v1.ConnectorV2 - (*MetricsViewSpec_DimensionV2)(nil), // 80: rill.runtime.v1.MetricsViewSpec.DimensionV2 - (*MetricsViewSpec_DimensionSelector)(nil), // 81: rill.runtime.v1.MetricsViewSpec.DimensionSelector - (*MetricsViewSpec_MeasureWindow)(nil), // 82: rill.runtime.v1.MetricsViewSpec.MeasureWindow - (*MetricsViewSpec_MeasureV2)(nil), // 83: rill.runtime.v1.MetricsViewSpec.MeasureV2 - (*MetricsViewSpec_AvailableComparisonOffset)(nil), // 84: rill.runtime.v1.MetricsViewSpec.AvailableComparisonOffset - (*MetricsViewSpec_AvailableTimeRange)(nil), // 85: rill.runtime.v1.MetricsViewSpec.AvailableTimeRange - nil, // 86: rill.runtime.v1.ReportSpec.AnnotationsEntry - nil, // 87: rill.runtime.v1.AlertSpec.AnnotationsEntry - nil, // 88: rill.runtime.v1.ConnectorSpec.PropertiesEntry - nil, // 89: rill.runtime.v1.ConnectorSpec.PropertiesFromVariablesEntry - (*timestamppb.Timestamp)(nil), // 90: google.protobuf.Timestamp - (*structpb.Struct)(nil), // 91: google.protobuf.Struct - (*StructType)(nil), // 92: rill.runtime.v1.StructType - (TimeGrain)(0), // 93: rill.runtime.v1.TimeGrain - (*Expression)(nil), // 94: rill.runtime.v1.Expression - (ExportFormat)(0), // 95: rill.runtime.v1.ExportFormat - (*Color)(nil), // 96: rill.runtime.v1.Color - (*structpb.Value)(nil), // 97: google.protobuf.Value + (ExploreWebView)(0), // 2: rill.runtime.v1.ExploreWebView + (AssertionStatus)(0), // 3: rill.runtime.v1.AssertionStatus + (MetricsViewSpec_MeasureType)(0), // 4: rill.runtime.v1.MetricsViewSpec.MeasureType + (MetricsViewSpec_ComparisonMode)(0), // 5: rill.runtime.v1.MetricsViewSpec.ComparisonMode + (BucketExtractPolicy_Strategy)(0), // 6: rill.runtime.v1.BucketExtractPolicy.Strategy + (*Resource)(nil), // 7: rill.runtime.v1.Resource + (*ResourceMeta)(nil), // 8: rill.runtime.v1.ResourceMeta + (*ResourceName)(nil), // 9: rill.runtime.v1.ResourceName + (*ProjectParser)(nil), // 10: rill.runtime.v1.ProjectParser + (*ProjectParserSpec)(nil), // 11: rill.runtime.v1.ProjectParserSpec + (*ProjectParserState)(nil), // 12: rill.runtime.v1.ProjectParserState + (*SourceV2)(nil), // 13: rill.runtime.v1.SourceV2 + (*SourceSpec)(nil), // 14: rill.runtime.v1.SourceSpec + (*SourceState)(nil), // 15: rill.runtime.v1.SourceState + (*ModelV2)(nil), // 16: rill.runtime.v1.ModelV2 + (*ModelSpec)(nil), // 17: rill.runtime.v1.ModelSpec + (*ModelState)(nil), // 18: rill.runtime.v1.ModelState + (*MetricsViewV2)(nil), // 19: rill.runtime.v1.MetricsViewV2 + (*MetricsViewSpec)(nil), // 20: rill.runtime.v1.MetricsViewSpec + (*SecurityRule)(nil), // 21: rill.runtime.v1.SecurityRule + (*SecurityRuleAccess)(nil), // 22: rill.runtime.v1.SecurityRuleAccess + (*SecurityRuleFieldAccess)(nil), // 23: rill.runtime.v1.SecurityRuleFieldAccess + (*SecurityRuleRowFilter)(nil), // 24: rill.runtime.v1.SecurityRuleRowFilter + (*MetricsViewState)(nil), // 25: rill.runtime.v1.MetricsViewState + (*Explore)(nil), // 26: rill.runtime.v1.Explore + (*ExploreSpec)(nil), // 27: rill.runtime.v1.ExploreSpec + (*ExploreState)(nil), // 28: rill.runtime.v1.ExploreState + (*ExploreTimeRange)(nil), // 29: rill.runtime.v1.ExploreTimeRange + (*ExploreComparisonTimeRange)(nil), // 30: rill.runtime.v1.ExploreComparisonTimeRange + (*ExplorePreset)(nil), // 31: rill.runtime.v1.ExplorePreset + (*FieldSelector)(nil), // 32: rill.runtime.v1.FieldSelector + (*StringListValue)(nil), // 33: rill.runtime.v1.StringListValue + (*Migration)(nil), // 34: rill.runtime.v1.Migration + (*MigrationSpec)(nil), // 35: rill.runtime.v1.MigrationSpec + (*MigrationState)(nil), // 36: rill.runtime.v1.MigrationState + (*Report)(nil), // 37: rill.runtime.v1.Report + (*ReportSpec)(nil), // 38: rill.runtime.v1.ReportSpec + (*ReportState)(nil), // 39: rill.runtime.v1.ReportState + (*ReportExecution)(nil), // 40: rill.runtime.v1.ReportExecution + (*Alert)(nil), // 41: rill.runtime.v1.Alert + (*AlertSpec)(nil), // 42: rill.runtime.v1.AlertSpec + (*Notifier)(nil), // 43: rill.runtime.v1.Notifier + (*AlertState)(nil), // 44: rill.runtime.v1.AlertState + (*AlertExecution)(nil), // 45: rill.runtime.v1.AlertExecution + (*AssertionResult)(nil), // 46: rill.runtime.v1.AssertionResult + (*PullTrigger)(nil), // 47: rill.runtime.v1.PullTrigger + (*PullTriggerSpec)(nil), // 48: rill.runtime.v1.PullTriggerSpec + (*PullTriggerState)(nil), // 49: rill.runtime.v1.PullTriggerState + (*RefreshTrigger)(nil), // 50: rill.runtime.v1.RefreshTrigger + (*RefreshTriggerSpec)(nil), // 51: rill.runtime.v1.RefreshTriggerSpec + (*RefreshTriggerState)(nil), // 52: rill.runtime.v1.RefreshTriggerState + (*RefreshModelTrigger)(nil), // 53: rill.runtime.v1.RefreshModelTrigger + (*BucketPlanner)(nil), // 54: rill.runtime.v1.BucketPlanner + (*BucketPlannerSpec)(nil), // 55: rill.runtime.v1.BucketPlannerSpec + (*BucketPlannerState)(nil), // 56: rill.runtime.v1.BucketPlannerState + (*BucketExtractPolicy)(nil), // 57: rill.runtime.v1.BucketExtractPolicy + (*Theme)(nil), // 58: rill.runtime.v1.Theme + (*ThemeSpec)(nil), // 59: rill.runtime.v1.ThemeSpec + (*ThemeState)(nil), // 60: rill.runtime.v1.ThemeState + (*Component)(nil), // 61: rill.runtime.v1.Component + (*ComponentSpec)(nil), // 62: rill.runtime.v1.ComponentSpec + (*ComponentState)(nil), // 63: rill.runtime.v1.ComponentState + (*ComponentVariable)(nil), // 64: rill.runtime.v1.ComponentVariable + (*Canvas)(nil), // 65: rill.runtime.v1.Canvas + (*CanvasSpec)(nil), // 66: rill.runtime.v1.CanvasSpec + (*CanvasState)(nil), // 67: rill.runtime.v1.CanvasState + (*CanvasItem)(nil), // 68: rill.runtime.v1.CanvasItem + (*API)(nil), // 69: rill.runtime.v1.API + (*APISpec)(nil), // 70: rill.runtime.v1.APISpec + (*APIState)(nil), // 71: rill.runtime.v1.APIState + (*Schedule)(nil), // 72: rill.runtime.v1.Schedule + (*ParseError)(nil), // 73: rill.runtime.v1.ParseError + (*ValidationError)(nil), // 74: rill.runtime.v1.ValidationError + (*DependencyError)(nil), // 75: rill.runtime.v1.DependencyError + (*ExecutionError)(nil), // 76: rill.runtime.v1.ExecutionError + (*CharLocation)(nil), // 77: rill.runtime.v1.CharLocation + (*ConnectorSpec)(nil), // 78: rill.runtime.v1.ConnectorSpec + (*ConnectorState)(nil), // 79: rill.runtime.v1.ConnectorState + (*ConnectorV2)(nil), // 80: rill.runtime.v1.ConnectorV2 + (*MetricsViewSpec_DimensionV2)(nil), // 81: rill.runtime.v1.MetricsViewSpec.DimensionV2 + (*MetricsViewSpec_DimensionSelector)(nil), // 82: rill.runtime.v1.MetricsViewSpec.DimensionSelector + (*MetricsViewSpec_MeasureWindow)(nil), // 83: rill.runtime.v1.MetricsViewSpec.MeasureWindow + (*MetricsViewSpec_MeasureV2)(nil), // 84: rill.runtime.v1.MetricsViewSpec.MeasureV2 + (*MetricsViewSpec_AvailableComparisonOffset)(nil), // 85: rill.runtime.v1.MetricsViewSpec.AvailableComparisonOffset + (*MetricsViewSpec_AvailableTimeRange)(nil), // 86: rill.runtime.v1.MetricsViewSpec.AvailableTimeRange + nil, // 87: rill.runtime.v1.ReportSpec.AnnotationsEntry + nil, // 88: rill.runtime.v1.AlertSpec.AnnotationsEntry + nil, // 89: rill.runtime.v1.ConnectorSpec.PropertiesEntry + nil, // 90: rill.runtime.v1.ConnectorSpec.PropertiesFromVariablesEntry + (*timestamppb.Timestamp)(nil), // 91: google.protobuf.Timestamp + (*structpb.Struct)(nil), // 92: google.protobuf.Struct + (*StructType)(nil), // 93: rill.runtime.v1.StructType + (TimeGrain)(0), // 94: rill.runtime.v1.TimeGrain + (*Expression)(nil), // 95: rill.runtime.v1.Expression + (ExportFormat)(0), // 96: rill.runtime.v1.ExportFormat + (*Color)(nil), // 97: rill.runtime.v1.Color + (*structpb.Value)(nil), // 98: google.protobuf.Value } var file_rill_runtime_v1_resources_proto_depIdxs = []int32{ - 7, // 0: rill.runtime.v1.Resource.meta:type_name -> rill.runtime.v1.ResourceMeta - 9, // 1: rill.runtime.v1.Resource.project_parser:type_name -> rill.runtime.v1.ProjectParser - 12, // 2: rill.runtime.v1.Resource.source:type_name -> rill.runtime.v1.SourceV2 - 15, // 3: rill.runtime.v1.Resource.model:type_name -> rill.runtime.v1.ModelV2 - 18, // 4: rill.runtime.v1.Resource.metrics_view:type_name -> rill.runtime.v1.MetricsViewV2 - 25, // 5: rill.runtime.v1.Resource.explore:type_name -> rill.runtime.v1.Explore - 33, // 6: rill.runtime.v1.Resource.migration:type_name -> rill.runtime.v1.Migration - 36, // 7: rill.runtime.v1.Resource.report:type_name -> rill.runtime.v1.Report - 40, // 8: rill.runtime.v1.Resource.alert:type_name -> rill.runtime.v1.Alert - 46, // 9: rill.runtime.v1.Resource.pull_trigger:type_name -> rill.runtime.v1.PullTrigger - 49, // 10: rill.runtime.v1.Resource.refresh_trigger:type_name -> rill.runtime.v1.RefreshTrigger - 53, // 11: rill.runtime.v1.Resource.bucket_planner:type_name -> rill.runtime.v1.BucketPlanner - 57, // 12: rill.runtime.v1.Resource.theme:type_name -> rill.runtime.v1.Theme - 60, // 13: rill.runtime.v1.Resource.component:type_name -> rill.runtime.v1.Component - 64, // 14: rill.runtime.v1.Resource.canvas:type_name -> rill.runtime.v1.Canvas - 68, // 15: rill.runtime.v1.Resource.api:type_name -> rill.runtime.v1.API - 79, // 16: rill.runtime.v1.Resource.connector:type_name -> rill.runtime.v1.ConnectorV2 - 8, // 17: rill.runtime.v1.ResourceMeta.name:type_name -> rill.runtime.v1.ResourceName - 8, // 18: rill.runtime.v1.ResourceMeta.refs:type_name -> rill.runtime.v1.ResourceName - 8, // 19: rill.runtime.v1.ResourceMeta.owner:type_name -> rill.runtime.v1.ResourceName - 90, // 20: rill.runtime.v1.ResourceMeta.created_on:type_name -> google.protobuf.Timestamp - 90, // 21: rill.runtime.v1.ResourceMeta.spec_updated_on:type_name -> google.protobuf.Timestamp - 90, // 22: rill.runtime.v1.ResourceMeta.state_updated_on:type_name -> google.protobuf.Timestamp - 90, // 23: rill.runtime.v1.ResourceMeta.deleted_on:type_name -> google.protobuf.Timestamp + 8, // 0: rill.runtime.v1.Resource.meta:type_name -> rill.runtime.v1.ResourceMeta + 10, // 1: rill.runtime.v1.Resource.project_parser:type_name -> rill.runtime.v1.ProjectParser + 13, // 2: rill.runtime.v1.Resource.source:type_name -> rill.runtime.v1.SourceV2 + 16, // 3: rill.runtime.v1.Resource.model:type_name -> rill.runtime.v1.ModelV2 + 19, // 4: rill.runtime.v1.Resource.metrics_view:type_name -> rill.runtime.v1.MetricsViewV2 + 26, // 5: rill.runtime.v1.Resource.explore:type_name -> rill.runtime.v1.Explore + 34, // 6: rill.runtime.v1.Resource.migration:type_name -> rill.runtime.v1.Migration + 37, // 7: rill.runtime.v1.Resource.report:type_name -> rill.runtime.v1.Report + 41, // 8: rill.runtime.v1.Resource.alert:type_name -> rill.runtime.v1.Alert + 47, // 9: rill.runtime.v1.Resource.pull_trigger:type_name -> rill.runtime.v1.PullTrigger + 50, // 10: rill.runtime.v1.Resource.refresh_trigger:type_name -> rill.runtime.v1.RefreshTrigger + 54, // 11: rill.runtime.v1.Resource.bucket_planner:type_name -> rill.runtime.v1.BucketPlanner + 58, // 12: rill.runtime.v1.Resource.theme:type_name -> rill.runtime.v1.Theme + 61, // 13: rill.runtime.v1.Resource.component:type_name -> rill.runtime.v1.Component + 65, // 14: rill.runtime.v1.Resource.canvas:type_name -> rill.runtime.v1.Canvas + 69, // 15: rill.runtime.v1.Resource.api:type_name -> rill.runtime.v1.API + 80, // 16: rill.runtime.v1.Resource.connector:type_name -> rill.runtime.v1.ConnectorV2 + 9, // 17: rill.runtime.v1.ResourceMeta.name:type_name -> rill.runtime.v1.ResourceName + 9, // 18: rill.runtime.v1.ResourceMeta.refs:type_name -> rill.runtime.v1.ResourceName + 9, // 19: rill.runtime.v1.ResourceMeta.owner:type_name -> rill.runtime.v1.ResourceName + 91, // 20: rill.runtime.v1.ResourceMeta.created_on:type_name -> google.protobuf.Timestamp + 91, // 21: rill.runtime.v1.ResourceMeta.spec_updated_on:type_name -> google.protobuf.Timestamp + 91, // 22: rill.runtime.v1.ResourceMeta.state_updated_on:type_name -> google.protobuf.Timestamp + 91, // 23: rill.runtime.v1.ResourceMeta.deleted_on:type_name -> google.protobuf.Timestamp 0, // 24: rill.runtime.v1.ResourceMeta.reconcile_status:type_name -> rill.runtime.v1.ReconcileStatus - 90, // 25: rill.runtime.v1.ResourceMeta.reconcile_on:type_name -> google.protobuf.Timestamp - 8, // 26: rill.runtime.v1.ResourceMeta.renamed_from:type_name -> rill.runtime.v1.ResourceName - 10, // 27: rill.runtime.v1.ProjectParser.spec:type_name -> rill.runtime.v1.ProjectParserSpec - 11, // 28: rill.runtime.v1.ProjectParser.state:type_name -> rill.runtime.v1.ProjectParserState - 72, // 29: rill.runtime.v1.ProjectParserState.parse_errors:type_name -> rill.runtime.v1.ParseError - 13, // 30: rill.runtime.v1.SourceV2.spec:type_name -> rill.runtime.v1.SourceSpec - 14, // 31: rill.runtime.v1.SourceV2.state:type_name -> rill.runtime.v1.SourceState - 91, // 32: rill.runtime.v1.SourceSpec.properties:type_name -> google.protobuf.Struct - 71, // 33: rill.runtime.v1.SourceSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule - 90, // 34: rill.runtime.v1.SourceState.refreshed_on:type_name -> google.protobuf.Timestamp - 16, // 35: rill.runtime.v1.ModelV2.spec:type_name -> rill.runtime.v1.ModelSpec - 17, // 36: rill.runtime.v1.ModelV2.state:type_name -> rill.runtime.v1.ModelState - 71, // 37: rill.runtime.v1.ModelSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule - 91, // 38: rill.runtime.v1.ModelSpec.incremental_state_resolver_properties:type_name -> google.protobuf.Struct - 91, // 39: rill.runtime.v1.ModelSpec.partitions_resolver_properties:type_name -> google.protobuf.Struct - 91, // 40: rill.runtime.v1.ModelSpec.input_properties:type_name -> google.protobuf.Struct - 91, // 41: rill.runtime.v1.ModelSpec.stage_properties:type_name -> google.protobuf.Struct - 91, // 42: rill.runtime.v1.ModelSpec.output_properties:type_name -> google.protobuf.Struct - 91, // 43: rill.runtime.v1.ModelState.result_properties:type_name -> google.protobuf.Struct - 90, // 44: rill.runtime.v1.ModelState.refreshed_on:type_name -> google.protobuf.Timestamp - 91, // 45: rill.runtime.v1.ModelState.incremental_state:type_name -> google.protobuf.Struct - 92, // 46: rill.runtime.v1.ModelState.incremental_state_schema:type_name -> rill.runtime.v1.StructType - 19, // 47: rill.runtime.v1.MetricsViewV2.spec:type_name -> rill.runtime.v1.MetricsViewSpec - 24, // 48: rill.runtime.v1.MetricsViewV2.state:type_name -> rill.runtime.v1.MetricsViewState - 93, // 49: rill.runtime.v1.MetricsViewSpec.smallest_time_grain:type_name -> rill.runtime.v1.TimeGrain - 80, // 50: rill.runtime.v1.MetricsViewSpec.dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionV2 - 83, // 51: rill.runtime.v1.MetricsViewSpec.measures:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureV2 - 20, // 52: rill.runtime.v1.MetricsViewSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule - 4, // 53: rill.runtime.v1.MetricsViewSpec.default_comparison_mode:type_name -> rill.runtime.v1.MetricsViewSpec.ComparisonMode - 85, // 54: rill.runtime.v1.MetricsViewSpec.available_time_ranges:type_name -> rill.runtime.v1.MetricsViewSpec.AvailableTimeRange - 21, // 55: rill.runtime.v1.SecurityRule.access:type_name -> rill.runtime.v1.SecurityRuleAccess - 22, // 56: rill.runtime.v1.SecurityRule.field_access:type_name -> rill.runtime.v1.SecurityRuleFieldAccess - 23, // 57: rill.runtime.v1.SecurityRule.row_filter:type_name -> rill.runtime.v1.SecurityRuleRowFilter - 94, // 58: rill.runtime.v1.SecurityRuleRowFilter.expression:type_name -> rill.runtime.v1.Expression - 19, // 59: rill.runtime.v1.MetricsViewState.valid_spec:type_name -> rill.runtime.v1.MetricsViewSpec - 26, // 60: rill.runtime.v1.Explore.spec:type_name -> rill.runtime.v1.ExploreSpec - 27, // 61: rill.runtime.v1.Explore.state:type_name -> rill.runtime.v1.ExploreState - 31, // 62: rill.runtime.v1.ExploreSpec.dimensions_selector:type_name -> rill.runtime.v1.FieldSelector - 31, // 63: rill.runtime.v1.ExploreSpec.measures_selector:type_name -> rill.runtime.v1.FieldSelector - 58, // 64: rill.runtime.v1.ExploreSpec.embedded_theme:type_name -> rill.runtime.v1.ThemeSpec - 28, // 65: rill.runtime.v1.ExploreSpec.time_ranges:type_name -> rill.runtime.v1.ExploreTimeRange - 30, // 66: rill.runtime.v1.ExploreSpec.default_preset:type_name -> rill.runtime.v1.ExplorePreset - 20, // 67: rill.runtime.v1.ExploreSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule - 26, // 68: rill.runtime.v1.ExploreState.valid_spec:type_name -> rill.runtime.v1.ExploreSpec - 29, // 69: rill.runtime.v1.ExploreTimeRange.comparison_time_ranges:type_name -> rill.runtime.v1.ExploreComparisonTimeRange - 31, // 70: rill.runtime.v1.ExplorePreset.dimensions_selector:type_name -> rill.runtime.v1.FieldSelector - 31, // 71: rill.runtime.v1.ExplorePreset.measures_selector:type_name -> rill.runtime.v1.FieldSelector - 1, // 72: rill.runtime.v1.ExplorePreset.comparison_mode:type_name -> rill.runtime.v1.ExploreComparisonMode - 32, // 73: rill.runtime.v1.FieldSelector.fields:type_name -> rill.runtime.v1.StringListValue - 34, // 74: rill.runtime.v1.Migration.spec:type_name -> rill.runtime.v1.MigrationSpec - 35, // 75: rill.runtime.v1.Migration.state:type_name -> rill.runtime.v1.MigrationState - 37, // 76: rill.runtime.v1.Report.spec:type_name -> rill.runtime.v1.ReportSpec - 38, // 77: rill.runtime.v1.Report.state:type_name -> rill.runtime.v1.ReportState - 71, // 78: rill.runtime.v1.ReportSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule - 95, // 79: rill.runtime.v1.ReportSpec.export_format:type_name -> rill.runtime.v1.ExportFormat - 42, // 80: rill.runtime.v1.ReportSpec.notifiers:type_name -> rill.runtime.v1.Notifier - 86, // 81: rill.runtime.v1.ReportSpec.annotations:type_name -> rill.runtime.v1.ReportSpec.AnnotationsEntry - 90, // 82: rill.runtime.v1.ReportState.next_run_on:type_name -> google.protobuf.Timestamp - 39, // 83: rill.runtime.v1.ReportState.current_execution:type_name -> rill.runtime.v1.ReportExecution - 39, // 84: rill.runtime.v1.ReportState.execution_history:type_name -> rill.runtime.v1.ReportExecution - 90, // 85: rill.runtime.v1.ReportExecution.report_time:type_name -> google.protobuf.Timestamp - 90, // 86: rill.runtime.v1.ReportExecution.started_on:type_name -> google.protobuf.Timestamp - 90, // 87: rill.runtime.v1.ReportExecution.finished_on:type_name -> google.protobuf.Timestamp - 41, // 88: rill.runtime.v1.Alert.spec:type_name -> rill.runtime.v1.AlertSpec - 43, // 89: rill.runtime.v1.Alert.state:type_name -> rill.runtime.v1.AlertState - 71, // 90: rill.runtime.v1.AlertSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule - 91, // 91: rill.runtime.v1.AlertSpec.resolver_properties:type_name -> google.protobuf.Struct - 91, // 92: rill.runtime.v1.AlertSpec.query_for_attributes:type_name -> google.protobuf.Struct - 42, // 93: rill.runtime.v1.AlertSpec.notifiers:type_name -> rill.runtime.v1.Notifier - 87, // 94: rill.runtime.v1.AlertSpec.annotations:type_name -> rill.runtime.v1.AlertSpec.AnnotationsEntry - 91, // 95: rill.runtime.v1.Notifier.properties:type_name -> google.protobuf.Struct - 90, // 96: rill.runtime.v1.AlertState.next_run_on:type_name -> google.protobuf.Timestamp - 44, // 97: rill.runtime.v1.AlertState.current_execution:type_name -> rill.runtime.v1.AlertExecution - 44, // 98: rill.runtime.v1.AlertState.execution_history:type_name -> rill.runtime.v1.AlertExecution - 45, // 99: rill.runtime.v1.AlertExecution.result:type_name -> rill.runtime.v1.AssertionResult - 90, // 100: rill.runtime.v1.AlertExecution.execution_time:type_name -> google.protobuf.Timestamp - 90, // 101: rill.runtime.v1.AlertExecution.started_on:type_name -> google.protobuf.Timestamp - 90, // 102: rill.runtime.v1.AlertExecution.finished_on:type_name -> google.protobuf.Timestamp - 90, // 103: rill.runtime.v1.AlertExecution.suppressed_since:type_name -> google.protobuf.Timestamp - 2, // 104: rill.runtime.v1.AssertionResult.status:type_name -> rill.runtime.v1.AssertionStatus - 91, // 105: rill.runtime.v1.AssertionResult.fail_row:type_name -> google.protobuf.Struct - 47, // 106: rill.runtime.v1.PullTrigger.spec:type_name -> rill.runtime.v1.PullTriggerSpec - 48, // 107: rill.runtime.v1.PullTrigger.state:type_name -> rill.runtime.v1.PullTriggerState - 50, // 108: rill.runtime.v1.RefreshTrigger.spec:type_name -> rill.runtime.v1.RefreshTriggerSpec - 51, // 109: rill.runtime.v1.RefreshTrigger.state:type_name -> rill.runtime.v1.RefreshTriggerState - 8, // 110: rill.runtime.v1.RefreshTriggerSpec.resources:type_name -> rill.runtime.v1.ResourceName - 52, // 111: rill.runtime.v1.RefreshTriggerSpec.models:type_name -> rill.runtime.v1.RefreshModelTrigger - 54, // 112: rill.runtime.v1.BucketPlanner.spec:type_name -> rill.runtime.v1.BucketPlannerSpec - 55, // 113: rill.runtime.v1.BucketPlanner.state:type_name -> rill.runtime.v1.BucketPlannerState - 56, // 114: rill.runtime.v1.BucketPlannerSpec.extract_policy:type_name -> rill.runtime.v1.BucketExtractPolicy - 5, // 115: rill.runtime.v1.BucketExtractPolicy.rows_strategy:type_name -> rill.runtime.v1.BucketExtractPolicy.Strategy - 5, // 116: rill.runtime.v1.BucketExtractPolicy.files_strategy:type_name -> rill.runtime.v1.BucketExtractPolicy.Strategy - 58, // 117: rill.runtime.v1.Theme.spec:type_name -> rill.runtime.v1.ThemeSpec - 59, // 118: rill.runtime.v1.Theme.state:type_name -> rill.runtime.v1.ThemeState - 96, // 119: rill.runtime.v1.ThemeSpec.primary_color:type_name -> rill.runtime.v1.Color - 96, // 120: rill.runtime.v1.ThemeSpec.secondary_color:type_name -> rill.runtime.v1.Color - 61, // 121: rill.runtime.v1.Component.spec:type_name -> rill.runtime.v1.ComponentSpec - 62, // 122: rill.runtime.v1.Component.state:type_name -> rill.runtime.v1.ComponentState - 91, // 123: rill.runtime.v1.ComponentSpec.resolver_properties:type_name -> google.protobuf.Struct - 91, // 124: rill.runtime.v1.ComponentSpec.renderer_properties:type_name -> google.protobuf.Struct - 63, // 125: rill.runtime.v1.ComponentSpec.input:type_name -> rill.runtime.v1.ComponentVariable - 63, // 126: rill.runtime.v1.ComponentSpec.output:type_name -> rill.runtime.v1.ComponentVariable - 61, // 127: rill.runtime.v1.ComponentState.valid_spec:type_name -> rill.runtime.v1.ComponentSpec - 97, // 128: rill.runtime.v1.ComponentVariable.default_value:type_name -> google.protobuf.Value - 65, // 129: rill.runtime.v1.Canvas.spec:type_name -> rill.runtime.v1.CanvasSpec - 66, // 130: rill.runtime.v1.Canvas.state:type_name -> rill.runtime.v1.CanvasState - 63, // 131: rill.runtime.v1.CanvasSpec.variables:type_name -> rill.runtime.v1.ComponentVariable - 67, // 132: rill.runtime.v1.CanvasSpec.items:type_name -> rill.runtime.v1.CanvasItem - 20, // 133: rill.runtime.v1.CanvasSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule - 65, // 134: rill.runtime.v1.CanvasState.valid_spec:type_name -> rill.runtime.v1.CanvasSpec - 69, // 135: rill.runtime.v1.API.spec:type_name -> rill.runtime.v1.APISpec - 70, // 136: rill.runtime.v1.API.state:type_name -> rill.runtime.v1.APIState - 91, // 137: rill.runtime.v1.APISpec.resolver_properties:type_name -> google.protobuf.Struct - 91, // 138: rill.runtime.v1.APISpec.openapi_parameters:type_name -> google.protobuf.Struct - 91, // 139: rill.runtime.v1.APISpec.openapi_response_schema:type_name -> google.protobuf.Struct - 76, // 140: rill.runtime.v1.ParseError.start_location:type_name -> rill.runtime.v1.CharLocation - 88, // 141: rill.runtime.v1.ConnectorSpec.properties:type_name -> rill.runtime.v1.ConnectorSpec.PropertiesEntry - 89, // 142: rill.runtime.v1.ConnectorSpec.properties_from_variables:type_name -> rill.runtime.v1.ConnectorSpec.PropertiesFromVariablesEntry - 77, // 143: rill.runtime.v1.ConnectorV2.spec:type_name -> rill.runtime.v1.ConnectorSpec - 78, // 144: rill.runtime.v1.ConnectorV2.state:type_name -> rill.runtime.v1.ConnectorState - 93, // 145: rill.runtime.v1.MetricsViewSpec.DimensionSelector.time_grain:type_name -> rill.runtime.v1.TimeGrain - 81, // 146: rill.runtime.v1.MetricsViewSpec.MeasureWindow.order_by:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector - 3, // 147: rill.runtime.v1.MetricsViewSpec.MeasureV2.type:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureType - 82, // 148: rill.runtime.v1.MetricsViewSpec.MeasureV2.window:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureWindow - 81, // 149: rill.runtime.v1.MetricsViewSpec.MeasureV2.per_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector - 81, // 150: rill.runtime.v1.MetricsViewSpec.MeasureV2.required_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector - 84, // 151: rill.runtime.v1.MetricsViewSpec.AvailableTimeRange.comparison_offsets:type_name -> rill.runtime.v1.MetricsViewSpec.AvailableComparisonOffset - 152, // [152:152] is the sub-list for method output_type - 152, // [152:152] is the sub-list for method input_type - 152, // [152:152] is the sub-list for extension type_name - 152, // [152:152] is the sub-list for extension extendee - 0, // [0:152] is the sub-list for field type_name + 91, // 25: rill.runtime.v1.ResourceMeta.reconcile_on:type_name -> google.protobuf.Timestamp + 9, // 26: rill.runtime.v1.ResourceMeta.renamed_from:type_name -> rill.runtime.v1.ResourceName + 11, // 27: rill.runtime.v1.ProjectParser.spec:type_name -> rill.runtime.v1.ProjectParserSpec + 12, // 28: rill.runtime.v1.ProjectParser.state:type_name -> rill.runtime.v1.ProjectParserState + 73, // 29: rill.runtime.v1.ProjectParserState.parse_errors:type_name -> rill.runtime.v1.ParseError + 14, // 30: rill.runtime.v1.SourceV2.spec:type_name -> rill.runtime.v1.SourceSpec + 15, // 31: rill.runtime.v1.SourceV2.state:type_name -> rill.runtime.v1.SourceState + 92, // 32: rill.runtime.v1.SourceSpec.properties:type_name -> google.protobuf.Struct + 72, // 33: rill.runtime.v1.SourceSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule + 91, // 34: rill.runtime.v1.SourceState.refreshed_on:type_name -> google.protobuf.Timestamp + 17, // 35: rill.runtime.v1.ModelV2.spec:type_name -> rill.runtime.v1.ModelSpec + 18, // 36: rill.runtime.v1.ModelV2.state:type_name -> rill.runtime.v1.ModelState + 72, // 37: rill.runtime.v1.ModelSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule + 92, // 38: rill.runtime.v1.ModelSpec.incremental_state_resolver_properties:type_name -> google.protobuf.Struct + 92, // 39: rill.runtime.v1.ModelSpec.partitions_resolver_properties:type_name -> google.protobuf.Struct + 92, // 40: rill.runtime.v1.ModelSpec.input_properties:type_name -> google.protobuf.Struct + 92, // 41: rill.runtime.v1.ModelSpec.stage_properties:type_name -> google.protobuf.Struct + 92, // 42: rill.runtime.v1.ModelSpec.output_properties:type_name -> google.protobuf.Struct + 92, // 43: rill.runtime.v1.ModelState.result_properties:type_name -> google.protobuf.Struct + 91, // 44: rill.runtime.v1.ModelState.refreshed_on:type_name -> google.protobuf.Timestamp + 92, // 45: rill.runtime.v1.ModelState.incremental_state:type_name -> google.protobuf.Struct + 93, // 46: rill.runtime.v1.ModelState.incremental_state_schema:type_name -> rill.runtime.v1.StructType + 20, // 47: rill.runtime.v1.MetricsViewV2.spec:type_name -> rill.runtime.v1.MetricsViewSpec + 25, // 48: rill.runtime.v1.MetricsViewV2.state:type_name -> rill.runtime.v1.MetricsViewState + 94, // 49: rill.runtime.v1.MetricsViewSpec.smallest_time_grain:type_name -> rill.runtime.v1.TimeGrain + 81, // 50: rill.runtime.v1.MetricsViewSpec.dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionV2 + 84, // 51: rill.runtime.v1.MetricsViewSpec.measures:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureV2 + 21, // 52: rill.runtime.v1.MetricsViewSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule + 5, // 53: rill.runtime.v1.MetricsViewSpec.default_comparison_mode:type_name -> rill.runtime.v1.MetricsViewSpec.ComparisonMode + 86, // 54: rill.runtime.v1.MetricsViewSpec.available_time_ranges:type_name -> rill.runtime.v1.MetricsViewSpec.AvailableTimeRange + 22, // 55: rill.runtime.v1.SecurityRule.access:type_name -> rill.runtime.v1.SecurityRuleAccess + 23, // 56: rill.runtime.v1.SecurityRule.field_access:type_name -> rill.runtime.v1.SecurityRuleFieldAccess + 24, // 57: rill.runtime.v1.SecurityRule.row_filter:type_name -> rill.runtime.v1.SecurityRuleRowFilter + 95, // 58: rill.runtime.v1.SecurityRuleRowFilter.expression:type_name -> rill.runtime.v1.Expression + 20, // 59: rill.runtime.v1.MetricsViewState.valid_spec:type_name -> rill.runtime.v1.MetricsViewSpec + 27, // 60: rill.runtime.v1.Explore.spec:type_name -> rill.runtime.v1.ExploreSpec + 28, // 61: rill.runtime.v1.Explore.state:type_name -> rill.runtime.v1.ExploreState + 32, // 62: rill.runtime.v1.ExploreSpec.dimensions_selector:type_name -> rill.runtime.v1.FieldSelector + 32, // 63: rill.runtime.v1.ExploreSpec.measures_selector:type_name -> rill.runtime.v1.FieldSelector + 59, // 64: rill.runtime.v1.ExploreSpec.embedded_theme:type_name -> rill.runtime.v1.ThemeSpec + 29, // 65: rill.runtime.v1.ExploreSpec.time_ranges:type_name -> rill.runtime.v1.ExploreTimeRange + 31, // 66: rill.runtime.v1.ExploreSpec.default_preset:type_name -> rill.runtime.v1.ExplorePreset + 21, // 67: rill.runtime.v1.ExploreSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule + 27, // 68: rill.runtime.v1.ExploreState.valid_spec:type_name -> rill.runtime.v1.ExploreSpec + 30, // 69: rill.runtime.v1.ExploreTimeRange.comparison_time_ranges:type_name -> rill.runtime.v1.ExploreComparisonTimeRange + 32, // 70: rill.runtime.v1.ExplorePreset.dimensions_selector:type_name -> rill.runtime.v1.FieldSelector + 32, // 71: rill.runtime.v1.ExplorePreset.measures_selector:type_name -> rill.runtime.v1.FieldSelector + 95, // 72: rill.runtime.v1.ExplorePreset.where:type_name -> rill.runtime.v1.Expression + 1, // 73: rill.runtime.v1.ExplorePreset.comparison_mode:type_name -> rill.runtime.v1.ExploreComparisonMode + 2, // 74: rill.runtime.v1.ExplorePreset.view:type_name -> rill.runtime.v1.ExploreWebView + 33, // 75: rill.runtime.v1.FieldSelector.fields:type_name -> rill.runtime.v1.StringListValue + 35, // 76: rill.runtime.v1.Migration.spec:type_name -> rill.runtime.v1.MigrationSpec + 36, // 77: rill.runtime.v1.Migration.state:type_name -> rill.runtime.v1.MigrationState + 38, // 78: rill.runtime.v1.Report.spec:type_name -> rill.runtime.v1.ReportSpec + 39, // 79: rill.runtime.v1.Report.state:type_name -> rill.runtime.v1.ReportState + 72, // 80: rill.runtime.v1.ReportSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule + 96, // 81: rill.runtime.v1.ReportSpec.export_format:type_name -> rill.runtime.v1.ExportFormat + 43, // 82: rill.runtime.v1.ReportSpec.notifiers:type_name -> rill.runtime.v1.Notifier + 87, // 83: rill.runtime.v1.ReportSpec.annotations:type_name -> rill.runtime.v1.ReportSpec.AnnotationsEntry + 91, // 84: rill.runtime.v1.ReportState.next_run_on:type_name -> google.protobuf.Timestamp + 40, // 85: rill.runtime.v1.ReportState.current_execution:type_name -> rill.runtime.v1.ReportExecution + 40, // 86: rill.runtime.v1.ReportState.execution_history:type_name -> rill.runtime.v1.ReportExecution + 91, // 87: rill.runtime.v1.ReportExecution.report_time:type_name -> google.protobuf.Timestamp + 91, // 88: rill.runtime.v1.ReportExecution.started_on:type_name -> google.protobuf.Timestamp + 91, // 89: rill.runtime.v1.ReportExecution.finished_on:type_name -> google.protobuf.Timestamp + 42, // 90: rill.runtime.v1.Alert.spec:type_name -> rill.runtime.v1.AlertSpec + 44, // 91: rill.runtime.v1.Alert.state:type_name -> rill.runtime.v1.AlertState + 72, // 92: rill.runtime.v1.AlertSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule + 92, // 93: rill.runtime.v1.AlertSpec.resolver_properties:type_name -> google.protobuf.Struct + 92, // 94: rill.runtime.v1.AlertSpec.query_for_attributes:type_name -> google.protobuf.Struct + 43, // 95: rill.runtime.v1.AlertSpec.notifiers:type_name -> rill.runtime.v1.Notifier + 88, // 96: rill.runtime.v1.AlertSpec.annotations:type_name -> rill.runtime.v1.AlertSpec.AnnotationsEntry + 92, // 97: rill.runtime.v1.Notifier.properties:type_name -> google.protobuf.Struct + 91, // 98: rill.runtime.v1.AlertState.next_run_on:type_name -> google.protobuf.Timestamp + 45, // 99: rill.runtime.v1.AlertState.current_execution:type_name -> rill.runtime.v1.AlertExecution + 45, // 100: rill.runtime.v1.AlertState.execution_history:type_name -> rill.runtime.v1.AlertExecution + 46, // 101: rill.runtime.v1.AlertExecution.result:type_name -> rill.runtime.v1.AssertionResult + 91, // 102: rill.runtime.v1.AlertExecution.execution_time:type_name -> google.protobuf.Timestamp + 91, // 103: rill.runtime.v1.AlertExecution.started_on:type_name -> google.protobuf.Timestamp + 91, // 104: rill.runtime.v1.AlertExecution.finished_on:type_name -> google.protobuf.Timestamp + 91, // 105: rill.runtime.v1.AlertExecution.suppressed_since:type_name -> google.protobuf.Timestamp + 3, // 106: rill.runtime.v1.AssertionResult.status:type_name -> rill.runtime.v1.AssertionStatus + 92, // 107: rill.runtime.v1.AssertionResult.fail_row:type_name -> google.protobuf.Struct + 48, // 108: rill.runtime.v1.PullTrigger.spec:type_name -> rill.runtime.v1.PullTriggerSpec + 49, // 109: rill.runtime.v1.PullTrigger.state:type_name -> rill.runtime.v1.PullTriggerState + 51, // 110: rill.runtime.v1.RefreshTrigger.spec:type_name -> rill.runtime.v1.RefreshTriggerSpec + 52, // 111: rill.runtime.v1.RefreshTrigger.state:type_name -> rill.runtime.v1.RefreshTriggerState + 9, // 112: rill.runtime.v1.RefreshTriggerSpec.resources:type_name -> rill.runtime.v1.ResourceName + 53, // 113: rill.runtime.v1.RefreshTriggerSpec.models:type_name -> rill.runtime.v1.RefreshModelTrigger + 55, // 114: rill.runtime.v1.BucketPlanner.spec:type_name -> rill.runtime.v1.BucketPlannerSpec + 56, // 115: rill.runtime.v1.BucketPlanner.state:type_name -> rill.runtime.v1.BucketPlannerState + 57, // 116: rill.runtime.v1.BucketPlannerSpec.extract_policy:type_name -> rill.runtime.v1.BucketExtractPolicy + 6, // 117: rill.runtime.v1.BucketExtractPolicy.rows_strategy:type_name -> rill.runtime.v1.BucketExtractPolicy.Strategy + 6, // 118: rill.runtime.v1.BucketExtractPolicy.files_strategy:type_name -> rill.runtime.v1.BucketExtractPolicy.Strategy + 59, // 119: rill.runtime.v1.Theme.spec:type_name -> rill.runtime.v1.ThemeSpec + 60, // 120: rill.runtime.v1.Theme.state:type_name -> rill.runtime.v1.ThemeState + 97, // 121: rill.runtime.v1.ThemeSpec.primary_color:type_name -> rill.runtime.v1.Color + 97, // 122: rill.runtime.v1.ThemeSpec.secondary_color:type_name -> rill.runtime.v1.Color + 62, // 123: rill.runtime.v1.Component.spec:type_name -> rill.runtime.v1.ComponentSpec + 63, // 124: rill.runtime.v1.Component.state:type_name -> rill.runtime.v1.ComponentState + 92, // 125: rill.runtime.v1.ComponentSpec.resolver_properties:type_name -> google.protobuf.Struct + 92, // 126: rill.runtime.v1.ComponentSpec.renderer_properties:type_name -> google.protobuf.Struct + 64, // 127: rill.runtime.v1.ComponentSpec.input:type_name -> rill.runtime.v1.ComponentVariable + 64, // 128: rill.runtime.v1.ComponentSpec.output:type_name -> rill.runtime.v1.ComponentVariable + 62, // 129: rill.runtime.v1.ComponentState.valid_spec:type_name -> rill.runtime.v1.ComponentSpec + 98, // 130: rill.runtime.v1.ComponentVariable.default_value:type_name -> google.protobuf.Value + 66, // 131: rill.runtime.v1.Canvas.spec:type_name -> rill.runtime.v1.CanvasSpec + 67, // 132: rill.runtime.v1.Canvas.state:type_name -> rill.runtime.v1.CanvasState + 64, // 133: rill.runtime.v1.CanvasSpec.variables:type_name -> rill.runtime.v1.ComponentVariable + 68, // 134: rill.runtime.v1.CanvasSpec.items:type_name -> rill.runtime.v1.CanvasItem + 21, // 135: rill.runtime.v1.CanvasSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule + 66, // 136: rill.runtime.v1.CanvasState.valid_spec:type_name -> rill.runtime.v1.CanvasSpec + 70, // 137: rill.runtime.v1.API.spec:type_name -> rill.runtime.v1.APISpec + 71, // 138: rill.runtime.v1.API.state:type_name -> rill.runtime.v1.APIState + 92, // 139: rill.runtime.v1.APISpec.resolver_properties:type_name -> google.protobuf.Struct + 92, // 140: rill.runtime.v1.APISpec.openapi_parameters:type_name -> google.protobuf.Struct + 92, // 141: rill.runtime.v1.APISpec.openapi_response_schema:type_name -> google.protobuf.Struct + 77, // 142: rill.runtime.v1.ParseError.start_location:type_name -> rill.runtime.v1.CharLocation + 89, // 143: rill.runtime.v1.ConnectorSpec.properties:type_name -> rill.runtime.v1.ConnectorSpec.PropertiesEntry + 90, // 144: rill.runtime.v1.ConnectorSpec.properties_from_variables:type_name -> rill.runtime.v1.ConnectorSpec.PropertiesFromVariablesEntry + 78, // 145: rill.runtime.v1.ConnectorV2.spec:type_name -> rill.runtime.v1.ConnectorSpec + 79, // 146: rill.runtime.v1.ConnectorV2.state:type_name -> rill.runtime.v1.ConnectorState + 94, // 147: rill.runtime.v1.MetricsViewSpec.DimensionSelector.time_grain:type_name -> rill.runtime.v1.TimeGrain + 82, // 148: rill.runtime.v1.MetricsViewSpec.MeasureWindow.order_by:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector + 4, // 149: rill.runtime.v1.MetricsViewSpec.MeasureV2.type:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureType + 83, // 150: rill.runtime.v1.MetricsViewSpec.MeasureV2.window:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureWindow + 82, // 151: rill.runtime.v1.MetricsViewSpec.MeasureV2.per_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector + 82, // 152: rill.runtime.v1.MetricsViewSpec.MeasureV2.required_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector + 85, // 153: rill.runtime.v1.MetricsViewSpec.AvailableTimeRange.comparison_offsets:type_name -> rill.runtime.v1.MetricsViewSpec.AvailableComparisonOffset + 154, // [154:154] is the sub-list for method output_type + 154, // [154:154] is the sub-list for method input_type + 154, // [154:154] is the sub-list for extension type_name + 154, // [154:154] is the sub-list for extension extendee + 0, // [0:154] is the sub-list for field type_name } func init() { file_rill_runtime_v1_resources_proto_init() } @@ -9018,6 +9272,7 @@ func file_rill_runtime_v1_resources_proto_init() { (*SecurityRule_FieldAccess)(nil), (*SecurityRule_RowFilter)(nil), } + file_rill_runtime_v1_resources_proto_msgTypes[24].OneofWrappers = []any{} file_rill_runtime_v1_resources_proto_msgTypes[25].OneofWrappers = []any{ (*FieldSelector_All)(nil), (*FieldSelector_Fields)(nil), @@ -9036,7 +9291,7 @@ func file_rill_runtime_v1_resources_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_rill_runtime_v1_resources_proto_rawDesc, - NumEnums: 6, + NumEnums: 7, NumMessages: 84, NumExtensions: 0, NumServices: 0, diff --git a/proto/gen/rill/runtime/v1/resources.pb.validate.go b/proto/gen/rill/runtime/v1/resources.pb.validate.go index bee4c845468..942f65a5f90 100644 --- a/proto/gen/rill/runtime/v1/resources.pb.validate.go +++ b/proto/gen/rill/runtime/v1/resources.pb.validate.go @@ -4888,11 +4888,96 @@ func (m *ExplorePreset) validate(all bool) error { } } - // no validation rules for TimeRange - // no validation rules for ComparisonMode - // no validation rules for ComparisonDimension + if m.Where != nil { + + if all { + switch v := interface{}(m.GetWhere()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, ExplorePresetValidationError{ + field: "Where", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, ExplorePresetValidationError{ + field: "Where", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetWhere()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return ExplorePresetValidationError{ + field: "Where", + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + + if m.TimeRange != nil { + // no validation rules for TimeRange + } + + if m.Timezone != nil { + // no validation rules for Timezone + } + + if m.TimeGrain != nil { + // no validation rules for TimeGrain + } + + if m.CompareTimeRange != nil { + // no validation rules for CompareTimeRange + } + + if m.ComparisonDimension != nil { + // no validation rules for ComparisonDimension + } + + if m.View != nil { + // no validation rules for View + } + + if m.OverviewSortBy != nil { + // no validation rules for OverviewSortBy + } + + if m.OverviewSortAsc != nil { + // no validation rules for OverviewSortAsc + } + + if m.OverviewExpandedDimension != nil { + // no validation rules for OverviewExpandedDimension + } + + if m.TimeDimensionMeasure != nil { + // no validation rules for TimeDimensionMeasure + } + + if m.TimeDimensionChartType != nil { + // no validation rules for TimeDimensionChartType + } + + if m.TimeDimensionPin != nil { + // no validation rules for TimeDimensionPin + } + + if m.PivotSortBy != nil { + // no validation rules for PivotSortBy + } + + if m.PivotSortAsc != nil { + // no validation rules for PivotSortAsc + } if len(errors) > 0 { return ExplorePresetMultiError(errors) diff --git a/proto/gen/rill/runtime/v1/runtime.swagger.yaml b/proto/gen/rill/runtime/v1/runtime.swagger.yaml index 244219e170d..3af83dcd147 100644 --- a/proto/gen/rill/runtime/v1/runtime.swagger.yaml +++ b/proto/gen/rill/runtime/v1/runtime.swagger.yaml @@ -4090,18 +4090,52 @@ definitions: measuresSelector: $ref: '#/definitions/v1FieldSelector' description: Dynamic selector for `measures`. Will be processed during validation, so it will always be empty in `state.valid_spec`. + where: + $ref: '#/definitions/v1Expression' timeRange: type: string description: |- Time range for the explore. It corresponds to the `range` property of the explore's `time_ranges`. If not found in `time_ranges`, it should be added to the list. + timezone: + type: string + timeGrain: + type: string comparisonMode: $ref: '#/definitions/v1ExploreComparisonMode' description: Comparison mode. + compareTimeRange: + type: string comparisonDimension: type: string description: If comparison_mode is EXPLORE_COMPARISON_MODE_DIMENSION, this indicates the dimension to use. + view: + $ref: '#/definitions/v1ExploreWebView' + overviewSortBy: + type: string + overviewSortAsc: + type: boolean + overviewExpandedDimension: + type: string + timeDimensionMeasure: + type: string + timeDimensionChartType: + type: string + timeDimensionPin: + type: boolean + pivotRows: + type: array + items: + type: string + pivotCols: + type: array + items: + type: string + pivotSortBy: + type: string + pivotSortAsc: + type: boolean v1ExploreSpec: type: object properties: @@ -4182,6 +4216,15 @@ definitions: type: object $ref: '#/definitions/v1ExploreComparisonTimeRange' title: Comparison time ranges available for this time range + v1ExploreWebView: + type: string + enum: + - EXPLORE_ACTIVE_PAGE_UNSPECIFIED + - EXPLORE_ACTIVE_PAGE_OVERVIEW + - EXPLORE_ACTIVE_PAGE_TIME_DIMENSION + - EXPLORE_ACTIVE_PAGE_PIVOT + - EXPLORE_ACTIVE_PAGE_CANVAS + default: EXPLORE_ACTIVE_PAGE_UNSPECIFIED v1ExportFormat: type: string enum: diff --git a/proto/rill/runtime/v1/resources.proto b/proto/rill/runtime/v1/resources.proto index 69dab04db85..cff49623ecc 100644 --- a/proto/rill/runtime/v1/resources.proto +++ b/proto/rill/runtime/v1/resources.proto @@ -384,14 +384,36 @@ message ExplorePreset { repeated string measures = 4; // Dynamic selector for `measures`. Will be processed during validation, so it will always be empty in `state.valid_spec`. FieldSelector measures_selector = 10; + + optional Expression where = 25; + // Time range for the explore. // It corresponds to the `range` property of the explore's `time_ranges`. // If not found in `time_ranges`, it should be added to the list. - string time_range = 6; + optional string time_range = 6; + optional string timezone = 11; + optional string time_grain = 12; + // Comparison mode. ExploreComparisonMode comparison_mode = 7; + optional string compare_time_range = 13; // If comparison_mode is EXPLORE_COMPARISON_MODE_DIMENSION, this indicates the dimension to use. - string comparison_dimension = 8; + optional string comparison_dimension = 8; + + optional ExploreWebView view = 14; + + optional string overview_sort_by = 15; + optional bool overview_sort_asc = 16; + optional string overview_expanded_dimension = 17; + + optional string time_dimension_measure = 18; + optional string time_dimension_chart_type = 19; + optional bool time_dimension_pin = 20; + + repeated string pivot_rows = 21; + repeated string pivot_cols = 22; + optional string pivot_sort_by = 23; + optional bool pivot_sort_asc = 24; } enum ExploreComparisonMode { @@ -401,6 +423,14 @@ enum ExploreComparisonMode { EXPLORE_COMPARISON_MODE_DIMENSION = 3; } +enum ExploreWebView { + EXPLORE_ACTIVE_PAGE_UNSPECIFIED = 0; + EXPLORE_ACTIVE_PAGE_OVERVIEW = 1; + EXPLORE_ACTIVE_PAGE_TIME_DIMENSION = 2; + EXPLORE_ACTIVE_PAGE_PIVOT = 3; + EXPLORE_ACTIVE_PAGE_CANVAS = 4; +} + // FieldSelector describes logic for selecting a list of fields. // It is useful for dynamically evaluating fields when the list of potential fields is not known at parse time. message FieldSelector { diff --git a/runtime/compilers/rillv1/parse_explore.go b/runtime/compilers/rillv1/parse_explore.go index 38a10fc4222..99e42752fee 100644 --- a/runtime/compilers/rillv1/parse_explore.go +++ b/runtime/compilers/rillv1/parse_explore.go @@ -12,7 +12,7 @@ import ( ) type ExploreYAML struct { - commonYAML `yaml:",inline"` // Not accessed here, only setting it so we can use KnownFields for YAML parsing + commonYAML `yaml:",inline"` // Not accessed here, only setting it so we can use KnownFields for YAML parsing DisplayName string `yaml:"display_name"` Title string `yaml:"title"` // Deprecated: use display_name Description string `yaml:"description"` @@ -228,14 +228,22 @@ func (p *Parser) parseExplore(node *Node) error { presetMeasuresSelector = tmp.Defaults.Measures.Proto() } + var tr *string + if tmp.Defaults.TimeRange != "" { + tr = &tmp.Defaults.TimeRange + } + var compareDim *string + if tmp.Defaults.ComparisonDimension != "" { + compareDim = &tmp.Defaults.ComparisonDimension + } defaultPreset = &runtimev1.ExplorePreset{ Dimensions: presetDimensions, DimensionsSelector: presetDimensionsSelector, Measures: presetMeasures, MeasuresSelector: presetMeasuresSelector, - TimeRange: tmp.Defaults.TimeRange, + TimeRange: tr, ComparisonMode: mode, - ComparisonDimension: tmp.Defaults.ComparisonDimension, + ComparisonDimension: compareDim, } } diff --git a/runtime/compilers/rillv1/parse_metrics_view.go b/runtime/compilers/rillv1/parse_metrics_view.go index 15593deba8a..4822766ed93 100644 --- a/runtime/compilers/rillv1/parse_metrics_view.go +++ b/runtime/compilers/rillv1/parse_metrics_view.go @@ -4,30 +4,29 @@ import ( "fmt" "strings" "time" + // Load IANA time zone data + _ "time/tzdata" runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1" "github.com/rilldata/rill/runtime/pkg/duration" "gopkg.in/yaml.v3" - - // Load IANA time zone data - _ "time/tzdata" ) // MetricsViewYAML is the raw structure of a MetricsView resource defined in YAML type MetricsViewYAML struct { commonYAML `yaml:",inline"` // Not accessed here, only setting it so we can use KnownFields for YAML parsing - DisplayName string `yaml:"display_name"` - Title string `yaml:"title"` // Deprecated: use display_name - Description string `yaml:"description"` - Model string `yaml:"model"` - Database string `yaml:"database"` - DatabaseSchema string `yaml:"database_schema"` - Table string `yaml:"table"` - TimeDimension string `yaml:"timeseries"` - Watermark string `yaml:"watermark"` - SmallestTimeGrain string `yaml:"smallest_time_grain"` - FirstDayOfWeek uint32 `yaml:"first_day_of_week"` - FirstMonthOfYear uint32 `yaml:"first_month_of_year"` + DisplayName string `yaml:"display_name"` + Title string `yaml:"title"` // Deprecated: use display_name + Description string `yaml:"description"` + Model string `yaml:"model"` + Database string `yaml:"database"` + DatabaseSchema string `yaml:"database_schema"` + Table string `yaml:"table"` + TimeDimension string `yaml:"timeseries"` + Watermark string `yaml:"watermark"` + SmallestTimeGrain string `yaml:"smallest_time_grain"` + FirstDayOfWeek uint32 `yaml:"first_day_of_week"` + FirstMonthOfYear uint32 `yaml:"first_month_of_year"` Dimensions []*struct { Name string DisplayName string `yaml:"display_name"` @@ -884,14 +883,22 @@ func (p *Parser) parseMetricsView(node *Node) error { if len(spec.DefaultMeasures) == 0 { presetMeasuresSelector = &runtimev1.FieldSelector{Selector: &runtimev1.FieldSelector_All{All: true}} } + var tr *string + if spec.DefaultTimeRange != "" { + tr = &spec.DefaultTimeRange + } + var compareDim *string + if spec.DefaultComparisonDimension != "" { + compareDim = &spec.DefaultComparisonDimension + } e.ExploreSpec.DefaultPreset = &runtimev1.ExplorePreset{ Dimensions: spec.DefaultDimensions, DimensionsSelector: presetDimensionsSelector, Measures: spec.DefaultMeasures, MeasuresSelector: presetMeasuresSelector, - TimeRange: spec.DefaultTimeRange, + TimeRange: tr, ComparisonMode: exploreComparisonMode, - ComparisonDimension: spec.DefaultComparisonDimension, + ComparisonDimension: compareDim, } return nil diff --git a/web-admin/src/features/bookmarks/Bookmarks.svelte b/web-admin/src/features/bookmarks/Bookmarks.svelte index 6033533e159..1319aed869b 100644 --- a/web-admin/src/features/bookmarks/Bookmarks.svelte +++ b/web-admin/src/features/bookmarks/Bookmarks.svelte @@ -6,7 +6,6 @@ } from "@rilldata/web-admin/client"; import BookmarkDialog from "@rilldata/web-admin/features/bookmarks/BookmarkDialog.svelte"; import BookmarksContent from "@rilldata/web-admin/features/bookmarks/BookmarksDropdownMenuContent.svelte"; - import { createBookmarkApplier } from "@rilldata/web-admin/features/bookmarks/applyBookmark"; import { createHomeBookmarkModifier } from "@rilldata/web-admin/features/bookmarks/createOrUpdateHomeBookmark"; import { getBookmarkDataForDashboard } from "@rilldata/web-admin/features/bookmarks/getBookmarkDataForDashboard"; import type { BookmarkEntry } from "@rilldata/web-admin/features/bookmarks/selectors"; @@ -29,12 +28,6 @@ let showDialog = false; let bookmark: BookmarkEntry | null = null; - $: bookmarkApplier = createBookmarkApplier( - $runtime?.instanceId, - metricsViewName, - exploreName, - ); - $: exploreStore = useExploreStore(exploreName); $: projectId = useProjectId($page.params.organization, $page.params.project); @@ -46,10 +39,6 @@ ); const bookmarkDeleter = createAdminServiceRemoveBookmark(); - function selectBookmark(bookmark: BookmarkEntry) { - bookmarkApplier(bookmark.resource); - } - async function createHomeBookmark() { await homeBookmarkModifier(getBookmarkDataForDashboard($exploreStore)); eventBus.emit("notification", { @@ -102,7 +91,6 @@ showDialog = true; bookmark = detail; }} - on:select={({ detail }) => selectBookmark(detail)} {metricsViewName} {exploreName} /> diff --git a/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte b/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte index ffad6412388..39717eff9b4 100644 --- a/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte +++ b/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte @@ -11,9 +11,14 @@ import BookmarkItem from "@rilldata/web-admin/features/bookmarks/BookmarksDropdownMenuItem.svelte"; import { getBookmarks, + getFilledInBookmarks, searchBookmarks, } from "@rilldata/web-admin/features/bookmarks/selectors"; import { Search } from "@rilldata/web-common/components/search"; + import { useExploreStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; + import { getBasePreset } from "@rilldata/web-common/features/dashboards/url-state/getBasePreset"; + import { getLocalUserPreferencesState } from "@rilldata/web-common/features/dashboards/user-preferences"; + import { useExploreValidSpec } from "@rilldata/web-common/features/explores/selectors"; import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import { useQueryClient } from "@tanstack/svelte-query"; import { BookmarkPlusIcon } from "lucide-svelte"; @@ -28,6 +33,13 @@ $: organization = $page.params.organization; $: project = $page.params.project; + $: dashboard = useExploreStore(exploreName); + $: validExploreSpec = useExploreValidSpec($runtime.instanceId, exploreName); + $: exploreSpec = $validExploreSpec.data?.explore ?? {}; + $: basePreset = getBasePreset( + exploreSpec, + getLocalUserPreferencesState(exploreName), + ); let searchText: string; let bookmarks: ReturnType; @@ -39,7 +51,14 @@ metricsViewName, exploreName, ); - $: filteredBookmarks = searchBookmarks($bookmarks.data, searchText); + $: filledInBookmarks = getFilledInBookmarks( + $bookmarks.data, + `/${organization}/${project}/explore/${exploreName}`, + $dashboard, + exploreSpec, + basePreset, + ); + $: filteredBookmarks = searchBookmarks(filledInBookmarks, searchText); $: projectPermissions = getProjectPermissions(organization, project); $: manageProject = $projectPermissions.data?.manageProject; @@ -108,7 +127,6 @@ @@ -119,7 +137,6 @@ diff --git a/web-admin/src/features/bookmarks/BookmarksDropdownMenuItem.svelte b/web-admin/src/features/bookmarks/BookmarksDropdownMenuItem.svelte index 6272118871c..cdbfee95d26 100644 --- a/web-admin/src/features/bookmarks/BookmarksDropdownMenuItem.svelte +++ b/web-admin/src/features/bookmarks/BookmarksDropdownMenuItem.svelte @@ -13,11 +13,6 @@ const dispatch = createEventDispatcher(); - function selectBookmark(e) { - if (e.skipSelection) return; - dispatch("select", bookmark); - } - function editBookmark(e) { e.skipSelection = true; dispatch("edit", bookmark); @@ -34,14 +29,12 @@
e.key === "Enter" && e.currentTarget.click()} on:mouseenter={() => (hovered = true)} on:mouseleave={() => (hovered = false)} role="menuitem" tabindex="-1" > - {/if}
- + {#if !readOnly}
{#if hovered} diff --git a/web-admin/src/features/bookmarks/applyBookmark.ts b/web-admin/src/features/bookmarks/applyBookmark.ts deleted file mode 100644 index 9863897c2e8..00000000000 --- a/web-admin/src/features/bookmarks/applyBookmark.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { V1Bookmark } from "@rilldata/web-admin/client"; -import { metricsExplorerStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; -import { useExploreValidSpec } from "@rilldata/web-common/features/explores/selectors"; -import { createQueryServiceMetricsViewSchema } from "@rilldata/web-common/runtime-client"; -import { get } from "svelte/store"; - -export function createBookmarkApplier( - instanceId: string, - metricsViewName: string, - exploreName: string, -) { - const validExploreSpec = useExploreValidSpec(instanceId, exploreName); - const metricsSchema = createQueryServiceMetricsViewSchema( - instanceId, - metricsViewName, - ); - - return (bookmark: V1Bookmark) => { - const validExploreSpecResp = get(validExploreSpec); - const metricsSchemaResp = get(metricsSchema); - if ( - !bookmark.data || - !validExploreSpecResp.data?.metricsView || - !validExploreSpecResp.data?.explore || - !metricsSchemaResp.data?.schema - ) { - return; - } - metricsExplorerStore.syncFromUrl( - exploreName, - decodeURIComponent(bookmark.data), - validExploreSpecResp.data.metricsView, - validExploreSpecResp.data.explore, - metricsSchemaResp.data.schema, - ); - }; -} diff --git a/web-admin/src/features/bookmarks/selectors.ts b/web-admin/src/features/bookmarks/selectors.ts index 731bb7f65b9..86f5c1f7013 100644 --- a/web-admin/src/features/bookmarks/selectors.ts +++ b/web-admin/src/features/bookmarks/selectors.ts @@ -1,6 +1,8 @@ import { + adminServiceListBookmarks, createAdminServiceGetCurrentUser, createAdminServiceListBookmarks, + getAdminServiceListBookmarksQueryKey, type V1Bookmark, } from "@rilldata/web-admin/client"; import { useProjectId } from "@rilldata/web-admin/features/projects/selectors"; @@ -8,13 +10,17 @@ import type { CompoundQueryResult } from "@rilldata/web-common/features/compound import { getDashboardStateFromUrl } from "@rilldata/web-common/features/dashboards/proto-state/fromProto"; import { useMetricsViewTimeRange } from "@rilldata/web-common/features/dashboards/selectors"; import { useExploreStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { timeControlStateSelector } from "@rilldata/web-common/features/dashboards/time-controls/time-control-store"; +import { convertMetricsEntityToURLSearchParams } from "@rilldata/web-common/features/dashboards/url-state/convertMetricsEntityToURLSearchParams"; import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors"; import { useExploreValidSpec } from "@rilldata/web-common/features/explores/selectors"; +import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; import { prettyFormatTimeRange } from "@rilldata/web-common/lib/time/ranges"; import { TimeRangePreset } from "@rilldata/web-common/lib/time/types"; import { createQueryServiceMetricsViewSchema, + type V1ExplorePreset, type V1ExploreSpec, type V1MetricsViewSpec, type V1StructType, @@ -25,8 +31,10 @@ import { derived, type Readable } from "svelte/store"; export type BookmarkEntry = { resource: V1Bookmark; + metricsEntity: Partial; filtersOnly: boolean; absoluteTimeRange: boolean; + url: string; }; export type Bookmarks = { @@ -66,29 +74,13 @@ export function getBookmarks( !schemaResp.isFetching && userResp.isSuccess && !!userResp.data.user, - select: (resp) => { - const bookmarks: Bookmarks = { - home: undefined, - personal: [], - shared: [], - }; - resp.bookmarks?.forEach((bookmarkResource) => { - const bookmark = parseBookmarkEntry( - bookmarkResource, - validSpec.data?.metricsView ?? {}, - validSpec.data?.explore ?? {}, - schemaResp.data?.schema ?? {}, - ); - if (bookmarkResource.default) { - bookmarks.home = bookmark; - } else if (bookmarkResource.shared) { - bookmarks.shared.push(bookmark); - } else { - bookmarks.personal.push(bookmark); - } - }); - return bookmarks; - }, + select: (resp) => + categorizeBookmarks( + resp.bookmarks ?? [], + validSpec.data?.metricsView, + validSpec.data?.explore, + schemaResp.data?.schema, + ), queryClient, }, }, @@ -96,10 +88,62 @@ export function getBookmarks( ); } +export async function fetchBookmarks( + projectId: string, + exploreName: string, + metricsSpec: V1MetricsViewSpec | undefined, + exploreSpec: V1ExploreSpec | undefined, +) { + const params = { + projectId, + resourceKind: ResourceKind.Explore, + resourceName: exploreName, + }; + const bookmarksResp = await queryClient.fetchQuery({ + queryKey: getAdminServiceListBookmarksQueryKey(params), + queryFn: ({ signal }) => adminServiceListBookmarks(params, signal), + }); + return categorizeBookmarks( + bookmarksResp.bookmarks ?? [], + metricsSpec, + exploreSpec, + undefined, // TODO + ); +} + +function categorizeBookmarks( + bookmarkResp: V1Bookmark[], + metricsSpec: V1MetricsViewSpec | undefined, + exploreSpec: V1ExploreSpec | undefined, + schema: V1StructType | undefined, +) { + const bookmarks: Bookmarks = { + home: undefined, + personal: [], + shared: [], + }; + bookmarkResp?.forEach((bookmarkResource) => { + const bookmark = parseBookmarkEntry( + bookmarkResource, + metricsSpec ?? {}, + exploreSpec ?? {}, + schema ?? {}, + ); + if (bookmarkResource.default) { + bookmarks.home = bookmark; + } else if (bookmarkResource.shared) { + bookmarks.shared.push(bookmark); + } else { + bookmarks.personal.push(bookmark); + } + }); + return bookmarks; +} + export function searchBookmarks( - bookmarks: Bookmarks, + bookmarks: Bookmarks | undefined, searchText: string, -): Bookmarks { +): Bookmarks | undefined { if (!searchText || !bookmarks) return bookmarks; searchText = searchText.toLowerCase(); const matchName = (bookmark: BookmarkEntry | undefined) => @@ -182,6 +226,39 @@ export function getPrettySelectedTimeRange( ); } +export function getFilledInBookmarks( + bookmarks: Bookmarks | undefined, + baseUrl: string, + dashboard: MetricsExplorerEntity, + exploreSpec: V1ExploreSpec, + basePreset: V1ExplorePreset, +): Bookmarks | undefined { + if (!bookmarks) return undefined; + + if (!baseUrl.startsWith("http")) { + // handle case where only path is provided + baseUrl = "http://localhost" + baseUrl; + } + + return { + home: bookmarks.home + ? getFilledInBookmark( + bookmarks.home, + baseUrl, + dashboard, + exploreSpec, + basePreset, + ) + : undefined, + personal: bookmarks.personal.map((b) => + getFilledInBookmark(b, baseUrl, dashboard, exploreSpec, basePreset), + ), + shared: bookmarks.shared.map((b) => + getFilledInBookmark(b, baseUrl, dashboard, exploreSpec, basePreset), + ), + }; +} + function parseBookmarkEntry( bookmarkResource: V1Bookmark, metricsViewSpec: V1MetricsViewSpec, @@ -196,8 +273,30 @@ function parseBookmarkEntry( ); return { resource: bookmarkResource, + metricsEntity, absoluteTimeRange: metricsEntity.selectedTimeRange?.name === TimeRangePreset.CUSTOM, filtersOnly: !metricsEntity.pivot, + url: "", // will be filled in along with existing dashboard + }; +} + +function getFilledInBookmark( + bookmark: BookmarkEntry, + baseUrl: string, + dashboard: MetricsExplorerEntity, + exploreSpec: V1ExploreSpec, + basePreset: V1ExplorePreset, +) { + const url = new URL(baseUrl); + convertMetricsEntityToURLSearchParams( + { ...dashboard, ...bookmark.metricsEntity }, + url.searchParams, + exploreSpec, + basePreset, + ); + return { + ...bookmark, + url: `${url.pathname}${url.search}`, }; } diff --git a/web-admin/src/features/dashboards/query-mappers/getDashboardFromAggregationRequest.ts b/web-admin/src/features/dashboards/query-mappers/getDashboardFromAggregationRequest.ts index c46174a728b..10b6b588c1f 100644 --- a/web-admin/src/features/dashboards/query-mappers/getDashboardFromAggregationRequest.ts +++ b/web-admin/src/features/dashboards/query-mappers/getDashboardFromAggregationRequest.ts @@ -18,7 +18,9 @@ import { import { getDashboardStateFromUrl } from "@rilldata/web-common/features/dashboards/proto-state/fromProto"; import { createAndExpression, + createSubQueryExpression, forEachIdentifier, + getAllIdentifiers, } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; @@ -79,24 +81,18 @@ export async function getDashboardFromAggregationRequest({ exprHasComparison(req.having) || dashboard.dimensionThresholdFilters.length > 0 ) { - const expr = await convertExprToToplist( - queryClient, - instanceId, - dashboard.name, + const extraFilter = createSubQueryExpression( dimension, - req.measures?.[0]?.name ?? "", - req.timeRange, - req.comparisonTimeRange, - executionTime, - req.where, + getAllIdentifiers(req.having), req.having, ); - if (expr) { - dashboard.whereFilter = - mergeFilters( - dashboard.whereFilter ?? createAndExpression([]), - createAndExpression([expr]), - ) ?? createAndExpression([]); + if (dashboard.whereFilter?.cond?.exprs?.length) { + dashboard.whereFilter = createAndExpression([ + dashboard.whereFilter, + extraFilter, + ]); + } else { + dashboard.whereFilter = extraFilter; } } else { dashboard.dimensionThresholdFilters = [ diff --git a/web-admin/src/features/dashboards/query-mappers/mapQueryToDashboard.ts b/web-admin/src/features/dashboards/query-mappers/mapQueryToDashboard.ts index dc743b9f6e5..9e323210718 100644 --- a/web-admin/src/features/dashboards/query-mappers/mapQueryToDashboard.ts +++ b/web-admin/src/features/dashboards/query-mappers/mapQueryToDashboard.ts @@ -5,7 +5,6 @@ import type { QueryRequests, } from "@rilldata/web-admin/features/dashboards/query-mappers/types"; import type { CompoundQueryResult } from "@rilldata/web-common/features/compound-query-result"; -import { getProtoFromDashboardState } from "@rilldata/web-common/features/dashboards/proto-state/toProto"; import { getDefaultMetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { initLocalUserPreferenceStore } from "@rilldata/web-common/features/dashboards/user-preferences"; @@ -20,7 +19,7 @@ import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import { derived, get, readable } from "svelte/store"; type DashboardStateForQuery = { - state?: string; + dashboard?: MetricsExplorerEntity; exploreName?: string; }; @@ -140,7 +139,7 @@ export function mapQueryToDashboard( isFetching: false, error: "", data: { - state: getProtoFromDashboardState(newDashboard), + dashboard: newDashboard, exploreName, }, }); diff --git a/web-admin/src/features/dashboards/query-mappers/utils.ts b/web-admin/src/features/dashboards/query-mappers/utils.ts index 861f607abac..737d60b9d43 100644 --- a/web-admin/src/features/dashboards/query-mappers/utils.ts +++ b/web-admin/src/features/dashboards/query-mappers/utils.ts @@ -9,6 +9,8 @@ import { } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { PreviousCompleteRangeMap } from "@rilldata/web-common/features/dashboards/time-controls/time-range-mappers"; +import { convertMetricsEntityToURLSearchParams } from "@rilldata/web-common/features/dashboards/url-state/convertMetricsEntityToURLSearchParams"; +import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; import { isoDurationToFullTimeRange } from "@rilldata/web-common/lib/time/ranges/iso-ranges"; import { type DashboardTimeControls, @@ -17,13 +19,17 @@ import { } from "@rilldata/web-common/lib/time/types"; import { getQueryServiceMetricsViewAggregationQueryKey, + getRuntimeServiceGetExploreQueryKey, queryServiceMetricsViewAggregation, type QueryServiceMetricsViewAggregationBody, + runtimeServiceGetExplore, type V1Expression, type V1TimeRange, type V1TimeRangeSummary, } from "@rilldata/web-common/runtime-client"; +import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import type { QueryClient } from "@tanstack/svelte-query"; +import { get } from "svelte/store"; // We are manually sending in duration, offset and round to grain for previous complete ranges. // This is to map back that split @@ -209,15 +215,40 @@ export function getExploreName(webOpenPath: string) { return matches[1]; } -export function getExplorePageUrl( +export async function getExplorePageUrl( curPageUrl: URL, organization: string, project: string, exploreName: string, - state: string, + dashboard: MetricsExplorerEntity, ) { + const instanceId = get(runtime).instanceId; + const { explore } = await queryClient.fetchQuery({ + queryFn: ({ signal }) => + runtimeServiceGetExplore( + instanceId, + { + name: exploreName, + }, + signal, + ), + queryKey: getRuntimeServiceGetExploreQueryKey(instanceId, { + name: exploreName, + }), + // this loader function is run for every param change in url. + // so to avoid re-fetching explore everytime we set this so that it hits cache. + staleTime: Infinity, + }); + const url = new URL(`${curPageUrl.protocol}//${curPageUrl.host}`); url.pathname = `/${organization}/${project}/explore/${exploreName}`; - url.searchParams.set("state", state); + + const exploreSpec = explore?.explore?.state?.validSpec; + convertMetricsEntityToURLSearchParams( + dashboard, + url.searchParams, + exploreSpec ?? {}, + exploreSpec?.defaultPreset ?? {}, + ); return url.toString(); } diff --git a/web-admin/src/features/dashboards/selectors.ts b/web-admin/src/features/dashboards/selectors.ts new file mode 100644 index 00000000000..425a949cbc1 --- /dev/null +++ b/web-admin/src/features/dashboards/selectors.ts @@ -0,0 +1,49 @@ +import type { QueryFunction } from "@rilldata/svelte-query"; +import { getBasePreset } from "@rilldata/web-common/features/dashboards/url-state/getBasePreset"; +import { getLocalUserPreferencesState } from "@rilldata/web-common/features/dashboards/user-preferences"; +import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; +import { + getRuntimeServiceGetExploreQueryKey, + runtimeServiceGetExplore, +} from "@rilldata/web-common/runtime-client"; +import { error } from "@sveltejs/kit"; + +export async function fetchExploreSpec( + instanceId: string, + exploreName: string, +) { + const queryParams = { + name: exploreName, + }; + const queryKey = getRuntimeServiceGetExploreQueryKey(instanceId, queryParams); + const queryFunction: QueryFunction< + Awaited> + > = ({ signal }) => runtimeServiceGetExplore(instanceId, queryParams, signal); + + const response = await queryClient.fetchQuery({ + queryFn: queryFunction, + queryKey, + staleTime: Infinity, + }); + + const exploreResource = response.explore; + const metricsViewResource = response.metricsView; + + if (!exploreResource?.explore) { + throw error(404, "Explore not found"); + } + if (!metricsViewResource?.metricsView) { + throw error(404, "Metrics view not found"); + } + + const basePreset = getBasePreset( + exploreResource.explore.state?.validSpec ?? {}, + getLocalUserPreferencesState(exploreName), + ); + + return { + explore: exploreResource, + metricsView: metricsViewResource, + basePreset, + }; +} diff --git a/web-admin/src/features/embeds/ExploreEmbed.svelte b/web-admin/src/features/embeds/ExploreEmbed.svelte index cb218482f98..3e5db6dcde5 100644 --- a/web-admin/src/features/embeds/ExploreEmbed.svelte +++ b/web-admin/src/features/embeds/ExploreEmbed.svelte @@ -1,9 +1,8 @@ diff --git a/web-admin/src/routes/[organization]/[project]/-/reports/[report]/open/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/reports/[report]/open/+page.svelte index 25cb4aa905a..96c5809743c 100644 --- a/web-admin/src/routes/[organization]/[project]/-/reports/[report]/open/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/-/reports/[report]/open/+page.svelte @@ -34,18 +34,22 @@ $report?.data?.resource?.report?.spec?.annotations ?? {}, ); - $: if ($dashboardStateForReport?.data) { - void goto( - getExplorePageUrl( + async function gotExplorePage() { + return goto( + await getExplorePageUrl( $page.url, organization, project, $dashboardStateForReport.data.exploreName, - $dashboardStateForReport.data.state, + $dashboardStateForReport.data.dashboard, ), ); } + $: if ($dashboardStateForReport?.data) { + void gotExplorePage(); + } + // TODO: error handling diff --git a/web-admin/src/routes/[organization]/[project]/-/share/[token]/+layout.ts b/web-admin/src/routes/[organization]/[project]/-/share/[token]/+layout.ts new file mode 100644 index 00000000000..15d2d119ff3 --- /dev/null +++ b/web-admin/src/routes/[organization]/[project]/-/share/[token]/+layout.ts @@ -0,0 +1,26 @@ +import { fetchExploreSpec } from "@rilldata/web-admin/features/dashboards/selectors"; +import { fetchMagicAuthToken } from "@rilldata/web-admin/features/projects/selectors"; +import { error } from "@sveltejs/kit"; + +export const load = async ({ params: { token }, parent }) => { + const { runtime } = await parent(); + + try { + const tokenData = await fetchMagicAuthToken(token); + + const { explore, metricsView, basePreset } = await fetchExploreSpec( + runtime?.instanceId, + tokenData.token?.resourceName, + ); + + return { + explore, + metricsView, + basePreset, + token: tokenData?.token, + }; + } catch (e) { + console.error(e); + throw error(404, "Unable to find token"); + } +}; diff --git a/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.svelte index 66c0368acc0..ba8e8706590 100644 --- a/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.svelte @@ -4,9 +4,8 @@ import { createAdminServiceGetProject } from "@rilldata/web-admin/client"; import { Dashboard } from "@rilldata/web-common/features/dashboards"; import DashboardThemeProvider from "@rilldata/web-common/features/dashboards/DashboardThemeProvider.svelte"; - import DashboardURLStateProvider from "@rilldata/web-common/features/dashboards/proto-state/DashboardURLStateProvider.svelte"; + import DashboardURLStateSync from "@rilldata/web-common/features/dashboards/url-state/DashboardURLStateSync.svelte"; import StateManagersProvider from "@rilldata/web-common/features/dashboards/state-managers/StateManagersProvider.svelte"; - import DashboardStateProvider from "@rilldata/web-common/features/dashboards/stores/DashboardStateProvider.svelte"; import { eventBus } from "@rilldata/web-common/lib/event-bus/event-bus"; import { createRuntimeServiceGetExplore } from "@rilldata/web-common/runtime-client"; import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; @@ -14,7 +13,11 @@ export let data: PageData; - $: ({ resourceName } = data.token); + $: ({ + basePreset, + partialMetrics, + token: { resourceName }, + } = data); $: ({ organization, project } = $page.params); // Query the `GetProject` API with cookie-based auth to determine if the user has access to the original dashboard @@ -52,18 +55,14 @@ metricsViewName={explore.metricsView.meta.name.name} exploreName={resourceName} > - - - - - - - + + + + + {/if} {/key} diff --git a/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.ts b/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.ts index 6b061042194..c9e98e9a674 100644 --- a/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.ts +++ b/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.ts @@ -1,35 +1,56 @@ -import { - adminServiceGetMagicAuthToken, - getAdminServiceGetMagicAuthTokenQueryKey, -} from "@rilldata/web-admin/features/public-urls/get-magic-auth-token"; -import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; -import { error } from "@sveltejs/kit"; -import { type QueryFunction } from "@tanstack/svelte-query"; +import { getDashboardStateFromUrl } from "@rilldata/web-common/features/dashboards/proto-state/fromProto"; +import { metricsExplorerStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { convertMetricsEntityToURLSearchParams } from "@rilldata/web-common/features/dashboards/url-state/convertMetricsEntityToURLSearchParams"; +import { convertURLToMetricsExplore } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToMetricsExplore"; +import { redirect } from "@sveltejs/kit"; +import { get } from "svelte/store"; -export const load = async ({ params: { token }, url: { searchParams } }) => { - const queryKey = getAdminServiceGetMagicAuthTokenQueryKey(token); - const queryFunction: QueryFunction< - Awaited> - > = ({ signal }) => adminServiceGetMagicAuthToken(token, signal); +export const load = async ({ url, parent }) => { + const { explore, metricsView, basePreset, token } = await parent(); + const exploreName = token.resourceName; + const metricsViewSpec = metricsView.metricsView?.state?.validSpec; + const exploreSpec = explore.explore?.state?.validSpec; - try { - const tokenData = await queryClient.fetchQuery({ - queryKey, - queryFn: queryFunction, - }); - - const state = searchParams.get("state"); - - // Add the token's `state` to the URL (only if there's no existing URL `state`) - if (tokenData?.token?.state && !state) { - searchParams.set("state", tokenData.token.state); - } + let partialMetrics: Partial = {}; + const errors: Error[] = []; + if (metricsViewSpec && exploreSpec) { + const { entity, errors: errorsFromConvert } = convertURLToMetricsExplore( + url.searchParams, + metricsViewSpec, + exploreSpec, + basePreset, + ); + partialMetrics = entity; + errors.push(...errorsFromConvert); + } - return { - token: tokenData?.token, - }; - } catch (e) { - console.error(e); - throw error(404, "Unable to find token"); + if ( + !(exploreName in get(metricsExplorerStore).entities) && + token.state && + ![...url.searchParams.keys()].length + ) { + // Initial load of the dashboard. + // Merge home token state to url if present and there are no params in the url + // convert legacy state to new readable url format + const metricsEntity = getDashboardStateFromUrl( + token.state, + metricsViewSpec, + exploreSpec, + {}, // TODO + ); + const newUrl = new URL(url); + convertMetricsEntityToURLSearchParams( + metricsEntity, + newUrl.searchParams, + exploreSpec, + basePreset, + ); + throw redirect(307, `${newUrl.pathname}${newUrl.search}`); } + + return { + partialMetrics, + errors, + }; }; diff --git a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+layout.ts b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+layout.ts new file mode 100644 index 00000000000..7c69112e9a0 --- /dev/null +++ b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+layout.ts @@ -0,0 +1,48 @@ +import { + type Bookmarks, + fetchBookmarks, +} from "@rilldata/web-admin/features/bookmarks/selectors"; +import { fetchExploreSpec } from "@rilldata/web-admin/features/dashboards/selectors"; +import { + type V1ExplorePreset, + type V1Resource, +} from "@rilldata/web-common/runtime-client"; + +export const load = async ({ params, depends, parent }) => { + const { project, runtime } = await parent(); + + const exploreName = params.dashboard; + + depends(exploreName, "explore"); + + try { + const { explore, metricsView, basePreset } = await fetchExploreSpec( + runtime?.instanceId, + exploreName, + ); + + // used to merge home bookmark to url state + const bookmarks = await fetchBookmarks( + project.id, + exploreName, + metricsView.metricsView?.state?.validSpec, + explore.explore?.state?.validSpec, + ); + + return { + explore, + metricsView, + basePreset, + bookmarks, + }; + } catch { + // error handled in +page.svelte for now + // TODO: move it here + return { + explore: {}, + metricsView: {}, + basePreset: {}, + bookmarks: {}, + }; + } +}; diff --git a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte index 45ed40a17cc..8869eb26ae8 100644 --- a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte @@ -1,26 +1,37 @@ + +
+ + {#if error} + + + + + + {error} + + + {/if} +
diff --git a/web-common/src/features/dashboards/filters/measure-filters/measure-filter-utils.ts b/web-common/src/features/dashboards/filters/measure-filters/measure-filter-utils.ts index 25c16b48de2..3753d55d232 100644 --- a/web-common/src/features/dashboards/filters/measure-filters/measure-filter-utils.ts +++ b/web-common/src/features/dashboards/filters/measure-filters/measure-filter-utils.ts @@ -7,6 +7,8 @@ import { createAndExpression, createSubQueryExpression, filterExpressions, + isExpressionUnsupported, + isJoinerExpression, } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; import type { DimensionThresholdFilter, @@ -42,17 +44,24 @@ export function mergeDimensionAndMeasureFilter( * Measure filters will be sub-queries */ export function splitWhereFilter(whereFilter: V1Expression | undefined) { + if (whereFilter && isExpressionUnsupported(whereFilter)) { + return { dimensionFilters: whereFilter, dimensionThresholdFilters: [] }; + } + const dimensionFilters = createAndExpression([]); const dimensionThresholdFilters: DimensionThresholdFilter[] = []; whereFilter?.cond?.exprs?.filter((e) => { const subqueryExpr = e.cond?.exprs?.[1]; if (subqueryExpr?.subquery) { + const filters = isJoinerExpression(subqueryExpr.subquery.having) + ? (subqueryExpr.subquery.having?.cond?.exprs + ?.map(mapExprToMeasureFilter) + .filter(Boolean) as MeasureFilterEntry[]) + : [mapExprToMeasureFilter(subqueryExpr.subquery.having)]; + dimensionThresholdFilters.push({ name: subqueryExpr.subquery.dimension ?? "", - filters: - (subqueryExpr.subquery.having?.cond?.exprs - ?.map(mapExprToMeasureFilter) - .filter(Boolean) as MeasureFilterEntry[]) ?? [], + filters: filters.filter(Boolean) as MeasureFilterEntry[], }); return; } diff --git a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte index 009c1164c59..f97250b3dda 100644 --- a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte +++ b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte @@ -27,6 +27,7 @@ import { createAndExpression, createOrExpression, + isExpressionUnsupported, sanitiseExpression, } from "../stores/filter-utils"; import { mergeDimensionAndMeasureFilter } from "../filters/measure-filters/measure-filter-utils"; @@ -100,13 +101,17 @@ uri, } = dimension); - $: where = sanitiseExpression( - mergeDimensionAndMeasureFilter( - getFiltersForOtherDimensions(whereFilter, dimensionName), - dimensionThresholdFilters, - ), - undefined, - ); + $: isComplexFilter = isExpressionUnsupported(whereFilter); + $: where = isComplexFilter + ? whereFilter + : sanitiseExpression( + mergeDimensionAndMeasureFilter( + getFiltersForOtherDimensions(whereFilter, dimensionName), + dimensionThresholdFilters, + ), + undefined, + ); + $: console.log(isComplexFilter, where); $: measures = getIndependentMeasures( metricsView, diff --git a/web-common/src/features/dashboards/proto-state/fromProto.ts b/web-common/src/features/dashboards/proto-state/fromProto.ts index f49a164ea93..c28b16ef54f 100644 --- a/web-common/src/features/dashboards/proto-state/fromProto.ts +++ b/web-common/src/features/dashboards/proto-state/fromProto.ts @@ -134,7 +134,9 @@ export function getDashboardStateFromProto( dashboard.compareTimeRange, ); // backwards compatibility - correctComparisonTimeRange(entity.selectedComparisonTimeRange); + entity.selectedComparisonTimeRange.name = correctComparisonTimeRange( + entity.selectedComparisonTimeRange.name, + ); } if (dashboard.showTimeComparison !== undefined) { entity.showTimeComparison = Boolean(dashboard.showTimeComparison); @@ -223,7 +225,9 @@ export function base64ToProto(message: string) { return protoBase64.dec(message); } -function fromExpressionProto(expression: Expression): V1Expression | undefined { +export function fromExpressionProto( + expression: Expression, +): V1Expression | undefined { switch (expression.expression.case) { case "ident": return { @@ -422,29 +426,22 @@ function fromPivotProto( }; } -function correctComparisonTimeRange( - comparisonTimeRange: DashboardTimeControls, -) { - switch (comparisonTimeRange.name as string) { +export function correctComparisonTimeRange(name: string) { + switch (name) { case "CONTIGUOUS": - comparisonTimeRange.name = TimeComparisonOption.CONTIGUOUS; - break; + return TimeComparisonOption.CONTIGUOUS; case "P1D": - comparisonTimeRange.name = TimeComparisonOption.DAY; - break; + return TimeComparisonOption.DAY; case "P1W": - comparisonTimeRange.name = TimeComparisonOption.WEEK; - break; + return TimeComparisonOption.WEEK; case "P1M": - comparisonTimeRange.name = TimeComparisonOption.MONTH; - break; + return TimeComparisonOption.MONTH; case "P3M": - comparisonTimeRange.name = TimeComparisonOption.QUARTER; - break; + return TimeComparisonOption.QUARTER; case "P1Y": - comparisonTimeRange.name = TimeComparisonOption.YEAR; - break; + return TimeComparisonOption.YEAR; } + return name; } function chartTypeMap(chartType: string | undefined): TDDChart { diff --git a/web-common/src/features/dashboards/state-managers/actions/dimension-filters.ts b/web-common/src/features/dashboards/state-managers/actions/dimension-filters.ts index da2176a70fa..054e748cca5 100644 --- a/web-common/src/features/dashboards/state-managers/actions/dimension-filters.ts +++ b/web-common/src/features/dashboards/state-managers/actions/dimension-filters.ts @@ -1,3 +1,4 @@ +import { splitWhereFilter } from "@rilldata/web-common/features/dashboards/filters/measure-filters/measure-filter-utils"; import { createInExpression, getValueIndexInExpression, @@ -156,6 +157,16 @@ export function deselectItemsInFilter( } } +export function setFilters( + { dashboard }: DashboardMutables, + filter: V1Expression, +) { + const { dimensionFilters, dimensionThresholdFilters } = + splitWhereFilter(filter); + dashboard.whereFilter = dimensionFilters; + dashboard.dimensionThresholdFilters = dimensionThresholdFilters; +} + export const dimensionFilterActions = { /** * Toggles whether the given dimension value is selected in the @@ -170,4 +181,5 @@ export const dimensionFilterActions = { removeDimensionFilter, selectItemsInFilter, deselectItemsInFilter, + setFilters, }; diff --git a/web-common/src/features/dashboards/state-managers/selectors/dimension-filters.ts b/web-common/src/features/dashboards/state-managers/selectors/dimension-filters.ts index ab53b79055f..4a409f38a40 100644 --- a/web-common/src/features/dashboards/state-managers/selectors/dimension-filters.ts +++ b/web-common/src/features/dashboards/state-managers/selectors/dimension-filters.ts @@ -3,6 +3,7 @@ import { filterItemsSortFunction } from "@rilldata/web-common/features/dashboard import { forEachIdentifier, getValuesInExpression, + isExpressionUnsupported, matchExpressionByName, } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; import type { @@ -17,6 +18,9 @@ export const selectedDimensionValues = ( dashData: AtLeast, ): ((dimName: string) => string[]) => { return (dimName: string) => { + // if it is a complex filter unsupported by UI then no values are selected + if (isExpressionUnsupported(dashData.dashboard.whereFilter)) return []; + // FIXME: it is possible for this way of accessing the filters // to return the same value twice, which would seem to indicate // a bug in the way we're setting the filters / active values. diff --git a/web-common/src/features/dashboards/state-managers/state-managers.ts b/web-common/src/features/dashboards/state-managers/state-managers.ts index fa6e38fee59..cce34168762 100644 --- a/web-common/src/features/dashboards/state-managers/state-managers.ts +++ b/web-common/src/features/dashboards/state-managers/state-managers.ts @@ -7,6 +7,7 @@ import { getPersistentDashboardStore, initPersistentDashboardStore, } from "@rilldata/web-common/features/dashboards/stores/persistent-dashboard-state"; +import { initLocalUserPreferenceStore } from "@rilldata/web-common/features/dashboards/user-preferences"; import { type ExploreValidSpecResponse, useExploreValidSpec, @@ -139,6 +140,7 @@ export function createStateManagers({ // TODO: once we move everything from dashboard-stores to here, we can get rid of the global initPersistentDashboardStore((extraKeyPrefix || "") + exploreName); + initLocalUserPreferenceStore(exploreName); const persistentDashboardStore = getPersistentDashboardStore(); return { diff --git a/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts b/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts index 088b76dae5e..a330939ec9b 100644 --- a/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts +++ b/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts @@ -9,15 +9,18 @@ import { type MetricsExplorerEntity, } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { getPersistentDashboardState } from "@rilldata/web-common/features/dashboards/stores/persistent-dashboard-state"; -import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; -import { getLocalUserPreferences } from "@rilldata/web-common/features/dashboards/user-preferences"; +import { convertPresetToMetricsExplore } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToMetricsExplore"; +import { getBasePreset } from "@rilldata/web-common/features/dashboards/url-state/getBasePreset"; +import { + getLocalUserPreferences, + getLocalUserPreferencesState, +} from "@rilldata/web-common/features/dashboards/user-preferences"; import { getTimeComparisonParametersForComponent } from "@rilldata/web-common/lib/time/comparisons"; import { DEFAULT_TIME_RANGES } from "@rilldata/web-common/lib/time/config"; import { getDefaultTimeGrain } from "@rilldata/web-common/lib/time/grains"; import { ISODurationToTimePreset } from "@rilldata/web-common/lib/time/ranges"; import { isoDurationToFullTimeRange } from "@rilldata/web-common/lib/time/ranges/iso-ranges"; import type { TimeComparisonOption } from "@rilldata/web-common/lib/time/types"; -import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; import type { MetricsViewSpecDimensionV2, MetricsViewSpecMeasureV2, @@ -41,7 +44,8 @@ export function setDefaultTimeRange( !fullTimeRange.timeRangeSummary?.max ) return; - const timeZone = get(getLocalUserPreferences()).timeZone; + const timeZone = + explorePreset?.timezone || get(getLocalUserPreferences()).timeZone; const fullTimeStart = new Date(fullTimeRange.timeRangeSummary.min); const fullTimeEnd = new Date(fullTimeRange.timeRangeSummary.max); const timeRange = isoDurationToFullTimeRange( @@ -139,68 +143,27 @@ export function getDefaultMetricsExplorerEntity( metricsView: V1MetricsViewSpec, explore: V1ExploreSpec, fullTimeRange: V1MetricsViewTimeRangeResponse | undefined, + basePreset = getBasePreset(explore, getLocalUserPreferencesState(name)), ): MetricsExplorerEntity { - const defaultMeasureNames = - explore?.defaultPreset?.measures ?? explore?.measures ?? []; - - const defaultDimNames = - explore?.defaultPreset?.dimensions ?? explore?.dimensions ?? []; - - const metricsExplorer: MetricsExplorerEntity = { + const { entity: baseEntity } = convertPresetToMetricsExplore( + metricsView, + explore, + basePreset, + ); + const metricsExplorer = { + // fields filled here are the ones that are not stored and loaded to/from URL name, - visibleMeasureKeys: new Set( - defaultMeasureNames - .map((dm) => normaliseName(dm, metricsView.measures)) - .filter((dm) => !!dm) as string[], - ), - allMeasuresVisible: defaultMeasureNames.length === explore.measures?.length, - visibleDimensionKeys: new Set( - defaultDimNames - .map((dd) => normaliseName(dd, metricsView.dimensions)) - .filter((dd) => !!dd) as string[], - ), - allDimensionsVisible: defaultDimNames.length === explore.dimensions?.length, - leaderboardMeasureName: defaultMeasureNames[0], - whereFilter: createAndExpression([]), havingFilter: createAndExpression([]), - dimensionThresholdFilters: [], dimensionFilterExcludeMode: new Map(), leaderboardContextColumn: LeaderboardContextColumn.HIDDEN, dashboardSortType: SortType.VALUE, sortDirection: SortDirection.DESCENDING, - selectedTimezone: "UTC", - selectedTimeRange: undefined, - activePage: DashboardState_ActivePage.DEFAULT, - selectedComparisonDimension: undefined, - selectedDimensionName: undefined, - - showTimeComparison: false, temporaryFilterName: null, - tdd: { - chartType: TDDChart.DEFAULT, - expandedMeasureName: "", - pinIndex: -1, - }, - pivot: { - active: false, - rows: { - dimension: [], - }, - columns: { - dimension: [], - measure: [], - }, - rowJoinType: "nest", - expanded: {}, - sorting: [], - rowPage: 1, - enableComparison: true, - columnPage: 1, - activeCell: null, - }, contextColumnWidths: { ...contextColWidthDefaults }, - }; + + ...baseEntity, + } as MetricsExplorerEntity; // set time range related stuff setDefaultTimeRange(explore?.defaultPreset, metricsExplorer, fullTimeRange); setDefaultComparison(metricsView, explore, metricsExplorer, fullTimeRange); diff --git a/web-common/src/features/dashboards/stores/dashboard-stores.ts b/web-common/src/features/dashboards/stores/dashboard-stores.ts index 107c59f66fd..f6d8a09a875 100644 --- a/web-common/src/features/dashboards/stores/dashboard-stores.ts +++ b/web-common/src/features/dashboards/stores/dashboard-stores.ts @@ -149,16 +149,20 @@ const metricsViewReducers = { metricsView: V1MetricsViewSpec, explore: V1ExploreSpec, fullTimeRange: V1MetricsViewTimeRangeResponse | undefined, + initState: Partial = {}, ) { update((state) => { if (state.entities[name]) return state; - state.entities[name] = getDefaultMetricsExplorerEntity( - name, - metricsView, - explore, - fullTimeRange, - ); + state.entities[name] = { + ...getDefaultMetricsExplorerEntity( + name, + metricsView, + explore, + fullTimeRange, + ), + ...initState, + }; state.entities[name] = restorePersistedDashboardState( state.entities[name], ); @@ -202,9 +206,30 @@ const metricsViewReducers = { }); }, + mergePartialExplorerEntity( + name: string, + partialMetrics: Partial, + metricsView: V1MetricsViewSpec, + ) { + updateMetricsExplorerByName(name, (metricsExplorer) => { + for (const key in partialMetrics) { + metricsExplorer[key] = partialMetrics[key]; + } + // this hack is needed since what is shown for comparison is not a single source + // TODO: use an enum and get rid of this + if (!partialMetrics.showTimeComparison) { + metricsExplorer.showTimeComparison = false; + } + metricsExplorer.dimensionFilterExcludeMode = + includeExcludeModeFromFilters(partialMetrics.whereFilter); + AdvancedMeasureCorrector.correct(metricsExplorer, metricsView); + }); + }, + sync(name: string, explore: V1ExploreSpec) { if (!name || !explore || !explore.measures) return; updateMetricsExplorerByName(name, (metricsExplorer) => { + console.log("sync"); // remove references to non existent measures syncMeasures(explore, metricsExplorer); diff --git a/web-common/src/features/dashboards/stores/filter-utils.ts b/web-common/src/features/dashboards/stores/filter-utils.ts index 132b85214a1..a72d5ef4b60 100644 --- a/web-common/src/features/dashboards/stores/filter-utils.ts +++ b/web-common/src/features/dashboards/stores/filter-utils.ts @@ -305,3 +305,36 @@ export function isExpressionIncomplete(expression: V1Expression): boolean { // If the operation is specified and a defined, non-empty val is found, the expression is complete return false; } + +export function isJoinerExpression(expression: V1Expression | undefined) { + return ( + expression?.cond?.op && + (expression.cond.op === V1Operation.OPERATION_AND || + expression.cond.op === V1Operation.OPERATION_OR) + ); +} + +export function isExpressionUnsupported(expression: V1Expression) { + if ( + !expression.cond || + !expression.cond.exprs || + expression.cond?.op !== V1Operation.OPERATION_AND + ) { + return true; + } + + for (const expr of expression.cond.exprs) { + if (expr.cond?.op !== V1Operation.OPERATION_IN) return true; + + const subqueryExpr = expr.cond?.exprs?.[1]; + if ( + subqueryExpr?.subquery?.having?.cond?.exprs?.length && + isJoinerExpression(subqueryExpr.subquery.having) && + subqueryExpr.subquery.having.cond.exprs.length > 1 + ) { + return true; + } + } + + return false; +} diff --git a/web-common/src/features/dashboards/stores/syncDashboardState.ts b/web-common/src/features/dashboards/stores/syncDashboardState.ts index 5dc9ab6cd2d..6646456f4ff 100644 --- a/web-common/src/features/dashboards/stores/syncDashboardState.ts +++ b/web-common/src/features/dashboards/stores/syncDashboardState.ts @@ -66,8 +66,9 @@ export function createDashboardStateSync( const exploreName = get(ctx.exploreName); if (exploreName in get(metricsExplorerStore).entities) { // Successive syncs with metrics view spec - metricsExplorerStore.sync(exploreName, explore); + // metricsExplorerStore.sync(exploreName, explore); } else { + console.log("init"); // Running for the 1st time. Initialise the dashboard store. metricsExplorerStore.init( exploreName, @@ -79,13 +80,13 @@ export function createDashboardStateSync( get(page).url.searchParams.get("state") ?? initialUrlStateRes?.data; if (initialUrlState) { // If there is data to be loaded, load it during the init - metricsExplorerStore.syncFromUrl( - exploreName, - initialUrlState, - metricsView, - explore, - metricsViewSchemaRes.data.schema, - ); + // metricsExplorerStore.syncFromUrl( + // exploreName, + // initialUrlState, + // metricsView, + // explore, + // metricsViewSchemaRes.data.schema, + // ); // Call sync to make sure changes in dashboard are honoured metricsExplorerStore.sync(exploreName, explore); } diff --git a/web-common/src/features/dashboards/stores/test-data/data.ts b/web-common/src/features/dashboards/stores/test-data/data.ts index 21c9faf69c3..3678aca12df 100644 --- a/web-common/src/features/dashboards/stores/test-data/data.ts +++ b/web-common/src/features/dashboards/stores/test-data/data.ts @@ -1,7 +1,10 @@ +import { PivotChipType } from "@rilldata/web-common/features/dashboards/pivot/types"; import { createAndExpression, createInExpression, } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { getBasePreset } from "@rilldata/web-common/features/dashboards/url-state/getBasePreset"; import { getLocalIANA } from "@rilldata/web-common/lib/time/timezone"; import { getOffset, @@ -13,6 +16,7 @@ import { TimeOffsetType, TimeRangePreset, } from "@rilldata/web-common/lib/time/types"; +import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; import type { MetricsViewSpecDimensionV2, MetricsViewSpecMeasureV2, @@ -180,6 +184,12 @@ export const AD_BIDS_METRICS_WITH_BOOL_DIMENSION: V1MetricsViewSpec = { }, ], }; +export const AD_BIDS_METRICS_3_MEASURES_DIMENSIONS: V1MetricsViewSpec = { + displayName: AD_BIDS_NAME, + table: AD_BIDS_SOURCE_NAME, + measures: AD_BIDS_THREE_MEASURES, + dimensions: AD_BIDS_THREE_DIMENSIONS, +}; export const AD_BIDS_EXPLORE_INIT: V1ExploreSpec = { displayName: AD_BIDS_EXPLORE_NAME, @@ -221,6 +231,10 @@ export const AD_BIDS_EXPLORE_WITH_BOOL_DIMENSION: V1ExploreSpec = { ], }; +export const AD_BIDS_BASE_PRESET = getBasePreset(AD_BIDS_EXPLORE_INIT, { + timeZone: "UTC", +}); + export const AD_BIDS_SCHEMA: V1StructType = { fields: [ { @@ -310,3 +324,52 @@ export const CUSTOM_TEST_CONTROLS = { start: TestTimeConstants.LAST_18_HOURS, end: TestTimeConstants.LAST_12_HOURS, } as DashboardTimeControls; + +export const AD_BIDS_PIVOT_ENTITY: Partial = { + activePage: DashboardState_ActivePage.PIVOT, + pivot: { + active: true, + rows: { + dimension: [ + { + id: AD_BIDS_PUBLISHER_DIMENSION, + type: PivotChipType.Dimension, + title: AD_BIDS_PUBLISHER_DIMENSION, + }, + { + id: V1TimeGrain.TIME_GRAIN_HOUR, + type: PivotChipType.Time, + title: "hour", + }, + ], + }, + columns: { + measure: [ + { + id: AD_BIDS_IMPRESSIONS_MEASURE, + type: PivotChipType.Measure, + title: AD_BIDS_IMPRESSIONS_MEASURE, + }, + ], + dimension: [ + { + id: AD_BIDS_DOMAIN_DIMENSION, + type: PivotChipType.Dimension, + title: AD_BIDS_DOMAIN_DIMENSION, + }, + { + id: V1TimeGrain.TIME_GRAIN_DAY, + type: PivotChipType.Time, + title: "day", + }, + ], + }, + expanded: {}, + sorting: [], + columnPage: 1, + rowPage: 1, + enableComparison: true, + activeCell: null, + rowJoinType: "nest", + }, +}; diff --git a/web-common/src/features/dashboards/stores/test-data/helpers.ts b/web-common/src/features/dashboards/stores/test-data/helpers.ts index 5d01bfd39d9..e95668bdec9 100644 --- a/web-common/src/features/dashboards/stores/test-data/helpers.ts +++ b/web-common/src/features/dashboards/stores/test-data/helpers.ts @@ -1,4 +1,5 @@ import { QueryClient } from "@rilldata/svelte-query"; +import { PivotChipType } from "@rilldata/web-common/features/dashboards/pivot/types"; import { createStateManagers } from "@rilldata/web-common/features/dashboards/state-managers/state-managers"; import { getDefaultMetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; import { metricsExplorerStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; @@ -16,11 +17,14 @@ import { AD_BIDS_TIME_RANGE_SUMMARY, } from "@rilldata/web-common/features/dashboards/stores/test-data/data"; import type { ExploreValidSpecResponse } from "@rilldata/web-common/features/explores/selectors"; +import { TIME_GRAIN } from "@rilldata/web-common/lib/time/config"; import type { DashboardTimeControls } from "@rilldata/web-common/lib/time/types"; -import type { - V1ExploreSpec, - V1Expression, - V1MetricsViewSpec, +import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; +import { + type V1ExploreSpec, + type V1Expression, + type V1MetricsViewSpec, + V1TimeGrain, } from "@rilldata/web-common/runtime-client"; import { deepClone } from "@vitest/utils"; import { get } from "svelte/store"; @@ -161,3 +165,66 @@ export function getPartialDashboard( ); return partialDashboard; } + +export function getPivotedPartialDashboard( + pivotRowDimensions: string[], + pivotRowTimeDimensions: V1TimeGrain[], + pivotColumnMeasures: string[], + pivotColumnDimensions: string[], + pivotColumnTimeDimensions: V1TimeGrain[], +): Partial { + const hasPivot = + !!pivotRowDimensions.length || + !!pivotRowTimeDimensions.length || + !!pivotColumnMeasures.length || + !!pivotColumnDimensions.length || + !!pivotColumnTimeDimensions.length; + return { + activePage: hasPivot + ? DashboardState_ActivePage.PIVOT + : DashboardState_ActivePage.DEFAULT, + pivot: { + active: hasPivot, + rows: { + dimension: [ + ...pivotRowDimensions.map((r) => ({ + id: r, + type: PivotChipType.Dimension, + title: r, + })), + ...pivotRowTimeDimensions.map((g) => ({ + id: g, + type: PivotChipType.Time, + title: TIME_GRAIN[g]?.label.toString(), + })), + ], + }, + columns: { + measure: pivotColumnMeasures.map((m) => ({ + id: m, + type: PivotChipType.Measure, + title: m, + })), + dimension: [ + ...pivotColumnDimensions.map((r) => ({ + id: r, + type: PivotChipType.Dimension, + title: r, + })), + ...pivotColumnTimeDimensions.map((g) => ({ + id: g, + type: PivotChipType.Time, + title: TIME_GRAIN[g]?.label.toString(), + })), + ], + }, + expanded: {}, + sorting: [], + columnPage: 1, + rowPage: 1, + enableComparison: true, + activeCell: null, + rowJoinType: "nest", + }, + }; +} diff --git a/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte b/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte new file mode 100644 index 00000000000..d7c0fbdb190 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte @@ -0,0 +1,111 @@ + + +{#if schemaError} + +{:else if timeRangeSummaryError} + +{:else if $dashboardStore} + +{/if} diff --git a/web-common/src/features/dashboards/url-state/DashboardURLStateSyncWrapper.svelte b/web-common/src/features/dashboards/url-state/DashboardURLStateSyncWrapper.svelte new file mode 100644 index 00000000000..b295b03c587 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/DashboardURLStateSyncWrapper.svelte @@ -0,0 +1,42 @@ + + + + + diff --git a/web-common/src/features/dashboards/url-state/convertLegacyStateToExplorePreset.ts b/web-common/src/features/dashboards/url-state/convertLegacyStateToExplorePreset.ts new file mode 100644 index 00000000000..4478629ccbc --- /dev/null +++ b/web-common/src/features/dashboards/url-state/convertLegacyStateToExplorePreset.ts @@ -0,0 +1,385 @@ +import { FromProtoTimeGrainMap } from "@rilldata/web-common/features/dashboards/proto-state/enum-maps"; +import { convertFilterToExpression } from "@rilldata/web-common/features/dashboards/proto-state/filter-converter"; +import { + correctComparisonTimeRange, + fromExpressionProto, +} from "@rilldata/web-common/features/dashboards/proto-state/fromProto"; +import { + createAndExpression, + createSubQueryExpression, + getAllIdentifiers, +} from "@rilldata/web-common/features/dashboards/stores/filter-utils"; +import { + ExplorePresetDefaultChartType, + URLStateDefaultTimezone, +} from "@rilldata/web-common/features/dashboards/url-state/defaults"; +import { + getMultiFieldError, + getSingleFieldError, +} from "@rilldata/web-common/features/dashboards/url-state/error-message-helpers"; +import { mapLegacyChartType } from "@rilldata/web-common/features/dashboards/url-state/legacyMappers"; +import { + FromActivePageMap, + FromURLParamTimeDimensionMap, + ToURLParamTimeDimensionMap, +} from "@rilldata/web-common/features/dashboards/url-state/mappers"; +import { + getMapFromArray, + getMissingValues, +} from "@rilldata/web-common/lib/arrayUtils"; +import type { TimeGrain } from "@rilldata/web-common/proto/gen/rill/runtime/v1/time_grain_pb"; +import { + type DashboardState, + DashboardState_ActivePage, + DashboardState_LeaderboardSortDirection, + PivotElement, +} from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; +import { + type MetricsViewSpecDimensionV2, + type MetricsViewSpecMeasureV2, + V1ExploreComparisonMode, + type V1ExplorePreset, + type V1ExploreSpec, + type V1Expression, + type V1MetricsViewSpec, +} from "@rilldata/web-common/runtime-client"; + +export function convertLegacyStateToExplorePreset( + legacyState: DashboardState, + metricsView: V1MetricsViewSpec, + explore: V1ExploreSpec, + basePreset: V1ExplorePreset, +) { + const preset: V1ExplorePreset = { + ...basePreset, + }; + const errors: Error[] = []; + + const measures = getMapFromArray( + metricsView.measures?.filter((m) => explore.measures?.includes(m.name!)) ?? + [], + (m) => m.name!, + ); + const dimensions = getMapFromArray( + metricsView.dimensions?.filter((d) => + explore.dimensions?.includes(d.name!), + ) ?? [], + (d) => d.name!, + ); + + if (legacyState.activePage !== DashboardState_ActivePage.UNSPECIFIED) { + preset.view = FromActivePageMap[legacyState.activePage]; + } + + if (legacyState.filters) { + // backwards compatibility for our older filter format + preset.where = convertFilterToExpression(legacyState.filters); + // TODO: correct older values that would have strings for non-strings + } else if (legacyState.where) { + preset.where = fromExpressionProto(legacyState.where); + } + if (legacyState.having) { + preset.where ??= createAndExpression([]); + const exprs = preset.where?.cond?.exprs as V1Expression[]; + legacyState.having.forEach((h) => { + if (!h.filter) return; + const expr = fromExpressionProto(h.filter); + exprs.push( + createSubQueryExpression(h.name, getAllIdentifiers(expr), expr), + ); + }); + } + + const { preset: trPreset, errors: trErrors } = fromLegacyTimeRangeFields( + legacyState, + dimensions, + ); + Object.assign(preset, trPreset); + errors.push(...trErrors); + + const { preset: ovPreset, errors: ovErrors } = fromLegacyOverviewFields( + legacyState, + measures, + dimensions, + explore, + ); + Object.assign(preset, ovPreset); + errors.push(...ovErrors); + + const { preset: tddPreset, errors: tddErrors } = + fromLegacyTimeDimensionFields(legacyState, measures); + Object.assign(preset, tddPreset); + errors.push(...tddErrors); + + const { preset: pivotPreset, errors: pivotErrors } = fromLegacyPivotFields( + legacyState, + measures, + dimensions, + ); + Object.assign(preset, pivotPreset); + errors.push(...pivotErrors); + + return { preset, errors }; +} + +function fromLegacyTimeRangeFields( + legacyState: DashboardState, + dimensions: Map, +) { + const preset: V1ExplorePreset = {}; + const errors: Error[] = []; + + if (legacyState.timeRange?.name) { + preset.timeRange = legacyState.timeRange.name; + // TODO: custom time range + } + if (legacyState.timeGrain) { + // TODO + // preset.timeGrain = legacyState.timeGrain; + } + + if (legacyState.compareTimeRange?.name) { + preset.compareTimeRange = correctComparisonTimeRange( + legacyState.compareTimeRange.name, + ); + // TODO: custom time range + } + if (legacyState.showTimeComparison) { + preset.comparisonMode = + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_TIME; + } + + if (legacyState.comparisonDimension) { + if (dimensions.has(legacyState.comparisonDimension)) { + preset.comparisonDimension = legacyState.comparisonDimension; + preset.comparisonMode = + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_DIMENSION; + } else { + errors.push( + getSingleFieldError( + "compare dimension", + legacyState.comparisonDimension, + ), + ); + } + } else { + // older state would unset comparison dimension when empty + preset.comparisonDimension = ""; + } + + if (!preset.comparisonMode) { + // if there was no comparison in legacyState it was an unset to `None` + preset.comparisonMode = + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_NONE; + } + + preset.timezone = legacyState.selectedTimezone ?? URLStateDefaultTimezone; + + // TODO: scrubRange + + return { preset, errors }; +} + +function fromLegacyOverviewFields( + legacyState: DashboardState, + measures: Map, + dimensions: Map, + explore: V1ExploreSpec, +) { + const preset: V1ExplorePreset = {}; + const errors: Error[] = []; + + if (legacyState.allMeasuresVisible) { + preset.measures = explore.measures ?? []; + } else if (legacyState.visibleMeasures?.length) { + preset.measures = legacyState.visibleMeasures.filter((m) => + measures.has(m), + ); + const missingMeasures = getMissingValues( + legacyState.visibleMeasures, + preset.measures, + ); + if (missingMeasures.length) { + errors.push(getMultiFieldError("measure", missingMeasures)); + } + } + + if (legacyState.allDimensionsVisible) { + preset.dimensions = explore.dimensions ?? []; + } else if (legacyState.visibleDimensions?.length) { + preset.dimensions = legacyState.visibleDimensions.filter((d) => + dimensions.has(d), + ); + const missingDimensions = getMissingValues( + legacyState.visibleDimensions, + preset.dimensions, + ); + if (missingDimensions.length) { + errors.push(getMultiFieldError("dimension", missingDimensions)); + } + } + + if (legacyState.leaderboardMeasure !== undefined) { + if (measures.has(legacyState.leaderboardMeasure)) { + preset.overviewSortBy = legacyState.leaderboardMeasure; + } else { + errors.push( + getSingleFieldError("sort by measure", legacyState.leaderboardMeasure), + ); + } + } + + if (legacyState.leaderboardSortDirection) { + preset.overviewSortAsc = + legacyState.leaderboardSortDirection === + DashboardState_LeaderboardSortDirection.ASCENDING; + } + + if (legacyState.leaderboardContextColumn !== undefined) { + // TODO + } + + if (legacyState.leaderboardSortType) { + // TODO + } + + if (legacyState.selectedDimension) { + if (dimensions.has(legacyState.selectedDimension)) { + preset.overviewExpandedDimension = legacyState.selectedDimension; + } else { + errors.push( + getSingleFieldError( + "expanded dimension", + legacyState.selectedDimension, + ), + ); + } + } else if (legacyState.activePage !== DashboardState_ActivePage.UNSPECIFIED) { + // UNSPECIFIED means it was a partial state stored to proto state + // So anything other than that would need to unset this + preset.overviewExpandedDimension = ""; + } + + return { preset, errors }; +} + +function fromLegacyTimeDimensionFields( + legacyState: DashboardState, + measures: Map, +) { + const preset: V1ExplorePreset = {}; + const errors: Error[] = []; + + if (!legacyState.expandedMeasure) { + if (legacyState.activePage) { + preset.timeDimensionMeasure = ""; + preset.timeDimensionPin = false; + preset.timeDimensionChartType = ExplorePresetDefaultChartType; + } + return { preset, errors }; + } + + if (!measures.has(legacyState.expandedMeasure)) { + errors.push( + getSingleFieldError("expanded measure", legacyState.expandedMeasure), + ); + return { preset, errors }; + } + + preset.timeDimensionMeasure = legacyState.expandedMeasure; + preset.timeDimensionPin = false; // TODO + preset.timeDimensionChartType = mapLegacyChartType(legacyState.chartType); + + return { preset, errors }; +} + +function fromLegacyPivotFields( + legacyState: DashboardState, + measures: Map, + dimensions: Map, +) { + const preset: V1ExplorePreset = {}; + const errors: Error[] = []; + + const mapTimeDimension = (grain: TimeGrain) => + ToURLParamTimeDimensionMap[FromProtoTimeGrainMap[grain]] ?? ""; + const mapAllDimension = (dimension: PivotElement) => { + if (dimension?.element.case === "pivotTimeDimension") { + return mapTimeDimension(dimension?.element.value); + } else { + return dimension?.element.value as string; + } + }; + + if ( + legacyState.pivotRowAllDimensions?.length || + legacyState.pivotColumnAllDimensions?.length + ) { + preset.pivotRows = legacyState.pivotRowAllDimensions.map(mapAllDimension); + preset.pivotCols = + legacyState.pivotColumnAllDimensions.map(mapAllDimension); + } else if ( + // backwards compatibility for state + legacyState.pivotRowDimensions?.length || + legacyState.pivotRowTimeDimensions?.length || + legacyState.pivotColumnDimensions?.length || + legacyState.pivotColumnTimeDimensions?.length + ) { + preset.pivotRows = [ + ...legacyState.pivotRowTimeDimensions + .map(mapTimeDimension) + .filter(Boolean), + ...legacyState.pivotRowDimensions, + ]; + preset.pivotCols = [ + ...legacyState.pivotColumnTimeDimensions + .map(mapTimeDimension) + .filter(Boolean), + ...legacyState.pivotColumnDimensions, + ]; + } + + if (legacyState.pivotColumnMeasures?.length) { + preset.pivotCols ??= []; + preset.pivotCols.push(...legacyState.pivotColumnMeasures); + } + + if (preset.pivotRows?.length) { + const allValues = preset.pivotRows; + preset.pivotRows = preset.pivotRows.filter( + (r) => dimensions.has(r) || r in FromURLParamTimeDimensionMap, + ); + const missingRows = getMissingValues(allValues, preset.pivotRows); + if (missingRows.length) { + errors.push(getMultiFieldError("pivot row", missingRows)); + } + } + + if (preset.pivotCols?.length) { + const allValues = preset.pivotCols; + preset.pivotCols = preset.pivotCols.filter( + (c) => + dimensions.has(c) || + measures.has(c) || + c in FromURLParamTimeDimensionMap, + ); + const missingCols = getMissingValues(allValues, preset.pivotCols); + if (missingCols.length) { + errors.push(getMultiFieldError("pivot column", missingCols)); + } + } + + if ( + legacyState.activePage !== DashboardState_ActivePage.PIVOT && + // UNSPECIFIED means it was a partial state stored to proto state + legacyState.activePage !== DashboardState_ActivePage.UNSPECIFIED + ) { + // legacy state would unset when active page is not pivot + preset.pivotRows = []; + preset.pivotCols = []; + } + + // TODO: other fields + + return { preset, errors }; +} diff --git a/web-common/src/features/dashboards/url-state/convertMetricsEntityToURLSearchParams.ts b/web-common/src/features/dashboards/url-state/convertMetricsEntityToURLSearchParams.ts new file mode 100644 index 00000000000..31122d0e8ea --- /dev/null +++ b/web-common/src/features/dashboards/url-state/convertMetricsEntityToURLSearchParams.ts @@ -0,0 +1,231 @@ +import { mergeMeasureFilters } from "@rilldata/web-common/features/dashboards/filters/measure-filters/measure-filter-utils"; +import { + type PivotChipData, + PivotChipType, +} from "@rilldata/web-common/features/dashboards/pivot/types"; +import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { + URLStateDefaultSortDirection, + URLStateDefaultTDDChartType, + URLStateDefaultTimeRange, + URLStateDefaultTimezone, +} from "@rilldata/web-common/features/dashboards/url-state/defaults"; +import { convertExpressionToFilterParam } from "@rilldata/web-common/features/dashboards/url-state/filters/converters"; +import { + FromActivePageMap, + ToURLParamTDDChartMap, + ToURLParamTimeDimensionMap, + ToURLParamViewMap, +} from "@rilldata/web-common/features/dashboards/url-state/mappers"; +import { + arrayOrderedEquals, + arrayUnorderedEquals, +} from "@rilldata/web-common/lib/arrayUtils"; +import { + type V1ExplorePreset, + type V1ExploreSpec, + V1ExploreWebView, +} from "@rilldata/web-common/runtime-client"; + +export function convertMetricsEntityToURLSearchParams( + metrics: MetricsExplorerEntity, + searchParams: URLSearchParams, + explore: V1ExploreSpec, + preset: V1ExplorePreset, +) { + if (!metrics) return; + + const currentView = FromActivePageMap[metrics.activePage]; + if ( + (preset.view !== undefined && preset.view !== currentView) || + (preset.view === undefined && + currentView !== V1ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW) + ) { + searchParams.set("vw", ToURLParamViewMap[currentView] as string); + } + + const expr = mergeMeasureFilters(metrics); + if (expr && expr?.cond?.exprs?.length) { + searchParams.set("f", convertExpressionToFilterParam(expr)); + } + + toTimeRangesUrl(metrics, searchParams, preset); + + toOverviewUrl(metrics, searchParams, explore, preset); + + toTimeDimensionUrlParams(metrics, searchParams, preset); + + toPivotUrlParams(metrics, searchParams, preset); +} + +function toTimeRangesUrl( + metrics: MetricsExplorerEntity, + searchParams: URLSearchParams, + preset: V1ExplorePreset, +) { + if ( + (preset.timeRange !== undefined && + metrics.selectedTimeRange !== undefined && + metrics.selectedTimeRange.name !== preset.timeRange) || + (preset.timeRange === undefined && + metrics.selectedTimeRange?.name !== URLStateDefaultTimeRange) + ) { + searchParams.set("tr", metrics.selectedTimeRange?.name ?? ""); + } + if ( + // if preset has timezone then only set if selected is not the same + (preset.timezone !== undefined && + metrics.selectedTimezone !== preset.timezone) || + // else if the timezone is not the default then set the param + (preset.timezone === undefined && + metrics.selectedTimezone !== URLStateDefaultTimezone) + ) { + searchParams.set("tz", metrics.selectedTimezone); + } + + if ( + (preset.compareTimeRange !== undefined && + metrics.selectedComparisonTimeRange !== undefined && + metrics.selectedComparisonTimeRange.name !== preset.compareTimeRange) || + (preset.compareTimeRange === undefined && + !!metrics.selectedComparisonTimeRange?.name) + ) { + searchParams.set("ctr", metrics.selectedComparisonTimeRange?.name ?? ""); + } + if ( + // if preset has a compare dimension then only set if selected is not the same + (preset.comparisonDimension !== undefined && + metrics.selectedComparisonDimension !== preset.comparisonDimension) || + // else if there is no default then set if there was a selected compare dimension + (preset.comparisonDimension === undefined && + !!metrics.selectedComparisonDimension) + ) { + searchParams.set("cd", metrics.selectedComparisonDimension ?? ""); + } + + // TODO: grain +} + +function toOverviewUrl( + metrics: MetricsExplorerEntity, + searchParams: URLSearchParams, + explore: V1ExploreSpec, + preset: V1ExplorePreset, +) { + if (metrics.visibleMeasureKeys) { + const measures = [...metrics.visibleMeasureKeys]; + const presetMeasures = preset.measures ?? explore.measures ?? []; + if (!arrayUnorderedEquals(measures, presetMeasures)) { + if (metrics.allMeasuresVisible) { + searchParams.set("o.m", "*"); + } else { + searchParams.set("o.m", measures.join(",")); + } + } + } + + if (metrics.visibleDimensionKeys) { + const dimensions = [...metrics.visibleDimensionKeys]; + const presetDimensions = preset.dimensions ?? explore.dimensions ?? []; + if (!arrayUnorderedEquals(dimensions, presetDimensions)) { + if (metrics.allDimensionsVisible) { + searchParams.set("o.d", "*"); + } else { + searchParams.set("o.d", dimensions.join(",")); + } + } + } + + const defaultLeaderboardMeasure = + preset.measures?.[0] ?? explore.measures?.[0]; + if ( + // if sort by is defined in preset then only set param if selected is not the same. + (preset.overviewSortBy && + metrics.leaderboardMeasureName !== preset.overviewSortBy) || + // else the default is the 1st measure in preset or explore, so check that next + (!preset.overviewSortBy && + metrics.leaderboardMeasureName !== defaultLeaderboardMeasure) + ) { + searchParams.set("o.sb", metrics.leaderboardMeasureName); + } + + const sortAsc = metrics.sortDirection === SortDirection.ASCENDING; + if ( + // if preset has a sort direction then only set if not the same + (preset.overviewSortAsc !== undefined && + preset.overviewSortAsc !== sortAsc) || + // else if the direction is not the default then set the param + (preset.overviewSortAsc === undefined && + metrics.sortDirection !== URLStateDefaultSortDirection) + ) { + searchParams.set("o.sd", sortAsc ? "ASC" : "DESC"); + } + + if ( + (preset.overviewExpandedDimension !== undefined && + metrics.selectedDimensionName !== preset.overviewExpandedDimension) || + (preset.overviewExpandedDimension === undefined && + metrics.selectedDimensionName) + ) { + searchParams.set("o.ed", metrics.selectedDimensionName ?? ""); + } +} + +function toTimeDimensionUrlParams( + metrics: MetricsExplorerEntity, + searchParams: URLSearchParams, + preset: V1ExplorePreset, +) { + if ( + (preset.timeDimensionMeasure !== undefined && + metrics.tdd.expandedMeasureName !== preset.timeDimensionMeasure) || + (preset.timeDimensionMeasure === undefined && + metrics.tdd.expandedMeasureName) + ) { + searchParams.set("tdd.m", metrics.tdd.expandedMeasureName ?? ""); + } + + if ( + (preset.timeDimensionChartType !== undefined && + ToURLParamTDDChartMap[metrics.tdd.chartType] !== + preset.timeDimensionChartType) || + (preset.timeDimensionChartType === undefined && + metrics.tdd.chartType !== URLStateDefaultTDDChartType) + ) { + searchParams.set( + "tdd.ct", + ToURLParamTDDChartMap[metrics.tdd.chartType] ?? "", + ); + } + + // TODO: pin + // TODO: what should be done when chartType is set but expandedMeasureName is not +} + +function toPivotUrlParams( + metrics: MetricsExplorerEntity, + searchParams: URLSearchParams, + preset: V1ExplorePreset, +) { + const mapPivotEntry = (data: PivotChipData) => { + if (data.type === PivotChipType.Time) + return ToURLParamTimeDimensionMap[data.id] as string; + return data.id; + }; + + const rows = metrics.pivot.rows.dimension.map(mapPivotEntry); + if (!arrayOrderedEquals(rows, preset.pivotRows ?? [])) { + searchParams.set("p.r", rows.join(",")); + } + + const cols = [ + ...metrics.pivot.columns.dimension.map(mapPivotEntry), + ...metrics.pivot.columns.measure.map(mapPivotEntry), + ]; + if (!arrayOrderedEquals(cols, preset.pivotCols ?? [])) { + searchParams.set("p.c", cols.join(",")); + } + + // TODO: other fields +} diff --git a/web-common/src/features/dashboards/url-state/convertMetricsExploreToPreset.ts b/web-common/src/features/dashboards/url-state/convertMetricsExploreToPreset.ts new file mode 100644 index 00000000000..523ef75bb93 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/convertMetricsExploreToPreset.ts @@ -0,0 +1,168 @@ +import { mergeDimensionAndMeasureFilter } from "@rilldata/web-common/features/dashboards/filters/measure-filters/measure-filter-utils"; +import { + type PivotChipData, + PivotChipType, +} from "@rilldata/web-common/features/dashboards/pivot/types"; +import { createAndExpression } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { + FromActivePageMap, + ToURLParamTDDChartMap, + ToURLParamTimeDimensionMap, +} from "@rilldata/web-common/features/dashboards/url-state/mappers"; +import { DashboardState_LeaderboardSortDirection } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; +import { + V1ExploreComparisonMode, + type V1ExplorePreset, + type V1ExploreSpec, +} from "@rilldata/web-common/runtime-client"; + +export function convertMetricsExploreToPreset( + metrics: Partial, + explore: V1ExploreSpec, +) { + const preset: V1ExplorePreset = {}; + + if (metrics.activePage) { + preset.view = FromActivePageMap[metrics.activePage]; + } + + if (metrics.whereFilter || metrics.dimensionThresholdFilters) { + preset.where = mergeDimensionAndMeasureFilter( + metrics.whereFilter ?? createAndExpression([]), + metrics.dimensionThresholdFilters ?? [], + ); + } + + Object.assign(preset, fromMetricsExploreTimeRangeFields(metrics)); + + Object.assign(preset, fromMetricsExploreOverviewFields(metrics, explore)); + + Object.assign(preset, fromMetricsExploreTimeDimensionFields(metrics)); + + Object.assign(preset, fromMetricsExplorePivotFields(metrics)); + + return preset; +} + +function fromMetricsExploreTimeRangeFields( + metrics: Partial, +) { + const preset: V1ExplorePreset = {}; + + if (metrics.selectedTimeRange?.name) { + preset.timeRange = metrics.selectedTimeRange?.name; + // TODO: custom time range + } + // TODO: grain + + if (metrics.selectedComparisonTimeRange?.name) { + preset.compareTimeRange = metrics.selectedComparisonTimeRange.name; + // TODO: custom time range + } + if (metrics.showTimeComparison) { + preset.comparisonMode = + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_TIME; + } + + if (metrics.selectedComparisonDimension !== undefined) { + preset.comparisonDimension = metrics.selectedComparisonDimension; + if (metrics.selectedComparisonDimension) { + preset.comparisonMode = + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_DIMENSION; + } + } + + if (metrics.selectedTimezone) { + preset.timezone = metrics.selectedTimezone; + } + + // TODO: scrubRange + + return preset; +} + +function fromMetricsExploreOverviewFields( + metrics: Partial, + explore: V1ExploreSpec, +) { + const preset: V1ExplorePreset = {}; + + if (metrics.allMeasuresVisible) { + preset.measures = explore.measures ?? []; + } else if (metrics.visibleMeasureKeys) { + preset.measures = [...metrics.visibleMeasureKeys]; + } + + if (metrics.allDimensionsVisible) { + preset.dimensions = explore.dimensions ?? []; + } else if (metrics.visibleDimensionKeys) { + preset.dimensions = [...metrics.visibleDimensionKeys]; + } + + if (metrics.leaderboardMeasureName !== undefined) { + preset.overviewSortBy = metrics.leaderboardMeasureName; + } + + if (metrics.sortDirection) { + preset.overviewSortAsc = + metrics.sortDirection === + DashboardState_LeaderboardSortDirection.ASCENDING; + } + + if (metrics.leaderboardContextColumn !== undefined) { + // TODO + } + + if (metrics.dashboardSortType) { + // TODO + } + + if (metrics.selectedDimensionName !== undefined) { + preset.overviewExpandedDimension = metrics.selectedDimensionName; + } + + return preset; +} + +function fromMetricsExploreTimeDimensionFields( + metrics: Partial, +) { + const preset: V1ExplorePreset = {}; + + if (!metrics.tdd) { + return preset; + } + + preset.timeDimensionMeasure = metrics.tdd.expandedMeasureName; + preset.timeDimensionPin = false; // TODO + preset.timeDimensionChartType = ToURLParamTDDChartMap[metrics.tdd.chartType]; + + return preset; +} + +function fromMetricsExplorePivotFields( + metrics: Partial, +) { + const preset: V1ExplorePreset = {}; + + if (!metrics.pivot) { + return preset; + } + + const mapPivotEntry = (data: PivotChipData) => { + if (data.type === PivotChipType.Time) + return ToURLParamTimeDimensionMap[data.id] as string; + return data.id; + }; + + preset.pivotRows = metrics.pivot.rows.dimension.map(mapPivotEntry); + preset.pivotCols = [ + ...metrics.pivot.columns.dimension.map(mapPivotEntry), + ...metrics.pivot.columns.measure.map(mapPivotEntry), + ]; + + // TODO: other fields + + return preset; +} diff --git a/web-common/src/features/dashboards/url-state/convertPresetToMetricsExplore.ts b/web-common/src/features/dashboards/url-state/convertPresetToMetricsExplore.ts new file mode 100644 index 00000000000..6dd562a1437 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/convertPresetToMetricsExplore.ts @@ -0,0 +1,437 @@ +import { splitWhereFilter } from "@rilldata/web-common/features/dashboards/filters/measure-filters/measure-filter-utils"; +import { + type PivotChipData, + PivotChipType, +} from "@rilldata/web-common/features/dashboards/pivot/types"; +import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; +import { convertURLToExplorePreset } from "@rilldata/web-common/features/dashboards/url-state/convertURLToExplorePreset"; +import { + getMultiFieldError, + getSingleFieldError, +} from "@rilldata/web-common/features/dashboards/url-state/error-message-helpers"; +import { + FromURLParamTDDChartMap, + FromURLParamTimeDimensionMap, + ToActivePageViewMap, +} from "@rilldata/web-common/features/dashboards/url-state/mappers"; +import { + getMapFromArray, + getMissingValues, +} from "@rilldata/web-common/lib/arrayUtils"; +import { TIME_GRAIN } from "@rilldata/web-common/lib/time/config"; +import type { DashboardTimeControls } from "@rilldata/web-common/lib/time/types"; +import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; +import { + type MetricsViewSpecDimensionV2, + type MetricsViewSpecMeasureV2, + V1ExploreComparisonMode, + type V1ExplorePreset, + type V1ExploreSpec, + V1ExploreWebView, + type V1MetricsViewSpec, +} from "@rilldata/web-common/runtime-client"; + +export function convertURLToMetricsExplore( + searchParams: URLSearchParams, + metricsView: V1MetricsViewSpec, + explore: V1ExploreSpec, + basePreset: V1ExplorePreset, +) { + const errors: Error[] = []; + const { preset, errors: errorsFromPreset } = convertURLToExplorePreset( + searchParams, + metricsView, + explore, + basePreset, + ); + errors.push(...errorsFromPreset); + const { entity, errors: errorsFromEntity } = convertPresetToMetricsExplore( + metricsView, + explore, + preset, + ); + errors.push(...errorsFromEntity); + return { entity, errors }; +} + +/** + * Converts a V1ExplorePreset to our internal metrics explore state. + * V1ExplorePreset could come from url state, bookmark, alert or report. + */ +export function convertPresetToMetricsExplore( + metricsView: V1MetricsViewSpec, + explore: V1ExploreSpec, + preset: V1ExplorePreset, +) { + const entity: Partial = {}; + const errors: Error[] = []; + + const measures = getMapFromArray( + metricsView.measures?.filter((m) => explore.measures?.includes(m.name!)) ?? + [], + (m) => m.name!, + ); + const dimensions = getMapFromArray( + metricsView.dimensions?.filter((d) => + explore.dimensions?.includes(d.name!), + ) ?? [], + (d) => d.name!, + ); + + if (preset.view) { + entity.activePage = Number(ToActivePageViewMap[preset.view] ?? "0"); + } + + if (preset.where) { + const { dimensionFilters, dimensionThresholdFilters } = splitWhereFilter( + preset.where, + ); + entity.whereFilter = dimensionFilters; + entity.dimensionThresholdFilters = dimensionThresholdFilters; + } + + const { entity: trEntity, errors: trErrors } = fromTimeRangesParams( + preset, + dimensions, + ); + Object.assign(entity, trEntity); + errors.push(...trErrors); + + const { entity: ovEntity, errors: ovErrors } = fromOverviewUrlParams( + measures, + dimensions, + explore, + preset, + ); + Object.assign(entity, ovEntity); + errors.push(...ovErrors); + + const { entity: tddEntity, errors: tddErrors } = fromTimeDimensionUrlParams( + measures, + preset, + ); + Object.assign(entity, tddEntity); + errors.push(...tddErrors); + + const { entity: pivotEntity, errors: pivotErrors } = fromPivotUrlParams( + measures, + dimensions, + preset, + ); + Object.assign(entity, pivotEntity); + errors.push(...pivotErrors); + + return { entity, errors }; +} + +function fromTimeRangesParams( + preset: V1ExplorePreset, + dimensions: Map, +) { + const entity: Partial = {}; + const errors: Error[] = []; + + if (preset.timeRange) { + const { timeRange, error } = fromTimeRangeUrlParam(preset.timeRange); + if (error) errors.push(error); + entity.selectedTimeRange = timeRange; + } + + if (preset.timezone) { + entity.selectedTimezone = preset.timezone; + } + + if (preset.compareTimeRange) { + const { timeRange, error } = fromTimeRangeUrlParam(preset.compareTimeRange); + if (error) errors.push(error); + entity.selectedComparisonTimeRange = timeRange; + entity.showTimeComparison = true; + // unset compare dimension + entity.selectedComparisonDimension = ""; + } + + if (preset.comparisonDimension) { + if (dimensions.has(preset.comparisonDimension)) { + entity.selectedComparisonDimension = preset.comparisonDimension; + // unset compare time ranges + entity.selectedComparisonTimeRange = undefined; + entity.showTimeComparison = false; + } else { + errors.push( + getSingleFieldError("compare dimension", preset.comparisonDimension), + ); + } + } + + if ( + preset.comparisonMode === + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_NONE + ) { + // unset all comparison setting if mode is none + entity.selectedComparisonTimeRange = undefined; + entity.selectedComparisonDimension = ""; + entity.showTimeComparison = false; + } + + // TODO: grain + + return { entity, errors }; +} + +function fromTimeRangeUrlParam(tr: string): { + timeRange?: DashboardTimeControls; + error?: Error; +} { + // TODO: validation + return { + timeRange: { + name: tr, + } as DashboardTimeControls, + }; + + // return { + // error: new Error(`unknown time range: ${tr}`), + // }; +} + +function fromOverviewUrlParams( + measures: Map, + dimensions: Map, + explore: V1ExploreSpec, + preset: V1ExplorePreset, +) { + const entity: Partial = {}; + const errors: Error[] = []; + + if (preset.measures?.length) { + const selectedMeasures = preset.measures.filter((m) => measures.has(m)); + const missingMeasures = getMissingValues(selectedMeasures, preset.measures); + if (missingMeasures.length) { + errors.push(getMultiFieldError("measure", missingMeasures)); + } + + entity.allMeasuresVisible = + selectedMeasures.length === explore.measures?.length; + entity.visibleMeasureKeys = new Set(selectedMeasures); + } + + if (preset.dimensions?.length) { + const selectedDimensions = preset.dimensions.filter((d) => + dimensions.has(d), + ); + const missingDimensions = getMissingValues( + selectedDimensions, + preset.dimensions, + ); + if (missingDimensions.length) { + errors.push(getMultiFieldError("dimension", missingDimensions)); + } + + entity.allDimensionsVisible = + selectedDimensions.length === explore.dimensions?.length; + entity.visibleDimensionKeys = new Set(selectedDimensions); + } + + if (preset.overviewSortBy) { + if (measures.has(preset.overviewSortBy)) { + entity.leaderboardMeasureName = preset.overviewSortBy; + } else { + errors.push( + getSingleFieldError("sort by measure", preset.overviewSortBy), + ); + } + } + + if (preset.overviewSortAsc !== undefined) { + entity.sortDirection = preset.overviewSortAsc + ? SortDirection.ASCENDING + : SortDirection.DESCENDING; + } + + if (preset.overviewExpandedDimension !== undefined) { + if (preset.overviewExpandedDimension === "") { + entity.selectedDimensionName = ""; + // if preset didnt have a view then this is a dimension table unset. + if ( + preset.view === V1ExploreWebView.EXPLORE_ACTIVE_PAGE_UNSPECIFIED || + preset.view === undefined + ) { + entity.activePage = DashboardState_ActivePage.DEFAULT; + } + } else if (dimensions.has(preset.overviewExpandedDimension)) { + entity.selectedDimensionName = preset.overviewExpandedDimension; + if ( + preset.view === V1ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW || + preset.view === V1ExploreWebView.EXPLORE_ACTIVE_PAGE_UNSPECIFIED || + preset.view === undefined + ) { + entity.activePage = DashboardState_ActivePage.DIMENSION_TABLE; + } + } else { + errors.push( + getSingleFieldError( + "expanded dimension", + preset.overviewExpandedDimension, + ), + ); + } + } + + return { entity, errors }; +} + +function fromTimeDimensionUrlParams( + measures: Map, + preset: V1ExplorePreset, +): { + entity: Partial; + errors: Error[]; +} { + if (preset.timeDimensionMeasure === undefined) { + return { + entity: {}, + errors: [], + }; + } + + const errors: Error[] = []; + + let expandedMeasureName = preset.timeDimensionMeasure; + if (expandedMeasureName && !measures.has(expandedMeasureName)) { + expandedMeasureName = ""; + errors.push(getSingleFieldError("expanded measure", expandedMeasureName)); + } + + // unset + if (expandedMeasureName === "") { + return { + entity: { + tdd: { + expandedMeasureName: "", + chartType: TDDChart.DEFAULT, + pinIndex: -1, + }, + }, + errors, + }; + } + + const entity: Partial = { + tdd: { + expandedMeasureName, + chartType: preset.timeDimensionChartType + ? FromURLParamTDDChartMap[preset.timeDimensionChartType] + : TDDChart.DEFAULT, + pinIndex: preset.timeDimensionPin ? Number(preset.timeDimensionPin) : -1, + }, + }; + + return { + entity, + errors, + }; +} + +function fromPivotUrlParams( + measures: Map, + dimensions: Map, + preset: V1ExplorePreset, +): { + entity: Partial; + errors: Error[]; +} { + const errors: Error[] = []; + + const mapPivotEntry = (entry: string): PivotChipData | undefined => { + if (entry in FromURLParamTimeDimensionMap) { + const grain = FromURLParamTimeDimensionMap[entry]; + return { + id: grain, + title: TIME_GRAIN[grain]?.label, + type: PivotChipType.Time, + }; + } + + if (measures.has(entry)) { + const m = measures.get(entry)!; + return { + id: entry, + title: m.displayName || m.name || "Unknown", + type: PivotChipType.Measure, + }; + } + + if (dimensions.has(entry)) { + const d = dimensions.get(entry)!; + return { + id: entry, + title: d.displayName || d.name || "Unknown", + type: PivotChipType.Dimension, + }; + } + + errors.push(getSingleFieldError("pivot entry", entry)); + + return undefined; + }; + + let hasSomePivotFields = false; + + const rowDimensions: PivotChipData[] = []; + if (preset.pivotRows) { + preset.pivotRows.forEach((pivotRow) => { + const chip = mapPivotEntry(pivotRow); + if (!chip) return; + rowDimensions.push(chip); + }); + hasSomePivotFields = true; + } + + const colMeasures: PivotChipData[] = []; + const colDimensions: PivotChipData[] = []; + if (preset.pivotCols) { + preset.pivotCols.forEach((pivotRow) => { + const chip = mapPivotEntry(pivotRow); + if (!chip) return; + if (chip.type === PivotChipType.Measure) { + colMeasures.push(chip); + } else { + colDimensions.push(chip); + } + }); + hasSomePivotFields = true; + } + + if (!hasSomePivotFields) { + return { + entity: {}, + errors, + }; + } + + return { + entity: { + pivot: { + active: preset.view === V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, + rows: { + dimension: rowDimensions, + }, + columns: { + measure: colMeasures, + dimension: colDimensions, + }, + // TODO: other fields + expanded: {}, + sorting: [], + columnPage: 1, + rowPage: 1, + enableComparison: true, + activeCell: null, + rowJoinType: "nest", + }, + }, + errors, + }; +} diff --git a/web-common/src/features/dashboards/url-state/convertURLToExplorePreset.ts b/web-common/src/features/dashboards/url-state/convertURLToExplorePreset.ts new file mode 100644 index 00000000000..700b0be0e5e --- /dev/null +++ b/web-common/src/features/dashboards/url-state/convertURLToExplorePreset.ts @@ -0,0 +1,343 @@ +import { base64ToProto } from "@rilldata/web-common/features/dashboards/proto-state/fromProto"; +import { createAndExpression } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; +import { convertLegacyStateToExplorePreset } from "@rilldata/web-common/features/dashboards/url-state/convertLegacyStateToExplorePreset"; +import { + getMultiFieldError, + getSingleFieldError, +} from "@rilldata/web-common/features/dashboards/url-state/error-message-helpers"; +import { convertFilterParamToExpression } from "@rilldata/web-common/features/dashboards/url-state/filters/converters"; +import { + FromURLParamTimeDimensionMap, + FromURLParamViewMap, +} from "@rilldata/web-common/features/dashboards/url-state/mappers"; +import { + getMapFromArray, + getMissingValues, +} from "@rilldata/web-common/lib/arrayUtils"; +import { DashboardState } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; +import { + type MetricsViewSpecDimensionV2, + type MetricsViewSpecMeasureV2, + V1ExploreComparisonMode, + type V1ExplorePreset, + type V1ExploreSpec, + type V1Expression, + type V1MetricsViewSpec, + V1Operation, +} from "@rilldata/web-common/runtime-client"; + +export function convertURLToExplorePreset( + searchParams: URLSearchParams, + metricsView: V1MetricsViewSpec, + explore: V1ExploreSpec, + basePreset: V1ExplorePreset, +) { + const preset: V1ExplorePreset = { + ...basePreset, + }; + const errors: Error[] = []; + + const measures = getMapFromArray( + metricsView.measures?.filter((m) => explore.measures?.includes(m.name!)) ?? + [], + (m) => m.name!, + ); + const dimensions = getMapFromArray( + metricsView.dimensions?.filter((d) => + explore.dimensions?.includes(d.name!), + ) ?? [], + (d) => d.name!, + ); + + // Support legacy dashboard param. + // This will be applied 1st so that any newer params added can be applied as well. + if (searchParams.has("state")) { + const legacyState = searchParams.get("state") as string; + const { preset: presetFromLegacyState, errors: errorsFromLegacyState } = + fromLegacyStateUrlParam(legacyState, metricsView, explore, basePreset); + Object.assign(preset, presetFromLegacyState); + errors.push(...errorsFromLegacyState); + } + + if (searchParams.has("vw")) { + preset.view = FromURLParamViewMap[searchParams.get("vw") as string]; + } + + if (searchParams.has("f")) { + const { expr, errors: filterErrors } = fromFilterUrlParam( + searchParams.get("f") as string, + ); + if (filterErrors) errors.push(...filterErrors); + if (expr) preset.where = expr; + } + + const { preset: trPreset, errors: trErrors } = fromTimeRangesParams( + searchParams, + dimensions, + ); + Object.assign(preset, trPreset); + errors.push(...trErrors); + + const { preset: ovPreset, errors: ovErrors } = fromOverviewUrlParams( + searchParams, + measures, + dimensions, + explore, + ); + Object.assign(preset, ovPreset); + errors.push(...ovErrors); + + const { preset: tddPreset, errors: tddErrors } = fromTimeDimensionUrlParams( + searchParams, + measures, + ); + Object.assign(preset, tddPreset); + errors.push(...tddErrors); + + const { preset: pivotPreset, errors: pivotErrors } = fromPivotUrlParams( + searchParams, + measures, + dimensions, + ); + Object.assign(preset, pivotPreset); + errors.push(...pivotErrors); + + return { preset, errors }; +} + +function fromLegacyStateUrlParam( + legacyState: string, + metricsView: V1MetricsViewSpec, + explore: V1ExploreSpec, + basePreset: V1ExplorePreset, +) { + try { + legacyState = legacyState.includes("%") + ? decodeURIComponent(legacyState) + : legacyState; + const legacyDashboardState = DashboardState.fromBinary( + base64ToProto(legacyState), + ); + + return convertLegacyStateToExplorePreset( + legacyDashboardState, + metricsView, + explore, + basePreset, + ); + } catch (e) { + return { + preset: {}, + errors: [e], // TODO: parse and show meaningful error + }; + } +} + +function fromFilterUrlParam(filter: string): { + expr?: V1Expression; + errors?: Error[]; +} { + try { + let expr = convertFilterParamToExpression(filter); + // if root is not AND/OR then add AND + if ( + expr?.cond?.op !== V1Operation.OPERATION_AND && + expr?.cond?.op !== V1Operation.OPERATION_OR + ) { + expr = createAndExpression([expr]); + } + return { expr }; + } catch (e) { + return { errors: [e] }; + } +} + +function fromTimeRangesParams( + searchParams: URLSearchParams, + dimensions: Map, +) { + const preset: V1ExplorePreset = {}; + const errors: Error[] = []; + + if (searchParams.has("tr")) { + preset.timeRange = searchParams.get("tr") as string; + // TODO: parse and return errors + } + if (searchParams.has("tz")) { + preset.timezone = searchParams.get("tz") as string; + } + + if (searchParams.has("ctr")) { + preset.compareTimeRange = searchParams.get("ctr") as string; + preset.comparisonMode ??= + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_TIME; + // TODO: parse and return errors + } + if (searchParams.has("cd")) { + const comparisonDimension = searchParams.get("cd") as string; + // unsetting a default from url + if (comparisonDimension === "") { + preset.comparisonDimension = ""; + preset.comparisonMode ??= + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_NONE; + } else if (dimensions.has(comparisonDimension)) { + preset.comparisonDimension = comparisonDimension; + preset.comparisonMode ??= + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_DIMENSION; + } else { + errors.push( + getSingleFieldError("compare dimension", comparisonDimension), + ); + } + } + + // TODO: grain from time range ? + + return { preset, errors }; +} + +function fromOverviewUrlParams( + searchParams: URLSearchParams, + measures: Map, + dimensions: Map, + explore: V1ExploreSpec, +) { + const preset: V1ExplorePreset = {}; + const errors: Error[] = []; + + if (searchParams.has("o.m")) { + const mes = searchParams.get("o.m") as string; + if (mes === "*") { + preset.measures = explore.measures ?? []; + } else { + const selectedMeasures = mes.split(",").filter((m) => measures.has(m)); + preset.measures = selectedMeasures; + const missingMeasures = getMissingValues( + mes.split(","), + selectedMeasures, + ); + if (missingMeasures.length) { + errors.push(getMultiFieldError("measure", missingMeasures)); + } + } + } + + if (searchParams.has("o.d")) { + const dims = searchParams.get("o.d") as string; + if (dims === "*") { + preset.dimensions = explore.dimensions ?? []; + } else { + const selectedDimensions = dims + .split(",") + .filter((d) => dimensions.has(d)); + preset.dimensions = selectedDimensions; + const missingDimensions = getMissingValues( + dims.split(","), + selectedDimensions, + ); + if (missingDimensions.length) { + errors.push(getMultiFieldError("dimension", missingDimensions)); + } + } + } + + if (searchParams.has("o.sb")) { + const sortBy = searchParams.get("o.sb") as string; + if (measures.has(sortBy)) { + preset.overviewSortBy = sortBy; + } else { + errors.push(getSingleFieldError("sort by measure", sortBy)); + } + } + + if (searchParams.has("o.sd")) { + preset.overviewSortAsc = (searchParams.get("o.sd") as string) === "ASC"; + } + + if (searchParams.has("o.ed")) { + const dim = searchParams.get("o.ed") as string; + if ( + dimensions.has(dim) || + // we are unsetting from a default preset + dim === "" + ) { + preset.overviewExpandedDimension = dim; + } else { + errors.push(getSingleFieldError("expanded dimension", dim)); + } + } + + return { preset, errors }; +} + +function fromTimeDimensionUrlParams( + searchParams: URLSearchParams, + measures: Map, +) { + const preset: V1ExplorePreset = {}; + const errors: Error[] = []; + + if (searchParams.has("tdd.m")) { + const mes = searchParams.get("tdd.m") as string; + if ( + measures.has(mes) || + // we are unsetting from a default preset + mes === "" + ) { + preset.timeDimensionMeasure = mes; + } else { + errors.push(getSingleFieldError("expanded measure", mes)); + } + } + if (searchParams.has("tdd.ct")) { + preset.timeDimensionChartType = searchParams.get("tdd.ct") as string; + } + if (searchParams.has("tdd.p")) { + preset.timeDimensionPin = true; + } + + return { + preset, + errors, + }; +} + +function fromPivotUrlParams( + searchParams: URLSearchParams, + measures: Map, + dimensions: Map, +) { + const preset: V1ExplorePreset = {}; + const errors: Error[] = []; + + if (searchParams.has("p.r")) { + const rows = (searchParams.get("p.r") as string).split(","); + const validRows = rows.filter( + (r) => dimensions.has(r) || r in FromURLParamTimeDimensionMap, + ); + preset.pivotRows = validRows; + const missingRows = getMissingValues(validRows, rows); + if (missingRows.length) { + errors.push(getMultiFieldError("pivot row", missingRows)); + } + } + + if (searchParams.has("p.c")) { + const cols = (searchParams.get("p.c") as string).split(","); + const validCols = cols.filter( + (c) => + dimensions.has(c) || + measures.has(c) || + c in FromURLParamTimeDimensionMap, + ); + preset.pivotCols = validCols; + const missingCols = getMissingValues(validCols, cols); + if (missingCols.length) { + errors.push(getMultiFieldError("pivot column", missingCols)); + } + } + + // TODO: other fields + + return { preset, errors }; +} diff --git a/web-common/src/features/dashboards/url-state/defaults.ts b/web-common/src/features/dashboards/url-state/defaults.ts new file mode 100644 index 00000000000..3e1ab01026d --- /dev/null +++ b/web-common/src/features/dashboards/url-state/defaults.ts @@ -0,0 +1,20 @@ +import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; +import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; +import { + V1ExploreComparisonMode, + V1ExploreWebView, +} from "@rilldata/web-common/runtime-client"; + +export const URLStateDefaultView = + V1ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW; + +export const URLStateDefaultTimeRange = "inf"; +export const URLStateDefaultTimezone = "UTC"; + +export const URLStateDefaultCompareMode = + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_NONE; + +export const URLStateDefaultSortDirection = SortDirection.DESCENDING; + +export const URLStateDefaultTDDChartType = TDDChart.DEFAULT; +export const ExplorePresetDefaultChartType = "timeseries"; diff --git a/web-common/src/features/dashboards/url-state/error-message-helpers.ts b/web-common/src/features/dashboards/url-state/error-message-helpers.ts new file mode 100644 index 00000000000..2eb45bb6c8c --- /dev/null +++ b/web-common/src/features/dashboards/url-state/error-message-helpers.ts @@ -0,0 +1,10 @@ +export function getSingleFieldError(fieldLabel: string, field: string) { + return new Error(`Select ${fieldLabel}: "${field}" is not valid.`); +} + +export function getMultiFieldError(fieldLabel: string, fields: string[]) { + const plural = fields.length > 1; + return new Error( + `Selected ${fieldLabel}${plural ? "s" : ""}: "${fields.join(",")}" ${plural ? "are" : "is"} not valid.`, + ); +} diff --git a/web-common/src/features/dashboards/url-state/filters/converters.ts b/web-common/src/features/dashboards/url-state/filters/converters.ts index a21410f3c3f..edb5a8da866 100644 --- a/web-common/src/features/dashboards/url-state/filters/converters.ts +++ b/web-common/src/features/dashboards/url-state/filters/converters.ts @@ -1,3 +1,8 @@ +import { BinaryOperationReverseMap } from "@rilldata/web-common/features/dashboards/url-state/filters/post-processors"; +import { + type V1Expression, + V1Operation, +} from "@rilldata/web-common/runtime-client"; import grammar from "./expression.cjs"; import nearley from "nearley"; @@ -5,7 +10,36 @@ const compiledGrammar = nearley.Grammar.fromCompiled(grammar); export function convertFilterParamToExpression(filter: string) { const parser = new nearley.Parser(compiledGrammar); parser.feed(filter); - return parser.results[0]; + return parser.results[0] as V1Expression; +} + +export function convertExpressionToFilterParam( + expr: V1Expression, + depth = 0, +): string { + if (!expr) return ""; + + if (expr.val) { + if (typeof expr.val === "string") { + return `'${expr.val}'`; + } + return expr.val + ""; + } + + if (expr.ident) return expr.ident; + + switch (expr.cond?.op) { + case V1Operation.OPERATION_AND: + case V1Operation.OPERATION_OR: + return convertJoinerExpressionToFilterParam(expr, depth); + + case V1Operation.OPERATION_IN: + case V1Operation.OPERATION_NIN: + return convertInExpressionToFilterParam(expr, depth); + + default: + return convertBinaryExpressionToFilterParam(expr, depth); + } } export function stripParserError(err: Error) { @@ -14,3 +48,63 @@ export function stripParserError(err: Error) { err.message.indexOf("Instead, I was expecting") - 1, ); } + +function convertJoinerExpressionToFilterParam( + expr: V1Expression, + depth: number, +) { + const joiner = expr.cond?.op === V1Operation.OPERATION_AND ? " AND " : " OR "; + + const parts = expr.cond?.exprs + ?.map((e) => convertExpressionToFilterParam(e, depth + 1)) + .filter(Boolean); + if (!parts?.length) return ""; + const exprParam = parts.join(joiner); + + if (depth === 0) { + return exprParam; + } + return `(${exprParam})`; +} + +function convertInExpressionToFilterParam(expr: V1Expression, depth: number) { + if (!expr.cond?.exprs?.length) return ""; + const joiner = expr.cond?.op === V1Operation.OPERATION_IN ? "IN" : "NIN"; + + const column = expr.cond.exprs[0]?.ident; + if (!column) return ""; + + if (expr.cond.exprs[1]?.subquery?.having) { + // TODO: support `NIN ` + const having = convertExpressionToFilterParam( + expr.cond.exprs[1]?.subquery?.having, + 0, + ); + if (having) return `${column} having (${having})`; + } + + if (expr.cond.exprs.length > 1) { + const vals = expr.cond.exprs + .slice(1) + .map((e) => convertExpressionToFilterParam(e, depth + 1)) + .filter(Boolean); + return `${column} ${joiner} (${vals.join(",")})`; + } + + return ""; +} + +function convertBinaryExpressionToFilterParam( + expr: V1Expression, + depth: number, +) { + if (!expr.cond?.op || !(expr.cond?.op in BinaryOperationReverseMap)) + return ""; + const op = BinaryOperationReverseMap[expr.cond.op]; + if (!expr.cond?.exprs?.length) return ""; + const left = convertExpressionToFilterParam(expr.cond.exprs[0], depth + 1); + const right = convertExpressionToFilterParam(expr.cond.exprs[1], depth + 1); + if (!left || !right) return ""; + + return `${left} ${op} ${right}`; +} diff --git a/web-common/src/features/dashboards/url-state/filters/expression.spec.ts b/web-common/src/features/dashboards/url-state/filters/expression.spec.ts index fe390db00a1..945180c3ef7 100644 --- a/web-common/src/features/dashboards/url-state/filters/expression.spec.ts +++ b/web-common/src/features/dashboards/url-state/filters/expression.spec.ts @@ -17,6 +17,10 @@ import nearley from "nearley"; describe("expression", () => { describe("positive cases", () => { const Cases = [ + { + expr: "country IN ('US','IN')", + expected_expression: createInExpression("country", ["US", "IN"]), + }, { expr: "country IN ('US','IN') and state = 'ABC'", expected_expression: createAndExpression([ @@ -73,7 +77,7 @@ describe("expression", () => { // assert that there is only match. this ensures unambiguous grammar. expect(parser.results).length(1); - expect(convertFilterParamToExpression(expr)).toEqual( + expect(convertFilterParamToExpression(expr)).to.deep.eq( expected_expression, ); }); diff --git a/web-common/src/features/dashboards/url-state/filters/post-processors.ts b/web-common/src/features/dashboards/url-state/filters/post-processors.ts index a3ac991b18e..62340cf747b 100644 --- a/web-common/src/features/dashboards/url-state/filters/post-processors.ts +++ b/web-common/src/features/dashboards/url-state/filters/post-processors.ts @@ -6,9 +6,10 @@ import { createSubQueryExpression, getAllIdentifiers, } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; +import { reverseMap } from "@rilldata/web-common/features/dashboards/url-state/mappers"; import { V1Operation } from "@rilldata/web-common/runtime-client"; -const BinaryOperationMap = { +const BinaryOperationMap: Record = { "=": V1Operation.OPERATION_EQ, "!=": V1Operation.OPERATION_NEQ, ">": V1Operation.OPERATION_GT, @@ -16,6 +17,7 @@ const BinaryOperationMap = { "<": V1Operation.OPERATION_LT, "<=": V1Operation.OPERATION_LTE, }; +export const BinaryOperationReverseMap = reverseMap(BinaryOperationMap); export const binaryPostprocessor = ([left, _1, op, _2, right]) => createBinaryExpression(left, BinaryOperationMap[op], right); diff --git a/web-common/src/features/dashboards/url-state/getBasePreset.ts b/web-common/src/features/dashboards/url-state/getBasePreset.ts new file mode 100644 index 00000000000..1c54e40a594 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/getBasePreset.ts @@ -0,0 +1,51 @@ +import { createAndExpression } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; +import { + URLStateDefaultTDDChartType, + URLStateDefaultTimeRange, + URLStateDefaultTimezone, +} from "@rilldata/web-common/features/dashboards/url-state/defaults"; +import { ToURLParamTDDChartMap } from "@rilldata/web-common/features/dashboards/url-state/mappers"; +import type { LocalUserPreferences } from "@rilldata/web-common/features/dashboards/user-preferences"; +import { + V1ExploreComparisonMode, + type V1ExplorePreset, + type V1ExploreSpec, + V1ExploreWebView, +} from "@rilldata/web-common/runtime-client"; + +export function getBasePreset( + explore: V1ExploreSpec, + preferences: LocalUserPreferences, +) { + const basePreset: V1ExplorePreset = { + view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW, + where: createAndExpression([]), + + measures: explore.measures, + dimensions: explore.dimensions, + + timeRange: URLStateDefaultTimeRange, + timezone: preferences.timeZone ?? URLStateDefaultTimezone, + timeGrain: "", + comparisonMode: V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_NONE, + compareTimeRange: "", + comparisonDimension: "", + + overviewSortBy: explore.measures?.[0], + overviewSortAsc: false, + overviewExpandedDimension: "", + + timeDimensionMeasure: "", + timeDimensionChartType: ToURLParamTDDChartMap[URLStateDefaultTDDChartType], + timeDimensionPin: false, + + pivotCols: [], + pivotRows: [], + pivotSortBy: "", + pivotSortAsc: false, + + ...(explore.defaultPreset ?? {}), + }; + + return basePreset; +} diff --git a/web-common/src/features/dashboards/url-state/legacyMappers.ts b/web-common/src/features/dashboards/url-state/legacyMappers.ts new file mode 100644 index 00000000000..c2944614102 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/legacyMappers.ts @@ -0,0 +1,16 @@ +import { ExplorePresetDefaultChartType } from "@rilldata/web-common/features/dashboards/url-state/defaults"; + +const LegacyCharTypeToPresetChartType: Record = { + default: ExplorePresetDefaultChartType, + grouped_bar: "bar", + stacked_bar: "stacked_bar", + stacked_area: "stacked_area", +}; +export function mapLegacyChartType(chartType: string | undefined) { + if (!chartType) { + return ExplorePresetDefaultChartType; + } + return ( + LegacyCharTypeToPresetChartType[chartType] ?? ExplorePresetDefaultChartType + ); +} diff --git a/web-common/src/features/dashboards/url-state/mappers.ts b/web-common/src/features/dashboards/url-state/mappers.ts new file mode 100644 index 00000000000..1396ceb1a29 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/mappers.ts @@ -0,0 +1,59 @@ +import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; +import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; +import { + V1ExploreWebView, + V1TimeGrain, +} from "@rilldata/web-common/runtime-client"; + +export const FromURLParamViewMap: Record = { + overview: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW, + pivot: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, + time_dimension: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_TIME_DIMENSION, +}; +export const ToURLParamViewMap = reverseMap(FromURLParamViewMap); + +export const FromActivePageMap: Record< + DashboardState_ActivePage, + V1ExploreWebView +> = { + [DashboardState_ActivePage.UNSPECIFIED]: + V1ExploreWebView.EXPLORE_ACTIVE_PAGE_UNSPECIFIED, + [DashboardState_ActivePage.DEFAULT]: + V1ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW, + [DashboardState_ActivePage.PIVOT]: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, + [DashboardState_ActivePage.DIMENSION_TABLE]: + V1ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW, + [DashboardState_ActivePage.TIME_DIMENSIONAL_DETAIL]: + V1ExploreWebView.EXPLORE_ACTIVE_PAGE_TIME_DIMENSION, +}; +export const ToActivePageViewMap = reverseMap(FromActivePageMap); +ToActivePageViewMap[V1ExploreWebView.EXPLORE_ACTIVE_PAGE_OVERVIEW] = + DashboardState_ActivePage.DEFAULT; + +export const FromURLParamTimeDimensionMap: Record = { + "time.hour": V1TimeGrain.TIME_GRAIN_HOUR, + "time.day": V1TimeGrain.TIME_GRAIN_DAY, + "time.month": V1TimeGrain.TIME_GRAIN_MONTH, +}; +export const ToURLParamTimeDimensionMap = reverseMap( + FromURLParamTimeDimensionMap, +); + +export const FromURLParamTDDChartMap: Record = { + timeseries: TDDChart.DEFAULT, + bar: TDDChart.GROUPED_BAR, + stacked_bar: TDDChart.STACKED_BAR, + stacked_area: TDDChart.STACKED_AREA, +}; +export const ToURLParamTDDChartMap = reverseMap(FromURLParamTDDChartMap); + +export function reverseMap< + K extends string | number, + V extends string | number, +>(map: Partial>): Partial> { + const revMap = {} as Partial>; + for (const k in map) { + revMap[map[k] as string | number] = k; + } + return revMap; +} diff --git a/web-common/src/features/dashboards/url-state/sparse-preset.spec.ts b/web-common/src/features/dashboards/url-state/sparse-preset.spec.ts new file mode 100644 index 00000000000..546cd48a6f7 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/sparse-preset.spec.ts @@ -0,0 +1,183 @@ +import { getDefaultMetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; +import { metricsExplorerStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { + AD_BIDS_BASE_PRESET, + AD_BIDS_EXPLORE_INIT, + AD_BIDS_EXPLORE_NAME, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + AD_BIDS_TIME_RANGE_SUMMARY, +} from "@rilldata/web-common/features/dashboards/stores/test-data/data"; +import { + getPartialDashboard, + resetDashboardStore, +} from "@rilldata/web-common/features/dashboards/stores/test-data/helpers"; +import { + AD_BIDS_APPLY_BP_MEASURE_FILTER, + AD_BIDS_APPLY_DOM_DIMENSION_FILTER, + AD_BIDS_APPLY_IMP_MEASURE_FILTER, + AD_BIDS_APPLY_PUB_DIMENSION_FILTER, + AD_BIDS_OPEN_BP_TDD, + AD_BIDS_OPEN_DOM_BP_PIVOT, + AD_BIDS_OPEN_DOM_DIMENSION_TABLE, + AD_BIDS_OPEN_IMP_TDD, + AD_BIDS_OPEN_PUB_DIMENSION_TABLE, + AD_BIDS_OPEN_PUB_IMP_PIVOT, + AD_BIDS_REMOVE_IMP_MEASURE_FILTER, + AD_BIDS_REMOVE_PUB_DIMENSION_FILTER, + AD_BIDS_SET_P4W_TIME_RANGE_FILTER, + AD_BIDS_SET_P7D_TIME_RANGE_FILTER, + applyMutationsToDashboard, + type TestDashboardMutation, +} from "@rilldata/web-common/features/dashboards/stores/test-data/store-mutations"; +import { convertMetricsExploreToPreset } from "@rilldata/web-common/features/dashboards/url-state/convertMetricsExploreToPreset"; +import { + convertPresetToMetricsExplore, + convertURLToMetricsExplore, +} from "@rilldata/web-common/features/dashboards/url-state/convertPresetToMetricsExplore"; +import { cleanMetricsExplore } from "@rilldata/web-common/features/dashboards/url-state/url-state.spec"; +import { + getLocalUserPreferences, + initLocalUserPreferenceStore, +} from "@rilldata/web-common/features/dashboards/user-preferences"; +import { deepClone } from "@vitest/utils"; +import { get } from "svelte/store"; +import { beforeAll, beforeEach, describe, expect, it } from "vitest"; + +const TestCases: { + title: string; + mutations: TestDashboardMutation[]; + keys: (keyof MetricsExplorerEntity)[]; +}[] = [ + { + title: "filters", + mutations: [ + AD_BIDS_APPLY_PUB_DIMENSION_FILTER, + AD_BIDS_APPLY_IMP_MEASURE_FILTER, + ], + keys: ["whereFilter", "dimensionThresholdFilters"], + }, + { + title: "time range", + mutations: [AD_BIDS_SET_P7D_TIME_RANGE_FILTER], + keys: ["selectedTimeRange", "selectedComparisonTimeRange"], + }, + { + title: "dimension table", + mutations: [AD_BIDS_OPEN_PUB_DIMENSION_TABLE], + keys: ["activePage", "selectedDimensionName"], + }, + { + title: "time dimension details", + mutations: [AD_BIDS_OPEN_IMP_TDD], + keys: ["activePage", "tdd"], + }, + { + title: "pivot", + mutations: [AD_BIDS_OPEN_PUB_IMP_PIVOT], + keys: ["activePage", "pivot"], + }, +]; +// list of mutations that reverts all mutations from the above test cases +const TestCasesOppositeMutations = [ + AD_BIDS_REMOVE_PUB_DIMENSION_FILTER, + AD_BIDS_APPLY_DOM_DIMENSION_FILTER, + AD_BIDS_REMOVE_IMP_MEASURE_FILTER, + AD_BIDS_APPLY_BP_MEASURE_FILTER, + AD_BIDS_SET_P4W_TIME_RANGE_FILTER, + AD_BIDS_OPEN_DOM_DIMENSION_TABLE, + AD_BIDS_OPEN_BP_TDD, + AD_BIDS_OPEN_DOM_BP_PIVOT, +]; + +describe("sparse preset", () => { + beforeAll(() => { + initLocalUserPreferenceStore(AD_BIDS_EXPLORE_NAME); + }); + + beforeEach(() => { + resetDashboardStore(); + getLocalUserPreferences().updateTimeZone("UTC"); + localStorage.setItem( + `${AD_BIDS_EXPLORE_NAME}-userPreference`, + `{"timezone":"UTC"}`, + ); + }); + + describe("should reset dashboard store", () => { + for (const { title, mutations } of TestCases) { + it(`from ${title}`, () => { + const initEntity = getDefaultMetricsExplorerEntity( + AD_BIDS_EXPLORE_NAME, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + AD_BIDS_EXPLORE_INIT, + AD_BIDS_TIME_RANGE_SUMMARY, + ); + cleanMetricsExplore(initEntity); + applyMutationsToDashboard(AD_BIDS_EXPLORE_NAME, mutations); + + const url = new URL("http://localhost"); + // get the entity from no param url + const { entity: entityFromUrl } = convertURLToMetricsExplore( + url.searchParams, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + AD_BIDS_EXPLORE_INIT, + AD_BIDS_BASE_PRESET, + ); + + expect(entityFromUrl).toEqual(initEntity); + }); + } + }); + + describe("should reset partial dashboard store", () => { + for (const { title, mutations, keys } of TestCases) { + it(`to ${title}`, () => { + // apply the mutations for the test + applyMutationsToDashboard(AD_BIDS_EXPLORE_NAME, mutations); + + // get and store the partial from the current state + const partialEntity = getPartialDashboard(AD_BIDS_EXPLORE_NAME, keys); + const partialPreset = convertMetricsExploreToPreset( + partialEntity, + AD_BIDS_EXPLORE_INIT, + ); + // convert to partial preset and back to entity + const { entity: partialEntityFromPreset } = + convertPresetToMetricsExplore( + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + AD_BIDS_EXPLORE_INIT, + partialPreset, + ); + // remove parts not in the state + cleanMetricsExplore(partialEntity); + // assert that the partial from entity match the one from state + expect(partialEntityFromPreset).toEqual(partialEntity); + + // apply mutations to reverse the previous mutations + applyMutationsToDashboard( + AD_BIDS_EXPLORE_NAME, + TestCasesOppositeMutations, + ); + + // merge partial entity from preset to current state + metricsExplorerStore.mergePartialExplorerEntity( + AD_BIDS_EXPLORE_NAME, + partialEntityFromPreset, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + ); + + // get and store the current state + const curEntity = deepClone( + get(metricsExplorerStore).entities[AD_BIDS_EXPLORE_NAME], + ); + cleanMetricsExplore(curEntity); + // assert that the current entity is not changed when the partial entity selected is applied to it. + expect(curEntity).toEqual({ + ...curEntity, + ...partialEntity, + }); + }); + } + }); +}); diff --git a/web-common/src/features/dashboards/url-state/url-state.spec.ts b/web-common/src/features/dashboards/url-state/url-state.spec.ts new file mode 100644 index 00000000000..e1cefc06830 --- /dev/null +++ b/web-common/src/features/dashboards/url-state/url-state.spec.ts @@ -0,0 +1,511 @@ +import { SortDirection } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; +import { getProtoFromDashboardState } from "@rilldata/web-common/features/dashboards/proto-state/toProto"; +import { getDefaultMetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; +import { + createAndExpression, + createInExpression, +} from "@rilldata/web-common/features/dashboards/stores/filter-utils"; +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { + AD_BIDS_BID_PRICE_MEASURE, + AD_BIDS_DOMAIN_DIMENSION, + AD_BIDS_EXPLORE_INIT, + AD_BIDS_EXPLORE_NAME, + AD_BIDS_IMPRESSIONS_MEASURE, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + AD_BIDS_PUBLISHER_DIMENSION, + AD_BIDS_TIME_RANGE_SUMMARY, +} from "@rilldata/web-common/features/dashboards/stores/test-data/data"; +import { getPivotedPartialDashboard } from "@rilldata/web-common/features/dashboards/stores/test-data/helpers"; +import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; +import { convertURLToMetricsExplore } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToMetricsExplore"; +import { convertMetricsEntityToURLSearchParams } from "@rilldata/web-common/features/dashboards/url-state/convertMetricsEntityToURLSearchParams"; +import { getBasePreset } from "@rilldata/web-common/features/dashboards/url-state/getBasePreset"; +import { + getLocalUserPreferences, + initLocalUserPreferenceStore, +} from "@rilldata/web-common/features/dashboards/user-preferences"; +import { + type DashboardTimeControls, + TimeRangePreset, +} from "@rilldata/web-common/lib/time/types"; +import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; +import { + type V1ExplorePreset, + type V1ExploreSpec, + V1ExploreWebView, + V1TimeGrain, +} from "@rilldata/web-common/runtime-client"; +import { beforeAll, beforeEach, describe, expect, it } from "vitest"; + +const TestCases: { + title: string; + entity: Partial; + preset?: V1ExplorePreset; + expectedUrl: string; +}[] = [ + { + title: "filter", + entity: { + whereFilter: createAndExpression([ + createInExpression(AD_BIDS_PUBLISHER_DIMENSION, ["Yahoo"]), + ]), + dimensionThresholdFilters: [], + }, + expectedUrl: "http://localhost/?f=publisher+IN+%28%27Yahoo%27%29", + }, + + { + title: "Time range without preset", + entity: { + selectedTimeRange: { + name: TimeRangePreset.LAST_4_WEEKS, + } as DashboardTimeControls, + selectedTimezone: "Asia/Kathmandu", + }, + expectedUrl: "http://localhost/?tr=P4W&tz=Asia%2FKathmandu", + }, + { + title: "Time range with preset and state matching preset", + entity: { + selectedTimeRange: { + name: TimeRangePreset.LAST_7_DAYS, + } as DashboardTimeControls, + selectedTimezone: "Asia/Kathmandu", + }, + preset: { + timeRange: "P7D", + timezone: "Asia/Kathmandu", + }, + expectedUrl: "http://localhost/", + }, + { + title: "Time range with preset and state not matching preset", + entity: { + selectedTimeRange: { + name: TimeRangePreset.LAST_4_WEEKS, + } as DashboardTimeControls, + selectedTimezone: "America/Los_Angeles", + }, + preset: { + timeRange: "P7D", + timezone: "Asia/Kathmandu", + }, + expectedUrl: "http://localhost/?tr=P4W&tz=America%2FLos_Angeles", + }, + + { + title: + "Measures/dimensions visibility with no preset and partially visible measures/dimensions in state", + entity: { + visibleMeasureKeys: new Set([AD_BIDS_IMPRESSIONS_MEASURE]), + allMeasuresVisible: false, + visibleDimensionKeys: new Set([AD_BIDS_PUBLISHER_DIMENSION]), + allDimensionsVisible: false, + }, + expectedUrl: "http://localhost/?o.m=impressions&o.d=publisher", + }, + { + title: + "Measures/dimensions visibility with no preset and all measures/dimensions visible in state", + entity: { + visibleMeasureKeys: new Set([ + AD_BIDS_IMPRESSIONS_MEASURE, + AD_BIDS_BID_PRICE_MEASURE, + ]), + allMeasuresVisible: true, + visibleDimensionKeys: new Set([ + AD_BIDS_PUBLISHER_DIMENSION, + AD_BIDS_DOMAIN_DIMENSION, + ]), + allDimensionsVisible: true, + }, + expectedUrl: "http://localhost/", + }, + { + title: + "Measures/dimensions visibility with preset and partially visible measures/dimensions in state matching preset", + entity: { + visibleMeasureKeys: new Set([AD_BIDS_IMPRESSIONS_MEASURE]), + allMeasuresVisible: false, + visibleDimensionKeys: new Set([AD_BIDS_PUBLISHER_DIMENSION]), + allDimensionsVisible: false, + }, + preset: { + measures: [AD_BIDS_IMPRESSIONS_MEASURE], + dimensions: [AD_BIDS_PUBLISHER_DIMENSION], + }, + expectedUrl: "http://localhost/", + }, + { + title: + "Measures/dimensions visibility with preset and all measures/dimensions visible in state not matching preset", + entity: { + visibleMeasureKeys: new Set([ + AD_BIDS_IMPRESSIONS_MEASURE, + AD_BIDS_BID_PRICE_MEASURE, + ]), + allMeasuresVisible: true, + visibleDimensionKeys: new Set([ + AD_BIDS_PUBLISHER_DIMENSION, + AD_BIDS_DOMAIN_DIMENSION, + ]), + allDimensionsVisible: true, + }, + preset: { + measures: [AD_BIDS_IMPRESSIONS_MEASURE], + dimensions: [AD_BIDS_PUBLISHER_DIMENSION], + }, + expectedUrl: "http://localhost/?o.m=*&o.d=*", + }, + + { + title: + "Leaderboard configs with no preset and leaderboard sort measure in state different than default", + entity: { + leaderboardMeasureName: AD_BIDS_BID_PRICE_MEASURE, + sortDirection: SortDirection.ASCENDING, + }, + expectedUrl: "http://localhost/?o.sb=bid_price&o.sd=ASC", + }, + { + title: + "Leaderboard configs with no preset and leaderboard sort measure in state same as default", + entity: { + leaderboardMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, + sortDirection: SortDirection.DESCENDING, + }, + expectedUrl: "http://localhost/", + }, + { + title: + "Leaderboard configs with preset and leaderboard sort measure in state same as preset", + entity: { + leaderboardMeasureName: AD_BIDS_BID_PRICE_MEASURE, + sortDirection: SortDirection.ASCENDING, + }, + preset: { + overviewSortBy: AD_BIDS_BID_PRICE_MEASURE, + overviewSortAsc: true, + }, + expectedUrl: "http://localhost/", + }, + { + title: + "Leaderboard configs with preset and leaderboard sort measure in state different than preset", + entity: { + leaderboardMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, + sortDirection: SortDirection.DESCENDING, + }, + preset: { + overviewSortBy: AD_BIDS_BID_PRICE_MEASURE, + overviewSortAsc: true, + }, + expectedUrl: "http://localhost/?o.sb=impressions&o.sd=DESC", + }, + + { + title: "Dimension table with no preset and dimension table active in state", + entity: { + activePage: DashboardState_ActivePage.DIMENSION_TABLE, + selectedDimensionName: AD_BIDS_PUBLISHER_DIMENSION, + }, + expectedUrl: "http://localhost/?o.ed=publisher", + }, + { + title: + "Dimension table with preset and with dimension table in state same as preset", + entity: { + activePage: DashboardState_ActivePage.DIMENSION_TABLE, + selectedDimensionName: AD_BIDS_DOMAIN_DIMENSION, + }, + preset: { + overviewExpandedDimension: AD_BIDS_DOMAIN_DIMENSION, + }, + expectedUrl: "http://localhost/", + }, + { + title: + "Dimension table with preset and with dimension table in state different than preset", + entity: { + activePage: DashboardState_ActivePage.DIMENSION_TABLE, + selectedDimensionName: AD_BIDS_PUBLISHER_DIMENSION, + }, + preset: { + overviewExpandedDimension: AD_BIDS_DOMAIN_DIMENSION, + }, + expectedUrl: "http://localhost/?o.ed=publisher", + }, + { + title: + "Dimension table with preset and with no dimension table in state different than preset", + entity: { + activePage: DashboardState_ActivePage.DEFAULT, + selectedDimensionName: "", + }, + preset: { + overviewExpandedDimension: AD_BIDS_DOMAIN_DIMENSION, + }, + expectedUrl: "http://localhost/?o.ed=", + }, + + { + title: + "Time dimensional details with no preset and has time dimensional details in state", + entity: { + activePage: DashboardState_ActivePage.TIME_DIMENSIONAL_DETAIL, + tdd: { + expandedMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, + chartType: TDDChart.STACKED_BAR, + pinIndex: -1, + }, + }, + expectedUrl: + "http://localhost/?vw=time_dimension&tdd.m=impressions&tdd.ct=stacked_bar", + }, + { + title: + "Time dimensional details with preset and has time dimensional details in state same as presets", + entity: { + activePage: DashboardState_ActivePage.TIME_DIMENSIONAL_DETAIL, + tdd: { + expandedMeasureName: AD_BIDS_IMPRESSIONS_MEASURE, + chartType: TDDChart.STACKED_BAR, + pinIndex: -1, + }, + }, + preset: { + view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_TIME_DIMENSION, + timeDimensionMeasure: AD_BIDS_IMPRESSIONS_MEASURE, + timeDimensionChartType: "stacked_bar", + }, + expectedUrl: "http://localhost/", + }, + { + title: + "Time dimensional details with preset and has time dimensional details in state different than presets", + entity: { + activePage: DashboardState_ActivePage.DEFAULT, + tdd: { + expandedMeasureName: "", + chartType: TDDChart.DEFAULT, + pinIndex: -1, + }, + }, + preset: { + view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_TIME_DIMENSION, + timeDimensionMeasure: AD_BIDS_IMPRESSIONS_MEASURE, + timeDimensionChartType: "stacked_bar", + }, + expectedUrl: "http://localhost/?vw=overview&tdd.m=&tdd.ct=timeseries", + }, + + { + title: "Pivot with no preset and has pivot in state", + entity: getPivotedPartialDashboard( + [AD_BIDS_PUBLISHER_DIMENSION], + [V1TimeGrain.TIME_GRAIN_HOUR], + [AD_BIDS_IMPRESSIONS_MEASURE], + [AD_BIDS_DOMAIN_DIMENSION], + [V1TimeGrain.TIME_GRAIN_DAY], + ), + expectedUrl: + "http://localhost/?vw=pivot&p.r=publisher%2Ctime.hour&p.c=domain%2Ctime.day%2Cimpressions", + }, + { + title: "Pivot with preset and has pivot in state same as preset", + entity: getPivotedPartialDashboard( + [AD_BIDS_PUBLISHER_DIMENSION], + [V1TimeGrain.TIME_GRAIN_HOUR], + [AD_BIDS_IMPRESSIONS_MEASURE], + [AD_BIDS_DOMAIN_DIMENSION], + [V1TimeGrain.TIME_GRAIN_DAY], + ), + preset: { + view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, + pivotRows: ["publisher", "time.hour"], + pivotCols: ["domain", "time.day", "impressions"], + }, + expectedUrl: "http://localhost/", + }, + { + title: "Pivot with preset and pivot in state different as preset", + entity: getPivotedPartialDashboard( + [AD_BIDS_DOMAIN_DIMENSION], + [V1TimeGrain.TIME_GRAIN_DAY], + [AD_BIDS_IMPRESSIONS_MEASURE], + [], + [], + ), + preset: { + view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, + pivotRows: ["publisher", "time.hour"], + pivotCols: ["domain", "time.day", "impressions"], + }, + expectedUrl: "http://localhost/?p.r=domain%2Ctime.day&p.c=impressions", + }, + { + title: "Pivot with preset and no pivot in state different as preset", + entity: getPivotedPartialDashboard([], [], [], [], []), + preset: { + view: V1ExploreWebView.EXPLORE_ACTIVE_PAGE_PIVOT, + pivotRows: ["publisher", "time.hour"], + pivotCols: ["domain", "time.day", "impressions"], + }, + expectedUrl: "http://localhost/?vw=overview&p.r=&p.c=", + }, +]; + +describe("Human readable URL state", () => { + beforeAll(() => { + initLocalUserPreferenceStore(AD_BIDS_EXPLORE_NAME); + }); + + beforeEach(() => { + getLocalUserPreferences().updateTimeZone("UTC"); + localStorage.setItem( + `${AD_BIDS_EXPLORE_NAME}-userPreference`, + `{"timezone":"UTC"}`, + ); + }); + + describe("Should update url state and restore default state on empty params", () => { + for (const { title, entity, preset, expectedUrl } of TestCases) { + it(title, () => { + const url = new URL("http://localhost"); + const explore: V1ExploreSpec = { + ...AD_BIDS_EXPLORE_INIT, + ...(preset ? { defaultPreset: preset } : {}), + }; + const basePreset = getBasePreset(explore, { + timeZone: "UTC", + }); + const initEntity = getDefaultMetricsExplorerEntity( + AD_BIDS_EXPLORE_NAME, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + explore, + AD_BIDS_TIME_RANGE_SUMMARY, + ); + cleanMetricsExplore(initEntity); + + // load url params with update metrics state + convertMetricsEntityToURLSearchParams( + { + ...initEntity, + ...entity, + }, + url.searchParams, + explore, + basePreset, + ); + + expect(url.toString()).to.eq(expectedUrl); + + // get back the entity from url params + const { entity: entityFromUrl } = convertURLToMetricsExplore( + url.searchParams, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + explore, + basePreset, + ); + + // assert that the entity we got back matches the expected entity + expect(entityFromUrl).toEqual({ + ...initEntity, + ...entity, + }); + + // go back to default url + const defaultUrl = new URL("http://localhost"); + const { entity: entityFromDefaultUrl } = convertURLToMetricsExplore( + defaultUrl.searchParams, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + explore, + basePreset, + ); + + // assert that the entity we got back matches the original + expect(entityFromDefaultUrl).toEqual(initEntity); + }); + } + }); + + describe("Should set correct state for legacy protobuf state and restore default state on empty params", () => { + for (const { title, entity, preset } of TestCases) { + it(title, () => { + const url = new URL("http://localhost"); + const explore: V1ExploreSpec = { + ...AD_BIDS_EXPLORE_INIT, + ...(preset ? { defaultPreset: preset } : {}), + }; + const basePreset = getBasePreset(explore, { + timeZone: "UTC", + }); + const initEntity = getDefaultMetricsExplorerEntity( + AD_BIDS_EXPLORE_NAME, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + explore, + AD_BIDS_TIME_RANGE_SUMMARY, + ); + cleanMetricsExplore(initEntity); + // load url with legacy protobuf state + url.searchParams.set( + "state", + getProtoFromDashboardState({ + ...initEntity, + ...entity, + }), + ); + + // get back the entity from url params + const { entity: entityFromUrl } = convertURLToMetricsExplore( + url.searchParams, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + explore, + basePreset, + ); + // assert that the entity we got back matches the expected entity + expect(entityFromUrl).toEqual({ + ...initEntity, + ...entity, + }); + + // go back to default url + const defaultUrl = new URL("http://localhost"); + const { entity: entityFromDefaultUrl } = convertURLToMetricsExplore( + defaultUrl.searchParams, + AD_BIDS_METRICS_3_MEASURES_DIMENSIONS, + explore, + basePreset, + ); + + // assert that the entity we got back matches the original + expect(entityFromDefaultUrl).toEqual(initEntity); + }); + } + }); +}); + +// cleans up any UI only state from MetricsExplorerEntity +export function cleanMetricsExplore( + metricsExplorerEntity: Partial, +) { + delete metricsExplorerEntity.name; + delete metricsExplorerEntity.dimensionFilterExcludeMode; + delete metricsExplorerEntity.havingFilter; + delete metricsExplorerEntity.temporaryFilterName; + delete metricsExplorerEntity.contextColumnWidths; + delete metricsExplorerEntity.dimensionSearchText; + if (metricsExplorerEntity.selectedTimeRange) { + metricsExplorerEntity.selectedTimeRange = { + name: metricsExplorerEntity.selectedTimeRange?.name ?? "inf", + // TODO: grain + } as DashboardTimeControls; + } + delete metricsExplorerEntity.lastDefinedScrubRange; + + // TODO + delete metricsExplorerEntity.selectedScrubRange; + delete metricsExplorerEntity.leaderboardContextColumn; + delete metricsExplorerEntity.dashboardSortType; +} diff --git a/web-common/src/features/dashboards/user-preferences.ts b/web-common/src/features/dashboards/user-preferences.ts index 06c20677258..a34ec679aea 100644 --- a/web-common/src/features/dashboards/user-preferences.ts +++ b/web-common/src/features/dashboards/user-preferences.ts @@ -1,6 +1,6 @@ import { localStorageStore } from "@rilldata/web-common/lib/store-utils"; import { getLocalIANA } from "@rilldata/web-common/lib/time/timezone"; -import type { Readable, Writable } from "svelte/store"; +import { type Readable, type Writable } from "svelte/store"; /** * TODO: We should create a single user preference store for all dashboards @@ -13,9 +13,9 @@ export interface LocalUserPreferences { } let localUserPreferences: Writable; -export function initLocalUserPreferenceStore(metricsViewName: string) { +export function initLocalUserPreferenceStore(exploreName: string) { localUserPreferences = localStorageStore( - `${metricsViewName}-userPreference`, + `${exploreName}-userPreference`, { timeZone: getLocalIANA(), }, @@ -40,6 +40,25 @@ function localUserPreferencesActions() { }; } +export function getLocalUserPreferencesState( + exploreName: string, +): LocalUserPreferences { + const localstorageUserPreference = localStorage.getItem( + `${exploreName}-userPreference`, + ); + if (localstorageUserPreference) { + try { + return JSON.parse(localstorageUserPreference); + } catch { + // TODO + } + } + + return { + timeZone: getLocalIANA(), + }; +} + export function getLocalUserPreferences(): Readable & ReturnType { return { diff --git a/web-common/src/lib/arrayUtils.ts b/web-common/src/lib/arrayUtils.ts index 28be43dfc5b..0a6caf14ec4 100644 --- a/web-common/src/lib/arrayUtils.ts +++ b/web-common/src/lib/arrayUtils.ts @@ -26,3 +26,24 @@ export function createBatches(array: T[], batchSize: number): T[][] { } return batches; } + +export function arrayUnorderedEquals(src: T[], tar: T[]) { + if (src.length !== tar.length) return false; + const set = new Set(src); + return tar.every((t) => set.has(t)); +} + +export function arrayOrderedEquals(src: T[], tar: T[]) { + if (src.length !== tar.length) return false; + for (let i = 0; i < src.length; i += 1) { + if (src[i] !== tar[i]) return false; + } + return true; +} + +/** + * Returns values in tar that are missing in src. + */ +export function getMissingValues(src: T[], tar: T[]) { + return tar.filter((v) => !src.includes(v)); +} diff --git a/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts b/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts index 629f4350f1c..fe5f9f7d2df 100644 --- a/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts +++ b/web-common/src/proto/gen/rill/runtime/v1/resources_pb.ts @@ -75,6 +75,44 @@ proto3.util.setEnumType(ExploreComparisonMode, "rill.runtime.v1.ExploreCompariso { no: 3, name: "EXPLORE_COMPARISON_MODE_DIMENSION" }, ]); +/** + * @generated from enum rill.runtime.v1.ExploreWebView + */ +export enum ExploreWebView { + /** + * @generated from enum value: EXPLORE_ACTIVE_PAGE_UNSPECIFIED = 0; + */ + EXPLORE_ACTIVE_PAGE_UNSPECIFIED = 0, + + /** + * @generated from enum value: EXPLORE_ACTIVE_PAGE_OVERVIEW = 1; + */ + EXPLORE_ACTIVE_PAGE_OVERVIEW = 1, + + /** + * @generated from enum value: EXPLORE_ACTIVE_PAGE_TIME_DIMENSION = 2; + */ + EXPLORE_ACTIVE_PAGE_TIME_DIMENSION = 2, + + /** + * @generated from enum value: EXPLORE_ACTIVE_PAGE_PIVOT = 3; + */ + EXPLORE_ACTIVE_PAGE_PIVOT = 3, + + /** + * @generated from enum value: EXPLORE_ACTIVE_PAGE_CANVAS = 4; + */ + EXPLORE_ACTIVE_PAGE_CANVAS = 4, +} +// Retrieve enum metadata with: proto3.getEnumType(ExploreWebView) +proto3.util.setEnumType(ExploreWebView, "rill.runtime.v1.ExploreWebView", [ + { no: 0, name: "EXPLORE_ACTIVE_PAGE_UNSPECIFIED" }, + { no: 1, name: "EXPLORE_ACTIVE_PAGE_OVERVIEW" }, + { no: 2, name: "EXPLORE_ACTIVE_PAGE_TIME_DIMENSION" }, + { no: 3, name: "EXPLORE_ACTIVE_PAGE_PIVOT" }, + { no: 4, name: "EXPLORE_ACTIVE_PAGE_CANVAS" }, +]); + /** * @generated from enum rill.runtime.v1.AssertionStatus */ @@ -2362,14 +2400,29 @@ export class ExplorePreset extends Message { */ measuresSelector?: FieldSelector; + /** + * @generated from field: optional rill.runtime.v1.Expression where = 25; + */ + where?: Expression; + /** * Time range for the explore. * It corresponds to the `range` property of the explore's `time_ranges`. * If not found in `time_ranges`, it should be added to the list. * - * @generated from field: string time_range = 6; + * @generated from field: optional string time_range = 6; + */ + timeRange?: string; + + /** + * @generated from field: optional string timezone = 11; + */ + timezone?: string; + + /** + * @generated from field: optional string time_grain = 12; */ - timeRange = ""; + timeGrain?: string; /** * Comparison mode. @@ -2378,12 +2431,72 @@ export class ExplorePreset extends Message { */ comparisonMode = ExploreComparisonMode.UNSPECIFIED; + /** + * @generated from field: optional string compare_time_range = 13; + */ + compareTimeRange?: string; + /** * If comparison_mode is EXPLORE_COMPARISON_MODE_DIMENSION, this indicates the dimension to use. * - * @generated from field: string comparison_dimension = 8; + * @generated from field: optional string comparison_dimension = 8; + */ + comparisonDimension?: string; + + /** + * @generated from field: optional rill.runtime.v1.ExploreWebView view = 14; + */ + view?: ExploreWebView; + + /** + * @generated from field: optional string overview_sort_by = 15; + */ + overviewSortBy?: string; + + /** + * @generated from field: optional bool overview_sort_asc = 16; + */ + overviewSortAsc?: boolean; + + /** + * @generated from field: optional string overview_expanded_dimension = 17; + */ + overviewExpandedDimension?: string; + + /** + * @generated from field: optional string time_dimension_measure = 18; + */ + timeDimensionMeasure?: string; + + /** + * @generated from field: optional string time_dimension_chart_type = 19; + */ + timeDimensionChartType?: string; + + /** + * @generated from field: optional bool time_dimension_pin = 20; + */ + timeDimensionPin?: boolean; + + /** + * @generated from field: repeated string pivot_rows = 21; + */ + pivotRows: string[] = []; + + /** + * @generated from field: repeated string pivot_cols = 22; + */ + pivotCols: string[] = []; + + /** + * @generated from field: optional string pivot_sort_by = 23; + */ + pivotSortBy?: string; + + /** + * @generated from field: optional bool pivot_sort_asc = 24; */ - comparisonDimension = ""; + pivotSortAsc?: boolean; constructor(data?: PartialMessage) { super(); @@ -2397,9 +2510,24 @@ export class ExplorePreset extends Message { { no: 9, name: "dimensions_selector", kind: "message", T: FieldSelector }, { no: 4, name: "measures", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, { no: 10, name: "measures_selector", kind: "message", T: FieldSelector }, - { no: 6, name: "time_range", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 25, name: "where", kind: "message", T: Expression, opt: true }, + { no: 6, name: "time_range", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 11, name: "timezone", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 12, name: "time_grain", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, { no: 7, name: "comparison_mode", kind: "enum", T: proto3.getEnumType(ExploreComparisonMode) }, - { no: 8, name: "comparison_dimension", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 13, name: "compare_time_range", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 8, name: "comparison_dimension", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 14, name: "view", kind: "enum", T: proto3.getEnumType(ExploreWebView), opt: true }, + { no: 15, name: "overview_sort_by", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 16, name: "overview_sort_asc", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, + { no: 17, name: "overview_expanded_dimension", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 18, name: "time_dimension_measure", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 19, name: "time_dimension_chart_type", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 20, name: "time_dimension_pin", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, + { no: 21, name: "pivot_rows", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, + { no: 22, name: "pivot_cols", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, + { no: 23, name: "pivot_sort_by", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 24, name: "pivot_sort_asc", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): ExplorePreset { diff --git a/web-common/src/runtime-client/gen/index.schemas.ts b/web-common/src/runtime-client/gen/index.schemas.ts index 011ff5ab201..8b7f5371ede 100644 --- a/web-common/src/runtime-client/gen/index.schemas.ts +++ b/web-common/src/runtime-client/gen/index.schemas.ts @@ -829,26 +829,6 @@ export const V1ResourceEvent = { RESOURCE_EVENT_DELETE: "RESOURCE_EVENT_DELETE", } as const; -export interface V1Resource { - meta?: V1ResourceMeta; - projectParser?: V1ProjectParser; - source?: V1SourceV2; - model?: V1ModelV2; - metricsView?: V1MetricsViewV2; - explore?: V1Explore; - migration?: V1Migration; - report?: V1Report; - alert?: V1Alert; - pullTrigger?: V1PullTrigger; - refreshTrigger?: V1RefreshTrigger; - bucketPlanner?: V1BucketPlanner; - theme?: V1Theme; - component?: V1Component; - canvas?: V1Canvas; - api?: V1API; - connector?: V1ConnectorV2; -} - export type V1ResolveComponentResponseRendererProperties = { [key: string]: any; }; @@ -864,13 +844,6 @@ If it resolves to false, the other fields are not set. */ rendererProperties?: V1ResolveComponentResponseRendererProperties; } -export interface V1ReportState { - nextRunOn?: string; - currentExecution?: V1ReportExecution; - executionHistory?: V1ReportExecution[]; - executionCount?: number; -} - export type V1ReportSpecAnnotations = { [key: string]: string }; export interface V1ReportSpec { @@ -899,6 +872,13 @@ export interface V1ReportExecution { finishedOn?: string; } +export interface V1ReportState { + nextRunOn?: string; + currentExecution?: V1ReportExecution; + executionHistory?: V1ReportExecution[]; + executionCount?: number; +} + export interface V1Report { spec?: V1ReportSpec; state?: V1ReportState; @@ -917,6 +897,26 @@ export interface V1RefreshTrigger { state?: V1RefreshTriggerState; } +export interface V1Resource { + meta?: V1ResourceMeta; + projectParser?: V1ProjectParser; + source?: V1SourceV2; + model?: V1ModelV2; + metricsView?: V1MetricsViewV2; + explore?: V1Explore; + migration?: V1Migration; + report?: V1Report; + alert?: V1Alert; + pullTrigger?: V1PullTrigger; + refreshTrigger?: V1RefreshTrigger; + bucketPlanner?: V1BucketPlanner; + theme?: V1Theme; + component?: V1Component; + canvas?: V1Canvas; + api?: V1API; + connector?: V1ConnectorV2; +} + export interface V1RefreshModelTrigger { /** The model to refresh. */ model?: string; @@ -1363,11 +1363,6 @@ export interface V1MetricsViewSchemaResponse { export type V1MetricsViewRowsResponseDataItem = { [key: string]: any }; -export interface V1MetricsViewRowsResponse { - meta?: V1MetricsViewColumn[]; - data?: V1MetricsViewRowsResponseDataItem[]; -} - export interface V1MetricsViewFilter { include?: MetricsViewFilterCond[]; exclude?: MetricsViewFilterCond[]; @@ -1481,6 +1476,11 @@ export interface V1MetricsViewColumn { nullable?: boolean; } +export interface V1MetricsViewRowsResponse { + meta?: V1MetricsViewColumn[]; + data?: V1MetricsViewRowsResponseDataItem[]; +} + export interface V1MetricsViewAggregationSort { name?: string; desc?: boolean; @@ -1493,31 +1493,6 @@ export interface V1MetricsViewAggregationResponse { data?: V1MetricsViewAggregationResponseDataItem[]; } -export interface V1MetricsViewAggregationRequest { - instanceId?: string; - metricsView?: string; - dimensions?: V1MetricsViewAggregationDimension[]; - measures?: V1MetricsViewAggregationMeasure[]; - sort?: V1MetricsViewAggregationSort[]; - timeRange?: V1TimeRange; - comparisonTimeRange?: V1TimeRange; - timeStart?: string; - timeEnd?: string; - pivotOn?: string[]; - aliases?: V1MetricsViewComparisonMeasureAlias[]; - where?: V1Expression; - /** Optional. If both where and where_sql are set, both will be applied with an AND between them. */ - whereSql?: string; - having?: V1Expression; - /** Optional. If both having and having_sql are set, both will be applied with an AND between them. */ - havingSql?: string; - limit?: string; - offset?: string; - priority?: number; - filter?: V1MetricsViewFilter; - exact?: boolean; -} - export interface V1MetricsViewAggregationMeasureComputeURI { dimension?: string; } @@ -1567,6 +1542,31 @@ export interface V1MetricsViewAggregationDimension { alias?: string; } +export interface V1MetricsViewAggregationRequest { + instanceId?: string; + metricsView?: string; + dimensions?: V1MetricsViewAggregationDimension[]; + measures?: V1MetricsViewAggregationMeasure[]; + sort?: V1MetricsViewAggregationSort[]; + timeRange?: V1TimeRange; + comparisonTimeRange?: V1TimeRange; + timeStart?: string; + timeEnd?: string; + pivotOn?: string[]; + aliases?: V1MetricsViewComparisonMeasureAlias[]; + where?: V1Expression; + /** Optional. If both where and where_sql are set, both will be applied with an AND between them. */ + whereSql?: string; + having?: V1Expression; + /** Optional. If both having and having_sql are set, both will be applied with an AND between them. */ + havingSql?: string; + limit?: string; + offset?: string; + priority?: number; + filter?: V1MetricsViewFilter; + exact?: boolean; +} + export interface V1MapType { keyType?: Runtimev1Type; valueType?: Runtimev1Type; @@ -1825,6 +1825,18 @@ export const V1ExportFormat = { EXPORT_FORMAT_PARQUET: "EXPORT_FORMAT_PARQUET", } as const; +export type V1ExploreWebView = + (typeof V1ExploreWebView)[keyof typeof V1ExploreWebView]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const V1ExploreWebView = { + EXPLORE_ACTIVE_PAGE_UNSPECIFIED: "EXPLORE_ACTIVE_PAGE_UNSPECIFIED", + EXPLORE_ACTIVE_PAGE_OVERVIEW: "EXPLORE_ACTIVE_PAGE_OVERVIEW", + EXPLORE_ACTIVE_PAGE_TIME_DIMENSION: "EXPLORE_ACTIVE_PAGE_TIME_DIMENSION", + EXPLORE_ACTIVE_PAGE_PIVOT: "EXPLORE_ACTIVE_PAGE_PIVOT", + EXPLORE_ACTIVE_PAGE_CANVAS: "EXPLORE_ACTIVE_PAGE_CANVAS", +} as const; + export interface V1ExploreState { validSpec?: V1ExploreSpec; } @@ -1860,13 +1872,28 @@ export interface V1ExplorePreset { /** Measures to show. If `measures_selector` is set, this will only be set in `state.valid_spec`. */ measures?: string[]; measuresSelector?: V1FieldSelector; + where?: V1Expression; /** Time range for the explore. It corresponds to the `range` property of the explore's `time_ranges`. If not found in `time_ranges`, it should be added to the list. */ timeRange?: string; + timezone?: string; + timeGrain?: string; comparisonMode?: V1ExploreComparisonMode; + compareTimeRange?: string; /** If comparison_mode is EXPLORE_COMPARISON_MODE_DIMENSION, this indicates the dimension to use. */ comparisonDimension?: string; + view?: V1ExploreWebView; + overviewSortBy?: string; + overviewSortAsc?: boolean; + overviewExpandedDimension?: string; + timeDimensionMeasure?: string; + timeDimensionChartType?: string; + timeDimensionPin?: boolean; + pivotRows?: string[]; + pivotCols?: string[]; + pivotSortBy?: string; + pivotSortAsc?: boolean; } export interface V1ExploreSpec { diff --git a/web-local/src/routes/(viz)/explore/[name]/+layout.ts b/web-local/src/routes/(viz)/explore/[name]/+layout.ts new file mode 100644 index 00000000000..46549841882 --- /dev/null +++ b/web-local/src/routes/(viz)/explore/[name]/+layout.ts @@ -0,0 +1,28 @@ +import { fetchExploreSpec } from "@rilldata/web-admin/features/dashboards/selectors"; +import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; +import { error } from "@sveltejs/kit"; +import { get } from "svelte/store"; + +export const load = async ({ params, depends }) => { + const { instanceId } = get(runtime); + + const exploreName = params.name; + + depends(exploreName, "explore"); + + try { + const { explore, metricsView, basePreset } = await fetchExploreSpec( + instanceId, + exploreName, + ); + + return { + explore, + metricsView, + basePreset, + }; + } catch (e) { + console.error(e); + throw error(404, "Explore not found"); + } +}; diff --git a/web-local/src/routes/(viz)/explore/[name]/+page.svelte b/web-local/src/routes/(viz)/explore/[name]/+page.svelte index 272d933421d..d9be090e747 100644 --- a/web-local/src/routes/(viz)/explore/[name]/+page.svelte +++ b/web-local/src/routes/(viz)/explore/[name]/+page.svelte @@ -4,9 +4,8 @@ import DashboardThemeProvider from "@rilldata/web-common/features/dashboards/DashboardThemeProvider.svelte"; import { resetSelectedMockUserAfterNavigate } from "@rilldata/web-common/features/dashboards/granular-access-policies/resetSelectedMockUserAfterNavigate"; import { selectedMockUserStore } from "@rilldata/web-common/features/dashboards/granular-access-policies/stores"; - import DashboardURLStateProvider from "@rilldata/web-common/features/dashboards/proto-state/DashboardURLStateProvider.svelte"; + import DashboardURLStateSync from "@rilldata/web-common/features/dashboards/url-state/DashboardURLStateSync.svelte"; import StateManagersProvider from "@rilldata/web-common/features/dashboards/state-managers/StateManagersProvider.svelte"; - import DashboardStateProvider from "@rilldata/web-common/features/dashboards/stores/DashboardStateProvider.svelte"; import { useProjectParser } from "@rilldata/web-common/features/entity-management/resource-selectors"; import { useExploreValidSpec } from "@rilldata/web-common/features/explores/selectors"; import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; @@ -16,20 +15,21 @@ const queryClient = useQueryClient(); export let data: PageData; + $: ({ metricsView, explore, basePreset, partialMetrics } = data); resetSelectedMockUserAfterNavigate(queryClient); - $: exploreName = data.explore.meta?.name?.name as string; - $: metricsViewName = data.metricsView?.meta?.name?.name as string; + $: exploreName = explore.meta?.name?.name as string; + $: metricsViewName = metricsView?.meta?.name?.name as string; $: ({ instanceId } = $runtime); $: filePaths = [ - ...(data.explore.meta?.filePaths ?? []), - ...(data.metricsView.meta?.filePaths ?? []), + ...(explore.meta?.filePaths ?? []), + ...(metricsView.meta?.filePaths ?? []), ]; - $: explore = useExploreValidSpec(instanceId, exploreName); - $: measures = $explore.data?.explore?.measures ?? []; + $: exploreQuery = useExploreValidSpec(instanceId, exploreName); + $: measures = $exploreQuery.data?.explore?.measures ?? []; $: projectParserQuery = useProjectParser(queryClient, instanceId, { enabled: $selectedMockUserStore?.admin, }); @@ -39,7 +39,7 @@ (error) => filePaths.includes(error.filePath as string), ); $: mockUserHasNoAccess = - $selectedMockUserStore && $explore.error?.response?.status === 404; + $selectedMockUserStore && $exploreQuery.error?.response?.status === 404; @@ -48,7 +48,7 @@ {#if measures.length === 0 && $selectedMockUserStore !== null} @@ -60,20 +60,18 @@ /> {:else if mockUserHasNoAccess} {:else} {#key exploreName} - - - - - - - + + + + + {/key} {/if} diff --git a/web-local/src/routes/(viz)/explore/[name]/+page.ts b/web-local/src/routes/(viz)/explore/[name]/+page.ts index dc8cee4e97c..6cb5353373f 100644 --- a/web-local/src/routes/(viz)/explore/[name]/+page.ts +++ b/web-local/src/routes/(viz)/explore/[name]/+page.ts @@ -1,52 +1,26 @@ -import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient.js"; -import { - getRuntimeServiceGetExploreQueryKey, - runtimeServiceGetExplore, -} from "@rilldata/web-common/runtime-client"; -import { error } from "@sveltejs/kit"; -import type { QueryFunction } from "@tanstack/svelte-query"; -import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; -import { get } from "svelte/store"; - -export const load = async ({ params, depends }) => { - const { instanceId } = get(runtime); - - const exploreName = params.name; - - depends(exploreName, "explore"); +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { convertURLToMetricsExplore } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToMetricsExplore"; + +export const load = async ({ url, parent }) => { + const { explore, metricsView, basePreset } = await parent(); + const metricsViewSpec = metricsView.metricsView?.state?.validSpec; + const exploreSpec = explore.explore?.state?.validSpec; + + let partialMetrics: Partial = {}; + const errors: Error[] = []; + if (metricsViewSpec && exploreSpec && url) { + const { entity, errors: errorsFromConvert } = convertURLToMetricsExplore( + url.searchParams, + metricsViewSpec, + exploreSpec, + basePreset, + ); + partialMetrics = entity; + errors.push(...errorsFromConvert); + } - const queryParams = { - name: exploreName, + return { + partialMetrics, + errors, }; - - const queryKey = getRuntimeServiceGetExploreQueryKey(instanceId, queryParams); - - const queryFunction: QueryFunction< - Awaited> - > = ({ signal }) => runtimeServiceGetExplore(instanceId, queryParams, signal); - - try { - const response = await queryClient.fetchQuery({ - queryFn: queryFunction, - queryKey, - }); - - const exploreResource = response.explore; - const metricsViewResource = response.metricsView; - - if (!exploreResource?.explore) { - throw error(404, "Explore not found"); - } - if (!metricsViewResource?.metricsView) { - throw error(404, "Metrics view not found"); - } - - return { - explore: exploreResource, - metricsView: metricsViewResource, - }; - } catch (e) { - console.error(e); - throw error(404, "Explore not found"); - } };