data_modelling_core/models/
cross_domain.rs1use serde::{Deserialize, Serialize};
7use uuid::Uuid;
8
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
15pub struct CrossDomainTableRef {
16 pub id: Uuid,
18
19 pub source_domain: String,
21
22 pub table_id: Uuid,
24
25 #[serde(skip_serializing_if = "Option::is_none")]
27 pub display_alias: Option<String>,
28
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub position: Option<super::Position>,
32
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub notes: Option<String>,
36
37 #[serde(default = "chrono::Utc::now")]
39 pub created_at: chrono::DateTime<chrono::Utc>,
40}
41
42impl CrossDomainTableRef {
43 pub fn new(source_domain: String, table_id: Uuid) -> Self {
45 Self {
46 id: Uuid::new_v4(),
47 source_domain,
48 table_id,
49 display_alias: None,
50 position: None,
51 notes: None,
52 created_at: chrono::Utc::now(),
53 }
54 }
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
62pub struct CrossDomainRelationshipRef {
63 pub id: Uuid,
65
66 pub source_domain: String,
68
69 pub relationship_id: Uuid,
71
72 pub source_table_id: Uuid,
74
75 pub target_table_id: Uuid,
77
78 #[serde(default = "chrono::Utc::now")]
80 pub created_at: chrono::DateTime<chrono::Utc>,
81}
82
83impl CrossDomainRelationshipRef {
84 pub fn new(
86 source_domain: String,
87 relationship_id: Uuid,
88 source_table_id: Uuid,
89 target_table_id: Uuid,
90 ) -> Self {
91 Self {
92 id: Uuid::new_v4(),
93 source_domain,
94 relationship_id,
95 source_table_id,
96 target_table_id,
97 created_at: chrono::Utc::now(),
98 }
99 }
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
108pub struct CrossDomainConfig {
109 #[serde(default = "default_schema_version")]
111 pub schema_version: String,
112
113 #[serde(default)]
115 pub imported_tables: Vec<CrossDomainTableRef>,
116
117 #[serde(default)]
121 pub imported_relationships: Vec<CrossDomainRelationshipRef>,
122}
123
124fn default_schema_version() -> String {
125 "1.0".to_string()
126}
127
128impl CrossDomainConfig {
129 pub fn new() -> Self {
131 Self::default()
132 }
133
134 pub fn add_table_ref(&mut self, source_domain: String, table_id: Uuid) -> usize {
137 if let Some(idx) = self
139 .imported_tables
140 .iter()
141 .position(|t| t.source_domain == source_domain && t.table_id == table_id)
142 {
143 return idx;
144 }
145
146 let ref_entry = CrossDomainTableRef::new(source_domain, table_id);
147 self.imported_tables.push(ref_entry);
148 self.imported_tables.len() - 1
149 }
150
151 pub fn get_table_ref(&self, idx: usize) -> Option<&CrossDomainTableRef> {
153 self.imported_tables.get(idx)
154 }
155
156 pub fn remove_table_ref(&mut self, table_id: Uuid) -> bool {
158 let initial_len = self.imported_tables.len();
159 self.imported_tables.retain(|t| t.table_id != table_id);
160
161 self.imported_relationships
163 .retain(|r| r.source_table_id != table_id && r.target_table_id != table_id);
164
165 self.imported_tables.len() != initial_len
166 }
167
168 pub fn add_relationship_ref(
171 &mut self,
172 source_domain: String,
173 relationship_id: Uuid,
174 source_table_id: Uuid,
175 target_table_id: Uuid,
176 ) -> usize {
177 if let Some(idx) = self
179 .imported_relationships
180 .iter()
181 .position(|r| r.source_domain == source_domain && r.relationship_id == relationship_id)
182 {
183 return idx;
184 }
185
186 let ref_entry = CrossDomainRelationshipRef::new(
187 source_domain,
188 relationship_id,
189 source_table_id,
190 target_table_id,
191 );
192 self.imported_relationships.push(ref_entry);
193 self.imported_relationships.len() - 1
194 }
195
196 pub fn get_relationship_ref(&self, idx: usize) -> Option<&CrossDomainRelationshipRef> {
198 self.imported_relationships.get(idx)
199 }
200
201 pub fn remove_relationship_ref(&mut self, relationship_id: Uuid) -> bool {
203 let initial_len = self.imported_relationships.len();
204 self.imported_relationships
205 .retain(|r| r.relationship_id != relationship_id);
206 self.imported_relationships.len() != initial_len
207 }
208
209 pub fn get_tables_from_domain(&self, domain: &str) -> Vec<Uuid> {
211 self.imported_tables
212 .iter()
213 .filter(|t| t.source_domain == domain)
214 .map(|t| t.table_id)
215 .collect()
216 }
217
218 pub fn is_table_imported(&self, table_id: Uuid) -> bool {
220 self.imported_tables.iter().any(|t| t.table_id == table_id)
221 }
222
223 pub fn get_table_source_domain(&self, table_id: Uuid) -> Option<&str> {
225 self.imported_tables
226 .iter()
227 .find(|t| t.table_id == table_id)
228 .map(|t| t.source_domain.as_str())
229 }
230}
231
232#[cfg(test)]
233mod tests {
234 use super::*;
235
236 #[test]
237 fn test_add_table_ref() {
238 let mut config = CrossDomainConfig::new();
239 let table_id = Uuid::new_v4();
240
241 config.add_table_ref("finance".to_string(), table_id);
242
243 assert_eq!(config.imported_tables.len(), 1);
244 assert_eq!(config.imported_tables[0].source_domain, "finance");
245 assert_eq!(config.imported_tables[0].table_id, table_id);
246 }
247
248 #[test]
249 fn test_duplicate_table_ref() {
250 let mut config = CrossDomainConfig::new();
251 let table_id = Uuid::new_v4();
252
253 config.add_table_ref("finance".to_string(), table_id);
254 config.add_table_ref("finance".to_string(), table_id);
255
256 assert_eq!(config.imported_tables.len(), 1);
258 }
259
260 #[test]
261 fn test_remove_table_ref_removes_relationships() {
262 let mut config = CrossDomainConfig::new();
263 let table_a = Uuid::new_v4();
264 let table_b = Uuid::new_v4();
265 let rel_id = Uuid::new_v4();
266
267 config.add_table_ref("finance".to_string(), table_a);
268 config.add_table_ref("finance".to_string(), table_b);
269 config.add_relationship_ref("finance".to_string(), rel_id, table_a, table_b);
270
271 assert_eq!(config.imported_relationships.len(), 1);
272
273 config.remove_table_ref(table_a);
274
275 assert_eq!(config.imported_relationships.len(), 0);
277 }
278}