metabase_api_rs/repository/
database.rs

1//! Database repository for data access
2//!
3//! This module provides the repository layer for database operations,
4//! handling all data access and HTTP communication for database-related functionality.
5
6use crate::core::models::database::DatabaseMetadata;
7use crate::core::models::field::Field;
8use crate::core::models::{MetabaseId, SyncResult};
9use crate::repository::traits::{
10    FilterParams, PaginationParams, Repository, RepositoryError, RepositoryResult,
11};
12use crate::transport::http_provider_safe::{HttpProviderExt, HttpProviderSafe};
13use async_trait::async_trait;
14use serde_json::json;
15use std::sync::Arc;
16
17/// Database-specific filter parameters
18#[derive(Debug, Clone, Default)]
19pub struct DatabaseFilterParams {
20    pub engine: Option<String>,
21    pub native_permissions: Option<String>,
22}
23
24impl DatabaseFilterParams {
25    /// Convert to query parameters
26    pub fn to_query_params(&self) -> Vec<(String, String)> {
27        let mut params = vec![];
28        if let Some(engine) = &self.engine {
29            params.push(("engine".to_string(), engine.clone()));
30        }
31        if let Some(permissions) = &self.native_permissions {
32            params.push(("native_permissions".to_string(), permissions.clone()));
33        }
34        params
35    }
36}
37
38/// Database repository trait for database-specific operations
39#[async_trait]
40pub trait DatabaseRepository:
41    Repository<Entity = DatabaseMetadata, Id = MetabaseId> + Send + Sync
42{
43    /// Sync database schema
44    async fn sync_database_schema(&self, id: &MetabaseId) -> RepositoryResult<SyncResult>;
45
46    /// Get database fields
47    async fn get_database_fields(&self, id: &MetabaseId) -> RepositoryResult<Vec<Field>>;
48
49    /// Get database schemas
50    async fn get_database_schemas(&self, id: &MetabaseId) -> RepositoryResult<Vec<String>>;
51
52    /// List databases with specific filters
53    async fn list_with_filters(
54        &self,
55        pagination: Option<PaginationParams>,
56        filters: Option<DatabaseFilterParams>,
57    ) -> RepositoryResult<Vec<DatabaseMetadata>>;
58}
59
60/// HTTP implementation of DatabaseRepository
61pub struct HttpDatabaseRepository {
62    http_provider: Arc<dyn HttpProviderSafe>,
63}
64
65impl HttpDatabaseRepository {
66    /// Creates a new HttpDatabaseRepository
67    pub fn new(http_provider: Arc<dyn HttpProviderSafe>) -> Self {
68        Self { http_provider }
69    }
70}
71
72#[async_trait]
73impl Repository for HttpDatabaseRepository {
74    type Entity = DatabaseMetadata;
75    type Id = MetabaseId;
76
77    async fn get(&self, id: &Self::Id) -> RepositoryResult<Self::Entity> {
78        let path = format!("/api/database/{}/metadata", id.0);
79        self.http_provider
80            .get(&path)
81            .await
82            .map_err(|e| RepositoryError::Network(e.to_string()))
83    }
84
85    async fn list(
86        &self,
87        pagination: Option<PaginationParams>,
88        _filters: Option<FilterParams>,
89    ) -> RepositoryResult<Vec<Self::Entity>> {
90        // For database listing, we ignore generic FilterParams and use list_with_filters
91        let mut path = "/api/database".to_string();
92        let mut query_params = vec![];
93
94        if let Some(pagination) = pagination {
95            query_params.extend(pagination.to_query_params());
96        }
97
98        if !query_params.is_empty() {
99            let query_string = query_params
100                .iter()
101                .map(|(k, v)| format!("{}={}", k, v))
102                .collect::<Vec<_>>()
103                .join("&");
104            path = format!("{}?{}", path, query_string);
105        }
106
107        self.http_provider
108            .get(&path)
109            .await
110            .map_err(|e| RepositoryError::Network(e.to_string()))
111    }
112
113    async fn create(&self, _entity: &Self::Entity) -> RepositoryResult<Self::Entity> {
114        // Database creation is typically done through admin interface
115        Err(RepositoryError::Other(
116            "Database creation not supported through API".to_string(),
117        ))
118    }
119
120    async fn update(
121        &self,
122        _id: &Self::Id,
123        _entity: &Self::Entity,
124    ) -> RepositoryResult<Self::Entity> {
125        // Database updates are typically done through admin interface
126        Err(RepositoryError::Other(
127            "Database updates not supported through API".to_string(),
128        ))
129    }
130
131    async fn delete(&self, _id: &Self::Id) -> RepositoryResult<()> {
132        // Database deletion is typically done through admin interface
133        Err(RepositoryError::Other(
134            "Database deletion not supported through API".to_string(),
135        ))
136    }
137}
138
139#[async_trait]
140impl DatabaseRepository for HttpDatabaseRepository {
141    async fn sync_database_schema(&self, id: &MetabaseId) -> RepositoryResult<SyncResult> {
142        let path = format!("/api/database/{}/sync_schema", id.0);
143        self.http_provider
144            .post(&path, &json!({}))
145            .await
146            .map_err(|e| RepositoryError::Network(e.to_string()))
147    }
148
149    async fn get_database_fields(&self, id: &MetabaseId) -> RepositoryResult<Vec<Field>> {
150        let path = format!("/api/database/{}/fields", id.0);
151        self.http_provider
152            .get(&path)
153            .await
154            .map_err(|e| RepositoryError::Network(e.to_string()))
155    }
156
157    async fn get_database_schemas(&self, id: &MetabaseId) -> RepositoryResult<Vec<String>> {
158        let path = format!("/api/database/{}/schemas", id.0);
159        self.http_provider
160            .get(&path)
161            .await
162            .map_err(|e| RepositoryError::Network(e.to_string()))
163    }
164
165    async fn list_with_filters(
166        &self,
167        pagination: Option<PaginationParams>,
168        filters: Option<DatabaseFilterParams>,
169    ) -> RepositoryResult<Vec<DatabaseMetadata>> {
170        let mut path = "/api/database".to_string();
171        let mut query_params = vec![];
172
173        if let Some(pagination) = pagination {
174            query_params.extend(pagination.to_query_params());
175        }
176
177        if let Some(filters) = filters {
178            query_params.extend(filters.to_query_params());
179        }
180
181        if !query_params.is_empty() {
182            let query_string = query_params
183                .iter()
184                .map(|(k, v)| format!("{}={}", k, v))
185                .collect::<Vec<_>>()
186                .join("&");
187            path = format!("{}?{}", path, query_string);
188        }
189
190        self.http_provider
191            .get(&path)
192            .await
193            .map_err(|e| RepositoryError::Network(e.to_string()))
194    }
195}