diff --git a/src/internal/m365/collection/groups/backup_test.go b/src/internal/m365/collection/groups/backup_test.go index c3cf1890e..d7ed5c368 100644 --- a/src/internal/m365/collection/groups/backup_test.go +++ b/src/internal/m365/collection/groups/backup_test.go @@ -61,6 +61,11 @@ func (bh mockBackupHandler) augmentItemInfo( // no-op } +//lint:ignore U1000 false linter issue due to generics +func (bh mockBackupHandler) supportsItemMetadata() bool { + return false +} + func (bh mockBackupHandler) canMakeDeltaQueries() bool { return true } diff --git a/src/internal/m365/collection/groups/channel_handler.go b/src/internal/m365/collection/groups/channel_handler.go index 4b0adc16a..1ed528a5c 100644 --- a/src/internal/m365/collection/groups/channel_handler.go +++ b/src/internal/m365/collection/groups/channel_handler.go @@ -125,6 +125,12 @@ func (bh channelsBackupHandler) augmentItemInfo( // no-op } +//lint:ignore U1000 false linter issue due to generics +func (bh channelsBackupHandler) supportsItemMetadata() bool { + // No .data and .meta files for channel messages + return false +} + func channelContainer(ch models.Channelable) container[models.Channelable] { return container[models.Channelable]{ storageDirFolders: path.Elements{ptr.Val(ch.GetId())}, diff --git a/src/internal/m365/collection/groups/collection.go b/src/internal/m365/collection/groups/collection.go index 76877c461..bebbca464 100644 --- a/src/internal/m365/collection/groups/collection.go +++ b/src/internal/m365/collection/groups/collection.go @@ -348,8 +348,12 @@ func (col *lazyFetchCollection[C, I]) streamItems(ctx context.Context, errs *fau // deleted items in a conversation. It might be added in the future // if graph supports it, so make sure we put up both .data and .meta // files for deletions. - col.stream <- data.NewDeletedItem(id + metadata.DataFileSuffix) - col.stream <- data.NewDeletedItem(id + metadata.MetaFileSuffix) + if col.getAndAugment.supportsItemMetadata() { + col.stream <- data.NewDeletedItem(id + metadata.DataFileSuffix) + col.stream <- data.NewDeletedItem(id + metadata.MetaFileSuffix) + } else { + col.stream <- data.NewDeletedItem(id) + } atomic.AddInt64(&streamedItems, 1) col.Counter.Inc(count.StreamItemsRemoved) @@ -378,6 +382,10 @@ func (col *lazyFetchCollection[C, I]) streamItems(ctx context.Context, errs *fau "item_id", id, "parent_path", path.LoggableDir(col.LocationPath().String())) + // Conversation posts carry a .data suffix, while channel messages + // don't have any suffix. Metadata files are only supported for conversations. + dataFile := id + // Handle metadata before data so that if metadata file fails, // we are not left with an orphaned data file. // @@ -396,15 +404,14 @@ func (col *lazyFetchCollection[C, I]) streamItems(ctx context.Context, errs *fau if err != nil && !errors.Is(err, errMetadataFilesNotSupported) { errs.AddRecoverable(ctx, clues.StackWC(ctx, err)) - return - } + return + } - if err == nil { // Skip adding progress reader for metadata files. It doesn't add // much value. storeItem, err := data.NewPrefetchedItem( itemMeta, - id+metadata.MetaFileSuffix, + metaFile, // Use the same last modified time as post's. modTime) if err != nil { @@ -422,12 +429,12 @@ func (col *lazyFetchCollection[C, I]) streamItems(ctx context.Context, errs *fau modTime: modTime, getAndAugment: col.getAndAugment, resourceID: col.protectedResource, - itemID: id, + itemID: dataFile, containerIDs: col.FullPath().Folders(), contains: col.contains, parentPath: col.LocationPath().String(), }, - id+metadata.DataFileSuffix, + dataFile, modTime, col.Counter, el) diff --git a/src/internal/m365/collection/groups/collection_test.go b/src/internal/m365/collection/groups/collection_test.go index 96c2d462f..f971715f7 100644 --- a/src/internal/m365/collection/groups/collection_test.go +++ b/src/internal/m365/collection/groups/collection_test.go @@ -180,6 +180,11 @@ func (getAndAugmentChannelMessage) augmentItemInfo(*details.GroupsInfo, models.C // no-op } +//lint:ignore U1000 false linter issue due to generics +func (getAndAugmentChannelMessage) supportsItemMetadata() bool { + return false +} + func (suite *CollectionUnitSuite) TestPrefetchCollection_streamItems() { var ( t = suite.T() @@ -322,6 +327,11 @@ func (m *getAndAugmentConversation) augmentItemInfo(*details.GroupsInfo, models. // no-op } +//lint:ignore U1000 false linter issue due to generics +func (m *getAndAugmentConversation) supportsItemMetadata() bool { + return true +} + func (m *getAndAugmentConversation) check(t *testing.T, expected []string) { // Sort before comparing. We could use a set, but that would prevent us from // detecting duplicates. diff --git a/src/internal/m365/collection/groups/conversation_handler.go b/src/internal/m365/collection/groups/conversation_handler.go index edba3fa8a..9cadee5ed 100644 --- a/src/internal/m365/collection/groups/conversation_handler.go +++ b/src/internal/m365/collection/groups/conversation_handler.go @@ -170,6 +170,11 @@ func (bh conversationsBackupHandler) augmentItemInfo( dgi.Post.Topic = ptr.Val(c.GetTopic()) } +//lint:ignore U1000 false linter issue due to generics +func (bh conversationsBackupHandler) supportsItemMetadata() bool { + return true +} + func conversationThreadContainer( c models.Conversationable, t models.ConversationThreadable, diff --git a/src/internal/m365/collection/groups/handlers.go b/src/internal/m365/collection/groups/handlers.go index 21463f77b..8c7090d2f 100644 --- a/src/internal/m365/collection/groups/handlers.go +++ b/src/internal/m365/collection/groups/handlers.go @@ -36,6 +36,7 @@ type getItemAndAugmentInfoer[C graph.GetIDer, I groupsItemer] interface { getItemer[I] getItemMetadataer[C, I] augmentItemInfoer[C] + supportsItemMetadataer[C, I] } type augmentItemInfoer[C graph.GetIDer] interface { @@ -60,6 +61,10 @@ type getItemMetadataer[C graph.GetIDer, I groupsItemer] interface { ) (io.ReadCloser, int, error) } +type supportsItemMetadataer[C graph.GetIDer, I groupsItemer] interface { + supportsItemMetadata() bool +} + // gets all containers for the resource type getContainerser[C graph.GetIDer] interface { getContainers(