1#![allow(elided_named_lifetimes)]
54#![allow(missing_docs)]
55#![allow(unused_imports)]
56#![allow(clippy::needless_lifetimes)]
57#![allow(clippy::too_many_arguments)]
58#![cfg_attr(docsrs, feature(doc_cfg))]
59
60#[cfg(feature = "requests")]
62pub mod candidate_applications;
63#[cfg(feature = "requests")]
65pub mod candidates;
66#[cfg(feature = "requests")]
68pub mod companies;
69#[cfg(feature = "requests")]
71pub mod compensations;
72#[cfg(feature = "requests")]
74pub mod custom_fields;
75#[cfg(feature = "requests")]
77pub mod custom_object_fields;
78#[cfg(feature = "requests")]
80pub mod custom_object_records;
81#[cfg(feature = "requests")]
83pub mod custom_objects;
84#[cfg(feature = "requests")]
86pub mod departments;
87#[cfg(feature = "requests")]
89pub mod employment_types;
90#[cfg(feature = "requests")]
92pub mod entitlements;
93#[cfg(feature = "requests")]
95pub mod job;
96#[cfg(feature = "requests")]
98pub mod job_requisitions;
99#[cfg(feature = "requests")]
101pub mod leave_balances;
102#[cfg(feature = "requests")]
104pub mod leave_requests;
105#[cfg(feature = "requests")]
107pub mod leave_types;
108#[cfg(feature = "requests")]
110pub mod legal_entities;
111#[cfg(feature = "requests")]
113pub mod me;
114mod methods;
115#[cfg(feature = "requests")]
117pub mod object_categories;
118#[cfg(feature = "requests")]
120pub mod shift_inputs;
121#[cfg(feature = "requests")]
123pub mod teams;
124#[cfg(test)]
125mod tests;
126#[cfg(feature = "requests")]
128pub mod time_entries;
129#[cfg(feature = "requests")]
131pub mod tracks_and_levels;
132pub mod types;
133#[cfg(feature = "requests")]
135pub mod users;
136pub mod utils;
137#[cfg(feature = "requests")]
139pub mod work_locations;
140#[cfg(feature = "requests")]
142pub mod workers;
143
144#[cfg(feature = "requests")]
145use std::env;
146
147#[cfg(not(target_arch = "wasm32"))]
148#[cfg(feature = "requests")]
149static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
150
151#[derive(Clone, Debug)]
153#[cfg(feature = "requests")]
154pub struct Client {
155 token: String,
156 base_url: String,
157
158 #[cfg(feature = "retry")]
159 client: reqwest_middleware::ClientWithMiddleware,
160 #[cfg(feature = "retry")]
161 #[cfg(not(target_arch = "wasm32"))]
162 #[allow(dead_code)]
163 client_http1_only: reqwest_middleware::ClientWithMiddleware,
164
165 #[cfg(not(feature = "retry"))]
166 client: reqwest::Client,
167 #[cfg(not(feature = "retry"))]
168 #[cfg(not(target_arch = "wasm32"))]
169 #[allow(dead_code)]
170 client_http1_only: reqwest::Client,
171}
172
173#[cfg(feature = "retry")]
175#[cfg(feature = "requests")]
176pub struct RequestBuilder(pub reqwest_middleware::RequestBuilder);
177#[cfg(not(feature = "retry"))]
178#[cfg(feature = "requests")]
179pub struct RequestBuilder(pub reqwest::RequestBuilder);
180
181#[cfg(feature = "requests")]
182impl Client {
183 #[tracing::instrument(skip(token))]
188 #[cfg(not(target_arch = "wasm32"))]
189 pub fn new_from_reqwest<T>(
190 token: T,
191 builder_http: reqwest::ClientBuilder,
192 builder_websocket: reqwest::ClientBuilder,
193 ) -> Self
194 where
195 T: ToString + std::fmt::Debug,
196 {
197 #[cfg(feature = "retry")]
198 {
199 let retry_policy =
201 reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
202 match (builder_http.build(), builder_websocket.build()) {
203 (Ok(c), Ok(c1)) => {
204 let client = reqwest_middleware::ClientBuilder::new(c)
205 .with(reqwest_tracing::TracingMiddleware::default())
207 .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
209 reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
210 |req: &reqwest::Request| req.try_clone().is_some(),
211 ))
212 .build();
213 let client_http1_only = reqwest_middleware::ClientBuilder::new(c1)
214 .with(reqwest_tracing::TracingMiddleware::default())
215 .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
216 reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
217 |req: &reqwest::Request| req.try_clone().is_some(),
218 ))
219 .build();
220 Client {
221 token: token.to_string(),
222 base_url: "https://rest.ripplingapis.com".to_string(),
223
224 client,
225 client_http1_only,
226 }
227 }
228 (Err(e), _) | (_, Err(e)) => panic!("creating reqwest client failed: {:?}", e),
229 }
230 }
231 #[cfg(not(feature = "retry"))]
232 {
233 match (builder_http.build(), builder_websocket.build()) {
234 (Ok(c), Ok(c1)) => Client {
235 token: token.to_string(),
236 base_url: "https://rest.ripplingapis.com".to_string(),
237
238 client: c,
239 client_http1_only: c1,
240 },
241 (Err(e), _) | (_, Err(e)) => panic!("creating reqwest client failed: {:?}", e),
242 }
243 }
244 }
245
246 #[tracing::instrument(skip(token))]
251 #[cfg(target_arch = "wasm32")]
252 pub fn new_from_reqwest<T>(token: T, builder_http: reqwest::ClientBuilder) -> Self
253 where
254 T: ToString + std::fmt::Debug,
255 {
256 #[cfg(feature = "retry")]
257 {
258 let retry_policy =
260 reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
261 match builder_http.build() {
262 Ok(c) => {
263 let client = reqwest_middleware::ClientBuilder::new(c)
264 .with(reqwest_tracing::TracingMiddleware::default())
266 .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
268 reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
269 |req: &reqwest::Request| req.try_clone().is_some(),
270 ))
271 .build();
272 Client {
273 token: token.to_string(),
274 base_url: "https://rest.ripplingapis.com".to_string(),
275
276 client,
277 }
278 }
279 Err(e) => panic!("creating reqwest client failed: {:?}", e),
280 }
281 }
282 #[cfg(not(feature = "retry"))]
283 {
284 match builder_http.build() {
285 Ok(c) => Client {
286 token: token.to_string(),
287 base_url: "https://rest.ripplingapis.com".to_string(),
288
289 client: c,
290 },
291 Err(e) => panic!("creating reqwest client failed: {:?}", e),
292 }
293 }
294 }
295
296 #[tracing::instrument(skip(token))]
300 pub fn new<T>(token: T) -> Self
301 where
302 T: ToString + std::fmt::Debug,
303 {
304 #[cfg(not(target_arch = "wasm32"))]
305 let client = reqwest::Client::builder()
306 .user_agent(APP_USER_AGENT)
307 .timeout(std::time::Duration::from_secs(600))
309 .connect_timeout(std::time::Duration::from_secs(60));
310 #[cfg(target_arch = "wasm32")]
311 let client = reqwest::Client::builder();
312 #[cfg(not(target_arch = "wasm32"))]
313 let client_http1 = reqwest::Client::builder()
314 .user_agent(APP_USER_AGENT)
316 .timeout(std::time::Duration::from_secs(600))
317 .connect_timeout(std::time::Duration::from_secs(60))
318 .http1_only();
319 #[cfg(not(target_arch = "wasm32"))]
320 return Self::new_from_reqwest(token, client, client_http1);
321 #[cfg(target_arch = "wasm32")]
322 Self::new_from_reqwest(token, client)
323 }
324
325 #[tracing::instrument]
327 pub fn set_base_url<H>(&mut self, base_url: H)
328 where
329 H: Into<String> + std::fmt::Display + std::fmt::Debug,
330 {
331 self.base_url = base_url.to_string().trim_end_matches('/').to_string();
332 }
333
334 #[tracing::instrument]
336 pub fn new_from_env() -> Self {
337 let token = env::var("RIPPLING_API_TOKEN").expect("must set RIPPLING_API_TOKEN");
338 let base_url =
339 env::var("RIPPLING_HOST").unwrap_or("https://rest.ripplingapis.com".to_string());
340
341 let mut c = Client::new(token);
342 c.set_base_url(base_url);
343 c
344 }
345
346 #[tracing::instrument]
348 pub async fn request_raw(
349 &self,
350 method: reqwest::Method,
351 uri: &str,
352 body: Option<reqwest::Body>,
353 ) -> anyhow::Result<RequestBuilder> {
354 let u = if uri.starts_with("https://") || uri.starts_with("http://") {
355 uri.to_string()
356 } else {
357 format!("{}/{}", self.base_url, uri.trim_start_matches('/'))
358 };
359
360 let mut req = self.client.request(method, &u);
361
362 req = req.bearer_auth(&self.token);
364
365 req = req.header(
367 reqwest::header::ACCEPT,
368 reqwest::header::HeaderValue::from_static("application/json"),
369 );
370 req = req.header(
371 reqwest::header::CONTENT_TYPE,
372 reqwest::header::HeaderValue::from_static("application/json"),
373 );
374
375 if let Some(body) = body {
376 req = req.body(body);
377 }
378
379 Ok(RequestBuilder(req))
380 }
381
382 pub fn candidate_applications(&self) -> candidate_applications::CandidateApplications {
384 candidate_applications::CandidateApplications::new(self.clone())
385 }
386
387 pub fn candidates(&self) -> candidates::Candidates {
389 candidates::Candidates::new(self.clone())
390 }
391
392 pub fn companies(&self) -> companies::Companies {
394 companies::Companies::new(self.clone())
395 }
396
397 pub fn compensations(&self) -> compensations::Compensations {
399 compensations::Compensations::new(self.clone())
400 }
401
402 pub fn custom_fields(&self) -> custom_fields::CustomFields {
404 custom_fields::CustomFields::new(self.clone())
405 }
406
407 pub fn custom_object_fields(&self) -> custom_object_fields::CustomObjectFields {
409 custom_object_fields::CustomObjectFields::new(self.clone())
410 }
411
412 pub fn custom_object_records(&self) -> custom_object_records::CustomObjectRecords {
414 custom_object_records::CustomObjectRecords::new(self.clone())
415 }
416
417 pub fn custom_objects(&self) -> custom_objects::CustomObjects {
419 custom_objects::CustomObjects::new(self.clone())
420 }
421
422 pub fn departments(&self) -> departments::Departments {
424 departments::Departments::new(self.clone())
425 }
426
427 pub fn employment_types(&self) -> employment_types::EmploymentTypes {
429 employment_types::EmploymentTypes::new(self.clone())
430 }
431
432 pub fn entitlements(&self) -> entitlements::Entitlements {
434 entitlements::Entitlements::new(self.clone())
435 }
436
437 pub fn job(&self) -> job::Job {
439 job::Job::new(self.clone())
440 }
441
442 pub fn job_requisitions(&self) -> job_requisitions::JobRequisitions {
444 job_requisitions::JobRequisitions::new(self.clone())
445 }
446
447 pub fn leave_balances(&self) -> leave_balances::LeaveBalances {
449 leave_balances::LeaveBalances::new(self.clone())
450 }
451
452 pub fn leave_requests(&self) -> leave_requests::LeaveRequests {
454 leave_requests::LeaveRequests::new(self.clone())
455 }
456
457 pub fn leave_types(&self) -> leave_types::LeaveTypes {
459 leave_types::LeaveTypes::new(self.clone())
460 }
461
462 pub fn legal_entities(&self) -> legal_entities::LegalEntities {
464 legal_entities::LegalEntities::new(self.clone())
465 }
466
467 pub fn me(&self) -> me::Me {
469 me::Me::new(self.clone())
470 }
471
472 pub fn object_categories(&self) -> object_categories::ObjectCategories {
474 object_categories::ObjectCategories::new(self.clone())
475 }
476
477 pub fn shift_inputs(&self) -> shift_inputs::ShiftInputs {
479 shift_inputs::ShiftInputs::new(self.clone())
480 }
481
482 pub fn teams(&self) -> teams::Teams {
484 teams::Teams::new(self.clone())
485 }
486
487 pub fn time_entries(&self) -> time_entries::TimeEntries {
489 time_entries::TimeEntries::new(self.clone())
490 }
491
492 pub fn tracks_and_levels(&self) -> tracks_and_levels::TracksAndLevels {
494 tracks_and_levels::TracksAndLevels::new(self.clone())
495 }
496
497 pub fn users(&self) -> users::Users {
499 users::Users::new(self.clone())
500 }
501
502 pub fn work_locations(&self) -> work_locations::WorkLocations {
504 work_locations::WorkLocations::new(self.clone())
505 }
506
507 pub fn workers(&self) -> workers::Workers {
509 workers::Workers::new(self.clone())
510 }
511}