Cleaning up

This commit is contained in:
Zoe Roux 2020-08-03 03:27:09 +02:00
parent 9bf18c8c8c
commit 9c549c593b
19 changed files with 166 additions and 195 deletions

View File

@ -6,6 +6,8 @@ namespace Kyoo.Models.Exceptions
{
public override string Message { get; }
public DuplicatedItemException() {}
public DuplicatedItemException(string message)
{
Message = message;

View File

@ -169,11 +169,5 @@ namespace Kyoo.Controllers
foreach (string slug in slugs)
await Delete(slug);
}
public static bool IsDuplicateException(DbUpdateException ex)
{
return ex.InnerException is PostgresException inner
&& inner.SqlState == PostgresErrorCodes.UniqueViolation;
}
}
}

View File

@ -56,19 +56,7 @@ namespace Kyoo.Controllers
throw new ArgumentNullException(nameof(obj));
_database.Entry(obj).State = EntityState.Added;
try
{
await _database.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
_database.DiscardChanges();
if (IsDuplicateException(ex))
throw new DuplicatedItemException($"Trying to insert a duplicated collection (slug {obj.Slug} already exists).");
throw;
}
await _database.SaveChangesAsync($"Trying to insert a duplicated collection (slug {obj.Slug} already exists).");
return obj;
}

View File

@ -84,20 +84,8 @@ namespace Kyoo.Controllers
if (obj.Tracks != null)
foreach (Track entry in obj.Tracks)
_database.Entry(entry).State = EntityState.Added;
try
{
await _database.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
_database.DiscardChanges();
if (IsDuplicateException(ex))
throw new DuplicatedItemException($"Trying to insert a duplicated episode (slug {obj.Slug} already exists).");
throw;
}
await _database.SaveChangesAsync($"Trying to insert a duplicated episode (slug {obj.Slug} already exists).");
return obj;
}

View File

@ -51,20 +51,7 @@ namespace Kyoo.Controllers
throw new ArgumentNullException(nameof(obj));
_database.Entry(obj).State = EntityState.Added;
try
{
await _database.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
_database.DiscardChanges();
if (IsDuplicateException(ex))
throw new DuplicatedItemException($"Trying to insert a duplicated genre (slug {obj.Slug} already exists).");
throw;
}
await _database.SaveChangesAsync($"Trying to insert a duplicated genre (slug {obj.Slug} already exists).");
return obj;
}

View File

@ -61,18 +61,7 @@ namespace Kyoo.Controllers
foreach (ProviderLink entry in obj.ProviderLinks)
_database.Entry(entry).State = EntityState.Added;
try
{
await _database.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
_database.DiscardChanges();
if (IsDuplicateException(ex))
throw new DuplicatedItemException($"Trying to insert a duplicated library (slug {obj.Slug} already exists).");
throw;
}
await _database.SaveChangesAsync($"Trying to insert a duplicated library (slug {obj.Slug} already exists).");
return obj;
}

View File

@ -61,18 +61,7 @@ namespace Kyoo.Controllers
foreach (MetadataID entry in obj.ExternalIDs)
_database.Entry(entry).State = EntityState.Added;
try
{
await _database.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
_database.DiscardChanges();
if (IsDuplicateException(ex))
throw new DuplicatedItemException($"Trying to insert a duplicated people (slug {obj.Slug} already exists).");
throw;
}
await _database.SaveChangesAsync($"Trying to insert a duplicated people (slug {obj.Slug} already exists).");
return obj;
}

View File

@ -35,18 +35,7 @@ namespace Kyoo.Controllers
_database.Entry(obj).State = EntityState.Added;
try
{
await _database.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
_database.DiscardChanges();
if (IsDuplicateException(ex))
throw new DuplicatedItemException($"Trying to insert a duplicated provider (slug {obj.Slug} already exists).");
throw;
}
await _database.SaveChangesAsync($"Trying to insert a duplicated provider (slug {obj.Slug} already exists).");
return obj;
}

View File

@ -89,18 +89,7 @@ namespace Kyoo.Controllers
foreach (MetadataID entry in obj.ExternalIDs)
_database.Entry(entry).State = EntityState.Added;
try
{
await _database.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
_database.DiscardChanges();
if (IsDuplicateException(ex))
throw new DuplicatedItemException($"Trying to insert a duplicated season (slug {obj.Slug} already exists).");
throw;
}
await _database.SaveChangesAsync($"Trying to insert a duplicated season (slug {obj.Slug} already exists).");
return obj;
}

