use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::rest::{ResourceOperation, ResourcePath, RestResource};
use crate::HttpMethod;
use super::common::{ChargeCurrency, ChargeStatus};
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct ApplicationCharge {
#[serde(skip_serializing)]
pub id: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub price: Option<String>,
#[serde(skip_serializing)]
pub status: Option<ChargeStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
pub test: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub return_url: Option<String>,
#[serde(skip_serializing)]
pub confirmation_url: Option<String>,
#[serde(skip_serializing)]
pub currency: Option<ChargeCurrency>,
#[serde(skip_serializing)]
pub created_at: Option<DateTime<Utc>>,
#[serde(skip_serializing)]
pub updated_at: Option<DateTime<Utc>>,
}
impl ApplicationCharge {
#[must_use]
pub fn is_active(&self) -> bool {
self.status.as_ref().map_or(false, ChargeStatus::is_active)
}
#[must_use]
pub fn is_pending(&self) -> bool {
self.status.as_ref().map_or(false, ChargeStatus::is_pending)
}
#[must_use]
pub fn is_test(&self) -> bool {
self.test.unwrap_or(false)
}
}
impl RestResource for ApplicationCharge {
type Id = u64;
type FindParams = ApplicationChargeFindParams;
type AllParams = ApplicationChargeListParams;
type CountParams = ();
const NAME: &'static str = "ApplicationCharge";
const PLURAL: &'static str = "application_charges";
const PATHS: &'static [ResourcePath] = &[
ResourcePath::new(
HttpMethod::Get,
ResourceOperation::Find,
&["id"],
"application_charges/{id}",
),
ResourcePath::new(
HttpMethod::Get,
ResourceOperation::All,
&[],
"application_charges",
),
ResourcePath::new(
HttpMethod::Post,
ResourceOperation::Create,
&[],
"application_charges",
),
];
fn get_id(&self) -> Option<Self::Id> {
self.id
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct ApplicationChargeFindParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub fields: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct ApplicationChargeListParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub since_id: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fields: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::rest::{get_path, ResourceOperation};
#[test]
fn test_application_charge_serialization() {
let charge = ApplicationCharge {
id: Some(12345),
name: Some("Pro Widget".to_string()),
price: Some("19.99".to_string()),
status: Some(ChargeStatus::Pending),
test: Some(true),
return_url: Some("https://myapp.com/callback".to_string()),
confirmation_url: Some("https://shop.myshopify.com/confirm".to_string()),
currency: Some(ChargeCurrency::new("USD")),
created_at: Some(
DateTime::parse_from_rfc3339("2024-01-15T10:30:00Z")
.unwrap()
.with_timezone(&Utc),
),
updated_at: Some(
DateTime::parse_from_rfc3339("2024-01-15T10:35:00Z")
.unwrap()
.with_timezone(&Utc),
),
};
let json = serde_json::to_string(&charge).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(parsed["name"], "Pro Widget");
assert_eq!(parsed["price"], "19.99");
assert_eq!(parsed["test"], true);
assert_eq!(parsed["return_url"], "https://myapp.com/callback");
assert!(parsed.get("id").is_none());
assert!(parsed.get("status").is_none());
assert!(parsed.get("confirmation_url").is_none());
assert!(parsed.get("currency").is_none());
assert!(parsed.get("created_at").is_none());
assert!(parsed.get("updated_at").is_none());
}
#[test]
fn test_application_charge_deserialization() {
let json = r#"{
"id": 675931192,
"name": "Super Duper Expensive action",
"price": "100.00",
"status": "active",
"test": false,
"return_url": "https://super-duper.shopifyapps.com/",
"confirmation_url": "https://jsmith.myshopify.com/admin/charges/675931192/confirm_application_charge",
"currency": {
"currency": "USD"
},
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:35:00Z"
}"#;
let charge: ApplicationCharge = serde_json::from_str(json).unwrap();
assert_eq!(charge.id, Some(675931192));
assert_eq!(
charge.name,
Some("Super Duper Expensive action".to_string())
);
assert_eq!(charge.price, Some("100.00".to_string()));
assert_eq!(charge.status, Some(ChargeStatus::Active));
assert_eq!(charge.test, Some(false));
assert_eq!(
charge.return_url,
Some("https://super-duper.shopifyapps.com/".to_string())
);
assert!(charge.confirmation_url.is_some());
assert_eq!(charge.currency.as_ref().unwrap().code(), Some("USD"));
assert!(charge.created_at.is_some());
assert!(charge.updated_at.is_some());
}
#[test]
fn test_application_charge_convenience_methods() {
let active_charge = ApplicationCharge {
status: Some(ChargeStatus::Active),
..Default::default()
};
assert!(active_charge.is_active());
assert!(!active_charge.is_pending());
let pending_charge = ApplicationCharge {
status: Some(ChargeStatus::Pending),
..Default::default()
};
assert!(pending_charge.is_pending());
assert!(!pending_charge.is_active());
let test_charge = ApplicationCharge {
test: Some(true),
..Default::default()
};
assert!(test_charge.is_test());
let non_test_charge = ApplicationCharge {
test: Some(false),
..Default::default()
};
assert!(!non_test_charge.is_test());
let default_charge = ApplicationCharge::default();
assert!(!default_charge.is_test());
assert!(!default_charge.is_active());
assert!(!default_charge.is_pending());
}
#[test]
fn test_application_charge_paths() {
let find_path = get_path(
ApplicationCharge::PATHS,
ResourceOperation::Find,
&["id"],
);
assert!(find_path.is_some());
assert_eq!(find_path.unwrap().template, "application_charges/{id}");
let all_path = get_path(ApplicationCharge::PATHS, ResourceOperation::All, &[]);
assert!(all_path.is_some());
assert_eq!(all_path.unwrap().template, "application_charges");
let create_path = get_path(ApplicationCharge::PATHS, ResourceOperation::Create, &[]);
assert!(create_path.is_some());
assert_eq!(create_path.unwrap().template, "application_charges");
assert_eq!(create_path.unwrap().http_method, HttpMethod::Post);
let update_path = get_path(
ApplicationCharge::PATHS,
ResourceOperation::Update,
&["id"],
);
assert!(update_path.is_none());
let delete_path = get_path(
ApplicationCharge::PATHS,
ResourceOperation::Delete,
&["id"],
);
assert!(delete_path.is_none());
let count_path = get_path(ApplicationCharge::PATHS, ResourceOperation::Count, &[]);
assert!(count_path.is_none());
}
#[test]
fn test_application_charge_list_params() {
let params = ApplicationChargeListParams {
limit: Some(50),
since_id: Some(100),
fields: Some("id,name,price".to_string()),
};
let json = serde_json::to_value(¶ms).unwrap();
assert_eq!(json["limit"], 50);
assert_eq!(json["since_id"], 100);
assert_eq!(json["fields"], "id,name,price");
let empty_params = ApplicationChargeListParams::default();
let empty_json = serde_json::to_value(&empty_params).unwrap();
assert_eq!(empty_json, serde_json::json!({}));
}
#[test]
fn test_application_charge_constants() {
assert_eq!(ApplicationCharge::NAME, "ApplicationCharge");
assert_eq!(ApplicationCharge::PLURAL, "application_charges");
}
#[test]
fn test_application_charge_get_id() {
let charge_with_id = ApplicationCharge {
id: Some(12345),
..Default::default()
};
assert_eq!(charge_with_id.get_id(), Some(12345));
let charge_without_id = ApplicationCharge::default();
assert_eq!(charge_without_id.get_id(), None);
}
}