diff --git a/Kyoo.CommonAPI/DatabaseContext.cs b/Kyoo.CommonAPI/DatabaseContext.cs index f3d7f867..cb6762b0 100644 --- a/Kyoo.CommonAPI/DatabaseContext.cs +++ b/Kyoo.CommonAPI/DatabaseContext.cs @@ -140,6 +140,23 @@ namespace Kyoo .Property(t => t.IsForced) .ValueGeneratedNever(); + modelBuilder.Entity() + .HasMany(x => x.Seasons) + .WithOne(x => x.Show) + .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity() + .HasMany(x => x.Episodes) + .WithOne(x => x.Show) + .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity() + .HasMany(x => x.Episodes) + .WithOne(x => x.Season) + .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity() + .HasMany(x => x.Tracks) + .WithOne(x => x.Episode) + .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity() .HasMany(x => x.Libraries) .WithMany(x => x.Providers) diff --git a/Kyoo.Postgresql/Kyoo.Postgresql.csproj b/Kyoo.Postgresql/Kyoo.Postgresql.csproj index 1add3667..d4fbfb84 100644 --- a/Kyoo.Postgresql/Kyoo.Postgresql.csproj +++ b/Kyoo.Postgresql/Kyoo.Postgresql.csproj @@ -27,14 +27,14 @@ - all - false - runtime + + + - all - false - runtime + + + diff --git a/Kyoo.Postgresql/Migrations/20210616203804_Initial.Designer.cs b/Kyoo.Postgresql/Migrations/20210619153358_Initial.Designer.cs similarity index 99% rename from Kyoo.Postgresql/Migrations/20210616203804_Initial.Designer.cs rename to Kyoo.Postgresql/Migrations/20210619153358_Initial.Designer.cs index 83f90e5c..a4228834 100644 --- a/Kyoo.Postgresql/Migrations/20210616203804_Initial.Designer.cs +++ b/Kyoo.Postgresql/Migrations/20210619153358_Initial.Designer.cs @@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace Kyoo.Postgresql.Migrations { [DbContext(typeof(PostgresContext))] - [Migration("20210616203804_Initial")] + [Migration("20210619153358_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -634,7 +634,8 @@ namespace Kyoo.Postgresql.Migrations { b.HasOne("Kyoo.Models.Season", "Season") .WithMany("Episodes") - .HasForeignKey("SeasonID"); + .HasForeignKey("SeasonID") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Kyoo.Models.Show", "Show") .WithMany("Episodes") diff --git a/Kyoo.Postgresql/Migrations/20210616203804_Initial.cs b/Kyoo.Postgresql/Migrations/20210619153358_Initial.cs similarity index 99% rename from Kyoo.Postgresql/Migrations/20210616203804_Initial.cs rename to Kyoo.Postgresql/Migrations/20210619153358_Initial.cs index 94367a9d..d6d70251 100644 --- a/Kyoo.Postgresql/Migrations/20210616203804_Initial.cs +++ b/Kyoo.Postgresql/Migrations/20210619153358_Initial.cs @@ -432,7 +432,7 @@ namespace Kyoo.Postgresql.Migrations column: x => x.SeasonID, principalTable: "Seasons", principalColumn: "ID", - onDelete: ReferentialAction.Restrict); + onDelete: ReferentialAction.Cascade); table.ForeignKey( name: "FK_Episodes_Shows_ShowID", column: x => x.ShowID, diff --git a/Kyoo.Postgresql/Migrations/PostgresContextModelSnapshot.cs b/Kyoo.Postgresql/Migrations/PostgresContextModelSnapshot.cs index a88a7ab2..f4ef9dd2 100644 --- a/Kyoo.Postgresql/Migrations/PostgresContextModelSnapshot.cs +++ b/Kyoo.Postgresql/Migrations/PostgresContextModelSnapshot.cs @@ -632,7 +632,8 @@ namespace Kyoo.Postgresql.Migrations { b.HasOne("Kyoo.Models.Season", "Season") .WithMany("Episodes") - .HasForeignKey("SeasonID"); + .HasForeignKey("SeasonID") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Kyoo.Models.Show", "Show") .WithMany("Episodes") diff --git a/Kyoo.SqLite/Migrations/20210613135157_Initial.Designer.cs b/Kyoo.SqLite/Migrations/20210619154617_Initial.Designer.cs similarity index 99% rename from Kyoo.SqLite/Migrations/20210613135157_Initial.Designer.cs rename to Kyoo.SqLite/Migrations/20210619154617_Initial.Designer.cs index 188c7b25..6dc81b0e 100644 --- a/Kyoo.SqLite/Migrations/20210613135157_Initial.Designer.cs +++ b/Kyoo.SqLite/Migrations/20210619154617_Initial.Designer.cs @@ -9,7 +9,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Kyoo.SqLite.Migrations { [DbContext(typeof(SqLiteContext))] - [Migration("20210613135157_Initial")] + [Migration("20210619154617_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -614,7 +614,8 @@ namespace Kyoo.SqLite.Migrations { b.HasOne("Kyoo.Models.Season", "Season") .WithMany("Episodes") - .HasForeignKey("SeasonID"); + .HasForeignKey("SeasonID") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Kyoo.Models.Show", "Show") .WithMany("Episodes") diff --git a/Kyoo.SqLite/Migrations/20210613135157_Initial.cs b/Kyoo.SqLite/Migrations/20210619154617_Initial.cs similarity index 99% rename from Kyoo.SqLite/Migrations/20210613135157_Initial.cs rename to Kyoo.SqLite/Migrations/20210619154617_Initial.cs index bece453b..a2bbf56f 100644 --- a/Kyoo.SqLite/Migrations/20210613135157_Initial.cs +++ b/Kyoo.SqLite/Migrations/20210619154617_Initial.cs @@ -424,7 +424,7 @@ namespace Kyoo.SqLite.Migrations column: x => x.SeasonID, principalTable: "Seasons", principalColumn: "ID", - onDelete: ReferentialAction.Restrict); + onDelete: ReferentialAction.Cascade); table.ForeignKey( name: "FK_Episodes_Shows_ShowID", column: x => x.ShowID, diff --git a/Kyoo.SqLite/Triggers/TriggersMigrations.Designer.cs b/Kyoo.SqLite/Migrations/20210619154654_Triggers.Designer.cs similarity index 99% rename from Kyoo.SqLite/Triggers/TriggersMigrations.Designer.cs rename to Kyoo.SqLite/Migrations/20210619154654_Triggers.Designer.cs index 83bbdcd1..01d5e928 100644 --- a/Kyoo.SqLite/Triggers/TriggersMigrations.Designer.cs +++ b/Kyoo.SqLite/Migrations/20210619154654_Triggers.Designer.cs @@ -9,7 +9,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Kyoo.SqLite.Migrations { [DbContext(typeof(SqLiteContext))] - [Migration("20210613135215_Triggers")] + [Migration("20210619154654_Triggers")] partial class Triggers { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -614,7 +614,8 @@ namespace Kyoo.SqLite.Migrations { b.HasOne("Kyoo.Models.Season", "Season") .WithMany("Episodes") - .HasForeignKey("SeasonID"); + .HasForeignKey("SeasonID") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Kyoo.Models.Show", "Show") .WithMany("Episodes") diff --git a/Kyoo.SqLite/Triggers/TriggersMigrations.cs b/Kyoo.SqLite/Migrations/20210619154654_Triggers.cs similarity index 57% rename from Kyoo.SqLite/Triggers/TriggersMigrations.cs rename to Kyoo.SqLite/Migrations/20210619154654_Triggers.cs index 242cb4a1..2dce467d 100644 --- a/Kyoo.SqLite/Triggers/TriggersMigrations.cs +++ b/Kyoo.SqLite/Migrations/20210619154654_Triggers.cs @@ -2,34 +2,34 @@ namespace Kyoo.SqLite.Migrations { - public partial class Triggers : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.Sql(@" + public partial class Triggers : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql(@" CREATE TRIGGER SeasonSlugInsert AFTER INSERT ON Seasons FOR EACH ROW BEGIN UPDATE Seasons SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) || '-s' || SeasonNumber WHERE ID == new.ID; END"); - migrationBuilder.Sql(@" + migrationBuilder.Sql(@" CREATE TRIGGER SeasonSlugUpdate AFTER UPDATE OF SeasonNumber, ShowID ON Seasons FOR EACH ROW BEGIN UPDATE Seasons SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) || '-s' || SeasonNumber WHERE ID == new.ID; END"); - migrationBuilder.Sql(@" + migrationBuilder.Sql(@" CREATE TRIGGER ShowSlugUpdate AFTER UPDATE OF Slug ON Shows FOR EACH ROW BEGIN UPDATE Seasons SET Slug = new.Slug || '-s' || SeasonNumber WHERE ShowID = new.ID; END;"); - } + } - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.Sql("DROP TRIGGER SeasonSlugInsert;"); - migrationBuilder.Sql("DROP TRIGGER SeasonSlugUpdate;"); - migrationBuilder.Sql("DROP TRIGGER ShowSlugUpdate;"); - } - } -} + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DROP TRIGGER SeasonSlugInsert;"); + migrationBuilder.Sql("DROP TRIGGER SeasonSlugUpdate;"); + migrationBuilder.Sql("DROP TRIGGER ShowSlugUpdate;"); + } + } +} \ No newline at end of file diff --git a/Kyoo.SqLite/Migrations/SqLiteContextModelSnapshot.cs b/Kyoo.SqLite/Migrations/SqLiteContextModelSnapshot.cs index 16c6105a..a03f5cbe 100644 --- a/Kyoo.SqLite/Migrations/SqLiteContextModelSnapshot.cs +++ b/Kyoo.SqLite/Migrations/SqLiteContextModelSnapshot.cs @@ -612,7 +612,8 @@ namespace Kyoo.SqLite.Migrations { b.HasOne("Kyoo.Models.Season", "Season") .WithMany("Episodes") - .HasForeignKey("SeasonID"); + .HasForeignKey("SeasonID") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Kyoo.Models.Show", "Show") .WithMany("Episodes") diff --git a/Kyoo.Tests/Library/SpecificTests/GlobalTests.cs b/Kyoo.Tests/Library/SpecificTests/SanityTests.cs similarity index 51% rename from Kyoo.Tests/Library/SpecificTests/GlobalTests.cs rename to Kyoo.Tests/Library/SpecificTests/SanityTests.cs index 57b8c4c3..7d7794fa 100644 --- a/Kyoo.Tests/Library/SpecificTests/GlobalTests.cs +++ b/Kyoo.Tests/Library/SpecificTests/SanityTests.cs @@ -23,27 +23,6 @@ namespace Kyoo.Tests.SpecificTests Assert.False(ReferenceEquals(TestSample.Get(), TestSample.Get())); } - [Fact] - public async Task DeleteShowWithEpisodeAndSeason() - { - Show show = TestSample.Get(); - show.Seasons = new[] - { - TestSample.Get() - }; - show.Seasons.First().Episodes = new[] - { - TestSample.Get() - }; - await _repositories.Context.AddAsync(show); - - Assert.Equal(1, await _repositories.LibraryManager.ShowRepository.GetCount()); - await _repositories.LibraryManager.ShowRepository.Delete(show); - Assert.Equal(0, await _repositories.LibraryManager.ShowRepository.GetCount()); - Assert.Equal(0, await _repositories.LibraryManager.SeasonRepository.GetCount()); - Assert.Equal(0, await _repositories.LibraryManager.EpisodeRepository.GetCount()); - } - public void Dispose() { _repositories.Dispose(); diff --git a/Kyoo.Tests/Library/SpecificTests/SeasonTests.cs b/Kyoo.Tests/Library/SpecificTests/SeasonTests.cs index 7690416a..bb529e05 100644 --- a/Kyoo.Tests/Library/SpecificTests/SeasonTests.cs +++ b/Kyoo.Tests/Library/SpecificTests/SeasonTests.cs @@ -12,6 +12,14 @@ namespace Kyoo.Tests.SpecificTests { } } + [Collection(nameof(Postgresql))] + public class PostgresSeasonTests : SeasonTests + { + public PostgresSeasonTests(PostgresFixture postgres) + : base(new RepositoryActivator(postgres)) + { } + } + public abstract class SeasonTests : RepositoryTests { private readonly ISeasonRepository _repository; diff --git a/Kyoo.Tests/Library/SpecificTests/ShowTests.cs b/Kyoo.Tests/Library/SpecificTests/ShowTests.cs index 33977be2..2f1bf331 100644 --- a/Kyoo.Tests/Library/SpecificTests/ShowTests.cs +++ b/Kyoo.Tests/Library/SpecificTests/ShowTests.cs @@ -268,5 +268,21 @@ namespace Kyoo.Tests.SpecificTests ICollection ret = await _repository.Search(query); KAssert.DeepEqual(value, ret.First()); } + + [Fact] + public async Task DeleteShowWithEpisodeAndSeason() + { + Show show = TestSample.Get(); + await Repositories.LibraryManager.Load(show, x => x.Seasons); + await Repositories.LibraryManager.Load(show, x => x.Episodes); + Assert.Equal(1, await _repository.GetCount()); + Assert.Equal(1, show.Seasons.Count); + Assert.Equal(1, show.Episodes.Count); + await _repository.Delete(show); + Assert.Equal(0, await Repositories.LibraryManager.ShowRepository.GetCount()); + Assert.Equal(0, await Repositories.LibraryManager.SeasonRepository.GetCount()); + Assert.Equal(0, await Repositories.LibraryManager.EpisodeRepository.GetCount()); + } + } } \ No newline at end of file diff --git a/Kyoo.Tests/Library/TestContext.cs b/Kyoo.Tests/Library/TestContext.cs index 0ecb4637..1662e12c 100644 --- a/Kyoo.Tests/Library/TestContext.cs +++ b/Kyoo.Tests/Library/TestContext.cs @@ -4,6 +4,7 @@ using Kyoo.Postgresql; using Kyoo.SqLite; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; using Npgsql; using Xunit; @@ -80,6 +81,7 @@ namespace Kyoo.Tests conn.ReloadTypes(); TestSample.FillDatabase(context); + conn.Close(); } public void Dispose() @@ -111,12 +113,15 @@ namespace Kyoo.Tests _context = new DbContextOptionsBuilder() .UseNpgsql(_connection) + .UseLoggerFactory(LoggerFactory.Create(x => x.AddConsole())) + .EnableSensitiveDataLogging() + .EnableDetailedErrors() .Options; } public static string GetConnectionString(string database) { - return $"Server=127.0.0.1;Port=5432;Database={database};User ID=kyoo;Password=kyooPassword"; + return $"Server=127.0.0.1;Port=5432;Database={database};User ID=kyoo;Password=kyooPassword;Include Error Detail=true"; } public override void Dispose() diff --git a/Kyoo.Tests/Library/TestSample.cs b/Kyoo.Tests/Library/TestSample.cs index 6a729286..3e5c57da 100644 --- a/Kyoo.Tests/Library/TestSample.cs +++ b/Kyoo.Tests/Library/TestSample.cs @@ -6,6 +6,15 @@ namespace Kyoo.Tests { public static class TestSample { + private static readonly Dictionary> NewSamples = new() + { + { + typeof(Show), + () => new Show() + } + }; + + private static readonly Dictionary> Samples = new() { { @@ -26,8 +35,8 @@ namespace Kyoo.Tests "school students, they had long ceased to think of each other as friends.", Status = Status.Finished, TrailerUrl = null, - StartAir = new DateTime(2011), - EndAir = new DateTime(2011), + StartAir = new DateTime(2011, 1, 1), + EndAir = new DateTime(2011, 1, 1), Poster = "poster", Logo = "logo", Backdrop = "backdrop", @@ -84,13 +93,32 @@ namespace Kyoo.Tests { return (T)Samples[typeof(T)](); } + + public static T GetNew() + { + return (T)NewSamples[typeof(T)](); + } public static void FillDatabase(DatabaseContext context) { - context.Shows.Add(Get()); - context.Seasons.Add(Get()); - // context.Episodes.Add(Get()); - // context.People.Add(Get()); + Show show = Get(); + show.ID = 0; + context.Shows.Add(show); + + Season season = Get(); + season.ID = 0; + season.ShowID = 0; + season.Show = show; + context.Seasons.Add(season); + + Episode episode = Get(); + episode.ID = 0; + episode.ShowID = 0; + episode.Show = show; + episode.SeasonID = 0; + episode.Season = season; + context.Episodes.Add(episode); + context.SaveChanges(); } } diff --git a/Kyoo/Controllers/Repositories/SeasonRepository.cs b/Kyoo/Controllers/Repositories/SeasonRepository.cs index d4529606..fe042e66 100644 --- a/Kyoo/Controllers/Repositories/SeasonRepository.cs +++ b/Kyoo/Controllers/Repositories/SeasonRepository.cs @@ -123,13 +123,9 @@ namespace Kyoo.Controllers { if (obj == null) throw new ArgumentNullException(nameof(obj)); - - _database.Entry(obj).State = EntityState.Deleted; - obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Deleted); + + _database.Remove(obj); await _database.SaveChangesAsync(); - // - // if (obj.Episodes != null) - // await _episodes.Value.DeleteRange(obj.Episodes); } } } \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/ShowRepository.cs b/Kyoo/Controllers/Repositories/ShowRepository.cs index 360d5d28..769cb232 100644 --- a/Kyoo/Controllers/Repositories/ShowRepository.cs +++ b/Kyoo/Controllers/Repositories/ShowRepository.cs @@ -181,7 +181,7 @@ namespace Kyoo.Controllers /// public override async Task Delete(Show obj) { - _database.Entry(obj).State = EntityState.Deleted; + _database.Remove(obj); await _database.SaveChangesAsync(); } } diff --git a/Kyoo/Startup.cs b/Kyoo/Startup.cs index e52aaee1..983d1d6c 100644 --- a/Kyoo/Startup.cs +++ b/Kyoo/Startup.cs @@ -4,6 +4,7 @@ using Kyoo.Authentication; using Kyoo.Controllers; using Kyoo.Models; using Kyoo.Models.Options; +using Kyoo.Postgresql; using Kyoo.SqLite; using Kyoo.Tasks; using Microsoft.AspNetCore.Builder; @@ -46,8 +47,8 @@ namespace Kyoo // TODO remove postgres from here and load it like a normal plugin. _plugins.LoadPlugins(new IPlugin[] { new CoreModule(configuration), - // new PostgresModule(configuration, host), - new SqLiteModule(configuration, host), + new PostgresModule(configuration, host), + // new SqLiteModule(configuration, host), new AuthenticationModule(configuration, loggerFactory, host) }); }