diff --git a/src/cli/export/sharepoint_test.go b/src/cli/export/sharepoint_test.go index 4850173ca..76439cf13 100644 --- a/src/cli/export/sharepoint_test.go +++ b/src/cli/export/sharepoint_test.go @@ -60,8 +60,7 @@ func (suite *SharePointUnitSuite) TestAddSharePointCommands() { "--" + flags.FileCreatedBeforeFN, flagsTD.FileCreatedBeforeInput, "--" + flags.FileModifiedAfterFN, flagsTD.FileModifiedAfterInput, "--" + flags.FileModifiedBeforeFN, flagsTD.FileModifiedBeforeInput, - "--" + flags.ListItemFN, flagsTD.FlgInputs(flagsTD.ListItemInput), - "--" + flags.ListFolderFN, flagsTD.FlgInputs(flagsTD.ListFolderInput), + "--" + flags.ListFN, flagsTD.FlgInputs(flagsTD.ListsInput), "--" + flags.PageFN, flagsTD.FlgInputs(flagsTD.PageInput), "--" + flags.PageFolderFN, flagsTD.FlgInputs(flagsTD.PageFolderInput), "--" + flags.FormatFN, flagsTD.FormatType, @@ -88,8 +87,7 @@ func (suite *SharePointUnitSuite) TestAddSharePointCommands() { assert.Equal(t, flagsTD.FileCreatedBeforeInput, opts.FileCreatedBefore) assert.Equal(t, flagsTD.FileModifiedAfterInput, opts.FileModifiedAfter) assert.Equal(t, flagsTD.FileModifiedBeforeInput, opts.FileModifiedBefore) - assert.ElementsMatch(t, flagsTD.ListItemInput, opts.ListItem) - assert.ElementsMatch(t, flagsTD.ListFolderInput, opts.ListFolder) + assert.ElementsMatch(t, flagsTD.ListsInput, opts.Lists) assert.ElementsMatch(t, flagsTD.PageInput, opts.Page) assert.ElementsMatch(t, flagsTD.PageFolderInput, opts.PageFolder) assert.Equal(t, flagsTD.Archive, opts.ExportCfg.Archive) diff --git a/src/cli/flags/sharepoint.go b/src/cli/flags/sharepoint.go index a7053115e..30af71975 100644 --- a/src/cli/flags/sharepoint.go +++ b/src/cli/flags/sharepoint.go @@ -12,8 +12,7 @@ const ( const ( LibraryFN = "library" - ListFolderFN = "list" - ListItemFN = "list-item" + ListFN = "list" PageFolderFN = "page-folder" PageFN = "page" SiteFN = "site" // site only accepts WebURL values @@ -22,8 +21,7 @@ const ( var ( LibraryFV string - ListFolderFV []string - ListItemFV []string + ListFV []string PageFolderFV []string PageFV []string SiteIDFV []string @@ -67,17 +65,11 @@ func AddSharePointDetailsAndRestoreFlags(cmd *cobra.Command) { "Select files modified before this datetime.") // lists - fs.StringSliceVar( - &ListFolderFV, - ListFolderFN, nil, + &ListFV, + ListFN, nil, "Select lists by name; accepts '"+Wildcard+"' to select all lists.") - cobra.CheckErr(fs.MarkHidden(ListFolderFN)) - fs.StringSliceVar( - &ListItemFV, - ListItemFN, nil, - "Select lists by item name; accepts '"+Wildcard+"' to select all lists.") - cobra.CheckErr(fs.MarkHidden(ListItemFN)) + cobra.CheckErr(fs.MarkHidden(ListFN)) // pages diff --git a/src/cli/flags/testdata/flags.go b/src/cli/flags/testdata/flags.go index 8544063c7..09a9c12b4 100644 --- a/src/cli/flags/testdata/flags.go +++ b/src/cli/flags/testdata/flags.go @@ -59,8 +59,7 @@ var ( FileModifiedAfterInput = "fileModifiedAfter" FileModifiedBeforeInput = "fileModifiedBefore" - ListFolderInput = []string{"listFolder1", "listFolder2"} - ListItemInput = []string{"listItem1", "listItem2"} + ListsInput = []string{"listName1", "listName2"} PageFolderInput = []string{"pageFolder1", "pageFolder2"} PageInput = []string{"page1", "page2"} diff --git a/src/cli/restore/groups_test.go b/src/cli/restore/groups_test.go index 0a8e2f17f..63141eaed 100644 --- a/src/cli/restore/groups_test.go +++ b/src/cli/restore/groups_test.go @@ -60,8 +60,7 @@ func (suite *GroupsUnitSuite) TestAddGroupsCommands() { "--" + flags.FileCreatedBeforeFN, flagsTD.FileCreatedBeforeInput, "--" + flags.FileModifiedAfterFN, flagsTD.FileModifiedAfterInput, "--" + flags.FileModifiedBeforeFN, flagsTD.FileModifiedBeforeInput, - "--" + flags.ListItemFN, flagsTD.FlgInputs(flagsTD.ListItemInput), - "--" + flags.ListFolderFN, flagsTD.FlgInputs(flagsTD.ListFolderInput), + "--" + flags.ListFN, flagsTD.FlgInputs(flagsTD.ListsInput), "--" + flags.PageFN, flagsTD.FlgInputs(flagsTD.PageInput), "--" + flags.PageFolderFN, flagsTD.FlgInputs(flagsTD.PageFolderInput), "--" + flags.CollisionsFN, flagsTD.Collisions, @@ -92,6 +91,7 @@ func (suite *GroupsUnitSuite) TestAddGroupsCommands() { assert.Equal(t, flagsTD.FileModifiedBeforeInput, opts.FileModifiedBefore) assert.Equal(t, flagsTD.Collisions, opts.RestoreCfg.Collisions) assert.Equal(t, flagsTD.Destination, opts.RestoreCfg.Destination) + assert.ElementsMatch(t, flagsTD.ListsInput, opts.Lists) // assert.Equal(t, flagsTD.ToResource, opts.RestoreCfg.ProtectedResource) assert.True(t, flags.NoPermissionsFV) flagsTD.AssertProviderFlags(t, cmd) diff --git a/src/cli/restore/sharepoint_test.go b/src/cli/restore/sharepoint_test.go index ef28f399a..05e83df0c 100644 --- a/src/cli/restore/sharepoint_test.go +++ b/src/cli/restore/sharepoint_test.go @@ -59,8 +59,7 @@ func (suite *SharePointUnitSuite) TestAddSharePointCommands() { "--" + flags.FileCreatedBeforeFN, flagsTD.FileCreatedBeforeInput, "--" + flags.FileModifiedAfterFN, flagsTD.FileModifiedAfterInput, "--" + flags.FileModifiedBeforeFN, flagsTD.FileModifiedBeforeInput, - "--" + flags.ListItemFN, flagsTD.FlgInputs(flagsTD.ListItemInput), - "--" + flags.ListFolderFN, flagsTD.FlgInputs(flagsTD.ListFolderInput), + "--" + flags.ListFN, flagsTD.FlgInputs(flagsTD.ListsInput), "--" + flags.PageFN, flagsTD.FlgInputs(flagsTD.PageInput), "--" + flags.PageFolderFN, flagsTD.FlgInputs(flagsTD.PageFolderInput), "--" + flags.CollisionsFN, flagsTD.Collisions, @@ -89,8 +88,7 @@ func (suite *SharePointUnitSuite) TestAddSharePointCommands() { assert.Equal(t, flagsTD.FileCreatedBeforeInput, opts.FileCreatedBefore) assert.Equal(t, flagsTD.FileModifiedAfterInput, opts.FileModifiedAfter) assert.Equal(t, flagsTD.FileModifiedBeforeInput, opts.FileModifiedBefore) - assert.ElementsMatch(t, flagsTD.ListItemInput, opts.ListItem) - assert.ElementsMatch(t, flagsTD.ListFolderInput, opts.ListFolder) + assert.ElementsMatch(t, flagsTD.ListsInput, opts.Lists) assert.ElementsMatch(t, flagsTD.PageInput, opts.Page) assert.ElementsMatch(t, flagsTD.PageFolderInput, opts.PageFolder) assert.Equal(t, flagsTD.Collisions, opts.RestoreCfg.Collisions) diff --git a/src/cli/utils/flags.go b/src/cli/utils/flags.go index 4bcf52811..9fa87793f 100644 --- a/src/cli/utils/flags.go +++ b/src/cli/utils/flags.go @@ -1,8 +1,12 @@ package utils import ( + "errors" "strconv" + "github.com/alcionai/clues" + + "github.com/alcionai/corso/src/cli/flags" "github.com/alcionai/corso/src/pkg/dttm" "github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/selectors" @@ -39,3 +43,51 @@ func trimFolderSlash(folders []string) []string { return res } + +func validateCommonTimeFlags(opts any) error { + timeFlags := []string{ + flags.FileCreatedAfterFN, + flags.FileCreatedBeforeFN, + flags.FileModifiedAfterFN, + flags.FileModifiedBeforeFN, + } + + isFlagPopulated := func(opts any, flag string) bool { + switch opts := opts.(type) { + case GroupsOpts: + _, ok := opts.Populated[flag] + return ok + case SharePointOpts: + _, ok := opts.Populated[flag] + return ok + default: + return false + } + } + + getTimeField := func(opts any, flag string) (string, error) { + switch opts := opts.(type) { + case GroupsOpts: + return opts.GetFileTimeField(flag), nil + case SharePointOpts: + return opts.GetFileTimeField(flag), nil + default: + return "", errors.New("unsupported type") + } + } + + for _, flag := range timeFlags { + if populated := isFlagPopulated(opts, flag); populated { + timeField, err := getTimeField(opts, flag) + if err != nil { + return err + } + + if !IsValidTimeFormat(timeField) { + return clues.New("invalid time format for " + flag) + } + } + } + + return nil +} diff --git a/src/cli/utils/groups.go b/src/cli/utils/groups.go index c5fe65519..5846493b7 100644 --- a/src/cli/utils/groups.go +++ b/src/cli/utils/groups.go @@ -32,8 +32,7 @@ type GroupsOpts struct { FileModifiedAfter string FileModifiedBefore string - ListFolder []string - ListItem []string + Lists []string PageFolder []string Page []string @@ -44,6 +43,21 @@ type GroupsOpts struct { Populated flags.PopulatedFlags } +func (g GroupsOpts) GetFileTimeField(flag string) string { + switch flag { + case flags.FileCreatedAfterFN: + return g.FileCreatedAfter + case flags.FileCreatedBeforeFN: + return g.FileCreatedBefore + case flags.FileModifiedAfterFN: + return g.FileModifiedAfter + case flags.FileModifiedBeforeFN: + return g.FileModifiedBefore + default: + return "" + } +} + func GroupsAllowedCategories() map[string]struct{} { return map[string]struct{}{ flags.DataLibraries: {}, @@ -93,8 +107,7 @@ func MakeGroupsOpts(cmd *cobra.Command) GroupsOpts { MessageLastReplyAfter: flags.MessageLastReplyAfterFV, MessageLastReplyBefore: flags.MessageLastReplyBeforeFV, - ListFolder: flags.ListFolderFV, - ListItem: flags.ListItemFV, + Lists: flags.ListFV, Page: flags.PageFV, PageFolder: flags.PageFolderFV, @@ -126,22 +139,6 @@ func ValidateGroupsRestoreFlags(backupID string, opts GroupsOpts, isRestore bool } } - if _, ok := opts.Populated[flags.FileCreatedAfterFN]; ok && !IsValidTimeFormat(opts.FileCreatedAfter) { - return clues.New("invalid time format for " + flags.FileCreatedAfterFN) - } - - if _, ok := opts.Populated[flags.FileCreatedBeforeFN]; ok && !IsValidTimeFormat(opts.FileCreatedBefore) { - return clues.New("invalid time format for " + flags.FileCreatedBeforeFN) - } - - if _, ok := opts.Populated[flags.FileModifiedAfterFN]; ok && !IsValidTimeFormat(opts.FileModifiedAfter) { - return clues.New("invalid time format for " + flags.FileModifiedAfterFN) - } - - if _, ok := opts.Populated[flags.FileModifiedBeforeFN]; ok && !IsValidTimeFormat(opts.FileModifiedBefore) { - return clues.New("invalid time format for " + flags.FileModifiedBeforeFN) - } - if _, ok := opts.Populated[flags.MessageCreatedAfterFN]; ok && !IsValidTimeFormat(opts.MessageCreatedAfter) { return clues.New("invalid time format for " + flags.MessageCreatedAfterFN) } @@ -158,7 +155,7 @@ func ValidateGroupsRestoreFlags(backupID string, opts GroupsOpts, isRestore bool return clues.New("invalid time format for " + flags.MessageLastReplyBeforeFN) } - return nil + return validateCommonTimeFlags(opts) } // AddGroupsFilter adds the scope of the provided values to the selector's @@ -181,7 +178,7 @@ func IncludeGroupsRestoreDataSelectors(ctx context.Context, opts GroupsOpts) *se var ( groups = opts.Groups folderPaths, fileNames = len(opts.FolderPath), len(opts.FileName) - listFolders, listItems = len(opts.ListFolder), len(opts.ListItem) + lists = len(opts.Lists) pageFolders, pageItems = len(opts.PageFolder), len(opts.Page) chans, chanMsgs = len(opts.Channels), len(opts.Messages) convs, convPosts = len(opts.Conversations), len(opts.Posts) @@ -194,7 +191,7 @@ func IncludeGroupsRestoreDataSelectors(ctx context.Context, opts GroupsOpts) *se sel := selectors.NewGroupsRestore(groups) if folderPaths+fileNames+ - listFolders+listItems+ + lists+ pageFolders+pageItems+ chans+chanMsgs+ convs+convPosts == 0 { @@ -204,58 +201,43 @@ func IncludeGroupsRestoreDataSelectors(ctx context.Context, opts GroupsOpts) *se // sharepoint site selectors - if folderPaths+fileNames+ - listFolders+listItems+ - pageFolders+pageItems > 0 { - if folderPaths+fileNames > 0 { - if fileNames == 0 { - opts.FileName = selectors.Any() - } - - opts.FolderPath = trimFolderSlash(opts.FolderPath) - containsFolders, prefixFolders := splitFoldersIntoContainsAndPrefix(opts.FolderPath) - - if len(containsFolders) > 0 { - sel.Include(sel.LibraryItems(containsFolders, opts.FileName)) - } - - if len(prefixFolders) > 0 { - sel.Include(sel.LibraryItems(prefixFolders, opts.FileName, selectors.PrefixMatch())) - } + if folderPaths+fileNames > 0 { + if fileNames == 0 { + opts.FileName = selectors.Any() } - if listFolders+listItems > 0 { - if listItems == 0 { - opts.ListItem = selectors.Any() - } + opts.FolderPath = trimFolderSlash(opts.FolderPath) + containsFolders, prefixFolders := splitFoldersIntoContainsAndPrefix(opts.FolderPath) - opts.ListFolder = trimFolderSlash(opts.ListFolder) - containsFolders, prefixFolders := splitFoldersIntoContainsAndPrefix(opts.ListFolder) - - if len(containsFolders) > 0 { - sel.Include(sel.ListItems(containsFolders, opts.ListItem)) - } - - if len(prefixFolders) > 0 { - sel.Include(sel.ListItems(prefixFolders, opts.ListItem, selectors.PrefixMatch())) - } + if len(containsFolders) > 0 { + sel.Include(sel.LibraryItems(containsFolders, opts.FileName)) } - if pageFolders+pageItems > 0 { - if pageItems == 0 { - opts.Page = selectors.Any() - } + if len(prefixFolders) > 0 { + sel.Include(sel.LibraryItems(prefixFolders, opts.FileName, selectors.PrefixMatch())) + } + } - opts.PageFolder = trimFolderSlash(opts.PageFolder) - containsFolders, prefixFolders := splitFoldersIntoContainsAndPrefix(opts.PageFolder) + if lists > 0 { + opts.Lists = trimFolderSlash(opts.Lists) + sel.Include(sel.ListItems(opts.Lists, opts.Lists, selectors.StrictEqualMatch())) + sel.Configure(selectors.Config{OnlyMatchItemNames: true}) + } - if len(containsFolders) > 0 { - sel.Include(sel.PageItems(containsFolders, opts.Page)) - } + if pageFolders+pageItems > 0 { + if pageItems == 0 { + opts.Page = selectors.Any() + } - if len(prefixFolders) > 0 { - sel.Include(sel.PageItems(prefixFolders, opts.Page, selectors.PrefixMatch())) - } + opts.PageFolder = trimFolderSlash(opts.PageFolder) + containsFolders, prefixFolders := splitFoldersIntoContainsAndPrefix(opts.PageFolder) + + if len(containsFolders) > 0 { + sel.Include(sel.PageItems(containsFolders, opts.Page)) + } + + if len(prefixFolders) > 0 { + sel.Include(sel.PageItems(prefixFolders, opts.Page, selectors.PrefixMatch())) } } diff --git a/src/cli/utils/groups_test.go b/src/cli/utils/groups_test.go index bfdf18a6a..3b1d8859e 100644 --- a/src/cli/utils/groups_test.go +++ b/src/cli/utils/groups_test.go @@ -30,6 +30,7 @@ func (suite *GroupsUtilsSuite) TestIncludeGroupsRestoreDataSelectors() { containsOnly = []string{"contains"} prefixOnly = []string{"/prefix"} containsAndPrefix = []string{"contains", "/prefix"} + listNames = []string{"list-name1"} onlySlash = []string{string(path.PathSeparator)} ) @@ -91,29 +92,12 @@ func (suite *GroupsUtilsSuite) TestIncludeGroupsRestoreDataSelectors() { expectIncludeLen: 2, }, { - name: "list contains", + name: "list names", opts: utils.GroupsOpts{ - FileName: empty, - FolderPath: empty, - ListItem: empty, - ListFolder: containsOnly, + Lists: listNames, }, expectIncludeLen: 1, }, - { - name: "list prefixes", - opts: utils.GroupsOpts{ - ListFolder: prefixOnly, - }, - expectIncludeLen: 1, - }, - { - name: "list prefixes and contains", - opts: utils.GroupsOpts{ - ListFolder: containsAndPrefix, - }, - expectIncludeLen: 2, - }, { name: "Page Folder", opts: utils.GroupsOpts{ diff --git a/src/cli/utils/sharepoint.go b/src/cli/utils/sharepoint.go index bac8298b1..e6609f446 100644 --- a/src/cli/utils/sharepoint.go +++ b/src/cli/utils/sharepoint.go @@ -25,8 +25,7 @@ type SharePointOpts struct { FileModifiedAfter string FileModifiedBefore string - ListFolder []string - ListItem []string + Lists []string PageFolder []string Page []string @@ -37,6 +36,21 @@ type SharePointOpts struct { Populated flags.PopulatedFlags } +func (s SharePointOpts) GetFileTimeField(flag string) string { + switch flag { + case flags.FileCreatedAfterFN: + return s.FileCreatedAfter + case flags.FileCreatedBeforeFN: + return s.FileCreatedBefore + case flags.FileModifiedAfterFN: + return s.FileModifiedAfter + case flags.FileModifiedBeforeFN: + return s.FileModifiedBefore + default: + return "" + } +} + func MakeSharePointOpts(cmd *cobra.Command) SharePointOpts { return SharePointOpts{ SiteID: flags.SiteIDFV, @@ -50,8 +64,7 @@ func MakeSharePointOpts(cmd *cobra.Command) SharePointOpts { FileModifiedAfter: flags.FileModifiedAfterFV, FileModifiedBefore: flags.FileModifiedBeforeFV, - ListFolder: flags.ListFolderFV, - ListItem: flags.ListItemFV, + Lists: flags.ListFV, Page: flags.PageFV, PageFolder: flags.PageFolderFV, @@ -108,23 +121,7 @@ func ValidateSharePointRestoreFlags(backupID string, opts SharePointOpts) error } } - if _, ok := opts.Populated[flags.FileCreatedAfterFN]; ok && !IsValidTimeFormat(opts.FileCreatedAfter) { - return clues.New("invalid time format for " + flags.FileCreatedAfterFN) - } - - if _, ok := opts.Populated[flags.FileCreatedBeforeFN]; ok && !IsValidTimeFormat(opts.FileCreatedBefore) { - return clues.New("invalid time format for " + flags.FileCreatedBeforeFN) - } - - if _, ok := opts.Populated[flags.FileModifiedAfterFN]; ok && !IsValidTimeFormat(opts.FileModifiedAfter) { - return clues.New("invalid time format for " + flags.FileModifiedAfterFN) - } - - if _, ok := opts.Populated[flags.FileModifiedBeforeFN]; ok && !IsValidTimeFormat(opts.FileModifiedBefore) { - return clues.New("invalid time format for " + flags.FileModifiedBeforeFN) - } - - return nil + return validateCommonTimeFlags(opts) } // AddSharePointInfo adds the scope of the provided values to the selector's @@ -146,24 +143,24 @@ func AddSharePointInfo( func IncludeSharePointRestoreDataSelectors(ctx context.Context, opts SharePointOpts) *selectors.SharePointRestore { sites := opts.SiteID - lfp, lfn := len(opts.FolderPath), len(opts.FileName) - ls, lwu := len(opts.SiteID), len(opts.WebURL) - slp, sli := len(opts.ListFolder), len(opts.ListItem) - pf, pi := len(opts.PageFolder), len(opts.Page) + folderPaths, fileNames := len(opts.FolderPath), len(opts.FileName) + siteIDs, webUrls := len(opts.SiteID), len(opts.WebURL) + lists := len(opts.Lists) + pageFolders, pageItems := len(opts.PageFolder), len(opts.Page) - if ls == 0 { + if siteIDs == 0 { sites = selectors.Any() } sel := selectors.NewSharePointRestore(sites) - if lfp+lfn+lwu+slp+sli+pf+pi == 0 { + if folderPaths+fileNames+webUrls+lists+pageFolders+pageItems == 0 { sel.Include(sel.AllData()) return sel } - if lfp+lfn > 0 { - if lfn == 0 { + if folderPaths+fileNames > 0 { + if fileNames == 0 { opts.FileName = selectors.Any() } @@ -179,25 +176,14 @@ func IncludeSharePointRestoreDataSelectors(ctx context.Context, opts SharePointO } } - if slp+sli > 0 { - if sli == 0 { - opts.ListItem = selectors.Any() - } - - opts.ListFolder = trimFolderSlash(opts.ListFolder) - containsFolders, prefixFolders := splitFoldersIntoContainsAndPrefix(opts.ListFolder) - - if len(containsFolders) > 0 { - sel.Include(sel.ListItems(containsFolders, opts.ListItem)) - } - - if len(prefixFolders) > 0 { - sel.Include(sel.ListItems(prefixFolders, opts.ListItem, selectors.PrefixMatch())) - } + if lists > 0 { + opts.Lists = trimFolderSlash(opts.Lists) + sel.Include(sel.ListItems(opts.Lists, opts.Lists, selectors.StrictEqualMatch())) + sel.Configure(selectors.Config{OnlyMatchItemNames: true}) } - if pf+pi > 0 { - if pi == 0 { + if pageFolders+pageItems > 0 { + if pageItems == 0 { opts.Page = selectors.Any() } @@ -213,7 +199,7 @@ func IncludeSharePointRestoreDataSelectors(ctx context.Context, opts SharePointO } } - if lwu > 0 { + if webUrls > 0 { urls := make([]string, 0, len(opts.WebURL)) for _, wu := range opts.WebURL { diff --git a/src/cli/utils/sharepoint_test.go b/src/cli/utils/sharepoint_test.go index 2523685d7..030d8a62d 100644 --- a/src/cli/utils/sharepoint_test.go +++ b/src/cli/utils/sharepoint_test.go @@ -31,6 +31,7 @@ func (suite *SharePointUtilsSuite) TestIncludeSharePointRestoreDataSelectors() { multi = []string{"more", "than", "one"} containsOnly = []string{"contains"} prefixOnly = []string{"/prefix"} + listNames = []string{"list-name1"} containsAndPrefix = []string{"contains", "/prefix"} onlySlash = []string{string(path.PathSeparator)} ) @@ -60,8 +61,7 @@ func (suite *SharePointUtilsSuite) TestIncludeSharePointRestoreDataSelectors() { opts: utils.SharePointOpts{ FileName: single, FolderPath: single, - ListItem: single, - ListFolder: single, + Lists: single, SiteID: single, WebURL: single, }, @@ -108,31 +108,12 @@ func (suite *SharePointUtilsSuite) TestIncludeSharePointRestoreDataSelectors() { expectIncludeLen: 2, }, { - name: "list contains", + name: "list names", opts: utils.SharePointOpts{ - FileName: empty, - FolderPath: empty, - ListItem: empty, - ListFolder: containsOnly, - SiteID: empty, - WebURL: empty, + Lists: listNames, }, expectIncludeLen: 1, }, - { - name: "list prefixes", - opts: utils.SharePointOpts{ - ListFolder: prefixOnly, - }, - expectIncludeLen: 1, - }, - { - name: "list prefixes and contains", - opts: utils.SharePointOpts{ - ListFolder: containsAndPrefix, - }, - expectIncludeLen: 2, - }, { name: "weburl contains", opts: utils.SharePointOpts{ diff --git a/src/pkg/selectors/sharepoint.go b/src/pkg/selectors/sharepoint.go index 0cd60d257..8b5951aef 100644 --- a/src/pkg/selectors/sharepoint.go +++ b/src/pkg/selectors/sharepoint.go @@ -263,12 +263,9 @@ func (s *sharePoint) AllData() []SharePointScope { // If any slice contains selectors.None, that slice is reduced to [selectors.None] // Any empty slice defaults to [selectors.None] func (s *sharePoint) Lists(lists []string, opts ...option) []SharePointScope { - var ( - scopes = []SharePointScope{} - os = append([]option{pathComparator()}, opts...) - ) + scopes := []SharePointScope{} - scopes = append(scopes, makeScope[SharePointScope](SharePointList, lists, os...)) + scopes = append(scopes, makeScope[SharePointScope](SharePointList, lists, opts...)) return scopes } @@ -526,6 +523,7 @@ func (c sharePointCategory) pathValues( folderCat, itemCat categorizer itemID string rFld string + itemName string ) switch c { @@ -536,14 +534,22 @@ func (c sharePointCategory) pathValues( folderCat, itemCat = SharePointLibraryFolder, SharePointLibraryItem rFld = ent.SharePoint.ParentPath + itemName = ent.ItemInfo.SharePoint.ItemName case SharePointList, SharePointListItem: folderCat, itemCat = SharePointList, SharePointListItem + rFld = ent.LocationRef + if cfg.OnlyMatchItemNames { + rFld = ent.ItemInfo.SharePoint.List.Name + } + + itemName = ent.ItemInfo.SharePoint.List.Name case SharePointPage, SharePointPageFolder: folderCat, itemCat = SharePointPageFolder, SharePointPage rFld = ent.LocationRef + itemName = ent.ItemInfo.SharePoint.ItemName default: return nil, clues.New("unrecognized sharePointCategory").With("category", c) @@ -555,7 +561,7 @@ func (c sharePointCategory) pathValues( } if cfg.OnlyMatchItemNames { - item = ent.ItemInfo.SharePoint.ItemName + item = itemName } result := map[categorizer][]string{ @@ -640,7 +646,11 @@ func (s SharePointScope) set(cat sharePointCategory, v []string, opts ...option) os := []option{} switch cat { - case SharePointLibraryFolder, SharePointList, SharePointPage: + // SharePointList does not apply here because: + // 1.there is no nested folders -> there cannot be lists within other lists + // 2. list itself is the item -> so container and item are the same + // since there is no path involved here, we do not need any path filters. + case SharePointLibraryFolder, SharePointPage: os = append(os, pathComparator()) } diff --git a/src/pkg/selectors/sharepoint_test.go b/src/pkg/selectors/sharepoint_test.go index 7474afcd5..30eb3abef 100644 --- a/src/pkg/selectors/sharepoint_test.go +++ b/src/pkg/selectors/sharepoint_test.go @@ -170,6 +170,8 @@ func (suite *SharePointSelectorSuite) TestSharePointRestore_Reduce() { itemElems3 = []string{"folderD", "folderE"} pairAC = "folderA/folderC" pairGH = "folderG/folderH" + listID1 = "list1" + listID2 = "list2" item = toRR( path.LibrariesCategory, "sid", @@ -187,6 +189,8 @@ func (suite *SharePointSelectorSuite) TestSharePointRestore_Reduce() { "item3") item4 = stubRepoRef(path.SharePointService, path.PagesCategory, "sid", pairGH, "item4") item5 = stubRepoRef(path.SharePointService, path.PagesCategory, "sid", pairGH, "item5") + list1 = stubRepoRef(path.SharePointService, path.ListsCategory, "sid", listID1, listID1) + list2 = stubRepoRef(path.SharePointService, path.ListsCategory, "sid", listID2, listID2) ) deets := &details.Details{ @@ -252,6 +256,34 @@ func (suite *SharePointSelectorSuite) TestSharePointRestore_Reduce() { }, }, }, + { + RepoRef: list1, + LocationRef: listID1, + ItemRef: "list1", + ItemInfo: details.ItemInfo{ + SharePoint: &details.SharePointInfo{ + ItemType: details.SharePointList, + List: &details.ListInfo{ + Name: "listName1", + }, + ParentPath: strings.Join([]string{listID1}, "/"), + }, + }, + }, + { + RepoRef: list2, + LocationRef: listID2, + ItemRef: "list2", + ItemInfo: details.ItemInfo{ + SharePoint: &details.SharePointInfo{ + ItemType: details.SharePointList, + List: &details.ListInfo{ + Name: "listName2", + }, + ParentPath: strings.Join([]string{listID2}, "/"), + }, + }, + }, }, }, } @@ -273,7 +305,7 @@ func (suite *SharePointSelectorSuite) TestSharePointRestore_Reduce() { odr.Include(odr.AllData()) return odr }, - expect: arr(item, item2, item3, item4, item5), + expect: arr(item, item2, item3, item4, item5, list1, list2), }, { name: "only match item", @@ -331,6 +363,25 @@ func (suite *SharePointSelectorSuite) TestSharePointRestore_Reduce() { }, expect: arr(item4, item5), }, + { + name: "lists match by id", + makeSelector: func() *SharePointRestore { + odr := NewSharePointRestore([]string{"sid"}) + odr.Include(odr.Lists([]string{listID1, listID2})) + return odr + }, + expect: arr(list1, list2), + }, + { + name: "lists match by name", + makeSelector: func() *SharePointRestore { + odr := NewSharePointRestore([]string{"sid"}) + odr.Include(odr.ListItems(Any(), []string{"listName2"})) + return odr + }, + expect: arr(list2), + cfg: Config{OnlyMatchItemNames: true}, + }, } for _, test := range table { suite.Run(test.name, func() { @@ -361,7 +412,7 @@ func (suite *SharePointSelectorSuite) TestSharePointCategory_PathValues() { "dir2.d", itemID, } - elems = []string{"dir1", "dir2", itemID} + elems = []string{itemID, itemID} ) table := []struct { @@ -401,9 +452,9 @@ func (suite *SharePointSelectorSuite) TestSharePointCategory_PathValues() { name: "SharePoint Lists", sc: SharePointListItem, pathElems: elems, - locRef: "dir1/dir2", + locRef: itemID, expected: map[categorizer][]string{ - SharePointList: {"dir1/dir2"}, + SharePointList: {itemID}, SharePointListItem: {itemID, shortRef}, }, cfg: Config{}, @@ -423,17 +474,28 @@ func (suite *SharePointSelectorSuite) TestSharePointCategory_PathValues() { test.pathElems...) require.NoError(t, err, clues.ToCore(err)) + di := details.ItemInfo{} + + if test.sc.PathType() == path.LibrariesCategory { + di.SharePoint = &details.SharePointInfo{ + ItemName: itemName, + ParentPath: test.parentPath, + } + } else if test.sc.PathType() == path.ListsCategory { + di.SharePoint = &details.SharePointInfo{ + List: &details.ListInfo{ + Name: itemName, + }, + ParentPath: test.parentPath, + } + } + ent := details.Entry{ RepoRef: itemPath.String(), ShortRef: shortRef, ItemRef: itemPath.Item(), LocationRef: test.locRef, - ItemInfo: details.ItemInfo{ - SharePoint: &details.SharePointInfo{ - ItemName: itemName, - ParentPath: test.parentPath, - }, - }, + ItemInfo: di, } pv, err := test.sc.pathValues(itemPath, ent, test.cfg)