use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::types::wire::wire_enum;
wire_enum! {
pub enum OAuthClientStatus {
Active => "ACTIVE",
Disabled => "DISABLED",
}
}
wire_enum! {
pub enum OAuthResponseType {
Code => "code",
Token => "token",
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct OAuthClient {
#[serde(default)]
pub client_id: Option<String>,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub url: Option<String>,
#[serde(
default,
deserialize_with = "crate::types::serde_util::null_as_default"
)]
pub redirect_uri: Vec<String>,
#[serde(default)]
pub live_trading_approved: Option<bool>,
#[serde(default)]
pub status: Option<OAuthClientStatus>,
#[serde(default)]
pub terms_of_use: Option<String>,
#[serde(default)]
pub privacy_policy: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct OAuthCode {
pub code: String,
pub client_id: String,
pub redirect_uri: String,
pub scope: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct OAuthToken {
pub access_token: String,
pub scope: String,
pub token_type: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct OAuthRequest {
pub account_id: Uuid,
pub client_id: String,
pub client_secret: String,
pub redirect_uri: String,
pub scope: String,
}
impl OAuthRequest {
pub fn new(
account_id: Uuid,
client_id: impl Into<String>,
client_secret: impl Into<String>,
redirect_uri: impl Into<String>,
scope: impl Into<String>,
) -> Self {
Self {
account_id,
client_id: client_id.into(),
client_secret: client_secret.into(),
redirect_uri: redirect_uri.into(),
scope: scope.into(),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetOAuthClientRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub response_type: Option<OAuthResponseType>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub redirect_uri: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub scope: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_token_request_serializes_as_json_not_a_form() {
let request = OAuthRequest::new(
Uuid::nil(),
"client",
"secret",
"https://example.invalid/cb",
"account:write trading",
);
let json = serde_json::to_value(&request).unwrap();
assert_eq!(json["client_id"], "client");
assert_eq!(json["scope"], "account:write trading");
}
#[test]
fn a_client_with_no_redirects_listed_decodes() {
let client: OAuthClient = serde_json::from_value(serde_json::json!({
"client_id": "abc",
"status": "ACTIVE",
}))
.unwrap();
assert!(client.redirect_uri.is_empty());
assert_eq!(client.status, Some(OAuthClientStatus::Active));
}
}