Skip to content

Commit 285837b

Browse files
sypharJoshua Nelson
authored and
Joshua Nelson
committed
Fixes #1267 - fix handling of github graphql errors without path or data
1 parent 1e22bc1 commit 285837b

File tree

1 file changed

+58
-27
lines changed

1 file changed

+58
-27
lines changed

src/repositories/github.rs

+58-27
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,11 @@ impl RepositoryForge for GitHub {
9898
"repo": name.repo,
9999
}),
100100
)?;
101-
if let Some(repo) = response.data.repository {
102-
Ok(Some(Repository {
101+
102+
Ok(response
103+
.data
104+
.and_then(|data| data.repository)
105+
.map(|repo| Repository {
103106
id: repo.id,
104107
name_with_owner: repo.name_with_owner,
105108
description: repo.description,
@@ -108,9 +111,6 @@ impl RepositoryForge for GitHub {
108111
forks: repo.fork_count,
109112
issues: repo.issues.total_count,
110113
}))
111-
} else {
112-
Ok(None)
113-
}
114114
}
115115

116116
fn fetch_repositories(&self, node_ids: &[String]) -> Result<FetchRepositoriesResult> {
@@ -123,12 +123,14 @@ impl RepositoryForge for GitHub {
123123

124124
// The error is returned *before* we reach the rate limit, to ensure we always have an
125125
// amount of API calls we can make at any time.
126-
trace!(
127-
"GitHub GraphQL rate limit remaining: {}",
128-
response.data.rate_limit.remaining
129-
);
130-
if response.data.rate_limit.remaining < self.github_updater_min_rate_limit {
131-
return Err(RateLimitReached.into());
126+
if let Some(ref data) = response.data {
127+
trace!(
128+
"GitHub GraphQL rate limit remaining: {}",
129+
data.rate_limit.remaining
130+
);
131+
if data.rate_limit.remaining < self.github_updater_min_rate_limit {
132+
return Err(RateLimitReached.into());
133+
}
132134
}
133135

134136
let mut ret = FetchRepositoriesResult::default();
@@ -139,23 +141,29 @@ impl RepositoryForge for GitHub {
139141
("NOT_FOUND", [Segment(nodes), Index(idx)]) if nodes == "nodes" => {
140142
ret.missing.push(node_ids[*idx as usize].clone());
141143
}
144+
("RATE_LIMITED", []) => {
145+
return Err(RateLimitReached.into());
146+
}
142147
_ => failure::bail!("error updating repositories: {}", error.message),
143148
}
144149
}
145-
// When a node is missing (for example if the repository was deleted or made private) the
146-
// GraphQL API will return *both* a `null` instead of the data in the nodes list and a
147-
// `NOT_FOUND` error in the errors list.
148-
for node in response.data.nodes.into_iter().flatten() {
149-
let repo = Repository {
150-
id: node.id,
151-
name_with_owner: node.name_with_owner,
152-
description: node.description,
153-
last_activity_at: node.pushed_at,
154-
stars: node.stargazer_count,
155-
forks: node.fork_count,
156-
issues: node.issues.total_count,
157-
};
158-
ret.present.insert(repo.id.clone(), repo);
150+
151+
if let Some(data) = response.data {
152+
// When a node is missing (for example if the repository was deleted or made private) the
153+
// GraphQL API will return *both* a `null` instead of the data in the nodes list and a
154+
// `NOT_FOUND` error in the errors list.
155+
for node in data.nodes.into_iter().flatten() {
156+
let repo = Repository {
157+
id: node.id,
158+
name_with_owner: node.name_with_owner,
159+
description: node.description,
160+
last_activity_at: node.pushed_at,
161+
stars: node.stargazer_count,
162+
forks: node.fork_count,
163+
issues: node.issues.total_count,
164+
};
165+
ret.present.insert(repo.id.clone(), repo);
166+
}
159167
}
160168

161169
Ok(ret)
@@ -190,7 +198,7 @@ impl GitHub {
190198

191199
#[derive(Debug, Deserialize)]
192200
struct GraphResponse<T> {
193-
data: T,
201+
data: Option<T>,
194202
#[serde(default)]
195203
errors: Vec<GraphError>,
196204
}
@@ -199,6 +207,7 @@ struct GraphResponse<T> {
199207
struct GraphError {
200208
#[serde(rename = "type")]
201209
error_type: String,
210+
#[serde(default)]
202211
path: Vec<GraphErrorPath>,
203212
message: String,
204213
}
@@ -252,7 +261,29 @@ mod tests {
252261
use mockito::mock;
253262

254263
#[test]
255-
fn test_rate_limit() {
264+
fn test_rate_limit_fail() {
265+
crate::test::wrapper(|env| {
266+
let mut config = env.base_config();
267+
config.github_accesstoken = Some("qsjdnfqdq".to_owned());
268+
let updater = GitHub::new(&config).expect("GitHub::new failed").unwrap();
269+
270+
let _m1 = mock("POST", "/graphql")
271+
.with_header("content-type", "application/json")
272+
.with_body(
273+
r#"{"errors":[{"type":"RATE_LIMITED","message":"API rate limit exceeded"}]}"#,
274+
)
275+
.create();
276+
277+
match updater.fetch_repositories(&[String::new()]) {
278+
Err(e) if format!("{:?}", e).contains("RateLimitReached") => {}
279+
x => panic!("Expected Err(RateLimitReached), found: {:?}", x),
280+
}
281+
Ok(())
282+
});
283+
}
284+
285+
#[test]
286+
fn test_rate_limit_manual() {
256287
crate::test::wrapper(|env| {
257288
let mut config = env.base_config();
258289
config.github_accesstoken = Some("qsjdnfqdq".to_owned());

0 commit comments

Comments
 (0)