Browse Source

Merge pull request #1467 from jackh726/graphql

Followup cleanups from graphql addtion
Santiago Pastorino 3 years ago
parent
commit
1e11a3ef47
4 changed files with 214 additions and 313 deletions
  1. 24 95
      src/actions.rs
  2. 53 112
      src/agenda.rs
  3. 89 39
      src/github.rs
  4. 48 67
      src/github/graphql.rs

+ 24 - 95
src/actions.rs

@@ -32,7 +32,7 @@ pub enum QueryKind {
 pub struct QueryMap<'a> {
     pub name: &'a str,
     pub kind: QueryKind,
-    pub query: github::GithubQuery<'a>,
+    pub query: Box<dyn github::IssuesQuery + Send + Sync>,
 }
 
 #[derive(serde::Serialize)]
@@ -83,106 +83,35 @@ impl<'a> Action for Step<'a> {
                 };
 
                 for QueryMap { name, kind, query } in queries {
-                    match query {
-                        github::GithubQuery::REST(query) => match kind {
+                    let issues = query.query(&repository, &gh).await;
+
+                    match issues {
+                        Ok(issues_decorator) => match kind {
                             QueryKind::List => {
-                                let issues_search_result = repository.get_issues(&gh, &query).await;
-
-                                match issues_search_result {
-                                    Ok(issues) => {
-                                        let issues_decorator: Vec<_> = issues
-                                            .iter()
-                                            .map(|issue| IssueDecorator {
-                                                title: issue.title.clone(),
-                                                number: issue.number,
-                                                html_url: issue.html_url.clone(),
-                                                repo_name: repository.name().to_owned(),
-                                                labels: issue
-                                                    .labels
-                                                    .iter()
-                                                    .map(|l| l.name.as_ref())
-                                                    .collect::<Vec<_>>()
-                                                    .join(", "),
-                                                assignees: issue
-                                                    .assignees
-                                                    .iter()
-                                                    .map(|u| u.login.as_ref())
-                                                    .collect::<Vec<_>>()
-                                                    .join(", "),
-                                                updated_at: to_human(issue.updated_at),
-                                            })
-                                            .collect();
-
-                                        results
-                                            .entry(*name)
-                                            .or_insert(Vec::new())
-                                            .extend(issues_decorator);
-                                    }
-                                    Err(err) => {
-                                        eprintln!("ERROR: {}", err);
-                                        err.chain()
-                                            .skip(1)
-                                            .for_each(|cause| eprintln!("because: {}", cause));
-                                        std::process::exit(1);
-                                    }
-                                }
+                                results
+                                    .entry(*name)
+                                    .or_insert(Vec::new())
+                                    .extend(issues_decorator);
                             }
-
                             QueryKind::Count => {
-                                let count = repository.get_issues_count(&gh, &query).await;
-
-                                match count {
-                                    Ok(count) => {
-                                        let result = if let Some(value) = context.get(*name) {
-                                            value.as_u64().unwrap() + count as u64
-                                        } else {
-                                            count as u64
-                                        };
-
-                                        context.insert(*name, &result);
-                                    }
-                                    Err(err) => {
-                                        eprintln!("ERROR: {}", err);
-                                        err.chain()
-                                            .skip(1)
-                                            .for_each(|cause| eprintln!("because: {}", cause));
-                                        std::process::exit(1);
-                                    }
-                                }
+                                let count = issues_decorator.len();
+                                let result = if let Some(value) = context.get(*name) {
+                                    value.as_u64().unwrap() + count as u64
+                                } else {
+                                    count as u64
+                                };
+
+                                context.insert(*name, &result);
                             }
                         },
-                        github::GithubQuery::GraphQL(query) => {
-                            let issues = query.query(&repository, &gh).await;
-
-                            match issues {
-                                Ok(issues_decorator) => match kind {
-                                    QueryKind::List => {
-                                        results
-                                            .entry(*name)
-                                            .or_insert(Vec::new())
-                                            .extend(issues_decorator);
-                                    }
-                                    QueryKind::Count => {
-                                        let count = issues_decorator.len();
-                                        let result = if let Some(value) = context.get(*name) {
-                                            value.as_u64().unwrap() + count as u64
-                                        } else {
-                                            count as u64
-                                        };
-
-                                        context.insert(*name, &result);
-                                    }
-                                },
-                                Err(err) => {
-                                    eprintln!("ERROR: {}", err);
-                                    err.chain()
-                                        .skip(1)
-                                        .for_each(|cause| eprintln!("because: {}", cause));
-                                    std::process::exit(1);
-                                }
-                            }
+                        Err(err) => {
+                            eprintln!("ERROR: {}", err);
+                            err.chain()
+                                .skip(1)
+                                .for_each(|cause| eprintln!("because: {}", cause));
+                            std::process::exit(1);
                         }
-                    };
+                    }
                 }
             }
         }

