mirror of
https://github.com/retailcrm/mg-transport-core.git
synced 2024-11-21 20:56:04 +03:00
Improve errors handling
This commit is contained in:
parent
6af8588b17
commit
65f0ecfa6a
@ -11,10 +11,6 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
credentialsTransport = []string{
|
||||
"/api/integration-modules/{code}",
|
||||
"/api/integration-modules/{code}/edit",
|
||||
}
|
||||
markdownSymbols = []string{"*", "_", "`", "["}
|
||||
slashRegex = regexp.MustCompile(`/+$`)
|
||||
)
|
||||
|
50
core/error_scopes.go
Normal file
50
core/error_scopes.go
Normal 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
15
core/error_scopes_test.go
Normal 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))
|
||||
}
|
@ -5,7 +5,6 @@ import (
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
@ -17,10 +16,15 @@ import (
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"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"
|
||||
)
|
||||
|
||||
var DefaultScopes = []string{
|
||||
"integration_read",
|
||||
"integration_write",
|
||||
}
|
||||
|
||||
var defaultCurrencies = map[string]string{
|
||||
"rub": "₽",
|
||||
"uah": "₴",
|
||||
@ -108,60 +112,41 @@ func (u *Utils) GenerateToken() string {
|
||||
}
|
||||
|
||||
// GetAPIClient will initialize RetailCRM api client from url and key.
|
||||
func (u *Utils) GetAPIClient(url, key string) (*v5.Client, int, error) {
|
||||
client := v5.New(url, key)
|
||||
func (u *Utils) GetAPIClient(url, key string, scopes []string) (*retailcrm.Client, int, error) {
|
||||
client := retailcrm.New(url, key).
|
||||
WithLogger(retailcrm.DebugLoggerAdapter(u.Logger))
|
||||
client.Debug = u.IsDebug
|
||||
|
||||
cr, status, e := client.APICredentials()
|
||||
if e != nil && e.Error() != "" {
|
||||
u.Logger.Error(url, status, e.Error(), cr)
|
||||
return nil, http.StatusInternalServerError, errors.New(e.Error())
|
||||
cr, status, err := client.APICredentials()
|
||||
if err != nil {
|
||||
return nil, status, err
|
||||
}
|
||||
|
||||
if !cr.Success {
|
||||
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 {
|
||||
if res := u.checkScopes(cr.Scopes, scopes); len(res) != 0 {
|
||||
u.Logger.Error(url, status, res)
|
||||
return nil, http.StatusBadRequest, errors.New("missing credentials")
|
||||
return nil, http.StatusBadRequest, NewInsufficientScopesErr(res)
|
||||
}
|
||||
|
||||
return client, 0, nil
|
||||
}
|
||||
|
||||
func (u *Utils) checkCredentials(credential []string) []string {
|
||||
rc := make([]string, len(credentialsTransport))
|
||||
copy(rc, credentialsTransport)
|
||||
func (u *Utils) checkScopes(scopes []string, scopesRequired []string) []string {
|
||||
rs := make([]string, len(scopesRequired))
|
||||
copy(rs, scopesRequired)
|
||||
|
||||
for _, vc := range credential {
|
||||
for kn, vn := range rc {
|
||||
if vn == vc {
|
||||
if len(rc) == 1 {
|
||||
rc = rc[:0]
|
||||
for _, vs := range scopes {
|
||||
for kn, vn := range rs {
|
||||
if vn == vs {
|
||||
if len(rs) == 1 {
|
||||
rs = rs[:0]
|
||||
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.
|
||||
|
@ -2,6 +2,7 @@ package core
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
@ -9,7 +10,7 @@ import (
|
||||
|
||||
"github.com/h2non/gock"
|
||||
"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"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -68,9 +69,10 @@ func (u *UtilsTest) Test_GenerateToken() {
|
||||
|
||||
func (u *UtilsTest) Test_GetAPIClient_FailRuntime() {
|
||||
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.NotNil(u.T(), err)
|
||||
}
|
||||
@ -80,9 +82,9 @@ func (u *UtilsTest) Test_GetAPIClient_FailAPI() {
|
||||
gock.New(testCRMURL).
|
||||
Get("/credentials").
|
||||
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)
|
||||
if assert.NotNil(u.T(), err) {
|
||||
assert.Equal(u.T(), "invalid credentials", err.Error())
|
||||
@ -90,9 +92,9 @@ func (u *UtilsTest) Test_GetAPIClient_FailAPI() {
|
||||
}
|
||||
|
||||
func (u *UtilsTest) Test_GetAPIClient_FailAPICredentials() {
|
||||
resp := v5.CredentialResponse{
|
||||
resp := retailcrm.CredentialResponse{
|
||||
Success: true,
|
||||
Credentials: []string{},
|
||||
Scopes: []string{},
|
||||
SiteAccess: "all",
|
||||
SitesAvailable: []string{},
|
||||
}
|
||||
@ -105,20 +107,17 @@ func (u *UtilsTest) Test_GetAPIClient_FailAPICredentials() {
|
||||
Reply(http.StatusOK).
|
||||
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)
|
||||
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() {
|
||||
resp := v5.CredentialResponse{
|
||||
Success: true,
|
||||
Credentials: []string{
|
||||
"/api/integration-modules/{code}",
|
||||
"/api/integration-modules/{code}/edit",
|
||||
},
|
||||
resp := retailcrm.CredentialResponse{
|
||||
Success: true,
|
||||
Scopes: DefaultScopes,
|
||||
SiteAccess: "all",
|
||||
SitesAvailable: []string{"site"},
|
||||
}
|
||||
@ -131,7 +130,7 @@ func (u *UtilsTest) Test_GetAPIClient_Success() {
|
||||
Reply(http.StatusOK).
|
||||
BodyString(string(data))
|
||||
|
||||
_, status, err := u.utils.GetAPIClient(testCRMURL, "key")
|
||||
_, status, err := u.utils.GetAPIClient(testCRMURL, "key", DefaultScopes)
|
||||
require.NoError(u.T(), err)
|
||||
assert.Equal(u.T(), 0, status)
|
||||
}
|
||||
@ -162,6 +161,21 @@ func (u *UtilsTest) Test_RemoveTrailingSlash() {
|
||||
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) {
|
||||
defer gock.Off()
|
||||
gock.New(testMGURL)
|
||||
|
4
go.mod
4
go.mod
@ -14,7 +14,6 @@ require (
|
||||
github.com/go-playground/validator/v10 v10.8.0
|
||||
github.com/go-sql-driver/mysql v1.5.0 // 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/sessions v1.2.0
|
||||
github.com/h2non/gock v1.0.10
|
||||
@ -24,12 +23,11 @@ require (
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/lib/pq v1.9.0 // 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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||
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/stretchr/testify v1.7.0
|
||||
github.com/ugorji/go v1.2.6 // indirect
|
||||
|
13
go.sum
13
go.sum
@ -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.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.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/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.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
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/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
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/h2non/gock v1.0.10 h1:EzHYzKKSLN4xk0w193uAy3tp8I3+L1jmaI2Mjg4lCgU=
|
||||
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.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
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-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
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 v1.3.0/go.mod h1:QRoPE2SM6ST7i2g0yEdqm7Iw98y7cYuq3q14Ot+6N8c=
|
||||
github.com/retailcrm/api-client-go/v2 v2.0.1 h1:wyM0F1VTSJPO8PVEXB0u7s6ZEs0xRCnUu7YdQ1E4UZ8=
|
||||
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/go.mod h1:AWV6BueE28/6SCoyfKURTo4lF0oXYoOKmHTzehd5vAI=
|
||||
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/gormigrate.v1 v1.6.0 h1:XpYM6RHQPmzwY7Uyu+t+xxMXc86JYFJn4nEc9HzQjsI=
|
||||
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/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
Loading…
Reference in New Issue
Block a user