Skip to content

Commit

Permalink
fix: metadata in crash post
Browse files Browse the repository at this point in the history
  • Loading branch information
bobbyg603 committed Sep 20, 2024
1 parent 863fbac commit ea06eb8
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 15 deletions.
57 changes: 50 additions & 7 deletions BugSplatDotNetStandard.Test/Api/CrashPostClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using BugSplatDotNetStandard.Http;
using Moq;
using Moq.Protected;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
using static Tests.StackTraceFactory;

Expand All @@ -18,22 +19,30 @@ namespace Tests
[TestFixture]
public class CrashPostClientTest
{
const string database = "fred";
const string application = "my-net-crasher";
const string version = "1.0";

FileInfo lockedFile;
FileInfo minidumpFile;
FileStream lockedFileWriter;

string database;
string application = "TestApp";
string version = "1.0.0";
string clientId;
string clientSecret;

[OneTimeSetUp]
public void SetUp()
{
DotNetEnv.Env.Load();
database = Environment.GetEnvironmentVariable("BUGSPLAT_DATABASE");
clientId = Environment.GetEnvironmentVariable("BUGSPLAT_CLIENT_ID");
clientSecret = Environment.GetEnvironmentVariable("BUGSPLAT_CLIENT_SECRET");

var bytesToWrite = Encoding.UTF8.GetBytes("This file is locked");
lockedFile = new FileInfo("lockedFile.txt");
lockedFileWriter = File.Open(lockedFile.FullName, FileMode.Create, FileAccess.Write, FileShare.None);
lockedFileWriter.Write(bytesToWrite, 0, bytesToWrite.Length);
lockedFileWriter.Flush();

minidumpFile = new FileInfo("minidump.dmp");
File.Create(minidumpFile.FullName).Close();
}
Expand Down Expand Up @@ -61,7 +70,6 @@ public void CrashPostClient_Constructor_ShouldThrowIfS3ClientFactoryIsNullOrEmpt
[Test]
public async Task CrashPostClient_PostException_ShouldReturn200()
{
var expectedUri = $"https://{database}.bugsplat.com/api/getCrashUploadUrl?database={database}&appName={application}&appVersion={version}";
var stackTrace = CreateStackTrace();
var bugsplat = new BugSplat(database, application, version);
var getCrashUrl = "https://fake.url.com";
Expand All @@ -86,7 +94,6 @@ public async Task CrashPostClient_PostException_ShouldReturn200()
[Test]
public async Task CrashPostClient_PostMinidump_ShouldReturn200()
{
var expectedUri = $"https://{database}.bugsplat.com/api/getCrashUploadUrl?database={database}&appName={application}&appVersion={version}";
var bugsplat = new BugSplat(database, application, version);
var getCrashUrl = "https://fake.url.com";
var mockHttp = CreateMockHttpClientForExceptionPost(getCrashUrl);
Expand Down Expand Up @@ -136,7 +143,6 @@ await sut.PostException(
[Test]
public void CrashPostClient_PostCrashFile_ShouldNotThrowIfAttachmentLocked()
{
var stackTrace = CreateStackTrace();
var bugsplat = new BugSplat(database, application, version);
bugsplat.Attachments.Add(lockedFile);
var getCrashUrl = "https://fake.url.com";
Expand All @@ -159,6 +165,43 @@ await sut.PostCrashFile(
});
}

[Test]
[Explicit]
public async Task CrashPostClient_PostCrashFile_PostCrashAndMetadata()
{
var stackTrace = CreateStackTrace();
var bugsplat = new BugSplat(database, application, version);
bugsplat.Description = "Test description";
bugsplat.Email = "[email protected]";
bugsplat.Key = "Test key";
bugsplat.Notes = "Test notes";
bugsplat.User = "Test user";
var oauth2ApiClient = OAuth2ApiClient.Create(clientId, clientSecret)
.Authenticate()
.Result;
var crashDetailsClient = CrashDetailsClient.Create(oauth2ApiClient);
var sut = new CrashPostClient(HttpClientFactory.Default, S3ClientFactory.Default);

var postResult = await sut.PostException(
database,
application,
version,
stackTrace,
ExceptionPostOptions.Create(bugsplat)
);
var postResponseContent = JObject.Parse(postResult.Content.ReadAsStringAsync().Result);
var id = postResponseContent["crashId"].Value<int>();

var crashDetails = await crashDetailsClient.GetCrashDetails(database, id);
var crashDetailsContent = JObject.Parse(crashDetails.Content.ReadAsStringAsync().Result);
Assert.AreEqual(HttpStatusCode.OK, postResult.StatusCode);
Assert.AreEqual(bugsplat.Description, crashDetailsContent["description"].Value<string>());
Assert.AreEqual(bugsplat.Email, crashDetailsContent["email"].Value<string>());
Assert.AreEqual(bugsplat.Key, crashDetailsContent["appKey"].Value<string>());
Assert.AreEqual(bugsplat.Notes, crashDetailsContent["comments"].Value<string>());
Assert.AreEqual(bugsplat.User, crashDetailsContent["user"].Value<string>());
}

private Mock<HttpMessageHandler> CreateMockHttpClientForExceptionPost(string crashUploadUrl)
{
var getCrashUploadUrlResponse = new HttpResponseMessage();
Expand Down
51 changes: 51 additions & 0 deletions BugSplatDotNetStandard/Api/CrashDetailsClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@

using System;
using System.Net.Http;
using System.Threading.Tasks;
using static BugSplatDotNetStandard.Utils.ArgumentContracts;

namespace BugSplatDotNetStandard.Api
{
/// <summary>
/// Used to make requests to the BugSplat Versions API
/// </summary>
public class CrashDetailsClient
{
private IBugSplatApiClient bugsplatApiClient;

internal CrashDetailsClient(IBugSplatApiClient bugsplatApiClient)
{
ThrowIfArgumentIsNull(bugsplatApiClient, "bugsplatApiClient");

this.bugsplatApiClient = bugsplatApiClient;
}

/// <summary>
/// Create a CrashDetailsApiClient, consumer is responsible for authentication
/// </summary>
/// <param name="bugsplatApiClient">An authenticated instance of BugSplatApiClient that will be used for API requests</param>
public static CrashDetailsClient Create(IBugSplatApiClient bugsplatApiClient)
{
return new CrashDetailsClient(bugsplatApiClient);
}

/// <summary>
/// Get details of a crash from a BugSplat database by ID
/// </summary>
/// <param name="database">BugSplat database for which crash information will be retrieved</param>
/// <param name="id">Id of the crash to retrieve</param>
public async Task<HttpResponseMessage> GetCrashDetails(string database, int id)
{
ThrowIfArgumentIsNullOrEmpty(database, "database");
ThrowIfArgumentIsNullOrNegative(id, "id");

ThrowIfNotAuthenticated(bugsplatApiClient);

var response = await this.bugsplatApiClient.GetAsync($"/api/crash/details?database={database}&id={id}");

ThrowIfHttpRequestFailed(response);

return response;
}
}
}
43 changes: 35 additions & 8 deletions BugSplatDotNetStandard/Api/CrashPostClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ IS3ClientFactory s3ClientFactory
{
ThrowIfArgumentIsNull(httpClientFactory, "httpClientFactory");
ThrowIfArgumentIsNull(s3ClientFactory, "s3ClientFactory");

this.httpClient = httpClientFactory.CreateClient();
this.s3Client = s3ClientFactory.CreateClient();
}
Expand Down Expand Up @@ -83,7 +83,9 @@ public async Task<HttpResponseMessage> PostException(
version,
md5,
s3Key,
crashTypeId
crashTypeId,
defaultPostOptions,
overridePostOptions
);

ThrowIfHttpRequestFailed(commitS3CrashResponse);
Expand Down Expand Up @@ -176,7 +178,9 @@ public async Task<HttpResponseMessage> PostCrashFile(
version,
md5,
s3Key,
crashTypeId
crashTypeId,
defaultPostOptions,
overridePostOptions
);

ThrowIfHttpRequestFailed(commitS3CrashResponse);
Expand Down Expand Up @@ -210,21 +214,45 @@ private async Task<HttpResponseMessage> CommitS3CrashUpload(
string version,
string md5,
string s3Key,
int crashTypeId
int crashTypeId,
IBugSplatPostOptions defaultOptions,
IBugSplatPostOptions overrideOptions = null
)
{
var baseUrl = this.CreateBaseUrlFromDatabase(database);
var route = $"{baseUrl}/api/commitS3CrashUpload";
var description = GetStringValueOrDefault(overrideOptions?.Description, defaultOptions.Description);
var email = GetStringValueOrDefault(overrideOptions?.Email, defaultOptions.Email);
var key = GetStringValueOrDefault(overrideOptions?.Key, defaultOptions.Key);
var notes = GetStringValueOrDefault(overrideOptions?.Notes, defaultOptions.Notes);
var user = GetStringValueOrDefault(overrideOptions?.User, defaultOptions.User);
var body = new MultipartFormDataContent()
{
{ new StringContent(database), "database" },
{ new StringContent(application), "appName" },
{ new StringContent(version), "appVersion" },
{ new StringContent(description), "description" },
{ new StringContent(email), "email" },
{ new StringContent(key), "appKey" },
{ new StringContent(notes), "notes" },
{ new StringContent(user), "user" },
{ new StringContent(crashTypeId.ToString()), "crashTypeId" },
{ new StringContent(s3Key), "s3Key" },
{ new StringContent(md5), "md5" }
};

var formDataParams = overrideOptions?.FormDataParams ?? new List<IFormDataParam>();
formDataParams.AddRange(defaultOptions.FormDataParams);
foreach (var param in formDataParams)
{
if (!string.IsNullOrEmpty(param.FileName))
{
body.Add(param.Content, param.Name, param.FileName);
continue;
}
body.Add(param.Content, param.Name);
}
var baseUrl = this.CreateBaseUrlFromDatabase(database);
var route = $"{baseUrl}/api/commitS3CrashUpload";

return await httpClient.PostAsync(route, body);
}

Expand All @@ -243,8 +271,7 @@ int crashPostSize
var baseUrl = this.CreateBaseUrlFromDatabase(database);
var path = $"{baseUrl}/api/getCrashUploadUrl";
var route = $"{path}?database={database}&appName={application}&appVersion={version}&crashPostSize={crashPostSize}";



return await httpClient.GetAsync(route);
}

Expand Down
8 changes: 8 additions & 0 deletions BugSplatDotNetStandard/Utils/ArgumentContracts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ internal static void ThrowIfArgumentIsNull(object argument, string name)
}
}

internal static void ThrowIfArgumentIsNullOrNegative(int argument, string name)
{
if (argument <= 0)
{
throw new ArgumentException($"{name} cannot be null or less than zero!");
}
}

internal static void ThrowIfArgumentIsNullOrEmpty(string argument, string name)
{
if (string.IsNullOrEmpty(argument))
Expand Down

0 comments on commit ea06eb8

Please sign in to comment.