Adding the missing documentation on every classes and methods

This commit is contained in:
Zoe Roux 2021-10-07 21:48:40 +02:00
parent 6ee5f3ef24
commit fd50a2dedc
18 changed files with 159 additions and 47 deletions

View File

@ -45,7 +45,6 @@
<PropertyGroup Condition="$(CheckCodingStyle) == true">
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>CS1591;SA1600;SA1601</NoWarn>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)../Kyoo.ruleset</CodeAnalysisRuleSet>
<!-- <AnalysisMode>AllEnabledByDefault</AnalysisMode>-->

View File

@ -105,9 +105,17 @@ namespace Kyoo.Abstractions.Models
return CreateReference(path, typeof(T));
}
/// <summary>
/// Return a <see cref="ConfigurationReference"/> meaning that the given path is of any type.
/// It means that the type can't be edited.
/// </summary>
/// <param name="path">
/// The path that will be untyped (separated by ':' or "__". If empty, it will start at root).
/// </param>
/// <returns>A configuration reference representing a path of any type.</returns>
public static ConfigurationReference CreateUntyped(string path)
{
return new(path, null);
return new ConfigurationReference(path, null);
}
}
}

View File

@ -425,6 +425,11 @@ namespace Kyoo.Utils
return (T)method.MakeGenericMethod(types).Invoke(instance, args.ToArray());
}
/// <summary>
/// Convert a dictionary to a query string.
/// </summary>
/// <param name="query">The list of query parameters.</param>
/// <returns>A valid query string with all items in the dictionary.</returns>
public static string ToQueryString(this Dictionary<string, string> query)
{
if (!query.Any())
@ -432,6 +437,11 @@ namespace Kyoo.Utils
return "?" + string.Join('&', query.Select(x => $"{x.Key}={x.Value}"));
}
/// <summary>
/// Rethrow the exception without modifying the stack trace.
/// This is similar to the <c>rethrow;</c> code but is useful when the exception is not in a catch block.
/// </summary>
/// <param name="ex">The exception to rethrow.</param>
[System.Diagnostics.CodeAnalysis.DoesNotReturn]
public static void ReThrow([NotNull] this Exception ex)
{

View File

@ -23,6 +23,9 @@ using IdentityModel;
namespace Kyoo.Authentication
{
/// <summary>
/// Some functions to handle password management.
/// </summary>
public static class PasswordUtils
{
/// <summary>

View File

@ -32,6 +32,11 @@ using Newtonsoft.Json.Linq;
namespace Kyoo.Core.Controllers
{
/// <summary>
/// A class to ease configuration management. This work WITH Microsoft's package, you can still use IOptions patterns
/// to access your options, this manager ease dynamic work and editing.
/// It works with <see cref="ConfigurationReference"/>.
/// </summary>
public class ConfigurationManager : IConfigurationManager
{
/// <summary>

View File

@ -165,11 +165,13 @@ namespace Kyoo.Core.Controllers
});
}
/// <inheritdoc />
public Task<ICollection<Track>> ExtractInfos(Episode episode, bool reExtract)
{
return _transcoder.ExtractInfos(episode, reExtract);
}
/// <inheritdoc />
public IActionResult Transmux(Episode episode)
{
return _transcoder.Transmux(episode);

View File

@ -29,6 +29,9 @@ using Kyoo.Utils;
namespace Kyoo.Core.Controllers
{
/// <summary>
/// An class to interact with the database. Every repository is mapped through here.
/// </summary>
public class LibraryManager : ILibraryManager
{
/// <summary>

View File

@ -29,6 +29,10 @@ namespace Kyoo.Core.Controllers
/// </summary>
public class PassthroughPermissionValidator : IPermissionValidator
{
/// <summary>
/// Create a new <see cref="PassthroughPermissionValidator"/>.
/// </summary>
/// <param name="logger">The logger used to warn that no real permission validator exists.</param>
[SuppressMessage("ReSharper", "SuggestBaseTypeForParameterInConstructor",
Justification = "ILogger should include the typeparam for context.")]
public PassthroughPermissionValidator(ILogger<PassthroughPermissionValidator> logger)

View File

@ -22,6 +22,9 @@ using Newtonsoft.Json;
namespace Kyoo.Core
{
/// <summary>
/// A class containing helper methods.
/// </summary>
public static class Helper
{
/// <summary>

View File

@ -22,24 +22,53 @@ using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using JetBrains.Annotations;
using Kyoo.Abstractions.Models;
namespace Kyoo.Core.Api
{
/// <summary>
/// A static class containing methods to parse the <c>where</c> query string.
/// </summary>
public static class ApiHelper
{
public static Expression StringCompatibleExpression(Func<Expression, Expression, BinaryExpression> operand,
Expression left,
Expression right)
/// <summary>
/// Make an expression (like
/// <see cref="Expression.LessThan(System.Linq.Expressions.Expression,System.Linq.Expressions.Expression)"/>
/// compatible with strings). If the expressions are not strings, the given <paramref name="operand"/> is
/// constructed but if the expressions are strings, this method make the <param name="operand"/> compatible with
/// strings.
/// </summary>
/// <param name="operand">
/// The expression to make compatible. It should be something like
/// <see cref="Expression.LessThan(System.Linq.Expressions.Expression,System.Linq.Expressions.Expression)"/> or
/// <see cref="Expression.Equal(System.Linq.Expressions.Expression,System.Linq.Expressions.Expression)"/>.
/// </param>
/// <param name="left">The first parameter to compare.</param>
/// <param name="right">The second parameter to compare.</param>
/// <returns>A comparison expression compatible with strings</returns>
public static BinaryExpression StringCompatibleExpression(
[NotNull] Func<Expression, Expression, BinaryExpression> operand,
[NotNull] Expression left,
[NotNull] Expression right)
{
if (left is MemberExpression member && ((PropertyInfo)member.Member).PropertyType == typeof(string))
{
MethodCallExpression call = Expression.Call(typeof(string), "Compare", null, left, right);
return operand(call, Expression.Constant(0));
}
return operand(left, right);
if (left is not MemberExpression member || ((PropertyInfo)member.Member).PropertyType != typeof(string))
return operand(left, right);
MethodCallExpression call = Expression.Call(typeof(string), "Compare", null, left, right);
return operand(call, Expression.Constant(0));
}
/// <summary>
/// Parse a <c>where</c> query for the given <typeparamref name="T"/>. Items can be filtered by any property
/// of the given type.
/// </summary>
/// <param name="where">The list of filters.</param>
/// <param name="defaultWhere">
/// A custom expression to initially filter a collection. It will be combined with the parsed expression.
/// </param>
/// <typeparam name="T">The type to create filters for.</typeparam>
/// <exception cref="ArgumentException">A filter is invalid.</exception>
/// <returns>An expression representing the filters that can be used anywhere or compiled</returns>
public static Expression<Func<T, bool>> ParseWhere<T>(Dictionary<string, string> where,
Expression<Func<T, bool>> defaultWhere = null)
{
@ -96,18 +125,17 @@ namespace Kyoo.Core.Api
"not" when valueExpr == null => _ResourceEqual(propertyExpr, value, true),
"eq" => Expression.Equal(propertyExpr, valueExpr),
"not" => Expression.NotEqual(propertyExpr, valueExpr!),
"lt" => StringCompatibleExpression(Expression.LessThan, propertyExpr, valueExpr),
"lte" => StringCompatibleExpression(Expression.LessThanOrEqual, propertyExpr, valueExpr),
"gt" => StringCompatibleExpression(Expression.GreaterThan, propertyExpr, valueExpr),
"gte" => StringCompatibleExpression(Expression.GreaterThanOrEqual, propertyExpr, valueExpr),
"not" => Expression.NotEqual(propertyExpr, valueExpr),
"lt" => StringCompatibleExpression(Expression.LessThan, propertyExpr, valueExpr!),
"lte" => StringCompatibleExpression(Expression.LessThanOrEqual, propertyExpr, valueExpr!),
"gt" => StringCompatibleExpression(Expression.GreaterThan, propertyExpr, valueExpr!),
"gte" => StringCompatibleExpression(Expression.GreaterThanOrEqual, propertyExpr, valueExpr!),
_ => throw new ArgumentException($"Invalid operand: {operand}")
};
if (expression != null)
expression = Expression.AndAlso(expression, condition);
else
expression = condition;
expression = expression != null
? Expression.AndAlso(expression, condition)
: condition;
}
return Expression.Lambda<Func<T, bool>>(expression!, param);

View File

@ -27,7 +27,10 @@ using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Core.Api
{
public class BaseApi : ControllerBase
/// <summary>
/// A common API containing custom methods to help inheritors.
/// </summary>
public abstract class BaseApi : ControllerBase
{
/// <summary>
/// Construct and return a page from an api.

View File

@ -32,8 +32,13 @@ using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Core.Api
{
/// <summary>
/// An attribute to put on most controllers. It handle fields loading (only retuning fields requested and if they
/// are requested, load them) and help for the <c>where</c> query parameter.
/// </summary>
public class ResourceViewAttribute : ActionFilterAttribute
{
/// <inheritdoc />
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.ActionArguments.TryGetValue("where", out object dic) && dic is Dictionary<string, string> where)
@ -92,6 +97,7 @@ namespace Kyoo.Core.Api
base.OnActionExecuting(context);
}
/// <inheritdoc />
public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
if (context.Result is ObjectResult result)

View File

@ -24,8 +24,13 @@ using Newtonsoft.Json.Linq;
namespace Kyoo.Core.Api
{
/// <summary>
/// A custom role's convertor to inline the person or the show depending on the value of
/// <see cref="PeopleRole.ForPeople"/>.
/// </summary>
public class PeopleRoleConverter : JsonConverter<PeopleRole>
{
/// <inheritdoc />
public override void WriteJson(JsonWriter writer, PeopleRole value, JsonSerializer serializer)
{
ICollection<PeopleRole> oldPeople = value.Show?.People;
@ -46,6 +51,7 @@ namespace Kyoo.Core.Api
value.People.Roles = oldRoles;
}
/// <inheritdoc />
public override PeopleRole ReadJson(JsonReader reader,
Type objectType,
PeopleRole existingValue,

View File

@ -41,9 +41,21 @@ namespace Kyoo.Core.Api
[ApiDefinition("Videos", Group = WatchGroup)]
public class VideoApi : Controller
{
/// <summary>
/// The library manager used to modify or retrieve information in the data store.
/// </summary>
private readonly ILibraryManager _libraryManager;
/// <summary>
/// The file system used to send video files.
/// </summary>
private readonly IFileSystem _files;
/// <summary>
/// Create a new <see cref="VideoApi"/>.
/// </summary>
/// <param name="libraryManager">The library manager used to retrieve episodes.</param>
/// <param name="files">The file manager used to send video files.</param>
public VideoApi(ILibraryManager libraryManager,
IFileSystem files)
{

View File

@ -24,8 +24,12 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Kyoo.Postgresql.Migrations
{
/// <summary>
/// The initial migration that build most of the database.
/// </summary>
public partial class Initial : Migration
{
/// <inheritdoc/>
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterDatabase()
@ -783,6 +787,7 @@ namespace Kyoo.Postgresql.Migrations
column: "episode_id");
}
/// <inheritdoc/>
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(

View File

@ -20,8 +20,12 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Kyoo.Postgresql.Migrations
{
/// <summary>
/// A migration that adds postgres triggers to update slugs.
/// </summary>
public partial class Triggers : Migration
{
/// <inheritdoc/>
protected override void Up(MigrationBuilder migrationBuilder)
{
// language=PostgreSQL
@ -42,7 +46,7 @@ namespace Kyoo.Postgresql.Migrations
// language=PostgreSQL
migrationBuilder.Sql(@"
CREATE TRIGGER season_slug_trigger BEFORE INSERT OR UPDATE OF season_number, show_id ON seasons
CREATE TRIGGER season_slug_trigger BEFORE INSERT OR UPDATE OF season_number, show_id ON seasons
FOR EACH ROW EXECUTE PROCEDURE season_slug_update();");
// language=PostgreSQL
@ -66,7 +70,7 @@ namespace Kyoo.Postgresql.Migrations
// language=PostgreSQL
migrationBuilder.Sql(@"
CREATE TRIGGER episode_slug_trigger
CREATE TRIGGER episode_slug_trigger
BEFORE INSERT OR UPDATE OF absolute_number, episode_number, season_number, show_id ON episodes
FOR EACH ROW EXECUTE PROCEDURE episode_slug_update();");
@ -80,7 +84,7 @@ namespace Kyoo.Postgresql.Migrations
UPDATE seasons SET slug = CONCAT(NEW.slug, '-s', season_number) WHERE show_id = NEW.id;
UPDATE episodes SET slug = CASE
WHEN season_number IS NULL AND episode_number IS NULL THEN NEW.slug
WHEN season_number IS NULL THEN CONCAT(NEW.slug, '-', absolute_number)
WHEN season_number IS NULL THEN CONCAT(NEW.slug, '-', absolute_number)
ELSE CONCAT(NEW.slug, '-s', season_number, 'e', episode_number)
END WHERE show_id = NEW.id;
RETURN NEW;
@ -128,7 +132,7 @@ namespace Kyoo.Postgresql.Migrations
BEGIN
IF NEW.track_index = 0 THEN
NEW.track_index := (SELECT COUNT(*) FROM tracks
WHERE episode_id = NEW.episode_id AND type = NEW.type
WHERE episode_id = NEW.episode_id AND type = NEW.type
AND language = NEW.language AND is_forced = NEW.is_forced);
END IF;
NEW.slug := CONCAT(
@ -149,7 +153,7 @@ namespace Kyoo.Postgresql.Migrations
$$;");
// language=PostgreSQL
migrationBuilder.Sql(@"
CREATE TRIGGER track_slug_trigger
CREATE TRIGGER track_slug_trigger
BEFORE INSERT OR UPDATE OF episode_id, is_forced, language, track_index, type ON tracks
FOR EACH ROW EXECUTE PROCEDURE track_slug_update();");
@ -167,11 +171,12 @@ namespace Kyoo.Postgresql.Migrations
INNER JOIN collections AS c ON l.collection_id = c.id
WHERE s.id = l.show_id))
UNION ALL
SELECT -c0.id, c0.slug, c0.name AS title, c0.overview, 'unknown'::status AS status,
SELECT -c0.id, c0.slug, c0.name AS title, c0.overview, 'unknown'::status AS status,
NULL AS start_air, NULL AS end_air, c0.images, 'collection'::item_type AS type
FROM collections AS c0");
}
/// <inheritdoc/>
protected override void Down(MigrationBuilder migrationBuilder)
{
// language=PostgreSQL

View File

@ -21,8 +21,12 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Kyoo.SqLite.Migrations
{
/// <summary>
/// The initial migration that build most of the database.
/// </summary>
public partial class Initial : Migration
{
/// <inheritdoc/>
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
@ -775,6 +779,7 @@ namespace Kyoo.SqLite.Migrations
column: "EpisodeID");
}
/// <inheritdoc/>
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(

View File

@ -20,31 +20,35 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Kyoo.SqLite.Migrations
{
/// <summary>
/// A migration that adds sqlite triggers to update slugs.
/// </summary>
public partial class Triggers : Migration
{
/// <inheritdoc/>
protected override void Up(MigrationBuilder migrationBuilder)
{
// language=SQLite
migrationBuilder.Sql(@"
CREATE TRIGGER SeasonSlugInsert AFTER INSERT ON Seasons FOR EACH ROW
BEGIN
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");
// language=SQLite
migrationBuilder.Sql(@"
CREATE TRIGGER SeasonSlugUpdate AFTER UPDATE OF SeasonNumber, ShowID ON Seasons FOR EACH ROW
BEGIN
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");
// language=SQLite
migrationBuilder.Sql(@"
CREATE TRIGGER EpisodeSlugInsert AFTER INSERT ON Episodes FOR EACH ROW
BEGIN
UPDATE Episodes
SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) ||
CREATE TRIGGER EpisodeSlugInsert AFTER INSERT ON Episodes FOR EACH ROW
BEGIN
UPDATE Episodes
SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) ||
CASE
WHEN SeasonNumber IS NULL AND AbsoluteNumber IS NULL THEN ''
WHEN SeasonNumber IS NULL THEN '-' || AbsoluteNumber
@ -54,11 +58,11 @@ namespace Kyoo.SqLite.Migrations
END");
// language=SQLite
migrationBuilder.Sql(@"
CREATE TRIGGER EpisodeSlugUpdate AFTER UPDATE OF AbsoluteNumber, EpisodeNumber, SeasonNumber, ShowID
ON Episodes FOR EACH ROW
BEGIN
UPDATE Episodes
SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) ||
CREATE TRIGGER EpisodeSlugUpdate AFTER UPDATE OF AbsoluteNumber, EpisodeNumber, SeasonNumber, ShowID
ON Episodes FOR EACH ROW
BEGIN
UPDATE Episodes
SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) ||
CASE
WHEN SeasonNumber IS NULL AND AbsoluteNumber IS NULL THEN ''
WHEN SeasonNumber IS NULL THEN '-' || AbsoluteNumber
@ -69,7 +73,7 @@ namespace Kyoo.SqLite.Migrations
// language=SQLite
migrationBuilder.Sql(@"
CREATE TRIGGER TrackSlugInsert
CREATE TRIGGER TrackSlugInsert
AFTER INSERT ON Tracks
FOR EACH ROW
BEGIN
@ -98,7 +102,7 @@ namespace Kyoo.SqLite.Migrations
END;");
// language=SQLite
migrationBuilder.Sql(@"
CREATE TRIGGER TrackSlugUpdate
CREATE TRIGGER TrackSlugUpdate
AFTER UPDATE OF EpisodeID, IsForced, Language, TrackIndex, Type ON Tracks
FOR EACH ROW
BEGIN
@ -107,7 +111,7 @@ namespace Kyoo.SqLite.Migrations
WHERE EpisodeID = new.EpisodeID AND Type = new.Type
AND Language = new.Language AND IsForced = new.IsForced
) WHERE ID = new.ID AND TrackIndex = 0;
UPDATE Tracks SET Slug =
UPDATE Tracks SET Slug =
(SELECT Slug FROM Episodes WHERE ID = EpisodeID) ||
'.' || Language ||
CASE (TrackIndex)
@ -128,7 +132,7 @@ namespace Kyoo.SqLite.Migrations
END;");
// language=SQLite
migrationBuilder.Sql(@"
CREATE TRIGGER EpisodeUpdateTracksSlug
CREATE TRIGGER EpisodeUpdateTracksSlug
AFTER UPDATE OF Slug ON Episodes
FOR EACH ROW
BEGIN
@ -157,8 +161,8 @@ namespace Kyoo.SqLite.Migrations
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;
UPDATE Episodes
SET Slug = new.Slug ||
UPDATE Episodes
SET Slug = new.Slug ||
CASE
WHEN SeasonNumber IS NULL AND AbsoluteNumber IS NULL THEN ''
WHEN SeasonNumber IS NULL THEN '-' || AbsoluteNumber
@ -181,11 +185,12 @@ namespace Kyoo.SqLite.Migrations
INNER JOIN Collections AS c ON l.CollectionID = c.ID
WHERE s.ID = l.ShowID))
UNION ALL
SELECT -c0.ID, c0.Slug, c0.Name AS Title, c0.Overview, 0 AS Status,
SELECT -c0.ID, c0.Slug, c0.Name AS Title, c0.Overview, 0 AS Status,
NULL AS StartAir, NULL AS EndAir, c0.Images, 2 AS Type
FROM collections AS c0");
}
/// <inheritdoc/>
protected override void Down(MigrationBuilder migrationBuilder)
{
// language=SQLite