Skip to content

Commit

Permalink
[azcore] Add ETag type to Core (#15207)
Browse files Browse the repository at this point in the history
* add type def for ETag

* adding etag specific files and more functionality

* testing weak

* testing for etag equality

* weak tag comparisons

* adding docstrings

* addressing richard and jeffs feedback. split equals to strong and weak, using more HasValue throughout

* fixing one test

* adding EOL

* adding note about the difference between nil and empty etags

* more feedback

* fix spelling, remove comment about nil

* format check
  • Loading branch information
seankane-msft authored Aug 9, 2021
1 parent 6742bfa commit b977244
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 0 deletions.
47 changes: 47 additions & 0 deletions sdk/azcore/etag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// +build go1.13

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package azcore

import (
"strings"
)

// ETag is a property used for optimistic concurrency during updates
// ETag is a validator based on https://tools.ietf.org/html/rfc7232#section-2.3.2
// An ETag can be empty ("").
type ETag string

// ETagAny is an ETag that represents everything, the value is "*"
const ETagAny ETag = "*"

// Equals does a strong comparison of two ETags. Equals returns true when both
// ETags are not weak and the values of the underlying strings are equal.
func (e ETag) Equals(other ETag) bool {
return !e.IsWeak() && !other.IsWeak() && e == other
}

// WeakEquals does a weak comparison of two ETags. Two ETags are equivalent if their opaque-tags match
// character-by-character, regardless of either or both being tagged as "weak".
func (e ETag) WeakEquals(other ETag) bool {
getStart := func(e1 ETag) int {
if e1.IsWeak() {
return 2
}
return 0
}
aStart := getStart(e)
bStart := getStart(other)

aVal := e[aStart:]
bVal := other[bStart:]

return aVal == bVal
}

// IsWeak specifies whether the ETag is strong or weak.
func (e ETag) IsWeak() bool {
return len(e) >= 4 && strings.HasPrefix(string(e), "W/\"") && strings.HasSuffix(string(e), "\"")
}
134 changes: 134 additions & 0 deletions sdk/azcore/etag_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// +build go1.13

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package azcore

import (
"testing"

"github.com/stretchr/testify/require"
)

func createETag(s string) ETag {
return ETag(s)
}

func TestETagEquals(t *testing.T) {
e1 := createETag("tag")
require.Equal(t, string(e1), "tag")

e2 := createETag("\"tag\"")
require.Equal(t, string(e2), "\"tag\"")

e3 := createETag("W/\"weakETag\"")
require.Equal(t, string(e3), "W/\"weakETag\"")
require.Truef(t, e3.IsWeak(), "ETag is expected to be weak")

strongETag := createETag("\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\"")
require.Equal(t, string(strongETag), "\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\"")

require.Falsef(t, ETagAny.IsWeak(), "ETagAny should not be weak")
}

func TestETagWeak(t *testing.T) {
et1 := createETag("tag")
require.Falsef(t, et1.IsWeak(), "expected etag to be strong")

et2 := createETag("\"tag\"")
require.Falsef(t, et2.IsWeak(), "expected etag to be strong")

et3 := createETag("W/\"weakETag\"")
require.Truef(t, et3.IsWeak(), "expected etag to be weak")

et4 := createETag("W/\"\"")
require.Truef(t, et4.IsWeak(), "expected etag to be weak")

et5 := ETagAny
require.Falsef(t, et5.IsWeak(), "expected etag to be strong")
}

func TestETagEquality(t *testing.T) {
weakTag := createETag("W/\"\"")
weakTag1 := createETag("W/\"1\"")
weakTag2 := createETag("W/\"Two\"")
strongTag1 := createETag("\"1\"")
strongTag2 := createETag("\"Two\"")
strongTagValidChars := createETag("\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\"")
weakTagValidChars := createETag("W/\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\"")

require.Falsef(t, weakTag.Equals(weakTag), "Expected etags to not be equal")
require.Falsef(t, weakTag1.Equals(weakTag1), "Expected etags to not be equal")
require.Falsef(t, weakTag2.Equals(weakTag2), "Expected etags to not be equal")
require.Falsef(t, weakTagValidChars.Equals(weakTagValidChars), "Expected etags to not be equal")

require.Truef(t, strongTag1.Equals(strongTag1), "Expected etags to be equal")
require.Truef(t, strongTag2.Equals(strongTag2), "Expected etags to be equal")
require.Truef(t, strongTagValidChars.Equals(strongTagValidChars), "Expected etags to be equal")

require.Falsef(t, weakTag1.Equals(weakTag), "Expected etags to not be equal")
require.Falsef(t, strongTagValidChars.Equals(weakTagValidChars), "Expected etags to not be equal")
require.Falsef(t, weakTag2.Equals(weakTag1), "Expected etags to not be equal")
require.Falsef(t, strongTag1.Equals(weakTag1), "Expected etags to not be equal")
require.Falsef(t, strongTag2.Equals(weakTag2), "Expected etags to not be equal")
}

func TestEtagAny(t *testing.T) {
anyETag := ETagAny
star := createETag("*")
weakStar := createETag("W\"*\"")
quotedStar := createETag("\"*\"")

require.Truef(t, anyETag.Equals(anyETag), "Expected etags to be equal")
require.Truef(t, ETagAny.Equals(anyETag), "Expected etags to be equal")

require.Truef(t, star.Equals(star), "Expected etags to be equal")
require.Truef(t, ETagAny.Equals(star), "Expected etags to be equal")
require.Truef(t, anyETag.Equals(star), "Expected etags to be equal")

require.Falsef(t, star.Equals(weakStar), "Expected etags to be equal")
require.Falsef(t, ETagAny.Equals(weakStar), "Expected etags to be equal")
require.Falsef(t, weakStar.Equals(quotedStar), "Expected etags to be equal")

require.Falsef(t, quotedStar.Equals(star), "Expected etags to be equal")

require.Truef(t, star.Equals(ETagAny), "Expected etags to be equal")
}

func TestETagWeakComparison(t *testing.T) {
// W/""
weakTag := createETag("W/\"\"")
// W/"1"
weakTag1 := createETag("W/\"1\"")
// W/"Two"
weakTagTwo := createETag("W/\"Two\"")
// W/"two"
weakTagtwo := createETag("W/\"two\"")
// "1"
strongTag1 := createETag("\"1\"")
// "Two"
strongTagTwo := createETag("\"Two\"")
// "two"
strongTagtwo := createETag("\"two\"")

require.Truef(t, weakTag.WeakEquals(weakTag), "expected etags to be equal")
require.Truef(t, weakTag1.WeakEquals(weakTag1), "expected etags to be equal")
require.Truef(t, weakTagTwo.WeakEquals(weakTagTwo), "expected etags to be equal")
require.Truef(t, weakTagtwo.WeakEquals(weakTagtwo), "expected etags to be equal")
require.Truef(t, strongTag1.WeakEquals(strongTag1), "expected etags to be equal")
require.Truef(t, strongTagTwo.WeakEquals(strongTagTwo), "expected etags to be equal")
require.Truef(t, strongTagtwo.WeakEquals(strongTagtwo), "expected etags to be equal")

require.Falsef(t, weakTag1.WeakEquals(weakTag), "Expected etags to not be equal")
require.Falsef(t, weakTagTwo.WeakEquals(weakTag1), "Expected etags to not be equal")

require.Truef(t, strongTag1.WeakEquals(weakTag1), "expected etags to be equal")
require.Truef(t, strongTagTwo.WeakEquals(weakTagTwo), "expected etags to be equal")

require.Falsef(t, weakTag1.WeakEquals(strongTagTwo), "Expected etags to not be equal")
require.Falsef(t, weakTagtwo.WeakEquals(strongTagTwo), "Expected etags to not be equal")

require.Falsef(t, strongTagtwo.WeakEquals(strongTagTwo), "Expected etags to not be equal")
require.Falsef(t, weakTagtwo.WeakEquals(weakTagTwo), "Expected etags to not be equal")
}
1 change: 1 addition & 0 deletions sdk/azcore/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module github.com/Azure/azure-sdk-for-go/sdk/azcore

require (
github.com/Azure/azure-sdk-for-go/sdk/internal v0.5.2
github.com/stretchr/testify v1.7.0 // indirect
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b
)

Expand Down
4 changes: 4 additions & 0 deletions sdk/azcore/go.sum
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
github.com/Azure/azure-sdk-for-go/sdk/internal v0.5.2 h1:E2xwjsWU81O/XuSaxAGa8Jmqz4Vm4NmrpMSO9/XevDg=
github.com/Azure/azure-sdk-for-go/sdk/internal v0.5.2/go.mod h1:Hl9Vte0DDolj9zqzmfnmY9/zfZbiT5KnvXqVwAvnR8Q=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b h1:k+E048sYJHyVnsr1GDrRZWQ32D2C7lWs9JRc0bel53A=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
Expand All @@ -19,4 +22,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 comments on commit b977244

Please sign in to comment.