diff --git a/README.md b/README.md index 436926bf..7d722888 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,32 @@ gh webhook forward --repo=ehuss/triagebot-test --events=* \ Where the value in `--secret` is the secret value you place in `GITHUB_WEBHOOK_SECRET` in the `.env` file, and `--repo` is the repo you want to test against. +You can test webhooks with `cURL`. For example to test the Zulip hooks (commands sent to the +Triagebot from the Rust lang Zulip), you start the triagebot on localhost:8000 and then simulate a +Zulip hook payload: +``` sh +curl http://localhost:8000/zulip-hook \ + -H "Content-Type: application/json" \ + -d '{ + "data": "", + "token": "", + "message": { + "sender_id": , + "recipient_id": , + "sender_full_name": "Randolph Carter", + "sender_email": "r.carter@rust-lang.org", + "type": "stream" + } + }' +``` + +Where: +- `CMD` is the exact command you would issue @triagebot on Zulip (ex. open a direct chat with the + bot and send "work show") +- `ZULIP_TOKEN`: can be anything. Must correspond to the env var `$ZULIP_TOKEN` on your workstation +- `YOUR_ID`: your GitHub user ID. Must be existing in your local triagebot database (table `users` and as + foreign key also in `review_prefs`) + #### ngrok The following is an example of using to provide webhook forwarding. diff --git a/src/db.rs b/src/db.rs index c74606cf..ff3add0d 100644 --- a/src/db.rs +++ b/src/db.rs @@ -344,4 +344,5 @@ CREATE EXTENSION IF NOT EXISTS intarray;", " CREATE UNIQUE INDEX IF NOT EXISTS review_prefs_user_id ON review_prefs(user_id); ", + "ALTER TABLE review_prefs ADD COLUMN max_assigned_prs INT DEFAULT NULL;", ]; diff --git a/src/handlers/pull_requests_assignment_update.rs b/src/handlers/pull_requests_assignment_update.rs index 95edde32..7e7d3baa 100644 --- a/src/handlers/pull_requests_assignment_update.rs +++ b/src/handlers/pull_requests_assignment_update.rs @@ -88,18 +88,16 @@ WHERE r.user_id = $1;"; pub async fn set_review_prefs( db: &DbClient, user_id: u64, - max: u32, -) -> anyhow::Result { + pref_max_prs: u32, +) -> anyhow::Result { let q = " UPDATE review_prefs r -SET max_assigned_prs = $2 +SET max_assigned_prs = $1 FROM users u -WHERE r.user_id=$1 AND u.user_id=r.user_id -RETURNING u.username, r.*"; - let row = db - .query_one(q, &[&(max as i32), &(user_id as i64)]) +WHERE r.user_id=$2 AND u.user_id=r.user_id;"; + let res = db + .execute(q, &[&(pref_max_prs as i32), &(user_id as i64)]) .await - .context("Error retrieving review preferences") - .unwrap(); - Ok(row.into()) + .context("Error setting review preferences")?; + Ok(res) } diff --git a/src/lib.rs b/src/lib.rs index 9a8e8569..bc663ba1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,17 +131,27 @@ pub struct ReviewPrefs { pub username: String, pub user_id: i64, pub assigned_prs: Vec, + pub max_assigned_prs: Option, } -impl ReviewPrefs { - fn to_string(&self) -> String { +impl fmt::Display for ReviewPrefs { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let prs = self .assigned_prs .iter() .map(|pr| format!("#{}", pr)) .collect::>() .join(", "); - format!("Username: {}\nAssigned PRs: {}", self.username, prs) + let max = if self.max_assigned_prs.is_none() { + "" + } else { + &format!("{}", self.max_assigned_prs.expect("NaN")) + }; + write!( + f, + "Username: {}\nAssigned PRs: {}\nCurrent review capacity: {}", + self.username, prs, max + ) } } @@ -152,6 +162,7 @@ impl From for ReviewPrefs { username: row.get("username"), user_id: row.get("user_id"), assigned_prs: row.get("assigned_prs"), + max_assigned_prs: row.get("max_assigned_prs"), } } } diff --git a/src/zulip.rs b/src/zulip.rs index 21da889a..8e041eb1 100644 --- a/src/zulip.rs +++ b/src/zulip.rs @@ -264,34 +264,33 @@ async fn query_pr_assignments( let db_client = ctx.db.get().await; - let record = match subcommand { + let reply = match subcommand { "show" => { - let rec = get_review_prefs(&db_client, gh_id).await; - if rec.is_err() { - anyhow::bail!("No preferences set.") - } - rec? + let rec = get_review_prefs(&db_client, gh_id) + .await + .context("Could not query review preferences")?; + rec.to_string() } "set" => { - let max = match words.next() { + let pref_max_prs = match words.next() { Some(max_value) => { if words.next().is_some() { anyhow::bail!("Too many parameters."); } - max_value - .parse::() - .context("Wrong parameter format. Must be a positive integer.")? + max_value.parse::().context( + "Wrong parameter format. Must be a positive integer (and fit a u32).", + )? } None => anyhow::bail!("Missing parameter."), }; - set_review_prefs(&db_client, gh_id, max) + set_review_prefs(&db_client, gh_id, pref_max_prs) .await - .context("Error occurred while setting review preferences.")? + .context("Could not set review preferences")?; + format!("Review capacity set to {}", pref_max_prs) } _ => anyhow::bail!("Invalid subcommand."), }; - - Ok(Some(record.to_string())) + Ok(Some(reply)) } // This does two things: