module APIBuilder.API (
API
, APIT
, runAPI
, runRoute
, routeResponse
, routeRequest
, liftEither
, liftBuilder
, liftState
, name
, baseURL
, customizeRoute
, customizeRequest ) where
import APIBuilder.Builder
import APIBuilder.Decoding
import APIBuilder.Error
import APIBuilder.Routes
import Control.Exception
import Control.Monad.Trans.Either
import Control.Monad.Trans.State
import Control.Monad.Trans.Class (lift)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Data.Aeson (FromJSON)
import Data.Text (Text)
import qualified Data.Text as T
import Data.ByteString.Lazy (ByteString)
import Network.HTTP.Conduit
type API s e a = APIT s e IO a
type APIT s e m a = EitherT (APIError e) (StateT Builder (StateT s m)) a
liftEither :: Monad m => EitherT (APIError e) (StateT Builder (StateT s m)) a -> APIT s e m a
liftEither = id
liftBuilder :: Monad m => StateT Builder (StateT s m) a -> APIT s e m a
liftBuilder = lift
liftState :: Monad m => StateT s m a -> APIT s e m a
liftState = lift . lift
runAPI :: MonadIO m
=> Builder
-> s
-> APIT s e m a
-> m (Either (APIError e) a)
runAPI b s api = evalStateT (evalStateT (runEitherT api) 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
req <- hoistEither $ routeRequest b route `eitherOr` InvalidURLError
resp <- do
r <- liftIO $ try $ withManager (httpLbs req)
hoistEither $ either (Left . HTTPError) Right r
return resp
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 })