diff --git a/src/internal/connector/graph_connector_helper_test.go b/src/internal/connector/graph_connector_helper_test.go index 628c68d36..6934162ab 100644 --- a/src/internal/connector/graph_connector_helper_test.go +++ b/src/internal/connector/graph_connector_helper_test.go @@ -722,14 +722,14 @@ func permissionEqual(expected metadata.Permission, got metadata.Permission) bool return true } -func compareOneDriveItem( +func compareDriveItem( t *testing.T, expected map[string][]byte, item data.Stream, restorePermissions bool, rootDir bool, ) bool { - // Skip OneDrive permissions in the folder that used to be the root. We don't + // Skip Drive permissions in the folder that used to be the root. We don't // have a good way to materialize these in the test right now. if rootDir && item.UUID() == metadata.DirMetaFileSuffix { return false @@ -747,7 +747,7 @@ func compareOneDriveItem( ) if isMeta { - var itemType *onedrive.MetadataItem + var itemType *metadata.Item assert.IsType(t, itemType, item) } else { @@ -824,8 +824,7 @@ func compareOneDriveItem( t, expectedMeta.Permissions, itemPerms, - permissionEqual, - ) + permissionEqual) return true } @@ -887,7 +886,7 @@ func compareItem( } case path.OneDriveService: - return compareOneDriveItem(t, expected, item, restorePermissions, rootDir) + return compareDriveItem(t, expected, item, restorePermissions, rootDir) case path.SharePointService: if category != path.LibrariesCategory { @@ -895,7 +894,7 @@ func compareItem( } // SharePoint libraries reuses OneDrive code. - return compareOneDriveItem(t, expected, item, restorePermissions, rootDir) + return compareDriveItem(t, expected, item, restorePermissions, rootDir) default: assert.FailNowf(t, "unexpected service: %s", service.String()) diff --git a/src/internal/connector/onedrive/collection.go b/src/internal/connector/onedrive/collection.go index 2a827ce27..a4caafae2 100644 --- a/src/internal/connector/onedrive/collection.go +++ b/src/internal/connector/onedrive/collection.go @@ -37,8 +37,8 @@ var ( _ data.Stream = &Item{} _ data.StreamInfo = &Item{} _ data.StreamModTime = &Item{} - _ data.Stream = &MetadataItem{} - _ data.StreamModTime = &MetadataItem{} + _ data.Stream = &metadata.Item{} + _ data.StreamModTime = &metadata.Item{} ) // Collection represents a set of OneDrive objects retrieved from M365 @@ -120,12 +120,12 @@ func pathToLocation(p path.Path) (*path.Builder, error) { return nil, nil } - odp, err := path.ToOneDrivePath(p) + dp, err := path.ToDrivePath(p) if err != nil { return nil, err } - return path.Builder{}.Append(odp.Root).Append(odp.Folders...), nil + return path.Builder{}.Append(dp.Root).Append(dp.Folders...), nil } // NewCollection creates a Collection @@ -306,53 +306,14 @@ type Item struct { info details.ItemInfo } -func (od *Item) UUID() string { - return od.id -} - -func (od *Item) ToReader() io.ReadCloser { - return od.data -} - // Deleted implements an interface function. However, OneDrive items are marked // as deleted by adding them to the exclude list so this can always return // false. -func (od Item) Deleted() bool { - return false -} - -func (od *Item) Info() details.ItemInfo { - return od.info -} - -func (od *Item) ModTime() time.Time { - return od.info.Modified() -} - -type MetadataItem struct { - id string - data io.ReadCloser - modTime time.Time -} - -func (od *MetadataItem) UUID() string { - return od.id -} - -func (od *MetadataItem) ToReader() io.ReadCloser { - return od.data -} - -// Deleted implements an interface function. However, OneDrive items are marked -// as deleted by adding them to the exclude list so this can always return -// false. -func (od MetadataItem) Deleted() bool { - return false -} - -func (od *MetadataItem) ModTime() time.Time { - return od.modTime -} +func (i Item) Deleted() bool { return false } +func (i *Item) UUID() string { return i.id } +func (i *Item) ToReader() io.ReadCloser { return i.data } +func (i *Item) Info() details.ItemInfo { return i.info } +func (i *Item) ModTime() time.Time { return i.info.Modified() } // getDriveItemContent fetch drive item's contents with retries func (oc *Collection) getDriveItemContent( @@ -602,12 +563,12 @@ func (oc *Collection) populateItems(ctx context.Context, errs *fault.Bus) { return progReader, nil }) - oc.data <- &MetadataItem{ - id: metaFileName + metaSuffix, - data: metaReader, + oc.data <- &metadata.Item{ + ID: metaFileName + metaSuffix, + Data: metaReader, // Metadata file should always use the latest time as // permissions change does not update mod time. - modTime: time.Now(), + Mod: time.Now(), } // Item read successfully, add to collection diff --git a/src/internal/connector/onedrive/metadata/metadata.go b/src/internal/connector/onedrive/metadata/metadata.go index 51e9105d5..32fb33707 100644 --- a/src/internal/connector/onedrive/metadata/metadata.go +++ b/src/internal/connector/onedrive/metadata/metadata.go @@ -1,5 +1,10 @@ package metadata +import ( + "io" + "time" +) + // ItemMeta contains metadata about the Item. It gets stored in a // separate file in kopia type Metadata struct { @@ -10,3 +15,17 @@ type Metadata struct { SharingMode SharingMode `json:"permissionMode,omitempty"` Permissions []Permission `json:"permissions,omitempty"` } + +type Item struct { + ID string + Data io.ReadCloser + Mod time.Time +} + +// Deleted implements an interface function. However, OneDrive items are marked +// as deleted by adding them to the exclude list so this can always return +// false. +func (i *Item) Deleted() bool { return false } +func (i *Item) UUID() string { return i.ID } +func (i *Item) ToReader() io.ReadCloser { return i.Data } +func (i *Item) ModTime() time.Time { return i.Mod } diff --git a/src/internal/connector/onedrive/permission.go b/src/internal/connector/onedrive/permission.go index 101ae1ddf..7cd4b530d 100644 --- a/src/internal/connector/onedrive/permission.go +++ b/src/internal/connector/onedrive/permission.go @@ -22,12 +22,12 @@ func getParentMetadata( ) (metadata.Metadata, error) { parentMeta, ok := metas[parentPath.String()] if !ok { - onedrivePath, err := path.ToOneDrivePath(parentPath) + drivePath, err := path.ToDrivePath(parentPath) if err != nil { return metadata.Metadata{}, clues.Wrap(err, "invalid restore path") } - if len(onedrivePath.Folders) != 0 { + if len(drivePath.Folders) != 0 { return metadata.Metadata{}, clues.Wrap(err, "computing item permissions") } @@ -88,7 +88,10 @@ func getCollectionMetadata( // traversing folderMetas and finding the first item with custom // permissions. folderMetas is expected to have all the parent // directory metas for this to work. -func computeParentPermissions(itemPath path.Path, folderMetas map[string]metadata.Metadata) (metadata.Metadata, error) { +func computeParentPermissions( + itemPath path.Path, + folderMetas map[string]metadata.Metadata, +) (metadata.Metadata, error) { var ( parent path.Path meta metadata.Metadata @@ -105,12 +108,12 @@ func computeParentPermissions(itemPath path.Path, folderMetas map[string]metadat return metadata.Metadata{}, clues.New("getting parent") } - onedrivePath, err := path.ToOneDrivePath(parent) + drivePath, err := path.ToDrivePath(parent) if err != nil { return metadata.Metadata{}, clues.New("get parent path") } - if len(onedrivePath.Folders) == 0 { + if len(drivePath.Folders) == 0 { return metadata.Metadata{}, nil } diff --git a/src/internal/connector/onedrive/permission_test.go b/src/internal/connector/onedrive/permission_test.go index 77c0d63ca..0c0a95d1a 100644 --- a/src/internal/connector/onedrive/permission_test.go +++ b/src/internal/connector/onedrive/permission_test.go @@ -23,27 +23,34 @@ func TestPermissionsUnitTestSuite(t *testing.T) { } func (suite *PermissionsUnitTestSuite) TestComputeParentPermissions() { + runComputeParentPermissionsTest(suite, path.OneDriveService, path.FilesCategory, "user") +} + +func runComputeParentPermissionsTest( + suite *PermissionsUnitTestSuite, + service path.ServiceType, + category path.CategoryType, + resourceOwner string, +) { entryPath := fmt.Sprintf(rootDrivePattern, "drive-id") + "/level0/level1/level2/entry" rootEntryPath := fmt.Sprintf(rootDrivePattern, "drive-id") + "/entry" entry, err := path.Build( "tenant", - "user", - path.OneDriveService, - path.FilesCategory, + resourceOwner, + service, + category, false, - strings.Split(entryPath, "/")..., - ) + strings.Split(entryPath, "/")...) require.NoError(suite.T(), err, "creating path") rootEntry, err := path.Build( "tenant", - "user", - path.OneDriveService, - path.FilesCategory, + resourceOwner, + service, + category, false, - strings.Split(rootEntryPath, "/")..., - ) + strings.Split(rootEntryPath, "/")...) require.NoError(suite.T(), err, "creating path") level2, err := entry.Dir() diff --git a/src/internal/connector/onedrive/restore.go b/src/internal/connector/onedrive/restore.go index 5cfa27861..0cff8b465 100644 --- a/src/internal/connector/onedrive/restore.go +++ b/src/internal/connector/onedrive/restore.go @@ -152,7 +152,7 @@ func RestoreCollection( ctx, end := diagnostics.Span(ctx, "gc:oneDrive:restoreCollection", diagnostics.Label("path", directory)) defer end() - drivePath, err := path.ToOneDrivePath(directory) + drivePath, err := path.ToDrivePath(directory) if err != nil { return metrics, clues.Wrap(err, "creating drive path").WithClues(ctx) } @@ -791,12 +791,12 @@ func AugmentRestorePaths(backupVersion int, paths []path.Path) ([]path.Path, err return nil, err } - onedrivePath, err := path.ToOneDrivePath(np) + drivePath, err := path.ToDrivePath(np) if err != nil { return nil, err } - if len(onedrivePath.Folders) == 0 { + if len(drivePath.Folders) == 0 { break } diff --git a/src/pkg/backup/details/details.go b/src/pkg/backup/details/details.go index 677079212..b731d9bbe 100644 --- a/src/pkg/backup/details/details.go +++ b/src/pkg/backup/details/details.go @@ -522,9 +522,9 @@ func (de Entry) ToLocationIDer(backupVersion int) (LocationIDer, error) { return nil, clues.Wrap(err, "getting item RepoRef") } - p, err := path.ToOneDrivePath(rr) + p, err := path.ToDrivePath(rr) if err != nil { - return nil, clues.New("converting RepoRef to OneDrive path") + return nil, clues.New("converting RepoRef to drive path") } baseLoc := path.Builder{}.Append(p.Root).Append(p.Folders...) diff --git a/src/pkg/path/onedrive.go b/src/pkg/path/drive.go similarity index 78% rename from src/pkg/path/onedrive.go rename to src/pkg/path/drive.go index 48c443311..b073ff125 100644 --- a/src/pkg/path/onedrive.go +++ b/src/pkg/path/drive.go @@ -8,19 +8,21 @@ import "github.com/alcionai/clues" // // driveID is `b!X_8Z2zuXpkKkXZsr7gThk9oJpuj0yXVGnK5_VjRRPK-q725SX_8ZQJgFDK8PlFxA` and // folders[] is []{"Folder1", "Folder2"} +// +// Should be compatible with all drive-based services (ex: oneDrive, sharePoint Libraries, etc) type DrivePath struct { DriveID string Root string Folders Elements } -func ToOneDrivePath(p Path) (*DrivePath, error) { +func ToDrivePath(p Path) (*DrivePath, error) { folders := p.Folders() // Must be at least `drives//root:` if len(folders) < 3 { return nil, clues. - New("folder path doesn't match expected format for OneDrive items"). + New("folder path doesn't match expected format for Drive items"). With("path_folders", p.Folder(false)) } @@ -29,7 +31,7 @@ func ToOneDrivePath(p Path) (*DrivePath, error) { // Returns the path to the folder within the drive (i.e. under `root:`) func GetDriveFolderPath(p Path) (string, error) { - drivePath, err := ToOneDrivePath(p) + drivePath, err := ToDrivePath(p) if err != nil { return "", err } diff --git a/src/pkg/path/onedrive_test.go b/src/pkg/path/drive_test.go similarity index 97% rename from src/pkg/path/onedrive_test.go rename to src/pkg/path/drive_test.go index d81c59e31..cddd050bf 100644 --- a/src/pkg/path/onedrive_test.go +++ b/src/pkg/path/drive_test.go @@ -54,7 +54,7 @@ func (suite *OneDrivePathSuite) Test_ToOneDrivePath() { p, err := path.Build("tenant", "user", path.OneDriveService, path.FilesCategory, false, tt.pathElements...) require.NoError(suite.T(), err, clues.ToCore(err)) - got, err := path.ToOneDrivePath(p) + got, err := path.ToDrivePath(p) tt.errCheck(t, err) if err != nil { return