@@ -92,7 +92,46 @@ impl HttpClient {
9292 }
9393 }
9494
95+ /// Send a request to the GitHub API and return the response.
9596 fn graphql < R , V > ( & self , query : & str , variables : V , org : & str ) -> anyhow:: Result < R >
97+ where
98+ R : serde:: de:: DeserializeOwned ,
99+ V : serde:: Serialize ,
100+ {
101+ let res = self . send_graphql_req ( query, variables, org) ?;
102+
103+ if let Some ( error) = res. errors . first ( ) {
104+ bail ! ( "graphql error: {}" , error. message) ;
105+ }
106+
107+ read_graphql_data ( res)
108+ }
109+
110+ /// Send a request to the GitHub API and return the response.
111+ /// If the request contains the error type `NOT_FOUND`, this method returns `Ok(None)`.
112+ fn graphql_opt < R , V > ( & self , query : & str , variables : V , org : & str ) -> anyhow:: Result < Option < R > >
113+ where
114+ R : serde:: de:: DeserializeOwned ,
115+ V : serde:: Serialize ,
116+ {
117+ let res = self . send_graphql_req ( query, variables, org) ?;
118+
119+ if let Some ( error) = res. errors . first ( ) {
120+ if error. type_ == Some ( GraphErrorType :: NotFound ) {
121+ return Ok ( None ) ;
122+ }
123+ bail ! ( "graphql error: {}" , error. message) ;
124+ }
125+
126+ read_graphql_data ( res)
127+ }
128+
129+ fn send_graphql_req < R , V > (
130+ & self ,
131+ query : & str ,
132+ variables : V ,
133+ org : & str ,
134+ ) -> anyhow:: Result < GraphResult < R > >
96135 where
97136 R : serde:: de:: DeserializeOwned ,
98137 V : serde:: Serialize ,
@@ -105,19 +144,13 @@ impl HttpClient {
105144 let resp = self
106145 . req ( Method :: POST , & GitHubUrl :: new ( "graphql" , org) ) ?
107146 . json ( & Request { query, variables } )
108- . send ( ) ?
147+ . send ( )
148+ . context ( "failed to send graphql request" ) ?
109149 . custom_error_for_status ( ) ?;
110150
111- let res : GraphResult < R > = resp. json_annotated ( ) . with_context ( || {
151+ resp. json_annotated ( ) . with_context ( || {
112152 format ! ( "Failed to decode response body on graphql request with query '{query}'" )
113- } ) ?;
114- if let Some ( error) = res. errors . first ( ) {
115- bail ! ( "graphql error: {}" , error. message) ;
116- } else if let Some ( data) = res. data {
117- Ok ( data)
118- } else {
119- bail ! ( "missing graphql data" ) ;
120- }
153+ } )
121154 }
122155
123156 fn rest_paginated < F , T > ( & self , method : & Method , url : & GitHubUrl , mut f : F ) -> anyhow:: Result < ( ) >
@@ -159,6 +192,17 @@ impl HttpClient {
159192 }
160193}
161194
195+ fn read_graphql_data < R > ( res : GraphResult < R > ) -> anyhow:: Result < R >
196+ where
197+ R : serde:: de:: DeserializeOwned ,
198+ {
199+ if let Some ( data) = res. data {
200+ Ok ( data)
201+ } else {
202+ bail ! ( "missing graphql data" ) ;
203+ }
204+ }
205+
162206fn allow_not_found ( resp : Response , method : Method , url : & str ) -> Result < ( ) , anyhow:: Error > {
163207 match resp. status ( ) {
164208 StatusCode :: NOT_FOUND => {
@@ -180,9 +224,19 @@ struct GraphResult<T> {
180224
181225#[ derive( Debug , serde:: Deserialize ) ]
182226struct GraphError {
227+ #[ serde( rename = "type" ) ]
228+ type_ : Option < GraphErrorType > ,
183229 message : String ,
184230}
185231
232+ #[ derive( Debug , serde:: Deserialize , PartialEq , Eq ) ]
233+ #[ serde( rename_all = "SCREAMING_SNAKE_CASE" ) ]
234+ enum GraphErrorType {
235+ NotFound ,
236+ #[ serde( other) ]
237+ Other ,
238+ }
239+
186240#[ derive( serde:: Deserialize ) ]
187241struct GraphNodes < T > {
188242 nodes : Vec < Option < T > > ,
0 commit comments