diff --git a/src/pkg/services/m365/api/graph/errors.go b/src/pkg/services/m365/api/graph/errors.go index 3c0674963..945b2f54d 100644 --- a/src/pkg/services/m365/api/graph/errors.go +++ b/src/pkg/services/m365/api/graph/errors.go @@ -66,6 +66,7 @@ const ( // @microsoft.graph.conflictBehavior=fail finds a conflicting file. nameAlreadyExists errorCode = "nameAlreadyExists" NotAllowed errorCode = "notAllowed" + notFound errorCode = "NotFound" noResolvedUsers errorCode = "noResolvedUsers" QuotaExceeded errorCode = "ErrorQuotaExceeded" RequestResourceNotFound errorCode = "Request_ResourceNotFound" @@ -128,10 +129,10 @@ func stackWithCoreErr(ctx context.Context, err error, traceDepth int) error { err = clues.Stack(core.ErrResourceNotAccessible, err) case isErrInsufficientAuthorization(ode, err): err = clues.Stack(core.ErrInsufficientAuthorization, err) - case isErrNotFound(ode, err): - err = clues.Stack(core.ErrNotFound, err) case isErrItemAlreadyExists(ode, err): err = clues.Stack(core.ErrAlreadyExists, err) + case isErrNotFound(ode, err): + err = clues.Stack(core.ErrNotFound, err) } return stackWithDepth(ctx, err, 1+traceDepth) @@ -140,7 +141,8 @@ func stackWithCoreErr(ctx context.Context, err error, traceDepth int) error { // unexported categorizers, for use with stackWithCoreErr func isErrApplicationThrottled(ode oDataErr, err error) bool { - return ode.hasErrorCode(err, ApplicationThrottled) + return ode.hasErrorCode(err, ApplicationThrottled) || + ode.hasResponseCode(err, http.StatusTooManyRequests) } func isErrInsufficientAuthorization(ode oDataErr, err error) bool { @@ -149,11 +151,13 @@ func isErrInsufficientAuthorization(ode oDataErr, err error) bool { func isErrNotFound(ode oDataErr, err error) bool { return clues.HasLabel(err, LabelStatus(http.StatusNotFound)) || + ode.hasResponseCode(err, http.StatusNotFound) || ode.hasErrorCode( err, ErrorItemNotFound, ItemNotFound, - syncFolderNotFound) + syncFolderNotFound, + notFound) } func isErrUserNotFound(ode oDataErr, err error) bool { @@ -177,8 +181,7 @@ func isErrItemAlreadyExists(ode oDataErr, err error) bool { } func isErrResourceLocked(ode oDataErr, err error) bool { - return ode.hasInnerErrorCode(err, ResourceLocked) || - ode.hasErrorCode(err, NotAllowed) || + return ode.hasErrorCode(err, ResourceLocked, NotAllowed) || ode.errMessageMatchesAllFilters( err, filters.In([]string{"the service principal for resource"}), @@ -285,7 +288,7 @@ func IsErrSiteNotFound(err error) bool { } func IsErrSharingDisabled(err error) bool { - return parseODataErr(err).hasInnerErrorCode(err, sharingDisabled) + return parseODataErr(err).hasErrorCode(err, sharingDisabled) } // --------------------------------------------------------------------------- @@ -577,20 +580,9 @@ func (ode oDataErr) hasErrorCode(err error, codes ...errorCode) bool { cs[i] = string(c) } - return filters.Equal(cs).Compare(ode.Main.Code) -} + eq := filters.Equal(cs) -func (ode oDataErr) hasInnerErrorCode(err error, codes ...errorCode) bool { - if !ode.isODataErr { - return false - } - - cs := make([]string, len(codes)) - for i, c := range codes { - cs[i] = string(c) - } - - return filters.Equal(cs).Compare(ode.Inner.Code) + return eq.CompareAny(ode.Main.Code, ode.Inner.Code) } // only use this as a last resort. Prefer the code or statuscode if possible. diff --git a/src/pkg/services/m365/api/graph/errors_test.go b/src/pkg/services/m365/api/graph/errors_test.go index f33271994..19025b3f5 100644 --- a/src/pkg/services/m365/api/graph/errors_test.go +++ b/src/pkg/services/m365/api/graph/errors_test.go @@ -83,6 +83,11 @@ func (suite *GraphErrorsUnitSuite) TestIsErrApplicationThrottled() { err: graphTD.ODataErr(string(ApplicationThrottled)), expect: assert.True, }, + { + name: "too many requests resp status", + err: graphTD.ODataErrWithStatus(http.StatusTooManyRequests, "err"), + expect: assert.True, + }, } for _, test := range table { suite.Run(test.name, func() { @@ -165,6 +170,7 @@ func (suite *GraphErrorsUnitSuite) TestIsErrNotFound() { table := []struct { name string err error + inner error expect assert.BoolAssertionFunc }{ { @@ -180,23 +186,49 @@ func (suite *GraphErrorsUnitSuite) TestIsErrNotFound() { { name: "non-matching oDataErr", err: graphTD.ODataErr("fnords"), + inner: graphTD.ODataInner("fnords"), expect: assert.False, }, { name: "not-found oDataErr", + err: graphTD.ODataErr(string(notFound)), + inner: graphTD.ODataInner(string(notFound)), + expect: assert.True, + }, + { + name: "error item not-found oDataErr", err: graphTD.ODataErr(string(ErrorItemNotFound)), + inner: graphTD.ODataInner(string(ErrorItemNotFound)), + expect: assert.True, + }, + { + name: "item not-found oDataErr", + err: graphTD.ODataErr(string(ItemNotFound)), + inner: graphTD.ODataInner(string(ItemNotFound)), expect: assert.True, }, { name: "sync-not-found oDataErr", err: graphTD.ODataErr(string(syncFolderNotFound)), + inner: graphTD.ODataInner(string(syncFolderNotFound)), + expect: assert.True, + }, + { + name: "not found resp status", + err: graphTD.ODataErrWithStatus(http.StatusNotFound, "err"), expect: assert.True, }, } for _, test := range table { suite.Run(test.name, func() { + t := suite.T() + ode := parseODataErr(test.err) - test.expect(suite.T(), isErrNotFound(ode, test.err)) + test.expect(t, isErrNotFound(ode, test.err)) + + if test.inner != nil { + test.expect(t, isErrNotFound(ode, test.inner)) + } }) } } diff --git a/src/pkg/services/m365/api/graph/testdata/errors.go b/src/pkg/services/m365/api/graph/testdata/errors.go index 8cdef9826..9fe3c61be 100644 --- a/src/pkg/services/m365/api/graph/testdata/errors.go +++ b/src/pkg/services/m365/api/graph/testdata/errors.go @@ -36,6 +36,18 @@ func ODataErrWithMsg(code, message string) *odataerrors.ODataError { return odErr } +func ODataErrWithStatus(status int, code string) *odataerrors.ODataError { + odErr := odataerrors.NewODataError() + merr := odataerrors.NewMainError() + merr.SetCode(&code) + // graph sdk expects the message to be available + merr.SetMessage(&code) + odErr.SetErrorEscaped(merr) + odErr.SetStatusCode(status) + + return odErr +} + func ODataInner(innerCode string) *odataerrors.ODataError { odErr := odataerrors.NewODataError() inerr := odataerrors.NewInnerError()