add api funcs for creating documentLibs

Adds api handlers for creating document libraries in sharepoint.
This is the first step in allowing us to restore drives that were
deleted between backup and restore.
This commit is contained in:
ryanfkeepers 2023-07-12 19:13:44 -06:00
parent 57125f3ea3
commit f4b92139bc
5 changed files with 174 additions and 12 deletions

View File

@ -35,6 +35,7 @@ type BackupHandler interface {
api.Getter api.Getter
GetItemPermissioner GetItemPermissioner
GetItemer GetItemer
NewDrivePagerer
// PathPrefix constructs the service and category specific path prefix for // PathPrefix constructs the service and category specific path prefix for
// the given values. // the given values.
@ -49,7 +50,6 @@ type BackupHandler interface {
// ServiceCat returns the service and category used by this implementation. // ServiceCat returns the service and category used by this implementation.
ServiceCat() (path.ServiceType, path.CategoryType) ServiceCat() (path.ServiceType, path.CategoryType)
NewDrivePager(resourceOwner string, fields []string) api.DrivePager
NewItemPager(driveID, link string, fields []string) api.DriveItemDeltaEnumerator NewItemPager(driveID, link string, fields []string) api.DriveItemDeltaEnumerator
// FormatDisplayPath creates a human-readable string to represent the // FormatDisplayPath creates a human-readable string to represent the
// provided path. // provided path.
@ -61,6 +61,10 @@ type BackupHandler interface {
IncludesDir(dir string) bool IncludesDir(dir string) bool
} }
type NewDrivePagerer interface {
NewDrivePager(resourceOwner string, fields []string) api.DrivePager
}
type GetItemPermissioner interface { type GetItemPermissioner interface {
GetItemPermission( GetItemPermission(
ctx context.Context, ctx context.Context,
@ -86,7 +90,9 @@ type RestoreHandler interface {
GetItemsByCollisionKeyser GetItemsByCollisionKeyser
GetRootFolderer GetRootFolderer
ItemInfoAugmenter ItemInfoAugmenter
NewDrivePagerer
NewItemContentUploader NewItemContentUploader
PostDriver
PostItemInContainerer PostItemInContainerer
DeleteItemPermissioner DeleteItemPermissioner
UpdateItemPermissioner UpdateItemPermissioner
@ -145,6 +151,13 @@ type UpdateItemLinkSharer interface {
) (models.Permissionable, error) ) (models.Permissionable, error)
} }
type PostDriver interface {
PostDrive(
ctx context.Context,
protectedResourceID, driveName string,
) (models.Driveable, error)
}
type PostItemInContainerer interface { type PostItemInContainerer interface {
PostItemInContainer( PostItemInContainer(
ctx context.Context, ctx context.Context,

View File

@ -8,6 +8,7 @@ import (
"github.com/microsoftgraph/msgraph-sdk-go/drives" "github.com/microsoftgraph/msgraph-sdk-go/drives"
"github.com/microsoftgraph/msgraph-sdk-go/models" "github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/alcionai/clues"
"github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/common/ptr"
odConsts "github.com/alcionai/corso/src/internal/m365/onedrive/consts" odConsts "github.com/alcionai/corso/src/internal/m365/onedrive/consts"
"github.com/alcionai/corso/src/pkg/backup/details" "github.com/alcionai/corso/src/pkg/backup/details"
@ -133,6 +134,19 @@ func NewRestoreHandler(ac api.Client) *itemRestoreHandler {
return &itemRestoreHandler{ac.Drives()} return &itemRestoreHandler{ac.Drives()}
} }
func (h itemRestoreHandler) PostDrive(
context.Context,
string, string,
) (models.Driveable, error) {
return nil, clues.New("creating drives in oneDrive is not supported")
}
func (h itemRestoreHandler) NewDrivePager(
resourceOwner string, fields []string,
) api.DrivePager {
return h.ac.NewUserDrivePager(resourceOwner, fields)
}
// AugmentItemInfo will populate a details.OneDriveInfo struct // AugmentItemInfo will populate a details.OneDriveInfo struct
// with properties from the drive item. ItemSize is specified // with properties from the drive item. ItemSize is specified
// separately for restore processes because the local itemable // separately for restore processes because the local itemable

View File

@ -157,11 +157,25 @@ func (h libraryBackupHandler) IncludesDir(dir string) bool {
var _ onedrive.RestoreHandler = &libraryRestoreHandler{} var _ onedrive.RestoreHandler = &libraryRestoreHandler{}
type libraryRestoreHandler struct { type libraryRestoreHandler struct {
ac api.Drives ac api.Client
}
func (h libraryRestoreHandler) PostDrive(
ctx context.Context,
siteID, driveName string,
) (models.Driveable, error) {
return h.ac.Lists().PostDrive(ctx, siteID, driveName)
} }
func NewRestoreHandler(ac api.Client) *libraryRestoreHandler { func NewRestoreHandler(ac api.Client) *libraryRestoreHandler {
return &libraryRestoreHandler{ac.Drives()} return &libraryRestoreHandler{ac}
}
func (h libraryRestoreHandler) NewDrivePager(
resourceOwner string,
fields []string,
) api.DrivePager {
return h.ac.Drives().NewSiteDrivePager(resourceOwner, fields)
} }
func (h libraryRestoreHandler) AugmentItemInfo( func (h libraryRestoreHandler) AugmentItemInfo(
@ -177,21 +191,21 @@ func (h libraryRestoreHandler) DeleteItem(
ctx context.Context, ctx context.Context,
driveID, itemID string, driveID, itemID string,
) error { ) error {
return h.ac.DeleteItem(ctx, driveID, itemID) return h.ac.Drives().DeleteItem(ctx, driveID, itemID)
} }
func (h libraryRestoreHandler) DeleteItemPermission( func (h libraryRestoreHandler) DeleteItemPermission(
ctx context.Context, ctx context.Context,
driveID, itemID, permissionID string, driveID, itemID, permissionID string,
) error { ) error {
return h.ac.DeleteItemPermission(ctx, driveID, itemID, permissionID) return h.ac.Drives().DeleteItemPermission(ctx, driveID, itemID, permissionID)
} }
func (h libraryRestoreHandler) GetItemsInContainerByCollisionKey( func (h libraryRestoreHandler) GetItemsInContainerByCollisionKey(
ctx context.Context, ctx context.Context,
driveID, containerID string, driveID, containerID string,
) (map[string]api.DriveItemIDType, error) { ) (map[string]api.DriveItemIDType, error) {
m, err := h.ac.GetItemsInContainerByCollisionKey(ctx, driveID, containerID) m, err := h.ac.Drives().GetItemsInContainerByCollisionKey(ctx, driveID, containerID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -203,7 +217,7 @@ func (h libraryRestoreHandler) NewItemContentUpload(
ctx context.Context, ctx context.Context,
driveID, itemID string, driveID, itemID string,
) (models.UploadSessionable, error) { ) (models.UploadSessionable, error) {
return h.ac.NewItemContentUpload(ctx, driveID, itemID) return h.ac.Drives().NewItemContentUpload(ctx, driveID, itemID)
} }
func (h libraryRestoreHandler) PostItemPermissionUpdate( func (h libraryRestoreHandler) PostItemPermissionUpdate(
@ -211,7 +225,7 @@ func (h libraryRestoreHandler) PostItemPermissionUpdate(
driveID, itemID string, driveID, itemID string,
body *drives.ItemItemsItemInvitePostRequestBody, body *drives.ItemItemsItemInvitePostRequestBody,
) (drives.ItemItemsItemInviteResponseable, error) { ) (drives.ItemItemsItemInviteResponseable, error) {
return h.ac.PostItemPermissionUpdate(ctx, driveID, itemID, body) return h.ac.Drives().PostItemPermissionUpdate(ctx, driveID, itemID, body)
} }
func (h libraryRestoreHandler) PostItemLinkShareUpdate( func (h libraryRestoreHandler) PostItemLinkShareUpdate(
@ -219,7 +233,7 @@ func (h libraryRestoreHandler) PostItemLinkShareUpdate(
driveID, itemID string, driveID, itemID string,
body *drives.ItemItemsItemCreateLinkPostRequestBody, body *drives.ItemItemsItemCreateLinkPostRequestBody,
) (models.Permissionable, error) { ) (models.Permissionable, error) {
return h.ac.PostItemLinkShareUpdate(ctx, driveID, itemID, body) return h.ac.Drives().PostItemLinkShareUpdate(ctx, driveID, itemID, body)
} }
func (h libraryRestoreHandler) PostItemInContainer( func (h libraryRestoreHandler) PostItemInContainer(
@ -228,21 +242,21 @@ func (h libraryRestoreHandler) PostItemInContainer(
newItem models.DriveItemable, newItem models.DriveItemable,
onCollision control.CollisionPolicy, onCollision control.CollisionPolicy,
) (models.DriveItemable, error) { ) (models.DriveItemable, error) {
return h.ac.PostItemInContainer(ctx, driveID, parentFolderID, newItem, onCollision) return h.ac.Drives().PostItemInContainer(ctx, driveID, parentFolderID, newItem, onCollision)
} }
func (h libraryRestoreHandler) GetFolderByName( func (h libraryRestoreHandler) GetFolderByName(
ctx context.Context, ctx context.Context,
driveID, parentFolderID, folderName string, driveID, parentFolderID, folderName string,
) (models.DriveItemable, error) { ) (models.DriveItemable, error) {
return h.ac.GetFolderByName(ctx, driveID, parentFolderID, folderName) return h.ac.Drives().GetFolderByName(ctx, driveID, parentFolderID, folderName)
} }
func (h libraryRestoreHandler) GetRootFolder( func (h libraryRestoreHandler) GetRootFolder(
ctx context.Context, ctx context.Context,
driveID string, driveID string,
) (models.DriveItemable, error) { ) (models.DriveItemable, error) {
return h.ac.GetRootFolder(ctx, driveID) return h.ac.Drives().GetRootFolder(ctx, driveID)
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -0,0 +1,64 @@
package api
import (
"context"
"github.com/alcionai/clues"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/m365/graph"
)
// ---------------------------------------------------------------------------
// controller
// ---------------------------------------------------------------------------
func (c Client) Lists() Lists {
return Lists{c}
}
// Lists is an interface-compliant provider of the client.
type Lists struct {
Client
}
// PostDrive creates a new list of type drive. Specifically used to create
// documentLibraries for SharePoint Sites.
func (c Lists) PostDrive(
ctx context.Context,
siteID, driveName string,
) (models.Driveable, error) {
list := models.NewList()
list.SetDisplayName(&driveName)
list.SetDescription(ptr.To("corso auto-generated restore destination"))
li := models.NewListInfo()
li.SetTemplate(ptr.To("documentLibrary"))
list.SetList(li)
// creating a list of type documentLibrary will result in the creation
// of a new drive owned by the given site.
builder := c.Stable.
Client().
Sites().
BySiteId(siteID).
Lists()
newList, err := builder.Post(ctx, list, nil)
if graph.IsErrItemAlreadyExistsConflict(err) {
return nil, clues.Stack(graph.ErrItemAlreadyExistsConflict, err).WithClues(ctx)
}
if err != nil {
return nil, graph.Wrap(ctx, err, "creating documentLibrary list")
}
// drive information is not returned by the list creation.
drive, err := builder.
ByListId(ptr.Val(newList.GetId())).
Drive().
Get(ctx, nil)
return drive, graph.Wrap(ctx, err, "fetching created documentLibrary").OrNil()
}

View File

@ -0,0 +1,57 @@
package api_test
import (
"testing"
"github.com/alcionai/clues"
"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/m365/graph"
"github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/internal/tester/tconfig"
"github.com/alcionai/corso/src/pkg/control/testdata"
)
type ListsAPIIntgSuite struct {
tester.Suite
its intgTesterSetup
}
func (suite *ListsAPIIntgSuite) SetupSuite() {
suite.its = newIntegrationTesterSetup(suite.T())
}
func TestListsAPIIntgSuite(t *testing.T) {
suite.Run(t, &ListsAPIIntgSuite{
Suite: tester.NewIntegrationSuite(
t,
[][]string{tconfig.M365AcctCredEnvs}),
})
}
func (suite *ListsAPIIntgSuite) TestLists_PostDrive() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
var (
acl = suite.its.ac.Lists()
driveName = testdata.DefaultRestoreConfig("list_api_post_drive").Location
siteID = suite.its.siteID
)
// first post, should have no errors
list, err := acl.PostDrive(ctx, siteID, driveName)
require.NoError(t, err, clues.ToCore(err))
// the site name cannot be set when posting, only its DisplayName.
// so we double check here that we're still getting the name we expect.
assert.Equal(t, driveName, ptr.Val(list.GetName()))
// second post, same name, should error on name conflict]
list, err = acl.PostDrive(ctx, siteID, driveName)
require.ErrorIs(t, err, graph.ErrItemAlreadyExistsConflict, clues.ToCore(err))
}