Restore with OneDrive/SharePoint ID (#3151)

Allow restoring OneDrive and SharePoint items by
M365 ID as well as file name and ShortRef

Does create an edgecase where attempting to
restore an item named the same as another item's
M365 ID causes both items to be restored

Moves the consts for OneDrive file suffixes to a
different package so that the selectors package
can also import them without a cycle

---

#### Does this PR need a docs update or release note?

- [x]  Yes, it's included
- [ ] 🕐 Yes, but in a later PR
- [ ]  No

#### Type of change

- [x] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Supportability/Tests
- [ ] 💻 CI/Deployment
- [ ] 🧹 Tech Debt/Cleanup

#### Issue(s)

* closes #3142

#### Test Plan

- [x] 💪 Manual
- [ ]  Unit test
- [ ] 💚 E2E
This commit is contained in:
ashmrtn 2023-04-19 08:30:03 -07:00 committed by GitHub
parent d5eee4479d
commit 9664afaa12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 112 additions and 81 deletions

View File

@ -24,6 +24,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- ParentPath of json output for Exchange calendar now shows names instead of IDs. - ParentPath of json output for Exchange calendar now shows names instead of IDs.
- Fixed failure when downloading huge amount of attachments - Fixed failure when downloading huge amount of attachments
### Known Issues
- Restoring a OneDrive or SharePoint file with the same name as a file with that name as its M365 ID may restore both items.
## [v0.6.1] (beta) - 2023-03-21 ## [v0.6.1] (beta) - 2023-03-21
### Added ### Added

View File

@ -2,12 +2,14 @@ package testdata
import ( import (
"context" "context"
"strings"
"time" "time"
"github.com/alcionai/clues" "github.com/alcionai/clues"
"github.com/alcionai/corso/src/cli/utils" "github.com/alcionai/corso/src/cli/utils"
"github.com/alcionai/corso/src/internal/common" "github.com/alcionai/corso/src/internal/common"
"github.com/alcionai/corso/src/internal/connector/onedrive/metadata"
"github.com/alcionai/corso/src/pkg/backup" "github.com/alcionai/corso/src/pkg/backup"
"github.com/alcionai/corso/src/pkg/backup/details" "github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/backup/details/testdata" "github.com/alcionai/corso/src/pkg/backup/details/testdata"
@ -411,11 +413,13 @@ var (
}, },
}, },
{ {
Name: "NoSelectRepoItemName", Name: "SelectRepoItemName",
Expected: []details.DetailsEntry{}, Expected: []details.DetailsEntry{
testdata.OneDriveItems[0],
},
Opts: utils.OneDriveOpts{ Opts: utils.OneDriveOpts{
FileName: []string{ FileName: []string{
testdata.OneDriveItemPath1.Item(), strings.TrimSuffix(testdata.OneDriveItemPath1.Item(), metadata.DataFileSuffix),
}, },
}, },
}, },
@ -530,11 +534,13 @@ var (
}, },
}, },
{ {
Name: "NoSelectRepoItemName", Name: "SelectRepoItemName",
Expected: []details.DetailsEntry{}, Expected: []details.DetailsEntry{
testdata.SharePointLibraryItems[0],
},
Opts: utils.SharePointOpts{ Opts: utils.SharePointOpts{
FileName: []string{ FileName: []string{
testdata.SharePointLibraryItemPath1.Item(), strings.TrimSuffix(testdata.SharePointLibraryItemPath1.Item(), metadata.DataFileSuffix),
}, },
}, },
}, },

View File

