Dedupe collection items for OneDrive (#2118)

## Description

Under some circumstances items can be returned multiple times when iterating through the endpoint. This dedupes items within a single collection.

It does not address the following:
* item being removed from the collection during iteration
* item appearing multiple times in different collections

## 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
- [x] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Test
- [ ] 💻 CI/Deployment
- [x] 🧹 Tech Debt/Cleanup

## Issue(s)

* #1954 

## Test Plan

- [ ] 💪 Manual
- [x]  Unit test
- [ ] 💚 E2E
This commit is contained in:
ashmrtn 2023-01-11 16:00:41 -08:00 committed by GitHub
parent 53c5828caa
commit 32b11cdca8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 47 additions and 11 deletions

View File

@ -49,7 +49,7 @@ type Collection struct {
// represents // represents
folderPath path.Path folderPath path.Path
// M365 IDs of file items within this collection // M365 IDs of file items within this collection
driveItems []models.DriveItemable driveItems map[string]models.DriveItemable
// M365 ID of the drive this collection was created from // M365 ID of the drive this collection was created from
driveID string driveID string
source driveSource source driveSource
@ -79,6 +79,7 @@ func NewCollection(
) *Collection { ) *Collection {
c := &Collection{ c := &Collection{
folderPath: folderPath, folderPath: folderPath,
driveItems: map[string]models.DriveItemable{},
driveID: driveID, driveID: driveID,
source: source, source: source,
service: service, service: service,
@ -101,7 +102,7 @@ func NewCollection(
// Adds an itemID to the collection // Adds an itemID to the collection
// This will make it eligible to be populated // This will make it eligible to be populated
func (oc *Collection) Add(item models.DriveItemable) { func (oc *Collection) Add(item models.DriveItemable) {
oc.driveItems = append(oc.driveItems, item) oc.driveItems[*item.GetId()] = item
} }
// Items() returns the channel containing M365 Exchange objects // Items() returns the channel containing M365 Exchange objects

View File

@ -62,12 +62,14 @@ func (suite *CollectionUnitTestSuite) TestCollection() {
table := []struct { table := []struct {
name string name string
numInstances int
source driveSource source driveSource
itemReader itemReaderFunc itemReader itemReaderFunc
infoFrom func(*testing.T, details.ItemInfo) (string, string) infoFrom func(*testing.T, details.ItemInfo) (string, string)
}{ }{
{ {
name: "oneDrive", name: "oneDrive, no duplicates",
numInstances: 1,
source: OneDriveSource, source: OneDriveSource,
itemReader: func(context.Context, models.DriveItemable) (details.ItemInfo, io.ReadCloser, error) { itemReader: func(context.Context, models.DriveItemable) (details.ItemInfo, io.ReadCloser, error) {
return details.ItemInfo{OneDrive: &details.OneDriveInfo{ItemName: testItemName}}, return details.ItemInfo{OneDrive: &details.OneDriveInfo{ItemName: testItemName}},
@ -80,7 +82,36 @@ func (suite *CollectionUnitTestSuite) TestCollection() {
}, },
}, },
{ {
name: "sharePoint", name: "oneDrive, duplicates",
numInstances: 3,
source: OneDriveSource,
itemReader: func(context.Context, models.DriveItemable) (details.ItemInfo, io.ReadCloser, error) {
return details.ItemInfo{OneDrive: &details.OneDriveInfo{ItemName: testItemName}},
io.NopCloser(bytes.NewReader(testItemData)),
nil
},
infoFrom: func(t *testing.T, dii details.ItemInfo) (string, string) {
require.NotNil(t, dii.OneDrive)
return dii.OneDrive.ItemName, dii.OneDrive.ParentPath
},
},
{
name: "sharePoint, no duplicates",
numInstances: 1,
source: SharePointSource,
itemReader: func(context.Context, models.DriveItemable) (details.ItemInfo, io.ReadCloser, error) {
return details.ItemInfo{SharePoint: &details.SharePointInfo{ItemName: testItemName}},
io.NopCloser(bytes.NewReader(testItemData)),
nil
},
infoFrom: func(t *testing.T, dii details.ItemInfo) (string, string) {
require.NotNil(t, dii.SharePoint)
return dii.SharePoint.ItemName, dii.SharePoint.ParentPath
},
},
{
name: "sharePoint, duplicates",
numInstances: 3,
source: SharePointSource, source: SharePointSource,
itemReader: func(context.Context, models.DriveItemable) (details.ItemInfo, io.ReadCloser, error) { itemReader: func(context.Context, models.DriveItemable) (details.ItemInfo, io.ReadCloser, error) {
return details.ItemInfo{SharePoint: &details.SharePointInfo{ItemName: testItemName}}, return details.ItemInfo{SharePoint: &details.SharePointInfo{ItemName: testItemName}},
@ -119,7 +150,11 @@ func (suite *CollectionUnitTestSuite) TestCollection() {
// Set a item reader, add an item and validate we get the item back // Set a item reader, add an item and validate we get the item back
mockItem := models.NewDriveItem() mockItem := models.NewDriveItem()
mockItem.SetId(&testItemID) mockItem.SetId(&testItemID)
for i := 0; i < test.numInstances; i++ {
coll.Add(mockItem) coll.Add(mockItem)
}
coll.itemReader = test.itemReader coll.itemReader = test.itemReader
// Read items from the collection // Read items from the collection