Skip to content

Commit

Permalink
Restricted to a subset of volunteers for testing.
Browse files Browse the repository at this point in the history
  • Loading branch information
apiraino committed Nov 6, 2024
1 parent 742b66b commit c8cb80a
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 13 deletions.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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": "<CMD>",
"token": "<ZULIP_TOKEN>",
"message": {
"sender_id": <YOUR_ID>,
"recipient_id": <YOUR_ID>,
"sender_full_name": "Randolph Carter",
"sender_email": "[email protected]",
"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 <https://ngrok.com/> to provide webhook forwarding.
Expand Down
1 change: 1 addition & 0 deletions src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;",
];
17 changes: 17 additions & 0 deletions src/handlers/pull_requests_assignment_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,20 @@ WHERE r.user_id = $1;";
.unwrap();
Ok(row.into())
}

pub async fn set_review_prefs(
db: &DbClient,
user_id: u64,
pref_max_prs: u32,
) -> anyhow::Result<u64, anyhow::Error> {
let q = "
UPDATE review_prefs r
SET max_assigned_prs = $1
ROM users u
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")?;
Ok(res)
}
17 changes: 14 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,17 +131,27 @@ pub struct ReviewPrefs {
pub username: String,
pub user_id: i64,
pub assigned_prs: Vec<i32>,
pub max_assigned_prs: Option<u32>,
}

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::<Vec<String>>()
.join(", ");
format!("Username: {}\nAssigned PRs: {}", self.username, prs)
let max = if self.max_assigned_prs.is_none() {
"<not set>"
} else {
&format!("{}", self.max_assigned_prs.expect("NaN"))
};
write!(
f,
"Username: {}\nAssigned PRs: {}\nCurrent review capacity: {}",
self.username, prs, max
)
}
}

Expand All @@ -152,6 +162,7 @@ impl From<tokio_postgres::row::Row> 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"),
}
}
}
Expand Down
50 changes: 40 additions & 10 deletions src/zulip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::db::notifications::{self, delete_ping, move_indices, record_ping, Ide
use crate::github::{get_id_for_username, GithubClient};
use crate::handlers::docs_update::docs_update;
use crate::handlers::project_goals::{self, ping_project_goals_owners};
use crate::handlers::pull_requests_assignment_update::get_review_prefs;
use crate::handlers::pull_requests_assignment_update::{get_review_prefs, set_review_prefs};
use crate::handlers::Context;
use anyhow::{format_err, Context as _};
use std::env;
Expand Down Expand Up @@ -159,7 +159,7 @@ fn handle_command<'a>(
Some("meta") => add_meta_notification(&ctx, gh_id, words).await
.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:?}")),
Some("work") => query_pr_assignments(&ctx, gh_id, words).await
.map_err(|e| format_err!("Failed to parse `work` command. Synopsis: work <show>: shows your current PRs assignment\n\nError: {e:?}")),
.map_err(|e| format_err!("Failed to parse `work` command. Synopsis:\nwork <show>: shows your current PRs assignment\nwork set <max>: set your max number of assigned PRs to review\n\nError: {e:?}")),
_ => {
while let Some(word) = next {
if word == "@**triagebot**" {
Expand Down Expand Up @@ -241,25 +241,55 @@ async fn query_pr_assignments(
gh_id: u64,
mut words: impl Iterator<Item = &str>,
) -> anyhow::Result<Option<String>> {
let testers = [
3161395, // jhpratt
5910697, // nadriel
6098822, // apiraino
20113453, // matthewjasper
31162821, // jackh726
39484203, // jieyouxu
43851243, // fee1-dead
74931857, // albertlarsan68
];

if !testers.contains(&gh_id) {
anyhow::bail!("Sorry, this feature is currently restricted to testers.")
}

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 {
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 pref_max_prs = match words.next() {
Some(max_value) => {
if words.next().is_some() {
anyhow::bail!("Too many parameters.");
}
max_value.parse::<u32>().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, pref_max_prs)
.await
.context("Error occurred while setting 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:
Expand Down

0 comments on commit c8cb80a

Please sign in to comment.