From ad2d046d19d1a0effe220899b1820719d890af21 Mon Sep 17 00:00:00 2001 From: ashmrtn Date: Thu, 15 Sep 2022 17:17:28 -0700 Subject: [PATCH] Create folder struct for backup details (#869) ## Description Folder entries allow recording the directory hierarchy of a backup in backup details for other consumers. Although folders are present in the backup details and they have the logic to be displayed, they are not expected to be shown to users (later PRs will ensure this). ## Type of change - [x] :sunflower: Feature - [ ] :bug: Bugfix - [ ] :world_map: Documentation - [ ] :robot: Test - [ ] :computer: CI/Deployment - [ ] :hamster: Trivial/Minor ## Issue(s) * closes #860 ## Test Plan - [ ] :muscle: Manual - [x] :zap: Unit test - [ ] :green_heart: E2E --- src/pkg/backup/details/details.go | 61 +++++++++++++++++++++++++- src/pkg/backup/details/details_test.go | 57 ++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 1 deletion(-) diff --git a/src/pkg/backup/details/details.go b/src/pkg/backup/details/details.go index dc90af031..133ddea7b 100644 --- a/src/pkg/backup/details/details.go +++ b/src/pkg/backup/details/details.go @@ -11,6 +11,12 @@ import ( "github.com/alcionai/corso/src/internal/model" ) +type FolderEntry struct { + RepoRef string + ShortRef string + Info ItemInfo +} + // -------------------------------------------------------------------------------- // Model // -------------------------------------------------------------------------------- @@ -83,7 +89,8 @@ type Details struct { DetailsModel // internal - mu sync.Mutex `json:"-"` + mu sync.Mutex `json:"-"` + knownFolders map[string]struct{} `json:"-"` } func (d *Details) Add(repoRef, shortRef string, info ItemInfo) { @@ -96,6 +103,31 @@ func (d *Details) Add(repoRef, shortRef string, info ItemInfo) { }) } +// AddFolders adds entries for the given folders. It skips adding entries that +// have been added by previous calls. +func (d *Details) AddFolders(folders []FolderEntry) { + d.mu.Lock() + defer d.mu.Unlock() + + if d.knownFolders == nil { + d.knownFolders = map[string]struct{}{} + } + + for _, folder := range folders { + if _, ok := d.knownFolders[folder.ShortRef]; ok { + // Entry already exists, nothing to do. + continue + } + + d.knownFolders[folder.ShortRef] = struct{}{} + d.Entries = append(d.Entries, DetailsEntry{ + RepoRef: folder.RepoRef, + ShortRef: folder.ShortRef, + ItemInfo: folder.Info, + }) + } +} + // -------------------------------------------------------------------------------- // Entry // -------------------------------------------------------------------------------- @@ -127,6 +159,10 @@ func (de DetailsEntry) MinimumPrintable() any { func (de DetailsEntry) Headers() []string { hs := []string{"Reference"} + if de.ItemInfo.Folder != nil { + hs = append(hs, de.ItemInfo.Folder.Headers()...) + } + if de.ItemInfo.Exchange != nil { hs = append(hs, de.ItemInfo.Exchange.Headers()...) } @@ -146,6 +182,10 @@ func (de DetailsEntry) Headers() []string { func (de DetailsEntry) Values() []string { vs := []string{de.ShortRef} + if de.ItemInfo.Folder != nil { + vs = append(vs, de.ItemInfo.Folder.Values()...) + } + if de.ItemInfo.Exchange != nil { vs = append(vs, de.ItemInfo.Exchange.Values()...) } @@ -174,11 +214,14 @@ const ( SharepointItem itemType = iota + 100 OneDriveItem itemType = iota + 200 + + FolderItem itemType = iota + 300 ) // ItemInfo is a oneOf that contains service specific // information about the item it tracks type ItemInfo struct { + Folder *FolderInfo `json:"folder,omitempty"` Exchange *ExchangeInfo `json:"exchange,omitempty"` Sharepoint *SharepointInfo `json:"sharepoint,omitempty"` OneDrive *OneDriveInfo `json:"oneDrive,omitempty"` @@ -192,6 +235,9 @@ type ItemInfo struct { // calendar event). func (i ItemInfo) infoType() itemType { switch { + case i.Folder != nil: + return i.Folder.ItemType + case i.Exchange != nil: return i.Exchange.ItemType @@ -205,6 +251,19 @@ func (i ItemInfo) infoType() itemType { return UnknownType } +type FolderInfo struct { + ItemType itemType + DisplayName string `json:"displayName"` +} + +func (i FolderInfo) Headers() []string { + return []string{"Display Name"} +} + +func (i FolderInfo) Values() []string { + return []string{i.DisplayName} +} + // ExchangeInfo describes an exchange item type ExchangeInfo struct { ItemType itemType diff --git a/src/pkg/backup/details/details_test.go b/src/pkg/backup/details/details_test.go index 27a1200fd..f78e7106e 100644 --- a/src/pkg/backup/details/details_test.go +++ b/src/pkg/backup/details/details_test.go @@ -170,3 +170,60 @@ func (suite *DetailsUnitSuite) TestDetailsModel_Path() { }) } } + +func (suite *DetailsUnitSuite) TestDetails_AddFolders() { + table := []struct { + name string + folders []details.FolderEntry + expectedShortRefs []string + }{ + { + name: "MultipleFolders", + folders: []details.FolderEntry{ + { + RepoRef: "rr1", + ShortRef: "sr1", + }, + { + RepoRef: "rr2", + ShortRef: "sr2", + }, + }, + expectedShortRefs: []string{"sr1", "sr2"}, + }, + { + name: "MultipleFoldersWithRepeats", + folders: []details.FolderEntry{ + { + RepoRef: "rr1", + ShortRef: "sr1", + }, + { + RepoRef: "rr2", + ShortRef: "sr2", + }, + { + RepoRef: "rr1", + ShortRef: "sr1", + }, + { + RepoRef: "rr3", + ShortRef: "sr3", + }, + }, + expectedShortRefs: []string{"sr1", "sr2", "sr3"}, + }, + } + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + deets := details.Details{} + deets.AddFolders(test.folders) + + assert.Len(t, deets.Entries, len(test.expectedShortRefs)) + + for _, e := range deets.Entries { + assert.Contains(t, test.expectedShortRefs, e.ShortRef) + } + }) + } +}