Skip to content

Commit 1961aac

Browse files
committed
github repo updater: support new secondary rate limits
1 parent 3fbfc9c commit 1961aac

1 file changed

Lines changed: 65 additions & 9 deletions

File tree

  • crates/lib/docs_rs_repository_stats/src

crates/lib/docs_rs_repository_stats/src/github.rs

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ use crate::{
33
config::Config,
44
updater::{FetchRepositoriesResult, Repository, RepositoryForge, RepositoryName},
55
};
6-
use anyhow::{Result, bail};
6+
use anyhow::{Result, anyhow, bail};
77
use async_trait::async_trait;
88
use chrono::{DateTime, Utc};
99
use docs_rs_utils::APP_USER_AGENT;
1010
use reqwest::{
11-
Client as HttpClient,
11+
Client as HttpClient, StatusCode,
1212
header::{ACCEPT, AUTHORIZATION, HeaderMap, HeaderValue, USER_AGENT},
1313
};
14-
use serde::Deserialize;
14+
use serde::{Deserialize, Serialize};
1515
use tracing::{trace, warn};
1616

1717
const GRAPHQL_UPDATE: &str = "query($ids: [ID!]!) {
@@ -155,7 +155,7 @@ impl RepositoryForge for GitHub {
155155
("RATE_LIMITED", []) => {
156156
return Err(RateLimitReached.into());
157157
}
158-
_ => anyhow::bail!("error updating repositories: {}", error.message),
158+
_ => bail!("error updating repositories: {}", error.message),
159159
}
160160
}
161161

@@ -198,12 +198,24 @@ impl GitHub {
198198
.await?;
199199

200200
let status = response.status();
201-
202-
if status.is_client_error() || status.is_server_error() {
203-
let body = response.text().await?;
204-
bail!("GitHub GraphQL response status: {}\n{}", status, body);
201+
let body = response.text().await?;
202+
203+
if status == StatusCode::FORBIDDEN
204+
&& let Ok(api_error) = serde_json::from_str::<ApiError>(&body)
205+
&& (api_error
206+
.documentation_url
207+
.contains("secondary-rate-limits")
208+
|| api_error.message.contains("secondary rate limit"))
209+
{
210+
Err(RateLimitReached.into())
211+
} else if status.is_client_error() || status.is_server_error() {
212+
Err(anyhow!(
213+
"GitHub GraphQL response status: {}\n{}",
214+
status,
215+
body
216+
))
205217
} else {
206-
Ok(response.json().await?)
218+
Ok(serde_json::from_str(&body)?)
207219
}
208220
}
209221
}
@@ -266,10 +278,17 @@ struct GraphIssues {
266278
total_count: i64,
267279
}
268280

281+
#[derive(Debug, Serialize, Deserialize)]
282+
struct ApiError {
283+
documentation_url: String,
284+
message: String,
285+
}
286+
269287
#[cfg(test)]
270288
mod tests {
271289
use crate::{
272290
Config, GitHub, RateLimitReached,
291+
github::ApiError,
273292
updater::{RepositoryForge, repository_name},
274293
};
275294
use anyhow::Result;
@@ -417,4 +436,41 @@ mod tests {
417436

418437
Ok(())
419438
}
439+
440+
#[tokio::test]
441+
async fn test_secondary_rate_limit() -> Result<()> {
442+
let config = github_config()?;
443+
let (mut server, updater) = mock_server_and_github(&config).await;
444+
445+
let _m1 = server
446+
.mock("POST", "/graphql")
447+
.with_header("content-type", "application/json")
448+
.with_status(403)
449+
.with_body(&serde_json::to_string(&ApiError {
450+
documentation_url: "https://docs.github.com/graphql/overview/\
451+
rate-limits-and-node-limits-for-the-graphql-api#secondary-rate-limits"
452+
.into(),
453+
message: "You have exceeded a secondary rate limit.
454+
Please wait a few minutes before you try again.
455+
For more on scraping GitHub and how it may affect your rights,
456+
please review our Terms of Service
457+
(https://docs.github.com/en/site-policy/github-terms/github-terms-of-service)
458+
If you reach out to GitHub Support for help, please include the request ID
459+
ECEE:193CF9:5A5D684:1866A8EB:698779A9."
460+
.into(),
461+
})?)
462+
.create();
463+
464+
assert!(
465+
updater
466+
.fetch_repository(
467+
&repository_name("https://gitlab.com/foo/bar").expect("repository_name failed"),
468+
)
469+
.await
470+
.unwrap_err()
471+
.is::<RateLimitReached>()
472+
);
473+
474+
Ok(())
475+
}
420476
}

0 commit comments

Comments
 (0)