Don't display folders in restore output (#3414)
#### Does this PR need a docs update or release note? - [x] ✅ Yes, it's included - [ ] 🕐 Yes, but in a later PR - [ ] ⛔ No #### Type of change - [ ] 🌻 Feature - [x] 🐛 Bugfix - [ ] 🗺️ Documentation - [ ] 🤖 Supportability/Tests - [ ] 💻 CI/Deployment - [ ] 🧹 Tech Debt/Cleanup #### Issue(s) * closes #3281 #### Test Plan - [x] 💪 Manual - [ ] ⚡ Unit test - [ ] 💚 E2E
This commit is contained in:
parent
ecb1fdc40a
commit
04a30ac662
@ -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.
|
- 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 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
|
- 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
|
### Known Issues
|
||||||
- Restore operations will merge duplicate Exchange folders at the same hierarchy level into a single folder.
|
- Restore operations will merge duplicate Exchange folders at the same hierarchy level into a single folder.
|
||||||
|
|||||||
@ -116,7 +116,7 @@ func restoreExchangeCmd(cmd *cobra.Command, args []string) error {
|
|||||||
return Only(ctx, clues.Wrap(err, "Failed to run Exchange restore"))
|
return Only(ctx, clues.Wrap(err, "Failed to run Exchange restore"))
|
||||||
}
|
}
|
||||||
|
|
||||||
ds.PrintEntries(ctx)
|
ds.Items().PrintEntries(ctx)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -117,7 +117,7 @@ func restoreOneDriveCmd(cmd *cobra.Command, args []string) error {
|
|||||||
return Only(ctx, clues.Wrap(err, "Failed to run OneDrive restore"))
|
return Only(ctx, clues.Wrap(err, "Failed to run OneDrive restore"))
|
||||||
}
|
}
|
||||||
|
|
||||||
ds.PrintEntries(ctx)
|
ds.Items().PrintEntries(ctx)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -122,7 +122,7 @@ func restoreSharePointCmd(cmd *cobra.Command, args []string) error {
|
|||||||
return Only(ctx, clues.Wrap(err, "Failed to run SharePoint restore"))
|
return Only(ctx, clues.Wrap(err, "Failed to run SharePoint restore"))
|
||||||
}
|
}
|
||||||
|
|
||||||
ds.PrintEntries(ctx)
|
ds.Items().PrintEntries(ctx)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -139,25 +139,36 @@ type DetailsModel struct {
|
|||||||
// Print writes the DetailModel Entries to StdOut, in the format
|
// Print writes the DetailModel Entries to StdOut, in the format
|
||||||
// requested by the caller.
|
// requested by the caller.
|
||||||
func (dm DetailsModel) PrintEntries(ctx context.Context) {
|
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() {
|
if print.DisplayJSONFormat() {
|
||||||
printJSON(ctx, dm)
|
printJSON(ctx, entries)
|
||||||
} else {
|
} 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{}
|
perType := map[ItemType][]print.Printable{}
|
||||||
|
|
||||||
for _, de := range dm.Entries {
|
for _, ent := range entries {
|
||||||
it := de.infoType()
|
it := ent.infoType()
|
||||||
ps, ok := perType[it]
|
ps, ok := perType[it]
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
ps = []print.Printable{}
|
ps = []print.Printable{}
|
||||||
}
|
}
|
||||||
|
|
||||||
perType[it] = append(ps, print.Printable(de))
|
perType[it] = append(ps, print.Printable(ent))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ps := range perType {
|
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{}
|
ents := []print.Printable{}
|
||||||
|
|
||||||
for _, ent := range dm.Entries {
|
for _, ent := range entries {
|
||||||
ents = append(ents, print.Printable(ent))
|
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
|
// Items returns a slice of *ItemInfo that does not contain any FolderInfo
|
||||||
// entries. Required because not all folders in the details are valid resource
|
// entries. Required because not all folders in the details are valid resource
|
||||||
// paths, and we want to slice out metadata.
|
// 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))
|
res := make([]*Entry, 0, len(dm.Entries))
|
||||||
|
|
||||||
for i := 0; i < len(dm.Entries); i++ {
|
for i := 0; i < len(dm.Entries); i++ {
|
||||||
@ -457,6 +468,13 @@ func withoutMetadataSuffix(id string) string {
|
|||||||
// Entry
|
// 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
|
// Entry describes a single item stored in a Backup
|
||||||
type Entry struct {
|
type Entry struct {
|
||||||
// RepoRef is the full storage path of the item in Kopia
|
// RepoRef is the full storage path of the item in Kopia
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user