Improve errors handling

This commit is contained in:
tishmaria90 2021-11-03 17:27:56 +03:00 committed by GitHub
parent 6af8588b17
commit 65f0ecfa6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 129 additions and 66 deletions

View File

@ -11,10 +11,6 @@ import (
) )
var ( var (
credentialsTransport = []string{
"/api/integration-modules/{code}",
"/api/integration-modules/{code}/edit",
}
markdownSymbols = []string{"*", "_", "`", "["} markdownSymbols = []string{"*", "_", "`", "["}
slashRegex = regexp.MustCompile(`/+$`) slashRegex = regexp.MustCompile(`/+$`)
) )

50
core/error_scopes.go Normal file
View File

@ -0,0 +1,50 @@
package core
import (
"errors"
"fmt"
"strings"
)
// ErrInsufficientScopes is wrapped by the insufficientScopesErr.
// If errors.Is(err, ErrInsufficientScopes) returns true then error implements ScopesList.
var ErrInsufficientScopes = errors.New("insufficient scopes")
// ScopesList is a contract for the scopes list.
type ScopesList interface {
Scopes() []string
}
// insufficientScopesErr contains information about missing auth scopes.
type insufficientScopesErr struct {
scopes []string
wrapped error
}
// Error message.
func (e insufficientScopesErr) Error() string {
return e.wrapped.Error()
}
// Unwrap underlying error.
func (e insufficientScopesErr) Unwrap() error {
return e.wrapped
}
// Scopes that are missing.
func (e insufficientScopesErr) Scopes() []string {
return e.scopes
}
// String returns string representation of an error with scopes that are missing.
func (e insufficientScopesErr) String() string {
return fmt.Sprintf("Missing scopes: %s", strings.Join(e.Scopes(), ", "))
}
// NewInsufficientScopesErr is a insufficientScopesErr constructor.
func NewInsufficientScopesErr(scopes []string) error {
return insufficientScopesErr{
scopes: scopes,
wrapped: ErrInsufficientScopes,
}
}

15
core/error_scopes_test.go Normal file
View File

@ -0,0 +1,15 @@
package core
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
)
func TestError_NewScopesError(t *testing.T) {
scopes := []string{"scope1", "scope2"}
scopesError := NewInsufficientScopesErr(scopes)
assert.True(t, errors.Is(scopesError, ErrInsufficientScopes))
}

View File

