better error handling

This commit is contained in:
Edgar 2020-06-10 16:19:02 +02:00
parent 48339228ce
commit a120bf67cc
No known key found for this signature in database
GPG key ID: 8731E6C0166EAA85
4 changed files with 55 additions and 16 deletions

View file

@ -21,5 +21,4 @@ jsonwebtoken = "7"
base64 = "0.12" base64 = "0.12"
log = "0.4" log = "0.4"
dotenv = "0.15.0" dotenv = "0.15.0"
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
thiserror = "1.0"

View file

@ -1,11 +1,40 @@
//! Errors created by this crate. //! Errors created by this crate.
use std::collections::HashMap;
use std::fmt;
use std::error::Error;
use serde::{Deserialize, Serialize};
use thiserror::Error; /// Represents a error HATEOAS link
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct ErrorLink {
href: String,
rel: String,
method: String,
}
/// A enum that represents the possible errors. /// A paypal api response error.
#[derive(Debug, Error)] #[derive(Debug, Serialize, Deserialize)]
pub enum Errors { pub struct ApiResponseError {
/// A error used when a api call fails. /// The error name.
#[error("failure when calling the paypal api")] name: String,
ApiCallFailure(String), /// The error message.
} message: String,
/// Paypal debug id
debug_id: String,
/// Error details
details: Vec<HashMap<String, String>>,
/// Only available on Identity errors
error: Option<String>,
/// Only available on Identity errors
error_description: Option<String>,
/// Links with more information about the error.
links: Vec<ErrorLink>,
}
impl fmt::Display for ApiResponseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:#?}", self)
}
}
impl Error for ApiResponseError {}

View file

@ -279,11 +279,10 @@ impl Client {
let token = res.json::<AccessToken>().await?; let token = res.json::<AccessToken>().await?;
self.auth.expires = Some((Instant::now(), Duration::new(token.expires_in, 0))); self.auth.expires = Some((Instant::now(), Duration::new(token.expires_in, 0)));
self.auth.access_token = Some(token); self.auth.access_token = Some(token);
Ok(())
} else { } else {
return Err(Box::new(errors::Errors::ApiCallFailure(res.text().await?))); Err(Box::new(res.json::<errors::ApiResponseError>().await?))
} }
Ok(())
} }
/// Checks if the access token expired. /// Checks if the access token expired.

View file

@ -311,6 +311,7 @@ pub struct Item {
pub category: Option<ItemCategoryType>, pub category: Option<ItemCategoryType>,
} }
/// The status of the payment authorization.
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum AuthorizationStatus { pub enum AuthorizationStatus {
@ -332,6 +333,7 @@ pub enum AuthorizationStatus {
Pending, Pending,
} }
/// Details about the status of the authorization.
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum AuthorizationStatusDetails { pub enum AuthorizationStatusDetails {
@ -339,6 +341,7 @@ pub enum AuthorizationStatusDetails {
PendingReview, PendingReview,
} }
/// A payment authorization.
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct AuthorizationWithData { pub struct AuthorizationWithData {
/// The status for the authorized payment. /// The status for the authorized payment.
@ -347,6 +350,7 @@ pub struct AuthorizationWithData {
pub status_details: AuthorizationStatusDetails, pub status_details: AuthorizationStatusDetails,
} }
/// The capture status.
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum CaptureStatus { pub enum CaptureStatus {
@ -362,6 +366,7 @@ pub enum CaptureStatus {
Refunded, Refunded,
} }
/// Details about the captured payment status.
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum CaptureStatusDetails { pub enum CaptureStatusDetails {
@ -393,6 +398,7 @@ pub enum CaptureStatusDetails {
VerificationRequired, VerificationRequired,
} }
/// A captured payment.
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct Capture { pub struct Capture {
/// The status of the captured payment. /// The status of the captured payment.
@ -401,6 +407,7 @@ pub struct Capture {
pub status_details: CaptureStatusDetails, pub status_details: CaptureStatusDetails,
} }
/// The status of the refund
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum RefundStatus { pub enum RefundStatus {
@ -420,6 +427,7 @@ pub enum RefundStatusDetails {
Echeck, Echeck,
} }
/// A refund
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct Refund { pub struct Refund {
/// The status of the refund. /// The status of the refund.
@ -428,6 +436,7 @@ pub struct Refund {
pub status_details: RefundStatusDetails, pub status_details: RefundStatusDetails,
} }
/// The comprehensive history of payments for the purchase unit.
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct PaymentCollection { pub struct PaymentCollection {
/// An array of authorized payments for a purchase unit. A purchase unit can have zero or more authorized payments. /// An array of authorized payments for a purchase unit. A purchase unit can have zero or more authorized payments.
@ -438,6 +447,7 @@ pub struct PaymentCollection {
pub refunds: Vec<Refund>, pub refunds: Vec<Refund>,
} }
/// Represents either a full or partial order that the payer intends to purchase from the payee.
#[derive(Debug, Default, Serialize, Deserialize)] #[derive(Debug, Default, Serialize, Deserialize)]
pub struct PurchaseUnit { pub struct PurchaseUnit {
/// The API caller-provided external ID for the purchase unit. Required for multiple purchase units when you must update the order through PATCH. /// The API caller-provided external ID for the purchase unit. Required for multiple purchase units when you must update the order through PATCH.
@ -488,11 +498,13 @@ pub struct PurchaseUnit {
/// The name and address of the person to whom to ship the items. /// The name and address of the person to whom to ship the items.
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub shipping: Option<ShippingDetail>, pub shipping: Option<ShippingDetail>,
/// The comprehensive history of payments for the purchase unit.
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub payments: Option<PaymentCollection>, pub payments: Option<PaymentCollection>,
} }
impl PurchaseUnit { impl PurchaseUnit {
/// Creates a new PurchaseUnit with the required properties.
pub fn new(amount: Amount) -> Self { pub fn new(amount: Amount) -> Self {
Self { Self {
amount, amount,
@ -798,7 +810,7 @@ impl super::Client {
let order = res.json::<Order>().await?; let order = res.json::<Order>().await?;
Ok(order) Ok(order)
} else { } else {
Err(Box::new(errors::Errors::ApiCallFailure(res.text().await?))) Err(Box::new(res.json::<errors::ApiResponseError>().await?))
} }
} }
@ -826,7 +838,7 @@ impl super::Client {
let order = res.json::<Order>().await?; let order = res.json::<Order>().await?;
Ok(order) Ok(order)
} else { } else {
Err(Box::new(errors::Errors::ApiCallFailure(res.text().await?))) Err(Box::new(res.json::<errors::ApiResponseError>().await?))
} }
} }
@ -912,7 +924,7 @@ impl super::Client {
if res.status().is_success() { if res.status().is_success() {
Ok(()) Ok(())
} else { } else {
Err(Box::new(errors::Errors::ApiCallFailure(res.text().await?))) Err(Box::new(res.json::<errors::ApiResponseError>().await?))
} }
} }