module Network.Sendgrid.Api
( Authentication(..)
, EmailMessage(..)
, makeRequest
, getRequest
, postRequest
, sendEmail
) where
import Control.Applicative ((<$>), (<*>))
import Control.Monad (mzero)
import Control.Monad.Catch
import Control.Monad.IO.Class
import Control.Monad.Trans.Control
import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy as L
import Data.List (partition)
import Data.Monoid ((<>))
import qualified Data.Text as T
import Network.HTTP.Conduit
import Network.Sendgrid.Utils (urlEncode)
baseUrl :: String
baseUrl = "https://api.sendgrid.com/api/"
class Tupled a where asTuple :: a -> [(String, String)]
data Authentication = Authentication
{ user :: String
, key :: String
} deriving ( Show )
instance Tupled Authentication where
asTuple a =
[ ("api_user", u)
, ("api_key", k) ]
where u = user a
k = key a
data EmailMessage = EmailMessage {
to :: String
, from :: String
, subject :: String
, text :: String
} deriving ( Eq, Show )
instance Tupled EmailMessage where
asTuple a =
let t = (to a)
f = (from a)
s = (subject a)
x = (text a) in
[ ("to", t)
, ("from", f)
, ("subject", s)
, ("text", x) ]
urlEncodeVars :: [(String,String)] -> String
urlEncodeVars [] = []
urlEncodeVars ((n,v):t) =
let (same,diff) = partition ((==n) . fst) t
in urlEncode n ++ '=' : foldl (\x y -> x ++ ',' : urlEncode y) (urlEncode $ v) (map snd same)
++ urlEncodeRest diff
where urlEncodeRest [] = []
urlEncodeRest diff = '&' : urlEncodeVars diff
data Method = GET | POST
class AsByteString a where
asByteString :: a -> BS.ByteString
showBS :: Show a => a -> BS.ByteString
showBS = BS.pack . show
instance AsByteString Method where
asByteString = showBS
instance Show Method where
show GET = "GET"
show POST = "POST"
makeRequest method url body =
let rBody = BS.pack . urlEncodeVars $ body in
do
initReq <- parseUrl url
let req = initReq
{ method = showBS method
, requestHeaders = [ ("content-type", "application/x-www-form-urlencoded") ]
, requestBody = RequestBodyBS $ rBody
}
response <- withManager $ httpLbs req
return $ responseBody response
postRequest :: (MonadThrow m, MonadIO m, MonadBaseControl IO m) =>
String ->
[ (String, String) ] ->
m L.ByteString
postRequest = makeRequest POST
getRequest :: (MonadThrow m, MonadIO m, MonadBaseControl IO m) =>
String ->
[ (String, String) ] ->
m L.ByteString
getRequest = makeRequest GET
data Profile = Profile {
profileUsername :: String
, profileEmail :: String
, profileActive :: String
, profileFirstName :: String
, profileLastName :: String
, profileAddress :: String
, profileCity :: String
, profileState :: String
, profileZip :: String
, profileCountry :: String
, profilePhone :: String
, profileWebsite :: String
} deriving ( Show )
instance Aeson.FromJSON Profile where
parseJSON (Aeson.Object o) =
Profile <$>
o Aeson..: "username" <*>
o Aeson..: "email" <*>
o Aeson..: "active" <*>
o Aeson..: "first_name" <*>
o Aeson..: "last_name" <*>
o Aeson..: "address" <*>
o Aeson..: "city" <*>
o Aeson..: "state" <*>
o Aeson..: "zip" <*>
o Aeson..: "country" <*>
o Aeson..: "phone" <*>
o Aeson..: "website"
parseJSON _ = mzero
getProfile :: (MonadThrow m, MonadIO m, MonadBaseControl IO m, Tupled a) =>
a ->
m (L.ByteString)
getProfile auth = let fullUrl = baseUrl <> "profile.get.json" in
makeRequest POST fullUrl (asTuple auth)
data MailSuccess = MailSuccess {
message :: String
} deriving ( Show )
instance Aeson.FromJSON MailSuccess where
parseJSON (Aeson.Object o) = MailSuccess <$> o Aeson..: "message"
parseJSON _ = mzero
sendEmail :: (Tupled a1, Tupled a) =>
a ->
a1 ->
IO (Maybe MailSuccess)
sendEmail auth message =
let fullUrl = baseUrl <> "mail.send.json"
response = makeRequest POST fullUrl (asTuple auth <> asTuple message) in
Aeson.decode <$> response