GC: Item mail attachment Handling (#2422)
## Description Support for `itemAttachment.Mail` added to GC restore pipeline. Nested attachments within items disabled due to Kiota bug. Issue #2428 created to re-enable `itemAttachment.Item.Attachments` when the bug is patched. <!-- Insert PR description--> ## Does this PR need a docs update or release note? - [x] 🏢 : Yes. Known issues and ChangeLog updates required. ## 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. --> * related to https://github.com/microsoft/kiota-serialization-json-go/issues/61<issue> * closes #2372 ## Test Plan - [x] ⚡ Unit test
This commit is contained in:
parent
f782d1b634
commit
a4b50a1ec0
@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased] (alpha)
|
## [Unreleased] (alpha)
|
||||||
|
### Added
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Support for item.Attachment:Mail restore
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
### Known Issues
|
||||||
|
- Nested attachments are currently not restored due to an [issue](https://github.com/microsoft/kiota-serialization-json-go/issues/61) discovered in the Graph APIs
|
||||||
|
|
||||||
## [v0.3.0] (alpha) - 2023-2-07
|
## [v0.3.0] (alpha) - 2023-2-07
|
||||||
|
|
||||||
|
|||||||
@ -68,8 +68,9 @@ func uploadAttachment(
|
|||||||
name = *prev.GetName()
|
name = *prev.GetName()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Update to support PII protection
|
// TODO: (rkeepers) Update to support PII protection
|
||||||
logger.Ctx(ctx).Infow("item attachment uploads are not supported ",
|
msg := "item attachment restore not supported for this type. skipping upload."
|
||||||
|
logger.Ctx(ctx).Infow(msg,
|
||||||
"err", err,
|
"err", err,
|
||||||
"attachment_name", name,
|
"attachment_name", name,
|
||||||
"attachment_type", attachmentType,
|
"attachment_type", attachmentType,
|
||||||
|
|||||||
@ -130,12 +130,13 @@ type containerDeleter interface {
|
|||||||
|
|
||||||
// TestRestoreExchangeObject verifies path.Category usage for restored objects
|
// TestRestoreExchangeObject verifies path.Category usage for restored objects
|
||||||
func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() {
|
func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() {
|
||||||
a := tester.NewM365Account(suite.T())
|
t := suite.T()
|
||||||
|
a := tester.NewM365Account(t)
|
||||||
m365, err := a.M365Config()
|
m365, err := a.M365Config()
|
||||||
require.NoError(suite.T(), err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
service, err := createService(m365)
|
service, err := createService(m365)
|
||||||
require.NoError(suite.T(), err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
deleters := map[path.CategoryType]containerDeleter{
|
deleters := map[path.CategoryType]containerDeleter{
|
||||||
path.EmailCategory: suite.ac.Mail(),
|
path.EmailCategory: suite.ac.Mail(),
|
||||||
@ -187,6 +188,48 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() {
|
|||||||
return *folder.GetId()
|
return *folder.GetId()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Test Mail: Item Attachment_Mail",
|
||||||
|
bytes: mockconnector.GetMockMessageWithItemAttachmentMail("Mail Item Attachment"),
|
||||||
|
category: path.EmailCategory,
|
||||||
|
destination: func(t *testing.T, ctx context.Context) string {
|
||||||
|
folderName := "TestRestoreMailItemAttachment: " + common.FormatSimpleDateTime(now)
|
||||||
|
folder, err := suite.ac.Mail().CreateMailFolder(ctx, userID, folderName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return *folder.GetId()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Test Mail: Hydrated Item Attachment Mail",
|
||||||
|
bytes: mockconnector.GetMockMessageWithNestedItemAttachmentMail(t,
|
||||||
|
mockconnector.GetMockMessageBytes("Basic Item Attachment"),
|
||||||
|
"Mail Item Attachment",
|
||||||
|
),
|
||||||
|
category: path.EmailCategory,
|
||||||
|
destination: func(t *testing.T, ctx context.Context) string {
|
||||||
|
folderName := "TestRestoreMailBasicItemAttachment: " + common.FormatSimpleDateTime(now)
|
||||||
|
folder, err := suite.ac.Mail().CreateMailFolder(ctx, userID, folderName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return *folder.GetId()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Test Mail: Hydrated Item Attachment Mail One Attach",
|
||||||
|
bytes: mockconnector.GetMockMessageWithNestedItemAttachmentMail(t,
|
||||||
|
mockconnector.GetMockMessageWithDirectAttachment("Item Attachment Included"),
|
||||||
|
"Mail Item Attachment",
|
||||||
|
),
|
||||||
|
category: path.EmailCategory,
|
||||||
|
destination: func(t *testing.T, ctx context.Context) string {
|
||||||
|
folderName := "ItemMailAttachmentwAttachment " + 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
|
{ // Restore will upload the Message without uploading the attachment
|
||||||
name: "Test Mail: Item Attachment_NestedEvent",
|
name: "Test Mail: Item Attachment_NestedEvent",
|
||||||
bytes: mockconnector.GetMockMessageWithNestedItemAttachmentEvent("Nested Item Attachment"),
|
bytes: mockconnector.GetMockMessageWithNestedItemAttachmentEvent("Nested Item Attachment"),
|
||||||
@ -291,6 +334,7 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() {
|
|||||||
)
|
)
|
||||||
assert.NoError(t, err, support.ConnectorStackErrorTrace(err))
|
assert.NoError(t, err, support.ConnectorStackErrorTrace(err))
|
||||||
assert.NotNil(t, info, "item info was not populated")
|
assert.NotNil(t, info, "item info was not populated")
|
||||||
|
assert.NotNil(t, deleters)
|
||||||
assert.NoError(t, deleters[test.category].DeleteContainer(ctx, userID, destination))
|
assert.NoError(t, deleters[test.category].DeleteContainer(ctx, userID, destination))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -283,6 +283,20 @@ func SendMailToBackStore(
|
|||||||
|
|
||||||
for _, attachment := range attached {
|
for _, attachment := range attached {
|
||||||
if err := uploadAttachment(ctx, uploader, attachment); err != nil {
|
if err := uploadAttachment(ctx, uploader, attachment); err != nil {
|
||||||
|
if attachment.GetOdataType() != nil &&
|
||||||
|
*attachment.GetOdataType() == "#microsoft.graph.itemAttachment" {
|
||||||
|
var name string
|
||||||
|
if attachment.GetName() != nil {
|
||||||
|
name = *attachment.GetName()
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Ctx(ctx).Infow(
|
||||||
|
"item attachment upload not successful. content not accepted by M365 server",
|
||||||
|
"Attachment Name", name)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
errs = support.WrapAndAppend(
|
errs = support.WrapAndAppend(
|
||||||
fmt.Sprintf("uploading attachment for message %s: %s",
|
fmt.Sprintf("uploading attachment for message %s: %s",
|
||||||
id, support.ConnectorStackErrorTrace(err)),
|
id, support.ConnectorStackErrorTrace(err)),
|
||||||
|
|||||||
@ -3,6 +3,12 @@ package mockconnector
|
|||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
js "github.com/microsoft/kiota-serialization-json-go"
|
||||||
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/alcionai/corso/src/internal/common"
|
"github.com/alcionai/corso/src/internal/common"
|
||||||
)
|
)
|
||||||
@ -360,6 +366,143 @@ func GetMockMessageWithItemAttachmentEvent(subject string) []byte {
|
|||||||
return []byte(message)
|
return []byte(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetMockMessageWithItemAttachmentMail(subject string) []byte {
|
||||||
|
//nolint:lll
|
||||||
|
// Order of fields:
|
||||||
|
// 1. subject
|
||||||
|
// 2. alias
|
||||||
|
// 3. sender address
|
||||||
|
// 4. from address
|
||||||
|
// 5. toRecipients email address
|
||||||
|
template := `{
|
||||||
|
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('f435c656-f8b2-4d71-93c3-6e092f52a167')/messages(attachments())/$entity",
|
||||||
|
"@odata.etag": "W/\"CQAAABYAAAB8wYc0thTTTYl3RpEYIUq+AADKTqr3\"",
|
||||||
|
"id": "AAMkAGQ1NzViZTdhLTEwMTMtNGJjNi05YWI2LTg4NWRlZDA2Y2UxOABGAAAAAAAPvVwUramXT7jlSGpVU8_7BwB8wYc0thTTTYl3RpEYIUq_AAAAAAEMAAB8wYc0thTTTYl3RpEYIUq_AADKo35SAAA=",
|
||||||
|
"createdDateTime": "2023-02-06T20:03:40Z",
|
||||||
|
"lastModifiedDateTime": "2023-02-06T20:03:42Z",
|
||||||
|
"changeKey": "CQAAABYAAAB8wYc0thTTTYl3RpEYIUq+AADKTqr3",
|
||||||
|
"categories": [],
|
||||||
|
"receivedDateTime": "2023-02-06T20:03:40Z",
|
||||||
|
"sentDateTime": "2023-02-06T20:03:37Z",
|
||||||
|
"hasAttachments": true,
|
||||||
|
"internetMessageId": "<SJ0PR17MB5622C17321AE356F5202A857C3DA9@SJ0PR17MB5622.namprd17.prod.outlook.com>",
|
||||||
|
"subject": "%[1]s",
|
||||||
|
"bodyPreview": "Nested Items are not encapsulated in a trivial manner. Review the findings.\r\n\r\nBest,\r\n\r\nYour Test Case",
|
||||||
|
"importance": "normal",
|
||||||
|
"parentFolderId": "AQMkAGQ1NzViZTdhLTEwMTMtNGJjNi05YWI2LTg4ADVkZWQwNmNlMTgALgAAAw_9XBStqZdPuOVIalVTz7sBAHzBhzS2FNNNiXdGkRghSr4AAAIBDAAAAA==",
|
||||||
|
"conversationId": "AAQkAGQ1NzViZTdhLTEwMTMtNGJjNi05YWI2LTg4NWRlZDA2Y2UxOAAQAPe8pEQOrBxLvFNhfDtMyEI=",
|
||||||
|
"conversationIndex": "AQHZOmYA97ykRA6sHEu8U2F8O0zIQg==",
|
||||||
|
"isDeliveryReceiptRequested": false,
|
||||||
|
"isReadReceiptRequested": false,
|
||||||
|
"isRead": false,
|
||||||
|
"isDraft": false,
|
||||||
|
"webLink": "https://outlook.office365.com/owal=ReadMessageItem",
|
||||||
|
"inferenceClassification": "focused",
|
||||||
|
"body": {
|
||||||
|
"contentType": "html",
|
||||||
|
"content": "<html><head>\r\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><style type=\"text/css\" style=\"display:none\">\r\n<!--\r\np\r\n\t{margin-top:0;\r\n\tmargin-bottom:0}\r\n-->\r\n</style></head><body dir=\"ltr\"><div class=\"elementToProof\" style=\"font-family:Calibri,Arial,Helvetica,sans-serif; font-size:12pt; color:rgb(0,0,0); background-color:rgb(255,255,255)\">Nested Items are not encapsulated in a trivial manner. Review the findings.</div><div class=\"elementToProof\" style=\"font-family:Calibri,Arial,Helvetica,sans-serif; font-size:12pt; color:rgb(0,0,0); background-color:rgb(255,255,255)\"><br></div><div class=\"elementToProof\" style=\"font-family:Calibri,Arial,Helvetica,sans-serif; font-size:12pt; color:rgb(0,0,0); background-color:rgb(255,255,255)\">Best, </div><div class=\"elementToProof\" style=\"font-family:Calibri,Arial,Helvetica,sans-serif; font-size:12pt; color:rgb(0,0,0); background-color:rgb(255,255,255)\"><br></div><div class=\"elementToProof\" style=\"font-family:Calibri,Arial,Helvetica,sans-serif; font-size:12pt; color:rgb(0,0,0); background-color:rgb(255,255,255)\">Your Test Case</div></body></html>"
|
||||||
|
},
|
||||||
|
"sender": {
|
||||||
|
"emailAddress": {
|
||||||
|
"name": "%[2]s",
|
||||||
|
"address": "%[3]s"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"from": {
|
||||||
|
"emailAddress": {
|
||||||
|
"name": "%[2]s",
|
||||||
|
"address": "%[4]s"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"toRecipients": [
|
||||||
|
{
|
||||||
|
"emailAddress": {
|
||||||
|
"name": "%[2]s",
|
||||||
|
"address": "%[5]s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ccRecipients": [],
|
||||||
|
"bccRecipients": [],
|
||||||
|
"replyTo": [],
|
||||||
|
"flag": {
|
||||||
|
"flagStatus": "notFlagged"
|
||||||
|
},
|
||||||
|
"attachments": [
|
||||||
|
{
|
||||||
|
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#/attachments(microsoft.graph.itemAttachment/item())/$entity",
|
||||||
|
"@odata.type": "#microsoft.graph.itemAttachment",
|
||||||
|
"id": "AAMkAGQ1NzViZTdhLTEwMTMtNGJjNi05YWI2LTg4NWRlZDA2Y2UxOABGAAAAAAAPvVwUramXT7jlSGpVU8_7BwB8wYc0thTTTYl3RpEYIUq_AAAAAAEMAAB8wYc0thTTTYl3RpEYIUq_AADKo35SAAABEgAQABv3spWM8g5IriSvYJe5kO8=",
|
||||||
|
"lastModifiedDateTime": "2023-02-06T20:03:40Z",
|
||||||
|
"name": "Not Something Small. 28-Jul-2022_20:53:33 Different",
|
||||||
|
"contentType": null,
|
||||||
|
"size": 10959,
|
||||||
|
"isInline": false,
|
||||||
|
"item@odata.associationLink": "https://graph.microsoft.com/v1.0/users('f435c656-f8b2-4d71-93c3-6e092f52a167')/messages('')/$ref",
|
||||||
|
"item@odata.navigationLink": "https://graph.microsoft.com/v1.0/users('f435c656-f8b2-4d71-93c3-6e092f52a167')/messages('')",
|
||||||
|
"item": {
|
||||||
|
"@odata.type": "#microsoft.graph.message",
|
||||||
|
"id": "",
|
||||||
|
"createdDateTime": "2023-02-06T20:03:40Z",
|
||||||
|
"lastModifiedDateTime": "2023-02-06T20:03:40Z",
|
||||||
|
"receivedDateTime": "2022-07-28T20:53:33Z",
|
||||||
|
"sentDateTime": "2022-07-28T20:53:33Z",
|
||||||
|
"hasAttachments": false,
|
||||||
|
"internetMessageId": "<MWHPR1401MB1952C46D4A46B6398F562B0FA6E99@MWHPR1401MB1952.namprd14.prod.outlook.com>",
|
||||||
|
"subject": "Not Something Small. 28-Jul-2022_20:53:33 Different",
|
||||||
|
"bodyPreview": "I've been going through with the changing of messages. It shouldn't have the same calls, right? Call Me?\r\n\r\nWe want to be able to send multiple messages and we want to be able to respond and do other things that make sense for our users. In this case. Let",
|
||||||
|
"importance": "normal",
|
||||||
|
"conversationId": "AAQkAGQ1NzViZTdhLTEwMTMtNGJjNi05YWI2LTg4NWRlZDA2Y2UxOAAQAOlAM0OrVQlHkhUZeZMPxgg=",
|
||||||
|
"conversationIndex": "AQHYosQZ6UAzQ6tVCUeSFRl5kw/GCA==",
|
||||||
|
"isDeliveryReceiptRequested": false,
|
||||||
|
"isReadReceiptRequested": false,
|
||||||
|
"isRead": true,
|
||||||
|
"isDraft": false,
|
||||||
|
"webLink": "https://outlook.office365.com/owa/?AttachmentItemID=Aviewmodel=ItemAttachment",
|
||||||
|
"body": {
|
||||||
|
"contentType": "html",
|
||||||
|
"content": "<html><head>\r\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><meta name=\"Generator\" content=\"Microsoft Word 15 (filtered medium)\"><style><!--@font-face{font-family:\"Cambria Math\"}@font-face{font-family:Calibri}p.MsoNormal, li.MsoNormal, div.MsoNormal{margin:0in;font-size:11.0pt;font-family:\"Calibri\",sans-serif}span.EmailStyle17{font-family:\"Calibri\",sans-serif;color:windowtext}.MsoChpDefault{font-family:\"Calibri\",sans-serif}@page WordSection1{margin:1.0in 1.0in 1.0in 1.0in}div.WordSection1{}--></style></head><body lang=\"EN-US\" link=\"#0563C1\" vlink=\"#954F72\" style=\"word-wrap:break-word\"><div class=\"WordSection1\"><p class=\"MsoNormal\">I've been going through with the changing of messages. It shouldn't have the same calls, right? Call Me? </p><p class=\"MsoNormal\"> </p><p class=\"MsoNormal\">We want to be able to send multiple messages and we want to be able to respond and do other things that make sense for our users. In this case. Let’s consider a Mailbox</p></div></body></html>"
|
||||||
|
},
|
||||||
|
"sender": {
|
||||||
|
"emailAddress": {
|
||||||
|
"name": "%[2]s",
|
||||||
|
"address": "%[3]s"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"from": {
|
||||||
|
"emailAddress": {
|
||||||
|
"name": "%[2]s",
|
||||||
|
"address": "%[4]s"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"toRecipients": [
|
||||||
|
{
|
||||||
|
"emailAddress": {
|
||||||
|
"name": "Direct Report",
|
||||||
|
"address": "notAvailable@8qzvrj.onmicrosoft.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flag": {
|
||||||
|
"flagStatus": "notFlagged"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
|
||||||
|
message := fmt.Sprintf(
|
||||||
|
template,
|
||||||
|
subject,
|
||||||
|
defaultAlias,
|
||||||
|
defaultMessageSender,
|
||||||
|
defaultMessageFrom,
|
||||||
|
defaultMessageTo,
|
||||||
|
)
|
||||||
|
|
||||||
|
return []byte(message)
|
||||||
|
}
|
||||||
|
|
||||||
func GetMockMessageWithNestedItemAttachmentEvent(subject string) []byte {
|
func GetMockMessageWithNestedItemAttachmentEvent(subject string) []byte {
|
||||||
//nolint:lll
|
//nolint:lll
|
||||||
// Order of fields:
|
// Order of fields:
|
||||||
@ -545,3 +688,46 @@ func GetMockMessageWithNestedItemAttachmentEvent(subject string) []byte {
|
|||||||
|
|
||||||
return []byte(message)
|
return []byte(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetMockMessageWithNestedItemAttachmentMail(t *testing.T, nested []byte, subject string) []byte {
|
||||||
|
base := GetMockMessageBytes(subject)
|
||||||
|
message, err := hydrateMessage(base)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
nestedMessage, err := hydrateMessage(nested)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
iaNode := models.NewItemAttachment()
|
||||||
|
attachmentSize := int32(len(nested))
|
||||||
|
iaNode.SetSize(&attachmentSize)
|
||||||
|
|
||||||
|
internalName := "Nested Message"
|
||||||
|
iaNode.SetName(&internalName)
|
||||||
|
iaNode.SetItem(nestedMessage)
|
||||||
|
message.SetAttachments([]models.Attachmentable{iaNode})
|
||||||
|
|
||||||
|
wtr := js.NewJsonSerializationWriter()
|
||||||
|
err = wtr.WriteObjectValue("", message)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
byteArray, err := wtr.GetSerializedContent()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return byteArray
|
||||||
|
}
|
||||||
|
|
||||||
|
func hydrateMessage(byteArray []byte) (models.Messageable, error) {
|
||||||
|
parseNode, err := js.NewJsonParseNodeFactory().GetRootParseNode("application/json", byteArray)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "deserializing bytes into base m365 object")
|
||||||
|
}
|
||||||
|
|
||||||
|
anObject, err := parseNode.GetObjectValue(models.CreateMessageFromDiscriminatorValue)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "parsing m365 object factory")
|
||||||
|
}
|
||||||
|
|
||||||
|
message := anObject.(models.Messageable)
|
||||||
|
|
||||||
|
return message, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -7,7 +7,11 @@ import (
|
|||||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
const itemAttachment = "#microsoft.graph.itemAttachment"
|
//==========================================================
|
||||||
|
// m365Transform.go contains utility functions that
|
||||||
|
// either add, modify, or remove fields from M365
|
||||||
|
// objects for interacton with M365 services
|
||||||
|
//=========================================================
|
||||||
|
|
||||||
// CloneMessageableFields places data from original data into new message object.
|
// CloneMessageableFields places data from original data into new message object.
|
||||||
// SingleLegacyValueProperty is not populated during this operation
|
// SingleLegacyValueProperty is not populated during this operation
|
||||||
@ -282,14 +286,35 @@ func cloneColumnDefinitionable(orig models.ColumnDefinitionable) models.ColumnDe
|
|||||||
return newColumn
|
return newColumn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===============================================================================================
|
||||||
|
// Sanitization section
|
||||||
|
// Set of functions that support ItemAttachemtable object restoration.
|
||||||
|
// These attachments can be nested as well as possess one of the other
|
||||||
|
// reference types. To ensure proper upload, each interior`item` requires
|
||||||
|
// that certain fields be modified.
|
||||||
|
// ItemAttachment:
|
||||||
|
// https://learn.microsoft.com/en-us/graph/api/resources/itemattachment?view=graph-rest-1.0
|
||||||
|
// https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/attachments-and-ews-in-exchange
|
||||||
|
// https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/folders-and-items-in-ews-in-exchange
|
||||||
|
// ===============================================================================================
|
||||||
|
// M365 Models possess a field, OData.Type which indicate
|
||||||
|
// the represent the intended model in string format.
|
||||||
|
// The constants listed here identify the supported itemAttachments
|
||||||
|
// currently supported for Restore operations.
|
||||||
|
// itemAttachments
|
||||||
|
// support ODataType values
|
||||||
|
//
|
||||||
|
//nolint:lll
|
||||||
|
const (
|
||||||
|
itemAttachment = "#microsoft.graph.itemAttachment"
|
||||||
|
eventItemType = "#microsoft.graph.event"
|
||||||
|
mailItemType = "#microsoft.graph.message"
|
||||||
|
)
|
||||||
|
|
||||||
// ToItemAttachment transforms internal item, OutlookItemables, into
|
// ToItemAttachment transforms internal item, OutlookItemables, into
|
||||||
// objects that are able to be uploaded into M365.
|
// objects that are able to be uploaded into M365.
|
||||||
// Supported Internal Items:
|
|
||||||
// - Events
|
|
||||||
func ToItemAttachment(orig models.Attachmentable) (models.Attachmentable, error) {
|
func ToItemAttachment(orig models.Attachmentable) (models.Attachmentable, error) {
|
||||||
transform, ok := orig.(models.ItemAttachmentable)
|
transform, ok := orig.(models.ItemAttachmentable)
|
||||||
supported := "#microsoft.graph.event"
|
|
||||||
|
|
||||||
if !ok { // Shouldn't ever happen
|
if !ok { // Shouldn't ever happen
|
||||||
return nil, fmt.Errorf("transforming attachment to item attachment")
|
return nil, fmt.Errorf("transforming attachment to item attachment")
|
||||||
}
|
}
|
||||||
@ -298,7 +323,7 @@ func ToItemAttachment(orig models.Attachmentable) (models.Attachmentable, error)
|
|||||||
itemType := item.GetOdataType()
|
itemType := item.GetOdataType()
|
||||||
|
|
||||||
switch *itemType {
|
switch *itemType {
|
||||||
case supported:
|
case eventItemType:
|
||||||
event := item.(models.Eventable)
|
event := item.(models.Eventable)
|
||||||
|
|
||||||
newEvent, err := sanitizeEvent(event)
|
newEvent, err := sanitizeEvent(event)
|
||||||
@ -308,12 +333,45 @@ func ToItemAttachment(orig models.Attachmentable) (models.Attachmentable, error)
|
|||||||
|
|
||||||
transform.SetItem(newEvent)
|
transform.SetItem(newEvent)
|
||||||
|
|
||||||
|
return transform, nil
|
||||||
|
case mailItemType:
|
||||||
|
message := item.(models.Messageable)
|
||||||
|
|
||||||
|
newMessage, err := sanitizeMessage(message)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
transform.SetItem(newMessage)
|
||||||
|
|
||||||
return transform, nil
|
return transform, nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("exiting ToItemAttachment: %s not supported", *itemType)
|
return nil, fmt.Errorf("exiting ToItemAttachment: %s not supported", *itemType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO #2428 (dadam39): re-apply nested attachments for itemAttachments
|
||||||
|
// func sanitizeAttachments(attached []models.Attachmentable) ([]models.Attachmentable, error) {
|
||||||
|
// attachments := make([]models.Attachmentable, len(attached))
|
||||||
|
|
||||||
|
// for _, ax := range attached {
|
||||||
|
// if *ax.GetOdataType() == itemAttachment {
|
||||||
|
// newAttachment, err := ToItemAttachment(ax)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// attachments = append(attachments, newAttachment)
|
||||||
|
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
|
||||||
|
// attachments = append(attachments, ax)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return attachments, nil
|
||||||
|
// }
|
||||||
|
|
||||||
// sanitizeEvent transfers data into event object and
|
// sanitizeEvent transfers data into event object and
|
||||||
// removes unique IDs from the M365 object
|
// removes unique IDs from the M365 object
|
||||||
func sanitizeEvent(orig models.Eventable) (models.Eventable, error) {
|
func sanitizeEvent(orig models.Eventable) (models.Eventable, error) {
|
||||||
@ -324,7 +382,9 @@ func sanitizeEvent(orig models.Eventable) (models.Eventable, error) {
|
|||||||
newEvent.SetCalendar(orig.GetCalendar())
|
newEvent.SetCalendar(orig.GetCalendar())
|
||||||
newEvent.SetCreatedDateTime(orig.GetCreatedDateTime())
|
newEvent.SetCreatedDateTime(orig.GetCreatedDateTime())
|
||||||
newEvent.SetEnd(orig.GetEnd())
|
newEvent.SetEnd(orig.GetEnd())
|
||||||
newEvent.SetHasAttachments(orig.GetHasAttachments())
|
// TODO: dadams39 Nested attachments not supported
|
||||||
|
// Upstream: https://github.com/microsoft/kiota-serialization-json-go/issues/61
|
||||||
|
newEvent.SetHasAttachments(nil)
|
||||||
newEvent.SetHideAttendees(orig.GetHideAttendees())
|
newEvent.SetHideAttendees(orig.GetHideAttendees())
|
||||||
newEvent.SetImportance(orig.GetImportance())
|
newEvent.SetImportance(orig.GetImportance())
|
||||||
newEvent.SetIsAllDay(orig.GetIsAllDay())
|
newEvent.SetIsAllDay(orig.GetIsAllDay())
|
||||||
@ -337,7 +397,7 @@ func sanitizeEvent(orig models.Eventable) (models.Eventable, error) {
|
|||||||
newEvent.SetSubject(orig.GetSubject())
|
newEvent.SetSubject(orig.GetSubject())
|
||||||
newEvent.SetType(orig.GetType())
|
newEvent.SetType(orig.GetType())
|
||||||
|
|
||||||
// Sanitation
|
// Sanitation NOTE
|
||||||
// isDraft and isOrganizer *bool ptr's have to be removed completely
|
// isDraft and isOrganizer *bool ptr's have to be removed completely
|
||||||
// from JSON in order for POST method to succeed.
|
// from JSON in order for POST method to succeed.
|
||||||
// Current as of 2/2/2023
|
// Current as of 2/2/2023
|
||||||
@ -346,25 +406,34 @@ func sanitizeEvent(orig models.Eventable) (models.Eventable, error) {
|
|||||||
newEvent.SetIsDraft(nil)
|
newEvent.SetIsDraft(nil)
|
||||||
newEvent.SetAdditionalData(orig.GetAdditionalData())
|
newEvent.SetAdditionalData(orig.GetAdditionalData())
|
||||||
|
|
||||||
attached := orig.GetAttachments()
|
// TODO #2428 (dadam39): re-apply nested attachments for itemAttachments
|
||||||
attachments := make([]models.Attachmentable, len(attached))
|
// Upstream: https://github.com/microsoft/kiota-serialization-json-go/issues/61
|
||||||
|
// attachments, err := sanitizeAttachments(message.GetAttachments())
|
||||||
for _, ax := range attached {
|
// if err != nil {
|
||||||
if *ax.GetOdataType() == itemAttachment {
|
// return nil, err
|
||||||
newAttachment, err := ToItemAttachment(ax)
|
// }
|
||||||
if err != nil {
|
newEvent.SetAttachments(nil)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
attachments = append(attachments, newAttachment)
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
attachments = append(attachments, ax)
|
|
||||||
}
|
|
||||||
|
|
||||||
newEvent.SetAttachments(attachments)
|
|
||||||
|
|
||||||
return newEvent, nil
|
return newEvent, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sanitizeMessage(orig models.Messageable) (models.Messageable, error) {
|
||||||
|
message := ToMessage(orig)
|
||||||
|
|
||||||
|
// TODO #2428 (dadam39): re-apply nested attachments for itemAttachments
|
||||||
|
// Upstream: https://github.com/microsoft/kiota-serialization-json-go/issues/61
|
||||||
|
// attachments, err := sanitizeAttachments(message.GetAttachments())
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
message.SetAttachments(nil)
|
||||||
|
|
||||||
|
// The following fields are set to nil to
|
||||||
|
// not interfere with M365 guard checks.
|
||||||
|
message.SetHasAttachments(nil)
|
||||||
|
message.SetParentFolderId(nil)
|
||||||
|
message.SetInternetMessageHeaders(nil)
|
||||||
|
message.SetIsDraft(nil)
|
||||||
|
|
||||||
|
return message, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -12,6 +12,8 @@ Below is a list of known Corso issues and limitations:
|
|||||||
Advanced restore options such as in-place restore, or restore to a specific folder or to a different account aren't
|
Advanced restore options such as in-place restore, or restore to a specific folder or to a different account aren't
|
||||||
yet supported.
|
yet supported.
|
||||||
|
|
||||||
|
* Restoration of Nested attachments within Exchange Mail or Calendars aren't yet supported.
|
||||||
|
|
||||||
* Provides no guarantees about whether data moved, added, or deleted in M365
|
* Provides no guarantees about whether data moved, added, or deleted in M365
|
||||||
while a backup is being created will be included in the running backup.
|
while a backup is being created will be included in the running backup.
|
||||||
Future backups run when the data isn't modified will include the data.
|
Future backups run when the data isn't modified will include the data.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user