Skip to main content

kittycad/
lib.rs

1//! A fully generated & opinionated API client for the KittyCAD API.
2//!
3//! [![docs.rs](https://docs.rs/kittycad/badge.svg)](https://docs.rs/kittycad)
4//!
5//! ## API Details
6//!
7//! API server for Zoo
8//!
9//!
10//!
11//! ### Contact
12//!
13//!
14//! | url | email |
15//! |----|----|
16//! | <https://zoo.dev> | api@zoo.dev |
17//!
18//!
19//!
20//! ## Client Details
21//!
22//! This client is generated from the [OpenAPI specs](https://api.zoo.dev) based on API spec version `0.1.0`. This way it will remain up to date as features are added.
23//!
24//! The documentation for the crate is generated
25//! along with the code to make this library easy to use.
26//!
27//!
28//! To install the library, add the following to your `Cargo.toml` file.
29//!
30//! ```toml
31//! [dependencies]
32//! kittycad = "0.4.9"
33//! ```
34//!
35//! ## Basic example
36//!
37//! Typical use will require intializing a `Client`. This requires
38//! a user agent string and set of credentials.
39//!
40//! ```rust,no_run
41//! use kittycad::Client;
42//!
43//! let client = Client::new(String::from("api-key"));
44//! ```
45//!
46//! Alternatively, the library can search for most of the variables required for
47//! the client in the environment:
48//!
49//! - `KITTYCAD_API_TOKEN`
50//! - `ZOO_API_TOKEN`
51//!
52//! And then you can create a client from the environment.
53//!
54//! ```rust,no_run
55//! use kittycad::Client;
56//!
57//! let client = Client::new_from_env();
58//! ```
59#![allow(mismatched_lifetime_syntaxes)]
60#![allow(missing_docs)]
61#![allow(unused_imports)]
62#![allow(clippy::needless_lifetimes)]
63#![allow(clippy::too_many_arguments)]
64#![cfg_attr(docsrs, feature(doc_cfg))]
65
66/// API calls that have been performed by users can be queried by the API. This is helpful for debugging as well as billing.
67///
68/// FROM: <https://zoo.dev/docs/api/api-calls>
69#[cfg(feature = "requests")]
70pub mod api_calls;
71/// API tokens allow users to call the API outside of their session token that is used as a cookie in the user interface. Users can create, delete, and list their API tokens. But, of course, you need an API token to do this, so first be sure to generate one in the account UI.
72///
73/// FROM: <https://zoo.dev/docs/api/api-tokens>
74#[cfg(feature = "requests")]
75pub mod api_tokens;
76/// Endpoints for third party app grant flows.
77///
78/// FROM: <https://zoo.dev/docs/api/apps>
79#[cfg(feature = "requests")]
80pub mod apps;
81/// Endpoints that allow for code execution or creation of code execution environments.
82///
83/// FROM: <https://zoo.dev/docs/api/executor>
84#[cfg(feature = "requests")]
85pub mod executor;
86/// CAD file operations. Create, get, and list CAD file conversions. More endpoints will be added here in the future as we build out transforms, etc on CAD models.
87///
88/// FROM: <https://zoo.dev/docs/api/file>
89#[cfg(feature = "requests")]
90pub mod file;
91/// Hidden API endpoints that should not show up in the docs.
92///
93/// FROM: <https://zoo.dev/docs/api/hidden>
94#[cfg(feature = "requests")]
95pub mod hidden;
96/// Meta information about the API.
97///
98/// FROM: <https://zoo.dev/docs/api/meta>
99#[cfg(feature = "requests")]
100pub mod meta;
101mod methods;
102/// Machine learning to generate CAD models and other things.
103///
104/// FROM: <https://zoo.dev/docs/api/ml>
105#[cfg(feature = "requests")]
106pub mod ml;
107/// Modeling API for updating your 3D files using the Zoo engine.
108///
109/// FROM: <https://zoo.dev/docs/api/modeling>
110#[cfg(feature = "requests")]
111pub mod modeling;
112/// Endpoints that implement OAuth 2.0 grant flows.
113///
114/// FROM: <https://zoo.dev/docs/api/oauth2>
115#[cfg(feature = "requests")]
116pub mod oauth2;
117/// An organization is a group of users of the Zoo API. Here, we can add users to an org and perform operations on orgs.
118///
119/// FROM: <https://zoo.dev/docs/api/orgs>
120#[cfg(feature = "requests")]
121pub mod orgs;
122/// Operations around payments and billing.
123///
124/// FROM: <https://zoo.dev/docs/api/payments>
125#[cfg(feature = "requests")]
126pub mod payments;
127/// Service accounts allow organizations to call the API. Organization admins can create, delete, and list the service accounts for their org. Service accounts are scoped to an organization not individual users, these are better to use for automations than individual API tokens, since they won't stop working when an individual leaves the company.
128///
129/// FROM: <https://zoo.dev/docs/api/service-accounts>
130#[cfg(feature = "requests")]
131pub mod service_accounts;
132/// Operations involving our swag store.
133///
134/// FROM: <https://zoo.dev/docs/api/store>
135#[cfg(feature = "requests")]
136pub mod store;
137#[cfg(test)]
138mod tests;
139pub mod types;
140/// Unit conversion operations.
141///
142/// FROM: <https://zoo.dev/docs/api/file>
143#[cfg(feature = "requests")]
144pub mod unit;
145/// A user is someone who uses the Zoo API. Here, we can create, delete, and list users. We can also get information about a user. Operations will only be authorized if the user is requesting information about themselves.
146///
147/// FROM: <https://zoo.dev/docs/api/users>
148#[cfg(feature = "requests")]
149pub mod users;
150
151#[cfg(feature = "requests")]
152use std::env;
153
154#[cfg(not(target_arch = "wasm32"))]
155#[cfg(feature = "requests")]
156static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
157
158/// Entrypoint for interacting with the API client.
159#[derive(Clone, Debug)]
160#[cfg(feature = "requests")]
161pub struct Client {
162    token: String,
163    base_url: String,
164
165    #[cfg(feature = "retry")]
166    client: reqwest_middleware::ClientWithMiddleware,
167    #[cfg(feature = "retry")]
168    #[cfg(not(target_arch = "wasm32"))]
169    #[allow(dead_code)]
170    client_http1_only: reqwest_middleware::ClientWithMiddleware,
171
172    #[cfg(not(feature = "retry"))]
173    client: reqwest::Client,
174    #[cfg(not(feature = "retry"))]
175    #[cfg(not(target_arch = "wasm32"))]
176    #[allow(dead_code)]
177    client_http1_only: reqwest::Client,
178}
179
180/// A request builder.
181#[cfg(feature = "retry")]
182#[cfg(feature = "requests")]
183pub struct RequestBuilder(pub reqwest_middleware::RequestBuilder);
184#[cfg(not(feature = "retry"))]
185#[cfg(feature = "requests")]
186pub struct RequestBuilder(pub reqwest::RequestBuilder);
187
188#[cfg(feature = "requests")]
189impl Client {
190    /// Create a new Client struct. It takes a type that can convert into
191    /// an &str (`String` or `Vec<u8>` for example). As long as the function is
192    /// given a valid API key your requests will work.
193    /// Also takes reqwest client builders, for customizing the client's behaviour.
194    #[tracing::instrument(skip(token))]
195    #[cfg(not(target_arch = "wasm32"))]
196    pub fn new_from_reqwest<T>(
197        token: T,
198        builder_http: reqwest::ClientBuilder,
199        builder_websocket: reqwest::ClientBuilder,
200    ) -> Self
201    where
202        T: ToString + std::fmt::Debug,
203    {
204        #[cfg(feature = "retry")]
205        {
206            // Retry up to 3 times with increasing intervals between attempts.
207            let retry_policy =
208                reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
209            match (builder_http.build(), builder_websocket.build()) {
210                (Ok(c), Ok(c1)) => {
211                    let client = reqwest_middleware::ClientBuilder::new(c)
212                        // Trace HTTP requests. See the tracing crate to make use of these traces.
213                        .with(reqwest_tracing::TracingMiddleware::default())
214                        // Retry failed requests.
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                    let client_http1_only = reqwest_middleware::ClientBuilder::new(c1)
221                        .with(reqwest_tracing::TracingMiddleware::default())
222                        .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
223                            reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
224                            |req: &reqwest::Request| req.try_clone().is_some(),
225                        ))
226                        .build();
227                    Client {
228                        token: token.to_string(),
229                        base_url: "https://api.zoo.dev".to_string(),
230
231                        client,
232                        client_http1_only,
233                    }
234                }
235                (Err(e), _) | (_, Err(e)) => panic!("creating reqwest client failed: {:?}", e),
236            }
237        }
238        #[cfg(not(feature = "retry"))]
239        {
240            match (builder_http.build(), builder_websocket.build()) {
241                (Ok(c), Ok(c1)) => Client {
242                    token: token.to_string(),
243                    base_url: "https://api.zoo.dev".to_string(),
244
245                    client: c,
246                    client_http1_only: c1,
247                },
248                (Err(e), _) | (_, Err(e)) => panic!("creating reqwest client failed: {:?}", e),
249            }
250        }
251    }
252
253    /// Create a new Client struct. It takes a type that can convert into
254    /// an &str (`String` or `Vec<u8>` for example). As long as the function is
255    /// given a valid API key your requests will work.
256    /// Also takes reqwest client builders, for customizing the client's behaviour.
257    #[tracing::instrument(skip(token))]
258    #[cfg(target_arch = "wasm32")]
259    pub fn new_from_reqwest<T>(token: T, builder_http: reqwest::ClientBuilder) -> Self
260    where
261        T: ToString + std::fmt::Debug,
262    {
263        #[cfg(feature = "retry")]
264        {
265            // Retry up to 3 times with increasing intervals between attempts.
266            let retry_policy =
267                reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
268            match builder_http.build() {
269                Ok(c) => {
270                    let client = reqwest_middleware::ClientBuilder::new(c)
271                        // Trace HTTP requests. See the tracing crate to make use of these traces.
272                        .with(reqwest_tracing::TracingMiddleware::default())
273                        // Retry failed requests.
274                        .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
275                            reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
276                            |req: &reqwest::Request| req.try_clone().is_some(),
277                        ))
278                        .build();
279                    Client {
280                        token: token.to_string(),
281                        base_url: "https://api.zoo.dev".to_string(),
282
283                        client,
284                    }
285                }
286                Err(e) => panic!("creating reqwest client failed: {:?}", e),
287            }
288        }
289        #[cfg(not(feature = "retry"))]
290        {
291            match builder_http.build() {
292                Ok(c) => Client {
293                    token: token.to_string(),
294                    base_url: "https://api.zoo.dev".to_string(),
295
296                    client: c,
297                },
298                Err(e) => panic!("creating reqwest client failed: {:?}", e),
299            }
300        }
301    }
302
303    /// Create a new Client struct. It takes a type that can convert into
304    /// an &str (`String` or `Vec<u8>` for example). As long as the function is
305    /// given a valid API key your requests will work.
306    #[tracing::instrument(skip(token))]
307    pub fn new<T>(token: T) -> Self
308    where
309        T: ToString + std::fmt::Debug,
310    {
311        #[cfg(not(target_arch = "wasm32"))]
312        let client = reqwest::Client::builder()
313            .user_agent(APP_USER_AGENT)
314            // For file conversions we need this to be long.
315            .timeout(std::time::Duration::from_secs(600))
316            .connect_timeout(std::time::Duration::from_secs(60));
317        #[cfg(target_arch = "wasm32")]
318        let client = reqwest::Client::builder();
319        #[cfg(not(target_arch = "wasm32"))]
320        let client_http1 = reqwest::Client::builder()
321            // For file conversions we need this to be long.
322            .user_agent(APP_USER_AGENT)
323            .timeout(std::time::Duration::from_secs(600))
324            .connect_timeout(std::time::Duration::from_secs(60))
325            .http1_only();
326        #[cfg(not(target_arch = "wasm32"))]
327        return Self::new_from_reqwest(token, client, client_http1);
328        #[cfg(target_arch = "wasm32")]
329        Self::new_from_reqwest(token, client)
330    }
331
332    /// Set the base URL for the client to something other than the default: <https://api.zoo.dev>.
333    #[tracing::instrument]
334    pub fn set_base_url<H>(&mut self, base_url: H)
335    where
336        H: Into<String> + std::fmt::Display + std::fmt::Debug,
337    {
338        self.base_url = base_url.to_string().trim_end_matches('/').to_string();
339    }
340
341    /// Create a new Client struct from the environment variable: `ENV_VARIABLE_PREFIX_API_TOKEN`.
342    #[tracing::instrument]
343    pub fn new_from_env() -> Self {
344        let token = if let Ok(token) = env::var("KITTYCAD_API_TOKEN") {
345            token
346        } else if let Ok(token) = env::var("ZOO_API_TOKEN") {
347            token
348        } else {
349            panic!("must set KITTYCAD_API_TOKEN or ZOO_API_TOKEN");
350        };
351        let base_url = if let Ok(base_url) = env::var("KITTYCAD_HOST") {
352            base_url
353        } else if let Ok(base_url) = env::var("ZOO_HOST") {
354            base_url
355        } else {
356            "https://api.zoo.dev".to_string()
357        };
358
359        let mut c = Client::new(token);
360        c.set_base_url(base_url);
361        c
362    }
363
364    /// Create a raw request to our API.
365    #[tracing::instrument]
366    pub async fn request_raw(
367        &self,
368        method: reqwest::Method,
369        uri: &str,
370        body: Option<reqwest::Body>,
371    ) -> anyhow::Result<RequestBuilder> {
372        let u = if uri.starts_with("https://") || uri.starts_with("http://") {
373            uri.to_string()
374        } else {
375            format!("{}/{}", self.base_url, uri.trim_start_matches('/'))
376        };
377
378        let mut req = self.client.request(method, &u);
379
380        // Add in our authentication.
381        req = req.bearer_auth(&self.token);
382
383        // Set the default headers.
384        req = req.header(
385            reqwest::header::ACCEPT,
386            reqwest::header::HeaderValue::from_static("application/json"),
387        );
388        req = req.header(
389            reqwest::header::CONTENT_TYPE,
390            reqwest::header::HeaderValue::from_static("application/json"),
391        );
392
393        if let Some(body) = body {
394            req = req.body(body);
395        }
396
397        Ok(RequestBuilder(req))
398    }
399
400    /// API calls that have been performed by users can be queried by the API. This is helpful for debugging as well as billing.
401    ///
402    /// FROM: <https://zoo.dev/docs/api/api-calls>
403    pub fn api_calls(&self) -> api_calls::ApiCalls {
404        api_calls::ApiCalls::new(self.clone())
405    }
406
407    /// API tokens allow users to call the API outside of their session token that is used as a cookie in the user interface. Users can create, delete, and list their API tokens. But, of course, you need an API token to do this, so first be sure to generate one in the account UI.
408    ///
409    /// FROM: <https://zoo.dev/docs/api/api-tokens>
410    pub fn api_tokens(&self) -> api_tokens::ApiTokens {
411        api_tokens::ApiTokens::new(self.clone())
412    }
413
414    /// Endpoints for third party app grant flows.
415    ///
416    /// FROM: <https://zoo.dev/docs/api/apps>
417    pub fn apps(&self) -> apps::Apps {
418        apps::Apps::new(self.clone())
419    }
420
421    /// Endpoints that allow for code execution or creation of code execution environments.
422    ///
423    /// FROM: <https://zoo.dev/docs/api/executor>
424    pub fn executor(&self) -> executor::Executor {
425        executor::Executor::new(self.clone())
426    }
427
428    /// CAD file operations. Create, get, and list CAD file conversions. More endpoints will be added here in the future as we build out transforms, etc on CAD models.
429    ///
430    /// FROM: <https://zoo.dev/docs/api/file>
431    pub fn file(&self) -> file::File {
432        file::File::new(self.clone())
433    }
434
435    /// Hidden API endpoints that should not show up in the docs.
436    ///
437    /// FROM: <https://zoo.dev/docs/api/hidden>
438    pub fn hidden(&self) -> hidden::Hidden {
439        hidden::Hidden::new(self.clone())
440    }
441
442    /// Meta information about the API.
443    ///
444    /// FROM: <https://zoo.dev/docs/api/meta>
445    pub fn meta(&self) -> meta::Meta {
446        meta::Meta::new(self.clone())
447    }
448
449    /// Machine learning to generate CAD models and other things.
450    ///
451    /// FROM: <https://zoo.dev/docs/api/ml>
452    pub fn ml(&self) -> ml::Ml {
453        ml::Ml::new(self.clone())
454    }
455
456    /// Modeling API for updating your 3D files using the Zoo engine.
457    ///
458    /// FROM: <https://zoo.dev/docs/api/modeling>
459    pub fn modeling(&self) -> modeling::Modeling {
460        modeling::Modeling::new(self.clone())
461    }
462
463    /// Endpoints that implement OAuth 2.0 grant flows.
464    ///
465    /// FROM: <https://zoo.dev/docs/api/oauth2>
466    pub fn oauth2(&self) -> oauth2::Oauth2 {
467        oauth2::Oauth2::new(self.clone())
468    }
469
470    /// An organization is a group of users of the Zoo API. Here, we can add users to an org and perform operations on orgs.
471    ///
472    /// FROM: <https://zoo.dev/docs/api/orgs>
473    pub fn orgs(&self) -> orgs::Orgs {
474        orgs::Orgs::new(self.clone())
475    }
476
477    /// Operations around payments and billing.
478    ///
479    /// FROM: <https://zoo.dev/docs/api/payments>
480    pub fn payments(&self) -> payments::Payments {
481        payments::Payments::new(self.clone())
482    }
483
484    /// Service accounts allow organizations to call the API. Organization admins can create, delete, and list the service accounts for their org. Service accounts are scoped to an organization not individual users, these are better to use for automations than individual API tokens, since they won't stop working when an individual leaves the company.
485    ///
486    /// FROM: <https://zoo.dev/docs/api/service-accounts>
487    pub fn service_accounts(&self) -> service_accounts::ServiceAccounts {
488        service_accounts::ServiceAccounts::new(self.clone())
489    }
490
491    /// Operations involving our swag store.
492    ///
493    /// FROM: <https://zoo.dev/docs/api/store>
494    pub fn store(&self) -> store::Store {
495        store::Store::new(self.clone())
496    }
497
498    /// Unit conversion operations.
499    ///
500    /// FROM: <https://zoo.dev/docs/api/file>
501    pub fn unit(&self) -> unit::Unit {
502        unit::Unit::new(self.clone())
503    }
504
505    /// A user is someone who uses the Zoo API. Here, we can create, delete, and list users. We can also get information about a user. Operations will only be authorized if the user is requesting information about themselves.
506    ///
507    /// FROM: <https://zoo.dev/docs/api/users>
508    pub fn users(&self) -> users::Users {
509        users::Users::new(self.clone())
510    }
511}