add boilerplate chats api calls

standard graph api boilerplate.
This commit is contained in:
ryanfkeepers 2024-01-19 17:38:01 -07:00
parent 0a6355eeb1
commit c999a3072f
4 changed files with 463 additions and 0 deletions

View File

@ -0,0 +1,89 @@
package api
import (
"testing"
"time"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/backup/details"
)
type ChatsAPIUnitSuite struct {
tester.Suite
}
func TestChatsAPIUnitSuite(t *testing.T) {
suite.Run(t, &ChatsAPIUnitSuite{Suite: tester.NewUnitSuite(t)})
}
func (suite *ChatsAPIUnitSuite) TestChatsInfo() {
start := time.Now()
tests := []struct {
name string
chatAndExpected func() (models.Chatable, *details.TeamsChatsInfo)
}{
{
name: "Empty chat",
chatAndExpected: func() (models.Chatable, *details.TeamsChatsInfo) {
chat := models.NewChat()
i := &details.TeamsChatsInfo{
ItemType: details.TeamsChat,
Chat: details.ChatInfo{},
}
return chat, i
},
},
{
name: "All fields",
chatAndExpected: func() (models.Chatable, *details.TeamsChatsInfo) {
now := time.Now()
then := now.Add(1 * time.Hour)
chat := models.NewChat()
chat.SetTopic(ptr.To("Hello world"))
chat.SetCreatedDateTime(&now)
chat.SetLastUpdatedDateTime(&then)
i := &details.TeamsChatsInfo{
ItemType: details.TeamsChat,
Chat: details.ChatInfo{
Name: "Hello world",
},
}
return chat, i
},
},
}
for _, test := range tests {
suite.Run(test.name, func() {
t := suite.T()
chat, expected := test.chatAndExpected()
result := TeamsChatInfo(chat)
assert.Equal(t, expected.Chat.Name, result.Chat.Name)
expectLastUpdated := chat.GetLastUpdatedDateTime()
if expectLastUpdated != nil {
assert.Equal(t, ptr.Val(expectLastUpdated), result.Modified)
} else {
assert.True(t, result.Modified.After(start))
}
expectCreated := chat.GetCreatedDateTime()
if expectCreated != nil {
assert.Equal(t, ptr.Val(expectCreated), result.Chat.CreatedAt)
} else {
assert.True(t, result.Chat.CreatedAt.After(start))
}
})
}
}

View File

