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:
parent
57125f3ea3
commit
f4b92139bc
@ -35,6 +35,7 @@ type BackupHandler interface {
|
||||
api.Getter
|
||||
GetItemPermissioner
|
||||
GetItemer
|
||||
NewDrivePagerer
|
||||
|
||||
// PathPrefix constructs the service and category specific path prefix for
|
||||
// the given values.
|
||||
@ -49,7 +50,6 @@ type BackupHandler interface {
|
||||
|
||||
// ServiceCat returns the service and category used by this implementation.
|
||||
ServiceCat() (path.ServiceType, path.CategoryType)
|
||||
NewDrivePager(resourceOwner string, fields []string) api.DrivePager
|
||||
NewItemPager(driveID, link string, fields []string) api.DriveItemDeltaEnumerator
|
||||
// FormatDisplayPath creates a human-readable string to represent the
|
||||
// provided path.
|
||||
@ -61,6 +61,10 @@ type BackupHandler interface {
|
||||
IncludesDir(dir string) bool
|
||||
}
|
||||
|
||||
type NewDrivePagerer interface {
|
||||
NewDrivePager(resourceOwner string, fields []string) api.DrivePager
|
||||
}
|
||||
|
||||
type GetItemPermissioner interface {
|
||||
GetItemPermission(
|
||||
ctx context.Context,
|
||||
@ -86,7 +90,9 @@ type RestoreHandler interface {
|
||||
GetItemsByCollisionKeyser
|
||||
GetRootFolderer
|
||||
ItemInfoAugmenter
|
||||
NewDrivePagerer
|
||||
NewItemContentUploader
|
||||
PostDriver
|
||||
PostItemInContainerer
|
||||
DeleteItemPermissioner
|
||||
UpdateItemPermissioner
|
||||
@ -145,6 +151,13 @@ type UpdateItemLinkSharer interface {
|
||||
) (models.Permissionable, error)
|
||||
}
|
||||
|
||||
type PostDriver interface {
|
||||
PostDrive(
|
||||
ctx context.Context,
|
||||
protectedResourceID, driveName string,
|
||||
) (models.Driveable, error)
|
||||
}
|
||||
|
||||
type PostItemInContainerer interface {
|
||||
PostItemInContainer(
|
||||
ctx context.Context,
|
||||
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"github.com/microsoftgraph/msgraph-sdk-go/drives"
|
||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||
|
||||
"github.com/alcionai/clues"
|
||||
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||
odConsts "github.com/alcionai/corso/src/internal/m365/onedrive/consts"
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
@ -133,6 +134,19 @@ func NewRestoreHandler(ac api.Client) *itemRestoreHandler {
|
||||
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
|
||||
// with properties from the drive item. ItemSize is specified
|
||||
// separately for restore processes because the local itemable
|
||||
|
||||
@ -157,11 +157,25 @@ func (h libraryBackupHandler) IncludesDir(dir string) bool {
|
||||
var _ onedrive.RestoreHandler = &libraryRestoreHandler{}
|
||||
|
||||
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 {
|
||||
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(
|
||||
@ -177,21 +191,21 @@ func (h libraryRestoreHandler) DeleteItem(
|
||||
ctx context.Context,
|
||||
driveID, itemID string,
|
||||
) error {
|
||||
return h.ac.DeleteItem(ctx, driveID, itemID)
|
||||
return h.ac.Drives().DeleteItem(ctx, driveID, itemID)
|
||||
}
|
||||
|
||||
func (h libraryRestoreHandler) DeleteItemPermission(
|
||||
ctx context.Context,
|
||||
driveID, itemID, permissionID string,
|
||||
) error {
|
||||
return h.ac.DeleteItemPermission(ctx, driveID, itemID, permissionID)
|
||||
return h.ac.Drives().DeleteItemPermission(ctx, driveID, itemID, permissionID)
|
||||
}
|
||||
|
||||
func (h libraryRestoreHandler) GetItemsInContainerByCollisionKey(
|
||||
ctx context.Context,
|
||||
driveID, containerID string,
|
||||
) (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 {
|
||||
return nil, err
|
||||
}
|
||||
@ -203,7 +217,7 @@ func (h libraryRestoreHandler) NewItemContentUpload(
|
||||
ctx context.Context,
|
||||
driveID, itemID string,
|
||||
) (models.UploadSessionable, error) {
|
||||
return h.ac.NewItemContentUpload(ctx, driveID, itemID)
|
||||
return h.ac.Drives().NewItemContentUpload(ctx, driveID, itemID)
|
||||
}
|
||||
|
||||
func (h libraryRestoreHandler) PostItemPermissionUpdate(
|
||||
@ -211,7 +225,7 @@ func (h libraryRestoreHandler) PostItemPermissionUpdate(
|
||||
driveID, itemID string,
|
||||
body *drives.ItemItemsItemInvitePostRequestBody,
|
||||
) (drives.ItemItemsItemInviteResponseable, error) {
|
||||
return h.ac.PostItemPermissionUpdate(ctx, driveID, itemID, body)
|
||||
return h.ac.Drives().PostItemPermissionUpdate(ctx, driveID, itemID, body)
|
||||
}
|
||||
|
||||
func (h libraryRestoreHandler) PostItemLinkShareUpdate(
|
||||
@ -219,7 +233,7 @@ func (h libraryRestoreHandler) PostItemLinkShareUpdate(
|
||||
driveID, itemID string,
|
||||
body *drives.ItemItemsItemCreateLinkPostRequestBody,
|
||||
) (models.Permissionable, error) {
|
||||
return h.ac.PostItemLinkShareUpdate(ctx, driveID, itemID, body)
|
||||
return h.ac.Drives().PostItemLinkShareUpdate(ctx, driveID, itemID, body)
|
||||
}
|
||||
|
||||
func (h libraryRestoreHandler) PostItemInContainer(
|
||||
@ -228,21 +242,21 @@ func (h libraryRestoreHandler) PostItemInContainer(
|
||||
newItem models.DriveItemable,
|
||||
onCollision control.CollisionPolicy,
|
||||
) (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(
|
||||
ctx context.Context,
|
||||
driveID, parentFolderID, folderName string,
|
||||
) (models.DriveItemable, error) {
|
||||
return h.ac.GetFolderByName(ctx, driveID, parentFolderID, folderName)
|
||||
return h.ac.Drives().GetFolderByName(ctx, driveID, parentFolderID, folderName)
|
||||
}
|
||||
|
||||
func (h libraryRestoreHandler) GetRootFolder(
|
||||
ctx context.Context,
|
||||
driveID string,
|
||||
) (models.DriveItemable, error) {
|
||||
return h.ac.GetRootFolder(ctx, driveID)
|
||||
return h.ac.Drives().GetRootFolder(ctx, driveID)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
64
src/pkg/services/m365/api/lists.go
Normal file
64
src/pkg/services/m365/api/lists.go
Normal 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()
|
||||
}
|
||||
57
src/pkg/services/m365/api/lists_test.go
Normal file
57
src/pkg/services/m365/api/lists_test.go
Normal 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))
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user