소스 검색

Use project field for proposed design meetings status

Tyler Mandry 2 년 전
부모
커밋
3ae50ca5bb
3개의 변경된 파일183개의 추가작업 그리고 6개의 파일을 삭제
  1. 89 0
      github-graphql/src/lib.rs
  2. 1 5
      src/agenda.rs
  3. 93 1
      src/github.rs

+ 89 - 0
github-graphql/src/lib.rs

@@ -270,3 +270,92 @@ pub mod docs_update_queries {
 mod schema {
     cynic::use_schema!("src/github.graphql");
 }
+
+#[cynic::schema_for_derives(file = "src/github.graphql", module = "schema")]
+pub mod project_items_by_status {
+    use super::queries::{PageInfo, Uri};
+    use super::schema;
+
+    #[derive(cynic::QueryVariables, Debug, Clone)]
+    pub struct Arguments {
+        pub project_number: i32,
+        pub after: Option<String>,
+    }
+
+    #[derive(cynic::QueryFragment, Debug)]
+    #[cynic(variables = "Arguments")]
+    pub struct Query {
+        #[arguments(login: "rust-lang")]
+        pub organization: Option<Organization>,
+    }
+
+    #[derive(cynic::QueryFragment, Debug)]
+    #[cynic(variables = "Arguments")]
+    pub struct Organization {
+        #[arguments(number: $project_number)]
+        pub project_v2: Option<ProjectV2>,
+    }
+
+    #[derive(cynic::QueryFragment, Debug)]
+    #[cynic(variables = "Arguments")]
+    pub struct ProjectV2 {
+        #[arguments(first: 100, after: $after)]
+        pub items: ProjectV2ItemConnection,
+    }
+
+    #[derive(cynic::QueryFragment, Debug)]
+    pub struct ProjectV2ItemConnection {
+        pub nodes: Option<Vec<Option<ProjectV2Item>>>,
+        pub page_info: PageInfo,
+    }
+
+    #[derive(cynic::QueryFragment, Debug)]
+    pub struct ProjectV2Item {
+        pub content: Option<ProjectV2ItemContent>,
+        #[arguments(name = "Status")]
+        pub field_value_by_name: Option<ProjectV2ItemFieldValue>,
+    }
+
+    impl ProjectV2Item {
+        pub fn status(&self) -> &Option<ProjectV2ItemFieldValue> {
+            &self.field_value_by_name
+        }
+    }
+
+    #[derive(cynic::InlineFragments, Debug)]
+    pub enum ProjectV2ItemContent {
+        Issue(Issue),
+
+        #[cynic(fallback)]
+        Other,
+    }
+
+    #[derive(cynic::InlineFragments, Debug)]
+    pub enum ProjectV2ItemFieldValue {
+        ProjectV2ItemFieldSingleSelectValue(ProjectV2ItemFieldSingleSelectValue),
+
+        #[cynic(fallback)]
+        Other,
+    }
+
+    impl ProjectV2ItemFieldValue {
+        pub fn as_str(&self) -> Option<&str> {
+            Some(match self {
+                Self::ProjectV2ItemFieldSingleSelectValue(val) => val.name.as_deref()?,
+                _ => return None,
+            })
+        }
+    }
+
+    #[derive(cynic::QueryFragment, Debug)]
+    pub struct Issue {
+        pub title: String,
+        pub url: Uri,
+        pub number: i32,
+    }
+
+    #[derive(cynic::QueryFragment, Debug)]
+    pub struct ProjectV2ItemFieldSingleSelectValue {
+        pub name: Option<String>,
+    }
+}

+ 1 - 5
src/agenda.rs

@@ -587,11 +587,7 @@ pub fn lang_planning<'a>() -> Box<dyn Action + Send + Sync> {
                     QueryMap {
                         name: "proposed_meetings",
                         kind: QueryKind::List,
-                        query: Arc::new(github::Query {
-                            filters: vec![("state", "open"), ("is", "issue")],
-                            include_labels: vec!["meeting-proposal"],
-                            exclude_labels: vec!["meeting-scheduled"],
-                        }),
+                        query: Arc::new(github::ProposedDesignMeetings),
                     },
                 ],
             },

