Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add User.Segment property #1920

Merged
merged 5 commits into from
Sep 15, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
- `SentryOptions.AttachStackTrace` is now enabled by default. ([#1907](https://github.com/getsentry/sentry-dotnet/pull/1907))
- Update Sentry Android SDK to version 6.4.1 ([#1911](https://github.com/getsentry/sentry-dotnet/pull/1911))
- Update Sentry Cocoa SDK to version 7.24.1 ([#1912](https://github.com/getsentry/sentry-dotnet/pull/1912))
- Add TransactionNameSource annotation ([#1910](https://github.com/getsentry/sentry-dotnet/pull/1910))
- Add `TransactionNameSource` annotation ([#1910](https://github.com/getsentry/sentry-dotnet/pull/1910))
- Add `User.Segment` property ([#1920](https://github.com/getsentry/sentry-dotnet/pull/1920))

## Fixes

Expand Down
7 changes: 1 addition & 6 deletions src/Sentry/IEventLike.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,7 @@ public static class EventLikeExtensions
/// <summary>
/// Whether a <see cref="User"/> has been set to the object with any of its fields non null.
/// </summary>
public static bool HasUser(this IEventLike eventLike)
=> eventLike.User.Email is not null
|| eventLike.User.Id is not null
|| eventLike.User.Username is not null
|| eventLike.User.InternalOther?.Count > 0
|| eventLike.User.IpAddress is not null;
public static bool HasUser(this IEventLike eventLike) => eventLike.User.HasAnyData();

/// <summary>
/// Sets the fingerprint to the object.
Expand Down
104 changes: 58 additions & 46 deletions src/Sentry/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,50 +15,55 @@ public sealed class User : IJsonSerializable
{
internal Action<User>? PropertyChanged { get; set; }

private string? _id;
private string? _username;
private string? _email;
private string? _ipAddress;
private string? _segment;
private IDictionary<string, string>? _other;

/// <summary>
/// The email address of the user.
/// The unique ID of the user.
/// </summary>
/// <value>
/// The user's email address.
/// </value>
public string? Email
public string? Id
{
get => _email;
get => _id;
set
{
_email = value;
_id = value;
PropertyChanged?.Invoke(this);
}
}

private string? _id;

/// <summary>
/// The unique ID of the user.
/// The username of the user.
/// </summary>
/// <value>
/// The unique identifier.
/// </value>
public string? Id
public string? Username
{
get => _id;
get => _username;
set
{
_id = value;
_username = value;
PropertyChanged?.Invoke(this);
}
}

private string? _ipAddress;
/// <summary>
/// The email address of the user.
/// </summary>
public string? Email
{
get => _email;
set
{
_email = value;
PropertyChanged?.Invoke(this);
}
}

/// <summary>
/// The IP of the user.
/// The IP address of the user.
/// </summary>
/// <value>
/// The user's IP address.
/// </value>
public string? IpAddress
{
get => _ipAddress;
Expand All @@ -69,33 +74,30 @@ public string? IpAddress
}
}

private string? _username;

/// <summary>
/// The username of the user.
/// The segment the user belongs to.
/// </summary>
/// <value>
/// The user's username.
/// </value>
public string? Username
public string? Segment
{
get => _username;
get => _segment;
set
{
_username = value;
_segment = value;
PropertyChanged?.Invoke(this);
}
}

internal IDictionary<string, string>? InternalOther { get; private set; }

/// <summary>
/// Additional information about the user.
/// </summary>
public IDictionary<string, string> Other
{
get => InternalOther ??= new Dictionary<string, string>();
set => InternalOther = value;
get => _other ??= new Dictionary<string, string>();
set
{
_other = value;
PropertyChanged?.Invoke(this);
}
}

/// <summary>
Expand All @@ -105,9 +107,7 @@ public IDictionary<string, string> Other
public User Clone()
{
var user = new User();

CopyTo(user);

return user;
}

Expand All @@ -118,26 +118,36 @@ internal void CopyTo(User? user)
return;
}

user.Email ??= Email;
user.Id ??= Id;
user.Username ??= Username;
user.Email ??= Email;
user.IpAddress ??= IpAddress;
user.Segment ??= Segment;

user.InternalOther ??= InternalOther?.ToDictionary(
user._other ??= _other?.ToDictionary(
entry => entry.Key,
entry => entry.Value);
}

internal bool HasAnyData() =>
Id is not null ||
Username is not null ||
Email is not null ||
IpAddress is not null ||
Segment is not null ||
_other?.Count > 0;

/// <inheritdoc />
public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? _)
{
writer.WriteStartObject();

writer.WriteStringIfNotWhiteSpace("email", Email);
writer.WriteStringIfNotWhiteSpace("id", Id);
writer.WriteStringIfNotWhiteSpace("ip_address", IpAddress);
writer.WriteStringIfNotWhiteSpace("username", Username);
writer.WriteStringDictionaryIfNotEmpty("other", InternalOther!);
writer.WriteStringIfNotWhiteSpace("email", Email);
writer.WriteStringIfNotWhiteSpace("ip_address", IpAddress);
writer.WriteStringIfNotWhiteSpace("segment", Segment);
writer.WriteStringDictionaryIfNotEmpty("other", _other!);

writer.WriteEndObject();
}
Expand All @@ -147,19 +157,21 @@ public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? _)
/// </summary>
public static User FromJson(JsonElement json)
{
var email = json.GetPropertyOrNull("email")?.GetString();
var id = json.GetPropertyOrNull("id")?.GetString();
var ip = json.GetPropertyOrNull("ip_address")?.GetString();
var username = json.GetPropertyOrNull("username")?.GetString();
var email = json.GetPropertyOrNull("email")?.GetString();
var ip = json.GetPropertyOrNull("ip_address")?.GetString();
var segment = json.GetPropertyOrNull("segment")?.GetString();
var other = json.GetPropertyOrNull("other")?.GetStringDictionaryOrNull();

return new User
{
Email = email,
Id = id,
IpAddress = ip,
Username = username,
InternalOther = other?.WhereNotNullValue().ToDictionary()
Email = email,
IpAddress = ip,
Segment = segment,
_other = other?.WhereNotNullValue().ToDictionary()
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,7 @@ namespace Sentry
public string? Id { get; set; }
public string? IpAddress { get; set; }
public System.Collections.Generic.IDictionary<string, string> Other { get; set; }
public string? Segment { get; set; }
public string? Username { get; set; }
public Sentry.User Clone() { }
public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,7 @@ namespace Sentry
public string? Id { get; set; }
public string? IpAddress { get; set; }
public System.Collections.Generic.IDictionary<string, string> Other { get; set; }
public string? Segment { get; set; }
public string? Username { get; set; }
public Sentry.User Clone() { }
public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,7 @@ namespace Sentry
public string? Id { get; set; }
public string? IpAddress { get; set; }
public System.Collections.Generic.IDictionary<string, string> Other { get; set; }
public string? Segment { get; set; }
public string? Username { get; set; }
public Sentry.User Clone() { }
public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? _) { }
Expand Down
40 changes: 24 additions & 16 deletions test/Sentry.Tests/Protocol/ScopeExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public void HasUser_DefaultScope_ReturnsFalse()
public void HasUser_NullUser_ReturnsFalse()
{
var sut = _fixture.GetSut();
sut.User = null;
sut.User = null!;
Assert.False(sut.HasUser());
}

Expand Down Expand Up @@ -52,6 +52,14 @@ public void HasUser_UserWithUserName_ReturnsTrue()
Assert.True(sut.HasUser());
}

[Fact]
public void HasUser_UserWithEmail_ReturnsTrue()
{
var sut = _fixture.GetSut();
sut.User.Email = "test";
Assert.True(sut.HasUser());
}

[Fact]
public void HasUser_UserWithIpAddress_ReturnsTrue()
{
Expand All @@ -61,10 +69,10 @@ public void HasUser_UserWithIpAddress_ReturnsTrue()
}

[Fact]
public void HasUser_UserWithEmail_ReturnsTrue()
public void HasUser_UserWithSegment_ReturnsTrue()
{
var sut = _fixture.GetSut();
sut.User.Email = "test";
sut.User.Segment = "test";
Assert.True(sut.HasUser());
}

Expand Down Expand Up @@ -408,8 +416,8 @@ public void AddBreadcrumb_ValueTuple_AllArgumentsMatch()
Assert.Equal(expectedMessage, actual.Message);
Assert.Equal(expectedCategory, actual.Category);
Assert.Equal(expectedType, actual.Type);
Assert.Equal(expectedData.key, actual.Data.Single().Key);
Assert.Equal(expectedData.value, actual.Data.Single().Value);
Assert.Equal(expectedData.key, actual.Data?.Single().Key);
Assert.Equal(expectedData.value, actual.Data?.Single().Value);
Assert.Equal(expectedLevel, actual.Level);
}
#endif
Expand All @@ -435,8 +443,8 @@ public void AddBreadcrumb_Dictionary_AllArgumentsMatch()
Assert.Equal(expectedMessage, actual.Message);
Assert.Equal(expectedCategory, actual.Category);
Assert.Equal(expectedType, actual.Type);
Assert.Equal(expectedData.Single().Key, actual.Data.Single().Key);
Assert.Equal(expectedData.Single().Value, actual.Data.Single().Value);
Assert.Equal(expectedData.Single().Key, actual.Data?.Single().Key);
Assert.Equal(expectedData.Single().Value, actual.Data?.Single().Value);
Assert.Equal(expectedLevel, actual.Level);
}

Expand Down Expand Up @@ -464,8 +472,8 @@ public void AddBreadcrumb_ImmutableDictionary_AllArgumentsMatch()
Assert.Equal(expectedMessage, actual.Message);
Assert.Equal(expectedCategory, actual.Category);
Assert.Equal(expectedType, actual.Type);
Assert.Equal(expectedData.Single().Key, actual.Data.Single().Key);
Assert.Equal(expectedData.Single().Value, actual.Data.Single().Value);
Assert.Equal(expectedData.Single().Key, actual.Data?.Single().Key);
Assert.Equal(expectedData.Single().Value, actual.Data?.Single().Value);
Assert.Equal(expectedLevel, actual.Level);
}

Expand Down Expand Up @@ -510,10 +518,10 @@ public void AddAttachment_AllArgumentsMatch()
// Assert
var attachment = Assert.Single(scope.Attachments);

Assert.Equal(expectedStream, attachment?.Content.GetStream());
Assert.Equal(expectedFileName, attachment?.FileName);
Assert.Equal(expectedType, attachment?.Type);
Assert.Equal(expectedContentType, attachment?.ContentType);
Assert.Equal(expectedStream, attachment.Content.GetStream());
Assert.Equal(expectedFileName, attachment.FileName);
Assert.Equal(expectedType, attachment.Type);
Assert.Equal(expectedContentType, attachment.ContentType);
}

[Fact]
Expand Down Expand Up @@ -554,17 +562,17 @@ public void AddAttachment_FromFile_ArgumentsResolvedCorrectly()

// Assert
var attachment = Assert.Single(scope.Attachments);
using var stream = attachment?.Content.GetStream();
using var stream = attachment.Content.GetStream();

Assert.Equal("MyFile.txt", attachment?.FileName);
Assert.Equal("MyFile.txt", attachment.FileName);
Assert.Equal(12, stream.Length);
}

[Fact]
public void Apply_Null_Target_DoesNotThrow()
{
var sut = _fixture.GetSut();
sut.Apply(null);
sut.Apply(null!);
}

[Fact]
Expand Down
Loading