diff --git a/src/internal/m365/collection/groups/backup.go b/src/internal/m365/collection/groups/backup.go index 9b31126a1..fe53db51a 100644 --- a/src/internal/m365/collection/groups/backup.go +++ b/src/internal/m365/collection/groups/backup.go @@ -61,25 +61,51 @@ func CreateCollections( // conversations as well. // Also, this should be produced by the Handler. // chanPager := handler.NewChannelsPager(qp.ProtectedResource.ID()) - // TODO(neha): enumerate channels - channels := []graph.Displayable{} - collections, err := populateCollections( - ctx, - qp, - handler, - su, - channels, - scope, - // dps, - bpc.Options, - errs) - if err != nil { - return nil, clues.Wrap(err, "filling collections") - } + // enumerating channels + pager := handler.NewChannelsPager(qp.ProtectedResource.ID(), []string{}) - for _, coll := range collections { - allCollections = append(allCollections, coll) + // Loop through all pages returned by Graph API. + for { + var ( + err error + page api.PageLinker + ) + + page, err = pager.GetPage(graph.ConsumeNTokens(ctx, graph.SingleGetOrDeltaLC)) + if err != nil { + return nil, graph.Wrap(ctx, err, "retrieving drives") + } + + channels, err := pager.ValuesIn(page) + if err != nil { + return nil, graph.Wrap(ctx, err, "extracting drives from response") + } + + collections, err := populateCollections( + ctx, + qp, + handler, + su, + channels, + scope, + // dps, + bpc.Options, + errs) + if err != nil { + return nil, clues.Wrap(err, "filling collections") + } + + for _, coll := range collections { + allCollections = append(allCollections, coll) + } + + nextLink := ptr.Val(page.GetOdataNextLink()) + if len(nextLink) == 0 { + break + } + + pager.SetNext(nextLink) } return allCollections, nil @@ -90,7 +116,7 @@ func populateCollections( qp graph.QueryParams, bh BackupHandler, statusUpdater support.StatusUpdater, - channels []graph.Displayable, + channels []models.Channelable, scope selectors.GroupsScope, // dps DeltaPaths, ctrlOpts control.Options, @@ -153,11 +179,13 @@ func populateCollections( // } // ictx = clues.Add(ictx, "previous_path", prevPath) - + // TODO: Neha check this + var fields []string // TODO: the handler should provide this implementation. + // TODO: if we doing this messages are items for us. items, err := collectItems( ctx, - bh.NewMessagePager(qp.ProtectedResource.ID(), ptr.Val(c.GetId()))) + bh.NewMessagePager(qp.ProtectedResource.ID(), ptr.Val(c.GetId()), fields)) if err != nil { el.AddRecoverable(ctx, clues.Stack(err)) continue @@ -306,7 +334,7 @@ func includeContainer( directory := ptr.Val(gd.GetDisplayName()) // TODO(keepers): awaiting parent branch to update to main - ok := scope.Matches(selectors.GroupsCategoryUnknown, directory) + ok := scope.Matches(selectors.GroupsChannelMessage, directory) logger.Ctx(ctx).With( "included", ok, diff --git a/src/internal/m365/collection/groups/groups_handler.go b/src/internal/m365/collection/groups/groups_handler.go new file mode 100644 index 000000000..68faeb75c --- /dev/null +++ b/src/internal/m365/collection/groups/groups_handler.go @@ -0,0 +1,58 @@ +package groups + +import ( + "context" + + "github.com/alcionai/corso/src/pkg/selectors" + "github.com/alcionai/corso/src/pkg/services/m365/api" + "github.com/microsoft/kiota-abstractions-go/serialization" + "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +var _ BackupHandler = &groupBackupHandler{} + +type groupBackupHandler struct { + ac api.Channels + groupID string + scope selectors.GroupsScope +} + +func NewGroupBackupHandler(groupID string, ac api.Channels, scope selectors.GroupsScope) groupBackupHandler { + return groupBackupHandler{ + ac: ac, + groupID: groupID, + scope: scope, + } +} + +func (gHandler groupBackupHandler) GetChannelByID(ctx context.Context, teamID, channelID string) (models.Channelable, error) { + return gHandler.ac.Client.Channels().GetChannel(ctx, teamID, channelID) +} + +func (gHandler groupBackupHandler) NewChannelsPager( + teamID string, + fields []string, +) api.ChannelDeltaEnumerator { + return gHandler.ac.NewChannelPager(teamID, fields) +} + +func (gHandler groupBackupHandler) GetMessageByID( + ctx context.Context, + teamID, channelID, itemID string, +) (models.ChatMessageable, error) { + return gHandler.ac.GetMessageByID(ctx, teamID, channelID, itemID) +} + +func (gHandler groupBackupHandler) NewMessagePager( + teamID, channelID string, + fields []string, +) api.ChannelMessageDeltaEnumerator { + return gHandler.ac.NewMessagePager(teamID, channelID, fields) +} + +func (gHandler groupBackupHandler) GetMessageReplies( + ctx context.Context, + teamID, channelID, messageID string, +) (serialization.Parsable, error) { + return gHandler.ac.GetReplies(ctx, teamID, channelID, messageID) +} diff --git a/src/internal/m365/collection/groups/handlers.go b/src/internal/m365/collection/groups/handlers.go index bf3cb8f0f..b68edb8d6 100644 --- a/src/internal/m365/collection/groups/handlers.go +++ b/src/internal/m365/collection/groups/handlers.go @@ -16,6 +16,7 @@ type BackupHandler interface { ) (models.Channelable, error) NewChannelsPager( teamID string, + fields []string, ) api.ChannelDeltaEnumerator GetMessageByID( @@ -24,6 +25,7 @@ type BackupHandler interface { ) (models.ChatMessageable, error) NewMessagePager( teamID, channelID string, + fields []string, ) api.ChannelMessageDeltaEnumerator GetMessageReplies( @@ -31,10 +33,3 @@ type BackupHandler interface { teamID, channelID, messageID string, ) (serialization.Parsable, error) } - -type BackupMessagesHandler interface { - GetMessage(ctx context.Context, teamID, channelID, itemID string) (models.ChatMessageable, error) - NewMessagePager(teamID, channelID string) api.ChannelMessageDeltaEnumerator - GetChannel(ctx context.Context, teamID, channelID string) (models.Channelable, error) - GetReply(ctx context.Context, teamID, channelID, messageID string) (serialization.Parsable, error) -} diff --git a/src/internal/m365/service/groups/backup.go b/src/internal/m365/service/groups/backup.go index b74b5fde0..c48d1587c 100644 --- a/src/internal/m365/service/groups/backup.go +++ b/src/internal/m365/service/groups/backup.go @@ -10,6 +10,7 @@ import ( "github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/m365/collection/drive" + "github.com/alcionai/corso/src/internal/m365/collection/groups" "github.com/alcionai/corso/src/internal/m365/collection/site" "github.com/alcionai/corso/src/internal/m365/graph" "github.com/alcionai/corso/src/internal/m365/support" @@ -89,6 +90,27 @@ func ProduceBackupCollections( el.AddRecoverable(ctx, err) continue } + + case path.ChannelMessagesCategory: + mbpc := inject.BackupProducerConfig{ + LastBackupVersion: bpc.LastBackupVersion, + Options: bpc.Options, + ProtectedResource: bpc.ProtectedResource, + Selector: bpc.Selector, + } + + dbcs, err = groups.CreateCollections( + ctx, + mbpc, + groups.NewGroupBackupHandler(bpc.ProtectedResource.ID(), ac.Channels(), scope), + creds.AzureTenantID, + scope, + su, + errs) + if err != nil { + el.AddRecoverable(ctx, err) + continue + } } collections = append(collections, dbcs...) diff --git a/src/pkg/services/m365/api/channels.go b/src/pkg/services/m365/api/channels.go index d48b59d3d..a8480a5a2 100644 --- a/src/pkg/services/m365/api/channels.go +++ b/src/pkg/services/m365/api/channels.go @@ -12,7 +12,6 @@ import ( "github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/m365/graph" "github.com/alcionai/corso/src/pkg/backup/details" - "github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/logger" ) @@ -108,7 +107,6 @@ func (c Channels) GetChannelByName( func (c Channels) GetMessage( ctx context.Context, teamID, channelID, itemID string, - errs *fault.Bus, ) (serialization.Parsable, *details.GroupsInfo, error) { var size int64 @@ -128,6 +126,27 @@ func (c Channels) GetMessage( return message, ChannelMessageInfo(message, size), nil } +// GetMessage retrieves a ChannelMessage item. +func (c Channels) GetMessageByID( + ctx context.Context, + teamID, channelID, itemID string, +) (models.ChatMessageable, error) { + message, err := c.Stable. + Client(). + Teams(). + ByTeamId(teamID). + Channels(). + ByChannelId(channelID). + Messages(). + ByChatMessageId(itemID). + Get(ctx, nil) + if err != nil { + return nil, graph.Stack(ctx, err) + } + + return message, nil +} + // --------------------------------------------------------------------------- // replies // --------------------------------------------------------------------------- diff --git a/src/pkg/services/m365/api/channels_pager.go b/src/pkg/services/m365/api/channels_pager.go index aebd51759..b50fe3fbc 100644 --- a/src/pkg/services/m365/api/channels_pager.go +++ b/src/pkg/services/m365/api/channels_pager.go @@ -86,6 +86,7 @@ type ChannelDeltaEnumerator interface { PageLinker ValuesInPageLinker[models.Channelable] SetNextLinker + GetPage(ctx context.Context) (PageLinker, error) } // TODO: implement @@ -98,8 +99,7 @@ type channelPageCtrl struct { } func (c Channels) NewChannelPager( - teamID, - channelID string, + teamID string, fields []string, ) *channelPageCtrl { requestConfig := &teams.ItemChannelsRequestBuilderGetRequestConfiguration{