CollectionAPI: Documenting the public api

This commit is contained in:
Zoe Roux 2021-09-21 12:37:59 +02:00
parent 561b8e81b2
commit f0e9054b36
4 changed files with 90 additions and 18 deletions

View File

@ -179,7 +179,6 @@ namespace Kyoo.Abstractions.Controllers
/// Delete all resources that match the predicate. /// Delete all resources that match the predicate.
/// </summary> /// </summary>
/// <param name="where">A predicate to filter resources to delete. Every resource that match this will be deleted.</param> /// <param name="where">A predicate to filter resources to delete. Every resource that match this will be deleted.</param>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
Task DeleteAll([NotNull] Expression<Func<T, bool>> where); Task DeleteAll([NotNull] Expression<Func<T, bool>> where);
} }

View File

@ -30,6 +30,7 @@ namespace Kyoo.Abstractions.Models.Utils
/// <summary> /// <summary>
/// The list of errors that where made in the request. /// The list of errors that where made in the request.
/// </summary> /// </summary>
/// <example><c>["InvalidFilter: no field 'startYear' on a collection"]</c></example>
[NotNull] public string[] Errors { get; set; } [NotNull] public string[] Errors { get; set; }
/// <summary> /// <summary>

View File

@ -24,7 +24,9 @@ using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models; using Kyoo.Abstractions.Models;
using Kyoo.Abstractions.Models.Exceptions; using Kyoo.Abstractions.Models.Exceptions;
using Kyoo.Abstractions.Models.Permissions; using Kyoo.Abstractions.Models.Permissions;
using Kyoo.Abstractions.Models.Utils;
using Kyoo.Core.Models.Options; using Kyoo.Core.Models.Options;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using static Kyoo.Abstractions.Models.Utils.Constants; using static Kyoo.Abstractions.Models.Utils.Constants;
@ -67,21 +69,25 @@ namespace Kyoo.Core.Api
} }
/// <summary> /// <summary>
/// Lists <see cref="Show"/> that are contained in the <see cref="Collection"/> with id <paramref name="id"/>. /// Get shows in collection (via id)
/// </summary> /// </summary>
/// <remarks>
/// Lists the shows that are contained in the collection with the given id.
/// </remarks>
/// <param name="id">The ID of the <see cref="Collection"/>.</param> /// <param name="id">The ID of the <see cref="Collection"/>.</param>
/// <param name="sortBy">A key to sort shows by. See <see cref="Sort{T}"/> for more information.</param> /// <param name="sortBy">A key to sort shows by.</param>
/// <param name="afterID">An optional show's ID to start the query from this specific item.</param> /// <param name="afterID">An optional show's ID to start the query from this specific item.</param>
/// <param name="where"> /// <param name="where">An optional list of filters.</param>
/// An optional list of filters. See <see cref="ApiHelper.ParseWhere{T}"/> for more details.
/// </param>
/// <param name="limit">The number of shows to return.</param> /// <param name="limit">The number of shows to return.</param>
/// <returns>A page of shows.</returns> /// <returns>A page of shows.</returns>
/// <response code="400"><paramref name="sortBy"/> or <paramref name="where"/> is invalid.</response> /// <response code="400">The filters or the sort parameters are invalid.</response>
/// <response code="404">No collection with the ID <paramref name="id"/> could be found.</response> /// <response code="404">No collection with the given ID could be found.</response>
[HttpGet("{id:int}/shows")] [HttpGet("{id:int}/shows")]
[HttpGet("{id:int}/show", Order = AlternativeRoute)] [HttpGet("{id:int}/show", Order = AlternativeRoute)]
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<Page<Show>>> GetShows(int id, public async Task<ActionResult<Page<Show>>> GetShows(int id,
[FromQuery] string sortBy, [FromQuery] string sortBy,
[FromQuery] int afterID, [FromQuery] int afterID,
@ -101,13 +107,30 @@ namespace Kyoo.Core.Api
} }
catch (ArgumentException ex) catch (ArgumentException ex)
{ {
return BadRequest(new { Error = ex.Message }); return BadRequest(new RequestError(ex.Message));
} }
} }
/// <summary>
/// Get shows in collection (via slug)
/// </summary>
/// <remarks>
/// Lists the shows that are contained in the collection with the given slug.
/// </remarks>
/// <param name="slug">The slug of the <see cref="Collection"/>.</param>
/// <param name="sortBy">A key to sort shows by.</param>
/// <param name="afterID">An optional show's ID to start the query from this specific item.</param>
/// <param name="where">An optional list of filters.</param>
/// <param name="limit">The number of shows to return.</param>
/// <returns>A page of shows.</returns>
/// <response code="400">The filters or the sort parameters are invalid.</response>
/// <response code="404">No collection with the given slug could be found.</response>
[HttpGet("{slug}/shows")] [HttpGet("{slug}/shows")]
[HttpGet("{slug}/show", Order = AlternativeRoute)] [HttpGet("{slug}/show", Order = AlternativeRoute)]
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<Page<Show>>> GetShows(string slug, public async Task<ActionResult<Page<Show>>> GetShows(string slug,
[FromQuery] string sortBy, [FromQuery] string sortBy,
[FromQuery] int afterID, [FromQuery] int afterID,
@ -127,10 +150,24 @@ namespace Kyoo.Core.Api
} }
catch (ArgumentException ex) catch (ArgumentException ex)
{ {
return BadRequest(new { Error = ex.Message }); return BadRequest(new RequestError(ex.Message));
} }
} }
/// <summary>
/// Get libraries containing this collection
/// </summary>
/// <remarks>
/// Lists the libraries that contain the collection with the given id.
/// </remarks>
/// <param name="slug">The slug of the <see cref="Collection"/>.</param>
/// <param name="sortBy">A key to sort shows by.</param>
/// <param name="afterID">An optional show's ID to start the query from this specific item.</param>
/// <param name="where">An optional list of filters.</param>
/// <param name="limit">The number of shows to return.</param>
/// <returns>A page of shows.</returns>
/// <response code="400">The filters or the sort parameters are invalid.</response>
/// <response code="404">No collection with the given slug could be found.</response>
[HttpGet("{id:int}/libraries")] [HttpGet("{id:int}/libraries")]
[HttpGet("{id:int}/library", Order = AlternativeRoute)] [HttpGet("{id:int}/library", Order = AlternativeRoute)]
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]

