feat: import messages from go version

This commit is contained in:
sam 2024-10-28 23:42:57 +01:00
parent b56a71e105
commit a50a8567dd
Signed by: sam
GPG key ID: 5F3C3C1B3166639D
15 changed files with 503 additions and 769 deletions

View file

@ -25,6 +25,7 @@ public class DatabaseConnection(Guid id, ILogger logger, NpgsqlConnection inner)
IDisposable
{
public Guid ConnectionId => id;
public NpgsqlConnection Inner => inner;
private readonly ILogger _logger = logger.ForContext<DatabaseConnection>();
private readonly DateTimeOffset _openTime = DateTimeOffset.UtcNow;

View file

@ -19,6 +19,9 @@ using NodaTime;
namespace Catalogger.Backend.Database;
/// <summary>
/// Manages database migrations.
/// </summary>
public class DatabaseMigrator(ILogger logger, IClock clock, DatabaseConnection conn)
: IDisposable,
IAsyncDisposable
@ -26,7 +29,10 @@ public class DatabaseMigrator(ILogger logger, IClock clock, DatabaseConnection c
private const string RootPath = "Catalogger.Backend.Database";
private static readonly int MigrationsPathLength = $"{RootPath}.Migrations.".Length;
public async Task Migrate()
/// <summary>
/// Migrates the database to the latest version.
/// </summary>
public async Task MigrateUp()
{
var migrations = GetMigrationNames().ToArray();
logger.Debug("Getting current database migration");
@ -65,6 +71,43 @@ public class DatabaseMigrator(ILogger logger, IClock clock, DatabaseConnection c
await tx.CommitAsync();
}
/// <summary>
/// Migrates the database to a previous version.
/// </summary>
/// <param name="count">The number of migrations to revert. If higher than the number of applied migrations,
/// reverts the database to a clean slate.</param>
public async Task MigrateDown(int count = 1)
{
await using var tx = await conn.BeginTransactionAsync();
var migrationCount = 0;
var totalStartTime = clock.GetCurrentInstant();
for (var i = count; i > 0; i--)
{
var migration = await GetCurrentMigration();
if (migration == null)
{
logger.Information(
"More down migrations requested than were in the database, finishing early"
);
break;
}
logger.Debug("Reverting migration {Migration}", migration);
var startTime = clock.GetCurrentInstant();
await ExecuteMigration(tx, migration.MigrationName, up: false);
var took = clock.GetCurrentInstant() - startTime;
logger.Debug("Reverted migration {Migration} in {Took}", migration, took);
migrationCount++;
}
var totalTook = clock.GetCurrentInstant() - totalStartTime;
logger.Information("Reverted {Count} migrations in {Took}", migrationCount, totalTook);
// Finally, commit the transaction
await tx.CommitAsync();
}
private async Task ExecuteMigration(DbTransaction tx, string migrationName, bool up = true)
{
var query = await GetResource(
@ -73,11 +116,17 @@ public class DatabaseMigrator(ILogger logger, IClock clock, DatabaseConnection c
// Run the migration
await conn.ExecuteAsync(query, transaction: tx);
// Store that we ran the migration
await conn.ExecuteAsync(
"INSERT INTO migrations (migration_name, applied_at) VALUES (@MigrationName, @AppliedAt)",
new { MigrationName = migrationName, AppliedAt = clock.GetCurrentInstant() }
);
// Store that we ran the migration (or reverted it)
if (up)
await conn.ExecuteAsync(
"INSERT INTO migrations (migration_name, applied_at) VALUES (@MigrationName, now())",
new { MigrationName = migrationName }
);
else
await conn.ExecuteAsync(
"DELETE FROM migrations WHERE migration_name = @MigrationName",
new { MigrationName = migrationName }
);
}
/// Returns the current migration. If no migrations have been applied, returns null

View file

@ -148,19 +148,17 @@ public class DatabasePool
Array.ConvertAll((long[])value, i => (ulong)i);
}
public class JsonTypeHandler<T> : SqlMapper.TypeHandler<T>
private class JsonTypeHandler<T> : SqlMapper.TypeHandler<T>
{
public override T Parse(object value)
{
string json = (string)value;
var json = (string)value;
return JsonSerializer.Deserialize<T>(json)
?? throw new CataloggerError("JsonTypeHandler<T> returned null");
}
public override void SetValue(IDbDataParameter parameter, T? value)
{
public override void SetValue(IDbDataParameter parameter, T? value) =>
parameter.Value = JsonSerializer.Serialize(value);
}
}
}

View file

@ -13,7 +13,6 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
using System.ComponentModel.DataAnnotations.Schema;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Rest.Core;
@ -22,10 +21,8 @@ namespace Catalogger.Backend.Database.Models;
public class Guild
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public required ulong Id { get; init; }
[Column(TypeName = "jsonb")]
public ChannelConfig Channels { get; init; } = new();
public string[] BannedSystems { get; set; } = [];
public ulong[] KeyRoles { get; set; } = [];

View file

@ -13,13 +13,10 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
using System.ComponentModel.DataAnnotations.Schema;
namespace Catalogger.Backend.Database.Models;
public class Message
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public required ulong Id { get; init; }
public ulong? OriginalId { get; set; }
@ -38,5 +35,3 @@ public class Message
public int AttachmentSize { get; set; } = 0;
}
public record IgnoredMessage([property: DatabaseGenerated(DatabaseGeneratedOption.None)] ulong Id);