servant-0.11: A family of combinators for defining webservices APIs

Safe Haskell | None
Language | Haskell2010

Servant.Utils.Links

Contents

- Building and using safe links
- Adding custom types
  - Link accessors

Description

Type safe generation of internal links.

Given an API with a few endpoints:

    >>> :set -XDataKinds -XTypeFamilies -XTypeOperators
    >>> import Servant.API
    >>> import Servant.Utils.Links
    >>> import Data.Proxy
    >>>
    >>>
    >>>
    >>> type Hello = "hello" :> Get '[JSON] Int
    >>> type Bye   = "bye"   :> QueryParam "name" String :> Delete '[JSON] NoContent
    >>> type API   = Hello :<|> Bye
    >>> let api = Proxy :: Proxy API

It is possible to generate links that are guaranteed to be within `API` with `safeLink`. The first argument to `safeLink` is a type representing the API you would like to restrict links to. The second argument is the destination endpoint you would like the link to point to, this will need to end with a verb like GET or POST. Further arguments may be required depending on the type of the endpoint. If everything lines up you will get a `Link` out the other end.

You may omit `QueryParam`s and the like should you not want to provide them, but types which form part of the URL path like `Capture` must be included. The reason you may want to omit `QueryParam`s is that safeLink is a bit magical: if parameters are included that could take input it will return a function that accepts that input and generates a link. This is best shown with an example. Here, a link is generated with no parameters:

    >>> let hello = Proxy :: Proxy ("hello" :> Get '[JSON] Int)
    >>> toUrlPiece (safeLink api hello :: Link)
    "hello"

If the API has an endpoint with parameters then we can generate links with or without those:

    >>> let with = Proxy :: Proxy ("bye" :> QueryParam "name" String :> Delete '[JSON] NoContent)
    >>> toUrlPiece $ safeLink api with (Just "Hubert")
    "bye?name=Hubert"

    >>> let without = Proxy :: Proxy ("bye" :> Delete '[JSON] NoContent)
    >>> toUrlPiece $ safeLink api without
    "bye"

If you would like create a helper for generating links only within that API, you can partially apply safeLink if you specify a correct type signature like so:

    >>> :set -XConstraintKinds
    >>> :{
    >>> let apiLink :: (IsElem endpoint API, HasLink endpoint)
    >>> => Proxy endpoint -> MkLink endpoint
    >>> apiLink = safeLink api
    >>> :}

Attempting to construct a link to an endpoint that does not exist in api will result in a type error like this:

    >>> let bad_link = Proxy :: Proxy ("hello" :> Delete '[JSON] NoContent)
    >>> safeLink api bad_link
    ...
    ...Could not deduce...
    ...

This error is essentially saying that the type family couldn't find bad_link under api after trying the open (but empty) type family `IsElem'` as a last resort.

Synopsis

- module Servant.API.TypeLevel
- safeLink :: forall endpoint api. (IsElem endpoint api, HasLink endpoint) => Proxy api -> Proxy endpoint -> MkLink endpoint
- data URI :: * = URI {
  - uriScheme :: String
  - uriAuthority :: Maybe URIAuth
  - uriPath :: String
  - uriQuery :: String
  - uriFragment :: String
  }
- class HasLink endpoint where
  - type MkLink endpoint
- data Link
- linkURI :: Link -> URI
- linkURI' :: LinkArrayElementStyle -> Link -> URI
- data LinkArrayElementStyle
  - = LinkArrayElementBracket
  - | LinkArrayElementPlain
- data Param
  - = SingleParam String Text
  - | ArrayElemParam String Text
  - | FlagParam String
- linkSegments :: Link -> [String]
- linkQueryParams :: Link -> [Param]

# Documentation

module Servant.API.TypeLevel

# Building and using safe links

Note that `URI` is from the Network.URI module in the `network-uri` package.

safeLink Source #

Arguments

:: (IsElem endpoint api, HasLink endpoint) |
=> Proxy api | The whole API that this endpoint is a part of
-> Proxy endpoint | The API endpoint you would like to point to
-> MkLink endpoint |

Create a valid (by construction) relative URI with query params.

This function will only typecheck if `endpoint` is part of the API `api`

data URI :: * #

Represents a general universal resource identifier using its component parts.

For example, for the URI

      foo://anonymous@www.haskell.org:42/ghc?query#frag

the components are:

Constructors

URI |

Fields

- uriScheme :: String

      foo:

- uriAuthority :: Maybe URIAuth

      //anonymous@www.haskell.org:42

- uriPath :: String

      /ghc

- uriQuery :: String

      ?query

- uriFragment :: String

      #frag

Instances

Eq URI |
Methods (==) :: URI -> URI -> Bool # (/=) :: URI -> URI -> Bool #
Data URI |

Methods

gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> URI -> c URI #

gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c URI #

toConstr :: URI -> Constr #

dataTypeOf :: URI -> DataType #

dataCast1 :: Typeable (* -> *) t => (forall d. Data d => c (t d)) -> Maybe (c URI) #

dataCast2 :: Typeable (* -> * -> *) t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c URI) #

gmapT :: (forall b. Data b => b -> b) -> URI -> URI #

gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> URI -> r #

gmapQr :: (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> URI -> r #

gmapQ :: (forall d. Data d => d -> u) -> URI -> [u] #

gmapQi :: Int -> (forall d. Data d => d -> u) -> URI -> u #

gmapM :: Monad m => (forall d. Data d => d -> m d) -> URI -> m URI #

gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> URI -> m URI #

gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> URI -> m URI #

Ord URI |
Methods compare :: URI -> URI -> Ordering # (<) :: URI -> URI -> Bool # (<=) :: URI -> URI -> Bool # (>) :: URI -> URI -> Bool # (>=) :: URI -> URI -> Bool # max :: URI -> URI -> URI # min :: URI -> URI -> URI #
Show URI |
Methods showsPrec :: Int -> URI -> ShowS # show :: URI -> String # showList :: [URI] -> ShowS #
Generic URI |
Associated Types type Rep URI :: * -> * # Methods from :: URI -> Rep URI x # to :: Rep URI x -> URI #
NFData URI |
Methods rnf :: URI -> () #
type Rep URI |

type Rep URI = D1 (MetaData "URI" "Network.URI" "network-uri-2.6.1.0-7BN1tbB3iHQ2XgvmqLAYph" False) (C1 (MetaCons "URI" PrefixI True) ((:*:) ((:*:) (S1 (MetaSel (Just Symbol "uriScheme") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 String)) (S1 (MetaSel (Just Symbol "uriAuthority") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 (Maybe URIAuth)))) ((:*:) (S1 (MetaSel (Just Symbol "uriPath") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 String)) ((:*:) (S1 (MetaSel (Just Symbol "uriQuery") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 String)) (S1 (MetaSel (Just Symbol "uriFragment") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 String))))))

# Adding custom types

class HasLink endpoint where Source #

Construct a toLink for an endpoint.

Minimal complete definition

toLink

Associated Types

type MkLink endpoint Source #

Methods

toLink :: Proxy endpoint -> Link -> MkLink endpoint Source #

Instances

HasLink * Raw Source # |
Associated Types type MkLink Raw (endpoint :: Raw) :: * Source # Methods toLink :: Proxy Raw endpoint -> Link -> MkLink Raw endpoint Source #
HasLink k1 sub => HasLink * ((:>) k1 * (AuthProtect k tag) sub) Source # |
Associated Types type MkLink ((:>) k1 * (AuthProtect k tag) sub) (endpoint :: (:>) k1 * (AuthProtect k tag) sub) :: * Source # Methods toLink :: Proxy ((k1 :> *) (AuthProtect k tag) sub) endpoint -> Link -> MkLink ((k1 :> *) (AuthProtect k tag) sub) endpoint Source #
HasLink k sub => HasLink * ((:>) k * (BasicAuth realm a) sub) Source # |
Associated Types type MkLink ((:>) k * (BasicAuth realm a) sub) (endpoint :: (:>) k * (BasicAuth realm a) sub) :: * Source # Methods toLink :: Proxy ((k :> *) (BasicAuth realm a) sub) endpoint -> Link -> MkLink ((k :> *) (BasicAuth realm a) sub) endpoint Source #
HasLink k sub => HasLink * ((:>) k * RemoteHost sub) Source # |
Associated Types type MkLink ((:>) k * RemoteHost sub) (endpoint :: (:>) k * RemoteHost sub) :: * Source # Methods toLink :: Proxy ((k :> *) RemoteHost sub) endpoint -> Link -> MkLink ((k :> *) RemoteHost sub) endpoint Source #
HasLink k sub => HasLink * ((:>) k * (Header sym a) sub) Source # |
Associated Types type MkLink ((:>) k * (Header sym a) sub) (endpoint :: (:>) k * (Header sym a) sub) :: * Source # Methods toLink :: Proxy ((k :> *) (Header sym a) sub) endpoint -> Link -> MkLink ((k :> *) (Header sym a) sub) endpoint Source #
(ToHttpApiData v, HasLink k sub) => HasLink * ((:>) k * (CaptureAll * sym v) sub) Source # |
Associated Types type MkLink ((:>) k * (CaptureAll * sym v) sub) (endpoint :: (:>) k * (CaptureAll * sym v) sub) :: * Source # Methods toLink :: Proxy ((k :> *) (CaptureAll * sym v) sub) endpoint -> Link -> MkLink ((k :> *) (CaptureAll * sym v) sub) endpoint Source #
(ToHttpApiData v, HasLink k sub) => HasLink * ((:>) k * (Capture * sym v) sub) Source # |
Associated Types type MkLink ((:>) k * (Capture * sym v) sub) (endpoint :: (:>) k * (Capture * sym v) sub) :: * Source # Methods toLink :: Proxy ((k :> *) (Capture * sym v) sub) endpoint -> Link -> MkLink ((k :> *) (Capture * sym v) sub) endpoint Source #
HasLink k1 sub => HasLink * ((:>) k1 * (ReqBody k ct a) sub) Source # |
Associated Types type MkLink ((:>) k1 * (ReqBody k ct a) sub) (endpoint :: (:>) k1 * (ReqBody k ct a) sub) :: * Source # Methods toLink :: Proxy ((k1 :> *) (ReqBody k ct a) sub) endpoint -> Link -> MkLink ((k1 :> *) (ReqBody k ct a) sub) endpoint Source #
(KnownSymbol sym, HasLink k sub) => HasLink * ((:>) k * (QueryFlag sym) sub) Source # |
Associated Types type MkLink ((:>) k * (QueryFlag sym) sub) (endpoint :: (:>) k * (QueryFlag sym) sub) :: * Source # Methods toLink :: Proxy ((k :> *) (QueryFlag sym) sub) endpoint -> Link -> MkLink ((k :> *) (QueryFlag sym) sub) endpoint Source #
(KnownSymbol sym, ToHttpApiData v, HasLink k sub) => HasLink * ((:>) k * (QueryParams * sym v) sub) Source # |
Associated Types type MkLink ((:>) k * (QueryParams * sym v) sub) (endpoint :: (:>) k * (QueryParams * sym v) sub) :: * Source # Methods toLink :: Proxy ((k :> *) (QueryParams * sym v) sub) endpoint -> Link -> MkLink ((k :> *) (QueryParams * sym v) sub) endpoint Source #
(KnownSymbol sym, ToHttpApiData v, HasLink k sub) => HasLink * ((:>) k * (QueryParam * sym v) sub) Source # |
Associated Types type MkLink ((:>) k * (QueryParam * sym v) sub) (endpoint :: (:>) k * (QueryParam * sym v) sub) :: * Source # Methods toLink :: Proxy ((k :> *) (QueryParam * sym v) sub) endpoint -> Link -> MkLink ((k :> *) (QueryParam * sym v) sub) endpoint Source #
(KnownSymbol sym, HasLink k sub) => HasLink * ((:>) k Symbol sym sub) Source # |
Associated Types type MkLink ((:>) k Symbol sym sub) (endpoint :: (:>) k Symbol sym sub) :: * Source # Methods toLink :: Proxy ((k :> Symbol) sym sub) endpoint -> Link -> MkLink ((k :> Symbol) sym sub) endpoint Source #
HasLink * (Verb k k1 m s ct a) Source # |
Associated Types type MkLink (Verb k k1 m s ct a) (endpoint :: Verb k k1 m s ct a) :: * Source # Methods toLink :: Proxy (Verb k k1 m s ct a) endpoint -> Link -> MkLink (Verb k k1 m s ct a) endpoint Source #

data Link Source #

A safe link datatype. The only way of constructing a `Link` is using `safeLink`, which means any `Link` is guaranteed to be part of the mentioned API.

Instances

Show Link Source # |
Methods showsPrec :: Int -> Link -> ShowS # show :: Link -> String # showList :: [Link] -> ShowS #
ToHttpApiData Link Source # |
Methods toUrlPiece :: Link -> Text # toEncodedUrlPiece :: Link -> Builder # toHeader :: Link -> ByteString # toQueryParam :: Link -> Text #

linkURI :: Link -> URI Source #

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

    >>> type API = "foo/bar" :> Get '[JSON] Int
    >>> linkURI $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API)
    foo%2Fbar

linkURI' :: LinkArrayElementStyle -> Link -> URI Source #

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

data LinkArrayElementStyle Source #

How to encode array query elements.

Constructors

LinkArrayElementBracket

    foo[]=1&foo[]=2

LinkArrayElementPlain

    foo=1&foo=2

Instances

Bounded LinkArrayElementStyle Source # |
Methods minBound :: LinkArrayElementStyle # maxBound :: LinkArrayElementStyle #
Enum LinkArrayElementStyle Source # |

Methods

succ :: LinkArrayElementStyle -> LinkArrayElementStyle #

pred :: LinkArrayElementStyle -> LinkArrayElementStyle #

toEnum :: Int -> LinkArrayElementStyle #

fromEnum :: LinkArrayElementStyle -> Int #

enumFrom :: LinkArrayElementStyle -> [LinkArrayElementStyle] #

enumFromThen :: LinkArrayElementStyle -> LinkArrayElementStyle -> [LinkArrayElementStyle] #

enumFromTo :: LinkArrayElementStyle -> LinkArrayElementStyle -> [LinkArrayElementStyle] #

enumFromThenTo :: LinkArrayElementStyle -> LinkArrayElementStyle -> LinkArrayElementStyle -> [LinkArrayElementStyle] #

Eq LinkArrayElementStyle Source # |
Methods (==) :: LinkArrayElementStyle -> LinkArrayElementStyle -> Bool # (/=) :: LinkArrayElementStyle -> LinkArrayElementStyle -> Bool #
Ord LinkArrayElementStyle Source # |

Methods

compare :: LinkArrayElementStyle -> LinkArrayElementStyle -> Ordering #

(<) :: LinkArrayElementStyle -> LinkArrayElementStyle -> Bool #

(<=) :: LinkArrayElementStyle -> LinkArrayElementStyle -> Bool #

(>) :: LinkArrayElementStyle -> LinkArrayElementStyle -> Bool #

(>=) :: LinkArrayElementStyle -> LinkArrayElementStyle -> Bool #

max :: LinkArrayElementStyle -> LinkArrayElementStyle -> LinkArrayElementStyle #

min :: LinkArrayElementStyle -> LinkArrayElementStyle -> LinkArrayElementStyle #

Show LinkArrayElementStyle Source # |
Methods showsPrec :: Int -> LinkArrayElementStyle -> ShowS # show :: LinkArrayElementStyle -> String # showList :: [LinkArrayElementStyle] -> ShowS #

## Link accessors

data Param Source #

Query parameter.

Constructors

SingleParam String Text |
ArrayElemParam String Text |
FlagParam String |

Instances

Show Param Source # |
Methods showsPrec :: Int -> Param -> ShowS # show :: Param -> String # showList :: [Param] -> ShowS #

linkSegments :: Link -> [String] Source #

linkQueryParams :: Link -> [Param] Source #