add the non-delta item pager to drives (#3639)

introduces a non-delta item pager to drives_pager, and a func for building an item collision detection cache.  Implementation is coming in the next PR.

---

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

- [x]  No

#### Type of change

- [x] 🌻 Feature

#### Issue(s)

* #3562

#### Test Plan

- [x]  Unit test
- [x] 💚 E2E
This commit is contained in:
Keepers 2023-06-28 09:05:58 -06:00 committed by GitHub
parent df100b9b3b
commit 14639af017
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 178 additions and 5 deletions

View File

@ -30,7 +30,7 @@ func (suite *ContactsPagerIntgSuite) SetupSuite() {
suite.its = newIntegrationTesterSetup(suite.T()) suite.its = newIntegrationTesterSetup(suite.T())
} }
func (suite *ContactsPagerIntgSuite) TestGetItemsInContainerByCollisionKey() { func (suite *ContactsPagerIntgSuite) TestContacts_GetItemsInContainerByCollisionKey() {
t := suite.T() t := suite.T()
ac := suite.its.ac.Contacts() ac := suite.its.ac.Contacts()

View File

@ -37,14 +37,14 @@ var ErrFolderNotFound = clues.New("folder not found")
// GetFolderByName will lookup the specified folder by name within the parentFolderID folder. // GetFolderByName will lookup the specified folder by name within the parentFolderID folder.
func (c Drives) GetFolderByName( func (c Drives) GetFolderByName(
ctx context.Context, ctx context.Context,
driveID, parentFolderID, folderID string, driveID, parentFolderID, folderName string,
) (models.DriveItemable, error) { ) (models.DriveItemable, error) {
// The `Children().Get()` API doesn't yet support $filter, so using that to find a folder // The `Children().Get()` API doesn't yet support $filter, so using that to find a folder
// will be sub-optimal. // will be sub-optimal.
// Instead, we leverage OneDrive path-based addressing - // Instead, we leverage OneDrive path-based addressing -
// https://learn.microsoft.com/en-us/graph/onedrive-addressing-driveitems#path-based-addressing // https://learn.microsoft.com/en-us/graph/onedrive-addressing-driveitems#path-based-addressing
// - which allows us to lookup an item by its path relative to the parent ID // - which allows us to lookup an item by its path relative to the parent ID
rawURL := fmt.Sprintf(itemByPathRawURLFmt, driveID, parentFolderID, folderID) rawURL := fmt.Sprintf(itemByPathRawURLFmt, driveID, parentFolderID, folderName)
builder := drives.NewItemItemsDriveItemItemRequestBuilder(rawURL, c.Stable.Adapter()) builder := drives.NewItemItemsDriveItemItemRequestBuilder(rawURL, c.Stable.Adapter())
foundItem, err := builder.Get(ctx, nil) foundItem, err := builder.Get(ctx, nil)

View File

@ -17,6 +17,81 @@ import (
"github.com/alcionai/corso/src/pkg/logger" "github.com/alcionai/corso/src/pkg/logger"
) )
// ---------------------------------------------------------------------------
// non-delta item pager
// ---------------------------------------------------------------------------
var _ itemPager[models.DriveItemable] = &driveItemPageCtrl{}
type driveItemPageCtrl struct {
gs graph.Servicer
builder *drives.ItemItemsItemChildrenRequestBuilder
options *drives.ItemItemsItemChildrenRequestBuilderGetRequestConfiguration
}
func (c Drives) NewDriveItemPager(
driveID, containerID string,
selectProps ...string,
) itemPager[models.DriveItemable] {
options := &drives.ItemItemsItemChildrenRequestBuilderGetRequestConfiguration{
QueryParameters: &drives.ItemItemsItemChildrenRequestBuilderGetQueryParameters{
Top: ptr.To(maxNonDeltaPageSize),
},
}
if len(selectProps) > 0 {
options.QueryParameters.Select = selectProps
}
builder := c.Stable.
Client().
Drives().
ByDriveId(driveID).
Items().
ByDriveItemId(containerID).
Children()
return &driveItemPageCtrl{c.Stable, builder, options}
}
//lint:ignore U1000 False Positive
func (p *driveItemPageCtrl) getPage(ctx context.Context) (PageLinkValuer[models.DriveItemable], error) {
page, err := p.builder.Get(ctx, p.options)
if err != nil {
return nil, graph.Stack(ctx, err)
}
return EmptyDeltaLinker[models.DriveItemable]{PageLinkValuer: page}, nil
}
//lint:ignore U1000 False Positive
func (p *driveItemPageCtrl) setNext(nextLink string) {
p.builder = drives.NewItemItemsItemChildrenRequestBuilder(nextLink, p.gs.Adapter())
}
func (c Drives) GetItemsInContainerByCollisionKey(
ctx context.Context,
driveID, containerID string,
) (map[string]string, error) {
ctx = clues.Add(ctx, "container_id", containerID)
pager := c.NewDriveItemPager(driveID, containerID, idAnd("name")...)
items, err := enumerateItems(ctx, pager)
if err != nil {
return nil, graph.Wrap(ctx, err, "enumerating drive items")
}
m := map[string]string{}
for _, item := range items {
m[DriveItemCollisionKey(item)] = ptr.Val(item.GetId())
}
return m, nil
}
// ---------------------------------------------------------------------------
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// delta item pager // delta item pager
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -0,0 +1,89 @@
package api_test
import (
"testing"
"github.com/alcionai/clues"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/tester"
)
type DrivePagerIntgSuite struct {
tester.Suite
its intgTesterSetup
}
func TestDrivePagerIntgSuite(t *testing.T) {
suite.Run(t, &DrivePagerIntgSuite{
Suite: tester.NewIntegrationSuite(
t,
[][]string{tester.M365AcctCredEnvs}),
})
}
func (suite *DrivePagerIntgSuite) SetupSuite() {
suite.its = newIntegrationTesterSetup(suite.T())
}
func (suite *DrivePagerIntgSuite) TestDrives_GetItemsInContainerByCollisionKey() {
table := []struct {
name string
driveID string
rootFolderID string
}{
{
name: "user drive",
driveID: suite.its.userDriveID,
rootFolderID: suite.its.userDriveRootFolderID,
},
{
name: "site drive",
driveID: suite.its.siteDriveID,
rootFolderID: suite.its.siteDriveRootFolderID,
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
items, err := suite.its.ac.Stable.
Client().
Drives().
ByDriveId(test.driveID).
Items().
ByDriveItemId(test.rootFolderID).
Children().
Get(ctx, nil)
require.NoError(t, err, clues.ToCore(err))
ims := items.GetValue()
expect := make([]string, 0, len(ims))
assert.NotEmptyf(
t,
ims,
"need at least one item to compare in user %s drive %s folder %s",
suite.its.userID, test.driveID, test.rootFolderID)
results, err := suite.its.ac.Drives().GetItemsInContainerByCollisionKey(ctx, test.driveID, test.rootFolderID)
require.NoError(t, err, clues.ToCore(err))
require.NotEmpty(t, results)
for k, v := range results {
assert.NotEmpty(t, k, "all keys should be populated")
assert.NotEmpty(t, v, "all values should be populated")
}
for _, e := range expect {
_, ok := results[e]
assert.Truef(t, ok, "expected results to contain collision key: %s", e)
}
})
}
}

View File

@ -30,7 +30,7 @@ func (suite *EventsPagerIntgSuite) SetupSuite() {
suite.its = newIntegrationTesterSetup(suite.T()) suite.its = newIntegrationTesterSetup(suite.T())
} }
func (suite *EventsPagerIntgSuite) TestGetItemsInContainerByCollisionKey() { func (suite *EventsPagerIntgSuite) TestEvents_GetItemsInContainerByCollisionKey() {
t := suite.T() t := suite.T()
ac := suite.its.ac.Events() ac := suite.its.ac.Events()

View File

@ -9,10 +9,12 @@ import (
"github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/services/m365/api" "github.com/alcionai/corso/src/pkg/services/m365/api"
"github.com/alcionai/corso/src/pkg/services/m365/api/mock"
) )
type intgTesterSetup struct { type intgTesterSetup struct {
ac api.Client ac api.Client
gockAC api.Client
userID string userID string
userDriveID string userDriveID string
userDriveRootFolderID string userDriveRootFolderID string
@ -34,6 +36,11 @@ func newIntegrationTesterSetup(t *testing.T) intgTesterSetup {
its.ac, err = api.NewClient(creds) its.ac, err = api.NewClient(creds)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
its.gockAC, err = mock.NewClient(creds)
require.NoError(t, err, clues.ToCore(err))
// user drive
its.userID = tester.M365UserID(t) its.userID = tester.M365UserID(t)
userDrive, err := its.ac.Users().GetDefaultDrive(ctx, its.userID) userDrive, err := its.ac.Users().GetDefaultDrive(ctx, its.userID)
@ -48,6 +55,8 @@ func newIntegrationTesterSetup(t *testing.T) intgTesterSetup {
its.siteID = tester.M365SiteID(t) its.siteID = tester.M365SiteID(t)
// site
siteDrive, err := its.ac.Sites().GetDefaultDrive(ctx, its.siteID) siteDrive, err := its.ac.Sites().GetDefaultDrive(ctx, its.siteID)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))

View File

@ -30,7 +30,7 @@ func (suite *MailPagerIntgSuite) SetupSuite() {
suite.its = newIntegrationTesterSetup(suite.T()) suite.its = newIntegrationTesterSetup(suite.T())
} }
func (suite *MailPagerIntgSuite) TestGetItemsInContainerByCollisionKey() { func (suite *MailPagerIntgSuite) TestMail_GetItemsInContainerByCollisionKey() {
t := suite.T() t := suite.T()
ac := suite.its.ac.Mail() ac := suite.its.ac.Mail()