2019-10-21 21:45:38 -07:00
|
|
|
use std::future::Future;
|
2019-11-07 16:06:01 -08:00
|
|
|
use std::sync::Arc;
|
2019-10-21 21:45:38 -07:00
|
|
|
|
2019-10-22 19:33:15 -07:00
|
|
|
use log;
|
2021-05-21 17:38:30 -07:00
|
|
|
use reqwest::{ Client, RequestBuilder, Method };
|
2019-10-21 21:45:38 -07:00
|
|
|
|
2019-10-23 00:39:40 -07:00
|
|
|
use crate::Result;
|
2020-06-04 21:52:43 -07:00
|
|
|
use crate::ResponseInfo;
|
2019-10-20 00:54:01 -07:00
|
|
|
use crate::RiotApiConfig;
|
2020-06-04 21:52:43 -07:00
|
|
|
use crate::RiotApiError;
|
2019-10-21 21:45:38 -07:00
|
|
|
use crate::req::RegionalRequester;
|
|
|
|
use crate::util::InsertOnlyCHashMap;
|
2019-10-17 16:00:04 -07:00
|
|
|
|
2019-10-23 00:39:40 -07:00
|
|
|
/// For retrieving data from the Riot Games API.
|
2019-11-02 20:55:07 -07:00
|
|
|
///
|
2020-02-01 13:35:37 -08:00
|
|
|
/// # Usage
|
|
|
|
///
|
|
|
|
/// Construct an instance using [`with_key(api_key)`](RiotApi::with_key) or
|
|
|
|
/// [`with_config(config)`](RiotApi::with_config).
|
|
|
|
///
|
|
|
|
/// An instance provides access to "endpoint handles" which in turn provide access
|
|
|
|
/// to individual API method calls. For example, getting a summoner by name:
|
|
|
|
/// ```ignore
|
|
|
|
/// riot_api.summoner_v4().get_by_summoner_name(Region::NA, "LugnutsK")
|
|
|
|
/// ```
|
|
|
|
///
|
2019-11-02 20:55:07 -07:00
|
|
|
/// # Rate Limiting
|
|
|
|
///
|
2019-11-07 16:06:01 -08:00
|
|
|
/// The Riot Game API enforces _dynamic_ rate limiting, meaning that rate limits are
|
2020-02-01 13:35:37 -08:00
|
|
|
/// specified in response headers and (theoretically) could change at any time.
|
2019-11-02 20:55:07 -07:00
|
|
|
/// Riven keeps track of changing rate limits seamlessly, preventing you from
|
|
|
|
/// getting blacklisted.
|
|
|
|
///
|
|
|
|
/// Riven's rate limiting is highly efficient, meaning that it can reach the limits
|
|
|
|
/// of your rate limit without going over.
|
2020-02-01 13:35:37 -08:00
|
|
|
///
|
|
|
|
/// To adjust rate limiting, see [RiotApiConfig](crate::RiotApiConfig) and use
|
|
|
|
/// [`with_config(config)`](RiotApi::with_config) to construct an instance.
|
2019-10-21 21:45:38 -07:00
|
|
|
pub struct RiotApi {
|
|
|
|
/// Configuration settings.
|
|
|
|
config: RiotApiConfig,
|
|
|
|
/// Client for making requests.
|
|
|
|
client: Client,
|
|
|
|
|
|
|
|
/// Per-region requesters.
|
2019-11-04 17:00:59 -08:00
|
|
|
regional_requesters: InsertOnlyCHashMap<&'static str, RegionalRequester>,
|
2019-10-17 16:00:04 -07:00
|
|
|
}
|
|
|
|
|
2019-10-21 21:45:38 -07:00
|
|
|
impl RiotApi {
|
2020-02-01 13:35:37 -08:00
|
|
|
/// Constructs a new instance from the given [RiotApiConfig](crate::RiotApiConfig), consuming it.
|
2019-11-02 22:42:14 -07:00
|
|
|
pub fn with_config(mut config: RiotApiConfig) -> Self {
|
|
|
|
let client_builder = config.client_builder.take()
|
|
|
|
.expect("!NONE CLIENT_BUILDER IN CONFIG.");
|
2019-10-17 16:00:04 -07:00
|
|
|
Self {
|
2019-10-21 21:45:38 -07:00
|
|
|
config: config,
|
2019-11-02 22:42:14 -07:00
|
|
|
client: client_builder.build().expect("Failed to create client from builder."),
|
2019-10-21 21:45:38 -07:00
|
|
|
regional_requesters: InsertOnlyCHashMap::new(),
|
2019-10-17 16:00:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-01 13:35:37 -08:00
|
|
|
/// Constructs a new instance from the given API key, using default configuration.
|
|
|
|
///
|
|
|
|
/// `api_key` should be a Riot Games API key from
|
|
|
|
/// [https://developer.riotgames.com/](https://developer.riotgames.com/),
|
|
|
|
/// and should look like `"RGAPI-01234567-89ab-cdef-0123-456789abcdef"`.
|
2021-05-21 17:31:52 -07:00
|
|
|
pub fn with_key(api_key: impl AsRef<[u8]>) -> Self {
|
2019-10-17 16:00:04 -07:00
|
|
|
Self::with_config(RiotApiConfig::with_key(api_key))
|
|
|
|
}
|
2019-10-21 21:45:38 -07:00
|
|
|
|
2021-05-21 17:38:30 -07:00
|
|
|
/// This method should generally not be used directly. Consider using endpoint wrappers instead.
|
2020-02-01 13:35:37 -08:00
|
|
|
///
|
2021-05-21 17:38:30 -07:00
|
|
|
/// 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);
|
|
|
|
self.client.request(method, format!("{}/{}", base_url_platform, path))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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.
|
2020-02-01 13:35:37 -08:00
|
|
|
///
|
|
|
|
/// # Parameters
|
|
|
|
/// * `method_id` - A unique string id representing the endpoint method for per-method rate limiting.
|
2021-05-21 17:38:30 -07:00
|
|
|
/// * `region_platform` - The stringified platform, used in rate limiting.
|
|
|
|
/// * `request` - The request information. Use `request()` to obtain a `RequestBuilder` instance.
|
2020-06-04 21:52:43 -07:00
|
|
|
///
|
|
|
|
/// # Returns
|
|
|
|
/// A future resolving to a `Result` containg either a `Option<T>` (success) or a `RiotApiError` (failure).
|
2021-05-21 17:38:30 -07:00
|
|
|
pub async fn execute_optional<'a, T: serde::de::DeserializeOwned + 'a>(&'a self,
|
|
|
|
method_id: &'static str, region_platform: &'static str, request: RequestBuilder)
|
2020-06-04 21:52:43 -07:00
|
|
|
-> Result<Option<T>>
|
2019-10-21 21:45:38 -07:00
|
|
|
{
|
2021-05-21 17:38:30 -07:00
|
|
|
let rinfo = self.execute_raw(method_id, region_platform, request).await?;
|
2020-06-04 21:52:43 -07:00
|
|
|
if rinfo.status_none {
|
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
let retries = rinfo.retries;
|
|
|
|
let status = rinfo.response.status();
|
2020-06-05 11:14:43 -07:00
|
|
|
let value = rinfo.response.json::<Option<T>>().await;
|
|
|
|
value.map_err(|e| RiotApiError::new(e, retries, None, Some(status)))
|
2019-11-07 16:06:01 -08:00
|
|
|
}
|
|
|
|
|
2021-05-21 17:38:30 -07:00
|
|
|
/// This method should generally not be used directly. Consider using endpoint wrappers instead.
|
2020-02-01 13:35:37 -08:00
|
|
|
///
|
2021-05-21 17:38:30 -07:00
|
|
|
/// This sends a request based on the given parameters and returns a parsed result.
|
2020-02-01 13:35:37 -08:00
|
|
|
///
|
|
|
|
/// # Parameters
|
|
|
|
/// * `method_id` - A unique string id representing the endpoint method for per-method rate limiting.
|
2021-05-21 17:38:30 -07:00
|
|
|
/// * `region_platform` - The stringified platform, used in rate limiting.
|
|
|
|
/// * `request` - The request information. Use `request()` to obtain a `RequestBuilder` instance.
|
2020-06-04 21:52:43 -07:00
|
|
|
///
|
|
|
|
/// # Returns
|
|
|
|
/// A future resolving to a `Result` containg either a `T` (success) or a `RiotApiError` (failure).
|
2021-05-21 17:38:30 -07:00
|
|
|
pub async fn execute<'a, T: serde::de::DeserializeOwned + 'a>(&'a self,
|
|
|
|
method_id: &'static str, region_platform: &'static str, request: RequestBuilder)
|
2020-06-04 21:52:43 -07:00
|
|
|
-> Result<T>
|
|
|
|
{
|
2021-05-21 17:38:30 -07:00
|
|
|
let rinfo = self.execute_raw(method_id, region_platform, request).await?;
|
2020-06-04 21:52:43 -07: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)))
|
|
|
|
}
|
|
|
|
|
2021-05-21 17:38:30 -07:00
|
|
|
/// This method should generally not be used directly. Consider using endpoint wrappers instead.
|
2020-06-04 21:52:43 -07:00
|
|
|
///
|
2021-05-21 17:38:30 -07:00
|
|
|
/// This sends a request based on the given parameters and returns a raw `ResponseInfo`.
|
2020-06-04 21:52:43 -07:00
|
|
|
///
|
|
|
|
/// 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.
|
2021-05-21 17:38:30 -07:00
|
|
|
/// * `region_platform` - The stringified platform, used in rate limiting.
|
|
|
|
/// * `request` - The request information. Use `request()` to obtain a `RequestBuilder` instance.
|
2020-06-04 21:52:43 -07:00
|
|
|
///
|
|
|
|
/// # Returns
|
|
|
|
/// A future resolving to a `Result` containg either a `ResponseInfo` (success) or a `RiotApiError` (failure).
|
2021-05-21 17:31:52 -07:00
|
|
|
pub fn execute_raw(&self, method_id: &'static str, region_platform: &'static str, request: RequestBuilder)
|
2021-05-21 15:35:31 -07:00
|
|
|
-> impl Future<Output = Result<ResponseInfo>> + '_
|
2019-11-07 16:06:01 -08:00
|
|
|
{
|
|
|
|
self.regional_requester(region_platform)
|
2021-05-21 17:31:52 -07:00
|
|
|
.execute(&self.config, method_id, request)
|
2019-10-21 21:45:38 -07:00
|
|
|
}
|
2019-11-07 16:06:01 -08:00
|
|
|
|
2020-02-01 13:35:37 -08:00
|
|
|
/// Get or create the RegionalRequester for the given region.
|
2019-11-07 16:06:01 -08:00
|
|
|
fn regional_requester(&self, region_platform: &'static str) -> Arc<RegionalRequester> {
|
|
|
|
self.regional_requesters.get_or_insert_with(region_platform, || {
|
|
|
|
log::debug!("Creating requester for region platform {}.", region_platform);
|
|
|
|
RegionalRequester::new()
|
|
|
|
})
|
|
|
|
}
|
2019-10-17 16:00:04 -07:00
|
|
|
}
|