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:
parent
df100b9b3b
commit
14639af017
@ -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()
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
89
src/pkg/services/m365/api/drive_pager_test.go
Normal file
89
src/pkg/services/m365/api/drive_pager_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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()
|
||||||
|
|
||||||
|
|||||||
@ -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))
|
||||||
|
|
||||||
|
|||||||
@ -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()
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user