packages feed

quickwebapp 2.1.1.0 → 3.0.0.1

raw patch · 2 files changed

+103/−77 lines, 2 filesdep +aesondep +containersdep +eitherdep −http-typesdep −scottyPVP ok

version bump matches the API change (PVP)

Dependencies added: aeson, containers, either, lucid, servant, servant-lucid, servant-server, warp

Dependencies removed: http-types, scotty

API changes (from Hackage documentation)

- QuickWebApp: class FromLBS a
- QuickWebApp: class ToLBS a
- QuickWebApp: err422 :: Status
- QuickWebApp: fromLBS :: FromLBS a => ByteString -> a
- QuickWebApp: instance FromLBS ByteString
- QuickWebApp: instance FromLBS String
- QuickWebApp: instance FromLBS Text
- QuickWebApp: instance ToLBS ByteString
- QuickWebApp: instance ToLBS String
- QuickWebApp: instance ToLBS Text
- QuickWebApp: interactWebEither :: (FromLBS a, ToLBS b, ToLBS e) => (a -> Either e b) -> IO ()
- QuickWebApp: interactWebEitherOn :: (FromLBS a, ToLBS b, ToLBS e) => Int -> (a -> Either e b) -> IO ()
- QuickWebApp: interactWebOn :: (FromLBS a, ToLBS b) => Int -> (a -> b) -> IO ()
- QuickWebApp: toLBS :: ToLBS a => a -> ByteString
+ QuickWebApp: instance Data.Aeson.Types.Class.FromJSON QuickWebApp.Input
+ QuickWebApp: instance Data.Aeson.Types.Class.ToJSON QuickWebApp.Output
+ QuickWebApp: instance GHC.Classes.Eq QuickWebApp.Input
+ QuickWebApp: instance GHC.Classes.Eq QuickWebApp.Output
+ QuickWebApp: instance GHC.Generics.Constructor QuickWebApp.C1_0Input
+ QuickWebApp: instance GHC.Generics.Constructor QuickWebApp.C1_0Output
+ QuickWebApp: instance GHC.Generics.Datatype QuickWebApp.D1Input
+ QuickWebApp: instance GHC.Generics.Datatype QuickWebApp.D1Output
+ QuickWebApp: instance GHC.Generics.Generic QuickWebApp.Input
+ QuickWebApp: instance GHC.Generics.Generic QuickWebApp.Output
+ QuickWebApp: instance GHC.Generics.Selector QuickWebApp.S1_0_0Input
+ QuickWebApp: instance GHC.Generics.Selector QuickWebApp.S1_0_0Output
+ QuickWebApp: instance GHC.Show.Show QuickWebApp.Input
+ QuickWebApp: instance GHC.Show.Show QuickWebApp.Output
+ QuickWebApp: instance Lucid.Base.ToHtml QuickWebApp.Home
+ QuickWebApp: instance Servant.API.ContentTypes.FromFormUrlEncoded QuickWebApp.Input
- QuickWebApp: interactWeb :: (FromLBS a, ToLBS b) => (a -> b) -> IO ()
+ QuickWebApp: interactWeb :: (FromText a, ToText b) => (a -> Either String b) -> IO ()

Files

