11use anyhow:: Result ;
22use serde:: { Deserialize , Serialize } ;
3+ use std:: collections:: BTreeMap ;
34
45use super :: BacklogClient ;
56
67#[ derive( Debug , Clone , Serialize , Deserialize ) ]
78#[ serde( rename_all = "camelCase" ) ]
89pub struct User {
910 pub id : u64 ,
10- pub user_id : String ,
11+ /// `null` for bot accounts (e.g. automation bots have no userId in Backlog API).
12+ pub user_id : Option < String > ,
1113 pub name : String ,
12- pub mail_address : String ,
14+ /// `null` for bot accounts.
15+ pub mail_address : Option < String > ,
1316 pub role_type : u8 ,
17+ #[ serde( default ) ]
18+ pub lang : Option < String > ,
19+ #[ serde( default ) ]
20+ pub last_login_time : Option < String > ,
21+ #[ serde( flatten) ]
22+ pub extra : BTreeMap < String , serde_json:: Value > ,
1423}
1524
1625impl BacklogClient {
@@ -19,6 +28,18 @@ impl BacklogClient {
1928 serde_json:: from_value ( value)
2029 . map_err ( |e| anyhow:: anyhow!( "Failed to deserialize user response: {}" , e) )
2130 }
31+
32+ pub fn get_users ( & self ) -> Result < Vec < User > > {
33+ let value = self . get ( "/users" ) ?;
34+ serde_json:: from_value ( value)
35+ . map_err ( |e| anyhow:: anyhow!( "Failed to deserialize users response: {}" , e) )
36+ }
37+
38+ pub fn get_user ( & self , user_id : u64 ) -> Result < User > {
39+ let value = self . get ( & format ! ( "/users/{user_id}" ) ) ?;
40+ serde_json:: from_value ( value)
41+ . map_err ( |e| anyhow:: anyhow!( "Failed to deserialize user response: {}" , e) )
42+ }
2243}
2344
2445#[ cfg( test) ]
@@ -33,7 +54,9 @@ mod tests {
3354 "userId" : "john" ,
3455 "name" : "John Doe" ,
3556 "mailAddress" : "john@example.com" ,
36- "roleType" : 1
57+ "roleType" : 1 ,
58+ "lang" : "ja" ,
59+ "lastLoginTime" : "2024-01-01T00:00:00Z"
3760 } )
3861 }
3962
@@ -48,7 +71,7 @@ mod tests {
4871 let client = BacklogClient :: new_with ( & server. base_url ( ) , "test-key" ) . unwrap ( ) ;
4972 let user = client. get_myself ( ) . unwrap ( ) ;
5073 assert_eq ! ( user. id, 123 ) ;
51- assert_eq ! ( user. user_id, "john" ) ;
74+ assert_eq ! ( user. user_id. as_deref ( ) , Some ( "john" ) ) ;
5275 assert_eq ! ( user. name, "John Doe" ) ;
5376 }
5477
@@ -67,25 +90,83 @@ mod tests {
6790 }
6891
6992 #[ test]
70- fn deserialize_user ( ) {
71- let v = json ! ( {
72- "id" : 123 ,
73- "userId" : "john" ,
74- "name" : "John Doe" ,
75- "mailAddress" : "john@example.com" ,
76- "roleType" : 1
93+ fn get_users_returns_list ( ) {
94+ let server = MockServer :: start ( ) ;
95+ server. mock ( |when, then| {
96+ when. method ( GET ) . path ( "/users" ) ;
97+ then. status ( 200 ) . json_body ( json ! ( [ user_json( ) ] ) ) ;
98+ } ) ;
99+
100+ let client = BacklogClient :: new_with ( & server. base_url ( ) , "test-key" ) . unwrap ( ) ;
101+ let users = client. get_users ( ) . unwrap ( ) ;
102+ assert_eq ! ( users. len( ) , 1 ) ;
103+ assert_eq ! ( users[ 0 ] . id, 123 ) ;
104+ }
105+
106+ #[ test]
107+ fn get_users_returns_error_on_api_failure ( ) {
108+ let server = MockServer :: start ( ) ;
109+ server. mock ( |when, then| {
110+ when. method ( GET ) . path ( "/users" ) ;
111+ then. status ( 403 )
112+ . json_body ( json ! ( { "errors" : [ { "message" : "Forbidden" } ] } ) ) ;
113+ } ) ;
114+
115+ let client = BacklogClient :: new_with ( & server. base_url ( ) , "test-key" ) . unwrap ( ) ;
116+ let err = client. get_users ( ) . unwrap_err ( ) ;
117+ assert ! ( err. to_string( ) . contains( "Forbidden" ) ) ;
118+ }
119+
120+ #[ test]
121+ fn get_user_returns_parsed_struct ( ) {
122+ let server = MockServer :: start ( ) ;
123+ server. mock ( |when, then| {
124+ when. method ( GET ) . path ( "/users/123" ) ;
125+ then. status ( 200 ) . json_body ( user_json ( ) ) ;
77126 } ) ;
127+
128+ let client = BacklogClient :: new_with ( & server. base_url ( ) , "test-key" ) . unwrap ( ) ;
129+ let user = client. get_user ( 123 ) . unwrap ( ) ;
130+ assert_eq ! ( user. id, 123 ) ;
131+ assert_eq ! ( user. name, "John Doe" ) ;
132+ }
133+
134+ #[ test]
135+ fn get_user_returns_error_on_not_found ( ) {
136+ let server = MockServer :: start ( ) ;
137+ server. mock ( |when, then| {
138+ when. method ( GET ) . path ( "/users/999" ) ;
139+ then. status ( 404 )
140+ . json_body ( json ! ( { "errors" : [ { "message" : "No user" } ] } ) ) ;
141+ } ) ;
142+
143+ let client = BacklogClient :: new_with ( & server. base_url ( ) , "test-key" ) . unwrap ( ) ;
144+ let err = client. get_user ( 999 ) . unwrap_err ( ) ;
145+ assert ! ( err. to_string( ) . contains( "No user" ) ) ;
146+ }
147+
148+ #[ test]
149+ fn deserialize_user ( ) {
150+ let v = user_json ( ) ;
78151 let user: User = serde_json:: from_value ( v) . unwrap ( ) ;
79152 assert_eq ! ( user. id, 123 ) ;
80- assert_eq ! ( user. user_id, "john" ) ;
153+ assert_eq ! ( user. user_id. as_deref ( ) , Some ( "john" ) ) ;
81154 assert_eq ! ( user. name, "John Doe" ) ;
82- assert_eq ! ( user. mail_address, "john@example.com" ) ;
155+ assert_eq ! ( user. mail_address. as_deref ( ) , Some ( "john@example.com" ) ) ;
83156 assert_eq ! ( user. role_type, 1 ) ;
84157 }
85158
86159 #[ test]
87- fn deserialize_user_fails_on_missing_required_field ( ) {
88- let v = json ! ( { "id" : 123 , "userId" : "john" } ) ;
89- assert ! ( serde_json:: from_value:: <User >( v) . is_err( ) ) ;
160+ fn deserialize_user_with_null_user_id ( ) {
161+ let v = json ! ( {
162+ "id" : 1 ,
163+ "userId" : null,
164+ "name" : "Bot" ,
165+ "mailAddress" : null,
166+ "roleType" : 2
167+ } ) ;
168+ let user: User = serde_json:: from_value ( v) . unwrap ( ) ;
169+ assert_eq ! ( user. user_id, None ) ;
170+ assert_eq ! ( user. mail_address, None ) ;
90171 }
91172}
0 commit comments