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())
}
func (suite *ContactsPagerIntgSuite) TestGetItemsInContainerByCollisionKey() {
func (suite *ContactsPagerIntgSuite) TestContacts_GetItemsInContainerByCollisionKey() {
t := suite.T()
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.
func (c Drives) GetFolderByName(
ctx context.Context,
driveID, parentFolderID, folderID string,
driveID, parentFolderID, folderName string,
) (models.DriveItemable, error) {
// The `Children().Get()` API doesn't yet support $filter, so using that to find a folder
// will be sub-optimal.
// Instead, we leverage OneDrive 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
rawURL := fmt.Sprintf(itemByPathRawURLFmt, driveID, parentFolderID, folderID)
rawURL := fmt.Sprintf(itemByPathRawURLFmt, driveID, parentFolderID, folderName)
builder := drives.NewItemItemsDriveItemItemRequestBuilder(rawURL, c.Stable.Adapter())
foundItem, err := builder.Get(ctx, nil)

View File

@ -17,6 +17,81 @@ import (
"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
// ---------------------------------------------------------------------------

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())
}
func (suite *EventsPagerIntgSuite) TestGetItemsInContainerByCollisionKey() {
func (suite *EventsPagerIntgSuite) TestEvents_GetItemsInContainerByCollisionKey() {
t := suite.T()
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/tester"
"github.com/alcionai/corso/src/pkg/services/m365/api"
"github.com/alcionai/corso/src/pkg/services/m365/api/mock"
)
type intgTesterSetup struct {
ac api.Client
gockAC api.Client
userID string
userDriveID string
userDriveRootFolderID string
@ -34,6 +36,11 @@ func newIntegrationTesterSetup(t *testing.T) intgTesterSetup {
its.ac, err = api.NewClient(creds)
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)
userDrive, err := its.ac.Users().GetDefaultDrive(ctx, its.userID)
@ -48,6 +55,8 @@ func newIntegrationTesterSetup(t *testing.T) intgTesterSetup {
its.siteID = tester.M365SiteID(t)
// site
siteDrive, err := its.ac.Sites().GetDefaultDrive(ctx, its.siteID)
require.NoError(t, err, clues.ToCore(err))

View File

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