Skip to content

Commit

Permalink
Added more analytics
Browse files Browse the repository at this point in the history
  • Loading branch information
Aragas committed Apr 17, 2024
1 parent 22837f6 commit 533954c
Show file tree
Hide file tree
Showing 14 changed files with 376 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace BUTR.Site.NexusMods.Client.Options;

public sealed record CrashReporterOptions
{
public required string Endpoint { get; init; }
}
15 changes: 14 additions & 1 deletion src/BUTR.Site.NexusMods.Client/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,24 @@ public static WebAssemblyHostBuilder CreateHostBuilder(string[] args)
.ConfigureServices((builder, services) =>
{
services.Configure<BackendOptions>(builder.Configuration.GetSection("Backend"));
services.Configure<CrashReporterOptions>(builder.Configuration.GetSection("CrashReporter"));

services.AddScoped(_ => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress), DefaultRequestHeaders = { { "User-Agent", userAgent } } });
services.AddScoped(_ => new HttpClient
{
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress),
DefaultRequestHeaders = { { "User-Agent", userAgent } }
});
services.AddHttpClient("InternalReports").ConfigureHttpClient((_, client) =>
{
client.BaseAddress = new Uri($"{builder.HostEnvironment.BaseAddress}reports/");
client.DefaultRequestHeaders.Add("User-Agent", userAgent);
}).AddHttpMessageHandler<AssetsDelegatingHandler>();
services.AddHttpClient<ICrashReporterClient, CrashReporterClient>().ConfigureHttpClient((sp, client) =>
{
var opts = sp.GetRequiredService<IOptions<CrashReporterOptions>>().Value;
client.BaseAddress = new Uri(opts.Endpoint);
client.DefaultRequestHeaders.Add("User-Agent", userAgent);
}).AddHttpMessageHandler<AuthenticationAnd401DelegatingHandler>();
services.AddHttpClient("BackendAuthentication").ConfigureBackend(userAgent)
.AddHttpMessageHandler<AuthenticationInjectionDelegatingHandler>().AddHttpMessageHandler<TenantDelegatingHandler>();
services.AddHttpClient("Backend").ConfigureBackend(userAgent)
Expand Down Expand Up @@ -103,6 +114,8 @@ public static WebAssemblyHostBuilder CreateHostBuilder(string[] args)
services.AddTransient<IQuartzClient, QuartzClient>(sp => ConfigureClient(sp, (http, opt) => new QuartzClient(http, opt)));
services.AddTransient<IRecreateStacktraceClient, RecreateStacktraceClient>(sp => ConfigureClient(sp, (http, opt) => new RecreateStacktraceClient(http, opt)));
services.AddTransient<IGitHubClient, GitHubClient>(sp => ConfigureClient(sp, (http, opt) => new GitHubClient(http, opt)));
services.AddTransient<ICrashReportsAnalyzerClient, CrashReportsAnalyzerClient>(sp => ConfigureClient(sp, (http, opt) => new CrashReportsAnalyzerClient(http, opt)));
services.AddTransient<IModsAnalyzerClient, ModsAnalyzerClient>(sp => ConfigureClient(sp, (http, opt) => new ModsAnalyzerClient(http, opt)));

services.AddScoped<TenantProvider>();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using BUTR.Site.NexusMods.ServerClient;

using Microsoft.Extensions.Options;

using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

namespace BUTR.Site.NexusMods.Client.Services;

public interface ICrashReporterClient
{
Task<CrashReportModel?> GetCrashReportModelAsync(string id, CancellationToken ct);
}

public sealed class CrashReporterClient : ICrashReporterClient
{
private readonly HttpClient _httpClient;
private readonly JsonSerializerOptions _jsonSerializerOptions;

public CrashReporterClient(HttpClient httpClient, IOptions<JsonSerializerOptions> jsonSerializerOptions)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
_jsonSerializerOptions = jsonSerializerOptions.Value ?? throw new ArgumentNullException(nameof(jsonSerializerOptions));
}

