relay_event_schema/protocol/contexts/
app.rs

1use relay_protocol::{Annotated, Empty, FromValue, IntoValue, Object, Value};
2
3use crate::processor::ProcessValue;
4use crate::protocol::LenientString;
5
6/// Application information.
7///
8/// App context describes the application. As opposed to the runtime, this is the actual
9/// application that was running and carries metadata about the current session.
10#[derive(Clone, Debug, Default, PartialEq, Empty, FromValue, IntoValue, ProcessValue)]
11pub struct AppContext {
12    /// Start time of the app.
13    ///
14    /// Formatted UTC timestamp when the user started the application.
15    #[metastructure(pii = "maybe")]
16    pub app_start_time: Annotated<String>,
17
18    /// Application-specific device identifier.
19    #[metastructure(pii = "maybe")]
20    pub device_app_hash: Annotated<String>,
21
22    /// String identifying the kind of build. For example, `testflight`.
23    pub build_type: Annotated<String>,
24
25    /// Version-independent application identifier, often a dotted bundle ID.
26    pub app_identifier: Annotated<String>,
27
28    /// Application name as it appears on the platform.
29    pub app_name: Annotated<String>,
30
31    /// Application version as it appears on the platform.
32    pub app_version: Annotated<String>,
33
34    /// Internal build ID as it appears on the platform.
35    pub app_build: Annotated<LenientString>,
36
37    /// Amount of memory used by the application in bytes.
38    pub app_memory: Annotated<u64>,
39
40    /// A flag indicating whether the app is in foreground or not. An app is in foreground when it's visible to the user.
41    pub in_foreground: Annotated<bool>,
42
43    /// The names of the currently visible views.
44    #[metastructure(skip_serialization = "empty")]
45    pub view_names: Annotated<Vec<Annotated<String>>>,
46
47    /// Additional arbitrary fields for forwards compatibility.
48    #[metastructure(additional_properties, retain = true, pii = "maybe")]
49    pub other: Object<Value>,
50}
51
52impl super::DefaultContext for AppContext {
53    fn default_key() -> &'static str {
54        "app"
55    }
56
57    fn from_context(context: super::Context) -> Option<Self> {
58        match context {
59            super::Context::App(c) => Some(*c),
60            _ => None,
61        }
62    }
63
64    fn cast(context: &super::Context) -> Option<&Self> {
65        match context {
66            super::Context::App(c) => Some(c),
67            _ => None,
68        }
69    }
70
71    fn cast_mut(context: &mut super::Context) -> Option<&mut Self> {
72        match context {
73            super::Context::App(c) => Some(c),
74            _ => None,
75        }
76    }
77
78    fn into_context(self) -> super::Context {
79        super::Context::App(Box::new(self))
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86    use crate::protocol::Context;
87
88    #[test]
89    fn test_app_context_roundtrip() {
90        let json = r#"{
91  "app_start_time": "2018-02-08T22:21:57Z",
92  "device_app_hash": "4c793e3776474877ae30618378e9662a",
93  "build_type": "testflight",
94  "app_identifier": "foo.bar.baz",
95  "app_name": "Baz App",
96  "app_version": "1.0",
97  "app_build": "100001",
98  "app_memory": 22883948,
99  "in_foreground": true,
100  "view_names": [
101    "FooViewController",
102    "BarViewController"
103  ],
104  "other": "value",
105  "type": "app"
106}"#;
107        let context = Annotated::new(Context::App(Box::new(AppContext {
108            app_start_time: Annotated::new("2018-02-08T22:21:57Z".to_string()),
109            device_app_hash: Annotated::new("4c793e3776474877ae30618378e9662a".to_string()),
110            build_type: Annotated::new("testflight".to_string()),
111            app_identifier: Annotated::new("foo.bar.baz".to_string()),
112            app_name: Annotated::new("Baz App".to_string()),
113            app_version: Annotated::new("1.0".to_string()),
114            app_build: Annotated::new("100001".to_string().into()),
115            app_memory: Annotated::new(22883948),
116            in_foreground: Annotated::new(true),
117            view_names: Annotated::new(vec![
118                Annotated::new("FooViewController".to_string()),
119                Annotated::new("BarViewController".to_string()),
120            ]),
121            other: {
122                let mut map = Object::new();
123                map.insert(
124                    "other".to_string(),
125                    Annotated::new(Value::String("value".to_string())),
126                );
127                map
128            },
129        })));
130
131        assert_eq!(context, Annotated::from_json(json).unwrap());
132        assert_eq!(json, context.to_json_pretty().unwrap());
133    }
134}