diff --git a/src/internal/tester/tconfig/config.go b/src/internal/tester/tconfig/config.go index a900f26f2..dbaebaecb 100644 --- a/src/internal/tester/tconfig/config.go +++ b/src/internal/tester/tconfig/config.go @@ -28,6 +28,7 @@ const ( TestCfgSiteURL = "m365siteurl" TestCfgTeamID = "m365teamid" TestCfgGroupID = "m365groupid" + TestCfgChannelID = "m365channelid" TestCfgUserID = "m365userid" TestCfgSecondaryUserID = "secondarym365userid" TestCfgTertiaryUserID = "tertiarym365userid" @@ -45,6 +46,7 @@ const ( EnvCorsoM365TestSiteURL = "CORSO_M365_TEST_SITE_URL" EnvCorsoM365TestTeamID = "CORSO_M365_TEST_TEAM_ID" EnvCorsoM365TestGroupID = "CORSO_M365_TEST_GROUP_ID" + EnvCorsoM365TestChannelID = "CORSO_M365_TEST_CHANNEL_ID" EnvCorsoM365TestUserID = "CORSO_M365_TEST_USER_ID" EnvCorsoSecondaryM365TestSiteID = "CORSO_SECONDARY_M365_TEST_SITE_ID" EnvCorsoSecondaryM365TestUserID = "CORSO_SECONDARY_M365_TEST_USER_ID" @@ -166,6 +168,12 @@ func ReadTestConfig() (map[string]string, error) { os.Getenv(EnvCorsoM365TestGroupID), vpr.GetString(TestCfgGroupID), "6f24b40d-b13d-4752-980f-f5fb9fba7aa0") + fallbackTo( + testEnv, + TestCfgChannelID, + os.Getenv(EnvCorsoM365TestChannelID), + vpr.GetString(TestCfgChannelID), + "19:nY6QHZ3hnHJ6ylarBxPjCOLRJNvrL3oKI5iW15QxTPA1@thread.tacv2") fallbackTo( testEnv, TestCfgSiteURL, diff --git a/src/internal/tester/tconfig/protected_resources.go b/src/internal/tester/tconfig/protected_resources.go index caac0c586..6a8d6a579 100644 --- a/src/internal/tester/tconfig/protected_resources.go +++ b/src/internal/tester/tconfig/protected_resources.go @@ -246,3 +246,14 @@ func M365GroupID(t *testing.T) string { return strings.ToLower(cfg[TestCfgTeamID]) } + +// M365ChannelID returns a channelID string representing the m365TeamsID described +// by either the env var CORSO_M365_TEST_CHANNEL_ID, the corso_test.toml config +// file or the default value (in that order of priority). The default is a +// last-attempt fallback that will only work on alcion's testing org. +func M365ChannelID(t *testing.T) string { + cfg, err := ReadTestConfig() + require.NoError(t, err, "retrieving m365 channel id from test configuration: %+v", clues.ToCore(err)) + + return cfg[TestCfgChannelID] +} diff --git a/src/pkg/services/m365/api/channels.go b/src/pkg/services/m365/api/channels.go index a6e0e3072..52e18b24b 100644 --- a/src/pkg/services/m365/api/channels.go +++ b/src/pkg/services/m365/api/channels.go @@ -2,6 +2,7 @@ package api import ( "context" + "fmt" "github.com/alcionai/clues" "github.com/alcionai/corso/src/internal/common/ptr" @@ -34,7 +35,7 @@ type Channels struct { // CreateContainer makes an channels with the name in the team func (c Channels) CreateChannel( ctx context.Context, - teamID, _, containerName string, + teamID, containerName string, ) (graph.Container, error) { body := models.NewChannel() body.SetDisplayName(&containerName) @@ -77,16 +78,13 @@ func (c Channels) DeleteChannel( return nil } -// prefer GetChannelByID where possible. -// use this only in cases where the models.Channelable -// is required. func (c Channels) GetChannel( ctx context.Context, teamID, containerID string, ) (models.Channelable, error) { config := &teams.ItemChannelsChannelItemRequestBuilderGetRequestConfiguration{ QueryParameters: &teams.ItemChannelsChannelItemRequestBuilderGetQueryParameters{ - Select: idAnd("name", "owner"), + Select: idAnd("displayName"), }, } @@ -116,20 +114,26 @@ func (c Channels) GetChannelByID( return ChannelsDisplayable{Channelable: channel}, nil } -// GetChannelByName fetches a calendar by name +// GetChannelByName fetches a channel by name func (c Channels) GetChannelByName( ctx context.Context, - teamID, _, containerName string, + teamID, containerName string, ) (graph.Container, error) { - ctx = clues.Add(ctx, "channel_name", containerName) + filter := fmt.Sprintf("displayName eq '%s'", containerName) + options := &teams.ItemChannelsRequestBuilderGetRequestConfiguration{ + QueryParameters: &teams.ItemChannelsRequestBuilderGetQueryParameters{ + Filter: &filter, + }, + } + resp, err := c.Stable. Client(). Teams(). ByTeamId(teamID). Channels(). - Get(ctx, nil) + Get(ctx, options) if err != nil { return nil, graph.Stack(ctx, err).WithClues(ctx) @@ -141,9 +145,9 @@ func (c Channels) GetChannelByName( return nil, clues.New("channel not found").WithClues(ctx) } - // We only allow the api to match one calendar with the provided name. + // We only allow the api to match one channel with the provided name. // If we match multiples, we'll eagerly return the first one. - logger.Ctx(ctx).Debugw("calendars matched the name search", "calendar_count", len(gv)) + logger.Ctx(ctx).Debugw("channels matched the name search") // Sanity check ID and name cal := gv[0] @@ -170,7 +174,7 @@ func (c Channels) PatchChannel( Patch(ctx, body, nil) if err != nil { - return graph.Wrap(ctx, err, "patching event calendar") + return graph.Wrap(ctx, err, "patching channel") } return nil diff --git a/src/pkg/services/m365/api/channels_pager_test.go b/src/pkg/services/m365/api/channels_pager_test.go new file mode 100644 index 000000000..4c8bc46e8 --- /dev/null +++ b/src/pkg/services/m365/api/channels_pager_test.go @@ -0,0 +1,162 @@ +package api_test + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/alcionai/clues" + "github.com/alcionai/corso/src/internal/common/ptr" + "github.com/alcionai/corso/src/internal/tester" + "github.com/alcionai/corso/src/internal/tester/tconfig" + "github.com/alcionai/corso/src/pkg/account" + "github.com/alcionai/corso/src/pkg/services/m365/api" + "github.com/microsoftgraph/msgraph-sdk-go/models" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type ChannelPagerIntgSuite struct { + tester.Suite + its intgTesterSetup +} + +func TestChannelPagerIntgSuite(t *testing.T) { + suite.Run(t, &ChannelPagerIntgSuite{ + Suite: tester.NewIntegrationSuite( + t, + [][]string{tconfig.M365AcctCredEnvs}), + }) +} + +func (suite *ChannelPagerIntgSuite) SetupSuite() { + suite.its = newIntegrationTesterSetup(suite.T()) +} + +func (suite *ChannelPagerIntgSuite) TestChannels_GetPage() { + t := suite.T() + + ctx, flush := tester.NewContext(t) + defer flush() + + teamID := tconfig.M365TeamsID(t) + channelID := tconfig.M365ChannelID(t) + pager := suite.its.ac.Channels().NewMessagePager(teamID, channelID, []string{}) + a, err := pager.GetPage(ctx) + assert.NoError(t, err, clues.ToCore(err)) + assert.NotNil(t, a) +} + +func (suite *ChannelPagerIntgSuite) TestChannels_CreateGetAndDelete() { + t := suite.T() + ctx, flush := tester.NewContext(t) + defer flush() + + var ( + yy, mm, dd = time.Now().Date() + hh = time.Now().Hour() + min = time.Now().Minute() + ss = time.Now().Second() + containerName = fmt.Sprintf("testChannel%d%d%d%d%d%d", yy, mm, dd, hh, min, ss) + teamID = tconfig.M365TeamsID(t) + credentials = suite.its.ac.Credentials + chanClient = suite.its.ac.Channels() + ) + + // GET channel - should be not found + _, err := suite.its.ac.Channels().GetChannelByName(ctx, teamID, containerName) + assert.Error(t, err, clues.ToCore(err)) + + // POST channel + channelPost, err := suite.its.ac.Channels().CreateChannel(ctx, teamID, containerName) + assert.NoError(t, err, clues.ToCore(err)) + + postChannelID := ptr.Val(channelPost.GetId()) + + // DELETE channel + defer func() { + _, err := chanClient.GetChannelByID(ctx, teamID, postChannelID) + + if err != nil { + fmt.Println("could not find channel: ", err) + } else { + deleteChannel(ctx, credentials, teamID, postChannelID) + } + }() + + // GET channel -should be found + channel, err := chanClient.GetChannelByName(ctx, teamID, containerName) + assert.NoError(t, err, clues.ToCore(err)) + assert.Equal(t, ptr.Val(channel.GetDisplayName()), containerName) + + // PATCH channel + patchBody := models.NewChannel() + patchName := fmt.Sprintf("othername%d%d%d%d%d%d", yy, mm, dd, hh, min, ss) + patchBody.SetDisplayName(ptr.To(patchName)) + err = chanClient.PatchChannel(ctx, teamID, postChannelID, patchBody) + assert.NoError(t, err, clues.ToCore(err)) + assert.Equal(t, ptr.Val(channel.GetDisplayName()), containerName) + + // GET channel -should not be found with old name + _, err = chanClient.GetChannelByName(ctx, teamID, containerName) + assert.Error(t, err, clues.ToCore(err)) + + // GET channel -should be found with new name + channel, err = chanClient.GetChannelByName(ctx, teamID, patchName) + assert.NoError(t, err, clues.ToCore(err)) + assert.Equal(t, ptr.Val(channel.GetDisplayName()), patchName) + assert.Equal(t, ptr.Val(channel.GetId()), postChannelID) + + // GET channel -should not be found with old name + err = chanClient.DeleteChannel(ctx, teamID, postChannelID) + assert.NoError(t, err, clues.ToCore(err)) + + // GET channel -should not be found anymore + _, err = chanClient.GetChannel(ctx, teamID, postChannelID) + assert.Error(t, err, clues.ToCore(err)) +} + +func deleteChannel(ctx context.Context, credentials account.M365Config, teamID, postChannelID string) { + srv, err := api.NewService(credentials) + if err != nil { + fmt.Println("Error found in getting creds") + } + + if err != nil { + fmt.Println("Error found in getting creds") + } + + err = srv.Client(). + Teams(). + ByTeamId(teamID). + Channels(). + ByChannelId(postChannelID). + Delete(ctx, nil) + if err != nil { + fmt.Println("channel could not be delete in defer") + } +} + +// func (suite *ChannelPagerIntgSuite) TestMessages_CreateGetAndDelete() { +// t := suite.T() +// ctx, flush := tester.NewContext(t) +// defer flush() + +// var ( +// teamID = tconfig.M365TeamsID(t) +// channelID = tconfig.M365ChannelID(t) +// credentials = suite.its.ac.Credentials +// chanClient = suite.its.ac.Channels() +// ) + +// POST channel +// patchBody := models.NewChatMessage() +// body := models.NewItemBody() +// content := "Hello World" +// body.SetContent(&content) +// patchBody.SetBody(body) + +// _, := suite.its.ac.Channels().PostMessage(ctx, teamID, channelID, patchBody) +// assert.NoError(t, err, clues.ToCore(err)) +// }