{-# LANGUAGE DataKinds #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} -- | Jenkins REST API interface module Jenkins.Rest ( -- * Query Jenkins Jenkins , HasConnectInfo(..) , ConnectInfo(..) , defaultConnectInfo , Result(..) , runJenkins -- ** Combinators , get , getS , post , post_ , concurrently , orElse , liftIO , with -- ** Method , module Jenkins.Rest.Method -- ** Convenience , postXML , traverseC , traverseC_ , reload , restart , forceRestart -- * Optics , jenkinsUrl , jenkinsPort , jenkinsUser , jenkinsApiToken , jenkinsPassword , _Error , _Disconnect , _Result , JenkinsException(..) -- * Reexports , Request , HttpException ) where import Control.Applicative ((<$)) import Control.Lens import Control.Monad.Trans.Resource (ResourceT, runResourceT) import Control.Monad.IO.Class (liftIO) import qualified Data.ByteString as Strict import qualified Data.ByteString.Lazy as Lazy import Data.Conduit (ResumableSource, ($$+-)) import qualified Data.Conduit.List as CL import Data.Monoid (mempty) import Network.HTTP.Conduit (Request, HttpException) import Text.XML (Document, renderLBS, def) import Jenkins.Rest.Internal import Jenkins.Rest.Method import Network.HTTP.Conduit.Lens -- | @GET@ query -- -- While the return type is a lazy bytestring, the entire response -- sits in memory anyway: lazy I/O is not used get :: Method Complete f -> Jenkins Lazy.ByteString get m = fmap Lazy.fromChunks . liftIO . runResourceT =<< liftJ (Get m ($$+- CL.consume)) -- | @GET@ query -- -- If you don't close the source eventually (either explicitly with -- 'Data.Conduit.closeResumableSource' or implicitly by reading from it) -- it will leak a socket. getS :: Method Complete f -> Jenkins (ResumableSource (ResourceT IO) Strict.ByteString) getS m = liftJ (Get m id) -- | @POST@ query (with a payload) post :: (forall f. Method Complete f) -> Lazy.ByteString -> Jenkins () post m body = liftJ (Post m body ()) -- | @POST@ query (without payload) post_ :: (forall f. Method Complete f) -> Jenkins () post_ m = post m mempty -- | Do both queries 'concurrently' concurrently :: Jenkins a -> Jenkins b -> Jenkins (a, b) concurrently ja jb = liftJ (Conc ja jb (,)) -- | @orElse a b@ runs @a@ and only runs @b@ if @a@ has thrown a @JenkinsException@ orElse :: Jenkins a -> Jenkins a -> Jenkins a orElse ja jb = liftJ (Or ja jb) -- | Make local changes to the 'Request' with :: (Request -> Request) -> Jenkins a -> Jenkins a with f j = liftJ $ With f j id -- | @POST@ job's @config.xml@ (or any other xml, really) in @xml-conduit@ format postXML :: (forall f. Method Complete f) -> Document -> Jenkins () postXML m = with (requestHeaders <>~ [("Content-Type", "text/xml")]) . post m . renderLBS def -- | Make a bunch of queries 'concurrently' traverseC :: (a -> Jenkins b) -> [a] -> Jenkins [b] traverseC f = foldr go (return []) where go x xs = do (y, ys) <- concurrently (f x) xs; return (y : ys) -- | Make a bunch of queries 'concurrently' ignoring their results traverseC_ :: (a -> Jenkins b) -> [a] -> Jenkins () traverseC_ f = foldr (\x xs -> () <$ concurrently (f x) xs) (return ()) -- | Reload jenkins configuration from disk -- -- Calls @/reload@ and disconnects reload :: Jenkins a reload = do post_ "reload"; disconnect -- | Restart jenkins safely -- -- Calls @/safeRestart@ and disconnects -- -- @/safeRestart@ allows all running jobs to complete restart :: Jenkins a restart = do post_ "safeRestart"; disconnect -- | Force jenkins to restart without waiting for running jobs to finish -- -- Calls @/restart@ and disconnects forceRestart :: Jenkins a forceRestart = do post_ "restart"; disconnect -- Disconnect from Jenkins. Any following queries won't be executed disconnect :: Jenkins a disconnect = liftJ Dcon {-# INLINE disconnect #-}