1
+ use crate :: test_record;
1
2
use anyhow:: Context ;
2
3
use async_trait:: async_trait;
4
+ use bytes:: Bytes ;
3
5
use chrono:: { DateTime , FixedOffset , Utc } ;
4
6
use futures:: { future:: BoxFuture , FutureExt } ;
5
7
use hyper:: header:: HeaderValue ;
@@ -21,22 +23,32 @@ pub struct User {
21
23
}
22
24
23
25
impl GithubClient {
24
- async fn _send_req ( & self , req : RequestBuilder ) -> anyhow:: Result < ( Response , String ) > {
26
+ async fn send_req ( & self , req : RequestBuilder ) -> anyhow:: Result < ( Bytes , String ) > {
25
27
const MAX_ATTEMPTS : usize = 2 ;
26
- log:: debug!( "_send_req with {:?}" , req) ;
28
+ log:: debug!( "send_req with {:?}" , req) ;
27
29
let req_dbg = format ! ( "{:?}" , req) ;
28
30
let req = req
29
31
. build ( )
30
32
. with_context ( || format ! ( "building reqwest {}" , req_dbg) ) ?;
31
33
34
+ let test_capture_info = test_record:: capture_request ( & req) ;
35
+
32
36
let mut resp = self . client . execute ( req. try_clone ( ) . unwrap ( ) ) . await ?;
33
37
if let Some ( sleep) = Self :: needs_retry ( & resp) . await {
34
38
resp = self . retry ( req, sleep, MAX_ATTEMPTS ) . await ?;
35
39
}
40
+ let status = resp. status ( ) ;
41
+ let maybe_err = resp. error_for_status_ref ( ) . err ( ) ;
42
+ let body = resp
43
+ . bytes ( )
44
+ . await
45
+ . with_context ( || format ! ( "failed to read response body {req_dbg}" ) ) ?;
46
+ test_record:: record_request ( test_capture_info, status, & body) ;
47
+ if let Some ( e) = maybe_err {
48
+ return Err ( e. into ( ) ) ;
49
+ }
36
50
37
- resp. error_for_status_ref ( ) ?;
38
-
39
- Ok ( ( resp, req_dbg) )
51
+ Ok ( ( body, req_dbg) )
40
52
}
41
53
42
54
async fn needs_retry ( resp : & Response ) -> Option < Duration > {
@@ -116,6 +128,7 @@ impl GithubClient {
116
128
. unwrap ( ) ,
117
129
)
118
130
. await ?;
131
+ rate_resp. error_for_status_ref ( ) ?;
119
132
let rate_limit_response = rate_resp. json :: < RateLimitResponse > ( ) . await ?;
120
133
121
134
// Check url for search path because github has different rate limits for the search api
@@ -151,27 +164,12 @@ impl GithubClient {
151
164
. boxed ( )
152
165
}
153
166
154
- async fn send_req ( & self , req : RequestBuilder ) -> anyhow:: Result < Vec < u8 > > {
155
- let ( mut resp, req_dbg) = self . _send_req ( req) . await ?;
156
-
157
- let mut body = Vec :: new ( ) ;
158
- while let Some ( chunk) = resp. chunk ( ) . await . transpose ( ) {
159
- let chunk = chunk
160
- . context ( "reading stream failed" )
161
- . map_err ( anyhow:: Error :: from)
162
- . context ( req_dbg. clone ( ) ) ?;
163
- body. extend_from_slice ( & chunk) ;
164
- }
165
-
166
- Ok ( body)
167
- }
168
-
169
167
pub async fn json < T > ( & self , req : RequestBuilder ) -> anyhow:: Result < T >
170
168
where
171
169
T : serde:: de:: DeserializeOwned ,
172
170
{
173
- let ( resp , req_dbg ) = self . _send_req ( req) . await ?;
174
- Ok ( resp . json ( ) . await . context ( req_dbg ) ?)
171
+ let ( body , _req_dbg ) = self . send_req ( req) . await ?;
172
+ Ok ( serde_json :: from_slice ( & body ) ?)
175
173
}
176
174
}
177
175
@@ -414,8 +412,8 @@ impl IssueRepository {
414
412
async fn has_label ( & self , client : & GithubClient , label : & str ) -> anyhow:: Result < bool > {
415
413
#[ allow( clippy:: redundant_pattern_matching) ]
416
414
let url = format ! ( "{}/labels/{}" , self . url( client) , label) ;
417
- match client. _send_req ( client. get ( & url) ) . await {
418
- Ok ( ( _ , _ ) ) => Ok ( true ) ,
415
+ match client. send_req ( client. get ( & url) ) . await {
416
+ Ok ( _ ) => Ok ( true ) ,
419
417
Err ( e) => {
420
418
if e. downcast_ref :: < reqwest:: Error > ( )
421
419
. map_or ( false , |e| e. status ( ) == Some ( StatusCode :: NOT_FOUND ) )
@@ -495,7 +493,7 @@ impl Issue {
495
493
body : & ' a str ,
496
494
}
497
495
client
498
- . _send_req ( client. patch ( & edit_url) . json ( & ChangedIssue { body } ) )
496
+ . send_req ( client. patch ( & edit_url) . json ( & ChangedIssue { body } ) )
499
497
. await
500
498
. context ( "failed to edit issue body" ) ?;
501
499
Ok ( ( ) )
@@ -513,7 +511,7 @@ impl Issue {
513
511
body : & ' a str ,
514
512
}
515
513
client
516
- . _send_req (
514
+ . send_req (
517
515
client
518
516
. patch ( & comment_url)
519
517
. json ( & NewComment { body : new_body } ) ,
@@ -534,7 +532,7 @@ impl Issue {
534
532
. expect ( "expected api host" ) ;
535
533
let comments_url = format ! ( "{}{comments_path}" , client. api_url) ;
536
534
client
537
- . _send_req ( client. post ( & comments_url) . json ( & PostComment { body } ) )
535
+ . send_req ( client. post ( & comments_url) . json ( & PostComment { body } ) )
538
536
. await
539
537
. context ( "failed to post comment" ) ?;
540
538
Ok ( ( ) )
@@ -560,7 +558,7 @@ impl Issue {
560
558
}
561
559
562
560
client
563
- . _send_req ( client. delete ( & url) )
561
+ . send_req ( client. delete ( & url) )
564
562
. await
565
563
. context ( "failed to delete label" ) ?;
566
564
@@ -617,7 +615,7 @@ impl Issue {
617
615
}
618
616
619
617
client
620
- . _send_req ( client. post ( & url) . json ( & LabelsReq {
618
+ . send_req ( client. post ( & url) . json ( & LabelsReq {
621
619
labels : known_labels,
622
620
} ) )
623
621
. await
@@ -668,7 +666,7 @@ impl Issue {
668
666
assignees : & ' a [ & ' a str ] ,
669
667
}
670
668
client
671
- . _send_req ( client. delete ( & url) . json ( & AssigneeReq {
669
+ . send_req ( client. delete ( & url) . json ( & AssigneeReq {
672
670
assignees : & assignees[ ..] ,
673
671
} ) )
674
672
. await
@@ -761,7 +759,7 @@ impl Issue {
761
759
}
762
760
let url = format ! ( "{}/issues/{}" , self . repository( ) . url( client) , self . number) ;
763
761
client
764
- . _send_req ( client. patch ( & url) . json ( & SetMilestone {
762
+ . send_req ( client. patch ( & url) . json ( & SetMilestone {
765
763
milestone : milestone_no,
766
764
} ) )
767
765
. await
@@ -776,7 +774,7 @@ impl Issue {
776
774
state : & ' a str ,
777
775
}
778
776
client
779
- . _send_req (
777
+ . send_req (
780
778
client
781
779
. patch ( & edit_url)
782
780
. json ( & CloseIssue { state : "closed" } ) ,
@@ -801,8 +799,9 @@ impl Issue {
801
799
after
802
800
) ) ;
803
801
req = req. header ( "Accept" , "application/vnd.github.v3.diff" ) ;
804
- let diff = client. send_req ( req) . await ?;
805
- Ok ( Some ( String :: from ( String :: from_utf8_lossy ( & diff) ) ) )
802
+ let ( diff, _) = client. send_req ( req) . await ?;
803
+ let body = String :: from_utf8_lossy ( & diff) . to_string ( ) ;
804
+ Ok ( Some ( body) )
806
805
}
807
806
808
807
/// Returns the commits from this pull request (no commits are returned if this `Issue` is not
@@ -1246,7 +1245,7 @@ impl Repository {
1246
1245
) -> anyhow:: Result < ( ) > {
1247
1246
let url = format ! ( "{}/git/refs/{}" , self . url( client) , refname) ;
1248
1247
client
1249
- . _send_req ( client. patch ( & url) . json ( & serde_json:: json!( {
1248
+ . send_req ( client. patch ( & url) . json ( & serde_json:: json!( {
1250
1249
"sha" : sha,
1251
1250
"force" : true ,
1252
1251
} ) ) )
@@ -1505,7 +1504,7 @@ impl Repository {
1505
1504
pub async fn merge_upstream ( & self , client : & GithubClient , branch : & str ) -> anyhow:: Result < ( ) > {
1506
1505
let url = format ! ( "{}/merge-upstream" , self . url( client) ) ;
1507
1506
client
1508
- . _send_req ( client. post ( & url) . json ( & serde_json:: json!( {
1507
+ . send_req ( client. post ( & url) . json ( & serde_json:: json!( {
1509
1508
"branch" : branch,
1510
1509
} ) ) )
1511
1510
. await
@@ -1811,27 +1810,23 @@ impl GithubClient {
1811
1810
repo : & str ,
1812
1811
branch : & str ,
1813
1812
path : & str ,
1814
- ) -> anyhow:: Result < Option < Vec < u8 > > > {
1813
+ ) -> anyhow:: Result < Option < Bytes > > {
1815
1814
let url = format ! ( "{}/{repo}/{branch}/{path}" , self . raw_url) ;
1816
1815
let req = self . get ( & url) ;
1817
1816
let req_dbg = format ! ( "{:?}" , req) ;
1818
1817
let req = req
1819
1818
. build ( )
1820
1819
. with_context ( || format ! ( "failed to build request {:?}" , req_dbg) ) ?;
1821
- let mut resp = self . client . execute ( req) . await . context ( req_dbg. clone ( ) ) ?;
1820
+ let test_capture_info = test_record:: capture_request ( & req) ;
1821
+ let resp = self . client . execute ( req) . await . context ( req_dbg. clone ( ) ) ?;
1822
1822
let status = resp. status ( ) ;
1823
+ let body = resp
1824
+ . bytes ( )
1825
+ . await
1826
+ . with_context ( || format ! ( "failed to read response body {req_dbg}" ) ) ?;
1827
+ test_record:: record_request ( test_capture_info, status, & body) ;
1823
1828
match status {
1824
- StatusCode :: OK => {
1825
- let mut buf = Vec :: with_capacity ( resp. content_length ( ) . unwrap_or ( 4 ) as usize ) ;
1826
- while let Some ( chunk) = resp. chunk ( ) . await . transpose ( ) {
1827
- let chunk = chunk
1828
- . context ( "reading stream failed" )
1829
- . map_err ( anyhow:: Error :: from)
1830
- . context ( req_dbg. clone ( ) ) ?;
1831
- buf. extend_from_slice ( & chunk) ;
1832
- }
1833
- Ok ( Some ( buf) )
1834
- }
1829
+ StatusCode :: OK => Ok ( Some ( body) ) ,
1835
1830
StatusCode :: NOT_FOUND => Ok ( None ) ,
1836
1831
status => anyhow:: bail!( "failed to GET {}: {}" , url, status) ,
1837
1832
}
@@ -2036,11 +2031,8 @@ impl IssuesQuery for LeastRecentlyReviewedPullRequests {
2036
2031
let req = client. post ( & format ! ( "{}/graphql" , client. api_url) ) ;
2037
2032
let req = req. json ( & query) ;
2038
2033
2039
- let ( resp, req_dbg) = client. _send_req ( req) . await ?;
2040
- let data = resp
2041
- . json :: < cynic:: GraphQlResponse < queries:: LeastRecentlyReviewedPullRequests > > ( )
2042
- . await
2043
- . context ( req_dbg) ?;
2034
+ let data: cynic:: GraphQlResponse < queries:: LeastRecentlyReviewedPullRequests > =
2035
+ client. json ( req) . await ?;
2044
2036
if let Some ( errors) = data. errors {
2045
2037
anyhow:: bail!( "There were graphql errors. {:?}" , errors) ;
2046
2038
}
0 commit comments