1use crate::api::auth::{AuthManager, Credentials};
4use crate::api::CardListParams;
5use crate::core::error::{Error, Result};
6use crate::core::models::common::{CardId, ExportFormat};
7#[cfg(feature = "query-builder")]
8use crate::core::models::mbql::MbqlQuery;
9use crate::core::models::{
10 Card, Collection, Dashboard, DatabaseMetadata, DatasetQuery, Field, HealthStatus, MetabaseId,
11 NativeQuery, Pagination, QueryResult, SyncResult, User,
12};
13use crate::service::ServiceManager;
14use crate::transport::http_provider_safe::{HttpClientAdapter, HttpProviderSafe};
15use crate::transport::HttpClient;
16use serde_json::{json, Value};
17use std::collections::HashMap;
18use std::sync::Arc;
19
20#[cfg(feature = "cache")]
21use crate::cache::{cache_key, CacheConfig, CacheLayer};
22
23#[derive(Clone)]
25pub struct MetabaseClient {
26 pub(super) auth_manager: AuthManager,
27 pub(super) base_url: String,
28 pub(super) service_manager: ServiceManager,
29 #[cfg(feature = "cache")]
30 pub(super) cache: CacheLayer,
31}
32
33impl MetabaseClient {
34 pub fn new(base_url: impl Into<String>) -> Result<Self> {
36 let base_url = base_url.into();
37
38 if !base_url.starts_with("http://") && !base_url.starts_with("https://") {
40 return Err(Error::Config(
41 "Invalid URL: must start with http:// or https://".to_string(),
42 ));
43 }
44
45 let http_client = HttpClient::new(&base_url)?;
46 let auth_manager = AuthManager::new();
47
48 let http_provider: Arc<dyn HttpProviderSafe> =
50 Arc::new(HttpClientAdapter::new(http_client));
51 let service_manager = ServiceManager::new(http_provider);
52
53 Ok(Self {
54 auth_manager,
55 base_url,
56 service_manager,
57 #[cfg(feature = "cache")]
58 cache: CacheLayer::new(CacheConfig::default()),
59 })
60 }
61
62 #[cfg(feature = "cache")]
64 pub fn with_cache(base_url: impl Into<String>, cache_config: CacheConfig) -> Result<Self> {
65 let base_url = base_url.into();
66
67 if !base_url.starts_with("http://") && !base_url.starts_with("https://") {
69 return Err(Error::Config(
70 "Invalid URL: must start with http:// or https://".to_string(),
71 ));
72 }
73
74 let http_client = HttpClient::new(&base_url)?;
75 let auth_manager = AuthManager::new();
76
77 let http_provider: Arc<dyn HttpProviderSafe> =
79 Arc::new(HttpClientAdapter::new(http_client));
80 let service_manager = ServiceManager::new(http_provider);
81
82 Ok(Self {
83 auth_manager,
84 base_url,
85 service_manager,
86 cache: CacheLayer::new(cache_config),
87 })
88 }
89
90 pub fn base_url(&self) -> &str {
92 &self.base_url
93 }
94
95 pub fn is_authenticated(&self) -> bool {
97 self.auth_manager.is_authenticated()
98 }
99
100 #[cfg(feature = "cache")]
102 pub fn is_cache_enabled(&self) -> bool {
103 self.cache.is_enabled()
104 }
105
106 #[cfg(feature = "cache")]
108 pub fn set_cache_enabled(&mut self, enabled: bool) {
109 self.cache.set_enabled(enabled);
110 }
111
112 #[cfg(not(feature = "cache"))]
114 pub fn is_cache_enabled(&self) -> bool {
115 false
116 }
117
118 #[cfg(not(feature = "cache"))]
120 pub fn set_cache_enabled(&mut self, _enabled: bool) {
121 }
123
124 pub async fn authenticate(&mut self, credentials: Credentials) -> Result<()> {
126 let (session_id, user) = self
128 .service_manager
129 .auth_service()
130 .ok_or_else(|| Error::Config("Auth service not available".to_string()))?
131 .authenticate(credentials)
132 .await
133 .map_err(|e| Error::Config(format!("Service error: {}", e)))?;
134
135 self.auth_manager.set_session(session_id, user);
136 Ok(())
137 }
138
139 pub async fn logout(&mut self) -> Result<()> {
141 if !self.is_authenticated() {
142 return Ok(());
143 }
144
145 let session_id = self.auth_manager.get_session_id();
147
148 if let Some(id) = session_id {
149 self.service_manager
151 .auth_service()
152 .ok_or_else(|| Error::Config("Auth service not available".to_string()))?
153 .logout(&id)
154 .await
155 .map_err(|e| Error::Config(format!("Service error: {}", e)))?;
156 }
157
158 self.auth_manager.clear_session();
160 Ok(())
161 }
162
163 pub async fn health_check(&self) -> Result<HealthStatus> {
165 self.service_manager
167 .auth_service()
168 .ok_or_else(|| Error::Config("Auth service not available".to_string()))?
169 .health_check()
170 .await
171 .map_err(|e| Error::Config(format!("Service error: {}", e)))
172 }
173
174 pub async fn get_current_user(&self) -> Result<User> {
176 if !self.is_authenticated() {
177 return Err(Error::Authentication("Not authenticated".to_string()));
178 }
179
180 let session_id = self
181 .auth_manager
182 .get_session_id()
183 .ok_or_else(|| Error::Authentication("No session available".to_string()))?;
184
185 self.service_manager
187 .auth_service()
188 .ok_or_else(|| Error::Config("Auth service not available".to_string()))?
189 .get_current_user(&session_id)
190 .await
191 .map_err(|e| Error::Config(format!("Service error: {}", e)))
192 }
193
194 pub async fn get_card(&self, id: i64) -> Result<Card> {
198 #[cfg(feature = "cache")]
199 {
200 let cache_key = cache_key("card", id);
201 if let Some(card) = self.cache.get_metadata::<Card>(&cache_key) {
202 return Ok(card);
203 }
204 }
205
206 let card = self
208 .service_manager
209 .card_service()
210 .ok_or_else(|| Error::Config("Card service not available".to_string()))?
211 .get_card(CardId(id as i32))
212 .await
213 .map_err(|e| Error::Config(format!("Service error: {}", e)))?;
214
215 #[cfg(feature = "cache")]
216 {
217 let cache_key = cache_key("card", id);
218 let _ = self.cache.set_metadata(cache_key, &card);
219 }
220
221 Ok(card)
222 }
223
224 pub async fn list_cards(&self, params: Option<CardListParams>) -> Result<Vec<Card>> {
226 use crate::repository::card::CardFilterParams;
227
228 let filters = params.map(|p| CardFilterParams {
230 f: p.f,
231 model_type: p.model_type,
232 archived: None,
233 collection_id: None,
234 });
235
236 self.service_manager
238 .card_service()
239 .ok_or_else(|| Error::Config("Card service not available".to_string()))?
240 .list_cards(None, filters)
241 .await
242 .map_err(|e| Error::Config(format!("Service error: {}", e)))
243 }
244
245 pub async fn create_card(&self, card: Card) -> Result<Card> {
247 if !self.is_authenticated() {
248 return Err(Error::Authentication(
249 "Authentication required to create card".to_string(),
250 ));
251 }
252
253 self.service_manager
255 .card_service()
256 .ok_or_else(|| Error::Config("Card service not available".to_string()))?
257 .create_card(card)
258 .await
259 .map_err(|e| Error::Config(format!("Service error: {}", e)))
260 }
261
262 pub async fn update_card(&self, id: i64, updates: serde_json::Value) -> Result<Card> {
264 if !self.is_authenticated() {
265 return Err(Error::Authentication(
266 "Authentication required to update card".to_string(),
267 ));
268 }
269
270 #[cfg(feature = "cache")]
271 {
272 let cache_key = cache_key("card", id);
273 self.cache.invalidate(&cache_key);
274 }
275
276 let service = self
278 .service_manager
279 .card_service()
280 .ok_or_else(|| Error::Config("Card service not available".to_string()))?;
281
282 let mut existing_card = service
283 .get_card(CardId(id as i32))
284 .await
285 .map_err(|e| Error::Config(format!("Service error: {}", e)))?;
286
287 if let Some(name) = updates.get("name").and_then(|v| v.as_str()) {
289 existing_card.name = name.to_string();
290 }
291 if let Some(description) = updates.get("description") {
292 existing_card.description = if description.is_null() {
293 None
294 } else {
295 description.as_str().map(|s| s.to_string())
296 };
297 }
298 if let Some(display) = updates.get("display").and_then(|v| v.as_str()) {
299 existing_card.display = display.to_string();
300 }
301 if let Some(dataset_query) = updates.get("dataset_query") {
302 existing_card.dataset_query = Some(dataset_query.clone());
303 }
304 if let Some(visualization_settings) = updates.get("visualization_settings") {
305 existing_card.visualization_settings = visualization_settings.clone();
306 }
307
308 service
310 .update_card(CardId(id as i32), existing_card)
311 .await
312 .map_err(|e| Error::Config(format!("Service error: {}", e)))
313 }
314
315 pub async fn delete_card(&self, id: i64) -> Result<()> {
317 if !self.is_authenticated() {
318 return Err(Error::Authentication(
319 "Authentication required to delete card".to_string(),
320 ));
321 }
322
323 #[cfg(feature = "cache")]
324 {
325 let cache_key = cache_key("card", id);
326 self.cache.invalidate(&cache_key);
327 }
328
329 self.service_manager
331 .card_service()
332 .ok_or_else(|| Error::Config("Card service not available".to_string()))?
333 .delete_card(CardId(id as i32))
334 .await
335 .map_err(|e| Error::Config(format!("Service error: {}", e)))
336 }
337
338 pub async fn get_collection(&self, id: MetabaseId) -> Result<Collection> {
342 use crate::core::models::common::CollectionId;
343
344 #[cfg(feature = "cache")]
345 {
346 let cache_key = cache_key("collection", id.0);
347 if let Some(collection) = self.cache.get_metadata::<Collection>(&cache_key) {
348 return Ok(collection);
349 }
350 }
351
352 let collection = self
354 .service_manager
355 .collection_service()
356 .ok_or_else(|| Error::Config("Collection service not available".to_string()))?
357 .get_collection(CollectionId(id.0 as i32))
358 .await
359 .map_err(|e| Error::Config(format!("Service error: {}", e)))?;
360
361 #[cfg(feature = "cache")]
362 {
363 let cache_key = cache_key("collection", id.0);
364 let _ = self.cache.set_metadata(cache_key, &collection);
365 }
366
367 Ok(collection)
368 }
369
370 pub async fn list_collections(&self) -> Result<Vec<Collection>> {
372 self.service_manager
374 .collection_service()
375 .ok_or_else(|| Error::Config("Collection service not available".to_string()))?
376 .list_collections(None, None)
377 .await
378 .map_err(|e| Error::Config(format!("Service error: {}", e)))
379 }
380
381 pub async fn create_collection(&self, collection: Collection) -> Result<Collection> {
383 if !self.is_authenticated() {
384 return Err(Error::Authentication(
385 "Authentication required to create collection".to_string(),
386 ));
387 }
388
389 self.service_manager
391 .collection_service()
392 .ok_or_else(|| Error::Config("Collection service not available".to_string()))?
393 .create_collection(collection)
394 .await
395 .map_err(|e| Error::Config(format!("Service error: {}", e)))
396 }
397
398 pub async fn update_collection(
400 &self,
401 id: MetabaseId,
402 updates: serde_json::Value,
403 ) -> Result<Collection> {
404 use crate::core::models::common::CollectionId;
405
406 if !self.is_authenticated() {
407 return Err(Error::Authentication(
408 "Authentication required to update collection".to_string(),
409 ));
410 }
411
412 #[cfg(feature = "cache")]
413 {
414 let cache_key = cache_key("collection", id.0);
415 self.cache.invalidate(&cache_key);
416 }
417
418 let service = self
420 .service_manager
421 .collection_service()
422 .ok_or_else(|| Error::Config("Collection service not available".to_string()))?;
423
424 let mut existing_collection = service
425 .get_collection(CollectionId(id.0 as i32))
426 .await
427 .map_err(|e| Error::Config(format!("Service error: {}", e)))?;
428
429 if let Some(name) = updates.get("name").and_then(|v| v.as_str()) {
431 existing_collection.name = name.to_string();
432 }
433 if let Some(description) = updates.get("description") {
434 existing_collection.description = if description.is_null() {
435 None
436 } else {
437 description.as_str().map(|s| s.to_string())
438 };
439 }
440 if let Some(parent_id) = updates.get("parent_id") {
441 existing_collection.parent_id = if parent_id.is_null() {
442 None
443 } else {
444 parent_id.as_i64().map(|id| id as i32)
445 };
446 }
447 if let Some(color) = updates.get("color") {
448 existing_collection.color = if color.is_null() {
449 None
450 } else {
451 color.as_str().map(|s| s.to_string())
452 };
453 }
454 if let Some(archived) = updates.get("archived").and_then(|v| v.as_bool()) {
455 existing_collection.archived = Some(archived);
456 }
457
458 service
460 .update_collection(CollectionId(id.0 as i32), existing_collection)
461 .await
462 .map_err(|e| Error::Config(format!("Service error: {}", e)))
463 }
464
465 pub async fn archive_collection(&self, id: MetabaseId) -> Result<Collection> {
467 use crate::core::models::common::CollectionId;
468
469 if !self.is_authenticated() {
470 return Err(Error::Authentication(
471 "Authentication required to archive collection".to_string(),
472 ));
473 }
474
475 #[cfg(feature = "cache")]
476 {
477 let cache_key = cache_key("collection", id.0);
478 self.cache.invalidate(&cache_key);
479 }
480
481 self.service_manager
483 .collection_service()
484 .ok_or_else(|| Error::Config("Collection service not available".to_string()))?
485 .archive_collection(CollectionId(id.0 as i32))
486 .await
487 .map_err(|e| Error::Config(format!("Service error: {}", e)))?;
488
489 self.get_collection(id).await
491 }
492
493 pub async fn get_dashboard(&self, id: MetabaseId) -> Result<Dashboard> {
497 use crate::core::models::common::DashboardId;
498
499 #[cfg(feature = "cache")]
500 {
501 let cache_key = cache_key("dashboard", id.0);
502 if let Some(dashboard) = self.cache.get_metadata::<Dashboard>(&cache_key) {
503 return Ok(dashboard);
504 }
505 }
506
507 let dashboard = self
509 .service_manager
510 .dashboard_service()
511 .ok_or_else(|| Error::Config("Dashboard service not available".to_string()))?
512 .get_dashboard(DashboardId(id.0 as i32))
513 .await
514 .map_err(|e| Error::Config(format!("Service error: {}", e)))?;
515
516 #[cfg(feature = "cache")]
517 {
518 let cache_key = cache_key("dashboard", id.0);
519 let _ = self.cache.set_metadata(cache_key, &dashboard);
520 }
521
522 Ok(dashboard)
523 }
524
525 pub async fn list_dashboards(&self, pagination: Option<Pagination>) -> Result<Vec<Dashboard>> {
527 use crate::repository::traits::PaginationParams;
528
529 let pagination_params = pagination.map(|p| PaginationParams {
531 page: None, limit: Some(p.limit() as u32),
533 offset: Some(p.offset() as u32),
534 });
535
536 self.service_manager
538 .dashboard_service()
539 .ok_or_else(|| Error::Config("Dashboard service not available".to_string()))?
540 .list_dashboards(pagination_params, None)
541 .await
542 .map_err(|e| Error::Config(format!("Service error: {}", e)))
543 }
544
545 pub async fn create_dashboard(&self, dashboard: Dashboard) -> Result<Dashboard> {
547 if !self.is_authenticated() {
548 return Err(Error::Authentication(
549 "Authentication required to create dashboard".to_string(),
550 ));
551 }
552
553 self.service_manager
555 .dashboard_service()
556 .ok_or_else(|| Error::Config("Dashboard service not available".to_string()))?
557 .create_dashboard(dashboard)
558 .await
559 .map_err(|e| Error::Config(format!("Service error: {}", e)))
560 }
561
562 pub async fn update_dashboard(
564 &self,
565 id: MetabaseId,
566 updates: serde_json::Value,
567 ) -> Result<Dashboard> {
568 use crate::core::models::common::DashboardId;
569
570 if !self.is_authenticated() {
571 return Err(Error::Authentication(
572 "Authentication required to update dashboard".to_string(),
573 ));
574 }
575
576 #[cfg(feature = "cache")]
577 {
578 let cache_key = cache_key("dashboard", id.0);
579 self.cache.invalidate(&cache_key);
580 }
581
582 let dashboard: Dashboard = serde_json::from_value(updates)
584 .map_err(|e| Error::Validation(format!("Invalid dashboard update: {}", e)))?;
585
586 self.service_manager
588 .dashboard_service()
589 .ok_or_else(|| Error::Config("Dashboard service not available".to_string()))?
590 .update_dashboard(DashboardId(id.0 as i32), dashboard)
591 .await
592 .map_err(|e| Error::Config(format!("Service error: {}", e)))
593 }
594
595 pub async fn delete_dashboard(&self, id: MetabaseId) -> Result<()> {
597 use crate::core::models::common::DashboardId;
598
599 if !self.is_authenticated() {
600 return Err(Error::Authentication(
601 "Authentication required to delete dashboard".to_string(),
602 ));
603 }
604
605 #[cfg(feature = "cache")]
606 {
607 let cache_key = cache_key("dashboard", id.0);
608 self.cache.invalidate(&cache_key);
609 }
610
611 self.service_manager
613 .dashboard_service()
614 .ok_or_else(|| Error::Config("Dashboard service not available".to_string()))?
615 .delete_dashboard(DashboardId(id.0 as i32))
616 .await
617 .map_err(|e| Error::Config(format!("Service error: {}", e)))
618 }
619
620 pub async fn execute_query(&self, query: DatasetQuery) -> Result<QueryResult> {
624 if !self.is_authenticated() {
625 return Err(Error::Authentication(
626 "Authentication required to execute query".to_string(),
627 ));
628 }
629
630 self.service_manager
632 .query_service()
633 .ok_or_else(|| Error::Config("Query service not available".to_string()))?
634 .execute_dataset_query(query)
635 .await
636 .map_err(|e| Error::Config(format!("Service error: {}", e)))
637 }
638
639 pub async fn execute_native_query(
641 &self,
642 database: MetabaseId,
643 native_query: NativeQuery,
644 ) -> Result<QueryResult> {
645 if !self.is_authenticated() {
646 return Err(Error::Authentication(
647 "Authentication required to execute native query".to_string(),
648 ));
649 }
650
651 self.service_manager
653 .query_service()
654 .ok_or_else(|| Error::Config("Query service not available".to_string()))?
655 .execute_native_query(database.0 as i32, native_query)
656 .await
657 .map_err(|e| Error::Config(format!("Service error: {}", e)))
658 }
659
660 pub async fn execute_card_query(
664 &self,
665 card_id: i64,
666 parameters: Option<Value>,
667 ) -> Result<QueryResult> {
668 if !self.is_authenticated() {
669 return Err(Error::Authentication(
670 "Authentication required to execute card query".to_string(),
671 ));
672 }
673
674 self.service_manager
676 .card_service()
677 .ok_or_else(|| Error::Config("Card service not available".to_string()))?
678 .execute_card_query(CardId(card_id as i32), parameters)
679 .await
680 .map_err(|e| Error::Config(format!("Service error: {}", e)))
681 }
682
683 pub async fn export_card_query(
685 &self,
686 card_id: i64,
687 format: ExportFormat,
688 parameters: Option<Value>,
689 ) -> Result<Vec<u8>> {
690 if !self.is_authenticated() {
691 return Err(Error::Authentication(
692 "Authentication required to export card query".to_string(),
693 ));
694 }
695
696 self.service_manager
698 .card_service()
699 .ok_or_else(|| Error::Config("Card service not available".to_string()))?
700 .export_card_query(CardId(card_id as i32), format, parameters)
701 .await
702 .map_err(|e| Error::Config(format!("Service error: {}", e)))
703 }
704
705 pub async fn execute_card_pivot_query(
707 &self,
708 card_id: i64,
709 parameters: Option<Value>,
710 ) -> Result<QueryResult> {
711 if !self.is_authenticated() {
712 return Err(Error::Authentication(
713 "Authentication required to execute pivot query".to_string(),
714 ));
715 }
716
717 self.service_manager
719 .card_service()
720 .ok_or_else(|| Error::Config("Card service not available".to_string()))?
721 .execute_card_pivot_query(CardId(card_id as i32), parameters)
722 .await
723 .map_err(|e| Error::Config(format!("Service error: {}", e)))
724 }
725
726 pub async fn get_database_metadata(&self, database_id: MetabaseId) -> Result<DatabaseMetadata> {
730 #[cfg(feature = "cache")]
731 {
732 let cache_key = cache_key("database_metadata", database_id.0);
733 if let Some(metadata) = self.cache.get_metadata::<DatabaseMetadata>(&cache_key) {
734 return Ok(metadata);
735 }
736 }
737
738 let metadata = self
740 .service_manager
741 .database_service()
742 .ok_or_else(|| Error::Config("Database service not available".to_string()))?
743 .get_database_metadata(database_id)
744 .await
745 .map_err(|e| Error::Config(format!("Service error: {}", e)))?;
746
747 #[cfg(feature = "cache")]
748 {
749 let cache_key = cache_key("database_metadata", database_id.0);
750 let _ = self.cache.set_metadata(cache_key, &metadata);
751 }
752
753 Ok(metadata)
754 }
755
756 pub async fn sync_database_schema(&self, database_id: MetabaseId) -> Result<SyncResult> {
758 if !self.is_authenticated() {
759 return Err(Error::Authentication(
760 "Authentication required to sync database schema".to_string(),
761 ));
762 }
763
764 #[cfg(feature = "cache")]
765 {
766 let cache_key = cache_key("database_metadata", database_id.0);
767 self.cache.invalidate(&cache_key);
768 }
769
770 self.service_manager
772 .database_service()
773 .ok_or_else(|| Error::Config("Database service not available".to_string()))?
774 .sync_database_schema(database_id)
775 .await
776 .map_err(|e| Error::Config(format!("Service error: {}", e)))
777 }
778
779 pub async fn get_database_fields(&self, database_id: MetabaseId) -> Result<Vec<Field>> {
781 self.service_manager
783 .database_service()
784 .ok_or_else(|| Error::Config("Database service not available".to_string()))?
785 .get_database_fields(database_id)
786 .await
787 .map_err(|e| Error::Config(format!("Service error: {}", e)))
788 }
789
790 pub async fn get_database_schemas(&self, database_id: MetabaseId) -> Result<Vec<String>> {
792 self.service_manager
794 .database_service()
795 .ok_or_else(|| Error::Config("Database service not available".to_string()))?
796 .get_database_schemas(database_id)
797 .await
798 .map_err(|e| Error::Config(format!("Service error: {}", e)))
799 }
800
801 pub async fn execute_dataset_query(&self, query: Value) -> Result<QueryResult> {
805 if !self.is_authenticated() {
806 return Err(Error::Authentication(
807 "Authentication required to execute dataset query".to_string(),
808 ));
809 }
810
811 self.service_manager
813 .query_service()
814 .ok_or_else(|| Error::Config("Query service not available".to_string()))?
815 .execute_raw_query(query)
816 .await
817 .map_err(|e| Error::Config(format!("Service error: {}", e)))
818 }
819
820 pub async fn execute_dataset_native(&self, query: Value) -> Result<QueryResult> {
822 if !self.is_authenticated() {
823 return Err(Error::Authentication(
824 "Authentication required to execute native dataset query".to_string(),
825 ));
826 }
827
828 self.service_manager
830 .query_service()
831 .ok_or_else(|| Error::Config("Query service not available".to_string()))?
832 .execute_raw_query(query)
833 .await
834 .map_err(|e| Error::Config(format!("Service error: {}", e)))
835 }
836
837 pub async fn execute_dataset_pivot(&self, query: Value) -> Result<QueryResult> {
839 if !self.is_authenticated() {
840 return Err(Error::Authentication(
841 "Authentication required to execute pivot dataset query".to_string(),
842 ));
843 }
844
845 self.service_manager
847 .query_service()
848 .ok_or_else(|| Error::Config("Query service not available".to_string()))?
849 .execute_pivot_query(query)
850 .await
851 .map_err(|e| Error::Config(format!("Service error: {}", e)))
852 }
853
854 pub async fn export_dataset(&self, format: ExportFormat, query: Value) -> Result<Vec<u8>> {
856 if !self.is_authenticated() {
857 return Err(Error::Authentication(
858 "Authentication required to export dataset".to_string(),
859 ));
860 }
861
862 self.service_manager
864 .query_service()
865 .ok_or_else(|| Error::Config("Query service not available".to_string()))?
866 .export_query(format.as_str(), query)
867 .await
868 .map_err(|e| Error::Config(format!("Service error: {}", e)))
869 }
870
871 #[cfg(feature = "query-builder")]
875 pub async fn execute_mbql_query(
876 &self,
877 database_id: MetabaseId,
878 query: MbqlQuery,
879 ) -> Result<QueryResult> {
880 if !self.is_authenticated() {
881 return Err(Error::Authentication(
882 "Authentication required to execute MBQL query".to_string(),
883 ));
884 }
885
886 let dataset_query = query.to_dataset_query(database_id);
887
888 self.service_manager
890 .query_service()
891 .ok_or_else(|| Error::Config("Query service not available".to_string()))?
892 .execute_dataset_query(dataset_query)
893 .await
894 .map_err(|e| Error::Config(format!("Service error: {}", e)))
895 }
896
897 #[cfg(feature = "query-builder")]
899 pub async fn export_mbql_query(
900 &self,
901 database_id: MetabaseId,
902 query: MbqlQuery,
903 format: ExportFormat,
904 ) -> Result<Vec<u8>> {
905 if !self.is_authenticated() {
906 return Err(Error::Authentication(
907 "Authentication required to export MBQL query".to_string(),
908 ));
909 }
910
911 let dataset_query = query.to_dataset_query(database_id);
912
913 let query_value = serde_json::to_value(&dataset_query)
916 .map_err(|e| Error::Serialization(e.to_string()))?;
917
918 self.service_manager
919 .query_service()
920 .ok_or_else(|| Error::Config("Query service not available".to_string()))?
921 .export_query(format.as_str(), query_value)
922 .await
923 .map_err(|e| Error::Config(format!("Service error: {}", e)))
924 }
925
926 pub async fn execute_sql(&self, database_id: MetabaseId, sql: &str) -> Result<QueryResult> {
930 let native_query = NativeQuery::new(sql);
931 self.execute_native_query(database_id, native_query).await
932 }
933
934 pub async fn execute_sql_with_params(
936 &self,
937 database_id: MetabaseId,
938 sql: &str,
939 params: HashMap<String, Value>,
940 ) -> Result<QueryResult> {
941 let mut native_query = NativeQuery::new(sql);
942 for (name, value) in params {
943 native_query = native_query.with_param(&name, value);
944 }
945 self.execute_native_query(database_id, native_query).await
946 }
947
948 pub async fn export_sql_query(
950 &self,
951 database_id: MetabaseId,
952 sql: &str,
953 format: ExportFormat,
954 ) -> Result<Vec<u8>> {
955 if !self.is_authenticated() {
956 return Err(Error::Authentication(
957 "Authentication required to export SQL query".to_string(),
958 ));
959 }
960
961 let native_query = NativeQuery::new(sql);
962 let request = json!({
963 "database": database_id.0,
964 "type": "native",
965 "native": {
966 "query": native_query.query,
967 "template-tags": native_query.template_tags
968 }
969 });
970
971 self.service_manager
973 .query_service()
974 .ok_or_else(|| Error::Config("Query service not available".to_string()))?
975 .export_query(format.as_str(), request)
976 .await
977 .map_err(|e| Error::Config(format!("Service error: {}", e)))
978 }
979}