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:
parent
010d8d5df1
commit
f4556288a5
@ -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)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -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,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user