Tests now pass

This commit is contained in:
Julian K. Arni 2017-09-12 19:49:55 -04:00
parent 5bd9d253ce
commit ffbfa42a14
3 changed files with 47 additions and 21 deletions

View file

@ -225,11 +225,10 @@ instance OVERLAPPABLE_
{ requestAccept = fromList $ toList accept { requestAccept = fromList $ toList accept
, requestMethod = method , requestMethod = method
} }
case mimeUnrender (Proxy :: Proxy ct) $ responseBody response of response `decodedAs` (Proxy :: Proxy ct)
Left err -> throwError $ DecodeFailure (pack err) response where
Right val -> return val
where method = reflectMethod (Proxy :: Proxy method)
accept = contentTypes (Proxy :: Proxy ct) accept = contentTypes (Proxy :: Proxy ct)
method = reflectMethod (Proxy :: Proxy method)
instance OVERLAPPING_ instance OVERLAPPING_
( RunClient m, ReflectMethod method ( RunClient m, ReflectMethod method

View file

@ -1,11 +1,44 @@
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE OverloadedStrings #-}
{-| Types for possible backends to run client-side `Request` queries -} {-| Types for possible backends to run client-side `Request` queries -}
module Servant.Client.Core.Internal.Class where module Servant.Client.Core.Internal.Class where
import Control.Monad.Error.Class (MonadError) import Control.Monad (unless)
import Servant.Client.Core.Internal.Request (Request, Response, import Control.Monad.Error.Class (MonadError, throwError)
ServantError) import Data.Proxy (Proxy)
import qualified Data.Text as T
import Network.HTTP.Media (MediaType, matches,
parseAccept, (//))
import Servant.API (MimeUnrender,
contentTypes,
mimeUnrender)
import Servant.Client.Core.Internal.Request (Request, Response (..),
ServantError (..))
import Data.Foldable (toList)
class (MonadError ServantError m) => RunClient m where class (MonadError ServantError m) => RunClient m where
-- | How to make a request.
runRequest :: Request -> m Response runRequest :: Request -> m Response
checkContentTypeHeader :: RunClient m => Response -> m MediaType
checkContentTypeHeader response =
case lookup "Content-Type" $ toList $ responseHeaders response of
Nothing -> pure $ "application"//"octet-stream"
Just t -> case parseAccept t of
Nothing -> throwError $ InvalidContentTypeHeader response
Just t' -> pure t'
decodedAs :: forall ct a m. (MimeUnrender ct a, RunClient m)
=> Response -> Proxy ct -> m a
decodedAs response contentType = do
responseContentType <- checkContentTypeHeader response
unless (any (matches responseContentType) accept) $
throwError $ UnsupportedContentType responseContentType response
case mimeUnrender contentType $ responseBody response of
Left err -> throwError $ DecodeFailure (T.pack err) response
Right val -> return val
where
accept = toList $ contentTypes contentType

View file

@ -24,7 +24,7 @@
{-# OPTIONS_GHC -fno-warn-name-shadowing #-} {-# OPTIONS_GHC -fno-warn-name-shadowing #-}
#include "overlapping-compat.h" #include "overlapping-compat.h"
module Servant.ClientSpec where module Servant.ClientSpec (spec) where
import Prelude () import Prelude ()
import Prelude.Compat import Prelude.Compat
@ -35,18 +35,13 @@ import Control.Concurrent (ThreadId, forkIO,
import Control.Exception (bracket) import Control.Exception (bracket)
import Control.Monad.Error.Class (throwError) import Control.Monad.Error.Class (throwError)
import Data.Aeson import Data.Aeson
import qualified Data.ByteString.Lazy as BS
import Data.Char (chr, isPrint) import Data.Char (chr, isPrint)
import Data.Foldable (toList)
import Data.Foldable (forM_) import Data.Foldable (forM_)
import Data.Maybe (isJust)
import Data.Monoid hiding (getLast) import Data.Monoid hiding (getLast)
import Data.Proxy import Data.Proxy
import Data.Sequence (findIndexL)
import qualified Generics.SOP as SOP import qualified Generics.SOP as SOP
import GHC.Generics (Generic) import GHC.Generics (Generic)
import qualified Network.HTTP.Client as C import qualified Network.HTTP.Client as C
import Network.HTTP.Media
import qualified Network.HTTP.Types as HTTP import qualified Network.HTTP.Types as HTTP
import Network.Socket import Network.Socket
import qualified Network.Wai as Wai import qualified Network.Wai as Wai
@ -96,8 +91,8 @@ spec = describe "Servant.Client" $ do
-- * test data types -- * test data types
data Person = Person data Person = Person
{ name :: String { _name :: String
, age :: Integer , _age :: Integer
} deriving (Eq, Show, Generic) } deriving (Eq, Show, Generic)
instance ToJSON Person instance ToJSON Person
@ -233,14 +228,14 @@ genAuthAPI = Proxy
type instance AuthServerData (AuthProtect "auth-tag") = () type instance AuthServerData (AuthProtect "auth-tag") = ()
type instance AuthClientData (AuthProtect "auth-tag") = () type instance AuthClientData (AuthProtect "auth-tag") = ()
genAuthHandler :: AuthHandler Request () genAuthHandler :: AuthHandler Wai.Request ()
genAuthHandler = genAuthHandler =
let handler req = case lookup "AuthHeader" (toList $ requestHeaders req) of let handler req = case lookup "AuthHeader" (Wai.requestHeaders req) of
Nothing -> throwError (err401 { errBody = "Missing auth header" }) Nothing -> throwError (err401 { errBody = "Missing auth header" })
Just _ -> return () Just _ -> return ()
in mkAuthHandler handler in mkAuthHandler handler
genAuthServerContext :: Context '[ AuthHandler Request () ] genAuthServerContext :: Context '[ AuthHandler Wai.Request () ]
genAuthServerContext = genAuthHandler :. EmptyContext genAuthServerContext = genAuthHandler :. EmptyContext
genAuthServer :: Application genAuthServer :: Application
@ -297,6 +292,7 @@ genericClientServer = serve (Proxy :: Proxy GenericClientAPI) (
manager' :: C.Manager manager' :: C.Manager
manager' = unsafePerformIO $ C.newManager C.defaultManagerSettings manager' = unsafePerformIO $ C.newManager C.defaultManagerSettings
runClient :: ClientM a -> BaseUrl -> IO (Either ServantError a)
runClient x baseUrl' = runClientM x (ClientEnv manager' baseUrl') runClient x baseUrl' = runClientM x (ClientEnv manager' baseUrl')
sucessSpec :: Spec sucessSpec :: Spec
@ -344,8 +340,6 @@ sucessSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do
Right r -> do Right r -> do
responseStatusCode r `shouldBe` HTTP.status200 responseStatusCode r `shouldBe` HTTP.status200
responseBody r `shouldBe` "rawSuccess" responseBody r `shouldBe` "rawSuccess"
findIndexL (\x -> fst x == HTTP.hContentType) (responseHeaders r)
`shouldSatisfy` isJust
it "Servant.API.Raw should return a Left in case of failure" $ \(_, baseUrl) -> do it "Servant.API.Raw should return a Left in case of failure" $ \(_, baseUrl) -> do
res <- runClient (getRawFailure HTTP.methodGet) baseUrl res <- runClient (getRawFailure HTTP.methodGet) baseUrl