rhbzquery 0.1.1 → 0.2
raw patch · 9 files changed
+147/−70 lines, 9 files
Files
- ChangeLog.md +3/−0
- README.md +4/−2
- TODO +9/−0
- rhbzquery.cabal +4/−1
- src/Fields.hs +30/−32
- src/Help.hs +2/−0
- src/Main.hs +21/−15
- src/ParseArg.hs +61/−8
- test/tests.hs +13/−12
ChangeLog.md view
@@ -1,5 +1,8 @@ # rhbzquery releases +## 0.2 (2020-12-09)+- support most search type operators (regexp, substr, isempty, and negatives)+ ## 0.1.1 (2020-12-04) - --file a bug - --help: do not format FIELDS
README.md view
@@ -18,9 +18,9 @@ `rhbzquery "Package Review" reporter_realname="Your Name"` : open package reviews you reported -`rhbzquery --mine all flag=fedora-review+` : all your open and closed approved reviews+`rhbzquery --mine all flag~fedora-review+` : all your open and closed approved reviews -`rhbzquery summary=bugzilla` : open bugs with summary containing "bugzilla":+`rhbzquery component~bugzilla summary~bugzilla` : open bugs with component and summary containing "bugzilla" `rhbzquery --file f33 xyz` : file a bug against the xyz package in F33 @@ -29,6 +29,8 @@ ## Installation Run `stack install` or `cabal install`.++Binary packages for Fedora are available from <https://copr.fedorainfracloud.org/coprs/petersen/rhbzquery/>. ## Requests and feedback Feature requests, bug reports and contributions are welcome.
+ TODO view
@@ -0,0 +1,9 @@+- copy url to clipboard+- tui bug browser+- help: --fields-matching+- use NOT Closed ?+- cc=me, reporter=me+- config file: for defining "my team" etc+- --help: only show most important fields+ --help-all for all+- --count bugs
rhbzquery.cabal view
@@ -1,5 +1,5 @@ name: rhbzquery-version: 0.1.1+version: 0.2 synopsis: Bugzilla query tool description: A CLI tool for creating bugzilla queries for bugzilla.redhat.com.@@ -14,6 +14,7 @@ build-type: Simple extra-doc-files: ChangeLog.md README.md+ TODO cabal-version: 1.18 tested-with: GHC == 8.4.4, GHC == 8.6.5, GHC == 8.8.4, GHC == 8.10.2 @@ -55,6 +56,8 @@ if impl(ghc >= 8.4) ghc-options: -Wmissing-export-lists -Wpartial-fields+ if impl(ghc >= 8.10)+ ghc-options: -Wunused-packages test-suite test main-is: tests.hs
src/Fields.hs view
@@ -8,7 +8,7 @@ import Data.Version import Numeric.Natural -import ParseArg (ArgType(..), ProductVersion(..))+import ParseArg (ArgType(..), Operator(..), ProductVersion(..), showOp) data BzFields = BzProduct | BzVersion@@ -21,28 +21,27 @@ show BzProduct = "product" show BzVersion = "version" show BzComponent = "component"- show (BzParameter f) = mapFields f+ show (BzParameter f) = f show (BzMeta c n) = c: show n -mapFields :: String -> String-mapFields "itm" = "cf_internal_target_milestone"-mapFields "itr" = "cf_internal_target_release"-mapFields "status" = "bug_status"-mapFields "verified" = "cf_verified"-mapFields s = s--mapComplex :: String -> String-mapComplex "sst" = "agile_team.name"-mapComplex "summary" = "short_desc"-mapComplex "flag" = "flagtypes.name"-mapComplex "flags" = "flagtypes.name"-mapComplex p = p+mapField :: String -> String+mapField "itm" = "cf_internal_target_milestone"+mapField "itr" = "cf_internal_target_release"+mapField "status" = "bug_status"+mapField "verified" = "cf_verified"+mapField "sst" = "agile_team.name"+mapField "summary" = "short_desc"+mapField "flag" = "flagtypes.name"+mapField "flags" = "flagtypes.name"+mapField p = p argToSimpleField :: ArgType -> [(BzFields,String)] argToSimpleField (ArgProdVer prodver) = productVersionQuery prodver-argToSimpleField (ArgParameter p v) =- [(BzParameter (mapComplex p), v)]+argToSimpleField (ArgParameter p Equals v) =+ [(BzParameter (mapField p), v)]+argToSimpleField (ArgParameter p _ v) =+ error $ "Only '" ++ p ++ "=" ++ v ++ "'allowed here" argToSimpleField (ArgOther c) = [(BzComponent,c)] -- FIXME or error for "all"?@@ -53,21 +52,20 @@ case arg of ArgProdVer prodver -> (i,productVersionQuery prodver) ArgStatusAll -> (i,[])- ArgParameter "sst" v ->- (i+1,[(BzMeta 'f' i, mapComplex "sst")- ,(BzMeta 'o' i, "substr")- ,(BzMeta 'v' i, "sst_" ++ v)])- ArgParameter "summary" v ->- (i+1,[(BzMeta 'f' i, mapComplex "summary")- ,(BzMeta 'o' i, "substr")- ,(BzMeta 'v' i, v)])- ArgParameter param v ->- let p = mapComplex param- in if '.' `elem` p- then (i+1,[(BzMeta 'f' i, p)- ,(BzMeta 'o' i, "substr")- ,(BzMeta 'v' i, v)])- else (i,[(BzParameter p, v)])+ ArgParameter p op v ->+ let param = mapField p+ val = if p == "sst"+ then "sst_" ++ v+ else v+ in if '.' `elem` param || op /= Equals+ then (i+1,[(BzMeta 'f' i, param)+ ,(BzMeta 'o' i, showOp op)+ ,(BzMeta 'v' i, val)])+ else (i,[(BzParameter param, val)])+ ArgParameterEmpty p e ->+ let param = mapField p+ in (i+1,[(BzMeta 'f' i, param)+ ,(BzMeta 'o' i, if e then "isempty" else "isnotempty")]) ArgOther c -> (i,[(BzComponent,c)]) productVersionQuery :: ProductVersion -> [(BzFields,String)]
src/Help.hs view
@@ -21,6 +21,8 @@ , P.empty , P.text "PRODUCTVERSION = " <> P.lbrace <> P.align (P.fillCat (P.punctuate P.comma (map P.text ["rawhide", "fedora", "fXY", "epel", "epelX", "rhel8", "rhel7", "rhelX.Z"])) <> P.rbrace) , P.empty+ , P.text "'op' is " <> P.align (P.fillCat (P.punctuate P.comma (map P.text ["'='(equals)", "'!='(notequals)", "'~'(substring)", "'!~'(notsubstring)", "'=~'(regexp)", "'!=~'(notregexp)", "'~a~'(allwordssubstr)", "'!a~'(nowordssubstr)", "'~c~'(casesubstring)", "'~e~'(isempty)", "'!e~'(isnotempty)", "'~w~'(allwords)", "'!w~'(nowords)"])))+ , P.empty , P.text "See https://github.com/juhp/rhbzquery#readme for examples" ]
src/Main.hs view
@@ -1,6 +1,9 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} +#if !MIN_VERSION_simple_cmd_args(0,1,3)+import Control.Applicative ((<|>))+#endif import Control.Monad.Extra import Data.Bifunctor import qualified Data.ByteString.Char8 as B@@ -26,31 +29,34 @@ import Paths_rhbzquery import User +data OtherQuery = CreateBug | APIQuery+ main :: IO () main = simpleCmdArgsWithMods (Just version) (fullDesc <> header "Bugzilla query tool" <> progDescDoc (Just detailedHelp)) $ run <$> switchWith 'n' "dryrun" "Do not open url" <*> switchWith 'm' "mine" "My bugs" <*>- switchWith 'f' "file" "File a bug" <*>+ optional (flagWith' CreateBug 'f' "file" "File a bug" <|>+ flagWith' APIQuery 'w' "api" "Web API query") <*> some (strArg argHelp) where- run :: Bool -> Bool -> Bool -> [String] -> IO ()- run dryrun mine file args = do- user <- if mine && not file+ run :: Bool -> Bool -> Maybe OtherQuery -> [String] -> IO ()+ run dryrun mine mquery args = do+ user <- if mine then do mail <- getBzUser- return [ArgParameter "assigned_to" mail]+ return [ArgParameter "assigned_to" Equals mail] else return [] let argtypes = mapMaybe readBzQueryArg args- url = if file- then- let query = L.nub $ concatMap argToSimpleField argtypes- in "https://" <> B.pack brc <> "/enter_bug.cgi" <> renderQuery True (bzQuery query)- else- let status = [ArgParameter "bug_status" "__open__" | not (hasStatusSet argtypes)]- query = L.nub $ numberMetaFields $ status ++ user ++ argtypes- in "https://" <> B.pack brc <> "/buglist.cgi" <> renderQuery True (bzQuery query)+ url = case mquery of+ Just CreateBug ->+ let query = L.nub $ concatMap argToSimpleField argtypes+ in "https://" <> B.pack brc <> "/enter_bug.cgi" <> renderQuery True (bzQuery query)+ _ ->+ let status = [ArgParameter "bug_status" Equals "__open__" | not (hasStatusSet argtypes)]+ query = L.nub $ numberMetaFields $ status ++ user ++ argtypes+ in "https://" <> B.pack brc <> "/" <> (if isNothing mquery then "buglist.cgi" else "rest/bug")<> renderQuery True (bzQuery query) B.putStrLn url unless dryrun $ do whenJustM (findExecutable "xdg-open") $ \xdgOpen ->@@ -59,8 +65,8 @@ hasStatusSet :: [ArgType] -> Bool hasStatusSet [] = False hasStatusSet (ArgStatusAll:_) = True- hasStatusSet ((ArgParameter "bug_status" _):_) = True- hasStatusSet ((ArgParameter "status" _):_) = True+ hasStatusSet ((ArgParameter "bug_status" _ _):_) = True+ hasStatusSet ((ArgParameter "status" _ _):_) = True hasStatusSet (_:rest) = hasStatusSet rest numberMetaFields :: [ArgType] -> [(BzFields,String)]
src/ParseArg.hs view
@@ -2,6 +2,8 @@ argHelp, readBzQueryArg, ArgType(..),+ Operator(..),+ showOp, ProductVersion(..), statusList )@@ -14,11 +16,36 @@ import Numeric.Natural argHelp :: String-argHelp = "[COMPONENT|STATUS|PRODUCTVERSION|FIELD=VALUE]..."+argHelp = "[COMPONENT|STATUS|PRODUCTVERSION|FIELD=VALUE|FIELDopVALUE]..." +data Operator = Equals | NotEqual+ | Contains | NotContain+ | ContainsCase+ | Regexp |NotRegexp+ | ContainsAll | NotContainAll+ | ContainsWords | NotContainWords+ -- | ContentMatches | ContentNotMatch+ deriving Eq++showOp :: Operator -> String+showOp Equals = "equals"+showOp NotEqual = "notequals"+showOp Contains = "substring"+showOp NotContain = "notsubstring"+showOp ContainsCase = "casesubstring"+showOp Regexp = "regexp"+showOp NotRegexp = "notregexp"+showOp ContainsAll = "allwordssubstr"+showOp NotContainAll = "nowordssubstr"+showOp ContainsWords = "allwords"+showOp NotContainWords = "nowords"+--showOp IsEmpty = "isempty"+--showOp IsNotEmpty = "isnotempty"+ data ArgType = ArgProdVer ProductVersion | ArgStatusAll- | ArgParameter String String+ | ArgParameter String Operator String+ | ArgParameterEmpty String Bool | ArgOther String readBzQueryArg :: String -> Maybe ArgType@@ -51,12 +78,38 @@ if caps == "ALL" then Just ArgStatusAll else- ArgParameter "bug_status" <$> find (== upper s) statusList+ ArgParameter "bug_status" Equals <$> find (== upper s) statusList parseParam :: String -> Maybe ArgType parseParam ps =- case splitOn "=" ps of- [a,b] -> if null a || null b- then error $ "bad parameter: " ++ ps- else Just (ArgParameter a b)- _ -> Nothing+ parseParamWith "!=~" NotRegexp <|>+ parseParamWith "=~" Regexp <|>+ parseParamWith "!=" NotEqual <|>+ parseParamWith "=" Equals <|>+ parseParamWith "!~" NotContain <|>+ parseParamWith "~a~" ContainsAll <|>+ parseParamWith "!a~" NotContainAll <|>+ parseParamWith "~c~" ContainsCase <|>+ parseParamEmpty "~e~" True <|>+ parseParamEmpty "!e~" False <|>+ parseParamWith "~w~" ContainsWords <|>+ parseParamWith "!w~" NotContainWords <|>+-- parseParamWith "~m~" ContentMatches <|>+-- parseParamWith "!m~" ContentNotMatch <|>+ parseParamWith "~" Contains+ where+ parseParamWith :: String -> Operator -> Maybe ArgType+ parseParamWith op oper =+ case splitOn op ps of+ [a,b] -> if null a || null b+ then error $ "bad parameter: " ++ ps+ else Just (ArgParameter a oper b)+ _ -> Nothing++ parseParamEmpty :: String -> Bool -> Maybe ArgType+ parseParamEmpty op empty =+ case stripSuffix op ps of+ Just a -> if null a+ then error $ "bad parameter: " ++ ps+ else Just (ArgParameterEmpty a empty)+ _ -> Nothing
test/tests.hs view
@@ -4,8 +4,7 @@ rhbzquery :: ([String], String) -> IO () rhbzquery (args,expect) = do out <- cmd "rhbzquery" ("-n" : args)- let cgi = if "--file" `elem` args then "enter_bug" else "buglist"- req = removePrefix ("https://bugzilla.redhat.com/" ++ cgi ++ ".cgi?") out+ let req = removePrefix "https://bugzilla.redhat.com/" out unless (req == expect) $ do cmdN "rhbzquery" ("-n" : args) putStrLn $ "returned> " ++ req@@ -15,21 +14,23 @@ tests :: [([String],String)] tests = [(["f33", "pango"],- "bug_status=__open__&product=Fedora&version=33&component=pango")+ "buglist.cgi?bug_status=__open__&product=Fedora&version=33&component=pango") ,(["closed", "rawhide", "xyz"],- "bug_status=CLOSED&product=Fedora&version=rawhide&component=xyz")+ "buglist.cgi?bug_status=CLOSED&product=Fedora&version=rawhide&component=xyz") ,(["rhel8.3", "bash"],- "bug_status=__open__&product=Red%20Hat%20Enterprise%20Linux%208&version=8.3&component=bash")+ "buglist.cgi?bug_status=__open__&product=Red%20Hat%20Enterprise%20Linux%208&version=8.3&component=bash") ,(["rhel8", "bash"],- "bug_status=__open__&product=Red%20Hat%20Enterprise%20Linux%208&component=bash")+ "buglist.cgi?bug_status=__open__&product=Red%20Hat%20Enterprise%20Linux%208&component=bash") ,(["Package Review", "reporter_realname=Your Name"],- "bug_status=__open__&component=Package%20Review&reporter_realname=Your%20Name")- ,(["flag=fedora-review+"],- "bug_status=__open__&f0=flagtypes.name&o0=substr&v0=fedora-review%2B")- ,(["summary=bugzilla"],- "bug_status=__open__&f0=short_desc&o0=substr&v0=bugzilla")+ "buglist.cgi?bug_status=__open__&component=Package%20Review&reporter_realname=Your%20Name")+ ,(["flag~fedora-review+"],+ "buglist.cgi?bug_status=__open__&f0=flagtypes.name&o0=substring&v0=fedora-review%2B")+ ,(["summary~bugzilla"],+ "buglist.cgi?bug_status=__open__&f0=short_desc&o0=substring&v0=bugzilla") ,(["--file", "f33", "bugzilla"],- "product=Fedora&version=33&component=bugzilla")+ "enter_bug.cgi?product=Fedora&version=33&component=bugzilla")+ ,(["--api", "fedora", "component~llvm"],+ "rest/bug?bug_status=__open__&product=Fedora&f0=component&o0=substring&v0=llvm") ] main :: IO ()