{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} import Control.Exception (bracket) import Control.Monad import Data.Maybe import qualified Data.Text as T import Data.Time.Clock (diffUTCTime) import System.Environment (getArgs) import System.IO import Web.Bugzilla.RedHat import Web.Bugzilla.RedHat.Search main :: IO () main = dispatch Nothing Nothing =<< getArgs dispatch :: Maybe UserEmail -> Maybe BugzillaServer -> [String] -> IO () dispatch Nothing s ("--login" : user : as) = dispatch (Just $ T.pack user) s as dispatch l Nothing ("--server" : server : as) = dispatch l (Just $ T.pack server) as dispatch l s ["--assigned-to", user] = withBz l s $ doAssignedTo (T.pack user) dispatch l s ["--assigned-to-brief", user] = withBz l s $ doAssignedToBrief (T.pack user) dispatch l s ["--requests", user] = withBz l s $ doRequests (T.pack user) dispatch l s ["--history", bug, n] = withBz l s $ doHistory (read bug) (read n) dispatch _ _ _ = usage usage :: IO () usage = hPutStrLn stderr "Connection options:" >> hPutStrLn stderr " --server [domain name] - REQUIRED. The Bugzilla server to access." >> hPutStrLn stderr " --login [user email] - The user to log in with." >> hPutStrLn stderr "" >> hPutStrLn stderr "Bugzilla queries:" >> hPutStrLn stderr " --assigned-to [user email] - List bugs assigned to the user." >> hPutStrLn stderr " --assigned-to [user email] - List bugs assigned to the user." >> hPutStrLn stderr " --requests [user email] - List requests for the user." >> hPutStrLn stderr " --history [bug number] [n] - List the most recent 'n' changes to the bug." withBz :: Maybe UserEmail -> Maybe BugzillaServer -> (BugzillaSession -> IO ()) -> IO () withBz mLogin mServer f = do let server = case mServer of Just s -> s Nothing -> error "Please specify a server with '--server'" ctx <- newBugzillaContext server case mLogin of Just login -> do hPutStrLn stderr "Enter password: " password <- T.pack <$> withEcho False getLine mSession <- loginSession ctx login password case mSession of Just session -> do hPutStrLn stderr "Login successful." f session Nothing -> do hPutStrLn stderr "Login failed. Falling back to anonymous session." f $ anonymousSession ctx Nothing -> f $ anonymousSession ctx doAssignedTo :: UserEmail -> BugzillaSession -> IO () doAssignedTo user session = do let search = AssignedToField .==. user bugs <- searchBugs session search mapM_ showBug bugs where showBug Bug {..} = putStrLn $ show bugId ++ ": " ++ show bugSummary ++ " [" ++ T.unpack bugStatus ++ ": " ++ T.unpack bugResolution ++ "] Updated: " ++ show bugLastChangeTime doAssignedToBrief :: UserEmail -> BugzillaSession -> IO () doAssignedToBrief user session = do let search = AssignedToField .==. user bugs <- searchBugs' session search mapM_ print bugs doRequests :: UserEmail -> BugzillaSession -> IO () doRequests user session = do let needinfoSearch = FlagRequesteeField .==. user .&&. FlagsField `contains` "needinfo" needinfoBugs <- searchBugs session needinfoSearch mapM_ showNeedinfo needinfoBugs let reviewSearch = FlagRequesteeField .==. user .&&. (FlagsField `contains` "review" .||. FlagsField `contains` "feedback") reviewBugs <- map bugId <$> searchBugs session reviewSearch forM_ reviewBugs $ \rBugId -> do attachments <- getAttachments session rBugId mapM_ showReview $ filter (any hasReviewFlag . attachmentFlags) attachments mapM_ showFeedback $ filter (any hasFeedbackFlag . attachmentFlags) attachments where showNeedinfo Bug {..} = do let flags = filter hasNeedinfoFlag $ fromMaybe [] bugFlags forM_ flags $ \flag -> putStrLn $ "[NEEDINFO] " ++ show bugId ++ ": " ++ show bugSummary ++ " (" ++ show (flagSetter flag) ++ " " ++ show (flagCreationDate flag) ++ ")" showReview Attachment {..} = putStrLn $ "[REVIEW] " ++ show attachmentBugId ++ ": " ++ show attachmentSummary ++ " (" ++ show attachmentCreator ++ " " ++ show attachmentCreationTime ++ ")" showFeedback Attachment {..} = putStrLn $ "[FEEDBACK] " ++ show attachmentBugId ++ ": " ++ show attachmentSummary ++ " (" ++ show attachmentCreator ++ " " ++ show attachmentCreationTime ++ ")" hasNeedinfoFlag f = flagRequestee f == Just user && flagName f == "needinfo" hasReviewFlag f = flagRequestee f == Just user && flagName f == "review" hasFeedbackFlag f = flagRequestee f == Just user && flagName f == "feedback" doHistory :: BugId -> Int -> BugzillaSession -> IO () doHistory bug count session = do comments <- getComments session bug history <- getHistory session bug recentEventsRev <- takeRecent count (reverse comments) (reverse $ historyEvents history) mapM_ putStrLn (reverse recentEventsRev) where takeRecent :: Int -> [Comment] -> [HistoryEvent] -> IO [String] takeRecent 0 _ _ = return [] takeRecent n (c:cs) (e:es) | commentCreationTime c `diffUTCTime` historyEventTime e >= 0 = (:) <$> showComment c <*> takeRecent (n - 1) cs (e:es) | otherwise = (:) <$> showEvent e <*> takeRecent (n - 1) (c:cs) es takeRecent n cs@(_:_) [] = mapM showComment $ take n cs takeRecent n [] es@(_:_) = mapM showEvent $ take n es takeRecent _ [] [] = return [] -- FIXME: showComment and showEvent will call getUser for the same -- user over and over again. You should never do this in a real application. showComment Comment {..} = do user <- getUser session commentCreator let commentUserRealName = maybe commentCreator userRealName user let commentUserEmail = fromMaybe commentCreator $ userEmail =<< user return $ "(Comment " ++ show commentCount ++ ") " ++ T.unpack commentUserRealName ++ " <" ++ T.unpack commentUserEmail ++ "> " ++ show commentCreationTime ++ "\n" ++ (unlines . map (" " ++) . lines . T.unpack $ commentText) showEvent HistoryEvent {..} = do user <- getUser session historyEventUser let eventUserRealName = maybe historyEventUser userRealName user let eventUserEmail = fromMaybe historyEventUser $ userEmail =<< user return $ "(Event " ++ show historyEventId ++ ") " ++ T.unpack eventUserRealName ++ " <" ++ T.unpack eventUserEmail ++ ">\n" ++ concatMap showChange historyEventChanges showChange (TextFieldChange f (Modification r a aid)) = showChange' f r a aid showChange (ListFieldChange f (Modification r a aid)) = showChange' f r a aid showChange (IntFieldChange f (Modification r a aid)) = showChange' f r a aid showChange (TimeFieldChange f (Modification r a aid)) = showChange' f r a aid showChange (BoolFieldChange f (Modification r a aid)) = showChange' f r a aid showChange' f r a aid = " " ++ showField f ++ ": " ++ showMod r ++ " -> " ++ showMod a ++ showAid aid ++ "\n" showField = T.unpack . fieldName showMod :: Show a => Maybe a -> String showMod (Just v) = show v showMod Nothing = "___" showAid :: Maybe AttachmentId -> String showAid (Just aid) = " (Attachment " ++ show aid ++ ")" showAid Nothing = "" withEcho :: Bool -> IO a -> IO a withEcho echo action = bracket (hGetEcho stdin) (hSetEcho stdin) (const $ hSetEcho stdin echo >> action)