Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d92f3446b5 | ||
|
|
fadd5ec5df | ||
|
|
0f41256010 | ||
|
|
8e35c81197 | ||
|
|
7d642d6b6b | ||
|
|
7302a3f059 | ||
|
|
05bd2f5ea3 | ||
|
|
8fbe4ea326 | ||
|
|
ae0d0f6684 | ||
|
|
b2f0870c27 | ||
|
|
47e5707ae4 | ||
|
|
d9525310a1 | ||
|
|
a8b375ae60 | ||
|
|
4e6bbf2be9 | ||
|
|
19ba595391 | ||
|
|
59df02db13 | ||
|
|
847142b2a8 | ||
|
|
fb0a26067e | ||
|
|
5d15caf89b | ||
|
|
f505ee4643 | ||
|
|
c6054c5661 | ||
|
|
38c2a1ed11 | ||
|
|
e3256869f8 |
@ -1,4 +1,4 @@
|
|||||||
// Code generated by "stringer -type=opStatus -linecomment"; DO NOT EDIT.
|
// Code generated by "stringer -type=OpStatus -linecomment"; DO NOT EDIT.
|
||||||
|
|
||||||
package operations
|
package operations
|
||||||
|
|
||||||
@ -15,13 +15,13 @@ func _() {
|
|||||||
_ = x[NoData-4]
|
_ = x[NoData-4]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _opStatus_name = "Status UnknownIn ProgressCompletedFailedNo Data"
|
const _OpStatus_name = "Status UnknownIn ProgressCompletedFailedNo Data"
|
||||||
|
|
||||||
var _opStatus_index = [...]uint8{0, 14, 25, 34, 40, 47}
|
var _OpStatus_index = [...]uint8{0, 14, 25, 34, 40, 47}
|
||||||
|
|
||||||
func (i OpStatus) String() string {
|
func (i OpStatus) String() string {
|
||||||
if i < 0 || i >= OpStatus(len(_opStatus_index)-1) {
|
if i < 0 || i >= OpStatus(len(_OpStatus_index)-1) {
|
||||||
return "opStatus(" + strconv.FormatInt(int64(i), 10) + ")"
|
return "OpStatus(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
}
|
}
|
||||||
return _opStatus_name[_opStatus_index[i]:_opStatus_index[i+1]]
|
return _OpStatus_name[_OpStatus_index[i]:_OpStatus_index[i+1]]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,6 +26,8 @@ const (
|
|||||||
TestCfgSecondarySiteID = "secondarym365siteid"
|
TestCfgSecondarySiteID = "secondarym365siteid"
|
||||||
TestCfgSiteID = "m365siteid"
|
TestCfgSiteID = "m365siteid"
|
||||||
TestCfgSiteURL = "m365siteurl"
|
TestCfgSiteURL = "m365siteurl"
|
||||||
|
TestCfgTeamID = "m365teamid"
|
||||||
|
TestCfgGroupID = "m365groupid"
|
||||||
TestCfgUserID = "m365userid"
|
TestCfgUserID = "m365userid"
|
||||||
TestCfgSecondaryUserID = "secondarym365userid"
|
TestCfgSecondaryUserID = "secondarym365userid"
|
||||||
TestCfgTertiaryUserID = "tertiarym365userid"
|
TestCfgTertiaryUserID = "tertiarym365userid"
|
||||||
@ -41,6 +43,8 @@ const (
|
|||||||
EnvCorsoM365LoadTestOrgUsers = "CORSO_M365_LOAD_TEST_ORG_USERS"
|
EnvCorsoM365LoadTestOrgUsers = "CORSO_M365_LOAD_TEST_ORG_USERS"
|
||||||
EnvCorsoM365TestSiteID = "CORSO_M365_TEST_SITE_ID"
|
EnvCorsoM365TestSiteID = "CORSO_M365_TEST_SITE_ID"
|
||||||
EnvCorsoM365TestSiteURL = "CORSO_M365_TEST_SITE_URL"
|
EnvCorsoM365TestSiteURL = "CORSO_M365_TEST_SITE_URL"
|
||||||
|
EnvCorsoM365TestTeamID = "CORSO_M365_TEST_TEAM_ID"
|
||||||
|
EnvCorsoM365TestGroupID = "CORSO_M365_TEST_GROUP_ID"
|
||||||
EnvCorsoM365TestUserID = "CORSO_M365_TEST_USER_ID"
|
EnvCorsoM365TestUserID = "CORSO_M365_TEST_USER_ID"
|
||||||
EnvCorsoSecondaryM365TestSiteID = "CORSO_SECONDARY_M365_TEST_SITE_ID"
|
EnvCorsoSecondaryM365TestSiteID = "CORSO_SECONDARY_M365_TEST_SITE_ID"
|
||||||
EnvCorsoSecondaryM365TestUserID = "CORSO_SECONDARY_M365_TEST_USER_ID"
|
EnvCorsoSecondaryM365TestUserID = "CORSO_SECONDARY_M365_TEST_USER_ID"
|
||||||
@ -150,6 +154,18 @@ func ReadTestConfig() (map[string]string, error) {
|
|||||||
os.Getenv(EnvCorsoM365TestSiteID),
|
os.Getenv(EnvCorsoM365TestSiteID),
|
||||||
vpr.GetString(TestCfgSiteID),
|
vpr.GetString(TestCfgSiteID),
|
||||||
"4892edf5-2ebf-46be-a6e5-a40b2cbf1c1a,38ab6d06-fc82-4417-af93-22d8733c22be")
|
"4892edf5-2ebf-46be-a6e5-a40b2cbf1c1a,38ab6d06-fc82-4417-af93-22d8733c22be")
|
||||||
|
fallbackTo(
|
||||||
|
testEnv,
|
||||||
|
TestCfgTeamID,
|
||||||
|
os.Getenv(EnvCorsoM365TestTeamID),
|
||||||
|
vpr.GetString(TestCfgTeamID),
|
||||||
|
"6f24b40d-b13d-4752-980f-f5fb9fba7aa0")
|
||||||
|
fallbackTo(
|
||||||
|
testEnv,
|
||||||
|
TestCfgGroupID,
|
||||||
|
os.Getenv(EnvCorsoM365TestGroupID),
|
||||||
|
vpr.GetString(TestCfgGroupID),
|
||||||
|
"6f24b40d-b13d-4752-980f-f5fb9fba7aa0")
|
||||||
fallbackTo(
|
fallbackTo(
|
||||||
testEnv,
|
testEnv,
|
||||||
TestCfgSiteURL,
|
TestCfgSiteURL,
|
||||||
|
|||||||
@ -220,3 +220,29 @@ func UnlicensedM365UserID(t *testing.T) string {
|
|||||||
|
|
||||||
return strings.ToLower(cfg[TestCfgSecondaryUserID])
|
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])
|
||||||
|
}
|
||||||
|
|||||||
@ -30,6 +30,8 @@ const (
|
|||||||
ExchangeMetadataService // exchangeMetadata
|
ExchangeMetadataService // exchangeMetadata
|
||||||
OneDriveMetadataService // onedriveMetadata
|
OneDriveMetadataService // onedriveMetadata
|
||||||
SharePointMetadataService // sharepointMetadata
|
SharePointMetadataService // sharepointMetadata
|
||||||
|
GroupsService // groups
|
||||||
|
GroupsMetadataService // groupsMetadata
|
||||||
)
|
)
|
||||||
|
|
||||||
func toServiceType(service string) ServiceType {
|
func toServiceType(service string) ServiceType {
|
||||||
|
|||||||
@ -15,11 +15,13 @@ func _() {
|
|||||||
_ = x[ExchangeMetadataService-4]
|
_ = x[ExchangeMetadataService-4]
|
||||||
_ = x[OneDriveMetadataService-5]
|
_ = x[OneDriveMetadataService-5]
|
||||||
_ = x[SharePointMetadataService-6]
|
_ = x[SharePointMetadataService-6]
|
||||||
|
_ = x[GroupsService-7]
|
||||||
|
_ = x[GroupsMetadataService-8]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _ServiceType_name = "UnknownServiceexchangeonedrivesharepointexchangeMetadataonedriveMetadatasharepointMetadata"
|
const _ServiceType_name = "UnknownServiceexchangeonedrivesharepointexchangeMetadataonedriveMetadatasharepointMetadatagroupsgroupsMetadata"
|
||||||
|
|
||||||
var _ServiceType_index = [...]uint8{0, 14, 22, 30, 40, 56, 72, 90}
|
var _ServiceType_index = [...]uint8{0, 14, 22, 30, 40, 56, 72, 90, 96, 110}
|
||||||
|
|
||||||
func (i ServiceType) String() string {
|
func (i ServiceType) String() string {
|
||||||
if i < 0 || i >= ServiceType(len(_ServiceType_index)-1) {
|
if i < 0 || i >= ServiceType(len(_ServiceType_index)-1) {
|
||||||
|
|||||||
197
src/pkg/services/m365/api/groups.go
Normal file
197
src/pkg/services/m365/api/groups.go
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
teamsAdditionalDataLabel = "Team"
|
||||||
|
ResourceProvisioningOptions = "resourceProvisioningOptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// controller
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func (c Client) Groups() Groups {
|
||||||
|
return Groups{c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// On creation of each Teams team a corrsponding group gets created.
|
||||||
|
// The group acts as the protected resource, and all teams data like events,
|
||||||
|
// drive and mail messages are owned by that group.
|
||||||
|
|
||||||
|
// Groups is an interface-compliant provider of the client.
|
||||||
|
type Groups struct {
|
||||||
|
Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, func(ctx context.Context, g models.Groupable) bool { return true }, errs, service)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTeams retrieves all Teams.
|
||||||
|
func (c Groups) GetTeams(
|
||||||
|
ctx context.Context,
|
||||||
|
errs *fault.Bus,
|
||||||
|
) ([]models.Groupable, error) {
|
||||||
|
service, err := c.Service()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return getGroups(ctx, FetchOnlyTeams, errs, service)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAll retrieves all groups.
|
||||||
|
func getGroups(
|
||||||
|
ctx context.Context,
|
||||||
|
filterGroupsData func(ctx context.Context, g models.Groupable) bool,
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
iter, err := msgraphgocore.NewPageIterator[models.Groupable](
|
||||||
|
resp,
|
||||||
|
service.Adapter(),
|
||||||
|
models.CreateGroupCollectionResponseFromDiscriminatorValue)
|
||||||
|
if err != nil {
|
||||||
|
return nil, graph.Wrap(ctx, err, "creating groups iterator")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
groups = make([]models.Groupable, 0)
|
||||||
|
el = errs.Local()
|
||||||
|
)
|
||||||
|
|
||||||
|
iterator := func(item models.Groupable) bool {
|
||||||
|
if el.Failure() != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
err := ValidateGroup(item)
|
||||||
|
if err != nil {
|
||||||
|
el.AddRecoverable(ctx, graph.Wrap(ctx, err, "validating groups"))
|
||||||
|
} else {
|
||||||
|
if filterGroupsData(ctx, item) {
|
||||||
|
groups = append(groups, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := iter.Iterate(ctx, iterator); err != nil {
|
||||||
|
return nil, graph.Wrap(ctx, err, "iterating all groups")
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups, el.Failure()
|
||||||
|
}
|
||||||
|
|
||||||
|
func FetchOnlyTeams(ctx context.Context, g models.Groupable) bool {
|
||||||
|
log := logger.Ctx(ctx)
|
||||||
|
|
||||||
|
if g.GetAdditionalData()[ResourceProvisioningOptions] != nil {
|
||||||
|
val, _ := tform.AnyValueToT[[]any](ResourceProvisioningOptions, g.GetAdditionalData())
|
||||||
|
for _, v := range val {
|
||||||
|
s, err := str.AnyToString(v)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("could not be converted to string value: ", ResourceProvisioningOptions)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if s == teamsAdditionalDataLabel {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetID retrieves group by groupID.
|
||||||
|
func (c Groups) GetByID(
|
||||||
|
ctx context.Context,
|
||||||
|
identifier string,
|
||||||
|
) (models.Groupable, error) {
|
||||||
|
service, err := c.Service()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := service.Client().Groups().ByGroupId(identifier).Get(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
err := graph.Wrap(ctx, err, "getting group by id")
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, graph.Stack(ctx, err).OrNil()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTeamByID retrieves group by groupID.
|
||||||
|
func (c Groups) GetTeamByID(
|
||||||
|
ctx context.Context,
|
||||||
|
identifier string,
|
||||||
|
) (models.Groupable, error) {
|
||||||
|
service, err := c.Service()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := service.Client().Groups().ByGroupId(identifier).Get(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
err := graph.Wrap(ctx, err, "getting group by id")
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !FetchOnlyTeams(ctx, resp) {
|
||||||
|
err := clues.New("given teamID is not related to any team")
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, graph.Stack(ctx, err).OrNil()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// helpers
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ValidateGroup ensures the item is a Groupable, and contains the necessary
|
||||||
|
// identifiers that we handle with all groups.
|
||||||
|
func ValidateGroup(item models.Groupable) error {
|
||||||
|
if item.GetId() == nil {
|
||||||
|
return clues.New("missing ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
if item.GetDisplayName() == nil {
|
||||||
|
return clues.New("missing display name")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
240
src/pkg/services/m365/api/groups_test.go
Normal file
240
src/pkg/services/m365/api/groups_test.go
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
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/alcionai/corso/src/pkg/services/m365/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GroupUnitSuite struct {
|
||||||
|
tester.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGroupsUnitSuite(t *testing.T) {
|
||||||
|
suite.Run(t, &GroupUnitSuite{Suite: tester.NewUnitSuite(t)})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *GroupUnitSuite) TestValidateGroup() {
|
||||||
|
group := models.NewGroup()
|
||||||
|
group.SetDisplayName(ptr.To("testgroup"))
|
||||||
|
group.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("testgroup"))
|
||||||
|
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("testgroup"))
|
||||||
|
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 GroupsIntgSuite struct {
|
||||||
|
tester.Suite
|
||||||
|
its intgTesterSetup
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGroupsIntgSuite(t *testing.T) {
|
||||||
|
suite.Run(t, &GroupsIntgSuite{
|
||||||
|
Suite: tester.NewIntegrationSuite(
|
||||||
|
t,
|
||||||
|
[][]string{tconfig.M365AcctCredEnvs}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *GroupsIntgSuite) SetupSuite() {
|
||||||
|
suite.its = newIntegrationTesterSetup(suite.T())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *GroupsIntgSuite) TestGetAllGroups() {
|
||||||
|
t := suite.T()
|
||||||
|
|
||||||
|
ctx, flush := tester.NewContext(t)
|
||||||
|
defer flush()
|
||||||
|
|
||||||
|
groups, err := suite.its.ac.
|
||||||
|
Groups().
|
||||||
|
GetAll(ctx, fault.New(true))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotZero(t, len(groups), "must have at least one group")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *GroupsIntgSuite) TestGetAllTeams() {
|
||||||
|
t := suite.T()
|
||||||
|
|
||||||
|
ctx, flush := tester.NewContext(t)
|
||||||
|
defer flush()
|
||||||
|
|
||||||
|
groups, err := suite.its.ac.
|
||||||
|
Groups().
|
||||||
|
GetTeams(ctx, fault.New(true))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotZero(t, len(groups), "must have at least one group")
|
||||||
|
|
||||||
|
for _, team := range groups {
|
||||||
|
assert.True(t, api.FetchOnlyTeams(ctx, team), "must not return non groups groups")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *GroupsIntgSuite) TestTeams_GetByID() {
|
||||||
|
var (
|
||||||
|
t = suite.T()
|
||||||
|
teamID = tconfig.M365TeamsID(t)
|
||||||
|
)
|
||||||
|
|
||||||
|
teamsAPI := suite.its.ac.Groups()
|
||||||
|
|
||||||
|
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.GetTeamByID(ctx, test.id)
|
||||||
|
test.expectErr(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *GroupsIntgSuite) TestGroups_GetByID() {
|
||||||
|
var (
|
||||||
|
t = suite.T()
|
||||||
|
groupID = tconfig.M365GroupID(t)
|
||||||
|
)
|
||||||
|
|
||||||
|
groupsAPI := suite.its.ac.Groups()
|
||||||
|
|
||||||
|
table := []struct {
|
||||||
|
name string
|
||||||
|
id string
|
||||||
|
expectErr func(*testing.T, error)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "3 part id",
|
||||||
|
id: groupID,
|
||||||
|
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 := groupsAPI.GetByID(ctx, test.id)
|
||||||
|
test.expectErr(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -83,6 +83,7 @@ type intgTesterSetup struct {
|
|||||||
siteID string
|
siteID string
|
||||||
siteDriveID string
|
siteDriveID string
|
||||||
siteDriveRootFolderID string
|
siteDriveRootFolderID string
|
||||||
|
teamID string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newIntegrationTesterSetup(t *testing.T) intgTesterSetup {
|
func newIntegrationTesterSetup(t *testing.T) intgTesterSetup {
|
||||||
@ -131,5 +132,13 @@ func newIntegrationTesterSetup(t *testing.T) intgTesterSetup {
|
|||||||
|
|
||||||
its.siteDriveRootFolderID = ptr.Val(siteDriveRootFolder.GetId())
|
its.siteDriveRootFolderID = ptr.Val(siteDriveRootFolder.GetId())
|
||||||
|
|
||||||
|
// teams
|
||||||
|
its.teamID = tconfig.M365TeamsID(t)
|
||||||
|
|
||||||
|
team, err := its.ac.Groups().GetTeamByID(ctx, its.teamID)
|
||||||
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
|
its.teamID = ptr.Val(team.GetId())
|
||||||
|
|
||||||
return its
|
return its
|
||||||
}
|
}
|
||||||
|
|||||||
@ -313,6 +313,82 @@ func SitesMap(
|
|||||||
return idname.NewCache(itn), nil
|
return idname.NewCache(itn), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Teams
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Team is the minimal information required to identify and display a M365 Team.
|
||||||
|
type Team struct {
|
||||||
|
ID string
|
||||||
|
|
||||||
|
// DisplayName is the human-readable name of the team. Normally the plaintext name that the
|
||||||
|
// user provided when they created the team or the updated name if it was changed.
|
||||||
|
// Ex: displayName: "Testing Team"
|
||||||
|
DisplayName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// TeamsCompat returns a list of teams in the specified M365 tenant.
|
||||||
|
func TeamsCompat(ctx context.Context, acct account.Account) ([]*Team, error) {
|
||||||
|
errs := fault.New(true)
|
||||||
|
|
||||||
|
us, err := Teams(ctx, acct, errs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return us, errs.Failure()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Teams returns a list of teams in the specified M365 tenant
|
||||||
|
func Teams(ctx context.Context, acct account.Account, errs *fault.Bus) ([]*Team, error) {
|
||||||
|
ac, err := makeAC(ctx, acct, path.GroupsService)
|
||||||
|
if err != nil {
|
||||||
|
return nil, clues.Stack(err).WithClues(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return getAllTeams(ctx, ac.Groups())
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseUser extracts information from `models.Userable` we care about
|
||||||
|
func parseTeam(item models.Groupable) (*Team, error) {
|
||||||
|
if item.GetDisplayName() == nil {
|
||||||
|
return nil, clues.New("Team missing display name").
|
||||||
|
With("Team ID", ptr.Val(item.GetId()))
|
||||||
|
}
|
||||||
|
|
||||||
|
u := &Team{
|
||||||
|
ID: ptr.Val(item.GetId()),
|
||||||
|
DisplayName: ptr.Val(item.GetDisplayName()),
|
||||||
|
}
|
||||||
|
|
||||||
|
return u, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type getAllGroupers interface {
|
||||||
|
GetAll(ctx context.Context, errs *fault.Bus) ([]models.Groupable, error)
|
||||||
|
GetTeams(ctx context.Context, errs *fault.Bus) ([]models.Groupable, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAllTeams(ctx context.Context, gas getAllGroupers) ([]*Team, error) {
|
||||||
|
teams, err := gas.GetTeams(ctx, fault.New(true))
|
||||||
|
if err != nil {
|
||||||
|
return nil, clues.Wrap(err, "retrieving teams")
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := make([]*Team, 0, len(teams))
|
||||||
|
|
||||||
|
for _, team := range teams {
|
||||||
|
t, err := parseTeam(team)
|
||||||
|
if err != nil {
|
||||||
|
return nil, clues.Wrap(err, "parsing teams")
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = append(ret, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// helpers
|
// helpers
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@ -145,6 +145,30 @@ func (suite *M365IntegrationSuite) TestSites() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *M365IntegrationSuite) TestTeams() {
|
||||||
|
t := suite.T()
|
||||||
|
|
||||||
|
ctx, flush := tester.NewContext(t)
|
||||||
|
defer flush()
|
||||||
|
|
||||||
|
graph.InitializeConcurrencyLimiter(ctx, true, 4)
|
||||||
|
|
||||||
|
acct := tconfig.NewM365Account(suite.T())
|
||||||
|
|
||||||
|
teams, err := Teams(ctx, acct, fault.New(true))
|
||||||
|
assert.NoError(t, err, clues.ToCore(err))
|
||||||
|
assert.NotEmpty(t, teams)
|
||||||
|
|
||||||
|
for _, team := range teams {
|
||||||
|
suite.Run("team_"+team.ID, func() {
|
||||||
|
t := suite.T()
|
||||||
|
|
||||||
|
assert.NotEmpty(t, team.ID)
|
||||||
|
assert.NotEmpty(t, team.DisplayName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type m365UnitSuite struct {
|
type m365UnitSuite struct {
|
||||||
tester.Suite
|
tester.Suite
|
||||||
}
|
}
|
||||||
@ -473,6 +497,45 @@ func (suite *DiscoveryIntgSuite) TestSites_InvalidCredentials() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *DiscoveryIntgSuite) TestTeams_InvalidCredentials() {
|
||||||
|
table := []struct {
|
||||||
|
name string
|
||||||
|
acct func(t *testing.T) account.Account
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Invalid Credentials",
|
||||||
|
acct: func(t *testing.T) account.Account {
|
||||||
|
a, err := account.NewAccount(
|
||||||
|
account.ProviderM365,
|
||||||
|
account.M365Config{
|
||||||
|
M365: credentials.M365{
|
||||||
|
AzureClientID: "Test",
|
||||||
|
AzureClientSecret: "without",
|
||||||
|
},
|
||||||
|
AzureTenantID: "data",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
|
return a
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range table {
|
||||||
|
suite.Run(test.name, func() {
|
||||||
|
t := suite.T()
|
||||||
|
|
||||||
|
ctx, flush := tester.NewContext(t)
|
||||||
|
defer flush()
|
||||||
|
|
||||||
|
teams, err := Teams(ctx, test.acct(t), fault.New(true))
|
||||||
|
assert.Empty(t, teams, "returned some teams")
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *DiscoveryIntgSuite) TestGetUserInfo() {
|
func (suite *DiscoveryIntgSuite) TestGetUserInfo() {
|
||||||
table := []struct {
|
table := []struct {
|
||||||
name string
|
name string
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user