relay_filter/
interface.rs1use url::Url;
4
5use relay_event_schema::protocol::{
6 Csp, Event, EventType, Exception, LogEntry, OurLog, Replay, SessionAggregates, SessionUpdate,
7 Span, Values,
8};
9
10#[derive(Clone, Debug, Default)]
12pub struct UserAgent<'a> {
13 pub raw: Option<&'a str>,
15 pub parsed: Option<relay_ua::UserAgent<'a>>,
17}
18
19impl<'a> UserAgent<'a> {
20 pub fn parsed(&mut self) -> Option<&'_ relay_ua::UserAgent<'a>> {
22 match &mut self.parsed {
23 Some(parsed) => Some(parsed),
24 parsed @ None => {
25 *parsed = Some(relay_ua::parse_user_agent(self.raw?));
26 parsed.as_ref()
27 }
28 }
29 }
30}
31
32pub trait Filterable {
34 fn csp(&self) -> Option<&Csp> {
36 None
37 }
38
39 fn exceptions(&self) -> Option<&Values<Exception>> {
41 None
42 }
43
44 fn ip_addr(&self) -> Option<&str> {
46 None
47 }
48
49 fn logentry(&self) -> Option<&LogEntry> {
51 None
52 }
53
54 fn release(&self) -> Option<&str> {
56 None
57 }
58
59 fn transaction(&self) -> Option<&str> {
61 None
62 }
63
64 fn url(&self) -> Option<Url> {
66 None
67 }
68
69 fn user_agent(&self) -> UserAgent<'_> {
71 Default::default()
72 }
73
74 fn header(&self, _header_name: &str) -> Option<&str> {
81 None
82 }
83}
84
85impl Filterable for Event {
86 fn csp(&self) -> Option<&Csp> {
87 if self.ty.value() != Some(&EventType::Csp) {
88 return None;
89 }
90 self.csp.value()
91 }
92
93 fn exceptions(&self) -> Option<&Values<Exception>> {
94 self.exceptions.value()
95 }
96
97 fn ip_addr(&self) -> Option<&str> {
98 let user = self.user.value()?;
99 Some(user.ip_address.value()?.as_ref())
100 }
101
102 fn logentry(&self) -> Option<&LogEntry> {
103 self.logentry.value()
104 }
105
106 fn release(&self) -> Option<&str> {
107 self.release.as_str()
108 }
109
110 fn transaction(&self) -> Option<&str> {
111 if self.ty.value() != Some(&EventType::Transaction) {
112 return None;
113 }
114 self.transaction.as_str()
115 }
116
117 fn url(&self) -> Option<Url> {
118 let url_str = self.request.value()?.url.value()?;
119 Url::parse(url_str).ok()
120 }
121
122 fn user_agent(&self) -> UserAgent<'_> {
123 UserAgent {
124 raw: self.user_agent(),
125 parsed: None,
126 }
127 }
128
129 fn header(&self, header_name: &str) -> Option<&str> {
130 self.request
131 .value()?
132 .headers
133 .value()?
134 .get_header(header_name)
135 }
136}
137
138impl Filterable for Replay {
139 fn ip_addr(&self) -> Option<&str> {
140 let user = self.user.value()?;
141 Some(user.ip_address.value()?.as_ref())
142 }
143
144 fn release(&self) -> Option<&str> {
145 self.release.as_str()
146 }
147
148 fn url(&self) -> Option<Url> {
149 let url_str = self.request.value()?.url.value()?;
150 Url::parse(url_str).ok()
151 }
152
153 fn user_agent(&self) -> UserAgent<'_> {
154 UserAgent {
155 raw: self.user_agent(),
156 parsed: None,
157 }
158 }
159
160 fn header(&self, header_name: &str) -> Option<&str> {
161 self.request
162 .value()?
163 .headers
164 .value()?
165 .get_header(header_name)
166 }
167}
168
169impl Filterable for Span {
170 fn ip_addr(&self) -> Option<&str> {
171 self.data.value()?.client_address.as_str()
172 }
173
174 fn release(&self) -> Option<&str> {
175 self.data.value()?.release.as_str()
176 }
177
178 fn transaction(&self) -> Option<&str> {
179 self.data.value()?.segment_name.as_str()
180 }
181
182 fn url(&self) -> Option<Url> {
183 let url_str = self.data.value()?.url_full.as_str()?;
184 Url::parse(url_str).ok()
185 }
186
187 fn user_agent(&self) -> UserAgent<'_> {
188 let raw = self
189 .data
190 .value()
191 .and_then(|data| data.user_agent_original.as_str());
192 UserAgent { raw, parsed: None }
193 }
194}
195
196impl Filterable for SessionUpdate {
197 fn ip_addr(&self) -> Option<&str> {
198 self.attributes
199 .ip_address
200 .as_ref()
201 .map(|addr| addr.as_str())
202 }
203
204 fn release(&self) -> Option<&str> {
205 Some(&self.attributes.release)
206 }
207
208 fn user_agent(&self) -> UserAgent<'_> {
209 UserAgent {
210 raw: self.attributes.user_agent.as_deref(),
211 parsed: None,
212 }
213 }
214}
215
216impl Filterable for SessionAggregates {
217 fn ip_addr(&self) -> Option<&str> {
218 self.attributes
219 .ip_address
220 .as_ref()
221 .map(|addr| addr.as_str())
222 }
223
224 fn release(&self) -> Option<&str> {
225 Some(&self.attributes.release)
226 }
227
228 fn user_agent(&self) -> UserAgent<'_> {
229 UserAgent {
230 raw: self.attributes.user_agent.as_deref(),
231 parsed: None,
232 }
233 }
234}
235
236impl Filterable for OurLog {
237 fn release(&self) -> Option<&str> {
238 self.attributes
239 .value()?
240 .get_value("sentry.release")?
241 .as_str()
242 }
243
244 fn user_agent(&self) -> UserAgent<'_> {
245 let parsed = (|| {
246 let attributes = self.attributes.value()?;
247
248 let family = attributes.get_value("sentry.browser.name")?.as_str()?;
249 let version = attributes.get_value("sentry.browser.version")?.as_str()?;
250 let mut parts = version.splitn(3, '.');
251
252 Some(relay_ua::UserAgent {
253 family: family.into(),
254 major: parts.next().map(Into::into),
255 minor: parts.next().map(Into::into),
256 patch: parts.next().map(Into::into),
257 })
258 })();
259
260 UserAgent { raw: None, parsed }
261 }
262}