module Network.API.Builder.API (
API
, APIT
, execAPI
, runAPI
, runRoute
, routeResponse
, routeRequest
, liftEither
, liftManager
, liftBuilder
, liftState
, name
, baseURL
, customizeRoute
, customizeRequest ) where
import Network.API.Builder.Builder
import Network.API.Builder.Decoding
import Network.API.Builder.Error
import Network.API.Builder.Routes
import Control.Exception
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Either
import Control.Monad.Trans.Reader
import Control.Monad.Trans.State
import Data.Aeson (FromJSON)
import Data.ByteString.Lazy (ByteString)
import Data.Text (Text)
import Network.HTTP.Conduit
import qualified Data.Text as T
type API s e a = APIT s e IO a
type APIT s e m a = EitherT (APIError e) (ReaderT Manager (StateT Builder (StateT s m))) a
liftEither :: Monad m => EitherT (APIError e) (ReaderT Manager (StateT Builder (StateT s m))) a -> APIT s e m a
liftEither = id
liftManager :: Monad m => ReaderT Manager (StateT Builder (StateT s m)) a -> APIT s e m a
liftManager = lift
liftBuilder :: Monad m => StateT Builder (StateT s m) a -> APIT s e m a
liftBuilder = lift . lift
liftState :: Monad m => StateT s m a -> APIT s e m a
liftState = lift . lift . lift
execAPI :: MonadIO m
=> Builder
-> s
-> APIT s e m a
-> m (Either (APIError e) a)
execAPI b s api = do
m <- liftIO $ newManager conduitManagerSettings
(res, _, _) <- runAPI b m s api
liftIO $ closeManager m
return res
runAPI :: MonadIO m
=> Builder
-> Manager
-> s
-> APIT s e m a
-> m (Either (APIError e) a, Builder, s)
runAPI b m s api = do
((res, b'), s') <- runStateT (runStateT (runReaderT (runEitherT api) m) b) s
return (res, b', s')
runRoute :: (FromJSON a, FromJSON e, MonadIO m) => Route -> APIT s e m a
runRoute route = routeResponse route >>= hoistEither . decode . responseBody
routeResponse :: (MonadIO m) => Route -> APIT s e m (Response ByteString)
routeResponse route = do
b <- liftBuilder get
m <- liftManager ask
req <- hoistEither $ routeRequest b route `eitherOr` InvalidURLError
do
r <- liftIO $ try $ httpLbs req m
hoistEither $ either (Left . HTTPError) Right r
eitherOr :: Maybe a -> b -> Either b a
a `eitherOr` b =
case a of
Just x -> Right x
Nothing -> Left b
routeRequest :: Builder -> Route -> Maybe Request
routeRequest b route =
let initialURL = parseUrl (T.unpack $ routeURL (_baseURL b) (_customizeRoute b route)) in
fmap (\url -> _customizeRequest b $ url { method = httpMethod route }) initialURL
name :: Monad m => Text -> APIT s e m ()
name t = liftBuilder $ modify (\b -> b { _name = t })
baseURL :: Monad m => Text -> APIT s e m ()
baseURL t = liftBuilder $ modify (\b -> b { _baseURL = t })
customizeRoute :: Monad m => (Route -> Route) -> APIT s e m ()
customizeRoute f = liftBuilder $ modify (\b -> b { _customizeRoute = f })
customizeRequest :: Monad m => (Request -> Request) -> APIT s e m ()
customizeRequest f = liftBuilder $ modify (\b -> b { _customizeRequest = f })