+ 53 - 112
src/agenda.rs

@@ -1,6 +1,5 @@
 use crate::actions::{Action, Query, QueryKind, QueryMap, Step};
 use crate::github;
-use std::collections::HashMap;
 
 pub fn prioritization<'a>() -> Box<dyn Action> {
     let mut actions = Vec::new();
@@ -11,7 +10,7 @@ pub fn prioritization<'a>() -> Box<dyn Action> {
     queries.push(QueryMap {
         name: "mcp_new_not_seconded",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["major-change", "to-announce"],
             exclude_labels: vec![
@@ -22,14 +21,13 @@ pub fn prioritization<'a>() -> Box<dyn Action> {
                 "t-libs",
                 "t-libs-api",
             ],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "mcp_old_not_seconded",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["major-change"],
             exclude_labels: vec![
@@ -40,46 +38,42 @@ pub fn prioritization<'a>() -> Box<dyn Action> {
                 "t-libs",
                 "t-libs-api",
             ],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "in_pre_fcp",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["proposed-final-comment-period"],
             exclude_labels: vec!["t-libs", "t-libs-api"],
-            ordering: HashMap::new(),
         }),
     });
     queries.push(QueryMap {
         name: "in_fcp",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["final-comment-period"],
             exclude_labels: vec!["t-libs", "t-libs-api"],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "mcp_accepted",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "all")],
             include_labels: vec!["major-change-accepted", "to-announce"],
             exclude_labels: vec!["t-libs", "t-libs-api"],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "fcp_finished",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "all")],
             include_labels: vec![
                 "finished-final-comment-period",
@@ -87,7 +81,6 @@ pub fn prioritization<'a>() -> Box<dyn Action> {
                 "to-announce",
             ],
             exclude_labels: vec!["t-libs", "t-libs-api"],
-            ordering: HashMap::new(),
         }),
     });
 
@@ -101,28 +94,26 @@ pub fn prioritization<'a>() -> Box<dyn Action> {
     queries.push(QueryMap {
         name: "in_pre_fcp",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["proposed-final-comment-period", "T-compiler"],
             exclude_labels: vec!["t-libs", "t-libs-api"],
-            ordering: HashMap::new(),
         }),
     });
     queries.push(QueryMap {
         name: "in_fcp",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["final-comment-period", "T-compiler"],
             exclude_labels: vec!["t-libs", "t-libs-api"],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "fcp_finished",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "all")],
             include_labels: vec![
                 "finished-final-comment-period",
@@ -130,7 +121,6 @@ pub fn prioritization<'a>() -> Box<dyn Action> {
                 "to-announce",
             ],
             exclude_labels: vec!["t-libs", "t-libs-api"],
-            ordering: HashMap::new(),
         }),
     });
 
@@ -144,28 +134,26 @@ pub fn prioritization<'a>() -> Box<dyn Action> {
     queries.push(QueryMap {
         name: "in_pre_fcp",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["proposed-final-comment-period"],
             exclude_labels: vec!["t-libs", "t-libs-api"],
-            ordering: HashMap::new(),
         }),
     });
     queries.push(QueryMap {
         name: "in_fcp",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["final-comment-period"],
             exclude_labels: vec!["t-libs", "t-libs-api"],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "fcp_finished",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "all")],
             include_labels: vec![
                 "finished-final-comment-period",
@@ -173,7 +161,6 @@ pub fn prioritization<'a>() -> Box<dyn Action> {
                 "to-announce",
             ],
             exclude_labels: vec!["t-libs", "t-libs-api"],
