Skip to content

Commit

Permalink
Merge pull request #2 from getsentry/client/basic-types
Browse files Browse the repository at this point in the history
Client contexts and sample
  • Loading branch information
bruno-garcia authored May 22, 2018
2 parents 967f1a8 + 8355dd8 commit 18461f1
Show file tree
Hide file tree
Showing 34 changed files with 1,902 additions and 24 deletions.
9 changes: 9 additions & 0 deletions Sentry.sln
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1 - Solution Items", "1 - S
README.md = README.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{77454495-55EE-4B40-A089-71B9E8F82E89}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Console.Basic", "samples\Sentry.Samples.Console.Basic\Sentry.Samples.Console.Basic.csproj", "{65F5A969-B386-48F5-8B7E-6C90D6C720BC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -42,13 +46,18 @@ Global
{1E0F969B-67F9-4FCC-BCBF-596DB6460C7C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1E0F969B-67F9-4FCC-BCBF-596DB6460C7C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1E0F969B-67F9-4FCC-BCBF-596DB6460C7C}.Release|Any CPU.Build.0 = Release|Any CPU
{65F5A969-B386-48F5-8B7E-6C90D6C720BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{65F5A969-B386-48F5-8B7E-6C90D6C720BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{65F5A969-B386-48F5-8B7E-6C90D6C720BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{65F5A969-B386-48F5-8B7E-6C90D6C720BC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{F2486CC8-FAB7-4775-976F-C5A4CF97867F} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB}
{1E0F969B-67F9-4FCC-BCBF-596DB6460C7C} = {83263231-1A2A-4733-B759-EEFF14E8C5D5}
{65F5A969-B386-48F5-8B7E-6C90D6C720BC} = {77454495-55EE-4B40-A089-71B9E8F82E89}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0C652B1A-DF72-4EE5-A98B-194FE2C054F6}
Expand Down
12 changes: 12 additions & 0 deletions samples/Sentry.Samples.Console.Basic/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Sentry.Samples.Console.Basic
{
static class Program
{
static void Main()
{
var sentry = new HttpSentryClient();
// This exception is captured and sent to Sentry
throw null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="../../src/Sentry/Sentry.csproj" />
</ItemGroup>

</Project>
9 changes: 0 additions & 9 deletions src/Sentry/Class1.cs

This file was deleted.

8 changes: 8 additions & 0 deletions src/Sentry/Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Sentry
{
internal static class Constants
{
public const string DsnEnvironmentVariable = "SENTRY_DSN";
public const string DisableSdkDsnValue = "disabled";
}
}
178 changes: 178 additions & 0 deletions src/Sentry/Dsn.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
using System;
using System.Diagnostics;

namespace Sentry
{
/// <summary>
/// The Data Source Name of a given project in Sentry.
/// </summary>
/// <remarks>
/// <see href="https://docs.sentry.io/quickstart/#configure-the-dsn"/>
/// </remarks>
public class Dsn
{
private readonly string _dsn;

/// <summary>
/// The project ID which the authenticated user is bound to.
/// </summary>
public string ProjectId { get; }
/// <summary>
/// An optional path of which Sentry is hosted
/// </summary>
public string Path { get; }
/// <summary>
/// The optional secret key to authenticate the SDK.
/// </summary>
public string SecretKey { get; }
/// <summary>
/// The required public key to authenticate the SDK.
/// </summary>
public string PublicKey { get; }
/// <summary>
/// The URI used to communicate with Sentry
/// </summary>
public Uri SentryUri { get; }

/// <summary>
/// Initializes a new instance of the <see cref="Dsn"/> class.
/// </summary>
/// <param name="dsn">The DSN in the format: {PROTOCOL}://{PUBLIC_KEY}@{HOST}/{PATH}{PROJECT_ID}</param>
/// <remarks>
/// A legacy DSN containing a secret will also be accepted: {PROTOCOL}://{PUBLIC_KEY}:{SECRET_KEY}@{HOST}/{PATH}{PROJECT_ID}
/// </remarks>
public Dsn(string dsn)
{
var parsed = Parse(dsn, throwOnError: true);
Debug.Assert(parsed != null, "Parse should throw instead of returning null!");

var (projectId, path, secretKey, publicKey, sentryUri) = parsed.Value;

_dsn = dsn;
ProjectId = projectId;
Path = path;
SecretKey = secretKey;
PublicKey = publicKey;
SentryUri = sentryUri;
}

private Dsn(string dsn, string projectId, string path, string secretKey, string publicKey, Uri sentryUri)
{
_dsn = dsn;
ProjectId = projectId;
Path = path;
SecretKey = secretKey;
PublicKey = publicKey;
SentryUri = sentryUri;
}

/// <summary>
/// Tries to parse the string into a <see cref="Dsn"/>
/// </summary>
/// <param name="dsn">The string to attempt parsing.</param>
/// <param name="finalDsn">The <see cref="Dsn"/> when successfully parsed.</param>
/// <returns><c>true</c> if the string is a valid <see cref="Dsn"/> as was successfully parsed. Otherwise, <c>false</c>.</returns>
public static bool TryParse(string dsn, out Dsn finalDsn)
{
try
{
var parsed = Parse(dsn, throwOnError: false);
if (!parsed.HasValue)
{
finalDsn = null;
return false;
}

var (projectId, path, secretKey, publicKey, sentryUri) = parsed.Value;

finalDsn = new Dsn(
dsn,
projectId,
path,
secretKey,
publicKey,
sentryUri);

return true;
}
catch
{
// Parse should not throw though!
finalDsn = null;
return false;
}
}

private static (string projectId, string path, string secretKey, string publicKey, Uri sentryUri)?
Parse(string dsn, bool throwOnError)
{
Uri uri;
if (throwOnError)
{
uri = new Uri(dsn); // Throws the UriFormatException one would expect
}
else if (Uri.TryCreate(dsn, UriKind.Absolute, out var parsedUri))
{
uri = parsedUri;
}
else
{
return null;
}

// uri.UserInfo returns empty string instead of null when no user info data is provided
if (string.IsNullOrWhiteSpace(uri.UserInfo))
{
if (throwOnError)
{
throw new ArgumentException("Invalid DSN: No public key provided.");
}
return null;
}

var keys = uri.UserInfo.Split(':');
var publicKey = keys[0];
if (string.IsNullOrWhiteSpace(publicKey))
{
if (throwOnError)
{
throw new ArgumentException("Invalid DSN: No public key provided.");
}
return null;
}

string secretKey = null;
if (keys.Length > 1)
{
secretKey = keys[1];
}

var path = uri.AbsolutePath.Substring(0, uri.AbsolutePath.LastIndexOf('/'));
var projectId = uri.AbsoluteUri.Substring(uri.AbsoluteUri.LastIndexOf('/') + 1);

if (string.IsNullOrWhiteSpace(projectId))
{
if (throwOnError)
{
throw new ArgumentException("Invalid DSN: A Project Id is required.");
}
return null;
}

var builder = new UriBuilder
{
Scheme = uri.Scheme,
Host = uri.DnsSafeHost,
Port = uri.Port,
Path = $"{path}/api/{projectId}/store/"
};

return (projectId, path, secretKey, publicKey, builder.Uri);
}

/// <summary>
/// The original DSN string used to create this instance
/// </summary>
public override string ToString() => _dsn;
}
}
35 changes: 35 additions & 0 deletions src/Sentry/HttpSentryClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Sentry
{
///
public class HttpSentryClient : ISentryClient, IDisposable
{
///
public HttpSentryClient()
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}

private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
// TODO: A proper implementation
CaptureEventAsync(new SentryEvent(e.ExceptionObject as Exception));
}

///
public Task<SentryResponse> CaptureEventAsync(SentryEvent @event, CancellationToken cancellationToken = default)
=> Task.FromResult(new SentryResponse(false));

///
public SentryResponse CaptureEvent(SentryEvent @event) => new SentryResponse(false);

///
public void Dispose()
{
AppDomain.CurrentDomain.UnhandledException -= CurrentDomain_UnhandledException;
}
}
}
26 changes: 26 additions & 0 deletions src/Sentry/ISentryClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Threading;
using System.Threading.Tasks;

namespace Sentry
{
/// <summary>
/// Sentry client
/// </summary>
public interface ISentryClient
{
/// <summary>
/// Sends the <see cref="SentryEvent" /> to Sentry asynchronously
/// </summary>
/// <param name="event">The event to send to Sentry.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns><see cref="SentryResponse"/></returns>
Task<SentryResponse> CaptureEventAsync(SentryEvent @event, CancellationToken cancellationToken = default);

/// <summary>
/// Sends the <see cref="SentryEvent" /> to Sentry
/// </summary>
/// <param name="event">The event to send to Sentry.</param>
/// <returns><see cref="SentryResponse"/></returns>
SentryResponse CaptureEvent(SentryEvent @event);
}
}
12 changes: 12 additions & 0 deletions src/Sentry/JsonSerializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace Sentry
{
internal static class JsonSerializer
{
private static readonly StringEnumConverter StringEnumConverter = new StringEnumConverter();

public static string SerializeObject<T>(T @object) => JsonConvert.SerializeObject(@object, StringEnumConverter);
}
}
6 changes: 6 additions & 0 deletions src/Sentry/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using System;
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Sentry.Tests")]

[assembly: CLSCompliant(true)]
Loading

0 comments on commit 18461f1

Please sign in to comment.