QuickWebApp.hs view
@@ -1,88 +1,108 @@-{-# LANGUAGE FlexibleInstances    #-}-{-# LANGUAGE OverloadedStrings    #-}-{-# LANGUAGE TypeSynonymInstances #-}-{-| A quick-and-dirty api generator, for any function `a -> b` which can be wrapped-    inside a function `ByteString -> ByteString`.-    It is inspired from the 'interact' function from 'Prelude'.+{-# LANGUAGE DataKinds             #-}+{-# LANGUAGE DeriveGeneric         #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE TypeOperators         #-}++{-| A quick-and-dirty api generator, for any function @a -> "Either" "String" b@.+ +If you only have a function @f :: a -> b@, simply run @interactWeb ("Right" .f)@ -}-module QuickWebApp where+module QuickWebApp (+    interactWeb+  ) where -import qualified Data.ByteString            as BS-import qualified Data.ByteString.Lazy       as BL-import qualified Data.ByteString.Lazy.Char8 as BC-import qualified Data.Text                  as TS-import qualified Data.Text.Lazy             as TL-import qualified Data.Text.Lazy.Encoding    as TL-import           Network.HTTP.Types.Status-import           Web.Scotty+import           Control.Monad.Trans.Either+import           Data.Aeson+import qualified Data.ByteString.Lazy.Char8 as BL8+import           Data.Either.Combinators+import qualified Data.Map                   as M+import           Data.Text                  (Text)+import           Data.Text.Lazy.Encoding    (decodeUtf8)+import           GHC.Generics+import           Network.Wai.Handler.Warp+import           System.Environment --- | Represents types which can be converted to a Lazy 'ByteString'-class ToLBS a where-    toLBS :: a -> BL.ByteString+import           Lucid+import           Servant+import           Servant.HTML.Lucid -instance ToLBS BL.ByteString where-    toLBS = id+type API = Get '[HTML] Home+       :<|> ReqBody '[JSON, FormUrlEncoded] Input :> Post '[JSON] Output -instance ToLBS BS.ByteString where-    toLBS = BL.fromStrict -instance ToLBS TL.Text where-    toLBS = TL.encodeUtf8+data Home = Home+instance ToHtml Home where+  toHtml Home = doctypehtml_ $ do+    head_ $ do+      title_ "Api powered by QuickWebApp"+      meta_ [charset_ "utf-8"]+      link_ [rel_ "stylesheet", type_ "text/css",  href_ "http://groundfloor.neocities.org/default.css"]+    body_ $ do+      header_ $ do+        h1_ "interactWeb :: (a -> Either String b) -> IO ()"+        p_ $ do+          "Powered by "+          a_ [href_ "http://hackage.haskell.org/package/servant"] "servant"+          " and "+          a_ [href_ "http://hackage.haskell.org/package/lucid"] "lucid"+      div_ [ style_ "width: 80%; margin: auto;"] $ do+        div_ [style_ "display: flex; flex-direction: row; align-items: flex-stretch"] $ do+          section_ [style_ "flex: 1 1 50%;",  class_ "input"] $ do+            h2_ "Try out your function here"+            form_ [action_ "/" , method_ "POST"] $ do+              textarea_ [ name_ "input" ] ""+              input_ [type_ "submit" , value_ "Test"]+          section_ [style_ "flex: 1 1 50%", class_ "output"] $ do+            h2_ "Results here"+            pre_ $ samp_ [id_ "output"] ""+        h2_ "You can also curl or httpie"+        kbd_ "http :8080 input=\"<your input string>\""+        kbd_ "curl localhost:8080 -d input=\"<your input string>\""+        h2_ "Where to go from here ?"+        p_ $ do+          "TODO: This is where I show you the boilerplate to kickstart \+          \ your api, but I've not done that yet. "+          a_ [href_ "http://github.com/jtanguy/quickwebapp/issues/2"] "Corresponding github issue"+        term "script" [src_ "//code.jquery.com/jquery-1.11.3.min.js"] ""+      script_ "$(function(){ \+        \ $('form').submit(function(){ \+        \    $.post($(this).attr('action'), $(this).serialize(), function(json) { \+        \         $('#output').html(json.output);\+        \             }, 'json');\+        \                 return false;\+        \                   });\+        \                   });"+  toHtmlRaw = toHtml -instance ToLBS TS.Text where-    toLBS = TL.encodeUtf8 . TL.fromStrict -instance ToLBS String where-    toLBS = BC.pack---- | Represents types which can be converted from a Lazy 'ByteString'--- This is intended for other String-like types-class FromLBS a where-    fromLBS :: BL.ByteString -> a--instance FromLBS BL.ByteString where-    fromLBS = id--instance FromLBS BS.ByteString where-    fromLBS = BL.toStrict--instance FromLBS TL.Text where-    fromLBS = TL.decodeUtf8+newtype Input = Input { input :: Text } deriving (Show, Eq, Generic) -instance FromLBS TS.Text where-    fromLBS = TL.toStrict . TL.decodeUtf8+instance FromJSON Input+instance FromFormUrlEncoded Input where+  fromFormUrlEncoded = eitherDecode . encode . M.fromList -instance FromLBS String where-    fromLBS = BC.unpack+newtype Output = Output { output :: Text } deriving (Show, Eq, Generic)+instance ToJSON Output --- | Unprocessable entity error code (temporary fix for a missing status in--- http-types)-err422 :: Status-err422 = mkStatus 422 "Unprocessable Entity"+{-| Tranform a function into a webapp.+The inputs and outputs must implement 'FromText' and 'ToText', respectively. --- | Equivalent to 'interactWebOn 3000'-interactWeb :: (FromLBS a, ToLBS b) => (a -> b) -> IO ()-interactWeb = interactWebOn 3000+You can query it via a browser at <http://localhost:8080> or by using+curl/httpie --- | Equivalent to 'interactWebEitherOn 3000'-interactWebEither :: (FromLBS a, ToLBS b, ToLBS e) => (a -> Either e b) -> IO ()-interactWebEither = interactWebEitherOn 3000+  > http :8080 input="<your input string>" --- | Create an API with a 'POST' endpoint-interactWebOn :: (FromLBS a, ToLBS b) => Int -> (a -> b) -> IO ()-interactWebOn port f = scotty port $ post "/" $ do-                c <- body-                setHeader "Content-Type" "text/plain"-                raw . toLBS . f . fromLBS $ c+  > curl localhost:8080 -d input="<your input string>"+-}+interactWeb :: (FromText a, ToText b) => (a -> Either String b) -> IO ()+interactWeb f = do+    port <- maybe 8080 read <$> lookupEnv "PORT"+    run port (serve (Proxy :: Proxy API) (return Home :<|> handler))+  where+    handler = maybe (left $ err "Could not convert from text")+                    (hoistEither . mapBoth err (Output . toText) . f) . fromText . input --- | Create an API with a 'POST' endpoint--- If the function fails and returns a 'Left' value, return a 422 response with--- the error in the body-interactWebEitherOn :: (FromLBS a, ToLBS b, ToLBS e) => Int -> (a -> Either e b) -> IO ()-interactWebEitherOn port f = scotty port $ post "/" $ do-                c <- body-                setHeader "Content-Type" "text/plain"-                case f . fromLBS $ c of-                    Right res -> raw . toLBS $ res-                    Left err -> status err422 >> (raw . toLBS $ err)+err :: String -> ServantErr+err e = ServantErr 422 "Unprocessable Entity" (BL8.pack (show e)) [] 
quickwebapp.cabal view
@@ -1,5 +1,5 @@ name:                quickwebapp-version:             2.1.1.0+version:             3.0.0.1 synopsis:            A quick webapp generator for any file processing tool description:     A quick-and-dirty api generator, inspired from the 'interact' function from@@ -7,7 +7,7 @@     .     > interactWeb reverse      .-    This creates a server listening on port 3000.+    This creates a server listening on port 8080, or environment value PORT.     . license:             GPL-3 license-file:        LICENSE@@ -29,9 +29,15 @@ library   exposed-modules:     QuickWebApp   build-depends:       base ==4.*,+                       containers,                        bytestring,                        text,-                       http-types,-                       scotty-  -- hs-source-dirs:      src+                       aeson,+                       either,+                       servant,+                       servant-server,+                       servant-lucid,+                       lucid,+                       warp+   default-language:    Haskell2010