Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 4 additions & 33 deletions src/github/issue_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,41 +210,12 @@ impl IssuesQuery for LeastRecentlyReviewedPullRequests {
client: &'a GithubClient,
_team_client: &'a TeamClient,
) -> anyhow::Result<Vec<crate::actions::IssueDecorator>> {
use cynic::QueryBuilder;
use github_graphql::queries;

let repository_owner = repo.owner();
let repository_name = repo.name();

let mut prs: Vec<queries::PullRequest> = vec![];

let mut args = queries::LeastRecentlyReviewedPullRequestsArguments {
repository_owner,
repository_name,
after: None,
};
loop {
let query = queries::LeastRecentlyReviewedPullRequests::build(args.clone());
let req = client.post(&client.graphql_url);
let req = req.json(&query);

let data: cynic::GraphQlResponse<queries::LeastRecentlyReviewedPullRequests> =
client.json(req).await?;
if let Some(errors) = data.errors {
anyhow::bail!("There were graphql errors. {errors:?}");
}
let repository = data
.data
.context("No data returned.")?
.repository
.context("No repository.")?;
prs.extend(repository.pull_requests.nodes);
let page_info = repository.pull_requests.page_info;
if !page_info.has_next_page || page_info.end_cursor.is_none() {
break;
}
args.after = page_info.end_cursor;
}
let prs = client
.least_recently_reviewed_prs(repository_owner, repository_name)
.await?;

let mut prs: Vec<_> = prs
.into_iter()
Expand Down Expand Up @@ -310,7 +281,7 @@ impl IssuesQuery for LeastRecentlyReviewedPullRequests {
updated_at,
pr.number as u64,
pr.title,
pr.url.0,
pr.url,
repository_name,
labels,
author.login,
Expand Down
188 changes: 188 additions & 0 deletions src/github/queries/least_recently_reviewed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
use anyhow::Context as _;
use serde::Deserialize;

use crate::github::GithubClient;

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct QueryResponse {
pub repository: Option<Repository>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Repository {
pub pull_requests: PullRequestConnection,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct PullRequestConnection {
pub page_info: PageInfo,
pub nodes: Vec<PullRequest>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct PageInfo {
pub has_next_page: bool,
pub end_cursor: Option<String>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct PullRequest {
pub number: i32,
pub author: Option<Actor>,
pub created_at: chrono::DateTime<chrono::Utc>,
pub url: String,
pub title: String,
pub is_draft: bool,
pub labels: Option<Connection<Label>>,
pub assignees: Connection<User>,
pub comments: ConnectionWithCount<IssueComment>,
pub latest_reviews: Option<ConnectionWithCount<PullRequestReview>>,
}

#[derive(Deserialize, Debug)]
pub struct Connection<T> {
pub nodes: Vec<T>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct ConnectionWithCount<T> {
pub total_count: i32,
pub nodes: Vec<T>,
}

#[derive(Deserialize, Debug)]
pub struct Actor {
pub login: String,
}

#[derive(Deserialize, Debug)]
pub struct Label {
pub name: String,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct User {
pub login: String,
pub database_id: Option<i32>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct IssueComment {
pub author: Option<Actor>,
pub created_at: chrono::DateTime<chrono::Utc>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct PullRequestReview {
pub author: Option<Actor>,
pub created_at: chrono::DateTime<chrono::Utc>,
}

impl GithubClient {
pub async fn least_recently_reviewed_prs(
&self,
owner: &str,
name: &str,
) -> anyhow::Result<Vec<PullRequest>> {
let mut prs = Vec::new();
let mut after = Option::<String>::None;

loop {
let mut data = self
.graphql_query(
r#"
query LeastRecentlyReviewedPullRequests(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a good case for the local GH database mirror, to avoid these kinds of remote GH queries :)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you mean by local gh db? do you mean that to cache results of this query? This specific query is run when I prepare the weekly triage agenda, I need fresh data everytime

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant https://rust-lang.zulipchat.com/#narrow/channel/224082-triagebot/topic/Integrating.20a.20GitHub.20mirror.20.2B.20dashboard.20in.20triagebot/with/612107852, if we had the GitHub mirror in the triagebot DB, this could just be an SQL query (potentially with a force-refresh before running the data if up-to-date state is required).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the "force-refresh" part is def. needed for my queries :)

$repository_owner: String!,
$repository_name: String!,
$after: String
) {
repository(owner: $repository_owner, name: $repository_name) {
pullRequests(
states: [OPEN],
first: 100,
after: $after,
labels: ["S-waiting-on-review"],
orderBy: { direction: ASC, field: UPDATED_AT }
) {
totalCount
pageInfo {
hasNextPage
endCursor
}
nodes {
number
author {
login
}
createdAt
url
title
isDraft
labels(first: 100) {
nodes {
name
}
}
assignees(first: 100) {
nodes {
login
databaseId
}
}
comments(first: 100, orderBy: { direction: DESC, field: UPDATED_AT }) {
totalCount
nodes {
author {
login
}
createdAt
}
}
latestReviews(last: 20) {
totalCount
nodes {
author {
login
}
createdAt
}
}
}
}
}
}
"#,
serde_json::json!({
"repository_owner": owner.to_string(),
"repository_name": name.to_string(),
"after": after,
}),
)
.await
.context("failed to query the least recently reviewed prs")?;

let response: QueryResponse =
serde_json::from_value(data["data"].take()).context("failed to deserialize")?;

let repository = response.repository.context("No repository.")?;
prs.extend(repository.pull_requests.nodes);

let page_info = repository.pull_requests.page_info;
if !page_info.has_next_page || page_info.end_cursor.is_none() {
break;
}
after = page_info.end_cursor;
}

Ok(prs)
}
}
1 change: 1 addition & 0 deletions src/github/queries/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub(crate) mod issue_with_comments;
pub(crate) mod least_recently_reviewed;
pub(crate) mod user_comments_in_org;
pub(crate) mod user_contributions;
pub(crate) mod user_info;
Expand Down