Fetch mod time when getting added and removed items (#4266)
Also return mod time when available if getting the set of added and removed items. This will be leveraged in later PRs to implement kopia assisted incrementals for exchange Does not change any logic in collections right now, just adds the fields to be returned Also adds an additional return value denoting if the mod times are expected to be valid. This is required because events delta cannot return mod time --- #### Does this PR need a docs update or release note? - [ ] ✅ Yes, it's included - [ ] 🕐 Yes, but in a later PR - [x] ⛔ No #### Type of change - [ ] 🌻 Feature - [ ] 🐛 Bugfix - [ ] 🗺️ Documentation - [ ] 🤖 Supportability/Tests - [ ] 💻 CI/Deployment - [x] 🧹 Tech Debt/Cleanup #### Issue(s) * #2023 #### Test Plan - [ ] 💪 Manual - [x] ⚡ Unit test - [ ] 💚 E2E
This commit is contained in:
parent
647f7326d7
commit
4a9951b876
@ -160,7 +160,7 @@ func populateCollections(
|
|||||||
|
|
||||||
ictx = clues.Add(ictx, "previous_path", prevPath)
|
ictx = clues.Add(ictx, "previous_path", prevPath)
|
||||||
|
|
||||||
added, removed, newDelta, err := bh.itemEnumerator().
|
added, _, removed, newDelta, err := bh.itemEnumerator().
|
||||||
GetAddedAndRemovedItemIDs(
|
GetAddedAndRemovedItemIDs(
|
||||||
ictx,
|
ictx,
|
||||||
qp.ProtectedResource.ID(),
|
qp.ProtectedResource.ID(),
|
||||||
@ -201,7 +201,7 @@ func populateCollections(
|
|||||||
|
|
||||||
collections[cID] = &edc
|
collections[cID] = &edc
|
||||||
|
|
||||||
for _, add := range added {
|
for add := range added {
|
||||||
edc.added[add] = struct{}{}
|
edc.added[add] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -72,14 +73,15 @@ func (mg mockGetter) GetAddedAndRemovedItemIDs(
|
|||||||
_ bool,
|
_ bool,
|
||||||
_ bool,
|
_ bool,
|
||||||
) (
|
) (
|
||||||
[]string,
|
map[string]time.Time,
|
||||||
|
bool,
|
||||||
[]string,
|
[]string,
|
||||||
api.DeltaUpdate,
|
api.DeltaUpdate,
|
||||||
error,
|
error,
|
||||||
) {
|
) {
|
||||||
results, ok := mg.results[cID]
|
results, ok := mg.results[cID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil, api.DeltaUpdate{}, clues.New("mock not found for " + cID)
|
return nil, false, nil, api.DeltaUpdate{}, clues.New("mock not found for " + cID)
|
||||||
}
|
}
|
||||||
|
|
||||||
delta := results.newDelta
|
delta := results.newDelta
|
||||||
@ -87,7 +89,12 @@ func (mg mockGetter) GetAddedAndRemovedItemIDs(
|
|||||||
delta.URL = ""
|
delta.URL = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
return results.added, results.removed, delta, results.err
|
resAdded := make(map[string]time.Time, len(results.added))
|
||||||
|
for _, add := range results.added {
|
||||||
|
resAdded[add] = time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resAdded, false, results.removed, delta, results.err
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ graph.ContainerResolver = &mockResolver{}
|
var _ graph.ContainerResolver = &mockResolver{}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package exchange
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/microsoft/kiota-abstractions-go/serialization"
|
"github.com/microsoft/kiota-abstractions-go/serialization"
|
||||||
|
|
||||||
@ -30,7 +31,7 @@ type addedAndRemovedItemGetter interface {
|
|||||||
user, containerID, oldDeltaToken string,
|
user, containerID, oldDeltaToken string,
|
||||||
immutableIDs bool,
|
immutableIDs bool,
|
||||||
canMakeDeltaQueries bool,
|
canMakeDeltaQueries bool,
|
||||||
) ([]string, []string, api.DeltaUpdate, error)
|
) (map[string]time.Time, bool, []string, api.DeltaUpdate, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type itemGetterSerializer interface {
|
type itemGetterSerializer interface {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
|
||||||
"github.com/alcionai/corso/src/internal/common/pii"
|
"github.com/alcionai/corso/src/internal/common/pii"
|
||||||
"github.com/alcionai/corso/src/internal/common/ptr"
|
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||||
@ -153,13 +154,13 @@ func populateCollections(
|
|||||||
// and will return an error if a delta token is queried.
|
// and will return an error if a delta token is queried.
|
||||||
canMakeDeltaQueries := len(ptr.Val(c.GetEmail())) > 0
|
canMakeDeltaQueries := len(ptr.Val(c.GetEmail())) > 0
|
||||||
|
|
||||||
add, rem, du, err := bh.getChannelMessageIDs(ctx, cID, prevDelta, canMakeDeltaQueries)
|
add, _, rem, du, err := bh.getChannelMessageIDs(ctx, cID, prevDelta, canMakeDeltaQueries)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
el.AddRecoverable(ctx, clues.Stack(err))
|
el.AddRecoverable(ctx, clues.Stack(err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
added := str.SliceToMap(add)
|
added := str.SliceToMap(maps.Keys(add))
|
||||||
removed := str.SliceToMap(rem)
|
removed := str.SliceToMap(rem)
|
||||||
|
|
||||||
if len(du.URL) > 0 {
|
if len(du.URL) > 0 {
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||||
@ -57,8 +58,14 @@ func (bh mockBackupHandler) getChannelMessageIDs(
|
|||||||
_ context.Context,
|
_ context.Context,
|
||||||
_, _ string,
|
_, _ string,
|
||||||
_ bool,
|
_ bool,
|
||||||
) ([]string, []string, api.DeltaUpdate, error) {
|
) (map[string]time.Time, bool, []string, api.DeltaUpdate, error) {
|
||||||
return bh.messageIDs, bh.deletedMsgIDs, api.DeltaUpdate{}, bh.messagesErr
|
idRes := make(map[string]time.Time, len(bh.messageIDs))
|
||||||
|
|
||||||
|
for _, id := range bh.messageIDs {
|
||||||
|
idRes[id] = time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return idRes, true, bh.deletedMsgIDs, api.DeltaUpdate{}, bh.messagesErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bh mockBackupHandler) includeContainer(
|
func (bh mockBackupHandler) includeContainer(
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package groups
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||||
|
|
||||||
@ -40,7 +41,7 @@ func (bh channelsBackupHandler) getChannelMessageIDs(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
channelID, prevDelta string,
|
channelID, prevDelta string,
|
||||||
canMakeDeltaQueries bool,
|
canMakeDeltaQueries bool,
|
||||||
) ([]string, []string, api.DeltaUpdate, error) {
|
) (map[string]time.Time, bool, []string, api.DeltaUpdate, error) {
|
||||||
return bh.ac.GetChannelMessageIDs(ctx, bh.protectedResource, channelID, prevDelta, canMakeDeltaQueries)
|
return bh.ac.GetChannelMessageIDs(ctx, bh.protectedResource, channelID, prevDelta, canMakeDeltaQueries)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package groups
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||||
|
|
||||||
@ -25,7 +26,7 @@ type backupHandler interface {
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
channelID, prevDelta string,
|
channelID, prevDelta string,
|
||||||
canMakeDeltaQueries bool,
|
canMakeDeltaQueries bool,
|
||||||
) ([]string, []string, api.DeltaUpdate, error)
|
) (map[string]time.Time, bool, []string, api.DeltaUpdate, error)
|
||||||
|
|
||||||
// includeContainer evaluates whether the channel is included
|
// includeContainer evaluates whether the channel is included
|
||||||
// in the provided scope.
|
// in the provided scope.
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||||
@ -381,12 +382,12 @@ func testExchangeContinuousBackups(suite *ExchangeBackupIntgSuite, toggles contr
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
items []string
|
items map[string]time.Time
|
||||||
)
|
)
|
||||||
|
|
||||||
switch category {
|
switch category {
|
||||||
case path.EmailCategory:
|
case path.EmailCategory:
|
||||||
items, _, _, err = ac.Mail().GetAddedAndRemovedItemIDs(
|
items, _, _, _, err = ac.Mail().GetAddedAndRemovedItemIDs(
|
||||||
ctx,
|
ctx,
|
||||||
uidn.ID(),
|
uidn.ID(),
|
||||||
containerID,
|
containerID,
|
||||||
@ -395,7 +396,7 @@ func testExchangeContinuousBackups(suite *ExchangeBackupIntgSuite, toggles contr
|
|||||||
true)
|
true)
|
||||||
|
|
||||||
case path.EventsCategory:
|
case path.EventsCategory:
|
||||||
items, _, _, err = ac.Events().GetAddedAndRemovedItemIDs(
|
items, _, _, _, err = ac.Events().GetAddedAndRemovedItemIDs(
|
||||||
ctx,
|
ctx,
|
||||||
uidn.ID(),
|
uidn.ID(),
|
||||||
containerID,
|
containerID,
|
||||||
@ -404,7 +405,7 @@ func testExchangeContinuousBackups(suite *ExchangeBackupIntgSuite, toggles contr
|
|||||||
true)
|
true)
|
||||||
|
|
||||||
case path.ContactsCategory:
|
case path.ContactsCategory:
|
||||||
items, _, _, err = ac.Contacts().GetAddedAndRemovedItemIDs(
|
items, _, _, _, err = ac.Contacts().GetAddedAndRemovedItemIDs(
|
||||||
ctx,
|
ctx,
|
||||||
uidn.ID(),
|
uidn.ID(),
|
||||||
containerID,
|
containerID,
|
||||||
@ -423,7 +424,7 @@ func testExchangeContinuousBackups(suite *ExchangeBackupIntgSuite, toggles contr
|
|||||||
dest := dataset[category].dests[destName]
|
dest := dataset[category].dests[destName]
|
||||||
dest.locRef = locRef.String()
|
dest.locRef = locRef.String()
|
||||||
dest.containerID = containerID
|
dest.containerID = containerID
|
||||||
dest.itemRefs = items
|
dest.itemRefs = maps.Keys(items)
|
||||||
dataset[category].dests[destName] = dest
|
dataset[category].dests[destName] = dest
|
||||||
|
|
||||||
// Add the directory and all its ancestors to the cache so we can compare
|
// Add the directory and all its ancestors to the cache so we can compare
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||||
@ -35,6 +36,10 @@ func (p *channelMessagePageCtrl) GetPage(
|
|||||||
return resp, graph.Stack(ctx, err).OrNil()
|
return resp, graph.Stack(ctx, err).OrNil()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *channelMessagePageCtrl) ValidModTimes() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (c Channels) NewChannelMessagePager(
|
func (c Channels) NewChannelMessagePager(
|
||||||
teamID, channelID string,
|
teamID, channelID string,
|
||||||
selectProps ...string,
|
selectProps ...string,
|
||||||
@ -100,6 +105,10 @@ func (p *channelMessageDeltaPageCtrl) Reset(context.Context) {
|
|||||||
Delta()
|
Delta()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *channelMessageDeltaPageCtrl) ValidModTimes() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (c Channels) NewChannelMessageDeltaPager(
|
func (c Channels) NewChannelMessageDeltaPager(
|
||||||
teamID, channelID, prevDelta string,
|
teamID, channelID, prevDelta string,
|
||||||
selectProps ...string,
|
selectProps ...string,
|
||||||
@ -141,16 +150,16 @@ func (c Channels) GetChannelMessageIDs(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
teamID, channelID, prevDeltaLink string,
|
teamID, channelID, prevDeltaLink string,
|
||||||
canMakeDeltaQueries bool,
|
canMakeDeltaQueries bool,
|
||||||
) ([]string, []string, DeltaUpdate, error) {
|
) (map[string]time.Time, bool, []string, DeltaUpdate, error) {
|
||||||
added, removed, du, err := getAddedAndRemovedItemIDs(
|
added, validModTimes, removed, du, err := getAddedAndRemovedItemIDs[models.ChatMessageable](
|
||||||
ctx,
|
ctx,
|
||||||
c.NewChannelMessagePager(teamID, channelID),
|
c.NewChannelMessagePager(teamID, channelID),
|
||||||
c.NewChannelMessageDeltaPager(teamID, channelID, prevDeltaLink),
|
c.NewChannelMessageDeltaPager(teamID, channelID, prevDeltaLink),
|
||||||
prevDeltaLink,
|
prevDeltaLink,
|
||||||
canMakeDeltaQueries,
|
canMakeDeltaQueries,
|
||||||
addedAndRemovedByDeletedDateTime)
|
addedAndRemovedByDeletedDateTime[models.ChatMessageable])
|
||||||
|
|
||||||
return added, removed, du, clues.Stack(err).OrNil()
|
return added, validModTimes, removed, du, clues.Stack(err).OrNil()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@ -180,6 +189,10 @@ func (p *channelMessageRepliesPageCtrl) GetOdataNextLink() *string {
|
|||||||
return ptr.To("")
|
return ptr.To("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *channelMessageRepliesPageCtrl) ValidModTimes() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (c Channels) NewChannelMessageRepliesPager(
|
func (c Channels) NewChannelMessageRepliesPager(
|
||||||
teamID, channelID, messageID string,
|
teamID, channelID, messageID string,
|
||||||
selectProps ...string,
|
selectProps ...string,
|
||||||
@ -242,6 +255,10 @@ func (p *channelPageCtrl) GetPage(
|
|||||||
return resp, graph.Stack(ctx, err).OrNil()
|
return resp, graph.Stack(ctx, err).OrNil()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *channelPageCtrl) ValidModTimes() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (c Channels) NewChannelPager(
|
func (c Channels) NewChannelPager(
|
||||||
teamID string,
|
teamID string,
|
||||||
) *channelPageCtrl {
|
) *channelPageCtrl {
|
||||||
|
|||||||
@ -56,7 +56,7 @@ func (suite *ChannelsPagerIntgSuite) TestEnumerateChannelMessages() {
|
|||||||
ctx, flush := tester.NewContext(t)
|
ctx, flush := tester.NewContext(t)
|
||||||
defer flush()
|
defer flush()
|
||||||
|
|
||||||
addedIDs, _, du, err := ac.GetChannelMessageIDs(
|
addedIDs, _, _, du, err := ac.GetChannelMessageIDs(
|
||||||
ctx,
|
ctx,
|
||||||
suite.its.group.id,
|
suite.its.group.id,
|
||||||
suite.its.group.testContainerID,
|
suite.its.group.testContainerID,
|
||||||
@ -67,7 +67,7 @@ func (suite *ChannelsPagerIntgSuite) TestEnumerateChannelMessages() {
|
|||||||
require.NotZero(t, du.URL, "delta link")
|
require.NotZero(t, du.URL, "delta link")
|
||||||
require.True(t, du.Reset, "reset due to empty prev delta link")
|
require.True(t, du.Reset, "reset due to empty prev delta link")
|
||||||
|
|
||||||
addedIDs, deletedIDs, du, err := ac.GetChannelMessageIDs(
|
addedIDs, _, deletedIDs, du, err := ac.GetChannelMessageIDs(
|
||||||
ctx,
|
ctx,
|
||||||
suite.its.group.id,
|
suite.its.group.id,
|
||||||
suite.its.group.testContainerID,
|
suite.its.group.testContainerID,
|
||||||
@ -79,7 +79,7 @@ func (suite *ChannelsPagerIntgSuite) TestEnumerateChannelMessages() {
|
|||||||
require.NotZero(t, du.URL, "delta link")
|
require.NotZero(t, du.URL, "delta link")
|
||||||
require.False(t, du.Reset, "prev delta link should be valid")
|
require.False(t, du.Reset, "prev delta link should be valid")
|
||||||
|
|
||||||
for _, id := range addedIDs {
|
for id := range addedIDs {
|
||||||
suite.Run(id+"-replies", func() {
|
suite.Run(id+"-replies", func() {
|
||||||
testEnumerateChannelMessageReplies(
|
testEnumerateChannelMessageReplies(
|
||||||
suite.T(),
|
suite.T(),
|
||||||
|
|||||||
@ -17,22 +17,23 @@ const (
|
|||||||
// get easily misspelled.
|
// get easily misspelled.
|
||||||
// eg: we don't need a const for "id"
|
// eg: we don't need a const for "id"
|
||||||
const (
|
const (
|
||||||
bccRecipients = "bccRecipients"
|
bccRecipients = "bccRecipients"
|
||||||
ccRecipients = "ccRecipients"
|
ccRecipients = "ccRecipients"
|
||||||
createdDateTime = "createdDateTime"
|
createdDateTime = "createdDateTime"
|
||||||
displayName = "displayName"
|
displayName = "displayName"
|
||||||
emailAddresses = "emailAddresses"
|
emailAddresses = "emailAddresses"
|
||||||
givenName = "givenName"
|
givenName = "givenName"
|
||||||
isCancelled = "isCancelled"
|
isCancelled = "isCancelled"
|
||||||
isDraft = "isDraft"
|
isDraft = "isDraft"
|
||||||
mobilePhone = "mobilePhone"
|
lastModifiedDateTime = "lastModifiedDateTime"
|
||||||
parentFolderID = "parentFolderId"
|
mobilePhone = "mobilePhone"
|
||||||
receivedDateTime = "receivedDateTime"
|
parentFolderID = "parentFolderId"
|
||||||
recurrence = "recurrence"
|
receivedDateTime = "receivedDateTime"
|
||||||
sentDateTime = "sentDateTime"
|
recurrence = "recurrence"
|
||||||
surname = "surname"
|
sentDateTime = "sentDateTime"
|
||||||
toRecipients = "toRecipients"
|
surname = "surname"
|
||||||
userPrincipalName = "userPrincipalName"
|
toRecipients = "toRecipients"
|
||||||
|
userPrincipalName = "userPrincipalName"
|
||||||
)
|
)
|
||||||
|
|
||||||
// header keys
|
// header keys
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||||
@ -136,6 +137,10 @@ func (p *contactsPageCtrl) SetNextLink(nextLink string) {
|
|||||||
p.builder = users.NewItemContactFoldersItemContactsRequestBuilder(nextLink, p.gs.Adapter())
|
p.builder = users.NewItemContactFoldersItemContactsRequestBuilder(nextLink, p.gs.Adapter())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *contactsPageCtrl) ValidModTimes() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (c Contacts) GetItemsInContainerByCollisionKey(
|
func (c Contacts) GetItemsInContainerByCollisionKey(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID, containerID string,
|
userID, containerID string,
|
||||||
@ -249,12 +254,16 @@ func (p *contactDeltaPager) Reset(ctx context.Context) {
|
|||||||
p.builder = getContactDeltaBuilder(ctx, p.gs, p.userID, p.containerID)
|
p.builder = getContactDeltaBuilder(ctx, p.gs, p.userID, p.containerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *contactDeltaPager) ValidModTimes() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (c Contacts) GetAddedAndRemovedItemIDs(
|
func (c Contacts) GetAddedAndRemovedItemIDs(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID, containerID, prevDeltaLink string,
|
userID, containerID, prevDeltaLink string,
|
||||||
immutableIDs bool,
|
immutableIDs bool,
|
||||||
canMakeDeltaQueries bool,
|
canMakeDeltaQueries bool,
|
||||||
) ([]string, []string, DeltaUpdate, error) {
|
) (map[string]time.Time, bool, []string, DeltaUpdate, error) {
|
||||||
ctx = clues.Add(
|
ctx = clues.Add(
|
||||||
ctx,
|
ctx,
|
||||||
"data_category", path.ContactsCategory,
|
"data_category", path.ContactsCategory,
|
||||||
@ -266,12 +275,12 @@ func (c Contacts) GetAddedAndRemovedItemIDs(
|
|||||||
containerID,
|
containerID,
|
||||||
prevDeltaLink,
|
prevDeltaLink,
|
||||||
immutableIDs,
|
immutableIDs,
|
||||||
idAnd()...)
|
idAnd(lastModifiedDateTime)...)
|
||||||
pager := c.NewContactsPager(
|
pager := c.NewContactsPager(
|
||||||
userID,
|
userID,
|
||||||
containerID,
|
containerID,
|
||||||
immutableIDs,
|
immutableIDs,
|
||||||
idAnd()...)
|
idAnd(lastModifiedDateTime)...)
|
||||||
|
|
||||||
return getAddedAndRemovedItemIDs[models.Contactable](
|
return getAddedAndRemovedItemIDs[models.Contactable](
|
||||||
ctx,
|
ctx,
|
||||||
@ -279,5 +288,5 @@ func (c Contacts) GetAddedAndRemovedItemIDs(
|
|||||||
deltaPager,
|
deltaPager,
|
||||||
prevDeltaLink,
|
prevDeltaLink,
|
||||||
canMakeDeltaQueries,
|
canMakeDeltaQueries,
|
||||||
addedAndRemovedByAddtlData)
|
addedAndRemovedByAddtlData[models.Contactable])
|
||||||
}
|
}
|
||||||
|
|||||||
@ -61,6 +61,10 @@ func (p *driveItemPageCtrl) SetNextLink(nextLink string) {
|
|||||||
p.builder = drives.NewItemItemsItemChildrenRequestBuilder(nextLink, p.gs.Adapter())
|
p.builder = drives.NewItemItemsItemChildrenRequestBuilder(nextLink, p.gs.Adapter())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *driveItemPageCtrl) ValidModTimes() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
type DriveItemIDType struct {
|
type DriveItemIDType struct {
|
||||||
ItemID string
|
ItemID string
|
||||||
IsFolder bool
|
IsFolder bool
|
||||||
@ -185,6 +189,10 @@ func (p *DriveItemDeltaPageCtrl) Reset(context.Context) {
|
|||||||
Delta()
|
Delta()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *DriveItemDeltaPageCtrl) ValidModTimes() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// user's drives pager
|
// user's drives pager
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@ -252,6 +260,10 @@ func (p *userDrivePager) SetNextLink(link string) {
|
|||||||
p.builder = users.NewItemDrivesRequestBuilder(link, p.gs.Adapter())
|
p.builder = users.NewItemDrivesRequestBuilder(link, p.gs.Adapter())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *userDrivePager) ValidModTimes() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// site's libraries pager
|
// site's libraries pager
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@ -303,6 +315,10 @@ func (p *siteDrivePager) SetNextLink(link string) {
|
|||||||
p.builder = sites.NewItemDrivesRequestBuilder(link, p.gs.Adapter())
|
p.builder = sites.NewItemDrivesRequestBuilder(link, p.gs.Adapter())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *siteDrivePager) ValidModTimes() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// drive pager
|
// drive pager
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package api
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||||
@ -141,6 +142,10 @@ func (p *eventsPageCtrl) SetNextLink(nextLink string) {
|
|||||||
p.builder = users.NewItemCalendarsItemEventsRequestBuilder(nextLink, p.gs.Adapter())
|
p.builder = users.NewItemCalendarsItemEventsRequestBuilder(nextLink, p.gs.Adapter())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *eventsPageCtrl) ValidModTimes() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (c Events) GetItemsInContainerByCollisionKey(
|
func (c Events) GetItemsInContainerByCollisionKey(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID, containerID string,
|
userID, containerID string,
|
||||||
@ -248,12 +253,16 @@ func (p *eventDeltaPager) Reset(ctx context.Context) {
|
|||||||
p.builder = getEventDeltaBuilder(ctx, p.gs, p.userID, p.containerID)
|
p.builder = getEventDeltaBuilder(ctx, p.gs, p.userID, p.containerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *eventDeltaPager) ValidModTimes() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (c Events) GetAddedAndRemovedItemIDs(
|
func (c Events) GetAddedAndRemovedItemIDs(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID, containerID, prevDeltaLink string,
|
userID, containerID, prevDeltaLink string,
|
||||||
immutableIDs bool,
|
immutableIDs bool,
|
||||||
canMakeDeltaQueries bool,
|
canMakeDeltaQueries bool,
|
||||||
) ([]string, []string, DeltaUpdate, error) {
|
) (map[string]time.Time, bool, []string, DeltaUpdate, error) {
|
||||||
ctx = clues.Add(
|
ctx = clues.Add(
|
||||||
ctx,
|
ctx,
|
||||||
"data_category", path.EventsCategory,
|
"data_category", path.EventsCategory,
|
||||||
@ -270,7 +279,7 @@ func (c Events) GetAddedAndRemovedItemIDs(
|
|||||||
userID,
|
userID,
|
||||||
containerID,
|
containerID,
|
||||||
immutableIDs,
|
immutableIDs,
|
||||||
idAnd()...)
|
idAnd(lastModifiedDateTime)...)
|
||||||
|
|
||||||
return getAddedAndRemovedItemIDs[models.Eventable](
|
return getAddedAndRemovedItemIDs[models.Eventable](
|
||||||
ctx,
|
ctx,
|
||||||
@ -278,5 +287,5 @@ func (c Events) GetAddedAndRemovedItemIDs(
|
|||||||
deltaPager,
|
deltaPager,
|
||||||
prevDeltaLink,
|
prevDeltaLink,
|
||||||
canMakeDeltaQueries,
|
canMakeDeltaQueries,
|
||||||
addedAndRemovedByAddtlData)
|
addedAndRemovedByAddtlData[models.Eventable])
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,6 +52,10 @@ type Resetter interface {
|
|||||||
Reset(context.Context)
|
Reset(context.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ValidModTimer interface {
|
||||||
|
ValidModTimes() bool
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// common funcs
|
// common funcs
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@ -76,6 +80,7 @@ func NextAndDeltaLink(pl DeltaLinker) (string, string) {
|
|||||||
type Pager[T any] interface {
|
type Pager[T any] interface {
|
||||||
GetPager[NextLinkValuer[T]]
|
GetPager[NextLinkValuer[T]]
|
||||||
SetNextLinker
|
SetNextLinker
|
||||||
|
ValidModTimer
|
||||||
}
|
}
|
||||||
|
|
||||||
func enumerateItems[T any](
|
func enumerateItems[T any](
|
||||||
@ -114,6 +119,7 @@ type DeltaPager[T any] interface {
|
|||||||
GetPager[DeltaLinkValuer[T]]
|
GetPager[DeltaLinkValuer[T]]
|
||||||
Resetter
|
Resetter
|
||||||
SetNextLinker
|
SetNextLinker
|
||||||
|
ValidModTimer
|
||||||
}
|
}
|
||||||
|
|
||||||
func deltaEnumerateItems[T any](
|
func deltaEnumerateItems[T any](
|
||||||
@ -173,7 +179,7 @@ func deltaEnumerateItems[T any](
|
|||||||
// shared enumeration runner funcs
|
// shared enumeration runner funcs
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
type addedAndRemovedHandler[T any] func(items []T) ([]string, []string, error)
|
type addedAndRemovedHandler[T any] func(items []T) (map[string]time.Time, []string, error)
|
||||||
|
|
||||||
func getAddedAndRemovedItemIDs[T any](
|
func getAddedAndRemovedItemIDs[T any](
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
@ -182,16 +188,16 @@ func getAddedAndRemovedItemIDs[T any](
|
|||||||
prevDeltaLink string,
|
prevDeltaLink string,
|
||||||
canMakeDeltaQueries bool,
|
canMakeDeltaQueries bool,
|
||||||
aarh addedAndRemovedHandler[T],
|
aarh addedAndRemovedHandler[T],
|
||||||
) ([]string, []string, DeltaUpdate, error) {
|
) (map[string]time.Time, bool, []string, DeltaUpdate, error) {
|
||||||
if canMakeDeltaQueries {
|
if canMakeDeltaQueries {
|
||||||
ts, du, err := deltaEnumerateItems[T](ctx, deltaPager, prevDeltaLink)
|
ts, du, err := deltaEnumerateItems[T](ctx, deltaPager, prevDeltaLink)
|
||||||
if err != nil && (!graph.IsErrInvalidDelta(err) || len(prevDeltaLink) == 0) {
|
if err != nil && (!graph.IsErrInvalidDelta(err) || len(prevDeltaLink) == 0) {
|
||||||
return nil, nil, DeltaUpdate{}, graph.Stack(ctx, err)
|
return nil, false, nil, DeltaUpdate{}, graph.Stack(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
a, r, err := aarh(ts)
|
a, r, err := aarh(ts)
|
||||||
return a, r, du, graph.Stack(ctx, err).OrNil()
|
return a, deltaPager.ValidModTimes(), r, du, graph.Stack(ctx, err).OrNil()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,12 +205,12 @@ func getAddedAndRemovedItemIDs[T any](
|
|||||||
|
|
||||||
ts, err := enumerateItems(ctx, pager)
|
ts, err := enumerateItems(ctx, pager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, DeltaUpdate{}, graph.Stack(ctx, err)
|
return nil, false, nil, DeltaUpdate{}, graph.Stack(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
a, r, err := aarh(ts)
|
a, r, err := aarh(ts)
|
||||||
|
|
||||||
return a, r, du, graph.Stack(ctx, err).OrNil()
|
return a, pager.ValidModTimes(), r, du, graph.Stack(ctx, err).OrNil()
|
||||||
}
|
}
|
||||||
|
|
||||||
type getIDer interface {
|
type getIDer interface {
|
||||||
@ -218,8 +224,15 @@ type getIDAndAddtler interface {
|
|||||||
GetAdditionalData() map[string]any
|
GetAdditionalData() map[string]any
|
||||||
}
|
}
|
||||||
|
|
||||||
func addedAndRemovedByAddtlData[T any](items []T) ([]string, []string, error) {
|
type getModTimer interface {
|
||||||
added, removed := []string{}, []string{}
|
GetLastModifiedDateTime() *time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func addedAndRemovedByAddtlData[T any](
|
||||||
|
items []T,
|
||||||
|
) (map[string]time.Time, []string, error) {
|
||||||
|
added := map[string]time.Time{}
|
||||||
|
removed := []string{}
|
||||||
|
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
giaa, ok := any(item).(getIDAndAddtler)
|
giaa, ok := any(item).(getIDAndAddtler)
|
||||||
@ -232,7 +245,13 @@ func addedAndRemovedByAddtlData[T any](items []T) ([]string, []string, error) {
|
|||||||
// be 'changed' or 'deleted'. We don't really care about the cause: both
|
// be 'changed' or 'deleted'. We don't really care about the cause: both
|
||||||
// cases are handled the same way in storage.
|
// cases are handled the same way in storage.
|
||||||
if giaa.GetAdditionalData()[graph.AddtlDataRemoved] == nil {
|
if giaa.GetAdditionalData()[graph.AddtlDataRemoved] == nil {
|
||||||
added = append(added, ptr.Val(giaa.GetId()))
|
var modTime time.Time
|
||||||
|
|
||||||
|
if mt, ok := giaa.(getModTimer); ok {
|
||||||
|
modTime = ptr.Val(mt.GetLastModifiedDateTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
added[ptr.Val(giaa.GetId())] = modTime
|
||||||
} else {
|
} else {
|
||||||
removed = append(removed, ptr.Val(giaa.GetId()))
|
removed = append(removed, ptr.Val(giaa.GetId()))
|
||||||
}
|
}
|
||||||
@ -248,8 +267,11 @@ type getIDAndDeletedDateTimer interface {
|
|||||||
GetDeletedDateTime() *time.Time
|
GetDeletedDateTime() *time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func addedAndRemovedByDeletedDateTime[T any](items []T) ([]string, []string, error) {
|
func addedAndRemovedByDeletedDateTime[T any](
|
||||||
added, removed := []string{}, []string{}
|
items []T,
|
||||||
|
) (map[string]time.Time, []string, error) {
|
||||||
|
added := map[string]time.Time{}
|
||||||
|
removed := []string{}
|
||||||
|
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
giaddt, ok := any(item).(getIDAndDeletedDateTimer)
|
giaddt, ok := any(item).(getIDAndDeletedDateTimer)
|
||||||
@ -259,7 +281,13 @@ func addedAndRemovedByDeletedDateTime[T any](items []T) ([]string, []string, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
if giaddt.GetDeletedDateTime() == nil {
|
if giaddt.GetDeletedDateTime() == nil {
|
||||||
added = append(added, ptr.Val(giaddt.GetId()))
|
var modTime time.Time
|
||||||
|
|
||||||
|
if mt, ok := giaddt.(getModTimer); ok {
|
||||||
|
modTime = ptr.Val(mt.GetLastModifiedDateTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
added[ptr.Val(giaddt.GetId())] = modTime
|
||||||
} else {
|
} else {
|
||||||
removed = append(removed, ptr.Val(giaddt.GetId()))
|
removed = append(removed, ptr.Val(giaddt.GetId()))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||||
@ -76,16 +77,19 @@ func (p *testPager) GetPage(ctx context.Context) (NextLinkValuer[any], error) {
|
|||||||
|
|
||||||
func (p *testPager) SetNextLink(nextLink string) {}
|
func (p *testPager) SetNextLink(nextLink string) {}
|
||||||
|
|
||||||
|
func (p testPager) ValidModTimes() bool { return true }
|
||||||
|
|
||||||
// mock id pager
|
// mock id pager
|
||||||
|
|
||||||
var _ Pager[any] = &testIDsPager{}
|
var _ Pager[any] = &testIDsPager{}
|
||||||
|
|
||||||
type testIDsPager struct {
|
type testIDsPager struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
added []string
|
added map[string]time.Time
|
||||||
removed []string
|
removed []string
|
||||||
errorCode string
|
errorCode string
|
||||||
needsReset bool
|
needsReset bool
|
||||||
|
validModTimes bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *testIDsPager) GetPage(
|
func (p *testIDsPager) GetPage(
|
||||||
@ -103,10 +107,11 @@ func (p *testIDsPager) GetPage(
|
|||||||
|
|
||||||
values := make([]any, 0, len(p.added)+len(p.removed))
|
values := make([]any, 0, len(p.added)+len(p.removed))
|
||||||
|
|
||||||
for _, a := range p.added {
|
for a, modTime := range p.added {
|
||||||
// contact chosen arbitrarily, any exchange model should work
|
// contact chosen arbitrarily, any exchange model should work
|
||||||
itm := models.NewContact()
|
itm := models.NewContact()
|
||||||
itm.SetId(ptr.To(a))
|
itm.SetId(ptr.To(a))
|
||||||
|
itm.SetLastModifiedDateTime(ptr.To(modTime))
|
||||||
values = append(values, itm)
|
values = append(values, itm)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,14 +137,19 @@ func (p *testIDsPager) Reset(context.Context) {
|
|||||||
p.errorCode = ""
|
p.errorCode = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p testIDsPager) ValidModTimes() bool {
|
||||||
|
return p.validModTimes
|
||||||
|
}
|
||||||
|
|
||||||
var _ DeltaPager[any] = &testIDsDeltaPager{}
|
var _ DeltaPager[any] = &testIDsDeltaPager{}
|
||||||
|
|
||||||
type testIDsDeltaPager struct {
|
type testIDsDeltaPager struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
added []string
|
added map[string]time.Time
|
||||||
removed []string
|
removed []string
|
||||||
errorCode string
|
errorCode string
|
||||||
needsReset bool
|
needsReset bool
|
||||||
|
validModTimes bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *testIDsDeltaPager) GetPage(
|
func (p *testIDsDeltaPager) GetPage(
|
||||||
@ -157,10 +167,11 @@ func (p *testIDsDeltaPager) GetPage(
|
|||||||
|
|
||||||
values := make([]any, 0, len(p.added)+len(p.removed))
|
values := make([]any, 0, len(p.added)+len(p.removed))
|
||||||
|
|
||||||
for _, a := range p.added {
|
for a, modTime := range p.added {
|
||||||
// contact chosen arbitrarily, any exchange model should work
|
// contact chosen arbitrarily, any exchange model should work
|
||||||
itm := models.NewContact()
|
itm := models.NewContact()
|
||||||
itm.SetId(ptr.To(a))
|
itm.SetId(ptr.To(a))
|
||||||
|
itm.SetLastModifiedDateTime(ptr.To(modTime))
|
||||||
values = append(values, itm)
|
values = append(values, itm)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,6 +197,10 @@ func (p *testIDsDeltaPager) Reset(context.Context) {
|
|||||||
p.errorCode = ""
|
p.errorCode = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p testIDsDeltaPager) ValidModTimes() bool {
|
||||||
|
return p.validModTimes
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Tests
|
// Tests
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@ -252,11 +267,14 @@ func (suite *PagerUnitSuite) TestEnumerateItems() {
|
|||||||
|
|
||||||
func (suite *PagerUnitSuite) TestGetAddedAndRemovedItemIDs() {
|
func (suite *PagerUnitSuite) TestGetAddedAndRemovedItemIDs() {
|
||||||
type expected struct {
|
type expected struct {
|
||||||
added []string
|
added map[string]time.Time
|
||||||
removed []string
|
removed []string
|
||||||
deltaUpdate DeltaUpdate
|
deltaUpdate DeltaUpdate
|
||||||
|
validModTimes bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
pagerGetter func(
|
pagerGetter func(
|
||||||
@ -265,9 +283,10 @@ func (suite *PagerUnitSuite) TestGetAddedAndRemovedItemIDs() {
|
|||||||
deltaPagerGetter func(
|
deltaPagerGetter func(
|
||||||
*testing.T,
|
*testing.T,
|
||||||
) DeltaPager[any]
|
) DeltaPager[any]
|
||||||
prevDelta string
|
prevDelta string
|
||||||
expect expected
|
expect expected
|
||||||
canDelta bool
|
canDelta bool
|
||||||
|
validModTimes bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no prev delta",
|
name: "no prev delta",
|
||||||
@ -276,13 +295,46 @@ func (suite *PagerUnitSuite) TestGetAddedAndRemovedItemIDs() {
|
|||||||
},
|
},
|
||||||
deltaPagerGetter: func(t *testing.T) DeltaPager[any] {
|
deltaPagerGetter: func(t *testing.T) DeltaPager[any] {
|
||||||
return &testIDsDeltaPager{
|
return &testIDsDeltaPager{
|
||||||
t: t,
|
t: t,
|
||||||
added: []string{"uno", "dos"},
|
added: map[string]time.Time{
|
||||||
|
"uno": now.Add(time.Minute),
|
||||||
|
"dos": now.Add(2 * time.Minute),
|
||||||
|
},
|
||||||
|
removed: []string{"tres", "quatro"},
|
||||||
|
validModTimes: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expect: expected{
|
||||||
|
added: map[string]time.Time{
|
||||||
|
"uno": now.Add(time.Minute),
|
||||||
|
"dos": now.Add(2 * time.Minute),
|
||||||
|
},
|
||||||
|
removed: []string{"tres", "quatro"},
|
||||||
|
deltaUpdate: DeltaUpdate{Reset: true},
|
||||||
|
validModTimes: true,
|
||||||
|
},
|
||||||
|
canDelta: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no prev delta invalid mod times",
|
||||||
|
pagerGetter: func(t *testing.T) Pager[any] {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
deltaPagerGetter: func(t *testing.T) DeltaPager[any] {
|
||||||
|
return &testIDsDeltaPager{
|
||||||
|
t: t,
|
||||||
|
added: map[string]time.Time{
|
||||||
|
"uno": {},
|
||||||
|
"dos": {},
|
||||||
|
},
|
||||||
removed: []string{"tres", "quatro"},
|
removed: []string{"tres", "quatro"},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
expect: expected{
|
expect: expected{
|
||||||
added: []string{"uno", "dos"},
|
added: map[string]time.Time{
|
||||||
|
"uno": {},
|
||||||
|
"dos": {},
|
||||||
|
},
|
||||||
removed: []string{"tres", "quatro"},
|
removed: []string{"tres", "quatro"},
|
||||||
deltaUpdate: DeltaUpdate{Reset: true},
|
deltaUpdate: DeltaUpdate{Reset: true},
|
||||||
},
|
},
|
||||||
@ -295,16 +347,24 @@ func (suite *PagerUnitSuite) TestGetAddedAndRemovedItemIDs() {
|
|||||||
},
|
},
|
||||||
deltaPagerGetter: func(t *testing.T) DeltaPager[any] {
|
deltaPagerGetter: func(t *testing.T) DeltaPager[any] {
|
||||||
return &testIDsDeltaPager{
|
return &testIDsDeltaPager{
|
||||||
t: t,
|
t: t,
|
||||||
added: []string{"uno", "dos"},
|
added: map[string]time.Time{
|
||||||
removed: []string{"tres", "quatro"},
|
"uno": now.Add(time.Minute),
|
||||||
|
"dos": now.Add(2 * time.Minute),
|
||||||
|
},
|
||||||
|
removed: []string{"tres", "quatro"},
|
||||||
|
validModTimes: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
prevDelta: "delta",
|
prevDelta: "delta",
|
||||||
expect: expected{
|
expect: expected{
|
||||||
added: []string{"uno", "dos"},
|
added: map[string]time.Time{
|
||||||
removed: []string{"tres", "quatro"},
|
"uno": now.Add(time.Minute),
|
||||||
deltaUpdate: DeltaUpdate{Reset: false},
|
"dos": now.Add(2 * time.Minute),
|
||||||
|
},
|
||||||
|
removed: []string{"tres", "quatro"},
|
||||||
|
deltaUpdate: DeltaUpdate{Reset: false},
|
||||||
|
validModTimes: true,
|
||||||
},
|
},
|
||||||
canDelta: true,
|
canDelta: true,
|
||||||
},
|
},
|
||||||
@ -315,18 +375,26 @@ func (suite *PagerUnitSuite) TestGetAddedAndRemovedItemIDs() {
|
|||||||
},
|
},
|
||||||
deltaPagerGetter: func(t *testing.T) DeltaPager[any] {
|
deltaPagerGetter: func(t *testing.T) DeltaPager[any] {
|
||||||
return &testIDsDeltaPager{
|
return &testIDsDeltaPager{
|
||||||
t: t,
|
t: t,
|
||||||
added: []string{"uno", "dos"},
|
added: map[string]time.Time{
|
||||||
removed: []string{"tres", "quatro"},
|
"uno": now.Add(time.Minute),
|
||||||
errorCode: "SyncStateNotFound",
|
"dos": now.Add(2 * time.Minute),
|
||||||
needsReset: true,
|
},
|
||||||
|
removed: []string{"tres", "quatro"},
|
||||||
|
errorCode: "SyncStateNotFound",
|
||||||
|
needsReset: true,
|
||||||
|
validModTimes: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
prevDelta: "delta",
|
prevDelta: "delta",
|
||||||
expect: expected{
|
expect: expected{
|
||||||
added: []string{"uno", "dos"},
|
added: map[string]time.Time{
|
||||||
removed: []string{"tres", "quatro"},
|
"uno": now.Add(time.Minute),
|
||||||
deltaUpdate: DeltaUpdate{Reset: true},
|
"dos": now.Add(2 * time.Minute),
|
||||||
|
},
|
||||||
|
removed: []string{"tres", "quatro"},
|
||||||
|
deltaUpdate: DeltaUpdate{Reset: true},
|
||||||
|
validModTimes: true,
|
||||||
},
|
},
|
||||||
canDelta: true,
|
canDelta: true,
|
||||||
},
|
},
|
||||||
@ -334,18 +402,26 @@ func (suite *PagerUnitSuite) TestGetAddedAndRemovedItemIDs() {
|
|||||||
name: "delta not allowed",
|
name: "delta not allowed",
|
||||||
pagerGetter: func(t *testing.T) Pager[any] {
|
pagerGetter: func(t *testing.T) Pager[any] {
|
||||||
return &testIDsPager{
|
return &testIDsPager{
|
||||||
t: t,
|
t: t,
|
||||||
added: []string{"uno", "dos"},
|
added: map[string]time.Time{
|
||||||
removed: []string{"tres", "quatro"},
|
"uno": now.Add(time.Minute),
|
||||||
|
"dos": now.Add(2 * time.Minute),
|
||||||
|
},
|
||||||
|
removed: []string{"tres", "quatro"},
|
||||||
|
validModTimes: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
deltaPagerGetter: func(t *testing.T) DeltaPager[any] {
|
deltaPagerGetter: func(t *testing.T) DeltaPager[any] {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
expect: expected{
|
expect: expected{
|
||||||
added: []string{"uno", "dos"},
|
added: map[string]time.Time{
|
||||||
removed: []string{"tres", "quatro"},
|
"uno": now.Add(time.Minute),
|
||||||
deltaUpdate: DeltaUpdate{Reset: true},
|
"dos": now.Add(2 * time.Minute),
|
||||||
|
},
|
||||||
|
removed: []string{"tres", "quatro"},
|
||||||
|
deltaUpdate: DeltaUpdate{Reset: true},
|
||||||
|
validModTimes: true,
|
||||||
},
|
},
|
||||||
canDelta: false,
|
canDelta: false,
|
||||||
},
|
},
|
||||||
@ -358,18 +434,19 @@ func (suite *PagerUnitSuite) TestGetAddedAndRemovedItemIDs() {
|
|||||||
ctx, flush := tester.NewContext(t)
|
ctx, flush := tester.NewContext(t)
|
||||||
defer flush()
|
defer flush()
|
||||||
|
|
||||||
added, removed, deltaUpdate, err := getAddedAndRemovedItemIDs[any](
|
added, validModTimes, removed, deltaUpdate, err := getAddedAndRemovedItemIDs[any](
|
||||||
ctx,
|
ctx,
|
||||||
test.pagerGetter(t),
|
test.pagerGetter(t),
|
||||||
test.deltaPagerGetter(t),
|
test.deltaPagerGetter(t),
|
||||||
test.prevDelta,
|
test.prevDelta,
|
||||||
test.canDelta,
|
test.canDelta,
|
||||||
addedAndRemovedByAddtlData)
|
addedAndRemovedByAddtlData[any])
|
||||||
|
|
||||||
require.NoErrorf(t, err, "getting added and removed item IDs: %+v", clues.ToCore(err))
|
require.NoErrorf(t, err, "getting added and removed item IDs: %+v", clues.ToCore(err))
|
||||||
require.EqualValues(t, test.expect.added, added, "added item IDs")
|
assert.Equal(t, test.expect.added, added, "added item IDs and mod times")
|
||||||
require.EqualValues(t, test.expect.removed, removed, "removed item IDs")
|
assert.Equal(t, test.expect.validModTimes, validModTimes, "valid mod times")
|
||||||
require.Equal(t, test.expect.deltaUpdate, deltaUpdate, "delta update")
|
assert.EqualValues(t, test.expect.removed, removed, "removed item IDs")
|
||||||
|
assert.Equal(t, test.expect.deltaUpdate, deltaUpdate, "delta update")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package api
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||||
@ -166,6 +167,10 @@ func (p *mailsPageCtrl) SetNextLink(nextLink string) {
|
|||||||
p.builder = users.NewItemMailFoldersItemMessagesRequestBuilder(nextLink, p.gs.Adapter())
|
p.builder = users.NewItemMailFoldersItemMessagesRequestBuilder(nextLink, p.gs.Adapter())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *mailsPageCtrl) ValidModTimes() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (c Mail) GetItemsInContainerByCollisionKey(
|
func (c Mail) GetItemsInContainerByCollisionKey(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID, containerID string,
|
userID, containerID string,
|
||||||
@ -279,12 +284,16 @@ func (p *mailDeltaPager) Reset(ctx context.Context) {
|
|||||||
p.builder = getMailDeltaBuilder(ctx, p.gs, p.userID, p.containerID)
|
p.builder = getMailDeltaBuilder(ctx, p.gs, p.userID, p.containerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *mailDeltaPager) ValidModTimes() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (c Mail) GetAddedAndRemovedItemIDs(
|
func (c Mail) GetAddedAndRemovedItemIDs(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID, containerID, prevDeltaLink string,
|
userID, containerID, prevDeltaLink string,
|
||||||
immutableIDs bool,
|
immutableIDs bool,
|
||||||
canMakeDeltaQueries bool,
|
canMakeDeltaQueries bool,
|
||||||
) ([]string, []string, DeltaUpdate, error) {
|
) (map[string]time.Time, bool, []string, DeltaUpdate, error) {
|
||||||
ctx = clues.Add(
|
ctx = clues.Add(
|
||||||
ctx,
|
ctx,
|
||||||
"data_category", path.EmailCategory,
|
"data_category", path.EmailCategory,
|
||||||
@ -296,12 +305,12 @@ func (c Mail) GetAddedAndRemovedItemIDs(
|
|||||||
containerID,
|
containerID,
|
||||||
prevDeltaLink,
|
prevDeltaLink,
|
||||||
immutableIDs,
|
immutableIDs,
|
||||||
idAnd()...)
|
idAnd(lastModifiedDateTime)...)
|
||||||
pager := c.NewMailPager(
|
pager := c.NewMailPager(
|
||||||
userID,
|
userID,
|
||||||
containerID,
|
containerID,
|
||||||
immutableIDs,
|
immutableIDs,
|
||||||
idAnd()...)
|
idAnd(lastModifiedDateTime)...)
|
||||||
|
|
||||||
return getAddedAndRemovedItemIDs[models.Messageable](
|
return getAddedAndRemovedItemIDs[models.Messageable](
|
||||||
ctx,
|
ctx,
|
||||||
@ -309,5 +318,5 @@ func (c Mail) GetAddedAndRemovedItemIDs(
|
|||||||
deltaPager,
|
deltaPager,
|
||||||
prevDeltaLink,
|
prevDeltaLink,
|
||||||
canMakeDeltaQueries,
|
canMakeDeltaQueries,
|
||||||
addedAndRemovedByAddtlData)
|
addedAndRemovedByAddtlData[models.Messageable])
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,11 @@ import (
|
|||||||
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ api.Pager[any] = &Pager[any]{}
|
||||||
|
_ api.DeltaPager[any] = &DeltaPager[any]{}
|
||||||
|
)
|
||||||
|
|
||||||
type DeltaNextLinkValues[T any] struct {
|
type DeltaNextLinkValues[T any] struct {
|
||||||
Next *string
|
Next *string
|
||||||
Delta *string
|
Delta *string
|
||||||
@ -61,7 +66,8 @@ func (p *Pager[T]) GetPage(
|
|||||||
return &link, p.ToReturn[idx].Err
|
return &link, p.ToReturn[idx].Err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pager[T]) SetNextLink(string) {}
|
func (p *Pager[T]) SetNextLink(string) {}
|
||||||
|
func (p *Pager[T]) ValidModTimes() bool { return true }
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// delta pager
|
// delta pager
|
||||||
@ -94,3 +100,4 @@ func (p *DeltaPager[T]) GetPage(
|
|||||||
|
|
||||||
func (p *DeltaPager[T]) SetNextLink(string) {}
|
func (p *DeltaPager[T]) SetNextLink(string) {}
|
||||||
func (p *DeltaPager[T]) Reset(context.Context) {}
|
func (p *DeltaPager[T]) Reset(context.Context) {}
|
||||||
|
func (p *DeltaPager[T]) ValidModTimes() bool { return true }
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user