kittycad/lib.rs
1//! A fully generated & opinionated API client for the KittyCAD API.
2//!
3//! [](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}