View File

@ -102,18 +102,7 @@ namespace Kyoo.Controllers
foreach (MetadataID entry in obj.ExternalIDs)
_database.Entry(entry).State = EntityState.Added;
try
{
await _database.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
_database.DiscardChanges();
if (IsDuplicateException(ex))
throw new DuplicatedItemException($"Trying to insert a duplicated show (slug {obj.Slug} already exists).");
throw;
}
await _database.SaveChangesAsync($"Trying to insert a duplicated show (slug {obj.Slug} already exists).");
return obj;
}
@ -139,23 +128,17 @@ namespace Kyoo.Controllers
{
if (collectionID != null)
{
_database.CollectionLinks.AddIfNotExist(new CollectionLink { CollectionID = collectionID, ShowID = showID},
x => x.CollectionID == collectionID && x.ShowID == showID);
await _database.CollectionLinks.AddAsync(new CollectionLink {CollectionID = collectionID, ShowID = showID});
}
if (libraryID != null)
{
_database.LibraryLinks.AddIfNotExist(new LibraryLink {LibraryID = libraryID.Value, ShowID = showID},
x => x.LibraryID == libraryID.Value && x.CollectionID == null && x.ShowID == showID);
await _database.LibraryLinks.AddAsync(new LibraryLink {LibraryID = libraryID.Value, ShowID = showID});
}
if (libraryID != null && collectionID != null)
{
_database.LibraryLinks.AddIfNotExist(
new LibraryLink {LibraryID = libraryID.Value, CollectionID = collectionID.Value},
x => x.LibraryID == libraryID && x.CollectionID == collectionID && x.ShowID == null);
await _database.LibraryLinks.AddAsync(new LibraryLink {LibraryID = libraryID.Value, CollectionID = collectionID.Value});
}
await _database.SaveChangesAsync();
}
public override async Task Delete(Show obj)

View File

@ -34,18 +34,7 @@ namespace Kyoo.Controllers
throw new ArgumentNullException(nameof(obj));
_database.Entry(obj).State = EntityState.Added;
try
{
await _database.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
_database.DiscardChanges();
if (IsDuplicateException(ex))
throw new DuplicatedItemException($"Trying to insert a duplicated studio (slug {obj.Slug} already exists).");
throw;
}
await _database.SaveChangesAsync($"Trying to insert a duplicated studio (slug {obj.Slug} already exists).");
return obj;
}

View File

@ -4,7 +4,6 @@ using System.Linq.Expressions;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.EntityFrameworkCore;
namespace Kyoo.Controllers
@ -66,17 +65,7 @@ namespace Kyoo.Controllers
_database.Entry(obj).State = EntityState.Added;
try
{
await _database.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
_database.DiscardChanges();
if (IsDuplicateException(ex))
throw new DuplicatedItemException($"Trying to insert a duplicated track (slug {obj.Slug} already exists).");
throw;
}
await _database.SaveChangesAsync($"Trying to insert a duplicated track (slug {obj.Slug} already exists).");
return obj;
}

View File

