add clues, fault to selectors (#2335)
## Description Adds clues and fault handling to selectors pkg. Some bleed upward into the CLI occured from where the cli directly calls selectors.Reduce. ## Does this PR need a docs update or release note? - [x] ⛔ No ## Type of change - [x] 🧹 Tech Debt/Cleanup ## Issue(s) * #1970 ## Test Plan - [x] ⚡ Unit test - [x] 💚 E2E
This commit is contained in:
parent
faad5d35a4
commit
5537a11948
@ -16,6 +16,7 @@ import (
|
||||
"github.com/alcionai/corso/src/internal/model"
|
||||
"github.com/alcionai/corso/src/pkg/backup"
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
"github.com/alcionai/corso/src/pkg/repository"
|
||||
"github.com/alcionai/corso/src/pkg/selectors"
|
||||
@ -470,9 +471,10 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error {
|
||||
|
||||
defer utils.CloseRepo(ctx, r)
|
||||
|
||||
ds, err := runDetailsExchangeCmd(ctx, r, backupID, opts)
|
||||
if err != nil {
|
||||
return Only(ctx, err)
|
||||
ds, errs := runDetailsExchangeCmd(ctx, r, backupID, opts)
|
||||
if errs.Err() != nil {
|
||||
// TODO: log/display iterated errors
|
||||
return Only(ctx, errs.Err())
|
||||
}
|
||||
|
||||
if len(ds.Entries) == 0 {
|
||||
@ -486,29 +488,33 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// runDetailsExchangeCmd actually performs the lookup in backup details.
|
||||
// the fault.Errors return is always non-nil. Callers should check if
|
||||
// errs.Err() == nil.
|
||||
func runDetailsExchangeCmd(
|
||||
ctx context.Context,
|
||||
r repository.BackupGetter,
|
||||
backupID string,
|
||||
opts utils.ExchangeOpts,
|
||||
) (*details.Details, error) {
|
||||
) (*details.Details, *fault.Errors) {
|
||||
errs := fault.New(false)
|
||||
|
||||
if err := utils.ValidateExchangeRestoreFlags(backupID, opts); err != nil {
|
||||
return nil, err
|
||||
return nil, errs.Fail(err)
|
||||
}
|
||||
|
||||
d, _, err := r.BackupDetails(ctx, backupID)
|
||||
if err != nil {
|
||||
if errors.Is(err, kopia.ErrNotFound) {
|
||||
return nil, errors.Errorf("No backup exists with the id %s", backupID)
|
||||
return nil, errs.Fail(errors.Errorf("No backup exists with the id %s", backupID))
|
||||
}
|
||||
|
||||
return nil, errors.Wrap(err, "Failed to get backup details in the repository")
|
||||
return nil, errs.Fail(errors.Wrap(err, "Failed to get backup details in the repository"))
|
||||
}
|
||||
|
||||
sel := utils.IncludeExchangeRestoreDataSelectors(opts)
|
||||
utils.FilterExchangeRestoreInfoSelectors(sel, opts)
|
||||
|
||||
return sel.Reduce(ctx, d), nil
|
||||
return sel.Reduce(ctx, d, errs), errs
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
@ -223,33 +223,14 @@ func (suite *ExchangeSuite) TestExchangeBackupDetailsSelectors() {
|
||||
ctx,
|
||||
test.BackupGetter,
|
||||
"backup-ID",
|
||||
test.Opts,
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
|
||||
test.Opts)
|
||||
assert.NoError(t, err.Err(), "failure")
|
||||
assert.Empty(t, err.Errs(), "recovered errors")
|
||||
assert.ElementsMatch(t, test.Expected, output.Entries)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *ExchangeSuite) TestExchangeBackupDetailsSelectorsBadBackupID() {
|
||||
t := suite.T()
|
||||
ctx, flush := tester.NewContext()
|
||||
backupGetter := &testdata.MockBackupGetter{}
|
||||
|
||||
defer flush()
|
||||
|
||||
output, err := runDetailsExchangeCmd(
|
||||
ctx,
|
||||
backupGetter,
|
||||
"backup-ID",
|
||||
utils.ExchangeOpts{},
|
||||
)
|
||||
assert.Error(t, err)
|
||||
|
||||
assert.Empty(t, output)
|
||||
}
|
||||
|
||||
func (suite *ExchangeSuite) TestExchangeBackupDetailsSelectorsBadFormats() {
|
||||
ctx, flush := tester.NewContext()
|
||||
defer flush()
|
||||
@ -260,10 +241,9 @@ func (suite *ExchangeSuite) TestExchangeBackupDetailsSelectorsBadFormats() {
|
||||
ctx,
|
||||
test.BackupGetter,
|
||||
"backup-ID",
|
||||
test.Opts,
|
||||
)
|
||||
|
||||
assert.Error(t, err)
|
||||
test.Opts)
|
||||
assert.Error(t, err.Err(), "failure")
|
||||
assert.Empty(t, err.Errs(), "recovered errors")
|
||||
assert.Empty(t, output)
|
||||
})
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ import (
|
||||
"github.com/alcionai/corso/src/internal/model"
|
||||
"github.com/alcionai/corso/src/pkg/backup"
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
"github.com/alcionai/corso/src/pkg/repository"
|
||||
"github.com/alcionai/corso/src/pkg/selectors"
|
||||
@ -362,9 +363,10 @@ func detailsOneDriveCmd(cmd *cobra.Command, args []string) error {
|
||||
Populated: utils.GetPopulatedFlags(cmd),
|
||||
}
|
||||
|
||||
ds, err := runDetailsOneDriveCmd(ctx, r, backupID, opts)
|
||||
if err != nil {
|
||||
return Only(ctx, err)
|
||||
ds, errs := runDetailsOneDriveCmd(ctx, r, backupID, opts)
|
||||
if errs.Err() != nil {
|
||||
// TODO: log/display iterated errors
|
||||
return Only(ctx, errs.Err())
|
||||
}
|
||||
|
||||
if len(ds.Entries) == 0 {
|
||||
@ -378,29 +380,33 @@ func detailsOneDriveCmd(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// runDetailsOneDriveCmd actually performs the lookup in backup details.
|
||||
// the fault.Errors return is always non-nil. Callers should check if
|
||||
// errs.Err() == nil.
|
||||
func runDetailsOneDriveCmd(
|
||||
ctx context.Context,
|
||||
r repository.BackupGetter,
|
||||
backupID string,
|
||||
opts utils.OneDriveOpts,
|
||||
) (*details.Details, error) {
|
||||
) (*details.Details, *fault.Errors) {
|
||||
errs := fault.New(false)
|
||||
|
||||
if err := utils.ValidateOneDriveRestoreFlags(backupID, opts); err != nil {
|
||||
return nil, err
|
||||
return nil, errs.Fail(err)
|
||||
}
|
||||
|
||||
d, _, err := r.BackupDetails(ctx, backupID)
|
||||
if err != nil {
|
||||
if errors.Is(err, kopia.ErrNotFound) {
|
||||
return nil, errors.Errorf("no backup exists with the id %s", backupID)
|
||||
return nil, errs.Fail(errors.Errorf("no backup exists with the id %s", backupID))
|
||||
}
|
||||
|
||||
return nil, errors.Wrap(err, "Failed to get backup details in the repository")
|
||||
return nil, errs.Fail(errors.Wrap(err, "Failed to get backup details in the repository"))
|
||||
}
|
||||
|
||||
sel := utils.IncludeOneDriveRestoreDataSelectors(opts)
|
||||
utils.FilterOneDriveRestoreInfoSelectors(sel, opts)
|
||||
|
||||
return sel.Reduce(ctx, d), nil
|
||||
return sel.Reduce(ctx, d, errs), errs
|
||||
}
|
||||
|
||||
// `corso backup delete onedrive [<flag>...]`
|
||||
|
||||
@ -98,10 +98,9 @@ func (suite *OneDriveSuite) TestOneDriveBackupDetailsSelectors() {
|
||||
ctx,
|
||||
test.BackupGetter,
|
||||
"backup-ID",
|
||||
test.Opts,
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
|
||||
test.Opts)
|
||||
assert.NoError(t, err.Err())
|
||||
assert.Empty(t, err.Errs())
|
||||
assert.ElementsMatch(t, test.Expected, output.Entries)
|
||||
})
|
||||
}
|
||||
@ -117,10 +116,9 @@ func (suite *OneDriveSuite) TestOneDriveBackupDetailsSelectorsBadFormats() {
|
||||
ctx,
|
||||
test.BackupGetter,
|
||||
"backup-ID",
|
||||
test.Opts,
|
||||
)
|
||||
|
||||
assert.Error(t, err)
|
||||
test.Opts)
|
||||
assert.Error(t, err.Err())
|
||||
assert.Empty(t, err.Errs())
|
||||
assert.Empty(t, output)
|
||||
})
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ import (
|
||||
"github.com/alcionai/corso/src/internal/model"
|
||||
"github.com/alcionai/corso/src/pkg/backup"
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
"github.com/alcionai/corso/src/pkg/repository"
|
||||
"github.com/alcionai/corso/src/pkg/selectors"
|
||||
@ -481,9 +482,10 @@ func detailsSharePointCmd(cmd *cobra.Command, args []string) error {
|
||||
Populated: utils.GetPopulatedFlags(cmd),
|
||||
}
|
||||
|
||||
ds, err := runDetailsSharePointCmd(ctx, r, backupID, opts)
|
||||
if err != nil {
|
||||
return Only(ctx, err)
|
||||
ds, errs := runDetailsSharePointCmd(ctx, r, backupID, opts)
|
||||
if errs.Err() != nil {
|
||||
// TODO: log/display iterated errors
|
||||
return Only(ctx, errs.Err())
|
||||
}
|
||||
|
||||
if len(ds.Entries) == 0 {
|
||||
@ -497,27 +499,31 @@ func detailsSharePointCmd(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// runDetailsSharePointCmd actually performs the lookup in backup details.
|
||||
// the fault.Errors return is always non-nil. Callers should check if
|
||||
// errs.Err() == nil.
|
||||
func runDetailsSharePointCmd(
|
||||
ctx context.Context,
|
||||
r repository.BackupGetter,
|
||||
backupID string,
|
||||
opts utils.SharePointOpts,
|
||||
) (*details.Details, error) {
|
||||
) (*details.Details, *fault.Errors) {
|
||||
errs := fault.New(false)
|
||||
|
||||
if err := utils.ValidateSharePointRestoreFlags(backupID, opts); err != nil {
|
||||
return nil, err
|
||||
return nil, errs.Fail(err)
|
||||
}
|
||||
|
||||
d, _, err := r.BackupDetails(ctx, backupID)
|
||||
if err != nil {
|
||||
if errors.Is(err, kopia.ErrNotFound) {
|
||||
return nil, errors.Errorf("no backup exists with the id %s", backupID)
|
||||
return nil, errs.Fail(errors.Errorf("no backup exists with the id %s", backupID))
|
||||
}
|
||||
|
||||
return nil, errors.Wrap(err, "Failed to get backup details in the repository")
|
||||
return nil, errs.Fail(errors.Wrap(err, "Failed to get backup details in the repository"))
|
||||
}
|
||||
|
||||
sel := utils.IncludeSharePointRestoreDataSelectors(opts)
|
||||
utils.FilterSharePointRestoreInfoSelectors(sel, opts)
|
||||
|
||||
return sel.Reduce(ctx, d), nil
|
||||
return sel.Reduce(ctx, d, errs), errs
|
||||
}
|
||||
|
||||
@ -213,10 +213,9 @@ func (suite *SharePointSuite) TestSharePointBackupDetailsSelectors() {
|
||||
ctx,
|
||||
test.BackupGetter,
|
||||
"backup-ID",
|
||||
test.Opts,
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
|
||||
test.Opts)
|
||||
assert.NoError(t, err.Err())
|
||||
assert.Empty(t, err.Errs())
|
||||
assert.ElementsMatch(t, test.Expected, output.Entries)
|
||||
})
|
||||
}
|
||||
@ -232,10 +231,9 @@ func (suite *SharePointSuite) TestSharePointBackupDetailsSelectorsBadFormats() {
|
||||
ctx,
|
||||
test.BackupGetter,
|
||||
"backup-ID",
|
||||
test.Opts,
|
||||
)
|
||||
|
||||
assert.Error(t, err)
|
||||
test.Opts)
|
||||
assert.Error(t, err.Err())
|
||||
assert.Empty(t, err.Errs())
|
||||
assert.Empty(t, output)
|
||||
})
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ go 1.19
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0
|
||||
github.com/alcionai/clues v0.0.0-20230131232239-cee86233b005
|
||||
github.com/alcionai/clues v0.0.0-20230202001016-cbda58c9de9e
|
||||
github.com/aws/aws-sdk-go v1.44.192
|
||||
github.com/aws/aws-xray-sdk-go v1.8.0
|
||||
github.com/google/uuid v1.3.0
|
||||
|
||||
@ -54,6 +54,8 @@ github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpH
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
|
||||
github.com/alcionai/clues v0.0.0-20230131232239-cee86233b005 h1:eTgICcmcydEWG8J+hgnidf0pzujV3Gd2XqmknykZkzA=
|
||||
github.com/alcionai/clues v0.0.0-20230131232239-cee86233b005/go.mod h1:UlAs8jkWIpsOMakiC8NxPgQQVQRdvyf1hYMszlYYLb4=
|
||||
github.com/alcionai/clues v0.0.0-20230202001016-cbda58c9de9e h1:KMRGDB9lh0wC/WYVmQ28MJ07qiHszCSH2PRwkw2YElM=
|
||||
github.com/alcionai/clues v0.0.0-20230202001016-cbda58c9de9e/go.mod h1:UlAs8jkWIpsOMakiC8NxPgQQVQRdvyf1hYMszlYYLb4=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
|
||||
@ -344,7 +344,7 @@ func formatDetailsForRestoration(
|
||||
deets *details.Details,
|
||||
errs *fault.Errors,
|
||||
) ([]path.Path, error) {
|
||||
fds, err := sel.Reduce(ctx, deets)
|
||||
fds, err := sel.Reduce(ctx, deets, errs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/selectors"
|
||||
)
|
||||
|
||||
@ -52,7 +53,7 @@ func Example_newSelector() {
|
||||
bSel.ToOneDriveBackup()
|
||||
}
|
||||
|
||||
// Output: OneDrive service is not Exchange: wrong selector service type
|
||||
// Output: wrong selector service type: OneDrive is not Exchange
|
||||
}
|
||||
|
||||
// ExampleIncludeFoldersAndItems demonstrates how to select for granular data.
|
||||
@ -141,10 +142,11 @@ func Example_reduceDetails() {
|
||||
ser := selectors.NewExchangeRestore(
|
||||
[]string{"your-user-id", "foo-user-id", "bar-user-id"},
|
||||
)
|
||||
errAgg := fault.New(false)
|
||||
|
||||
// The Reduce() call is where our constructed selectors are applied to the data
|
||||
// from a previous backup record.
|
||||
filteredDetails := ser.Reduce(ctxBG, exampleDetails)
|
||||
filteredDetails := ser.Reduce(ctxBG, exampleDetails, errAgg)
|
||||
|
||||
// We haven't added any scopes to our selector yet, so none of the data is retained.
|
||||
fmt.Println("Before adding scopes:", len(filteredDetails.Entries))
|
||||
@ -153,7 +155,7 @@ func Example_reduceDetails() {
|
||||
ser.Filter(ser.MailSubject("the answer to life"))
|
||||
|
||||
// Now that we've selected our data, we should find a result.
|
||||
filteredDetails = ser.Reduce(ctxBG, exampleDetails)
|
||||
filteredDetails = ser.Reduce(ctxBG, exampleDetails, errAgg)
|
||||
fmt.Println("After adding scopes:", len(filteredDetails.Entries))
|
||||
|
||||
// Output: Before adding scopes: 0
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/alcionai/corso/src/internal/common"
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/filters"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
@ -704,7 +705,11 @@ func (s ExchangeScope) setDefaults() {
|
||||
|
||||
// Reduce filters the entries in a details struct to only those that match the
|
||||
// inclusions, filters, and exclusions in the selector.
|
||||
func (s exchange) Reduce(ctx context.Context, deets *details.Details) *details.Details {
|
||||
func (s exchange) Reduce(
|
||||
ctx context.Context,
|
||||
deets *details.Details,
|
||||
errs fault.Adder,
|
||||
) *details.Details {
|
||||
return reduce[ExchangeScope](
|
||||
ctx,
|
||||
deets,
|
||||
@ -714,7 +719,7 @@ func (s exchange) Reduce(ctx context.Context, deets *details.Details) *details.D
|
||||
path.EventsCategory: ExchangeEvent,
|
||||
path.EmailCategory: ExchangeMail,
|
||||
},
|
||||
)
|
||||
errs)
|
||||
}
|
||||
|
||||
// matchesInfo handles the standard behavior when comparing a scope and an ExchangeFilter
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"github.com/alcionai/corso/src/internal/common"
|
||||
"github.com/alcionai/corso/src/internal/tester"
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/fault/mock"
|
||||
"github.com/alcionai/corso/src/pkg/filters"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
@ -1029,10 +1030,13 @@ func (suite *ExchangeSelectorSuite) TestExchangeRestore_Reduce() {
|
||||
ctx, flush := tester.NewContext()
|
||||
defer flush()
|
||||
|
||||
errs := mock.NewAdder()
|
||||
|
||||
sel := test.makeSelector()
|
||||
results := sel.Reduce(ctx, test.deets)
|
||||
results := sel.Reduce(ctx, test.deets, errs)
|
||||
paths := results.Paths()
|
||||
assert.Equal(t, test.expect, paths)
|
||||
assert.Empty(t, errs.Errs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/alcionai/corso/src/internal/common"
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/filters"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
@ -483,7 +484,11 @@ func (s OneDriveScope) DiscreteCopy(user string) OneDriveScope {
|
||||
|
||||
// Reduce filters the entries in a details struct to only those that match the
|
||||
// inclusions, filters, and exclusions in the selector.
|
||||
func (s oneDrive) Reduce(ctx context.Context, deets *details.Details) *details.Details {
|
||||
func (s oneDrive) Reduce(
|
||||
ctx context.Context,
|
||||
deets *details.Details,
|
||||
errs fault.Adder,
|
||||
) *details.Details {
|
||||
return reduce[OneDriveScope](
|
||||
ctx,
|
||||
deets,
|
||||
@ -491,7 +496,7 @@ func (s oneDrive) Reduce(ctx context.Context, deets *details.Details) *details.D
|
||||
map[path.CategoryType]oneDriveCategory{
|
||||
path.FilesCategory: OneDriveItem,
|
||||
},
|
||||
)
|
||||
errs)
|
||||
}
|
||||
|
||||
// matchesInfo handles the standard behavior when comparing a scope and an oneDriveInfo
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"github.com/alcionai/corso/src/internal/common"
|
||||
"github.com/alcionai/corso/src/internal/tester"
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/fault/mock"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
|
||||
@ -241,10 +242,13 @@ func (suite *OneDriveSelectorSuite) TestOneDriveRestore_Reduce() {
|
||||
ctx, flush := tester.NewContext()
|
||||
defer flush()
|
||||
|
||||
errs := mock.NewAdder()
|
||||
|
||||
sel := test.makeSelector()
|
||||
results := sel.Reduce(ctx, test.deets)
|
||||
results := sel.Reduce(ctx, test.deets, errs)
|
||||
paths := results.Paths()
|
||||
assert.Equal(t, test.expect, paths)
|
||||
assert.Empty(t, errs.Errs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,10 +5,11 @@ import (
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/alcionai/clues"
|
||||
D "github.com/alcionai/corso/src/internal/diagnostics"
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/filters"
|
||||
"github.com/alcionai/corso/src/pkg/logger"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
|
||||
@ -286,6 +287,7 @@ func reduce[T scopeT, C categoryT](
|
||||
deets *details.Details,
|
||||
s Selector,
|
||||
dataCategories map[path.CategoryType]C,
|
||||
errs fault.Adder,
|
||||
) *details.Details {
|
||||
ctx, end := D.Span(ctx, "selectors:reduce")
|
||||
defer end()
|
||||
@ -311,7 +313,7 @@ func reduce[T scopeT, C categoryT](
|
||||
for _, ent := range deets.Items() {
|
||||
repoPath, err := path.FromDataLayerPath(ent.RepoRef, true)
|
||||
if err != nil {
|
||||
logger.Ctx(ctx).Debugw("transforming repoRef to path", "err", err)
|
||||
errs.Add(clues.Wrap(err, "transforming repoRef to path").WithClues(ctx))
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/alcionai/corso/src/internal/tester"
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/fault/mock"
|
||||
"github.com/alcionai/corso/src/pkg/filters"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
@ -273,13 +274,17 @@ func (suite *SelectorScopesSuite) TestReduce() {
|
||||
ctx, flush := tester.NewContext()
|
||||
defer flush()
|
||||
|
||||
errs := mock.NewAdder()
|
||||
|
||||
ds := deets()
|
||||
result := reduce[mockScope](
|
||||
ctx,
|
||||
&ds,
|
||||
test.sel().Selector,
|
||||
dataCats)
|
||||
dataCats,
|
||||
errs)
|
||||
require.NotNil(t, result)
|
||||
require.Empty(t, errs.Errs, "iteration errors")
|
||||
assert.Len(t, result.Entries, test.expectLen)
|
||||
})
|
||||
}
|
||||
|
||||
@ -5,9 +5,11 @@ import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/alcionai/clues"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/filters"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
@ -30,8 +32,9 @@ var serviceToPathType = map[service]path.ServiceType{
|
||||
}
|
||||
|
||||
var (
|
||||
ErrorBadSelectorCast = errors.New("wrong selector service type")
|
||||
ErrorNoMatchingItems = errors.New("no items match the specified selectors")
|
||||
ErrorBadSelectorCast = errors.New("wrong selector service type")
|
||||
ErrorNoMatchingItems = errors.New("no items match the specified selectors")
|
||||
ErrorUnrecognizedService = errors.New("unrecognized service")
|
||||
)
|
||||
|
||||
const (
|
||||
@ -67,7 +70,7 @@ var (
|
||||
const All = "All"
|
||||
|
||||
type Reducer interface {
|
||||
Reduce(context.Context, *details.Details) *details.Details
|
||||
Reduce(context.Context, *details.Details, fault.Adder) *details.Details
|
||||
}
|
||||
|
||||
// selectorResourceOwners aggregates all discrete path category types described
|
||||
@ -234,13 +237,17 @@ func (s Selector) PathService() path.ServiceType {
|
||||
// from the generic selector by interpreting the selector service type rather
|
||||
// than have the caller make that interpretation. Returns an error if the
|
||||
// service is unsupported.
|
||||
func (s Selector) Reduce(ctx context.Context, deets *details.Details) (*details.Details, error) {
|
||||
func (s Selector) Reduce(
|
||||
ctx context.Context,
|
||||
deets *details.Details,
|
||||
errs fault.Adder,
|
||||
) (*details.Details, error) {
|
||||
r, err := selectorAsIface[Reducer](s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.Reduce(ctx, deets), nil
|
||||
return r.Reduce(ctx, deets, errs), nil
|
||||
}
|
||||
|
||||
// returns the sets of path categories identified in each scope set.
|
||||
@ -272,7 +279,7 @@ func selectorAsIface[T any](s Selector) (T, error) {
|
||||
a, err = func() (any, error) { return s.ToSharePointRestore() }()
|
||||
t = a.(T)
|
||||
default:
|
||||
err = errors.New("service not supported: " + s.Service.String())
|
||||
err = clues.Stack(ErrorUnrecognizedService, errors.New(s.Service.String()))
|
||||
}
|
||||
|
||||
return t, err
|
||||
@ -374,7 +381,7 @@ func pathComparator() option {
|
||||
}
|
||||
|
||||
func badCastErr(cast, is service) error {
|
||||
return errors.Wrapf(ErrorBadSelectorCast, "%s service is not %s", cast, is)
|
||||
return clues.Stack(ErrorBadSelectorCast, errors.Errorf("%s is not %s", cast, is))
|
||||
}
|
||||
|
||||
func join(s ...string) string {
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"github.com/alcionai/corso/src/internal/common"
|
||||
"github.com/alcionai/corso/src/internal/tester"
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/fault/mock"
|
||||
"github.com/alcionai/corso/src/pkg/selectors"
|
||||
"github.com/alcionai/corso/src/pkg/selectors/testdata"
|
||||
)
|
||||
@ -264,8 +265,11 @@ func (suite *SelectorReduceSuite) TestReduce() {
|
||||
|
||||
for _, test := range table {
|
||||
suite.T().Run(test.name, func(t *testing.T) {
|
||||
output := test.selFunc().Reduce(ctx, allDetails)
|
||||
errs := mock.NewAdder()
|
||||
|
||||
output := test.selFunc().Reduce(ctx, allDetails, errs)
|
||||
assert.ElementsMatch(t, test.expected, output.Entries)
|
||||
assert.Empty(t, errs.Errs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
|
||||
@ -555,7 +556,11 @@ func (s SharePointScope) DiscreteCopy(site string) SharePointScope {
|
||||
|
||||
// Reduce filters the entries in a details struct to only those that match the
|
||||
// inclusions, filters, and exclusions in the selector.
|
||||
func (s sharePoint) Reduce(ctx context.Context, deets *details.Details) *details.Details {
|
||||
func (s sharePoint) Reduce(
|
||||
ctx context.Context,
|
||||
deets *details.Details,
|
||||
errs fault.Adder,
|
||||
) *details.Details {
|
||||
return reduce[SharePointScope](
|
||||
ctx,
|
||||
deets,
|
||||
@ -565,7 +570,7 @@ func (s sharePoint) Reduce(ctx context.Context, deets *details.Details) *details
|
||||
path.ListsCategory: SharePointListItem,
|
||||
path.PagesCategory: SharePointPage,
|
||||
},
|
||||
)
|
||||
errs)
|
||||
}
|
||||
|
||||
// matchesInfo handles the standard behavior when comparing a scope and an sharePointInfo
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/alcionai/corso/src/internal/tester"
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/fault/mock"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
|
||||
@ -305,10 +306,13 @@ func (suite *SharePointSelectorSuite) TestSharePointRestore_Reduce() {
|
||||
ctx, flush := tester.NewContext()
|
||||
defer flush()
|
||||
|
||||
errs := mock.NewAdder()
|
||||
|
||||
sel := test.makeSelector()
|
||||
results := sel.Reduce(ctx, test.deets)
|
||||
results := sel.Reduce(ctx, test.deets, errs)
|
||||
paths := results.Paths()
|
||||
assert.Equal(t, test.expect, paths)
|
||||
assert.Empty(t, errs.Errs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user