diff --git a/api/errorgroups/v1/errorgroups.proto b/api/errorgroups/v1/errorgroups.proto index 2c30656..939132c 100644 --- a/api/errorgroups/v1/errorgroups.proto +++ b/api/errorgroups/v1/errorgroups.proto @@ -23,6 +23,12 @@ enum Order { ORDER_OLDEST = 2; } +message TimeRange { + google.protobuf.Duration duration = 1; + google.protobuf.Timestamp from = 2; + google.protobuf.Timestamp to = 3; +} + message GetGroupsRequest { message Filter { bool is_new = 1; @@ -31,13 +37,14 @@ message GetGroupsRequest { string service = 1; optional string env = 2; optional string release = 3; - google.protobuf.Duration duration = 4; + google.protobuf.Duration duration = 4 [deprecated = true]; uint32 limit = 5; uint32 offset = 6; Order order = 7; bool with_total = 8; optional string source = 9; optional Filter filter = 10; + optional TimeRange time_range = 11; } message GetGroupsResponse { @@ -57,10 +64,11 @@ message GetGroupsResponse { message GetTopGroupsRequest { optional string env = 1; optional string source = 2; - google.protobuf.Duration duration = 3; + google.protobuf.Duration duration = 3 [deprecated = true]; uint32 limit = 4; uint32 offset = 5; bool with_total = 6; + optional TimeRange time_range = 7; } message GetTopGroupsResponse { @@ -80,8 +88,9 @@ message GetHistRequest { optional uint64 group_hash = 2; optional string env = 3; optional string release = 4; - optional google.protobuf.Duration duration = 5; + optional google.protobuf.Duration duration = 5 [deprecated = true]; optional string source = 6; + optional TimeRange time_range = 7; } message GetHistResponse { diff --git a/internal/api/errorgroups/v1/grpc/api.go b/internal/api/errorgroups/v1/grpc/api.go index 0e057fc..62071e5 100644 --- a/internal/api/errorgroups/v1/grpc/api.go +++ b/internal/api/errorgroups/v1/grpc/api.go @@ -1,12 +1,15 @@ package grpc import ( + "google.golang.org/protobuf/types/known/durationpb" + + "github.com/ozontech/seq-ui/internal/app/types" "github.com/ozontech/seq-ui/internal/pkg/service/errorgroups" - generated "github.com/ozontech/seq-ui/pkg/errorgroups/v1" + api "github.com/ozontech/seq-ui/pkg/errorgroups/v1" ) type API struct { - generated.UnimplementedErrorGroupsServiceServer + api.UnimplementedErrorGroupsServiceServer service errorgroups.Service } @@ -16,3 +19,26 @@ func New(svc errorgroups.Service) *API { service: svc, } } + +type trReq interface { + GetTimeRange() *api.TimeRange + GetDuration() *durationpb.Duration +} + +func parseTimeRange(req trReq) *types.TimeRange { + var timeRange *types.TimeRange + if tr := req.GetTimeRange(); tr != nil { + timeRange = &types.TimeRange{ + Duration: tr.Duration.AsDuration(), + + From: tr.From.AsTime(), + To: tr.To.AsTime(), + } + } + if timeRange == nil && req.GetDuration() != nil { + timeRange = &types.TimeRange{ + Duration: req.GetDuration().AsDuration(), + } + } + return timeRange +} diff --git a/internal/api/errorgroups/v1/grpc/get_groups.go b/internal/api/errorgroups/v1/grpc/get_groups.go index 4a72451..2bd6ac7 100644 --- a/internal/api/errorgroups/v1/grpc/get_groups.go +++ b/internal/api/errorgroups/v1/grpc/get_groups.go @@ -3,7 +3,6 @@ package grpc import ( "context" "encoding/json" - "time" "go.opentelemetry.io/otel/attribute" "google.golang.org/protobuf/types/known/timestamppb" @@ -41,20 +40,18 @@ func (a *API) GetGroups(ctx context.Context, req *errorgroups.GetGroupsRequest) filterRaw, _ := json.Marshal(req.Filter) attributes = append(attributes, attribute.KeyValue{Key: "filter", Value: attribute.StringValue(string(filterRaw))}) } - span.SetAttributes(attributes...) - - var duration *time.Duration - if req.Duration != nil { - parsedDuration := req.Duration.AsDuration() - duration = &parsedDuration + if req.TimeRange != nil { + trRaw, _ := json.Marshal(req.TimeRange) + attributes = append(attributes, attribute.KeyValue{Key: "time_range", Value: attribute.StringValue(string(trRaw))}) } + span.SetAttributes(attributes...) request := types.GetErrorGroupsRequest{ Service: req.Service, Env: req.Env, Source: req.Source, Release: req.Release, - Duration: duration, + TimeRange: parseTimeRange(req), Limit: req.Limit, Offset: req.Offset, Order: types.ErrorGroupsOrder(req.Order), diff --git a/internal/api/errorgroups/v1/grpc/get_groups_test.go b/internal/api/errorgroups/v1/grpc/get_groups_test.go index a15d5f3..00c2ed2 100644 --- a/internal/api/errorgroups/v1/grpc/get_groups_test.go +++ b/internal/api/errorgroups/v1/grpc/get_groups_test.go @@ -23,7 +23,7 @@ func TestGetGroups(t *testing.T) { source = "test-source" release = "test-release" duration = 2 * time.Minute - now = time.Now() + now = time.Now().Truncate(0).UTC() oneMinuteAgo = now.Add(-1 * time.Minute) twoMinutesAgo = now.Add(-2 * time.Minute) someErr = errors.New("some err") @@ -47,7 +47,7 @@ func TestGetGroups(t *testing.T) { mockArgs *mockArgs }{ { - name: "ok", + name: "ok_duration", req: &errorgroups_v1.GetGroupsRequest{ Service: service, @@ -82,13 +82,160 @@ func TestGetGroups(t *testing.T) { }, }, + mockArgs: &mockArgs{ + req: types.GetErrorGroupsRequest{ + Service: service, + Env: &env, + Source: &source, + Release: &release, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + Limit: 2, + Offset: 0, + Order: types.OrderOldest, + WithTotal: true, + }, + + groups: []types.ErrorGroup{ + { + Hash: 123, + Message: "some error 1", + Source: source, + Count: 10, + FirstSeenAt: twoMinutesAgo, + LastSeenAt: oneMinuteAgo, + }, + { + Hash: 456, + Message: "some error 2", + Source: source, + Count: 5, + FirstSeenAt: twoMinutesAgo, + LastSeenAt: oneMinuteAgo, + }, + }, + total: 10, + }, + }, + { + name: "ok_timerange", + + req: &errorgroups_v1.GetGroupsRequest{ + Service: service, + Env: &env, + Source: &source, + Release: &release, + Duration: durationpb.New(duration), + TimeRange: &errorgroups_v1.TimeRange{ + From: timestamppb.New(twoMinutesAgo), + To: timestamppb.New(now), + }, + Limit: 2, + Offset: 0, + Order: errorgroups_v1.Order_ORDER_OLDEST, + WithTotal: true, + }, + want: &errorgroups_v1.GetGroupsResponse{ + Total: 10, + Groups: []*errorgroups_v1.GetGroupsResponse_Group{ + { + Hash: 123, + Message: "some error 1", + Source: source, + SeenTotal: 10, + FirstSeenAt: timestamppb.New(twoMinutesAgo), + LastSeenAt: timestamppb.New(oneMinuteAgo), + }, + { + Hash: 456, + Message: "some error 2", + Source: source, + SeenTotal: 5, + FirstSeenAt: timestamppb.New(twoMinutesAgo), + LastSeenAt: timestamppb.New(oneMinuteAgo), + }, + }, + }, + + mockArgs: &mockArgs{ + req: types.GetErrorGroupsRequest{ + Service: service, + Env: &env, + Source: &source, + Release: &release, + TimeRange: &types.TimeRange{ + From: twoMinutesAgo, + To: now, + }, + Limit: 2, + Offset: 0, + Order: types.OrderOldest, + WithTotal: true, + }, + + groups: []types.ErrorGroup{ + { + Hash: 123, + Message: "some error 1", + Source: source, + Count: 10, + FirstSeenAt: twoMinutesAgo, + LastSeenAt: oneMinuteAgo, + }, + { + Hash: 456, + Message: "some error 2", + Source: source, + Count: 5, + FirstSeenAt: twoMinutesAgo, + LastSeenAt: oneMinuteAgo, + }, + }, + total: 10, + }, + }, + { + name: "ok_no_timerange", + + req: &errorgroups_v1.GetGroupsRequest{ + Service: service, + Env: &env, + Source: &source, + Release: &release, + Limit: 2, + Offset: 0, + Order: errorgroups_v1.Order_ORDER_OLDEST, + WithTotal: true, + }, + want: &errorgroups_v1.GetGroupsResponse{ + Total: 10, + Groups: []*errorgroups_v1.GetGroupsResponse_Group{ + { + Hash: 123, + Message: "some error 1", + Source: source, + SeenTotal: 10, + FirstSeenAt: timestamppb.New(twoMinutesAgo), + LastSeenAt: timestamppb.New(oneMinuteAgo), + }, + { + Hash: 456, + Message: "some error 2", + Source: source, + SeenTotal: 5, + FirstSeenAt: timestamppb.New(twoMinutesAgo), + LastSeenAt: timestamppb.New(oneMinuteAgo), + }, + }, + }, + mockArgs: &mockArgs{ req: types.GetErrorGroupsRequest{ Service: service, Env: &env, Source: &source, Release: &release, - Duration: &duration, Limit: 2, Offset: 0, Order: types.OrderOldest, @@ -124,7 +271,6 @@ func TestGetGroups(t *testing.T) { Env: &env, Source: &source, Release: &release, - Duration: durationpb.New(duration), Limit: 2, Offset: 0, Order: errorgroups_v1.Order_ORDER_OLDEST, @@ -161,7 +307,6 @@ func TestGetGroups(t *testing.T) { Env: &env, Source: &source, Release: &release, - Duration: &duration, Limit: 2, Offset: 0, Order: types.OrderOldest, diff --git a/internal/api/errorgroups/v1/grpc/get_hist.go b/internal/api/errorgroups/v1/grpc/get_hist.go index e33d36b..491cfcb 100644 --- a/internal/api/errorgroups/v1/grpc/get_hist.go +++ b/internal/api/errorgroups/v1/grpc/get_hist.go @@ -2,8 +2,8 @@ package grpc import ( "context" + "encoding/json" "strconv" - "time" "go.opentelemetry.io/otel/attribute" "google.golang.org/protobuf/types/known/timestamppb" @@ -39,13 +39,11 @@ func (a *API) GetHist(ctx context.Context, req *errorgroups.GetHistRequest) (*er if req.Source != nil { attributes = append(attributes, attribute.KeyValue{Key: "source", Value: attribute.StringValue(*req.Source)}) } - span.SetAttributes(attributes...) - - var duration *time.Duration - if req.Duration != nil { - parsedDuration := req.Duration.AsDuration() - duration = &parsedDuration + if req.TimeRange != nil { + trRaw, _ := json.Marshal(req.TimeRange) + attributes = append(attributes, attribute.KeyValue{Key: "time_range", Value: attribute.StringValue(string(trRaw))}) } + span.SetAttributes(attributes...) request := types.GetErrorHistRequest{ Service: req.Service, @@ -53,7 +51,7 @@ func (a *API) GetHist(ctx context.Context, req *errorgroups.GetHistRequest) (*er Env: req.Env, Source: req.Source, Release: req.Release, - Duration: duration, + TimeRange: parseTimeRange(req), } hist, err := a.service.GetHist(ctx, request) if err != nil { diff --git a/internal/api/errorgroups/v1/grpc/get_hist_test.go b/internal/api/errorgroups/v1/grpc/get_hist_test.go index fc2acf3..e8ce2bd 100644 --- a/internal/api/errorgroups/v1/grpc/get_hist_test.go +++ b/internal/api/errorgroups/v1/grpc/get_hist_test.go @@ -24,7 +24,7 @@ func TestGetHist(t *testing.T) { source = "test-source" release = "test-release" duration = 2 * time.Minute - now = time.Now() + now = time.Now().Truncate(0).UTC() oneMinuteAgo = now.Add(-1 * time.Minute) twoMinutesAgo = now.Add(-2 * time.Minute) someErr = errors.New("some err") @@ -47,7 +47,7 @@ func TestGetHist(t *testing.T) { mockArgs *mockArgs }{ { - name: "ok", + name: "ok_duration", req: &errorgroups_v1.GetHistRequest{ GroupHash: &groupHash, @@ -72,7 +72,90 @@ func TestGetHist(t *testing.T) { Env: &env, Source: &source, Release: &release, - Duration: &duration, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + }, + + hist: types.ErrorHist{ + Buckets: []types.ErrorHistBucket{ + {Time: oneMinuteAgo, Count: 10}, + {Time: twoMinutesAgo, Count: 20}, + }, + Interval: 123, + }, + }, + }, + { + name: "ok_timerange", + + req: &errorgroups_v1.GetHistRequest{ + GroupHash: &groupHash, + Service: &service, + Env: &env, + Source: &source, + Release: &release, + Duration: durationpb.New(duration), + TimeRange: &errorgroups_v1.TimeRange{ + From: timestamppb.New(twoMinutesAgo), + To: timestamppb.New(now), + }, + }, + want: &errorgroups_v1.GetHistResponse{ + Buckets: []*errorgroups_v1.Bucket{ + {Time: timestamppb.New(oneMinuteAgo), Count: 10}, + {Time: timestamppb.New(twoMinutesAgo), Count: 20}, + }, + Interval: 123, + }, + + mockArgs: &mockArgs{ + req: types.GetErrorHistRequest{ + GroupHash: &groupHash, + Service: &service, + Env: &env, + Source: &source, + Release: &release, + TimeRange: &types.TimeRange{ + From: twoMinutesAgo, + To: now, + }, + }, + + hist: types.ErrorHist{ + Buckets: []types.ErrorHistBucket{ + {Time: oneMinuteAgo, Count: 10}, + {Time: twoMinutesAgo, Count: 20}, + }, + Interval: 123, + }, + }, + }, + { + name: "ok_no_timerange", + + req: &errorgroups_v1.GetHistRequest{ + GroupHash: &groupHash, + Service: &service, + Env: &env, + Source: &source, + Release: &release, + }, + want: &errorgroups_v1.GetHistResponse{ + Buckets: []*errorgroups_v1.Bucket{ + {Time: timestamppb.New(oneMinuteAgo), Count: 10}, + {Time: timestamppb.New(twoMinutesAgo), Count: 20}, + }, + Interval: 123, + }, + + mockArgs: &mockArgs{ + req: types.GetErrorHistRequest{ + GroupHash: &groupHash, + Service: &service, + Env: &env, + Source: &source, + Release: &release, }, hist: types.ErrorHist{ diff --git a/internal/api/errorgroups/v1/grpc/get_top_groups.go b/internal/api/errorgroups/v1/grpc/get_top_groups.go index 03349ed..649150f 100644 --- a/internal/api/errorgroups/v1/grpc/get_top_groups.go +++ b/internal/api/errorgroups/v1/grpc/get_top_groups.go @@ -2,7 +2,7 @@ package grpc import ( "context" - "time" + "encoding/json" "go.opentelemetry.io/otel/attribute" @@ -30,18 +30,16 @@ func (a *API) GetTopGroups(ctx context.Context, req *errorgroups.GetTopGroupsReq if req.Duration != nil { attributes = append(attributes, attribute.KeyValue{Key: "duration", Value: attribute.StringValue(req.Duration.String())}) } - span.SetAttributes(attributes...) - - var duration *time.Duration - if req.Duration != nil { - parsedDuration := req.Duration.AsDuration() - duration = &parsedDuration + if req.TimeRange != nil { + trRaw, _ := json.Marshal(req.TimeRange) + attributes = append(attributes, attribute.KeyValue{Key: "time_range", Value: attribute.StringValue(string(trRaw))}) } + span.SetAttributes(attributes...) request := types.GetTopErrorGroupsRequest{ Env: req.Env, Source: req.Source, - Duration: duration, + TimeRange: parseTimeRange(req), Limit: req.Limit, Offset: req.Offset, WithTotal: req.WithTotal, diff --git a/internal/api/errorgroups/v1/grpc/get_top_groups_test.go b/internal/api/errorgroups/v1/grpc/get_top_groups_test.go index 3105792..06f22db 100644 --- a/internal/api/errorgroups/v1/grpc/get_top_groups_test.go +++ b/internal/api/errorgroups/v1/grpc/get_top_groups_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/timestamppb" "github.com/ozontech/seq-ui/internal/app/types" svc_mock "github.com/ozontech/seq-ui/internal/pkg/service/errorgroups/mock" @@ -17,10 +18,12 @@ import ( func TestGetTopGroups(t *testing.T) { var ( - env = "test-env" - source = "test-source" - duration = 2 * time.Minute - someErr = errors.New("some err") + env = "test-env" + source = "test-source" + duration = 2 * time.Minute + now = time.Now().Truncate(0).UTC() + twoMinutesAgo = now.Add(-2 * time.Minute) + someErr = errors.New("some err") ) type mockArgs struct { @@ -41,7 +44,7 @@ func TestGetTopGroups(t *testing.T) { mockArgs *mockArgs }{ { - name: "ok", + name: "ok_duration", req: &errorgroups_v1.GetTopGroupsRequest{ Env: &env, @@ -69,11 +72,130 @@ func TestGetTopGroups(t *testing.T) { }, }, + mockArgs: &mockArgs{ + req: types.GetTopErrorGroupsRequest{ + Env: &env, + Source: &source, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + Limit: 2, + Offset: 0, + WithTotal: true, + }, + + groups: []types.TopErrorGroup{ + { + Hash: 123, + Message: "some error 1", + Source: source, + Count: 10, + }, + { + Hash: 456, + Message: "some error 2", + Source: source, + Count: 5, + }, + }, + total: 10, + }, + }, + { + name: "ok_timerange", + + req: &errorgroups_v1.GetTopGroupsRequest{ + Env: &env, + Source: &source, + Duration: durationpb.New(duration), + TimeRange: &errorgroups_v1.TimeRange{ + From: timestamppb.New(twoMinutesAgo), + To: timestamppb.New(now), + }, + Limit: 2, + Offset: 0, + WithTotal: true, + }, + want: &errorgroups_v1.GetTopGroupsResponse{ + Total: 10, + Groups: []*errorgroups_v1.GetTopGroupsResponse_Group{ + { + Hash: 123, + Message: "some error 1", + Source: source, + SeenTotal: 10, + }, + { + Hash: 456, + Message: "some error 2", + Source: source, + SeenTotal: 5, + }, + }, + }, + + mockArgs: &mockArgs{ + req: types.GetTopErrorGroupsRequest{ + Env: &env, + Source: &source, + TimeRange: &types.TimeRange{ + From: twoMinutesAgo, + To: now, + }, + Limit: 2, + Offset: 0, + WithTotal: true, + }, + + groups: []types.TopErrorGroup{ + { + Hash: 123, + Message: "some error 1", + Source: source, + Count: 10, + }, + { + Hash: 456, + Message: "some error 2", + Source: source, + Count: 5, + }, + }, + total: 10, + }, + }, + { + name: "ok_no_timerange", + + req: &errorgroups_v1.GetTopGroupsRequest{ + Env: &env, + Source: &source, + Limit: 2, + Offset: 0, + WithTotal: true, + }, + want: &errorgroups_v1.GetTopGroupsResponse{ + Total: 10, + Groups: []*errorgroups_v1.GetTopGroupsResponse_Group{ + { + Hash: 123, + Message: "some error 1", + Source: source, + SeenTotal: 10, + }, + { + Hash: 456, + Message: "some error 2", + Source: source, + SeenTotal: 5, + }, + }, + }, + mockArgs: &mockArgs{ req: types.GetTopErrorGroupsRequest{ Env: &env, Source: &source, - Duration: &duration, Limit: 2, Offset: 0, WithTotal: true, diff --git a/internal/api/errorgroups/v1/http/api.go b/internal/api/errorgroups/v1/http/api.go index b8134d0..f6356ee 100644 --- a/internal/api/errorgroups/v1/http/api.go +++ b/internal/api/errorgroups/v1/http/api.go @@ -6,6 +6,7 @@ import ( "github.com/go-chi/chi/v5" + "github.com/ozontech/seq-ui/internal/app/types" "github.com/ozontech/seq-ui/internal/pkg/service/errorgroups" ) @@ -33,6 +34,46 @@ func (a *API) Router() chi.Router { return mux } +type timeRange struct { + Duration string `json:"duration" format:"duration" example:"1h"` + + From time.Time `json:"from" format:"date-time"` + To time.Time `json:"to" format:"date-time"` +} // @name errorgroups.v1.TimeRange + +func parseTimeRange(tr *timeRange, dur *string) (*types.TimeRange, error) { + var timeRange *types.TimeRange + if tr != nil { + var ( + d time.Duration + err error + ) + if tr.Duration != "" { + d, err = time.ParseDuration(tr.Duration) + if err != nil { + return nil, err + } + } + + timeRange = &types.TimeRange{ + Duration: d, + + From: tr.From, + To: tr.To, + } + } + if timeRange == nil && dur != nil && *dur != "" { + d, err := time.ParseDuration(*dur) + if err != nil { + return nil, err + } + timeRange = &types.TimeRange{ + Duration: d, + } + } + return timeRange, nil +} + func parseGroupHash(groupHash *string) (*uint64, error) { if groupHash == nil { return nil, nil @@ -41,12 +82,3 @@ func parseGroupHash(groupHash *string) (*uint64, error) { parsedGroupHash, err := strconv.ParseUint(*groupHash, 10, 64) return &parsedGroupHash, err } - -func parseDuration(d *string) (*time.Duration, error) { - if d == nil { - return nil, nil - } - - parsedDuration, err := time.ParseDuration(*d) - return &parsedDuration, err -} diff --git a/internal/api/errorgroups/v1/http/get_groups.go b/internal/api/errorgroups/v1/http/get_groups.go index 3bfc2dc..41eeda9 100644 --- a/internal/api/errorgroups/v1/http/get_groups.go +++ b/internal/api/errorgroups/v1/http/get_groups.go @@ -35,12 +35,6 @@ func (a *API) serveGetGroups(w http.ResponseWriter, r *http.Request) { return } - parsedDuration, err := parseDuration(httpReq.Duration) - if err != nil { - wr.Error(fmt.Errorf("failed to parse duration: %w", err), http.StatusBadRequest) - return - } - attributes := []attribute.KeyValue{ {Key: "service", Value: attribute.StringValue(httpReq.Service)}, {Key: "limit", Value: attribute.IntValue(int(httpReq.Limit))}, @@ -64,14 +58,24 @@ func (a *API) serveGetGroups(w http.ResponseWriter, r *http.Request) { filterRaw, _ := json.Marshal(httpReq.Filter) attributes = append(attributes, attribute.KeyValue{Key: "filter", Value: attribute.StringValue(string(filterRaw))}) } + if httpReq.TimeRange != nil { + trRaw, _ := json.Marshal(httpReq.TimeRange) + attributes = append(attributes, attribute.KeyValue{Key: "time_range", Value: attribute.StringValue(string(trRaw))}) + } span.SetAttributes(attributes...) + tr, err := parseTimeRange(httpReq.TimeRange, httpReq.Duration) + if err != nil { + wr.Error(fmt.Errorf("failed to parse time range: %w", err), http.StatusBadRequest) + return + } + req := types.GetErrorGroupsRequest{ Service: httpReq.Service, Env: httpReq.Env, Source: httpReq.Source, Release: httpReq.Release, - Duration: parsedDuration, + TimeRange: tr, Limit: httpReq.Limit, Offset: httpReq.Offset, Order: httpReq.Order.toDomain(), @@ -129,12 +133,13 @@ type getGroupsRequest struct { Env *string `json:"env,omitempty"` Source *string `json:"source,omitempty"` Release *string `json:"release,omitempty"` - // In go duration format. If not specified, then for the entire time. - Duration *string `json:"duration,omitempty" format:"duration" example:"1h"` - Limit uint32 `json:"limit"` - Offset uint32 `json:"offset"` - Order order `json:"order"` - WithTotal bool `json:"with_total"` + // Deprecated: Use time_range instead + Duration *string `json:"duration,omitempty" format:"duration" example:"1h"` + TimeRange *timeRange `json:"time_range,omitempty"` + Limit uint32 `json:"limit"` + Offset uint32 `json:"offset"` + Order order `json:"order"` + WithTotal bool `json:"with_total"` Filter *groupsFilter `json:"filter,omitempty"` } // @name errorgroups.v1.GetGroupsRequest diff --git a/internal/api/errorgroups/v1/http/get_groups_test.go b/internal/api/errorgroups/v1/http/get_groups_test.go index 5a51807..1343626 100644 --- a/internal/api/errorgroups/v1/http/get_groups_test.go +++ b/internal/api/errorgroups/v1/http/get_groups_test.go @@ -21,7 +21,7 @@ func TestServeGetGroups(t *testing.T) { source = "test-source" durationStr = "2m" duration = 2 * time.Minute - now = time.Now() + now = time.Now().Truncate(0) oneMinuteAgo = now.Add(-1 * time.Minute) twoMinutesAgo = now.Add(-2 * time.Minute) someErr = errors.New("some err") @@ -45,7 +45,7 @@ func TestServeGetGroups(t *testing.T) { mockArgs *mockArgs }{ { - name: "ok", + name: "ok_duration", req: getGroupsRequest{ Service: service, @@ -80,13 +80,160 @@ func TestServeGetGroups(t *testing.T) { }, }, + mockArgs: &mockArgs{ + req: types.GetErrorGroupsRequest{ + Service: service, + Env: &env, + Source: &source, + Release: &release, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + Limit: 2, + Offset: 0, + Order: types.OrderFrequent, + WithTotal: true, + }, + + groups: []types.ErrorGroup{ + { + Hash: 123, + Message: "some error 1", + Source: source, + Count: 5, + FirstSeenAt: twoMinutesAgo, + LastSeenAt: oneMinuteAgo, + }, + { + Hash: 456, + Message: "some error 2", + Source: source, + Count: 10, + FirstSeenAt: twoMinutesAgo, + LastSeenAt: oneMinuteAgo, + }, + }, + total: 10, + }, + }, + { + name: "ok_timerange", + + req: getGroupsRequest{ + Service: service, + Env: &env, + Source: &source, + Release: &release, + Duration: &durationStr, + TimeRange: &timeRange{ + From: twoMinutesAgo, + To: now, + }, + Limit: 2, + Offset: 0, + Order: OrderFrequent, + WithTotal: true, + }, + want: getGroupsResponse{ + Total: 10, + Groups: []group{ + { + Hash: "123", + Message: "some error 1", + Source: source, + SeenTotal: 5, + FirstSeenAt: twoMinutesAgo, + LastSeenAt: oneMinuteAgo, + }, + { + Hash: "456", + Message: "some error 2", + Source: source, + SeenTotal: 10, + FirstSeenAt: twoMinutesAgo, + LastSeenAt: oneMinuteAgo, + }, + }, + }, + + mockArgs: &mockArgs{ + req: types.GetErrorGroupsRequest{ + Service: service, + Env: &env, + Source: &source, + Release: &release, + TimeRange: &types.TimeRange{ + From: twoMinutesAgo, + To: now, + }, + Limit: 2, + Offset: 0, + Order: types.OrderFrequent, + WithTotal: true, + }, + + groups: []types.ErrorGroup{ + { + Hash: 123, + Message: "some error 1", + Source: source, + Count: 5, + FirstSeenAt: twoMinutesAgo, + LastSeenAt: oneMinuteAgo, + }, + { + Hash: 456, + Message: "some error 2", + Source: source, + Count: 10, + FirstSeenAt: twoMinutesAgo, + LastSeenAt: oneMinuteAgo, + }, + }, + total: 10, + }, + }, + { + name: "ok_no_timerange", + + req: getGroupsRequest{ + Service: service, + Env: &env, + Source: &source, + Release: &release, + Limit: 2, + Offset: 0, + Order: OrderFrequent, + WithTotal: true, + }, + want: getGroupsResponse{ + Total: 10, + Groups: []group{ + { + Hash: "123", + Message: "some error 1", + Source: source, + SeenTotal: 5, + FirstSeenAt: twoMinutesAgo, + LastSeenAt: oneMinuteAgo, + }, + { + Hash: "456", + Message: "some error 2", + Source: source, + SeenTotal: 10, + FirstSeenAt: twoMinutesAgo, + LastSeenAt: oneMinuteAgo, + }, + }, + }, + mockArgs: &mockArgs{ req: types.GetErrorGroupsRequest{ Service: service, Env: &env, Source: &source, Release: &release, - Duration: &duration, Limit: 2, Offset: 0, Order: types.OrderFrequent, @@ -122,7 +269,6 @@ func TestServeGetGroups(t *testing.T) { Env: &env, Source: &source, Release: &release, - Duration: &durationStr, Limit: 2, Offset: 0, Order: OrderFrequent, @@ -159,7 +305,6 @@ func TestServeGetGroups(t *testing.T) { Env: &env, Source: &source, Release: &release, - Duration: &duration, Limit: 2, Offset: 0, Order: types.OrderFrequent, diff --git a/internal/api/errorgroups/v1/http/get_hist.go b/internal/api/errorgroups/v1/http/get_hist.go index 9b838c5..8686d50 100644 --- a/internal/api/errorgroups/v1/http/get_hist.go +++ b/internal/api/errorgroups/v1/http/get_hist.go @@ -34,18 +34,6 @@ func (a *API) serveGetHist(w http.ResponseWriter, r *http.Request) { return } - parsedGroupHash, err := parseGroupHash(httpReq.GroupHash) - if err != nil { - wr.Error(fmt.Errorf("failed to parse group_hash: %w", err), http.StatusBadRequest) - return - } - - parsedDuration, err := parseDuration(httpReq.Duration) - if err != nil { - wr.Error(fmt.Errorf("failed to parse duration: %w", err), http.StatusBadRequest) - return - } - attributes := []attribute.KeyValue{} if httpReq.GroupHash != nil { attributes = append(attributes, attribute.KeyValue{Key: "group_hash", Value: attribute.StringValue(*httpReq.GroupHash)}) @@ -65,15 +53,31 @@ func (a *API) serveGetHist(w http.ResponseWriter, r *http.Request) { if httpReq.Source != nil { attributes = append(attributes, attribute.KeyValue{Key: "source", Value: attribute.StringValue(*httpReq.Source)}) } + if httpReq.TimeRange != nil { + trRaw, _ := json.Marshal(httpReq.TimeRange) + attributes = append(attributes, attribute.KeyValue{Key: "time_range", Value: attribute.StringValue(string(trRaw))}) + } span.SetAttributes(attributes...) + parsedGroupHash, err := parseGroupHash(httpReq.GroupHash) + if err != nil { + wr.Error(fmt.Errorf("failed to parse group_hash: %w", err), http.StatusBadRequest) + return + } + + tr, err := parseTimeRange(httpReq.TimeRange, httpReq.Duration) + if err != nil { + wr.Error(fmt.Errorf("failed to parse time range: %w", err), http.StatusBadRequest) + return + } + req := types.GetErrorHistRequest{ Service: httpReq.Service, GroupHash: parsedGroupHash, Env: httpReq.Env, Source: httpReq.Source, Release: httpReq.Release, - Duration: parsedDuration, + TimeRange: tr, } hist, err := a.service.GetHist(ctx, req) if err != nil { @@ -90,8 +94,9 @@ type getHistRequest struct { Env *string `json:"env,omitempty"` Source *string `json:"source,omitempty"` Release *string `json:"release,omitempty"` - // In go duration format. If not specified, `1h` is used. - Duration *string `json:"duration,omitempty" format:"duration" example:"1h"` + // Deprecated: Use time_range instead + Duration *string `json:"duration,omitempty" format:"duration" example:"1h"` + TimeRange *timeRange `json:"time_range,omitempty"` } // @name errorgroups.v1.GetHistRequest type getHistResponse struct { diff --git a/internal/api/errorgroups/v1/http/get_hist_test.go b/internal/api/errorgroups/v1/http/get_hist_test.go index eb2edb1..dd00dd8 100644 --- a/internal/api/errorgroups/v1/http/get_hist_test.go +++ b/internal/api/errorgroups/v1/http/get_hist_test.go @@ -23,7 +23,7 @@ func TestServeGetHist(t *testing.T) { release = "test-release" durationStr = "2m" duration = 2 * time.Minute - now = time.Now() + now = time.Now().Truncate(0) oneMinuteAgo = now.Add(-1 * time.Minute) twoMinutesAgo = now.Add(-2 * time.Minute) someErr = errors.New("some err") @@ -46,7 +46,7 @@ func TestServeGetHist(t *testing.T) { mockArgs *mockArgs }{ { - name: "ok", + name: "ok_duration", req: getHistRequest{ GroupHash: &groupHashStr, @@ -77,7 +77,102 @@ func TestServeGetHist(t *testing.T) { Env: &env, Source: &source, Release: &release, - Duration: &duration, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + }, + + hist: types.ErrorHist{ + Buckets: []types.ErrorHistBucket{ + {Time: twoMinutesAgo, Count: 100}, + {Time: oneMinuteAgo, Count: 200}, + }, + Interval: 123, + }, + }, + }, + { + name: "ok_timerange", + + req: getHistRequest{ + GroupHash: &groupHashStr, + Service: &service, + Env: &env, + Source: &source, + Release: &release, + Duration: &durationStr, + TimeRange: &timeRange{ + From: twoMinutesAgo, + To: now, + }, + }, + want: getHistResponse{ + Buckets: []bucket{ + { + Time: twoMinutesAgo, + Count: 100, + }, + { + Time: oneMinuteAgo, + Count: 200, + }, + }, + Interval: 123, + }, + + mockArgs: &mockArgs{ + req: types.GetErrorHistRequest{ + GroupHash: &groupHash, + Service: &service, + Env: &env, + Source: &source, + Release: &release, + TimeRange: &types.TimeRange{ + From: twoMinutesAgo, + To: now, + }, + }, + + hist: types.ErrorHist{ + Buckets: []types.ErrorHistBucket{ + {Time: twoMinutesAgo, Count: 100}, + {Time: oneMinuteAgo, Count: 200}, + }, + Interval: 123, + }, + }, + }, + { + name: "ok_no_timerange", + + req: getHistRequest{ + GroupHash: &groupHashStr, + Service: &service, + Env: &env, + Source: &source, + Release: &release, + }, + want: getHistResponse{ + Buckets: []bucket{ + { + Time: twoMinutesAgo, + Count: 100, + }, + { + Time: oneMinuteAgo, + Count: 200, + }, + }, + Interval: 123, + }, + + mockArgs: &mockArgs{ + req: types.GetErrorHistRequest{ + GroupHash: &groupHash, + Service: &service, + Env: &env, + Source: &source, + Release: &release, }, hist: types.ErrorHist{ diff --git a/internal/api/errorgroups/v1/http/get_top_groups.go b/internal/api/errorgroups/v1/http/get_top_groups.go index 60c7611..c04d65d 100644 --- a/internal/api/errorgroups/v1/http/get_top_groups.go +++ b/internal/api/errorgroups/v1/http/get_top_groups.go @@ -34,12 +34,6 @@ func (a *API) serveGetTopGroups(w http.ResponseWriter, r *http.Request) { return } - parsedDuration, err := parseDuration(httpReq.Duration) - if err != nil { - wr.Error(fmt.Errorf("failed to parse duration: %w", err), http.StatusBadRequest) - return - } - attributes := []attribute.KeyValue{ {Key: "limit", Value: attribute.IntValue(int(httpReq.Limit))}, {Key: "offset", Value: attribute.IntValue(int(httpReq.Offset))}, @@ -54,12 +48,22 @@ func (a *API) serveGetTopGroups(w http.ResponseWriter, r *http.Request) { if httpReq.Duration != nil { attributes = append(attributes, attribute.KeyValue{Key: "duration", Value: attribute.StringValue(*httpReq.Duration)}) } + if httpReq.TimeRange != nil { + trRaw, _ := json.Marshal(httpReq.TimeRange) + attributes = append(attributes, attribute.KeyValue{Key: "time_range", Value: attribute.StringValue(string(trRaw))}) + } span.SetAttributes(attributes...) + tr, err := parseTimeRange(httpReq.TimeRange, httpReq.Duration) + if err != nil { + wr.Error(fmt.Errorf("failed to parse time range: %w", err), http.StatusBadRequest) + return + } + req := types.GetTopErrorGroupsRequest{ Env: httpReq.Env, Source: httpReq.Source, - Duration: parsedDuration, + TimeRange: tr, Limit: httpReq.Limit, Offset: httpReq.Offset, WithTotal: httpReq.WithTotal, @@ -80,11 +84,12 @@ func (a *API) serveGetTopGroups(w http.ResponseWriter, r *http.Request) { type getTopGroupsRequest struct { Env *string `json:"env,omitempty"` Source *string `json:"source,omitempty"` - // In go duration format. If not specified, then for the entire time. - Duration *string `json:"duration,omitempty" format:"duration" example:"1h"` - Limit uint32 `json:"limit"` - Offset uint32 `json:"offset"` - WithTotal bool `json:"with_total"` + // Deprecated: Use time_range instead + Duration *string `json:"duration,omitempty" format:"duration" example:"1h"` + TimeRange *timeRange `json:"time_range,omitempty"` + Limit uint32 `json:"limit"` + Offset uint32 `json:"offset"` + WithTotal bool `json:"with_total"` } // @name errorgroups.v1.GetTopGroupsRequest type getTopGroupsResponse struct { diff --git a/internal/api/errorgroups/v1/http/get_top_groups_test.go b/internal/api/errorgroups/v1/http/get_top_groups_test.go index 5f840cc..c129a81 100644 --- a/internal/api/errorgroups/v1/http/get_top_groups_test.go +++ b/internal/api/errorgroups/v1/http/get_top_groups_test.go @@ -15,11 +15,13 @@ import ( func TestServeGetTopGroups(t *testing.T) { var ( - env = "test-env" - source = "test-source" - durationStr = "2m" - duration = 2 * time.Minute - someErr = errors.New("some err") + env = "test-env" + source = "test-source" + durationStr = "2m" + duration = 2 * time.Minute + now = time.Now().Truncate(0) + twoMinutesAgo = now.Add(-2 * time.Minute) + someErr = errors.New("some err") ) type mockArgs struct { @@ -40,7 +42,7 @@ func TestServeGetTopGroups(t *testing.T) { mockArgs *mockArgs }{ { - name: "ok", + name: "ok_duration", req: getTopGroupsRequest{ Env: &env, @@ -68,11 +70,130 @@ func TestServeGetTopGroups(t *testing.T) { }, }, + mockArgs: &mockArgs{ + req: types.GetTopErrorGroupsRequest{ + Env: &env, + Source: &source, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + Limit: 2, + Offset: 0, + WithTotal: true, + }, + + groups: []types.TopErrorGroup{ + { + Hash: 123, + Message: "some error 1", + Source: source, + Count: 5, + }, + { + Hash: 456, + Message: "some error 2", + Source: source, + Count: 10, + }, + }, + total: 10, + }, + }, + { + name: "ok_timerange", + + req: getTopGroupsRequest{ + Env: &env, + Source: &source, + Duration: &durationStr, + TimeRange: &timeRange{ + From: twoMinutesAgo, + To: now, + }, + Limit: 2, + Offset: 0, + WithTotal: true, + }, + want: getTopGroupsResponse{ + Total: 10, + Groups: []topGroup{ + { + Hash: "123", + Message: "some error 1", + Source: source, + SeenTotal: 5, + }, + { + Hash: "456", + Message: "some error 2", + Source: source, + SeenTotal: 10, + }, + }, + }, + + mockArgs: &mockArgs{ + req: types.GetTopErrorGroupsRequest{ + Env: &env, + Source: &source, + TimeRange: &types.TimeRange{ + From: twoMinutesAgo, + To: now, + }, + Limit: 2, + Offset: 0, + WithTotal: true, + }, + + groups: []types.TopErrorGroup{ + { + Hash: 123, + Message: "some error 1", + Source: source, + Count: 5, + }, + { + Hash: 456, + Message: "some error 2", + Source: source, + Count: 10, + }, + }, + total: 10, + }, + }, + { + name: "ok_no_timerange", + + req: getTopGroupsRequest{ + Env: &env, + Source: &source, + Limit: 2, + Offset: 0, + WithTotal: true, + }, + want: getTopGroupsResponse{ + Total: 10, + Groups: []topGroup{ + { + Hash: "123", + Message: "some error 1", + Source: source, + SeenTotal: 5, + }, + { + Hash: "456", + Message: "some error 2", + Source: source, + SeenTotal: 10, + }, + }, + }, + mockArgs: &mockArgs{ req: types.GetTopErrorGroupsRequest{ Env: &env, Source: &source, - Duration: &duration, Limit: 2, Offset: 0, WithTotal: true, @@ -95,7 +216,6 @@ func TestServeGetTopGroups(t *testing.T) { total: 10, }, }, - { name: "err_svc", diff --git a/internal/app/types/errorgroups.go b/internal/app/types/errorgroups.go index 8400d41..2e482e5 100644 --- a/internal/app/types/errorgroups.go +++ b/internal/app/types/errorgroups.go @@ -12,12 +12,38 @@ const ( OrderOldest ) +type TimeRange struct { + Duration time.Duration + + From time.Time + To time.Time +} + +func (tr *TimeRange) IsAbsolute() bool { + return tr != nil && !tr.From.IsZero() && !tr.To.IsZero() +} + +func (tr *TimeRange) AbsoluteDuration() time.Duration { + if !tr.IsAbsolute() { + return 0 + } + return tr.To.Sub(tr.From) +} + +func (tr *TimeRange) IsRelative() bool { + return tr != nil && tr.Duration != 0 +} + +func (tr *TimeRange) IsEmpty() bool { + return tr == nil || !tr.IsAbsolute() && !tr.IsRelative() +} + type GetErrorGroupsRequest struct { Service string Env *string Source *string Release *string - Duration *time.Duration + TimeRange *TimeRange Limit uint32 Offset uint32 Order ErrorGroupsOrder @@ -36,7 +62,7 @@ type ErrorGroup struct { type GetTopErrorGroupsRequest struct { Env *string Source *string - Duration *time.Duration + TimeRange *TimeRange Limit uint32 Offset uint32 WithTotal bool @@ -55,7 +81,7 @@ type GetErrorHistRequest struct { Env *string Source *string Release *string - Duration *time.Duration + TimeRange *TimeRange } type ErrorHistBucket struct { diff --git a/internal/pkg/repository_ch/error_groups.go b/internal/pkg/repository_ch/error_groups.go index ca71ac9..12d845e 100644 --- a/internal/pkg/repository_ch/error_groups.go +++ b/internal/pkg/repository_ch/error_groups.go @@ -33,7 +33,7 @@ func (r *repository) GetErrorGroups( where["release"] = *req.Release } - if req.Duration != nil && *req.Duration != 0 { + if !req.TimeRange.IsEmpty() { var ( hashes []uint64 // ordered infos errorInfos @@ -44,11 +44,11 @@ func (r *repository) GetErrorGroups( if req.Order == types.OrderFrequent { counts, err = r.getErrorCounts(ctx, getErrorCountsParams{ - duration: req.Duration, - where: where, - orderBy: "count DESC", - limit: uint64(req.Limit), - offset: uint64(req.Offset), + tr: req.TimeRange, + where: where, + orderBy: "count DESC", + limit: uint64(req.Limit), + offset: uint64(req.Offset), }) if err != nil { return nil, err @@ -76,12 +76,12 @@ func (r *repository) GetErrorGroups( } } else { subQuery := r.getHashSubQuery(getHashSubQueryParams{ - table: "error_groups", - where: where, - duration: req.Duration, - orderBy: orderBy(req.Order, true), - limit: uint64(req.Limit), - offset: uint64(req.Offset), + table: "error_groups", + where: where, + tr: req.TimeRange, + orderBy: orderBy(req.Order, true), + limit: uint64(req.Limit), + offset: uint64(req.Offset), }) infos, err = r.getErrorInfos(ctx, getErrorInfosParams{ @@ -108,8 +108,8 @@ func (r *repository) GetErrorGroups( where["_group_hash"] = hashes counts, err = r.getErrorCounts(ctx, getErrorCountsParams{ - duration: req.Duration, - where: where, + tr: req.TimeRange, + where: where, }) if err != nil { return nil, err @@ -136,12 +136,11 @@ func (r *repository) GetErrorGroups( } subQ := r.getHashSubQuery(getHashSubQueryParams{ - table: "error_groups", - where: where, - duration: req.Duration, - orderBy: orderBy(req.Order, true), - limit: uint64(req.Limit), - offset: uint64(req.Offset), + table: "error_groups", + where: where, + orderBy: orderBy(req.Order, true), + limit: uint64(req.Limit), + offset: uint64(req.Offset), }) infos, err := r.getErrorInfos(ctx, getErrorInfosParams{ @@ -197,9 +196,9 @@ func (r *repository) GetErrorGroupsTotal( } return r.getTotal(ctx, getTotalParams{ - where: where, - table: "error_groups", - duration: req.Duration, + where: where, + table: "error_groups", + tr: req.TimeRange, }) } @@ -227,13 +226,13 @@ func (r *repository) GetNewErrorGroups( limit: uint64(req.Limit), offset: uint64(req.Offset), }) - if req.Release != nil && *req.Release != "" { // new by releases, ignore duration + if req.Release != nil && *req.Release != "" { // new by releases, ignore time range subQ = subQ.Having(sq.Eq{ "any(release)": *req.Release, "count()": 1, }) - } else if req.Duration != nil && *req.Duration != 0 { // new by duration - subQ = subQ.Having(sq.GtOrEq{"minMerge(first_seen_at)": r.nowFn().Add(-req.Duration.Abs())}) + } else if !req.TimeRange.IsEmpty() { // new by time range + subQ = subQ.Having(r.timeRangeCond("minMerge(first_seen_at)", req.TimeRange)) } infos, err := r.getErrorInfos(ctx, getErrorInfosParams{ @@ -288,13 +287,13 @@ func (r *repository) GetNewErrorGroupsTotal( subQ = subQ.Where(sq.Eq{"source": *req.Source}) } - if req.Release != nil && *req.Release != "" { // new by releases, ignore duration + if req.Release != nil && *req.Release != "" { // new by releases, ignore time range subQ = subQ.Having(sq.Eq{ "any(release)": *req.Release, "count()": 1, }) - } else if req.Duration != nil && *req.Duration != 0 { // new by duration - subQ = subQ.Having(sq.GtOrEq{"minMerge(first_seen_at)": r.nowFn().Add(-req.Duration.Abs())}) + } else if !req.TimeRange.IsEmpty() { // new by time range + subQ = subQ.Having(r.timeRangeCond("minMerge(first_seen_at)", req.TimeRange)) } q := sq.Select("count()").FromSelect(subQ, "subQ") @@ -330,13 +329,13 @@ func (r *repository) GetTopErrorGroups( where["source"] = *req.Source } - if req.Duration != nil && *req.Duration != 0 { + if !req.TimeRange.IsEmpty() { counts, err := r.getErrorCounts(ctx, getErrorCountsParams{ - duration: req.Duration, - where: where, - orderBy: "count DESC", - limit: uint64(req.Limit), - offset: uint64(req.Offset), + tr: req.TimeRange, + where: where, + orderBy: "count DESC", + limit: uint64(req.Limit), + offset: uint64(req.Offset), }) if err != nil { return nil, err @@ -427,9 +426,9 @@ func (r *repository) GetTopErrorGroupsTotal( } return r.getTotal(ctx, getTotalParams{ - table: "error_groups_brief", - where: where, - duration: req.Duration, + table: "error_groups_brief", + where: where, + tr: req.TimeRange, }) } @@ -437,7 +436,7 @@ func (r *repository) GetErrorHist( ctx context.Context, req types.GetErrorHistRequest, ) (types.ErrorHist, error) { - histData := getHistData(req.Duration) + histData := r.getHistData(req.TimeRange) q := sq. Select( @@ -466,8 +465,8 @@ func (r *repository) GetErrorHist( if req.Release != nil && *req.Release != "" { q = q.Where(sq.Eq{"release": *req.Release}) } - if req.Duration != nil && *req.Duration != 0 { - q = q.Where(sq.GtOrEq{histData.column: r.nowFn().Add(-req.Duration.Abs())}) + if !req.TimeRange.IsEmpty() { + q = q.Where(r.timeRangeCond(histData.column, req.TimeRange)) } query, args := q.MustSql() @@ -858,12 +857,12 @@ func (r *repository) DiffByReleasesTotal( } type getHashSubQueryParams struct { - table string - where sq.Eq - duration *time.Duration - orderBy string - limit uint64 - offset uint64 + table string + where sq.Eq + tr *types.TimeRange + orderBy string + limit uint64 + offset uint64 } func (r *repository) getHashSubQuery(params getHashSubQueryParams) sq.SelectBuilder { @@ -876,8 +875,8 @@ func (r *repository) getHashSubQuery(params getHashSubQueryParams) sq.SelectBuil Limit(params.limit). Offset(params.offset) - if params.duration != nil && *params.duration != 0 { - subQ = subQ.Having(sq.GtOrEq{"maxMerge(last_seen_at)": r.nowFn().Add(-params.duration.Abs())}) + if !params.tr.IsEmpty() { + subQ = subQ.Having(r.timeRangeCond("maxMerge(last_seen_at)", params.tr)) } if r.sharded { subQ = subQ.Distinct() @@ -887,9 +886,9 @@ func (r *repository) getHashSubQuery(params getHashSubQueryParams) sq.SelectBuil } type getTotalParams struct { - table string - where sq.Eq - duration *time.Duration + table string + where sq.Eq + tr *types.TimeRange } func (r *repository) getTotal( @@ -901,9 +900,9 @@ func (r *repository) getTotal( Where(params.where) var table string - if dur := params.duration; dur != nil && *dur != 0 { - histData := getHistData(dur) - q = q.Where(sq.GtOrEq{histData.column: r.nowFn().Add(-dur.Abs())}) + if tr := params.tr; !tr.IsEmpty() { + histData := r.getHistData(tr) + q = q.Where(r.timeRangeCond(histData.column, tr)) table = histData.table } else { @@ -1022,18 +1021,18 @@ func (c errorCounts) mapByHash() map[uint64]errorCount { } type getErrorCountsParams struct { - duration *time.Duration - where sq.Eq - orderBy string - limit uint64 - offset uint64 + tr *types.TimeRange + where sq.Eq + orderBy string + limit uint64 + offset uint64 } func (r *repository) getErrorCounts( ctx context.Context, params getErrorCountsParams, ) (errorCounts, error) { - histData := getHistData(params.duration) + histData := r.getHistData(params.tr) q := sq. Select( @@ -1042,7 +1041,7 @@ func (r *repository) getErrorCounts( ). From(histData.table). Where(params.where). - Where(sq.GtOrEq{histData.column: r.nowFn().Add(-params.duration.Abs())}). + Where(r.timeRangeCond(histData.column, params.tr)). GroupBy("_group_hash") if params.orderBy != "" { @@ -1082,34 +1081,13 @@ func (r *repository) in() string { return "IN" } -func orderBy(o types.ErrorGroupsOrder, sub bool) string { - seenTotal := "seen_total DESC" - lastSeenAt := "last_seen_at DESC" - firstSeenAt := "first_seen_at" - if sub { - seenTotal = "countMerge(seen_total) DESC" - lastSeenAt = "maxMerge(last_seen_at) DESC" - firstSeenAt = "minMerge(first_seen_at)" - } - - switch o { - case types.OrderFrequent: - return seenTotal - case types.OrderLatest: - return lastSeenAt - case types.OrderOldest: - return firstSeenAt - } - return seenTotal -} - type histData struct { table string column string interval uint64 } -func getHistData(duration *time.Duration) histData { +func (r *repository) getHistData(tr *types.TimeRange) histData { const ( table_10min = "agg_events_10min" table_1d = "agg_events_1d" @@ -1125,18 +1103,36 @@ func getHistData(duration *time.Duration) histData { day = 24 * hour week = 7 * day month = 31 * day + + table_10min_TTL = 90 * day ) data := func(table, column string, interval time.Duration) histData { return histData{table: table, column: column, interval: uint64(interval.Seconds())} } - if duration == nil || *duration == 0 { + if tr.IsEmpty() { return data(table_1d, startOfMonth, month) } // try get ~30 buckets - d := *duration + var d time.Duration + if tr.IsAbsolute() { + d = tr.AbsoluteDuration() + if r.nowFn().Sub(tr.From) > table_10min_TTL { + switch { + case d <= month: + return data(table_1d, startDate, day) + case d <= 7*month: + return data(table_1d, startOfWeek, week) + default: + return data(table_1d, startOfMonth, month) + } + } + } else { + d = tr.Duration + } + switch { case d <= 5*time.Hour: return data(table_10min, startDate, _10min) @@ -1150,3 +1146,39 @@ func getHistData(duration *time.Duration) histData { return data(table_1d, startOfMonth, month) } } + +func (r *repository) timeRangeCond(column string, tr *types.TimeRange) any { + if tr.IsEmpty() { + return nil + } + + if tr.IsAbsolute() { + return sq.And{ + sq.GtOrEq{column: tr.From}, + sq.LtOrEq{column: tr.To}, + } + } + + return sq.GtOrEq{column: r.nowFn().Add(-tr.Duration.Abs())} +} + +func orderBy(o types.ErrorGroupsOrder, sub bool) string { + seenTotal := "seen_total DESC" + lastSeenAt := "last_seen_at DESC" + firstSeenAt := "first_seen_at" + if sub { + seenTotal = "countMerge(seen_total) DESC" + lastSeenAt = "maxMerge(last_seen_at) DESC" + firstSeenAt = "minMerge(first_seen_at)" + } + + switch o { + case types.OrderFrequent: + return seenTotal + case types.OrderLatest: + return lastSeenAt + case types.OrderOldest: + return firstSeenAt + } + return seenTotal +} diff --git a/internal/pkg/repository_ch/error_groups_test.go b/internal/pkg/repository_ch/error_groups_test.go index f2d48bd..4cf3048 100644 --- a/internal/pkg/repository_ch/error_groups_test.go +++ b/internal/pkg/repository_ch/error_groups_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + sq "github.com/n-r-w/squirrel" "github.com/stretchr/testify/require" "github.com/ozontech/seq-ui/internal/app/types" @@ -22,17 +23,17 @@ func TestGetHistData(t *testing.T) { month = 31 * day ) + fakeNow := fakeNow(time.Now()) + tests := []struct { name string - duration time.Duration - + tr *types.TimeRange want histData }{ { name: "nil", - duration: -1, want: histData{ table: "agg_events_1d", column: "toStartOfMonth(start_date)", @@ -40,9 +41,9 @@ func TestGetHistData(t *testing.T) { }, }, { - name: "zero", + name: "empty", - duration: 0, + tr: &types.TimeRange{}, want: histData{ table: "agg_events_1d", column: "toStartOfMonth(start_date)", @@ -50,9 +51,11 @@ func TestGetHistData(t *testing.T) { }, }, { - name: "5_hour", + name: "duration_5_hour", - duration: 5 * time.Hour, + tr: &types.TimeRange{ + Duration: 5 * time.Hour, + }, want: histData{ table: "agg_events_10min", column: "start_date", @@ -60,9 +63,11 @@ func TestGetHistData(t *testing.T) { }, }, { - name: "1_day", + name: "duration_1_day", - duration: day, + tr: &types.TimeRange{ + Duration: day, + }, want: histData{ table: "agg_events_10min", column: "toStartOfHour(start_date)", @@ -70,9 +75,11 @@ func TestGetHistData(t *testing.T) { }, }, { - name: "1_month", + name: "duration_1_month", - duration: month, + tr: &types.TimeRange{ + Duration: month, + }, want: histData{ table: "agg_events_10min", column: "toStartOfDay(start_date)", @@ -80,9 +87,49 @@ func TestGetHistData(t *testing.T) { }, }, { - name: "7_month", + name: "duration_7_month", + + tr: &types.TimeRange{ + Duration: 7 * month, + }, + want: histData{ + table: "agg_events_1d", + column: "toStartOfWeek(start_date)", + interval: uint64(week.Seconds()), + }, + }, + { + name: "duration_1_year", + + tr: &types.TimeRange{ + Duration: 12 * month, + }, + want: histData{ + table: "agg_events_1d", + column: "toStartOfMonth(start_date)", + interval: uint64(month.Seconds()), + }, + }, + { + name: "absolute_old_1_month", + + tr: &types.TimeRange{ + From: fakeNow().Add(-(3*month + 14*day)), + To: fakeNow().Add(-(2*month + 14*day)), + }, + want: histData{ + table: "agg_events_1d", + column: "start_date", + interval: uint64(day.Seconds()), + }, + }, + { + name: "absolute_old_6_month", - duration: 7 * month, + tr: &types.TimeRange{ + From: fakeNow().Add(-12 * month), + To: fakeNow().Add(-6 * month), + }, want: histData{ table: "agg_events_1d", column: "toStartOfWeek(start_date)", @@ -90,27 +137,130 @@ func TestGetHistData(t *testing.T) { }, }, { - name: "1_year", + name: "absolute_old_1_year", - duration: 12 * month, + tr: &types.TimeRange{ + From: fakeNow().Add(-18 * month), + To: fakeNow().Add(-6 * month), + }, want: histData{ table: "agg_events_1d", column: "toStartOfMonth(start_date)", interval: uint64(month.Seconds()), }, }, + { + name: "absolute_new_5_hour", + + tr: &types.TimeRange{ + From: fakeNow().Add(-5 * time.Hour), + To: fakeNow(), + }, + want: histData{ + table: "agg_events_10min", + column: "start_date", + interval: uint64(_10min.Seconds()), + }, + }, + { + name: "absolute_new_1_day", + + tr: &types.TimeRange{ + From: fakeNow().Add(-day), + To: fakeNow(), + }, + want: histData{ + table: "agg_events_10min", + column: "toStartOfHour(start_date)", + interval: uint64(hour.Seconds()), + }, + }, + { + name: "absolute_new_1_month", + + tr: &types.TimeRange{ + From: fakeNow().Add(-month), + To: fakeNow(), + }, + want: histData{ + table: "agg_events_10min", + column: "toStartOfDay(start_date)", + interval: uint64(day.Seconds()), + }, + }, + { + name: "absolute_new_2_month", + + tr: &types.TimeRange{ + From: fakeNow().Add(-2 * month), + To: fakeNow(), + }, + want: histData{ + table: "agg_events_1d", + column: "toStartOfWeek(start_date)", + interval: uint64(week.Seconds()), + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() - var dur *time.Duration - if tt.duration != -1 { - dur = &tt.duration - } + r := newRepo(nil, false, nil, fakeNow) - got := getHistData(dur) + got := r.getHistData(tt.tr) + require.Equal(t, tt.want, got) + }) + } +} + +func TestTimeRangeCond(t *testing.T) { + var ( + fakeNow = fakeNow(time.Now()) + col = "test-col" + ) + + tests := []struct { + name string + + tr *types.TimeRange + want any + }{ + { + name: "nil", + + want: nil, + }, + { + name: "absolute", + + tr: &types.TimeRange{ + From: fakeNow().Add(-time.Hour), + To: fakeNow(), + }, + want: sq.And{ + sq.GtOrEq{col: fakeNow().Add(-time.Hour)}, + sq.LtOrEq{col: fakeNow()}, + }, + }, + { + name: "relative", + + tr: &types.TimeRange{ + Duration: time.Hour, + }, + want: sq.GtOrEq{col: fakeNow().Add(-time.Hour)}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + r := newRepo(nil, false, nil, fakeNow) + + got := r.timeRangeCond(col, tt.tr) require.Equal(t, tt.want, got) }) } @@ -143,7 +293,7 @@ func TestGetErrorGroups(t *testing.T) { mockConns []*mockConnRows }{ { - name: "ok_no_duration", + name: "ok_no_timerange", req: types.GetErrorGroupsRequest{ Service: service, @@ -182,14 +332,16 @@ func TestGetErrorGroups(t *testing.T) { }, }, { - name: "ok_duration_frequent", + name: "ok_timerange_relative_frequent", req: types.GetErrorGroupsRequest{ - Service: service, - Duration: &duration, - Limit: 10, - Offset: 20, - Order: types.OrderFrequent, + Service: service, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + Limit: 10, + Offset: 20, + Order: types.OrderFrequent, }, wantGroupsCount: 2, @@ -231,14 +383,68 @@ func TestGetErrorGroups(t *testing.T) { }, }, { - name: "ok_duration_not_frequent", + name: "ok_timerange_absolute_frequent", req: types.GetErrorGroupsRequest{ - Service: service, - Duration: &duration, - Limit: 10, - Offset: 20, - Order: types.OrderLatest, + Service: service, + TimeRange: &types.TimeRange{ + From: timeDiff, + To: fakeNow(), + }, + Limit: 10, + Offset: 20, + Order: types.OrderFrequent, + }, + wantGroupsCount: 2, + + mockConns: []*mockConnRows{ + { + query: "SELECT _group_hash, countMerge(counts) as count" + + " FROM agg_events_10min" + + " WHERE service = ? AND (toStartOfHour(start_date) >= ? AND toStartOfHour(start_date) <= ?)" + + " GROUP BY _group_hash" + + " ORDER BY count DESC" + + " LIMIT 10 OFFSET 20", + args: []any{service, timeDiff, fakeNow()}, + + rows: &mockRowsScanStruct{ + scanStructFns: []func(any) error{ + func(v any) error { + *v.(*errorCount) = errorCount{Hash: 123} + return nil + }, + func(v any) error { + *v.(*errorCount) = errorCount{Hash: 456} + return nil + }, + }, + }, + }, + { + query: "SELECT _group_hash, source, any(message) as message, minMerge(first_seen_at) as first_seen_at, maxMerge(last_seen_at) as last_seen_at" + + " FROM error_groups" + + " WHERE _group_hash IN (?,?) AND service = ?" + + " GROUP BY _group_hash, source", + args: []any{uint64(123), uint64(456), service}, + + rows: &mockRowsCount{ + count: 2, + isScanStruct: true, + }, + }, + }, + }, + { + name: "ok_timerange_relative_not_frequent", + + req: types.GetErrorGroupsRequest{ + Service: service, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + Limit: 10, + Offset: 20, + Order: types.OrderLatest, }, wantGroupsCount: 2, @@ -291,6 +497,70 @@ func TestGetErrorGroups(t *testing.T) { }, }, }, + { + name: "ok_timerange_absolute_not_frequent", + + req: types.GetErrorGroupsRequest{ + Service: service, + TimeRange: &types.TimeRange{ + From: timeDiff, + To: fakeNow(), + }, + Limit: 10, + Offset: 20, + Order: types.OrderLatest, + }, + wantGroupsCount: 2, + + mockConns: []*mockConnRows{ + { + query: fmt.Sprintf( + "SELECT _group_hash, source, any(message) as message, minMerge(first_seen_at) as first_seen_at, maxMerge(last_seen_at) as last_seen_at"+ + " FROM error_groups"+ + " WHERE service = ? AND _group_hash IN (%s)"+ + " GROUP BY _group_hash, source"+ + " ORDER BY last_seen_at DESC", + + "SELECT _group_hash"+ + " FROM error_groups"+ + " WHERE service = ?"+ + " GROUP BY _group_hash"+ + " HAVING (maxMerge(last_seen_at) >= ? AND maxMerge(last_seen_at) <= ?)"+ + " ORDER BY maxMerge(last_seen_at) DESC"+ + " LIMIT 10 OFFSET 20", + ), + args: []any{ + service, + service, timeDiff, fakeNow(), + }, + + rows: &mockRowsScanStruct{ + scanStructFns: []func(any) error{ + func(v any) error { + *v.(*errorInfo) = errorInfo{Hash: 123} + return nil + }, + func(v any) error { + *v.(*errorInfo) = errorInfo{Hash: 456} + return nil + }, + }, + }, + }, + { + query: "SELECT _group_hash, countMerge(counts) as count" + + " FROM agg_events_10min" + + " WHERE _group_hash IN (?,?) AND service = ? AND (toStartOfHour(start_date) >= ? AND toStartOfHour(start_date) <= ?)" + + " GROUP BY _group_hash", + args: []any{uint64(123), uint64(456), service, timeDiff, fakeNow()}, + + rows: &mockRowsCount{ + count: 2, + isScanStruct: true, + }, + }, + }, + }, { name: "ok_full_filters_sharded", @@ -354,11 +624,13 @@ func TestGetErrorGroups(t *testing.T) { }, }, { - name: "ok_no_rows_duration_frequent", + name: "ok_no_rows_timerange_frequent", req: types.GetErrorGroupsRequest{ - Duration: &duration, - Order: types.OrderFrequent, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + Order: types.OrderFrequent, }, wantGroupsCount: 0, @@ -371,11 +643,13 @@ func TestGetErrorGroups(t *testing.T) { }, }, { - name: "ok_no_rows_duration_no_frequent", + name: "ok_no_rows_timerange_no_frequent", req: types.GetErrorGroupsRequest{ - Duration: &duration, - Order: types.OrderLatest, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + Order: types.OrderLatest, }, wantGroupsCount: 0, @@ -470,15 +744,51 @@ func TestGetErrorGroupsTotal(t *testing.T) { args: []any{service}, }, }, + { + name: "ok_timerange_relative", + + req: types.GetErrorGroupsRequest{ + Service: service, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + }, + + mockConn: &mockConnRow{ + query: "SELECT uniq(_group_hash)" + + " FROM agg_events_10min" + + " WHERE service = ? AND toStartOfHour(start_date) >= ?", + + args: []any{service, timeDiff}, + }, + }, + { + name: "ok_timerange_absolute", + + req: types.GetErrorGroupsRequest{ + Service: service, + TimeRange: &types.TimeRange{ + From: timeDiff, + To: fakeNow(), + }, + }, + + mockConn: &mockConnRow{ + query: "SELECT uniq(_group_hash)" + + " FROM agg_events_10min" + + " WHERE service = ? AND (toStartOfHour(start_date) >= ? AND toStartOfHour(start_date) <= ?)", + + args: []any{service, timeDiff, fakeNow()}, + }, + }, { name: "ok_full_filters", req: types.GetErrorGroupsRequest{ - Service: service, - Env: &env, - Source: &source, - Release: &release, - Duration: &duration, + Service: service, + Env: &env, + Source: &source, + Release: &release, }, queryFilter: map[string]string{ @@ -488,9 +798,9 @@ func TestGetErrorGroupsTotal(t *testing.T) { mockConn: &mockConnRow{ query: "SELECT uniq(_group_hash)" + - " FROM agg_events_10min" + - " WHERE env = ? AND filter1 = ? AND filter2 = ? AND release = ? AND service = ? AND source = ? AND toStartOfHour(start_date) >= ?", - args: []any{env, "value1", "value2", release, service, source, timeDiff}, + " FROM error_groups" + + " WHERE env = ? AND filter1 = ? AND filter2 = ? AND release = ? AND service = ? AND source = ?", + args: []any{env, "value1", "value2", release, service, source}, }, }, { @@ -561,12 +871,14 @@ func TestGetNewErrorGroups(t *testing.T) { name: "ok_by_releases", req: types.GetErrorGroupsRequest{ - Service: service, - Release: &release, - Duration: &duration, - Limit: 20, - Offset: 5, - Order: types.OrderFrequent, + Service: service, + Release: &release, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + Limit: 20, + Offset: 5, + Order: types.OrderFrequent, }, wantGroupsCount: 2, @@ -598,13 +910,15 @@ func TestGetNewErrorGroups(t *testing.T) { }, }, { - name: "ok_by_duration", + name: "ok_by_timerange_relative", req: types.GetErrorGroupsRequest{ - Service: service, - Duration: &duration, - Limit: 10, - Order: types.OrderLatest, + Service: service, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + Limit: 10, + Order: types.OrderLatest, }, wantGroupsCount: 2, @@ -635,17 +949,60 @@ func TestGetNewErrorGroups(t *testing.T) { }, }, }, + { + name: "ok_by_timerange_absolute", + + req: types.GetErrorGroupsRequest{ + Service: service, + TimeRange: &types.TimeRange{ + From: timeDiff, + To: fakeNow(), + }, + Limit: 10, + Order: types.OrderLatest, + }, + wantGroupsCount: 2, + + mockConn: &mockConnRows{ + query: fmt.Sprintf( + "SELECT _group_hash, source, any(message) as message, countMerge(seen_total) as seen_total, minMerge(first_seen_at) as first_seen_at, maxMerge(last_seen_at) as last_seen_at"+ + " FROM error_groups"+ + " WHERE service = ? AND _group_hash IN (%s)"+ + " GROUP BY _group_hash, source"+ + " ORDER BY last_seen_at DESC", + + "SELECT _group_hash"+ + " FROM error_groups"+ + " WHERE service = ?"+ + " GROUP BY _group_hash"+ + " HAVING (minMerge(first_seen_at) >= ? AND minMerge(first_seen_at) <= ?)"+ + " ORDER BY maxMerge(last_seen_at) DESC"+ + " LIMIT 10 OFFSET 0", + ), + args: []any{ + service, + service, timeDiff, fakeNow(), + }, + + rows: &mockRowsCount{ + count: 2, + isScanStruct: true, + }, + }, + }, { name: "ok_full_filters_sharded", req: types.GetErrorGroupsRequest{ - Service: service, - Env: &env, - Source: &source, - Duration: &duration, - Limit: 10, - Offset: 20, - Order: types.OrderOldest, + Service: service, + Env: &env, + Source: &source, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + Limit: 10, + Offset: 20, + Order: types.OrderOldest, }, wantGroupsCount: 2, @@ -762,9 +1119,11 @@ func TestGetNewErrorGroupsTotal(t *testing.T) { name: "ok_by_releases", req: types.GetErrorGroupsRequest{ - Service: service, - Release: &release, - Duration: &duration, + Service: service, + Release: &release, + TimeRange: &types.TimeRange{ + Duration: duration, + }, }, mockConn: &mockConnRow{ @@ -781,11 +1140,13 @@ func TestGetNewErrorGroupsTotal(t *testing.T) { }, }, { - name: "ok_by_duration", + name: "ok_by_timerange_relative", req: types.GetErrorGroupsRequest{ - Service: service, - Duration: &duration, + Service: service, + TimeRange: &types.TimeRange{ + Duration: duration, + }, }, mockConn: &mockConnRow{ @@ -801,14 +1162,40 @@ func TestGetNewErrorGroupsTotal(t *testing.T) { args: []any{service, timeDiff}, }, }, + { + name: "ok_by_timerange_absolute", + + req: types.GetErrorGroupsRequest{ + Service: service, + TimeRange: &types.TimeRange{ + From: timeDiff, + To: fakeNow(), + }, + }, + + mockConn: &mockConnRow{ + query: fmt.Sprintf( + "SELECT count() FROM (%s) AS subQ", + + "SELECT _group_hash"+ + " FROM error_groups"+ + " WHERE service = ?"+ + " GROUP BY _group_hash"+ + " HAVING (minMerge(first_seen_at) >= ? AND minMerge(first_seen_at) <= ?)", + ), + args: []any{service, timeDiff, fakeNow()}, + }, + }, { name: "ok_full_filters", req: types.GetErrorGroupsRequest{ - Service: service, - Duration: &duration, - Env: &env, - Source: &source, + Service: service, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + Env: &env, + Source: &source, }, queryFilter: map[string]string{ @@ -892,7 +1279,7 @@ func TestGetTopErrorGroups(t *testing.T) { mockConns []*mockConnRows }{ { - name: "ok_no_duration", + name: "ok_no_timerange", req: types.GetTopErrorGroupsRequest{ Limit: 10, @@ -926,12 +1313,14 @@ func TestGetTopErrorGroups(t *testing.T) { }, }, { - name: "ok_duration", + name: "ok_timerange_relative", req: types.GetTopErrorGroupsRequest{ - Duration: &duration, - Limit: 10, - Offset: 20, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + Limit: 10, + Offset: 20, }, wantGroupsCount: 2, @@ -972,6 +1361,56 @@ func TestGetTopErrorGroups(t *testing.T) { }, }, }, + { + name: "ok_timerange_absolute", + + req: types.GetTopErrorGroupsRequest{ + TimeRange: &types.TimeRange{ + From: timeDiff, + To: fakeNow(), + }, + Limit: 10, + Offset: 20, + }, + wantGroupsCount: 2, + + mockConns: []*mockConnRows{ + { + query: "SELECT _group_hash, countMerge(counts) as count" + + " FROM agg_events_10min" + + " WHERE (1=1) AND (toStartOfHour(start_date) >= ? AND toStartOfHour(start_date) <= ?)" + + " GROUP BY _group_hash" + + " ORDER BY count DESC" + + " LIMIT 10 OFFSET 20", + args: []any{timeDiff, fakeNow()}, + + rows: &mockRowsScanStruct{ + scanStructFns: []func(any) error{ + func(v any) error { + *v.(*errorCount) = errorCount{Hash: 123} + return nil + }, + func(v any) error { + *v.(*errorCount) = errorCount{Hash: 456} + return nil + }, + }, + }, + }, + { + query: "SELECT _group_hash, source, any(message) as message" + + " FROM error_groups" + + " WHERE _group_hash IN (?,?)" + + " GROUP BY _group_hash, source", + args: []any{uint64(123), uint64(456)}, + + rows: &mockRowsCount{ + count: 2, + isScanStruct: true, + }, + }, + }, + }, { name: "ok_full_filters_sharded", @@ -1018,7 +1457,7 @@ func TestGetTopErrorGroups(t *testing.T) { }, }, { - name: "ok_no_rows_no_duration", + name: "ok_no_rows_no_timerange", req: types.GetTopErrorGroupsRequest{}, wantGroupsCount: 0, @@ -1032,10 +1471,12 @@ func TestGetTopErrorGroups(t *testing.T) { }, }, { - name: "ok_no_rows_duration", + name: "ok_no_rows_timerange", req: types.GetTopErrorGroupsRequest{ - Duration: &duration, + TimeRange: &types.TimeRange{ + Duration: duration, + }, }, wantGroupsCount: 0, @@ -1114,7 +1555,7 @@ func TestGetTopErrorGroupsTotal(t *testing.T) { mockConn *mockConnRow }{ { - name: "ok_no_duration", + name: "ok_no_timerange", req: types.GetTopErrorGroupsRequest{}, @@ -1127,10 +1568,12 @@ func TestGetTopErrorGroupsTotal(t *testing.T) { }, }, { - name: "ok_duration", + name: "ok_timerange_relative", req: types.GetTopErrorGroupsRequest{ - Duration: &duration, + TimeRange: &types.TimeRange{ + Duration: duration, + }, }, mockConn: &mockConnRow{ @@ -1141,6 +1584,25 @@ func TestGetTopErrorGroupsTotal(t *testing.T) { args: []any{timeDiff}, }, }, + { + name: "ok_timerange_absolute", + + req: types.GetTopErrorGroupsRequest{ + TimeRange: &types.TimeRange{ + From: timeDiff, + To: fakeNow(), + }, + }, + + mockConn: &mockConnRow{ + query: "" + + "SELECT uniq(_group_hash)" + + " FROM agg_events_10min" + + " WHERE (1=1) AND (toStartOfHour(start_date) >= ? AND toStartOfHour(start_date) <= ?)", + + args: []any{timeDiff, fakeNow()}, + }, + }, { name: "ok_full_filters", @@ -1645,6 +2107,55 @@ func TestGetErrorHist(t *testing.T) { }, }, }, + { + name: "ok_timerange_relative", + + req: types.GetErrorHistRequest{ + TimeRange: &types.TimeRange{ + Duration: duration, + }, + }, + wantBucketsCount: 2, + + mockConn: &mockConnRows{ + query: "" + + "SELECT start_date, countMerge(counts) as counts" + + " FROM agg_events_10min" + + " WHERE start_date >= ?" + + " GROUP BY start_date" + + " ORDER BY start_date", + args: []any{timeDiff}, + + rows: &mockRowsCount{ + count: 2, + }, + }, + }, + { + name: "ok_timerange_absolute", + + req: types.GetErrorHistRequest{ + TimeRange: &types.TimeRange{ + From: timeDiff, + To: fakeNow(), + }, + }, + wantBucketsCount: 2, + + mockConn: &mockConnRows{ + query: "" + + "SELECT start_date, countMerge(counts) as counts" + + " FROM agg_events_10min" + + " WHERE (start_date >= ? AND start_date <= ?)" + + " GROUP BY start_date" + + " ORDER BY start_date", + args: []any{timeDiff, fakeNow()}, + + rows: &mockRowsCount{ + count: 2, + }, + }, + }, { name: "ok_full_filters", @@ -1654,7 +2165,9 @@ func TestGetErrorHist(t *testing.T) { Env: &env, Source: &source, Release: &release, - Duration: &duration, + TimeRange: &types.TimeRange{ + Duration: duration, + }, }, wantBucketsCount: 2, diff --git a/internal/pkg/service/errorgroups/service.go b/internal/pkg/service/errorgroups/service.go index 5cdf509..f6af295 100644 --- a/internal/pkg/service/errorgroups/service.go +++ b/internal/pkg/service/errorgroups/service.go @@ -54,11 +54,10 @@ func (s *service) GetNewErrorGroups( ctx context.Context, req types.GetErrorGroupsRequest, ) ([]types.ErrorGroup, uint64, error) { - // If the release and duration are not specified, + // If the release and time range are not specified, // then we are looking for errors for all time and releases. // In this case, we believe that there are no new errors. - if (req.Release == nil || *req.Release == "") && - (req.Duration == nil || *req.Duration == 0) { + if (req.Release == nil || *req.Release == "") && req.TimeRange.IsEmpty() { return nil, 0, nil } @@ -75,6 +74,10 @@ func getErrorGroups( return nil, 0, types.NewErrInvalidRequestField("'service' must not be empty") } + if err := validateTimeRange(req.TimeRange); err != nil { + return nil, 0, types.NewErrInvalidRequestField(err.Error()) + } + if req.Limit == 0 { req.Limit = defaultLimit } @@ -109,6 +112,10 @@ func (s *service) GetTopErrorGroups( ctx context.Context, req types.GetTopErrorGroupsRequest, ) ([]types.TopErrorGroup, uint64, error) { + if err := validateTimeRange(req.TimeRange); err != nil { + return nil, 0, types.NewErrInvalidRequestField(err.Error()) + } + if req.Limit == 0 { req.Limit = defaultLimit } @@ -143,6 +150,10 @@ func (s *service) GetHist( ctx context.Context, req types.GetErrorHistRequest, ) (types.ErrorHist, error) { + if err := validateTimeRange(req.TimeRange); err != nil { + return types.ErrorHist{}, types.NewErrInvalidRequestField(err.Error()) + } + return s.repo.GetErrorHist(ctx, req) } @@ -292,3 +303,29 @@ func (s *service) DiffByReleases( return groups, total, err } + +func validateTimeRange(tr *types.TimeRange) error { + if tr == nil { + return nil + } + + err := func(s string) error { + return fmt.Errorf("validate timerange failed: %s", s) + } + + if tr.Duration != 0 && (!tr.From.IsZero() || !tr.To.IsZero()) { + return err("only one of 'duration' or 'from'/'to' must be specified") + } + if tr.Duration == 0 { + if tr.From.IsZero() && tr.To.IsZero() { + return err("at least one of 'duration' or 'from'/'to' must be specified") + } + if tr.From.IsZero() && !tr.To.IsZero() || !tr.From.IsZero() && tr.To.IsZero() { + return err("both 'from'/'to' must be specified") + } + if tr.From.Equal(tr.To) || tr.From.After(tr.To) { + return err("'from' should be before 'to'") + } + } + return nil +} diff --git a/internal/pkg/service/errorgroups/service_test.go b/internal/pkg/service/errorgroups/service_test.go index 5170a62..be857e9 100644 --- a/internal/pkg/service/errorgroups/service_test.go +++ b/internal/pkg/service/errorgroups/service_test.go @@ -15,6 +15,97 @@ import ( mock "github.com/ozontech/seq-ui/internal/pkg/repository_ch/mock" ) +func TestValidateTimeRange(t *testing.T) { + var ( + dur = time.Second + to = time.Now() + from = to.Add(-2 * time.Second) + ) + + tests := []struct { + name string + + tr *types.TimeRange + wantErr string + }{ + { + name: "ok_nil", + tr: nil, + }, + { + name: "ok_duration", + tr: &types.TimeRange{ + Duration: dur, + }, + }, + { + name: "ok_from_to", + tr: &types.TimeRange{ + From: from, + To: to, + }, + }, + { + name: "err_both", + tr: &types.TimeRange{ + Duration: dur, + From: from, + To: to, + }, + wantErr: "only one of 'duration' or 'from'/'to' must be specified", + }, + { + name: "err_empty", + tr: &types.TimeRange{}, + wantErr: "at least one of 'duration' or 'from'/'to' must be specified", + }, + { + name: "err_only_from", + tr: &types.TimeRange{ + From: from, + }, + wantErr: "both 'from'/'to' must be specified", + }, + { + name: "err_only_to", + tr: &types.TimeRange{ + To: to, + }, + wantErr: "both 'from'/'to' must be specified", + }, + { + name: "err_from_to_equal", + tr: &types.TimeRange{ + From: from, + To: from, + }, + wantErr: "'from' should be before 'to'", + }, + { + name: "err_from_before_to", + tr: &types.TimeRange{ + From: to, + To: from, + }, + wantErr: "'from' should be before 'to'", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + err := validateTimeRange(tt.tr) + if tt.wantErr == "" { + require.NoError(t, err) + return + } + + require.Contains(t, err.Error(), tt.wantErr) + }) + } +} + func TestGetErrorGroups(t *testing.T) { var ( service = "test-svc" @@ -100,12 +191,47 @@ func TestGetErrorGroups(t *testing.T) { total: 100, }, }, + { + name: "ok_time_range", + + req: types.GetErrorGroupsRequest{ + Service: service, + Limit: 10, + TimeRange: &types.TimeRange{ + Duration: time.Minute, + }, + }, + wantGroupsCount: 10, + wantTotal: 0, + + mockArgs: &mockArgs{ + req: types.GetErrorGroupsRequest{ + Service: service, + Limit: 10, + TimeRange: &types.TimeRange{ + Duration: time.Minute, + }, + }, + + groupsCount: 10, + total: 0, + }, + }, { name: "err_no_service", req: types.GetErrorGroupsRequest{}, wantErr: true, }, + { + name: "err_timerange", + + req: types.GetErrorGroupsRequest{ + Service: service, + TimeRange: &types.TimeRange{}, + }, + wantErr: true, + }, { name: "err_repo_groups", @@ -228,20 +354,18 @@ func TestGetNewErrorGroups(t *testing.T) { name: "ok_limit", req: types.GetErrorGroupsRequest{ - Service: service, - Release: &release, - Duration: &duration, - Limit: 5, + Service: service, + Release: &release, + Limit: 5, }, wantGroupsCount: 5, wantTotal: 0, mockArgs: &mockArgs{ req: types.GetErrorGroupsRequest{ - Service: service, - Release: &release, - Duration: &duration, - Limit: 5, + Service: service, + Release: &release, + Limit: 5, }, groupsCount: 5, @@ -251,19 +375,17 @@ func TestGetNewErrorGroups(t *testing.T) { name: "ok_no_limit", req: types.GetErrorGroupsRequest{ - Service: service, - Release: &release, - Duration: &duration, + Service: service, + Release: &release, }, wantGroupsCount: 10, wantTotal: 0, mockArgs: &mockArgs{ req: types.GetErrorGroupsRequest{ - Service: service, - Release: &release, - Duration: &duration, - Limit: defaultLimit, + Service: service, + Release: &release, + Limit: defaultLimit, }, groupsCount: 10, @@ -275,7 +397,6 @@ func TestGetNewErrorGroups(t *testing.T) { req: types.GetErrorGroupsRequest{ Service: service, Release: &release, - Duration: &duration, Limit: 10, WithTotal: true, }, @@ -286,7 +407,6 @@ func TestGetNewErrorGroups(t *testing.T) { req: types.GetErrorGroupsRequest{ Service: service, Release: &release, - Duration: &duration, Limit: 10, WithTotal: true, }, @@ -295,12 +415,47 @@ func TestGetNewErrorGroups(t *testing.T) { total: 100, }, }, + { + name: "ok_timerange", + + req: types.GetErrorGroupsRequest{ + Service: service, + Release: &release, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + Limit: 10, + }, + wantGroupsCount: 10, + + mockArgs: &mockArgs{ + req: types.GetErrorGroupsRequest{ + Service: service, + Release: &release, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + Limit: 10, + }, + + groupsCount: 10, + }, + }, { name: "err_no_service", req: types.GetErrorGroupsRequest{ - Release: &release, - Duration: &duration, + Release: &release, + }, + wantErr: true, + }, + { + name: "err_timerange", + + req: types.GetErrorGroupsRequest{ + Service: service, + Release: &release, + TimeRange: &types.TimeRange{}, }, wantErr: true, }, @@ -310,7 +465,6 @@ func TestGetNewErrorGroups(t *testing.T) { req: types.GetErrorGroupsRequest{ Service: service, Release: &release, - Duration: &duration, Limit: 10, WithTotal: true, }, @@ -320,7 +474,6 @@ func TestGetNewErrorGroups(t *testing.T) { req: types.GetErrorGroupsRequest{ Service: service, Release: &release, - Duration: &duration, Limit: 10, WithTotal: true, }, @@ -335,7 +488,6 @@ func TestGetNewErrorGroups(t *testing.T) { req: types.GetErrorGroupsRequest{ Service: service, Release: &release, - Duration: &duration, Limit: 10, WithTotal: true, }, @@ -345,7 +497,6 @@ func TestGetNewErrorGroups(t *testing.T) { req: types.GetErrorGroupsRequest{ Service: service, Release: &release, - Duration: &duration, Limit: 10, WithTotal: true, }, @@ -396,7 +547,10 @@ func TestGetNewErrorGroups(t *testing.T) { } func TestGetTopErrorGroups(t *testing.T) { - var someErr = errors.New("some err") + var ( + duration = time.Hour + someErr = errors.New("some err") + ) type mockArgs struct { req types.GetTopErrorGroupsRequest @@ -470,6 +624,36 @@ func TestGetTopErrorGroups(t *testing.T) { total: 100, }, }, + { + name: "ok_timerange", + + req: types.GetTopErrorGroupsRequest{ + Limit: 10, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + }, + wantGroupsCount: 10, + + mockArgs: &mockArgs{ + req: types.GetTopErrorGroupsRequest{ + Limit: 10, + TimeRange: &types.TimeRange{ + Duration: duration, + }, + }, + + groupsCount: 10, + }, + }, + { + name: "err_timerange", + + req: types.GetTopErrorGroupsRequest{ + TimeRange: &types.TimeRange{}, + }, + wantErr: true, + }, { name: "err_repo_groups", @@ -550,7 +734,10 @@ func TestGetTopErrorGroups(t *testing.T) { } func TestGetHist(t *testing.T) { - var someErr = errors.New("some err") + var ( + duration = time.Hour + someErr = errors.New("some err") + ) type mockArgs struct { req types.GetErrorHistRequest @@ -580,6 +767,34 @@ func TestGetHist(t *testing.T) { bucketsCount: 50, }, }, + { + name: "ok_timerange", + + req: types.GetErrorHistRequest{ + TimeRange: &types.TimeRange{ + Duration: duration, + }, + }, + wantBucketsCount: 50, + + mockArgs: &mockArgs{ + req: types.GetErrorHistRequest{ + TimeRange: &types.TimeRange{ + Duration: duration, + }, + }, + + bucketsCount: 50, + }, + }, + { + name: "err_timerange", + + req: types.GetErrorHistRequest{ + TimeRange: &types.TimeRange{}, + }, + wantErr: true, + }, { name: "err_repo", diff --git a/pkg/errorgroups/v1/errorgroups.pb.go b/pkg/errorgroups/v1/errorgroups.pb.go index 1c055a7..cc0e2c1 100644 --- a/pkg/errorgroups/v1/errorgroups.pb.go +++ b/pkg/errorgroups/v1/errorgroups.pb.go @@ -71,14 +71,78 @@ func (Order) EnumDescriptor() ([]byte, []int) { return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{0} } +type TimeRange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Duration *durationpb.Duration `protobuf:"bytes,1,opt,name=duration,proto3" json:"duration,omitempty"` + From *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=from,proto3" json:"from,omitempty"` + To *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=to,proto3" json:"to,omitempty"` +} + +func (x *TimeRange) Reset() { + *x = TimeRange{} + if protoimpl.UnsafeEnabled { + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TimeRange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TimeRange) ProtoMessage() {} + +func (x *TimeRange) ProtoReflect() protoreflect.Message { + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TimeRange.ProtoReflect.Descriptor instead. +func (*TimeRange) Descriptor() ([]byte, []int) { + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{0} +} + +func (x *TimeRange) GetDuration() *durationpb.Duration { + if x != nil { + return x.Duration + } + return nil +} + +func (x *TimeRange) GetFrom() *timestamppb.Timestamp { + if x != nil { + return x.From + } + return nil +} + +func (x *TimeRange) GetTo() *timestamppb.Timestamp { + if x != nil { + return x.To + } + return nil +} + type GetGroupsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` - Env *string `protobuf:"bytes,2,opt,name=env,proto3,oneof" json:"env,omitempty"` - Release *string `protobuf:"bytes,3,opt,name=release,proto3,oneof" json:"release,omitempty"` + Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` + Env *string `protobuf:"bytes,2,opt,name=env,proto3,oneof" json:"env,omitempty"` + Release *string `protobuf:"bytes,3,opt,name=release,proto3,oneof" json:"release,omitempty"` + // Deprecated: Marked as deprecated in errorgroups/v1/errorgroups.proto. Duration *durationpb.Duration `protobuf:"bytes,4,opt,name=duration,proto3" json:"duration,omitempty"` Limit uint32 `protobuf:"varint,5,opt,name=limit,proto3" json:"limit,omitempty"` Offset uint32 `protobuf:"varint,6,opt,name=offset,proto3" json:"offset,omitempty"` @@ -86,12 +150,13 @@ type GetGroupsRequest struct { WithTotal bool `protobuf:"varint,8,opt,name=with_total,json=withTotal,proto3" json:"with_total,omitempty"` Source *string `protobuf:"bytes,9,opt,name=source,proto3,oneof" json:"source,omitempty"` Filter *GetGroupsRequest_Filter `protobuf:"bytes,10,opt,name=filter,proto3,oneof" json:"filter,omitempty"` + TimeRange *TimeRange `protobuf:"bytes,11,opt,name=time_range,json=timeRange,proto3,oneof" json:"time_range,omitempty"` } func (x *GetGroupsRequest) Reset() { *x = GetGroupsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[0] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -104,7 +169,7 @@ func (x *GetGroupsRequest) String() string { func (*GetGroupsRequest) ProtoMessage() {} func (x *GetGroupsRequest) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[0] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -117,7 +182,7 @@ func (x *GetGroupsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetGroupsRequest.ProtoReflect.Descriptor instead. func (*GetGroupsRequest) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{0} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{1} } func (x *GetGroupsRequest) GetService() string { @@ -141,6 +206,7 @@ func (x *GetGroupsRequest) GetRelease() string { return "" } +// Deprecated: Marked as deprecated in errorgroups/v1/errorgroups.proto. func (x *GetGroupsRequest) GetDuration() *durationpb.Duration { if x != nil { return x.Duration @@ -190,6 +256,13 @@ func (x *GetGroupsRequest) GetFilter() *GetGroupsRequest_Filter { return nil } +func (x *GetGroupsRequest) GetTimeRange() *TimeRange { + if x != nil { + return x.TimeRange + } + return nil +} + type GetGroupsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -202,7 +275,7 @@ type GetGroupsResponse struct { func (x *GetGroupsResponse) Reset() { *x = GetGroupsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[1] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -215,7 +288,7 @@ func (x *GetGroupsResponse) String() string { func (*GetGroupsResponse) ProtoMessage() {} func (x *GetGroupsResponse) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[1] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -228,7 +301,7 @@ func (x *GetGroupsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetGroupsResponse.ProtoReflect.Descriptor instead. func (*GetGroupsResponse) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{1} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{2} } func (x *GetGroupsResponse) GetTotal() uint64 { @@ -250,18 +323,20 @@ type GetTopGroupsRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Env *string `protobuf:"bytes,1,opt,name=env,proto3,oneof" json:"env,omitempty"` - Source *string `protobuf:"bytes,2,opt,name=source,proto3,oneof" json:"source,omitempty"` + Env *string `protobuf:"bytes,1,opt,name=env,proto3,oneof" json:"env,omitempty"` + Source *string `protobuf:"bytes,2,opt,name=source,proto3,oneof" json:"source,omitempty"` + // Deprecated: Marked as deprecated in errorgroups/v1/errorgroups.proto. Duration *durationpb.Duration `protobuf:"bytes,3,opt,name=duration,proto3" json:"duration,omitempty"` Limit uint32 `protobuf:"varint,4,opt,name=limit,proto3" json:"limit,omitempty"` Offset uint32 `protobuf:"varint,5,opt,name=offset,proto3" json:"offset,omitempty"` WithTotal bool `protobuf:"varint,6,opt,name=with_total,json=withTotal,proto3" json:"with_total,omitempty"` + TimeRange *TimeRange `protobuf:"bytes,7,opt,name=time_range,json=timeRange,proto3,oneof" json:"time_range,omitempty"` } func (x *GetTopGroupsRequest) Reset() { *x = GetTopGroupsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[2] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -274,7 +349,7 @@ func (x *GetTopGroupsRequest) String() string { func (*GetTopGroupsRequest) ProtoMessage() {} func (x *GetTopGroupsRequest) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[2] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -287,7 +362,7 @@ func (x *GetTopGroupsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTopGroupsRequest.ProtoReflect.Descriptor instead. func (*GetTopGroupsRequest) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{2} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{3} } func (x *GetTopGroupsRequest) GetEnv() string { @@ -304,6 +379,7 @@ func (x *GetTopGroupsRequest) GetSource() string { return "" } +// Deprecated: Marked as deprecated in errorgroups/v1/errorgroups.proto. func (x *GetTopGroupsRequest) GetDuration() *durationpb.Duration { if x != nil { return x.Duration @@ -332,6 +408,13 @@ func (x *GetTopGroupsRequest) GetWithTotal() bool { return false } +func (x *GetTopGroupsRequest) GetTimeRange() *TimeRange { + if x != nil { + return x.TimeRange + } + return nil +} + type GetTopGroupsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -344,7 +427,7 @@ type GetTopGroupsResponse struct { func (x *GetTopGroupsResponse) Reset() { *x = GetTopGroupsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[3] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -357,7 +440,7 @@ func (x *GetTopGroupsResponse) String() string { func (*GetTopGroupsResponse) ProtoMessage() {} func (x *GetTopGroupsResponse) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[3] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -370,7 +453,7 @@ func (x *GetTopGroupsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTopGroupsResponse.ProtoReflect.Descriptor instead. func (*GetTopGroupsResponse) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{3} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{4} } func (x *GetTopGroupsResponse) GetTotal() uint64 { @@ -392,18 +475,20 @@ type GetHistRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Service *string `protobuf:"bytes,1,opt,name=service,proto3,oneof" json:"service,omitempty"` - GroupHash *uint64 `protobuf:"varint,2,opt,name=group_hash,json=groupHash,proto3,oneof" json:"group_hash,omitempty"` - Env *string `protobuf:"bytes,3,opt,name=env,proto3,oneof" json:"env,omitempty"` - Release *string `protobuf:"bytes,4,opt,name=release,proto3,oneof" json:"release,omitempty"` + Service *string `protobuf:"bytes,1,opt,name=service,proto3,oneof" json:"service,omitempty"` + GroupHash *uint64 `protobuf:"varint,2,opt,name=group_hash,json=groupHash,proto3,oneof" json:"group_hash,omitempty"` + Env *string `protobuf:"bytes,3,opt,name=env,proto3,oneof" json:"env,omitempty"` + Release *string `protobuf:"bytes,4,opt,name=release,proto3,oneof" json:"release,omitempty"` + // Deprecated: Marked as deprecated in errorgroups/v1/errorgroups.proto. Duration *durationpb.Duration `protobuf:"bytes,5,opt,name=duration,proto3,oneof" json:"duration,omitempty"` Source *string `protobuf:"bytes,6,opt,name=source,proto3,oneof" json:"source,omitempty"` + TimeRange *TimeRange `protobuf:"bytes,7,opt,name=time_range,json=timeRange,proto3,oneof" json:"time_range,omitempty"` } func (x *GetHistRequest) Reset() { *x = GetHistRequest{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[4] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -416,7 +501,7 @@ func (x *GetHistRequest) String() string { func (*GetHistRequest) ProtoMessage() {} func (x *GetHistRequest) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[4] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -429,7 +514,7 @@ func (x *GetHistRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetHistRequest.ProtoReflect.Descriptor instead. func (*GetHistRequest) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{4} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{5} } func (x *GetHistRequest) GetService() string { @@ -460,6 +545,7 @@ func (x *GetHistRequest) GetRelease() string { return "" } +// Deprecated: Marked as deprecated in errorgroups/v1/errorgroups.proto. func (x *GetHistRequest) GetDuration() *durationpb.Duration { if x != nil { return x.Duration @@ -474,6 +560,13 @@ func (x *GetHistRequest) GetSource() string { return "" } +func (x *GetHistRequest) GetTimeRange() *TimeRange { + if x != nil { + return x.TimeRange + } + return nil +} + type GetHistResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -486,7 +579,7 @@ type GetHistResponse struct { func (x *GetHistResponse) Reset() { *x = GetHistResponse{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[5] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -499,7 +592,7 @@ func (x *GetHistResponse) String() string { func (*GetHistResponse) ProtoMessage() {} func (x *GetHistResponse) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[5] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -512,7 +605,7 @@ func (x *GetHistResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetHistResponse.ProtoReflect.Descriptor instead. func (*GetHistResponse) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{5} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{6} } func (x *GetHistResponse) GetBuckets() []*Bucket { @@ -541,7 +634,7 @@ type Bucket struct { func (x *Bucket) Reset() { *x = Bucket{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[6] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -554,7 +647,7 @@ func (x *Bucket) String() string { func (*Bucket) ProtoMessage() {} func (x *Bucket) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[6] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -567,7 +660,7 @@ func (x *Bucket) ProtoReflect() protoreflect.Message { // Deprecated: Use Bucket.ProtoReflect.Descriptor instead. func (*Bucket) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{6} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{7} } func (x *Bucket) GetTime() *timestamppb.Timestamp { @@ -599,7 +692,7 @@ type GetDetailsRequest struct { func (x *GetDetailsRequest) Reset() { *x = GetDetailsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[7] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -612,7 +705,7 @@ func (x *GetDetailsRequest) String() string { func (*GetDetailsRequest) ProtoMessage() {} func (x *GetDetailsRequest) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[7] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -625,7 +718,7 @@ func (x *GetDetailsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDetailsRequest.ProtoReflect.Descriptor instead. func (*GetDetailsRequest) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{7} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{8} } func (x *GetDetailsRequest) GetService() string { @@ -681,7 +774,7 @@ type GetDetailsResponse struct { func (x *GetDetailsResponse) Reset() { *x = GetDetailsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[8] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -694,7 +787,7 @@ func (x *GetDetailsResponse) String() string { func (*GetDetailsResponse) ProtoMessage() {} func (x *GetDetailsResponse) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[8] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -707,7 +800,7 @@ func (x *GetDetailsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDetailsResponse.ProtoReflect.Descriptor instead. func (*GetDetailsResponse) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{8} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{9} } func (x *GetDetailsResponse) GetGroupHash() uint64 { @@ -778,7 +871,7 @@ type GetReleasesRequest struct { func (x *GetReleasesRequest) Reset() { *x = GetReleasesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[9] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -791,7 +884,7 @@ func (x *GetReleasesRequest) String() string { func (*GetReleasesRequest) ProtoMessage() {} func (x *GetReleasesRequest) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[9] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -804,7 +897,7 @@ func (x *GetReleasesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetReleasesRequest.ProtoReflect.Descriptor instead. func (*GetReleasesRequest) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{9} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{10} } func (x *GetReleasesRequest) GetService() string { @@ -832,7 +925,7 @@ type GetReleasesResponse struct { func (x *GetReleasesResponse) Reset() { *x = GetReleasesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[10] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -845,7 +938,7 @@ func (x *GetReleasesResponse) String() string { func (*GetReleasesResponse) ProtoMessage() {} func (x *GetReleasesResponse) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[10] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -858,7 +951,7 @@ func (x *GetReleasesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetReleasesResponse.ProtoReflect.Descriptor instead. func (*GetReleasesResponse) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{10} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{11} } func (x *GetReleasesResponse) GetReleases() []string { @@ -882,7 +975,7 @@ type GetServicesRequest struct { func (x *GetServicesRequest) Reset() { *x = GetServicesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[11] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -895,7 +988,7 @@ func (x *GetServicesRequest) String() string { func (*GetServicesRequest) ProtoMessage() {} func (x *GetServicesRequest) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[11] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -908,7 +1001,7 @@ func (x *GetServicesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetServicesRequest.ProtoReflect.Descriptor instead. func (*GetServicesRequest) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{11} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{12} } func (x *GetServicesRequest) GetQuery() string { @@ -950,7 +1043,7 @@ type GetServicesResponse struct { func (x *GetServicesResponse) Reset() { *x = GetServicesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[12] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -963,7 +1056,7 @@ func (x *GetServicesResponse) String() string { func (*GetServicesResponse) ProtoMessage() {} func (x *GetServicesResponse) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[12] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -976,7 +1069,7 @@ func (x *GetServicesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetServicesResponse.ProtoReflect.Descriptor instead. func (*GetServicesResponse) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{12} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{13} } func (x *GetServicesResponse) GetServices() []string { @@ -1004,7 +1097,7 @@ type DiffByReleasesRequest struct { func (x *DiffByReleasesRequest) Reset() { *x = DiffByReleasesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[13] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1017,7 +1110,7 @@ func (x *DiffByReleasesRequest) String() string { func (*DiffByReleasesRequest) ProtoMessage() {} func (x *DiffByReleasesRequest) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[13] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1030,7 +1123,7 @@ func (x *DiffByReleasesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DiffByReleasesRequest.ProtoReflect.Descriptor instead. func (*DiffByReleasesRequest) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{13} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{14} } func (x *DiffByReleasesRequest) GetService() string { @@ -1101,7 +1194,7 @@ type DiffByReleasesResponse struct { func (x *DiffByReleasesResponse) Reset() { *x = DiffByReleasesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[14] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1114,7 +1207,7 @@ func (x *DiffByReleasesResponse) String() string { func (*DiffByReleasesResponse) ProtoMessage() {} func (x *DiffByReleasesResponse) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[14] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1127,7 +1220,7 @@ func (x *DiffByReleasesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DiffByReleasesResponse.ProtoReflect.Descriptor instead. func (*DiffByReleasesResponse) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{14} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{15} } func (x *DiffByReleasesResponse) GetTotal() uint64 { @@ -1155,7 +1248,7 @@ type GetGroupsRequest_Filter struct { func (x *GetGroupsRequest_Filter) Reset() { *x = GetGroupsRequest_Filter{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[15] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1168,7 +1261,7 @@ func (x *GetGroupsRequest_Filter) String() string { func (*GetGroupsRequest_Filter) ProtoMessage() {} func (x *GetGroupsRequest_Filter) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[15] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1181,7 +1274,7 @@ func (x *GetGroupsRequest_Filter) ProtoReflect() protoreflect.Message { // Deprecated: Use GetGroupsRequest_Filter.ProtoReflect.Descriptor instead. func (*GetGroupsRequest_Filter) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{0, 0} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{1, 0} } func (x *GetGroupsRequest_Filter) GetIsNew() bool { @@ -1207,7 +1300,7 @@ type GetGroupsResponse_Group struct { func (x *GetGroupsResponse_Group) Reset() { *x = GetGroupsResponse_Group{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[16] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1220,7 +1313,7 @@ func (x *GetGroupsResponse_Group) String() string { func (*GetGroupsResponse_Group) ProtoMessage() {} func (x *GetGroupsResponse_Group) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[16] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1233,7 +1326,7 @@ func (x *GetGroupsResponse_Group) ProtoReflect() protoreflect.Message { // Deprecated: Use GetGroupsResponse_Group.ProtoReflect.Descriptor instead. func (*GetGroupsResponse_Group) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{1, 0} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{2, 0} } func (x *GetGroupsResponse_Group) GetHash() uint64 { @@ -1292,7 +1385,7 @@ type GetTopGroupsResponse_Group struct { func (x *GetTopGroupsResponse_Group) Reset() { *x = GetTopGroupsResponse_Group{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[17] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1305,7 +1398,7 @@ func (x *GetTopGroupsResponse_Group) String() string { func (*GetTopGroupsResponse_Group) ProtoMessage() {} func (x *GetTopGroupsResponse_Group) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[17] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1318,7 +1411,7 @@ func (x *GetTopGroupsResponse_Group) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTopGroupsResponse_Group.ProtoReflect.Descriptor instead. func (*GetTopGroupsResponse_Group) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{3, 0} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{4, 0} } func (x *GetTopGroupsResponse_Group) GetHash() uint64 { @@ -1361,7 +1454,7 @@ type GetDetailsResponse_Distribution struct { func (x *GetDetailsResponse_Distribution) Reset() { *x = GetDetailsResponse_Distribution{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[18] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1374,7 +1467,7 @@ func (x *GetDetailsResponse_Distribution) String() string { func (*GetDetailsResponse_Distribution) ProtoMessage() {} func (x *GetDetailsResponse_Distribution) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[18] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1387,7 +1480,7 @@ func (x *GetDetailsResponse_Distribution) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDetailsResponse_Distribution.ProtoReflect.Descriptor instead. func (*GetDetailsResponse_Distribution) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{8, 0} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{9, 0} } func (x *GetDetailsResponse_Distribution) GetValue() string { @@ -1418,7 +1511,7 @@ type GetDetailsResponse_Distributions struct { func (x *GetDetailsResponse_Distributions) Reset() { *x = GetDetailsResponse_Distributions{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[19] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1431,7 +1524,7 @@ func (x *GetDetailsResponse_Distributions) String() string { func (*GetDetailsResponse_Distributions) ProtoMessage() {} func (x *GetDetailsResponse_Distributions) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[19] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1444,7 +1537,7 @@ func (x *GetDetailsResponse_Distributions) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDetailsResponse_Distributions.ProtoReflect.Descriptor instead. func (*GetDetailsResponse_Distributions) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{8, 1} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{9, 1} } func (x *GetDetailsResponse_Distributions) GetByEnv() []*GetDetailsResponse_Distribution { @@ -1486,7 +1579,7 @@ type DiffByReleasesResponse_ReleaseInfo struct { func (x *DiffByReleasesResponse_ReleaseInfo) Reset() { *x = DiffByReleasesResponse_ReleaseInfo{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[21] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1499,7 +1592,7 @@ func (x *DiffByReleasesResponse_ReleaseInfo) String() string { func (*DiffByReleasesResponse_ReleaseInfo) ProtoMessage() {} func (x *DiffByReleasesResponse_ReleaseInfo) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[21] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1512,7 +1605,7 @@ func (x *DiffByReleasesResponse_ReleaseInfo) ProtoReflect() protoreflect.Message // Deprecated: Use DiffByReleasesResponse_ReleaseInfo.ProtoReflect.Descriptor instead. func (*DiffByReleasesResponse_ReleaseInfo) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{14, 0} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{15, 0} } func (x *DiffByReleasesResponse_ReleaseInfo) GetSeenTotal() uint64 { @@ -1538,7 +1631,7 @@ type DiffByReleasesResponse_Group struct { func (x *DiffByReleasesResponse_Group) Reset() { *x = DiffByReleasesResponse_Group{} if protoimpl.UnsafeEnabled { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[22] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1551,7 +1644,7 @@ func (x *DiffByReleasesResponse_Group) String() string { func (*DiffByReleasesResponse_Group) ProtoMessage() {} func (x *DiffByReleasesResponse_Group) ProtoReflect() protoreflect.Message { - mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[22] + mi := &file_errorgroups_v1_errorgroups_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1564,7 +1657,7 @@ func (x *DiffByReleasesResponse_Group) ProtoReflect() protoreflect.Message { // Deprecated: Use DiffByReleasesResponse_Group.ProtoReflect.Descriptor instead. func (*DiffByReleasesResponse_Group) Descriptor() ([]byte, []int) { - return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{14, 1} + return file_errorgroups_v1_errorgroups_proto_rawDescGZIP(), []int{15, 1} } func (x *DiffByReleasesResponse_Group) GetHash() uint64 { @@ -1619,304 +1712,330 @@ var file_errorgroups_v1_errorgroups_proto_rawDesc = []byte{ 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0xc1, 0x03, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, - 0x00, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x72, 0x65, 0x6c, - 0x65, 0x61, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x07, 0x72, 0x65, - 0x6c, 0x65, 0x61, 0x73, 0x65, 0x88, 0x01, 0x01, 0x12, 0x35, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, - 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x2b, 0x0a, - 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x77, 0x69, - 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x77, 0x69, 0x74, 0x68, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x06, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x06, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x44, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, + 0x6f, 0x74, 0x6f, 0x22, 0x9e, 0x01, 0x0a, 0x09, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, + 0x65, 0x12, 0x35, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x2a, 0x0a, 0x02, 0x74, 0x6f, 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, 0x02, 0x74, 0x6f, 0x22, 0x93, 0x04, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x00, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x72, 0x65, + 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x07, 0x72, + 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x08, 0x64, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x02, 0x18, 0x01, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x15, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, + 0x1d, 0x0a, 0x0a, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x09, 0x77, 0x69, 0x74, 0x68, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x1b, + 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, + 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x44, 0x0a, 0x06, 0x66, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x48, 0x03, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x88, 0x01, + 0x01, 0x12, 0x3d, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, + 0x48, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x88, 0x01, 0x01, + 0x1a, 0x1f, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x15, 0x0a, 0x06, 0x69, 0x73, + 0x5f, 0x6e, 0x65, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x69, 0x73, 0x4e, 0x65, + 0x77, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x6e, 0x76, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x72, 0x65, + 0x6c, 0x65, 0x61, 0x73, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xd7, 0x02, 0x0a, 0x11, 0x47, + 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x3f, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x48, - 0x03, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x88, 0x01, 0x01, 0x1a, 0x1f, 0x0a, 0x06, - 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x15, 0x0a, 0x06, 0x69, 0x73, 0x5f, 0x6e, 0x65, 0x77, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x69, 0x73, 0x4e, 0x65, 0x77, 0x42, 0x06, 0x0a, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, + 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x1a, 0xea, 0x01, 0x0a, 0x05, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, + 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x65, 0x65, 0x6e, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x3e, + 0x0a, 0x0d, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x61, 0x74, 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, 0x0b, 0x66, 0x69, 0x72, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x41, 0x74, 0x12, 0x3c, + 0x0a, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x61, 0x74, 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, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x41, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x22, 0xb2, 0x02, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x03, + 0x65, 0x6e, 0x76, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x65, 0x6e, 0x76, + 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x88, 0x01, 0x01, + 0x12, 0x39, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x02, 0x18, + 0x01, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x77, 0x69, 0x74, + 0x68, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x77, + 0x69, 0x74, 0x68, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x3d, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, + 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x02, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x52, + 0x61, 0x6e, 0x67, 0x65, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x6e, 0x76, 0x42, + 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xde, 0x01, 0x0a, 0x14, 0x47, 0x65, + 0x74, 0x54, 0x6f, 0x70, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x42, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x1a, 0x6c, 0x0a, 0x05, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, + 0x65, 0x65, 0x6e, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x09, 0x73, 0x65, 0x65, 0x6e, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x22, 0xfb, 0x02, 0x0a, 0x0e, 0x47, + 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, + 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, + 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x48, 0x01, 0x52, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x48, 0x61, 0x73, 0x68, 0x88, 0x01, 0x01, + 0x12, 0x15, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, + 0x03, 0x65, 0x6e, 0x76, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x72, 0x65, 0x6c, 0x65, 0x61, + 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x07, 0x72, 0x65, 0x6c, 0x65, + 0x61, 0x73, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3e, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x42, 0x02, 0x18, 0x01, 0x48, 0x04, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x05, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x88, 0x01, 0x01, 0x12, 0x3d, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, + 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, + 0x67, 0x65, 0x48, 0x06, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x88, + 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x0d, + 0x0a, 0x0b, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x6e, 0x76, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, - 0x65, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x09, 0x0a, 0x07, - 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0xd7, 0x02, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x12, 0x3f, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x06, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x73, 0x1a, 0xea, 0x01, 0x0a, 0x05, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, - 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x68, 0x61, - 0x73, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, - 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x09, 0x73, 0x65, 0x65, 0x6e, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x3e, 0x0a, 0x0d, 0x66, - 0x69, 0x72, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x61, 0x74, 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, 0x0b, - 0x66, 0x69, 0x72, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x41, 0x74, 0x12, 0x3c, 0x0a, 0x0c, 0x6c, - 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x09, + 0x0a, 0x07, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x5f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x48, + 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x07, 0x62, + 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, + 0x63, 0x6b, 0x65, 0x74, 0x52, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x1a, 0x0a, + 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x22, 0x4e, 0x0a, 0x06, 0x42, 0x75, 0x63, + 0x6b, 0x65, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 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, 0x0a, 0x6c, - 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x41, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x22, 0xe0, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x03, 0x65, 0x6e, 0x76, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x88, 0x01, 0x01, - 0x12, 0x1b, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x48, 0x01, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x35, 0x0a, - 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, - 0x66, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, - 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x77, 0x69, 0x74, 0x68, 0x54, 0x6f, 0x74, 0x61, - 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x6e, 0x76, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x22, 0xde, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x12, 0x42, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, - 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x1a, 0x6c, 0x0a, 0x05, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, - 0x68, 0x61, 0x73, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x74, - 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x65, 0x65, 0x6e, - 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x22, 0xa9, 0x02, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x48, 0x01, 0x52, 0x09, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x48, 0x61, 0x73, 0x68, 0x88, 0x01, 0x01, 0x12, 0x15, 0x0a, 0x03, 0x65, - 0x6e, 0x76, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x88, - 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x07, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x88, 0x01, - 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x04, - 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, - 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x05, 0x52, - 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x6e, 0x76, 0x42, 0x0a, 0x0a, - 0x08, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x64, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x22, 0x5f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, - 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x07, 0x62, - 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, - 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, - 0x61, 0x6c, 0x22, 0x4e, 0x0a, 0x06, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x2e, 0x0a, 0x04, - 0x74, 0x69, 0x6d, 0x65, 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, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x22, 0xcf, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x67, 0x72, 0x6f, - 0x75, 0x70, 0x48, 0x61, 0x73, 0x68, 0x12, 0x15, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, - 0x07, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, - 0x52, 0x07, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x06, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x6e, 0x76, 0x42, 0x0a, 0x0a, - 0x08, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x22, 0xea, 0x06, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x48, 0x61, 0x73, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x65, 0x65, 0x6e, 0x54, 0x6f, - 0x74, 0x61, 0x6c, 0x12, 0x3e, 0x0a, 0x0d, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, - 0x6e, 0x5f, 0x61, 0x74, 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, 0x0b, 0x66, 0x69, 0x72, 0x73, 0x74, 0x53, 0x65, 0x65, - 0x6e, 0x41, 0x74, 0x12, 0x3c, 0x0a, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, - 0x5f, 0x61, 0x74, 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, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x41, - 0x74, 0x12, 0x56, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x69, 0x73, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0d, 0x64, 0x69, 0x73, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x4a, 0x0a, 0x08, 0x6c, 0x6f, 0x67, - 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, - 0x4c, 0x6f, 0x67, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x6c, 0x6f, - 0x67, 0x54, 0x61, 0x67, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0x3e, 0x0a, - 0x0c, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x1a, 0xc5, 0x02, - 0x0a, 0x0d, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, - 0x46, 0x0a, 0x06, 0x62, 0x79, 0x5f, 0x65, 0x6e, 0x76, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2f, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, + 0x69, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xcf, 0x01, 0x0a, 0x11, 0x47, 0x65, + 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1d, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x00, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1d, + 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x48, 0x61, 0x73, 0x68, 0x12, 0x15, 0x0a, + 0x03, 0x65, 0x6e, 0x76, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x03, 0x65, 0x6e, + 0x76, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x07, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, + 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x88, 0x01, 0x01, + 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x06, 0x0a, 0x04, + 0x5f, 0x65, 0x6e, 0x76, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, + 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0xea, 0x06, 0x0a, 0x12, + 0x47, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x68, 0x61, 0x73, 0x68, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x48, 0x61, 0x73, + 0x68, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, + 0x65, 0x65, 0x6e, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x09, 0x73, 0x65, 0x65, 0x6e, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x3e, 0x0a, 0x0d, 0x66, 0x69, + 0x72, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x61, 0x74, 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, 0x0b, 0x66, + 0x69, 0x72, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x41, 0x74, 0x12, 0x3c, 0x0a, 0x0c, 0x6c, 0x61, + 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x61, 0x74, 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, 0x6c, 0x61, + 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x41, 0x74, 0x12, 0x56, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x30, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x05, 0x62, 0x79, 0x45, 0x6e, 0x76, 0x12, 0x4e, 0x0a, 0x0a, 0x62, 0x79, 0x5f, 0x72, 0x65, - 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, - 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x62, 0x79, - 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x09, 0x62, 0x79, 0x5f, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, - 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x62, 0x79, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x4e, 0x0a, 0x0a, 0x62, 0x79, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x72, 0x72, 0x6f, - 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x69, - 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x62, 0x79, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x1a, 0x3a, 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x54, 0x61, 0x67, 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, 0x53, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x12, 0x15, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, - 0x52, 0x03, 0x65, 0x6e, 0x76, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x6e, 0x76, - 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0x31, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, - 0x65, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, - 0x08, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x08, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x22, 0x77, 0x0a, 0x12, 0x47, 0x65, 0x74, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x15, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x88, 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05, - 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x69, 0x6d, - 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, - 0x6e, 0x76, 0x22, 0x31, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x8e, 0x02, 0x0a, 0x15, 0x44, 0x69, 0x66, 0x66, 0x42, 0x79, - 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6c, - 0x65, 0x61, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6c, - 0x65, 0x61, 0x73, 0x65, 0x73, 0x12, 0x15, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x06, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, - 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, - 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x05, 0x6f, - 0x72, 0x64, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x77, 0x69, 0x74, 0x68, 0x54, 0x6f, - 0x74, 0x61, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x6e, 0x76, 0x42, 0x09, 0x0a, 0x07, 0x5f, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0xca, 0x04, 0x0a, 0x16, 0x44, 0x69, 0x66, 0x66, 0x42, - 0x79, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x44, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, + 0x73, 0x52, 0x0d, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x4a, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x07, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x54, 0x61, 0x67, 0x73, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x1a, 0x3e, 0x0a, 0x0c, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x65, + 0x72, 0x63, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x70, 0x65, 0x72, + 0x63, 0x65, 0x6e, 0x74, 0x1a, 0xc5, 0x02, 0x0a, 0x0d, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x46, 0x0a, 0x06, 0x62, 0x79, 0x5f, 0x65, 0x6e, 0x76, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x69, 0x73, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x62, 0x79, 0x45, 0x6e, 0x76, 0x12, 0x4e, + 0x0a, 0x0a, 0x62, 0x79, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x62, 0x79, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x12, 0x4c, + 0x0a, 0x09, 0x62, 0x79, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x08, 0x62, 0x79, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x4e, 0x0a, 0x0a, + 0x62, 0x79, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2f, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x09, 0x62, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x1a, 0x3a, 0x0a, 0x0c, + 0x4c, 0x6f, 0x67, 0x54, 0x61, 0x67, 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, 0x53, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x52, + 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, + 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x88, 0x01, 0x01, 0x42, + 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x6e, 0x76, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0x31, 0x0a, + 0x13, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, + 0x22, 0x77, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x15, 0x0a, 0x03, + 0x65, 0x6e, 0x76, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x65, 0x6e, 0x76, + 0x88, 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x6e, 0x76, 0x22, 0x31, 0x0a, 0x13, 0x47, 0x65, 0x74, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x8e, 0x02, 0x0a, + 0x15, 0x44, 0x69, 0x66, 0x66, 0x42, 0x79, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x12, 0x15, 0x0a, 0x03, + 0x65, 0x6e, 0x76, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x65, 0x6e, 0x76, + 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x88, 0x01, 0x01, + 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x2b, + 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x77, + 0x69, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x09, 0x77, 0x69, 0x74, 0x68, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, + 0x6e, 0x76, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0xca, 0x04, + 0x0a, 0x16, 0x44, 0x69, 0x66, 0x66, 0x42, 0x79, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x44, + 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, + 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x44, 0x69, 0x66, 0x66, 0x42, 0x79, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x06, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x73, 0x1a, 0x2c, 0x0a, 0x0b, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x65, 0x65, 0x6e, 0x54, 0x6f, 0x74, + 0x61, 0x6c, 0x1a, 0xa5, 0x03, 0x0a, 0x05, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, + 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, + 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3e, 0x0a, 0x0d, 0x66, 0x69, + 0x72, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x61, 0x74, 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, 0x0b, 0x66, + 0x69, 0x72, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x41, 0x74, 0x12, 0x3c, 0x0a, 0x0c, 0x6c, 0x61, + 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x6c, 0x61, + 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x41, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x12, 0x63, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, + 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x66, 0x66, 0x42, 0x79, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x1a, 0x2c, 0x0a, - 0x0b, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x0a, 0x0a, - 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x09, 0x73, 0x65, 0x65, 0x6e, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x1a, 0xa5, 0x03, 0x0a, 0x05, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x12, 0x3e, 0x0a, 0x0d, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, - 0x6e, 0x5f, 0x61, 0x74, 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, 0x0b, 0x66, 0x69, 0x72, 0x73, 0x74, 0x53, 0x65, 0x65, - 0x6e, 0x41, 0x74, 0x12, 0x3c, 0x0a, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, - 0x5f, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x41, - 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x63, 0x0a, 0x0d, 0x72, 0x65, 0x6c, - 0x65, 0x61, 0x73, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x3e, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x44, 0x69, 0x66, 0x66, 0x42, 0x79, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, - 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x1a, 0x73, - 0x0a, 0x11, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x49, 0x6e, 0x66, 0x6f, 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, 0x48, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x66, 0x66, 0x42, 0x79, 0x52, 0x65, 0x6c, 0x65, - 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x6c, - 0x65, 0x61, 0x73, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x2a, 0x3f, 0x0a, 0x05, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x0e, - 0x4f, 0x52, 0x44, 0x45, 0x52, 0x5f, 0x46, 0x52, 0x45, 0x51, 0x55, 0x45, 0x4e, 0x54, 0x10, 0x00, - 0x12, 0x10, 0x0a, 0x0c, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x5f, 0x4c, 0x41, 0x54, 0x45, 0x53, 0x54, - 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x5f, 0x4f, 0x4c, 0x44, 0x45, - 0x53, 0x54, 0x10, 0x02, 0x32, 0x81, 0x05, 0x0a, 0x12, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x52, 0x0a, 0x09, 0x47, - 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x20, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x5b, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, - 0x23, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x07, - 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x12, 0x1e, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x55, 0x0a, 0x0a, 0x47, 0x65, - 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x21, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x65, 0x72, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, + 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x1a, 0x73, 0x0a, 0x11, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, + 0x49, 0x6e, 0x66, 0x6f, 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, 0x48, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x66, + 0x66, 0x42, 0x79, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x2a, 0x3f, 0x0a, 0x05, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x0e, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x5f, 0x46, 0x52, 0x45, + 0x51, 0x55, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x4f, 0x52, 0x44, 0x45, 0x52, + 0x5f, 0x4c, 0x41, 0x54, 0x45, 0x53, 0x54, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4f, 0x52, 0x44, + 0x45, 0x52, 0x5f, 0x4f, 0x4c, 0x44, 0x45, 0x53, 0x54, 0x10, 0x02, 0x32, 0x81, 0x05, 0x0a, 0x12, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x52, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, + 0x20, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x21, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x23, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x58, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, - 0x12, 0x22, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x0b, 0x47, - 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, + 0x54, 0x6f, 0x70, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x12, 0x1e, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x61, 0x0a, 0x0e, 0x44, 0x69, 0x66, 0x66, 0x42, 0x79, 0x52, - 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x66, 0x66, 0x42, 0x79, 0x52, - 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, + 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, + 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x55, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, + 0x21, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x52, + 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x65, + 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x58, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x12, 0x22, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x61, 0x0a, 0x0e, + 0x44, 0x69, 0x66, 0x66, 0x42, 0x79, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x66, 0x66, 0x42, 0x79, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x7a, 0x6f, 0x6e, 0x74, 0x65, 0x63, 0x68, 0x2f, - 0x73, 0x65, 0x71, 0x2d, 0x75, 0x69, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x66, 0x66, 0x42, 0x79, 0x52, 0x65, 0x6c, + 0x65, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, + 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x7a, + 0x6f, 0x6e, 0x74, 0x65, 0x63, 0x68, 0x2f, 0x73, 0x65, 0x71, 0x2d, 0x75, 0x69, 0x2f, 0x70, 0x6b, + 0x67, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2f, 0x76, 0x31, + 0x3b, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1932,81 +2051,88 @@ func file_errorgroups_v1_errorgroups_proto_rawDescGZIP() []byte { } var file_errorgroups_v1_errorgroups_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_errorgroups_v1_errorgroups_proto_msgTypes = make([]protoimpl.MessageInfo, 24) +var file_errorgroups_v1_errorgroups_proto_msgTypes = make([]protoimpl.MessageInfo, 25) var file_errorgroups_v1_errorgroups_proto_goTypes = []any{ (Order)(0), // 0: errorgroups.v1.Order - (*GetGroupsRequest)(nil), // 1: errorgroups.v1.GetGroupsRequest - (*GetGroupsResponse)(nil), // 2: errorgroups.v1.GetGroupsResponse - (*GetTopGroupsRequest)(nil), // 3: errorgroups.v1.GetTopGroupsRequest - (*GetTopGroupsResponse)(nil), // 4: errorgroups.v1.GetTopGroupsResponse - (*GetHistRequest)(nil), // 5: errorgroups.v1.GetHistRequest - (*GetHistResponse)(nil), // 6: errorgroups.v1.GetHistResponse - (*Bucket)(nil), // 7: errorgroups.v1.Bucket - (*GetDetailsRequest)(nil), // 8: errorgroups.v1.GetDetailsRequest - (*GetDetailsResponse)(nil), // 9: errorgroups.v1.GetDetailsResponse - (*GetReleasesRequest)(nil), // 10: errorgroups.v1.GetReleasesRequest - (*GetReleasesResponse)(nil), // 11: errorgroups.v1.GetReleasesResponse - (*GetServicesRequest)(nil), // 12: errorgroups.v1.GetServicesRequest - (*GetServicesResponse)(nil), // 13: errorgroups.v1.GetServicesResponse - (*DiffByReleasesRequest)(nil), // 14: errorgroups.v1.DiffByReleasesRequest - (*DiffByReleasesResponse)(nil), // 15: errorgroups.v1.DiffByReleasesResponse - (*GetGroupsRequest_Filter)(nil), // 16: errorgroups.v1.GetGroupsRequest.Filter - (*GetGroupsResponse_Group)(nil), // 17: errorgroups.v1.GetGroupsResponse.Group - (*GetTopGroupsResponse_Group)(nil), // 18: errorgroups.v1.GetTopGroupsResponse.Group - (*GetDetailsResponse_Distribution)(nil), // 19: errorgroups.v1.GetDetailsResponse.Distribution - (*GetDetailsResponse_Distributions)(nil), // 20: errorgroups.v1.GetDetailsResponse.Distributions - nil, // 21: errorgroups.v1.GetDetailsResponse.LogTagsEntry - (*DiffByReleasesResponse_ReleaseInfo)(nil), // 22: errorgroups.v1.DiffByReleasesResponse.ReleaseInfo - (*DiffByReleasesResponse_Group)(nil), // 23: errorgroups.v1.DiffByReleasesResponse.Group - nil, // 24: errorgroups.v1.DiffByReleasesResponse.Group.ReleaseInfosEntry - (*durationpb.Duration)(nil), // 25: google.protobuf.Duration - (*timestamppb.Timestamp)(nil), // 26: google.protobuf.Timestamp + (*TimeRange)(nil), // 1: errorgroups.v1.TimeRange + (*GetGroupsRequest)(nil), // 2: errorgroups.v1.GetGroupsRequest + (*GetGroupsResponse)(nil), // 3: errorgroups.v1.GetGroupsResponse + (*GetTopGroupsRequest)(nil), // 4: errorgroups.v1.GetTopGroupsRequest + (*GetTopGroupsResponse)(nil), // 5: errorgroups.v1.GetTopGroupsResponse + (*GetHistRequest)(nil), // 6: errorgroups.v1.GetHistRequest + (*GetHistResponse)(nil), // 7: errorgroups.v1.GetHistResponse + (*Bucket)(nil), // 8: errorgroups.v1.Bucket + (*GetDetailsRequest)(nil), // 9: errorgroups.v1.GetDetailsRequest + (*GetDetailsResponse)(nil), // 10: errorgroups.v1.GetDetailsResponse + (*GetReleasesRequest)(nil), // 11: errorgroups.v1.GetReleasesRequest + (*GetReleasesResponse)(nil), // 12: errorgroups.v1.GetReleasesResponse + (*GetServicesRequest)(nil), // 13: errorgroups.v1.GetServicesRequest + (*GetServicesResponse)(nil), // 14: errorgroups.v1.GetServicesResponse + (*DiffByReleasesRequest)(nil), // 15: errorgroups.v1.DiffByReleasesRequest + (*DiffByReleasesResponse)(nil), // 16: errorgroups.v1.DiffByReleasesResponse + (*GetGroupsRequest_Filter)(nil), // 17: errorgroups.v1.GetGroupsRequest.Filter + (*GetGroupsResponse_Group)(nil), // 18: errorgroups.v1.GetGroupsResponse.Group + (*GetTopGroupsResponse_Group)(nil), // 19: errorgroups.v1.GetTopGroupsResponse.Group + (*GetDetailsResponse_Distribution)(nil), // 20: errorgroups.v1.GetDetailsResponse.Distribution + (*GetDetailsResponse_Distributions)(nil), // 21: errorgroups.v1.GetDetailsResponse.Distributions + nil, // 22: errorgroups.v1.GetDetailsResponse.LogTagsEntry + (*DiffByReleasesResponse_ReleaseInfo)(nil), // 23: errorgroups.v1.DiffByReleasesResponse.ReleaseInfo + (*DiffByReleasesResponse_Group)(nil), // 24: errorgroups.v1.DiffByReleasesResponse.Group + nil, // 25: errorgroups.v1.DiffByReleasesResponse.Group.ReleaseInfosEntry + (*durationpb.Duration)(nil), // 26: google.protobuf.Duration + (*timestamppb.Timestamp)(nil), // 27: google.protobuf.Timestamp } var file_errorgroups_v1_errorgroups_proto_depIdxs = []int32{ - 25, // 0: errorgroups.v1.GetGroupsRequest.duration:type_name -> google.protobuf.Duration - 0, // 1: errorgroups.v1.GetGroupsRequest.order:type_name -> errorgroups.v1.Order - 16, // 2: errorgroups.v1.GetGroupsRequest.filter:type_name -> errorgroups.v1.GetGroupsRequest.Filter - 17, // 3: errorgroups.v1.GetGroupsResponse.groups:type_name -> errorgroups.v1.GetGroupsResponse.Group - 25, // 4: errorgroups.v1.GetTopGroupsRequest.duration:type_name -> google.protobuf.Duration - 18, // 5: errorgroups.v1.GetTopGroupsResponse.groups:type_name -> errorgroups.v1.GetTopGroupsResponse.Group - 25, // 6: errorgroups.v1.GetHistRequest.duration:type_name -> google.protobuf.Duration - 7, // 7: errorgroups.v1.GetHistResponse.buckets:type_name -> errorgroups.v1.Bucket - 26, // 8: errorgroups.v1.Bucket.time:type_name -> google.protobuf.Timestamp - 26, // 9: errorgroups.v1.GetDetailsResponse.first_seen_at:type_name -> google.protobuf.Timestamp - 26, // 10: errorgroups.v1.GetDetailsResponse.last_seen_at:type_name -> google.protobuf.Timestamp - 20, // 11: errorgroups.v1.GetDetailsResponse.distributions:type_name -> errorgroups.v1.GetDetailsResponse.Distributions - 21, // 12: errorgroups.v1.GetDetailsResponse.log_tags:type_name -> errorgroups.v1.GetDetailsResponse.LogTagsEntry - 0, // 13: errorgroups.v1.DiffByReleasesRequest.order:type_name -> errorgroups.v1.Order - 23, // 14: errorgroups.v1.DiffByReleasesResponse.groups:type_name -> errorgroups.v1.DiffByReleasesResponse.Group - 26, // 15: errorgroups.v1.GetGroupsResponse.Group.first_seen_at:type_name -> google.protobuf.Timestamp - 26, // 16: errorgroups.v1.GetGroupsResponse.Group.last_seen_at:type_name -> google.protobuf.Timestamp - 19, // 17: errorgroups.v1.GetDetailsResponse.Distributions.by_env:type_name -> errorgroups.v1.GetDetailsResponse.Distribution - 19, // 18: errorgroups.v1.GetDetailsResponse.Distributions.by_release:type_name -> errorgroups.v1.GetDetailsResponse.Distribution - 19, // 19: errorgroups.v1.GetDetailsResponse.Distributions.by_source:type_name -> errorgroups.v1.GetDetailsResponse.Distribution - 19, // 20: errorgroups.v1.GetDetailsResponse.Distributions.by_service:type_name -> errorgroups.v1.GetDetailsResponse.Distribution - 26, // 21: errorgroups.v1.DiffByReleasesResponse.Group.first_seen_at:type_name -> google.protobuf.Timestamp - 26, // 22: errorgroups.v1.DiffByReleasesResponse.Group.last_seen_at:type_name -> google.protobuf.Timestamp - 24, // 23: errorgroups.v1.DiffByReleasesResponse.Group.release_infos:type_name -> errorgroups.v1.DiffByReleasesResponse.Group.ReleaseInfosEntry - 22, // 24: errorgroups.v1.DiffByReleasesResponse.Group.ReleaseInfosEntry.value:type_name -> errorgroups.v1.DiffByReleasesResponse.ReleaseInfo - 1, // 25: errorgroups.v1.ErrorGroupsService.GetGroups:input_type -> errorgroups.v1.GetGroupsRequest - 3, // 26: errorgroups.v1.ErrorGroupsService.GetTopGroups:input_type -> errorgroups.v1.GetTopGroupsRequest - 5, // 27: errorgroups.v1.ErrorGroupsService.GetHist:input_type -> errorgroups.v1.GetHistRequest - 8, // 28: errorgroups.v1.ErrorGroupsService.GetDetails:input_type -> errorgroups.v1.GetDetailsRequest - 10, // 29: errorgroups.v1.ErrorGroupsService.GetReleases:input_type -> errorgroups.v1.GetReleasesRequest - 12, // 30: errorgroups.v1.ErrorGroupsService.GetServices:input_type -> errorgroups.v1.GetServicesRequest - 14, // 31: errorgroups.v1.ErrorGroupsService.DiffByReleases:input_type -> errorgroups.v1.DiffByReleasesRequest - 2, // 32: errorgroups.v1.ErrorGroupsService.GetGroups:output_type -> errorgroups.v1.GetGroupsResponse - 4, // 33: errorgroups.v1.ErrorGroupsService.GetTopGroups:output_type -> errorgroups.v1.GetTopGroupsResponse - 6, // 34: errorgroups.v1.ErrorGroupsService.GetHist:output_type -> errorgroups.v1.GetHistResponse - 9, // 35: errorgroups.v1.ErrorGroupsService.GetDetails:output_type -> errorgroups.v1.GetDetailsResponse - 11, // 36: errorgroups.v1.ErrorGroupsService.GetReleases:output_type -> errorgroups.v1.GetReleasesResponse - 13, // 37: errorgroups.v1.ErrorGroupsService.GetServices:output_type -> errorgroups.v1.GetServicesResponse - 15, // 38: errorgroups.v1.ErrorGroupsService.DiffByReleases:output_type -> errorgroups.v1.DiffByReleasesResponse - 32, // [32:39] is the sub-list for method output_type - 25, // [25:32] is the sub-list for method input_type - 25, // [25:25] is the sub-list for extension type_name - 25, // [25:25] is the sub-list for extension extendee - 0, // [0:25] is the sub-list for field type_name + 26, // 0: errorgroups.v1.TimeRange.duration:type_name -> google.protobuf.Duration + 27, // 1: errorgroups.v1.TimeRange.from:type_name -> google.protobuf.Timestamp + 27, // 2: errorgroups.v1.TimeRange.to:type_name -> google.protobuf.Timestamp + 26, // 3: errorgroups.v1.GetGroupsRequest.duration:type_name -> google.protobuf.Duration + 0, // 4: errorgroups.v1.GetGroupsRequest.order:type_name -> errorgroups.v1.Order + 17, // 5: errorgroups.v1.GetGroupsRequest.filter:type_name -> errorgroups.v1.GetGroupsRequest.Filter + 1, // 6: errorgroups.v1.GetGroupsRequest.time_range:type_name -> errorgroups.v1.TimeRange + 18, // 7: errorgroups.v1.GetGroupsResponse.groups:type_name -> errorgroups.v1.GetGroupsResponse.Group + 26, // 8: errorgroups.v1.GetTopGroupsRequest.duration:type_name -> google.protobuf.Duration + 1, // 9: errorgroups.v1.GetTopGroupsRequest.time_range:type_name -> errorgroups.v1.TimeRange + 19, // 10: errorgroups.v1.GetTopGroupsResponse.groups:type_name -> errorgroups.v1.GetTopGroupsResponse.Group + 26, // 11: errorgroups.v1.GetHistRequest.duration:type_name -> google.protobuf.Duration + 1, // 12: errorgroups.v1.GetHistRequest.time_range:type_name -> errorgroups.v1.TimeRange + 8, // 13: errorgroups.v1.GetHistResponse.buckets:type_name -> errorgroups.v1.Bucket + 27, // 14: errorgroups.v1.Bucket.time:type_name -> google.protobuf.Timestamp + 27, // 15: errorgroups.v1.GetDetailsResponse.first_seen_at:type_name -> google.protobuf.Timestamp + 27, // 16: errorgroups.v1.GetDetailsResponse.last_seen_at:type_name -> google.protobuf.Timestamp + 21, // 17: errorgroups.v1.GetDetailsResponse.distributions:type_name -> errorgroups.v1.GetDetailsResponse.Distributions + 22, // 18: errorgroups.v1.GetDetailsResponse.log_tags:type_name -> errorgroups.v1.GetDetailsResponse.LogTagsEntry + 0, // 19: errorgroups.v1.DiffByReleasesRequest.order:type_name -> errorgroups.v1.Order + 24, // 20: errorgroups.v1.DiffByReleasesResponse.groups:type_name -> errorgroups.v1.DiffByReleasesResponse.Group + 27, // 21: errorgroups.v1.GetGroupsResponse.Group.first_seen_at:type_name -> google.protobuf.Timestamp + 27, // 22: errorgroups.v1.GetGroupsResponse.Group.last_seen_at:type_name -> google.protobuf.Timestamp + 20, // 23: errorgroups.v1.GetDetailsResponse.Distributions.by_env:type_name -> errorgroups.v1.GetDetailsResponse.Distribution + 20, // 24: errorgroups.v1.GetDetailsResponse.Distributions.by_release:type_name -> errorgroups.v1.GetDetailsResponse.Distribution + 20, // 25: errorgroups.v1.GetDetailsResponse.Distributions.by_source:type_name -> errorgroups.v1.GetDetailsResponse.Distribution + 20, // 26: errorgroups.v1.GetDetailsResponse.Distributions.by_service:type_name -> errorgroups.v1.GetDetailsResponse.Distribution + 27, // 27: errorgroups.v1.DiffByReleasesResponse.Group.first_seen_at:type_name -> google.protobuf.Timestamp + 27, // 28: errorgroups.v1.DiffByReleasesResponse.Group.last_seen_at:type_name -> google.protobuf.Timestamp + 25, // 29: errorgroups.v1.DiffByReleasesResponse.Group.release_infos:type_name -> errorgroups.v1.DiffByReleasesResponse.Group.ReleaseInfosEntry + 23, // 30: errorgroups.v1.DiffByReleasesResponse.Group.ReleaseInfosEntry.value:type_name -> errorgroups.v1.DiffByReleasesResponse.ReleaseInfo + 2, // 31: errorgroups.v1.ErrorGroupsService.GetGroups:input_type -> errorgroups.v1.GetGroupsRequest + 4, // 32: errorgroups.v1.ErrorGroupsService.GetTopGroups:input_type -> errorgroups.v1.GetTopGroupsRequest + 6, // 33: errorgroups.v1.ErrorGroupsService.GetHist:input_type -> errorgroups.v1.GetHistRequest + 9, // 34: errorgroups.v1.ErrorGroupsService.GetDetails:input_type -> errorgroups.v1.GetDetailsRequest + 11, // 35: errorgroups.v1.ErrorGroupsService.GetReleases:input_type -> errorgroups.v1.GetReleasesRequest + 13, // 36: errorgroups.v1.ErrorGroupsService.GetServices:input_type -> errorgroups.v1.GetServicesRequest + 15, // 37: errorgroups.v1.ErrorGroupsService.DiffByReleases:input_type -> errorgroups.v1.DiffByReleasesRequest + 3, // 38: errorgroups.v1.ErrorGroupsService.GetGroups:output_type -> errorgroups.v1.GetGroupsResponse + 5, // 39: errorgroups.v1.ErrorGroupsService.GetTopGroups:output_type -> errorgroups.v1.GetTopGroupsResponse + 7, // 40: errorgroups.v1.ErrorGroupsService.GetHist:output_type -> errorgroups.v1.GetHistResponse + 10, // 41: errorgroups.v1.ErrorGroupsService.GetDetails:output_type -> errorgroups.v1.GetDetailsResponse + 12, // 42: errorgroups.v1.ErrorGroupsService.GetReleases:output_type -> errorgroups.v1.GetReleasesResponse + 14, // 43: errorgroups.v1.ErrorGroupsService.GetServices:output_type -> errorgroups.v1.GetServicesResponse + 16, // 44: errorgroups.v1.ErrorGroupsService.DiffByReleases:output_type -> errorgroups.v1.DiffByReleasesResponse + 38, // [38:45] is the sub-list for method output_type + 31, // [31:38] is the sub-list for method input_type + 31, // [31:31] is the sub-list for extension type_name + 31, // [31:31] is the sub-list for extension extendee + 0, // [0:31] is the sub-list for field type_name } func init() { file_errorgroups_v1_errorgroups_proto_init() } @@ -2016,7 +2142,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { } if !protoimpl.UnsafeEnabled { file_errorgroups_v1_errorgroups_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*GetGroupsRequest); i { + switch v := v.(*TimeRange); i { case 0: return &v.state case 1: @@ -2028,7 +2154,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } file_errorgroups_v1_errorgroups_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*GetGroupsResponse); i { + switch v := v.(*GetGroupsRequest); i { case 0: return &v.state case 1: @@ -2040,7 +2166,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } file_errorgroups_v1_errorgroups_proto_msgTypes[2].Exporter = func(v any, i int) any { - switch v := v.(*GetTopGroupsRequest); i { + switch v := v.(*GetGroupsResponse); i { case 0: return &v.state case 1: @@ -2052,7 +2178,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } file_errorgroups_v1_errorgroups_proto_msgTypes[3].Exporter = func(v any, i int) any { - switch v := v.(*GetTopGroupsResponse); i { + switch v := v.(*GetTopGroupsRequest); i { case 0: return &v.state case 1: @@ -2064,7 +2190,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } file_errorgroups_v1_errorgroups_proto_msgTypes[4].Exporter = func(v any, i int) any { - switch v := v.(*GetHistRequest); i { + switch v := v.(*GetTopGroupsResponse); i { case 0: return &v.state case 1: @@ -2076,7 +2202,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } file_errorgroups_v1_errorgroups_proto_msgTypes[5].Exporter = func(v any, i int) any { - switch v := v.(*GetHistResponse); i { + switch v := v.(*GetHistRequest); i { case 0: return &v.state case 1: @@ -2088,7 +2214,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } file_errorgroups_v1_errorgroups_proto_msgTypes[6].Exporter = func(v any, i int) any { - switch v := v.(*Bucket); i { + switch v := v.(*GetHistResponse); i { case 0: return &v.state case 1: @@ -2100,7 +2226,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } file_errorgroups_v1_errorgroups_proto_msgTypes[7].Exporter = func(v any, i int) any { - switch v := v.(*GetDetailsRequest); i { + switch v := v.(*Bucket); i { case 0: return &v.state case 1: @@ -2112,7 +2238,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } file_errorgroups_v1_errorgroups_proto_msgTypes[8].Exporter = func(v any, i int) any { - switch v := v.(*GetDetailsResponse); i { + switch v := v.(*GetDetailsRequest); i { case 0: return &v.state case 1: @@ -2124,7 +2250,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } file_errorgroups_v1_errorgroups_proto_msgTypes[9].Exporter = func(v any, i int) any { - switch v := v.(*GetReleasesRequest); i { + switch v := v.(*GetDetailsResponse); i { case 0: return &v.state case 1: @@ -2136,7 +2262,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } file_errorgroups_v1_errorgroups_proto_msgTypes[10].Exporter = func(v any, i int) any { - switch v := v.(*GetReleasesResponse); i { + switch v := v.(*GetReleasesRequest); i { case 0: return &v.state case 1: @@ -2148,7 +2274,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } file_errorgroups_v1_errorgroups_proto_msgTypes[11].Exporter = func(v any, i int) any { - switch v := v.(*GetServicesRequest); i { + switch v := v.(*GetReleasesResponse); i { case 0: return &v.state case 1: @@ -2160,7 +2286,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } file_errorgroups_v1_errorgroups_proto_msgTypes[12].Exporter = func(v any, i int) any { - switch v := v.(*GetServicesResponse); i { + switch v := v.(*GetServicesRequest); i { case 0: return &v.state case 1: @@ -2172,7 +2298,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } file_errorgroups_v1_errorgroups_proto_msgTypes[13].Exporter = func(v any, i int) any { - switch v := v.(*DiffByReleasesRequest); i { + switch v := v.(*GetServicesResponse); i { case 0: return &v.state case 1: @@ -2184,7 +2310,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } file_errorgroups_v1_errorgroups_proto_msgTypes[14].Exporter = func(v any, i int) any { - switch v := v.(*DiffByReleasesResponse); i { + switch v := v.(*DiffByReleasesRequest); i { case 0: return &v.state case 1: @@ -2196,7 +2322,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } file_errorgroups_v1_errorgroups_proto_msgTypes[15].Exporter = func(v any, i int) any { - switch v := v.(*GetGroupsRequest_Filter); i { + switch v := v.(*DiffByReleasesResponse); i { case 0: return &v.state case 1: @@ -2208,7 +2334,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } file_errorgroups_v1_errorgroups_proto_msgTypes[16].Exporter = func(v any, i int) any { - switch v := v.(*GetGroupsResponse_Group); i { + switch v := v.(*GetGroupsRequest_Filter); i { case 0: return &v.state case 1: @@ -2220,7 +2346,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } file_errorgroups_v1_errorgroups_proto_msgTypes[17].Exporter = func(v any, i int) any { - switch v := v.(*GetTopGroupsResponse_Group); i { + switch v := v.(*GetGroupsResponse_Group); i { case 0: return &v.state case 1: @@ -2232,7 +2358,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } file_errorgroups_v1_errorgroups_proto_msgTypes[18].Exporter = func(v any, i int) any { - switch v := v.(*GetDetailsResponse_Distribution); i { + switch v := v.(*GetTopGroupsResponse_Group); i { case 0: return &v.state case 1: @@ -2244,6 +2370,18 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } file_errorgroups_v1_errorgroups_proto_msgTypes[19].Exporter = func(v any, i int) any { + switch v := v.(*GetDetailsResponse_Distribution); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_errorgroups_v1_errorgroups_proto_msgTypes[20].Exporter = func(v any, i int) any { switch v := v.(*GetDetailsResponse_Distributions); i { case 0: return &v.state @@ -2255,7 +2393,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { return nil } } - file_errorgroups_v1_errorgroups_proto_msgTypes[21].Exporter = func(v any, i int) any { + file_errorgroups_v1_errorgroups_proto_msgTypes[22].Exporter = func(v any, i int) any { switch v := v.(*DiffByReleasesResponse_ReleaseInfo); i { case 0: return &v.state @@ -2267,7 +2405,7 @@ func file_errorgroups_v1_errorgroups_proto_init() { return nil } } - file_errorgroups_v1_errorgroups_proto_msgTypes[22].Exporter = func(v any, i int) any { + file_errorgroups_v1_errorgroups_proto_msgTypes[23].Exporter = func(v any, i int) any { switch v := v.(*DiffByReleasesResponse_Group); i { case 0: return &v.state @@ -2280,20 +2418,20 @@ func file_errorgroups_v1_errorgroups_proto_init() { } } } - file_errorgroups_v1_errorgroups_proto_msgTypes[0].OneofWrappers = []any{} - file_errorgroups_v1_errorgroups_proto_msgTypes[2].OneofWrappers = []any{} - file_errorgroups_v1_errorgroups_proto_msgTypes[4].OneofWrappers = []any{} - file_errorgroups_v1_errorgroups_proto_msgTypes[7].OneofWrappers = []any{} - file_errorgroups_v1_errorgroups_proto_msgTypes[9].OneofWrappers = []any{} - file_errorgroups_v1_errorgroups_proto_msgTypes[11].OneofWrappers = []any{} - file_errorgroups_v1_errorgroups_proto_msgTypes[13].OneofWrappers = []any{} + file_errorgroups_v1_errorgroups_proto_msgTypes[1].OneofWrappers = []any{} + file_errorgroups_v1_errorgroups_proto_msgTypes[3].OneofWrappers = []any{} + file_errorgroups_v1_errorgroups_proto_msgTypes[5].OneofWrappers = []any{} + file_errorgroups_v1_errorgroups_proto_msgTypes[8].OneofWrappers = []any{} + file_errorgroups_v1_errorgroups_proto_msgTypes[10].OneofWrappers = []any{} + file_errorgroups_v1_errorgroups_proto_msgTypes[12].OneofWrappers = []any{} + file_errorgroups_v1_errorgroups_proto_msgTypes[14].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_errorgroups_v1_errorgroups_proto_rawDesc, NumEnums: 1, - NumMessages: 24, + NumMessages: 25, NumExtensions: 0, NumServices: 1, }, diff --git a/swagger/swagger.json b/swagger/swagger.json index 468ef69..f679b0a 100644 --- a/swagger/swagger.json +++ b/swagger/swagger.json @@ -1850,7 +1850,7 @@ "type": "object", "properties": { "duration": { - "description": "In go duration format. If not specified, then for the entire time.", + "description": "Deprecated: Use time_range instead", "type": "string", "format": "duration", "example": "1h" @@ -1879,6 +1879,9 @@ "source": { "type": "string" }, + "time_range": { + "$ref": "#/definitions/errorgroups.v1.TimeRange" + }, "with_total": { "type": "boolean" } @@ -1902,7 +1905,7 @@ "type": "object", "properties": { "duration": { - "description": "In go duration format. If not specified, `1h` is used.", + "description": "Deprecated: Use time_range instead", "type": "string", "format": "duration", "example": "1h" @@ -1922,6 +1925,9 @@ }, "source": { "type": "string" + }, + "time_range": { + "$ref": "#/definitions/errorgroups.v1.TimeRange" } } }, @@ -1994,7 +2000,7 @@ "type": "object", "properties": { "duration": { - "description": "In go duration format. If not specified, then for the entire time.", + "description": "Deprecated: Use time_range instead", "type": "string", "format": "duration", "example": "1h" @@ -2011,6 +2017,9 @@ "source": { "type": "string" }, + "time_range": { + "$ref": "#/definitions/errorgroups.v1.TimeRange" + }, "with_total": { "type": "boolean" } @@ -2077,6 +2086,24 @@ "OrderOldest" ] }, + "errorgroups.v1.TimeRange": { + "type": "object", + "properties": { + "duration": { + "type": "string", + "format": "duration", + "example": "1h" + }, + "from": { + "type": "string", + "format": "date-time" + }, + "to": { + "type": "string", + "format": "date-time" + } + } + }, "errorgroups.v1.TopGroup": { "type": "object", "properties": {