2021-06-30 23:34:34 +00:00
|
|
|
use std::future::Future;
|
|
|
|
|
2024-07-11 23:59:11 +00:00
|
|
|
use memo_map::MemoMap;
|
2024-01-21 03:41:46 +00:00
|
|
|
use reqwest::{Client, Method, RequestBuilder};
|
2023-05-10 18:20:15 +00:00
|
|
|
#[cfg(feature = "tracing")]
|
2021-07-23 22:40:55 +00:00
|
|
|
use tracing as log;
|
|
|
|
|
2023-05-10 18:20:15 +00:00
|
|
|
use crate::req::RegionalRequester;
|
2024-01-21 03:41:46 +00:00
|
|
|
use crate::{ResponseInfo, Result, RiotApiConfig, RiotApiError};
|
2021-06-30 23:34:34 +00:00
|
|
|
|
|
|
|
/// For retrieving data from the Riot Games API.
|
|
|
|
///
|
|
|
|
/// # Usage
|
|
|
|
///
|
2021-09-19 18:22:09 +00:00
|
|
|
/// Construct an instance using [`RiotApi::new(api_key or config)`](RiotApi::new).
|
|
|
|
/// The parameter may be a Riot API key string or a [`RiotApiConfig`]. Riot API
|
|
|
|
/// keys are obtained from the [Riot Developer Portal](https://developer.riotgames.com/)
|
|
|
|
/// and look like `"RGAPI-01234567-89ab-cdef-0123-456789abcdef"`.
|
2021-06-30 23:34:34 +00:00
|
|
|
///
|
2021-09-19 18:22:09 +00:00
|
|
|
/// An instance provides access to "endpoint handles" which in turn provide
|
|
|
|
/// access to individual API method calls. For example, to get a summoner by
|
|
|
|
/// name we first access the [`summoner_v4()`](RiotApi::summoner_v4) endpoints
|
|
|
|
/// then call the [`get_by_summoner_name()`](crate::endpoints::SummonerV4::get_by_summoner_name)
|
|
|
|
/// method:
|
2021-06-30 23:34:34 +00:00
|
|
|
/// ```ignore
|
|
|
|
/// riot_api.summoner_v4().get_by_summoner_name(Region::NA, "LugnutsK")
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// # Rate Limiting
|
|
|
|
///
|
|
|
|
/// The Riot Game API enforces _dynamic_ rate limiting, meaning that rate limits are
|
2021-09-19 18:22:09 +00:00
|
|
|
/// specified in response headers and can change at any time.
|
2021-06-30 23:34:34 +00:00
|
|
|
/// Riven keeps track of changing rate limits seamlessly, preventing you from
|
|
|
|
/// getting blacklisted.
|
|
|
|
///
|
2021-09-19 18:22:09 +00:00
|
|
|
/// Riven's rate limiting is highly efficient; it can use the full throughput
|
|
|
|
/// of your rate limit without triggering 429 errors.
|
2021-06-30 23:34:34 +00:00
|
|
|
///
|
2021-09-19 18:22:09 +00:00
|
|
|
/// To adjust rate limiting, see [RiotApiConfig] and use
|
2021-12-29 19:17:11 +00:00
|
|
|
/// [`RiotApi::new(config)`](RiotApi::new) to construct an instance.
|
2021-06-30 23:34:34 +00:00
|
|
|
pub struct RiotApi {
|
|
|
|
/// Configuration settings.
|
|
|
|
config: RiotApiConfig,
|
|
|
|
/// Client for making requests.
|
|
|
|
client: Client,
|
|
|
|
|
|
|
|
/// Per-region requesters.
|
2024-07-11 23:59:11 +00:00
|
|
|
regional_requesters: MemoMap<&'static str, RegionalRequester>,
|
2021-06-30 23:34:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl RiotApi {
|
2021-09-19 18:22:09 +00:00
|
|
|
/// Constructs a new instance from an API key (e.g. `"RGAPI-01234567-89ab-cdef-0123-456789abcdef"`) or a [RiotApiConfig].
|
|
|
|
pub fn new(config: impl Into<RiotApiConfig>) -> Self {
|
|
|
|
let mut config = config.into();
|
2023-05-10 18:20:15 +00:00
|
|
|
let client_builder = config
|
|
|
|
.client_builder
|
|
|
|
.take()
|
2021-09-19 18:22:09 +00:00
|
|
|
.expect("CLIENT_BUILDER IN CONFIG SHOULD NOT BE NONE.");
|
2021-06-30 23:34:34 +00:00
|
|
|
Self {
|
2021-10-30 05:38:48 +00:00
|
|
|
config,
|
2023-05-10 18:20:15 +00:00
|
|
|
client: client_builder
|
|
|
|
.build()
|
|
|
|
.expect("Failed to create client from builder."),
|
2024-07-11 23:59:11 +00:00
|
|
|
regional_requesters: MemoMap::new(),
|
2021-06-30 23:34:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This method should generally not be used directly. Consider using endpoint wrappers instead.
|
|
|
|
///
|
|
|
|
/// Creates a `RequestBuilder` instance with the given parameters, for use with the `execute*()` methods.
|
|
|
|
///
|
|
|
|
/// # Parameters
|
|
|
|
/// * `method` - The HTTP method for this request.
|
|
|
|
/// * `region_platform` - The stringified platform, used to create the base URL.
|
|
|
|
/// * `path` - The URL path, appended to the base URL.
|
|
|
|
pub fn request(&self, method: Method, region_platform: &str, path: &str) -> RequestBuilder {
|
|
|
|
let base_url_platform = self.config.base_url.replace("{}", region_platform);
|
2023-05-10 18:20:15 +00:00
|
|
|
self.client
|
|
|
|
.request(method, format!("{}{}", base_url_platform, path))
|
2021-06-30 23:34:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// This method should generally not be used directly. Consider using endpoint wrappers instead.
|
|
|
|
///
|
|
|
|
/// This sends a request based on the given parameters and returns a parsed result.
|
|
|
|
///
|
|
|
|
/// # Parameters
|
|
|
|
/// * `method_id` - A unique string id representing the endpoint method for per-method rate limiting.
|
|
|
|
/// * `region_platform` - The stringified platform, used in rate limiting.
|
|
|
|
/// * `request` - The request information. Use `request()` to obtain a `RequestBuilder` instance.
|
|
|
|
///
|
|
|
|
/// # Returns
|
|
|
|
/// A future resolving to a `Result` containg either a `T` (success) or a `RiotApiError` (failure).
|
2023-05-10 18:20:15 +00:00
|
|
|
pub async fn execute_val<'a, T: serde::de::DeserializeOwned + 'a>(
|
|
|
|
&'a self,
|
|
|
|
method_id: &'static str,
|
|
|
|
region_platform: &'static str,
|
|
|
|
request: RequestBuilder,
|
|
|
|
) -> Result<T> {
|
|
|
|
let rinfo = self
|
|
|
|
.execute_raw(method_id, region_platform, request)
|
|
|
|
.await?;
|
2021-06-30 23:34:34 +00:00
|
|
|
let retries = rinfo.retries;
|
|
|
|
let status = rinfo.response.status();
|
|
|
|
let value = rinfo.response.json::<T>().await;
|
|
|
|
value.map_err(|e| RiotApiError::new(e, retries, None, Some(status)))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This method should generally not be used directly. Consider using endpoint wrappers instead.
|
|
|
|
///
|
|
|
|
/// This sends a request based on the given parameters and returns an optional parsed result.
|
|
|
|
///
|
|
|
|
/// # Parameters
|
|
|
|
/// * `method_id` - A unique string id representing the endpoint method for per-method rate limiting.
|
|
|
|
/// * `region_platform` - The stringified platform, used in rate limiting.
|
|
|
|
/// * `request` - The request information. Use `request()` to obtain a `RequestBuilder` instance.
|
|
|
|
///
|
|
|
|
/// # Returns
|
2021-09-09 21:31:39 +00:00
|
|
|
/// A future resolving to a `Result` containg either an `Option<T>` (success) or a `RiotApiError` (failure).
|
2023-05-10 18:20:15 +00:00
|
|
|
pub async fn execute_opt<'a, T: serde::de::DeserializeOwned + 'a>(
|
|
|
|
&'a self,
|
|
|
|
method_id: &'static str,
|
|
|
|
region_platform: &'static str,
|
|
|
|
request: RequestBuilder,
|
|
|
|
) -> Result<Option<T>> {
|
|
|
|
let rinfo = self
|
|
|
|
.execute_raw(method_id, region_platform, request)
|
|
|
|
.await?;
|
2021-06-30 23:34:34 +00:00
|
|
|
if rinfo.status_none {
|
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
let retries = rinfo.retries;
|
|
|
|
let status = rinfo.response.status();
|
|
|
|
let value = rinfo.response.json::<Option<T>>().await;
|
|
|
|
value.map_err(|e| RiotApiError::new(e, retries, None, Some(status)))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This method should generally not be used directly. Consider using endpoint wrappers instead.
|
|
|
|
///
|
|
|
|
/// This sends a request based on the given parameters but does not deserialize any response body.
|
|
|
|
///
|
|
|
|
/// # Parameters
|
|
|
|
/// * `method_id` - A unique string id representing the endpoint method for per-method rate limiting.
|
|
|
|
/// * `region_platform` - The stringified platform, used in rate limiting.
|
|
|
|
/// * `request` - The request information. Use `request()` to obtain a `RequestBuilder` instance.
|
|
|
|
///
|
|
|
|
/// # Returns
|
|
|
|
/// A future resolving to a `Result` containg either `()` (success) or a `RiotApiError` (failure).
|
2023-05-10 18:20:15 +00:00
|
|
|
pub async fn execute(
|
|
|
|
&self,
|
|
|
|
method_id: &'static str,
|
|
|
|
region_platform: &'static str,
|
|
|
|
request: RequestBuilder,
|
|
|
|
) -> Result<()> {
|
|
|
|
let rinfo = self
|
|
|
|
.execute_raw(method_id, region_platform, request)
|
|
|
|
.await?;
|
2021-06-30 23:34:34 +00:00
|
|
|
let retries = rinfo.retries;
|
|
|
|
let status = rinfo.response.status();
|
2023-05-10 18:20:15 +00:00
|
|
|
rinfo
|
|
|
|
.response
|
|
|
|
.error_for_status()
|
2021-06-30 23:34:34 +00:00
|
|
|
.map(|_| ())
|
|
|
|
.map_err(|e| RiotApiError::new(e, retries, None, Some(status)))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This method should generally not be used directly. Consider using endpoint wrappers instead.
|
|
|
|
///
|
|
|
|
/// This sends a request based on the given parameters and returns a raw `ResponseInfo`.
|
|
|
|
///
|
|
|
|
/// This can be used to implement a Riot API proxy without needing to deserialize and reserialize JSON responses.
|
|
|
|
///
|
|
|
|
/// # Parameters
|
|
|
|
/// * `method_id` - A unique string id representing the endpoint method for per-method rate limiting.
|
|
|
|
/// * `region_platform` - The stringified platform, used in rate limiting.
|
|
|
|
/// * `request` - The request information. Use `request()` to obtain a `RequestBuilder` instance.
|
|
|
|
///
|
|
|
|
/// # Returns
|
|
|
|
/// A future resolving to a `Result` containg either a `ResponseInfo` (success) or a `RiotApiError` (failure).
|
2023-05-10 18:20:15 +00:00
|
|
|
pub fn execute_raw(
|
|
|
|
&self,
|
|
|
|
method_id: &'static str,
|
|
|
|
region_platform: &'static str,
|
|
|
|
request: RequestBuilder,
|
|
|
|
) -> impl Future<Output = Result<ResponseInfo>> + '_ {
|
2021-06-30 23:34:34 +00:00
|
|
|
self.regional_requester(region_platform)
|
|
|
|
.execute(&self.config, method_id, request)
|
|
|
|
}
|
|
|
|
|
2024-05-06 06:47:40 +00:00
|
|
|
/// Gets the [`RiotApiConfig::rso_clear_header`] for use in RSO endpoints.
|
|
|
|
pub(crate) fn get_rso_clear_header(&self) -> Option<&str> {
|
|
|
|
self.config.rso_clear_header.as_deref()
|
|
|
|
}
|
|
|
|
|
2021-06-30 23:34:34 +00:00
|
|
|
/// Get or create the RegionalRequester for the given region.
|
2024-07-11 23:59:11 +00:00
|
|
|
fn regional_requester(&self, region_platform: &'static str) -> &RegionalRequester {
|
2023-05-10 18:20:15 +00:00
|
|
|
self.regional_requesters
|
2024-07-11 23:59:11 +00:00
|
|
|
.get_or_insert(®ion_platform, || {
|
2023-05-10 18:20:15 +00:00
|
|
|
log::debug!(
|
|
|
|
"Creating requester for region platform {}.",
|
|
|
|
region_platform
|
|
|
|
);
|
|
|
|
RegionalRequester::new()
|
|
|
|
})
|
2021-06-30 23:34:34 +00:00
|
|
|
}
|
|
|
|
}
|