separate pager and plain apis for exchange (#3627)
Separates the pager and enumerattion functionality from the rest of the exchange api funcs for each category. Purely code movement, no logic changes. --- #### Does this PR need a docs update or release note? - [x] ⛔ No #### Type of change - [x] 🧹 Tech Debt/Cleanup #### Issue(s) * #3562 #### Test Plan - [x] ⚡ Unit test - [x] 💚 E2E
This commit is contained in:
parent
416383a99c
commit
c70207b1f8
@ -14,7 +14,6 @@ import (
|
||||
"github.com/alcionai/corso/src/internal/m365/graph"
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/selectors"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -137,79 +136,6 @@ func (c Contacts) PatchFolder(
|
||||
return nil
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// container pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// EnumerateContainers iterates through all of the users current
|
||||
// contacts folders, converting each to a graph.CacheFolder, and calling
|
||||
// fn(cf) on each one.
|
||||
// Folder hierarchy is represented in its current state, and does
|
||||
// not contain historical data.
|
||||
func (c Contacts) EnumerateContainers(
|
||||
ctx context.Context,
|
||||
userID, baseContainerID string,
|
||||
fn func(graph.CachedContainer) error,
|
||||
errs *fault.Bus,
|
||||
) error {
|
||||
config := &users.ItemContactFoldersItemChildFoldersRequestBuilderGetRequestConfiguration{
|
||||
QueryParameters: &users.ItemContactFoldersItemChildFoldersRequestBuilderGetQueryParameters{
|
||||
Select: idAnd(displayName, parentFolderID),
|
||||
},
|
||||
}
|
||||
|
||||
el := errs.Local()
|
||||
builder := c.Stable.
|
||||
Client().
|
||||
Users().
|
||||
ByUserId(userID).
|
||||
ContactFolders().
|
||||
ByContactFolderId(baseContainerID).
|
||||
ChildFolders()
|
||||
|
||||
for {
|
||||
if el.Failure() != nil {
|
||||
break
|
||||
}
|
||||
|
||||
resp, err := builder.Get(ctx, config)
|
||||
if err != nil {
|
||||
return graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
for _, fold := range resp.GetValue() {
|
||||
if el.Failure() != nil {
|
||||
return el.Failure()
|
||||
}
|
||||
|
||||
if err := graph.CheckIDNameAndParentFolderID(fold); err != nil {
|
||||
errs.AddRecoverable(ctx, graph.Stack(ctx, err).Label(fault.LabelForceNoBackupCreation))
|
||||
continue
|
||||
}
|
||||
|
||||
fctx := clues.Add(
|
||||
ctx,
|
||||
"container_id", ptr.Val(fold.GetId()),
|
||||
"container_display_name", ptr.Val(fold.GetDisplayName()))
|
||||
|
||||
temp := graph.NewCacheFolder(fold, nil, nil)
|
||||
if err := fn(&temp); err != nil {
|
||||
errs.AddRecoverable(ctx, graph.Stack(fctx, err).Label(fault.LabelForceNoBackupCreation))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
link, ok := ptr.ValOK(resp.GetOdataNextLink())
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
builder = users.NewItemContactFoldersItemChildFoldersRequestBuilder(link, c.Stable.Adapter())
|
||||
}
|
||||
|
||||
return el.Failure()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// items
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -284,145 +210,6 @@ func (c Contacts) DeleteItem(
|
||||
return nil
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// item pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
var _ itemPager = &contactPager{}
|
||||
|
||||
type contactPager struct {
|
||||
gs graph.Servicer
|
||||
builder *users.ItemContactFoldersItemContactsRequestBuilder
|
||||
options *users.ItemContactFoldersItemContactsRequestBuilderGetRequestConfiguration
|
||||
}
|
||||
|
||||
func (c Contacts) NewContactPager(
|
||||
ctx context.Context,
|
||||
userID, containerID string,
|
||||
immutableIDs bool,
|
||||
) itemPager {
|
||||
config := &users.ItemContactFoldersItemContactsRequestBuilderGetRequestConfiguration{
|
||||
QueryParameters: &users.ItemContactFoldersItemContactsRequestBuilderGetQueryParameters{
|
||||
Select: idAnd(parentFolderID),
|
||||
},
|
||||
Headers: newPreferHeaders(preferPageSize(maxNonDeltaPageSize), preferImmutableIDs(immutableIDs)),
|
||||
}
|
||||
|
||||
builder := c.Stable.
|
||||
Client().
|
||||
Users().
|
||||
ByUserId(userID).
|
||||
ContactFolders().
|
||||
ByContactFolderId(containerID).
|
||||
Contacts()
|
||||
|
||||
return &contactPager{c.Stable, builder, config}
|
||||
}
|
||||
|
||||
func (p *contactPager) getPage(ctx context.Context) (DeltaPageLinker, error) {
|
||||
resp, err := p.builder.Get(ctx, p.options)
|
||||
if err != nil {
|
||||
return nil, graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
return EmptyDeltaLinker[models.Contactable]{PageLinkValuer: resp}, nil
|
||||
}
|
||||
|
||||
func (p *contactPager) setNext(nextLink string) {
|
||||
p.builder = users.NewItemContactFoldersItemContactsRequestBuilder(nextLink, p.gs.Adapter())
|
||||
}
|
||||
|
||||
// non delta pagers don't need reset
|
||||
func (p *contactPager) reset(context.Context) {}
|
||||
|
||||
func (p *contactPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) {
|
||||
return toValues[models.Contactable](pl)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// delta item pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
var _ itemPager = &contactDeltaPager{}
|
||||
|
||||
type contactDeltaPager struct {
|
||||
gs graph.Servicer
|
||||
userID string
|
||||
containerID string
|
||||
builder *users.ItemContactFoldersItemContactsDeltaRequestBuilder
|
||||
options *users.ItemContactFoldersItemContactsDeltaRequestBuilderGetRequestConfiguration
|
||||
}
|
||||
|
||||
func getContactDeltaBuilder(
|
||||
ctx context.Context,
|
||||
gs graph.Servicer,
|
||||
userID, containerID string,
|
||||
options *users.ItemContactFoldersItemContactsDeltaRequestBuilderGetRequestConfiguration,
|
||||
) *users.ItemContactFoldersItemContactsDeltaRequestBuilder {
|
||||
builder := gs.Client().Users().ByUserId(userID).ContactFolders().ByContactFolderId(containerID).Contacts().Delta()
|
||||
return builder
|
||||
}
|
||||
|
||||
func (c Contacts) NewContactDeltaPager(
|
||||
ctx context.Context,
|
||||
userID, containerID, oldDelta string,
|
||||
immutableIDs bool,
|
||||
) itemPager {
|
||||
options := &users.ItemContactFoldersItemContactsDeltaRequestBuilderGetRequestConfiguration{
|
||||
QueryParameters: &users.ItemContactFoldersItemContactsDeltaRequestBuilderGetQueryParameters{
|
||||
Select: idAnd(parentFolderID),
|
||||
},
|
||||
Headers: newPreferHeaders(preferPageSize(maxDeltaPageSize), preferImmutableIDs(immutableIDs)),
|
||||
}
|
||||
|
||||
var builder *users.ItemContactFoldersItemContactsDeltaRequestBuilder
|
||||
if oldDelta != "" {
|
||||
builder = users.NewItemContactFoldersItemContactsDeltaRequestBuilder(oldDelta, c.Stable.Adapter())
|
||||
} else {
|
||||
builder = getContactDeltaBuilder(ctx, c.Stable, userID, containerID, options)
|
||||
}
|
||||
|
||||
return &contactDeltaPager{c.Stable, userID, containerID, builder, options}
|
||||
}
|
||||
|
||||
func (p *contactDeltaPager) getPage(ctx context.Context) (DeltaPageLinker, error) {
|
||||
resp, err := p.builder.Get(ctx, p.options)
|
||||
if err != nil {
|
||||
return nil, graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *contactDeltaPager) setNext(nextLink string) {
|
||||
p.builder = users.NewItemContactFoldersItemContactsDeltaRequestBuilder(nextLink, p.gs.Adapter())
|
||||
}
|
||||
|
||||
func (p *contactDeltaPager) reset(ctx context.Context) {
|
||||
p.builder = getContactDeltaBuilder(ctx, p.gs, p.userID, p.containerID, p.options)
|
||||
}
|
||||
|
||||
func (p *contactDeltaPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) {
|
||||
return toValues[models.Contactable](pl)
|
||||
}
|
||||
|
||||
func (c Contacts) GetAddedAndRemovedItemIDs(
|
||||
ctx context.Context,
|
||||
userID, containerID, oldDelta string,
|
||||
immutableIDs bool,
|
||||
canMakeDeltaQueries bool,
|
||||
) ([]string, []string, DeltaUpdate, error) {
|
||||
ctx = clues.Add(
|
||||
ctx,
|
||||
"category", selectors.ExchangeContact,
|
||||
"container_id", containerID)
|
||||
|
||||
pager := c.NewContactPager(ctx, userID, containerID, immutableIDs)
|
||||
deltaPager := c.NewContactDeltaPager(ctx, userID, containerID, oldDelta, immutableIDs)
|
||||
|
||||
return getAddedAndRemovedItemIDs(ctx, c.Stable, pager, deltaPager, oldDelta, canMakeDeltaQueries)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Serialization
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
226
src/pkg/services/m365/api/contacts_pager.go
Normal file
226
src/pkg/services/m365/api/contacts_pager.go
Normal file
@ -0,0 +1,226 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/alcionai/clues"
|
||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||
"github.com/microsoftgraph/msgraph-sdk-go/users"
|
||||
|
||||
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||
"github.com/alcionai/corso/src/internal/m365/graph"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/selectors"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// container pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// EnumerateContainers iterates through all of the users current
|
||||
// contacts folders, converting each to a graph.CacheFolder, and calling
|
||||
// fn(cf) on each one.
|
||||
// Folder hierarchy is represented in its current state, and does
|
||||
// not contain historical data.
|
||||
func (c Contacts) EnumerateContainers(
|
||||
ctx context.Context,
|
||||
userID, baseContainerID string,
|
||||
fn func(graph.CachedContainer) error,
|
||||
errs *fault.Bus,
|
||||
) error {
|
||||
config := &users.ItemContactFoldersItemChildFoldersRequestBuilderGetRequestConfiguration{
|
||||
QueryParameters: &users.ItemContactFoldersItemChildFoldersRequestBuilderGetQueryParameters{
|
||||
Select: idAnd(displayName, parentFolderID),
|
||||
},
|
||||
}
|
||||
|
||||
el := errs.Local()
|
||||
builder := c.Stable.
|
||||
Client().
|
||||
Users().
|
||||
ByUserId(userID).
|
||||
ContactFolders().
|
||||
ByContactFolderId(baseContainerID).
|
||||
ChildFolders()
|
||||
|
||||
for {
|
||||
if el.Failure() != nil {
|
||||
break
|
||||
}
|
||||
|
||||
resp, err := builder.Get(ctx, config)
|
||||
if err != nil {
|
||||
return graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
for _, fold := range resp.GetValue() {
|
||||
if el.Failure() != nil {
|
||||
return el.Failure()
|
||||
}
|
||||
|
||||
if err := graph.CheckIDNameAndParentFolderID(fold); err != nil {
|
||||
errs.AddRecoverable(ctx, graph.Stack(ctx, err).Label(fault.LabelForceNoBackupCreation))
|
||||
continue
|
||||
}
|
||||
|
||||
fctx := clues.Add(
|
||||
ctx,
|
||||
"container_id", ptr.Val(fold.GetId()),
|
||||
"container_display_name", ptr.Val(fold.GetDisplayName()))
|
||||
|
||||
temp := graph.NewCacheFolder(fold, nil, nil)
|
||||
if err := fn(&temp); err != nil {
|
||||
errs.AddRecoverable(ctx, graph.Stack(fctx, err).Label(fault.LabelForceNoBackupCreation))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
link, ok := ptr.ValOK(resp.GetOdataNextLink())
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
builder = users.NewItemContactFoldersItemChildFoldersRequestBuilder(link, c.Stable.Adapter())
|
||||
}
|
||||
|
||||
return el.Failure()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// item pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
var _ itemPager = &contactPager{}
|
||||
|
||||
type contactPager struct {
|
||||
gs graph.Servicer
|
||||
builder *users.ItemContactFoldersItemContactsRequestBuilder
|
||||
options *users.ItemContactFoldersItemContactsRequestBuilderGetRequestConfiguration
|
||||
}
|
||||
|
||||
func (c Contacts) NewContactPager(
|
||||
ctx context.Context,
|
||||
userID, containerID string,
|
||||
immutableIDs bool,
|
||||
) itemPager {
|
||||
config := &users.ItemContactFoldersItemContactsRequestBuilderGetRequestConfiguration{
|
||||
QueryParameters: &users.ItemContactFoldersItemContactsRequestBuilderGetQueryParameters{
|
||||
Select: idAnd(parentFolderID),
|
||||
},
|
||||
Headers: newPreferHeaders(preferPageSize(maxNonDeltaPageSize), preferImmutableIDs(immutableIDs)),
|
||||
}
|
||||
|
||||
builder := c.Stable.
|
||||
Client().
|
||||
Users().
|
||||
ByUserId(userID).
|
||||
ContactFolders().
|
||||
ByContactFolderId(containerID).
|
||||
Contacts()
|
||||
|
||||
return &contactPager{c.Stable, builder, config}
|
||||
}
|
||||
|
||||
func (p *contactPager) getPage(ctx context.Context) (DeltaPageLinker, error) {
|
||||
resp, err := p.builder.Get(ctx, p.options)
|
||||
if err != nil {
|
||||
return nil, graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
return EmptyDeltaLinker[models.Contactable]{PageLinkValuer: resp}, nil
|
||||
}
|
||||
|
||||
func (p *contactPager) setNext(nextLink string) {
|
||||
p.builder = users.NewItemContactFoldersItemContactsRequestBuilder(nextLink, p.gs.Adapter())
|
||||
}
|
||||
|
||||
// non delta pagers don't need reset
|
||||
func (p *contactPager) reset(context.Context) {}
|
||||
|
||||
func (p *contactPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) {
|
||||
return toValues[models.Contactable](pl)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// delta item pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
var _ itemPager = &contactDeltaPager{}
|
||||
|
||||
type contactDeltaPager struct {
|
||||
gs graph.Servicer
|
||||
userID string
|
||||
containerID string
|
||||
builder *users.ItemContactFoldersItemContactsDeltaRequestBuilder
|
||||
options *users.ItemContactFoldersItemContactsDeltaRequestBuilderGetRequestConfiguration
|
||||
}
|
||||
|
||||
func getContactDeltaBuilder(
|
||||
ctx context.Context,
|
||||
gs graph.Servicer,
|
||||
userID, containerID string,
|
||||
options *users.ItemContactFoldersItemContactsDeltaRequestBuilderGetRequestConfiguration,
|
||||
) *users.ItemContactFoldersItemContactsDeltaRequestBuilder {
|
||||
builder := gs.Client().Users().ByUserId(userID).ContactFolders().ByContactFolderId(containerID).Contacts().Delta()
|
||||
return builder
|
||||
}
|
||||
|
||||
func (c Contacts) NewContactDeltaPager(
|
||||
ctx context.Context,
|
||||
userID, containerID, oldDelta string,
|
||||
immutableIDs bool,
|
||||
) itemPager {
|
||||
options := &users.ItemContactFoldersItemContactsDeltaRequestBuilderGetRequestConfiguration{
|
||||
QueryParameters: &users.ItemContactFoldersItemContactsDeltaRequestBuilderGetQueryParameters{
|
||||
Select: idAnd(parentFolderID),
|
||||
},
|
||||
Headers: newPreferHeaders(preferPageSize(maxDeltaPageSize), preferImmutableIDs(immutableIDs)),
|
||||
}
|
||||
|
||||
var builder *users.ItemContactFoldersItemContactsDeltaRequestBuilder
|
||||
if oldDelta != "" {
|
||||
builder = users.NewItemContactFoldersItemContactsDeltaRequestBuilder(oldDelta, c.Stable.Adapter())
|
||||
} else {
|
||||
builder = getContactDeltaBuilder(ctx, c.Stable, userID, containerID, options)
|
||||
}
|
||||
|
||||
return &contactDeltaPager{c.Stable, userID, containerID, builder, options}
|
||||
}
|
||||
|
||||
func (p *contactDeltaPager) getPage(ctx context.Context) (DeltaPageLinker, error) {
|
||||
resp, err := p.builder.Get(ctx, p.options)
|
||||
if err != nil {
|
||||
return nil, graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *contactDeltaPager) setNext(nextLink string) {
|
||||
p.builder = users.NewItemContactFoldersItemContactsDeltaRequestBuilder(nextLink, p.gs.Adapter())
|
||||
}
|
||||
|
||||
func (p *contactDeltaPager) reset(ctx context.Context) {
|
||||
p.builder = getContactDeltaBuilder(ctx, p.gs, p.userID, p.containerID, p.options)
|
||||
}
|
||||
|
||||
func (p *contactDeltaPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) {
|
||||
return toValues[models.Contactable](pl)
|
||||
}
|
||||
|
||||
func (c Contacts) GetAddedAndRemovedItemIDs(
|
||||
ctx context.Context,
|
||||
userID, containerID, oldDelta string,
|
||||
immutableIDs bool,
|
||||
canMakeDeltaQueries bool,
|
||||
) ([]string, []string, DeltaUpdate, error) {
|
||||
ctx = clues.Add(
|
||||
ctx,
|
||||
"category", selectors.ExchangeContact,
|
||||
"container_id", containerID)
|
||||
|
||||
pager := c.NewContactPager(ctx, userID, containerID, immutableIDs)
|
||||
deltaPager := c.NewContactDeltaPager(ctx, userID, containerID, oldDelta, immutableIDs)
|
||||
|
||||
return getAddedAndRemovedItemIDs(ctx, c.Stable, pager, deltaPager, oldDelta, canMakeDeltaQueries)
|
||||
}
|
||||
@ -18,7 +18,6 @@ import (
|
||||
"github.com/alcionai/corso/src/internal/m365/graph"
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -190,86 +189,6 @@ func (c Events) PatchCalendar(
|
||||
return nil
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// container pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// EnumerateContainers iterates through all of the users current
|
||||
// calendars, converting each to a graph.CacheFolder, and
|
||||
// calling fn(cf) on each one.
|
||||
// Folder hierarchy is represented in its current state, and does
|
||||
// not contain historical data.
|
||||
func (c Events) EnumerateContainers(
|
||||
ctx context.Context,
|
||||
userID, baseContainerID string,
|
||||
fn func(graph.CachedContainer) error,
|
||||
errs *fault.Bus,
|
||||
) error {
|
||||
var (
|
||||
el = errs.Local()
|
||||
config = &users.ItemCalendarsRequestBuilderGetRequestConfiguration{
|
||||
QueryParameters: &users.ItemCalendarsRequestBuilderGetQueryParameters{
|
||||
Select: idAnd("name"),
|
||||
},
|
||||
}
|
||||
builder = c.Stable.
|
||||
Client().
|
||||
Users().
|
||||
ByUserId(userID).
|
||||
Calendars()
|
||||
)
|
||||
|
||||
for {
|
||||
if el.Failure() != nil {
|
||||
break
|
||||
}
|
||||
|
||||
resp, err := builder.Get(ctx, config)
|
||||
if err != nil {
|
||||
return graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
for _, cal := range resp.GetValue() {
|
||||
if el.Failure() != nil {
|
||||
break
|
||||
}
|
||||
|
||||
cd := CalendarDisplayable{Calendarable: cal}
|
||||
if err := graph.CheckIDAndName(cd); err != nil {
|
||||
errs.AddRecoverable(ctx, graph.Stack(ctx, err).Label(fault.LabelForceNoBackupCreation))
|
||||
continue
|
||||
}
|
||||
|
||||
fctx := clues.Add(
|
||||
ctx,
|
||||
"container_id", ptr.Val(cal.GetId()),
|
||||
"container_name", ptr.Val(cal.GetName()))
|
||||
|
||||
temp := graph.NewCacheFolder(
|
||||
cd,
|
||||
path.Builder{}.Append(ptr.Val(cd.GetId())), // storage path
|
||||
path.Builder{}.Append(ptr.Val(cd.GetDisplayName()))) // display location
|
||||
if err := fn(&temp); err != nil {
|
||||
errs.AddRecoverable(ctx, graph.Stack(fctx, err).Label(fault.LabelForceNoBackupCreation))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
link, ok := ptr.ValOK(resp.GetOdataNextLink())
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
builder = users.NewItemCalendarsRequestBuilder(link, c.Stable.Adapter())
|
||||
}
|
||||
|
||||
return el.Failure()
|
||||
}
|
||||
|
||||
const (
|
||||
eventBetaDeltaURLTemplate = "https://graph.microsoft.com/beta/users/%s/calendars/%s/events/delta"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// items
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -434,154 +353,6 @@ func (c Events) PostLargeAttachment(
|
||||
return us, nil
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// item pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
var _ itemPager = &eventPager{}
|
||||
|
||||
type eventPager struct {
|
||||
gs graph.Servicer
|
||||
builder *users.ItemCalendarsItemEventsRequestBuilder
|
||||
options *users.ItemCalendarsItemEventsRequestBuilderGetRequestConfiguration
|
||||
}
|
||||
|
||||
func (c Events) NewEventPager(
|
||||
ctx context.Context,
|
||||
userID, containerID string,
|
||||
immutableIDs bool,
|
||||
) (itemPager, error) {
|
||||
options := &users.ItemCalendarsItemEventsRequestBuilderGetRequestConfiguration{
|
||||
Headers: newPreferHeaders(preferPageSize(maxNonDeltaPageSize), preferImmutableIDs(immutableIDs)),
|
||||
}
|
||||
|
||||
builder := c.Stable.
|
||||
Client().
|
||||
Users().
|
||||
ByUserId(userID).
|
||||
Calendars().
|
||||
ByCalendarId(containerID).
|
||||
Events()
|
||||
|
||||
return &eventPager{c.Stable, builder, options}, nil
|
||||
}
|
||||
|
||||
func (p *eventPager) getPage(ctx context.Context) (DeltaPageLinker, error) {
|
||||
resp, err := p.builder.Get(ctx, p.options)
|
||||
if err != nil {
|
||||
return nil, graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
return EmptyDeltaLinker[models.Eventable]{PageLinkValuer: resp}, nil
|
||||
}
|
||||
|
||||
func (p *eventPager) setNext(nextLink string) {
|
||||
p.builder = users.NewItemCalendarsItemEventsRequestBuilder(nextLink, p.gs.Adapter())
|
||||
}
|
||||
|
||||
// non delta pagers don't need reset
|
||||
func (p *eventPager) reset(context.Context) {}
|
||||
|
||||
func (p *eventPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) {
|
||||
return toValues[models.Eventable](pl)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// delta item pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
var _ itemPager = &eventDeltaPager{}
|
||||
|
||||
type eventDeltaPager struct {
|
||||
gs graph.Servicer
|
||||
userID string
|
||||
containerID string
|
||||
builder *users.ItemCalendarsItemEventsDeltaRequestBuilder
|
||||
options *users.ItemCalendarsItemEventsDeltaRequestBuilderGetRequestConfiguration
|
||||
}
|
||||
|
||||
func (c Events) NewEventDeltaPager(
|
||||
ctx context.Context,
|
||||
userID, containerID, oldDelta string,
|
||||
immutableIDs bool,
|
||||
) (itemPager, error) {
|
||||
options := &users.ItemCalendarsItemEventsDeltaRequestBuilderGetRequestConfiguration{
|
||||
Headers: newPreferHeaders(preferPageSize(maxDeltaPageSize), preferImmutableIDs(immutableIDs)),
|
||||
}
|
||||
|
||||
var builder *users.ItemCalendarsItemEventsDeltaRequestBuilder
|
||||
|
||||
if oldDelta == "" {
|
||||
builder = getEventDeltaBuilder(ctx, c.Stable, userID, containerID, options)
|
||||
} else {
|
||||
builder = users.NewItemCalendarsItemEventsDeltaRequestBuilder(oldDelta, c.Stable.Adapter())
|
||||
}
|
||||
|
||||
return &eventDeltaPager{c.Stable, userID, containerID, builder, options}, nil
|
||||
}
|
||||
|
||||
func getEventDeltaBuilder(
|
||||
ctx context.Context,
|
||||
gs graph.Servicer,
|
||||
userID, containerID string,
|
||||
options *users.ItemCalendarsItemEventsDeltaRequestBuilderGetRequestConfiguration,
|
||||
) *users.ItemCalendarsItemEventsDeltaRequestBuilder {
|
||||
// Graph SDK only supports delta queries against events on the beta version, so we're
|
||||
// manufacturing use of the beta version url to make the call instead.
|
||||
// See: https://learn.microsoft.com/ko-kr/graph/api/event-delta?view=graph-rest-beta&tabs=http
|
||||
// Note that the delta item body is skeletal compared to the actual event struct. Lucky
|
||||
// for us, we only need the item ID. As a result, even though we hacked the version, the
|
||||
// response body parses properly into the v1.0 structs and complies with our wanted interfaces.
|
||||
// Likewise, the NextLink and DeltaLink odata tags carry our hack forward, so the rest of the code
|
||||
// works as intended (until, at least, we want to _not_ call the beta anymore).
|
||||
rawURL := fmt.Sprintf(eventBetaDeltaURLTemplate, userID, containerID)
|
||||
builder := users.NewItemCalendarsItemEventsDeltaRequestBuilder(rawURL, gs.Adapter())
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
func (p *eventDeltaPager) getPage(ctx context.Context) (DeltaPageLinker, error) {
|
||||
resp, err := p.builder.Get(ctx, p.options)
|
||||
if err != nil {
|
||||
return nil, graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *eventDeltaPager) setNext(nextLink string) {
|
||||
p.builder = users.NewItemCalendarsItemEventsDeltaRequestBuilder(nextLink, p.gs.Adapter())
|
||||
}
|
||||
|
||||
func (p *eventDeltaPager) reset(ctx context.Context) {
|
||||
p.builder = getEventDeltaBuilder(ctx, p.gs, p.userID, p.containerID, p.options)
|
||||
}
|
||||
|
||||
func (p *eventDeltaPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) {
|
||||
return toValues[models.Eventable](pl)
|
||||
}
|
||||
|
||||
func (c Events) GetAddedAndRemovedItemIDs(
|
||||
ctx context.Context,
|
||||
userID, containerID, oldDelta string,
|
||||
immutableIDs bool,
|
||||
canMakeDeltaQueries bool,
|
||||
) ([]string, []string, DeltaUpdate, error) {
|
||||
ctx = clues.Add(ctx, "container_id", containerID)
|
||||
|
||||
pager, err := c.NewEventPager(ctx, userID, containerID, immutableIDs)
|
||||
if err != nil {
|
||||
return nil, nil, DeltaUpdate{}, graph.Wrap(ctx, err, "creating non-delta pager")
|
||||
}
|
||||
|
||||
deltaPager, err := c.NewEventDeltaPager(ctx, userID, containerID, oldDelta, immutableIDs)
|
||||
if err != nil {
|
||||
return nil, nil, DeltaUpdate{}, graph.Wrap(ctx, err, "creating delta pager")
|
||||
}
|
||||
|
||||
return getAddedAndRemovedItemIDs(ctx, c.Stable, pager, deltaPager, oldDelta, canMakeDeltaQueries)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Serialization
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
243
src/pkg/services/m365/api/events_pager.go
Normal file
243
src/pkg/services/m365/api/events_pager.go
Normal file
@ -0,0 +1,243 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/alcionai/clues"
|
||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||
"github.com/microsoftgraph/msgraph-sdk-go/users"
|
||||
|
||||
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||
"github.com/alcionai/corso/src/internal/m365/graph"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
|
||||
const (
|
||||
eventBetaDeltaURLTemplate = "https://graph.microsoft.com/beta/users/%s/calendars/%s/events/delta"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// container pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// EnumerateContainers iterates through all of the users current
|
||||
// calendars, converting each to a graph.CacheFolder, and
|
||||
// calling fn(cf) on each one.
|
||||
// Folder hierarchy is represented in its current state, and does
|
||||
// not contain historical data.
|
||||
func (c Events) EnumerateContainers(
|
||||
ctx context.Context,
|
||||
userID, baseContainerID string,
|
||||
fn func(graph.CachedContainer) error,
|
||||
errs *fault.Bus,
|
||||
) error {
|
||||
var (
|
||||
el = errs.Local()
|
||||
config = &users.ItemCalendarsRequestBuilderGetRequestConfiguration{
|
||||
QueryParameters: &users.ItemCalendarsRequestBuilderGetQueryParameters{
|
||||
Select: idAnd("name"),
|
||||
},
|
||||
}
|
||||
builder = c.Stable.
|
||||
Client().
|
||||
Users().
|
||||
ByUserId(userID).
|
||||
Calendars()
|
||||
)
|
||||
|
||||
for {
|
||||
if el.Failure() != nil {
|
||||
break
|
||||
}
|
||||
|
||||
resp, err := builder.Get(ctx, config)
|
||||
if err != nil {
|
||||
return graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
for _, cal := range resp.GetValue() {
|
||||
if el.Failure() != nil {
|
||||
break
|
||||
}
|
||||
|
||||
cd := CalendarDisplayable{Calendarable: cal}
|
||||
if err := graph.CheckIDAndName(cd); err != nil {
|
||||
errs.AddRecoverable(ctx, graph.Stack(ctx, err).Label(fault.LabelForceNoBackupCreation))
|
||||
continue
|
||||
}
|
||||
|
||||
fctx := clues.Add(
|
||||
ctx,
|
||||
"container_id", ptr.Val(cal.GetId()),
|
||||
"container_name", ptr.Val(cal.GetName()))
|
||||
|
||||
temp := graph.NewCacheFolder(
|
||||
cd,
|
||||
path.Builder{}.Append(ptr.Val(cd.GetId())), // storage path
|
||||
path.Builder{}.Append(ptr.Val(cd.GetDisplayName()))) // display location
|
||||
if err := fn(&temp); err != nil {
|
||||
errs.AddRecoverable(ctx, graph.Stack(fctx, err).Label(fault.LabelForceNoBackupCreation))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
link, ok := ptr.ValOK(resp.GetOdataNextLink())
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
builder = users.NewItemCalendarsRequestBuilder(link, c.Stable.Adapter())
|
||||
}
|
||||
|
||||
return el.Failure()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// item pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
var _ itemPager = &eventPager{}
|
||||
|
||||
type eventPager struct {
|
||||
gs graph.Servicer
|
||||
builder *users.ItemCalendarsItemEventsRequestBuilder
|
||||
options *users.ItemCalendarsItemEventsRequestBuilderGetRequestConfiguration
|
||||
}
|
||||
|
||||
func (c Events) NewEventPager(
|
||||
ctx context.Context,
|
||||
userID, containerID string,
|
||||
immutableIDs bool,
|
||||
) (itemPager, error) {
|
||||
options := &users.ItemCalendarsItemEventsRequestBuilderGetRequestConfiguration{
|
||||
Headers: newPreferHeaders(preferPageSize(maxNonDeltaPageSize), preferImmutableIDs(immutableIDs)),
|
||||
}
|
||||
|
||||
builder := c.Stable.
|
||||
Client().
|
||||
Users().
|
||||
ByUserId(userID).
|
||||
Calendars().
|
||||
ByCalendarId(containerID).
|
||||
Events()
|
||||
|
||||
return &eventPager{c.Stable, builder, options}, nil
|
||||
}
|
||||
|
||||
func (p *eventPager) getPage(ctx context.Context) (DeltaPageLinker, error) {
|
||||
resp, err := p.builder.Get(ctx, p.options)
|
||||
if err != nil {
|
||||
return nil, graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
return EmptyDeltaLinker[models.Eventable]{PageLinkValuer: resp}, nil
|
||||
}
|
||||
|
||||
func (p *eventPager) setNext(nextLink string) {
|
||||
p.builder = users.NewItemCalendarsItemEventsRequestBuilder(nextLink, p.gs.Adapter())
|
||||
}
|
||||
|
||||
// non delta pagers don't need reset
|
||||
func (p *eventPager) reset(context.Context) {}
|
||||
|
||||
func (p *eventPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) {
|
||||
return toValues[models.Eventable](pl)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// delta item pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
var _ itemPager = &eventDeltaPager{}
|
||||
|
||||
type eventDeltaPager struct {
|
||||
gs graph.Servicer
|
||||
userID string
|
||||
containerID string
|
||||
builder *users.ItemCalendarsItemEventsDeltaRequestBuilder
|
||||
options *users.ItemCalendarsItemEventsDeltaRequestBuilderGetRequestConfiguration
|
||||
}
|
||||
|
||||
func (c Events) NewEventDeltaPager(
|
||||
ctx context.Context,
|
||||
userID, containerID, oldDelta string,
|
||||
immutableIDs bool,
|
||||
) (itemPager, error) {
|
||||
options := &users.ItemCalendarsItemEventsDeltaRequestBuilderGetRequestConfiguration{
|
||||
Headers: newPreferHeaders(preferPageSize(maxDeltaPageSize), preferImmutableIDs(immutableIDs)),
|
||||
}
|
||||
|
||||
var builder *users.ItemCalendarsItemEventsDeltaRequestBuilder
|
||||
|
||||
if oldDelta == "" {
|
||||
builder = getEventDeltaBuilder(ctx, c.Stable, userID, containerID, options)
|
||||
} else {
|
||||
builder = users.NewItemCalendarsItemEventsDeltaRequestBuilder(oldDelta, c.Stable.Adapter())
|
||||
}
|
||||
|
||||
return &eventDeltaPager{c.Stable, userID, containerID, builder, options}, nil
|
||||
}
|
||||
|
||||
func getEventDeltaBuilder(
|
||||
ctx context.Context,
|
||||
gs graph.Servicer,
|
||||
userID, containerID string,
|
||||
options *users.ItemCalendarsItemEventsDeltaRequestBuilderGetRequestConfiguration,
|
||||
) *users.ItemCalendarsItemEventsDeltaRequestBuilder {
|
||||
// Graph SDK only supports delta queries against events on the beta version, so we're
|
||||
// manufacturing use of the beta version url to make the call instead.
|
||||
// See: https://learn.microsoft.com/ko-kr/graph/api/event-delta?view=graph-rest-beta&tabs=http
|
||||
// Note that the delta item body is skeletal compared to the actual event struct. Lucky
|
||||
// for us, we only need the item ID. As a result, even though we hacked the version, the
|
||||
// response body parses properly into the v1.0 structs and complies with our wanted interfaces.
|
||||
// Likewise, the NextLink and DeltaLink odata tags carry our hack forward, so the rest of the code
|
||||
// works as intended (until, at least, we want to _not_ call the beta anymore).
|
||||
rawURL := fmt.Sprintf(eventBetaDeltaURLTemplate, userID, containerID)
|
||||
builder := users.NewItemCalendarsItemEventsDeltaRequestBuilder(rawURL, gs.Adapter())
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
func (p *eventDeltaPager) getPage(ctx context.Context) (DeltaPageLinker, error) {
|
||||
resp, err := p.builder.Get(ctx, p.options)
|
||||
if err != nil {
|
||||
return nil, graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *eventDeltaPager) setNext(nextLink string) {
|
||||
p.builder = users.NewItemCalendarsItemEventsDeltaRequestBuilder(nextLink, p.gs.Adapter())
|
||||
}
|
||||
|
||||
func (p *eventDeltaPager) reset(ctx context.Context) {
|
||||
p.builder = getEventDeltaBuilder(ctx, p.gs, p.userID, p.containerID, p.options)
|
||||
}
|
||||
|
||||
func (p *eventDeltaPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) {
|
||||
return toValues[models.Eventable](pl)
|
||||
}
|
||||
|
||||
func (c Events) GetAddedAndRemovedItemIDs(
|
||||
ctx context.Context,
|
||||
userID, containerID, oldDelta string,
|
||||
immutableIDs bool,
|
||||
canMakeDeltaQueries bool,
|
||||
) ([]string, []string, DeltaUpdate, error) {
|
||||
ctx = clues.Add(ctx, "container_id", containerID)
|
||||
|
||||
pager, err := c.NewEventPager(ctx, userID, containerID, immutableIDs)
|
||||
if err != nil {
|
||||
return nil, nil, DeltaUpdate{}, graph.Wrap(ctx, err, "creating non-delta pager")
|
||||
}
|
||||
|
||||
deltaPager, err := c.NewEventDeltaPager(ctx, userID, containerID, oldDelta, immutableIDs)
|
||||
if err != nil {
|
||||
return nil, nil, DeltaUpdate{}, graph.Wrap(ctx, err, "creating delta pager")
|
||||
}
|
||||
|
||||
return getAddedAndRemovedItemIDs(ctx, c.Stable, pager, deltaPager, oldDelta, canMakeDeltaQueries)
|
||||
}
|
||||
@ -17,7 +17,6 @@ import (
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/logger"
|
||||
"github.com/alcionai/corso/src/pkg/selectors"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -188,109 +187,6 @@ func (c Mail) PatchFolder(
|
||||
return nil
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// container pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
type mailFolderPager struct {
|
||||
service graph.Servicer
|
||||
builder *users.ItemMailFoldersRequestBuilder
|
||||
}
|
||||
|
||||
func (c Mail) NewMailFolderPager(userID string) mailFolderPager {
|
||||
// v1.0 non delta /mailFolders endpoint does not return any of the nested folders
|
||||
rawURL := fmt.Sprintf(mailFoldersBetaURLTemplate, userID)
|
||||
builder := users.NewItemMailFoldersRequestBuilder(rawURL, c.Stable.Adapter())
|
||||
|
||||
return mailFolderPager{c.Stable, builder}
|
||||
}
|
||||
|
||||
func (p *mailFolderPager) getPage(ctx context.Context) (PageLinker, error) {
|
||||
page, err := p.builder.Get(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
return page, nil
|
||||
}
|
||||
|
||||
func (p *mailFolderPager) setNext(nextLink string) {
|
||||
p.builder = users.NewItemMailFoldersRequestBuilder(nextLink, p.service.Adapter())
|
||||
}
|
||||
|
||||
func (p *mailFolderPager) valuesIn(pl PageLinker) ([]models.MailFolderable, error) {
|
||||
// Ideally this should be `users.ItemMailFoldersResponseable`, but
|
||||
// that is not a thing as stable returns different result
|
||||
page, ok := pl.(models.MailFolderCollectionResponseable)
|
||||
if !ok {
|
||||
return nil, clues.New("converting to ItemMailFoldersResponseable")
|
||||
}
|
||||
|
||||
return page.GetValue(), nil
|
||||
}
|
||||
|
||||
// EnumerateContainers iterates through all of the users current
|
||||
// mail folders, converting each to a graph.CacheFolder, and calling
|
||||
// fn(cf) on each one.
|
||||
// Folder hierarchy is represented in its current state, and does
|
||||
// not contain historical data.
|
||||
func (c Mail) EnumerateContainers(
|
||||
ctx context.Context,
|
||||
userID, baseContainerID string,
|
||||
fn func(graph.CachedContainer) error,
|
||||
errs *fault.Bus,
|
||||
) error {
|
||||
el := errs.Local()
|
||||
pgr := c.NewMailFolderPager(userID)
|
||||
|
||||
for {
|
||||
if el.Failure() != nil {
|
||||
break
|
||||
}
|
||||
|
||||
page, err := pgr.getPage(ctx)
|
||||
if err != nil {
|
||||
return graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
resp, err := pgr.valuesIn(page)
|
||||
if err != nil {
|
||||
return graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
for _, fold := range resp {
|
||||
if el.Failure() != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if err := graph.CheckIDNameAndParentFolderID(fold); err != nil {
|
||||
errs.AddRecoverable(ctx, graph.Stack(ctx, err).Label(fault.LabelForceNoBackupCreation))
|
||||
continue
|
||||
}
|
||||
|
||||
fctx := clues.Add(
|
||||
ctx,
|
||||
"container_id", ptr.Val(fold.GetId()),
|
||||
"container_name", ptr.Val(fold.GetDisplayName()))
|
||||
|
||||
temp := graph.NewCacheFolder(fold, nil, nil)
|
||||
if err := fn(&temp); err != nil {
|
||||
errs.AddRecoverable(ctx, graph.Stack(fctx, err).Label(fault.LabelForceNoBackupCreation))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
link, ok := ptr.ValOK(page.GetOdataNextLink())
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
pgr.setNext(link)
|
||||
}
|
||||
|
||||
return el.Failure()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// items
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -549,161 +445,6 @@ func (c Mail) PostLargeAttachment(
|
||||
return us, nil
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// item pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
var _ itemPager = &mailPager{}
|
||||
|
||||
type mailPager struct {
|
||||
gs graph.Servicer
|
||||
builder *users.ItemMailFoldersItemMessagesRequestBuilder
|
||||
options *users.ItemMailFoldersItemMessagesRequestBuilderGetRequestConfiguration
|
||||
}
|
||||
|
||||
func (c Mail) NewMailPager(
|
||||
ctx context.Context,
|
||||
userID, containerID string,
|
||||
immutableIDs bool,
|
||||
) itemPager {
|
||||
config := &users.ItemMailFoldersItemMessagesRequestBuilderGetRequestConfiguration{
|
||||
QueryParameters: &users.ItemMailFoldersItemMessagesRequestBuilderGetQueryParameters{
|
||||
Select: idAnd("isRead"),
|
||||
},
|
||||
Headers: newPreferHeaders(preferPageSize(maxNonDeltaPageSize), preferImmutableIDs(immutableIDs)),
|
||||
}
|
||||
|
||||
builder := c.Stable.
|
||||
Client().
|
||||
Users().
|
||||
ByUserId(userID).
|
||||
MailFolders().
|
||||
ByMailFolderId(containerID).
|
||||
Messages()
|
||||
|
||||
return &mailPager{c.Stable, builder, config}
|
||||
}
|
||||
|
||||
func (p *mailPager) getPage(ctx context.Context) (DeltaPageLinker, error) {
|
||||
page, err := p.builder.Get(ctx, p.options)
|
||||
if err != nil {
|
||||
return nil, graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
return EmptyDeltaLinker[models.Messageable]{PageLinkValuer: page}, nil
|
||||
}
|
||||
|
||||
func (p *mailPager) setNext(nextLink string) {
|
||||
p.builder = users.NewItemMailFoldersItemMessagesRequestBuilder(nextLink, p.gs.Adapter())
|
||||
}
|
||||
|
||||
// non delta pagers don't have reset
|
||||
func (p *mailPager) reset(context.Context) {}
|
||||
|
||||
func (p *mailPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) {
|
||||
return toValues[models.Messageable](pl)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// delta item pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
var _ itemPager = &mailDeltaPager{}
|
||||
|
||||
type mailDeltaPager struct {
|
||||
gs graph.Servicer
|
||||
userID string
|
||||
containerID string
|
||||
builder *users.ItemMailFoldersItemMessagesDeltaRequestBuilder
|
||||
options *users.ItemMailFoldersItemMessagesDeltaRequestBuilderGetRequestConfiguration
|
||||
}
|
||||
|
||||
func getMailDeltaBuilder(
|
||||
ctx context.Context,
|
||||
gs graph.Servicer,
|
||||
user, containerID string,
|
||||
options *users.ItemMailFoldersItemMessagesDeltaRequestBuilderGetRequestConfiguration,
|
||||
) *users.ItemMailFoldersItemMessagesDeltaRequestBuilder {
|
||||
builder := gs.
|
||||
Client().
|
||||
Users().
|
||||
ByUserId(user).
|
||||
MailFolders().
|
||||
ByMailFolderId(containerID).
|
||||
Messages().
|
||||
Delta()
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
func (c Mail) NewMailDeltaPager(
|
||||
ctx context.Context,
|
||||
userID, containerID, oldDelta string,
|
||||
immutableIDs bool,
|
||||
) itemPager {
|
||||
config := &users.ItemMailFoldersItemMessagesDeltaRequestBuilderGetRequestConfiguration{
|
||||
QueryParameters: &users.ItemMailFoldersItemMessagesDeltaRequestBuilderGetQueryParameters{
|
||||
Select: idAnd("isRead"),
|
||||
},
|
||||
Headers: newPreferHeaders(preferPageSize(maxDeltaPageSize), preferImmutableIDs(immutableIDs)),
|
||||
}
|
||||
|
||||
var builder *users.ItemMailFoldersItemMessagesDeltaRequestBuilder
|
||||
|
||||
if len(oldDelta) > 0 {
|
||||
builder = users.NewItemMailFoldersItemMessagesDeltaRequestBuilder(oldDelta, c.Stable.Adapter())
|
||||
} else {
|
||||
builder = getMailDeltaBuilder(ctx, c.Stable, userID, containerID, config)
|
||||
}
|
||||
|
||||
return &mailDeltaPager{c.Stable, userID, containerID, builder, config}
|
||||
}
|
||||
|
||||
func (p *mailDeltaPager) getPage(ctx context.Context) (DeltaPageLinker, error) {
|
||||
page, err := p.builder.Get(ctx, p.options)
|
||||
if err != nil {
|
||||
return nil, graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
return page, nil
|
||||
}
|
||||
|
||||
func (p *mailDeltaPager) setNext(nextLink string) {
|
||||
p.builder = users.NewItemMailFoldersItemMessagesDeltaRequestBuilder(nextLink, p.gs.Adapter())
|
||||
}
|
||||
|
||||
func (p *mailDeltaPager) reset(ctx context.Context) {
|
||||
p.builder = p.gs.
|
||||
Client().
|
||||
Users().
|
||||
ByUserId(p.userID).
|
||||
MailFolders().
|
||||
ByMailFolderId(p.containerID).
|
||||
Messages().
|
||||
Delta()
|
||||
}
|
||||
|
||||
func (p *mailDeltaPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) {
|
||||
return toValues[models.Messageable](pl)
|
||||
}
|
||||
|
||||
func (c Mail) GetAddedAndRemovedItemIDs(
|
||||
ctx context.Context,
|
||||
userID, containerID, oldDelta string,
|
||||
immutableIDs bool,
|
||||
canMakeDeltaQueries bool,
|
||||
) ([]string, []string, DeltaUpdate, error) {
|
||||
ctx = clues.Add(
|
||||
ctx,
|
||||
"category", selectors.ExchangeMail,
|
||||
"container_id", containerID)
|
||||
|
||||
pager := c.NewMailPager(ctx, userID, containerID, immutableIDs)
|
||||
deltaPager := c.NewMailDeltaPager(ctx, userID, containerID, oldDelta, immutableIDs)
|
||||
|
||||
return getAddedAndRemovedItemIDs(ctx, c.Stable, pager, deltaPager, oldDelta, canMakeDeltaQueries)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Serialization
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
273
src/pkg/services/m365/api/mail_pager.go
Normal file
273
src/pkg/services/m365/api/mail_pager.go
Normal file
@ -0,0 +1,273 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/alcionai/clues"
|
||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||
"github.com/microsoftgraph/msgraph-sdk-go/users"
|
||||
|
||||
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||
"github.com/alcionai/corso/src/internal/m365/graph"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/selectors"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// container pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
type mailFolderPager struct {
|
||||
service graph.Servicer
|
||||
builder *users.ItemMailFoldersRequestBuilder
|
||||
}
|
||||
|
||||
func (c Mail) NewMailFolderPager(userID string) mailFolderPager {
|
||||
// v1.0 non delta /mailFolders endpoint does not return any of the nested folders
|
||||
rawURL := fmt.Sprintf(mailFoldersBetaURLTemplate, userID)
|
||||
builder := users.NewItemMailFoldersRequestBuilder(rawURL, c.Stable.Adapter())
|
||||
|
||||
return mailFolderPager{c.Stable, builder}
|
||||
}
|
||||
|
||||
func (p *mailFolderPager) getPage(ctx context.Context) (PageLinker, error) {
|
||||
page, err := p.builder.Get(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
return page, nil
|
||||
}
|
||||
|
||||
func (p *mailFolderPager) setNext(nextLink string) {
|
||||
p.builder = users.NewItemMailFoldersRequestBuilder(nextLink, p.service.Adapter())
|
||||
}
|
||||
|
||||
func (p *mailFolderPager) valuesIn(pl PageLinker) ([]models.MailFolderable, error) {
|
||||
// Ideally this should be `users.ItemMailFoldersResponseable`, but
|
||||
// that is not a thing as stable returns different result
|
||||
page, ok := pl.(models.MailFolderCollectionResponseable)
|
||||
if !ok {
|
||||
return nil, clues.New("converting to ItemMailFoldersResponseable")
|
||||
}
|
||||
|
||||
return page.GetValue(), nil
|
||||
}
|
||||
|
||||
// EnumerateContainers iterates through all of the users current
|
||||
// mail folders, converting each to a graph.CacheFolder, and calling
|
||||
// fn(cf) on each one.
|
||||
// Folder hierarchy is represented in its current state, and does
|
||||
// not contain historical data.
|
||||
func (c Mail) EnumerateContainers(
|
||||
ctx context.Context,
|
||||
userID, baseContainerID string,
|
||||
fn func(graph.CachedContainer) error,
|
||||
errs *fault.Bus,
|
||||
) error {
|
||||
el := errs.Local()
|
||||
pgr := c.NewMailFolderPager(userID)
|
||||
|
||||
for {
|
||||
if el.Failure() != nil {
|
||||
break
|
||||
}
|
||||
|
||||
page, err := pgr.getPage(ctx)
|
||||
if err != nil {
|
||||
return graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
resp, err := pgr.valuesIn(page)
|
||||
if err != nil {
|
||||
return graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
for _, fold := range resp {
|
||||
if el.Failure() != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if err := graph.CheckIDNameAndParentFolderID(fold); err != nil {
|
||||
errs.AddRecoverable(ctx, graph.Stack(ctx, err).Label(fault.LabelForceNoBackupCreation))
|
||||
continue
|
||||
}
|
||||
|
||||
fctx := clues.Add(
|
||||
ctx,
|
||||
"container_id", ptr.Val(fold.GetId()),
|
||||
"container_name", ptr.Val(fold.GetDisplayName()))
|
||||
|
||||
temp := graph.NewCacheFolder(fold, nil, nil)
|
||||
if err := fn(&temp); err != nil {
|
||||
errs.AddRecoverable(ctx, graph.Stack(fctx, err).Label(fault.LabelForceNoBackupCreation))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
link, ok := ptr.ValOK(page.GetOdataNextLink())
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
pgr.setNext(link)
|
||||
}
|
||||
|
||||
return el.Failure()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// item pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
var _ itemPager = &mailPager{}
|
||||
|
||||
type mailPager struct {
|
||||
gs graph.Servicer
|
||||
builder *users.ItemMailFoldersItemMessagesRequestBuilder
|
||||
options *users.ItemMailFoldersItemMessagesRequestBuilderGetRequestConfiguration
|
||||
}
|
||||
|
||||
func (c Mail) NewMailPager(
|
||||
ctx context.Context,
|
||||
userID, containerID string,
|
||||
immutableIDs bool,
|
||||
) itemPager {
|
||||
config := &users.ItemMailFoldersItemMessagesRequestBuilderGetRequestConfiguration{
|
||||
QueryParameters: &users.ItemMailFoldersItemMessagesRequestBuilderGetQueryParameters{
|
||||
Select: idAnd("isRead"),
|
||||
},
|
||||
Headers: newPreferHeaders(preferPageSize(maxNonDeltaPageSize), preferImmutableIDs(immutableIDs)),
|
||||
}
|
||||
|
||||
builder := c.Stable.
|
||||
Client().
|
||||
Users().
|
||||
ByUserId(userID).
|
||||
MailFolders().
|
||||
ByMailFolderId(containerID).
|
||||
Messages()
|
||||
|
||||
return &mailPager{c.Stable, builder, config}
|
||||
}
|
||||
|
||||
func (p *mailPager) getPage(ctx context.Context) (DeltaPageLinker, error) {
|
||||
page, err := p.builder.Get(ctx, p.options)
|
||||
if err != nil {
|
||||
return nil, graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
return EmptyDeltaLinker[models.Messageable]{PageLinkValuer: page}, nil
|
||||
}
|
||||
|
||||
func (p *mailPager) setNext(nextLink string) {
|
||||
p.builder = users.NewItemMailFoldersItemMessagesRequestBuilder(nextLink, p.gs.Adapter())
|
||||
}
|
||||
|
||||
// non delta pagers don't have reset
|
||||
func (p *mailPager) reset(context.Context) {}
|
||||
|
||||
func (p *mailPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) {
|
||||
return toValues[models.Messageable](pl)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// delta item pager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
var _ itemPager = &mailDeltaPager{}
|
||||
|
||||
type mailDeltaPager struct {
|
||||
gs graph.Servicer
|
||||
userID string
|
||||
containerID string
|
||||
builder *users.ItemMailFoldersItemMessagesDeltaRequestBuilder
|
||||
options *users.ItemMailFoldersItemMessagesDeltaRequestBuilderGetRequestConfiguration
|
||||
}
|
||||
|
||||
func getMailDeltaBuilder(
|
||||
ctx context.Context,
|
||||
gs graph.Servicer,
|
||||
user, containerID string,
|
||||
options *users.ItemMailFoldersItemMessagesDeltaRequestBuilderGetRequestConfiguration,
|
||||
) *users.ItemMailFoldersItemMessagesDeltaRequestBuilder {
|
||||
builder := gs.
|
||||
Client().
|
||||
Users().
|
||||
ByUserId(user).
|
||||
MailFolders().
|
||||
ByMailFolderId(containerID).
|
||||
Messages().
|
||||
Delta()
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
func (c Mail) NewMailDeltaPager(
|
||||
ctx context.Context,
|
||||
userID, containerID, oldDelta string,
|
||||
immutableIDs bool,
|
||||
) itemPager {
|
||||
config := &users.ItemMailFoldersItemMessagesDeltaRequestBuilderGetRequestConfiguration{
|
||||
QueryParameters: &users.ItemMailFoldersItemMessagesDeltaRequestBuilderGetQueryParameters{
|
||||
Select: idAnd("isRead"),
|
||||
},
|
||||
Headers: newPreferHeaders(preferPageSize(maxDeltaPageSize), preferImmutableIDs(immutableIDs)),
|
||||
}
|
||||
|
||||
var builder *users.ItemMailFoldersItemMessagesDeltaRequestBuilder
|
||||
|
||||
if len(oldDelta) > 0 {
|
||||
builder = users.NewItemMailFoldersItemMessagesDeltaRequestBuilder(oldDelta, c.Stable.Adapter())
|
||||
} else {
|
||||
builder = getMailDeltaBuilder(ctx, c.Stable, userID, containerID, config)
|
||||
}
|
||||
|
||||
return &mailDeltaPager{c.Stable, userID, containerID, builder, config}
|
||||
}
|
||||
|
||||
func (p *mailDeltaPager) getPage(ctx context.Context) (DeltaPageLinker, error) {
|
||||
page, err := p.builder.Get(ctx, p.options)
|
||||
if err != nil {
|
||||
return nil, graph.Stack(ctx, err)
|
||||
}
|
||||
|
||||
return page, nil
|
||||
}
|
||||
|
||||
func (p *mailDeltaPager) setNext(nextLink string) {
|
||||
p.builder = users.NewItemMailFoldersItemMessagesDeltaRequestBuilder(nextLink, p.gs.Adapter())
|
||||
}
|
||||
|
||||
func (p *mailDeltaPager) reset(ctx context.Context) {
|
||||
p.builder = p.gs.
|
||||
Client().
|
||||
Users().
|
||||
ByUserId(p.userID).
|
||||
MailFolders().
|
||||
ByMailFolderId(p.containerID).
|
||||
Messages().
|
||||
Delta()
|
||||
}
|
||||
|
||||
func (p *mailDeltaPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) {
|
||||
return toValues[models.Messageable](pl)
|
||||
}
|
||||
|
||||
func (c Mail) GetAddedAndRemovedItemIDs(
|
||||
ctx context.Context,
|
||||
userID, containerID, oldDelta string,
|
||||
immutableIDs bool,
|
||||
canMakeDeltaQueries bool,
|
||||
) ([]string, []string, DeltaUpdate, error) {
|
||||
ctx = clues.Add(
|
||||
ctx,
|
||||
"category", selectors.ExchangeMail,
|
||||
"container_id", containerID)
|
||||
|
||||
pager := c.NewMailPager(ctx, userID, containerID, immutableIDs)
|
||||
deltaPager := c.NewMailDeltaPager(ctx, userID, containerID, oldDelta, immutableIDs)
|
||||
|
||||
return getAddedAndRemovedItemIDs(ctx, c.Stable, pager, deltaPager, oldDelta, canMakeDeltaQueries)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user