Fix most remaining wsl lint errors (#656)

* Fix wsl lint errors in pkg package

* Fix wsl lint errors in most of internal package

Leave some sub-packages out that have higher churn at the moment.
This commit is contained in:
ashmrtn 2022-08-26 10:58:58 -07:00 committed by GitHub
parent 02c2b0b4d6
commit 09cc2769d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 289 additions and 20 deletions

View File

@ -8,14 +8,17 @@ type StringConfigurer interface {
// map[string]string matching type. // map[string]string matching type.
func UnionStringConfigs(cfgs ...StringConfigurer) (map[string]string, error) { func UnionStringConfigs(cfgs ...StringConfigurer) (map[string]string, error) {
union := map[string]string{} union := map[string]string{}
for _, cfg := range cfgs { for _, cfg := range cfgs {
c, err := cfg.StringConfig() c, err := cfg.StringConfig()
if err != nil { if err != nil {
return nil, err return nil, err
} }
for k, v := range c { for k, v := range c {
union[k] = v union[k] = v
} }
} }
return union, nil return union, nil
} }

View File

@ -43,6 +43,7 @@ func (e Err) Format(s fmt.State, verb rune) {
f.Format(s, verb) f.Format(s, verb)
return return
} }
// Formatting magic courtesy of github.com/pkg/errors. // Formatting magic courtesy of github.com/pkg/errors.
switch verb { switch verb {
case 'v': case 'v':
@ -50,6 +51,7 @@ func (e Err) Format(s fmt.State, verb rune) {
fmt.Fprintf(s, "%+v\n", e.Cause()) fmt.Fprintf(s, "%+v\n", e.Cause())
return return
} }
fallthrough fallthrough
case 's', 'q': case 's', 'q':
// nolint:errcheck // nolint:errcheck

View File

@ -39,6 +39,7 @@ func (suite *ErrorsUnitSuite) TestPropagatesIs() {
err := assert.AnError err := assert.AnError
te := testErr{*common.EncapsulateError(err)} te := testErr{*common.EncapsulateError(err)}
te2 := testErr2{*common.EncapsulateError(te)} te2 := testErr2{*common.EncapsulateError(te)}
assert.True(suite.T(), errors.Is(te2, err)) assert.True(suite.T(), errors.Is(te2, err))
} }
@ -46,7 +47,8 @@ func (suite *ErrorsUnitSuite) TestPropagatesAs() {
err := assert.AnError err := assert.AnError
te := testErr{*common.EncapsulateError(err)} te := testErr{*common.EncapsulateError(err)}
te2 := testErr2{*common.EncapsulateError(te)} te2 := testErr2{*common.EncapsulateError(te)}
var tmp testErr
tmp := testErr{}
assert.True(suite.T(), errors.As(te2, &tmp)) assert.True(suite.T(), errors.As(te2, &tmp))
} }
@ -54,14 +56,16 @@ func (suite *ErrorsUnitSuite) TestAs() {
err := assert.AnError err := assert.AnError
te := testErr{*common.EncapsulateError(err)} te := testErr{*common.EncapsulateError(err)}
te2 := testErr2{*common.EncapsulateError(te)} te2 := testErr2{*common.EncapsulateError(te)}
var tmp testErr2
tmp := testErr2{}
assert.True(suite.T(), errors.As(te2, &tmp)) assert.True(suite.T(), errors.As(te2, &tmp))
} }
func (suite *ErrorsUnitSuite) TestAsIsUnique() { func (suite *ErrorsUnitSuite) TestAsIsUnique() {
err := assert.AnError err := assert.AnError
te := testErr{*common.EncapsulateError(err)} te := testErr{*common.EncapsulateError(err)}
var tmp testErr2
tmp := testErr2{}
assert.False(suite.T(), errors.As(te, &tmp)) assert.False(suite.T(), errors.As(te, &tmp))
} }

View File

@ -6,6 +6,7 @@ func ContainsString(super []string, sub string) bool {
return true return true
} }
} }
return false return false
} }
@ -16,5 +17,6 @@ func First(vs ...string) string {
return v return v
} }
} }
return "" return ""
} }

View File

@ -22,6 +22,7 @@ func (suite *CommonSlicesSuite) TestContainsString() {
target := "fnords" target := "fnords"
good := []string{"fnords"} good := []string{"fnords"}
bad := []string{"foo", "bar"} bad := []string{"foo", "bar"}
assert.True(t, common.ContainsString(good, target)) assert.True(t, common.ContainsString(good, target))
assert.False(t, common.ContainsString(bad, target)) assert.False(t, common.ContainsString(bad, target))
} }

View File

@ -34,13 +34,16 @@ func ParseTime(s string) (time.Time, error) {
if len(s) == 0 { if len(s) == 0 {
return time.Time{}, errors.New("cannot interpret an empty string as time.Time") return time.Time{}, errors.New("cannot interpret an empty string as time.Time")
} }
t, err := time.Parse(StandardTimeFormat, s) t, err := time.Parse(StandardTimeFormat, s)
if err == nil { if err == nil {
return t.UTC(), nil return t.UTC(), nil
} }
t, err = time.Parse(SimpleDateTimeFormat, s) t, err = time.Parse(SimpleDateTimeFormat, s)
if err == nil { if err == nil {
return t.UTC(), nil return t.UTC(), nil
} }
return time.Time{}, errors.New("unable to format time string: " + s) return time.Time{}, errors.New("unable to format time string: " + s)
} }

View File

