Add restore path generation code (#3362)
In preparation for switching to folder IDs, add logic to generate the restore path based on prefix information from the RepoRef and LocationRef of items Contains fallback code (and tests) to handle older details versions that may not have had LocationRef Manually tested restore from old backup that didn't have any LocationRef information Manually tested restore checking that calendar names are shown instead of IDs in progress bar --- #### 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 - [x] 🌻 Feature - [ ] 🐛 Bugfix - [ ] 🗺️ Documentation - [ ] 🤖 Supportability/Tests - [ ] 💻 CI/Deployment - [ ] 🧹 Tech Debt/Cleanup #### Issue(s) * #3197 * fixes #3218 #### Test Plan - [x] 💪 Manual - [x] ⚡ Unit test - [ ] 💚 E2E
This commit is contained in:
parent
c5b388a721
commit
2e4fc71310
@ -0,0 +1,180 @@
|
|||||||
|
package pathtransformer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/alcionai/clues"
|
||||||
|
|
||||||
|
"github.com/alcionai/corso/src/internal/version"
|
||||||
|
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||||
|
"github.com/alcionai/corso/src/pkg/fault"
|
||||||
|
"github.com/alcionai/corso/src/pkg/logger"
|
||||||
|
"github.com/alcionai/corso/src/pkg/path"
|
||||||
|
)
|
||||||
|
|
||||||
|
func locationRef(
|
||||||
|
ent *details.Entry,
|
||||||
|
repoRef path.Path,
|
||||||
|
backupVersion int,
|
||||||
|
) (*path.Builder, error) {
|
||||||
|
loc := ent.LocationRef
|
||||||
|
|
||||||
|
// At this backup version all data types should populate LocationRef.
|
||||||
|
if len(loc) > 0 || backupVersion >= version.OneDrive7LocationRef {
|
||||||
|
return path.Builder{}.SplitUnescapeAppend(loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We could get an empty LocationRef either because it wasn't populated or it
|
||||||
|
// was in the root of the data type.
|
||||||
|
elems := repoRef.Folders()
|
||||||
|
|
||||||
|
if ent.OneDrive != nil || ent.SharePoint != nil {
|
||||||
|
dp, err := path.ToDrivePath(repoRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, clues.Wrap(err, "fallback for LocationRef")
|
||||||
|
}
|
||||||
|
|
||||||
|
elems = append([]string{dp.Root}, dp.Folders...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.Builder{}.Append(elems...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func basicLocationPath(repoRef path.Path, locRef *path.Builder) (path.Path, error) {
|
||||||
|
if len(locRef.Elements()) == 0 {
|
||||||
|
res, err := path.ServicePrefix(
|
||||||
|
repoRef.Tenant(),
|
||||||
|
repoRef.ResourceOwner(),
|
||||||
|
repoRef.Service(),
|
||||||
|
repoRef.Category())
|
||||||
|
if err != nil {
|
||||||
|
return nil, clues.Wrap(err, "getting prefix for empty location")
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return locRef.ToDataLayerPath(
|
||||||
|
repoRef.Tenant(),
|
||||||
|
repoRef.ResourceOwner(),
|
||||||
|
repoRef.Service(),
|
||||||
|
repoRef.Category(),
|
||||||
|
false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func drivePathMerge(
|
||||||
|
ent *details.Entry,
|
||||||
|
repoRef path.Path,
|
||||||
|
locRef *path.Builder,
|
||||||
|
) (path.Path, error) {
|
||||||
|
// Try getting the drive ID from the item. Not all details versions had it
|
||||||
|
// though.
|
||||||
|
var driveID string
|
||||||
|
|
||||||
|
if ent.SharePoint != nil {
|
||||||
|
driveID = ent.SharePoint.DriveID
|
||||||
|
} else if ent.OneDrive != nil {
|
||||||
|
driveID = ent.OneDrive.DriveID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to trying to get from RepoRef.
|
||||||
|
if len(driveID) == 0 {
|
||||||
|
odp, err := path.ToDrivePath(repoRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, clues.Wrap(err, "fallback getting DriveID")
|
||||||
|
}
|
||||||
|
|
||||||
|
driveID = odp.DriveID
|
||||||
|
}
|
||||||
|
|
||||||
|
return basicLocationPath(
|
||||||
|
repoRef,
|
||||||
|
path.BuildDriveLocation(driveID, locRef.Elements()...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeRestorePathsForEntry(
|
||||||
|
ctx context.Context,
|
||||||
|
backupVersion int,
|
||||||
|
ent *details.Entry,
|
||||||
|
) (path.RestorePaths, error) {
|
||||||
|
res := path.RestorePaths{}
|
||||||
|
|
||||||
|
repoRef, err := path.FromDataLayerPath(ent.RepoRef, true)
|
||||||
|
if err != nil {
|
||||||
|
err = clues.Wrap(err, "parsing RepoRef").
|
||||||
|
WithClues(ctx).
|
||||||
|
With("repo_ref", clues.Hide(ent.RepoRef), "location_ref", clues.Hide(ent.LocationRef))
|
||||||
|
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res.StoragePath = repoRef
|
||||||
|
ctx = clues.Add(ctx, "repo_ref", repoRef)
|
||||||
|
|
||||||
|
// Get the LocationRef so we can munge it onto our path.
|
||||||
|
locRef, err := locationRef(ent, repoRef, backupVersion)
|
||||||
|
if err != nil {
|
||||||
|
err = clues.Wrap(err, "parsing LocationRef after reduction").
|
||||||
|
WithClues(ctx).
|
||||||
|
With("location_ref", clues.Hide(ent.LocationRef))
|
||||||
|
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = clues.Add(ctx, "location_ref", locRef)
|
||||||
|
|
||||||
|
// Now figure out what type of ent it is and munge the path accordingly.
|
||||||
|
// Eventually we're going to need munging for:
|
||||||
|
// * Exchange Calendars (different folder handling)
|
||||||
|
// * Exchange Email/Contacts
|
||||||
|
// * OneDrive/SharePoint (needs drive information)
|
||||||
|
if ent.Exchange != nil {
|
||||||
|
// TODO(ashmrtn): Eventually make Events have it's own function to handle
|
||||||
|
// setting the restore destination properly.
|
||||||
|
res.RestorePath, err = basicLocationPath(repoRef, locRef)
|
||||||
|
} else if ent.OneDrive != nil ||
|
||||||
|
(ent.SharePoint != nil && ent.SharePoint.ItemType == details.SharePointLibrary) ||
|
||||||
|
(ent.SharePoint != nil && ent.SharePoint.ItemType == details.OneDriveItem) {
|
||||||
|
res.RestorePath, err = drivePathMerge(ent, repoRef, locRef)
|
||||||
|
} else {
|
||||||
|
return res, clues.New("unknown entry type").WithClues(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return res, clues.Wrap(err, "generating RestorePath").WithClues(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPaths takes a set of filtered details entries and returns a set of
|
||||||
|
// RestorePaths for the entries.
|
||||||
|
func GetPaths(
|
||||||
|
ctx context.Context,
|
||||||
|
backupVersion int,
|
||||||
|
items []*details.Entry,
|
||||||
|
errs *fault.Bus,
|
||||||
|
) ([]path.RestorePaths, error) {
|
||||||
|
var (
|
||||||
|
paths = make([]path.RestorePaths, len(items))
|
||||||
|
el = errs.Local()
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, ent := range items {
|
||||||
|
if el.Failure() != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
restorePaths, err := makeRestorePathsForEntry(ctx, backupVersion, ent)
|
||||||
|
if err != nil {
|
||||||
|
el.AddRecoverable(clues.Wrap(err, "getting restore paths"))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
paths[i] = restorePaths
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Ctx(ctx).Infof("found %d details entries to restore", len(paths))
|
||||||
|
|
||||||
|
return paths, el.Failure()
|
||||||
|
}
|
||||||
@ -0,0 +1,340 @@
|
|||||||
|
package pathtransformer_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/alcionai/clues"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
|
"github.com/alcionai/corso/src/internal/operations/pathtransformer"
|
||||||
|
"github.com/alcionai/corso/src/internal/tester"
|
||||||
|
"github.com/alcionai/corso/src/internal/version"
|
||||||
|
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||||
|
"github.com/alcionai/corso/src/pkg/backup/details/testdata"
|
||||||
|
"github.com/alcionai/corso/src/pkg/fault"
|
||||||
|
"github.com/alcionai/corso/src/pkg/path"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RestorePathTransformerUnitSuite struct {
|
||||||
|
tester.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRestorePathTransformerUnitSuite(t *testing.T) {
|
||||||
|
suite.Run(t, &RestorePathTransformerUnitSuite{Suite: tester.NewUnitSuite(t)})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *RestorePathTransformerUnitSuite) TestGetPaths() {
|
||||||
|
type expectPaths struct {
|
||||||
|
storage string
|
||||||
|
restore string
|
||||||
|
isRestorePrefix bool
|
||||||
|
}
|
||||||
|
|
||||||
|
toRestore := func(
|
||||||
|
repoRef path.Path,
|
||||||
|
unescapedFolders ...string,
|
||||||
|
) string {
|
||||||
|
return path.Builder{}.
|
||||||
|
Append(
|
||||||
|
repoRef.Tenant(),
|
||||||
|
repoRef.Service().String(),
|
||||||
|
repoRef.ResourceOwner(),
|
||||||
|
repoRef.Category().String()).
|
||||||
|
Append(unescapedFolders...).
|
||||||
|
String()
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
driveID = "some-drive-id"
|
||||||
|
extraItemName = "some-item"
|
||||||
|
SharePointRootItemPath = testdata.SharePointRootPath.MustAppend(extraItemName, true)
|
||||||
|
)
|
||||||
|
|
||||||
|
table := []struct {
|
||||||
|
name string
|
||||||
|
backupVersion int
|
||||||
|
input []*details.Entry
|
||||||
|
expectErr assert.ErrorAssertionFunc
|
||||||
|
expected []expectPaths
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "SharePoint List Errors",
|
||||||
|
// No version bump for the change so we always have to check for this.
|
||||||
|
backupVersion: version.All8MigrateUserPNToID,
|
||||||
|
input: []*details.Entry{
|
||||||
|
{
|
||||||
|
RepoRef: SharePointRootItemPath.RR.String(),
|
||||||
|
LocationRef: SharePointRootItemPath.Loc.String(),
|
||||||
|
ItemInfo: details.ItemInfo{
|
||||||
|
SharePoint: &details.SharePointInfo{
|
||||||
|
ItemType: details.SharePointList,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: assert.Error,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SharePoint Page Errors",
|
||||||
|
// No version bump for the change so we always have to check for this.
|
||||||
|
backupVersion: version.All8MigrateUserPNToID,
|
||||||
|
input: []*details.Entry{
|
||||||
|
{
|
||||||
|
RepoRef: SharePointRootItemPath.RR.String(),
|
||||||
|
LocationRef: SharePointRootItemPath.Loc.String(),
|
||||||
|
ItemInfo: details.ItemInfo{
|
||||||
|
SharePoint: &details.SharePointInfo{
|
||||||
|
ItemType: details.SharePointPage,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: assert.Error,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SharePoint old format, item in root",
|
||||||
|
// No version bump for the change so we always have to check for this.
|
||||||
|
backupVersion: version.All8MigrateUserPNToID,
|
||||||
|
input: []*details.Entry{
|
||||||
|
{
|
||||||
|
RepoRef: SharePointRootItemPath.RR.String(),
|
||||||
|
LocationRef: SharePointRootItemPath.Loc.String(),
|
||||||
|
ItemInfo: details.ItemInfo{
|
||||||
|
SharePoint: &details.SharePointInfo{
|
||||||
|
ItemType: details.OneDriveItem,
|
||||||
|
DriveID: driveID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: assert.NoError,
|
||||||
|
expected: []expectPaths{
|
||||||
|
{
|
||||||
|
storage: SharePointRootItemPath.RR.String(),
|
||||||
|
restore: toRestore(
|
||||||
|
SharePointRootItemPath.RR,
|
||||||
|
append(
|
||||||
|
[]string{"drives", driveID},
|
||||||
|
SharePointRootItemPath.Loc.Elements()...)...),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SharePoint, no LocationRef, no DriveID, item in root",
|
||||||
|
backupVersion: version.OneDrive6NameInMeta,
|
||||||
|
input: []*details.Entry{
|
||||||
|
{
|
||||||
|
RepoRef: SharePointRootItemPath.RR.String(),
|
||||||
|
ItemInfo: details.ItemInfo{
|
||||||
|
SharePoint: &details.SharePointInfo{
|
||||||
|
ItemType: details.SharePointLibrary,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: assert.NoError,
|
||||||
|
expected: []expectPaths{
|
||||||
|
{
|
||||||
|
storage: SharePointRootItemPath.RR.String(),
|
||||||
|
restore: toRestore(
|
||||||
|
SharePointRootItemPath.RR,
|
||||||
|
append(
|
||||||
|
[]string{"drives"},
|
||||||
|
// testdata path has '.d' on the drives folder we need to remove.
|
||||||
|
SharePointRootItemPath.RR.Folders()[1:]...)...),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OneDrive, nested item",
|
||||||
|
backupVersion: version.All8MigrateUserPNToID,
|
||||||
|
input: []*details.Entry{
|
||||||
|
{
|
||||||
|
RepoRef: testdata.OneDriveItemPath2.RR.String(),
|
||||||
|
LocationRef: testdata.OneDriveItemPath2.Loc.String(),
|
||||||
|
ItemInfo: details.ItemInfo{
|
||||||
|
OneDrive: &details.OneDriveInfo{
|
||||||
|
ItemType: details.OneDriveItem,
|
||||||
|
DriveID: driveID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: assert.NoError,
|
||||||
|
expected: []expectPaths{
|
||||||
|
{
|
||||||
|
storage: testdata.OneDriveItemPath2.RR.String(),
|
||||||
|
restore: toRestore(
|
||||||
|
testdata.OneDriveItemPath2.RR,
|
||||||
|
append(
|
||||||
|
[]string{"drives", driveID},
|
||||||
|
testdata.OneDriveItemPath2.Loc.Elements()...)...),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Exchange Email, extra / in path",
|
||||||
|
backupVersion: version.All8MigrateUserPNToID,
|
||||||
|
input: []*details.Entry{
|
||||||
|
{
|
||||||
|
RepoRef: testdata.ExchangeEmailItemPath3.RR.String(),
|
||||||
|
LocationRef: testdata.ExchangeEmailItemPath3.Loc.String(),
|
||||||
|
ItemInfo: details.ItemInfo{
|
||||||
|
Exchange: &details.ExchangeInfo{
|
||||||
|
ItemType: details.ExchangeMail,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: assert.NoError,
|
||||||
|
expected: []expectPaths{
|
||||||
|
{
|
||||||
|
storage: testdata.ExchangeEmailItemPath3.RR.String(),
|
||||||
|
restore: toRestore(
|
||||||
|
testdata.ExchangeEmailItemPath3.RR,
|
||||||
|
testdata.ExchangeEmailItemPath3.Loc.Elements()...),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Exchange Email, no LocationRef, extra / in path",
|
||||||
|
backupVersion: version.OneDrive7LocationRef,
|
||||||
|
input: []*details.Entry{
|
||||||
|
{
|
||||||
|
RepoRef: testdata.ExchangeEmailItemPath3.RR.String(),
|
||||||
|
LocationRef: testdata.ExchangeEmailItemPath3.Loc.String(),
|
||||||
|
ItemInfo: details.ItemInfo{
|
||||||
|
Exchange: &details.ExchangeInfo{
|
||||||
|
ItemType: details.ExchangeMail,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: assert.NoError,
|
||||||
|
expected: []expectPaths{
|
||||||
|
{
|
||||||
|
storage: testdata.ExchangeEmailItemPath3.RR.String(),
|
||||||
|
restore: toRestore(
|
||||||
|
testdata.ExchangeEmailItemPath3.RR,
|
||||||
|
testdata.ExchangeEmailItemPath3.Loc.Elements()...),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Exchange Contact",
|
||||||
|
backupVersion: version.All8MigrateUserPNToID,
|
||||||
|
input: []*details.Entry{
|
||||||
|
{
|
||||||
|
RepoRef: testdata.ExchangeContactsItemPath1.RR.String(),
|
||||||
|
LocationRef: testdata.ExchangeContactsItemPath1.Loc.String(),
|
||||||
|
ItemInfo: details.ItemInfo{
|
||||||
|
Exchange: &details.ExchangeInfo{
|
||||||
|
ItemType: details.ExchangeContact,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: assert.NoError,
|
||||||
|
expected: []expectPaths{
|
||||||
|
{
|
||||||
|
storage: testdata.ExchangeContactsItemPath1.RR.String(),
|
||||||
|
restore: toRestore(
|
||||||
|
testdata.ExchangeContactsItemPath1.RR,
|
||||||
|
testdata.ExchangeContactsItemPath1.Loc.Elements()...),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Exchange Contact, root dir",
|
||||||
|
backupVersion: version.All8MigrateUserPNToID,
|
||||||
|
input: []*details.Entry{
|
||||||
|
{
|
||||||
|
RepoRef: testdata.ExchangeContactsItemPath1.RR.String(),
|
||||||
|
ItemInfo: details.ItemInfo{
|
||||||
|
Exchange: &details.ExchangeInfo{
|
||||||
|
ItemType: details.ExchangeContact,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: assert.NoError,
|
||||||
|
expected: []expectPaths{
|
||||||
|
{
|
||||||
|
storage: testdata.ExchangeContactsItemPath1.RR.String(),
|
||||||
|
restore: toRestore(testdata.ExchangeContactsItemPath1.RR, "tmp"),
|
||||||
|
isRestorePrefix: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Exchange Event",
|
||||||
|
backupVersion: version.All8MigrateUserPNToID,
|
||||||
|
input: []*details.Entry{
|
||||||
|
{
|
||||||
|
RepoRef: testdata.ExchangeEmailItemPath3.RR.String(),
|
||||||
|
LocationRef: testdata.ExchangeEmailItemPath3.Loc.String(),
|
||||||
|
ItemInfo: details.ItemInfo{
|
||||||
|
Exchange: &details.ExchangeInfo{
|
||||||
|
ItemType: details.ExchangeMail,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: assert.NoError,
|
||||||
|
expected: []expectPaths{
|
||||||
|
{
|
||||||
|
storage: testdata.ExchangeEmailItemPath3.RR.String(),
|
||||||
|
restore: toRestore(
|
||||||
|
testdata.ExchangeEmailItemPath3.RR,
|
||||||
|
testdata.ExchangeEmailItemPath3.Loc.Elements()...),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range table {
|
||||||
|
suite.Run(test.name, func() {
|
||||||
|
ctx, flush := tester.NewContext()
|
||||||
|
defer flush()
|
||||||
|
|
||||||
|
t := suite.T()
|
||||||
|
|
||||||
|
paths, err := pathtransformer.GetPaths(
|
||||||
|
ctx,
|
||||||
|
test.backupVersion,
|
||||||
|
test.input,
|
||||||
|
fault.New(true))
|
||||||
|
test.expectErr(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := make([]path.RestorePaths, 0, len(test.expected))
|
||||||
|
|
||||||
|
for _, e := range test.expected {
|
||||||
|
tmp := path.RestorePaths{}
|
||||||
|
p, err := path.FromDataLayerPath(e.storage, true)
|
||||||
|
require.NoError(t, err, "parsing expected storage path", clues.ToCore(err))
|
||||||
|
|
||||||
|
tmp.StoragePath = p
|
||||||
|
|
||||||
|
p, err = path.FromDataLayerPath(e.restore, false)
|
||||||
|
require.NoError(t, err, "parsing expected restore path", clues.ToCore(err))
|
||||||
|
|
||||||
|
if e.isRestorePrefix {
|
||||||
|
p, err = p.Dir()
|
||||||
|
require.NoError(t, err, "getting service prefix", clues.ToCore(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp.RestorePath = p
|
||||||
|
|
||||||
|
expected = append(expected, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.ElementsMatch(t, expected, paths)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/alcionai/corso/src/internal/model"
|
"github.com/alcionai/corso/src/internal/model"
|
||||||
"github.com/alcionai/corso/src/internal/observe"
|
"github.com/alcionai/corso/src/internal/observe"
|
||||||
"github.com/alcionai/corso/src/internal/operations/inject"
|
"github.com/alcionai/corso/src/internal/operations/inject"
|
||||||
|
"github.com/alcionai/corso/src/internal/operations/pathtransformer"
|
||||||
"github.com/alcionai/corso/src/internal/stats"
|
"github.com/alcionai/corso/src/internal/stats"
|
||||||
"github.com/alcionai/corso/src/internal/streamstore"
|
"github.com/alcionai/corso/src/internal/streamstore"
|
||||||
"github.com/alcionai/corso/src/pkg/account"
|
"github.com/alcionai/corso/src/pkg/account"
|
||||||
@ -355,41 +356,9 @@ func formatDetailsForRestoration(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
paths, err := pathtransformer.GetPaths(ctx, backupVersion, fds.Items(), errs)
|
||||||
fdsPaths = fds.Paths()
|
|
||||||
paths = make([]path.RestorePaths, len(fdsPaths))
|
|
||||||
shortRefs = make([]string, len(fdsPaths))
|
|
||||||
el = errs.Local()
|
|
||||||
)
|
|
||||||
|
|
||||||
for i := range fdsPaths {
|
|
||||||
if el.Failure() != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := path.FromDataLayerPath(fdsPaths[i], true)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
el.AddRecoverable(clues.
|
return nil, clues.Wrap(err, "getting restore paths")
|
||||||
Wrap(err, "parsing details path after reduction").
|
|
||||||
WithMap(clues.In(ctx)).
|
|
||||||
With("path", fdsPaths[i]))
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
dir, err := p.Dir()
|
|
||||||
if err != nil {
|
|
||||||
el.AddRecoverable(clues.
|
|
||||||
Wrap(err, "getting restore directory after reduction").
|
|
||||||
WithClues(ctx).
|
|
||||||
With("path", fdsPaths[i]))
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
paths[i].StoragePath = p
|
|
||||||
paths[i].RestorePath = dir
|
|
||||||
shortRefs[i] = p.ShortRef()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if sel.Service == selectors.ServiceOneDrive {
|
if sel.Service == selectors.ServiceOneDrive {
|
||||||
@ -399,7 +368,5 @@ func formatDetailsForRestoration(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Ctx(ctx).With("short_refs", shortRefs).Infof("found %d details entries to restore", len(shortRefs))
|
return paths, nil
|
||||||
|
|
||||||
return paths, el.Failure()
|
|
||||||
}
|
}
|
||||||
|
|||||||
101
src/pkg/backup/details/testdata/testdata.go
vendored
101
src/pkg/backup/details/testdata/testdata.go
vendored
@ -54,10 +54,10 @@ func locFromRepo(rr path.Path, isItem bool) *path.Builder {
|
|||||||
|
|
||||||
type repoRefAndLocRef struct {
|
type repoRefAndLocRef struct {
|
||||||
RR path.Path
|
RR path.Path
|
||||||
loc *path.Builder
|
Loc *path.Builder
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p repoRefAndLocRef) mustAppend(newElement string, isItem bool) repoRefAndLocRef {
|
func (p repoRefAndLocRef) MustAppend(newElement string, isItem bool) repoRefAndLocRef {
|
||||||
e := newElement + folderSuffix
|
e := newElement + folderSuffix
|
||||||
|
|
||||||
if isItem {
|
if isItem {
|
||||||
@ -68,7 +68,7 @@ func (p repoRefAndLocRef) mustAppend(newElement string, isItem bool) repoRefAndL
|
|||||||
RR: mustAppendPath(p.RR, e, isItem),
|
RR: mustAppendPath(p.RR, e, isItem),
|
||||||
}
|
}
|
||||||
|
|
||||||
res.loc = locFromRepo(res.RR, isItem)
|
res.Loc = locFromRepo(res.RR, isItem)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
@ -85,7 +85,7 @@ func (p repoRefAndLocRef) FolderLocation() string {
|
|||||||
lastElem = f[len(f)-2]
|
lastElem = f[len(f)-2]
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.loc.Append(strings.TrimSuffix(lastElem, folderSuffix)).String()
|
return p.Loc.Append(strings.TrimSuffix(lastElem, folderSuffix)).String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func mustPathRep(ref string, isItem bool) repoRefAndLocRef {
|
func mustPathRep(ref string, isItem bool) repoRefAndLocRef {
|
||||||
@ -115,7 +115,7 @@ func mustPathRep(ref string, isItem bool) repoRefAndLocRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res.RR = rr
|
res.RR = rr
|
||||||
res.loc = locFromRepo(rr, isItem)
|
res.Loc = locFromRepo(rr, isItem)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
@ -138,12 +138,12 @@ var (
|
|||||||
Time4 = time.Date(2023, 10, 21, 10, 0, 0, 0, time.UTC)
|
Time4 = time.Date(2023, 10, 21, 10, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
ExchangeEmailInboxPath = mustPathRep("tenant-id/exchange/user-id/email/Inbox", false)
|
ExchangeEmailInboxPath = mustPathRep("tenant-id/exchange/user-id/email/Inbox", false)
|
||||||
ExchangeEmailBasePath = ExchangeEmailInboxPath.mustAppend("subfolder", false)
|
ExchangeEmailBasePath = ExchangeEmailInboxPath.MustAppend("subfolder", false)
|
||||||
ExchangeEmailBasePath2 = ExchangeEmailInboxPath.mustAppend("othersubfolder/", false)
|
ExchangeEmailBasePath2 = ExchangeEmailInboxPath.MustAppend("othersubfolder/", false)
|
||||||
ExchangeEmailBasePath3 = ExchangeEmailBasePath2.mustAppend("subsubfolder", false)
|
ExchangeEmailBasePath3 = ExchangeEmailBasePath2.MustAppend("subsubfolder", false)
|
||||||
ExchangeEmailItemPath1 = ExchangeEmailBasePath.mustAppend(ItemName1, true)
|
ExchangeEmailItemPath1 = ExchangeEmailBasePath.MustAppend(ItemName1, true)
|
||||||
ExchangeEmailItemPath2 = ExchangeEmailBasePath2.mustAppend(ItemName2, true)
|
ExchangeEmailItemPath2 = ExchangeEmailBasePath2.MustAppend(ItemName2, true)
|
||||||
ExchangeEmailItemPath3 = ExchangeEmailBasePath3.mustAppend(ItemName3, true)
|
ExchangeEmailItemPath3 = ExchangeEmailBasePath3.MustAppend(ItemName3, true)
|
||||||
|
|
||||||
ExchangeEmailItems = []details.Entry{
|
ExchangeEmailItems = []details.Entry{
|
||||||
{
|
{
|
||||||
@ -151,7 +151,7 @@ var (
|
|||||||
ShortRef: ExchangeEmailItemPath1.RR.ShortRef(),
|
ShortRef: ExchangeEmailItemPath1.RR.ShortRef(),
|
||||||
ParentRef: ExchangeEmailItemPath1.RR.ToBuilder().Dir().ShortRef(),
|
ParentRef: ExchangeEmailItemPath1.RR.ToBuilder().Dir().ShortRef(),
|
||||||
ItemRef: ExchangeEmailItemPath1.ItemLocation(),
|
ItemRef: ExchangeEmailItemPath1.ItemLocation(),
|
||||||
LocationRef: ExchangeEmailItemPath1.loc.String(),
|
LocationRef: ExchangeEmailItemPath1.Loc.String(),
|
||||||
ItemInfo: details.ItemInfo{
|
ItemInfo: details.ItemInfo{
|
||||||
Exchange: &details.ExchangeInfo{
|
Exchange: &details.ExchangeInfo{
|
||||||
ItemType: details.ExchangeMail,
|
ItemType: details.ExchangeMail,
|
||||||
@ -166,7 +166,7 @@ var (
|
|||||||
ShortRef: ExchangeEmailItemPath2.RR.ShortRef(),
|
ShortRef: ExchangeEmailItemPath2.RR.ShortRef(),
|
||||||
ParentRef: ExchangeEmailItemPath2.RR.ToBuilder().Dir().ShortRef(),
|
ParentRef: ExchangeEmailItemPath2.RR.ToBuilder().Dir().ShortRef(),
|
||||||
ItemRef: ExchangeEmailItemPath2.ItemLocation(),
|
ItemRef: ExchangeEmailItemPath2.ItemLocation(),
|
||||||
LocationRef: ExchangeEmailItemPath2.loc.String(),
|
LocationRef: ExchangeEmailItemPath2.Loc.String(),
|
||||||
ItemInfo: details.ItemInfo{
|
ItemInfo: details.ItemInfo{
|
||||||
Exchange: &details.ExchangeInfo{
|
Exchange: &details.ExchangeInfo{
|
||||||
ItemType: details.ExchangeMail,
|
ItemType: details.ExchangeMail,
|
||||||
@ -181,7 +181,7 @@ var (
|
|||||||
ShortRef: ExchangeEmailItemPath3.RR.ShortRef(),
|
ShortRef: ExchangeEmailItemPath3.RR.ShortRef(),
|
||||||
ParentRef: ExchangeEmailItemPath3.RR.ToBuilder().Dir().ShortRef(),
|
ParentRef: ExchangeEmailItemPath3.RR.ToBuilder().Dir().ShortRef(),
|
||||||
ItemRef: ExchangeEmailItemPath3.ItemLocation(),
|
ItemRef: ExchangeEmailItemPath3.ItemLocation(),
|
||||||
LocationRef: ExchangeEmailItemPath3.loc.String(),
|
LocationRef: ExchangeEmailItemPath3.Loc.String(),
|
||||||
ItemInfo: details.ItemInfo{
|
ItemInfo: details.ItemInfo{
|
||||||
Exchange: &details.ExchangeInfo{
|
Exchange: &details.ExchangeInfo{
|
||||||
ItemType: details.ExchangeMail,
|
ItemType: details.ExchangeMail,
|
||||||
@ -194,10 +194,10 @@ var (
|
|||||||
}
|
}
|
||||||
|
|
||||||
ExchangeContactsRootPath = mustPathRep("tenant-id/exchange/user-id/contacts/contacts", false)
|
ExchangeContactsRootPath = mustPathRep("tenant-id/exchange/user-id/contacts/contacts", false)
|
||||||
ExchangeContactsBasePath = ExchangeContactsRootPath.mustAppend("contacts", false)
|
ExchangeContactsBasePath = ExchangeContactsRootPath.MustAppend("contacts", false)
|
||||||
ExchangeContactsBasePath2 = ExchangeContactsRootPath.mustAppend("morecontacts", false)
|
ExchangeContactsBasePath2 = ExchangeContactsRootPath.MustAppend("morecontacts", false)
|
||||||
ExchangeContactsItemPath1 = ExchangeContactsBasePath.mustAppend(ItemName1, true)
|
ExchangeContactsItemPath1 = ExchangeContactsBasePath.MustAppend(ItemName1, true)
|
||||||
ExchangeContactsItemPath2 = ExchangeContactsBasePath2.mustAppend(ItemName2, true)
|
ExchangeContactsItemPath2 = ExchangeContactsBasePath2.MustAppend(ItemName2, true)
|
||||||
|
|
||||||
ExchangeContactsItems = []details.Entry{
|
ExchangeContactsItems = []details.Entry{
|
||||||
{
|
{
|
||||||
@ -205,7 +205,7 @@ var (
|
|||||||
ShortRef: ExchangeContactsItemPath1.RR.ShortRef(),
|
ShortRef: ExchangeContactsItemPath1.RR.ShortRef(),
|
||||||
ParentRef: ExchangeContactsItemPath1.RR.ToBuilder().Dir().ShortRef(),
|
ParentRef: ExchangeContactsItemPath1.RR.ToBuilder().Dir().ShortRef(),
|
||||||
ItemRef: ExchangeContactsItemPath1.ItemLocation(),
|
ItemRef: ExchangeContactsItemPath1.ItemLocation(),
|
||||||
LocationRef: ExchangeContactsItemPath1.loc.String(),
|
LocationRef: ExchangeContactsItemPath1.Loc.String(),
|
||||||
ItemInfo: details.ItemInfo{
|
ItemInfo: details.ItemInfo{
|
||||||
Exchange: &details.ExchangeInfo{
|
Exchange: &details.ExchangeInfo{
|
||||||
ItemType: details.ExchangeContact,
|
ItemType: details.ExchangeContact,
|
||||||
@ -218,7 +218,7 @@ var (
|
|||||||
ShortRef: ExchangeContactsItemPath2.RR.ShortRef(),
|
ShortRef: ExchangeContactsItemPath2.RR.ShortRef(),
|
||||||
ParentRef: ExchangeContactsItemPath2.RR.ToBuilder().Dir().ShortRef(),
|
ParentRef: ExchangeContactsItemPath2.RR.ToBuilder().Dir().ShortRef(),
|
||||||
ItemRef: ExchangeContactsItemPath2.ItemLocation(),
|
ItemRef: ExchangeContactsItemPath2.ItemLocation(),
|
||||||
LocationRef: ExchangeContactsItemPath2.loc.String(),
|
LocationRef: ExchangeContactsItemPath2.Loc.String(),
|
||||||
ItemInfo: details.ItemInfo{
|
ItemInfo: details.ItemInfo{
|
||||||
Exchange: &details.ExchangeInfo{
|
Exchange: &details.ExchangeInfo{
|
||||||
ItemType: details.ExchangeContact,
|
ItemType: details.ExchangeContact,
|
||||||
@ -228,11 +228,10 @@ var (
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
ExchangeEventsRootPath = mustPathRep("tenant-id/exchange/user-id/events/holidays", false)
|
ExchangeEventsBasePath = mustPathRep("tenant-id/exchange/user-id/events/holidays", false)
|
||||||
ExchangeEventsBasePath = ExchangeEventsRootPath.mustAppend("holidays", false)
|
ExchangeEventsBasePath2 = mustPathRep("tenant-id/exchange/user-id/events/moreholidays", false)
|
||||||
ExchangeEventsBasePath2 = ExchangeEventsRootPath.mustAppend("moreholidays", false)
|
ExchangeEventsItemPath1 = ExchangeEventsBasePath.MustAppend(ItemName1, true)
|
||||||
ExchangeEventsItemPath1 = ExchangeEventsBasePath.mustAppend(ItemName1, true)
|
ExchangeEventsItemPath2 = ExchangeEventsBasePath2.MustAppend(ItemName2, true)
|
||||||
ExchangeEventsItemPath2 = ExchangeEventsBasePath2.mustAppend(ItemName2, true)
|
|
||||||
|
|
||||||
ExchangeEventsItems = []details.Entry{
|
ExchangeEventsItems = []details.Entry{
|
||||||
{
|
{
|
||||||
@ -240,7 +239,7 @@ var (
|
|||||||
ShortRef: ExchangeEventsItemPath1.RR.ShortRef(),
|
ShortRef: ExchangeEventsItemPath1.RR.ShortRef(),
|
||||||
ParentRef: ExchangeEventsItemPath1.RR.ToBuilder().Dir().ShortRef(),
|
ParentRef: ExchangeEventsItemPath1.RR.ToBuilder().Dir().ShortRef(),
|
||||||
ItemRef: ExchangeEventsItemPath1.ItemLocation(),
|
ItemRef: ExchangeEventsItemPath1.ItemLocation(),
|
||||||
LocationRef: ExchangeEventsItemPath1.loc.String(),
|
LocationRef: ExchangeEventsItemPath1.Loc.String(),
|
||||||
ItemInfo: details.ItemInfo{
|
ItemInfo: details.ItemInfo{
|
||||||
Exchange: &details.ExchangeInfo{
|
Exchange: &details.ExchangeInfo{
|
||||||
ItemType: details.ExchangeEvent,
|
ItemType: details.ExchangeEvent,
|
||||||
@ -256,7 +255,7 @@ var (
|
|||||||
ShortRef: ExchangeEventsItemPath2.RR.ShortRef(),
|
ShortRef: ExchangeEventsItemPath2.RR.ShortRef(),
|
||||||
ParentRef: ExchangeEventsItemPath2.RR.ToBuilder().Dir().ShortRef(),
|
ParentRef: ExchangeEventsItemPath2.RR.ToBuilder().Dir().ShortRef(),
|
||||||
ItemRef: ExchangeEventsItemPath2.ItemLocation(),
|
ItemRef: ExchangeEventsItemPath2.ItemLocation(),
|
||||||
LocationRef: ExchangeEventsItemPath2.loc.String(),
|
LocationRef: ExchangeEventsItemPath2.Loc.String(),
|
||||||
ItemInfo: details.ItemInfo{
|
ItemInfo: details.ItemInfo{
|
||||||
Exchange: &details.ExchangeInfo{
|
Exchange: &details.ExchangeInfo{
|
||||||
ItemType: details.ExchangeEvent,
|
ItemType: details.ExchangeEvent,
|
||||||
@ -270,17 +269,17 @@ var (
|
|||||||
}
|
}
|
||||||
|
|
||||||
OneDriveRootPath = mustPathRep("tenant-id/onedrive/user-id/files/drives/foo/root:", false)
|
OneDriveRootPath = mustPathRep("tenant-id/onedrive/user-id/files/drives/foo/root:", false)
|
||||||
OneDriveFolderPath = OneDriveRootPath.mustAppend("folder", false)
|
OneDriveFolderPath = OneDriveRootPath.MustAppend("folder", false)
|
||||||
OneDriveBasePath1 = OneDriveFolderPath.mustAppend("a", false)
|
OneDriveBasePath1 = OneDriveFolderPath.MustAppend("a", false)
|
||||||
OneDriveBasePath2 = OneDriveFolderPath.mustAppend("b", false)
|
OneDriveBasePath2 = OneDriveFolderPath.MustAppend("b", false)
|
||||||
|
|
||||||
OneDriveItemPath1 = OneDriveFolderPath.mustAppend(ItemName1, true)
|
OneDriveItemPath1 = OneDriveFolderPath.MustAppend(ItemName1, true)
|
||||||
OneDriveItemPath2 = OneDriveBasePath1.mustAppend(ItemName2, true)
|
OneDriveItemPath2 = OneDriveBasePath1.MustAppend(ItemName2, true)
|
||||||
OneDriveItemPath3 = OneDriveBasePath2.mustAppend(ItemName3, true)
|
OneDriveItemPath3 = OneDriveBasePath2.MustAppend(ItemName3, true)
|
||||||
|
|
||||||
OneDriveFolderFolder = OneDriveFolderPath.loc.PopFront().String()
|
OneDriveFolderFolder = OneDriveFolderPath.Loc.PopFront().String()
|
||||||
OneDriveParentFolder1 = OneDriveBasePath1.loc.PopFront().String()
|
OneDriveParentFolder1 = OneDriveBasePath1.Loc.PopFront().String()
|
||||||
OneDriveParentFolder2 = OneDriveBasePath2.loc.PopFront().String()
|
OneDriveParentFolder2 = OneDriveBasePath2.Loc.PopFront().String()
|
||||||
|
|
||||||
OneDriveItems = []details.Entry{
|
OneDriveItems = []details.Entry{
|
||||||
{
|
{
|
||||||
@ -288,7 +287,7 @@ var (
|
|||||||
ShortRef: OneDriveItemPath1.RR.ShortRef(),
|
ShortRef: OneDriveItemPath1.RR.ShortRef(),
|
||||||
ParentRef: OneDriveItemPath1.RR.ToBuilder().Dir().ShortRef(),
|
ParentRef: OneDriveItemPath1.RR.ToBuilder().Dir().ShortRef(),
|
||||||
ItemRef: OneDriveItemPath1.ItemLocation(),
|
ItemRef: OneDriveItemPath1.ItemLocation(),
|
||||||
LocationRef: OneDriveItemPath1.loc.String(),
|
LocationRef: OneDriveItemPath1.Loc.String(),
|
||||||
ItemInfo: details.ItemInfo{
|
ItemInfo: details.ItemInfo{
|
||||||
OneDrive: &details.OneDriveInfo{
|
OneDrive: &details.OneDriveInfo{
|
||||||
ItemType: details.OneDriveItem,
|
ItemType: details.OneDriveItem,
|
||||||
@ -306,7 +305,7 @@ var (
|
|||||||
ShortRef: OneDriveItemPath2.RR.ShortRef(),
|
ShortRef: OneDriveItemPath2.RR.ShortRef(),
|
||||||
ParentRef: OneDriveItemPath2.RR.ToBuilder().Dir().ShortRef(),
|
ParentRef: OneDriveItemPath2.RR.ToBuilder().Dir().ShortRef(),
|
||||||
ItemRef: OneDriveItemPath2.ItemLocation(),
|
ItemRef: OneDriveItemPath2.ItemLocation(),
|
||||||
LocationRef: OneDriveItemPath2.loc.String(),
|
LocationRef: OneDriveItemPath2.Loc.String(),
|
||||||
ItemInfo: details.ItemInfo{
|
ItemInfo: details.ItemInfo{
|
||||||
OneDrive: &details.OneDriveInfo{
|
OneDrive: &details.OneDriveInfo{
|
||||||
ItemType: details.OneDriveItem,
|
ItemType: details.OneDriveItem,
|
||||||
@ -324,7 +323,7 @@ var (
|
|||||||
ShortRef: OneDriveItemPath3.RR.ShortRef(),
|
ShortRef: OneDriveItemPath3.RR.ShortRef(),
|
||||||
ParentRef: OneDriveItemPath3.RR.ToBuilder().Dir().ShortRef(),
|
ParentRef: OneDriveItemPath3.RR.ToBuilder().Dir().ShortRef(),
|
||||||
ItemRef: OneDriveItemPath3.ItemLocation(),
|
ItemRef: OneDriveItemPath3.ItemLocation(),
|
||||||
LocationRef: OneDriveItemPath3.loc.String(),
|
LocationRef: OneDriveItemPath3.Loc.String(),
|
||||||
ItemInfo: details.ItemInfo{
|
ItemInfo: details.ItemInfo{
|
||||||
OneDrive: &details.OneDriveInfo{
|
OneDrive: &details.OneDriveInfo{
|
||||||
ItemType: details.OneDriveItem,
|
ItemType: details.OneDriveItem,
|
||||||
@ -340,17 +339,17 @@ var (
|
|||||||
}
|
}
|
||||||
|
|
||||||
SharePointRootPath = mustPathRep("tenant-id/sharepoint/site-id/libraries/drives/foo/root:", false)
|
SharePointRootPath = mustPathRep("tenant-id/sharepoint/site-id/libraries/drives/foo/root:", false)
|
||||||
SharePointLibraryPath = SharePointRootPath.mustAppend("library", false)
|
SharePointLibraryPath = SharePointRootPath.MustAppend("library", false)
|
||||||
SharePointBasePath1 = SharePointLibraryPath.mustAppend("a", false)
|
SharePointBasePath1 = SharePointLibraryPath.MustAppend("a", false)
|
||||||
SharePointBasePath2 = SharePointLibraryPath.mustAppend("b", false)
|
SharePointBasePath2 = SharePointLibraryPath.MustAppend("b", false)
|
||||||
|
|
||||||
SharePointLibraryItemPath1 = SharePointLibraryPath.mustAppend(ItemName1, true)
|
SharePointLibraryItemPath1 = SharePointLibraryPath.MustAppend(ItemName1, true)
|
||||||
SharePointLibraryItemPath2 = SharePointBasePath1.mustAppend(ItemName2, true)
|
SharePointLibraryItemPath2 = SharePointBasePath1.MustAppend(ItemName2, true)
|
||||||
SharePointLibraryItemPath3 = SharePointBasePath2.mustAppend(ItemName3, true)
|
SharePointLibraryItemPath3 = SharePointBasePath2.MustAppend(ItemName3, true)
|
||||||
|
|
||||||
SharePointLibraryFolder = SharePointLibraryPath.loc.PopFront().String()
|
SharePointLibraryFolder = SharePointLibraryPath.Loc.PopFront().String()
|
||||||
SharePointParentLibrary1 = SharePointBasePath1.loc.PopFront().String()
|
SharePointParentLibrary1 = SharePointBasePath1.Loc.PopFront().String()
|
||||||
SharePointParentLibrary2 = SharePointBasePath2.loc.PopFront().String()
|
SharePointParentLibrary2 = SharePointBasePath2.Loc.PopFront().String()
|
||||||
|
|
||||||
SharePointLibraryItems = []details.Entry{
|
SharePointLibraryItems = []details.Entry{
|
||||||
{
|
{
|
||||||
@ -358,7 +357,7 @@ var (
|
|||||||
ShortRef: SharePointLibraryItemPath1.RR.ShortRef(),
|
ShortRef: SharePointLibraryItemPath1.RR.ShortRef(),
|
||||||
ParentRef: SharePointLibraryItemPath1.RR.ToBuilder().Dir().ShortRef(),
|
ParentRef: SharePointLibraryItemPath1.RR.ToBuilder().Dir().ShortRef(),
|
||||||
ItemRef: SharePointLibraryItemPath1.ItemLocation(),
|
ItemRef: SharePointLibraryItemPath1.ItemLocation(),
|
||||||
LocationRef: SharePointLibraryItemPath1.loc.String(),
|
LocationRef: SharePointLibraryItemPath1.Loc.String(),
|
||||||
ItemInfo: details.ItemInfo{
|
ItemInfo: details.ItemInfo{
|
||||||
SharePoint: &details.SharePointInfo{
|
SharePoint: &details.SharePointInfo{
|
||||||
ItemType: details.SharePointLibrary,
|
ItemType: details.SharePointLibrary,
|
||||||
@ -376,7 +375,7 @@ var (
|
|||||||
ShortRef: SharePointLibraryItemPath2.RR.ShortRef(),
|
ShortRef: SharePointLibraryItemPath2.RR.ShortRef(),
|
||||||
ParentRef: SharePointLibraryItemPath2.RR.ToBuilder().Dir().ShortRef(),
|
ParentRef: SharePointLibraryItemPath2.RR.ToBuilder().Dir().ShortRef(),
|
||||||
ItemRef: SharePointLibraryItemPath2.ItemLocation(),
|
ItemRef: SharePointLibraryItemPath2.ItemLocation(),
|
||||||
LocationRef: SharePointLibraryItemPath2.loc.String(),
|
LocationRef: SharePointLibraryItemPath2.Loc.String(),
|
||||||
ItemInfo: details.ItemInfo{
|
ItemInfo: details.ItemInfo{
|
||||||
SharePoint: &details.SharePointInfo{
|
SharePoint: &details.SharePointInfo{
|
||||||
ItemType: details.SharePointLibrary,
|
ItemType: details.SharePointLibrary,
|
||||||
@ -394,7 +393,7 @@ var (
|
|||||||
ShortRef: SharePointLibraryItemPath3.RR.ShortRef(),
|
ShortRef: SharePointLibraryItemPath3.RR.ShortRef(),
|
||||||
ParentRef: SharePointLibraryItemPath3.RR.ToBuilder().Dir().ShortRef(),
|
ParentRef: SharePointLibraryItemPath3.RR.ToBuilder().Dir().ShortRef(),
|
||||||
ItemRef: SharePointLibraryItemPath3.ItemLocation(),
|
ItemRef: SharePointLibraryItemPath3.ItemLocation(),
|
||||||
LocationRef: SharePointLibraryItemPath3.loc.String(),
|
LocationRef: SharePointLibraryItemPath3.Loc.String(),
|
||||||
ItemInfo: details.ItemInfo{
|
ItemInfo: details.ItemInfo{
|
||||||
SharePoint: &details.SharePointInfo{
|
SharePoint: &details.SharePointInfo{
|
||||||
ItemType: details.SharePointLibrary,
|
ItemType: details.SharePointLibrary,
|
||||||
|
|||||||
@ -38,3 +38,13 @@ func GetDriveFolderPath(p Path) (string, error) {
|
|||||||
|
|
||||||
return Builder{}.Append(drivePath.Folders...).String(), nil
|
return Builder{}.Append(drivePath.Folders...).String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildDriveLocation takes a driveID and a set of unescaped element names,
|
||||||
|
// including the root folder, and returns a *path.Builder containing the
|
||||||
|
// canonical path representation for the drive path.
|
||||||
|
func BuildDriveLocation(
|
||||||
|
driveID string,
|
||||||
|
unescapedElements ...string,
|
||||||
|
) *Builder {
|
||||||
|
return Builder{}.Append("drives", driveID).Append(unescapedElements...)
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package path_test
|
package path_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
@ -63,3 +64,49 @@ func (suite *OneDrivePathSuite) Test_ToOneDrivePath() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *OneDrivePathSuite) TestFormatDriveFolders() {
|
||||||
|
const (
|
||||||
|
driveID = "some-drive-id"
|
||||||
|
drivePrefix = "drives/" + driveID
|
||||||
|
)
|
||||||
|
|
||||||
|
table := []struct {
|
||||||
|
name string
|
||||||
|
input []string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "normal",
|
||||||
|
input: []string{
|
||||||
|
"root:",
|
||||||
|
"foo",
|
||||||
|
"bar",
|
||||||
|
},
|
||||||
|
expected: strings.Join(
|
||||||
|
append([]string{drivePrefix}, "root:", "foo", "bar"),
|
||||||
|
"/"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "has character that would be escaped",
|
||||||
|
input: []string{
|
||||||
|
"root:",
|
||||||
|
"foo/",
|
||||||
|
"bar",
|
||||||
|
},
|
||||||
|
// Element "foo/" should end up escaped in the string output.
|
||||||
|
expected: strings.Join(
|
||||||
|
append([]string{drivePrefix}, "root:", `foo\/`, "bar"),
|
||||||
|
"/"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range table {
|
||||||
|
suite.Run(test.name, func() {
|
||||||
|
assert.Equal(
|
||||||
|
suite.T(),
|
||||||
|
test.expected,
|
||||||
|
path.BuildDriveLocation(driveID, test.input...).String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -249,18 +249,6 @@ func (suite *SelectorReduceSuite) TestReduce() {
|
|||||||
},
|
},
|
||||||
expected: []details.Entry{testdata.ExchangeEventsItems[0]},
|
expected: []details.Entry{testdata.ExchangeEventsItems[0]},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "ExchangeEventsByFolderRoot",
|
|
||||||
selFunc: func() selectors.Reducer {
|
|
||||||
sel := selectors.NewExchangeRestore(selectors.Any())
|
|
||||||
sel.Include(sel.EventCalendars(
|
|
||||||
[]string{testdata.ExchangeEventsRootPath.FolderLocation()},
|
|
||||||
))
|
|
||||||
|
|
||||||
return sel
|
|
||||||
},
|
|
||||||
expected: testdata.ExchangeEventsItems,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range table {
|
for _, test := range table {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user