1
1
use crate :: db:: users:: record_username;
2
2
use crate :: github:: { User , UserId } ;
3
3
use anyhow:: Context ;
4
+ use bytes:: BytesMut ;
5
+ use postgres_types:: { to_sql_checked, FromSql , IsNull , ToSql , Type } ;
4
6
use std:: collections:: HashMap ;
7
+ use std:: error:: Error ;
8
+
9
+ #[ derive( Debug , Default , Copy , Clone ) ]
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
+ }
5
56
6
57
#[ derive( Debug ) ]
7
58
pub struct ReviewPrefs {
8
59
pub id : uuid:: Uuid ,
9
60
pub user_id : i64 ,
10
61
pub max_assigned_prs : Option < i32 > ,
62
+ pub rotation_mode : RotationMode ,
11
63
}
12
64
13
65
impl From < tokio_postgres:: row:: Row > for ReviewPrefs {
@@ -16,6 +68,7 @@ impl From<tokio_postgres::row::Row> for ReviewPrefs {
16
68
id : row. get ( "id" ) ,
17
69
user_id : row. get ( "user_id" ) ,
18
70
max_assigned_prs : row. get ( "max_assigned_prs" ) ,
71
+ rotation_mode : row. get ( "rotation_mode" ) ,
19
72
}
20
73
}
21
74
}
@@ -27,7 +80,7 @@ pub async fn get_review_prefs(
27
80
user_id : UserId ,
28
81
) -> anyhow:: Result < Option < ReviewPrefs > > {
29
82
let query = "
30
- SELECT id, user_id, max_assigned_prs
83
+ SELECT id, user_id, max_assigned_prs, rotation_mode
31
84
FROM review_prefs
32
85
WHERE review_prefs.user_id = $1;" ;
33
86
let row = db
@@ -56,10 +109,15 @@ pub async fn get_review_prefs_batch<'a>(
56
109
. collect ( ) ;
57
110
let lowercase_users: Vec < & str > = lowercase_map. keys ( ) . map ( |s| s. as_str ( ) ) . collect ( ) ;
58
111
59
- // 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
60
113
// `From<tokio_postgres::row::Row> for ReviewPrefs`.
61
114
let query = "
62
- 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
63
121
FROM review_prefs AS r
64
122
JOIN users AS u ON u.user_id = r.user_id
65
123
WHERE lower(u.username) = ANY($1);" ;
@@ -87,28 +145,33 @@ pub async fn upsert_review_prefs(
87
145
db : & tokio_postgres:: Client ,
88
146
user : User ,
89
147
max_assigned_prs : Option < u32 > ,
148
+ rotation_mode : RotationMode ,
90
149
) -> anyhow:: Result < u64 , anyhow:: Error > {
91
150
// We need to have the user stored in the DB to have a valid FK link in review_prefs
92
151
record_username ( db, user. id , & user. login ) . await ?;
93
152
94
153
let max_assigned_prs = max_assigned_prs. map ( |v| v as i32 ) ;
95
154
let query = "
96
- INSERT INTO review_prefs(user_id, max_assigned_prs)
97
- VALUES ($1, $2)
155
+ INSERT INTO review_prefs(user_id, max_assigned_prs, rotation_mode )
156
+ VALUES ($1, $2, $3 )
98
157
ON CONFLICT (user_id)
99
158
DO UPDATE
100
- SET max_assigned_prs = excluded.max_assigned_prs" ;
159
+ SET max_assigned_prs = excluded.max_assigned_prs,
160
+ rotation_mode = excluded.rotation_mode" ;
101
161
102
162
let res = db
103
- . execute ( query, & [ & ( user. id as i64 ) , & max_assigned_prs] )
163
+ . execute (
164
+ query,
165
+ & [ & ( user. id as i64 ) , & max_assigned_prs, & rotation_mode] ,
166
+ )
104
167
. await
105
168
. context ( "Error upserting review preferences" ) ?;
106
169
Ok ( res)
107
170
}
108
171
109
172
#[ cfg( test) ]
110
173
mod tests {
111
- use crate :: db:: review_prefs:: { get_review_prefs, upsert_review_prefs} ;
174
+ use crate :: db:: review_prefs:: { get_review_prefs, upsert_review_prefs, RotationMode } ;
112
175
use crate :: db:: users:: get_user;
113
176
use crate :: tests:: github:: user;
114
177
use crate :: tests:: run_db_test;
@@ -117,7 +180,13 @@ mod tests {
117
180
async fn insert_prefs_create_user ( ) {
118
181
run_db_test ( |ctx| async {
119
182
let user = user ( "Martin" , 1 ) ;
120
- 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 ?;
121
190
assert_eq ! ( get_user( & ctx. db_client( ) , user. id) . await ?. unwrap( ) , user) ;
122
191
123
192
Ok ( ctx)
@@ -128,7 +197,13 @@ mod tests {
128
197
#[ tokio:: test]
129
198
async fn insert_max_assigned_prs ( ) {
130
199
run_db_test ( |ctx| async {
131
- 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 ?;
132
207
assert_eq ! (
133
208
get_review_prefs( & ctx. db_client( ) , 1 )
134
209
. await ?
@@ -147,18 +222,18 @@ mod tests {
147
222
run_db_test ( |ctx| async {
148
223
let db = ctx. db_client ( ) ;
149
224
150
- upsert_review_prefs ( & db, user ( "Martin" , 1 ) , Some ( 5 ) ) . await ?;
225
+ upsert_review_prefs ( & db, user ( "Martin" , 1 ) , Some ( 5 ) , RotationMode :: OnRotation ) . await ?;
151
226
assert_eq ! (
152
227
get_review_prefs( & db, 1 ) . await ?. unwrap( ) . max_assigned_prs,
153
228
Some ( 5 )
154
229
) ;
155
- upsert_review_prefs ( & db, user ( "Martin" , 1 ) , Some ( 10 ) ) . await ?;
230
+ upsert_review_prefs ( & db, user ( "Martin" , 1 ) , Some ( 10 ) , RotationMode :: OnRotation ) . await ?;
156
231
assert_eq ! (
157
232
get_review_prefs( & db, 1 ) . await ?. unwrap( ) . max_assigned_prs,
158
233
Some ( 10 )
159
234
) ;
160
235
161
- upsert_review_prefs ( & db, user ( "Martin" , 1 ) , None ) . await ?;
236
+ upsert_review_prefs ( & db, user ( "Martin" , 1 ) , None , RotationMode :: OnRotation ) . await ?;
162
237
assert_eq ! (
163
238
get_review_prefs( & db, 1 ) . await ?. unwrap( ) . max_assigned_prs,
164
239
None
0 commit comments