From 07997b0987f5a00f1a9320f7f2f5fb57f37270d1 Mon Sep 17 00:00:00 2001 From: ryanfkeepers Date: Thu, 18 Jan 2024 17:58:08 -0700 Subject: [PATCH] add chats boilerplate to details --- src/pkg/backup/details/chats.go | 120 ++++++++++++++++++++++++ src/pkg/backup/details/chats_test.go | 71 ++++++++++++++ src/pkg/backup/details/groups.go | 2 +- src/pkg/backup/details/iteminfo.go | 19 ++++ src/pkg/backup/details/iteminfo_test.go | 11 ++- 5 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 src/pkg/backup/details/chats.go create mode 100644 src/pkg/backup/details/chats_test.go diff --git a/src/pkg/backup/details/chats.go b/src/pkg/backup/details/chats.go new file mode 100644 index 000000000..04e2d61a7 --- /dev/null +++ b/src/pkg/backup/details/chats.go @@ -0,0 +1,120 @@ +package details + +import ( + "fmt" + "strconv" + "time" + + "github.com/alcionai/clues" + + "github.com/alcionai/corso/src/pkg/dttm" + "github.com/alcionai/corso/src/pkg/path" +) + +// NewChatsLocationIDer builds a LocationIDer for the chats. +func NewChatsLocationIDer( + category path.CategoryType, + escapedFolders ...string, +) (uniqueLoc, error) { + if err := path.ValidateServiceAndCategory(path.TeamsChatsService, category); err != nil { + return uniqueLoc{}, clues.Wrap(err, "making chats LocationIDer") + } + + pb := path.Builder{}.Append(category.String()).Append(escapedFolders...) + + return uniqueLoc{ + pb: pb, + prefixElems: 1, + }, nil +} + +// TeamsChatsInfo describes a chat within teams chats. +type TeamsChatsInfo struct { + ItemType ItemType `json:"itemType,omitempty"` + Modified time.Time `json:"modified,omitempty"` + ParentPath string `json:"parentPath,omitempty"` + + Chat ChatInfo `json:"chat,omitempty"` +} + +type ChatInfo struct { + CreatedAt time.Time `json:"createdAt,omitempty"` + HasExternalMembers bool `json:"hasExternalMemebers,omitempty"` + LastMessageAt time.Time `json:"lastMessageAt,omitempty"` + LastMessagePreview string `json:"preview,omitempty"` + Members []string `json:"members,omitempty"` + MessageCount int `json:"size,omitempty"` + Name string `json:"name,omitempty"` +} + +// Headers returns the human-readable names of properties in a ChatsInfo +// for printing out to a terminal in a columnar display. +func (i TeamsChatsInfo) Headers() []string { + switch i.ItemType { + case TeamsChat: + return []string{"Name", "Last message", "Last message at", "Message count", "Created", "Members"} + } + + return []string{} +} + +// Values returns the values matching the Headers list for printing +// out to a terminal in a columnar display. +func (i TeamsChatsInfo) Values() []string { + switch i.ItemType { + case TeamsChat: + members := "" + icmLen := len(i.Chat.Members) + + if icmLen > 0 { + members = i.Chat.Members[0] + } + + if icmLen > 1 { + members = fmt.Sprintf("%s, and %d more", members, icmLen-1) + } + + return []string{ + i.Chat.Name, + i.Chat.LastMessagePreview, + dttm.FormatToTabularDisplay(i.Chat.LastMessageAt), + strconv.Itoa(i.Chat.MessageCount), + dttm.FormatToTabularDisplay(i.Chat.CreatedAt), + members, + } + } + + return []string{} +} + +func (i *TeamsChatsInfo) UpdateParentPath(newLocPath *path.Builder) { + i.ParentPath = newLocPath.String() +} + +func (i *TeamsChatsInfo) uniqueLocation(baseLoc *path.Builder) (*uniqueLoc, error) { + var category path.CategoryType + + switch i.ItemType { + case TeamsChat: + category = path.ChatsCategory + } + + loc, err := NewChatsLocationIDer(category, baseLoc.Elements()...) + + return &loc, err +} + +func (i *TeamsChatsInfo) updateFolder(f *FolderInfo) error { + // Use a switch instead of a rather large if-statement. Just make sure it's an + // Exchange type. If it's not return an error. + switch i.ItemType { + case TeamsChat: + default: + return clues.New("unsupported non-Chats ItemType"). + With("item_type", i.ItemType) + } + + f.DataType = i.ItemType + + return nil +} diff --git a/src/pkg/backup/details/chats_test.go b/src/pkg/backup/details/chats_test.go new file mode 100644 index 000000000..476a32341 --- /dev/null +++ b/src/pkg/backup/details/chats_test.go @@ -0,0 +1,71 @@ +package details_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + + "github.com/alcionai/corso/src/internal/tester" + "github.com/alcionai/corso/src/pkg/backup/details" + "github.com/alcionai/corso/src/pkg/dttm" +) + +type ChatsUnitSuite struct { + tester.Suite +} + +func TestChatsUnitSuite(t *testing.T) { + suite.Run(t, &ChatsUnitSuite{Suite: tester.NewUnitSuite(t)}) +} + +func (suite *ChatsUnitSuite) TestChatsPrintable() { + now := time.Now() + then := now.Add(time.Minute) + + table := []struct { + name string + info details.TeamsChatsInfo + expectHs []string + expectVs []string + }{ + { + name: "channel message", + info: details.TeamsChatsInfo{ + ItemType: details.TeamsChat, + ParentPath: "parentpath", + Chat: details.ChatInfo{ + CreatedAt: now, + HasExternalMembers: true, + LastMessageAt: then, + LastMessagePreview: "last message preview", + Members: []string{"foo@bar.baz", "fnords@smarf.zoomba"}, + MessageCount: 42, + Name: "chat name", + }, + }, + expectHs: []string{"Name", "Last message", "Last message at", "Message count", "Created", "Members"}, + expectVs: []string{ + "chat name", + "last message preview", + dttm.FormatToTabularDisplay(then), + "42", + dttm.FormatToTabularDisplay(now), + "foo@bar.baz, and 1 more", + }, + }, + } + for _, test := range table { + suite.Run(test.name, func() { + t := suite.T() + + hs := test.info.Headers() + vs := test.info.Values() + + assert.Equal(t, len(hs), len(vs)) + assert.Equal(t, test.expectHs, hs) + assert.Equal(t, test.expectVs, vs) + }) + } +} diff --git a/src/pkg/backup/details/groups.go b/src/pkg/backup/details/groups.go index 99829f05a..a280dd3b7 100644 --- a/src/pkg/backup/details/groups.go +++ b/src/pkg/backup/details/groups.go @@ -78,7 +78,7 @@ type ChannelMessageInfo struct { Subject string `json:"subject,omitempty"` } -// Headers returns the human-readable names of properties in a SharePointInfo +// Headers returns the human-readable names of properties in a gropusInfo // for printing out to a terminal in a columnar display. func (i GroupsInfo) Headers() []string { switch i.ItemType { diff --git a/src/pkg/backup/details/iteminfo.go b/src/pkg/backup/details/iteminfo.go index e2eaf357e..22391d2d8 100644 --- a/src/pkg/backup/details/iteminfo.go +++ b/src/pkg/backup/details/iteminfo.go @@ -41,6 +41,9 @@ const ( // Groups/Teams(40x) GroupsChannelMessage ItemType = 401 GroupsConversationPost ItemType = 402 + + // Teams Chat + TeamsChat ItemType = 501 ) func UpdateItem(item *ItemInfo, newLocPath *path.Builder) { @@ -73,6 +76,7 @@ type ItemInfo struct { SharePoint *SharePointInfo `json:"sharePoint,omitempty"` OneDrive *OneDriveInfo `json:"oneDrive,omitempty"` Groups *GroupsInfo `json:"groups,omitempty"` + TeamsChats *TeamsChatsInfo `json:"teamsChats,omitempty"` // Optional item extension data Extension *ExtensionData `json:"extension,omitempty"` } @@ -99,6 +103,9 @@ func (i ItemInfo) infoType() ItemType { case i.Groups != nil: return i.Groups.ItemType + + case i.TeamsChats != nil: + return i.TeamsChats.ItemType } return UnknownType @@ -120,6 +127,9 @@ func (i ItemInfo) size() int64 { case i.Folder != nil: return i.Folder.Size + + case i.TeamsChats != nil: + return int64(i.TeamsChats.Chat.MessageCount) } return 0 @@ -141,6 +151,9 @@ func (i ItemInfo) Modified() time.Time { case i.Folder != nil: return i.Folder.Modified + + case i.TeamsChats != nil: + return i.TeamsChats.Modified } return time.Time{} @@ -160,6 +173,9 @@ func (i ItemInfo) uniqueLocation(baseLoc *path.Builder) (*uniqueLoc, error) { case i.Groups != nil: return i.Groups.uniqueLocation(baseLoc) + case i.TeamsChats != nil: + return i.TeamsChats.uniqueLocation(baseLoc) + default: return nil, clues.New("unsupported type") } @@ -179,6 +195,9 @@ func (i ItemInfo) updateFolder(f *FolderInfo) error { case i.Groups != nil: return i.Groups.updateFolder(f) + case i.TeamsChats != nil: + return i.TeamsChats.updateFolder(f) + default: return clues.New("unsupported type") } diff --git a/src/pkg/backup/details/iteminfo_test.go b/src/pkg/backup/details/iteminfo_test.go index 1073edb27..87a28873c 100644 --- a/src/pkg/backup/details/iteminfo_test.go +++ b/src/pkg/backup/details/iteminfo_test.go @@ -71,12 +71,21 @@ func (suite *ItemInfoUnitSuite) TestItemInfo_IsDriveItem() { { name: "exchange anything", ii: ItemInfo{ - Groups: &GroupsInfo{ + Exchange: &ExchangeInfo{ ItemType: ExchangeMail, }, }, expect: assert.False, }, + { + name: "teams chat", + ii: ItemInfo{ + TeamsChats: &TeamsChatsInfo{ + ItemType: TeamsChat, + }, + }, + expect: assert.False, + }, } for _, test := range table { suite.Run(test.name, func() {