relay_event_normalization/
timestamp.rs1use relay_event_schema::processor::{
2 ProcessValue, ProcessingAction, ProcessingResult, ProcessingState, Processor,
3};
4use relay_event_schema::protocol::{Breadcrumb, Event, Span};
5use relay_protocol::{Error, Meta};
6
7pub struct TimestampProcessor;
19
20impl Processor for TimestampProcessor {
21 fn process_event(
22 &mut self,
23 event: &mut Event,
24 _: &mut Meta,
25 state: &ProcessingState,
26 ) -> ProcessingResult {
27 if let Some(end_timestamp) = event.timestamp.value() {
28 if end_timestamp.into_inner().timestamp_millis() < 0 {
29 return Err(ProcessingAction::InvalidTransaction(
30 "timestamp is too stale",
31 ));
32 }
33 }
34 if let Some(start_timestamp) = event.start_timestamp.value() {
35 if start_timestamp.into_inner().timestamp_millis() < 0 {
36 return Err(ProcessingAction::InvalidTransaction(
37 "timestamp is too stale",
38 ));
39 }
40 }
41
42 event.process_child_values(self, state)?;
43
44 Ok(())
45 }
46
47 fn process_span(
48 &mut self,
49 span: &mut Span,
50 meta: &mut Meta,
51 _: &ProcessingState<'_>,
52 ) -> ProcessingResult {
53 if let Some(start_timestamp) = span.start_timestamp.value() {
54 if start_timestamp.into_inner().timestamp_millis() < 0 {
55 meta.add_error(Error::invalid(format!(
56 "start_timestamp is too stale: {start_timestamp}"
57 )));
58 return Err(ProcessingAction::DeleteValueHard);
59 }
60 }
61 if let Some(end_timestamp) = span.timestamp.value() {
62 if end_timestamp.into_inner().timestamp_millis() < 0 {
63 meta.add_error(Error::invalid(format!(
64 "timestamp is too stale: {end_timestamp}"
65 )));
66 return Err(ProcessingAction::DeleteValueHard);
67 }
68 }
69
70 Ok(())
71 }
72
73 fn process_breadcrumb(
74 &mut self,
75 breadcrumb: &mut Breadcrumb,
76 meta: &mut Meta,
77 _: &ProcessingState<'_>,
78 ) -> ProcessingResult where {
79 if let Some(timestamp) = breadcrumb.timestamp.value() {
80 if timestamp.into_inner().timestamp_millis() < 0 {
81 meta.add_error(Error::invalid(format!(
82 "timestamp is too stale: {timestamp}"
83 )));
84 return Err(ProcessingAction::DeleteValueHard);
85 }
86 }
87
88 Ok(())
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use relay_event_schema::processor::{ProcessingState, process_value};
95 use relay_event_schema::protocol::{Breadcrumb, Event, Span};
96 use relay_protocol::{Annotated, assert_annotated_snapshot, get_value};
97
98 use crate::timestamp::TimestampProcessor;
99
100 #[test]
101 fn test_accept_recent_errors() {
102 let json = r#"{
103 "event_id": "52df9022835246eeb317dbd739ccd059",
104 "timestamp": 1
105}"#;
106 let mut error = Annotated::<Event>::from_json(json).unwrap();
107 assert!(
108 process_value(&mut error, &mut TimestampProcessor, ProcessingState::root()).is_ok()
109 );
110 assert_eq!(get_value!(error.timestamp!).into_inner().timestamp(), 1);
111 }
112
113 #[test]
114 fn test_reject_stale_errors() {
115 let json = r#"{
116 "event_id": "52df9022835246eeb317dbd739ccd059",
117 "timestamp": -1
118}"#;
119 let mut error = Annotated::<Event>::from_json(json).unwrap();
120 assert_eq!(
121 process_value(&mut error, &mut TimestampProcessor, ProcessingState::root())
122 .unwrap_err()
123 .to_string(),
124 "invalid transaction event: timestamp is too stale"
125 );
126 }
127
128 #[test]
129 fn test_accept_recent_transactions() {
130 let json = r#"{
131 "event_id": "52df9022835246eeb317dbd739ccd059",
132 "start_timestamp": 1,
133 "timestamp": 2
134}"#;
135 let mut transaction = Annotated::<Event>::from_json(json).unwrap();
136 assert!(
137 process_value(
138 &mut transaction,
139 &mut TimestampProcessor,
140 ProcessingState::root()
141 )
142 .is_ok()
143 );
144 }
145
146 #[test]
147 fn test_reject_stale_transactions() {
148 let json = r#"{
149 "event_id": "52df9022835246eeb317dbd739ccd059",
150 "start_timestamp": -2,
151 "timestamp": -1
152}"#;
153 let mut transaction = Annotated::<Event>::from_json(json).unwrap();
154 assert_eq!(
155 process_value(
156 &mut transaction,
157 &mut TimestampProcessor,
158 ProcessingState::root()
159 )
160 .unwrap_err()
161 .to_string(),
162 "invalid transaction event: timestamp is too stale"
163 );
164 }
165
166 #[test]
167 fn test_reject_long_running_transactions() {
168 let json = r#"{
169 "event_id": "52df9022835246eeb317dbd739ccd059",
170 "start_timestamp": -1,
171 "timestamp": 1
172}"#;
173 let mut transaction = Annotated::<Event>::from_json(json).unwrap();
174 assert_eq!(
175 process_value(
176 &mut transaction,
177 &mut TimestampProcessor,
178 ProcessingState::root()
179 )
180 .unwrap_err()
181 .to_string(),
182 "invalid transaction event: timestamp is too stale"
183 );
184 }
185
186 #[test]
187 fn test_accept_recent_span() {
188 let json = r#"{
189 "span_id": "52df9022835246eeb317dbd739ccd050",
190 "start_timestamp": 1,
191 "timestamp": 2
192 }"#;
193 let mut span = Annotated::<Span>::from_json(json).unwrap();
194 assert!(process_value(&mut span, &mut TimestampProcessor, ProcessingState::root()).is_ok());
195 assert_eq!(
196 get_value!(span.start_timestamp!).into_inner().timestamp(),
197 1
198 );
199 assert_eq!(get_value!(span.timestamp!).into_inner().timestamp(), 2);
200 }
201
202 #[test]
203 fn test_reject_stale_span() {
204 let json = r#"{
205 "span_id": "52df9022835246eeb317dbd739ccd050",
206 "start_timestamp": -2,
207 "timestamp": -1
208 }"#;
209 let mut span = Annotated::<Span>::from_json(json).unwrap();
210 assert!(process_value(&mut span, &mut TimestampProcessor, ProcessingState::root()).is_ok());
211 assert_annotated_snapshot!(&span, @r###"
212 {
213 "_meta": {
214 "": {
215 "err": [
216 [
217 "invalid_data",
218 {
219 "reason": "start_timestamp is too stale: 1969-12-31 23:59:58 UTC"
220 }
221 ]
222 ]
223 }
224 }
225 }
226 "###);
227 }
228
229 #[test]
230 fn test_reject_long_running_span() {
231 let json = r#"{
232 "span_id": "52df9022835246eeb317dbd739ccd050",
233 "start_timestamp": -1,
234 "timestamp": 1
235 }"#;
236 let mut span = Annotated::<Span>::from_json(json).unwrap();
237 assert!(process_value(&mut span, &mut TimestampProcessor, ProcessingState::root()).is_ok());
238 assert_annotated_snapshot!(&span, @r###"
239 {
240 "_meta": {
241 "": {
242 "err": [
243 [
244 "invalid_data",
245 {
246 "reason": "start_timestamp is too stale: 1969-12-31 23:59:59 UTC"
247 }
248 ]
249 ]
250 }
251 }
252 }
253 "###);
254 }
255
256 #[test]
257 fn test_accept_recent_breadcrumb() {
258 let json = r#"{
259 "timestamp": 1
260 }"#;
261 let mut breadcrumb = Annotated::<Breadcrumb>::from_json(json).unwrap();
262 assert!(
263 process_value(
264 &mut breadcrumb,
265 &mut TimestampProcessor,
266 ProcessingState::root()
267 )
268 .is_ok()
269 );
270 assert_eq!(
271 get_value!(breadcrumb.timestamp!).into_inner().timestamp(),
272 1
273 );
274 }
275
276 #[test]
277 fn test_reject_stale_breadcrumb() {
278 let json = r#"{
279 "timestamp": -1
280 }"#;
281 let mut breadcrumb = Annotated::<Breadcrumb>::from_json(json).unwrap();
282 assert!(
283 process_value(
284 &mut breadcrumb,
285 &mut TimestampProcessor,
286 ProcessingState::root()
287 )
288 .is_ok()
289 );
290 assert_annotated_snapshot!(&breadcrumb, @r###"
291 {
292 "_meta": {
293 "": {
294 "err": [
295 [
296 "invalid_data",
297 {
298 "reason": "timestamp is too stale: 1969-12-31 23:59:59 UTC"
299 }
300 ]
301 ]
302 }
303 }
304 }
305 "###);
306 }
307}