Skip to main content

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.1"
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 processed_mail;
88#[cfg(feature = "requests")]
89pub mod profile;
90#[cfg(feature = "requests")]
91pub mod remote_version;
92#[cfg(feature = "requests")]
93pub mod saved_views;
94#[cfg(feature = "requests")]
95pub mod search;
96#[cfg(feature = "requests")]
97pub mod share_links;
98#[cfg(feature = "requests")]
99pub mod statistics;
100#[cfg(feature = "requests")]
101pub mod status;
102#[cfg(feature = "requests")]
103pub mod storage_paths;
104#[cfg(feature = "requests")]
105pub mod tags;
106#[cfg(feature = "requests")]
107pub mod tasks;
108#[cfg(test)]
109mod tests;
110#[cfg(feature = "requests")]
111pub mod token;
112#[cfg(feature = "requests")]
113pub mod trash;
114pub mod types;
115#[cfg(feature = "requests")]
116pub mod ui_settings;
117#[cfg(feature = "requests")]
118pub mod users;
119#[cfg(feature = "requests")]
120pub mod workflow_actions;
121#[cfg(feature = "requests")]
122pub mod workflow_triggers;
123#[cfg(feature = "requests")]
124pub mod workflows;
125
126#[cfg(feature = "requests")]
127use std::env;
128
129#[cfg(not(target_arch = "wasm32"))]
130#[cfg(feature = "requests")]
131static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
132
133/// Entrypoint for interacting with the API client.
134#[derive(Clone, Debug)]
135#[cfg(feature = "requests")]
136pub struct Client {
137    token: String,
138    base_url: String,
139
140    #[cfg(feature = "retry")]
141    client: reqwest_middleware::ClientWithMiddleware,
142    #[cfg(feature = "retry")]
143    #[cfg(not(target_arch = "wasm32"))]
144    #[allow(dead_code)]
145    client_http1_only: reqwest_middleware::ClientWithMiddleware,
146
147    #[cfg(not(feature = "retry"))]
148    client: reqwest::Client,
149    #[cfg(not(feature = "retry"))]
150    #[cfg(not(target_arch = "wasm32"))]
151    #[allow(dead_code)]
152    client_http1_only: reqwest::Client,
153}
154
155/// A request builder.
156#[cfg(feature = "retry")]
157#[cfg(feature = "requests")]
158pub struct RequestBuilder(pub reqwest_middleware::RequestBuilder);
159#[cfg(not(feature = "retry"))]
160#[cfg(feature = "requests")]
161pub struct RequestBuilder(pub reqwest::RequestBuilder);
162
163#[cfg(feature = "requests")]
164impl Client {
165    /// Create a new Client struct. It takes a type that can convert into
166    /// an &str (`String` or `Vec<u8>` for example). As long as the function is
167    /// given a valid API key your requests will work.
168    /// Also takes reqwest client builders, for customizing the client's behaviour.
169    #[tracing::instrument(skip(token))]
170    #[cfg(not(target_arch = "wasm32"))]
171    pub fn new_from_reqwest<T>(
172        token: T,
173        builder_http: reqwest::ClientBuilder,
174        builder_websocket: reqwest::ClientBuilder,
175    ) -> Self
176    where
177        T: ToString + std::fmt::Debug,
178    {
179        #[cfg(feature = "retry")]
180        {
181            // Retry up to 3 times with increasing intervals between attempts.
182            let retry_policy =
183                reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
184            match (builder_http.build(), builder_websocket.build()) {
185                (Ok(c), Ok(c1)) => {
186                    let client = reqwest_middleware::ClientBuilder::new(c)
187                        // Trace HTTP requests. See the tracing crate to make use of these traces.
188                        .with(reqwest_tracing::TracingMiddleware::default())
189                        // Retry failed requests.
190                        .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
191                            reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
192                            |req: &reqwest::Request| req.try_clone().is_some(),
193                        ))
194                        .build();
195                    let client_http1_only = reqwest_middleware::ClientBuilder::new(c1)
196                        .with(reqwest_tracing::TracingMiddleware::default())
197                        .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
198                            reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
199                            |req: &reqwest::Request| req.try_clone().is_some(),
200                        ))
201                        .build();
202                    Client {
203                        token: token.to_string(),
204                        base_url: "https://your-paperles.url/api".to_string(),
205
206                        client,
207                        client_http1_only,
208                    }
209                }
210                (Err(e), _) | (_, Err(e)) => panic!("creating reqwest client failed: {e:?}"),
211            }
212        }
213        #[cfg(not(feature = "retry"))]
214        {
215            match (builder_http.build(), builder_websocket.build()) {
216                (Ok(c), Ok(c1)) => Client {
217                    token: token.to_string(),
218                    base_url: "https://your-paperles.url/api".to_string(),
219
220                    client: c,
221                    client_http1_only: c1,
222                },
223                (Err(e), _) | (_, Err(e)) => panic!("creating reqwest client failed: {:?}", e),
224            }
225        }
226    }
227
228    /// Create a new Client struct. It takes a type that can convert into
229    /// an &str (`String` or `Vec<u8>` for example). As long as the function is
230    /// given a valid API key your requests will work.
231    /// Also takes reqwest client builders, for customizing the client's behaviour.
232    #[tracing::instrument(skip(token))]
233    #[cfg(target_arch = "wasm32")]
234    pub fn new_from_reqwest<T>(token: T, builder_http: reqwest::ClientBuilder) -> Self
235    where
236        T: ToString + std::fmt::Debug,
237    {
238        #[cfg(feature = "retry")]
239        {
240            // Retry up to 3 times with increasing intervals between attempts.
241            let retry_policy =
242                reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
243            match builder_http.build() {
244                Ok(c) => {
245                    let client = reqwest_middleware::ClientBuilder::new(c)
246                        // Trace HTTP requests. See the tracing crate to make use of these traces.
247                        .with(reqwest_tracing::TracingMiddleware::default())
248                        // Retry failed requests.
249                        .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
250                            reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
251                            |req: &reqwest::Request| req.try_clone().is_some(),
252                        ))
253                        .build();
254                    Client {
255                        token: token.to_string(),
256                        base_url: "https://your-paperles.url/api".to_string(),
257
258                        client,
259                    }
260                }
261                Err(e) => panic!("creating reqwest client failed: {:?}", e),
262            }
263        }
264        #[cfg(not(feature = "retry"))]
265        {
266            match builder_http.build() {
267                Ok(c) => Client {
268                    token: token.to_string(),
269                    base_url: "https://your-paperles.url/api".to_string(),
270
271                    client: c,
272                },
273                Err(e) => panic!("creating reqwest client failed: {:?}", e),
274            }
275        }
276    }
277
278    /// Create a new Client struct. It takes a type that can convert into
279    /// an &str (`String` or `Vec<u8>` for example). As long as the function is
280    /// given a valid API key your requests will work.
281    #[tracing::instrument(skip(token))]
282    pub fn new<T>(token: T) -> Self
283    where
284        T: ToString + std::fmt::Debug,
285    {
286        #[cfg(not(target_arch = "wasm32"))]
287        let client = reqwest::Client::builder()
288            .user_agent(APP_USER_AGENT)
289            // For file conversions we need this to be long.
290            .timeout(std::time::Duration::from_secs(600))
291            .connect_timeout(std::time::Duration::from_secs(60));
292        #[cfg(target_arch = "wasm32")]
293        let client = reqwest::Client::builder();
294        #[cfg(not(target_arch = "wasm32"))]
295        let client_http1 = reqwest::Client::builder()
296            // For file conversions we need this to be long.
297            .user_agent(APP_USER_AGENT)
298            .timeout(std::time::Duration::from_secs(600))
299            .connect_timeout(std::time::Duration::from_secs(60))
300            .http1_only();
301        #[cfg(not(target_arch = "wasm32"))]
302        return Self::new_from_reqwest(token, client, client_http1);
303        #[cfg(target_arch = "wasm32")]
304        Self::new_from_reqwest(token, client)
305    }
306
307    /// Set the base URL for the client to something other than the default: <https://your-paperles.url/api>.
308    #[tracing::instrument]
309    pub fn set_base_url<H>(&mut self, base_url: H)
310    where
311        H: Into<String> + std::fmt::Display + std::fmt::Debug,
312    {
313        self.base_url = base_url.to_string().trim_end_matches('/').to_string();
314    }
315
316    /// Create a new Client struct from the environment variable: `ENV_VARIABLE_PREFIX_API_TOKEN`.
317    #[tracing::instrument]
318    pub fn new_from_env() -> Self {
319        let token = env::var("PAPERLESS_API_CLIENT_API_TOKEN")
320            .expect("must set PAPERLESS_API_CLIENT_API_TOKEN");
321        let base_url = env::var("PAPERLESS_API_CLIENT_HOST")
322            .unwrap_or("https://your-paperles.url/api".to_string());
323
324        let mut c = Client::new(token);
325        c.set_base_url(base_url);
326        c
327    }
328
329    /// Create a raw request to our API.
330    #[tracing::instrument]
331    pub async fn request_raw(
332        &self,
333        method: reqwest::Method,
334        uri: &str,
335        body: Option<reqwest::Body>,
336    ) -> anyhow::Result<RequestBuilder> {
337        let u = if uri.starts_with("https://") || uri.starts_with("http://") {
338            uri.to_string()
339        } else {
340            format!("{}/{}", self.base_url, uri.trim_start_matches('/'))
341        };
342
343        let mut req = self.client.request(method, &u);
344
345        // Add in our authentication.
346        req = req.bearer_auth(&self.token);
347
348        // Set the default headers.
349        req = req.header(
350            reqwest::header::ACCEPT,
351            reqwest::header::HeaderValue::from_static("application/json"),
352        );
353        req = req.header(
354            reqwest::header::CONTENT_TYPE,
355            reqwest::header::HeaderValue::from_static("application/json"),
356        );
357
358        if let Some(body) = body {
359            req = req.body(body);
360        }
361
362        Ok(RequestBuilder(req))
363    }
364
365    /// Return a reference to an interface that provides access to bulk_edit_objects operations.
366    pub fn bulk_edit_objects(&self) -> bulk_edit_objects::BulkEditObjects {
367        bulk_edit_objects::BulkEditObjects::new(self.clone())
368    }
369
370    /// Return a reference to an interface that provides access to config operations.
371    pub fn config(&self) -> config::Config {
372        config::Config::new(self.clone())
373    }
374
375    /// Return a reference to an interface that provides access to correspondents operations.
376    pub fn correspondents(&self) -> correspondents::Correspondents {
377        correspondents::Correspondents::new(self.clone())
378    }
379
380    /// Return a reference to an interface that provides access to custom_fields operations.
381    pub fn custom_fields(&self) -> custom_fields::CustomFields {
382        custom_fields::CustomFields::new(self.clone())
383    }
384
385    /// Return a reference to an interface that provides access to document_types operations.
386    pub fn document_types(&self) -> document_types::DocumentTypes {
387        document_types::DocumentTypes::new(self.clone())
388    }
389
390    /// Return a reference to an interface that provides access to documents operations.
391    pub fn documents(&self) -> documents::Documents {
392        documents::Documents::new(self.clone())
393    }
394
395    /// Return a reference to an interface that provides access to groups operations.
396    pub fn groups(&self) -> groups::Groups {
397        groups::Groups::new(self.clone())
398    }
399
400    /// Return a reference to an interface that provides access to logs operations.
401    pub fn logs(&self) -> logs::Logs {
402        logs::Logs::new(self.clone())
403    }
404
405    /// Return a reference to an interface that provides access to mail_accounts operations.
406    pub fn mail_accounts(&self) -> mail_accounts::MailAccounts {
407        mail_accounts::MailAccounts::new(self.clone())
408    }
409
410    /// Return a reference to an interface that provides access to mail_rules operations.
411    pub fn mail_rules(&self) -> mail_rules::MailRules {
412        mail_rules::MailRules::new(self.clone())
413    }
414
415    /// Return a reference to an interface that provides access to oauth operations.
416    pub fn oauth(&self) -> oauth::Oauth {
417        oauth::Oauth::new(self.clone())
418    }
419
420    /// Return a reference to an interface that provides access to processed_mail operations.
421    pub fn processed_mail(&self) -> processed_mail::ProcessedMail {
422        processed_mail::ProcessedMail::new(self.clone())
423    }
424
425    /// Return a reference to an interface that provides access to profile operations.
426    pub fn profile(&self) -> profile::Profile {
427        profile::Profile::new(self.clone())
428    }
429
430    /// Return a reference to an interface that provides access to remote_version operations.
431    pub fn remote_version(&self) -> remote_version::RemoteVersion {
432        remote_version::RemoteVersion::new(self.clone())
433    }
434
435    /// Return a reference to an interface that provides access to saved_views operations.
436    pub fn saved_views(&self) -> saved_views::SavedViews {
437        saved_views::SavedViews::new(self.clone())
438    }
439
440    /// Return a reference to an interface that provides access to search operations.
441    pub fn search(&self) -> search::Search {
442        search::Search::new(self.clone())
443    }
444
445    /// Return a reference to an interface that provides access to share_links operations.
446    pub fn share_links(&self) -> share_links::ShareLinks {
447        share_links::ShareLinks::new(self.clone())
448    }
449
450    /// Return a reference to an interface that provides access to statistics operations.
451    pub fn statistics(&self) -> statistics::Statistics {
452        statistics::Statistics::new(self.clone())
453    }
454
455    /// Return a reference to an interface that provides access to status operations.
456    pub fn status(&self) -> status::Status {
457        status::Status::new(self.clone())
458    }
459
460    /// Return a reference to an interface that provides access to storage_paths operations.
461    pub fn storage_paths(&self) -> storage_paths::StoragePaths {
462        storage_paths::StoragePaths::new(self.clone())
463    }
464
465    /// Return a reference to an interface that provides access to tags operations.
466    pub fn tags(&self) -> tags::Tags {
467        tags::Tags::new(self.clone())
468    }
469
470    /// Return a reference to an interface that provides access to tasks operations.
471    pub fn tasks(&self) -> tasks::Tasks {
472        tasks::Tasks::new(self.clone())
473    }
474
475    /// Return a reference to an interface that provides access to token operations.
476    pub fn token(&self) -> token::Token {
477        token::Token::new(self.clone())
478    }
479
480    /// Return a reference to an interface that provides access to trash operations.
481    pub fn trash(&self) -> trash::Trash {
482        trash::Trash::new(self.clone())
483    }
484
485    /// Return a reference to an interface that provides access to ui_settings operations.
486    pub fn ui_settings(&self) -> ui_settings::UiSettings {
487        ui_settings::UiSettings::new(self.clone())
488    }
489
490    /// Return a reference to an interface that provides access to users operations.
491    pub fn users(&self) -> users::Users {
492        users::Users::new(self.clone())
493    }
494
495    /// Return a reference to an interface that provides access to workflow_actions operations.
496    pub fn workflow_actions(&self) -> workflow_actions::WorkflowActions {
497        workflow_actions::WorkflowActions::new(self.clone())
498    }
499
500    /// Return a reference to an interface that provides access to workflow_triggers operations.
501    pub fn workflow_triggers(&self) -> workflow_triggers::WorkflowTriggers {
502        workflow_triggers::WorkflowTriggers::new(self.clone())
503    }
504
505    /// Return a reference to an interface that provides access to workflows operations.
506    pub fn workflows(&self) -> workflows::Workflows {
507        workflows::Workflows::new(self.clone())
508    }
509}