adds usage of the export format validation check to export cli commands. Also moves the restore cfg validation check out of the service flags validation checks and into the generic restore runner for better separation of concerns. --- #### Does this PR need a docs update or release note? - [x] ⛔ No #### Type of change - [x] 🌻 Feature #### Issue(s) * #3988 #### Test Plan - [x] ⚡ Unit test - [x] 💚 E2E
229 lines
6.1 KiB
Go
229 lines
6.1 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
|
|
|
|
ListFolder []string
|
|
ListItem []string
|
|
|
|
PageFolder []string
|
|
Page []string
|
|
|
|
RestoreCfg RestoreCfgOpts
|
|
ExportCfg ExportCfgOpts
|
|
|
|
Populated flags.PopulatedFlags
|
|
}
|
|
|
|
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,
|
|
|
|
ListFolder: flags.ListFolderFV,
|
|
ListItem: flags.ListItemFV,
|
|
|
|
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),
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// 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
|
|
|
|
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)
|
|
|
|
if ls == 0 {
|
|
sites = selectors.Any()
|
|
}
|
|
|
|
sel := selectors.NewSharePointRestore(sites)
|
|
|
|
if lfp+lfn+lwu+slp+sli+pf+pi == 0 {
|
|
sel.Include(sel.AllData())
|
|
return sel
|
|
}
|
|
|
|
if lfp+lfn > 0 {
|
|
if lfn == 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 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 pf+pi > 0 {
|
|
if pi == 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 lwu > 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)
|
|
}
|