Skip to content

Commit 995a0a8

Browse files
authored
feat: Basic attachment support (#466)
1 parent 6e777c2 commit 995a0a8

File tree

5 files changed

+91
-4
lines changed

5 files changed

+91
-4
lines changed

sentry-core/src/client.rs

+7
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,13 @@ impl Client {
278278
envelope.add_item(session_item);
279279
}
280280
}
281+
282+
if let Some(scope) = scope {
283+
for attachment in scope.attachments.iter().cloned() {
284+
envelope.add_item(attachment);
285+
}
286+
}
287+
281288
transport.send_envelope(envelope);
282289
return event_id;
283290
}

sentry-core/src/scope/real.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::fmt;
44
use std::sync::{Arc, Mutex, PoisonError, RwLock};
55

66
use crate::performance::TransactionOrSpan;
7-
use crate::protocol::{Breadcrumb, Context, Event, Level, User, Value};
7+
use crate::protocol::{Attachment, Breadcrumb, Context, Event, Level, User, Value};
88
use crate::session::Session;
99
use crate::Client;
1010

@@ -46,6 +46,7 @@ pub struct Scope {
4646
pub(crate) event_processors: Arc<Vec<EventProcessor>>,
4747
pub(crate) session: Arc<Mutex<Option<Session>>>,
4848
pub(crate) span: Arc<Option<TransactionOrSpan>>,
49+
pub(crate) attachments: Arc<Vec<Attachment>>,
4950
}
5051

