mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-31 20:24:27 -04:00
Add issues api
This commit is contained in:
parent
050b420f9a
commit
9f003189e9
35
back/src/Kyoo.Abstractions/Controllers/IIssueRepository.cs
Normal file
35
back/src/Kyoo.Abstractions/Controllers/IIssueRepository.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Kyoo - A portable and vast media library solution.
|
||||||
|
// Copyright (c) Kyoo.
|
||||||
|
//
|
||||||
|
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
// Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// any later version.
|
||||||
|
//
|
||||||
|
// Kyoo is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Kyoo.Abstractions.Models;
|
||||||
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
|
|
||||||
|
namespace Kyoo.Abstractions.Controllers;
|
||||||
|
|
||||||
|
public interface IIssueRepository
|
||||||
|
{
|
||||||
|
Task<ICollection<Issue>> GetAll(Filter<Issue>? filter = default);
|
||||||
|
|
||||||
|
Task<int> GetCount(Filter<Issue>? filter = default);
|
||||||
|
|
||||||
|
Task<Issue> Upsert(Issue issue);
|
||||||
|
|
||||||
|
Task DeleteAll(Filter<Issue>? filter = default);
|
||||||
|
}
|
@ -45,7 +45,7 @@ public class Issue : IAddedDate
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Some extra data that could store domain-specific info.
|
/// Some extra data that could store domain-specific info.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Dictionary<string, object> Extra { get; set; }
|
public Dictionary<string, object> Extra { get; set; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public DateTime AddedDate { get; set; }
|
public DateTime AddedDate { get; set; }
|
||||||
|
140
back/src/Kyoo.Core/Controllers/Repositories/EfHelpers.cs
Normal file
140
back/src/Kyoo.Core/Controllers/Repositories/EfHelpers.cs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// Kyoo - A portable and vast media library solution.
|
||||||
|
// Copyright (c) Kyoo.
|
||||||
|
//
|
||||||
|
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
// Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// any later version.
|
||||||
|
//
|
||||||
|
// Kyoo is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// 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.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
|
using Kyoo.Postgresql;
|
||||||
|
using Kyoo.Utils;
|
||||||
|
|
||||||
|
namespace Kyoo.Core.Controllers;
|
||||||
|
|
||||||
|
public static class EfHelpers
|
||||||
|
{
|
||||||
|
public static Expression<Func<T, bool>> ToEfLambda<T>(this Filter<T>? filter)
|
||||||
|
{
|
||||||
|
if (filter == null)
|
||||||
|
return x => true;
|
||||||
|
|
||||||
|
ParameterExpression x = Expression.Parameter(typeof(T), "x");
|
||||||
|
|
||||||
|
Expression CmpRandomHandler(string cmp, string seed, Guid refId)
|
||||||
|
{
|
||||||
|
MethodInfo concat = typeof(string).GetMethod(
|
||||||
|
nameof(string.Concat),
|
||||||
|
new[] { typeof(string), typeof(string) }
|
||||||
|
)!;
|
||||||
|
Expression id = Expression.Call(
|
||||||
|
Expression.Property(x, "ID"),
|
||||||
|
nameof(Guid.ToString),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
Expression xrng = Expression.Call(concat, Expression.Constant(seed), id);
|
||||||
|
Expression left = Expression.Call(
|
||||||
|
typeof(DatabaseContext),
|
||||||
|
nameof(DatabaseContext.MD5),
|
||||||
|
null,
|
||||||
|
xrng
|
||||||
|
);
|
||||||
|
Expression right = Expression.Call(
|
||||||
|
typeof(DatabaseContext),
|
||||||
|
nameof(DatabaseContext.MD5),
|
||||||
|
null,
|
||||||
|
Expression.Constant($"{seed}{refId}")
|
||||||
|
);
|
||||||
|
return cmp switch
|
||||||
|
{
|
||||||
|
"=" => Expression.Equal(left, right),
|
||||||
|
"<" => Expression.GreaterThan(left, right),
|
||||||
|
">" => Expression.LessThan(left, right),
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
BinaryExpression StringCompatibleExpression(
|
||||||
|
Func<Expression, Expression, BinaryExpression> operand,
|
||||||
|
string property,
|
||||||
|
object value
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var left = Expression.Property(x, property);
|
||||||
|
var right = Expression.Constant(value, ((PropertyInfo)left.Member).PropertyType);
|
||||||
|
if (left.Type != typeof(string))
|
||||||
|
return operand(left, right);
|
||||||
|
MethodCallExpression call = Expression.Call(
|
||||||
|
typeof(string),
|
||||||
|
"Compare",
|
||||||
|
null,
|
||||||
|
left,
|
||||||
|
right
|
||||||
|
);
|
||||||
|
return operand(call, Expression.Constant(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression Exp(
|
||||||
|
Func<Expression, Expression, BinaryExpression> operand,
|
||||||
|
string property,
|
||||||
|
object? value
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var prop = Expression.Property(x, property);
|
||||||
|
var val = Expression.Constant(value, ((PropertyInfo)prop.Member).PropertyType);
|
||||||
|
return operand(prop, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression Parse(Filter<T> f)
|
||||||
|
{
|
||||||
|
return f switch
|
||||||
|
{
|
||||||
|
Filter<T>.And(var first, var second)
|
||||||
|
=> Expression.AndAlso(Parse(first), Parse(second)),
|
||||||
|
Filter<T>.Or(var first, var second)
|
||||||
|
=> Expression.OrElse(Parse(first), Parse(second)),
|
||||||
|
Filter<T>.Not(var inner) => Expression.Not(Parse(inner)),
|
||||||
|
Filter<T>.Eq(var property, var value) => Exp(Expression.Equal, property, value),
|
||||||
|
Filter<T>.Ne(var property, var value) => Exp(Expression.NotEqual, property, value),
|
||||||
|
Filter<T>.Gt(var property, var value)
|
||||||
|
=> StringCompatibleExpression(Expression.GreaterThan, property, value),
|
||||||
|
Filter<T>.Ge(var property, var value)
|
||||||
|
=> StringCompatibleExpression(Expression.GreaterThanOrEqual, property, value),
|
||||||
|
Filter<T>.Lt(var property, var value)
|
||||||
|
=> StringCompatibleExpression(Expression.LessThan, property, value),
|
||||||
|
Filter<T>.Le(var property, var value)
|
||||||
|
=> StringCompatibleExpression(Expression.LessThanOrEqual, property, value),
|
||||||
|
Filter<T>.Has(var property, var value)
|
||||||
|
=> Expression.Call(
|
||||||
|
typeof(Enumerable),
|
||||||
|
"Contains",
|
||||||
|
new[] { value.GetType() },
|
||||||
|
Expression.Property(x, property),
|
||||||
|
Expression.Constant(value)
|
||||||
|
),
|
||||||
|
Filter<T>.CmpRandom(var op, var seed, var refId)
|
||||||
|
=> CmpRandomHandler(op, seed, refId),
|
||||||
|
Filter<T>.Lambda(var lambda)
|
||||||
|
=> ExpressionArgumentReplacer.ReplaceParams(lambda.Body, lambda.Parameters, x),
|
||||||
|
_ => throw new NotImplementedException(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression body = Parse(filter);
|
||||||
|
return Expression.Lambda<Func<T, bool>>(body, x);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
// Kyoo - A portable and vast media library solution.
|
||||||
|
// Copyright (c) Kyoo.
|
||||||
|
//
|
||||||
|
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
// Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// any later version.
|
||||||
|
//
|
||||||
|
// Kyoo is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// 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.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Kyoo.Abstractions.Controllers;
|
||||||
|
using Kyoo.Abstractions.Models;
|
||||||
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
|
using Kyoo.Postgresql;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Kyoo.Core.Controllers;
|
||||||
|
|
||||||
|
public class IssueRepository(DatabaseContext database) : IIssueRepository
|
||||||
|
{
|
||||||
|
public async Task<ICollection<Issue>> GetAll(Filter<Issue>? filter = null)
|
||||||
|
{
|
||||||
|
return await database.Issues.Where(filter.ToEfLambda()).ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<int> GetCount(Filter<Issue>? filter = null)
|
||||||
|
{
|
||||||
|
return database.Issues.Where(filter.ToEfLambda()).CountAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Issue> Upsert(Issue issue)
|
||||||
|
{
|
||||||
|
issue.AddedDate = DateTime.UtcNow;
|
||||||
|
await database.Issues.Upsert(issue).RunAsync();
|
||||||
|
return issue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task DeleteAll(Filter<Issue>? filter = null)
|
||||||
|
{
|
||||||
|
return database.Issues.Where(filter.ToEfLambda()).ExecuteDeleteAsync();
|
||||||
|
}
|
||||||
|
}
|
@ -118,125 +118,6 @@ namespace Kyoo.Core.Controllers
|
|||||||
return _Sort(query, sortBy, false).ThenBy(x => x.Id);
|
return _Sort(query, sortBy, false).ThenBy(x => x.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Expression<Func<T, bool>> ParseFilter(Filter<T>? filter)
|
|
||||||
{
|
|
||||||
if (filter == null)
|
|
||||||
return x => true;
|
|
||||||
|
|
||||||
ParameterExpression x = Expression.Parameter(typeof(T), "x");
|
|
||||||
|
|
||||||
Expression CmpRandomHandler(string cmp, string seed, Guid refId)
|
|
||||||
{
|
|
||||||
MethodInfo concat = typeof(string).GetMethod(
|
|
||||||
nameof(string.Concat),
|
|
||||||
new[] { typeof(string), typeof(string) }
|
|
||||||
)!;
|
|
||||||
Expression id = Expression.Call(
|
|
||||||
Expression.Property(x, "ID"),
|
|
||||||
nameof(Guid.ToString),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
Expression xrng = Expression.Call(concat, Expression.Constant(seed), id);
|
|
||||||
Expression left = Expression.Call(
|
|
||||||
typeof(DatabaseContext),
|
|
||||||
nameof(DatabaseContext.MD5),
|
|
||||||
null,
|
|
||||||
xrng
|
|
||||||
);
|
|
||||||
Expression right = Expression.Call(
|
|
||||||
typeof(DatabaseContext),
|
|
||||||
nameof(DatabaseContext.MD5),
|
|
||||||
null,
|
|
||||||
Expression.Constant($"{seed}{refId}")
|
|
||||||
);
|
|
||||||
return cmp switch
|
|
||||||
{
|
|
||||||
"=" => Expression.Equal(left, right),
|
|
||||||
"<" => Expression.GreaterThan(left, right),
|
|
||||||
">" => Expression.LessThan(left, right),
|
|
||||||
_ => throw new NotImplementedException()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
BinaryExpression StringCompatibleExpression(
|
|
||||||
Func<Expression, Expression, BinaryExpression> operand,
|
|
||||||
string property,
|
|
||||||
object value
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var left = Expression.Property(x, property);
|
|
||||||
var right = Expression.Constant(value, ((PropertyInfo)left.Member).PropertyType);
|
|
||||||
if (left.Type != typeof(string))
|
|
||||||
return operand(left, right);
|
|
||||||
MethodCallExpression call = Expression.Call(
|
|
||||||
typeof(string),
|
|
||||||
"Compare",
|
|
||||||
null,
|
|
||||||
left,
|
|
||||||
right
|
|
||||||
);
|
|
||||||
return operand(call, Expression.Constant(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
Expression Exp(
|
|
||||||
Func<Expression, Expression, BinaryExpression> operand,
|
|
||||||
string property,
|
|
||||||
object? value
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var prop = Expression.Property(x, property);
|
|
||||||
var val = Expression.Constant(value, ((PropertyInfo)prop.Member).PropertyType);
|
|
||||||
return operand(prop, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
Expression Parse(Filter<T> f)
|
|
||||||
{
|
|
||||||
return f switch
|
|
||||||
{
|
|
||||||
Filter<T>.And(var first, var second)
|
|
||||||
=> Expression.AndAlso(Parse(first), Parse(second)),
|
|
||||||
Filter<T>.Or(var first, var second)
|
|
||||||
=> Expression.OrElse(Parse(first), Parse(second)),
|
|
||||||
Filter<T>.Not(var inner) => Expression.Not(Parse(inner)),
|
|
||||||
Filter<T>.Eq(var property, var value) => Exp(Expression.Equal, property, value),
|
|
||||||
Filter<T>.Ne(var property, var value)
|
|
||||||
=> Exp(Expression.NotEqual, property, value),
|
|
||||||
Filter<T>.Gt(var property, var value)
|
|
||||||
=> StringCompatibleExpression(Expression.GreaterThan, property, value),
|
|
||||||
Filter<T>.Ge(var property, var value)
|
|
||||||
=> StringCompatibleExpression(
|
|
||||||
Expression.GreaterThanOrEqual,
|
|
||||||
property,
|
|
||||||
value
|
|
||||||
),
|
|
||||||
Filter<T>.Lt(var property, var value)
|
|
||||||
=> StringCompatibleExpression(Expression.LessThan, property, value),
|
|
||||||
Filter<T>.Le(var property, var value)
|
|
||||||
=> StringCompatibleExpression(Expression.LessThanOrEqual, property, value),
|
|
||||||
Filter<T>.Has(var property, var value)
|
|
||||||
=> Expression.Call(
|
|
||||||
typeof(Enumerable),
|
|
||||||
"Contains",
|
|
||||||
new[] { value.GetType() },
|
|
||||||
Expression.Property(x, property),
|
|
||||||
Expression.Constant(value)
|
|
||||||
),
|
|
||||||
Filter<T>.CmpRandom(var op, var seed, var refId)
|
|
||||||
=> CmpRandomHandler(op, seed, refId),
|
|
||||||
Filter<T>.Lambda(var lambda)
|
|
||||||
=> ExpressionArgumentReplacer.ReplaceParams(
|
|
||||||
lambda.Body,
|
|
||||||
lambda.Parameters,
|
|
||||||
x
|
|
||||||
),
|
|
||||||
_ => throw new NotImplementedException(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Expression body = Parse(filter);
|
|
||||||
return Expression.Lambda<Func<T, bool>>(body, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected IQueryable<T> AddIncludes(IQueryable<T> query, Include<T>? include)
|
protected IQueryable<T> AddIncludes(IQueryable<T> query, Include<T>? include)
|
||||||
{
|
{
|
||||||
if (include == null)
|
if (include == null)
|
||||||
@ -405,7 +286,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
filter = Filter.And(filter, keysetFilter);
|
filter = Filter.And(filter, keysetFilter);
|
||||||
}
|
}
|
||||||
if (filter != null)
|
if (filter != null)
|
||||||
query = query.Where(ParseFilter(filter));
|
query = query.Where(filter.ToEfLambda());
|
||||||
|
|
||||||
if (limit.Reverse)
|
if (limit.Reverse)
|
||||||
query = query.Reverse();
|
query = query.Reverse();
|
||||||
@ -422,7 +303,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
{
|
{
|
||||||
IQueryable<T> query = Database.Set<T>();
|
IQueryable<T> query = Database.Set<T>();
|
||||||
if (filter != null)
|
if (filter != null)
|
||||||
query = query.Where(ParseFilter(filter));
|
query = query.Where(filter.ToEfLambda());
|
||||||
return query.CountAsync();
|
return query.CountAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +75,11 @@ namespace Kyoo.Core
|
|||||||
.As<IWatchStatusRepository>()
|
.As<IWatchStatusRepository>()
|
||||||
.AsSelf()
|
.AsSelf()
|
||||||
.InstancePerLifetimeScope();
|
.InstancePerLifetimeScope();
|
||||||
|
builder
|
||||||
|
.RegisterType<IssueRepository>()
|
||||||
|
.As<IIssueRepository>()
|
||||||
|
.AsSelf()
|
||||||
|
.InstancePerLifetimeScope();
|
||||||
builder.RegisterType<SqlVariableContext>().InstancePerLifetimeScope();
|
builder.RegisterType<SqlVariableContext>().InstancePerLifetimeScope();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
119
back/src/Kyoo.Core/Views/Metadata/IssueApi.cs
Normal file
119
back/src/Kyoo.Core/Views/Metadata/IssueApi.cs
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
// Kyoo - A portable and vast media library solution.
|
||||||
|
// Copyright (c) Kyoo.
|
||||||
|
//
|
||||||
|
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
// Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// any later version.
|
||||||
|
//
|
||||||
|
// Kyoo is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Kyoo.Abstractions.Controllers;
|
||||||
|
using Kyoo.Abstractions.Models;
|
||||||
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using static Kyoo.Abstractions.Models.Utils.Constants;
|
||||||
|
|
||||||
|
namespace Kyoo.Core.Api;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create or list issues on the instance
|
||||||
|
/// </summary>
|
||||||
|
[Route("issues")]
|
||||||
|
[Route("issue", Order = AlternativeRoute)]
|
||||||
|
[ApiController]
|
||||||
|
[PartialPermission(nameof(Issue), Group = Group.Admin)]
|
||||||
|
[ApiDefinition("Issue", Group = AdminGroup)]
|
||||||
|
public class IssueApi(IIssueRepository issues) : Controller
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Get count
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Get the number of issues that match the filters.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="filter">A list of filters to respect.</param>
|
||||||
|
/// <returns>How many issues matched that filter.</returns>
|
||||||
|
/// <response code="400">Invalid filters.</response>
|
||||||
|
[HttpGet("count")]
|
||||||
|
[PartialPermission(Kind.Read)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
||||||
|
public async Task<ActionResult<int>> GetCount([FromQuery] Filter<Issue> filter)
|
||||||
|
{
|
||||||
|
return await issues.GetCount(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all issues
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Get all issues that match the given filter.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="filter">Filter the returned items.</param>
|
||||||
|
/// <returns>A list of issues that match every filters.</returns>
|
||||||
|
/// <response code="400">Invalid filters or sort information.</response>
|
||||||
|
[HttpGet]
|
||||||
|
[PartialPermission(Kind.Read)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
||||||
|
public async Task<ActionResult<ICollection<Issue>>> GetAll([FromQuery] Filter<Issue>? filter)
|
||||||
|
{
|
||||||
|
return Ok(await issues.GetAll(filter));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Upsert issue
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Create or update an issue.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="issue">The issue to create.</param>
|
||||||
|
/// <returns>The created issue.</returns>
|
||||||
|
/// <response code="400">The issue in the request body is invalid.</response>
|
||||||
|
[HttpPost]
|
||||||
|
[PartialPermission(Kind.Create)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
||||||
|
public async Task<ActionResult<Issue>> Create([FromBody] Issue issue)
|
||||||
|
{
|
||||||
|
return await issues.Upsert(issue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete issues
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Delete all issues matching the given filters.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="filter">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]
|
||||||
|
[PartialPermission(Kind.Delete)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
||||||
|
public async Task<IActionResult> Delete([FromQuery] Filter<Issue> filter)
|
||||||
|
{
|
||||||
|
if (filter == null)
|
||||||
|
return BadRequest(
|
||||||
|
new RequestError("Incule a filter to delete items, all items won't be deleted.")
|
||||||
|
);
|
||||||
|
|
||||||
|
await issues.DeleteAll(filter);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
}
|
@ -39,7 +39,7 @@ namespace Kyoo.Core.Api;
|
|||||||
[PartialPermission(nameof(User), Group = Group.Admin)]
|
[PartialPermission(nameof(User), Group = Group.Admin)]
|
||||||
[ApiDefinition("Users", Group = ResourcesGroup)]
|
[ApiDefinition("Users", Group = ResourcesGroup)]
|
||||||
public class UserApi(ILibraryManager libraryManager, IThumbnailsManager thumbs)
|
public class UserApi(ILibraryManager libraryManager, IThumbnailsManager thumbs)
|
||||||
: CrudApi<User>(libraryManager.Users)
|
: CrudApi<User>(libraryManager!.Users)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get profile picture
|
/// Get profile picture
|
||||||
|
@ -409,9 +409,7 @@ namespace Kyoo.Postgresql
|
|||||||
|
|
||||||
modelBuilder.Entity<Movie>().Ignore(x => x.Links);
|
modelBuilder.Entity<Movie>().Ignore(x => x.Links);
|
||||||
|
|
||||||
|
modelBuilder.Entity<Issue>().HasKey(x => new { x.Domain, x.Cause });
|
||||||
modelBuilder.Entity<Issue>()
|
|
||||||
.HasKey(x => new { x.Domain, x.Cause });
|
|
||||||
|
|
||||||
// TODO: Waiting for https://github.com/dotnet/efcore/issues/29825
|
// TODO: Waiting for https://github.com/dotnet/efcore/issues/29825
|
||||||
// modelBuilder.Entity<T>()
|
// modelBuilder.Entity<T>()
|
||||||
@ -419,7 +417,8 @@ namespace Kyoo.Postgresql
|
|||||||
// {
|
// {
|
||||||
// x.ToJson();
|
// x.ToJson();
|
||||||
// });
|
// });
|
||||||
modelBuilder.Entity<Issue>()
|
modelBuilder
|
||||||
|
.Entity<Issue>()
|
||||||
.Property(x => x.Extra)
|
.Property(x => x.Extra)
|
||||||
.HasConversion(
|
.HasConversion(
|
||||||
v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
|
v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
|
||||||
|
@ -13,7 +13,8 @@ namespace Kyoo.Postgresql.Migrations
|
|||||||
{
|
{
|
||||||
migrationBuilder.DropForeignKey(
|
migrationBuilder.DropForeignKey(
|
||||||
name: "fk_show_watch_status_episodes_next_episode_id",
|
name: "fk_show_watch_status_episodes_next_episode_id",
|
||||||
table: "show_watch_status");
|
table: "show_watch_status"
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "issues",
|
name: "issues",
|
||||||
@ -23,12 +24,17 @@ namespace Kyoo.Postgresql.Migrations
|
|||||||
cause = table.Column<string>(type: "text", nullable: false),
|
cause = table.Column<string>(type: "text", nullable: false),
|
||||||
reason = table.Column<string>(type: "text", nullable: false),
|
reason = table.Column<string>(type: "text", nullable: false),
|
||||||
extra = table.Column<string>(type: "json", nullable: false),
|
extra = table.Column<string>(type: "json", nullable: false),
|
||||||
added_date = table.Column<DateTime>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'")
|
added_date = table.Column<DateTime>(
|
||||||
|
type: "timestamp with time zone",
|
||||||
|
nullable: false,
|
||||||
|
defaultValueSql: "now() at time zone 'utc'"
|
||||||
|
)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
table.PrimaryKey("pk_issues", x => new { x.domain, x.cause });
|
table.PrimaryKey("pk_issues", x => new { x.domain, x.cause });
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.AddForeignKey(
|
migrationBuilder.AddForeignKey(
|
||||||
name: "fk_show_watch_status_episodes_next_episode_id",
|
name: "fk_show_watch_status_episodes_next_episode_id",
|
||||||
@ -36,7 +42,8 @@ namespace Kyoo.Postgresql.Migrations
|
|||||||
column: "next_episode_id",
|
column: "next_episode_id",
|
||||||
principalTable: "episodes",
|
principalTable: "episodes",
|
||||||
principalColumn: "id",
|
principalColumn: "id",
|
||||||
onDelete: ReferentialAction.SetNull);
|
onDelete: ReferentialAction.SetNull
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -44,17 +51,18 @@ namespace Kyoo.Postgresql.Migrations
|
|||||||
{
|
{
|
||||||
migrationBuilder.DropForeignKey(
|
migrationBuilder.DropForeignKey(
|
||||||
name: "fk_show_watch_status_episodes_next_episode_id",
|
name: "fk_show_watch_status_episodes_next_episode_id",
|
||||||
table: "show_watch_status");
|
table: "show_watch_status"
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(name: "issues");
|
||||||
name: "issues");
|
|
||||||
|
|
||||||
migrationBuilder.AddForeignKey(
|
migrationBuilder.AddForeignKey(
|
||||||
name: "fk_show_watch_status_episodes_next_episode_id",
|
name: "fk_show_watch_status_episodes_next_episode_id",
|
||||||
table: "show_watch_status",
|
table: "show_watch_status",
|
||||||
column: "next_episode_id",
|
column: "next_episode_id",
|
||||||
principalTable: "episodes",
|
principalTable: "episodes",
|
||||||
principalColumn: "id");
|
principalColumn: "id"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user