From 04a30ac662a260fc6515a8cce1c70c6686894a9d Mon Sep 17 00:00:00 2001 From: ashmrtn Date: Mon, 15 May 2023 16:28:04 -0700 Subject: [PATCH] Don't display folders in restore output (#3414) #### Does this PR need a docs update or release note? - [x] :white_check_mark: Yes, it's included - [ ] :clock1: Yes, but in a later PR - [ ] :no_entry: No #### Type of change - [ ] :sunflower: Feature - [x] :bug: Bugfix - [ ] :world_map: Documentation - [ ] :robot: Supportability/Tests - [ ] :computer: CI/Deployment - [ ] :broom: Tech Debt/Cleanup #### Issue(s) * closes #3281 #### Test Plan - [x] :muscle: Manual - [ ] :zap: Unit test - [ ] :green_heart: E2E --- CHANGELOG.md | 1 + src/cli/restore/exchange.go | 2 +- src/cli/restore/onedrive.go | 2 +- src/cli/restore/sharepoint.go | 2 +- src/pkg/backup/details/details.go | 36 +++++++++++++++++++++++-------- 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bad0fc9a..369ea303e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Refined oneDrive rate limiter controls to reduce throttling errors. - Fix handling of duplicate folders at the same hierarchy level in Exchange. Duplicate folders will be merged during restore operations. - Fix backup for mailboxes that has used up all their storage quota +- Restored folders no longer appear in the Restore results. Only restored items will be displayed. ### Known Issues - Restore operations will merge duplicate Exchange folders at the same hierarchy level into a single folder. diff --git a/src/cli/restore/exchange.go b/src/cli/restore/exchange.go index f7f7fdd9c..1f80958ab 100644 --- a/src/cli/restore/exchange.go +++ b/src/cli/restore/exchange.go @@ -116,7 +116,7 @@ func restoreExchangeCmd(cmd *cobra.Command, args []string) error { return Only(ctx, clues.Wrap(err, "Failed to run Exchange restore")) } - ds.PrintEntries(ctx) + ds.Items().PrintEntries(ctx) return nil } diff --git a/src/cli/restore/onedrive.go b/src/cli/restore/onedrive.go index 1b0ebf410..a3724229e 100644 --- a/src/cli/restore/onedrive.go +++ b/src/cli/restore/onedrive.go @@ -117,7 +117,7 @@ func restoreOneDriveCmd(cmd *cobra.Command, args []string) error { return Only(ctx, clues.Wrap(err, "Failed to run OneDrive restore")) } - ds.PrintEntries(ctx) + ds.Items().PrintEntries(ctx) return nil } diff --git a/src/cli/restore/sharepoint.go b/src/cli/restore/sharepoint.go index e974af3f7..63678a718 100644 --- a/src/cli/restore/sharepoint.go +++ b/src/cli/restore/sharepoint.go @@ -122,7 +122,7 @@ func restoreSharePointCmd(cmd *cobra.Command, args []string) error { return Only(ctx, clues.Wrap(err, "Failed to run SharePoint restore")) } - ds.PrintEntries(ctx) + ds.Items().PrintEntries(ctx) return nil } diff --git a/src/pkg/backup/details/details.go b/src/pkg/backup/details/details.go index 578b16015..14a92eb99 100644 --- a/src/pkg/backup/details/details.go +++ b/src/pkg/backup/details/details.go @@ -139,25 +139,36 @@ type DetailsModel struct { // Print writes the DetailModel Entries to StdOut, in the format // requested by the caller. func (dm DetailsModel) PrintEntries(ctx context.Context) { + printEntries(ctx, dm.Entries) +} + +type infoer interface { + Entry | *Entry + // Need this here so we can access the infoType function without a type + // assertion. See https://stackoverflow.com/a/71378366 for more details. + infoType() ItemType +} + +func printEntries[T infoer](ctx context.Context, entries []T) { if print.DisplayJSONFormat() { - printJSON(ctx, dm) + printJSON(ctx, entries) } else { - printTable(ctx, dm) + printTable(ctx, entries) } } -func printTable(ctx context.Context, dm DetailsModel) { +func printTable[T infoer](ctx context.Context, entries []T) { perType := map[ItemType][]print.Printable{} - for _, de := range dm.Entries { - it := de.infoType() + for _, ent := range entries { + it := ent.infoType() ps, ok := perType[it] if !ok { ps = []print.Printable{} } - perType[it] = append(ps, print.Printable(de)) + perType[it] = append(ps, print.Printable(ent)) } for _, ps := range perType { @@ -165,10 +176,10 @@ func printTable(ctx context.Context, dm DetailsModel) { } } -func printJSON(ctx context.Context, dm DetailsModel) { +func printJSON[T infoer](ctx context.Context, entries []T) { ents := []print.Printable{} - for _, ent := range dm.Entries { + for _, ent := range entries { ents = append(ents, print.Printable(ent)) } @@ -194,7 +205,7 @@ func (dm DetailsModel) Paths() []string { // Items returns a slice of *ItemInfo that does not contain any FolderInfo // entries. Required because not all folders in the details are valid resource // paths, and we want to slice out metadata. -func (dm DetailsModel) Items() []*Entry { +func (dm DetailsModel) Items() entrySet { res := make([]*Entry, 0, len(dm.Entries)) for i := 0; i < len(dm.Entries); i++ { @@ -457,6 +468,13 @@ func withoutMetadataSuffix(id string) string { // Entry // -------------------------------------------------------------------------------- +// Add a new type so we can transparently use PrintAll in different situations. +type entrySet []*Entry + +func (ents entrySet) PrintEntries(ctx context.Context) { + printEntries(ctx, ents) +} + // Entry describes a single item stored in a Backup type Entry struct { // RepoRef is the full storage path of the item in Kopia