Skip to content

Commit 04de6af

Browse files
committed
fix: Allow for Contexts data type to be infered from the key value
1 parent 117f2d0 commit 04de6af

File tree

2 files changed

+125
-4
lines changed

2 files changed

+125
-4
lines changed

sentry-types/src/protocol/v7.rs

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ use std::str;
1616
use std::time::SystemTime;
1717

1818
use self::debugid::{CodeId, DebugId};
19-
use serde::Serializer;
20-
use serde::{Deserialize, Serialize};
19+
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
2120
use thiserror::Error;
2221
use url::Url;
2322
use uuid::Uuid;
@@ -1121,6 +1120,23 @@ impl Context {
11211120
Context::Other(..) => "unknown",
11221121
}
11231122
}
1123+
1124+
/// Returns the `Context` based on the type key and provided json payload.
1125+
pub fn from_type(
1126+
r#type: &str,
1127+
payload: serde_json::Value,
1128+
) -> Result<Context, serde_json::Error> {
1129+
Ok(match r#type {
1130+
"device" => Self::from(DeviceContext::deserialize(payload)?),
1131+
"os" => Self::from(OsContext::deserialize(payload)?),
1132+
"runtime" => Self::from(RuntimeContext::deserialize(payload)?),
1133+
"app" => Self::from(AppContext::deserialize(payload)?),
1134+
"browser" => Self::from(BrowserContext::deserialize(payload)?),
1135+
"trace" => Self::from(TraceContext::deserialize(payload)?),
1136+
"gpu" => Self::from(GpuContext::deserialize(payload)?),
1137+
_ => Context::Other(Map::deserialize(payload)?),
1138+
})
1139+
}
11241140
}
11251141

11261142
/// Optional device screen orientation
@@ -1477,6 +1493,41 @@ into_context!(Trace, TraceContext);
14771493
into_context!(Gpu, GpuContext);
14781494
into_context!(Profile, ProfileContext);
14791495

1496+
struct ContextsVisitor;
1497+
1498+
impl<'de> de::Visitor<'de> for ContextsVisitor {
1499+
type Value = Map<String, Context>;
1500+
1501+
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1502+
formatter.write_str("invalid `contexts` object provided")
1503+
}
1504+
1505+
fn visit_map<A>(self, mut access: A) -> Result<Self::Value, A::Error>
1506+
where
1507+
A: de::MapAccess<'de>,
1508+
{
1509+
let mut map: Map<String, Context> = Map::new();
1510+
1511+
while let Some((key, value)) = access.next_entry::<String, Value>()? {
1512+
match Context::from_type(&key, value) {
1513+
Ok(context) => {
1514+
map.insert(key, context);
1515+
}
1516+
Err(e) => return Err(de::Error::custom(e.to_string())),
1517+
}
1518+
}
1519+
1520+
Ok(map)
1521+
}
1522+
}
1523+
1524+
fn deserialize_contexts<'de, D>(deserializer: D) -> Result<Map<String, Context>, D::Error>
1525+
where
1526+
D: Deserializer<'de>,
1527+
{
1528+
deserializer.deserialize_map(ContextsVisitor {})
1529+
}
1530+
14801531
mod event {
14811532
use super::*;
14821533

@@ -1578,7 +1629,11 @@ pub struct Event<'a> {
15781629
#[serde(default, skip_serializing_if = "Option::is_none")]
15791630
pub request: Option<Request>,
15801631
/// Optional contexts.
1581-
#[serde(default, skip_serializing_if = "Map::is_empty")]
1632+
#[serde(
1633+
default,
1634+
skip_serializing_if = "Map::is_empty",
1635+
deserialize_with = "deserialize_contexts"
1636+
)]
15821637
pub contexts: Map<String, Context>,
15831638
/// List of breadcrumbs to send along.
15841639
#[serde(default, skip_serializing_if = "Values::is_empty")]
@@ -1943,7 +1998,11 @@ pub struct Transaction<'a> {
19431998
/// The collection of finished spans part of this transaction.
19441999
pub spans: Vec<Span>,
19452000
/// Optional contexts.
1946-
#[serde(default, skip_serializing_if = "Map::is_empty")]
2001+
#[serde(
2002+
default,
2003+
skip_serializing_if = "Map::is_empty",
2004+
deserialize_with = "deserialize_contexts"
2005+
)]
19472006
pub contexts: Map<String, Context>,
19482007
/// Optionally HTTP request data to be sent along.
19492008
#[serde(default, skip_serializing_if = "Option::is_none")]

sentry-types/tests/test_protocol_v7.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1379,6 +1379,68 @@ mod test_contexts {
13791379
\"contexts\":{\"other\":{\"type\":\"unknown\",\"aha\":\"oho\"}}}"
13801380
);
13811381
}
1382+
1383+
#[test]
1384+
fn test_typeless_context() {
1385+
let payload = r#"
1386+
{
1387+
"event_id": "d43e86c96e424a93a4fbda156dd17341",
1388+
"timestamp": 1514103120,
1389+
"contexts": {
1390+
"device": {
1391+
"name": "iphone",
1392+
"family": "iphone",
1393+
"model": "iphone7,3",
1394+
"model_id": "AH223",
1395+
"arch": "arm64"
1396+
},
1397+
"os": { "name": "iOS", "version": "11.4.2", "build": "ADSA23" },
1398+
"runtime": { "name": "magicvm", "version": "5.3" },
1399+
"app": {
1400+
"app_start_time": "2018-02-08T22:21:57Z",
1401+
"build_type": "testflight",
1402+
"app_name": "Baz App",
1403+
"app_version": "1.0",
1404+
"app_build": "100001"
1405+
},
1406+
"browser": { "name": "Chrome", "version": "59.0.3071" },
1407+
"gpu": {
1408+
"name": "AMD Radeon Pro 560",
1409+
"vendor_name": "Apple",
1410+
"memory_size": 4096
1411+
},
1412+
"trace": {
1413+
"trace_id": "12312012123120121231201212312012",
1414+
"span_id": "0415201309082013",
1415+
"parent_span_id": null,
1416+
"description": "<OrganizationContext>",
1417+
"op": "http.server"
1418+
},
1419+
"random": { "aha": "oho" }
1420+
}
1421+
}
1422+
"#;
1423+
1424+
let event: v7::Event = serde_json::from_str(payload).unwrap();
1425+
let ctx = event.contexts;
1426+
1427+
assert!(ctx.contains_key("device"));
1428+
assert_eq!(ctx.get("device").unwrap().type_name(), "device");
1429+
assert!(ctx.contains_key("os"));
1430+
assert_eq!(ctx.get("os").unwrap().type_name(), "os");
1431+
assert!(ctx.contains_key("runtime"));
1432+
assert_eq!(ctx.get("runtime").unwrap().type_name(), "runtime");
1433+
assert!(ctx.contains_key("app"));
1434+
assert_eq!(ctx.get("app").unwrap().type_name(), "app");
1435+
assert!(ctx.contains_key("browser"));
1436+
assert_eq!(ctx.get("browser").unwrap().type_name(), "browser");
1437+
assert!(ctx.contains_key("trace"));
1438+
assert_eq!(ctx.get("trace").unwrap().type_name(), "trace");
1439+
assert!(ctx.contains_key("gpu"));
1440+
assert_eq!(ctx.get("gpu").unwrap().type_name(), "gpu");
1441+
assert!(ctx.contains_key("random"));
1442+
assert_eq!(ctx.get("random").unwrap().type_name(), "unknown");
1443+
}
13821444
}
13831445

13841446
#[test]

0 commit comments

Comments
 (0)