{-# LANGUAGE CPP #-}
module Mail.Hailgun.Communication
( getRequest
, postRequest
, toQueryParams
, parseResponse
) where
import Control.Arrow (second)
import Control.Monad.Catch (MonadThrow (..))
import Control.Monad.IO.Class (MonadIO (..))
import Data.Aeson
import qualified Data.ByteString.Char8 as BC
import qualified Data.ByteString.Lazy.Char8 as BLC
import Data.Foldable (foldMap)
import Data.Monoid
import Mail.Hailgun.Errors
import Mail.Hailgun.Internal.Data
import qualified Network.HTTP.Client as NC
import Network.HTTP.Client.Internal (addProxy)
import Network.HTTP.Client.MultipartFormData (Part (..), formDataBody)
import qualified Network.HTTP.Types.Method as NM
import qualified Network.HTTP.Types.Status as NT
toQueryParams :: [(BC.ByteString, BC.ByteString)] -> [(BC.ByteString, Maybe BC.ByteString)]
toQueryParams = fmap (second Just)
getRequest :: (MonadThrow m) => String -> HailgunContext -> [(BC.ByteString, Maybe BC.ByteString)] -> m NC.Request
getRequest url context queryParams = do
initRequest <- NC.parseUrl url
let request = appEndo (applyHailgunAuth context) $
initRequest
{ NC.method = NM.methodGet
#if MIN_VERSION_http_client(0,5,0)
#else
, NC.checkStatus = ignoreStatus
#endif
}
return $ NC.setQueryString queryParams request
postRequest :: (MonadThrow m, MonadIO m) => String -> HailgunContext -> [Part] -> m NC.Request
postRequest url context parts = do
initRequest <- NC.parseUrl url
let request = initRequest
{ NC.method = NM.methodPost
#if MIN_VERSION_http_client(0,5,0)
#else
, NC.checkStatus = ignoreStatus
#endif
}
requestWithBody <- formDataBody parts request
return $ appEndo (applyHailgunAuth context) requestWithBody
applyHailgunAuth :: HailgunContext -> Endo NC.Request
applyHailgunAuth context = addRequestProxy (hailgunProxy context) <> authRequest context
addRequestProxy :: Maybe NC.Proxy -> Endo NC.Request
addRequestProxy = foldMap addProxy'
authRequest :: HailgunContext -> Endo NC.Request
authRequest context = Endo $ NC.applyBasicAuth (BC.pack "api") (BC.pack . hailgunApiKey $ context)
addProxy' :: NC.Proxy -> Endo NC.Request
addProxy' proxy = Endo $ addProxy (NC.proxyHost proxy) (NC.proxyPort proxy)
parseResponse :: (FromJSON a) => NC.Response BLC.ByteString -> Either HailgunErrorResponse a
parseResponse response = statusToResponse . NT.statusCode . NC.responseStatus $ response
where
statusToResponse s
| s == 200 = responseDecode response
| s `elem` [400, 401, 402, 404] = gatherErrors . responseDecode $ response
| s `elem` [500, 502, 503, 504] = serverError
| otherwise = unexpectedError s
responseDecode :: (FromJSON a) => NC.Response BLC.ByteString -> Either HailgunErrorResponse a
responseDecode = mapError . eitherDecode . NC.responseBody
ignoreStatus :: a -> b -> c -> Maybe d
ignoreStatus _ _ _ = Nothing