use crate::core::models::database::DatabaseMetadata;
use crate::core::models::field::Field;
use crate::core::models::{MetabaseId, SyncResult};
use crate::repository::traits::{
FilterParams, PaginationParams, Repository, RepositoryError, RepositoryResult,
};
use crate::transport::http_provider_safe::{HttpProviderExt, HttpProviderSafe};
use async_trait::async_trait;
use serde_json::json;
use std::sync::Arc;
#[derive(Debug, Clone, Default)]
pub struct DatabaseFilterParams {
pub engine: Option<String>,
pub native_permissions: Option<String>,
}
impl DatabaseFilterParams {
pub fn to_query_params(&self) -> Vec<(String, String)> {
let mut params = vec![];
if let Some(engine) = &self.engine {
params.push(("engine".to_string(), engine.clone()));
}
if let Some(permissions) = &self.native_permissions {
params.push(("native_permissions".to_string(), permissions.clone()));
}
params
}
}
#[async_trait]
pub trait DatabaseRepository:
Repository<Entity = DatabaseMetadata, Id = MetabaseId> + Send + Sync
{
async fn sync_database_schema(&self, id: &MetabaseId) -> RepositoryResult<SyncResult>;
async fn get_database_fields(&self, id: &MetabaseId) -> RepositoryResult<Vec<Field>>;
async fn get_database_schemas(&self, id: &MetabaseId) -> RepositoryResult<Vec<String>>;
async fn list_with_filters(
&self,
pagination: Option<PaginationParams>,
filters: Option<DatabaseFilterParams>,
) -> RepositoryResult<Vec<DatabaseMetadata>>;
}
pub struct HttpDatabaseRepository {
http_provider: Arc<dyn HttpProviderSafe>,
}
impl HttpDatabaseRepository {
pub fn new(http_provider: Arc<dyn HttpProviderSafe>) -> Self {
Self { http_provider }
}
}
#[async_trait]
impl Repository for HttpDatabaseRepository {
type Entity = DatabaseMetadata;
type Id = MetabaseId;
async fn get(&self, id: &Self::Id) -> RepositoryResult<Self::Entity> {
let path = format!("/api/database/{}/metadata", id.0);
self.http_provider
.get(&path)
.await
.map_err(|e| RepositoryError::Network(e.to_string()))
}
async fn list(
&self,
pagination: Option<PaginationParams>,
_filters: Option<FilterParams>,
) -> RepositoryResult<Vec<Self::Entity>> {
let mut path = "/api/database".to_string();
let mut query_params = vec![];
if let Some(pagination) = pagination {
query_params.extend(pagination.to_query_params());
}
if !query_params.is_empty() {
let query_string = query_params
.iter()
.map(|(k, v)| format!("{}={}", k, v))
.collect::<Vec<_>>()
.join("&");
path = format!("{}?{}", path, query_string);
}
self.http_provider
.get(&path)
.await
.map_err(|e| RepositoryError::Network(e.to_string()))
}
async fn create(&self, _entity: &Self::Entity) -> RepositoryResult<Self::Entity> {
Err(RepositoryError::Other(
"Database creation not supported through API".to_string(),
))
}
async fn update(
&self,
_id: &Self::Id,
_entity: &Self::Entity,
) -> RepositoryResult<Self::Entity> {
Err(RepositoryError::Other(
"Database updates not supported through API".to_string(),
))
}
async fn delete(&self, _id: &Self::Id) -> RepositoryResult<()> {
Err(RepositoryError::Other(
"Database deletion not supported through API".to_string(),
))
}
}
#[async_trait]
impl DatabaseRepository for HttpDatabaseRepository {
async fn sync_database_schema(&self, id: &MetabaseId) -> RepositoryResult<SyncResult> {
let path = format!("/api/database/{}/sync_schema", id.0);
self.http_provider
.post(&path, &json!({}))
.await
.map_err(|e| RepositoryError::Network(e.to_string()))
}
async fn get_database_fields(&self, id: &MetabaseId) -> RepositoryResult<Vec<Field>> {
let path = format!("/api/database/{}/fields", id.0);
self.http_provider
.get(&path)
.await
.map_err(|e| RepositoryError::Network(e.to_string()))
}
async fn get_database_schemas(&self, id: &MetabaseId) -> RepositoryResult<Vec<String>> {
let path = format!("/api/database/{}/schemas", id.0);
self.http_provider
.get(&path)
.await
.map_err(|e| RepositoryError::Network(e.to_string()))
}
async fn list_with_filters(
&self,
pagination: Option<PaginationParams>,
filters: Option<DatabaseFilterParams>,
) -> RepositoryResult<Vec<DatabaseMetadata>> {
let mut path = "/api/database".to_string();
let mut query_params = vec![];
if let Some(pagination) = pagination {
query_params.extend(pagination.to_query_params());
}
if let Some(filters) = filters {
query_params.extend(filters.to_query_params());
}
if !query_params.is_empty() {
let query_string = query_params
.iter()
.map(|(k, v)| format!("{}={}", k, v))
.collect::<Vec<_>>()
.join("&");
path = format!("{}?{}", path, query_string);
}
self.http_provider
.get(&path)
.await
.map_err(|e| RepositoryError::Network(e.to_string()))
}
}