{- lat - tool to track alerts from LWN. - Copyright (C) 2010 Magnus Therning - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, version 3 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -} module RepEng where import Control.Monad.Error import Control.Exception as CE import Data.Maybe import Network.XmlRpc.Client import Network.XmlRpc.Internals import System.Directory import System.FilePath import qualified Data.Ini as Ini import qualified Data.Ini.Reader as IniR import qualified AlertDB as ADB import qualified Types as T -- stuff that should be removed at some point, leftovers from use of MissingH fromRight (Right a) = a fromRight _ = error "RepEng.fromRight: Left!" -- assoc of backend name and function (taking path to database and the alert) type Engine = (String, FilePath -> T.Alert -> IO ()) allEngines :: [Engine] allEngines = [ ("jira", _jiraReport)] -- _getConfigFile :: IO Data.Ini.Types.Config _getConfigFile = do home <- getAppUserDataDirectory "lat" confStr <- readFile $ home "lat.conf" let conf = IniR.parse confStr case conf of Left _ -> return Ini.emptyConfig Right c -> return c data JiraConfig = JiraConfig { jiraURL :: String , jiraUser :: String , jiraPwd :: String , jiraProject :: String , jiraType :: String , jiraPriority :: String , jiraComponent :: String } deriving (Show) -- _getWithDefault cfg section option def = either (const def) id (get cfg section option) _getWithDefault cfg section option def = maybe def id (Ini.getOption section option cfg) _getJiraConfig cfg = JiraConfig url user pwd project typ priority component where url = _getWithDefault cfg "jira" "url" "NoUrl" -- http://scale-dev.uk.xensource.com/rpc/xmlrpc user = _getWithDefault cfg "jira" "user" "NoUser" -- magnusth pwd = _getWithDefault cfg "jira" "pwd" "NoPassword" project = _getWithDefault cfg "jira" "project" "NoProject" -- Carbon typ = _getWithDefault cfg "jira" "type" "NoType" -- Task priority = _getWithDefault cfg "jira" "priority" "NoPriority" -- Major component = _getWithDefault cfg "jira" "component" "NoComponent" -- Security _jiraReport :: FilePath -> T.Alert -> IO () _jiraReport dbpath a = do cfg <- liftM _getJiraConfig _getConfigFile bracket (_jiraLogin (jiraURL cfg) (jiraUser cfg) (jiraPwd cfg)) (_jiraLogout (jiraURL cfg)) (\ s -> do --proj <- _getProjectKey (jiraURL cfg) s (jiraProject cfg) --typ <- _getTypeId (jiraURL cfg) s (jiraType cfg) --pri <- _getPriorityId (jiraURL cfg) s (jiraPriority cfg) --comp <- _getComponentId (jiraURL cfg) s proj (jiraComponent cfg) key <- _createIssue cfg a s putStrLn $ "Recorded as " ++ key ADB.runAlertDB dbpath (ADB.setReportedAlert (fromJust $ T.alertId a))) _jiraLogin :: String -- ^ url -> String -- ^ username -> String -- ^ password -> IO String -- ^ session token _jiraLogin url = remote url "jira1.login" _jiraLogout :: String -- ^ url -> String -- ^ session token -> IO Bool _jiraLogout url = remote url "jira1.logout" _jiraGetProjects :: String -> String -> IO [[(String, String)]] _jiraGetProjects url = remote url "jira1.getProjects" _jiraGetTypes :: String -> String -> IO [[(String, String)]] _jiraGetTypes url = remote url "jira1.getIssueTypes" _jiraGetPriorities :: String -> String -> IO [[(String, String)]] _jiraGetPriorities url = remote url "jira1.getPriorities" _jiraGetComponents :: String -- ^ url -> String -- ^ session token -> String -- ^ project name -> IO [[(String, String)]] _jiraGetComponents url = remote url "jira1.getComponents" _jiraCreateIssue :: String -- ^ url -> String -- ^ session token -> Value -- ^ issue description -> IO String _jiraCreateIssue url session desc = do res <- remote url "jira1.createIssue" session desc liftM fromRight $ runErrorT (fromValue $ fromJust $ lookup "key" res) _jiraUpdateIssue :: String -- ^ url -> String -- ^ session token -> String -- ^ issue key (e.g. "CA-12345") -> Value -- ^ issue description -> IO String _jiraUpdateIssue url session issue desc = do res <- remote url "jira1.updateIssue" session issue desc liftM fromRight $ runErrorT (fromValue $ fromJust $ lookup "key" res) _getJiraValue jiraFunc matchKey matchValue key = let filterF p = lookup matchKey p == Just matchValue in do projects <- jiraFunc return . fromJust . lookup key $ head (filter filterF projects) _getProjectKey url session project = _getJiraValue (_jiraGetProjects url session) "name" project "key" _getTypeId url session typ = _getJiraValue (_jiraGetTypes url session) "name" typ "id" _getPriorityId url session priority = _getJiraValue (_jiraGetPriorities url session) "name" priority "id" _getComponentId url session project component = _getJiraValue (_jiraGetComponents url session project) "name" component "id" -- for some strange reason I can't manage to set the components of an issue as -- its created, hence the two calls _createIssue cfg alert session = let summary = "(Upstream security issue) " ++ show (T.prettyTerse alert) description = show $ T.prettyVerbose alert url = jiraURL cfg in do project <- _getProjectKey url session (jiraProject cfg) typ <- _getTypeId url session (jiraType cfg) priority <- _getPriorityId url session (jiraPriority cfg) component <- _getComponentId url session project (jiraComponent cfg) let issueDesc = toValue [("project", project), ("type", typ), ("priority", priority), ("summary", summary), ("assignee", jiraUser cfg), ("description", description)] newIssue <- _jiraCreateIssue url session issueDesc let issueDescUpdate = toValue [("components", [component])] _jiraUpdateIssue url session newIssue issueDescUpdate -- {- ********************************************* -} -- import Data.Time.Calendar -- _sillyDistro = T.distro "name" $ Just "url" -- _sillyAlert = T.alert ["url"] "identity" "package" (ModifiedJulianDay 42) False _sillyDistro -- _sillyDesc = [("project", "CA"), ("type", "3"), ("priority", "3"), ("summary", "MT: another summary"), ("assignee", "magnusth"), ("description", "Description of issue created with lat")] -- _sillyUpdate = [("components", ["10095"])] -- __url = "http://scale-dev.uk.xensource.com/rpc/xmlrpc" -- -- {- -- sess <- _jiraLogin __url "magnusth" " -- ik <- _jiraCreateIssue __url sess (toValue _sillyDesc) -- _jiraUpdateIssue __url sess ik (toValue _sillyUpdate) -- -}