add iteminfo to restore details (#1041)

## Description

Extends the restore details with itemInfo from restored items.

## Type of change

- [x] 🌻 Feature

## Issue(s)

* #977

## Test Plan

- [x] 💪 Manual
- [x] 💚 E2E
This commit is contained in:
Keepers 2022-10-05 15:19:21 -06:00 committed by GitHub
parent 010d8d5df1
commit f4556288a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 96 additions and 65 deletions

View File

@ -373,15 +373,24 @@ func (suite *ExchangeServiceSuite) TestGetContainerID() {
// is able to restore a several messageable item to a Mailbox. // is able to restore a several messageable item to a Mailbox.
// The result should be all successful items restored within the same folder. // The result should be all successful items restored within the same folder.
func (suite *ExchangeServiceSuite) TestRestoreMessages() { func (suite *ExchangeServiceSuite) TestRestoreMessages() {
ctx := context.Background() var (
userID := tester.M365UserID(suite.T()) ctx = context.Background()
now := time.Now() userID = tester.M365UserID(suite.T())
now = time.Now()
folderName = "TestRestoreMessage: " + common.FormatSimpleDateTime(now)
)
folderName := "TestRestoreMessage: " + common.FormatSimpleDateTime(now)
folder, err := CreateMailFolder(ctx, suite.es, userID, folderName) folder, err := CreateMailFolder(ctx, suite.es, userID, folderName)
require.NoError(suite.T(), err) require.NoError(suite.T(), err)
folderID := *folder.GetId() folderID := *folder.GetId()
defer func() {
// Remove the folder containing message prior to exiting test
err = DeleteMailFolder(ctx, suite.es, userID, folderID)
assert.NoError(suite.T(), err, "Failure during folder clean-up")
}()
tests := []struct { tests := []struct {
name string name string
bytes []byte bytes []byte
@ -402,67 +411,80 @@ func (suite *ExchangeServiceSuite) TestRestoreMessages() {
for _, test := range tests { for _, test := range tests {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
err = RestoreMailMessage(context.Background(), info, err := RestoreMailMessage(context.Background(),
test.bytes, test.bytes,
suite.es, suite.es,
control.Copy, control.Copy,
folderID, folderID,
userID, userID,
) )
require.NoError(t, err, support.ConnectorStackErrorTrace(err)) assert.NoError(t, err, support.ConnectorStackErrorTrace(err))
assert.NotNil(t, info, "message item info")
}) })
} }
err = DeleteMailFolder(ctx, suite.es, userID, folderID)
assert.NoError(suite.T(), err, "Failure during folder clean-up")
} }
// TestRestoreContact ensures contact object can be created, placed into // TestRestoreContact ensures contact object can be created, placed into
// the Corso Folder. The function handles test clean-up. // the Corso Folder. The function handles test clean-up.
func (suite *ExchangeServiceSuite) TestRestoreContact() { func (suite *ExchangeServiceSuite) TestRestoreContact() {
t := suite.T() var (
ctx := context.Background() t = suite.T()
userID := tester.M365UserID(t) ctx = context.Background()
now := time.Now() userID = tester.M365UserID(t)
now = time.Now()
folderName = "TestRestoreContact: " + common.FormatSimpleDateTime(now)
)
folderName := "TestRestoreContact: " + common.FormatSimpleDateTime(now)
aFolder, err := CreateContactFolder(ctx, suite.es, userID, folderName) aFolder, err := CreateContactFolder(ctx, suite.es, userID, folderName)
require.NoError(t, err) require.NoError(t, err)
folderID := *aFolder.GetId() folderID := *aFolder.GetId()
err = RestoreExchangeContact(context.Background(),
defer func() {
// Remove the folder containing contact prior to exiting test
err = DeleteContactFolder(ctx, suite.es, userID, folderID)
assert.NoError(t, err)
}()
info, err := RestoreExchangeContact(context.Background(),
mockconnector.GetMockContactBytes("Corso TestContact"), mockconnector.GetMockContactBytes("Corso TestContact"),
suite.es, suite.es,
control.Copy, control.Copy,
folderID, folderID,
userID) userID)
assert.NoError(t, err) assert.NoError(t, err, support.ConnectorStackErrorTrace(err))
// Removes folder containing contact prior to exiting test assert.NotNil(t, info, "contact item info")
err = DeleteContactFolder(ctx, suite.es, userID, folderID)
assert.NoError(t, err)
} }
// TestRestoreEvent verifies that event object is able to created // TestRestoreEvent verifies that event object is able to created
// and sent into the test account of the Corso user in the newly created Corso Calendar // and sent into the test account of the Corso user in the newly created Corso Calendar
func (suite *ExchangeServiceSuite) TestRestoreEvent() { func (suite *ExchangeServiceSuite) TestRestoreEvent() {
t := suite.T() var (
ctx := context.Background() t = suite.T()
userID := tester.M365UserID(t) ctx = context.Background()
name := "TestRestoreEvent: " + common.FormatSimpleDateTime(time.Now()) userID = tester.M365UserID(t)
name = "TestRestoreEvent: " + common.FormatSimpleDateTime(time.Now())
)
calendar, err := CreateCalendar(ctx, suite.es, userID, name) calendar, err := CreateCalendar(ctx, suite.es, userID, name)
require.NoError(t, err) require.NoError(t, err)
calendarID := *calendar.GetId() calendarID := *calendar.GetId()
err = RestoreExchangeEvent(context.Background(),
defer func() {
// Removes calendar containing events created during the test
err = DeleteCalendar(ctx, suite.es, userID, calendarID)
assert.NoError(t, err)
}()
info, err := RestoreExchangeEvent(context.Background(),
mockconnector.GetMockEventWithAttendeesBytes(name), mockconnector.GetMockEventWithAttendeesBytes(name),
suite.es, suite.es,
control.Copy, control.Copy,
calendarID, calendarID,
userID) userID)
assert.NoError(t, err) assert.NoError(t, err, support.ConnectorStackErrorTrace(err))
// Removes calendar containing events created during the test assert.NotNil(t, info, "event item info")
err = DeleteCalendar(ctx, suite.es, userID, *calendar.GetId())
assert.NoError(t, err)
} }
// TestGetRestoreContainer checks the ability to Create a "container" for the // TestGetRestoreContainer checks the ability to Create a "container" for the
@ -585,7 +607,7 @@ func (suite *ExchangeServiceSuite) TestRestoreExchangeObject() {
for _, test := range tests { for _, test := range tests {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
destination := test.destination() destination := test.destination()
err := RestoreExchangeObject( info, err := RestoreExchangeObject(
ctx, ctx,
test.bytes, test.bytes,
test.category, test.category,
@ -594,7 +616,9 @@ func (suite *ExchangeServiceSuite) TestRestoreExchangeObject() {
destination, destination,
userID, userID,
) )
assert.NoError(t, err) assert.NoError(t, err, support.ConnectorStackErrorTrace(err))
assert.NotNil(t, info, "item info is populated")
cleanupError := test.cleanupFunc(ctx, service, userID, destination) cleanupError := test.cleanupFunc(ctx, service, userID, destination)
assert.NoError(t, cleanupError) assert.NoError(t, cleanupError)
}) })

View File

@ -78,9 +78,9 @@ func RestoreExchangeObject(
policy control.CollisionPolicy, policy control.CollisionPolicy,
service graph.Service, service graph.Service,
destination, user string, destination, user string,
) error { ) (*details.ExchangeInfo, error) {
if policy != control.Copy { if policy != control.Copy {
return fmt.Errorf("restore policy: %s not supported for RestoreExchangeObject", policy) return nil, fmt.Errorf("restore policy: %s not supported for RestoreExchangeObject", policy)
} }
setting := categoryToOptionIdentifier(category) setting := categoryToOptionIdentifier(category)
@ -93,7 +93,7 @@ func RestoreExchangeObject(
case events: case events:
return RestoreExchangeEvent(ctx, bits, service, control.Copy, destination, user) return RestoreExchangeEvent(ctx, bits, service, control.Copy, destination, user)
default: default:
return fmt.Errorf("type: %s not supported for RestoreExchangeObject", category) return nil, fmt.Errorf("type: %s not supported for RestoreExchangeObject", category)
} }
} }
@ -109,17 +109,17 @@ func RestoreExchangeContact(
service graph.Service, service graph.Service,
cp control.CollisionPolicy, cp control.CollisionPolicy,
destination, user string, destination, user string,
) error { ) (*details.ExchangeInfo, error) {
contact, err := support.CreateContactFromBytes(bits) contact, err := support.CreateContactFromBytes(bits)
if err != nil { if err != nil {
return errors.Wrap(err, "failure to create contact from bytes: RestoreExchangeContact") return nil, errors.Wrap(err, "failure to create contact from bytes: RestoreExchangeContact")
} }
response, err := service.Client().UsersById(user).ContactFoldersById(destination).Contacts().Post(ctx, contact, nil) response, err := service.Client().UsersById(user).ContactFoldersById(destination).Contacts().Post(ctx, contact, nil)
if err != nil { if err != nil {
name := *contact.GetGivenName() name := *contact.GetGivenName()
return errors.Wrap( return nil, errors.Wrap(
err, err,
"failure to create Contact during RestoreExchangeContact: "+name+" "+ "failure to create Contact during RestoreExchangeContact: "+name+" "+
support.ConnectorStackErrorTrace(err), support.ConnectorStackErrorTrace(err),
@ -127,10 +127,10 @@ func RestoreExchangeContact(
} }
if response == nil { if response == nil {
return errors.New("msgraph contact post fail: REST response not received") return nil, errors.New("msgraph contact post fail: REST response not received")
} }
return nil return ContactInfo(contact), nil
} }
// RestoreExchangeEvent restores a contact to the @bits byte // RestoreExchangeEvent restores a contact to the @bits byte
@ -145,17 +145,17 @@ func RestoreExchangeEvent(
service graph.Service, service graph.Service,
cp control.CollisionPolicy, cp control.CollisionPolicy,
destination, user string, destination, user string,
) error { ) (*details.ExchangeInfo, error) {
event, err := support.CreateEventFromBytes(bits) event, err := support.CreateEventFromBytes(bits)
if err != nil { if err != nil {
return err return nil, err
} }
transformedEvent := support.ToEventSimplified(event) transformedEvent := support.ToEventSimplified(event)
response, err := service.Client().UsersById(user).CalendarsById(destination).Events().Post(ctx, transformedEvent, nil) response, err := service.Client().UsersById(user).CalendarsById(destination).Events().Post(ctx, transformedEvent, nil)
if err != nil { if err != nil {
return errors.Wrap(err, return nil, errors.Wrap(err,
fmt.Sprintf( fmt.Sprintf(
"failure to event creation failure during RestoreExchangeEvent: %s", "failure to event creation failure during RestoreExchangeEvent: %s",
support.ConnectorStackErrorTrace(err)), support.ConnectorStackErrorTrace(err)),
@ -163,10 +163,10 @@ func RestoreExchangeEvent(
} }
if response == nil { if response == nil {
return errors.New("msgraph event post fail: REST response not received") return nil, errors.New("msgraph event post fail: REST response not received")
} }
return nil return EventInfo(event), nil
} }
// RestoreMailMessage utility function to place an exchange.Mail // RestoreMailMessage utility function to place an exchange.Mail
@ -182,11 +182,11 @@ func RestoreMailMessage(
cp control.CollisionPolicy, cp control.CollisionPolicy,
destination, destination,
user string, user string,
) error { ) (*details.ExchangeInfo, error) {
// Creates messageable object from original bytes // Creates messageable object from original bytes
originalMessage, err := support.CreateMessageFromBytes(bits) originalMessage, err := support.CreateMessageFromBytes(bits)
if err != nil { if err != nil {
return err return nil, err
} }
// Sets fields from original message from storage // Sets fields from original message from storage
clone := support.ToMessage(originalMessage) clone := support.ToMessage(originalMessage)
@ -223,7 +223,7 @@ func RestoreMailMessage(
"policy", cp) "policy", cp)
fallthrough fallthrough
case control.Copy: case control.Copy:
return SendMailToBackStore(ctx, service, user, destination, clone) return MessageInfo(clone), SendMailToBackStore(ctx, service, user, destination, clone)
} }
} }
@ -383,7 +383,7 @@ func restoreCollection(
byteArray := buf.Bytes() byteArray := buf.Bytes()
err = RestoreExchangeObject(ctx, byteArray, category, policy, gs, folderID, user) info, err := RestoreExchangeObject(ctx, byteArray, category, policy, gs, folderID, user)
if err != nil { if err != nil {
// More information to be here // More information to be here
errUpdater( errUpdater(
@ -406,7 +406,9 @@ func restoreCollection(
itemPath.String(), itemPath.String(),
itemPath.ShortRef(), itemPath.ShortRef(),
"", "",
details.ItemInfo{}) details.ItemInfo{
Exchange: info,
})
} }
} }
} }

