on master help sign in
Use http-api-data package instead of ToText/FromText
40d2c68Nickolay Kudasov
servant-client/servant-client.cabal+1
@@ -34,6 +34,7 @@ library
34 , attoparsec
34 , attoparsec
35 , bytestring
35 , bytestring
36 , exceptions
36 , exceptions
37+ , http-api-data
37 , http-client
38 , http-client
38 , http-client-tls
39 , http-client-tls
39 , http-media
40 , http-media
servant-client/src/Servant/Client.hs+18−18
@@ -92,7 +92,7 @@ instance (HasClient a, HasClient b) => HasClient (a :<|> b) where
92-- of this value at the right place in the request path.
92-- of this value at the right place in the request path.
93--
93--
94-- You can control how values for this type are turned into
94-- 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.
96--
96--
97-- Example:
97-- Example:
98--
98--
@@ -105,7 +105,7 @@ instance (HasClient a, HasClient b) => HasClient (a :<|> b) where
105-- > getBook = client myApi host manager
105-- > getBook = client myApi host manager
106-- > where host = BaseUrl Http "localhost" 8080
106-- > where host = BaseUrl Http "localhost" 8080
107-- > -- then you can just use "getBook" to query that endpoint
107-- > -- 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)
109 => HasClient (Capture capture a :> sublayout) where
109 => HasClient (Capture capture a :> sublayout) where
110
110
111 type Client (Capture capture a :> sublayout) =
111 type Client (Capture capture a :> sublayout) =
@@ -117,7 +117,7 @@ instance (KnownSymbol capture, ToText a, HasClient sublayout)
117 baseurl
117 baseurl
118 manager
118 manager
119
119
120− where p = unpack (toText val)
120+ where p = unpack (toUrlPiece val)
121
121
122-- | If you have a 'Delete' endpoint in your API, the client
122-- | If you have a 'Delete' endpoint in your API, the client
123-- side querying function that is created when calling 'client'
123-- side querying function that is created when calling 'client'
@@ -205,12 +205,12 @@ instance
205-- That function will take care of encoding this argument as Text
205-- That function will take care of encoding this argument as Text
206-- in the request headers.
206-- in the request headers.
207--
207--
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.
209--
209--
210-- Example:
210-- Example:
211--
211--
212-- > newtype Referer = Referer { referrer :: Text }
212-- > newtype Referer = Referer { referrer :: Text }
213−-- > deriving (Eq, Show, Generic, FromText, ToText)
213+-- > deriving (Eq, Show, Generic, FromText, ToHttpApiData)
214-- >
214-- >
215-- > -- GET /view-my-referer
215-- > -- GET /view-my-referer
216-- > type MyApi = "view-my-referer" :> Header "Referer" Referer :> Get '[JSON] Referer
216-- > type MyApi = "view-my-referer" :> Header "Referer" Referer :> Get '[JSON] Referer
@@ -223,7 +223,7 @@ instance
223-- > where host = BaseUrl Http "localhost" 8080
223-- > where host = BaseUrl Http "localhost" 8080
224-- > -- then you can just use "viewRefer" to query that endpoint
224-- > -- then you can just use "viewRefer" to query that endpoint
225-- > -- specifying Nothing or e.g Just "http://haskell.org/" as arguments
225-- > -- 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)
227 => HasClient (Header sym a :> sublayout) where
227 => HasClient (Header sym a :> sublayout) where
228
228
229 type Client (Header sym a :> sublayout) =
229 type Client (Header sym a :> sublayout) =
@@ -368,7 +368,7 @@ instance
368-- of inserting a textual representation of this value in the query string.
368-- of inserting a textual representation of this value in the query string.
369--
369--
370-- You can control how values for your type are turned into
370-- 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.
372--
372--
373-- Example:
373-- Example:
374--
374--
@@ -383,7 +383,7 @@ instance
383-- > -- then you can just use "getBooksBy" to query that endpoint.
383-- > -- then you can just use "getBooksBy" to query that endpoint.
384-- > -- 'getBooksBy Nothing' for all books
384-- > -- 'getBooksBy Nothing' for all books
385-- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov
385-- > -- '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)
387 => HasClient (QueryParam sym a :> sublayout) where
387 => HasClient (QueryParam sym a :> sublayout) where
388
388
389 type Client (QueryParam sym a :> sublayout) =
389 type Client (QueryParam sym a :> sublayout) =
@@ -401,7 +401,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
401
401
402 where pname = cs pname'
402 where pname = cs pname'
403 pname' = symbolVal (Proxy :: Proxy sym)
403 pname' = symbolVal (Proxy :: Proxy sym)
404− mparamText = fmap toText mparam
404+ mparamText = fmap toQueryParam mparam
405
405
406-- | If you use a 'QueryParams' in one of your endpoints in your API,
406-- | If you use a 'QueryParams' in one of your endpoints in your API,
407-- the corresponding querying function will automatically take
407-- the corresponding querying function will automatically take
@@ -415,7 +415,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
415-- under the same query string parameter name.
415-- under the same query string parameter name.
416--
416--
417-- You can control how values for your type are turned into
417-- 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.
419--
419--
420-- Example:
420-- Example:
421--
421--
@@ -431,7 +431,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
431-- > -- 'getBooksBy []' for all books
431-- > -- 'getBooksBy []' for all books
432-- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
432-- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
433-- > -- to get all books by Asimov and Heinlein
433-- > -- to get all books by Asimov and Heinlein
434−instance (KnownSymbol sym, ToText a, HasClient sublayout)
434+instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout)
435 => HasClient (QueryParams sym a :> sublayout) where
435 => HasClient (QueryParams sym a :> sublayout) where
436
436
437 type Client (QueryParams sym a :> sublayout) =
437 type Client (QueryParams sym a :> sublayout) =
@@ -447,7 +447,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
447
447
448 where pname = cs pname'
448 where pname = cs pname'
449 pname' = symbolVal (Proxy :: Proxy sym)
449 pname' = symbolVal (Proxy :: Proxy sym)
450− paramlist' = map (Just . toText) paramlist
450+ paramlist' = map (Just . toQueryParam) paramlist
451
451
452-- | If you use a 'QueryFlag' in one of your endpoints in your API,
452-- | If you use a 'QueryFlag' in one of your endpoints in your API,
453-- the corresponding querying function will automatically take
453-- the corresponding querying function will automatically take
@@ -498,7 +498,7 @@ instance (KnownSymbol sym, HasClient sublayout)
498-- of inserting a textual representation of this value in the query string.
498-- of inserting a textual representation of this value in the query string.
499--
499--
500-- You can control how values for your type are turned into
500-- 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.
502--
502--
503-- Example:
503-- Example:
504--
504--
@@ -513,7 +513,7 @@ instance (KnownSymbol sym, HasClient sublayout)
513-- > -- then you can just use "getBooksBy" to query that endpoint.
513-- > -- then you can just use "getBooksBy" to query that endpoint.
514-- > -- 'getBooksBy Nothing' for all books
514-- > -- 'getBooksBy Nothing' for all books
515-- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov
515-- > -- '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)
517 => HasClient (MatrixParam sym a :> sublayout) where
517 => HasClient (MatrixParam sym a :> sublayout) where
518
518
519 type Client (MatrixParam sym a :> sublayout) =
519 type Client (MatrixParam sym a :> sublayout) =
@@ -529,7 +529,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
529 baseurl manager
529 baseurl manager
530
530
531 where pname = symbolVal (Proxy :: Proxy sym)
531 where pname = symbolVal (Proxy :: Proxy sym)
532− mparamText = fmap (cs . toText) mparam
532+ mparamText = fmap (cs . toQueryParam) mparam
533
533
534-- | If you use a 'MatrixParams' in one of your endpoints in your API,
534-- | If you use a 'MatrixParams' in one of your endpoints in your API,
535-- the corresponding querying function will automatically take an
535-- the corresponding querying function will automatically take an
@@ -543,7 +543,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
543-- same matrix string parameter name.
543-- same matrix string parameter name.
544--
544--
545-- You can control how values for your type are turned into text by
545-- 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.
547--
547--
548-- Example:
548-- Example:
549--
549--
@@ -559,7 +559,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
559-- > -- 'getBooksBy []' for all books
559-- > -- 'getBooksBy []' for all books
560-- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
560-- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
561-- > -- to get all books by Asimov and Heinlein
561-- > -- to get all books by Asimov and Heinlein
562−instance (KnownSymbol sym, ToText a, HasClient sublayout)
562+instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout)
563 => HasClient (MatrixParams sym a :> sublayout) where
563 => HasClient (MatrixParams sym a :> sublayout) where
564
564
565 type Client (MatrixParams sym a :> sublayout) =
565 type Client (MatrixParams sym a :> sublayout) =
@@ -575,7 +575,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
575
575
576 where pname = cs pname'
576 where pname = cs pname'
577 pname' = symbolVal (Proxy :: Proxy sym)
577 pname' = symbolVal (Proxy :: Proxy sym)
578− paramlist' = map (Just . toText) paramlist
578+ paramlist' = map (Just . toQueryParam) paramlist
579
579
580-- | If you use a 'MatrixFlag' in one of your endpoints in your API,
580-- | If you use a 'MatrixFlag' in one of your endpoints in your API,
581-- the corresponding querying function will automatically take an
581-- 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
26import Network.URI hiding (path)
26import Network.URI hiding (path)
27import Servant.API.ContentTypes
27import Servant.API.ContentTypes
28import Servant.Common.BaseUrl
28import Servant.Common.BaseUrl
29−import Servant.Common.Text
30
29
31import qualified Network.HTTP.Client as Client
30import qualified Network.HTTP.Client as Client
32
31
32+import Web.HttpApiData
33+
33data ServantError
34data ServantError
34 = FailureResponse
35 = FailureResponse
35 { responseStatus :: Status
36 { responseStatus :: Status
@@ -86,9 +87,9 @@ appendToQueryString pname pvalue req =
86 req { qs = qs req ++ [(pname, pvalue)]
87 req { qs = qs req ++ [(pname, pvalue)]
87 }
88 }
88
89
89−addHeader :: ToText a => String -> a -> Req -> Req
90+addHeader :: ToHttpApiData a => String -> a -> Req -> Req
90addHeader name val req = req { headers = headers req
91addHeader name val req = req { headers = headers req
91− ++ [(name, toText val)]
92+ ++ [(name, decodeUtf8 (toHeader val))]
92 }
93 }
93
94
94setRQBody :: ByteString -> MediaType -> Req -> Req
95setRQBody :: 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
107instance (KnownSymbol path, HasMock rest) => HasMock (path :> rest) where
107instance (KnownSymbol path, HasMock rest) => HasMock (path :> rest) where
108 mock _ = mock (Proxy :: Proxy rest)
108 mock _ = mock (Proxy :: Proxy rest)
109
109
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
111 mock _ = \_ -> mock (Proxy :: Proxy rest)
111 mock _ = \_ -> mock (Proxy :: Proxy rest)
112
112
113instance (AllCTUnrender ctypes a, HasMock rest) => HasMock (ReqBody ctypes a :> rest) where
113instance (AllCTUnrender ctypes a, HasMock rest) => HasMock (ReqBody ctypes a :> rest) where
@@ -125,29 +125,29 @@ instance HasMock rest => HasMock (Vault :> rest) where
125instance HasMock rest => HasMock (HttpVersion :> rest) where
125instance HasMock rest => HasMock (HttpVersion :> rest) where
126 mock _ = \_ -> mock (Proxy :: Proxy rest)
126 mock _ = \_ -> mock (Proxy :: Proxy rest)
127
127
128−instance (KnownSymbol s, FromText a, HasMock rest)
128+instance (KnownSymbol s, FromHttpApiData a, HasMock rest)
129 => HasMock (QueryParam s a :> rest) where
129 => HasMock (QueryParam s a :> rest) where
130 mock _ = \_ -> mock (Proxy :: Proxy rest)
130 mock _ = \_ -> mock (Proxy :: Proxy rest)
131
131
132−instance (KnownSymbol s, FromText a, HasMock rest)
132+instance (KnownSymbol s, FromHttpApiData a, HasMock rest)
133 => HasMock (QueryParams s a :> rest) where
133 => HasMock (QueryParams s a :> rest) where
134 mock _ = \_ -> mock (Proxy :: Proxy rest)
134 mock _ = \_ -> mock (Proxy :: Proxy rest)
135
135
136instance (KnownSymbol s, HasMock rest) => HasMock (QueryFlag s :> rest) where
136instance (KnownSymbol s, HasMock rest) => HasMock (QueryFlag s :> rest) where
137 mock _ = \_ -> mock (Proxy :: Proxy rest)
137 mock _ = \_ -> mock (Proxy :: Proxy rest)
138
138
139−instance (KnownSymbol s, FromText a, HasMock rest)
139+instance (KnownSymbol s, FromHttpApiData a, HasMock rest)
140 => HasMock (MatrixParam s a :> rest) where
140 => HasMock (MatrixParam s a :> rest) where
141 mock _ = \_ -> mock (Proxy :: Proxy rest)
141 mock _ = \_ -> mock (Proxy :: Proxy rest)
142
142
143−instance (KnownSymbol s, FromText a, HasMock rest)
143+instance (KnownSymbol s, FromHttpApiData a, HasMock rest)
144 => HasMock (MatrixParams s a :> rest) where
144 => HasMock (MatrixParams s a :> rest) where
145 mock _ = \_ -> mock (Proxy :: Proxy rest)
145 mock _ = \_ -> mock (Proxy :: Proxy rest)
146
146
147instance (KnownSymbol s, HasMock rest) => HasMock (MatrixFlag s :> rest) where
147instance (KnownSymbol s, HasMock rest) => HasMock (MatrixFlag s :> rest) where
148 mock _ = \_ -> mock (Proxy :: Proxy rest)
148 mock _ = \_ -> mock (Proxy :: Proxy rest)
149
149
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
151 mock _ = \_ -> mock (Proxy :: Proxy rest)
151 mock _ = \_ -> mock (Proxy :: Proxy rest)
152
152
153instance (Arbitrary a, AllCTRender ctypes a) => HasMock (Delete ctypes a) where
153instance (Arbitrary a, AllCTRender ctypes a) => HasMock (Delete ctypes a) where
servant-server/servant-server.cabal+1
@@ -48,6 +48,7 @@ library
48 , attoparsec >= 0.12 && < 0.14
48 , attoparsec >= 0.12 && < 0.14
49 , bytestring >= 0.10 && < 0.11
49 , bytestring >= 0.10 && < 0.11
50 , containers >= 0.5 && < 0.6
50 , containers >= 0.5 && < 0.6
51+ , http-api-data
51 , http-types >= 0.8 && < 0.9
52 , http-types >= 0.8 && < 0.9
52 , network-uri >= 2.6 && < 2.7
53 , network-uri >= 2.6 && < 2.7
53 , mtl >= 2 && < 3
54 , mtl >= 2 && < 3
servant-server/src/Servant.hs−3
@@ -5,8 +5,6 @@ module Servant (
5 module Servant.API,
5 module Servant.API,
6 -- | For implementing servers for servant APIs.
6 -- | For implementing servers for servant APIs.
7 module Servant.Server,
7 module Servant.Server,
8− -- | Using your types in request paths and query string parameters
9− module Servant.Common.Text,
10 -- | Utilities on top of the servant core
8 -- | Utilities on top of the servant core
11 module Servant.Utils.Links,
9 module Servant.Utils.Links,
12 module Servant.Utils.StaticFiles,
10 module Servant.Utils.StaticFiles,
@@ -16,7 +14,6 @@ module Servant (
16
14
17import Data.Proxy
15import Data.Proxy
18import Servant.API
16import Servant.API
19−import Servant.Common.Text
20import Servant.Server
17import Servant.Server
21import Servant.Utils.Links
18import Servant.Utils.Links
22import Servant.Utils.StaticFiles
19import Servant.Utils.StaticFiles
servant-server/src/Servant/Server/Internal.hs+25−23
@@ -53,13 +53,15 @@ import Servant.API.ContentTypes (AcceptHeader (..),
53 AllCTUnrender (..))
53 AllCTUnrender (..))
54import Servant.API.ResponseHeaders (Headers, getResponse, GetHeaders,
54import Servant.API.ResponseHeaders (Headers, getResponse, GetHeaders,
55 getHeaders)
55 getHeaders)
56−import Servant.Common.Text (FromText, fromText)
57
56
58import Servant.Server.Internal.PathInfo
57import Servant.Server.Internal.PathInfo
59import Servant.Server.Internal.Router
58import Servant.Server.Internal.Router
60import Servant.Server.Internal.RoutingApplication
59import Servant.Server.Internal.RoutingApplication
61import Servant.Server.Internal.ServantErr
60import Servant.Server.Internal.ServantErr
62
61
62+import Web.HttpApiData (FromHttpApiData)
63+import Web.HttpApiData.Internal (parseUrlPieceMaybe, parseHeaderMaybe, parseQueryParamMaybe)
64+
63class HasServer layout where
65class HasServer layout where
64 type ServerT layout (m :: * -> *) :: *
66 type ServerT layout (m :: * -> *) :: *
65
67
@@ -89,8 +91,8 @@ instance (HasServer a, HasServer b) => HasServer (a :<|> b) where
89 where pa = Proxy :: Proxy a
91 where pa = Proxy :: Proxy a
90 pb = Proxy :: Proxy b
92 pb = Proxy :: Proxy b
91
93
92−captured :: FromText a => proxy (Capture sym a) -> Text -> Maybe a
94+captured :: FromHttpApiData a => proxy (Capture sym a) -> Text -> Maybe a
93−captured _ = fromText
95+captured _ = parseUrlPieceMaybe
94
96
95-- | If you use 'Capture' in one of the endpoints for your API,
97-- | If you use 'Capture' in one of the endpoints for your API,
96-- this automatically requires your server-side handler to be a function
98-- this automatically requires your server-side handler to be a function
@@ -99,7 +101,7 @@ captured _ = fromText
99-- it into a value of the type you specify.
101-- it into a value of the type you specify.
100--
102--
101-- You can control how it'll be converted from 'Text' to your type
103-- 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.
103--
105--
104-- Example:
106-- Example:
105--
107--
@@ -109,7 +111,7 @@ captured _ = fromText
109-- > server = getBook
111-- > server = getBook
110-- > where getBook :: Text -> ExceptT ServantErr IO Book
112-- > where getBook :: Text -> ExceptT ServantErr IO Book
111-- > getBook isbn = ...
113-- > getBook isbn = ...
112−instance (KnownSymbol capture, FromText a, HasServer sublayout)
114+instance (KnownSymbol capture, FromHttpApiData a, HasServer sublayout)
113 => HasServer (Capture capture a :> sublayout) where
115 => HasServer (Capture capture a :> sublayout) where
114
116
115 type ServerT (Capture capture a :> sublayout) m =
117 type ServerT (Capture capture a :> sublayout) m =
@@ -282,12 +284,12 @@ instance
282-- This lets servant worry about extracting it from the request and turning
284-- This lets servant worry about extracting it from the request and turning
283-- it into a value of the type you specify.
285-- it into a value of the type you specify.
284--
286--
285−-- All it asks is for a 'FromText' instance.
287+-- All it asks is for a 'FromHttpApiData' instance.
286--
288--
287-- Example:
289-- Example:
288--
290--
289-- > newtype Referer = Referer Text
291-- > newtype Referer = Referer Text
290−-- > deriving (Eq, Show, FromText, ToText)
292+-- > deriving (Eq, Show, FromHttpApiData, ToText)
291-- >
293-- >
292-- > -- GET /view-my-referer
294-- > -- GET /view-my-referer
293-- > type MyApi = "view-my-referer" :> Header "Referer" Referer :> Get '[JSON] Referer
295-- > type MyApi = "view-my-referer" :> Header "Referer" Referer :> Get '[JSON] Referer
@@ -296,14 +298,14 @@ instance
296-- > server = viewReferer
298-- > server = viewReferer
297-- > where viewReferer :: Referer -> ExceptT ServantErr IO referer
299-- > where viewReferer :: Referer -> ExceptT ServantErr IO referer
298-- > viewReferer referer = return referer
300-- > viewReferer referer = return referer
299−instance (KnownSymbol sym, FromText a, HasServer sublayout)
301+instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
300 => HasServer (Header sym a :> sublayout) where
302 => HasServer (Header sym a :> sublayout) where
301
303
302 type ServerT (Header sym a :> sublayout) m =
304 type ServerT (Header sym a :> sublayout) m =
303 Maybe a -> ServerT sublayout m
305 Maybe a -> ServerT sublayout m
304
306
305 route Proxy subserver = WithRequest $ \ request ->
307 route Proxy subserver = WithRequest $ \ request ->
306− let mheader = fromText . decodeUtf8 =<< lookup str (requestHeaders request)
308+ let mheader = parseHeaderMaybe =<< lookup str (requestHeaders request)
307 in route (Proxy :: Proxy sublayout) (feedTo subserver mheader)
309 in route (Proxy :: Proxy sublayout) (feedTo subserver mheader)
308 where str = fromString $ symbolVal (Proxy :: Proxy sym)
310 where str = fromString $ symbolVal (Proxy :: Proxy sym)
309
311
@@ -451,7 +453,7 @@ instance
451-- hand you 'Nothing'.
453-- hand you 'Nothing'.
452--
454--
453-- You can control how it'll be converted from 'Text' to your type
455-- 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.
455--
457--
456-- Example:
458-- Example:
457--
459--
@@ -462,7 +464,7 @@ instance
462-- > where getBooksBy :: Maybe Text -> ExceptT ServantErr IO [Book]
464-- > where getBooksBy :: Maybe Text -> ExceptT ServantErr IO [Book]
463-- > getBooksBy Nothing = ...return all books...
465-- > getBooksBy Nothing = ...return all books...
464-- > getBooksBy (Just author) = ...return books by the given author...
466-- > 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)
466 => HasServer (QueryParam sym a :> sublayout) where
468 => HasServer (QueryParam sym a :> sublayout) where
467
469
468 type ServerT (QueryParam sym a :> sublayout) m =
470 type ServerT (QueryParam sym a :> sublayout) m =
@@ -474,7 +476,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
474 case lookup paramname querytext of
476 case lookup paramname querytext of
475 Nothing -> Nothing -- param absent from the query string
477 Nothing -> Nothing -- param absent from the query string
476 Just Nothing -> Nothing -- param present with no value -> Nothing
478 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
478 -- the right type
480 -- the right type
479 in route (Proxy :: Proxy sublayout) (feedTo subserver param)
481 in route (Proxy :: Proxy sublayout) (feedTo subserver param)
480 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
482 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
@@ -488,7 +490,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
488-- the type you specify.
490-- the type you specify.
489--
491--
490-- You can control how the individual values are converted from 'Text' to your type
492-- 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.
492--
494--
493-- Example:
495-- Example:
494--
496--
@@ -498,7 +500,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
498-- > server = getBooksBy
500-- > server = getBooksBy
499-- > where getBooksBy :: [Text] -> ExceptT ServantErr IO [Book]
501-- > where getBooksBy :: [Text] -> ExceptT ServantErr IO [Book]
500-- > getBooksBy authors = ...return all books by these authors...
502-- > getBooksBy authors = ...return all books by these authors...
501−instance (KnownSymbol sym, FromText a, HasServer sublayout)
503+instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
502 => HasServer (QueryParams sym a :> sublayout) where
504 => HasServer (QueryParams sym a :> sublayout) where
503
505
504 type ServerT (QueryParams sym a :> sublayout) m =
506 type ServerT (QueryParams sym a :> sublayout) m =
@@ -507,7 +509,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
507 route Proxy subserver = WithRequest $ \ request ->
509 route Proxy subserver = WithRequest $ \ request ->
508 let querytext = parseQueryText $ rawQueryString request
510 let querytext = parseQueryText $ rawQueryString request
509 -- if sym is "foo", we look for query string parameters
511 -- 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
511 -- corresponding values
513 -- corresponding values
512 parameters = filter looksLikeParam querytext
514 parameters = filter looksLikeParam querytext
513 values = mapMaybe (convert . snd) parameters
515 values = mapMaybe (convert . snd) parameters
@@ -515,7 +517,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
515 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
517 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
516 looksLikeParam (name, _) = name == paramname || name == (paramname <> "[]")
518 looksLikeParam (name, _) = name == paramname || name == (paramname <> "[]")
517 convert Nothing = Nothing
519 convert Nothing = Nothing
518− convert (Just v) = fromText v
520+ convert (Just v) = parseQueryParamMaybe v
519
521
520-- | If you use @'QueryFlag' "published"@ in one of the endpoints for your API,
522-- | If you use @'QueryFlag' "published"@ in one of the endpoints for your API,
521-- this automatically requires your server-side handler to be a function
523-- this automatically requires your server-side handler to be a function
@@ -559,7 +561,7 @@ parseMatrixText = parseQueryText
559-- hand you 'Nothing'.
561-- hand you 'Nothing'.
560--
562--
561-- You can control how it'll be converted from 'Text' to your type
563-- 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.
563--
565--
564-- Example:
566-- Example:
565--
567--
@@ -570,7 +572,7 @@ parseMatrixText = parseQueryText
570-- > where getBooksBy :: Maybe Text -> ExceptT ServantErr IO [Book]
572-- > where getBooksBy :: Maybe Text -> ExceptT ServantErr IO [Book]
571-- > getBooksBy Nothing = ...return all books...
573-- > getBooksBy Nothing = ...return all books...
572-- > getBooksBy (Just author) = ...return books by the given author...
574-- > 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)
574 => HasServer (MatrixParam sym a :> sublayout) where
576 => HasServer (MatrixParam sym a :> sublayout) where
575
577
576 type ServerT (MatrixParam sym a :> sublayout) m =
578 type ServerT (MatrixParam sym a :> sublayout) m =
@@ -583,7 +585,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
583 param = case lookup paramname querytext of
585 param = case lookup paramname querytext of
584 Nothing -> Nothing -- param absent from the query string
586 Nothing -> Nothing -- param absent from the query string
585 Just Nothing -> Nothing -- param present with no value -> Nothing
587 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
587 -- the right type
589 -- the right type
588 route (Proxy :: Proxy sublayout) (feedTo subserver param)
590 route (Proxy :: Proxy sublayout) (feedTo subserver param)
589 _ -> route (Proxy :: Proxy sublayout) (feedTo subserver Nothing)
591 _ -> route (Proxy :: Proxy sublayout) (feedTo subserver Nothing)
@@ -599,7 +601,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
599-- the type you specify.
601-- the type you specify.
600--
602--
601-- You can control how the individual values are converted from 'Text' to your type
603-- 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.
603--
605--
604-- Example:
606-- Example:
605--
607--
@@ -609,7 +611,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
609-- > server = getBooksBy
611-- > server = getBooksBy
610-- > where getBooksBy :: [Text] -> ExceptT ServantErr IO [Book]
612-- > where getBooksBy :: [Text] -> ExceptT ServantErr IO [Book]
611-- > getBooksBy authors = ...return all books by these authors...
613-- > getBooksBy authors = ...return all books by these authors...
612−instance (KnownSymbol sym, FromText a, HasServer sublayout)
614+instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
613 => HasServer (MatrixParams sym a :> sublayout) where
615 => HasServer (MatrixParams sym a :> sublayout) where
614
616
615 type ServerT (MatrixParams sym a :> sublayout) m =
617 type ServerT (MatrixParams sym a :> sublayout) m =
@@ -620,7 +622,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
620 (first : _)
622 (first : _)
621 -> do let matrixtext = parseMatrixText . encodeUtf8 $ T.tail first
623 -> do let matrixtext = parseMatrixText . encodeUtf8 $ T.tail first
622 -- if sym is "foo", we look for matrix parameters
624 -- 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
624 -- corresponding values
626 -- corresponding values
625 parameters = filter looksLikeParam matrixtext
627 parameters = filter looksLikeParam matrixtext
626 values = mapMaybe (convert . snd) parameters
628 values = mapMaybe (convert . snd) parameters
@@ -629,7 +631,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
629 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
631 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
630 looksLikeParam (name, _) = name == paramname || name == (paramname <> "[]")
632 looksLikeParam (name, _) = name == paramname || name == (paramname <> "[]")
631 convert Nothing = Nothing
633 convert Nothing = Nothing
632− convert (Just v) = fromText v
634+ convert (Just v) = parseQueryParamMaybe v
633
635
634-- | If you use @'MatrixFlag' "published"@ in one of the endpoints for your API,
636-- | If you use @'MatrixFlag' "published"@ in one of the endpoints for your API,
635-- this automatically requires your server-side handler to be a function
637-- this automatically requires your server-side handler to be a function
servant/servant.cabal+1−1
@@ -52,6 +52,7 @@ library
52 , bytestring == 0.10.*
52 , bytestring == 0.10.*
53 , bytestring-conversion == 0.3.*
53 , bytestring-conversion == 0.3.*
54 , case-insensitive >= 1.2
54 , case-insensitive >= 1.2
55+ , http-api-data
55 , http-media >= 0.4 && < 0.7
56 , http-media >= 0.4 && < 0.7
56 , http-types == 0.8.*
57 , http-types == 0.8.*
57 , text >= 1 && < 2
58 , text >= 1 && < 2
@@ -92,7 +93,6 @@ test-suite spec
92 other-modules:
93 other-modules:
93 Servant.API.ContentTypesSpec
94 Servant.API.ContentTypesSpec
94 Servant.API.ResponseHeadersSpec
95 Servant.API.ResponseHeadersSpec
95− Servant.Common.TextSpec
96 Servant.Utils.LinksSpec
96 Servant.Utils.LinksSpec
97 build-depends:
97 build-depends:
98 base == 4.*
98 base == 4.*
servant/src/Servant/API.hs+4−4
@@ -50,9 +50,9 @@ module Servant.API (
50 module Servant.API.Raw,
50 module Servant.API.Raw,
51 -- | Plugging in a wai 'Network.Wai.Application', serving directories
51 -- | Plugging in a wai 'Network.Wai.Application', serving directories
52
52
53− -- * FromText and ToText
53+ -- * FromHttpApiData and ToHttpApiData
54− module Servant.Common.Text,
54+ module Web.HttpApiData,
55− -- | Classes and instances for types that can be converted to and from @Text@
55+ -- | Classes and instances for types that can be converted to and from HTTP API data.
56
56
57 -- * Utilities
57 -- * Utilities
58 module Servant.Utils.Links,
58 module Servant.Utils.Links,
@@ -88,7 +88,7 @@ import Servant.API.ResponseHeaders (AddHeader (addHeader),
88 getHeadersHList, getResponse)
88 getHeadersHList, getResponse)
89import Servant.API.Sub ((:>))
89import Servant.API.Sub ((:>))
90import Servant.API.Vault (Vault)
90import Servant.API.Vault (Vault)
91−import Servant.Common.Text (FromText (..), ToText (..))
91+import Web.HttpApiData (FromHttpApiData (..), ToHttpApiData (..))
92import Servant.Utils.Links (HasLink (..), IsElem, IsElem',
92import Servant.Utils.Links (HasLink (..), IsElem, IsElem',
93 URI (..), safeLink)
93 URI (..), safeLink)
94
94
servant/src/Servant/API/Header.hs−1
@@ -23,7 +23,6 @@ data Header (sym :: Symbol) a = Header a
23
23
24-- $setup
24-- $setup
25-- >>> import Servant.API
25-- >>> import Servant.API
26−-- >>> import Servant.Common.Text
27-- >>> import Data.Aeson
26-- >>> import Data.Aeson
28-- >>> import Data.Text
27-- >>> import Data.Text
29-- >>> data Book
28-- >>> data Book
servant/src/Servant/API/ReqBody.hs−1
@@ -16,7 +16,6 @@ data ReqBody (contentTypes :: [*]) a
16
16
17-- $setup
17-- $setup
18-- >>> import Servant.API
18-- >>> import Servant.API
19−-- >>> import Servant.Common.Text
20-- >>> import Data.Aeson
19-- >>> import Data.Aeson
21-- >>> import Data.Text
20-- >>> import Data.Text
22-- >>> data Book
21-- >>> data Book
servant/src/Servant/API/Sub.hs−1
@@ -20,7 +20,6 @@ infixr 9 :>
20
20
21-- $setup
21-- $setup
22-- >>> import Servant.API
22-- >>> import Servant.API
23−-- >>> import Servant.Common.Text
24-- >>> import Data.Aeson
23-- >>> import Data.Aeson
25-- >>> import Data.Text
24-- >>> import Data.Text
26-- >>> data World
25-- >>> data World
servant/src/Servant/Utils/Links.hs+11−11
@@ -113,7 +113,7 @@ import Network.URI ( URI(..), escapeURIString, isUnreserved )
113import GHC.TypeLits ( KnownSymbol, symbolVal )
113import GHC.TypeLits ( KnownSymbol, symbolVal )
114import GHC.Exts(Constraint)
114import GHC.Exts(Constraint)
115
115
116−import Servant.Common.Text
116+import Web.HttpApiData
117import Servant.API.Capture ( Capture )
117import Servant.API.Capture ( Capture )
118import Servant.API.ReqBody ( ReqBody )
118import Servant.API.ReqBody ( ReqBody )
119import Servant.API.QueryParam ( QueryParam, QueryParams, QueryFlag )
119import Servant.API.QueryParam ( QueryParam, QueryParams, QueryFlag )
@@ -271,22 +271,22 @@ instance (KnownSymbol sym, HasLink sub) => HasLink (sym :> sub) where
271
271
272
272
273-- QueryParam instances
273-- QueryParam instances
274−instance (KnownSymbol sym, ToText v, HasLink sub)
274+instance (KnownSymbol sym, ToHttpApiData v, HasLink sub)
275 => HasLink (QueryParam sym v :> sub) where
275 => HasLink (QueryParam sym v :> sub) where
276 type MkLink (QueryParam sym v :> sub) = Maybe v -> MkLink sub
276 type MkLink (QueryParam sym v :> sub) = Maybe v -> MkLink sub
277 toLink _ l mv =
277 toLink _ l mv =
278 toLink (Proxy :: Proxy sub) $
278 toLink (Proxy :: Proxy sub) $
279− maybe id (addQueryParam . SingleParam k . toText) mv l
279+ maybe id (addQueryParam . SingleParam k . toQueryParam) mv l
280 where
280 where
281 k :: String
281 k :: String
282 k = symbolVal (Proxy :: Proxy sym)
282 k = symbolVal (Proxy :: Proxy sym)
283
283
284−instance (KnownSymbol sym, ToText v, HasLink sub)
284+instance (KnownSymbol sym, ToHttpApiData v, HasLink sub)
285 => HasLink (QueryParams sym v :> sub) where
285 => HasLink (QueryParams sym v :> sub) where
286 type MkLink (QueryParams sym v :> sub) = [v] -> MkLink sub
286 type MkLink (QueryParams sym v :> sub) = [v] -> MkLink sub
287 toLink _ l =
287 toLink _ l =
288 toLink (Proxy :: Proxy sub) .
288 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
290 where
290 where
291 k = symbolVal (Proxy :: Proxy sym)
291 k = symbolVal (Proxy :: Proxy sym)
292
292
@@ -301,21 +301,21 @@ instance (KnownSymbol sym, HasLink sub)
301 k = symbolVal (Proxy :: Proxy sym)
301 k = symbolVal (Proxy :: Proxy sym)
302
302
303-- MatrixParam instances
303-- MatrixParam instances
304−instance (KnownSymbol sym, ToText v, HasLink sub)
304+instance (KnownSymbol sym, ToHttpApiData v, HasLink sub)
305 => HasLink (MatrixParam sym v :> sub) where
305 => HasLink (MatrixParam sym v :> sub) where
306 type MkLink (MatrixParam sym v :> sub) = Maybe v -> MkLink sub
306 type MkLink (MatrixParam sym v :> sub) = Maybe v -> MkLink sub
307 toLink _ l mv =
307 toLink _ l mv =
308 toLink (Proxy :: Proxy sub) $
308 toLink (Proxy :: Proxy sub) $
309− maybe id (addMatrixParam . SingleParam k . toText) mv l
309+ maybe id (addMatrixParam . SingleParam k . toQueryParam) mv l
310 where
310 where
311 k = symbolVal (Proxy :: Proxy sym)
311 k = symbolVal (Proxy :: Proxy sym)
312
312
313−instance (KnownSymbol sym, ToText v, HasLink sub)
313+instance (KnownSymbol sym, ToHttpApiData v, HasLink sub)
314 => HasLink (MatrixParams sym v :> sub) where
314 => HasLink (MatrixParams sym v :> sub) where
315 type MkLink (MatrixParams sym v :> sub) = [v] -> MkLink sub
315 type MkLink (MatrixParams sym v :> sub) = [v] -> MkLink sub
316 toLink _ l =
316 toLink _ l =
317 toLink (Proxy :: Proxy sub) .
317 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
319 where
319 where
320 k = symbolVal (Proxy :: Proxy sym)
320 k = symbolVal (Proxy :: Proxy sym)
321
321
@@ -334,12 +334,12 @@ instance HasLink sub => HasLink (ReqBody ct a :> sub) where
334 type MkLink (ReqBody ct a :> sub) = MkLink sub
334 type MkLink (ReqBody ct a :> sub) = MkLink sub
335 toLink _ = toLink (Proxy :: Proxy sub)
335 toLink _ = toLink (Proxy :: Proxy sub)
336
336
337−instance (ToText v, HasLink sub)
337+instance (ToHttpApiData v, HasLink sub)
338 => HasLink (Capture sym v :> sub) where
338 => HasLink (Capture sym v :> sub) where
339 type MkLink (Capture sym v :> sub) = v -> MkLink sub
339 type MkLink (Capture sym v :> sub) = v -> MkLink sub
340 toLink _ l v =
340 toLink _ l v =
341 toLink (Proxy :: Proxy sub) $
341 toLink (Proxy :: Proxy sub) $
342− addSegment (escape . unpack $ toText v) l
342+ addSegment (escape . unpack $ toUrlPiece v) l
343
343
344instance HasLink sub => HasLink (Header sym a :> sub) where
344instance HasLink sub => HasLink (Header sym a :> sub) where
345 type MkLink (Header sym a :> sub) = MkLink sub
345 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:
21- socket-io-1.3.3
21- socket-io-1.3.3
22- stm-delay-0.1.1.1
22- stm-delay-0.1.1.1
23- control-monad-omega-0.3.1
23- control-monad-omega-0.3.1
24+- http-api-data-0.1.0
24resolver: lts-2.22
25resolver: lts-2.22
stack.yaml+1
@@ -16,4 +16,5 @@ packages:
16extra-deps:
16extra-deps:
17- engine-io-wai-1.0.2
17- engine-io-wai-1.0.2
18- control-monad-omega-0.3.1
18- control-monad-omega-0.3.1
19+- http-api-data-0.1.0
19resolver: nightly-2015-09-10
20resolver: nightly-2015-09-10