Skip to content

Commit

Permalink
Added tenant support
Browse files Browse the repository at this point in the history
  • Loading branch information
Aragas committed Oct 10, 2024
1 parent a45c1bb commit e09773f
Show file tree
Hide file tree
Showing 41 changed files with 781 additions and 173 deletions.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ namespace BUTR.CrashReport.Server.Contexts;

public class AppDbContext : DbContext
{
public DbSet<ReportEntity> ReportEntities { get; set; }
public DbSet<IdEntity> IdEntities { get; set; }
public DbSet<FileEntity> FileEntities { get; set; }
public DbSet<HtmlEntity> HtmlEntities { get; set; }
public DbSet<JsonEntity> JsonEntities { get; set; }

public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
Expand All @@ -17,8 +18,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.ApplyConfiguration(new ReportEntityConfiguration());
modelBuilder.ApplyConfiguration(new IdEntityConfiguration());
modelBuilder.ApplyConfiguration(new FileEntityConfiguration());
modelBuilder.ApplyConfiguration(new HtmlEntityConfiguration());
modelBuilder.ApplyConfiguration(new JsonEntityConfiguration());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using BUTR.CrashReport.Server.Models.Database;

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace BUTR.CrashReport.Server.Contexts.Config;

public class HtmlEntityConfiguration : BaseEntityConfiguration<HtmlEntity>
{
protected override void ConfigureModel(EntityTypeBuilder<HtmlEntity> builder)
{
builder.Property(x => x.CrashReportId).HasColumnName("crash_report_id");
builder.Property(x => x.DataCompressed).HasColumnName("data_compressed");
builder.ToTable("html_entity").HasKey(x => x.CrashReportId).HasName("html_entity_pkey");

builder.HasOne(x => x.Id)
.WithOne()
.HasForeignKey<IdEntity>(x => x.CrashReportId)
.HasPrincipalKey<HtmlEntity>(x => x.CrashReportId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("html_entity_id_entity_fkey");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@ public class IdEntityConfiguration : BaseEntityConfiguration<IdEntity>
{
protected override void ConfigureModel(EntityTypeBuilder<IdEntity> builder)
{
builder.Property(x => x.FileId).HasColumnName("file_id");
builder.Property(x => x.CrashReportId).HasColumnName("crash_report_id");
builder.Property(x => x.Version).HasColumnName("version");
builder.Property(x => x.Created).HasColumnName("created");
builder.ToTable("id_entity").HasKey(x => x.FileId);
builder.Property(x => x.FileId).HasColumnName("file_id");
builder.ToTable("id_entity").HasKey(x => x.CrashReportId).HasName("id_entity_pkey");

builder.HasIndex(x => x.CrashReportId).IsUnique(false);
builder.HasIndex(x => x.FileId).IsUnique(false).HasDatabaseName("id_entity_file_id_idx");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using BUTR.CrashReport.Server.Models.Database;

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace BUTR.CrashReport.Server.Contexts.Config;

public class JsonEntityConfiguration : BaseEntityConfiguration<JsonEntity>
{
protected override void ConfigureModel(EntityTypeBuilder<JsonEntity> builder)
{
builder.Property(x => x.CrashReportId).HasColumnName("crash_report_id");
builder.Property(x => x.Json).HasColumnName("data").HasColumnType("jsonb");
builder.ToTable("json_entity").HasKey(x => x.CrashReportId);

builder.HasOne(x => x.Id)
.WithOne()
.HasForeignKey<IdEntity>(x => x.CrashReportId)
.HasPrincipalKey<JsonEntity>(x => x.CrashReportId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("json_entity_id_entity_fkey");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using BUTR.CrashReport.Server.Models.Database;

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace BUTR.CrashReport.Server.Contexts.Config;

public class ReportEntityConfiguration : BaseEntityConfiguration<ReportEntity>
{
protected override void ConfigureModel(EntityTypeBuilder<ReportEntity> builder)
{
builder.Property(x => x.CrashReportId).HasColumnName("crash_report_id");
builder.Property(x => x.Tenant).HasColumnName("tenant");
builder.Property(x => x.Version).HasColumnName("version");
builder.Property(x => x.Created).HasColumnName("created");
builder.ToTable("report_entity").HasKey(x => x.CrashReportId).HasName("report_entity_pkey");

builder.HasOne(x => x.Id)
.WithOne(x => x.Report)
.HasForeignKey<IdEntity>(x => x.CrashReportId)
.HasPrincipalKey<ReportEntity>(x => x.CrashReportId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("report_entity_id_entity_fkey");

builder.HasOne(x => x.Html)
.WithOne(x => x.Report)
.HasForeignKey<HtmlEntity>(x => x.CrashReportId)
.HasPrincipalKey<ReportEntity>(x => x.CrashReportId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("report_entity_html_entity_fkey");

builder.HasOne(x => x.Json)
.WithOne(x => x.Report)
.HasForeignKey<JsonEntity>(x => x.CrashReportId)
.HasPrincipalKey<ReportEntity>(x => x.CrashReportId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("report_entity_json_entity_fkey");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace BUTR.CrashReport.Server.Models.Database;

public sealed record HtmlEntity : IEntity
{
public required Guid CrashReportId { get; set; }
public required byte[] DataCompressed { get; set; }

public ReportEntity? Report { get; set; }
public IdEntity? Id { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ namespace BUTR.CrashReport.Server.Models.Database;

public sealed record IdEntity : IEntity
{
public required string FileId { get; set; }
public required Guid CrashReportId { get; set; }
public required byte Version { get; set; }
public required DateTime Created { get; set; }
public required string FileId { get; set; }

public ReportEntity? Report { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace BUTR.CrashReport.Server.Models.Database;

public sealed record JsonEntity : IEntity
{
public required Guid CrashReportId { get; set; }
public required string Json { get; set; }

public ReportEntity? Report { get; set; }
public IdEntity? Id { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;

namespace BUTR.CrashReport.Server.Models.Database;

public sealed record ReportEntity : IEntity
{
public required Guid CrashReportId { get; set; }
public required byte Tenant { get; set; }
public required byte Version { get; set; }
public required DateTime Created { get; set; }

public IdEntity? Id { get; set; }
public HtmlEntity? Html { get; set; }
public JsonEntity? Json { get; set; }
}
2 changes: 1 addition & 1 deletion src/BUTR.CrashReport.Server.sln
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Server.v13
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Server.v14", "BUTR.CrashReport.Server.v14\BUTR.CrashReport.Server.v14.csproj", "{0448DA45-5D06-4D7B-9F0D-37BCB26BD82F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Server.Base", "BUTR.CrashReport.Server.Base\BUTR.CrashReport.Server.Base.csproj", "{1EB86E89-9CEE-465D-8F0F-69BB0D06A3DA}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Server.Persistence", "BUTR.CrashReport.Server.Persistence\BUTR.CrashReport.Server.Persistence.csproj", "{1EB86E89-9CEE-465D-8F0F-69BB0D06A3DA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\BUTR.CrashReport.Server.Base\BUTR.CrashReport.Server.Base.csproj" PrivateAssets="all" IncludeAssets="compile" Private="false" />
<ProjectReference Include="..\BUTR.CrashReport.Server.Persistence\BUTR.CrashReport.Server.Persistence.csproj" PrivateAssets="all" IncludeAssets="compile" Private="false" />
</ItemGroup>

</Project>
25 changes: 18 additions & 7 deletions src/BUTR.CrashReport.Server.v13/HtmlHandlerV13.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class HtmlHandlerV13
private readonly FileIdGenerator _fileIdGenerator;
private CrashUploadOptions _options;

private readonly Counter<int> _reportTenant;
private readonly Counter<int> _reportVersion;

public HtmlHandlerV13(
Expand All @@ -46,6 +47,7 @@ public HtmlHandlerV13(
IMeterFactory meterFactory)
{
var meter = meterFactory.Create("BUTR.CrashReportServer.Controllers.CrashUploadController", "1.0.0");
_reportTenant = meter.CreateCounter<int>("report-tenant", unit: "Count");
_reportVersion = meter.CreateCounter<int>("report-version", unit: "Count");

_logger = logger ?? throw new ArgumentNullException(nameof(logger));
Expand All @@ -61,32 +63,41 @@ public async Task<IActionResult> UploadHtmlAsync(ControllerBase controller, Canc
{
controller.Request.EnableBuffering();

var tenant = byte.TryParse(controller.Request.Headers["Tenant"].ToString(), out var tenantId) ? tenantId : (byte) 0;

using var streamReader = new StreamReader(controller.Request.Body);
var html = await streamReader.ReadToEndAsync(ct);
var (valid, version, crashReportModel) = ParseHtml(html);
if (!valid)
if (!valid || crashReportModel is null)
{
_logger.LogWarning("Invalid HTML");
return controller.StatusCode(StatusCodes.Status500InternalServerError);
}

if (await _dbContext.IdEntities.FirstOrDefaultAsync(x => x.CrashReportId == crashReportModel!.Id, ct) is { } idEntity)
if (await _dbContext.IdEntities.FirstOrDefaultAsync(x => x.CrashReportId == crashReportModel.Id, ct) is { } idEntity)
return controller.Ok($"{_options.BaseUri}/{idEntity.FileId}");

var json = JsonSerializer.Serialize(crashReportModel, _jsonSerializerOptionsWeb);

controller.Request.Body.Seek(0, SeekOrigin.Begin);
await using var compressedHtmlStream = await _gZipCompressor.CompressAsync(controller.Request.Body, ct);

idEntity = new IdEntity { FileId = _fileIdGenerator.Generate(ct), CrashReportId = crashReportModel!.Id, Version = version, Created = DateTime.UtcNow, };
await _dbContext.IdEntities.AddAsync(idEntity, ct);
await _dbContext.FileEntities.AddAsync(new FileEntity { FileId = idEntity.FileId, DataCompressed = compressedHtmlStream.ToArray(), }, ct);
if (version >= 13) await _dbContext.JsonEntities.AddAsync(new JsonEntity { FileId = idEntity.FileId, CrashReport = json, }, ct);
await _dbContext.ReportEntities.AddAsync(new ReportEntity
{
CrashReportId = crashReportModel.Id,
Tenant = tenant,
Version = version,
Created = DateTime.UtcNow,
}, ct);
await _dbContext.IdEntities.AddAsync(idEntity = new IdEntity { CrashReportId = crashReportModel.Id, FileId = _fileIdGenerator.Generate(ct), }, ct);
await _dbContext.HtmlEntities.AddAsync(new HtmlEntity { CrashReportId = crashReportModel.Id, DataCompressed = compressedHtmlStream.ToArray(), }, ct);
if (version >= 13) await _dbContext.JsonEntities.AddAsync(new JsonEntity { CrashReportId = crashReportModel.Id, Json = json, }, ct);
await _dbContext.SaveChangesAsync(ct);

_reportTenant.Add(1, new[] { new KeyValuePair<string, object?>("Tenant", tenant) });
_reportVersion.Add(1, new[] { new KeyValuePair<string, object?>("Version", version) });

return controller.Ok($"{_options.BaseUri}/{idEntity.FileId}");
return controller.Ok(tenant == 0 ? $"{_options.BaseUri}/{idEntity.FileId}" : $"{_options.BaseUri}/{tenant}/{idEntity.FileId}");
}

private static (bool isValid, byte version, CrashReportModel? crashReportModel) ParseHtml(string html)
Expand Down
Loading

0 comments on commit e09773f

Please sign in to comment.