Merge pull request #745 from phadej/issue-715
Resolve #715: Add linkUri'
This commit is contained in:
commit
2cfcff4c7f
1 changed files with 57 additions and 13 deletions
|
@ -87,8 +87,14 @@ module Servant.Utils.Links (
|
||||||
, URI(..)
|
, URI(..)
|
||||||
-- * Adding custom types
|
-- * Adding custom types
|
||||||
, HasLink(..)
|
, HasLink(..)
|
||||||
, linkURI
|
|
||||||
, Link
|
, Link
|
||||||
|
, linkURI
|
||||||
|
, linkURI'
|
||||||
|
, LinkArrayElementStyle (..)
|
||||||
|
-- ** Link accessors
|
||||||
|
, Param (..)
|
||||||
|
, linkSegments
|
||||||
|
, linkQueryParams
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.List
|
import Data.List
|
||||||
|
@ -118,21 +124,24 @@ import Servant.API.Experimental.Auth ( AuthProtect )
|
||||||
-- The only way of constructing a 'Link' is using 'safeLink', which means any
|
-- The only way of constructing a 'Link' is using 'safeLink', which means any
|
||||||
-- 'Link' is guaranteed to be part of the mentioned API.
|
-- 'Link' is guaranteed to be part of the mentioned API.
|
||||||
data Link = Link
|
data Link = Link
|
||||||
{ _segments :: [String] -- ^ Segments of "foo/bar" would be ["foo", "bar"]
|
{ _segments :: [String] -- ^ Segments of "foo/bar" would be ["foo", "bar"]
|
||||||
, _queryParams :: [Param Query]
|
, _queryParams :: [Param]
|
||||||
} deriving Show
|
} deriving Show
|
||||||
|
|
||||||
|
linkSegments :: Link -> [String]
|
||||||
|
linkSegments = _segments
|
||||||
|
|
||||||
|
linkQueryParams :: Link -> [Param]
|
||||||
|
linkQueryParams = _queryParams
|
||||||
|
|
||||||
instance ToHttpApiData Link where
|
instance ToHttpApiData Link where
|
||||||
toHeader = TE.encodeUtf8 . toUrlPiece
|
toHeader = TE.encodeUtf8 . toUrlPiece
|
||||||
toUrlPiece l =
|
toUrlPiece l =
|
||||||
let uri = linkURI l
|
let uri = linkURI l
|
||||||
in Text.pack $ uriPath uri ++ uriQuery uri
|
in Text.pack $ uriPath uri ++ uriQuery uri
|
||||||
|
|
||||||
-- Phantom types for Param
|
-- | Query parameter.
|
||||||
data Query
|
data Param
|
||||||
|
|
||||||
-- | Query param
|
|
||||||
data Param a
|
|
||||||
= SingleParam String Text.Text
|
= SingleParam String Text.Text
|
||||||
| ArrayElemParam String Text.Text
|
| ArrayElemParam String Text.Text
|
||||||
| FlagParam String
|
| FlagParam String
|
||||||
|
@ -141,27 +150,59 @@ data Param a
|
||||||
addSegment :: String -> Link -> Link
|
addSegment :: String -> Link -> Link
|
||||||
addSegment seg l = l { _segments = _segments l <> [seg] }
|
addSegment seg l = l { _segments = _segments l <> [seg] }
|
||||||
|
|
||||||
addQueryParam :: Param Query -> Link -> Link
|
addQueryParam :: Param -> Link -> Link
|
||||||
addQueryParam qp l =
|
addQueryParam qp l =
|
||||||
l { _queryParams = _queryParams l <> [qp] }
|
l { _queryParams = _queryParams l <> [qp] }
|
||||||
|
|
||||||
|
-- | Transform 'Link' into 'URI'.
|
||||||
|
--
|
||||||
|
-- >>> type API = "something" :> Get '[JSON] Int
|
||||||
|
-- >>> linkURI $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API)
|
||||||
|
-- something
|
||||||
|
--
|
||||||
|
-- >>> type API = "sum" :> QueryParams "x" Int :> Get '[JSON] Int
|
||||||
|
-- >>> linkURI $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API) [1, 2, 3]
|
||||||
|
-- sum?x[]=1&x[]=2&x[]=3
|
||||||
|
--
|
||||||
linkURI :: Link -> URI
|
linkURI :: Link -> URI
|
||||||
linkURI (Link segments q_params) =
|
linkURI = linkURI' LinkArrayElementBracket
|
||||||
|
|
||||||
|
-- | How to encode array query elements.
|
||||||
|
data LinkArrayElementStyle
|
||||||
|
= LinkArrayElementBracket -- ^ @foo[]=1&foo[]=2@
|
||||||
|
| LinkArrayElementPlain -- ^ @foo=1&foo=2@
|
||||||
|
deriving (Eq, Ord, Show, Enum, Bounded)
|
||||||
|
|
||||||
|
-- | Configurable 'linkURI'.
|
||||||
|
--
|
||||||
|
-- >>> type API = "sum" :> QueryParams "x" Int :> Get '[JSON] Int
|
||||||
|
-- >>> linkURI' LinkArrayElementBracket $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API) [1, 2, 3]
|
||||||
|
-- sum?x[]=1&x[]=2&x[]=3
|
||||||
|
--
|
||||||
|
-- >>> linkURI' LinkArrayElementPlain $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API) [1, 2, 3]
|
||||||
|
-- sum?x=1&x=2&x=3
|
||||||
|
--
|
||||||
|
linkURI' :: LinkArrayElementStyle -> Link -> URI
|
||||||
|
linkURI' addBrackets (Link segments q_params) =
|
||||||
URI mempty -- No scheme (relative)
|
URI mempty -- No scheme (relative)
|
||||||
Nothing -- Or authority (relative)
|
Nothing -- Or authority (relative)
|
||||||
(intercalate "/" segments)
|
(intercalate "/" segments)
|
||||||
(makeQueries q_params) mempty
|
(makeQueries q_params) mempty
|
||||||
where
|
where
|
||||||
makeQueries :: [Param Query] -> String
|
makeQueries :: [Param] -> String
|
||||||
makeQueries [] = ""
|
makeQueries [] = ""
|
||||||
makeQueries xs =
|
makeQueries xs =
|
||||||
"?" <> intercalate "&" (fmap makeQuery xs)
|
"?" <> intercalate "&" (fmap makeQuery xs)
|
||||||
|
|
||||||
makeQuery :: Param Query -> String
|
makeQuery :: Param -> String
|
||||||
makeQuery (ArrayElemParam k v) = escape k <> "[]=" <> escape (Text.unpack v)
|
makeQuery (ArrayElemParam k v) = escape k <> style <> escape (Text.unpack v)
|
||||||
makeQuery (SingleParam k v) = escape k <> "=" <> escape (Text.unpack v)
|
makeQuery (SingleParam k v) = escape k <> "=" <> escape (Text.unpack v)
|
||||||
makeQuery (FlagParam k) = escape k
|
makeQuery (FlagParam k) = escape k
|
||||||
|
|
||||||
|
style = case addBrackets of
|
||||||
|
LinkArrayElementBracket -> "[]="
|
||||||
|
LinkArrayElementPlain -> "="
|
||||||
|
|
||||||
escape :: String -> String
|
escape :: String -> String
|
||||||
escape = escapeURIString isUnreserved
|
escape = escapeURIString isUnreserved
|
||||||
|
|
||||||
|
@ -265,3 +306,6 @@ instance HasLink Raw where
|
||||||
instance HasLink sub => HasLink (AuthProtect tag :> sub) where
|
instance HasLink sub => HasLink (AuthProtect tag :> sub) where
|
||||||
type MkLink (AuthProtect tag :> sub) = MkLink sub
|
type MkLink (AuthProtect tag :> sub) = MkLink sub
|
||||||
toLink _ = toLink (Proxy :: Proxy sub)
|
toLink _ = toLink (Proxy :: Proxy sub)
|
||||||
|
|
||||||
|
-- $setup
|
||||||
|
-- >>> import Servant.API
|
||||||
|
|
Loading…
Reference in a new issue