Safe Haskell | Safe-Inferred |
---|---|
Language | GHC2021 |
Module for common re-exports
Synopsis
- newtype Server (m :: Type -> Type) = Server {}
- data Api a
- newtype Path = Path {}
- data PathItem
- data Route (m :: Type -> Type) = Route {}
- data Json
- data AnyMedia
- data OctetStream
- data FormUrlEncoded
- class ToServer a where
- class MonadIO (MonadOf a) => ToRoute a where
- toRouteInfo :: RouteInfo -> RouteInfo
- toRouteFun :: a -> ServerFun (MonadOf a)
- data MediaType
- class ToMediaType (a :: k) where
- class ToMediaType ty => ToRespBody (ty :: k) b where
- toRespBody :: b -> ByteString
- class IsResp a where
- type RespBody a
- type RespError a
- type RespMedia a
- ok :: RespBody a -> a
- bad :: Status -> RespError a -> a
- noContent :: Status -> a
- addHeaders :: ResponseHeaders -> a -> a
- getHeaders :: a -> ResponseHeaders
- setStatus :: Status -> a -> a
- getRespBody :: a -> Maybe (RespBody a)
- getRespError :: a -> Maybe (RespError a)
- getStatus :: a -> Status
- setMedia :: MediaType -> a -> a
- getMedia :: MediaType
- toResponse :: a -> Response
- badReq :: IsResp a => RespError a -> a
- internalServerError :: IsResp a => RespError a -> a
- notImplemented :: IsResp a => RespError a -> a
- redirect :: IsResp a => Text -> a
- setHeader :: (IsResp a, ToHttpApiData h) => HeaderName -> h -> a -> a
- setCookie :: (ToForm cookie, IsResp resp) => SetCookie cookie -> resp -> resp
- data SetCookie a = SetCookie {}
- defCookie :: a -> SetCookie a
- newtype Send (method :: k) (m :: k1 -> Type) (a :: k1) = Send {
- unSend :: m a
- class IsMethod (a :: k) where
- data GET
- data POST
- data PUT
- data DELETE
- data PATCH
- data OPTIONS
- data HEAD
- data TRACE
- type family UrlOf a where ...
- class ToUrl a where
- data Url = Url {}
- renderUrl :: IsString a => Url -> a
- data a :| b = a :| b
- (/.) :: ToServer a => Path -> a -> Server (MonadOf a)
- newtype Capture (sym :: Symbol) a = Capture a
- newtype Query (sym :: Symbol) a = Query a
- newtype QueryFlag (sym :: Symbol) = QueryFlag Bool
- newtype Optional (sym :: Symbol) a = Optional (Maybe a)
- newtype Header (sym :: Symbol) a = Header a
- newtype OptionalHeader (sym :: Symbol) a = OptionalHeader (Maybe a)
- newtype Cookie a = Cookie (Maybe a)
- newtype PathInfo = PathInfo [Text]
- newtype FullPathInfo = FullPathInfo Text
- newtype RawRequest = RawRequest Request
- staticFiles :: forall (m :: Type -> Type). MonadIO m => [(FilePath, ByteString)] -> Server m
- data Plugin (m :: Type -> Type) = Plugin {}
- type PluginFun (m :: Type -> Type) = ServerFun m -> ServerFun m
- class MonadIO (MonadOf f) => ToPlugin f where
- toPluginInfo :: RouteInfo -> RouteInfo
- toPluginFun :: f -> ServerFun (MonadOf f) -> ServerFun (MonadOf f)
- applyPlugin :: ToPlugin f => f -> Server (MonadOf f) -> Server (MonadOf f)
- ($:) :: ToPlugin f => f -> Server (MonadOf f) -> Server (MonadOf f)
- prependServerAction :: MonadIO m => m () -> Plugin m
- appendServerAction :: MonadIO m => m () -> Plugin m
- processResponse :: MonadIO m => (m (Maybe Response) -> m (Maybe Response)) -> Plugin m
- data Request
- data Response
- okResponse :: forall {k} (mime :: k) a. ToRespBody mime a => a -> Response
- badResponse :: forall {k} (mime :: k) a. ToRespBody mime a => Status -> a -> Response
- badRequest :: forall {k} (media :: k) a. ToRespBody media a => a -> Response
- type ServerFun (m :: Type -> Type) = Request -> m (Maybe Response)
- class Monad m => HasServer (m :: Type -> Type) where
- type ServerResult (m :: Type -> Type)
- renderServer :: Server m -> ServerResult m
- fromReader :: env -> Server (ReaderT env IO) -> Server IO
- class ToText a where
- mapRouteInfo :: forall (m :: Type -> Type). (RouteInfo -> RouteInfo) -> Server m -> Server m
- mapServerFun :: (ServerFun m -> ServerFun n) -> Server m -> Server n
- mapResponse :: forall (m :: Type -> Type). Functor m => (Response -> Response) -> Server m -> Server m
- atPath :: forall (m :: Type -> Type). Path -> Server m -> Server m
- filterPath :: forall (m :: Type -> Type). (Path -> Bool) -> Server m -> Server m
- getServerPaths :: forall (m :: Type -> Type). Server m -> [Path]
- addPathLink :: forall (m :: Type -> Type). Path -> Path -> Server m -> Server m
- toOpenApi :: forall (m :: Type -> Type). Server m -> OpenApi
- setDescription :: forall (m :: Type -> Type). Text -> Server m -> Server m
- describeInputs :: forall (m :: Type -> Type). [(Text, Text)] -> Server m -> Server m
- setSummary :: forall (m :: Type -> Type). Text -> Server m -> Server m
- class IsString a where
- fromString :: String -> a
- class Generic a
- type String = [Char]
- data Maybe a
- data Text
- catMaybes :: [Maybe a] -> [a]
- fromMaybe :: a -> Maybe a -> a
- mapMaybe :: (a -> Maybe b) -> [a] -> [b]
- class ToJSON a where
- toJSON :: a -> Value
- toEncoding :: a -> Encoding
- toJSONList :: [a] -> Value
- toEncodingList :: [a] -> Encoding
- omitField :: a -> Bool
- class FromJSON a where
- parseJSON :: Value -> Parser a
- parseJSONList :: Value -> Parser [a]
- omittedField :: Maybe a
- class Monad m => MonadIO (m :: Type -> Type) where
- words :: String -> [String]
- unwords :: [String] -> String
- unlines :: [String] -> String
- lines :: String -> [String]
- maybeToList :: Maybe a -> [a]
- maybe :: b -> (a -> b) -> Maybe a -> b
- listToMaybe :: [a] -> Maybe a
- isNothing :: Maybe a -> Bool
- isJust :: Maybe a -> Bool
- fromJust :: HasCallStack => Maybe a -> a
- class ToMarkup a where
- toMarkup :: a -> Markup
- preEscapedToMarkup :: a -> Markup
- type Html = Markup
- class MonadTrans (t :: (Type -> Type) -> Type -> Type) where
- class Default a where
- def :: a
- defaultFormOptions :: FormOptions
- parseUnique :: FromHttpApiData v => Text -> Form -> Either Text v
- parseMaybe :: FromHttpApiData v => Text -> Form -> Either Text (Maybe v)
- parseAll :: FromHttpApiData v => Text -> Form -> Either Text [v]
- lookupUnique :: Text -> Form -> Either Text Text
- lookupMaybe :: Text -> Form -> Either Text (Maybe Text)
- lookupAll :: Text -> Form -> [Text]
- urlEncodeAsFormStable :: ToForm a => a -> ByteString
- urlEncodeAsForm :: ToForm a => a -> ByteString
- urlDecodeAsForm :: FromForm a => ByteString -> Either Text a
- urlDecodeParams :: ByteString -> Either Text [(Text, Text)]
- urlDecodeForm :: ByteString -> Either Text Form
- urlEncodeParams :: [(Text, Text)] -> ByteString
- urlEncodeFormStable :: Form -> ByteString
- urlEncodeForm :: Form -> ByteString
- genericFromForm :: (Generic a, GFromForm a (Rep a)) => FormOptions -> Form -> Either Text a
- toEntriesByKeyStable :: (Ord k, FromFormKey k, FromHttpApiData v) => Form -> Either Text [(k, [v])]
- toEntriesByKey :: (FromFormKey k, FromHttpApiData v) => Form -> Either Text [(k, [v])]
- genericToForm :: (Generic a, GToForm a (Rep a)) => FormOptions -> a -> Form
- fromEntriesByKey :: (ToFormKey k, ToHttpApiData v) => [(k, [v])] -> Form
- toListStable :: Form -> [(Text, Text)]
- class ToFormKey k where
- class FromFormKey k where
- parseFormKey :: Text -> Either Text k
- newtype Form = Form {}
- class ToForm a where
- class FromForm a where
- data FormOptions = FormOptions {
- fieldLabelModifier :: String -> String
- readTextData :: Read a => Text -> Either Text a
- parseBoundedHeader :: (ToHttpApiData a, Bounded a, Enum a) => ByteString -> Either Text a
- parseBoundedQueryParam :: (ToHttpApiData a, Bounded a, Enum a) => Text -> Either Text a
- parseBoundedUrlPiece :: (ToHttpApiData a, Bounded a, Enum a) => Text -> Either Text a
- parseBoundedEnumOfI :: (Bounded a, Enum a) => (a -> Text) -> Text -> Either Text a
- parseBoundedEnumOf :: (Bounded a, Enum a) => (a -> Text) -> Text -> Either Text a
- parseBoundedTextData :: (Show a, Bounded a, Enum a) => Text -> Either Text a
- parseQueryParamWithPrefix :: FromHttpApiData a => Text -> Text -> Either Text a
- parseHeaderWithPrefix :: FromHttpApiData a => ByteString -> ByteString -> Either Text a
- parseUrlPieceWithPrefix :: FromHttpApiData a => Text -> Text -> Either Text a
- showTextData :: Show a => a -> Text
- parseQueryParamMaybe :: FromHttpApiData a => Text -> Maybe a
- parseHeaderMaybe :: FromHttpApiData a => ByteString -> Maybe a
- parseUrlPieceMaybe :: FromHttpApiData a => Text -> Maybe a
- parseQueryParams :: (Traversable t, FromHttpApiData a) => t Text -> Either Text (t a)
- toQueryParams :: (Functor t, ToHttpApiData a) => t a -> t Text
- parseUrlPieces :: (Traversable t, FromHttpApiData a) => t Text -> Either Text (t a)
- toUrlPieces :: (Functor t, ToHttpApiData a) => t a -> t Text
- class ToHttpApiData a where
- toUrlPiece :: a -> Text
- toEncodedUrlPiece :: a -> Builder
- toHeader :: a -> ByteString
- toQueryParam :: a -> Text
- toEncodedQueryParam :: a -> Builder
- class FromHttpApiData a where
- parseUrlPiece :: Text -> Either Text a
- parseHeader :: ByteString -> Either Text a
- parseQueryParam :: Text -> Either Text a
- newtype LenientData a = LenientData {
- getLenientData :: Either Text a
- statusIsServerError :: Status -> Bool
- statusIsClientError :: Status -> Bool
- statusIsRedirection :: Status -> Bool
- statusIsSuccessful :: Status -> Bool
- statusIsInformational :: Status -> Bool
- networkAuthenticationRequired511 :: Status
- status511 :: Status
- httpVersionNotSupported505 :: Status
- status505 :: Status
- gatewayTimeout504 :: Status
- status504 :: Status
- serviceUnavailable503 :: Status
- status503 :: Status
- badGateway502 :: Status
- status502 :: Status
- notImplemented501 :: Status
- status501 :: Status
- internalServerError500 :: Status
- status500 :: Status
- requestHeaderFieldsTooLarge431 :: Status
- status431 :: Status
- tooManyRequests429 :: Status
- status429 :: Status
- preconditionRequired428 :: Status
- status428 :: Status
- upgradeRequired426 :: Status
- status426 :: Status
- unprocessableEntity422 :: Status
- status422 :: Status
- imATeapot418 :: Status
- status418 :: Status
- expectationFailed417 :: Status
- status417 :: Status
- requestedRangeNotSatisfiable416 :: Status
- status416 :: Status
- unsupportedMediaType415 :: Status
- status415 :: Status
- requestURITooLong414 :: Status
- status414 :: Status
- requestEntityTooLarge413 :: Status
- status413 :: Status
- preconditionFailed412 :: Status
- status412 :: Status
- lengthRequired411 :: Status
- status411 :: Status
- gone410 :: Status
- status410 :: Status
- conflict409 :: Status
- status409 :: Status
- requestTimeout408 :: Status
- status408 :: Status
- proxyAuthenticationRequired407 :: Status
- status407 :: Status
- notAcceptable406 :: Status
- status406 :: Status
- methodNotAllowed405 :: Status
- status405 :: Status
- notFound404 :: Status
- status404 :: Status
- forbidden403 :: Status
- status403 :: Status
- paymentRequired402 :: Status
- status402 :: Status
- unauthorized401 :: Status
- status401 :: Status
- badRequest400 :: Status
- status400 :: Status
- permanentRedirect308 :: Status
- status308 :: Status
- temporaryRedirect307 :: Status
- status307 :: Status
- useProxy305 :: Status
- status305 :: Status
- notModified304 :: Status
- status304 :: Status
- seeOther303 :: Status
- status303 :: Status
- found302 :: Status
- status302 :: Status
- movedPermanently301 :: Status
- status301 :: Status
- multipleChoices300 :: Status
- status300 :: Status
- partialContent206 :: Status
- status206 :: Status
- resetContent205 :: Status
- status205 :: Status
- noContent204 :: Status
- status204 :: Status
- nonAuthoritative203 :: Status
- status203 :: Status
- accepted202 :: Status
- status202 :: Status
- created201 :: Status
- status201 :: Status
- ok200 :: Status
- status200 :: Status
- switchingProtocols101 :: Status
- status101 :: Status
- continue100 :: Status
- status100 :: Status
- mkStatus :: Int -> ByteString -> Status
- data Status = Status {}
- type RequestHeaders = [Header]
- type ResponseHeaders = [Header]
- class Typeable a => ToSchema a where
- declareNamedSchema :: Proxy a -> Declare (Definitions Schema) NamedSchema
- class ToParamSchema a where
- toParamSchema :: Proxy a -> Schema
- data OpenApi
- paramClasses :: [Name]
- bodyClasses :: [Name]
- paramBodyClasses :: [Name]
- httpClasses :: [Name]
- mapDerive :: (Name -> Q [Dec]) -> [Name] -> Q [Dec]
- deriveNewtypeParam :: Name -> Q [Dec]
- deriveParam :: Name -> Q [Dec]
- deriveNewtypeBody :: Name -> Q [Dec]
- deriveBody :: Name -> Q [Dec]
- deriveNewtypeForm :: Name -> Q [Dec]
- deriveForm :: Name -> Q [Dec]
- deriveNewtypeHttp :: Name -> Q [Dec]
- deriveHttp :: Name -> Q [Dec]
- deriveNewtypeParamBody :: Name -> Q [Dec]
- deriveParamBody :: Name -> Q [Dec]
types
newtype Server (m :: Type -> Type) #
Server type. It is a function fron request to response. Some servers does not return valid value. We use it to find right path.
Example:
server :: Server IO server = "api/v1" /. [ "foo" /. handleFoo , "bar" /. handleBar ] handleFoo :: Query "name" Int -> Get IO (Resp Json Text) handleBar :: Post Json IO Text
Note that server is monoid and it can be constructed with Monoid functions and
path constructor (/.)
. To pass inputs for handler we can use special newtype wrappers:
Query
- for required query parametersOptional
- for optional query parametersQueryFlag
- for boolean query flagsCapture
- for parsing elements of URIHeader
- for parsing headersOptionalHeader
- for parsing optional headersBody
- fot request-body input
and other request types.
To distinguish by HTTP-method we use corresponding constructors: Get, Post, Put, etc. Let's discuss the structure of the constructor. Let's take Get for example:
type Get m a = Send GET m a newtype Send method m a = Send (m a)
Let's look at the arguments of he type
method
- type tag of the HTTP-method (GET, POST, PUT, DELETE, etc.)m
- underlying server monada
- response type. It should be convertible to the type of the response (seeIsResp
class).
HTTP API container
Append (Api a) (Api a) | alternative between two API's |
Empty | an empty API that does nothing |
WithPath Path (Api a) | path prefix for an API |
HandleRoute a | handle route |
Instances
Foldable Api | |
Defined in Mig.Core.Api fold :: Monoid m => Api m -> m # foldMap :: Monoid m => (a -> m) -> Api a -> m # foldMap' :: Monoid m => (a -> m) -> Api a -> m # foldr :: (a -> b -> b) -> b -> Api a -> b # foldr' :: (a -> b -> b) -> b -> Api a -> b # foldl :: (b -> a -> b) -> b -> Api a -> b # foldl' :: (b -> a -> b) -> b -> Api a -> b # foldr1 :: (a -> a -> a) -> Api a -> a # foldl1 :: (a -> a -> a) -> Api a -> a # elem :: Eq a => a -> Api a -> Bool # maximum :: Ord a => Api a -> a # | |
Traversable Api | |
Functor Api | |
Monoid (Api a) | |
Semigroup (Api a) | |
Show a => Show (Api a) | |
Eq a => Eq (Api a) | |
Path is a chain of elements which can be static types or capture.
There is IsString
instance which allows us to create paths from strings. Examples:
"api/v1/foo" ==> Path [StaticPath "api", StaticPath "v1", StaticPath "foo"] "api/v1/*" ==> Path [StaticPath "api", StaticPath "v1", CapturePath "*"]
Instances
IsString Path | |
Defined in Mig.Core.Api fromString :: String -> Path # | |
Monoid Path | |
Semigroup Path | |
Show Path | |
Eq Path | |
Ord Path | |
ToHttpApiData Path | |
Defined in Mig.Core.Api toUrlPiece :: Path -> Text # toEncodedUrlPiece :: Path -> Builder # toHeader :: Path -> ByteString # toQueryParam :: Path -> Text # toEncodedQueryParam :: Path -> Builder # |
Path can be a static item or capture with a name
Instances
Show PathItem | |
Eq PathItem | |
Ord PathItem | |
Defined in Mig.Core.Api | |
ToHttpApiData PathItem | |
Defined in Mig.Core.Api toUrlPiece :: PathItem -> Text # toEncodedUrlPiece :: PathItem -> Builder # toHeader :: PathItem -> ByteString # toQueryParam :: PathItem -> Text # toEncodedQueryParam :: PathItem -> Builder # |
data Route (m :: Type -> Type) #
Route contains API-info and how to run it
Instances
MonadIO m => ToRoute (Route m) | |
Defined in Mig.Core.Class.Route toRouteInfo :: RouteInfo -> RouteInfo # |
DSL
Type-level tag for JSON media type It is converted to "application/json"
Instances
ToMediaType Json | |
Defined in Mig.Core.Class.MediaType | |
FromJSON a => FromReqBody Json a | |
Defined in Mig.Core.Class.MediaType fromReqBody :: ByteString -> Either Text a # | |
ToJSON a => ToRespBody Json a | |
Defined in Mig.Core.Class.MediaType toRespBody :: a -> ByteString # |
Signifies any media. It prescribes the server renderer to lookup media-type at run-time in the "Conten-Type" header. As media-type it is rendered to "*/*".
It is useful for values for which we want to derive content type from run-time values. For example it is used for static file servers to get media type from file extension.
Instances
ToMediaType AnyMedia | |
Defined in Mig.Core.Class.MediaType | |
ToRespBody AnyMedia ByteString | |
Defined in Mig.Core.Class.MediaType toRespBody :: ByteString -> ByteString0 # | |
ToRespBody AnyMedia ByteString | |
Defined in Mig.Core.Class.MediaType toRespBody :: ByteString -> ByteString # |
data OctetStream #
Media type octet stream is for passing raw byte-strings in the request body. It is converted to "application/octet-stream"
Instances
ToMediaType OctetStream | |
Defined in Mig.Core.Class.MediaType | |
FromReqBody OctetStream ByteString | |
Defined in Mig.Core.Class.MediaType fromReqBody :: ByteString0 -> Either Text ByteString # | |
FromReqBody OctetStream ByteString | |
Defined in Mig.Core.Class.MediaType fromReqBody :: ByteString -> Either Text ByteString # | |
ToRespBody OctetStream ByteString | |
Defined in Mig.Core.Class.MediaType toRespBody :: ByteString -> ByteString0 # | |
ToRespBody OctetStream ByteString | |
Defined in Mig.Core.Class.MediaType toRespBody :: ByteString -> ByteString # |
data FormUrlEncoded #
Type-level tag for FORM url encoded media-type. It is converted to "application/x-www-form-urlencoded"
Instances
ToMediaType FormUrlEncoded | |
Defined in Mig.Core.Class.MediaType | |
FromForm a => FromReqBody FormUrlEncoded a | |
Defined in Mig.Core.Class.MediaType fromReqBody :: ByteString -> Either Text a # | |
ToForm a => ToRespBody FormUrlEncoded a | |
Defined in Mig.Core.Class.MediaType toRespBody :: a -> ByteString # |
Values that can be converted to server
Instances
ToRoute a => ToServer a | |
Defined in Mig.Core.Class.Server | |
ToServer (Server m) | |
ToServer a => ToServer [a] | |
Defined in Mig.Core.Class.Server |
class MonadIO (MonadOf a) => ToRoute a where #
Values that represent routes. A route is a function of arbitrary number of arguments. Where each argument is one of the special newtype-wrappers that read type-safe information from HTTP-request and return type of the route function is a value of something convertible to HTTP-request.
toRouteInfo :: RouteInfo -> RouteInfo #
Update API info
toRouteFun :: a -> ServerFun (MonadOf a) #
Convert to route
Instances
An HTTP media type, consisting of the type, subtype, and parameters.
Instances
IsString MediaType | |
Defined in Network.HTTP.Media.MediaType.Internal fromString :: String -> MediaType # | |
Show MediaType | |
Eq MediaType | |
Ord MediaType | |
Defined in Network.HTTP.Media.MediaType.Internal | |
Accept MediaType | |
Defined in Network.HTTP.Media.MediaType.Internal parseAccept :: ByteString -> Maybe MediaType # matches :: MediaType -> MediaType -> Bool # moreSpecificThan :: MediaType -> MediaType -> Bool # | |
RenderHeader MediaType | |
Defined in Network.HTTP.Media.MediaType.Internal renderHeader :: MediaType -> ByteString # | |
HasContentType Encoding (Maybe MediaType) | |
Defined in Data.OpenApi.Lens | |
HasContent RequestBody (InsOrdHashMap MediaType MediaTypeObject) | |
Defined in Data.OpenApi.Lens | |
HasContent Response (InsOrdHashMap MediaType MediaTypeObject) | |
Defined in Data.OpenApi.Lens |
class ToMediaType (a :: k) where #
Conversion of type-level tags to media type values
Instances
ToMediaType Html | |
Defined in Mig.Core.Class.MediaType | |
ToMediaType ByteString | |
Defined in Mig.Core.Class.MediaType | |
ToMediaType AnyMedia | |
Defined in Mig.Core.Class.MediaType | |
ToMediaType FormUrlEncoded | |
Defined in Mig.Core.Class.MediaType | |
ToMediaType Json | |
Defined in Mig.Core.Class.MediaType | |
ToMediaType OctetStream | |
Defined in Mig.Core.Class.MediaType | |
ToMediaType Text | |
Defined in Mig.Core.Class.MediaType |
class ToMediaType ty => ToRespBody (ty :: k) b where #
Values that can be rendered to response body byte string.
toRespBody :: b -> ByteString #
Instances
ToMarkup a => ToRespBody Html a | |
Defined in Mig.Core.Class.MediaType toRespBody :: a -> ByteString # | |
ToRespBody AnyMedia ByteString | |
Defined in Mig.Core.Class.MediaType toRespBody :: ByteString -> ByteString0 # | |
ToRespBody AnyMedia ByteString | |
Defined in Mig.Core.Class.MediaType toRespBody :: ByteString -> ByteString # | |
ToForm a => ToRespBody FormUrlEncoded a | |
Defined in Mig.Core.Class.MediaType toRespBody :: a -> ByteString # | |
ToJSON a => ToRespBody Json a | |
Defined in Mig.Core.Class.MediaType toRespBody :: a -> ByteString # | |
ToRespBody OctetStream ByteString | |
Defined in Mig.Core.Class.MediaType toRespBody :: ByteString -> ByteString0 # | |
ToRespBody OctetStream ByteString | |
Defined in Mig.Core.Class.MediaType toRespBody :: ByteString -> ByteString # | |
ToRespBody Text Text | |
Defined in Mig.Core.Class.MediaType toRespBody :: Text -> ByteString # | |
ToRespBody Text Text | |
Defined in Mig.Core.Class.MediaType toRespBody :: Text -> ByteString # |
response
Values that can be converted to low-level response.
The repsonse value is usually one of two cases:
Resp a
-- for routes which always produce a valueRespOr err a
- for routes that can also produce an error or value.Response
- low-level HTTP-response.
ok, bad, noContent, addHeaders, getHeaders, setStatus, getRespBody, getRespError, getStatus, getMedia, toResponse
the type of response body value
the type of an error
the media tpye of resp
Returns valid repsonse with 200 status
bad :: Status -> RespError a -> a #
Returns an error with given status
response with no content
addHeaders :: ResponseHeaders -> a -> a #
Add some header to the response
getHeaders :: a -> ResponseHeaders #
Get response headers
setStatus :: Status -> a -> a #
Sets repsonse status
getRespBody :: a -> Maybe (RespBody a) #
Get response body
getRespError :: a -> Maybe (RespError a) #
Get response error
Get response status
setMedia :: MediaType -> a -> a #
Set the media type of the response
Reads the media type by response type
toResponse :: a -> Response #
Converts value to low-level response
Instances
internalServerError :: IsResp a => RespError a -> a #
Internal server error. The bad
response with 500 status.
notImplemented :: IsResp a => RespError a -> a #
Not implemented route. The bad
response with 501 status.
redirect :: IsResp a => Text -> a #
Redirect to url. It is bad
response with 302 status and set header of Location to a given URL.
setHeader :: (IsResp a, ToHttpApiData h) => HeaderName -> h -> a -> a #
Set header for response
setCookie :: (ToForm cookie, IsResp resp) => SetCookie cookie -> resp -> resp #
Set cookie as http header from form url encoded value
Set cookie params. For explanation see an article https://web.archive.org/web/20170122122852/https://www.nczonline.net/blog/2009/05/05/http-cookies-explained/
methods
newtype Send (method :: k) (m :: k1 -> Type) (a :: k1) #
Route response type. It encodes the route method in the type and which monad is used and which type the response has.
The repsonse value is usually one of two cases:
Resp media a
-- for routes which always produce a valueRespOr media err a
- for routes that can also produce an error or value.
See the class IsResp
for more details on response types.
Instances
MonadTrans (Send method :: (Type -> Type) -> Type -> Type) | |
Defined in Mig.Core.Types.Route | |
MonadIO m => MonadIO (Send method m) | |
Defined in Mig.Core.Types.Route | |
Applicative m => Applicative (Send method m) | |
Defined in Mig.Core.Types.Route pure :: a -> Send method m a # (<*>) :: Send method m (a -> b) -> Send method m a -> Send method m b # liftA2 :: (a -> b -> c) -> Send method m a -> Send method m b -> Send method m c # (*>) :: Send method m a -> Send method m b -> Send method m b # (<*) :: Send method m a -> Send method m b -> Send method m a # | |
Functor m => Functor (Send method m) | |
Monad m => Monad (Send method m) | |
(MonadIO m, IsResp a, IsMethod method) => ToRoute (Send method m a) | |
Defined in Mig.Core.Class.Route toRouteInfo :: RouteInfo -> RouteInfo # toRouteFun :: Send method m a -> ServerFun (MonadOf (Send method m a)) # | |
(ToRespBody (RespMedia a) (RespError a), IsResp a) => FromClient (Send method Client a) | |
Defined in Mig.Client type ClientResult (Send method Client a) # fromClient :: Send method Client a -> ClientResult (Send method Client a) # | |
MapRequest (Send method Client a) | |
Defined in Mig.Client mapRequest :: (Request -> Request) -> Send method Client a -> Send method Client a mapCapture :: (CaptureMap -> CaptureMap) -> Send method Client a -> Send method Client a | |
(IsMethod method, FromReqBody (RespMedia a) (RespBody a), IsResp a) => ToClient (Send method Client a) | |
type ClientResult (Send method Client a) | |
Defined in Mig.Client |
class IsMethod (a :: k) where #
Converts type-level tag for methods to value
Instances
IsMethod DELETE | |
Defined in Mig.Core.Types.Route | |
IsMethod GET | |
Defined in Mig.Core.Types.Route | |
IsMethod HEAD | |
Defined in Mig.Core.Types.Route | |
IsMethod OPTIONS | |
Defined in Mig.Core.Types.Route | |
IsMethod PATCH | |
Defined in Mig.Core.Types.Route | |
IsMethod POST | |
Defined in Mig.Core.Types.Route | |
IsMethod PUT | |
Defined in Mig.Core.Types.Route | |
IsMethod TRACE | |
Defined in Mig.Core.Types.Route |
type-level GET-method tag
type-level POST-method tag
type-level PUT-method tag
type-level DELETE-method tag
type-level PATCH-method tag
type-level OPTIONS-method tag
type-level HEAD-method tag
type-level TRACE-method tag
safe URLs
type family UrlOf a where ... #
Converts route type to URL function
UrlOf (Send method m a) = Url | |
UrlOf (Query name value -> b) = Query name value -> UrlOf b | |
UrlOf (Optional name value -> b) = Optional name value -> UrlOf b | |
UrlOf (Capture name value -> b) = Capture name value -> UrlOf b | |
UrlOf (QueryFlag name -> b) = QueryFlag name -> UrlOf b | |
UrlOf (Header name value -> b) = UrlOf b | |
UrlOf (OptionalHeader name value -> b) = UrlOf b | |
UrlOf (Body media value -> b) = UrlOf b | |
UrlOf (Cookie value -> b) = UrlOf b | |
UrlOf (PathInfo -> b) = UrlOf b | |
UrlOf (FullPathInfo -> b) = UrlOf b | |
UrlOf (RawRequest -> b) = UrlOf b | |
UrlOf (IsSecure -> b) = UrlOf b | |
UrlOf (a, b) = (UrlOf a, UrlOf b) | |
UrlOf (a, b, c) = (UrlOf a, UrlOf b, UrlOf c) | |
UrlOf (a, b, c, d) = (UrlOf a, UrlOf b, UrlOf c, UrlOf d) | |
UrlOf (a, b, c, d, e) = (UrlOf a, UrlOf b, UrlOf c, UrlOf d, UrlOf e) | |
UrlOf (a, b, c, d, e, f) = (UrlOf a, UrlOf b, UrlOf c, UrlOf d, UrlOf e, UrlOf f) | |
UrlOf (a :| b) = UrlOf a :| UrlOf b |
Converts server to safe url. We can use it to generate safe URL constructors to be used in HTML templates An example of how we can create safe URL's. Note that order of URL's should be the same as in server definition:
type GreetingRoute = Get Html type BlogPostRoute = Optional "id" BlogPostId -> Get Html type ListPostsRoute = Get Html data Routes = Routes { greeting :: GreetingRoute , blogPost :: BlogPostRoute , listPosts :: ListPostsRoute } -- URLs data Urls = Urls { greeting :: UrlOf GreetingRoute , blogPost :: UrlOf BlogPostRoute , listPosts :: UrlOf ListPostsRoute } {\-| Site URL's URL's should be listed in the same order as they appear in the server -\} urls :: Urls urls = Urls{..} where greeting :| blogPost :| listPosts toUrl (server undefined)
Instances
ToUrl Url | |
(ToUrl a, ToUrl b) => ToUrl (a :| b) | |
(KnownSymbol sym, ToHttpApiData a, ToUrl b) => ToUrl (Capture sym a -> b) | |
(KnownSymbol sym, ToHttpApiData a, ToUrl b) => ToUrl (Optional sym a -> b) | |
(KnownSymbol sym, ToHttpApiData a, ToUrl b) => ToUrl (Query sym a -> b) | |
(KnownSymbol sym, ToUrl b) => ToUrl (QueryFlag sym -> b) | |
(ToUrl a, ToUrl b) => ToUrl (a, b) | |
(ToUrl a, ToUrl b, ToUrl c) => ToUrl (a, b, c) | |
(ToUrl a, ToUrl b, ToUrl c, ToUrl d) => ToUrl (a, b, c, d) | |
Infix synonym for pair. It can be useful to stack together
many client functions in the output of toClient
function.
a :| b |
Instances
(ToUrl a, ToUrl b) => ToUrl (a :| b) | |
(MapRequest a, MapRequest b) => MapRequest (a :| b) | |
Defined in Mig.Client mapRequest :: (Request -> Request) -> (a :| b) -> a :| b mapCapture :: (CaptureMap -> CaptureMap) -> (a :| b) -> a :| b | |
(ToClient a, ToClient b) => ToClient (a :| b) | |
Defined in Mig.Client |
path and query
Build API for routes with queries and captures. Use monoid to combine several routes together.
(/.) :: ToServer a => Path -> a -> Server (MonadOf a) infixr 4 #
Constructs server which can handle given path. Example:
"api/v1/get/info" /. handleInfo
For captures we use wild-cards:
"api/v1/get/info/*" /. handleInfo
And handle info has capture argument:
handleInfo :: Capture "nameA" -> Get IO (Resp Json value)
The name for the capture is derived from the type signature of the route handler. Note that if capture is in the last position of the path we can omit wild cards. The proper amount of captures will be derived from the type signature of the handler.
newtype Capture (sym :: Symbol) a #
Argument of capture from the query.
"api/route/{foo} if api/route/bar passed" ==> (Capture bar) :: Capture "Foo" barType
Capture a |
Instances
(FromHttpApiData a, ToParamSchema a, ToPlugin b, KnownSymbol sym) => ToPlugin (Capture sym a -> b) | |
Defined in Mig.Core.Class.Plugin | |
(FromHttpApiData a, ToParamSchema a, ToRoute b, KnownSymbol sym) => ToRoute (Capture sym a -> b) | |
Defined in Mig.Core.Class.Route toRouteInfo :: RouteInfo -> RouteInfo # toRouteFun :: (Capture sym a -> b) -> ServerFun (MonadOf (Capture sym a -> b)) # | |
(KnownSymbol sym, ToHttpApiData a, ToUrl b) => ToUrl (Capture sym a -> b) | |
FromClient b => FromClient (Capture sym a -> b) | |
Defined in Mig.Client type ClientResult (Capture sym a -> b) # fromClient :: (Capture sym a -> b) -> ClientResult (Capture sym a -> b) # | |
(KnownSymbol sym, ToHttpApiData a, ToClient b) => ToClient (Capture sym a -> b) | |
Defined in Mig.Client | |
type ClientResult (Capture sym a -> b) | |
Defined in Mig.Client |
newtype Query (sym :: Symbol) a #
Required URL parameter query.
"api/route?foo=bar" ==> (Query bar) :: Query "foo" a
Query a |
Instances
(FromHttpApiData a, ToParamSchema a, ToPlugin b, KnownSymbol sym) => ToPlugin (Query sym a -> b) | |
Defined in Mig.Core.Class.Plugin | |
(FromHttpApiData a, ToParamSchema a, ToRoute b, KnownSymbol sym) => ToRoute (Query sym a -> b) | |
Defined in Mig.Core.Class.Route toRouteInfo :: RouteInfo -> RouteInfo # toRouteFun :: (Query sym a -> b) -> ServerFun (MonadOf (Query sym a -> b)) # | |
(KnownSymbol sym, ToHttpApiData a, ToUrl b) => ToUrl (Query sym a -> b) | |
FromClient b => FromClient (Query sym a -> b) | |
Defined in Mig.Client type ClientResult (Query sym a -> b) # fromClient :: (Query sym a -> b) -> ClientResult (Query sym a -> b) # | |
(KnownSymbol sym, ToHttpApiData a, ToClient b) => ToClient (Query sym a -> b) | |
Defined in Mig.Client | |
type ClientResult (Query sym a -> b) | |
Defined in Mig.Client |
newtype QueryFlag (sym :: Symbol) #
Query flag. It is a boolean value in the URL-query. If it is missing
it is False
if it is in the query but does not have any value it is True
.
Also it can have values true/false
in the query.
Instances
(ToPlugin b, KnownSymbol sym) => ToPlugin (QueryFlag sym -> b) | |
Defined in Mig.Core.Class.Plugin | |
(ToRoute b, KnownSymbol sym) => ToRoute (QueryFlag sym -> b) | |
Defined in Mig.Core.Class.Route toRouteInfo :: RouteInfo -> RouteInfo # toRouteFun :: (QueryFlag sym -> b) -> ServerFun (MonadOf (QueryFlag sym -> b)) # | |
(KnownSymbol sym, ToUrl b) => ToUrl (QueryFlag sym -> b) | |
FromClient b => FromClient (QueryFlag a -> b) | |
Defined in Mig.Client type ClientResult (QueryFlag a -> b) # fromClient :: (QueryFlag a -> b) -> ClientResult (QueryFlag a -> b) # | |
(KnownSymbol sym, ToClient b) => ToClient (QueryFlag sym -> b) | |
Defined in Mig.Client | |
type ClientResult (QueryFlag a -> b) | |
Defined in Mig.Client |
newtype Optional (sym :: Symbol) a #
Optional URL parameter query.
"api/route?foo=bar" ==> (Optional maybeBar) :: Query "foo" a
Instances
(FromHttpApiData a, ToParamSchema a, ToPlugin b, KnownSymbol sym) => ToPlugin (Optional sym a -> b) | |
Defined in Mig.Core.Class.Plugin | |
(FromHttpApiData a, ToParamSchema a, ToRoute b, KnownSymbol sym) => ToRoute (Optional sym a -> b) | |
Defined in Mig.Core.Class.Route toRouteInfo :: RouteInfo -> RouteInfo # toRouteFun :: (Optional sym a -> b) -> ServerFun (MonadOf (Optional sym a -> b)) # | |
(KnownSymbol sym, ToHttpApiData a, ToUrl b) => ToUrl (Optional sym a -> b) | |
FromClient b => FromClient (Optional sym a -> b) | |
Defined in Mig.Client type ClientResult (Optional sym a -> b) # fromClient :: (Optional sym a -> b) -> ClientResult (Optional sym a -> b) # | |
(KnownSymbol sym, ToHttpApiData a, ToClient b) => ToClient (Optional sym a -> b) | |
Defined in Mig.Client | |
type ClientResult (Optional sym a -> b) | |
Defined in Mig.Client |
newtype Header (sym :: Symbol) a #
Reads value from the required header by name. For example if the request has header:
"foo": "bar"
It reads the value:
(Header bar) :: Header "foo" barType
Header a |
Instances
(FromHttpApiData a, ToParamSchema a, ToPlugin b, KnownSymbol sym) => ToPlugin (Header sym a -> b) | |
Defined in Mig.Core.Class.Plugin | |
(FromHttpApiData a, ToParamSchema a, ToRoute b, KnownSymbol sym) => ToRoute (Header sym a -> b) | |
Defined in Mig.Core.Class.Route toRouteInfo :: RouteInfo -> RouteInfo # toRouteFun :: (Header sym a -> b) -> ServerFun (MonadOf (Header sym a -> b)) # | |
FromClient b => FromClient (Header sym a -> b) | |
Defined in Mig.Client type ClientResult (Header sym a -> b) # fromClient :: (Header sym a -> b) -> ClientResult (Header sym a -> b) # | |
(KnownSymbol sym, ToHttpApiData a, ToClient b) => ToClient (Header sym a -> b) | |
Defined in Mig.Client | |
type ClientResult (Header sym a -> b) | |
Defined in Mig.Client |
newtype OptionalHeader (sym :: Symbol) a #
Reads value from the optional header by name. For example if the request has header:
"foo": "bar"
It reads the value:
(OptionalHeader (Just bar)) :: OptionalHeader "foo" barType
OptionalHeader (Maybe a) |
Instances
Reads a cookie. It's an optional header with name Cookie. The cookie is URL-encoded and read with instnace of FromForm class.
data MyCookie = MyCookie { secret :: Text , count :: Int } deriving (Generic, FromForm) > "secret=lolkek&count=101" (Cookie (Just (MyCookie { secret = "lolkek", count = 101 }))) :: Cookie MyCookie
Instances
(FromForm a, ToPlugin b) => ToPlugin (Cookie a -> b) | |
Defined in Mig.Core.Class.Plugin | |
(FromForm a, ToRoute b) => ToRoute (Cookie a -> b) | |
Defined in Mig.Core.Class.Route toRouteInfo :: RouteInfo -> RouteInfo # toRouteFun :: (Cookie a -> b) -> ServerFun (MonadOf (Cookie a -> b)) # |
Reads current path info.
"api/foo/bar" ==> PathInfo ["foo", "bar"]
Instances
ToPlugin a => ToPlugin (PathInfo -> a) | |
Defined in Mig.Core.Class.Plugin | |
ToRoute b => ToRoute (PathInfo -> b) | |
Defined in Mig.Core.Class.Route toRouteInfo :: RouteInfo -> RouteInfo # toRouteFun :: (PathInfo -> b) -> ServerFun (MonadOf (PathInfo -> b)) # | |
FromClient b => FromClient (PathInfo -> b) | |
Defined in Mig.Client type ClientResult (PathInfo -> b) # fromClient :: (PathInfo -> b) -> ClientResult (PathInfo -> b) # | |
ToClient b => ToClient (PathInfo -> b) | |
Defined in Mig.Client | |
type ClientResult (PathInfo -> b) | |
Defined in Mig.Client |
newtype FullPathInfo #
Reads current full-path info with queries.
"api/foo/bar?param=value" ==> FullPathInfo "api/foo/bar?param=value"
Instances
ToPlugin a => ToPlugin (FullPathInfo -> a) | |
Defined in Mig.Core.Class.Plugin toPluginInfo :: RouteInfo -> RouteInfo # toPluginFun :: (FullPathInfo -> a) -> ServerFun (MonadOf (FullPathInfo -> a)) -> ServerFun (MonadOf (FullPathInfo -> a)) # | |
ToRoute b => ToRoute (FullPathInfo -> b) | |
Defined in Mig.Core.Class.Route toRouteInfo :: RouteInfo -> RouteInfo # toRouteFun :: (FullPathInfo -> b) -> ServerFun (MonadOf (FullPathInfo -> b)) # |
newtype RawRequest #
Read low-level request. Note that it does not affect the API schema
Instances
ToPlugin a => ToPlugin (RawRequest -> a) | |
Defined in Mig.Core.Class.Plugin toPluginInfo :: RouteInfo -> RouteInfo # toPluginFun :: (RawRequest -> a) -> ServerFun (MonadOf (RawRequest -> a)) -> ServerFun (MonadOf (RawRequest -> a)) # | |
ToRoute b => ToRoute (RawRequest -> b) | |
Defined in Mig.Core.Class.Route toRouteInfo :: RouteInfo -> RouteInfo # toRouteFun :: (RawRequest -> b) -> ServerFun (MonadOf (RawRequest -> b)) # | |
FromClient b => FromClient (RawRequest -> b) | |
Defined in Mig.Client type ClientResult (RawRequest -> b) # fromClient :: (RawRequest -> b) -> ClientResult (RawRequest -> b) # | |
ToClient b => ToClient (RawRequest -> b) | |
Defined in Mig.Client toClient :: forall (m :: Type -> Type). Server m -> RawRequest -> b # clientArity :: Int # | |
type ClientResult (RawRequest -> b) | |
Defined in Mig.Client |
response
How to modify response and attach specific info to it
specific cases
staticFiles :: forall (m :: Type -> Type). MonadIO m => [(FilePath, ByteString)] -> Server m #
Serves static files. The file path is a path to where to server the file. The media-type is derived from the extension. There is a special case if we need to server the file from the rooot of the server we can omit everything from the path but keep extension. Otherwise it is not able to derive the media type.
It is convenient to use it with function embedRecursiveDir
from the library file-embed
or file-embed-lzma
.
Plugins
data Plugin (m :: Type -> Type) #
Plugin can convert all routes of the server.
It is wrapper on top of ServerFun m -> ServerFun m
.
We can apply plugins to servers with applyPlugin
function
also plugin has Monoid instance which is like Monoid.Endo or functional composition (.)
.
class MonadIO (MonadOf f) => ToPlugin f where #
Values that can represent a plugin. We use various newtype-wrappers to query type-safe info from request.
toPluginInfo :: RouteInfo -> RouteInfo #
toPluginFun :: f -> ServerFun (MonadOf f) -> ServerFun (MonadOf f) #
Instances
applyPlugin :: ToPlugin f => f -> Server (MonadOf f) -> Server (MonadOf f) #
Applies plugin to all routes of the server.
prependServerAction :: MonadIO m => m () -> Plugin m #
Prepends action to the server
appendServerAction :: MonadIO m => m () -> Plugin m #
Post appends action to the server
processResponse :: MonadIO m => (m (Maybe Response) -> m (Maybe Response)) -> Plugin m #
Applies transformation to the response
Low-level types
Http request
Http response
Instances
Show Response | |
Eq Response | |
IsResp Response | |
Defined in Mig.Core.Class.Response ok :: RespBody Response -> Response # bad :: Status -> RespError Response -> Response # noContent :: Status -> Response # addHeaders :: ResponseHeaders -> Response -> Response # getHeaders :: Response -> ResponseHeaders # setStatus :: Status -> Response -> Response # getRespBody :: Response -> Maybe (RespBody Response) # getRespError :: Response -> Maybe (RespError Response) # getStatus :: Response -> Status # setMedia :: MediaType -> Response -> Response # toResponse :: Response -> Response # | |
MonadIO m => ToPlugin (PluginFun m) | |
Defined in Mig.Core.Class.Plugin | |
type RespBody Response | |
Defined in Mig.Core.Class.Response | |
type RespError Response | |
Defined in Mig.Core.Class.Response | |
type RespMedia Response | |
Defined in Mig.Core.Class.Response |
okResponse :: forall {k} (mime :: k) a. ToRespBody mime a => a -> Response #
Respond with ok 200-status
badResponse :: forall {k} (mime :: k) a. ToRespBody mime a => Status -> a -> Response #
Bad response qith given status
badRequest :: forall {k} (media :: k) a. ToRespBody media a => a -> Response #
Bad request response
type ServerFun (m :: Type -> Type) = Request -> m (Maybe Response) #
Low-level representation of the server.
Missing route for a given request returns Nothing
.
Render
Render Reader-IO monad servers to IO servers.
class Monad m => HasServer (m :: Type -> Type) where #
Class contains types which can be converted to IO-based server to run as with WAI-interface.
We can run plain IO-servers and ReaderT over IO based servers. Readers can be wrapped in newtypes.
In that case we can derive automatically HasServer
instance.
type ServerResult (m :: Type -> Type) #
renderServer :: Server m -> ServerResult m #
Instances
HasServer IO | |
Defined in Mig.Core.Class.Server type ServerResult IO # renderServer :: Server IO -> ServerResult IO # | |
HasServer (ReaderT env IO) | |
Defined in Mig.Core.Class.Server type ServerResult (ReaderT env IO) # renderServer :: Server (ReaderT env IO) -> ServerResult (ReaderT env IO) # | |
HasServer (ReaderT env (ExceptT Text IO)) | |
Defined in Mig.Core.Class.Server |
Convertes
Values convertible to lazy text
Instances
ToText Text | |
Defined in Mig.Core.Types.Http | |
ToText Text | |
Defined in Mig.Core.Types.Http | |
ToText String | |
Defined in Mig.Core.Types.Http | |
ToText Float | |
Defined in Mig.Core.Types.Http | |
ToText Int | |
Defined in Mig.Core.Types.Http |
Server
mapRouteInfo :: forall (m :: Type -> Type). (RouteInfo -> RouteInfo) -> Server m -> Server m #
Maps over route API-information
mapServerFun :: (ServerFun m -> ServerFun n) -> Server m -> Server n #
Applies server function to all routes
mapResponse :: forall (m :: Type -> Type). Functor m => (Response -> Response) -> Server m -> Server m #
Mapps response of the server
atPath :: forall (m :: Type -> Type). Path -> Server m -> Server m #
Sub-server for a server on given path it might be usefule to emulate links from one route to another within the server or reuse part of the server inside another server.
getServerPaths :: forall (m :: Type -> Type). Server m -> [Path] #
Returns a list of all paths in the server
addPathLink :: forall (m :: Type -> Type). Path -> Path -> Server m -> Server m #
Links one route of the server to another so that every call to first path is redirected to the second path
OpenApi
setDescription :: forall (m :: Type -> Type). Text -> Server m -> Server m #
Sets description of the route
describeInputs :: forall (m :: Type -> Type). [(Text, Text)] -> Server m -> Server m #
Appends descriptiton for the inputs. It passes pairs for (input-name, input-description)
.
special name request-body is dedicated to request body input
nd raw-input is dedicated to raw input
Class for string-like datastructures; used by the overloaded string extension (-XOverloadedStrings in GHC).
fromString :: String -> a #
Instances
Representable types of kind *
.
This class is derivable in GHC with the DeriveGeneric
flag on.
A Generic
instance must satisfy the following laws:
from
.to
≡id
to
.from
≡id
Instances
The Maybe
type encapsulates an optional value. A value of type
either contains a value of type Maybe
aa
(represented as
),
or it is empty (represented as Just
aNothing
). Using Maybe
is a good way to
deal with errors or exceptional cases without resorting to drastic
measures such as error
.
The Maybe
type is also a monad. It is a simple kind of error
monad, where all errors are represented by Nothing
. A richer
error monad can be built using the Either
type.
Instances
A space efficient, packed, unboxed Unicode text type.
Instances
catMaybes :: [Maybe a] -> [a] #
The catMaybes
function takes a list of Maybe
s and returns
a list of all the Just
values.
Examples
Basic usage:
>>>
catMaybes [Just 1, Nothing, Just 3]
[1,3]
When constructing a list of Maybe
values, catMaybes
can be used
to return all of the "success" results (if the list is the result
of a map
, then mapMaybe
would be more appropriate):
>>>
import Text.Read ( readMaybe )
>>>
[readMaybe x :: Maybe Int | x <- ["1", "Foo", "3"] ]
[Just 1,Nothing,Just 3]>>>
catMaybes $ [readMaybe x :: Maybe Int | x <- ["1", "Foo", "3"] ]
[1,3]
fromMaybe :: a -> Maybe a -> a #
The fromMaybe
function takes a default value and a Maybe
value. If the Maybe
is Nothing
, it returns the default value;
otherwise, it returns the value contained in the Maybe
.
Examples
Basic usage:
>>>
fromMaybe "" (Just "Hello, World!")
"Hello, World!"
>>>
fromMaybe "" Nothing
""
Read an integer from a string using readMaybe
. If we fail to
parse an integer, we want to return 0
by default:
>>>
import Text.Read ( readMaybe )
>>>
fromMaybe 0 (readMaybe "5")
5>>>
fromMaybe 0 (readMaybe "")
0
mapMaybe :: (a -> Maybe b) -> [a] -> [b] #
The mapMaybe
function is a version of map
which can throw
out elements. In particular, the functional argument returns
something of type
. If this is Maybe
bNothing
, no element
is added on to the result list. If it is
, then Just
bb
is
included in the result list.
Examples
Using
is a shortcut for mapMaybe
f x
in most cases:catMaybes
$ map
f x
>>>
import Text.Read ( readMaybe )
>>>
let readMaybeInt = readMaybe :: String -> Maybe Int
>>>
mapMaybe readMaybeInt ["1", "Foo", "3"]
[1,3]>>>
catMaybes $ map readMaybeInt ["1", "Foo", "3"]
[1,3]
If we map the Just
constructor, the entire list should be returned:
>>>
mapMaybe Just [1,2,3]
[1,2,3]
A type that can be converted to JSON.
Instances in general must specify toJSON
and should (but don't need
to) specify toEncoding
.
An example type and instance:
-- Allow ourselves to writeText
literals. {-# LANGUAGE OverloadedStrings #-} data Coord = Coord { x :: Double, y :: Double } instanceToJSON
Coord wheretoJSON
(Coord x y) =object
["x".=
x, "y".=
y]toEncoding
(Coord x y) =pairs
("x".=
x<>
"y".=
y)
Instead of manually writing your ToJSON
instance, there are two options
to do it automatically:
- Data.Aeson.TH provides Template Haskell functions which will derive an instance at compile time. The generated instance is optimized for your type so it will probably be more efficient than the following option.
- The compiler can provide a default generic implementation for
toJSON
.
To use the second, simply add a deriving
clause to your
datatype and declare a Generic
ToJSON
instance. If you require nothing other than
defaultOptions
, it is sufficient to write (and this is the only
alternative where the default toJSON
implementation is sufficient):
{-# LANGUAGE DeriveGeneric #-} import GHC.Generics data Coord = Coord { x :: Double, y :: Double } derivingGeneric
instanceToJSON
Coord wheretoEncoding
=genericToEncoding
defaultOptions
or more conveniently using the DerivingVia extension
deriving viaGenerically
Coord instanceToJSON
Coord
If on the other hand you wish to customize the generic decoding, you have to implement both methods:
customOptions =defaultOptions
{fieldLabelModifier
=map
toUpper
} instanceToJSON
Coord wheretoJSON
=genericToJSON
customOptionstoEncoding
=genericToEncoding
customOptions
Previous versions of this library only had the toJSON
method. Adding
toEncoding
had two reasons:
toEncoding
is more efficient for the common case that the output oftoJSON
is directly serialized to aByteString
. Further, expressing either method in terms of the other would be non-optimal.- The choice of defaults allows a smooth transition for existing users:
Existing instances that do not define
toEncoding
still compile and have the correct semantics. This is ensured by making the default implementation oftoEncoding
usetoJSON
. This produces correct results, but since it performs an intermediate conversion to aValue
, it will be less efficient than directly emitting anEncoding
. (this also means that specifying nothing more thaninstance ToJSON Coord
would be sufficient as a generically decoding instance, but there probably exists no good reason to not specifytoEncoding
in new instances.)
Nothing
Convert a Haskell value to a JSON-friendly intermediate type.
toEncoding :: a -> Encoding #
Encode a Haskell value as JSON.
The default implementation of this method creates an
intermediate Value
using toJSON
. This provides
source-level compatibility for people upgrading from older
versions of this library, but obviously offers no performance
advantage.
To benefit from direct encoding, you must provide an
implementation for this method. The easiest way to do so is by
having your types implement Generic
using the DeriveGeneric
extension, and then have GHC generate a method body as follows.
instanceToJSON
Coord wheretoEncoding
=genericToEncoding
defaultOptions
toJSONList :: [a] -> Value #
toEncodingList :: [a] -> Encoding #
Defines when it is acceptable to omit a field of this type from a record.
Used by (
operator, and Generics and TH deriving
with .?=
)
.omitNothingFields
= True
Since: aeson-2.2.0.0
Instances
A type that can be converted from JSON, with the possibility of failure.
In many cases, you can get the compiler to generate parsing code for you (see below). To begin, let's cover writing an instance by hand.
There are various reasons a conversion could fail. For example, an
Object
could be missing a required key, an Array
could be of
the wrong size, or a value could be of an incompatible type.
The basic ways to signal a failed conversion are as follows:
fail
yields a custom error message: it is the recommended way of reporting a failure;empty
(ormzero
) is uninformative: use it when the error is meant to be caught by some(
;<|>
)typeMismatch
can be used to report a failure when the encountered value is not of the expected JSON type;unexpected
is an appropriate alternative when more than one type may be expected, or to keep the expected type implicit.
prependFailure
(or modifyFailure
) add more information to a parser's
error messages.
An example type and instance using typeMismatch
and prependFailure
:
-- Allow ourselves to writeText
literals. {-# LANGUAGE OverloadedStrings #-} data Coord = Coord { x :: Double, y :: Double } instanceFromJSON
Coord whereparseJSON
(Object
v) = Coord<$>
v.:
"x"<*>
v.:
"y" -- We do not expect a non-Object
value here. -- We could useempty
to fail, buttypeMismatch
-- gives a much more informative error message.parseJSON
invalid =prependFailure
"parsing Coord failed, " (typeMismatch
"Object" invalid)
For this common case of only being concerned with a single
type of JSON value, the functions withObject
, withScientific
, etc.
are provided. Their use is to be preferred when possible, since
they are more terse. Using withObject
, we can rewrite the above instance
(assuming the same language extension and data type) as:
instanceFromJSON
Coord whereparseJSON
=withObject
"Coord" $ \v -> Coord<$>
v.:
"x"<*>
v.:
"y"
Instead of manually writing your FromJSON
instance, there are two options
to do it automatically:
- Data.Aeson.TH provides Template Haskell functions which will derive an instance at compile time. The generated instance is optimized for your type so it will probably be more efficient than the following option.
- The compiler can provide a default generic implementation for
parseJSON
.
To use the second, simply add a deriving
clause to your
datatype and declare a Generic
FromJSON
instance for your datatype without giving
a definition for parseJSON
.
For example, the previous example can be simplified to just:
{-# LANGUAGE DeriveGeneric #-} import GHC.Generics data Coord = Coord { x :: Double, y :: Double } derivingGeneric
instanceFromJSON
Coord
or using the DerivingVia extension
deriving viaGenerically
Coord instanceFromJSON
Coord
The default implementation will be equivalent to
parseJSON =
; if you need different
options, you can customize the generic decoding by defining:genericParseJSON
defaultOptions
customOptions =defaultOptions
{fieldLabelModifier
=map
toUpper
} instanceFromJSON
Coord whereparseJSON
=genericParseJSON
customOptions
Nothing
parseJSON :: Value -> Parser a #
parseJSONList :: Value -> Parser [a] #
omittedField :: Maybe a #
Default value for optional fields.
Used by (
operator, and Generics and TH deriving
with .:?=
)
(default).allowOmittedFields
= True
Since: aeson-2.2.0.0
Instances
class Monad m => MonadIO (m :: Type -> Type) where #
Monads in which IO
computations may be embedded.
Any monad built by applying a sequence of monad transformers to the
IO
monad will be an instance of this class.
Instances should satisfy the following laws, which state that liftIO
is a transformer of monads:
Lift a computation from the IO
monad.
This allows us to run IO computations in any monadic stack, so long as it supports these kinds of operations
(i.e. IO
is the base monad for the stack).
Example
import Control.Monad.Trans.State -- from the "transformers" library printState :: Show s => StateT s IO () printState = do state <- get liftIO $ print state
Had we omitted
, we would have ended up with this error:liftIO
• Couldn't match type ‘IO’ with ‘StateT s IO’ Expected type: StateT s IO () Actual type: IO ()
The important part here is the mismatch between StateT s IO ()
and
.IO
()
Luckily, we know of a function that takes an
and returns an IO
a(m a)
:
,
enabling us to run the program and see the expected results:liftIO
> evalStateT printState "hello" "hello" > evalStateT printState 3 3
Instances
MonadIO IO | Since: base-4.9.0.0 |
Defined in Control.Monad.IO.Class | |
MonadIO Client | |
Defined in Mig.Client | |
MonadIO Client' | |
Defined in Mig.Client | |
MonadIO Q | |
Defined in Language.Haskell.TH.Syntax | |
MonadIO m => MonadIO (ResourceT m) | |
Defined in Control.Monad.Trans.Resource.Internal | |
(Functor f, MonadIO m) => MonadIO (FreeT f m) | |
Defined in Control.Monad.Trans.Free | |
(Error e, MonadIO m) => MonadIO (ErrorT e m) | |
Defined in Control.Monad.Trans.Error | |
MonadIO m => MonadIO (ReaderT r m) | |
Defined in Control.Monad.Trans.Reader | |
MonadIO m => MonadIO (ConduitT i o m) | |
Defined in Data.Conduit.Internal.Conduit | |
MonadIO m => MonadIO (Send method m) | |
Defined in Mig.Core.Types.Route | |
MonadIO m => MonadIO (Pipe l i o u m) | |
Defined in Data.Conduit.Internal.Pipe |
words
breaks a string up into a list of words, which were delimited
by white space.
>>>
words "Lorem ipsum\ndolor"
["Lorem","ipsum","dolor"]
lines
breaks a string up into a list of strings at newline
characters. The resulting strings do not contain newlines.
Note that after splitting the string at newline characters, the last part of the string is considered a line even if it doesn't end with a newline. For example,
>>>
lines ""
[]
>>>
lines "\n"
[""]
>>>
lines "one"
["one"]
>>>
lines "one\n"
["one"]
>>>
lines "one\n\n"
["one",""]
>>>
lines "one\ntwo"
["one","two"]
>>>
lines "one\ntwo\n"
["one","two"]
Thus
contains at least as many elements as newlines in lines
ss
.
maybeToList :: Maybe a -> [a] #
The maybeToList
function returns an empty list when given
Nothing
or a singleton list when given Just
.
Examples
Basic usage:
>>>
maybeToList (Just 7)
[7]
>>>
maybeToList Nothing
[]
One can use maybeToList
to avoid pattern matching when combined
with a function that (safely) works on lists:
>>>
import Text.Read ( readMaybe )
>>>
sum $ maybeToList (readMaybe "3")
3>>>
sum $ maybeToList (readMaybe "")
0
maybe :: b -> (a -> b) -> Maybe a -> b #
The maybe
function takes a default value, a function, and a Maybe
value. If the Maybe
value is Nothing
, the function returns the
default value. Otherwise, it applies the function to the value inside
the Just
and returns the result.
Examples
Basic usage:
>>>
maybe False odd (Just 3)
True
>>>
maybe False odd Nothing
False
Read an integer from a string using readMaybe
. If we succeed,
return twice the integer; that is, apply (*2)
to it. If instead
we fail to parse an integer, return 0
by default:
>>>
import Text.Read ( readMaybe )
>>>
maybe 0 (*2) (readMaybe "5")
10>>>
maybe 0 (*2) (readMaybe "")
0
Apply show
to a Maybe Int
. If we have Just n
, we want to show
the underlying Int
n
. But if we have Nothing
, we return the
empty string instead of (for example) "Nothing":
>>>
maybe "" show (Just 5)
"5">>>
maybe "" show Nothing
""
listToMaybe :: [a] -> Maybe a #
The listToMaybe
function returns Nothing
on an empty list
or
where Just
aa
is the first element of the list.
Examples
Basic usage:
>>>
listToMaybe []
Nothing
>>>
listToMaybe [9]
Just 9
>>>
listToMaybe [1,2,3]
Just 1
Composing maybeToList
with listToMaybe
should be the identity
on singleton/empty lists:
>>>
maybeToList $ listToMaybe [5]
[5]>>>
maybeToList $ listToMaybe []
[]
But not on lists with more than one element:
>>>
maybeToList $ listToMaybe [1,2,3]
[1]
fromJust :: HasCallStack => Maybe a -> a #
Class allowing us to use a single function for Markup values
Convert a value to Markup.
preEscapedToMarkup :: a -> Markup #
Convert a value to Markup without escaping
Instances
ToMarkup Int32 | |
Defined in Text.Blaze | |
ToMarkup Int64 | |
Defined in Text.Blaze | |
ToMarkup Word32 | |
Defined in Text.Blaze | |
ToMarkup Word64 | |
Defined in Text.Blaze | |
ToMarkup Markup | |
Defined in Text.Blaze | |
ToMarkup Link Source # | |
Defined in Mig.Extra.Server.Html | |
ToMarkup Text | |
Defined in Text.Blaze | |
ToMarkup Builder | |
Defined in Text.Blaze | |
ToMarkup Text | |
Defined in Text.Blaze | |
ToMarkup String | |
Defined in Text.Blaze | |
ToMarkup Integer | |
Defined in Text.Blaze | |
ToMarkup Natural | |
Defined in Text.Blaze | |
ToMarkup Bool | |
Defined in Text.Blaze | |
ToMarkup Char | |
Defined in Text.Blaze | |
ToMarkup Double | |
Defined in Text.Blaze | |
ToMarkup Float | |
Defined in Text.Blaze | |
ToMarkup Int | |
Defined in Text.Blaze | |
ToMarkup Word | |
Defined in Text.Blaze | |
ToMarkup (NonEmpty Char) | |
ToMarkup [Markup] | |
Defined in Text.Blaze |
class MonadTrans (t :: (Type -> Type) -> Type -> Type) where #
The class of monad transformers. Instances should satisfy the
following laws, which state that lift
is a monad transformation:
lift :: Monad m => m a -> t m a #
Lift a computation from the argument monad to the constructed monad.
Instances
MonadTrans Free | This is not a true monad transformer. It is only a monad transformer "up to |
Defined in Control.Monad.Free | |
MonadTrans Yoneda | |
Defined in Data.Functor.Yoneda | |
MonadTrans ResourceT | |
Defined in Control.Monad.Trans.Resource.Internal | |
Alternative f => MonadTrans (CofreeT f) | |
Defined in Control.Comonad.Trans.Cofree | |
Functor f => MonadTrans (FreeT f) | |
Defined in Control.Monad.Trans.Free | |
MonadTrans (ErrorT e) | |
Defined in Control.Monad.Trans.Error | |
MonadTrans (ReaderT r) | |
Defined in Control.Monad.Trans.Reader | |
MonadTrans (ConduitT i o) | |
Defined in Data.Conduit.Internal.Conduit | |
MonadTrans (Send method :: (Type -> Type) -> Type -> Type) | |
Defined in Mig.Core.Types.Route | |
MonadTrans (Pipe l i o u) | |
Defined in Data.Conduit.Internal.Pipe |
A class for types with a default value.
Nothing
Instances
defaultFormOptions :: FormOptions #
Default encoding FormOptions
.
FormOptions
{fieldLabelModifier
= id }
parseUnique :: FromHttpApiData v => Text -> Form -> Either Text v #
Lookup a unique value for a given key and parse it with parseQueryParam
.
Fail if there is zero or more than one value for the key.
>>>
parseUnique "age" [] :: Either Text Word8
Left "Could not find key \"age\"">>>
parseUnique "age" [("age", "12"), ("age", "25")] :: Either Text Word8
Left "Duplicate key \"age\"">>>
parseUnique "age" [("age", "seven")] :: Either Text Word8
Left "could not parse: `seven' (input does not start with a digit)">>>
parseUnique "age" [("age", "777")] :: Either Text Word8
Left "out of bounds: `777' (should be between 0 and 255)">>>
parseUnique "age" [("age", "7")] :: Either Text Word8
Right 7
parseMaybe :: FromHttpApiData v => Text -> Form -> Either Text (Maybe v) #
Lookup an optional value for a given key and parse it with parseQueryParam
.
Fail if there is more than one value for the key.
>>>
parseMaybe "age" [] :: Either Text (Maybe Word8)
Right Nothing>>>
parseMaybe "age" [("age", "12"), ("age", "25")] :: Either Text (Maybe Word8)
Left "Duplicate key \"age\"">>>
parseMaybe "age" [("age", "seven")] :: Either Text (Maybe Word8)
Left "could not parse: `seven' (input does not start with a digit)">>>
parseMaybe "age" [("age", "777")] :: Either Text (Maybe Word8)
Left "out of bounds: `777' (should be between 0 and 255)">>>
parseMaybe "age" [("age", "7")] :: Either Text (Maybe Word8)
Right (Just 7)
parseAll :: FromHttpApiData v => Text -> Form -> Either Text [v] #
Lookup all values for a given key in a Form
and parse them with parseQueryParams
.
>>>
parseAll "age" [] :: Either Text [Word8]
Right []>>>
parseAll "age" [("age", "8"), ("age", "seven")] :: Either Text [Word8]
Left "could not parse: `seven' (input does not start with a digit)">>>
parseAll "age" [("age", "8"), ("age", "777")] :: Either Text [Word8]
Left "out of bounds: `777' (should be between 0 and 255)">>>
parseAll "age" [("age", "12"), ("age", "25")] :: Either Text [Word8]
Right [12,25]
lookupUnique :: Text -> Form -> Either Text Text #
Lookup a unique value for a key. Fail if there is zero or more than one value.
>>>
lookupUnique "name" []
Left "Could not find key \"name\"">>>
lookupUnique "name" [("name", "Oleg")]
Right "Oleg">>>
lookupUnique "name" [("name", "Oleg"), ("name", "David")]
Left "Duplicate key \"name\""
lookupMaybe :: Text -> Form -> Either Text (Maybe Text) #
Lookup an optional value for a key. Fail if there is more than one value.
>>>
lookupMaybe "name" []
Right Nothing>>>
lookupMaybe "name" [("name", "Oleg")]
Right (Just "Oleg")>>>
lookupMaybe "name" [("name", "Oleg"), ("name", "David")]
Left "Duplicate key \"name\""
lookupAll :: Text -> Form -> [Text] #
Find all values corresponding to a given key in a Form
.
>>>
lookupAll "name" []
[]>>>
lookupAll "name" [("name", "Oleg")]
["Oleg"]>>>
lookupAll "name" [("name", "Oleg"), ("name", "David")]
["Oleg","David"]
urlEncodeAsFormStable :: ToForm a => a -> ByteString #
This is a convenience function for encoding a datatype that has instance
of ToForm
directly to a application/x-www-form-urlencoded
ByteString
.
This is effectively
.urlEncodeFormStable
. toForm
>>>
urlEncodeAsFormStable Person {name = "Dennis", age = 22}
"age=22&name=Dennis"
urlEncodeAsForm :: ToForm a => a -> ByteString #
This is a convenience function for encoding a datatype that has instance
of ToForm
directly to a application/x-www-form-urlencoded
ByteString
.
This is effectively
.urlEncodeForm
. toForm
_NOTE:_ this encoding is unstable and may result in different key order
(but not values). For a stable encoding see urlEncodeAsFormStable
.
urlDecodeAsForm :: FromForm a => ByteString -> Either Text a #
This is a convenience function for decoding a
application/x-www-form-urlencoded
ByteString
directly to a datatype
that has an instance of FromForm
.
This is effectively
.fromForm
<=<
urlDecodeForm
>>>
urlDecodeAsForm "name=Dennis&age=22" :: Either Text Person
Right (Person {name = "Dennis", age = 22})
urlDecodeParams :: ByteString -> Either Text [(Text, Text)] #
Decode an application/x-www-form-urlencoded
ByteString
to a list of key-value pairs.
See also urlDecodeForm
.
urlDecodeForm :: ByteString -> Either Text Form #
Decode an application/x-www-form-urlencoded
ByteString
to a Form
.
Key-value pairs get decoded normally:
>>>
urlDecodeForm "name=Greg&lastname=Weber"
Right (fromList [("lastname","Weber"),("name","Greg")])
Keys with no values get decoded to pairs with empty values.
>>>
urlDecodeForm "is_test"
Right (fromList [("is_test","")])
Empty keys are allowed:
>>>
urlDecodeForm "=foobar"
Right (fromList [("","foobar")])
The empty string gets decoded into an empty Form
:
>>>
urlDecodeForm ""
Right (fromList [])
Everything is un-escaped with unEscapeString
:
>>>
urlDecodeForm "fullname=Andres%20L%C3%B6h"
Right (fromList [("fullname","Andres L\246h")])
Improperly formed strings result in an error:
>>>
urlDecodeForm "this=has=too=many=equals"
Left "not a valid pair: this=has=too=many=equals"
urlEncodeParams :: [(Text, Text)] -> ByteString #
Encode a list of key-value pairs to an application/x-www-form-urlencoded
ByteString
.
See also urlEncodeFormStable
.
urlEncodeFormStable :: Form -> ByteString #
Encode a Form
to an application/x-www-form-urlencoded
ByteString
.
For an unstable (but faster) encoding see urlEncodeForm
.
Key-value pairs get encoded to key=value
and separated by &
:
>>>
urlEncodeFormStable [("name", "Julian"), ("lastname", "Arni")]
"lastname=Arni&name=Julian"
Keys with empty values get encoded to just key
(without the =
sign):
>>>
urlEncodeFormStable [("is_test", "")]
"is_test"
Empty keys are allowed too:
>>>
urlEncodeFormStable [("", "foobar")]
"=foobar"
However, if both key and value are empty, the key-value pair is ignored.
(This prevents
from being a true isomorphism).urlDecodeForm
. urlEncodeFormStable
>>>
urlEncodeFormStable [("", "")]
""
Everything is escaped with
:escapeURIString
isUnreserved
>>>
urlEncodeFormStable [("fullname", "Andres Löh")]
"fullname=Andres%20L%C3%B6h"
urlEncodeForm :: Form -> ByteString #
Encode a Form
to an application/x-www-form-urlencoded
ByteString
.
_NOTE:_ this encoding is unstable and may result in different key order
(but not values). For a stable encoding see urlEncodeFormStable
.
genericFromForm :: (Generic a, GFromForm a (Rep a)) => FormOptions -> Form -> Either Text a #
A Generic
-based implementation of fromForm
.
This is used as a default implementation in FromForm
.
Note that this only works for records (i.e. product data types with named fields):
data Person = Person
{ name :: String
, age :: Int
} deriving (Generic
)
In this implementation each field's value gets decoded using parseQueryParam
.
Two field types are exceptions:
- for values of type
an entry is parsed if present in theMaybe
aForm
and the is decoded withparseQueryParam
; if no entry is present result isNothing
; - for values of type
[a]
(except[
) all entries are parsed to produce a list of parsed values;Char
]
Here's an example:
data Post = Post { title :: String , subtitle :: Maybe String , comments :: [String] } deriving (Generic
,Show
) instanceFromForm
Post
>>>
urlDecodeAsForm "comments=Nice%20post%21&comments=%2B1&title=Test" :: Either Text Post
Right (Post {title = "Test", subtitle = Nothing, comments = ["Nice post!","+1"]})
toEntriesByKeyStable :: (Ord k, FromFormKey k, FromHttpApiData v) => Form -> Either Text [(k, [v])] #
Parse a Form
into a list of entries groupped by key.
>>>
toEntriesByKeyStable [("name", "Nick"), ("color", "red"), ("color", "white")] :: Either Text [(Text, [Text])]
Right [("color",["red","white"]),("name",["Nick"])]
For an unstable (but faster) conversion see toEntriesByKey
.
toEntriesByKey :: (FromFormKey k, FromHttpApiData v) => Form -> Either Text [(k, [v])] #
Parse a Form
into a list of entries groupped by key.
_NOTE:_ this conversion is unstable and may result in different key order
(but not values). For a stable encoding see toEntriesByKeyStable
.
genericToForm :: (Generic a, GToForm a (Rep a)) => FormOptions -> a -> Form #
A Generic
-based implementation of toForm
.
This is used as a default implementation in ToForm
.
Note that this only works for records (i.e. product data types with named fields):
data Person = Person
{ name :: String
, age :: Int
} deriving (Generic
)
In this implementation each field's value gets encoded using toQueryParam
.
Two field types are exceptions:
- for values of type
an entry is added to theMaybe
aForm
only when it is
and the encoded value isJust
x
;toQueryParam
xNothing
values are omitted from theForm
; - for values of type
[a]
(except[
) an entry is added for every item in the list; if the list is empty no entries are added to theChar
]Form
;
Here's an example:
data Post = Post { title :: String , subtitle :: Maybe String , comments :: [String] } deriving (Generic
,Show
) instanceToForm
Post
>>>
urlEncodeAsFormStable Post { title = "Test", subtitle = Nothing, comments = ["Nice post!", "+1"] }
"comments=Nice%20post%21&comments=%2B1&title=Test"
fromEntriesByKey :: (ToFormKey k, ToHttpApiData v) => [(k, [v])] -> Form #
Convert a list of entries groupped by key into a Form
.
>>>
fromEntriesByKey [("name",["Nick"]),("color",["red","blue"])]
fromList [("color","red"),("color","blue"),("name","Nick")]
Instances
class FromFormKey k where #
Instances
The contents of a form, not yet URL-encoded.
Form
can be URL-encoded with urlEncodeForm
and URL-decoded with urlDecodeForm
.
Instances
Monoid Form | |
Semigroup Form | |
IsList Form | _NOTE:_ |
Generic Form | |
Read Form | |
Show Form | |
Eq Form | |
FromForm Form | |
ToForm Form | |
Defined in Web.Internal.FormUrlEncoded | |
type Item Form | |
Defined in Web.Internal.FormUrlEncoded | |
type Rep Form | |
Defined in Web.Internal.FormUrlEncoded |
Convert a value into Form
.
An example type and instance:
{-# LANGUAGE OverloadedLists #-} data Person = Person { name :: String , age :: Int } instanceToForm
Person wheretoForm
person = [ ("name",toQueryParam
(name person)) , ("age",toQueryParam
(age person)) ]
Instead of manually writing
instances you can
use a default generic implementation of ToForm
.toForm
To do that, simply add deriving
clause to your datatype
and declare a Generic
ToForm
instance for your datatype without
giving definition for toForm
.
For instance, the previous example can be simplified into this:
data Person = Person { name :: String , age :: Int } deriving (Generic
) instanceToForm
Person
The default implementation of toForm
is genericToForm
.
Nothing
Instances
ToForm Form | |
Defined in Web.Internal.FormUrlEncoded | |
ToHttpApiData v => ToForm (IntMap [v]) | |
Defined in Web.Internal.FormUrlEncoded | |
(ToFormKey k, ToHttpApiData v) => ToForm [(k, v)] | |
Defined in Web.Internal.FormUrlEncoded | |
(ToFormKey k, ToHttpApiData v) => ToForm (Map k [v]) | |
Defined in Web.Internal.FormUrlEncoded | |
(ToFormKey k, ToHttpApiData v) => ToForm (HashMap k [v]) | |
Defined in Web.Internal.FormUrlEncoded |
Parse Form
into a value.
An example type and instance:
data Person = Person { name :: String , age :: Int } instanceFromForm
Person wherefromForm
f = Person<$>
parseUnique
"name" f<*>
parseUnique
"age" f
Instead of manually writing
instances you can
use a default generic implementation of FromForm
.fromForm
To do that, simply add deriving
clause to your datatype
and declare a Generic
FromForm
instance for your datatype without
giving definition for fromForm
.
For instance, the previous example can be simplified into this:
data Person = Person { name :: String , age :: Int } deriving (Generic
) instanceFromForm
Person
The default implementation of fromForm
is genericFromForm
.
It only works for records and it will use parseQueryParam
for each field's value.
Nothing
Instances
FromForm Form | |
FromHttpApiData v => FromForm (IntMap [v]) | |
(FromFormKey k, FromHttpApiData v) => FromForm [(k, v)] | _NOTE:_ this conversion is unstable and may result in different key order (but not values). |
(Ord k, FromFormKey k, FromHttpApiData v) => FromForm (Map k [v]) | |
(Eq k, Hashable k, FromFormKey k, FromHttpApiData v) => FromForm (HashMap k [v]) | |
data FormOptions #
Generic
-based deriving options for ToForm
and FromForm
.
A common use case for non-default FormOptions
is to strip a prefix off of field labels:
data Project = Project { projectName :: String , projectSize :: Int } deriving (Generic
,Show
) myOptions ::FormOptions
myOptions =FormOptions
{fieldLabelModifier
=map
toLower
.drop
(length
"project") } instanceToForm
Project wheretoForm
=genericToForm
myOptions instanceFromForm
Project wherefromForm
=genericFromForm
myOptions
>>>
urlEncodeAsFormStable Project { projectName = "http-api-data", projectSize = 172 }
"name=http-api-data&size=172">>>
urlDecodeAsForm "name=http-api-data&size=172" :: Either Text Project
Right (Project {projectName = "http-api-data", projectSize = 172})
FormOptions | |
|
readTextData :: Read a => Text -> Either Text a #
Parse URL piece using
instance.Read
Use for types which do not involve letters:
>>>
readTextData "1991-06-02" :: Either Text Day
Right 1991-06-02
This parser is case sensitive and will not match
in presence of letters:showTextData
>>>
readTextData (showTextData True) :: Either Text Bool
Left "could not parse: `true'"
See
.parseBoundedTextData
parseBoundedHeader :: (ToHttpApiData a, Bounded a, Enum a) => ByteString -> Either Text a #
Parse values based on
instance.
Uses ToHttpApiData
to get possible values.toHeader
parseBoundedQueryParam :: (ToHttpApiData a, Bounded a, Enum a) => Text -> Either Text a #
Case insensitive.
Parse values case insensitively based on
instance.
Uses ToHttpApiData
to get possible values.toQueryParam
parseBoundedUrlPiece :: (ToHttpApiData a, Bounded a, Enum a) => Text -> Either Text a #
Case insensitive.
Parse values case insensitively based on
instance.
Uses ToHttpApiData
to get possible values.toUrlPiece
parseBoundedEnumOfI :: (Bounded a, Enum a) => (a -> Text) -> Text -> Either Text a #
Case insensitive.
Parse values case insensitively based on a precalculated mapping
of their
representations.Text
>>>
parseBoundedEnumOfI toUrlPiece "FALSE" :: Either Text Bool
Right False
For case sensitive parser see parseBoundedEnumOf
.
parseBoundedEnumOf :: (Bounded a, Enum a) => (a -> Text) -> Text -> Either Text a #
Parse values based on a precalculated mapping of their
representation.Text
>>>
parseBoundedEnumOf toUrlPiece "true" :: Either Text Bool
Right True
For case insensitive parser see parseBoundedEnumOfI
.
parseBoundedTextData :: (Show a, Bounded a, Enum a) => Text -> Either Text a #
Case insensitive.
Parse values case insensitively based on
instance.Show
>>>
parseBoundedTextData "true" :: Either Text Bool
Right True>>>
parseBoundedTextData "FALSE" :: Either Text Bool
Right False
This can be used as a default implementation for enumeration types:
>>>
data MyData = Foo | Bar | Baz deriving (Show, Bounded, Enum)
>>>
instance FromHttpApiData MyData where parseUrlPiece = parseBoundedTextData
>>>
parseUrlPiece "foo" :: Either Text MyData
Right Foo
parseQueryParamWithPrefix :: FromHttpApiData a => Text -> Text -> Either Text a #
Case insensitive.
Parse given text case insensitive and then parse the rest of the input
using
.parseQueryParam
>>>
parseQueryParamWithPrefix "z" "z10" :: Either Text Int
Right 10
parseHeaderWithPrefix :: FromHttpApiData a => ByteString -> ByteString -> Either Text a #
Parse given bytestring then parse the rest of the input using
.parseHeader
data BasicAuthToken = BasicAuthToken Text deriving (Show) instance FromHttpApiData BasicAuthToken where parseHeader h = BasicAuthToken <$> parseHeaderWithPrefix "Basic " h parseQueryParam p = BasicAuthToken <$> parseQueryParam p
>>>
parseHeader "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" :: Either Text BasicAuthToken
Right (BasicAuthToken "QWxhZGRpbjpvcGVuIHNlc2FtZQ==")
parseUrlPieceWithPrefix :: FromHttpApiData a => Text -> Text -> Either Text a #
Case insensitive.
Parse given text case insensitive and then parse the rest of the input
using
.parseUrlPiece
>>>
parseUrlPieceWithPrefix "Just " "just 10" :: Either Text Int
Right 10>>>
parseUrlPieceWithPrefix "Left " "left" :: Either Text Bool
Left "could not parse: `left'"
This can be used to implement
for single field constructors:FromHttpApiData
>>>
data Foo = Foo Int deriving (Show)
>>>
instance FromHttpApiData Foo where parseUrlPiece s = Foo <$> parseUrlPieceWithPrefix "Foo " s
>>>
parseUrlPiece "foo 1" :: Either Text Foo
Right (Foo 1)
showTextData :: Show a => a -> Text #
Lower case.
Convert to URL piece using
instance.
The result is always lower cased.Show
>>>
showTextData True
"true"
This can be used as a default implementation for enumeration types:
>>>
data MyData = Foo | Bar | Baz deriving (Show)
>>>
instance ToHttpApiData MyData where toUrlPiece = showTextData
>>>
toUrlPiece Foo
"foo"
parseQueryParamMaybe :: FromHttpApiData a => Text -> Maybe a #
Parse query param value in a
.Maybe
>>>
parseQueryParamMaybe "true" :: Maybe Bool
Just True
parseHeaderMaybe :: FromHttpApiData a => ByteString -> Maybe a #
Parse HTTP header value in a
.Maybe
>>>
parseHeaderMaybe "hello" :: Maybe Text
Just "hello"
parseUrlPieceMaybe :: FromHttpApiData a => Text -> Maybe a #
Parse URL path piece in a
.Maybe
>>>
parseUrlPieceMaybe "12" :: Maybe Int
Just 12
parseQueryParams :: (Traversable t, FromHttpApiData a) => t Text -> Either Text (t a) #
Parse multiple query parameters.
>>>
parseQueryParams ["1", "2", "3"] :: Either Text [Int]
Right [1,2,3]>>>
parseQueryParams ["64", "128", "256"] :: Either Text [Word8]
Left "out of bounds: `256' (should be between 0 and 255)"
toQueryParams :: (Functor t, ToHttpApiData a) => t a -> t Text #
Convert multiple values to a list of query parameter values.
>>>
toQueryParams [fromGregorian 2015 10 03, fromGregorian 2015 12 01] :: [Text]
["2015-10-03","2015-12-01"]
parseUrlPieces :: (Traversable t, FromHttpApiData a) => t Text -> Either Text (t a) #
Parse multiple URL pieces.
>>>
parseUrlPieces ["true", "false"] :: Either Text [Bool]
Right [True,False]>>>
parseUrlPieces ["123", "hello", "world"] :: Either Text [Int]
Left "could not parse: `hello' (input does not start with a digit)"
toUrlPieces :: (Functor t, ToHttpApiData a) => t a -> t Text #
Convert multiple values to a list of URL pieces.
>>>
toUrlPieces [1, 2, 3] :: [Text]
["1","2","3"]
class ToHttpApiData a where #
Convert value to HTTP API data.
WARNING: Do not derive this using DeriveAnyClass
as the generated
instance will loop indefinitely.
toUrlPiece :: a -> Text #
Convert to URL path piece.
toEncodedUrlPiece :: a -> Builder #
Convert to a URL path piece, making sure to encode any special chars.
The default definition uses
but this may be overriden with a more efficient version.urlEncodeBuilder
False
toHeader :: a -> ByteString #
Convert to HTTP header value.
toQueryParam :: a -> Text #
Convert to query param value.
toEncodedQueryParam :: a -> Builder #
Convert to URL query param,
The default definition uses
but this may be overriden with a more efficient version.urlEncodeBuilder
True
Since: http-api-data-0.5.1
Instances
class FromHttpApiData a where #
Parse value from HTTP API data.
WARNING: Do not derive this using DeriveAnyClass
as the generated
instance will loop indefinitely.
parseUrlPiece :: Text -> Either Text a #
Parse URL path piece.
parseHeader :: ByteString -> Either Text a #
Parse HTTP header value.
parseQueryParam :: Text -> Either Text a #
Parse query param value.
Instances
newtype LenientData a #
Lenient parameters. FromHttpApiData
combinators always return Right
.
Since: http-api-data-0.3.5
Instances
statusIsServerError :: Status -> Bool #
Server Error class
statusIsClientError :: Status -> Bool #
Client Error class
statusIsRedirection :: Status -> Bool #
Redirection class
statusIsSuccessful :: Status -> Bool #
Successful class
statusIsInformational :: Status -> Bool #
Informational class
networkAuthenticationRequired511 :: Status #
Network Authentication Required 511 (RFC 6585)
httpVersionNotSupported505 :: Status #
HTTP Version Not Supported 505
Gateway Timeout 504
serviceUnavailable503 :: Status #
Service Unavailable 503
badGateway502 :: Status #
Bad Gateway 502
Not Implemented 501
internalServerError500 :: Status #
Internal Server Error 500
requestHeaderFieldsTooLarge431 :: Status #
Request Header Fields Too Large 431 (RFC 6585)
tooManyRequests429 :: Status #
Too Many Requests 429 (RFC 6585)
preconditionRequired428 :: Status #
Precondition Required 428 (RFC 6585)
upgradeRequired426 :: Status #
Upgrade Required 426 (https://tools.ietf.org/html/rfc7231#section-6.5.15)
Upgrade Required 426 (https://tools.ietf.org/html/rfc7231#section-6.5.15)
unprocessableEntity422 :: Status #
Unprocessable Entity 422 (RFC 4918)
imATeapot418 :: Status #
I'm a teapot 418
expectationFailed417 :: Status #
Expectation Failed 417
requestedRangeNotSatisfiable416 :: Status #
Requested Range Not Satisfiable 416
unsupportedMediaType415 :: Status #
Unsupported Media Type 415
requestURITooLong414 :: Status #
Request-URI Too Long 414
requestEntityTooLarge413 :: Status #
Request Entity Too Large 413
preconditionFailed412 :: Status #
Precondition Failed 412
Length Required 411
conflict409 :: Status #
Conflict 409
Request Timeout 408
proxyAuthenticationRequired407 :: Status #
Proxy Authentication Required 407
Not Acceptable 406
methodNotAllowed405 :: Status #
Method Not Allowed 405
notFound404 :: Status #
Not Found 404
forbidden403 :: Status #
Forbidden 403
paymentRequired402 :: Status #
Payment Required 402
Unauthorized 401
badRequest400 :: Status #
Bad Request 400
permanentRedirect308 :: Status #
Permanent Redirect 308
temporaryRedirect307 :: Status #
Temporary Redirect 307
useProxy305 :: Status #
Use Proxy 305
Not Modified 304
seeOther303 :: Status #
See Other 303
movedPermanently301 :: Status #
Moved Permanently 301
multipleChoices300 :: Status #
Multiple Choices 300
Partial Content 206
Reset Content 205
noContent204 :: Status #
No Content 204
nonAuthoritative203 :: Status #
Non-Authoritative Information 203
accepted202 :: Status #
Accepted 202
created201 :: Status #
Created 201
switchingProtocols101 :: Status #
Switching Protocols 101
continue100 :: Status #
Continue 100
mkStatus :: Int -> ByteString -> Status #
Create a Status from status code and message.
HTTP Status.
Only the statusCode
is used for comparisons.
Please use mkStatus
to create status codes from code and message, or the Enum
instance or the
status code constants (like ok200
). There might be additional record members in the future.
Note that the Show instance is only for debugging.
type RequestHeaders = [Header] #
Request Headers
type ResponseHeaders = [Header] #
Response Headers
class Typeable a => ToSchema a where #
Convert a type into
.Schema
An example type and instance:
{-# LANGUAGE OverloadedStrings #-} -- allows to writeText
literals {-# LANGUAGE OverloadedLists #-} -- allows to writeMap
andHashMap
as lists import Control.Lens import Data.Proxy import Data.OpenApi data Coord = Coord { x :: Double, y :: Double } instance ToSchema Coord where declareNamedSchema _ = do doubleSchema <- declareSchemaRef (Proxy :: Proxy Double) return $ NamedSchema (Just "Coord") $ mempty & type_ ?~ OpenApiObject & properties .~ [ ("x", doubleSchema) , ("y", doubleSchema) ] & required .~ [ "x", "y" ]
Instead of manually writing your
instance you can
use a default generic implementation of ToSchema
.declareNamedSchema
To do that, simply add deriving
clause to your datatype
and declare a Generic
instance for your datatype without
giving definition for ToSchema
.declareNamedSchema
For instance, the previous example can be simplified into this:
{-# LANGUAGE DeriveGeneric #-} import GHC.Generics (Generic) data Coord = Coord { x :: Double, y :: Double } deriving Generic instance ToSchema Coord
Nothing
declareNamedSchema :: Proxy a -> Declare (Definitions Schema) NamedSchema #
Convert a type into an optionally named schema together with all used definitions. Note that the schema itself is included in definitions only if it is recursive (and thus needs its definition in scope).
Instances
class ToParamSchema a where #
Convert a type into a plain
.Schema
In previous versions of the package there was a separate type called ParamSchema
, which was
included in a greater Schema
. Now this is a single class, but distinction for schema generators
for "simple" types is preserved.
ToParamSchema
is suited only for primitive-like types without nested fields and such.
An example type and instance:
{-# LANGUAGE OverloadedStrings #-} -- allows to write Text
literals
import Control.Lens
data Direction = Up | Down
instance ToParamSchema Direction where
toParamSchema _ = mempty
& type_ ?~ OpenApiString
& enum_ ?~ [ "Up", "Down" ]
Instead of manually writing your
instance you can
use a default generic implementation of ToParamSchema
.toParamSchema
To do that, simply add deriving
clause to your datatype
and declare a FPFormat
instance for your datatype without
giving definition for ToParamSchema
.toParamSchema
For instance, the previous example can be simplified into this:
{-# LANGUAGE DeriveGeneric #-} import GHC.Generics (Generic) data Direction = Up | Down deriving Generic instance ToParamSchema Direction
Nothing
toParamSchema :: Proxy a -> Schema #
Convert a type into a plain parameter schema.
>>>
BSL.putStrLn $ encodePretty $ toParamSchema (Proxy :: Proxy Integer)
{ "type": "integer" }
Instances
This is the root document object for the API specification.
Instances
paramClasses :: [Name] Source #
bodyClasses :: [Name] Source #
paramBodyClasses :: [Name] Source #
httpClasses :: [Name] Source #
deriveNewtypeParam :: Name -> Q [Dec] Source #
Derives standard WEB-classes for a newtype suitable for request parameter
deriveParam :: Name -> Q [Dec] Source #
Derives standard WEB-classes for a type suitable for request parameter
deriveNewtypeBody :: Name -> Q [Dec] Source #
Derives standard WEB-classes for a newtype suitable for request body or response
deriveBody :: Name -> Q [Dec] Source #
Derives standard WEB-classes for a type suitable for request body or response
deriveNewtypeForm :: Name -> Q [Dec] Source #
Derives standard WEB-classes for a newtype suitable for request form
deriveForm :: Name -> Q [Dec] Source #
Derives standard WEB-classes for a type suitable for request form