adds tenant site lookup on new graph conn (#1540)

## Description

Adds a lookup for all tenant site ids when a new
graph connector is created.  Also adds an enum
to flag which resource set (users, sites, or all)
that the connector should initialize.

## Type of change

- [x] 🌻 Feature

## Issue(s)

* #1506

## Test Plan

- [x] 💚 E2E
This commit is contained in:
Keepers 2022-11-17 17:31:41 -07:00 committed by GitHub
parent 9fe9cd1ce6
commit 07a8d13d1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 250 additions and 175 deletions

View File

@ -176,7 +176,7 @@ func getGCAndVerifyUser(ctx context.Context, userID string) (*connector.GraphCon
}
// build a graph connector
gc, err := connector.NewGraphConnector(ctx, acct)
gc, err := connector.NewGraphConnector(ctx, acct, connector.Users)
if err != nil {
return nil, "", errors.Wrap(err, "connecting to graph api")
}

View File

@ -44,6 +44,7 @@ var (
// Supports:
// - exchange (contacts, email, and events)
// Input: go run ./getItem.go --user <user>
//
// --m365ID <m365ID> --category <oneof: contacts, email, events>
func main() {
ctx, _ := logger.SeedLevel(context.Background(), logger.Development)
@ -170,7 +171,7 @@ func getGC(ctx context.Context) (*connector.GraphConnector, error) {
return nil, Only(ctx, errors.Wrap(err, "finding m365 account details"))
}
gc, err := connector.NewGraphConnector(ctx, acct)
gc, err := connector.NewGraphConnector(ctx, acct, connector.Users)
if err != nil {
return nil, Only(ctx, errors.Wrap(err, "connecting to graph API"))
}

View File

@ -255,7 +255,7 @@ func getGC(ctx context.Context) (*connector.GraphConnector, error) {
}
// build a graph connector
gc, err := connector.NewGraphConnector(ctx, acct)
gc, err := connector.NewGraphConnector(ctx, acct, connector.Users)
if err != nil {
return nil, Only(ctx, errors.Wrap(err, "connecting to graph api"))
}

View File

@ -42,7 +42,7 @@ func (suite *ConnectorDataCollectionIntegrationSuite) SetupSuite() {
_, err := tester.GetRequiredEnvVars(tester.M365AcctCredEnvs...)
require.NoError(suite.T(), err)
suite.connector = loadConnector(ctx, suite.T())
suite.connector = loadConnector(ctx, suite.T(), AllResources)
suite.user = tester.M365UserID(suite.T())
suite.site = tester.M365SiteID(suite.T())
tester.LogTimeOfTest(suite.T())
@ -58,7 +58,7 @@ func (suite *ConnectorDataCollectionIntegrationSuite) TestExchangeDataCollection
ctx, flush := tester.NewContext()
defer flush()
connector := loadConnector(ctx, suite.T())
connector := loadConnector(ctx, suite.T(), Users)
tests := []struct {
name string
getSelector func(t *testing.T) selectors.Selector
@ -123,7 +123,7 @@ func (suite *ConnectorDataCollectionIntegrationSuite) TestInvalidUserForDataColl
defer flush()
invalidUser := "foo@example.com"
connector := loadConnector(ctx, suite.T())
connector := loadConnector(ctx, suite.T(), Users)
tests := []struct {
name string
getSelector func(t *testing.T) selectors.Selector
@ -162,7 +162,7 @@ func (suite *ConnectorDataCollectionIntegrationSuite) TestSharePointDataCollecti
ctx, flush := tester.NewContext()
defer flush()
connector := loadConnector(ctx, suite.T())
connector := loadConnector(ctx, suite.T(), Users)
tests := []struct {
name string
getSelector func(t *testing.T) selectors.Selector
@ -230,7 +230,7 @@ func (suite *ConnectorCreateExchangeCollectionIntegrationSuite) SetupSuite() {
_, err := tester.GetRequiredEnvVars(tester.M365AcctCredEnvs...)
require.NoError(suite.T(), err)
suite.connector = loadConnector(ctx, suite.T())
suite.connector = loadConnector(ctx, suite.T(), Users)
suite.user = tester.M365UserID(suite.T())
suite.site = tester.M365SiteID(suite.T())
tester.LogTimeOfTest(suite.T())
@ -263,7 +263,7 @@ func (suite *ConnectorCreateExchangeCollectionIntegrationSuite) TestMailFetch()
},
}
gc := loadConnector(ctx, t)
gc := loadConnector(ctx, t, Users)
for _, test := range tests {
suite.T().Run(test.name, func(t *testing.T) {
@ -292,7 +292,7 @@ func (suite *ConnectorCreateExchangeCollectionIntegrationSuite) TestMailSerializ
defer flush()
t := suite.T()
connector := loadConnector(ctx, t)
connector := loadConnector(ctx, t, Users)
sel := selectors.NewExchangeBackup()
sel.Include(sel.MailFolders([]string{suite.user}, []string{exchange.DefaultMailFolder}, selectors.PrefixMatch()))
collection, err := connector.createExchangeCollections(ctx, sel.Scopes()[0])
@ -326,7 +326,7 @@ func (suite *ConnectorCreateExchangeCollectionIntegrationSuite) TestContactSeria
ctx, flush := tester.NewContext()
defer flush()
connector := loadConnector(ctx, suite.T())
connector := loadConnector(ctx, suite.T(), Users)
tests := []struct {
name string
@ -379,7 +379,7 @@ func (suite *ConnectorCreateExchangeCollectionIntegrationSuite) TestEventsSerial
ctx, flush := tester.NewContext()
defer flush()
connector := loadConnector(ctx, suite.T())
connector := loadConnector(ctx, suite.T(), Users)
tests := []struct {
name, expected string
@ -447,7 +447,7 @@ func (suite *ConnectorCreateExchangeCollectionIntegrationSuite) TestAccessOfInbo
defer flush()
t := suite.T()
connector := loadConnector(ctx, t)
connector := loadConnector(ctx, t, Users)
sel := selectors.NewExchangeBackup()
sel.Include(sel.MailFolders(selectors.Any(), []string{exchange.DefaultMailFolder}, selectors.PrefixMatch()))
scopes := sel.DiscreteScopes(connector.GetUsers())
@ -488,7 +488,7 @@ func (suite *ConnectorCreateSharePointCollectionIntegrationSuite) SetupSuite() {
_, err := tester.GetRequiredEnvVars(tester.M365AcctCredEnvs...)
require.NoError(suite.T(), err)
suite.connector = loadConnector(ctx, suite.T())
suite.connector = loadConnector(ctx, suite.T(), Sites)
suite.user = tester.M365UserID(suite.T())
tester.LogTimeOfTest(suite.T())
}
@ -502,7 +502,7 @@ func (suite *ConnectorCreateSharePointCollectionIntegrationSuite) TestCreateShar
userID = tester.M365UserID(t)
)
gc := loadConnector(ctx, t)
gc := loadConnector(ctx, t, Sites)
scope := selectors.NewSharePointBackup().Folders(
[]string{userID},
[]string{"foo"},

View File

@ -39,9 +39,9 @@ func checkRequiredValues(c graph.Container) error {
return nil
}
//======================================
// =========================================
// cachedContainer Implementations
//======================
// =========================================
var _ graph.CachedContainer = &cacheFolder{}
@ -52,7 +52,7 @@ type cacheFolder struct {
// =========================================
// Required Functions to satisfy interfaces
//=====================================
// =========================================
func (cf cacheFolder) Path() *path.Builder {
return cf.p
@ -79,6 +79,7 @@ func (c CalendarDisplayable) GetDisplayName() *string {
// GetParentFolderId returns the default calendar name address
// EventCalendars have a flat hierarchy and Calendars are rooted
// at the default
//
//nolint:revive
func (c CalendarDisplayable) GetParentFolderId() *string {
return nil

View File

@ -5,6 +5,7 @@ import (
"testing"
"time"
absser "github.com/microsoft/kiota-abstractions-go/serialization"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
@ -253,7 +254,9 @@ func (suite *ExchangeServiceSuite) TestGraphQueryFunctions() {
},
{
name: "GraphQuery: Get All Users",
function: GetAllUsersForTenant,
function: func(ctx context.Context, gs graph.Service, toss string) (absser.Parsable, error) {
return GetAllUsersForTenant(ctx, gs)
},
},
{
name: "GraphQuery: Get All ContactFolders",

View File

@ -6,6 +6,7 @@ package exchange
// Legacy Value Tags and constants are used to override certain values within
// M365 objects.
// Master Property Value Document:
//
// https://interoperability.blob.core.windows.net/files/MS-OXPROPS/%5bMS-OXPROPS%5d.pdf
const (
// MailRestorePropertyTag inhibits exchange.Mail.Message from being "resent" through the server.
@ -29,7 +30,7 @@ const (
// ----------------------------------
// Default Folder Names
//------------------------
// ----------------------------------
// Mail Definitions: https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0
// inbox and root
@ -38,9 +39,9 @@ const (
DefaultContactFolder = "Contacts"
DefaultCalendar = "Calendar"
//---------------------
// ----------------------------------
// Paging
//-----------------
// ----------------------------------
// nextDataLink definition https://docs.microsoft.com/en-us/graph/paging
nextDataLink = "@odata.nextLink"
)

View File

@ -25,7 +25,7 @@ import (
// Constant Section
// Defines the allowable strings that can be passed into
// selectors for M365 objects
//------------------------------------------------------------
// -----------------------------------------------------------------------
var (
fieldsForCalendars = map[string]int{
"changeKey": 1,
@ -118,12 +118,12 @@ func CategoryToOptionIdentifier(category path.CategoryType) optionIdentifier {
}
}
//---------------------------------------------------------------------------
// -----------------------------------------------------------------------
// exchange.Query Option Section
// These functions can be used to filter a response on M365
// Graph queries and reduce / filter the amount of data returned
// which reduces the overall latency of complex calls
//----------------------------------------------------------------
// -----------------------------------------------------------------------
func optionsForFolderMessages(moreOps []string) (*msmfmessage.MessagesRequestBuilderGetRequestConfiguration, error) {
selecting, err := buildOptions(moreOps, messages)

View File

@ -23,9 +23,10 @@ type exchangeService struct {
credentials account.M365Config
}
// /------------------------------------------------------------
// ------------------------------------------------------------
// Functions to comply with graph.Service Interface
// -------------------------------------------------------
// ------------------------------------------------------------
func (es *exchangeService) Client() *msgraphsdk.GraphServiceClient {
return &es.client
}

View File

@ -74,9 +74,8 @@ func GetAllContactFolderNamesForUser(ctx context.Context, gs graph.Service, user
return gs.Client().UsersById(user).ContactFolders().Get(ctx, options)
}
// GetAllUsersForTenant is a GraphQuery for retrieving all the UserCollectionResponse with
// that contains the UserID and email for each user. All other information is omitted
func GetAllUsersForTenant(ctx context.Context, gs graph.Service, user string) (absser.Parsable, error) {
// GetAllUsersForTenant makes a GraphQuery request retrieving all the users in the tenant.
func GetAllUsersForTenant(ctx context.Context, gs graph.Service) (absser.Parsable, error) {
selecting := []string{"userPrincipalName"}
options, err := optionsForUsers(selecting)

View File

@ -86,6 +86,7 @@ func (c CalendarDisplayable) GetDisplayName() *string {
// GetParentFolderId returns the default calendar name address
// EventCalendars have a flat hierarchy and Calendars are rooted
// at the default
//
//nolint:revive
func (c CalendarDisplayable) GetParentFolderId() *string {
return &c.parentID

View File

@ -4,10 +4,11 @@ package connector
import (
"context"
"fmt"
"runtime/trace"
"strings"
"sync"
"github.com/microsoft/kiota-abstractions-go/serialization"
msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go"
msgraphgocore "github.com/microsoftgraph/msgraph-sdk-go-core"
"github.com/microsoftgraph/msgraph-sdk-go/models"
@ -16,6 +17,7 @@ import (
"github.com/alcionai/corso/src/internal/connector/exchange"
"github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/internal/connector/onedrive"
"github.com/alcionai/corso/src/internal/connector/sharepoint"
"github.com/alcionai/corso/src/internal/connector/support"
"github.com/alcionai/corso/src/internal/data"
D "github.com/alcionai/corso/src/internal/diagnostics"
@ -73,7 +75,16 @@ func (gs graphService) ErrPolicy() bool {
return gs.failFast
}
func NewGraphConnector(ctx context.Context, acct account.Account) (*GraphConnector, error) {
type resource int
const (
UnknownResource resource = iota
AllResources
Users
Sites
)
func NewGraphConnector(ctx context.Context, acct account.Account, r resource) (*GraphConnector, error) {
m365, err := acct.M365Config()
if err != nil {
return nil, errors.Wrap(err, "retrieving m365 account configuration")
@ -93,16 +104,17 @@ func NewGraphConnector(ctx context.Context, acct account.Account) (*GraphConnect
gc.graphService = *aService
err = gc.setTenantUsers(ctx)
if err != nil {
if r == AllResources || r == Users {
if err = gc.setTenantUsers(ctx); err != nil {
return nil, errors.Wrap(err, "retrieving tenant user list")
}
}
// TODO: users or sites, one or the other, not both.
err = gc.setTenantSites(ctx)
if err != nil {
if r == AllResources || r == Sites {
if err = gc.setTenantSites(ctx); err != nil {
return nil, errors.Wrap(err, "retrieveing tenant site list")
}
}
return &gc, nil
}
@ -133,61 +145,43 @@ func (gs *graphService) EnableFailFast() {
// setTenantUsers queries the M365 to identify the users in the
// workspace. The users field is updated during this method
// iff the return value is nil
// iff the returned error is nil
func (gc *GraphConnector) setTenantUsers(ctx context.Context) error {
ctx, end := D.Span(ctx, "gc:setTenantUsers")
defer end()
response, err := exchange.GetAllUsersForTenant(ctx, gc.graphService, "")
if err != nil {
return errors.Wrapf(
err,
"tenant %s M365 query: %s",
users, err := getResources(
ctx,
gc.graphService,
gc.tenant,
support.ConnectorStackErrorTrace(err),
)
}
userIterator, err := msgraphgocore.NewPageIterator(
response,
&gc.graphService.adapter,
exchange.GetAllUsersForTenant,
models.CreateUserCollectionResponseFromDiscriminatorValue,
identifyUser,
)
if err != nil {
return errors.Wrap(err, support.ConnectorStackErrorTrace(err))
}
callbackFunc := func(userItem interface{}) bool {
user, ok := userItem.(models.Userable)
if !ok {
err = support.WrapAndAppend(gc.graphService.adapter.GetBaseUrl(), errors.New("received non-User on iteration"), err)
return true
}
if user.GetUserPrincipalName() == nil {
err = support.WrapAndAppend(
gc.graphService.adapter.GetBaseUrl(),
fmt.Errorf("no email address for User: %s", *user.GetId()),
err,
)
return true
}
// *user.GetId() is populated for every M365 entityable object by M365 backstore
gc.Users[*user.GetUserPrincipalName()] = *user.GetId()
return true
}
iterateError := userIterator.Iterate(ctx, callbackFunc)
if iterateError != nil {
err = support.WrapAndAppend(gc.graphService.adapter.GetBaseUrl(), iterateError, err)
}
return err
}
gc.Users = users
return nil
}
// Transforms an interface{} into a key,value pair representing
// userPrincipalName:userID.
func identifyUser(item any) (string, string, error) {
m, ok := item.(models.Userable)
if !ok {
return "", "", errors.New("iteration retrieved non-User item")
}
if m.GetUserPrincipalName() == nil {
return "", "", errors.Errorf("no principal name for User: %s", *m.GetId())
}
return *m.GetUserPrincipalName(), *m.GetId(), nil
}
// GetUsers returns the email address of users within tenant.
func (gc *GraphConnector) GetUsers() []string {
return buildFromMap(true, gc.Users)
@ -199,68 +193,53 @@ func (gc *GraphConnector) GetUsersIds() []string {
}
// setTenantSites queries the M365 to identify the sites in the
// workspace. The sitets field is updated during this method
// iff the return value is nil
// workspace. The sites field is updated during this method
// iff the returned error is nil.
func (gc *GraphConnector) setTenantSites(ctx context.Context) error {
// TODO
gc.Sites = map[string]string{}
// ctx, end := D.Span(ctx, "gc:setTenantSites")
// defer end()
ctx, end := D.Span(ctx, "gc:setTenantSites")
defer end()
// response, err := exchange.GetAllUsersForTenant(ctx, gc.graphService, "")
// if err != nil {
// return errors.Wrapf(
// err,
// "tenant %s M365 query: %s",
// gc.tenant,
// support.ConnectorStackErrorTrace(err),
// )
// }
sites, err := getResources(
ctx,
gc.graphService,
gc.tenant,
sharepoint.GetAllSitesForTenant,
models.CreateSiteCollectionResponseFromDiscriminatorValue,
identifySite,
)
if err != nil {
return err
}
// userIterator, err := msgraphgocore.NewPageIterator(
// response,
// &gc.graphService.adapter,
// models.CreateUserCollectionResponseFromDiscriminatorValue,
// )
// if err != nil {
// return errors.Wrap(err, support.ConnectorStackErrorTrace(err))
// }
// callbackFunc := func(userItem interface{}) bool {
// user, ok := userItem.(models.Userable)
// if !ok {
// err = support.WrapAndAppend(gc.graphService.adapter.GetBaseUrl(),
// errors.New("received non-User on iteration"), err)
// return true
// }
// if user.GetUserPrincipalName() == nil {
// err = support.WrapAndAppend(
// gc.graphService.adapter.GetBaseUrl(),
// fmt.Errorf("no email address for User: %s", *user.GetId()),
// err,
// )
// return true
// }
// // *user.GetId() is populated for every M365 entityable object by M365 backstore
// gc.Users[*user.GetUserPrincipalName()] = *user.GetId()
// return true
// }
// iterateError := userIterator.Iterate(ctx, callbackFunc)
// if iterateError != nil {
// err = support.WrapAndAppend(gc.graphService.adapter.GetBaseUrl(), iterateError, err)
// }
// return err
gc.Sites = sites
return nil
}
var errKnownSkippableCase = errors.New("case is known and skippable")
// Transforms an interface{} into a key,value pair representing
// siteName:siteID.
func identifySite(item any) (string, string, error) {
m, ok := item.(models.Siteable)
if !ok {
return "", "", errors.New("iteration retrieved non-Site item")
}
if m.GetName() == nil {
// the built-in site at "htps://{tenant-domain}/search" never has a name.
if m.GetWebUrl() != nil && strings.HasSuffix(*m.GetWebUrl(), "/search") {
return "", "", errKnownSkippableCase
}
return "", "", errors.Errorf("no name for Site: %s", *m.GetId())
}
return *m.GetName(), *m.GetId(), nil
}
// GetSites returns the siteIDs of sharepoint sites within tenant.
func (gc *GraphConnector) GetSites() []string {
return buildFromMap(true, gc.Sites)
@ -365,6 +344,57 @@ func (gc *GraphConnector) incrementAwaitingMessages() {
// Helper Funcs
// ---------------------------------------------------------------------------
func getResources(
ctx context.Context,
gs graph.Service,
tenantID string,
query func(context.Context, graph.Service) (serialization.Parsable, error),
parser func(parseNode serialization.ParseNode) (serialization.Parsable, error),
identify func(any) (string, string, error),
) (map[string]string, error) {
resources := map[string]string{}
response, err := query(ctx, gs)
if err != nil {
return nil, errors.Wrapf(
err,
"retrieving resources for tenant %s: %s",
tenantID,
support.ConnectorStackErrorTrace(err),
)
}
iter, err := msgraphgocore.NewPageIterator(response, gs.Adapter(), parser)
if err != nil {
return nil, errors.Wrap(err, support.ConnectorStackErrorTrace(err))
}
var iterErrs error
callbackFunc := func(item any) bool {
k, v, err := identify(item)
if err != nil {
if errors.Is(err, errKnownSkippableCase) {
return true
}
iterErrs = support.WrapAndAppend(gs.Adapter().GetBaseUrl(), err, iterErrs)
return true
}
resources[k] = v
return true
}
if err := iter.Iterate(ctx, callbackFunc); err != nil {
return nil, errors.Wrap(err, support.ConnectorStackErrorTrace(err))
}
return resources, iterErrs
}
// IsRecoverableError returns true iff error is a RecoverableGCEerror
func IsRecoverableError(e error) bool {
var recoverable support.RecoverableGCError

View File

@ -65,7 +65,7 @@ func (suite *DisconnectedGraphConnectorSuite) TestBadConnection() {
for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) {
gc, err := NewGraphConnector(ctx, test.acct(t))
gc, err := NewGraphConnector(ctx, test.acct(t), Users)
assert.Nil(t, gc, test.name+" failed")
assert.NotNil(t, err, test.name+"failed")
})

View File

@ -162,6 +162,7 @@ type restoreBackupInfo struct {
name string
service path.ServiceType
collections []colInfo
resource resource
}
func attachmentEqual(
@ -858,15 +859,16 @@ func getSelectorWith(service path.ServiceType) selectors.Selector {
case path.OneDriveService:
s = selectors.ServiceOneDrive
}
// TODO: ^ sharepoint
return selectors.Selector{
Service: s,
}
}
func loadConnector(ctx context.Context, t *testing.T) *GraphConnector {
func loadConnector(ctx context.Context, t *testing.T, r resource) *GraphConnector {
a := tester.NewM365Account(t)
connector, err := NewGraphConnector(ctx, a)
connector, err := NewGraphConnector(ctx, a, r)
require.NoError(t, err)
return connector

View File

@ -39,7 +39,7 @@ func (suite *GraphConnectorIntegrationSuite) SetupSuite() {
_, err := tester.GetRequiredEnvVars(tester.M365AcctCredEnvs...)
require.NoError(suite.T(), err)
suite.connector = loadConnector(ctx, suite.T())
suite.connector = loadConnector(ctx, suite.T(), Users)
suite.user = tester.M365UserID(suite.T())
tester.LogTimeOfTest(suite.T())
}
@ -63,8 +63,8 @@ func (suite *GraphConnectorIntegrationSuite) TestSetTenantUsers() {
suite.Equal(len(newConnector.Users), 0)
err = newConnector.setTenantUsers(ctx)
assert.NoError(suite.T(), err)
suite.Greater(len(newConnector.Users), 0)
suite.NoError(err)
suite.Less(0, len(newConnector.Users))
}
// TestSetTenantUsers verifies GraphConnector's ability to query
@ -86,10 +86,8 @@ func (suite *GraphConnectorIntegrationSuite) TestSetTenantSites() {
suite.Equal(0, len(newConnector.Sites))
err = newConnector.setTenantSites(ctx)
assert.NoError(suite.T(), err)
// TODO: should be non-zero once implemented.
// suite.Greater(len(newConnector.Users), 0)
suite.Equal(0, len(newConnector.Sites))
suite.NoError(err)
suite.Less(0, len(newConnector.Sites))
}
func (suite *GraphConnectorIntegrationSuite) TestEmptyCollections() {
@ -194,7 +192,7 @@ func runRestoreBackupTest(
start := time.Now()
restoreGC := loadConnector(ctx, t)
restoreGC := loadConnector(ctx, t, test.resource)
restoreSel := getSelectorWith(test.service)
deets, err := restoreGC.RestoreDataCollections(ctx, restoreSel, dest, collections)
require.NoError(t, err)
@ -228,7 +226,7 @@ func runRestoreBackupTest(
})
}
backupGC := loadConnector(ctx, t)
backupGC := loadConnector(ctx, t, test.resource)
backupSel := backupSelectorForExpected(t, test.service, expectedDests)
t.Logf("Selective backup of %s\n", backupSel)
@ -255,6 +253,7 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() {
{
name: "EmailsWithAttachments",
service: path.ExchangeService,
resource: Users,
collections: []colInfo{
{
pathElements: []string{"Inbox"},
@ -281,6 +280,7 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() {
{
name: "MultipleEmailsMultipleFolders",
service: path.ExchangeService,
resource: Users,
collections: []colInfo{
{
pathElements: []string{"Inbox"},
@ -326,6 +326,7 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() {
{
name: "MultipleContactsSingleFolder",
service: path.ExchangeService,
resource: Users,
collections: []colInfo{
{
pathElements: []string{"Contacts"},
@ -353,6 +354,7 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() {
{
name: "MultipleContactsMutlipleFolders",
service: path.ExchangeService,
resource: Users,
collections: []colInfo{
{
pathElements: []string{"Work"},
@ -477,6 +479,7 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames
{
name: "Contacts",
service: path.ExchangeService,
resource: Users,
collections: []colInfo{
{
pathElements: []string{"Work"},
@ -574,7 +577,7 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames
dest.ContainerName,
)
restoreGC := loadConnector(ctx, t)
restoreGC := loadConnector(ctx, t, test.resource)
deets, err := restoreGC.RestoreDataCollections(ctx, restoreSel, dest, collections)
require.NoError(t, err)
require.NotNil(t, deets)
@ -592,7 +595,7 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames
// Run a backup and compare its output with what we put in.
backupGC := loadConnector(ctx, t)
backupGC := loadConnector(ctx, t, test.resource)
backupSel := backupSelectorForExpected(t, test.service, expectedDests)
t.Log("Selective backup of", backupSel)
@ -624,6 +627,7 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiuserRestoreAndBackup() {
{
name: "Email",
service: path.ExchangeService,
resource: Users,
collections: []colInfo{
{
pathElements: []string{"Inbox"},
@ -660,6 +664,7 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiuserRestoreAndBackup() {
{
name: "Contacts",
service: path.ExchangeService,
resource: Users,
collections: []colInfo{
{
pathElements: []string{"Work"},

View File

@ -0,0 +1,21 @@
package sharepoint
import (
"context"
absser "github.com/microsoft/kiota-abstractions-go/serialization"
mssite "github.com/microsoftgraph/msgraph-sdk-go/sites"
"github.com/alcionai/corso/src/internal/connector/graph"
)
// GetAllSitesForTenant makes a GraphQuery request retrieving all sites in the tenant.
func GetAllSitesForTenant(ctx context.Context, gs graph.Service) (absser.Parsable, error) {
options := &mssite.SitesRequestBuilderGetRequestConfiguration{
QueryParameters: &mssite.SitesRequestBuilderGetQueryParameters{
Select: []string{"id", "name", "weburl"},
},
}
return gs.Client().Sites().Get(ctx, options)
}

View File

@ -406,9 +406,9 @@ func SetAdditionalDataToEventMessage(
// ToEventSimplified transforms an event to simplifed restore format
// To overcome some of the MS Graph API challenges, the event object is modified in the following ways:
// * Instead of adding attendees and generating spurious notifications,
// - Instead of adding attendees and generating spurious notifications,
// add a summary of attendees at the beginning to the event before the original body content
// * event.attendees is set to an empty list
// - event.attendees is set to an empty list
func ToEventSimplified(orig models.Eventable) models.Eventable {
attendees := FormatAttendees(orig, *orig.GetBody().GetContentType() == models.HTML_BODYTYPE)
orig.SetAttendees([]models.Attendeeable{})

View File

@ -128,7 +128,12 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) {
defer close(complete)
// retrieve data from the producer
gc, err := connector.NewGraphConnector(ctx, op.account)
resource := connector.Users
if op.Selectors.Service == selectors.ServiceSharePoint {
resource = connector.Sites
}
gc, err := connector.NewGraphConnector(ctx, op.account, resource)
if err != nil {
err = errors.Wrap(err, "connecting to graph api")
opStats.readErr = err

View File

@ -166,7 +166,12 @@ func (op *RestoreOperation) Run(ctx context.Context) (restoreDetails *details.De
defer close(gcComplete)
// restore those collections using graph
gc, err := connector.NewGraphConnector(ctx, op.account)
resource := connector.Users
if op.Selectors.Service == selectors.ServiceSharePoint {
resource = connector.Sites
}
gc, err := connector.NewGraphConnector(ctx, op.account, resource)
if err != nil {
err = errors.Wrap(err, "connecting to microsoft servers")
opStats.writeErr = err

View File

@ -12,7 +12,7 @@ import (
// Users returns a list of users in the specified M365 tenant
// TODO: Implement paging support
func Users(ctx context.Context, m365Account account.Account) ([]string, error) {
gc, err := connector.NewGraphConnector(ctx, m365Account)
gc, err := connector.NewGraphConnector(ctx, m365Account, connector.Users)
if err != nil {
return nil, errors.Wrap(err, "could not initialize M365 graph connection")
}
@ -23,7 +23,7 @@ func Users(ctx context.Context, m365Account account.Account) ([]string, error) {
// UserIDs returns a list of user IDs for the specified M365 tenant
// TODO: Implement paging support
func UserIDs(ctx context.Context, m365Account account.Account) ([]string, error) {
gc, err := connector.NewGraphConnector(ctx, m365Account)
gc, err := connector.NewGraphConnector(ctx, m365Account, connector.Users)
if err != nil {
return nil, errors.Wrap(err, "could not initialize M365 graph connection")
}
@ -32,7 +32,7 @@ func UserIDs(ctx context.Context, m365Account account.Account) ([]string, error)
}
func GetEmailAndUserID(ctx context.Context, m365Account account.Account) (map[string]string, error) {
gc, err := connector.NewGraphConnector(ctx, m365Account)
gc, err := connector.NewGraphConnector(ctx, m365Account, connector.Users)
if err != nil {
return nil, errors.Wrap(err, "could not initialize M365 graph connection")
}