1use relay_conventions::attributes::{SENTRY__DESCRIPTION, SENTRY__OP, SENTRY__ORIGIN};
2use relay_conventions::name::name_for_op_and_attributes;
3use relay_event_schema::protocol::Attributes;
4
5pub fn name_for_attributes(attributes: &Attributes) -> Option<String> {
16 let origin = attributes
17 .get_value(SENTRY__ORIGIN)
18 .and_then(|o| o.as_str());
19 let description = attributes
20 .get_value(SENTRY__DESCRIPTION)
21 .and_then(|d| d.as_str());
22
23 if let Some(description) = description
24 && origin == Some("manual")
25 {
26 return Some(description.to_owned());
27 }
28
29 let op = attributes.get_value(SENTRY__OP)?.as_str()?;
30 Some(name_for_op_and_attributes(op, |name| attributes.get_value(name)).unwrap_or(op.to_owned()))
31}
32
33#[cfg(test)]
34mod tests {
35 use relay_protocol::Annotated;
36
37 use super::*;
38
39 #[test]
40 fn test_attributes_falls_back_to_op_when_no_templates_defined() {
41 let attributes = Attributes::from([(
42 "sentry.op".to_owned(),
43 Annotated::new("foo".to_owned().into()),
44 )]);
45
46 assert_eq!(name_for_attributes(&attributes), Some("foo".to_owned()));
47 }
48
49 #[test]
50 fn test_attributes_uses_the_first_matching_template() {
51 let attributes = Attributes::from([
52 (
53 "sentry.op".to_owned(),
54 Annotated::new("db".to_owned().into()),
55 ),
56 (
57 "db.query.summary".to_owned(),
58 Annotated::new("SELECT users".to_owned().into()),
59 ),
60 (
61 "db.operation.name".to_owned(),
62 Annotated::new("INSERT".to_owned().into()),
63 ),
64 (
65 "db.collection.name".to_owned(),
66 Annotated::new("widgets".to_owned().into()),
67 ),
68 ]);
69
70 assert_eq!(
71 name_for_attributes(&attributes),
72 Some("SELECT users".to_owned())
73 );
74 }
75
76 #[test]
77 fn test_attributes_uses_fallback_templates_when_data_is_missing() {
78 let attributes = Attributes::from([
79 (
80 "sentry.op".to_owned(),
81 Annotated::new("db".to_owned().into()),
82 ),
83 (
84 "db.operation.name".to_owned(),
85 Annotated::new("INSERT".to_owned().into()),
86 ),
87 (
88 "db.collection.name".to_owned(),
89 Annotated::new("widgets".to_owned().into()),
90 ),
91 ]);
92
93 assert_eq!(
94 name_for_attributes(&attributes),
95 Some("INSERT widgets".to_owned())
96 );
97 }
98
99 #[test]
100 fn test_attributes_falls_back_to_hardcoded_name_when_nothing_matches() {
101 let attributes = Attributes::from([(
102 "sentry.op".to_owned(),
103 Annotated::new("db".to_owned().into()),
104 )]);
105
106 assert_eq!(
107 name_for_attributes(&attributes),
108 Some("Database operation".to_owned())
109 );
110 }
111
112 #[test]
113 fn test_manual_spans_use_description_v2() {
114 let attributes = Attributes::from([
115 (
116 "sentry.origin".to_owned(),
117 Annotated::new("manual".to_owned().into()),
118 ),
119 (
120 "sentry.description".to_owned(),
121 Annotated::new("Custom name".to_owned().into()),
122 ),
123 (
124 "sentry.op".to_owned(),
125 Annotated::new("db".to_owned().into()),
126 ),
127 (
128 "db.query.summary".to_owned(),
129 Annotated::new("SELECT users".to_owned().into()),
130 ),
131 (
132 "db.operation.name".to_owned(),
133 Annotated::new("INSERT".to_owned().into()),
134 ),
135 (
136 "db.collection.name".to_owned(),
137 Annotated::new("widgets".to_owned().into()),
138 ),
139 ]);
140
141 assert_eq!(
142 name_for_attributes(&attributes),
143 Some("Custom name".to_owned())
144 );
145 }
146}