Danny 3f3c8217a3
GC: Component: Version 40.0 (#917)
## Description
Upgrades Graph API to the newest version. Breaking change from the previous version, all functionality needs to be inspected to ensure operation.  
<!-- Insert PR description-->

## Type of change

<!--- Please check the type of change your PR introduces: --->
- [x] 🌻 Feature


## Issue(s)

<!-- Can reference multiple issues. Use one of the following "magic words" - "closes, fixes" to auto-close the Github issue. -->
*closes #828<issue>

## Test Plan

- [x]  Unit test
- [x] 💚 E2E
2022-09-22 18:59:51 +00:00

142 lines
4.1 KiB
Go

package onedrive
import (
"context"
"fmt"
"github.com/microsoftgraph/msgraph-sdk-go/drives/item/root/delta"
"github.com/microsoftgraph/msgraph-sdk-go/me/drives/item/items"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/pkg/errors"
"github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/internal/connector/support"
"github.com/alcionai/corso/src/pkg/logger"
)
var errFolderNotFound = errors.New("folder not found")
const (
// nextLinkKey is used to find the next link in a paged
// graph response
nextLinkKey = "@odata.nextLink"
itemChildrenRawURLFmt = "https://graph.microsoft.com/v1.0/drives/%s/items/%s/children"
)
// Enumerates the drives for the specified user
func drives(ctx context.Context, service graph.Service, user string) ([]models.Driveable, error) {
r, err := service.Client().UsersById(user).Drives().Get(ctx, nil)
if err != nil {
return nil, errors.Wrapf(err, "failed to retrieve user drives. user: %s, details: %s",
user, support.ConnectorStackErrorTrace(err))
}
logger.Ctx(ctx).Debugf("Found %d drives for user %s", len(r.GetValue()), user)
return r.GetValue(), nil
}
// itemCollector functions collect the items found in a drive
type itemCollector func(ctx context.Context, driveID string, driveItems []models.DriveItemable) error
// collectItems will enumerate all items in the specified drive and hand them to the
// provided `collector` method
func collectItems(
ctx context.Context,
service graph.Service,
driveID string,
collector itemCollector,
) error {
// TODO: Specify a timestamp in the delta query
// https://docs.microsoft.com/en-us/graph/api/driveitem-delta?
// view=graph-rest-1.0&tabs=http#example-4-retrieving-delta-results-using-a-timestamp
builder := service.Client().DrivesById(driveID).Root().Delta()
for {
r, err := builder.Get(ctx, nil)
if err != nil {
return errors.Wrapf(
err,
"failed to query drive items. details: %s",
support.ConnectorStackErrorTrace(err),
)
}
err = collector(ctx, driveID, r.GetValue())
if err != nil {
return err
}
// Check if there are more items
if _, found := r.GetAdditionalData()[nextLinkKey]; !found {
break
}
nextLink := r.GetAdditionalData()[nextLinkKey].(*string)
logger.Ctx(ctx).Debugf("Found %s nextLink", *nextLink)
builder = delta.NewDeltaRequestBuilder(*nextLink, service.Adapter())
}
return nil
}
// getFolder will lookup the specified folder name under `parentFolderID`
func getFolder(ctx context.Context, service graph.Service, driveID string, parentFolderID string,
folderName string,
) (models.DriveItemable, error) {
children, err := service.Client().DrivesById(driveID).ItemsById(parentFolderID).Children().Get(ctx, nil)
if err != nil {
return nil, errors.Wrapf(
err,
"failed to get children. details: %s",
support.ConnectorStackErrorTrace(err),
)
}
for _, item := range children.GetValue() {
if item.GetFolder() == nil || item.GetName() == nil || *item.GetName() != folderName {
continue
}
return item, nil
}
return nil, errors.WithStack(errFolderNotFound)
}
// Create a new item in the specified folder
func createItem(ctx context.Context, service graph.Service, driveID string, parentFolderID string,
item models.DriveItemable,
) (models.DriveItemable, error) {
// Graph SDK doesn't yet provide a POST method for `/children` so we set the `rawUrl` ourselves as recommended
// here: https://github.com/microsoftgraph/msgraph-sdk-go/issues/155#issuecomment-1136254310
rawURL := fmt.Sprintf(itemChildrenRawURLFmt, driveID, parentFolderID)
builder := items.NewItemsRequestBuilder(rawURL, service.Adapter())
newItem, err := builder.Post(ctx, item, nil)
if err != nil {
return nil, errors.Wrapf(
err,
"failed to create item. details: %s",
support.ConnectorStackErrorTrace(err),
)
}
return newItem, nil
}
// newItem initializes a `models.DriveItemable` that can be used as input to `createItem`
func newItem(name string, folder bool) models.DriveItemable {
item := models.NewDriveItem()
item.SetName(&name)
if folder {
item.SetFolder(models.NewFolder())
} else {
item.SetFile(models.NewFile())
}
return item
}