git.iko.soy/typeable/servant
on master help sign in
Merge pull request #250 from fizruk/http-api-data

Use http-api-data

commit3ce7c9758ac1f35896aa3559281dd7bb2ae1d400 authorJulian Arni <jkarni@gmail.com> dateThu 8 Oct 2015, 23:16 (11 years ago) parent05623f5707e46a
20 files changed+78−299
servant-client/CHANGELOG.md+1 servant-client/servant-client.cabal+1 servant-client/src/Servant/Client.hs+18−18 servant-client/src/Servant/Common/Req.hs+4−3 servant-mock/src/Servant/Mock.hs+6−6 servant-server/CHANGELOG.md+1 servant-server/servant-server.cabal+1 servant-server/src/Servant.hs−3 servant-server/src/Servant/Server/Internal.hs+26−24 servant/CHANGELOG.md+2−1 servant/servant.cabal+1−2 servant/src/Servant/API.hs+4−4 servant/src/Servant/API/Header.hs−1 servant/src/Servant/API/ReqBody.hs−1 servant/src/Servant/API/Sub.hs−1 servant/src/Servant/Common/Text.hs−149 servant/src/Servant/Utils/Links.hs+11−11 servant/test/Servant/Common/TextSpec.hs−74 stack-ghc-7.8.4.yaml+1 stack.yaml+1−1
servant-client/CHANGELOG.md+1
@@ -5,6 +5,7 @@ HEAD
55* Support for the `HttpVersion`, `IsSecure`, `RemoteHost` and `Vault` combinators
66* Added support for `path` on `BaseUrl`.
77* `client` now takes an explicit `Manager` argument.
8+* Use `http-api-data` instead of `Servant.Common.Text`
89
9100.4.1
1011-----
servant-client/servant-client.cabal+1
@@ -34,6 +34,7 @@ library
3434 , attoparsec
3535 , bytestring
3636 , exceptions
37+ , http-api-data >= 0.1 && < 0.2
3738 , http-client
3839 , http-client-tls
3940 , http-media
servant-client/src/Servant/Client.hs+18−18
@@ -92,7 +92,7 @@ instance (HasClient a, HasClient b) => HasClient (a :<|> b) where
9292-- of this value at the right place in the request path.
9393--
9494-- You can control how values for this type are turned into
95−-- text by specifying a 'ToText' instance for your type.
95+-- text by specifying a 'ToHttpApiData' instance for your type.
9696--
9797-- Example:
9898--
@@ -105,7 +105,7 @@ instance (HasClient a, HasClient b) => HasClient (a :<|> b) where
105105-- > getBook = client myApi host manager
106106-- > where host = BaseUrl Http "localhost" 8080
107107-- > -- then you can just use "getBook" to query that endpoint
108−instance (KnownSymbol capture, ToText a, HasClient sublayout)
108+instance (KnownSymbol capture, ToHttpApiData a, HasClient sublayout)
109109 => HasClient (Capture capture a :> sublayout) where
110110
111111 type Client (Capture capture a :> sublayout) =
@@ -117,7 +117,7 @@ instance (KnownSymbol capture, ToText a, HasClient sublayout)
117117 baseurl
118118 manager
119119
120− where p = unpack (toText val)
120+ where p = unpack (toUrlPiece val)
121121
122122-- | If you have a 'Delete' endpoint in your API, the client
123123-- side querying function that is created when calling 'client'
@@ -205,12 +205,12 @@ instance
205205-- That function will take care of encoding this argument as Text
206206-- in the request headers.
207207--
208−-- All you need is for your type to have a 'ToText' instance.
208+-- All you need is for your type to have a 'ToHttpApiData' instance.
209209--
210210-- Example:
211211--
212212-- > newtype Referer = Referer { referrer :: Text }
213−-- > deriving (Eq, Show, Generic, FromText, ToText)
213+-- > deriving (Eq, Show, Generic, FromText, ToHttpApiData)
214214-- >
215215-- > -- GET /view-my-referer
216216-- > type MyApi = "view-my-referer" :> Header "Referer" Referer :> Get '[JSON] Referer
@@ -223,7 +223,7 @@ instance
223223-- > where host = BaseUrl Http "localhost" 8080
224224-- > -- then you can just use "viewRefer" to query that endpoint
225225-- > -- specifying Nothing or e.g Just "http://haskell.org/" as arguments
226−instance (KnownSymbol sym, ToText a, HasClient sublayout)
226+instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout)
227227 => HasClient (Header sym a :> sublayout) where
228228
229229 type Client (Header sym a :> sublayout) =
@@ -368,7 +368,7 @@ instance
368368-- of inserting a textual representation of this value in the query string.
369369--
370370-- You can control how values for your type are turned into
371−-- text by specifying a 'ToText' instance for your type.
371+-- text by specifying a 'ToHttpApiData' instance for your type.
372372--
373373-- Example:
374374--
@@ -383,7 +383,7 @@ instance
383383-- > -- then you can just use "getBooksBy" to query that endpoint.
384384-- > -- 'getBooksBy Nothing' for all books
385385-- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov
386−instance (KnownSymbol sym, ToText a, HasClient sublayout)
386+instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout)
387387 => HasClient (QueryParam sym a :> sublayout) where
388388
389389 type Client (QueryParam sym a :> sublayout) =
@@ -401,7 +401,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
401401
402402 where pname = cs pname'
403403 pname' = symbolVal (Proxy :: Proxy sym)
404− mparamText = fmap toText mparam
404+ mparamText = fmap toQueryParam mparam
405405
406406-- | If you use a 'QueryParams' in one of your endpoints in your API,
407407-- the corresponding querying function will automatically take
@@ -415,7 +415,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
415415-- under the same query string parameter name.
416416--
417417-- You can control how values for your type are turned into
418−-- text by specifying a 'ToText' instance for your type.
418+-- text by specifying a 'ToHttpApiData' instance for your type.
419419--
420420-- Example:
421421--
@@ -431,7 +431,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
431431-- > -- 'getBooksBy []' for all books
432432-- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
433433-- > -- to get all books by Asimov and Heinlein
434−instance (KnownSymbol sym, ToText a, HasClient sublayout)
434+instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout)
435435 => HasClient (QueryParams sym a :> sublayout) where
436436
437437 type Client (QueryParams sym a :> sublayout) =
@@ -447,7 +447,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
447447
448448 where pname = cs pname'
449449 pname' = symbolVal (Proxy :: Proxy sym)
450− paramlist' = map (Just . toText) paramlist
450+ paramlist' = map (Just . toQueryParam) paramlist
451451
452452-- | If you use a 'QueryFlag' in one of your endpoints in your API,
453453-- the corresponding querying function will automatically take
@@ -498,7 +498,7 @@ instance (KnownSymbol sym, HasClient sublayout)
498498-- of inserting a textual representation of this value in the query string.
499499--
500500-- You can control how values for your type are turned into
501−-- text by specifying a 'ToText' instance for your type.
501+-- text by specifying a 'ToHttpApiData' instance for your type.
502502--
503503-- Example:
504504--
@@ -513,7 +513,7 @@ instance (KnownSymbol sym, HasClient sublayout)
513513-- > -- then you can just use "getBooksBy" to query that endpoint.
514514-- > -- 'getBooksBy Nothing' for all books
515515-- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov
516−instance (KnownSymbol sym, ToText a, HasClient sublayout)
516+instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout)
517517 => HasClient (MatrixParam sym a :> sublayout) where
518518
519519 type Client (MatrixParam sym a :> sublayout) =
@@ -529,7 +529,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
529529 baseurl manager
530530
531531 where pname = symbolVal (Proxy :: Proxy sym)
532− mparamText = fmap (cs . toText) mparam
532+ mparamText = fmap (cs . toQueryParam) mparam
533533
534534-- | If you use a 'MatrixParams' in one of your endpoints in your API,
535535-- the corresponding querying function will automatically take an
@@ -543,7 +543,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
543543-- same matrix string parameter name.
544544--
545545-- You can control how values for your type are turned into text by
546−-- specifying a 'ToText' instance for your type.
546+-- specifying a 'ToHttpApiData' instance for your type.
547547--
548548-- Example:
549549--
@@ -559,7 +559,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
559559-- > -- 'getBooksBy []' for all books
560560-- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
561561-- > -- to get all books by Asimov and Heinlein
562−instance (KnownSymbol sym, ToText a, HasClient sublayout)
562+instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout)
563563 => HasClient (MatrixParams sym a :> sublayout) where
564564
565565 type Client (MatrixParams sym a :> sublayout) =
@@ -575,7 +575,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
575575
576576 where pname = cs pname'
577577 pname' = symbolVal (Proxy :: Proxy sym)
578− paramlist' = map (Just . toText) paramlist
578+ paramlist' = map (Just . toQueryParam) paramlist
579579
580580-- | If you use a 'MatrixFlag' in one of your endpoints in your API,
581581-- the corresponding querying function will automatically take an
servant-client/src/Servant/Common/Req.hs+4−3
@@ -26,10 +26,11 @@ import qualified Network.HTTP.Types.Header as HTTP
2626import Network.URI hiding (path)
2727import Servant.API.ContentTypes
2828import Servant.Common.BaseUrl
29−import Servant.Common.Text
3029
3130import qualified Network.HTTP.Client as Client
3231
32+import Web.HttpApiData
33+
3334data ServantError
3435 = FailureResponse
3536 { responseStatus :: Status
@@ -86,9 +87,9 @@ appendToQueryString pname pvalue req =
8687 req { qs = qs req ++ [(pname, pvalue)]
8788 }
8889
89−addHeader :: ToText a => String -> a -> Req -> Req
90+addHeader :: ToHttpApiData a => String -> a -> Req -> Req
9091addHeader name val req = req { headers = headers req
91− ++ [(name, toText val)]
92+ ++ [(name, decodeUtf8 (toHeader val))]
9293 }
9394
9495setRQBody :: ByteString -> MediaType -> Req -> Req
servant-mock/src/Servant/Mock.hs+6−6
@@ -107,7 +107,7 @@ instance (HasMock a, HasMock b) => HasMock (a :<|> b) where
107107instance (KnownSymbol path, HasMock rest) => HasMock (path :> rest) where
108108 mock _ = mock (Proxy :: Proxy rest)
109109
110−instance (KnownSymbol s, FromText a, HasMock rest) => HasMock (Capture s a :> rest) where
110+instance (KnownSymbol s, FromHttpApiData a, HasMock rest) => HasMock (Capture s a :> rest) where
111111 mock _ = \_ -> mock (Proxy :: Proxy rest)
112112
113113instance (AllCTUnrender ctypes a, HasMock rest) => HasMock (ReqBody ctypes a :> rest) where
@@ -125,29 +125,29 @@ instance HasMock rest => HasMock (Vault :> rest) where
125125instance HasMock rest => HasMock (HttpVersion :> rest) where
126126 mock _ = \_ -> mock (Proxy :: Proxy rest)
127127
128−instance (KnownSymbol s, FromText a, HasMock rest)
128+instance (KnownSymbol s, FromHttpApiData a, HasMock rest)
129129 => HasMock (QueryParam s a :> rest) where
130130 mock _ = \_ -> mock (Proxy :: Proxy rest)
131131
132−instance (KnownSymbol s, FromText a, HasMock rest)
132+instance (KnownSymbol s, FromHttpApiData a, HasMock rest)
133133 => HasMock (QueryParams s a :> rest) where
134134 mock _ = \_ -> mock (Proxy :: Proxy rest)
135135
136136instance (KnownSymbol s, HasMock rest) => HasMock (QueryFlag s :> rest) where
137137 mock _ = \_ -> mock (Proxy :: Proxy rest)
138138
139−instance (KnownSymbol s, FromText a, HasMock rest)
139+instance (KnownSymbol s, FromHttpApiData a, HasMock rest)
140140 => HasMock (MatrixParam s a :> rest) where
141141 mock _ = \_ -> mock (Proxy :: Proxy rest)
142142
143−instance (KnownSymbol s, FromText a, HasMock rest)
143+instance (KnownSymbol s, FromHttpApiData a, HasMock rest)
144144 => HasMock (MatrixParams s a :> rest) where
145145 mock _ = \_ -> mock (Proxy :: Proxy rest)
146146
147147instance (KnownSymbol s, HasMock rest) => HasMock (MatrixFlag s :> rest) where
148148 mock _ = \_ -> mock (Proxy :: Proxy rest)
149149
150−instance (KnownSymbol h, FromText a, HasMock rest) => HasMock (Header h a :> rest) where
150+instance (KnownSymbol h, FromHttpApiData a, HasMock rest) => HasMock (Header h a :> rest) where
151151 mock _ = \_ -> mock (Proxy :: Proxy rest)
152152
153153instance (Arbitrary a, AllCTRender ctypes a) => HasMock (Delete ctypes a) where
servant-server/CHANGELOG.md+1
@@ -3,6 +3,7 @@ HEAD
33
44* Support for the `HttpVersion`, `IsSecure`, `RemoteHost` and `Vault` combinators
55* Drop `EitherT` in favor of `ExceptT`
6+* Use `http-api-data` instead of `Servant.Common.Text`
67
780.4.1
89-----
servant-server/servant-server.cabal+1
@@ -48,6 +48,7 @@ library
4848 , attoparsec >= 0.12 && < 0.14
4949 , bytestring >= 0.10 && < 0.11
5050 , containers >= 0.5 && < 0.6
51+ , http-api-data >= 0.1 && < 0.2
5152 , http-types >= 0.8 && < 0.9
5253 , network-uri >= 2.6 && < 2.7
5354 , mtl >= 2 && < 3
servant-server/src/Servant.hs−3
@@ -5,8 +5,6 @@ module Servant (
55 module Servant.API,
66 -- | For implementing servers for servant APIs.
77 module Servant.Server,
8− -- | Using your types in request paths and query string parameters
9− module Servant.Common.Text,
108 -- | Utilities on top of the servant core
119 module Servant.Utils.Links,
1210 module Servant.Utils.StaticFiles,
@@ -16,7 +14,6 @@ module Servant (
1614
1715import Data.Proxy
1816import Servant.API
19−import Servant.Common.Text
2017import Servant.Server
2118import Servant.Utils.Links
2219import Servant.Utils.StaticFiles
servant-server/src/Servant/Server/Internal.hs+26−24
@@ -32,7 +32,7 @@ import Data.String (fromString)
3232import Data.String.Conversions (cs, (<>), ConvertibleStrings)
3333import Data.Text (Text)
3434import qualified Data.Text as T
35−import Data.Text.Encoding (decodeUtf8, encodeUtf8)
35+import Data.Text.Encoding (encodeUtf8)
3636import Data.Typeable
3737import GHC.TypeLits (KnownSymbol, symbolVal)
3838import Network.HTTP.Types hiding (Header, ResponseHeaders)
@@ -53,13 +53,15 @@ import Servant.API.ContentTypes (AcceptHeader (..),
5353 AllCTUnrender (..))
5454import Servant.API.ResponseHeaders (Headers, getResponse, GetHeaders,
5555 getHeaders)
56−import Servant.Common.Text (FromText, fromText)
5756
5857import Servant.Server.Internal.PathInfo
5958import Servant.Server.Internal.Router
6059import Servant.Server.Internal.RoutingApplication
6160import Servant.Server.Internal.ServantErr
6261
62+import Web.HttpApiData (FromHttpApiData)
63+import Web.HttpApiData.Internal (parseUrlPieceMaybe, parseHeaderMaybe, parseQueryParamMaybe)
64+
6365class HasServer layout where
6466 type ServerT layout (m :: * -> *) :: *
6567
@@ -89,8 +91,8 @@ instance (HasServer a, HasServer b) => HasServer (a :<|> b) where
8991 where pa = Proxy :: Proxy a
9092 pb = Proxy :: Proxy b
9193
92−captured :: FromText a => proxy (Capture sym a) -> Text -> Maybe a
93−captured _ = fromText
94+captured :: FromHttpApiData a => proxy (Capture sym a) -> Text -> Maybe a
95+captured _ = parseUrlPieceMaybe
9496
9597-- | If you use 'Capture' in one of the endpoints for your API,
9698-- this automatically requires your server-side handler to be a function
@@ -99,7 +101,7 @@ captured _ = fromText
99101-- it into a value of the type you specify.
100102--
101103-- You can control how it'll be converted from 'Text' to your type
102−-- by simply providing an instance of 'FromText' for your type.
104+-- by simply providing an instance of 'FromHttpApiData' for your type.
103105--
104106-- Example:
105107--
@@ -109,7 +111,7 @@ captured _ = fromText
109111-- > server = getBook
110112-- > where getBook :: Text -> ExceptT ServantErr IO Book
111113-- > getBook isbn = ...
112−instance (KnownSymbol capture, FromText a, HasServer sublayout)
114+instance (KnownSymbol capture, FromHttpApiData a, HasServer sublayout)
113115 => HasServer (Capture capture a :> sublayout) where
114116
115117 type ServerT (Capture capture a :> sublayout) m =
@@ -282,12 +284,12 @@ instance
282284-- This lets servant worry about extracting it from the request and turning
283285-- it into a value of the type you specify.
284286--
285−-- All it asks is for a 'FromText' instance.
287+-- All it asks is for a 'FromHttpApiData' instance.
286288--
287289-- Example:
288290--
289291-- > newtype Referer = Referer Text
290−-- > deriving (Eq, Show, FromText, ToText)
292+-- > deriving (Eq, Show, FromHttpApiData, ToText)
291293-- >
292294-- > -- GET /view-my-referer
293295-- > type MyApi = "view-my-referer" :> Header "Referer" Referer :> Get '[JSON] Referer
@@ -296,14 +298,14 @@ instance
296298-- > server = viewReferer
297299-- > where viewReferer :: Referer -> ExceptT ServantErr IO referer
298300-- > viewReferer referer = return referer
299−instance (KnownSymbol sym, FromText a, HasServer sublayout)
301+instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
300302 => HasServer (Header sym a :> sublayout) where
301303
302304 type ServerT (Header sym a :> sublayout) m =
303305 Maybe a -> ServerT sublayout m
304306
305307 route Proxy subserver = WithRequest $ \ request ->
306− let mheader = fromText . decodeUtf8 =<< lookup str (requestHeaders request)
308+ let mheader = parseHeaderMaybe =<< lookup str (requestHeaders request)
307309 in route (Proxy :: Proxy sublayout) (feedTo subserver mheader)
308310 where str = fromString $ symbolVal (Proxy :: Proxy sym)
309311
@@ -451,7 +453,7 @@ instance
451453-- hand you 'Nothing'.
452454--
453455-- You can control how it'll be converted from 'Text' to your type
454−-- by simply providing an instance of 'FromText' for your type.
456+-- by simply providing an instance of 'FromHttpApiData' for your type.
455457--
456458-- Example:
457459--
@@ -462,7 +464,7 @@ instance
462464-- > where getBooksBy :: Maybe Text -> ExceptT ServantErr IO [Book]
463465-- > getBooksBy Nothing = ...return all books...
464466-- > getBooksBy (Just author) = ...return books by the given author...
465−instance (KnownSymbol sym, FromText a, HasServer sublayout)
467+instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
466468 => HasServer (QueryParam sym a :> sublayout) where
467469
468470 type ServerT (QueryParam sym a :> sublayout) m =
@@ -474,7 +476,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
474476 case lookup paramname querytext of
475477 Nothing -> Nothing -- param absent from the query string
476478 Just Nothing -> Nothing -- param present with no value -> Nothing
477− Just (Just v) -> fromText v -- if present, we try to convert to
479+ Just (Just v) -> parseQueryParamMaybe v -- if present, we try to convert to
478480 -- the right type
479481 in route (Proxy :: Proxy sublayout) (feedTo subserver param)
480482 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
@@ -488,7 +490,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
488490-- the type you specify.
489491--
490492-- You can control how the individual values are converted from 'Text' to your type
491−-- by simply providing an instance of 'FromText' for your type.
493+-- by simply providing an instance of 'FromHttpApiData' for your type.
492494--
493495-- Example:
494496--
@@ -498,7 +500,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
498500-- > server = getBooksBy
499501-- > where getBooksBy :: [Text] -> ExceptT ServantErr IO [Book]
500502-- > getBooksBy authors = ...return all books by these authors...
501−instance (KnownSymbol sym, FromText a, HasServer sublayout)
503+instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
502504 => HasServer (QueryParams sym a :> sublayout) where
503505
504506 type ServerT (QueryParams sym a :> sublayout) m =
@@ -507,7 +509,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
507509 route Proxy subserver = WithRequest $ \ request ->
508510 let querytext = parseQueryText $ rawQueryString request
509511 -- if sym is "foo", we look for query string parameters
510− -- named "foo" or "foo[]" and call fromText on the
512+ -- named "foo" or "foo[]" and call parseQueryParam on the
511513 -- corresponding values
512514 parameters = filter looksLikeParam querytext
513515 values = mapMaybe (convert . snd) parameters
@@ -515,7 +517,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
515517 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
516518 looksLikeParam (name, _) = name == paramname || name == (paramname <> "[]")
517519 convert Nothing = Nothing
518− convert (Just v) = fromText v
520+ convert (Just v) = parseQueryParamMaybe v
519521
520522-- | If you use @'QueryFlag' "published"@ in one of the endpoints for your API,
521523-- this automatically requires your server-side handler to be a function
@@ -559,7 +561,7 @@ parseMatrixText = parseQueryText
559561-- hand you 'Nothing'.
560562--
561563-- You can control how it'll be converted from 'Text' to your type
562−-- by simply providing an instance of 'FromText' for your type.
564+-- by simply providing an instance of 'FromHttpApiData' for your type.
563565--
564566-- Example:
565567--
@@ -570,7 +572,7 @@ parseMatrixText = parseQueryText
570572-- > where getBooksBy :: Maybe Text -> ExceptT ServantErr IO [Book]
571573-- > getBooksBy Nothing = ...return all books...
572574-- > getBooksBy (Just author) = ...return books by the given author...
573−instance (KnownSymbol sym, FromText a, HasServer sublayout)
575+instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
574576 => HasServer (MatrixParam sym a :> sublayout) where
575577
576578 type ServerT (MatrixParam sym a :> sublayout) m =
@@ -583,7 +585,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
583585 param = case lookup paramname querytext of
584586 Nothing -> Nothing -- param absent from the query string
585587 Just Nothing -> Nothing -- param present with no value -> Nothing
586− Just (Just v) -> fromText v -- if present, we try to convert to
588+ Just (Just v) -> parseQueryParamMaybe v -- if present, we try to convert to
587589 -- the right type
588590 route (Proxy :: Proxy sublayout) (feedTo subserver param)
589591 _ -> route (Proxy :: Proxy sublayout) (feedTo subserver Nothing)
@@ -599,7 +601,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
599601-- the type you specify.
600602--
601603-- You can control how the individual values are converted from 'Text' to your type
602−-- by simply providing an instance of 'FromText' for your type.
604+-- by simply providing an instance of 'FromHttpApiData' for your type.
603605--
604606-- Example:
605607--
@@ -609,7 +611,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
609611-- > server = getBooksBy
610612-- > where getBooksBy :: [Text] -> ExceptT ServantErr IO [Book]
611613-- > getBooksBy authors = ...return all books by these authors...
612−instance (KnownSymbol sym, FromText a, HasServer sublayout)
614+instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
613615 => HasServer (MatrixParams sym a :> sublayout) where
614616
615617 type ServerT (MatrixParams sym a :> sublayout) m =
@@ -620,7 +622,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
620622 (first : _)
621623 -> do let matrixtext = parseMatrixText . encodeUtf8 $ T.tail first
622624 -- if sym is "foo", we look for matrix parameters
623− -- named "foo" or "foo[]" and call fromText on the
625+ -- named "foo" or "foo[]" and call parseQueryParam on the
624626 -- corresponding values
625627 parameters = filter looksLikeParam matrixtext
626628 values = mapMaybe (convert . snd) parameters
@@ -629,7 +631,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
629631 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
630632 looksLikeParam (name, _) = name == paramname || name == (paramname <> "[]")
631633 convert Nothing = Nothing
632− convert (Just v) = fromText v
634+ convert (Just v) = parseQueryParamMaybe v
633635
634636-- | If you use @'MatrixFlag' "published"@ in one of the endpoints for your API,
635637-- this automatically requires your server-side handler to be a function
servant/CHANGELOG.md+2−1
@@ -3,7 +3,8 @@ HEAD
33
44* Add `HttpVersion`, `IsSecure`, `RemoteHost` and `Vault` combinators
55* Fix safeLink, so Header is not in fact required.
6−* Added more instances for (:<|>)
6+* Add more instances for (:<|>)
7+* Use `http-api-data` instead of `Servant.Common.Text`
78
890.4.2
910-----
servant/servant.cabal+1−2
@@ -44,7 +44,6 @@ library
4444 Servant.API.ResponseHeaders
4545 Servant.API.Sub
4646 Servant.API.Vault
47− Servant.Common.Text
4847 Servant.Utils.Links
4948 build-depends:
5049 base >=4.7 && <5
@@ -53,6 +52,7 @@ library
5352 , bytestring == 0.10.*
5453 , bytestring-conversion == 0.3.*
5554 , case-insensitive >= 1.2
55+ , http-api-data >= 0.1 && < 0.2
5656 , http-media >= 0.4 && < 0.7
5757 , http-types == 0.8.*
5858 , text >= 1 && < 2
@@ -93,7 +93,6 @@ test-suite spec
9393 other-modules:
9494 Servant.API.ContentTypesSpec
9595 Servant.API.ResponseHeadersSpec
96− Servant.Common.TextSpec
9796 Servant.Utils.LinksSpec
9897 build-depends:
9998 base == 4.*
servant/src/Servant/API.hs+4−4
@@ -50,9 +50,9 @@ module Servant.API (
5050 module Servant.API.Raw,
5151 -- | Plugging in a wai 'Network.Wai.Application', serving directories
5252
53− -- * FromText and ToText
54− module Servant.Common.Text,
55− -- | Classes and instances for types that can be converted to and from @Text@
53+ -- * FromHttpApiData and ToHttpApiData
54+ module Web.HttpApiData,
55+ -- | Classes and instances for types that can be converted to and from HTTP API data.
5656
5757 -- * Utilities
5858 module Servant.Utils.Links,
@@ -88,7 +88,7 @@ import Servant.API.ResponseHeaders (AddHeader (addHeader),
8888 getHeadersHList, getResponse)
8989import Servant.API.Sub ((:>))
9090import Servant.API.Vault (Vault)
91−import Servant.Common.Text (FromText (..), ToText (..))
91+import Web.HttpApiData (FromHttpApiData (..), ToHttpApiData (..))
9292import Servant.Utils.Links (HasLink (..), IsElem, IsElem',
9393 URI (..), safeLink)
9494
servant/src/Servant/API/Header.hs−1
@@ -23,7 +23,6 @@ data Header (sym :: Symbol) a = Header a
2323
2424-- $setup
2525-- >>> import Servant.API
26−-- >>> import Servant.Common.Text
2726-- >>> import Data.Aeson
2827-- >>> import Data.Text
2928-- >>> data Book
servant/src/Servant/API/ReqBody.hs−1
@@ -16,7 +16,6 @@ data ReqBody (contentTypes :: [*]) a
1616
1717-- $setup
1818-- >>> import Servant.API
19−-- >>> import Servant.Common.Text
2019-- >>> import Data.Aeson
2120-- >>> import Data.Text
2221-- >>> data Book
servant/src/Servant/API/Sub.hs−1
@@ -20,7 +20,6 @@ infixr 9 :>
2020
2121-- $setup
2222-- >>> import Servant.API
23−-- >>> import Servant.Common.Text
2423-- >>> import Data.Aeson
2524-- >>> import Data.Text
2625-- >>> data World
servant/src/Servant/Common/Text.hsdeleted−149
@@ -1,149 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE OverloadedStrings #-}
4−{-# LANGUAGE TypeSynonymInstances #-}
5−module Servant.Common.Text
6− ( FromText(..)
7− , ToText(..)
8− ) where
9−
10−#if !MIN_VERSION_base(4,8,0)
11−import Control.Applicative ((<$>))
12−#endif
13−import Data.Int (Int16, Int32, Int64, Int8)
14−import Data.String.Conversions (cs)
15−import Data.Text (Text)
16−import Data.Text.Read (Reader, decimal, rational, signed)
17−import Data.Word (Word16, Word32, Word64, Word8
18−#if !MIN_VERSION_base(4,8,0)
19− , Word
20−#endif
21− )
22−
23−-- | For getting values from url captures and query string parameters
24−-- Instances should obey:
25−-- > fromText (toText a) == Just a
26−class FromText a where
27− fromText :: Text -> Maybe a
28−
29−-- | For putting values in paths and query string parameters
30−-- Instances should obey:
31−-- > fromText (toText a) == Just a
32−class ToText a where
33− toText :: a -> Text
34−
35−instance FromText Text where
36− fromText = Just
37−
38−instance ToText Text where
39− toText = id
40−
41−instance FromText String where
42− fromText = Just . cs
43−
44−instance ToText String where
45− toText = cs
46−
47−-- |
48−-- >>> fromText ("true"::Text) :: Maybe Bool
49−-- Just True
50−-- >>> fromText ("false"::Text) :: Maybe Bool
51−-- Just False
52−-- >>> fromText ("anything else"::Text) :: Maybe Bool
53−-- Nothing
54−instance FromText Bool where
55− fromText "true" = Just True
56− fromText "false" = Just False
57− fromText _ = Nothing
58−
59−-- |
60−-- >>> toText True
61−-- "true"
62−-- >>> toText False
63−-- "false"
64−instance ToText Bool where
65− toText True = "true"
66− toText False = "false"
67−
68−instance FromText Int where
69− fromText = runReader (signed decimal)
70−
71−instance ToText Int where
72− toText = cs . show
73−
74−instance FromText Int8 where
75− fromText = runReader (signed decimal)
76−
77−instance ToText Int8 where
78− toText = cs . show
79−
80−instance FromText Int16 where
81− fromText = runReader (signed decimal)
82−
83−instance ToText Int16 where
84− toText = cs . show
85−
86−instance FromText Int32 where
87− fromText = runReader (signed decimal)
88−
89−instance ToText Int32 where
90− toText = cs . show
91−
92−instance FromText Int64 where
93− fromText = runReader (signed decimal)
94−
95−instance ToText Int64 where
96− toText = cs . show
97−
98−instance FromText Word where
99− fromText = runReader decimal
100−
101−instance ToText Word where
102− toText = cs . show
103−
104−instance FromText Word8 where
105− fromText = runReader decimal
106−
107−instance ToText Word8 where
108− toText = cs . show
109−
110−instance FromText Word16 where
111− fromText = runReader decimal
112−
113−instance ToText Word16 where
114− toText = cs . show
115−
116−instance FromText Word32 where
117− fromText = runReader decimal
118−
119−instance ToText Word32 where
120− toText = cs . show
121−
122−instance FromText Word64 where
123− fromText = runReader decimal
124−
125−instance ToText Word64 where
126− toText = cs . show
127−
128−instance FromText Integer where
129− fromText = runReader (signed decimal)
130−
131−instance ToText Integer where
132− toText = cs . show
133−
134−instance FromText Double where
135− fromText x = fromRational <$> runReader rational x
136−
137−instance ToText Double where
138− toText = cs . show
139−
140−instance FromText Float where
141− -- Double is more practically accurate due to weird rounding when using
142− -- rational. We convert to double and then convert to Float.
143− fromText x = fromRational <$> runReader rational x
144−
145−instance ToText Float where
146− toText = cs . show
147−
148−runReader :: Reader a -> Text -> Maybe a
149−runReader reader t = either (const Nothing) (Just . fst) $ reader t
servant/src/Servant/Utils/Links.hs+11−11
@@ -113,7 +113,7 @@ import Network.URI ( URI(..), escapeURIString, isUnreserved )
113113import GHC.TypeLits ( KnownSymbol, symbolVal )
114114import GHC.Exts(Constraint)
115115
116−import Servant.Common.Text
116+import Web.HttpApiData
117117import Servant.API.Capture ( Capture )
118118import Servant.API.ReqBody ( ReqBody )
119119import Servant.API.QueryParam ( QueryParam, QueryParams, QueryFlag )
@@ -271,22 +271,22 @@ instance (KnownSymbol sym, HasLink sub) => HasLink (sym :> sub) where
271271
272272
273273-- QueryParam instances
274−instance (KnownSymbol sym, ToText v, HasLink sub)
274+instance (KnownSymbol sym, ToHttpApiData v, HasLink sub)
275275 => HasLink (QueryParam sym v :> sub) where
276276 type MkLink (QueryParam sym v :> sub) = Maybe v -> MkLink sub
277277 toLink _ l mv =
278278 toLink (Proxy :: Proxy sub) $
279− maybe id (addQueryParam . SingleParam k . toText) mv l
279+ maybe id (addQueryParam . SingleParam k . toQueryParam) mv l
280280 where
281281 k :: String
282282 k = symbolVal (Proxy :: Proxy sym)
283283
284−instance (KnownSymbol sym, ToText v, HasLink sub)
284+instance (KnownSymbol sym, ToHttpApiData v, HasLink sub)
285285 => HasLink (QueryParams sym v :> sub) where
286286 type MkLink (QueryParams sym v :> sub) = [v] -> MkLink sub
287287 toLink _ l =
288288 toLink (Proxy :: Proxy sub) .
289− foldl' (\l' v -> addQueryParam (ArrayElemParam k (toText v)) l') l
289+ foldl' (\l' v -> addQueryParam (ArrayElemParam k (toQueryParam v)) l') l
290290 where
291291 k = symbolVal (Proxy :: Proxy sym)
292292
@@ -301,21 +301,21 @@ instance (KnownSymbol sym, HasLink sub)
301301 k = symbolVal (Proxy :: Proxy sym)
302302
303303-- MatrixParam instances
304−instance (KnownSymbol sym, ToText v, HasLink sub)
304+instance (KnownSymbol sym, ToHttpApiData v, HasLink sub)
305305 => HasLink (MatrixParam sym v :> sub) where
306306 type MkLink (MatrixParam sym v :> sub) = Maybe v -> MkLink sub
307307 toLink _ l mv =
308308 toLink (Proxy :: Proxy sub) $
309− maybe id (addMatrixParam . SingleParam k . toText) mv l
309+ maybe id (addMatrixParam . SingleParam k . toQueryParam) mv l
310310 where
311311 k = symbolVal (Proxy :: Proxy sym)
312312
313−instance (KnownSymbol sym, ToText v, HasLink sub)
313+instance (KnownSymbol sym, ToHttpApiData v, HasLink sub)
314314 => HasLink (MatrixParams sym v :> sub) where
315315 type MkLink (MatrixParams sym v :> sub) = [v] -> MkLink sub
316316 toLink _ l =
317317 toLink (Proxy :: Proxy sub) .
318− foldl' (\l' v -> addMatrixParam (ArrayElemParam k (toText v)) l') l
318+ foldl' (\l' v -> addMatrixParam (ArrayElemParam k (toQueryParam v)) l') l
319319 where
320320 k = symbolVal (Proxy :: Proxy sym)
321321
@@ -334,12 +334,12 @@ instance HasLink sub => HasLink (ReqBody ct a :> sub) where
334334 type MkLink (ReqBody ct a :> sub) = MkLink sub
335335 toLink _ = toLink (Proxy :: Proxy sub)
336336
337−instance (ToText v, HasLink sub)
337+instance (ToHttpApiData v, HasLink sub)
338338 => HasLink (Capture sym v :> sub) where
339339 type MkLink (Capture sym v :> sub) = v -> MkLink sub
340340 toLink _ l v =
341341 toLink (Proxy :: Proxy sub) $
342− addSegment (escape . unpack $ toText v) l
342+ addSegment (escape . unpack $ toUrlPiece v) l
343343
344344instance HasLink sub => HasLink (Header sym a :> sub) where
345345 type MkLink (Header sym a :> sub) = MkLink sub
servant/test/Servant/Common/TextSpec.hsdeleted−74
@@ -1,74 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−module Servant.Common.TextSpec where
3−
4−import Data.Int (Int16, Int32, Int64, Int8)
5−import Data.Text (Text)
6−import Data.Word (Word16, Word32, Word64, Word8
7−#if !MIN_VERSION_base(4,8,0)
8− , Word
9−#endif
10− )
11−import Servant.Common.Text
12−import Test.Hspec
13−import Test.QuickCheck
14−import Test.QuickCheck.Instances ()
15−
16−spec :: Spec
17−spec = describe "Servant.Common.Text" $ do
18−
19− context "FromText and ToText laws" $ do
20−
21− it "holds for Text" $
22− property $ \x -> textLaw (x :: Text)
23−
24− it "holds for String" $
25− property $ \x -> textLaw (x :: String)
26−
27− it "holds for Bool" $
28− property $ \x -> textLaw (x :: Bool)
29−
30− it "holds for Int" $
31− property $ \x -> textLaw (x :: Int)
32−
33− it "holds for Int8" $
34− property $ \x -> textLaw (x :: Int8)
35−
36− it "holds for Int16" $
37− property $ \x -> textLaw (x :: Int16)
38−
39− it "holds for Int32" $
40− property $ \x -> textLaw (x :: Int32)
41−
42− it "holds for Int64" $
43− property $ \x -> textLaw (x :: Int64)
44−
45− it "holds for Word" $
46− property $ \x -> textLaw (x :: Word)
47−
48− it "holds for Word8" $
49− property $ \x -> textLaw (x :: Word8)
50−
51− it "holds for Word16" $
52− property $ \x -> textLaw (x :: Word16)
53−
54− it "holds for Word32" $
55− property $ \x -> textLaw (x :: Word32)
56−
57− it "holds for Word64" $
58− property $ \x -> textLaw (x :: Word64)
59−
60− it "holds for Integer" $
61− property $ \x -> textLaw (x :: Integer)
62−
63− -- The following two properties are only reasonably expected to hold up
64− -- to a certain precision.
65− --
66− -- http://en.wikipedia.org/wiki/Floating_point#Internal_representation
67− it "holds for Double" $
68− property $ \x -> textLaw (x :: Double)
69−
70− it "holds for Float" $
71− property $ \x -> textLaw (x :: Float)
72−
73−textLaw :: (FromText a, ToText a, Eq a) => a -> Bool
74−textLaw a = fromText (toText a) == Just a
stack-ghc-7.8.4.yaml+1
@@ -21,4 +21,5 @@ extra-deps:
2121- socket-io-1.3.3
2222- stm-delay-0.1.1.1
2323- control-monad-omega-0.3.1
24+- http-api-data-0.1.1.1
2425resolver: lts-2.22
stack.yaml+1−1
@@ -16,4 +16,4 @@ packages:
1616extra-deps:
1717- engine-io-wai-1.0.2
1818- control-monad-omega-0.3.1
19−resolver: nightly-2015-09-10
19+resolver: nightly-2015-10-08