diff --git a/src/internal/connector/exchange/event.go b/src/internal/connector/exchange/event.go new file mode 100644 index 000000000..9e1b7c003 --- /dev/null +++ b/src/internal/connector/exchange/event.go @@ -0,0 +1,43 @@ +package exchange + +import ( + "time" + + "github.com/microsoftgraph/msgraph-sdk-go/models" + + "github.com/alcionai/corso/internal/common" + "github.com/alcionai/corso/pkg/backup/details" +) + +// EventInfo searchable metadata for stored event objects. +func EventInfo(evt models.Eventable) *details.ExchangeInfo { + organizer := "" + subject := "" + start := time.Time{} + + if evt.GetOrganizer() != nil && + evt.GetOrganizer().GetEmailAddress() != nil && + evt.GetOrganizer().GetEmailAddress().GetAddress() != nil { + organizer = *evt.GetOrganizer(). + GetEmailAddress(). + GetAddress() + } + if evt.GetSubject() != nil { + subject = *evt.GetSubject() + } + if evt.GetStart() != nil && + evt.GetStart().GetDateTime() != nil { + // timeString has 'Z' literal added to ensure the stored + // DateTime is not: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC) + timeString := *evt.GetStart().GetDateTime() + "Z" + output, err := common.ParseTime(timeString) + if err == nil { + start = output + } + } + return &details.ExchangeInfo{ + Organizer: organizer, + Subject: subject, + EventStart: start, + } +} diff --git a/src/internal/connector/exchange/event_test.go b/src/internal/connector/exchange/event_test.go new file mode 100644 index 000000000..02b4603d8 --- /dev/null +++ b/src/internal/connector/exchange/event_test.go @@ -0,0 +1,98 @@ +package exchange + +import ( + "testing" + "time" + + "github.com/microsoftgraph/msgraph-sdk-go/models" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/alcionai/corso/internal/common" + "github.com/alcionai/corso/internal/connector/mockconnector" + "github.com/alcionai/corso/internal/connector/support" + "github.com/alcionai/corso/pkg/backup/details" +) + +type EventSuite struct { + suite.Suite +} + +func TestEventSuite(t *testing.T) { + suite.Run(t, &EventSuite{}) +} + +// TestEventInfo verifies that searchable event metadata +// can be properly retrieved from a models.Eventable object +func (suite *EventSuite) TestEventInfo() { + initial := time.Now() + + now := initial.Format(common.StandardTimeFormat) + suite.T().Logf("Initial: %v\nFormatted: %v\n", initial, now) + tests := []struct { + name string + evtAndRP func() (models.Eventable, *details.ExchangeInfo) + }{ + { + name: "Empty event", + evtAndRP: func() (models.Eventable, *details.ExchangeInfo) { + return models.NewEvent(), &details.ExchangeInfo{} + }, + }, + { + name: "Start time only", + evtAndRP: func() (models.Eventable, *details.ExchangeInfo) { + event := models.NewEvent() + dateTime := models.NewDateTimeTimeZone() + dateTime.SetDateTime(&now) + event.SetStart(dateTime) + full, err := time.Parse(common.StandardTimeFormat, now) + require.NoError(suite.T(), err) + return event, &details.ExchangeInfo{Received: full} + }, + }, + { + name: "Subject Only", + evtAndRP: func() (models.Eventable, *details.ExchangeInfo) { + subject := "Hello Corso" + event := models.NewEvent() + event.SetSubject(&subject) + return event, &details.ExchangeInfo{Subject: subject} + }, + }, + { + name: "Using mockable", + evtAndRP: func() (models.Eventable, *details.ExchangeInfo) { + bytes := mockconnector.GetMockEventBytes("Test Mock") + event, err := support.CreateEventFromBytes(bytes) + require.NoError(suite.T(), err) + subject := " Test MockReview + Lunch" + organizer := "foobar3@8qzvrj.onmicrosoft.com" + eventTime := time.Date(2022, time.April, 28, 3, 41, 58, 0, time.UTC) + return event, &details.ExchangeInfo{ + Subject: subject, + Organizer: organizer, + EventStart: eventTime, + } + }, + }, + } + for _, test := range tests { + suite.T().Run(test.name, func(t *testing.T) { + event, expected := test.evtAndRP() + result := EventInfo(event) + suite.Equal(expected.Subject, result.Subject) + suite.Equal(expected.Sender, result.Sender) + expYear, expMonth, expDay := expected.EventStart.Date() + expHr, expMin, expSec := expected.EventStart.Clock() + recvYear, recvMonth, recvDay := result.EventStart.Date() + recvHr, recvMin, recvSec := result.EventStart.Clock() + suite.Equal(expYear, recvYear) + suite.Equal(expMonth, recvMonth) + suite.Equal(expDay, recvDay) + suite.Equal(expHr, recvHr) + suite.Equal(expMin, recvMin) + suite.Equal(expSec, recvSec) + }) + } +} diff --git a/src/internal/connector/exchange/exchange_data_collection.go b/src/internal/connector/exchange/exchange_data_collection.go index c631fe54c..075023373 100644 --- a/src/internal/connector/exchange/exchange_data_collection.go +++ b/src/internal/connector/exchange/exchange_data_collection.go @@ -225,7 +225,7 @@ func eventToDataCollection( return support.WrapAndAppend(*event.GetId(), errors.Wrap(err, "serializing content"), nil) } if byteArray != nil { - dataChannel <- &Stream{id: *event.GetId(), message: byteArray, info: nil} + dataChannel <- &Stream{id: *event.GetId(), message: byteArray, info: EventInfo(event)} } return nil } diff --git a/src/pkg/backup/details/details.go b/src/pkg/backup/details/details.go index 81e24c4ee..3ada95c2d 100644 --- a/src/pkg/backup/details/details.go +++ b/src/pkg/backup/details/details.go @@ -128,6 +128,8 @@ type ExchangeInfo struct { Sender string `json:"sender,omitempty"` Subject string `json:"subject,omitempty"` Received time.Time `json:"received,omitempty"` + EventStart time.Time `json:"eventStart,omitempty"` + Organizer string `json:"organizer,omitempty"` ContactName string `json:"contactName,omitempty"` }