@ -1,18 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using IdentityServer4.EntityFramework.Entities;
using IdentityServer4.EntityFramework.Extensions;
using IdentityServer4.EntityFramework.Interfaces;
using IdentityServer4.EntityFramework.Options;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Microsoft.Extensions.Options;
using Npgsql;
namespace Kyoo
{
@ -175,8 +178,112 @@ namespace Kyoo
modelBuilder.Entity<Episode>()
.HasIndex(x => new {x.ShowID, x.SeasonNumber, x.EpisodeNumber, x.AbsoluteNumber})
.IsUnique();
modelBuilder.Entity<LibraryLink>()
.HasIndex(x => new {x.LibraryID, x.ShowID, x.CollectionID})
.IsUnique();
modelBuilder.Entity<CollectionLink>()
.HasIndex(x => new {x.CollectionID, x.ShowID})
.IsUnique();
}
public override int SaveChanges()
{
try
{
return base.SaveChanges();
}
catch (DbUpdateException ex)
{
DiscardChanges();
if (IsDuplicateException(ex))
throw new DuplicatedItemException();
throw;
}
}
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
try
{
return base.SaveChanges(acceptAllChangesOnSuccess);
}
catch (DbUpdateException ex)
{
DiscardChanges();
if (IsDuplicateException(ex))
throw new DuplicatedItemException();
throw;
}
}
public int SaveChanges(string duplicateMessage)
{
try
{
return base.SaveChanges();
}
catch (DbUpdateException ex)
{
DiscardChanges();
if (IsDuplicateException(ex))
throw new DuplicatedItemException(duplicateMessage);
throw;
}
}
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess,
CancellationToken cancellationToken = new CancellationToken())
{
try
{
return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
catch (DbUpdateException ex)
{
DiscardChanges();
if (IsDuplicateException(ex))
throw new DuplicatedItemException();
throw;
}
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
try
{
return await base.SaveChangesAsync(cancellationToken);
}
catch (DbUpdateException ex)
{
DiscardChanges();
if (IsDuplicateException(ex))
throw new DuplicatedItemException();
throw;
}
}
public async Task<int> SaveChangesAsync(string duplicateMessage,
CancellationToken cancellationToken = new CancellationToken())
{
try
{
return await base.SaveChangesAsync(cancellationToken);
}
catch (DbUpdateException ex)
{
DiscardChanges();
if (IsDuplicateException(ex))
throw new DuplicatedItemException(duplicateMessage);
throw;
}
}
public static bool IsDuplicateException(DbUpdateException ex)
{
return ex.InnerException is PostgresException inner
&& inner.SqlState == PostgresErrorCodes.UniqueViolation;
}
public void DiscardChanges()
{
foreach (EntityEntry entry in ChangeTracker.Entries().Where(x => x.State != EntityState.Unchanged
@ -186,13 +293,4 @@ namespace Kyoo
}
}
}
}
public static class DbSetExtension
{
public static EntityEntry<T> AddIfNotExist<T>(this DbSet<T> db, T entity, Func<T, bool> predicate) where T : class
{
bool exists = db.Any(predicate);
return exists ? null : db.Add(entity);
}
}

View File

@ -10,7 +10,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Kyoo.Models.DatabaseMigrations.Internal
{
[DbContext(typeof(DatabaseContext))]
[Migration("20200724211017_Initial")]
[Migration("20200803005331_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -28,9 +28,6 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("ImgPrimary")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
@ -66,10 +63,11 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.HasKey("ID");
b.HasIndex("CollectionID");
b.HasIndex("ShowID");
b.HasIndex("CollectionID", "ShowID")
.IsUnique();
b.ToTable("CollectionLinks");
});
@ -203,10 +201,11 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.HasIndex("CollectionID");
b.HasIndex("LibraryID");
b.HasIndex("ShowID");
b.HasIndex("LibraryID", "ShowID", "CollectionID")
.IsUnique();
b.ToTable("LibraryLinks");
});

View File

@ -17,8 +17,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
Slug = table.Column<string>(nullable: true),
Name = table.Column<string>(nullable: true),
Poster = table.Column<string>(nullable: true),
Overview = table.Column<string>(nullable: true),
ImgPrimary = table.Column<string>(nullable: true)
Overview = table.Column<string>(nullable: true)
},
constraints: table =>
{
@ -402,16 +401,17 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_CollectionLinks_CollectionID",
table: "CollectionLinks",
column: "CollectionID");
migrationBuilder.CreateIndex(
name: "IX_CollectionLinks_ShowID",
table: "CollectionLinks",
column: "ShowID");
migrationBuilder.CreateIndex(
name: "IX_CollectionLinks_CollectionID_ShowID",
table: "CollectionLinks",
columns: new[] { "CollectionID", "ShowID" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Collections_Slug",
table: "Collections",
@ -451,16 +451,17 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
table: "LibraryLinks",
column: "CollectionID");
migrationBuilder.CreateIndex(
name: "IX_LibraryLinks_LibraryID",
table: "LibraryLinks",
column: "LibraryID");
migrationBuilder.CreateIndex(
name: "IX_LibraryLinks_ShowID",
table: "LibraryLinks",
column: "ShowID");
migrationBuilder.CreateIndex(
name: "IX_LibraryLinks_LibraryID_ShowID_CollectionID",
table: "LibraryLinks",
columns: new[] { "LibraryID", "ShowID", "CollectionID" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_MetadataIds_EpisodeID",
table: "MetadataIds",

View File

@ -26,9 +26,6 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("ImgPrimary")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
@ -64,10 +61,11 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.HasKey("ID");
b.HasIndex("CollectionID");
b.HasIndex("ShowID");
b.HasIndex("CollectionID", "ShowID")
.IsUnique();
b.ToTable("CollectionLinks");
});
@ -201,10 +199,11 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.HasIndex("CollectionID");
b.HasIndex("LibraryID");
b.HasIndex("ShowID");
b.HasIndex("LibraryID", "ShowID", "CollectionID")
.IsUnique();
b.ToTable("LibraryLinks");
});

