Skip to content

Commit

Permalink
Copy context info from iOS (#1884)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattjohnsonpint authored Sep 1, 2022
1 parent cb735d1 commit e646fa2
Show file tree
Hide file tree
Showing 31 changed files with 489 additions and 56 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- Add and configure options for the iOS SDK ([#1849](https://github.com/getsentry/sentry-dotnet/pull/1849))
- Set default `Release` and `Distribution` for iOS and Android ([#1856](https://github.com/getsentry/sentry-dotnet/pull/1856))
- Apply WinUI 3 exception handler in Sentry core ([#1863](https://github.com/getsentry/sentry-dotnet/pull/1863))
- Copy context info from iOS ([#1884](https://github.com/getsentry/sentry-dotnet/pull/1884))

### Fixes

Expand Down
7 changes: 7 additions & 0 deletions src/Sentry/Internal/ICloneable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Sentry.Internal
{
internal interface ICloneable<out T>
{
T Clone();
}
}
13 changes: 13 additions & 0 deletions src/Sentry/Internal/IUpdatable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Sentry.Internal
{
internal interface IUpdatable<in T> : IUpdatable
where T: IUpdatable<T>
{
void UpdateFrom(T source);
}

internal interface IUpdatable
{
void UpdateFrom(object source);
}
}
26 changes: 25 additions & 1 deletion src/Sentry/Platforms/iOS/IosEventProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Sentry.Extensibility;
using Sentry.iOS.Extensions;

namespace Sentry.iOS;

Expand All @@ -13,7 +14,30 @@ public IosEventProcessor(SentryCocoaOptions options)

public SentryEvent Process(SentryEvent @event)
{
// TODO: Apply device/os context info
// Get a temp event from the Cocoa SDK
using var tempEvent = GetTempEvent();

// Now we'll copy the context info into our own, leveraging the fact that the JSON
// serialization is compatible, since both are designed to send the same data to Sentry.
var json = tempEvent.Context?.ToJsonString();
if (json != null)
{
var jsonDoc = JsonDocument.Parse(json);
var contexts = Contexts.FromJson(jsonDoc.RootElement);
contexts.CopyTo(@event.Contexts);
}

return @event;
}

private static SentryCocoa.SentryEvent GetTempEvent()
{
// This will populate an event with all of the information we need, without actually capturing that event.
var @event = new SentryCocoa.SentryEvent();
SentryCocoa.SentrySdk.ConfigureScope(scope =>
{
scope.ApplyToEvent(@event, 0);
});

return @event;
}
Expand Down
33 changes: 30 additions & 3 deletions src/Sentry/Protocol/App.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using System;
using System.Text.Json;
using Sentry.Extensibility;
using Sentry.Internal;
using Sentry.Internal.Extensions;

// ReSharper disable once CheckNamespace
namespace Sentry.Protocol
{
/// <summary>
Expand All @@ -14,7 +14,7 @@ namespace Sentry.Protocol
/// was running and carries meta data about the current session.
/// </remarks>
/// <seealso href="https://develop.sentry.dev/sdk/event-payloads/contexts/"/>
public sealed class App : IJsonSerializable
public sealed class App : IJsonSerializable, ICloneable<App>, IUpdatable<App>
{
/// <summary>
/// Tells Sentry which type of context this is.
Expand Down Expand Up @@ -59,7 +59,9 @@ public sealed class App : IJsonSerializable
/// <summary>
/// Clones this instance.
/// </summary>
internal App Clone()
internal App Clone() => ((ICloneable<App>)this).Clone();

App ICloneable<App>.Clone()
=> new()
{
Identifier = Identifier,
Expand All @@ -71,6 +73,31 @@ internal App Clone()
Build = Build
};

/// <summary>
/// Updates this instance with data from the properties in the <paramref name="source"/>,
/// unless there is already a value in the existing property.
/// </summary>
internal void UpdateFrom(App source) => ((IUpdatable<App>)this).UpdateFrom(source);

void IUpdatable.UpdateFrom(object source)
{
if (source is App app)
{
((IUpdatable<App>)this).UpdateFrom(app);
}
}

void IUpdatable<App>.UpdateFrom(App source)
{
Identifier ??= source.Identifier;
StartTime ??= source.StartTime;
Hash ??= source.Hash;
BuildType ??= source.BuildType;
Name ??= source.Name;
Version ??= source.Version;
Build ??= source.Build;
}

/// <inheritdoc />
public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? _)
{
Expand Down
29 changes: 25 additions & 4 deletions src/Sentry/Protocol/Browser.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// ReSharper disable once CheckNamespace

using System.Text.Json;
using Sentry.Extensibility;
using Sentry.Internal;
using Sentry.Internal.Extensions;

namespace Sentry.Protocol
Expand All @@ -12,7 +11,7 @@ namespace Sentry.Protocol
/// web request that triggered the event.
/// </summary>
/// <seealso href="https://develop.sentry.dev/sdk/event-payloads/contexts/"/>
public sealed class Browser : IJsonSerializable
public sealed class Browser : IJsonSerializable, ICloneable<Browser>, IUpdatable<Browser>
{
/// <summary>
/// Tells Sentry which type of context this is.
Expand All @@ -32,13 +31,35 @@ public sealed class Browser : IJsonSerializable
/// <summary>
/// Clones this instance
/// </summary>
internal Browser Clone()
internal Browser Clone() => ((ICloneable<Browser>)this).Clone();

Browser ICloneable<Browser>.Clone()
=> new()
{
Name = Name,
Version = Version
};

/// <summary>
/// Updates this instance with data from the properties in the <paramref name="source"/>,
/// unless there is already a value in the existing property.
/// </summary>
internal void UpdateFrom(Browser source) => ((IUpdatable<Browser>)this).UpdateFrom(source);

void IUpdatable.UpdateFrom(object source)
{
if (source is Browser browser)
{
((IUpdatable<Browser>)this).UpdateFrom(browser);
}
}

void IUpdatable<Browser>.UpdateFrom(Browser source)
{
Name ??= source.Name;
Version ??= source.Version;
}

/// <inheritdoc />
public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? _)
{
Expand Down
48 changes: 35 additions & 13 deletions src/Sentry/Protocol/Contexts.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text.Json;
using Sentry.Extensibility;
using Sentry.Internal;
using Sentry.Internal.Extensions;
using Sentry.Protocol;
using OperatingSystem = Sentry.Protocol.OperatingSystem;

// ReSharper disable once CheckNamespace
namespace Sentry
{
/// <summary>
Expand Down Expand Up @@ -76,18 +79,37 @@ internal void CopyTo(Contexts to)
{
foreach (var kv in this)
{
var value = kv.Key switch
{
App.Type when kv.Value is App app => app.Clone(),
Browser.Type when kv.Value is Browser browser => browser.Clone(),
Device.Type when kv.Value is Device device => device.Clone(),
OperatingSystem.Type when kv.Value is OperatingSystem os => os.Clone(),
Runtime.Type when kv.Value is Runtime runtime => runtime.Clone(),
Gpu.Type when kv.Value is Gpu gpu => gpu.Clone(),
_ => kv.Value
};

to.TryAdd(kv.Key, value);
to.AddOrUpdate(kv.Key,

addValueFactory: _ =>
kv.Value is ICloneable<object> cloneable
? cloneable.Clone()
: kv.Value,

updateValueFactory: (_, existing) =>
{
if (existing is IUpdatable updatable)
{
updatable.UpdateFrom(kv.Value);
}
else if (kv.Value is IDictionary<string, object?> source &&
existing is IDictionary<string, object?> target)
{
foreach (var item in source)
{
if (!target.TryGetValue(item.Key, out var value))
{
target.Add(item);
}
else if (value is null)
{
target[item.Key] = item.Value;
}
}
}

return existing;
});
}
}

Expand All @@ -103,7 +125,7 @@ public static Contexts FromJson(JsonElement json)

foreach (var (name, value) in json.EnumerateObject())
{
var type = value.GetPropertyOrNull("type")?.GetString();
var type = value.GetPropertyOrNull("type")?.GetString() ?? name;

// Handle known context types
if (string.Equals(type, App.Type, StringComparison.OrdinalIgnoreCase))
Expand Down
62 changes: 59 additions & 3 deletions src/Sentry/Protocol/Device.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
using System;
using System.Text.Json;
using Sentry.Extensibility;
using Sentry.Internal;
using Sentry.Internal.Extensions;

// ReSharper disable once CheckNamespace
namespace Sentry.Protocol
{
/// <summary>
/// Describes the device that caused the event. This is most appropriate for mobile applications.
/// </summary>
/// <seealso href="https://develop.sentry.dev/sdk/event-payloads/contexts/"/>
public sealed class Device : IJsonSerializable
public sealed class Device : IJsonSerializable, ICloneable<Device>, IUpdatable<Device>
{
/// <summary>
/// Tells Sentry which type of context this is.
Expand Down Expand Up @@ -245,7 +245,9 @@ public sealed class Device : IJsonSerializable
/// <summary>
/// Clones this instance.
/// </summary>
internal Device Clone()
internal Device Clone() => ((ICloneable<Device>)this).Clone();

Device ICloneable<Device>.Clone()
=> new()
{
Name = Name,
Expand Down Expand Up @@ -286,6 +288,60 @@ internal Device Clone()
SupportsLocationService = SupportsLocationService
};

/// <summary>
/// Updates this instance with data from the properties in the <paramref name="source"/>,
/// unless there is already a value in the existing property.
/// </summary>
internal void UpdateFrom(Device source) => ((IUpdatable<Device>)this).UpdateFrom(source);

void IUpdatable.UpdateFrom(object source)
{
if (source is Device device)
{
((IUpdatable<Device>)this).UpdateFrom(device);
}
}

void IUpdatable<Device>.UpdateFrom(Device source)
{
Name ??= source.Name;
Manufacturer ??= source.Manufacturer;
Brand ??= source.Brand;
Architecture ??= source.Architecture;
BatteryLevel ??= source.BatteryLevel;
IsCharging ??= source.IsCharging;
IsOnline ??= source.IsOnline;
BootTime ??= source.BootTime;
ExternalFreeStorage ??= source.ExternalFreeStorage;
ExternalStorageSize ??= source.ExternalStorageSize;
ScreenResolution ??= source.ScreenResolution;
ScreenDensity ??= source.ScreenDensity;
ScreenDpi ??= source.ScreenDpi;
Family ??= source.Family;
FreeMemory ??= source.FreeMemory;
FreeStorage ??= source.FreeStorage;
MemorySize ??= source.MemorySize;
Model ??= source.Model;
ModelId ??= source.ModelId;
Orientation ??= source.Orientation;
Simulator ??= source.Simulator;
StorageSize ??= source.StorageSize;
Timezone ??= source.Timezone;
UsableMemory ??= source.UsableMemory;
LowMemory ??= source.LowMemory;
ProcessorCount ??= source.ProcessorCount;
CpuDescription ??= source.CpuDescription;
ProcessorFrequency ??= source.ProcessorFrequency;
SupportsVibration ??= source.SupportsVibration;
DeviceType ??= source.DeviceType;
BatteryStatus ??= source.BatteryStatus;
DeviceUniqueIdentifier ??= source.DeviceUniqueIdentifier;
SupportsAccelerometer ??= source.SupportsAccelerometer;
SupportsGyroscope ??= source.SupportsGyroscope;
SupportsAudio ??= source.SupportsAudio;
SupportsLocationService ??= source.SupportsLocationService;
}

/// <inheritdoc />
public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? _)
{
Expand Down
1 change: 0 additions & 1 deletion src/Sentry/Protocol/DeviceOrientation.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Runtime.Serialization;

// ReSharper disable once CheckNamespace
namespace Sentry.Protocol
{
/// <summary>
Expand Down
Loading

0 comments on commit e646fa2

Please sign in to comment.