diff --git a/src/internal/tester/tconfig/config.go b/src/internal/tester/tconfig/config.go index c6bcd6e4b..1d9a89310 100644 --- a/src/internal/tester/tconfig/config.go +++ b/src/internal/tester/tconfig/config.go @@ -25,6 +25,8 @@ const ( TestCfgAzureTenantID = "azure_tenantid" TestCfgSiteID = "m365siteid" TestCfgSiteURL = "m365siteurl" + TestCfgTeamID = "m365teamid" + TestCfgGroupID = "m365groupid" TestCfgUserID = "m365userid" TestCfgSecondaryUserID = "secondarym365userid" TestCfgTertiaryUserID = "tertiarym365userid" @@ -38,6 +40,8 @@ const ( const ( EnvCorsoM365TestSiteID = "CORSO_M365_TEST_SITE_ID" EnvCorsoM365TestSiteURL = "CORSO_M365_TEST_SITE_URL" + EnvCorsoM365TestTeamID = "CORSO_M365_TEST_TEAM_ID" + EnvCorsoM365TestGroupID = "CORSO_M365_TEST_Group_ID" EnvCorsoM365TestUserID = "CORSO_M365_TEST_USER_ID" EnvCorsoSecondaryM365TestUserID = "CORSO_SECONDARY_M365_TEST_USER_ID" EnvCorsoTertiaryM365TestUserID = "CORSO_TERTIARY_M365_TEST_USER_ID" @@ -148,6 +152,18 @@ func ReadTestConfig() (map[string]string, error) { os.Getenv(EnvCorsoM365TestSiteID), vpr.GetString(TestCfgSiteID), "10rqc2.sharepoint.com,4892edf5-2ebf-46be-a6e5-a40b2cbf1c1a,38ab6d06-fc82-4417-af93-22d8733c22be") + fallbackTo( + testEnv, + TestCfgTeamID, + os.Getenv(EnvCorsoM365TestTeamID), + vpr.GetString(TestCfgTeamID), + "d288d6bc-4595-4ff5-87a1-6e7fd750aa42") + fallbackTo( + testEnv, + TestCfgGroupID, + os.Getenv(EnvCorsoM365TestGroupID), + vpr.GetString(TestCfgGroupID), + "3d1129b1-52e1-4f49-a47a-a515b14c8a8e") fallbackTo( testEnv, TestCfgSiteURL, diff --git a/src/internal/tester/tconfig/protected_resources.go b/src/internal/tester/tconfig/protected_resources.go index b9e31ce06..13c1c9fb9 100644 --- a/src/internal/tester/tconfig/protected_resources.go +++ b/src/internal/tester/tconfig/protected_resources.go @@ -209,3 +209,29 @@ func UnlicensedM365UserID(t *testing.T) string { return strings.ToLower(cfg[TestCfgSecondaryUserID]) } + +// Teams + +// M365TeamsID returns a teamID string representing the m365TeamsID described +// by either the env var CORSO_M365_TEST_TEAM_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 M365TeamsID(t *testing.T) string { + cfg, err := ReadTestConfig() + require.NoError(t, err, "retrieving m365 team id from test configuration: %+v", clues.ToCore(err)) + + return strings.ToLower(cfg[TestCfgTeamID]) +} + +// Groups + +// M365GroupID returns a groupID string representing the m365GroupID described +// by either the env var CORSO_M365_TEST_Group_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 M365GroupID(t *testing.T) string { + cfg, err := ReadTestConfig() + require.NoError(t, err, "retrieving m365 group id from test configuration: %+v", clues.ToCore(err)) + + return strings.ToLower(cfg[TestCfgTeamID]) +} diff --git a/src/pkg/services/m365/api/groups.go b/src/pkg/services/m365/api/groups.go index 9889e2bde..8eef3fba3 100644 --- a/src/pkg/services/m365/api/groups.go +++ b/src/pkg/services/m365/api/groups.go @@ -4,13 +4,14 @@ import ( "context" "github.com/alcionai/clues" + msgraphgocore "github.com/microsoftgraph/msgraph-sdk-go-core" + "github.com/microsoftgraph/msgraph-sdk-go/models" + "github.com/alcionai/corso/src/internal/common/str" "github.com/alcionai/corso/src/internal/common/tform" "github.com/alcionai/corso/src/internal/m365/graph" "github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/logger" - msgraphgocore "github.com/microsoftgraph/msgraph-sdk-go-core" - "github.com/microsoftgraph/msgraph-sdk-go/models" ) const ( @@ -22,8 +23,8 @@ const ( // controller // --------------------------------------------------------------------------- -func (c Client) Groups() Groups { - return Groups{c} +func (c Client) Teams() Teams { + return Teams{c} } // On creation of each Teams team a corrsponding group gets created. @@ -31,12 +32,12 @@ func (c Client) Groups() Groups { // drive and mail messages are owned by that group. // Teams is an interface-compliant provider of the client. -type Groups struct { +type Teams struct { Client } // GetAllTeams retrieves all groups. -func (c Groups) GetAllTeams( +func (c Teams) GetAll( ctx context.Context, errs *fault.Bus, ) ([]models.Groupable, error) { @@ -48,19 +49,6 @@ func (c Groups) GetAllTeams( return getGroups(ctx, true, errs, service) } -// GetAllGroups retrieves all groups. -func (c Groups) GetAll( - ctx context.Context, - errs *fault.Bus, -) ([]models.Groupable, error) { - service, err := c.Service() - if err != nil { - return nil, err - } - - return getGroups(ctx, false, errs, service) -} - // GetAll retrieves all groups. func getGroups( ctx context.Context, @@ -68,7 +56,6 @@ func getGroups( errs *fault.Bus, service graph.Servicer, ) ([]models.Groupable, error) { - resp, err := service.Client().Groups().Get(ctx, nil) if err != nil { return nil, graph.Wrap(ctx, err, "getting all groups") @@ -123,16 +110,18 @@ func IsTeam(ctx context.Context, g models.Groupable) bool { log.Debug("could not be converted to string value: ", ResourceProvisioningOptions) return false } + if s == teamsAdditionalDataLabel { return true } } } + return false } // GetID retrieves team by groupID/teamID. -func (c Groups) GetByID( +func (c Teams) GetByID( ctx context.Context, identifier string, ) (models.Groupable, error) { @@ -148,6 +137,12 @@ func (c Groups) GetByID( return nil, err } + if !IsTeam(ctx, resp) { + err := clues.New("given teamID is not related to any team") + + return nil, err + } + return resp, graph.Stack(ctx, err).OrNil() } diff --git a/src/pkg/services/m365/api/groups_test.go b/src/pkg/services/m365/api/groups_test.go index 0bb3e3686..dcb039dc5 100644 --- a/src/pkg/services/m365/api/groups_test.go +++ b/src/pkg/services/m365/api/groups_test.go @@ -3,13 +3,18 @@ package api_test import ( "testing" + "github.com/alcionai/clues" + "github.com/google/uuid" + "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" "github.com/alcionai/corso/src/pkg/fault" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" + "github.com/alcionai/corso/src/pkg/services/m365/api" ) type TeamsUnitSuite struct { @@ -20,6 +25,61 @@ func TestTeamsUnitSuite(t *testing.T) { suite.Run(t, &TeamsUnitSuite{Suite: tester.NewUnitSuite(t)}) } +func (suite *TeamsUnitSuite) TestValidateGroup() { + team := models.NewTeam() + team.SetDisplayName(ptr.To("testgroup")) + team.SetId(ptr.To("testID")) + + tests := []struct { + name string + args models.Groupable + errCheck assert.ErrorAssertionFunc + errIsSkippable bool + }{ + { + name: "Valid group ", + args: func() *models.Group { + s := models.NewGroup() + s.SetId(ptr.To("id")) + s.SetDisplayName(ptr.To("testTeam")) + return s + }(), + errCheck: assert.NoError, + }, + { + name: "No name", + args: func() *models.Group { + s := models.NewGroup() + s.SetId(ptr.To("id")) + return s + }(), + errCheck: assert.Error, + }, + { + name: "No ID", + args: func() *models.Group { + s := models.NewGroup() + s.SetDisplayName(ptr.To("testTeam")) + return s + }(), + errCheck: assert.Error, + }, + } + + for _, test := range tests { + suite.Run(test.name, func() { + t := suite.T() + + err := api.ValidateGroup(test.args) + test.errCheck(t, err, clues.ToCore(err)) + + if test.errIsSkippable { + assert.ErrorIs(t, err, api.ErrKnownSkippableCase) + } + }) + } +} + type TeamsIntgSuite struct { tester.Suite its intgTesterSetup @@ -37,19 +97,75 @@ func (suite *TeamsIntgSuite) SetupSuite() { suite.its = newIntegrationTesterSetup(suite.T()) } -func (suite *TeamsIntgSuite) TestGetAll() { +func (suite *TeamsIntgSuite) TestGetAllTeams() { t := suite.T() ctx, flush := tester.NewContext(t) defer flush() teams, err := suite.its.ac. - Groups(). - GetAllTeams(ctx, fault.New(true)) + Teams(). + GetAll(ctx, fault.New(true)) require.NoError(t, err) require.NotZero(t, len(teams), "must have at least one team") for _, team := range teams { - assert.NotEmpty(t, ptr.Val(team.GetDisplayName()), "must not return onedrive teams") + assert.True(t, api.IsTeam(ctx, team), "must not return non teams groups") + } +} + +func (suite *TeamsIntgSuite) TestTeams_GetByID() { + var ( + t = suite.T() + teamID = tconfig.M365TeamsID(t) + ) + + teamsAPI := suite.its.ac.Teams() + + table := []struct { + name string + id string + expectErr func(*testing.T, error) + }{ + { + name: "3 part id", + id: teamID, + expectErr: func(t *testing.T, err error) { + assert.NoError(t, err, clues.ToCore(err)) + }, + }, + { + name: "malformed id", + id: uuid.NewString(), + expectErr: func(t *testing.T, err error) { + assert.Error(t, err, clues.ToCore(err)) + }, + }, + { + name: "random id", + id: uuid.NewString() + "," + uuid.NewString(), + expectErr: func(t *testing.T, err error) { + assert.Error(t, err, clues.ToCore(err)) + }, + }, + + { + name: "malformed url", + id: "barunihlda", + expectErr: func(t *testing.T, err error) { + assert.Error(t, err, clues.ToCore(err)) + }, + }, + } + for _, test := range table { + suite.Run(test.name, func() { + t := suite.T() + + ctx, flush := tester.NewContext(t) + defer flush() + + _, err := teamsAPI.GetByID(ctx, test.id) + test.expectErr(t, err) + }) } } diff --git a/src/pkg/services/m365/api/helper_test.go b/src/pkg/services/m365/api/helper_test.go index 05e16b00e..984411f4a 100644 --- a/src/pkg/services/m365/api/helper_test.go +++ b/src/pkg/services/m365/api/helper_test.go @@ -82,6 +82,7 @@ type intgTesterSetup struct { siteID string siteDriveID string siteDriveRootFolderID string + teamID string } func newIntegrationTesterSetup(t *testing.T) intgTesterSetup { @@ -130,5 +131,13 @@ func newIntegrationTesterSetup(t *testing.T) intgTesterSetup { its.siteDriveRootFolderID = ptr.Val(siteDriveRootFolder.GetId()) + // teams + its.teamID = tconfig.M365TeamsID(t) + + team, err := its.ac.Teams().GetByID(ctx, its.teamID) + require.NoError(t, err, clues.ToCore(err)) + + its.teamID = ptr.Val(team.GetId()) + return its }