5152
impl fmt::Debug for Scope {
@@ -218,6 +219,16 @@ impl Scope {
218219
Arc::make_mut(&mut self.event_processors).push(Arc::new(f));
219220
}
220221

222+
/// Adds an attachment to the scope
223+
pub fn add_attachment(&mut self, attachment: Attachment) {
224+
Arc::make_mut(&mut self.attachments).push(attachment);
225+
}
226+
227+
/// Clears attachments from the scope
228+
pub fn clear_attachments(&mut self) {
229+
Arc::make_mut(&mut self.attachments).clear();
230+
}
231+
221232
/// Applies the contained scoped data to fill an event.
222233
pub fn apply_to_event(&self, mut event: Event<'static>) -> Option<Event<'static>> {
223234
// TODO: event really should have an optional level

sentry-types/src/protocol/attachment.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,15 @@ impl AttachmentType {
3737
}
3838
}
3939

40-
#[derive(Clone, PartialEq)]
40+
#[derive(Clone, PartialEq, Default)]
4141
/// Represents an attachment item.
4242
pub struct Attachment {
4343
/// The actual attachment data.
4444
pub buffer: Vec<u8>,
4545
/// The filename of the attachment.
4646
pub filename: String,
47+
/// The Content Type of the attachment
48+
pub content_type: Option<String>,
4749
/// The special type of this attachment.
4850
pub ty: Option<AttachmentType>,
4951
}
@@ -56,10 +58,14 @@ impl Attachment {
5658
{
5759
writeln!(
5860
writer,
59-
r#"{{"type":"attachment","length":{length},"filename":"{filename}","attachment_type":"{at}"}}"#,
61+
r#"{{"type":"attachment","length":{length},"filename":"{filename}","attachment_type":"{at}","content_type":"{ct}"}}"#,
6062
filename = self.filename,
6163
length = self.buffer.len(),
62-
at = self.ty.unwrap_or_default().as_str()
64+
at = self.ty.unwrap_or_default().as_str(),
65+
ct = self
66+
.content_type
67+
.as_ref()
68+
.unwrap_or(&"application/octet-stream".to_string())
6369
)?;
6470

6571
writer.write_all(&self.buffer)?;
@@ -74,6 +80,7 @@ impl fmt::Debug for Attachment {
7480
f.debug_struct("Attachment")
7581
.field("buffer", &self.buffer.len())
7682
.field("filename", &self.filename)
83+
.field("content_type", &self.content_type)
7784
.field("type", &self.ty)
7885
.finish()
7986
}

sentry-types/src/protocol/envelope.rs

+34
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ impl From<Transaction<'static>> for EnvelopeItem {
6565
}
6666
}
6767

68+
impl From<Attachment> for EnvelopeItem {
69+
fn from(attachment: Attachment) -> Self {
70+
EnvelopeItem::Attachment(attachment)
71+
}
72+
}
73+
6874
/// An Iterator over the items of an Envelope.
6975
#[derive(Clone)]
7076
pub struct EnvelopeItemIter<'s> {
@@ -352,6 +358,34 @@ mod test {
352358
r#"{"event_id":"22d00b3f-d1b1-4b5d-8d20-49d138cd8a9c"}
353359
{"type":"transaction","length":200}
354360
{"event_id":"22d00b3fd1b14b5d8d2049d138cd8a9c","start_timestamp":1595256674.296,"spans":[{"span_id":"d42cee9fc3e74f5c","trace_id":"335e53d614474acc9f89e632b776cc28","start_timestamp":1595256674.296}]}
361+
"#
362+
)
363+
}
364+
365+
#[test]
366+
fn test_event_with_attachment() {
367+
let event_id = Uuid::parse_str("22d00b3f-d1b1-4b5d-8d20-49d138cd8a9c").unwrap();
368+
let timestamp = timestamp("2020-07-20T14:51:14.296Z");
369+
let event = Event {
370+
event_id,
371+
timestamp,
372+
..Default::default()
373+
};
374+
let mut envelope: Envelope = event.into();
375+
376+
envelope.add_item(Attachment {
377+
buffer: "some content".as_bytes().to_vec(),
378+
filename: "file.txt".to_string(),
379+
..Default::default()
380+
});
381+
382+
assert_eq!(
383+
to_str(envelope),
384+
r#"{"event_id":"22d00b3f-d1b1-4b5d-8d20-49d138cd8a9c"}
385+
{"type":"event","length":74}
386+
{"event_id":"22d00b3fd1b14b5d8d2049d138cd8a9c","timestamp":1595256674.296}
387+
{"type":"attachment","length":12,"filename":"file.txt","attachment_type":"event.attachment","content_type":"application/octet-stream"}
388+
some content
355389
"#
356390
)
357391
}

sentry/tests/test_basic.rs

+28
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use std::sync::atomic::{AtomicUsize, Ordering};
44
use std::sync::Arc;
55

6+
use sentry::protocol::{Attachment, EnvelopeItem};
67
use sentry::types::Uuid;
78

89
#[test]
@@ -173,3 +174,30 @@ fn test_attached_stacktrace() {
173174
.flat_map(|ev| ev.threads.into_iter().filter_map(|thrd| thrd.stacktrace));
174175
assert_eq!(stacktraces.count(), 3);
175176
}
177+
178+
#[test]
179+
fn test_attachment_sent_from_scope() {
180+
let envelopes = sentry::test::with_captured_envelopes(|| {
181+
sentry::with_scope(
182+
|scope| {
183+
scope.add_attachment(Attachment {
184+
buffer: vec![1, 2, 3, 4, 5, 6, 7, 8, 9],
185+
filename: "test-file.bin".to_string(),
186+
..Default::default()
187+
})
188+
},
189+
|| sentry::capture_message("test", sentry::Level::Error),
190+
);
191+
});
192+
193+
assert_eq!(envelopes.len(), 1);
194+
195+
let items = envelopes[0].items().collect::<Vec<_>>();
196+
197+
assert_eq!(items.len(), 2);
198+
assert!(matches!(items[1],
199+
EnvelopeItem::Attachment(attachment)
200+
if attachment.filename == *"test-file.bin"
201+
&& attachment.buffer == vec![1, 2, 3, 4, 5, 6, 7, 8, 9]
202+
));
203+
}

0 commit comments

Comments
 (0)