packages feed

hreq-core 0.1.0.0 → 0.1.1.0

raw patch · 7 files changed

+63/−4 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Hreq.Core.API.Response: ResStatus :: a -> Nat -> ResContent a
+ Hreq.Core.API.Response: [SResStatus] :: forall (a :: Type) (n :: Nat). Sing a -> Sing n -> SResContent ( 'ResStatus a n)
+ Hreq.Core.API.Response: instance (Data.Singletons.SingI a, Data.Singletons.SingI n) => Data.Singletons.SingI ('Hreq.Core.API.Response.ResStatus a n)
+ Hreq.Core.API.Response: type ResStatus = 'ResStatus ()
+ Hreq.Core.Client.ClientError: InvalidStatusCode :: Response -> ClientError
+ Hreq.Core.Client.HasResponse: instance (Control.Monad.Error.Class.MonadError Hreq.Core.Client.ClientError.ClientError m, GHC.TypeNats.KnownNat n) => Hreq.Core.Client.HasResponse.HasResponse '[Hreq.Core.API.Response.ResStatus n] m
+ Hreq.Core.Client.HasResponse: instance (Control.Monad.Error.Class.MonadError Hreq.Core.Client.ClientError.ClientError m, GHC.TypeNats.KnownNat n, Data.Singletons.SingI ('Hreq.Core.API.Internal.Res (r : rs)), Hreq.Core.API.TypeLevel.HttpResConstraints (r : rs)) => Hreq.Core.Client.HasResponse.HasResponse (Hreq.Core.API.Response.ResStatus n : r : rs) m

Files

CHANGELOG.md view
@@ -1,3 +1,10 @@+## Hreq-Core 0.1.1.0++* Matching status codes with in response. ++One can specify that a certain status code is expected in the http response +and when its not received we throw an error.+ ## Hreq-Core 0.1.0.0  * Initial public release
hreq-core.cabal view
@@ -1,6 +1,6 @@ cabal-version: >= 2.0 name:           hreq-core-version:        0.1.0.0+version:        0.1.1.0 synopsis:       Core functionality for Hreq Http client library description:    Core functionality for Hreq. A type dependent highlevel HTTP client library inspired by servant-client. category:       Network, Web
src/Hreq/Core/API/Response.hs view
@@ -5,13 +5,14 @@  import Data.Kind (Type) import Data.Singletons (Sing, SingI (..))-import GHC.TypeLits (Symbol)+import GHC.TypeLits (Nat, Symbol)  -- * Response type data ResContent a =     ResBody a a   | ResHeaders [(Symbol, a)]   | ResStream a a+  | ResStatus a Nat   | Raw a  -- * Response type synonyms@@ -19,12 +20,14 @@ type ResHeaders = 'ResHeaders type ResStream = 'ResStream type Raw = 'Raw ()+type ResStatus = 'ResStatus ()  -- * Response as a Singleton GADT data SResContent (a :: ResContent Type) where   SResBody :: forall ctyp a. Sing ctyp -> Sing a -> SResContent ('ResBody ctyp a)   SResStream :: forall ctyp a. Sing ctyp -> Sing a -> SResContent ('ResStream ctyp a)   SResHeaders :: forall (ts :: [(Symbol, Type)]). Sing ts -> SResContent ('ResHeaders ts)+  SResStatus :: forall (a :: Type) (n :: Nat) . Sing a -> Sing n ->  SResContent ('ResStatus a n)   SRaw :: forall (a :: Type) . Sing a -> SResContent ('Raw a) type instance Sing = SResContent @@ -36,6 +39,9 @@  instance SingI ts => SingI ('ResHeaders ts :: ResContent Type) where   sing = SResHeaders sing++instance (SingI a, SingI n) => SingI ('ResStatus a n :: ResContent Type) where+  sing = SResStatus sing sing  instance SingI a => SingI ('Raw a :: ResContent Type) where   sing = SRaw sing
src/Hreq/Core/API/TypeLevel.hs view
@@ -3,7 +3,7 @@ module Hreq.Core.API.TypeLevel where  import Data.Kind (Type, Constraint)-import GHC.TypeLits (Symbol, TypeError, ErrorMessage(..), KnownSymbol)+import GHC.TypeLits (Symbol, TypeError, ErrorMessage(..), KnownSymbol, KnownNat) import Network.HTTP.Types (Header) import Web.HttpApiData (ToHttpApiData) @@ -61,6 +61,7 @@ type family HttpRes (res :: [ ResContent Type ]) :: [ Type ] where   HttpRes '[] = '[]   HttpRes ('ResBody ctyp a ': ts) = a ': HttpRes ts+  HttpRes ('ResStatus _a code ': ts) = HttpRes ts   HttpRes ('ResHeaders (s ': hs) ': ts) = [Header] ': HttpRes ts   HttpRes ('ResHeaders '[]  ': ts) = HttpRes ts   HttpRes ('Raw a ': ts) = HttpRes ts@@ -68,6 +69,7 @@ -- | Response content types Constraints. type family HttpResConstraints (res :: [ResContent Type]) :: Constraint where   HttpResConstraints '[] = ()+  HttpResConstraints  ('ResStatus _a code ': ts) = (KnownNat code, HttpResConstraints ts)   HttpResConstraints  ('ResBody ctyp a ': ts) =      (HasMediaType ctyp, MediaDecode ctyp a, HttpResConstraints ts)   HttpResConstraints  ('ResStream ctyp a ': ts) = (HasMediaType ctyp, HttpResConstraints ts)
src/Hreq/Core/Client/ClientError.hs view
@@ -26,6 +26,8 @@   | UnsupportedContentType MediaType Response   -- | The content-type header is invalid   | InvalidContentTypeHeader Response+  -- | The received status code didn't much the expected.+  | InvalidStatusCode Response   -- | There was a connection error, and no response was received   | ConnectionError SomeException   deriving (Show, Generic, Typeable)
src/Hreq/Core/Client/HasRequest.hs view
@@ -69,6 +69,7 @@   SCons (SResBody sctyp _a) _rs -> Just $ mediaType sctyp   SCons (SRaw _) rs -> getAcceptHeader rs   SCons (SResHeaders _) rs -> getAcceptHeader rs+  SCons (SResStatus _ _) rs -> getAcceptHeader rs   SCons (SResStream sctyp _) _rs -> Just $ mediaType sctyp  -- | Transform a 'Hlist' of inputs into a 'Request'
src/Hreq/Core/Client/HasResponse.hs view
@@ -60,11 +60,44 @@    httpRes _ _ =  error "GHC Error" +-- | Expected status code much match received code instance {-# OVERLAPPING #-}+  ( MonadError ClientError m+  , KnownNat n+  )+  => HasResponse '[ ResStatus n ] m where+  type HttpOutput '[ ResStatus n ] = Response++  httpRes _ res = do+    let expectedCode = fromIntegral @Integer @Int $ natVal (Proxy @n)+        rcode = resStatusCode res+    when (expectedCode /= rcode) $ throwError (InvalidStatusCode res)++    return res++-- | Expected status code much match received code in a response code list+instance {-# OVERLAPPING #-}+  ( MonadError ClientError m+  , KnownNat n+  , SingI ('Res (r ': rs))+  , HttpResConstraints (r ': rs)+  )+  => HasResponse (ResStatus n : r : rs) m where+  type HttpOutput (ResStatus n : r : rs) =  Hlist (HttpRes (r ': rs))++  httpRes _ response = do+    let expectedCode = fromIntegral @Integer @Int $ natVal (Proxy @n)+        rcode = resStatusCode response+    when (expectedCode /= rcode) $ throwError (InvalidStatusCode response)++    case sing @('Res (r ': rs)) of+      SRes xs -> decodeAsHlist xs response++instance {-# OVERLAPPING #-}   ( MediaDecode ctyp a   , MonadError ClientError m   )-  => HasResponse '[ 'ResBody  ctyp a ] m where+  => HasResponse '[ 'ResBody ctyp a ] m where   type HttpOutput '[ 'ResBody ctyp a ] = a    httpRes _ = decodeAsBody (Proxy @ctyp)@@ -145,6 +178,14 @@   SCons (SResHeaders SNil) xs ->     decodeAsHlist xs response +  SCons (SResStatus _ snat) xs -> do+    let rcode = resStatusCode response+        expectedCode = withKnownNat snat (fromIntegral @Integer @Int $ natVal snat)++    when (rcode /= expectedCode) $ throwError (InvalidStatusCode response)++    decodeAsHlist xs response+    -- Should never match because we have a class instance   -- that triggers a type error when 'Raw' is in a non-singleton   -- type level list