adds ListInfo details to SharepointInfo details (#4916)

adds ListInfo details to SharepointInfo details
Changes previously approved in PR: https://github.com/alcionai/corso/pull/4851

#### 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] 🌻 Feature

#### Issue(s)
#4754 

#### Test Plan

<!-- How will this be tested prior to merging.-->
- [x] 💪 Manual
- [x]  Unit test
- [x] 💚 E2E
This commit is contained in:
Hitesh Pattanayak 2023-12-22 12:20:34 +05:30 committed by GitHub
parent c3b15a00bd
commit 50fd28aab2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 200 additions and 31 deletions

View File

@ -942,7 +942,7 @@ var defaultOneDriveLocationIDer = func(driveID string, elems ...string) details.
}
var defaultSharePointLocationIDer = func(driveID string, elems ...string) details.LocationIDer {
return details.NewSharePointLocationIDer(driveID, elems...)
return details.NewSharePointLocationIDer(path.LibrariesCategory, driveID, elems...)
}
func (h mockBackupHandler[T]) IsAllPass() bool {

View File

@ -149,7 +149,8 @@ func (h siteBackupHandler) NewLocationIDer(
driveID string,
elems ...string,
) details.LocationIDer {
return details.NewSharePointLocationIDer(driveID, elems...)
_, cat := h.ServiceCat()
return details.NewSharePointLocationIDer(cat, driveID, elems...)
}
func (h siteBackupHandler) GetItemPermission(

View File

@ -296,7 +296,7 @@ var defaultOneDriveLocationIDer = func(driveID string, elems ...string) details.
}
var defaultSharePointLocationIDer = func(driveID string, elems ...string) details.LocationIDer {
return details.NewSharePointLocationIDer(driveID, elems...)
return details.NewSharePointLocationIDer(path.LibrariesCategory, driveID, elems...)
}
func (h BackupHandler[T]) IsAllPass() bool {

View File

@ -116,7 +116,7 @@ func (suite *DetailsUnitSuite) TestDetailsEntry_HeadersValues() {
expectVs: []string{"deadbeef", "sender", "Parent", "subject", nowStr},
},
{
name: "sharepoint info",
name: "sharepoint library info",
entry: Entry{
RepoRef: "reporef",
ShortRef: "deadbeef",
@ -125,6 +125,7 @@ func (suite *DetailsUnitSuite) TestDetailsEntry_HeadersValues() {
ItemInfo: ItemInfo{
SharePoint: &SharePointInfo{
ItemName: "itemName",
ItemType: SharePointLibrary,
ParentPath: "parentPath",
Size: 1000,
WebURL: "https://not.a.real/url",
@ -147,6 +148,66 @@ func (suite *DetailsUnitSuite) TestDetailsEntry_HeadersValues() {
nowStr,
},
},
{
name: "sharepoint list info for genericList template",
entry: Entry{
RepoRef: "reporef",
ShortRef: "deadbeef",
LocationRef: "locationref",
ItemRef: "itemref",
ItemInfo: ItemInfo{
SharePoint: &SharePointInfo{
ItemType: SharePointList,
List: &ListInfo{
Name: "list1",
ItemCount: 50,
Template: "genericList",
Created: now,
Modified: now,
WebURL: "https://10rqc2.sharepoint.com/sites/site-4754-small-lists/Lists/list1",
},
},
},
},
expectHs: []string{"ID", "List", "Items", "Created", "Modified"},
expectVs: []string{
"deadbeef",
"list1",
"50",
nowStr,
nowStr,
},
},
{
name: "sharepoint list info for documentLibrary template",
entry: Entry{
RepoRef: "reporef",
ShortRef: "deadbeef",
LocationRef: "locationref",
ItemRef: "itemref",
ItemInfo: ItemInfo{
SharePoint: &SharePointInfo{
ItemType: SharePointList,
List: &ListInfo{
Name: "Shared%20Documents",
ItemCount: 50,
Template: "documentLibrary",
Created: now,
Modified: now,
WebURL: "https://10rqc2.sharepoint.com/sites/site-4754-small-lists/Lists/Shared%20Documents",
},
},
},
},
expectHs: []string{"ID", "List", "Items", "Created", "Modified"},
expectVs: []string{
"deadbeef",
"Shared%20Documents",
"50",
nowStr,
nowStr,
},
},
{
name: "oneDrive info",
entry: Entry{
@ -846,6 +907,34 @@ var pathItemsTable = []struct {
expectRepoRefs: []string{"abcde", "12345", "foo.meta"},
expectLocationRefs: []string{"locationref", "locationref2", "locationref.dirmeta"},
},
{
name: "multiple entries with not recoverables",
ents: []Entry{
{
RepoRef: "abcde",
LocationRef: "locationref",
},
{
RepoRef: "foo.meta",
LocationRef: "locationref.dirmeta",
ItemRef: "itemref.meta",
ItemInfo: ItemInfo{
SharePoint: &SharePointInfo{ItemType: SharePointList},
},
},
{
RepoRef: "invalid-template-list-file",
LocationRef: "locationref-invalid-template-list-file",
ItemRef: "itemref-template-list-file",
ItemInfo: ItemInfo{
SharePoint: &SharePointInfo{ItemType: SharePointList},
NotRecoverable: true,
},
},
},
expectRepoRefs: []string{"abcde", "foo.meta"},
expectLocationRefs: []string{"locationref", "locationref.dirmeta"},
},
}
func (suite *DetailsUnitSuite) TestDetailsModel_Path() {
@ -1277,12 +1366,16 @@ func (suite *DetailsUnitSuite) TestUnarshalTo() {
func (suite *DetailsUnitSuite) TestLocationIDer_FromEntry() {
const (
rrString = "tenant-id/%s/user-id/%s/drives/drive-id/root:/some/folder/stuff/item"
driveID = "driveID"
rrString = "tenant-id/%s/user-id/%s/drives/drive-id/root:/some/folder/stuff/item"
listsRrString = "tenant-id/%s/site-id/%s/lists/list-id/list-id"
driveID = "driveID"
listID = "listID"
expectedUniqueLocFmt = "%s/" + driveID + "/root:/some/folder/stuff"
expectedExchangeUniqueLocFmt = "%s/root:/some/folder/stuff"
expectedDetailsLoc = "root:/some/folder/stuff"
expectedListUniqueLocFmt = "%s/" + listID
expectedListDetailsLoc = listID
)
table := []struct {
@ -1379,6 +1472,21 @@ func (suite *DetailsUnitSuite) TestLocationIDer_FromEntry() {
backupVersion: version.OneDrive7LocationRef,
expectedErr: require.Error,
},
{
name: "SharePoint List With LocationRef",
service: path.SharePointService.String(),
category: path.ListsCategory.String(),
itemInfo: ItemInfo{
SharePoint: &SharePointInfo{
ItemType: SharePointList,
List: &ListInfo{},
},
},
backupVersion: version.OneDrive7LocationRef,
hasLocRef: true,
expectedErr: require.NoError,
expectedUniqueLoc: fmt.Sprintf(expectedListUniqueLocFmt, path.ListsCategory),
},
{
name: "Exchange Email With LocationRef Old Version",
service: path.ExchangeService.String(),
@ -1479,13 +1587,23 @@ func (suite *DetailsUnitSuite) TestLocationIDer_FromEntry() {
suite.Run(test.name, func() {
t := suite.T()
rr := ""
detailsLoc := ""
if test.category == path.ListsCategory.String() {
rr = fmt.Sprintf(listsRrString, test.service, test.category)
detailsLoc = expectedListDetailsLoc
} else {
rr = fmt.Sprintf(rrString, test.service, test.category)
detailsLoc = expectedDetailsLoc
}
entry := Entry{
RepoRef: fmt.Sprintf(rrString, test.service, test.category),
RepoRef: rr,
ItemInfo: test.itemInfo,
}
if test.hasLocRef {
entry.LocationRef = expectedDetailsLoc
entry.LocationRef = detailsLoc
}
loc, err := entry.ToLocationIDer(test.backupVersion)
@ -1502,7 +1620,7 @@ func (suite *DetailsUnitSuite) TestLocationIDer_FromEntry() {
"unique location")
assert.Equal(
t,
expectedDetailsLoc,
detailsLoc,
loc.InDetails().String(),
"details location")
})

View File

@ -74,7 +74,8 @@ type ItemInfo struct {
OneDrive *OneDriveInfo `json:"oneDrive,omitempty"`
Groups *GroupsInfo `json:"groups,omitempty"`
// Optional item extension data
Extension *ExtensionData `json:"extension,omitempty"`
Extension *ExtensionData `json:"extension,omitempty"`
NotRecoverable bool `json:"notRecoverable,omitempty"`
}
// typedInfo should get embedded in each sesrvice type to track

View File

@ -67,7 +67,9 @@ func (dm DetailsModel) Paths() []string {
r := make([]string, 0, len(dm.Entries))
for _, ent := range dm.Entries {
if ent.Folder != nil || ent.isMetaFile() {
if ent.Folder != nil ||
ent.isMetaFile() ||
ent.NotRecoverable {
continue
}
@ -85,7 +87,9 @@ func (dm DetailsModel) Items() entrySet {
for i := 0; i < len(dm.Entries); i++ {
ent := dm.Entries[i]
if ent.Folder != nil || ent.isMetaFile() {
if ent.Folder != nil ||
ent.isMetaFile() ||
ent.NotRecoverable {
continue
}

View File

@ -1,6 +1,7 @@
package details
import (
"fmt"
"time"
"github.com/alcionai/clues"
@ -13,16 +14,24 @@ import (
// NewSharePointLocationIDer builds a LocationIDer for the drive and folder
// path. The path denoted by the folders should be unique within the drive.
func NewSharePointLocationIDer(
category path.CategoryType,
driveID string,
escapedFolders ...string,
) uniqueLoc {
pb := path.Builder{}.
Append(path.LibrariesCategory.String(), driveID).
Append(escapedFolders...)
pb := path.Builder{}.Append(category.String())
prefixElems := 1
if len(driveID) > 0 { // for library category
pb = pb.Append(driveID)
prefixElems = 2
}
pb = pb.Append(escapedFolders...)
return uniqueLoc{
pb: pb,
prefixElems: 2,
prefixElems: prefixElems,
}
}
@ -39,26 +48,55 @@ type SharePointInfo struct {
Size int64 `json:"size,omitempty"`
WebURL string `json:"webUrl,omitempty"`
SiteID string `json:"siteID,omitempty"`
List *ListInfo `json:"list,omitempty"`
}
type ListInfo struct {
Name string `json:"name,omitempty"`
ItemCount int64 `json:"itemCount,omitempty"`
Template string `json:"template,omitempty"`
WebURL string `json:"webUrl,omitempty"`
Created time.Time `json:"created,omitempty"`
Modified time.Time `json:"modified,omitempty"`
}
// Headers returns the human-readable names of properties in a SharePointInfo
// for printing out to a terminal in a columnar display.
func (i SharePointInfo) Headers() []string {
return []string{"ItemName", "Library", "ParentPath", "Size", "Owner", "Created", "Modified"}
switch i.ItemType {
case SharePointLibrary:
return []string{"ItemName", "Library", "ParentPath", "Size", "Owner", "Created", "Modified"}
case SharePointList:
return []string{"List", "Items", "Created", "Modified"}
}
return []string{}
}
// Values returns the values matching the Headers list for printing
// out to a terminal in a columnar display.
func (i SharePointInfo) Values() []string {
return []string{
i.ItemName,
i.DriveName,
i.ParentPath,
humanize.Bytes(uint64(i.Size)),
i.Owner,
dttm.FormatToTabularDisplay(i.Created),
dttm.FormatToTabularDisplay(i.Modified),
switch i.ItemType {
case SharePointLibrary:
return []string{
i.ItemName,
i.DriveName,
i.ParentPath,
humanize.Bytes(uint64(i.Size)),
i.Owner,
dttm.FormatToTabularDisplay(i.Created),
dttm.FormatToTabularDisplay(i.Modified),
}
case SharePointList:
return []string{
i.List.Name,
fmt.Sprintf("%d", i.List.ItemCount),
dttm.FormatToTabularDisplay(i.List.Created),
dttm.FormatToTabularDisplay(i.List.Modified),
}
}
return []string{}
}
func (i *SharePointInfo) UpdateParentPath(newLocPath *path.Builder) {
@ -66,11 +104,14 @@ func (i *SharePointInfo) UpdateParentPath(newLocPath *path.Builder) {
}
func (i *SharePointInfo) uniqueLocation(baseLoc *path.Builder) (*uniqueLoc, error) {
if len(i.DriveID) == 0 {
return nil, clues.New("empty drive ID")
}
loc := uniqueLoc{}
loc := NewSharePointLocationIDer(i.DriveID, baseLoc.Elements()...)
switch i.ItemType {
case SharePointLibrary, OneDriveItem:
loc = NewSharePointLocationIDer(path.LibrariesCategory, i.DriveID, baseLoc.Elements()...)
case SharePointList:
loc = NewSharePointLocationIDer(path.ListsCategory, "", baseLoc.Elements()...)
}
return &loc, nil
}
@ -78,8 +119,11 @@ func (i *SharePointInfo) uniqueLocation(baseLoc *path.Builder) (*uniqueLoc, erro
func (i *SharePointInfo) updateFolder(f *FolderInfo) error {
// TODO(ashmrtn): Change to just SharePointLibrary when the code that
// generates the item type is fixed.
if i.ItemType == OneDriveItem || i.ItemType == SharePointLibrary {
switch i.ItemType {
case OneDriveItem, SharePointLibrary:
return updateFolderWithinDrive(SharePointLibrary, i.DriveName, i.DriveID, f)
case SharePointList:
return nil
}
return clues.New("unsupported non-SharePoint ItemType").With("item_type", i.ItemType)

View File

@ -6,12 +6,13 @@ import (
"io"
"testing"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/google/uuid"
"github.com/microsoft/kiota-abstractions-go/serialization"
kjson "github.com/microsoft/kiota-serialization-json-go"
"github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors"
"github.com/stretchr/testify/require"
"github.com/alcionai/corso/src/internal/common/ptr"
)
func ODataErr(code string) *odataerrors.ODataError {