get rid of a lot of useless proxies and implement POST requests support on server and client side

This commit is contained in:
Alp Mestanogullari 2014-10-24 13:36:19 +02:00
parent 8c260f5127
commit 8c8bc15ea9
2 changed files with 49 additions and 7 deletions

View file

@ -20,6 +20,7 @@ library
base >=4 && <5 base >=4 && <5
, either , either
, aeson , aeson
, bytestring
, string-conversions , string-conversions
, http-client , http-client
, http-types , http-types

View file

@ -7,6 +7,7 @@ import Control.Monad
import Control.Monad.IO.Class import Control.Monad.IO.Class
import Control.Monad.Trans.Either import Control.Monad.Trans.Either
import Data.Aeson import Data.Aeson
import Data.ByteString.Lazy (ByteString)
import Data.Proxy import Data.Proxy
import Data.String.Conversions import Data.String.Conversions
import GHC.TypeLits import GHC.TypeLits
@ -32,6 +33,9 @@ import System.IO.Unsafe (unsafePerformIO)
-- and serves the contained type as JSON. -- and serves the contained type as JSON.
data Get a data Get a
-- | Endpoint for POST requests.
data Post a
-- | The contained API (second argument) can be found under @("/" ++ path)@ -- | The contained API (second argument) can be found under @("/" ++ path)@
-- (path being the first argument). -- (path being the first argument).
data (path :: k) :> a = Proxy path :> a data (path :: k) :> a = Proxy path :> a
@ -74,18 +78,33 @@ instance ToJSON result => HasServer (Get result) where
responseLBS (mkStatus status (cs message)) [] (cs message) responseLBS (mkStatus status (cs message)) [] (cs message)
| otherwise = return Nothing | otherwise = return Nothing
instance ToJSON a => HasServer (Post a) where
type Server (Post a) = EitherT (Int, String) IO a
route Proxy action request
| null (pathInfo request) && requestMethod request == methodPost = do
e <- runEitherT action
return $ Just $ case e of
Right out ->
responseLBS status201 [("Content-Type", "application/json")] (encode out)
Left (status, message) ->
responseLBS (mkStatus status (cs message)) [] (cs message)
| otherwise = return Nothing
instance (KnownSymbol path, HasServer sublayout) => HasServer (path :> sublayout) where instance (KnownSymbol path, HasServer sublayout) => HasServer (path :> sublayout) where
type Server (path :> sublayout) = path :> (Server sublayout) type Server (path :> sublayout) = Server sublayout
route Proxy (path :> subserver) request = case pathInfo request of route Proxy subserver request = case pathInfo request of
(first : rest) (first : rest)
| first == cs (symbolVal path) | first == cs (symbolVal proxyPath)
-> route (Proxy :: Proxy sublayout) subserver request{ -> route (Proxy :: Proxy sublayout) subserver request{
pathInfo = rest pathInfo = rest
} }
_ -> return Nothing _ -> return Nothing
where proxyPath = Proxy :: Proxy path
instance (HasServer a, HasServer b) => HasServer (a :<|> b) where instance (HasServer a, HasServer b) => HasServer (a :<|> b) where
type Server (a :<|> b) = (Server a :<|> Server b) type Server (a :<|> b) = Server a :<|> Server b
route Proxy (a :<|> b) request = do route Proxy (a :<|> b) request = do
m <- route (Proxy :: Proxy a) a request m <- route (Proxy :: Proxy a) a request
case m of case m of
@ -124,15 +143,37 @@ instance FromJSON result => HasClient (Get result) where
maybe (left "HTTP GET request returned invalid json") return $ maybe (left "HTTP GET request returned invalid json") return $
decode' (Http.Client.responseBody innerResponse) decode' (Http.Client.responseBody innerResponse)
instance FromJSON a => HasClient (Post a) where
type Client (Post a) = ByteString -> URI -> EitherT String IO a
clientWithRoute Proxy path rqbody uri = do
partialRequest <- liftIO . Http.Client.parseUrl $
show ( nullURI { uriPath = path }
`relativeTo` uri
)
let request = partialRequest
{ Http.Client.method = methodPost
, Http.Client.requestBody = Http.Client.RequestBodyLBS rqbody
}
innerResponse <- liftIO . __withGlobalManager $ \ manager ->
Http.Client.httpLbs request manager
when (Http.Client.responseStatus innerResponse /= status201) $
left ("HTTP POST request failed with status: " ++ show (Http.Client.responseStatus innerResponse))
maybe (left "HTTP POST request returned invalid json") return $
decode' (Http.Client.responseBody innerResponse)
instance (KnownSymbol path, HasClient sublayout) => HasClient (path :> sublayout) where instance (KnownSymbol path, HasClient sublayout) => HasClient (path :> sublayout) where
type Client (path :> sublayout) = path :> (Client sublayout) type Client (path :> sublayout) = Client sublayout
clientWithRoute Proxy path = clientWithRoute Proxy path =
(Proxy :: Proxy path) :>
clientWithRoute (Proxy :: Proxy sublayout) clientWithRoute (Proxy :: Proxy sublayout)
(path ++ "/" ++ (symbolVal (Proxy :: Proxy path))) (path ++ "/" ++ (symbolVal (Proxy :: Proxy path)))
instance (HasClient a, HasClient b) => HasClient (a :<|> b) where instance (HasClient a, HasClient b) => HasClient (a :<|> b) where
type Client (a :<|> b) = (Client a :<|> Client b) type Client (a :<|> b) = Client a :<|> Client b
clientWithRoute Proxy path = clientWithRoute Proxy path =
clientWithRoute (Proxy :: Proxy a) path :<|> clientWithRoute (Proxy :: Proxy a) path :<|>
clientWithRoute (Proxy :: Proxy b) path clientWithRoute (Proxy :: Proxy b) path