corso/src/pkg/fault/item.go
Abin Simon 68256cf59f
Change printing of backup stats to single line (#4526)
Previously we were printing a table for a single item which, for one
was unnecessary, but this also make the output of a backup more
compact.

Before:

```
Logging to file: /home/meain/.cache/corso/logs/2023-10-20T10-21-23Z.log
Connecting to M365                               done
Connecting to repository                      1s done

Backing Up ∙ Teams Testing (meain)
Discovering items to backup                  27s done
    Libraries (TeamsTestingmeain)                  done (found 0 items)
    Libraries (TeamsTestingmeain-Shared0)          done (found 0 items)
    Libraries (TeamsTestingmeain-Private2)         done (found 0 items)
    Messages                                    7s done (found 6 channels)
Backing up data                               7s done
Backup complete ∙ 7b40dd40-f808-4d57-8e39-b4553e48dc5d
    ID                                    Bytes Uploaded  Items Uploaded  Items Skipped  Errors
    7b40dd40-f808-4d57-8e39-b4553e48dc5d  0 B             0               0              0

Completed Backups:
    ID                                    Started At            Duration       Status     Resource Owner
    7b40dd40-f808-4d57-8e39-b4553e48dc5d  2023-10-20T10:21:32Z  36.632747912s  Completed  Teams Testing (meain)
```

After:
```
Connecting to M365                               done
Connecting to repository                      1s done

Backing Up ∙ Teams Testing (meain)
Discovering items to backup                  31s done
    Libraries (TeamsTestingmeain)                  done (found 0 items)
    Libraries (TeamsTestingmeain-Shared0)          done (found 0 items)
    Libraries (TeamsTestingmeain-Private2)         done (found 0 items)
    Messages                                    9s done (found 6 channels)
Backing up data                               7s done
Backup complete ∙ ffb2f619-1cb7-4a11-b3e2-7300aa513c6a
Bytes Uploaded: 0 B | Items Uploaded: 0 | Items Skipped: 0 | Errors: 0

Completed Backups:
    ID                                    ID                    Started At     Duration   Status                 Resource Owner
    ffb2f619-1cb7-4a11-b3e2-7300aa513c6a  2023-10-20T10:23:35Z  40.096203016s  Completed  Teams Testing (meain)
```


---

#### 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

<!--- Please check the type of change your PR introduces: --->
- [x] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Supportability/Tests
- [ ] 💻 CI/Deployment
- [ ] 🧹 Tech Debt/Cleanup

#### Issue(s)

<!-- Can reference multiple issues. Use one of the following "magic words" - "closes, fixes" to auto-close the Github issue. -->
* #<issue>

#### Test Plan

<!-- How will this be tested prior to merging.-->
- [ ] 💪 Manual
- [x]  Unit test
- [ ] 💚 E2E
2023-11-16 12:43:27 +00:00

152 lines
4.4 KiB
Go

package fault
import "github.com/alcionai/corso/src/cli/print"
const (
AddtlCreatedBy = "created_by"
AddtlLastModBy = "last_modified_by"
AddtlContainerID = "container_id"
AddtlContainerName = "container_name"
AddtlContainerPath = "container_path"
AddtlMalwareDesc = "malware_description"
)
type ItemType string
const (
FileType ItemType = "file"
ContainerType ItemType = "container"
ResourceOwnerType ItemType = "resource_owner"
)
func (it ItemType) Printable() string {
switch it {
case FileType:
return "File"
case ContainerType:
return "Container"
case ResourceOwnerType:
return "Resource Owner"
}
return "Unknown"
}
var (
_ error = &Item{}
_ print.Printable = &Item{}
)
// Item contains a concrete reference to a thing that failed
// during processing. The categorization of the item is determined
// by its Type: file, container, or reourceOwner.
//
// Item is compliant with the error interface so that it can be
// aggregated with the fault bus, and deserialized using the
// errors.As() func. The idea is that fault,Items, during
// processing, will get packed into bus.AddRecoverable (or failure)
// as part of standard error handling, and later deserialized
// by the end user (cli or sdk) for surfacing human-readable and
// identifiable points of failure.
type Item struct {
// deduplication namespace; the maximally-unique boundary of the
// item ID. The scope of this boundary depends on the service.
// ex: exchange items are unique within their category, drive items
// are only unique within a given drive.
Namespace string `json:"namespace"`
// deduplication identifier; the ID of the observed item.
ID string `json:"id"`
// a human-readable reference: file/container name, email, etc
Name string `json:"name"`
// tracks the type of item represented by this entry.
Type ItemType `json:"type"`
// Error() of the causal error, or a sentinel if this is the
// source of the error. In case of ID collisions, the first
// item takes priority.
Cause string `json:"cause"`
// Additional is a catch-all map for storing data that might
// be relevant to particular types or contexts of items without
// being globally relevant. Ex: parent container references,
// created-by ids, last modified, etc. Should be used sparingly,
// only for information that might be immediately relevant to the
// end user.
Additional map[string]any `json:"additional"`
}
// dedupeID is the id used to deduplicate items when aggreagating
// errors in fault.Errors().
func (i *Item) dedupeID() string {
return i.Namespace + i.ID
}
// Error complies with the error interface.
func (i *Item) Error() string {
if i == nil {
return "<nil>"
}
if len(i.Type) == 0 {
return "processing item of unknown type"
}
return string("processing " + i.Type)
}
func (i Item) MinimumPrintable() any {
return i
}
// Headers returns the human-readable names of properties of an Item
// for printing out to a terminal.
func (i Item) Headers(bool) []string {
// NOTE: skipID does not make sense in this context
return []string{"Action", "Type", "Name", "Container", "Cause"}
}
// Values populates the printable values matching the Headers list.
func (i Item) Values(bool) []string {
var cn string
acn, ok := i.Additional[AddtlContainerName]
if ok {
str, ok := acn.(string)
if ok {
cn = str
}
}
return []string{"Error", i.Type.Printable(), i.Name, cn, i.Cause}
}
// ContainerErr produces a Container-type Item for tracking erroneous items
func ContainerErr(cause error, namespace, id, name string, addtl map[string]any) *Item {
return itemErr(ContainerType, cause, namespace, id, name, addtl)
}
// FileErr produces a File-type Item for tracking erroneous items.
func FileErr(cause error, namespace, id, name string, addtl map[string]any) *Item {
return itemErr(FileType, cause, namespace, id, name, addtl)
}
// OnwerErr produces a ResourceOwner-type Item for tracking erroneous items.
func OwnerErr(cause error, namespace, id, name string, addtl map[string]any) *Item {
return itemErr(ResourceOwnerType, cause, namespace, id, name, addtl)
}
// itemErr produces a Item of the provided type for tracking erroneous items.
func itemErr(t ItemType, cause error, namespace, id, name string, addtl map[string]any) *Item {
return &Item{
Namespace: namespace,
ID: id,
Name: name,
Type: t,
Cause: cause.Error(),
Additional: addtl,
}
}