packages feed

wai-routes-0.3.3: examples/Example.hs

{-# LANGUAGE OverloadedStrings, TemplateHaskell, QuasiQuotes, TypeFamilies #-}
module Main where

import Network.Wai
import Network.Wai.Middleware.Routes
import Network.Wai.Middleware.Routes.ContentTypes
import Network.Wai.Application.Static
import Network.Wai.Handler.Warp
import Network.HTTP.Types
import qualified Data.Text as T
import Data.Text (Text)
import Data.Aeson
import Data.IORef
import qualified Data.Map as M
import Control.Monad.Trans

-- The database of users
data User = User
  { userId   :: Int
  , userName :: Text
  , userAge  :: Int
  } deriving (Show, Read, Eq)
type DB = [User]

-- JSON instance
instance ToJSON User where
  toJSON x = object [ "uid" .= userId x, "name" .= userName x, "age" .= userAge x ]


-- The Site argument
data MyRoute = MyRoute (IORef DB)

-- Make MyRoute Routable
mkRoute "MyRoute" [parseRoutes|
/             HomeR            GET
/users        UsersR           GET
/user/#Int    UserR:
    /              UserRootR   GET
    /delete        UserDeleteR GET POST
/skip        SkipR             GET
|]


-- Handlers

-- Standard Json output
jsonOut :: ToJSON a => a -> Response
jsonOut = responseLBS status200 jsonHeaders . encode
  where
    jsonHeaders :: ResponseHeaders
    jsonHeaders = [contentType typeJson]

-- Util: Fetch the database
getDB :: HandlerM MyRoute DB
getDB = do
  MyRoute dbref <- master
  liftIO $ readIORef dbref

-- Display the possible actions
getHomeR :: Handler MyRoute
getHomeR = runHandlerM $ return $ jsonOut $ M.fromList (
                 [("description", [["Simple User database Example"]])
                 ,("links"
                  ,[["home",  showRoute HomeR]
                   ,["users", showRoute UsersR]
                   ,["skip",  showRoute SkipR]
                   ]
                  )
                 ] :: [(Text, [[Text]])] )

-- Display all the users
getUsersR :: Handler MyRoute
getUsersR = runHandlerM $ do
  db <- getDB
  let dblinks = map linkify db
  let out = M.fromList (
          [("description", [["Users List"]])
          ,("links", dblinks)] :: [(Text, [[Text]])] )
  return $ jsonOut out
  where
    linkify user = [userName user, showRoute $ UserR (userId user) UserRootR]

-- Display a single user
getUserRootR :: Int -> Handler MyRoute
getUserRootR i = runHandlerM $ do
  db <- getDB
  case ulookup i db of
    Nothing -> return $ jsonOut ("ERROR: User not found" :: Text)
    Just user -> do
      let out = M.fromList (
            [("description", [["User details"]])
            ,("data"
             ,[["Id",   T.pack $ show $ userId user]
              ,["Name", userName user]
              ,["Age",  T.pack $ show $ userAge user]
              ]
             )
            ,("links"
             ,[["details",            showRoute $ UserR (userId user) UserRootR]
              ,["delete (post only)", showRoute $ UserR (userId user) UserDeleteR]
              ]
             )
            ] :: [(Text, [[Text]])] )
      return $ jsonOut out
  where
    ulookup _ [] = Nothing
    ulookup ui (u:us) = if userId u == ui then Just u else ulookup ui us

-- Delete a user: GET
getUserDeleteR :: Int -> Handler MyRoute
getUserDeleteR _ = runHandlerM $ return $ jsonOut err
  where err = ["DELETE","please use POST"]::[Text]

-- Delete a user: POST
postUserDeleteR :: Int -> Handler MyRoute
postUserDeleteR _ = runHandlerM $ return $ jsonOut err
  where err = ["DELETE","not implemented"]::[Text]

-- Demonstrate skipping routes
getSkipR :: Handler MyRoute
getSkipR = runHandlerM next

-- Initial database
initdb :: [User]
initdb =
    [ User 1 "Anon Amos" 23
    , User 2 "Bo Lively" 28
    ]

-- A new middleware to catch all skipped routes
data MySkippedRoute = MySkippedRoute

-- Make MyRoute Routable
mkRoute "MySkippedRoute" [parseRoutes|
/skip         SkippedR          GET
|]

getSkippedR :: Handler MySkippedRoute
getSkippedR = runHandlerM $ return $ jsonOut err
  where err = ["SKIPPED","skipped route"]::[Text]

-- The application that uses our route
-- NOTE: We use the Route Monad to simplify routing
application :: RouteM ()
application = do
  db <- liftIO $ newIORef initdb
  route (MyRoute db)
  route MySkippedRoute
  defaultAction $ staticApp $ defaultFileServerSettings "static"

-- Run the application
main :: IO ()
main = do
  putStrLn "Starting server on port 8080"
  toWaiApp application >>= run 8080