square_ox/
client.rs

1/*!
2The [SquareClient](crate::client::SquareClient) allows the user of the crate
3to use the [Square API](https://developer.squareup.com) in an idiomatic way.
4
5# Example: Creating a client
6In order to create a client you will need your account access token that can be found
7in the [Developer Apps](https://developer.squareup.com/apps) page for the specific
8application you are wanting to use.
9
10```rust
11 const ACCESS_TOKEN:&str = "your_square_access_token";
12
13use square_ox::client::SquareClient;
14let client = SquareClient::new(ACCESS_TOKEN);
15```
16After creating a client you will be able to use all of the clients methods.
17
18*/
19use crate::api::{SquareAPI, Verb};
20use crate::errors::SquareError;
21use crate::response::SquareResponse;
22
23use reqwest::{header, Client};
24use serde::Serialize;
25use std::default::Default;
26
27#[derive(Copy, Clone)]
28pub enum ClientMode {
29    Production,
30    Sandboxed,
31}
32
33/// The default mode we start a client in is Sandboxed
34impl Default for ClientMode {
35    fn default() -> Self {
36        Self::Sandboxed
37    }
38}
39
40/// The SquareClient contains many useful methods allowing for convenient
41/// use of the [Square API](https://developer.squareup.com).
42#[derive(Clone)]
43pub struct SquareClient {
44    access_token: String,
45    pub(crate) client_mode: ClientMode,
46}
47
48impl SquareClient {
49    /// Create a new [SquareClient](SquareClient)
50    ///
51    /// # Arguments
52    /// * `access_token` - The access token for the Square App you
53    /// want to use the client with is required.
54    ///
55    /// # Example: Create a new client
56    /// ```
57    /// const ACCESS_TOKEN:&str = "your_square_access_token";
58    /// use square_ox::client::SquareClient;
59    ///
60    /// let client = SquareClient::new(ACCESS_TOKEN);
61    /// ```
62    pub fn new(access_token: &str) -> Self {
63        Self {
64            access_token: access_token.to_string(),
65            client_mode: Default::default(),
66        }
67    }
68
69    /// Set the client to Production Mode
70    ///
71    /// # Arguments
72    /// This method takes no arguments, as by default the client will use SandBox Mode.
73    ///
74    /// # Example
75    /// ```
76    /// const ACCESS_TOKEN:&str = "your_square_access_token";
77    ///
78    /// use square_ox::client::SquareClient;
79    /// let client = SquareClient::new(ACCESS_TOKEN).production();
80    /// ```
81    pub fn production(self) -> Self {
82        Self {
83            access_token: self.access_token,
84            client_mode: ClientMode::Production,
85        }
86    }
87
88    /// Sends a request to a given [SquareAPI](crate::api::SquareAPI)
89    /// # Arguments
90    /// * `api` - The [SquareAPI](crate::api::SquareAPI) to send the request to
91    /// * `body` - The json that will be included in the request.
92    /// All types that meet the conditions to be deserialized to JSON are accepted.
93    ///
94    /// # Example:
95    /// ```
96    /// use env_logger::Builder;
97    ///  async {
98    ///     use square_ox::{
99    ///         api::{
100    ///             Verb, SquareAPI, payment::PaymentRequest
101    ///         },
102    ///         client,
103    ///         builder::Builder
104    ///     };
105    ///     const ACCESS_TOKEN:&str = "your_square_access_token";
106    ///     let payment = Builder::from(PaymentRequest::default()).build().await;
107    ///
108    ///     let client = client::SquareClient::new(ACCESS_TOKEN);
109    ///     client.request( Verb::POST, SquareAPI::Payments("".to_string()), Some(&payment), None).await.expect("");
110    /// };
111    ///
112    /// ```
113    pub async fn request<T>(
114        &self,
115        verb: Verb,
116        endpoint: SquareAPI,
117        json: Option<&T>,
118        parameters: Option<Vec<(String, String)>>,
119    ) -> Result<SquareResponse, SquareError>
120    where
121        T: Serialize + ?Sized,
122    {
123        let url = self.endpoint(endpoint).clone();
124        let authorization_header = format!("Bearer {}", &self.access_token);
125
126        // Add the headers to the request
127        let mut headers = header::HeaderMap::new();
128        headers.insert(
129            header::AUTHORIZATION,
130            header::HeaderValue::from_str(&authorization_header)?,
131        );
132
133        // Create a client with the appropriate headers
134        let client = Client::builder().default_headers(headers).build()?;
135
136        println!("url: {}", &url);
137
138        // Send the request to the Square API, and get the response
139        let mut builder = match verb {
140            Verb::GET => client.get(&url),
141            Verb::POST => client.post(&url),
142            Verb::PUT => client.put(&url),
143            Verb::PATCH => client.patch(&url),
144            Verb::DELETE => client.delete(&url),
145        };
146
147        // Add query parameters if there are any
148        if let Some(parameters) = parameters {
149            builder = builder.query(&parameters);
150        }
151
152        // Add a json body if there is one
153        if let Some(json) = json {
154            builder = builder.json(json)
155        }
156
157        // Deserialize the response into a SquareResponse
158        let response: SquareResponse = builder.send().await?.json().await?;
159
160        // TODO debug code!
161        // let response = builder.send().await?.text().await?;
162        // println!("{:?}", response);
163        // let response: SquareResponse = serde_json::from_str(&response)?;
164        // println!("{:?}", response);
165
166        // handle the possibility of an error being returned by the Square API
167        if response.errors.is_some() && response.errors.as_ref().unwrap().len() > 0 {
168            return Err(SquareError::from(response.errors))
169        }
170
171        Ok(response)
172    }
173}