Skip to content

Commit 52cd11d

Browse files
committed
Restricted to a subset of volunteers for testing.
1 parent 742b66b commit 52cd11d

File tree

5 files changed

+98
-13
lines changed

5 files changed

+98
-13
lines changed

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,32 @@ gh webhook forward --repo=ehuss/triagebot-test --events=* \
9595

9696
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.
9797

98+
You can test webhooks with `cURL`. For example to test the Zulip hooks (commands sent to the
99+
Triagebot from the Rust lang Zulip), you start the triagebot on localhost:8000 and then simulate a
100+
Zulip hook payload:
101+
``` sh
102+
curl http://localhost:8000/zulip-hook \
103+
-H "Content-Type: application/json" \
104+
-d '{
105+
"data": "<CMD>",
106+
"token": "<ZULIP_TOKEN>",
107+
"message": {
108+
"sender_id": <YOUR_ID>,
109+
"recipient_id": <YOUR_ID>,
110+
"sender_full_name": "Randolph Carter",
111+
"sender_email": "[email protected]",
112+
"type": "stream"
113+
}
114+
}'
115+
```
116+
117+
Where:
118+
- `CMD` is the exact command you would issue @triagebot on Zulip (ex. open a direct chat with the
119+
bot and send "work show")
120+
- `ZULIP_TOKEN`: can be anything. Must correspond to the env var `$ZULIP_TOKEN` on your workstation
121+
- `YOUR_ID`: your GitHub user ID. Must be existing in your local triagebot database (table `users` and as
122+
foreign key also in `review_prefs`)
123+
98124
#### ngrok
99125

100126
The following is an example of using <https://ngrok.com/> to provide webhook forwarding.

src/db.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,4 +344,5 @@ CREATE EXTENSION IF NOT EXISTS intarray;",
344344
"
345345
CREATE UNIQUE INDEX IF NOT EXISTS review_prefs_user_id ON review_prefs(user_id);
346346
",
347+
"ALTER TABLE review_prefs ADD COLUMN max_assigned_prs INT DEFAULT NULL;",
347348
];

src/handlers/pull_requests_assignment_update.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,20 @@ WHERE r.user_id = $1;";
8484
.unwrap();
8585
Ok(row.into())
8686
}
87+
88+
pub async fn set_review_prefs(
89+
db: &DbClient,
90+
user_id: u64,
91+
pref_max_prs: u32,
92+
) -> anyhow::Result<u64, anyhow::Error> {
93+
let q = "
94+
UPDATE review_prefs r
95+
SET max_assigned_prs = $1
96+
ROM users u
97+
WHERE r.user_id=$2 AND u.user_id=r.user_id;";
98+
let res = db
99+
.execute(q, &[&(pref_max_prs as i32), &(user_id as i64)])
100+
.await
101+
.context("Error retrieving review preferences")?;
102+
Ok(res)
103+
}

src/lib.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,17 +131,27 @@ pub struct ReviewPrefs {
131131
pub username: String,
132132
pub user_id: i64,
133133
pub assigned_prs: Vec<i32>,
134+
pub max_assigned_prs: Option<u32>,
134135
}
135136

136-
impl ReviewPrefs {
137-
fn to_string(&self) -> String {
137+
impl fmt::Display for ReviewPrefs {
138+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
138139
let prs = self
139140
.assigned_prs
140141
.iter()
141142
.map(|pr| format!("#{}", pr))
142143
.collect::<Vec<String>>()
143144
.join(", ");
144-
format!("Username: {}\nAssigned PRs: {}", self.username, prs)
145+
let max = if self.max_assigned_prs.is_none() {
146+
"<not set>"
147+
} else {
148+
&format!("{}", self.max_assigned_prs.expect("NaN"))
149+
};
150+
write!(
151+
f,
152+
"Username: {}\nAssigned PRs: {}\nCurrent review capacity: {}",
153+
self.username, prs, max
154+
)
145155
}
146156
}
147157

@@ -152,6 +162,7 @@ impl From<tokio_postgres::row::Row> for ReviewPrefs {
152162
username: row.get("username"),
153163
user_id: row.get("user_id"),
154164
assigned_prs: row.get("assigned_prs"),
165+
max_assigned_prs: row.get("max_assigned_prs"),
155166
}
156167
}
157168
}

src/zulip.rs

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::db::notifications::{self, delete_ping, move_indices, record_ping, Ide
33
use crate::github::{get_id_for_username, GithubClient};
44
use crate::handlers::docs_update::docs_update;
55
use crate::handlers::project_goals::{self, ping_project_goals_owners};
6-
use crate::handlers::pull_requests_assignment_update::get_review_prefs;
6+
use crate::handlers::pull_requests_assignment_update::{get_review_prefs, set_review_prefs};
77
use crate::handlers::Context;
88
use anyhow::{format_err, Context as _};
99
use std::env;
@@ -159,7 +159,7 @@ fn handle_command<'a>(
159159
Some("meta") => add_meta_notification(&ctx, gh_id, words).await
160160
.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:?}")),
161161
Some("work") => query_pr_assignments(&ctx, gh_id, words).await
162-
.map_err(|e| format_err!("Failed to parse `work` command. Synopsis: work <show>: shows your current PRs assignment\n\nError: {e:?}")),
162+
.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:?}")),
163163
_ => {
164164
while let Some(word) = next {
165165
if word == "@**triagebot**" {
@@ -241,25 +241,55 @@ async fn query_pr_assignments(
241241
gh_id: u64,
242242
mut words: impl Iterator<Item = &str>,
243243
) -> anyhow::Result<Option<String>> {
244+
let testers = [
245+
3161395, // jhpratt
246+
5910697, // nadriel
247+
6098822, // apiraino
248+
20113453, // matthewjasper
249+
31162821, // jackh726
250+
39484203, // jieyouxu
251+
43851243, // fee1-dead
252+
74931857, // albertlarsan68
253+
];
254+
255+
if !testers.contains(&gh_id) {
256+
anyhow::bail!("Sorry, this feature is currently restricted to testers.")
257+
}
258+
244259
let subcommand = match words.next() {
245260
Some(subcommand) => subcommand,
246261
None => anyhow::bail!("no subcommand provided"),
247262
};
248263

249264
let db_client = ctx.db.get().await;
250265

251-
let record = match subcommand {
266+
let reply = match subcommand {
252267
"show" => {
253-
let rec = get_review_prefs(&db_client, gh_id).await;
254-
if rec.is_err() {
255-
anyhow::bail!("No preferences set.")
256-
}
257-
rec?
268+
let rec = get_review_prefs(&db_client, gh_id)
269+
.await
270+
.context("Could not query review preferences")?;
271+
rec.to_string()
272+
}
273+
"set" => {
274+
let pref_max_prs = match words.next() {
275+
Some(max_value) => {
276+
if words.next().is_some() {
277+
anyhow::bail!("Too many parameters.");
278+
}
279+
max_value.parse::<u32>().context(
280+
"Wrong parameter format. Must be a positive integer (and fit a u32).",
281+
)?
282+
}
283+
None => anyhow::bail!("Missing parameter."),
284+
};
285+
set_review_prefs(&db_client, gh_id, pref_max_prs)
286+
.await
287+
.context("Error occurred while setting review preferences.")?;
288+
format!("Review capacity set to {}", pref_max_prs)
258289
}
259290
_ => anyhow::bail!("Invalid subcommand."),
260291
};
261-
262-
Ok(Some(record.to_string()))
292+
Ok(Some(reply))
263293
}
264294

265295
// This does two things:

0 commit comments

Comments
 (0)