From 64bea79ef74eba4cebd06a576f6c2dd2c84fe10c Mon Sep 17 00:00:00 2001 From: Noi Date: Tue, 23 Aug 2022 19:57:04 -0700 Subject: [PATCH] ModLogEntry: Add composite foreign key reference --- Data/BotDatabaseContext.cs | 7 +- Data/CachedGuildMessage.cs | 5 +- Data/CachedGuildUser.cs | 5 + Data/CachedUser.cs | 2 +- .../20220824023321_AddModLogs.Designer.cs | 232 ++++++++++++++++++ Data/Migrations/20220824023321_AddModLogs.cs | 55 +++++ .../BotDatabaseContextModelSnapshot.cs | 63 +++++ Data/ModLogEntry.cs | 11 +- 8 files changed, 373 insertions(+), 7 deletions(-) create mode 100644 Data/Migrations/20220824023321_AddModLogs.Designer.cs create mode 100644 Data/Migrations/20220824023321_AddModLogs.cs diff --git a/Data/BotDatabaseContext.cs b/Data/BotDatabaseContext.cs index bb269f9..d1e1a1b 100644 --- a/Data/BotDatabaseContext.cs +++ b/Data/BotDatabaseContext.cs @@ -54,6 +54,11 @@ public class BotDatabaseContext : DbContext { }); modelBuilder.Entity(e => e.Property(p => p.CreatedAt).HasDefaultValueSql("now()")); modelBuilder.HasPostgresEnum(); - modelBuilder.Entity(e => e.Property(p => p.Timestamp).HasDefaultValueSql("now()")); + modelBuilder.Entity(e => { + e.Property(p => p.Timestamp).HasDefaultValueSql("now()"); + e.HasOne(entry => entry.User) + .WithMany(gu => gu.Logs) + .HasForeignKey(entry => new {entry.GuildId, entry.UserId}); + }); } } diff --git a/Data/CachedGuildMessage.cs b/Data/CachedGuildMessage.cs index 34b7b3b..c39aa4d 100644 --- a/Data/CachedGuildMessage.cs +++ b/Data/CachedGuildMessage.cs @@ -53,12 +53,11 @@ public class CachedGuildMessage { /// public string? Content { get; set; } = null!; - /// - /// If included in the query, references the associated for this entry. - /// + /// [ForeignKey(nameof(AuthorId))] [InverseProperty(nameof(CachedUser.GuildMessages))] public CachedUser Author { get; set; } = null!; + // TODO set up composite foreign key. will require rewriting some parts in modules... // Used by MessageCachingSubservice internal static CachedGuildMessage? Clone(CachedGuildMessage? original) { diff --git a/Data/CachedGuildUser.cs b/Data/CachedGuildUser.cs index 3a6f14a..6d57b1c 100644 --- a/Data/CachedGuildUser.cs +++ b/Data/CachedGuildUser.cs @@ -33,4 +33,9 @@ public class CachedGuildUser { [ForeignKey(nameof(UserId))] [InverseProperty(nameof(CachedUser.Guilds))] public CachedUser User { get; set; } = null!; + + /// + /// If included in the query, references all items associated with this entry. + /// + public ICollection Logs { get; set; } = null!; } diff --git a/Data/CachedUser.cs b/Data/CachedUser.cs index dfdcfae..939d27b 100644 --- a/Data/CachedUser.cs +++ b/Data/CachedUser.cs @@ -8,7 +8,7 @@ namespace RegexBot.Data; [Table("cache_users")] public class CachedUser { /// - /// Gets the user's snowflake ID. + /// Gets the associated user's snowflake ID. /// [Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] diff --git a/Data/Migrations/20220824023321_AddModLogs.Designer.cs b/Data/Migrations/20220824023321_AddModLogs.Designer.cs new file mode 100644 index 0000000..70fd9f9 --- /dev/null +++ b/Data/Migrations/20220824023321_AddModLogs.Designer.cs @@ -0,0 +1,232 @@ +// +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using RegexBot.Data; + +#nullable disable + +namespace RegexBot.Data.Migrations +{ + [DbContext(typeof(BotDatabaseContext))] + [Migration("20220824023321_AddModLogs")] + partial class AddModLogs + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "mod_log_type", new[] { "other", "note", "warn", "timeout", "kick", "ban" }); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("RegexBot.Data.CachedGuildMessage", b => + { + b.Property("MessageId") + .HasColumnType("bigint") + .HasColumnName("message_id"); + + b.Property>("AttachmentNames") + .IsRequired() + .HasColumnType("text[]") + .HasColumnName("attachment_names"); + + b.Property("AuthorId") + .HasColumnType("bigint") + .HasColumnName("author_id"); + + b.Property("ChannelId") + .HasColumnType("bigint") + .HasColumnName("channel_id"); + + b.Property("Content") + .HasColumnType("text") + .HasColumnName("content"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now()"); + + b.Property("EditedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("edited_at"); + + b.Property("GuildId") + .HasColumnType("bigint") + .HasColumnName("guild_id"); + + b.HasKey("MessageId") + .HasName("pk_cache_guildmessages"); + + b.HasIndex("AuthorId") + .HasDatabaseName("ix_cache_guildmessages_author_id"); + + b.ToTable("cache_guildmessages", (string)null); + }); + + modelBuilder.Entity("RegexBot.Data.CachedGuildUser", b => + { + b.Property("UserId") + .HasColumnType("bigint") + .HasColumnName("user_id"); + + b.Property("GuildId") + .HasColumnType("bigint") + .HasColumnName("guild_id"); + + b.Property("FirstSeenTime") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("first_seen_time") + .HasDefaultValueSql("now()"); + + b.Property("GULastUpdateTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("gu_last_update_time"); + + b.Property("Nickname") + .HasColumnType("text") + .HasColumnName("nickname"); + + b.HasKey("UserId", "GuildId") + .HasName("pk_cache_usersinguild"); + + b.ToTable("cache_usersinguild", (string)null); + }); + + modelBuilder.Entity("RegexBot.Data.CachedUser", b => + { + b.Property("UserId") + .HasColumnType("bigint") + .HasColumnName("user_id"); + + b.Property("AvatarUrl") + .HasColumnType("text") + .HasColumnName("avatar_url"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(4) + .HasColumnType("character(4)") + .HasColumnName("discriminator") + .IsFixedLength(); + + b.Property("ULastUpdateTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("u_last_update_time"); + + b.Property("Username") + .IsRequired() + .HasColumnType("text") + .HasColumnName("username"); + + b.HasKey("UserId") + .HasName("pk_cache_users"); + + b.ToTable("cache_users", (string)null); + }); + + modelBuilder.Entity("RegexBot.Data.ModLogEntry", b => + { + b.Property("LogId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("log_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("LogId")); + + b.Property("GuildId") + .HasColumnType("bigint") + .HasColumnName("guild_id"); + + b.Property("IssuedBy") + .IsRequired() + .HasColumnType("text") + .HasColumnName("issued_by"); + + b.Property("LogType") + .HasColumnType("integer") + .HasColumnName("log_type"); + + b.Property("Message") + .HasColumnType("text") + .HasColumnName("message"); + + b.Property("Timestamp") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("timestamp") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("bigint") + .HasColumnName("user_id"); + + b.HasKey("LogId") + .HasName("pk_modlogs"); + + b.HasIndex("GuildId", "UserId") + .HasDatabaseName("ix_modlogs_guild_id_user_id"); + + b.ToTable("modlogs", (string)null); + }); + + modelBuilder.Entity("RegexBot.Data.CachedGuildMessage", b => + { + b.HasOne("RegexBot.Data.CachedUser", "Author") + .WithMany("GuildMessages") + .HasForeignKey("AuthorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_cache_guildmessages_cache_users_author_id"); + + b.Navigation("Author"); + }); + + modelBuilder.Entity("RegexBot.Data.CachedGuildUser", b => + { + b.HasOne("RegexBot.Data.CachedUser", "User") + .WithMany("Guilds") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_cache_usersinguild_cache_users_user_id"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("RegexBot.Data.ModLogEntry", b => + { + b.HasOne("RegexBot.Data.CachedGuildUser", "User") + .WithMany("Logs") + .HasForeignKey("GuildId", "UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_modlogs_cache_usersinguild_user_temp_id"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("RegexBot.Data.CachedGuildUser", b => + { + b.Navigation("Logs"); + }); + + modelBuilder.Entity("RegexBot.Data.CachedUser", b => + { + b.Navigation("GuildMessages"); + + b.Navigation("Guilds"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Data/Migrations/20220824023321_AddModLogs.cs b/Data/Migrations/20220824023321_AddModLogs.cs new file mode 100644 index 0000000..3112a19 --- /dev/null +++ b/Data/Migrations/20220824023321_AddModLogs.cs @@ -0,0 +1,55 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace RegexBot.Data.Migrations +{ + public partial class AddModLogs : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:mod_log_type", "other,note,warn,timeout,kick,ban"); + + migrationBuilder.CreateTable( + name: "modlogs", + columns: table => new + { + log_id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + timestamp = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + guild_id = table.Column(type: "bigint", nullable: false), + user_id = table.Column(type: "bigint", nullable: false), + log_type = table.Column(type: "integer", nullable: false), + issued_by = table.Column(type: "text", nullable: false), + message = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("pk_modlogs", x => x.log_id); + table.ForeignKey( + name: "fk_modlogs_cache_usersinguild_user_temp_id", + columns: x => new { x.guild_id, x.user_id }, + principalTable: "cache_usersinguild", + principalColumns: new[] { "user_id", "guild_id" }, + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "ix_modlogs_guild_id_user_id", + table: "modlogs", + columns: new[] { "guild_id", "user_id" }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "modlogs"); + + migrationBuilder.AlterDatabase() + .OldAnnotation("Npgsql:Enum:mod_log_type", "other,note,warn,timeout,kick,ban"); + } + } +} diff --git a/Data/Migrations/BotDatabaseContextModelSnapshot.cs b/Data/Migrations/BotDatabaseContextModelSnapshot.cs index 038cc06..75b2917 100644 --- a/Data/Migrations/BotDatabaseContextModelSnapshot.cs +++ b/Data/Migrations/BotDatabaseContextModelSnapshot.cs @@ -21,6 +21,7 @@ namespace RegexBot.Data.Migrations .HasAnnotation("ProductVersion", "6.0.7") .HasAnnotation("Relational:MaxIdentifierLength", 63); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "mod_log_type", new[] { "other", "note", "warn", "timeout", "kick", "ban" }); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); modelBuilder.Entity("RegexBot.Data.CachedGuildMessage", b => @@ -131,6 +132,51 @@ namespace RegexBot.Data.Migrations b.ToTable("cache_users", (string)null); }); + modelBuilder.Entity("RegexBot.Data.ModLogEntry", b => + { + b.Property("LogId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("log_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("LogId")); + + b.Property("GuildId") + .HasColumnType("bigint") + .HasColumnName("guild_id"); + + b.Property("IssuedBy") + .IsRequired() + .HasColumnType("text") + .HasColumnName("issued_by"); + + b.Property("LogType") + .HasColumnType("integer") + .HasColumnName("log_type"); + + b.Property("Message") + .HasColumnType("text") + .HasColumnName("message"); + + b.Property("Timestamp") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("timestamp") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("bigint") + .HasColumnName("user_id"); + + b.HasKey("LogId") + .HasName("pk_modlogs"); + + b.HasIndex("GuildId", "UserId") + .HasDatabaseName("ix_modlogs_guild_id_user_id"); + + b.ToTable("modlogs", (string)null); + }); + modelBuilder.Entity("RegexBot.Data.CachedGuildMessage", b => { b.HasOne("RegexBot.Data.CachedUser", "Author") @@ -155,6 +201,23 @@ namespace RegexBot.Data.Migrations b.Navigation("User"); }); + modelBuilder.Entity("RegexBot.Data.ModLogEntry", b => + { + b.HasOne("RegexBot.Data.CachedGuildUser", "User") + .WithMany("Logs") + .HasForeignKey("GuildId", "UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_modlogs_cache_usersinguild_user_temp_id"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("RegexBot.Data.CachedGuildUser", b => + { + b.Navigation("Logs"); + }); + modelBuilder.Entity("RegexBot.Data.CachedUser", b => { b.Navigation("GuildMessages"); diff --git a/Data/ModLogEntry.cs b/Data/ModLogEntry.cs index 99b1011..1b553f3 100644 --- a/Data/ModLogEntry.cs +++ b/Data/ModLogEntry.cs @@ -7,7 +7,7 @@ namespace RegexBot.Data; /// Represents a moderation log entry. /// [Table("modlogs")] -public class ModLogEntry { +public class ModLogEntry : ISharedEvent { /// /// Gets the ID number for this entry. /// @@ -22,7 +22,9 @@ public class ModLogEntry { /// public long GuildId { get; set; } - /// + /// + /// Gets the ID of the users for which this log entry pertains. + /// public long UserId { get; set; } /// @@ -40,4 +42,9 @@ public class ModLogEntry { /// Gets any additional message associated with this log entry. /// public string? Message { get; set; } + + /// + /// If included in the query, gets the associated for this entry. + /// + public CachedGuildUser User { get; set; } = null!; } \ No newline at end of file