diff --git a/src/internal/connector/onedrive/drive.go b/src/internal/connector/onedrive/drive.go index 974112650..5cd1967bb 100644 --- a/src/internal/connector/onedrive/drive.go +++ b/src/internal/connector/onedrive/drive.go @@ -3,6 +3,7 @@ package onedrive import ( "context" "fmt" + "strings" "github.com/microsoftgraph/msgraph-sdk-go/drives/item/items" "github.com/microsoftgraph/msgraph-sdk-go/drives/item/items/item" @@ -158,3 +159,70 @@ func newItem(name string, folder bool) models.DriveItemable { return itemToCreate } + +// GetAllFolders returns all folders in all drives for the given user. If a +// prefix is given, returns all folders with that prefix, regardless of if they +// are a subfolder or top-level folder in the hierarchy. +func GetAllFolders( + ctx context.Context, + gs graph.Service, + userID string, + prefix string, +) ([]models.DriveItemable, error) { + drives, err := drives(ctx, gs, userID) + if err != nil { + return nil, errors.Wrap(err, "getting OneDrive folders") + } + + res := []models.DriveItemable{} + + for _, d := range drives { + err = collectItems( + ctx, + gs, + *d.GetId(), + func(innerCtx context.Context, driveID string, items []models.DriveItemable) error { + for _, item := range items { + // Skip the root item. + if item.GetRoot() != nil { + continue + } + + // Only selecting folders right now, not packages. + if item.GetFolder() == nil { + continue + } + + if !strings.HasPrefix(*item.GetName(), prefix) { + continue + } + + // Add the item instead of the folder because the item has more + // functionality. + res = append(res, item) + } + + return nil + }, + ) + if err != nil { + return nil, errors.Wrapf(err, "getting items for drive %s", *d.GetName()) + } + } + + return res, nil +} + +func DeleteItem( + ctx context.Context, + gs graph.Service, + driveID string, + itemID string, +) error { + err := gs.Client().DrivesById(driveID).ItemsById(itemID).Delete(ctx, nil) + if err != nil { + return errors.Wrapf(err, "deleting item with ID %s", itemID) + } + + return nil +} diff --git a/src/internal/connector/onedrive/drive_test.go b/src/internal/connector/onedrive/drive_test.go new file mode 100644 index 000000000..f55aa53d3 --- /dev/null +++ b/src/internal/connector/onedrive/drive_test.go @@ -0,0 +1,146 @@ +package onedrive + +import ( + "context" + "strings" + "testing" + + msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/alcionai/corso/src/internal/common" + "github.com/alcionai/corso/src/internal/connector/graph" + "github.com/alcionai/corso/src/internal/tester" + "github.com/alcionai/corso/src/pkg/account" +) + +// TODO(ashmrtn): Merge with similar structs in graph and exchange packages. +type testService struct { + adapter msgraphsdk.GraphRequestAdapter + client msgraphsdk.GraphServiceClient + failFast bool + credentials account.M365Config +} + +func (ts *testService) Client() *msgraphsdk.GraphServiceClient { + return &ts.client +} + +func (ts *testService) Adapter() *msgraphsdk.GraphRequestAdapter { + return &ts.adapter +} + +func (ts *testService) ErrPolicy() bool { + return ts.failFast +} + +// TODO(ashmrtn): Merge with similar functions in connector and exchange +// packages. +func loadService(t *testing.T) *testService { + a := tester.NewM365Account(t) + m365, err := a.M365Config() + require.NoError(t, err) + + adapter, err := graph.CreateAdapter( + m365.TenantID, + m365.ClientID, + m365.ClientSecret, + ) + require.NoError(t, err) + + service := &testService{ + adapter: *adapter, + client: *msgraphsdk.NewGraphServiceClient(adapter), + failFast: false, + credentials: m365, + } + + return service +} + +type OneDriveSuite struct { + suite.Suite + userID string +} + +func TestOneDriveDriveSuite(t *testing.T) { + if err := tester.RunOnAny( + tester.CorsoCITests, + tester.CorsoOneDriveTests, + ); err != nil { + t.Skip(err) + } + + suite.Run(t, new(OneDriveSuite)) +} + +func (suite *OneDriveSuite) SetupSuite() { + suite.userID = tester.M365UserID(suite.T()) +} + +func (suite *OneDriveSuite) TestCreateGetDeleteFolder() { + t := suite.T() + ctx := context.Background() + folderIDs := []string{} + folderName1 := "Corso_Folder_Test_" + common.FormatNow(common.SimpleTimeTesting) + folderElements := []string{folderName1} + gs := loadService(t) + + drives, err := drives(ctx, gs, suite.userID) + require.NoError(t, err) + require.NotEmpty(t, drives) + + driveID := *drives[0].GetId() + + folderID, err := createRestoreFolders(ctx, gs, driveID, folderElements) + require.NoError(t, err) + + folderIDs = append(folderIDs, folderID) + + defer func() { + assert.NoError(t, DeleteItem(ctx, gs, driveID, folderIDs[0])) + }() + + folderName2 := "Corso_Folder_Test_" + common.FormatNow(common.SimpleTimeTesting) + folderElements = append(folderElements, folderName2) + + folderID, err = createRestoreFolders(ctx, gs, driveID, folderElements) + require.NoError(t, err) + + folderIDs = append(folderIDs, folderID) + + table := []struct { + name string + prefix string + }{ + { + name: "NoPrefix", + prefix: "", + }, + { + name: "Prefix", + prefix: "Corso_Folder_Test", + }, + } + + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + allFolders, err := GetAllFolders(ctx, gs, suite.userID, test.prefix) + require.NoError(t, err) + + foundFolderIDs := []string{} + + for _, f := range allFolders { + if *f.GetName() == folderName1 || *f.GetName() == folderName2 { + foundFolderIDs = append(foundFolderIDs, *f.GetId()) + } + + assert.True(t, strings.HasPrefix(*f.GetName(), test.prefix), "folder prefix") + } + + assert.ElementsMatch(t, folderIDs, foundFolderIDs) + }) + } +} diff --git a/src/internal/tester/integration_runners.go b/src/internal/tester/integration_runners.go index f9a194f4b..f3c8e26c1 100644 --- a/src/internal/tester/integration_runners.go +++ b/src/internal/tester/integration_runners.go @@ -19,6 +19,7 @@ const ( CorsoGraphConnectorTests = "CORSO_GRAPH_CONNECTOR_TESTS" CorsoKopiaWrapperTests = "CORSO_KOPIA_WRAPPER_TESTS" CorsoModelStoreTests = "CORSO_MODEL_STORE_TESTS" + CorsoOneDriveTests = "CORSO_ONE_DRIVE_TESTS" CorsoOperationTests = "CORSO_OPERATION_TESTS" CorsoRepositoryTests = "CORSO_REPOSITORY_TESTS" )