diff --git a/codegen/src/client.rs b/codegen/src/client.rs index 298f887..060c30f 100644 --- a/codegen/src/client.rs +++ b/codegen/src/client.rs @@ -66,6 +66,7 @@ pub fn generate_client_file( base_url: String, authorization_token: Option, timeout: std::time::Duration, + runtime_info: Vec<(&'static str, String)>, } impl Client { @@ -79,6 +80,7 @@ pub fn generate_client_file( base_url: "https://api.sumup.com".to_string(), authorization_token, timeout: std::time::Duration::from_secs(10), + runtime_info: crate::version::runtime_info(), } } @@ -130,6 +132,11 @@ pub fn generate_client_file( self.timeout } + /// Returns the runtime headers sent with each request. + pub(crate) fn runtime_headers(&self) -> &[(&'static str, String)] { + &self.runtime_info + } + #(#tag_methods)* } diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 4462018..01d6d94 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -74,6 +74,7 @@ impl Generator { )); self.ensure_directories()?; + self.generate_api_version_file()?; self.generate_common_module()?; self.generate_tag_modules()?; self.generate_client_module()?; @@ -181,6 +182,29 @@ impl Generator { generate_client_file(&self.out_path, &self.spec, &self.schemas_by_tag.tag_schemas) } + fn generate_api_version_file(&self) -> Result<(), String> { + let api_version = self.spec.info.version.trim().to_string(); + + if api_version.is_empty() { + return Err("OpenAPI spec version is empty".to_string()); + } + + let mut version_path = self.out_path.clone(); + version_path.push("api_version.rs"); + + let api_version_literal = syn::LitStr::new(&api_version, Span::call_site()); + let tokens = quote! { + /// The version of the SumUp API spec. + pub const API_VERSION: &str = #api_version_literal; + }; + + let contents = format_generated_code(tokens); + std::fs::write(&version_path, &contents) + .map_err(|e| format!("Failed to write api_version.rs: {}", e))?; + + Ok(()) + } + fn generate_mod_rs(&self) -> Result<(), String> { generate_mod_file(&self.out_path, &self.schemas_by_tag) } diff --git a/codegen/src/operation.rs b/codegen/src/operation.rs index cb74da0..d5ce450 100644 --- a/codegen/src/operation.rs +++ b/codegen/src/operation.rs @@ -411,6 +411,9 @@ fn generate_operation_method( if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } #query_additions if let Some(body) = body { request = request.json(&body); @@ -428,6 +431,9 @@ fn generate_operation_method( if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } #query_additions let response = request.send().await?; } @@ -442,6 +448,9 @@ fn generate_operation_method( if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } #query_additions let response = request.send().await?; } diff --git a/sdk/build.rs b/sdk/build.rs new file mode 100644 index 0000000..a449c7e --- /dev/null +++ b/sdk/build.rs @@ -0,0 +1,22 @@ +#![forbid(unsafe_code)] + +use std::process::Command; + +fn main() { + let rustc_version = Command::new("rustc") + .arg("--version") + .output() + .ok() + .and_then(|output| { + if output.status.success() { + String::from_utf8(output.stdout).ok() + } else { + None + } + }) + .map(|version| version.trim().to_string()); + + if let Some(version) = rustc_version { + println!("cargo:rustc-env=SUMUP_RUSTC_VERSION={}", version); + } +} diff --git a/sdk/src/api_version.rs b/sdk/src/api_version.rs new file mode 100644 index 0000000..7d3c72b --- /dev/null +++ b/sdk/src/api_version.rs @@ -0,0 +1,4 @@ +// The contents of this file are generated; do not modify them. + +/// The version of the SumUp API spec. +pub const API_VERSION: &str = "1.0.0"; diff --git a/sdk/src/client.rs b/sdk/src/client.rs index e45bb13..33306cb 100644 --- a/sdk/src/client.rs +++ b/sdk/src/client.rs @@ -9,6 +9,7 @@ pub struct Client { base_url: String, authorization_token: Option, timeout: std::time::Duration, + runtime_info: Vec<(&'static str, String)>, } impl Client { /// Creates a new SumUp API client with the default base URL. @@ -21,6 +22,7 @@ impl Client { base_url: "https://api.sumup.com".to_string(), authorization_token, timeout: std::time::Duration::from_secs(10), + runtime_info: crate::version::runtime_info(), } } /// Overrides the underlying HTTP client used for requests. @@ -63,6 +65,10 @@ impl Client { pub fn timeout(&self) -> std::time::Duration { self.timeout } + /// Returns the runtime headers sent with each request. + pub(crate) fn runtime_headers(&self) -> &[(&'static str, String)] { + &self.runtime_info + } pub fn checkouts(&self) -> crate::resources::checkouts::CheckoutsClient<'_> { crate::resources::checkouts::CheckoutsClient::new(self) } diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index fa3cc64..a4fa520 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -149,6 +149,7 @@ #![forbid(unsafe_code)] +pub mod api_version; pub mod client; pub mod datetime; pub mod error; diff --git a/sdk/src/resources/checkouts.rs b/sdk/src/resources/checkouts.rs index 3be1e04..5bf678d 100644 --- a/sdk/src/resources/checkouts.rs +++ b/sdk/src/resources/checkouts.rs @@ -532,6 +532,9 @@ impl<'a> CheckoutsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } if let Some(ref value) = params.checkout_reference { request = request.query(&[("checkout_reference", value)]); } @@ -578,6 +581,9 @@ impl<'a> CheckoutsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -632,6 +638,9 @@ impl<'a> CheckoutsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -682,6 +691,9 @@ impl<'a> CheckoutsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -728,6 +740,9 @@ impl<'a> CheckoutsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -789,6 +804,9 @@ impl<'a> CheckoutsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } if let Some(ref value) = params.amount { request = request.query(&[("amount", value)]); } diff --git a/sdk/src/resources/customers.rs b/sdk/src/resources/customers.rs index 4fe1455..6fe2884 100644 --- a/sdk/src/resources/customers.rs +++ b/sdk/src/resources/customers.rs @@ -110,6 +110,9 @@ impl<'a> CustomersClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -158,6 +161,9 @@ impl<'a> CustomersClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -208,6 +214,9 @@ impl<'a> CustomersClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -257,6 +266,9 @@ impl<'a> CustomersClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -312,6 +324,9 @@ impl<'a> CustomersClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { diff --git a/sdk/src/resources/members.rs b/sdk/src/resources/members.rs index 7e6fa42..6683317 100644 --- a/sdk/src/resources/members.rs +++ b/sdk/src/resources/members.rs @@ -190,6 +190,9 @@ impl<'a> MembersClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } if let Some(ref value) = params.offset { request = request.query(&[("offset", value)]); } @@ -249,6 +252,9 @@ impl<'a> MembersClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -302,6 +308,9 @@ impl<'a> MembersClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -340,6 +349,9 @@ impl<'a> MembersClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -383,6 +395,9 @@ impl<'a> MembersClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { diff --git a/sdk/src/resources/memberships.rs b/sdk/src/resources/memberships.rs index 59b3fe3..1f593e2 100644 --- a/sdk/src/resources/memberships.rs +++ b/sdk/src/resources/memberships.rs @@ -136,6 +136,9 @@ impl<'a> MembershipsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } if let Some(ref value) = params.offset { request = request.query(&[("offset", value)]); } diff --git a/sdk/src/resources/merchant.rs b/sdk/src/resources/merchant.rs index a90fb64..2ff0aff 100644 --- a/sdk/src/resources/merchant.rs +++ b/sdk/src/resources/merchant.rs @@ -462,6 +462,9 @@ impl<'a> MerchantClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } if let Some(ref value) = params.include { request = request.query(&[("include[]", value)]); } @@ -502,6 +505,9 @@ impl<'a> MerchantClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -545,6 +551,9 @@ impl<'a> MerchantClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -582,6 +591,9 @@ impl<'a> MerchantClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { diff --git a/sdk/src/resources/merchants.rs b/sdk/src/resources/merchants.rs index 80b931c..8c5b036 100644 --- a/sdk/src/resources/merchants.rs +++ b/sdk/src/resources/merchants.rs @@ -433,6 +433,9 @@ impl<'a> MerchantsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } if let Some(ref value) = params.version { request = request.query(&[("version", value)]); } @@ -472,6 +475,9 @@ impl<'a> MerchantsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } if let Some(ref value) = params.version { request = request.query(&[("version", value)]); } @@ -519,6 +525,9 @@ impl<'a> MerchantsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } if let Some(ref value) = params.version { request = request.query(&[("version", value)]); } diff --git a/sdk/src/resources/payouts.rs b/sdk/src/resources/payouts.rs index 5f9326d..c049931 100644 --- a/sdk/src/resources/payouts.rs +++ b/sdk/src/resources/payouts.rs @@ -68,6 +68,9 @@ impl<'a> PayoutsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } request = request.query(&[("start_date", ¶ms.start_date)]); request = request.query(&[("end_date", ¶ms.end_date)]); if let Some(ref value) = params.format { @@ -118,6 +121,9 @@ impl<'a> PayoutsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } request = request.query(&[("start_date", ¶ms.start_date)]); request = request.query(&[("end_date", ¶ms.end_date)]); if let Some(ref value) = params.format { diff --git a/sdk/src/resources/readers.rs b/sdk/src/resources/readers.rs index 62d48f1..361b42e 100644 --- a/sdk/src/resources/readers.rs +++ b/sdk/src/resources/readers.rs @@ -437,6 +437,9 @@ impl<'a> ReadersClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -471,6 +474,9 @@ impl<'a> ReadersClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -522,6 +528,9 @@ impl<'a> ReadersClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -560,6 +569,9 @@ impl<'a> ReadersClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -603,6 +615,9 @@ impl<'a> ReadersClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -662,6 +677,9 @@ impl<'a> ReadersClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -753,6 +771,9 @@ impl<'a> ReadersClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -839,6 +860,9 @@ impl<'a> ReadersClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { diff --git a/sdk/src/resources/receipts.rs b/sdk/src/resources/receipts.rs index ffcf7a7..d0bbf21 100644 --- a/sdk/src/resources/receipts.rs +++ b/sdk/src/resources/receipts.rs @@ -219,6 +219,9 @@ impl<'a> ReceiptsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } request = request.query(&[("mid", ¶ms.mid)]); if let Some(ref value) = params.tx_event_id { request = request.query(&[("tx_event_id", value)]); diff --git a/sdk/src/resources/roles.rs b/sdk/src/resources/roles.rs index 0ddf5c0..1a4bf8f 100644 --- a/sdk/src/resources/roles.rs +++ b/sdk/src/resources/roles.rs @@ -106,6 +106,9 @@ impl<'a> RolesClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -144,6 +147,9 @@ impl<'a> RolesClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -191,6 +197,9 @@ impl<'a> RolesClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -235,6 +244,9 @@ impl<'a> RolesClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -278,6 +290,9 @@ impl<'a> RolesClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { diff --git a/sdk/src/resources/subaccounts.rs b/sdk/src/resources/subaccounts.rs index b4c6b0f..e974e1f 100644 --- a/sdk/src/resources/subaccounts.rs +++ b/sdk/src/resources/subaccounts.rs @@ -127,6 +127,9 @@ impl<'a> SubaccountsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } if let Some(ref value) = params.query { request = request.query(&[("query", value)]); } @@ -166,6 +169,9 @@ impl<'a> SubaccountsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -204,6 +210,9 @@ impl<'a> SubaccountsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -236,6 +245,9 @@ impl<'a> SubaccountsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { @@ -270,6 +282,9 @@ impl<'a> SubaccountsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } let response = request.send().await?; let status = response.status(); match status { diff --git a/sdk/src/resources/transactions.rs b/sdk/src/resources/transactions.rs index 28ade14..a540196 100644 --- a/sdk/src/resources/transactions.rs +++ b/sdk/src/resources/transactions.rs @@ -536,6 +536,9 @@ impl<'a> TransactionsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } if let Some(body) = body { request = request.json(&body); } @@ -582,6 +585,9 @@ impl<'a> TransactionsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } if let Some(ref value) = params.id { request = request.query(&[("id", value)]); } @@ -635,6 +641,9 @@ impl<'a> TransactionsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } if let Some(ref value) = params.transaction_code { request = request.query(&[("transaction_code", value)]); } @@ -716,6 +725,9 @@ impl<'a> TransactionsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } if let Some(ref value) = params.id { request = request.query(&[("id", value)]); } @@ -777,6 +789,9 @@ impl<'a> TransactionsClient<'a> { if let Some(token) = self.client.authorization_token() { request = request.header("Authorization", format!("Bearer {}", token)); } + for (header_name, header_value) in self.client.runtime_headers() { + request = request.header(*header_name, header_value); + } if let Some(ref value) = params.transaction_code { request = request.query(&[("transaction_code", value)]); } diff --git a/sdk/src/version.rs b/sdk/src/version.rs index d291a79..5542eb5 100644 --- a/sdk/src/version.rs +++ b/sdk/src/version.rs @@ -5,3 +5,74 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub fn user_agent() -> String { format!("sumup-rs/v{}", VERSION) } + +/// Returns the runtime headers for SDK requests +pub fn runtime_info() -> Vec<(&'static str, String)> { + vec![ + ( + "X-Sumup-Api-Version", + crate::api_version::API_VERSION.to_string(), + ), + ("X-SumUp-Lang", "rust".to_string()), + ("X-SumUp-Package-Version", VERSION.to_string()), + ("X-SumUp-Os", std::env::consts::OS.to_string()), + ("X-SumUp-Arch", runtime_arch()), + ("X-SumUp-Runtime", "rust".to_string()), + ("X-SumUp-Runtime-Version", rustc_version()), + ] +} + +fn rustc_version() -> String { + option_env!("SUMUP_RUSTC_VERSION") + .unwrap_or("unknown") + .to_string() +} + +fn runtime_arch() -> String { + match std::env::consts::ARCH { + "x86_64" => "x86_64", + "x86" | "i686" => "x86", + "aarch64" => "arm64", + "arm" => "arm", + other => other, + } + .to_string() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn runtime_info_includes_expected_headers() { + let headers = runtime_info(); + + let names: Vec<&str> = headers.iter().map(|(name, _)| *name).collect(); + assert!(names.contains(&"X-Sumup-Api-Version")); + assert!(names.contains(&"X-SumUp-Lang")); + assert!(names.contains(&"X-SumUp-Package-Version")); + assert!(names.contains(&"X-SumUp-Os")); + assert!(names.contains(&"X-SumUp-Arch")); + assert!(names.contains(&"X-SumUp-Runtime")); + assert!(names.contains(&"X-SumUp-Runtime-Version")); + } + + #[test] + fn runtime_info_uses_normalized_arch_value() { + let expected = match std::env::consts::ARCH { + "x86_64" => "x86_64", + "x86" | "i686" => "x86", + "aarch64" => "arm64", + "arm" => "arm", + other => other, + }; + + let arch = runtime_info() + .into_iter() + .find(|(name, _)| *name == "X-SumUp-Arch") + .map(|(_, value)| value) + .expect("runtime info should include X-SumUp-Arch"); + + assert_eq!(arch, expected); + } +} diff --git a/sdk/tests/client.rs b/sdk/tests/client.rs index cb061da..8e7e094 100644 --- a/sdk/tests/client.rs +++ b/sdk/tests/client.rs @@ -105,6 +105,36 @@ async fn client_requests_include_user_agent_and_custom_authorization() { .expect("request should succeed"); } +#[tokio::test] +#[serial] +async fn client_requests_include_runtime_headers() { + let server = MockServer::start().await; + let _guard = EnvVarGuard::set("SUMUP_API_KEY", "env-token"); + let expected_user_agent = version::user_agent(); + + let mut mock = Mock::given(method("GET")) + .and(path("/v0.1/checkouts")) + .and(header("User-Agent", expected_user_agent.as_str())); + + for (header_name, header_value) in version::runtime_info() { + mock = mock.and(header(header_name, header_value.as_str())); + } + + let _mock = mock + .respond_with(ResponseTemplate::new(200).set_body_json(json!([]))) + .expect(1) + .mount_as_scoped(&server) + .await; + + let client = Client::new().with_base_url(server.uri()); + + client + .checkouts() + .list(sumup::resources::checkouts::ListParams::default()) + .await + .expect("request should succeed"); +} + #[test] #[serial] fn client_returns_none_when_authorization_missing() {