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

relocate list and pages de-serialization functions #4852

Merged
merged 3 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/internal/common/keys/keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package keys

type Set map[string]struct{}

func (ks Set) HasKey(key string) bool {
if _, ok := ks[key]; ok {
return true
}

return false
}

func (ks Set) Keys() []string {
sliceKeys := make([]string, 0)

for k := range ks {
sliceKeys = append(sliceKeys, k)
}

return sliceKeys
}

func HasKeys(data map[string]any, keys ...string) bool {
for _, k := range keys {
if _, ok := data[k]; !ok {
return false
}
}

return true
}
122 changes: 122 additions & 0 deletions src/internal/common/keys/keys_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package keys

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"

"github.com/alcionai/corso/src/internal/tester"
)

type KeySetTestSuite struct {
tester.Suite
}

func TestKeySetTestSuite(t *testing.T) {
suite.Run(t, &KeySetTestSuite{Suite: tester.NewUnitSuite(t)})
}

func (suite *KeySetTestSuite) TestHasKey() {
tests := []struct {
name string
keySet Set
key string
expect assert.BoolAssertionFunc
}{
{
name: "key exists in the set",
keySet: Set{"key1": {}, "key2": {}},
key: "key1",
expect: assert.True,
},
{
name: "key does not exist in the set",
keySet: Set{"key1": {}, "key2": {}},
key: "nonexistent",
expect: assert.False,
},
{
name: "empty set",
keySet: Set{},
key: "key",
expect: assert.False,
},
}

for _, test := range tests {
suite.Run(test.name, func() {
test.expect(suite.T(), test.keySet.HasKey(test.key))
})
}
}

func (suite *KeySetTestSuite) TestKeys() {
tests := []struct {
name string
keySet Set
expect assert.ValueAssertionFunc
}{
{
name: "non-empty set",
keySet: Set{"key1": {}, "key2": {}},
expect: assert.NotEmpty,
},
{
name: "empty set",
keySet: Set{},
expect: assert.Empty,
},
}

for _, test := range tests {
suite.Run(test.name, func() {
keys := test.keySet.Keys()
test.expect(suite.T(), keys, []string{"key1", "key2"})
})
}
}

func (suite *KeySetTestSuite) TestHasKeys() {
tests := []struct {
name string
data map[string]any
keys []string
expect assert.BoolAssertionFunc
}{
{
name: "has all keys",
data: map[string]any{
"key1": "data1",
"key2": 2,
"key3": struct{}{},
},
keys: []string{"key1", "key2", "key3"},
expect: assert.True,
},
{
name: "has some keys",
data: map[string]any{
"key1": "data1",
"key2": 2,
},
keys: []string{"key1", "key2", "key3"},
expect: assert.False,
},
{
name: "has no key",
data: map[string]any{
"key1": "data1",
"key2": 2,
},
keys: []string{"key4", "key5", "key6"},
expect: assert.False,
},
}

for _, test := range tests {
suite.Run(test.name, func() {
test.expect(suite.T(), HasKeys(test.data, test.keys...))
})
}
}
5 changes: 3 additions & 2 deletions src/internal/m365/collection/site/collection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func (suite *SharePointCollectionSuite) TestCollection_Items() {
},
getItem: func(t *testing.T, itemName string) data.Item {
byteArray := spMock.Page(itemName)
page, err := betaAPI.CreatePageFromBytes(byteArray)
page, err := betaAPI.BytesToSitePageable(byteArray)
require.NoError(t, err, clues.ToCore(err))

data, err := data.NewPrefetchedItemWithInfo(
Expand Down Expand Up @@ -307,7 +307,8 @@ func (suite *SharePointCollectionSuite) TestListCollection_Restore() {

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

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

Expand Down
20 changes: 20 additions & 0 deletions src/internal/m365/collection/site/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,23 @@ type getItemByIDer interface {
type getItemser interface {
GetItems(ctx context.Context, cc api.CallConfig) ([]models.Listable, error)
}

type restoreHandler interface {
PostLister
DeleteLister
}

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

type DeleteLister interface {
DeleteList(
ctx context.Context,
listID string,
) error
}
29 changes: 29 additions & 0 deletions src/internal/m365/collection/site/lists_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,32 @@ func (bh listsBackupHandler) GetItemByID(ctx context.Context, itemID string) (mo
func (bh listsBackupHandler) GetItems(ctx context.Context, cc api.CallConfig) ([]models.Listable, error) {
return bh.ac.GetLists(ctx, bh.protectedResource, cc)
}

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,
storedListData []byte,
) (models.Listable, error) {
return rh.ac.PostList(ctx, rh.protectedResource, listName, storedListData)
}

func (rh listsRestoreHandler) DeleteList(
ctx context.Context,
listID string,
) error {
return rh.ac.DeleteList(ctx, rh.protectedResource, listID)
}
26 changes: 22 additions & 4 deletions src/internal/m365/collection/site/mock/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,33 @@ import (
)

type ListHandler struct {
ListItem models.Listable
Err error
List models.Listable
Err error
}

func (lh *ListHandler) GetItemByID(ctx context.Context, itemID string) (models.Listable, error) {
ls := models.NewList()

lh.ListItem = ls
lh.ListItem.SetId(ptr.To(itemID))
lh.List = ls
lh.List.SetId(ptr.To(itemID))

return ls, lh.Err
}

type ListRestoreHandler struct {
List models.Listable
Err error
}

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

lh.List = ls
lh.List.SetDisplayName(ptr.To(listName))

return lh.List, lh.Err
}
60 changes: 10 additions & 50 deletions src/internal/m365/collection/site/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@ 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"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/diagnostics"
"github.com/alcionai/corso/src/internal/m365/collection/drive"
Expand Down Expand Up @@ -42,6 +40,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 +88,7 @@ func ConsumeRestoreCollections(
case path.ListsCategory:
metrics, err = RestoreListCollection(
ictx,
ac.Stable,
listsRh,
dc,
rcc.RestoreConfig.Location,
deets,
Expand Down Expand Up @@ -135,7 +134,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 @@ -149,65 +148,26 @@ func restoreListItem(
listName = itemData.ID()
)

byteArray, err := io.ReadAll(itemData.ToReader())
bytes, err := io.ReadAll(itemData.ToReader())
if err != nil {
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
}
newName := fmt.Sprintf("%s_%s", destName, listName)

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)

// 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, bytes)
if err != nil {
return dii, graph.Wrap(ctx, err, "restoring list")
}

// 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()))
}
}
return dii, clues.WrapWC(ctx, err, "restoring lists")
}

dii.SharePoint = ListToSPInfo(restoredList, int64(len(byteArray)))
dii.SharePoint = ListToSPInfo(restoredList, int64(len(bytes)))

return dii, nil
}

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

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