Add additional OneDrive metadata to backup details (#952)

## Description

Adds file size and timestamps to enable selectors using this metadata

## Type of change

- [x] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Test
- [ ] 💻 CI/Deployment
- [ ] 🐹 Trivial/Minor

## Issue(s)

* #594 

## Test Plan

<!-- How will this be tested prior to merging.-->
- [ ] 💪 Manual
- [x]  Unit test
- [x] 💚 E2E
This commit is contained in:
Vaibhav Kamra 2022-09-26 15:54:42 -07:00 committed by GitHub
parent ea73873ffb
commit 1df48997ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 47 additions and 29 deletions

View File

@ -45,7 +45,7 @@ type itemReaderFunc func(
ctx context.Context,
service graph.Service,
driveID, itemID string,
) (name string, itemData io.ReadCloser, err error)
) (itemInfo *details.OneDriveInfo, itemData io.ReadCloser, err error)
// NewCollection creates a Collection
func NewCollection(
@ -113,7 +113,7 @@ func (oc *Collection) populateItems(ctx context.Context) {
for _, itemID := range oc.driveItemIDs {
// Read the item
itemName, itemData, err := oc.itemReader(ctx, oc.service, oc.driveID, itemID)
itemInfo, itemData, err := oc.itemReader(ctx, oc.service, oc.driveID, itemID)
if err != nil {
errs = support.WrapAndAppendf(itemID, err, errs)
@ -125,14 +125,13 @@ func (oc *Collection) populateItems(ctx context.Context) {
}
// Item read successfully, add to collection
itemsRead++
itemInfo.ParentPath = oc.folderPath.String()
oc.data <- &Item{
id: itemName,
id: itemInfo.ItemName,
data: itemData,
info: &details.OneDriveInfo{
ItemType: details.OneDriveItem,
ItemName: itemName,
ParentPath: oc.folderPath.String(),
},
info: itemInfo,
}
}

View File

@ -16,6 +16,7 @@ import (
"github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/internal/connector/support"
"github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/pkg/backup/details"
)
type OneDriveCollectionSuite struct {
@ -73,8 +74,8 @@ func (suite *OneDriveCollectionSuite) TestOneDriveCollection() {
// Set a item reader, add an item and validate we get the item back
coll.Add(testItemID)
coll.itemReader = func(context.Context, graph.Service, string, string) (string, io.ReadCloser, error) {
return testItemName, io.NopCloser(bytes.NewReader(testItemData)), nil
coll.itemReader = func(context.Context, graph.Service, string, string) (*details.OneDriveInfo, io.ReadCloser, error) {
return &details.OneDriveInfo{ItemName: testItemName}, io.NopCloser(bytes.NewReader(testItemData)), nil
}
// Read items from the collection
@ -122,8 +123,8 @@ func (suite *OneDriveCollectionSuite) TestOneDriveCollectionReadError() {
readError := errors.New("Test error")
coll.itemReader = func(context.Context, graph.Service, string, string) (name string, data io.ReadCloser, err error) {
return "", nil, readError
coll.itemReader = func(context.Context, graph.Service, string, string) (*details.OneDriveInfo, io.ReadCloser, error) {
return nil, nil, readError
}
coll.Items()

View File

@ -14,6 +14,7 @@ import (
"github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/internal/connector/support"
"github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/logger"
)
@ -30,19 +31,19 @@ func driveItemReader(
ctx context.Context,
service graph.Service,
driveID, itemID string,
) (string, io.ReadCloser, error) {
) (*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, errors.Wrapf(err, "failed to get item %s", itemID)
return nil, nil, errors.Wrapf(err, "failed to get item %s", itemID)
}
// 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, errors.Errorf("file does not have a download URL. ID: %s, %#v",
return nil, nil, errors.Errorf("file does not have a download URL. ID: %s, %#v",
itemID, item.GetAdditionalData())
}
@ -52,10 +53,16 @@ func driveItemReader(
// middleware/options configured
resp, err := http.Get(*downloadURL)
if err != nil {
return "", nil, errors.Wrapf(err, "failed to download file from %s", *downloadURL)
return nil, nil, errors.Wrapf(err, "failed to download file from %s", *downloadURL)
}
return *item.GetName(), resp.Body, nil
return &details.OneDriveInfo{
ItemType: details.OneDriveItem,
ItemName: *item.GetName(),
Created: *item.GetCreatedDateTime(),
LastModified: *item.GetLastModifiedDateTime(),
Size: *item.GetSize(),
}, resp.Body, nil
}
// driveItemWriter is used to initialize and return an io.Writer to upload data for the specified item

View File

@ -102,14 +102,16 @@ func (suite *ItemIntegrationSuite) TestItemReader() {
)
// Read data for the file
name, itemData, err := driveItemReader(ctx, suite, driveID, driveItemID)
itemInfo, itemData, err := driveItemReader(ctx, suite, driveID, driveItemID)
require.NoError(suite.T(), err)
require.NotEmpty(suite.T(), name)
require.NotNil(suite.T(), itemInfo)
require.NotEmpty(suite.T(), itemInfo.ItemName)
size, err := io.Copy(io.Discard, itemData)
require.NoError(suite.T(), err)
require.NotZero(suite.T(), size)
suite.T().Logf("Read %d bytes from file %s.", size, name)
require.Equal(suite.T(), size, itemInfo.Size)
suite.T().Logf("Read %d bytes from file %s.", size, itemInfo.ItemName)
}
// TestItemWriter is an integration test for uploading data to OneDrive

View File

@ -364,16 +364,22 @@ type OneDriveInfo struct {
ItemType ItemType `json:"itemType,omitempty"`
ParentPath string `json:"parentPath"`
ItemName string `json:"itemName"`
Size int64 `json:"size,omitempty"`
Created time.Time `json:"created,omitempty"`
LastModified time.Time `json:"lastModified,omitempty"`
}
// Headers returns the human-readable names of properties in a OneDriveInfo
// for printing out to a terminal in a columnar display.
func (i OneDriveInfo) Headers() []string {
return []string{"ItemName", "ParentPath"}
return []string{"ItemName", "ParentPath", "Size", "Created", "LastModified"}
}
// Values returns the values matching the Headers list for printing
// out to a terminal in a columnar display.
func (i OneDriveInfo) Values() []string {
return []string{i.ItemName, i.ParentPath}
return []string{
i.ItemName, i.ParentPath, strconv.FormatInt(i.Size, 10),
common.FormatTabularDisplayTime(i.Created), common.FormatTabularDisplayTime(i.LastModified),
}
}

View File

@ -114,11 +114,14 @@ func (suite *DetailsUnitSuite) TestDetailsEntry_HeadersValues() {
OneDrive: &details.OneDriveInfo{
ItemName: "itemName",
ParentPath: "parentPath",
Size: 1000,
Created: now,
LastModified: now,
},
},
},
expectHs: []string{"Reference", "ItemName", "ParentPath"},
expectVs: []string{"deadbeef", "itemName", "parentPath"},
expectHs: []string{"Reference", "ItemName", "ParentPath", "Size", "Created", "LastModified"},
expectVs: []string{"deadbeef", "itemName", "parentPath", "1000", nowStr, nowStr},
},
}