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.10"
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/// Operations for user-owned projects, public project discovery, publishing, voting, and share links.
128///
129/// FROM: <https://zoo.dev/docs/api/projects>
130#[cfg(feature = "requests")]
131pub mod projects;
132/// 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.
133///
134/// FROM: <https://zoo.dev/docs/api/service-accounts>
135#[cfg(feature = "requests")]
136pub mod service_accounts;
137/// Operations involving our swag store.
138///
139/// FROM: <https://zoo.dev/docs/api/store>
140#[cfg(feature = "requests")]
141pub mod store;
142#[cfg(test)]
143mod tests;
144pub mod types;
145/// Unit conversion operations.
146///
147/// FROM: <https://zoo.dev/docs/api/file>
148#[cfg(feature = "requests")]
149pub mod unit;
150/// 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.
151///
152/// FROM: <https://zoo.dev/docs/api/users>
153#[cfg(feature = "requests")]
154pub mod users;
155
156#[cfg(feature = "requests")]
157use std::env;
158
159#[cfg(not(target_arch = "wasm32"))]
160#[cfg(feature = "requests")]
161static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
162
163/// Entrypoint for interacting with the API client.
164#[derive(Clone, Debug)]
165#[cfg(feature = "requests")]
166pub struct Client {
167    token: String,
168    base_url: String,
169
170    #[cfg(feature = "retry")]
171    client: reqwest_middleware::ClientWithMiddleware,
172    #[cfg(feature = "retry")]
173    #[cfg(not(target_arch = "wasm32"))]
174    #[allow(dead_code)]
175    client_http1_only: reqwest_middleware::ClientWithMiddleware,
176
177    #[cfg(not(feature = "retry"))]
178    client: reqwest::Client,
179    #[cfg(not(feature = "retry"))]
180    #[cfg(not(target_arch = "wasm32"))]
181    #[allow(dead_code)]
182    client_http1_only: reqwest::Client,
183}
184
185/// A request builder.
186#[cfg(feature = "retry")]
187#[cfg(feature = "requests")]
188pub struct RequestBuilder(pub reqwest_middleware::RequestBuilder);
189#[cfg(not(feature = "retry"))]
190#[cfg(feature = "requests")]
191pub struct RequestBuilder(pub reqwest::RequestBuilder);
192
193#[cfg(feature = "requests")]
194impl Client {
195    /// Create a new Client struct. It takes a type that can convert into
196    /// an &str (`String` or `Vec<u8>` for example). As long as the function is
197    /// given a valid API key your requests will work.
198    /// Also takes reqwest client builders, for customizing the client's behaviour.
199    #[tracing::instrument(skip(token))]
200    #[cfg(not(target_arch = "wasm32"))]
201    pub fn new_from_reqwest<T>(
202        token: T,
203        builder_http: reqwest::ClientBuilder,
204        builder_websocket: reqwest::ClientBuilder,
205    ) -> Self
206    where
207        T: ToString + std::fmt::Debug,
208    {
209        #[cfg(feature = "retry")]
210        {
211            // Retry up to 3 times with increasing intervals between attempts.
212            let retry_policy =
213                reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
214            match (builder_http.build(), builder_websocket.build()) {
215                (Ok(c), Ok(c1)) => {
216                    let client = reqwest_middleware::ClientBuilder::new(c)
217                        // Trace HTTP requests. See the tracing crate to make use of these traces.
218                        .with(reqwest_tracing::TracingMiddleware::default())
219                        // Retry failed requests.
220                        .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
221                            reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
222                            |req: &reqwest::Request| req.try_clone().is_some(),
223                        ))
224                        .build();
225                    let client_http1_only = reqwest_middleware::ClientBuilder::new(c1)
226                        .with(reqwest_tracing::TracingMiddleware::default())
227                        .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
228                            reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
229                            |req: &reqwest::Request| req.try_clone().is_some(),
230                        ))
231                        .build();
232                    Client {
233                        token: token.to_string(),
234                        base_url: "https://api.zoo.dev".to_string(),
235
236                        client,
237                        client_http1_only,
238                    }
239                }
240                (Err(e), _) | (_, Err(e)) => panic!("creating reqwest client failed: {:?}", e),
241            }
242        }
243        #[cfg(not(feature = "retry"))]
244        {
245            match (builder_http.build(), builder_websocket.build()) {
246                (Ok(c), Ok(c1)) => Client {
247                    token: token.to_string(),
248                    base_url: "https://api.zoo.dev".to_string(),
249
250                    client: c,
251                    client_http1_only: c1,
252                },
253                (Err(e), _) | (_, Err(e)) => panic!("creating reqwest client failed: {:?}", e),
254            }
255        }
256    }
257
258    /// Create a new Client struct. It takes a type that can convert into
259    /// an &str (`String` or `Vec<u8>` for example). As long as the function is
260    /// given a valid API key your requests will work.
261    /// Also takes reqwest client builders, for customizing the client's behaviour.
262    #[tracing::instrument(skip(token))]
263    #[cfg(target_arch = "wasm32")]
264    pub fn new_from_reqwest<T>(token: T, builder_http: reqwest::ClientBuilder) -> Self
265    where
266        T: ToString + std::fmt::Debug,
267    {
268        #[cfg(feature = "retry")]
269        {
270            // Retry up to 3 times with increasing intervals between attempts.
271            let retry_policy =
272                reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
273            match builder_http.build() {
274                Ok(c) => {
275                    let client = reqwest_middleware::ClientBuilder::new(c)
276                        // Trace HTTP requests. See the tracing crate to make use of these traces.
277                        .with(reqwest_tracing::TracingMiddleware::default())
278                        // Retry failed requests.
279                        .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
280                            reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
281                            |req: &reqwest::Request| req.try_clone().is_some(),
282                        ))
283                        .build();
284                    Client {
285                        token: token.to_string(),
286                        base_url: "https://api.zoo.dev".to_string(),
287
288                        client,
289                    }
290                }
291                Err(e) => panic!("creating reqwest client failed: {:?}", e),
292            }
293        }
294        #[cfg(not(feature = "retry"))]
295        {
296            match builder_http.build() {
297                Ok(c) => Client {
298                    token: token.to_string(),
299                    base_url: "https://api.zoo.dev".to_string(),
300
301                    client: c,
302                },
303                Err(e) => panic!("creating reqwest client failed: {:?}", e),
304            }
305        }
306    }
307
308    /// Create a new Client struct. It takes a type that can convert into
309    /// an &str (`String` or `Vec<u8>` for example). As long as the function is
310    /// given a valid API key your requests will work.
311    #[tracing::instrument(skip(token))]
312    pub fn new<T>(token: T) -> Self
313    where
314        T: ToString + std::fmt::Debug,
315    {
316        #[cfg(not(target_arch = "wasm32"))]
317        let client = reqwest::Client::builder()
318            .user_agent(APP_USER_AGENT)
319            // For file conversions we need this to be long.
320            .timeout(std::time::Duration::from_secs(600))
321            .connect_timeout(std::time::Duration::from_secs(60));
322        #[cfg(target_arch = "wasm32")]
323        let client = reqwest::Client::builder();
324        #[cfg(not(target_arch = "wasm32"))]
325        let client_http1 = reqwest::Client::builder()
326            // For file conversions we need this to be long.
327            .user_agent(APP_USER_AGENT)
328            .timeout(std::time::Duration::from_secs(600))
329            .connect_timeout(std::time::Duration::from_secs(60))
330            .http1_only();
331        #[cfg(not(target_arch = "wasm32"))]
332        return Self::new_from_reqwest(token, client, client_http1);
333        #[cfg(target_arch = "wasm32")]
334        Self::new_from_reqwest(token, client)
335    }
336
337    /// Set the base URL for the client to something other than the default: <https://api.zoo.dev>.
338    #[tracing::instrument]
339    pub fn set_base_url<H>(&mut self, base_url: H)
340    where
341        H: Into<String> + std::fmt::Display + std::fmt::Debug,
342    {
343        self.base_url = base_url.to_string().trim_end_matches('/').to_string();
344    }
345
346    /// Create a new Client struct from the environment variable: `ENV_VARIABLE_PREFIX_API_TOKEN`.
347    #[tracing::instrument]
348    pub fn new_from_env() -> Self {
349        let token = if let Ok(token) = env::var("KITTYCAD_API_TOKEN") {
350            token
351        } else if let Ok(token) = env::var("ZOO_API_TOKEN") {
352            token
353        } else {
354            panic!("must set KITTYCAD_API_TOKEN or ZOO_API_TOKEN");
355        };
356        let base_url = if let Ok(base_url) = env::var("KITTYCAD_HOST") {
357            base_url
358        } else if let Ok(base_url) = env::var("ZOO_HOST") {
359            base_url
360        } else {
361            "https://api.zoo.dev".to_string()
362        };
363
364        let mut c = Client::new(token);
365        c.set_base_url(base_url);
366        c
367    }
368
369    /// Create a raw request to our API.
370    #[tracing::instrument]
371    pub async fn request_raw(
372        &self,
373        method: reqwest::Method,
374        uri: &str,
375        body: Option<reqwest::Body>,
376    ) -> anyhow::Result<RequestBuilder> {
377        let u = if uri.starts_with("https://") || uri.starts_with("http://") {
378            uri.to_string()
379        } else {
380            format!("{}/{}", self.base_url, uri.trim_start_matches('/'))
381        };
382
383        let mut req = self.client.request(method, &u);
384
385        // Add in our authentication.
386        req = req.bearer_auth(&self.token);
387
388        // Set the default headers.
389        req = req.header(
390            reqwest::header::ACCEPT,
391            reqwest::header::HeaderValue::from_static("application/json"),
392        );
393        req = req.header(
394            reqwest::header::CONTENT_TYPE,
395            reqwest::header::HeaderValue::from_static("application/json"),
396        );
397
398        if let Some(body) = body {
399            req = req.body(body);
400        }
401
402        Ok(RequestBuilder(req))
403    }
404
405    /// API calls that have been performed by users can be queried by the API. This is helpful for debugging as well as billing.
406    ///
407    /// FROM: <https://zoo.dev/docs/api/api-calls>
408    pub fn api_calls(&self) -> api_calls::ApiCalls {
409        api_calls::ApiCalls::new(self.clone())
410    }
411
412    /// 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.
413    ///
414    /// FROM: <https://zoo.dev/docs/api/api-tokens>
415    pub fn api_tokens(&self) -> api_tokens::ApiTokens {
416        api_tokens::ApiTokens::new(self.clone())
417    }
418
419    /// Endpoints for third party app grant flows.
420    ///
421    /// FROM: <https://zoo.dev/docs/api/apps>
422    pub fn apps(&self) -> apps::Apps {
423        apps::Apps::new(self.clone())
424    }
425
426    /// Endpoints that allow for code execution or creation of code execution environments.
427    ///
428    /// FROM: <https://zoo.dev/docs/api/executor>
429    pub fn executor(&self) -> executor::Executor {
430        executor::Executor::new(self.clone())
431    }
432
433    /// 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.
434    ///
435    /// FROM: <https://zoo.dev/docs/api/file>
436    pub fn file(&self) -> file::File {
437        file::File::new(self.clone())
438    }
439
440    /// Hidden API endpoints that should not show up in the docs.
441    ///
442    /// FROM: <https://zoo.dev/docs/api/hidden>
443    pub fn hidden(&self) -> hidden::Hidden {
444        hidden::Hidden::new(self.clone())
445    }
446
447    /// Meta information about the API.
448    ///
449    /// FROM: <https://zoo.dev/docs/api/meta>
450    pub fn meta(&self) -> meta::Meta {
451        meta::Meta::new(self.clone())
452    }
453
454    /// Machine learning to generate CAD models and other things.
455    ///
456    /// FROM: <https://zoo.dev/docs/api/ml>
457    pub fn ml(&self) -> ml::Ml {
458        ml::Ml::new(self.clone())
459    }
460
461    /// Modeling API for updating your 3D files using the Zoo engine.
462    ///
463    /// FROM: <https://zoo.dev/docs/api/modeling>
464    pub fn modeling(&self) -> modeling::Modeling {
465        modeling::Modeling::new(self.clone())
466    }
467
468    /// Endpoints that implement OAuth 2.0 grant flows.
469    ///
470    /// FROM: <https://zoo.dev/docs/api/oauth2>
471    pub fn oauth2(&self) -> oauth2::Oauth2 {
472        oauth2::Oauth2::new(self.clone())
473    }
474
475    /// An organization is a group of users of the Zoo API. Here, we can add users to an org and perform operations on orgs.
476    ///
477    /// FROM: <https://zoo.dev/docs/api/orgs>
478    pub fn orgs(&self) -> orgs::Orgs {
479        orgs::Orgs::new(self.clone())
480    }
481
482    /// Operations around payments and billing.
483    ///
484    /// FROM: <https://zoo.dev/docs/api/payments>
485    pub fn payments(&self) -> payments::Payments {
486        payments::Payments::new(self.clone())
487    }
488
489    /// Operations for user-owned projects, public project discovery, publishing, voting, and share links.
490    ///
491    /// FROM: <https://zoo.dev/docs/api/projects>
492    pub fn projects(&self) -> projects::Projects {
493        projects::Projects::new(self.clone())
494    }
495
496    /// 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.
497    ///
498    /// FROM: <https://zoo.dev/docs/api/service-accounts>
499    pub fn service_accounts(&self) -> service_accounts::ServiceAccounts {
500        service_accounts::ServiceAccounts::new(self.clone())
501    }
502
503    /// Operations involving our swag store.
504    ///
505    /// FROM: <https://zoo.dev/docs/api/store>
506    pub fn store(&self) -> store::Store {
507        store::Store::new(self.clone())
508    }
509
510    /// Unit conversion operations.
511    ///
512    /// FROM: <https://zoo.dev/docs/api/file>
513    pub fn unit(&self) -> unit::Unit {
514        unit::Unit::new(self.clone())
515    }
516
517    /// 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.
518    ///
519    /// FROM: <https://zoo.dev/docs/api/users>
520    pub fn users(&self) -> users::Users {
521        users::Users::new(self.clone())
522    }
523}