data_modelling_sdk/model/
api_loader.rs

1//! API-based model loading
2//!
3//! Specialized loader for API storage backend that loads models via HTTP endpoints.
4
5#[cfg(feature = "api-backend")]
6use crate::storage::StorageError;
7#[cfg(feature = "api-backend")]
8use crate::storage::api::ApiStorageBackend;
9#[cfg(feature = "api-backend")]
10use std::collections::HashMap;
11#[cfg(feature = "api-backend")]
12use tracing::info;
13#[cfg(feature = "api-backend")]
14use uuid::Uuid;
15
16#[cfg(feature = "api-backend")]
17use super::loader::{ModelLoadResult, RelationshipData, TableData};
18
19/// API-based model loader
20#[cfg(feature = "api-backend")]
21pub struct ApiModelLoader {
22    backend: ApiStorageBackend,
23}
24
25#[cfg(feature = "api-backend")]
26impl ApiModelLoader {
27    /// Create a new API model loader
28    pub fn new(backend: ApiStorageBackend) -> Self {
29        Self { backend }
30    }
31
32    /// Load model from API endpoints (domain-scoped).
33    ///
34    /// Loads tables and relationships via HTTP API:
35    /// - GET /workspace/domains/{domain}/tables
36    /// - GET /workspace/domains/{domain}/relationships
37    pub async fn load_model(&self, domain: &str) -> Result<ModelLoadResult, StorageError> {
38        // Load tables and relationships via API
39        let tables_json = self.backend.load_tables(domain).await?;
40        let relationships_json = self.backend.load_relationships(domain).await?;
41
42        // Convert JSON to TableData and RelationshipData
43        let mut tables = Vec::new();
44        let mut table_ids: HashMap<Uuid, String> = HashMap::new();
45
46        for table_json in tables_json.iter() {
47            // Extract basic fields from JSON
48            let id_str = table_json
49                .get("id")
50                .and_then(|v| v.as_str())
51                .ok_or_else(|| StorageError::SerializationError("Missing table id".to_string()))?;
52            let id = Uuid::parse_str(id_str).map_err(|e| {
53                StorageError::SerializationError(format!("Invalid table id: {}", e))
54            })?;
55
56            let name = table_json
57                .get("name")
58                .and_then(|v| v.as_str())
59                .map(|s| s.to_string())
60                .ok_or_else(|| {
61                    StorageError::SerializationError("Missing table name".to_string())
62                })?;
63
64            // Serialize back to YAML for consistency with file-based storage
65            let yaml_content = serde_yaml::to_string(table_json).map_err(|e| {
66                StorageError::SerializationError(format!("Failed to serialize table: {}", e))
67            })?;
68
69            table_ids.insert(id, name.clone());
70            tables.push(TableData {
71                id,
72                name,
73                yaml_file_path: None, // API doesn't have file paths
74                yaml_content,
75            });
76        }
77
78        info!("Loaded {} tables from API", tables.len());
79
80        // Convert relationships
81        let mut relationships = Vec::new();
82        let mut orphaned_relationships = Vec::new();
83
84        for rel_json in relationships_json {
85            let id_str = rel_json.get("id").and_then(|v| v.as_str()).ok_or_else(|| {
86                StorageError::SerializationError("Missing relationship id".to_string())
87            })?;
88            let id = Uuid::parse_str(id_str).map_err(|e| {
89                StorageError::SerializationError(format!("Invalid relationship id: {}", e))
90            })?;
91
92            let source_id_str = rel_json
93                .get("source_table_id")
94                .and_then(|v| v.as_str())
95                .ok_or_else(|| {
96                    StorageError::SerializationError("Missing source_table_id".to_string())
97                })?;
98            let source_table_id = Uuid::parse_str(source_id_str).map_err(|e| {
99                StorageError::SerializationError(format!("Invalid source_table_id: {}", e))
100            })?;
101
102            let target_id_str = rel_json
103                .get("target_table_id")
104                .and_then(|v| v.as_str())
105                .ok_or_else(|| {
106                    StorageError::SerializationError("Missing target_table_id".to_string())
107                })?;
108            let target_table_id = Uuid::parse_str(target_id_str).map_err(|e| {
109                StorageError::SerializationError(format!("Invalid target_table_id: {}", e))
110            })?;
111
112            let source_exists = table_ids.contains_key(&source_table_id);
113            let target_exists = table_ids.contains_key(&target_table_id);
114
115            if source_exists && target_exists {
116                relationships.push(RelationshipData {
117                    id,
118                    source_table_id,
119                    target_table_id,
120                });
121            } else {
122                orphaned_relationships.push(RelationshipData {
123                    id,
124                    source_table_id,
125                    target_table_id,
126                });
127            }
128        }
129
130        info!(
131            "Loaded {} relationships ({} orphaned) from API",
132            relationships.len(),
133            orphaned_relationships.len()
134        );
135
136        Ok(ModelLoadResult {
137            tables,
138            relationships,
139            orphaned_relationships,
140        })
141    }
142}