From 1196b6e1f51510b163b557aa7202cf70c889edad Mon Sep 17 00:00:00 2001 From: apiraino Date: Tue, 27 Feb 2024 16:39:21 +0100 Subject: [PATCH] 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. --- .../pull_requests_assignment_update.rs | 16 ++++++++++ src/lib.rs | 32 +++++++++++++++++++ src/zulip.rs | 25 ++++++++++++++- 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/handlers/pull_requests_assignment_update.rs b/src/handlers/pull_requests_assignment_update.rs index 2e08adfa..41ee7335 100644 --- a/src/handlers/pull_requests_assignment_update.rs +++ b/src/handlers/pull_requests_assignment_update.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use crate::db::notifications::record_username; use crate::github::retrieve_pull_requests; use crate::jobs::Job; +use crate::ReviewPrefs; use anyhow::Context as _; use async_trait::async_trait; use tokio_postgres::Client as DbClient; @@ -70,3 +71,18 @@ WHERE review_prefs.user_id=$1"; .await .context("Insert DB error") } + +/// Get pull request assignments for a team member +pub async fn get_review_prefs(db: &DbClient, user_id: u64) -> anyhow::Result { + let q = " +SELECT username,r.* +FROM review_prefs r +JOIN users on r.user_id=users.user_id +WHERE r.user_id = $1;"; + let row = db + .query_one(q, &[&(user_id as i64)]) + .await + .context("Error retrieving review preferences") + .unwrap(); + Ok(row.into()) +} diff --git a/src/lib.rs b/src/lib.rs index b0d353b1..bf3df35a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ use crate::github::PullRequestDetails; use anyhow::Context; use handlers::HandlerError; use interactions::ErrorComment; +use serde::Serialize; use std::fmt; use tracing as log; @@ -125,6 +126,37 @@ impl From for WebhookError { } } +#[derive(Debug, Serialize)] +pub struct ReviewPrefs { + pub id: uuid::Uuid, + pub username: String, + pub user_id: i64, + pub assigned_prs: Vec, +} + +impl ReviewPrefs { + fn to_string(&self) -> String { + let prs = self + .assigned_prs + .iter() + .map(|pr| format!("#{}", pr)) + .collect::>() + .join(", "); + format!("Username: {}\nAssigned PRs: {}", self.username, prs) + } +} + +impl From for ReviewPrefs { + fn from(row: tokio_postgres::row::Row) -> Self { + Self { + id: row.get("id"), + username: row.get("username"), + user_id: row.get("user_id"), + assigned_prs: row.get("assigned_prs"), + } + } +} + pub fn deserialize_payload(v: &str) -> anyhow::Result { let mut deserializer = serde_json::Deserializer::from_str(&v); let res: Result = serde_path_to_error::deserialize(&mut deserializer); diff --git a/src/zulip.rs b/src/zulip.rs index 66e79cf9..f3546b88 100644 --- a/src/zulip.rs +++ b/src/zulip.rs @@ -2,6 +2,7 @@ use crate::db::notifications::add_metadata; use crate::db::notifications::{self, delete_ping, move_indices, record_ping, Identifier}; use crate::github::{self, GithubClient}; use crate::handlers::docs_update::docs_update; +use crate::handlers::pull_requests_assignment_update::get_review_prefs; use crate::handlers::Context; use anyhow::{format_err, Context as _}; use std::convert::TryInto; @@ -155,7 +156,9 @@ fn handle_command<'a>( Some("move") => move_notification(&ctx, gh_id, words).await .map_err(|e| format_err!("Failed to parse movement, expected `move `: {e:?}.")), Some("meta") => add_meta_notification(&ctx, gh_id, words).await - .map_err(|e| format_err!("Failed to parse movement, expected `move `: {e:?}.")), + .map_err(|e| format_err!("Failed to parse `meta` command. Synopsis: meta : Add to your notification identified by (>0)\n\nError: {e:?}")), + Some("work") => query_pr_assignments(&ctx, gh_id, words).await + .map_err(|e| format_err!("Failed to parse `work` command. Synopsis: work : shows your current PRs assignment\n\nError: {e:?}")), _ => { while let Some(word) = next { if word == "@**triagebot**" { @@ -199,6 +202,26 @@ fn handle_command<'a>( }) } +async fn query_pr_assignments( + ctx: &&Context, + gh_id: u64, + mut words: impl Iterator, +) -> anyhow::Result> { + let subcommand = match words.next() { + Some(subcommand) => subcommand, + None => anyhow::bail!("no subcommand provided"), + }; + + let db_client = ctx.db.get().await; + + let record = match subcommand { + "show" => get_review_prefs(&db_client, gh_id).await?, + _ => anyhow::bail!("Invalid subcommand."), + }; + + Ok(Some(record.to_string())) +} + // This does two things: // * execute the command for the other user // * tell the user executed for that a command was run as them by the user