{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TupleSections #-}
module Api (
Api,
Connection,
MonadApi(..),
call,
getJ,
postJ,
putJ,
req,
reqJ,
reqS,
withApi,
withApiConfig,
withApiHttp,
withApiHttps,
withEndpoint,
withOption,
withPath,
withPaths
)
where
import Control.Applicative
import Control.Monad.Catch
import Control.Monad.IO.Class
import Control.Monad.Reader
import Data.Aeson
import Data.ByteString (ByteString)
import Data.Proxy (Proxy)
import Data.Semigroup hiding (Option)
import Data.Text
import Data.Typeable
import GHC.Generics
import Network.HTTP.Client.MultipartFormData (Part)
import Network.HTTP.Req hiding (req)
import qualified Network.HTTP.Req as Req
type Connection scheme = (Url scheme, Option scheme)
data InvalidUrlException = InvalidUrlException ByteString deriving (Show, Typeable)
instance Exception InvalidUrlException
newtype Api scheme a = Api (ReaderT (Connection scheme) (ReaderT HttpConfig IO) a) deriving (Functor, Applicative, Monad, MonadIO, MonadReader (Connection scheme), MonadCatch, MonadThrow, MonadPlus, Alternative)
instance MonadHttp (Api scheme) where
handleHttpException = throwM
getHttpConfig = Api $ lift ask
class (MonadReader (Connection scheme) m, MonadHttp m) => MonadApi scheme m
instance MonadApi scheme (Api scheme)
withApiConfig :: MonadIO m => HttpConfig -> Connection scheme -> Api scheme a -> m a
withApiConfig config connection (Api call) = liftIO $ runReaderT (runReaderT call connection) config
withApi :: MonadIO m => Connection scheme -> Api scheme a -> m a
withApi = withApiConfig defaultHttpConfig
withApiHttp :: (MonadIO m, MonadThrow m) => ByteString -> Api Http a -> m a
withApiHttp url call = case parseUrlHttp url of
Nothing -> throwM $ InvalidUrlException url
Just connection -> withApi connection call
withApiHttps :: (MonadIO m, MonadThrow m) => ByteString -> Api Https a -> m a
withApiHttps url call = case parseUrlHttps url of
Nothing -> throwM $ InvalidUrlException url
Just connection -> withApi connection call
withOption :: MonadReader (Connection scheme) m => Option scheme -> m a -> m a
withOption option call = local modify call
where
modify (url, options) = (url, options <> option)
withPath :: MonadReader (Connection scheme) m => Text -> m a -> m a
withPath path call = local modify call
where
modify (url, options) = (url /: path, options)
withPaths :: MonadReader (Connection scheme) m => [Text] -> m a -> m a
withPaths [] call = call
withPaths (p:ps) call = withPath p $ withPaths ps call
withEndpoint :: MonadReader (Connection scheme) m => Text -> m a -> m a
withEndpoint endpoint = withPaths $ splitOn "/" endpoint
call :: (MonadApi scheme m, HttpBodyAllowed (AllowsBody method) (ProvidesBody body), HttpMethod method, HttpBody body, HttpResponse response) => method -> body -> Proxy response -> m response
call method body response = do
(url, options) <- ask
Req.req method url body response options
req :: (MonadApi scheme m, HttpBodyAllowed (AllowsBody method) (ProvidesBody body), HttpMethod method, HttpBody body, HttpResponse response) => method -> body -> Proxy response -> m (HttpResponseBody response)
req method body response = responseBody <$> call method body response
reqJ :: (MonadApi scheme m, HttpBodyAllowed (AllowsBody method) (ProvidesBody body), HttpMethod method, HttpBody body, FromJSON a) => method -> body -> m a
reqJ method body = req method body jsonResponse
reqS :: (MonadApi scheme m, HttpBodyAllowed (AllowsBody method) (ProvidesBody body), HttpMethod method, HttpBody body) => method -> body -> m ByteString
reqS method body = req method body bsResponse
req_ :: (MonadApi scheme m, HttpBodyAllowed (AllowsBody method) (ProvidesBody body), HttpMethod method, HttpBody body) => method -> body -> m ()
req_ method body = req method body ignoreResponse
getJ :: (MonadApi scheme m, FromJSON a) => m a
getJ = reqJ GET NoReqBody
getS :: MonadApi scheme m => m ByteString
getS = reqS GET NoReqBody
postJ :: (MonadApi scheme m, ToJSON up, FromJSON down) => up -> m down
postJ = reqJ POST . ReqBodyJson
postJ_ :: (MonadApi scheme m, ToJSON up) => up -> m ()
postJ_ = req_ POST . ReqBodyJson
putJ :: (MonadApi scheme m, ToJSON up, FromJSON down) => up -> m down
putJ = reqJ PUT . ReqBodyJson
delete_ :: MonadApi scheme m => m ()
delete_ = req_ DELETE NoReqBody
deleteJ :: (MonadApi scheme m, FromJSON a) => m a
deleteJ = req DELETE NoReqBody jsonResponse
Comments
0 B
|👍
/👎
0 B
|👍
/👎
0 B
|👍
/👎
0 B
|👍
/👎