@ -9,7 +9,6 @@ import (
"github.com/alcionai/corso/internal/connector" "github.com/alcionai/corso/internal/connector"
"github.com/alcionai/corso/internal/connector/support" "github.com/alcionai/corso/internal/connector/support"
"github.com/alcionai/corso/internal/data"
"github.com/alcionai/corso/internal/kopia" "github.com/alcionai/corso/internal/kopia"
"github.com/alcionai/corso/internal/model" "github.com/alcionai/corso/internal/model"
"github.com/alcionai/corso/internal/stats" "github.com/alcionai/corso/internal/stats"
@ -78,13 +77,13 @@ type backupStats struct {
// Run begins a synchronous backup operation. // Run begins a synchronous backup operation.
func (op *BackupOperation) Run(ctx context.Context) (err error) { func (op *BackupOperation) Run(ctx context.Context) (err error) {
// TODO: persist initial state of backupOperation in modelstore
// persist operation results to the model store on exit
var ( var (
opStats backupStats opStats backupStats
backupDetails *details.Details backupDetails *details.Details
) )
// TODO: persist initial state of backupOperation in modelstore
// persist operation results to the model store on exit
defer func() { defer func() {
err = op.persistResults(time.Now(), &opStats) err = op.persistResults(time.Now(), &opStats)
if err != nil { if err != nil {
@ -93,8 +92,8 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) {
err = op.createBackupModels(ctx, opStats.k.SnapshotID, backupDetails) err = op.createBackupModels(ctx, opStats.k.SnapshotID, backupDetails)
if err != nil { if err != nil {
// todo: we're not persisting this yet, except for the error shown to the user.
opStats.writeErr = err opStats.writeErr = err
// todo: ^ we're not persisting this yet, except for the error shown to the user.
} }
}() }()
@ -103,14 +102,15 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) {
if err != nil { if err != nil {
err = errors.Wrap(err, "connecting to graph api") err = errors.Wrap(err, "connecting to graph api")
opStats.readErr = err opStats.readErr = err
return err return err
} }
var cs []data.Collection cs, err := gc.ExchangeDataCollection(ctx, op.Selectors)
cs, err = gc.ExchangeDataCollection(ctx, op.Selectors)
if err != nil { if err != nil {
err = errors.Wrap(err, "retrieving service data") err = errors.Wrap(err, "retrieving service data")
opStats.readErr = err opStats.readErr = err
return err return err
} }
@ -119,8 +119,10 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) {
if err != nil { if err != nil {
err = errors.Wrap(err, "backing up service data") err = errors.Wrap(err, "backing up service data")
opStats.writeErr = err opStats.writeErr = err
return err return err
} }
opStats.started = true opStats.started = true
opStats.gc = gc.AwaitStatus() opStats.gc = gc.AwaitStatus()
@ -139,6 +141,7 @@ func (op *BackupOperation) persistResults(
op.Status = Completed op.Status = Completed
if !opStats.started { if !opStats.started {
op.Status = Failed op.Status = Failed
return multierror.Append( return multierror.Append(
errors.New("errors prevented the operation from processing"), errors.New("errors prevented the operation from processing"),
opStats.readErr, opStats.readErr,
@ -174,10 +177,12 @@ func (op *BackupOperation) createBackupModels(
op.Results.ReadWrites, op.Results.ReadWrites,
op.Results.StartAndEndTime, op.Results.StartAndEndTime,
) )
err = op.store.Put(ctx, model.BackupSchema, b) err = op.store.Put(ctx, model.BackupSchema, b)
if err != nil { if err != nil {
return errors.Wrap(err, "creating backup model") return errors.Wrap(err, "creating backup model")
} }
op.Results.BackupID = b.ID op.Results.BackupID = b.ID
return nil return nil

View File

@ -82,6 +82,7 @@ func TestBackupOpIntegrationSuite(t *testing.T) {
); err != nil { ); err != nil {
t.Skip(err) t.Skip(err)
} }
suite.Run(t, new(BackupOpIntegrationSuite)) suite.Run(t, new(BackupOpIntegrationSuite))
} }
@ -164,7 +165,6 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run() {
}, },
}, },
} }
for _, test := range tests { for _, test := range tests {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
// need to initialize the repository before we can test connecting to it. // need to initialize the repository before we can test connecting to it.

View File

@ -63,8 +63,10 @@ func (op operation) validate() error {
if op.kopia == nil { if op.kopia == nil {
return errors.New("missing kopia connection") return errors.New("missing kopia connection")
} }
if op.store == nil { if op.store == nil {
return errors.New("missing modelstore") return errors.New("missing modelstore")
} }
return nil return nil
} }

View File

@ -29,6 +29,7 @@ func (suite *OperationSuite) TestNewOperation() {
func (suite *OperationSuite) TestOperation_Validate() { func (suite *OperationSuite) TestOperation_Validate() {
kwStub := &kopia.Wrapper{} kwStub := &kopia.Wrapper{}
swStub := &store.Wrapper{} swStub := &store.Wrapper{}
table := []struct { table := []struct {
name string name string
kw *kopia.Wrapper kw *kopia.Wrapper

View File

@ -81,16 +81,14 @@ type restoreStats struct {
// Run begins a synchronous restore operation. // Run begins a synchronous restore operation.
func (op *RestoreOperation) Run(ctx context.Context) (err error) { func (op *RestoreOperation) Run(ctx context.Context) (err error) {
// TODO: persist initial state of restoreOperation in modelstore // TODO: persist initial state of restoreOperation in modelstore
// persist operation results to the model store on exit // persist operation results to the model store on exit
opStats := restoreStats{} opStats := restoreStats{}
// TODO: persist results?
defer func() { defer func() {
err = op.persistResults(time.Now(), &opStats) err = op.persistResults(time.Now(), &opStats)
if err != nil { if err != nil {
return return
} }
// TODO: persist results?
}() }()
// retrieve the restore point details // retrieve the restore point details
@ -98,6 +96,7 @@ func (op *RestoreOperation) Run(ctx context.Context) (err error) {
if err != nil { if err != nil {
err = errors.Wrap(err, "getting backup details for restore") err = errors.Wrap(err, "getting backup details for restore")
opStats.readErr = err opStats.readErr = err
return err return err
} }
@ -116,15 +115,19 @@ func (op *RestoreOperation) Run(ctx context.Context) (err error) {
// todo: use path pkg for this // todo: use path pkg for this
fdsPaths := fds.Paths() fdsPaths := fds.Paths()
paths := make([][]string, len(fdsPaths)) paths := make([][]string, len(fdsPaths))
for i := range fdsPaths { for i := range fdsPaths {
paths[i] = strings.Split(fdsPaths[i], "/") paths[i] = strings.Split(fdsPaths[i], "/")
} }
dcs, err := op.kopia.RestoreMultipleItems(ctx, b.SnapshotID, paths) dcs, err := op.kopia.RestoreMultipleItems(ctx, b.SnapshotID, paths)
if err != nil { if err != nil {
err = errors.Wrap(err, "retrieving service data") err = errors.Wrap(err, "retrieving service data")
opStats.readErr = err opStats.readErr = err
return err return err
} }
opStats.cs = dcs opStats.cs = dcs
// restore those collections using graph // restore those collections using graph
@ -132,6 +135,7 @@ func (op *RestoreOperation) Run(ctx context.Context) (err error) {
if err != nil { if err != nil {
err = errors.Wrap(err, "connecting to graph api") err = errors.Wrap(err, "connecting to graph api")
opStats.writeErr = err opStats.writeErr = err
return err return err
} }
@ -139,8 +143,10 @@ func (op *RestoreOperation) Run(ctx context.Context) (err error) {
if err != nil { if err != nil {
err = errors.Wrap(err, "restoring service data") err = errors.Wrap(err, "restoring service data")
opStats.writeErr = err opStats.writeErr = err
return err return err
} }
opStats.started = true opStats.started = true
opStats.gc = gc.AwaitStatus() opStats.gc = gc.AwaitStatus()
logger.Ctx(ctx).Debug(gc.PrintableStatus()) logger.Ctx(ctx).Debug(gc.PrintableStatus())
@ -157,8 +163,10 @@ func (op *RestoreOperation) persistResults(
op.Results.CompletedAt = time.Now() op.Results.CompletedAt = time.Now()
op.Status = Completed op.Status = Completed
if !opStats.started { if !opStats.started {
op.Status = Failed op.Status = Failed
return multierror.Append( return multierror.Append(
errors.New("errors prevented the operation from processing"), errors.New("errors prevented the operation from processing"),
opStats.readErr, opStats.readErr,

View File

@ -92,6 +92,7 @@ func TestRestoreOpIntegrationSuite(t *testing.T) {
); err != nil { ); err != nil {
t.Skip(err) t.Skip(err)
} }
suite.Run(t, new(RestoreOpIntegrationSuite)) suite.Run(t, new(RestoreOpIntegrationSuite))
} }
@ -110,16 +111,19 @@ func (suite *RestoreOpIntegrationSuite) SetupSuite() {
k := kopia.NewConn(st) k := kopia.NewConn(st)
require.NoError(t, k.Initialize(ctx)) require.NoError(t, k.Initialize(ctx))
suite.kopiaCloser = func(ctx context.Context) { suite.kopiaCloser = func(ctx context.Context) {
k.Close(ctx) k.Close(ctx)
} }
kw, err := kopia.NewWrapper(k) kw, err := kopia.NewWrapper(k)
require.NoError(t, err) require.NoError(t, err)
suite.kw = kw suite.kw = kw
ms, err := kopia.NewModelStore(k) ms, err := kopia.NewModelStore(k)
require.NoError(t, err) require.NoError(t, err)
suite.ms = ms suite.ms = ms
sw := store.NewKopiaStore(ms) sw := store.NewKopiaStore(ms)
@ -148,9 +152,11 @@ func (suite *RestoreOpIntegrationSuite) TearDownSuite() {
if suite.ms != nil { if suite.ms != nil {
suite.ms.Close(ctx) suite.ms.Close(ctx)
} }
if suite.kw != nil { if suite.kw != nil {
suite.kw.Close(ctx) suite.kw.Close(ctx)
} }
if suite.kopiaCloser != nil { if suite.kopiaCloser != nil {
suite.kopiaCloser(ctx) suite.kopiaCloser(ctx)
} }

View File

@ -28,5 +28,6 @@ func NewM365Account(t *testing.T) account.Account {
}, },
) )
require.NoError(t, err, "initializing account") require.NoError(t, err, "initializing account")
return acc return acc
} }

View File

@ -27,6 +27,7 @@ func StubRootCmd(args ...string) *cobra.Command {
}, },
} }
c.SetArgs(args) c.SetArgs(args)
return c return c
} }

View File

@ -39,10 +39,12 @@ func cloneTestConfig() map[string]string {
if testConfig == nil { if testConfig == nil {
return map[string]string{} return map[string]string{}
} }
clone := map[string]string{} clone := map[string]string{}
for k, v := range testConfig { for k, v := range testConfig {
clone[k] = v clone[k] = v
} }
return clone return clone
} }
@ -55,7 +57,6 @@ func NewTestViper() (*viper.Viper, error) {
} }
// Or use a custom file location // Or use a custom file location
fileName := path.Base(configFilePath)
ext := path.Ext(configFilePath) ext := path.Ext(configFilePath)
if len(ext) == 0 { if len(ext) == 0 {
return nil, errors.New("corso_test requires an extension") return nil, errors.New("corso_test requires an extension")
@ -64,6 +65,8 @@ func NewTestViper() (*viper.Viper, error) {
vpr.SetConfigFile(configFilePath) vpr.SetConfigFile(configFilePath)
vpr.AddConfigPath(path.Dir(configFilePath)) vpr.AddConfigPath(path.Dir(configFilePath))
vpr.SetConfigType(ext[1:]) vpr.SetConfigType(ext[1:])
fileName := path.Base(configFilePath)
fileName = strings.TrimSuffix(fileName, ext) fileName = strings.TrimSuffix(fileName, ext)
vpr.SetConfigName(fileName) vpr.SetConfigName(fileName)
@ -105,9 +108,10 @@ func readTestConfig() (map[string]string, error) {
vpr.GetString(TestCfgUserID), vpr.GetString(TestCfgUserID),
"lidiah@8qzvrj.onmicrosoft.com", "lidiah@8qzvrj.onmicrosoft.com",
) )
testEnv[EnvCorsoTestConfigFilePath] = os.Getenv(EnvCorsoTestConfigFilePath)
testEnv[EnvCorsoTestConfigFilePath] = os.Getenv(EnvCorsoTestConfigFilePath)
testConfig = testEnv testConfig = testEnv
return cloneTestConfig(), nil return cloneTestConfig(), nil
} }
@ -130,6 +134,7 @@ func MakeTempTestConfigClone(t *testing.T, overrides map[string]string) (*viper.
if len(fName) == 0 || fName == "." || fName == "/" { if len(fName) == 0 || fName == "." || fName == "/" {
fName = ".corso_test.toml" fName = ".corso_test.toml"
} }
tDir := t.TempDir() tDir := t.TempDir()
tDirFp := path.Join(tDir, fName) tDirFp := path.Join(tDir, fName)
@ -137,8 +142,8 @@ func MakeTempTestConfigClone(t *testing.T, overrides map[string]string) (*viper.
return nil, "", err return nil, "", err
} }
vpr := viper.New()
ext := path.Ext(fName) ext := path.Ext(fName)
vpr := viper.New()
vpr.SetConfigFile(tDirFp) vpr.SetConfigFile(tDirFp)
vpr.AddConfigPath(tDir) vpr.AddConfigPath(tDir)
vpr.SetConfigType(strings.TrimPrefix(ext, ".")) vpr.SetConfigType(strings.TrimPrefix(ext, "."))

View File

@ -10,13 +10,16 @@ import (
// If any of the env values are zero length, returns an error. // If any of the env values are zero length, returns an error.
func GetRequiredEnvVars(evs ...string) (map[string]string, error) { func GetRequiredEnvVars(evs ...string) (map[string]string, error) {
vals := map[string]string{} vals := map[string]string{}
for _, ev := range evs { for _, ev := range evs {
ge := os.Getenv(ev) ge := os.Getenv(ev)
if len(ge) == 0 { if len(ge) == 0 {
return nil, errors.New(ev + " env var required for test suite") return nil, errors.New(ev + " env var required for test suite")
} }
vals[ev] = ge vals[ev] = ge
} }
return vals, nil return vals, nil
} }
@ -25,14 +28,17 @@ func GetRequiredEnvVars(evs ...string) (map[string]string, error) {
// If any of the env values are zero length, returns an error. // If any of the env values are zero length, returns an error.
func GetRequiredEnvSls(evs ...[]string) (map[string]string, error) { func GetRequiredEnvSls(evs ...[]string) (map[string]string, error) {
vals := map[string]string{} vals := map[string]string{}
for _, ev := range evs { for _, ev := range evs {
r, err := GetRequiredEnvVars(ev...) r, err := GetRequiredEnvVars(ev...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
for k, v := range r { for k, v := range r {
vals[k] = v vals[k] = v
} }
} }
return vals, nil return vals, nil
} }

View File

@ -19,6 +19,7 @@ func TestEnvvarsSuite(t *testing.T) {
func (suite *EnvvarsTestSuite) TestRunOnAny() { func (suite *EnvvarsTestSuite) TestRunOnAny() {
envVariable := "TEST_ENVVARS_SUITE" envVariable := "TEST_ENVVARS_SUITE"
os.Setenv(envVariable, "1") os.Setenv(envVariable, "1")
table := []struct { table := []struct {
name string name string
param string param string
@ -41,5 +42,6 @@ func (suite *EnvvarsTestSuite) TestRunOnAny() {
test.function(suite.T(), result) test.function(suite.T(), result)
}) })
} }
os.Unsetenv(envVariable) os.Unsetenv(envVariable)
} }

View File

@ -35,11 +35,13 @@ func RunOnAny(tests ...string) error {
for _, test := range tests { for _, test := range tests {
l += len(os.Getenv(test)) l += len(os.Getenv(test))
} }
if l == 0 { if l == 0 {
return fmt.Errorf( return fmt.Errorf(
"%s env vars are not flagged for testing", "%s env vars are not flagged for testing",
strings.Join(tests, ", ")) strings.Join(tests, ", "))
} }
return nil return nil
} }
@ -47,10 +49,13 @@ func RunOnAny(tests ...string) error {
func LogTimeOfTest(t *testing.T) string { func LogTimeOfTest(t *testing.T) string {
now := time.Now().UTC().Format(time.RFC3339Nano) now := time.Now().UTC().Format(time.RFC3339Nano)
name := t.Name() name := t.Name()
if name == "" { if name == "" {
t.Logf("Test run at %s.", now) t.Logf("Test run at %s.", now)
return now return now
} }
t.Logf("%s run at %s", name, now) t.Logf("%s run at %s", name, now)
return now return now
} }

View File

@ -13,18 +13,24 @@ func LoadAFile(aFile string) ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer f.Close() defer f.Close()
buffer := make([]byte, 0) buffer := make([]byte, 0)
reader := bufio.NewScanner(f) reader := bufio.NewScanner(f)
for reader.Scan() { for reader.Scan() {
temp := reader.Bytes() temp := reader.Bytes()
buffer = append(buffer, temp...) buffer = append(buffer, temp...)
} }
aErr := reader.Err() aErr := reader.Err()
if aErr != nil { if aErr != nil {
return nil, aErr return nil, aErr
} }
return buffer, nil return buffer, nil
} }
return bytes, nil return bytes, nil
} }

View File

@ -23,6 +23,7 @@ var AWSStorageCredEnvs = []string{
// configs. // configs.
func NewPrefixedS3Storage(t *testing.T) storage.Storage { func NewPrefixedS3Storage(t *testing.T) storage.Storage {
now := LogTimeOfTest(t) now := LogTimeOfTest(t)
cfg, err := readTestConfig() cfg, err := readTestConfig()
require.NoError(t, err, "configuring storage from test file") require.NoError(t, err, "configuring storage from test file")
@ -39,5 +40,6 @@ func NewPrefixedS3Storage(t *testing.T) storage.Storage {
}, },
) )
require.NoError(t, err, "creating storage") require.NoError(t, err, "creating storage")
return st return st
} }

View File

@ -13,5 +13,6 @@ import (
func M365UserID(t *testing.T) string { func M365UserID(t *testing.T) string {
cfg, err := readTestConfig() cfg, err := readTestConfig()
require.NoError(t, err, "retrieving m365 user id from test configuration") require.NoError(t, err, "retrieving m365 user id from test configuration")
return cfg[TestCfgUserID] return cfg[TestCfgUserID]
} }

View File

@ -29,6 +29,7 @@ type Account struct {
// NewAccount aggregates all the supplied configurations into a single configuration // NewAccount aggregates all the supplied configurations into a single configuration
func NewAccount(p accountProvider, cfgs ...common.StringConfigurer) (Account, error) { func NewAccount(p accountProvider, cfgs ...common.StringConfigurer) (Account, error) {
cs, err := common.UnionStringConfigs(cfgs...) cs, err := common.UnionStringConfigs(cfgs...)
return Account{ return Account{
Provider: p, Provider: p,
Config: cs, Config: cs,

View File

@ -32,6 +32,7 @@ func (c M365Config) StringConfig() (map[string]string, error) {
keyM365ClientSecret: c.ClientSecret, keyM365ClientSecret: c.ClientSecret,
keyM365TenantID: c.TenantID, keyM365TenantID: c.TenantID,
} }
return cfg, c.validate() return cfg, c.validate()
} }
@ -43,6 +44,7 @@ func (a Account) M365Config() (M365Config, error) {
c.ClientSecret = a.Config[keyM365ClientSecret] c.ClientSecret = a.Config[keyM365ClientSecret]
c.TenantID = a.Config[keyM365TenantID] c.TenantID = a.Config[keyM365TenantID]
} }
return c, c.validate() return c, c.validate()
} }
@ -52,10 +54,12 @@ func (c M365Config) validate() error {
credentials.ClientSecret: c.ClientSecret, credentials.ClientSecret: c.ClientSecret,
TenantID: c.TenantID, TenantID: c.TenantID,
} }
for k, v := range check { for k, v := range check {
if len(v) == 0 { if len(v) == 0 {
return errors.Wrap(errMissingRequired, k) return errors.Wrap(errMissingRequired, k)
} }
} }
return nil return nil
} }

