Do not return an error if folder was deleted (#1849)

## Description

When backing up Exchange, don't return an error if the folder/calendar we're trying to fetch item IDs for has been deleted.

Error codes pulled with graph explorer API

Manually tested with an Exchange mail backup

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

- [ ]  Yes, it's included
- [ ] 🕐 Yes, but in a later PR
- [x]  No 

## Type of change

<!--- Please check the type of change your PR introduces: --->
- [ ] 🌻 Feature
- [x] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Test
- [ ] 💻 CI/Deployment
- [ ] 🐹 Trivial/Minor

## Issue(s)

* closes #1846 

## Test Plan

<!-- How will this be tested prior to merging.-->
- [x] 💪 Manual
- [ ]  Unit test
- [ ] 💚 E2E
This commit is contained in:
ashmrtn 2022-12-16 16:06:44 -08:00 committed by GitHub
parent 6c72eefdae
commit 715e436dd9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -7,6 +7,7 @@ import (
multierror "github.com/hashicorp/go-multierror" multierror "github.com/hashicorp/go-multierror"
"github.com/microsoftgraph/msgraph-sdk-go/models" "github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors"
msuser "github.com/microsoftgraph/msgraph-sdk-go/users" msuser "github.com/microsoftgraph/msgraph-sdk-go/users"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -19,6 +20,24 @@ import (
"github.com/alcionai/corso/src/pkg/selectors" "github.com/alcionai/corso/src/pkg/selectors"
) )
const (
errEmailFolderNotFound = "ErrorSyncFolderNotFound"
errItemNotFound = "ErrorItemNotFound"
)
var errContainerDeleted = errors.New("container deleted")
func hasErrorCode(err error, code string) bool {
var oDataError *odataerrors.ODataError
if !errors.As(err, &oDataError) {
return false
}
return oDataError.GetError() != nil &&
oDataError.GetError().GetCode() != nil &&
*oDataError.GetError().GetCode() == code
}
// FilterContainersAndFillCollections is a utility function // FilterContainersAndFillCollections is a utility function
// that places the M365 object ids belonging to specific directories // that places the M365 object ids belonging to specific directories
// into a Collection. Messages outside of those directories are omitted. // into a Collection. Messages outside of those directories are omitted.
@ -75,6 +94,30 @@ func FilterContainersAndFillCollections(
continue continue
} }
fetchFunc, err := getFetchIDFunc(qp.Category)
if err != nil {
errs = support.WrapAndAppend(qp.ResourceOwner, err, errs)
continue
}
var deleted bool
jobs, delta, err := fetchFunc(ctx, service, qp.ResourceOwner, cID, dps.deltas[cID])
if err != nil && !errors.Is(err, errContainerDeleted) {
deleted = true
errs = support.WrapAndAppend(qp.ResourceOwner, err, errs)
}
if len(delta) > 0 {
deltaURLs[cID] = delta
}
// Delay creating the new container so we can handle setting the current
// path correctly if the folder was deleted.
if deleted {
dirPath = nil
}
edc := NewCollection( edc := NewCollection(
qp.ResourceOwner, qp.ResourceOwner,
dirPath, dirPath,
@ -86,23 +129,12 @@ func FilterContainersAndFillCollections(
) )
collections[cID] = &edc collections[cID] = &edc
fetchFunc, err := getFetchIDFunc(qp.Category) if deleted {
if err != nil {
errs = support.WrapAndAppend(qp.ResourceOwner, err, errs)
continue continue
} }
jobs, delta, err := fetchFunc(ctx, edc.service, qp.ResourceOwner, cID, dps.deltas[cID])
if err != nil {
errs = support.WrapAndAppend(qp.ResourceOwner, err, errs)
}
edc.jobs = append(edc.jobs, jobs...) edc.jobs = append(edc.jobs, jobs...)
if len(delta) > 0 {
deltaURLs[cID] = delta
}
// add the current path for the container ID to be used in the next backup // add the current path for the container ID to be used in the next backup
// as the "previous path", for reference in case of a rename or relocation. // as the "previous path", for reference in case of a rename or relocation.
currPaths[cID] = dirPath.Folder() currPaths[cID] = dirPath.Folder()
@ -226,6 +258,14 @@ func FetchEventIDsFromCalendar(
for { for {
resp, err := builder.Get(ctx, options) resp, err := builder.Get(ctx, options)
if err != nil { if err != nil {
if hasErrorCode(err, errItemNotFound) {
// The folder was deleted between the time we populated the container
// cache and when we tried to fetch data for it. All we can do is
// return no jobs because we've only pulled basic info about each
// item.
return nil, "", errors.WithStack(errContainerDeleted)
}
return nil, "", errors.Wrap(err, support.ConnectorStackErrorTrace(err)) return nil, "", errors.Wrap(err, support.ConnectorStackErrorTrace(err))
} }
@ -288,6 +328,14 @@ func FetchContactIDsFromDirectory(
for { for {
resp, err := builder.Get(ctx, options) resp, err := builder.Get(ctx, options)
if err != nil { if err != nil {
if hasErrorCode(err, errItemNotFound) {
// The folder was deleted between the time we populated the container
// cache and when we tried to fetch data for it. All we can do is
// return no jobs because we've only pulled basic info about each
// item.
return nil, "", errors.WithStack(errContainerDeleted)
}
return nil, deltaURL, errors.Wrap(err, support.ConnectorStackErrorTrace(err)) return nil, deltaURL, errors.Wrap(err, support.ConnectorStackErrorTrace(err))
} }
@ -354,6 +402,14 @@ func FetchMessageIDsFromDirectory(
for { for {
resp, err := builder.Get(ctx, options) resp, err := builder.Get(ctx, options)
if err != nil { if err != nil {
if hasErrorCode(err, errEmailFolderNotFound) {
// The folder was deleted between the time we populated the container
// cache and when we tried to fetch data for it. All we can do is
// return no jobs because we've only pulled basic info about each
// item.
return nil, "", errors.WithStack(errContainerDeleted)
}
return nil, deltaURL, errors.Wrap(err, support.ConnectorStackErrorTrace(err)) return nil, deltaURL, errors.Wrap(err, support.ConnectorStackErrorTrace(err))
} }