Refactor: Extract new fn GhApiClient::do_fetch

Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
Jiahao XU 2024-05-28 00:54:14 +10:00
parent 361aca4d8f
commit 1350ac342f
No known key found for this signature in database
GPG key ID: 76D1E687CA3C4928
3 changed files with 66 additions and 87 deletions

View file

@ -1,5 +1,6 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
future::Future,
ops::Deref, ops::Deref,
sync::{ sync::{
atomic::{AtomicBool, Ordering::Relaxed}, atomic::{AtomicBool, Ordering::Relaxed},
@ -132,17 +133,55 @@ impl GhApiClient {
} }
impl GhApiClient { impl GhApiClient {
async fn do_fetch_release_artifacts( fn check_retry_after(&self) -> Result<(), GhApiError> {
&self, let mut guard = self.0.retry_after.lock().unwrap();
release: &GhRelease,
auth_token: Option<&str>, if let Some(retry_after) = *guard {
) -> Result<Option<release_artifacts::Artifacts>, GhApiError> { if retry_after.elapsed().is_zero() {
match release_artifacts::fetch_release_artifacts(&self.0.client, release, auth_token).await return Err(GhApiError::RateLimit {
{ retry_after: Some(retry_after - Instant::now()),
Ok(artifacts) => Ok(Some(artifacts)), });
Err(GhApiError::NotFound) => Ok(None), } else {
Err(err) => Err(err), // Instant retry_after is already reached.
*guard = None;
}
} }
Ok(())
}
async fn do_fetch<T, U, GraphQLFn, RestfulFn, GraphQLFut, RestfulFut>(
&self,
graphql_func: GraphQLFn,
restful_func: RestfulFn,
data: &T,
) -> Result<U, GhApiError>
where
GraphQLFn: Fn(&remote::Client, &T, &str) -> GraphQLFut,
RestfulFn: Fn(&remote::Client, &T, Option<&str>) -> RestfulFut,
GraphQLFut: Future<Output = Result<U, GhApiError>> + Send + Sync + 'static,
RestfulFut: Future<Output = Result<U, GhApiError>> + 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. /// Return `Ok(Some(api_artifact_url))` if exists.
@ -159,34 +198,18 @@ impl GhApiClient {
let res = once_cell let res = once_cell
.get_or_try_init(|| { .get_or_try_init(|| {
Box::pin(async { 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(); Ok(artifacts) => Ok(Some(artifacts)),
Err(GhApiError::NotFound) => Ok(None),
if let Some(retry_after) = *guard { Err(err) => Err(err),
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;
}
};
} }
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; .await;

View file

@ -11,7 +11,7 @@ use compact_str::{CompactString, ToCompactString};
use serde::Deserialize; use serde::Deserialize;
use super::{ use super::{
common::{issue_graphql_query, issue_restful_api, percent_encode_http_url_path}, common::{issue_graphql_query, issue_restful_api},
GhApiError, GhRelease, GhRepo, 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, client: &remote::Client,
GhRelease { GhRelease {
repo: GhRepo { owner, repo }, 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, client: &remote::Client,
GhRelease { GhRelease {
repo: GhRepo { owner, repo }, 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<Artifacts, GhApiError> {
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"))
}

View file

@ -1,8 +1,8 @@
use compact_str::{CompactString, ToCompactString}; use compact_str::CompactString;
use serde::Deserialize; use serde::Deserialize;
use super::{ use super::{
common::{issue_graphql_query, issue_restful_api, percent_encode_http_url_path}, common::{issue_graphql_query, issue_restful_api},
remote, GhApiError, GhRepo, 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, client: &remote::Client,
GhRepo { owner, repo }: &GhRepo, GhRepo { owner, repo }: &GhRepo,
auth_token: Option<&str>, auth_token: Option<&str>,
@ -44,7 +44,7 @@ struct GraphQLData {
repository: Option<RepoInfo>, repository: Option<RepoInfo>,
} }
async fn fetch_repo_info_graphql_api( pub(super) async fn fetch_repo_info_graphql_api(
client: &remote::Client, client: &remote::Client,
GhRepo { owner, repo }: &GhRepo, GhRepo { owner, repo }: &GhRepo,
auth_token: &str, auth_token: &str,
@ -64,25 +64,3 @@ query {{
issue_graphql_query(client, query, auth_token).await 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<RepoInfo, GhApiError> {
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"))
}