Add PUT /collections/id/movie/id route to link movies/shows to colletions

This commit is contained in:
Zoe Roux 2023-11-01 16:34:40 +01:00
parent b2f4933a5f
commit 88eb325079
4 changed files with 86 additions and 17 deletions

View File

@ -42,6 +42,7 @@ namespace Kyoo.Abstractions
where T : IBaseRepository where T : IBaseRepository
{ {
return builder.RegisterType<T>() return builder.RegisterType<T>()
.AsSelf()
.As<IBaseRepository>() .As<IBaseRepository>()
.As(Utility.GetGenericDefinition(typeof(T), typeof(IRepository<>))!) .As(Utility.GetGenericDefinition(typeof(T), typeof(IRepository<>))!)
.InstancePerLifetimeScope(); .InstancePerLifetimeScope();
@ -62,7 +63,7 @@ namespace Kyoo.Abstractions
where T : notnull where T : notnull
where T2 : IBaseRepository, T where T2 : IBaseRepository, T
{ {
return builder.RegisterRepository<T2>().As<T>(); return builder.RegisterRepository<T2>().AsSelf().As<T>();
} }
} }
} }

View File

@ -19,6 +19,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models; using Kyoo.Abstractions.Models;
@ -81,6 +82,18 @@ namespace Kyoo.Core.Controllers
throw new ArgumentException("The collection's name must be set and not empty"); throw new ArgumentException("The collection's name must be set and not empty");
} }
public async Task AddMovie(int id, int movieId)
{
_database.AddLinks<Collection, Movie>(id, movieId);
await _database.SaveChangesAsync();
}
public async Task AddShow(int id, int showId)
{
_database.AddLinks<Collection, Show>(id, showId);
await _database.SaveChangesAsync();
}
/// <inheritdoc /> /// <inheritdoc />
public override async Task Delete(Collection obj) public override async Task Delete(Collection obj)
{ {

View File

@ -24,6 +24,7 @@ using Kyoo.Abstractions.Models;
using Kyoo.Abstractions.Models.Attributes; using Kyoo.Abstractions.Models.Attributes;
using Kyoo.Abstractions.Models.Permissions; using Kyoo.Abstractions.Models.Permissions;
using Kyoo.Abstractions.Models.Utils; using Kyoo.Abstractions.Models.Utils;
using Kyoo.Core.Controllers;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using static Kyoo.Abstractions.Models.Utils.Constants; using static Kyoo.Abstractions.Models.Utils.Constants;
@ -40,23 +41,78 @@ namespace Kyoo.Core.Api
[ApiDefinition("Collections", Group = ResourcesGroup)] [ApiDefinition("Collections", Group = ResourcesGroup)]
public class CollectionApi : CrudThumbsApi<Collection> public class CollectionApi : CrudThumbsApi<Collection>
{ {
/// <summary>
/// The library manager used to modify or retrieve information about the data store.
/// </summary>
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly CollectionRepository _collections;
/// <summary>
/// Create a new <see cref="CollectionApi"/>.
/// </summary>
/// <param name="libraryManager">
/// The library manager used to modify or retrieve information about the data store.
/// </param>
/// <param name="thumbs">The thumbnail manager used to retrieve images paths.</param>
public CollectionApi(ILibraryManager libraryManager, public CollectionApi(ILibraryManager libraryManager,
CollectionRepository collections,
IThumbnailsManager thumbs) IThumbnailsManager thumbs)
: base(libraryManager.Collections, thumbs) : base(libraryManager.Collections, thumbs)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_collections = collections;
}
/// <summary>
/// Add a movie
/// </summary>
/// <remarks>
/// Add a movie in the collection.
/// </remarks>
/// <param name="identifier">The ID or slug of the <see cref="Collection"/>.</param>
/// <param name="movie">The ID or slug of the <see cref="Movie"/> to add.</param>
/// <returns>Nothing if successful.</returns>
/// <response code="404">No collection or movie with the given ID could be found.</response>
/// <response code="409">The specified movie is already in this collection.</response>
[HttpPut("{identifier:id}/movies/{movie:id}")]
[HttpPut("{identifier:id}/movie/{movie:id}", Order = AlternativeRoute)]
[PartialPermission(Kind.Write)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult> AddMovie(Identifier identifier, Identifier movie)
{
int collectionId = await identifier.Match(
async id => (await _libraryManager.Collections.Get(id)).Id,
async slug => (await _libraryManager.Collections.Get(slug)).Id
);
int movieId = await movie.Match(
async id => (await _libraryManager.Movies.Get(id)).Id,
async slug => (await _libraryManager.Movies.Get(slug)).Id
);
await _collections.AddMovie(collectionId, movieId);
return NoContent();
}
/// <summary>
/// Add a show
/// </summary>
/// <remarks>
/// Add a show in the collection.
/// </remarks>
/// <param name="identifier">The ID or slug of the <see cref="Collection"/>.</param>
/// <param name="show">The ID or slug of the <see cref="Show"/> to add.</param>
/// <returns>Nothing if successful.</returns>
/// <response code="404">No collection or show with the given ID could be found.</response>
/// <response code="409">The specified show is already in this collection.</response>
[HttpPut("{identifier:id}/shows/{show:id}")]
[HttpPut("{identifier:id}/show/{show:id}", Order = AlternativeRoute)]
[PartialPermission(Kind.Write)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult> AddShow(Identifier identifier, Identifier show)
{
int collectionId = await identifier.Match(
async id => (await _libraryManager.Collections.Get(id)).Id,
async slug => (await _libraryManager.Collections.Get(slug)).Id
);
int showId = await show.Match(
async id => (await _libraryManager.Shows.Get(id)).Id,
async slug => (await _libraryManager.Shows.Get(slug)).Id
);
await _collections.AddShow(collectionId, showId);
return NoContent();
} }
/// <summary> /// <summary>
@ -83,11 +139,11 @@ namespace Kyoo.Core.Api
[FromQuery] Sort<Show> sortBy, [FromQuery] Sort<Show> sortBy,
[FromQuery] Dictionary<string, string> where, [FromQuery] Dictionary<string, string> where,
[FromQuery] Pagination pagination, [FromQuery] Pagination pagination,
[FromQuery] Include<Show> fields) [FromQuery] Include<Show>? fields)
{ {
ICollection<Show> resources = await _libraryManager.Shows.GetAll( ICollection<Show> resources = await _libraryManager.Shows.GetAll(
ApiHelper.ParseWhere(where, identifier.IsContainedIn<Show, Collection>(x => x.Collections!)), ApiHelper.ParseWhere(where, identifier.IsContainedIn<Show, Collection>(x => x.Collections!)),
sortBy, sortBy ?? new Sort<Show>.By(x => x.StartAir),
pagination, pagination,
fields fields
); );

View File

@ -116,13 +116,12 @@ namespace Kyoo.Postgresql
/// <param name="second">The ID of the second resource.</param> /// <param name="second">The ID of the second resource.</param>
/// <typeparam name="T1">The first resource type of the relation. It is the owner of the second</typeparam> /// <typeparam name="T1">The first resource type of the relation. It is the owner of the second</typeparam>
/// <typeparam name="T2">The second resource type of the relation. It is the contained resource.</typeparam> /// <typeparam name="T2">The second resource type of the relation. It is the contained resource.</typeparam>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> public void AddLinks<T1, T2>(int first, int second)
public async Task AddLinks<T1, T2>(int first, int second)
where T1 : class, IResource where T1 : class, IResource
where T2 : class, IResource where T2 : class, IResource
{ {
await Set<Dictionary<string, object>>(LinkName<T1, T2>()) Set<Dictionary<string, object>>(LinkName<T1, T2>())
.AddAsync(new Dictionary<string, object> .Add(new Dictionary<string, object>
{ {
[LinkNameFk<T1>()] = first, [LinkNameFk<T1>()] = first,
[LinkNameFk<T2>()] = second [LinkNameFk<T2>()] = second