diff --git a/crates/binstalk-git-repo-api/src/gh_api_client.rs b/crates/binstalk-git-repo-api/src/gh_api_client.rs index 30d7d1d3..08d75597 100644 --- a/crates/binstalk-git-repo-api/src/gh_api_client.rs +++ b/crates/binstalk-git-repo-api/src/gh_api_client.rs @@ -1,5 +1,6 @@ use std::{ collections::HashMap, + future::Future, ops::Deref, sync::{ atomic::{AtomicBool, Ordering::Relaxed}, @@ -132,17 +133,55 @@ impl GhApiClient { } impl GhApiClient { - async fn do_fetch_release_artifacts( - &self, - release: &GhRelease, - auth_token: Option<&str>, - ) -> Result, GhApiError> { - match release_artifacts::fetch_release_artifacts(&self.0.client, release, auth_token).await - { - Ok(artifacts) => Ok(Some(artifacts)), - Err(GhApiError::NotFound) => Ok(None), - Err(err) => Err(err), + fn check_retry_after(&self) -> Result<(), GhApiError> { + let mut guard = self.0.retry_after.lock().unwrap(); + + if let Some(retry_after) = *guard { + if retry_after.elapsed().is_zero() { + return Err(GhApiError::RateLimit { + retry_after: Some(retry_after - Instant::now()), + }); + } else { + // Instant retry_after is already reached. + *guard = None; + } } + + Ok(()) + } + async fn do_fetch( + &self, + graphql_func: GraphQLFn, + restful_func: RestfulFn, + data: &T, + ) -> Result + where + GraphQLFn: Fn(&remote::Client, &T, &str) -> GraphQLFut, + RestfulFn: Fn(&remote::Client, &T, Option<&str>) -> RestfulFut, + GraphQLFut: Future> + Send + Sync + 'static, + RestfulFut: Future> + Send + Sync + 'static, + { + self.check_retry_after()?; + + if self.0.is_auth_token_valid.load(Relaxed) { + if let Some(auth_token) = self.0.auth_token.as_deref() { + match graphql_func(&self.0.client, data, auth_token).await { + Err(GhApiError::Unauthorized) => { + self.0.is_auth_token_valid.store(false, Relaxed); + } + res => return res.map_err(|err| err.context("GraphQL API")), + } + } + } + + match restful_func(&self.0.client, data, self.0.auth_token.as_deref()).await { + Err(GhApiError::Unauthorized) => { + self.0.is_auth_token_valid.store(false, Relaxed); + restful_func(&self.0.client, data, None).await + } + res => res, + } + .map_err(|err| err.context("Restful API")) } /// Return `Ok(Some(api_artifact_url))` if exists. @@ -159,34 +198,18 @@ impl GhApiClient { let res = once_cell .get_or_try_init(|| { Box::pin(async { + match self + .do_fetch( + release_artifacts::fetch_release_artifacts_graphql_api, + release_artifacts::fetch_release_artifacts_restful_api, + &release, + ) + .await { - let mut guard = self.0.retry_after.lock().unwrap(); - - if let Some(retry_after) = *guard { - if retry_after.elapsed().is_zero() { - return Err(GhApiError::RateLimit { - retry_after: Some(retry_after - Instant::now()), - }); - } else { - // Instant retry_after is already reached. - *guard = None; - } - }; + Ok(artifacts) => Ok(Some(artifacts)), + Err(GhApiError::NotFound) => Ok(None), + Err(err) => Err(err), } - - if self.0.is_auth_token_valid.load(Relaxed) { - match self - .do_fetch_release_artifacts(&release, self.0.auth_token.as_deref()) - .await - { - Err(GhApiError::Unauthorized) => { - self.0.is_auth_token_valid.store(false, Relaxed); - } - res => return res, - } - } - - self.do_fetch_release_artifacts(&release, None).await }) }) .await; diff --git a/crates/binstalk-git-repo-api/src/gh_api_client/release_artifacts.rs b/crates/binstalk-git-repo-api/src/gh_api_client/release_artifacts.rs index 718f4c4e..5855f57c 100644 --- a/crates/binstalk-git-repo-api/src/gh_api_client/release_artifacts.rs +++ b/crates/binstalk-git-repo-api/src/gh_api_client/release_artifacts.rs @@ -11,7 +11,7 @@ use compact_str::{CompactString, ToCompactString}; use serde::Deserialize; use super::{ - common::{issue_graphql_query, issue_restful_api, percent_encode_http_url_path}, + common::{issue_graphql_query, issue_restful_api}, GhApiError, GhRelease, GhRepo, }; @@ -66,7 +66,7 @@ impl Artifacts { } } -fn fetch_release_artifacts_restful_api( +pub(super) fn fetch_release_artifacts_restful_api( client: &remote::Client, GhRelease { repo: GhRepo { owner, repo }, @@ -130,7 +130,7 @@ impl fmt::Display for FilterCondition { } } -fn fetch_release_artifacts_graphql_api( +pub(super) fn fetch_release_artifacts_graphql_api( client: &remote::Client, GhRelease { repo: GhRepo { owner, repo }, @@ -192,25 +192,3 @@ releaseAssets({cond}) {{ } } } - -pub(super) async fn fetch_release_artifacts( - client: &remote::Client, - release: &GhRelease, - auth_token: Option<&str>, -) -> Result { - if let Some(auth_token) = auth_token { - let res = fetch_release_artifacts_graphql_api(client, release, auth_token) - .await - .map_err(|err| err.context("GraphQL API")); - - match res { - // Fallback to Restful API - Err(GhApiError::Unauthorized) => (), - res => return res, - } - } - - fetch_release_artifacts_restful_api(client, release, auth_token) - .await - .map_err(|err| err.context("Restful API")) -} diff --git a/crates/binstalk-git-repo-api/src/gh_api_client/repo_info.rs b/crates/binstalk-git-repo-api/src/gh_api_client/repo_info.rs index c2666738..51c00bc7 100644 --- a/crates/binstalk-git-repo-api/src/gh_api_client/repo_info.rs +++ b/crates/binstalk-git-repo-api/src/gh_api_client/repo_info.rs @@ -1,8 +1,8 @@ -use compact_str::{CompactString, ToCompactString}; +use compact_str::CompactString; use serde::Deserialize; use super::{ - common::{issue_graphql_query, issue_restful_api, percent_encode_http_url_path}, + common::{issue_graphql_query, issue_restful_api}, remote, GhApiError, GhRepo, }; @@ -31,7 +31,7 @@ impl RepoInfo { } } -async fn fetch_repo_info_restful_api( +pub(super) async fn fetch_repo_info_restful_api( client: &remote::Client, GhRepo { owner, repo }: &GhRepo, auth_token: Option<&str>, @@ -44,7 +44,7 @@ struct GraphQLData { repository: Option, } -async fn fetch_repo_info_graphql_api( +pub(super) async fn fetch_repo_info_graphql_api( client: &remote::Client, GhRepo { owner, repo }: &GhRepo, auth_token: &str, @@ -64,25 +64,3 @@ query {{ issue_graphql_query(client, query, auth_token).await } - -pub(super) async fn fetch_repo_info( - client: &remote::Client, - repo: &GhRepo, - auth_token: Option<&str>, -) -> Result { - if let Some(auth_token) = auth_token { - let res = fetch_repo_info_graphql_api(client, repo, auth_token) - .await - .map_err(|err| err.context("GraphQL API")); - - match res { - // Fallback to Restful API - Err(GhApiError::Unauthorized) => (), - res => return res, - } - } - - fetch_repo_info_restful_api(client, repo, auth_token) - .await - .map_err(|err| err.context("Restful API")) -}