transition api methods to interfaces (#2010)

## Description

replaces the new api client methods with interfaces, to prepare for testing funcions with mocks instead of integration.

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

- [x]  No 

## Type of change

- [x] 🤖 Test

## Issue(s)

* #1967

## Test Plan

- [x]  Unit test
- [x] 💚 E2E
This commit is contained in:
Keepers 2023-01-05 14:03:35 -07:00 committed by GitHub
parent 2e92d10777
commit 70d5a5ab56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 213 additions and 130 deletions

View File

@ -87,6 +87,18 @@ func newService(creds account.M365Config) (*graph.Service, error) {
return graph.NewService(adapter), nil
}
func (c Client) Contacts() Contacts {
return Contacts{c}
}
func (c Client) Events() Events {
return Events{c}
}
func (c Client) Mail() Mail {
return Mail{c}
}
// ---------------------------------------------------------------------------
// helper funcs
// ---------------------------------------------------------------------------

View File

@ -174,11 +174,11 @@ func (suite *ExchangeServiceSuite) TestGraphQueryFunctions() {
}{
{
name: "GraphQuery: Get All ContactFolders",
function: c.GetAllContactFolderNamesForUser,
function: c.Contacts().GetAllContactFolderNamesForUser,
},
{
name: "GraphQuery: Get All Calendars for User",
function: c.GetAllCalendarNamesForUser,
function: c.Events().GetAllCalendarNamesForUser,
},
}

View File

@ -13,9 +13,21 @@ import (
"github.com/alcionai/corso/src/internal/connector/support"
)
// ---------------------------------------------------------------------------
// controller
// ---------------------------------------------------------------------------
type Contacts struct {
Client
}
// ---------------------------------------------------------------------------
// methods
// ---------------------------------------------------------------------------
// CreateContactFolder makes a contact folder with the displayName of folderName.
// If successful, returns the created folder object.
func (c Client) CreateContactFolder(
func (c Contacts) CreateContactFolder(
ctx context.Context,
user, folderName string,
) (models.ContactFolderable, error) {
@ -28,7 +40,7 @@ func (c Client) CreateContactFolder(
// DeleteContactFolder deletes the ContactFolder associated with the M365 ID if permissions are valid.
// Errors returned if the function call was not successful.
func (c Client) DeleteContactFolder(
func (c Contacts) DeleteContactFolder(
ctx context.Context,
user, folderID string,
) error {
@ -36,7 +48,7 @@ func (c Client) DeleteContactFolder(
}
// RetrieveContactDataForUser is a GraphRetrievalFun that returns all associated fields.
func (c Client) RetrieveContactDataForUser(
func (c Contacts) RetrieveContactDataForUser(
ctx context.Context,
user, m365ID string,
) (serialization.Parsable, error) {
@ -46,7 +58,7 @@ func (c Client) RetrieveContactDataForUser(
// GetAllContactFolderNamesForUser is a GraphQuery function for getting
// ContactFolderId and display names for contacts. All other information is omitted.
// Does not return the default Contact Folder
func (c Client) GetAllContactFolderNamesForUser(
func (c Contacts) GetAllContactFolderNamesForUser(
ctx context.Context,
user string,
) (serialization.Parsable, error) {
@ -58,16 +70,13 @@ func (c Client) GetAllContactFolderNamesForUser(
return c.stable.Client().UsersById(user).ContactFolders().Get(ctx, options)
}
func (c Client) GetContactFolderByID(
func (c Contacts) GetContainerByID(
ctx context.Context,
userID, dirID string,
optionalFields ...string,
) (models.ContactFolderable, error) {
fields := append([]string{"displayName", "parentFolderId"}, optionalFields...)
ofcf, err := optionsForContactFolderByID(fields)
) (graph.Container, error) {
ofcf, err := optionsForContactFolderByID([]string{"displayName", "parentFolderId"})
if err != nil {
return nil, errors.Wrapf(err, "options for contact folder: %v", fields)
return nil, errors.Wrap(err, "options for contact folder")
}
return c.stable.Client().
@ -76,13 +85,13 @@ func (c Client) GetContactFolderByID(
Get(ctx, ofcf)
}
// EnumerateContactsFolders iterates through all of the users current
// EnumerateContainers iterates through all of the users current
// contacts folders, converting each to a graph.CacheFolder, and calling
// fn(cf) on each one. If fn(cf) errors, the error is aggregated
// into a multierror that gets returned to the caller.
// Folder hierarchy is represented in its current state, and does
// not contain historical data.
func (c Client) EnumerateContactsFolders(
func (c Contacts) EnumerateContainers(
ctx context.Context,
userID, baseDirID string,
fn func(graph.CacheFolder) error,
@ -138,9 +147,7 @@ func (c Client) EnumerateContactsFolders(
return errs.ErrorOrNil()
}
// FetchContactIDsFromDirectory function that returns a list of all the m365IDs of the contacts
// of the targeted directory
func (c Client) FetchContactIDsFromDirectory(
func (c Contacts) GetAddedAndRemovedItemIDs(
ctx context.Context,
user, directoryID, oldDelta string,
) ([]string, []string, DeltaUpdate, error) {

View File

@ -14,9 +14,21 @@ import (
"github.com/alcionai/corso/src/pkg/path"
)
// ---------------------------------------------------------------------------
// controller
// ---------------------------------------------------------------------------
type Events struct {
Client
}
// ---------------------------------------------------------------------------
// methods
// ---------------------------------------------------------------------------
// CreateCalendar makes an event Calendar with the name in the user's M365 exchange account
// Reference: https://docs.microsoft.com/en-us/graph/api/user-post-calendars?view=graph-rest-1.0&tabs=go
func (c Client) CreateCalendar(
func (c Events) CreateCalendar(
ctx context.Context,
user, calendarName string,
) (models.Calendarable, error) {
@ -28,7 +40,7 @@ func (c Client) CreateCalendar(
// DeleteCalendar removes calendar from user's M365 account
// Reference: https://docs.microsoft.com/en-us/graph/api/calendar-delete?view=graph-rest-1.0&tabs=go
func (c Client) DeleteCalendar(
func (c Events) DeleteCalendar(
ctx context.Context,
user, calendarID string,
) error {
@ -36,7 +48,7 @@ func (c Client) DeleteCalendar(
}
// RetrieveEventDataForUser is a GraphRetrievalFunc that returns event data.
func (c Client) RetrieveEventDataForUser(
func (c Events) RetrieveEventDataForUser(
ctx context.Context,
user, m365ID string,
) (serialization.Parsable, error) {
@ -55,15 +67,15 @@ func (c Client) GetAllCalendarNamesForUser(
return c.stable.Client().UsersById(user).Calendars().Get(ctx, options)
}
// EnumerateCalendars iterates through all of the users current
// contacts folders, converting each to a graph.CacheFolder, and
// EnumerateContainers iterates through all of the users current
// calendars, converting each to a graph.CacheFolder, and
// calling fn(cf) on each one. If fn(cf) errors, the error is
// aggregated into a multierror that gets returned to the caller.
// Folder hierarchy is represented in its current state, and does
// not contain historical data.
func (c Client) EnumerateCalendars(
func (c Events) EnumerateContainers(
ctx context.Context,
userID string,
userID, baseDirID string,
fn func(graph.CacheFolder) error,
) error {
service, err := c.service()
@ -112,8 +124,7 @@ func (c Client) EnumerateCalendars(
return errs.ErrorOrNil()
}
// FetchEventIDsFromCalendar returns a list of all M365IDs of events of the targeted Calendar.
func (c Client) FetchEventIDsFromCalendar(
func (c Events) GetAddedAndRemovedItemIDs(
ctx context.Context,
user, calendarID, oldDelta string,
) ([]string, []string, DeltaUpdate, error) {

View File

@ -13,9 +13,21 @@ import (
"github.com/alcionai/corso/src/internal/connector/support"
)
// ---------------------------------------------------------------------------
// controller
// ---------------------------------------------------------------------------
type Mail struct {
Client
}
// ---------------------------------------------------------------------------
// methods
// ---------------------------------------------------------------------------
// CreateMailFolder makes a mail folder iff a folder of the same name does not exist
// Reference: https://docs.microsoft.com/en-us/graph/api/user-post-mailfolders?view=graph-rest-1.0&tabs=http
func (c Client) CreateMailFolder(
func (c Mail) CreateMailFolder(
ctx context.Context,
user, folder string,
) (models.MailFolderable, error) {
@ -27,7 +39,7 @@ func (c Client) CreateMailFolder(
return c.stable.Client().UsersById(user).MailFolders().Post(ctx, requestBody, nil)
}
func (c Client) CreateMailFolderWithParent(
func (c Mail) CreateMailFolderWithParent(
ctx context.Context,
user, folder, parentID string,
) (models.MailFolderable, error) {
@ -51,30 +63,47 @@ func (c Client) CreateMailFolderWithParent(
// DeleteMailFolder removes a mail folder with the corresponding M365 ID from the user's M365 Exchange account
// Reference: https://docs.microsoft.com/en-us/graph/api/mailfolder-delete?view=graph-rest-1.0&tabs=http
func (c Client) DeleteMailFolder(
func (c Mail) DeleteMailFolder(
ctx context.Context,
user, folderID string,
) error {
return c.stable.Client().UsersById(user).MailFoldersById(folderID).Delete(ctx, nil)
}
func (c Mail) GetContainerByID(
ctx context.Context,
userID, dirID string,
) (graph.Container, error) {
service, err := c.service()
if err != nil {
return nil, err
}
ofmf, err := optionsForMailFoldersItem([]string{"displayName", "parentFolderId"})
if err != nil {
return nil, errors.Wrap(err, "options for mail folder")
}
return service.Client().UsersById(userID).MailFoldersById(dirID).Get(ctx, ofmf)
}
// RetrieveMessageDataForUser is a GraphRetrievalFunc that returns message data.
func (c Client) RetrieveMessageDataForUser(
func (c Mail) RetrieveMessageDataForUser(
ctx context.Context,
user, m365ID string,
) (serialization.Parsable, error) {
return c.stable.Client().UsersById(user).MessagesById(m365ID).Get(ctx, nil)
}
// EnumeratetMailFolders iterates through all of the users current
// EnumerateContainers iterates through all of the users current
// mail folders, converting each to a graph.CacheFolder, and calling
// fn(cf) on each one. If fn(cf) errors, the error is aggregated
// into a multierror that gets returned to the caller.
// Folder hierarchy is represented in its current state, and does
// not contain historical data.
func (c Client) EnumerateMailFolders(
func (c Mail) EnumerateContainers(
ctx context.Context,
userID string,
userID, baseDirID string,
fn func(graph.CacheFolder) error,
) error {
service, err := c.service()
@ -116,22 +145,7 @@ func (c Client) EnumerateMailFolders(
return errs.ErrorOrNil()
}
func (c Client) GetMailFolderByID(
ctx context.Context,
userID, dirID string,
optionalFields ...string,
) (models.MailFolderable, error) {
ofmf, err := optionsForMailFoldersItem(optionalFields)
if err != nil {
return nil, errors.Wrapf(err, "options for mail folder: %v", optionalFields)
}
return c.stable.Client().UsersById(userID).MailFoldersById(dirID).Get(ctx, ofmf)
}
// FetchMessageIDsFromDirectory function that returns a list of all the m365IDs of the exchange.Mail
// of the targeted directory
func (c Client) FetchMessageIDsFromDirectory(
func (c Mail) GetAddedAndRemovedItemIDs(
ctx context.Context,
user, directoryID, oldDelta string,
) ([]string, []string, DeltaUpdate, error) {

View File

@ -5,7 +5,6 @@ import (
"github.com/pkg/errors"
"github.com/alcionai/corso/src/internal/connector/exchange/api"
"github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/internal/connector/support"
"github.com/alcionai/corso/src/pkg/path"
@ -15,7 +14,8 @@ var _ graph.ContainerResolver = &contactFolderCache{}
type contactFolderCache struct {
*containerResolver
ac api.Client
enumer containersEnumerator
getter containerGetter
userID string
}
@ -24,7 +24,7 @@ func (cfc *contactFolderCache) populateContactRoot(
directoryID string,
baseContainerPath []string,
) error {
f, err := cfc.ac.GetContactFolderByID(ctx, cfc.userID, directoryID)
f, err := cfc.getter.GetContainerByID(ctx, cfc.userID, directoryID)
if err != nil {
return errors.Wrapf(
err,
@ -53,7 +53,7 @@ func (cfc *contactFolderCache) Populate(
return err
}
err := cfc.ac.EnumerateContactsFolders(ctx, cfc.userID, baseID, cfc.addFolder)
err := cfc.enumer.EnumerateContainers(ctx, cfc.userID, baseID, cfc.addFolder)
if err != nil {
return err
}

View File

@ -10,6 +10,29 @@ import (
"github.com/alcionai/corso/src/pkg/path"
)
// ---------------------------------------------------------------------------
// common interfaces
// ---------------------------------------------------------------------------
type containerGetter interface {
GetContainerByID(
ctx context.Context,
userID, dirID string,
) (graph.Container, error)
}
type containersEnumerator interface {
EnumerateContainers(
ctx context.Context,
userID, baseDirID string,
fn func(graph.CacheFolder) error,
) error
}
// ---------------------------------------------------------------------------
// controller
// ---------------------------------------------------------------------------
// Exchange has a limit of 300 for folder depth. OneDrive has a limit on path
// length of 400 characters (including separators) which would be roughly 200
// folders if each folder is only a single character.

View File

@ -8,6 +8,7 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"github.com/alcionai/corso/src/internal/connector/exchange/api"
"github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/internal/connector/support"
"github.com/alcionai/corso/src/internal/data"
@ -205,12 +206,25 @@ func DataCollections(
return collections, errs
}
func getterByType(ac api.Client, category path.CategoryType) (addedAndRemovedItemIDsGetter, error) {
switch category {
case path.EmailCategory:
return ac.Mail(), nil
case path.EventsCategory:
return ac.Events(), nil
case path.ContactsCategory:
return ac.Contacts(), nil
default:
return nil, fmt.Errorf("category %s not supported by getFetchIDFunc", category)
}
}
// createCollections - utility function that retrieves M365
// IDs through Microsoft Graph API. The selectors.ExchangeScope
// determines the type of collections that are retrieved.
func createCollections(
ctx context.Context,
acct account.M365Config,
creds account.M365Config,
user string,
scope selectors.ExchangeScope,
dps DeltaPaths,
@ -220,15 +234,22 @@ func createCollections(
var (
errs *multierror.Error
allCollections = make([]data.Collection, 0)
ac = api.Client{Credentials: creds}
category = scope.Category().PathType()
)
getter, err := getterByType(ac, category)
if err != nil {
return nil, err
}
// Create collection of ExchangeDataCollection
collections := make(map[string]data.Collection)
qp := graph.QueryParams{
Category: scope.Category().PathType(),
Category: category,
ResourceOwner: user,
Credentials: acct,
Credentials: creds,
}
foldersComplete, closer := observe.MessageWithCompletion(fmt.Sprintf("∙ %s - %s:", qp.Category, user))
@ -243,6 +264,7 @@ func createCollections(
err = filterContainersAndFillCollections(
ctx,
qp,
getter,
collections,
su,
resolver,

View File

@ -5,7 +5,6 @@ import (
"github.com/pkg/errors"
"github.com/alcionai/corso/src/internal/connector/exchange/api"
"github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/pkg/path"
)
@ -14,7 +13,7 @@ var _ graph.ContainerResolver = &eventCalendarCache{}
type eventCalendarCache struct {
*containerResolver
ac api.Client
enumer containersEnumerator
userID string
}
@ -30,7 +29,7 @@ func (ecc *eventCalendarCache) Populate(
ecc.containerResolver = newContainerResolver()
}
err := ecc.ac.EnumerateCalendars(ctx, ecc.userID, ecc.addFolder)
err := ecc.enumer.EnumerateContainers(ctx, ecc.userID, "", ecc.addFolder)
if err != nil {
return err
}

View File

@ -142,11 +142,11 @@ func (col *Collection) Items() <-chan data.Stream {
func GetQueryAndSerializeFunc(ac api.Client, category path.CategoryType) (api.GraphRetrievalFunc, GraphSerializeFunc) {
switch category {
case path.ContactsCategory:
return ac.RetrieveContactDataForUser, serializeAndStreamContact
return ac.Contacts().RetrieveContactDataForUser, serializeAndStreamContact
case path.EventsCategory:
return ac.RetrieveEventDataForUser, serializeAndStreamEvent
return ac.Events().RetrieveEventDataForUser, serializeAndStreamEvent
case path.EmailCategory:
return ac.RetrieveMessageDataForUser, serializeAndStreamMessage
return ac.Mail().RetrieveMessageDataForUser, serializeAndStreamMessage
// Unsupported options returns nil, nil
default:
return nil, nil

View File

@ -50,14 +50,15 @@ func (suite *CacheResolverSuite) TestPopulate() {
eventFunc := func(t *testing.T) graph.ContainerResolver {
return &eventCalendarCache{
userID: tester.M365UserID(t),
ac: ac,
enumer: ac.Events(),
}
}
contactFunc := func(t *testing.T) graph.ContainerResolver {
return &contactFolderCache{
userID: tester.M365UserID(t),
ac: ac,
enumer: ac.Contacts(),
getter: ac.Contacts(),
}
}

View File

@ -5,7 +5,6 @@ import (
"github.com/pkg/errors"
"github.com/alcionai/corso/src/internal/connector/exchange/api"
"github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/internal/connector/support"
"github.com/alcionai/corso/src/pkg/path"
@ -18,7 +17,8 @@ var _ graph.ContainerResolver = &mailFolderCache{}
// nameLookup map: Key: DisplayName Value: ID
type mailFolderCache struct {
*containerResolver
ac api.Client
enumer containersEnumerator
getter containerGetter
userID string
}
@ -33,7 +33,7 @@ func (mc *mailFolderCache) populateMailRoot(
for _, fldr := range []string{rootFolderAlias, DefaultMailFolder} {
var directory string
f, err := mc.ac.GetMailFolderByID(ctx, mc.userID, fldr, "displayName", "parentFolderId")
f, err := mc.getter.GetContainerByID(ctx, mc.userID, fldr)
if err != nil {
return errors.Wrap(err, "fetching root folder"+support.ConnectorStackErrorTrace(err))
}
@ -65,7 +65,7 @@ func (mc *mailFolderCache) Populate(
return err
}
err := mc.ac.EnumerateMailFolders(ctx, mc.userID, mc.addFolder)
err := mc.enumer.EnumerateContainers(ctx, mc.userID, "", mc.addFolder)
if err != nil {
return err
}

View File

@ -84,9 +84,12 @@ func (suite *MailFolderCacheIntegrationSuite) TestDeltaFetch() {
ac, err := api.NewClient(suite.credentials)
require.NoError(t, err)
acm := ac.Mail()
mfc := mailFolderCache{
userID: userID,
ac: ac,
enumer: acm,
getter: acm,
}
require.NoError(t, mfc.Populate(ctx, test.root, test.path...))

View File

@ -69,14 +69,14 @@ func (suite *ExchangeRestoreSuite) TestRestoreContact() {
folderName = "TestRestoreContact: " + common.FormatSimpleDateTime(now)
)
aFolder, err := suite.ac.CreateContactFolder(ctx, userID, folderName)
aFolder, err := suite.ac.Contacts().CreateContactFolder(ctx, userID, folderName)
require.NoError(t, err)
folderID := *aFolder.GetId()
defer func() {
// Remove the folder containing contact prior to exiting test
err = suite.ac.DeleteContactFolder(ctx, userID, folderID)
err = suite.ac.Contacts().DeleteContactFolder(ctx, userID, folderID)
assert.NoError(t, err)
}()
@ -103,14 +103,14 @@ func (suite *ExchangeRestoreSuite) TestRestoreEvent() {
name = "TestRestoreEvent: " + common.FormatSimpleDateTime(time.Now())
)
calendar, err := suite.ac.CreateCalendar(ctx, userID, name)
calendar, err := suite.ac.Events().CreateCalendar(ctx, userID, name)
require.NoError(t, err)
calendarID := *calendar.GetId()
defer func() {
// Removes calendar containing events created during the test
err = suite.ac.DeleteCalendar(ctx, userID, calendarID)
err = suite.ac.Events().DeleteCalendar(ctx, userID, calendarID)
assert.NoError(t, err)
}()
@ -146,10 +146,10 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() {
name: "Test Mail",
bytes: mockconnector.GetMockMessageBytes("Restore Exchange Object"),
category: path.EmailCategory,
cleanupFunc: suite.ac.DeleteMailFolder,
cleanupFunc: suite.ac.Mail().DeleteMailFolder,
destination: func(t *testing.T, ctx context.Context) string {
folderName := "TestRestoreMailObject: " + common.FormatSimpleDateTime(now)
folder, err := suite.ac.CreateMailFolder(ctx, userID, folderName)
folder, err := suite.ac.Mail().CreateMailFolder(ctx, userID, folderName)
require.NoError(t, err)
return *folder.GetId()
@ -159,10 +159,10 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() {
name: "Test Mail: One Direct Attachment",
bytes: mockconnector.GetMockMessageWithDirectAttachment("Restore 1 Attachment"),
category: path.EmailCategory,
cleanupFunc: suite.ac.DeleteMailFolder,
cleanupFunc: suite.ac.Mail().DeleteMailFolder,
destination: func(t *testing.T, ctx context.Context) string {
folderName := "TestRestoreMailwithAttachment: " + common.FormatSimpleDateTime(now)
folder, err := suite.ac.CreateMailFolder(ctx, userID, folderName)
folder, err := suite.ac.Mail().CreateMailFolder(ctx, userID, folderName)
require.NoError(t, err)
return *folder.GetId()
@ -172,10 +172,10 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() {
name: "Test Mail: One Large Attachment",
bytes: mockconnector.GetMockMessageWithLargeAttachment("Restore Large Attachment"),
category: path.EmailCategory,
cleanupFunc: suite.ac.DeleteMailFolder,
cleanupFunc: suite.ac.Mail().DeleteMailFolder,
destination: func(t *testing.T, ctx context.Context) string {
folderName := "TestRestoreMailwithLargeAttachment: " + common.FormatSimpleDateTime(now)
folder, err := suite.ac.CreateMailFolder(ctx, userID, folderName)
folder, err := suite.ac.Mail().CreateMailFolder(ctx, userID, folderName)
require.NoError(t, err)
return *folder.GetId()
@ -185,10 +185,10 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() {
name: "Test Mail: Two Attachments",
bytes: mockconnector.GetMockMessageWithTwoAttachments("Restore 2 Attachments"),
category: path.EmailCategory,
cleanupFunc: suite.ac.DeleteMailFolder,
cleanupFunc: suite.ac.Mail().DeleteMailFolder,
destination: func(t *testing.T, ctx context.Context) string {
folderName := "TestRestoreMailwithAttachments: " + common.FormatSimpleDateTime(now)
folder, err := suite.ac.CreateMailFolder(ctx, userID, folderName)
folder, err := suite.ac.Mail().CreateMailFolder(ctx, userID, folderName)
require.NoError(t, err)
return *folder.GetId()
@ -198,10 +198,10 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() {
name: "Test Mail: Reference(OneDrive) Attachment",
bytes: mockconnector.GetMessageWithOneDriveAttachment("Restore Reference(OneDrive) Attachment"),
category: path.EmailCategory,
cleanupFunc: suite.ac.DeleteMailFolder,
cleanupFunc: suite.ac.Mail().DeleteMailFolder,
destination: func(t *testing.T, ctx context.Context) string {
folderName := "TestRestoreMailwithReferenceAttachment: " + common.FormatSimpleDateTime(now)
folder, err := suite.ac.CreateMailFolder(ctx, userID, folderName)
folder, err := suite.ac.Mail().CreateMailFolder(ctx, userID, folderName)
require.NoError(t, err)
return *folder.GetId()
@ -212,10 +212,10 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() {
name: "Test Contact",
bytes: mockconnector.GetMockContactBytes("Test_Omega"),
category: path.ContactsCategory,
cleanupFunc: suite.ac.DeleteContactFolder,
cleanupFunc: suite.ac.Contacts().DeleteContactFolder,
destination: func(t *testing.T, ctx context.Context) string {
folderName := "TestRestoreContactObject: " + common.FormatSimpleDateTime(now)
folder, err := suite.ac.CreateContactFolder(ctx, userID, folderName)
folder, err := suite.ac.Contacts().CreateContactFolder(ctx, userID, folderName)
require.NoError(t, err)
return *folder.GetId()
@ -225,10 +225,10 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() {
name: "Test Events",
bytes: mockconnector.GetDefaultMockEventBytes("Restored Event Object"),
category: path.EventsCategory,
cleanupFunc: suite.ac.DeleteCalendar,
cleanupFunc: suite.ac.Events().DeleteCalendar,
destination: func(t *testing.T, ctx context.Context) string {
calendarName := "TestRestoreEventObject: " + common.FormatSimpleDateTime(now)
calendar, err := suite.ac.CreateCalendar(ctx, userID, calendarName)
calendar, err := suite.ac.Events().CreateCalendar(ctx, userID, calendarName)
require.NoError(t, err)
return *calendar.GetId()
@ -238,10 +238,10 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() {
name: "Test Event with Attachment",
bytes: mockconnector.GetMockEventWithAttachment("Restored Event Attachment"),
category: path.EventsCategory,
cleanupFunc: suite.ac.DeleteCalendar,
cleanupFunc: suite.ac.Events().DeleteCalendar,
destination: func(t *testing.T, ctx context.Context) string {
calendarName := "TestRestoreEventObject_" + common.FormatSimpleDateTime(now)
calendar, err := suite.ac.CreateCalendar(ctx, userID, calendarName)
calendar, err := suite.ac.Events().CreateCalendar(ctx, userID, calendarName)
require.NoError(t, err)
return *calendar.GetId()

View File

@ -48,23 +48,27 @@ func PopulateExchangeContainerResolver(
switch qp.Category {
case path.EmailCategory:
acm := ac.Mail()
res = &mailFolderCache{
userID: qp.ResourceOwner,
ac: ac,
getter: acm,
enumer: acm,
}
cacheRoot = rootFolderAlias
case path.ContactsCategory:
acc := ac.Contacts()
res = &contactFolderCache{
userID: qp.ResourceOwner,
ac: ac,
getter: acc,
enumer: acc,
}
cacheRoot = DefaultContactFolder
case path.EventsCategory:
res = &eventCalendarCache{
userID: qp.ResourceOwner,
ac: ac,
enumer: ac.Events(),
}
cacheRoot = DefaultCalendar

View File

@ -2,7 +2,6 @@ package exchange
import (
"context"
"fmt"
"github.com/pkg/errors"
@ -16,6 +15,13 @@ import (
"github.com/alcionai/corso/src/pkg/selectors"
)
type addedAndRemovedItemIDsGetter interface {
GetAddedAndRemovedItemIDs(
ctx context.Context,
user, containerID, oldDeltaToken string,
) ([]string, []string, api.DeltaUpdate, error)
}
// filterContainersAndFillCollections is a utility function
// that places the M365 object ids belonging to specific directories
// into a Collection. Messages outside of those directories are omitted.
@ -24,6 +30,7 @@ import (
func filterContainersAndFillCollections(
ctx context.Context,
qp graph.QueryParams,
getter addedAndRemovedItemIDsGetter,
collections map[string]data.Collection,
statusUpdater support.StatusUpdater,
resolver graph.ContainerResolver,
@ -41,17 +48,14 @@ func filterContainersAndFillCollections(
tombstones = makeTombstones(dps)
)
// TODO(rkeepers): pass in the api client instead of generating it here.
// TODO(rkeepers): this should be passed in from the caller, probably
// as an interface that satisfies the NewCollection requirements.
// But this will work for the short term.
ac, err := api.NewClient(qp.Credentials)
if err != nil {
return err
}
getJobs, err := getFetchIDFunc(ac, qp.Category)
if err != nil {
return support.WrapAndAppend(qp.ResourceOwner, err, errs)
}
for _, c := range resolver.Items() {
if ctrlOpts.FailFast && errs != nil {
return errs
@ -89,7 +93,7 @@ func filterContainersAndFillCollections(
}
}
added, removed, newDelta, err := getJobs(ctx, qp.ResourceOwner, cID, prevDelta)
added, removed, newDelta, err := getter.GetAddedAndRemovedItemIDs(ctx, qp.ResourceOwner, cID, prevDelta)
if err != nil {
if graph.IsErrDeletedInFlight(err) == nil {
errs = support.WrapAndAppend(qp.ResourceOwner, err, errs)
@ -217,24 +221,3 @@ func pathFromPrevString(ps string) (path.Path, error) {
return p, nil
}
// FetchIDFunc collection of helper functions which return a list of all item
// IDs in the given container and a delta token for future requests if the
// container supports fetching delta records.
type FetchIDFunc func(
ctx context.Context,
user, containerID, oldDeltaToken string,
) ([]string, []string, api.DeltaUpdate, error)
func getFetchIDFunc(ac api.Client, category path.CategoryType) (FetchIDFunc, error) {
switch category {
case path.EmailCategory:
return ac.FetchMessageIDsFromDirectory, nil
case path.EventsCategory:
return ac.FetchEventIDsFromCalendar, nil
case path.ContactsCategory:
return ac.FetchContactIDsFromDirectory, nil
default:
return nil, fmt.Errorf("category %s not supported by getFetchIDFunc", category)
}
}

View File

@ -455,9 +455,11 @@ func CreateContainerDestinaion(
switch category {
case path.EmailCategory:
if directoryCache == nil {
acm := ac.Mail()
mfc := &mailFolderCache{
userID: user,
ac: ac,
enumer: acm,
getter: acm,
}
caches[category] = mfc
@ -475,9 +477,11 @@ func CreateContainerDestinaion(
case path.ContactsCategory:
if directoryCache == nil {
acc := ac.Contacts()
cfc := &contactFolderCache{
userID: user,
ac: ac,
enumer: acc,
getter: acc,
}
caches[category] = cfc
newCache = true
@ -496,7 +500,7 @@ func CreateContainerDestinaion(
if directoryCache == nil {
ecc := &eventCalendarCache{
userID: user,
ac: ac,
enumer: ac.Events(),
}
caches[category] = ecc
newCache = true
@ -543,7 +547,7 @@ func establishMailRestoreLocation(
continue
}
temp, err := ac.CreateMailFolderWithParent(ctx, user, folder, folderID)
temp, err := ac.Mail().CreateMailFolderWithParent(ctx, user, folder, folderID)
if err != nil {
// Should only error if cache malfunctions or incorrect parameters
return "", errors.Wrap(err, support.ConnectorStackErrorTrace(err))
@ -590,7 +594,7 @@ func establishContactsRestoreLocation(
return cached, nil
}
temp, err := ac.CreateContactFolder(ctx, user, folders[0])
temp, err := ac.Contacts().CreateContactFolder(ctx, user, folders[0])
if err != nil {
return "", errors.Wrap(err, support.ConnectorStackErrorTrace(err))
}
@ -623,7 +627,7 @@ func establishEventsRestoreLocation(
return cached, nil
}
temp, err := ac.CreateCalendar(ctx, user, folders[0])
temp, err := ac.Events().CreateCalendar(ctx, user, folders[0])
if err != nil {
return "", errors.Wrap(err, support.ConnectorStackErrorTrace(err))
}

View File

@ -942,7 +942,7 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_exchangeIncrementals() {
switch category {
case path.EmailCategory:
ids, _, _, err := ac.FetchMessageIDsFromDirectory(ctx, suite.user, containerID, "")
ids, _, _, err := ac.Mail().GetAddedAndRemovedItemIDs(ctx, suite.user, containerID, "")
require.NoError(t, err, "getting message ids")
require.NotEmpty(t, ids, "message ids in folder")
@ -950,7 +950,7 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_exchangeIncrementals() {
require.NoError(t, err, "deleting email item: %s", support.ConnectorStackErrorTrace(err))
case path.ContactsCategory:
ids, _, _, err := ac.FetchContactIDsFromDirectory(ctx, suite.user, containerID, "")
ids, _, _, err := ac.Contacts().GetAddedAndRemovedItemIDs(ctx, suite.user, containerID, "")
require.NoError(t, err, "getting contact ids")
require.NotEmpty(t, ids, "contact ids in folder")