@ -1,14 +1,14 @@
package metadata package metadata
import ( import (
"github.com/alcionai/corso/src/internal/connector/onedrive" "github.com/alcionai/corso/src/internal/connector/onedrive/metadata"
"github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/path"
) )
func IsMetadataFile(p path.Path) bool { func IsMetadataFile(p path.Path) bool {
switch p.Service() { switch p.Service() {
case path.OneDriveService: case path.OneDriveService:
return onedrive.IsMetaFile(p.Item()) return metadata.HasMetaSuffix(p.Item())
default: default:
return false return false

View File

@ -10,7 +10,7 @@ import (
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/connector/graph/metadata" "github.com/alcionai/corso/src/internal/connector/graph/metadata"
"github.com/alcionai/corso/src/internal/connector/onedrive" odmetadata "github.com/alcionai/corso/src/internal/connector/onedrive/metadata"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/path"
) )
@ -29,12 +29,12 @@ var (
notMetaSuffixes = []string{ notMetaSuffixes = []string{
"", "",
onedrive.DataFileSuffix, odmetadata.DataFileSuffix,
} }
metaSuffixes = []string{ metaSuffixes = []string{
onedrive.MetaFileSuffix, odmetadata.MetaFileSuffix,
onedrive.DirMetaFileSuffix, odmetadata.DirMetaFileSuffix,
} }
cases = []testCase{ cases = []testCase{

View File

@ -19,6 +19,7 @@ import (
"github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/common/ptr"
exchMock "github.com/alcionai/corso/src/internal/connector/exchange/mock" exchMock "github.com/alcionai/corso/src/internal/connector/exchange/mock"
"github.com/alcionai/corso/src/internal/connector/onedrive" "github.com/alcionai/corso/src/internal/connector/onedrive"
"github.com/alcionai/corso/src/internal/connector/onedrive/metadata"
"github.com/alcionai/corso/src/internal/connector/support" "github.com/alcionai/corso/src/internal/connector/support"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
@ -730,7 +731,7 @@ func compareOneDriveItem(
) bool { ) bool {
// Skip OneDrive permissions in the folder that used to be the root. We don't // Skip OneDrive 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() == onedrive.DirMetaFileSuffix { if rootDir && item.UUID() == metadata.DirMetaFileSuffix {
return false return false
} }
@ -742,8 +743,7 @@ func compareOneDriveItem(
var ( var (
displayName string displayName string
name = item.UUID() name = item.UUID()
isMeta = strings.HasSuffix(name, onedrive.MetaFileSuffix) || isMeta = metadata.HasMetaSuffix(name)
strings.HasSuffix(name, onedrive.DirMetaFileSuffix)
) )
if isMeta { if isMeta {
@ -780,7 +780,7 @@ func compareOneDriveItem(
key := name key := name
if strings.HasSuffix(name, onedrive.MetaFileSuffix) { if strings.HasSuffix(name, metadata.MetaFileSuffix) {
key = itemMeta.FileName key = itemMeta.FileName
} }
@ -851,7 +851,7 @@ func compareOneDriveItem(
// Display name in ItemInfo should match the name the file was given in the // Display name in ItemInfo should match the name the file was given in the
// test. Name used for the lookup key has a `.data` suffix to make it unique // test. Name used for the lookup key has a `.data` suffix to make it unique
// from the metadata files' lookup keys. // from the metadata files' lookup keys.
assert.Equal(t, fileData.FileName, displayName+onedrive.DataFileSuffix) assert.Equal(t, fileData.FileName, displayName+metadata.DataFileSuffix)
return true return true
} }
@ -1225,8 +1225,7 @@ func collectionsForInfo(
// We do not count metadata files against item count // We do not count metadata files against item count
if backupVersion > 0 && if backupVersion > 0 &&
(service == path.OneDriveService || service == path.SharePointService) && (service == path.OneDriveService || service == path.SharePointService) &&
(strings.HasSuffix(info.items[i].name, onedrive.MetaFileSuffix) || metadata.HasMetaSuffix(info.items[i].name) {
strings.HasSuffix(info.items[i].name, onedrive.DirMetaFileSuffix)) {
continue continue
} }

View File

@ -17,6 +17,7 @@ import (
"github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/internal/connector/onedrive" "github.com/alcionai/corso/src/internal/connector/onedrive"
"github.com/alcionai/corso/src/internal/connector/onedrive/metadata"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/internal/version" "github.com/alcionai/corso/src/internal/version"
"github.com/alcionai/corso/src/pkg/account" "github.com/alcionai/corso/src/pkg/account"
@ -165,43 +166,43 @@ func (c *onedriveCollection) withFile(name string, fileData []byte, perm permDat
c.items = append(c.items, onedriveItemWithData( c.items = append(c.items, onedriveItemWithData(
c.t, c.t,
name, name,
name+onedrive.DataFileSuffix, name+metadata.DataFileSuffix,
fileData)) fileData))
case version.OneDrive1DataAndMetaFiles, 2, version.OneDrive3IsMetaMarker, case version.OneDrive1DataAndMetaFiles, 2, version.OneDrive3IsMetaMarker,
version.OneDrive4DirIncludesPermissions, version.OneDrive5DirMetaNoName: version.OneDrive4DirIncludesPermissions, version.OneDrive5DirMetaNoName:
c.items = append(c.items, onedriveItemWithData( c.items = append(c.items, onedriveItemWithData(
c.t, c.t,
name+onedrive.DataFileSuffix, name+metadata.DataFileSuffix,
name+onedrive.DataFileSuffix, name+metadata.DataFileSuffix,
fileData)) fileData))
metadata := onedriveMetadata( md := onedriveMetadata(
c.t, c.t,
"", "",
name+onedrive.MetaFileSuffix, name+metadata.MetaFileSuffix,
name+onedrive.MetaFileSuffix, name+metadata.MetaFileSuffix,
perm, perm,
c.backupVersion >= versionPermissionSwitchedToID) c.backupVersion >= versionPermissionSwitchedToID)
c.items = append(c.items, metadata) c.items = append(c.items, md)
c.aux = append(c.aux, metadata) c.aux = append(c.aux, md)
case version.OneDrive6NameInMeta, version.OneDrive7LocationRef: case version.OneDrive6NameInMeta, version.OneDrive7LocationRef:
c.items = append(c.items, onedriveItemWithData( c.items = append(c.items, onedriveItemWithData(
c.t, c.t,
name+onedrive.DataFileSuffix, name+metadata.DataFileSuffix,
name+onedrive.DataFileSuffix, name+metadata.DataFileSuffix,
fileData)) fileData))
metadata := onedriveMetadata( md := onedriveMetadata(
c.t, c.t,
name, name,
name+onedrive.MetaFileSuffix, name+metadata.MetaFileSuffix,
name, name,
perm, perm,
c.backupVersion >= versionPermissionSwitchedToID) c.backupVersion >= versionPermissionSwitchedToID)
c.items = append(c.items, metadata) c.items = append(c.items, md)
c.aux = append(c.aux, metadata) c.aux = append(c.aux, md)
default: default:
assert.FailNowf(c.t, "bad backup version", "version %d", c.backupVersion) assert.FailNowf(c.t, "bad backup version", "version %d", c.backupVersion)
@ -222,8 +223,8 @@ func (c *onedriveCollection) withFolder(name string, perm permData) *onedriveCol
onedriveMetadata( onedriveMetadata(
c.t, c.t,
"", "",
name+onedrive.DirMetaFileSuffix, name+metadata.DirMetaFileSuffix,
name+onedrive.DirMetaFileSuffix, name+metadata.DirMetaFileSuffix,
perm, perm,
c.backupVersion >= versionPermissionSwitchedToID)) c.backupVersion >= versionPermissionSwitchedToID))
@ -255,16 +256,16 @@ func (c *onedriveCollection) withPermissions(perm permData) *onedriveCollection
return c return c
} }
metadata := onedriveMetadata( md := onedriveMetadata(
c.t, c.t,
name, name,
metaName+onedrive.DirMetaFileSuffix, metaName+metadata.DirMetaFileSuffix,
metaName+onedrive.DirMetaFileSuffix, metaName+metadata.DirMetaFileSuffix,
perm, perm,
c.backupVersion >= versionPermissionSwitchedToID) c.backupVersion >= versionPermissionSwitchedToID)
c.items = append(c.items, metadata) c.items = append(c.items, md)
c.aux = append(c.aux, metadata) c.aux = append(c.aux, md)
return c return c
} }

View File

@ -5,7 +5,6 @@ import (
"context" "context"
"io" "io"
"net/http" "net/http"
"strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@ -17,6 +16,7 @@ import (
"github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/internal/connector/onedrive/api" "github.com/alcionai/corso/src/internal/connector/onedrive/api"
"github.com/alcionai/corso/src/internal/connector/onedrive/metadata"
"github.com/alcionai/corso/src/internal/connector/support" "github.com/alcionai/corso/src/internal/connector/support"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/observe" "github.com/alcionai/corso/src/internal/observe"
@ -39,18 +39,10 @@ const (
// be retried // be retried
maxDownloadRetires = 3 maxDownloadRetires = 3
MetaFileSuffix = ".meta"
DirMetaFileSuffix = ".dirmeta"
DataFileSuffix = ".data"
// Used to compare in case of OneNote files // Used to compare in case of OneNote files
MaxOneNoteFileSize = 2 * 1024 * 1024 * 1024 MaxOneNoteFileSize = 2 * 1024 * 1024 * 1024
) )
func IsMetaFile(name string) bool {
return strings.HasSuffix(name, MetaFileSuffix) || strings.HasSuffix(name, DirMetaFileSuffix)
}
var ( var (
_ data.BackupCollection = &Collection{} _ data.BackupCollection = &Collection{}
_ data.Stream = &Item{} _ data.Stream = &Item{}
@ -524,12 +516,12 @@ func (oc *Collection) populateItems(ctx context.Context, errs *fault.Bus) {
atomic.AddInt64(&itemsFound, 1) atomic.AddInt64(&itemsFound, 1)
metaFileName = itemID metaFileName = itemID
metaSuffix = MetaFileSuffix metaSuffix = metadata.MetaFileSuffix
} else { } else {
atomic.AddInt64(&dirsFound, 1) atomic.AddInt64(&dirsFound, 1)
// metaFileName not set for directories so we get just ".dirmeta" // metaFileName not set for directories so we get just ".dirmeta"
metaSuffix = DirMetaFileSuffix metaSuffix = metadata.DirMetaFileSuffix
} }
// Fetch metadata for the file // Fetch metadata for the file
@ -556,7 +548,7 @@ func (oc *Collection) populateItems(ctx context.Context, errs *fault.Bus) {
ctx = clues.Add(ctx, "backup_item_info", itemInfo) ctx = clues.Add(ctx, "backup_item_info", itemInfo)
if isFile { if isFile {
dataSuffix := DataFileSuffix dataSuffix := metadata.DataFileSuffix
// Construct a new lazy readCloser to feed to the collection consumer. // Construct a new lazy readCloser to feed to the collection consumer.
// This ensures that downloads won't be attempted unless that consumer // This ensures that downloads won't be attempted unless that consumer

View File

@ -19,6 +19,7 @@ import (
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/internal/connector/onedrive/metadata"
"github.com/alcionai/corso/src/internal/connector/support" "github.com/alcionai/corso/src/internal/connector/support"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
@ -266,7 +267,7 @@ func (suite *CollectionUnitTestSuite) TestCollection() {
readItem := readItems[0] readItem := readItems[0]
readItemInfo := readItem.(data.StreamInfo) readItemInfo := readItem.(data.StreamInfo)
assert.Equal(t, testItemID+DataFileSuffix, readItem.UUID()) assert.Equal(t, testItemID+metadata.DataFileSuffix, readItem.UUID())
require.Implements(t, (*data.StreamModTime)(nil), readItem) require.Implements(t, (*data.StreamModTime)(nil), readItem)
mt := readItem.(data.StreamModTime) mt := readItem.(data.StreamModTime)
@ -292,7 +293,7 @@ func (suite *CollectionUnitTestSuite) TestCollection() {
if test.source == OneDriveSource { if test.source == OneDriveSource {
readItemMeta := readItems[1] readItemMeta := readItems[1]
assert.Equal(t, testItemID+MetaFileSuffix, readItemMeta.UUID()) assert.Equal(t, testItemID+metadata.MetaFileSuffix, readItemMeta.UUID())
readMetaData, err := io.ReadAll(readItemMeta.ToReader()) readMetaData, err := io.ReadAll(readItemMeta.ToReader())
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
@ -588,7 +589,7 @@ func (suite *CollectionUnitTestSuite) TestCollectionPermissionBackupLatestModTim
require.Equal(t, 1, collStatus.Metrics.Successes) require.Equal(t, 1, collStatus.Metrics.Successes)
for _, i := range readItems { for _, i := range readItems {
if strings.HasSuffix(i.UUID(), MetaFileSuffix) { if strings.HasSuffix(i.UUID(), metadata.MetaFileSuffix) {
content, err := io.ReadAll(i.ToReader()) content, err := io.ReadAll(i.ToReader())
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
require.Equal(t, content, []byte("{}")) require.Equal(t, content, []byte("{}"))

View File

@ -16,6 +16,7 @@ import (
"github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/internal/connector/onedrive/api" "github.com/alcionai/corso/src/internal/connector/onedrive/api"
"github.com/alcionai/corso/src/internal/connector/onedrive/metadata"
"github.com/alcionai/corso/src/internal/connector/support" "github.com/alcionai/corso/src/internal/connector/support"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/observe" "github.com/alcionai/corso/src/internal/observe"
@ -447,7 +448,7 @@ func (c *Collections) Get(
} }
service, category := c.source.toPathServiceCat() service, category := c.source.toPathServiceCat()
metadata, err := graph.MakeMetadataCollection( md, err := graph.MakeMetadataCollection(
c.tenant, c.tenant,
c.resourceOwner, c.resourceOwner,
service, service,
@ -464,7 +465,7 @@ func (c *Collections) Get(
// empty/missing and default to a full backup. // empty/missing and default to a full backup.
logger.CtxErr(ctx, err).Info("making metadata collection for future incremental backups") logger.CtxErr(ctx, err).Info("making metadata collection for future incremental backups")
} else { } else {
collections = append(collections, metadata) collections = append(collections, md)
} }
// TODO(ashmrtn): Track and return the set of items to exclude. // TODO(ashmrtn): Track and return the set of items to exclude.
@ -535,8 +536,8 @@ func (c *Collections) handleDelete(
return nil return nil
} }
excluded[itemID+DataFileSuffix] = struct{}{} excluded[itemID+metadata.DataFileSuffix] = struct{}{}
excluded[itemID+MetaFileSuffix] = struct{}{} excluded[itemID+metadata.MetaFileSuffix] = struct{}{}
// Exchange counts items streamed through it which includes deletions so // Exchange counts items streamed through it which includes deletions so
// add that here too. // add that here too.
c.NumFiles++ c.NumFiles++
@ -853,8 +854,8 @@ func (c *Collections) UpdateCollections(
// Always add a file to the excluded list. The file may have been // Always add a file to the excluded list. The file may have been
// renamed/moved/modified, so we still have to drop the // renamed/moved/modified, so we still have to drop the
// original one and download a fresh copy. // original one and download a fresh copy.
excluded[itemID+DataFileSuffix] = struct{}{} excluded[itemID+metadata.DataFileSuffix] = struct{}{}
excluded[itemID+MetaFileSuffix] = struct{}{} excluded[itemID+metadata.MetaFileSuffix] = struct{}{}
} }
default: default:

View File

@ -19,6 +19,7 @@ import (
gapi "github.com/alcionai/corso/src/internal/connector/graph/api" gapi "github.com/alcionai/corso/src/internal/connector/graph/api"
"github.com/alcionai/corso/src/internal/connector/onedrive/api" "github.com/alcionai/corso/src/internal/connector/onedrive/api"
"github.com/alcionai/corso/src/internal/connector/onedrive/api/mock" "github.com/alcionai/corso/src/internal/connector/onedrive/api/mock"
"github.com/alcionai/corso/src/internal/connector/onedrive/metadata"
"github.com/alcionai/corso/src/internal/connector/support" "github.com/alcionai/corso/src/internal/connector/support"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
@ -147,8 +148,8 @@ func (suite *OneDriveCollectionsUnitSuite) TestGetCanonicalPath() {
func getDelList(files ...string) map[string]struct{} { func getDelList(files ...string) map[string]struct{} {
delList := map[string]struct{}{} delList := map[string]struct{}{}
for _, file := range files { for _, file := range files {
delList[file+DataFileSuffix] = struct{}{} delList[file+metadata.DataFileSuffix] = struct{}{}
delList[file+MetaFileSuffix] = struct{}{} delList[file+metadata.MetaFileSuffix] = struct{}{}
} }
return delList return delList

View File

@ -0,0 +1,13 @@
package metadata
import "strings"
const (
MetaFileSuffix = ".meta"
DirMetaFileSuffix = ".dirmeta"
DataFileSuffix = ".data"
)
func HasMetaSuffix(name string) bool {
return strings.HasSuffix(name, MetaFileSuffix) || strings.HasSuffix(name, DirMetaFileSuffix)
}

View File

@ -9,6 +9,7 @@ import (
"github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/internal/connector/onedrive/metadata"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/version" "github.com/alcionai/corso/src/internal/version"
"github.com/alcionai/corso/src/pkg/account" "github.com/alcionai/corso/src/pkg/account"
@ -69,10 +70,10 @@ func getCollectionMetadata(
// Root folder doesn't have a metadata file associated with it. // Root folder doesn't have a metadata file associated with it.
folders := collectionPath.Folders() folders := collectionPath.Folders()
metaName := folders[len(folders)-1] + DirMetaFileSuffix metaName := folders[len(folders)-1] + metadata.DirMetaFileSuffix
if backupVersion >= version.OneDrive5DirMetaNoName { if backupVersion >= version.OneDrive5DirMetaNoName {
metaName = DirMetaFileSuffix metaName = metadata.DirMetaFileSuffix
} }
meta, err := fetchAndReadMetadata(ctx, dc, metaName) meta, err := fetchAndReadMetadata(ctx, dc, metaName)

View File

@ -13,6 +13,7 @@ import (
"github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/internal/connector/onedrive/metadata"
"github.com/alcionai/corso/src/internal/connector/support" "github.com/alcionai/corso/src/internal/connector/support"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/diagnostics" "github.com/alcionai/corso/src/internal/diagnostics"
@ -304,14 +305,14 @@ func restoreItem(
// only v1+ backups from this point on // only v1+ backups from this point on
if strings.HasSuffix(itemUUID, MetaFileSuffix) { if strings.HasSuffix(itemUUID, metadata.MetaFileSuffix) {
// Just skip this for the moment since we moved the code to the above // Just skip this for the moment since we moved the code to the above
// item restore path. We haven't yet stopped fetching these items in // item restore path. We haven't yet stopped fetching these items in
// RestoreOp, so we still need to handle them in some way. // RestoreOp, so we still need to handle them in some way.
return details.ItemInfo{}, true, nil return details.ItemInfo{}, true, nil
} }
if strings.HasSuffix(itemUUID, DirMetaFileSuffix) { if strings.HasSuffix(itemUUID, metadata.DirMetaFileSuffix) {
// Only the version.OneDrive1DataAndMetaFiles needed to deserialize the // Only the version.OneDrive1DataAndMetaFiles needed to deserialize the
// permission for child folders here. Later versions can request // permission for child folders here. Later versions can request
// permissions inline when processing the collection. // permissions inline when processing the collection.
@ -327,7 +328,7 @@ func restoreItem(
return details.ItemInfo{}, true, clues.Wrap(err, "getting directory metadata").WithClues(ctx) return details.ItemInfo{}, true, clues.Wrap(err, "getting directory metadata").WithClues(ctx)
} }
trimmedPath := strings.TrimSuffix(itemPath.String(), DirMetaFileSuffix) trimmedPath := strings.TrimSuffix(itemPath.String(), metadata.DirMetaFileSuffix)
folderMetas[trimmedPath] = meta folderMetas[trimmedPath] = meta
return details.ItemInfo{}, true, nil return details.ItemInfo{}, true, nil
@ -424,7 +425,7 @@ func restoreV1File(
itemPath path.Path, itemPath path.Path,
itemData data.Stream, itemData data.Stream,
) (details.ItemInfo, error) { ) (details.ItemInfo, error) {
trimmedName := strings.TrimSuffix(itemData.UUID(), DataFileSuffix) trimmedName := strings.TrimSuffix(itemData.UUID(), metadata.DataFileSuffix)
itemID, itemInfo, err := restoreData( itemID, itemInfo, err := restoreData(
ctx, ctx,
@ -446,7 +447,7 @@ func restoreV1File(
} }
// Fetch item permissions from the collection and restore them. // Fetch item permissions from the collection and restore them.
metaName := trimmedName + MetaFileSuffix metaName := trimmedName + metadata.MetaFileSuffix
meta, err := fetchAndReadMetadata(ctx, fetcher, metaName) meta, err := fetchAndReadMetadata(ctx, fetcher, metaName)
if err != nil { if err != nil {
@ -485,10 +486,10 @@ func restoreV6File(
itemPath path.Path, itemPath path.Path,
itemData data.Stream, itemData data.Stream,
) (details.ItemInfo, error) { ) (details.ItemInfo, error) {
trimmedName := strings.TrimSuffix(itemData.UUID(), DataFileSuffix) trimmedName := strings.TrimSuffix(itemData.UUID(), metadata.DataFileSuffix)
// Get metadata file so we can determine the file name. // Get metadata file so we can determine the file name.
metaName := trimmedName + MetaFileSuffix metaName := trimmedName + metadata.MetaFileSuffix
meta, err := fetchAndReadMetadata(ctx, fetcher, metaName) meta, err := fetchAndReadMetadata(ctx, fetcher, metaName)
if err != nil { if err != nil {

View File

@ -18,7 +18,7 @@ import (
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
exchMock "github.com/alcionai/corso/src/internal/connector/exchange/mock" exchMock "github.com/alcionai/corso/src/internal/connector/exchange/mock"
"github.com/alcionai/corso/src/internal/connector/onedrive" "github.com/alcionai/corso/src/internal/connector/onedrive/metadata"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/fault"
@ -390,8 +390,8 @@ func (suite *KopiaIntegrationSuite) TestBackupCollections_NoDetailsForMeta() {
locPath, locPath,
3) 3)
mc.Names[0] = testFileName mc.Names[0] = testFileName
mc.Names[1] = testFileName + onedrive.MetaFileSuffix mc.Names[1] = testFileName + metadata.MetaFileSuffix
mc.Names[2] = storePath.Folders()[0] + onedrive.DirMetaFileSuffix mc.Names[2] = storePath.Folders()[0] + metadata.DirMetaFileSuffix
return []data.BackupCollection{mc} return []data.BackupCollection{mc}
}, },
@ -456,7 +456,7 @@ func (suite *KopiaIntegrationSuite) TestBackupCollections_NoDetailsForMeta() {
continue continue
} }
assert.False(t, onedrive.IsMetaFile(entry.RepoRef), "metadata entry in details") assert.False(t, metadata.HasMetaSuffix(entry.RepoRef), "metadata entry in details")
} }
// Shouldn't have any items to merge because the cached files are metadata // Shouldn't have any items to merge because the cached files are metadata

View File

@ -11,7 +11,7 @@ import (
"github.com/alcionai/corso/src/internal/common" "github.com/alcionai/corso/src/internal/common"
"github.com/alcionai/corso/src/internal/common/crash" "github.com/alcionai/corso/src/internal/common/crash"
"github.com/alcionai/corso/src/internal/connector" "github.com/alcionai/corso/src/internal/connector"
"github.com/alcionai/corso/src/internal/connector/onedrive" "github.com/alcionai/corso/src/internal/connector/onedrive/metadata"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/events" "github.com/alcionai/corso/src/internal/events"
"github.com/alcionai/corso/src/internal/kopia" "github.com/alcionai/corso/src/internal/kopia"
@ -471,7 +471,7 @@ func getBackupDetails(
if b.Version >= version.OneDrive1DataAndMetaFiles && b.Version < version.OneDrive3IsMetaMarker { if b.Version >= version.OneDrive1DataAndMetaFiles && b.Version < version.OneDrive3IsMetaMarker {
for _, d := range deets.Entries { for _, d := range deets.Entries {
if d.OneDrive != nil { if d.OneDrive != nil {
d.OneDrive.IsMeta = onedrive.IsMetaFile(d.RepoRef) d.OneDrive.IsMeta = metadata.HasMetaSuffix(d.RepoRef)
} }
} }
} }

View File

@ -3,10 +3,12 @@ package selectors
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"github.com/alcionai/clues" "github.com/alcionai/clues"
"github.com/alcionai/corso/src/internal/common" "github.com/alcionai/corso/src/internal/common"
"github.com/alcionai/corso/src/internal/connector/onedrive/metadata"
"github.com/alcionai/corso/src/pkg/backup/details" "github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/filters" "github.com/alcionai/corso/src/pkg/filters"
@ -400,9 +402,11 @@ func (c oneDriveCategory) pathValues(
// Ignore `drives/<driveID>/root:` for folder comparison // Ignore `drives/<driveID>/root:` for folder comparison
rFld := path.Builder{}.Append(repo.Folders()...).PopFront().PopFront().PopFront().String() rFld := path.Builder{}.Append(repo.Folders()...).PopFront().PopFront().PopFront().String()
itemID := strings.TrimSuffix(repo.Item(), metadata.DataFileSuffix)
result := map[categorizer][]string{ result := map[categorizer][]string{
OneDriveFolder: {rFld}, OneDriveFolder: {rFld},
OneDriveItem: {ent.OneDrive.ItemName, ent.ShortRef}, OneDriveItem: {ent.OneDrive.ItemName, ent.ShortRef, itemID},
} }
if len(ent.LocationRef) > 0 { if len(ent.LocationRef) > 0 {

View File

@ -270,7 +270,7 @@ func (suite *OneDriveSelectorSuite) TestOneDriveCategory_PathValues() {
expected := map[categorizer][]string{ expected := map[categorizer][]string{
OneDriveFolder: {"dir1/dir2"}, OneDriveFolder: {"dir1/dir2"},
OneDriveItem: {fileName, shortRef}, OneDriveItem: {fileName, shortRef, fileName + "-id"},
} }
ent := details.DetailsEntry{ ent := details.DetailsEntry{

View File

@ -3,10 +3,12 @@ package selectors
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"github.com/alcionai/clues" "github.com/alcionai/clues"
"github.com/alcionai/corso/src/internal/common" "github.com/alcionai/corso/src/internal/common"
"github.com/alcionai/corso/src/internal/connector/onedrive/metadata"
"github.com/alcionai/corso/src/pkg/backup/details" "github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/filters" "github.com/alcionai/corso/src/pkg/filters"
@ -522,6 +524,7 @@ func (c sharePointCategory) pathValues(
folderCat, itemCat categorizer folderCat, itemCat categorizer
itemName = repo.Item() itemName = repo.Item()
dropDriveFolderPrefix bool dropDriveFolderPrefix bool
itemID string
) )
switch c { switch c {
@ -532,6 +535,7 @@ func (c sharePointCategory) pathValues(
dropDriveFolderPrefix = true dropDriveFolderPrefix = true
folderCat, itemCat = SharePointLibraryFolder, SharePointLibraryItem folderCat, itemCat = SharePointLibraryFolder, SharePointLibraryItem
itemID = strings.TrimSuffix(itemName, metadata.DataFileSuffix)
itemName = ent.SharePoint.ItemName itemName = ent.SharePoint.ItemName
case SharePointList, SharePointListItem: case SharePointList, SharePointListItem:
@ -555,6 +559,10 @@ func (c sharePointCategory) pathValues(
itemCat: {itemName, ent.ShortRef}, itemCat: {itemName, ent.ShortRef},
} }
if len(itemID) > 0 {
result[itemCat] = append(result[itemCat], itemID)
}
if len(ent.LocationRef) > 0 { if len(ent.LocationRef) > 0 {
result[folderCat] = append(result[folderCat], ent.LocationRef) result[folderCat] = append(result[folderCat], ent.LocationRef)
} }

View File

@ -353,7 +353,7 @@ func (suite *SharePointSelectorSuite) TestSharePointCategory_PathValues() {
pathElems: driveElems, pathElems: driveElems,
expected: map[categorizer][]string{ expected: map[categorizer][]string{
SharePointLibraryFolder: {"dir1/dir2"}, SharePointLibraryFolder: {"dir1/dir2"},
SharePointLibraryItem: {itemName, shortRef}, SharePointLibraryItem: {itemName, shortRef, itemName + "-id"},
}, },
}, },
{ {