From f4556288a516c0d4af8e6d4ecf98273a4cd14bbe Mon Sep 17 00:00:00 2001 From: Keepers Date: Wed, 5 Oct 2022 15:19:21 -0600 Subject: [PATCH] add iteminfo to restore details (#1041) ## Description Extends the restore details with itemInfo from restored items. ## Type of change - [x] :sunflower: Feature ## Issue(s) * #977 ## Test Plan - [x] :muscle: Manual - [x] :green_heart: E2E --- .../exchange/exchange_service_test.go | 84 ++++++++++++------- .../connector/exchange/service_restore.go | 38 +++++---- .../connector/graph_connector_test.go | 4 - src/internal/connector/onedrive/item.go | 17 ++-- src/internal/connector/onedrive/restore.go | 18 ++-- 5 files changed, 96 insertions(+), 65 deletions(-) diff --git a/src/internal/connector/exchange/exchange_service_test.go b/src/internal/connector/exchange/exchange_service_test.go index a0d1ad26a..409ba5e88 100644 --- a/src/internal/connector/exchange/exchange_service_test.go +++ b/src/internal/connector/exchange/exchange_service_test.go @@ -373,15 +373,24 @@ func (suite *ExchangeServiceSuite) TestGetContainerID() { // is able to restore a several messageable item to a Mailbox. // The result should be all successful items restored within the same folder. func (suite *ExchangeServiceSuite) TestRestoreMessages() { - ctx := context.Background() - userID := tester.M365UserID(suite.T()) - now := time.Now() + var ( + ctx = context.Background() + 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) require.NoError(suite.T(), err) 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 { name string bytes []byte @@ -402,67 +411,80 @@ func (suite *ExchangeServiceSuite) TestRestoreMessages() { for _, test := range tests { suite.T().Run(test.name, func(t *testing.T) { - err = RestoreMailMessage(context.Background(), + info, err := RestoreMailMessage(context.Background(), test.bytes, suite.es, control.Copy, folderID, 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 // the Corso Folder. The function handles test clean-up. func (suite *ExchangeServiceSuite) TestRestoreContact() { - t := suite.T() - ctx := context.Background() - userID := tester.M365UserID(t) - now := time.Now() + var ( + t = suite.T() + ctx = context.Background() + 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) require.NoError(t, err) 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"), suite.es, control.Copy, folderID, userID) - assert.NoError(t, err) - // Removes folder containing contact prior to exiting test - err = DeleteContactFolder(ctx, suite.es, userID, folderID) - assert.NoError(t, err) + assert.NoError(t, err, support.ConnectorStackErrorTrace(err)) + assert.NotNil(t, info, "contact item info") } // 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 func (suite *ExchangeServiceSuite) TestRestoreEvent() { - t := suite.T() - ctx := context.Background() - userID := tester.M365UserID(t) - name := "TestRestoreEvent: " + common.FormatSimpleDateTime(time.Now()) + var ( + t = suite.T() + ctx = context.Background() + userID = tester.M365UserID(t) + name = "TestRestoreEvent: " + common.FormatSimpleDateTime(time.Now()) + ) + calendar, err := CreateCalendar(ctx, suite.es, userID, name) require.NoError(t, err) 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), suite.es, control.Copy, calendarID, userID) - assert.NoError(t, err) - // Removes calendar containing events created during the test - err = DeleteCalendar(ctx, suite.es, userID, *calendar.GetId()) - assert.NoError(t, err) + assert.NoError(t, err, support.ConnectorStackErrorTrace(err)) + assert.NotNil(t, info, "event item info") } // TestGetRestoreContainer checks the ability to Create a "container" for the @@ -585,7 +607,7 @@ func (suite *ExchangeServiceSuite) TestRestoreExchangeObject() { for _, test := range tests { suite.T().Run(test.name, func(t *testing.T) { destination := test.destination() - err := RestoreExchangeObject( + info, err := RestoreExchangeObject( ctx, test.bytes, test.category, @@ -594,7 +616,9 @@ func (suite *ExchangeServiceSuite) TestRestoreExchangeObject() { destination, 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) assert.NoError(t, cleanupError) }) diff --git a/src/internal/connector/exchange/service_restore.go b/src/internal/connector/exchange/service_restore.go index 33833b9df..756fbd450 100644 --- a/src/internal/connector/exchange/service_restore.go +++ b/src/internal/connector/exchange/service_restore.go @@ -78,9 +78,9 @@ func RestoreExchangeObject( policy control.CollisionPolicy, service graph.Service, destination, user string, -) error { +) (*details.ExchangeInfo, error) { 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) @@ -93,7 +93,7 @@ func RestoreExchangeObject( case events: return RestoreExchangeEvent(ctx, bits, service, control.Copy, destination, user) 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, cp control.CollisionPolicy, destination, user string, -) error { +) (*details.ExchangeInfo, error) { contact, err := support.CreateContactFromBytes(bits) 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) if err != nil { name := *contact.GetGivenName() - return errors.Wrap( + return nil, errors.Wrap( err, "failure to create Contact during RestoreExchangeContact: "+name+" "+ support.ConnectorStackErrorTrace(err), @@ -127,10 +127,10 @@ func RestoreExchangeContact( } 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 @@ -145,17 +145,17 @@ func RestoreExchangeEvent( service graph.Service, cp control.CollisionPolicy, destination, user string, -) error { +) (*details.ExchangeInfo, error) { event, err := support.CreateEventFromBytes(bits) if err != nil { - return err + return nil, err } transformedEvent := support.ToEventSimplified(event) response, err := service.Client().UsersById(user).CalendarsById(destination).Events().Post(ctx, transformedEvent, nil) if err != nil { - return errors.Wrap(err, + return nil, errors.Wrap(err, fmt.Sprintf( "failure to event creation failure during RestoreExchangeEvent: %s", support.ConnectorStackErrorTrace(err)), @@ -163,10 +163,10 @@ func RestoreExchangeEvent( } 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 @@ -182,11 +182,11 @@ func RestoreMailMessage( cp control.CollisionPolicy, destination, user string, -) error { +) (*details.ExchangeInfo, error) { // Creates messageable object from original bytes originalMessage, err := support.CreateMessageFromBytes(bits) if err != nil { - return err + return nil, err } // Sets fields from original message from storage clone := support.ToMessage(originalMessage) @@ -223,7 +223,7 @@ func RestoreMailMessage( "policy", cp) fallthrough 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() - err = RestoreExchangeObject(ctx, byteArray, category, policy, gs, folderID, user) + info, err := RestoreExchangeObject(ctx, byteArray, category, policy, gs, folderID, user) if err != nil { // More information to be here errUpdater( @@ -406,7 +406,9 @@ func restoreCollection( itemPath.String(), itemPath.ShortRef(), "", - details.ItemInfo{}) + details.ItemInfo{ + Exchange: info, + }) } } } diff --git a/src/internal/connector/graph_connector_test.go b/src/internal/connector/graph_connector_test.go index 5bb188b69..4a7c6acac 100644 --- a/src/internal/connector/graph_connector_test.go +++ b/src/internal/connector/graph_connector_test.go @@ -47,10 +47,6 @@ func TestGraphConnectorIntegrationSuite(t *testing.T) { } func (suite *GraphConnectorIntegrationSuite) SetupSuite() { - if err := tester.RunOnAny(tester.CorsoCITests); err != nil { - suite.T().Skip(err) - } - ctx := context.Background() _, err := tester.GetRequiredEnvVars(tester.M365AcctCredEnvs...) require.NoError(suite.T(), err) diff --git a/src/internal/connector/onedrive/item.go b/src/internal/connector/onedrive/item.go index b70ceccd7..ea16a8c07 100644 --- a/src/internal/connector/onedrive/item.go +++ b/src/internal/connector/onedrive/item.go @@ -9,6 +9,7 @@ import ( "time" msup "github.com/microsoftgraph/msgraph-sdk-go/drives/item/items/item/createuploadsession" + "github.com/microsoftgraph/msgraph-sdk-go/models" "github.com/pkg/errors" "gopkg.in/resty.v1" @@ -56,13 +57,19 @@ func driveItemReader( 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{ ItemType: details.OneDriveItem, - ItemName: *item.GetName(), - Created: *item.GetCreatedDateTime(), - Modified: *item.GetLastModifiedDateTime(), - Size: *item.GetSize(), - }, resp.Body, nil + ItemName: *di.GetName(), + Created: *di.GetCreatedDateTime(), + Modified: *di.GetLastModifiedDateTime(), + Size: *di.GetSize(), + } } // driveItemWriter is used to initialize and return an io.Writer to upload data for the specified item diff --git a/src/internal/connector/onedrive/restore.go b/src/internal/connector/onedrive/restore.go index 41033244b..bcd5826c7 100644 --- a/src/internal/connector/onedrive/restore.go +++ b/src/internal/connector/onedrive/restore.go @@ -142,7 +142,7 @@ func restoreCollection( metrics.TotalBytes += int64(len(copyBuffer)) - err := restoreItem(ctx, + itemInfo, err := restoreItem(ctx, service, itemData, drivePath.driveID, @@ -165,7 +165,9 @@ func restoreCollection( itemPath.String(), itemPath.ShortRef(), "", - details.ItemInfo{}) + details.ItemInfo{ + OneDrive: itemInfo, + }) metrics.Successes++ } @@ -224,7 +226,7 @@ func restoreItem( itemData data.Stream, driveID, parentFolderID string, copyBuffer []byte, -) error { +) (*details.OneDriveInfo, error) { defer trace.StartRegion(ctx, "gc:oneDrive:restoreItem").End() itemName := itemData.UUID() @@ -233,26 +235,26 @@ func restoreItem( // Get the stream size (needed to create the upload session) ss, ok := itemData.(data.StreamSize) 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 newItem, err := createItem(ctx, service, driveID, parentFolderID, newItem(itemData.UUID(), false)) 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 w, err := driveItemWriter(ctx, service, driveID, *newItem.GetId(), ss.Size()) 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 _, err = io.CopyBuffer(w, itemData.ToReader(), copyBuffer) 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 }