espocrm_rs/
lib.rs

1//! # espocrm-rs
2//!
3//! The `espocrm-rs` crate provides an API Client for EspoCRM. This client is based on the official PHP API client provided by the EspoCRM team.
4//! You can find this client [here](https://docs.espocrm.com/development/api-client-php/).
5//!
6//! ## Getting started
7//! To get started you'll have to provide the URL where EspoCRM is located at. You will also have to set the way you want to authenticate with EspoCRM.
8//! This can be done in one of the following ways:
9//! - Username+Password
10//! - API Key
11//! - HMAC (Recommended)
12//!
13//! The following example creates an EspoApiClient with HMAC authorization
14//! ```rust
15//! use espocrm_rs::EspoApiClient;
16//!
17//! let client = EspoApiClient::new("https://espocrm.example.com")
18//!     .set_api_key("Your API Key here")
19//!     .set_secret_key("Your API Secret")
20//!     .build();
21//! ```
22//!
23//! The following example creates an EspoApiClient with API Key authorization
24//! ```rust
25//! use espocrm_rs::EspoApiClient;
26//! let client = EspoApiClient::new("https://espocrm.example.com")
27//!     .set_api_key("Your API Key here")
28//!     .build();
29//! ```
30//!
31//! The following example creates an EspoApiClient with Username+Password authorization.
32//! **This is highly discouraged!**
33//! ```rust
34//! use espocrm_rs::EspoApiClient;
35//! let client = EspoApiClient::new("https://espocrm.example.com")
36//!     .set_username("Your Username here")
37//!     .set_password("Your Password here")
38//!     .build();
39//! ```
40//!
41//! # Making a GET request
42//! To make a request, you need to know a couple things:
43//! - The request method to use
44//! - On what to perform the request
45//! - Optionally, any data needed for the request
46//!
47//! Most of these things are laid out pretty well in the EspoCRM API documentation [here](https://docs.espocrm.com/development/api/)
48//! ```rust
49//! use espocrm_rs::{EspoApiClient, Params, Where, FilterType, Value, NoGeneric, Method};
50//!
51//! let params = Params::default()
52//!     .set_offset(0)
53//!     .set_where(vec![
54//!         Where {
55//!             r#type: FilterType::IsTrue,
56//!             attribute: "exampleField".to_string(),
57//!             value: None
58//!         },
59//!         Where {
60//!             r#type: FilterType::ArrayAnyOf,
61//!             attribute: "exampleField2".to_string(),
62//!             value: Some(Value::array(vec![
63//!                 Value::str("a"),
64//!                 Value::str("b"),
65//!                 Value::str("c")
66//!             ]))
67//!         }
68//!     ])
69//!     .build();
70//!
71//! let client = EspoApiClient::new("https://espocrm.example.com")
72//!     .set_secret_key("Your Secret Key")
73//!     .set_api_key("Your api key")
74//!     .build();
75//!
76//! let result = client.request::<NoGeneric, &str>(Method::Get, "Contact", Some(params), None);
77//! ```
78//!
79//! # Making a POST, PUT or DELETE request
80//! These are all similar in working. They'll serialize your data into json using Serde's serialize trait
81//!
82//! ```rust
83//! use espocrm_rs::{EspoApiClient, Method};
84//! use serde::Serialize;
85//!
86//! #[derive(Serialize, Clone)]
87//! struct MyData {
88//!     some_value:         String,
89//!     some_other_value:   i64
90//! }
91//!
92//! let client = EspoApiClient::new("https://espocrm.example.com")
93//!     .set_secret_key("Your Secret Key")
94//!     .set_api_key("Your api key")
95//!     .build();
96//!
97//! let data = MyData {
98//!     some_value: "value".to_string(),
99//!     some_other_value: 10
100//! };
101//!
102//! let result = client.request(Method::Post, "Contact", None, Some(data));
103//!```
104//!
105
106extern crate core;
107
108mod espocrm_api_client;
109mod espocrm_types;
110mod serializer;
111mod tracing_if;
112
113pub use espocrm_api_client::*;
114pub use espocrm_types::*;
115
116#[cfg(test)]
117mod tests {
118    use crate::espocrm_api_client::EspoApiClient;
119    use crate::espocrm_types::{FilterType, Order, Params, Value, Where};
120    use crate::serializer::serialize;
121    use std::collections::HashSet;
122    use std::hash::Hash;
123
124    const URL: &str = "foo";
125
126    fn assert_eq_unsorted_vec<T: Eq + Hash>(a: &Vec<T>, b: &Vec<T>) -> bool {
127        let a: HashSet<_> = a.iter().collect();
128        let b: HashSet<_> = b.iter().collect();
129
130        a == b
131    }
132
133    #[test]
134    fn url() {
135        let client = EspoApiClient::new(URL);
136        assert_eq!(client.url, "foo".to_string());
137    }
138
139    #[test]
140    fn username() {
141        let client = EspoApiClient::new(URL).set_username("bar").build();
142
143        assert_eq!(Some("bar".to_string()), client.username);
144    }
145
146    #[test]
147    fn password() {
148        let client = EspoApiClient::new(URL).set_password("bar").build();
149
150        assert_eq!(Some("bar".to_string()), client.password);
151    }
152
153    #[test]
154    fn api_key() {
155        let client = EspoApiClient::new(URL).set_api_key("bar").build();
156
157        assert_eq!(Some("bar".to_string()), client.api_key);
158    }
159
160    #[test]
161    fn secret_key() {
162        let client = EspoApiClient::new(URL).set_secret_key("bar").build();
163
164        assert_eq!(Some("bar".to_string()), client.secret_key);
165    }
166
167    #[test]
168    fn full() {
169        let client = EspoApiClient::new(URL)
170            .set_username("username")
171            .set_password("password")
172            .set_api_key("api_key")
173            .set_secret_key("secret_key")
174            .build();
175
176        assert_eq!(Some("username".to_string()), client.username);
177        assert_eq!(Some("password".to_string()), client.password);
178        assert_eq!(Some("api_key".to_string()), client.api_key);
179        assert_eq!(Some("secret_key".to_string()), client.secret_key);
180        assert_eq!("foo".to_string(), client.url);
181    }
182
183    #[test]
184    fn modify_url() {
185        let client = EspoApiClient::new(URL).set_url("bar").build();
186
187        assert_eq!("bar".to_string(), client.url)
188    }
189
190    #[test]
191    fn normalize_url() {
192        let client = EspoApiClient::new(URL);
193        let normalized_url = client.normalize_url("Contact".to_string());
194
195        assert_eq!(format!("{}{}Contact", URL, client.url_path), normalized_url)
196    }
197
198    #[test]
199    fn serialize_basic() {
200        let params = Params::new().set_offset(0).set_order(Order::Desc).build();
201
202        let serialized = serialize(params).unwrap();
203        let serialized_split: Vec<_> = serialized.split("&").collect();
204
205        let correct = vec!["order=desc", "offset=0"];
206        assert!(assert_eq_unsorted_vec(&serialized_split, &correct))
207    }
208
209    #[test]
210    fn serialize_without_where_value() {
211        let params = Params::new()
212            .set_offset(0)
213            .set_where(vec![Where {
214                r#type: FilterType::IsTrue,
215                attribute: "exampleBoolean".to_string(),
216                value: None,
217            }])
218            .build();
219
220        let serialized = serialize(params).unwrap();
221
222        assert_eq!(
223            "offset=0&where%5B0%5D%5Btype%5D=isTrue&where%5B0%5D%5Battribute%5D=exampleBoolean"
224                .to_string(),
225            serialized
226        );
227    }
228
229    #[test]
230    fn serialize_with_where_string_value() {
231        let params = Params::new()
232            .set_offset(0)
233            .set_where(vec![Where {
234                r#type: FilterType::IsTrue,
235                attribute: "exampleBoolean".to_string(),
236                value: Some(Value::str("a")),
237            }])
238            .build();
239
240        let serialized = serialize(params).unwrap();
241
242        assert_eq!("offset=0&where%5B0%5D%5Btype%5D=isTrue&where%5B0%5D%5Battribute%5D=exampleBoolean&where%5B0%5D%5Bvalue%5D=a".to_string(), serialized);
243    }
244
245    #[test]
246    fn serialize_with_where_array_value() {
247        let params = Params::new()
248            .set_offset(0)
249            .set_where(vec![Where {
250                r#type: FilterType::IsTrue,
251                attribute: "exampleBoolean".to_string(),
252                value: Some(Value::Array(Some(vec![
253                    Value::str("a"),
254                    Value::str("b"),
255                    Value::str("c"),
256                ]))),
257            }])
258            .build();
259
260        let serialized = serialize(params).unwrap();
261
262        /*
263           The left hand side has been created with the following PHP code:
264
265           $where = [
266               [
267                   'type' => 'isTrue',
268                   'attribute' => 'exampleBoolean',
269                   'value' => ['a', 'b', 'c']
270               ],
271           ];
272
273           $params = [
274               'offset' => 0,
275               'where' => $where
276           ];
277
278           echo http_build_query($params);
279        */
280        assert_eq!("offset=0&where%5B0%5D%5Btype%5D=isTrue&where%5B0%5D%5Battribute%5D=exampleBoolean&where%5B0%5D%5Bvalue%5D%5B0%5D=a&where%5B0%5D%5Bvalue%5D%5B1%5D=b&where%5B0%5D%5Bvalue%5D%5B2%5D=c".to_string(), serialized);
281    }
282}