corso/src/cli/utils/sharepoint.go
Hitesh Pattanayak e16d4c5bd9
select list by name while export and restore (#4999)
selects list by name while export and restore

#### Does this PR need a docs update or release note?
- [x]  No

#### Type of change

<!--- Please check the type of change your PR introduces: --->
- [x] 🧹 Tech Debt/Cleanup

#### Issue(s)
#4754 
#### Test Plan

<!-- How will this be tested prior to merging.-->
- [x] 💪 Manual
- [x]  Unit test
- [x] 💚 E2E
2024-01-12 15:54:58 +00:00

242 lines
6.3 KiB
Go

package utils
import (
"context"
"net/url"
"strings"
"github.com/alcionai/clues"
"github.com/spf13/cobra"
"github.com/alcionai/corso/src/cli/flags"
"github.com/alcionai/corso/src/pkg/logger"
"github.com/alcionai/corso/src/pkg/selectors"
)
type SharePointOpts struct {
SiteID []string
WebURL []string
Library string
FileName []string // for libraries, to duplicate onedrive interface
FolderPath []string // for libraries, to duplicate onedrive interface
FileCreatedAfter string
FileCreatedBefore string
FileModifiedAfter string
FileModifiedBefore string
Lists []string
PageFolder []string
Page []string
RestoreCfg RestoreCfgOpts
ExportCfg ExportCfgOpts
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,
WebURL: flags.WebURLFV,
Library: flags.LibraryFV,
FileName: flags.FileNameFV,
FolderPath: flags.FolderPathFV,
FileCreatedAfter: flags.FileCreatedAfterFV,
FileCreatedBefore: flags.FileCreatedBeforeFV,
FileModifiedAfter: flags.FileModifiedAfterFV,
FileModifiedBefore: flags.FileModifiedBeforeFV,
Lists: flags.ListFV,
Page: flags.PageFV,
PageFolder: flags.PageFolderFV,
RestoreCfg: makeRestoreCfgOpts(cmd),
ExportCfg: makeExportCfgOpts(cmd),
// populated contains the list of flags that appear in the
// command, according to pflags. Use this to differentiate
// between an "empty" and a "missing" value.
Populated: flags.GetPopulatedFlags(cmd),
}
}
func SharePointAllowedCategories() map[string]struct{} {
return map[string]struct{}{
flags.DataLibraries: {},
// flags.DataLists: {}, [TODO]: uncomment when lists are enabled
}
}
func AddCategories(sel *selectors.SharePointBackup, cats []string) *selectors.SharePointBackup {
if len(cats) == 0 {
// backup of sharepoint lists not enabled yet
// sel.Include(sel.LibraryFolders(selectors.Any()), sel.Lists(selectors.Any()))
sel.Include(sel.LibraryFolders(selectors.Any()))
}
for _, d := range cats {
switch d {
// backup of sharepoint lists not enabled yet
// case flags.DataLists:
// sel.Include(sel.Lists(selectors.Any()))
case flags.DataLibraries:
sel.Include(sel.LibraryFolders(selectors.Any()))
}
}
return sel
}
// ValidateSharePointRestoreFlags checks common flags for correctness and interdependencies
func ValidateSharePointRestoreFlags(backupID string, opts SharePointOpts) error {
if len(backupID) == 0 {
return clues.New("a backup ID is required")
}
// ensure url can parse all weburls provided by --site.
if _, ok := opts.Populated[flags.SiteFN]; ok {
for _, wu := range opts.WebURL {
if _, err := url.Parse(wu); err != nil {
return clues.New("invalid site url: " + wu)
}
}
}
return validateCommonTimeFlags(opts)
}
// AddSharePointInfo adds the scope of the provided values to the selector's
// filter set
func AddSharePointInfo(
sel *selectors.SharePointRestore,
v string,
f func(string) []selectors.SharePointScope,
) {
if len(v) == 0 {
return
}
sel.Filter(f(v))
}
// IncludeSharePointRestoreDataSelectors builds the common data-selector
// inclusions for SharePoint commands.
func IncludeSharePointRestoreDataSelectors(ctx context.Context, opts SharePointOpts) *selectors.SharePointRestore {
sites := opts.SiteID
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 siteIDs == 0 {
sites = selectors.Any()
}
sel := selectors.NewSharePointRestore(sites)
if folderPaths+fileNames+webUrls+lists+pageFolders+pageItems == 0 {
sel.Include(sel.AllData())
return sel
}
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 lists > 0 {
opts.Lists = trimFolderSlash(opts.Lists)
sel.Include(sel.ListItems(opts.Lists, opts.Lists, selectors.StrictEqualMatch()))
sel.Configure(selectors.Config{OnlyMatchItemNames: true})
}
if pageFolders+pageItems > 0 {
if pageItems == 0 {
opts.Page = selectors.Any()
}
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()))
}
}
if webUrls > 0 {
urls := make([]string, 0, len(opts.WebURL))
for _, wu := range opts.WebURL {
// for normalization, ensure the site has a https:// prefix.
wu = strings.TrimPrefix(wu, "https://")
wu = strings.TrimPrefix(wu, "http://")
// don't add a prefix to path-only values
if len(wu) > 0 && wu != "*" && !strings.HasPrefix(wu, "/") {
wu = "https://" + wu
}
u, err := url.Parse(wu)
if err != nil {
// shouldn't be possible to err, if we called validation first.
logger.Ctx(ctx).With("web_url", wu).Error("malformed web url")
continue
}
urls = append(urls, u.String())
}
sel.Include(sel.WebURL(urls))
}
return sel
}
// FilterSharePointRestoreInfoSelectors builds the common info-selector filters.
func FilterSharePointRestoreInfoSelectors(
sel *selectors.SharePointRestore,
opts SharePointOpts,
) {
AddSharePointInfo(sel, opts.Library, sel.Library)
AddSharePointInfo(sel, opts.FileCreatedAfter, sel.CreatedAfter)
AddSharePointInfo(sel, opts.FileCreatedBefore, sel.CreatedBefore)
AddSharePointInfo(sel, opts.FileModifiedAfter, sel.ModifiedAfter)
AddSharePointInfo(sel, opts.FileModifiedBefore, sel.ModifiedBefore)
}