View File

@ -71,6 +71,7 @@ func PrintAll(ctx context.Context, bs []Backup) {
for _, b := range bs { for _, b := range bs {
ps = append(ps, print.Printable(b)) ps = append(ps, print.Printable(b))
} }
print.All(ctx, ps...) print.All(ctx, ps...)
} }
@ -111,6 +112,7 @@ func (b Backup) Headers() []string {
func (b Backup) Values() []string { func (b Backup) Values() []string {
errCount := support.GetNumberOfErrors(b.ReadErrors) + support.GetNumberOfErrors(b.WriteErrors) errCount := support.GetNumberOfErrors(b.ReadErrors) + support.GetNumberOfErrors(b.WriteErrors)
status := fmt.Sprintf("%s (%d errors)", b.Status, errCount) status := fmt.Sprintf("%s (%d errors)", b.Status, errCount)
return []string{ return []string{
common.FormatTime(b.StartedAt), common.FormatTime(b.StartedAt),
string(b.ID), string(b.ID),

View File

@ -27,6 +27,7 @@ func TestBackupSuite(t *testing.T) {
func stubBackup(t time.Time) backup.Backup { func stubBackup(t time.Time) backup.Backup {
sel := selectors.NewExchangeBackup() sel := selectors.NewExchangeBackup()
sel.Include(sel.Users(selectors.Any())) sel.Include(sel.Users(selectors.Any()))
return backup.Backup{ return backup.Backup{
BaseModel: model.BaseModel{ BaseModel: model.BaseModel{
ID: model.StableID("id"), ID: model.StableID("id"),
@ -62,14 +63,15 @@ func (suite *BackupSuite) TestBackup_HeadersValues() {
} }
hs := b.Headers() hs := b.Headers()
assert.Equal(t, expectHs, hs) assert.Equal(t, expectHs, hs)
nowFmt := common.FormatTime(now)
nowFmt := common.FormatTime(now)
expectVs := []string{ expectVs := []string{
nowFmt, nowFmt,
"id", "id",
"status (2 errors)", "status (2 errors)",
selectors.All, selectors.All,
} }
vs := b.Values() vs := b.Values()
assert.Equal(t, expectVs, vs) assert.Equal(t, expectVs, vs)
} }

View File

@ -26,6 +26,7 @@ func (dm DetailsModel) PrintEntries(ctx context.Context) {
for _, de := range dm.Entries { for _, de := range dm.Entries {
ps = append(ps, de) ps = append(ps, de)
} }
print.All(ctx, ps...) print.All(ctx, ps...)
} }
@ -33,9 +34,11 @@ func (dm DetailsModel) PrintEntries(ctx context.Context) {
func (dm DetailsModel) Paths() []string { func (dm DetailsModel) Paths() []string {
ents := dm.Entries ents := dm.Entries
r := make([]string, len(ents)) r := make([]string, len(ents))
for i := range ents { for i := range ents {
r[i] = ents[i].RepoRef r[i] = ents[i].RepoRef
} }
return r return r
} }
@ -88,30 +91,38 @@ func (de DetailsEntry) MinimumPrintable() any {
// for printing out to a terminal in a columnar display. // for printing out to a terminal in a columnar display.
func (de DetailsEntry) Headers() []string { func (de DetailsEntry) Headers() []string {
hs := []string{"Repo Ref"} hs := []string{"Repo Ref"}
if de.ItemInfo.Exchange != nil { if de.ItemInfo.Exchange != nil {
hs = append(hs, de.ItemInfo.Exchange.Headers()...) hs = append(hs, de.ItemInfo.Exchange.Headers()...)
} }
if de.ItemInfo.Sharepoint != nil { if de.ItemInfo.Sharepoint != nil {
hs = append(hs, de.ItemInfo.Sharepoint.Headers()...) hs = append(hs, de.ItemInfo.Sharepoint.Headers()...)
} }
if de.ItemInfo.OneDrive != nil { if de.ItemInfo.OneDrive != nil {
hs = append(hs, de.ItemInfo.OneDrive.Headers()...) hs = append(hs, de.ItemInfo.OneDrive.Headers()...)
} }
return hs return hs
} }
// Values returns the values matching the Headers list. // Values returns the values matching the Headers list.
func (de DetailsEntry) Values() []string { func (de DetailsEntry) Values() []string {
vs := []string{de.RepoRef} vs := []string{de.RepoRef}
if de.ItemInfo.Exchange != nil { if de.ItemInfo.Exchange != nil {
vs = append(vs, de.ItemInfo.Exchange.Values()...) vs = append(vs, de.ItemInfo.Exchange.Values()...)
} }
if de.ItemInfo.Sharepoint != nil { if de.ItemInfo.Sharepoint != nil {
vs = append(vs, de.ItemInfo.Sharepoint.Values()...) vs = append(vs, de.ItemInfo.Sharepoint.Values()...)
} }
if de.ItemInfo.OneDrive != nil { if de.ItemInfo.OneDrive != nil {
vs = append(vs, de.ItemInfo.OneDrive.Values()...) vs = append(vs, de.ItemInfo.OneDrive.Values()...)
} }
return vs return vs
} }

View File

@ -19,8 +19,10 @@ func TestOptionsSuite(t *testing.T) {
func (suite *OptionsSuite) TestNewOptions() { func (suite *OptionsSuite) TestNewOptions() {
t := suite.T() t := suite.T()
o1 := control.NewOptions(true) o1 := control.NewOptions(true)
assert.True(t, o1.FailFast, "failFast") assert.True(t, o1.FailFast, "failFast")
o2 := control.NewOptions(false) o2 := control.NewOptions(false)
assert.False(t, o2.FailFast, "failFast") assert.False(t, o2.FailFast, "failFast")
} }

View File

@ -26,6 +26,7 @@ func GetAWS(override map[string]string) AWS {
if ovr, ok := override[AWSAccessKeyID]; ok && ovr != "" { if ovr, ok := override[AWSAccessKeyID]; ok && ovr != "" {
accessKey = ovr accessKey = ovr
} }
secretKey := os.Getenv(AWSSecretAccessKey) secretKey := os.Getenv(AWSSecretAccessKey)
sessToken := os.Getenv(AWSSessionToken) sessToken := os.Getenv(AWSSessionToken)
@ -44,10 +45,12 @@ func (c AWS) Validate() error {
AWSSecretAccessKey: c.SecretKey, AWSSecretAccessKey: c.SecretKey,
AWSSessionToken: c.SessionToken, AWSSessionToken: c.SessionToken,
} }
for k, v := range check { for k, v := range check {
if len(v) == 0 { if len(v) == 0 {
return errors.Wrap(errMissingRequired, k) return errors.Wrap(errMissingRequired, k)
} }
} }
return nil return nil
} }

View File

@ -21,6 +21,7 @@ func GetCorso() Corso {
// todo (rkeeprs): read from either corso config file or env vars. // todo (rkeeprs): read from either corso config file or env vars.
// https://github.com/alcionai/corso/issues/120 // https://github.com/alcionai/corso/issues/120
corsoPasswd := os.Getenv(CorsoPassword) corsoPasswd := os.Getenv(CorsoPassword)
return Corso{ return Corso{
CorsoPassword: corsoPasswd, CorsoPassword: corsoPasswd,
} }
@ -30,10 +31,12 @@ func (c Corso) Validate() error {
check := map[string]string{ check := map[string]string{
CorsoPassword: c.CorsoPassword, CorsoPassword: c.CorsoPassword,
} }
for k, v := range check { for k, v := range check {
if len(v) == 0 { if len(v) == 0 {
return errors.Wrap(errMissingRequired, k) return errors.Wrap(errMissingRequired, k)
} }
} }
return nil return nil
} }

View File

@ -33,10 +33,12 @@ func (c M365) Validate() error {
ClientID: c.ClientID, ClientID: c.ClientID,
ClientSecret: c.ClientSecret, ClientSecret: c.ClientSecret,
} }
for k, v := range check { for k, v := range check {
if len(v) == 0 { if len(v) == 0 {
return errors.Wrap(errMissingRequired, k) return errors.Wrap(errMissingRequired, k)
} }
} }
return nil return nil
} }

View File

@ -83,6 +83,7 @@ func NewIn(negate bool, category any, substr string) Filter {
// Checks whether the filter matches the input // Checks whether the filter matches the input
func (f Filter) Matches(input string) bool { func (f Filter) Matches(input string) bool {
var cmp func(string, string) bool var cmp func(string, string) bool
switch f.Comparator { switch f.Comparator {
case Equal: case Equal:
cmp = equals cmp = equals
@ -97,10 +98,12 @@ func (f Filter) Matches(input string) bool {
case In: case In:
cmp = in cmp = in
} }
result := cmp(f.Target, norm(input)) result := cmp(f.Target, norm(input))
if f.Negate { if f.Negate {
result = !result result = !result
} }
return result return result
} }

View File

@ -68,14 +68,17 @@ func singleton(level logLevel) *zap.SugaredLogger {
lgr *zap.Logger lgr *zap.Logger
err error err error
) )
if level != Production { if level != Production {
cfg := zap.NewDevelopmentConfig() cfg := zap.NewDevelopmentConfig()
switch level { switch level {
case Info: case Info:
cfg.Level = zap.NewAtomicLevelAt(zapcore.InfoLevel) cfg.Level = zap.NewAtomicLevelAt(zapcore.InfoLevel)
case Warn: case Warn:
cfg.Level = zap.NewAtomicLevelAt(zapcore.WarnLevel) cfg.Level = zap.NewAtomicLevelAt(zapcore.WarnLevel)
} }
lgr, err = cfg.Build() lgr, err = cfg.Build()
} else { } else {
lgr, err = zap.NewProduction() lgr, err = zap.NewProduction()
@ -87,6 +90,7 @@ func singleton(level logLevel) *zap.SugaredLogger {
} }
loggerton = lgr.Sugar() loggerton = lgr.Sugar()
return loggerton return loggerton
} }
@ -126,6 +130,7 @@ func Seed(ctx context.Context) (ctxOut context.Context, zsl *zap.SugaredLogger)
} }
level = levelOf(levelString) level = levelOf(levelString)
return // return values handled in defer return // return values handled in defer
} }
@ -135,6 +140,7 @@ func Ctx(ctx context.Context) *zap.SugaredLogger {
if l == nil { if l == nil {
return singleton(levelOf(llFlag)) return singleton(levelOf(llFlag))
} }
return l.(*zap.SugaredLogger) return l.(*zap.SugaredLogger)
} }
@ -148,5 +154,6 @@ func levelOf(lvl string) logLevel {
case "error": case "error":
return Production return Production
} }
return Info return Info
} }

