mochow_sdk_rust/mochow/
client.rs

1/*
2 * Copyright 2024 Baidu, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 * either express or implied. See the License for the specific language governing permissions
12 * and limitations under the License.
13 */
14
15/*
16client to connect to mochow server
17 */
18use std::time::Duration;
19
20use derive_builder::Builder;
21use reqwest::Response;
22use reqwest_middleware::{ClientBuilder, ClientWithMiddleware, RequestBuilder};
23use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware};
24use reqwest_tracing::TracingMiddleware;
25use serde::{Deserialize, Serialize};
26
27use crate::{auth::credentials, error::SdkError};
28
29use super::{api::*, config::*};
30
31#[derive(Debug, Clone, Builder)]
32pub struct MochowClient {
33    /// with important credential information
34    #[builder(setter(into))]
35    pub(crate) credential: credentials::Credentials,
36    /// underlying http client
37    #[builder(setter(into))]
38    pub(crate) http_client: ClientWithMiddleware,
39
40    #[builder(setter(into))]
41    pub configuration: ClientConfiguration,
42}
43
44/// every request should imple IntoRequest trait, it's just a rest request for http client
45pub trait IntoRequest {
46    fn into_request(
47        self,
48        config: &ClientConfiguration,
49        client: &ClientWithMiddleware,
50    ) -> RequestBuilder;
51}
52
53impl MochowClient {
54    /// create a new mochow client with account, api_key and endpoint
55    /// ```rust
56    /// use mochow_rust_sdk::mochow::client::MochowClient;
57    /// let client = MochowClient::new("account", "api_key", "endpoint").unwrap();
58    /// ```
59    pub fn new(account: &str, api_key: &str, endpoint: &str) -> Result<Self, SdkError> {
60        Self::new_with_configuration(
61            &ClientConfigurationBuilder::default()
62                .account(account)
63                .api_key(api_key)
64                .endpoint(endpoint)
65                .build()?,
66        )
67    }
68
69    // get the http client with config
70    fn _http_client(config: &ClientConfiguration) -> ClientWithMiddleware {
71        let retry_policy = ExponentialBackoff::builder().build_with_max_retries(config.max_retries);
72        ClientBuilder::new(reqwest::Client::new())
73            // Trace HTTP request
74            .with(TracingMiddleware::default())
75            // Retry
76            .with(RetryTransientMiddleware::new_with_policy(retry_policy))
77            .build()
78    }
79
80    /// create a new mochow client with configuration
81    /// ```rust
82    /// use mochow_rust_sdk::mochow::{config::ClientConfigurationBuilder, client::MochowClient};
83    /// let config = ClientConfigurationBuilder::default()
84    /// 	.account("account")
85    /// 	.api_key("api_key")
86    /// 	.endpoint("endpoint")
87    /// 	.build().unwrap();
88    /// let client = MochowClient::new_with_configuration(&config).unwrap();
89    /// ```
90    pub fn new_with_configuration(config: &ClientConfiguration) -> Result<Self, SdkError> {
91        let mut config = config.clone();
92        if config.account.is_empty() || config.api_key.is_empty() || config.endpoint.is_empty() {
93            return Err(SdkError::ParamsError(
94                "account, apiKey and endpoint missing for creating mochow client".to_string(),
95            ));
96        }
97        let auth = credentials::Credentials::new(&config.account, &config.api_key)?;
98        let endpoint = config.endpoint.clone();
99        let endpoint = if endpoint.starts_with("http://") || endpoint.starts_with("https://") {
100            endpoint
101        } else {
102            format!("http://{}", endpoint)
103        };
104        config.endpoint = endpoint;
105        let ret = MochowClientBuilder::default()
106            .credential(auth)
107            .http_client(Self::_http_client(&config))
108            .configuration(config)
109            .build()?;
110        Ok(ret)
111    }
112
113    /// create a database
114    /// ```rust
115    /// let _ = client.create_database("test").await?;
116    /// ```
117    pub async fn create_database(&self, data_base: &str) -> Result<CommonResponse, SdkError> {
118        let args = CreateDatabaseArgsBuilder::default()
119            .database(data_base)
120            .build()?;
121        let req = self.prepare_request(args);
122        let res = req.send_and_log().await?;
123        Ok(res.json::<CommonResponse>().await?)
124    }
125
126    /// drop the database you created, before deleting the database, all tables in the database must be deleted in advance
127    /// ```rust
128    /// let _ = client.drop_database("test").await?;
129    /// ```
130    pub async fn drop_database(&self, data_base: &str) -> Result<CommonResponse, SdkError> {
131        let args = DropDatabaseArgsBuilder::default()
132            .database(data_base)
133            .build()?;
134        let req = self.prepare_request(args);
135        let res = req.send_and_log().await?;
136        Ok(res.json::<CommonResponse>().await?)
137    }
138
139    /// list current all databases
140    /// ```rust
141    /// let ret = client.list_database("test").await?;
142    /// println!("{:?}", ret.databases);
143    /// ```
144    pub async fn list_database(&self) -> Result<ListDatabaseResponse, SdkError> {
145        let args = ListDatabaseArgsBuilder::default().build()?;
146        let req = self.prepare_request(args);
147        let res = req.send_and_log().await?;
148        Ok(res.json::<ListDatabaseResponse>().await?)
149    }
150
151    /// check if the database is exist,
152    pub async fn hash_database(&self, data_base: &str) -> Result<bool, SdkError> {
153        let list_database_resp = self.list_database().await?;
154        Ok(list_database_resp
155            .databases
156            .contains(&data_base.to_string()))
157    }
158
159    /// create table
160    /// please check the [crate::mochow::api::CreateTableArgs]
161    /// ```rust
162    /// use mochow_rust_sdk::mochow::api::{CreateTableArgsBuilder, Partition, TableSchema}
163    /// let fields = vec![];
164    /// let indexes = vec![];
165    /// let args = CreateTableArgsBuilder::default()
166    ///     .database("test_db")
167    ///     .table("test_table")
168    ///     .description("basic test".to_string())
169    ///     .replication(3 as u32)
170    ///     .partition(Partition {
171    ///         partition_type: PartitionType::HASH,
172    ///         partition_num: 3,
173    ///      })
174    ///      .schema(TableSchema {
175    ///         fields: fields,
176    ///         indexes: indexes,
177    ///      })
178    ///     .build()?;
179    /// let create_table_resp = client.create_table(&args).await?;
180    /// println!("{:?}", create_table_resp);
181    /// ```
182    pub async fn create_table(&self, args: &CreateTableArgs) -> Result<CommonResponse, SdkError> {
183        let req = self.prepare_request(args.clone());
184        let res = req.send_and_log().await?;
185        Ok(res.json::<CommonResponse>().await?)
186    }
187
188    /// drop table
189    pub async fn drop_table(
190        &self,
191        data_base: &str,
192        table: &str,
193    ) -> Result<CommonResponse, SdkError> {
194        let args = DropTableArgsBuilder::default()
195            .database(data_base)
196            .table(table)
197            .build()?;
198        let req = self.prepare_request(args);
199        let res = req.send_and_log().await?;
200        Ok(res.json::<CommonResponse>().await?)
201    }
202
203    /// list table
204    pub async fn list_table(&self, data_base: &str) -> Result<ListTableResponse, SdkError> {
205        let args = ListTableArgsBuilder::default()
206            .database(data_base)
207            .build()?;
208        let req = self.prepare_request(args);
209        let res = req.send_and_log().await?;
210        Ok(res.json::<ListTableResponse>().await?)
211    }
212
213    /// has table
214    pub async fn has_table(&self, data_base: &str, table: &str) -> Result<bool, SdkError> {
215        let list_table = self.list_table(data_base).await?;
216        Ok(list_table.tables.contains(&table.to_string()))
217    }
218
219    /// descript table
220    pub async fn desc_table(
221        &self,
222        data_base: &str,
223        table: &str,
224    ) -> Result<DescriptTableResponse, SdkError> {
225        let args = DescriptTableArgsBuilder::default()
226            .database(data_base)
227            .table(table)
228            .build()?;
229        let req = self.prepare_request(args);
230        let res = req.send_and_log().await?;
231        Ok(res.json::<DescriptTableResponse>().await?)
232    }
233
234    /// add field for table, currently only supports adding scalar fields
235    /// ```rust
236    /// use mochow_rust_sdk::mochow::api::{TableSchema, FieldSchemaBuilder, AddFieldArgsBuilder};
237    /// let fields = vec![FieldSchemaBuilder::default()
238    ///     .field_name("bookAlias")
239    ///     .field_type("STRING")
240    ///     .build()?];
241    /// let args = AddFieldArgsBuilder::default()
242    ///     .database("test_db")
243    ///     .table("test_table")
244    ///     .schema(TableSchema{
245    ///         fields: fields,
246    ///         indexes: vec![],
247    ///     })
248    ///     .build()?;
249    /// let ret = client.add_field(&args).await?;
250    /// ```
251    pub async fn add_field(&self, args: &AddFieldArgs) -> Result<CommonResponse, SdkError> {
252        let req = self.prepare_request(args.clone());
253        let res = req.send_and_log().await?;
254        Ok(res.json::<CommonResponse>().await?)
255    }
256
257    /// show table stats
258    pub async fn show_table_stats(
259        &self,
260        data_base: &str,
261        table: &str,
262    ) -> Result<StatsTableResponse, SdkError> {
263        let args = StatsTableArgsBuilder::default()
264            .database(data_base)
265            .table(table)
266            .build()?;
267        let req = self.prepare_request(args);
268        let res = req.send_and_log().await?;
269        Ok(res.json::<StatsTableResponse>().await?)
270    }
271
272    /// alias table
273    pub async fn alias_table(
274        &self,
275        data_base: &str,
276        table: &str,
277        alias: &str,
278    ) -> Result<CommonResponse, SdkError> {
279        let args = AliasTableArgsBuilder::default()
280            .database(data_base)
281            .table(table)
282            .alias(alias)
283            .build()?;
284        let req = self.prepare_request(args);
285        let res = req.send_and_log().await?;
286        Ok(res.json::<CommonResponse>().await?)
287    }
288
289    /// unalias table
290    pub async fn unalias_table(
291        &self,
292        data_base: &str,
293        table: &str,
294        alias: &str,
295    ) -> Result<CommonResponse, SdkError> {
296        let args = UnaliasTableArgsBuilder::default()
297            .database(data_base)
298            .table(table)
299            .alias(alias)
300            .build()?;
301        let req = self.prepare_request(args);
302        let res = req.send_and_log().await?;
303        Ok(res.json::<CommonResponse>().await?)
304    }
305
306    /// create index, only support for vector index
307    /// ```rust
308    /// let indexes = vec![IndexSchemaBuilder::default()
309    ///     .index_name("vector_idx")
310    ///     .field("vector")
311    ///     .index_type(IndexType::HNSW)
312    ///     ..metric_type(MetricType::L2)
313    ///     .params(VectorIndexParams::HNSW(HNSWIndexParam {
314    ///         m: 16,
315    ///         ef_construction: 200,
316    ///     }))
317    ///     .build()?];
318    /// let args = CreateIndexArgsBuilder::default()
319    ///     .database("test_db")
320    ///     .table("test_table")
321    ///     .indexes(indexes)
322    ///     .build()?;
323    /// let ret = client.create_index(&args).await?;
324    /// ```
325    pub async fn create_index(&self, args: &CreateIndexArgs) -> Result<CommonResponse, SdkError> {
326        let req = self.prepare_request(args.clone());
327        let res = req.send_and_log().await?;
328        Ok(res.json::<CommonResponse>().await?)
329    }
330
331    /// descript index
332    pub async fn desc_index(
333        &self,
334        data_base: &str,
335        table: &str,
336        index_name: &str,
337    ) -> Result<DescriptIndexResponse, SdkError> {
338        let args = DescriptIndexArgsBuilder::default()
339            .database(data_base)
340            .table(table)
341            .index_name(index_name)
342            .build()?;
343        let req = self.prepare_request(args.clone());
344        let res = req.send_and_log().await?;
345        Ok(res.json::<DescriptIndexResponse>().await?)
346    }
347
348    /// modify vector index info, only support 'autobuild' attribute
349    /// ```rust
350    /// let index = IndexSchemaBuilder::default()
351    ///     .index_name("vector_idx")
352    ///     .auto_build(true)
353    ///     .auto_build_policy(
354    ///         AutoBuildPolicyBuilder::default()
355    ///             .policy_type(AutoBuildPolicyType::PERIODICAL)
356    ///             .period_in_second(5000 as u64)
357    ///             .build()?,
358    ///     )
359    ///     .build()?;
360    /// let args = ModifyIndexArgsBuilder::default()
361    ///     .database("test_db")
362    ///     .table("test_table")
363    ///     .index(index)
364    ///     .build()?;
365    /// let ret = client.modify_index(&args).await?;
366    /// ```
367    pub async fn modify_index(&self, args: &ModifyIndexArgs) -> Result<CommonResponse, SdkError> {
368        let req = self.prepare_request(args.clone());
369        let res = req.send_and_log().await?;
370        Ok(res.json::<CommonResponse>().await?)
371    }
372
373    /// rebuild index, only support for vector index
374    pub async fn rebuild_index(
375        &self,
376        data_base: &str,
377        table: &str,
378        index_name: &str,
379    ) -> Result<CommonResponse, SdkError> {
380        let args = RebuildIndexArgsBuilder::default()
381            .database(data_base)
382            .table(table)
383            .index_name(index_name)
384            .build()?;
385        let req = self.prepare_request(args);
386        let res = req.send_and_log().await?;
387        Ok(res.json::<CommonResponse>().await?)
388    }
389
390    /// delete index
391    pub async fn delete_index(
392        &self,
393        data_base: &str,
394        table: &str,
395        index_name: &str,
396    ) -> Result<CommonResponse, SdkError> {
397        let args = DeleteIndexArgsBuilder::default()
398            .database(data_base)
399            .table(table)
400            .index_name(index_name)
401            .build()?;
402        let req = self.prepare_request(args);
403        let res = req.send_and_log().await?;
404        Ok(res.json::<CommonResponse>().await?)
405    }
406
407    /// insert row, when the primary key of the record already exists, an insertion error occurs, not support insert batch atomicity
408    /// ```rust
409    /// #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
410    /// struct MyRecord {
411    ///     #[serde(default)]
412    ///     id: String,
413    ///     #[serde(default, rename = "bookName")]
414    ///     book_name: String,
415    ///     #[serde(default)]
416    ///     author: String,
417    ///     #[serde(default)]
418    ///     page: i32,
419    ///     #[serde(default)]
420    ///     vector: Vec<f64>,
421    /// }
422    /// // insert row with a defined struct
423    /// let args1 = InsertRowArgsBuilder::default()
424    ///     .database(&TESTDATABSE.to_string())
425    ///     .table(&TESTTABLE.to_string())
426    ///     .rows(vec![MyRecord {
427    ///         id: "0001".to_string(),
428    ///         book_name: "西游记".to_string(),
429    ///         author: "吴承恩".to_string(),
430    ///         page: 21,
431    ///         vector: vec![0.2123, 0.24, 0.213],
432    ///     }])
433    ///     .build()?;
434    /// // insert row with a json object
435    /// let _ret = client.insert_row(&args1).await?;
436    /// let args2 = InsertRowArgsBuilder::default()
437    ///     .database(&TESTDATABSE.to_string())
438    ///     .table(&TESTTABLE.to_string())
439    ///     .rows(vec![serde_json::json!({
440    ///         "id": "0002",
441    ///         "bookName": "西游记",
442    ///         "author": "吴承恩",
443    ///         "page": 22,
444    ///         "vector": [0.2123, 0.24, 0.213],
445    ///     })])
446    ///     .build()?;
447    /// let _ret = client.insert_row(&args2).await?;
448    /// ```
449    pub async fn insert_row<T: Serialize + Clone>(
450        &self,
451        args: &InsertRowArgs<T>,
452    ) -> Result<InsertRowsResponse, SdkError> {
453        let req = self.prepare_request(args.clone());
454        let res = req.send_and_log().await?;
455        Ok(res.json::<InsertRowsResponse>().await?)
456    }
457
458    /// upsert row, when the primary key of the record already exists, overwrite the old data with the new data as a whole, not support insert batch atomicity
459    /// ```
460    /// let args = UpsertRowArgsBuilder::default()
461    ///     .database(&TESTDATABSE.to_string())
462    ///     .table(&TESTTABLE.to_string())
463    ///     .rows(vec![
464    ///         serde_json::json!({
465    ///             "id":       "0001",
466    ///             "bookName": "西游记",
467    ///             "author":   "吴承恩",
468    ///             "page":     21,
469    ///             "vector":   [0.2123, 0.21, 0.213],
470    ///         })
471    ///     ])
472    ///     .build()?;
473    /// let _ret = client.upsert_row(&args).await?;
474    /// ```
475    pub async fn upsert_row<T: Serialize + Clone>(
476        &self,
477        args: &UpsertRowArgs<T>,
478    ) -> Result<UpsertRowsResponse, SdkError> {
479        let req = self.prepare_request(args.clone());
480        let res = req.send_and_log().await?;
481        Ok(res.json::<UpsertRowsResponse>().await?)
482    }
483
484    /// update row, update the value of one or more scalar fields in a specified record
485    /// ```rust
486    /// let args = UpdateRowArgsBuilder::default()
487    ///     .database(&TESTDATABSE.to_string())
488    ///     .table(&TESTTABLE.to_string())
489    ///     .primary_key(serde_json::json!({
490    ///         "id": "0001",
491    ///     }))
492    ///     .update(serde_json::json!({
493    ///         "bookName": "红楼梦",
494    ///         "author":   "曹雪芹",
495    ///         "page":     100,
496    ///     }))
497    ///     .build()?;
498    /// let _ret = client.update_row(&args).await?;
499    /// ```
500    pub async fn update_row(&self, args: &UpdateRowArgs) -> Result<CommonResponse, SdkError> {
501        let req = self.prepare_request(args.clone());
502        let res = req.send_and_log().await?;
503        Ok(res.json::<CommonResponse>().await?)
504    }
505
506    /// delete rows, you can delete multiple records by primary key, or filter the records to be deleted
507    /// ```rust
508    /// let args = DeleteRowArgsBuilder::default()
509    ///     .database(&TESTDATABSE.to_string())
510    ///     .table(&TESTTABLE.to_string())
511    ///     .primary_key(serde_json::json!({
512    ///         "id": "0001",
513    ///     }))
514    ///     .filter("page >= 22")
515    ///     .build()?;
516    /// let _ret = UTCLIENT.delete_rows(&args).await?;
517    /// ```
518    pub async fn delete_rows(&self, args: &DeleteRowArgs) -> Result<CommonResponse, SdkError> {
519        let req = self.prepare_request(args.clone());
520        let res = req.send_and_log().await?;
521        Ok(res.json::<CommonResponse>().await?)
522    }
523
524    /// query row, query single row by primary key
525    /// ```rust
526    /// let args = QueryRowArgsBuilder::default()
527    ///     .database(&TESTDATABSE.to_string())
528    ///     .table(&TESTTABLE.to_string())
529    ///     .primary_key(serde_json::json!({
530    ///         "id": "0001",
531    ///     }))
532    ///     .projections(vec!["id".to_string(), "bookName".to_string()])
533    ///     .retrieve_vector(false)
534    ///     .build()?;
535    /// let query_ret: QueryRowsResponse<MyRecord> = UTCLIENT.query_row(&args).await?;
536    /// println!("query_row ret: {:?}", query_ret.row);
537    /// let row1 = query_ret.row;
538    /// let query_ret: QueryRowsResponse<serde_json::Value> = UTCLIENT.query_row(&args).await?;
539    /// println!("query_row ret: {:?}", query_ret.row);
540    /// // convert json value to struct
541    /// let row2 = serde_json::from_value(query_ret.row)?;
542    /// assert_eq!(row1, row2);
543    /// ```
544    pub async fn query_row<T>(&self, args: &QueryRowArgs) -> Result<QueryRowsResponse<T>, SdkError>
545    where
546        T: for<'de> Deserialize<'de>,
547    {
548        let req = self.prepare_request(args.clone());
549        let res = req.send_and_log().await?;
550        Ok(res.json::<QueryRowsResponse<T>>().await?)
551    }
552
553    /// search rows
554    /// basing ann search of vector fields, support filter by scalar fields
555    /// ```rust
556    /// let search_args = SearchRowsArgsBuilder::default()
557    ///     .database(&TESTDATABSE.to_string())
558    ///     .table(&TESTTABLE.to_string())
559    ///     .anns(
560    ///         AnnsSearchParamsBuilder::default()
561    ///             .vector_field("vector")
562    ///             .vector_floats(vec![0.3123, 0.43, 0.213])
563    ///             .params(VectorSearchParams::HNSW(HNSWSearchParams {
564    ///                 ef: 200,
565    ///                 limit: 10,
566    ///                 distance_far: None,
567    ///                 distance_near: None,
568    ///                 pruning: false,
569    ///             }))
570    ///             .filter("bookName = '三国演义'")
571    ///             .build()?,
572    ///     )
573    ///     .retrieve_vector(true)
574    ///     .build()?;
575    /// let ret: SearchRowsResponse<serde_json::Value> = client.search_rows(&search_args).await?;
576    /// ```
577    pub async fn search_rows<T>(
578        &self,
579        args: &SearchRowsArgs,
580    ) -> Result<SearchRowsResponse<T>, SdkError>
581    where
582        T: for<'de> Deserialize<'de>,
583    {
584        let req = self.prepare_request(args.clone());
585        let res = req.send_and_log().await?;
586        Ok(res.json::<SearchRowsResponse<T>>().await?)
587    }
588
589    /// select rows
590    /// filter records by scalar fields
591    /// ```rust
592    /// let mut args = SelectRowsArgsBuilder::default()
593    ///     .database(&TESTDATABSE.to_string())
594    ///     .table(&TESTTABLE.to_string())
595    ///     .projections(vec![
596    ///         "id".to_string(),
597    ///         "bookName".to_string(),
598    ///         "page".to_string(),
599    ///     ])
600    ///     .filter("page > 21")
601    ///     .limit(1 as u32)
602    ///     .build()?;
603    /// loop {
604    ///     let ret: SelectRowsResponse<serde_json::Value> = client.select_rows(&args).await?;
605    ///     println!("select_rows ret: {:?}", ret);
606    ///     if !ret.is_truncated {
607    ///         break;
608    ///     } else {
609    ///         args.marker = Some(ret.next_marker);
610    ///     }
611    /// }
612    /// ```
613    pub async fn select_rows<T>(
614        &self,
615        args: &SelectRowsArgs,
616    ) -> Result<SelectRowsResponse<T>, SdkError>
617    where
618        T: for<'de> Deserialize<'de>,
619    {
620        let req = self.prepare_request(args.clone());
621        let res = req.send_and_log().await?;
622        Ok(res.json::<SelectRowsResponse<T>>().await?)
623    }
624
625    /// batch search rows
626    /// 1. basing ann search of vector fields, support filter by scalar fields
627    /// 2. support batch query
628    /// ```rust
629    /// let batch_ann_params = BatchAnnsSearchParamsBuilder::default()
630    ///     .vector_field("vector")
631    ///     .vector_floats(vec![vec![0.3123, 0.43, 0.213], vec![0.5512, 0.33, 0.43]])
632    ///     .params(VectorSearchParams::HNSW(HNSWSearchParams {
633    ///         ef: 200,
634    ///         limit: 10,
635    ///         distance_far: None,
636    ///         distance_near: None,
637    ///         pruning: false,
638    ///     }))
639    ///     .filter("bookName = '三国演义'")
640    ///     .build()?;
641    /// let batch_search_args = BatchSearchRowsArgsBuilder::default()
642    ///     .database(&TESTDATABSE.to_string())
643    ///     .table(&TESTTABLE.to_string())
644    ///     .anns(batch_ann_params)
645    ///     .retrieve_vector(true)
646    ///     .build()?;
647    /// let batch_rets: BatchSearchRowsResponse<serde_json::Value> =
648    ///     UTCLIENT.batch_search_rows(&batch_search_args).await?;
649    /// for (i, bs) in batch_rets.results.iter().enumerate() {
650    ///     println!("batch: {}, {:?}", i, bs.search_vector_floats);
651    ///     for (j, ss) in bs.rows.iter().enumerate() {
652    ///         println!("{}, {:?}", j, ss);
653    ///     }
654    /// }
655    /// ```
656    pub async fn batch_search_rows<T>(
657        &self,
658        args: &BatchSearchRowsArgs,
659    ) -> Result<BatchSearchRowsResponse<T>, SdkError>
660    where
661        T: for<'de> Deserialize<'de>,
662    {
663        let req = self.prepare_request(args.clone());
664        let res = req.send_and_log().await?;
665        Ok(res.json::<BatchSearchRowsResponse<T>>().await?)
666    }
667
668    fn prepare_request(&self, req: impl IntoRequest) -> RequestBuilder {
669        let mut req = req.into_request(&self.configuration, &self.http_client);
670        if !self.credential.token.is_empty() {
671            req = req.bearer_auth(&self.credential.token)
672        };
673        for (key, value) in &self.configuration.get_request_headers() {
674            req = req.header(key, value);
675        }
676        req.timeout(Duration::from_secs(self.configuration.time_out_seconds))
677    }
678}
679
680trait SendAndLog {
681    async fn send_and_log(self) -> Result<Response, SdkError>;
682}
683
684impl SendAndLog for RequestBuilder {
685    async fn send_and_log(self) -> Result<Response, SdkError> {
686        let res = self.send().await?;
687        let status_code = res.status();
688        let request_id = if let Some(header_value) = res.headers().get("Request-ID") {
689            if let Ok(request_id) = header_value.to_str() {
690                request_id.to_string()
691            } else {
692                "".to_string()
693            }
694        } else {
695            "".to_string()
696        };
697        if status_code.is_client_error() || status_code.is_server_error() {
698            // try to parse service error message, if failed, use default error message
699            let service_msg = res.json::<CommonResponse>().await;
700            let msg = match service_msg {
701                Ok(msg) => msg,
702                Err(e) => CommonResponse {
703                    code: -1,
704                    msg: format!("Service json error message decode failed: {}", e),
705                },
706            };
707            return Err(SdkError::ServiceError(ServiceError {
708                status_code: status_code.as_u16() as i32,
709                request_id: request_id,
710                server_code: msg.clone().code.into(),
711                resp: msg,
712            }));
713        }
714        Ok(res)
715    }
716}