Skip to content

Instantly share code, notes, and snippets.

@moodmosaic
Created May 25, 2017 03:21
Show Gist options
  • Save moodmosaic/0f9b3813549bb8ce016075b09d3e13a5 to your computer and use it in GitHub Desktop.
Save moodmosaic/0f9b3813549bb8ce016075b09d3e13a5 to your computer and use it in GitHub Desktop.
Servant example from "Write a client library for any web API in 5 minutes" https://haskell-servant.github.io/client-in-5-minutes.html
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -fno-warn-unused-imports #-}
import Control.Applicative
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Trans.Except
import Data.Aeson
import Data.Monoid
import Data.Proxy
import Data.Text (Text)
import GHC.Generics
import Network.HTTP.Client (Manager, defaultManagerSettings,
newManager)
import System.IO.Unsafe (unsafePerformIO)
import Servant.API
import Servant.Client
import qualified Data.Text as T
import qualified Data.Text.IO as T
type HackageAPI =
"users" :> Get '[JSON] [UserSummary]
:<|> "user" :> Capture "username" Username :> Get '[JSON] UserDetailed
:<|> "packages" :> Get '[JSON] [Package]
type Username = Text
data UserSummary = UserSummary
{ summaryUsername :: Username
, summaryUserid :: Int
} deriving (Eq, Show)
instance FromJSON UserSummary where
parseJSON (Object o) =
UserSummary <$> o .: "username"
<*> o .: "userid"
parseJSON _ = mzero
type Group = Text
data UserDetailed = UserDetailed
{ username :: Username
, userid :: Int
, groups :: [Group]
} deriving (Eq, Show, Generic)
instance FromJSON UserDetailed
newtype Package = Package { packageName :: Text }
deriving (Eq, Show, Generic)
instance FromJSON Package
hackageAPI :: Proxy HackageAPI
hackageAPI = Proxy
{-# NOINLINE manager #-}
manager :: Manager
manager = unsafePerformIO $ newManager defaultManagerSettings
getUsers :: ExceptT ServantError IO [UserSummary]
getUser :: Username -> ExceptT ServantError IO UserDetailed
getPackages :: ExceptT ServantError IO [Package]
getUsers :<|> getUser :<|> getPackages =
client hackageAPI (BaseUrl Http "hackage.haskell.org" 80 "") manager
main :: IO ()
main = print =<< uselessNumbers
uselessNumbers :: IO (Either ServantError ())
uselessNumbers = runExceptT $ do
users <- getUsers
liftIO . putStrLn $ show (length users) ++ " users"
user <- liftIO $ do
putStrLn "Enter a valid hackage username"
T.getLine
userDetailed <- getUser user
liftIO . T.putStrLn $ user <> " maintains " <> T.pack (show (length $ groups userDetailed)) <> " packages"
packages <- getPackages
let monadPackages = filter (isMonadPackage . packageName) packages
liftIO . putStrLn $ show (length monadPackages) ++ " monad packages"
where isMonadPackage = T.isInfixOf "monad"
-- Initial servant-example.cabal generated by cabal init. For further
-- documentation, see http://haskell.org/cabal/users-guide/
name: servant-example
version: 0.1.0.0
license: BSD3
license-file: LICENSE
author: Nikos Baxevanis
maintainer: [email protected]
category: Web
build-type: Simple
extra-source-files: ChangeLog.md
cabal-version: >=1.10
executable servant-example
main-is: Main.hs
build-depends:
aeson >= 0.8
, base >=4.7 && < 5
, http-client > 0.4 && < 0.5
, servant == 0.5.*
, servant-client == 0.5.*
, text
, transformers
, transformers-compat
default-language: Haskell2010
resolver: lts-6.4
packages:
- '.'
extra-deps:
- servant-0.5
- servant-client-0.5
@moodmosaic
Copy link
Author

moodmosaic commented May 25, 2017

The above files can be build with stack build and run with stack exec servant-example.


Optional

If you use ide-haskell or similar, you might want to install ghc-mod and stylish-haskell for the sandboxed GHC (7.10.3) installed by lts-6.4:

stack build ghc-mod
stack build stylish-haskell

Then run Atom inside the sandbox and it'll pick up the correct ghc-mod and stylish-haskell for that specific version of GHC

stack exec atom.cmd

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment