print separate tables per entry type (#709)
## Description Since a backup can encapsulate multiple data types (ex: mail, contacts, and events), it doesn't make sense to print one table with all disjoint values. This change splits up the print output so that each data type in the details gets its own table. ## Type of change Please check the type of change your PR introduces: - [x] 🌻 Feature ## Issue(s) #501 ## Test Plan - [ ] 💪 Manual - [x] ⚡ Unit test - [ ] 💚 E2E
This commit is contained in:
parent
6de326a2c4
commit
5c59522fc2
@ -15,6 +15,7 @@ func ContactInfo(contact models.Contactable) *details.ExchangeInfo {
|
||||
}
|
||||
|
||||
return &details.ExchangeInfo{
|
||||
ItemType: details.ExchangeContact,
|
||||
ContactName: name,
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/alcionai/corso/pkg/backup/details"
|
||||
@ -25,7 +26,8 @@ func (suite *ContactSuite) TestContactInfo() {
|
||||
{
|
||||
name: "Empty Contact",
|
||||
contactAndRP: func() (models.Contactable, *details.ExchangeInfo) {
|
||||
return models.NewContact(), &details.ExchangeInfo{}
|
||||
i := &details.ExchangeInfo{ItemType: details.ExchangeContact}
|
||||
return models.NewContact(), i
|
||||
},
|
||||
}, {
|
||||
name: "Only Name",
|
||||
@ -33,14 +35,18 @@ func (suite *ContactSuite) TestContactInfo() {
|
||||
aPerson := "Whole Person"
|
||||
contact := models.NewContact()
|
||||
contact.SetDisplayName(&aPerson)
|
||||
return contact, &details.ExchangeInfo{ContactName: aPerson}
|
||||
i := &details.ExchangeInfo{
|
||||
ItemType: details.ExchangeContact,
|
||||
ContactName: aPerson,
|
||||
}
|
||||
return contact, i
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
suite.T().Run(test.name, func(t *testing.T) {
|
||||
contact, expected := test.contactAndRP()
|
||||
suite.Equal(expected, ContactInfo(contact))
|
||||
assert.Equal(t, expected, ContactInfo(contact))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,6 +46,7 @@ func EventInfo(evt models.Eventable) *details.ExchangeInfo {
|
||||
}
|
||||
|
||||
return &details.ExchangeInfo{
|
||||
ItemType: details.ExchangeEvent,
|
||||
Organizer: organizer,
|
||||
Subject: subject,
|
||||
EventStart: start,
|
||||
|
||||
@ -37,7 +37,8 @@ func (suite *EventSuite) TestEventInfo() {
|
||||
{
|
||||
name: "Empty event",
|
||||
evtAndRP: func() (models.Eventable, *details.ExchangeInfo) {
|
||||
return models.NewEvent(), &details.ExchangeInfo{}
|
||||
i := &details.ExchangeInfo{ItemType: details.ExchangeEvent}
|
||||
return models.NewEvent(), i
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -49,7 +50,11 @@ func (suite *EventSuite) TestEventInfo() {
|
||||
event.SetStart(dateTime)
|
||||
full, err := time.Parse(common.StandardTimeFormat, now)
|
||||
require.NoError(suite.T(), err)
|
||||
return event, &details.ExchangeInfo{Received: full}
|
||||
i := &details.ExchangeInfo{
|
||||
ItemType: details.ExchangeEvent,
|
||||
Received: full,
|
||||
}
|
||||
return event, i
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -58,7 +63,11 @@ func (suite *EventSuite) TestEventInfo() {
|
||||
subject := "Hello Corso"
|
||||
event := models.NewEvent()
|
||||
event.SetSubject(&subject)
|
||||
return event, &details.ExchangeInfo{Subject: subject}
|
||||
i := &details.ExchangeInfo{
|
||||
ItemType: details.ExchangeEvent,
|
||||
Subject: subject,
|
||||
}
|
||||
return event, i
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -70,11 +79,13 @@ func (suite *EventSuite) TestEventInfo() {
|
||||
subject := " Test MockReview + Lunch"
|
||||
organizer := "foobar3@8qzvrj.onmicrosoft.com"
|
||||
eventTime := time.Date(2022, time.April, 28, 3, 41, 58, 0, time.UTC)
|
||||
return event, &details.ExchangeInfo{
|
||||
i := &details.ExchangeInfo{
|
||||
ItemType: details.ExchangeEvent,
|
||||
Subject: subject,
|
||||
Organizer: organizer,
|
||||
EventStart: eventTime,
|
||||
}
|
||||
return event, i
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@ func MessageInfo(msg models.Messageable) *details.ExchangeInfo {
|
||||
}
|
||||
|
||||
return &details.ExchangeInfo{
|
||||
ItemType: details.ExchangeMail,
|
||||
Sender: sender,
|
||||
Subject: subject,
|
||||
Received: received,
|
||||
|
||||
@ -26,7 +26,8 @@ func (suite *MessageSuite) TestMessageInfo() {
|
||||
{
|
||||
name: "Empty message",
|
||||
msgAndRP: func() (models.Messageable, *details.ExchangeInfo) {
|
||||
return models.NewMessage(), &details.ExchangeInfo{}
|
||||
i := &details.ExchangeInfo{ItemType: details.ExchangeMail}
|
||||
return models.NewMessage(), i
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -39,7 +40,11 @@ func (suite *MessageSuite) TestMessageInfo() {
|
||||
sea.SetAddress(&sender)
|
||||
sr.SetEmailAddress(sea)
|
||||
msg.SetSender(sr)
|
||||
return msg, &details.ExchangeInfo{Sender: sender}
|
||||
i := &details.ExchangeInfo{
|
||||
ItemType: details.ExchangeMail,
|
||||
Sender: sender,
|
||||
}
|
||||
return msg, i
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -48,7 +53,11 @@ func (suite *MessageSuite) TestMessageInfo() {
|
||||
subject := "Hello world"
|
||||
msg := models.NewMessage()
|
||||
msg.SetSubject(&subject)
|
||||
return msg, &details.ExchangeInfo{Subject: subject}
|
||||
i := &details.ExchangeInfo{
|
||||
ItemType: details.ExchangeMail,
|
||||
Subject: subject,
|
||||
}
|
||||
return msg, i
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -57,7 +66,11 @@ func (suite *MessageSuite) TestMessageInfo() {
|
||||
now := time.Now()
|
||||
msg := models.NewMessage()
|
||||
msg.SetReceivedDateTime(&now)
|
||||
return msg, &details.ExchangeInfo{Received: now}
|
||||
i := &details.ExchangeInfo{
|
||||
ItemType: details.ExchangeMail,
|
||||
Received: now,
|
||||
}
|
||||
return msg, i
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -74,7 +87,13 @@ func (suite *MessageSuite) TestMessageInfo() {
|
||||
msg.SetSender(sr)
|
||||
msg.SetSubject(&subject)
|
||||
msg.SetReceivedDateTime(&now)
|
||||
return msg, &details.ExchangeInfo{Sender: sender, Subject: subject, Received: now}
|
||||
i := &details.ExchangeInfo{
|
||||
ItemType: details.ExchangeMail,
|
||||
Sender: sender,
|
||||
Subject: subject,
|
||||
Received: now,
|
||||
}
|
||||
return msg, i
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -119,7 +119,11 @@ func (oc *Collection) populateItems(ctx context.Context) {
|
||||
oc.data <- &Item{
|
||||
id: itemID,
|
||||
data: itemData,
|
||||
info: &details.OneDriveInfo{ItemName: itemName, ParentPath: oc.folderPath},
|
||||
info: &details.OneDriveInfo{
|
||||
ItemType: details.OneDriveItem,
|
||||
ItemName: itemName,
|
||||
ParentPath: oc.folderPath,
|
||||
},
|
||||
}
|
||||
}
|
||||
close(oc.data)
|
||||
|
||||
@ -2,10 +2,12 @@ package details
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/alcionai/corso/cli/print"
|
||||
"github.com/alcionai/corso/internal/common"
|
||||
"github.com/alcionai/corso/internal/model"
|
||||
)
|
||||
|
||||
@ -22,13 +24,23 @@ type DetailsModel struct {
|
||||
// Print writes the DetailModel Entries to StdOut, in the format
|
||||
// requested by the caller.
|
||||
func (dm DetailsModel) PrintEntries(ctx context.Context) {
|
||||
ps := []print.Printable{}
|
||||
perType := map[itemType][]print.Printable{}
|
||||
|
||||
for _, de := range dm.Entries {
|
||||
ps = append(ps, de)
|
||||
it := de.infoType()
|
||||
ps, ok := perType[it]
|
||||
|
||||
if !ok {
|
||||
ps = []print.Printable{}
|
||||
}
|
||||
|
||||
perType[it] = append(ps, print.Printable(de))
|
||||
}
|
||||
|
||||
for _, ps := range perType {
|
||||
print.All(ctx, ps...)
|
||||
}
|
||||
}
|
||||
|
||||
// Paths returns the list of Paths extracted from the Entries slice.
|
||||
func (dm DetailsModel) Paths() []string {
|
||||
@ -126,6 +138,21 @@ func (de DetailsEntry) Values() []string {
|
||||
return vs
|
||||
}
|
||||
|
||||
type itemType int
|
||||
|
||||
const (
|
||||
UnknownType itemType = iota
|
||||
|
||||
// separate each service by a factor of 100 for padding
|
||||
ExchangeContact
|
||||
ExchangeEvent
|
||||
ExchangeMail
|
||||
|
||||
SharepointItem itemType = iota + 100
|
||||
|
||||
OneDriveItem itemType = iota + 200
|
||||
)
|
||||
|
||||
// ItemInfo is a oneOf that contains service specific
|
||||
// information about the item it tracks
|
||||
type ItemInfo struct {
|
||||
@ -134,8 +161,30 @@ type ItemInfo struct {
|
||||
OneDrive *OneDriveInfo `json:"oneDrive,omitempty"`
|
||||
}
|
||||
|
||||
// typedInfo should get embedded in each sesrvice type to track
|
||||
// the type of item it stores for multi-item service support.
|
||||
|
||||
// infoType provides internal categorization for collecting like-typed ItemInfos.
|
||||
// It should return the most granular value type (ex: "event" for an exchange
|
||||
// calendar event).
|
||||
func (i ItemInfo) infoType() itemType {
|
||||
switch {
|
||||
case i.Exchange != nil:
|
||||
return i.Exchange.ItemType
|
||||
|
||||
case i.Sharepoint != nil:
|
||||
return i.Sharepoint.ItemType
|
||||
|
||||
case i.OneDrive != nil:
|
||||
return i.OneDrive.ItemType
|
||||
}
|
||||
|
||||
return UnknownType
|
||||
}
|
||||
|
||||
// ExchangeInfo describes an exchange item
|
||||
type ExchangeInfo struct {
|
||||
ItemType itemType
|
||||
Sender string `json:"sender,omitempty"`
|
||||
Subject string `json:"subject,omitempty"`
|
||||
Received time.Time `json:"received,omitempty"`
|
||||
@ -147,47 +196,72 @@ type ExchangeInfo struct {
|
||||
|
||||
// Headers returns the human-readable names of properties in an ExchangeInfo
|
||||
// for printing out to a terminal in a columnar display.
|
||||
func (e ExchangeInfo) Headers() []string {
|
||||
func (i ExchangeInfo) Headers() []string {
|
||||
switch i.ItemType {
|
||||
case ExchangeEvent:
|
||||
return []string{"Organizer", "Subject", "Starts", "Recurring"}
|
||||
|
||||
case ExchangeContact:
|
||||
return []string{"Contact Name"}
|
||||
|
||||
case ExchangeMail:
|
||||
return []string{"Sender", "Subject", "Received"}
|
||||
}
|
||||
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// Values returns the values matching the Headers list for printing
|
||||
// out to a terminal in a columnar display.
|
||||
func (e ExchangeInfo) Values() []string {
|
||||
return []string{e.Sender, e.Subject, e.Received.Format(time.RFC3339Nano)}
|
||||
func (i ExchangeInfo) Values() []string {
|
||||
switch i.ItemType {
|
||||
case ExchangeEvent:
|
||||
return []string{i.Organizer, i.Subject, common.FormatTime(i.EventStart), strconv.FormatBool(i.EventRecurs)}
|
||||
|
||||
case ExchangeContact:
|
||||
return []string{i.ContactName}
|
||||
|
||||
case ExchangeMail:
|
||||
return []string{i.Sender, i.Subject, common.FormatTime(i.Received)}
|
||||
}
|
||||
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// SharepointInfo describes a sharepoint item
|
||||
// TODO: Implement this. This is currently here
|
||||
// just to illustrate usage
|
||||
type SharepointInfo struct{}
|
||||
type SharepointInfo struct {
|
||||
ItemType itemType
|
||||
}
|
||||
|
||||
// Headers returns the human-readable names of properties in a SharepointInfo
|
||||
// for printing out to a terminal in a columnar display.
|
||||
func (s SharepointInfo) Headers() []string {
|
||||
func (i SharepointInfo) Headers() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// Values returns the values matching the Headers list for printing
|
||||
// out to a terminal in a columnar display.
|
||||
func (s SharepointInfo) Values() []string {
|
||||
func (i SharepointInfo) Values() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// OneDriveInfo describes a oneDrive item
|
||||
type OneDriveInfo struct {
|
||||
ItemType itemType
|
||||
ParentPath string `json:"parentPath"`
|
||||
ItemName string `json:"itemName"`
|
||||
}
|
||||
|
||||
// Headers returns the human-readable names of properties in a OneDriveInfo
|
||||
// for printing out to a terminal in a columnar display.
|
||||
func (oi OneDriveInfo) Headers() []string {
|
||||
func (i OneDriveInfo) Headers() []string {
|
||||
return []string{"ItemName", "ParentPath"}
|
||||
}
|
||||
|
||||
// Values returns the values matching the Headers list for printing
|
||||
// out to a terminal in a columnar display.
|
||||
func (oi OneDriveInfo) Values() []string {
|
||||
return []string{oi.ItemName, oi.ParentPath}
|
||||
func (i OneDriveInfo) Values() []string {
|
||||
return []string{i.ItemName, i.ParentPath}
|
||||
}
|
||||
|
||||
@ -2,11 +2,12 @@ package details_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/alcionai/corso/internal/common"
|
||||
"github.com/alcionai/corso/pkg/backup/details"
|
||||
)
|
||||
|
||||
@ -23,8 +24,9 @@ func TestDetailsUnitSuite(t *testing.T) {
|
||||
}
|
||||
|
||||
func (suite *DetailsUnitSuite) TestDetailsEntry_HeadersValues() {
|
||||
now := time.Now()
|
||||
nowStr := now.Format(time.RFC3339Nano)
|
||||
nowStr := common.FormatNow(common.StandardTimeFormat)
|
||||
now, err := common.ParseTime(nowStr)
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
table := []struct {
|
||||
name string
|
||||
@ -41,11 +43,43 @@ func (suite *DetailsUnitSuite) TestDetailsEntry_HeadersValues() {
|
||||
expectVs: []string{"reporef"},
|
||||
},
|
||||
{
|
||||
name: "exhange info",
|
||||
name: "exchange event info",
|
||||
entry: details.DetailsEntry{
|
||||
RepoRef: "reporef",
|
||||
ItemInfo: details.ItemInfo{
|
||||
Exchange: &details.ExchangeInfo{
|
||||
ItemType: details.ExchangeEvent,
|
||||
EventStart: now,
|
||||
Organizer: "organizer",
|
||||
EventRecurs: true,
|
||||
Subject: "subject",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectHs: []string{"Repo Ref", "Organizer", "Subject", "Starts", "Recurring"},
|
||||
expectVs: []string{"reporef", "organizer", "subject", nowStr, "true"},
|
||||
},
|
||||
{
|
||||
name: "exchange contact info",
|
||||
entry: details.DetailsEntry{
|
||||
RepoRef: "reporef",
|
||||
ItemInfo: details.ItemInfo{
|
||||
Exchange: &details.ExchangeInfo{
|
||||
ItemType: details.ExchangeContact,
|
||||
ContactName: "contactName",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectHs: []string{"Repo Ref", "Contact Name"},
|
||||
expectVs: []string{"reporef", "contactName"},
|
||||
},
|
||||
{
|
||||
name: "exchange mail info",
|
||||
entry: details.DetailsEntry{
|
||||
RepoRef: "reporef",
|
||||
ItemInfo: details.ItemInfo{
|
||||
Exchange: &details.ExchangeInfo{
|
||||
ItemType: details.ExchangeMail,
|
||||
Sender: "sender",
|
||||
Subject: "subject",
|
||||
Received: now,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user