Skip to content

Commit 1196b6e

Browse files
committed
Add Zulip integration to query PRs assignment
Implement a command to query from Zulip the number of assigned PRs. The command returns own Github username and assigned PRs to review. This command cannot be be used to check other peoples' work. Allows testing that the DB queries created in #1773 are working correctly.
1 parent 75dc576 commit 1196b6e

File tree

3 files changed

+72
-1
lines changed

3 files changed

+72
-1
lines changed

src/handlers/pull_requests_assignment_update.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::collections::HashMap;
33
use crate::db::notifications::record_username;
44
use crate::github::retrieve_pull_requests;
55
use crate::jobs::Job;
6+
use crate::ReviewPrefs;
67
use anyhow::Context as _;
78
use async_trait::async_trait;
89
use tokio_postgres::Client as DbClient;
@@ -70,3 +71,18 @@ WHERE review_prefs.user_id=$1";
7071
.await
7172
.context("Insert DB error")
7273
}
74+
75+
/// Get pull request assignments for a team member
76+
pub async fn get_review_prefs(db: &DbClient, user_id: u64) -> anyhow::Result<ReviewPrefs> {
77+
let q = "
78+
SELECT username,r.*
79+
FROM review_prefs r
80+
JOIN users on r.user_id=users.user_id
81+
WHERE r.user_id = $1;";
82+
let row = db
83+
.query_one(q, &[&(user_id as i64)])
84+
.await
85+
.context("Error retrieving review preferences")
86+
.unwrap();
87+
Ok(row.into())
88+
}

src/lib.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::github::PullRequestDetails;
88
use anyhow::Context;
99
use handlers::HandlerError;
1010
use interactions::ErrorComment;
11+
use serde::Serialize;
1112
use std::fmt;
1213
use tracing as log;
1314

@@ -125,6 +126,37 @@ impl From<anyhow::Error> for WebhookError {
125126
}
126127
}
127128

129+
#[derive(Debug, Serialize)]
130+
pub struct ReviewPrefs {
131+
pub id: uuid::Uuid,
132+
pub username: String,
133+
pub user_id: i64,
134+
pub assigned_prs: Vec<i32>,
135+
}
136+
137+
impl ReviewPrefs {
138+
fn to_string(&self) -> String {
139+
let prs = self
140+
.assigned_prs
141+
.iter()
142+
.map(|pr| format!("#{}", pr))
143+
.collect::<Vec<String>>()
144+
.join(", ");
145+
format!("Username: {}\nAssigned PRs: {}", self.username, prs)
146+
}
147+
}
148+
149+
impl From<tokio_postgres::row::Row> for ReviewPrefs {
150+
fn from(row: tokio_postgres::row::Row) -> Self {
151+
Self {
152+
id: row.get("id"),
153+
username: row.get("username"),
154+
user_id: row.get("user_id"),
155+
assigned_prs: row.get("assigned_prs"),
156+
}
157+
}
158+
}
159+
128160
pub fn deserialize_payload<T: serde::de::DeserializeOwned>(v: &str) -> anyhow::Result<T> {
129161
let mut deserializer = serde_json::Deserializer::from_str(&v);
130162
let res: Result<T, _> = serde_path_to_error::deserialize(&mut deserializer);

src/zulip.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::db::notifications::add_metadata;
22
use crate::db::notifications::{self, delete_ping, move_indices, record_ping, Identifier};
33
use crate::github::{self, GithubClient};
44
use crate::handlers::docs_update::docs_update;
5+
use crate::handlers::pull_requests_assignment_update::get_review_prefs;
56
use crate::handlers::Context;
67
use anyhow::{format_err, Context as _};
78
use std::convert::TryInto;
@@ -155,7 +156,9 @@ fn handle_command<'a>(
155156
Some("move") => move_notification(&ctx, gh_id, words).await
156157
.map_err(|e| format_err!("Failed to parse movement, expected `move <from> <to>`: {e:?}.")),
157158
Some("meta") => add_meta_notification(&ctx, gh_id, words).await
158-
.map_err(|e| format_err!("Failed to parse movement, expected `move <idx> <meta...>`: {e:?}.")),
159+
.map_err(|e| format_err!("Failed to parse `meta` command. Synopsis: meta <num> <text>: Add <text> to your notification identified by <num> (>0)\n\nError: {e:?}")),
160+
Some("work") => query_pr_assignments(&ctx, gh_id, words).await
161+
.map_err(|e| format_err!("Failed to parse `work` command. Synopsis: work <show>: shows your current PRs assignment\n\nError: {e:?}")),
159162
_ => {
160163
while let Some(word) = next {
161164
if word == "@**triagebot**" {
@@ -199,6 +202,26 @@ fn handle_command<'a>(
199202
})
200203
}
201204

205+
async fn query_pr_assignments(
206+
ctx: &&Context,
207+
gh_id: u64,
208+
mut words: impl Iterator<Item = &str>,
209+
) -> anyhow::Result<Option<String>> {
210+
let subcommand = match words.next() {
211+
Some(subcommand) => subcommand,
212+
None => anyhow::bail!("no subcommand provided"),
213+
};
214+
215+
let db_client = ctx.db.get().await;
216+
217+
let record = match subcommand {
218+
"show" => get_review_prefs(&db_client, gh_id).await?,
219+
_ => anyhow::bail!("Invalid subcommand."),
220+
};
221+
222+
Ok(Some(record.to_string()))
223+
}
224+
202225
// This does two things:
203226
// * execute the command for the other user
204227
// * tell the user executed for that a command was run as them by the user

0 commit comments

Comments
 (0)