data_modelling_sdk/models/relationship.rs
1//! Relationship model for the SDK
2
3use super::enums::{Cardinality, RelationshipType};
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6use uuid::Uuid;
7
8/// Foreign key column mapping details
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
10pub struct ForeignKeyDetails {
11 /// Column name in the source table
12 pub source_column: String,
13 /// Column name in the target table
14 pub target_column: String,
15}
16
17/// ETL job metadata for data flow relationships
18#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
19pub struct ETLJobMetadata {
20 /// Name of the ETL job that creates this relationship
21 pub job_name: String,
22 /// Optional notes about the ETL job
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub notes: Option<String>,
25 /// Job execution frequency (e.g., "daily", "hourly")
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub frequency: Option<String>,
28}
29
30/// Connection point coordinates for relationship visualization
31#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
32pub struct ConnectionPoint {
33 /// X coordinate
34 pub x: f64,
35 /// Y coordinate
36 pub y: f64,
37}
38
39/// Visual metadata for relationship rendering on canvas
40#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
41pub struct VisualMetadata {
42 /// Connection point identifier on source table
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub source_connection_point: Option<String>,
45 /// Connection point identifier on target table
46 #[serde(skip_serializing_if = "Option::is_none")]
47 pub target_connection_point: Option<String>,
48 /// Waypoints for routing the relationship line
49 #[serde(default)]
50 pub routing_waypoints: Vec<ConnectionPoint>,
51 /// Position for the relationship label
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub label_position: Option<ConnectionPoint>,
54}
55
56/// Relationship model representing a connection between two tables
57///
58/// Relationships can represent foreign keys, data flows, dependencies, or ETL transformations.
59/// They connect a source table to a target table with optional metadata about cardinality,
60/// foreign key details, and ETL job information.
61///
62/// # Example
63///
64/// ```rust
65/// use data_modelling_sdk::models::Relationship;
66///
67/// let source_id = uuid::Uuid::new_v4();
68/// let target_id = uuid::Uuid::new_v4();
69/// let relationship = Relationship::new(source_id, target_id);
70/// ```
71#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
72pub struct Relationship {
73 /// Unique identifier for the relationship (UUIDv4)
74 pub id: Uuid,
75 /// ID of the source table
76 pub source_table_id: Uuid,
77 /// ID of the target table
78 pub target_table_id: Uuid,
79 /// Cardinality (OneToOne, OneToMany, ManyToMany)
80 #[serde(skip_serializing_if = "Option::is_none")]
81 pub cardinality: Option<Cardinality>,
82 /// Whether the source side is optional (nullable foreign key)
83 #[serde(skip_serializing_if = "Option::is_none")]
84 pub source_optional: Option<bool>,
85 /// Whether the target side is optional
86 #[serde(skip_serializing_if = "Option::is_none")]
87 pub target_optional: Option<bool>,
88 /// Foreign key column mapping details
89 #[serde(skip_serializing_if = "Option::is_none")]
90 pub foreign_key_details: Option<ForeignKeyDetails>,
91 /// ETL job metadata for data flow relationships
92 #[serde(skip_serializing_if = "Option::is_none")]
93 pub etl_job_metadata: Option<ETLJobMetadata>,
94 /// Type of relationship (ForeignKey, DataFlow, Dependency, ETL)
95 #[serde(skip_serializing_if = "Option::is_none")]
96 pub relationship_type: Option<RelationshipType>,
97 /// Optional notes about the relationship
98 #[serde(skip_serializing_if = "Option::is_none")]
99 pub notes: Option<String>,
100 /// Visual metadata for canvas rendering
101 #[serde(skip_serializing_if = "Option::is_none")]
102 pub visual_metadata: Option<VisualMetadata>,
103 /// Draw.io edge ID for diagram integration
104 #[serde(skip_serializing_if = "Option::is_none")]
105 pub drawio_edge_id: Option<String>,
106 /// Creation timestamp
107 pub created_at: DateTime<Utc>,
108 /// Last update timestamp
109 pub updated_at: DateTime<Utc>,
110}
111
112impl Relationship {
113 /// Create a new relationship between two tables
114 ///
115 /// # Arguments
116 ///
117 /// * `source_table_id` - UUID of the source table
118 /// * `target_table_id` - UUID of the target table
119 ///
120 /// # Returns
121 ///
122 /// A new `Relationship` instance with a generated UUIDv4 ID and current timestamps.
123 ///
124 /// # Example
125 ///
126 /// ```rust
127 /// use data_modelling_sdk::models::Relationship;
128 ///
129 /// let source_id = uuid::Uuid::new_v4();
130 /// let target_id = uuid::Uuid::new_v4();
131 /// let rel = Relationship::new(source_id, target_id);
132 /// ```
133 pub fn new(source_table_id: Uuid, target_table_id: Uuid) -> Self {
134 let now = Utc::now();
135 let id = Self::generate_id(source_table_id, target_table_id);
136 Self {
137 id,
138 source_table_id,
139 target_table_id,
140 cardinality: None,
141 source_optional: None,
142 target_optional: None,
143 foreign_key_details: None,
144 etl_job_metadata: None,
145 relationship_type: None,
146 notes: None,
147 visual_metadata: None,
148 drawio_edge_id: None,
149 created_at: now,
150 updated_at: now,
151 }
152 }
153
154 /// Generate a UUIDv4 for a new relationship id.
155 ///
156 /// Note: params are retained for backward-compatibility with previous deterministic-v5 API.
157 pub fn generate_id(_source_table_id: Uuid, _target_table_id: Uuid) -> Uuid {
158 Uuid::new_v4()
159 }
160}