diff --git a/databroker/databroker.go b/databroker/databroker.go index c91f8e267..39c459425 100644 --- a/databroker/databroker.go +++ b/databroker/databroker.go @@ -93,6 +93,13 @@ func (srv *dataBrokerServer) Put(ctx context.Context, req *databrokerpb.PutReque return srv.server.Put(ctx, req) } +func (srv *dataBrokerServer) Patch(ctx context.Context, req *databrokerpb.PatchRequest) (*databrokerpb.PatchResponse, error) { + if err := grpcutil.RequireSignedJWT(ctx, srv.sharedKey.Load()); err != nil { + return nil, err + } + return srv.server.Patch(ctx, req) +} + func (srv *dataBrokerServer) ReleaseLease(ctx context.Context, req *databrokerpb.ReleaseLeaseRequest) (*emptypb.Empty, error) { if err := grpcutil.RequireSignedJWT(ctx, srv.sharedKey.Load()); err != nil { return nil, err diff --git a/internal/databroker/server.go b/internal/databroker/server.go index 98eda6b3d..f2c8443cd 100644 --- a/internal/databroker/server.go +++ b/internal/databroker/server.go @@ -234,6 +234,45 @@ func (srv *Server) Put(ctx context.Context, req *databroker.PutRequest) (*databr return res, nil } +// Patch updates specific fields of an existing record. +func (srv *Server) Patch(ctx context.Context, req *databroker.PatchRequest) (*databroker.PatchResponse, error) { + ctx, span := trace.StartSpan(ctx, "databroker.grpc.Patch") + defer span.End() + + records := req.GetRecords() + if len(records) == 1 { + log.Info(ctx). + Str("record-type", records[0].GetType()). + Str("record-id", records[0].GetId()). + Msg("patch") + } else { + var recordType string + for _, record := range records { + recordType = record.GetType() + } + log.Info(ctx). + Int("record-count", len(records)). + Str("record-type", recordType). + Msg("patch") + } + + db, err := srv.getBackend() + if err != nil { + return nil, err + } + + serverVersion, patchedRecords, err := db.Patch(ctx, records, req.GetFieldMask()) + if err != nil { + return nil, err + } + res := &databroker.PatchResponse{ + ServerVersion: serverVersion, + Records: patchedRecords, + } + + return res, nil +} + // ReleaseLease releases a lease. func (srv *Server) ReleaseLease(ctx context.Context, req *databroker.ReleaseLeaseRequest) (*emptypb.Empty, error) { ctx, span := trace.StartSpan(ctx, "databroker.grpc.ReleaseLease") diff --git a/internal/databroker/server_test.go b/internal/databroker/server_test.go index 19ea8c3ef..0a348c770 100644 --- a/internal/databroker/server_test.go +++ b/internal/databroker/server_test.go @@ -18,6 +18,7 @@ import ( "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/fieldmaskpb" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" @@ -84,6 +85,58 @@ func TestServer_Get(t *testing.T) { }) } +func TestServer_Patch(t *testing.T) { + cfg := newServerConfig() + srv := newServer(cfg) + + s := &session.Session{ + Id: "1", + OauthToken: &session.OAuthToken{AccessToken: "access-token"}, + } + data := protoutil.NewAny(s) + _, err := srv.Put(context.Background(), &databroker.PutRequest{ + Records: []*databroker.Record{{ + Type: data.TypeUrl, + Id: s.Id, + Data: data, + }}, + }) + require.NoError(t, err) + + fm, err := fieldmaskpb.New(s, "accessed_at") + require.NoError(t, err) + + now := timestamppb.Now() + s.AccessedAt = now + s.OauthToken.AccessToken = "access-token-field-ignored" + data = protoutil.NewAny(s) + patchResponse, err := srv.Patch(context.Background(), &databroker.PatchRequest{ + Records: []*databroker.Record{{ + Type: data.TypeUrl, + Id: s.Id, + Data: data, + }}, + FieldMask: fm, + }) + require.NoError(t, err) + testutil.AssertProtoEqual(t, protoutil.NewAny(&session.Session{ + Id: "1", + AccessedAt: now, + OauthToken: &session.OAuthToken{AccessToken: "access-token"}, + }), patchResponse.GetRecord().GetData()) + + getResponse, err := srv.Get(context.Background(), &databroker.GetRequest{ + Type: data.TypeUrl, + Id: s.Id, + }) + require.NoError(t, err) + testutil.AssertProtoEqual(t, protoutil.NewAny(&session.Session{ + Id: "1", + AccessedAt: now, + OauthToken: &session.OAuthToken{AccessToken: "access-token"}, + }), getResponse.GetRecord().GetData()) +} + func TestServer_Options(t *testing.T) { cfg := newServerConfig() srv := newServer(cfg) diff --git a/pkg/grpc/databroker/databroker.go b/pkg/grpc/databroker/databroker.go index 91a57f7a7..08d7a82d3 100644 --- a/pkg/grpc/databroker/databroker.go +++ b/pkg/grpc/databroker/databroker.go @@ -150,6 +150,15 @@ func (x *PutResponse) GetRecord() *Record { return records[0] } +// GetRecord gets the first record, or nil if there are none. +func (x *PatchResponse) GetRecord() *Record { + records := x.GetRecords() + if len(records) == 0 { + return nil + } + return records[0] +} + // SetFilterByID sets the filter to an id. func (x *QueryRequest) SetFilterByID(id string) { x.Filter = &structpb.Struct{Fields: map[string]*structpb.Value{ diff --git a/pkg/grpc/databroker/databroker.pb.go b/pkg/grpc/databroker/databroker.pb.go index 7f8c30b83..1e2fe2209 100644 --- a/pkg/grpc/databroker/databroker.pb.go +++ b/pkg/grpc/databroker/databroker.pb.go @@ -16,6 +16,7 @@ import ( anypb "google.golang.org/protobuf/types/known/anypb" durationpb "google.golang.org/protobuf/types/known/durationpb" emptypb "google.golang.org/protobuf/types/known/emptypb" + fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb" structpb "google.golang.org/protobuf/types/known/structpb" timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" @@ -623,6 +624,116 @@ func (x *PutResponse) GetRecords() []*Record { return nil } +type PatchRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Records []*Record `protobuf:"bytes,1,rep,name=records,proto3" json:"records,omitempty"` + FieldMask *fieldmaskpb.FieldMask `protobuf:"bytes,2,opt,name=field_mask,json=fieldMask,proto3" json:"field_mask,omitempty"` +} + +func (x *PatchRequest) Reset() { + *x = PatchRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_databroker_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PatchRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PatchRequest) ProtoMessage() {} + +func (x *PatchRequest) ProtoReflect() protoreflect.Message { + mi := &file_databroker_proto_msgTypes[10] + 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 PatchRequest.ProtoReflect.Descriptor instead. +func (*PatchRequest) Descriptor() ([]byte, []int) { + return file_databroker_proto_rawDescGZIP(), []int{10} +} + +func (x *PatchRequest) GetRecords() []*Record { + if x != nil { + return x.Records + } + return nil +} + +func (x *PatchRequest) GetFieldMask() *fieldmaskpb.FieldMask { + if x != nil { + return x.FieldMask + } + return nil +} + +type PatchResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ServerVersion uint64 `protobuf:"varint,1,opt,name=server_version,json=serverVersion,proto3" json:"server_version,omitempty"` + Records []*Record `protobuf:"bytes,2,rep,name=records,proto3" json:"records,omitempty"` +} + +func (x *PatchResponse) Reset() { + *x = PatchResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_databroker_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PatchResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PatchResponse) ProtoMessage() {} + +func (x *PatchResponse) ProtoReflect() protoreflect.Message { + mi := &file_databroker_proto_msgTypes[11] + 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 PatchResponse.ProtoReflect.Descriptor instead. +func (*PatchResponse) Descriptor() ([]byte, []int) { + return file_databroker_proto_rawDescGZIP(), []int{11} +} + +func (x *PatchResponse) GetServerVersion() uint64 { + if x != nil { + return x.ServerVersion + } + return 0 +} + +func (x *PatchResponse) GetRecords() []*Record { + if x != nil { + return x.Records + } + return nil +} + type SetOptionsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -635,7 +746,7 @@ type SetOptionsRequest struct { func (x *SetOptionsRequest) Reset() { *x = SetOptionsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_databroker_proto_msgTypes[10] + mi := &file_databroker_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -648,7 +759,7 @@ func (x *SetOptionsRequest) String() string { func (*SetOptionsRequest) ProtoMessage() {} func (x *SetOptionsRequest) ProtoReflect() protoreflect.Message { - mi := &file_databroker_proto_msgTypes[10] + mi := &file_databroker_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -661,7 +772,7 @@ func (x *SetOptionsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SetOptionsRequest.ProtoReflect.Descriptor instead. func (*SetOptionsRequest) Descriptor() ([]byte, []int) { - return file_databroker_proto_rawDescGZIP(), []int{10} + return file_databroker_proto_rawDescGZIP(), []int{12} } func (x *SetOptionsRequest) GetType() string { @@ -689,7 +800,7 @@ type SetOptionsResponse struct { func (x *SetOptionsResponse) Reset() { *x = SetOptionsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_databroker_proto_msgTypes[11] + mi := &file_databroker_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -702,7 +813,7 @@ func (x *SetOptionsResponse) String() string { func (*SetOptionsResponse) ProtoMessage() {} func (x *SetOptionsResponse) ProtoReflect() protoreflect.Message { - mi := &file_databroker_proto_msgTypes[11] + mi := &file_databroker_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -715,7 +826,7 @@ func (x *SetOptionsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SetOptionsResponse.ProtoReflect.Descriptor instead. func (*SetOptionsResponse) Descriptor() ([]byte, []int) { - return file_databroker_proto_rawDescGZIP(), []int{11} + return file_databroker_proto_rawDescGZIP(), []int{13} } func (x *SetOptionsResponse) GetOptions() *Options { @@ -738,7 +849,7 @@ type SyncRequest struct { func (x *SyncRequest) Reset() { *x = SyncRequest{} if protoimpl.UnsafeEnabled { - mi := &file_databroker_proto_msgTypes[12] + mi := &file_databroker_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -751,7 +862,7 @@ func (x *SyncRequest) String() string { func (*SyncRequest) ProtoMessage() {} func (x *SyncRequest) ProtoReflect() protoreflect.Message { - mi := &file_databroker_proto_msgTypes[12] + mi := &file_databroker_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -764,7 +875,7 @@ func (x *SyncRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SyncRequest.ProtoReflect.Descriptor instead. func (*SyncRequest) Descriptor() ([]byte, []int) { - return file_databroker_proto_rawDescGZIP(), []int{12} + return file_databroker_proto_rawDescGZIP(), []int{14} } func (x *SyncRequest) GetServerVersion() uint64 { @@ -799,7 +910,7 @@ type SyncResponse struct { func (x *SyncResponse) Reset() { *x = SyncResponse{} if protoimpl.UnsafeEnabled { - mi := &file_databroker_proto_msgTypes[13] + mi := &file_databroker_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -812,7 +923,7 @@ func (x *SyncResponse) String() string { func (*SyncResponse) ProtoMessage() {} func (x *SyncResponse) ProtoReflect() protoreflect.Message { - mi := &file_databroker_proto_msgTypes[13] + mi := &file_databroker_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -825,7 +936,7 @@ func (x *SyncResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SyncResponse.ProtoReflect.Descriptor instead. func (*SyncResponse) Descriptor() ([]byte, []int) { - return file_databroker_proto_rawDescGZIP(), []int{13} + return file_databroker_proto_rawDescGZIP(), []int{15} } func (x *SyncResponse) GetRecord() *Record { @@ -846,7 +957,7 @@ type SyncLatestRequest struct { func (x *SyncLatestRequest) Reset() { *x = SyncLatestRequest{} if protoimpl.UnsafeEnabled { - mi := &file_databroker_proto_msgTypes[14] + mi := &file_databroker_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -859,7 +970,7 @@ func (x *SyncLatestRequest) String() string { func (*SyncLatestRequest) ProtoMessage() {} func (x *SyncLatestRequest) ProtoReflect() protoreflect.Message { - mi := &file_databroker_proto_msgTypes[14] + mi := &file_databroker_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -872,7 +983,7 @@ func (x *SyncLatestRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SyncLatestRequest.ProtoReflect.Descriptor instead. func (*SyncLatestRequest) Descriptor() ([]byte, []int) { - return file_databroker_proto_rawDescGZIP(), []int{14} + return file_databroker_proto_rawDescGZIP(), []int{16} } func (x *SyncLatestRequest) GetType() string { @@ -897,7 +1008,7 @@ type SyncLatestResponse struct { func (x *SyncLatestResponse) Reset() { *x = SyncLatestResponse{} if protoimpl.UnsafeEnabled { - mi := &file_databroker_proto_msgTypes[15] + mi := &file_databroker_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -910,7 +1021,7 @@ func (x *SyncLatestResponse) String() string { func (*SyncLatestResponse) ProtoMessage() {} func (x *SyncLatestResponse) ProtoReflect() protoreflect.Message { - mi := &file_databroker_proto_msgTypes[15] + mi := &file_databroker_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -923,7 +1034,7 @@ func (x *SyncLatestResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SyncLatestResponse.ProtoReflect.Descriptor instead. func (*SyncLatestResponse) Descriptor() ([]byte, []int) { - return file_databroker_proto_rawDescGZIP(), []int{15} + return file_databroker_proto_rawDescGZIP(), []int{17} } func (m *SyncLatestResponse) GetResponse() isSyncLatestResponse_Response { @@ -979,7 +1090,7 @@ type AcquireLeaseRequest struct { func (x *AcquireLeaseRequest) Reset() { *x = AcquireLeaseRequest{} if protoimpl.UnsafeEnabled { - mi := &file_databroker_proto_msgTypes[16] + mi := &file_databroker_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -992,7 +1103,7 @@ func (x *AcquireLeaseRequest) String() string { func (*AcquireLeaseRequest) ProtoMessage() {} func (x *AcquireLeaseRequest) ProtoReflect() protoreflect.Message { - mi := &file_databroker_proto_msgTypes[16] + mi := &file_databroker_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1005,7 +1116,7 @@ func (x *AcquireLeaseRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AcquireLeaseRequest.ProtoReflect.Descriptor instead. func (*AcquireLeaseRequest) Descriptor() ([]byte, []int) { - return file_databroker_proto_rawDescGZIP(), []int{16} + return file_databroker_proto_rawDescGZIP(), []int{18} } func (x *AcquireLeaseRequest) GetName() string { @@ -1035,7 +1146,7 @@ type AcquireLeaseResponse struct { func (x *AcquireLeaseResponse) Reset() { *x = AcquireLeaseResponse{} if protoimpl.UnsafeEnabled { - mi := &file_databroker_proto_msgTypes[17] + mi := &file_databroker_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1048,7 +1159,7 @@ func (x *AcquireLeaseResponse) String() string { func (*AcquireLeaseResponse) ProtoMessage() {} func (x *AcquireLeaseResponse) ProtoReflect() protoreflect.Message { - mi := &file_databroker_proto_msgTypes[17] + mi := &file_databroker_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1061,7 +1172,7 @@ func (x *AcquireLeaseResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AcquireLeaseResponse.ProtoReflect.Descriptor instead. func (*AcquireLeaseResponse) Descriptor() ([]byte, []int) { - return file_databroker_proto_rawDescGZIP(), []int{17} + return file_databroker_proto_rawDescGZIP(), []int{19} } func (x *AcquireLeaseResponse) GetId() string { @@ -1083,7 +1194,7 @@ type ReleaseLeaseRequest struct { func (x *ReleaseLeaseRequest) Reset() { *x = ReleaseLeaseRequest{} if protoimpl.UnsafeEnabled { - mi := &file_databroker_proto_msgTypes[18] + mi := &file_databroker_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1096,7 +1207,7 @@ func (x *ReleaseLeaseRequest) String() string { func (*ReleaseLeaseRequest) ProtoMessage() {} func (x *ReleaseLeaseRequest) ProtoReflect() protoreflect.Message { - mi := &file_databroker_proto_msgTypes[18] + mi := &file_databroker_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1109,7 +1220,7 @@ func (x *ReleaseLeaseRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReleaseLeaseRequest.ProtoReflect.Descriptor instead. func (*ReleaseLeaseRequest) Descriptor() ([]byte, []int) { - return file_databroker_proto_rawDescGZIP(), []int{18} + return file_databroker_proto_rawDescGZIP(), []int{20} } func (x *ReleaseLeaseRequest) GetName() string { @@ -1139,7 +1250,7 @@ type RenewLeaseRequest struct { func (x *RenewLeaseRequest) Reset() { *x = RenewLeaseRequest{} if protoimpl.UnsafeEnabled { - mi := &file_databroker_proto_msgTypes[19] + mi := &file_databroker_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1152,7 +1263,7 @@ func (x *RenewLeaseRequest) String() string { func (*RenewLeaseRequest) ProtoMessage() {} func (x *RenewLeaseRequest) ProtoReflect() protoreflect.Message { - mi := &file_databroker_proto_msgTypes[19] + mi := &file_databroker_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1165,7 +1276,7 @@ func (x *RenewLeaseRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RenewLeaseRequest.ProtoReflect.Descriptor instead. func (*RenewLeaseRequest) Descriptor() ([]byte, []int) { - return file_databroker_proto_rawDescGZIP(), []int{19} + return file_databroker_proto_rawDescGZIP(), []int{21} } func (x *RenewLeaseRequest) GetName() string { @@ -1199,174 +1310,194 @@ var file_databroker_proto_rawDesc = []byte{ 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 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, 0xe8, 0x01, 0x0a, 0x06, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, - 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x0e, - 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x28, - 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, - 0x6e, 0x79, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3b, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x69, - 0x66, 0x69, 0x65, 0x64, 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, 0x6d, 0x6f, 0x64, 0x69, 0x66, - 0x69, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, - 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, - 0x22, 0x65, 0x0a, 0x08, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0e, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x15, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x72, 0x65, - 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x13, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x37, 0x0a, 0x07, 0x4f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, - 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, - 0x22, 0x30, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, - 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x69, 0x64, 0x22, 0x39, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x12, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x52, - 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x06, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x22, 0x29, 0x0a, - 0x11, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x22, 0x97, 0x01, 0x0a, 0x0c, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, - 0x65, 0x72, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x12, 0x2f, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x22, 0xac, 0x01, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, - 0x65, 0x72, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, - 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, - 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0d, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x22, 0x3a, 0x0a, 0x0a, 0x50, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x2c, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x12, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x52, 0x65, - 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x22, 0x62, 0x0a, - 0x0b, 0x50, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x02, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, + 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, + 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, 0xe8, 0x01, 0x0a, 0x06, 0x52, 0x65, 0x63, 0x6f, + 0x72, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x28, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3b, 0x0a, 0x0b, 0x6d, 0x6f, + 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 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, 0x6d, 0x6f, 0x64, + 0x69, 0x66, 0x69, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, + 0x41, 0x74, 0x22, 0x65, 0x0a, 0x08, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, + 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x15, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, + 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x13, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x52, 0x65, 0x63, 0x6f, + 0x72, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x37, 0x0a, 0x07, 0x4f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, + 0x74, 0x79, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, + 0x74, 0x79, 0x22, 0x30, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x22, 0x39, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, + 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x06, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x22, + 0x29, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x22, 0x97, 0x01, 0x0a, 0x0c, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x14, 0x0a, + 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x12, 0x2f, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x06, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x22, 0xac, 0x01, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, + 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x72, 0x65, 0x63, + 0x6f, 0x72, 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, + 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x22, 0x3a, 0x0a, 0x0a, 0x50, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x2c, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, + 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x22, + 0x62, 0x0a, 0x0b, 0x50, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, + 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, + 0x6b, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f, + 0x72, 0x64, 0x73, 0x22, 0x77, 0x0a, 0x0c, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, - 0x73, 0x22, 0x56, 0x0a, 0x11, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x07, 0x6f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x61, - 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x43, 0x0a, 0x12, 0x53, 0x65, 0x74, - 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x2d, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x6f, - 0x0a, 0x0b, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, + 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 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, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, + 0x6b, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0x64, 0x0a, 0x0d, + 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x56, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x72, 0x65, - 0x63, 0x6f, 0x72, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, - 0x3a, 0x0a, 0x0c, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x12, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x63, - 0x6f, 0x72, 0x64, 0x52, 0x06, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x22, 0x27, 0x0a, 0x11, 0x53, - 0x79, 0x6e, 0x63, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x22, 0x82, 0x01, 0x0a, 0x12, 0x53, 0x79, 0x6e, 0x63, 0x4c, 0x61, 0x74, - 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x72, - 0x65, 0x63, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x64, 0x61, - 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x48, - 0x00, 0x52, 0x06, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x32, 0x0a, 0x08, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x64, 0x61, - 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x73, 0x48, 0x00, 0x52, 0x08, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x0a, 0x0a, - 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x60, 0x0a, 0x13, 0x41, 0x63, 0x71, - 0x75, 0x69, 0x72, 0x65, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x02, 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, 0x22, 0x26, 0x0a, 0x14, 0x41, - 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x22, 0x39, 0x0a, 0x13, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4c, 0x65, - 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, - 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x6e, - 0x0a, 0x11, 0x52, 0x65, 0x6e, 0x65, 0x77, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 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, 0x32, 0xbf, - 0x05, 0x0a, 0x11, 0x44, 0x61, 0x74, 0x61, 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x51, 0x0a, 0x0c, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x4c, - 0x65, 0x61, 0x73, 0x65, 0x12, 0x1f, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, - 0x72, 0x2e, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, + 0x65, 0x72, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, + 0x64, 0x73, 0x22, 0x56, 0x0a, 0x11, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x07, 0x6f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, + 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x43, 0x0a, 0x12, 0x53, 0x65, + 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x2d, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x4f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, + 0x6f, 0x0a, 0x0b, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, + 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x72, + 0x65, 0x63, 0x6f, 0x72, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x22, 0x3a, 0x0a, 0x0c, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x12, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x52, 0x65, + 0x63, 0x6f, 0x72, 0x64, 0x52, 0x06, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x22, 0x27, 0x0a, 0x11, + 0x53, 0x79, 0x6e, 0x63, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x82, 0x01, 0x0a, 0x12, 0x53, 0x79, 0x6e, 0x63, 0x4c, 0x61, + 0x74, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x06, + 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x64, + 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x32, 0x0a, 0x08, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x64, + 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x48, 0x00, 0x52, 0x08, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x0a, + 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x60, 0x0a, 0x13, 0x41, 0x63, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x02, 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, 0x22, 0x26, 0x0a, 0x14, + 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x22, 0x39, 0x0a, 0x13, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4c, + 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, + 0x6e, 0x0a, 0x11, 0x52, 0x65, 0x6e, 0x65, 0x77, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 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, 0x32, + 0xfd, 0x05, 0x0a, 0x11, 0x44, 0x61, 0x74, 0x61, 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x51, 0x0a, 0x0c, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x4c, 0x65, 0x61, 0x73, 0x65, 0x12, 0x1f, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x16, - 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, - 0x6b, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x42, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x16, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, - 0x72, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x03, 0x50, 0x75, 0x74, 0x12, 0x16, 0x2e, 0x64, 0x61, 0x74, - 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x50, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, - 0x50, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x05, 0x51, - 0x75, 0x65, 0x72, 0x79, 0x12, 0x18, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, - 0x72, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, - 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x52, 0x65, 0x6c, - 0x65, 0x61, 0x73, 0x65, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x12, 0x1f, 0x2e, 0x64, 0x61, 0x74, 0x61, - 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4c, 0x65, - 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x12, 0x43, 0x0a, 0x0a, 0x52, 0x65, 0x6e, 0x65, 0x77, 0x4c, 0x65, 0x61, 0x73, 0x65, - 0x12, 0x1d, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x52, 0x65, - 0x6e, 0x65, 0x77, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4b, 0x0a, 0x0a, 0x53, 0x65, 0x74, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, - 0x65, 0x72, 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, - 0x72, 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x17, 0x2e, 0x64, - 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, - 0x65, 0x72, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, - 0x01, 0x12, 0x4d, 0x0a, 0x0a, 0x53, 0x79, 0x6e, 0x63, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x12, - 0x1d, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x53, 0x79, 0x6e, - 0x63, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, + 0x6b, 0x65, 0x72, 0x2e, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x4c, 0x65, 0x61, 0x73, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, + 0x16, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, + 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x42, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x16, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, + 0x65, 0x72, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x03, 0x50, 0x75, 0x74, 0x12, 0x16, 0x2e, 0x64, 0x61, + 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x50, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, + 0x2e, 0x50, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x05, + 0x50, 0x61, 0x74, 0x63, 0x68, 0x12, 0x18, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, + 0x65, 0x72, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x19, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x74, + 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x05, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x12, 0x18, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, + 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, + 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x52, 0x65, 0x6c, 0x65, + 0x61, 0x73, 0x65, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x12, 0x1f, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, + 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4c, 0x65, 0x61, + 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x12, 0x43, 0x0a, 0x0a, 0x52, 0x65, 0x6e, 0x65, 0x77, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x12, + 0x1d, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x6e, + 0x65, 0x77, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4b, 0x0a, 0x0a, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, + 0x72, 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, + 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x17, 0x2e, 0x64, 0x61, + 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, + 0x72, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, + 0x12, 0x4d, 0x0a, 0x0a, 0x53, 0x79, 0x6e, 0x63, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x53, 0x79, 0x6e, 0x63, - 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, - 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, - 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, - 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, - 0x6f, 0x6b, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, + 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x4c, + 0x61, 0x74, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x42, + 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, + 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, + 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, + 0x6b, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1381,7 +1512,7 @@ func file_databroker_proto_rawDescGZIP() []byte { return file_databroker_proto_rawDescData } -var file_databroker_proto_msgTypes = make([]protoimpl.MessageInfo, 20) +var file_databroker_proto_msgTypes = make([]protoimpl.MessageInfo, 22) var file_databroker_proto_goTypes = []interface{}{ (*Record)(nil), // 0: databroker.Record (*Versions)(nil), // 1: databroker.Versions @@ -1393,63 +1524,71 @@ var file_databroker_proto_goTypes = []interface{}{ (*QueryResponse)(nil), // 7: databroker.QueryResponse (*PutRequest)(nil), // 8: databroker.PutRequest (*PutResponse)(nil), // 9: databroker.PutResponse - (*SetOptionsRequest)(nil), // 10: databroker.SetOptionsRequest - (*SetOptionsResponse)(nil), // 11: databroker.SetOptionsResponse - (*SyncRequest)(nil), // 12: databroker.SyncRequest - (*SyncResponse)(nil), // 13: databroker.SyncResponse - (*SyncLatestRequest)(nil), // 14: databroker.SyncLatestRequest - (*SyncLatestResponse)(nil), // 15: databroker.SyncLatestResponse - (*AcquireLeaseRequest)(nil), // 16: databroker.AcquireLeaseRequest - (*AcquireLeaseResponse)(nil), // 17: databroker.AcquireLeaseResponse - (*ReleaseLeaseRequest)(nil), // 18: databroker.ReleaseLeaseRequest - (*RenewLeaseRequest)(nil), // 19: databroker.RenewLeaseRequest - (*anypb.Any)(nil), // 20: google.protobuf.Any - (*timestamppb.Timestamp)(nil), // 21: google.protobuf.Timestamp - (*structpb.Struct)(nil), // 22: google.protobuf.Struct - (*durationpb.Duration)(nil), // 23: google.protobuf.Duration - (*emptypb.Empty)(nil), // 24: google.protobuf.Empty + (*PatchRequest)(nil), // 10: databroker.PatchRequest + (*PatchResponse)(nil), // 11: databroker.PatchResponse + (*SetOptionsRequest)(nil), // 12: databroker.SetOptionsRequest + (*SetOptionsResponse)(nil), // 13: databroker.SetOptionsResponse + (*SyncRequest)(nil), // 14: databroker.SyncRequest + (*SyncResponse)(nil), // 15: databroker.SyncResponse + (*SyncLatestRequest)(nil), // 16: databroker.SyncLatestRequest + (*SyncLatestResponse)(nil), // 17: databroker.SyncLatestResponse + (*AcquireLeaseRequest)(nil), // 18: databroker.AcquireLeaseRequest + (*AcquireLeaseResponse)(nil), // 19: databroker.AcquireLeaseResponse + (*ReleaseLeaseRequest)(nil), // 20: databroker.ReleaseLeaseRequest + (*RenewLeaseRequest)(nil), // 21: databroker.RenewLeaseRequest + (*anypb.Any)(nil), // 22: google.protobuf.Any + (*timestamppb.Timestamp)(nil), // 23: google.protobuf.Timestamp + (*structpb.Struct)(nil), // 24: google.protobuf.Struct + (*fieldmaskpb.FieldMask)(nil), // 25: google.protobuf.FieldMask + (*durationpb.Duration)(nil), // 26: google.protobuf.Duration + (*emptypb.Empty)(nil), // 27: google.protobuf.Empty } var file_databroker_proto_depIdxs = []int32{ - 20, // 0: databroker.Record.data:type_name -> google.protobuf.Any - 21, // 1: databroker.Record.modified_at:type_name -> google.protobuf.Timestamp - 21, // 2: databroker.Record.deleted_at:type_name -> google.protobuf.Timestamp + 22, // 0: databroker.Record.data:type_name -> google.protobuf.Any + 23, // 1: databroker.Record.modified_at:type_name -> google.protobuf.Timestamp + 23, // 2: databroker.Record.deleted_at:type_name -> google.protobuf.Timestamp 0, // 3: databroker.GetResponse.record:type_name -> databroker.Record - 22, // 4: databroker.QueryRequest.filter:type_name -> google.protobuf.Struct + 24, // 4: databroker.QueryRequest.filter:type_name -> google.protobuf.Struct 0, // 5: databroker.QueryResponse.records:type_name -> databroker.Record 0, // 6: databroker.PutRequest.records:type_name -> databroker.Record 0, // 7: databroker.PutResponse.records:type_name -> databroker.Record - 2, // 8: databroker.SetOptionsRequest.options:type_name -> databroker.Options - 2, // 9: databroker.SetOptionsResponse.options:type_name -> databroker.Options - 0, // 10: databroker.SyncResponse.record:type_name -> databroker.Record - 0, // 11: databroker.SyncLatestResponse.record:type_name -> databroker.Record - 1, // 12: databroker.SyncLatestResponse.versions:type_name -> databroker.Versions - 23, // 13: databroker.AcquireLeaseRequest.duration:type_name -> google.protobuf.Duration - 23, // 14: databroker.RenewLeaseRequest.duration:type_name -> google.protobuf.Duration - 16, // 15: databroker.DataBrokerService.AcquireLease:input_type -> databroker.AcquireLeaseRequest - 3, // 16: databroker.DataBrokerService.Get:input_type -> databroker.GetRequest - 24, // 17: databroker.DataBrokerService.ListTypes:input_type -> google.protobuf.Empty - 8, // 18: databroker.DataBrokerService.Put:input_type -> databroker.PutRequest - 6, // 19: databroker.DataBrokerService.Query:input_type -> databroker.QueryRequest - 18, // 20: databroker.DataBrokerService.ReleaseLease:input_type -> databroker.ReleaseLeaseRequest - 19, // 21: databroker.DataBrokerService.RenewLease:input_type -> databroker.RenewLeaseRequest - 10, // 22: databroker.DataBrokerService.SetOptions:input_type -> databroker.SetOptionsRequest - 12, // 23: databroker.DataBrokerService.Sync:input_type -> databroker.SyncRequest - 14, // 24: databroker.DataBrokerService.SyncLatest:input_type -> databroker.SyncLatestRequest - 17, // 25: databroker.DataBrokerService.AcquireLease:output_type -> databroker.AcquireLeaseResponse - 4, // 26: databroker.DataBrokerService.Get:output_type -> databroker.GetResponse - 5, // 27: databroker.DataBrokerService.ListTypes:output_type -> databroker.ListTypesResponse - 9, // 28: databroker.DataBrokerService.Put:output_type -> databroker.PutResponse - 7, // 29: databroker.DataBrokerService.Query:output_type -> databroker.QueryResponse - 24, // 30: databroker.DataBrokerService.ReleaseLease:output_type -> google.protobuf.Empty - 24, // 31: databroker.DataBrokerService.RenewLease:output_type -> google.protobuf.Empty - 11, // 32: databroker.DataBrokerService.SetOptions:output_type -> databroker.SetOptionsResponse - 13, // 33: databroker.DataBrokerService.Sync:output_type -> databroker.SyncResponse - 15, // 34: databroker.DataBrokerService.SyncLatest:output_type -> databroker.SyncLatestResponse - 25, // [25:35] is the sub-list for method output_type - 15, // [15:25] is the sub-list for method input_type - 15, // [15:15] is the sub-list for extension type_name - 15, // [15:15] is the sub-list for extension extendee - 0, // [0:15] is the sub-list for field type_name + 0, // 8: databroker.PatchRequest.records:type_name -> databroker.Record + 25, // 9: databroker.PatchRequest.field_mask:type_name -> google.protobuf.FieldMask + 0, // 10: databroker.PatchResponse.records:type_name -> databroker.Record + 2, // 11: databroker.SetOptionsRequest.options:type_name -> databroker.Options + 2, // 12: databroker.SetOptionsResponse.options:type_name -> databroker.Options + 0, // 13: databroker.SyncResponse.record:type_name -> databroker.Record + 0, // 14: databroker.SyncLatestResponse.record:type_name -> databroker.Record + 1, // 15: databroker.SyncLatestResponse.versions:type_name -> databroker.Versions + 26, // 16: databroker.AcquireLeaseRequest.duration:type_name -> google.protobuf.Duration + 26, // 17: databroker.RenewLeaseRequest.duration:type_name -> google.protobuf.Duration + 18, // 18: databroker.DataBrokerService.AcquireLease:input_type -> databroker.AcquireLeaseRequest + 3, // 19: databroker.DataBrokerService.Get:input_type -> databroker.GetRequest + 27, // 20: databroker.DataBrokerService.ListTypes:input_type -> google.protobuf.Empty + 8, // 21: databroker.DataBrokerService.Put:input_type -> databroker.PutRequest + 10, // 22: databroker.DataBrokerService.Patch:input_type -> databroker.PatchRequest + 6, // 23: databroker.DataBrokerService.Query:input_type -> databroker.QueryRequest + 20, // 24: databroker.DataBrokerService.ReleaseLease:input_type -> databroker.ReleaseLeaseRequest + 21, // 25: databroker.DataBrokerService.RenewLease:input_type -> databroker.RenewLeaseRequest + 12, // 26: databroker.DataBrokerService.SetOptions:input_type -> databroker.SetOptionsRequest + 14, // 27: databroker.DataBrokerService.Sync:input_type -> databroker.SyncRequest + 16, // 28: databroker.DataBrokerService.SyncLatest:input_type -> databroker.SyncLatestRequest + 19, // 29: databroker.DataBrokerService.AcquireLease:output_type -> databroker.AcquireLeaseResponse + 4, // 30: databroker.DataBrokerService.Get:output_type -> databroker.GetResponse + 5, // 31: databroker.DataBrokerService.ListTypes:output_type -> databroker.ListTypesResponse + 9, // 32: databroker.DataBrokerService.Put:output_type -> databroker.PutResponse + 11, // 33: databroker.DataBrokerService.Patch:output_type -> databroker.PatchResponse + 7, // 34: databroker.DataBrokerService.Query:output_type -> databroker.QueryResponse + 27, // 35: databroker.DataBrokerService.ReleaseLease:output_type -> google.protobuf.Empty + 27, // 36: databroker.DataBrokerService.RenewLease:output_type -> google.protobuf.Empty + 13, // 37: databroker.DataBrokerService.SetOptions:output_type -> databroker.SetOptionsResponse + 15, // 38: databroker.DataBrokerService.Sync:output_type -> databroker.SyncResponse + 17, // 39: databroker.DataBrokerService.SyncLatest:output_type -> databroker.SyncLatestResponse + 29, // [29:40] is the sub-list for method output_type + 18, // [18:29] is the sub-list for method input_type + 18, // [18:18] is the sub-list for extension type_name + 18, // [18:18] is the sub-list for extension extendee + 0, // [0:18] is the sub-list for field type_name } func init() { file_databroker_proto_init() } @@ -1579,7 +1718,7 @@ func file_databroker_proto_init() { } } file_databroker_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetOptionsRequest); i { + switch v := v.(*PatchRequest); i { case 0: return &v.state case 1: @@ -1591,7 +1730,7 @@ func file_databroker_proto_init() { } } file_databroker_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetOptionsResponse); i { + switch v := v.(*PatchResponse); i { case 0: return &v.state case 1: @@ -1603,7 +1742,7 @@ func file_databroker_proto_init() { } } file_databroker_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SyncRequest); i { + switch v := v.(*SetOptionsRequest); i { case 0: return &v.state case 1: @@ -1615,7 +1754,7 @@ func file_databroker_proto_init() { } } file_databroker_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SyncResponse); i { + switch v := v.(*SetOptionsResponse); i { case 0: return &v.state case 1: @@ -1627,7 +1766,7 @@ func file_databroker_proto_init() { } } file_databroker_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SyncLatestRequest); i { + switch v := v.(*SyncRequest); i { case 0: return &v.state case 1: @@ -1639,7 +1778,7 @@ func file_databroker_proto_init() { } } file_databroker_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SyncLatestResponse); i { + switch v := v.(*SyncResponse); i { case 0: return &v.state case 1: @@ -1651,7 +1790,7 @@ func file_databroker_proto_init() { } } file_databroker_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AcquireLeaseRequest); i { + switch v := v.(*SyncLatestRequest); i { case 0: return &v.state case 1: @@ -1663,7 +1802,7 @@ func file_databroker_proto_init() { } } file_databroker_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AcquireLeaseResponse); i { + switch v := v.(*SyncLatestResponse); i { case 0: return &v.state case 1: @@ -1675,7 +1814,7 @@ func file_databroker_proto_init() { } } file_databroker_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReleaseLeaseRequest); i { + switch v := v.(*AcquireLeaseRequest); i { case 0: return &v.state case 1: @@ -1687,6 +1826,30 @@ func file_databroker_proto_init() { } } file_databroker_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AcquireLeaseResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_databroker_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReleaseLeaseRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_databroker_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RenewLeaseRequest); i { case 0: return &v.state @@ -1700,7 +1863,7 @@ func file_databroker_proto_init() { } } file_databroker_proto_msgTypes[2].OneofWrappers = []interface{}{} - file_databroker_proto_msgTypes[15].OneofWrappers = []interface{}{ + file_databroker_proto_msgTypes[17].OneofWrappers = []interface{}{ (*SyncLatestResponse_Record)(nil), (*SyncLatestResponse_Versions)(nil), } @@ -1710,7 +1873,7 @@ func file_databroker_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_databroker_proto_rawDesc, NumEnums: 0, - NumMessages: 20, + NumMessages: 22, NumExtensions: 0, NumServices: 1, }, @@ -1744,6 +1907,8 @@ type DataBrokerServiceClient interface { ListTypes(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ListTypesResponse, error) // Put saves a record. Put(ctx context.Context, in *PutRequest, opts ...grpc.CallOption) (*PutResponse, error) + // Patch updates specific fields of an existing record. + Patch(ctx context.Context, in *PatchRequest, opts ...grpc.CallOption) (*PatchResponse, error) // Query queries for records. Query(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error) // ReleaseLease releases a distributed mutex lease. @@ -1802,6 +1967,15 @@ func (c *dataBrokerServiceClient) Put(ctx context.Context, in *PutRequest, opts return out, nil } +func (c *dataBrokerServiceClient) Patch(ctx context.Context, in *PatchRequest, opts ...grpc.CallOption) (*PatchResponse, error) { + out := new(PatchResponse) + err := c.cc.Invoke(ctx, "/databroker.DataBrokerService/Patch", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *dataBrokerServiceClient) Query(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error) { out := new(QueryResponse) err := c.cc.Invoke(ctx, "/databroker.DataBrokerService/Query", in, out, opts...) @@ -1912,6 +2086,8 @@ type DataBrokerServiceServer interface { ListTypes(context.Context, *emptypb.Empty) (*ListTypesResponse, error) // Put saves a record. Put(context.Context, *PutRequest) (*PutResponse, error) + // Patch updates specific fields of an existing record. + Patch(context.Context, *PatchRequest) (*PatchResponse, error) // Query queries for records. Query(context.Context, *QueryRequest) (*QueryResponse, error) // ReleaseLease releases a distributed mutex lease. @@ -1942,6 +2118,9 @@ func (*UnimplementedDataBrokerServiceServer) ListTypes(context.Context, *emptypb func (*UnimplementedDataBrokerServiceServer) Put(context.Context, *PutRequest) (*PutResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Put not implemented") } +func (*UnimplementedDataBrokerServiceServer) Patch(context.Context, *PatchRequest) (*PatchResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Patch not implemented") +} func (*UnimplementedDataBrokerServiceServer) Query(context.Context, *QueryRequest) (*QueryResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Query not implemented") } @@ -2037,6 +2216,24 @@ func _DataBrokerService_Put_Handler(srv interface{}, ctx context.Context, dec fu return interceptor(ctx, in, info, handler) } +func _DataBrokerService_Patch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PatchRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DataBrokerServiceServer).Patch(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/databroker.DataBrokerService/Patch", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DataBrokerServiceServer).Patch(ctx, req.(*PatchRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _DataBrokerService_Query_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryRequest) if err := dec(in); err != nil { @@ -2171,6 +2368,10 @@ var _DataBrokerService_serviceDesc = grpc.ServiceDesc{ MethodName: "Put", Handler: _DataBrokerService_Put_Handler, }, + { + MethodName: "Patch", + Handler: _DataBrokerService_Patch_Handler, + }, { MethodName: "Query", Handler: _DataBrokerService_Query_Handler, diff --git a/pkg/grpc/databroker/databroker.proto b/pkg/grpc/databroker/databroker.proto index bae04b7fc..a7676e763 100644 --- a/pkg/grpc/databroker/databroker.proto +++ b/pkg/grpc/databroker/databroker.proto @@ -6,6 +6,7 @@ option go_package = "github.com/pomerium/pomerium/pkg/grpc/databroker"; import "google/protobuf/any.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/empty.proto"; +import "google/protobuf/field_mask.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; @@ -60,6 +61,15 @@ message PutResponse { repeated Record records = 2; } +message PatchRequest { + repeated Record records = 1; + google.protobuf.FieldMask field_mask = 2; +} +message PatchResponse { + uint64 server_version = 1; + repeated Record records = 2; +} + message SetOptionsRequest { string type = 1; Options options = 2; @@ -114,6 +124,8 @@ service DataBrokerService { rpc ListTypes(google.protobuf.Empty) returns (ListTypesResponse); // Put saves a record. rpc Put(PutRequest) returns (PutResponse); + // Patch updates specific fields of an existing record. + rpc Patch(PatchRequest) returns (PatchResponse); // Query queries for records. rpc Query(QueryRequest) returns (QueryResponse); // ReleaseLease releases a distributed mutex lease. diff --git a/pkg/grpc/databroker/mock_databroker/databroker.pb.go b/pkg/grpc/databroker/mock_databroker/databroker.pb.go index b73c27f1e..66e184030 100644 --- a/pkg/grpc/databroker/mock_databroker/databroker.pb.go +++ b/pkg/grpc/databroker/mock_databroker/databroker.pb.go @@ -133,6 +133,26 @@ func (mr *MockDataBrokerServiceClientMockRecorder) ListTypes(ctx, in interface{} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTypes", reflect.TypeOf((*MockDataBrokerServiceClient)(nil).ListTypes), varargs...) } +// Patch mocks base method. +func (m *MockDataBrokerServiceClient) Patch(ctx context.Context, in *databroker.PatchRequest, opts ...grpc.CallOption) (*databroker.PatchResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Patch", varargs...) + ret0, _ := ret[0].(*databroker.PatchResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Patch indicates an expected call of Patch. +func (mr *MockDataBrokerServiceClientMockRecorder) Patch(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockDataBrokerServiceClient)(nil).Patch), varargs...) +} + // Put mocks base method. func (m *MockDataBrokerServiceClient) Put(ctx context.Context, in *databroker.PutRequest, opts ...grpc.CallOption) (*databroker.PutResponse, error) { m.ctrl.T.Helper() @@ -587,6 +607,21 @@ func (mr *MockDataBrokerServiceServerMockRecorder) ListTypes(arg0, arg1 interfac return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTypes", reflect.TypeOf((*MockDataBrokerServiceServer)(nil).ListTypes), arg0, arg1) } +// Patch mocks base method. +func (m *MockDataBrokerServiceServer) Patch(arg0 context.Context, arg1 *databroker.PatchRequest) (*databroker.PatchResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Patch", arg0, arg1) + ret0, _ := ret[0].(*databroker.PatchResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Patch indicates an expected call of Patch. +func (mr *MockDataBrokerServiceServerMockRecorder) Patch(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockDataBrokerServiceServer)(nil).Patch), arg0, arg1) +} + // Put mocks base method. func (m *MockDataBrokerServiceServer) Put(arg0 context.Context, arg1 *databroker.PutRequest) (*databroker.PutResponse, error) { m.ctrl.T.Helper() diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 170c43fa7..dfdbff6ea 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -11,6 +11,7 @@ import ( "google.golang.org/grpc/status" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/known/anypb" + "google.golang.org/protobuf/types/known/fieldmaskpb" "github.com/pomerium/pomerium/internal/log" "github.com/pomerium/pomerium/pkg/grpc/databroker" @@ -37,6 +38,8 @@ type Backend interface { ListTypes(ctx context.Context) ([]string, error) // Put is used to insert or update records. Put(ctx context.Context, records []*databroker.Record) (serverVersion uint64, err error) + // Patch is used to update specific fields of existing records. + Patch(ctx context.Context, records []*databroker.Record, fields *fieldmaskpb.FieldMask) (serverVersion uint64, patchedRecords []*databroker.Record, err error) // SetOptions sets the options for a type. SetOptions(ctx context.Context, recordType string, options *databroker.Options) error // Sync syncs record changes after the specified version. diff --git a/pkg/storage/storagetest/storagetest.go b/pkg/storage/storagetest/storagetest.go index e34e4f00b..f4dff4006 100644 --- a/pkg/storage/storagetest/storagetest.go +++ b/pkg/storage/storagetest/storagetest.go @@ -4,6 +4,11 @@ package storagetest import ( "context" + "fmt" + "os" + "runtime" + "strconv" + "sync" "testing" "github.com/stretchr/testify/assert" @@ -18,15 +23,8 @@ import ( "github.com/pomerium/pomerium/pkg/storage" ) -// BackendWithPatch is a storage.Backend with an additional Patch() method. -// TODO: delete this type once Patch() is added to the storage.Backend interface -type BackendWithPatch interface { - storage.Backend - Patch(context.Context, []*databroker.Record, *fieldmaskpb.FieldMask) (uint64, []*databroker.Record, error) -} - // TestBackendPatch verifies the behavior of the backend Patch() method. -func TestBackendPatch(t *testing.T, ctx context.Context, backend BackendWithPatch) { //nolint:revive +func TestBackendPatch(t *testing.T, ctx context.Context, backend storage.Backend) { //nolint:revive mkRecord := func(s *session.Session) *databroker.Record { a, _ := anypb.New(s) return &databroker.Record{ @@ -36,83 +34,141 @@ func TestBackendPatch(t *testing.T, ctx context.Context, backend BackendWithPatc } } - // Populate an initial set of session records. - s1 := &session.Session{ - Id: "session-1", - IdToken: &session.IDToken{Issuer: "issuer-1"}, - OauthToken: &session.OAuthToken{AccessToken: "access-token-1"}, - } - s2 := &session.Session{ - Id: "session-2", - IdToken: &session.IDToken{Issuer: "issuer-2"}, - OauthToken: &session.OAuthToken{AccessToken: "access-token-2"}, - } - s3 := &session.Session{ - Id: "session-3", - IdToken: &session.IDToken{Issuer: "issuer-3"}, - OauthToken: &session.OAuthToken{AccessToken: "access-token-3"}, - } - initial := []*databroker.Record{mkRecord(s1), mkRecord(s2), mkRecord(s3)} - - _, err := backend.Put(ctx, initial) - require.NoError(t, err) - - // Now patch just the oauth_token field. - u1 := &session.Session{ - Id: "session-1", - OauthToken: &session.OAuthToken{AccessToken: "access-token-1-new"}, - } - u2 := &session.Session{ - Id: "session-4-does-not-exist", - OauthToken: &session.OAuthToken{AccessToken: "access-token-4-new"}, - } - u3 := &session.Session{ - Id: "session-3", - OauthToken: &session.OAuthToken{AccessToken: "access-token-3-new"}, - } - - mask, _ := fieldmaskpb.New(&session.Session{}, "oauth_token") - - _, updated, err := backend.Patch( - ctx, []*databroker.Record{mkRecord(u1), mkRecord(u2), mkRecord(u3)}, mask) - require.NoError(t, err) - - // The OAuthToken message should be updated but the IDToken message should - // be unchanged, as it was not included in the field mask. The results - // should indicate that only two records were updated (one did not exist). - assert.Equal(t, 2, len(updated)) - assert.Greater(t, updated[0].Version, initial[0].Version) - assert.Greater(t, updated[1].Version, initial[2].Version) - testutil.AssertProtoJSONEqual(t, `{ - "@type": "type.googleapis.com/session.Session", - "id": "session-1", - "idToken": { - "issuer": "issuer-1" - }, - "oauthToken": { - "accessToken": "access-token-1-new" + t.Run("basic", func(t *testing.T) { + // Populate an initial set of session records. + s1 := &session.Session{ + Id: "session-1", + IdToken: &session.IDToken{Issuer: "issuer-1"}, + OauthToken: &session.OAuthToken{AccessToken: "access-token-1"}, } - }`, updated[0].Data) - testutil.AssertProtoJSONEqual(t, `{ - "@type": "type.googleapis.com/session.Session", - "id": "session-3", - "idToken": { - "issuer": "issuer-3" - }, - "oauthToken": { - "accessToken": "access-token-3-new" + s2 := &session.Session{ + Id: "session-2", + IdToken: &session.IDToken{Issuer: "issuer-2"}, + OauthToken: &session.OAuthToken{AccessToken: "access-token-2"}, } - }`, updated[1].Data) + s3 := &session.Session{ + Id: "session-3", + IdToken: &session.IDToken{Issuer: "issuer-3"}, + OauthToken: &session.OAuthToken{AccessToken: "access-token-3"}, + } + initial := []*databroker.Record{mkRecord(s1), mkRecord(s2), mkRecord(s3)} - // Verify that the updates will indeed be seen by a subsequent Get(). - // Note: first truncate the modified_at timestamps to 1 µs precision, as - // that is the maximum precision supported by Postgres. - r1, _ := backend.Get(ctx, "type.googleapis.com/session.Session", "session-1") - truncateTimestamps(updated[0].ModifiedAt, r1.ModifiedAt) - testutil.AssertProtoEqual(t, updated[0], r1) - r3, _ := backend.Get(ctx, "type.googleapis.com/session.Session", "session-3") - truncateTimestamps(updated[1].ModifiedAt, r3.ModifiedAt) - testutil.AssertProtoEqual(t, updated[1], r3) + _, err := backend.Put(ctx, initial) + require.NoError(t, err) + + // Now patch just the oauth_token field. + u1 := &session.Session{ + Id: "session-1", + OauthToken: &session.OAuthToken{AccessToken: "access-token-1-new"}, + } + u2 := &session.Session{ + Id: "session-4-does-not-exist", + OauthToken: &session.OAuthToken{AccessToken: "access-token-4-new"}, + } + u3 := &session.Session{ + Id: "session-3", + OauthToken: &session.OAuthToken{AccessToken: "access-token-3-new"}, + } + + mask, _ := fieldmaskpb.New(&session.Session{}, "oauth_token") + + _, updated, err := backend.Patch( + ctx, []*databroker.Record{mkRecord(u1), mkRecord(u2), mkRecord(u3)}, mask) + require.NoError(t, err) + + // The OAuthToken message should be updated but the IDToken message should + // be unchanged, as it was not included in the field mask. The results + // should indicate that only two records were updated (one did not exist). + assert.Equal(t, 2, len(updated)) + assert.Greater(t, updated[0].Version, initial[0].Version) + assert.Greater(t, updated[1].Version, initial[2].Version) + testutil.AssertProtoJSONEqual(t, `{ + "@type": "type.googleapis.com/session.Session", + "id": "session-1", + "idToken": { + "issuer": "issuer-1" + }, + "oauthToken": { + "accessToken": "access-token-1-new" + } + }`, updated[0].Data) + testutil.AssertProtoJSONEqual(t, `{ + "@type": "type.googleapis.com/session.Session", + "id": "session-3", + "idToken": { + "issuer": "issuer-3" + }, + "oauthToken": { + "accessToken": "access-token-3-new" + } + }`, updated[1].Data) + + // Verify that the updates will indeed be seen by a subsequent Get(). + // Note: first truncate the modified_at timestamps to 1 µs precision, as + // that is the maximum precision supported by Postgres. + r1, _ := backend.Get(ctx, "type.googleapis.com/session.Session", "session-1") + truncateTimestamps(updated[0].ModifiedAt, r1.ModifiedAt) + testutil.AssertProtoEqual(t, updated[0], r1) + r3, _ := backend.Get(ctx, "type.googleapis.com/session.Session", "session-3") + truncateTimestamps(updated[1].ModifiedAt, r3.ModifiedAt) + testutil.AssertProtoEqual(t, updated[1], r3) + }) + + t.Run("concurrent", func(t *testing.T) { + if n := gomaxprocs(); n < 2 { + t.Skipf("skipping concurrent test (GOMAXPROCS = %d)", n) + } + + rs1 := make([]*databroker.Record, 1) + rs2 := make([]*databroker.Record, 1) + + s1 := session.Session{Id: "concurrent", OauthToken: &session.OAuthToken{}} + s2 := session.Session{Id: "concurrent", OauthToken: &session.OAuthToken{}} + + // Store an initial version of a session record. + rs1[0] = mkRecord(&s1) + _, err := backend.Put(ctx, rs1) + require.NoError(t, err) + + fmAccessToken, err := fieldmaskpb.New(&session.Session{}, "oauth_token.access_token") + require.NoError(t, err) + fmRefreshToken, err := fieldmaskpb.New(&session.Session{}, "oauth_token.refresh_token") + require.NoError(t, err) + + var wg sync.WaitGroup + + // Repeatedly make Patch calls to update the session from two separate + // goroutines (one updating just the access token, the other updating + // just the refresh token.) Verify that no updates are lost. + for i := 0; i < 100; i++ { + access := fmt.Sprintf("access-%d", i) + s1.OauthToken.AccessToken = access + rs1[0] = mkRecord(&s1) + + refresh := fmt.Sprintf("refresh-%d", i) + s2.OauthToken.RefreshToken = refresh + rs2[0] = mkRecord(&s2) + + wg.Add(2) + go func() { + _, _, _ = backend.Patch(ctx, rs1, fmAccessToken) + wg.Done() + }() + go func() { + _, _, _ = backend.Patch(ctx, rs2, fmRefreshToken) + wg.Done() + }() + wg.Wait() + + r, err := backend.Get(ctx, "type.googleapis.com/session.Session", "concurrent") + require.NoError(t, err) + data, err := r.Data.UnmarshalNew() + require.NoError(t, err) + s := data.(*session.Session) + require.Equal(t, access, s.OauthToken.AccessToken) + require.Equal(t, refresh, s.OauthToken.RefreshToken) + } + }) } // truncateTimestamps truncates Timestamp messages to 1 µs precision. @@ -121,3 +177,11 @@ func truncateTimestamps(ts ...*timestamppb.Timestamp) { t.Nanos = (t.Nanos / 1000) * 1000 } } + +func gomaxprocs() int { + env := os.Getenv("GOMAXPROCS") + if n, err := strconv.Atoi(env); err == nil { + return n + } + return runtime.NumCPU() +}