1
1
use crate :: db:: users:: record_username;
2
2
use crate :: github:: { User , UserId } ;
3
3
use anyhow:: Context ;
4
- use serde:: Serialize ;
4
+ use bytes:: BytesMut ;
5
+ use postgres_types:: { to_sql_checked, FromSql , IsNull , ToSql , Type } ;
5
6
use std:: collections:: HashMap ;
7
+ use std:: error:: Error ;
6
8
7
- #[ derive( Debug , Serialize ) ]
9
+ #[ derive( Debug , Default , Copy , Clone , PartialEq ) ]
10
+ pub enum RotationMode {
11
+ /// The reviewer can be automatically assigned by triagebot,
12
+ /// and they can be assigned through teams and assign groups.
13
+ #[ default]
14
+ OnRotation ,
15
+ /// The user is off rotation (e.g. on a vacation) and cannot be assigned automatically
16
+ /// nor through teams and assign groups.
17
+ OffRotation ,
18
+ }
19
+
20
+ impl < ' a > FromSql < ' a > for RotationMode {
21
+ fn from_sql ( ty : & Type , raw : & ' a [ u8 ] ) -> Result < Self , Box < dyn Error + Sync + Send > > {
22
+ let value = <& str as FromSql >:: from_sql ( ty, raw) ?;
23
+ match value {
24
+ "on-rotation" => Ok ( Self :: OnRotation ) ,
25
+ "off-rotation" => Ok ( Self :: OffRotation ) ,
26
+ _ => Err ( format ! ( "Unknown value for RotationMode: {value}" ) . into ( ) ) ,
27
+ }
28
+ }
29
+
30
+ fn accepts ( ty : & Type ) -> bool {
31
+ <& str as FromSql >:: accepts ( ty)
32
+ }
33
+ }
34
+
35
+ impl ToSql for RotationMode {
36
+ fn to_sql ( & self , ty : & Type , out : & mut BytesMut ) -> Result < IsNull , Box < dyn Error + Sync + Send > >
37
+ where
38
+ Self : Sized ,
39
+ {
40
+ let value = match self {
41
+ RotationMode :: OnRotation => "on-rotation" ,
42
+ RotationMode :: OffRotation => "off-rotation" ,
43
+ } ;
44
+ <& str as ToSql >:: to_sql ( & value, ty, out)
45
+ }
46
+
47
+ fn accepts ( ty : & Type ) -> bool
48
+ where
49
+ Self : Sized ,
50
+ {
51
+ <& str as FromSql >:: accepts ( ty)
52
+ }
53
+
54
+ to_sql_checked ! ( ) ;
55
+ }
56
+
57
+ #[ derive( Debug ) ]
8
58
pub struct ReviewPrefs {
9
59
pub id : uuid:: Uuid ,
10
60
pub user_id : i64 ,
11
61
pub max_assigned_prs : Option < i32 > ,
62
+ pub rotation_mode : RotationMode ,
12
63
}
13
64
14
65
impl From < tokio_postgres:: row:: Row > for ReviewPrefs {
@@ -17,6 +68,7 @@ impl From<tokio_postgres::row::Row> for ReviewPrefs {
17
68
id : row. get ( "id" ) ,
18
69
user_id : row. get ( "user_id" ) ,
19
70
max_assigned_prs : row. get ( "max_assigned_prs" ) ,
71
+ rotation_mode : row. get ( "rotation_mode" ) ,
20
72
}
21
73
}
22
74
}
@@ -28,7 +80,7 @@ pub async fn get_review_prefs(
28
80
user_id : UserId ,
29
81
) -> anyhow:: Result < Option < ReviewPrefs > > {
30
82
let query = "
31
- SELECT id, user_id, max_assigned_prs
83
+ SELECT id, user_id, max_assigned_prs, rotation_mode
32
84
FROM review_prefs
33
85
WHERE review_prefs.user_id = $1;" ;
34
86
let row = db
@@ -57,10 +109,15 @@ pub async fn get_review_prefs_batch<'a>(
57
109
. collect ( ) ;
58
110
let lowercase_users: Vec < & str > = lowercase_map. keys ( ) . map ( |s| s. as_str ( ) ) . collect ( ) ;
59
111
60
- // The id/user_id/max_assigned_prs columns have to match the names used in
112
+ // The id/user_id/max_assigned_prs/rotation_mode columns have to match the names used in
61
113
// `From<tokio_postgres::row::Row> for ReviewPrefs`.
62
114
let query = "
63
- SELECT lower(u.username) AS username, r.id AS id, r.user_id AS user_id, r.max_assigned_prs AS max_assigned_prs
115
+ SELECT
116
+ lower(u.username) AS username,
117
+ r.id AS id,
118
+ r.user_id AS user_id,
119
+ r.max_assigned_prs AS max_assigned_prs,
120
+ r.rotation_mode AS rotation_mode
64
121
FROM review_prefs AS r
65
122
JOIN users AS u ON u.user_id = r.user_id
66
123
WHERE lower(u.username) = ANY($1);" ;
@@ -88,28 +145,33 @@ pub async fn upsert_review_prefs(
88
145
db : & tokio_postgres:: Client ,
89
146
user : User ,
90
147
max_assigned_prs : Option < u32 > ,
148
+ rotation_mode : RotationMode ,
91
149
) -> anyhow:: Result < u64 , anyhow:: Error > {
92
150
// We need to have the user stored in the DB to have a valid FK link in review_prefs
93
151
record_username ( db, user. id , & user. login ) . await ?;
94
152
95
153
let max_assigned_prs = max_assigned_prs. map ( |v| v as i32 ) ;
96
154
let query = "
97
- INSERT INTO review_prefs(user_id, max_assigned_prs)
98
- VALUES ($1, $2)
155
+ INSERT INTO review_prefs(user_id, max_assigned_prs, rotation_mode )
156
+ VALUES ($1, $2, $3 )
99
157
ON CONFLICT (user_id)
100
158
DO UPDATE
101
- SET max_assigned_prs = excluded.max_assigned_prs" ;
159
+ SET max_assigned_prs = excluded.max_assigned_prs,
160
+ rotation_mode = excluded.rotation_mode" ;
102
161
103
162
let res = db
104
- . execute ( query, & [ & ( user. id as i64 ) , & max_assigned_prs] )
163
+ . execute (
164
+ query,
165
+ & [ & ( user. id as i64 ) , & max_assigned_prs, & rotation_mode] ,
166
+ )
105
167
. await
106
168
. context ( "Error upserting review preferences" ) ?;
107
169
Ok ( res)
108
170
}
109
171
110
172
#[ cfg( test) ]
111
173
mod tests {
112
- use crate :: db:: review_prefs:: { get_review_prefs, upsert_review_prefs} ;
174
+ use crate :: db:: review_prefs:: { get_review_prefs, upsert_review_prefs, RotationMode } ;
113
175
use crate :: db:: users:: get_user;
114
176
use crate :: tests:: github:: user;
115
177
use crate :: tests:: run_db_test;
@@ -118,7 +180,13 @@ mod tests {
118
180
async fn insert_prefs_create_user ( ) {
119
181
run_db_test ( |ctx| async {
120
182
let user = user ( "Martin" , 1 ) ;
121
- upsert_review_prefs ( & ctx. db_client ( ) , user. clone ( ) , Some ( 1 ) ) . await ?;
183
+ upsert_review_prefs (
184
+ & ctx. db_client ( ) ,
185
+ user. clone ( ) ,
186
+ Some ( 1 ) ,
187
+ RotationMode :: OnRotation ,
188
+ )
189
+ . await ?;
122
190
assert_eq ! ( get_user( & ctx. db_client( ) , user. id) . await ?. unwrap( ) , user) ;
123
191
124
192
Ok ( ctx)
@@ -129,7 +197,13 @@ mod tests {
129
197
#[ tokio:: test]
130
198
async fn insert_max_assigned_prs ( ) {
131
199
run_db_test ( |ctx| async {
132
- upsert_review_prefs ( & ctx. db_client ( ) , user ( "Martin" , 1 ) , Some ( 5 ) ) . await ?;
200
+ upsert_review_prefs (
201
+ & ctx. db_client ( ) ,
202
+ user ( "Martin" , 1 ) ,
203
+ Some ( 5 ) ,
204
+ RotationMode :: OnRotation ,
205
+ )
206
+ . await ?;
133
207
assert_eq ! (
134
208
get_review_prefs( & ctx. db_client( ) , 1 )
135
209
. await ?
@@ -148,18 +222,18 @@ mod tests {
148
222
run_db_test ( |ctx| async {
149
223
let db = ctx. db_client ( ) ;
150
224
151
- upsert_review_prefs ( & db, user ( "Martin" , 1 ) , Some ( 5 ) ) . await ?;
225
+ upsert_review_prefs ( & db, user ( "Martin" , 1 ) , Some ( 5 ) , RotationMode :: OnRotation ) . await ?;
152
226
assert_eq ! (
153
227
get_review_prefs( & db, 1 ) . await ?. unwrap( ) . max_assigned_prs,
154
228
Some ( 5 )
155
229
) ;
156
- upsert_review_prefs ( & db, user ( "Martin" , 1 ) , Some ( 10 ) ) . await ?;
230
+ upsert_review_prefs ( & db, user ( "Martin" , 1 ) , Some ( 10 ) , RotationMode :: OnRotation ) . await ?;
157
231
assert_eq ! (
158
232
get_review_prefs( & db, 1 ) . await ?. unwrap( ) . max_assigned_prs,
159
233
Some ( 10 )
160
234
) ;
161
235
162
- upsert_review_prefs ( & db, user ( "Martin" , 1 ) , None ) . await ?;
236
+ upsert_review_prefs ( & db, user ( "Martin" , 1 ) , None , RotationMode :: OnRotation ) . await ?;
163
237
assert_eq ! (
164
238
get_review_prefs( & db, 1 ) . await ?. unwrap( ) . max_assigned_prs,
165
239
None
@@ -169,4 +243,26 @@ mod tests {
169
243
} )
170
244
. await ;
171
245
}
246
+
247
+ #[ tokio:: test]
248
+ async fn set_rotation_mode ( ) {
249
+ run_db_test ( |ctx| async {
250
+ let db = ctx. db_client ( ) ;
251
+ let user = user ( "Martin" , 1 ) ;
252
+
253
+ upsert_review_prefs ( & db, user. clone ( ) , Some ( 5 ) , RotationMode :: OnRotation ) . await ?;
254
+ assert_eq ! (
255
+ get_review_prefs( & db, 1 ) . await ?. unwrap( ) . rotation_mode,
256
+ RotationMode :: OnRotation
257
+ ) ;
258
+ upsert_review_prefs ( & db, user. clone ( ) , Some ( 10 ) , RotationMode :: OffRotation ) . await ?;
259
+ assert_eq ! (
260
+ get_review_prefs( & db, 1 ) . await ?. unwrap( ) . rotation_mode,
261
+ RotationMode :: OffRotation
262
+ ) ;
263
+
264
+ Ok ( ctx)
265
+ } )
266
+ . await ;
267
+ }
172
268
}
0 commit comments