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}