diff --git a/v2/account_claims.go b/v2/account_claims.go index 05850fc..3a9b7ac 100644 --- a/v2/account_claims.go +++ b/v2/account_claims.go @@ -255,7 +255,7 @@ type Account struct { Mappings Mapping `json:"mappings,omitempty"` Authorization ExternalAuthorization `json:"authorization,omitempty"` Trace *MsgTrace `json:"trace,omitempty"` - ClusterTraffic ClusterTraffic `json:"cluster_traffic,omitempty"` + ClusterTraffic string `json:"cluster_traffic,omitempty"` Info GenericFields } @@ -324,7 +324,7 @@ func (a *Account) Validate(acct *AccountClaims, vr *ValidationResults) { a.SigningKeys.Validate(vr) a.Info.Validate(vr) - if err := a.ClusterTraffic.Valid(); err != nil { + if err := ClusterTraffic(a.ClusterTraffic).Valid(); err != nil { vr.AddError(err.Error()) } } diff --git a/v2/types.go b/v2/types.go index f0049ab..d1c3942 100644 --- a/v2/types.go +++ b/v2/types.go @@ -21,6 +21,7 @@ import ( "net" "net/url" "reflect" + "sort" "strconv" "strings" "time" @@ -422,26 +423,46 @@ func (u *StringList) Remove(p ...string) { } // TagList is a unique array of lower case strings -// All tag list methods lower case the strings in the arguments type TagList []string -// Contains returns true if the list contains the tags +// Contains returns true if the list contains the tag func (u *TagList) Contains(p string) bool { return u.find(p) != -1 } +// ContainsEqualsFold returns true if the list contain the tag regardless of case +func (u *TagList) ContainsEqualsFold(p string) bool { + return u.findEqualsFold(p) != -1 +} + +// Equals returns true if the lists are strictly equal func (u *TagList) Equals(other *TagList) bool { if len(*u) != len(*other) { return false } - for _, v := range *u { - if other.find(v) == -1 { + + a := sort.StringSlice(*u) + sort.Sort(a) + b := sort.StringSlice(*other) + sort.Sort(b) + + for i, v := range a { + if v != b[i] { return false } } return true } +func (u *TagList) findEqualsFold(p string) int { + for idx, t := range *u { + if strings.EqualFold(p, t) { + return idx + } + } + return -1 +} + func (u *TagList) find(p string) int { for idx, t := range *u { if p == t { @@ -451,7 +472,7 @@ func (u *TagList) find(p string) int { return -1 } -// Add appends 1 or more tags to a list +// Add appends 1 or more tags to a list, case of the arguments is preserved. func (u *TagList) Add(p ...string) { for _, v := range p { v = strings.TrimSpace(v) @@ -464,7 +485,7 @@ func (u *TagList) Add(p ...string) { } } -// Remove removes 1 or more tags from a list +// Remove removes 1 or more tags from a list, tags must be strictly equal func (u *TagList) Remove(p ...string) error { for _, v := range p { v = strings.TrimSpace(v) @@ -479,6 +500,22 @@ func (u *TagList) Remove(p ...string) error { return nil } +// RemoveEqualsFold removes 1 or more tags from a list as long as their +// values are equal regardless of fold. +func (u *TagList) RemoveEqualsFold(p ...string) error { + for _, v := range p { + v = strings.TrimSpace(v) + idx := u.findEqualsFold(v) + if idx != -1 { + a := *u + *u = append(a[:idx], a[idx+1:]...) + } else { + return fmt.Errorf("unable to remove tag: %q - not found", v) + } + } + return nil +} + type CIDRList []string func (c *CIDRList) Contains(p string) bool { diff --git a/v2/types_test.go b/v2/types_test.go index 9d8a205..b85deef 100644 --- a/v2/types_test.go +++ b/v2/types_test.go @@ -453,6 +453,29 @@ func TestTagList_CasePreservingContains(t *testing.T) { } } +func TestTagList_EqualsFoldContains(t *testing.T) { + type test struct { + v string + a TagList + ok bool + } + + tests := []test{ + {v: "A", a: TagList{}, ok: false}, + {v: "A", a: TagList{"a"}, ok: true}, + {v: "A", a: TagList{"A"}, ok: true}, + {v: "a", a: TagList{"a:hello"}, ok: false}, + {v: "a:a", a: TagList{"a:c"}, ok: false}, + } + + for idx, test := range tests { + found := test.a.ContainsEqualsFold(test.v) + if !found && test.ok { + t.Errorf("[%d] expected to contain %q", idx, test.v) + } + } +} + func TestTagList_Add(t *testing.T) { type test struct { v string @@ -503,3 +526,30 @@ func TestTagList_Delete(t *testing.T) { } } } + +func TestTagList_EqualsFoldDelete(t *testing.T) { + type test struct { + v string + a TagList + shouldBe TagList + shouldFail bool + } + + tests := []test{ + {v: "A", a: TagList{}, shouldBe: TagList{}, shouldFail: true}, + {v: "A", a: TagList{"A"}, shouldBe: TagList{}}, + {v: "a", a: TagList{"A"}, shouldBe: TagList{}}, + {v: "a:Hello", a: TagList{"a:hello"}, shouldBe: TagList{}}, + {v: "a:a", a: TagList{"a:A"}, shouldBe: TagList{}}, + } + + for idx, test := range tests { + err := test.a.RemoveEqualsFold(test.v) + if test.shouldFail && err == nil { + t.Fatalf("[%d] expected delete to fail: %v", idx, test.a) + } + if !test.a.Equals(&test.shouldBe) { + t.Fatalf("[%d] expected lists to be equal: %v", idx, test.a) + } + } +}