rust_woocommerce/controllers/
entities.rs

1use anyhow::{anyhow, Result};
2use serde::Serialize;
3use tokio::task::JoinSet;
4use url::Url;
5
6use crate::{ApiClient, BatchObject};
7
8use super::Entity;
9
10const BATCH: &str = "batch";
11
12impl ApiClient {
13    /// This API lets you retrieve and view a specific entity by ID.
14    ///
15    ///
16    /// # Example
17    ///
18    /// ```no_run
19    /// use anyhow::Result;
20    /// use rust_woocommerce::{Product, ApiClient, Config};
21    /// use tracing::info;
22    ///
23    /// #[tokio::main]
24    /// async fn main() -> Result<()> {
25    ///     tracing_subscriber::fmt::init();
26    ///     let config = Config::new("woo.toml")?;
27    ///     let client = ApiClient::new(&config)?;
28    ///     let retrieved = client.retrieve::<Product>(12345).await?;
29    ///     info!("Retrieved product has sku: {}", retrieved.sku);
30    ///     Ok(())
31    /// }
32    /// ```
33    pub async fn retrieve<T: Entity>(&self, entity_id: i32) -> Result<T> {
34        let uri = self
35            .base_url
36            .join(&T::endpoint())?
37            .join(&entity_id.to_string())?;
38        self.get_request_with_tries(uri, 3).await
39    }
40
41    async fn get_request_with_tries<T: Entity>(&self, uri: Url, tries: i32) -> Result<T> {
42        for i in 1..tries {
43            tracing::debug!("Connecting {uri}, try {i}");
44            match self
45                .client
46                .get(uri.clone())
47                .basic_auth(self.ck(), Some(self.cs()))
48                .send()
49                .await?
50                .json::<T>()
51                .await
52            {
53                Ok(r) => {
54                    return Ok(r);
55                }
56                Err(e) => {
57                    tracing::error!(
58                        "Failed to connect to {uri} with error: {e}\n{} tries left",
59                        tries - i
60                    );
61                    tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
62                    continue;
63                }
64            }
65        }
66        Err(anyhow!(
67            "Error retrieving entity with uri: {}",
68            uri.to_string()
69        ))
70    }
71    /// This API helps you to view all entities of type T.
72    ///
73    /// # Example
74    ///
75    /// ```no_run
76    /// use anyhow::Result;
77    /// use rust_woocommerce::{Product, ApiClient, Config};
78    /// use tracing::info;
79    ///
80    /// #[tokio::main]
81    /// async fn main() -> Result<()> {
82    ///     tracing_subscriber::fmt::init();
83    ///     let config = Config::new("woo.toml")?;
84    ///     let client = ApiClient::new(&config)?;
85    ///     let products = client.list_all::<Product>().await?;
86    ///     info!("Got {} products", products.len());
87    ///     Ok(())
88    /// }
89    /// ```
90    pub async fn list_all<T: Entity>(&self) -> Result<Vec<T>> {
91        let uri = self.base_url.join(&T::endpoint())?;
92        let mut result = Vec::new();
93        let mut set = JoinSet::new();
94        let total_response = self
95            .client
96            .get(uri.clone())
97            .basic_auth(&self.ck(), Some(self.cs()))
98            .send()
99            .await?;
100        let total = total_response
101            .headers()
102            .get("X-WP-Total")
103            .and_then(|h| h.to_str().ok().map(|p| p.parse::<i32>().ok()))
104            .flatten()
105            .unwrap_or_default();
106        let per_page = 50;
107        let total_pages = total / per_page + 1;
108        for page in 1..=total_pages {
109            let client = self.client();
110            let ck = self.ck();
111            let cs = Some(self.cs());
112            let url = uri.clone();
113            set.spawn(async move {
114                client
115                    .get(url)
116                    .query(&[("page", page), ("per_page", per_page)])
117                    .basic_auth(ck, cs)
118                    .send()
119                    .await?
120                    .json::<Vec<T>>()
121                    .await
122            });
123        }
124        while let Some(Ok(Ok(v))) = set.join_next().await {
125            result.extend(v)
126        }
127        Ok(result)
128    }
129    /// This API helps you to create a new entity of type T.
130    ///
131    /// # Example
132    ///
133    /// ```no_run
134    /// use anyhow::Result;
135    /// use rust_woocommerce::{Product, ApiClient, Config, Attribute};
136    /// use tracing::info;
137    ///
138    /// #[tokio::main]
139    /// async fn main() -> Result<()> {
140    ///     tracing_subscriber::fmt::init();
141    ///     let config = Config::new("woo.toml")?;
142    ///     let client = ApiClient::new(&config)?;
143    ///     let attribute = Attribute::builder()
144    ///         .name("Test Attribute")
145    ///         .option("Best")
146    ///         .visible()
147    ///         .build();
148    ///     let new_product = Product::builder()
149    ///         .name("Test Product For Example")
150    ///         .featured()
151    ///         .short_description("The most professional description")
152    ///         .sku("product for test 42")
153    ///         .regular_price("6969")
154    ///         .manage_stock()
155    ///         .stock_quantity(42)
156    ///         .weight("50")
157    ///         .dimensions("4", "3", "2")
158    ///         .shipping_class("large")
159    ///         .images("https://cs14.pikabu.ru/post_img/2021/06/27/7/1624794514137159585.jpg")
160    ///         .attribute(attribute)
161    ///         .build();
162    ///     let created: Product = client.create(new_product).await?;
163    ///     info!("Create product {} with id: {}", created.name, created.id);
164    ///     Ok(())
165    /// }
166    /// ```
167    pub async fn create<T: Entity>(&self, object: impl Serialize) -> Result<T> {
168        let uri = self.base_url.join(&T::endpoint())?;
169        self.post_request_with_tries(&object, uri, 3).await
170    }
171
172    async fn post_request_with_tries<T: Entity, O: Serialize + Sized>(
173        &self,
174        object: &O,
175        uri: Url,
176        tries: i32,
177    ) -> Result<T> {
178        for i in 1..tries {
179            tracing::debug!("Connecting {uri}, try {i}");
180            match self
181                .client
182                .post(uri.clone())
183                .basic_auth(self.ck(), Some(self.cs()))
184                .json(&object)
185                .send()
186                .await?
187                .json::<T>()
188                .await
189            {
190                Ok(r) => {
191                    return Ok(r);
192                }
193                Err(e) => {
194                    tracing::error!(
195                        "Failed to connect to {uri} with error: {e}\n{} tries left",
196                        tries - i
197                    );
198                    tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await;
199                    continue;
200                }
201            }
202        }
203        Err(anyhow!(
204            "Error creating entity with uri: {}",
205            uri.to_string()
206        ))
207    }
208    /// This API lets you make changes to entity.
209    ///
210    /// # Example
211    ///
212    /// ```no_run
213    /// use anyhow::{anyhow, Result};
214    /// use rust_woocommerce::{Product, ApiClient, Config};
215    /// use tracing::info;
216    ///
217    /// #[tokio::main]
218    /// async fn main() -> Result<()> {
219    ///     tracing_subscriber::fmt::init();
220    ///     let config = Config::new("woo.toml")?;
221    ///     let client = ApiClient::new(&config)?;
222    ///     let update = Product::builder().unfeatured().build();
223    ///     let updated: Product = client.update(12345, update).await?;
224    ///     info!(
225    ///     "Update product {}, new feature is {}",
226    ///     updated.name, updated.featured
227    /// );
228    ///     Ok(())
229    /// }
230    /// ```
231    pub async fn update<T: Entity>(&self, entity_id: i32, object: impl Serialize) -> Result<T> {
232        let uri = self
233            .base_url
234            .join(&T::endpoint())?
235            .join(&entity_id.to_string())?;
236        self.put_request_with_tries(&object, uri, 3).await
237    }
238
239    async fn put_request_with_tries<T: Entity, O: Serialize + Sized>(
240        &self,
241        object: &O,
242        uri: Url,
243        tries: i32,
244    ) -> Result<T> {
245        for i in 1..tries {
246            tracing::debug!("Connecting {uri}, try {i}");
247            match self
248                .client
249                .put(uri.clone())
250                .basic_auth(self.ck(), Some(self.cs()))
251                .json(&object)
252                .send()
253                .await?
254                .json::<T>()
255                .await
256            {
257                Ok(r) => return Ok(r),
258                Err(e) => {
259                    tracing::error!(
260                        "Failed to connect to {uri} with error: {e}\n{} tries left",
261                        tries - i
262                    );
263                    tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
264                    continue;
265                }
266            }
267        }
268        Err(anyhow!(
269            "Error updating entity with uri: {}",
270            uri.to_string()
271        ))
272    }
273    /// This API helps you delete a product.
274    ///
275    /// # Example
276    ///
277    /// ```no_run
278    /// use anyhow::Result;
279    /// use rust_woocommerce::{Product, ApiClient, Config};
280    /// use tracing::info;
281    ///
282    /// #[tokio::main]
283    /// async fn main() -> Result<()> {
284    ///     tracing_subscriber::fmt::init();
285    ///     let config = Config::new("woo.toml")?;
286    ///     let client = ApiClient::new(&config)?;
287    ///     let deleted: Product = client.delete(12345).await?;
288    ///     info!("Product {} deleted", deleted.name);
289    ///     Ok(())
290    /// }
291    /// ```
292    pub async fn delete<T: Entity>(&self, entity_id: i32) -> Result<T> {
293        let uri = self
294            .base_url
295            .join(&T::endpoint())?
296            .join(&entity_id.to_string())?;
297        self.delete_request_with_tries(uri, 3).await
298    }
299
300    async fn delete_request_with_tries<T: Entity>(&self, uri: Url, tries: i32) -> Result<T> {
301        for i in 1..tries {
302            tracing::debug!("Connecting {uri}, try {i}");
303            match self
304                .client
305                .delete(uri.clone())
306                .basic_auth(self.ck(), Some(self.cs()))
307                .query(&[("force", true)])
308                .send()
309                .await?
310                .json::<T>()
311                .await
312            {
313                Ok(r) => {
314                    return Ok(r);
315                }
316                Err(e) => {
317                    tracing::error!(
318                        "Failed to connect to {uri} with error: {e}\n{} tries left",
319                        tries - i
320                    );
321                    tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
322                    continue;
323                }
324            }
325        }
326        Err(anyhow!(
327            "Error deleting entity with uri: {}",
328            uri.to_string()
329        ))
330    }
331    /// This API helps you to batch create multiple entities.
332    ///
333    /// # Example
334    ///
335    /// ```no_run
336    /// use anyhow::Result;
337    /// use rust_woocommerce::{Product, ApiClient, Config, Attribute};
338    /// use tracing::info;
339    ///
340    /// #[tokio::main]
341    /// async fn main() -> Result<()> {
342    ///     tracing_subscriber::fmt::init();
343    ///     let config = Config::new("woo.toml")?;
344    ///     let client = ApiClient::new(&config)?;
345    ///     let attribute = Attribute::builder()
346    ///         .name("Test Attribute")
347    ///         .option("Best")
348    ///         .visible()
349    ///         .build();
350    ///     let new_product = Product::builder()
351    ///         .name("Test Product For Example")
352    ///         .featured()
353    ///         .short_description("The most professional description")
354    ///         .sku("product for test 42")
355    ///         .regular_price("6969")
356    ///         .manage_stock()
357    ///         .stock_quantity(42)
358    ///         .weight("50")
359    ///         .dimensions("4", "3", "2")
360    ///         .shipping_class("large")
361    ///         .images("https://cs14.pikabu.ru/post_img/2021/06/27/7/1624794514137159585.jpg")
362    ///         .attribute(attribute)
363    ///         .build();
364    ///     let batch_create = vec![new_product];
365    ///     let batch_created: Vec<Product> = client.batch_create(batch_create).await?;
366    ///     Ok(())
367    /// }
368    /// ```
369    pub async fn batch_create<T: Entity, O: Serialize + Clone + Send + 'static>(
370        &self,
371        create_objects: Vec<O>,
372    ) -> Result<Vec<T>> {
373        let mut result = Vec::new();
374        let mut set = JoinSet::new();
375        let uri = self.base_url.join(&T::endpoint())?.join(BATCH)?;
376        let batched = create_objects
377            .chunks(100)
378            .map(|c| BatchObject::builder().extend_create(c.to_vec()).build())
379            .collect::<Vec<_>>();
380        for batch in batched {
381            let client = self.client();
382            let ck = self.ck();
383            let cs = Some(self.cs());
384            let url = uri.clone();
385            set.spawn(async move {
386                client
387                    .post(url)
388                    .basic_auth(ck, cs)
389                    .json(&batch)
390                    .send()
391                    .await?
392                    .json::<BatchObject<T>>()
393                    .await
394            });
395        }
396        while let Some(Ok(Ok(v))) = set.join_next().await {
397            result.extend(v.create)
398        }
399        Ok(result.into_iter().flatten().collect::<Vec<_>>())
400    }
401    /// This API helps you to batch update multiple entities.
402    ///
403    /// # Example
404    ///
405    /// ```no_run
406    /// use rust_woocommerce::{ApiClient, Config};
407    /// use rust_woocommerce::Category;
408    /// use anyhow::Result;
409    /// use tracing::info;
410    ///
411    /// #[tokio::main]
412    /// async fn main() -> Result<()> {
413    ///     tracing_subscriber::fmt::init();
414    ///     let config = Config::new("woo.toml")?;
415    ///     let client = ApiClient::new(&config)?;
416    ///     let batch_update = Category::update()
417    ///         .id(12345)
418    ///         .description("Some description")
419    ///         .build();
420    ///     let batch_updated: Vec<Category> = client.batch_update(vec![batch_update]).await?;
421    ///     Ok(())
422    /// }
423    /// ```
424    pub async fn batch_update<T: Entity, O: Serialize + Clone + Send + 'static>(
425        &self,
426        update_objects: Vec<O>,
427    ) -> Result<Vec<T>> {
428        let mut result = Vec::new();
429        let mut set = JoinSet::new();
430        let uri = self.base_url.join(&T::endpoint())?.join(BATCH)?;
431        let batched = update_objects
432            .chunks(100)
433            .map(|c| BatchObject::builder().extend_update(c.to_vec()).build())
434            .collect::<Vec<_>>();
435        for batch in batched {
436            let client = self.client();
437            let ck = self.ck();
438            let cs = Some(self.cs());
439            let url = uri.clone();
440            set.spawn(async move {
441                client
442                    .post(url)
443                    .basic_auth(ck, cs)
444                    .json(&batch)
445                    .send()
446                    .await?
447                    .json::<BatchObject<T>>()
448                    .await
449            });
450        }
451        while let Some(Ok(Ok(v))) = set.join_next().await {
452            result.extend(v.update)
453        }
454        Ok(result.into_iter().flatten().collect::<Vec<_>>())
455    }
456    /// This API helps you to batch delete multiple entities.
457    ///
458    /// # Example
459    ///
460    /// ```no_run
461    /// use rust_woocommerce::{ApiClient, Config};
462    /// use rust_woocommerce::Category;
463    /// use anyhow::Result;
464    /// use tracing::info;
465    ///
466    /// #[tokio::main]
467    /// async fn main() -> Result<()> {
468    ///     tracing_subscriber::fmt::init();
469    ///     let config = Config::new("woo.toml")?;
470    ///     let client = ApiClient::new(&config)?;
471    ///     let batch_deleted: Vec<Category> = client.batch_delete(vec![12345]).await?;
472    ///     Ok(())
473    /// }
474    /// ```
475    pub async fn batch_delete<T: Entity>(&self, delete_objects: Vec<i32>) -> Result<Vec<T>> {
476        let mut result = Vec::new();
477        let mut set = JoinSet::new();
478        let uri = self.base_url.join(&T::endpoint())?.join(BATCH)?;
479        let batched = delete_objects
480            .chunks(100)
481            .map(|c| BatchObject::builder().extend_delete(c.to_vec()).build())
482            .collect::<Vec<_>>();
483        for batch in batched {
484            let client = self.client();
485            let ck = self.ck();
486            let cs = Some(self.cs());
487            let url = uri.clone();
488            set.spawn(async move {
489                client
490                    .post(url)
491                    .basic_auth(ck, cs)
492                    .query(&[("force", true)])
493                    .json(&batch)
494                    .send()
495                    .await?
496                    .json::<BatchObject<T>>()
497                    .await
498            });
499        }
500        while let Some(Ok(Ok(v))) = set.join_next().await {
501            result.extend(v.delete)
502        }
503        Ok(result.into_iter().flatten().collect::<Vec<_>>())
504    }
505    /// This API lets you retrieve and view a specific subentity by ID.
506    ///
507    /// # Example
508    /// ```no_run
509    /// use anyhow::Result;
510    /// use tracing::info;
511    ///
512    /// use rust_woocommerce::{ApiClient, Config, ProductVariation};
513    ///
514    /// #[tokio::main]
515    /// async fn main() -> Result<()> {
516    ///     tracing_subscriber::fmt::init();
517    ///     let config = Config::new("woo.toml")?;
518    ///     let client = ApiClient::new(&config)?;
519    ///     let retrieved_variation: ProductVariation = client
520    ///         .retrieve_subentity(12345, 42)
521    ///         .await?;
522    ///     info!("Retrieved variation has sku: {}", retrieved_variation.sku);
523    ///     Ok(())
524    /// }
525    /// ```
526    pub async fn retrieve_subentity<T: Entity>(
527        &self,
528        entity_id: i32,
529        subentity_id: i32,
530    ) -> Result<T> {
531        let uri = self
532            .base_url
533            .join(&T::child_endpoint(entity_id))?
534            .join(&subentity_id.to_string())?;
535        self.get_request_with_tries(uri, 3).await
536    }
537    /// This API lets you view all subentities of entity.
538    ///
539    /// # Example
540    /// ```no_run
541    /// use anyhow::Result;
542    /// use tracing::info;
543    ///
544    /// use rust_woocommerce::{ApiClient, Config, ProductVariation};
545    ///
546    /// #[tokio::main]
547    /// async fn main() -> Result<()> {
548    ///     tracing_subscriber::fmt::init();
549    ///     let config = Config::new("woo.toml")?;
550    ///     let client = ApiClient::new(&config)?;
551    ///     let variations = client
552    ///         .list_all_subentities::<ProductVariation>(12345)
553    ///         .await?;
554    ///     info!(
555    ///         "Got {} variations for product with id: 12345",
556    ///         variations.len()
557    ///     );
558    ///     Ok(())
559    /// }
560    /// ```
561    pub async fn list_all_subentities<T: Entity>(&self, entity_id: i32) -> Result<Vec<T>> {
562        let uri = self.base_url.join(&T::child_endpoint(entity_id))?;
563
564        for i in 1..3 {
565            tracing::debug!("Connecting {uri}, try {i}");
566            match self
567                .client
568                .get(uri.clone())
569                .basic_auth(self.ck(), Some(self.cs()))
570                .send()
571                .await?
572                .json::<Vec<T>>()
573                .await
574            {
575                Ok(r) => return Ok(r),
576                Err(e) => {
577                    tracing::error!(
578                        "Failed to connect to {uri} with error: {e}\n{} tries left",
579                        3 - i
580                    );
581                    tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await;
582                    continue;
583                }
584            }
585        }
586        Err(anyhow!(
587            "Error retrieving subentitity for entity with id: {entity_id}"
588        ))
589    }
590    /// This API helps you create a new subentity.
591    ///
592    /// # Example
593    /// ```no_run
594    /// use anyhow::Result;
595    /// use tracing::info;
596    ///
597    /// use rust_woocommerce::{ApiClient, Config, ProductVariation};
598    ///
599    /// #[tokio::main]
600    /// async fn main() -> Result<()> {
601    ///     tracing_subscriber::fmt::init();
602    ///     let config = Config::new("woo.toml")?;
603    ///     let client = ApiClient::new(&config)?;
604    ///     let variation = ProductVariation::builder()
605    ///         .sku("Best variation")
606    ///         .regular_price("6969")
607    ///         .manage_stock()
608    ///         .stock_quantity(96)
609    ///         .weight("52")
610    ///         .dimensions("5", "4", "3")
611    ///         .attribute(None, "Test Attribute", "Best")
612    ///         .build();
613    ///     let created_variation: ProductVariation =
614    ///         client.create_subentity(12345, variation).await?;
615    ///     info!(
616    ///         "Variation {} created with price: {}",
617    ///         created_variation.sku, created_variation.price
618    ///     );
619    ///     Ok(())
620    /// }
621    /// ```
622    pub async fn create_subentity<T: Entity>(
623        &self,
624        entity_id: i32,
625        object: impl Serialize,
626    ) -> Result<T> {
627        let uri = self.base_url.join(&T::child_endpoint(entity_id))?;
628        self.post_request_with_tries(&object, uri, 3).await
629    }
630    /// This API lets you make changes to subentity.
631    ///
632    /// # Example
633    /// ```no_run
634    /// use anyhow::Result;
635    /// use tracing::info;
636    ///
637    /// use rust_woocommerce::{ApiClient, Config, ProductVariation};
638    ///
639    /// #[tokio::main]
640    /// async fn main() -> Result<()> {
641    ///     tracing_subscriber::fmt::init();
642    ///     let config = Config::new("woo.toml")?;
643    ///     let client = ApiClient::new(&config)?;
644    ///     let update = ProductVariation::builder().regular_price("7000").build();
645    ///     let updated_variation: ProductVariation = client
646    ///         .update_subentity(12345, 42, update)
647    ///         .await?;
648    ///     info!(
649    ///         "Variation {} updated with price: {}",
650    ///         updated_variation.sku, updated_variation.price
651    ///     );
652    ///     Ok(())
653    /// }
654    /// ```
655    pub async fn update_subentity<T: Entity>(
656        &self,
657        entity_id: i32,
658        subentity_id: i32,
659        object: impl Serialize,
660    ) -> Result<T> {
661        let uri = self
662            .base_url
663            .join(&T::child_endpoint(entity_id))?
664            .join(&subentity_id.to_string())?;
665        self.put_request_with_tries(&object, uri, 3).await
666    }
667    /// This API helps you delete subentity.
668    ///
669    /// # Example
670    /// ```no_run
671    /// use anyhow::Result;
672    /// use tracing::info;
673    ///
674    /// use rust_woocommerce::{ApiClient, Config, ProductVariation};
675    ///
676    /// #[tokio::main]
677    /// async fn main() -> Result<()> {
678    ///     tracing_subscriber::fmt::init();
679    ///     let config = Config::new("woo.toml")?;
680    ///     let client = ApiClient::new(&config)?;
681    ///     let deleted_variation: ProductVariation = client
682    ///         .delete_subentity(12345, 42)
683    ///         .await?;
684    ///     info!("Variation {} deleted", deleted_variation.sku);
685    ///     Ok(())
686    /// }
687    /// ```
688    pub async fn delete_subentity<T: Entity>(
689        &self,
690        entity_id: i32,
691        subentity_id: i32,
692    ) -> Result<T> {
693        let uri = self
694            .base_url
695            .join(&T::child_endpoint(entity_id))?
696            .join(&subentity_id.to_string())?;
697        self.delete_request_with_tries(uri, 3).await
698    }
699    /// This API helps you to batch create subentities.
700    ///
701    /// # Example
702    ///
703    /// ```no_run
704    /// use anyhow::Result;
705    /// use tracing::info;
706    ///
707    /// use rust_woocommerce::{ApiClient, Config, ProductVariation};
708    ///
709    /// #[tokio::main]
710    /// async fn main() -> Result<()> {
711    ///     tracing_subscriber::fmt::init();
712    ///     let config = Config::new("woo.toml")?;
713    ///     let client = ApiClient::new(&config)?;
714    ///     let variation = ProductVariation::builder()
715    ///         .sku("Best SKU")
716    ///         .regular_price("6969")
717    ///         .manage_stock()
718    ///         .stock_quantity(96)
719    ///         .weight("52")
720    ///         .dimensions("5", "4", "3")
721    ///         .attribute(None, "Test Attribute", "Best")
722    ///         .build();
723    ///     let batch_created_variation: Vec<ProductVariation> = client
724    ///         .batch_create_subentity(12345, vec![variation])
725    ///         .await?;
726    ///     Ok(())
727    /// }
728    /// ```
729    pub async fn batch_create_subentity<T: Entity, O: Serialize + Clone + Send + 'static>(
730        &self,
731        entity_id: i32,
732        create_objects: Vec<O>,
733    ) -> Result<Vec<T>> {
734        let mut result = Vec::new();
735        let mut set = JoinSet::new();
736        let uri = self
737            .base_url
738            .join(&T::child_endpoint(entity_id))?
739            .join(BATCH)?;
740        let batched = create_objects
741            .chunks(100)
742            .map(|c| BatchObject::builder().extend_create(c.to_vec()).build())
743            .collect::<Vec<_>>();
744        for batch in batched {
745            let client = self.client();
746            let ck = self.ck();
747            let cs = Some(self.cs());
748            let url = uri.clone();
749            set.spawn(async move {
750                client
751                    .post(url)
752                    .basic_auth(ck, cs)
753                    .json(&batch)
754                    .send()
755                    .await?
756                    .json::<BatchObject<T>>()
757                    .await
758            });
759        }
760        while let Some(Ok(Ok(v))) = set.join_next().await {
761            result.extend(v.create)
762        }
763        Ok(result.into_iter().flatten().collect::<Vec<_>>())
764    }
765    /// This API helps you to batch update subentities.
766    ///
767    /// # Example
768    ///
769    /// ```no_run
770    /// use anyhow::Result;
771    /// use tracing::info;
772    ///
773    /// use rust_woocommerce::{ApiClient, Config, ProductVariation};
774    ///
775    /// #[tokio::main]
776    /// async fn main() -> Result<()> {
777    ///     tracing_subscriber::fmt::init();
778    ///     let config = Config::new("woo.toml")?;
779    ///     let client = ApiClient::new(&config)?;
780    ///     let batch_update_variation = vec![ProductVariation::builder()
781    ///         .id(42)
782    ///         .regular_price("777")
783    ///         .build()];
784    ///     let batch_updated_variation: Vec<ProductVariation> = client
785    ///         .batch_update_subentity(12345, batch_update_variation)
786    ///         .await?;
787    ///     Ok(())
788    /// }
789    /// ```
790    pub async fn batch_update_subentity<T: Entity, O: Serialize + Clone + Send + 'static>(
791        &self,
792        entity_id: i32,
793        update_objects: Vec<O>,
794    ) -> Result<Vec<T>> {
795        let mut result = Vec::new();
796        let mut set = JoinSet::new();
797        let uri = self
798            .base_url
799            .join(&T::child_endpoint(entity_id))?
800            .join(BATCH)?;
801        let batched = update_objects
802            .chunks(100)
803            .map(|c| BatchObject::builder().extend_update(c.to_vec()).build())
804            .collect::<Vec<_>>();
805        for batch in batched {
806            let client = self.client();
807            let ck = self.ck();
808            let cs = Some(self.cs());
809            let url = uri.clone();
810            set.spawn(async move {
811                client
812                    .post(url)
813                    .basic_auth(ck, cs)
814                    .json(&batch)
815                    .send()
816                    .await?
817                    .json::<BatchObject<T>>()
818                    .await
819            });
820        }
821        while let Some(Ok(Ok(v))) = set.join_next().await {
822            result.extend(v.update)
823        }
824        Ok(result.into_iter().flatten().collect::<Vec<_>>())
825    }
826    /// This API helps you to batch delete subentities.
827    ///
828    /// # Example
829    ///
830    /// ```no_run
831    /// use anyhow::Result;
832    /// use tracing::info;
833    ///
834    /// use rust_woocommerce::{ApiClient, Config, ProductVariation};
835    ///
836    /// #[tokio::main]
837    /// async fn main() -> Result<()> {
838    ///     tracing_subscriber::fmt::init();
839    ///     let config = Config::new("woo.toml")?;
840    ///     let client = ApiClient::new(&config)?;
841    ///     let batch_deleted_variation: Vec<ProductVariation> = client
842    ///         .batch_delete_subentity(12345, vec![42])
843    ///         .await?;
844    ///     Ok(())
845    /// }
846    /// ```
847    pub async fn batch_delete_subentity<T: Entity, O: Serialize + Clone + Send + 'static>(
848        &self,
849        entity_id: i32,
850        delete_objects: Vec<O>,
851    ) -> Result<Vec<T>> {
852        let mut result = Vec::new();
853        let mut set = JoinSet::new();
854        let uri = self
855            .base_url
856            .join(&T::child_endpoint(entity_id))?
857            .join(BATCH)?;
858        let batched = delete_objects
859            .chunks(100)
860            .map(|c| BatchObject::builder().extend_delete(c.to_vec()).build())
861            .collect::<Vec<_>>();
862        for batch in batched {
863            let client = self.client();
864            let ck = self.ck();
865            let cs = Some(self.cs());
866            let url = uri.clone();
867            set.spawn(async move {
868                client
869                    .post(url)
870                    .basic_auth(ck, cs)
871                    .json(&batch)
872                    .send()
873                    .await?
874                    .json::<BatchObject<T>>()
875                    .await
876            });
877        }
878        while let Some(Ok(Ok(v))) = set.join_next().await {
879            result.extend(v.delete)
880        }
881        Ok(result.into_iter().flatten().collect::<Vec<_>>())
882    }
883}