From 1003f9335e2e7efaf6c281d1f13d160872e97ebe Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Wed, 20 Feb 2019 12:49:30 +0000 Subject: [PATCH] New options in PinOptions This starts handling Metadata and UserAllocations in the PinOptions object. The Pin protobuf has been modified to embed a PinOptions message which is defined separately. Query arguments for the Metadata map are declared by their "meta-" prefix: "?meta-something=something&meta-something-else-b". Additional tests have been added, along with an Equals() method for PinOptions. License: MIT Signed-off-by: Hector Sanjuan --- api/pb/types.pb.go | 135 +++++++++++++++++++++++++++++++-------------- api/pb/types.proto | 16 ++++-- api/types.go | 117 ++++++++++++++++++++++++++++++--------- api/types_test.go | 51 +++++++++++++++++ 4 files changed, 246 insertions(+), 73 deletions(-) diff --git a/api/pb/types.pb.go b/api/pb/types.pb.go index 285b274ec..b488b94ca 100644 --- a/api/pb/types.pb.go +++ b/api/pb/types.pb.go @@ -55,19 +55,15 @@ func (Pin_PinType) EnumDescriptor() ([]byte, []int) { } type Pin struct { - Cid []byte `protobuf:"bytes,1,opt,name=Cid,proto3" json:"Cid,omitempty"` - Type Pin_PinType `protobuf:"varint,2,opt,name=Type,proto3,enum=api.pb.Pin_PinType" json:"Type,omitempty"` - Allocations [][]byte `protobuf:"bytes,3,rep,name=Allocations,proto3" json:"Allocations,omitempty"` - MaxDepth int32 `protobuf:"zigzag32,4,opt,name=MaxDepth,proto3" json:"MaxDepth,omitempty"` - Reference []byte `protobuf:"bytes,5,opt,name=Reference,proto3" json:"Reference,omitempty"` - ReplicationFactorMin int32 `protobuf:"zigzag32,8,opt,name=ReplicationFactorMin,proto3" json:"ReplicationFactorMin,omitempty"` - ReplicationFactorMax int32 `protobuf:"zigzag32,9,opt,name=ReplicationFactorMax,proto3" json:"ReplicationFactorMax,omitempty"` - Name string `protobuf:"bytes,10,opt,name=Name,proto3" json:"Name,omitempty"` - ShardSize uint64 `protobuf:"varint,11,opt,name=ShardSize,proto3" json:"ShardSize,omitempty"` - Metadata map[string]string `protobuf:"bytes,12,rep,name=Metadata,proto3" json:"Metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Cid []byte `protobuf:"bytes,1,opt,name=Cid,proto3" json:"Cid,omitempty"` + Type Pin_PinType `protobuf:"varint,2,opt,name=Type,proto3,enum=api.pb.Pin_PinType" json:"Type,omitempty"` + Allocations [][]byte `protobuf:"bytes,3,rep,name=Allocations,proto3" json:"Allocations,omitempty"` + MaxDepth int32 `protobuf:"zigzag32,4,opt,name=MaxDepth,proto3" json:"MaxDepth,omitempty"` + Reference []byte `protobuf:"bytes,5,opt,name=Reference,proto3" json:"Reference,omitempty"` + Options *PinOptions `protobuf:"bytes,6,opt,name=Options,proto3" json:"Options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Pin) Reset() { *m = Pin{} } @@ -130,35 +126,86 @@ func (m *Pin) GetReference() []byte { return nil } -func (m *Pin) GetReplicationFactorMin() int32 { +func (m *Pin) GetOptions() *PinOptions { + if m != nil { + return m.Options + } + return nil +} + +type PinOptions struct { + ReplicationFactorMin int32 `protobuf:"zigzag32,1,opt,name=ReplicationFactorMin,proto3" json:"ReplicationFactorMin,omitempty"` + ReplicationFactorMax int32 `protobuf:"zigzag32,2,opt,name=ReplicationFactorMax,proto3" json:"ReplicationFactorMax,omitempty"` + Name string `protobuf:"bytes,3,opt,name=Name,proto3" json:"Name,omitempty"` + ShardSize uint64 `protobuf:"varint,4,opt,name=ShardSize,proto3" json:"ShardSize,omitempty"` + UserAllocations []string `protobuf:"bytes,5,rep,name=UserAllocations,proto3" json:"UserAllocations,omitempty"` + Metadata map[string]string `protobuf:"bytes,6,rep,name=Metadata,proto3" json:"Metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PinOptions) Reset() { *m = PinOptions{} } +func (m *PinOptions) String() string { return proto.CompactTextString(m) } +func (*PinOptions) ProtoMessage() {} +func (*PinOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_d938547f84707355, []int{1} +} + +func (m *PinOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PinOptions.Unmarshal(m, b) +} +func (m *PinOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PinOptions.Marshal(b, m, deterministic) +} +func (m *PinOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_PinOptions.Merge(m, src) +} +func (m *PinOptions) XXX_Size() int { + return xxx_messageInfo_PinOptions.Size(m) +} +func (m *PinOptions) XXX_DiscardUnknown() { + xxx_messageInfo_PinOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_PinOptions proto.InternalMessageInfo + +func (m *PinOptions) GetReplicationFactorMin() int32 { if m != nil { return m.ReplicationFactorMin } return 0 } -func (m *Pin) GetReplicationFactorMax() int32 { +func (m *PinOptions) GetReplicationFactorMax() int32 { if m != nil { return m.ReplicationFactorMax } return 0 } -func (m *Pin) GetName() string { +func (m *PinOptions) GetName() string { if m != nil { return m.Name } return "" } -func (m *Pin) GetShardSize() uint64 { +func (m *PinOptions) GetShardSize() uint64 { if m != nil { return m.ShardSize } return 0 } -func (m *Pin) GetMetadata() map[string]string { +func (m *PinOptions) GetUserAllocations() []string { + if m != nil { + return m.UserAllocations + } + return nil +} + +func (m *PinOptions) GetMetadata() map[string]string { if m != nil { return m.Metadata } @@ -168,33 +215,37 @@ func (m *Pin) GetMetadata() map[string]string { func init() { proto.RegisterEnum("api.pb.Pin_PinType", Pin_PinType_name, Pin_PinType_value) proto.RegisterType((*Pin)(nil), "api.pb.Pin") - proto.RegisterMapType((map[string]string)(nil), "api.pb.Pin.MetadataEntry") + proto.RegisterType((*PinOptions)(nil), "api.pb.PinOptions") + proto.RegisterMapType((map[string]string)(nil), "api.pb.PinOptions.MetadataEntry") } func init() { proto.RegisterFile("types.proto", fileDescriptor_d938547f84707355) } var fileDescriptor_d938547f84707355 = []byte{ - // 352 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0x4d, 0x0f, 0xd2, 0x30, - 0x18, 0xb6, 0xac, 0xb0, 0xed, 0xdd, 0x20, 0xf3, 0x95, 0x43, 0x25, 0x1e, 0x1a, 0x2e, 0xee, 0xb4, - 0x03, 0xc6, 0xc4, 0xe8, 0x09, 0x41, 0x4d, 0x48, 0x30, 0xa4, 0xe8, 0x0f, 0x28, 0xac, 0x86, 0xc6, - 0xb9, 0x2d, 0xa3, 0x18, 0xe6, 0x7f, 0xf0, 0x3f, 0x9b, 0x76, 0x08, 0x98, 0xe0, 0xa1, 0xc9, 0xf3, - 0xf1, 0x7e, 0xb4, 0x4f, 0x0a, 0x91, 0x69, 0x6b, 0x75, 0xcc, 0xea, 0xa6, 0x32, 0x15, 0x0e, 0x64, - 0xad, 0xb3, 0x7a, 0x37, 0xfd, 0x4d, 0xc1, 0xdb, 0xe8, 0x12, 0x13, 0xf0, 0x16, 0x3a, 0x67, 0x84, - 0x93, 0x34, 0x16, 0x16, 0xe2, 0x4b, 0xa0, 0x5f, 0xda, 0x5a, 0xb1, 0x1e, 0x27, 0xe9, 0x68, 0xf6, - 0x2c, 0xeb, 0x1a, 0xb2, 0x8d, 0x2e, 0xed, 0xb1, 0x96, 0x70, 0x05, 0xc8, 0x21, 0x9a, 0x17, 0x45, - 0xb5, 0x97, 0x46, 0x57, 0xe5, 0x91, 0x79, 0xdc, 0x4b, 0x63, 0x71, 0x2f, 0xe1, 0x04, 0x82, 0xb5, - 0x3c, 0x2f, 0x55, 0x6d, 0x0e, 0x8c, 0x72, 0x92, 0x3e, 0x15, 0x57, 0x8e, 0x2f, 0x20, 0x14, 0xea, - 0x9b, 0x6a, 0x54, 0xb9, 0x57, 0xac, 0xef, 0xd6, 0xdf, 0x04, 0x9c, 0xc1, 0x58, 0xa8, 0xba, 0xd0, - 0xdd, 0xa4, 0x8f, 0x72, 0x6f, 0xaa, 0x66, 0xad, 0x4b, 0x16, 0xb8, 0x29, 0x0f, 0xbd, 0xc7, 0x3d, - 0xf2, 0xcc, 0xc2, 0xff, 0xf5, 0xc8, 0x33, 0x22, 0xd0, 0xcf, 0xf2, 0x87, 0x62, 0xc0, 0x49, 0x1a, - 0x0a, 0x87, 0xed, 0xcd, 0xb6, 0x07, 0xd9, 0xe4, 0x5b, 0xfd, 0x4b, 0xb1, 0x88, 0x93, 0x94, 0x8a, - 0x9b, 0x80, 0xaf, 0x21, 0x58, 0x2b, 0x23, 0x73, 0x69, 0x24, 0x8b, 0xb9, 0x97, 0x46, 0xb3, 0xe7, - 0xf7, 0x11, 0xfd, 0xf5, 0x3e, 0x94, 0xa6, 0x69, 0xc5, 0xb5, 0x74, 0xf2, 0x0e, 0x86, 0xff, 0x58, - 0x36, 0xf8, 0xef, 0xaa, 0x75, 0xc1, 0x87, 0xc2, 0x42, 0x1c, 0x43, 0xff, 0xa7, 0x2c, 0x4e, 0x5d, - 0xf2, 0xa1, 0xe8, 0xc8, 0xdb, 0xde, 0x1b, 0x32, 0xfd, 0x0a, 0xfe, 0x25, 0x7a, 0x8c, 0xc0, 0x7f, - 0x2f, 0x73, 0x0b, 0x93, 0x27, 0x18, 0x43, 0xb0, 0x94, 0x46, 0x3a, 0x46, 0x2c, 0xb3, 0x2b, 0x1c, - 0xeb, 0x21, 0xc2, 0x68, 0x51, 0x9c, 0x8e, 0x46, 0x35, 0xcb, 0xf9, 0x27, 0xa7, 0x79, 0x38, 0xbc, - 0xbc, 0xcc, 0x51, 0xba, 0xa2, 0xc1, 0x20, 0xf1, 0x57, 0x34, 0xf0, 0x93, 0x60, 0x37, 0x70, 0xdf, - 0xe3, 0xd5, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x63, 0x03, 0x06, 0x76, 0x2d, 0x02, 0x00, 0x00, + // 386 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x52, 0x4d, 0x8f, 0xd3, 0x30, + 0x10, 0xc5, 0x71, 0x9a, 0x6e, 0x26, 0xdd, 0x65, 0x77, 0xd8, 0x43, 0xb4, 0xe2, 0x60, 0xf5, 0x82, + 0x0f, 0x28, 0x87, 0x70, 0x41, 0xc0, 0xa5, 0xb4, 0xc0, 0xa9, 0x50, 0xb9, 0xf4, 0x07, 0xb8, 0xad, + 0x51, 0x2d, 0x42, 0x62, 0x25, 0x2e, 0x6a, 0xf8, 0x37, 0xfc, 0x1e, 0xfe, 0x14, 0xb2, 0xd3, 0x2f, + 0xd8, 0x1e, 0x22, 0xcd, 0x7b, 0x6f, 0x5e, 0x66, 0xf2, 0x32, 0x90, 0xd8, 0xd6, 0xa8, 0x26, 0x33, + 0x75, 0x65, 0x2b, 0x8c, 0xa4, 0xd1, 0x99, 0x59, 0x0e, 0x7f, 0x07, 0x40, 0x67, 0xba, 0xc4, 0x5b, + 0xa0, 0x63, 0xbd, 0x4e, 0x09, 0x23, 0x7c, 0x20, 0x5c, 0x89, 0x2f, 0x20, 0xfc, 0xda, 0x1a, 0x95, + 0x06, 0x8c, 0xf0, 0x9b, 0xfc, 0x59, 0xd6, 0x19, 0xb2, 0x99, 0x2e, 0xdd, 0xe3, 0x24, 0xe1, 0x1b, + 0x90, 0x41, 0x32, 0x2a, 0x8a, 0x6a, 0x25, 0xad, 0xae, 0xca, 0x26, 0xa5, 0x8c, 0xf2, 0x81, 0x38, + 0xa7, 0xf0, 0x01, 0xae, 0xa6, 0x72, 0x37, 0x51, 0xc6, 0x6e, 0xd2, 0x90, 0x11, 0x7e, 0x27, 0x8e, + 0x18, 0x9f, 0x43, 0x2c, 0xd4, 0x37, 0x55, 0xab, 0x72, 0xa5, 0xd2, 0x9e, 0x1f, 0x7f, 0x22, 0xf0, + 0x25, 0xf4, 0xbf, 0x98, 0xee, 0xbd, 0x11, 0x23, 0x3c, 0xc9, 0xf1, 0x6c, 0x8f, 0xbd, 0x22, 0x0e, + 0x2d, 0xc3, 0x05, 0xf4, 0xf7, 0xab, 0x61, 0x02, 0xfd, 0xf7, 0x72, 0xed, 0xca, 0xdb, 0x27, 0x38, + 0x80, 0xab, 0x89, 0xb4, 0xd2, 0x23, 0xe2, 0xd0, 0x54, 0xed, 0x51, 0x80, 0x08, 0x37, 0xe3, 0x62, + 0xdb, 0x58, 0x55, 0x4f, 0x46, 0x9f, 0x3c, 0x47, 0xf1, 0x1a, 0xe2, 0xf9, 0x46, 0xd6, 0x9d, 0x3d, + 0x1c, 0xfe, 0x09, 0x00, 0x4e, 0xe3, 0x30, 0x87, 0x7b, 0xa1, 0x4c, 0xa1, 0xbb, 0xaf, 0xfb, 0x28, + 0x57, 0xb6, 0xaa, 0xa7, 0xba, 0xf4, 0xd9, 0xdd, 0x89, 0x8b, 0xda, 0x65, 0x8f, 0xdc, 0xf9, 0x70, + 0x2f, 0x7a, 0xe4, 0x0e, 0x11, 0xc2, 0xcf, 0xf2, 0x87, 0x4a, 0x29, 0x23, 0x3c, 0x16, 0xbe, 0x76, + 0x69, 0xf9, 0xcd, 0xe6, 0xfa, 0x97, 0xf2, 0x51, 0x86, 0xe2, 0x44, 0x20, 0x87, 0xa7, 0x8b, 0x46, + 0xd5, 0xe7, 0x7f, 0xa3, 0xc7, 0x28, 0x8f, 0xc5, 0xff, 0x34, 0xbe, 0xeb, 0x32, 0x58, 0x4b, 0x2b, + 0xd3, 0x88, 0x51, 0x9e, 0xe4, 0xec, 0x71, 0xb0, 0xd9, 0xa1, 0xe5, 0x43, 0x69, 0xeb, 0x56, 0x1c, + 0x1d, 0x0f, 0x6f, 0xe1, 0xfa, 0x1f, 0xc9, 0x5d, 0xcf, 0x77, 0xd5, 0xfa, 0x04, 0x62, 0xe1, 0x4a, + 0xbc, 0x87, 0xde, 0x4f, 0x59, 0x6c, 0xbb, 0xf3, 0x89, 0x45, 0x07, 0xde, 0x04, 0xaf, 0xc9, 0x32, + 0xf2, 0x07, 0xf8, 0xea, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xda, 0x40, 0x89, 0x72, 0x8f, 0x02, + 0x00, 0x00, } diff --git a/api/pb/types.proto b/api/pb/types.proto index 81326c22e..3b35c2493 100644 --- a/api/pb/types.proto +++ b/api/pb/types.proto @@ -14,10 +14,14 @@ message Pin { repeated bytes Allocations = 3; sint32 MaxDepth = 4; bytes Reference = 5; - reserved 6,7; - sint32 ReplicationFactorMin = 8; - sint32 ReplicationFactorMax = 9; - string Name = 10; - uint64 ShardSize = 11; - map Metadata = 12; + PinOptions Options = 6; +} + +message PinOptions { + sint32 ReplicationFactorMin = 1; + sint32 ReplicationFactorMax = 2; + string Name = 3; + uint64 ShardSize = 4; + repeated string UserAllocations = 5; + map Metadata = 6; } \ No newline at end of file diff --git a/api/types.go b/api/types.go index 127e08f71..66f7161d6 100644 --- a/api/types.go +++ b/api/types.go @@ -707,21 +707,72 @@ func (pT PinType) String() string { } } +var pinOptionsMetaPrefix = "meta-" + // PinOptions wraps user-defined options for Pins type PinOptions struct { ReplicationFactorMin int `json:"replication_factor_min" codec:"rn,omitempty"` ReplicationFactorMax int `json:"replication_factor_max" codec:"rx,omitempty"` Name string `json:"name" codec:"n,omitempty"` ShardSize uint64 `json:"shard_size" codec:"s,omitempty"` + UserAllocations []string `json:"user_allocations" codec:"ua,omitempty"` Metadata map[string]string `json:"metadata" codec:"m,omitempty"` } +// Equals returns true of two PinOption objects are equivalent. +func (po *PinOptions) Equals(po2 *PinOptions) bool { + if po.ReplicationFactorMax != po2.ReplicationFactorMax { + return false + } + + if po.ReplicationFactorMin != po2.ReplicationFactorMin { + return false + } + + if po.ShardSize != po2.ShardSize { + return false + } + + lenAllocs1 := len(po.UserAllocations) + lenAllocs2 := len(po2.UserAllocations) + if lenAllocs1 != lenAllocs2 { + return false + } + + // avoid side effecs in the original objects + allocs1 := make([]string, lenAllocs1, lenAllocs1) + allocs2 := make([]string, lenAllocs2, lenAllocs2) + copy(allocs1, po.UserAllocations) + copy(allocs2, po2.UserAllocations) + sort.Strings(allocs1) + sort.Strings(allocs2) + if strings.Join(allocs1, ",") != strings.Join(allocs2, ",") { + return false + } + + for k, v := range po.Metadata { + v2 := po2.Metadata[k] + if k != "" && v != v2 { + return false + } + } + return true +} + // ToQuery returns the PinOption as query arguments. func (po *PinOptions) ToQuery() string { q := url.Values{} q.Set("replication-min", fmt.Sprintf("%d", po.ReplicationFactorMin)) q.Set("replication-max", fmt.Sprintf("%d", po.ReplicationFactorMax)) q.Set("name", po.Name) + q.Set("shard-size", fmt.Sprintf("%d", po.ShardSize)) + q.Set("user-allocations", strings.Join(po.UserAllocations, ",")) + for k, v := range po.Metadata { + if k == "" { + continue + } + q.Set(fmt.Sprintf("%s%s", pinOptionsMetaPrefix, k), v) + } return q.Encode() } @@ -750,6 +801,26 @@ func (po *PinOptions) FromQuery(q url.Values) { if rpl, err := strconv.Atoi(rplStrMax); err == nil { po.ReplicationFactorMax = rpl } + + if shsize, err := strconv.ParseUint(q.Get("shard-size"), 10, 64); err == nil { + po.ShardSize = shsize + } + + if allocs := q.Get("user-allocations"); allocs != "" { + po.UserAllocations = strings.Split(allocs, ",") + } + + po.Metadata = make(map[string]string) + for k := range q { + if !strings.HasPrefix(k, pinOptionsMetaPrefix) { + continue + } + metaKey := strings.TrimPrefix(k, pinOptionsMetaPrefix) + if metaKey == "" { + continue + } + po.Metadata[metaKey] = q.Get(k) + } } // Pin carries all the information associated to a CID that is pinned @@ -858,18 +929,23 @@ func (pin *Pin) ProtoMarshal() ([]byte, error) { allocs[i] = bs } - pbPin := &pb.Pin{ - Cid: pin.Cid.Bytes(), - Type: convertPinType(pin.Type), - Allocations: allocs, - MaxDepth: int32(pin.MaxDepth), - Reference: pin.Reference.Bytes(), + opts := &pb.PinOptions{ ReplicationFactorMin: int32(pin.ReplicationFactorMin), ReplicationFactorMax: int32(pin.ReplicationFactorMax), Name: pin.Name, ShardSize: pin.ShardSize, + UserAllocations: pin.UserAllocations, Metadata: pin.Metadata, } + + pbPin := &pb.Pin{ + Cid: pin.Cid.Bytes(), + Type: convertPinType(pin.Type), + Allocations: allocs, + MaxDepth: int32(pin.MaxDepth), + Reference: pin.Reference.Bytes(), + Options: opts, + } return proto.Marshal(pbPin) } @@ -910,11 +986,14 @@ func (pin *Pin) ProtoUnmarshal(data []byte) error { pin.Reference = ref } pin.Reference = ref - pin.ReplicationFactorMin = int(pbPin.GetReplicationFactorMin()) - pin.ReplicationFactorMax = int(pbPin.GetReplicationFactorMax()) - pin.Name = pbPin.GetName() - pin.ShardSize = pbPin.GetShardSize() - pin.Metadata = pbPin.GetMetadata() + + opts := pbPin.GetOptions() + pin.ReplicationFactorMin = int(opts.GetReplicationFactorMin()) + pin.ReplicationFactorMax = int(opts.GetReplicationFactorMax()) + pin.Name = opts.GetName() + pin.ShardSize = opts.GetShardSize() + pin.UserAllocations = opts.GetUserAllocations() + pin.Metadata = opts.GetMetadata() return nil } @@ -941,7 +1020,7 @@ func (pin Pin) Equals(pin2 Pin) bool { return false } - if pin1s.ShardSize != pin2s.ShardSize { + if pin1s.Reference != pin2s.Reference { return false } @@ -952,19 +1031,7 @@ func (pin Pin) Equals(pin2 Pin) bool { return false } - if pin1s.ReplicationFactorMax != pin2s.ReplicationFactorMax { - return false - } - - if pin1s.ReplicationFactorMin != pin2s.ReplicationFactorMin { - return false - } - - if pin1s.Reference != pin2s.Reference { - return false - } - - return true + return pin.PinOptions.Equals(&pin2.PinOptions) } // IsRemotePin determines whether a Pin's ReplicationFactor has diff --git a/api/types_test.go b/api/types_test.go index 87e447f90..f051d413f 100644 --- a/api/types_test.go +++ b/api/types_test.go @@ -2,6 +2,7 @@ package api import ( "fmt" + "net/url" "reflect" "strings" "testing" @@ -349,3 +350,53 @@ func TestPinTags(t *testing.T) { typ := reflect.TypeOf(PinSerial{}) checkDupTags(t, "codec", typ, nil) } + +func TestPinOptionsQuery(t *testing.T) { + testcases := []*PinOptions{ + &PinOptions{ + ReplicationFactorMax: 3, + ReplicationFactorMin: 2, + Name: "abc", + ShardSize: 33, + UserAllocations: []string{"host1", "host2"}, + Metadata: map[string]string{ + "hello": "bye", + "hello2": "bye2", + }, + }, + &PinOptions{ + ReplicationFactorMax: -1, + ReplicationFactorMin: 0, + Name: "", + ShardSize: 0, + UserAllocations: []string{}, + Metadata: nil, + }, + &PinOptions{ + ReplicationFactorMax: -1, + ReplicationFactorMin: 0, + Name: "", + ShardSize: 0, + UserAllocations: nil, + Metadata: map[string]string{ + "": "bye", + }, + }, + } + + for _, tc := range testcases { + queryStr := tc.ToQuery() + q, err := url.ParseQuery(queryStr) + if err != nil { + t.Error("error parsing query", err) + } + po2 := &PinOptions{} + po2.FromQuery(q) + if !tc.Equals(po2) { + t.Error("expected equal PinOptions") + t.Error(queryStr) + t.Errorf("%+v\n", tc) + t.Errorf("%+v\n", po2) + } + } +}