View File

@ -45,7 +45,8 @@ namespace Kyoo.Core.Api
private readonly IRepository<T> _repository; private readonly IRepository<T> _repository;
/// <summary> /// <summary>
/// The base URL of Kyoo. This will be used to create links for images and <see cref="Abstractions.Models.Page{T}"/>. /// The base URL of Kyoo. This will be used to create links for images and
/// <see cref="Abstractions.Models.Page{T}"/>.
/// </summary> /// </summary>
protected Uri BaseURL { get; } protected Uri BaseURL { get; }
@ -110,7 +111,7 @@ namespace Kyoo.Core.Api
/// <remarks> /// <remarks>
/// Get a specific resource via it's slug (a unique, human readable identifier). /// Get a specific resource via it's slug (a unique, human readable identifier).
/// </remarks> /// </remarks>
/// <param name="slug" example="1">The slug of the resource to retrieve.</param> /// <param name="slug">The slug of the resource to retrieve.</param>
/// <returns>The retrieved resource.</returns> /// <returns>The retrieved resource.</returns>
/// <response code="404">A resource with the given ID does not exist.</response> /// <response code="404">A resource with the given ID does not exist.</response>
[HttpGet("{slug}")] [HttpGet("{slug}")]
@ -166,7 +167,8 @@ namespace Kyoo.Core.Api
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
public async Task<ActionResult<Page<T>>> GetAll([FromQuery] string sortBy, public async Task<ActionResult<Page<T>>> GetAll(
[FromQuery] string sortBy,
[FromQuery] int afterID, [FromQuery] int afterID,
[FromQuery] Dictionary<string, string> where, [FromQuery] Dictionary<string, string> where,
[FromQuery] int limit = 20) [FromQuery] int limit = 20)
@ -253,9 +255,20 @@ namespace Kyoo.Core.Api
} }
} }
/// <summary>
/// Delete by ID
/// </summary>
/// <remarks>
/// Delete one item via it's ID.
/// </remarks>
/// <param name="id">The ID of the resource to delete.</param>
/// <returns>The item has successfully been deleted.</returns>
/// <response code="404">No item could be found with the given id.</response>
[HttpDelete("{id:int}")] [HttpDelete("{id:int}")]
[PartialPermission(Kind.Delete)] [PartialPermission(Kind.Delete)]
public virtual async Task<IActionResult> Delete(int id) [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> Delete(int id)
{ {
try try
{ {
@ -269,9 +282,20 @@ namespace Kyoo.Core.Api
return Ok(); return Ok();
} }
/// <summary>
/// Delete by slug
/// </summary>
/// <remarks>
/// Delete one item via it's slug (an unique, human-readable identifier).
/// </remarks>
/// <param name="slug">The slug of the resource to delete.</param>
/// <returns>The item has successfully been deleted.</returns>
/// <response code="404">No item could be found with the given slug.</response>
[HttpDelete("{slug}")] [HttpDelete("{slug}")]
[PartialPermission(Kind.Delete)] [PartialPermission(Kind.Delete)]
public virtual async Task<IActionResult> Delete(string slug) [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> Delete(string slug)
{ {
try try
{ {
@ -285,17 +309,28 @@ namespace Kyoo.Core.Api
return Ok(); return Ok();
} }
/// <summary>
/// Delete all where
/// </summary>
/// <remarks>
/// Delete all items matching the given filters. If no filter is specified, delete all items.
/// </remarks>
/// <param name="where">The list of filters.</param>
/// <returns>The item(s) has successfully been deleted.</returns>
/// <response code="400">One or multiple filters are invalid.</response>
[HttpDelete] [HttpDelete]
[PartialPermission(Kind.Delete)] [PartialPermission(Kind.Delete)]
public virtual async Task<IActionResult> Delete(Dictionary<string, string> where) [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
public async Task<IActionResult> Delete([FromQuery] Dictionary<string, string> where)
{ {
try try
{ {
await _repository.DeleteAll(ApiHelper.ParseWhere<T>(where)); await _repository.DeleteAll(ApiHelper.ParseWhere<T>(where));
} }
catch (ItemNotFoundException) catch (ArgumentException ex)
{ {
return NotFound(); return BadRequest(new RequestError(ex.Message));
} }
return Ok(); return Ok();