From 2b23253a8de63e27c358dc1337d5ae26198a80f8 Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Tue, 5 May 2015 18:00:10 +0200 Subject: [PATCH 01/15] first getting-started example --- servant-examples/getting-started/GS1.hs | 45 +++++++++++++++++++++++++ servant-examples/servant-examples.cabal | 13 +++++++ 2 files changed, 58 insertions(+) create mode 100644 servant-examples/getting-started/GS1.hs diff --git a/servant-examples/getting-started/GS1.hs b/servant-examples/getting-started/GS1.hs new file mode 100644 index 00000000..9cc815b6 --- /dev/null +++ b/servant-examples/getting-started/GS1.hs @@ -0,0 +1,45 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE TypeOperators #-} + +import Data.Aeson +import Data.Time.Calendar +import GHC.Generics +import Network.Wai.Handler.Warp (run) +import Servant + +data User = User + { name :: String + , age :: Int + , email :: String + , registration_date :: Day + } deriving (Eq, Show, Generic) + +-- orphan ToJSON instance for Day. necessary to derive one for User +instance ToJSON Day where + -- display a day in YYYY-mm-dd format + toJSON d = toJSON (showGregorian d) + +instance ToJSON User + +type UserAPI = "users" :> Get '[JSON] [User] + +users :: [User] +users = + [ User "Isaac Newton" 372 "isaac@newton.co.uk" (fromGregorian 1683 3 1) + , User "Albert Einstein" 136 "ae@mc2.org" (fromGregorian 1905 12 1) + ] + +userAPI :: Proxy UserAPI +userAPI = Proxy + +server :: Server UserAPI +server = return users + +runServer :: Int -> IO () +runServer port = run port (serve userAPI server) + +main :: IO () +main = runServer 8081 + diff --git a/servant-examples/servant-examples.cabal b/servant-examples/servant-examples.cabal index 0dc4e22a..c0869ef7 100644 --- a/servant-examples/servant-examples.cabal +++ b/servant-examples/servant-examples.cabal @@ -13,6 +13,19 @@ category: Web build-type: Simple cabal-version: >=1.10 +executable getting-started-1 + main-is: GS1.hs + build-depends: + aeson >= 0.8 + , base >= 4.7 + , either + , servant + , servant-server + , time + , warp + hs-source-dirs: getting-started + default-language: Haskell2010 + executable hackage main-is: hackage.hs build-depends: From 66456cafa2c73993d1376c97759aff37290dcee4 Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Tue, 5 May 2015 21:50:24 +0200 Subject: [PATCH 02/15] refactor a bit + add GS2 --- servant-examples/getting-started/GS1.hs | 11 ++--- servant-examples/getting-started/GS2.hs | 49 +++++++++++++++++++ .../getting-started/getting-started.hs | 24 +++++++++ servant-examples/servant-examples.cabal | 6 ++- 4 files changed, 81 insertions(+), 9 deletions(-) create mode 100644 servant-examples/getting-started/GS2.hs create mode 100644 servant-examples/getting-started/getting-started.hs diff --git a/servant-examples/getting-started/GS1.hs b/servant-examples/getting-started/GS1.hs index 9cc815b6..a6daffec 100644 --- a/servant-examples/getting-started/GS1.hs +++ b/servant-examples/getting-started/GS1.hs @@ -2,11 +2,12 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TypeOperators #-} +module GS1 where import Data.Aeson import Data.Time.Calendar import GHC.Generics -import Network.Wai.Handler.Warp (run) +import Network.Wai import Servant data User = User @@ -37,9 +38,5 @@ userAPI = Proxy server :: Server UserAPI server = return users -runServer :: Int -> IO () -runServer port = run port (serve userAPI server) - -main :: IO () -main = runServer 8081 - +app :: Application +app = serve userAPI server diff --git a/servant-examples/getting-started/GS2.hs b/servant-examples/getting-started/GS2.hs new file mode 100644 index 00000000..bf935320 --- /dev/null +++ b/servant-examples/getting-started/GS2.hs @@ -0,0 +1,49 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE TypeOperators #-} +module GS2 where + +import Data.Aeson +import Data.Time.Calendar +import GHC.Generics +import Network.Wai +import Servant + +data User = User + { name :: String + , age :: Int + , email :: String + , registration_date :: Day + } deriving (Eq, Show, Generic) + +-- orphan ToJSON instance for Day. necessary to derive one for User +instance ToJSON Day where + -- display a day in YYYY-mm-dd format + toJSON d = toJSON (showGregorian d) + +instance ToJSON User + +type UserAPI = "users" :> Get '[JSON] [User] + :<|> "albert" :> Get '[JSON] User + :<|> "isaac" :> Get '[JSON] User + +isaac :: User +isaac = User "Isaac Newton" 372 "isaac@newton.co.uk" (fromGregorian 1683 3 1) + +albert :: User +albert = User "Albert Einstein" 136 "ae@mc2.org" (fromGregorian 1905 12 1) + +users :: [User] +users = [isaac, albert] + +userAPI :: Proxy UserAPI +userAPI = Proxy + +server :: Server UserAPI +server = return users + :<|> return albert + :<|> return isaac + +app :: Application +app = serve userAPI server diff --git a/servant-examples/getting-started/getting-started.hs b/servant-examples/getting-started/getting-started.hs new file mode 100644 index 00000000..1b757e8e --- /dev/null +++ b/servant-examples/getting-started/getting-started.hs @@ -0,0 +1,24 @@ +import Network.Wai +import Network.Wai.Handler.Warp +import System.Environment + +import qualified GS1 +import qualified GS2 + +app :: String -> Maybe Application +app n = case n of + "1" -> Just GS1.app + "2" -> Just GS2.app + _ -> Nothing + +main :: IO () +main = do + args <- getArgs + case args of + [n] -> maybe usage (run 8081) (app n) + _ -> usage + +usage :: IO () +usage = do + putStrLn "Usage:\t getting-started N" + putStrLn "\t\twhere N is the number of the example you want to run." diff --git a/servant-examples/servant-examples.cabal b/servant-examples/servant-examples.cabal index c0869ef7..4fc63834 100644 --- a/servant-examples/servant-examples.cabal +++ b/servant-examples/servant-examples.cabal @@ -13,8 +13,9 @@ category: Web build-type: Simple cabal-version: >=1.10 -executable getting-started-1 - main-is: GS1.hs +executable getting-started + main-is: getting-started.hs + other-modules: GS1, GS2 build-depends: aeson >= 0.8 , base >= 4.7 @@ -22,6 +23,7 @@ executable getting-started-1 , servant , servant-server , time + , wai , warp hs-source-dirs: getting-started default-language: Haskell2010 From 3edc067c66ef8b3973634b6a36e0db1a59bfa3d1 Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Tue, 5 May 2015 22:54:55 +0200 Subject: [PATCH 03/15] servant-examples: add GS3 --- servant-examples/getting-started/GS3.hs | 80 +++++++++++++++++++ .../getting-started/getting-started.hs | 2 + 2 files changed, 82 insertions(+) create mode 100644 servant-examples/getting-started/GS3.hs diff --git a/servant-examples/getting-started/GS3.hs b/servant-examples/getting-started/GS3.hs new file mode 100644 index 00000000..5dec0123 --- /dev/null +++ b/servant-examples/getting-started/GS3.hs @@ -0,0 +1,80 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE TypeOperators #-} +module GS3 where + +import Control.Monad.Trans.Either +import Data.Aeson +import Data.List +import GHC.Generics +import Network.Wai +import Servant + +data Position = Position + { x :: Int + , y :: Int + } deriving Generic + +instance ToJSON Position + +newtype HelloMessage = HelloMessage { msg :: String } + deriving Generic + +instance ToJSON HelloMessage + +data ClientInfo = ClientInfo + { name :: String + , email :: String + , age :: Int + , interested_in :: [String] + } deriving Generic + +instance FromJSON ClientInfo + +data Email = Email + { from :: String + , to :: String + , subject :: String + , body :: String + } deriving Generic + +instance ToJSON Email + +emailForClient :: ClientInfo -> Email +emailForClient c = Email from' to' subject' body' + + where from' = "great@company.com" + to' = email c + subject' = "Hey " ++ name c ++ ", we miss you!" + body' = "Hi " ++ name c ++ ",\n\n" + ++ "Since you've recently turned " ++ show (age c) + ++ ", have you checked out our latest " + ++ intercalate ", " (interested_in c) + ++ " ? Give us a visit!" + +type API = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position + :<|> "hello" :> QueryParam "name" String :> Get '[JSON] HelloMessage + :<|> "marketing" :> ReqBody '[JSON] ClientInfo :> Post '[JSON] Email + +api :: Proxy API +api = Proxy + +server :: Server API +server = position + :<|> hello + :<|> marketing + + where position :: Int -> Int -> EitherT Int IO Position + position x y = return (Position x y) + + hello :: Maybe String -> EitherT Int IO HelloMessage + hello mname = return . HelloMessage $ case mname of + Nothing -> "Hello, anonymous coward" + Just n -> "Hello, " ++ n + + marketing :: ClientInfo -> EitherT Int IO Email + marketing clientinfo = return (emailForClient clientinfo) + +app :: Application +app = serve api server diff --git a/servant-examples/getting-started/getting-started.hs b/servant-examples/getting-started/getting-started.hs index 1b757e8e..ec0e93df 100644 --- a/servant-examples/getting-started/getting-started.hs +++ b/servant-examples/getting-started/getting-started.hs @@ -4,11 +4,13 @@ import System.Environment import qualified GS1 import qualified GS2 +import qualified GS3 app :: String -> Maybe Application app n = case n of "1" -> Just GS1.app "2" -> Just GS2.app + "3" -> Just GS3.app _ -> Nothing main :: IO () From f3002b36daabc1475d5a1851a0e33ec5723474f1 Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Wed, 6 May 2015 00:20:54 +0200 Subject: [PATCH 04/15] examples: tweak GS3 --- servant-examples/getting-started/GS3.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/servant-examples/getting-started/GS3.hs b/servant-examples/getting-started/GS3.hs index 5dec0123..26c2a7fe 100644 --- a/servant-examples/getting-started/GS3.hs +++ b/servant-examples/getting-started/GS3.hs @@ -51,7 +51,7 @@ emailForClient c = Email from' to' subject' body' ++ "Since you've recently turned " ++ show (age c) ++ ", have you checked out our latest " ++ intercalate ", " (interested_in c) - ++ " ? Give us a visit!" + ++ " products? Give us a visit!" type API = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position :<|> "hello" :> QueryParam "name" String :> Get '[JSON] HelloMessage @@ -65,15 +65,15 @@ server = position :<|> hello :<|> marketing - where position :: Int -> Int -> EitherT Int IO Position + where position :: Int -> Int -> EitherT ServantErr IO Position position x y = return (Position x y) - hello :: Maybe String -> EitherT Int IO HelloMessage + hello :: Maybe String -> EitherT ServantErr IO HelloMessage hello mname = return . HelloMessage $ case mname of Nothing -> "Hello, anonymous coward" Just n -> "Hello, " ++ n - marketing :: ClientInfo -> EitherT Int IO Email + marketing :: ClientInfo -> EitherT ServantErr IO Email marketing clientinfo = return (emailForClient clientinfo) app :: Application From e81ac8fbc5dcf09268f2d8e0e9dbd4c0fda98cc3 Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Wed, 6 May 2015 13:36:04 +0200 Subject: [PATCH 05/15] examples: add GS4 --- servant-examples/getting-started/GS4.hs | 63 +++++++++++++++++++ .../getting-started/getting-started.hs | 2 + servant-examples/servant-examples.cabal | 2 + 3 files changed, 67 insertions(+) create mode 100644 servant-examples/getting-started/GS4.hs diff --git a/servant-examples/getting-started/GS4.hs b/servant-examples/getting-started/GS4.hs new file mode 100644 index 00000000..89e7cce3 --- /dev/null +++ b/servant-examples/getting-started/GS4.hs @@ -0,0 +1,63 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE OverloadedStrings #-} +module GS4 where + +import Data.Aeson +import Data.Foldable (foldMap) +import GHC.Generics +import Lucid +import Network.Wai +import Servant +import Servant.HTML.Lucid + +data Person = Person + { firstName :: String + , lastName :: String + , age :: Int + } deriving Generic -- for the JSON instance + +-- JSON serialization +instance ToJSON Person + +-- HTML serialization of a single person +instance ToHtml Person where + toHtml p = + tr_ $ do + td_ (toHtml $ firstName p) + td_ (toHtml $ lastName p) + td_ (toHtml . show $ age p) + + toHtmlRaw = toHtml + +-- HTML serialization of a list of persons +instance ToHtml [Person] where + toHtml persons = table_ $ do + tr_ $ do + td_ "first name" + td_ "last name" + td_ "age" + + foldMap toHtml persons + + toHtmlRaw = toHtml + +persons :: [Person] +persons = + [ Person "Isaac" "Newton" 372 + , Person "Albert" "Einstein" 136 + ] + +type PersonAPI = "persons" :> Get '[JSON, HTML] [Person] + +personAPI :: Proxy PersonAPI +personAPI = Proxy + +server :: Server PersonAPI +server = return persons + +app :: Application +app = serve personAPI server diff --git a/servant-examples/getting-started/getting-started.hs b/servant-examples/getting-started/getting-started.hs index ec0e93df..f3a0ace1 100644 --- a/servant-examples/getting-started/getting-started.hs +++ b/servant-examples/getting-started/getting-started.hs @@ -5,12 +5,14 @@ import System.Environment import qualified GS1 import qualified GS2 import qualified GS3 +import qualified GS4 app :: String -> Maybe Application app n = case n of "1" -> Just GS1.app "2" -> Just GS2.app "3" -> Just GS3.app + "4" -> Just GS4.app _ -> Nothing main :: IO () diff --git a/servant-examples/servant-examples.cabal b/servant-examples/servant-examples.cabal index 4fc63834..2e7af148 100644 --- a/servant-examples/servant-examples.cabal +++ b/servant-examples/servant-examples.cabal @@ -20,7 +20,9 @@ executable getting-started aeson >= 0.8 , base >= 4.7 , either + , lucid , servant + , servant-lucid , servant-server , time , wai From 1d3eefaf48dea6e3d43057956a90c5e9cf2c239e Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Wed, 6 May 2015 15:07:49 +0200 Subject: [PATCH 06/15] examples: add GS5 --- servant-examples/getting-started/GS5.hs | 37 +++++++++++++++++++ .../getting-started/getting-started.hs | 2 + servant-examples/servant-examples.cabal | 2 + 3 files changed, 41 insertions(+) create mode 100644 servant-examples/getting-started/GS5.hs diff --git a/servant-examples/getting-started/GS5.hs b/servant-examples/getting-started/GS5.hs new file mode 100644 index 00000000..6a6c91b7 --- /dev/null +++ b/servant-examples/getting-started/GS5.hs @@ -0,0 +1,37 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE OverloadedStrings #-} +module GS5 where + +import Control.Monad.IO.Class +import Control.Monad.Trans.Either +import Data.Aeson +import GHC.Generics +import Network.Wai +import Servant +import System.Directory + +type IOAPI = "myfile.txt" :> Get '[JSON] FileContent + +ioAPI :: Proxy IOAPI +ioAPI = Proxy + +newtype FileContent = FileContent + { content :: String } + deriving Generic + +instance ToJSON FileContent + +server :: Server IOAPI +server = do + exists <- liftIO (doesFileExist "myfile.txt") + if exists + then liftIO (readFile "myfile.txt") >>= return . FileContent + else left custom404Err + + where custom404Err = err404 { errBody = "myfile.txt just isn't there, please leave this server alone." } + +app :: Application +app = serve ioAPI server diff --git a/servant-examples/getting-started/getting-started.hs b/servant-examples/getting-started/getting-started.hs index f3a0ace1..f32ee674 100644 --- a/servant-examples/getting-started/getting-started.hs +++ b/servant-examples/getting-started/getting-started.hs @@ -6,6 +6,7 @@ import qualified GS1 import qualified GS2 import qualified GS3 import qualified GS4 +import qualified GS5 app :: String -> Maybe Application app n = case n of @@ -13,6 +14,7 @@ app n = case n of "2" -> Just GS2.app "3" -> Just GS3.app "4" -> Just GS4.app + "5" -> Just GS5.app _ -> Nothing main :: IO () diff --git a/servant-examples/servant-examples.cabal b/servant-examples/servant-examples.cabal index 2e7af148..ef2f8cc3 100644 --- a/servant-examples/servant-examples.cabal +++ b/servant-examples/servant-examples.cabal @@ -19,12 +19,14 @@ executable getting-started build-depends: aeson >= 0.8 , base >= 4.7 + , directory , either , lucid , servant , servant-lucid , servant-server , time + , transformers , wai , warp hs-source-dirs: getting-started From ec95cb035543379170c27ca7db6a5d4598e024e1 Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Wed, 6 May 2015 15:57:22 +0200 Subject: [PATCH 07/15] examples: add GS6 --- servant-examples/getting-started/GS6.hs | 18 ++++++++++++++++++ .../getting-started/getting-started.hs | 2 ++ 2 files changed, 20 insertions(+) create mode 100644 servant-examples/getting-started/GS6.hs diff --git a/servant-examples/getting-started/GS6.hs b/servant-examples/getting-started/GS6.hs new file mode 100644 index 00000000..4fdc3faa --- /dev/null +++ b/servant-examples/getting-started/GS6.hs @@ -0,0 +1,18 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +module GS6 where + +import Network.Wai +import Servant + +type API = "code" :> Raw + +api :: Proxy API +api = Proxy + +server :: Server API +server = serveDirectory "getting-started" + +app :: Application +app = serve api server diff --git a/servant-examples/getting-started/getting-started.hs b/servant-examples/getting-started/getting-started.hs index f32ee674..7674e1cf 100644 --- a/servant-examples/getting-started/getting-started.hs +++ b/servant-examples/getting-started/getting-started.hs @@ -7,6 +7,7 @@ import qualified GS2 import qualified GS3 import qualified GS4 import qualified GS5 +import qualified GS6 app :: String -> Maybe Application app n = case n of @@ -15,6 +16,7 @@ app n = case n of "3" -> Just GS3.app "4" -> Just GS4.app "5" -> Just GS5.app + "6" -> Just GS6.app _ -> Nothing main :: IO () From c892aae429461d66192ee16287325591c8814d4e Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Wed, 6 May 2015 19:01:32 +0200 Subject: [PATCH 08/15] examples: add GS7 --- servant-examples/getting-started/GS7.hs | 33 +++++++++++++++++++ .../getting-started/getting-started.hs | 2 ++ 2 files changed, 35 insertions(+) create mode 100644 servant-examples/getting-started/GS7.hs diff --git a/servant-examples/getting-started/GS7.hs b/servant-examples/getting-started/GS7.hs new file mode 100644 index 00000000..83dfb93a --- /dev/null +++ b/servant-examples/getting-started/GS7.hs @@ -0,0 +1,33 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +module GS7 where + +import Control.Monad.Trans.Either +import Control.Monad.Trans.Reader +import Network.Wai +import Servant + +type ReaderAPI = "a" :> Get '[JSON] Int + :<|> "b" :> Get '[JSON] String + +readerAPI :: Proxy ReaderAPI +readerAPI = Proxy + +readerServerT :: ServerT ReaderAPI (Reader String) +readerServerT = a :<|> b + + where a :: Reader String Int + a = return 1797 + + b :: Reader String String + b = ask + +readerServer :: Server ReaderAPI +readerServer = enter readerToEither readerServerT + + where readerToEither :: Reader String :~> EitherT ServantErr IO + readerToEither = Nat $ \r -> return (runReader r "hi") + +app :: Application +app = serve readerAPI readerServer diff --git a/servant-examples/getting-started/getting-started.hs b/servant-examples/getting-started/getting-started.hs index 7674e1cf..7f6f8c90 100644 --- a/servant-examples/getting-started/getting-started.hs +++ b/servant-examples/getting-started/getting-started.hs @@ -8,6 +8,7 @@ import qualified GS3 import qualified GS4 import qualified GS5 import qualified GS6 +import qualified GS7 app :: String -> Maybe Application app n = case n of @@ -17,6 +18,7 @@ app n = case n of "4" -> Just GS4.app "5" -> Just GS5.app "6" -> Just GS6.app + "7" -> Just GS7.app _ -> Nothing main :: IO () From 0000189760cb5dcfd0e16537dc7b495b0a651c7d Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Thu, 7 May 2015 17:48:21 +0200 Subject: [PATCH 09/15] examples: add GS8 --- servant-examples/getting-started/GS3.hs | 12 +++-- servant-examples/getting-started/GS8.hs | 46 +++++++++++++++++++ .../getting-started/getting-started.hs | 1 + servant-examples/getting-started/gs8-main.hs | 4 ++ servant-examples/servant-examples.cabal | 13 ++++++ 5 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 servant-examples/getting-started/GS8.hs create mode 100644 servant-examples/getting-started/gs8-main.hs diff --git a/servant-examples/getting-started/GS3.hs b/servant-examples/getting-started/GS3.hs index 26c2a7fe..505982cf 100644 --- a/servant-examples/getting-started/GS3.hs +++ b/servant-examples/getting-started/GS3.hs @@ -14,13 +14,15 @@ import Servant data Position = Position { x :: Int , y :: Int - } deriving Generic + } deriving (Show, Generic) +instance FromJSON Position instance ToJSON Position newtype HelloMessage = HelloMessage { msg :: String } - deriving Generic + deriving (Show, Generic) +instance FromJSON HelloMessage instance ToJSON HelloMessage data ClientInfo = ClientInfo @@ -28,17 +30,19 @@ data ClientInfo = ClientInfo , email :: String , age :: Int , interested_in :: [String] - } deriving Generic + } deriving (Show, Generic) instance FromJSON ClientInfo +instance ToJSON ClientInfo data Email = Email { from :: String , to :: String , subject :: String , body :: String - } deriving Generic + } deriving (Show, Generic) +instance FromJSON Email instance ToJSON Email emailForClient :: ClientInfo -> Email diff --git a/servant-examples/getting-started/GS8.hs b/servant-examples/getting-started/GS8.hs new file mode 100644 index 00000000..56039e5a --- /dev/null +++ b/servant-examples/getting-started/GS8.hs @@ -0,0 +1,46 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +module GS8 where + +import Control.Monad.Trans.Either +import Data.Aeson +import Servant +import Servant.Client + +import GS3 + +position :: Int -- ^ value for "x" + -> Int -- ^ value for "y" + -> BaseUrl + -> EitherT ServantError IO Position + +hello :: Maybe String -- ^ an optional value for "name" + -> BaseUrl + -> EitherT ServantError IO HelloMessage + +marketing :: ClientInfo -- ^ value for the request body + -> BaseUrl + -> EitherT ServantError IO Email + +position :<|> hello :<|> marketing = client api + +baseUrl :: BaseUrl +baseUrl = BaseUrl Http "localhost" 8081 + +queries :: EitherT ServantError IO (Position, HelloMessage, Email) +queries = do + pos <- position 10 10 baseUrl + msg <- hello (Just "servant") baseUrl + em <- marketing (ClientInfo "Alp" "alp@foo.com" 26 ["haskell", "mathematics"]) baseUrl + return (pos, msg, em) + +run :: IO () +run = do + res <- runEitherT queries + case res of + Left err -> putStrLn $ "Error: " ++ show err + Right (pos, msg, em) -> do + print pos + print msg + print em diff --git a/servant-examples/getting-started/getting-started.hs b/servant-examples/getting-started/getting-started.hs index 7f6f8c90..bf7103d2 100644 --- a/servant-examples/getting-started/getting-started.hs +++ b/servant-examples/getting-started/getting-started.hs @@ -19,6 +19,7 @@ app n = case n of "5" -> Just GS5.app "6" -> Just GS6.app "7" -> Just GS7.app + "8" -> Just GS3.app _ -> Nothing main :: IO () diff --git a/servant-examples/getting-started/gs8-main.hs b/servant-examples/getting-started/gs8-main.hs new file mode 100644 index 00000000..b6a9e30e --- /dev/null +++ b/servant-examples/getting-started/gs8-main.hs @@ -0,0 +1,4 @@ +import GS8 + +main :: IO () +main = run diff --git a/servant-examples/servant-examples.cabal b/servant-examples/servant-examples.cabal index ef2f8cc3..287e26bd 100644 --- a/servant-examples/servant-examples.cabal +++ b/servant-examples/servant-examples.cabal @@ -32,6 +32,19 @@ executable getting-started hs-source-dirs: getting-started default-language: Haskell2010 +executable gs8-main + main-is: gs8-main.hs + hs-source-dirs: getting-started + default-language: Haskell2010 + build-depends: + aeson + , base + , either + , servant + , servant-client + , servant-server + , wai + executable hackage main-is: hackage.hs build-depends: From 77b15e9cdc79e3affdf958f149046475a8593182 Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Sat, 9 May 2015 12:27:45 +0200 Subject: [PATCH 10/15] examples: add GS9 --- servant-examples/getting-started/GS9.hs | 101 ++++++++++++++++++ .../getting-started/getting-started.hs | 26 ++--- .../getting-started/gs9/index.html | 26 +++++ servant-examples/getting-started/gs9/ui.js | 61 +++++++++++ servant-examples/servant-examples.cabal | 5 +- 5 files changed, 206 insertions(+), 13 deletions(-) create mode 100644 servant-examples/getting-started/GS9.hs create mode 100644 servant-examples/getting-started/gs9/index.html create mode 100644 servant-examples/getting-started/gs9/ui.js diff --git a/servant-examples/getting-started/GS9.hs b/servant-examples/getting-started/GS9.hs new file mode 100644 index 00000000..3d7a16c6 --- /dev/null +++ b/servant-examples/getting-started/GS9.hs @@ -0,0 +1,101 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE TypeOperators #-} +module GS9 where + +import Control.Applicative +import Control.Monad.IO.Class +import Data.Aeson +import Data.Char +import Data.List +import GHC.Generics +import qualified Language.Javascript.JQuery as JQ +import Math.Probable +import Network.Wai +import Servant +import Servant.JQuery + +data Point = Point + { x :: Double + , y :: Double + } deriving Generic + +instance ToJSON Point + +randomPoint :: MonadIO m => m Point +randomPoint = liftIO . mwc $ Point <$> d <*> d + + where d = doubleIn (-1, 1) + +data Search a = Search + { query :: String + , results :: [a] + } deriving Generic + +mkSearch :: String -> [a] -> Search a +mkSearch = Search + +instance ToJSON a => ToJSON (Search a) + +data Book = Book + { author :: String + , title :: String + , year :: Int + } deriving Generic + +instance ToJSON Book + +book :: String -> String -> Int -> Book +book = Book + +books :: [Book] +books = + [ book "Paul Hudak" "The Haskell School of Expression: Learning Functional Programming through Multimedia" 2000 + , book "Bryan O'Sullivan, Don Stewart, and John Goerzen" "Real World Haskell" 2008 + , book "Miran Lipovača" "Learn You a Haskell for Great Good!" 2011 + , book "Graham Hutton" "Programming in Haskell" 2007 + , book "Simon Marlow" "Parallel and Concurrent Programming in Haskell" 2013 + , book "Richard Bird" "Introduction to Functional Programming using Haskell" 1998 + ] + +searchBook :: Monad m => Maybe String -> m (Search Book) +searchBook Nothing = return (mkSearch "" books) +searchBook (Just q) = return (mkSearch q books') + + where books' = filter (\b -> q' `isInfixOf` map toLower (author b) + || q' `isInfixOf` map toLower (title b) + ) + books + q' = map toLower q + +type API = "point" :> Get '[JSON] Point + :<|> "books" :> QueryParam "q" String :> Get '[JSON] (Search Book) + +type API' = API :<|> Raw + +api :: Proxy API +api = Proxy + +api' :: Proxy API' +api' = Proxy + +server :: Server API +server = randomPoint + :<|> searchBook + +server' :: Server API' +server' = server + :<|> serveDirectory "getting-started/gs9" + +apiJS :: String +apiJS = jsForAPI api + +writeJSFiles :: IO () +writeJSFiles = do + writeFile "getting-started/gs9/api.js" apiJS + jq <- readFile =<< JQ.file + writeFile "getting-started/gs9/jq.js" jq + +app :: Application +app = serve api' server' diff --git a/servant-examples/getting-started/getting-started.hs b/servant-examples/getting-started/getting-started.hs index bf7103d2..d0b90950 100644 --- a/servant-examples/getting-started/getting-started.hs +++ b/servant-examples/getting-started/getting-started.hs @@ -9,24 +9,26 @@ import qualified GS4 import qualified GS5 import qualified GS6 import qualified GS7 +import qualified GS9 -app :: String -> Maybe Application -app n = case n of - "1" -> Just GS1.app - "2" -> Just GS2.app - "3" -> Just GS3.app - "4" -> Just GS4.app - "5" -> Just GS5.app - "6" -> Just GS6.app - "7" -> Just GS7.app - "8" -> Just GS3.app - _ -> Nothing +app :: String -> (Application -> IO ()) -> IO () +app n f = case n of + "1" -> f GS1.app + "2" -> f GS2.app + "3" -> f GS3.app + "4" -> f GS4.app + "5" -> f GS5.app + "6" -> f GS6.app + "7" -> f GS7.app + "8" -> f GS3.app + "9" -> GS9.writeJSFiles >> f GS9.app + _ -> usage main :: IO () main = do args <- getArgs case args of - [n] -> maybe usage (run 8081) (app n) + [n] -> app n (run 8081) _ -> usage usage :: IO () diff --git a/servant-examples/getting-started/gs9/index.html b/servant-examples/getting-started/gs9/index.html new file mode 100644 index 00000000..27d8630c --- /dev/null +++ b/servant-examples/getting-started/gs9/index.html @@ -0,0 +1,26 @@ + + + + + + Getting started - 9 - servant-jquery + + +

