Skip to content

Commit

Permalink
Split code to Persistent
Browse files Browse the repository at this point in the history
Added v14 support of crash reports
  • Loading branch information
Aragas committed Sep 22, 2024
1 parent ed8144b commit de96e7e
Show file tree
Hide file tree
Showing 142 changed files with 1,154 additions and 598 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BUTR.CrashReport.Models" Version="13.0.0.82" />
<PackageReference Include="BUTR.CrashReport.Bannerlord.Parser" Version="13.0.0.82" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\BUTR.Site.NexusMods.Server.Persistence\BUTR.Site.NexusMods.Server.Persistence.csproj" />
<ProjectReference Include="..\BUTR.Site.NexusMods.Server.ValueObjects.Vogen\BUTR.Site.NexusMods.Server.ValueObjects.Vogen.csproj" />
</ItemGroup>

</Project>
110 changes: 110 additions & 0 deletions src/BUTR.Site.NexusMods.Server.CrashReport.v13/CrashReportV13.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.Json;
using BUTR.CrashReport.Models;
using BUTR.Site.NexusMods.Server.Extensions;
using BUTR.Site.NexusMods.Server.Models;
using BUTR.Site.NexusMods.Server.Models.Database;
using BUTR.Site.NexusMods.Server.Repositories;

namespace BUTR.Site.NexusMods.Server.CrashReport.v13;

