From 2b23f6a8db88a45c26a1b187152c69b880dc65af Mon Sep 17 00:00:00 2001 From: Edgar Luque Date: Wed, 28 Sep 2022 16:25:44 +0200 Subject: [PATCH] change the way sandbox is handled, allow to provide a mock server url --- Cargo.toml | 11 +++++----- examples/invoice.rs | 4 ++-- src/api/invoice.rs | 2 +- src/client.rs | 52 +++++++++++++++++++++++++++++++-------------- src/endpoint.rs | 17 +-------------- src/lib.rs | 2 +- 6 files changed, 47 insertions(+), 41 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 345c962..3b37d1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "paypal-rs" -version = "0.2.2" +version = "0.2.3" authors = ["Edgar "] description = "A library that wraps the paypal api asynchronously." repository = "https://github.com/edg-l/paypal-rs/" license = "MIT OR Apache-2.0" -keywords = ["paypal", "api", "async"] -categories = ["api-bindings"] +keywords = ["paypal", "paypal-api"] +categories = ["api-bindings", "web-programming::http-client"] documentation = "https://docs.rs/paypal-rs" readme = "README.md" edition = "2021" @@ -14,7 +14,7 @@ edition = "2021" [dependencies] reqwest = { version = "0.11.12", default-features = false, features = ["json"] } -serde = { version = "1.0.144", features = ["derive"] } +serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.85" serde_with = "2.0.1" chrono = { version = "0.4.22", features = ["serde"] } @@ -26,10 +26,11 @@ derive_builder = "0.11.2" serde_qs = "0.10.1" [dev-dependencies] -tokio = { version = "1.21.1", features = ["macros", "rt-multi-thread"] } +tokio = { version = "1.21.2", features = ["macros", "rt-multi-thread"] } dotenv = "0.15.0" anyhow = "1.0.65" color-eyre = "0.6.2" +wiremock = "0.5.14" [features] default = ["reqwest/native-tls"] diff --git a/examples/invoice.rs b/examples/invoice.rs index 7fa96c4..1867065 100644 --- a/examples/invoice.rs +++ b/examples/invoice.rs @@ -1,6 +1,6 @@ use color_eyre::Result; use paypal_rs::data::invoice::*; -use paypal_rs::{api::invoice::*, data::common::Money}; +use paypal_rs::{api::invoice::*, data::common::Money, PaypalEnv}; use paypal_rs::{data::common::Currency, Client}; #[tokio::main] @@ -11,7 +11,7 @@ async fn main() -> Result<()> { let clientid = std::env::var("PAYPAL_CLIENTID")?; let secret = std::env::var("PAYPAL_SECRET")?; - let mut client = Client::new(clientid, secret, true); + let mut client = Client::new(clientid, secret, PaypalEnv::Sandbox); client.get_access_token().await?; let payload = InvoicePayloadBuilder::default() diff --git a/src/api/invoice.rs b/src/api/invoice.rs index 5d0297f..4d1360f 100644 --- a/src/api/invoice.rs +++ b/src/api/invoice.rs @@ -401,7 +401,7 @@ mod tests { let clientid = std::env::var("PAYPAL_CLIENTID").unwrap(); let secret = std::env::var("PAYPAL_SECRET").unwrap(); - let mut client = Client::new(clientid, secret, true); + let mut client = Client::new(clientid, secret, crate::PaypalEnv::Sandbox); client.get_access_token().await.unwrap(); client } diff --git a/src/client.rs b/src/client.rs index b864132..6fe3b0e 100644 --- a/src/client.rs +++ b/src/client.rs @@ -14,7 +14,7 @@ use crate::{ /// Represents the access token returned by the OAuth2 authentication. /// /// -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct AccessToken { /// The OAuth2 scopes. pub scope: String, @@ -31,7 +31,7 @@ pub struct AccessToken { } /// Stores OAuth2 information. -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Auth { /// Your client id. pub client_id: String, @@ -44,16 +44,44 @@ pub struct Auth { } /// Represents a client used to interact with the paypal api. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Client { /// Internal http client pub(crate) client: reqwest::Client, /// Whether you are or not in a sandbox enviroment. - pub sandbox: bool, + pub env: PaypalEnv, /// Api Auth information pub auth: Auth, } +/// The paypal api environment. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum PaypalEnv { + /// The live environment. + Live, + /// The sandbox environment. + Sandbox, + /// For mocking. + Mock(String), +} + +impl PaypalEnv { + /// Returns the endpoint of this environment. + pub fn endpoint(&self) -> &str { + match &self { + PaypalEnv::Live => LIVE_ENDPOINT, + PaypalEnv::Sandbox => SANDBOX_ENDPOINT, + PaypalEnv::Mock(endpoint) => endpoint.as_str(), + } + } + + /// Constructs a url from the target. + pub fn make_url(&self, target: &str) -> String { + assert!(target.starts_with('/'), "target path must start with '/'"); + format!("{}{}", self.endpoint(), target) + } +} + impl Client { /// Returns a new client, you must get_access_token afterwards to interact with the api. /// @@ -76,10 +104,10 @@ impl Client { /// client.get_access_token().await.unwrap(); /// } /// ``` - pub fn new(client_id: String, secret: String, sandbox: bool) -> Client { + pub fn new(client_id: String, secret: String, env: PaypalEnv) -> Client { Client { client: reqwest::Client::new(), - sandbox, + env, auth: Auth { client_id, secret, @@ -89,14 +117,6 @@ impl Client { } } - fn endpoint(&self) -> &str { - if self.sandbox { - SANDBOX_ENDPOINT - } else { - LIVE_ENDPOINT - } - } - /// Sets up the request headers as required on https://developer.paypal.com/docs/api/reference/api-requests/#http-request-headers async fn setup_headers( &self, @@ -158,7 +178,7 @@ impl Client { } let res = self .client - .post(format!("{}/v1/oauth2/token", self.endpoint()).as_str()) + .post(self.env.make_url("/v1/oauth2/token")) .basic_auth(&self.auth.client_id, Some(&self.auth.secret)) .header("Content-Type", "x-www-form-urlencoded") .header("Accept", "application/json") @@ -193,7 +213,7 @@ impl Client { where E: Endpoint, { - let mut url = endpoint.full_path(self.sandbox); + let mut url = self.env.make_url(&endpoint.relative_path()); if let Some(query) = endpoint.query() { let query_string = serde_qs::to_string(&query).expect("serialize the query correctly"); diff --git a/src/endpoint.rs b/src/endpoint.rs index 2684598..6e57124 100644 --- a/src/endpoint.rs +++ b/src/endpoint.rs @@ -1,6 +1,6 @@ //! This module contains the endpoint trait used to implemented api endpoints. -use crate::{LIVE_ENDPOINT, SANDBOX_ENDPOINT}; +use crate::{LIVE_ENDPOINT, SANDBOX_ENDPOINT, PaypalEnv}; use serde::{de::DeserializeOwned, Serialize}; use std::borrow::Cow; @@ -28,19 +28,4 @@ pub trait Endpoint { fn body(&self) -> Option { None } - - /// The full path of this endpoint. - /// - /// Automatically implemented. - fn full_path(&self, is_sandbox: bool) -> String { - let relative_path = self.relative_path(); - - assert!(relative_path.starts_with('/'), "relative path must start with '/'"); - - if is_sandbox { - format!("{}{}", SANDBOX_ENDPOINT, relative_path) - } else { - format!("{}{}", LIVE_ENDPOINT, relative_path) - } - } } diff --git a/src/lib.rs b/src/lib.rs index 5efbf6f..4ec0544 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -184,7 +184,7 @@ mod tests { let clientid = env::var("PAYPAL_CLIENTID").unwrap(); let secret = env::var("PAYPAL_SECRET").unwrap(); - Client::new(clientid, secret, true) + Client::new(clientid, secret, crate::PaypalEnv::Sandbox) } #[test]