Books

+ +
+

Results for ""

+
    +
+
+
+

Approximating π

+

Count: 0

+

Successes: 0

+

+ + + + + + \ No newline at end of file diff --git a/servant-examples/getting-started/gs9/ui.js b/servant-examples/getting-started/gs9/ui.js new file mode 100644 index 00000000..755de45b --- /dev/null +++ b/servant-examples/getting-started/gs9/ui.js @@ -0,0 +1,61 @@ +/* book search */ +function updateResults(data) +{ + console.log(data); + $('#results').html(""); + $('#query').text("\"" + data.query + "\""); + for(var i = 0; i < data.results.length; i++) + { + $('#results').append(renderBook(data.results[i])); + } +} + +function renderBook(book) +{ + var li = '
  • ' + book.title + ', ' + + book.author + ' - ' + book.year + '
  • '; + return li; +} + +function searchBooks() +{ + var q = $('#q').val(); + getbooks(q, updateResults, console.log) +} + +searchBooks(); +$('#q').keyup(function() { + searchBooks(); +}); + +/* approximating pi */ +var count = 0; +var successes = 0; + +function f(data) +{ + var x = data.x, y = data.y; + if(x*x + y*y <= 1) + { + successes++; + } + + count++; + + update('#count', count); + update('#successes', successes); + update('#pi', 4*successes/count); +} + +function update(id, val) +{ + $(id).text(val); +} + +function refresh() +{ + getpoint(f, console.log); +} + +window.setInterval(refresh, 200); + diff --git a/servant-examples/servant-examples.cabal b/servant-examples/servant-examples.cabal index 287e26bd..dab0ab5f 100644 --- a/servant-examples/servant-examples.cabal +++ b/servant-examples/servant-examples.cabal @@ -15,14 +15,17 @@ cabal-version: >=1.10 executable getting-started main-is: getting-started.hs - other-modules: GS1, GS2 + other-modules: GS1, GS2, GS3, GS4, GS5, GS6, GS7, GS8, GS9 build-depends: aeson >= 0.8 , base >= 4.7 , directory , either + , js-jquery , lucid + , probable , servant + , servant-jquery , servant-lucid , servant-server , time From 616b203792e044595348af971e7469f6abe1a24f Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Sat, 9 May 2015 16:05:09 +0200 Subject: [PATCH 11/15] examples: add GS10 --- servant-examples/getting-started/GS10.hs | 94 +++++++++++++++++++ servant-examples/getting-started/GS9.hs | 28 +++--- .../getting-started/getting-started.hs | 2 + servant-examples/servant-examples.cabal | 8 +- 4 files changed, 118 insertions(+), 14 deletions(-) create mode 100644 servant-examples/getting-started/GS10.hs diff --git a/servant-examples/getting-started/GS10.hs b/servant-examples/getting-started/GS10.hs new file mode 100644 index 00000000..3456df2f --- /dev/null +++ b/servant-examples/getting-started/GS10.hs @@ -0,0 +1,94 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE MultiParamTypeClasses #-} +module GS10 where + +import Data.ByteString.Lazy (ByteString) +import Data.Text.Lazy (pack) +import Data.Text.Lazy.Encoding (encodeUtf8) +import Network.HTTP.Types +import Network.Wai +import Servant +import Servant.Docs +import Servant.Docs.Pandoc (pandoc) +import Text.Pandoc.Options (def, WriterOptions(..)) +import Text.Pandoc.Writers.HTML (writeHtmlString) +import qualified GS3 + +type DocsAPI = GS3.API :<|> Raw + +instance ToCapture (Capture "x" Int) where + toCapture _ = DocCapture "x" "(integer) position on the x axis" + +instance ToCapture (Capture "y" Int) where + toCapture _ = DocCapture "y" "(integer) position on the y axis" + +instance ToSample GS3.Position GS3.Position where + toSample _ = Just (GS3.Position 3 14) + +instance ToParam (QueryParam "name" String) where + toParam _ = + DocQueryParam "name" + ["Alp", "John Doe", "..."] + "Name of the person to say hello to." + Normal + +instance ToSample GS3.HelloMessage GS3.HelloMessage where + toSamples _ = + [ ("When a value is provided for 'name'", GS3.HelloMessage "Hello, Alp") + , ("When 'name' is not specified", GS3.HelloMessage "Hello, anonymous coward") + ] + +ci :: GS3.ClientInfo +ci = GS3.ClientInfo "Alp" "alp@foo.com" 26 ["haskell", "mathematics"] + +instance ToSample GS3.ClientInfo GS3.ClientInfo where + toSample _ = Just ci + +instance ToSample GS3.Email GS3.Email where + toSample _ = Just (GS3.emailForClient ci) + +api :: Proxy DocsAPI +api = Proxy + +docsBS :: ByteString +docsBS = encodeUtf8 + . pack + . writeHtmlString opts + . pandoc + $ docsWithIntros [intro] GS3.api + + where opts = def { writerHtml5 = True + , writerTableOfContents = True + , writerHighlight = True + , writerStandalone = True + , writerTemplate = + concat + [ "" + , "" + , "" + , "API Docs - $title$" + , "" + , "" + , "$toc$" + , "
    " + , "$body$" + , "" + , "" + ] + } + intro = DocIntro "Welcome" ["This is our super webservice's API.", "Enjoy!"] + +server :: Server DocsAPI +server = GS3.server :<|> serveDocs + + where serveDocs _ respond = + respond $ responseLBS ok200 [html] docsBS + + html = ("Content-Type", "text/html") + +app :: Application +app = serve api server diff --git a/servant-examples/getting-started/GS9.hs b/servant-examples/getting-started/GS9.hs index 3d7a16c6..23b65514 100644 --- a/servant-examples/getting-started/GS9.hs +++ b/servant-examples/getting-started/GS9.hs @@ -2,20 +2,22 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TypeOperators #-} +{-# LANGUAGE OverloadedStrings #-} module GS9 where import Control.Applicative import Control.Monad.IO.Class import Data.Aeson -import Data.Char -import Data.List +import Data.Text (Text) import GHC.Generics -import qualified Language.Javascript.JQuery as JQ import Math.Probable import Network.Wai import Servant import Servant.JQuery +import qualified Data.Text as T +import qualified Language.Javascript.JQuery as JQ + data Point = Point { x :: Double , y :: Double @@ -29,24 +31,24 @@ randomPoint = liftIO . mwc $ Point <$> d <*> d where d = doubleIn (-1, 1) data Search a = Search - { query :: String + { query :: Text , results :: [a] } deriving Generic -mkSearch :: String -> [a] -> Search a +mkSearch :: Text -> [a] -> Search a mkSearch = Search instance ToJSON a => ToJSON (Search a) data Book = Book - { author :: String - , title :: String + { author :: Text + , title :: Text , year :: Int } deriving Generic instance ToJSON Book -book :: String -> String -> Int -> Book +book :: Text -> Text -> Int -> Book book = Book books :: [Book] @@ -59,18 +61,18 @@ books = , book "Richard Bird" "Introduction to Functional Programming using Haskell" 1998 ] -searchBook :: Monad m => Maybe String -> m (Search Book) +searchBook :: Monad m => Maybe Text -> m (Search Book) searchBook Nothing = return (mkSearch "" books) searchBook (Just q) = return (mkSearch q books') - where books' = filter (\b -> q' `isInfixOf` map toLower (author b) - || q' `isInfixOf` map toLower (title b) + where books' = filter (\b -> q' `T.isInfixOf` T.toLower (author b) + || q' `T.isInfixOf` T.toLower (title b) ) books - q' = map toLower q + q' = T.toLower q type API = "point" :> Get '[JSON] Point - :<|> "books" :> QueryParam "q" String :> Get '[JSON] (Search Book) + :<|> "books" :> QueryParam "q" Text :> Get '[JSON] (Search Book) type API' = API :<|> Raw diff --git a/servant-examples/getting-started/getting-started.hs b/servant-examples/getting-started/getting-started.hs index d0b90950..b1335af7 100644 --- a/servant-examples/getting-started/getting-started.hs +++ b/servant-examples/getting-started/getting-started.hs @@ -10,6 +10,7 @@ import qualified GS5 import qualified GS6 import qualified GS7 import qualified GS9 +import qualified GS10 app :: String -> (Application -> IO ()) -> IO () app n f = case n of @@ -22,6 +23,7 @@ app n f = case n of "7" -> f GS7.app "8" -> f GS3.app "9" -> GS9.writeJSFiles >> f GS9.app + "10" -> f GS10.app _ -> usage main :: IO () diff --git a/servant-examples/servant-examples.cabal b/servant-examples/servant-examples.cabal index dab0ab5f..7bc9e77a 100644 --- a/servant-examples/servant-examples.cabal +++ b/servant-examples/servant-examples.cabal @@ -15,19 +15,25 @@ cabal-version: >=1.10 executable getting-started main-is: getting-started.hs - other-modules: GS1, GS2, GS3, GS4, GS5, GS6, GS7, GS8, GS9 + other-modules: GS1, GS2, GS3, GS4, GS5, GS6, GS7, GS8, GS9, GS10 build-depends: aeson >= 0.8 , base >= 4.7 + , bytestring , directory , either + , http-types , js-jquery , lucid + , pandoc , probable , servant + , servant-docs , servant-jquery , servant-lucid + , servant-pandoc >= 0.2 , servant-server + , text , time , transformers , wai From a403eee488bc63cda2c8c312c6780c98ad56ff99 Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Sat, 9 May 2015 16:27:36 +0200 Subject: [PATCH 12/15] examples: don't depend on pandoc & servant-pandoc, just serve a markdown file --- servant-examples/getting-started/GS10.hs | 31 +++--------------------- servant-examples/servant-examples.cabal | 2 -- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/servant-examples/getting-started/GS10.hs b/servant-examples/getting-started/GS10.hs index 3456df2f..fb8e4bf3 100644 --- a/servant-examples/getting-started/GS10.hs +++ b/servant-examples/getting-started/GS10.hs @@ -13,9 +13,6 @@ import Network.HTTP.Types import Network.Wai import Servant import Servant.Docs -import Servant.Docs.Pandoc (pandoc) -import Text.Pandoc.Options (def, WriterOptions(..)) -import Text.Pandoc.Writers.HTML (writeHtmlString) import qualified GS3 type DocsAPI = GS3.API :<|> Raw @@ -57,38 +54,18 @@ api = Proxy docsBS :: ByteString docsBS = encodeUtf8 . pack - . writeHtmlString opts - . pandoc + . markdown $ docsWithIntros [intro] GS3.api - where opts = def { writerHtml5 = True - , writerTableOfContents = True - , writerHighlight = True - , writerStandalone = True - , writerTemplate = - concat - [ "" - , "" - , "" - , "API Docs - $title$" - , "" - , "" - , "$toc$" - , "
    " - , "$body$" - , "" - , "" - ] - } - intro = DocIntro "Welcome" ["This is our super webservice's API.", "Enjoy!"] + where intro = DocIntro "Welcome" ["This is our super webservice's API.", "Enjoy!"] server :: Server DocsAPI server = GS3.server :<|> serveDocs where serveDocs _ respond = - respond $ responseLBS ok200 [html] docsBS + respond $ responseLBS ok200 [plain] docsBS - html = ("Content-Type", "text/html") + plain = ("Content-Type", "text/plain") app :: Application app = serve api server diff --git a/servant-examples/servant-examples.cabal b/servant-examples/servant-examples.cabal index 7bc9e77a..a5faa08b 100644 --- a/servant-examples/servant-examples.cabal +++ b/servant-examples/servant-examples.cabal @@ -25,13 +25,11 @@ executable getting-started , http-types , js-jquery , lucid - , pandoc , probable , servant , servant-docs , servant-jquery , servant-lucid - , servant-pandoc >= 0.2 , servant-server , text , time From e0f4ed64261ff163d0e35da73c686690660db33a Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Sat, 9 May 2015 17:31:54 +0200 Subject: [PATCH 13/15] adapt examples to baseurl fix --- servant-examples/getting-started/GS8.hs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/servant-examples/getting-started/GS8.hs b/servant-examples/getting-started/GS8.hs index 56039e5a..ace1991a 100644 --- a/servant-examples/getting-started/GS8.hs +++ b/servant-examples/getting-started/GS8.hs @@ -12,27 +12,24 @@ import GS3 position :: Int -- ^ value for "x" -> Int -- ^ value for "y" - -> BaseUrl -> EitherT ServantError IO Position hello :: Maybe String -- ^ an optional value for "name" - -> BaseUrl -> EitherT ServantError IO HelloMessage marketing :: ClientInfo -- ^ value for the request body - -> BaseUrl -> EitherT ServantError IO Email -position :<|> hello :<|> marketing = client api +position :<|> hello :<|> marketing = client api baseUrl baseUrl :: BaseUrl baseUrl = BaseUrl Http "localhost" 8081 queries :: EitherT ServantError IO (Position, HelloMessage, Email) queries = do - pos <- position 10 10 baseUrl - msg <- hello (Just "servant") baseUrl - em <- marketing (ClientInfo "Alp" "alp@foo.com" 26 ["haskell", "mathematics"]) baseUrl + pos <- position 10 10 + msg <- hello (Just "servant") + em <- marketing (ClientInfo "Alp" "alp@foo.com" 26 ["haskell", "mathematics"]) return (pos, msg, em) run :: IO () From 46537447066bc43288f61588d1966cfc7eb71ffe Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Sun, 10 May 2015 12:20:02 +0200 Subject: [PATCH 14/15] examples: use 'random' instead of 'probable' for GS9 --- servant-examples/getting-started/GS9.hs | 9 +++++---- servant-examples/servant-examples.cabal | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/servant-examples/getting-started/GS9.hs b/servant-examples/getting-started/GS9.hs index 23b65514..382dd068 100644 --- a/servant-examples/getting-started/GS9.hs +++ b/servant-examples/getting-started/GS9.hs @@ -10,10 +10,10 @@ import Control.Monad.IO.Class import Data.Aeson import Data.Text (Text) import GHC.Generics -import Math.Probable import Network.Wai import Servant import Servant.JQuery +import System.Random import qualified Data.Text as T import qualified Language.Javascript.JQuery as JQ @@ -26,9 +26,10 @@ data Point = Point instance ToJSON Point randomPoint :: MonadIO m => m Point -randomPoint = liftIO . mwc $ Point <$> d <*> d - - where d = doubleIn (-1, 1) +randomPoint = liftIO . getStdRandom $ \g -> + let (rx, g') = randomR (-1, 1) g + (ry, g'') = randomR (-1, 1) g' + in (Point rx ry, g'') data Search a = Search { query :: Text diff --git a/servant-examples/servant-examples.cabal b/servant-examples/servant-examples.cabal index a5faa08b..30db3497 100644 --- a/servant-examples/servant-examples.cabal +++ b/servant-examples/servant-examples.cabal @@ -25,7 +25,7 @@ executable getting-started , http-types , js-jquery , lucid - , probable + , random , servant , servant-docs , servant-jquery From 39d114ab1227c63ddcd2b2ad31ac9cef56981f9c Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Sun, 10 May 2015 13:39:18 +0200 Subject: [PATCH 15/15] getting-started -> tutorial --- .../getting-started/getting-started.hs | 39 ------------------- servant-examples/servant-examples.cabal | 14 +++---- .../GS1.hs => tutorial/T1.hs} | 2 +- .../GS10.hs => tutorial/T10.hs} | 30 +++++++------- .../GS2.hs => tutorial/T2.hs} | 2 +- .../GS3.hs => tutorial/T3.hs} | 2 +- .../GS4.hs => tutorial/T4.hs} | 2 +- .../GS5.hs => tutorial/T5.hs} | 2 +- .../GS6.hs => tutorial/T6.hs} | 4 +- .../GS7.hs => tutorial/T7.hs} | 2 +- .../GS8.hs => tutorial/T8.hs} | 4 +- .../GS9.hs => tutorial/T9.hs} | 8 ++-- .../gs8-main.hs => tutorial/t8-main.hs} | 2 +- .../gs9 => tutorial/t9}/index.html | 2 +- .../gs9 => tutorial/t9}/ui.js | 0 servant-examples/tutorial/tutorial.hs | 39 +++++++++++++++++++ 16 files changed, 77 insertions(+), 77 deletions(-) delete mode 100644 servant-examples/getting-started/getting-started.hs rename servant-examples/{getting-started/GS1.hs => tutorial/T1.hs} (98%) rename servant-examples/{getting-started/GS10.hs => tutorial/T10.hs} (64%) rename servant-examples/{getting-started/GS2.hs => tutorial/T2.hs} (98%) rename servant-examples/{getting-started/GS3.hs => tutorial/T3.hs} (99%) rename servant-examples/{getting-started/GS4.hs => tutorial/T4.hs} (98%) rename servant-examples/{getting-started/GS5.hs => tutorial/T5.hs} (98%) rename servant-examples/{getting-started/GS6.hs => tutorial/T6.hs} (80%) rename servant-examples/{getting-started/GS7.hs => tutorial/T7.hs} (97%) rename servant-examples/{getting-started/GS8.hs => tutorial/T8.hs} (97%) rename servant-examples/{getting-started/GS9.hs => tutorial/T9.hs} (94%) rename servant-examples/{getting-started/gs8-main.hs => tutorial/t8-main.hs} (70%) rename servant-examples/{getting-started/gs9 => tutorial/t9}/index.html (93%) rename servant-examples/{getting-started/gs9 => tutorial/t9}/ui.js (100%) create mode 100644 servant-examples/tutorial/tutorial.hs diff --git a/servant-examples/getting-started/getting-started.hs b/servant-examples/getting-started/getting-started.hs deleted file mode 100644 index b1335af7..00000000 --- a/servant-examples/getting-started/getting-started.hs +++ /dev/null @@ -1,39 +0,0 @@ -import Network.Wai -import Network.Wai.Handler.Warp -import System.Environment - -import qualified GS1 -import qualified GS2 -import qualified GS3 -import qualified GS4 -import qualified GS5 -import qualified GS6 -import qualified GS7 -import qualified GS9 -import qualified GS10 - -app :: String -> (Application -> IO ()) -> IO () -app n f = case n of - "1" -> f GS1.app - "2" -> f GS2.app - "3" -> f GS3.app - "4" -> f GS4.app - "5" -> f GS5.app - "6" -> f GS6.app - "7" -> f GS7.app - "8" -> f GS3.app - "9" -> GS9.writeJSFiles >> f GS9.app - "10" -> f GS10.app - _ -> usage - -main :: IO () -main = do - args <- getArgs - case args of - [n] -> app n (run 8081) - _ -> usage - -usage :: IO () -usage = do - putStrLn "Usage:\t getting-started N" - putStrLn "\t\twhere N is the number of the example you want to run." diff --git a/servant-examples/servant-examples.cabal b/servant-examples/servant-examples.cabal index 30db3497..371a4fd3 100644 --- a/servant-examples/servant-examples.cabal +++ b/servant-examples/servant-examples.cabal @@ -13,9 +13,9 @@ category: Web build-type: Simple cabal-version: >=1.10 -executable getting-started - main-is: getting-started.hs - other-modules: GS1, GS2, GS3, GS4, GS5, GS6, GS7, GS8, GS9, GS10 +executable tutorial + main-is: tutorial.hs + other-modules: T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 build-depends: aeson >= 0.8 , base >= 4.7 @@ -36,12 +36,12 @@ executable getting-started , transformers , wai , warp - hs-source-dirs: getting-started + hs-source-dirs: tutorial default-language: Haskell2010 -executable gs8-main - main-is: gs8-main.hs - hs-source-dirs: getting-started +executable t8-main + main-is: t8-main.hs + hs-source-dirs: tutorial default-language: Haskell2010 build-depends: aeson diff --git a/servant-examples/getting-started/GS1.hs b/servant-examples/tutorial/T1.hs similarity index 98% rename from servant-examples/getting-started/GS1.hs rename to servant-examples/tutorial/T1.hs index a6daffec..9fb93c8c 100644 --- a/servant-examples/getting-started/GS1.hs +++ b/servant-examples/tutorial/T1.hs @@ -2,7 +2,7 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TypeOperators #-} -module GS1 where +module T1 where import Data.Aeson import Data.Time.Calendar diff --git a/servant-examples/getting-started/GS10.hs b/servant-examples/tutorial/T10.hs similarity index 64% rename from servant-examples/getting-started/GS10.hs rename to servant-examples/tutorial/T10.hs index fb8e4bf3..39e48e04 100644 --- a/servant-examples/getting-started/GS10.hs +++ b/servant-examples/tutorial/T10.hs @@ -4,7 +4,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE MultiParamTypeClasses #-} -module GS10 where +module T10 where import Data.ByteString.Lazy (ByteString) import Data.Text.Lazy (pack) @@ -13,9 +13,9 @@ import Network.HTTP.Types import Network.Wai import Servant import Servant.Docs -import qualified GS3 +import qualified T3 -type DocsAPI = GS3.API :<|> Raw +type DocsAPI = T3.API :<|> Raw instance ToCapture (Capture "x" Int) where toCapture _ = DocCapture "x" "(integer) position on the x axis" @@ -23,8 +23,8 @@ instance ToCapture (Capture "x" Int) where instance ToCapture (Capture "y" Int) where toCapture _ = DocCapture "y" "(integer) position on the y axis" -instance ToSample GS3.Position GS3.Position where - toSample _ = Just (GS3.Position 3 14) +instance ToSample T3.Position T3.Position where + toSample _ = Just (T3.Position 3 14) instance ToParam (QueryParam "name" String) where toParam _ = @@ -33,20 +33,20 @@ instance ToParam (QueryParam "name" String) where "Name of the person to say hello to." Normal -instance ToSample GS3.HelloMessage GS3.HelloMessage where +instance ToSample T3.HelloMessage T3.HelloMessage where toSamples _ = - [ ("When a value is provided for 'name'", GS3.HelloMessage "Hello, Alp") - , ("When 'name' is not specified", GS3.HelloMessage "Hello, anonymous coward") + [ ("When a value is provided for 'name'", T3.HelloMessage "Hello, Alp") + , ("When 'name' is not specified", T3.HelloMessage "Hello, anonymous coward") ] -ci :: GS3.ClientInfo -ci = GS3.ClientInfo "Alp" "alp@foo.com" 26 ["haskell", "mathematics"] +ci :: T3.ClientInfo +ci = T3.ClientInfo "Alp" "alp@foo.com" 26 ["haskell", "mathematics"] -instance ToSample GS3.ClientInfo GS3.ClientInfo where +instance ToSample T3.ClientInfo T3.ClientInfo where toSample _ = Just ci -instance ToSample GS3.Email GS3.Email where - toSample _ = Just (GS3.emailForClient ci) +instance ToSample T3.Email T3.Email where + toSample _ = Just (T3.emailForClient ci) api :: Proxy DocsAPI api = Proxy @@ -55,12 +55,12 @@ docsBS :: ByteString docsBS = encodeUtf8 . pack . markdown - $ docsWithIntros [intro] GS3.api + $ docsWithIntros [intro] T3.api where intro = DocIntro "Welcome" ["This is our super webservice's API.", "Enjoy!"] server :: Server DocsAPI -server = GS3.server :<|> serveDocs +server = T3.server :<|> serveDocs where serveDocs _ respond = respond $ responseLBS ok200 [plain] docsBS diff --git a/servant-examples/getting-started/GS2.hs b/servant-examples/tutorial/T2.hs similarity index 98% rename from servant-examples/getting-started/GS2.hs rename to servant-examples/tutorial/T2.hs index bf935320..69ded18c 100644 --- a/servant-examples/getting-started/GS2.hs +++ b/servant-examples/tutorial/T2.hs @@ -2,7 +2,7 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TypeOperators #-} -module GS2 where +module T2 where import Data.Aeson import Data.Time.Calendar diff --git a/servant-examples/getting-started/GS3.hs b/servant-examples/tutorial/T3.hs similarity index 99% rename from servant-examples/getting-started/GS3.hs rename to servant-examples/tutorial/T3.hs index 505982cf..dcff930b 100644 --- a/servant-examples/getting-started/GS3.hs +++ b/servant-examples/tutorial/T3.hs @@ -2,7 +2,7 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TypeOperators #-} -module GS3 where +module T3 where import Control.Monad.Trans.Either import Data.Aeson diff --git a/servant-examples/getting-started/GS4.hs b/servant-examples/tutorial/T4.hs similarity index 98% rename from servant-examples/getting-started/GS4.hs rename to servant-examples/tutorial/T4.hs index 89e7cce3..595eabcb 100644 --- a/servant-examples/getting-started/GS4.hs +++ b/servant-examples/tutorial/T4.hs @@ -4,7 +4,7 @@ {-# LANGUAGE TypeOperators #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} -module GS4 where +module T4 where import Data.Aeson import Data.Foldable (foldMap) diff --git a/servant-examples/getting-started/GS5.hs b/servant-examples/tutorial/T5.hs similarity index 98% rename from servant-examples/getting-started/GS5.hs rename to servant-examples/tutorial/T5.hs index 6a6c91b7..0811fd9b 100644 --- a/servant-examples/getting-started/GS5.hs +++ b/servant-examples/tutorial/T5.hs @@ -3,7 +3,7 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE OverloadedStrings #-} -module GS5 where +module T5 where import Control.Monad.IO.Class import Control.Monad.Trans.Either diff --git a/servant-examples/getting-started/GS6.hs b/servant-examples/tutorial/T6.hs similarity index 80% rename from servant-examples/getting-started/GS6.hs rename to servant-examples/tutorial/T6.hs index 4fdc3faa..c0abb1df 100644 --- a/servant-examples/getting-started/GS6.hs +++ b/servant-examples/tutorial/T6.hs @@ -1,7 +1,7 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} -module GS6 where +module T6 where import Network.Wai import Servant @@ -12,7 +12,7 @@ api :: Proxy API api = Proxy server :: Server API -server = serveDirectory "getting-started" +server = serveDirectory "tutorial" app :: Application app = serve api server diff --git a/servant-examples/getting-started/GS7.hs b/servant-examples/tutorial/T7.hs similarity index 97% rename from servant-examples/getting-started/GS7.hs rename to servant-examples/tutorial/T7.hs index 83dfb93a..d67cfcd4 100644 --- a/servant-examples/getting-started/GS7.hs +++ b/servant-examples/tutorial/T7.hs @@ -1,7 +1,7 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} -module GS7 where +module T7 where import Control.Monad.Trans.Either import Control.Monad.Trans.Reader diff --git a/servant-examples/getting-started/GS8.hs b/servant-examples/tutorial/T8.hs similarity index 97% rename from servant-examples/getting-started/GS8.hs rename to servant-examples/tutorial/T8.hs index ace1991a..089702bc 100644 --- a/servant-examples/getting-started/GS8.hs +++ b/servant-examples/tutorial/T8.hs @@ -1,14 +1,14 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} -module GS8 where +module T8 where import Control.Monad.Trans.Either import Data.Aeson import Servant import Servant.Client -import GS3 +import T3 position :: Int -- ^ value for "x" -> Int -- ^ value for "y" diff --git a/servant-examples/getting-started/GS9.hs b/servant-examples/tutorial/T9.hs similarity index 94% rename from servant-examples/getting-started/GS9.hs rename to servant-examples/tutorial/T9.hs index 382dd068..1b0633f0 100644 --- a/servant-examples/getting-started/GS9.hs +++ b/servant-examples/tutorial/T9.hs @@ -3,7 +3,7 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE OverloadedStrings #-} -module GS9 where +module T9 where import Control.Applicative import Control.Monad.IO.Class @@ -89,16 +89,16 @@ server = randomPoint server' :: Server API' server' = server - :<|> serveDirectory "getting-started/gs9" + :<|> serveDirectory "tutorial/t9" apiJS :: String apiJS = jsForAPI api writeJSFiles :: IO () writeJSFiles = do - writeFile "getting-started/gs9/api.js" apiJS + writeFile "tutorial/t9/api.js" apiJS jq <- readFile =<< JQ.file - writeFile "getting-started/gs9/jq.js" jq + writeFile "tutorial/t9/jq.js" jq app :: Application app = serve api' server' diff --git a/servant-examples/getting-started/gs8-main.hs b/servant-examples/tutorial/t8-main.hs similarity index 70% rename from servant-examples/getting-started/gs8-main.hs rename to servant-examples/tutorial/t8-main.hs index b6a9e30e..b70d5a61 100644 --- a/servant-examples/getting-started/gs8-main.hs +++ b/servant-examples/tutorial/t8-main.hs @@ -1,4 +1,4 @@ -import GS8 +import T8 main :: IO () main = run diff --git a/servant-examples/getting-started/gs9/index.html b/servant-examples/tutorial/t9/index.html similarity index 93% rename from servant-examples/getting-started/gs9/index.html rename to servant-examples/tutorial/t9/index.html index 27d8630c..7ec49c70 100644 --- a/servant-examples/getting-started/gs9/index.html +++ b/servant-examples/tutorial/t9/index.html @@ -3,7 +3,7 @@ - Getting started - 9 - servant-jquery + Tutorial - 9 - servant-jquery

    Books

    diff --git a/servant-examples/getting-started/gs9/ui.js b/servant-examples/tutorial/t9/ui.js similarity index 100% rename from servant-examples/getting-started/gs9/ui.js rename to servant-examples/tutorial/t9/ui.js diff --git a/servant-examples/tutorial/tutorial.hs b/servant-examples/tutorial/tutorial.hs new file mode 100644 index 00000000..55f0408b --- /dev/null +++ b/servant-examples/tutorial/tutorial.hs @@ -0,0 +1,39 @@ +import Network.Wai +import Network.Wai.Handler.Warp +import System.Environment + +import qualified T1 +import qualified T2 +import qualified T3 +import qualified T4 +import qualified T5 +import qualified T6 +import qualified T7 +import qualified T9 +import qualified T10 + +app :: String -> (Application -> IO ()) -> IO () +app n f = case n of + "1" -> f T1.app + "2" -> f T2.app + "3" -> f T3.app + "4" -> f T4.app + "5" -> f T5.app + "6" -> f T6.app + "7" -> f T7.app + "8" -> f T3.app + "9" -> T9.writeJSFiles >> f T9.app + "10" -> f T10.app + _ -> usage + +main :: IO () +main = do + args <- getArgs + case args of + [n] -> app n (run 8081) + _ -> usage + +usage :: IO () +usage = do + putStrLn "Usage:\t tutorial N" + putStrLn "\t\twhere N is the number of the example you want to run."