View File

@ -70,6 +70,7 @@ func Initialize(
dataLayer: w, dataLayer: w,
modelStore: ms, modelStore: ms,
} }
return &r, nil return &r, nil
} }
@ -109,6 +110,7 @@ func Connect(
dataLayer: w, dataLayer: w,
modelStore: ms, modelStore: ms,
} }
return &r, nil return &r, nil
} }
@ -116,6 +118,7 @@ func (r *Repository) Close(ctx context.Context) error {
if r.dataLayer != nil { if r.dataLayer != nil {
err := r.dataLayer.Close(ctx) err := r.dataLayer.Close(ctx)
r.dataLayer = nil r.dataLayer = nil
if err != nil { if err != nil {
return errors.Wrap(err, "closing corso DataLayer") return errors.Wrap(err, "closing corso DataLayer")
} }
@ -124,8 +127,10 @@ func (r *Repository) Close(ctx context.Context) error {
if r.modelStore == nil { if r.modelStore == nil {
return nil return nil
} }
err := r.modelStore.Close(ctx) err := r.modelStore.Close(ctx)
r.modelStore = nil r.modelStore = nil
return errors.Wrap(err, "closing corso ModelStore") return errors.Wrap(err, "closing corso ModelStore")
} }
@ -191,5 +196,6 @@ func (r Repository) DeleteBackup(ctx context.Context, id model.StableID) error {
} }
sw := store.NewKopiaStore(r.modelStore) sw := store.NewKopiaStore(r.modelStore)
return sw.DeleteBackup(ctx, id) return sw.DeleteBackup(ctx, id)
} }

View File

@ -97,6 +97,7 @@ func TestRepositoryIntegrationSuite(t *testing.T) {
); err != nil { ); err != nil {
t.Skip(err) t.Skip(err)
} }
suite.Run(t, new(RepositoryIntegrationSuite)) suite.Run(t, new(RepositoryIntegrationSuite))
} }

View File

