@@ -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)
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+import Web.HttpApiData (FromHttpApiData)
63+import Web.HttpApiData.Internal (parseUrlPieceMaybe, parseHeaderMaybe, parseQueryParamMaybe)
63class HasServer layout where
65class HasServer layout where
64 type ServerT layout (m :: * -> *) :: *
66 type ServerT layout (m :: * -> *) :: *
@@ -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
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
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.
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.
104-- Example:
106-- Example:
@@ -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
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.
285−-- All it asks is for a 'FromText' instance.
287+-- All it asks is for a 'FromHttpApiData' instance.
287-- Example:
289-- Example:
289-- > newtype Referer = Referer Text
291-- > newtype Referer = Referer Text
290−-- > deriving (Eq, Show, FromText, ToText)
292+-- > deriving (Eq, Show, FromHttpApiData, ToText)
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
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
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)
@@ -451,7 +453,7 @@ instance
451-- hand you 'Nothing'.
453-- hand you 'Nothing'.
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.
456-- Example:
458-- Example:
@@ -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
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.
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.
493-- Example:
495-- Example:
@@ -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
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
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'.
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.
564-- Example:
566-- Example:
@@ -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
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.
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.
604-- Example:
606-- Example:
@@ -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
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
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