use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CrossDomainTableRef {
pub id: Uuid,
pub source_domain: String,
pub table_id: Uuid,
#[serde(skip_serializing_if = "Option::is_none")]
pub display_alias: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<super::Position>,
#[serde(skip_serializing_if = "Option::is_none")]
pub notes: Option<String>,
#[serde(default = "chrono::Utc::now")]
pub created_at: chrono::DateTime<chrono::Utc>,
}
impl CrossDomainTableRef {
pub fn new(source_domain: String, table_id: Uuid) -> Self {
Self {
id: Uuid::new_v4(),
source_domain,
table_id,
display_alias: None,
position: None,
notes: None,
created_at: chrono::Utc::now(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CrossDomainRelationshipRef {
pub id: Uuid,
pub source_domain: String,
pub relationship_id: Uuid,
pub source_table_id: Uuid,
pub target_table_id: Uuid,
#[serde(default = "chrono::Utc::now")]
pub created_at: chrono::DateTime<chrono::Utc>,
}
impl CrossDomainRelationshipRef {
pub fn new(
source_domain: String,
relationship_id: Uuid,
source_table_id: Uuid,
target_table_id: Uuid,
) -> Self {
Self {
id: Uuid::new_v4(),
source_domain,
relationship_id,
source_table_id,
target_table_id,
created_at: chrono::Utc::now(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct CrossDomainConfig {
#[serde(default = "default_schema_version")]
pub schema_version: String,
#[serde(default)]
pub imported_tables: Vec<CrossDomainTableRef>,
#[serde(default)]
pub imported_relationships: Vec<CrossDomainRelationshipRef>,
}
fn default_schema_version() -> String {
"1.0".to_string()
}
impl CrossDomainConfig {
pub fn new() -> Self {
Self::default()
}
pub fn add_table_ref(&mut self, source_domain: String, table_id: Uuid) -> usize {
if let Some(idx) = self
.imported_tables
.iter()
.position(|t| t.source_domain == source_domain && t.table_id == table_id)
{
return idx;
}
let ref_entry = CrossDomainTableRef::new(source_domain, table_id);
self.imported_tables.push(ref_entry);
self.imported_tables.len() - 1
}
pub fn get_table_ref(&self, idx: usize) -> Option<&CrossDomainTableRef> {
self.imported_tables.get(idx)
}
pub fn remove_table_ref(&mut self, table_id: Uuid) -> bool {
let initial_len = self.imported_tables.len();
self.imported_tables.retain(|t| t.table_id != table_id);
self.imported_relationships
.retain(|r| r.source_table_id != table_id && r.target_table_id != table_id);
self.imported_tables.len() != initial_len
}
pub fn add_relationship_ref(
&mut self,
source_domain: String,
relationship_id: Uuid,
source_table_id: Uuid,
target_table_id: Uuid,
) -> usize {
if let Some(idx) = self
.imported_relationships
.iter()
.position(|r| r.source_domain == source_domain && r.relationship_id == relationship_id)
{
return idx;
}
let ref_entry = CrossDomainRelationshipRef::new(
source_domain,
relationship_id,
source_table_id,
target_table_id,
);
self.imported_relationships.push(ref_entry);
self.imported_relationships.len() - 1
}
pub fn get_relationship_ref(&self, idx: usize) -> Option<&CrossDomainRelationshipRef> {
self.imported_relationships.get(idx)
}
pub fn remove_relationship_ref(&mut self, relationship_id: Uuid) -> bool {
let initial_len = self.imported_relationships.len();
self.imported_relationships
.retain(|r| r.relationship_id != relationship_id);
self.imported_relationships.len() != initial_len
}
pub fn get_tables_from_domain(&self, domain: &str) -> Vec<Uuid> {
self.imported_tables
.iter()
.filter(|t| t.source_domain == domain)
.map(|t| t.table_id)
.collect()
}
pub fn is_table_imported(&self, table_id: Uuid) -> bool {
self.imported_tables.iter().any(|t| t.table_id == table_id)
}
pub fn get_table_source_domain(&self, table_id: Uuid) -> Option<&str> {
self.imported_tables
.iter()
.find(|t| t.table_id == table_id)
.map(|t| t.source_domain.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add_table_ref() {
let mut config = CrossDomainConfig::new();
let table_id = Uuid::new_v4();
config.add_table_ref("finance".to_string(), table_id);
assert_eq!(config.imported_tables.len(), 1);
assert_eq!(config.imported_tables[0].source_domain, "finance");
assert_eq!(config.imported_tables[0].table_id, table_id);
}
#[test]
fn test_duplicate_table_ref() {
let mut config = CrossDomainConfig::new();
let table_id = Uuid::new_v4();
config.add_table_ref("finance".to_string(), table_id);
config.add_table_ref("finance".to_string(), table_id);
assert_eq!(config.imported_tables.len(), 1);
}
#[test]
fn test_remove_table_ref_removes_relationships() {
let mut config = CrossDomainConfig::new();
let table_a = Uuid::new_v4();
let table_b = Uuid::new_v4();
let rel_id = Uuid::new_v4();
config.add_table_ref("finance".to_string(), table_a);
config.add_table_ref("finance".to_string(), table_b);
config.add_relationship_ref("finance".to_string(), rel_id, table_a, table_b);
assert_eq!(config.imported_relationships.len(), 1);
config.remove_table_ref(table_a);
assert_eq!(config.imported_relationships.len(), 0);
}
}