formalizes ErrApplicationThrottled (#4317)

adds graph error identification for application throttled and adds an ApplicationThrottled sentinel to pkg/errs for sdk consumer identification.

---

#### Does this PR need a docs update or release note?

- [x]  No

#### Type of change

- [x] 🌻 Feature

#### Test Plan

- [x]  Unit test
- [x] 💚 E2E
This commit is contained in:
Keepers 2023-09-20 15:27:01 -06:00 committed by GitHub
parent b212c37fd3
commit c1ec3c6648
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 58 additions and 4 deletions

View File

@ -26,6 +26,7 @@ import (
type errorCode string
const (
applicationThrottled errorCode = "ApplicationThrottled"
// this auth error is a catch-all used by graph in a variety of cases:
// users without licenses, bad jwts, missing account permissions, etc.
AuthenticationError errorCode = "AuthenticationError"
@ -81,6 +82,10 @@ const (
)
var (
// ErrApplicationThrottled occurs if throttling retries are exhausted and completely
// fails out.
ErrApplicationThrottled = clues.New("application throttled")
// The folder or item was deleted between the time we identified
// it and when we tried to fetch data for it.
ErrDeletedInFlight = clues.New("deleted in flight")
@ -116,6 +121,11 @@ var (
ErrResourceOwnerNotFound = clues.New("resource owner not found in tenant")
)
func IsErrApplicationThrottled(err error) bool {
return hasErrorCode(err, applicationThrottled) ||
errors.Is(err, ErrApplicationThrottled)
}
func IsErrAuthenticationError(err error) bool {
return hasErrorCode(err, AuthenticationError)
}

View File

@ -94,6 +94,40 @@ func (suite *GraphErrorsUnitSuite) TestIsErrConnectionReset() {
}
}
func (suite *GraphErrorsUnitSuite) TestIsErrApplicationThrottled() {
table := []struct {
name string
err error
expect assert.BoolAssertionFunc
}{
{
name: "nil",
err: nil,
expect: assert.False,
},
{
name: "non-matching",
err: assert.AnError,
expect: assert.False,
},
{
name: "non-matching oDataErr",
err: odErr("fnords"),
expect: assert.False,
},
{
name: "applicationThrottled oDataErr",
err: odErr(string(applicationThrottled)),
expect: assert.True,
},
}
for _, test := range table {
suite.Run(test.name, func() {
test.expect(suite.T(), IsErrApplicationThrottled(test.err))
})
}
}
func (suite *GraphErrorsUnitSuite) TestIsErrAuthenticationError() {
table := []struct {
name string

View File

@ -113,6 +113,10 @@ func (hw httpWrapper) Request(
break
}
if IsErrApplicationThrottled(err) {
return nil, Stack(ictx, clues.Stack(ErrApplicationThrottled, err))
}
var http2StreamErr http2.StreamError
if !errors.As(err, &http2StreamErr) {
return nil, Stack(ictx, err)

View File

@ -360,6 +360,10 @@ func (aw *adapterWrap) Send(
break
}
if IsErrApplicationThrottled(err) {
return nil, clues.Stack(ErrApplicationThrottled, err).WithTrace(1).WithClues(ictx)
}
if !IsErrConnectionReset(err) && !connectionEnded.Compare(err.Error()) {
return nil, clues.Stack(err).WithTrace(1).WithClues(ictx)
}

View File

@ -13,20 +13,22 @@ import (
type errEnum string
const (
RepoAlreadyExists errEnum = "repository-already-exists"
ApplicationThrottled errEnum = "application-throttled"
BackupNotFound errEnum = "backup-not-found"
ServiceNotEnabled errEnum = "service-not-enabled"
RepoAlreadyExists errEnum = "repository-already-exists"
ResourceOwnerNotFound errEnum = "resource-owner-not-found"
ServiceNotEnabled errEnum = "service-not-enabled"
)
// map of enums to errors. We might want to re-use an enum for multiple
// internal errors (ex: "ServiceNotEnabled" may exist in both graph and
// non-graph producers).
var internalToExternal = map[errEnum][]error{
RepoAlreadyExists: {repository.ErrorRepoAlreadyExists},
ApplicationThrottled: {graph.ErrApplicationThrottled},
BackupNotFound: {repository.ErrorBackupNotFound},
ServiceNotEnabled: {graph.ErrServiceNotEnabled},
RepoAlreadyExists: {repository.ErrorRepoAlreadyExists},
ResourceOwnerNotFound: {graph.ErrResourceOwnerNotFound},
ServiceNotEnabled: {graph.ErrServiceNotEnabled},
}
// Internal returns the internal errors which match to the public error category.