packages feed

apiary 1.4.5 → 2.0.0

raw patch · 10 files changed

+340/−70 lines, 10 filesdep +aesondep +directorydep +resourcetdep ~wai-extranew-uploaderPVP ok

version bump matches the API change (PVP)

Dependencies added: aeson, directory, resourcet

Dependency ranges changed: wai-extra

API changes (from Hackage documentation)

- Control.Monad.Apiary: defaultContentType :: ApiaryConfig -> ByteString
- Control.Monad.Apiary: defaultHeaders :: ApiaryConfig -> ResponseHeaders
- Control.Monad.Apiary: defaultStatus :: ApiaryConfig -> Status
- Control.Monad.Apiary: failHeaders :: ApiaryConfig -> ResponseHeaders
- Control.Monad.Apiary: failStatus :: ApiaryConfig -> Status
- Control.Monad.Apiary: mimeType :: ApiaryConfig -> FilePath -> ByteString
- Control.Monad.Apiary: notFound :: ApiaryConfig -> Application
- Control.Monad.Apiary: rootPattern :: ApiaryConfig -> [Text]
- Control.Monad.Apiary.Action: data RequestBody
- Control.Monad.Apiary.Action: documentDescription :: DefaultDocumentConfig -> Maybe Html
- Control.Monad.Apiary.Action: documentGoogleAnalytics :: DefaultDocumentConfig -> Maybe Text
- Control.Monad.Apiary.Action: documentTitle :: DefaultDocumentConfig -> Text
- Control.Monad.Apiary.Action: documentUseCDN :: DefaultDocumentConfig -> Bool
- Control.Monad.Apiary.Filter: instance HasDesc Proxy
- Control.Monad.Apiary.Filter: instance HasDesc QueryKey
- Control.Monad.Apiary.Filter: instance HasDesc SProxy
- Control.Monad.Apiary.Filter: queryKeyDesc :: QueryKey -> Maybe Html
- Data.Apiary.Document: accept :: MethodDoc -> Maybe ByteString
- Data.Apiary.Document: document :: MethodDoc -> Text
- Data.Apiary.Document: groups :: Documents -> [(Text, [PathDoc])]
- Data.Apiary.Document: methods :: PathDoc -> [(Method, [MethodDoc])]
- Data.Apiary.Document: noGroup :: Documents -> [PathDoc]
- Data.Apiary.Document: path :: PathDoc -> Route
- Data.Apiary.Document: preconditions :: MethodDoc -> [Html]
- Data.Apiary.Document: queries :: MethodDoc -> [QueryDoc]
- Data.Apiary.Document: queryDocument :: QueryDoc -> (Maybe Html)
- Data.Apiary.Document: queryName :: QueryDoc -> Text
- Data.Apiary.Document: queryRep :: QueryDoc -> QueryRep
- Data.Apiary.Document: queryStrategy :: QueryDoc -> StrategyRep
- Data.Apiary.Document.Html: documentDescription :: DefaultDocumentConfig -> Maybe Html
- Data.Apiary.Document.Html: documentGoogleAnalytics :: DefaultDocumentConfig -> Maybe Text
- Data.Apiary.Document.Html: documentTitle :: DefaultDocumentConfig -> Text
- Data.Apiary.Document.Html: documentUseCDN :: DefaultDocumentConfig -> Bool
- Data.Apiary.Document.Html: instance Default DefaultDocumentConfig
- Data.Apiary.Method: instance Eq Method
- Data.Apiary.Method: instance Hashable Method
- Data.Apiary.Method: instance IsString Method
- Data.Apiary.Method: instance Ord Method
- Data.Apiary.Method: instance Read Method
- Data.Apiary.Method: instance Show Method
- Data.Apiary.Param: fileContent :: File -> ByteString
- Data.Apiary.Param: fileContentType :: File -> ByteString
- Data.Apiary.Param: fileName :: File -> ByteString
- Data.Apiary.Param: fileParameter :: File -> ByteString
- Data.Apiary.Param: instance Eq File
- Data.Apiary.Param: instance Eq QueryRep
- Data.Apiary.Param: instance Eq StrategyRep
- Data.Apiary.Param: instance Path Bool
- Data.Apiary.Param: instance Path ByteString
- Data.Apiary.Param: instance Path Char
- Data.Apiary.Param: instance Path Day
- Data.Apiary.Param: instance Path Double
- Data.Apiary.Param: instance Path Float
- Data.Apiary.Param: instance Path Int
- Data.Apiary.Param: instance Path Int16
- Data.Apiary.Param: instance Path Int32
- Data.Apiary.Param: instance Path Int64
- Data.Apiary.Param: instance Path Int8
- Data.Apiary.Param: instance Path Integer
- Data.Apiary.Param: instance Path String
- Data.Apiary.Param: instance Path Text
- Data.Apiary.Param: instance Path Word
- Data.Apiary.Param: instance Path Word16
- Data.Apiary.Param: instance Path Word32
- Data.Apiary.Param: instance Path Word64
- Data.Apiary.Param: instance Path Word8
- Data.Apiary.Param: instance Query ()
- Data.Apiary.Param: instance Query Bool
- Data.Apiary.Param: instance Query ByteString
- Data.Apiary.Param: instance Query Day
- Data.Apiary.Param: instance Query Double
- Data.Apiary.Param: instance Query Float
- Data.Apiary.Param: instance Query Int
- Data.Apiary.Param: instance Query Int16
- Data.Apiary.Param: instance Query Int32
- Data.Apiary.Param: instance Query Int64
- Data.Apiary.Param: instance Query Int8
- Data.Apiary.Param: instance Query Integer
- Data.Apiary.Param: instance Query String
- Data.Apiary.Param: instance Query Text
- Data.Apiary.Param: instance Query Word
- Data.Apiary.Param: instance Query Word16
- Data.Apiary.Param: instance Query Word32
- Data.Apiary.Param: instance Query Word64
- Data.Apiary.Param: instance Query Word8
- Data.Apiary.Param: instance Query a => Query (Maybe a)
- Data.Apiary.Param: instance Query a => ReqParam a
- Data.Apiary.Param: instance ReqParam File
- Data.Apiary.Param: instance Show File
- Data.Apiary.Param: instance Show QueryRep
- Data.Apiary.Param: instance Show StrategyRep
- Data.Apiary.Param: instance Strategy First
- Data.Apiary.Param: instance Strategy Many
- Data.Apiary.Param: instance Strategy One
- Data.Apiary.Param: instance Strategy Option
- Data.Apiary.Param: instance Strategy Optional
- Data.Apiary.Param: instance Strategy Some
- Data.Apiary.Param: instance Typeable File
- Data.Apiary.Param: instance Typeable Text
- Data.Apiary.Param: strategyInfo :: StrategyRep -> Text
- Web.Apiary.Heroku: defaultPort :: HerokuConfig -> Int
- Web.Apiary.Heroku: herokuApiaryConfig :: HerokuConfig -> ApiaryConfig
- Web.Apiary.Heroku: herokuAppName :: HerokuConfig -> Maybe String
- Web.Apiary.Heroku: herokuExecutableName :: HerokuConfig -> String
- Web.Apiary.Heroku: instance Default HerokuConfig
- Web.Apiary.Heroku: instance Extension Heroku
+ Control.Monad.Apiary: [defaultContentType] :: ApiaryConfig -> ByteString
+ Control.Monad.Apiary: [defaultHeaders] :: ApiaryConfig -> ResponseHeaders
+ Control.Monad.Apiary: [defaultStatus] :: ApiaryConfig -> Status
+ Control.Monad.Apiary: [failHeaders] :: ApiaryConfig -> ResponseHeaders
+ Control.Monad.Apiary: [failStatus] :: ApiaryConfig -> Status
+ Control.Monad.Apiary: [maxRequestSize] :: ApiaryConfig -> Word64
+ Control.Monad.Apiary: [mimeType] :: ApiaryConfig -> FilePath -> ByteString
+ Control.Monad.Apiary: [notFound] :: ApiaryConfig -> Application
+ Control.Monad.Apiary: [rootPattern] :: ApiaryConfig -> [Text]
+ Control.Monad.Apiary: [uploadFilePath] :: ApiaryConfig -> Maybe FilePath
+ Control.Monad.Apiary: getApiary :: Monad m => ApiaryConfig -> ApiaryT '[] '[] IO m () -> m Application
+ Control.Monad.Apiary: getApiaryTWith :: (Monad actM, Monad m) => (forall b. actM b -> IO b) -> Initializer m '[] exts -> ApiaryConfig -> ApiaryT exts '[] actM m () -> m Application
+ Control.Monad.Apiary: getApiaryWith :: Monad m => Initializer m '[] exts -> ApiaryConfig -> ApiaryT exts '[] IO m () -> m Application
+ Control.Monad.Apiary.Action: [documentDescription] :: DefaultDocumentConfig -> Maybe Html
+ Control.Monad.Apiary.Action: [documentGoogleAnalytics] :: DefaultDocumentConfig -> Maybe Text
+ Control.Monad.Apiary.Action: [documentTitle] :: DefaultDocumentConfig -> Text
+ Control.Monad.Apiary.Action: [documentUseCDN] :: DefaultDocumentConfig -> Bool
+ Control.Monad.Apiary.Action: application :: Monad m => Application -> ActionT exts prms m a
+ Control.Monad.Apiary.Action: data ActionReqBody
+ Control.Monad.Apiary.Filter: [queryKeyDesc] :: QueryKey -> Maybe Html
+ Control.Monad.Apiary.Filter: instance Control.Monad.Apiary.Filter.HasDesc Control.Monad.Apiary.Filter.QueryKey
+ Control.Monad.Apiary.Filter: instance Control.Monad.Apiary.Filter.HasDesc Data.Apiary.SProxy.SProxy
+ Control.Monad.Apiary.Filter: instance Control.Monad.Apiary.Filter.HasDesc Data.Proxy.Proxy
+ Control.Monad.Apiary.Filter: jsonReqBody :: (KnownSymbol k, MonadIO actM, k </ prms, FromJSON a) => proxy k -> Filter exts actM m prms ((k := a) : prms)
+ Data.Apiary.Document: [accept] :: MethodDoc -> Maybe ByteString
+ Data.Apiary.Document: [document] :: MethodDoc -> Text
+ Data.Apiary.Document: [groups] :: Documents -> [(Text, [PathDoc])]
+ Data.Apiary.Document: [methods] :: PathDoc -> [(Method, [MethodDoc])]
+ Data.Apiary.Document: [noGroup] :: Documents -> [PathDoc]
+ Data.Apiary.Document: [path] :: PathDoc -> Route
+ Data.Apiary.Document: [preconditions] :: MethodDoc -> [Html]
+ Data.Apiary.Document: [queries] :: MethodDoc -> [QueryDoc]
+ Data.Apiary.Document: [queryDocument] :: QueryDoc -> (Maybe Html)
+ Data.Apiary.Document: [queryName] :: QueryDoc -> Text
+ Data.Apiary.Document: [queryRep] :: QueryDoc -> QueryRep
+ Data.Apiary.Document: [queryStrategy] :: QueryDoc -> StrategyRep
+ Data.Apiary.Document.Html: [documentDescription] :: DefaultDocumentConfig -> Maybe Html
+ Data.Apiary.Document.Html: [documentGoogleAnalytics] :: DefaultDocumentConfig -> Maybe Text
+ Data.Apiary.Document.Html: [documentTitle] :: DefaultDocumentConfig -> Text
+ Data.Apiary.Document.Html: [documentUseCDN] :: DefaultDocumentConfig -> Bool
+ Data.Apiary.Document.Html: instance Data.Default.Class.Default Data.Apiary.Document.Html.DefaultDocumentConfig
+ Data.Apiary.Method: instance Data.Hashable.Class.Hashable Data.Apiary.Method.Method
+ Data.Apiary.Method: instance Data.String.IsString Data.Apiary.Method.Method
+ Data.Apiary.Method: instance GHC.Classes.Eq Data.Apiary.Method.Method
+ Data.Apiary.Method: instance GHC.Classes.Ord Data.Apiary.Method.Method
+ Data.Apiary.Method: instance GHC.Read.Read Data.Apiary.Method.Method
+ Data.Apiary.Method: instance GHC.Show.Show Data.Apiary.Method.Method
+ Data.Apiary.Param: [fileContentType] :: File -> ByteString
+ Data.Apiary.Param: [fileContent] :: File -> Either ByteString FilePath
+ Data.Apiary.Param: [fileName] :: File -> ByteString
+ Data.Apiary.Param: [fileParameter] :: File -> ByteString
+ Data.Apiary.Param: [strategyInfo] :: StrategyRep -> Text
+ Data.Apiary.Param: instance Data.Apiary.Param.Path Data.ByteString.Internal.ByteString
+ Data.Apiary.Param: instance Data.Apiary.Param.Path Data.ByteString.Lazy.Internal.ByteString
+ Data.Apiary.Param: instance Data.Apiary.Param.Path Data.Text.Internal.Lazy.Text
+ Data.Apiary.Param: instance Data.Apiary.Param.Path Data.Text.Internal.Text
+ Data.Apiary.Param: instance Data.Apiary.Param.Path Data.Time.Calendar.Days.Day
+ Data.Apiary.Param: instance Data.Apiary.Param.Path GHC.Base.String
+ Data.Apiary.Param: instance Data.Apiary.Param.Path GHC.Int.Int16
+ Data.Apiary.Param: instance Data.Apiary.Param.Path GHC.Int.Int32
+ Data.Apiary.Param: instance Data.Apiary.Param.Path GHC.Int.Int64
+ Data.Apiary.Param: instance Data.Apiary.Param.Path GHC.Int.Int8
+ Data.Apiary.Param: instance Data.Apiary.Param.Path GHC.Integer.Type.Integer
+ Data.Apiary.Param: instance Data.Apiary.Param.Path GHC.Types.Bool
+ Data.Apiary.Param: instance Data.Apiary.Param.Path GHC.Types.Char
+ Data.Apiary.Param: instance Data.Apiary.Param.Path GHC.Types.Double
+ Data.Apiary.Param: instance Data.Apiary.Param.Path GHC.Types.Float
+ Data.Apiary.Param: instance Data.Apiary.Param.Path GHC.Types.Int
+ Data.Apiary.Param: instance Data.Apiary.Param.Path GHC.Types.Word
+ Data.Apiary.Param: instance Data.Apiary.Param.Path GHC.Word.Word16
+ Data.Apiary.Param: instance Data.Apiary.Param.Path GHC.Word.Word32
+ Data.Apiary.Param: instance Data.Apiary.Param.Path GHC.Word.Word64
+ Data.Apiary.Param: instance Data.Apiary.Param.Path GHC.Word.Word8
+ Data.Apiary.Param: instance Data.Apiary.Param.Query ()
+ Data.Apiary.Param: instance Data.Apiary.Param.Query Data.ByteString.Internal.ByteString
+ Data.Apiary.Param: instance Data.Apiary.Param.Query Data.ByteString.Lazy.Internal.ByteString
+ Data.Apiary.Param: instance Data.Apiary.Param.Query Data.Text.Internal.Lazy.Text
+ Data.Apiary.Param: instance Data.Apiary.Param.Query Data.Text.Internal.Text
+ Data.Apiary.Param: instance Data.Apiary.Param.Query Data.Time.Calendar.Days.Day
+ Data.Apiary.Param: instance Data.Apiary.Param.Query GHC.Base.String
+ Data.Apiary.Param: instance Data.Apiary.Param.Query GHC.Int.Int16
+ Data.Apiary.Param: instance Data.Apiary.Param.Query GHC.Int.Int32
+ Data.Apiary.Param: instance Data.Apiary.Param.Query GHC.Int.Int64
+ Data.Apiary.Param: instance Data.Apiary.Param.Query GHC.Int.Int8
+ Data.Apiary.Param: instance Data.Apiary.Param.Query GHC.Integer.Type.Integer
+ Data.Apiary.Param: instance Data.Apiary.Param.Query GHC.Types.Bool
+ Data.Apiary.Param: instance Data.Apiary.Param.Query GHC.Types.Double
+ Data.Apiary.Param: instance Data.Apiary.Param.Query GHC.Types.Float
+ Data.Apiary.Param: instance Data.Apiary.Param.Query GHC.Types.Int
+ Data.Apiary.Param: instance Data.Apiary.Param.Query GHC.Types.Word
+ Data.Apiary.Param: instance Data.Apiary.Param.Query GHC.Word.Word16
+ Data.Apiary.Param: instance Data.Apiary.Param.Query GHC.Word.Word32
+ Data.Apiary.Param: instance Data.Apiary.Param.Query GHC.Word.Word64
+ Data.Apiary.Param: instance Data.Apiary.Param.Query GHC.Word.Word8
+ Data.Apiary.Param: instance Data.Apiary.Param.Query a => Data.Apiary.Param.Query (GHC.Base.Maybe a)
+ Data.Apiary.Param: instance Data.Apiary.Param.Query a => Data.Apiary.Param.ReqParam a
+ Data.Apiary.Param: instance Data.Apiary.Param.ReqParam Data.Apiary.Param.File
+ Data.Apiary.Param: instance Data.Apiary.Param.Strategy Data.Apiary.Param.First
+ Data.Apiary.Param: instance Data.Apiary.Param.Strategy Data.Apiary.Param.Many
+ Data.Apiary.Param: instance Data.Apiary.Param.Strategy Data.Apiary.Param.One
+ Data.Apiary.Param: instance Data.Apiary.Param.Strategy Data.Apiary.Param.Option
+ Data.Apiary.Param: instance Data.Apiary.Param.Strategy Data.Apiary.Param.Optional
+ Data.Apiary.Param: instance Data.Apiary.Param.Strategy Data.Apiary.Param.Some
+ Data.Apiary.Param: instance GHC.Classes.Eq Data.Apiary.Param.File
+ Data.Apiary.Param: instance GHC.Classes.Eq Data.Apiary.Param.QueryRep
+ Data.Apiary.Param: instance GHC.Classes.Eq Data.Apiary.Param.StrategyRep
+ Data.Apiary.Param: instance GHC.Show.Show Data.Apiary.Param.File
+ Data.Apiary.Param: instance GHC.Show.Show Data.Apiary.Param.QueryRep
+ Data.Apiary.Param: instance GHC.Show.Show Data.Apiary.Param.StrategyRep
+ Web.Apiary.Heroku: [defaultPort] :: HerokuConfig -> Int
+ Web.Apiary.Heroku: [herokuApiaryConfig] :: HerokuConfig -> ApiaryConfig
+ Web.Apiary.Heroku: [herokuAppName] :: HerokuConfig -> Maybe String
+ Web.Apiary.Heroku: [herokuExecutableName] :: HerokuConfig -> String
+ Web.Apiary.Heroku: instance Control.Monad.Apiary.Action.Internal.Extension Web.Apiary.Heroku.Heroku
+ Web.Apiary.Heroku: instance Data.Default.Class.Default Web.Apiary.Heroku.HerokuConfig
- Control.Monad.Apiary: ApiaryConfig :: Application -> Status -> ResponseHeaders -> ByteString -> Status -> ResponseHeaders -> [Text] -> (FilePath -> ByteString) -> ApiaryConfig
+ Control.Monad.Apiary: ApiaryConfig :: Application -> Status -> ResponseHeaders -> ByteString -> Status -> ResponseHeaders -> Word64 -> Maybe FilePath -> [Text] -> (FilePath -> ByteString) -> ApiaryConfig
- Control.Monad.Apiary: runApiary :: Monad m => (Application -> m a) -> ApiaryConfig -> ApiaryT [] [] IO m () -> m a
+ Control.Monad.Apiary: runApiary :: Monad m => (Application -> m a) -> ApiaryConfig -> ApiaryT '[] '[] IO m () -> m a
- Control.Monad.Apiary: runApiaryTWith :: (Monad actM, Monad m) => (forall b. actM b -> IO b) -> (Application -> m a) -> Initializer m [] exts -> ApiaryConfig -> ApiaryT exts [] actM m () -> m a
+ Control.Monad.Apiary: runApiaryTWith :: (Monad actM, Monad m) => (forall b. actM b -> IO b) -> (Application -> m a) -> Initializer m '[] exts -> ApiaryConfig -> ApiaryT exts '[] actM m () -> m a
- Control.Monad.Apiary: runApiaryWith :: Monad m => (Application -> m a) -> Initializer m [] exts -> ApiaryConfig -> ApiaryT exts [] IO m () -> m a
+ Control.Monad.Apiary: runApiaryWith :: Monad m => (Application -> m a) -> Initializer m '[] exts -> ApiaryConfig -> ApiaryT exts '[] IO m () -> m a
- Control.Monad.Apiary.Action: Multipart :: {-# UNPACK #-} !ByteString -> [Param] -> [File] -> RequestBody
+ Control.Monad.Apiary.Action: Multipart :: [Param] -> [File] -> ActionReqBody
- Control.Monad.Apiary.Action: Unknown :: ByteString -> RequestBody
+ Control.Monad.Apiary.Action: Unknown :: ByteString -> ActionReqBody
- Control.Monad.Apiary.Action: UrlEncoded :: [Param] -> [File] -> RequestBody
+ Control.Monad.Apiary.Action: UrlEncoded :: [Param] -> ActionReqBody
- Control.Monad.Apiary.Action: applyDict :: Dict prms -> ActionT exts prms m a -> ActionT exts [] m a
+ Control.Monad.Apiary.Action: applyDict :: Dict prms -> ActionT exts prms m a -> ActionT exts '[] m a
- Control.Monad.Apiary.Action: getReqBody :: MonadIO m => ActionT exts prms m RequestBody
+ Control.Monad.Apiary.Action: getReqBody :: MonadIO m => ActionT exts prms m ActionReqBody
- Control.Monad.Apiary.Filter: focus :: Monad actM => (Doc -> Doc) -> Maybe Method -> (Path prms' (ActionT exts [] actM) () -> Path prms (ActionT exts [] actM) ()) -> Filter exts actM m prms prms'
+ Control.Monad.Apiary.Filter: focus :: Monad actM => (Doc -> Doc) -> Maybe Method -> (Path prms' (ActionT exts '[] actM) () -> Path prms (ActionT exts '[] actM) ()) -> Filter exts actM m prms prms'
- Data.Apiary.Extension: type Middleware' = forall exts. ActionT exts [] IO () -> ActionT exts [] IO ()
+ Data.Apiary.Extension: type Middleware' = forall exts. ActionT exts '[] IO () -> ActionT exts '[] IO ()
- Data.Apiary.Param: File :: ByteString -> ByteString -> ByteString -> ByteString -> File
+ Data.Apiary.Param: File :: ByteString -> ByteString -> ByteString -> Either ByteString FilePath -> File
- Web.Apiary.Heroku: runHeroku :: MonadIO m => (Int -> Application -> m a) -> HerokuConfig -> ApiaryT '[Heroku] [] IO m () -> m a
+ Web.Apiary.Heroku: runHeroku :: MonadIO m => (Int -> Application -> m a) -> HerokuConfig -> ApiaryT '[Heroku] '[] IO m () -> m a
- Web.Apiary.Heroku: runHerokuTWith :: (MonadIO m, Monad actM) => (forall b. actM b -> IO b) -> (Int -> Application -> m a) -> Initializer m '[Heroku] exts -> HerokuConfig -> ApiaryT exts [] actM m () -> m a
+ Web.Apiary.Heroku: runHerokuTWith :: (MonadIO m, Monad actM) => (forall b. actM b -> IO b) -> (Int -> Application -> m a) -> Initializer m '[Heroku] exts -> HerokuConfig -> ApiaryT exts '[] actM m () -> m a
- Web.Apiary.Heroku: runHerokuWith :: MonadIO m => (Int -> Application -> m a) -> Initializer m '[Heroku] exts -> HerokuConfig -> ApiaryT exts [] IO m () -> m a
+ Web.Apiary.Heroku: runHerokuWith :: MonadIO m => (Int -> Application -> m a) -> Initializer m '[Heroku] exts -> HerokuConfig -> ApiaryT exts '[] IO m () -> m a

Files

CHANGELOG.md view
@@ -1,3 +1,10 @@+# 2.0.0+* add methods to get application from Apiary monad stack.+* add Control.Monad.Apiary.Action.application to mount a wai application at certain route.+* rewrite getReqBody to allow file upload with temp file backend, check out ApiaryConfig and Data.Param.File.+* guard against large request body.+* add Control.Monad.Filter.jsonReqBody to do type level route based on request body's JSON type.+ # 1.4.5 * fix: MonadPlus lows of ApiaryT Monad 
apiary.cabal view
@@ -1,5 +1,5 @@ name:                apiary-version:             1.4.5+version:             2.0.0 synopsis:            Simple and type safe web framework that generate web API documentation. description:   Simple and type safe web framework that can be automatically generate API documentation.@@ -95,8 +95,9 @@                      , mtl                  >=2.1   && <2.3                      , monad-control        >=0.3   && <1.1                      , exceptions           >=0.6   && <0.9+                     , resourcet            >=0.4.6 && <1.2 -                     , http-types           >=0.8   && <0.9+                     , http-types           >=0.8   && <0.10                      , mime-types           >=0.1   && <0.2                       , text                 >=1.1   && <1.3@@ -107,6 +108,7 @@                      , blaze-markup         >=0.6   && <0.8                      , case-insensitive     >=1.1   && <1.3                      , vault                >=0.3   && <0.4+                     , aeson                >=0.8   && <0.12                       , data-default-class   >=0.0   && <0.1                      , unordered-containers >=0.2   && <0.3@@ -116,8 +118,8 @@                      , unix-compat          >=0.4   && <0.5                      , http-date            >=0.0   && <0.1 -                     , wai                  >=3.0   && <3.1-                     , wai-extra            >=3.0   && <3.1+                     , wai                  >=3.0   && <3.3+                     , wai-extra            >=3.0.15 && <3.1                      , stringsearch         >=0.3   && <0.4                      , web-routing          >=0.6   && <0.7                      , types-compat         >=0.1   && <0.2@@ -134,15 +136,17 @@   type:                exitcode-stdio-1.0   build-depends:       base                      >=4.6   && <4.9                      , mtl                       >=2.1   && <2.3-                     , tasty                     >=0.10  && <0.11+                     , tasty                     >=0.10  && <0.12                      , tasty-hunit               >=0.9   && <0.10                      , tasty-quickcheck          >=0.8   && <0.9                      , apiary                      , bytestring           >=0.10  && <0.11-                     , http-types           >=0.8   && <0.9+                     , http-types           >=0.8   && <0.10                      , HUnit                >=1.2   && <1.4-                     , wai                  >=3.0   && <3.1+                     , wai                  >=3.0   && <3.3                      , wai-extra            >=3.0   && <3.1+                     , directory+                     , aeson                >=0.8   && <0.12    hs-source-dirs:      tests   ghc-options:         -Wall -fno-warn-missing-signatures
src/Control/Monad/Apiary.hs view
@@ -5,6 +5,9 @@     , runApiaryTWith     , runApiaryWith     , runApiary+    , getApiaryTWith+    , getApiaryWith+    , getApiary     , ApiaryConfig(..)     -- * execute action     , action
src/Control/Monad/Apiary/Action.hs view
@@ -1,7 +1,8 @@-module Control.Monad.Apiary.Action +module Control.Monad.Apiary.Action     ( ActionT     , hoistActionT     -- * stop action+    , application     , stop      -- * getter@@ -46,7 +47,7 @@     , getParams     , getQueryParams -    , RequestBody(..), getReqBody+    , ActionReqBody(..), getReqBody     , getReqBodyParams     , getReqBodyFiles     -- ** setter
src/Control/Monad/Apiary/Action/Internal.hs view
@@ -18,6 +18,7 @@ module Control.Monad.Apiary.Action.Internal     ( ActionT +    , application     , stop      , param@@ -57,7 +58,8 @@     , getQueryParams     , getReqBodyParams     , getReqBodyFiles-    , RequestBody(..)+    , getReqBodyJSON+    , ActionReqBody(..)     , getReqBody      , devFile@@ -78,7 +80,7 @@     , getConfig     , getState     , modifyState-    , getRequestBody+    , getReqBodyInternal     , execActionT     , applyDict @@ -109,12 +111,15 @@     (MonadTransControl(..), MonadBaseControl(..)     , ComposeSt     , defaultLiftBaseWith, defaultRestoreM)+import Control.Exception (try, onException)+import Control.Monad.Trans.Resource (createInternalState, closeInternalState)  import Network.Mime(defaultMimeLookup) import Network.HTTP.Date(parseHTTPDate, epochTimeToHTTPDate, formatHTTPDate) import qualified Network.HTTP.Types as HTTP import qualified Network.Wai as Wai import qualified Network.Wai.Parse as P+import Network.Wai.Request (requestSizeCheck, RequestSizeException(..))  import qualified Network.Routing.Dict as Dict import Data.Apiary.Param(Param, File(..))@@ -133,6 +138,9 @@ import qualified Data.Text as T import qualified Data.Text.Lazy as TL import qualified Data.Vault.Lazy as V+import Data.Word (Word64)+import Data.Aeson (FromJSON)+import qualified Data.Aeson as JSON  data ApiaryConfig = ApiaryConfig     { -- | call when no handler matched.@@ -144,6 +152,14 @@     , defaultContentType  :: S.ByteString     , failStatus          :: HTTP.Status     , failHeaders         :: HTTP.ResponseHeaders+      -- | maximum request size, default to 5MB. since 2.0.0.+    , maxRequestSize      :: Word64+      -- | where to store upload file. since 2.0.0.+      --+      -- default to 'Nothing', which saves file content in memory.+      -- NOTE. once you set this value to some path,+      -- it's your responsibility to clean uploaded files. eg. move or remove it.+    , uploadFilePath      :: Maybe FilePath       -- | used by 'Control.Monad.Apiary.Filter.root' filter.     , rootPattern         :: [T.Text]     , mimeType            :: FilePath -> S.ByteString@@ -160,7 +176,7 @@ defaultNotFound _ f = f      $ Wai.responseLBS HTTP.status404 [("Content-Type", "text/plain")] "404 Page Notfound.\n"  instance Default ApiaryConfig where-    def = ApiaryConfig +    def = ApiaryConfig         { notFound            = defaultNotFound         , defaultStatus       = HTTP.ok200         , defaultHeaders      = []@@ -168,6 +184,8 @@         , failStatus          = HTTP.internalServerError500         , failHeaders         = []         , rootPattern         = ["index.html", "index.htm"]+        , maxRequestSize      = 5242880+        , uploadFilePath      = Nothing         , mimeType            = defaultMimeLookup . T.pack         } @@ -197,13 +215,11 @@  -------------------------------------------------------------------------------- -data RequestBody-    = Unknown S.ByteString -- ^ raw body-    | UrlEncoded [Param] [File]-    | Multipart-        {-#UNPACK#-}!S.ByteString-        [Param]-        [File] -- ^ boundary params files+data ActionReqBody+    = Unknown L.ByteString -- ^ raw body+    | UrlEncoded [Param]   -- ^ url-encoded params+    | Multipart [Param] [File] -- ^ boundary params files+  deriving (Show, Eq)  data ActionState = ActionState     { actionResponse    :: ResponseBody@@ -211,7 +227,7 @@     , actionHeaders     :: HTTP.ResponseHeaders     , actionVault       :: V.Vault     , actionContentType :: S.ByteString-    , actionReqBody     :: Maybe RequestBody+    , actionReqBody     :: Maybe ActionReqBody     }  initialState :: ApiaryConfig -> ActionState@@ -256,12 +272,13 @@     , actionExts      :: Extensions exts     } -data Action a +data Action a     = Continue ActionState a-    | Pass (Maybe RequestBody)+    | Pass (Maybe ActionReqBody)     | Stop Wai.Response+    | App Wai.Application -newtype ActionT exts prms m a = ActionT { unActionT :: forall b. +newtype ActionT exts prms m a = ActionT { unActionT :: forall b.     Dict.Dict prms     -> ActionEnv exts     -> ActionState@@ -276,15 +293,22 @@     return (Continue st' a) {-# INLINE runActionT #-} -actionT :: Monad m +actionT :: Monad m         => (Dict.Dict prms -> ActionEnv exts -> ActionState -> m (Action a))         -> ActionT exts prms m a actionT f = ActionT $ \dict env !st cont -> f dict env st >>= \case     Pass b          -> return $ Pass b     Stop s          -> return $ Stop s     Continue !st' a -> cont a st'+    App a           -> return $ App a {-# INLINE actionT #-} +-- | stop and proxy current request to a 'Wai.Application', since 2.0.0.+application :: Monad m+        => Wai.Application+        -> ActionT exts prms m a+application app = ActionT $ \_ _ _ _ -> return $ App app+ -- | n must be Monad, so cant be MFunctor. hoistActionT :: (Monad m, Monad n)              => (forall b. m b -> n b) -> ActionT exts prms m a -> ActionT exts prms n a@@ -296,11 +320,12 @@ {-# INLINE applyDict #-}  execActionT :: ApiaryConfig -> Extensions exts -> Documents -> ActionT exts '[] IO () -> Wai.Application-execActionT config exts doc m request send = +execActionT config exts doc m request send =     runActionT m Dict.emptyDict (ActionEnv config request doc exts) (initialState config) >>= \case         Pass _       -> notFound config request send         Stop s       -> send s         Continue r _ -> send $ toResponse r+        App a        -> a request send  -------------------------------------------------------------------------------- @@ -360,6 +385,7 @@         Continue !st a -> return $ Continue st a         Stop stp       -> return $ Stop stp         Pass b         -> unActionT n dict e s { actionReqBody = b } cont+        App a          -> return $ App a     {-# INLINE mzero #-}     {-# INLINE mplus #-} @@ -374,7 +400,7 @@     restoreT m = actionT $ \_ _ _ -> m #else     newtype StT (ActionT exts prms) a = StActionT { unStActionT :: Action a }-    liftWith f = actionT $ \prms e !s -> +    liftWith f = actionT $ \prms e !s ->         liftM (\a -> Continue s a) (f $ \t -> liftM StActionT $ runActionT t prms e s)     restoreT m = actionT $ \_ _ _ -> liftM unStActionT m #endif@@ -444,51 +470,102 @@     , quoteDec  = error "params QQ is defined only exp."     } +-- | only get parameters in query string.+getQueryParams :: Monad m => ActionT exts prms m HTTP.Query+getQueryParams = Wai.queryString <$> getRequest+ getDocuments :: Monad m => ActionT exts prms m Documents getDocuments = liftM actionDocuments getEnv --- | parse request body and return it. since 1.2.2.-getReqBody :: MonadIO m => ActionT exts prms m RequestBody+-- | parse request body into 'ActionReqBody' and return it. since 1.2.2.+getReqBody :: MonadIO m => ActionT exts prms m ActionReqBody getReqBody = ActionT $ \_ e s c -> case actionReqBody s of     Just  b -> c b s     Nothing -> do         let req  = actionRequest e-            body = Wai.requestBody req-        b <- liftIO $ case P.getRequestBodyType req of-            Nothing                  -> Unknown `liftM` body-            Just typ@P.UrlEncoded    -> sink UrlEncoded typ body-            Just typ@(P.Multipart b) -> sink (Multipart b) typ body-        c b s { actionReqBody = Just b }+            config = actionConfig e+            rbody = Wai.requestBody =<< requestSizeCheck (maxRequestSize config) req++        b <- liftIO $ try (+            case P.getRequestBodyType req of+                Nothing                  -> sinkRaw rbody+                Just typ@P.UrlEncoded    -> sinkUrlEncoded typ rbody+                Just typ@(P.Multipart _) ->+                    case uploadFilePath config of+                        Nothing -> sinkMultipartLBS typ rbody+                        Just p  -> sinkMultipartToDisk p typ rbody+            )+        case b of+            Left (RequestSizeException limit) ->+                return $ Stop $ Wai.responseLBS HTTP.status413 [] $ B.toLazyByteString $+                    "Request body is too large(limit is "+                        `mappend` B.fromString (show limit) `mappend` " bytes)"+            Left _   ->+                return $ Stop $ Wai.responseLBS HTTP.status400 [] $ "Bad Request"++            Right b' ->+                c b' s { actionReqBody = Just b' }   where-    sink con typ body = do-        (p, f) <- P.sinkRequestBody P.lbsBackEnd typ body-        return $ con p (map convFile f)+    sinkRaw rbody = do+        let loop front = do+                bs <- rbody+                if S.null bs+                    then return $ L.fromChunks $ front []+                    else loop $ front . (bs:)+        Unknown `liftM` loop id -    convFile (p, P.FileInfo{..}) = File p fileName fileContentType fileContent+    sinkUrlEncoded typ rbody = do+        (p, _) <- P.sinkRequestBody P.lbsBackEnd typ rbody+        return (UrlEncoded p) -getQueryParams :: Monad m => ActionT exts prms m HTTP.Query-getQueryParams = Wai.queryString <$> getRequest+    sinkMultipartLBS typ rbody = do+        (p, f) <- P.sinkRequestBody P.lbsBackEnd typ rbody+        let f' = map (\ (pn, P.FileInfo{..})+                    -> File pn fileName fileContentType (Left fileContent)+                ) f+        return (Multipart p f') -getRequestBody :: MonadIO m => ActionT exts prms m ([Param], [File])-getRequestBody = getReqBody >>= return . \case+    sinkMultipartToDisk path typ rbody = do+        internalState <- createInternalState+        (p, f) <-+            P.sinkRequestBody+                (P.tempFileBackEndOpts (return path) "apiaryUpload" internalState)+                typ+                rbody+            `onException` closeInternalState internalState+        let f' = map (\ (pn, P.FileInfo{..})+                    -> File pn fileName fileContentType (Right fileContent)+                ) f+        return (Multipart p f')++getReqBodyInternal :: MonadIO m => ActionT exts prms m ([Param], [File])+getReqBodyInternal = getReqBody >>= return . \case     Unknown _       -> ([], [])-    UrlEncoded  p f -> (p, f)-    Multipart _ p f -> (p, f)+    UrlEncoded  p   -> (p, [])+    Multipart   p f -> (p, f)  -- | parse request body and return params. since 1.0.0. getReqBodyParams :: MonadIO m => ActionT exts prms m [Param] getReqBodyParams = getReqBody >>= return . \case     Unknown _       -> []-    UrlEncoded  p _ -> p-    Multipart _ p _ -> p+    UrlEncoded  p   -> p+    Multipart   p _ -> p  -- | parse request body and return files. since 0.9.0.0. getReqBodyFiles :: MonadIO m => ActionT exts prms m [File] getReqBodyFiles = getReqBody >>= return . \case-    Unknown _       -> []-    UrlEncoded  _ f -> f-    Multipart _ _ f -> f+    Multipart   _ f -> f+    _               -> [] +-- | parse request body and try parse it as JSON.+--+-- it's recommended to use 'Control.Monad.Apiary.Filter.jsonReqBody' filter+-- to leverage type level routing instead of 'getReqBodyJSON'. since 2.0.0.+getReqBodyJSON :: (MonadIO m, FromJSON a) => ActionT exts prms m (Maybe a)+getReqBodyJSON = getReqBody >>= return . \case+    Unknown lbs     -> JSON.decode' lbs+    _               -> Nothing+ -- | get all request headers. since 0.6.0.0. getHeaders :: Monad m => ActionT exts prms m HTTP.RequestHeaders getHeaders = Wai.requestHeaders `liftM` getRequest@@ -591,7 +668,7 @@ -- -- 303 See Other (HTTP/1.1)  or -- 302 Moved Temporarily (Other)--- +-- -- since 0.6.2.0. redirect :: Monad m => S.ByteString -> ActionT exts prms m () redirect to = do
src/Control/Monad/Apiary/Filter.hs view
@@ -24,6 +24,7 @@     -- * header matcher     , eqHeader     , header+    , jsonReqBody     , accept      -- * other@@ -47,8 +48,8 @@ import Control.Monad.Trans(MonadIO)  import Control.Monad.Apiary.Action.Internal-    ( getQueryParams, getRequestBody-    , getRequest, ContentType, contentType+    ( getQueryParams, getReqBodyInternal+    , getRequest, getReqBodyJSON, ContentType, contentType     , getConfig, ApiaryConfig(..)     ) @@ -78,6 +79,7 @@     , pFirst, pOne, pOption, pOptional, pMany, pSome     ) import Data.Apiary.Method(Method)+import Data.Aeson (FromJSON)  -- | filter by HTTP method. since 0.1.0.0. --@@ -145,7 +147,7 @@       => query k -> strategy v -> Filter exts actM m prms (SNext strategy k v prms) query k w = focus doc Nothing $ R.raw "query" $ \d t -> do     qs      <- getQueryParams-    (ps,fs) <- getRequestBody+    (ps,fs) <- getReqBodyInternal     let as = map snd . filter ((SC.pack (symbolVal k) ==) . fst) $ reqParams (Proxy :: Proxy v) qs ps fs     case strategy w k as d of         Nothing -> mzero@@ -218,7 +220,7 @@             => proxy k -> Filter exts actM m prms (k ':= Bool ': prms) switchQuery k = focus doc Nothing $ R.raw "switch" $ \d t -> do     qs      <- getQueryParams-    (ps,fs) <- getRequestBody+    (ps,fs) <- getReqBodyInternal     let n = maybe False id . fmap (maybe True id) . lookup (SC.pack $ symbolVal k) $ reqParams (Proxy :: Proxy Bool) qs ps fs     return (Dict.add k n d, t)   where@@ -243,6 +245,15 @@     if v == v' then return (d,t) else mzero   where     doc = DocPrecondition $ "header: " <> toHtml (symbolVal k) <> " = " <> toHtml (show v)++-- | filter by JSON typed body. since 2.0.0.+jsonReqBody :: (KnownSymbol k, MonadIO actM, k </ prms, FromJSON a)+       => proxy k -> Filter exts actM m prms (k ':= a ': prms)+jsonReqBody k = focus doc Nothing $ R.raw "json body" $ \d t -> do+    n <- maybe mzero return =<< getReqBodyJSON+    return (Dict.add k n d, t)+  where+    doc = DocPrecondition $ "json body: " <> toHtml (symbolVal k)  -- | require Accept header and set response Content-Type. since 0.16.0. accept :: Monad actM => ContentType -> Filter' exts actM m
src/Control/Monad/Apiary/Internal.hs view
@@ -14,6 +14,9 @@     , runApiaryTWith     , runApiaryWith     , runApiary+    , getApiaryTWith+    , getApiaryWith+    , getApiary     , ApiaryConfig(..)      , action@@ -96,7 +99,7 @@ newtype ApiaryT exts prms actM m a = ApiaryT { unApiaryT :: forall b.     ApiaryEnv exts prms actM     -> (a -> ApiaryWriter exts actM -> m b)-    -> m b +    -> m b     }  apiaryT :: Monad m@@ -141,6 +144,35 @@           -> m a runApiary run = runApiaryWith run noExtension +-- | get 'Application' from Apiary monad. since 2.0.0.+getApiaryTWith :: (Monad actM, Monad m)+               => (forall b. actM b -> IO b)+               -> Initializer m '[] exts+               -> ApiaryConfig+               -> ApiaryT exts '[] actM m ()+               -> m Wai.Application+getApiaryTWith runAct (Initializer ir) conf m = ir NoExtension $ \exts -> do+    wtr <- unApiaryT m (initialEnv conf exts) (\_ w -> return w)+    let doc = docsToDocuments $ writerDoc wtr []+        rtr = writerRouter wtr R.empty+        mw  = allMiddleware exts . writerMw wtr+        mw' = allMiddleware' exts+        app = mw $ execActionT conf exts doc (mw' $ hoistActionT runAct $ routerToAction rtr)+    return $! app++getApiaryWith :: Monad m+              => Initializer m '[] exts+              -> ApiaryConfig+              -> ApiaryT exts '[] IO m ()+              -> m Wai.Application+getApiaryWith = getApiaryTWith id++getApiary :: Monad m+          => ApiaryConfig+          -> ApiaryT '[] '[] IO m ()+          -> m Wai.Application+getApiary = getApiaryWith noExtension+ --------------------------------------------------------------------------------  instance Functor (ApiaryT exts prms actM m) where@@ -159,7 +191,7 @@     return x = ApiaryT $ \_ cont -> cont x mempty     m >>= k = ApiaryT $ \env cont ->         unApiaryT    m  env $ \a hdr  ->-        unApiaryT (k a) env $ \b hdr' -> +        unApiaryT (k a) env $ \b hdr' ->         let hdr'' = hdr `mappend` hdr'         in hdr'' `seq` cont b hdr'' @@ -180,7 +212,7 @@ #else     newtype StT (ApiaryT exts prms actM) a = StTApiary { unStTApiary :: (a, ApiaryWriter exts actM) }     liftWith f = apiaryT $ \env ->-        liftM (\a -> (a, mempty)) +        liftM (\a -> (a, mempty))         (f $ \t -> liftM StTApiary $ unApiaryT t env (\a w -> return (a,w)))     restoreT m = apiaryT $ \_ -> liftM unStTApiary m #endif@@ -243,6 +275,9 @@         id  -- | add middleware.+--+-- please note that, this method just provide a shortcut to stack middleware.+-- middlewares are added to whole Apiary application rather than specific route. middleware :: Monad actM => Wai.Middleware -> ApiaryT exts prms actM m () middleware mw = addRoute (ApiaryWriter id id mw) 
src/Data/Apiary/Param.hs view
@@ -97,12 +97,14 @@  type Param = (S.ByteString, S.ByteString) -data File = File-    { fileParameter   :: S.ByteString-    , fileName        :: S.ByteString-    , fileContentType :: S.ByteString-    , fileContent     :: L.ByteString-    } deriving (Show, Eq, Typeable)+data File+    = File {+          fileParameter   :: S.ByteString+        , fileName        :: S.ByteString+        , fileContentType :: S.ByteString+        , fileContent     :: Either L.ByteString FilePath+        }+    deriving (Show, Eq, Typeable)  data QueryRep     = Strict   TypeRep -- ^ require value@@ -223,11 +225,11 @@     readQuery  = fmap (TL.decodeUtf8With lenientDecode . L.fromStrict)     qTypeRep _ = typeRep (Proxy :: Proxy Text) -instance Query S.ByteString where +instance Query S.ByteString where     readQuery  = id     qTypeRep _ = typeRep (Proxy :: Proxy Text) -instance Query L.ByteString where +instance Query L.ByteString where     readQuery  = fmap L.fromStrict     qTypeRep _ = typeRep (Proxy :: Proxy Text) 
src/Web/Apiary.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE ExplicitNamespaces #-} -module Web.Apiary +module Web.Apiary     ( module Control.Monad.Apiary     , module Control.Monad.Apiary.Action     , module Control.Monad.Apiary.Filter@@ -28,12 +28,15 @@     -- | Html     , module Text.Blaze.Html     ) where- + import Control.Monad.Apiary     ( ApiaryT     , runApiaryTWith     , runApiaryWith     , runApiary+    , getApiaryTWith+    , getApiaryWith+    , getApiary     , ApiaryConfig(..)     , action     , middleware@@ -45,6 +48,7 @@  import Control.Monad.Apiary.Action     ( ActionT+    , application     , stop     , param     , params@@ -78,6 +82,7 @@     , switchQuery     , eqHeader     , header+    , jsonReqBody     , accept     , ssl     )
tests/Application.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE DataKinds #-}+{-# LANGUAGE TemplateHaskell #-}  {-# OPTIONS_GHC -fno-warn-missing-signatures #-} @@ -19,12 +20,16 @@ import qualified Network.Wai.Test as WT import qualified Network.Wai as Wai import qualified Network.HTTP.Types as HTTP+import System.Directory (removeFile)  import qualified Data.ByteString.Lazy.Char8 as L import qualified Data.ByteString.Char8 as S+import Data.Word (Word64)+import qualified Data.Aeson as JSON+import qualified Data.Aeson.TH as JSON  testReq :: String -> (Request -> IO ()) -> TestTree-testReq str f = +testReq str f =     let (meth, other) = break (== ' ') str         (p,  version) = break (== ' ') (tail other)     in testCase str $ f (WT.setPath (setVersion version $ (WT.defaultRequest { Wai.requestMethod = S.pack meth })) (S.pack p))@@ -88,7 +93,7 @@     method POST  . action $ contentType "text/plain" >> bytes "POST"  methodFilterTest :: TestTree-methodFilterTest = testGroup "methodFilter" $ map ($methodFilterApp)+methodFilterTest = testGroup "methodFilter" $ map ($ methodFilterApp)     [ testReq "GET /"    . assertPlain200 "GET"     , testReq "POST /"   . assertPlain200 "POST"     , testReq "GET /foo" . assert404@@ -330,7 +335,7 @@ multipleFilter1Test = testGroup "multiple test1: root, method"     [ testReq "GET /index.html" $ assertPlain200 "GET /"      multipleFilter1App     , testReq "POST /"          $ assertHtml200 "POST /"      multipleFilter1App-    , testReq "DELETE /"        $ assertPlain200 "DELETE ANY" multipleFilter1App +    , testReq "DELETE /"        $ assertPlain200 "DELETE ANY" multipleFilter1App     , testReq "PUT /"           $ assert404 multipleFilter1App     ] @@ -360,6 +365,123 @@  -------------------------------------------------------------------------------- +limit :: Word64+limit = 1024++largeReq :: WT.SRequest+largeReq = WT.SRequest+    Wai.defaultRequest+        { Wai.requestBodyLength = Wai.KnownLength (limit + 1)+        , Wai.requestMethod = HTTP.methodPut+        }+    "some request body"++largeReq' :: WT.SRequest+largeReq' = WT.SRequest+    Wai.defaultRequest+        { Wai.requestBodyLength = Wai.ChunkedBody+        , Wai.requestMethod = HTTP.methodPost+        }+    (L.replicate (fromIntegral (limit + 1)) 'a')++assertLargeReq413 :: Application -> WT.SRequest -> IO ()+assertLargeReq413 app req = flip WT.runSession app $ do+    respond <- WT.srequest req+    WT.assertStatus 413 respond++tooLargeReqTestApp :: Application+tooLargeReqTestApp = runIdentity . runApiary return (def {maxRequestSize = limit}) $ do+    root $ do+        method PUT  . action $ do+            b <- getReqBody+            b `seq` bytes "Test"+        method POST . action $ do+            b <- getReqBody+            b `seq` bytes "Test"++tooLargeReqTest :: TestTree+tooLargeReqTest = testGroup "large request body"+    [ testCase "Large request" $ assertLargeReq413 tooLargeReqTestApp largeReq+    , testCase "Large request" $ assertLargeReq413 tooLargeReqTestApp largeReq'+    ]++--------------------------------------------------------------------------------++dalvikRequest :: IO WT.SRequest+dalvikRequest = do+    bs <- L.readFile "./tests/dalvik-request"+    return $ WT.SRequest+        Wai.defaultRequest+            {+              Wai.requestBodyLength = Wai.ChunkedBody+            , Wai.requestMethod = HTTP.methodPost+            , Wai.requestHeaders = [("Content-Type", "multipart/form-data; boundary=*****")]+            }+        bs++multiPartTestApp :: Application+multiPartTestApp = runIdentity . runApiary return def $ do+    root $ do+        method POST . action $ do+            p <- getReqBodyParams+            f <- getReqBodyFiles+            guard (not . null $ p)+            guard (not . null $ f)+            case fileContent (head f) of+                Left lbs -> bytes "lbs file"++multiPartTestApp' :: Application+multiPartTestApp' = runIdentity . runApiary return (def {uploadFilePath = Just "./"}) $ do+    root $ do+        method POST . action $ do+            p <- getReqBodyParams+            f <- getReqBodyFiles+            guard (not . null $ p)+            guard (not . null $ f)+            case fileContent (head f) of+                Right path -> do+                    liftIO $ removeFile path+                    bytes "disk file"++multiPartTest :: TestTree+multiPartTest = testGroup "multipart request body"+    [ testCase "multipart test" $ assertSRequest 200 Nothing "lbs file" multiPartTestApp =<< dalvikRequest+    , testCase "multipart test" $ assertSRequest 200 Nothing "disk file" multiPartTestApp' =<< dalvikRequest+    ]++--------------------------------------------------------------------------------++data Foo = Foo {foo :: Int} deriving Show++$(JSON.deriveJSON JSON.defaultOptions ''Foo)++jsonRequest :: WT.SRequest+jsonRequest = WT.SRequest+        Wai.defaultRequest { Wai.requestMethod = HTTP.methodPost }+        "{\"foo\": 123}"++jsonReqTestApp :: Application+jsonReqTestApp = runIdentity . runApiary return (def {uploadFilePath = Just "./"}) $ do+    root $ do+        method POST . (jsonReqBody [key|foo|]) . action $ do+            f <- param [key|foo|]+            showing $ foo f++jsonReqTestApp' :: Application+jsonReqTestApp' = runIdentity . runApiary return (def {uploadFilePath = Just "./"}) $ do+    root $ do+        method POST . (jsonReqBody [key|foo|]) . action $ do+            f <- param [key|foo|]+            showing $ (f :: Int)++jsonReqBodyTest :: TestTree+jsonReqBodyTest = testGroup "json request body filter"+    [ testCase "json request body test" $ assertSRequest 200 Nothing "123" jsonReqTestApp jsonRequest+    , testCase "json request body test" $ assertSRequest 404 (Just "text/plain") "404 Page Notfound.\n" jsonReqTestApp' jsonRequest+    ]++--------------------------------------------------------------------------------+ test :: TestTree test = testGroup "Application"     [ helloWorldAllTest@@ -373,5 +495,8 @@     , acceptTest     , multipleFilter1Test     , issue17Test+    , tooLargeReqTest+    , multiPartTest+    , jsonReqBodyTest     ]