paperless_api_client/
lib.rs1#![allow(mismatched_lifetime_syntaxes)]
57#![allow(missing_docs)]
58#![allow(unused_imports)]
59#![allow(clippy::needless_lifetimes)]
60#![allow(clippy::too_many_arguments)]
61#![cfg_attr(docsrs, feature(doc_cfg))]
62
63#[cfg(feature = "requests")]
64pub mod bulk_edit_objects;
65#[cfg(feature = "requests")]
66pub mod config;
67#[cfg(feature = "requests")]
68pub mod correspondents;
69#[cfg(feature = "requests")]
70pub mod custom_fields;
71#[cfg(feature = "requests")]
72pub mod document_types;
73#[cfg(feature = "requests")]
74pub mod documents;
75#[cfg(feature = "requests")]
76pub mod groups;
77#[cfg(feature = "requests")]
78pub mod logs;
79#[cfg(feature = "requests")]
80pub mod mail_accounts;
81#[cfg(feature = "requests")]
82pub mod mail_rules;
83mod methods;
84#[cfg(feature = "requests")]
85pub mod oauth;
86#[cfg(feature = "requests")]
87pub mod profile;
88#[cfg(feature = "requests")]
89pub mod remote_version;
90#[cfg(feature = "requests")]
91pub mod saved_views;
92#[cfg(feature = "requests")]
93pub mod search;
94#[cfg(feature = "requests")]
95pub mod share_links;
96#[cfg(feature = "requests")]
97pub mod statistics;
98#[cfg(feature = "requests")]
99pub mod status;
100#[cfg(feature = "requests")]
101pub mod storage_paths;
102#[cfg(feature = "requests")]
103pub mod tags;
104#[cfg(feature = "requests")]
105pub mod tasks;
106#[cfg(test)]
107mod tests;
108#[cfg(feature = "requests")]
109pub mod token;
110#[cfg(feature = "requests")]
111pub mod trash;
112pub mod types;
113#[cfg(feature = "requests")]
114pub mod ui_settings;
115#[cfg(feature = "requests")]
116pub mod users;
117#[cfg(feature = "requests")]
118pub mod workflow_actions;
119#[cfg(feature = "requests")]
120pub mod workflow_triggers;
121#[cfg(feature = "requests")]
122pub mod workflows;
123
124#[cfg(feature = "requests")]
125use std::env;
126
127#[cfg(not(target_arch = "wasm32"))]
128#[cfg(feature = "requests")]
129static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
130
131#[derive(Clone, Debug)]
133#[cfg(feature = "requests")]
134pub struct Client {
135 token: String,
136 base_url: String,
137
138 #[cfg(feature = "retry")]
139 client: reqwest_middleware::ClientWithMiddleware,
140 #[cfg(feature = "retry")]
141 #[cfg(not(target_arch = "wasm32"))]
142 #[allow(dead_code)]
143 client_http1_only: reqwest_middleware::ClientWithMiddleware,
144
145 #[cfg(not(feature = "retry"))]
146 client: reqwest::Client,
147 #[cfg(not(feature = "retry"))]
148 #[cfg(not(target_arch = "wasm32"))]
149 #[allow(dead_code)]
150 client_http1_only: reqwest::Client,
151}
152
153#[cfg(feature = "retry")]
155#[cfg(feature = "requests")]
156pub struct RequestBuilder(pub reqwest_middleware::RequestBuilder);
157#[cfg(not(feature = "retry"))]
158#[cfg(feature = "requests")]
159pub struct RequestBuilder(pub reqwest::RequestBuilder);
160
161#[cfg(feature = "requests")]
162impl Client {
163 #[tracing::instrument(skip(token))]
168 #[cfg(not(target_arch = "wasm32"))]
169 pub fn new_from_reqwest<T>(
170 token: T,
171 builder_http: reqwest::ClientBuilder,
172 builder_websocket: reqwest::ClientBuilder,
173 ) -> Self
174 where
175 T: ToString + std::fmt::Debug,
176 {
177 #[cfg(feature = "retry")]
178 {
179 let retry_policy =
181 reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
182 match (builder_http.build(), builder_websocket.build()) {
183 (Ok(c), Ok(c1)) => {
184 let client = reqwest_middleware::ClientBuilder::new(c)
185 .with(reqwest_tracing::TracingMiddleware::default())
187 .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
189 reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
190 |req: &reqwest::Request| req.try_clone().is_some(),
191 ))
192 .build();
193 let client_http1_only = reqwest_middleware::ClientBuilder::new(c1)
194 .with(reqwest_tracing::TracingMiddleware::default())
195 .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
196 reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
197 |req: &reqwest::Request| req.try_clone().is_some(),
198 ))
199 .build();
200 Client {
201 token: token.to_string(),
202 base_url: "https://your-paperles.url/api".to_string(),
203
204 client,
205 client_http1_only,
206 }
207 }
208 (Err(e), _) | (_, Err(e)) => panic!("creating reqwest client failed: {e:?}"),
209 }
210 }
211 #[cfg(not(feature = "retry"))]
212 {
213 match (builder_http.build(), builder_websocket.build()) {
214 (Ok(c), Ok(c1)) => Client {
215 token: token.to_string(),
216 base_url: "https://your-paperles.url/api".to_string(),
217
218 client: c,
219 client_http1_only: c1,
220 },
221 (Err(e), _) | (_, Err(e)) => panic!("creating reqwest client failed: {:?}", e),
222 }
223 }
224 }
225
226 #[tracing::instrument(skip(token))]
231 #[cfg(target_arch = "wasm32")]
232 pub fn new_from_reqwest<T>(token: T, builder_http: reqwest::ClientBuilder) -> Self
233 where
234 T: ToString + std::fmt::Debug,
235 {
236 #[cfg(feature = "retry")]
237 {
238 let retry_policy =
240 reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
241 match builder_http.build() {
242 Ok(c) => {
243 let client = reqwest_middleware::ClientBuilder::new(c)
244 .with(reqwest_tracing::TracingMiddleware::default())
246 .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
248 reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
249 |req: &reqwest::Request| req.try_clone().is_some(),
250 ))
251 .build();
252 Client {
253 token: token.to_string(),
254 base_url: "https://your-paperles.url/api".to_string(),
255
256 client,
257 }
258 }
259 Err(e) => panic!("creating reqwest client failed: {:?}", e),
260 }
261 }
262 #[cfg(not(feature = "retry"))]
263 {
264 match builder_http.build() {
265 Ok(c) => Client {
266 token: token.to_string(),
267 base_url: "https://your-paperles.url/api".to_string(),
268
269 client: c,
270 },
271 Err(e) => panic!("creating reqwest client failed: {:?}", e),
272 }
273 }
274 }
275
276 #[tracing::instrument(skip(token))]
280 pub fn new<T>(token: T) -> Self
281 where
282 T: ToString + std::fmt::Debug,
283 {
284 #[cfg(not(target_arch = "wasm32"))]
285 let client = reqwest::Client::builder()
286 .user_agent(APP_USER_AGENT)
287 .timeout(std::time::Duration::from_secs(600))
289 .connect_timeout(std::time::Duration::from_secs(60));
290 #[cfg(target_arch = "wasm32")]
291 let client = reqwest::Client::builder();
292 #[cfg(not(target_arch = "wasm32"))]
293 let client_http1 = reqwest::Client::builder()
294 .user_agent(APP_USER_AGENT)
296 .timeout(std::time::Duration::from_secs(600))
297 .connect_timeout(std::time::Duration::from_secs(60))
298 .http1_only();
299 #[cfg(not(target_arch = "wasm32"))]
300 return Self::new_from_reqwest(token, client, client_http1);
301 #[cfg(target_arch = "wasm32")]
302 Self::new_from_reqwest(token, client)
303 }
304
305 #[tracing::instrument]
307 pub fn set_base_url<H>(&mut self, base_url: H)
308 where
309 H: Into<String> + std::fmt::Display + std::fmt::Debug,
310 {
311 self.base_url = base_url.to_string().trim_end_matches('/').to_string();
312 }
313
314 #[tracing::instrument]
316 pub fn new_from_env() -> Self {
317 let token = env::var("PAPERLESS_API_CLIENT_API_TOKEN")
318 .expect("must set PAPERLESS_API_CLIENT_API_TOKEN");
319 let base_url = env::var("PAPERLESS_API_CLIENT_HOST")
320 .unwrap_or("https://your-paperles.url/api".to_string());
321
322 let mut c = Client::new(token);
323 c.set_base_url(base_url);
324 c
325 }
326
327 #[tracing::instrument]
329 pub async fn request_raw(
330 &self,
331 method: reqwest::Method,
332 uri: &str,
333 body: Option<reqwest::Body>,
334 ) -> anyhow::Result<RequestBuilder> {
335 let u = if uri.starts_with("https://") || uri.starts_with("http://") {
336 uri.to_string()
337 } else {
338 format!("{}/{}", self.base_url, uri.trim_start_matches('/'))
339 };
340
341 let mut req = self.client.request(method, &u);
342
343 req = req.bearer_auth(&self.token);
345
346 req = req.header(
348 reqwest::header::ACCEPT,
349 reqwest::header::HeaderValue::from_static("application/json"),
350 );
351 req = req.header(
352 reqwest::header::CONTENT_TYPE,
353 reqwest::header::HeaderValue::from_static("application/json"),
354 );
355
356 if let Some(body) = body {
357 req = req.body(body);
358 }
359
360 Ok(RequestBuilder(req))
361 }
362
363 pub fn bulk_edit_objects(&self) -> bulk_edit_objects::BulkEditObjects {
365 bulk_edit_objects::BulkEditObjects::new(self.clone())
366 }
367
368 pub fn config(&self) -> config::Config {
370 config::Config::new(self.clone())
371 }
372
373 pub fn correspondents(&self) -> correspondents::Correspondents {
375 correspondents::Correspondents::new(self.clone())
376 }
377
378 pub fn custom_fields(&self) -> custom_fields::CustomFields {
380 custom_fields::CustomFields::new(self.clone())
381 }
382
383 pub fn document_types(&self) -> document_types::DocumentTypes {
385 document_types::DocumentTypes::new(self.clone())
386 }
387
388 pub fn documents(&self) -> documents::Documents {
390 documents::Documents::new(self.clone())
391 }
392
393 pub fn groups(&self) -> groups::Groups {
395 groups::Groups::new(self.clone())
396 }
397
398 pub fn logs(&self) -> logs::Logs {
400 logs::Logs::new(self.clone())
401 }
402
403 pub fn mail_accounts(&self) -> mail_accounts::MailAccounts {
405 mail_accounts::MailAccounts::new(self.clone())
406 }
407
408 pub fn mail_rules(&self) -> mail_rules::MailRules {
410 mail_rules::MailRules::new(self.clone())
411 }
412
413 pub fn oauth(&self) -> oauth::Oauth {
415 oauth::Oauth::new(self.clone())
416 }
417
418 pub fn profile(&self) -> profile::Profile {
420 profile::Profile::new(self.clone())
421 }
422
423 pub fn remote_version(&self) -> remote_version::RemoteVersion {
425 remote_version::RemoteVersion::new(self.clone())
426 }
427
428 pub fn saved_views(&self) -> saved_views::SavedViews {
430 saved_views::SavedViews::new(self.clone())
431 }
432
433 pub fn search(&self) -> search::Search {
435 search::Search::new(self.clone())
436 }
437
438 pub fn share_links(&self) -> share_links::ShareLinks {
440 share_links::ShareLinks::new(self.clone())
441 }
442
443 pub fn statistics(&self) -> statistics::Statistics {
445 statistics::Statistics::new(self.clone())
446 }
447
448 pub fn status(&self) -> status::Status {
450 status::Status::new(self.clone())
451 }
452
453 pub fn storage_paths(&self) -> storage_paths::StoragePaths {
455 storage_paths::StoragePaths::new(self.clone())
456 }
457
458 pub fn tags(&self) -> tags::Tags {
460 tags::Tags::new(self.clone())
461 }
462
463 pub fn tasks(&self) -> tasks::Tasks {
465 tasks::Tasks::new(self.clone())
466 }
467
468 pub fn token(&self) -> token::Token {
470 token::Token::new(self.clone())
471 }
472
473 pub fn trash(&self) -> trash::Trash {
475 trash::Trash::new(self.clone())
476 }
477
478 pub fn ui_settings(&self) -> ui_settings::UiSettings {
480 ui_settings::UiSettings::new(self.clone())
481 }
482
483 pub fn users(&self) -> users::Users {
485 users::Users::new(self.clone())
486 }
487
488 pub fn workflow_actions(&self) -> workflow_actions::WorkflowActions {
490 workflow_actions::WorkflowActions::new(self.clone())
491 }
492
493 pub fn workflow_triggers(&self) -> workflow_triggers::WorkflowTriggers {
495 workflow_triggers::WorkflowTriggers::new(self.clone())
496 }
497
498 pub fn workflows(&self) -> workflows::Workflows {
500 workflows::Workflows::new(self.clone())
501 }
502}