Amazonka Email service, an example

Amazonka is a huge intimidating package full of amazing goodness.

Here is the full overview

What makes amazonka scary is what makes it great...

  • The packages are split up along service lines
  • The same auth routines control everything!

I think illustrating the power of amazonka can be done with one quick email example.

{-# LANGUAGE OverloadedStrings #-}
module SESDemo where


import           Network.AWS
import           Network.AWS.SES
import           SESDemo.Internal


import           Control.Lens
import           System.IO


testCreateSendMail = sendEmail "<source-email>" dest msg
 where
  dest = destination & dToAddresses .~ ["<target-email>"]
  msg = message content' body'
  content' = content "" & cData .~ "Email as a service"
  body' = body & bHTML .~ (Just (content "<h1>People love their cars </h1>"))

testSendEmail :: IO ()
testSendEmail = do
    env <- newEnv Oregon Discover
    l <- newLogger Debug stdout
    _ <- runResourceT . runAWS (env & envLogger .~ l) $ (send testCreateSendMail)
    return ()

testSendEmail calls our brand new S3 service!

So there are lots of lenses and things going on in Amazonka that are scary on first sight.

I am going to go through how I pieced it together and hopefully that will be helpful.

First the function with the name I wanted!

sendEmail
    :: Text -- ^ 'seSource'
    -> Destination -- ^ 'seDestination'
    -> Message -- ^ 'seMessage'
    -> SendEmail
sendEmail pSource_ pDestination_ pMessage_ =
    SendEmail'
    { _seReturnPath = Nothing
    , _seSourceARN = Nothing
    , _seReturnPathARN = Nothing
    , _seReplyToAddresses = Nothing
    , _seSource = pSource_
    , _seDestination = pDestination_
    , _seMessage = pMessage_
    }

What is a SendEmail?

From the haskell docs...


Represents a request instructing the service to send a single email message.

This datatype can be used in application code to compose a message consisting of source, destination, message, reply-to, and return-path parts. This object can then be sent using the SendEmail action.

See: sendEmail smart constructor.


So the sendEmail function is a smart constructor for SendEmail.

There is another for Message, Content and Body.

There are also lenses to change what I want the default fields (the ones not created by the smart constructor) to be.

from above:

body' = body & bHTML .~ (Just (content "<h1>People love their cars </h1>"))

So I selected Html instead of plain text, to allow us to send an email as Html.

What next?

So we have a SendEmail but what do we do?

Doing a little exploring in the amazonka package I found what to do.


import Control.Lens
import Network.AWS
import Network.AWS.S3
import System.IO

example :: IO PutObjectResponse
example = do
    e <- newEnv Frankfurt Discover :: IO Env
    l <- newLogger Debug stdout :: IO Logger
    b <- sourceFileIO "local/path/to/object-payload"
    runResourceT . runAWS (e & envLogger .~ l) $
    send (putObject "bucket-name" "object-key" b)
    

The function doing the heavy lifting is send.

-- | Send a request, returning the associated response if successful.
send :: (MonadAWS m, AWSRequest a) => a -> m (Rs a)
send = liftAWS . AWST.send

Looking back at SendEmail, it is an instance of AWSRequest!

Looks like we have a way to send things now... The last detail is the authorization.

Authorization is handled in the newEnv part of that example above.

Follow instructions in amazon's docs to set it up.

...

That is about it, hope this is helpful!