diff --git a/src/cli/backup/exchange.go b/src/cli/backup/exchange.go index b99fcc1ef..6f4892e4d 100644 --- a/src/cli/backup/exchange.go +++ b/src/cli/backup/exchange.go @@ -453,8 +453,7 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error { return nil } -// runDetailsExchangeCmd actually performs the lookup in backup details. Assumes -// len(backupID) > 0. +// runDetailsExchangeCmd actually performs the lookup in backup details. func runDetailsExchangeCmd( ctx context.Context, r repository.BackupGetter, diff --git a/src/cli/backup/onedrive.go b/src/cli/backup/onedrive.go index 59a4c4d37..2a6b8e1f2 100644 --- a/src/cli/backup/onedrive.go +++ b/src/cli/backup/onedrive.go @@ -90,7 +90,7 @@ func addOneDriveCommands(parent *cobra.Command) *cobra.Command { c, fs = utils.AddCommand(parent, oneDriveListCmd()) fs.StringVar(&backupID, - "backup", "", + utils.BackupFN, "", "ID of the backup to retrieve.") case detailsCommand: @@ -344,8 +344,7 @@ func detailsOneDriveCmd(cmd *cobra.Command, args []string) error { return nil } -// runDetailsOneDriveCmd actually performs the lookup in backup details. Assumes -// len(backupID) > 0. +// runDetailsOneDriveCmd actually performs the lookup in backup details. func runDetailsOneDriveCmd( ctx context.Context, r repository.BackupGetter, diff --git a/src/cli/backup/sharepoint.go b/src/cli/backup/sharepoint.go index e56d3e794..c8a638968 100644 --- a/src/cli/backup/sharepoint.go +++ b/src/cli/backup/sharepoint.go @@ -1,6 +1,8 @@ package backup import ( + "context" + "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -12,6 +14,7 @@ import ( "github.com/alcionai/corso/src/internal/kopia" "github.com/alcionai/corso/src/internal/model" "github.com/alcionai/corso/src/pkg/backup" + "github.com/alcionai/corso/src/pkg/backup/details" "github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/repository" "github.com/alcionai/corso/src/pkg/selectors" @@ -23,7 +26,9 @@ import ( // ------------------------------------------------------------------------------------------------ var ( - site []string + site []string + libraryPaths []string + libraryItems []string sharepointData []string ) @@ -33,10 +38,10 @@ const ( ) const ( - sharePointServiceCommand = "sharepoint" - sharePointServiceCommandCreateUseSuffix = "--site | '" + utils.Wildcard + "'" - sharePointServiceCommandDeleteUseSuffix = "--backup " - // sharePointServiceCommandDetailsUseSuffix = "--backup " + sharePointServiceCommand = "sharepoint" + sharePointServiceCommandCreateUseSuffix = "--site | '" + utils.Wildcard + "'" + sharePointServiceCommandDeleteUseSuffix = "--backup " + sharePointServiceCommandDetailsUseSuffix = "--backup " ) const ( @@ -54,9 +59,9 @@ corso backup create sharepoint --site '*'` sharePointServiceCommandDeleteExamples = `# Delete SharePoint backup with ID 1234abcd-12ab-cd34-56de-1234abcd corso backup delete sharepoint --backup 1234abcd-12ab-cd34-56de-1234abcd` -// sharePointServiceCommandDetailsExamples = `# Explore 's files from backup 1234abcd-12ab-cd34-56de-1234abcd -// -// corso backup details sharepoint --backup 1234abcd-12ab-cd34-56de-1234abcd --site ` + sharePointServiceCommandDetailsExamples = `# Explore 's files from backup 1234abcd-12ab-cd34-56de-1234abcd + +corso backup details sharepoint --backup 1234abcd-12ab-cd34-56de-1234abcd --site ` ) // called by backup.go to map parent subcommands to provider-specific handling. @@ -80,18 +85,45 @@ func addSharePointCommands(parent *cobra.Command) *cobra.Command { fs.StringSliceVar( &sharepointData, utils.DataFN, nil, - "Select one or more types of data to backup: "+dataLibraries) + "Select one or more types of data to backup: "+dataLibraries+".") options.AddOperationFlags(c) case listCommand: c, fs = utils.AddCommand(parent, sharePointListCmd(), utils.HideCommand()) fs.StringVar(&backupID, - "backup", "", + utils.BackupFN, "", "ID of the backup to retrieve.") - // case detailsCommand: - // c, fs = utils.AddCommand(parent, sharePointDetailsCmd()) + case detailsCommand: + c, fs = utils.AddCommand(parent, sharePointDetailsCmd()) + + c.Use = c.Use + " " + sharePointServiceCommandDetailsUseSuffix + c.Example = sharePointServiceCommandDetailsExamples + + fs.StringVar(&backupID, + utils.BackupFN, "", + "ID of the backup to retrieve.") + cobra.CheckErr(c.MarkFlagRequired(utils.BackupFN)) + + // sharepoint hierarchy flags + + fs.StringSliceVar( + &libraryPaths, + utils.LibraryFN, nil, + "Select backup details by Library name.") + + fs.StringSliceVar( + &libraryItems, + utils.LibraryItemFN, nil, + "Select backup details by library item name or ID.") + + // info flags + + // fs.StringVar( + // &fileCreatedAfter, + // utils.FileCreatedAfterFN, "", + // "Select backup details for items created after this datetime.") case deleteCommand: c, fs = utils.AddCommand(parent, sharePointDeleteCmd(), utils.HideCommand()) @@ -282,3 +314,93 @@ func deleteSharePointCmd(cmd *cobra.Command, args []string) error { return nil } + +// ------------------------------------------------------------------------------------------------ +// backup details +// ------------------------------------------------------------------------------------------------ + +// `corso backup details onedrive [...]` +func sharePointDetailsCmd() *cobra.Command { + return &cobra.Command{ + Use: sharePointServiceCommand, + Short: "Shows the details of a M365 SharePoint service backup", + RunE: detailsSharePointCmd, + Args: cobra.NoArgs, + Example: sharePointServiceCommandDetailsExamples, + } +} + +// lists the history of backup operations +func detailsSharePointCmd(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + if utils.HasNoFlagsAndShownHelp(cmd) { + return nil + } + + s, acct, err := config.GetStorageAndAccount(ctx, true, nil) + if err != nil { + return Only(ctx, err) + } + + r, err := repository.Connect(ctx, acct, s, options.Control()) + if err != nil { + return Only(ctx, errors.Wrapf(err, "Failed to connect to the %s repository", s.Provider)) + } + + defer utils.CloseRepo(ctx, r) + + opts := utils.SharePointOpts{ + Sites: site, + LibraryPaths: libraryPaths, + LibraryItems: libraryItems, + + Populated: utils.GetPopulatedFlags(cmd), + } + + ds, err := runDetailsSharePointCmd(ctx, r, backupID, opts) + if err != nil { + return Only(ctx, err) + } + + if len(ds.Entries) == 0 { + Info(ctx, selectors.ErrorNoMatchingItems) + return nil + } + + ds.PrintEntries(ctx) + + return nil +} + +// runDetailsSharePointCmd actually performs the lookup in backup details. +func runDetailsSharePointCmd( + ctx context.Context, + r repository.BackupGetter, + backupID string, + opts utils.SharePointOpts, +) (*details.Details, error) { + if err := utils.ValidateSharePointRestoreFlags(backupID, opts); err != nil { + return nil, err + } + + d, _, err := r.BackupDetails(ctx, backupID) + if err != nil { + if errors.Is(err, kopia.ErrNotFound) { + return nil, errors.Errorf("no backup exists with the id %s", backupID) + } + + return nil, errors.Wrap(err, "Failed to get backup details in the repository") + } + + sel := selectors.NewSharePointRestore() + utils.IncludeSharePointRestoreDataSelectors(sel, opts) + utils.FilterSharePointRestoreInfoSelectors(sel, opts) + + // if no selector flags were specified, get all data in the service. + if len(sel.Scopes()) == 0 { + sel.Include(sel.Sites(selectors.Any())) + } + + return sel.Reduce(ctx, d), nil +} diff --git a/src/cli/backup/sharepoint_test.go b/src/cli/backup/sharepoint_test.go index 673e7def9..1eeeaa976 100644 --- a/src/cli/backup/sharepoint_test.go +++ b/src/cli/backup/sharepoint_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" + "github.com/alcionai/corso/src/cli/utils/testdata" "github.com/alcionai/corso/src/internal/tester" ) @@ -37,10 +38,10 @@ func (suite *SharePointSuite) TestAddSharePointCommands() { "list sharepoint", listCommand, expectUse, sharePointListCmd().Short, listSharePointCmd, }, - // { - // "details sharepoint", detailsCommand, expectUse + " " + sharePointServiceCommandDetailsUseSuffix, - // sharePointDetailsCmd().Short, detailsSharePointCmd, - // }, + { + "details sharepoint", detailsCommand, expectUse + " " + sharePointServiceCommandDetailsUseSuffix, + sharePointDetailsCmd().Short, detailsSharePointCmd, + }, { "delete sharepoint", deleteCommand, expectUse + " " + sharePointServiceCommandDeleteUseSuffix, sharePointDeleteCmd().Short, deleteSharePointCmd, @@ -87,40 +88,40 @@ func (suite *SharePointSuite) TestValidateSharePointBackupCreateFlags() { } } -// func (suite *SharePointSuite) TestSharePointBackupDetailsSelectors() { -// ctx, flush := tester.NewContext() -// defer flush() +func (suite *SharePointSuite) TestSharePointBackupDetailsSelectors() { + ctx, flush := tester.NewContext() + defer flush() -// for _, test := range testdata.SharePointOptionDetailLookups { -// suite.T().Run(test.Name, func(t *testing.T) { -// output, err := runDetailsSharePointCmd( -// ctx, -// test.BackupGetter, -// "backup-ID", -// test.Opts, -// ) -// assert.NoError(t, err) + for _, test := range testdata.SharePointOptionDetailLookups { + suite.T().Run(test.Name, func(t *testing.T) { + output, err := runDetailsSharePointCmd( + ctx, + test.BackupGetter, + "backup-ID", + test.Opts, + ) + assert.NoError(t, err) -// assert.ElementsMatch(t, test.Expected, output.Entries) -// }) -// } -// } + assert.ElementsMatch(t, test.Expected, output.Entries) + }) + } +} -// func (suite *SharePointSuite) TestSharePointBackupDetailsSelectorsBadFormats() { -// ctx, flush := tester.NewContext() -// defer flush() +func (suite *SharePointSuite) TestSharePointBackupDetailsSelectorsBadFormats() { + ctx, flush := tester.NewContext() + defer flush() -// for _, test := range testdata.BadSharePointOptionsFormats { -// suite.T().Run(test.Name, func(t *testing.T) { -// output, err := runDetailsSharePointCmd( -// ctx, -// test.BackupGetter, -// "backup-ID", -// test.Opts, -// ) + for _, test := range testdata.BadSharePointOptionsFormats { + suite.T().Run(test.Name, func(t *testing.T) { + output, err := runDetailsSharePointCmd( + ctx, + test.BackupGetter, + "backup-ID", + test.Opts, + ) -// assert.Error(t, err) -// assert.Empty(t, output) -// }) -// } -// } + assert.Error(t, err) + assert.Empty(t, output) + }) + } +} diff --git a/src/cli/utils/onedrive_test.go b/src/cli/utils/onedrive_test.go index 2866c1cb3..a0054f0fa 100644 --- a/src/cli/utils/onedrive_test.go +++ b/src/cli/utils/onedrive_test.go @@ -4,12 +4,21 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" "github.com/alcionai/corso/src/cli/utils" "github.com/alcionai/corso/src/pkg/selectors" ) -func (suite *ExchangeUtilsSuite) TestIncludeOneDriveRestoreDataSelectors() { +type OneDriveUtilsSuite struct { + suite.Suite +} + +func TestOneDriveUtilsSuite(t *testing.T) { + suite.Run(t, new(OneDriveUtilsSuite)) +} + +func (suite *OneDriveUtilsSuite) TestIncludeOneDriveRestoreDataSelectors() { var ( empty = []string{} single = []string{"single"} diff --git a/src/cli/utils/sharepoint.go b/src/cli/utils/sharepoint.go new file mode 100644 index 000000000..1d4eb719e --- /dev/null +++ b/src/cli/utils/sharepoint.go @@ -0,0 +1,97 @@ +package utils + +import ( + "errors" + + "github.com/alcionai/corso/src/pkg/selectors" +) + +const ( + LibraryItemFN = "library-item" + LibraryFN = "library" +) + +type SharePointOpts struct { + Sites []string + LibraryItems []string + LibraryPaths []string + + Populated PopulatedFlags +} + +// ValidateSharePointRestoreFlags checks common flags for correctness and interdependencies +func ValidateSharePointRestoreFlags(backupID string, opts SharePointOpts) error { + if len(backupID) == 0 { + return errors.New("a backup ID is required") + } + + // if _, ok := opts.Populated[FileCreatedAfterFN]; ok && !IsValidTimeFormat(opts.FileCreatedAfter) { + // return errors.New("invalid time format for created-after") + // } + + return nil +} + +// AddSharePointFilter adds the scope of the provided values to the selector's +// filter set +func AddSharePointFilter( + 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( + sel *selectors.SharePointRestore, + opts SharePointOpts, +) { + lp, ln := len(opts.LibraryPaths), len(opts.LibraryItems) + + // only use the inclusion if either a path or item name + // is specified + if lp+ln == 0 { + return + } + + if len(opts.Sites) == 0 { + opts.Sites = selectors.Any() + } + + // either scope the request to a set of sites + if lp+ln == 0 { + sel.Include(sel.Sites(opts.Sites)) + + return + } + + opts.LibraryPaths = trimFolderSlash(opts.LibraryPaths) + + if ln == 0 { + opts.LibraryItems = selectors.Any() + } + + containsFolders, prefixFolders := splitFoldersIntoContainsAndPrefix(opts.LibraryPaths) + + if len(containsFolders) > 0 { + sel.Include(sel.LibraryItems(opts.Sites, containsFolders, opts.LibraryItems)) + } + + if len(prefixFolders) > 0 { + sel.Include(sel.LibraryItems(opts.Sites, prefixFolders, opts.LibraryItems, selectors.PrefixMatch())) + } +} + +// FilterSharePointRestoreInfoSelectors builds the common info-selector filters. +func FilterSharePointRestoreInfoSelectors( + sel *selectors.SharePointRestore, + opts SharePointOpts, +) { + // AddSharePointFilter(sel, opts.FileCreatedAfter, sel.CreatedAfter) +} diff --git a/src/cli/utils/sharepoint_test.go b/src/cli/utils/sharepoint_test.go new file mode 100644 index 000000000..f57ec67f0 --- /dev/null +++ b/src/cli/utils/sharepoint_test.go @@ -0,0 +1,99 @@ +package utils_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + + "github.com/alcionai/corso/src/cli/utils" + "github.com/alcionai/corso/src/pkg/selectors" +) + +type SharePointUtilsSuite struct { + suite.Suite +} + +func TestSharePointUtilsSuite(t *testing.T) { + suite.Run(t, new(SharePointUtilsSuite)) +} + +func (suite *ExchangeUtilsSuite) TestIncludeSharePointRestoreDataSelectors() { + var ( + empty = []string{} + single = []string{"single"} + multi = []string{"more", "than", "one"} + containsOnly = []string{"contains"} + prefixOnly = []string{"/prefix"} + containsAndPrefix = []string{"contains", "/prefix"} + ) + + table := []struct { + name string + opts utils.SharePointOpts + expectIncludeLen int + }{ + { + name: "no inputs", + opts: utils.SharePointOpts{ + Sites: empty, + LibraryPaths: empty, + LibraryItems: empty, + }, + expectIncludeLen: 0, + }, + { + name: "single inputs", + opts: utils.SharePointOpts{ + Sites: single, + LibraryPaths: single, + LibraryItems: single, + }, + expectIncludeLen: 1, + }, + { + name: "multi inputs", + opts: utils.SharePointOpts{ + Sites: multi, + LibraryPaths: multi, + LibraryItems: multi, + }, + expectIncludeLen: 1, + }, + { + name: "library contains", + opts: utils.SharePointOpts{ + Sites: empty, + LibraryPaths: containsOnly, + LibraryItems: empty, + }, + expectIncludeLen: 1, + }, + { + name: "library prefixes", + opts: utils.SharePointOpts{ + Sites: empty, + LibraryPaths: prefixOnly, + LibraryItems: empty, + }, + expectIncludeLen: 1, + }, + { + name: "library prefixes and contains", + opts: utils.SharePointOpts{ + Sites: empty, + LibraryPaths: containsAndPrefix, + LibraryItems: empty, + }, + expectIncludeLen: 2, + }, + } + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + sel := selectors.NewSharePointRestore() + // no return, mutates sel as a side effect + utils.IncludeSharePointRestoreDataSelectors(sel, test.opts) + assert.Len(t, sel.Includes, test.expectIncludeLen) + }) + } +} diff --git a/src/cli/utils/testdata/opts.go b/src/cli/utils/testdata/opts.go index 74de0f426..2fdab60b6 100644 --- a/src/cli/utils/testdata/opts.go +++ b/src/cli/utils/testdata/opts.go @@ -395,6 +395,94 @@ var ( } ) +type SharePointOptionsTest struct { + Name string + Opts utils.SharePointOpts + BackupGetter *MockBackupGetter + Expected []details.DetailsEntry +} + +var ( + // BadSharePointOptionsFormats contains SharePointOpts with flags that should + // cause errors about the format of the input flag. Mocks are configured to + // allow the system to run if it doesn't throw an error on formatting. + BadSharePointOptionsFormats = []SharePointOptionsTest{ + // { + // Name: "BadFileCreatedBefore", + // Opts: utils.OneDriveOpts{ + // FileCreatedBefore: "foo", + // Populated: utils.PopulatedFlags{ + // utils.FileCreatedBeforeFN: struct{}{}, + // }, + // }, + // }, + // { + // Name: "EmptyFileCreatedBefore", + // Opts: utils.OneDriveOpts{ + // FileCreatedBefore: "", + // Populated: utils.PopulatedFlags{ + // utils.FileCreatedBeforeFN: struct{}{}, + // }, + // }, + // }, + } + + // SharePointOptionDetailLookups contains flag inputs and expected results for + // some choice input patterns. This set is not exhaustive. All inputs and + // outputs are according to the data laid out in selectors/testdata. Mocks are + // configured to return the full dataset listed in selectors/testdata. + SharePointOptionDetailLookups = []SharePointOptionsTest{ + { + Name: "AllLibraryItems", + Expected: testdata.SharePointLibraryItems, + Opts: utils.SharePointOpts{ + LibraryPaths: selectors.Any(), + }, + }, + { + Name: "FolderPrefixMatch", + Expected: testdata.SharePointLibraryItems, + Opts: utils.SharePointOpts{ + LibraryPaths: []string{testdata.SharePointLibraryFolder}, + }, + }, + { + Name: "FolderPrefixMatchTrailingSlash", + Expected: testdata.SharePointLibraryItems, + Opts: utils.SharePointOpts{ + LibraryPaths: []string{testdata.SharePointLibraryFolder + "/"}, + }, + }, + { + Name: "FolderPrefixMatchTrailingSlash", + Expected: testdata.SharePointLibraryItems, + Opts: utils.SharePointOpts{ + LibraryPaths: []string{testdata.SharePointLibraryFolder + "/"}, + }, + }, + { + Name: "ShortRef", + Expected: []details.DetailsEntry{ + testdata.SharePointLibraryItems[0], + testdata.SharePointLibraryItems[1], + }, + Opts: utils.SharePointOpts{ + LibraryItems: []string{ + testdata.SharePointLibraryItems[0].ShortRef, + testdata.SharePointLibraryItems[1].ShortRef, + }, + }, + }, + // { + // Name: "CreatedBefore", + // Expected: []details.DetailsEntry{testdata.SharePointLibraryItems[1]}, + // Opts: utils.SharePointOpts{ + // FileCreatedBefore: common.FormatTime(testdata.Time1.Add(time.Second)), + // }, + // }, + } +) + // MockBackupGetter implements the repo.BackupGetter interface and returns // (selectors/testdata.GetDetailsSet(), nil, nil) when BackupDetails is called // on the nil instance. If an instance is given or Backups is called returns an diff --git a/src/pkg/backup/details/details.go b/src/pkg/backup/details/details.go index 5ccd13cda..5c713f714 100644 --- a/src/pkg/backup/details/details.go +++ b/src/pkg/backup/details/details.go @@ -352,12 +352,14 @@ func (i ExchangeInfo) Values() []string { // SharePointInfo describes a sharepoint item type SharePointInfo struct { - ItemType ItemType `json:"itemType,omitempty"` - ItemName string `json:"itemName,omitempty"` - Created time.Time `json:"created,omitempty"` - Modified time.Time `josn:"modified,omitempty"` - WebURL string `json:"webUrl,omitempty"` - Size int64 `json:"size,omitempty"` + Created time.Time `json:"created,omitempty"` + ItemName string `json:"itemName,omitempty"` + ItemType ItemType `json:"itemType,omitempty"` + Modified time.Time `josn:"modified,omitempty"` + Owner string `json:"owner,omitempty"` + ParentPath string `json:"parentPath"` + Size int64 `json:"size,omitempty"` + WebURL string `json:"webUrl,omitempty"` } // Headers returns the human-readable names of properties in a SharePointInfo @@ -374,13 +376,13 @@ func (i SharePointInfo) Values() []string { // OneDriveInfo describes a oneDrive item type OneDriveInfo struct { - ItemType ItemType `json:"itemType,omitempty"` - ParentPath string `json:"parentPath"` - ItemName string `json:"itemName"` - Size int64 `json:"size,omitempty"` - Owner string `json:"owner,omitempty"` Created time.Time `json:"created,omitempty"` + ItemName string `json:"itemName"` + ItemType ItemType `json:"itemType,omitempty"` Modified time.Time `json:"modified,omitempty"` + Owner string `json:"owner,omitempty"` + ParentPath string `json:"parentPath"` + Size int64 `json:"size,omitempty"` } // Headers returns the human-readable names of properties in a OneDriveInfo diff --git a/src/pkg/selectors/testdata/details.go b/src/pkg/selectors/testdata/details.go index a2743c95f..6243cfaab 100644 --- a/src/pkg/selectors/testdata/details.go +++ b/src/pkg/selectors/testdata/details.go @@ -228,6 +228,70 @@ var ( }, }, } + + SharePointRootPath = mustParsePath("tenant-id/sharepoint/site-id/libraries/drives/foo/root:", false) + SharePointLibraryPath = mustAppendPath(SharePointRootPath, "library", false) + SharePointBasePath1 = mustAppendPath(SharePointLibraryPath, "a", false) + SharePointBasePath2 = mustAppendPath(SharePointLibraryPath, "b", false) + + SharePointLibraryItemPath1 = mustAppendPath(SharePointLibraryPath, ItemName1, true) + SharePointLibraryItemPath2 = mustAppendPath(SharePointBasePath1, ItemName2, true) + SharePointLibraryItemPath3 = mustAppendPath(SharePointBasePath2, ItemName3, true) + + SharePointLibraryFolder = stdpath.Join(SharePointLibraryPath.Folders()[3:]...) + SharePointParentLibrary1 = stdpath.Join(SharePointBasePath1.Folders()[3:]...) + SharePointParentLibrary2 = stdpath.Join(SharePointBasePath2.Folders()[3:]...) + + SharePointLibraryItems = []details.DetailsEntry{ + { + RepoRef: SharePointLibraryItemPath1.String(), + ShortRef: SharePointLibraryItemPath1.ShortRef(), + ParentRef: SharePointLibraryItemPath1.ToBuilder().Dir().ShortRef(), + ItemInfo: details.ItemInfo{ + SharePoint: &details.SharePointInfo{ + ItemType: details.SharePointItem, + ParentPath: SharePointLibraryFolder, + ItemName: SharePointLibraryItemPath1.Item() + "name", + Size: int64(23), + Owner: UserEmail1, + Created: Time2, + Modified: Time4, + }, + }, + }, + { + RepoRef: SharePointLibraryItemPath2.String(), + ShortRef: SharePointLibraryItemPath2.ShortRef(), + ParentRef: SharePointLibraryItemPath2.ToBuilder().Dir().ShortRef(), + ItemInfo: details.ItemInfo{ + SharePoint: &details.SharePointInfo{ + ItemType: details.SharePointItem, + ParentPath: SharePointParentLibrary1, + ItemName: SharePointLibraryItemPath2.Item() + "name", + Size: int64(42), + Owner: UserEmail1, + Created: Time1, + Modified: Time3, + }, + }, + }, + { + RepoRef: SharePointLibraryItemPath3.String(), + ShortRef: SharePointLibraryItemPath3.ShortRef(), + ParentRef: SharePointLibraryItemPath3.ToBuilder().Dir().ShortRef(), + ItemInfo: details.ItemInfo{ + SharePoint: &details.SharePointInfo{ + ItemType: details.SharePointItem, + ParentPath: SharePointParentLibrary2, + ItemName: SharePointLibraryItemPath3.Item() + "name", + Size: int64(19), + Owner: UserEmail2, + Created: Time2, + Modified: Time4, + }, + }, + }, + } ) func GetDetailsSet() *details.Details { @@ -249,6 +313,10 @@ func GetDetailsSet() *details.Details { entries = append(entries, e) } + for _, e := range SharePointLibraryItems { + entries = append(entries, e) + } + return &details.Details{ DetailsModel: details.DetailsModel{ Entries: entries,