add canSkipItemFailure handler func

adds a new func to the exchange backup handler: canskipItemFailure.
This interface allows any handler to evaluate the provided error and runtime
config to decide whether that error is able to be marked as skipped instead
of return a recoverable error as per standard.
This commit is contained in:
ryanfkeepers 2024-02-13 16:10:03 -07:00
parent 60438d9e60
commit dd71a5528a
8 changed files with 235 additions and 0 deletions

View File

@ -88,6 +88,15 @@ func (bh mockBackupHandler) folderGetter() containerGetter { return
func (bh mockBackupHandler) previewIncludeContainers() []string { return bh.previewIncludes }
func (bh mockBackupHandler) previewExcludeContainers() []string { return bh.previewExcludes }
func (bh mockBackupHandler) CanSkipItemFailure(
error,
string,
string,
control.Options,
) (fault.SkipCause, bool) {
return "", false
}
func (bh mockBackupHandler) NewContainerCache(
userID string,
) (string, graph.ContainerResolver) {

View File

@ -1,6 +1,8 @@
package exchange
import (
"github.com/alcionai/corso/src/pkg/control"
"github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/services/m365/api"
"github.com/alcionai/corso/src/pkg/services/m365/api/graph"
)
@ -52,3 +54,11 @@ func (h contactBackupHandler) NewContainerCache(
getter: h.ac,
}
}
func (h contactBackupHandler) CanSkipItemFailure(
error,
string, string,
control.Options,
) (fault.SkipCause, bool) {
return "", false
}

View File

@ -0,0 +1,31 @@
package exchange
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/control"
"github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/services/m365/api"
)
type ContactsBackupHandlerUnitSuite struct {
tester.Suite
}
func TestContactsBackupHandlerUnitSuite(t *testing.T) {
suite.Run(t, &ContactsBackupHandlerUnitSuite{Suite: tester.NewUnitSuite(t)})
}
func (suite *ContactsBackupHandlerUnitSuite) TestHandler_CanSkipItemFailure() {
t := suite.T()
h := newContactBackupHandler(api.Client{})
cause, result := h.CanSkipItemFailure(nil, "", "", control.Options{})
assert.False(t, result)
assert.Equal(t, fault.SkipCause(""), cause)
}

View File

@ -1,6 +1,12 @@
package exchange
import (
"errors"
"slices"
"github.com/alcionai/clues"
"github.com/alcionai/corso/src/pkg/control"
"github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/services/m365/api"
"github.com/alcionai/corso/src/pkg/services/m365/api/graph"
)
@ -52,3 +58,22 @@ func (h eventBackupHandler) NewContainerCache(
getter: h.ac,
}
}
func (h eventBackupHandler) CanSkipItemFailure(
err error,
resourceID, itemID string,
opts control.Options,
) (fault.SkipCause, bool) {
// yes, this is intentionally a todo. I'll get back to it.
if !errors.Is(err, clues.New("todo fix me")) {
return "", false
}
itemIDs, ok := opts.SkipTheseEventsOnInstance503[resourceID]
if !ok {
return "", false
}
// strict equals required here. ids are case sensitive.
return fault.SkipKnownEventInstance503s, slices.Contains(itemIDs, itemID)
}

View File

@ -0,0 +1,109 @@
package exchange
import (
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"github.com/alcionai/clues"
"github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/control"
"github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/services/m365/api"
)
type EventsBackupHandlerUnitSuite struct {
tester.Suite
}
func TestEventsBackupHandlerUnitSuite(t *testing.T) {
suite.Run(t, &EventsBackupHandlerUnitSuite{Suite: tester.NewUnitSuite(t)})
}
func (suite *EventsBackupHandlerUnitSuite) TestHandler_CanSkipItemFailure() {
var (
resourceID = uuid.NewString()
itemID = uuid.NewString()
)
table := []struct {
name string
err error
opts control.Options
expect assert.BoolAssertionFunc
expectCause fault.SkipCause
}{
{
name: "no config",
err: nil,
opts: control.Options{},
expect: assert.False,
},
{
name: "empty skip on 503",
err: nil,
opts: control.Options{
SkipTheseEventsOnInstance503: map[string][]string{},
},
expect: assert.False,
},
{
name: "nil error",
err: nil,
opts: control.Options{
SkipTheseEventsOnInstance503: map[string][]string{
"foo": []string{"bar", "baz"},
},
},
expect: assert.False,
},
{
name: "non-matching resource",
err: clues.New("fix me I'm wrong"),
opts: control.Options{
SkipTheseEventsOnInstance503: map[string][]string{
"foo": []string{"bar", "baz"},
},
},
expect: assert.False,
},
{
name: "non-matching item",
err: clues.New("fix me I'm wrong"),
opts: control.Options{
SkipTheseEventsOnInstance503: map[string][]string{
resourceID: []string{"bar", "baz"},
},
},
expect: assert.False,
},
{
name: "match on instance 503",
err: clues.New("fix me I'm wrong"),
opts: control.Options{
SkipTheseEventsOnInstance503: map[string][]string{
resourceID: []string{"bar", itemID},
},
},
expect: assert.True,
expectCause: fault.SkipKnownEventInstance503s,
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
h := newEventBackupHandler(api.Client{})
cause, result := h.CanSkipItemFailure(
test.err,
resourceID,
itemID,
test.opts)
test.expect(t, result)
assert.Equal(t, test.expectCause, cause)
})
}
}

View File

@ -26,6 +26,8 @@ type backupHandler interface {
previewIncludeContainers() []string
previewExcludeContainers() []string
NewContainerCache(userID string) (string, graph.ContainerResolver)
canSkipItemFailurer
}
type addedAndRemovedItemGetter interface {
@ -57,6 +59,14 @@ func BackupHandlers(ac api.Client) map[path.CategoryType]backupHandler {
}
}
type canSkipItemFailurer interface {
CanSkipItemFailure(
err error,
resourceID, itemID string,
opts control.Options,
) (fault.SkipCause, bool)
}
// ---------------------------------------------------------------------------
// restore
// ---------------------------------------------------------------------------

View File

@ -1,6 +1,8 @@
package exchange
import (
"github.com/alcionai/corso/src/pkg/control"
"github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/services/m365/api"
"github.com/alcionai/corso/src/pkg/services/m365/api/graph"
)
@ -57,3 +59,11 @@ func (h mailBackupHandler) NewContainerCache(
getter: h.ac,
}
}
func (h mailBackupHandler) CanSkipItemFailure(
error,
string, string,
control.Options,
) (fault.SkipCause, bool) {
return "", false
}

View File

@ -0,0 +1,31 @@
package exchange
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/control"
"github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/services/m365/api"
)
type MailBackupHandlerUnitSuite struct {
tester.Suite
}
func TestMailBackupHandlerUnitSuite(t *testing.T) {
suite.Run(t, &MailBackupHandlerUnitSuite{Suite: tester.NewUnitSuite(t)})
}
func (suite *MailBackupHandlerUnitSuite) TestHandler_CanSkipItemFailure() {
t := suite.T()
h := newMailBackupHandler(api.Client{})
cause, result := h.CanSkipItemFailure(nil, "", "", control.Options{})
assert.False(t, result)
assert.Equal(t, fault.SkipCause(""), cause)
}