Skip to main content

oxigdal_security/lineage/
mod.rs

1//! Data lineage tracking system.
2
3pub mod graph;
4pub mod metadata;
5pub mod query;
6
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use uuid::Uuid;
11
12/// Lineage node representing a data entity.
13#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
14pub struct LineageNode {
15    /// Node ID.
16    pub id: String,
17    /// Node type.
18    pub node_type: NodeType,
19    /// Entity URI or identifier.
20    pub entity_id: String,
21    /// Node metadata.
22    pub metadata: HashMap<String, String>,
23    /// Created timestamp.
24    pub created_at: DateTime<Utc>,
25}
26
27impl LineageNode {
28    /// Create a new lineage node.
29    pub fn new(node_type: NodeType, entity_id: String) -> Self {
30        Self {
31            id: Uuid::new_v4().to_string(),
32            node_type,
33            entity_id,
34            metadata: HashMap::new(),
35            created_at: Utc::now(),
36        }
37    }
38
39    /// Add metadata.
40    pub fn with_metadata(mut self, key: String, value: String) -> Self {
41        self.metadata.insert(key, value);
42        self
43    }
44}
45
46/// Node type in lineage graph.
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
48pub enum NodeType {
49    /// Dataset.
50    Dataset,
51    /// Processing operation.
52    Operation,
53    /// User/agent.
54    Agent,
55    /// Model.
56    Model,
57    /// Parameter set.
58    Parameters,
59}
60
61/// Lineage edge representing a relationship.
62#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
63pub struct LineageEdge {
64    /// Edge ID.
65    pub id: String,
66    /// Source node ID.
67    pub source_id: String,
68    /// Target node ID.
69    pub target_id: String,
70    /// Edge type.
71    pub edge_type: EdgeType,
72    /// Edge metadata.
73    pub metadata: HashMap<String, String>,
74    /// Created timestamp.
75    pub created_at: DateTime<Utc>,
76}
77
78impl LineageEdge {
79    /// Create a new lineage edge.
80    pub fn new(source_id: String, target_id: String, edge_type: EdgeType) -> Self {
81        Self {
82            id: Uuid::new_v4().to_string(),
83            source_id,
84            target_id,
85            edge_type,
86            metadata: HashMap::new(),
87            created_at: Utc::now(),
88        }
89    }
90
91    /// Add metadata.
92    pub fn with_metadata(mut self, key: String, value: String) -> Self {
93        self.metadata.insert(key, value);
94        self
95    }
96}
97
98/// Edge type in lineage graph.
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
100pub enum EdgeType {
101    /// Data derivation (wasDerivedFrom in PROV-O).
102    DerivedFrom,
103    /// Usage (used in PROV-O).
104    Used,
105    /// Generation (wasGeneratedBy in PROV-O).
106    GeneratedBy,
107    /// Attribution (wasAttributedTo in PROV-O).
108    AttributedTo,
109    /// Association (wasAssociatedWith in PROV-O).
110    AssociatedWith,
111}
112
113/// Lineage event for tracking operations.
114#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct LineageEvent {
116    /// Event ID.
117    pub id: String,
118    /// Event type.
119    pub event_type: String,
120    /// Timestamp.
121    pub timestamp: DateTime<Utc>,
122    /// Input nodes.
123    pub inputs: Vec<String>,
124    /// Output nodes.
125    pub outputs: Vec<String>,
126    /// Operation node.
127    pub operation: Option<String>,
128    /// Agent (user/service).
129    pub agent: Option<String>,
130    /// Event metadata.
131    pub metadata: HashMap<String, String>,
132}
133
134impl LineageEvent {
135    /// Create a new lineage event.
136    pub fn new(event_type: String) -> Self {
137        Self {
138            id: Uuid::new_v4().to_string(),
139            event_type,
140            timestamp: Utc::now(),
141            inputs: Vec::new(),
142            outputs: Vec::new(),
143            operation: None,
144            agent: None,
145            metadata: HashMap::new(),
146        }
147    }
148
149    /// Add input.
150    pub fn with_input(mut self, input_id: String) -> Self {
151        self.inputs.push(input_id);
152        self
153    }
154
155    /// Add output.
156    pub fn with_output(mut self, output_id: String) -> Self {
157        self.outputs.push(output_id);
158        self
159    }
160
161    /// Set operation.
162    pub fn with_operation(mut self, operation_id: String) -> Self {
163        self.operation = Some(operation_id);
164        self
165    }
166
167    /// Set agent.
168    pub fn with_agent(mut self, agent_id: String) -> Self {
169        self.agent = Some(agent_id);
170        self
171    }
172
173    /// Add metadata.
174    pub fn with_metadata(mut self, key: String, value: String) -> Self {
175        self.metadata.insert(key, value);
176        self
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183
184    #[test]
185    fn test_lineage_node_creation() {
186        let node = LineageNode::new(NodeType::Dataset, "dataset-123".to_string())
187            .with_metadata("format".to_string(), "GeoTIFF".to_string());
188
189        assert_eq!(node.node_type, NodeType::Dataset);
190        assert_eq!(node.entity_id, "dataset-123");
191        assert_eq!(node.metadata.get("format"), Some(&"GeoTIFF".to_string()));
192    }
193
194    #[test]
195    fn test_lineage_edge_creation() {
196        let edge = LineageEdge::new(
197            "node-1".to_string(),
198            "node-2".to_string(),
199            EdgeType::DerivedFrom,
200        )
201        .with_metadata("operation".to_string(), "reproject".to_string());
202
203        assert_eq!(edge.source_id, "node-1");
204        assert_eq!(edge.target_id, "node-2");
205        assert_eq!(edge.edge_type, EdgeType::DerivedFrom);
206    }
207
208    #[test]
209    fn test_lineage_event() {
210        let event = LineageEvent::new("transform".to_string())
211            .with_input("input-1".to_string())
212            .with_output("output-1".to_string())
213            .with_operation("op-1".to_string())
214            .with_agent("user-123".to_string());
215
216        assert_eq!(event.event_type, "transform");
217        assert_eq!(event.inputs.len(), 1);
218        assert_eq!(event.outputs.len(), 1);
219        assert_eq!(event.operation, Some("op-1".to_string()));
220        assert_eq!(event.agent, Some("user-123".to_string()));
221    }
222}