{-# LANGUAGE OverloadedStrings, ScopedTypeVariables, DeriveGeneric #-} module Webdriver(serveWebdriver) where import Happstack.Lite import Control.Concurrent.MVar import Data.Aeson import Data.Text (Text) import GHC.Generics import Data.ByteString.Lazy (ByteString) import qualified Data.HashMap.Strict as M import Data.UUID as ID import Data.UUID.V4 import Control.Monad.IO.Class (liftIO) import Data.Maybe (fromMaybe, isJust) import qualified Network.URI as URI import qualified Network.URI.Fetch as URI import Capabilities (processCaps) import JSON import qualified Internal as WD import qualified Internal.Load as WD serveWebdriver :: WD.Sessions -> ServerPart Response serveWebdriver sessions = do msum [ dir "status" serveStatus, dir "session" $ postSession sessions, dir "session" $ path $ serveSession sessions, nullDir >> ok (toResponse ("This is a WebDriver endpoint. Please copy the URL to hand to Selenium." :: Text)) ] serveSession :: WD.Sessions -> String -> ServerPart Response serveSession sessions = WD.withSession fail (\uuid session -> msum [ delSession sessions uuid, dir "timeouts" $ getTimeout session, dir "timeouts" $ setTimeout session, dir "url" $ navigateTo session, dir "url" $ getURL session, dir "refresh" $ reloadPage session ]) sessions where fail uuid'| Just _ <- ID.fromString uuid' = errJSON 404 "invalid session ID" $ ( "Session " ++ uuid' ++ " was not found in active sessions") | otherwise = errJSON 404 "invalid session ID" $ ("UUID " ++ uuid' ++ " is not a valid UUID") data Status = Status {ready :: Bool, message :: Text} deriving Generic instance ToJSON Status serveStatus = okJSON $ Status True "Always ready to open new sessions" data NewSession = NewSession {sessionId :: UUID, capabilities :: Object} deriving Generic instance ToJSON NewSession postSession sessions = do method POST nullDir caps' <- getJSON case processCaps caps' of Just caps -> do (uuid, _) <- liftIO $ WD.createSession sessions caps okJSON $ NewSession uuid caps Nothing -> errJSON 500 "session not created" "Invalid capabilities specified!" delSession sessions uuid = do method DELETE nullDir liftIO $ WD.delSession uuid sessions ok $ toResponse () getTimeout session = do method GET nullDir session' <- liftIO $ readMVar session okJSON $ WD.timeouts session' setTimeout session = do method POST nullDir update' <- getJSON session' <- liftIO $ readMVar session case update' of Just (Object update) | Object current <- toJSON $ WD.timeouts session', Success new <- fromJSON $ toJSON $ M.union update current -> do liftIO $ swapMVar session session' {WD.timeouts = new} ok $ toResponse () _ -> errJSON 400 "invalid argument" "Failed to parse JSON input" data NavigateTo = NavigateTo { url :: String } deriving Generic instance FromJSON NavigateTo navigateTo session = do method POST nullDir target' <- getJSON case target' of Just target | Just url' <- URI.parseAbsoluteURI target -> do liftIO $ WD.load session url' ok $ toResponse () Just target -> errJSON 400 "invalid argument" (target ++ " is not an absolute URL") Nothing -> errJSON 400 "invalid argument" "Failed to parse JSON input" getURL session = do method GET nullDir session' <- liftIO $ readMVar session ok $ toResponse $ show $ WD.currentURL session' reloadPage session = do method POST nullDir session' <- liftIO $ readMVar session liftIO $ WD.load session $ WD.currentURL session' ok $ toResponse ()