public static class CrashReportV13
{
private static ExceptionTypeId FromException(ExceptionModel exception)
{
var exc = exception;
while (exc.InnerException is not null)
exc = exc.InnerException;

return ExceptionTypeId.From(exc.Type);
}

private static string GetException(ExceptionModel? exception, bool inner = false) => exception is null ? string.Empty : $"""
{(inner ? "Inner " : string.Empty)}Exception information
Type: {exception.Type}
Message: {exception.Message}
CallStack:
{exception.CallStack}
{GetException(exception.InnerException, true)}
""";

public static bool TryFromJson(
IUnitOfWrite unitOfWrite,
TenantId tenant,
CrashReportFileId fileId,
CrashReportUrl url,
DateTime date,
byte version,
string content,
[NotNullWhen(true)] out CrashReportEntity? crashReportEntity,
[NotNullWhen(true)] out CrashReportToMetadataEntity? crashReportToMetadataEntity,
[NotNullWhen(true)] out IList<CrashReportToModuleMetadataEntity>? crashReportToModuleMetadataEntities)
{
if (version != 13)
{
crashReportEntity = null!;
crashReportToMetadataEntity = null!;
crashReportToModuleMetadataEntities = null!;
return false;
}

var report = JsonSerializer.Deserialize<CrashReportModel>(content);
if (report is null)
{
crashReportEntity = null!;
crashReportToMetadataEntity = null!;
crashReportToModuleMetadataEntities = null!;
return false;
}

var butrLoaderVersion = report.Metadata.AdditionalMetadata.FirstOrDefault(x => x.Key == "BUTRLoaderVersion")?.Value;
var blseVersion = report.Metadata.AdditionalMetadata.FirstOrDefault(x => x.Key == "BLSEVersion")?.Value;
var launcherExVersion = report.Metadata.AdditionalMetadata.FirstOrDefault(x => x.Key == "LauncherExVersion")?.Value;

var crashReportId = CrashReportId.From(report.Id);
crashReportEntity = new CrashReportEntity
{
TenantId = tenant,
CrashReportId = crashReportId,
Url = url,
Version = CrashReportVersion.From(report.Version),
GameVersion = GameVersion.From(report.Metadata.GameVersion),
ExceptionTypeId = FromException(report.Exception),
ExceptionType = unitOfWrite.UpsertEntityFactory.GetOrCreateExceptionType(FromException(report.Exception)),
Exception = GetException(report.Exception),
CreatedAt = fileId.Value.Length == 8 ? DateTimeOffset.UnixEpoch.ToUniversalTime() : date.ToUniversalTime(),
};
crashReportToMetadataEntity = new CrashReportToMetadataEntity
{
TenantId = tenant,
CrashReportId = crashReportId,
LauncherType = string.IsNullOrEmpty(report.Metadata.LauncherType) ? null : report.Metadata.LauncherType,
LauncherVersion = string.IsNullOrEmpty(report.Metadata.LauncherVersion) ? null : report.Metadata.LauncherVersion,
Runtime = string.IsNullOrEmpty(report.Metadata.Runtime) ? null : report.Metadata.Runtime,
BUTRLoaderVersion = butrLoaderVersion,
BLSEVersion = blseVersion,
LauncherExVersion = launcherExVersion,
OperatingSystemType = null,
OperatingSystemVersion = null,
};
crashReportToModuleMetadataEntities = report.Modules.DistinctBy(x => new { x.Id }).Select(x => new CrashReportToModuleMetadataEntity
{
TenantId = tenant,
CrashReportId = crashReportId,
ModuleId = ModuleId.From(x.Id),
Module = unitOfWrite.UpsertEntityFactory.GetOrCreateModule(ModuleId.From(x.Id)),
Version = ModuleVersion.From(x.Version),
NexusModsModId = NexusModsModId.TryParseUrl(x.Url, out var modId1) ? modId1 : null,
NexusModsMod = NexusModsModId.TryParseUrl(x.Url, out var modId2) ? unitOfWrite.UpsertEntityFactory.GetOrCreateNexusModsMod(modId2) : null,
InvolvedPosition = (byte) (report.InvolvedModules.IndexOf(y => y.ModuleOrLoaderPluginId == x.Id) + 1),
IsInvolved = report.InvolvedModules.Any(y => y.ModuleOrLoaderPluginId == x.Id),
}).ToArray();

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BUTR.CrashReport.Models" Version="14.0.0.84" />
<PackageReference Include="BUTR.CrashReport.Bannerlord.Parser" Version="14.0.0.84" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\BUTR.Site.NexusMods.Server.Persistence\BUTR.Site.NexusMods.Server.Persistence.csproj" />
<ProjectReference Include="..\BUTR.Site.NexusMods.Server.ValueObjects.Vogen\BUTR.Site.NexusMods.Server.ValueObjects.Vogen.csproj" />
</ItemGroup>

</Project>
147 changes: 147 additions & 0 deletions src/BUTR.Site.NexusMods.Server.CrashReport.v14/CrashReportV14.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.Json;
using BUTR.CrashReport.Bannerlord.Parser;
using BUTR.CrashReport.Models;
using BUTR.Site.NexusMods.Server.Extensions;
using BUTR.Site.NexusMods.Server.Models;
using BUTR.Site.NexusMods.Server.Models.Database;
using BUTR.Site.NexusMods.Server.Repositories;

namespace BUTR.Site.NexusMods.Server.CrashReport.v14;

public static class CrashReportV14
{
private static ExceptionTypeId FromException(ExceptionModel exception)
{
var exc = exception;
while (exc.InnerException is not null)
exc = exc.InnerException;

return ExceptionTypeId.From(exc.Type);
}

private static string GetException(ExceptionModel? exception, bool inner = false) => exception is null ? string.Empty : $"""
{(inner ? "Inner " : string.Empty)}Exception information
Type: {exception.Type}
Message: {exception.Message}
CallStack:
{exception.CallStack}
{GetException(exception.InnerException, true)}
""";

public static bool TryFromHtml(
IUnitOfWrite unitOfWrite,
TenantId tenant,
CrashReportFileId fileId,
CrashReportUrl url,
DateTime date,
byte version,
string content,
[NotNullWhen(true)] out CrashReportEntity? crashReportEntity,
[NotNullWhen(true)] out CrashReportToMetadataEntity? crashReportToMetadataEntity,
[NotNullWhen(true)] out IList<CrashReportToModuleMetadataEntity>? crashReportToModuleMetadataEntities)
{
var report = CrashReportParser.ParseLegacyHtml(version, content);
if (report is null)
{
crashReportEntity = null!;
crashReportToMetadataEntity = null!;
crashReportToModuleMetadataEntities = null!;
return false;
}
return TryFromModel(unitOfWrite, tenant, fileId, url, date, report, out crashReportEntity, out crashReportToMetadataEntity, out crashReportToModuleMetadataEntities);
}

public static bool TryFromJson(
IUnitOfWrite unitOfWrite,
TenantId tenant,
CrashReportFileId fileId,
CrashReportUrl url,
DateTime date,
byte version,
string content,
[NotNullWhen(true)] out CrashReportEntity? crashReportEntity,
[NotNullWhen(true)] out CrashReportToMetadataEntity? crashReportToMetadataEntity,
[NotNullWhen(true)] out IList<CrashReportToModuleMetadataEntity>? crashReportToModuleMetadataEntities)
{
if (version != 14)
{
crashReportEntity = null!;
crashReportToMetadataEntity = null!;
crashReportToModuleMetadataEntities = null!;
return false;
}

var report = JsonSerializer.Deserialize<CrashReportModel>(content);
if (report is null)
{
crashReportEntity = null!;
crashReportToMetadataEntity = null!;
crashReportToModuleMetadataEntities = null!;
return false;
}

return TryFromModel(unitOfWrite, tenant, fileId, url, date, report, out crashReportEntity, out crashReportToMetadataEntity, out crashReportToModuleMetadataEntities);
}
public static bool TryFromModel(
IUnitOfWrite unitOfWrite,
TenantId tenant,
CrashReportFileId fileId,
CrashReportUrl url,
DateTime date,
CrashReportModel report,
[NotNullWhen(true)] out CrashReportEntity? crashReportEntity,
[NotNullWhen(true)] out CrashReportToMetadataEntity? crashReportToMetadataEntity,
[NotNullWhen(true)] out IList<CrashReportToModuleMetadataEntity>? crashReportToModuleMetadataEntities)
{
var butrLoaderVersion = report.Metadata.AdditionalMetadata.FirstOrDefault(x => x.Key == "BUTRLoaderVersion")?.Value;
var blseVersion = report.Metadata.AdditionalMetadata.FirstOrDefault(x => x.Key == "BLSEVersion")?.Value;
var launcherExVersion = report.Metadata.AdditionalMetadata.FirstOrDefault(x => x.Key == "LauncherExVersion")?.Value;

var crashReportId = CrashReportId.From(report.Id);
crashReportEntity = new CrashReportEntity
{
TenantId = tenant,
CrashReportId = crashReportId,
Url = url,
Version = CrashReportVersion.From(report.Version),
GameVersion = GameVersion.From(report.Metadata.GameVersion),
ExceptionTypeId = FromException(report.Exception),
ExceptionType = unitOfWrite.UpsertEntityFactory.GetOrCreateExceptionType(FromException(report.Exception)),
Exception = GetException(report.Exception),
CreatedAt = fileId.Value.Length == 8 ? DateTimeOffset.UnixEpoch.ToUniversalTime() : date.ToUniversalTime(),
};
crashReportToMetadataEntity = new CrashReportToMetadataEntity
{
TenantId = tenant,
CrashReportId = crashReportId,
LauncherType = string.IsNullOrEmpty(report.Metadata.LauncherType) ? null : report.Metadata.LauncherType,
LauncherVersion = string.IsNullOrEmpty(report.Metadata.LauncherVersion) ? null : report.Metadata.LauncherVersion,
Runtime = string.IsNullOrEmpty(report.Metadata.Runtime) ? null : report.Metadata.Runtime,
BUTRLoaderVersion = butrLoaderVersion,
BLSEVersion = blseVersion,
LauncherExVersion = launcherExVersion,
OperatingSystemType = report.Metadata.OperatingSystemType.ToString(),
OperatingSystemVersion = report.Metadata.OperatingSystemVersion,
};
crashReportToModuleMetadataEntities = report.Modules.DistinctBy(x => new { x.Id }).Select(x => new CrashReportToModuleMetadataEntity
{
TenantId = tenant,
CrashReportId = crashReportId,
ModuleId = ModuleId.From(x.Id),
Module = unitOfWrite.UpsertEntityFactory.GetOrCreateModule(ModuleId.From(x.Id)),
Version = ModuleVersion.From(x.Version),
NexusModsModId = NexusModsModId.TryParseUrl(x.Url, out var modId1) ? modId1 : null,
NexusModsMod = NexusModsModId.TryParseUrl(x.Url, out var modId2) ? unitOfWrite.UpsertEntityFactory.GetOrCreateNexusModsMod(modId2) : null,
InvolvedPosition = (byte) (report.InvolvedModules.IndexOf(y => y.ModuleOrLoaderPluginId == x.Id) + 1),
IsInvolved = report.InvolvedModules.Any(y => y.ModuleOrLoaderPluginId == x.Id),
}).ToArray();

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
</PropertyGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\BUTR.Site.NexusMods.Server.ValueObjects.Vogen\BUTR.Site.NexusMods.Server.ValueObjects.Vogen.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Bannerlord.ModuleManager" Version="5.0.226" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ public sealed record CrashReportToMetadataEntity : IEntityWithTenant

public required string? BLSEVersion { get; init; }
public required string? LauncherExVersion { get; init; }

public required string? OperatingSystemType { get; init; }
public required string? OperatingSystemVersion { get; init; }


public override int GetHashCode() => HashCode.Combine(TenantId, CrashReportId, LauncherType, LauncherVersion, Runtime, BUTRLoaderVersion, BLSEVersion, LauncherExVersion);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using JetBrains.Annotations;

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;

namespace BUTR.Site.NexusMods.Server.Extensions;

/// <summary>
/// Provides extension methods for <see cref="IAsyncEnumerable{T}"/> objects.
/// </summary>
public static class IAsyncEnumerableExtensions
{
public static int IndexOf<T>(this IEnumerable<T> source, Predicate<T> predicate)
{
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(predicate);

var index = 0;
foreach (var item in source)
{
if (predicate(item))
return index;

index += 1;
}

return -1;
}
}
Loading

0 comments on commit de96e7e

Please sign in to comment.