diff --git a/src/cli/backup/exchange.go b/src/cli/backup/exchange.go index 5a12dba87..149da9f71 100644 --- a/src/cli/backup/exchange.go +++ b/src/cli/backup/exchange.go @@ -12,7 +12,7 @@ import ( "github.com/alcionai/corso/src/cli/options" . "github.com/alcionai/corso/src/cli/print" "github.com/alcionai/corso/src/cli/utils" - "github.com/alcionai/corso/src/internal/kopia" + "github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/model" "github.com/alcionai/corso/src/pkg/backup" "github.com/alcionai/corso/src/pkg/backup/details" @@ -323,16 +323,16 @@ func createExchangeCmd(cmd *cobra.Command, args []string) error { return nil } -func exchangeBackupCreateSelectors(userIDs, data []string) *selectors.ExchangeBackup { +func exchangeBackupCreateSelectors(userIDs, cats []string) *selectors.ExchangeBackup { sel := selectors.NewExchangeBackup(userIDs) - if len(data) == 0 { + if len(cats) == 0 { sel.Include(sel.ContactFolders(selectors.Any())) sel.Include(sel.MailFolders(selectors.Any())) sel.Include(sel.EventCalendars(selectors.Any())) } - for _, d := range data { + for _, d := range cats { switch d { case dataContacts: sel.Include(sel.ContactFolders(selectors.Any())) @@ -346,12 +346,12 @@ func exchangeBackupCreateSelectors(userIDs, data []string) *selectors.ExchangeBa return sel } -func validateExchangeBackupCreateFlags(userIDs, data []string) error { +func validateExchangeBackupCreateFlags(userIDs, cats []string) error { if len(userIDs) == 0 { return errors.New("--user requires one or more email addresses or the wildcard '*'") } - for _, d := range data { + for _, d := range cats { if d != dataContacts && d != dataEmail && d != dataEvents { return errors.New( d + " is an unrecognized data type; must be one of " + dataContacts + ", " + dataEmail + ", or " + dataEvents) @@ -394,7 +394,7 @@ func listExchangeCmd(cmd *cobra.Command, args []string) error { if len(backupID) > 0 { b, err := r.Backup(ctx, model.StableID(backupID)) if err != nil { - if errors.Is(err, kopia.ErrNotFound) { + if errors.Is(err, data.ErrNotFound) { return Only(ctx, errors.Errorf("No backup exists with the id %s", backupID)) } @@ -502,7 +502,7 @@ func runDetailsExchangeCmd( d, _, errs := r.BackupDetails(ctx, backupID) // TODO: log/track recoverable errors if errs.Err() != nil { - if errors.Is(errs.Err(), kopia.ErrNotFound) { + if errors.Is(errs.Err(), data.ErrNotFound) { return nil, errors.Errorf("No backup exists with the id %s", backupID) } diff --git a/src/cli/backup/onedrive.go b/src/cli/backup/onedrive.go index 2b60432ff..a99ad6d2b 100644 --- a/src/cli/backup/onedrive.go +++ b/src/cli/backup/onedrive.go @@ -12,7 +12,7 @@ import ( "github.com/alcionai/corso/src/cli/options" . "github.com/alcionai/corso/src/cli/print" "github.com/alcionai/corso/src/cli/utils" - "github.com/alcionai/corso/src/internal/kopia" + "github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/model" "github.com/alcionai/corso/src/pkg/backup" "github.com/alcionai/corso/src/pkg/backup/details" @@ -294,7 +294,7 @@ func listOneDriveCmd(cmd *cobra.Command, args []string) error { if len(backupID) > 0 { b, err := r.Backup(ctx, model.StableID(backupID)) if err != nil { - if errors.Is(err, kopia.ErrNotFound) { + if errors.Is(err, data.ErrNotFound) { return Only(ctx, errors.Errorf("No backup exists with the id %s", backupID)) } @@ -394,7 +394,7 @@ func runDetailsOneDriveCmd( d, _, errs := r.BackupDetails(ctx, backupID) // TODO: log/track recoverable errors if errs.Err() != nil { - if errors.Is(errs.Err(), kopia.ErrNotFound) { + if errors.Is(errs.Err(), data.ErrNotFound) { return nil, errors.Errorf("no backup exists with the id %s", backupID) } diff --git a/src/cli/backup/sharepoint.go b/src/cli/backup/sharepoint.go index c2a155334..12dd002d4 100644 --- a/src/cli/backup/sharepoint.go +++ b/src/cli/backup/sharepoint.go @@ -14,7 +14,7 @@ import ( "github.com/alcionai/corso/src/cli/utils" "github.com/alcionai/corso/src/internal/connector" "github.com/alcionai/corso/src/internal/connector/graph" - "github.com/alcionai/corso/src/internal/kopia" + "github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/model" "github.com/alcionai/corso/src/pkg/backup" "github.com/alcionai/corso/src/pkg/backup/details" @@ -266,7 +266,7 @@ func createSharePointCmd(cmd *cobra.Command, args []string) error { return nil } -func validateSharePointBackupCreateFlags(sites, weburls, data []string) error { +func validateSharePointBackupCreateFlags(sites, weburls, cats []string) error { if len(sites) == 0 && len(weburls) == 0 { return errors.New( "requires one or more --" + @@ -276,7 +276,7 @@ func validateSharePointBackupCreateFlags(sites, weburls, data []string) error { ) } - for _, d := range data { + for _, d := range cats { if d != dataLibraries && d != dataPages { return errors.New( d + " is an unrecognized data type; either " + dataLibraries + "or " + dataPages, @@ -290,7 +290,7 @@ func validateSharePointBackupCreateFlags(sites, weburls, data []string) error { // TODO: users might specify a data type, this only supports AllData(). func sharePointBackupCreateSelectors( ctx context.Context, - sites, weburls, data []string, + sites, weburls, cats []string, gc *connector.GraphConnector, ) (*selectors.SharePointBackup, error) { if len(sites) == 0 && len(weburls) == 0 { @@ -321,13 +321,13 @@ func sharePointBackupCreateSelectors( } sel := selectors.NewSharePointBackup(union) - if len(data) == 0 { + if len(cats) == 0 { sel.Include(sel.AllData()) return sel, nil } - for _, d := range data { + for _, d := range cats { switch d { case dataLibraries: sel.Include(sel.Libraries(selectors.Any())) @@ -372,7 +372,7 @@ func listSharePointCmd(cmd *cobra.Command, args []string) error { if len(backupID) > 0 { b, err := r.Backup(ctx, model.StableID(backupID)) if err != nil { - if errors.Is(err, kopia.ErrNotFound) { + if errors.Is(err, data.ErrNotFound) { return Only(ctx, errors.Errorf("No backup exists with the id %s", backupID)) } @@ -513,7 +513,7 @@ func runDetailsSharePointCmd( d, _, errs := r.BackupDetails(ctx, backupID) // TODO: log/track recoverable errors if errs.Err() != nil { - if errors.Is(errs.Err(), kopia.ErrNotFound) { + if errors.Is(errs.Err(), data.ErrNotFound) { return nil, errors.Errorf("no backup exists with the id %s", backupID) } diff --git a/src/cli/restore/exchange.go b/src/cli/restore/exchange.go index d0801061f..6188ed89c 100644 --- a/src/cli/restore/exchange.go +++ b/src/cli/restore/exchange.go @@ -10,7 +10,7 @@ import ( . "github.com/alcionai/corso/src/cli/print" "github.com/alcionai/corso/src/cli/utils" "github.com/alcionai/corso/src/internal/common" - "github.com/alcionai/corso/src/internal/kopia" + "github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/pkg/control" "github.com/alcionai/corso/src/pkg/repository" ) @@ -228,7 +228,7 @@ func restoreExchangeCmd(cmd *cobra.Command, args []string) error { ds, err := ro.Run(ctx) if err != nil { - if errors.Is(err, kopia.ErrNotFound) { + if errors.Is(err, data.ErrNotFound) { return Only(ctx, errors.Errorf("Backup or backup details missing for id %s", backupID)) } diff --git a/src/cli/restore/onedrive.go b/src/cli/restore/onedrive.go index bd8dc7816..7c4ac1337 100644 --- a/src/cli/restore/onedrive.go +++ b/src/cli/restore/onedrive.go @@ -10,7 +10,7 @@ import ( . "github.com/alcionai/corso/src/cli/print" "github.com/alcionai/corso/src/cli/utils" "github.com/alcionai/corso/src/internal/common" - "github.com/alcionai/corso/src/internal/kopia" + "github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/pkg/control" "github.com/alcionai/corso/src/pkg/repository" ) @@ -171,7 +171,7 @@ func restoreOneDriveCmd(cmd *cobra.Command, args []string) error { ds, err := ro.Run(ctx) if err != nil { - if errors.Is(err, kopia.ErrNotFound) { + if errors.Is(err, data.ErrNotFound) { return Only(ctx, errors.Errorf("Backup or backup details missing for id %s", backupID)) } diff --git a/src/cli/restore/sharepoint.go b/src/cli/restore/sharepoint.go index 13414f6b6..fb30d9587 100644 --- a/src/cli/restore/sharepoint.go +++ b/src/cli/restore/sharepoint.go @@ -10,7 +10,7 @@ import ( . "github.com/alcionai/corso/src/cli/print" "github.com/alcionai/corso/src/cli/utils" "github.com/alcionai/corso/src/internal/common" - "github.com/alcionai/corso/src/internal/kopia" + "github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/pkg/control" "github.com/alcionai/corso/src/pkg/repository" ) @@ -166,7 +166,7 @@ func restoreSharePointCmd(cmd *cobra.Command, args []string) error { ds, err := ro.Run(ctx) if err != nil { - if errors.Is(err, kopia.ErrNotFound) { + if errors.Is(err, data.ErrNotFound) { return Only(ctx, errors.Errorf("Backup or backup details missing for id %s", backupID)) } diff --git a/src/internal/data/data_collection.go b/src/internal/data/data_collection.go index 840268169..794b4bc16 100644 --- a/src/internal/data/data_collection.go +++ b/src/internal/data/data_collection.go @@ -1,6 +1,8 @@ package data import ( + "context" + "errors" "io" "time" @@ -12,6 +14,8 @@ import ( // standard ifaces // ------------------------------------------------------------------------------------------------ +var ErrNotFound = errors.New("not found") + type CollectionState int const ( @@ -66,6 +70,16 @@ type RestoreCollection interface { Collection } +// NotFoundRestoreCollection is a wrapper for a Collection that returns +// ErrNotFound for all Fetch calls. +type NotFoundRestoreCollection struct { + Collection +} + +func (c NotFoundRestoreCollection) Fetch(context.Context, string) (Stream, error) { + return nil, ErrNotFound +} + // Stream represents a single item within a Collection // that can be consumed as a stream (it embeds io.Reader) type Stream interface { diff --git a/src/internal/kopia/model_store.go b/src/internal/kopia/model_store.go index 495e94cff..2c6661d22 100644 --- a/src/internal/kopia/model_store.go +++ b/src/internal/kopia/model_store.go @@ -10,6 +10,7 @@ import ( "github.com/pkg/errors" "golang.org/x/exp/maps" + "github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/model" ) @@ -20,7 +21,6 @@ const ( ) var ( - ErrNotFound = errors.New("not found") errNoModelStoreID = errors.New("model has no ModelStoreID") errNoStableID = errors.New("model has no StableID") errBadTagKey = errors.New("tag key overlaps with required key") @@ -281,7 +281,7 @@ func (ms *ModelStore) getModelStoreID( } if len(metadata) == 0 { - return "", errors.Wrap(ErrNotFound, "getting ModelStoreID") + return "", errors.Wrap(data.ErrNotFound, "getting ModelStoreID") } if len(metadata) != 1 { @@ -302,7 +302,7 @@ func (ms *ModelStore) Get( ctx context.Context, s model.Schema, id model.StableID, - data model.Model, + m model.Model, ) error { if !s.Valid() { return errors.WithStack(errUnrecognizedSchema) @@ -313,7 +313,7 @@ func (ms *ModelStore) Get( return err } - return transmuteErr(ms.GetWithModelStoreID(ctx, s, modelID, data)) + return transmuteErr(ms.GetWithModelStoreID(ctx, s, modelID, m)) } // GetWithModelStoreID deserializes the model with the given ModelStoreID into @@ -323,7 +323,7 @@ func (ms *ModelStore) GetWithModelStoreID( ctx context.Context, s model.Schema, id manifest.ID, - data model.Model, + m model.Model, ) error { if !s.Valid() { return errors.WithStack(errUnrecognizedSchema) @@ -333,7 +333,7 @@ func (ms *ModelStore) GetWithModelStoreID( return errors.WithStack(errNoModelStoreID) } - metadata, err := ms.c.GetManifest(ctx, id, data) + metadata, err := ms.c.GetManifest(ctx, id, m) if err != nil { return errors.Wrap(transmuteErr(err), "getting model data") } @@ -343,7 +343,7 @@ func (ms *ModelStore) GetWithModelStoreID( } return errors.Wrap( - ms.populateBaseModelFromMetadata(data.Base(), metadata), + ms.populateBaseModelFromMetadata(m.Base(), metadata), "getting model by ID", ) } @@ -457,7 +457,7 @@ func (ms *ModelStore) Delete(ctx context.Context, s model.Schema, id model.Stabl latest, err := ms.getModelStoreID(ctx, s, id) if err != nil { - if errors.Is(err, ErrNotFound) { + if errors.Is(err, data.ErrNotFound) { return nil } @@ -490,7 +490,7 @@ func (ms *ModelStore) DeleteWithModelStoreID(ctx context.Context, id manifest.ID func transmuteErr(err error) error { switch { case errors.Is(err, manifest.ErrNotFound): - return ErrNotFound + return data.ErrNotFound default: return err } diff --git a/src/internal/kopia/model_store_test.go b/src/internal/kopia/model_store_test.go index a984cd479..ab697fb76 100644 --- a/src/internal/kopia/model_store_test.go +++ b/src/internal/kopia/model_store_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" + "github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/model" "github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/pkg/backup" @@ -360,9 +361,9 @@ func (suite *ModelStoreIntegrationSuite) TestPutGet_WithTags() { func (suite *ModelStoreIntegrationSuite) TestGet_NotFoundErrors() { t := suite.T() - assert.ErrorIs(t, suite.m.Get(suite.ctx, model.BackupOpSchema, "baz", nil), ErrNotFound) + assert.ErrorIs(t, suite.m.Get(suite.ctx, model.BackupOpSchema, "baz", nil), data.ErrNotFound) assert.ErrorIs( - t, suite.m.GetWithModelStoreID(suite.ctx, model.BackupOpSchema, "baz", nil), ErrNotFound) + t, suite.m.GetWithModelStoreID(suite.ctx, model.BackupOpSchema, "baz", nil), data.ErrNotFound) } func (suite *ModelStoreIntegrationSuite) TestPutGetOfTypeBadVersion() { @@ -630,7 +631,7 @@ func (suite *ModelStoreIntegrationSuite) TestPutUpdate() { } err = m.GetWithModelStoreID(ctx, theModelType, oldModelID, nil) - assert.ErrorIs(t, err, ErrNotFound) + assert.ErrorIs(t, err, data.ErrNotFound) }) } } @@ -691,7 +692,7 @@ func (suite *ModelStoreIntegrationSuite) TestPutDelete() { returned := &fooModel{} err := suite.m.GetWithModelStoreID(suite.ctx, theModelType, foo.ModelStoreID, returned) - assert.ErrorIs(t, err, ErrNotFound) + assert.ErrorIs(t, err, data.ErrNotFound) } func (suite *ModelStoreIntegrationSuite) TestPutDelete_BadIDsNoop() { @@ -775,7 +776,7 @@ func (suite *ModelStoreRegressionSuite) TestFailDuringWriteSessionHasNoVisibleEf assert.ErrorIs(t, err, assert.AnError) err = m.GetWithModelStoreID(ctx, theModelType, newID, nil) - assert.ErrorIs(t, err, ErrNotFound) + assert.ErrorIs(t, err, data.ErrNotFound) returned := &fooModel{} require.NoError( diff --git a/src/internal/kopia/wrapper.go b/src/internal/kopia/wrapper.go index d78e874c6..db2f8e645 100644 --- a/src/internal/kopia/wrapper.go +++ b/src/internal/kopia/wrapper.go @@ -317,7 +317,7 @@ func getItemStream( ) if err != nil { if strings.Contains(err.Error(), "entry not found") { - err = errors.Wrap(ErrNotFound, err.Error()) + err = errors.Wrap(data.ErrNotFound, err.Error()) } return nil, errors.Wrap(err, "getting nested object handle") diff --git a/src/internal/kopia/wrapper_test.go b/src/internal/kopia/wrapper_test.go index 4761adc33..03f42bcc3 100644 --- a/src/internal/kopia/wrapper_test.go +++ b/src/internal/kopia/wrapper_test.go @@ -500,7 +500,7 @@ func (suite *KopiaIntegrationSuite) TestBackupCollections_ReaderError() { // Files that had an error shouldn't make a dir entry in kopia. If they do we // may run into kopia-assisted incrementals issues because only mod time and // not file size is checked for StreamingFiles. - assert.ErrorIs(t, err, ErrNotFound, "errored file is restorable") + assert.ErrorIs(t, err, data.ErrNotFound, "errored file is restorable") } type backedupFile struct { diff --git a/src/internal/operations/manifests.go b/src/internal/operations/manifests.go index dcfb415c9..86ad7f1b5 100644 --- a/src/internal/operations/manifests.go +++ b/src/internal/operations/manifests.go @@ -97,7 +97,7 @@ func produceManifestsAndMetadata( if err != nil { // if no backup exists for any of the complete manifests, we want // to fall back to a complete backup. - if errors.Is(err, kopia.ErrNotFound) { + if errors.Is(err, data.ErrNotFound) { logger.Ctx(ctx).Infow("backup missing, falling back to full backup", clues.In(mctx).Slice()...) return ms, nil, false, nil } @@ -118,7 +118,7 @@ func produceManifestsAndMetadata( } colls, err := collectMetadata(mctx, mr, man, metadataFiles, tenantID) - if err != nil && !errors.Is(err, kopia.ErrNotFound) { + if err != nil && !errors.Is(err, data.ErrNotFound) { // prior metadata isn't guaranteed to exist. // if it doesn't, we'll just have to do a // full backup for that data.