shuttle_common/models/
telemetry.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use serde::{Deserialize, Serialize};

/// Status of a telemetry export configuration for an external sink
#[derive(Eq, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[typeshare::typeshare]
pub struct TelemetrySinkStatus {
    /// Indicates that the associated project is configured to export telemetry data to this sink
    enabled: bool,
}

/// A safe-for-display representation of the current telemetry export configuration for a given project
#[derive(Eq, Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[typeshare::typeshare]
pub struct TelemetryConfigResponse {
    betterstack: Option<TelemetrySinkStatus>,
    datadog: Option<TelemetrySinkStatus>,
    grafana_cloud: Option<TelemetrySinkStatus>,
}

impl From<Vec<TelemetrySinkConfig>> for TelemetryConfigResponse {
    fn from(value: Vec<TelemetrySinkConfig>) -> Self {
        let mut instance = Self::default();

        for sink in value {
            match sink {
                TelemetrySinkConfig::Betterstack(_) => {
                    instance.betterstack = Some(TelemetrySinkStatus { enabled: true })
                }
                TelemetrySinkConfig::Datadog(_) => {
                    instance.datadog = Some(TelemetrySinkStatus { enabled: true })
                }
                TelemetrySinkConfig::GrafanaCloud(_) => {
                    instance.grafana_cloud = Some(TelemetrySinkStatus { enabled: true })
                }
            }
        }

        instance
    }
}

/// The user-supplied config required to export telemetry to a given external sink
#[derive(
    Eq, Clone, PartialEq, Serialize, Deserialize, strum::AsRefStr, strum::EnumDiscriminants,
)]
#[serde(tag = "type", content = "content", rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
#[typeshare::typeshare]
#[strum_discriminants(derive(Serialize, Deserialize, strum::AsRefStr))]
#[strum_discriminants(serde(rename_all = "snake_case"))]
#[strum_discriminants(strum(serialize_all = "snake_case"))]
pub enum TelemetrySinkConfig {
    /// [Betterstack](https://betterstack.com/docs/logs/open-telemetry/)
    Betterstack(BetterstackConfig),
    /// [Datadog](https://docs.datadoghq.com/opentelemetry/collector_exporter/otel_collector_datadog_exporter)
    Datadog(DatadogConfig),
    /// [Grafana Cloud](https://grafana.com/docs/grafana-cloud/send-data/otlp/)
    GrafanaCloud(GrafanaCloudConfig),
}

impl TelemetrySinkConfig {
    pub fn as_db_type(&self) -> String {
        format!("project::telemetry::{}::config", self.as_ref())
    }
}

impl TelemetrySinkConfigDiscriminants {
    pub fn as_db_type(&self) -> String {
        format!("project::telemetry::{}::config", self.as_ref())
    }
}

#[derive(Eq, Clone, PartialEq, Serialize, Deserialize)]
#[typeshare::typeshare]
pub struct BetterstackConfig {
    pub source_token: String,
}
#[derive(Eq, Clone, PartialEq, Serialize, Deserialize)]
#[typeshare::typeshare]
pub struct DatadogConfig {
    pub api_key: String,
}
#[derive(Eq, Clone, PartialEq, Serialize, Deserialize)]
#[typeshare::typeshare]
pub struct GrafanaCloudConfig {
    pub token: String,
    pub endpoint: String,
    pub instance_id: String,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn sink_config_enum() {
        assert_eq!(
            "betterstack",
            TelemetrySinkConfig::Betterstack(BetterstackConfig {
                source_token: "".into()
            })
            .as_ref()
        );
        assert_eq!(
            "project::telemetry::betterstack::config",
            TelemetrySinkConfig::Betterstack(BetterstackConfig {
                source_token: "".into()
            })
            .as_db_type()
        );

        assert_eq!(
            "betterstack",
            TelemetrySinkConfigDiscriminants::Betterstack.as_ref()
        );
        assert_eq!(
            "grafana_cloud",
            TelemetrySinkConfigDiscriminants::GrafanaCloud.as_ref()
        );
        assert_eq!(
            "\"betterstack\"",
            serde_json::to_string(&TelemetrySinkConfigDiscriminants::Betterstack).unwrap()
        );
        assert_eq!(
            "\"grafana_cloud\"",
            serde_json::to_string(&TelemetrySinkConfigDiscriminants::GrafanaCloud).unwrap()
        );
    }
}