move onedrive.MetadataItem to metadata.Item (#3235)
In keeping with other changes that migrate shared metadata to the onedrive/metadata pkg for exported access. --- #### Does this PR need a docs update or release note? - [x] ⛔ No #### Type of change - [x] 🧹 Tech Debt/Cleanup #### Issue(s) * #3135 #### Test Plan - [x] ⚡ Unit test - [x] 💚 E2E
This commit is contained in:
parent
d067a79483
commit
7326730e0d
@ -722,14 +722,14 @@ func permissionEqual(expected metadata.Permission, got metadata.Permission) bool
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func compareOneDriveItem(
|
func compareDriveItem(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
expected map[string][]byte,
|
expected map[string][]byte,
|
||||||
item data.Stream,
|
item data.Stream,
|
||||||
restorePermissions bool,
|
restorePermissions bool,
|
||||||
rootDir bool,
|
rootDir bool,
|
||||||
) 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.
|
// have a good way to materialize these in the test right now.
|
||||||
if rootDir && item.UUID() == metadata.DirMetaFileSuffix {
|
if rootDir && item.UUID() == metadata.DirMetaFileSuffix {
|
||||||
return false
|
return false
|
||||||
@ -747,7 +747,7 @@ func compareOneDriveItem(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if isMeta {
|
if isMeta {
|
||||||
var itemType *onedrive.MetadataItem
|
var itemType *metadata.Item
|
||||||
|
|
||||||
assert.IsType(t, itemType, item)
|
assert.IsType(t, itemType, item)
|
||||||
} else {
|
} else {
|
||||||
@ -824,8 +824,7 @@ func compareOneDriveItem(
|
|||||||
t,
|
t,
|
||||||
expectedMeta.Permissions,
|
expectedMeta.Permissions,
|
||||||
itemPerms,
|
itemPerms,
|
||||||
permissionEqual,
|
permissionEqual)
|
||||||
)
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -887,7 +886,7 @@ func compareItem(
|
|||||||
}
|
}
|
||||||
|
|
||||||
case path.OneDriveService:
|
case path.OneDriveService:
|
||||||
return compareOneDriveItem(t, expected, item, restorePermissions, rootDir)
|
return compareDriveItem(t, expected, item, restorePermissions, rootDir)
|
||||||
|
|
||||||
case path.SharePointService:
|
case path.SharePointService:
|
||||||
if category != path.LibrariesCategory {
|
if category != path.LibrariesCategory {
|
||||||
@ -895,7 +894,7 @@ func compareItem(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SharePoint libraries reuses OneDrive code.
|
// SharePoint libraries reuses OneDrive code.
|
||||||
return compareOneDriveItem(t, expected, item, restorePermissions, rootDir)
|
return compareDriveItem(t, expected, item, restorePermissions, rootDir)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert.FailNowf(t, "unexpected service: %s", service.String())
|
assert.FailNowf(t, "unexpected service: %s", service.String())
|
||||||
|
|||||||
@ -37,8 +37,8 @@ var (
|
|||||||
_ data.Stream = &Item{}
|
_ data.Stream = &Item{}
|
||||||
_ data.StreamInfo = &Item{}
|
_ data.StreamInfo = &Item{}
|
||||||
_ data.StreamModTime = &Item{}
|
_ data.StreamModTime = &Item{}
|
||||||
_ data.Stream = &MetadataItem{}
|
_ data.Stream = &metadata.Item{}
|
||||||
_ data.StreamModTime = &MetadataItem{}
|
_ data.StreamModTime = &metadata.Item{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Collection represents a set of OneDrive objects retrieved from M365
|
// 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
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
odp, err := path.ToOneDrivePath(p)
|
dp, err := path.ToDrivePath(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// NewCollection creates a Collection
|
||||||
@ -306,53 +306,14 @@ type Item struct {
|
|||||||
info details.ItemInfo
|
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
|
// Deleted implements an interface function. However, OneDrive items are marked
|
||||||
// as deleted by adding them to the exclude list so this can always return
|
// as deleted by adding them to the exclude list so this can always return
|
||||||
// false.
|
// false.
|
||||||
func (od Item) Deleted() bool {
|
func (i Item) Deleted() bool { return false }
|
||||||
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 (od *Item) Info() details.ItemInfo {
|
func (i *Item) ModTime() time.Time { return i.info.Modified() }
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// getDriveItemContent fetch drive item's contents with retries
|
// getDriveItemContent fetch drive item's contents with retries
|
||||||
func (oc *Collection) getDriveItemContent(
|
func (oc *Collection) getDriveItemContent(
|
||||||
@ -602,12 +563,12 @@ func (oc *Collection) populateItems(ctx context.Context, errs *fault.Bus) {
|
|||||||
return progReader, nil
|
return progReader, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
oc.data <- &MetadataItem{
|
oc.data <- &metadata.Item{
|
||||||
id: metaFileName + metaSuffix,
|
ID: metaFileName + metaSuffix,
|
||||||
data: metaReader,
|
Data: metaReader,
|
||||||
// Metadata file should always use the latest time as
|
// Metadata file should always use the latest time as
|
||||||
// permissions change does not update mod time.
|
// permissions change does not update mod time.
|
||||||
modTime: time.Now(),
|
Mod: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Item read successfully, add to collection
|
// Item read successfully, add to collection
|
||||||
|
|||||||
@ -1,5 +1,10 @@
|
|||||||
package metadata
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
// ItemMeta contains metadata about the Item. It gets stored in a
|
// ItemMeta contains metadata about the Item. It gets stored in a
|
||||||
// separate file in kopia
|
// separate file in kopia
|
||||||
type Metadata struct {
|
type Metadata struct {
|
||||||
@ -10,3 +15,17 @@ type Metadata struct {
|
|||||||
SharingMode SharingMode `json:"permissionMode,omitempty"`
|
SharingMode SharingMode `json:"permissionMode,omitempty"`
|
||||||
Permissions []Permission `json:"permissions,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 }
|
||||||
|
|||||||
@ -22,12 +22,12 @@ func getParentMetadata(
|
|||||||
) (metadata.Metadata, error) {
|
) (metadata.Metadata, error) {
|
||||||
parentMeta, ok := metas[parentPath.String()]
|
parentMeta, ok := metas[parentPath.String()]
|
||||||
if !ok {
|
if !ok {
|
||||||
onedrivePath, err := path.ToOneDrivePath(parentPath)
|
drivePath, err := path.ToDrivePath(parentPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return metadata.Metadata{}, clues.Wrap(err, "invalid restore path")
|
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")
|
return metadata.Metadata{}, clues.Wrap(err, "computing item permissions")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +88,10 @@ func getCollectionMetadata(
|
|||||||
// traversing folderMetas and finding the first item with custom
|
// traversing folderMetas and finding the first item with custom
|
||||||
// permissions. folderMetas is expected to have all the parent
|
// permissions. folderMetas is expected to have all the parent
|
||||||
// directory metas for this to work.
|
// 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 (
|
var (
|
||||||
parent path.Path
|
parent path.Path
|
||||||
meta metadata.Metadata
|
meta metadata.Metadata
|
||||||
@ -105,12 +108,12 @@ func computeParentPermissions(itemPath path.Path, folderMetas map[string]metadat
|
|||||||
return metadata.Metadata{}, clues.New("getting parent")
|
return metadata.Metadata{}, clues.New("getting parent")
|
||||||
}
|
}
|
||||||
|
|
||||||
onedrivePath, err := path.ToOneDrivePath(parent)
|
drivePath, err := path.ToDrivePath(parent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return metadata.Metadata{}, clues.New("get parent path")
|
return metadata.Metadata{}, clues.New("get parent path")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(onedrivePath.Folders) == 0 {
|
if len(drivePath.Folders) == 0 {
|
||||||
return metadata.Metadata{}, nil
|
return metadata.Metadata{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,27 +23,34 @@ func TestPermissionsUnitTestSuite(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *PermissionsUnitTestSuite) TestComputeParentPermissions() {
|
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"
|
entryPath := fmt.Sprintf(rootDrivePattern, "drive-id") + "/level0/level1/level2/entry"
|
||||||
rootEntryPath := fmt.Sprintf(rootDrivePattern, "drive-id") + "/entry"
|
rootEntryPath := fmt.Sprintf(rootDrivePattern, "drive-id") + "/entry"
|
||||||
|
|
||||||
entry, err := path.Build(
|
entry, err := path.Build(
|
||||||
"tenant",
|
"tenant",
|
||||||
"user",
|
resourceOwner,
|
||||||
path.OneDriveService,
|
service,
|
||||||
path.FilesCategory,
|
category,
|
||||||
false,
|
false,
|
||||||
strings.Split(entryPath, "/")...,
|
strings.Split(entryPath, "/")...)
|
||||||
)
|
|
||||||
require.NoError(suite.T(), err, "creating path")
|
require.NoError(suite.T(), err, "creating path")
|
||||||
|
|
||||||
rootEntry, err := path.Build(
|
rootEntry, err := path.Build(
|
||||||
"tenant",
|
"tenant",
|
||||||
"user",
|
resourceOwner,
|
||||||
path.OneDriveService,
|
service,
|
||||||
path.FilesCategory,
|
category,
|
||||||
false,
|
false,
|
||||||
strings.Split(rootEntryPath, "/")...,
|
strings.Split(rootEntryPath, "/")...)
|
||||||
)
|
|
||||||
require.NoError(suite.T(), err, "creating path")
|
require.NoError(suite.T(), err, "creating path")
|
||||||
|
|
||||||
level2, err := entry.Dir()
|
level2, err := entry.Dir()
|
||||||
|
|||||||
@ -152,7 +152,7 @@ func RestoreCollection(
|
|||||||
ctx, end := diagnostics.Span(ctx, "gc:oneDrive:restoreCollection", diagnostics.Label("path", directory))
|
ctx, end := diagnostics.Span(ctx, "gc:oneDrive:restoreCollection", diagnostics.Label("path", directory))
|
||||||
defer end()
|
defer end()
|
||||||
|
|
||||||
drivePath, err := path.ToOneDrivePath(directory)
|
drivePath, err := path.ToDrivePath(directory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return metrics, clues.Wrap(err, "creating drive path").WithClues(ctx)
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
onedrivePath, err := path.ToOneDrivePath(np)
|
drivePath, err := path.ToDrivePath(np)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(onedrivePath.Folders) == 0 {
|
if len(drivePath.Folders) == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -522,9 +522,9 @@ func (de Entry) ToLocationIDer(backupVersion int) (LocationIDer, error) {
|
|||||||
return nil, clues.Wrap(err, "getting item RepoRef")
|
return nil, clues.Wrap(err, "getting item RepoRef")
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := path.ToOneDrivePath(rr)
|
p, err := path.ToDrivePath(rr)
|
||||||
if err != nil {
|
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...)
|
baseLoc := path.Builder{}.Append(p.Root).Append(p.Folders...)
|
||||||
|
|||||||
@ -8,19 +8,21 @@ import "github.com/alcionai/clues"
|
|||||||
//
|
//
|
||||||
// driveID is `b!X_8Z2zuXpkKkXZsr7gThk9oJpuj0yXVGnK5_VjRRPK-q725SX_8ZQJgFDK8PlFxA` and
|
// driveID is `b!X_8Z2zuXpkKkXZsr7gThk9oJpuj0yXVGnK5_VjRRPK-q725SX_8ZQJgFDK8PlFxA` and
|
||||||
// folders[] is []{"Folder1", "Folder2"}
|
// folders[] is []{"Folder1", "Folder2"}
|
||||||
|
//
|
||||||
|
// Should be compatible with all drive-based services (ex: oneDrive, sharePoint Libraries, etc)
|
||||||
type DrivePath struct {
|
type DrivePath struct {
|
||||||
DriveID string
|
DriveID string
|
||||||
Root string
|
Root string
|
||||||
Folders Elements
|
Folders Elements
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToOneDrivePath(p Path) (*DrivePath, error) {
|
func ToDrivePath(p Path) (*DrivePath, error) {
|
||||||
folders := p.Folders()
|
folders := p.Folders()
|
||||||
|
|
||||||
// Must be at least `drives/<driveID>/root:`
|
// Must be at least `drives/<driveID>/root:`
|
||||||
if len(folders) < 3 {
|
if len(folders) < 3 {
|
||||||
return nil, clues.
|
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))
|
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:`)
|
// Returns the path to the folder within the drive (i.e. under `root:`)
|
||||||
func GetDriveFolderPath(p Path) (string, error) {
|
func GetDriveFolderPath(p Path) (string, error) {
|
||||||
drivePath, err := ToOneDrivePath(p)
|
drivePath, err := ToDrivePath(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -54,7 +54,7 @@ func (suite *OneDrivePathSuite) Test_ToOneDrivePath() {
|
|||||||
p, err := path.Build("tenant", "user", path.OneDriveService, path.FilesCategory, false, tt.pathElements...)
|
p, err := path.Build("tenant", "user", path.OneDriveService, path.FilesCategory, false, tt.pathElements...)
|
||||||
require.NoError(suite.T(), err, clues.ToCore(err))
|
require.NoError(suite.T(), err, clues.ToCore(err))
|
||||||
|
|
||||||
got, err := path.ToOneDrivePath(p)
|
got, err := path.ToDrivePath(p)
|
||||||
tt.errCheck(t, err)
|
tt.errCheck(t, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
Loading…
x
Reference in New Issue
Block a user