Skip to content

Commit

Permalink
Merge pull request #2 from 999eagle/feat/user-agent
Browse files Browse the repository at this point in the history
add user-agent header and set application name
  • Loading branch information
LostQuasar authored Sep 29, 2024
2 parents d39a320 + 1f02c6c commit dacc111
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 17 deletions.
120 changes: 103 additions & 17 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,71 @@ use reqwest::header;
use std::fmt::Debug;
use strum_macros::EnumString;

/// All methods contain an `Option<String>` to provide an alternate api key to use if it differs from the default
pub struct OpenShockAPI {
client: reqwest::Client,
base_url: String,
default_key: String,
/// Builder for [`OpenShockAPI`]
#[derive(Default)]
pub struct OpenShockAPIBuilder {
base_url: Option<String>,
default_key: Option<String>,
app_name: Option<String>,
app_version: Option<String>,
}

/// Which list of shockers to return
#[derive(EnumString, Debug)]
pub enum ListShockerSource {
Own,
Shared,
}
impl OpenShockAPIBuilder {
/// Create a new builder
pub fn new() -> Self {
Self::default()
}

/// set the base URL to use
///
/// this is optional and can be provided to use a self-hosted instance of the OpenShock API. if
/// left unset, the default (`https://api.openshock.app`) will be used.
pub fn with_base_url(mut self, base_url: String) -> Self {
self.base_url = Some(base_url);
self
}

/// set the API token to use
///
/// this must be provided
pub fn with_default_api_token(mut self, default_api_token: String) -> Self {
self.default_key = Some(default_api_token);
self
}

/// set the name and optionally version of the app using this crate
///
/// this is optional. if provided, the information will be added to the user agent string for
/// all OpenShock API requests and also sent in [`OpenShockAPI::post_control`] so the app name
/// shows up in the OpenShock log.
pub fn with_app(mut self, app_name: String, app_version: Option<String>) -> Self {
self.app_name = Some(app_name);
self.app_version = app_version;
self
}

/// check parameters and build an instance of [`OpenShockAPI`]
pub fn build(self) -> Result<OpenShockAPI, Error> {
let base_url = self
.base_url
.unwrap_or("https://api.openshock.app".to_string());
let Some(default_key) = self.default_key else {
return Err(Error::MissingApiToken);
};

let mut user_agent = format!("rzap/{}", env!("CARGO_PKG_VERSION"));
// maybe add platform information as well?
let app_name = if let Some(app_name) = self.app_name {
if let Some(app_version) = self.app_version {
user_agent += &format!(" ({} {})", app_name, app_version);
} else {
user_agent += &format!(" ({})", app_name);
}
app_name
} else {
"rzap".to_string()
};

impl OpenShockAPI {
/// Create a new instance of the api interface with a default key and the base_url, because OpenShock can be self hosted `base_url` can be any url without the leading `/` if `None` is provided the default of <https://api.shocklink.net> is used.
pub fn new(base_url: Option<String>, default_key: String) -> Self {
let mut headers = header::HeaderMap::new();
headers.insert(
"Content-type",
Expand All @@ -29,16 +77,54 @@ impl OpenShockAPI {
"accept",
header::HeaderValue::from_static("application/json"),
);
headers.insert(
header::USER_AGENT,
header::HeaderValue::from_str(&user_agent).map_err(|e| Error::InvalidHeaderValue(e))?,
);
let client = reqwest::Client::builder()
.default_headers(headers)
.build()
.unwrap();
let base_url = base_url.unwrap_or("https://api.openshock.app".to_string());
OpenShockAPI {

Ok(OpenShockAPI {
client,
base_url,
default_key,
app_name,
})
}
}

/// All methods contain an `Option<String>` to provide an alternate api key to use if it differs from the default
pub struct OpenShockAPI {
client: reqwest::Client,
base_url: String,
default_key: String,
app_name: String,
}

/// Which list of shockers to return
#[derive(EnumString, Debug)]
pub enum ListShockerSource {
Own,
Shared,
}

impl OpenShockAPI {
/// Return a builder for the api interface
///
/// this is the same as [`OpenShockAPIBuilder::new`]
pub fn builder() -> OpenShockAPIBuilder {
OpenShockAPIBuilder::new()
}

/// Create a new instance of the api interface with a default key and the base_url, because OpenShock can be self hosted `base_url` can be any url without the leading `/` if `None` is provided the default of <https://api.shocklink.net> is used.
pub fn new(base_url: Option<String>, default_key: String) -> Self {
let mut builder = Self::builder().with_default_api_token(default_key);
if let Some(base_url) = base_url {
builder = builder.with_base_url(base_url);
}
builder.build().unwrap()
}

/// Gets user info from the provided API key, the default key from the instance is used if `None` is provided
Expand Down Expand Up @@ -108,7 +194,7 @@ impl OpenShockAPI {
duration: duration,
exclusive: true,
}],
custom_name: "rusty".to_string(),
custom_name: self.app_name.clone(),
})?;

let resp = self
Expand Down
10 changes: 10 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
/// Error type for functions in this crate
#[derive(Debug)]
pub enum Error {
/// error propagated from reqwest
Reqwest(reqwest::Error),
/// error propagated from serde
Serde(serde_json::Error),
/// no API token was provided to the API interface
MissingApiToken,
/// invalid header value when building the API interface
InvalidHeaderValue(reqwest::header::InvalidHeaderValue),
}

impl From<reqwest::Error> for Error {
Expand All @@ -22,6 +28,8 @@ impl std::fmt::Display for Error {
match self {
Self::Reqwest(e) => e.fmt(f),
Self::Serde(e) => e.fmt(f),
Self::MissingApiToken => write!(f, "no API token was provided"),
Self::InvalidHeaderValue(e) => write!(f, "invalid header value for user agent: {}", e),
}
}
}
Expand All @@ -31,6 +39,8 @@ impl std::error::Error for Error {
match self {
Self::Reqwest(e) => e.source(),
Self::Serde(e) => e.source(),
Self::MissingApiToken => None,
Self::InvalidHeaderValue(e) => e.source(),
}
}
}

0 comments on commit dacc111

Please sign in to comment.