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}