Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implements lists restore handlers #4831

Closed
wants to merge 9 commits into from
65 changes: 0 additions & 65 deletions src/internal/m365/collection/site/collection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@ import (

"github.com/alcionai/clues"
kioser "github.com/microsoft/kiota-serialization-json-go"
"github.com/microsoftgraph/msgraph-sdk-go/sites"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"

"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/m365/collection/site/mock"
betaAPI "github.com/alcionai/corso/src/internal/m365/service/sharepoint/api"
Expand All @@ -23,7 +21,6 @@ import (
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/control"
"github.com/alcionai/corso/src/pkg/control/testdata"
"github.com/alcionai/corso/src/pkg/count"
"github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/path"
Expand Down Expand Up @@ -282,65 +279,3 @@ func (suite *SharePointCollectionSuite) TestCollection_streamItems() {
})
}
}

// TestRestoreListCollection verifies Graph Restore API for the List Collection
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please separate code movement and code changes into different PRs for ease of review.

func (suite *SharePointCollectionSuite) TestListCollection_Restore() {
t := suite.T()
// https://github.com/microsoftgraph/msgraph-sdk-go/issues/490
t.Skip("disabled until upstream issue with list restore is fixed.")

ctx, flush := tester.NewContext(t)
defer flush()

service := createTestService(t, suite.creds)
listing := spMock.ListDefault("Mock List")
testName := "MockListing"
listing.SetDisplayName(&testName)
byteArray, err := service.Serialize(listing)
require.NoError(t, err, clues.ToCore(err))

listData, err := data.NewPrefetchedItemWithInfo(
io.NopCloser(bytes.NewReader(byteArray)),
testName,
details.ItemInfo{SharePoint: ListToSPInfo(listing, int64(len(byteArray)))})
require.NoError(t, err, clues.ToCore(err))

destName := testdata.DefaultRestoreConfig("").Location

deets, err := restoreListItem(ctx, service, listData, suite.siteID, destName)
assert.NoError(t, err, clues.ToCore(err))
t.Logf("List created: %s\n", deets.SharePoint.ItemName)

// Clean-Up
var (
builder = service.Client().Sites().BySiteId(suite.siteID).Lists()
isFound bool
deleteID string
)

for {
resp, err := builder.Get(ctx, nil)
assert.NoError(t, err, "getting site lists", clues.ToCore(err))

for _, temp := range resp.GetValue() {
if ptr.Val(temp.GetDisplayName()) == deets.SharePoint.ItemName {
isFound = true
deleteID = ptr.Val(temp.GetId())

break
}
}
// Get Next Link
link, ok := ptr.ValOK(resp.GetOdataNextLink())
if !ok {
break
}

builder = sites.NewItemListsRequestBuilder(link, service.Adapter())
}

if isFound {
err := DeleteList(ctx, service, suite.siteID, deleteID)
assert.NoError(t, err, clues.ToCore(err))
}
}
29 changes: 29 additions & 0 deletions src/internal/m365/collection/site/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,32 @@ type backupHandler interface {
type getItemByIDer interface {
GetItemByID(ctx context.Context, itemID string) (models.Listable, error)
}

type restoreHandler interface {
PostLister
PostListItemer
DeleteLister
}

type PostLister interface {
PostList(
ctx context.Context,
listName string,
oldListByteArray []byte,
) (models.Listable, error)
}

type PostListItemer interface {
PostListItem(
ctx context.Context,
listID string,
oldListByteArray []byte,
) ([]models.ListItemable, error)
}

type DeleteLister interface {
DeleteList(
ctx context.Context,
listID string,
) error
}
16 changes: 0 additions & 16 deletions src/internal/m365/collection/site/lists.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,19 +89,3 @@ func PreFetchLists(

return listTuples, nil
}

// DeleteList removes a list object from a site.
// deletes require unique http clients
// https://github.com/alcionai/corso/issues/2707
func DeleteList(
ctx context.Context,
gs graph.Servicer,
siteID, listID string,
) error {
err := gs.Client().Sites().BySiteId(siteID).Lists().ByListId(listID).Delete(ctx, nil)
if err != nil {
return graph.Wrap(ctx, err, "deleting list")
}

return nil
}
37 changes: 37 additions & 0 deletions src/internal/m365/collection/site/lists_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,40 @@ func NewListsBackupHandler(protectedResource string, ac api.Lists) listsBackupHa
func (bh listsBackupHandler) GetItemByID(ctx context.Context, itemID string) (models.Listable, error) {
return bh.ac.GetListByID(ctx, bh.protectedResource, itemID)
}

var _ restoreHandler = &listsRestoreHandler{}

type listsRestoreHandler struct {
ac api.Lists
protectedResource string
}

func NewListsRestoreHandler(protectedResource string, ac api.Lists) listsRestoreHandler {
return listsRestoreHandler{
ac: ac,
protectedResource: protectedResource,
}
}

func (rh listsRestoreHandler) PostList(
ctx context.Context,
listName string,
oldListByteArray []byte,
) (models.Listable, error) {
return rh.ac.PostList(ctx, rh.protectedResource, listName, oldListByteArray)
}

func (rh listsRestoreHandler) PostListItem(
ctx context.Context,
listID string,
oldListByteArray []byte,
) ([]models.ListItemable, error) {
return rh.ac.PostListItem(ctx, rh.protectedResource, listID, oldListByteArray)
}

func (rh listsRestoreHandler) DeleteList(
ctx context.Context,
listID string,
) error {
return rh.ac.DeleteList(ctx, rh.protectedResource, listID)
}
53 changes: 53 additions & 0 deletions src/internal/m365/collection/site/mock/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ package mock

import (
"context"
"errors"
"strings"

"github.com/alcionai/clues"
"github.com/microsoftgraph/msgraph-sdk-go/models"

"github.com/alcionai/corso/src/internal/common/ptr"
betaAPI "github.com/alcionai/corso/src/internal/m365/service/sharepoint/api"
)

type ListHandler struct {
Expand All @@ -21,3 +25,52 @@ func (lh *ListHandler) GetItemByID(ctx context.Context, itemID string) (models.L

return ls, lh.Err
}

type ListRestoreHandler struct {
Err error
}

func (lh *ListRestoreHandler) PostList(
ctx context.Context,
listName string,
oldListByteArray []byte,
) (models.Listable, error) {
newListName := listName

oldList, err := betaAPI.CreateListFromBytes(oldListByteArray)
if err != nil {
return nil, errors.New("error while creating old list")
}

if name, ok := ptr.ValOK(oldList.GetDisplayName()); ok {
nameParts := strings.Split(listName, "_")
if len(nameParts) > 0 {
nameParts[len(nameParts)-1] = name
newListName = strings.Join(nameParts, "_")
}
}

newList := betaAPI.ToListable(oldList, newListName)

return newList, lh.Err
}

func (lh *ListRestoreHandler) PostListItem(
ctx context.Context,
listID string,
oldListByteArray []byte,
) ([]models.ListItemable, error) {
oldList, err := betaAPI.CreateListFromBytes(oldListByteArray)
if err != nil {
return nil, clues.WrapWC(ctx, err, "creating old list to get list items")
}

contents := make([]models.ListItemable, 0)

for _, itm := range oldList.GetItems() {
temp := betaAPI.CloneListItem(itm)
contents = append(contents, temp)
}

return contents, lh.Err
}
53 changes: 11 additions & 42 deletions src/internal/m365/collection/site/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"runtime/trace"

"github.com/alcionai/clues"
"github.com/microsoftgraph/msgraph-sdk-go/models"

"github.com/alcionai/corso/src/internal/common/dttm"
"github.com/alcionai/corso/src/internal/common/idname"
Expand Down Expand Up @@ -42,6 +41,7 @@ func ConsumeRestoreCollections(
) (*support.ControllerOperationStatus, error) {
var (
lrh = drive.NewSiteRestoreHandler(ac, rcc.Selector.PathService())
listsRh = NewListsRestoreHandler(rcc.ProtectedResource.ID(), ac.Lists())
restoreMetrics support.CollectionMetrics
caches = drive.NewRestoreCaches(backupDriveIDNames)
el = errs.Local()
Expand Down Expand Up @@ -89,7 +89,7 @@ func ConsumeRestoreCollections(
case path.ListsCategory:
metrics, err = RestoreListCollection(
ictx,
ac.Stable,
listsRh,
dc,
rcc.RestoreConfig.Location,
deets,
Expand Down Expand Up @@ -135,7 +135,7 @@ func ConsumeRestoreCollections(
// Restored List can be verified within the Site contents.
func restoreListItem(
ctx context.Context,
service graph.Servicer,
rh restoreHandler,
itemData data.Item,
siteID, destName string,
) (details.ItemInfo, error) {
Expand All @@ -154,50 +154,19 @@ func restoreListItem(
return dii, clues.WrapWC(ctx, err, "reading backup data")
}

oldList, err := betaAPI.CreateListFromBytes(byteArray)
if err != nil {
return dii, clues.WrapWC(ctx, err, "creating item")
}

if name, ok := ptr.ValOK(oldList.GetDisplayName()); ok {
listName = name
}

var (
newName = fmt.Sprintf("%s_%s", destName, listName)
newList = betaAPI.ToListable(oldList, newName)
contents = make([]models.ListItemable, 0)
)

for _, itm := range oldList.GetItems() {
temp := betaAPI.CloneListItem(itm)
contents = append(contents, temp)
}

newList.SetItems(contents)
newName := fmt.Sprintf("%s_%s", destName, listName)

// Restore to List base to M365 back store
restoredList, err := service.Client().Sites().BySiteId(siteID).Lists().Post(ctx, newList, nil)
restoredList, err := rh.PostList(ctx, newName, byteArray)
if err != nil {
return dii, graph.Wrap(ctx, err, "restoring list")
return dii, clues.WrapWC(ctx, err, "restoring lists")
}

// Uploading of ListItems is conducted after the List is restored
// Reference: https://learn.microsoft.com/en-us/graph/api/listitem-create?view=graph-rest-1.0&tabs=http
if len(contents) > 0 {
for _, lItem := range contents {
_, err := service.Client().
Sites().
BySiteId(siteID).
Lists().
ByListId(ptr.Val(restoredList.GetId())).
Items().
Post(ctx, lItem, nil)
if err != nil {
return dii, graph.Wrap(ctx, err, "restoring list items").
With("restored_list_id", ptr.Val(restoredList.GetId()))
}
}
_, err = rh.PostListItem(ctx, ptr.Val(restoredList.GetId()), byteArray)
if err != nil {
return dii, clues.WrapWC(ctx, err, "restoring list items")
}

dii.SharePoint = ListToSPInfo(restoredList, int64(len(byteArray)))
Expand All @@ -207,7 +176,7 @@ func restoreListItem(

func RestoreListCollection(
ctx context.Context,
service graph.Servicer,
rh restoreHandler,
dc data.RestoreCollection,
restoreContainerName string,
deets *details.Builder,
Expand Down Expand Up @@ -243,7 +212,7 @@ func RestoreListCollection(

itemInfo, err := restoreListItem(
ctx,
service,
rh,
itemData,
siteID,
restoreContainerName)
Expand Down
Loading
Loading