diff --git a/src/internal/m365/collection/groups/conversation_handler.go b/src/internal/m365/collection/groups/conversation_handler.go index 8da0eded7..d34ea3a8a 100644 --- a/src/internal/m365/collection/groups/conversation_handler.go +++ b/src/internal/m365/collection/groups/conversation_handler.go @@ -124,13 +124,17 @@ func (bh conversationsBackupHandler) getItem( containerIDs path.Elements, // expects: [conversationID, threadID] postID string, ) (models.Postable, *details.GroupsInfo, error) { + cc := api.CallConfig{ + Expand: []string{"inReplyTo"}, + } + return bh.ac.GetConversationPost( ctx, groupID, containerIDs[0], containerIDs[1], postID, - api.CallConfig{}) + cc) } //lint:ignore U1000 false linter issue due to generics diff --git a/src/pkg/backup/details/groups.go b/src/pkg/backup/details/groups.go index 34f167e77..6857ea32a 100644 --- a/src/pkg/backup/details/groups.go +++ b/src/pkg/backup/details/groups.go @@ -65,6 +65,7 @@ type ConversationPostInfo struct { Creator string `json:"creator,omitempty"` Preview string `json:"preview,omitempty"` Recipients []string `json:"recipients,omitempty"` + InReplyTo string `json:"inReplyTo,omitempty"` Size int64 `json:"size,omitempty"` Topic string `json:"topic,omitempty"` } diff --git a/src/pkg/services/m365/api/conversations.go b/src/pkg/services/m365/api/conversations.go index ebfafe619..e8c90354d 100644 --- a/src/pkg/services/m365/api/conversations.go +++ b/src/pkg/services/m365/api/conversations.go @@ -69,8 +69,25 @@ func (c Conversations) GetConversationPost( preview = "malformed or unparseable content body: " + preview } + var inReplyToID string + + prevPost := post.GetInReplyTo() + if prevPost != nil { + inReplyToID = ptr.Val(prevPost.GetId()) + } + + // Set prev post to nil to avoid storing it again in the backup. Storage is unnecessary + // since this is a read only property and graph doesn't support POSTing it. This is + // also safe to do since we do a full enumeration every time, so post and all its + // ancestors are guaranteed to exist. + // + // All we need to persist here is the prev post ID here so that we can build the + // reply tree during restore operation and restore posts top to bottom using + // POST /groups/{id}/conversations/{id}/threads/{id}/posts/{id}/reply + post.SetInReplyTo(nil) + if !ptr.Val(post.GetHasAttachments()) && !HasAttachments(post.GetBody()) { - return post, conversationPostInfo(post, contentLen, preview), nil + return post, conversationPostInfo(post, contentLen, preview, inReplyToID), nil } attachments, totalSize, err := c.getAttachments( @@ -95,7 +112,9 @@ func (c Conversations) GetConversationPost( post.SetAttachments(attachments) - return post, conversationPostInfo(post, contentLen, preview), graph.Stack(ctx, err).OrNil() + return post, + conversationPostInfo(post, contentLen, preview, inReplyToID), + graph.Stack(ctx, err).OrNil() } // --------------------------------------------------------------------------- @@ -105,7 +124,7 @@ func (c Conversations) GetConversationPost( func conversationPostInfo( post models.Postable, size int64, - preview string, + preview, prevPostID string, ) *details.GroupsInfo { if post == nil { return nil @@ -120,6 +139,7 @@ func conversationPostInfo( CreatedAt: ptr.Val(post.GetCreatedDateTime()), Creator: sender, Preview: preview, + InReplyTo: prevPostID, Size: size, } diff --git a/src/pkg/services/m365/api/conversations_test.go b/src/pkg/services/m365/api/conversations_test.go index a5ac24de7..9a959288b 100644 --- a/src/pkg/services/m365/api/conversations_test.go +++ b/src/pkg/services/m365/api/conversations_test.go @@ -109,7 +109,7 @@ func (suite *ConversationsAPIUnitSuite) TestConversationPostInfo() { t := suite.T() post, expected := test.postAndInfo() - result := conversationPostInfo(post, 0, "") + result := conversationPostInfo(post, 0, "", "") assert.Equal(t, expected, result) })