@ -0,0 +1,74 @@
package api
import (
"context"
"github.com/microsoftgraph/msgraph-sdk-go/chats"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/services/m365/api/graph"
)
// ---------------------------------------------------------------------------
// controller
// ---------------------------------------------------------------------------
func (c Client) Chats() Chats {
return Chats{c}
}
// Chats is an interface-compliant provider of the client.
type Chats struct {
Client
}
// ---------------------------------------------------------------------------
// Chats
// ---------------------------------------------------------------------------
func (c Chats) GetChatByID(
ctx context.Context,
chatID string,
cc CallConfig,
) (models.Chatable, *details.TeamsChatsInfo, error) {
config := &chats.ChatItemRequestBuilderGetRequestConfiguration{
QueryParameters: &chats.ChatItemRequestBuilderGetQueryParameters{},
}
if len(cc.Select) > 0 {
config.QueryParameters.Select = cc.Select
}
if len(cc.Expand) > 0 {
config.QueryParameters.Expand = cc.Expand
}
resp, err := c.Stable.
Client().
Chats().
ByChatId(chatID).
Get(ctx, config)
if err != nil {
return nil, nil, graph.Stack(ctx, err)
}
return resp, TeamsChatInfo(resp), nil
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
func TeamsChatInfo(chat models.Chatable) *details.TeamsChatsInfo {
return &details.TeamsChatsInfo{
ItemType: details.TeamsChat,
Modified: ptr.OrNow(chat.GetLastUpdatedDateTime()),
Chat: details.ChatInfo{
CreatedAt: ptr.OrNow(chat.GetCreatedDateTime()),
Name: ptr.Val(chat.GetTopic()),
},
}
}

View File

@ -0,0 +1,172 @@
package api
import (
"context"
"github.com/alcionai/clues"
"github.com/microsoftgraph/msgraph-sdk-go/chats"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/microsoftgraph/msgraph-sdk-go/users"
"github.com/alcionai/corso/src/pkg/services/m365/api/graph"
"github.com/alcionai/corso/src/pkg/services/m365/api/pagers"
)
// ---------------------------------------------------------------------------
// chat message pager
// ---------------------------------------------------------------------------
// delta queries are not supported
var _ pagers.NonDeltaHandler[models.ChatMessageable] = &chatMessagePageCtrl{}
type chatMessagePageCtrl struct {
chatID string
gs graph.Servicer
builder *chats.ItemMessagesRequestBuilder
options *chats.ItemMessagesRequestBuilderGetRequestConfiguration
}
func (p *chatMessagePageCtrl) SetNextLink(nextLink string) {
p.builder = chats.NewItemMessagesRequestBuilder(nextLink, p.gs.Adapter())
}
func (p *chatMessagePageCtrl) GetPage(
ctx context.Context,
) (pagers.NextLinkValuer[models.ChatMessageable], error) {
resp, err := p.builder.Get(ctx, p.options)
return resp, graph.Stack(ctx, err).OrNil()
}
func (p *chatMessagePageCtrl) ValidModTimes() bool {
return true
}
func (c Chats) NewChatMessagePager(
chatID string,
cc CallConfig,
) *chatMessagePageCtrl {
builder := c.Stable.
Client().
Chats().
ByChatId(chatID).
Messages()
options := &chats.ItemMessagesRequestBuilderGetRequestConfiguration{
QueryParameters: &chats.ItemMessagesRequestBuilderGetQueryParameters{},
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 &chatMessagePageCtrl{
chatID: chatID,
builder: builder,
gs: c.Stable,
options: options,
}
}
// GetChatMessages fetches a delta of all messages in the chat.
func (c Chats) GetChatMessages(
ctx context.Context,
chatID string,
cc CallConfig,
) ([]models.ChatMessageable, error) {
ctx = clues.Add(ctx, "chat_id", chatID)
pager := c.NewChatMessagePager(chatID, cc)
items, err := pagers.BatchEnumerateItems[models.ChatMessageable](ctx, pager)
return items, graph.Stack(ctx, err).OrNil()
}
// GetChatMessageIDs fetches a delta of all messages in the chat.
// returns two maps: addedItems, deletedItems
func (c Chats) GetChatMessageIDs(
ctx context.Context,
chatID string,
cc CallConfig,
) (pagers.AddedAndRemoved, error) {
aar, err := pagers.GetAddedAndRemovedItemIDs[models.ChatMessageable](
ctx,
c.NewChatMessagePager(chatID, CallConfig{}),
nil,
"",
false, // delta queries are not supported
0,
pagers.AddedAndRemovedByDeletedDateTime[models.ChatMessageable],
IsNotSystemMessage)
return aar, clues.Stack(err).OrNil()
}
// ---------------------------------------------------------------------------
// chat pager
// ---------------------------------------------------------------------------
var _ pagers.NonDeltaHandler[models.Chatable] = &chatPageCtrl{}
type chatPageCtrl struct {
gs graph.Servicer
builder *users.ItemChatsRequestBuilder
options *users.ItemChatsRequestBuilderGetRequestConfiguration
}
func (p *chatPageCtrl) SetNextLink(nextLink string) {
p.builder = users.NewItemChatsRequestBuilder(nextLink, p.gs.Adapter())
}
func (p *chatPageCtrl) GetPage(
ctx context.Context,
) (pagers.NextLinkValuer[models.Chatable], error) {
resp, err := p.builder.Get(ctx, p.options)
return resp, graph.Stack(ctx, err).OrNil()
}
func (p *chatPageCtrl) ValidModTimes() bool {
return false
}
func (c Chats) NewChatPager(
userID string,
cc CallConfig,
) *chatPageCtrl {
options := &users.ItemChatsRequestBuilderGetRequestConfiguration{
QueryParameters: &users.ItemChatsRequestBuilderGetQueryParameters{},
Headers: newPreferHeaders(preferPageSize(maxNonDeltaPageSize)),
}
if len(cc.Select) > 0 {
options.QueryParameters.Select = cc.Select
}
if len(cc.Expand) > 0 {
options.QueryParameters.Expand = cc.Expand
}
res := &chatPageCtrl{
gs: c.Stable,
options: options,
builder: c.Stable.
Client().
Users().
ByUserId(userID).
Chats(),
}
return res
}
// GetChats fetches all chats in the team.
func (c Chats) GetChats(
ctx context.Context,
userID string,
cc CallConfig,
) ([]models.Chatable, error) {
return pagers.BatchEnumerateItems[models.Chatable](ctx, c.NewChatPager(userID, cc))
}

View File

@ -0,0 +1,128 @@
package api
import (
"regexp"
"testing"
"github.com/alcionai/clues"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/internal/tester/tconfig"
)
type ChatsPagerIntgSuite struct {
tester.Suite
its intgTesterSetup
}
func TestChatsPagerIntgSuite(t *testing.T) {
suite.Run(t, &ChatsPagerIntgSuite{
Suite: tester.NewIntegrationSuite(
t,
[][]string{tconfig.M365AcctCredEnvs}),
})
}
func (suite *ChatsPagerIntgSuite) SetupSuite() {
suite.its = newIntegrationTesterSetup(suite.T())
}
func (suite *ChatsPagerIntgSuite) TestEnumerateChats() {
var (
t = suite.T()
ac = suite.its.ac.Chats()
)
ctx, flush := tester.NewContext(t)
defer flush()
cc := CallConfig{
Expand: []string{"lastMessagePreview"},
}
chats, err := ac.GetChats(ctx, suite.its.user.id, cc)
require.NoError(t, err, clues.ToCore(err))
require.NotEmpty(t, chats)
for _, chat := range chats {
chatID := ptr.Val(chat.GetId())
suite.Run("chat_"+chatID, func() {
testGetChatByID(suite.T(), ac, chatID)
})
suite.Run("chat_messages_"+chatID, func() {
testEnumerateChatMessages(
suite.T(),
ac,
chatID,
chat.GetLastMessagePreview())
})
}
}
func testGetChatByID(
t *testing.T,
ac Chats,
chatID string,
) {
ctx, flush := tester.NewContext(t)
defer flush()
cc := CallConfig{}
chat, _, err := ac.GetChatByID(ctx, chatID, cc)
require.NoError(t, err, clues.ToCore(err))
require.NotNil(t, chat)
}
var attachmentHtmlRegexp = regexp.MustCompile("<attachment id=\"[a-zA-Z0-9].*\"></attachment>")
func testEnumerateChatMessages(
t *testing.T,
ac Chats,
chatID string,
lastMessagePreview models.ChatMessageInfoable,
) {
ctx, flush := tester.NewContext(t)
defer flush()
cc := CallConfig{}
messages, err := ac.GetChatMessages(ctx, chatID, cc)
require.NoError(t, err, clues.ToCore(err))
var lastID string
if lastMessagePreview != nil {
lastID = ptr.Val(lastMessagePreview.GetId())
}
for _, msg := range messages {
msgID := ptr.Val(msg.GetId())
assert.Equal(
t,
chatID,
ptr.Val(msg.GetChatId()),
"message:",
msgID)
if msgID == lastID {
previewContent := ptr.Val(lastMessagePreview.GetBody().GetContent())
msgContent := ptr.Val(msg.GetBody().GetContent())
previewContent = replaceAttachmentMarkup(previewContent, nil)
msgContent = replaceAttachmentMarkup(msgContent, nil)
assert.Equal(
t,
previewContent,
msgContent)
}
}
}