mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Improve duplicated error handling
This commit is contained in:
parent
f59f9a7ba0
commit
9a5c4ab087
@ -26,7 +26,7 @@ namespace Kyoo.Abstractions.Controllers
|
||||
public interface ILibraryManager
|
||||
{
|
||||
IRepository<T> Repository<T>()
|
||||
where T : class, IResource, IQuery;
|
||||
where T : IResource, IQuery;
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle libraries items (a wrapper around shows and collections).
|
||||
|
@ -30,7 +30,7 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The resource's type that this repository manage.</typeparam>
|
||||
public interface IRepository<T> : IBaseRepository
|
||||
where T : class, IResource, IQuery
|
||||
where T : IResource, IQuery
|
||||
{
|
||||
/// <summary>
|
||||
/// The event handler type for all events of this repository.
|
||||
|
@ -36,7 +36,7 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// The item to cache images.
|
||||
/// </param>
|
||||
/// <typeparam name="T">The type of the item</typeparam>
|
||||
/// <returns><c>true</c> if an image has been downloaded, <c>false</c> otherwise.</returns>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
Task DownloadImages<T>(T item)
|
||||
where T : IThumbnails;
|
||||
|
||||
@ -50,5 +50,16 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <returns>The path of the image for the given resource or null if it does not exists.</returns>
|
||||
string GetImagePath<T>(T item, string image, ImageQuality quality)
|
||||
where T : IThumbnails;
|
||||
|
||||
/// <summary>
|
||||
/// Delete images associated with the item.
|
||||
/// </summary>
|
||||
/// <param name="item">
|
||||
/// The item with cached images.
|
||||
/// </param>
|
||||
/// <typeparam name="T">The type of the item</typeparam>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
Task DeleteImages<T>(T item)
|
||||
where T : IThumbnails;
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <summary>
|
||||
/// An interface to represent a resource that can be retrieved from the database.
|
||||
/// </summary>
|
||||
public interface IResource
|
||||
public interface IResource : IQuery
|
||||
{
|
||||
/// <summary>
|
||||
/// A unique ID for this type of resource. This can't be changed and duplicates are not allowed.
|
||||
|
@ -92,7 +92,7 @@ namespace Kyoo.Core.Controllers
|
||||
public IRepository<User> Users { get; }
|
||||
|
||||
public IRepository<T> Repository<T>()
|
||||
where T : class, IResource, IQuery
|
||||
where T : IResource, IQuery
|
||||
{
|
||||
return (IRepository<T>)_repositories.First(x => x.RepositoryType == typeof(T));
|
||||
}
|
||||
|
@ -84,16 +84,20 @@ namespace Kyoo.Core.Controllers
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
protected override Task<Episode?> GetDuplicated(Episode item)
|
||||
{
|
||||
if (item is { SeasonNumber: not null, EpisodeNumber: not null })
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.ShowId == item.ShowId && x.SeasonNumber == item.SeasonNumber && x.EpisodeNumber == item.EpisodeNumber);
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.ShowId == item.ShowId && x.AbsoluteNumber == item.AbsoluteNumber);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<Episode> Create(Episode obj)
|
||||
{
|
||||
obj.ShowSlug = obj.Show?.Slug ?? (await _database.Shows.FirstAsync(x => x.Id == obj.ShowId)).Slug;
|
||||
await base.Create(obj);
|
||||
_database.Entry(obj).State = EntityState.Added;
|
||||
await _database.SaveChangesAsync(() =>
|
||||
obj is { SeasonNumber: not null, EpisodeNumber: not null }
|
||||
? _database.Episodes.FirstOrDefaultAsync(x => x.ShowId == obj.ShowId && x.SeasonNumber == obj.SeasonNumber && x.EpisodeNumber == obj.EpisodeNumber)
|
||||
: _database.Episodes.FirstOrDefaultAsync(x => x.ShowId == obj.ShowId && x.AbsoluteNumber == obj.AbsoluteNumber));
|
||||
await _database.SaveChangesAsync(() => GetDuplicated(obj));
|
||||
await IRepository<Episode>.OnResourceCreated(obj);
|
||||
return obj;
|
||||
}
|
||||
|
@ -214,6 +214,11 @@ namespace Kyoo.Core.Controllers
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected virtual Task<T?> GetDuplicated(T item)
|
||||
{
|
||||
return GetOrDefault(item.Slug);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Task<T?> GetOrDefault(Guid id, Include<T>? include = default)
|
||||
{
|
||||
@ -324,7 +329,14 @@ namespace Kyoo.Core.Controllers
|
||||
await Validate(obj);
|
||||
if (obj is IThumbnails thumbs)
|
||||
{
|
||||
await _thumbs.DownloadImages(thumbs);
|
||||
try
|
||||
{
|
||||
await _thumbs.DownloadImages(thumbs);
|
||||
}
|
||||
catch (DuplicatedItemException e) when (e.Existing is null)
|
||||
{
|
||||
throw new DuplicatedItemException(await GetDuplicated(obj));
|
||||
}
|
||||
if (thumbs.Poster != null)
|
||||
Database.Entry(thumbs).Reference(x => x.Poster).TargetEntry!.State = EntityState.Added;
|
||||
if (thumbs.Thumbnail != null)
|
||||
@ -470,6 +482,8 @@ namespace Kyoo.Core.Controllers
|
||||
public virtual Task Delete(T obj)
|
||||
{
|
||||
IRepository<T>.OnResourceDeleted(obj);
|
||||
if (obj is IThumbnails thumbs)
|
||||
return _thumbs.DeleteImages(thumbs);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
@ -16,12 +16,13 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Kyoo.Abstractions.Models.Exceptions;
|
||||
using Kyoo.Abstractions.Models.Utils;
|
||||
using Kyoo.Postgresql;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -70,6 +71,11 @@ namespace Kyoo.Core.Controllers
|
||||
_database = database;
|
||||
}
|
||||
|
||||
protected override Task<Season?> GetDuplicated(Season item)
|
||||
{
|
||||
return _database.Seasons.FirstOrDefaultAsync(x => x.ShowId == item.ShowId && x.SeasonNumber == item.SeasonNumber);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<ICollection<Season>> Search(string query, Include<Season>? include = default)
|
||||
{
|
||||
@ -83,11 +89,10 @@ namespace Kyoo.Core.Controllers
|
||||
public override async Task<Season> Create(Season obj)
|
||||
{
|
||||
await base.Create(obj);
|
||||
obj.ShowSlug = _database.Shows.First(x => x.Id == obj.ShowId).Slug;
|
||||
obj.ShowSlug = (await _database.Shows.FirstOrDefaultAsync(x => x.Id == obj.ShowId))?.Slug
|
||||
?? throw new ItemNotFoundException($"No show found with ID {obj.ShowId}");
|
||||
_database.Entry(obj).State = EntityState.Added;
|
||||
await _database.SaveChangesAsync(() =>
|
||||
_database.Seasons.FirstOrDefaultAsync(x => x.ShowId == obj.ShowId && x.SeasonNumber == obj.SeasonNumber)
|
||||
);
|
||||
await _database.SaveChangesAsync(() => GetDuplicated(obj));
|
||||
await IRepository<Season>.OnResourceCreated(obj);
|
||||
return obj;
|
||||
}
|
||||
@ -100,7 +105,7 @@ namespace Kyoo.Core.Controllers
|
||||
{
|
||||
if (resource.Show == null)
|
||||
{
|
||||
throw new ArgumentException($"Can't store a season not related to any show " +
|
||||
throw new ValidationException($"Can't store a season not related to any show " +
|
||||
$"(showID: {resource.ShowId}).");
|
||||
}
|
||||
resource.ShowId = resource.Show.Id;
|
||||
|
@ -19,6 +19,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Blurhash.SkiaSharp;
|
||||
@ -167,5 +168,24 @@ namespace Kyoo.Core.Controllers
|
||||
{
|
||||
return $"{_GetBaseImagePath(item, image)}.{quality.ToString().ToLowerInvariant()}.webp";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task DeleteImages<T>(T item)
|
||||
where T : IThumbnails
|
||||
{
|
||||
IEnumerable<string> images = new[] { "poster", "thumbnail", "logo" }
|
||||
.SelectMany(x => _GetBaseImagePath(item, x))
|
||||
.SelectMany(x => new[]
|
||||
{
|
||||
ImageQuality.High.ToString().ToLowerInvariant(),
|
||||
ImageQuality.Medium.ToString().ToLowerInvariant(),
|
||||
ImageQuality.Low.ToString().ToLowerInvariant(),
|
||||
}.Select(quality => $"{x}.{quality}.webp")
|
||||
);
|
||||
|
||||
foreach (string image in images)
|
||||
File.Delete(image);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,9 +54,13 @@ namespace Kyoo.Core
|
||||
case ItemNotFoundException ex:
|
||||
context.Result = new NotFoundObjectResult(new RequestError(ex.Message));
|
||||
break;
|
||||
case DuplicatedItemException ex:
|
||||
case DuplicatedItemException ex when ex.Existing is not null:
|
||||
context.Result = new ConflictObjectResult(ex.Existing);
|
||||
break;
|
||||
case DuplicatedItemException:
|
||||
// Should not happen but if it does, it is better than returning a 409 with no body since clients expect json content
|
||||
context.Result = new ConflictObjectResult(new RequestError("Duplicated item"));
|
||||
break;
|
||||
case Exception ex:
|
||||
_logger.LogError(ex, "Unhandled error");
|
||||
context.Result = new ServerErrorObjectResult(new RequestError("Internal Server Error"));
|
||||
|
Loading…
x
Reference in New Issue
Block a user