git.iko.soy/typeable/servant
on master help sign in
add an example on how to derive client functions for Hackage
commit29632c4ac23ad0e630ab2321553695247d883973 authorAlp Mestanogullari <alpmestan@gmail.com> dateThu 23 Apr 2015, 13:07 (11 years ago) parent6651df5
4 files changed+144
servant-examples/LICENSE+30 servant-examples/Setup.hs+2 servant-examples/hackage/hackage.hs+83 servant-examples/servant-examples.cabal+29
servant-examples/LICENSEnew file+30
@@ -0,0 +1,30 @@
1+Copyright (c) 2015, Alp Mestanogullari
2+
3+All rights reserved.
4+
5+Redistribution and use in source and binary forms, with or without
6+modification, are permitted provided that the following conditions are met:
7+
8+ * Redistributions of source code must retain the above copyright
9+ notice, this list of conditions and the following disclaimer.
10+
11+ * Redistributions in binary form must reproduce the above
12+ copyright notice, this list of conditions and the following
13+ disclaimer in the documentation and/or other materials provided
14+ with the distribution.
15+
16+ * Neither the name of Alp Mestanogullari nor the names of other
17+ contributors may be used to endorse or promote products derived
18+ from this software without specific prior written permission.
19+
20+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
servant-examples/Setup.hsnew file+2
@@ -0,0 +1,2 @@
1+import Distribution.Simple
2+main = defaultMain
servant-examples/hackage/hackage.hsnew file+83
@@ -0,0 +1,83 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE DeriveGeneric #-}
3+{-# LANGUAGE TypeOperators #-}
4+{-# LANGUAGE OverloadedStrings #-}
5+import Control.Applicative
6+import Control.Monad
7+import Control.Monad.IO.Class
8+import Control.Monad.Trans.Either
9+import Data.Aeson
10+import Data.Monoid
11+import Data.Proxy
12+import Data.Text (Text)
13+import GHC.Generics
14+import Servant.API
15+import Servant.Client
16+
17+import qualified Data.Text as T
18+import qualified Data.Text.IO as T
19+
20+type HackageAPI =
21+ "users" :> Get '[JSON] [UserSummary]
22+ :<|> "user" :> Capture "username" Username :> Get '[JSON] UserDetailed
23+ :<|> "packages" :> Get '[JSON] [Package]
24+
25+type Username = Text
26+
27+data UserSummary = UserSummary
28+ { summaryUsername :: Username
29+ , summaryUserid :: Int
30+ } deriving (Eq, Show)
31+
32+instance FromJSON UserSummary where
33+ parseJSON (Object o) =
34+ UserSummary <$> o .: "username"
35+ <*> o .: "userid"
36+
37+ parseJSON _ = mzero
38+
39+type Group = Text
40+
41+data UserDetailed = UserDetailed
42+ { username :: Username
43+ , userid :: Int
44+ , groups :: [Group]
45+ } deriving (Eq, Show, Generic)
46+
47+instance FromJSON UserDetailed
48+
49+newtype Package = Package { packageName :: Text }
50+ deriving (Eq, Show, Generic)
51+
52+instance FromJSON Package
53+
54+hackageAPI :: Proxy HackageAPI
55+hackageAPI = Proxy
56+
57+getUsers :: BaseUrl -> EitherT ServantError IO [UserSummary]
58+getUser :: Username -> BaseUrl -> EitherT ServantError IO UserDetailed
59+getPackages :: BaseUrl -> EitherT ServantError IO [Package]
60+getUsers :<|> getUser :<|> getPackages = client hackageAPI
61+
62+run :: (BaseUrl -> r) -> r
63+run f = f (BaseUrl Http "hackage.haskell.org" 80)
64+
65+main :: IO ()
66+main = print =<< uselessNumbers
67+
68+uselessNumbers :: IO (Either ServantError ())
69+uselessNumbers = runEitherT $ do
70+ users <- run getUsers
71+ liftIO . putStrLn $ show (length users) ++ " users"
72+
73+ user <- liftIO $ do
74+ putStrLn "Enter a valid hackage username"
75+ T.getLine
76+ userDetailed <- run (getUser user)
77+ liftIO . T.putStrLn $ user <> " maintains " <> T.pack (show (length $ groups userDetailed)) <> " packages"
78+
79+ packages <- run getPackages
80+ let monadPackages = filter (isMonadPackage . packageName) packages
81+ liftIO . putStrLn $ show (length monadPackages) ++ " monad packages"
82+
83+ where isMonadPackage = T.isInfixOf "monad"
servant-examples/servant-examples.cabalnew file+29
@@ -0,0 +1,29 @@
1+name: servant-examples
2+version: 0.3
3+synopsis: Example programs for servant
4+description: Example programs for servant
5+homepage: http://haskell-servant.github.io/
6+license: BSD3
7+license-file: LICENSE
8+author: Alp Mestanogullari
9+maintainer: alpmestan@gmail.com
10+-- copyright:
11+category: Web
12+build-type: Simple
13+-- extra-source-files:
14+cabal-version: >=1.10
15+
16+executable hackage
17+ main-is: hackage.hs
18+ -- other-modules:
19+ -- other-extensions:
20+ build-depends:
21+ aeson >= 0.8
22+ , base >=4.7
23+ , either
24+ , servant
25+ , servant-client
26+ , text
27+ , transformers
28+ hs-source-dirs: hackage
29+ default-language: Haskell2010