+ 93 - 1
src/github.rs

@@ -1,4 +1,4 @@
-use anyhow::Context;
+use anyhow::{anyhow, Context};
 use async_trait::async_trait;
 use chrono::{DateTime, FixedOffset, Utc};
 use futures::{future::BoxFuture, FutureExt};
@@ -2128,6 +2128,98 @@ impl IssuesQuery for LeastRecentlyReviewedPullRequests {
     }
 }
 
+async fn project_items_by_status(
+    client: &GithubClient,
+    status_filter: impl Fn(Option<&str>) -> bool,
+) -> anyhow::Result<Vec<github_graphql::project_items_by_status::ProjectV2ItemContent>> {
+    use cynic::QueryBuilder;
+    use github_graphql::project_items_by_status;
+
+    const DESIGN_MEETING_PROJECT: i32 = 31;
+    let mut args = project_items_by_status::Arguments {
+        project_number: DESIGN_MEETING_PROJECT,
+        after: None,
+    };
+
+    let mut all_items = vec![];
+    loop {
+        let query = project_items_by_status::Query::build(args.clone());
+        let req = client.post(Repository::GITHUB_GRAPHQL_API_URL);
+        let req = req.json(&query);
+
+        let (resp, req_dbg) = client._send_req(req).await?;
+        let data = resp
+            .json::<cynic::GraphQlResponse<project_items_by_status::Query>>()
+            .await
+            .context(req_dbg)?;
+        if let Some(errors) = data.errors {
+            anyhow::bail!("There were graphql errors. {:?}", errors);
+        }
+        let items = data
+            .data
+            .ok_or_else(|| anyhow!("No data returned."))?
+            .organization
+            .ok_or_else(|| anyhow!("Organization not found."))?
+            .project_v2
+            .ok_or_else(|| anyhow!("Project not found."))?
+            .items;
+        let filtered = items
+            .nodes
+            .ok_or_else(|| anyhow!("Malformed response."))?
+            .into_iter()
+            .flatten()
+            .filter(|item| {
+                status_filter(
+                    item.field_value_by_name
+                        .as_ref()
+                        .and_then(|status| status.as_str()),
+                )
+            })
+            .flat_map(|item| item.content);
+        all_items.extend(filtered);
+
+        let page_info = items.page_info;
+        if !page_info.has_next_page || page_info.end_cursor.is_none() {
+            break;
+        }
+        args.after = page_info.end_cursor;
+    }
+
+    Ok(all_items)
+}
+
+pub struct ProposedDesignMeetings;
+#[async_trait]
+impl IssuesQuery for ProposedDesignMeetings {
+    async fn query<'a>(
+        &'a self,
+        _repo: &'a Repository,
+        _include_fcp_details: bool,
+        client: &'a GithubClient,
+    ) -> anyhow::Result<Vec<crate::actions::IssueDecorator>> {
+        use github_graphql::project_items_by_status::ProjectV2ItemContent;
+
+        let items =
+            project_items_by_status(client, |status| status == Some("Needs triage")).await?;
+        Ok(items
+            .into_iter()
+            .flat_map(|item| match item {
+                ProjectV2ItemContent::Issue(issue) => Some(crate::actions::IssueDecorator {
+                    assignees: String::new(),
+                    number: issue.number.try_into().unwrap(),
+                    fcp_details: None,
+                    html_url: issue.url.0,
+                    title: issue.title,
+                    repo_name: String::new(),
+                    labels: String::new(),
+                    updated_at_hts: String::new(),
+                }),
+                _ => None,
+            })
+            .collect())
+    }
+}
+
 #[derive(Debug, serde::Deserialize)]
 pub struct GitReference {
     #[serde(rename = "ref")]