move service api to client methods (#4701)

sdk consumers need a way to configure the graph api client options when using the services api.  This is the first in a two-part change to expose those options.  This step moves the api to a client-based set of methods instead of free functions.  The next step will add client configuration to the service client.

---

#### Does this PR need a docs update or release note?

- [x] 🕐 Yes, but in a later PR

#### Type of change

- [x] 🌻 Feature

#### Test Plan

- [x] 💚 E2E
This commit is contained in:
Keepers 2023-11-16 18:03:57 -07:00 committed by GitHub
parent 2a85b61213
commit 963d78b75e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 174 additions and 290 deletions

View File

@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- Change file extension of messages export to json to match the content - Change file extension of messages export to json to match the content
- SDK consumption of the /services/m365 package has shifted from independent functions to a client-based api.
- SDK consumers can now configure the /services/m365 graph api client configuration when constructing a new m365 client.
### Fixed ### Fixed
- Handle OneDrive folders being deleted and recreated midway through a backup - Handle OneDrive folders being deleted and recreated midway through a backup

View File

@ -156,7 +156,12 @@ func createGroupsCmd(cmd *cobra.Command, args []string) error {
// TODO: log/print recoverable errors // TODO: log/print recoverable errors
errs := fault.New(false) errs := fault.New(false)
ins, err := m365.GroupsMap(ctx, *acct, errs) svcCli, err := m365.NewM365Client(ctx, *acct)
if err != nil {
return Only(ctx, clues.Stack(err))
}
ins, err := svcCli.GroupsMap(ctx, errs)
if err != nil { if err != nil {
return Only(ctx, clues.Wrap(err, "Failed to retrieve M365 groups")) return Only(ctx, clues.Wrap(err, "Failed to retrieve M365 groups"))
} }

View File

@ -157,7 +157,12 @@ func createSharePointCmd(cmd *cobra.Command, args []string) error {
// TODO: log/print recoverable errors // TODO: log/print recoverable errors
errs := fault.New(false) errs := fault.New(false)
ins, err := m365.SitesMap(ctx, *acct, errs) svcCli, err := m365.NewM365Client(ctx, *acct)
if err != nil {
return Only(ctx, clues.Stack(err))
}
ins, err := svcCli.SitesMap(ctx, errs)
if err != nil { if err != nil {
return Only(ctx, clues.Wrap(err, "Failed to retrieve M365 sites")) return Only(ctx, clues.Wrap(err, "Failed to retrieve M365 sites"))
} }

View File

@ -51,8 +51,9 @@ func NewClient(
creds account.M365Config, creds account.M365Config,
co control.Options, co control.Options,
counter *count.Bus, counter *count.Bus,
opts ...graph.Option,
) (Client, error) { ) (Client, error) {
s, err := NewService(creds, counter) s, err := NewService(creds, counter, opts...)
if err != nil { if err != nil {
return Client{}, err return Client{}, err
} }

View File

@ -8,9 +8,7 @@ import (
"github.com/alcionai/corso/src/internal/common/idname" "github.com/alcionai/corso/src/internal/common/idname"
"github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/path"
"github.com/alcionai/corso/src/pkg/services/m365/api" "github.com/alcionai/corso/src/pkg/services/m365/api"
) )
@ -29,19 +27,13 @@ type Group struct {
} }
// GroupByID retrieves a specific group. // GroupByID retrieves a specific group.
func GroupByID( func (c client) GroupByID(
ctx context.Context, ctx context.Context,
acct account.Account,
id string, id string,
) (*Group, error) { ) (*Group, error) {
ac, err := makeAC(ctx, acct, path.GroupsService)
if err != nil {
return nil, clues.Stack(err)
}
cc := api.CallConfig{} cc := api.CallConfig{}
g, err := ac.Groups().GetByID(ctx, id, cc) g, err := c.ac.Groups().GetByID(ctx, id, cc)
if err != nil { if err != nil {
return nil, clues.Stack(err) return nil, clues.Stack(err)
} }
@ -50,10 +42,10 @@ func GroupByID(
} }
// GroupsCompat returns a list of groups in the specified M365 tenant. // GroupsCompat returns a list of groups in the specified M365 tenant.
func GroupsCompat(ctx context.Context, acct account.Account) ([]*Group, error) { func (c client) GroupsCompat(ctx context.Context) ([]*Group, error) {
errs := fault.New(true) errs := fault.New(true)
us, err := Groups(ctx, acct, errs) us, err := c.Groups(ctx, errs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -62,17 +54,11 @@ func GroupsCompat(ctx context.Context, acct account.Account) ([]*Group, error) {
} }
// Groups returns a list of groups in the specified M365 tenant // Groups returns a list of groups in the specified M365 tenant
func Groups( func (c client) Groups(
ctx context.Context, ctx context.Context,
acct account.Account,
errs *fault.Bus, errs *fault.Bus,
) ([]*Group, error) { ) ([]*Group, error) {
ac, err := makeAC(ctx, acct, path.GroupsService) return getAllGroups(ctx, c.ac.Groups())
if err != nil {
return nil, clues.Stack(err)
}
return getAllGroups(ctx, ac.Groups())
} }
func getAllGroups( func getAllGroups(
@ -98,18 +84,12 @@ func getAllGroups(
return ret, nil return ret, nil
} }
func SitesInGroup( func (c client) SitesInGroup(
ctx context.Context, ctx context.Context,
acct account.Account,
groupID string, groupID string,
errs *fault.Bus, errs *fault.Bus,
) ([]*Site, error) { ) ([]*Site, error) {
ac, err := makeAC(ctx, acct, path.GroupsService) sites, err := c.ac.Groups().GetAllSites(ctx, groupID, errs)
if err != nil {
return nil, clues.Stack(err)
}
sites, err := ac.Groups().GetAllSites(ctx, groupID, errs)
if err != nil { if err != nil {
return nil, clues.Stack(err) return nil, clues.Stack(err)
} }
@ -144,12 +124,11 @@ func parseGroup(ctx context.Context, mg models.Groupable) (*Group, error) {
} }
// GroupsMap retrieves an id-name cache of all groups in the tenant. // GroupsMap retrieves an id-name cache of all groups in the tenant.
func GroupsMap( func (c client) GroupsMap(
ctx context.Context, ctx context.Context,
acct account.Account,
errs *fault.Bus, errs *fault.Bus,
) (idname.Cacher, error) { ) (idname.Cacher, error) {
groups, err := Groups(ctx, acct, errs) groups, err := c.Groups(ctx, errs)
if err != nil { if err != nil {
return idname.NewCache(nil), err return idname.NewCache(nil), err
} }

View File

@ -1,4 +1,4 @@
package m365_test package m365
import ( import (
"testing" "testing"
@ -11,17 +11,14 @@ import (
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/internal/tester/tconfig" "github.com/alcionai/corso/src/internal/tester/tconfig"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/credentials"
"github.com/alcionai/corso/src/pkg/errs" "github.com/alcionai/corso/src/pkg/errs"
"github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/services/m365"
"github.com/alcionai/corso/src/pkg/services/m365/api/graph" "github.com/alcionai/corso/src/pkg/services/m365/api/graph"
) )
type GroupsIntgSuite struct { type GroupsIntgSuite struct {
tester.Suite tester.Suite
acct account.Account cli client
} }
func TestGroupsIntgSuite(t *testing.T) { func TestGroupsIntgSuite(t *testing.T) {
@ -38,9 +35,13 @@ func (suite *GroupsIntgSuite) SetupSuite() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
graph.InitializeConcurrencyLimiter(ctx, true, 4) acct := tconfig.NewM365Account(t)
suite.acct = tconfig.NewM365Account(t) var err error
// will init the concurrency limiter
suite.cli, err = NewM365Client(ctx, acct)
require.NoError(t, err, clues.ToCore(err))
} }
func (suite *GroupsIntgSuite) TestGroupByID() { func (suite *GroupsIntgSuite) TestGroupByID() {
@ -49,11 +50,9 @@ func (suite *GroupsIntgSuite) TestGroupByID() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
graph.InitializeConcurrencyLimiter(ctx, true, 4)
gid := tconfig.M365TeamID(t) gid := tconfig.M365TeamID(t)
group, err := m365.GroupByID(ctx, suite.acct, gid) group, err := suite.cli.GroupByID(ctx, gid)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
require.NotNil(t, group) require.NotNil(t, group)
@ -67,11 +66,9 @@ func (suite *GroupsIntgSuite) TestGroupByID_ByEmail() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
graph.InitializeConcurrencyLimiter(ctx, true, 4)
gid := tconfig.M365TeamID(t) gid := tconfig.M365TeamID(t)
group, err := m365.GroupByID(ctx, suite.acct, gid) group, err := suite.cli.GroupByID(ctx, gid)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
require.NotNil(t, group) require.NotNil(t, group)
@ -80,7 +77,7 @@ func (suite *GroupsIntgSuite) TestGroupByID_ByEmail() {
gemail := tconfig.M365TeamEmail(t) gemail := tconfig.M365TeamEmail(t)
groupByEmail, err := m365.GroupByID(ctx, suite.acct, gemail) groupByEmail, err := suite.cli.GroupByID(ctx, gemail)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
require.NotNil(t, group) require.NotNil(t, group)
@ -93,9 +90,7 @@ func (suite *GroupsIntgSuite) TestGroupByID_notFound() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
graph.InitializeConcurrencyLimiter(ctx, true, 4) group, err := suite.cli.GroupByID(ctx, uuid.NewString())
group, err := m365.GroupByID(ctx, suite.acct, uuid.NewString())
require.Nil(t, group) require.Nil(t, group)
require.ErrorIs(t, err, graph.ErrResourceOwnerNotFound, clues.ToCore(err)) require.ErrorIs(t, err, graph.ErrResourceOwnerNotFound, clues.ToCore(err))
require.True(t, errs.Is(err, errs.ResourceOwnerNotFound)) require.True(t, errs.Is(err, errs.ResourceOwnerNotFound))
@ -107,12 +102,7 @@ func (suite *GroupsIntgSuite) TestGroups() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
graph.InitializeConcurrencyLimiter(ctx, true, 4) groups, err := suite.cli.Groups(ctx, fault.New(true))
groups, err := m365.Groups(
ctx,
suite.acct,
fault.New(true))
assert.NoError(t, err, clues.ToCore(err)) assert.NoError(t, err, clues.ToCore(err))
assert.NotEmpty(t, groups) assert.NotEmpty(t, groups)
@ -137,15 +127,9 @@ func (suite *GroupsIntgSuite) TestSitesInGroup() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
graph.InitializeConcurrencyLimiter(ctx, true, 4)
gid := tconfig.M365TeamID(t) gid := tconfig.M365TeamID(t)
sites, err := m365.SitesInGroup( sites, err := suite.cli.SitesInGroup(ctx, gid, fault.New(true))
ctx,
suite.acct,
gid,
fault.New(true))
assert.NoError(t, err, clues.ToCore(err)) assert.NoError(t, err, clues.ToCore(err))
assert.NotEmpty(t, sites) assert.NotEmpty(t, sites)
} }
@ -156,12 +140,7 @@ func (suite *GroupsIntgSuite) TestGroupsMap() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
graph.InitializeConcurrencyLimiter(ctx, true, 4) gm, err := suite.cli.GroupsMap(ctx, fault.New(true))
gm, err := m365.GroupsMap(
ctx,
suite.acct,
fault.New(true))
assert.NoError(t, err, clues.ToCore(err)) assert.NoError(t, err, clues.ToCore(err))
assert.NotEmpty(t, gm) assert.NotEmpty(t, gm)
@ -177,44 +156,3 @@ func (suite *GroupsIntgSuite) TestGroupsMap() {
}) })
} }
} }
func (suite *GroupsIntgSuite) TestGroups_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()
groups, err := m365.Groups(
ctx,
test.acct(t),
fault.New(true))
assert.Empty(t, groups, "returned no groups")
assert.NotNil(t, err)
})
}
}

