diff options
Diffstat (limited to 'src/query/issues.rs')
-rw-r--r-- | src/query/issues.rs | 118 |
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(()) +} |