river_data_core/models/
streams.rs1use serde::{Deserialize, Serialize};
2use uuid::Uuid;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct DataStream {
6 pub id: Uuid,
7 pub source_system: String,
8 pub source_key: String,
9 pub source_name: Option<String>,
10 pub source_path: Option<String>,
11 pub metadata: serde_json::Value,
12 pub site_parameter_id: Option<Uuid>,
13 #[serde(default, skip_serializing_if = "Option::is_none")]
16 pub measurement_type: Option<String>,
17 pub is_active: bool,
18 pub last_data_time: Option<chrono::DateTime<chrono::Utc>>,
19}
20
21#[derive(Debug, Serialize)]
22pub struct RegisterStreamRequest {
23 pub source_system: String,
24 pub source_key: String,
25 pub source_name: Option<String>,
26 pub source_path: Option<String>,
27 pub metadata: serde_json::Value,
28 #[serde(skip_serializing_if = "Option::is_none")]
30 pub measurement_type: Option<String>,
31}
32
33#[derive(Debug, Clone, Serialize)]
34pub struct IngestReading {
35 pub time: chrono::DateTime<chrono::Utc>,
36 pub raw_value: f64,
37 #[serde(skip_serializing_if = "is_zero")]
38 pub replicate_index: i16,
39 #[serde(skip_serializing_if = "Option::is_none")]
40 pub sensor_id: Option<Uuid>,
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub calibration_id: Option<Uuid>,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub deployment_id: Option<Uuid>,
45 #[serde(skip_serializing_if = "Option::is_none")]
48 pub measurement_type: Option<String>,
49}
50
51impl IngestReading {
52 pub fn new(time: chrono::DateTime<chrono::Utc>, raw_value: f64) -> Self {
54 Self {
55 time,
56 raw_value,
57 replicate_index: 0,
58 sensor_id: None,
59 calibration_id: None,
60 deployment_id: None,
61 measurement_type: None,
62 }
63 }
64}
65
66fn is_zero(v: &i16) -> bool {
67 *v == 0
68}
69
70#[derive(Debug, Serialize)]
71pub struct IngestStatusEvent {
72 pub time: chrono::DateTime<chrono::Utc>,
73 pub value: String,
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn test_ingest_reading_serialization() {
82 let r = IngestReading::new(chrono::Utc::now(), 42.5);
83 let json = serde_json::to_value(&r).unwrap();
84 assert_eq!(json["raw_value"], 42.5);
85 assert!(json.get("replicate_index").is_none());
86 assert!(json.get("sensor_id").is_none());
87 assert!(json.get("measurement_type").is_none());
88 }
89
90 #[test]
91 fn test_register_stream_request() {
92 let req = RegisterStreamRequest {
93 source_system: "test_system".to_string(),
94 source_key: "source_1".to_string(),
95 source_name: Some("stream_a".to_string()),
96 source_path: None,
97 metadata: serde_json::json!({"device": "dev_001"}),
98 measurement_type: None,
99 };
100 let json = serde_json::to_value(&req).unwrap();
101 assert_eq!(json["source_system"], "test_system");
102 assert_eq!(json["metadata"]["device"], "dev_001");
103 }
104
105 #[test]
106 fn test_data_stream_deserialization() {
107 let json = serde_json::json!({
108 "id": "550e8400-e29b-41d4-a716-446655440000",
109 "source_system": "test_system",
110 "source_key": "source_1",
111 "source_name": "stream_a",
112 "source_path": null,
113 "metadata": {},
114 "site_parameter_id": null,
115 "is_active": true,
116 "last_data_time": null
117 });
118 let stream: DataStream = serde_json::from_value(json).unwrap();
119 assert_eq!(stream.source_system, "test_system");
120 assert!(stream.is_active);
121 assert!(stream.site_parameter_id.is_none());
122 }
123}