servant 0.8 → 0.8.1
raw patch · 7 files changed
+49/−10 lines, 7 filesdep ~aeson
Dependency ranges changed: aeson
Files
- CHANGELOG.md +5/−0
- servant.cabal +2/−2
- src/Servant/API.hs +2/−2
- src/Servant/API/Capture.hs +14/−1
- src/Servant/API/Internal/Test/ComprehensiveAPI.hs +11/−4
- src/Servant/Utils/Links.hs +10/−1
- test/Servant/Utils/LinksSpec.hs +5/−0
CHANGELOG.md view
@@ -1,3 +1,8 @@+0.8.1+----++* Add `CaptureAll` combinator. Captures all of the remaining segments in a URL.+ 0.8 ---
servant.cabal view
@@ -1,5 +1,5 @@ name: servant-version: 0.8+version: 0.8.1 synopsis: A family of combinators for defining webservices APIs description: A family of combinators for defining webservices APIs and serving them@@ -51,7 +51,7 @@ build-depends: base >= 4.7 && < 4.10 , base-compat >= 0.9 && < 0.10- , aeson >= 0.7 && < 0.12+ , aeson >= 0.7 && < 1.1 , attoparsec >= 0.12 && < 0.14 , bytestring >= 0.10 && < 0.11 , bytestring-conversion >= 0.3 && < 0.4
src/Servant/API.hs view
@@ -8,7 +8,7 @@ -- * Accessing information from the request module Servant.API.Capture,- -- | Capturing parts of the url path as parsed values: @'Capture'@+ -- | Capturing parts of the url path as parsed values: @'Capture'@ and @'CaptureAll'@ module Servant.API.Header, -- | Retrieving specific headers from the request module Servant.API.HttpVersion,@@ -60,7 +60,7 @@ import Servant.API.Alternative ((:<|>) (..)) import Servant.API.BasicAuth (BasicAuth,BasicAuthData(..))-import Servant.API.Capture (Capture)+import Servant.API.Capture (Capture, CaptureAll) import Servant.API.ContentTypes (Accept (..), FormUrlEncoded, FromFormUrlEncoded (..), JSON, MimeRender (..), NoContent (NoContent),
src/Servant/API/Capture.hs view
@@ -2,7 +2,7 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE PolyKinds #-} {-# OPTIONS_HADDOCK not-home #-}-module Servant.API.Capture (Capture) where+module Servant.API.Capture (Capture, CaptureAll) where import Data.Typeable (Typeable) import GHC.TypeLits (Symbol)@@ -15,9 +15,22 @@ data Capture (sym :: Symbol) a deriving (Typeable) ++-- | Capture all remaining values from the request path under a certain type+-- @a@.+--+-- Example:+--+-- >>> -- GET /src/*+-- >>> type MyAPI = "src" :> CaptureAll "segments" Text :> Get '[JSON] SourceFile+data CaptureAll (sym :: Symbol) a+ deriving (Typeable)+ -- $setup -- >>> import Servant.API -- >>> import Data.Aeson -- >>> import Data.Text -- >>> data Book -- >>> instance ToJSON Book where { toJSON = undefined }+-- >>> data SourceFile+-- >>> instance ToJSON SourceFile where { toJSON = undefined }
src/Servant/API/Internal/Test/ComprehensiveAPI.hs view
@@ -13,6 +13,13 @@ type GET = Get '[JSON] NoContent type ComprehensiveAPI =+ ComprehensiveAPIWithoutRaw :<|>+ Raw++comprehensiveAPI :: Proxy ComprehensiveAPI+comprehensiveAPI = Proxy++type ComprehensiveAPIWithoutRaw = GET :<|> Get '[JSON] Int :<|> Capture "foo" Int :> GET :<|>@@ -22,7 +29,6 @@ QueryParam "foo" Int :> GET :<|> QueryParams "foo" Int :> GET :<|> QueryFlag "foo" :> GET :<|>--- Raw :<|> RemoteHost :> GET :<|> ReqBody '[JSON] Int :> GET :<|> Get '[JSON] (Headers '[Header "foo" Int] NoContent) :<|>@@ -30,7 +36,8 @@ Vault :> GET :<|> Verb 'POST 204 '[JSON] NoContent :<|> Verb 'POST 204 '[JSON] Int :<|>- WithNamedContext "foo" '[] GET+ WithNamedContext "foo" '[] GET :<|>+ CaptureAll "foo" Int :> GET -comprehensiveAPI :: Proxy ComprehensiveAPI-comprehensiveAPI = Proxy+comprehensiveAPIWithoutRaw :: Proxy ComprehensiveAPIWithoutRaw+comprehensiveAPIWithoutRaw = Proxy
src/Servant/Utils/Links.hs view
@@ -107,7 +107,7 @@ import Web.HttpApiData import Servant.API.BasicAuth ( BasicAuth )-import Servant.API.Capture ( Capture )+import Servant.API.Capture ( Capture, CaptureAll ) import Servant.API.ReqBody ( ReqBody ) import Servant.API.QueryParam ( QueryParam, QueryParams, QueryFlag ) import Servant.API.Header ( Header )@@ -163,6 +163,8 @@ IsElem sa (ReqBody y x :> sb) = IsElem sa sb IsElem (Capture z y :> sa) (Capture x y :> sb) = IsElem sa sb+ IsElem (CaptureAll z y :> sa) (CaptureAll x y :> sb)+ = IsElem sa sb IsElem sa (QueryParam x y :> sb) = IsElem sa sb IsElem sa (QueryParams x y :> sb) = IsElem sa sb IsElem sa (QueryFlag x :> sb) = IsElem sa sb@@ -283,6 +285,13 @@ toLink _ l v = toLink (Proxy :: Proxy sub) $ addSegment (escape . Text.unpack $ toUrlPiece v) l++instance (ToHttpApiData v, HasLink sub)+ => HasLink (CaptureAll sym v :> sub) where+ type MkLink (CaptureAll sym v :> sub) = [v] -> MkLink sub+ toLink _ l vs =+ toLink (Proxy :: Proxy sub) $+ foldl' (flip $ addSegment . escape . Text.unpack . toUrlPiece) l vs instance HasLink sub => HasLink (Header sym a :> sub) where type MkLink (Header sym a :> sub) = MkLink sub
test/Servant/Utils/LinksSpec.hs view
@@ -13,6 +13,7 @@ type TestApi = -- Capture and query params "hello" :> Capture "name" String :> QueryParam "capital" Bool :> Delete '[JSON] NoContent+ :<|> "all" :> CaptureAll "names" String :> Get '[JSON] NoContent -- Flags :<|> "balls" :> QueryFlag "bouncy" :> QueryFlag "fast" :> Delete '[JSON] NoContent@@ -46,6 +47,10 @@ :> Delete '[JSON] NoContent) apiLink l2 "bye" (Just True) `shouldBeURI` "hello/bye?capital=true" + it "generates correct links for CaptureAll" $ do+ apiLink (Proxy :: Proxy ("all" :> CaptureAll "names" String :> Get '[JSON] NoContent))+ ["roads", "lead", "to", "rome"]+ `shouldBeURI` "all/roads/lead/to/rome" it "generates correct links for query flags" $ do let l1 = Proxy :: Proxy ("balls" :> QueryFlag "bouncy"