@@ -18,22 +18,29 @@ use octocrab::models::IssueState;
18
18
use octocrab:: params:: pulls:: Sort ;
19
19
use octocrab:: params:: { Direction , State } ;
20
20
use octocrab:: Octocrab ;
21
- use std:: collections:: { HashMap , HashSet } ;
21
+ use std:: collections:: HashMap ;
22
22
use tokio:: sync:: RwLockWriteGuard ;
23
23
use tracing as log;
24
24
25
+ #[ derive( Clone , Debug ) ]
26
+ pub struct AssignedPullRequest {
27
+ pub title : String ,
28
+ }
29
+
25
30
/// Maps users to a set of currently assigned open non-draft pull requests.
26
31
/// We store this map in memory, rather than in the DB, because it can get desynced when webhooks
27
32
/// are missed.
28
33
/// It is thus reloaded when triagebot starts and also periodically, so it is not needed to store it
29
34
/// in the DB.
30
35
#[ derive( Debug , Default ) ]
31
36
pub struct ReviewerWorkqueue {
32
- reviewers : HashMap < UserId , HashSet < PullRequestNumber > > ,
37
+ reviewers : HashMap < UserId , HashMap < PullRequestNumber , AssignedPullRequest > > ,
33
38
}
34
39
35
40
impl ReviewerWorkqueue {
36
- pub fn new ( reviewers : HashMap < UserId , HashSet < PullRequestNumber > > ) -> Self {
41
+ pub fn new (
42
+ reviewers : HashMap < UserId , HashMap < PullRequestNumber , AssignedPullRequest > > ,
43
+ ) -> Self {
37
44
Self { reviewers }
38
45
}
39
46
@@ -112,6 +119,10 @@ pub(super) async fn handle_input<'a>(
112
119
return Ok ( ( ) ) ;
113
120
}
114
121
122
+ let assigned_pr = AssignedPullRequest {
123
+ title : pr. title . clone ( ) ,
124
+ } ;
125
+
115
126
match input {
116
127
// The PR was assigned to a specific user, and it is waiting for a review.
117
128
ReviewPrefsInput :: Assigned { assignee } => {
@@ -120,7 +131,7 @@ pub(super) async fn handle_input<'a>(
120
131
assignee. login
121
132
) ;
122
133
123
- upsert_pr_into_user_queue ( & mut workqueue, assignee. id , pr_number) ;
134
+ upsert_pr_into_user_queue ( & mut workqueue, assignee. id , pr_number, assigned_pr ) ;
124
135
}
125
136
ReviewPrefsInput :: Unassigned { assignee } => {
126
137
log:: info!(
@@ -139,7 +150,12 @@ pub(super) async fn handle_input<'a>(
139
150
// receive.
140
151
ReviewPrefsInput :: OtherChange => {
141
152
for assignee in & event. issue . assignees {
142
- if upsert_pr_into_user_queue ( & mut workqueue, assignee. id , pr_number) {
153
+ if upsert_pr_into_user_queue (
154
+ & mut workqueue,
155
+ assignee. id ,
156
+ pr_number,
157
+ assigned_pr. clone ( ) ,
158
+ ) {
143
159
log:: info!( "Adding PR {pr_number} to workqueue of {}." , assignee. login) ;
144
160
}
145
161
}
@@ -155,10 +171,11 @@ pub async fn load_workqueue(client: &Octocrab) -> anyhow::Result<ReviewerWorkque
155
171
let prs = retrieve_pull_request_assignments ( "rust-lang" , "rust" , & client) . await ?;
156
172
157
173
// Aggregate PRs by user
158
- let aggregated: HashMap < UserId , HashSet < PullRequestNumber > > =
159
- prs. into_iter ( ) . fold ( HashMap :: new ( ) , |mut acc, ( user, pr) | {
174
+ let aggregated: HashMap < UserId , HashMap < PullRequestNumber , AssignedPullRequest > > = prs
175
+ . into_iter ( )
176
+ . fold ( HashMap :: new ( ) , |mut acc, ( user, pr_number, pr) | {
160
177
let prs = acc. entry ( user. id ) . or_default ( ) ;
161
- prs. insert ( pr) ;
178
+ prs. insert ( pr_number , pr) ;
162
179
acc
163
180
} ) ;
164
181
tracing:: debug!( "PR assignments\n {aggregated:?}" ) ;
@@ -174,7 +191,7 @@ pub async fn retrieve_pull_request_assignments(
174
191
owner : & str ,
175
192
repository : & str ,
176
193
client : & Octocrab ,
177
- ) -> anyhow:: Result < Vec < ( User , PullRequestNumber ) > > {
194
+ ) -> anyhow:: Result < Vec < ( User , PullRequestNumber , AssignedPullRequest ) > > {
178
195
let mut assignments = vec ! [ ] ;
179
196
180
197
// We use the REST API to fetch open pull requests, as it is much (~5-10x)
@@ -224,6 +241,9 @@ pub async fn retrieve_pull_request_assignments(
224
241
id : ( * user. id ) . into ( ) ,
225
242
} ,
226
243
pr. number ,
244
+ AssignedPullRequest {
245
+ title : pr. title . clone ( ) . unwrap_or_default ( ) ,
246
+ } ,
227
247
) ) ;
228
248
}
229
249
}
@@ -234,7 +254,10 @@ pub async fn retrieve_pull_request_assignments(
234
254
}
235
255
236
256
/// Get pull request assignments for a team member
237
- pub async fn get_assigned_prs ( ctx : & Context , user_id : UserId ) -> HashSet < PullRequestNumber > {
257
+ pub async fn get_assigned_prs (
258
+ ctx : & Context ,
259
+ user_id : UserId ,
260
+ ) -> HashMap < PullRequestNumber , AssignedPullRequest > {
238
261
ctx. workqueue
239
262
. read ( )
240
263
. await
@@ -245,15 +268,22 @@ pub async fn get_assigned_prs(ctx: &Context, user_id: UserId) -> HashSet<PullReq
245
268
}
246
269
247
270
/// Add a PR to the workqueue of a team member.
271
+ /// Updates data of the pull request if it already was in the workqueue.
248
272
/// Ensures no accidental PR duplicates.
249
273
///
250
274
/// Returns true if the PR was actually inserted.
251
275
fn upsert_pr_into_user_queue (
252
276
workqueue : & mut RwLockWriteGuard < ReviewerWorkqueue > ,
253
277
user_id : UserId ,
254
278
pr : PullRequestNumber ,
279
+ assigned_pr : AssignedPullRequest ,
255
280
) -> bool {
256
- workqueue. reviewers . entry ( user_id) . or_default ( ) . insert ( pr)
281
+ workqueue
282
+ . reviewers
283
+ . entry ( user_id)
284
+ . or_default ( )
285
+ . insert ( pr, assigned_pr)
286
+ . is_none ( )
257
287
}
258
288
259
289
/// Delete a PR from the workqueue of a team member.
@@ -270,7 +300,7 @@ fn delete_pr_from_user_queue(
270
300
/// Delete a PR from the workqueue completely.
271
301
fn delete_pr_from_all_queues ( workqueue : & mut ReviewerWorkqueue , pr : PullRequestNumber ) {
272
302
for queue in workqueue. reviewers . values_mut ( ) {
273
- queue. retain ( |pr_number| * pr_number != pr) ;
303
+ queue. retain ( |pr_number, _ | * pr_number != pr) ;
274
304
}
275
305
}
276
306
@@ -308,7 +338,9 @@ mod tests {
308
338
use crate :: config:: Config ;
309
339
use crate :: github:: { Issue , IssuesAction , IssuesEvent , Repository , User } ;
310
340
use crate :: github:: { Label , PullRequestNumber } ;
311
- use crate :: handlers:: pr_tracking:: { handle_input, parse_input, upsert_pr_into_user_queue} ;
341
+ use crate :: handlers:: pr_tracking:: {
342
+ handle_input, parse_input, upsert_pr_into_user_queue, AssignedPullRequest ,
343
+ } ;
312
344
use crate :: tests:: github:: { default_test_user, issue, pull_request, user} ;
313
345
use crate :: tests:: { run_db_test, TestContext } ;
314
346
@@ -504,7 +536,7 @@ mod tests {
504
536
. get ( & user. id )
505
537
. cloned ( )
506
538
. unwrap_or_default ( )
507
- . into_iter ( )
539
+ . into_keys ( )
508
540
. collect :: < Vec < _ > > ( ) ;
509
541
assigned. sort ( ) ;
510
542
assert_eq ! ( assigned, expected_prs) ;
@@ -514,7 +546,14 @@ mod tests {
514
546
{
515
547
let mut workqueue = ctx. handler_ctx ( ) . workqueue . write ( ) . await ;
516
548
for & pr in prs {
517
- upsert_pr_into_user_queue ( & mut workqueue, user. id , pr) ;
549
+ upsert_pr_into_user_queue (
550
+ & mut workqueue,
551
+ user. id ,
552
+ pr,
553
+ AssignedPullRequest {
554
+ title : format ! ( "PR {pr}" ) ,
555
+ } ,
556
+ ) ;
518
557
}
519
558
}
520
559
check_assigned_prs ( & ctx, user, prs) . await ;
0 commit comments