You've already forked AstralRinth
Serve stale Tiltify data if API is not available (#6427)
This commit is contained in:
@@ -60,15 +60,24 @@ struct TiltifyMeta {
|
||||
subscription_source_type: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct CampaignInfo {
|
||||
total_donations_usd: Decimal,
|
||||
target_usd: Decimal,
|
||||
num_donators: usize,
|
||||
cached_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
const CAMPAIGN_INFO_CACHE_NAMESPACE: &str = "campaign_info";
|
||||
const CAMPAIGN_INFO_CACHE_TTL_SECONDS: i64 = 15 * 60;
|
||||
const CAMPAIGN_INFO_CACHE_STALE_SECONDS: i64 = 15 * 60;
|
||||
const CAMPAIGN_INFO_CACHE_TTL_SECONDS: i64 = 24 * 60 * 60;
|
||||
|
||||
impl CampaignInfo {
|
||||
fn is_stale(&self) -> bool {
|
||||
Utc::now().signed_duration_since(self.cached_at)
|
||||
>= Duration::seconds(CAMPAIGN_INFO_CACHE_STALE_SECONDS)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct TiltifyCampaignResponse {
|
||||
@@ -305,66 +314,89 @@ pub async fn pride_26(
|
||||
.await
|
||||
.wrap_internal_err("connecting to redis")?;
|
||||
|
||||
if let Some(cached) = redis_connection
|
||||
.get(CAMPAIGN_INFO_CACHE_NAMESPACE, campaign_id)
|
||||
.await
|
||||
.wrap_internal_err("getting cached campaign info")?
|
||||
{
|
||||
let campaign_info = serde_json::from_str::<CampaignInfo>(&cached)
|
||||
.wrap_internal_err("parsing cached campaign info")?;
|
||||
return Ok(web::Json(campaign_info));
|
||||
}
|
||||
|
||||
let access_token = tiltify
|
||||
.access_token()
|
||||
.await
|
||||
.wrap_internal_err("fetching Tiltify access token")?;
|
||||
let url = format!(
|
||||
"https://v5api.tiltify.com/api/public/team_campaigns/{campaign_id}",
|
||||
);
|
||||
let response = http
|
||||
.get(url)
|
||||
.bearer_auth(&access_token)
|
||||
.send()
|
||||
.await
|
||||
.wrap_internal_err("fetching campaign from Tiltify")?
|
||||
.error_for_status()
|
||||
.wrap_internal_err("fetching campaign from Tiltify")?
|
||||
.json::<TiltifyCampaignResponse>()
|
||||
.await
|
||||
.wrap_internal_err("parsing Tiltify response")?;
|
||||
|
||||
let raised_currency = &response.data.total_amount_raised.currency;
|
||||
if raised_currency != "USD" {
|
||||
return Err(ApiError::Internal(eyre!(
|
||||
"total amount raised is in {raised_currency}, must be USD"
|
||||
)));
|
||||
}
|
||||
|
||||
let goal_currency = &response.data.goal.currency;
|
||||
if goal_currency != "USD" {
|
||||
return Err(ApiError::Internal(eyre!(
|
||||
"goal amount is in {goal_currency}, must be USD"
|
||||
)));
|
||||
}
|
||||
|
||||
let campaign_info = CampaignInfo {
|
||||
total_donations_usd: response.data.total_amount_raised.value,
|
||||
target_usd: response.data.goal.value,
|
||||
num_donators: num_donators(&http, &access_token, campaign_id).await?,
|
||||
};
|
||||
|
||||
redis_connection
|
||||
.set_serialized_to_json(
|
||||
let cached = redis_connection
|
||||
.get_deserialized_from_json::<CampaignInfo>(
|
||||
CAMPAIGN_INFO_CACHE_NAMESPACE,
|
||||
campaign_id,
|
||||
&campaign_info,
|
||||
Some(CAMPAIGN_INFO_CACHE_TTL_SECONDS),
|
||||
)
|
||||
.await
|
||||
.wrap_internal_err("caching campaign info")?;
|
||||
.wrap_internal_err("getting cached campaign info")?;
|
||||
|
||||
Ok(web::Json(campaign_info))
|
||||
if let Some(cached) = &cached
|
||||
&& !cached.is_stale()
|
||||
{
|
||||
return Ok(web::Json(cached.clone()));
|
||||
}
|
||||
|
||||
let result = async {
|
||||
let access_token = tiltify
|
||||
.access_token()
|
||||
.await
|
||||
.wrap_internal_err("fetching Tiltify access token")?;
|
||||
let url = format!(
|
||||
"https://v5api.tiltify.com/api/public/team_campaigns/{campaign_id}",
|
||||
);
|
||||
let response = http
|
||||
.get(url)
|
||||
.bearer_auth(&access_token)
|
||||
.send()
|
||||
.await
|
||||
.wrap_internal_err("fetching campaign from Tiltify")?
|
||||
.error_for_status()
|
||||
.wrap_internal_err("fetching campaign from Tiltify")?
|
||||
.json::<TiltifyCampaignResponse>()
|
||||
.await
|
||||
.wrap_internal_err("parsing Tiltify response")?;
|
||||
|
||||
let raised_currency = &response.data.total_amount_raised.currency;
|
||||
if raised_currency != "USD" {
|
||||
return Err(ApiError::Internal(eyre!(
|
||||
"total amount raised is in {raised_currency}, must be USD"
|
||||
)));
|
||||
}
|
||||
|
||||
let goal_currency = &response.data.goal.currency;
|
||||
if goal_currency != "USD" {
|
||||
return Err(ApiError::Internal(eyre!(
|
||||
"goal amount is in {goal_currency}, must be USD"
|
||||
)));
|
||||
}
|
||||
|
||||
let campaign_info = CampaignInfo {
|
||||
total_donations_usd: response.data.total_amount_raised.value,
|
||||
target_usd: response.data.goal.value,
|
||||
num_donators: num_donators(&http, &access_token, campaign_id)
|
||||
.await?,
|
||||
cached_at: Utc::now(),
|
||||
};
|
||||
|
||||
redis_connection
|
||||
.set_serialized_to_json(
|
||||
CAMPAIGN_INFO_CACHE_NAMESPACE,
|
||||
campaign_id,
|
||||
&campaign_info,
|
||||
Some(CAMPAIGN_INFO_CACHE_TTL_SECONDS),
|
||||
)
|
||||
.await
|
||||
.wrap_internal_err("caching campaign info")?;
|
||||
|
||||
Ok(campaign_info)
|
||||
}
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(campaign_info) => Ok(web::Json(campaign_info)),
|
||||
Err(error) => {
|
||||
if let Some(cached) = cached {
|
||||
warn!(
|
||||
"Failed to refresh campaign info from Tiltify: {error:?}"
|
||||
);
|
||||
Ok(web::Json(cached))
|
||||
} else {
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn num_donators(
|
||||
|
||||
Reference in New Issue
Block a user