Add retry logic to API calls
Since apparently these can 500 occasionally.
This commit is contained in:
parent
81f12da99c
commit
19126f4981
@ -5,6 +5,7 @@ use serde_json::Value;
|
|||||||
use std::{env, time::Duration};
|
use std::{env, time::Duration};
|
||||||
use tempfile::tempfile;
|
use tempfile::tempfile;
|
||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
|
use tokio::time::sleep;
|
||||||
use tokio_util::compat::FuturesAsyncReadCompatExt;
|
use tokio_util::compat::FuturesAsyncReadCompatExt;
|
||||||
use tracing::{info, instrument, warn};
|
use tracing::{info, instrument, warn};
|
||||||
|
|
||||||
@ -17,23 +18,36 @@ pub struct DownloadLinkResponse {
|
|||||||
|
|
||||||
#[instrument(skip(client))]
|
#[instrument(skip(client))]
|
||||||
pub async fn get(client: &Client, mod_id: i32, file_id: i64) -> Result<DownloadLinkResponse> {
|
pub async fn get(client: &Client, mod_id: i32, file_id: i64) -> Result<DownloadLinkResponse> {
|
||||||
let res = client
|
for attempt in 1..=3 {
|
||||||
.get(format!(
|
let res = match client
|
||||||
"https://api.nexusmods.com/v1/games/{}/mods/{}/files/{}/download_link.json",
|
.get(format!(
|
||||||
GAME_NAME, mod_id, file_id
|
"https://api.nexusmods.com/v1/games/{}/mods/{}/files/{}/download_link.json",
|
||||||
))
|
GAME_NAME, mod_id, file_id
|
||||||
.header("accept", "application/json")
|
))
|
||||||
.header("apikey", env::var("NEXUS_API_KEY")?)
|
.header("accept", "application/json")
|
||||||
.header("user-agent", USER_AGENT)
|
.header("apikey", env::var("NEXUS_API_KEY")?)
|
||||||
.send()
|
.header("user-agent", USER_AGENT)
|
||||||
.await?
|
.send()
|
||||||
.error_for_status()?;
|
.await?
|
||||||
|
.error_for_status()
|
||||||
|
{
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(err) => {
|
||||||
|
warn!(error = %err, attempt, "Failed to get download link for file, trying again after 1 second");
|
||||||
|
sleep(std::time::Duration::from_secs(1)).await;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
info!(status = %res.status(), "fetched file download link from API");
|
info!(status = %res.status(), "fetched file download link from API");
|
||||||
let wait = rate_limit_wait_duration(&res)?;
|
let wait = rate_limit_wait_duration(&res)?;
|
||||||
let json = res.json::<Value>().await?;
|
let json = res.json::<Value>().await?;
|
||||||
|
|
||||||
Ok(DownloadLinkResponse { wait, json })
|
return Ok(DownloadLinkResponse { wait, json });
|
||||||
|
}
|
||||||
|
Err(anyhow!(
|
||||||
|
"Failed to get download link for file in three attempts"
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DownloadLinkResponse {
|
impl DownloadLinkResponse {
|
||||||
@ -76,7 +90,8 @@ impl DownloadLinkResponse {
|
|||||||
return Ok(tokio_file);
|
return Ok(tokio_file);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!(error = %err, attempt, "Failed to download file, trying again");
|
warn!(error = %err, attempt, "Failed to download file, trying again after 1 second");
|
||||||
|
sleep(std::time::Duration::from_secs(1)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,8 @@ use chrono::NaiveDateTime;
|
|||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::{env, time::Duration};
|
use std::{env, time::Duration};
|
||||||
use tracing::{info, instrument};
|
use tokio::time::sleep;
|
||||||
|
use tracing::{info, instrument, warn};
|
||||||
|
|
||||||
use super::{rate_limit_wait_duration, GAME_NAME, USER_AGENT};
|
use super::{rate_limit_wait_duration, GAME_NAME, USER_AGENT};
|
||||||
|
|
||||||
@ -26,23 +27,34 @@ pub struct ApiFile<'a> {
|
|||||||
|
|
||||||
#[instrument(skip(client))]
|
#[instrument(skip(client))]
|
||||||
pub async fn get(client: &Client, nexus_mod_id: i32) -> Result<FilesResponse> {
|
pub async fn get(client: &Client, nexus_mod_id: i32) -> Result<FilesResponse> {
|
||||||
let res = client
|
for attempt in 1..=3 {
|
||||||
.get(format!(
|
let res = match client
|
||||||
"https://api.nexusmods.com/v1/games/{}/mods/{}/files.json",
|
.get(format!(
|
||||||
GAME_NAME, nexus_mod_id
|
"https://api.nexusmods.com/v1/games/{}/mods/{}/files.json",
|
||||||
))
|
GAME_NAME, nexus_mod_id
|
||||||
.header("accept", "application/json")
|
))
|
||||||
.header("apikey", env::var("NEXUS_API_KEY")?)
|
.header("accept", "application/json")
|
||||||
.header("user-agent", USER_AGENT)
|
.header("apikey", env::var("NEXUS_API_KEY")?)
|
||||||
.send()
|
.header("user-agent", USER_AGENT)
|
||||||
.await?
|
.send()
|
||||||
.error_for_status()?;
|
.await?
|
||||||
|
.error_for_status()
|
||||||
|
{
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(err) => {
|
||||||
|
warn!(error = %err, attempt, "Failed to get files for mod, trying again after 1 second");
|
||||||
|
sleep(std::time::Duration::from_secs(1)).await;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
info!(status = %res.status(), "fetched files for mod from API");
|
info!(status = %res.status(), "fetched files for mod from API");
|
||||||
let wait = rate_limit_wait_duration(&res)?;
|
let wait = rate_limit_wait_duration(&res)?;
|
||||||
let json = res.json::<Value>().await?;
|
let json = res.json::<Value>().await?;
|
||||||
|
|
||||||
Ok(FilesResponse { wait, json })
|
return Ok(FilesResponse { wait, json });
|
||||||
|
}
|
||||||
|
Err(anyhow!("Failed to get files for mod in three attempts"))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FilesResponse {
|
impl FilesResponse {
|
||||||
|
Loading…
Reference in New Issue
Block a user