Abin Simon 1f0338a80d
Use http client from msgraphcore (#1484)
## Description

Use http client from msgraphcore instead of the one from net/http as it has preconfigured middleware.

## Type of change

<!--- Please check the type of change your PR introduces: --->
- [ ] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Test
- [ ] 💻 CI/Deployment
- [x] 🐹 Trivial/Minor

## Issue(s)

<!-- Can reference multiple issues. Use one of the following "magic words" - "closes, fixes" to auto-close the Github issue. -->
* https://github.com/alcionai/corso/issues/581

## Test Plan

<!-- How will this be tested prior to merging.-->
- [ ] 💪 Manual
- [ ]  Unit test
- [x] 💚 E2E
2022-11-18 12:01:12 +00:00

119 lines
4.0 KiB
Go

package onedrive
import (
"context"
"io"
"time"
msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go"
msgraphgocore "github.com/microsoftgraph/msgraph-sdk-go-core"
msup "github.com/microsoftgraph/msgraph-sdk-go/drives/item/items/item/createuploadsession"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/pkg/errors"
"github.com/alcionai/corso/src/internal/common"
"github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/internal/connector/support"
"github.com/alcionai/corso/src/internal/connector/uploadsession"
"github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/logger"
)
const (
// downloadUrlKey is used to find the download URL in a
// DriveItem response
downloadURLKey = "@microsoft.graph.downloadUrl"
)
// itemReader will return a io.ReadCloser for the specified item
// It crafts this by querying M365 for a download URL for the item
// and using a http client to initialize a reader
func driveItemReader(
ctx context.Context,
service graph.Service,
driveID, itemID string,
) (*details.OneDriveInfo, io.ReadCloser, error) {
logger.Ctx(ctx).Debugf("Reading Item %s at %s", itemID, time.Now())
item, err := service.Client().DrivesById(driveID).ItemsById(itemID).Get(ctx, nil)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to get item %s", itemID)
}
logger.Ctx(ctx).Debugw("reading item", "name", *item.GetName(), "time", common.Now())
// Get the download URL - https://docs.microsoft.com/en-us/graph/api/driveitem-get-content
// These URLs are pre-authenticated and can be used to download the data using the standard
// http client
if _, found := item.GetAdditionalData()[downloadURLKey]; !found {
return nil, nil, errors.Errorf("file does not have a download URL. ID: %s, %#v",
itemID, item.GetAdditionalData())
}
downloadURL := item.GetAdditionalData()[downloadURLKey].(*string)
clientOptions := msgraphsdk.GetDefaultClientOptions()
middlewares := msgraphgocore.GetDefaultMiddlewaresWithOptions(&clientOptions)
httpClient := msgraphgocore.GetDefaultClient(&clientOptions, middlewares...)
httpClient.Timeout = 0 // need infinite timeout for pulling large files
resp, err := httpClient.Get(*downloadURL)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to download file from %s", *downloadURL)
}
return driveItemInfo(item, *item.GetSize()), resp.Body, nil
}
// driveItemInfo will populate a details.OneDriveInfo struct
// with properties from the drive item. ItemSize is specified
// separately for restore processes because the local itemable
// doesn't have its size value updated as a side effect of creation,
// and kiota drops any SetSize update.
func driveItemInfo(di models.DriveItemable, itemSize int64) *details.OneDriveInfo {
ed, ok := di.GetCreatedBy().GetUser().GetAdditionalData()["email"]
email := ""
if ok {
email = *ed.(*string)
}
return &details.OneDriveInfo{
ItemType: details.OneDriveItem,
ItemName: *di.GetName(),
Created: *di.GetCreatedDateTime(),
Modified: *di.GetLastModifiedDateTime(),
Size: itemSize,
Owner: email,
}
}
// driveItemWriter is used to initialize and return an io.Writer to upload data for the specified item
// It does so by creating an upload session and using that URL to initialize an `itemWriter`
func driveItemWriter(
ctx context.Context,
service graph.Service,
driveID, itemID string,
itemSize int64,
) (io.Writer, error) {
// TODO: @vkamra verify if var session is the desired input
session := msup.NewCreateUploadSessionPostRequestBody()
r, err := service.Client().DrivesById(driveID).ItemsById(itemID).CreateUploadSession().Post(ctx, session, nil)
if err != nil {
return nil, errors.Wrapf(
err,
"failed to create upload session for item %s. details: %s",
itemID,
support.ConnectorStackErrorTrace(err),
)
}
url := *r.GetUploadUrl()
logger.Ctx(ctx).Debugf("Created an upload session for item %s. URL: %s", itemID, url)
return uploadsession.NewWriter(itemID, url, itemSize), nil
}