From 3bcc4053275397f03ae9adf58f4a3309381c467f Mon Sep 17 00:00:00 2001 From: Keepers Date: Wed, 21 Jun 2023 10:43:33 -0600 Subject: [PATCH] add generic item enumeratiton iface to api (#3628) Adds a generic item enumeration pager to the api's item_pager set. This compliments the more focused getIdsAndAdditional with something that's oriented towards single-folder, non-delta enumeration. --- #### Does this PR need a docs update or release note? - [x] :no_entry: No #### Type of change - [ ] :sunflower: Feature #### Issue(s) * #3562 #### Test Plan - [x] :zap: Unit test - [x] :green_heart: E2E --- src/pkg/services/m365/api/contacts_pager.go | 74 +++++--- src/pkg/services/m365/api/events_pager.go | 74 +++++--- src/pkg/services/m365/api/item_pager.go | 83 +++++++-- src/pkg/services/m365/api/item_pager_test.go | 167 ++++++++++++++++--- src/pkg/services/m365/api/mail_pager.go | 72 +++++--- 5 files changed, 363 insertions(+), 107 deletions(-) diff --git a/src/pkg/services/m365/api/contacts_pager.go b/src/pkg/services/m365/api/contacts_pager.go index da79b3ce9..a57873fe8 100644 --- a/src/pkg/services/m365/api/contacts_pager.go +++ b/src/pkg/services/m365/api/contacts_pager.go @@ -90,19 +90,51 @@ func (c Contacts) EnumerateContainers( // item pager // --------------------------------------------------------------------------- -var _ itemPager = &contactPager{} +var _ itemPager[models.Contactable] = &contactsPager{} -type contactPager struct { +type contactsPager struct { + // TODO(rkeeprs) +} + +func (c Contacts) NewContactsPager() itemPager[models.Contactable] { + // TODO(rkeepers) + return nil +} + +//lint:ignore U1000 False Positive +func (p *contactsPager) getPage(ctx context.Context) (PageLinker, error) { + // TODO(rkeepers) + return nil, nil +} + +//lint:ignore U1000 False Positive +func (p *contactsPager) setNext(nextLink string) { + // TODO(rkeepers) +} + +//lint:ignore U1000 False Positive +func (p *contactsPager) valuesIn(pl PageLinker) ([]models.Contactable, error) { + // TODO(rkeepers) + return nil, nil +} + +// --------------------------------------------------------------------------- +// item ID pager +// --------------------------------------------------------------------------- + +var _ itemIDPager = &contactIDPager{} + +type contactIDPager struct { gs graph.Servicer builder *users.ItemContactFoldersItemContactsRequestBuilder options *users.ItemContactFoldersItemContactsRequestBuilderGetRequestConfiguration } -func (c Contacts) NewContactPager( +func (c Contacts) NewContactIDsPager( ctx context.Context, userID, containerID string, immutableIDs bool, -) itemPager { +) itemIDPager { config := &users.ItemContactFoldersItemContactsRequestBuilderGetRequestConfiguration{ QueryParameters: &users.ItemContactFoldersItemContactsRequestBuilderGetQueryParameters{ Select: idAnd(parentFolderID), @@ -118,10 +150,10 @@ func (c Contacts) NewContactPager( ByContactFolderId(containerID). Contacts() - return &contactPager{c.Stable, builder, config} + return &contactIDPager{c.Stable, builder, config} } -func (p *contactPager) getPage(ctx context.Context) (DeltaPageLinker, error) { +func (p *contactIDPager) getPage(ctx context.Context) (DeltaPageLinker, error) { resp, err := p.builder.Get(ctx, p.options) if err != nil { return nil, graph.Stack(ctx, err) @@ -130,24 +162,24 @@ func (p *contactPager) getPage(ctx context.Context) (DeltaPageLinker, error) { return EmptyDeltaLinker[models.Contactable]{PageLinkValuer: resp}, nil } -func (p *contactPager) setNext(nextLink string) { +func (p *contactIDPager) 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 *contactIDPager) reset(context.Context) {} -func (p *contactPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) { +func (p *contactIDPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) { return toValues[models.Contactable](pl) } // --------------------------------------------------------------------------- -// delta item pager +// delta item ID pager // --------------------------------------------------------------------------- -var _ itemPager = &contactDeltaPager{} +var _ itemIDPager = &contactDeltaIDPager{} -type contactDeltaPager struct { +type contactDeltaIDPager struct { gs graph.Servicer userID string containerID string @@ -165,11 +197,11 @@ func getContactDeltaBuilder( return builder } -func (c Contacts) NewContactDeltaPager( +func (c Contacts) NewContactDeltaIDsPager( ctx context.Context, userID, containerID, oldDelta string, immutableIDs bool, -) itemPager { +) itemIDPager { options := &users.ItemContactFoldersItemContactsDeltaRequestBuilderGetRequestConfiguration{ QueryParameters: &users.ItemContactFoldersItemContactsDeltaRequestBuilderGetQueryParameters{ Select: idAnd(parentFolderID), @@ -184,10 +216,10 @@ func (c Contacts) NewContactDeltaPager( builder = getContactDeltaBuilder(ctx, c.Stable, userID, containerID, options) } - return &contactDeltaPager{c.Stable, userID, containerID, builder, options} + return &contactDeltaIDPager{c.Stable, userID, containerID, builder, options} } -func (p *contactDeltaPager) getPage(ctx context.Context) (DeltaPageLinker, error) { +func (p *contactDeltaIDPager) getPage(ctx context.Context) (DeltaPageLinker, error) { resp, err := p.builder.Get(ctx, p.options) if err != nil { return nil, graph.Stack(ctx, err) @@ -196,15 +228,15 @@ func (p *contactDeltaPager) getPage(ctx context.Context) (DeltaPageLinker, error return resp, nil } -func (p *contactDeltaPager) setNext(nextLink string) { +func (p *contactDeltaIDPager) setNext(nextLink string) { p.builder = users.NewItemContactFoldersItemContactsDeltaRequestBuilder(nextLink, p.gs.Adapter()) } -func (p *contactDeltaPager) reset(ctx context.Context) { +func (p *contactDeltaIDPager) reset(ctx context.Context) { p.builder = getContactDeltaBuilder(ctx, p.gs, p.userID, p.containerID, p.options) } -func (p *contactDeltaPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) { +func (p *contactDeltaIDPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) { return toValues[models.Contactable](pl) } @@ -219,8 +251,8 @@ func (c Contacts) GetAddedAndRemovedItemIDs( "category", selectors.ExchangeContact, "container_id", containerID) - pager := c.NewContactPager(ctx, userID, containerID, immutableIDs) - deltaPager := c.NewContactDeltaPager(ctx, userID, containerID, oldDelta, immutableIDs) + pager := c.NewContactIDsPager(ctx, userID, containerID, immutableIDs) + deltaPager := c.NewContactDeltaIDsPager(ctx, userID, containerID, oldDelta, immutableIDs) return getAddedAndRemovedItemIDs(ctx, c.Stable, pager, deltaPager, oldDelta, canMakeDeltaQueries) } diff --git a/src/pkg/services/m365/api/events_pager.go b/src/pkg/services/m365/api/events_pager.go index bb390a288..fb0354cde 100644 --- a/src/pkg/services/m365/api/events_pager.go +++ b/src/pkg/services/m365/api/events_pager.go @@ -98,19 +98,51 @@ func (c Events) EnumerateContainers( // item pager // --------------------------------------------------------------------------- -var _ itemPager = &eventPager{} +var _ itemPager[models.Eventable] = &eventsPager{} -type eventPager struct { +type eventsPager struct { + // TODO(rkeeprs) +} + +func (c Events) NewEventsPager() itemPager[models.Eventable] { + // TODO(rkeepers) + return nil +} + +//lint:ignore U1000 False Positive +func (p *eventsPager) getPage(ctx context.Context) (PageLinker, error) { + // TODO(rkeepers) + return nil, nil +} + +//lint:ignore U1000 False Positive +func (p *eventsPager) setNext(nextLink string) { + // TODO(rkeepers) +} + +//lint:ignore U1000 False Positive +func (p *eventsPager) valuesIn(pl PageLinker) ([]models.Eventable, error) { + // TODO(rkeepers) + return nil, nil +} + +// --------------------------------------------------------------------------- +// item ID pager +// --------------------------------------------------------------------------- + +var _ itemIDPager = &eventIDPager{} + +type eventIDPager struct { gs graph.Servicer builder *users.ItemCalendarsItemEventsRequestBuilder options *users.ItemCalendarsItemEventsRequestBuilderGetRequestConfiguration } -func (c Events) NewEventPager( +func (c Events) NewEventIDsPager( ctx context.Context, userID, containerID string, immutableIDs bool, -) (itemPager, error) { +) (itemIDPager, error) { options := &users.ItemCalendarsItemEventsRequestBuilderGetRequestConfiguration{ Headers: newPreferHeaders(preferPageSize(maxNonDeltaPageSize), preferImmutableIDs(immutableIDs)), } @@ -123,10 +155,10 @@ func (c Events) NewEventPager( ByCalendarId(containerID). Events() - return &eventPager{c.Stable, builder, options}, nil + return &eventIDPager{c.Stable, builder, options}, nil } -func (p *eventPager) getPage(ctx context.Context) (DeltaPageLinker, error) { +func (p *eventIDPager) getPage(ctx context.Context) (DeltaPageLinker, error) { resp, err := p.builder.Get(ctx, p.options) if err != nil { return nil, graph.Stack(ctx, err) @@ -135,24 +167,24 @@ func (p *eventPager) getPage(ctx context.Context) (DeltaPageLinker, error) { return EmptyDeltaLinker[models.Eventable]{PageLinkValuer: resp}, nil } -func (p *eventPager) setNext(nextLink string) { +func (p *eventIDPager) 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 *eventIDPager) reset(context.Context) {} -func (p *eventPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) { +func (p *eventIDPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) { return toValues[models.Eventable](pl) } // --------------------------------------------------------------------------- -// delta item pager +// delta item ID pager // --------------------------------------------------------------------------- -var _ itemPager = &eventDeltaPager{} +var _ itemIDPager = &eventDeltaIDPager{} -type eventDeltaPager struct { +type eventDeltaIDPager struct { gs graph.Servicer userID string containerID string @@ -160,11 +192,11 @@ type eventDeltaPager struct { options *users.ItemCalendarsItemEventsDeltaRequestBuilderGetRequestConfiguration } -func (c Events) NewEventDeltaPager( +func (c Events) NewEventDeltaIDsPager( ctx context.Context, userID, containerID, oldDelta string, immutableIDs bool, -) (itemPager, error) { +) (itemIDPager, error) { options := &users.ItemCalendarsItemEventsDeltaRequestBuilderGetRequestConfiguration{ Headers: newPreferHeaders(preferPageSize(maxDeltaPageSize), preferImmutableIDs(immutableIDs)), } @@ -177,7 +209,7 @@ func (c Events) NewEventDeltaPager( builder = users.NewItemCalendarsItemEventsDeltaRequestBuilder(oldDelta, c.Stable.Adapter()) } - return &eventDeltaPager{c.Stable, userID, containerID, builder, options}, nil + return &eventDeltaIDPager{c.Stable, userID, containerID, builder, options}, nil } func getEventDeltaBuilder( @@ -200,7 +232,7 @@ func getEventDeltaBuilder( return builder } -func (p *eventDeltaPager) getPage(ctx context.Context) (DeltaPageLinker, error) { +func (p *eventDeltaIDPager) getPage(ctx context.Context) (DeltaPageLinker, error) { resp, err := p.builder.Get(ctx, p.options) if err != nil { return nil, graph.Stack(ctx, err) @@ -209,15 +241,15 @@ func (p *eventDeltaPager) getPage(ctx context.Context) (DeltaPageLinker, error) return resp, nil } -func (p *eventDeltaPager) setNext(nextLink string) { +func (p *eventDeltaIDPager) setNext(nextLink string) { p.builder = users.NewItemCalendarsItemEventsDeltaRequestBuilder(nextLink, p.gs.Adapter()) } -func (p *eventDeltaPager) reset(ctx context.Context) { +func (p *eventDeltaIDPager) reset(ctx context.Context) { p.builder = getEventDeltaBuilder(ctx, p.gs, p.userID, p.containerID, p.options) } -func (p *eventDeltaPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) { +func (p *eventDeltaIDPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) { return toValues[models.Eventable](pl) } @@ -229,12 +261,12 @@ func (c Events) GetAddedAndRemovedItemIDs( ) ([]string, []string, DeltaUpdate, error) { ctx = clues.Add(ctx, "container_id", containerID) - pager, err := c.NewEventPager(ctx, userID, containerID, immutableIDs) + pager, err := c.NewEventIDsPager(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) + deltaPager, err := c.NewEventDeltaIDsPager(ctx, userID, containerID, oldDelta, immutableIDs) if err != nil { return nil, nil, DeltaUpdate{}, graph.Wrap(ctx, err, "creating delta pager") } diff --git a/src/pkg/services/m365/api/item_pager.go b/src/pkg/services/m365/api/item_pager.go index 00a93ea13..fca130c56 100644 --- a/src/pkg/services/m365/api/item_pager.go +++ b/src/pkg/services/m365/api/item_pager.go @@ -61,27 +61,57 @@ func (e EmptyDeltaLinker[T]) GetValue() []T { } // --------------------------------------------------------------------------- -// generic handler for paging item ids in a container +// generic handler for non-delta item paging in a container // --------------------------------------------------------------------------- -type itemPager interface { +type itemPager[T any] interface { // getPage get a page with the specified options from graph - getPage(context.Context) (DeltaPageLinker, error) + getPage(context.Context) (PageLinker, error) // setNext is used to pass in the next url got from graph setNext(string) - // reset is used to clear delta url in delta pagers. When - // reset is called, we reset the state(delta url) that we - // currently have and start a new delta query without the token. - reset(context.Context) // valuesIn gets us the values in a page - valuesIn(PageLinker) ([]getIDAndAddtler, error) + valuesIn(PageLinker) ([]T, error) } -type getIDAndAddtler interface { - GetId() *string - GetAdditionalData() map[string]any +func enumerateItems[T any]( + ctx context.Context, + pager itemPager[T], +) ([]T, error) { + var ( + result = make([]T, 0) + // stubbed initial value to ensure we enter the loop. + nextLink = "do-while" + ) + + for len(nextLink) > 0 { + // get the next page of data, check for standard errors + resp, err := pager.getPage(ctx) + if err != nil { + return nil, graph.Stack(ctx, err) + } + + // each category type responds with a different interface, but all + // of them comply with GetValue, which is where we'll get our item data. + items, err := pager.valuesIn(resp) + if err != nil { + return nil, graph.Stack(ctx, err) + } + + result = append(result, items...) + nextLink = NextLink(resp) + + pager.setNext(nextLink) + } + + logger.Ctx(ctx).Infow("completed enumeration", "count", len(result)) + + return result, nil } +// --------------------------------------------------------------------------- +// generic handler for delta-based ittem paging in a container +// --------------------------------------------------------------------------- + // uses a models interface compliant with { GetValues() []T } // to transform its results into a slice of getIDer interfaces. // Generics used here to handle the variation of msoft interfaces @@ -110,16 +140,34 @@ func toValues[T any](a any) ([]getIDAndAddtler, error) { return r, nil } +type itemIDPager interface { + // getPage get a page with the specified options from graph + getPage(context.Context) (DeltaPageLinker, error) + // setNext is used to pass in the next url got from graph + setNext(string) + // reset is used to clear delta url in delta pagers. When + // reset is called, we reset the state(delta url) that we + // currently have and start a new delta query without the token. + reset(context.Context) + // valuesIn gets us the values in a page + valuesIn(PageLinker) ([]getIDAndAddtler, error) +} + +type getIDAndAddtler interface { + GetId() *string + GetAdditionalData() map[string]any +} + func getAddedAndRemovedItemIDs( ctx context.Context, service graph.Servicer, - pager itemPager, - deltaPager itemPager, + pager itemIDPager, + deltaPager itemIDPager, oldDelta string, canMakeDeltaQueries bool, ) ([]string, []string, DeltaUpdate, error) { var ( - pgr itemPager + pgr itemIDPager resetDelta bool ) @@ -161,17 +209,16 @@ func getAddedAndRemovedItemIDs( // generic controller for retrieving all item ids in a container. func getItemsAddedAndRemovedFromContainer( ctx context.Context, - pager itemPager, + pager itemIDPager, ) ([]string, []string, string, error) { var ( addedIDs = []string{} removedIDs = []string{} deltaURL string + itemCount int + page int ) - itemCount := 0 - page := 0 - for { // get the next page of data, check for standard errors resp, err := pager.getPage(ctx) diff --git a/src/pkg/services/m365/api/item_pager_test.go b/src/pkg/services/m365/api/item_pager_test.go index 4c6dbfbeb..0f935e46a 100644 --- a/src/pkg/services/m365/api/item_pager_test.go +++ b/src/pkg/services/m365/api/item_pager_test.go @@ -5,6 +5,7 @@ import ( "strings" "testing" + "github.com/alcionai/clues" "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -19,6 +20,8 @@ import ( // mock impls & stubs // --------------------------------------------------------------------------- +// next and delta links + type nextLink struct { nextLink *string } @@ -36,6 +39,8 @@ func (l deltaNextLink) GetOdataDeltaLink() *string { return l.deltaLink } +// mock values + type testPagerValue struct { id string removed bool @@ -50,6 +55,8 @@ func (v testPagerValue) GetAdditionalData() map[string]any { return map[string]any{} } +// mock page + type testPage struct{} func (p testPage) GetOdataNextLink() *string { @@ -62,9 +69,35 @@ func (p testPage) GetOdataDeltaLink() *string { return ptr.To("") } -var _ itemPager = &testPager{} +// mock item pager + +var _ itemPager[any] = &testPager{} type testPager struct { + t *testing.T + items []any + pageErr error + valuesErr error +} + +//lint:ignore U1000 False Positive +func (p *testPager) getPage(ctx context.Context) (PageLinker, error) { + return testPage{}, p.pageErr +} + +//lint:ignore U1000 False Positive +func (p *testPager) setNext(nextLink string) {} + +//lint:ignore U1000 False Positive +func (p *testPager) valuesIn(pl PageLinker) ([]any, error) { + return p.items, p.valuesErr +} + +// mock id pager + +var _ itemIDPager = &testIDsPager{} + +type testIDsPager struct { t *testing.T added []string removed []string @@ -72,7 +105,7 @@ type testPager struct { needsReset bool } -func (p *testPager) getPage(ctx context.Context) (DeltaPageLinker, error) { +func (p *testIDsPager) getPage(ctx context.Context) (DeltaPageLinker, error) { if p.errorCode != "" { ierr := odataerrors.NewMainError() ierr.SetCode(&p.errorCode) @@ -85,8 +118,8 @@ func (p *testPager) getPage(ctx context.Context) (DeltaPageLinker, error) { return testPage{}, nil } -func (p *testPager) setNext(string) {} -func (p *testPager) reset(context.Context) { +func (p *testIDsPager) setNext(string) {} +func (p *testIDsPager) reset(context.Context) { if !p.needsReset { require.Fail(p.t, "reset should not be called") } @@ -95,7 +128,7 @@ func (p *testPager) reset(context.Context) { p.errorCode = "" } -func (p *testPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) { +func (p *testIDsPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) { items := []getIDAndAddtler{} for _, id := range p.added { @@ -121,11 +154,83 @@ func TestItemPagerUnitSuite(t *testing.T) { suite.Run(t, &ItemPagerUnitSuite{Suite: tester.NewUnitSuite(t)}) } +func (suite *ItemPagerUnitSuite) TestEnumerateItems() { + tests := []struct { + name string + getPager func(*testing.T, context.Context) itemPager[any] + expect []any + expectErr require.ErrorAssertionFunc + }{ + { + name: "happy path", + getPager: func( + t *testing.T, + ctx context.Context, + ) itemPager[any] { + return &testPager{ + t: t, + items: []any{"foo", "bar"}, + } + }, + expect: []any{"foo", "bar"}, + expectErr: require.NoError, + }, + { + name: "get values err", + getPager: func( + t *testing.T, + ctx context.Context, + ) itemPager[any] { + return &testPager{ + t: t, + valuesErr: assert.AnError, + } + }, + expect: nil, + expectErr: require.Error, + }, + { + name: "next page err", + getPager: func( + t *testing.T, + ctx context.Context, + ) itemPager[any] { + return &testPager{ + t: t, + pageErr: assert.AnError, + } + }, + expect: nil, + expectErr: require.Error, + }, + } + + for _, test := range tests { + suite.Run(test.name, func() { + t := suite.T() + + ctx, flush := tester.NewContext(t) + defer flush() + + result, err := enumerateItems[any](ctx, test.getPager(t, ctx)) + test.expectErr(t, err, clues.ToCore(err)) + + require.EqualValues(t, test.expect, result) + }) + } +} + func (suite *ItemPagerUnitSuite) TestGetAddedAndRemovedItemIDs() { tests := []struct { - name string - pagerGetter func(context.Context, graph.Servicer, string, string, bool) (itemPager, error) - deltaPagerGetter func(context.Context, graph.Servicer, string, string, string, bool) (itemPager, error) + name string + pagerGetter func(*testing.T, context.Context, graph.Servicer, string, string, bool) (itemIDPager, error) + deltaPagerGetter func( + *testing.T, + context.Context, + graph.Servicer, + string, string, string, + bool, + ) (itemIDPager, error) added []string removed []string deltaUpdate DeltaUpdate @@ -135,25 +240,27 @@ func (suite *ItemPagerUnitSuite) TestGetAddedAndRemovedItemIDs() { { name: "no prev delta", pagerGetter: func( + t *testing.T, ctx context.Context, gs graph.Servicer, user string, directory string, immutableIDs bool, - ) (itemPager, error) { + ) (itemIDPager, error) { // this should not be called return nil, assert.AnError }, deltaPagerGetter: func( + t *testing.T, ctx context.Context, gs graph.Servicer, user string, directory string, delta string, immutableIDs bool, - ) (itemPager, error) { - return &testPager{ - t: suite.T(), + ) (itemIDPager, error) { + return &testIDsPager{ + t: t, added: []string{"uno", "dos"}, removed: []string{"tres", "quatro"}, }, nil @@ -166,25 +273,27 @@ func (suite *ItemPagerUnitSuite) TestGetAddedAndRemovedItemIDs() { { name: "with prev delta", pagerGetter: func( + t *testing.T, ctx context.Context, gs graph.Servicer, user string, directory string, immutableIDs bool, - ) (itemPager, error) { + ) (itemIDPager, error) { // this should not be called return nil, assert.AnError }, deltaPagerGetter: func( + t *testing.T, ctx context.Context, gs graph.Servicer, user string, directory string, delta string, immutableIDs bool, - ) (itemPager, error) { - return &testPager{ - t: suite.T(), + ) (itemIDPager, error) { + return &testIDsPager{ + t: t, added: []string{"uno", "dos"}, removed: []string{"tres", "quatro"}, }, nil @@ -198,25 +307,27 @@ func (suite *ItemPagerUnitSuite) TestGetAddedAndRemovedItemIDs() { { name: "delta expired", pagerGetter: func( + t *testing.T, ctx context.Context, gs graph.Servicer, user string, directory string, immutableIDs bool, - ) (itemPager, error) { + ) (itemIDPager, error) { // this should not be called return nil, assert.AnError }, deltaPagerGetter: func( + t *testing.T, ctx context.Context, gs graph.Servicer, user string, directory string, delta string, immutableIDs bool, - ) (itemPager, error) { - return &testPager{ - t: suite.T(), + ) (itemIDPager, error) { + return &testIDsPager{ + t: t, added: []string{"uno", "dos"}, removed: []string{"tres", "quatro"}, errorCode: "SyncStateNotFound", @@ -232,27 +343,29 @@ func (suite *ItemPagerUnitSuite) TestGetAddedAndRemovedItemIDs() { { name: "quota exceeded", pagerGetter: func( + t *testing.T, ctx context.Context, gs graph.Servicer, user string, directory string, immutableIDs bool, - ) (itemPager, error) { - return &testPager{ - t: suite.T(), + ) (itemIDPager, error) { + return &testIDsPager{ + t: t, added: []string{"uno", "dos"}, removed: []string{"tres", "quatro"}, }, nil }, deltaPagerGetter: func( + t *testing.T, ctx context.Context, gs graph.Servicer, user string, directory string, delta string, immutableIDs bool, - ) (itemPager, error) { - return &testPager{errorCode: "ErrorQuotaExceeded"}, nil + ) (itemIDPager, error) { + return &testIDsPager{errorCode: "ErrorQuotaExceeded"}, nil }, added: []string{"uno", "dos"}, removed: []string{"tres", "quatro"}, @@ -268,8 +381,8 @@ func (suite *ItemPagerUnitSuite) TestGetAddedAndRemovedItemIDs() { ctx, flush := tester.NewContext(t) defer flush() - pager, _ := tt.pagerGetter(ctx, graph.Service{}, "user", "directory", false) - deltaPager, _ := tt.deltaPagerGetter(ctx, graph.Service{}, "user", "directory", tt.delta, false) + pager, _ := tt.pagerGetter(t, ctx, graph.Service{}, "user", "directory", false) + deltaPager, _ := tt.deltaPagerGetter(t, ctx, graph.Service{}, "user", "directory", tt.delta, false) added, removed, deltaUpdate, err := getAddedAndRemovedItemIDs( ctx, diff --git a/src/pkg/services/m365/api/mail_pager.go b/src/pkg/services/m365/api/mail_pager.go index 71ce09663..9dc2c470c 100644 --- a/src/pkg/services/m365/api/mail_pager.go +++ b/src/pkg/services/m365/api/mail_pager.go @@ -121,19 +121,51 @@ func (c Mail) EnumerateContainers( // item pager // --------------------------------------------------------------------------- -var _ itemPager = &mailPager{} +var _ itemPager[models.Messageable] = &mailPager{} type mailPager struct { + // TODO(rkeeprs) +} + +func (c Mail) NewMailPager() itemPager[models.Messageable] { + // TODO(rkeepers) + return nil +} + +//lint:ignore U1000 False Positive +func (p *mailPager) getPage(ctx context.Context) (PageLinker, error) { + // TODO(rkeepers) + return nil, nil +} + +//lint:ignore U1000 False Positive +func (p *mailPager) setNext(nextLink string) { + // TODO(rkeepers) +} + +//lint:ignore U1000 False Positive +func (p *mailPager) valuesIn(pl PageLinker) ([]models.Messageable, error) { + // TODO(rkeepers) + return nil, nil +} + +// --------------------------------------------------------------------------- +// item ID pager +// --------------------------------------------------------------------------- + +var _ itemIDPager = &mailIDPager{} + +type mailIDPager struct { gs graph.Servicer builder *users.ItemMailFoldersItemMessagesRequestBuilder options *users.ItemMailFoldersItemMessagesRequestBuilderGetRequestConfiguration } -func (c Mail) NewMailPager( +func (c Mail) NewMailIDsPager( ctx context.Context, userID, containerID string, immutableIDs bool, -) itemPager { +) itemIDPager { config := &users.ItemMailFoldersItemMessagesRequestBuilderGetRequestConfiguration{ QueryParameters: &users.ItemMailFoldersItemMessagesRequestBuilderGetQueryParameters{ Select: idAnd("isRead"), @@ -149,10 +181,10 @@ func (c Mail) NewMailPager( ByMailFolderId(containerID). Messages() - return &mailPager{c.Stable, builder, config} + return &mailIDPager{c.Stable, builder, config} } -func (p *mailPager) getPage(ctx context.Context) (DeltaPageLinker, error) { +func (p *mailIDPager) getPage(ctx context.Context) (DeltaPageLinker, error) { page, err := p.builder.Get(ctx, p.options) if err != nil { return nil, graph.Stack(ctx, err) @@ -161,24 +193,24 @@ func (p *mailPager) getPage(ctx context.Context) (DeltaPageLinker, error) { return EmptyDeltaLinker[models.Messageable]{PageLinkValuer: page}, nil } -func (p *mailPager) setNext(nextLink string) { +func (p *mailIDPager) 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 *mailIDPager) reset(context.Context) {} -func (p *mailPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) { +func (p *mailIDPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) { return toValues[models.Messageable](pl) } // --------------------------------------------------------------------------- -// delta item pager +// delta item ID pager // --------------------------------------------------------------------------- -var _ itemPager = &mailDeltaPager{} +var _ itemIDPager = &mailDeltaIDPager{} -type mailDeltaPager struct { +type mailDeltaIDPager struct { gs graph.Servicer userID string containerID string @@ -204,11 +236,11 @@ func getMailDeltaBuilder( return builder } -func (c Mail) NewMailDeltaPager( +func (c Mail) NewMailDeltaIDsPager( ctx context.Context, userID, containerID, oldDelta string, immutableIDs bool, -) itemPager { +) itemIDPager { config := &users.ItemMailFoldersItemMessagesDeltaRequestBuilderGetRequestConfiguration{ QueryParameters: &users.ItemMailFoldersItemMessagesDeltaRequestBuilderGetQueryParameters{ Select: idAnd("isRead"), @@ -224,10 +256,10 @@ func (c Mail) NewMailDeltaPager( builder = getMailDeltaBuilder(ctx, c.Stable, userID, containerID, config) } - return &mailDeltaPager{c.Stable, userID, containerID, builder, config} + return &mailDeltaIDPager{c.Stable, userID, containerID, builder, config} } -func (p *mailDeltaPager) getPage(ctx context.Context) (DeltaPageLinker, error) { +func (p *mailDeltaIDPager) getPage(ctx context.Context) (DeltaPageLinker, error) { page, err := p.builder.Get(ctx, p.options) if err != nil { return nil, graph.Stack(ctx, err) @@ -236,11 +268,11 @@ func (p *mailDeltaPager) getPage(ctx context.Context) (DeltaPageLinker, error) { return page, nil } -func (p *mailDeltaPager) setNext(nextLink string) { +func (p *mailDeltaIDPager) setNext(nextLink string) { p.builder = users.NewItemMailFoldersItemMessagesDeltaRequestBuilder(nextLink, p.gs.Adapter()) } -func (p *mailDeltaPager) reset(ctx context.Context) { +func (p *mailDeltaIDPager) reset(ctx context.Context) { p.builder = p.gs. Client(). Users(). @@ -251,7 +283,7 @@ func (p *mailDeltaPager) reset(ctx context.Context) { Delta() } -func (p *mailDeltaPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) { +func (p *mailDeltaIDPager) valuesIn(pl PageLinker) ([]getIDAndAddtler, error) { return toValues[models.Messageable](pl) } @@ -266,8 +298,8 @@ func (c Mail) GetAddedAndRemovedItemIDs( "category", selectors.ExchangeMail, "container_id", containerID) - pager := c.NewMailPager(ctx, userID, containerID, immutableIDs) - deltaPager := c.NewMailDeltaPager(ctx, userID, containerID, oldDelta, immutableIDs) + pager := c.NewMailIDsPager(ctx, userID, containerID, immutableIDs) + deltaPager := c.NewMailDeltaIDsPager(ctx, userID, containerID, oldDelta, immutableIDs) return getAddedAndRemovedItemIDs(ctx, c.Stable, pager, deltaPager, oldDelta, canMakeDeltaQueries) }