paperless_api_client/
lib.rs

1//! Paperless-ngx API client
2//!
3//! [![docs.rs](https://docs.rs/paperless-api-client/badge.svg)](https://docs.rs/paperless-api-client)
4//!
5//! ## API Details
6//!
7//! OpenAPI Spec for Paperless-ngx
8//!
9//!
10//!
11//!
12//!
13//!
14//! ## Client Details
15//!
16//!
17//!
18//! The documentation for the crate is generated
19//! along with the code to make this library easy to use.
20//!
21//!
22//! To install the library, add the following to your `Cargo.toml` file.
23//!
24//! ```toml
25//! [dependencies]
26//! paperless-api-client = "6.0.0"
27//! ```
28//!
29//! ## Basic example
30//!
31//! Typical use will require intializing a `Client`. This requires
32//! a user agent string and set of credentials.
33//!
34//! ```rust,no_run
35//! use paperless_api_client::Client;
36//!
37//! let client = Client::new(
38//!     String::from("api-key"),
39//! );
40//! ```
41//!
42//! Alternatively, the library can search for most of the variables required for
43//! the client in the environment:
44//!
45//! - `PAPERLESS_API_CLIENT_API_TOKEN`
46//!
47//!
48//! And then you can create a client from the environment.
49//!
50//! ```rust,no_run
51//! use paperless_api_client::Client;
52//!
53//! let client = Client::new_from_env();
54//! ```
55//!
56#![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/// Entrypoint for interacting with the API client.
132#[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/// A request builder.
154#[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    /// Create a new Client struct. It takes a type that can convert into
164    /// an &str (`String` or `Vec<u8>` for example). As long as the function is
165    /// given a valid API key your requests will work.
166    /// Also takes reqwest client builders, for customizing the client's behaviour.
167    #[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            // Retry up to 3 times with increasing intervals between attempts.
180            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                        // Trace HTTP requests. See the tracing crate to make use of these traces.
186                        .with(reqwest_tracing::TracingMiddleware::default())
187                        // Retry failed requests.
188                        .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    /// Create a new Client struct. It takes a type that can convert into
227    /// an &str (`String` or `Vec<u8>` for example). As long as the function is
228    /// given a valid API key your requests will work.
229    /// Also takes reqwest client builders, for customizing the client's behaviour.
230    #[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            // Retry up to 3 times with increasing intervals between attempts.
239            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                        // Trace HTTP requests. See the tracing crate to make use of these traces.
245                        .with(reqwest_tracing::TracingMiddleware::default())
246                        // Retry failed requests.
247                        .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    /// Create a new Client struct. It takes a type that can convert into
277    /// an &str (`String` or `Vec<u8>` for example). As long as the function is
278    /// given a valid API key your requests will work.
279    #[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            // For file conversions we need this to be long.
288            .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            // For file conversions we need this to be long.
295            .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    /// Set the base URL for the client to something other than the default: <https://your-paperles.url/api>.
306    #[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    /// Create a new Client struct from the environment variable: `ENV_VARIABLE_PREFIX_API_TOKEN`.
315    #[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    /// Create a raw request to our API.
328    #[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        // Add in our authentication.
344        req = req.bearer_auth(&self.token);
345
346        // Set the default headers.
347        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    /// Return a reference to an interface that provides access to bulk_edit_objects operations.
364    pub fn bulk_edit_objects(&self) -> bulk_edit_objects::BulkEditObjects {
365        bulk_edit_objects::BulkEditObjects::new(self.clone())
366    }
367
368    /// Return a reference to an interface that provides access to config operations.
369    pub fn config(&self) -> config::Config {
370        config::Config::new(self.clone())
371    }
372
373    /// Return a reference to an interface that provides access to correspondents operations.
374    pub fn correspondents(&self) -> correspondents::Correspondents {
375        correspondents::Correspondents::new(self.clone())
376    }
377
378    /// Return a reference to an interface that provides access to custom_fields operations.
379    pub fn custom_fields(&self) -> custom_fields::CustomFields {
380        custom_fields::CustomFields::new(self.clone())
381    }
382
383    /// Return a reference to an interface that provides access to document_types operations.
384    pub fn document_types(&self) -> document_types::DocumentTypes {
385        document_types::DocumentTypes::new(self.clone())
386    }
387
388    /// Return a reference to an interface that provides access to documents operations.
389    pub fn documents(&self) -> documents::Documents {
390        documents::Documents::new(self.clone())
391    }
392
393    /// Return a reference to an interface that provides access to groups operations.
394    pub fn groups(&self) -> groups::Groups {
395        groups::Groups::new(self.clone())
396    }
397
398    /// Return a reference to an interface that provides access to logs operations.
399    pub fn logs(&self) -> logs::Logs {
400        logs::Logs::new(self.clone())
401    }
402
403    /// Return a reference to an interface that provides access to mail_accounts operations.
404    pub fn mail_accounts(&self) -> mail_accounts::MailAccounts {
405        mail_accounts::MailAccounts::new(self.clone())
406    }
407
408    /// Return a reference to an interface that provides access to mail_rules operations.
409    pub fn mail_rules(&self) -> mail_rules::MailRules {
410        mail_rules::MailRules::new(self.clone())
411    }
412
413    /// Return a reference to an interface that provides access to oauth operations.
414    pub fn oauth(&self) -> oauth::Oauth {
415        oauth::Oauth::new(self.clone())
416    }
417
418    /// Return a reference to an interface that provides access to profile operations.
419    pub fn profile(&self) -> profile::Profile {
420        profile::Profile::new(self.clone())
421    }
422
423    /// Return a reference to an interface that provides access to remote_version operations.
424    pub fn remote_version(&self) -> remote_version::RemoteVersion {
425        remote_version::RemoteVersion::new(self.clone())
426    }
427
428    /// Return a reference to an interface that provides access to saved_views operations.
429    pub fn saved_views(&self) -> saved_views::SavedViews {
430        saved_views::SavedViews::new(self.clone())
431    }
432
433    /// Return a reference to an interface that provides access to search operations.
434    pub fn search(&self) -> search::Search {
435        search::Search::new(self.clone())
436    }
437
438    /// Return a reference to an interface that provides access to share_links operations.
439    pub fn share_links(&self) -> share_links::ShareLinks {
440        share_links::ShareLinks::new(self.clone())
441    }
442
443    /// Return a reference to an interface that provides access to statistics operations.
444    pub fn statistics(&self) -> statistics::Statistics {
445        statistics::Statistics::new(self.clone())
446    }
447
448    /// Return a reference to an interface that provides access to status operations.
449    pub fn status(&self) -> status::Status {
450        status::Status::new(self.clone())
451    }
452
453    /// Return a reference to an interface that provides access to storage_paths operations.
454    pub fn storage_paths(&self) -> storage_paths::StoragePaths {
455        storage_paths::StoragePaths::new(self.clone())
456    }
457
458    /// Return a reference to an interface that provides access to tags operations.
459    pub fn tags(&self) -> tags::Tags {
460        tags::Tags::new(self.clone())
461    }
462
463    /// Return a reference to an interface that provides access to tasks operations.
464    pub fn tasks(&self) -> tasks::Tasks {
465        tasks::Tasks::new(self.clone())
466    }
467
468    /// Return a reference to an interface that provides access to token operations.
469    pub fn token(&self) -> token::Token {
470        token::Token::new(self.clone())
471    }
472
473    /// Return a reference to an interface that provides access to trash operations.
474    pub fn trash(&self) -> trash::Trash {
475        trash::Trash::new(self.clone())
476    }
477
478    /// Return a reference to an interface that provides access to ui_settings operations.
479    pub fn ui_settings(&self) -> ui_settings::UiSettings {
480        ui_settings::UiSettings::new(self.clone())
481    }
482
483    /// Return a reference to an interface that provides access to users operations.
484    pub fn users(&self) -> users::Users {
485        users::Users::new(self.clone())
486    }
487
488    /// Return a reference to an interface that provides access to workflow_actions operations.
489    pub fn workflow_actions(&self) -> workflow_actions::WorkflowActions {
490        workflow_actions::WorkflowActions::new(self.clone())
491    }
492
493    /// Return a reference to an interface that provides access to workflow_triggers operations.
494    pub fn workflow_triggers(&self) -> workflow_triggers::WorkflowTriggers {
495        workflow_triggers::WorkflowTriggers::new(self.clone())
496    }
497
498    /// Return a reference to an interface that provides access to workflows operations.
499    pub fn workflows(&self) -> workflows::Workflows {
500        workflows::Workflows::new(self.clone())
501    }
502}