Export event data for invites (#5049)
<!-- PR description--> --- #### Does this PR need a docs update or release note? - [ ] ✅ Yes, it's included - [ ] 🕐 Yes, but in a later PR - [x] ⛔ No #### Type of change <!--- Please check the type of change your PR introduces: ---> - [x] 🌻 Feature - [ ] 🐛 Bugfix - [ ] 🗺️ Documentation - [ ] 🤖 Supportability/Tests - [ ] 💻 CI/Deployment - [ ] 🧹 Tech Debt/Cleanup #### Issue(s) <!-- Can reference multiple issues. Use one of the following "magic words" - "closes, fixes" to auto-close the Github issue. --> * https://github.com/alcionai/corso/issues/5039 #### Test Plan <!-- How will this be tested prior to merging.--> - [ ] 💪 Manual - [x] ⚡ Unit test - [ ] 💚 E2E
This commit is contained in:
parent
6d2d9c0099
commit
f1f805b3f8
@ -22,6 +22,7 @@ import (
|
||||
|
||||
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||
"github.com/alcionai/corso/src/internal/common/str"
|
||||
"github.com/alcionai/corso/src/internal/converters/ics"
|
||||
"github.com/alcionai/corso/src/pkg/logger"
|
||||
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
||||
)
|
||||
@ -50,6 +51,97 @@ func formatAddress(entry models.EmailAddressable) string {
|
||||
return fmt.Sprintf(addressFormat, name, email)
|
||||
}
|
||||
|
||||
// getICalData converts the emails to an event so that ical generation
|
||||
// can generate from it.
|
||||
func getICalData(ctx context.Context, data models.Messageable) (string, error) {
|
||||
msg, ok := data.(*models.EventMessageRequest)
|
||||
if !ok {
|
||||
return "", clues.NewWC(ctx, "unexpected message type").
|
||||
With("interface_type", fmt.Sprintf("%T", data))
|
||||
}
|
||||
|
||||
// This method returns nil if data is not pulled using the necessary expand property
|
||||
// .../messages/<message_id>/?expand=Microsoft.Graph.EventMessage/Event
|
||||
// Also works for emails which are a result of someone accepting an
|
||||
// invite. If we add this expand query parameter value when directly
|
||||
// fetching a cancellation mail, the request fails. It however looks
|
||||
// to be OK to run when listing emails although it gives empty({})
|
||||
// event value for cancellations.
|
||||
// TODO(meain): cancelled event details are available when pulling .eml
|
||||
if mevent := msg.GetEvent(); mevent != nil {
|
||||
return ics.FromEventable(ctx, mevent)
|
||||
}
|
||||
|
||||
// Exceptions(modifications) are covered under this, although graph just sends the
|
||||
// exception event and not the parent, which what eml obtained from graph also contains
|
||||
if ptr.Val(msg.GetMeetingMessageType()) != models.MEETINGREQUEST_MEETINGMESSAGETYPE {
|
||||
// We don't have event data if it not "REQUEST" type.
|
||||
// Both cancellation and acceptance does not return enough
|
||||
// information to recreate an event.
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// If data was not fetch with an expand property, then we can
|
||||
// approximate the details with the following
|
||||
event := models.NewEvent()
|
||||
event.SetId(msg.GetId())
|
||||
event.SetCreatedDateTime(msg.GetCreatedDateTime())
|
||||
event.SetLastModifiedDateTime(msg.GetLastModifiedDateTime())
|
||||
event.SetIsAllDay(msg.GetIsAllDay())
|
||||
event.SetStart(msg.GetStartDateTime())
|
||||
event.SetEnd(msg.GetEndDateTime())
|
||||
event.SetRecurrence(msg.GetRecurrence())
|
||||
// event.SetIsCancelled()
|
||||
event.SetSubject(msg.GetSubject())
|
||||
event.SetBodyPreview(msg.GetBodyPreview())
|
||||
event.SetBody(msg.GetBody())
|
||||
|
||||
// https://learn.microsoft.com/en-us/graph/api/resources/eventmessage?view=graph-rest-1.0
|
||||
// In addition, Outlook automatically creates an event instance in
|
||||
// the invitee's calendar, with the showAs property as tentative.
|
||||
event.SetShowAs(ptr.To(models.TENTATIVE_FREEBUSYSTATUS))
|
||||
|
||||
event.SetCategories(msg.GetCategories())
|
||||
event.SetWebLink(msg.GetWebLink())
|
||||
event.SetOrganizer(msg.GetFrom())
|
||||
|
||||
// NOTE: If an event was previously created and we added people to
|
||||
// it, the original list of attendee are not available.
|
||||
atts := []models.Attendeeable{}
|
||||
|
||||
for _, to := range msg.GetToRecipients() {
|
||||
att := models.NewAttendee()
|
||||
att.SetEmailAddress(to.GetEmailAddress())
|
||||
att.SetTypeEscaped(ptr.To(models.REQUIRED_ATTENDEETYPE))
|
||||
atts = append(atts, att)
|
||||
}
|
||||
|
||||
for _, cc := range msg.GetCcRecipients() {
|
||||
att := models.NewAttendee()
|
||||
att.SetEmailAddress(cc.GetEmailAddress())
|
||||
att.SetTypeEscaped(ptr.To(models.OPTIONAL_ATTENDEETYPE))
|
||||
atts = append(atts, att)
|
||||
}
|
||||
|
||||
// bcc did not show up in my tests, but adding for completeness
|
||||
for _, bcc := range msg.GetBccRecipients() {
|
||||
att := models.NewAttendee()
|
||||
att.SetEmailAddress(bcc.GetEmailAddress())
|
||||
att.SetTypeEscaped(ptr.To(models.OPTIONAL_ATTENDEETYPE))
|
||||
atts = append(atts, att)
|
||||
}
|
||||
|
||||
event.SetAttendees(atts)
|
||||
|
||||
event.SetLocation(msg.GetLocation())
|
||||
// event.SetSensitivity() // unavailable in msg
|
||||
event.SetImportance(msg.GetImportance())
|
||||
// event.SetOnlineMeeting() // not available in eml either
|
||||
event.SetAttachments(msg.GetAttachments())
|
||||
|
||||
return ics.FromEventable(ctx, event)
|
||||
}
|
||||
|
||||
// FromJSON converts a Messageable (as json) to .eml format
|
||||
func FromJSON(ctx context.Context, body []byte) (string, error) {
|
||||
ctx = clues.Add(ctx, "body_len", len(body))
|
||||
@ -62,10 +154,11 @@ func FromJSON(ctx context.Context, body []byte) (string, error) {
|
||||
ctx = clues.Add(ctx, "item_id", ptr.Val(data.GetId()))
|
||||
|
||||
email := mail.NewMSG()
|
||||
email.AllowDuplicateAddress = true // More "correct" conversion
|
||||
email.AddBccToHeader = true // Don't ignore Bcc
|
||||
email.AllowEmptyAttachments = true // Don't error on empty attachments
|
||||
email.UseProvidedAddress = true // Don't try to parse the email address
|
||||
email.Encoding = mail.EncodingBase64 // Doing it to be safe for when we have eventMessage (newline issues)
|
||||
email.AllowDuplicateAddress = true // More "correct" conversion
|
||||
email.AddBccToHeader = true // Don't ignore Bcc
|
||||
email.AllowEmptyAttachments = true // Don't error on empty attachments
|
||||
email.UseProvidedAddress = true // Don't try to parse the email address
|
||||
|
||||
if data.GetFrom() != nil {
|
||||
email.SetFrom(formatAddress(data.GetFrom().GetEmailAddress()))
|
||||
@ -189,6 +282,24 @@ func FromJSON(ctx context.Context, body []byte) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
switch data.(type) {
|
||||
case *models.EventMessageResponse, *models.EventMessage:
|
||||
// We can't handle this as of now, not enough information
|
||||
// TODO: Fetch event object from graph when fetching email
|
||||
case *models.CalendarSharingMessage:
|
||||
// TODO: Parse out calendar sharing message
|
||||
// https://github.com/alcionai/corso/issues/5041
|
||||
case *models.EventMessageRequest:
|
||||
cal, err := getICalData(ctx, data)
|
||||
if err != nil {
|
||||
return "", clues.Wrap(err, "getting ical attachment")
|
||||
}
|
||||
|
||||
if len(cal) > 0 {
|
||||
email.AddAlternative(mail.TextCalendar, cal)
|
||||
}
|
||||
}
|
||||
|
||||
if err = email.GetError(); err != nil {
|
||||
return "", clues.WrapWC(ctx, err, "converting to eml")
|
||||
}
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
package eml
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
ical "github.com/arran4/golang-ical"
|
||||
"github.com/jhillyerd/enmime"
|
||||
kjson "github.com/microsoft/kiota-serialization-json-go"
|
||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||
@ -15,6 +17,7 @@ import (
|
||||
|
||||
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||
"github.com/alcionai/corso/src/internal/converters/eml/testdata"
|
||||
"github.com/alcionai/corso/src/internal/converters/ics"
|
||||
"github.com/alcionai/corso/src/internal/tester"
|
||||
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
||||
)
|
||||
@ -188,3 +191,137 @@ func (suite *EMLUnitSuite) TestConvert_edge_cases() {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *EMLUnitSuite) TestConvert_eml_ics() {
|
||||
t := suite.T()
|
||||
|
||||
ctx, flush := tester.NewContext(t)
|
||||
defer flush()
|
||||
|
||||
body := []byte(testdata.EmailWithEventInfo)
|
||||
|
||||
out, err := FromJSON(ctx, body)
|
||||
assert.NoError(t, err, "converting to eml")
|
||||
|
||||
rmsg, err := api.BytesToMessageable(body)
|
||||
require.NoError(t, err, "creating message")
|
||||
|
||||
msg := rmsg.(*models.EventMessageRequest)
|
||||
|
||||
eml, err := enmime.ReadEnvelope(strings.NewReader(out))
|
||||
require.NoError(t, err, "reading created eml")
|
||||
require.NotNil(t, eml, "eml should not be nil")
|
||||
|
||||
require.Equal(t, 1, len(eml.OtherParts), "eml should have 1 attachment")
|
||||
require.Equal(t, "text/calendar", eml.OtherParts[0].ContentType, "eml attachment should be a calendar")
|
||||
|
||||
catt := *eml.OtherParts[0]
|
||||
cal, err := ical.ParseCalendar(bytes.NewReader(catt.Content))
|
||||
require.NoError(t, err, "parsing calendar")
|
||||
|
||||
event := cal.Events()[0]
|
||||
|
||||
assert.Equal(t, ptr.Val(msg.GetId()), event.Id())
|
||||
assert.Equal(t, ptr.Val(msg.GetSubject()), event.GetProperty(ical.ComponentPropertySummary).Value)
|
||||
|
||||
assert.Equal(
|
||||
t,
|
||||
msg.GetCreatedDateTime().Format(ics.ICalDateTimeFormat),
|
||||
event.GetProperty(ical.ComponentPropertyCreated).Value)
|
||||
assert.Equal(
|
||||
t,
|
||||
msg.GetLastModifiedDateTime().Format(ics.ICalDateTimeFormat),
|
||||
event.GetProperty(ical.ComponentPropertyLastModified).Value)
|
||||
|
||||
st, err := ics.GetUTCTime(
|
||||
ptr.Val(msg.GetStartDateTime().GetDateTime()),
|
||||
ptr.Val(msg.GetStartDateTime().GetTimeZone()))
|
||||
require.NoError(t, err, "getting start time")
|
||||
|
||||
et, err := ics.GetUTCTime(
|
||||
ptr.Val(msg.GetEndDateTime().GetDateTime()),
|
||||
ptr.Val(msg.GetEndDateTime().GetTimeZone()))
|
||||
require.NoError(t, err, "getting end time")
|
||||
|
||||
assert.Equal(
|
||||
t,
|
||||
st.Format(ics.ICalDateTimeFormat),
|
||||
event.GetProperty(ical.ComponentPropertyDtStart).Value)
|
||||
assert.Equal(
|
||||
t,
|
||||
et.Format(ics.ICalDateTimeFormat),
|
||||
event.GetProperty(ical.ComponentPropertyDtEnd).Value)
|
||||
|
||||
tos := msg.GetToRecipients()
|
||||
ccs := msg.GetCcRecipients()
|
||||
att := event.Attendees()
|
||||
|
||||
assert.Equal(t, len(tos)+len(ccs), len(att))
|
||||
|
||||
for _, to := range tos {
|
||||
found := false
|
||||
|
||||
for _, attendee := range att {
|
||||
if "mailto:"+ptr.Val(to.GetEmailAddress().GetAddress()) == attendee.Value {
|
||||
found = true
|
||||
|
||||
assert.Equal(t, "REQ-PARTICIPANT", attendee.ICalParameters["ROLE"][0])
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
assert.True(t, found, "to recipient not found in attendees")
|
||||
}
|
||||
|
||||
for _, cc := range ccs {
|
||||
found := false
|
||||
|
||||
for _, attendee := range att {
|
||||
if "mailto:"+ptr.Val(cc.GetEmailAddress().GetAddress()) == attendee.Value {
|
||||
found = true
|
||||
|
||||
assert.Equal(t, "OPT-PARTICIPANT", attendee.ICalParameters["ROLE"][0])
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
assert.True(t, found, "cc recipient not found in attendees")
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *EMLUnitSuite) TestConvert_eml_ics_from_event_obj() {
|
||||
t := suite.T()
|
||||
|
||||
ctx, flush := tester.NewContext(t)
|
||||
defer flush()
|
||||
|
||||
body := []byte(testdata.EmailWithEventObject)
|
||||
|
||||
out, err := FromJSON(ctx, body)
|
||||
assert.NoError(t, err, "converting to eml")
|
||||
|
||||
rmsg, err := api.BytesToMessageable(body)
|
||||
require.NoError(t, err, "creating message")
|
||||
|
||||
msg := rmsg.(*models.EventMessageRequest)
|
||||
evt := msg.GetEvent()
|
||||
|
||||
eml, err := enmime.ReadEnvelope(strings.NewReader(out))
|
||||
require.NoError(t, err, "reading created eml")
|
||||
require.NotNil(t, eml, "eml should not be nil")
|
||||
|
||||
require.Equal(t, 1, len(eml.OtherParts), "eml should have 1 attachment")
|
||||
require.Equal(t, "text/calendar", eml.OtherParts[0].ContentType, "eml attachment should be a calendar")
|
||||
|
||||
catt := *eml.OtherParts[0]
|
||||
cal, err := ical.ParseCalendar(bytes.NewReader(catt.Content))
|
||||
require.NoError(t, err, "parsing calendar")
|
||||
|
||||
event := cal.Events()[0]
|
||||
|
||||
assert.Equal(t, ptr.Val(evt.GetId()), event.Id())
|
||||
assert.NotEqual(t, ptr.Val(msg.GetSubject()), event.GetProperty(ical.ComponentPropertySummary).Value)
|
||||
assert.Equal(t, ptr.Val(evt.GetSubject()), event.GetProperty(ical.ComponentPropertySummary).Value)
|
||||
}
|
||||
|
||||
103
src/internal/converters/eml/testdata/email-with-event-info.json
vendored
Normal file
103
src/internal/converters/eml/testdata/email-with-event-info.json
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
{
|
||||
"@odata.type": "#microsoft.graph.eventMessageRequest",
|
||||
"@odata.etag": "W/\"CwAAABYAAABBFDg0JJk7TY1fmsJrh7tNAAFY+7zz\"",
|
||||
"id": "AAMkAGJiZmE2NGU4LTQ4YjktNDI1Mi1iMWQzLTQ1MmMxODJkZmQyNABGAAAAAABFdiK7oifWRb4ADuqgSRcnBwBBFDg0JJk7TY1fmsJrh7tNAAAAAAEJAABBFDg0JJk7TY1fmsJrh7tNAAFZU-vfAAA=",
|
||||
"createdDateTime": "2024-01-15T11:26:41Z",
|
||||
"lastModifiedDateTime": "2024-01-15T11:26:43Z",
|
||||
"changeKey": "CwAAABYAAABBFDg0JJk7TY1fmsJrh7tNAAFY+7zz",
|
||||
"categories": [],
|
||||
"receivedDateTime": "2024-01-15T11:26:41Z",
|
||||
"sentDateTime": "2024-01-15T11:26:41Z",
|
||||
"hasAttachments": false,
|
||||
"internetMessageId": "<PH0PR04MB7285DDED30186B1D8E1BD2AABC6C2@PH0PR04MB7285.namprd04.prod.outlook.com>",
|
||||
"subject": "Invitable event",
|
||||
"bodyPreview": "How come the sun is hot?\r\n\r\n________________________________________________________________________________\r\nMicrosoft Teams meeting\r\nJoin on your computer, mobile app or room device\r\nClick here to join the meeting\r\nMeeting ID: 290 273 192 285\r\nPasscode:",
|
||||
"importance": "normal",
|
||||
"parentFolderId": "AQMkAGJiAGZhNjRlOC00OGI5LTQyNTItYjFkMy00NTJjMTgyZGZkMjQALgAAA0V2IruiJ9ZFvgAO6qBJFycBAEEUODQkmTtNjV_awmuHu00AAAIBCQAAAA==",
|
||||
"conversationId": "AAQkAGJiZmE2NGU4LTQ4YjktNDI1Mi1iMWQzLTQ1MmMxODJkZmQyNAAQABoIUFzzByJMltJobtYSAJ0=",
|
||||
"conversationIndex": "AdpHpbXXGghQXPMHIkyW0mhu1hIAnQ==",
|
||||
"isDeliveryReceiptRequested": null,
|
||||
"isReadReceiptRequested": false,
|
||||
"isRead": true,
|
||||
"isDraft": false,
|
||||
"webLink": "https://outlook.office365.com/owa/?ItemID=AAMkAGJiZmE2NGU4LTQ4YjktNDI1Mi1iMWQzLTQ1MmMxODJkZmQyNABGAAAAAABFdiK7oifWRb4ADuqgSRcnBwBBFDg0JJk7TY1fmsJrh7tNAAAAAAEJAABBFDg0JJk7TY1fmsJrh7tNAAFZU%2FvfAAA%3D&exvsurl=1&viewmodel=ReadMessageItem",
|
||||
"inferenceClassification": "focused",
|
||||
"meetingMessageType": "meetingRequest",
|
||||
"type": "seriesMaster",
|
||||
"isOutOfDate": false,
|
||||
"isAllDay": false,
|
||||
"isDelegated": false,
|
||||
"responseRequested": true,
|
||||
"allowNewTimeProposals": null,
|
||||
"meetingRequestType": "newMeetingRequest",
|
||||
"body": {
|
||||
"contentType": "html",
|
||||
"content": "<html><head>\r\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"></head><body><div class=\"elementToProof\" style=\"font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif; font-size:12pt; color:rgb(0,0,0)\">How come the sun is hot?<br></div><br><div style=\"width:100%\"><span style=\"white-space:nowrap; color:#5F5F5F; opacity:.36\">________________________________________________________________________________</span> </div><div class=\"me-email-text\" lang=\"en-US\" style=\"color:#252424; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\"><div style=\"margin-top:24px; margin-bottom:20px\"><span style=\"font-size:24px; color:#252424\">Microsoft Teams meeting</span> </div><div style=\"margin-bottom:20px\"><div style=\"margin-top:0px; margin-bottom:0px; font-weight:bold\"><span style=\"font-size:14px; color:#252424\">Join on your computer, mobile app or room device</span> </div><a class=\"me-email-headline\" href=\"https://teams.microsoft.com/l/meetup-join/19%3ameeting_OGM4MWVlYjUtMjllMi00ZjY5LWE5YjgtMTc4MjJhMWI1MjRl%40thread.v2/0?context=%7b%22Tid%22%3a%22fb8afbaa-e94c-4ea5-8a8a-24aff04d7874%22%2c%22Oid%22%3a%227ceb8e03-bdc5-4509-a136-457526165ec0%22%7d\" target=\"_blank\" rel=\"noreferrer noopener\" style=\"font-size:14px; font-family:'Segoe UI Semibold','Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif; text-decoration:underline; color:#6264a7\">Click here to join the meeting</a> </div><div style=\"margin-bottom:20px; margin-top:20px\"><div style=\"margin-bottom:4px\"><span data-tid=\"meeting-code\" style=\"font-size:14px; color:#252424\">Meeting ID: <span style=\"font-size:16px; color:#252424\">290 273 192 285</span> </span><br><span style=\"font-size:14px; color:#252424\">Passcode: </span><span style=\"font-size:16px; color:#252424\">CwEBTS </span><div style=\"font-size:14px\"><a class=\"me-email-link\" target=\"_blank\" href=\"https://www.microsoft.com/en-us/microsoft-teams/download-app\" rel=\"noreferrer noopener\" style=\"font-size:14px; text-decoration:underline; color:#6264a7; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">Download Teams</a> | <a class=\"me-email-link\" target=\"_blank\" href=\"https://www.microsoft.com/microsoft-teams/join-a-meeting\" rel=\"noreferrer noopener\" style=\"font-size:14px; text-decoration:underline; color:#6264a7; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">Join on the web</a></div></div></div><div style=\"margin-bottom:24px; margin-top:20px\"><a class=\"me-email-link\" target=\"_blank\" href=\"https://aka.ms/JoinTeamsMeeting\" rel=\"noreferrer noopener\" style=\"font-size:14px; text-decoration:underline; color:#6264a7; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">Learn More</a> | <a class=\"me-email-link\" target=\"_blank\" href=\"https://teams.microsoft.com/meetingOptions/?organizerId=7ceb8e03-bdc5-4509-a136-457526165ec0&tenantId=fb8afbaa-e94c-4ea5-8a8a-24aff04d7874&threadId=19_meeting_OGM4MWVlYjUtMjllMi00ZjY5LWE5YjgtMTc4MjJhMWI1MjRl@thread.v2&messageId=0&language=en-US\" rel=\"noreferrer noopener\" style=\"font-size:14px; text-decoration:underline; color:#6264a7; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">Meeting options</a> </div></div><div style=\"font-size:14px; margin-bottom:4px; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\"></div><div style=\"font-size:12px\"></div><div></div><div style=\"width:100%\"><span style=\"white-space:nowrap; color:#5F5F5F; opacity:.36\">________________________________________________________________________________</span> </div></body></html>"
|
||||
},
|
||||
"sender": {
|
||||
"emailAddress": {
|
||||
"name": "Johanna Lorenz",
|
||||
"address": "JohannaL@10rqc2.onmicrosoft.com"
|
||||
}
|
||||
},
|
||||
"from": {
|
||||
"emailAddress": {
|
||||
"name": "Johanna Lorenz",
|
||||
"address": "JohannaL@10rqc2.onmicrosoft.com"
|
||||
}
|
||||
},
|
||||
"toRecipients": [
|
||||
{
|
||||
"emailAddress": {
|
||||
"name": "Faker 1",
|
||||
"address": "fakeid1@provider.com"
|
||||
}
|
||||
}
|
||||
],
|
||||
"ccRecipients": [
|
||||
{
|
||||
"emailAddress": {
|
||||
"name": "Faker 2",
|
||||
"address": "fakeid2@provider.com"
|
||||
}
|
||||
}
|
||||
],
|
||||
"bccRecipients": [],
|
||||
"replyTo": [],
|
||||
"flag": {
|
||||
"flagStatus": "notFlagged"
|
||||
},
|
||||
"startDateTime": {
|
||||
"dateTime": "2024-01-22T02:30:00.0000000",
|
||||
"timeZone": "UTC"
|
||||
},
|
||||
"endDateTime": {
|
||||
"dateTime": "2024-01-22T03:00:00.0000000",
|
||||
"timeZone": "UTC"
|
||||
},
|
||||
"location": {
|
||||
"displayName": "Microsoft Teams Meeting",
|
||||
"locationType": "default",
|
||||
"uniqueIdType": "unknown"
|
||||
},
|
||||
"recurrence": {
|
||||
"pattern": {
|
||||
"type": "daily",
|
||||
"interval": 1,
|
||||
"month": 0,
|
||||
"dayOfMonth": 0,
|
||||
"firstDayOfWeek": "sunday",
|
||||
"index": "first"
|
||||
},
|
||||
"range": {
|
||||
"type": "endDate",
|
||||
"startDate": "2024-01-21",
|
||||
"endDate": "2024-01-24",
|
||||
"recurrenceTimeZone": "tzone://Microsoft/Utc",
|
||||
"numberOfOccurrences": 0
|
||||
}
|
||||
},
|
||||
"previousLocation": null,
|
||||
"previousStartDateTime": null,
|
||||
"previousEndDateTime": null
|
||||
}
|
||||
204
src/internal/converters/eml/testdata/email-with-event-object.json
vendored
Normal file
204
src/internal/converters/eml/testdata/email-with-event-object.json
vendored
Normal file
@ -0,0 +1,204 @@
|
||||
{
|
||||
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('JohannaL%4010rqc2.onmicrosoft.com')/messages(microsoft.graph.eventMessage/event())/$entity",
|
||||
"@odata.type": "#microsoft.graph.eventMessageRequest",
|
||||
"@odata.etag": "W/\"CwAAABYAAABBFDg0JJk7TY1fmsJrh7tNAAFY+7zz\"",
|
||||
"id": "AAMkAGJiZmE2NGU4LTQ4YjktNDI1Mi1iMWQzLTQ1MmMxODJkZmQyNABGAAAAAABFdiK7oifWRb4ADuqgSRcnBwBBFDg0JJk7TY1fmsJrh7tNAAAAAAEJAABBFDg0JJk7TY1fmsJrh7tNAAFZU-vfAAA=",
|
||||
"createdDateTime": "2024-01-15T11:26:41Z",
|
||||
"lastModifiedDateTime": "2024-01-15T11:26:43Z",
|
||||
"changeKey": "CwAAABYAAABBFDg0JJk7TY1fmsJrh7tNAAFY+7zz",
|
||||
"categories": [],
|
||||
"receivedDateTime": "2024-01-15T11:26:41Z",
|
||||
"sentDateTime": "2024-01-15T11:26:41Z",
|
||||
"hasAttachments": false,
|
||||
"internetMessageId": "<PH0PR04MB7285DDED30186B1D8E1BD2AABC6C2@PH0PR04MB7285.namprd04.prod.outlook.com>",
|
||||
"subject": "Invitable event",
|
||||
"bodyPreview": "How come the sun is hot?\r\n\r\n________________________________________________________________________________\r\nMicrosoft Teams meeting\r\nJoin on your computer, mobile app or room device\r\nClick here to join the meeting\r\nMeeting ID: 290 273 192 285\r\nPasscode:",
|
||||
"importance": "normal",
|
||||
"parentFolderId": "AQMkAGJiAGZhNjRlOC00OGI5LTQyNTItYjFkMy00NTJjMTgyZGZkMjQALgAAA0V2IruiJ9ZFvgAO6qBJFycBAEEUODQkmTtNjV_awmuHu00AAAIBCQAAAA==",
|
||||
"conversationId": "AAQkAGJiZmE2NGU4LTQ4YjktNDI1Mi1iMWQzLTQ1MmMxODJkZmQyNAAQABoIUFzzByJMltJobtYSAJ0=",
|
||||
"conversationIndex": "AdpHpbXXGghQXPMHIkyW0mhu1hIAnQ==",
|
||||
"isDeliveryReceiptRequested": null,
|
||||
"isReadReceiptRequested": false,
|
||||
"isRead": true,
|
||||
"isDraft": false,
|
||||
"webLink": "https://outlook.office365.com/owa/?ItemID=AAMkAGJiZmE2NGU4LTQ4YjktNDI1Mi1iMWQzLTQ1MmMxODJkZmQyNABGAAAAAABFdiK7oifWRb4ADuqgSRcnBwBBFDg0JJk7TY1fmsJrh7tNAAAAAAEJAABBFDg0JJk7TY1fmsJrh7tNAAFZU%2FvfAAA%3D&exvsurl=1&viewmodel=ReadMessageItem",
|
||||
"inferenceClassification": "focused",
|
||||
"meetingMessageType": "meetingRequest",
|
||||
"type": "seriesMaster",
|
||||
"isOutOfDate": false,
|
||||
"isAllDay": false,
|
||||
"isDelegated": false,
|
||||
"responseRequested": true,
|
||||
"allowNewTimeProposals": null,
|
||||
"meetingRequestType": "newMeetingRequest",
|
||||
"body": {
|
||||
"contentType": "html",
|
||||
"content": "<html><head>\r\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"></head><body><div class=\"elementToProof\" style=\"font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif; font-size:12pt; color:rgb(0,0,0)\">How come the sun is hot?<br></div><br><div style=\"width:100%\"><span style=\"white-space:nowrap; color:#5F5F5F; opacity:.36\">________________________________________________________________________________</span> </div><div class=\"me-email-text\" lang=\"en-US\" style=\"color:#252424; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\"><div style=\"margin-top:24px; margin-bottom:20px\"><span style=\"font-size:24px; color:#252424\">Microsoft Teams meeting</span> </div><div style=\"margin-bottom:20px\"><div style=\"margin-top:0px; margin-bottom:0px; font-weight:bold\"><span style=\"font-size:14px; color:#252424\">Join on your computer, mobile app or room device</span> </div><a class=\"me-email-headline\" href=\"https://teams.microsoft.com/l/meetup-join/19%3ameeting_OGM4MWVlYjUtMjllMi00ZjY5LWE5YjgtMTc4MjJhMWI1MjRl%40thread.v2/0?context=%7b%22Tid%22%3a%22fb8afbaa-e94c-4ea5-8a8a-24aff04d7874%22%2c%22Oid%22%3a%227ceb8e03-bdc5-4509-a136-457526165ec0%22%7d\" target=\"_blank\" rel=\"noreferrer noopener\" style=\"font-size:14px; font-family:'Segoe UI Semibold','Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif; text-decoration:underline; color:#6264a7\">Click here to join the meeting</a> </div><div style=\"margin-bottom:20px; margin-top:20px\"><div style=\"margin-bottom:4px\"><span data-tid=\"meeting-code\" style=\"font-size:14px; color:#252424\">Meeting ID: <span style=\"font-size:16px; color:#252424\">290 273 192 285</span> </span><br><span style=\"font-size:14px; color:#252424\">Passcode: </span><span style=\"font-size:16px; color:#252424\">CwEBTS </span><div style=\"font-size:14px\"><a class=\"me-email-link\" target=\"_blank\" href=\"https://www.microsoft.com/en-us/microsoft-teams/download-app\" rel=\"noreferrer noopener\" style=\"font-size:14px; text-decoration:underline; color:#6264a7; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">Download Teams</a> | <a class=\"me-email-link\" target=\"_blank\" href=\"https://www.microsoft.com/microsoft-teams/join-a-meeting\" rel=\"noreferrer noopener\" style=\"font-size:14px; text-decoration:underline; color:#6264a7; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">Join on the web</a></div></div></div><div style=\"margin-bottom:24px; margin-top:20px\"><a class=\"me-email-link\" target=\"_blank\" href=\"https://aka.ms/JoinTeamsMeeting\" rel=\"noreferrer noopener\" style=\"font-size:14px; text-decoration:underline; color:#6264a7; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">Learn More</a> | <a class=\"me-email-link\" target=\"_blank\" href=\"https://teams.microsoft.com/meetingOptions/?organizerId=7ceb8e03-bdc5-4509-a136-457526165ec0&tenantId=fb8afbaa-e94c-4ea5-8a8a-24aff04d7874&threadId=19_meeting_OGM4MWVlYjUtMjllMi00ZjY5LWE5YjgtMTc4MjJhMWI1MjRl@thread.v2&messageId=0&language=en-US\" rel=\"noreferrer noopener\" style=\"font-size:14px; text-decoration:underline; color:#6264a7; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">Meeting options</a> </div></div><div style=\"font-size:14px; margin-bottom:4px; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\"></div><div style=\"font-size:12px\"></div><div></div><div style=\"width:100%\"><span style=\"white-space:nowrap; color:#5F5F5F; opacity:.36\">________________________________________________________________________________</span> </div></body></html>"
|
||||
},
|
||||
"sender": {
|
||||
"emailAddress": {
|
||||
"name": "Johanna Lorenz",
|
||||
"address": "JohannaL@10rqc2.onmicrosoft.com"
|
||||
}
|
||||
},
|
||||
"from": {
|
||||
"emailAddress": {
|
||||
"name": "Johanna Lorenz",
|
||||
"address": "JohannaL@10rqc2.onmicrosoft.com"
|
||||
}
|
||||
},
|
||||
"toRecipients": [
|
||||
{
|
||||
"emailAddress": {
|
||||
"name": "Pradeep Gupta",
|
||||
"address": "PradeepG@10rqc2.onmicrosoft.com"
|
||||
}
|
||||
}
|
||||
],
|
||||
"ccRecipients": [],
|
||||
"bccRecipients": [],
|
||||
"replyTo": [],
|
||||
"flag": {
|
||||
"flagStatus": "notFlagged"
|
||||
},
|
||||
"startDateTime": {
|
||||
"dateTime": "2024-01-22T02:30:00.0000000",
|
||||
"timeZone": "UTC"
|
||||
},
|
||||
"endDateTime": {
|
||||
"dateTime": "2024-01-22T03:00:00.0000000",
|
||||
"timeZone": "UTC"
|
||||
},
|
||||
"location": {
|
||||
"displayName": "Microsoft Teams Meeting",
|
||||
"locationType": "default",
|
||||
"uniqueIdType": "unknown"
|
||||
},
|
||||
"recurrence": {
|
||||
"pattern": {
|
||||
"type": "daily",
|
||||
"interval": 1,
|
||||
"month": 0,
|
||||
"dayOfMonth": 0,
|
||||
"firstDayOfWeek": "sunday",
|
||||
"index": "first"
|
||||
},
|
||||
"range": {
|
||||
"type": "endDate",
|
||||
"startDate": "2024-01-21",
|
||||
"endDate": "2024-01-24",
|
||||
"recurrenceTimeZone": "tzone://Microsoft/Utc",
|
||||
"numberOfOccurrences": 0
|
||||
}
|
||||
},
|
||||
"previousLocation": null,
|
||||
"previousStartDateTime": null,
|
||||
"previousEndDateTime": null,
|
||||
"event@odata.associationLink": "https://graph.microsoft.com/v1.0/users('JohannaL@10rqc2.onmicrosoft.com')/events('AAMkAGJiZmE2NGU4LTQ4YjktNDI1Mi1iMWQzLTQ1MmMxODJkZmQyNABGAAAAAABFdiK7oifWRb4ADuqgSRcnBwBBFDg0JJk7TY1fmsJrh7tNAAAAAAENAABBFDg0JJk7TY1fmsJrh7tNAAFZU--pAAA=')/$ref",
|
||||
"event@odata.navigationLink": "https://graph.microsoft.com/v1.0/users('JohannaL@10rqc2.onmicrosoft.com')/events('AAMkAGJiZmE2NGU4LTQ4YjktNDI1Mi1iMWQzLTQ1MmMxODJkZmQyNABGAAAAAABFdiK7oifWRb4ADuqgSRcnBwBBFDg0JJk7TY1fmsJrh7tNAAAAAAENAABBFDg0JJk7TY1fmsJrh7tNAAFZU--pAAA=')",
|
||||
"event": {
|
||||
"@odata.etag": "W/\"QRQ4NCSZO02NX5rCa4e7TQABWPwm+A==\"",
|
||||
"id": "AAMkAGJiZmE2NGU4LTQ4YjktNDI1Mi1iMWQzLTQ1MmMxODJkZmQyNABGAAAAAABFdiK7oifWRb4ADuqgSRcnBwBBFDg0JJk7TY1fmsJrh7tNAAAAAAENAABBFDg0JJk7TY1fmsJrh7tNAAFZU--pAAA=",
|
||||
"createdDateTime": "2024-01-15T11:26:39.1524133Z",
|
||||
"lastModifiedDateTime": "2024-01-16T10:29:34.8704061Z",
|
||||
"changeKey": "QRQ4NCSZO02NX5rCa4e7TQABWPwm+A==",
|
||||
"categories": [],
|
||||
"transactionId": "d19de894-1d85-dde1-ea5f-9332e850667b",
|
||||
"originalStartTimeZone": "India Standard Time",
|
||||
"originalEndTimeZone": "India Standard Time",
|
||||
"iCalUId": "040000008200E00074C5B7101A82E008000000002757EEB4A547DA01000000000000000010000000CCC41D0213F00E489061EF756A0E6864",
|
||||
"reminderMinutesBeforeStart": 15,
|
||||
"isReminderOn": true,
|
||||
"hasAttachments": false,
|
||||
"subject": "Different title to test",
|
||||
"bodyPreview": "How come the sun is hot?\r\n\r\n________________________________________________________________________________\r\nMicrosoft Teams meeting\r\nJoin on your computer, mobile app or room device\r\nClick here to join the meeting\r\nMeeting ID: 290 273 192 285\r\nPasscode:",
|
||||
"importance": "normal",
|
||||
"sensitivity": "normal",
|
||||
"isAllDay": false,
|
||||
"isCancelled": false,
|
||||
"isOrganizer": true,
|
||||
"responseRequested": true,
|
||||
"seriesMasterId": null,
|
||||
"showAs": "busy",
|
||||
"type": "seriesMaster",
|
||||
"webLink": "https://outlook.office365.com/owa/?itemid=AAMkAGJiZmE2NGU4LTQ4YjktNDI1Mi1iMWQzLTQ1MmMxODJkZmQyNABGAAAAAABFdiK7oifWRb4ADuqgSRcnBwBBFDg0JJk7TY1fmsJrh7tNAAAAAAENAABBFDg0JJk7TY1fmsJrh7tNAAFZU%2F%2FpAAA%3D&exvsurl=1&path=/calendar/item",
|
||||
"onlineMeetingUrl": null,
|
||||
"isOnlineMeeting": true,
|
||||
"onlineMeetingProvider": "teamsForBusiness",
|
||||
"allowNewTimeProposals": true,
|
||||
"occurrenceId": null,
|
||||
"isDraft": false,
|
||||
"hideAttendees": false,
|
||||
"responseStatus": {
|
||||
"response": "organizer",
|
||||
"time": "0001-01-01T00:00:00Z"
|
||||
},
|
||||
"body": {
|
||||
"contentType": "html",
|
||||
"content": "<html>\r\n<head>\r\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\r\n</head>\r\n<body>\r\n<div class=\"elementToProof\" style=\"font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif; font-size:12pt; color:rgb(0,0,0)\">\r\nHow come the sun is hot?<br>\r\n</div>\r\n<br>\r\n<div style=\"width:100%\"><span style=\"white-space:nowrap; color:#5F5F5F; opacity:.36\">________________________________________________________________________________</span>\r\n</div>\r\n<div class=\"me-email-text\" lang=\"en-US\" style=\"color:#252424; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">\r\n<div style=\"margin-top:24px; margin-bottom:20px\"><span style=\"font-size:24px; color:#252424\">Microsoft Teams meeting</span>\r\n</div>\r\n<div style=\"margin-bottom:20px\">\r\n<div style=\"margin-top:0px; margin-bottom:0px; font-weight:bold\"><span style=\"font-size:14px; color:#252424\">Join on your computer, mobile app or room device</span>\r\n</div>\r\n<a href=\"https://teams.microsoft.com/l/meetup-join/19%3ameeting_OGM4MWVlYjUtMjllMi00ZjY5LWE5YjgtMTc4MjJhMWI1MjRl%40thread.v2/0?context=%7b%22Tid%22%3a%22fb8afbaa-e94c-4ea5-8a8a-24aff04d7874%22%2c%22Oid%22%3a%227ceb8e03-bdc5-4509-a136-457526165ec0%22%7d\" class=\"me-email-headline\" style=\"font-size:14px; font-family:'Segoe UI Semibold','Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif; text-decoration:underline; color:#6264a7\">Click\r\n here to join the meeting</a> </div>\r\n<div style=\"margin-bottom:20px; margin-top:20px\">\r\n<div style=\"margin-bottom:4px\"><span data-tid=\"meeting-code\" style=\"font-size:14px; color:#252424\">Meeting ID:\r\n<span style=\"font-size:16px; color:#252424\">290 273 192 285</span> </span><br>\r\n<span style=\"font-size:14px; color:#252424\">Passcode: </span><span style=\"font-size:16px; color:#252424\">CwEBTS\r\n</span>\r\n<div style=\"font-size:14px\"><a href=\"https://www.microsoft.com/en-us/microsoft-teams/download-app\" class=\"me-email-link\" style=\"font-size:14px; text-decoration:underline; color:#6264a7; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">Download\r\n Teams</a> | <a href=\"https://www.microsoft.com/microsoft-teams/join-a-meeting\" class=\"me-email-link\" style=\"font-size:14px; text-decoration:underline; color:#6264a7; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">\r\nJoin on the web</a></div>\r\n</div>\r\n</div>\r\n<div style=\"margin-bottom:24px; margin-top:20px\"><a href=\"https://aka.ms/JoinTeamsMeeting\" class=\"me-email-link\" style=\"font-size:14px; text-decoration:underline; color:#6264a7; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">Learn More</a>\r\n | <a href=\"https://teams.microsoft.com/meetingOptions/?organizerId=7ceb8e03-bdc5-4509-a136-457526165ec0&tenantId=fb8afbaa-e94c-4ea5-8a8a-24aff04d7874&threadId=19_meeting_OGM4MWVlYjUtMjllMi00ZjY5LWE5YjgtMTc4MjJhMWI1MjRl@thread.v2&messageId=0&language=en-US\" class=\"me-email-link\" style=\"font-size:14px; text-decoration:underline; color:#6264a7; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">\r\nMeeting options</a> </div>\r\n</div>\r\n<div style=\"font-size:14px; margin-bottom:4px; font-family:'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif\">\r\n</div>\r\n<div style=\"font-size:12px\"></div>\r\n<div></div>\r\n<div style=\"width:100%\"><span style=\"white-space:nowrap; color:#5F5F5F; opacity:.36\">________________________________________________________________________________</span>\r\n</div>\r\n</body>\r\n</html>\r\n"
|
||||
},
|
||||
"start": {
|
||||
"dateTime": "2024-01-22T02:30:00.0000000",
|
||||
"timeZone": "UTC"
|
||||
},
|
||||
"end": {
|
||||
"dateTime": "2024-01-22T03:00:00.0000000",
|
||||
"timeZone": "UTC"
|
||||
},
|
||||
"location": {
|
||||
"displayName": "Microsoft Teams Meeting",
|
||||
"locationType": "default",
|
||||
"uniqueId": "Microsoft Teams Meeting",
|
||||
"uniqueIdType": "private"
|
||||
},
|
||||
"locations": [
|
||||
{
|
||||
"displayName": "Microsoft Teams Meeting",
|
||||
"locationType": "default",
|
||||
"uniqueId": "Microsoft Teams Meeting",
|
||||
"uniqueIdType": "private"
|
||||
}
|
||||
],
|
||||
"recurrence": {
|
||||
"pattern": {
|
||||
"type": "daily",
|
||||
"interval": 1,
|
||||
"month": 0,
|
||||
"dayOfMonth": 0,
|
||||
"firstDayOfWeek": "sunday",
|
||||
"index": "first"
|
||||
},
|
||||
"range": {
|
||||
"type": "endDate",
|
||||
"startDate": "2024-01-22",
|
||||
"endDate": "2024-01-25",
|
||||
"recurrenceTimeZone": "India Standard Time",
|
||||
"numberOfOccurrences": 0
|
||||
}
|
||||
},
|
||||
"attendees": [
|
||||
{
|
||||
"type": "required",
|
||||
"status": {
|
||||
"response": "none",
|
||||
"time": "0001-01-01T00:00:00Z"
|
||||
},
|
||||
"emailAddress": {
|
||||
"name": "Pradeep Gupta",
|
||||
"address": "PradeepG@10rqc2.onmicrosoft.com"
|
||||
}
|
||||
}
|
||||
],
|
||||
"organizer": {
|
||||
"emailAddress": {
|
||||
"name": "Johanna Lorenz",
|
||||
"address": "JohannaL@10rqc2.onmicrosoft.com"
|
||||
}
|
||||
},
|
||||
"onlineMeeting": {
|
||||
"joinUrl": "https://teams.microsoft.com/l/meetup-join/19%3ameeting_OGM4MWVlYjUtMjllMi00ZjY5LWE5YjgtMTc4MjJhMWI1MjRl%40thread.v2/0?context=%7b%22Tid%22%3a%22fb8afbaa-e94c-4ea5-8a8a-24aff04d7874%22%2c%22Oid%22%3a%227ceb8e03-bdc5-4509-a136-457526165ec0%22%7d"
|
||||
},
|
||||
"calendar@odata.associationLink": "https://graph.microsoft.com/v1.0/users('JohannaL@10rqc2.onmicrosoft.com')/calendars('AQMkAGJiAGZhNjRlOC00OGI5LTQyNTItYjFkMy00NTJjMTgyZGZkMjQALgAAA0V2IruiJ9ZFvgAO6qBJFycBAEEUODQkmTtNjV_awmuHu00AAAIBDQAAAA==')/$ref",
|
||||
"calendar@odata.navigationLink": "https://graph.microsoft.com/v1.0/users('JohannaL@10rqc2.onmicrosoft.com')/calendars('AQMkAGJiAGZhNjRlOC00OGI5LTQyNTItYjFkMy00NTJjMTgyZGZkMjQALgAAA0V2IruiJ9ZFvgAO6qBJFycBAEEUODQkmTtNjV_awmuHu00AAAIBDQAAAA==')"
|
||||
}
|
||||
}
|
||||
@ -4,3 +4,9 @@ import _ "embed"
|
||||
|
||||
//go:embed email-with-attachments.json
|
||||
var EmailWithAttachments string
|
||||
|
||||
//go:embed email-with-event-info.json
|
||||
var EmailWithEventInfo string
|
||||
|
||||
//go:embed email-with-event-object.json
|
||||
var EmailWithEventObject string
|
||||
|
||||
@ -30,8 +30,8 @@ import (
|
||||
// TODO locations: https://github.com/alcionai/corso/issues/5003
|
||||
|
||||
const (
|
||||
iCalDateTimeFormat = "20060102T150405Z"
|
||||
iCalDateFormat = "20060102"
|
||||
ICalDateTimeFormat = "20060102T150405Z"
|
||||
ICalDateFormat = "20060102"
|
||||
)
|
||||
|
||||
func keyValues(key, value string) *ics.KeyValues {
|
||||
@ -71,7 +71,7 @@ func getLocationString(location models.Locationable) string {
|
||||
return strings.Join(nonEmpty, ", ")
|
||||
}
|
||||
|
||||
func getUTCTime(ts, tz string) (time.Time, error) {
|
||||
func GetUTCTime(ts, tz string) (time.Time, error) {
|
||||
// Timezone is always converted to UTC. This is the easiest way to
|
||||
// ensure we have the correct time as the .ics file expects the same
|
||||
// timezone everywhere according to the spec.
|
||||
@ -179,14 +179,14 @@ func getRecurrencePattern(
|
||||
// the resolution we need
|
||||
parsedTime = parsedTime.Add(24*time.Hour - 1*time.Second)
|
||||
|
||||
endTime, err := getUTCTime(
|
||||
endTime, err := GetUTCTime(
|
||||
parsedTime.Format(string(dttm.M365DateTimeTimeZone)),
|
||||
ptr.Val(rrange.GetRecurrenceTimeZone()))
|
||||
if err != nil {
|
||||
return "", clues.WrapWC(ctx, err, "parsing end time")
|
||||
}
|
||||
|
||||
recurComponents = append(recurComponents, "UNTIL="+endTime.Format(iCalDateTimeFormat))
|
||||
recurComponents = append(recurComponents, "UNTIL="+endTime.Format(ICalDateTimeFormat))
|
||||
}
|
||||
case models.NOEND_RECURRENCERANGETYPE:
|
||||
// Nothing to do
|
||||
@ -208,13 +208,17 @@ func FromJSON(ctx context.Context, body []byte) (string, error) {
|
||||
With("body_len", len(body))
|
||||
}
|
||||
|
||||
return FromEventable(ctx, event)
|
||||
}
|
||||
|
||||
func FromEventable(ctx context.Context, event models.Eventable) (string, error) {
|
||||
cal := ics.NewCalendar()
|
||||
cal.SetProductId("-//Alcion//Corso") // Does this have to be customizable?
|
||||
|
||||
id := ptr.Val(event.GetId())
|
||||
iCalEvent := cal.AddEvent(id)
|
||||
|
||||
err = updateEventProperties(ctx, event, iCalEvent)
|
||||
err := updateEventProperties(ctx, event, iCalEvent)
|
||||
if err != nil {
|
||||
return "", clues.Wrap(err, "updating event properties")
|
||||
}
|
||||
@ -245,7 +249,7 @@ func FromJSON(ctx context.Context, body []byte) (string, error) {
|
||||
exICalEvent := cal.AddEvent(id)
|
||||
start := exception.GetOriginalStart() // will always be in UTC
|
||||
|
||||
exICalEvent.AddProperty(ics.ComponentProperty(ics.PropertyRecurrenceId), start.Format(iCalDateTimeFormat))
|
||||
exICalEvent.AddProperty(ics.ComponentProperty(ics.PropertyRecurrenceId), start.Format(ICalDateTimeFormat))
|
||||
|
||||
err = updateEventProperties(ctx, exception, exICalEvent)
|
||||
if err != nil {
|
||||
@ -285,7 +289,7 @@ func updateEventProperties(ctx context.Context, event models.Eventable, iCalEven
|
||||
startTimezone := event.GetStart().GetTimeZone()
|
||||
|
||||
if startString != nil {
|
||||
start, err := getUTCTime(ptr.Val(startString), ptr.Val(startTimezone))
|
||||
start, err := GetUTCTime(ptr.Val(startString), ptr.Val(startTimezone))
|
||||
if err != nil {
|
||||
return clues.WrapWC(ctx, err, "parsing start time")
|
||||
}
|
||||
@ -302,7 +306,7 @@ func updateEventProperties(ctx context.Context, event models.Eventable, iCalEven
|
||||
endTimezone := event.GetEnd().GetTimeZone()
|
||||
|
||||
if endString != nil {
|
||||
end, err := getUTCTime(ptr.Val(endString), ptr.Val(endTimezone))
|
||||
end, err := GetUTCTime(ptr.Val(endString), ptr.Val(endTimezone))
|
||||
if err != nil {
|
||||
return clues.WrapWC(ctx, err, "parsing end time")
|
||||
}
|
||||
@ -577,7 +581,7 @@ func updateEventProperties(ctx context.Context, event models.Eventable, iCalEven
|
||||
|
||||
dateStrings := []string{}
|
||||
for _, date := range cancelledDates {
|
||||
dateStrings = append(dateStrings, date.Format(iCalDateFormat))
|
||||
dateStrings = append(dateStrings, date.Format(ICalDateFormat))
|
||||
}
|
||||
|
||||
if len(dateStrings) > 0 {
|
||||
@ -598,7 +602,7 @@ func getCancelledDates(ctx context.Context, event models.Eventable) ([]time.Time
|
||||
|
||||
for _, ds := range dateStrings {
|
||||
// the data just contains date and no time which seems to work
|
||||
start, err := getUTCTime(ds, tz)
|
||||
start, err := GetUTCTime(ds, tz)
|
||||
if err != nil {
|
||||
return nil, clues.WrapWC(ctx, err, "parsing cancelled event date")
|
||||
}
|
||||
|
||||
@ -156,7 +156,7 @@ func (suite *ICSUnitSuite) TestGetUTCTime() {
|
||||
|
||||
for _, tt := range table {
|
||||
suite.Run(tt.name, func() {
|
||||
t, err := getUTCTime(tt.timestamp, tt.timezone)
|
||||
t, err := GetUTCTime(tt.timestamp, tt.timezone)
|
||||
tt.errCheck(suite.T(), err)
|
||||
|
||||
if !tt.time.Equal(time.Time{}) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user