@ -40,6 +40,7 @@ func NewExchangeBackup() *ExchangeBackup {
newSelector(ServiceExchange), newSelector(ServiceExchange),
}, },
} }
return &src return &src
} }
@ -49,7 +50,9 @@ func (s Selector) ToExchangeBackup() (*ExchangeBackup, error) {
if s.Service != ServiceExchange { if s.Service != ServiceExchange {
return nil, badCastErr(ServiceExchange, s.Service) return nil, badCastErr(ServiceExchange, s.Service)
} }
src := ExchangeBackup{exchange{s}} src := ExchangeBackup{exchange{s}}
return &src, nil return &src, nil
} }
@ -60,6 +63,7 @@ func NewExchangeRestore() *ExchangeRestore {
newSelector(ServiceExchange), newSelector(ServiceExchange),
}, },
} }
return &src return &src
} }
@ -69,7 +73,9 @@ func (s Selector) ToExchangeRestore() (*ExchangeRestore, error) {
if s.Service != ServiceExchange { if s.Service != ServiceExchange {
return nil, badCastErr(ServiceExchange, s.Service) return nil, badCastErr(ServiceExchange, s.Service)
} }
src := ExchangeRestore{exchange{s}} src := ExchangeRestore{exchange{s}}
return &src, nil return &src, nil
} }
@ -163,6 +169,7 @@ func (s *exchange) Contacts(users, folders, contacts []string) []ExchangeScope {
folders = normalize(folders) folders = normalize(folders)
contacts = normalize(contacts) contacts = normalize(contacts)
scopes := []ExchangeScope{} scopes := []ExchangeScope{}
for _, u := range users { for _, u := range users {
for _, f := range folders { for _, f := range folders {
scopes = append( scopes = append(
@ -171,6 +178,7 @@ func (s *exchange) Contacts(users, folders, contacts []string) []ExchangeScope {
) )
} }
} }
return scopes return scopes
} }
@ -183,12 +191,14 @@ func (s *exchange) ContactFolders(users, folders []string) []ExchangeScope {
users = normalize(users) users = normalize(users)
folders = normalize(folders) folders = normalize(folders)
scopes := []ExchangeScope{} scopes := []ExchangeScope{}
for _, u := range users { for _, u := range users {
scopes = append( scopes = append(
scopes, scopes,
makeScope[ExchangeScope](u, Group, ExchangeContactFolder, folders), makeScope[ExchangeScope](u, Group, ExchangeContactFolder, folders),
) )
} }
return scopes return scopes
} }
@ -201,12 +211,14 @@ func (s *exchange) Events(users, events []string) []ExchangeScope {
users = normalize(users) users = normalize(users)
events = normalize(events) events = normalize(events)
scopes := []ExchangeScope{} scopes := []ExchangeScope{}
for _, u := range users { for _, u := range users {
scopes = append( scopes = append(
scopes, scopes,
makeScope[ExchangeScope](u, Item, ExchangeEvent, events), makeScope[ExchangeScope](u, Item, ExchangeEvent, events),
) )
} }
return scopes return scopes
} }
@ -220,6 +232,7 @@ func (s *exchange) Mails(users, folders, mails []string) []ExchangeScope {
folders = normalize(folders) folders = normalize(folders)
mails = normalize(mails) mails = normalize(mails)
scopes := []ExchangeScope{} scopes := []ExchangeScope{}
for _, u := range users { for _, u := range users {
for _, f := range folders { for _, f := range folders {
scopes = append( scopes = append(
@ -228,6 +241,7 @@ func (s *exchange) Mails(users, folders, mails []string) []ExchangeScope {
) )
} }
} }
return scopes return scopes
} }
@ -240,12 +254,14 @@ func (s *exchange) MailFolders(users, folders []string) []ExchangeScope {
users = normalize(users) users = normalize(users)
folders = normalize(folders) folders = normalize(folders)
scopes := []ExchangeScope{} scopes := []ExchangeScope{}
for _, u := range users { for _, u := range users {
scopes = append( scopes = append(
scopes, scopes,
makeScope[ExchangeScope](u, Group, ExchangeMailFolder, folders), makeScope[ExchangeScope](u, Group, ExchangeMailFolder, folders),
) )
} }
return scopes return scopes
} }
@ -257,11 +273,13 @@ func (s *exchange) MailFolders(users, folders []string) []ExchangeScope {
func (s *exchange) Users(users []string) []ExchangeScope { func (s *exchange) Users(users []string) []ExchangeScope {
users = normalize(users) users = normalize(users)
scopes := []ExchangeScope{} scopes := []ExchangeScope{}
for _, u := range users { for _, u := range users {
scopes = append(scopes, makeScope[ExchangeScope](u, Group, ExchangeContactFolder, Any())) scopes = append(scopes, makeScope[ExchangeScope](u, Group, ExchangeContactFolder, Any()))
scopes = append(scopes, makeScope[ExchangeScope](u, Item, ExchangeEvent, Any())) scopes = append(scopes, makeScope[ExchangeScope](u, Item, ExchangeEvent, Any()))
scopes = append(scopes, makeScope[ExchangeScope](u, Group, ExchangeMailFolder, Any())) scopes = append(scopes, makeScope[ExchangeScope](u, Group, ExchangeMailFolder, Any()))
} }
return scopes return scopes
} }
@ -327,6 +345,7 @@ func (d ExchangeDestination) GetOrDefault(cat exchangeCategory, current string)
if !ok { if !ok {
return current return current
} }
return dest return dest
} }
@ -336,11 +355,14 @@ func (d ExchangeDestination) Set(cat exchangeCategory, dest string) error {
if len(dest) == 0 { if len(dest) == 0 {
return nil return nil
} }
cs := cat.String() cs := cat.String()
if curr, ok := d[cs]; ok { if curr, ok := d[cs]; ok {
return existingDestinationErr(cs, curr) return existingDestinationErr(cs, curr)
} }
d[cs] = dest d[cs] = dest
return nil return nil
} }
@ -424,6 +446,7 @@ func (ec exchangeCategory) leafCat() categorizer {
case ExchangeMail, ExchangeMailFolder: case ExchangeMail, ExchangeMailFolder:
return ExchangeMail return ExchangeMail
} }
return ec return ec
} }
@ -451,6 +474,7 @@ func (ec exchangeCategory) pathValues(path []string) map[categorizer]string {
if len(path) < 2 { if len(path) < 2 {
return m return m
} }
m[ExchangeUser] = path[1] m[ExchangeUser] = path[1]
/* /*
TODO/Notice: TODO/Notice:
@ -465,20 +489,26 @@ func (ec exchangeCategory) pathValues(path []string) map[categorizer]string {
if len(path) < 5 { if len(path) < 5 {
return m return m
} }
m[ExchangeContactFolder] = path[3] m[ExchangeContactFolder] = path[3]
m[ExchangeContact] = path[4] m[ExchangeContact] = path[4]
case ExchangeEvent: case ExchangeEvent:
if len(path) < 4 { if len(path) < 4 {
return m return m
} }
m[ExchangeEvent] = path[3] m[ExchangeEvent] = path[3]
case ExchangeMail: case ExchangeMail:
if len(path) < 5 { if len(path) < 5 {
return m return m
} }
m[ExchangeMailFolder] = path[3] m[ExchangeMailFolder] = path[3]
m[ExchangeMail] = path[4] m[ExchangeMail] = path[4]
} }
return m return m
} }
@ -604,18 +634,23 @@ func (s ExchangeScope) matchesInfo(info *details.ExchangeInfo) bool {
if info == nil { if info == nil {
return false return false
} }
// the scope must define targets to match on // the scope must define targets to match on
filterCat := s.FilterCategory() filterCat := s.FilterCategory()
targets := s.Get(filterCat) targets := s.Get(filterCat)
if len(targets) == 0 { if len(targets) == 0 {
return false return false
} }
if targets[0] == AnyTgt { if targets[0] == AnyTgt {
return true return true
} }
if targets[0] == NoneTgt { if targets[0] == NoneTgt {
return false return false
} }
// any of the targets for a given info filter may succeed. // any of the targets for a given info filter may succeed.
for _, target := range targets { for _, target := range targets {
switch filterCat { switch filterCat {
@ -637,5 +672,6 @@ func (s ExchangeScope) matchesInfo(info *details.ExchangeInfo) bool {
} }
} }
} }
return false return false
} }

View File

@ -281,13 +281,16 @@ func (suite *ExchangeSelectorSuite) TestExchangeSelector_Exclude_Users() {
for _, scope := range scopes { for _, scope := range scopes {
assert.Contains(t, join(u1, u2), scope[ExchangeUser.String()]) assert.Contains(t, join(u1, u2), scope[ExchangeUser.String()])
if scope[scopeKeyCategory] == ExchangeContactFolder.String() { if scope[scopeKeyCategory] == ExchangeContactFolder.String() {
assert.Equal(t, scope[ExchangeContact.String()], AnyTgt) assert.Equal(t, scope[ExchangeContact.String()], AnyTgt)
assert.Equal(t, scope[ExchangeContactFolder.String()], AnyTgt) assert.Equal(t, scope[ExchangeContactFolder.String()], AnyTgt)
} }
if scope[scopeKeyCategory] == ExchangeEvent.String() { if scope[scopeKeyCategory] == ExchangeEvent.String() {
assert.Equal(t, scope[ExchangeEvent.String()], AnyTgt) assert.Equal(t, scope[ExchangeEvent.String()], AnyTgt)
} }
if scope[scopeKeyCategory] == ExchangeMailFolder.String() { if scope[scopeKeyCategory] == ExchangeMailFolder.String() {
assert.Equal(t, scope[ExchangeMail.String()], AnyTgt) assert.Equal(t, scope[ExchangeMail.String()], AnyTgt)
assert.Equal(t, scope[ExchangeMailFolder.String()], AnyTgt) assert.Equal(t, scope[ExchangeMailFolder.String()], AnyTgt)
@ -310,13 +313,16 @@ func (suite *ExchangeSelectorSuite) TestExchangeSelector_Include_Users() {
for _, scope := range scopes { for _, scope := range scopes {
assert.Contains(t, join(u1, u2), scope[ExchangeUser.String()]) assert.Contains(t, join(u1, u2), scope[ExchangeUser.String()])
if scope[scopeKeyCategory] == ExchangeContactFolder.String() { if scope[scopeKeyCategory] == ExchangeContactFolder.String() {
assert.Equal(t, scope[ExchangeContact.String()], AnyTgt) assert.Equal(t, scope[ExchangeContact.String()], AnyTgt)
assert.Equal(t, scope[ExchangeContactFolder.String()], AnyTgt) assert.Equal(t, scope[ExchangeContactFolder.String()], AnyTgt)
} }
if scope[scopeKeyCategory] == ExchangeEvent.String() { if scope[scopeKeyCategory] == ExchangeEvent.String() {
assert.Equal(t, scope[ExchangeEvent.String()], AnyTgt) assert.Equal(t, scope[ExchangeEvent.String()], AnyTgt)
} }
if scope[scopeKeyCategory] == ExchangeMailFolder.String() { if scope[scopeKeyCategory] == ExchangeMailFolder.String() {
assert.Equal(t, scope[ExchangeMail.String()], AnyTgt) assert.Equal(t, scope[ExchangeMail.String()], AnyTgt)
assert.Equal(t, scope[ExchangeMailFolder.String()], AnyTgt) assert.Equal(t, scope[ExchangeMailFolder.String()], AnyTgt)
@ -379,6 +385,7 @@ func (suite *ExchangeSelectorSuite) TestExchangeBackup_Scopes() {
scopes := eb.Scopes() scopes := eb.Scopes()
assert.Len(suite.T(), scopes, 3) assert.Len(suite.T(), scopes, 3)
for _, sc := range scopes { for _, sc := range scopes {
cat := sc.Category() cat := sc.Category()
suite.T().Run(cat.String(), func(t *testing.T) { suite.T().Run(cat.String(), func(t *testing.T) {
@ -424,6 +431,7 @@ func (suite *ExchangeSelectorSuite) TestExchangeBackup_DiscreteScopes() {
expect: Any(), expect: Any(),
}, },
} }
for _, test := range table { for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
eb := NewExchangeBackup() eb := NewExchangeBackup()
@ -515,7 +523,6 @@ func (suite *ExchangeSelectorSuite) TestExchangeScope_Get() {
ExchangeMailFolder, ExchangeMailFolder,
ExchangeUser, ExchangeUser,
} }
for _, test := range table { for _, test := range table {
suite.T().Run(test.String(), func(t *testing.T) { suite.T().Run(test.String(), func(t *testing.T) {
for _, sc := range scopes { for _, sc := range scopes {
@ -538,10 +545,12 @@ func (suite *ExchangeSelectorSuite) TestExchangeScope_Get() {
func (suite *ExchangeSelectorSuite) TestExchangeScope_MatchesInfo() { func (suite *ExchangeSelectorSuite) TestExchangeScope_MatchesInfo() {
es := NewExchangeRestore() es := NewExchangeRestore()
const ( const (
sender = "smarf@2many.cooks" sender = "smarf@2many.cooks"
subject = "I have seen the fnords!" subject = "I have seen the fnords!"
) )
var ( var (
epoch = time.Time{} epoch = time.Time{}
now = time.Now() now = time.Now()
@ -590,6 +599,7 @@ func (suite *ExchangeSelectorSuite) TestExchangeScope_MatchesPath() {
fld = "mailFolder" fld = "mailFolder"
mail = "mailID" mail = "mailID"
) )
var ( var (
path = []string{"tid", usr, "mail", fld, mail} path = []string{"tid", usr, "mail", fld, mail}
es = NewExchangeRestore() es = NewExchangeRestore()
@ -684,21 +694,26 @@ func (suite *ExchangeSelectorSuite) TestExchangeRestore_Reduce() {
Entries: []details.DetailsEntry{}, Entries: []details.DetailsEntry{},
}, },
} }
for _, r := range refs { for _, r := range refs {
deets.Entries = append(deets.Entries, details.DetailsEntry{ deets.Entries = append(deets.Entries, details.DetailsEntry{
RepoRef: r, RepoRef: r,
}) })
} }
return deets return deets
} }
const ( const (
contact = "tid/uid/contact/cfld/cid" contact = "tid/uid/contact/cfld/cid"
event = "tid/uid/event/eid" event = "tid/uid/event/eid"
mail = "tid/uid/mail/mfld/mid" mail = "tid/uid/mail/mfld/mid"
) )
arr := func(s ...string) []string { arr := func(s ...string) []string {
return s return s
} }
table := []struct { table := []struct {
name string name string
deets *details.Details deets *details.Details
@ -837,19 +852,24 @@ func (suite *ExchangeSelectorSuite) TestScopesByCategory() {
events = es.Events(Any(), Any()) events = es.Events(Any(), Any())
mail = es.MailFolders(Any(), Any()) mail = es.MailFolders(Any(), Any())
) )
type expect struct { type expect struct {
contact int contact int
event int event int
mail int mail int
} }
type input []scope type input []scope
makeInput := func(es ...[]ExchangeScope) []scope { makeInput := func(es ...[]ExchangeScope) []scope {
mss := []scope{} mss := []scope{}
for _, sl := range es { for _, sl := range es {
for _, s := range sl { for _, s := range sl {
mss = append(mss, scope(s)) mss = append(mss, scope(s))
} }
} }
return mss return mss
} }
cats := map[pathType]exchangeCategory{ cats := map[pathType]exchangeCategory{
@ -857,6 +877,7 @@ func (suite *ExchangeSelectorSuite) TestScopesByCategory() {
exchangeEventPath: ExchangeEvent, exchangeEventPath: ExchangeEvent,
exchangeMailPath: ExchangeMail, exchangeMailPath: ExchangeMail,
} }
table := []struct { table := []struct {
name string name string
scopes input scopes input
@ -880,10 +901,12 @@ func (suite *ExchangeSelectorSuite) TestScopesByCategory() {
func (suite *ExchangeSelectorSuite) TestPasses() { func (suite *ExchangeSelectorSuite) TestPasses() {
deets := details.DetailsEntry{} deets := details.DetailsEntry{}
const ( const (
mid = "mailID" mid = "mailID"
cat = ExchangeMail cat = ExchangeMail
) )
var ( var (
es = NewExchangeRestore() es = NewExchangeRestore()
anyUser = setScopesToDefault(es.Users(Any())) anyUser = setScopesToDefault(es.Users(Any()))
@ -934,6 +957,7 @@ func (suite *ExchangeSelectorSuite) TestPasses() {
func (suite *ExchangeSelectorSuite) TestContains() { func (suite *ExchangeSelectorSuite) TestContains() {
target := "fnords" target := "fnords"
var ( var (
es = NewExchangeRestore() es = NewExchangeRestore()
anyUser = setScopesToDefault(es.Users(Any())) anyUser = setScopesToDefault(es.Users(Any()))
@ -943,6 +967,7 @@ func (suite *ExchangeSelectorSuite) TestContains() {
wrongType = setScopesToDefault(es.Contacts(Any(), Any(), Any())) wrongType = setScopesToDefault(es.Contacts(Any(), Any(), Any()))
wrongTypeGoodTarget = setScopesToDefault(es.Contacts(Any(), Any(), Any())) wrongTypeGoodTarget = setScopesToDefault(es.Contacts(Any(), Any(), Any()))
) )
table := []struct { table := []struct {
name string name string
scopes []ExchangeScope scopes []ExchangeScope
@ -977,6 +1002,7 @@ func (suite *ExchangeSelectorSuite) TestIsAny() {
specificMail = setScopesToDefault(es.Mails(Any(), Any(), []string{"mail"})) specificMail = setScopesToDefault(es.Mails(Any(), Any(), []string{"mail"}))
anyMail = setScopesToDefault(es.Mails(Any(), Any(), Any())) anyMail = setScopesToDefault(es.Mails(Any(), Any(), Any()))
) )
table := []struct { table := []struct {
name string name string
scopes []ExchangeScope scopes []ExchangeScope
@ -1041,6 +1067,7 @@ func (suite *ExchangeSelectorSuite) TestExchangeCategory_PathValues() {
ExchangeMailFolder: mailPath[3], ExchangeMailFolder: mailPath[3],
ExchangeMail: mailPath[4], ExchangeMail: mailPath[4],
} }
table := []struct { table := []struct {
cat exchangeCategory cat exchangeCategory
path []string path []string
@ -1064,7 +1091,9 @@ func (suite *ExchangeSelectorSuite) TestExchangeCategory_PathKeys() {
event := []categorizer{ExchangeUser, ExchangeEvent} event := []categorizer{ExchangeUser, ExchangeEvent}
mail := []categorizer{ExchangeUser, ExchangeMailFolder, ExchangeMail} mail := []categorizer{ExchangeUser, ExchangeMailFolder, ExchangeMail}
user := []categorizer{ExchangeUser} user := []categorizer{ExchangeUser}
var empty []categorizer var empty []categorizer
table := []struct { table := []struct {
cat exchangeCategory cat exchangeCategory
expect []categorizer expect []categorizer

View File

@ -24,6 +24,7 @@ func (mc mockCategorizer) String() string {
case rootCatStub: case rootCatStub:
return "root" return "root"
} }
return "unknown" return "unknown"
} }
@ -70,6 +71,7 @@ func (ms mockScope) categorizer() categorizer {
case leafCatStub.String(): case leafCatStub.String():
return leafCatStub return leafCatStub
} }
return unknownCatStub return unknownCatStub
} }
@ -94,6 +96,7 @@ func stubScope(match string) mockScope {
if len(match) > 0 { if len(match) > 0 {
sm = match sm = match
} }
return mockScope{ return mockScope{
rootCatStub.String(): AnyTgt, rootCatStub.String(): AnyTgt,
scopeKeyCategory: rootCatStub.String(), scopeKeyCategory: rootCatStub.String(),
@ -125,5 +128,6 @@ func setScopesToDefault[T scopeT](ts []T) []T {
for _, s := range ts { for _, s := range ts {
s.setDefaults() s.setDefaults()
} }
return ts return ts
} }

View File

@ -30,6 +30,7 @@ func NewOneDriveBackup() *OneDriveBackup {
newSelector(ServiceOneDrive), newSelector(ServiceOneDrive),
}, },
} }
return &src return &src
} }
@ -39,7 +40,9 @@ func (s Selector) ToOneDriveBackup() (*OneDriveBackup, error) {
if s.Service != ServiceOneDrive { if s.Service != ServiceOneDrive {
return nil, badCastErr(ServiceOneDrive, s.Service) return nil, badCastErr(ServiceOneDrive, s.Service)
} }
src := OneDriveBackup{oneDrive{s}} src := OneDriveBackup{oneDrive{s}}
return &src, nil return &src, nil
} }
@ -110,9 +113,11 @@ func (s *oneDrive) Filter(scopes ...[]OneDriveScope) {
func (s *oneDrive) Users(users []string) []OneDriveScope { func (s *oneDrive) Users(users []string) []OneDriveScope {
users = normalize(users) users = normalize(users)
scopes := []OneDriveScope{} scopes := []OneDriveScope{}
for _, u := range users { for _, u := range users {
scopes = append(scopes, makeScope[OneDriveScope](u, Group, OneDriveUser, users)) scopes = append(scopes, makeScope[OneDriveScope](u, Group, OneDriveUser, users))
} }
return scopes return scopes
} }
@ -197,6 +202,7 @@ func (c oneDriveCategory) pathValues(path []string) map[categorizer]string {
if len(path) < 2 { if len(path) < 2 {
return m return m
} }
m[OneDriveUser] = path[1] m[OneDriveUser] = path[1]
/* /*
TODO/Notice: TODO/Notice:
@ -306,12 +312,15 @@ func (s OneDriveScope) matchesInfo(info *details.OneDriveInfo) bool {
// the scope must define targets to match on // the scope must define targets to match on
filterCat := s.FilterCategory() filterCat := s.FilterCategory()
targets := s.Get(filterCat) targets := s.Get(filterCat)
if len(targets) == 0 { if len(targets) == 0 {
return false return false
} }
if targets[0] == AnyTgt { if targets[0] == AnyTgt {
return true return true
} }
if targets[0] == NoneTgt { if targets[0] == NoneTgt {
return false return false
} }
@ -323,5 +332,6 @@ func (s OneDriveScope) matchesInfo(info *details.OneDriveInfo) bool {
return target != NoneTgt return target != NoneTgt
} }
} }
return false return false
} }

View File

@ -60,6 +60,7 @@ func (suite *OneDriveSelectorSuite) TestOneDriveBackup_DiscreteScopes() {
expect: Any(), expect: Any(),
}, },
} }
for _, test := range table { for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
eb := NewOneDriveBackup() eb := NewOneDriveBackup()
@ -82,6 +83,7 @@ func (suite *OneDriveSelectorSuite) TestOneDriveSelector_Users() {
u1 = "u1" u1 = "u1"
u2 = "u2" u2 = "u2"
) )
userScopes := sel.Users([]string{u1, u2}) userScopes := sel.Users([]string{u1, u2})
for _, scope := range userScopes { for _, scope := range userScopes {
// Scope value is either u1 or u2 // Scope value is either u1 or u2

View File

@ -128,6 +128,7 @@ func makeScope[T scopeT](
cat.String(): join(vs...), cat.String(): join(vs...),
cat.rootCat().String(): resource, cat.rootCat().String(): resource,
} }
return s return s
} }
@ -157,19 +158,24 @@ func contains[T scopeT, C categoryT](s T, cat C, target string) bool {
if !typeAndCategoryMatches(cat, s.categorizer()) { if !typeAndCategoryMatches(cat, s.categorizer()) {
return false return false
} }
if len(target) == 0 { if len(target) == 0 {
return false return false
} }
compare := s[cat.String()] compare := s[cat.String()]
if len(compare) == 0 { if len(compare) == 0 {
return false return false
} }
if compare == NoneTgt { if compare == NoneTgt {
return false return false
} }
if compare == AnyTgt { if compare == AnyTgt {
return true return true
} }
return strings.Contains(compare, target) return strings.Contains(compare, target)
} }
@ -181,6 +187,7 @@ func getCatValue[T scopeT](s T, cat categorizer) []string {
if !ok { if !ok {
return None() return None()
} }
return split(v) return split(v)
} }
@ -203,6 +210,7 @@ func isAnyTarget[T scopeT, C categoryT](s T, cat C) bool {
if !typeAndCategoryMatches(cat, s.categorizer()) { if !typeAndCategoryMatches(cat, s.categorizer()) {
return false return false
} }
return s[cat.String()] == AnyTgt return s[cat.String()] == AnyTgt
} }
@ -229,6 +237,7 @@ func reduce[T scopeT, C categoryT](
for _, ent := range deets.Entries { for _, ent := range deets.Entries {
// todo: use Path pkg for this // todo: use Path pkg for this
path := strings.Split(ent.RepoRef, "/") path := strings.Split(ent.RepoRef, "/")
dc, ok := dataCategories[pathTypeIn(path)] dc, ok := dataCategories[pathTypeIn(path)]
if !ok { if !ok {
continue continue
@ -249,6 +258,7 @@ func reduce[T scopeT, C categoryT](
reduced := &details.Details{DetailsModel: deets.DetailsModel} reduced := &details.Details{DetailsModel: deets.DetailsModel}
reduced.Entries = ents reduced.Entries = ents
return reduced return reduced
} }
@ -275,6 +285,7 @@ func pathTypeIn(path []string) pathType {
if len(path) < 3 { if len(path) < 3 {
return unknownPathType return unknownPathType
} }
switch path[2] { switch path[2] {
case "mail": case "mail":
return exchangeMailPath return exchangeMailPath
@ -283,6 +294,7 @@ func pathTypeIn(path []string) pathType {
case "event": case "event":
return exchangeEventPath return exchangeEventPath
} }
return unknownPathType return unknownPathType
} }
@ -307,6 +319,7 @@ func scopesByCategory[T scopeT, C categoryT](
} }
} }
} }
return m return m
} }
@ -328,12 +341,14 @@ func passes[T scopeT](
if len(incs) > 0 { if len(incs) > 0 {
// at least one inclusion must apply. // at least one inclusion must apply.
var included bool var included bool
for _, inc := range incs { for _, inc := range incs {
if inc.matchesEntry(cat, pathValues, entry) { if inc.matchesEntry(cat, pathValues, entry) {
included = true included = true
break break
} }
} }
if !included { if !included {
return false return false
} }
@ -389,6 +404,7 @@ func matchesPathValues[T scopeT, C categoryT](
} }
} }
} }
return true return true
} }
@ -422,5 +438,6 @@ func typeAndCategoryMatches[C categoryT](a C, b categorizer) bool {
if !ok { if !ok {
return false return false
} }
return categoryMatches(a, bb) return categoryMatches(a, bb)
} }

View File

@ -226,6 +226,7 @@ func (suite *SelectorScopesSuite) TestReduce() {
dataCats := map[pathType]mockCategorizer{ dataCats := map[pathType]mockCategorizer{
unknownPathType: rootCatStub, unknownPathType: rootCatStub,
} }
for _, test := range reduceTestTable { for _, test := range reduceTestTable {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
ds := deets() ds := deets()
@ -264,6 +265,7 @@ func (suite *SelectorScopesSuite) TestPasses() {
cat := rootCatStub cat := rootCatStub
pathVals := map[categorizer]string{} pathVals := map[categorizer]string{}
entry := details.DetailsEntry{} entry := details.DetailsEntry{}
for _, test := range reduceTestTable { for _, test := range reduceTestTable {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
sel := test.sel() sel := test.sel()
@ -284,10 +286,13 @@ func toMockScope(sc []scope) []mockScope {
if len(sc) == 0 { if len(sc) == 0 {
return nil return nil
} }
ms := []mockScope{} ms := []mockScope{}
for _, s := range sc { for _, s := range sc {
ms = append(ms, mockScope(s)) ms = append(ms, mockScope(s))
} }
return ms return ms
} }

View File

@ -103,6 +103,7 @@ func (s Selector) String() string {
if err != nil { if err != nil {
return "error" return "error"
} }
return string(bs) return string(bs)
} }
@ -113,12 +114,14 @@ func appendScopes[T scopeT](to []scope, scopes ...[]T) []scope {
if len(to) == 0 { if len(to) == 0 {
to = []scope{} to = []scope{}
} }
for _, scopeSl := range scopes { for _, scopeSl := range scopes {
for _, s := range scopeSl { for _, s := range scopeSl {
s.setDefaults() s.setDefaults()
to = append(to, scope(s)) to = append(to, scope(s))
} }
} }
return to return to
} }
@ -126,9 +129,11 @@ func appendScopes[T scopeT](to []scope, scopes ...[]T) []scope {
// future TODO: if Inclues is nil, return filters. // future TODO: if Inclues is nil, return filters.
func scopes[T scopeT](s Selector) []T { func scopes[T scopeT](s Selector) []T {
scopes := []T{} scopes := []T{}
for _, v := range s.Includes { for _, v := range s.Includes {
scopes = append(scopes, T(v)) scopes = append(scopes, T(v))
} }
return scopes return scopes
} }
@ -158,10 +163,11 @@ func discreteScopes[T scopeT, C categoryT](
for k, v := range t { for k, v := range t {
w[k] = v w[k] = v
} }
set(w, rootCat, jdid) set(w, rootCat, jdid)
t = w t = w
} }
sl = append(sl, t) sl = append(sl, t)
} }
@ -203,9 +209,11 @@ func (p Printable) Resources() string {
if len(s) == 0 { if len(s) == 0 {
s = resourcesShortFormat(p.Filters) s = resourcesShortFormat(p.Filters)
} }
if len(s) == 0 { if len(s) == 0 {
s = "All" s = "All"
} }
return s return s
} }
@ -213,13 +221,16 @@ func (p Printable) Resources() string {
// plus, if more exist, " (len-1 more)" // plus, if more exist, " (len-1 more)"
func resourcesShortFormat(m map[string][]string) string { func resourcesShortFormat(m map[string][]string) string {
var s string var s string
for k := range m { for k := range m {
s = k s = k
break break
} }
if len(s) > 0 && len(m) > 1 { if len(s) > 0 && len(m) > 1 {
s = fmt.Sprintf("%s (%d more)", s, len(m)-1) s = fmt.Sprintf("%s (%d more)", s, len(m)-1)
} }
return s return s
} }
@ -230,14 +241,18 @@ func toResourceTypeMap(ms []scope) map[string][]string {
if len(ms) == 0 { if len(ms) == 0 {
return nil return nil
} }
r := make(map[string][]string) r := make(map[string][]string)
for _, m := range ms { for _, m := range ms {
res := m[scopeKeyResource] res := m[scopeKeyResource]
if res == AnyTgt { if res == AnyTgt {
res = All res = All
} }
r[res] = addToSet(r[res], m[scopeKeyDataType]) r[res] = addToSet(r[res], m[scopeKeyDataType])
} }
return r return r
} }
@ -248,11 +263,13 @@ func addToSet(set []string, v string) []string {
if len(set) == 0 { if len(set) == 0 {
return []string{v} return []string{v}
} }
for _, s := range set { for _, s := range set {
if s == v { if s == v {
return set return set
} }
} }
return append(set, v) return append(set, v)
} }
@ -293,13 +310,16 @@ func normalize(s []string) []string {
if len(s) == 0 { if len(s) == 0 {
return None() return None()
} }
for _, e := range s { for _, e := range s {
if e == AnyTgt { if e == AnyTgt {
return Any() return Any()
} }
if e == NoneTgt { if e == NoneTgt {
return None() return None()
} }
} }
return s return s
} }

View File

@ -133,6 +133,7 @@ func (suite *SelectorSuite) TestContains() {
does[key.String()] = target does[key.String()] = target
doesNot := stubScope("") doesNot := stubScope("")
doesNot[key.String()] = "smarf" doesNot[key.String()] = "smarf"
assert.True(t, contains(does, key, target), "does contain") assert.True(t, contains(does, key, target), "does contain")
assert.False(t, contains(doesNot, key, target), "does not contain") assert.False(t, contains(doesNot, key, target), "does not contain")
} }

View File

@ -26,16 +26,19 @@ func (c CommonConfig) StringConfig() (map[string]string, error) {
keyCommonCorsoPassword: c.CorsoPassword, keyCommonCorsoPassword: c.CorsoPassword,
keyCommonKopiaCfgDir: c.KopiaCfgDir, keyCommonKopiaCfgDir: c.KopiaCfgDir,
} }
return cfg, c.validate() return cfg, c.validate()
} }
// CommonConfig retrieves the CommonConfig details from the Storage config. // CommonConfig retrieves the CommonConfig details from the Storage config.
func (s Storage) CommonConfig() (CommonConfig, error) { func (s Storage) CommonConfig() (CommonConfig, error) {
c := CommonConfig{} c := CommonConfig{}
if len(s.Config) > 0 { if len(s.Config) > 0 {
c.CorsoPassword = orEmptyString(s.Config[keyCommonCorsoPassword]) c.CorsoPassword = orEmptyString(s.Config[keyCommonCorsoPassword])
c.KopiaCfgDir = orEmptyString(s.Config[keyCommonKopiaCfgDir]) c.KopiaCfgDir = orEmptyString(s.Config[keyCommonKopiaCfgDir])
} }
return c, c.validate() return c, c.validate()
} }
@ -44,6 +47,7 @@ func (c CommonConfig) validate() error {
if len(c.CorsoPassword) == 0 { if len(c.CorsoPassword) == 0 {
return errors.Wrap(errMissingRequired, credentials.CorsoPassword) return errors.Wrap(errMissingRequired, credentials.CorsoPassword)
} }
// kopiaCfgFilePath is not required // kopiaCfgFilePath is not required
return nil return nil
} }

View File

@ -43,12 +43,14 @@ func (c S3Config) StringConfig() (map[string]string, error) {
keyS3SecretKey: c.SecretKey, keyS3SecretKey: c.SecretKey,
keyS3SessionToken: c.SessionToken, keyS3SessionToken: c.SessionToken,
} }
return cfg, c.validate() return cfg, c.validate()
} }
// S3Config retrieves the S3Config details from the Storage config. // S3Config retrieves the S3Config details from the Storage config.
func (s Storage) S3Config() (S3Config, error) { func (s Storage) S3Config() (S3Config, error) {
c := S3Config{} c := S3Config{}
if len(s.Config) > 0 { if len(s.Config) > 0 {
c.AccessKey = orEmptyString(s.Config[keyS3AccessKey]) c.AccessKey = orEmptyString(s.Config[keyS3AccessKey])
c.Bucket = orEmptyString(s.Config[keyS3Bucket]) c.Bucket = orEmptyString(s.Config[keyS3Bucket])
@ -57,6 +59,7 @@ func (s Storage) S3Config() (S3Config, error) {
c.SecretKey = orEmptyString(s.Config[keyS3SecretKey]) c.SecretKey = orEmptyString(s.Config[keyS3SecretKey])
c.SessionToken = orEmptyString(s.Config[keyS3SessionToken]) c.SessionToken = orEmptyString(s.Config[keyS3SessionToken])
} }
return c, c.validate() return c, c.validate()
} }
@ -72,5 +75,6 @@ func (c S3Config) validate() error {
return errors.Wrap(errMissingRequired, k) return errors.Wrap(errMissingRequired, k)
} }
} }
return nil return nil
} }

View File

@ -38,6 +38,7 @@ type Storage struct {
// NewStorage aggregates all the supplied configurations into a single configuration. // NewStorage aggregates all the supplied configurations into a single configuration.
func NewStorage(p storageProvider, cfgs ...common.StringConfigurer) (Storage, error) { func NewStorage(p storageProvider, cfgs ...common.StringConfigurer) (Storage, error) {
cs, err := common.UnionStringConfigs(cfgs...) cs, err := common.UnionStringConfigs(cfgs...)
return Storage{ return Storage{
Provider: p, Provider: p,
Config: cs, Config: cs,
@ -53,8 +54,10 @@ func orEmptyString(v any) string {
fmt.Printf("panic recovery casting %v to string\n", v) fmt.Printf("panic recovery casting %v to string\n", v)
} }
}() }()
if v == nil { if v == nil {
return "" return ""
} }
return v.(string) return v.(string)
} }

View File

@ -14,10 +14,12 @@ import (
// GetBackup gets a single backup by id. // GetBackup gets a single backup by id.
func (w Wrapper) GetBackup(ctx context.Context, backupID model.StableID) (*backup.Backup, error) { func (w Wrapper) GetBackup(ctx context.Context, backupID model.StableID) (*backup.Backup, error) {
b := backup.Backup{} b := backup.Backup{}
err := w.Get(ctx, model.BackupSchema, backupID, &b) err := w.Get(ctx, model.BackupSchema, backupID, &b)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "getting backup") return nil, errors.Wrap(err, "getting backup")
} }
return &b, nil return &b, nil
} }
@ -27,15 +29,20 @@ func (w Wrapper) GetBackups(ctx context.Context) ([]backup.Backup, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
bs := make([]backup.Backup, len(bms)) bs := make([]backup.Backup, len(bms))
for i, bm := range bms { for i, bm := range bms {
b := backup.Backup{} b := backup.Backup{}
err := w.GetWithModelStoreID(ctx, model.BackupSchema, bm.ModelStoreID, &b) err := w.GetWithModelStoreID(ctx, model.BackupSchema, bm.ModelStoreID, &b)
if err != nil { if err != nil {
return nil, err return nil, err
} }
bs[i] = b bs[i] = b
} }
return bs, nil return bs, nil
} }
@ -45,19 +52,23 @@ func (w Wrapper) DeleteBackup(ctx context.Context, backupID model.StableID) erro
if err != nil { if err != nil {
return err return err
} }
if err := w.Delete(ctx, model.BackupDetailsSchema, deets.ID); err != nil { if err := w.Delete(ctx, model.BackupDetailsSchema, deets.ID); err != nil {
return err return err
} }
return w.Delete(ctx, model.BackupSchema, backupID) return w.Delete(ctx, model.BackupSchema, backupID)
} }
// GetDetails gets the backup details by ID. // GetDetails gets the backup details by ID.
func (w Wrapper) GetDetails(ctx context.Context, detailsID manifest.ID) (*details.Details, error) { func (w Wrapper) GetDetails(ctx context.Context, detailsID manifest.ID) (*details.Details, error) {
d := details.Details{} d := details.Details{}
err := w.GetWithModelStoreID(ctx, model.BackupDetailsSchema, detailsID, &d) err := w.GetWithModelStoreID(ctx, model.BackupDetailsSchema, detailsID, &d)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "getting details") return nil, errors.Wrap(err, "getting details")
} }
return &d, nil return &d, nil
} }

View File

@ -65,6 +65,7 @@ func (mms *MockModelStore) Get(
if mms.err != nil { if mms.err != nil {
return mms.err return mms.err
} }
switch s { switch s {
case model.BackupSchema: case model.BackupSchema:
unmarshal(mms.backup, data) unmarshal(mms.backup, data)
@ -73,6 +74,7 @@ func (mms *MockModelStore) Get(
default: default:
return errors.Errorf("schema %s not supported by mock Get", s) return errors.Errorf("schema %s not supported by mock Get", s)
} }
return nil return nil
} }
@ -84,16 +86,20 @@ func (mms *MockModelStore) GetIDsForType(
if mms.err != nil { if mms.err != nil {
return nil, mms.err return nil, mms.err
} }
switch s { switch s {
case model.BackupSchema: case model.BackupSchema:
b := backup.Backup{} b := backup.Backup{}
unmarshal(mms.backup, &b) unmarshal(mms.backup, &b)
return []*model.BaseModel{&b.BaseModel}, nil return []*model.BaseModel{&b.BaseModel}, nil
case model.BackupDetailsSchema: case model.BackupDetailsSchema:
d := details.Details{} d := details.Details{}
unmarshal(mms.backup, &d) unmarshal(mms.backup, &d)
return []*model.BaseModel{&d.BaseModel}, nil return []*model.BaseModel{&d.BaseModel}, nil
} }
return nil, errors.Errorf("schema %s not supported by mock GetIDsForType", s) return nil, errors.Errorf("schema %s not supported by mock GetIDsForType", s)
} }
@ -106,6 +112,7 @@ func (mms *MockModelStore) GetWithModelStoreID(
if mms.err != nil { if mms.err != nil {
return mms.err return mms.err
} }
switch s { switch s {
case model.BackupSchema: case model.BackupSchema:
unmarshal(mms.backup, data) unmarshal(mms.backup, data)
@ -114,6 +121,7 @@ func (mms *MockModelStore) GetWithModelStoreID(
default: default:
return errors.Errorf("schema %s not supported by mock GetWithModelStoreID", s) return errors.Errorf("schema %s not supported by mock GetWithModelStoreID", s)
} }
return nil return nil
} }
@ -130,6 +138,7 @@ func (mms *MockModelStore) Put(ctx context.Context, s model.Schema, m model.Mode
default: default:
return errors.Errorf("schema %s not supported by mock Put", s) return errors.Errorf("schema %s not supported by mock Put", s)
} }
return mms.err return mms.err
} }
@ -142,5 +151,6 @@ func (mms *MockModelStore) Update(ctx context.Context, s model.Schema, m model.M
default: default:
return errors.Errorf("schema %s not supported by mock Update", s) return errors.Errorf("schema %s not supported by mock Update", s)
} }
return mms.err return mms.err
} }