diff --git a/build/build-sdk-images/node/gen.sh b/build/build-sdk-images/node/gen.sh index 6a75c256f1..fa64f44f10 100644 --- a/build/build-sdk-images/node/gen.sh +++ b/build/build-sdk-images/node/gen.sh @@ -46,4 +46,4 @@ header ./sdks/nodejs/lib/alpha/alpha_grpc_pb.js header ./sdks/nodejs/lib/alpha/google/api/annotations_pb.js header ./sdks/nodejs/lib/alpha/google/api/http_pb.js header ./sdks/nodejs/lib/alpha/protoc-gen-openapiv2/options/annotations_pb.js -header ./sdks/nodejs/lib/alpha/protoc-gen-openapiv2/options/openapiv2_pb.js \ No newline at end of file +header ./sdks/nodejs/lib/alpha/protoc-gen-openapiv2/options/openapiv2_pb.js diff --git a/go.mod b/go.mod index 1333dc73c2..cf2e139ea6 100644 --- a/go.mod +++ b/go.mod @@ -11,12 +11,14 @@ require ( github.com/ahmetb/gen-crd-api-reference-docs v0.3.0 github.com/fsnotify/fsnotify v1.4.9 github.com/go-openapi/spec v0.19.5 + github.com/google/go-cmp v0.5.9 github.com/gorilla/websocket v1.4.2 github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 github.com/hashicorp/golang-lru v0.5.1 github.com/heptiolabs/healthcheck v0.0.0-20171201210846-da5fdee475fb github.com/joonix/log v0.0.0-20180502111528-d2d3f2f4a806 github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a + github.com/mennanov/fmutils v0.2.0 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.14.0 @@ -66,7 +68,6 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.1.0 // indirect github.com/google/uuid v1.3.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect diff --git a/go.sum b/go.sum index fd9db3ad98..e3dc690b71 100644 --- a/go.sum +++ b/go.sum @@ -313,6 +313,8 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mennanov/fmutils v0.2.0 h1:Hw/iuQPdKtiB2B9YYh+NX8iv7U7eQu1rICPjr8NvxSo= +github.com/mennanov/fmutils v0.2.0/go.mod h1:DE+qeI9Xy5s1GA4trgq8H26jr5DgJ4a9+0D1DPVCqyk= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= diff --git a/pkg/sdk/alpha/alpha.pb.go b/pkg/sdk/alpha/alpha.pb.go index d8aa41e1f1..dfb7b1d296 100644 --- a/pkg/sdk/alpha/alpha.pb.go +++ b/pkg/sdk/alpha/alpha.pb.go @@ -43,6 +43,8 @@ import ( _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + _ "google.golang.org/protobuf/types/known/emptypb" + fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb" ) const ( @@ -283,6 +285,180 @@ func (x *PlayerIDList) GetList() []string { return nil } +// A representation of a Counter. +type Counter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The name of the Counter + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // The current count of the Counter + Count *int64 `protobuf:"varint,2,opt,name=count,proto3,oneof" json:"count,omitempty"` + // The maximum capacity of the Counter + Capacity *int64 `protobuf:"varint,3,opt,name=capacity,proto3,oneof" json:"capacity,omitempty"` +} + +func (x *Counter) Reset() { + *x = Counter{} + if protoimpl.UnsafeEnabled { + mi := &file_alpha_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Counter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Counter) ProtoMessage() {} + +func (x *Counter) ProtoReflect() protoreflect.Message { + mi := &file_alpha_proto_msgTypes[5] + 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 Counter.ProtoReflect.Descriptor instead. +func (*Counter) Descriptor() ([]byte, []int) { + return file_alpha_proto_rawDescGZIP(), []int{5} +} + +func (x *Counter) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Counter) GetCount() int64 { + if x != nil && x.Count != nil { + return *x.Count + } + return 0 +} + +func (x *Counter) GetCapacity() int64 { + if x != nil && x.Capacity != nil { + return *x.Capacity + } + return 0 +} + +type GetCounterRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The name of the Counter to get + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *GetCounterRequest) Reset() { + *x = GetCounterRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_alpha_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetCounterRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetCounterRequest) ProtoMessage() {} + +func (x *GetCounterRequest) ProtoReflect() protoreflect.Message { + mi := &file_alpha_proto_msgTypes[6] + 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 GetCounterRequest.ProtoReflect.Descriptor instead. +func (*GetCounterRequest) Descriptor() ([]byte, []int) { + return file_alpha_proto_rawDescGZIP(), []int{6} +} + +func (x *GetCounterRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type UpdateCounterRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The Counter to update + Counter *Counter `protobuf:"bytes,1,opt,name=counter,proto3" json:"counter,omitempty"` + // Required. Mask (list) of fields to update. + // Fields are specified relative to the Counter + // (e.g. `count`, `capacity`; *not* `Counter.count` or `Counter.capacity`). + UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"` +} + +func (x *UpdateCounterRequest) Reset() { + *x = UpdateCounterRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_alpha_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateCounterRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateCounterRequest) ProtoMessage() {} + +func (x *UpdateCounterRequest) ProtoReflect() protoreflect.Message { + mi := &file_alpha_proto_msgTypes[7] + 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 UpdateCounterRequest.ProtoReflect.Descriptor instead. +func (*UpdateCounterRequest) Descriptor() ([]byte, []int) { + return file_alpha_proto_rawDescGZIP(), []int{7} +} + +func (x *UpdateCounterRequest) GetCounter() *Counter { + if x != nil { + return x.Counter + } + return nil +} + +func (x *UpdateCounterRequest) GetUpdateMask() *fieldmaskpb.FieldMask { + if x != nil { + return x.UpdateMask + } + return nil +} + var File_alpha_proto protoreflect.FileDescriptor var file_alpha_proto_rawDesc = []byte{ @@ -290,76 +466,129 @@ var file_alpha_proto_rawDesc = []byte{ 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, - 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, - 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x1d, 0x0a, 0x05, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x29, 0x0a, 0x04, 0x42, 0x6f, 0x6f, - 0x6c, 0x12, 0x21, 0x0a, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x42, - 0x0d, 0x92, 0x41, 0x0a, 0xa2, 0x02, 0x07, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x52, 0x04, - 0x62, 0x6f, 0x6f, 0x6c, 0x22, 0x26, 0x0a, 0x08, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x44, - 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x44, 0x22, 0x22, 0x0a, 0x0c, - 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, - 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, - 0x32, 0xa9, 0x06, 0x0a, 0x03, 0x53, 0x44, 0x4b, 0x12, 0x6d, 0x0a, 0x0d, 0x50, 0x6c, 0x61, 0x79, - 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, 0x1e, 0x2e, 0x61, 0x67, 0x6f, 0x6e, - 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x44, 0x1a, 0x1a, 0x2e, 0x61, 0x67, 0x6f, 0x6e, + 0x6f, 0x1a, 0x17, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x62, 0x65, 0x68, + 0x61, 0x76, 0x69, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 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, 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, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, + 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x1d, + 0x0a, 0x05, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x29, 0x0a, + 0x04, 0x42, 0x6f, 0x6f, 0x6c, 0x12, 0x21, 0x0a, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x42, 0x0d, 0x92, 0x41, 0x0a, 0xa2, 0x02, 0x07, 0x62, 0x6f, 0x6f, 0x6c, 0x65, + 0x61, 0x6e, 0x52, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x22, 0x26, 0x0a, 0x08, 0x50, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x44, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x44, + 0x22, 0x22, 0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x44, 0x4c, 0x69, 0x73, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, + 0x6c, 0x69, 0x73, 0x74, 0x22, 0x9d, 0x01, 0x0a, 0x07, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x12, + 0x1f, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x03, 0x48, 0x01, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, + 0x3a, 0x2b, 0xea, 0x41, 0x28, 0x0a, 0x12, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, + 0x76, 0x2f, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x12, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x7d, 0x42, 0x08, 0x0a, + 0x06, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x63, 0x61, 0x70, 0x61, + 0x63, 0x69, 0x74, 0x79, 0x22, 0x43, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1a, 0xe0, 0x41, 0x02, 0xfa, 0x41, 0x14, 0x0a, + 0x12, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x65, 0x72, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xad, 0x01, 0x0a, 0x14, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x53, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, + 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x65, 0x72, 0x42, 0x1a, 0xe0, 0x41, 0x02, 0xfa, 0x41, 0x14, 0x0a, 0x12, 0x61, 0x67, 0x6f, 0x6e, + 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x07, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x40, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 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, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x0a, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x32, 0xd5, 0x08, 0x0a, 0x03, 0x53, 0x44, + 0x4b, 0x12, 0x6d, 0x0a, 0x0d, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x12, 0x1e, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, + 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x49, 0x44, 0x1a, 0x1a, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, + 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x22, 0x20, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2f, 0x70, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x3a, 0x01, 0x2a, + 0x12, 0x73, 0x0a, 0x10, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x12, 0x1e, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, + 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x50, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x49, 0x44, 0x1a, 0x1a, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, + 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, + 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, 0x2f, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x70, 0x0a, 0x11, 0x53, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x1b, 0x2e, 0x61, 0x67, 0x6f, + 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x1b, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, + 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x1a, 0x16, 0x2f, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x63, 0x61, 0x70, 0x61, + 0x63, 0x69, 0x74, 0x79, 0x3a, 0x01, 0x2a, 0x12, 0x6d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x1b, 0x2e, 0x61, + 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1b, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x50, 0x6c, 0x61, 0x79, 0x65, - 0x72, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, 0x1e, 0x2e, 0x61, 0x67, - 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x44, 0x1a, 0x1a, 0x2e, 0x61, 0x67, - 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, - 0x18, 0x2f, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x64, - 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x70, 0x0a, 0x11, - 0x53, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, - 0x79, 0x12, 0x1b, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, - 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x1b, - 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x21, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1b, 0x1a, 0x16, 0x2f, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2f, 0x70, 0x6c, 0x61, 0x79, - 0x65, 0x72, 0x2f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x3a, 0x01, 0x2a, 0x12, 0x6d, - 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x63, - 0x69, 0x74, 0x79, 0x12, 0x1b, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, + 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, + 0x2f, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x63, 0x61, + 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x67, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, + 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1b, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, + 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, + 0x7b, 0x0a, 0x11, 0x49, 0x73, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x65, 0x64, 0x12, 0x1e, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, + 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x50, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x49, 0x44, 0x1a, 0x1a, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, + 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, + 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, 0x22, 0x2f, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, + 0x64, 0x2f, 0x7b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x44, 0x7d, 0x12, 0x77, 0x0a, 0x13, + 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x73, 0x12, 0x1b, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x1a, 0x1b, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, - 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x1e, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2f, 0x70, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x2f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x67, 0x0a, - 0x0e, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, - 0x1b, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, - 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1b, 0x2e, 0x61, - 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x15, 0x12, 0x13, 0x2f, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, - 0x2f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x7b, 0x0a, 0x11, 0x49, 0x73, 0x50, 0x6c, 0x61, 0x79, - 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x1e, 0x2e, 0x61, 0x67, - 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x44, 0x1a, 0x1a, 0x2e, 0x61, 0x67, - 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, - 0x22, 0x2f, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x63, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x2f, 0x7b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, - 0x49, 0x44, 0x7d, 0x12, 0x77, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x65, 0x64, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x2e, 0x61, 0x67, 0x6f, + 0x1a, 0x22, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, + 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x44, + 0x4c, 0x69, 0x73, 0x74, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x80, 0x01, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x65, 0x72, 0x12, 0x27, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, + 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x47, 0x65, 0x74, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, + 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x2a, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, + 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x2f, 0x2a, + 0x7d, 0xda, 0x41, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0xa6, 0x01, 0x0a, 0x0d, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x2a, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x22, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, - 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x50, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x1f, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2f, 0x70, 0x6c, 0x61, 0x79, - 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x53, 0x5a, 0x07, - 0x2e, 0x2f, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x92, 0x41, 0x47, 0x12, 0x1e, 0x0a, 0x0b, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x0f, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x65, 0x74, 0x2a, 0x01, 0x01, 0x32, 0x10, 0x61, - 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, - 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, - 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, + 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x4a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2e, 0x32, 0x23, 0x2f, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x7b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, + 0x72, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x2f, + 0x2a, 0x7d, 0x3a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0xda, 0x41, 0x13, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x65, 0x72, 0x2c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, + 0x6b, 0x42, 0x53, 0x5a, 0x07, 0x2e, 0x2f, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x92, 0x41, 0x47, 0x12, + 0x1e, 0x0a, 0x0b, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x0f, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x65, 0x74, 0x2a, + 0x01, 0x01, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, + 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -374,34 +603,44 @@ func file_alpha_proto_rawDescGZIP() []byte { return file_alpha_proto_rawDescData } -var file_alpha_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_alpha_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_alpha_proto_goTypes = []interface{}{ - (*Empty)(nil), // 0: agones.dev.sdk.alpha.Empty - (*Count)(nil), // 1: agones.dev.sdk.alpha.Count - (*Bool)(nil), // 2: agones.dev.sdk.alpha.Bool - (*PlayerID)(nil), // 3: agones.dev.sdk.alpha.PlayerID - (*PlayerIDList)(nil), // 4: agones.dev.sdk.alpha.PlayerIDList + (*Empty)(nil), // 0: agones.dev.sdk.alpha.Empty + (*Count)(nil), // 1: agones.dev.sdk.alpha.Count + (*Bool)(nil), // 2: agones.dev.sdk.alpha.Bool + (*PlayerID)(nil), // 3: agones.dev.sdk.alpha.PlayerID + (*PlayerIDList)(nil), // 4: agones.dev.sdk.alpha.PlayerIDList + (*Counter)(nil), // 5: agones.dev.sdk.alpha.Counter + (*GetCounterRequest)(nil), // 6: agones.dev.sdk.alpha.GetCounterRequest + (*UpdateCounterRequest)(nil), // 7: agones.dev.sdk.alpha.UpdateCounterRequest + (*fieldmaskpb.FieldMask)(nil), // 8: google.protobuf.FieldMask } var file_alpha_proto_depIdxs = []int32{ - 3, // 0: agones.dev.sdk.alpha.SDK.PlayerConnect:input_type -> agones.dev.sdk.alpha.PlayerID - 3, // 1: agones.dev.sdk.alpha.SDK.PlayerDisconnect:input_type -> agones.dev.sdk.alpha.PlayerID - 1, // 2: agones.dev.sdk.alpha.SDK.SetPlayerCapacity:input_type -> agones.dev.sdk.alpha.Count - 0, // 3: agones.dev.sdk.alpha.SDK.GetPlayerCapacity:input_type -> agones.dev.sdk.alpha.Empty - 0, // 4: agones.dev.sdk.alpha.SDK.GetPlayerCount:input_type -> agones.dev.sdk.alpha.Empty - 3, // 5: agones.dev.sdk.alpha.SDK.IsPlayerConnected:input_type -> agones.dev.sdk.alpha.PlayerID - 0, // 6: agones.dev.sdk.alpha.SDK.GetConnectedPlayers:input_type -> agones.dev.sdk.alpha.Empty - 2, // 7: agones.dev.sdk.alpha.SDK.PlayerConnect:output_type -> agones.dev.sdk.alpha.Bool - 2, // 8: agones.dev.sdk.alpha.SDK.PlayerDisconnect:output_type -> agones.dev.sdk.alpha.Bool - 0, // 9: agones.dev.sdk.alpha.SDK.SetPlayerCapacity:output_type -> agones.dev.sdk.alpha.Empty - 1, // 10: agones.dev.sdk.alpha.SDK.GetPlayerCapacity:output_type -> agones.dev.sdk.alpha.Count - 1, // 11: agones.dev.sdk.alpha.SDK.GetPlayerCount:output_type -> agones.dev.sdk.alpha.Count - 2, // 12: agones.dev.sdk.alpha.SDK.IsPlayerConnected:output_type -> agones.dev.sdk.alpha.Bool - 4, // 13: agones.dev.sdk.alpha.SDK.GetConnectedPlayers:output_type -> agones.dev.sdk.alpha.PlayerIDList - 7, // [7:14] is the sub-list for method output_type - 0, // [0:7] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + 5, // 0: agones.dev.sdk.alpha.UpdateCounterRequest.counter:type_name -> agones.dev.sdk.alpha.Counter + 8, // 1: agones.dev.sdk.alpha.UpdateCounterRequest.update_mask:type_name -> google.protobuf.FieldMask + 3, // 2: agones.dev.sdk.alpha.SDK.PlayerConnect:input_type -> agones.dev.sdk.alpha.PlayerID + 3, // 3: agones.dev.sdk.alpha.SDK.PlayerDisconnect:input_type -> agones.dev.sdk.alpha.PlayerID + 1, // 4: agones.dev.sdk.alpha.SDK.SetPlayerCapacity:input_type -> agones.dev.sdk.alpha.Count + 0, // 5: agones.dev.sdk.alpha.SDK.GetPlayerCapacity:input_type -> agones.dev.sdk.alpha.Empty + 0, // 6: agones.dev.sdk.alpha.SDK.GetPlayerCount:input_type -> agones.dev.sdk.alpha.Empty + 3, // 7: agones.dev.sdk.alpha.SDK.IsPlayerConnected:input_type -> agones.dev.sdk.alpha.PlayerID + 0, // 8: agones.dev.sdk.alpha.SDK.GetConnectedPlayers:input_type -> agones.dev.sdk.alpha.Empty + 6, // 9: agones.dev.sdk.alpha.SDK.GetCounter:input_type -> agones.dev.sdk.alpha.GetCounterRequest + 7, // 10: agones.dev.sdk.alpha.SDK.UpdateCounter:input_type -> agones.dev.sdk.alpha.UpdateCounterRequest + 2, // 11: agones.dev.sdk.alpha.SDK.PlayerConnect:output_type -> agones.dev.sdk.alpha.Bool + 2, // 12: agones.dev.sdk.alpha.SDK.PlayerDisconnect:output_type -> agones.dev.sdk.alpha.Bool + 0, // 13: agones.dev.sdk.alpha.SDK.SetPlayerCapacity:output_type -> agones.dev.sdk.alpha.Empty + 1, // 14: agones.dev.sdk.alpha.SDK.GetPlayerCapacity:output_type -> agones.dev.sdk.alpha.Count + 1, // 15: agones.dev.sdk.alpha.SDK.GetPlayerCount:output_type -> agones.dev.sdk.alpha.Count + 2, // 16: agones.dev.sdk.alpha.SDK.IsPlayerConnected:output_type -> agones.dev.sdk.alpha.Bool + 4, // 17: agones.dev.sdk.alpha.SDK.GetConnectedPlayers:output_type -> agones.dev.sdk.alpha.PlayerIDList + 5, // 18: agones.dev.sdk.alpha.SDK.GetCounter:output_type -> agones.dev.sdk.alpha.Counter + 5, // 19: agones.dev.sdk.alpha.SDK.UpdateCounter:output_type -> agones.dev.sdk.alpha.Counter + 11, // [11:20] is the sub-list for method output_type + 2, // [2:11] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_alpha_proto_init() } @@ -470,14 +709,51 @@ func file_alpha_proto_init() { return nil } } + file_alpha_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Counter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_alpha_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetCounterRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_alpha_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateCounterRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } + file_alpha_proto_msgTypes[5].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_alpha_proto_rawDesc, NumEnums: 0, - NumMessages: 5, + NumMessages: 8, NumExtensions: 0, NumServices: 1, }, diff --git a/pkg/sdk/alpha/alpha.pb.gw.go b/pkg/sdk/alpha/alpha.pb.gw.go index c606d2a838..e5dbfbd01d 100644 --- a/pkg/sdk/alpha/alpha.pb.gw.go +++ b/pkg/sdk/alpha/alpha.pb.gw.go @@ -254,6 +254,158 @@ func local_request_SDK_GetConnectedPlayers_0(ctx context.Context, marshaler runt } +func request_SDK_GetCounter_0(ctx context.Context, marshaler runtime.Marshaler, client SDKClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetCounterRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + + msg, err := client.GetCounter(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_SDK_GetCounter_0(ctx context.Context, marshaler runtime.Marshaler, server SDKServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetCounterRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + + msg, err := server.GetCounter(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_SDK_UpdateCounter_0 = &utilities.DoubleArray{Encoding: map[string]int{"counter": 0, "name": 1}, Base: []int{1, 4, 5, 2, 0, 0, 0, 0}, Check: []int{0, 1, 1, 2, 4, 2, 2, 3}} +) + +func request_SDK_UpdateCounter_0(ctx context.Context, marshaler runtime.Marshaler, client SDKClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UpdateCounterRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Counter); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if protoReq.UpdateMask == nil || len(protoReq.UpdateMask.GetPaths()) == 0 { + if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), protoReq.Counter); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } else { + protoReq.UpdateMask = fieldMask + } + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["counter.name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "counter.name") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "counter.name", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "counter.name", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_SDK_UpdateCounter_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.UpdateCounter(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_SDK_UpdateCounter_0(ctx context.Context, marshaler runtime.Marshaler, server SDKServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UpdateCounterRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Counter); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if protoReq.UpdateMask == nil || len(protoReq.UpdateMask.GetPaths()) == 0 { + if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), protoReq.Counter); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } else { + protoReq.UpdateMask = fieldMask + } + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["counter.name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "counter.name") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "counter.name", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "counter.name", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_SDK_UpdateCounter_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.UpdateCounter(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterSDKHandlerServer registers the http handlers for service SDK to "mux". // UnaryRPC :call SDKServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -435,13 +587,63 @@ func RegisterSDKHandlerServer(ctx context.Context, mux *runtime.ServeMux, server }) + mux.Handle("GET", pattern_SDK_GetCounter_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/agones.dev.sdk.alpha.SDK/GetCounter", runtime.WithHTTPPathPattern("/v1alpha1/{name=counters/*}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_SDK_GetCounter_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_SDK_GetCounter_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("PATCH", pattern_SDK_UpdateCounter_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/agones.dev.sdk.alpha.SDK/UpdateCounter", runtime.WithHTTPPathPattern("/v1alpha1/{counter.name=counters/*}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_SDK_UpdateCounter_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_SDK_UpdateCounter_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } // RegisterSDKHandlerFromEndpoint is same as RegisterSDKHandler but // automatically dials to "endpoint" and closes the connection when "ctx" gets done. func RegisterSDKHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.Dial(endpoint, opts...) + conn, err := grpc.DialContext(ctx, endpoint, opts...) if err != nil { return err } @@ -630,6 +832,50 @@ func RegisterSDKHandlerClient(ctx context.Context, mux *runtime.ServeMux, client }) + mux.Handle("GET", pattern_SDK_GetCounter_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/agones.dev.sdk.alpha.SDK/GetCounter", runtime.WithHTTPPathPattern("/v1alpha1/{name=counters/*}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_SDK_GetCounter_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_SDK_GetCounter_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("PATCH", pattern_SDK_UpdateCounter_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/agones.dev.sdk.alpha.SDK/UpdateCounter", runtime.WithHTTPPathPattern("/v1alpha1/{counter.name=counters/*}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_SDK_UpdateCounter_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_SDK_UpdateCounter_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -647,6 +893,10 @@ var ( pattern_SDK_IsPlayerConnected_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"alpha", "player", "connected", "playerID"}, "")) pattern_SDK_GetConnectedPlayers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"alpha", "player", "connected"}, "")) + + pattern_SDK_GetCounter_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 2, 5, 2}, []string{"v1alpha1", "counters", "name"}, "")) + + pattern_SDK_UpdateCounter_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 2, 5, 2}, []string{"v1alpha1", "counters", "counter.name"}, "")) ) var ( @@ -663,4 +913,8 @@ var ( forward_SDK_IsPlayerConnected_0 = runtime.ForwardResponseMessage forward_SDK_GetConnectedPlayers_0 = runtime.ForwardResponseMessage + + forward_SDK_GetCounter_0 = runtime.ForwardResponseMessage + + forward_SDK_UpdateCounter_0 = runtime.ForwardResponseMessage ) diff --git a/pkg/sdk/alpha/alpha_grpc.pb.go b/pkg/sdk/alpha/alpha_grpc.pb.go index 67ecc53bc4..dba5967080 100644 --- a/pkg/sdk/alpha/alpha_grpc.pb.go +++ b/pkg/sdk/alpha/alpha_grpc.pb.go @@ -91,6 +91,11 @@ type SDKClient interface { // // If GameServer.Status.Players.IDs is set manually through the Kubernetes API, use SDK.GameServer() or SDK.WatchGameServer() instead to view this value. GetConnectedPlayers(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*PlayerIDList, error) + // Gets a Counter. Returns NOT_FOUND if the Counter does not exist. + GetCounter(ctx context.Context, in *GetCounterRequest, opts ...grpc.CallOption) (*Counter, error) + // Updates a Counter. Returns NOT_FOUND if the Counter does not exist (name cannot be updated). + // Returns INVALID_ARGUMENT if the field mask path(s) are not field(s) of the Counter. + UpdateCounter(ctx context.Context, in *UpdateCounterRequest, opts ...grpc.CallOption) (*Counter, error) } type sDKClient struct { @@ -164,6 +169,24 @@ func (c *sDKClient) GetConnectedPlayers(ctx context.Context, in *Empty, opts ... return out, nil } +func (c *sDKClient) GetCounter(ctx context.Context, in *GetCounterRequest, opts ...grpc.CallOption) (*Counter, error) { + out := new(Counter) + err := c.cc.Invoke(ctx, "/agones.dev.sdk.alpha.SDK/GetCounter", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *sDKClient) UpdateCounter(ctx context.Context, in *UpdateCounterRequest, opts ...grpc.CallOption) (*Counter, error) { + out := new(Counter) + err := c.cc.Invoke(ctx, "/agones.dev.sdk.alpha.SDK/UpdateCounter", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // SDKServer is the server API for SDK service. // All implementations should embed UnimplementedSDKServer // for forward compatibility @@ -221,6 +244,11 @@ type SDKServer interface { // // If GameServer.Status.Players.IDs is set manually through the Kubernetes API, use SDK.GameServer() or SDK.WatchGameServer() instead to view this value. GetConnectedPlayers(context.Context, *Empty) (*PlayerIDList, error) + // Gets a Counter. Returns NOT_FOUND if the Counter does not exist. + GetCounter(context.Context, *GetCounterRequest) (*Counter, error) + // Updates a Counter. Returns NOT_FOUND if the Counter does not exist (name cannot be updated). + // Returns INVALID_ARGUMENT if the field mask path(s) are not field(s) of the Counter. + UpdateCounter(context.Context, *UpdateCounterRequest) (*Counter, error) } // UnimplementedSDKServer should be embedded to have forward compatible implementations. @@ -248,6 +276,12 @@ func (UnimplementedSDKServer) IsPlayerConnected(context.Context, *PlayerID) (*Bo func (UnimplementedSDKServer) GetConnectedPlayers(context.Context, *Empty) (*PlayerIDList, error) { return nil, status.Errorf(codes.Unimplemented, "method GetConnectedPlayers not implemented") } +func (UnimplementedSDKServer) GetCounter(context.Context, *GetCounterRequest) (*Counter, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetCounter not implemented") +} +func (UnimplementedSDKServer) UpdateCounter(context.Context, *UpdateCounterRequest) (*Counter, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateCounter not implemented") +} // UnsafeSDKServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to SDKServer will @@ -386,6 +420,42 @@ func _SDK_GetConnectedPlayers_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } +func _SDK_GetCounter_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetCounterRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SDKServer).GetCounter(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/agones.dev.sdk.alpha.SDK/GetCounter", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SDKServer).GetCounter(ctx, req.(*GetCounterRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SDK_UpdateCounter_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateCounterRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SDKServer).UpdateCounter(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/agones.dev.sdk.alpha.SDK/UpdateCounter", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SDKServer).UpdateCounter(ctx, req.(*UpdateCounterRequest)) + } + return interceptor(ctx, in, info, handler) +} + // SDK_ServiceDesc is the grpc.ServiceDesc for SDK service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -421,6 +491,14 @@ var SDK_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetConnectedPlayers", Handler: _SDK_GetConnectedPlayers_Handler, }, + { + MethodName: "GetCounter", + Handler: _SDK_GetCounter_Handler, + }, + { + MethodName: "UpdateCounter", + Handler: _SDK_UpdateCounter_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "alpha.proto", diff --git a/pkg/sdk/sdk.pb.go b/pkg/sdk/sdk.pb.go index 6e5cdd3387..ed414b2453 100644 --- a/pkg/sdk/sdk.pb.go +++ b/pkg/sdk/sdk.pb.go @@ -433,6 +433,9 @@ type GameServer_Status struct { // [Stage:Alpha] // [FeatureFlag:PlayerTracking] Players *GameServer_Status_PlayerStatus `protobuf:"bytes,4,opt,name=players,proto3" json:"players,omitempty"` + // [Stage:Alpha] + // [FeatureFlag:CountsAndLists] + Counters map[string]*GameServer_Status_CounterStatus `protobuf:"bytes,5,rep,name=counters,proto3" json:"counters,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *GameServer_Status) Reset() { @@ -495,6 +498,13 @@ func (x *GameServer_Status) GetPlayers() *GameServer_Status_PlayerStatus { return nil } +func (x *GameServer_Status) GetCounters() map[string]*GameServer_Status_CounterStatus { + if x != nil { + return x.Counters + } + return nil +} + type GameServer_Spec_Health struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -686,6 +696,63 @@ func (x *GameServer_Status_PlayerStatus) GetIds() []string { return nil } +// [Stage:Alpha] +// [FeatureFlag:CountsAndLists] +type GameServer_Status_CounterStatus struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Count int64 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"` + Capacity int64 `protobuf:"varint,2,opt,name=capacity,proto3" json:"capacity,omitempty"` +} + +func (x *GameServer_Status_CounterStatus) Reset() { + *x = GameServer_Status_CounterStatus{} + if protoimpl.UnsafeEnabled { + mi := &file_sdk_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GameServer_Status_CounterStatus) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GameServer_Status_CounterStatus) ProtoMessage() {} + +func (x *GameServer_Status_CounterStatus) ProtoReflect() protoreflect.Message { + mi := &file_sdk_proto_msgTypes[12] + 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 GameServer_Status_CounterStatus.ProtoReflect.Descriptor instead. +func (*GameServer_Status_CounterStatus) Descriptor() ([]byte, []int) { + return file_sdk_proto_rawDescGZIP(), []int{3, 2, 2} +} + +func (x *GameServer_Status_CounterStatus) GetCount() int64 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *GameServer_Status_CounterStatus) GetCapacity() int64 { + if x != nil { + return x.Capacity + } + return 0 +} + var File_sdk_proto protoreflect.FileDescriptor var file_sdk_proto_rawDesc = []byte{ @@ -701,7 +768,7 @@ var file_sdk_proto_rawDesc = []byte{ 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x24, 0x0a, 0x08, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x07, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x22, 0xae, 0x0a, 0x0a, + 0x01, 0x28, 0x03, 0x52, 0x07, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x22, 0xac, 0x0c, 0x0a, 0x0a, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x46, 0x0a, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, @@ -764,7 +831,7 @@ var file_sdk_proto_rawDesc = []byte{ 0x6c, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x13, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x53, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x1a, 0xc4, 0x02, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x1a, 0xc2, 0x04, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, @@ -776,69 +843,85 @@ var file_sdk_proto_rawDesc = []byte{ 0x32, 0x2e, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x1a, 0x2e, 0x0a, 0x04, 0x50, 0x6f, 0x72, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x1a, 0x52, 0x0a, 0x0c, 0x50, 0x6c, 0x61, - 0x79, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, - 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x69, - 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73, 0x32, 0x86, 0x06, - 0x0a, 0x03, 0x53, 0x44, 0x4b, 0x12, 0x48, 0x0a, 0x05, 0x52, 0x65, 0x61, 0x64, 0x79, 0x12, 0x15, - 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, - 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x11, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x0b, 0x22, 0x06, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x3a, 0x01, 0x2a, 0x12, - 0x4e, 0x0a, 0x08, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x61, 0x67, - 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, - 0x73, 0x64, 0x6b, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x14, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x0e, 0x22, 0x09, 0x2f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, - 0x4e, 0x0a, 0x08, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x15, 0x2e, 0x61, 0x67, - 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, - 0x73, 0x64, 0x6b, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x14, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x0e, 0x22, 0x09, 0x2f, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x3a, 0x01, 0x2a, 0x12, - 0x4c, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x15, 0x2e, 0x61, 0x67, 0x6f, 0x6e, + 0x52, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x4b, 0x0a, 0x08, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x61, 0x67, + 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x47, 0x61, 0x6d, + 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x1a, 0x2e, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x1a, 0x52, 0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, + 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73, 0x1a, 0x41, 0x0a, 0x0d, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x1a, 0x6c, 0x0a, + 0x0d, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 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, 0x45, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2f, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, + 0x2e, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0x86, 0x06, 0x0a, 0x03, + 0x53, 0x44, 0x4b, 0x12, 0x48, 0x0a, 0x05, 0x52, 0x65, 0x61, 0x64, 0x79, 0x12, 0x15, 0x2e, 0x61, + 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, + 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x11, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x0b, 0x22, 0x06, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x3a, 0x01, 0x2a, 0x12, 0x4e, 0x0a, + 0x08, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, - 0x6b, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x12, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x22, - 0x07, 0x2f, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x3a, 0x01, 0x2a, 0x28, 0x01, 0x12, 0x57, 0x0a, - 0x0d, 0x47, 0x65, 0x74, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x15, - 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1a, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, - 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x67, 0x61, 0x6d, 0x65, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x61, 0x0a, 0x0f, 0x57, 0x61, 0x74, 0x63, 0x68, 0x47, - 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x15, 0x2e, 0x61, 0x67, 0x6f, 0x6e, + 0x6b, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x14, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0e, 0x22, + 0x09, 0x2f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x4e, 0x0a, + 0x08, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x15, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x1a, 0x1a, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, - 0x6b, 0x2e, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x19, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x77, 0x61, 0x74, 0x63, 0x68, 0x2f, 0x67, 0x61, 0x6d, - 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x08, 0x53, 0x65, 0x74, - 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x18, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, - 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, - 0x15, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x1a, 0x0f, - 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, - 0x01, 0x2a, 0x12, 0x61, 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, + 0x1a, 0x15, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, + 0x6b, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x14, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0e, 0x22, + 0x09, 0x2f, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x3a, 0x01, 0x2a, 0x12, 0x4c, 0x0a, + 0x06, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x15, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, + 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, + 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x12, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x22, 0x07, 0x2f, + 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x3a, 0x01, 0x2a, 0x28, 0x01, 0x12, 0x57, 0x0a, 0x0d, 0x47, + 0x65, 0x74, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x15, 0x2e, 0x61, + 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x1a, 0x1a, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, + 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, + 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x67, 0x61, 0x6d, 0x65, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x12, 0x61, 0x0a, 0x0f, 0x57, 0x61, 0x74, 0x63, 0x68, 0x47, 0x61, 0x6d, + 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x15, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, + 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1a, + 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, + 0x47, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x13, 0x12, 0x11, 0x2f, 0x77, 0x61, 0x74, 0x63, 0x68, 0x2f, 0x67, 0x61, 0x6d, 0x65, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x08, 0x53, 0x65, 0x74, 0x4c, 0x61, + 0x62, 0x65, 0x6c, 0x12, 0x18, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x15, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x1a, 0x14, 0x2f, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x3a, 0x01, 0x2a, 0x12, 0x4f, 0x0a, 0x07, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x12, 0x18, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, - 0x6b, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x15, 0x2e, 0x61, 0x67, 0x6f, - 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x22, 0x08, 0x2f, 0x72, 0x65, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x3a, 0x01, 0x2a, 0x42, 0x4f, 0x5a, 0x05, 0x2e, 0x2f, 0x73, 0x64, 0x6b, 0x92, - 0x41, 0x45, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x64, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, - 0x0f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x65, 0x74, - 0x2a, 0x01, 0x01, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6d, 0x70, 0x74, 0x79, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x1a, 0x0f, 0x2f, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x01, 0x2a, + 0x12, 0x61, 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x18, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, + 0x64, 0x6b, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x15, 0x2e, 0x61, 0x67, + 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x1a, 0x14, 0x2f, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x3a, 0x01, 0x2a, 0x12, 0x4f, 0x0a, 0x07, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x12, 0x18, + 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, + 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x15, 0x2e, 0x61, 0x67, 0x6f, 0x6e, 0x65, + 0x73, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, + 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x22, 0x08, 0x2f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x3a, 0x01, 0x2a, 0x42, 0x4f, 0x5a, 0x05, 0x2e, 0x2f, 0x73, 0x64, 0x6b, 0x92, 0x41, 0x45, + 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x64, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x0f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x65, 0x74, 0x2a, 0x01, + 0x01, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, + 0x73, 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -853,20 +936,22 @@ func file_sdk_proto_rawDescGZIP() []byte { return file_sdk_proto_rawDescData } -var file_sdk_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_sdk_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_sdk_proto_goTypes = []interface{}{ - (*Empty)(nil), // 0: agones.dev.sdk.Empty - (*KeyValue)(nil), // 1: agones.dev.sdk.KeyValue - (*Duration)(nil), // 2: agones.dev.sdk.Duration - (*GameServer)(nil), // 3: agones.dev.sdk.GameServer - (*GameServer_ObjectMeta)(nil), // 4: agones.dev.sdk.GameServer.ObjectMeta - (*GameServer_Spec)(nil), // 5: agones.dev.sdk.GameServer.Spec - (*GameServer_Status)(nil), // 6: agones.dev.sdk.GameServer.Status - nil, // 7: agones.dev.sdk.GameServer.ObjectMeta.AnnotationsEntry - nil, // 8: agones.dev.sdk.GameServer.ObjectMeta.LabelsEntry - (*GameServer_Spec_Health)(nil), // 9: agones.dev.sdk.GameServer.Spec.Health - (*GameServer_Status_Port)(nil), // 10: agones.dev.sdk.GameServer.Status.Port - (*GameServer_Status_PlayerStatus)(nil), // 11: agones.dev.sdk.GameServer.Status.PlayerStatus + (*Empty)(nil), // 0: agones.dev.sdk.Empty + (*KeyValue)(nil), // 1: agones.dev.sdk.KeyValue + (*Duration)(nil), // 2: agones.dev.sdk.Duration + (*GameServer)(nil), // 3: agones.dev.sdk.GameServer + (*GameServer_ObjectMeta)(nil), // 4: agones.dev.sdk.GameServer.ObjectMeta + (*GameServer_Spec)(nil), // 5: agones.dev.sdk.GameServer.Spec + (*GameServer_Status)(nil), // 6: agones.dev.sdk.GameServer.Status + nil, // 7: agones.dev.sdk.GameServer.ObjectMeta.AnnotationsEntry + nil, // 8: agones.dev.sdk.GameServer.ObjectMeta.LabelsEntry + (*GameServer_Spec_Health)(nil), // 9: agones.dev.sdk.GameServer.Spec.Health + (*GameServer_Status_Port)(nil), // 10: agones.dev.sdk.GameServer.Status.Port + (*GameServer_Status_PlayerStatus)(nil), // 11: agones.dev.sdk.GameServer.Status.PlayerStatus + (*GameServer_Status_CounterStatus)(nil), // 12: agones.dev.sdk.GameServer.Status.CounterStatus + nil, // 13: agones.dev.sdk.GameServer.Status.CountersEntry } var file_sdk_proto_depIdxs = []int32{ 4, // 0: agones.dev.sdk.GameServer.object_meta:type_name -> agones.dev.sdk.GameServer.ObjectMeta @@ -877,29 +962,31 @@ var file_sdk_proto_depIdxs = []int32{ 9, // 5: agones.dev.sdk.GameServer.Spec.health:type_name -> agones.dev.sdk.GameServer.Spec.Health 10, // 6: agones.dev.sdk.GameServer.Status.ports:type_name -> agones.dev.sdk.GameServer.Status.Port 11, // 7: agones.dev.sdk.GameServer.Status.players:type_name -> agones.dev.sdk.GameServer.Status.PlayerStatus - 0, // 8: agones.dev.sdk.SDK.Ready:input_type -> agones.dev.sdk.Empty - 0, // 9: agones.dev.sdk.SDK.Allocate:input_type -> agones.dev.sdk.Empty - 0, // 10: agones.dev.sdk.SDK.Shutdown:input_type -> agones.dev.sdk.Empty - 0, // 11: agones.dev.sdk.SDK.Health:input_type -> agones.dev.sdk.Empty - 0, // 12: agones.dev.sdk.SDK.GetGameServer:input_type -> agones.dev.sdk.Empty - 0, // 13: agones.dev.sdk.SDK.WatchGameServer:input_type -> agones.dev.sdk.Empty - 1, // 14: agones.dev.sdk.SDK.SetLabel:input_type -> agones.dev.sdk.KeyValue - 1, // 15: agones.dev.sdk.SDK.SetAnnotation:input_type -> agones.dev.sdk.KeyValue - 2, // 16: agones.dev.sdk.SDK.Reserve:input_type -> agones.dev.sdk.Duration - 0, // 17: agones.dev.sdk.SDK.Ready:output_type -> agones.dev.sdk.Empty - 0, // 18: agones.dev.sdk.SDK.Allocate:output_type -> agones.dev.sdk.Empty - 0, // 19: agones.dev.sdk.SDK.Shutdown:output_type -> agones.dev.sdk.Empty - 0, // 20: agones.dev.sdk.SDK.Health:output_type -> agones.dev.sdk.Empty - 3, // 21: agones.dev.sdk.SDK.GetGameServer:output_type -> agones.dev.sdk.GameServer - 3, // 22: agones.dev.sdk.SDK.WatchGameServer:output_type -> agones.dev.sdk.GameServer - 0, // 23: agones.dev.sdk.SDK.SetLabel:output_type -> agones.dev.sdk.Empty - 0, // 24: agones.dev.sdk.SDK.SetAnnotation:output_type -> agones.dev.sdk.Empty - 0, // 25: agones.dev.sdk.SDK.Reserve:output_type -> agones.dev.sdk.Empty - 17, // [17:26] is the sub-list for method output_type - 8, // [8:17] is the sub-list for method input_type - 8, // [8:8] is the sub-list for extension type_name - 8, // [8:8] is the sub-list for extension extendee - 0, // [0:8] is the sub-list for field type_name + 13, // 8: agones.dev.sdk.GameServer.Status.counters:type_name -> agones.dev.sdk.GameServer.Status.CountersEntry + 12, // 9: agones.dev.sdk.GameServer.Status.CountersEntry.value:type_name -> agones.dev.sdk.GameServer.Status.CounterStatus + 0, // 10: agones.dev.sdk.SDK.Ready:input_type -> agones.dev.sdk.Empty + 0, // 11: agones.dev.sdk.SDK.Allocate:input_type -> agones.dev.sdk.Empty + 0, // 12: agones.dev.sdk.SDK.Shutdown:input_type -> agones.dev.sdk.Empty + 0, // 13: agones.dev.sdk.SDK.Health:input_type -> agones.dev.sdk.Empty + 0, // 14: agones.dev.sdk.SDK.GetGameServer:input_type -> agones.dev.sdk.Empty + 0, // 15: agones.dev.sdk.SDK.WatchGameServer:input_type -> agones.dev.sdk.Empty + 1, // 16: agones.dev.sdk.SDK.SetLabel:input_type -> agones.dev.sdk.KeyValue + 1, // 17: agones.dev.sdk.SDK.SetAnnotation:input_type -> agones.dev.sdk.KeyValue + 2, // 18: agones.dev.sdk.SDK.Reserve:input_type -> agones.dev.sdk.Duration + 0, // 19: agones.dev.sdk.SDK.Ready:output_type -> agones.dev.sdk.Empty + 0, // 20: agones.dev.sdk.SDK.Allocate:output_type -> agones.dev.sdk.Empty + 0, // 21: agones.dev.sdk.SDK.Shutdown:output_type -> agones.dev.sdk.Empty + 0, // 22: agones.dev.sdk.SDK.Health:output_type -> agones.dev.sdk.Empty + 3, // 23: agones.dev.sdk.SDK.GetGameServer:output_type -> agones.dev.sdk.GameServer + 3, // 24: agones.dev.sdk.SDK.WatchGameServer:output_type -> agones.dev.sdk.GameServer + 0, // 25: agones.dev.sdk.SDK.SetLabel:output_type -> agones.dev.sdk.Empty + 0, // 26: agones.dev.sdk.SDK.SetAnnotation:output_type -> agones.dev.sdk.Empty + 0, // 27: agones.dev.sdk.SDK.Reserve:output_type -> agones.dev.sdk.Empty + 19, // [19:28] is the sub-list for method output_type + 10, // [10:19] is the sub-list for method input_type + 10, // [10:10] is the sub-list for extension type_name + 10, // [10:10] is the sub-list for extension extendee + 0, // [0:10] is the sub-list for field type_name } func init() { file_sdk_proto_init() } @@ -1028,6 +1115,18 @@ func file_sdk_proto_init() { return nil } } + file_sdk_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GameServer_Status_CounterStatus); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -1035,7 +1134,7 @@ func file_sdk_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_sdk_proto_rawDesc, NumEnums: 0, - NumMessages: 12, + NumMessages: 14, NumExtensions: 0, NumServices: 1, }, diff --git a/pkg/sdk/sdk.pb.gw.go b/pkg/sdk/sdk.pb.gw.go index 1ff881cdea..11404b7878 100644 --- a/pkg/sdk/sdk.pb.gw.go +++ b/pkg/sdk/sdk.pb.gw.go @@ -530,7 +530,7 @@ func RegisterSDKHandlerServer(ctx context.Context, mux *runtime.ServeMux, server // RegisterSDKHandlerFromEndpoint is same as RegisterSDKHandler but // automatically dials to "endpoint" and closes the connection when "ctx" gets done. func RegisterSDKHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.Dial(endpoint, opts...) + conn, err := grpc.DialContext(ctx, endpoint, opts...) if err != nil { return err } diff --git a/pkg/sdkserver/localsdk.go b/pkg/sdkserver/localsdk.go index 26ae3b6fec..00c334ca2e 100644 --- a/pkg/sdkserver/localsdk.go +++ b/pkg/sdkserver/localsdk.go @@ -31,8 +31,10 @@ import ( "agones.dev/agones/pkg/sdk/beta" "agones.dev/agones/pkg/util/runtime" "github.com/fsnotify/fsnotify" + "github.com/mennanov/fmutils" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "google.golang.org/protobuf/proto" "k8s.io/apimachinery/pkg/util/yaml" ) @@ -585,6 +587,60 @@ func (l *LocalSDKServer) GetPlayerCapacity(_ context.Context, _ *alpha.Empty) (* return result, nil } +// GetCounter returns a Counter. Returns NOT_FOUND if the counter does not exist. +// [Stage:Alpha] +// [FeatureFlag:CountsAndLists] +func (l *LocalSDKServer) GetCounter(ctx context.Context, in *alpha.GetCounterRequest) (*alpha.Counter, error) { + if !runtime.FeatureEnabled(runtime.FeatureCountsAndLists) { + return nil, errors.Errorf("%s not enabled", runtime.FeatureCountsAndLists) + } + + l.logger.WithField("name", in.Name).Info("Getting Counter") + l.recordRequest("getcounter") + l.gsMutex.RLock() + defer l.gsMutex.RUnlock() + + if counter, ok := l.gs.Status.Counters[in.Name]; ok { + return &alpha.Counter{Name: in.Name, Count: &counter.Count, Capacity: &counter.Capacity}, nil + } + return nil, errors.Errorf("NOT_FOUND. %s Counter not found", in.Name) +} + +// UpdateCounter returns the updated Counter. Returns NOT_FOUND if the Counter does not exist. +// Returns INVALID_ARGUMENT if the FieldMask paths are invalid. +// [Stage:Alpha] +// [FeatureFlag:CountsAndLists] +func (l *LocalSDKServer) UpdateCounter(ctx context.Context, in *alpha.UpdateCounterRequest) (*alpha.Counter, error) { + if !runtime.FeatureEnabled(runtime.FeatureCountsAndLists) { + return nil, errors.Errorf("%s not enabled", runtime.FeatureCountsAndLists) + } + + l.logger.WithField("name", in.Counter.Name).Info("Updating Counter") + l.recordRequest("updatecounter") + l.gsMutex.RLock() + defer l.gsMutex.RUnlock() + + name := in.Counter.Name + if counter, ok := l.gs.Status.Counters[name]; ok { + // Check if the UpdateMask paths are valid, return INVALID_ARGUMENT if not. + // TODO: https://google.aip.dev/134, "Update masks must support a special value *, meaning full replacement". + if in.UpdateMask.IsValid(counter.ProtoReflect().Interface()) { + // Create *alpha.Counter from *sdk.GameServer_Status_CounterStatus for merging. + tmpCounter := &alpha.Counter{Name: name, Count: &counter.Count, Capacity: &counter.Capacity} + // Removes any fields from the request that are not included in the FieldMask Paths. + fmutils.Filter(in.Counter, in.UpdateMask.Paths) + // Now that the request is filtered we can merge it with the Counter entity. + proto.Merge(tmpCounter, in.Counter) + // Write newly updated Counter to gameserverstatus. + l.gs.Status.Counters[name].Count = *tmpCounter.Count + l.gs.Status.Counters[name].Capacity = *tmpCounter.Capacity + return &alpha.Counter{Name: name, Count: &l.gs.Status.Counters[name].Count, Capacity: &l.gs.Status.Counters[name].Capacity}, nil + } + return nil, errors.Errorf("INVALID_ARGUMENT. Field Mask Path(s) %v are invalid for Counter. Use valid %v", in.UpdateMask.GetPaths(), counter.ProtoReflect().Descriptor().Fields()) + } + return nil, errors.Errorf("NOT_FOUND. %s Counter not found", name) +} + // Close tears down all the things func (l *LocalSDKServer) Close() { l.updateObservers.Range(func(observer, _ interface{}) bool { diff --git a/pkg/sdkserver/localsdk_test.go b/pkg/sdkserver/localsdk_test.go index 79ac2f021e..47952d5440 100644 --- a/pkg/sdkserver/localsdk_test.go +++ b/pkg/sdkserver/localsdk_test.go @@ -28,7 +28,11 @@ import ( "agones.dev/agones/pkg/sdk" "agones.dev/agones/pkg/sdk/alpha" "agones.dev/agones/pkg/util/runtime" + "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/fieldmaskpb" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" ) @@ -607,6 +611,126 @@ func TestLocalSDKServerPlayerConnectAndDisconnect(t *testing.T) { } } +func TestLocalSDKServerGetCounter(t *testing.T) { + t.Parallel() + + runtime.FeatureTestMutex.Lock() + defer runtime.FeatureTestMutex.Unlock() + assert.NoError(t, runtime.ParseFeatures(string(runtime.FeatureCountsAndLists)+"=true")) + + name := "sessions" + count := int64(1) + capacity := int64(100) + counters := map[string]agonesv1.CounterStatus{ + name: {Count: count, Capacity: capacity}, + } + fixture := &agonesv1.GameServer{ + ObjectMeta: metav1.ObjectMeta{Name: "stuff"}, + Status: agonesv1.GameServerStatus{ + Counters: counters, + }, + } + + path, err := gsToTmpFile(fixture) + assert.NoError(t, err) + l, err := NewLocalSDKServer(path, "") + assert.Nil(t, err) + + stream := newGameServerMockStream() + go func() { + err := l.WatchGameServer(&sdk.Empty{}, stream) + assert.Nil(t, err) + }() + assertInitialWatchUpdate(t, stream) + + // wait for watching to begin + err = wait.Poll(time.Second, 10*time.Second, func() (bool, error) { + found := false + l.updateObservers.Range(func(_, _ interface{}) bool { + found = true + return false + }) + return found, nil + }) + assert.NoError(t, err) + + counterRequest := alpha.GetCounterRequest{Name: name} + expected := &alpha.Counter{Name: name, Count: &count, Capacity: &capacity} + counter, err := l.GetCounter(context.Background(), &counterRequest) + assert.NoError(t, err) + if diff := cmp.Diff(expected, counter, protocmp.Transform()); diff != "" { + t.Errorf("unexpected difference:\n%v", diff) + } +} + +func TestLocalSDKServerUpdateCounter(t *testing.T) { + t.Parallel() + + runtime.FeatureTestMutex.Lock() + defer runtime.FeatureTestMutex.Unlock() + assert.NoError(t, runtime.ParseFeatures(string(runtime.FeatureCountsAndLists)+"=true")) + + counterName := "sessions" + counters := map[string]agonesv1.CounterStatus{ + counterName: {Count: 1, Capacity: 100}, + } + fixture := &agonesv1.GameServer{ + ObjectMeta: metav1.ObjectMeta{Name: "stuff"}, + Status: agonesv1.GameServerStatus{ + Counters: counters, + }, + } + + path, err := gsToTmpFile(fixture) + assert.NoError(t, err) + l, err := NewLocalSDKServer(path, "") + assert.Nil(t, err) + + stream := newGameServerMockStream() + go func() { + err := l.WatchGameServer(&sdk.Empty{}, stream) + assert.Nil(t, err) + }() + assertInitialWatchUpdate(t, stream) + + // wait for watching to begin + err = wait.Poll(time.Second, 10*time.Second, func() (bool, error) { + found := false + l.updateObservers.Range(func(_, _ interface{}) bool { + found = true + return false + }) + return found, nil + }) + assert.NoError(t, err) + + // Check UpdateCounter only updates fields in the FieldMask + got, err := l.UpdateCounter(context.Background(), &alpha.UpdateCounterRequest{ + Counter: &alpha.Counter{ + Name: counterName, + Count: proto.Int64(9), + Capacity: proto.Int64(999), + }, + UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"count"}}, + }) + want := &alpha.Counter{ + Name: counterName, + Count: proto.Int64(9), + Capacity: proto.Int64(100), + } + assert.NoError(t, err) + if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" { + t.Errorf("unexpected difference:\n%v", diff) + } + + // Confirm updated Counter has been properly written to the GameServer + got, err = l.GetCounter(context.Background(), &alpha.GetCounterRequest{Name: counterName}) + assert.NoError(t, err) + if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" { + t.Errorf("unexpected difference:\n%v", diff) + } +} + // TestLocalSDKServerStateUpdates verify that SDK functions changes the state of the // GameServer object func TestLocalSDKServerStateUpdates(t *testing.T) { diff --git a/pkg/sdkserver/sdk.go b/pkg/sdkserver/sdk.go index 17c0afad77..3124bd55a4 100644 --- a/pkg/sdkserver/sdk.go +++ b/pkg/sdkserver/sdk.go @@ -18,6 +18,7 @@ import ( agonesv1 "agones.dev/agones/pkg/apis/agones/v1" "agones.dev/agones/pkg/sdk" "agones.dev/agones/pkg/util/runtime" + "google.golang.org/protobuf/proto" ) const ( @@ -77,5 +78,15 @@ func convert(gs *agonesv1.GameServer) *sdk.GameServer { } } + if runtime.FeatureEnabled(runtime.FeatureCountsAndLists) { + if gs.Status.Counters != nil { + counters := make(map[string]*sdk.GameServer_Status_CounterStatus, len(gs.Status.Counters)) + for key, counter := range gs.Status.Counters { + counters[key] = &sdk.GameServer_Status_CounterStatus{Count: *proto.Int64(counter.Count), Capacity: *proto.Int64(counter.Capacity)} + } + result.Status.Counters = counters + } + } + return result } diff --git a/pkg/sdkserver/sdkserver.go b/pkg/sdkserver/sdkserver.go index 10c788db78..dcfd297a83 100644 --- a/pkg/sdkserver/sdkserver.go +++ b/pkg/sdkserver/sdkserver.go @@ -38,6 +38,8 @@ import ( "agones.dev/agones/pkg/util/workerqueue" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" corev1 "k8s.io/api/core/v1" apiequality "k8s.io/apimachinery/pkg/api/equality" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -719,6 +721,29 @@ func (s *SDKServer) GetPlayerCapacity(ctx context.Context, _ *alpha.Empty) (*alp return &alpha.Count{Count: s.gsPlayerCapacity}, nil } +// GetCounter returns a Counter. Returns NOT_FOUND if the counter does not exist. +// [Stage:Alpha] +// [FeatureFlag:CountsAndLists] +func (s *SDKServer) GetCounter(ctx context.Context, in *alpha.GetCounterRequest) (*alpha.Counter, error) { + if !runtime.FeatureEnabled(runtime.FeatureCountsAndLists) { + return nil, errors.Errorf("%s not enabled", runtime.FeatureCountsAndLists) + } + // TODO(#2716): Implement me + return nil, status.Error(codes.Unimplemented, "GetCounter coming soon") +} + +// UpdateCounter returns the updated Counter. Returns NOT_FOUND if the Counter does not exist. +// Returns INVALID_ARGUMENT if the FieldMask paths are invalid. +// [Stage:Alpha] +// [FeatureFlag:CountsAndLists] +func (s *SDKServer) UpdateCounter(ctx context.Context, in *alpha.UpdateCounterRequest) (*alpha.Counter, error) { + if !runtime.FeatureEnabled(runtime.FeatureCountsAndLists) { + return nil, errors.Errorf("%s not enabled", runtime.FeatureCountsAndLists) + } + // TODO(#2716): Implement Me + return nil, status.Error(codes.Unimplemented, "UpdateCounter coming soon") +} + // sendGameServerUpdate sends a watch game server event func (s *SDKServer) sendGameServerUpdate(gs *agonesv1.GameServer) { s.logger.Debug("Sending GameServer Event to connectedStreams") diff --git a/proto/googleapis/google/api/client.proto b/proto/googleapis/google/api/client.proto new file mode 100644 index 0000000000..227ccf3a53 --- /dev/null +++ b/proto/googleapis/google/api/client.proto @@ -0,0 +1,349 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/launch_stage.proto"; +import "google/protobuf/descriptor.proto"; +import "google/protobuf/duration.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "ClientProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // A definition of a client library method signature. + // + // In client libraries, each proto RPC corresponds to one or more methods + // which the end user is able to call, and calls the underlying RPC. + // Normally, this method receives a single argument (a struct or instance + // corresponding to the RPC request object). Defining this field will + // add one or more overloads providing flattened or simpler method signatures + // in some languages. + // + // The fields on the method signature are provided as a comma-separated + // string. + // + // For example, the proto RPC and annotation: + // + // rpc CreateSubscription(CreateSubscriptionRequest) + // returns (Subscription) { + // option (google.api.method_signature) = "name,topic"; + // } + // + // Would add the following Java overload (in addition to the method accepting + // the request object): + // + // public final Subscription createSubscription(String name, String topic) + // + // The following backwards-compatibility guidelines apply: + // + // * Adding this annotation to an unannotated method is backwards + // compatible. + // * Adding this annotation to a method which already has existing + // method signature annotations is backwards compatible if and only if + // the new method signature annotation is last in the sequence. + // * Modifying or removing an existing method signature annotation is + // a breaking change. + // * Re-ordering existing method signature annotations is a breaking + // change. + repeated string method_signature = 1051; +} + +extend google.protobuf.ServiceOptions { + // The hostname for this service. + // This should be specified with no prefix or protocol. + // + // Example: + // + // service Foo { + // option (google.api.default_host) = "foo.googleapi.com"; + // ... + // } + string default_host = 1049; + + // OAuth scopes needed for the client. + // + // Example: + // + // service Foo { + // option (google.api.oauth_scopes) = \ + // "https://www.googleapis.com/auth/cloud-platform"; + // ... + // } + // + // If there is more than one scope, use a comma-separated string: + // + // Example: + // + // service Foo { + // option (google.api.oauth_scopes) = \ + // "https://www.googleapis.com/auth/cloud-platform," + // "https://www.googleapis.com/auth/monitoring"; + // ... + // } + string oauth_scopes = 1050; +} + +// Required information for every language. +message CommonLanguageSettings { + // Link to automatically generated reference documentation. Example: + // https://cloud.google.com/nodejs/docs/reference/asset/latest + string reference_docs_uri = 1 [deprecated = true]; + + // The destination where API teams want this client library to be published. + repeated ClientLibraryDestination destinations = 2; +} + +// Details about how and where to publish client libraries. +message ClientLibrarySettings { + // Version of the API to apply these settings to. + string version = 1; + + // Launch stage of this version of the API. + LaunchStage launch_stage = 2; + + // When using transport=rest, the client request will encode enums as + // numbers rather than strings. + bool rest_numeric_enums = 3; + + // Settings for legacy Java features, supported in the Service YAML. + JavaSettings java_settings = 21; + + // Settings for C++ client libraries. + CppSettings cpp_settings = 22; + + // Settings for PHP client libraries. + PhpSettings php_settings = 23; + + // Settings for Python client libraries. + PythonSettings python_settings = 24; + + // Settings for Node client libraries. + NodeSettings node_settings = 25; + + // Settings for .NET client libraries. + DotnetSettings dotnet_settings = 26; + + // Settings for Ruby client libraries. + RubySettings ruby_settings = 27; + + // Settings for Go client libraries. + GoSettings go_settings = 28; +} + +// This message configures the settings for publishing [Google Cloud Client +// libraries](https://cloud.google.com/apis/docs/cloud-client-libraries) +// generated from the service config. +message Publishing { + // A list of API method settings, e.g. the behavior for methods that use the + // long-running operation pattern. + repeated MethodSettings method_settings = 2; + + // Link to a place that API users can report issues. Example: + // https://issuetracker.google.com/issues/new?component=190865&template=1161103 + string new_issue_uri = 101; + + // Link to product home page. Example: + // https://cloud.google.com/asset-inventory/docs/overview + string documentation_uri = 102; + + // Used as a tracking tag when collecting data about the APIs developer + // relations artifacts like docs, packages delivered to package managers, + // etc. Example: "speech". + string api_short_name = 103; + + // GitHub label to apply to issues and pull requests opened for this API. + string github_label = 104; + + // GitHub teams to be added to CODEOWNERS in the directory in GitHub + // containing source code for the client libraries for this API. + repeated string codeowner_github_teams = 105; + + // A prefix used in sample code when demarking regions to be included in + // documentation. + string doc_tag_prefix = 106; + + // For whom the client library is being published. + ClientLibraryOrganization organization = 107; + + // Client library settings. If the same version string appears multiple + // times in this list, then the last one wins. Settings from earlier + // settings with the same version string are discarded. + repeated ClientLibrarySettings library_settings = 109; +} + +// Settings for Java client libraries. +message JavaSettings { + // The package name to use in Java. Clobbers the java_package option + // set in the protobuf. This should be used **only** by APIs + // who have already set the language_settings.java.package_name" field + // in gapic.yaml. API teams should use the protobuf java_package option + // where possible. + // + // Example of a YAML configuration:: + // + // publishing: + // java_settings: + // library_package: com.google.cloud.pubsub.v1 + string library_package = 1; + + // Configure the Java class name to use instead of the service's for its + // corresponding generated GAPIC client. Keys are fully-qualified + // service names as they appear in the protobuf (including the full + // the language_settings.java.interface_names" field in gapic.yaml. API + // teams should otherwise use the service name as it appears in the + // protobuf. + // + // Example of a YAML configuration:: + // + // publishing: + // java_settings: + // service_class_names: + // - google.pubsub.v1.Publisher: TopicAdmin + // - google.pubsub.v1.Subscriber: SubscriptionAdmin + map service_class_names = 2; + + // Some settings. + CommonLanguageSettings common = 3; +} + +// Settings for C++ client libraries. +message CppSettings { + // Some settings. + CommonLanguageSettings common = 1; +} + +// Settings for Php client libraries. +message PhpSettings { + // Some settings. + CommonLanguageSettings common = 1; +} + +// Settings for Python client libraries. +message PythonSettings { + // Some settings. + CommonLanguageSettings common = 1; +} + +// Settings for Node client libraries. +message NodeSettings { + // Some settings. + CommonLanguageSettings common = 1; +} + +// Settings for Dotnet client libraries. +message DotnetSettings { + // Some settings. + CommonLanguageSettings common = 1; +} + +// Settings for Ruby client libraries. +message RubySettings { + // Some settings. + CommonLanguageSettings common = 1; +} + +// Settings for Go client libraries. +message GoSettings { + // Some settings. + CommonLanguageSettings common = 1; +} + +// Describes the generator configuration for a method. +message MethodSettings { + // Describes settings to use when generating API methods that use the + // long-running operation pattern. + // All default values below are from those used in the client library + // generators (e.g. + // [Java](https://github.com/googleapis/gapic-generator-java/blob/04c2faa191a9b5a10b92392fe8482279c4404803/src/main/java/com/google/api/generator/gapic/composer/common/RetrySettingsComposer.java)). + message LongRunning { + // Initial delay after which the first poll request will be made. + // Default value: 5 seconds. + google.protobuf.Duration initial_poll_delay = 1; + + // Multiplier to gradually increase delay between subsequent polls until it + // reaches max_poll_delay. + // Default value: 1.5. + float poll_delay_multiplier = 2; + + // Maximum time between two subsequent poll requests. + // Default value: 45 seconds. + google.protobuf.Duration max_poll_delay = 3; + + // Total polling timeout. + // Default value: 5 minutes. + google.protobuf.Duration total_poll_timeout = 4; + } + + // The fully qualified name of the method, for which the options below apply. + // This is used to find the method to apply the options. + string selector = 1; + + // Describes settings to use for long-running operations when generating + // API methods for RPCs. Complements RPCs that use the annotations in + // google/longrunning/operations.proto. + // + // Example of a YAML configuration:: + // + // publishing: + // method_behavior: + // - selector: CreateAdDomain + // long_running: + // initial_poll_delay: + // seconds: 60 # 1 minute + // poll_delay_multiplier: 1.5 + // max_poll_delay: + // seconds: 360 # 6 minutes + // total_poll_timeout: + // seconds: 54000 # 90 minutes + LongRunning long_running = 2; +} + +// The organization for which the client libraries are being published. +// Affects the url where generated docs are published, etc. +enum ClientLibraryOrganization { + // Not useful. + CLIENT_LIBRARY_ORGANIZATION_UNSPECIFIED = 0; + + // Google Cloud Platform Org. + CLOUD = 1; + + // Ads (Advertising) Org. + ADS = 2; + + // Photos Org. + PHOTOS = 3; + + // Street View Org. + STREET_VIEW = 4; +} + +// To where should client libraries be published? +enum ClientLibraryDestination { + // Client libraries will neither be generated nor published to package + // managers. + CLIENT_LIBRARY_DESTINATION_UNSPECIFIED = 0; + + // Generate the client library in a repo under github.com/googleapis, + // but don't publish it to package managers. + GITHUB = 10; + + // Publish the library to package managers like nuget.org and npmjs.com. + PACKAGE_MANAGER = 20; +} diff --git a/proto/googleapis/google/api/field_behavior.proto b/proto/googleapis/google/api/field_behavior.proto new file mode 100644 index 0000000000..c4abe3b670 --- /dev/null +++ b/proto/googleapis/google/api/field_behavior.proto @@ -0,0 +1,90 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "FieldBehaviorProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.FieldOptions { + // A designation of a specific field behavior (required, output only, etc.) + // in protobuf messages. + // + // Examples: + // + // string name = 1 [(google.api.field_behavior) = REQUIRED]; + // State state = 1 [(google.api.field_behavior) = OUTPUT_ONLY]; + // google.protobuf.Duration ttl = 1 + // [(google.api.field_behavior) = INPUT_ONLY]; + // google.protobuf.Timestamp expire_time = 1 + // [(google.api.field_behavior) = OUTPUT_ONLY, + // (google.api.field_behavior) = IMMUTABLE]; + repeated google.api.FieldBehavior field_behavior = 1052; +} + +// An indicator of the behavior of a given field (for example, that a field +// is required in requests, or given as output but ignored as input). +// This **does not** change the behavior in protocol buffers itself; it only +// denotes the behavior and may affect how API tooling handles the field. +// +// Note: This enum **may** receive new values in the future. +enum FieldBehavior { + // Conventional default for enums. Do not use this. + FIELD_BEHAVIOR_UNSPECIFIED = 0; + + // Specifically denotes a field as optional. + // While all fields in protocol buffers are optional, this may be specified + // for emphasis if appropriate. + OPTIONAL = 1; + + // Denotes a field as required. + // This indicates that the field **must** be provided as part of the request, + // and failure to do so will cause an error (usually `INVALID_ARGUMENT`). + REQUIRED = 2; + + // Denotes a field as output only. + // This indicates that the field is provided in responses, but including the + // field in a request does nothing (the server *must* ignore it and + // *must not* throw an error as a result of the field's presence). + OUTPUT_ONLY = 3; + + // Denotes a field as input only. + // This indicates that the field is provided in requests, and the + // corresponding field is not included in output. + INPUT_ONLY = 4; + + // Denotes a field as immutable. + // This indicates that the field may be set once in a request to create a + // resource, but may not be changed thereafter. + IMMUTABLE = 5; + + // Denotes that a (repeated) field is an unordered list. + // This indicates that the service may provide the elements of the list + // in any arbitrary order, rather than the order the user originally + // provided. Additionally, the list's order may or may not be stable. + UNORDERED_LIST = 6; + + // Denotes that this field returns a non-empty default value if not set. + // This indicates that if the user provides the empty value in a request, + // a non-empty value will be returned. The user will not be aware of what + // non-empty value to expect. + NON_EMPTY_DEFAULT = 7; +} diff --git a/proto/googleapis/google/api/launch_stage.proto b/proto/googleapis/google/api/launch_stage.proto new file mode 100644 index 0000000000..6524db5756 --- /dev/null +++ b/proto/googleapis/google/api/launch_stage.proto @@ -0,0 +1,72 @@ +// Copyright 2015 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api;api"; +option java_multiple_files = true; +option java_outer_classname = "LaunchStageProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// The launch stage as defined by [Google Cloud Platform +// Launch Stages](https://cloud.google.com/terms/launch-stages). +enum LaunchStage { + // Do not use this default value. + LAUNCH_STAGE_UNSPECIFIED = 0; + + // The feature is not yet implemented. Users can not use it. + UNIMPLEMENTED = 6; + + // Prelaunch features are hidden from users and are only visible internally. + PRELAUNCH = 7; + + // Early Access features are limited to a closed group of testers. To use + // these features, you must sign up in advance and sign a Trusted Tester + // agreement (which includes confidentiality provisions). These features may + // be unstable, changed in backward-incompatible ways, and are not + // guaranteed to be released. + EARLY_ACCESS = 1; + + // Alpha is a limited availability test for releases before they are cleared + // for widespread use. By Alpha, all significant design issues are resolved + // and we are in the process of verifying functionality. Alpha customers + // need to apply for access, agree to applicable terms, and have their + // projects allowlisted. Alpha releases don't have to be feature complete, + // no SLAs are provided, and there are no technical support obligations, but + // they will be far enough along that customers can actually use them in + // test environments or for limited-use tests -- just like they would in + // normal production cases. + ALPHA = 2; + + // Beta is the point at which we are ready to open a release for any + // customer to use. There are no SLA or technical support obligations in a + // Beta release. Products will be complete from a feature perspective, but + // may have some open outstanding issues. Beta releases are suitable for + // limited production use cases. + BETA = 3; + + // GA features are open to all developers and are considered stable and + // fully qualified for production use. + GA = 4; + + // Deprecated features are scheduled to be shut down and removed. For more + // information, see the "Deprecation Policy" section of our [Terms of + // Service](https://cloud.google.com/terms/) + // and the [Google Cloud Platform Subject to the Deprecation + // Policy](https://cloud.google.com/terms/deprecation) documentation. + DEPRECATED = 5; +} diff --git a/proto/googleapis/google/api/resource.proto b/proto/googleapis/google/api/resource.proto new file mode 100644 index 0000000000..0ce0344f57 --- /dev/null +++ b/proto/googleapis/google/api/resource.proto @@ -0,0 +1,238 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/descriptor.proto"; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "ResourceProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.FieldOptions { + // An annotation that describes a resource reference, see + // [ResourceReference][]. + google.api.ResourceReference resource_reference = 1055; +} + +extend google.protobuf.FileOptions { + // An annotation that describes a resource definition without a corresponding + // message; see [ResourceDescriptor][]. + repeated google.api.ResourceDescriptor resource_definition = 1053; +} + +extend google.protobuf.MessageOptions { + // An annotation that describes a resource definition, see + // [ResourceDescriptor][]. + google.api.ResourceDescriptor resource = 1053; +} + +// A simple descriptor of a resource type. +// +// ResourceDescriptor annotates a resource message (either by means of a +// protobuf annotation or use in the service config), and associates the +// resource's schema, the resource type, and the pattern of the resource name. +// +// Example: +// +// message Topic { +// // Indicates this message defines a resource schema. +// // Declares the resource type in the format of {service}/{kind}. +// // For Kubernetes resources, the format is {api group}/{kind}. +// option (google.api.resource) = { +// type: "pubsub.googleapis.com/Topic" +// pattern: "projects/{project}/topics/{topic}" +// }; +// } +// +// The ResourceDescriptor Yaml config will look like: +// +// resources: +// - type: "pubsub.googleapis.com/Topic" +// pattern: "projects/{project}/topics/{topic}" +// +// Sometimes, resources have multiple patterns, typically because they can +// live under multiple parents. +// +// Example: +// +// message LogEntry { +// option (google.api.resource) = { +// type: "logging.googleapis.com/LogEntry" +// pattern: "projects/{project}/logs/{log}" +// pattern: "folders/{folder}/logs/{log}" +// pattern: "organizations/{organization}/logs/{log}" +// pattern: "billingAccounts/{billing_account}/logs/{log}" +// }; +// } +// +// The ResourceDescriptor Yaml config will look like: +// +// resources: +// - type: 'logging.googleapis.com/LogEntry' +// pattern: "projects/{project}/logs/{log}" +// pattern: "folders/{folder}/logs/{log}" +// pattern: "organizations/{organization}/logs/{log}" +// pattern: "billingAccounts/{billing_account}/logs/{log}" +message ResourceDescriptor { + // A description of the historical or future-looking state of the + // resource pattern. + enum History { + // The "unset" value. + HISTORY_UNSPECIFIED = 0; + + // The resource originally had one pattern and launched as such, and + // additional patterns were added later. + ORIGINALLY_SINGLE_PATTERN = 1; + + // The resource has one pattern, but the API owner expects to add more + // later. (This is the inverse of ORIGINALLY_SINGLE_PATTERN, and prevents + // that from being necessary once there are multiple patterns.) + FUTURE_MULTI_PATTERN = 2; + } + + // A flag representing a specific style that a resource claims to conform to. + enum Style { + // The unspecified value. Do not use. + STYLE_UNSPECIFIED = 0; + + // This resource is intended to be "declarative-friendly". + // + // Declarative-friendly resources must be more strictly consistent, and + // setting this to true communicates to tools that this resource should + // adhere to declarative-friendly expectations. + // + // Note: This is used by the API linter (linter.aip.dev) to enable + // additional checks. + DECLARATIVE_FRIENDLY = 1; + } + + // The resource type. It must be in the format of + // {service_name}/{resource_type_kind}. The `resource_type_kind` must be + // singular and must not include version numbers. + // + // Example: `storage.googleapis.com/Bucket` + // + // The value of the resource_type_kind must follow the regular expression + // /[A-Za-z][a-zA-Z0-9]+/. It should start with an upper case character and + // should use PascalCase (UpperCamelCase). The maximum number of + // characters allowed for the `resource_type_kind` is 100. + string type = 1; + + // Optional. The relative resource name pattern associated with this resource + // type. The DNS prefix of the full resource name shouldn't be specified here. + // + // The path pattern must follow the syntax, which aligns with HTTP binding + // syntax: + // + // Template = Segment { "/" Segment } ; + // Segment = LITERAL | Variable ; + // Variable = "{" LITERAL "}" ; + // + // Examples: + // + // - "projects/{project}/topics/{topic}" + // - "projects/{project}/knowledgeBases/{knowledge_base}" + // + // The components in braces correspond to the IDs for each resource in the + // hierarchy. It is expected that, if multiple patterns are provided, + // the same component name (e.g. "project") refers to IDs of the same + // type of resource. + repeated string pattern = 2; + + // Optional. The field on the resource that designates the resource name + // field. If omitted, this is assumed to be "name". + string name_field = 3; + + // Optional. The historical or future-looking state of the resource pattern. + // + // Example: + // + // // The InspectTemplate message originally only supported resource + // // names with organization, and project was added later. + // message InspectTemplate { + // option (google.api.resource) = { + // type: "dlp.googleapis.com/InspectTemplate" + // pattern: + // "organizations/{organization}/inspectTemplates/{inspect_template}" + // pattern: "projects/{project}/inspectTemplates/{inspect_template}" + // history: ORIGINALLY_SINGLE_PATTERN + // }; + // } + History history = 4; + + // The plural name used in the resource name and permission names, such as + // 'projects' for the resource name of 'projects/{project}' and the permission + // name of 'cloudresourcemanager.googleapis.com/projects.get'. It is the same + // concept of the `plural` field in k8s CRD spec + // https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/ + // + // Note: The plural form is required even for singleton resources. See + // https://aip.dev/156 + string plural = 5; + + // The same concept of the `singular` field in k8s CRD spec + // https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/ + // Such as "project" for the `resourcemanager.googleapis.com/Project` type. + string singular = 6; + + // Style flag(s) for this resource. + // These indicate that a resource is expected to conform to a given + // style. See the specific style flags for additional information. + repeated Style style = 10; +} + +// Defines a proto annotation that describes a string field that refers to +// an API resource. +message ResourceReference { + // The resource type that the annotated field references. + // + // Example: + // + // message Subscription { + // string topic = 2 [(google.api.resource_reference) = { + // type: "pubsub.googleapis.com/Topic" + // }]; + // } + // + // Occasionally, a field may reference an arbitrary resource. In this case, + // APIs use the special value * in their resource reference. + // + // Example: + // + // message GetIamPolicyRequest { + // string resource = 2 [(google.api.resource_reference) = { + // type: "*" + // }]; + // } + string type = 1; + + // The resource type of a child collection that the annotated field + // references. This is useful for annotating the `parent` field that + // doesn't have a fixed resource type. + // + // Example: + // + // message ListLogEntriesRequest { + // string parent = 1 [(google.api.resource_reference) = { + // child_type: "logging.googleapis.com/LogEntry" + // }; + // } + string child_type = 2; +} diff --git a/proto/sdk/alpha/alpha.proto b/proto/sdk/alpha/alpha.proto index 1a25e1e922..eed9f49765 100644 --- a/proto/sdk/alpha/alpha.proto +++ b/proto/sdk/alpha/alpha.proto @@ -18,6 +18,11 @@ package agones.dev.sdk.alpha; option go_package = "./alpha"; import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/field_mask.proto"; import "protoc-gen-openapiv2/options/annotations.proto"; option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { @@ -122,6 +127,24 @@ service SDK { get: "/alpha/player/connected" }; } + + // Gets a Counter. Returns NOT_FOUND if the Counter does not exist. + rpc GetCounter(GetCounterRequest) returns (Counter) { + option (google.api.http) = { + get: "/v1alpha1/{name=counters/*}" + }; + option (google.api.method_signature) = "name"; + } + + // Updates a Counter. Returns NOT_FOUND if the Counter does not exist (name cannot be updated). + // Returns INVALID_ARGUMENT if the field mask path(s) are not field(s) of the Counter. + rpc UpdateCounter(UpdateCounterRequest) returns (Counter) { + option (google.api.http) = { + patch: "/v1alpha1/{counter.name=counters/*}" + body: "counter" + }; + option (google.api.method_signature) = "counter,update_mask"; + } } // I am Empty @@ -146,4 +169,41 @@ message PlayerID { // List of Player IDs message PlayerIDList { repeated string list = 1; -} \ No newline at end of file +} + +// A representation of a Counter. +message Counter { + option (google.api.resource) = { + type: "agones.dev/Counter" + pattern: "counters/{counter}" + }; + // The name of the Counter + string name = 1; + // The current count of the Counter + optional int64 count = 2; + // The maximum capacity of the Counter + optional int64 capacity = 3; +} + +message GetCounterRequest { + // The name of the Counter to get + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "agones.dev/Counter" + }]; +} + +message UpdateCounterRequest { + // The Counter to update + Counter counter = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "agones.dev/Counter" + }]; + + // Required. Mask (list) of fields to update. + // Fields are specified relative to the Counter + // (e.g. `count`, `capacity`; *not* `Counter.count` or `Counter.capacity`). + google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = REQUIRED]; +} diff --git a/proto/sdk/sdk.proto b/proto/sdk/sdk.proto index d344de919f..085d4e1563 100644 --- a/proto/sdk/sdk.proto +++ b/proto/sdk/sdk.proto @@ -162,6 +162,13 @@ message GameServer { repeated string ids = 3; } + // [Stage:Alpha] + // [FeatureFlag:CountsAndLists] + message CounterStatus { + int64 count = 1; + int64 capacity = 2; + } + string state = 1; string address = 2; repeated Port ports = 3; @@ -169,5 +176,9 @@ message GameServer { // [Stage:Alpha] // [FeatureFlag:PlayerTracking] PlayerStatus players = 4; + + // [Stage:Alpha] + // [FeatureFlag:CountsAndLists] + map counters = 5; } } diff --git a/sdks/go/alpha_test.go b/sdks/go/alpha_test.go index 954dc7acec..13490b63c3 100644 --- a/sdks/go/alpha_test.go +++ b/sdks/go/alpha_test.go @@ -18,11 +18,11 @@ import ( "context" "testing" + "agones.dev/agones/pkg/sdk/alpha" "github.com/stretchr/testify/assert" - "google.golang.org/grpc" - - "agones.dev/agones/pkg/sdk/alpha" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) func TestAlphaGetAndSetPlayerCapacity(t *testing.T) { @@ -112,3 +112,15 @@ func (a *alphaMock) GetPlayerCapacity(ctx context.Context, in *alpha.Empty, opts func (a *alphaMock) GetPlayerCount(ctx context.Context, in *alpha.Empty, opts ...grpc.CallOption) (*alpha.Count, error) { return &alpha.Count{Count: a.playerCount}, nil } + +// GetCounter to be implemented +func (a *alphaMock) GetCounter(ctx context.Context, in *alpha.GetCounterRequest, opts ...grpc.CallOption) (*alpha.Counter, error) { + // TODO(#2716): Implement me! + return nil, status.Error(codes.Unimplemented, "GetCounter coming soon") +} + +// UpdateCounter to be implemented +func (a *alphaMock) UpdateCounter(ctx context.Context, in *alpha.UpdateCounterRequest, opts ...grpc.CallOption) (*alpha.Counter, error) { + // TODO(#2716): Implement me! + return nil, status.Error(codes.Unimplemented, "UpdateCounter coming soon") +} diff --git a/sdks/swagger/alpha.swagger.json b/sdks/swagger/alpha.swagger.json index 6d579f5a96..af23238db2 100644 --- a/sdks/swagger/alpha.swagger.json +++ b/sdks/swagger/alpha.swagger.json @@ -43,7 +43,7 @@ "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/alphaEmpty" + "$ref": "#/definitions/sdkalphaEmpty" } } }, @@ -182,6 +182,82 @@ "SDK" ] } + }, + "/v1alpha1/{counter.name}": { + "patch": { + "summary": "Updates a Counter. Returns NOT_FOUND if the Counter does not exist (name cannot be updated).\nReturns INVALID_ARGUMENT if the field mask path(s) are not field(s) of the Counter.", + "operationId": "UpdateCounter", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/alphaCounter" + } + } + }, + "parameters": [ + { + "name": "counter.name", + "description": "The name of the Counter", + "in": "path", + "required": true, + "type": "string", + "pattern": "counters/[^/]+" + }, + { + "name": "counter", + "description": "The Counter to update", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "count": { + "type": "string", + "format": "int64", + "title": "The current count of the Counter" + }, + "capacity": { + "type": "string", + "format": "int64", + "title": "The maximum capacity of the Counter" + } + }, + "title": "The Counter to update" + } + } + ], + "tags": [ + "SDK" + ] + } + }, + "/v1alpha1/{name}": { + "get": { + "summary": "Gets a Counter. Returns NOT_FOUND if the Counter does not exist.", + "operationId": "GetCounter", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/alphaCounter" + } + } + }, + "parameters": [ + { + "name": "name", + "description": "The name of the Counter to get", + "in": "path", + "required": true, + "type": "string", + "pattern": "counters/[^/]+" + } + ], + "tags": [ + "SDK" + ] + } } }, "definitions": { @@ -205,9 +281,25 @@ }, "description": "Store a count variable." }, - "alphaEmpty": { + "alphaCounter": { "type": "object", - "title": "I am Empty" + "properties": { + "name": { + "type": "string", + "title": "The name of the Counter" + }, + "count": { + "type": "string", + "format": "int64", + "title": "The current count of the Counter" + }, + "capacity": { + "type": "string", + "format": "int64", + "title": "The maximum capacity of the Counter" + } + }, + "description": "A representation of a Counter." }, "alphaPlayerID": { "type": "object", @@ -229,6 +321,10 @@ } }, "title": "List of Player IDs" + }, + "sdkalphaEmpty": { + "type": "object", + "title": "I am Empty" } } } diff --git a/sdks/swagger/sdk.swagger.json b/sdks/swagger/sdk.swagger.json index 091f349b2f..5f72c9d220 100644 --- a/sdks/swagger/sdk.swagger.json +++ b/sdks/swagger/sdk.swagger.json @@ -327,6 +327,20 @@ } } }, + "StatusCounterStatus": { + "type": "object", + "properties": { + "count": { + "type": "string", + "format": "int64" + }, + "capacity": { + "type": "string", + "format": "int64" + } + }, + "title": "[Stage:Alpha]\n[FeatureFlag:CountsAndLists]" + }, "StatusPlayerStatus": { "type": "object", "properties": { @@ -400,11 +414,20 @@ "ports": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/StatusPort" } }, "players": { - "$ref": "#/definitions/StatusPlayerStatus" + "$ref": "#/definitions/StatusPlayerStatus", + "title": "[Stage:Alpha]\n[FeatureFlag:PlayerTracking]" + }, + "counters": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/StatusCounterStatus" + }, + "title": "[Stage:Alpha]\n[FeatureFlag:CountsAndLists]" } } }, diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go new file mode 100644 index 0000000000..e54a76c7e3 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go @@ -0,0 +1,156 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cmpopts provides common options for the cmp package. +package cmpopts + +import ( + "errors" + "math" + "reflect" + "time" + + "github.com/google/go-cmp/cmp" +) + +func equateAlways(_, _ interface{}) bool { return true } + +// EquateEmpty returns a Comparer option that determines all maps and slices +// with a length of zero to be equal, regardless of whether they are nil. +// +// EquateEmpty can be used in conjunction with SortSlices and SortMaps. +func EquateEmpty() cmp.Option { + return cmp.FilterValues(isEmpty, cmp.Comparer(equateAlways)) +} + +func isEmpty(x, y interface{}) bool { + vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) + return (x != nil && y != nil && vx.Type() == vy.Type()) && + (vx.Kind() == reflect.Slice || vx.Kind() == reflect.Map) && + (vx.Len() == 0 && vy.Len() == 0) +} + +// EquateApprox returns a Comparer option that determines float32 or float64 +// values to be equal if they are within a relative fraction or absolute margin. +// This option is not used when either x or y is NaN or infinite. +// +// The fraction determines that the difference of two values must be within the +// smaller fraction of the two values, while the margin determines that the two +// values must be within some absolute margin. +// To express only a fraction or only a margin, use 0 for the other parameter. +// The fraction and margin must be non-negative. +// +// The mathematical expression used is equivalent to: +// +// |x-y| ≤ max(fraction*min(|x|, |y|), margin) +// +// EquateApprox can be used in conjunction with EquateNaNs. +func EquateApprox(fraction, margin float64) cmp.Option { + if margin < 0 || fraction < 0 || math.IsNaN(margin) || math.IsNaN(fraction) { + panic("margin or fraction must be a non-negative number") + } + a := approximator{fraction, margin} + return cmp.Options{ + cmp.FilterValues(areRealF64s, cmp.Comparer(a.compareF64)), + cmp.FilterValues(areRealF32s, cmp.Comparer(a.compareF32)), + } +} + +type approximator struct{ frac, marg float64 } + +func areRealF64s(x, y float64) bool { + return !math.IsNaN(x) && !math.IsNaN(y) && !math.IsInf(x, 0) && !math.IsInf(y, 0) +} +func areRealF32s(x, y float32) bool { + return areRealF64s(float64(x), float64(y)) +} +func (a approximator) compareF64(x, y float64) bool { + relMarg := a.frac * math.Min(math.Abs(x), math.Abs(y)) + return math.Abs(x-y) <= math.Max(a.marg, relMarg) +} +func (a approximator) compareF32(x, y float32) bool { + return a.compareF64(float64(x), float64(y)) +} + +// EquateNaNs returns a Comparer option that determines float32 and float64 +// NaN values to be equal. +// +// EquateNaNs can be used in conjunction with EquateApprox. +func EquateNaNs() cmp.Option { + return cmp.Options{ + cmp.FilterValues(areNaNsF64s, cmp.Comparer(equateAlways)), + cmp.FilterValues(areNaNsF32s, cmp.Comparer(equateAlways)), + } +} + +func areNaNsF64s(x, y float64) bool { + return math.IsNaN(x) && math.IsNaN(y) +} +func areNaNsF32s(x, y float32) bool { + return areNaNsF64s(float64(x), float64(y)) +} + +// EquateApproxTime returns a Comparer option that determines two non-zero +// time.Time values to be equal if they are within some margin of one another. +// If both times have a monotonic clock reading, then the monotonic time +// difference will be used. The margin must be non-negative. +func EquateApproxTime(margin time.Duration) cmp.Option { + if margin < 0 { + panic("margin must be a non-negative number") + } + a := timeApproximator{margin} + return cmp.FilterValues(areNonZeroTimes, cmp.Comparer(a.compare)) +} + +func areNonZeroTimes(x, y time.Time) bool { + return !x.IsZero() && !y.IsZero() +} + +type timeApproximator struct { + margin time.Duration +} + +func (a timeApproximator) compare(x, y time.Time) bool { + // Avoid subtracting times to avoid overflow when the + // difference is larger than the largest representable duration. + if x.After(y) { + // Ensure x is always before y + x, y = y, x + } + // We're within the margin if x+margin >= y. + // Note: time.Time doesn't have AfterOrEqual method hence the negation. + return !x.Add(a.margin).Before(y) +} + +// AnyError is an error that matches any non-nil error. +var AnyError anyError + +type anyError struct{} + +func (anyError) Error() string { return "any error" } +func (anyError) Is(err error) bool { return err != nil } + +// EquateErrors returns a Comparer option that determines errors to be equal +// if errors.Is reports them to match. The AnyError error can be used to +// match any non-nil error. +func EquateErrors() cmp.Option { + return cmp.FilterValues(areConcreteErrors, cmp.Comparer(compareErrors)) +} + +// areConcreteErrors reports whether x and y are types that implement error. +// The input types are deliberately of the interface{} type rather than the +// error type so that we can handle situations where the current type is an +// interface{}, but the underlying concrete types both happen to implement +// the error interface. +func areConcreteErrors(x, y interface{}) bool { + _, ok1 := x.(error) + _, ok2 := y.(error) + return ok1 && ok2 +} + +func compareErrors(x, y interface{}) bool { + xe := x.(error) + ye := y.(error) + return errors.Is(xe, ye) || errors.Is(ye, xe) +} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go new file mode 100644 index 0000000000..80c60617e4 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go @@ -0,0 +1,206 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmpopts + +import ( + "fmt" + "reflect" + "unicode" + "unicode/utf8" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/internal/function" +) + +// IgnoreFields returns an Option that ignores fields of the +// given names on a single struct type. It respects the names of exported fields +// that are forwarded due to struct embedding. +// The struct type is specified by passing in a value of that type. +// +// The name may be a dot-delimited string (e.g., "Foo.Bar") to ignore a +// specific sub-field that is embedded or nested within the parent struct. +func IgnoreFields(typ interface{}, names ...string) cmp.Option { + sf := newStructFilter(typ, names...) + return cmp.FilterPath(sf.filter, cmp.Ignore()) +} + +// IgnoreTypes returns an Option that ignores all values assignable to +// certain types, which are specified by passing in a value of each type. +func IgnoreTypes(typs ...interface{}) cmp.Option { + tf := newTypeFilter(typs...) + return cmp.FilterPath(tf.filter, cmp.Ignore()) +} + +type typeFilter []reflect.Type + +func newTypeFilter(typs ...interface{}) (tf typeFilter) { + for _, typ := range typs { + t := reflect.TypeOf(typ) + if t == nil { + // This occurs if someone tries to pass in sync.Locker(nil) + panic("cannot determine type; consider using IgnoreInterfaces") + } + tf = append(tf, t) + } + return tf +} +func (tf typeFilter) filter(p cmp.Path) bool { + if len(p) < 1 { + return false + } + t := p.Last().Type() + for _, ti := range tf { + if t.AssignableTo(ti) { + return true + } + } + return false +} + +// IgnoreInterfaces returns an Option that ignores all values or references of +// values assignable to certain interface types. These interfaces are specified +// by passing in an anonymous struct with the interface types embedded in it. +// For example, to ignore sync.Locker, pass in struct{sync.Locker}{}. +func IgnoreInterfaces(ifaces interface{}) cmp.Option { + tf := newIfaceFilter(ifaces) + return cmp.FilterPath(tf.filter, cmp.Ignore()) +} + +type ifaceFilter []reflect.Type + +func newIfaceFilter(ifaces interface{}) (tf ifaceFilter) { + t := reflect.TypeOf(ifaces) + if ifaces == nil || t.Name() != "" || t.Kind() != reflect.Struct { + panic("input must be an anonymous struct") + } + for i := 0; i < t.NumField(); i++ { + fi := t.Field(i) + switch { + case !fi.Anonymous: + panic("struct cannot have named fields") + case fi.Type.Kind() != reflect.Interface: + panic("embedded field must be an interface type") + case fi.Type.NumMethod() == 0: + // This matches everything; why would you ever want this? + panic("cannot ignore empty interface") + default: + tf = append(tf, fi.Type) + } + } + return tf +} +func (tf ifaceFilter) filter(p cmp.Path) bool { + if len(p) < 1 { + return false + } + t := p.Last().Type() + for _, ti := range tf { + if t.AssignableTo(ti) { + return true + } + if t.Kind() != reflect.Ptr && reflect.PtrTo(t).AssignableTo(ti) { + return true + } + } + return false +} + +// IgnoreUnexported returns an Option that only ignores the immediate unexported +// fields of a struct, including anonymous fields of unexported types. +// In particular, unexported fields within the struct's exported fields +// of struct types, including anonymous fields, will not be ignored unless the +// type of the field itself is also passed to IgnoreUnexported. +// +// Avoid ignoring unexported fields of a type which you do not control (i.e. a +// type from another repository), as changes to the implementation of such types +// may change how the comparison behaves. Prefer a custom Comparer instead. +func IgnoreUnexported(typs ...interface{}) cmp.Option { + ux := newUnexportedFilter(typs...) + return cmp.FilterPath(ux.filter, cmp.Ignore()) +} + +type unexportedFilter struct{ m map[reflect.Type]bool } + +func newUnexportedFilter(typs ...interface{}) unexportedFilter { + ux := unexportedFilter{m: make(map[reflect.Type]bool)} + for _, typ := range typs { + t := reflect.TypeOf(typ) + if t == nil || t.Kind() != reflect.Struct { + panic(fmt.Sprintf("%T must be a non-pointer struct", typ)) + } + ux.m[t] = true + } + return ux +} +func (xf unexportedFilter) filter(p cmp.Path) bool { + sf, ok := p.Index(-1).(cmp.StructField) + if !ok { + return false + } + return xf.m[p.Index(-2).Type()] && !isExported(sf.Name()) +} + +// isExported reports whether the identifier is exported. +func isExported(id string) bool { + r, _ := utf8.DecodeRuneInString(id) + return unicode.IsUpper(r) +} + +// IgnoreSliceElements returns an Option that ignores elements of []V. +// The discard function must be of the form "func(T) bool" which is used to +// ignore slice elements of type V, where V is assignable to T. +// Elements are ignored if the function reports true. +func IgnoreSliceElements(discardFunc interface{}) cmp.Option { + vf := reflect.ValueOf(discardFunc) + if !function.IsType(vf.Type(), function.ValuePredicate) || vf.IsNil() { + panic(fmt.Sprintf("invalid discard function: %T", discardFunc)) + } + return cmp.FilterPath(func(p cmp.Path) bool { + si, ok := p.Index(-1).(cmp.SliceIndex) + if !ok { + return false + } + if !si.Type().AssignableTo(vf.Type().In(0)) { + return false + } + vx, vy := si.Values() + if vx.IsValid() && vf.Call([]reflect.Value{vx})[0].Bool() { + return true + } + if vy.IsValid() && vf.Call([]reflect.Value{vy})[0].Bool() { + return true + } + return false + }, cmp.Ignore()) +} + +// IgnoreMapEntries returns an Option that ignores entries of map[K]V. +// The discard function must be of the form "func(T, R) bool" which is used to +// ignore map entries of type K and V, where K and V are assignable to T and R. +// Entries are ignored if the function reports true. +func IgnoreMapEntries(discardFunc interface{}) cmp.Option { + vf := reflect.ValueOf(discardFunc) + if !function.IsType(vf.Type(), function.KeyValuePredicate) || vf.IsNil() { + panic(fmt.Sprintf("invalid discard function: %T", discardFunc)) + } + return cmp.FilterPath(func(p cmp.Path) bool { + mi, ok := p.Index(-1).(cmp.MapIndex) + if !ok { + return false + } + if !mi.Key().Type().AssignableTo(vf.Type().In(0)) || !mi.Type().AssignableTo(vf.Type().In(1)) { + return false + } + k := mi.Key() + vx, vy := mi.Values() + if vx.IsValid() && vf.Call([]reflect.Value{k, vx})[0].Bool() { + return true + } + if vy.IsValid() && vf.Call([]reflect.Value{k, vy})[0].Bool() { + return true + } + return false + }, cmp.Ignore()) +} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go new file mode 100644 index 0000000000..0eb2a758c2 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go @@ -0,0 +1,147 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmpopts + +import ( + "fmt" + "reflect" + "sort" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/internal/function" +) + +// SortSlices returns a Transformer option that sorts all []V. +// The less function must be of the form "func(T, T) bool" which is used to +// sort any slice with element type V that is assignable to T. +// +// The less function must be: +// - Deterministic: less(x, y) == less(x, y) +// - Irreflexive: !less(x, x) +// - Transitive: if !less(x, y) and !less(y, z), then !less(x, z) +// +// The less function does not have to be "total". That is, if !less(x, y) and +// !less(y, x) for two elements x and y, their relative order is maintained. +// +// SortSlices can be used in conjunction with EquateEmpty. +func SortSlices(lessFunc interface{}) cmp.Option { + vf := reflect.ValueOf(lessFunc) + if !function.IsType(vf.Type(), function.Less) || vf.IsNil() { + panic(fmt.Sprintf("invalid less function: %T", lessFunc)) + } + ss := sliceSorter{vf.Type().In(0), vf} + return cmp.FilterValues(ss.filter, cmp.Transformer("cmpopts.SortSlices", ss.sort)) +} + +type sliceSorter struct { + in reflect.Type // T + fnc reflect.Value // func(T, T) bool +} + +func (ss sliceSorter) filter(x, y interface{}) bool { + vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) + if !(x != nil && y != nil && vx.Type() == vy.Type()) || + !(vx.Kind() == reflect.Slice && vx.Type().Elem().AssignableTo(ss.in)) || + (vx.Len() <= 1 && vy.Len() <= 1) { + return false + } + // Check whether the slices are already sorted to avoid an infinite + // recursion cycle applying the same transform to itself. + ok1 := sort.SliceIsSorted(x, func(i, j int) bool { return ss.less(vx, i, j) }) + ok2 := sort.SliceIsSorted(y, func(i, j int) bool { return ss.less(vy, i, j) }) + return !ok1 || !ok2 +} +func (ss sliceSorter) sort(x interface{}) interface{} { + src := reflect.ValueOf(x) + dst := reflect.MakeSlice(src.Type(), src.Len(), src.Len()) + for i := 0; i < src.Len(); i++ { + dst.Index(i).Set(src.Index(i)) + } + sort.SliceStable(dst.Interface(), func(i, j int) bool { return ss.less(dst, i, j) }) + ss.checkSort(dst) + return dst.Interface() +} +func (ss sliceSorter) checkSort(v reflect.Value) { + start := -1 // Start of a sequence of equal elements. + for i := 1; i < v.Len(); i++ { + if ss.less(v, i-1, i) { + // Check that first and last elements in v[start:i] are equal. + if start >= 0 && (ss.less(v, start, i-1) || ss.less(v, i-1, start)) { + panic(fmt.Sprintf("incomparable values detected: want equal elements: %v", v.Slice(start, i))) + } + start = -1 + } else if start == -1 { + start = i + } + } +} +func (ss sliceSorter) less(v reflect.Value, i, j int) bool { + vx, vy := v.Index(i), v.Index(j) + return ss.fnc.Call([]reflect.Value{vx, vy})[0].Bool() +} + +// SortMaps returns a Transformer option that flattens map[K]V types to be a +// sorted []struct{K, V}. The less function must be of the form +// "func(T, T) bool" which is used to sort any map with key K that is +// assignable to T. +// +// Flattening the map into a slice has the property that cmp.Equal is able to +// use Comparers on K or the K.Equal method if it exists. +// +// The less function must be: +// - Deterministic: less(x, y) == less(x, y) +// - Irreflexive: !less(x, x) +// - Transitive: if !less(x, y) and !less(y, z), then !less(x, z) +// - Total: if x != y, then either less(x, y) or less(y, x) +// +// SortMaps can be used in conjunction with EquateEmpty. +func SortMaps(lessFunc interface{}) cmp.Option { + vf := reflect.ValueOf(lessFunc) + if !function.IsType(vf.Type(), function.Less) || vf.IsNil() { + panic(fmt.Sprintf("invalid less function: %T", lessFunc)) + } + ms := mapSorter{vf.Type().In(0), vf} + return cmp.FilterValues(ms.filter, cmp.Transformer("cmpopts.SortMaps", ms.sort)) +} + +type mapSorter struct { + in reflect.Type // T + fnc reflect.Value // func(T, T) bool +} + +func (ms mapSorter) filter(x, y interface{}) bool { + vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) + return (x != nil && y != nil && vx.Type() == vy.Type()) && + (vx.Kind() == reflect.Map && vx.Type().Key().AssignableTo(ms.in)) && + (vx.Len() != 0 || vy.Len() != 0) +} +func (ms mapSorter) sort(x interface{}) interface{} { + src := reflect.ValueOf(x) + outType := reflect.StructOf([]reflect.StructField{ + {Name: "K", Type: src.Type().Key()}, + {Name: "V", Type: src.Type().Elem()}, + }) + dst := reflect.MakeSlice(reflect.SliceOf(outType), src.Len(), src.Len()) + for i, k := range src.MapKeys() { + v := reflect.New(outType).Elem() + v.Field(0).Set(k) + v.Field(1).Set(src.MapIndex(k)) + dst.Index(i).Set(v) + } + sort.Slice(dst.Interface(), func(i, j int) bool { return ms.less(dst, i, j) }) + ms.checkSort(dst) + return dst.Interface() +} +func (ms mapSorter) checkSort(v reflect.Value) { + for i := 1; i < v.Len(); i++ { + if !ms.less(v, i-1, i) { + panic(fmt.Sprintf("partial order detected: want %v < %v", v.Index(i-1), v.Index(i))) + } + } +} +func (ms mapSorter) less(v reflect.Value, i, j int) bool { + vx, vy := v.Index(i).Field(0), v.Index(j).Field(0) + return ms.fnc.Call([]reflect.Value{vx, vy})[0].Bool() +} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go new file mode 100644 index 0000000000..ca11a40249 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go @@ -0,0 +1,189 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmpopts + +import ( + "fmt" + "reflect" + "strings" + + "github.com/google/go-cmp/cmp" +) + +// filterField returns a new Option where opt is only evaluated on paths that +// include a specific exported field on a single struct type. +// The struct type is specified by passing in a value of that type. +// +// The name may be a dot-delimited string (e.g., "Foo.Bar") to select a +// specific sub-field that is embedded or nested within the parent struct. +func filterField(typ interface{}, name string, opt cmp.Option) cmp.Option { + // TODO: This is currently unexported over concerns of how helper filters + // can be composed together easily. + // TODO: Add tests for FilterField. + + sf := newStructFilter(typ, name) + return cmp.FilterPath(sf.filter, opt) +} + +type structFilter struct { + t reflect.Type // The root struct type to match on + ft fieldTree // Tree of fields to match on +} + +func newStructFilter(typ interface{}, names ...string) structFilter { + // TODO: Perhaps allow * as a special identifier to allow ignoring any + // number of path steps until the next field match? + // This could be useful when a concrete struct gets transformed into + // an anonymous struct where it is not possible to specify that by type, + // but the transformer happens to provide guarantees about the names of + // the transformed fields. + + t := reflect.TypeOf(typ) + if t == nil || t.Kind() != reflect.Struct { + panic(fmt.Sprintf("%T must be a non-pointer struct", typ)) + } + var ft fieldTree + for _, name := range names { + cname, err := canonicalName(t, name) + if err != nil { + panic(fmt.Sprintf("%s: %v", strings.Join(cname, "."), err)) + } + ft.insert(cname) + } + return structFilter{t, ft} +} + +func (sf structFilter) filter(p cmp.Path) bool { + for i, ps := range p { + if ps.Type().AssignableTo(sf.t) && sf.ft.matchPrefix(p[i+1:]) { + return true + } + } + return false +} + +// fieldTree represents a set of dot-separated identifiers. +// +// For example, inserting the following selectors: +// +// Foo +// Foo.Bar.Baz +// Foo.Buzz +// Nuka.Cola.Quantum +// +// Results in a tree of the form: +// +// {sub: { +// "Foo": {ok: true, sub: { +// "Bar": {sub: { +// "Baz": {ok: true}, +// }}, +// "Buzz": {ok: true}, +// }}, +// "Nuka": {sub: { +// "Cola": {sub: { +// "Quantum": {ok: true}, +// }}, +// }}, +// }} +type fieldTree struct { + ok bool // Whether this is a specified node + sub map[string]fieldTree // The sub-tree of fields under this node +} + +// insert inserts a sequence of field accesses into the tree. +func (ft *fieldTree) insert(cname []string) { + if ft.sub == nil { + ft.sub = make(map[string]fieldTree) + } + if len(cname) == 0 { + ft.ok = true + return + } + sub := ft.sub[cname[0]] + sub.insert(cname[1:]) + ft.sub[cname[0]] = sub +} + +// matchPrefix reports whether any selector in the fieldTree matches +// the start of path p. +func (ft fieldTree) matchPrefix(p cmp.Path) bool { + for _, ps := range p { + switch ps := ps.(type) { + case cmp.StructField: + ft = ft.sub[ps.Name()] + if ft.ok { + return true + } + if len(ft.sub) == 0 { + return false + } + case cmp.Indirect: + default: + return false + } + } + return false +} + +// canonicalName returns a list of identifiers where any struct field access +// through an embedded field is expanded to include the names of the embedded +// types themselves. +// +// For example, suppose field "Foo" is not directly in the parent struct, +// but actually from an embedded struct of type "Bar". Then, the canonical name +// of "Foo" is actually "Bar.Foo". +// +// Suppose field "Foo" is not directly in the parent struct, but actually +// a field in two different embedded structs of types "Bar" and "Baz". +// Then the selector "Foo" causes a panic since it is ambiguous which one it +// refers to. The user must specify either "Bar.Foo" or "Baz.Foo". +func canonicalName(t reflect.Type, sel string) ([]string, error) { + var name string + sel = strings.TrimPrefix(sel, ".") + if sel == "" { + return nil, fmt.Errorf("name must not be empty") + } + if i := strings.IndexByte(sel, '.'); i < 0 { + name, sel = sel, "" + } else { + name, sel = sel[:i], sel[i:] + } + + // Type must be a struct or pointer to struct. + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + if t.Kind() != reflect.Struct { + return nil, fmt.Errorf("%v must be a struct", t) + } + + // Find the canonical name for this current field name. + // If the field exists in an embedded struct, then it will be expanded. + sf, _ := t.FieldByName(name) + if !isExported(name) { + // Avoid using reflect.Type.FieldByName for unexported fields due to + // buggy behavior with regard to embeddeding and unexported fields. + // See https://golang.org/issue/4876 for details. + sf = reflect.StructField{} + for i := 0; i < t.NumField() && sf.Name == ""; i++ { + if t.Field(i).Name == name { + sf = t.Field(i) + } + } + } + if sf.Name == "" { + return []string{name}, fmt.Errorf("does not exist") + } + var ss []string + for i := range sf.Index { + ss = append(ss, t.FieldByIndex(sf.Index[:i+1]).Name) + } + if sel == "" { + return ss, nil + } + ssPost, err := canonicalName(sf.Type, sel) + return append(ss, ssPost...), err +} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/xform.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/xform.go new file mode 100644 index 0000000000..8812443a2f --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/xform.go @@ -0,0 +1,36 @@ +// Copyright 2018, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmpopts + +import ( + "github.com/google/go-cmp/cmp" +) + +type xformFilter struct{ xform cmp.Option } + +func (xf xformFilter) filter(p cmp.Path) bool { + for _, ps := range p { + if t, ok := ps.(cmp.Transform); ok && t.Option() == xf.xform { + return false + } + } + return true +} + +// AcyclicTransformer returns a Transformer with a filter applied that ensures +// that the transformer cannot be recursively applied upon its own output. +// +// An example use case is a transformer that splits a string by lines: +// +// AcyclicTransformer("SplitLines", func(s string) []string{ +// return strings.Split(s, "\n") +// }) +// +// Had this been an unfiltered Transformer instead, this would result in an +// infinite cycle converting a string to []string to [][]string and so on. +func AcyclicTransformer(name string, xformFunc interface{}) cmp.Option { + xf := xformFilter{cmp.Transformer(name, xformFunc)} + return cmp.FilterPath(xf.filter, xf.xform) +} diff --git a/vendor/github.com/mennanov/fmutils/.gitignore b/vendor/github.com/mennanov/fmutils/.gitignore new file mode 100644 index 0000000000..4424e21aff --- /dev/null +++ b/vendor/github.com/mennanov/fmutils/.gitignore @@ -0,0 +1,15 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +coverage.txt +.idea diff --git a/vendor/github.com/mennanov/fmutils/LICENSE b/vendor/github.com/mennanov/fmutils/LICENSE new file mode 100644 index 0000000000..7b95091627 --- /dev/null +++ b/vendor/github.com/mennanov/fmutils/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Renat Mennanov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/mennanov/fmutils/README.md b/vendor/github.com/mennanov/fmutils/README.md new file mode 100644 index 0000000000..9dd6dc75b7 --- /dev/null +++ b/vendor/github.com/mennanov/fmutils/README.md @@ -0,0 +1,43 @@ +# Golang protobuf FieldMask utils + +[![Build Status](https://cloud.drone.io/api/badges/mennanov/fmutils/status.svg?ref=refs/heads/main)](https://cloud.drone.io/mennanov/fmutils) +[![Coverage Status](https://codecov.io/gh/mennanov/fmutils/branch/main/graph/badge.svg)](https://codecov.io/gh/mennanov/fmutils) +[![PkgGoDev](https://pkg.go.dev/badge/github.com/mennanov/fmutils)](https://pkg.go.dev/github.com/mennanov/fmutils) + +### Filter a protobuf message with a FieldMask applied + +```go +// Keeps the fields mentioned in the paths untouched, all the other fields will be cleared. +fmutils.Filter(protoMessage, []string{"a.b.c", "d"}) +``` + +### Prune a protobuf message with a FieldMask applied + +```go +// Clears all the fields mentioned in the paths, all the other fields will be left untouched. +fmutils.Prune(protoMessage, []string{"a.b.c", "d"}) +``` + +### Working with Golang protobuf APIv1 + +This library uses the [new Go API for protocol buffers](https://blog.golang.org/protobuf-apiv2). +If your `*.pb.go` files are generated with the old version APIv1 then you have 2 choices: + +- migrate to the new APIv2 `google.golang.org/protobuf` +- upgrade an existing APIv1 version to `github.com/golang/protobuf@v1.4.0` that implements the new API + +In both cases you'll need to regenerate `*.pb.go` files. + +If you decide to stay with APIv1 then you need to use the [`proto.MessageV2`](https://pkg.go.dev/github.com/golang/protobuf@v1.4.3/proto#MessageV2) function like this: + +```go +import protov1 "github.com/golang/protobuf/proto" + +fmutils.Filter(protov1.MessageV2(protoMessage), []string{"a.b.c", "d"}) +``` + +[Read more about the Go protobuf API versions.](https://blog.golang.org/protobuf-apiv2#TOC_4.) + +### Examples + +See the [examples_test.go](https://github.com/mennanov/fmutils/blob/main/examples_test.go) for real life examples. diff --git a/vendor/github.com/mennanov/fmutils/fmutils.go b/vendor/github.com/mennanov/fmutils/fmutils.go new file mode 100644 index 0000000000..be3a866543 --- /dev/null +++ b/vendor/github.com/mennanov/fmutils/fmutils.go @@ -0,0 +1,152 @@ +package fmutils + +import ( + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" +) + +// Filter keeps the msg fields that are listed in the paths and clears all the rest. +// +// This is a handy wrapper for NestedMask.Filter method. +// If the same paths are used to process multiple proto messages use NestedMask.Filter method directly. +func Filter(msg proto.Message, paths []string) { + NestedMaskFromPaths(paths).Filter(msg) +} + +// Prune clears all the fields listed in paths from the given msg. +// +// This is a handy wrapper for NestedMask.Prune method. +// If the same paths are used to process multiple proto messages use NestedMask.Filter method directly. +func Prune(msg proto.Message, paths []string) { + NestedMaskFromPaths(paths).Prune(msg) +} + +// NestedMask represents a field mask as a recursive map. +type NestedMask map[string]NestedMask + +// NestedMaskFromPaths creates an instance of NestedMask for the given paths. +func NestedMaskFromPaths(paths []string) NestedMask { + mask := make(NestedMask) + for _, path := range paths { + curr := mask + var letters []rune + for _, letter := range path { + if letter == '.' { + if len(letters) == 0 { + continue + } + + key := string(letters) + c, ok := curr[key] + if !ok { + c = make(NestedMask) + curr[key] = c + } + curr = c + letters = nil + continue + } + letters = append(letters, letter) + } + if len(letters) != 0 { + key := string(letters) + if _, ok := curr[key]; !ok { + curr[key] = make(NestedMask) + } + } + } + + return mask +} + +// Filter keeps the msg fields that are listed in the paths and clears all the rest. +// +// If the mask is empty then all the fields are kept. +// Paths are assumed to be valid and normalized otherwise the function may panic. +// See google.golang.org/protobuf/types/known/fieldmaskpb for details. +func (mask NestedMask) Filter(msg proto.Message) { + if len(mask) == 0 { + return + } + + rft := msg.ProtoReflect() + rft.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool { + m, ok := mask[string(fd.Name())] + if ok { + if len(m) == 0 { + return true + } + + if fd.IsMap() { + xmap := rft.Get(fd).Map() + xmap.Range(func(mk protoreflect.MapKey, mv protoreflect.Value) bool { + if mi, ok := m[mk.String()]; ok { + if i, ok := mv.Interface().(protoreflect.Message); ok && len(mi) > 0 { + mi.Filter(i.Interface()) + } + } else { + xmap.Clear(mk) + } + + return true + }) + } else if fd.IsList() { + list := rft.Get(fd).List() + for i := 0; i < list.Len(); i++ { + m.Filter(list.Get(i).Message().Interface()) + } + } else if fd.Kind() == protoreflect.MessageKind { + m.Filter(rft.Get(fd).Message().Interface()) + } + } else { + rft.Clear(fd) + } + return true + }) +} + +// Prune clears all the fields listed in paths from the given msg. +// +// All other fields are kept untouched. If the mask is empty no fields are cleared. +// This operation is the opposite of NestedMask.Filter. +// Paths are assumed to be valid and normalized otherwise the function may panic. +// See google.golang.org/protobuf/types/known/fieldmaskpb for details. +func (mask NestedMask) Prune(msg proto.Message) { + if len(mask) == 0 { + return + } + + rft := msg.ProtoReflect() + rft.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool { + m, ok := mask[string(fd.Name())] + if ok { + if len(m) == 0 { + rft.Clear(fd) + return true + } + + if fd.IsMap() { + xmap := rft.Get(fd).Map() + xmap.Range(func(mk protoreflect.MapKey, mv protoreflect.Value) bool { + if mi, ok := m[mk.String()]; ok { + if i, ok := mv.Interface().(protoreflect.Message); ok && len(mi) > 0 { + mi.Prune(i.Interface()) + } else { + xmap.Clear(mk) + } + } + + return true + }) + } else if fd.IsList() { + list := rft.Get(fd).List() + for i := 0; i < list.Len(); i++ { + m.Prune(list.Get(i).Message().Interface()) + } + } else if fd.Kind() == protoreflect.MessageKind { + m.Prune(rft.Get(fd).Message().Interface()) + } + } + return true + }) +} diff --git a/vendor/google.golang.org/protobuf/internal/msgfmt/format.go b/vendor/google.golang.org/protobuf/internal/msgfmt/format.go new file mode 100644 index 0000000000..a319550f69 --- /dev/null +++ b/vendor/google.golang.org/protobuf/internal/msgfmt/format.go @@ -0,0 +1,261 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package msgfmt implements a text marshaler combining the desirable features +// of both the JSON and proto text formats. +// It is optimized for human readability and has no associated deserializer. +package msgfmt + +import ( + "bytes" + "fmt" + "reflect" + "sort" + "strconv" + "strings" + "time" + + "google.golang.org/protobuf/encoding/protowire" + "google.golang.org/protobuf/internal/detrand" + "google.golang.org/protobuf/internal/genid" + "google.golang.org/protobuf/internal/order" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" +) + +// Format returns a formatted string for the message. +func Format(m proto.Message) string { + return string(appendMessage(nil, m.ProtoReflect())) +} + +// FormatValue returns a formatted string for an arbitrary value. +func FormatValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) string { + return string(appendValue(nil, v, fd)) +} + +func appendValue(b []byte, v protoreflect.Value, fd protoreflect.FieldDescriptor) []byte { + switch v := v.Interface().(type) { + case nil: + return append(b, ""...) + case bool, int32, int64, uint32, uint64, float32, float64: + return append(b, fmt.Sprint(v)...) + case string: + return append(b, strconv.Quote(string(v))...) + case []byte: + return append(b, strconv.Quote(string(v))...) + case protoreflect.EnumNumber: + return appendEnum(b, v, fd) + case protoreflect.Message: + return appendMessage(b, v) + case protoreflect.List: + return appendList(b, v, fd) + case protoreflect.Map: + return appendMap(b, v, fd) + default: + panic(fmt.Sprintf("invalid type: %T", v)) + } +} + +func appendEnum(b []byte, v protoreflect.EnumNumber, fd protoreflect.FieldDescriptor) []byte { + if fd != nil { + if ev := fd.Enum().Values().ByNumber(v); ev != nil { + return append(b, ev.Name()...) + } + } + return strconv.AppendInt(b, int64(v), 10) +} + +func appendMessage(b []byte, m protoreflect.Message) []byte { + if b2 := appendKnownMessage(b, m); b2 != nil { + return b2 + } + + b = append(b, '{') + order.RangeFields(m, order.IndexNameFieldOrder, func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { + b = append(b, fd.TextName()...) + b = append(b, ':') + b = appendValue(b, v, fd) + b = append(b, delim()...) + return true + }) + b = appendUnknown(b, m.GetUnknown()) + b = bytes.TrimRight(b, delim()) + b = append(b, '}') + return b +} + +var protocmpMessageType = reflect.TypeOf(map[string]interface{}(nil)) + +func appendKnownMessage(b []byte, m protoreflect.Message) []byte { + md := m.Descriptor() + fds := md.Fields() + switch md.FullName() { + case genid.Any_message_fullname: + var msgVal protoreflect.Message + url := m.Get(fds.ByNumber(genid.Any_TypeUrl_field_number)).String() + if v := reflect.ValueOf(m); v.Type().ConvertibleTo(protocmpMessageType) { + // For protocmp.Message, directly obtain the sub-message value + // which is stored in structured form, rather than as raw bytes. + m2 := v.Convert(protocmpMessageType).Interface().(map[string]interface{}) + v, ok := m2[string(genid.Any_Value_field_name)].(proto.Message) + if !ok { + return nil + } + msgVal = v.ProtoReflect() + } else { + val := m.Get(fds.ByNumber(genid.Any_Value_field_number)).Bytes() + mt, err := protoregistry.GlobalTypes.FindMessageByURL(url) + if err != nil { + return nil + } + msgVal = mt.New() + err = proto.UnmarshalOptions{AllowPartial: true}.Unmarshal(val, msgVal.Interface()) + if err != nil { + return nil + } + } + + b = append(b, '{') + b = append(b, "["+url+"]"...) + b = append(b, ':') + b = appendMessage(b, msgVal) + b = append(b, '}') + return b + + case genid.Timestamp_message_fullname: + secs := m.Get(fds.ByNumber(genid.Timestamp_Seconds_field_number)).Int() + nanos := m.Get(fds.ByNumber(genid.Timestamp_Nanos_field_number)).Int() + if nanos < 0 || nanos >= 1e9 { + return nil + } + t := time.Unix(secs, nanos).UTC() + x := t.Format("2006-01-02T15:04:05.000000000") // RFC 3339 + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, ".000") + return append(b, x+"Z"...) + + case genid.Duration_message_fullname: + sign := "" + secs := m.Get(fds.ByNumber(genid.Duration_Seconds_field_number)).Int() + nanos := m.Get(fds.ByNumber(genid.Duration_Nanos_field_number)).Int() + if nanos <= -1e9 || nanos >= 1e9 || (secs > 0 && nanos < 0) || (secs < 0 && nanos > 0) { + return nil + } + if secs < 0 || nanos < 0 { + sign, secs, nanos = "-", -1*secs, -1*nanos + } + x := fmt.Sprintf("%s%d.%09d", sign, secs, nanos) + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, ".000") + return append(b, x+"s"...) + + case genid.BoolValue_message_fullname, + genid.Int32Value_message_fullname, + genid.Int64Value_message_fullname, + genid.UInt32Value_message_fullname, + genid.UInt64Value_message_fullname, + genid.FloatValue_message_fullname, + genid.DoubleValue_message_fullname, + genid.StringValue_message_fullname, + genid.BytesValue_message_fullname: + fd := fds.ByNumber(genid.WrapperValue_Value_field_number) + return appendValue(b, m.Get(fd), fd) + } + + return nil +} + +func appendUnknown(b []byte, raw protoreflect.RawFields) []byte { + rs := make(map[protoreflect.FieldNumber][]protoreflect.RawFields) + for len(raw) > 0 { + num, _, n := protowire.ConsumeField(raw) + rs[num] = append(rs[num], raw[:n]) + raw = raw[n:] + } + + var ns []protoreflect.FieldNumber + for n := range rs { + ns = append(ns, n) + } + sort.Slice(ns, func(i, j int) bool { return ns[i] < ns[j] }) + + for _, n := range ns { + var leftBracket, rightBracket string + if len(rs[n]) > 1 { + leftBracket, rightBracket = "[", "]" + } + + b = strconv.AppendInt(b, int64(n), 10) + b = append(b, ':') + b = append(b, leftBracket...) + for _, r := range rs[n] { + num, typ, n := protowire.ConsumeTag(r) + r = r[n:] + switch typ { + case protowire.VarintType: + v, _ := protowire.ConsumeVarint(r) + b = strconv.AppendInt(b, int64(v), 10) + case protowire.Fixed32Type: + v, _ := protowire.ConsumeFixed32(r) + b = append(b, fmt.Sprintf("0x%08x", v)...) + case protowire.Fixed64Type: + v, _ := protowire.ConsumeFixed64(r) + b = append(b, fmt.Sprintf("0x%016x", v)...) + case protowire.BytesType: + v, _ := protowire.ConsumeBytes(r) + b = strconv.AppendQuote(b, string(v)) + case protowire.StartGroupType: + v, _ := protowire.ConsumeGroup(num, r) + b = append(b, '{') + b = appendUnknown(b, v) + b = bytes.TrimRight(b, delim()) + b = append(b, '}') + default: + panic(fmt.Sprintf("invalid type: %v", typ)) + } + b = append(b, delim()...) + } + b = bytes.TrimRight(b, delim()) + b = append(b, rightBracket...) + b = append(b, delim()...) + } + return b +} + +func appendList(b []byte, v protoreflect.List, fd protoreflect.FieldDescriptor) []byte { + b = append(b, '[') + for i := 0; i < v.Len(); i++ { + b = appendValue(b, v.Get(i), fd) + b = append(b, delim()...) + } + b = bytes.TrimRight(b, delim()) + b = append(b, ']') + return b +} + +func appendMap(b []byte, v protoreflect.Map, fd protoreflect.FieldDescriptor) []byte { + b = append(b, '{') + order.RangeEntries(v, order.GenericKeyOrder, func(k protoreflect.MapKey, v protoreflect.Value) bool { + b = appendValue(b, k.Value(), fd.MapKey()) + b = append(b, ':') + b = appendValue(b, v, fd.MapValue()) + b = append(b, delim()...) + return true + }) + b = bytes.TrimRight(b, delim()) + b = append(b, '}') + return b +} + +func delim() string { + // Deliberately introduce instability into the message string to + // discourage users from depending on it. + if detrand.Bool() { + return " " + } + return ", " +} diff --git a/vendor/google.golang.org/protobuf/testing/protocmp/reflect.go b/vendor/google.golang.org/protobuf/testing/protocmp/reflect.go new file mode 100644 index 0000000000..0a5e47467a --- /dev/null +++ b/vendor/google.golang.org/protobuf/testing/protocmp/reflect.go @@ -0,0 +1,258 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package protocmp + +import ( + "reflect" + "sort" + "strconv" + "strings" + + "google.golang.org/protobuf/internal/genid" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/runtime/protoiface" +) + +func reflectValueOf(v interface{}) protoreflect.Value { + switch v := v.(type) { + case Enum: + return protoreflect.ValueOfEnum(v.Number()) + case Message: + return protoreflect.ValueOfMessage(v.ProtoReflect()) + case []byte: + return protoreflect.ValueOfBytes(v) // avoid overlap with reflect.Slice check below + default: + switch rv := reflect.ValueOf(v); { + case rv.Kind() == reflect.Slice: + return protoreflect.ValueOfList(reflectList{rv}) + case rv.Kind() == reflect.Map: + return protoreflect.ValueOfMap(reflectMap{rv}) + default: + return protoreflect.ValueOf(v) + } + } +} + +type reflectMessage Message + +func (m reflectMessage) stringKey(fd protoreflect.FieldDescriptor) string { + if m.Descriptor() != fd.ContainingMessage() { + panic("mismatching containing message") + } + return fd.TextName() +} + +func (m reflectMessage) Descriptor() protoreflect.MessageDescriptor { + return (Message)(m).Descriptor() +} +func (m reflectMessage) Type() protoreflect.MessageType { + return reflectMessageType{m.Descriptor()} +} +func (m reflectMessage) New() protoreflect.Message { + return m.Type().New() +} +func (m reflectMessage) Interface() protoreflect.ProtoMessage { + return Message(m) +} +func (m reflectMessage) Range(f func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool) { + // Range over populated known fields. + fds := m.Descriptor().Fields() + for i := 0; i < fds.Len(); i++ { + fd := fds.Get(i) + if m.Has(fd) && !f(fd, m.Get(fd)) { + return + } + } + + // Range over populated extension fields. + for _, xd := range m[messageTypeKey].(messageMeta).xds { + if m.Has(xd) && !f(xd, m.Get(xd)) { + return + } + } +} +func (m reflectMessage) Has(fd protoreflect.FieldDescriptor) bool { + _, ok := m[m.stringKey(fd)] + return ok +} +func (m reflectMessage) Clear(protoreflect.FieldDescriptor) { + panic("invalid mutation of read-only message") +} +func (m reflectMessage) Get(fd protoreflect.FieldDescriptor) protoreflect.Value { + v, ok := m[m.stringKey(fd)] + if !ok { + switch { + case fd.IsList(): + return protoreflect.ValueOfList(reflectList{}) + case fd.IsMap(): + return protoreflect.ValueOfMap(reflectMap{}) + case fd.Message() != nil: + return protoreflect.ValueOfMessage(reflectMessage{ + messageTypeKey: messageMeta{md: fd.Message()}, + }) + default: + return fd.Default() + } + } + + // The transformation may leave Any messages in structured form. + // If so, convert them back to a raw-encoded form. + if fd.FullName() == genid.Any_Value_field_fullname { + if m, ok := v.(Message); ok { + b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m) + if err != nil { + panic("BUG: " + err.Error()) + } + return protoreflect.ValueOfBytes(b) + } + } + + return reflectValueOf(v) +} +func (m reflectMessage) Set(protoreflect.FieldDescriptor, protoreflect.Value) { + panic("invalid mutation of read-only message") +} +func (m reflectMessage) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + panic("invalid mutation of read-only message") +} +func (m reflectMessage) NewField(protoreflect.FieldDescriptor) protoreflect.Value { + panic("not implemented") +} +func (m reflectMessage) WhichOneof(od protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + if m.Descriptor().Oneofs().ByName(od.Name()) != od { + panic("oneof descriptor does not belong to this message") + } + fds := od.Fields() + for i := 0; i < fds.Len(); i++ { + fd := fds.Get(i) + if _, ok := m[m.stringKey(fd)]; ok { + return fd + } + } + return nil +} +func (m reflectMessage) GetUnknown() protoreflect.RawFields { + var nums []protoreflect.FieldNumber + for k := range m { + if len(strings.Trim(k, "0123456789")) == 0 { + n, _ := strconv.ParseUint(k, 10, 32) + nums = append(nums, protoreflect.FieldNumber(n)) + } + } + sort.Slice(nums, func(i, j int) bool { return nums[i] < nums[j] }) + + var raw protoreflect.RawFields + for _, num := range nums { + b, _ := m[strconv.FormatUint(uint64(num), 10)].(protoreflect.RawFields) + raw = append(raw, b...) + } + return raw +} +func (m reflectMessage) SetUnknown(protoreflect.RawFields) { + panic("invalid mutation of read-only message") +} +func (m reflectMessage) IsValid() bool { + invalid, _ := m[messageInvalidKey].(bool) + return !invalid +} +func (m reflectMessage) ProtoMethods() *protoiface.Methods { + return nil +} + +type reflectMessageType struct{ protoreflect.MessageDescriptor } + +func (t reflectMessageType) New() protoreflect.Message { + panic("not implemented") +} +func (t reflectMessageType) Zero() protoreflect.Message { + panic("not implemented") +} +func (t reflectMessageType) Descriptor() protoreflect.MessageDescriptor { + return t.MessageDescriptor +} + +type reflectList struct{ v reflect.Value } + +func (ls reflectList) Len() int { + if !ls.IsValid() { + return 0 + } + return ls.v.Len() +} +func (ls reflectList) Get(i int) protoreflect.Value { + return reflectValueOf(ls.v.Index(i).Interface()) +} +func (ls reflectList) Set(int, protoreflect.Value) { + panic("invalid mutation of read-only list") +} +func (ls reflectList) Append(protoreflect.Value) { + panic("invalid mutation of read-only list") +} +func (ls reflectList) AppendMutable() protoreflect.Value { + panic("invalid mutation of read-only list") +} +func (ls reflectList) Truncate(int) { + panic("invalid mutation of read-only list") +} +func (ls reflectList) NewElement() protoreflect.Value { + panic("not implemented") +} +func (ls reflectList) IsValid() bool { + return ls.v.IsValid() +} + +type reflectMap struct{ v reflect.Value } + +func (ms reflectMap) Len() int { + if !ms.IsValid() { + return 0 + } + return ms.v.Len() +} +func (ms reflectMap) Range(f func(protoreflect.MapKey, protoreflect.Value) bool) { + if !ms.IsValid() { + return + } + ks := ms.v.MapKeys() + for _, k := range ks { + pk := reflectValueOf(k.Interface()).MapKey() + pv := reflectValueOf(ms.v.MapIndex(k).Interface()) + if !f(pk, pv) { + return + } + } +} +func (ms reflectMap) Has(k protoreflect.MapKey) bool { + if !ms.IsValid() { + return false + } + return ms.v.MapIndex(reflect.ValueOf(k.Interface())).IsValid() +} +func (ms reflectMap) Clear(protoreflect.MapKey) { + panic("invalid mutation of read-only list") +} +func (ms reflectMap) Get(k protoreflect.MapKey) protoreflect.Value { + if !ms.IsValid() { + return protoreflect.Value{} + } + v := ms.v.MapIndex(reflect.ValueOf(k.Interface())) + if !v.IsValid() { + return protoreflect.Value{} + } + return reflectValueOf(v.Interface()) +} +func (ms reflectMap) Set(protoreflect.MapKey, protoreflect.Value) { + panic("invalid mutation of read-only list") +} +func (ms reflectMap) Mutable(k protoreflect.MapKey) protoreflect.Value { + panic("invalid mutation of read-only list") +} +func (ms reflectMap) NewValue() protoreflect.Value { + panic("not implemented") +} +func (ms reflectMap) IsValid() bool { + return ms.v.IsValid() +} diff --git a/vendor/google.golang.org/protobuf/testing/protocmp/util.go b/vendor/google.golang.org/protobuf/testing/protocmp/util.go new file mode 100644 index 0000000000..ee66ae9893 --- /dev/null +++ b/vendor/google.golang.org/protobuf/testing/protocmp/util.go @@ -0,0 +1,684 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package protocmp + +import ( + "bytes" + "fmt" + "math" + "reflect" + "strings" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" +) + +var ( + enumReflectType = reflect.TypeOf(Enum{}) + messageReflectType = reflect.TypeOf(Message{}) +) + +// FilterEnum filters opt to only be applicable on standalone Enums, +// singular fields of enums, list fields of enums, or map fields of enum values, +// where the enum is the same type as the specified enum. +// +// The Go type of the last path step may be an: +// - Enum for singular fields, elements of a repeated field, +// values of a map field, or standalone Enums +// - []Enum for list fields +// - map[K]Enum for map fields +// - interface{} for a Message map entry value +// +// This must be used in conjunction with Transform. +func FilterEnum(enum protoreflect.Enum, opt cmp.Option) cmp.Option { + return FilterDescriptor(enum.Descriptor(), opt) +} + +// FilterMessage filters opt to only be applicable on standalone Messages, +// singular fields of messages, list fields of messages, or map fields of +// message values, where the message is the same type as the specified message. +// +// The Go type of the last path step may be an: +// - Message for singular fields, elements of a repeated field, +// values of a map field, or standalone Messages +// - []Message for list fields +// - map[K]Message for map fields +// - interface{} for a Message map entry value +// +// This must be used in conjunction with Transform. +func FilterMessage(message proto.Message, opt cmp.Option) cmp.Option { + return FilterDescriptor(message.ProtoReflect().Descriptor(), opt) +} + +// FilterField filters opt to only be applicable on the specified field +// in the message. It panics if a field of the given name does not exist. +// +// The Go type of the last path step may be an: +// - T for singular fields +// - []T for list fields +// - map[K]T for map fields +// - interface{} for a Message map entry value +// +// This must be used in conjunction with Transform. +func FilterField(message proto.Message, name protoreflect.Name, opt cmp.Option) cmp.Option { + md := message.ProtoReflect().Descriptor() + return FilterDescriptor(mustFindFieldDescriptor(md, name), opt) +} + +// FilterOneof filters opt to only be applicable on all fields within the +// specified oneof in the message. It panics if a oneof of the given name +// does not exist. +// +// The Go type of the last path step may be an: +// - T for singular fields +// - []T for list fields +// - map[K]T for map fields +// - interface{} for a Message map entry value +// +// This must be used in conjunction with Transform. +func FilterOneof(message proto.Message, name protoreflect.Name, opt cmp.Option) cmp.Option { + md := message.ProtoReflect().Descriptor() + return FilterDescriptor(mustFindOneofDescriptor(md, name), opt) +} + +// FilterDescriptor ignores the specified descriptor. +// +// The following descriptor types may be specified: +// - protoreflect.EnumDescriptor +// - protoreflect.MessageDescriptor +// - protoreflect.FieldDescriptor +// - protoreflect.OneofDescriptor +// +// For the behavior of each, see the corresponding filter function. +// Since this filter accepts a protoreflect.FieldDescriptor, it can be used +// to also filter for extension fields as a protoreflect.ExtensionDescriptor +// is just an alias to protoreflect.FieldDescriptor. +// +// This must be used in conjunction with Transform. +func FilterDescriptor(desc protoreflect.Descriptor, opt cmp.Option) cmp.Option { + f := newNameFilters(desc) + return cmp.FilterPath(f.Filter, opt) +} + +// IgnoreEnums ignores all enums of the specified types. +// It is equivalent to FilterEnum(enum, cmp.Ignore()) for each enum. +// +// This must be used in conjunction with Transform. +func IgnoreEnums(enums ...protoreflect.Enum) cmp.Option { + var ds []protoreflect.Descriptor + for _, e := range enums { + ds = append(ds, e.Descriptor()) + } + return IgnoreDescriptors(ds...) +} + +// IgnoreMessages ignores all messages of the specified types. +// It is equivalent to FilterMessage(message, cmp.Ignore()) for each message. +// +// This must be used in conjunction with Transform. +func IgnoreMessages(messages ...proto.Message) cmp.Option { + var ds []protoreflect.Descriptor + for _, m := range messages { + ds = append(ds, m.ProtoReflect().Descriptor()) + } + return IgnoreDescriptors(ds...) +} + +// IgnoreFields ignores the specified fields in the specified message. +// It is equivalent to FilterField(message, name, cmp.Ignore()) for each field +// in the message. +// +// This must be used in conjunction with Transform. +func IgnoreFields(message proto.Message, names ...protoreflect.Name) cmp.Option { + var ds []protoreflect.Descriptor + md := message.ProtoReflect().Descriptor() + for _, s := range names { + ds = append(ds, mustFindFieldDescriptor(md, s)) + } + return IgnoreDescriptors(ds...) +} + +// IgnoreOneofs ignores fields of the specified oneofs in the specified message. +// It is equivalent to FilterOneof(message, name, cmp.Ignore()) for each oneof +// in the message. +// +// This must be used in conjunction with Transform. +func IgnoreOneofs(message proto.Message, names ...protoreflect.Name) cmp.Option { + var ds []protoreflect.Descriptor + md := message.ProtoReflect().Descriptor() + for _, s := range names { + ds = append(ds, mustFindOneofDescriptor(md, s)) + } + return IgnoreDescriptors(ds...) +} + +// IgnoreDescriptors ignores the specified set of descriptors. +// It is equivalent to FilterDescriptor(desc, cmp.Ignore()) for each descriptor. +// +// This must be used in conjunction with Transform. +func IgnoreDescriptors(descs ...protoreflect.Descriptor) cmp.Option { + return cmp.FilterPath(newNameFilters(descs...).Filter, cmp.Ignore()) +} + +func mustFindFieldDescriptor(md protoreflect.MessageDescriptor, s protoreflect.Name) protoreflect.FieldDescriptor { + d := findDescriptor(md, s) + if fd, ok := d.(protoreflect.FieldDescriptor); ok && fd.TextName() == string(s) { + return fd + } + + var suggestion string + switch d := d.(type) { + case protoreflect.FieldDescriptor: + suggestion = fmt.Sprintf("; consider specifying field %q instead", d.TextName()) + case protoreflect.OneofDescriptor: + suggestion = fmt.Sprintf("; consider specifying oneof %q with IgnoreOneofs instead", d.Name()) + } + panic(fmt.Sprintf("message %q has no field %q%s", md.FullName(), s, suggestion)) +} + +func mustFindOneofDescriptor(md protoreflect.MessageDescriptor, s protoreflect.Name) protoreflect.OneofDescriptor { + d := findDescriptor(md, s) + if od, ok := d.(protoreflect.OneofDescriptor); ok && d.Name() == s { + return od + } + + var suggestion string + switch d := d.(type) { + case protoreflect.OneofDescriptor: + suggestion = fmt.Sprintf("; consider specifying oneof %q instead", d.Name()) + case protoreflect.FieldDescriptor: + suggestion = fmt.Sprintf("; consider specifying field %q with IgnoreFields instead", d.TextName()) + } + panic(fmt.Sprintf("message %q has no oneof %q%s", md.FullName(), s, suggestion)) +} + +func findDescriptor(md protoreflect.MessageDescriptor, s protoreflect.Name) protoreflect.Descriptor { + // Exact match. + if fd := md.Fields().ByTextName(string(s)); fd != nil { + return fd + } + if od := md.Oneofs().ByName(s); od != nil && !od.IsSynthetic() { + return od + } + + // Best-effort match. + // + // It's a common user mistake to use the CamelCased field name as it appears + // in the generated Go struct. Instead of complaining that it doesn't exist, + // suggest the real protobuf name that the user may have desired. + normalize := func(s protoreflect.Name) string { + return strings.Replace(strings.ToLower(string(s)), "_", "", -1) + } + for i := 0; i < md.Fields().Len(); i++ { + if fd := md.Fields().Get(i); normalize(fd.Name()) == normalize(s) { + return fd + } + } + for i := 0; i < md.Oneofs().Len(); i++ { + if od := md.Oneofs().Get(i); normalize(od.Name()) == normalize(s) { + return od + } + } + return nil +} + +type nameFilters struct { + names map[protoreflect.FullName]bool +} + +func newNameFilters(descs ...protoreflect.Descriptor) *nameFilters { + f := &nameFilters{names: make(map[protoreflect.FullName]bool)} + for _, d := range descs { + switch d := d.(type) { + case protoreflect.EnumDescriptor: + f.names[d.FullName()] = true + case protoreflect.MessageDescriptor: + f.names[d.FullName()] = true + case protoreflect.FieldDescriptor: + f.names[d.FullName()] = true + case protoreflect.OneofDescriptor: + for i := 0; i < d.Fields().Len(); i++ { + f.names[d.Fields().Get(i).FullName()] = true + } + default: + panic("invalid descriptor type") + } + } + return f +} + +func (f *nameFilters) Filter(p cmp.Path) bool { + vx, vy := p.Last().Values() + return (f.filterValue(vx) && f.filterValue(vy)) || f.filterFields(p) +} + +func (f *nameFilters) filterFields(p cmp.Path) bool { + // Trim off trailing type-assertions so that the filter can match on the + // concrete value held within an interface value. + if _, ok := p.Last().(cmp.TypeAssertion); ok { + p = p[:len(p)-1] + } + + // Filter for Message maps. + mi, ok := p.Index(-1).(cmp.MapIndex) + if !ok { + return false + } + ps := p.Index(-2) + if ps.Type() != messageReflectType { + return false + } + + // Check field name. + vx, vy := ps.Values() + mx := vx.Interface().(Message) + my := vy.Interface().(Message) + k := mi.Key().String() + if f.filterFieldName(mx, k) && f.filterFieldName(my, k) { + return true + } + + // Check field value. + vx, vy = mi.Values() + if f.filterFieldValue(vx) && f.filterFieldValue(vy) { + return true + } + + return false +} + +func (f *nameFilters) filterFieldName(m Message, k string) bool { + if _, ok := m[k]; !ok { + return true // treat missing fields as already filtered + } + var fd protoreflect.FieldDescriptor + switch mm := m[messageTypeKey].(messageMeta); { + case protoreflect.Name(k).IsValid(): + fd = mm.md.Fields().ByTextName(k) + default: + fd = mm.xds[k] + } + if fd != nil { + return f.names[fd.FullName()] + } + return false +} + +func (f *nameFilters) filterFieldValue(v reflect.Value) bool { + if !v.IsValid() { + return true // implies missing slice element or map entry + } + v = v.Elem() // map entries are always populated values + switch t := v.Type(); { + case t == enumReflectType || t == messageReflectType: + // Check for singular message or enum field. + return f.filterValue(v) + case t.Kind() == reflect.Slice && (t.Elem() == enumReflectType || t.Elem() == messageReflectType): + // Check for list field of enum or message type. + return f.filterValue(v.Index(0)) + case t.Kind() == reflect.Map && (t.Elem() == enumReflectType || t.Elem() == messageReflectType): + // Check for map field of enum or message type. + return f.filterValue(v.MapIndex(v.MapKeys()[0])) + } + return false +} + +func (f *nameFilters) filterValue(v reflect.Value) bool { + if !v.IsValid() { + return true // implies missing slice element or map entry + } + if !v.CanInterface() { + return false // implies unexported struct field + } + switch v := v.Interface().(type) { + case Enum: + return v.Descriptor() != nil && f.names[v.Descriptor().FullName()] + case Message: + return v.Descriptor() != nil && f.names[v.Descriptor().FullName()] + } + return false +} + +// IgnoreDefaultScalars ignores singular scalars that are unpopulated or +// explicitly set to the default value. +// This option does not effect elements in a list or entries in a map. +// +// This must be used in conjunction with Transform. +func IgnoreDefaultScalars() cmp.Option { + return cmp.FilterPath(func(p cmp.Path) bool { + // Filter for Message maps. + mi, ok := p.Index(-1).(cmp.MapIndex) + if !ok { + return false + } + ps := p.Index(-2) + if ps.Type() != messageReflectType { + return false + } + + // Check whether both fields are default or unpopulated scalars. + vx, vy := ps.Values() + mx := vx.Interface().(Message) + my := vy.Interface().(Message) + k := mi.Key().String() + return isDefaultScalar(mx, k) && isDefaultScalar(my, k) + }, cmp.Ignore()) +} + +func isDefaultScalar(m Message, k string) bool { + if _, ok := m[k]; !ok { + return true + } + + var fd protoreflect.FieldDescriptor + switch mm := m[messageTypeKey].(messageMeta); { + case protoreflect.Name(k).IsValid(): + fd = mm.md.Fields().ByTextName(k) + default: + fd = mm.xds[k] + } + if fd == nil || !fd.Default().IsValid() { + return false + } + switch fd.Kind() { + case protoreflect.BytesKind: + v, ok := m[k].([]byte) + return ok && bytes.Equal(fd.Default().Bytes(), v) + case protoreflect.FloatKind: + v, ok := m[k].(float32) + return ok && equalFloat64(fd.Default().Float(), float64(v)) + case protoreflect.DoubleKind: + v, ok := m[k].(float64) + return ok && equalFloat64(fd.Default().Float(), float64(v)) + case protoreflect.EnumKind: + v, ok := m[k].(Enum) + return ok && fd.Default().Enum() == v.Number() + default: + return reflect.DeepEqual(fd.Default().Interface(), m[k]) + } +} + +func equalFloat64(x, y float64) bool { + return x == y || (math.IsNaN(x) && math.IsNaN(y)) +} + +// IgnoreEmptyMessages ignores messages that are empty or unpopulated. +// It applies to standalone Messages, singular message fields, +// list fields of messages, and map fields of message values. +// +// This must be used in conjunction with Transform. +func IgnoreEmptyMessages() cmp.Option { + return cmp.FilterPath(func(p cmp.Path) bool { + vx, vy := p.Last().Values() + return (isEmptyMessage(vx) && isEmptyMessage(vy)) || isEmptyMessageFields(p) + }, cmp.Ignore()) +} + +func isEmptyMessageFields(p cmp.Path) bool { + // Filter for Message maps. + mi, ok := p.Index(-1).(cmp.MapIndex) + if !ok { + return false + } + ps := p.Index(-2) + if ps.Type() != messageReflectType { + return false + } + + // Check field value. + vx, vy := mi.Values() + if isEmptyMessageFieldValue(vx) && isEmptyMessageFieldValue(vy) { + return true + } + + return false +} + +func isEmptyMessageFieldValue(v reflect.Value) bool { + if !v.IsValid() { + return true // implies missing slice element or map entry + } + v = v.Elem() // map entries are always populated values + switch t := v.Type(); { + case t == messageReflectType: + // Check singular field for empty message. + if !isEmptyMessage(v) { + return false + } + case t.Kind() == reflect.Slice && t.Elem() == messageReflectType: + // Check list field for all empty message elements. + for i := 0; i < v.Len(); i++ { + if !isEmptyMessage(v.Index(i)) { + return false + } + } + case t.Kind() == reflect.Map && t.Elem() == messageReflectType: + // Check map field for all empty message values. + for _, k := range v.MapKeys() { + if !isEmptyMessage(v.MapIndex(k)) { + return false + } + } + default: + return false + } + return true +} + +func isEmptyMessage(v reflect.Value) bool { + if !v.IsValid() { + return true // implies missing slice element or map entry + } + if !v.CanInterface() { + return false // implies unexported struct field + } + if m, ok := v.Interface().(Message); ok { + for k := range m { + if k != messageTypeKey && k != messageInvalidKey { + return false + } + } + return true + } + return false +} + +// IgnoreUnknown ignores unknown fields in all messages. +// +// This must be used in conjunction with Transform. +func IgnoreUnknown() cmp.Option { + return cmp.FilterPath(func(p cmp.Path) bool { + // Filter for Message maps. + mi, ok := p.Index(-1).(cmp.MapIndex) + if !ok { + return false + } + ps := p.Index(-2) + if ps.Type() != messageReflectType { + return false + } + + // Filter for unknown fields (which always have a numeric map key). + return strings.Trim(mi.Key().String(), "0123456789") == "" + }, cmp.Ignore()) +} + +// SortRepeated sorts repeated fields of the specified element type. +// The less function must be of the form "func(T, T) bool" where T is the +// Go element type for the repeated field kind. +// +// The element type T can be one of the following: +// - Go type for a protobuf scalar kind except for an enum +// (i.e., bool, int32, int64, uint32, uint64, float32, float64, string, and []byte) +// - E where E is a concrete enum type that implements protoreflect.Enum +// - M where M is a concrete message type that implement proto.Message +// +// This option only applies to repeated fields within a protobuf message. +// It does not operate on higher-order Go types that seem like a repeated field. +// For example, a []T outside the context of a protobuf message will not be +// handled by this option. To sort Go slices that are not repeated fields, +// consider using "github.com/google/go-cmp/cmp/cmpopts".SortSlices instead. +// +// This must be used in conjunction with Transform. +func SortRepeated(lessFunc interface{}) cmp.Option { + t, ok := checkTTBFunc(lessFunc) + if !ok { + panic(fmt.Sprintf("invalid less function: %T", lessFunc)) + } + + var opt cmp.Option + var sliceType reflect.Type + switch vf := reflect.ValueOf(lessFunc); { + case t.Implements(enumV2Type): + et := reflect.Zero(t).Interface().(protoreflect.Enum).Type() + lessFunc = func(x, y Enum) bool { + vx := reflect.ValueOf(et.New(x.Number())) + vy := reflect.ValueOf(et.New(y.Number())) + return vf.Call([]reflect.Value{vx, vy})[0].Bool() + } + opt = FilterDescriptor(et.Descriptor(), cmpopts.SortSlices(lessFunc)) + sliceType = reflect.SliceOf(enumReflectType) + case t.Implements(messageV2Type): + mt := reflect.Zero(t).Interface().(protoreflect.ProtoMessage).ProtoReflect().Type() + lessFunc = func(x, y Message) bool { + mx := mt.New().Interface() + my := mt.New().Interface() + proto.Merge(mx, x) + proto.Merge(my, y) + vx := reflect.ValueOf(mx) + vy := reflect.ValueOf(my) + return vf.Call([]reflect.Value{vx, vy})[0].Bool() + } + opt = FilterDescriptor(mt.Descriptor(), cmpopts.SortSlices(lessFunc)) + sliceType = reflect.SliceOf(messageReflectType) + default: + switch t { + case reflect.TypeOf(bool(false)): + case reflect.TypeOf(int32(0)): + case reflect.TypeOf(int64(0)): + case reflect.TypeOf(uint32(0)): + case reflect.TypeOf(uint64(0)): + case reflect.TypeOf(float32(0)): + case reflect.TypeOf(float64(0)): + case reflect.TypeOf(string("")): + case reflect.TypeOf([]byte(nil)): + default: + panic(fmt.Sprintf("invalid element type: %v", t)) + } + opt = cmpopts.SortSlices(lessFunc) + sliceType = reflect.SliceOf(t) + } + + return cmp.FilterPath(func(p cmp.Path) bool { + // Filter to only apply to repeated fields within a message. + if t := p.Index(-1).Type(); t == nil || t != sliceType { + return false + } + if t := p.Index(-2).Type(); t == nil || t.Kind() != reflect.Interface { + return false + } + if t := p.Index(-3).Type(); t == nil || t != messageReflectType { + return false + } + return true + }, opt) +} + +func checkTTBFunc(lessFunc interface{}) (reflect.Type, bool) { + switch t := reflect.TypeOf(lessFunc); { + case t == nil: + return nil, false + case t.NumIn() != 2 || t.In(0) != t.In(1) || t.IsVariadic(): + return nil, false + case t.NumOut() != 1 || t.Out(0) != reflect.TypeOf(false): + return nil, false + default: + return t.In(0), true + } +} + +// SortRepeatedFields sorts the specified repeated fields. +// Sorting a repeated field is useful for treating the list as a multiset +// (i.e., a set where each value can appear multiple times). +// It panics if the field does not exist or is not a repeated field. +// +// The sort ordering is as follows: +// - Booleans are sorted where false is sorted before true. +// - Integers are sorted in ascending order. +// - Floating-point numbers are sorted in ascending order according to +// the total ordering defined by IEEE-754 (section 5.10). +// - Strings and bytes are sorted lexicographically in ascending order. +// - Enums are sorted in ascending order based on its numeric value. +// - Messages are sorted according to some arbitrary ordering +// which is undefined and may change in future implementations. +// +// The ordering chosen for repeated messages is unlikely to be aesthetically +// preferred by humans. Consider using a custom sort function: +// +// FilterField(m, "foo_field", SortRepeated(func(x, y *foopb.MyMessage) bool { +// ... // user-provided definition for less +// })) +// +// This must be used in conjunction with Transform. +func SortRepeatedFields(message proto.Message, names ...protoreflect.Name) cmp.Option { + var opts cmp.Options + md := message.ProtoReflect().Descriptor() + for _, name := range names { + fd := mustFindFieldDescriptor(md, name) + if !fd.IsList() { + panic(fmt.Sprintf("message field %q is not repeated", fd.FullName())) + } + + var lessFunc interface{} + switch fd.Kind() { + case protoreflect.BoolKind: + lessFunc = func(x, y bool) bool { return !x && y } + case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: + lessFunc = func(x, y int32) bool { return x < y } + case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: + lessFunc = func(x, y int64) bool { return x < y } + case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: + lessFunc = func(x, y uint32) bool { return x < y } + case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: + lessFunc = func(x, y uint64) bool { return x < y } + case protoreflect.FloatKind: + lessFunc = lessF32 + case protoreflect.DoubleKind: + lessFunc = lessF64 + case protoreflect.StringKind: + lessFunc = func(x, y string) bool { return x < y } + case protoreflect.BytesKind: + lessFunc = func(x, y []byte) bool { return bytes.Compare(x, y) < 0 } + case protoreflect.EnumKind: + lessFunc = func(x, y Enum) bool { return x.Number() < y.Number() } + case protoreflect.MessageKind, protoreflect.GroupKind: + lessFunc = func(x, y Message) bool { return x.String() < y.String() } + default: + panic(fmt.Sprintf("invalid kind: %v", fd.Kind())) + } + opts = append(opts, FilterDescriptor(fd, cmpopts.SortSlices(lessFunc))) + } + return opts +} + +func lessF32(x, y float32) bool { + // Bit-wise implementation of IEEE-754, section 5.10. + xi := int32(math.Float32bits(x)) + yi := int32(math.Float32bits(y)) + xi ^= int32(uint32(xi>>31) >> 1) + yi ^= int32(uint32(yi>>31) >> 1) + return xi < yi +} +func lessF64(x, y float64) bool { + // Bit-wise implementation of IEEE-754, section 5.10. + xi := int64(math.Float64bits(x)) + yi := int64(math.Float64bits(y)) + xi ^= int64(uint64(xi>>63) >> 1) + yi ^= int64(uint64(yi>>63) >> 1) + return xi < yi +} diff --git a/vendor/google.golang.org/protobuf/testing/protocmp/xform.go b/vendor/google.golang.org/protobuf/testing/protocmp/xform.go new file mode 100644 index 0000000000..364a55ca76 --- /dev/null +++ b/vendor/google.golang.org/protobuf/testing/protocmp/xform.go @@ -0,0 +1,354 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package protocmp provides protobuf specific options for the +// "github.com/google/go-cmp/cmp" package. +// +// The primary feature is the Transform option, which transform proto.Message +// types into a Message map that is suitable for cmp to introspect upon. +// All other options in this package must be used in conjunction with Transform. +package protocmp + +import ( + "reflect" + "strconv" + + "github.com/google/go-cmp/cmp" + + "google.golang.org/protobuf/encoding/protowire" + "google.golang.org/protobuf/internal/genid" + "google.golang.org/protobuf/internal/msgfmt" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/runtime/protoiface" + "google.golang.org/protobuf/runtime/protoimpl" +) + +var ( + enumV2Type = reflect.TypeOf((*protoreflect.Enum)(nil)).Elem() + messageV1Type = reflect.TypeOf((*protoiface.MessageV1)(nil)).Elem() + messageV2Type = reflect.TypeOf((*proto.Message)(nil)).Elem() +) + +// Enum is a dynamic representation of a protocol buffer enum that is +// suitable for cmp.Equal and cmp.Diff to compare upon. +type Enum struct { + num protoreflect.EnumNumber + ed protoreflect.EnumDescriptor +} + +// Descriptor returns the enum descriptor. +// It returns nil for a zero Enum value. +func (e Enum) Descriptor() protoreflect.EnumDescriptor { + return e.ed +} + +// Number returns the enum value as an integer. +func (e Enum) Number() protoreflect.EnumNumber { + return e.num +} + +// Equal reports whether e1 and e2 represent the same enum value. +func (e1 Enum) Equal(e2 Enum) bool { + if e1.ed.FullName() != e2.ed.FullName() { + return false + } + return e1.num == e2.num +} + +// String returns the name of the enum value if known (e.g., "ENUM_VALUE"), +// otherwise it returns the formatted decimal enum number (e.g., "14"). +func (e Enum) String() string { + if ev := e.ed.Values().ByNumber(e.num); ev != nil { + return string(ev.Name()) + } + return strconv.Itoa(int(e.num)) +} + +const ( + // messageTypeKey indicates the protobuf message type. + // The value type is always messageMeta. + // From the public API, it presents itself as only the type, but the + // underlying data structure holds arbitrary metadata about the message. + messageTypeKey = "@type" + + // messageInvalidKey indicates that the message is invalid. + // The value is always the boolean "true". + messageInvalidKey = "@invalid" +) + +type messageMeta struct { + m proto.Message + md protoreflect.MessageDescriptor + xds map[string]protoreflect.ExtensionDescriptor +} + +func (t messageMeta) String() string { + return string(t.md.FullName()) +} + +func (t1 messageMeta) Equal(t2 messageMeta) bool { + return t1.md.FullName() == t2.md.FullName() +} + +// Message is a dynamic representation of a protocol buffer message that is +// suitable for cmp.Equal and cmp.Diff to directly operate upon. +// +// Every populated known field (excluding extension fields) is stored in the map +// with the key being the short name of the field (e.g., "field_name") and +// the value determined by the kind and cardinality of the field. +// +// Singular scalars are represented by the same Go type as protoreflect.Value, +// singular messages are represented by the Message type, +// singular enums are represented by the Enum type, +// list fields are represented as a Go slice, and +// map fields are represented as a Go map. +// +// Every populated extension field is stored in the map with the key being the +// full name of the field surrounded by brackets (e.g., "[extension.full.name]") +// and the value determined according to the same rules as known fields. +// +// Every unknown field is stored in the map with the key being the field number +// encoded as a decimal string (e.g., "132") and the value being the raw bytes +// of the encoded field (as the protoreflect.RawFields type). +// +// Message values must not be created by or mutated by users. +type Message map[string]interface{} + +// Unwrap returns the original message value. +// It returns nil if this Message was not constructed from another message. +func (m Message) Unwrap() proto.Message { + mm, _ := m[messageTypeKey].(messageMeta) + return mm.m +} + +// Descriptor return the message descriptor. +// It returns nil for a zero Message value. +func (m Message) Descriptor() protoreflect.MessageDescriptor { + mm, _ := m[messageTypeKey].(messageMeta) + return mm.md +} + +// ProtoReflect returns a reflective view of m. +// It only implements the read-only operations of protoreflect.Message. +// Calling any mutating operations on m panics. +func (m Message) ProtoReflect() protoreflect.Message { + return (reflectMessage)(m) +} + +// ProtoMessage is a marker method from the legacy message interface. +func (m Message) ProtoMessage() {} + +// Reset is the required Reset method from the legacy message interface. +func (m Message) Reset() { + panic("invalid mutation of a read-only message") +} + +// String returns a formatted string for the message. +// It is intended for human debugging and has no guarantees about its +// exact format or the stability of its output. +func (m Message) String() string { + switch { + case m == nil: + return "" + case !m.ProtoReflect().IsValid(): + return "" + default: + return msgfmt.Format(m) + } +} + +type option struct{} + +// Transform returns a cmp.Option that converts each proto.Message to a Message. +// The transformation does not mutate nor alias any converted messages. +// +// The google.protobuf.Any message is automatically unmarshaled such that the +// "value" field is a Message representing the underlying message value +// assuming it could be resolved and properly unmarshaled. +// +// This does not directly transform higher-order composite Go types. +// For example, []*foopb.Message is not transformed into []Message, +// but rather the individual message elements of the slice are transformed. +// +// Note that there are currently no custom options for Transform, +// but the use of an unexported type keeps the future open. +func Transform(...option) cmp.Option { + // addrType returns a pointer to t if t isn't a pointer or interface. + addrType := func(t reflect.Type) reflect.Type { + if k := t.Kind(); k == reflect.Interface || k == reflect.Ptr { + return t + } + return reflect.PtrTo(t) + } + + // TODO: Should this transform protoreflect.Enum types to Enum as well? + return cmp.FilterPath(func(p cmp.Path) bool { + ps := p.Last() + if isMessageType(addrType(ps.Type())) { + return true + } + + // Check whether the concrete values of an interface both satisfy + // the Message interface. + if ps.Type().Kind() == reflect.Interface { + vx, vy := ps.Values() + if !vx.IsValid() || vx.IsNil() || !vy.IsValid() || vy.IsNil() { + return false + } + return isMessageType(addrType(vx.Elem().Type())) && isMessageType(addrType(vy.Elem().Type())) + } + + return false + }, cmp.Transformer("protocmp.Transform", func(v interface{}) Message { + // For user convenience, shallow copy the message value if necessary + // in order for it to implement the message interface. + if rv := reflect.ValueOf(v); rv.IsValid() && rv.Kind() != reflect.Ptr && !isMessageType(rv.Type()) { + pv := reflect.New(rv.Type()) + pv.Elem().Set(rv) + v = pv.Interface() + } + + m := protoimpl.X.MessageOf(v) + switch { + case m == nil: + return nil + case !m.IsValid(): + return Message{messageTypeKey: messageMeta{m: m.Interface(), md: m.Descriptor()}, messageInvalidKey: true} + default: + return transformMessage(m) + } + })) +} + +func isMessageType(t reflect.Type) bool { + // Avoid transforming the Message itself. + if t == reflect.TypeOf(Message(nil)) || t == reflect.TypeOf((*Message)(nil)) { + return false + } + return t.Implements(messageV1Type) || t.Implements(messageV2Type) +} + +func transformMessage(m protoreflect.Message) Message { + mx := Message{} + mt := messageMeta{m: m.Interface(), md: m.Descriptor(), xds: make(map[string]protoreflect.FieldDescriptor)} + + // Handle known and extension fields. + m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { + s := fd.TextName() + if fd.IsExtension() { + mt.xds[s] = fd + } + switch { + case fd.IsList(): + mx[s] = transformList(fd, v.List()) + case fd.IsMap(): + mx[s] = transformMap(fd, v.Map()) + default: + mx[s] = transformSingular(fd, v) + } + return true + }) + + // Handle unknown fields. + for b := m.GetUnknown(); len(b) > 0; { + num, _, n := protowire.ConsumeField(b) + s := strconv.Itoa(int(num)) + b2, _ := mx[s].(protoreflect.RawFields) + mx[s] = append(b2, b[:n]...) + b = b[n:] + } + + // Expand Any messages. + if mt.md.FullName() == genid.Any_message_fullname { + // TODO: Expose Transform option to specify a custom resolver? + s, _ := mx[string(genid.Any_TypeUrl_field_name)].(string) + b, _ := mx[string(genid.Any_Value_field_name)].([]byte) + mt, err := protoregistry.GlobalTypes.FindMessageByURL(s) + if mt != nil && err == nil { + m2 := mt.New() + err := proto.UnmarshalOptions{AllowPartial: true}.Unmarshal(b, m2.Interface()) + if err == nil { + mx[string(genid.Any_Value_field_name)] = transformMessage(m2) + } + } + } + + mx[messageTypeKey] = mt + return mx +} + +func transformList(fd protoreflect.FieldDescriptor, lv protoreflect.List) interface{} { + t := protoKindToGoType(fd.Kind()) + rv := reflect.MakeSlice(reflect.SliceOf(t), lv.Len(), lv.Len()) + for i := 0; i < lv.Len(); i++ { + v := reflect.ValueOf(transformSingular(fd, lv.Get(i))) + rv.Index(i).Set(v) + } + return rv.Interface() +} + +func transformMap(fd protoreflect.FieldDescriptor, mv protoreflect.Map) interface{} { + kfd := fd.MapKey() + vfd := fd.MapValue() + kt := protoKindToGoType(kfd.Kind()) + vt := protoKindToGoType(vfd.Kind()) + rv := reflect.MakeMapWithSize(reflect.MapOf(kt, vt), mv.Len()) + mv.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { + kv := reflect.ValueOf(transformSingular(kfd, k.Value())) + vv := reflect.ValueOf(transformSingular(vfd, v)) + rv.SetMapIndex(kv, vv) + return true + }) + return rv.Interface() +} + +func transformSingular(fd protoreflect.FieldDescriptor, v protoreflect.Value) interface{} { + switch fd.Kind() { + case protoreflect.EnumKind: + return Enum{num: v.Enum(), ed: fd.Enum()} + case protoreflect.MessageKind, protoreflect.GroupKind: + return transformMessage(v.Message()) + case protoreflect.BytesKind: + // The protoreflect API does not specify whether an empty bytes is + // guaranteed to be nil or not. Always return non-nil bytes to avoid + // leaking information about the concrete proto.Message implementation. + if len(v.Bytes()) == 0 { + return []byte{} + } + return v.Bytes() + default: + return v.Interface() + } +} + +func protoKindToGoType(k protoreflect.Kind) reflect.Type { + switch k { + case protoreflect.BoolKind: + return reflect.TypeOf(bool(false)) + case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: + return reflect.TypeOf(int32(0)) + case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: + return reflect.TypeOf(int64(0)) + case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: + return reflect.TypeOf(uint32(0)) + case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: + return reflect.TypeOf(uint64(0)) + case protoreflect.FloatKind: + return reflect.TypeOf(float32(0)) + case protoreflect.DoubleKind: + return reflect.TypeOf(float64(0)) + case protoreflect.StringKind: + return reflect.TypeOf(string("")) + case protoreflect.BytesKind: + return reflect.TypeOf([]byte(nil)) + case protoreflect.EnumKind: + return reflect.TypeOf(Enum{}) + case protoreflect.MessageKind, protoreflect.GroupKind: + return reflect.TypeOf(Message{}) + default: + panic("invalid kind") + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 47f7be46ed..21c6e3d109 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -157,6 +157,7 @@ github.com/google/gnostic/openapiv3 # github.com/google/go-cmp v0.5.9 ## explicit; go 1.13 github.com/google/go-cmp/cmp +github.com/google/go-cmp/cmp/cmpopts github.com/google/go-cmp/cmp/internal/diff github.com/google/go-cmp/cmp/internal/flags github.com/google/go-cmp/cmp/internal/function @@ -244,6 +245,9 @@ github.com/mattbaird/jsonpatch # github.com/matttproud/golang_protobuf_extensions v1.0.4 ## explicit; go 1.9 github.com/matttproud/golang_protobuf_extensions/pbutil +# github.com/mennanov/fmutils v0.2.0 +## explicit; go 1.15 +github.com/mennanov/fmutils # github.com/mitchellh/mapstructure v1.4.1 ## explicit; go 1.14 github.com/mitchellh/mapstructure @@ -534,6 +538,7 @@ google.golang.org/protobuf/internal/filetype google.golang.org/protobuf/internal/flags google.golang.org/protobuf/internal/genid google.golang.org/protobuf/internal/impl +google.golang.org/protobuf/internal/msgfmt google.golang.org/protobuf/internal/order google.golang.org/protobuf/internal/pragma google.golang.org/protobuf/internal/set @@ -545,6 +550,7 @@ google.golang.org/protobuf/reflect/protoreflect google.golang.org/protobuf/reflect/protoregistry google.golang.org/protobuf/runtime/protoiface google.golang.org/protobuf/runtime/protoimpl +google.golang.org/protobuf/testing/protocmp google.golang.org/protobuf/types/descriptorpb google.golang.org/protobuf/types/known/anypb google.golang.org/protobuf/types/known/durationpb