Creating Mails
The Mail
class in emil-common
is used to represent an
e-mail. Using the MailBuilder
makes creating values more
convenient. The MailBuilder
works by collecting a list of
transformations to an initial Mail
object and applying them when
invoking the build
method. There exists a set of predefined
transformations to cover the most common things. But you can create
your own easily, too.
Simple Mails
Creating mails without attachments:
import cats.effect._, emil._, emil.builder._
val mail: Mail[IO] = MailBuilder.build(
From("me@test.com"),
To("test@test.com"),
Subject("Hello!"),
CustomHeader(Header("User-Agent", "my-email-client")),
TextBody("Hello!\n\nThis is a mail."),
HtmlBody("<h1>Hello!</h1>\n<p>This <b>is</b> a mail.</p>")
)
// mail: Mail[IO] = Mail(
// header = MailHeader(
// id = "",
// messageId = None,
// folder = None,
// recipients = Recipients(
// to = List(MailAddress(name = None, address = "test@test.com")),
// cc = List(),
// bcc = List()
// ),
// sender = None,
// from = Some(value = MailAddress(name = None, address = "me@test.com")),
// replyTo = None,
// originationDate = None,
// subject = "Hello!",
// received = List(),
// flags = Set()
// ),
// additionalHeaders = Headers(
// all = List(
// Header(
// name = "User-Agent",
// value = NonEmptyList(head = "my-email-client", tail = List())
// )
// )
// ),
// body = HtmlAndText(
// text = Pure(
// value = StringContent(
// asString = """Hello!
//
// This is a mail."""
// )
// ),
// html = Pure(
// value = StringContent(
// asString = """<h1>Hello!</h1>
// <p>This <b>is</b> a mail.</p>"""
// )
// )
// ),
// attachments = Attachments(all = Vector())
// )
The Mail
defines an effect type, because the attachments and the
mail body does. Using MailBuilder.build
already applies all
transformations and yields the final Mail
instance. Using the
MailBuilder.apply
instead, would return the MailBuilder
instance
which can be further modified. It is also possible to go from an
existing Mail
to a MailBuilder
to change certain parts:
val builder = mail.asBuilder
// builder: MailBuilder[IO] = emil.builder.MailBuilder@1791e231
val mail2 = builder.
clearRecipients.
add(To("me2@test.com")).
set(Subject("Hello 2")).
build
// mail2: Mail[IO] = Mail(
// header = MailHeader(
// id = "",
// messageId = None,
// folder = None,
// recipients = Recipients(
// to = List(MailAddress(name = None, address = "me2@test.com")),
// cc = List(),
// bcc = List()
// ),
// sender = None,
// from = Some(value = MailAddress(name = None, address = "me@test.com")),
// replyTo = None,
// originationDate = None,
// subject = "Hello 2",
// received = List(),
// flags = Set()
// ),
// additionalHeaders = Headers(
// all = List(
// Header(
// name = "User-Agent",
// value = NonEmptyList(head = "my-email-client", tail = List())
// )
// )
// ),
// body = HtmlAndText(
// text = Pure(
// value = StringContent(
// asString = """Hello!
//
// This is a mail."""
// )
// ),
// html = Pure(
// value = StringContent(
// asString = """<h1>Hello!</h1>
// <p>This <b>is</b> a mail.</p>"""
// )
// )
// ),
// attachments = Attachments(all = Vector())
// )
The add
and set
methods are both doing the same thing: appending
transformations. Both names exists for better reading; i.e. a recipient
is by default appended, but the subject is not. The methods accept a
list of transformations, too.
val mail3 = mail2.asBuilder.
clearRecipients.
add(
To("me3@test.com"),
Cc("other@test.com")
).
build
// mail3: Mail[IO] = Mail(
// header = MailHeader(
// id = "",
// messageId = None,
// folder = None,
// recipients = Recipients(
// to = List(MailAddress(name = None, address = "me3@test.com")),
// cc = List(MailAddress(name = None, address = "other@test.com")),
// bcc = List()
// ),
// sender = None,
// from = Some(value = MailAddress(name = None, address = "me@test.com")),
// replyTo = None,
// originationDate = None,
// subject = "Hello 2",
// received = List(),
// flags = Set()
// ),
// additionalHeaders = Headers(
// all = List(
// Header(
// name = "User-Agent",
// value = NonEmptyList(head = "my-email-client", tail = List())
// )
// )
// ),
// body = HtmlAndText(
// text = Pure(
// value = StringContent(
// asString = """Hello!
//
// This is a mail."""
// )
// ),
// html = Pure(
// value = StringContent(
// asString = """<h1>Hello!</h1>
// <p>This <b>is</b> a mail.</p>"""
// )
// )
// ),
// attachments = Attachments(all = Vector())
// )
Mails with Attachments
Adding attachments is the same as with other data. Creating
attachments might be more involved, depending on where the data is
coming from. Emil defines transformations to add attachments from
files, urls and java’s InputStream
easily. Otherwise you need to get
a Stream[F, Byte]
from somewhere else.
import scala.concurrent.ExecutionContext
val mail4 = mail3.asBuilder.add(
AttachUrl[IO](getClass.getResource("/files/Test.pdf")).
withFilename("test.pdf").
withMimeType(MimeType.pdf)).
build
// mail4: Mail[IO] = Mail(
// header = MailHeader(
// id = "",
// messageId = None,
// folder = None,
// recipients = Recipients(
// to = List(MailAddress(name = None, address = "me3@test.com")),
// cc = List(MailAddress(name = None, address = "other@test.com")),
// bcc = List()
// ),
// sender = None,
// from = Some(value = MailAddress(name = None, address = "me@test.com")),
// replyTo = None,
// originationDate = None,
// subject = "Hello 2",
// received = List(),
// flags = Set()
// ),
// additionalHeaders = Headers(
// all = List(
// Header(
// name = "User-Agent",
// value = NonEmptyList(head = "my-email-client", tail = List())
// )
// )
// ),
// body = HtmlAndText(
// text = Pure(
// value = StringContent(
// asString = """Hello!
//
// This is a mail."""
// )
// ),
// html = Pure(
// value = StringContent(
// asString = """<h1>Hello!</h1>
// <p>This <b>is</b> a mail.</p>"""
// )
// )
// ),
// attachments = Attachments(
// all = Vector(
// Attachment(
// filename = Some(value = "test.pdf"),
// mimeType = MimeType(
// primary = "application",
// sub = "pdf",
// params = Map()
// ...
Emil creates a Stream[F, Byte]
from the java.net.URL
using the
fs2-io
api. The same is available when attaching files and
java.io.InputStream
s. Any given Stream[F, Byte]
can be attached as
well:
import fs2.Stream
val mydata: Stream[IO, Byte] = Stream.empty.covary[IO]
// mydata: Stream[IO, Byte] = Stream(..)
val mail5 = mail4.asBuilder.
clearAttachments.
add(
Attach(mydata).
withFilename("empty.txt")
).
build
// mail5: Mail[IO] = Mail(
// header = MailHeader(
// id = "",
// messageId = None,
// folder = None,
// recipients = Recipients(
// to = List(MailAddress(name = None, address = "me3@test.com")),
// cc = List(MailAddress(name = None, address = "other@test.com")),
// bcc = List()
// ),
// sender = None,
// from = Some(value = MailAddress(name = None, address = "me@test.com")),
// replyTo = None,
// originationDate = None,
// subject = "Hello 2",
// received = List(),
// flags = Set()
// ),
// additionalHeaders = Headers(
// all = List(
// Header(
// name = "User-Agent",
// value = NonEmptyList(head = "my-email-client", tail = List())
// )
// )
// ),
// body = HtmlAndText(
// text = Pure(
// value = StringContent(
// asString = """Hello!
//
// This is a mail."""
// )
// ),
// html = Pure(
// value = StringContent(
// asString = """<h1>Hello!</h1>
// <p>This <b>is</b> a mail.</p>"""
// )
// )
// ),
// attachments = Attachments(
// all = Vector(
// Attachment(
// filename = Some(value = "empty.txt"),
// mimeType = MimeType(
// primary = "application",
// sub = "octet-stream",
// params = Map()
// ...
Custom Transformations
Customs transformations can be easily created. It’s only a function
Mail[F] => Mail[F]
. This can be handy, if there is a better way to
retrieve attachments, or just to create common headers.
object MyHeader {
def apply[F[_]](value: String): Trans[F] =
CustomHeader("X-My-Header", value)
}
val mymail = MailBuilder.build[IO](
To("me@test.com"),
MyHeader("blablabla"),
TextBody("This is a text")
)
// mymail: Mail[IO] = Mail(
// header = MailHeader(
// id = "",
// messageId = None,
// folder = None,
// recipients = Recipients(
// to = List(MailAddress(name = None, address = "me@test.com")),
// cc = List(),
// bcc = List()
// ),
// sender = None,
// from = None,
// replyTo = None,
// originationDate = None,
// subject = "",
// received = List(),
// flags = Set()
// ),
// additionalHeaders = Headers(
// all = List(
// Header(
// name = "X-My-Header",
// value = NonEmptyList(head = "blablabla", tail = List())
// )
// )
// ),
// body = Text(text = Pure(value = StringContent(asString = "This is a text"))),
// attachments = Attachments(all = Vector())
// )
The above example simply delegates to an existing Trans
constructor.