lookup contact folder as fallback (#1174)

## Description

In the event that a user only has a primary contact
folder, and no subfolders, the contact folder legacy
iter needs to fall back to checking for the contacts
default folder in an isolated query, because that
folder isn't provided as part of the contacts folders
get request.

## Type of change

- [x] 🐛 Bugfix

## Issue(s)

* #1113

## Test Plan

- [x] 💪 Manual
- [x] 💚 E2E
This commit is contained in:
Keepers 2022-10-17 18:17:13 -06:00 committed by GitHub
parent 0963bbe364
commit 48cb751ee0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 90 additions and 36 deletions

View File

@ -291,6 +291,10 @@ func (suite *ExchangeServiceSuite) TestGraphQueryFunctions() {
name: "GraphQuery: Get All ContactFolders",
function: GetAllContactFolderNamesForUser,
},
{
name: "GraphQuery: Get Default ContactFolder",
function: GetDefaultContactFolderForUser,
},
{
name: "GraphQuery: Get All Events for User",
function: GetAllEventsForUser,

View File

@ -123,6 +123,12 @@ func (suite *ExchangeIteratorSuite) TestIterativeFunctions() {
iterativeFunction: IterateFilterContainersForCollections,
scope: contactScope[0],
transformer: models.CreateContactFolderCollectionResponseFromDiscriminatorValue,
}, {
name: "Default Contacts Folder",
queryFunction: GetDefaultContactFolderForUser,
iterativeFunction: IterateSelectAllContactsForCollections,
scope: contactScope[0],
transformer: models.CreateContactFolderCollectionResponseFromDiscriminatorValue,
},
}
for _, test := range tests {

View File

@ -6,8 +6,9 @@ import (
msuser "github.com/microsoftgraph/msgraph-sdk-go/users"
mscalendars "github.com/microsoftgraph/msgraph-sdk-go/users/item/calendars"
mscontactfolder "github.com/microsoftgraph/msgraph-sdk-go/users/item/contactfolders"
mscontactbyid "github.com/microsoftgraph/msgraph-sdk-go/users/item/contactfolders/item"
mscontactfolderitem "github.com/microsoftgraph/msgraph-sdk-go/users/item/contactfolders/item/contacts"
mscontactfolderitem "github.com/microsoftgraph/msgraph-sdk-go/users/item/contactfolders/item"
mscontactfolderchild "github.com/microsoftgraph/msgraph-sdk-go/users/item/contactfolders/item/childfolders"
mscontactfolderitemcontact "github.com/microsoftgraph/msgraph-sdk-go/users/item/contactfolders/item/contacts"
mscontacts "github.com/microsoftgraph/msgraph-sdk-go/users/item/contacts"
msevents "github.com/microsoftgraph/msgraph-sdk-go/users/item/events"
msfolder "github.com/microsoftgraph/msgraph-sdk-go/users/item/mailfolders"
@ -236,7 +237,7 @@ func optionsForContactFolders(moreOps []string) (
}
func optionsForContactFolderByID(moreOps []string) (
*mscontactbyid.ContactFolderItemRequestBuilderGetRequestConfiguration,
*mscontactfolderitem.ContactFolderItemRequestBuilderGetRequestConfiguration,
error,
) {
selecting, err := buildOptions(moreOps, folders)
@ -244,10 +245,10 @@ func optionsForContactFolderByID(moreOps []string) (
return nil, err
}
requestParameters := &mscontactbyid.ContactFolderItemRequestBuilderGetQueryParameters{
requestParameters := &mscontactfolderitem.ContactFolderItemRequestBuilderGetQueryParameters{
Select: selecting,
}
options := &mscontactbyid.ContactFolderItemRequestBuilderGetRequestConfiguration{
options := &mscontactfolderitem.ContactFolderItemRequestBuilderGetRequestConfiguration{
QueryParameters: requestParameters,
}
@ -298,16 +299,16 @@ func optionsForMailFoldersItem(
// TODO: Remove after Issue #828; requires updating msgraph to v0.34
func optionsForContactFoldersItem(
moreOps []string,
) (*mscontactfolderitem.ContactsRequestBuilderGetRequestConfiguration, error) {
) (*mscontactfolderitemcontact.ContactsRequestBuilderGetRequestConfiguration, error) {
selecting, err := buildOptions(moreOps, contacts)
if err != nil {
return nil, err
}
requestParameters := &mscontactfolderitem.ContactsRequestBuilderGetQueryParameters{
requestParameters := &mscontactfolderitemcontact.ContactsRequestBuilderGetQueryParameters{
Select: selecting,
}
options := &mscontactfolderitem.ContactsRequestBuilderGetRequestConfiguration{
options := &mscontactfolderitemcontact.ContactsRequestBuilderGetRequestConfiguration{
QueryParameters: requestParameters,
}
@ -332,6 +333,25 @@ func optionsForEvents(moreOps []string) (*msevents.EventsRequestBuilderGetReques
return options, nil
}
// optionsForContactChildFolders builds a contacts child folders request.
func optionsForContactChildFolders(
moreOps []string,
) (*mscontactfolderchild.ChildFoldersRequestBuilderGetRequestConfiguration, error) {
selecting, err := buildOptions(moreOps, contacts)
if err != nil {
return nil, err
}
requestParameters := &mscontactfolderchild.ChildFoldersRequestBuilderGetQueryParameters{
Select: selecting,
}
options := &mscontactfolderchild.ChildFoldersRequestBuilderGetRequestConfiguration{
QueryParameters: requestParameters,
}
return options, nil
}
// optionsForContacts transforms options into select query for MailContacts
// @return is the first call in Contacts().GetWithRequestConfigurationAndResponseHandler(options, handler)
func optionsForContacts(moreOps []string) (*mscontacts.ContactsRequestBuilderGetRequestConfiguration, error) {

View File

@ -346,10 +346,12 @@ func GetContainerID(
}
// SetupExchangeCollectionVars is a helper function returns a sets
// Exchange.Type specific functions based on scope
// Exchange.Type specific functions based on scope.
// The []GraphQuery slice provides fallback queries in the event that
// initial queries provide zero results.
func SetupExchangeCollectionVars(scope selectors.ExchangeScope) (
absser.ParsableFactory,
GraphQuery,
[]GraphQuery,
GraphIterateFunc,
error,
) {
@ -359,14 +361,14 @@ func SetupExchangeCollectionVars(scope selectors.ExchangeScope) (
if scope.IncludesCategory(selectors.ExchangeContact) {
return models.CreateContactFolderCollectionResponseFromDiscriminatorValue,
GetAllContactFolderNamesForUser,
[]GraphQuery{GetAllContactFolderNamesForUser, GetDefaultContactFolderForUser},
IterateSelectAllContactsForCollections,
nil
}
if scope.IncludesCategory(selectors.ExchangeEvent) {
return models.CreateCalendarCollectionResponseFromDiscriminatorValue,
GetAllCalendarNamesForUser,
[]GraphQuery{GetAllCalendarNamesForUser},
IterateSelectAllEventsFromCalendars,
nil
}

View File

@ -51,9 +51,25 @@ func GetAllCalendarNamesForUser(ctx context.Context, gs graph.Service, user stri
return gs.Client().UsersById(user).Calendars().Get(ctx, options)
}
// GetDefaultContactFolderForUser is a GraphQuery function for getting the ContactFolderId
// and display names for the default "Contacts" folder.
// Only returns the default Contact Folder
func GetDefaultContactFolderForUser(ctx context.Context, gs graph.Service, user string) (absser.Parsable, error) {
options, err := optionsForContactChildFolders([]string{"displayName", "parentFolderId"})
if err != nil {
return nil, err
}
return gs.Client().
UsersById(user).
ContactFoldersById(rootFolderAlias).
ChildFolders().
Get(ctx, options)
}
// GetAllContactFolderNamesForUser is a GraphQuery function for getting ContactFolderId
// and display names for contacts. All other information is omitted.
// Does not return the primary Contact Folder
// Does not return the default Contact Folder
func GetAllContactFolderNamesForUser(ctx context.Context, gs graph.Service, user string) (absser.Parsable, error) {
options, err := optionsForContactFolders([]string{"displayName", "parentFolderId"})
if err != nil {

View File

@ -334,40 +334,46 @@ func (gc *GraphConnector) legacyFetchItems(
resolver graph.ContainerResolver,
) (map[string]*exchange.Collection, error) {
var (
errs error
errs error
errUpdater = func(id string, err error) {
errs = support.WrapAndAppend(id, err, errs)
}
collections = map[string]*exchange.Collection{}
)
transformer, query, gIter, err := exchange.SetupExchangeCollectionVars(scope)
transformer, queries, gIter, err := exchange.SetupExchangeCollectionVars(scope)
if err != nil {
return nil, support.WrapAndAppend(gc.Service().Adapter().GetBaseUrl(), err, nil)
}
response, err := query(ctx, &gc.graphService, qp.User)
if err != nil {
return nil, errors.Wrapf(
err,
"user %s M365 query: %s",
qp.User, support.ConnectorStackErrorTrace(err))
}
// queries is assumed to provide fallbacks in case of empty results. Any
// non-zero collection production will break out of the loop.
for _, query := range queries {
response, err := query(ctx, &gc.graphService, qp.User)
if err != nil {
return nil, errors.Wrapf(
err,
"user %s M365 query: %s",
qp.User, support.ConnectorStackErrorTrace(err))
}
pageIterator, err := msgraphgocore.NewPageIterator(response, &gc.graphService.adapter, transformer)
if err != nil {
return nil, err
}
pageIterator, err := msgraphgocore.NewPageIterator(response, &gc.graphService.adapter, transformer)
if err != nil {
return nil, err
}
errUpdater := func(id string, err error) {
errs = support.WrapAndAppend(id, err, errs)
}
// callbackFunc iterates through all M365 object target and fills exchange.Collection.jobs[]
// with corresponding item M365IDs. New collections are created for each directory.
// Each directory used the M365 Identifier. The use of ID stops collisions betweens users
callbackFunc := gIter(ctx, qp, errUpdater, collections, gc.UpdateStatus, resolver)
// callbackFunc iterates through all M365 object target and fills exchange.Collection.jobs[]
// with corresponding item M365IDs. New collections are created for each directory.
// Each directory used the M365 Identifier. The use of ID stops collisions betweens users
callbackFunc := gIter(ctx, qp, errUpdater, collections, gc.UpdateStatus, resolver)
iterateError := pageIterator.Iterate(ctx, callbackFunc)
if err := pageIterator.Iterate(ctx, callbackFunc); err != nil {
return nil, support.WrapAndAppend(gc.graphService.adapter.GetBaseUrl(), err, errs)
}
if iterateError != nil {
errs = support.WrapAndAppend(gc.graphService.adapter.GetBaseUrl(), iterateError, errs)
if len(collections) > 0 {
break
}
}
return collections, errs