standardizes the calendar resolver interface (#2162)

## Description

Adds a little extra process into the calendar
resolver so that it mimics the mail and contact
resolvers.  This will allow us to collapse the
three resolvers into a more common handler
or interface.

We can take this change or drop it.  I added
the code in exploration of the event test failures,
and figured I'd throw it out just in case.

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

- [x]  No 

## Type of change

- [x] 🧹 Tech Debt/Cleanup

## Issue(s)

* #2022

## Test Plan

- [x] 💚 E2E
This commit is contained in:
Keepers 2023-01-19 15:30:14 -07:00 committed by GitHub
parent 0852de5d64
commit 8df00bd386
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 108 additions and 59 deletions

View File

@ -58,6 +58,28 @@ func (c Events) DeleteCalendar(
return c.stable.Client().UsersById(user).CalendarsById(calendarID).Delete(ctx, nil) return c.stable.Client().UsersById(user).CalendarsById(calendarID).Delete(ctx, nil)
} }
func (c Events) GetContainerByID(
ctx context.Context,
userID, containerID string,
) (graph.Container, error) {
service, err := c.service()
if err != nil {
return nil, err
}
ofc, err := optionsForCalendarsByID([]string{"name", "owner"})
if err != nil {
return nil, errors.Wrap(err, "options for event calendar")
}
cal, err := service.Client().UsersById(userID).CalendarsById(containerID).Get(ctx, ofc)
if err != nil {
return nil, err
}
return graph.CalendarDisplayable{Calendarable: cal}, nil
}
// GetItem retrieves an Eventable item. // GetItem retrieves an Eventable item.
func (c Events) GetItem( func (c Events) GetItem(
ctx context.Context, ctx context.Context,
@ -183,14 +205,9 @@ func (c Events) GetAddedAndRemovedItemIDs(
errs *multierror.Error errs *multierror.Error
) )
options, err := optionsForEventsByCalendarDelta([]string{"id"})
if err != nil {
return nil, nil, DeltaUpdate{}, err
}
if len(oldDelta) > 0 { if len(oldDelta) > 0 {
builder := users.NewItemCalendarsItemEventsDeltaRequestBuilder(oldDelta, service.Adapter()) builder := users.NewItemCalendarsItemEventsDeltaRequestBuilder(oldDelta, service.Adapter())
pgr := &eventPager{service, builder, options} pgr := &eventPager{service, builder, nil}
added, removed, deltaURL, err := getItemsAddedAndRemovedFromContainer(ctx, pgr) added, removed, deltaURL, err := getItemsAddedAndRemovedFromContainer(ctx, pgr)
// note: happy path, not the error condition // note: happy path, not the error condition
@ -217,7 +234,7 @@ func (c Events) GetAddedAndRemovedItemIDs(
// works as intended (until, at least, we want to _not_ call the beta anymore). // works as intended (until, at least, we want to _not_ call the beta anymore).
rawURL := fmt.Sprintf(eventBetaDeltaURLTemplate, user, calendarID) rawURL := fmt.Sprintf(eventBetaDeltaURLTemplate, user, calendarID)
builder := users.NewItemCalendarsItemEventsDeltaRequestBuilder(rawURL, service.Adapter()) builder := users.NewItemCalendarsItemEventsDeltaRequestBuilder(rawURL, service.Adapter())
pgr := &eventPager{service, builder, options} pgr := &eventPager{service, builder, nil}
added, removed, deltaURL, err := getItemsAddedAndRemovedFromContainer(ctx, pgr) added, removed, deltaURL, err := getItemsAddedAndRemovedFromContainer(ctx, pgr)
if err != nil { if err != nil {

View File

@ -21,18 +21,6 @@ var (
"owner": {}, "owner": {},
} }
fieldsForEvents = map[string]struct{}{
"calendar": {},
"end": {},
"id": {},
"isOnlineMeeting": {},
"isReminderOn": {},
"responseStatus": {},
"responseRequested": {},
"showAs": {},
"subject": {},
}
fieldsForFolders = map[string]struct{}{ fieldsForFolders = map[string]struct{}{
"childFolderCount": {}, "childFolderCount": {},
"displayName": {}, "displayName": {},
@ -112,6 +100,28 @@ func optionsForCalendars(moreOps []string) (
return options, nil return options, nil
} }
// optionsForCalendarsByID places allowed options for exchange.Calendar object
// @param moreOps should reflect elements from fieldsForCalendars
// @return is first call in Calendars().GetWithRequestConfigurationAndResponseHandler
func optionsForCalendarsByID(moreOps []string) (
*users.ItemCalendarsCalendarItemRequestBuilderGetRequestConfiguration,
error,
) {
selecting, err := buildOptions(moreOps, fieldsForCalendars)
if err != nil {
return nil, err
}
// should be a CalendarsRequestBuilderGetRequestConfiguration
requestParams := &users.ItemCalendarsCalendarItemRequestBuilderGetQueryParameters{
Select: selecting,
}
options := &users.ItemCalendarsCalendarItemRequestBuilderGetRequestConfiguration{
QueryParameters: requestParams,
}
return options, nil
}
// optionsForContactFolders places allowed options for exchange.ContactFolder object // optionsForContactFolders places allowed options for exchange.ContactFolder object
// @return is first call in ContactFolders().GetWithRequestConfigurationAndResponseHandler // @return is first call in ContactFolders().GetWithRequestConfigurationAndResponseHandler
func optionsForContactFolders(moreOps []string) ( func optionsForContactFolders(moreOps []string) (
@ -213,26 +223,6 @@ func optionsForContactFoldersItemDelta(
return options, nil return options, nil
} }
// optionsForEvents ensures a valid option inputs for `exchange.Events` when selected from within a Calendar
func optionsForEventsByCalendarDelta(
moreOps []string,
) (*users.ItemCalendarsItemEventsDeltaRequestBuilderGetRequestConfiguration, error) {
selecting, err := buildOptions(moreOps, fieldsForEvents)
if err != nil {
return nil, err
}
requestParameters := &users.ItemCalendarsItemEventsDeltaRequestBuilderGetQueryParameters{
Select: selecting,
}
options := &users.ItemCalendarsItemEventsDeltaRequestBuilderGetRequestConfiguration{
QueryParameters: requestParameters,
}
return options, nil
}
// optionsForContactChildFolders builds a contacts child folders request. // optionsForContactChildFolders builds a contacts child folders request.
func optionsForContactChildFolders( func optionsForContactChildFolders(
moreOps []string, moreOps []string,

View File

@ -6,6 +6,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/internal/connector/support"
"github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/path"
) )
@ -14,9 +15,43 @@ var _ graph.ContainerResolver = &eventCalendarCache{}
type eventCalendarCache struct { type eventCalendarCache struct {
*containerResolver *containerResolver
enumer containersEnumerator enumer containersEnumerator
getter containerGetter
userID string userID string
} }
// init ensures that the structure's fields are initialized.
// Fields Initialized when cache == nil:
// [mc.cache]
func (ecc *eventCalendarCache) init(
ctx context.Context,
) error {
if ecc.containerResolver == nil {
ecc.containerResolver = newContainerResolver()
}
return ecc.populateEventRoot(ctx)
}
// populateEventRoot manually fetches directories that are not returned during Graph for msgraph-sdk-go v. 40+
// DefaultCalendar is the traditional "Calendar".
// Action ensures that cache will stop at appropriate level.
// @error iff the struct is not properly instantiated
func (ecc *eventCalendarCache) populateEventRoot(ctx context.Context) error {
container := DefaultCalendar
f, err := ecc.getter.GetContainerByID(ctx, ecc.userID, container)
if err != nil {
return errors.Wrap(err, "fetching calendar "+support.ConnectorStackErrorTrace(err))
}
temp := graph.NewCacheFolder(f, path.Builder{}.Append(container))
if err := ecc.addFolder(temp); err != nil {
return errors.Wrap(err, "initializing calendar resolver")
}
return nil
}
// Populate utility function for populating eventCalendarCache. // Populate utility function for populating eventCalendarCache.
// Executes 1 additional Graph Query // Executes 1 additional Graph Query
// @param baseID: ignored. Present to conform to interface // @param baseID: ignored. Present to conform to interface
@ -25,8 +60,8 @@ func (ecc *eventCalendarCache) Populate(
baseID string, baseID string,
baseContainerPath ...string, baseContainerPath ...string,
) error { ) error {
if ecc.containerResolver == nil { if err := ecc.init(ctx); err != nil {
ecc.containerResolver = newContainerResolver() return errors.Wrap(err, "initializing")
} }
err := ecc.enumer.EnumerateContainers(ctx, ecc.userID, "", ecc.addFolder) err := ecc.enumer.EnumerateContainers(ctx, ecc.userID, "", ecc.addFolder)
@ -34,6 +69,10 @@ func (ecc *eventCalendarCache) Populate(
return errors.Wrap(err, "enumerating containers") return errors.Wrap(err, "enumerating containers")
} }
if err := ecc.populatePaths(ctx); err != nil {
return errors.Wrap(err, "establishing calendar paths")
}
return nil return nil
} }

View File

@ -51,6 +51,7 @@ func (suite *CacheResolverSuite) TestPopulate() {
return &eventCalendarCache{ return &eventCalendarCache{
userID: tester.M365UserID(t), userID: tester.M365UserID(t),
enumer: ac.Events(), enumer: ac.Events(),
getter: ac.Events(),
} }
} }

View File

@ -22,14 +22,25 @@ type mailFolderCache struct {
userID string userID string
} }
// init ensures that the structure's fields are initialized.
// Fields Initialized when cache == nil:
// [mc.cache]
func (mc *mailFolderCache) init(
ctx context.Context,
) error {
if mc.containerResolver == nil {
mc.containerResolver = newContainerResolver()
}
return mc.populateMailRoot(ctx)
}
// populateMailRoot manually fetches directories that are not returned during Graph for msgraph-sdk-go v. 40+ // populateMailRoot manually fetches directories that are not returned during Graph for msgraph-sdk-go v. 40+
// rootFolderAlias is the top-level directory for exchange.Mail. // rootFolderAlias is the top-level directory for exchange.Mail.
// DefaultMailFolder is the traditional "Inbox" for exchange.Mail // DefaultMailFolder is the traditional "Inbox" for exchange.Mail
// Action ensures that cache will stop at appropriate level. // Action ensures that cache will stop at appropriate level.
// @error iff the struct is not properly instantiated // @error iff the struct is not properly instantiated
func (mc *mailFolderCache) populateMailRoot( func (mc *mailFolderCache) populateMailRoot(ctx context.Context) error {
ctx context.Context,
) error {
for _, fldr := range []string{rootFolderAlias, DefaultMailFolder} { for _, fldr := range []string{rootFolderAlias, DefaultMailFolder} {
var directory string var directory string
@ -76,16 +87,3 @@ func (mc *mailFolderCache) Populate(
return nil return nil
} }
// init ensures that the structure's fields are initialized.
// Fields Initialized when cache == nil:
// [mc.cache]
func (mc *mailFolderCache) init(
ctx context.Context,
) error {
if mc.containerResolver == nil {
mc.containerResolver = newContainerResolver()
}
return mc.populateMailRoot(ctx)
}

View File

@ -66,9 +66,11 @@ func PopulateExchangeContainerResolver(
cacheRoot = DefaultContactFolder cacheRoot = DefaultContactFolder
case path.EventsCategory: case path.EventsCategory:
ecc := ac.Events()
res = &eventCalendarCache{ res = &eventCalendarCache{
userID: qp.ResourceOwner, userID: qp.ResourceOwner,
enumer: ac.Events(), getter: ecc,
enumer: ecc,
} }
cacheRoot = DefaultCalendar cacheRoot = DefaultCalendar

View File

@ -507,9 +507,11 @@ func CreateContainerDestinaion(
case path.EventsCategory: case path.EventsCategory:
if directoryCache == nil { if directoryCache == nil {
ace := ac.Events()
ecc := &eventCalendarCache{ ecc := &eventCalendarCache{
userID: user, userID: user,
enumer: ac.Events(), getter: ace,
enumer: ace,
} }
caches[category] = ecc caches[category] = ecc
newCache = true newCache = true