View File

@ -47,10 +47,6 @@ func TestGraphConnectorIntegrationSuite(t *testing.T) {
} }
func (suite *GraphConnectorIntegrationSuite) SetupSuite() { func (suite *GraphConnectorIntegrationSuite) SetupSuite() {
if err := tester.RunOnAny(tester.CorsoCITests); err != nil {
suite.T().Skip(err)
}
ctx := context.Background() ctx := context.Background()
_, err := tester.GetRequiredEnvVars(tester.M365AcctCredEnvs...) _, err := tester.GetRequiredEnvVars(tester.M365AcctCredEnvs...)
require.NoError(suite.T(), err) require.NoError(suite.T(), err)

View File

@ -9,6 +9,7 @@ import (
"time" "time"
msup "github.com/microsoftgraph/msgraph-sdk-go/drives/item/items/item/createuploadsession" 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/pkg/errors"
"gopkg.in/resty.v1" "gopkg.in/resty.v1"
@ -56,13 +57,19 @@ func driveItemReader(
return nil, 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 driveItemInfo(item), resp.Body, nil
}
// driveItemInfo will populate a details.OneDriveInfo struct
// with properties from the drive item.
func driveItemInfo(di models.DriveItemable) *details.OneDriveInfo {
return &details.OneDriveInfo{ return &details.OneDriveInfo{
ItemType: details.OneDriveItem, ItemType: details.OneDriveItem,
ItemName: *item.GetName(), ItemName: *di.GetName(),
Created: *item.GetCreatedDateTime(), Created: *di.GetCreatedDateTime(),
Modified: *item.GetLastModifiedDateTime(), Modified: *di.GetLastModifiedDateTime(),
Size: *item.GetSize(), Size: *di.GetSize(),
}, resp.Body, nil }
} }
// driveItemWriter is used to initialize and return an io.Writer to upload data for the specified item // driveItemWriter is used to initialize and return an io.Writer to upload data for the specified item

View File

@ -142,7 +142,7 @@ func restoreCollection(
metrics.TotalBytes += int64(len(copyBuffer)) metrics.TotalBytes += int64(len(copyBuffer))
err := restoreItem(ctx, itemInfo, err := restoreItem(ctx,
service, service,
itemData, itemData,
drivePath.driveID, drivePath.driveID,
@ -165,7 +165,9 @@ func restoreCollection(
itemPath.String(), itemPath.String(),
itemPath.ShortRef(), itemPath.ShortRef(),
"", "",
details.ItemInfo{}) details.ItemInfo{
OneDrive: itemInfo,
})
metrics.Successes++ metrics.Successes++
} }
@ -224,7 +226,7 @@ func restoreItem(
itemData data.Stream, itemData data.Stream,
driveID, parentFolderID string, driveID, parentFolderID string,
copyBuffer []byte, copyBuffer []byte,
) error { ) (*details.OneDriveInfo, error) {
defer trace.StartRegion(ctx, "gc:oneDrive:restoreItem").End() defer trace.StartRegion(ctx, "gc:oneDrive:restoreItem").End()
itemName := itemData.UUID() itemName := itemData.UUID()
@ -233,26 +235,26 @@ func restoreItem(
// Get the stream size (needed to create the upload session) // Get the stream size (needed to create the upload session)
ss, ok := itemData.(data.StreamSize) ss, ok := itemData.(data.StreamSize)
if !ok { if !ok {
return errors.Errorf("item %q does not implement DataStreamInfo", itemName) return nil, errors.Errorf("item %q does not implement DataStreamInfo", itemName)
} }
// Create Item // Create Item
newItem, err := createItem(ctx, service, driveID, parentFolderID, newItem(itemData.UUID(), false)) newItem, err := createItem(ctx, service, driveID, parentFolderID, newItem(itemData.UUID(), false))
if err != nil { if err != nil {
return errors.Wrapf(err, "failed to create item %s", itemName) return nil, errors.Wrapf(err, "failed to create item %s", itemName)
} }
// Get a drive item writer // Get a drive item writer
w, err := driveItemWriter(ctx, service, driveID, *newItem.GetId(), ss.Size()) w, err := driveItemWriter(ctx, service, driveID, *newItem.GetId(), ss.Size())
if err != nil { if err != nil {
return errors.Wrapf(err, "failed to create item upload session %s", itemName) return nil, errors.Wrapf(err, "failed to create item upload session %s", itemName)
} }
// Upload the stream data // Upload the stream data
_, err = io.CopyBuffer(w, itemData.ToReader(), copyBuffer) _, err = io.CopyBuffer(w, itemData.ToReader(), copyBuffer)
if err != nil { if err != nil {
return errors.Wrapf(err, "failed to upload data: item %s", itemName) return nil, errors.Wrapf(err, "failed to upload data: item %s", itemName)
} }
return nil return driveItemInfo(newItem), nil
} }