aboutsummaryrefslogtreecommitdiff
path: root/src/query/issues.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/query/issues.rs')
-rw-r--r--src/query/issues.rs118
1 files changed, 118 insertions, 0 deletions
diff --git a/src/query/issues.rs b/src/query/issues.rs
new file mode 100644
index 0000000..c829a22
--- /dev/null
+++ b/src/query/issues.rs
@@ -0,0 +1,118 @@
+#![allow(proc_macro_derive_resolution_fallback)]
+
+use graphql_client::{ GraphQLQuery, Response };
+use reqwest::Client;
+
+use chrono::{ Utc, TimeZone };
+use tracing::{ error, info, debug };
+
+use crate::{ Conn, query::* };
+
+type URI = String;
+type HTML = String;
+type DateTime = String;
+
+#[derive(GraphQLQuery)]
+#[graphql(
+ // curl https://api.github.com/graphql -H 'Authorization: bearer ...'
+ schema_path = "graphql/github.json",
+ query_path = "graphql/issues.graphql",
+ response_derives = "Debug"
+)]
+pub struct IssuesQuery;
+
+fn state_to_integer(state: issues_query::IssueState) -> i64 {
+ use issues_query::IssueState::*;
+ match state {
+ OPEN => 0,
+ CLOSED => 1,
+ Other(_) => 2
+ }
+}
+
+pub async fn update(mut conn: &mut Conn, github_api_token: &str, (ref owner, ref name): (String, String)) -> anyhow::Result<()> {
+ let repo = repo_id(conn, owner, name).await?;
+
+ let last_updated = last_updated(conn, repo)
+ .await?
+ .map(|t| Utc.timestamp(t, 0).to_rfc3339());
+ info!("updating repo {}/{} ({}), last update from {:?}", owner, name, repo, last_updated);
+
+ let client = Client::new();
+
+ let mut has_next_page = true;
+ let mut last_cursor = None;
+ while has_next_page {
+ eprint!(".");
+ let query = IssuesQuery::build_query(issues_query::Variables {
+ owner: owner.to_owned(),
+ name: name.to_owned(),
+ since: last_updated.clone(),
+ after: last_cursor.clone()
+ });
+
+ let res = graphql::query(&client, github_api_token, query).await?;
+ let response: Response<issues_query::ResponseData> = res.json().await?;
+
+ for error in response.errors.unwrap_or_default() {
+ error!("{:?}", error);
+ }
+
+ let repository = response.data
+ .expect("Missing response data")
+ .repository
+ .expect("Missing repository");
+
+ has_next_page = repository.issues.page_info.has_next_page;
+ debug!("has_next_page: {}", has_next_page);
+ let issues = repository.issues.edges.unwrap_or_default();
+
+ for issue in issues.into_iter().flatten() {
+ last_cursor = Some(issue.cursor);
+ if let Some(issue) = issue.node {
+ debug!("#{}: {}", issue.number, issue.title);
+ let ts = chrono::DateTime::parse_from_rfc3339(&issue.updated_at)
+ .expect("failed to parse datetime")
+ .timestamp();
+ let author = issue.author
+ .map(|author| author.login)
+ .unwrap_or_else(|| String::from("ghost"));
+
+ sqlx::query(
+ "REPLACE INTO issues (repo, number, state, title, body, user_login, html_url, updated_at)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
+ ).bind(repo).bind(issue.number)
+ .bind(state_to_integer(issue.state)).bind(issue.title).bind(issue.body_html)
+ .bind(author).bind(issue.url).bind(ts)
+ .execute(&mut conn)
+ .await?;
+
+ sqlx::query(
+ "DELETE FROM is_labeled WHERE repo=? AND issue=?"
+ ).bind(repo).bind(issue.number)
+ .execute(&mut conn)
+ .await?;
+
+ let labels = issue.labels
+ .map(|l| l.edges)
+ .unwrap_or_default()
+ .unwrap_or_default()
+ .into_iter()
+ .flatten()
+ .map(|l| l.node)
+ .flatten();
+
+ for label in labels {
+ debug!("label: {}", label.name);
+ sqlx::query(
+ "INSERT INTO is_labeled (repo, issue, label) VALUES (?, ?, (SELECT id FROM labels WHERE name=?))"
+ ).bind(repo).bind(issue.number).bind(label.name)
+ .execute(&mut conn)
+ .await?;
+ }
+ }
+ }
+ }
+
+ Ok(())
+}