Skip to content

Commit

Permalink
relocate list and pages de-serialization functions (#4852)
Browse files Browse the repository at this point in the history
relocate list and pages de-serialization functions

#### Does this PR need a docs update or release note?
- [x] ⛔ No

#### Type of change
- [x] 🧹 Tech Debt/Cleanup

#### Issue(s)
#4754 

#### Test Plan

<!-- How will this be tested prior to merging.-->
- [x] 💪 Manual
- [x] ⚡ Unit test
- [x] 💚 E2E
  • Loading branch information
HiteshRepo authored Dec 15, 2023
1 parent 30c4877 commit dbf10d1
Show file tree
Hide file tree
Showing 16 changed files with 1,370 additions and 343 deletions.
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

0 comments on commit dbf10d1

Please sign in to comment.