relay_event_normalization/
remove_other.rs1use relay_event_schema::processor::{ProcessValue, ProcessingResult, ProcessingState, Processor};
2use relay_event_schema::protocol::{Breadcrumb, Event};
3use relay_protocol::{Annotated, ErrorKind, Meta, Object, Value};
4
5fn create_errors(other: &mut Object<Value>) {
7 for value in other.values_mut() {
8 *value = Annotated::from_error(ErrorKind::InvalidAttribute, None);
9 }
10}
11
12pub struct RemoveOtherProcessor;
14
15impl Processor for RemoveOtherProcessor {
16 fn process_other(
17 &mut self,
18 other: &mut Object<Value>,
19 state: &ProcessingState<'_>,
20 ) -> ProcessingResult {
21 if !state.attrs().retain {
24 other.clear();
25 }
26
27 Ok(())
28 }
29
30 fn process_breadcrumb(
31 &mut self,
32 breadcrumb: &mut Breadcrumb,
33 _meta: &mut Meta,
34 state: &ProcessingState<'_>,
35 ) -> ProcessingResult {
36 let mut other = std::mem::take(&mut breadcrumb.other);
38 create_errors(&mut other);
39
40 breadcrumb.process_child_values(self, state)?;
42 breadcrumb.other = other;
43 Ok(())
44 }
45
46 fn process_event(
47 &mut self,
48 event: &mut Event,
49 _meta: &mut Meta,
50 state: &ProcessingState<'_>,
51 ) -> ProcessingResult {
52 let mut other = std::mem::take(&mut event.other);
54
55 other.remove("metadata");
57 other.remove("hashes");
58
59 other.remove("applecrashreport");
61 other.remove("device");
62 other.remove("repos");
63 other.remove("query");
64
65 create_errors(&mut other);
67
68 event.process_child_values(self, state)?;
70
71 event.other = other;
72 Ok(())
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use relay_event_schema::processor::process_value;
79 use relay_event_schema::protocol::{Context, Contexts, OsContext, User, Values};
80 use relay_protocol::{FromValue, get_value};
81 use similar_asserts::assert_eq;
82
83 use super::*;
84
85 #[test]
86 fn test_remove_legacy_attributes() {
87 let mut event = Annotated::new(Event {
88 other: {
89 let mut other = Object::new();
90 other.insert("applecrashreport".to_string(), Value::U64(42).into());
91 other.insert("device".to_string(), Value::U64(42).into());
92 other.insert("repos".to_string(), Value::U64(42).into());
93 other.insert("query".to_string(), Value::U64(42).into());
94 other
95 },
96 ..Default::default()
97 });
98
99 process_value(
100 &mut event,
101 &mut RemoveOtherProcessor,
102 ProcessingState::root(),
103 )
104 .unwrap();
105
106 assert!(event.value().unwrap().other.is_empty());
107 }
108
109 #[test]
110 fn test_remove_unknown_attributes() {
111 let mut event = Annotated::new(Event {
112 other: {
113 let mut other = Object::new();
114 other.insert("foo".to_string(), Value::U64(42).into());
115 other.insert("bar".to_string(), Value::U64(42).into());
116 other
117 },
118 ..Default::default()
119 });
120
121 process_value(
122 &mut event,
123 &mut RemoveOtherProcessor,
124 ProcessingState::root(),
125 )
126 .unwrap();
127
128 let other = &event.value().unwrap().other;
129 assert_eq!(
130 *other.get("foo").unwrap(),
131 Annotated::from_error(ErrorKind::InvalidAttribute, None)
132 );
133 assert_eq!(
134 *other.get("bar").unwrap(),
135 Annotated::from_error(ErrorKind::InvalidAttribute, None)
136 );
137 }
138
139 #[test]
140 fn test_remove_nested_other() {
141 let mut event = Annotated::new(Event {
142 user: Annotated::from(User {
143 other: {
144 let mut other = Object::new();
145 other.insert("foo".to_string(), Value::U64(42).into());
146 other.insert("bar".to_string(), Value::U64(42).into());
147 other
148 },
149 ..Default::default()
150 }),
151 ..Default::default()
152 });
153
154 process_value(
155 &mut event,
156 &mut RemoveOtherProcessor,
157 ProcessingState::root(),
158 )
159 .unwrap();
160
161 assert!(get_value!(event.user!).other.is_empty());
162 }
163
164 #[test]
165 fn test_retain_context_other() {
166 let mut os = OsContext::default();
167 os.other
168 .insert("foo".to_string(), Annotated::from(Value::U64(42)));
169
170 let mut contexts = Contexts::new();
171 contexts.insert("renamed".to_string(), Context::Os(Box::new(os)));
172
173 let mut event = Annotated::new(Event {
174 contexts: Annotated::new(contexts.clone()),
175 ..Default::default()
176 });
177
178 process_value(
179 &mut event,
180 &mut RemoveOtherProcessor,
181 ProcessingState::root(),
182 )
183 .unwrap();
184
185 assert_eq!(get_value!(event.contexts!).0, contexts.0);
186 }
187
188 #[test]
189 fn test_breadcrumb_errors() {
190 let mut event = Annotated::new(Event {
191 breadcrumbs: Annotated::new(Values::new(vec![Annotated::new(Breadcrumb {
192 other: {
193 let mut other = Object::new();
194 other.insert("foo".to_string(), Value::U64(42).into());
195 other.insert("bar".to_string(), Value::U64(42).into());
196 other
197 },
198 ..Breadcrumb::default()
199 })])),
200 ..Default::default()
201 });
202
203 process_value(
204 &mut event,
205 &mut RemoveOtherProcessor,
206 ProcessingState::root(),
207 )
208 .unwrap();
209
210 let other = &event
211 .value()
212 .unwrap()
213 .breadcrumbs
214 .value()
215 .unwrap()
216 .values
217 .value()
218 .unwrap()[0]
219 .value()
220 .unwrap()
221 .other;
222
223 assert_eq!(
224 *other.get("foo").unwrap(),
225 Annotated::from_error(ErrorKind::InvalidAttribute, None)
226 );
227 assert_eq!(
228 *other.get("bar").unwrap(),
229 Annotated::from_error(ErrorKind::InvalidAttribute, None)
230 );
231 }
232
233 #[test]
234 fn test_scrape_attempts() {
235 let json = serde_json::json!({
236 "scraping_attempts": [
237 {"status": "not_attempted", "url": "http://example.com/embedded.js"},
238 {"status": "not_attempted", "url": "http://example.com/embedded.js.map"},
239 ]
240 });
241
242 let mut event = Event::from_value(json.into());
243 process_value(
244 &mut event,
245 &mut RemoveOtherProcessor,
246 ProcessingState::root(),
247 )
248 .unwrap();
249 assert!(event.value().unwrap().other.is_empty());
250 }
251}