Trim trailing '/' from CLI input for restore/details (#1175)

## Description

Use paths package to trim unescaped trailing '/' characters from input for Exchange mail and OneDrive folder names. Add tests for Exchange showing that the trimming also works properly if the folder name ends with '/'.

## Type of change

<!--- Please check the type of change your PR introduces: --->
- [ ] 🌻 Feature
- [x] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Test
- [ ] 💻 CI/Deployment
- [ ] 🐹 Trivial/Minor

## Issue(s)

* closes #1147 

## Test Plan

<!-- How will this be tested prior to merging.-->
- [x] 💪 Manual
- [x]  Unit test
- [ ] 💚 E2E
This commit is contained in:
ashmrtn 2022-10-17 10:54:36 -07:00 committed by GitHub
parent 7d72cd12a4
commit 72a3c7ab3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 79 additions and 9 deletions

View File

@ -142,6 +142,8 @@ func IncludeExchangeRestoreDataSelectors(
return
}
opts.EmailFolder = trimFolderSlash(opts.EmailFolder)
// or add selectors for each type of data
AddExchangeInclude(sel, opts.Users, opts.ContactFolder, opts.Contact, sel.Contacts)
AddExchangeInclude(sel, opts.Users, opts.EmailFolder, opts.Email, sel.Mails)

View File

@ -7,6 +7,7 @@ import (
"github.com/spf13/pflag"
"github.com/alcionai/corso/src/internal/common"
"github.com/alcionai/corso/src/pkg/path"
)
type PopulatedFlags map[string]struct{}
@ -50,3 +51,16 @@ func IsValidBool(in string) bool {
_, err := strconv.ParseBool(in)
return err == nil
}
// trimFolderSlash takes a set of folder paths and returns a set of folder paths
// with any unescaped trailing `/` characters removed.
func trimFolderSlash(folders []string) []string {
res := make([]string, 0, len(folders))
for _, p := range folders {
// Use path package because it has logic to handle escaping already.
res = append(res, path.TrimTrailingSlash(p))
}
return res
}

View File

@ -87,6 +87,8 @@ func IncludeOneDriveRestoreDataSelectors(
return
}
opts.Paths = trimFolderSlash(opts.Paths)
if lp == 0 {
opts.Paths = selectors.Any()
}

View File

@ -139,8 +139,38 @@ var (
},
},
{
Name: "EmailsBySubject",
Name: "EmailsFolderPrefixMatchTrailingSlash",
Expected: testdata.ExchangeEmailItems,
Opts: utils.ExchangeOpts{
EmailFolder: []string{testdata.ExchangeEmailInboxPath.Folder() + "/"},
},
},
{
Name: "EmailsFolderWithSlashPrefixMatch",
Expected: []details.DetailsEntry{
testdata.ExchangeEmailItems[1],
testdata.ExchangeEmailItems[2],
},
Opts: utils.ExchangeOpts{
EmailFolder: []string{testdata.ExchangeEmailBasePath2.Folder()},
},
},
{
Name: "EmailsFolderWithSlashPrefixMatchTrailingSlash",
Expected: []details.DetailsEntry{
testdata.ExchangeEmailItems[1],
testdata.ExchangeEmailItems[2],
},
Opts: utils.ExchangeOpts{
EmailFolder: []string{testdata.ExchangeEmailBasePath2.Folder() + "/"},
},
},
{
Name: "EmailsBySubject",
Expected: []details.DetailsEntry{
testdata.ExchangeEmailItems[0],
testdata.ExchangeEmailItems[1],
},
Opts: utils.ExchangeOpts{
EmailSender: "a-person",
},
@ -181,7 +211,10 @@ var (
},
{
Name: "MultipleMailShortRef",
Expected: testdata.ExchangeEmailItems,
Expected: []details.DetailsEntry{
testdata.ExchangeEmailItems[0],
testdata.ExchangeEmailItems[1],
},
Opts: utils.ExchangeOpts{
Email: []string{
testdata.ExchangeEmailItemPath1.ShortRef(),

View File

@ -145,7 +145,7 @@ func (pb *Builder) appendElements(escaped bool, elements []string) error {
tmp := e
if escaped {
tmp = trimTrailingSlash(tmp)
tmp = TrimTrailingSlash(tmp)
// If tmp was just the path separator then it will be empty now.
if len(tmp) == 0 {
continue
@ -310,7 +310,7 @@ func (pb Builder) ToDataLayerOneDrivePath(
// resource-specific type. If p does not match any resource-specific paths or
// is malformed returns an error.
func FromDataLayerPath(p string, isItem bool) (Path, error) {
p = trimTrailingSlash(p)
p = TrimTrailingSlash(p)
// If p was just the path separator then it will be empty now.
if len(p) == 0 {
return nil, errors.Errorf("logically empty path given: %s", p)
@ -437,11 +437,11 @@ func validateEscapedElement(element string) error {
return nil
}
// trimTrailingSlash takes an escaped path element and returns an escaped path
// TrimTrailingSlash takes an escaped path element and returns an escaped path
// element with the trailing path separator character(s) removed if they were not
// escaped. If there were no trailing path separator character(s) or the separator(s)
// were escaped the input is returned unchanged.
func trimTrailingSlash(element string) string {
func TrimTrailingSlash(element string) string {
for len(element) > 0 && element[len(element)-1] == pathSeparator {
lastIdx := len(element) - 1
numSlashes := 0

View File

@ -92,7 +92,10 @@ func (suite *SelectorReduceSuite) TestReduce() {
return sel
},
expected: testdata.ExchangeEmailItems,
expected: []details.DetailsEntry{
testdata.ExchangeEmailItems[0],
testdata.ExchangeEmailItems[1],
},
},
{
name: "ExchangeMailReceivedTime",

View File

@ -36,6 +36,7 @@ func mustAppendPath(p path.Path, newElement string, isItem bool) path.Path {
const (
ItemName1 = "item1"
ItemName2 = "item2"
ItemName3 = "item3"
)
var (
@ -44,9 +45,11 @@ var (
ExchangeEmailInboxPath = mustParsePath("tenant-id/exchange/user-id/email/Inbox", false)
ExchangeEmailBasePath = mustAppendPath(ExchangeEmailInboxPath, "subfolder", false)
ExchangeEmailBasePath2 = mustAppendPath(ExchangeEmailInboxPath, "othersubfolder", false)
ExchangeEmailBasePath2 = mustAppendPath(ExchangeEmailInboxPath, "othersubfolder/", false)
ExchangeEmailBasePath3 = mustAppendPath(ExchangeEmailBasePath2, "subsubfolder", false)
ExchangeEmailItemPath1 = mustAppendPath(ExchangeEmailBasePath, ItemName1, true)
ExchangeEmailItemPath2 = mustAppendPath(ExchangeEmailBasePath2, ItemName2, true)
ExchangeEmailItemPath3 = mustAppendPath(ExchangeEmailBasePath3, ItemName3, true)
ExchangeEmailItems = []details.DetailsEntry{
{
@ -75,6 +78,19 @@ var (
},
},
},
{
RepoRef: ExchangeEmailItemPath3.String(),
ShortRef: ExchangeEmailItemPath3.ShortRef(),
ParentRef: ExchangeEmailItemPath3.ToBuilder().Dir().ShortRef(),
ItemInfo: details.ItemInfo{
Exchange: &details.ExchangeInfo{
ItemType: details.ExchangeMail,
Sender: "another-person",
Subject: "baz",
Received: Time2,
},
},
},
}
ExchangeContactsRootPath = mustParsePath("tenant-id/exchange/user-id/contacts/contacts", false)