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