From eddd35d4cc24f0752162e2c74e9940ce2c4b4237 Mon Sep 17 00:00:00 2001 From: kitsuyui Date: Sun, 28 Aug 2022 21:41:52 +0900 Subject: [PATCH 1/5] WIP: Enhance mure issues - Allows to set any query. - Make it searchable even in 100+ repositories (Close: https://github.com/kitsuyui/mure/issues/4 ). From fa5ed219eb2efb142db86c3bca556b817bf44dc6 Mon Sep 17 00:00:00 2001 From: kitsuyui Date: Sun, 28 Aug 2022 21:44:38 +0900 Subject: [PATCH 2/5] Make it searchable even in 100+ repositories Currently, it only fetches the first 100 records of search. Loop this until you get all the records using cursor. --- graphql/schema/query.graphql | 14 +++++++++-- src/app/issues/mod.rs | 35 ++++++++++----------------- src/github/api.rs | 47 ++++++++++++++++++++++++++++++++++-- 3 files changed, 70 insertions(+), 26 deletions(-) diff --git a/graphql/schema/query.graphql b/graphql/schema/query.graphql index 60b7ad3..0803339 100644 --- a/graphql/schema/query.graphql +++ b/graphql/schema/query.graphql @@ -1,5 +1,15 @@ -query SearchRepositoryQuery($query: String!) { - repos: search(query: $query, type: REPOSITORY, first: 100) { +query SearchRepositoryQuery($query: String!, $cursor: String, $first: Int!) { + repos: search( + query: $query + type: REPOSITORY + first: $first + after: $cursor + ) { + pageInfo { + startCursor + endCursor + hasNextPage + } edges { node { __typename diff --git a/src/app/issues/mod.rs b/src/app/issues/mod.rs index afeb8b0..d74f2e6 100644 --- a/src/app/issues/mod.rs +++ b/src/app/issues/mod.rs @@ -1,7 +1,5 @@ use crate::github; -use crate::github::api::search_repository_query::{ - ResponseData, SearchRepositoryQueryReposEdgesNode, Variables, -}; +use crate::github::api::search_repository_query::SearchRepositoryQueryReposEdgesNodeOnRepository; use crate::mure_error::Error; pub struct RepositorySummary { @@ -13,24 +11,18 @@ pub struct RepositorySummary { pub url: String, } -pub fn repository_summary(result: ResponseData) -> Result, Error> { +pub fn repository_summary( + repos: Vec, +) -> Result, Error> { let mut results: Vec = Vec::new(); - if let Some(edge) = result.repos.edges { - for edge_ in edge { - let node = edge_.expect("edge is None").node.expect("node is None"); - match node { - SearchRepositoryQueryReposEdgesNode::Repository(repo) => { - results.push(RepositorySummary { - name: repo.name.clone(), - number_of_issues: repo.issues.total_count, - number_of_pull_requests: repo.pull_requests.total_count, - default_branch_name: repo.default_branch_ref.unwrap().name.clone(), - url: repo.url.clone(), - }); - } - _ => unreachable!("unreachable!"), - } - } + for repo in repos { + results.push(RepositorySummary { + name: repo.name.clone(), + number_of_issues: repo.issues.total_count, + number_of_pull_requests: repo.pull_requests.total_count, + default_branch_name: repo.default_branch_ref.unwrap().name.clone(), + url: repo.url.clone(), + }); } Ok(results) } @@ -38,9 +30,8 @@ pub fn repository_summary(result: ResponseData) -> Result pub fn show_issues(user: &str) -> Result<(), Error> { // TODO: more flexible search query let query = format!("user:{} is:public fork:false archived:false", user); - let var = Variables { query }; let token = std::env::var("GH_TOKEN").expect("GH_TOKEN is not set"); - match github::api::search_repository(token, var) { + match github::api::search_all_repositories(token, query) { Err(e) => println!("{}", e), Ok(result) => { match repository_summary(result) { diff --git a/src/github/api.rs b/src/github/api.rs index 4e3c85d..c32a600 100644 --- a/src/github/api.rs +++ b/src/github/api.rs @@ -8,11 +8,54 @@ type URI = String; #[graphql( schema_path = "graphql/schema/schema.docs.graphql", query_path = "graphql/schema/query.graphql", - response_derives = "Debug,PartialEq,Eq" + response_derives = "Debug,PartialEq,Eq,Clone" )] pub struct SearchRepositoryQuery; -pub fn search_repository( +pub fn search_all_repositories( + token: String, + query: String, +) -> Result, Error> { + let mut results = + vec![] as Vec; + + let mut cursor = None as Option; + loop { + let variables = search_repository_query::Variables { + query: query.clone(), + first: 100, + cursor, + }; + let response = search_repositories(token.clone(), variables); + match response { + Ok(response) => { + let page_info = response.repos.page_info; + let edges = response.repos.edges; + if let Some(edge) = edges { + for edge_ in edge { + let node = edge_.expect("edge is None").node.expect("node is None"); + if let search_repository_query::SearchRepositoryQueryReposEdgesNode::Repository(repo) = + node + { + results.push(repo); + } + } + } + if page_info.has_next_page { + cursor = page_info.end_cursor; + } else { + break; + } + } + Err(err) => { + return Err(err); + } + } + } + Ok(results) +} + +fn search_repositories( token: String, variables: search_repository_query::Variables, ) -> Result { From 6545c8712c9b16c91fb6547bf7ba0a0b56bb68fb Mon Sep 17 00:00:00 2001 From: kitsuyui Date: Sun, 28 Aug 2022 22:22:20 +0900 Subject: [PATCH 3/5] Add --query option to mure issues This allows to search repositories according as desired. --- src/app/issues/mod.rs | 14 ++++++++------ src/github/api.rs | 10 +++++----- src/main.rs | 27 +++++++++++++++++++++------ 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/app/issues/mod.rs b/src/app/issues/mod.rs index d74f2e6..4997ab8 100644 --- a/src/app/issues/mod.rs +++ b/src/app/issues/mod.rs @@ -7,7 +7,7 @@ pub struct RepositorySummary { pub name: String, pub number_of_issues: i64, pub number_of_pull_requests: i64, - pub default_branch_name: String, + pub default_branch_name: Option, pub url: String, } @@ -20,18 +20,20 @@ pub fn repository_summary( name: repo.name.clone(), number_of_issues: repo.issues.total_count, number_of_pull_requests: repo.pull_requests.total_count, - default_branch_name: repo.default_branch_ref.unwrap().name.clone(), + default_branch_name: repo + .default_branch_ref + .as_ref() + .map(|default_branch_ref| default_branch_ref.name.clone()), url: repo.url.clone(), }); } Ok(results) } -pub fn show_issues(user: &str) -> Result<(), Error> { +pub fn show_issues(query: &str) -> Result<(), Error> { // TODO: more flexible search query - let query = format!("user:{} is:public fork:false archived:false", user); let token = std::env::var("GH_TOKEN").expect("GH_TOKEN is not set"); - match github::api::search_all_repositories(token, query) { + match github::api::search_all_repositories(&token, query) { Err(e) => println!("{}", e), Ok(result) => { match repository_summary(result) { @@ -43,7 +45,7 @@ pub fn show_issues(user: &str) -> Result<(), Error> { "{}\t{}\t{}\t{}", result.number_of_issues, result.number_of_pull_requests, - result.default_branch_name, + result.default_branch_name.unwrap_or_else(|| "".to_string()), result.url ); } diff --git a/src/github/api.rs b/src/github/api.rs index c32a600..dfb7572 100644 --- a/src/github/api.rs +++ b/src/github/api.rs @@ -13,8 +13,8 @@ type URI = String; pub struct SearchRepositoryQuery; pub fn search_all_repositories( - token: String, - query: String, + token: &str, + query: &str, ) -> Result, Error> { let mut results = vec![] as Vec; @@ -22,11 +22,11 @@ pub fn search_all_repositories( let mut cursor = None as Option; loop { let variables = search_repository_query::Variables { - query: query.clone(), + query: query.to_string(), first: 100, cursor, }; - let response = search_repositories(token.clone(), variables); + let response = search_repositories(token, variables); match response { Ok(response) => { let page_info = response.repos.page_info; @@ -56,7 +56,7 @@ pub fn search_all_repositories( } fn search_repositories( - token: String, + token: &str, variables: search_repository_query::Variables, ) -> Result { let request_body = SearchRepositoryQuery::build_query(variables); diff --git a/src/main.rs b/src/main.rs index 81af9b2..f68561f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,10 +30,19 @@ fn main() { Err(e) => println!("{}", e), } } - Some(("issues", _)) => match app::issues::show_issues(&config.github.username) { - Ok(_) => (), - Err(e) => println!("{}", e), - }, + Some(("issues", matches)) => { + let query = match matches.get_one::("query") { + Some(query) => query.to_string(), + None => format!( + "user:{} is:public fork:false archived:false", + &config.github.username + ), + }; + match app::issues::show_issues(&query) { + Ok(_) => (), + Err(e) => println!("{}", e), + } + } Some(("clone", matches)) => { let repo_url = matches.get_one::("url").unwrap(); match app::clone::clone(&config, repo_url) { @@ -55,7 +64,13 @@ fn parser() -> App<'static> { .required(false) .index(1), ); - let subcommand_issues = App::new("issues").about("show issues"); + let subcommand_issues = App::new("issues").about("show issues").arg( + clap::Arg::new("query") + .short('q') + .long("query") + .takes_value(true) + .help("query to search issues"), + ); let subcommand_clone = App::new("clone").about("clone repository").arg( clap::Arg::with_name("url") .help("repository url") @@ -93,7 +108,7 @@ fn test_parser() { } } let cmd = parser(); - match cmd.get_matches_from_safe(["mure", "issues"]) { + match cmd.get_matches_from_safe(["mure", "issues", "--query", "test"]) { Ok(matches) => { assert_eq!(matches.subcommand_name(), Some("issues")); } From b7da2c3d1d494f708825f37db98406f2f9124c09 Mon Sep 17 00:00:00 2001 From: kitsuyui Date: Sun, 28 Aug 2022 22:26:37 +0900 Subject: [PATCH 4/5] Avoid to reach API limit. --- src/github/api.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/github/api.rs b/src/github/api.rs index dfb7572..22ffa47 100644 --- a/src/github/api.rs +++ b/src/github/api.rs @@ -20,6 +20,7 @@ pub fn search_all_repositories( vec![] as Vec; let mut cursor = None as Option; + let mut count = 0; loop { let variables = search_repository_query::Variables { query: query.to_string(), @@ -51,6 +52,11 @@ pub fn search_all_repositories( return Err(err); } } + count += 1; + if count > 100 { + // Avoid infinite loop to prevent reaching github api limit. + break; + } } Ok(results) } From 8bf742c208cd0c98f2d0f240a81a4e4146dcd87c Mon Sep 17 00:00:00 2001 From: kitsuyui Date: Sun, 28 Aug 2022 22:40:12 +0900 Subject: [PATCH 5/5] Update README about mure issues search query --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index f7b85a6..bf99ee6 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,13 @@ Example: example-mure-issues +### Options + +`--query` option is available for advanced search like `--query 'user:kitsuyui'` +See this page for more about advanced search: https://docs.github.com/en/search-github/searching-on-github/searching-for-repositories + +Default search query is `user:{username} is:public fork:false archived:false` + ### mure refresh `mure refresh` updates the repository.