-
Notifications
You must be signed in to change notification settings - Fork 23
/
attachment.go
126 lines (107 loc) · 3.64 KB
/
attachment.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package exchange
import (
"context"
"fmt"
"github.com/alcionai/clues"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/pkg/logger"
"github.com/alcionai/corso/src/pkg/services/m365/api"
)
type attachmentPoster interface {
PostSmallAttachment(
ctx context.Context,
userID, containerID, itemID string,
body models.Attachmentable,
) error
PostLargeAttachment(
ctx context.Context,
userID, containerID, itemID, name string,
content []byte,
) (string, error)
}
const (
// Use large attachment logic for attachments > 3MB
// https://learn.microsoft.com/en-us/graph/outlook-large-attachments
largeAttachmentSize = 3 * 1024 * 1024
fileAttachmentOdataValue = "#microsoft.graph.fileAttachment"
itemAttachmentOdataValue = "#microsoft.graph.itemAttachment"
referenceAttachmentOdataValue = "#microsoft.graph.referenceAttachment"
)
func attachmentType(attachment models.Attachmentable) models.AttachmentType {
attachmentType := ptr.Val(attachment.GetOdataType())
switch attachmentType {
case fileAttachmentOdataValue:
return models.FILE_ATTACHMENTTYPE
case itemAttachmentOdataValue:
return models.ITEM_ATTACHMENTTYPE
case referenceAttachmentOdataValue:
return models.REFERENCE_ATTACHMENTTYPE
default:
// Should not hit this but default to ITEM_ATTACHMENTTYPE
// which will pick the default attachment upload mechanism
return models.ITEM_ATTACHMENTTYPE
}
}
// uploadAttachment will upload the specified message attachment to M365
func uploadAttachment(
ctx context.Context,
ap attachmentPoster,
userID, containerID, parentItemID string,
attachment models.Attachmentable,
) error {
var (
attachmentType = attachmentType(attachment)
id = ptr.Val(attachment.GetId())
name = ptr.Val(attachment.GetName())
size = ptr.Val(attachment.GetSize())
)
ctx = clues.Add(
ctx,
"attachment_size", size,
"attachment_id", id,
"attachment_name", clues.Hide(name),
"attachment_type", attachmentType,
"attachment_odata_type", ptr.Val(attachment.GetOdataType()),
"attachment_outlook_odata_type", getOutlookOdataType(attachment),
"parent_item_id", parentItemID)
logger.Ctx(ctx).Debug("uploading attachment")
// reference attachments that are inline() do not need to be recreated. The contents are part of the body.
if attachmentType == models.REFERENCE_ATTACHMENTTYPE && ptr.Val(attachment.GetIsInline()) {
logger.Ctx(ctx).Debug("skip uploading inline reference attachment")
return nil
}
// item Attachments to be skipped until the completion of Issue #2353
if attachmentType == models.ITEM_ATTACHMENTTYPE {
a, err := toItemAttachment(attachment)
if err != nil {
logger.CtxErr(ctx, err).Info(fmt.Sprintf("item attachment type not supported: %v", attachmentType))
return nil
}
attachment = a
}
// for file attachments sized >= 3MB
if attachmentType == models.FILE_ATTACHMENTTYPE && size >= largeAttachmentSize {
// We expect the entire attachment to fit in memory.
// Max attachment size is 150MB.
content, err := api.GetAttachmentContent(attachment)
if err != nil {
return clues.WrapWC(ctx, err, "serializing attachment content")
}
_, err = ap.PostLargeAttachment(ctx, userID, containerID, parentItemID, name, content)
return err
}
// for all other attachments
return ap.PostSmallAttachment(ctx, userID, containerID, parentItemID, attachment)
}
func getOutlookOdataType(query models.Attachmentable) string {
attachment, ok := query.(models.ItemAttachmentable)
if !ok {
return ""
}
item := attachment.GetItem()
if item == nil {
return ""
}
return ptr.Val(item.GetOdataType())
}