public async Task<CrashReportModel?> GetCrashReportModelAsync(string id, CancellationToken ct)
{
using var request = new HttpRequestMessage(HttpMethod.Get, $"{id}.json");
using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct);
if (!response.IsSuccessStatusCode) return null;
return await JsonSerializer.DeserializeAsync<CrashReportModel>(await response.Content.ReadAsStreamAsync(ct), _jsonSerializerOptions, ct);
}
}
3 changes: 3 additions & 0 deletions src/BUTR.Site.NexusMods.Client/wwwroot/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"Backend": {
"Endpoint": "https://sitenexusmods.butr.link/"
},
"CrashReporter": {
"Endpoint": "https://report.butr.link/"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System.Diagnostics.Contracts;

namespace BUTR.Site.NexusMods.Server.Models;

internal static class StringExtensions
{
[Pure]
public static LineSplitEnumerator SplitLines(this string str) => new(str.AsSpan());

[StructLayout(LayoutKind.Auto)]
[SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "<Pending>")]
public ref struct LineSplitEnumerator
{
private ReadOnlySpan<char> _str;

public LineSplitEnumerator(ReadOnlySpan<char> str)
{
_str = str;
Current = default;
}

public readonly LineSplitEnumerator GetEnumerator() => this;

public bool MoveNext()
{
if (_str.Length == 0)
return false;

var span = _str;
var index = span.IndexOfAny('\r', '\n');
if (index == -1)
{
_str = ReadOnlySpan<char>.Empty;
Current = new LineSplitEntry(span, ReadOnlySpan<char>.Empty);
return true;
}

if (index < span.Length - 1 && span[index] == '\r')
{
var next = span[index + 1];
if (next == '\n')
{
Current = new LineSplitEntry(span[..index], span.Slice(index, 2));
_str = span[(index + 2)..];
return true;
}
}

Current = new LineSplitEntry(span[..index], span.Slice(index, 1));
_str = span[(index + 1)..];
return true;
}

public LineSplitEntry Current { get; private set; }
}

[StructLayout(LayoutKind.Auto)]
[SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "<Pending>")]
public readonly ref struct LineSplitEntry
{
public LineSplitEntry(ReadOnlySpan<char> line, ReadOnlySpan<char> separator)
{
Line = line;
Separator = separator;
}

public ReadOnlySpan<char> Line { get; }
public ReadOnlySpan<char> Separator { get; }

public void Deconstruct(out ReadOnlySpan<char> line, out ReadOnlySpan<char> separator)
{
line = Line;
separator = Separator;
}

public static implicit operator ReadOnlySpan<char>(LineSplitEntry entry) => entry.Line;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,29 @@ public static TType FromException(ExceptionModel exception)

return From(exc.Type);
}
public static bool TryParseFromException(TValueType exception, out TType value)
{
Span<Range> dest = stackalloc Range[32];
ReadOnlySpan<char> lastTypeLine = default;
foreach (ReadOnlySpan<char> line in exception.SplitLines())
{
var count = line.Split(dest, ':');
if (count != 2) continue;
var firstPart = line[dest[0]].Trim();
var secondPart = line[dest[1]].Trim();
if (firstPart is "Type")
lastTypeLine = secondPart;
}

if (lastTypeLine.Length > 0)
{
value = From(lastTypeLine.ToString());
return true;
}

value = From("");
return false;
}

public static int GetHashCode(TType instance) => VogenDefaults<TType, TValueType>.GetHashCode(instance);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using BUTR.Site.NexusMods.Server.Models.Database;
using BUTR.Site.NexusMods.Server.Utils;
using BUTR.Site.NexusMods.Server.Utils.BindingSources;
using BUTR.Site.NexusMods.Server.Utils.Http.ApiResults;

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
Expand Down Expand Up @@ -43,7 +44,6 @@ public CrashReportsAnalyzerController(ILogger<CrashReportsAnalyzerController> lo
[HttpPost("GetDiagnostics")]
public async Task<ActionResult<CrashReportDiagnosticsResult?>> GetDiagnosticsAsync([BindTenant] TenantId tenant, [FromBody] CrashReportModel crashReport, [FromServices] ITenantContextAccessor tenantContextAccessor, CancellationToken ct)
{
if (!TenantId.Values.Contains(tenant)) return BadRequest();
tenantContextAccessor.Current = tenant;

if (tenant == TenantId.Bannerlord)
Expand Down Expand Up @@ -111,12 +111,13 @@ private async IAsyncEnumerable<ModuleUpdate> GetModuleUpdatesForBannerlordAsync(

var currentModuleIdsWithoutAnyData = crashReport.Modules.Where(x => !x.IsOfficial && string.IsNullOrEmpty(x.Url) && x.UpdateInfo is null)
.Select(x => ModuleId.From(x.Id)).ToArray();
var currentMexusModsUpdateInfos = crashReport.Modules.Where(x => !x.IsOfficial && x.UpdateInfo is { Value: "NexusMods" })
var currentMexusModsUpdateInfos = crashReport.Modules.Where(x => !x.IsOfficial && x.UpdateInfo is { Provider: "NexusMods" })
.Select(x => NexusModsModId.TryParse(x.UpdateInfo!.Value, out var modId) ? modId : NexusModsModId.None)
.Where(x => x != NexusModsModId.None).ToArray();
var currentNexusModsIds = crashReport.Modules.Where(x => !x.IsOfficial && !string.IsNullOrEmpty(x.Url))
.Select(x => NexusModsModId.TryParseUrl(x.Url, out var modId) ? modId : NexusModsModId.None)
.Where(x => x != NexusModsModId.None).ToArray();
.Where(x => x != NexusModsModId.None)
.Except(currentMexusModsUpdateInfos).ToArray();

var currentModules = crashReport.Modules
.Where(x => !x.IsOfficial).Select(x => new { ModuleId = ModuleId.From(x.Id), Version = ModuleVersion.From(x.Version) }).ToArray();
Expand Down
Loading

0 comments on commit 533954c

Please sign in to comment.