Skip to main content

relay_event_schema/protocol/span_v2/
container.rs

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