relay_filter/
releases.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
//! Implements event filtering based on application release version.
//!
//! A user may configure the server to ignore certain application releases
//! (known old bad releases) and Sentry will ignore events originating from
//! clients with the specified release.

use crate::{FilterStatKey, Filterable, ReleasesFilterConfig};

/// Filters events generated by known problematic SDK clients.
pub fn should_filter<F>(item: &F, config: &ReleasesFilterConfig) -> Result<(), FilterStatKey>
where
    F: Filterable,
{
    if let Some(release) = item.release() {
        if config.releases.is_match(release) {
            return Err(FilterStatKey::ReleaseVersion);
        }
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use relay_event_schema::protocol::{Event, LenientString, Span, SpanData};
    use relay_protocol::Annotated;

    use super::*;

    fn get_event_for_release(release: &str) -> Event {
        Event {
            release: Annotated::from(LenientString::from(release.to_string())),
            ..Event::default()
        }
    }

    fn get_span_for_release(release: &str) -> Span {
        Span {
            data: Annotated::new(SpanData {
                release: Annotated::from(LenientString::from(release.to_string())),
                ..Default::default()
            }),
            ..Default::default()
        }
    }

    #[test]
    fn test_release_filtering() {
        let examples = &[
            //simple matches
            ("1.2.3", &["1.3.0", "1.2.3", "1.3.1"][..], true),
            ("1.2.3", &["1.3.0", "1.3.1", "1.2.3"], true),
            ("1.2.3", &["1.2.3", "1.3.0", "1.3.1"], true),
            //pattern matches
            ("1.2.3", &["1.3.0", "1.2.*", "1.3.1"], true),
            ("1.2.3", &["1.3.0", "1.3.*", "1.*"], true),
            ("1.2.3", &["*", "1.3.0", "1.3.*"], true),
            //simple non match
            ("1.2.3", &["1.3.0", "1.2.4", "1.3.1"], false),
            //pattern non match
            ("1.2.3", &["1.4.0", "1.3.*", "3.*"], false),
            //sentry compatibility tests
            ("1.2.3", &[], false),
            ("1.2.3", &["1.1.1", "1.1.2", "1.3.1"], false),
            ("1.2.3", &["1.2.3"], true),
            ("1.2.3", &["1.2.*", "1.3.0", "1.3.1"], true),
            ("1.2.3", &["1.3.0", "1.*", "1.3.1"], true),
        ];

        for &(release, blocked_releases, expected) in examples {
            let event = get_event_for_release(release);
            let span = get_span_for_release(release);

            let config = ReleasesFilterConfig {
                releases: blocked_releases.iter().map(|&r| r.to_owned()).collect(),
            };

            let actual = should_filter(&event, &config) != Ok(());
            assert_eq!(
                actual,
                expected,
                "Release {release} should have {} been filtered by {blocked_releases:?}",
                if expected { "" } else { "not" },
            );

            let actual = should_filter(&span, &config) != Ok(());
            assert_eq!(
                actual,
                expected,
                "Release {release} should have {} been filtered by {blocked_releases:?}",
                if expected { "" } else { "not" },
            );
        }
    }
}