@ -5,7 +5,6 @@ import (
"crypto/sha1" "crypto/sha1"
"crypto/sha256" "crypto/sha256"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"net/http" "net/http"
"regexp" "regexp"
@ -17,10 +16,15 @@ import (
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/aws/aws-sdk-go/service/s3/s3manager"
v5 "github.com/retailcrm/api-client-go/v5" retailcrm "github.com/retailcrm/api-client-go/v2"
v1 "github.com/retailcrm/mg-transport-api-client-go/v1" v1 "github.com/retailcrm/mg-transport-api-client-go/v1"
) )
var DefaultScopes = []string{
"integration_read",
"integration_write",
}
var defaultCurrencies = map[string]string{ var defaultCurrencies = map[string]string{
"rub": "₽", "rub": "₽",
"uah": "₴", "uah": "₴",
@ -108,60 +112,41 @@ func (u *Utils) GenerateToken() string {
} }
// GetAPIClient will initialize RetailCRM api client from url and key. // GetAPIClient will initialize RetailCRM api client from url and key.
func (u *Utils) GetAPIClient(url, key string) (*v5.Client, int, error) { func (u *Utils) GetAPIClient(url, key string, scopes []string) (*retailcrm.Client, int, error) {
client := v5.New(url, key) client := retailcrm.New(url, key).
WithLogger(retailcrm.DebugLoggerAdapter(u.Logger))
client.Debug = u.IsDebug client.Debug = u.IsDebug
cr, status, e := client.APICredentials() cr, status, err := client.APICredentials()
if e != nil && e.Error() != "" { if err != nil {
u.Logger.Error(url, status, e.Error(), cr) return nil, status, err
return nil, http.StatusInternalServerError, errors.New(e.Error())
} }
if !cr.Success { if res := u.checkScopes(cr.Scopes, scopes); len(res) != 0 {
errMsg := "unknown error"
if e != nil {
if e.ApiError() != "" {
errMsg = e.ApiError()
} else if e.ApiErrors() != nil {
errMsg = ""
for key, errText := range e.ApiErrors() {
errMsg += fmt.Sprintf("[%s: %s] ", key, errText)
}
}
}
u.Logger.Error(url, status, errMsg, cr)
return nil, http.StatusBadRequest, errors.New("invalid credentials")
}
if res := u.checkCredentials(cr.Credentials); len(res) != 0 {
u.Logger.Error(url, status, res) u.Logger.Error(url, status, res)
return nil, http.StatusBadRequest, errors.New("missing credentials") return nil, http.StatusBadRequest, NewInsufficientScopesErr(res)
} }
return client, 0, nil return client, 0, nil
} }
func (u *Utils) checkCredentials(credential []string) []string { func (u *Utils) checkScopes(scopes []string, scopesRequired []string) []string {
rc := make([]string, len(credentialsTransport)) rs := make([]string, len(scopesRequired))
copy(rc, credentialsTransport) copy(rs, scopesRequired)
for _, vc := range credential { for _, vs := range scopes {
for kn, vn := range rc { for kn, vn := range rs {
if vn == vc { if vn == vs {
if len(rc) == 1 { if len(rs) == 1 {
rc = rc[:0] rs = rs[:0]
break break
} }
rc = append(rc[:kn], rc[kn+1:]...) rs = append(rs[:kn], rs[kn+1:]...)
} }
} }
} }
return rc return rs
} }
// UploadUserAvatar will upload avatar for user. // UploadUserAvatar will upload avatar for user.

View File

@ -2,6 +2,7 @@ package core
import ( import (
"encoding/json" "encoding/json"
"errors"
"net/http" "net/http"
"strings" "strings"
"testing" "testing"
@ -9,7 +10,7 @@ import (
"github.com/h2non/gock" "github.com/h2non/gock"
"github.com/op/go-logging" "github.com/op/go-logging"
v5 "github.com/retailcrm/api-client-go/v5" retailcrm "github.com/retailcrm/api-client-go/v2"
v1 "github.com/retailcrm/mg-transport-api-client-go/v1" v1 "github.com/retailcrm/mg-transport-api-client-go/v1"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -68,9 +69,10 @@ func (u *UtilsTest) Test_GenerateToken() {
func (u *UtilsTest) Test_GetAPIClient_FailRuntime() { func (u *UtilsTest) Test_GetAPIClient_FailRuntime() {
defer gock.Off() defer gock.Off()
gock.New(testCRMURL) gock.New(testCRMURL).
Reply(http.StatusInternalServerError)
_, status, err := u.utils.GetAPIClient(testCRMURL, "key") _, status, err := u.utils.GetAPIClient(testCRMURL, "key", []string{})
assert.Equal(u.T(), http.StatusInternalServerError, status) assert.Equal(u.T(), http.StatusInternalServerError, status)
assert.NotNil(u.T(), err) assert.NotNil(u.T(), err)
} }
@ -80,9 +82,9 @@ func (u *UtilsTest) Test_GetAPIClient_FailAPI() {
gock.New(testCRMURL). gock.New(testCRMURL).
Get("/credentials"). Get("/credentials").
Reply(http.StatusBadRequest). Reply(http.StatusBadRequest).
BodyString(`{"success": false, "errorMsg": "error message"}`) BodyString(`{"success": false, "errorMsg": "invalid credentials"}`)
_, status, err := u.utils.GetAPIClient(testCRMURL, "key") _, status, err := u.utils.GetAPIClient(testCRMURL, "key", []string{})
assert.Equal(u.T(), http.StatusBadRequest, status) assert.Equal(u.T(), http.StatusBadRequest, status)
if assert.NotNil(u.T(), err) { if assert.NotNil(u.T(), err) {
assert.Equal(u.T(), "invalid credentials", err.Error()) assert.Equal(u.T(), "invalid credentials", err.Error())
@ -90,9 +92,9 @@ func (u *UtilsTest) Test_GetAPIClient_FailAPI() {
} }
func (u *UtilsTest) Test_GetAPIClient_FailAPICredentials() { func (u *UtilsTest) Test_GetAPIClient_FailAPICredentials() {
resp := v5.CredentialResponse{ resp := retailcrm.CredentialResponse{
Success: true, Success: true,
Credentials: []string{}, Scopes: []string{},
SiteAccess: "all", SiteAccess: "all",
SitesAvailable: []string{}, SitesAvailable: []string{},
} }
@ -105,20 +107,17 @@ func (u *UtilsTest) Test_GetAPIClient_FailAPICredentials() {
Reply(http.StatusOK). Reply(http.StatusOK).
BodyString(string(data)) BodyString(string(data))
_, status, err := u.utils.GetAPIClient(testCRMURL, "key") _, status, err := u.utils.GetAPIClient(testCRMURL, "key", DefaultScopes)
assert.Equal(u.T(), http.StatusBadRequest, status) assert.Equal(u.T(), http.StatusBadRequest, status)
if assert.NotNil(u.T(), err) { if assert.NotNil(u.T(), err) {
assert.Equal(u.T(), "missing credentials", err.Error()) assert.True(u.T(), errors.Is(err, ErrInsufficientScopes))
} }
} }
func (u *UtilsTest) Test_GetAPIClient_Success() { func (u *UtilsTest) Test_GetAPIClient_Success() {
resp := v5.CredentialResponse{ resp := retailcrm.CredentialResponse{
Success: true, Success: true,
Credentials: []string{ Scopes: DefaultScopes,
"/api/integration-modules/{code}",
"/api/integration-modules/{code}/edit",
},
SiteAccess: "all", SiteAccess: "all",
SitesAvailable: []string{"site"}, SitesAvailable: []string{"site"},
} }
@ -131,7 +130,7 @@ func (u *UtilsTest) Test_GetAPIClient_Success() {
Reply(http.StatusOK). Reply(http.StatusOK).
BodyString(string(data)) BodyString(string(data))
_, status, err := u.utils.GetAPIClient(testCRMURL, "key") _, status, err := u.utils.GetAPIClient(testCRMURL, "key", DefaultScopes)
require.NoError(u.T(), err) require.NoError(u.T(), err)
assert.Equal(u.T(), 0, status) assert.Equal(u.T(), 0, status)
} }
@ -162,6 +161,21 @@ func (u *UtilsTest) Test_RemoveTrailingSlash() {
assert.Equal(u.T(), testCRMURL, u.utils.RemoveTrailingSlash(testCRMURL)) assert.Equal(u.T(), testCRMURL, u.utils.RemoveTrailingSlash(testCRMURL))
} }
func (u *UtilsTest) TestUtils_CheckScopes() {
required := []string{"one", "two"}
scopes := []string{"one", "two"}
diff := u.utils.checkScopes(scopes, required)
assert.Equal(u.T(), 0, len(diff))
scopes = []string{"three", "four"}
diff = u.utils.checkScopes(scopes, required)
assert.Equal(u.T(), 2, len(diff))
assert.Equal(u.T(), []string{"one", "two"}, diff)
}
func TestUtils_GetMGItemData_FailRuntime_GetImage(t *testing.T) { func TestUtils_GetMGItemData_FailRuntime_GetImage(t *testing.T) {
defer gock.Off() defer gock.Off()
gock.New(testMGURL) gock.New(testMGURL)

4
go.mod
View File

@ -14,7 +14,6 @@ require (
github.com/go-playground/validator/v10 v10.8.0 github.com/go-playground/validator/v10 v10.8.0
github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/go-sql-driver/mysql v1.5.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-querystring v1.0.0 // indirect
github.com/gorilla/securecookie v1.1.1 github.com/gorilla/securecookie v1.1.1
github.com/gorilla/sessions v1.2.0 github.com/gorilla/sessions v1.2.0
github.com/h2non/gock v1.0.10 github.com/h2non/gock v1.0.10
@ -24,12 +23,11 @@ require (
github.com/kr/text v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect
github.com/lib/pq v1.9.0 // indirect github.com/lib/pq v1.9.0 // indirect
github.com/mattn/go-isatty v0.0.13 // indirect github.com/mattn/go-isatty v0.0.13 // indirect
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 // indirect
github.com/nicksnyder/go-i18n/v2 v2.0.2 github.com/nicksnyder/go-i18n/v2 v2.0.2
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/retailcrm/api-client-go v1.3.0 github.com/retailcrm/api-client-go/v2 v2.0.1
github.com/retailcrm/mg-transport-api-client-go v1.1.32 github.com/retailcrm/mg-transport-api-client-go v1.1.32
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
github.com/ugorji/go v1.2.6 // indirect github.com/ugorji/go v1.2.6 // indirect

13
go.sum
View File

@ -136,10 +136,11 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@ -160,6 +161,8 @@ github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYb
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/h2non/gock v1.0.10 h1:EzHYzKKSLN4xk0w193uAy3tp8I3+L1jmaI2Mjg4lCgU= github.com/h2non/gock v1.0.10 h1:EzHYzKKSLN4xk0w193uAy3tp8I3+L1jmaI2Mjg4lCgU=
github.com/h2non/gock v1.0.10/go.mod h1:CZMcB0Lg5IWnr9bF79pPMg9WeV6WumxQiUJ1UvdO1iE= github.com/h2non/gock v1.0.10/go.mod h1:CZMcB0Lg5IWnr9bF79pPMg9WeV6WumxQiUJ1UvdO1iE=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@ -246,8 +249,8 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/retailcrm/api-client-go v1.3.0 h1:8cEtLZ9gk+nTEVuzA/wzQhb8tRfaxfCQHLdPtO+/gek= github.com/retailcrm/api-client-go/v2 v2.0.1 h1:wyM0F1VTSJPO8PVEXB0u7s6ZEs0xRCnUu7YdQ1E4UZ8=
github.com/retailcrm/api-client-go v1.3.0/go.mod h1:QRoPE2SM6ST7i2g0yEdqm7Iw98y7cYuq3q14Ot+6N8c= github.com/retailcrm/api-client-go/v2 v2.0.1/go.mod h1:1yTZl9+gd3+/k0kAJe7sYvC+mL4fqMwIwtnSgSWZlkQ=
github.com/retailcrm/mg-transport-api-client-go v1.1.32 h1:IBPltSoD5q2PPZJbNC/prK5F9rEVPXVx/ZzDpi7HKhs= github.com/retailcrm/mg-transport-api-client-go v1.1.32 h1:IBPltSoD5q2PPZJbNC/prK5F9rEVPXVx/ZzDpi7HKhs=
github.com/retailcrm/mg-transport-api-client-go v1.1.32/go.mod h1:AWV6BueE28/6SCoyfKURTo4lF0oXYoOKmHTzehd5vAI= github.com/retailcrm/mg-transport-api-client-go v1.1.32/go.mod h1:AWV6BueE28/6SCoyfKURTo4lF0oXYoOKmHTzehd5vAI=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@ -530,6 +533,8 @@ gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/gormigrate.v1 v1.6.0 h1:XpYM6RHQPmzwY7Uyu+t+xxMXc86JYFJn4nEc9HzQjsI= gopkg.in/gormigrate.v1 v1.6.0 h1:XpYM6RHQPmzwY7Uyu+t+xxMXc86JYFJn4nEc9HzQjsI=
gopkg.in/gormigrate.v1 v1.6.0/go.mod h1:Lf00lQrHqfSYWiTtPcyQabsDdM6ejZaMgV0OU6JMSlw= gopkg.in/gormigrate.v1 v1.6.0/go.mod h1:Lf00lQrHqfSYWiTtPcyQabsDdM6ejZaMgV0OU6JMSlw=
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=