Skip to content

Commit 25a894d

Browse files
authored
feat: Added internal bool CaptureAttachment to Hub (#4357)
1 parent a09c27d commit 25a894d

5 files changed

Lines changed: 122 additions & 0 deletions

File tree

src/Sentry/Internal/Hub.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Sentry.Extensibility;
22
using Sentry.Infrastructure;
33
using Sentry.Integrations;
4+
using Sentry.Internal.Extensions;
45
using Sentry.Protocol.Envelopes;
56
using Sentry.Protocol.Metrics;
67

@@ -757,6 +758,28 @@ public SentryId CaptureCheckIn(
757758
return SentryId.Empty;
758759
}
759760

761+
// Internal capture method that allows the Unity SDK to send attachments after an already captured event.
762+
// Kept internal as the preferred way of adding attachments is either on the scope or directly on the event.
763+
// See https://develop.sentry.dev/sdk/data-model/envelope-items/#attachment
764+
internal bool CaptureAttachment(SentryId eventId, SentryAttachment attachment)
765+
{
766+
if (!IsEnabled || eventId == SentryId.Empty || attachment.IsNull())
767+
{
768+
return false;
769+
}
770+
771+
try
772+
{
773+
var envelope = Envelope.FromAttachment(eventId, attachment, _options.DiagnosticLogger);
774+
return CaptureEnvelope(envelope);
775+
}
776+
catch (Exception e)
777+
{
778+
_options.LogError(e, "Failure to capture attachment");
779+
return false;
780+
}
781+
}
782+
760783
public async Task FlushAsync(TimeSpan timeout)
761784
{
762785
try

src/Sentry/Protocol/Envelopes/Envelope.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,12 @@ internal static Envelope FromClientReport(ClientReport clientReport)
445445
return new Envelope(header, items);
446446
}
447447

448+
/// <summary>
449+
/// Creates an envelope that contains only an attachment for an existing event.
450+
/// </summary>
451+
internal static Envelope FromAttachment(SentryId eventId, SentryAttachment attachment, IDiagnosticLogger? logger = null) =>
452+
new(eventId, CreateHeader(eventId), [EnvelopeItem.FromAttachment(attachment)]);
453+
448454
private static async Task<IReadOnlyDictionary<string, object?>> DeserializeHeaderAsync(
449455
Stream stream,
450456
CancellationToken cancellationToken = default)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace Sentry.Testing;
2+
3+
internal sealed class NullAttachmentContent : IAttachmentContent
4+
{
5+
public static NullAttachmentContent Instance { get; } = new();
6+
7+
public Stream GetStream() => Stream.Null;
8+
9+
private NullAttachmentContent() { }
10+
}

test/Sentry.Tests/HubTests.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1730,6 +1730,67 @@ public void CaptureFeedback_ConfigureScope_ScopeApplied(bool enabled)
17301730
_fixture.Client.Received(enabled ? 1 : 0).CaptureFeedback(Arg.Any<SentryFeedback>(), Arg.Is<Scope>(s => s.Tags["foo"] == "bar"), Arg.Any<SentryHint>());
17311731
}
17321732

1733+
[Theory]
1734+
[InlineData(true)]
1735+
[InlineData(false)]
1736+
public void CaptureAttachment_HubEnabled(bool enabled)
1737+
{
1738+
// Arrange
1739+
var hub = _fixture.GetSut();
1740+
if (!enabled)
1741+
{
1742+
hub.Dispose();
1743+
}
1744+
1745+
_fixture.Client.CaptureEnvelope(Arg.Any<Envelope>()).Returns(true);
1746+
1747+
var eventId = SentryId.Create();
1748+
var attachment = new SentryAttachment(
1749+
AttachmentType.Default,
1750+
new ByteAttachmentContent("test content"u8.ToArray()),
1751+
"test.txt",
1752+
"text/plain");
1753+
1754+
// Act
1755+
var result = hub.CaptureAttachment(eventId, attachment);
1756+
1757+
// Assert
1758+
result.Should().Be(enabled);
1759+
_fixture.Client.Received(enabled ? 1 : 0).CaptureEnvelope(Arg.Any<Envelope>());
1760+
}
1761+
1762+
[Fact]
1763+
public void CaptureAttachment_SentryIdEmpty_ReturnsFalse()
1764+
{
1765+
// Arrange
1766+
var hub = _fixture.GetSut();
1767+
1768+
var eventId = SentryId.Empty;
1769+
var attachment = new SentryAttachment(AttachmentType.Default, NullAttachmentContent.Instance, "test.txt", "text/plain");
1770+
1771+
// Act
1772+
var result = hub.CaptureAttachment(eventId, attachment);
1773+
1774+
// Assert
1775+
result.Should().Be(false);
1776+
_fixture.Client.DidNotReceive().CaptureEnvelope(Arg.Any<Envelope>());
1777+
}
1778+
1779+
[Fact]
1780+
public void CaptureAttachment_AttachmentNull_ReturnsFalse()
1781+
{
1782+
// Arrange
1783+
var hub = _fixture.GetSut();
1784+
var eventId = SentryId.Create();
1785+
1786+
// Act
1787+
var result = hub.CaptureAttachment(eventId, null!);
1788+
1789+
// Assert
1790+
result.Should().Be(false);
1791+
_fixture.Client.DidNotReceive().CaptureEnvelope(Arg.Any<Envelope>());
1792+
}
1793+
17331794
[Theory]
17341795
[InlineData(true)]
17351796
[InlineData(false)]

test/Sentry.Tests/Protocol/Envelopes/EnvelopeTests.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,4 +1090,26 @@ public async Task Serialization_RoundTrip_RecalculatesLengthHeader()
10901090
Assert.Contains("""{"foo":"2020-01-01T00:00:00+01:00"}""", serialized1);
10911091
Assert.Contains("""{"foo":"2020-01-01T00:00:00\u002B01:00"}""", serialized2);
10921092
}
1093+
1094+
[Fact]
1095+
public void FromAttachment_ValidAttachment_CreatesEnvelope()
1096+
{
1097+
// Arrange
1098+
var eventId = SentryId.Create();
1099+
var attachment = new SentryAttachment(
1100+
AttachmentType.Default,
1101+
new ByteAttachmentContent("test content"u8.ToArray()),
1102+
"test.txt",
1103+
"text/plain");
1104+
1105+
// Act
1106+
using var envelope = Envelope.FromAttachment(eventId, attachment);
1107+
1108+
// Assert
1109+
envelope.TryGetEventId().Should().Be(eventId);
1110+
envelope.Items.Should().HaveCount(1);
1111+
envelope.Items[0].Header["type"].Should().Be("attachment");
1112+
envelope.Items[0].Header["filename"].Should().Be("test.txt");
1113+
envelope.Items[0].Header["content_type"].Should().Be("text/plain");
1114+
}
10931115
}

0 commit comments

Comments
 (0)