View File

@ -17,15 +17,13 @@ namespace Kyoo.Tasks
public bool RunOnStartup => true;
public int Priority => 1000;
public Task Run(IServiceProvider serviceProvider, CancellationToken cancellationToken, string arguments = null)
public async Task Run(IServiceProvider serviceProvider, CancellationToken cancellationToken, string arguments = null)
{
using IServiceScope serviceScope = serviceProvider.CreateScope();
DatabaseContext database = serviceScope.ServiceProvider.GetService<DatabaseContext>();
IProviderRepository providers = serviceScope.ServiceProvider.GetService<IProviderRepository>();
IPluginManager pluginManager = serviceScope.ServiceProvider.GetService<IPluginManager>();
foreach (IMetadataProvider provider in pluginManager.GetPlugins<IMetadataProvider>())
database.Providers.AddIfNotExist(provider.Provider, x => x.Slug == provider.Provider.Slug);
database.SaveChanges();
return Task.CompletedTask;
await providers.CreateIfNotExists(provider.Provider);
}
public Task<IEnumerable<string>> GetPossibleParameters()

View File

@ -24,14 +24,14 @@ namespace Kyoo.Api
_libraryManager = libraryManager;
}
[HttpGet("{seasonID:int}/season")]
[HttpGet("{seasonID:int}/seasons")]
[HttpGet("{seasonID:int}/episode")]
[HttpGet("{seasonID:int}/episodes")]
[Authorize(Policy = "Read")]
public async Task<ActionResult<Page<Episode>>> GetSeasons(int seasonID,
public async Task<ActionResult<Page<Episode>>> GetEpisode(int seasonID,
[FromQuery] string sortBy,
[FromQuery] int afterID,
[FromQuery] Dictionary<string, string> where,
[FromQuery] int limit = 20)
[FromQuery] int limit = 30)
{
where.Remove("sortBy");
where.Remove("limit");
@ -56,15 +56,15 @@ namespace Kyoo.Api
}
}
[HttpGet("{showSlug}-{seasonNumber:int}/season")]
[HttpGet("{showSlug}-{seasonNumber:int}/seasons")]
[HttpGet("{showSlug}-{seasonNumber:int}/episode")]
[HttpGet("{showSlug}-{seasonNumber:int}/episodes")]
[Authorize(Policy = "Read")]
public async Task<ActionResult<Page<Episode>>> GetSeasons(string showSlug,
public async Task<ActionResult<Page<Episode>>> GetEpisode(string showSlug,
int seasonNumber,
[FromQuery] string sortBy,
[FromQuery] int afterID,
[FromQuery] Dictionary<string, string> where,
[FromQuery] int limit = 20)
[FromQuery] int limit = 30)
{
where.Remove("sortBy");
where.Remove("limit");
@ -90,15 +90,15 @@ namespace Kyoo.Api
}
}
[HttpGet("{showID:int}-{seasonNumber:int}/season")]
[HttpGet("{showID:int}-{seasonNumber:int}/seasons")]
[HttpGet("{showID:int}-{seasonNumber:int}/episode")]
[HttpGet("{showID:int}-{seasonNumber:int}/episodes")]
[Authorize(Policy = "Read")]
public async Task<ActionResult<Page<Episode>>> GetSeasons(int showID,
public async Task<ActionResult<Page<Episode>>> GetEpisode(int showID,
int seasonNumber,
[FromQuery] string sortBy,
[FromQuery] int afterID,
[FromQuery] Dictionary<string, string> where,
[FromQuery] int limit = 20)
[FromQuery] int limit = 30)
{
where.Remove("sortBy");
where.Remove("limit");

@ -1 +1 @@
Subproject commit d692f9f3d364d7d2748205374a3a84fb1fd4a938
Subproject commit db0589285c0790c0f2a2f5beb98fb37303256ec3