Skip to main content

relay_event_schema/protocol/span_v2/
container.rs

1use serde::{Deserialize, Serialize};
2
3/// Metadata supplied with an item container containing spans.
4///
5/// See also: <https://develop.sentry.dev/sdk/telemetry/spans/span-protocol/#version-and-ingest_settings-properties>.
6#[derive(Debug, Clone, Deserialize, Serialize)]
7pub struct ContainerMetadata {
8    /// The metadata version
9    ///
10    /// The version influences how spans are processed during ingestion.
11    ///
12    /// Currently supported versions:
13    /// - `missing`: no behaviour change
14    /// - `2`: adds support for `ingest_settings`
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub version: Option<u16>,
17    /// Settings controlling parts of the ingestion of individual spans.
18    ///
19    /// Supported since version `2`.
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub ingest_settings: Option<IngestSettings>,
22}
23
24/// Span ingestion settings.
25///
26/// See also: [`ContainerMetadata::ingest_settings`].
27#[derive(Debug, Clone, Deserialize, Serialize)]
28pub struct IngestSettings {
29    /// Controls how and if the client IP address should be inferred.
30    #[serde(
31        default,
32        skip_serializing_if = "Option::is_none",
33        deserialize_with = "none_on_error"
34    )]
35    pub infer_ip: Option<InferIp>,
36    /// Controls how and if the user agent should be inferred.
37    #[serde(
38        default,
39        skip_serializing_if = "Option::is_none",
40        deserialize_with = "none_on_error"
41    )]
42    pub infer_user_agent: Option<InferUserAgent>,
43}
44
45/// Settings controlling how client IP addresses should be inferred.
46///
47/// Specifically frontend SDKs may want to have the IP address automatically inferred.
48#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
49pub enum InferIp {
50    /// The IP address is inferred by from the client connection.
51    #[serde(rename = "auto")]
52    Auto,
53    /// The client IP address is never inferred.
54    #[serde(rename = "never")]
55    Never,
56}
57
58impl InferIp {
59    /// Returns `true` if `self` is [`Self::Auto`].
60    pub fn is_auto(&self) -> bool {
61        matches!(self, Self::Auto)
62    }
63}
64
65/// Settings controlling how the user agent should be inferred.
66///
67/// Specifically frontend SDKs may want to have the user agent and browser information automatically inferred
68/// from the HTTP header provided user agent.
69#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
70pub enum InferUserAgent {
71    /// The user agent should be inferred from the http request submitting the spans.
72    #[serde(rename = "auto")]
73    Auto,
74    /// The user agent should never be inferred.
75    #[serde(rename = "never")]
76    Never,
77}
78
79impl InferUserAgent {
80    /// Returns `true` if `self` is [`Self::Auto`].
81    pub fn is_auto(&self) -> bool {
82        matches!(self, Self::Auto)
83    }
84}
85
86fn none_on_error<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
87where
88    D: serde::de::Deserializer<'de>,
89    T: serde::de::DeserializeOwned + std::fmt::Debug,
90{
91    #[derive(Deserialize)]
92    #[serde(untagged)]
93    enum Try<T> {
94        T(T),
95        Err(serde::de::IgnoredAny),
96    }
97
98    Ok(match Try::<T>::deserialize(deserializer)? {
99        Try::T(t) => Some(t),
100        Try::Err(_) => None,
101    })
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    #[test]
109    fn test_valid_round_trip() {
110        let m: ContainerMetadata = serde_json::from_str(
111            r#"{
112            "version": 123, 
113            "ingest_settings": {
114                "infer_ip": "auto",
115                "infer_user_agent": "never"
116            }
117        }"#,
118        )
119        .unwrap();
120
121        insta::assert_json_snapshot!(&m, @r#"
122        {
123          "version": 123,
124          "ingest_settings": {
125            "infer_ip": "auto",
126            "infer_user_agent": "never"
127          }
128        }
129        "#);
130    }
131
132    #[test]
133    fn test_ingest_settings_invalid() {
134        let m: ContainerMetadata = serde_json::from_str(
135            r#"{"ingest_settings": {
136            "infer_ip": "unknown",
137            "infer_user_agent": 123
138        }}"#,
139        )
140        .unwrap();
141
142        insta::assert_json_snapshot!(&m, @r#"
143        {
144          "ingest_settings": {}
145        }
146        "#);
147    }
148
149    #[test]
150    fn test_version_invalid_fails() {
151        let _ = serde_json::from_str::<ContainerMetadata>(r#"{"version": {}}"#).unwrap_err();
152    }
153}