----------------------------------------------------------------------------- -- | -- Module : Api.hs -- -- Maintainer : adam.smyczek@gmail.com -- Stability : experimental -- Portability : portable -- -- ReviewBoard API -- -- This package provides the basic ReviewBoard API calls. -- All calls are executed inside the 'RBAction' monad that represents -- a session to the ReviewBoard server. A login to the server is performed -- in the 'RBAction' run method 'runRBAction'. -- -- All actions return the 'RBResponse' object containing the response -- status 'RBStatus' and the original JSon response. Errors are handled in -- two ways: -- -- * Network errors, for example connection errors throw an exception. -- -- * Response errors resulting in for example invalid request parameters are -- handled using the 'rbErrHandler' (by default print to stdin). -- -- The current version provides mainly API calls to handle review requests, -- but this can be easily extended using generalized 'rbPostRequest', 'rbGetRequest' -- or directly the main 'runRequest' functions. -- -- Refer to project page for details: -- -- -- TODOs: -- -- * Add more API calls, of cause! -- -- * Add abstraction for response data, for example 'reviewRequestNew' should -- return the @id@ of the new created request. Currently this have to be -- parsed from the JSon response e.g. using 'rrId' function. -- ----------------------------------------------------------------------------- module ReviewBoard.Api ( -- Modules module ReviewBoard.Core, module ReviewBoard.Browser, -- API calls reviewRequest, reviewRequestNew, reviewRequestDelete, reviewRequestSet, reviewRequestSetField, reviewRequestSaveDraft, -- reviewRequestPublishDraft, not supported yet reviewRequestDiffNew, reviewRequestList, -- Types RRField(..), -- Util functions execRBAction, rbPostRequest, rbGetRequest, rrId -- * Example -- $example1 ) where import ReviewBoard.Core import ReviewBoard.Browser import Network.URI import Network.HTTP import qualified Network.Browser as NB import Data.Maybe import Control.Monad.Error import Control.Monad.State -- | Execute a ReviewBoard action using the provided URL, user -- and password. -- execRBAction :: String -> String -> String -> (RBAction a) -> IO a execRBAction url user password action = do r <- runRBAction url user password action either error return $ fst r -- --------------------------------------------------------------------------- -- Review request API calls -- | Create new review request using the provided repository path and an optional -- submit_as user. The returned response contains the @id@ of the new created -- review request that can be accessed using 'rrId' helper function. -- reviewRequestNew :: String -> Maybe String -> RBAction RBResponse reviewRequestNew p (Just u) = rbPostRequest "reviewrequests/new" [("repository_path", p), ("submit_as", u)] reviewRequestNew p Nothing = rbPostRequest "reviewrequests/new" [("repository_path", p)] -- | Delete review request with request @id@. -- reviewRequestDelete :: Integer -> RBAction RBResponse reviewRequestDelete id = rbPostRequest ("reviewrequests/" ++ show id ++ "/delete") [] -- | Get review request with @id@. -- reviewRequest :: Integer -> RBAction RBResponse reviewRequest id = rbPostRequest ("reviewrequests/" ++ show id ) [] -- | Save review request draft whith @id@. -- reviewRequestSaveDraft :: Integer -> RBAction RBResponse reviewRequestSaveDraft id = rbPostRequest ("reviewrequests/" ++ show id ++ "/draft/save") [] -- Publish review request draft for id -- -- reviewRequestPublishDraft :: Integer -> RBAction RBResponse -- reviewRequestPublishDraft id = rbRequest ("reviewrequests/" ++ show id ++ "/reviews/draft/publish") [("shipit", "False")] -- | Set fields to review request draft with @id@. -- reviewRequestSet :: Integer -> [(RRField, String)] -> RBAction RBResponse reviewRequestSet id fs = rbPostRequest (concat ["reviewrequests/", show id, "/draft/set/"]) (map (\(f, v) -> (show f, v)) fs) -- | Set one field for review request draft with @id@. -- reviewRequestSetField :: Integer -> RRField -> String -> RBAction RBResponse reviewRequestSetField id f v = rbPostRequest (concat ["reviewrequests/", show id, "/draft/set/", show f]) [("value", v)] -- | Add a new diff to a review request with @id@, file path and the basedir parameter. -- reviewRequestDiffNew :: Integer -> String -> String -> RBAction RBResponse reviewRequestDiffNew id bd fp = do uri <- mkURI $ concat ["reviewrequests/", show id, "/diff/new"] let form = Form POST uri [fileUpload "path" fp "text/plain", textField "basedir" bd] runRequest form return -- | List review requests addressed to one user with a status. If user and status are Nothing, -- 'reviewRequestList' lists all request. -- reviewRequestList :: Maybe String -> Maybe String -> RBAction RBResponse reviewRequestList Nothing Nothing = rbGetRequest "reviewrequests/all" [] reviewRequestList (Just u) Nothing = rbGetRequest (concat ["reviewrequests/to/user/", u]) [] reviewRequestList Nothing (Just s) = rbGetRequest "reviewrequests/all" [(show STATUS, s)] reviewRequestList (Just u) (Just s) = rbGetRequest (concat ["reviewrequests/to/user/", u]) [(show STATUS, s)] -- --------------------------------------------------------------------------- -- Types -- | Review request field type. -- data RRField = STATUS | PUBLIC | SUMMARY | DESCRIPTION | TESTING_DONE | BUGS_CLOSED | BRANCH | TARGET_GROUPS | TARGET_PEOPLE deriving (Eq, Enum, Bounded) -- | Request field to name map. -- rrFieldMap :: [(RRField, String)] rrFieldMap = [ (STATUS, "status") , (PUBLIC, "public") , (SUMMARY, "summary") , (DESCRIPTION, "description") , (TESTING_DONE, "testing_done") , (BUGS_CLOSED, "bugs_closed") , (BRANCH, "branch") , (TARGET_GROUPS, "target_groups") , (TARGET_PEOPLE, "target_people") ] instance Show RRField where show = fromJust . flip lookup rrFieldMap -- --------------------------------------------------------------------------- -- API uitl functions -- | Default POST request builder for text field parameters only. -- rbPostRequest :: String -> [(String, String)] -> RBAction RBResponse rbPostRequest = rbRequest POST -- | Default GET request builder for text field parameters only. -- rbGetRequest :: String -> [(String, String)] -> RBAction RBResponse rbGetRequest = rbRequest GET -- | Generalized request runner for text field parameters only. -- rbRequest :: RequestMethod -> String -> [(String, String)] -> RBAction RBResponse rbRequest rm apiUrl attrs = do uri <- mkURI apiUrl let form = Form rm uri (map (\(n, v) -> textField n v) attrs) runRequest form return -- | Get the @id@ from a review request response. -- rrId :: RBResponse -> Maybe Integer rrId rsp = jsInt ["review_request", "id"] (rbRspBody rsp) {- $example1 The following RBAction creates a new review request draft, sets few fields and uploads a diff file: > newRRAction :: RBAction () > newRRAction = do > rsp <- reviewRequestNew "repository" Nothing > let id = fromJust . rrId $ rsp > reviewRequestsSetField id TARGET_PEOPLE "reviewers" > reviewRequestsSetField id DESCRIPTION "Request description" > reviewRequestsDiffNew id "basedir" "diffFileName" > reviewRequestSaveDraft id > liftIO $ print "Done." To run this action, execute: > execRBAction "url" "user" "password" newRRAction -}