{-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE TemplateHaskell #-} import Control.Lens import Data.Aeson hiding (defaultOptions) import Data.Map.Syntax ((##)) import Data.Monoid import Data.Proxy import Data.Text import GHC.Generics import qualified Heist.Interpreted as I import Snap.Core hiding (GET) import Snap.CORS import Snap.Snaplet import Snap.Snaplet.Auth import Snap.Snaplet.Session import Snap.Snaplet.Session.Backends.CookieSession import Snap.Snaplet.Auth.Backends.JsonFile import Snap.Snaplet.Heist import Snap.Http.Server (defaultConfig) import Servant.API import Servant (serveSnap, Server, serveDirectory) -- * Example -- | A greet message data type newtype Greet = Greet { _msg :: Text } deriving (Generic, Show) instance FromJSON Greet instance ToJSON Greet -- API specification type TestApi m f = -- GET /hello/:name?capital={true, false} returns a Greet as JSON "hello" :> Capture "name" Text :> QueryParam "capital" Bool :> Get '[JSON] Greet :<|> "hellosnap" :> Capture "name" Text :> QueryParam "capital" Bool :> Get '[JSON] Greet -- POST /greet with a Greet as JSON in the request body, -- returns a Greet as JSON :<|> "greet" :> ReqBody '[JSON] Greet :> Post '[JSON] Greet -- DELETE /greet/:greetid :<|> "greet" :> Capture "greetid" Text :> Delete '[JSON] () :<|> "stream" :> StreamGet NetstringFraming JSON (f Greet) :<|> "files" :> Raw :<|> "doraw" :> Raw -- Our application has some of the usual Snaplets data App = App { _heist :: Snaplet (Heist App) , _sess :: Snaplet SessionManager , _auth :: Snaplet (AuthManager App) } makeLenses ''App instance HasHeist App where heistLens = subSnaplet heist type AppHandler = Handler App App testApi :: Proxy (TestApi AppHandler StreamGenerator) testApi = Proxy -- Server-side handlers. -- -- There's one handler per endpoint, which, just like in the type -- that represents the API, are glued together using :<|>. -- -- Each handler runs in the 'AppHandler' monad. server :: Server (TestApi AppHandler StreamGenerator) '[] AppHandler server = helloH :<|> helloH' :<|> postGreetH :<|> deleteGreetH :<|> doStream :<|> serveDirectory "static" :<|> doRaw where helloH :: Text -> Maybe Bool -> AppHandler Greet helloH name Nothing = helloH name (Just False) helloH name (Just False) = return . Greet $ "Hello, " <> name helloH name (Just True) = return . Greet . toUpper $ "Hello, " <> name helloH' :: Text -> Maybe Bool -> (Handler App App) Greet helloH' name _ = with auth $ do cu <- currentUser return (Greet $ "Hi from snaplet, " <> name <> ". Login is " <> maybe "No login" (pack . show) cu) postGreetH :: Greet -> (Handler App App) Greet postGreetH greet = return greet deleteGreetH _ = return () doStream = return $ StreamGenerator $ \sendFirst sendRest -> do sendFirst (Greet "hi") sendRest (Greet "tao") sendRest (Greet "Howareya") doRaw = with auth $ do u <- currentUser let spl = "tName" ## I.textSplice (maybe "NoLogin" (pack . show) u) renderWithSplices "test" spl initApp :: SnapletInit App App initApp = makeSnaplet "myapp" "An example app in servant" Nothing $ do h <- nestSnaplet "" heist $ heistInit "templates" s <- nestSnaplet "sess" sess $ initCookieSessionManager "site_key.txt" "sess" Nothing (Just 3600) a <- nestSnaplet "" auth $ initJsonFileAuthManager defAuthSettings sess "users.json" addRoutes [("api", applyCORS defaultOptions $ serveSnap testApi server) ,("", writeText "Hello")] return $ App h s a -- Run the server. main :: IO () main = serveSnaplet defaultConfig initApp