GC: Item attachment contact support (#2465)

## Description
The logic for sanitizing Contactable data for restoration of `ItemAttachable.Contact` types. 

Contact `Item.Attachment`s required the removal of:
- `odata.Context` 
- `ETag` 
- `ParentFolder`

Otherwise, the following error occurs on POST. 
```bash
UnableToDeserializePostBody were unable to deserialize
```
<!-- Insert PR description-->

## Does this PR need a docs update or release note?
- [x]  No 

## Type of change

<!--- Please check the type of change your PR introduces: --->
- [x] 🌻 Feature
- [x] 🐛 Bugfix

## Issue(s)

<!-- Can reference multiple issues. Use one of the following "magic words" - "closes, fixes" to auto-close the Github issue. -->
* closes #2426<issue>

## Test Plan
- [x]  Unit test
This commit is contained in:
Danny 2023-02-10 09:23:18 -05:00 committed by GitHub
parent d9d0158b6f
commit b3a1de89bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 83 additions and 14 deletions

View File

@ -0,0 +1,14 @@
package ptr
// Val helper method for unwrapping strings
// Microsoft Graph saves many variables as string pointers.
// Function will safely check if the point is nil prior to
// dereferencing the pointer. If the pointer is nil,
// an empty string is returned.
func Val(ptr *string) string {
if ptr == nil {
return ""
}
return *ptr
}

View File

@ -8,6 +8,7 @@ import (
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/pkg/errors"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/connector/support"
"github.com/alcionai/corso/src/internal/connector/uploadsession"
"github.com/alcionai/corso/src/pkg/logger"
@ -63,19 +64,16 @@ func uploadAttachment(
attachment, err = support.ToItemAttachment(attachment)
if err != nil {
name := ""
if prev.GetName() != nil {
name = *prev.GetName()
}
name := ptr.Val(prev.GetName())
msg := "item attachment restore not supported for this type. skipping upload."
// TODO: (rkeepers) Update to support PII protection
msg := "item attachment restore not supported for this type. skipping upload."
logger.Ctx(ctx).Infow(msg,
"err", err,
"attachment_name", name,
"attachment_type", attachmentType,
"internal_item_type", getItemAttachmentItemType(prev),
"attachment_id", *prev.GetId(),
"attachment_id", ptr.Val(prev.GetId()),
)
return nil
@ -129,9 +127,6 @@ func getItemAttachmentItemType(query models.Attachmentable) string {
}
item := attachment.GetItem()
if item.GetOdataType() == nil {
return empty
}
return *item.GetOdataType()
return ptr.Val(item.GetOdataType())
}

View File

@ -230,6 +230,21 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() {
return *folder.GetId()
},
},
{
name: "Test Mail: Item Attachment_Contact",
bytes: mockconnector.GetMockMessageWithNestedItemAttachmentContact(t,
mockconnector.GetMockContactBytes("Victor"),
"Contact Item Attachment",
),
category: path.EmailCategory,
destination: func(t *testing.T, ctx context.Context) string {
folderName := "ItemMailAttachment_Contact " + common.FormatSimpleDateTime(now)
folder, err := suite.ac.Mail().CreateMailFolder(ctx, userID, folderName)
require.NoError(t, err)
return *folder.GetId()
},
},
{ // Restore will upload the Message without uploading the attachment
name: "Test Mail: Item Attachment_NestedEvent",
bytes: mockconnector.GetMockMessageWithNestedItemAttachmentEvent("Nested Item Attachment"),

View File

@ -5,6 +5,7 @@ import (
"fmt"
"testing"
absser "github.com/microsoft/kiota-abstractions-go/serialization"
js "github.com/microsoft/kiota-serialization-json-go"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/pkg/errors"
@ -706,8 +707,35 @@ func GetMockMessageWithNestedItemAttachmentMail(t *testing.T, nested []byte, sub
iaNode.SetItem(nestedMessage)
message.SetAttachments([]models.Attachmentable{iaNode})
return serialize(t, message)
}
func GetMockMessageWithNestedItemAttachmentContact(t *testing.T, nested []byte, subject string) []byte {
base := GetMockMessageBytes(subject)
message, err := hydrateMessage(base)
require.NoError(t, err)
parseNode, err := js.NewJsonParseNodeFactory().GetRootParseNode("application/json", nested)
require.NoError(t, err)
anObject, err := parseNode.GetObjectValue(models.CreateContactFromDiscriminatorValue)
require.NoError(t, err)
contact := anObject.(models.Contactable)
internalName := "Nested Contact"
iaNode := models.NewItemAttachment()
attachmentSize := int32(len(nested))
iaNode.SetSize(&attachmentSize)
iaNode.SetName(&internalName)
iaNode.SetItem(contact)
message.SetAttachments([]models.Attachmentable{iaNode})
return serialize(t, message)
}
func serialize(t *testing.T, item absser.Parsable) []byte {
wtr := js.NewJsonSerializationWriter()
err = wtr.WriteObjectValue("", message)
err := wtr.WriteObjectValue("", item)
require.NoError(t, err)
byteArray, err := wtr.GetSerializedContent()

View File

@ -306,9 +306,10 @@ func cloneColumnDefinitionable(orig models.ColumnDefinitionable) models.ColumnDe
//
//nolint:lll
const (
itemAttachment = "#microsoft.graph.itemAttachment"
eventItemType = "#microsoft.graph.event"
mailItemType = "#microsoft.graph.message"
itemAttachment = "#microsoft.graph.itemAttachment"
eventItemType = "#microsoft.graph.event"
mailItemType = "#microsoft.graph.message"
contactItemType = "#microsoft.graph.contact"
)
// ToItemAttachment transforms internal item, OutlookItemables, into
@ -323,6 +324,13 @@ func ToItemAttachment(orig models.Attachmentable) (models.Attachmentable, error)
itemType := item.GetOdataType()
switch *itemType {
case contactItemType:
contact := item.(models.Contactable)
revised := sanitizeContact(contact)
transform.SetItem(revised)
return transform, nil
case eventItemType:
event := item.(models.Eventable)
@ -372,6 +380,15 @@ func ToItemAttachment(orig models.Attachmentable) (models.Attachmentable, error)
// return attachments, nil
// }
// sanitizeContact removes fields which prevent a Contact from
// being uploaded as an attachment.
func sanitizeContact(orig models.Contactable) models.Contactable {
orig.SetParentFolderId(nil)
orig.SetAdditionalData(nil)
return orig
}
// sanitizeEvent transfers data into event object and
// removes unique IDs from the M365 object
func sanitizeEvent(orig models.Eventable) (models.Eventable, error) {