commit
3ce7c9758a
20 changed files with 78 additions and 299 deletions
|
@ -5,6 +5,7 @@ HEAD
|
||||||
* Support for the `HttpVersion`, `IsSecure`, `RemoteHost` and `Vault` combinators
|
* Support for the `HttpVersion`, `IsSecure`, `RemoteHost` and `Vault` combinators
|
||||||
* Added support for `path` on `BaseUrl`.
|
* Added support for `path` on `BaseUrl`.
|
||||||
* `client` now takes an explicit `Manager` argument.
|
* `client` now takes an explicit `Manager` argument.
|
||||||
|
* Use `http-api-data` instead of `Servant.Common.Text`
|
||||||
|
|
||||||
0.4.1
|
0.4.1
|
||||||
-----
|
-----
|
||||||
|
|
|
@ -34,6 +34,7 @@ library
|
||||||
, attoparsec
|
, attoparsec
|
||||||
, bytestring
|
, bytestring
|
||||||
, exceptions
|
, exceptions
|
||||||
|
, http-api-data >= 0.1 && < 0.2
|
||||||
, http-client
|
, http-client
|
||||||
, http-client-tls
|
, http-client-tls
|
||||||
, http-media
|
, http-media
|
||||||
|
|
|
@ -92,7 +92,7 @@ instance (HasClient a, HasClient b) => HasClient (a :<|> b) where
|
||||||
-- of this value at the right place in the request path.
|
-- of this value at the right place in the request path.
|
||||||
--
|
--
|
||||||
-- You can control how values for this type are turned into
|
-- You can control how values for this type are turned into
|
||||||
-- text by specifying a 'ToText' instance for your type.
|
-- text by specifying a 'ToHttpApiData' instance for your type.
|
||||||
--
|
--
|
||||||
-- Example:
|
-- Example:
|
||||||
--
|
--
|
||||||
|
@ -105,7 +105,7 @@ instance (HasClient a, HasClient b) => HasClient (a :<|> b) where
|
||||||
-- > getBook = client myApi host manager
|
-- > getBook = client myApi host manager
|
||||||
-- > where host = BaseUrl Http "localhost" 8080
|
-- > where host = BaseUrl Http "localhost" 8080
|
||||||
-- > -- then you can just use "getBook" to query that endpoint
|
-- > -- then you can just use "getBook" to query that endpoint
|
||||||
instance (KnownSymbol capture, ToText a, HasClient sublayout)
|
instance (KnownSymbol capture, ToHttpApiData a, HasClient sublayout)
|
||||||
=> HasClient (Capture capture a :> sublayout) where
|
=> HasClient (Capture capture a :> sublayout) where
|
||||||
|
|
||||||
type Client (Capture capture a :> sublayout) =
|
type Client (Capture capture a :> sublayout) =
|
||||||
|
@ -117,7 +117,7 @@ instance (KnownSymbol capture, ToText a, HasClient sublayout)
|
||||||
baseurl
|
baseurl
|
||||||
manager
|
manager
|
||||||
|
|
||||||
where p = unpack (toText val)
|
where p = unpack (toUrlPiece val)
|
||||||
|
|
||||||
-- | If you have a 'Delete' endpoint in your API, the client
|
-- | If you have a 'Delete' endpoint in your API, the client
|
||||||
-- side querying function that is created when calling 'client'
|
-- side querying function that is created when calling 'client'
|
||||||
|
@ -205,12 +205,12 @@ instance
|
||||||
-- That function will take care of encoding this argument as Text
|
-- That function will take care of encoding this argument as Text
|
||||||
-- in the request headers.
|
-- in the request headers.
|
||||||
--
|
--
|
||||||
-- All you need is for your type to have a 'ToText' instance.
|
-- All you need is for your type to have a 'ToHttpApiData' instance.
|
||||||
--
|
--
|
||||||
-- Example:
|
-- Example:
|
||||||
--
|
--
|
||||||
-- > newtype Referer = Referer { referrer :: Text }
|
-- > newtype Referer = Referer { referrer :: Text }
|
||||||
-- > deriving (Eq, Show, Generic, FromText, ToText)
|
-- > deriving (Eq, Show, Generic, FromText, ToHttpApiData)
|
||||||
-- >
|
-- >
|
||||||
-- > -- GET /view-my-referer
|
-- > -- GET /view-my-referer
|
||||||
-- > type MyApi = "view-my-referer" :> Header "Referer" Referer :> Get '[JSON] Referer
|
-- > type MyApi = "view-my-referer" :> Header "Referer" Referer :> Get '[JSON] Referer
|
||||||
|
@ -223,7 +223,7 @@ instance
|
||||||
-- > where host = BaseUrl Http "localhost" 8080
|
-- > where host = BaseUrl Http "localhost" 8080
|
||||||
-- > -- then you can just use "viewRefer" to query that endpoint
|
-- > -- then you can just use "viewRefer" to query that endpoint
|
||||||
-- > -- specifying Nothing or e.g Just "http://haskell.org/" as arguments
|
-- > -- specifying Nothing or e.g Just "http://haskell.org/" as arguments
|
||||||
instance (KnownSymbol sym, ToText a, HasClient sublayout)
|
instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout)
|
||||||
=> HasClient (Header sym a :> sublayout) where
|
=> HasClient (Header sym a :> sublayout) where
|
||||||
|
|
||||||
type Client (Header sym a :> sublayout) =
|
type Client (Header sym a :> sublayout) =
|
||||||
|
@ -368,7 +368,7 @@ instance
|
||||||
-- of inserting a textual representation of this value in the query string.
|
-- of inserting a textual representation of this value in the query string.
|
||||||
--
|
--
|
||||||
-- You can control how values for your type are turned into
|
-- You can control how values for your type are turned into
|
||||||
-- text by specifying a 'ToText' instance for your type.
|
-- text by specifying a 'ToHttpApiData' instance for your type.
|
||||||
--
|
--
|
||||||
-- Example:
|
-- Example:
|
||||||
--
|
--
|
||||||
|
@ -383,7 +383,7 @@ instance
|
||||||
-- > -- then you can just use "getBooksBy" to query that endpoint.
|
-- > -- then you can just use "getBooksBy" to query that endpoint.
|
||||||
-- > -- 'getBooksBy Nothing' for all books
|
-- > -- 'getBooksBy Nothing' for all books
|
||||||
-- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov
|
-- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov
|
||||||
instance (KnownSymbol sym, ToText a, HasClient sublayout)
|
instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout)
|
||||||
=> HasClient (QueryParam sym a :> sublayout) where
|
=> HasClient (QueryParam sym a :> sublayout) where
|
||||||
|
|
||||||
type Client (QueryParam sym a :> sublayout) =
|
type Client (QueryParam sym a :> sublayout) =
|
||||||
|
@ -401,7 +401,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
|
||||||
|
|
||||||
where pname = cs pname'
|
where pname = cs pname'
|
||||||
pname' = symbolVal (Proxy :: Proxy sym)
|
pname' = symbolVal (Proxy :: Proxy sym)
|
||||||
mparamText = fmap toText mparam
|
mparamText = fmap toQueryParam mparam
|
||||||
|
|
||||||
-- | If you use a 'QueryParams' in one of your endpoints in your API,
|
-- | If you use a 'QueryParams' in one of your endpoints in your API,
|
||||||
-- the corresponding querying function will automatically take
|
-- the corresponding querying function will automatically take
|
||||||
|
@ -415,7 +415,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
|
||||||
-- under the same query string parameter name.
|
-- under the same query string parameter name.
|
||||||
--
|
--
|
||||||
-- You can control how values for your type are turned into
|
-- You can control how values for your type are turned into
|
||||||
-- text by specifying a 'ToText' instance for your type.
|
-- text by specifying a 'ToHttpApiData' instance for your type.
|
||||||
--
|
--
|
||||||
-- Example:
|
-- Example:
|
||||||
--
|
--
|
||||||
|
@ -431,7 +431,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
|
||||||
-- > -- 'getBooksBy []' for all books
|
-- > -- 'getBooksBy []' for all books
|
||||||
-- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
|
-- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
|
||||||
-- > -- to get all books by Asimov and Heinlein
|
-- > -- to get all books by Asimov and Heinlein
|
||||||
instance (KnownSymbol sym, ToText a, HasClient sublayout)
|
instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout)
|
||||||
=> HasClient (QueryParams sym a :> sublayout) where
|
=> HasClient (QueryParams sym a :> sublayout) where
|
||||||
|
|
||||||
type Client (QueryParams sym a :> sublayout) =
|
type Client (QueryParams sym a :> sublayout) =
|
||||||
|
@ -447,7 +447,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
|
||||||
|
|
||||||
where pname = cs pname'
|
where pname = cs pname'
|
||||||
pname' = symbolVal (Proxy :: Proxy sym)
|
pname' = symbolVal (Proxy :: Proxy sym)
|
||||||
paramlist' = map (Just . toText) paramlist
|
paramlist' = map (Just . toQueryParam) paramlist
|
||||||
|
|
||||||
-- | If you use a 'QueryFlag' in one of your endpoints in your API,
|
-- | If you use a 'QueryFlag' in one of your endpoints in your API,
|
||||||
-- the corresponding querying function will automatically take
|
-- the corresponding querying function will automatically take
|
||||||
|
@ -498,7 +498,7 @@ instance (KnownSymbol sym, HasClient sublayout)
|
||||||
-- of inserting a textual representation of this value in the query string.
|
-- of inserting a textual representation of this value in the query string.
|
||||||
--
|
--
|
||||||
-- You can control how values for your type are turned into
|
-- You can control how values for your type are turned into
|
||||||
-- text by specifying a 'ToText' instance for your type.
|
-- text by specifying a 'ToHttpApiData' instance for your type.
|
||||||
--
|
--
|
||||||
-- Example:
|
-- Example:
|
||||||
--
|
--
|
||||||
|
@ -513,7 +513,7 @@ instance (KnownSymbol sym, HasClient sublayout)
|
||||||
-- > -- then you can just use "getBooksBy" to query that endpoint.
|
-- > -- then you can just use "getBooksBy" to query that endpoint.
|
||||||
-- > -- 'getBooksBy Nothing' for all books
|
-- > -- 'getBooksBy Nothing' for all books
|
||||||
-- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov
|
-- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov
|
||||||
instance (KnownSymbol sym, ToText a, HasClient sublayout)
|
instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout)
|
||||||
=> HasClient (MatrixParam sym a :> sublayout) where
|
=> HasClient (MatrixParam sym a :> sublayout) where
|
||||||
|
|
||||||
type Client (MatrixParam sym a :> sublayout) =
|
type Client (MatrixParam sym a :> sublayout) =
|
||||||
|
@ -529,7 +529,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
|
||||||
baseurl manager
|
baseurl manager
|
||||||
|
|
||||||
where pname = symbolVal (Proxy :: Proxy sym)
|
where pname = symbolVal (Proxy :: Proxy sym)
|
||||||
mparamText = fmap (cs . toText) mparam
|
mparamText = fmap (cs . toQueryParam) mparam
|
||||||
|
|
||||||
-- | If you use a 'MatrixParams' in one of your endpoints in your API,
|
-- | If you use a 'MatrixParams' in one of your endpoints in your API,
|
||||||
-- the corresponding querying function will automatically take an
|
-- the corresponding querying function will automatically take an
|
||||||
|
@ -543,7 +543,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
|
||||||
-- same matrix string parameter name.
|
-- same matrix string parameter name.
|
||||||
--
|
--
|
||||||
-- You can control how values for your type are turned into text by
|
-- You can control how values for your type are turned into text by
|
||||||
-- specifying a 'ToText' instance for your type.
|
-- specifying a 'ToHttpApiData' instance for your type.
|
||||||
--
|
--
|
||||||
-- Example:
|
-- Example:
|
||||||
--
|
--
|
||||||
|
@ -559,7 +559,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
|
||||||
-- > -- 'getBooksBy []' for all books
|
-- > -- 'getBooksBy []' for all books
|
||||||
-- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
|
-- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
|
||||||
-- > -- to get all books by Asimov and Heinlein
|
-- > -- to get all books by Asimov and Heinlein
|
||||||
instance (KnownSymbol sym, ToText a, HasClient sublayout)
|
instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout)
|
||||||
=> HasClient (MatrixParams sym a :> sublayout) where
|
=> HasClient (MatrixParams sym a :> sublayout) where
|
||||||
|
|
||||||
type Client (MatrixParams sym a :> sublayout) =
|
type Client (MatrixParams sym a :> sublayout) =
|
||||||
|
@ -575,7 +575,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
|
||||||
|
|
||||||
where pname = cs pname'
|
where pname = cs pname'
|
||||||
pname' = symbolVal (Proxy :: Proxy sym)
|
pname' = symbolVal (Proxy :: Proxy sym)
|
||||||
paramlist' = map (Just . toText) paramlist
|
paramlist' = map (Just . toQueryParam) paramlist
|
||||||
|
|
||||||
-- | If you use a 'MatrixFlag' in one of your endpoints in your API,
|
-- | If you use a 'MatrixFlag' in one of your endpoints in your API,
|
||||||
-- the corresponding querying function will automatically take an
|
-- the corresponding querying function will automatically take an
|
||||||
|
|
|
@ -26,10 +26,11 @@ import qualified Network.HTTP.Types.Header as HTTP
|
||||||
import Network.URI hiding (path)
|
import Network.URI hiding (path)
|
||||||
import Servant.API.ContentTypes
|
import Servant.API.ContentTypes
|
||||||
import Servant.Common.BaseUrl
|
import Servant.Common.BaseUrl
|
||||||
import Servant.Common.Text
|
|
||||||
|
|
||||||
import qualified Network.HTTP.Client as Client
|
import qualified Network.HTTP.Client as Client
|
||||||
|
|
||||||
|
import Web.HttpApiData
|
||||||
|
|
||||||
data ServantError
|
data ServantError
|
||||||
= FailureResponse
|
= FailureResponse
|
||||||
{ responseStatus :: Status
|
{ responseStatus :: Status
|
||||||
|
@ -86,9 +87,9 @@ appendToQueryString pname pvalue req =
|
||||||
req { qs = qs req ++ [(pname, pvalue)]
|
req { qs = qs req ++ [(pname, pvalue)]
|
||||||
}
|
}
|
||||||
|
|
||||||
addHeader :: ToText a => String -> a -> Req -> Req
|
addHeader :: ToHttpApiData a => String -> a -> Req -> Req
|
||||||
addHeader name val req = req { headers = headers req
|
addHeader name val req = req { headers = headers req
|
||||||
++ [(name, toText val)]
|
++ [(name, decodeUtf8 (toHeader val))]
|
||||||
}
|
}
|
||||||
|
|
||||||
setRQBody :: ByteString -> MediaType -> Req -> Req
|
setRQBody :: ByteString -> MediaType -> Req -> Req
|
||||||
|
|
|
@ -107,7 +107,7 @@ instance (HasMock a, HasMock b) => HasMock (a :<|> b) where
|
||||||
instance (KnownSymbol path, HasMock rest) => HasMock (path :> rest) where
|
instance (KnownSymbol path, HasMock rest) => HasMock (path :> rest) where
|
||||||
mock _ = mock (Proxy :: Proxy rest)
|
mock _ = mock (Proxy :: Proxy rest)
|
||||||
|
|
||||||
instance (KnownSymbol s, FromText a, HasMock rest) => HasMock (Capture s a :> rest) where
|
instance (KnownSymbol s, FromHttpApiData a, HasMock rest) => HasMock (Capture s a :> rest) where
|
||||||
mock _ = \_ -> mock (Proxy :: Proxy rest)
|
mock _ = \_ -> mock (Proxy :: Proxy rest)
|
||||||
|
|
||||||
instance (AllCTUnrender ctypes a, HasMock rest) => HasMock (ReqBody ctypes a :> rest) where
|
instance (AllCTUnrender ctypes a, HasMock rest) => HasMock (ReqBody ctypes a :> rest) where
|
||||||
|
@ -125,29 +125,29 @@ instance HasMock rest => HasMock (Vault :> rest) where
|
||||||
instance HasMock rest => HasMock (HttpVersion :> rest) where
|
instance HasMock rest => HasMock (HttpVersion :> rest) where
|
||||||
mock _ = \_ -> mock (Proxy :: Proxy rest)
|
mock _ = \_ -> mock (Proxy :: Proxy rest)
|
||||||
|
|
||||||
instance (KnownSymbol s, FromText a, HasMock rest)
|
instance (KnownSymbol s, FromHttpApiData a, HasMock rest)
|
||||||
=> HasMock (QueryParam s a :> rest) where
|
=> HasMock (QueryParam s a :> rest) where
|
||||||
mock _ = \_ -> mock (Proxy :: Proxy rest)
|
mock _ = \_ -> mock (Proxy :: Proxy rest)
|
||||||
|
|
||||||
instance (KnownSymbol s, FromText a, HasMock rest)
|
instance (KnownSymbol s, FromHttpApiData a, HasMock rest)
|
||||||
=> HasMock (QueryParams s a :> rest) where
|
=> HasMock (QueryParams s a :> rest) where
|
||||||
mock _ = \_ -> mock (Proxy :: Proxy rest)
|
mock _ = \_ -> mock (Proxy :: Proxy rest)
|
||||||
|
|
||||||
instance (KnownSymbol s, HasMock rest) => HasMock (QueryFlag s :> rest) where
|
instance (KnownSymbol s, HasMock rest) => HasMock (QueryFlag s :> rest) where
|
||||||
mock _ = \_ -> mock (Proxy :: Proxy rest)
|
mock _ = \_ -> mock (Proxy :: Proxy rest)
|
||||||
|
|
||||||
instance (KnownSymbol s, FromText a, HasMock rest)
|
instance (KnownSymbol s, FromHttpApiData a, HasMock rest)
|
||||||
=> HasMock (MatrixParam s a :> rest) where
|
=> HasMock (MatrixParam s a :> rest) where
|
||||||
mock _ = \_ -> mock (Proxy :: Proxy rest)
|
mock _ = \_ -> mock (Proxy :: Proxy rest)
|
||||||
|
|
||||||
instance (KnownSymbol s, FromText a, HasMock rest)
|
instance (KnownSymbol s, FromHttpApiData a, HasMock rest)
|
||||||
=> HasMock (MatrixParams s a :> rest) where
|
=> HasMock (MatrixParams s a :> rest) where
|
||||||
mock _ = \_ -> mock (Proxy :: Proxy rest)
|
mock _ = \_ -> mock (Proxy :: Proxy rest)
|
||||||
|
|
||||||
instance (KnownSymbol s, HasMock rest) => HasMock (MatrixFlag s :> rest) where
|
instance (KnownSymbol s, HasMock rest) => HasMock (MatrixFlag s :> rest) where
|
||||||
mock _ = \_ -> mock (Proxy :: Proxy rest)
|
mock _ = \_ -> mock (Proxy :: Proxy rest)
|
||||||
|
|
||||||
instance (KnownSymbol h, FromText a, HasMock rest) => HasMock (Header h a :> rest) where
|
instance (KnownSymbol h, FromHttpApiData a, HasMock rest) => HasMock (Header h a :> rest) where
|
||||||
mock _ = \_ -> mock (Proxy :: Proxy rest)
|
mock _ = \_ -> mock (Proxy :: Proxy rest)
|
||||||
|
|
||||||
instance (Arbitrary a, AllCTRender ctypes a) => HasMock (Delete ctypes a) where
|
instance (Arbitrary a, AllCTRender ctypes a) => HasMock (Delete ctypes a) where
|
||||||
|
|
|
@ -3,6 +3,7 @@ HEAD
|
||||||
|
|
||||||
* Support for the `HttpVersion`, `IsSecure`, `RemoteHost` and `Vault` combinators
|
* Support for the `HttpVersion`, `IsSecure`, `RemoteHost` and `Vault` combinators
|
||||||
* Drop `EitherT` in favor of `ExceptT`
|
* Drop `EitherT` in favor of `ExceptT`
|
||||||
|
* Use `http-api-data` instead of `Servant.Common.Text`
|
||||||
|
|
||||||
0.4.1
|
0.4.1
|
||||||
-----
|
-----
|
||||||
|
|
|
@ -48,6 +48,7 @@ library
|
||||||
, attoparsec >= 0.12 && < 0.14
|
, attoparsec >= 0.12 && < 0.14
|
||||||
, bytestring >= 0.10 && < 0.11
|
, bytestring >= 0.10 && < 0.11
|
||||||
, containers >= 0.5 && < 0.6
|
, containers >= 0.5 && < 0.6
|
||||||
|
, http-api-data >= 0.1 && < 0.2
|
||||||
, http-types >= 0.8 && < 0.9
|
, http-types >= 0.8 && < 0.9
|
||||||
, network-uri >= 2.6 && < 2.7
|
, network-uri >= 2.6 && < 2.7
|
||||||
, mtl >= 2 && < 3
|
, mtl >= 2 && < 3
|
||||||
|
|
|
@ -5,8 +5,6 @@ module Servant (
|
||||||
module Servant.API,
|
module Servant.API,
|
||||||
-- | For implementing servers for servant APIs.
|
-- | For implementing servers for servant APIs.
|
||||||
module Servant.Server,
|
module Servant.Server,
|
||||||
-- | Using your types in request paths and query string parameters
|
|
||||||
module Servant.Common.Text,
|
|
||||||
-- | Utilities on top of the servant core
|
-- | Utilities on top of the servant core
|
||||||
module Servant.Utils.Links,
|
module Servant.Utils.Links,
|
||||||
module Servant.Utils.StaticFiles,
|
module Servant.Utils.StaticFiles,
|
||||||
|
@ -16,7 +14,6 @@ module Servant (
|
||||||
|
|
||||||
import Data.Proxy
|
import Data.Proxy
|
||||||
import Servant.API
|
import Servant.API
|
||||||
import Servant.Common.Text
|
|
||||||
import Servant.Server
|
import Servant.Server
|
||||||
import Servant.Utils.Links
|
import Servant.Utils.Links
|
||||||
import Servant.Utils.StaticFiles
|
import Servant.Utils.StaticFiles
|
||||||
|
|
|
@ -32,7 +32,7 @@ import Data.String (fromString)
|
||||||
import Data.String.Conversions (cs, (<>), ConvertibleStrings)
|
import Data.String.Conversions (cs, (<>), ConvertibleStrings)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Data.Text.Encoding (decodeUtf8, encodeUtf8)
|
import Data.Text.Encoding (encodeUtf8)
|
||||||
import Data.Typeable
|
import Data.Typeable
|
||||||
import GHC.TypeLits (KnownSymbol, symbolVal)
|
import GHC.TypeLits (KnownSymbol, symbolVal)
|
||||||
import Network.HTTP.Types hiding (Header, ResponseHeaders)
|
import Network.HTTP.Types hiding (Header, ResponseHeaders)
|
||||||
|
@ -53,13 +53,15 @@ import Servant.API.ContentTypes (AcceptHeader (..),
|
||||||
AllCTUnrender (..))
|
AllCTUnrender (..))
|
||||||
import Servant.API.ResponseHeaders (Headers, getResponse, GetHeaders,
|
import Servant.API.ResponseHeaders (Headers, getResponse, GetHeaders,
|
||||||
getHeaders)
|
getHeaders)
|
||||||
import Servant.Common.Text (FromText, fromText)
|
|
||||||
|
|
||||||
import Servant.Server.Internal.PathInfo
|
import Servant.Server.Internal.PathInfo
|
||||||
import Servant.Server.Internal.Router
|
import Servant.Server.Internal.Router
|
||||||
import Servant.Server.Internal.RoutingApplication
|
import Servant.Server.Internal.RoutingApplication
|
||||||
import Servant.Server.Internal.ServantErr
|
import Servant.Server.Internal.ServantErr
|
||||||
|
|
||||||
|
import Web.HttpApiData (FromHttpApiData)
|
||||||
|
import Web.HttpApiData.Internal (parseUrlPieceMaybe, parseHeaderMaybe, parseQueryParamMaybe)
|
||||||
|
|
||||||
class HasServer layout where
|
class HasServer layout where
|
||||||
type ServerT layout (m :: * -> *) :: *
|
type ServerT layout (m :: * -> *) :: *
|
||||||
|
|
||||||
|
@ -89,8 +91,8 @@ instance (HasServer a, HasServer b) => HasServer (a :<|> b) where
|
||||||
where pa = Proxy :: Proxy a
|
where pa = Proxy :: Proxy a
|
||||||
pb = Proxy :: Proxy b
|
pb = Proxy :: Proxy b
|
||||||
|
|
||||||
captured :: FromText a => proxy (Capture sym a) -> Text -> Maybe a
|
captured :: FromHttpApiData a => proxy (Capture sym a) -> Text -> Maybe a
|
||||||
captured _ = fromText
|
captured _ = parseUrlPieceMaybe
|
||||||
|
|
||||||
-- | If you use 'Capture' in one of the endpoints for your API,
|
-- | If you use 'Capture' in one of the endpoints for your API,
|
||||||
-- this automatically requires your server-side handler to be a function
|
-- this automatically requires your server-side handler to be a function
|
||||||
|
@ -99,7 +101,7 @@ captured _ = fromText
|
||||||
-- it into a value of the type you specify.
|
-- it into a value of the type you specify.
|
||||||
--
|
--
|
||||||
-- You can control how it'll be converted from 'Text' to your type
|
-- You can control how it'll be converted from 'Text' to your type
|
||||||
-- by simply providing an instance of 'FromText' for your type.
|
-- by simply providing an instance of 'FromHttpApiData' for your type.
|
||||||
--
|
--
|
||||||
-- Example:
|
-- Example:
|
||||||
--
|
--
|
||||||
|
@ -109,7 +111,7 @@ captured _ = fromText
|
||||||
-- > server = getBook
|
-- > server = getBook
|
||||||
-- > where getBook :: Text -> ExceptT ServantErr IO Book
|
-- > where getBook :: Text -> ExceptT ServantErr IO Book
|
||||||
-- > getBook isbn = ...
|
-- > getBook isbn = ...
|
||||||
instance (KnownSymbol capture, FromText a, HasServer sublayout)
|
instance (KnownSymbol capture, FromHttpApiData a, HasServer sublayout)
|
||||||
=> HasServer (Capture capture a :> sublayout) where
|
=> HasServer (Capture capture a :> sublayout) where
|
||||||
|
|
||||||
type ServerT (Capture capture a :> sublayout) m =
|
type ServerT (Capture capture a :> sublayout) m =
|
||||||
|
@ -282,12 +284,12 @@ instance
|
||||||
-- This lets servant worry about extracting it from the request and turning
|
-- This lets servant worry about extracting it from the request and turning
|
||||||
-- it into a value of the type you specify.
|
-- it into a value of the type you specify.
|
||||||
--
|
--
|
||||||
-- All it asks is for a 'FromText' instance.
|
-- All it asks is for a 'FromHttpApiData' instance.
|
||||||
--
|
--
|
||||||
-- Example:
|
-- Example:
|
||||||
--
|
--
|
||||||
-- > newtype Referer = Referer Text
|
-- > newtype Referer = Referer Text
|
||||||
-- > deriving (Eq, Show, FromText, ToText)
|
-- > deriving (Eq, Show, FromHttpApiData, ToText)
|
||||||
-- >
|
-- >
|
||||||
-- > -- GET /view-my-referer
|
-- > -- GET /view-my-referer
|
||||||
-- > type MyApi = "view-my-referer" :> Header "Referer" Referer :> Get '[JSON] Referer
|
-- > type MyApi = "view-my-referer" :> Header "Referer" Referer :> Get '[JSON] Referer
|
||||||
|
@ -296,14 +298,14 @@ instance
|
||||||
-- > server = viewReferer
|
-- > server = viewReferer
|
||||||
-- > where viewReferer :: Referer -> ExceptT ServantErr IO referer
|
-- > where viewReferer :: Referer -> ExceptT ServantErr IO referer
|
||||||
-- > viewReferer referer = return referer
|
-- > viewReferer referer = return referer
|
||||||
instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
|
||||||
=> HasServer (Header sym a :> sublayout) where
|
=> HasServer (Header sym a :> sublayout) where
|
||||||
|
|
||||||
type ServerT (Header sym a :> sublayout) m =
|
type ServerT (Header sym a :> sublayout) m =
|
||||||
Maybe a -> ServerT sublayout m
|
Maybe a -> ServerT sublayout m
|
||||||
|
|
||||||
route Proxy subserver = WithRequest $ \ request ->
|
route Proxy subserver = WithRequest $ \ request ->
|
||||||
let mheader = fromText . decodeUtf8 =<< lookup str (requestHeaders request)
|
let mheader = parseHeaderMaybe =<< lookup str (requestHeaders request)
|
||||||
in route (Proxy :: Proxy sublayout) (feedTo subserver mheader)
|
in route (Proxy :: Proxy sublayout) (feedTo subserver mheader)
|
||||||
where str = fromString $ symbolVal (Proxy :: Proxy sym)
|
where str = fromString $ symbolVal (Proxy :: Proxy sym)
|
||||||
|
|
||||||
|
@ -451,7 +453,7 @@ instance
|
||||||
-- hand you 'Nothing'.
|
-- hand you 'Nothing'.
|
||||||
--
|
--
|
||||||
-- You can control how it'll be converted from 'Text' to your type
|
-- You can control how it'll be converted from 'Text' to your type
|
||||||
-- by simply providing an instance of 'FromText' for your type.
|
-- by simply providing an instance of 'FromHttpApiData' for your type.
|
||||||
--
|
--
|
||||||
-- Example:
|
-- Example:
|
||||||
--
|
--
|
||||||
|
@ -462,7 +464,7 @@ instance
|
||||||
-- > where getBooksBy :: Maybe Text -> ExceptT ServantErr IO [Book]
|
-- > where getBooksBy :: Maybe Text -> ExceptT ServantErr IO [Book]
|
||||||
-- > getBooksBy Nothing = ...return all books...
|
-- > getBooksBy Nothing = ...return all books...
|
||||||
-- > getBooksBy (Just author) = ...return books by the given author...
|
-- > getBooksBy (Just author) = ...return books by the given author...
|
||||||
instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
|
||||||
=> HasServer (QueryParam sym a :> sublayout) where
|
=> HasServer (QueryParam sym a :> sublayout) where
|
||||||
|
|
||||||
type ServerT (QueryParam sym a :> sublayout) m =
|
type ServerT (QueryParam sym a :> sublayout) m =
|
||||||
|
@ -474,7 +476,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
||||||
case lookup paramname querytext of
|
case lookup paramname querytext of
|
||||||
Nothing -> Nothing -- param absent from the query string
|
Nothing -> Nothing -- param absent from the query string
|
||||||
Just Nothing -> Nothing -- param present with no value -> Nothing
|
Just Nothing -> Nothing -- param present with no value -> Nothing
|
||||||
Just (Just v) -> fromText v -- if present, we try to convert to
|
Just (Just v) -> parseQueryParamMaybe v -- if present, we try to convert to
|
||||||
-- the right type
|
-- the right type
|
||||||
in route (Proxy :: Proxy sublayout) (feedTo subserver param)
|
in route (Proxy :: Proxy sublayout) (feedTo subserver param)
|
||||||
where paramname = cs $ symbolVal (Proxy :: Proxy sym)
|
where paramname = cs $ symbolVal (Proxy :: Proxy sym)
|
||||||
|
@ -488,7 +490,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
||||||
-- the type you specify.
|
-- the type you specify.
|
||||||
--
|
--
|
||||||
-- You can control how the individual values are converted from 'Text' to your type
|
-- You can control how the individual values are converted from 'Text' to your type
|
||||||
-- by simply providing an instance of 'FromText' for your type.
|
-- by simply providing an instance of 'FromHttpApiData' for your type.
|
||||||
--
|
--
|
||||||
-- Example:
|
-- Example:
|
||||||
--
|
--
|
||||||
|
@ -498,7 +500,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
||||||
-- > server = getBooksBy
|
-- > server = getBooksBy
|
||||||
-- > where getBooksBy :: [Text] -> ExceptT ServantErr IO [Book]
|
-- > where getBooksBy :: [Text] -> ExceptT ServantErr IO [Book]
|
||||||
-- > getBooksBy authors = ...return all books by these authors...
|
-- > getBooksBy authors = ...return all books by these authors...
|
||||||
instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
|
||||||
=> HasServer (QueryParams sym a :> sublayout) where
|
=> HasServer (QueryParams sym a :> sublayout) where
|
||||||
|
|
||||||
type ServerT (QueryParams sym a :> sublayout) m =
|
type ServerT (QueryParams sym a :> sublayout) m =
|
||||||
|
@ -507,7 +509,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
||||||
route Proxy subserver = WithRequest $ \ request ->
|
route Proxy subserver = WithRequest $ \ request ->
|
||||||
let querytext = parseQueryText $ rawQueryString request
|
let querytext = parseQueryText $ rawQueryString request
|
||||||
-- if sym is "foo", we look for query string parameters
|
-- if sym is "foo", we look for query string parameters
|
||||||
-- named "foo" or "foo[]" and call fromText on the
|
-- named "foo" or "foo[]" and call parseQueryParam on the
|
||||||
-- corresponding values
|
-- corresponding values
|
||||||
parameters = filter looksLikeParam querytext
|
parameters = filter looksLikeParam querytext
|
||||||
values = mapMaybe (convert . snd) parameters
|
values = mapMaybe (convert . snd) parameters
|
||||||
|
@ -515,7 +517,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
||||||
where paramname = cs $ symbolVal (Proxy :: Proxy sym)
|
where paramname = cs $ symbolVal (Proxy :: Proxy sym)
|
||||||
looksLikeParam (name, _) = name == paramname || name == (paramname <> "[]")
|
looksLikeParam (name, _) = name == paramname || name == (paramname <> "[]")
|
||||||
convert Nothing = Nothing
|
convert Nothing = Nothing
|
||||||
convert (Just v) = fromText v
|
convert (Just v) = parseQueryParamMaybe v
|
||||||
|
|
||||||
-- | If you use @'QueryFlag' "published"@ in one of the endpoints for your API,
|
-- | If you use @'QueryFlag' "published"@ in one of the endpoints for your API,
|
||||||
-- this automatically requires your server-side handler to be a function
|
-- this automatically requires your server-side handler to be a function
|
||||||
|
@ -559,7 +561,7 @@ parseMatrixText = parseQueryText
|
||||||
-- hand you 'Nothing'.
|
-- hand you 'Nothing'.
|
||||||
--
|
--
|
||||||
-- You can control how it'll be converted from 'Text' to your type
|
-- You can control how it'll be converted from 'Text' to your type
|
||||||
-- by simply providing an instance of 'FromText' for your type.
|
-- by simply providing an instance of 'FromHttpApiData' for your type.
|
||||||
--
|
--
|
||||||
-- Example:
|
-- Example:
|
||||||
--
|
--
|
||||||
|
@ -570,7 +572,7 @@ parseMatrixText = parseQueryText
|
||||||
-- > where getBooksBy :: Maybe Text -> ExceptT ServantErr IO [Book]
|
-- > where getBooksBy :: Maybe Text -> ExceptT ServantErr IO [Book]
|
||||||
-- > getBooksBy Nothing = ...return all books...
|
-- > getBooksBy Nothing = ...return all books...
|
||||||
-- > getBooksBy (Just author) = ...return books by the given author...
|
-- > getBooksBy (Just author) = ...return books by the given author...
|
||||||
instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
|
||||||
=> HasServer (MatrixParam sym a :> sublayout) where
|
=> HasServer (MatrixParam sym a :> sublayout) where
|
||||||
|
|
||||||
type ServerT (MatrixParam sym a :> sublayout) m =
|
type ServerT (MatrixParam sym a :> sublayout) m =
|
||||||
|
@ -583,7 +585,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
||||||
param = case lookup paramname querytext of
|
param = case lookup paramname querytext of
|
||||||
Nothing -> Nothing -- param absent from the query string
|
Nothing -> Nothing -- param absent from the query string
|
||||||
Just Nothing -> Nothing -- param present with no value -> Nothing
|
Just Nothing -> Nothing -- param present with no value -> Nothing
|
||||||
Just (Just v) -> fromText v -- if present, we try to convert to
|
Just (Just v) -> parseQueryParamMaybe v -- if present, we try to convert to
|
||||||
-- the right type
|
-- the right type
|
||||||
route (Proxy :: Proxy sublayout) (feedTo subserver param)
|
route (Proxy :: Proxy sublayout) (feedTo subserver param)
|
||||||
_ -> route (Proxy :: Proxy sublayout) (feedTo subserver Nothing)
|
_ -> route (Proxy :: Proxy sublayout) (feedTo subserver Nothing)
|
||||||
|
@ -599,7 +601,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
||||||
-- the type you specify.
|
-- the type you specify.
|
||||||
--
|
--
|
||||||
-- You can control how the individual values are converted from 'Text' to your type
|
-- You can control how the individual values are converted from 'Text' to your type
|
||||||
-- by simply providing an instance of 'FromText' for your type.
|
-- by simply providing an instance of 'FromHttpApiData' for your type.
|
||||||
--
|
--
|
||||||
-- Example:
|
-- Example:
|
||||||
--
|
--
|
||||||
|
@ -609,7 +611,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
||||||
-- > server = getBooksBy
|
-- > server = getBooksBy
|
||||||
-- > where getBooksBy :: [Text] -> ExceptT ServantErr IO [Book]
|
-- > where getBooksBy :: [Text] -> ExceptT ServantErr IO [Book]
|
||||||
-- > getBooksBy authors = ...return all books by these authors...
|
-- > getBooksBy authors = ...return all books by these authors...
|
||||||
instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
|
||||||
=> HasServer (MatrixParams sym a :> sublayout) where
|
=> HasServer (MatrixParams sym a :> sublayout) where
|
||||||
|
|
||||||
type ServerT (MatrixParams sym a :> sublayout) m =
|
type ServerT (MatrixParams sym a :> sublayout) m =
|
||||||
|
@ -620,7 +622,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
||||||
(first : _)
|
(first : _)
|
||||||
-> do let matrixtext = parseMatrixText . encodeUtf8 $ T.tail first
|
-> do let matrixtext = parseMatrixText . encodeUtf8 $ T.tail first
|
||||||
-- if sym is "foo", we look for matrix parameters
|
-- if sym is "foo", we look for matrix parameters
|
||||||
-- named "foo" or "foo[]" and call fromText on the
|
-- named "foo" or "foo[]" and call parseQueryParam on the
|
||||||
-- corresponding values
|
-- corresponding values
|
||||||
parameters = filter looksLikeParam matrixtext
|
parameters = filter looksLikeParam matrixtext
|
||||||
values = mapMaybe (convert . snd) parameters
|
values = mapMaybe (convert . snd) parameters
|
||||||
|
@ -629,7 +631,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
||||||
where paramname = cs $ symbolVal (Proxy :: Proxy sym)
|
where paramname = cs $ symbolVal (Proxy :: Proxy sym)
|
||||||
looksLikeParam (name, _) = name == paramname || name == (paramname <> "[]")
|
looksLikeParam (name, _) = name == paramname || name == (paramname <> "[]")
|
||||||
convert Nothing = Nothing
|
convert Nothing = Nothing
|
||||||
convert (Just v) = fromText v
|
convert (Just v) = parseQueryParamMaybe v
|
||||||
|
|
||||||
-- | If you use @'MatrixFlag' "published"@ in one of the endpoints for your API,
|
-- | If you use @'MatrixFlag' "published"@ in one of the endpoints for your API,
|
||||||
-- this automatically requires your server-side handler to be a function
|
-- this automatically requires your server-side handler to be a function
|
||||||
|
|
|
@ -3,7 +3,8 @@ HEAD
|
||||||
|
|
||||||
* Add `HttpVersion`, `IsSecure`, `RemoteHost` and `Vault` combinators
|
* Add `HttpVersion`, `IsSecure`, `RemoteHost` and `Vault` combinators
|
||||||
* Fix safeLink, so Header is not in fact required.
|
* Fix safeLink, so Header is not in fact required.
|
||||||
* Added more instances for (:<|>)
|
* Add more instances for (:<|>)
|
||||||
|
* Use `http-api-data` instead of `Servant.Common.Text`
|
||||||
|
|
||||||
0.4.2
|
0.4.2
|
||||||
-----
|
-----
|
||||||
|
|
|
@ -44,7 +44,6 @@ library
|
||||||
Servant.API.ResponseHeaders
|
Servant.API.ResponseHeaders
|
||||||
Servant.API.Sub
|
Servant.API.Sub
|
||||||
Servant.API.Vault
|
Servant.API.Vault
|
||||||
Servant.Common.Text
|
|
||||||
Servant.Utils.Links
|
Servant.Utils.Links
|
||||||
build-depends:
|
build-depends:
|
||||||
base >=4.7 && <5
|
base >=4.7 && <5
|
||||||
|
@ -53,6 +52,7 @@ library
|
||||||
, bytestring == 0.10.*
|
, bytestring == 0.10.*
|
||||||
, bytestring-conversion == 0.3.*
|
, bytestring-conversion == 0.3.*
|
||||||
, case-insensitive >= 1.2
|
, case-insensitive >= 1.2
|
||||||
|
, http-api-data >= 0.1 && < 0.2
|
||||||
, http-media >= 0.4 && < 0.7
|
, http-media >= 0.4 && < 0.7
|
||||||
, http-types == 0.8.*
|
, http-types == 0.8.*
|
||||||
, text >= 1 && < 2
|
, text >= 1 && < 2
|
||||||
|
@ -93,7 +93,6 @@ test-suite spec
|
||||||
other-modules:
|
other-modules:
|
||||||
Servant.API.ContentTypesSpec
|
Servant.API.ContentTypesSpec
|
||||||
Servant.API.ResponseHeadersSpec
|
Servant.API.ResponseHeadersSpec
|
||||||
Servant.Common.TextSpec
|
|
||||||
Servant.Utils.LinksSpec
|
Servant.Utils.LinksSpec
|
||||||
build-depends:
|
build-depends:
|
||||||
base == 4.*
|
base == 4.*
|
||||||
|
|
|
@ -50,9 +50,9 @@ module Servant.API (
|
||||||
module Servant.API.Raw,
|
module Servant.API.Raw,
|
||||||
-- | Plugging in a wai 'Network.Wai.Application', serving directories
|
-- | Plugging in a wai 'Network.Wai.Application', serving directories
|
||||||
|
|
||||||
-- * FromText and ToText
|
-- * FromHttpApiData and ToHttpApiData
|
||||||
module Servant.Common.Text,
|
module Web.HttpApiData,
|
||||||
-- | Classes and instances for types that can be converted to and from @Text@
|
-- | Classes and instances for types that can be converted to and from HTTP API data.
|
||||||
|
|
||||||
-- * Utilities
|
-- * Utilities
|
||||||
module Servant.Utils.Links,
|
module Servant.Utils.Links,
|
||||||
|
@ -88,7 +88,7 @@ import Servant.API.ResponseHeaders (AddHeader (addHeader),
|
||||||
getHeadersHList, getResponse)
|
getHeadersHList, getResponse)
|
||||||
import Servant.API.Sub ((:>))
|
import Servant.API.Sub ((:>))
|
||||||
import Servant.API.Vault (Vault)
|
import Servant.API.Vault (Vault)
|
||||||
import Servant.Common.Text (FromText (..), ToText (..))
|
import Web.HttpApiData (FromHttpApiData (..), ToHttpApiData (..))
|
||||||
import Servant.Utils.Links (HasLink (..), IsElem, IsElem',
|
import Servant.Utils.Links (HasLink (..), IsElem, IsElem',
|
||||||
URI (..), safeLink)
|
URI (..), safeLink)
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,6 @@ data Header (sym :: Symbol) a = Header a
|
||||||
|
|
||||||
-- $setup
|
-- $setup
|
||||||
-- >>> import Servant.API
|
-- >>> import Servant.API
|
||||||
-- >>> import Servant.Common.Text
|
|
||||||
-- >>> import Data.Aeson
|
-- >>> import Data.Aeson
|
||||||
-- >>> import Data.Text
|
-- >>> import Data.Text
|
||||||
-- >>> data Book
|
-- >>> data Book
|
||||||
|
|
|
@ -16,7 +16,6 @@ data ReqBody (contentTypes :: [*]) a
|
||||||
|
|
||||||
-- $setup
|
-- $setup
|
||||||
-- >>> import Servant.API
|
-- >>> import Servant.API
|
||||||
-- >>> import Servant.Common.Text
|
|
||||||
-- >>> import Data.Aeson
|
-- >>> import Data.Aeson
|
||||||
-- >>> import Data.Text
|
-- >>> import Data.Text
|
||||||
-- >>> data Book
|
-- >>> data Book
|
||||||
|
|
|
@ -20,7 +20,6 @@ infixr 9 :>
|
||||||
|
|
||||||
-- $setup
|
-- $setup
|
||||||
-- >>> import Servant.API
|
-- >>> import Servant.API
|
||||||
-- >>> import Servant.Common.Text
|
|
||||||
-- >>> import Data.Aeson
|
-- >>> import Data.Aeson
|
||||||
-- >>> import Data.Text
|
-- >>> import Data.Text
|
||||||
-- >>> data World
|
-- >>> data World
|
||||||
|
|
|
@ -1,149 +0,0 @@
|
||||||
{-# LANGUAGE CPP #-}
|
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
{-# LANGUAGE TypeSynonymInstances #-}
|
|
||||||
module Servant.Common.Text
|
|
||||||
( FromText(..)
|
|
||||||
, ToText(..)
|
|
||||||
) where
|
|
||||||
|
|
||||||
#if !MIN_VERSION_base(4,8,0)
|
|
||||||
import Control.Applicative ((<$>))
|
|
||||||
#endif
|
|
||||||
import Data.Int (Int16, Int32, Int64, Int8)
|
|
||||||
import Data.String.Conversions (cs)
|
|
||||||
import Data.Text (Text)
|
|
||||||
import Data.Text.Read (Reader, decimal, rational, signed)
|
|
||||||
import Data.Word (Word16, Word32, Word64, Word8
|
|
||||||
#if !MIN_VERSION_base(4,8,0)
|
|
||||||
, Word
|
|
||||||
#endif
|
|
||||||
)
|
|
||||||
|
|
||||||
-- | For getting values from url captures and query string parameters
|
|
||||||
-- Instances should obey:
|
|
||||||
-- > fromText (toText a) == Just a
|
|
||||||
class FromText a where
|
|
||||||
fromText :: Text -> Maybe a
|
|
||||||
|
|
||||||
-- | For putting values in paths and query string parameters
|
|
||||||
-- Instances should obey:
|
|
||||||
-- > fromText (toText a) == Just a
|
|
||||||
class ToText a where
|
|
||||||
toText :: a -> Text
|
|
||||||
|
|
||||||
instance FromText Text where
|
|
||||||
fromText = Just
|
|
||||||
|
|
||||||
instance ToText Text where
|
|
||||||
toText = id
|
|
||||||
|
|
||||||
instance FromText String where
|
|
||||||
fromText = Just . cs
|
|
||||||
|
|
||||||
instance ToText String where
|
|
||||||
toText = cs
|
|
||||||
|
|
||||||
-- |
|
|
||||||
-- >>> fromText ("true"::Text) :: Maybe Bool
|
|
||||||
-- Just True
|
|
||||||
-- >>> fromText ("false"::Text) :: Maybe Bool
|
|
||||||
-- Just False
|
|
||||||
-- >>> fromText ("anything else"::Text) :: Maybe Bool
|
|
||||||
-- Nothing
|
|
||||||
instance FromText Bool where
|
|
||||||
fromText "true" = Just True
|
|
||||||
fromText "false" = Just False
|
|
||||||
fromText _ = Nothing
|
|
||||||
|
|
||||||
-- |
|
|
||||||
-- >>> toText True
|
|
||||||
-- "true"
|
|
||||||
-- >>> toText False
|
|
||||||
-- "false"
|
|
||||||
instance ToText Bool where
|
|
||||||
toText True = "true"
|
|
||||||
toText False = "false"
|
|
||||||
|
|
||||||
instance FromText Int where
|
|
||||||
fromText = runReader (signed decimal)
|
|
||||||
|
|
||||||
instance ToText Int where
|
|
||||||
toText = cs . show
|
|
||||||
|
|
||||||
instance FromText Int8 where
|
|
||||||
fromText = runReader (signed decimal)
|
|
||||||
|
|
||||||
instance ToText Int8 where
|
|
||||||
toText = cs . show
|
|
||||||
|
|
||||||
instance FromText Int16 where
|
|
||||||
fromText = runReader (signed decimal)
|
|
||||||
|
|
||||||
instance ToText Int16 where
|
|
||||||
toText = cs . show
|
|
||||||
|
|
||||||
instance FromText Int32 where
|
|
||||||
fromText = runReader (signed decimal)
|
|
||||||
|
|
||||||
instance ToText Int32 where
|
|
||||||
toText = cs . show
|
|
||||||
|
|
||||||
instance FromText Int64 where
|
|
||||||
fromText = runReader (signed decimal)
|
|
||||||
|
|
||||||
instance ToText Int64 where
|
|
||||||
toText = cs . show
|
|
||||||
|
|
||||||
instance FromText Word where
|
|
||||||
fromText = runReader decimal
|
|
||||||
|
|
||||||
instance ToText Word where
|
|
||||||
toText = cs . show
|
|
||||||
|
|
||||||
instance FromText Word8 where
|
|
||||||
fromText = runReader decimal
|
|
||||||
|
|
||||||
instance ToText Word8 where
|
|
||||||
toText = cs . show
|
|
||||||
|
|
||||||
instance FromText Word16 where
|
|
||||||
fromText = runReader decimal
|
|
||||||
|
|
||||||
instance ToText Word16 where
|
|
||||||
toText = cs . show
|
|
||||||
|
|
||||||
instance FromText Word32 where
|
|
||||||
fromText = runReader decimal
|
|
||||||
|
|
||||||
instance ToText Word32 where
|
|
||||||
toText = cs . show
|
|
||||||
|
|
||||||
instance FromText Word64 where
|
|
||||||
fromText = runReader decimal
|
|
||||||
|
|
||||||
instance ToText Word64 where
|
|
||||||
toText = cs . show
|
|
||||||
|
|
||||||
instance FromText Integer where
|
|
||||||
fromText = runReader (signed decimal)
|
|
||||||
|
|
||||||
instance ToText Integer where
|
|
||||||
toText = cs . show
|
|
||||||
|
|
||||||
instance FromText Double where
|
|
||||||
fromText x = fromRational <$> runReader rational x
|
|
||||||
|
|
||||||
instance ToText Double where
|
|
||||||
toText = cs . show
|
|
||||||
|
|
||||||
instance FromText Float where
|
|
||||||
-- Double is more practically accurate due to weird rounding when using
|
|
||||||
-- rational. We convert to double and then convert to Float.
|
|
||||||
fromText x = fromRational <$> runReader rational x
|
|
||||||
|
|
||||||
instance ToText Float where
|
|
||||||
toText = cs . show
|
|
||||||
|
|
||||||
runReader :: Reader a -> Text -> Maybe a
|
|
||||||
runReader reader t = either (const Nothing) (Just . fst) $ reader t
|
|
|
@ -113,7 +113,7 @@ import Network.URI ( URI(..), escapeURIString, isUnreserved )
|
||||||
import GHC.TypeLits ( KnownSymbol, symbolVal )
|
import GHC.TypeLits ( KnownSymbol, symbolVal )
|
||||||
import GHC.Exts(Constraint)
|
import GHC.Exts(Constraint)
|
||||||
|
|
||||||
import Servant.Common.Text
|
import Web.HttpApiData
|
||||||
import Servant.API.Capture ( Capture )
|
import Servant.API.Capture ( Capture )
|
||||||
import Servant.API.ReqBody ( ReqBody )
|
import Servant.API.ReqBody ( ReqBody )
|
||||||
import Servant.API.QueryParam ( QueryParam, QueryParams, QueryFlag )
|
import Servant.API.QueryParam ( QueryParam, QueryParams, QueryFlag )
|
||||||
|
@ -271,22 +271,22 @@ instance (KnownSymbol sym, HasLink sub) => HasLink (sym :> sub) where
|
||||||
|
|
||||||
|
|
||||||
-- QueryParam instances
|
-- QueryParam instances
|
||||||
instance (KnownSymbol sym, ToText v, HasLink sub)
|
instance (KnownSymbol sym, ToHttpApiData v, HasLink sub)
|
||||||
=> HasLink (QueryParam sym v :> sub) where
|
=> HasLink (QueryParam sym v :> sub) where
|
||||||
type MkLink (QueryParam sym v :> sub) = Maybe v -> MkLink sub
|
type MkLink (QueryParam sym v :> sub) = Maybe v -> MkLink sub
|
||||||
toLink _ l mv =
|
toLink _ l mv =
|
||||||
toLink (Proxy :: Proxy sub) $
|
toLink (Proxy :: Proxy sub) $
|
||||||
maybe id (addQueryParam . SingleParam k . toText) mv l
|
maybe id (addQueryParam . SingleParam k . toQueryParam) mv l
|
||||||
where
|
where
|
||||||
k :: String
|
k :: String
|
||||||
k = symbolVal (Proxy :: Proxy sym)
|
k = symbolVal (Proxy :: Proxy sym)
|
||||||
|
|
||||||
instance (KnownSymbol sym, ToText v, HasLink sub)
|
instance (KnownSymbol sym, ToHttpApiData v, HasLink sub)
|
||||||
=> HasLink (QueryParams sym v :> sub) where
|
=> HasLink (QueryParams sym v :> sub) where
|
||||||
type MkLink (QueryParams sym v :> sub) = [v] -> MkLink sub
|
type MkLink (QueryParams sym v :> sub) = [v] -> MkLink sub
|
||||||
toLink _ l =
|
toLink _ l =
|
||||||
toLink (Proxy :: Proxy sub) .
|
toLink (Proxy :: Proxy sub) .
|
||||||
foldl' (\l' v -> addQueryParam (ArrayElemParam k (toText v)) l') l
|
foldl' (\l' v -> addQueryParam (ArrayElemParam k (toQueryParam v)) l') l
|
||||||
where
|
where
|
||||||
k = symbolVal (Proxy :: Proxy sym)
|
k = symbolVal (Proxy :: Proxy sym)
|
||||||
|
|
||||||
|
@ -301,21 +301,21 @@ instance (KnownSymbol sym, HasLink sub)
|
||||||
k = symbolVal (Proxy :: Proxy sym)
|
k = symbolVal (Proxy :: Proxy sym)
|
||||||
|
|
||||||
-- MatrixParam instances
|
-- MatrixParam instances
|
||||||
instance (KnownSymbol sym, ToText v, HasLink sub)
|
instance (KnownSymbol sym, ToHttpApiData v, HasLink sub)
|
||||||
=> HasLink (MatrixParam sym v :> sub) where
|
=> HasLink (MatrixParam sym v :> sub) where
|
||||||
type MkLink (MatrixParam sym v :> sub) = Maybe v -> MkLink sub
|
type MkLink (MatrixParam sym v :> sub) = Maybe v -> MkLink sub
|
||||||
toLink _ l mv =
|
toLink _ l mv =
|
||||||
toLink (Proxy :: Proxy sub) $
|
toLink (Proxy :: Proxy sub) $
|
||||||
maybe id (addMatrixParam . SingleParam k . toText) mv l
|
maybe id (addMatrixParam . SingleParam k . toQueryParam) mv l
|
||||||
where
|
where
|
||||||
k = symbolVal (Proxy :: Proxy sym)
|
k = symbolVal (Proxy :: Proxy sym)
|
||||||
|
|
||||||
instance (KnownSymbol sym, ToText v, HasLink sub)
|
instance (KnownSymbol sym, ToHttpApiData v, HasLink sub)
|
||||||
=> HasLink (MatrixParams sym v :> sub) where
|
=> HasLink (MatrixParams sym v :> sub) where
|
||||||
type MkLink (MatrixParams sym v :> sub) = [v] -> MkLink sub
|
type MkLink (MatrixParams sym v :> sub) = [v] -> MkLink sub
|
||||||
toLink _ l =
|
toLink _ l =
|
||||||
toLink (Proxy :: Proxy sub) .
|
toLink (Proxy :: Proxy sub) .
|
||||||
foldl' (\l' v -> addMatrixParam (ArrayElemParam k (toText v)) l') l
|
foldl' (\l' v -> addMatrixParam (ArrayElemParam k (toQueryParam v)) l') l
|
||||||
where
|
where
|
||||||
k = symbolVal (Proxy :: Proxy sym)
|
k = symbolVal (Proxy :: Proxy sym)
|
||||||
|
|
||||||
|
@ -334,12 +334,12 @@ instance HasLink sub => HasLink (ReqBody ct a :> sub) where
|
||||||
type MkLink (ReqBody ct a :> sub) = MkLink sub
|
type MkLink (ReqBody ct a :> sub) = MkLink sub
|
||||||
toLink _ = toLink (Proxy :: Proxy sub)
|
toLink _ = toLink (Proxy :: Proxy sub)
|
||||||
|
|
||||||
instance (ToText v, HasLink sub)
|
instance (ToHttpApiData v, HasLink sub)
|
||||||
=> HasLink (Capture sym v :> sub) where
|
=> HasLink (Capture sym v :> sub) where
|
||||||
type MkLink (Capture sym v :> sub) = v -> MkLink sub
|
type MkLink (Capture sym v :> sub) = v -> MkLink sub
|
||||||
toLink _ l v =
|
toLink _ l v =
|
||||||
toLink (Proxy :: Proxy sub) $
|
toLink (Proxy :: Proxy sub) $
|
||||||
addSegment (escape . unpack $ toText v) l
|
addSegment (escape . unpack $ toUrlPiece v) l
|
||||||
|
|
||||||
instance HasLink sub => HasLink (Header sym a :> sub) where
|
instance HasLink sub => HasLink (Header sym a :> sub) where
|
||||||
type MkLink (Header sym a :> sub) = MkLink sub
|
type MkLink (Header sym a :> sub) = MkLink sub
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
{-# LANGUAGE CPP #-}
|
|
||||||
module Servant.Common.TextSpec where
|
|
||||||
|
|
||||||
import Data.Int (Int16, Int32, Int64, Int8)
|
|
||||||
import Data.Text (Text)
|
|
||||||
import Data.Word (Word16, Word32, Word64, Word8
|
|
||||||
#if !MIN_VERSION_base(4,8,0)
|
|
||||||
, Word
|
|
||||||
#endif
|
|
||||||
)
|
|
||||||
import Servant.Common.Text
|
|
||||||
import Test.Hspec
|
|
||||||
import Test.QuickCheck
|
|
||||||
import Test.QuickCheck.Instances ()
|
|
||||||
|
|
||||||
spec :: Spec
|
|
||||||
spec = describe "Servant.Common.Text" $ do
|
|
||||||
|
|
||||||
context "FromText and ToText laws" $ do
|
|
||||||
|
|
||||||
it "holds for Text" $
|
|
||||||
property $ \x -> textLaw (x :: Text)
|
|
||||||
|
|
||||||
it "holds for String" $
|
|
||||||
property $ \x -> textLaw (x :: String)
|
|
||||||
|
|
||||||
it "holds for Bool" $
|
|
||||||
property $ \x -> textLaw (x :: Bool)
|
|
||||||
|
|
||||||
it "holds for Int" $
|
|
||||||
property $ \x -> textLaw (x :: Int)
|
|
||||||
|
|
||||||
it "holds for Int8" $
|
|
||||||
property $ \x -> textLaw (x :: Int8)
|
|
||||||
|
|
||||||
it "holds for Int16" $
|
|
||||||
property $ \x -> textLaw (x :: Int16)
|
|
||||||
|
|
||||||
it "holds for Int32" $
|
|
||||||
property $ \x -> textLaw (x :: Int32)
|
|
||||||
|
|
||||||
it "holds for Int64" $
|
|
||||||
property $ \x -> textLaw (x :: Int64)
|
|
||||||
|
|
||||||
it "holds for Word" $
|
|
||||||
property $ \x -> textLaw (x :: Word)
|
|
||||||
|
|
||||||
it "holds for Word8" $
|
|
||||||
property $ \x -> textLaw (x :: Word8)
|
|
||||||
|
|
||||||
it "holds for Word16" $
|
|
||||||
property $ \x -> textLaw (x :: Word16)
|
|
||||||
|
|
||||||
it "holds for Word32" $
|
|
||||||
property $ \x -> textLaw (x :: Word32)
|
|
||||||
|
|
||||||
it "holds for Word64" $
|
|
||||||
property $ \x -> textLaw (x :: Word64)
|
|
||||||
|
|
||||||
it "holds for Integer" $
|
|
||||||
property $ \x -> textLaw (x :: Integer)
|
|
||||||
|
|
||||||
-- The following two properties are only reasonably expected to hold up
|
|
||||||
-- to a certain precision.
|
|
||||||
--
|
|
||||||
-- http://en.wikipedia.org/wiki/Floating_point#Internal_representation
|
|
||||||
it "holds for Double" $
|
|
||||||
property $ \x -> textLaw (x :: Double)
|
|
||||||
|
|
||||||
it "holds for Float" $
|
|
||||||
property $ \x -> textLaw (x :: Float)
|
|
||||||
|
|
||||||
textLaw :: (FromText a, ToText a, Eq a) => a -> Bool
|
|
||||||
textLaw a = fromText (toText a) == Just a
|
|
|
@ -21,4 +21,5 @@ extra-deps:
|
||||||
- socket-io-1.3.3
|
- socket-io-1.3.3
|
||||||
- stm-delay-0.1.1.1
|
- stm-delay-0.1.1.1
|
||||||
- control-monad-omega-0.3.1
|
- control-monad-omega-0.3.1
|
||||||
|
- http-api-data-0.1.1.1
|
||||||
resolver: lts-2.22
|
resolver: lts-2.22
|
||||||
|
|
|
@ -16,4 +16,4 @@ packages:
|
||||||
extra-deps:
|
extra-deps:
|
||||||
- engine-io-wai-1.0.2
|
- engine-io-wai-1.0.2
|
||||||
- control-monad-omega-0.3.1
|
- control-monad-omega-0.3.1
|
||||||
resolver: nightly-2015-09-10
|
resolver: nightly-2015-10-08
|
||||||
|
|
Loading…
Reference in a new issue