feat: Add RSO support

pull/73/head
Mingwei Samuel 2024-05-05 23:47:40 -07:00
parent ea48d422e1
commit d745aaa868
7 changed files with 118 additions and 45 deletions

View File

@ -14,6 +14,7 @@ pub struct RiotApiConfig {
pub(crate) burst_factor: f32, pub(crate) burst_factor: f32,
pub(crate) duration_overhead: Duration, pub(crate) duration_overhead: Duration,
pub(crate) client_builder: Option<ClientBuilder>, pub(crate) client_builder: Option<ClientBuilder>,
pub(crate) rso_clear_header: Option<String>,
} }
impl RiotApiConfig { impl RiotApiConfig {
@ -81,6 +82,7 @@ impl RiotApiConfig {
burst_factor: Self::PRECONFIG_BURST_BURST_FACTOR, burst_factor: Self::PRECONFIG_BURST_BURST_FACTOR,
duration_overhead: Self::PRECONFIG_BURST_DURATION_OVERHEAD, duration_overhead: Self::PRECONFIG_BURST_DURATION_OVERHEAD,
client_builder: Some(ClientBuilder::new().default_headers(default_headers)), client_builder: Some(ClientBuilder::new().default_headers(default_headers)),
rso_clear_header: Some(Self::RIOT_KEY_HEADER.to_owned()),
} }
} }
@ -101,6 +103,7 @@ impl RiotApiConfig {
burst_factor: Self::PRECONFIG_BURST_BURST_FACTOR, burst_factor: Self::PRECONFIG_BURST_BURST_FACTOR,
duration_overhead: Self::PRECONFIG_BURST_DURATION_OVERHEAD, duration_overhead: Self::PRECONFIG_BURST_DURATION_OVERHEAD,
client_builder: Some(client_builder), client_builder: Some(client_builder),
rso_clear_header: Some(Self::RIOT_KEY_HEADER.to_owned()),
} }
} }
@ -295,6 +298,24 @@ impl RiotApiConfig {
self.duration_overhead = duration_overhead; self.duration_overhead = duration_overhead;
self self
} }
/// Sets the header to clear for RSO requests (if `Some`), or will not override any headers (if
/// `None`).
///
/// This is a bit of a hack. The client used by Riven is expected to include the API key as a
/// default header. However, if the API key is included in an [RSO](https://developer.riotgames.com/docs/lol#rso-integration)
/// request the server responds with a 400 "Bad request - Invalid authorization specified"
/// error. To avoid this the `rso_clear_header` header is overridden to be empty for RSO
/// requests.
///
/// This is set to `Some(`[`Self::RIOT_KEY_HEADER`]`)` by default.
///
/// # Returns
/// `self`, for chaining.
pub fn set_rso_clear_header(mut self, rso_clear_header: Option<String>) -> Self {
self.rso_clear_header = rso_clear_header;
self
}
} }
impl<T: AsRef<[u8]>> From<T> for RiotApiConfig { impl<T: AsRef<[u8]>> From<T> for RiotApiConfig {

View File

@ -8,7 +8,7 @@
/////////////////////////////////////////////// ///////////////////////////////////////////////
// http://www.mingweisamuel.com/riotapi-schema/tool/ // http://www.mingweisamuel.com/riotapi-schema/tool/
// Version a70746fcf353ba0ad0aceceafcc70d4ba8de4431 // Version 92f57e3e7279cc02ec6a5ce6665ca08354d6a178
//! Automatically generated endpoint handles. //! Automatically generated endpoint handles.
#![allow(clippy::let_and_return, clippy::too_many_arguments)] #![allow(clippy::let_and_return, clippy::too_many_arguments)]
@ -324,17 +324,21 @@ impl<'a> AccountV1<'a> {
/// Get account by access token /// Get account by access token
/// # Parameters /// # Parameters
/// * `route` - Route to query. /// * `route` - Route to query.
/// * `authorization` (required, in header) /// * `access_token` - RSO access token.
/// # RSO
/// This endpoint uses [Riot Sign On](https://developer.riotgames.com/docs/lol#rso-integration)
/// via the `access_token` parameter, instead of the Riot API key.
/// # Riot Developer API Reference /// # Riot Developer API Reference
/// <a href="https://developer.riotgames.com/api-methods/#account-v1/GET_getByAccessToken" target="_blank">`account-v1.getByAccessToken`</a> /// <a href="https://developer.riotgames.com/api-methods/#account-v1/GET_getByAccessToken" target="_blank">`account-v1.getByAccessToken`</a>
/// ///
/// Note: this method is automatically generated. /// Note: this method is automatically generated.
pub fn get_by_access_token(&self, route: RegionalRoute, authorization: &str) pub fn get_by_access_token(&self, route: RegionalRoute, access_token: impl std::fmt::Display)
-> impl Future<Output = Result<account_v1::Account>> + 'a -> impl Future<Output = Result<account_v1::Account>> + 'a
{ {
let route_str = route.into(); let route_str = route.into();
let request = self.base.request(Method::GET, route_str, "/riot/account/v1/accounts/me"); let request = self.base.request(Method::GET, route_str, "/riot/account/v1/accounts/me");
let request = request.header("Authorization", authorization); let mut request = request.bearer_auth(access_token);
if let Some(clear) = self.base.get_rso_clear_header() { request = request.header(clear, "") }
let future = self.base.execute_val::<account_v1::Account>("account-v1.getByAccessToken", route_str, request); let future = self.base.execute_val::<account_v1::Account>("account-v1.getByAccessToken", route_str, request);
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
let future = future.instrument(tracing::info_span!("account-v1.getByAccessToken")); let future = future.instrument(tracing::info_span!("account-v1.getByAccessToken"));
@ -927,17 +931,21 @@ impl<'a> LorDeckV1<'a> {
/// Get a list of the calling user's decks. /// Get a list of the calling user's decks.
/// # Parameters /// # Parameters
/// * `route` - Route to query. /// * `route` - Route to query.
/// * `authorization` (required, in header) /// * `access_token` - RSO access token.
/// # RSO
/// This endpoint uses [Riot Sign On](https://developer.riotgames.com/docs/lol#rso-integration)
/// via the `access_token` parameter, instead of the Riot API key.
/// # Riot Developer API Reference /// # Riot Developer API Reference
/// <a href="https://developer.riotgames.com/api-methods/#lor-deck-v1/GET_getDecks" target="_blank">`lor-deck-v1.getDecks`</a> /// <a href="https://developer.riotgames.com/api-methods/#lor-deck-v1/GET_getDecks" target="_blank">`lor-deck-v1.getDecks`</a>
/// ///
/// Note: this method is automatically generated. /// Note: this method is automatically generated.
pub fn get_decks(&self, route: RegionalRoute, authorization: &str) pub fn get_decks(&self, route: RegionalRoute, access_token: impl std::fmt::Display)
-> impl Future<Output = Result<Vec<lor_deck_v1::Deck>>> + 'a -> impl Future<Output = Result<Vec<lor_deck_v1::Deck>>> + 'a
{ {
let route_str = route.into(); let route_str = route.into();
let request = self.base.request(Method::GET, route_str, "/lor/deck/v1/decks/me"); let request = self.base.request(Method::GET, route_str, "/lor/deck/v1/decks/me");
let request = request.header("Authorization", authorization); let mut request = request.bearer_auth(access_token);
if let Some(clear) = self.base.get_rso_clear_header() { request = request.header(clear, "") }
let future = self.base.execute_val::<Vec<lor_deck_v1::Deck>>("lor-deck-v1.getDecks", route_str, request); let future = self.base.execute_val::<Vec<lor_deck_v1::Deck>>("lor-deck-v1.getDecks", route_str, request);
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
let future = future.instrument(tracing::info_span!("lor-deck-v1.getDecks")); let future = future.instrument(tracing::info_span!("lor-deck-v1.getDecks"));
@ -947,17 +955,21 @@ impl<'a> LorDeckV1<'a> {
/// Create a new deck for the calling user. /// Create a new deck for the calling user.
/// # Parameters /// # Parameters
/// * `route` - Route to query. /// * `route` - Route to query.
/// * `authorization` (required, in header) /// * `access_token` - RSO access token.
/// # RSO
/// This endpoint uses [Riot Sign On](https://developer.riotgames.com/docs/lol#rso-integration)
/// via the `access_token` parameter, instead of the Riot API key.
/// # Riot Developer API Reference /// # Riot Developer API Reference
/// <a href="https://developer.riotgames.com/api-methods/#lor-deck-v1/POST_createDeck" target="_blank">`lor-deck-v1.createDeck`</a> /// <a href="https://developer.riotgames.com/api-methods/#lor-deck-v1/POST_createDeck" target="_blank">`lor-deck-v1.createDeck`</a>
/// ///
/// Note: this method is automatically generated. /// Note: this method is automatically generated.
pub fn create_deck(&self, route: RegionalRoute, body: &lor_deck_v1::NewDeck, authorization: &str) pub fn create_deck(&self, route: RegionalRoute, access_token: impl std::fmt::Display, body: &lor_deck_v1::NewDeck)
-> impl Future<Output = Result<String>> + 'a -> impl Future<Output = Result<String>> + 'a
{ {
let route_str = route.into(); let route_str = route.into();
let request = self.base.request(Method::POST, route_str, "/lor/deck/v1/decks/me"); let request = self.base.request(Method::POST, route_str, "/lor/deck/v1/decks/me");
let request = request.header("Authorization", authorization); let mut request = request.bearer_auth(access_token);
if let Some(clear) = self.base.get_rso_clear_header() { request = request.header(clear, "") }
let request = request.body(serde_json::ser::to_vec(body).unwrap()); let request = request.body(serde_json::ser::to_vec(body).unwrap());
let future = self.base.execute_val::<String>("lor-deck-v1.createDeck", route_str, request); let future = self.base.execute_val::<String>("lor-deck-v1.createDeck", route_str, request);
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
@ -980,17 +992,21 @@ impl<'a> LorInventoryV1<'a> {
/// Return a list of cards owned by the calling user. /// Return a list of cards owned by the calling user.
/// # Parameters /// # Parameters
/// * `route` - Route to query. /// * `route` - Route to query.
/// * `authorization` (required, in header) /// * `access_token` - RSO access token.
/// # RSO
/// This endpoint uses [Riot Sign On](https://developer.riotgames.com/docs/lol#rso-integration)
/// via the `access_token` parameter, instead of the Riot API key.
/// # Riot Developer API Reference /// # Riot Developer API Reference
/// <a href="https://developer.riotgames.com/api-methods/#lor-inventory-v1/GET_getCards" target="_blank">`lor-inventory-v1.getCards`</a> /// <a href="https://developer.riotgames.com/api-methods/#lor-inventory-v1/GET_getCards" target="_blank">`lor-inventory-v1.getCards`</a>
/// ///
/// Note: this method is automatically generated. /// Note: this method is automatically generated.
pub fn get_cards(&self, route: RegionalRoute, authorization: &str) pub fn get_cards(&self, route: RegionalRoute, access_token: impl std::fmt::Display)
-> impl Future<Output = Result<Vec<lor_inventory_v1::Card>>> + 'a -> impl Future<Output = Result<Vec<lor_inventory_v1::Card>>> + 'a
{ {
let route_str = route.into(); let route_str = route.into();
let request = self.base.request(Method::GET, route_str, "/lor/inventory/v1/cards/me"); let request = self.base.request(Method::GET, route_str, "/lor/inventory/v1/cards/me");
let request = request.header("Authorization", authorization); let mut request = request.bearer_auth(access_token);
if let Some(clear) = self.base.get_rso_clear_header() { request = request.header(clear, "") }
let future = self.base.execute_val::<Vec<lor_inventory_v1::Card>>("lor-inventory-v1.getCards", route_str, request); let future = self.base.execute_val::<Vec<lor_inventory_v1::Card>>("lor-inventory-v1.getCards", route_str, request);
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
let future = future.instrument(tracing::info_span!("lor-inventory-v1.getCards")); let future = future.instrument(tracing::info_span!("lor-inventory-v1.getCards"));
@ -1358,17 +1374,21 @@ impl<'a> SummonerV4<'a> {
/// Get a summoner by access token. /// Get a summoner by access token.
/// # Parameters /// # Parameters
/// * `route` - Route to query. /// * `route` - Route to query.
/// * `authorization` (optional, in header) - Bearer token /// * `access_token` - RSO access token.
/// # RSO
/// This endpoint uses [Riot Sign On](https://developer.riotgames.com/docs/lol#rso-integration)
/// via the `access_token` parameter, instead of the Riot API key.
/// # Riot Developer API Reference /// # Riot Developer API Reference
/// <a href="https://developer.riotgames.com/api-methods/#summoner-v4/GET_getByAccessToken" target="_blank">`summoner-v4.getByAccessToken`</a> /// <a href="https://developer.riotgames.com/api-methods/#summoner-v4/GET_getByAccessToken" target="_blank">`summoner-v4.getByAccessToken`</a>
/// ///
/// Note: this method is automatically generated. /// Note: this method is automatically generated.
pub fn get_by_access_token(&self, route: PlatformRoute, authorization: Option<&str>) pub fn get_by_access_token(&self, route: PlatformRoute, access_token: impl std::fmt::Display)
-> impl Future<Output = Result<summoner_v4::Summoner>> + 'a -> impl Future<Output = Result<summoner_v4::Summoner>> + 'a
{ {
let route_str = route.into(); let route_str = route.into();
let request = self.base.request(Method::GET, route_str, "/lol/summoner/v4/summoners/me"); let request = self.base.request(Method::GET, route_str, "/lol/summoner/v4/summoners/me");
let mut request = request; if let Some(authorization) = authorization { request = request.header("Authorization", authorization); } let mut request = request.bearer_auth(access_token);
if let Some(clear) = self.base.get_rso_clear_header() { request = request.header(clear, "") }
let future = self.base.execute_val::<summoner_v4::Summoner>("summoner-v4.getByAccessToken", route_str, request); let future = self.base.execute_val::<summoner_v4::Summoner>("summoner-v4.getByAccessToken", route_str, request);
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
let future = future.instrument(tracing::info_span!("summoner-v4.getByAccessToken")); let future = future.instrument(tracing::info_span!("summoner-v4.getByAccessToken"));
@ -1688,17 +1708,21 @@ impl<'a> TftSummonerV1<'a> {
/// Get a summoner by access token. /// Get a summoner by access token.
/// # Parameters /// # Parameters
/// * `route` - Route to query. /// * `route` - Route to query.
/// * `authorization` (optional, in header) - Bearer token. /// * `access_token` - RSO access token.
/// # RSO
/// This endpoint uses [Riot Sign On](https://developer.riotgames.com/docs/lol#rso-integration)
/// via the `access_token` parameter, instead of the Riot API key.
/// # Riot Developer API Reference /// # Riot Developer API Reference
/// <a href="https://developer.riotgames.com/api-methods/#tft-summoner-v1/GET_getByAccessToken" target="_blank">`tft-summoner-v1.getByAccessToken`</a> /// <a href="https://developer.riotgames.com/api-methods/#tft-summoner-v1/GET_getByAccessToken" target="_blank">`tft-summoner-v1.getByAccessToken`</a>
/// ///
/// Note: this method is automatically generated. /// Note: this method is automatically generated.
pub fn get_by_access_token(&self, route: PlatformRoute, authorization: Option<&str>) pub fn get_by_access_token(&self, route: PlatformRoute, access_token: impl std::fmt::Display)
-> impl Future<Output = Result<tft_summoner_v1::Summoner>> + 'a -> impl Future<Output = Result<tft_summoner_v1::Summoner>> + 'a
{ {
let route_str = route.into(); let route_str = route.into();
let request = self.base.request(Method::GET, route_str, "/tft/summoner/v1/summoners/me"); let request = self.base.request(Method::GET, route_str, "/tft/summoner/v1/summoners/me");
let mut request = request; if let Some(authorization) = authorization { request = request.header("Authorization", authorization); } let mut request = request.bearer_auth(access_token);
if let Some(clear) = self.base.get_rso_clear_header() { request = request.header(clear, "") }
let future = self.base.execute_val::<tft_summoner_v1::Summoner>("tft-summoner-v1.getByAccessToken", route_str, request); let future = self.base.execute_val::<tft_summoner_v1::Summoner>("tft-summoner-v1.getByAccessToken", route_str, request);
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
let future = future.instrument(tracing::info_span!("tft-summoner-v1.getByAccessToken")); let future = future.instrument(tracing::info_span!("tft-summoner-v1.getByAccessToken"));

View File

@ -8,7 +8,7 @@
/////////////////////////////////////////////// ///////////////////////////////////////////////
// http://www.mingweisamuel.com/riotapi-schema/tool/ // http://www.mingweisamuel.com/riotapi-schema/tool/
// Version a70746fcf353ba0ad0aceceafcc70d4ba8de4431 // Version 92f57e3e7279cc02ec6a5ce6665ca08354d6a178
//! Metadata about the Riot API and Riven. //! Metadata about the Riot API and Riven.
//! //!

View File

@ -8,7 +8,7 @@
/////////////////////////////////////////////// ///////////////////////////////////////////////
// http://www.mingweisamuel.com/riotapi-schema/tool/ // http://www.mingweisamuel.com/riotapi-schema/tool/
// Version a70746fcf353ba0ad0aceceafcc70d4ba8de4431 // Version 92f57e3e7279cc02ec6a5ce6665ca08354d6a178
#![allow(missing_docs)] #![allow(missing_docs)]

View File

@ -187,6 +187,11 @@ impl RiotApi {
.execute(&self.config, method_id, request) .execute(&self.config, method_id, request)
} }
/// 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()
}
/// Get or create the RegionalRequester for the given region. /// Get or create the RegionalRequester for the given region.
fn regional_requester(&self, region_platform: &'static str) -> Arc<RegionalRequester> { fn regional_requester(&self, region_platform: &'static str) -> Arc<RegionalRequester> {
self.regional_requesters self.regional_requesters

View File

@ -77,6 +77,7 @@ impl<'a> {{= endpoint }}<'a> {
const method = dotUtils.changeCase.snakeCase(operationId.slice(operationId.indexOf('.') + 1)); const method = dotUtils.changeCase.snakeCase(operationId.slice(operationId.indexOf('.') + 1));
const resp200 = operation.responses['200']; const resp200 = operation.responses['200'];
const isRso = (null != operation.security) && (null != operation.security[0]['rso']);
/* Return type checks. */ /* Return type checks. */
let hasReturn = false; let hasReturn = false;
@ -109,6 +110,9 @@ impl<'a> {{= endpoint }}<'a> {
const argBuilder = [ const argBuilder = [
'route: ', dotUtils.changeCase.pascalCase(operation['x-route-enum']), 'Route' 'route: ', dotUtils.changeCase.pascalCase(operation['x-route-enum']), 'Route'
]; ];
if (isRso) {
argBuilder.push(', access_token: impl std::fmt::Display');
}
/* Add body params before path/query. */ /* Add body params before path/query. */
if (bodyType) { if (bodyType) {
@ -161,17 +165,17 @@ impl<'a> {{= endpoint }}<'a> {
}} }}
/// # Parameters /// # Parameters
/// * `route` - Route to query. /// * `route` - Route to query.
{{ {{? isRso }}
if (allParams) /// * `access_token` - RSO access token.
{ {{?}}
for (let param of allParams) {{~ allParams || [] :param }}
{
}}
/// * `{{= dotUtils.changeCase.snakeCase(param.name) }}` ({{= param.required ? 'required' : 'optional' }}, in {{= param.in }}){{= param.description ? ' - ' + param.description : ''}} /// * `{{= dotUtils.changeCase.snakeCase(param.name) }}` ({{= param.required ? 'required' : 'optional' }}, in {{= param.in }}){{= param.description ? ' - ' + param.description : ''}}
{{ {{~}}
} {{? isRso }}
} /// # RSO
}} /// This endpoint uses [Riot Sign On](https://developer.riotgames.com/docs/lol#rso-integration)
/// via the `access_token` parameter, instead of the Riot API key.
{{?}}
/// # Riot Developer API Reference /// # Riot Developer API Reference
/// <a href="{{= operation.externalDocs.url }}" target="_blank">`{{= operationId }}`</a> /// <a href="{{= operation.externalDocs.url }}" target="_blank">`{{= operationId }}`</a>
/// ///
@ -181,22 +185,16 @@ impl<'a> {{= endpoint }}<'a> {
{ {
let route_str = route.into(); let route_str = route.into();
let request = self.base.request(Method::{{= verb.toUpperCase() }}, route_str, {{= routeArgument }}); let request = self.base.request(Method::{{= verb.toUpperCase() }}, route_str, {{= routeArgument }});
{{ {{? isRso }}
for (let queryParam of queryParams) let mut request = request.bearer_auth(access_token);
{ if let Some(clear) = self.base.get_rso_clear_header() { request = request.header(clear, "") }
}} {{?}}
{{~ queryParams :queryParam }}
{{= dotUtils.formatAddQueryParam(queryParam) }} {{= dotUtils.formatAddQueryParam(queryParam) }}
{{ {{~}}
} {{~ headerParams :headerParam }}
}}
{{
for (const headerParam of headerParams)
{
}}
{{= dotUtils.formatAddHeaderParam(headerParam) }} {{= dotUtils.formatAddHeaderParam(headerParam) }}
{{ {{~}}
}
}}
{{? bodyType }} {{? bodyType }}
let request = request.body(serde_json::ser::to_vec(body).unwrap()); let request = request.body(serde_json::ser::to_vec(body).unwrap());
{{?}} {{?}}

25
riven/tests/tests_rso.rs Normal file
View File

@ -0,0 +1,25 @@
mod testutils;
use riven::consts::*;
use testutils::*;
const ROUTE: RegionalRoute = RegionalRoute::AMERICAS;
/// https://developer.riotgames.com/apis#account-v1/GET_getByAccessToken
#[riven_test]
async fn account_v1_getbyaccesstoken() -> Result<(), String> {
let Ok(access_token) = std::env::var("RGAPI_ACCESS_TOKEN") else {
eprintln!("`RGAPI_ACCESS_TOKEN` env var not set, cannot test RSO.");
return Ok(());
};
let account = riot_api()
.account_v1()
.get_by_access_token(ROUTE, access_token)
.await
.map_err(|e| format!("Failed to get account by riot ID: {}", e))?;
println!("{:#?}", account);
Ok(())
}