View File

@ -11,8 +11,22 @@ import (
"github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/path"
"github.com/alcionai/corso/src/pkg/services/m365/api" "github.com/alcionai/corso/src/pkg/services/m365/api"
"github.com/alcionai/corso/src/pkg/services/m365/api/graph"
) )
type client struct {
ac api.Client
}
func NewM365Client(
ctx context.Context,
acct account.Account,
opts ...graph.Option,
) (client, error) {
ac, err := makeAC(ctx, acct, opts...)
return client{ac}, clues.Stack(err).OrNil()
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// interfaces & structs // interfaces & structs
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -28,9 +42,10 @@ type getAller[T any] interface {
func makeAC( func makeAC(
ctx context.Context, ctx context.Context,
acct account.Account, acct account.Account,
pst path.ServiceType, opts ...graph.Option,
) (api.Client, error) { ) (api.Client, error) {
api.InitConcurrencyLimit(ctx, pst) // exchange service inits a limit to concurrency.
api.InitConcurrencyLimit(ctx, path.ExchangeService)
creds, err := acct.M365Config() creds, err := acct.M365Config()
if err != nil { if err != nil {
@ -45,5 +60,10 @@ func makeAC(
return api.Client{}, clues.WrapWC(ctx, err, "constructing api client") return api.Client{}, clues.WrapWC(ctx, err, "constructing api client")
} }
// run a test to ensure credentials work for the client
if err := cli.Access().GetToken(ctx); err != nil {
return api.Client{}, clues.Wrap(err, "checking client connection")
}
return cli, nil return cli, nil
} }

View File

@ -0,0 +1,61 @@
package m365
import (
"testing"
"github.com/alcionai/clues"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/internal/tester/tconfig"
"github.com/alcionai/corso/src/pkg/account"
)
type M365IntgSuite struct {
tester.Suite
}
func TestM365IntgSuite(t *testing.T) {
suite.Run(t, &M365IntgSuite{
Suite: tester.NewIntegrationSuite(
t,
[][]string{}),
})
}
func (suite *userIntegrationSuite) TestNewM365Client() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
_, err := NewM365Client(ctx, tconfig.NewM365Account(t))
assert.NoError(t, err, clues.ToCore(err))
}
func (suite *userIntegrationSuite) TestNewM365Client_invalidCredentials() {
table := []struct {
name string
acct func(t *testing.T) account.Account
}{
{
name: "Invalid Credentials",
acct: func(t *testing.T) account.Account {
return tconfig.NewFakeM365Account(t)
},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
_, err := NewM365Client(ctx, test.acct(t))
assert.Error(t, err, clues.ToCore(err))
})
}
}

View File

@ -10,10 +10,8 @@ import (
"github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/common/str" "github.com/alcionai/corso/src/internal/common/str"
"github.com/alcionai/corso/src/internal/common/tform" "github.com/alcionai/corso/src/internal/common/tform"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/logger" "github.com/alcionai/corso/src/pkg/logger"
"github.com/alcionai/corso/src/pkg/path"
"github.com/alcionai/corso/src/pkg/services/m365/api" "github.com/alcionai/corso/src/pkg/services/m365/api"
"github.com/alcionai/corso/src/pkg/services/m365/api/graph" "github.com/alcionai/corso/src/pkg/services/m365/api/graph"
) )
@ -52,21 +50,15 @@ type Site struct {
} }
// SiteByID retrieves a specific site. // SiteByID retrieves a specific site.
func SiteByID( func (c client) SiteByID(
ctx context.Context, ctx context.Context,
acct account.Account,
id string, id string,
) (*Site, error) { ) (*Site, error) {
ac, err := makeAC(ctx, acct, path.SharePointService)
if err != nil {
return nil, clues.Stack(err)
}
cc := api.CallConfig{ cc := api.CallConfig{
Expand: []string{"drive"}, Expand: []string{"drive"},
} }
return getSiteByID(ctx, ac.Sites(), id, cc) return getSiteByID(ctx, c.ac.Sites(), id, cc)
} }
func getSiteByID( func getSiteByID(
@ -84,13 +76,8 @@ func getSiteByID(
} }
// Sites returns a list of Sites in a specified M365 tenant // Sites returns a list of Sites in a specified M365 tenant
func Sites(ctx context.Context, acct account.Account, errs *fault.Bus) ([]*Site, error) { func (c client) Sites(ctx context.Context, errs *fault.Bus) ([]*Site, error) {
ac, err := makeAC(ctx, acct, path.SharePointService) return getAllSites(ctx, c.ac.Sites())
if err != nil {
return nil, clues.Stack(err)
}
return getAllSites(ctx, ac.Sites())
} }
func getAllSites( func getAllSites(
@ -174,12 +161,11 @@ func ParseSite(ctx context.Context, item models.Siteable) *Site {
// SitesMap retrieves all sites in the tenant, and returns two maps: one id-to-webURL, // SitesMap retrieves all sites in the tenant, and returns two maps: one id-to-webURL,
// and one webURL-to-id. // and one webURL-to-id.
func SitesMap( func (c client) SitesMap(
ctx context.Context, ctx context.Context,
acct account.Account,
errs *fault.Bus, errs *fault.Bus,
) (idname.Cacher, error) { ) (idname.Cacher, error) {
sites, err := Sites(ctx, acct, errs) sites, err := c.Sites(ctx, errs)
if err != nil { if err != nil {
return idname.NewCache(nil), err return idname.NewCache(nil), err
} }

View File

@ -14,8 +14,6 @@ import (
"github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/internal/tester/tconfig" "github.com/alcionai/corso/src/internal/tester/tconfig"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/credentials"
"github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/services/m365/api" "github.com/alcionai/corso/src/pkg/services/m365/api"
"github.com/alcionai/corso/src/pkg/services/m365/api/graph" "github.com/alcionai/corso/src/pkg/services/m365/api/graph"
@ -24,6 +22,7 @@ import (
type siteIntegrationSuite struct { type siteIntegrationSuite struct {
tester.Suite tester.Suite
cli client
} }
func TestSiteIntegrationSuite(t *testing.T) { func TestSiteIntegrationSuite(t *testing.T) {
@ -35,10 +34,18 @@ func TestSiteIntegrationSuite(t *testing.T) {
} }
func (suite *siteIntegrationSuite) SetupSuite() { func (suite *siteIntegrationSuite) SetupSuite() {
ctx, flush := tester.NewContext(suite.T()) t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush() defer flush()
graph.InitializeConcurrencyLimiter(ctx, true, 4) acct := tconfig.NewM365Account(t)
var err error
// will init the concurrency limiter
suite.cli, err = NewM365Client(ctx, acct)
require.NoError(t, err, clues.ToCore(err))
} }
func (suite *siteIntegrationSuite) TestSites() { func (suite *siteIntegrationSuite) TestSites() {
@ -47,9 +54,7 @@ func (suite *siteIntegrationSuite) TestSites() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
acct := tconfig.NewM365Account(t) sites, err := suite.cli.Sites(ctx, fault.New(true))
sites, err := Sites(ctx, acct, fault.New(true))
assert.NoError(t, err, clues.ToCore(err)) assert.NoError(t, err, clues.ToCore(err))
assert.NotEmpty(t, sites) assert.NotEmpty(t, sites)
@ -68,16 +73,14 @@ func (suite *siteIntegrationSuite) TestSites_GetByID() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
acct := tconfig.NewM365Account(t) sites, err := suite.cli.Sites(ctx, fault.New(true))
sites, err := Sites(ctx, acct, fault.New(true))
assert.NoError(t, err, clues.ToCore(err)) assert.NoError(t, err, clues.ToCore(err))
assert.NotEmpty(t, sites) assert.NotEmpty(t, sites)
for _, s := range sites { for _, s := range sites {
suite.Run("site_"+s.ID, func() { suite.Run("site_"+s.ID, func() {
t := suite.T() t := suite.T()
site, err := SiteByID(ctx, acct, s.ID) site, err := suite.cli.SiteByID(ctx, s.ID)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
assert.NotEmpty(t, site.WebURL) assert.NotEmpty(t, site.WebURL)
assert.NotEmpty(t, site.ID) assert.NotEmpty(t, site.ID)
@ -86,52 +89,6 @@ func (suite *siteIntegrationSuite) TestSites_GetByID() {
} }
} }
func (suite *siteIntegrationSuite) TestSites_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
},
},
{
name: "Empty Credentials",
acct: func(t *testing.T) account.Account {
// intentionally swallowing the error here
a, _ := account.NewAccount(account.ProviderM365)
return a
},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
sites, err := Sites(ctx, test.acct(t), fault.New(true))
assert.Empty(t, sites, "returned some sites")
assert.NotNil(t, err)
})
}
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Unit // Unit
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -10,9 +10,7 @@ import (
"github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/m365/service/exchange" "github.com/alcionai/corso/src/internal/m365/service/exchange"
"github.com/alcionai/corso/src/internal/m365/service/onedrive" "github.com/alcionai/corso/src/internal/m365/service/onedrive"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/path"
"github.com/alcionai/corso/src/pkg/services/m365/api" "github.com/alcionai/corso/src/pkg/services/m365/api"
) )
@ -27,10 +25,10 @@ type UserNoInfo struct {
// UsersCompatNoInfo returns a list of users in the specified M365 tenant. // UsersCompatNoInfo returns a list of users in the specified M365 tenant.
// TODO(pandeyabs): Rename this to Users now that `Info` support has been removed. Would // TODO(pandeyabs): Rename this to Users now that `Info` support has been removed. Would
// need corresponding changes in SDK consumers. // need corresponding changes in SDK consumers.
func UsersCompatNoInfo(ctx context.Context, acct account.Account) ([]*UserNoInfo, error) { func (c client) UsersCompatNoInfo(ctx context.Context) ([]*UserNoInfo, error) {
errs := fault.New(true) errs := fault.New(true)
us, err := usersNoInfo(ctx, acct, errs) us, err := usersNoInfo(ctx, c.ac, errs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -40,46 +38,29 @@ func UsersCompatNoInfo(ctx context.Context, acct account.Account) ([]*UserNoInfo
// UserHasMailbox returns true if the user has an exchange mailbox enabled // UserHasMailbox returns true if the user has an exchange mailbox enabled
// false otherwise, and a nil pointer and an error in case of error // false otherwise, and a nil pointer and an error in case of error
func UserHasMailbox(ctx context.Context, acct account.Account, userID string) (bool, error) { func (c client) UserHasMailbox(ctx context.Context, userID string) (bool, error) {
ac, err := makeAC(ctx, acct, path.ExchangeService) return exchange.IsServiceEnabled(ctx, c.ac.Users(), userID)
if err != nil {
return false, clues.Stack(err)
}
return exchange.IsServiceEnabled(ctx, ac.Users(), userID)
} }
func UserGetMailboxInfo( func (c client) UserGetMailboxInfo(
ctx context.Context, ctx context.Context,
acct account.Account,
userID string, userID string,
) (api.MailboxInfo, error) { ) (api.MailboxInfo, error) {
ac, err := makeAC(ctx, acct, path.ExchangeService) return exchange.GetMailboxInfo(ctx, c.ac.Users(), userID)
if err != nil {
return api.MailboxInfo{}, clues.Stack(err)
}
return exchange.GetMailboxInfo(ctx, ac.Users(), userID)
} }
// UserHasDrives returns true if the user has any drives // UserHasDrives returns true if the user has any drives
// false otherwise, and a nil pointer and an error in case of error // false otherwise, and a nil pointer and an error in case of error
func UserHasDrives(ctx context.Context, acct account.Account, userID string) (bool, error) { func (c client) UserHasDrives(ctx context.Context, userID string) (bool, error) {
ac, err := makeAC(ctx, acct, path.OneDriveService) return onedrive.IsServiceEnabled(ctx, c.ac.Users(), userID)
if err != nil {
return false, clues.Stack(err)
}
return onedrive.IsServiceEnabled(ctx, ac.Users(), userID)
} }
// usersNoInfo returns a list of users in the specified M365 tenant - with no info // usersNoInfo returns a list of users in the specified M365 tenant - with no info
func usersNoInfo(ctx context.Context, acct account.Account, errs *fault.Bus) ([]*UserNoInfo, error) { func usersNoInfo(
ac, err := makeAC(ctx, acct, path.UnknownService) ctx context.Context,
if err != nil { ac api.Client,
return nil, clues.Stack(err) errs *fault.Bus,
} ) ([]*UserNoInfo, error) {
us, err := ac.Users().GetAll(ctx, errs) us, err := ac.Users().GetAll(ctx, errs)
if err != nil { if err != nil {
return nil, err return nil, err
@ -105,13 +86,8 @@ func usersNoInfo(ctx context.Context, acct account.Account, errs *fault.Bus) ([]
return ret, nil return ret, nil
} }
func UserAssignedLicenses(ctx context.Context, acct account.Account, userID string) (int, error) { func (c client) UserAssignedLicenses(ctx context.Context, userID string) (int, error) {
ac, err := makeAC(ctx, acct, path.UnknownService) us, err := c.ac.Users().GetByID(
if err != nil {
return 0, clues.Stack(err)
}
us, err := ac.Users().GetByID(
ctx, ctx,
userID, userID,
api.CallConfig{Select: api.SelectProps("assignedLicenses")}) api.CallConfig{Select: api.SelectProps("assignedLicenses")})

View File

@ -11,15 +11,12 @@ import (
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/internal/tester/tconfig" "github.com/alcionai/corso/src/internal/tester/tconfig"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/credentials"
"github.com/alcionai/corso/src/pkg/services/m365/api" "github.com/alcionai/corso/src/pkg/services/m365/api"
"github.com/alcionai/corso/src/pkg/services/m365/api/graph"
) )
type userIntegrationSuite struct { type userIntegrationSuite struct {
tester.Suite tester.Suite
acct account.Account cli client
} }
func TestUserIntegrationSuite(t *testing.T) { func TestUserIntegrationSuite(t *testing.T) {
@ -31,12 +28,18 @@ func TestUserIntegrationSuite(t *testing.T) {
} }
func (suite *userIntegrationSuite) SetupSuite() { func (suite *userIntegrationSuite) SetupSuite() {
ctx, flush := tester.NewContext(suite.T()) t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush() defer flush()
graph.InitializeConcurrencyLimiter(ctx, true, 4) acct := tconfig.NewM365Account(t)
suite.acct = tconfig.NewM365Account(suite.T()) var err error
// will init the concurrency limiter
suite.cli, err = NewM365Client(ctx, acct)
require.NoError(t, err, clues.ToCore(err))
} }
func (suite *userIntegrationSuite) TestUsersCompat_HasNoInfo() { func (suite *userIntegrationSuite) TestUsersCompat_HasNoInfo() {
@ -45,11 +48,7 @@ func (suite *userIntegrationSuite) TestUsersCompat_HasNoInfo() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
graph.InitializeConcurrencyLimiter(ctx, true, 4) users, err := suite.cli.UsersCompatNoInfo(ctx)
acct := tconfig.NewM365Account(suite.T())
users, err := UsersCompatNoInfo(ctx, acct)
assert.NoError(t, err, clues.ToCore(err)) assert.NoError(t, err, clues.ToCore(err))
assert.NotEmpty(t, users) assert.NotEmpty(t, users)
@ -66,7 +65,6 @@ func (suite *userIntegrationSuite) TestUsersCompat_HasNoInfo() {
func (suite *userIntegrationSuite) TestUserHasMailbox() { func (suite *userIntegrationSuite) TestUserHasMailbox() {
t := suite.T() t := suite.T()
acct := tconfig.NewM365Account(t)
userID := tconfig.M365UserID(t) userID := tconfig.M365UserID(t)
table := []struct { table := []struct {
@ -92,7 +90,7 @@ func (suite *userIntegrationSuite) TestUserHasMailbox() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
enabled, err := UserHasMailbox(ctx, acct, test.user) enabled, err := suite.cli.UserHasMailbox(ctx, test.user)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, test.expect, enabled) assert.Equal(t, test.expect, enabled)
}) })
@ -101,7 +99,6 @@ func (suite *userIntegrationSuite) TestUserHasMailbox() {
func (suite *userIntegrationSuite) TestUserHasDrive() { func (suite *userIntegrationSuite) TestUserHasDrive() {
t := suite.T() t := suite.T()
acct := tconfig.NewM365Account(t)
userID := tconfig.M365UserID(t) userID := tconfig.M365UserID(t)
table := []struct { table := []struct {
@ -130,7 +127,7 @@ func (suite *userIntegrationSuite) TestUserHasDrive() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
enabled, err := UserHasDrives(ctx, acct, test.user) enabled, err := suite.cli.UserHasDrives(ctx, test.user)
test.expectErr(t, err, clues.ToCore(err)) test.expectErr(t, err, clues.ToCore(err))
assert.Equal(t, test.expect, enabled) assert.Equal(t, test.expect, enabled)
}) })
@ -139,7 +136,6 @@ func (suite *userIntegrationSuite) TestUserHasDrive() {
func (suite *userIntegrationSuite) TestUserGetMailboxInfo() { func (suite *userIntegrationSuite) TestUserGetMailboxInfo() {
t := suite.T() t := suite.T()
acct := tconfig.NewM365Account(t)
userID := tconfig.M365UserID(t) userID := tconfig.M365UserID(t)
table := []struct { table := []struct {
@ -195,55 +191,16 @@ func (suite *userIntegrationSuite) TestUserGetMailboxInfo() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
info, err := UserGetMailboxInfo(ctx, acct, test.user) info, err := suite.cli.UserGetMailboxInfo(ctx, test.user)
test.expectErr(t, err, clues.ToCore(err)) test.expectErr(t, err, clues.ToCore(err))
test.expect(t, info) test.expect(t, info)
}) })
} }
} }
func (suite *userIntegrationSuite) TestUsers_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()
users, err := UsersCompatNoInfo(ctx, test.acct(t))
assert.Empty(t, users, "returned some users")
assert.NotNil(t, err)
})
}
}
func (suite *userIntegrationSuite) TestUserAssignedLicenses() { func (suite *userIntegrationSuite) TestUserAssignedLicenses() {
t := suite.T() t := suite.T()
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
graph.InitializeConcurrencyLimiter(ctx, true, 4)
defer flush() defer flush()
@ -275,10 +232,7 @@ func (suite *userIntegrationSuite) TestUserAssignedLicenses() {
for _, run := range runs { for _, run := range runs {
t.Run(run.name, func(t *testing.T) { t.Run(run.name, func(t *testing.T) {
user, err := UserAssignedLicenses( user, err := suite.cli.UserAssignedLicenses(ctx, run.userID)
ctx,
suite.acct,
run.userID)
run.expectErr(t, err, clues.ToCore(err)) run.expectErr(t, err, clues.ToCore(err))
assert.Equal(t, run.expect, user) assert.Equal(t, run.expect, user)
}) })