-            ordering: HashMap::new(),
         }),
     });
 
@@ -188,22 +175,20 @@ pub fn prioritization<'a>() -> Box<dyn Action> {
     queries.push(QueryMap {
         name: "beta_nominated_t_compiler",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![],
             include_labels: vec!["beta-nominated", "T-compiler"],
             exclude_labels: vec!["beta-accepted"],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "beta_nominated_t_rustdoc",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![],
             include_labels: vec!["beta-nominated", "T-rustdoc"],
             exclude_labels: vec!["beta-accepted"],
-            ordering: HashMap::new(),
         }),
     });
 
@@ -211,22 +196,20 @@ pub fn prioritization<'a>() -> Box<dyn Action> {
     queries.push(QueryMap {
         name: "stable_nominated_t_compiler",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![],
             include_labels: vec!["stable-nominated", "T-compiler"],
             exclude_labels: vec!["stable-accepted"],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "stable_nominated_t_rustdoc",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![],
             include_labels: vec!["stable-nominated", "T-rustdoc"],
             exclude_labels: vec!["stable-accepted"],
-            ordering: HashMap::new(),
         }),
     });
 
@@ -234,11 +217,10 @@ pub fn prioritization<'a>() -> Box<dyn Action> {
     queries.push(QueryMap {
         name: "prs_waiting_on_team_t_compiler",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["S-waiting-on-team", "T-compiler"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
@@ -246,244 +228,217 @@ pub fn prioritization<'a>() -> Box<dyn Action> {
     queries.push(QueryMap {
         name: "issues_of_note_p_critical",
         kind: QueryKind::Count,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["T-compiler", "P-critical"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "issues_of_note_unassigned_p_critical",
         kind: QueryKind::Count,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open"), ("no", "assignee")],
             include_labels: vec!["T-compiler", "P-critical"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "issues_of_note_p_high",
         kind: QueryKind::Count,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["T-compiler", "P-high"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "issues_of_note_unassigned_p_high",
         kind: QueryKind::Count,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open"), ("no", "assignee")],
             include_labels: vec!["T-compiler", "P-high"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "issues_of_note_regression_from_stable_to_beta_p_critical",
         kind: QueryKind::Count,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["regression-from-stable-to-beta", "P-critical"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "issues_of_note_regression_from_stable_to_beta_p_high",
         kind: QueryKind::Count,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["regression-from-stable-to-beta", "P-high"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "issues_of_note_regression_from_stable_to_beta_p_medium",
         kind: QueryKind::Count,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["regression-from-stable-to-beta", "P-medium"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "issues_of_note_regression_from_stable_to_beta_p_low",
         kind: QueryKind::Count,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["regression-from-stable-to-beta", "P-low"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "issues_of_note_regression_from_stable_to_nightly_p_critical",
         kind: QueryKind::Count,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["regression-from-stable-to-nightly", "P-critical"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "issues_of_note_regression_from_stable_to_nightly_p_high",
         kind: QueryKind::Count,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["regression-from-stable-to-nightly", "P-high"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "issues_of_note_regression_from_stable_to_nightly_p_medium",
         kind: QueryKind::Count,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["regression-from-stable-to-nightly", "P-medium"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "issues_of_note_regression_from_stable_to_nightly_p_low",
         kind: QueryKind::Count,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["regression-from-stable-to-nightly", "P-low"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "issues_of_note_regression_from_stable_to_stable_p_critical",
         kind: QueryKind::Count,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["regression-from-stable-to-stable", "P-critical"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "issues_of_note_regression_from_stable_to_stable_p_high",
         kind: QueryKind::Count,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["regression-from-stable-to-stable", "P-high"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "issues_of_note_regression_from_stable_to_stable_p_medium",
         kind: QueryKind::Count,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["regression-from-stable-to-stable", "P-medium"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "issues_of_note_regression_from_stable_to_stable_p_low",
         kind: QueryKind::Count,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["regression-from-stable-to-stable", "P-low"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "p_critical_t_compiler",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["T-compiler", "P-critical"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "p_critical_t_rustdoc",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["T-rustdoc", "P-critical"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "beta_regressions_p_high",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["regression-from-stable-to-beta", "P-high"],
             exclude_labels: vec!["T-infra", "T-libs", "T-release", "T-rustdoc", "T-core"],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "nightly_regressions_unassigned_p_high",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open"), ("no", "assignee")],
             include_labels: vec!["regression-from-stable-to-nightly", "P-high"],
             exclude_labels: vec!["T-infra", "T-libs", "T-release", "T-rustdoc", "T-core"],
-            ordering: HashMap::new(),
         }),
     });
 
     queries.push(QueryMap {
         name: "nominated_t_compiler",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["I-nominated", "T-compiler"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
-    let mut ordering = HashMap::new();
-    ordering.insert("sort", "updated");
-    ordering.insert("per_page", "5");
-
     queries.push(QueryMap {
         name: "top_unreviewed_prs",
         kind: QueryKind::List,
-        query: github::GithubQuery::GraphQL(Box::new(
-            github::graphql::LeastRecentlyReviewedPullRequests,
-        )),
+        query: Box::new(github::graphql::LeastRecentlyReviewedPullRequests),
     });
 
     actions.push(Query {
@@ -499,11 +454,10 @@ pub fn prioritization<'a>() -> Box<dyn Action> {
     queries.push(QueryMap {
         name: "nominated_rfcs_t_compiler",
         kind: QueryKind::List,
-        query: github::GithubQuery::REST(github::Query {
+        query: Box::new(github::Query {
             filters: vec![("state", "open")],
             include_labels: vec!["T-compiler", "I-nominated"],
             exclude_labels: vec![],
-            ordering: HashMap::new(),
         }),
     });
 
@@ -528,31 +482,28 @@ pub fn lang<'a>() -> Box<dyn Action> {
                     QueryMap {
                         name: "pending_project_proposals",
                         kind: QueryKind::List,
-                        query: github::GithubQuery::REST(github::Query {
+                        query: Box::new(github::Query {
                             filters: vec![("state", "open"), ("is", "issue")],
                             include_labels: vec!["major-change"],
                             exclude_labels: vec!["charter-needed"],
-                            ordering: HashMap::new(),
                         }),
                     },
                     QueryMap {
                         name: "pending_lang_team_prs",
                         kind: QueryKind::List,
-                        query: github::GithubQuery::REST(github::Query {
+                        query: Box::new(github::Query {
                             filters: vec![("state", "open"), ("is", "pull-request")],
                             include_labels: vec![],
                             exclude_labels: vec![],
-                            ordering: HashMap::new(),
                         }),
                     },
                     QueryMap {
                         name: "scheduled_meetings",
                         kind: QueryKind::List,
-                        query: github::GithubQuery::REST(github::Query {
+                        query: Box::new(github::Query {
                             filters: vec![("state", "open"), ("is", "issue")],
                             include_labels: vec!["meeting-proposal", "meeting-scheduled"],
                             exclude_labels: vec![],
-                            ordering: HashMap::new(),
                         }),
                     },
                 ],
@@ -562,7 +513,7 @@ pub fn lang<'a>() -> Box<dyn Action> {
                 queries: vec![QueryMap {
                     name: "rfcs_waiting_to_be_merged",
                     kind: QueryKind::List,
-                    query: github::GithubQuery::REST(github::Query {
+                    query: Box::new(github::Query {
                         filters: vec![("state", "open"), ("is", "pr")],
                         include_labels: vec![
                             "disposition-merge",
@@ -570,7 +521,6 @@ pub fn lang<'a>() -> Box<dyn Action> {
                             "T-lang",
                         ],
                         exclude_labels: vec![],
-                        ordering: HashMap::new(),
                     }),
                 }],
             },
@@ -585,51 +535,46 @@ pub fn lang<'a>() -> Box<dyn Action> {
                     QueryMap {
                         name: "p_critical",
                         kind: QueryKind::List,
-                        query: github::GithubQuery::REST(github::Query {
+                        query: Box::new(github::Query {
                             filters: vec![("state", "open")],
                             include_labels: vec!["T-lang", "P-critical"],
                             exclude_labels: vec![],
-                            ordering: HashMap::new(),
                         }),
                     },
                     QueryMap {
                         name: "nominated",
                         kind: QueryKind::List,
-                        query: github::GithubQuery::REST(github::Query {
+                        query: Box::new(github::Query {
                             filters: vec![("state", "open")],
                             include_labels: vec!["T-lang", "I-nominated"],
                             exclude_labels: vec![],
-                            ordering: HashMap::new(),
                         }),
                     },
                     QueryMap {
                         name: "proposed_fcp",
                         kind: QueryKind::List,
-                        query: github::GithubQuery::REST(github::Query {
+                        query: Box::new(github::Query {
                             filters: vec![("state", "open")],
                             include_labels: vec!["T-lang", "proposed-final-comment-period"],
                             exclude_labels: vec!["finished-final-comment-period"],
-                            ordering: HashMap::new(),
                         }),
                     },
                     QueryMap {
                         name: "in_fcp",
                         kind: QueryKind::List,
-                        query: github::GithubQuery::REST(github::Query {
+                        query: Box::new(github::Query {
                             filters: vec![("state", "open")],
                             include_labels: vec!["T-lang", "final-comment-period"],
                             exclude_labels: vec!["finished-final-comment-period"],
-                            ordering: HashMap::new(),
                         }),
                     },
                     QueryMap {
                         name: "finished_fcp",
                         kind: QueryKind::List,
-                        query: github::GithubQuery::REST(github::Query {
+                        query: Box::new(github::Query {
                             filters: vec![("state", "open")],
                             include_labels: vec!["T-lang", "finished-final-comment-period"],
                             exclude_labels: vec![],
-                            ordering: HashMap::new(),
                         }),
                     },
                 ],
@@ -648,31 +593,28 @@ pub fn lang_planning<'a>() -> Box<dyn Action> {
                     QueryMap {
                         name: "pending_project_proposals",
                         kind: QueryKind::List,
-                        query: github::GithubQuery::REST(github::Query {
+                        query: Box::new(github::Query {
                             filters: vec![("state", "open"), ("is", "issue")],
                             include_labels: vec!["major-change"],
                             exclude_labels: vec!["charter-needed"],
-                            ordering: HashMap::new(),
                         }),
                     },
                     QueryMap {
                         name: "pending_lang_team_prs",
                         kind: QueryKind::List,
-                        query: github::GithubQuery::REST(github::Query {
+                        query: Box::new(github::Query {
                             filters: vec![("state", "open"), ("is", "pr")],
                             include_labels: vec![],
                             exclude_labels: vec![],
-                            ordering: HashMap::new(),
                         }),
                     },
                     QueryMap {
                         name: "proposed_meetings",
                         kind: QueryKind::List,
-                        query: github::GithubQuery::REST(github::Query {
+                        query: Box::new(github::Query {
                             filters: vec![("state", "open"), ("is", "issue")],
                             include_labels: vec!["meeting-proposal"],
                             exclude_labels: vec!["meeting-scheduled"],
-                            ordering: HashMap::new(),
                         }),
                     },
                 ],
@@ -682,11 +624,10 @@ pub fn lang_planning<'a>() -> Box<dyn Action> {
                 queries: vec![QueryMap {
                     name: "active_initiatives",
                     kind: QueryKind::List,
-                    query: github::GithubQuery::REST(github::Query {
+                    query: Box::new(github::Query {
                         filters: vec![("state", "open"), ("is", "issue")],
                         include_labels: vec!["lang-initiative"],
                         exclude_labels: vec![],
-                        ordering: HashMap::new(),
                     }),
                 }],
             },

+ 89 - 39
src/github.rs

@@ -1,5 +1,6 @@
 use anyhow::Context;
 
+use async_trait::async_trait;
 use chrono::{DateTime, FixedOffset, Utc};
 use futures::stream::{FuturesUnordered, StreamExt};
 use futures::{future::BoxFuture, FutureExt};
@@ -8,7 +9,6 @@ use once_cell::sync::OnceCell;
 use reqwest::header::{AUTHORIZATION, USER_AGENT};
 use reqwest::{Client, Request, RequestBuilder, Response, StatusCode};
 use std::{
-    collections::HashMap,
     fmt,
     time::{Duration, SystemTime},
 };
@@ -762,6 +762,12 @@ pub struct Repository {
     pub full_name: String,
 }
 
+struct Ordering<'a> {
+    pub sort: &'a str,
+    pub direction: &'a str,
+    pub per_page: &'a str,
+}
+
 impl Repository {
     const GITHUB_API_URL: &'static str = "https://api.github.com";
     const GITHUB_GRAPHQL_API_URL: &'static str = "https://api.github.com/graphql";
@@ -783,10 +789,27 @@ impl Repository {
             filters,
             include_labels,
             exclude_labels,
-            ordering,
-            ..
         } = query;
 
+        let mut ordering = Ordering {
+            sort: "created",
+            direction: "asc",
+            per_page: "100",
+        };
+        let filters: Vec<_> = filters
+            .clone()
+            .into_iter()
+            .filter(|(key, val)| {
+                match *key {
+                    "sort" => ordering.sort = val,
+                    "direction" => ordering.direction = val,
+                    "per_page" => ordering.per_page = val,
+                    _ => return true,
+                };
+                false
+            })
+            .collect();
+
         // `is: pull-request` indicates the query to retrieve PRs only
         let is_pr = filters
             .iter()
@@ -804,11 +827,11 @@ impl Repository {
             || is_pr && !include_labels.is_empty();
 
         let url = if use_search_api {
-            self.build_search_issues_url(filters, include_labels, exclude_labels, ordering)
+            self.build_search_issues_url(&filters, include_labels, exclude_labels, ordering)
         } else if is_pr {
-            self.build_pulls_url(filters, include_labels, ordering)
+            self.build_pulls_url(&filters, include_labels, ordering)
         } else {
-            self.build_issues_url(filters, include_labels, ordering)
+            self.build_issues_url(&filters, include_labels, ordering)
         };
 
         let result = client.get(&url);
@@ -826,19 +849,11 @@ impl Repository {
         }
     }
 
-    pub async fn get_issues_count<'a>(
-        &self,
-        client: &GithubClient,
-        query: &Query<'a>,
-    ) -> anyhow::Result<usize> {
-        Ok(self.get_issues(client, query).await?.len())
-    }
-
     fn build_issues_url(
         &self,
         filters: &Vec<(&str, &str)>,
         include_labels: &Vec<&str>,
-        ordering: &HashMap<&str, &str>,
+        ordering: Ordering<'_>,
     ) -> String {
         self.build_endpoint_url("issues", filters, include_labels, ordering)
     }
@@ -847,7 +862,7 @@ impl Repository {
         &self,
         filters: &Vec<(&str, &str)>,
         include_labels: &Vec<&str>,
-        ordering: &HashMap<&str, &str>,
+        ordering: Ordering<'_>,
     ) -> String {
         self.build_endpoint_url("pulls", filters, include_labels, ordering)
     }
@@ -857,7 +872,7 @@ impl Repository {
         endpoint: &str,
         filters: &Vec<(&str, &str)>,
         include_labels: &Vec<&str>,
-        ordering: &HashMap<&str, &str>,
+        ordering: Ordering<'_>,
     ) -> String {
         let filters = filters
             .iter()
@@ -867,18 +882,11 @@ impl Repository {
                 include_labels.join(",")
             )))
             .chain(std::iter::once("filter=all".to_owned()))
-            .chain(std::iter::once(format!(
-                "sort={}",
-                ordering.get("sort").unwrap_or(&"created")
-            )))
-            .chain(std::iter::once(format!(
-                "direction={}",
-                ordering.get("direction").unwrap_or(&"asc")
-            )))
-            .chain(std::iter::once(format!(
-                "per_page={}",
-                ordering.get("per_page").unwrap_or(&"100")
-            )))
+            .chain(std::iter::once(format!("sort={}", ordering.sort,)))
+            .chain(std::iter::once(
+                format!("direction={}", ordering.direction,),
+            ))
+            .chain(std::iter::once(format!("per_page={}", ordering.per_page,)))
             .collect::<Vec<_>>()
             .join("&");
         format!(
@@ -895,7 +903,7 @@ impl Repository {
         filters: &Vec<(&str, &str)>,
         include_labels: &Vec<&str>,
         exclude_labels: &Vec<&str>,
-        ordering: &HashMap<&str, &str>,
+        ordering: Ordering<'_>,
     ) -> String {
         let filters = filters
             .iter()
@@ -918,24 +926,57 @@ impl Repository {
             "{}/search/issues?q={}&sort={}&order={}&per_page={}",
             Repository::GITHUB_API_URL,
             filters,
-            ordering.get("sort").unwrap_or(&"created"),
-            ordering.get("direction").unwrap_or(&"asc"),
-            ordering.get("per_page").unwrap_or(&"100"),
+            ordering.sort,
+            ordering.direction,
+            ordering.per_page,
         )
     }
 }
 
-pub enum GithubQuery<'a> {
-    REST(Query<'a>),
-    GraphQL(Box<dyn graphql::IssuesQuery + Send + Sync>),
-}
-
 pub struct Query<'a> {
     // key/value filter
     pub filters: Vec<(&'a str, &'a str)>,
     pub include_labels: Vec<&'a str>,
     pub exclude_labels: Vec<&'a str>,
-    pub ordering: HashMap<&'a str, &'a str>,
+}
+
+#[async_trait]
+impl<'q> IssuesQuery for Query<'q> {
+    async fn query<'a>(
+        &'a self,
+        repo: &'a Repository,
+        client: &'a GithubClient,
+    ) -> anyhow::Result<Vec<crate::actions::IssueDecorator>> {
+        let issues = repo
+            .get_issues(&client, self)
+            .await
+            .with_context(|| "Unable to get issues.")?;
+
+        let issues_decorator: Vec<_> = issues
+            .iter()
+            .map(|issue| crate::actions::IssueDecorator {
+                title: issue.title.clone(),
+                number: issue.number,
+                html_url: issue.html_url.clone(),
+                repo_name: repo.name().to_owned(),
+                labels: issue
+                    .labels
+                    .iter()
+                    .map(|l| l.name.as_ref())
+                    .collect::<Vec<_>>()
+                    .join(", "),
+                assignees: issue
+                    .assignees
+                    .iter()
+                    .map(|u| u.login.as_ref())
+                    .collect::<Vec<_>>()
+                    .join(", "),
+                updated_at: crate::actions::to_human(issue.updated_at),
+            })
+            .collect();
+
+        Ok(issues_decorator)
+    }
 }
 
 #[derive(Debug, serde::Deserialize)]
@@ -1220,3 +1261,12 @@ pub struct GitUser {
 pub struct Parent {
     pub sha: String,
 }
+
+#[async_trait]
+pub trait IssuesQuery {
+    async fn query<'a>(
+        &'a self,
+        repo: &'a Repository,
+        client: &'a GithubClient,
+    ) -> anyhow::Result<Vec<crate::actions::IssueDecorator>>;
+}

+ 48 - 67
src/github/graphql.rs

@@ -1,6 +1,7 @@
 use anyhow::Context;
 use async_trait::async_trait;
 
+// This schema can be downloaded from https://docs.github.com/en/graphql/overview/public-schema
 #[cynic::schema_for_derives(file = "src/github/github.graphql", module = "schema")]
 mod queries {
     use super::schema;
@@ -53,8 +54,8 @@ mod queries {
         pub assignees: UserConnection,
         #[arguments(first = 100, order_by = IssueCommentOrder { direction: OrderDirection::Desc, field: IssueCommentOrderField::UpdatedAt })]
         pub comments: IssueCommentConnection,
-        #[arguments(last = 5)]
-        pub reviews: Option<PullRequestReviewConnection>,
+        #[arguments(last = 20)]
+        pub latest_reviews: Option<PullRequestReviewConnection>,
     }
 
     #[derive(cynic::QueryFragment, Debug)]
@@ -157,18 +158,9 @@ mod schema {
     cynic::use_schema!("src/github/github.graphql");
 }
 
-#[async_trait]
-pub trait IssuesQuery {
-    async fn query<'a>(
-        &'a self,
-        repo: &'a super::Repository,
-        client: &'a super::GithubClient,
-    ) -> anyhow::Result<Vec<crate::actions::IssueDecorator>>;
-}
-
 pub struct LeastRecentlyReviewedPullRequests;
 #[async_trait]
-impl IssuesQuery for LeastRecentlyReviewedPullRequests {
+impl super::IssuesQuery for LeastRecentlyReviewedPullRequests {
     async fn query<'a>(
         &'a self,
         repo: &'a super::Repository,
@@ -179,7 +171,7 @@ impl IssuesQuery for LeastRecentlyReviewedPullRequests {
         let repository_owner = repo.owner().to_owned();
         let repository_name = repo.name().to_owned();
 
-        let mut prs = vec![];
+        let mut prs: Vec<Option<queries::PullRequest>> = vec![];
 
         let mut args = queries::LeastRecentlyReviewedPullRequestsArguments {
             repository_owner,
@@ -226,21 +218,18 @@ impl IssuesQuery for LeastRecentlyReviewedPullRequests {
                 if pr.is_draft {
                     return None;
                 }
-                let labels = pr
-                    .labels
-                    .map(|labels| {
-                        labels
-                            .nodes
-                            .map(|nodes| {
-                                nodes
-                                    .into_iter()
-                                    .filter_map(|node| node)
-                                    .map(|node| node.name)
-                                    .collect::<Vec<_>>()
-                            })
-                            .unwrap_or_default()
-                    })
-                    .unwrap_or_default();
+                let labels = pr.labels;
+                let labels = (|| -> Option<_> {
+                    let labels = labels?;
+                    let nodes = labels.nodes?;
+                    let labels = nodes
+                        .into_iter()
+                        .filter_map(|node| node)
+                        .map(|node| node.name)
+                        .collect::<Vec<_>>();
+                    Some(labels)
+                })()
+                .unwrap_or_default();
                 if !labels.iter().any(|label| label == "T-compiler") {
                     return None;
                 }
@@ -255,47 +244,39 @@ impl IssuesQuery for LeastRecentlyReviewedPullRequests {
                     .map(|user| user.login)
                     .collect();
 
-                let mut reviews = pr
-                    .reviews
-                    .map(|reviews| {
-                        reviews
-                            .nodes
-                            .map(|nodes| {
-                                nodes
-                                    .into_iter()
-                                    .filter_map(|n| n)
-                                    .map(|review| {
-                                        (
-                                            review
-                                                .author
-                                                .map(|a| a.login)
-                                                .unwrap_or("N/A".to_string()),
-                                            review.created_at,
-                                        )
-                                    })
-                                    .collect::<Vec<_>>()
-                            })
-                            .unwrap_or_default()
-                    })
-                    .unwrap_or_default();
+                let latest_reviews = pr.latest_reviews;
+                let mut reviews = (|| -> Option<_> {
+                    let reviews = latest_reviews?;
+                    let nodes = reviews.nodes?;
+                    let reviews = nodes
+                        .into_iter()
+                        .filter_map(|node| node)
+                        .filter_map(|node| {
+                            let created_at = node.created_at;
+                            node.author.map(|author| (author, created_at))
+                        })
+                        .map(|(author, created_at)| (author.login, created_at))
+                        .collect::<Vec<_>>();
+                    Some(reviews)
+                })()
+                .unwrap_or_default();
                 reviews.sort_by_key(|r| r.1);
 
-                let comments = pr
-                    .comments
-                    .nodes
-                    .map(|nodes| {
-                        nodes
-                            .into_iter()
-                            .filter_map(|n| n)
-                            .map(|comment| {
-                                (
-                                    comment.author.map(|a| a.login).unwrap_or("N/A".to_string()),
-                                    comment.created_at,
-                                )
-                            })
-                            .collect::<Vec<_>>()
-                    })
-                    .unwrap_or_default();
+                let comments = pr.comments;
+                let comments = (|| -> Option<_> {
+                    let nodes = comments.nodes?;
+                    let comments = nodes
+                        .into_iter()
+                        .filter_map(|node| node)
+                        .filter_map(|node| {
+                            let created_at = node.created_at;
+                            node.author.map(|author| (author, created_at))
+                        })
+                        .map(|(author, created_at)| (author.login, created_at))
+                        .collect::<Vec<_>>();
+                    Some(comments)
+                })()
+                .unwrap_or_default();
                 let mut comments: Vec<_> = comments
                     .into_iter()
                     .filter(|comment| assignees.contains(&comment.0))