Improvements and tests for eml conversion (#4644)

<!-- PR description-->

---

#### Does this PR need a docs update or release note?

- [ ]  Yes, it's included
- [x] 🕐 Yes, but in a later PR
- [ ]  No

#### Type of change

<!--- Please check the type of change your PR introduces: --->
- [ ] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [x] 🤖 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/3893

#### Test Plan

<!-- How will this be tested prior to merging.-->
- [ ] 💪 Manual
- [x]  Unit test
- [ ] 💚 E2E
This commit is contained in:
Abin Simon 2023-11-16 12:33:36 +05:30 committed by GitHub
parent 576b8b6370
commit c3b7246ee9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 74 additions and 34 deletions

View File

@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] (beta)
### Added
- Added export support for emails in exchange backups as `.eml` files
### Changed
- Change file extension of messages export to json to match the content

View File

@ -7,7 +7,6 @@ import (
"os"
"github.com/alcionai/corso/src/internal/converters/eml"
"github.com/alcionai/corso/src/pkg/services/m365/api"
)
func main() {
@ -31,12 +30,7 @@ func main() {
case "msg":
switch to {
case "eml":
msg, err := api.BytesToMessageable(body)
if err != nil {
log.Fatal(err)
}
out, err = eml.ToEml(context.Background(), msg)
out, err = eml.FromJSON(context.Background(), body)
if err != nil {
log.Fatal(err)
}

View File

@ -13,26 +13,31 @@ import (
"fmt"
"github.com/alcionai/clues"
"github.com/microsoftgraph/msgraph-sdk-go/models"
mail "github.com/xhit/go-simple-mail/v2"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/pkg/logger"
"github.com/alcionai/corso/src/pkg/services/m365/api"
)
const (
fromFormat = "%s <%s>"
dateFormat = "2006-01-02 15:04:05 MST" // from xhit/go-simple-mail
addressFormat = "%s <%s>"
dateFormat = "2006-01-02 15:04:05 MST" // from xhit/go-simple-mail
)
// ToEml converts a Messageable to .eml format
func ToEml(ctx context.Context, data models.Messageable) (string, error) {
// FromJSON converts a Messageable (as json) to .eml format
func FromJSON(ctx context.Context, body []byte) (string, error) {
data, err := api.BytesToMessageable(body)
if err != nil {
return "", clues.Wrap(err, "converting to messageble")
}
email := mail.NewMSG()
if data.GetFrom() != nil {
email.SetFrom(
fmt.Sprintf(
fromFormat,
addressFormat,
ptr.Val(data.GetFrom().GetEmailAddress().GetName()),
ptr.Val(data.GetFrom().GetEmailAddress().GetAddress())))
}
@ -41,7 +46,7 @@ func ToEml(ctx context.Context, data models.Messageable) (string, error) {
for _, recipient := range data.GetToRecipients() {
email.AddTo(
fmt.Sprintf(
fromFormat,
addressFormat,
ptr.Val(recipient.GetEmailAddress().GetName()),
ptr.Val(recipient.GetEmailAddress().GetAddress())))
}
@ -51,7 +56,7 @@ func ToEml(ctx context.Context, data models.Messageable) (string, error) {
for _, recipient := range data.GetCcRecipients() {
email.AddCc(
fmt.Sprintf(
fromFormat,
addressFormat,
ptr.Val(recipient.GetEmailAddress().GetName()),
ptr.Val(recipient.GetEmailAddress().GetAddress())))
}
@ -61,7 +66,7 @@ func ToEml(ctx context.Context, data models.Messageable) (string, error) {
for _, recipient := range data.GetBccRecipients() {
email.AddBcc(
fmt.Sprintf(
fromFormat,
addressFormat,
ptr.Val(recipient.GetEmailAddress().GetName()),
ptr.Val(recipient.GetEmailAddress().GetAddress())))
}
@ -77,7 +82,7 @@ func ToEml(ctx context.Context, data models.Messageable) (string, error) {
} else if len(rts) != 0 {
email.SetReplyTo(
fmt.Sprintf(
fromFormat,
addressFormat,
ptr.Val(rts[0].GetEmailAddress().GetName()),
ptr.Val(rts[0].GetEmailAddress().GetAddress())))
}

View File

@ -1,12 +1,15 @@
package eml
import (
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/converters/eml/testdata"
"github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/services/m365/api"
@ -26,11 +29,57 @@ func (suite *EMLUnitSuite) TestConvert_messageble_to_eml() {
ctx, flush := tester.NewContext(t)
defer flush()
msg, err := api.BytesToMessageable([]byte(testdata.EmailWithAttachments))
body := []byte(testdata.EmailWithAttachments)
out, err := FromJSON(ctx, body)
assert.NoError(t, err, "converting to eml")
msg, err := api.BytesToMessageable(body)
require.NoError(t, err, "creating message")
_, err = ToEml(ctx, msg)
// TODO(meain): add more tests on the generated content
// Cannot test output directly as it contains a random boundary
assert.NoError(t, err, "converting to eml")
assert.Contains(t, out, fmt.Sprintf("Subject: %s", ptr.Val(msg.GetSubject())))
assert.Contains(t, out, fmt.Sprintf("Date: %s", msg.GetSentDateTime().Format(time.RFC1123Z)))
assert.Contains(
t,
out,
fmt.Sprintf(
`From: "%s" <%s>`,
ptr.Val(msg.GetFrom().GetEmailAddress().GetName()),
ptr.Val(msg.GetFrom().GetEmailAddress().GetAddress())))
for _, addr := range msg.GetToRecipients() {
assert.Contains(
t,
out,
fmt.Sprintf(
`To: "%s" <%s>`,
ptr.Val(addr.GetEmailAddress().GetName()),
ptr.Val(addr.GetEmailAddress().GetAddress())))
}
for _, addr := range msg.GetCcRecipients() {
assert.Contains(
t,
out,
fmt.Sprintf(
`Cc: "%s" <%s>`,
ptr.Val(addr.GetEmailAddress().GetName()),
ptr.Val(addr.GetEmailAddress().GetAddress())))
}
for _, addr := range msg.GetBccRecipients() {
assert.Contains(
t,
out,
fmt.Sprintf(
`Bcc: "%s" <%s>`,
ptr.Val(addr.GetEmailAddress().GetName()),
ptr.Val(addr.GetEmailAddress().GetAddress())))
}
// Only fist 30 chars as the .eml generator can introduce a
// newline in between the text to limit the column width of the
// output. It does not affect the data, but can break our tests and
// so using 30 as a safe limit to test.
assert.Contains(t, out, ptr.Val(msg.GetBody().GetContent())[:30], "body")
}

View File

@ -13,7 +13,6 @@ import (
"github.com/alcionai/corso/src/pkg/export"
"github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/path"
"github.com/alcionai/corso/src/pkg/services/m365/api"
)
func NewExportCollection(
@ -65,17 +64,7 @@ func streamItems(
continue
}
msg, err := api.BytesToMessageable(content)
if err != nil {
ch <- export.Item{
ID: id,
Error: clues.Wrap(err, "parsing email"),
}
continue
}
email, err := eml.ToEml(ctx, msg)
email, err := eml.FromJSON(ctx, content)
if err != nil {
ch <- export.Item{
ID: id,