1use crate::{
2 api::client::{BaserowClient, RequestTracing},
3 filter::{Filter, FilterTriple},
4 mapper::{FieldMapper, TableMapper},
5 Baserow, BaserowTable, OrderDirection,
6};
7use async_trait::async_trait;
8use reqwest::{header::AUTHORIZATION, Client, StatusCode};
9use serde::{de::DeserializeOwned, Deserialize, Serialize};
10use serde_json::Value;
11use std::{collections::HashMap, error::Error, vec};
12use tracing::{debug, info, instrument};
13
14#[derive(Deserialize, Serialize, Debug)]
18pub struct RowsResponse {
19 pub count: Option<i32>,
21 pub next: Option<String>,
23 pub previous: Option<String>,
25 pub results: Vec<HashMap<String, Value>>,
27}
28
29#[derive(Deserialize, Serialize, Debug)]
34pub struct TypedRowsResponse<T> {
35 pub count: Option<i32>,
37 pub next: Option<String>,
39 pub previous: Option<String>,
41 pub results: Vec<T>,
43}
44
45#[derive(Clone, Debug)]
50pub struct RowRequest {
51 pub view_id: Option<i32>,
53 pub order: Option<HashMap<String, OrderDirection>>,
55 pub filter: Option<Vec<FilterTriple>>,
57 pub page_size: Option<i32>,
59 pub page: Option<i32>,
61 pub user_field_names: Option<bool>,
63}
64
65impl Default for RowRequest {
66 fn default() -> Self {
67 Self {
68 view_id: None,
69 order: None,
70 filter: None,
71 page_size: Some(100),
72 page: Some(1),
73 user_field_names: None,
74 }
75 }
76}
77
78#[derive(Default)]
83pub struct RowRequestBuilder {
84 baserow: Option<Baserow>,
85 table: Option<BaserowTable>,
86 request: RowRequest,
87}
88
89impl RowRequestBuilder {
90 pub fn new() -> Self {
91 Self {
92 baserow: None,
93 table: None,
94 request: RowRequest::default(),
95 }
96 }
97
98 pub fn view(mut self, id: i32) -> Self {
100 self.request.view_id = Some(id);
101 self
102 }
103
104 pub fn size(mut self, size: i32) -> Self {
109 self.request.page_size = Some(size);
110 self
111 }
112
113 pub fn page(mut self, page: i32) -> Self {
118 self.request.page = Some(page);
119 self
120 }
121
122 pub fn user_field_names(mut self, enabled: bool) -> Self {
127 if self.table.as_ref().map_or(true, |t| t.mapper.is_none()) {
129 self.request.user_field_names = Some(enabled);
130 }
131 self
132 }
133
134 pub fn with_table(mut self, table: BaserowTable) -> Self {
135 self.table = Some(table);
136 self
137 }
138
139 pub fn with_baserow(mut self, baserow: Baserow) -> Self {
140 self.baserow = Some(baserow);
141 self
142 }
143
144 pub fn order_by(mut self, field: &str, direction: OrderDirection) -> Self {
146 match self.request.order {
147 Some(mut order) => {
148 order.insert(String::from(field), direction);
149 self.request.order = Some(order);
150 }
151 None => {
152 let mut order = HashMap::new();
153 order.insert(String::from(field), direction);
154 self.request.order = Some(order);
155 }
156 }
157 self
158 }
159
160 pub fn filter_by(mut self, field: &str, filter_op: Filter, value: &str) -> Self {
162 match self.request.filter {
163 Some(mut filter) => {
164 filter.push(FilterTriple {
165 field: String::from(field),
166 filter: filter_op,
167 value: String::from(value),
168 });
169 self.request.filter = Some(filter);
170 }
171 None => {
172 let mut filter: Vec<FilterTriple> = vec![];
173 filter.push(FilterTriple {
174 field: String::from(field),
175 filter: filter_op,
176 value: String::from(value),
177 });
178 self.request.filter = Some(filter);
179 }
180 }
181 self
182 }
183
184 pub async fn get<T>(self) -> Result<TypedRowsResponse<T>, Box<dyn Error>>
186 where
187 T: DeserializeOwned + 'static,
188 {
189 let table = self.table.expect("Table instance is missing");
190 let baserow = self.baserow.expect("Baserow instance is missing");
191 table.get(baserow, self.request).await
192 }
193}
194
195#[async_trait]
200pub trait BaserowTableOperations {
201 async fn auto_map(self) -> Result<BaserowTable, Box<dyn Error>>;
206
207 fn query(self) -> RowRequestBuilder;
213
214 async fn get<T>(
225 &self,
226 baserow: Baserow,
227 request: RowRequest,
228 ) -> Result<TypedRowsResponse<T>, Box<dyn Error>>
229 where
230 T: DeserializeOwned + 'static;
231
232 async fn create_one(
241 self,
242 data: HashMap<String, Value>,
243 user_field_names: Option<bool>,
244 ) -> Result<HashMap<String, Value>, Box<dyn Error>>;
245
246 async fn get_one<T>(self, id: u64, user_field_names: Option<bool>) -> Result<T, Box<dyn Error>>
258 where
259 T: DeserializeOwned + 'static;
260
261 async fn update(
271 self,
272 id: u64,
273 data: HashMap<String, Value>,
274 user_field_names: Option<bool>,
275 ) -> Result<HashMap<String, Value>, Box<dyn Error>>;
276
277 async fn delete(self, id: u64) -> Result<(), Box<dyn Error>>;
282}
283
284#[async_trait]
285impl BaserowTableOperations for BaserowTable {
286 #[instrument(skip(self), fields(table_id = ?self.id), err)]
287 async fn auto_map(mut self) -> Result<BaserowTable, Box<dyn Error>> {
288 let id = self.id.ok_or("Table ID is missing")?;
289
290 let baserow = self.baserow.clone().ok_or("Baserow instance is missing")?;
291 debug!("Fetching table fields for mapping");
292 let fields = baserow.table_fields(id).await?;
293 info!(
294 field_count = fields.len(),
295 "Successfully mapped table fields"
296 );
297
298 let mut mapper = TableMapper::new();
299 mapper.map_fields(fields.clone());
300 self.mapper = Some(mapper);
301
302 Ok(self)
303 }
304
305 fn query(self) -> RowRequestBuilder {
306 RowRequestBuilder::new()
307 .with_baserow(self.baserow.clone().unwrap())
308 .with_table(self.clone())
309 }
310
311 #[instrument(skip(self, baserow), fields(table_id = ?self.id), err)]
312 async fn get<T>(
313 &self,
314 baserow: Baserow,
315 request: RowRequest,
316 ) -> Result<TypedRowsResponse<T>, Box<dyn Error>>
317 where
318 T: DeserializeOwned + 'static,
319 {
320 if let Some(size) = request.page_size {
322 if size <= 0 {
323 return Err("Page size must be a positive integer".into());
324 }
325 }
326 if let Some(page) = request.page {
327 if page <= 0 {
328 return Err("Page number must be a positive integer".into());
329 }
330 }
331
332 let url = format!(
333 "{}/api/database/rows/table/{}/",
334 &baserow.configuration.base_url,
335 self.id.unwrap()
336 );
337
338 let mut req = Client::new().get(url);
339
340 if let Some(view_id) = request.view_id {
341 req = req.query(&[("view_id", view_id.to_string())]);
342 }
343
344 if baserow.configuration.jwt.is_some() {
345 req = req.header(
346 AUTHORIZATION,
347 format!(
348 "JWT {}",
349 &baserow.configuration.database_token.as_ref().unwrap()
350 ),
351 );
352 } else if baserow.configuration.database_token.is_some() {
353 req = req.header(
354 AUTHORIZATION,
355 format!(
356 "Token {}",
357 &baserow.configuration.database_token.as_ref().unwrap()
358 ),
359 );
360 }
361
362 if let Some(order) = request.order {
363 let mut order_str = String::new();
364 for (field, direction) in order {
365 let field_key = if let Some(mapper) = &self.mapper {
367 if let Some(field_id) = mapper.get_field_id(&field) {
368 format!("field_{}", field_id)
369 } else {
370 field
371 }
372 } else {
373 field
374 };
375
376 order_str.push_str(&format!(
377 "{}{}",
378 match direction {
379 OrderDirection::Asc => "",
380 OrderDirection::Desc => "-",
381 },
382 field_key
383 ));
384 }
385
386 req = req.query(&[("order_by", order_str)]);
387 }
388
389 if let Some(filter) = request.filter {
390 for triple in filter {
391 let field_key = if let Some(mapper) = &self.mapper {
393 if let Some(field_id) = mapper.get_field_id(&triple.field) {
394 format!("field_{}", field_id)
395 } else {
396 triple.field
397 }
398 } else {
399 triple.field
400 };
401
402 req = req.query(&[(
403 &format!("filter__{}__{}", field_key, triple.filter.as_str()),
404 triple.value,
405 )]);
406 }
407 }
408
409 if let Some(size) = request.page_size {
410 req = req.query(&[("size", size.to_string())]);
411 }
412
413 if let Some(page) = request.page {
414 req = req.query(&[("page", page.to_string())]);
415 }
416
417 if let Some(user_field_names) = request.user_field_names {
418 req = req.query(&[("user_field_names", user_field_names.to_string())]);
419 }
420
421 debug!("Executing table query");
422 let resp = baserow.trace_request(&baserow.client, req.build()?).await?;
423
424 match resp.status() {
425 StatusCode::OK => {
426 let response: RowsResponse = resp.json().await?;
427
428 let typed_results = if let Some(mapper) = &self.mapper {
429 response
431 .results
432 .into_iter()
433 .map(|row| mapper.deserialize_row(row))
434 .collect::<Result<Vec<T>, _>>()?
435 } else {
436 serde_json::from_value::<Vec<T>>(Value::Array(
438 response
439 .results
440 .into_iter()
441 .map(|m| Value::Object(serde_json::Map::from_iter(m.into_iter())))
442 .collect(),
443 ))?
444 };
445
446 Ok(TypedRowsResponse {
447 count: response.count,
448 next: response.next,
449 previous: response.previous,
450 results: typed_results,
451 })
452 }
453 _ => Err(Box::new(resp.error_for_status().unwrap_err())),
454 }
455 }
456
457 #[instrument(skip(self, data), fields(table_id = ?self.id, field_count = data.len()), err)]
458 async fn create_one(
459 self,
460 data: HashMap<String, Value>,
461 user_field_names: Option<bool>,
462 ) -> Result<HashMap<String, Value>, Box<dyn Error>> {
463 let baserow = self.baserow.expect("Baserow instance is missing");
464
465 let url = format!(
466 "{}/api/database/rows/table/{}/",
467 &baserow.configuration.base_url,
468 self.id.unwrap()
469 );
470
471 let mut req = baserow.client.post(url);
472
473 let request_data = if self.mapper.is_some() {
475 self.mapper.as_ref().unwrap().convert_to_field_ids(data)
476 } else {
477 data
478 };
479
480 if let Some(use_names) = user_field_names {
482 req = req.query(&[("user_field_names", use_names.to_string())]);
483 }
484
485 if baserow.configuration.jwt.is_some() {
486 req = req.header(
487 AUTHORIZATION,
488 format!("JWT {}", &baserow.configuration.jwt.as_ref().unwrap()),
489 );
490 } else if baserow.configuration.database_token.is_some() {
491 req = req.header(
492 AUTHORIZATION,
493 format!(
494 "Token {}",
495 &baserow.configuration.database_token.as_ref().unwrap()
496 ),
497 );
498 }
499
500 debug!("Creating new record");
501 let resp = baserow
502 .trace_request(&baserow.client, req.json(&request_data).build()?)
503 .await?;
504
505 match resp.status() {
506 StatusCode::OK => {
507 let response_data = resp.json::<HashMap<String, Value>>().await?;
508
509 if self.mapper.is_some() && user_field_names != Some(true) {
511 Ok(self
512 .mapper
513 .as_ref()
514 .unwrap()
515 .convert_to_field_names(response_data))
516 } else {
517 Ok(response_data)
518 }
519 }
520 _ => Err(Box::new(resp.error_for_status().unwrap_err())),
521 }
522 }
523
524 #[instrument(skip(self), fields(table_id = ?self.id, record_id = %id), err)]
525 async fn get_one<T>(
526 mut self,
527 id: u64,
528 user_field_names: Option<bool>,
529 ) -> Result<T, Box<dyn Error>>
530 where
531 T: DeserializeOwned + 'static,
532 {
533 let baserow = self.baserow.expect("Baserow instance is missing");
534
535 let url = format!(
536 "{}/api/database/rows/table/{}/{}/",
537 &baserow.configuration.base_url,
538 self.id.unwrap(),
539 id
540 );
541
542 let mut req = baserow.client.get(url);
543
544 if let Some(use_names) = user_field_names {
545 req = req.query(&[("user_field_names", use_names.to_string())]);
546 }
547
548 if baserow.configuration.jwt.is_some() {
549 req = req.header(
550 AUTHORIZATION,
551 format!("JWT {}", &baserow.configuration.jwt.as_ref().unwrap()),
552 );
553 } else if baserow.configuration.database_token.is_some() {
554 req = req.header(
555 AUTHORIZATION,
556 format!(
557 "Token {}",
558 &baserow.configuration.database_token.as_ref().unwrap()
559 ),
560 );
561 }
562
563 debug!("Fetching single record");
564 let resp = baserow.trace_request(&baserow.client, req.build()?).await?;
565
566 match resp.status() {
567 StatusCode::OK => {
568 let row: HashMap<String, Value> = resp.json().await?;
569
570 if std::any::TypeId::of::<T>() == std::any::TypeId::of::<HashMap<String, Value>>() {
572 Ok(serde_json::from_value(serde_json::to_value(row)?)?)
573 } else {
574 let mapper = self.mapper.clone().ok_or("Table mapper is missing. Call auto_map() first when using typed responses.")?;
576 Ok(mapper.deserialize_row(row)?)
577 }
578 }
579 _ => Err(Box::new(resp.error_for_status().unwrap_err())),
580 }
581 }
582
583 #[instrument(skip(self, data), fields(table_id = ?self.id, record_id = %id, field_count = data.len()), err)]
584 async fn update(
585 self,
586 id: u64,
587 data: HashMap<String, Value>,
588 user_field_names: Option<bool>,
589 ) -> Result<HashMap<String, Value>, Box<dyn Error>> {
590 let baserow = self.baserow.expect("Baserow instance is missing");
591
592 let url = format!(
593 "{}/api/database/rows/table/{}/{}/",
594 &baserow.configuration.base_url,
595 self.id.unwrap(),
596 id
597 );
598
599 let mut req = baserow.client.patch(url);
600
601 let request_data = if self.mapper.is_some() {
603 self.mapper.as_ref().unwrap().convert_to_field_ids(data)
604 } else {
605 data
606 };
607
608 if let Some(use_names) = user_field_names {
610 req = req.query(&[("user_field_names", use_names.to_string())]);
611 }
612
613 if baserow.configuration.jwt.is_some() {
614 req = req.header(
615 AUTHORIZATION,
616 format!("JWT {}", &baserow.configuration.jwt.as_ref().unwrap()),
617 );
618 } else if baserow.configuration.database_token.is_some() {
619 req = req.header(
620 AUTHORIZATION,
621 format!(
622 "Token {}",
623 &baserow.configuration.database_token.as_ref().unwrap()
624 ),
625 );
626 }
627
628 debug!("Updating record");
629 let resp = baserow
630 .trace_request(&baserow.client, req.json(&request_data).build()?)
631 .await?;
632
633 match resp.status() {
634 StatusCode::OK => {
635 let response_data = resp.json::<HashMap<String, Value>>().await?;
636
637 if self.mapper.is_some() && user_field_names != Some(true) {
639 Ok(self
640 .mapper
641 .as_ref()
642 .unwrap()
643 .convert_to_field_names(response_data))
644 } else {
645 Ok(response_data)
646 }
647 }
648 _ => Err(Box::new(resp.error_for_status().unwrap_err())),
649 }
650 }
651
652 #[instrument(skip(self), fields(table_id = ?self.id, record_id = %id), err)]
653 async fn delete(self, id: u64) -> Result<(), Box<dyn Error>> {
654 let baserow = self.baserow.expect("Baserow instance is missing");
655
656 let url = format!(
657 "{}/api/database/rows/table/{}/{}/",
658 &baserow.configuration.base_url,
659 self.id.unwrap(),
660 id
661 );
662
663 let mut req = baserow.client.delete(url);
664
665 if baserow.configuration.jwt.is_some() {
666 req = req.header(
667 AUTHORIZATION,
668 format!("JWT {}", &baserow.configuration.jwt.as_ref().unwrap()),
669 );
670 } else if baserow.configuration.database_token.is_some() {
671 req = req.header(
672 AUTHORIZATION,
673 format!(
674 "Token {}",
675 &baserow.configuration.database_token.as_ref().unwrap()
676 ),
677 );
678 }
679
680 debug!("Deleting record");
681 let resp = baserow.trace_request(&baserow.client, req.build()?).await?;
682
683 match resp.status() {
684 StatusCode::OK => Ok(()),
685 _ => Err(Box::new(resp.error_for_status().unwrap_err())),
686 }
687 }
688}
689
690#[cfg(test)]
691mod tests {
692 use super::*;
693 use crate::{
694 api::client::BaserowClient, filter::Filter, Baserow, BaserowTableOperations, ConfigBuilder,
695 OrderDirection,
696 };
697 use serde::Deserialize;
698 use serde_json::Value;
699 use std::collections::HashMap;
700
701 #[derive(Debug, Deserialize, PartialEq)]
702 struct TestUser {
703 name: String,
704 }
705
706 #[derive(Debug, Deserialize, PartialEq)]
707 struct ComplexRecord {
708 id: u64,
709 name: String,
710 email: String,
711 age: Option<i32>,
712 is_active: bool,
713 created_at: String,
714 }
715
716 #[tokio::test]
717 async fn test_collection_deserialization() {
718 let mut server = mockito::Server::new_async().await;
719 let mock_url = server.url();
720
721 let fields_mock = server
723 .mock("GET", "/api/database/fields/table/1234/")
724 .with_status(200)
725 .with_header("Content-Type", "application/json")
726 .with_body(r#"[
727 {"id": 1, "table_id": 1234, "name": "id", "order": 0, "type": "number", "primary": true, "read_only": false},
728 {"id": 2, "table_id": 1234, "name": "name", "order": 1, "type": "text", "primary": false, "read_only": false},
729 {"id": 3, "table_id": 1234, "name": "email", "order": 2, "type": "text", "primary": false, "read_only": false},
730 {"id": 4, "table_id": 1234, "name": "age", "order": 3, "type": "number", "primary": false, "read_only": false},
731 {"id": 5, "table_id": 1234, "name": "is_active", "order": 4, "type": "boolean", "primary": false, "read_only": false},
732 {"id": 6, "table_id": 1234, "name": "created_at", "order": 5, "type": "text", "primary": false, "read_only": false}
733 ]"#)
734 .create();
735
736 let rows_mock = server
738 .mock("GET", "/api/database/rows/table/1234/")
739 .match_query(mockito::Matcher::Any)
740 .with_status(200)
741 .with_header("Content-Type", "application/json")
742 .with_body(
743 r#"{
744 "count": 2,
745 "next": null,
746 "previous": null,
747 "results": [
748 {
749 "field_1": 101,
750 "field_2": "John Doe",
751 "field_3": "john@example.com",
752 "field_4": 30,
753 "field_5": true,
754 "field_6": "2023-01-01T00:00:00Z"
755 },
756 {
757 "field_1": 102,
758 "field_2": "Jane Smith",
759 "field_3": "jane@example.com",
760 "field_4": null,
761 "field_5": false,
762 "field_6": "2023-01-02T00:00:00Z"
763 }
764 ]
765 }"#,
766 )
767 .create();
768
769 let configuration = ConfigBuilder::new()
770 .base_url(&mock_url)
771 .api_key("test-token")
772 .build();
773 let baserow = Baserow::with_configuration(configuration);
774 let table = baserow.table_by_id(1234);
775
776 let mapped_table = table.auto_map().await.unwrap();
778 let response = mapped_table.query().get::<ComplexRecord>().await.unwrap();
779
780 assert_eq!(response.count, Some(2));
781 assert_eq!(response.results.len(), 2);
782
783 let record1 = &response.results[0];
785 assert_eq!(record1.id, 101);
786 assert_eq!(record1.name, "John Doe");
787 assert_eq!(record1.email, "john@example.com");
788 assert_eq!(record1.age, Some(30));
789 assert_eq!(record1.is_active, true);
790 assert_eq!(record1.created_at, "2023-01-01T00:00:00Z");
791
792 let record2 = &response.results[1];
794 assert_eq!(record2.id, 102);
795 assert_eq!(record2.name, "Jane Smith");
796 assert_eq!(record2.email, "jane@example.com");
797 assert_eq!(record2.age, None);
798 assert_eq!(record2.is_active, false);
799 assert_eq!(record2.created_at, "2023-01-02T00:00:00Z");
800
801 fields_mock.assert();
802 rows_mock.assert();
803 }
804
805 #[tokio::test]
806 async fn test_auto_map_and_user_field_names_exclusivity() {
807 let mut server = mockito::Server::new_async().await;
808 let mock_url = server.url();
809
810 let fields_mock = server
812 .mock("GET", "/api/database/fields/table/1234/")
813 .with_status(200)
814 .with_header("Content-Type", "application/json")
815 .with_body(r#"[{"id": 1, "table_id": 1234, "name": "Name", "order": 0, "type": "text", "primary": true, "read_only": false}]"#)
816 .create();
817
818 let rows_mock = server
820 .mock("GET", "/api/database/rows/table/1234/")
821 .match_query(mockito::Matcher::Any)
822 .expect_at_least(1)
823 .with_status(200)
824 .with_header("Content-Type", "application/json")
825 .with_body(
826 r#"{"count": 1, "next": null, "previous": null, "results": [{"field_1": "test"}]}"#,
827 )
828 .create();
829
830 let configuration = ConfigBuilder::new()
831 .base_url(&mock_url)
832 .api_key("test-token")
833 .build();
834 let baserow = Baserow::with_configuration(configuration);
835 let table = baserow.table_by_id(1234);
836
837 let mapped_table = table.clone().auto_map().await.unwrap();
839 let _query = mapped_table
840 .query()
841 .user_field_names(true) .get::<HashMap<String, Value>>()
843 .await
844 .unwrap();
845
846 rows_mock.assert();
848 fields_mock.assert();
849 }
850
851 #[tokio::test]
852 async fn test_field_mapping_in_query_params() {
853 let mut server = mockito::Server::new_async().await;
854 let mock_url = server.url();
855
856 let fields_mock = server
858 .mock("GET", "/api/database/fields/table/1234/")
859 .with_status(200)
860 .with_header("Content-Type", "application/json")
861 .with_body(r#"[
862 {"id": 1, "table_id": 1234, "name": "name", "order": 0, "type": "text", "primary": true, "read_only": false},
863 {"id": 2, "table_id": 1234, "name": "age", "order": 1, "type": "number", "primary": false, "read_only": false}
864 ]"#)
865 .create();
866
867 let rows_mock = server
869 .mock("GET", "/api/database/rows/table/1234/")
870 .match_query(mockito::Matcher::AllOf(vec![
871 mockito::Matcher::UrlEncoded(
872 "order_by".into(),
873 "field_1".into(), ),
875 mockito::Matcher::UrlEncoded(
876 "filter__field_2__equal".into(), "25".into(),
878 ),
879 ]))
880 .with_status(200)
881 .with_header("Content-Type", "application/json")
882 .with_body(r#"{"count": 1, "next": null, "previous": null, "results": [{"field_1": "John", "field_2": 25}]}"#)
883 .create();
884
885 let configuration = ConfigBuilder::new()
886 .base_url(&mock_url)
887 .api_key("test-token")
888 .build();
889 let baserow = Baserow::with_configuration(configuration);
890 let table = baserow.table_by_id(1234);
891
892 let mapped_table = table.auto_map().await.unwrap();
894 let _result = mapped_table
895 .query()
896 .order_by("name", OrderDirection::Asc)
897 .filter_by("age", Filter::Equal, "25")
898 .get::<HashMap<String, Value>>()
899 .await
900 .unwrap();
901
902 fields_mock.assert();
904 rows_mock.assert();
905 }
906
907 #[tokio::test]
908 async fn test_struct_deserialization_with_both_options() {
909 let mut server = mockito::Server::new_async().await;
910 let mock_url = server.url();
911
912 let fields_mock = server
914 .mock("GET", "/api/database/fields/table/1234/")
915 .with_status(200)
916 .with_header("Content-Type", "application/json")
917 .with_body(r#"[{"id": 1, "table_id": 1234, "name": "name", "order": 0, "type": "text", "primary": true, "read_only": false}]"#)
918 .create();
919
920 let rows_mock_auto_map = server
922 .mock("GET", "/api/database/rows/table/1234/")
923 .match_query(mockito::Matcher::Any)
924 .with_status(200)
925 .with_header("Content-Type", "application/json")
926 .with_body(
927 r#"{"count": 1, "next": null, "previous": null, "results": [{"field_1": "John"}]}"#,
928 )
929 .create();
930
931 let rows_mock_user_names = server
933 .mock("GET", "/api/database/rows/table/1234/")
934 .match_query(mockito::Matcher::AllOf(vec![mockito::Matcher::UrlEncoded(
935 "user_field_names".into(),
936 "true".into(),
937 )]))
938 .with_status(200)
939 .with_header("Content-Type", "application/json")
940 .with_body(
941 r#"{"count": 1, "next": null, "previous": null, "results": [{"name": "John"}]}"#,
942 )
943 .create();
944
945 let configuration = ConfigBuilder::new()
946 .base_url(&mock_url)
947 .api_key("test-token")
948 .build();
949 let baserow = Baserow::with_configuration(configuration);
950 let table = baserow.table_by_id(1234);
951
952 let mapped_table = table.clone().auto_map().await.unwrap();
954 let auto_map_result = mapped_table.query().get::<TestUser>().await.unwrap();
955
956 assert_eq!(
957 auto_map_result.results[0],
958 TestUser {
959 name: "John".to_string()
960 }
961 );
962
963 let user_names_result = table
965 .query()
966 .user_field_names(true)
967 .get::<TestUser>()
968 .await
969 .unwrap();
970
971 assert_eq!(
972 user_names_result.results[0],
973 TestUser {
974 name: "John".to_string()
975 }
976 );
977
978 fields_mock.assert();
980 rows_mock_auto_map.assert();
981 rows_mock_user_names.assert();
982 }
983}