get details working (and other cleanup) (#5116)
details wasn't properly listing backed up items. This fixes the details display, and contains some code clean-up that occurred along the way.
This commit is contained in:
parent
316e5f0195
commit
0efe25bb71
@ -213,7 +213,6 @@ func runDetailsTeamsChatsCmd(cmd *cobra.Command) error {
|
||||
opts := utils.MakeTeamsChatsOpts(cmd)
|
||||
|
||||
sel := utils.IncludeTeamsChatsRestoreDataSelectors(ctx, opts)
|
||||
sel.Configure(selectors.Config{OnlyMatchItemNames: true})
|
||||
utils.FilterTeamsChatsRestoreInfoSelectors(sel, opts)
|
||||
|
||||
ds, err := genericDetailsCommand(cmd, flags.BackupIDFV, sel.Selector)
|
||||
|
||||
@ -89,7 +89,10 @@ func IncludeTeamsChatsRestoreDataSelectors(ctx context.Context, opts TeamsChatsO
|
||||
users = selectors.Any()
|
||||
}
|
||||
|
||||
return selectors.NewTeamsChatsRestore(users)
|
||||
sel := selectors.NewTeamsChatsRestore(users)
|
||||
sel.Include(sel.Chats(selectors.Any()))
|
||||
|
||||
return sel
|
||||
}
|
||||
|
||||
// FilterTeamsChatsRestoreInfoSelectors builds the common info-selector filters.
|
||||
|
||||
@ -115,11 +115,8 @@ func populateCollection[I chatsItemer](
|
||||
)
|
||||
|
||||
ctx = clues.AddLabelCounter(ctx, cl.PlainAdder())
|
||||
cc := api.CallConfig{
|
||||
CanMakeDeltaQueries: false,
|
||||
}
|
||||
|
||||
items, err := bh.getItemIDs(ctx, cc)
|
||||
items, err := bh.getItemIDs(ctx)
|
||||
if err != nil {
|
||||
errs.AddRecoverable(ctx, clues.Stack(err))
|
||||
return collection, clues.Stack(errs.Failure()).OrNil()
|
||||
|
||||
@ -49,14 +49,6 @@ type mockBackupHandler struct {
|
||||
doNotInclude bool
|
||||
}
|
||||
|
||||
//lint:ignore U1000 false linter issue due to generics
|
||||
func (bh mockBackupHandler) augmentItemInfo(
|
||||
*details.TeamsChatsInfo,
|
||||
models.Chatable,
|
||||
) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
func (bh mockBackupHandler) container() container[models.Chatable] {
|
||||
return chatContainer()
|
||||
}
|
||||
@ -71,7 +63,6 @@ func (bh mockBackupHandler) getContainer(
|
||||
|
||||
func (bh mockBackupHandler) getItemIDs(
|
||||
_ context.Context,
|
||||
_ api.CallConfig,
|
||||
) ([]models.Chatable, error) {
|
||||
return bh.chats, bh.chatsErr
|
||||
}
|
||||
|
||||
@ -46,8 +46,11 @@ func (bh usersChatsBackupHandler) getContainer(
|
||||
//lint:ignore U1000 required for interface compliance
|
||||
func (bh usersChatsBackupHandler) getItemIDs(
|
||||
ctx context.Context,
|
||||
cc api.CallConfig,
|
||||
) ([]models.Chatable, error) {
|
||||
cc := api.CallConfig{
|
||||
Expand: []string{"lastMessagePreview"},
|
||||
}
|
||||
|
||||
return bh.ac.GetChats(
|
||||
ctx,
|
||||
bh.protectedResourceID,
|
||||
@ -89,26 +92,21 @@ func (bh usersChatsBackupHandler) getItem(
|
||||
|
||||
chatID := ptr.Val(chat.GetId())
|
||||
|
||||
cc := api.CallConfig{
|
||||
Expand: []string{"lastMessagePreview"},
|
||||
}
|
||||
|
||||
msgs, err := bh.ac.GetChatMessages(ctx, chatID, cc)
|
||||
msgs, err := bh.ac.GetChatMessages(ctx, chatID, api.CallConfig{})
|
||||
if err != nil {
|
||||
return nil, nil, clues.Stack(err)
|
||||
}
|
||||
|
||||
chat.SetMessages(msgs)
|
||||
|
||||
return chat, api.TeamsChatInfo(chat), nil
|
||||
}
|
||||
members, err := bh.ac.GetChatMembers(ctx, chatID, api.CallConfig{})
|
||||
if err != nil {
|
||||
return nil, nil, clues.Stack(err)
|
||||
}
|
||||
|
||||
//lint:ignore U1000 false linter issue due to generics
|
||||
func (bh usersChatsBackupHandler) augmentItemInfo(
|
||||
dgi *details.TeamsChatsInfo,
|
||||
c models.Chatable,
|
||||
) {
|
||||
// no-op
|
||||
chat.SetMembers(members)
|
||||
|
||||
return chat, api.TeamsChatInfo(chat), nil
|
||||
}
|
||||
|
||||
func chatContainer() container[models.Chatable] {
|
||||
|
||||
@ -66,7 +66,7 @@ func updateStatus(
|
||||
// or notMoved (if they match).
|
||||
func NewCollection[I chatsItemer](
|
||||
baseCol data.BaseCollection,
|
||||
getAndAugment getItemAndAugmentInfoer[I],
|
||||
getter getItemer[I],
|
||||
protectedResource string,
|
||||
items []I,
|
||||
contains container[I],
|
||||
@ -76,7 +76,7 @@ func NewCollection[I chatsItemer](
|
||||
BaseCollection: baseCol,
|
||||
items: items,
|
||||
contains: contains,
|
||||
getAndAugment: getAndAugment,
|
||||
getter: getter,
|
||||
statusUpdater: statusUpdater,
|
||||
stream: make(chan data.Item, collectionChannelBufferSize),
|
||||
protectedResource: protectedResource,
|
||||
@ -96,7 +96,7 @@ type lazyFetchCollection[I chatsItemer] struct {
|
||||
|
||||
items []I
|
||||
|
||||
getAndAugment getItemAndAugmentInfoer[I]
|
||||
getter getItemer[I]
|
||||
|
||||
statusUpdater support.StatusUpdater
|
||||
}
|
||||
@ -168,7 +168,7 @@ func (col *lazyFetchCollection[I]) streamItems(ctx context.Context, errs *fault.
|
||||
ictx,
|
||||
&lazyItemGetter[I]{
|
||||
modTime: modTime,
|
||||
getAndAugment: col.getAndAugment,
|
||||
getter: col.getter,
|
||||
resourceID: col.protectedResource,
|
||||
item: item,
|
||||
containerIDs: col.FullPath().Folders(),
|
||||
@ -192,7 +192,7 @@ func (col *lazyFetchCollection[I]) streamItems(ctx context.Context, errs *fault.
|
||||
}
|
||||
|
||||
type lazyItemGetter[I chatsItemer] struct {
|
||||
getAndAugment getItemAndAugmentInfoer[I]
|
||||
getter getItemer[I]
|
||||
resourceID string
|
||||
item I
|
||||
parentPath string
|
||||
@ -208,7 +208,7 @@ func (lig *lazyItemGetter[I]) GetData(
|
||||
writer := kjson.NewJsonSerializationWriter()
|
||||
defer writer.Close()
|
||||
|
||||
item, info, err := lig.getAndAugment.getItem(
|
||||
item, info, err := lig.getter.getItem(
|
||||
ctx,
|
||||
lig.resourceID,
|
||||
lig.item)
|
||||
@ -229,8 +229,6 @@ func (lig *lazyItemGetter[I]) GetData(
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
lig.getAndAugment.augmentItemInfo(info, lig.contains.container)
|
||||
|
||||
if err := writer.WriteObjectValue("", item); err != nil {
|
||||
err = clues.WrapWC(ctx, err, "writing item to serializer").Label(fault.LabelForceNoBackupCreation)
|
||||
errs.AddRecoverable(ctx, err)
|
||||
|
||||
@ -163,11 +163,6 @@ func (m getAndAugmentChat) getItem(
|
||||
return chat, &details.TeamsChatsInfo{}, m.err
|
||||
}
|
||||
|
||||
//lint:ignore U1000 false linter issue due to generics
|
||||
func (getAndAugmentChat) augmentItemInfo(*details.TeamsChatsInfo, models.Chatable) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
func (suite *CollectionUnitSuite) TestLazyFetchCollection_Items_LazyFetch() {
|
||||
var (
|
||||
t = suite.T()
|
||||
@ -225,7 +220,7 @@ func (suite *CollectionUnitSuite) TestLazyFetchCollection_Items_LazyFetch() {
|
||||
count.New()),
|
||||
items: test.items,
|
||||
contains: container[models.Chatable]{},
|
||||
getAndAugment: getterAugmenter,
|
||||
getter: getterAugmenter,
|
||||
stream: make(chan data.Item),
|
||||
statusUpdater: statusUpdater,
|
||||
}
|
||||
@ -287,7 +282,7 @@ func (suite *CollectionUnitSuite) TestLazyItem_GetDataErrors() {
|
||||
&lazyItemGetter[models.Chatable]{
|
||||
resourceID: "resourceID",
|
||||
item: chat,
|
||||
getAndAugment: &m,
|
||||
getter: &m,
|
||||
modTime: now,
|
||||
parentPath: parentPath,
|
||||
},
|
||||
@ -331,7 +326,7 @@ func (suite *CollectionUnitSuite) TestLazyItem_ReturnsEmptyReaderOnDeletedInFlig
|
||||
&lazyItemGetter[models.Chatable]{
|
||||
resourceID: "resourceID",
|
||||
item: chat,
|
||||
getAndAugment: &m,
|
||||
getter: &m,
|
||||
modTime: now,
|
||||
parentPath: parentPath,
|
||||
},
|
||||
@ -370,7 +365,7 @@ func (suite *CollectionUnitSuite) TestLazyItem() {
|
||||
&lazyItemGetter[models.Chatable]{
|
||||
resourceID: "resourceID",
|
||||
item: chat,
|
||||
getAndAugment: &m,
|
||||
getter: &m,
|
||||
modTime: now,
|
||||
parentPath: parentPath,
|
||||
},
|
||||
|
||||
@ -22,7 +22,7 @@ type chatsItemer interface {
|
||||
|
||||
type backupHandler[I chatsItemer] interface {
|
||||
getContainerer[I]
|
||||
getItemAndAugmentInfoer[I]
|
||||
getItemer[I]
|
||||
getItemer[I]
|
||||
getItemIDser[I]
|
||||
includeItemer[I]
|
||||
@ -39,22 +39,10 @@ type getContainerer[I chatsItemer] interface {
|
||||
) (container[I], error)
|
||||
}
|
||||
|
||||
type getItemAndAugmentInfoer[I chatsItemer] interface {
|
||||
getItemer[I]
|
||||
augmentItemInfoer[I]
|
||||
}
|
||||
|
||||
type augmentItemInfoer[I chatsItemer] interface {
|
||||
// augmentItemInfo completes the teamChatsInfo population with any data
|
||||
// owned by the container and not accessible to the item.
|
||||
augmentItemInfo(*details.TeamsChatsInfo, I)
|
||||
}
|
||||
|
||||
// gets all item IDs in the container
|
||||
type getItemIDser[I chatsItemer] interface {
|
||||
getItemIDs(
|
||||
ctx context.Context,
|
||||
cc api.CallConfig,
|
||||
) ([]I, error)
|
||||
}
|
||||
|
||||
|
||||
@ -103,6 +103,12 @@ func (de Entry) ToLocationIDer(backupVersion int) (LocationIDer, error) {
|
||||
}
|
||||
|
||||
baseLoc = path.Builder{}.Append(p.Root).Append(p.Folders...)
|
||||
|
||||
case TeamsChat:
|
||||
baseLoc = &path.Builder{}
|
||||
|
||||
default:
|
||||
return nil, clues.New("undentified item type").With("item_type", de.ItemInfo.infoType())
|
||||
}
|
||||
|
||||
if baseLoc == nil {
|
||||
@ -141,26 +147,23 @@ func (de Entry) MinimumPrintable() any {
|
||||
// Headers returns the human-readable names of properties in a DetailsEntry
|
||||
// for printing out to a terminal in a columnar display.
|
||||
func (de Entry) Headers(skipID bool) []string {
|
||||
hs := []string{}
|
||||
var hs []string
|
||||
|
||||
if de.ItemInfo.Folder != nil {
|
||||
switch {
|
||||
case de.ItemInfo.Folder != nil:
|
||||
hs = de.ItemInfo.Folder.Headers()
|
||||
}
|
||||
|
||||
if de.ItemInfo.Exchange != nil {
|
||||
case de.ItemInfo.Exchange != nil:
|
||||
hs = de.ItemInfo.Exchange.Headers()
|
||||
}
|
||||
|
||||
if de.ItemInfo.SharePoint != nil {
|
||||
case de.ItemInfo.SharePoint != nil:
|
||||
hs = de.ItemInfo.SharePoint.Headers()
|
||||
}
|
||||
|
||||
if de.ItemInfo.OneDrive != nil {
|
||||
case de.ItemInfo.OneDrive != nil:
|
||||
hs = de.ItemInfo.OneDrive.Headers()
|
||||
}
|
||||
|
||||
if de.ItemInfo.Groups != nil {
|
||||
case de.ItemInfo.Groups != nil:
|
||||
hs = de.ItemInfo.Groups.Headers()
|
||||
case de.ItemInfo.TeamsChats != nil:
|
||||
hs = de.ItemInfo.TeamsChats.Headers()
|
||||
default:
|
||||
hs = []string{"ERROR - Service not recognized"}
|
||||
}
|
||||
|
||||
if skipID {
|
||||
@ -172,26 +175,23 @@ func (de Entry) Headers(skipID bool) []string {
|
||||
|
||||
// Values returns the values matching the Headers list.
|
||||
func (de Entry) Values(skipID bool) []string {
|
||||
vs := []string{}
|
||||
var vs []string
|
||||
|
||||
if de.ItemInfo.Folder != nil {
|
||||
switch {
|
||||
case de.ItemInfo.Folder != nil:
|
||||
vs = de.ItemInfo.Folder.Values()
|
||||
}
|
||||
|
||||
if de.ItemInfo.Exchange != nil {
|
||||
case de.ItemInfo.Exchange != nil:
|
||||
vs = de.ItemInfo.Exchange.Values()
|
||||
}
|
||||
|
||||
if de.ItemInfo.SharePoint != nil {
|
||||
case de.ItemInfo.SharePoint != nil:
|
||||
vs = de.ItemInfo.SharePoint.Values()
|
||||
}
|
||||
|
||||
if de.ItemInfo.OneDrive != nil {
|
||||
case de.ItemInfo.OneDrive != nil:
|
||||
vs = de.ItemInfo.OneDrive.Values()
|
||||
}
|
||||
|
||||
if de.ItemInfo.Groups != nil {
|
||||
case de.ItemInfo.Groups != nil:
|
||||
vs = de.ItemInfo.Groups.Values()
|
||||
case de.ItemInfo.TeamsChats != nil:
|
||||
vs = de.ItemInfo.TeamsChats.Values()
|
||||
default:
|
||||
vs = []string{"ERROR - Service not recognized"}
|
||||
}
|
||||
|
||||
if skipID {
|
||||
|
||||
@ -91,19 +91,14 @@ func (i ItemInfo) infoType() ItemType {
|
||||
switch {
|
||||
case i.Folder != nil:
|
||||
return i.Folder.ItemType
|
||||
|
||||
case i.Exchange != nil:
|
||||
return i.Exchange.ItemType
|
||||
|
||||
case i.SharePoint != nil:
|
||||
return i.SharePoint.ItemType
|
||||
|
||||
case i.OneDrive != nil:
|
||||
return i.OneDrive.ItemType
|
||||
|
||||
case i.Groups != nil:
|
||||
return i.Groups.ItemType
|
||||
|
||||
case i.TeamsChats != nil:
|
||||
return i.TeamsChats.ItemType
|
||||
}
|
||||
@ -115,19 +110,14 @@ func (i ItemInfo) size() int64 {
|
||||
switch {
|
||||
case i.Exchange != nil:
|
||||
return i.Exchange.Size
|
||||
|
||||
case i.OneDrive != nil:
|
||||
return i.OneDrive.Size
|
||||
|
||||
case i.SharePoint != nil:
|
||||
return i.SharePoint.Size
|
||||
|
||||
case i.Groups != nil:
|
||||
return i.Groups.Size
|
||||
|
||||
case i.Folder != nil:
|
||||
return i.Folder.Size
|
||||
|
||||
case i.TeamsChats != nil:
|
||||
return int64(i.TeamsChats.Chat.MessageCount)
|
||||
}
|
||||
@ -139,19 +129,14 @@ func (i ItemInfo) Modified() time.Time {
|
||||
switch {
|
||||
case i.Exchange != nil:
|
||||
return i.Exchange.Modified
|
||||
|
||||
case i.OneDrive != nil:
|
||||
return i.OneDrive.Modified
|
||||
|
||||
case i.SharePoint != nil:
|
||||
return i.SharePoint.Modified
|
||||
|
||||
case i.Groups != nil:
|
||||
return i.Groups.Modified
|
||||
|
||||
case i.Folder != nil:
|
||||
return i.Folder.Modified
|
||||
|
||||
case i.TeamsChats != nil:
|
||||
return i.TeamsChats.Modified
|
||||
}
|
||||
@ -163,19 +148,14 @@ func (i ItemInfo) uniqueLocation(baseLoc *path.Builder) (*uniqueLoc, error) {
|
||||
switch {
|
||||
case i.Exchange != nil:
|
||||
return i.Exchange.uniqueLocation(baseLoc)
|
||||
|
||||
case i.OneDrive != nil:
|
||||
return i.OneDrive.uniqueLocation(baseLoc)
|
||||
|
||||
case i.SharePoint != nil:
|
||||
return i.SharePoint.uniqueLocation(baseLoc)
|
||||
|
||||
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")
|
||||
}
|
||||
@ -185,19 +165,14 @@ func (i ItemInfo) updateFolder(f *FolderInfo) error {
|
||||
switch {
|
||||
case i.Exchange != nil:
|
||||
return i.Exchange.updateFolder(f)
|
||||
|
||||
case i.OneDrive != nil:
|
||||
return i.OneDrive.updateFolder(f)
|
||||
|
||||
case i.SharePoint != nil:
|
||||
return i.SharePoint.updateFolder(f)
|
||||
|
||||
case i.Groups != nil:
|
||||
return i.Groups.updateFolder(f)
|
||||
|
||||
case i.TeamsChats != nil:
|
||||
return i.TeamsChats.updateFolder(f)
|
||||
|
||||
default:
|
||||
return clues.New("unsupported type")
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ type ChatInfo struct {
|
||||
LastMessagePreview string `json:"preview,omitempty"`
|
||||
Members []string `json:"members,omitempty"`
|
||||
MessageCount int `json:"messageCount,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Topic string `json:"topic,omitempty"`
|
||||
}
|
||||
|
||||
// Headers returns the human-readable names of properties in a ChatsInfo
|
||||
@ -52,7 +52,7 @@ type ChatInfo struct {
|
||||
func (i TeamsChatsInfo) Headers() []string {
|
||||
switch i.ItemType {
|
||||
case TeamsChat:
|
||||
return []string{"Name", "Last message", "Last message at", "Message count", "Created", "Members"}
|
||||
return []string{"Topic", "Last message", "Last message at", "Message count", "Created", "Members"}
|
||||
}
|
||||
|
||||
return []string{}
|
||||
@ -75,7 +75,7 @@ func (i TeamsChatsInfo) Values() []string {
|
||||
}
|
||||
|
||||
return []string{
|
||||
i.Chat.Name,
|
||||
i.Chat.Topic,
|
||||
i.Chat.LastMessagePreview,
|
||||
dttm.FormatToTabularDisplay(i.Chat.LastMessageAt),
|
||||
strconv.Itoa(i.Chat.MessageCount),
|
||||
|
||||
@ -42,7 +42,7 @@ func (suite *ChatsUnitSuite) TestChatsPrintable() {
|
||||
LastMessagePreview: "last message preview",
|
||||
Members: []string{"foo@bar.baz", "fnords@smarf.zoomba"},
|
||||
MessageCount: 42,
|
||||
Name: "chat name",
|
||||
Topic: "chat name",
|
||||
},
|
||||
},
|
||||
expectHs: []string{"Name", "Last message", "Last message at", "Message count", "Created", "Members"},
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/backup/identity"
|
||||
"github.com/alcionai/corso/src/pkg/dttm"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/filters"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
@ -254,21 +255,81 @@ func (sr *TeamsChatsRestore) ChatMember(memberID string) []TeamsChatsScope {
|
||||
}
|
||||
}
|
||||
|
||||
// ChatName produces one or more teamsChats chat name info scopes.
|
||||
// ChatTopic produces one or more teamsChats chat name info scopes.
|
||||
// Matches any chat whose name contains the provided string.
|
||||
// If any slice contains selectors.Any, that slice is reduced to [selectors.Any]
|
||||
// If any slice contains selectors.None, that slice is reduced to [selectors.None]
|
||||
// If any slice is empty, it defaults to [selectors.None]
|
||||
func (sr *TeamsChatsRestore) ChatName(memberID string) []TeamsChatsScope {
|
||||
func (sr *TeamsChatsRestore) ChatTopic(topic string) []TeamsChatsScope {
|
||||
return []TeamsChatsScope{
|
||||
makeInfoScope[TeamsChatsScope](
|
||||
TeamsChatsChat,
|
||||
TeamsChatsInfoChatName,
|
||||
[]string{memberID},
|
||||
TeamsChatsInfoChatTopic,
|
||||
[]string{topic},
|
||||
filters.In),
|
||||
}
|
||||
}
|
||||
|
||||
// ChatCreatedBefore produces one or more teamsChats chat name info scopes.
|
||||
// Matches any chat whose creation datetime is before the given datetime.
|
||||
// If any slice contains selectors.Any, that slice is reduced to [selectors.Any]
|
||||
// If any slice contains selectors.None, that slice is reduced to [selectors.None]
|
||||
// If any slice is empty, it defaults to [selectors.None]
|
||||
func (sr *TeamsChatsRestore) ChatCreatedBefore(datetime string) []TeamsChatsScope {
|
||||
return []TeamsChatsScope{
|
||||
makeInfoScope[TeamsChatsScope](
|
||||
TeamsChatsChat,
|
||||
TeamsChatsInfoChatCreatedBefore,
|
||||
[]string{datetime},
|
||||
filters.Greater),
|
||||
}
|
||||
}
|
||||
|
||||
// ChatCreatedBefore produces one or more teamsChats chat name info scopes.
|
||||
// Matches any chat whose creation datetime is after the given datetime.
|
||||
// If any slice contains selectors.Any, that slice is reduced to [selectors.Any]
|
||||
// If any slice contains selectors.None, that slice is reduced to [selectors.None]
|
||||
// If any slice is empty, it defaults to [selectors.None]
|
||||
func (sr *TeamsChatsRestore) ChatCreatedAfter(datetime string) []TeamsChatsScope {
|
||||
return []TeamsChatsScope{
|
||||
makeInfoScope[TeamsChatsScope](
|
||||
TeamsChatsChat,
|
||||
TeamsChatsInfoChatCreatedAfter,
|
||||
[]string{datetime},
|
||||
filters.Less),
|
||||
}
|
||||
}
|
||||
|
||||
// ChatLastMessageBefore produces one or more teamsChats chat name info scopes.
|
||||
// Matches any chat whose most recent message (if it has messages) is before the given datetime.
|
||||
// If any slice contains selectors.Any, that slice is reduced to [selectors.Any]
|
||||
// If any slice contains selectors.None, that slice is reduced to [selectors.None]
|
||||
// If any slice is empty, it defaults to [selectors.None]
|
||||
func (sr *TeamsChatsRestore) ChatLastMessageBefore(datetime string) []TeamsChatsScope {
|
||||
return []TeamsChatsScope{
|
||||
makeInfoScope[TeamsChatsScope](
|
||||
TeamsChatsChat,
|
||||
TeamsChatsInfoChatLastMessageBefore,
|
||||
[]string{datetime},
|
||||
filters.Greater),
|
||||
}
|
||||
}
|
||||
|
||||
// ChatCreatedBefore produces one or more teamsChats chat name info scopes.
|
||||
// Matches any chat whose most recent message (if it has messages) is after the given datetime.
|
||||
// If any slice contains selectors.Any, that slice is reduced to [selectors.Any]
|
||||
// If any slice contains selectors.None, that slice is reduced to [selectors.None]
|
||||
// If any slice is empty, it defaults to [selectors.None]
|
||||
func (sr *TeamsChatsRestore) ChatLastMessageAfter(datetime string) []TeamsChatsScope {
|
||||
return []TeamsChatsScope{
|
||||
makeInfoScope[TeamsChatsScope](
|
||||
TeamsChatsChat,
|
||||
TeamsChatsInfoChatLastMesasgeAfter,
|
||||
[]string{datetime},
|
||||
filters.Less),
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Categories
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -288,8 +349,12 @@ const (
|
||||
TeamsChatsChat teamsChatsCategory = "TeamsChatsChat"
|
||||
|
||||
// data contained within details.ItemInfo
|
||||
TeamsChatsInfoChatCreatedBefore teamsChatsCategory = "TeamsChatsInfoChatCreatedBefore"
|
||||
TeamsChatsInfoChatCreatedAfter teamsChatsCategory = "TeamsChatsInfoChatCreatedAfter"
|
||||
TeamsChatsInfoChatLastMessageBefore teamsChatsCategory = "TeamsChatsInfoChatLastMessageBefore"
|
||||
TeamsChatsInfoChatLastMesasgeAfter teamsChatsCategory = "TeamsChatsInfoChatLastMesasgeAfter"
|
||||
TeamsChatsInfoChatMember teamsChatsCategory = "TeamsChatsInfoChatMember"
|
||||
TeamsChatsInfoChatName teamsChatsCategory = "TeamsChatsInfoChatName"
|
||||
TeamsChatsInfoChatTopic teamsChatsCategory = "TeamsChatsInfoChatName"
|
||||
)
|
||||
|
||||
// teamsChatsLeafProperties describes common metadata of the leaf categories
|
||||
@ -317,7 +382,9 @@ func (ec teamsChatsCategory) String() string {
|
||||
// Ex: TeamsChatsUser.leafCat() => TeamsChatsUser
|
||||
func (ec teamsChatsCategory) leafCat() categorizer {
|
||||
switch ec {
|
||||
case TeamsChatsChat, TeamsChatsInfoChatMember, TeamsChatsInfoChatName:
|
||||
case TeamsChatsChat, TeamsChatsInfoChatMember, TeamsChatsInfoChatTopic,
|
||||
TeamsChatsInfoChatCreatedBefore, TeamsChatsInfoChatCreatedAfter,
|
||||
TeamsChatsInfoChatLastMessageBefore, TeamsChatsInfoChatLastMesasgeAfter:
|
||||
return TeamsChatsChat
|
||||
}
|
||||
|
||||
@ -505,8 +572,16 @@ func (s TeamsChatsScope) matchesInfo(dii details.ItemInfo) bool {
|
||||
switch infoCat {
|
||||
case TeamsChatsInfoChatMember:
|
||||
i = strings.Join(info.Chat.Members, ",")
|
||||
case TeamsChatsInfoChatName:
|
||||
i = info.Chat.Name
|
||||
case TeamsChatsInfoChatTopic:
|
||||
i = info.Chat.Topic
|
||||
case TeamsChatsInfoChatCreatedBefore, TeamsChatsInfoChatCreatedAfter:
|
||||
i = dttm.Format(info.Chat.CreatedAt)
|
||||
case TeamsChatsInfoChatLastMessageBefore, TeamsChatsInfoChatLastMesasgeAfter:
|
||||
if info.Chat.MessageCount < 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
i = dttm.Format(info.Chat.LastMessageAt)
|
||||
}
|
||||
|
||||
return s.Matches(infoCat, i)
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/alcionai/corso/src/internal/tester"
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/dttm"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/filters"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
@ -252,14 +253,16 @@ func (suite *TeamsChatsSelectorSuite) TestTeamsChatsScope_MatchesInfo() {
|
||||
cs := NewTeamsChatsRestore(Any())
|
||||
|
||||
const (
|
||||
name = "smarf mcfnords"
|
||||
topic = "smarf mcfnords"
|
||||
member = "cooks@2many.smarf"
|
||||
subject = "I have seen the fnords!"
|
||||
dtype = details.TeamsChat
|
||||
)
|
||||
|
||||
var (
|
||||
now = time.Now()
|
||||
future = now.Add(1 * time.Minute)
|
||||
past = dttm.Format(now.Add(-1 * time.Minute))
|
||||
future = dttm.Format(now.Add(1 * time.Minute))
|
||||
)
|
||||
|
||||
infoWith := func(itype details.ItemType) details.ItemInfo {
|
||||
@ -269,11 +272,11 @@ func (suite *TeamsChatsSelectorSuite) TestTeamsChatsScope_MatchesInfo() {
|
||||
Chat: details.ChatInfo{
|
||||
CreatedAt: now,
|
||||
HasExternalMembers: false,
|
||||
LastMessageAt: future,
|
||||
LastMessageAt: now,
|
||||
LastMessagePreview: "preview",
|
||||
Members: []string{member},
|
||||
MessageCount: 1,
|
||||
Name: name,
|
||||
Topic: topic,
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -285,12 +288,20 @@ func (suite *TeamsChatsSelectorSuite) TestTeamsChatsScope_MatchesInfo() {
|
||||
scope []TeamsChatsScope
|
||||
expect assert.BoolAssertionFunc
|
||||
}{
|
||||
{"chat with a different member", details.TeamsChat, cs.ChatMember("blarps"), assert.False},
|
||||
{"chat with the same member", details.TeamsChat, cs.ChatMember(member), assert.True},
|
||||
{"chat with a member submatch search", details.TeamsChat, cs.ChatMember(member[2:5]), assert.True},
|
||||
{"chat with a different name", details.TeamsChat, cs.ChatName("blarps"), assert.False},
|
||||
{"chat with the same name", details.TeamsChat, cs.ChatName(name), assert.True},
|
||||
{"chat with a subname search", details.TeamsChat, cs.ChatName(name[2:5]), assert.True},
|
||||
{"chat with a different member", dtype, cs.ChatMember("blarps"), assert.False},
|
||||
{"chat with the same member", dtype, cs.ChatMember(member), assert.True},
|
||||
{"chat with a member submatch search", dtype, cs.ChatMember(member[2:5]), assert.True},
|
||||
{"chat with a different topic", dtype, cs.ChatTopic("blarps"), assert.False},
|
||||
{"chat with the same topic", dtype, cs.ChatTopic(topic), assert.True},
|
||||
{"chat with a subtopic search", dtype, cs.ChatTopic(topic[2:5]), assert.True},
|
||||
{"chat created after", dtype, cs.ChatCreatedAfter(past), assert.True},
|
||||
{"chat not created after", dtype, cs.ChatCreatedAfter(future), assert.False},
|
||||
{"chat created before", dtype, cs.ChatCreatedBefore(future), assert.True},
|
||||
{"chat not created before", dtype, cs.ChatCreatedBefore(past), assert.False},
|
||||
{"chat last message after", dtype, cs.ChatLastMessageAfter(past), assert.True},
|
||||
{"chat last message not after", dtype, cs.ChatLastMessageAfter(future), assert.False},
|
||||
{"chat last message before", dtype, cs.ChatLastMessageBefore(future), assert.True},
|
||||
{"chat last message not before", dtype, cs.ChatLastMessageBefore(past), assert.False},
|
||||
}
|
||||
for _, test := range table {
|
||||
suite.Run(test.name, func() {
|
||||
|
||||
@ -103,7 +103,7 @@ func TeamsChatInfo(chat models.Chatable) *details.TeamsChatsInfo {
|
||||
LastMessagePreview: preview,
|
||||
Members: memberNames,
|
||||
MessageCount: len(msgs),
|
||||
Name: ptr.Val(chat.GetTopic()),
|
||||
Topic: ptr.Val(chat.GetTopic()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,79 @@ import (
|
||||
"github.com/alcionai/corso/src/pkg/services/m365/api/pagers"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// chat members pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// delta queries are not supported
|
||||
var _ pagers.NonDeltaHandler[models.ConversationMemberable] = &chatMembersPageCtrl{}
|
||||
|
||||
type chatMembersPageCtrl struct {
|
||||
chatID string
|
||||
gs graph.Servicer
|
||||
builder *chats.ItemMembersRequestBuilder
|
||||
options *chats.ItemMembersRequestBuilderGetRequestConfiguration
|
||||
}
|
||||
|
||||
func (p *chatMembersPageCtrl) SetNextLink(nextLink string) {
|
||||
p.builder = chats.NewItemMembersRequestBuilder(nextLink, p.gs.Adapter())
|
||||
}
|
||||
|
||||
func (p *chatMembersPageCtrl) GetPage(
|
||||
ctx context.Context,
|
||||
) (pagers.NextLinkValuer[models.ConversationMemberable], error) {
|
||||
resp, err := p.builder.Get(ctx, p.options)
|
||||
return resp, graph.Stack(ctx, err).OrNil()
|
||||
}
|
||||
|
||||
func (p *chatMembersPageCtrl) ValidModTimes() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c Chats) NewChatMembersPager(
|
||||
chatID string,
|
||||
cc CallConfig,
|
||||
) *chatMembersPageCtrl {
|
||||
builder := c.Stable.
|
||||
Client().
|
||||
Chats().
|
||||
ByChatId(chatID).
|
||||
Members()
|
||||
|
||||
options := &chats.ItemMembersRequestBuilderGetRequestConfiguration{
|
||||
QueryParameters: &chats.ItemMembersRequestBuilderGetQueryParameters{},
|
||||
Headers: newPreferHeaders(preferPageSize(maxNonDeltaPageSize)),
|
||||
}
|
||||
|
||||
if len(cc.Select) > 0 {
|
||||
options.QueryParameters.Select = cc.Select
|
||||
}
|
||||
|
||||
if len(cc.Expand) > 0 {
|
||||
options.QueryParameters.Expand = cc.Expand
|
||||
}
|
||||
|
||||
return &chatMembersPageCtrl{
|
||||
chatID: chatID,
|
||||
builder: builder,
|
||||
gs: c.Stable,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
// GetChatMembers fetches a delta of all members in the chat.
|
||||
func (c Chats) GetChatMembers(
|
||||
ctx context.Context,
|
||||
chatID string,
|
||||
cc CallConfig,
|
||||
) ([]models.ConversationMemberable, error) {
|
||||
ctx = clues.Add(ctx, "chat_id", chatID)
|
||||
pager := c.NewChatMembersPager(chatID, cc)
|
||||
items, err := pagers.BatchEnumerateItems[models.ConversationMemberable](ctx, pager)
|
||||
|
||||
return items, graph.Stack(ctx, err).OrNil()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// chat message pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@ -61,6 +61,11 @@ func (suite *ChatsPagerIntgSuite) TestEnumerateChats() {
|
||||
ac,
|
||||
chatID,
|
||||
chat.GetLastMessagePreview())
|
||||
|
||||
testEnumerateChatMembers(
|
||||
suite.T(),
|
||||
ac,
|
||||
chatID)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -123,3 +128,22 @@ func testEnumerateChatMessages(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testEnumerateChatMembers(
|
||||
t *testing.T,
|
||||
ac Chats,
|
||||
chatID string,
|
||||
) {
|
||||
ctx, flush := tester.NewContext(t)
|
||||
defer flush()
|
||||
|
||||
cc := CallConfig{}
|
||||
|
||||
members, err := ac.GetChatMembers(ctx, chatID, cc)
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
// no good way to test members right now. Even though
|
||||
// the graph api response contains the `userID` and `email`
|
||||
// properties, we can't access them in the sdk model
|
||||
assert.NotEmpty(t, members)
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ func (suite *ChatsAPIUnitSuite) TestChatsInfo() {
|
||||
ItemType: details.TeamsChat,
|
||||
Modified: then,
|
||||
Chat: details.ChatInfo{
|
||||
Name: "Hello world",
|
||||
Topic: "Hello world",
|
||||
LastMessageAt: then,
|
||||
LastMessagePreview: id,
|
||||
Members: []string{},
|
||||
@ -92,7 +92,7 @@ func (suite *ChatsAPIUnitSuite) TestChatsInfo() {
|
||||
ItemType: details.TeamsChat,
|
||||
Modified: then,
|
||||
Chat: details.ChatInfo{
|
||||
Name: "Hello world",
|
||||
Topic: "Hello world",
|
||||
LastMessageAt: then,
|
||||
LastMessagePreview: id,
|
||||
Members: []string{},
|
||||
@ -120,7 +120,7 @@ func (suite *ChatsAPIUnitSuite) TestChatsInfo() {
|
||||
ItemType: details.TeamsChat,
|
||||
Modified: then,
|
||||
Chat: details.ChatInfo{
|
||||
Name: "Hello world",
|
||||
Topic: "Hello world",
|
||||
LastMessageAt: time.Time{},
|
||||
LastMessagePreview: "",
|
||||
Members: []string{},
|
||||
@ -138,7 +138,7 @@ func (suite *ChatsAPIUnitSuite) TestChatsInfo() {
|
||||
chat, expected := test.expected()
|
||||
result := TeamsChatInfo(chat)
|
||||
|
||||
assert.Equal(t, expected.Chat.Name, result.Chat.Name)
|
||||
assert.Equal(t, expected.Chat.Topic, result.Chat.Topic)
|
||||
|
||||
expectCreated := chat.GetCreatedDateTime()
|
||||
if expectCreated != nil {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user