relay_filter/
generic.rs

1//! Implements generic filtering based on the [`RuleCondition`] DSL.
2//!
3//! Multiple generic filters can be defined and they are going to be checked in FIFO order. The
4//! first one that matches, will result in the event being discarded with a [`FilterStatKey`]
5//! identifying the matching filter.
6
7use std::iter::FusedIterator;
8
9use crate::{FilterStatKey, GenericFilterConfig, GenericFiltersConfig, GenericFiltersMap};
10
11use relay_protocol::{Getter, RuleCondition};
12
13/// Maximum supported version of the generic filters schema.
14///
15/// If the version in the project config is higher, no generic filters are applied.
16const MAX_SUPPORTED_VERSION: u16 = 1;
17
18/// Returns whether the given generic config versions are supported.
19pub fn are_generic_filters_supported(
20    global_filters_version: Option<u16>,
21    project_filters_version: u16,
22) -> bool {
23    global_filters_version.is_none_or(|v| v <= MAX_SUPPORTED_VERSION)
24        && project_filters_version <= MAX_SUPPORTED_VERSION
25}
26
27/// Checks events by patterns in their error messages.
28fn matches<F: Getter>(item: &F, condition: Option<&RuleCondition>) -> bool {
29    // TODO: the condition DSL needs to be extended to support more complex semantics, such as
30    //  collections operations.
31    condition.is_some_and(|condition| condition.matches(item))
32}
33
34/// Filters events by any generic condition.
35///
36/// Note that conditions may have type-specific getter strings, e.g. `"event.some_field"`. In order
37/// to make such a generic filter apply to non-Event types, make sure that the [`Getter`] implementation
38/// for that type maps `"event.some_field"` to the corresponding field on that type.
39pub(crate) fn should_filter<F: Getter>(
40    item: &F,
41    project_filters: &GenericFiltersConfig,
42    global_filters: Option<&GenericFiltersConfig>,
43) -> Result<(), FilterStatKey> {
44    let filters = merge_generic_filters(
45        project_filters,
46        global_filters,
47        #[cfg(test)]
48        MAX_SUPPORTED_VERSION,
49    );
50
51    for filter_config in filters {
52        if filter_config.is_enabled && matches(item, filter_config.condition) {
53            return Err(FilterStatKey::GenericFilter(filter_config.id.to_owned()));
54        }
55    }
56
57    Ok(())
58}
59
60/// Returns an iterator that yields merged generic configs.
61///
62/// Since filters of project and global configs are complementary and don't
63/// provide much value in isolation, both versions must match
64/// [`MAX_SUPPORTED_VERSION`] to be compatible.  If filters aren't compatible,
65/// an empty iterator is returned.
66///
67/// If filters are compatible, an iterator over all filters is returned. This
68/// iterator yields filters according to the principles below:
69/// - Filters from project configs are evaluated before filters from global
70///   configs.
71/// - No duplicates: once a filter is evaluated (yielded or skipped), no filter
72///   with the same id is evaluated again.
73/// - If a filter with the same id exists in projects and global configs, both
74///   are merged and the filter is yielded. Values from the filter in the project
75///   config are prioritized.
76fn merge_generic_filters<'a>(
77    project: &'a GenericFiltersConfig,
78    global: Option<&'a GenericFiltersConfig>,
79    #[cfg(test)] max_supported_version: u16,
80) -> impl Iterator<Item = GenericFilterConfigRef<'a>> {
81    #[cfg(not(test))]
82    let max_supported_version = MAX_SUPPORTED_VERSION;
83
84    let is_supported = project.version <= max_supported_version
85        && global.is_none_or(|gf| gf.version <= max_supported_version);
86
87    is_supported
88        .then(|| {
89            DynamicGenericFiltersConfigIter::new(&project.filters, global.map(|gc| &gc.filters))
90        })
91        .into_iter()
92        .flatten()
93}
94
95/// Iterator over the generic filters of the project and global configs.
96struct DynamicGenericFiltersConfigIter<'a> {
97    /// Generic project filters.
98    project: &'a GenericFiltersMap,
99    /// Index of the next filter in project configs to evaluate.
100    project_index: usize,
101    /// Generic global filters.
102    global: Option<&'a GenericFiltersMap>,
103    /// Index of the next filter in global configs to evaluate.
104    global_index: usize,
105}
106
107impl<'a> DynamicGenericFiltersConfigIter<'a> {
108    pub fn new(project: &'a GenericFiltersMap, global: Option<&'a GenericFiltersMap>) -> Self {
109        DynamicGenericFiltersConfigIter {
110            project,
111            project_index: 0,
112            global,
113            global_index: 0,
114        }
115    }
116}
117
118impl<'a> Iterator for DynamicGenericFiltersConfigIter<'a> {
119    type Item = GenericFilterConfigRef<'a>;
120
121    fn next(&mut self) -> Option<Self::Item> {
122        if let Some((id, filter)) = self.project.get_index(self.project_index) {
123            self.project_index += 1;
124            let merged = merge_filters(filter, self.global.and_then(|gf| gf.get(id)));
125            return Some(merged);
126        }
127
128        loop {
129            let (id, filter) = self.global?.get_index(self.global_index)?;
130            self.global_index += 1;
131            if !self.project.contains_key(id) {
132                return Some(filter.into());
133            }
134        }
135    }
136}
137
138impl FusedIterator for DynamicGenericFiltersConfigIter<'_> {}
139
140/// Merges the two filters with the same id, prioritizing values from the primary.
141///
142/// It's assumed both filters share the same id. The returned filter will have
143/// the primary filter's ID.
144fn merge_filters<'a>(
145    primary: &'a GenericFilterConfig,
146    secondary: Option<&'a GenericFilterConfig>,
147) -> GenericFilterConfigRef<'a> {
148    GenericFilterConfigRef {
149        id: primary.id.as_str(),
150        is_enabled: primary.is_enabled,
151        condition: primary
152            .condition
153            .as_ref()
154            .or(secondary.and_then(|filter| filter.condition.as_ref())),
155    }
156}
157
158#[derive(Debug, Default, PartialEq)]
159struct GenericFilterConfigRef<'a> {
160    id: &'a str,
161    is_enabled: bool,
162    condition: Option<&'a RuleCondition>,
163}
164
165impl<'a> From<&'a GenericFilterConfig> for GenericFilterConfigRef<'a> {
166    fn from(value: &'a GenericFilterConfig) -> Self {
167        GenericFilterConfigRef {
168            id: value.id.as_str(),
169            is_enabled: value.is_enabled,
170            condition: value.condition.as_ref(),
171        }
172    }
173}
174
175#[cfg(test)]
176mod tests {
177
178    use super::*;
179
180    use relay_event_schema::protocol::{Event, LenientString};
181    use relay_protocol::{Annotated, FromValue as _};
182
183    fn mock_filters() -> GenericFiltersMap {
184        vec![
185            GenericFilterConfig {
186                id: "firstReleases".to_string(),
187                is_enabled: true,
188                condition: Some(RuleCondition::eq("event.release", "1.0")),
189            },
190            GenericFilterConfig {
191                id: "helloTransactions".to_string(),
192                is_enabled: true,
193                condition: Some(RuleCondition::eq("event.transaction", "/hello")),
194            },
195        ]
196        .into()
197    }
198
199    #[test]
200    fn test_should_filter_match_rules() {
201        let config = GenericFiltersConfig {
202            version: 1,
203            filters: mock_filters(),
204        };
205
206        // Matching first rule.
207        let event = Event {
208            release: Annotated::new(LenientString("1.0".to_string())),
209            ..Default::default()
210        };
211        assert_eq!(
212            should_filter(&event, &config, None),
213            Err(FilterStatKey::GenericFilter("firstReleases".to_string()))
214        );
215
216        // Matching second rule.
217        let event = Event {
218            transaction: Annotated::new("/hello".to_string()),
219            ..Default::default()
220        };
221        assert_eq!(
222            should_filter(&event, &config, None),
223            Err(FilterStatKey::GenericFilter(
224                "helloTransactions".to_string()
225            ))
226        );
227    }
228
229    #[test]
230    fn test_should_filter_fifo_match_rules() {
231        let config = GenericFiltersConfig {
232            version: 1,
233            filters: mock_filters(),
234        };
235
236        // Matching both rules (first is taken).
237        let event = Event {
238            release: Annotated::new(LenientString("1.0".to_string())),
239            transaction: Annotated::new("/hello".to_string()),
240            ..Default::default()
241        };
242        assert_eq!(
243            should_filter(&event, &config, None),
244            Err(FilterStatKey::GenericFilter("firstReleases".to_string()))
245        );
246    }
247
248    #[test]
249    fn test_should_filter_match_no_rules() {
250        let config = GenericFiltersConfig {
251            version: 1,
252            filters: mock_filters(),
253        };
254
255        // Matching no rule.
256        let event = Event {
257            transaction: Annotated::new("/world".to_string()),
258            ..Default::default()
259        };
260        assert_eq!(should_filter(&event, &config, None), Ok(()));
261    }
262
263    #[test]
264    fn test_should_filter_with_higher_config_version() {
265        let config = GenericFiltersConfig {
266            // We simulate receiving a higher configuration version, which we don't support.
267            version: MAX_SUPPORTED_VERSION + 1,
268            filters: mock_filters(),
269        };
270
271        let event = Event {
272            release: Annotated::new(LenientString("1.0".to_string())),
273            transaction: Annotated::new("/hello".to_string()),
274            ..Default::default()
275        };
276        assert_eq!(should_filter(&event, &config, None), Ok(()));
277    }
278
279    #[test]
280    fn test_should_filter_from_global_filters() {
281        let project = GenericFiltersConfig {
282            version: 1,
283            filters: vec![GenericFilterConfig {
284                id: "firstReleases".to_string(),
285                is_enabled: true,
286                condition: Some(RuleCondition::eq("event.release", "1.0")),
287            }]
288            .into(),
289        };
290
291        let global = GenericFiltersConfig {
292            version: 1,
293            filters: vec![GenericFilterConfig {
294                id: "helloTransactions".to_string(),
295                is_enabled: true,
296                condition: Some(RuleCondition::eq("event.transaction", "/hello")),
297            }]
298            .into(),
299        };
300
301        let event = Event {
302            transaction: Annotated::new("/hello".to_string()),
303            ..Default::default()
304        };
305
306        assert_eq!(
307            should_filter(&event, &project, Some(&global)),
308            Err(FilterStatKey::GenericFilter(
309                "helloTransactions".to_string()
310            ))
311        );
312    }
313
314    fn empty_filter() -> GenericFiltersConfig {
315        GenericFiltersConfig {
316            version: 1,
317            filters: GenericFiltersMap::new(),
318        }
319    }
320
321    /// Returns a complete and enabled [`GenericFiltersConfig`].
322    fn enabled_filter(id: &str) -> GenericFiltersConfig {
323        GenericFiltersConfig {
324            version: 1,
325            filters: vec![GenericFilterConfig {
326                id: id.to_owned(),
327                is_enabled: true,
328                condition: Some(RuleCondition::eq("event.exceptions", "myError")),
329            }]
330            .into(),
331        }
332    }
333
334    /// Returns an enabled flag of a [`GenericFiltersConfig`].
335    fn enabled_flag_filter(id: &str) -> GenericFiltersConfig {
336        GenericFiltersConfig {
337            version: 1,
338            filters: vec![GenericFilterConfig {
339                id: id.to_owned(),
340                is_enabled: true,
341                condition: None,
342            }]
343            .into(),
344        }
345    }
346
347    /// Returns a complete but disabled [`GenericFiltersConfig`].
348    fn disabled_filter(id: &str) -> GenericFiltersConfig {
349        GenericFiltersConfig {
350            version: 1,
351            filters: vec![GenericFilterConfig {
352                id: id.to_owned(),
353                is_enabled: false,
354                condition: Some(RuleCondition::eq("event.exceptions", "myError")),
355            }]
356            .into(),
357        }
358    }
359
360    /// Returns a disabled flag of a [`GenericFiltersConfig`].
361    fn disabled_flag_filter(id: &str) -> GenericFiltersConfig {
362        GenericFiltersConfig {
363            version: 1,
364            filters: vec![GenericFilterConfig {
365                id: id.to_owned(),
366                is_enabled: false,
367                condition: None,
368            }]
369            .into(),
370        }
371    }
372
373    #[test]
374    fn test_no_combined_when_unsupported_project_version() {
375        let mut project = enabled_filter("unsupported-project");
376        project.version = 2;
377        let global = enabled_filter("supported-global");
378        assert!(merge_generic_filters(&project, Some(&global), 1).eq(None.into_iter()));
379    }
380
381    #[test]
382    fn test_no_combined_when_unsupported_project_version_no_global() {
383        let mut project = enabled_filter("unsupported-project");
384        project.version = 2;
385        assert!(merge_generic_filters(&project, None, 1).eq(None.into_iter()));
386    }
387
388    #[test]
389    fn test_no_combined_when_unsupported_global_version() {
390        let project = enabled_filter("supported-project");
391        let mut global = enabled_filter("unsupported-global");
392        global.version = 2;
393        assert!(merge_generic_filters(&project, Some(&global), 1).eq(None.into_iter()));
394    }
395
396    #[test]
397    fn test_no_combined_when_unsupported_project_and_global_version() {
398        let mut project = enabled_filter("unsupported-project");
399        project.version = 2;
400        let mut global = enabled_filter("unsupported-global");
401        global.version = 2;
402        assert!(merge_generic_filters(&project, Some(&global), 1).eq(None.into_iter()));
403    }
404
405    #[test]
406    fn test_both_combined_when_supported_project_and_global_version() {
407        let project = enabled_filter("supported-project");
408        let global = enabled_filter("supported-global");
409        assert!(
410            merge_generic_filters(&project, Some(&global), 1).eq([
411                project.filters.first().unwrap().1.into(),
412                global.filters.first().unwrap().1.into()
413            ]
414            .into_iter())
415        );
416    }
417
418    #[test]
419    fn test_project_combined_when_duplicated_filter_project_and_global() {
420        let project = enabled_filter("filter");
421        let global = enabled_filter("filter");
422        assert!(
423            merge_generic_filters(&project, Some(&global), 1).eq([project
424                .filters
425                .first()
426                .unwrap()
427                .1
428                .into()]
429            .into_iter())
430        );
431    }
432
433    #[test]
434    fn test_no_combined_when_empty_project_and_global() {
435        let project = empty_filter();
436        let global = empty_filter();
437        assert!(merge_generic_filters(&project, Some(&global), 1).eq(None.into_iter()));
438    }
439
440    #[test]
441    fn test_global_combined_when_empty_project_disabled_global_filter() {
442        let project = empty_filter();
443        let global = disabled_filter("disabled-global");
444        assert!(
445            merge_generic_filters(&project, Some(&global), 1).eq([global
446                .filters
447                .first()
448                .unwrap()
449                .1
450                .into()]
451            .into_iter())
452        );
453    }
454
455    #[test]
456    fn test_global_combined_when_empty_project_enabled_global_filters() {
457        let project = empty_filter();
458        let global = enabled_filter("enabled-global");
459        assert!(
460            merge_generic_filters(&project, Some(&global), 1).eq([global
461                .filters
462                .first()
463                .unwrap()
464                .1
465                .into()]
466            .into_iter())
467        );
468    }
469
470    #[test]
471    fn test_global_combined_when_empty_project_enabled_flag_global() {
472        let project = empty_filter();
473        let global = enabled_flag_filter("skip");
474        assert!(
475            merge_generic_filters(&project, Some(&global), 1).eq([global
476                .filters
477                .first()
478                .unwrap()
479                .1
480                .into()])
481        );
482    }
483
484    #[test]
485    fn test_project_combined_when_disabled_project_empty_global() {
486        let project = disabled_filter("disabled-project");
487        let global = empty_filter();
488        assert!(
489            merge_generic_filters(&project, Some(&global), 1).eq([project
490                .filters
491                .first()
492                .unwrap()
493                .1
494                .into()]
495            .into_iter())
496        );
497    }
498
499    #[test]
500    fn test_project_combined_when_disabled_project_missing_global() {
501        let project = disabled_filter("disabled-project");
502        assert!(
503            merge_generic_filters(&project, None, 1).eq([project
504                .filters
505                .first()
506                .unwrap()
507                .1
508                .into(),]
509            .into_iter())
510        );
511    }
512
513    #[test]
514    fn test_both_combined_when_different_disabled_project_and_global() {
515        let project = disabled_filter("disabled-project");
516        let global = disabled_filter("disabled-global");
517        assert!(merge_generic_filters(&project, Some(&global), 1).eq([
518            project.filters.first().unwrap().1.into(),
519            global.filters.first().unwrap().1.into()
520        ]));
521    }
522
523    #[test]
524    fn test_project_combined_duplicated_disabled_project_and_global() {
525        let project = disabled_filter("filter");
526        let global = disabled_filter("filter");
527        assert!(
528            merge_generic_filters(&project, Some(&global), 1).eq([project
529                .filters
530                .first()
531                .unwrap()
532                .1
533                .into()])
534        );
535    }
536
537    #[test]
538    fn test_merged_combined_when_disabled_project_enabled_global() {
539        let project = disabled_filter("filter");
540        let global = enabled_filter("filter");
541        let expected = &GenericFilterConfig {
542            id: "filter".to_owned(),
543            is_enabled: false,
544            condition: global.filters.first().unwrap().1.condition.clone(),
545        };
546        assert!(
547            merge_generic_filters(&project, Some(&global), 1).eq([expected.into()].into_iter())
548        );
549    }
550
551    #[test]
552    fn test_no_combined_when_enabled_flag_project_empty_global() {
553        let project = enabled_flag_filter("filter");
554        let global = empty_filter();
555        assert!(
556            merge_generic_filters(&project, Some(&global), 1).eq([project
557                .filters
558                .first()
559                .unwrap()
560                .1
561                .into()]
562            .into_iter())
563        );
564    }
565
566    #[test]
567    fn test_project_combined_when_enabled_flag_project_missing_global() {
568        let project = enabled_flag_filter("filter");
569        assert!(
570            merge_generic_filters(&project, None, 1).eq([project
571                .filters
572                .first()
573                .unwrap()
574                .1
575                .into()]
576            .into_iter())
577        );
578    }
579
580    #[test]
581    fn test_project_combined_when_disabled_flag_project_empty_global() {
582        let project = disabled_flag_filter("filter");
583        let global = empty_filter();
584        assert!(
585            merge_generic_filters(&project, Some(&global), 1).eq([project
586                .filters
587                .first()
588                .unwrap()
589                .1
590                .into()])
591        );
592    }
593
594    #[test]
595    fn test_project_combined_when_disabled_flag_project_missing_global() {
596        let project = disabled_flag_filter("filter");
597        assert!(
598            merge_generic_filters(&project, None, 1).eq([project
599                .filters
600                .first()
601                .unwrap()
602                .1
603                .into()])
604        );
605    }
606
607    #[test]
608    fn test_project_combined_when_enabled_project_empty_global() {
609        let project = enabled_filter("enabled-project");
610        let global = empty_filter();
611        assert!(
612            merge_generic_filters(&project, Some(&global), 1).eq([project
613                .filters
614                .first()
615                .unwrap()
616                .1
617                .into()]
618            .into_iter())
619        );
620    }
621
622    #[test]
623    fn test_project_combined_when_enabled_project_missing_global() {
624        let project = enabled_filter("enabled-project");
625        assert!(
626            merge_generic_filters(&project, None, 1).eq([project
627                .filters
628                .first()
629                .unwrap()
630                .1
631                .into()]
632            .into_iter())
633        );
634    }
635
636    #[test]
637    fn test_merged_combined_when_enabled_flag_project_disabled_global() {
638        let project = enabled_flag_filter("filter");
639        let global = disabled_filter("filter");
640        let expected = &GenericFilterConfig {
641            id: "filter".to_owned(),
642            is_enabled: true,
643            condition: global.filters.first().unwrap().1.condition.clone(),
644        };
645        assert!(
646            merge_generic_filters(&project, Some(&global), 1).eq([expected.into()].into_iter())
647        );
648    }
649
650    #[test]
651    fn test_global_combined_when_disabled_flag_project_disabled_global() {
652        let project = disabled_flag_filter("filter");
653        let global = disabled_filter("filter");
654        assert!(
655            merge_generic_filters(&project, Some(&global), 1).eq([global
656                .filters
657                .first()
658                .unwrap()
659                .1
660                .into()])
661        );
662    }
663
664    #[test]
665    fn test_project_combined_when_enabled_project_disabled_global() {
666        let project = enabled_filter("filter");
667        let global = disabled_filter("filter");
668        assert!(
669            merge_generic_filters(&project, Some(&global), 1).eq([project
670                .filters
671                .first()
672                .unwrap()
673                .1
674                .into()]
675            .into_iter())
676        );
677    }
678
679    #[test]
680    fn test_global_combined_when_enabled_flag_project_enabled_global() {
681        let project = enabled_flag_filter("filter");
682        let global = enabled_filter("filter");
683        assert!(
684            merge_generic_filters(&project, Some(&global), 1).eq([global
685                .filters
686                .first()
687                .unwrap()
688                .1
689                .into()]
690            .into_iter())
691        );
692    }
693
694    #[test]
695    fn test_merged_combined_when_disabled_flag_project_enabled_global() {
696        let project = disabled_flag_filter("filter");
697        let global = enabled_filter("filter");
698        let expected = &GenericFilterConfig {
699            id: "filter".to_owned(),
700            is_enabled: false,
701            condition: global.filters.first().unwrap().1.condition.clone(),
702        };
703        assert!(
704            merge_generic_filters(&project, Some(&global), 1).eq([expected.into()].into_iter())
705        );
706    }
707
708    #[test]
709    fn test_project_combined_when_enabled_project_enabled_flag_global() {
710        let project = enabled_filter("filter");
711        let global = enabled_flag_filter("filter");
712        assert!(
713            merge_generic_filters(&project, Some(&global), 1).eq([project
714                .filters
715                .first()
716                .unwrap()
717                .1
718                .into()]
719            .into_iter())
720        );
721    }
722
723    #[test]
724    fn test_project_combined_when_enabled_flags_project_and_global() {
725        let project = enabled_flag_filter("filter");
726        let global = enabled_flag_filter("filter");
727        assert!(
728            merge_generic_filters(&project, Some(&global), 1).eq([project
729                .filters
730                .first()
731                .unwrap()
732                .1
733                .into()]
734            .into_iter())
735        );
736    }
737
738    #[test]
739    fn test_multiple_combined_filters() {
740        let project = GenericFiltersConfig {
741            version: 1,
742            filters: vec![
743                GenericFilterConfig {
744                    id: "0".to_owned(),
745                    is_enabled: true,
746                    condition: Some(RuleCondition::eq("event.exceptions", "myError")),
747                },
748                GenericFilterConfig {
749                    id: "1".to_owned(),
750                    is_enabled: true,
751                    condition: None,
752                },
753                GenericFilterConfig {
754                    id: "2".to_owned(),
755                    is_enabled: true,
756                    condition: Some(RuleCondition::eq("event.exceptions", "myError")),
757                },
758            ]
759            .into(),
760        };
761        let global = GenericFiltersConfig {
762            version: 1,
763            filters: vec![
764                GenericFilterConfig {
765                    id: "1".to_owned(),
766                    is_enabled: false,
767                    condition: Some(RuleCondition::eq("event.exceptions", "myOtherError")),
768                },
769                GenericFilterConfig {
770                    id: "3".to_owned(),
771                    is_enabled: false,
772                    condition: Some(RuleCondition::eq("event.exceptions", "myLastError")),
773                },
774            ]
775            .into(),
776        };
777
778        let expected0 = &project.filters[0];
779        let expected1 = &GenericFilterConfig {
780            id: "1".to_owned(),
781            is_enabled: true,
782            condition: Some(RuleCondition::eq("event.exceptions", "myOtherError")),
783        };
784        let expected2 = &project.filters[2];
785        let expected3 = &global.filters[1];
786
787        assert!(
788            merge_generic_filters(&project, Some(&global), 1).eq([
789                expected0.into(),
790                expected1.into(),
791                expected2.into(),
792                expected3.into()
793            ]
794            .into_iter())
795        );
796    }
797
798    #[test]
799    fn test_os_name_not_filter() {
800        let config = GenericFiltersConfig {
801            version: 1,
802            filters: vec![GenericFilterConfig {
803                id: "os_name".to_owned(),
804                is_enabled: true,
805                condition: Some(RuleCondition::eq("event.contexts.os.name", "fooBar").negate()),
806            }]
807            .into(),
808        };
809
810        let cases = [("fooBar", false), ("foobar", true), ("other", true)];
811        for (name, filters) in cases {
812            let event = Event::from_value(
813                serde_json::json!({
814                    "contexts": {
815                        "os": {
816                            "name": name,
817                        },
818                    },
819                })
820                .into(),
821            );
822
823            let expected = if filters {
824                Err(FilterStatKey::GenericFilter("os_name".to_string()))
825            } else {
826                Ok(())
827            };
828
829            assert_eq!(
830                should_filter(event.value().unwrap(), &config, None),
831                expected
832            );
833        }
834    }
835}