From 9b8ee4f3f65a010f0d303ea2ae885426d532dd19 Mon Sep 17 00:00:00 2001 From: Ashlie Martinez Date: Thu, 5 Oct 2023 13:06:05 -0700 Subject: [PATCH] Add way to extract reason from tags Helper functions to store reason information as tags in a backup model and extract them. Also add tests for this. --- src/pkg/backup/backup.go | 44 ++++++++++ src/pkg/backup/backup_test.go | 151 ++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) diff --git a/src/pkg/backup/backup.go b/src/pkg/backup/backup.go index 76e068562..228d0c93a 100644 --- a/src/pkg/backup/backup.go +++ b/src/pkg/backup/backup.go @@ -2,6 +2,7 @@ package backup import ( "context" + "errors" "fmt" "strconv" "strings" @@ -12,6 +13,7 @@ import ( "github.com/alcionai/corso/src/cli/print" "github.com/alcionai/corso/src/internal/common/dttm" + "github.com/alcionai/corso/src/internal/common/errs" "github.com/alcionai/corso/src/internal/common/str" "github.com/alcionai/corso/src/internal/model" "github.com/alcionai/corso/src/internal/stats" @@ -242,6 +244,48 @@ func (b Backup) Bases() (PersistedBaseSet, error) { return res, nil } +func (b Backup) Tenant() (string, error) { + t := b.Tags[tenantIDKey] + if len(t) == 0 { + return "", clues.Wrap(errs.NotFound, "getting tenant") + } + + return t, nil +} + +// Reasons returns the set of services and categories this backup encompassed +// for the tenant and protected resource. +func (b Backup) Reasons() ([]identity.Reasoner, error) { + tenant, err := b.Tenant() + if err != nil { + return nil, clues.Stack(err) + } + + var res []identity.Reasoner + + for tag := range b.Tags { + service, cat, err := serviceCatStringToTypes(tag) + if err != nil { + // Assume it's just not one of the Reason tags. + if errors.Is(err, errMissingPrefix) { + continue + } + + return nil, clues.Wrap(err, "parsing reasons") + } + + res = append( + res, + identity.NewReason( + tenant, + str.First(b.ProtectedResourceID, b.Selector.DiscreteOwner), + service, + cat)) + } + + return res, nil +} + // -------------------------------------------------------------------------------- // CLI Output // -------------------------------------------------------------------------------- diff --git a/src/pkg/backup/backup_test.go b/src/pkg/backup/backup_test.go index 3c8a02479..7d086830f 100644 --- a/src/pkg/backup/backup_test.go +++ b/src/pkg/backup/backup_test.go @@ -13,6 +13,7 @@ import ( "golang.org/x/exp/maps" "github.com/alcionai/corso/src/internal/common/dttm" + "github.com/alcionai/corso/src/internal/common/errs" "github.com/alcionai/corso/src/internal/model" "github.com/alcionai/corso/src/internal/stats" "github.com/alcionai/corso/src/internal/tester" @@ -283,6 +284,156 @@ func (suite *BackupUnitSuite) TestBackup_Bases() { } } +func (suite *BackupUnitSuite) TestBackup_Tenant() { + const tenant = "tenant-id" + + stub := stubBackup(time.Now(), "user-id", "user-name") + + table := []struct { + name string + inputKey string + inputValue string + expectErr assert.ErrorAssertionFunc + expect string + }{ + { + name: "ProperlyFormatted", + inputKey: tenantIDKey, + inputValue: tenant, + expectErr: assert.NoError, + expect: tenant, + }, + { + name: "WrongKey", + inputKey: "foo", + inputValue: tenant, + expectErr: assert.Error, + }, + { + name: "EmptyValue", + inputKey: tenantIDKey, + inputValue: "", + expectErr: assert.Error, + }, + } + + for _, test := range table { + suite.Run(test.name, func() { + t := suite.T() + + b := stub + b.Tags = map[string]string{test.inputKey: test.inputValue} + + gotTenant, err := b.Tenant() + test.expectErr(t, err, clues.ToCore(err)) + + if err != nil { + assert.ErrorIs(t, err, errs.NotFound) + return + } + + assert.Equal(t, test.expect, gotTenant) + }) + } +} + +func (suite *BackupUnitSuite) TestBackup_Reasons() { + const ( + tenantID = "tenant-id" + userID = "user-id" + ) + + stub := stubBackup(time.Now(), userID, "user-name") + + defaultEmailReason := identity.NewReason( + tenantID, + stub.ProtectedResourceID, + path.ExchangeService, + path.EmailCategory) + defaultContactsReason := identity.NewReason( + tenantID, + stub.ProtectedResourceID, + path.ExchangeService, + path.ContactsCategory) + + table := []struct { + name string + getBackup func() *Backup + expectErr assert.ErrorAssertionFunc + expect []identity.Reasoner + }{ + { + name: "SingleReason", + getBackup: func() *Backup { + res := stub + res.Tags = map[string]string{} + + for k, v := range reasonTags(defaultEmailReason) { + res.Tags[k] = v + } + + return &res + }, + expectErr: assert.NoError, + expect: []identity.Reasoner{defaultEmailReason}, + }, + { + name: "MultipleReasons", + getBackup: func() *Backup { + res := stub + res.Tags = map[string]string{} + + for _, reason := range []identity.Reasoner{defaultEmailReason, defaultContactsReason} { + for k, v := range reasonTags(reason) { + res.Tags[k] = v + } + } + + return &res + }, + expectErr: assert.NoError, + expect: []identity.Reasoner{ + defaultEmailReason, + defaultContactsReason, + }, + }, + { + name: "SingleReason OtherTags", + getBackup: func() *Backup { + res := stub + res.Tags = map[string]string{} + + for k, v := range reasonTags(defaultEmailReason) { + res.Tags[k] = v + } + + res.Tags["foo"] = "bar" + + return &res + }, + expectErr: assert.NoError, + expect: []identity.Reasoner{defaultEmailReason}, + }, + } + + for _, test := range table { + suite.Run(test.name, func() { + t := suite.T() + + bup := test.getBackup() + + got, err := bup.Reasons() + test.expectErr(t, err, clues.ToCore(err)) + + if err != nil { + return + } + + assert.ElementsMatch(t, test.expect, got) + }) + } +} + func (suite *BackupUnitSuite) TestBackup_HeadersValues() { var ( t = suite.T()