mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-06-08 16:14:15 -04:00
* Updated to net7.0 * Updated GA to .net 7 * Updated System.IO.Abstractions to use New factory. * Converted Regex into SourceGenerator in Parser. * Updated more regex to source generators. * Enabled Nullability and more regex changes throughout codebase. * Parser is 100% GeneratedRegexified * Lots of nullability code * Enabled nullability for all repositories. * Fixed another unit test * Refactored some code around and took care of some todos. * Updating code for nullability and cleaning up methods that aren't used anymore. Refctored all uses of Parser.Normalize() to use new extension * More nullability exercises. 500 warnings to go. * Fixed a bug where custom file uploads for entities wouldn't save in webP. * Nullability is done for all DTOs * Fixed all unit tests and nullability for the project. Only OPDS is left which will be done with an upcoming OPDS enhancement. * Use localization in book service after validating * Code smells * Switched to preview build of swashbuckle for .net7 support * Fixed up merge issues * Disable emulate comic book when on single page reader * Fixed a regression where double page renderer wouldn't layout the images correctly * Updated to swashbuckle which support .net 7 * Fixed a bad GA action * Some code cleanup * More code smells * Took care of most of nullable issues * Fixed a broken test due to having more than one test run in parallel * I'm really not sure why the unit tests are failing or are so extremely slow on .net 7 * Updated all dependencies * Fixed up build and removed hardcoded framework from build scripts. (this merge removes Regex Source generators). Unit tests are completely busted. * Unit tests and code cleanup. Needs shakeout now. * Adjusted Series model since a few fields are not-nullable. Removed dead imports on the project. * Refactored to use Builder pattern for all unit tests. * Switched nullability down to warnings. It wasn't possible to switch due to constraint issues in DB Migration.
291 lines
9.3 KiB
C#
291 lines
9.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Linq.Expressions;
|
|
using System.Threading.Tasks;
|
|
using API.Data.Misc;
|
|
using API.Data.Repositories;
|
|
using API.Entities;
|
|
using API.Entities.Enums;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace API.Extensions;
|
|
|
|
public static class QueryableExtensions
|
|
{
|
|
public static IQueryable<Series> RestrictAgainstAgeRestriction(this IQueryable<Series> queryable, AgeRestriction restriction)
|
|
{
|
|
if (restriction.AgeRating == AgeRating.NotApplicable) return queryable;
|
|
var q = queryable.Where(s => s.Metadata.AgeRating <= restriction.AgeRating);
|
|
if (!restriction.IncludeUnknowns)
|
|
{
|
|
return q.Where(s => s.Metadata.AgeRating != AgeRating.Unknown);
|
|
}
|
|
|
|
return q;
|
|
}
|
|
|
|
public static IQueryable<CollectionTag> RestrictAgainstAgeRestriction(this IQueryable<CollectionTag> queryable, AgeRestriction restriction)
|
|
{
|
|
if (restriction.AgeRating == AgeRating.NotApplicable) return queryable;
|
|
|
|
if (restriction.IncludeUnknowns)
|
|
{
|
|
return queryable.Where(c => c.SeriesMetadatas.All(sm =>
|
|
sm.AgeRating <= restriction.AgeRating));
|
|
}
|
|
|
|
return queryable.Where(c => c.SeriesMetadatas.All(sm =>
|
|
sm.AgeRating <= restriction.AgeRating && sm.AgeRating > AgeRating.Unknown));
|
|
}
|
|
|
|
public static IQueryable<Genre> RestrictAgainstAgeRestriction(this IQueryable<Genre> queryable, AgeRestriction restriction)
|
|
{
|
|
if (restriction.AgeRating == AgeRating.NotApplicable) return queryable;
|
|
|
|
if (restriction.IncludeUnknowns)
|
|
{
|
|
return queryable.Where(c => c.SeriesMetadatas.All(sm =>
|
|
sm.AgeRating <= restriction.AgeRating));
|
|
}
|
|
|
|
return queryable.Where(c => c.SeriesMetadatas.All(sm =>
|
|
sm.AgeRating <= restriction.AgeRating && sm.AgeRating > AgeRating.Unknown));
|
|
}
|
|
|
|
public static IQueryable<Tag> RestrictAgainstAgeRestriction(this IQueryable<Tag> queryable, AgeRestriction restriction)
|
|
{
|
|
if (restriction.AgeRating == AgeRating.NotApplicable) return queryable;
|
|
|
|
if (restriction.IncludeUnknowns)
|
|
{
|
|
return queryable.Where(c => c.SeriesMetadatas.All(sm =>
|
|
sm.AgeRating <= restriction.AgeRating));
|
|
}
|
|
|
|
return queryable.Where(c => c.SeriesMetadatas.All(sm =>
|
|
sm.AgeRating <= restriction.AgeRating && sm.AgeRating > AgeRating.Unknown));
|
|
}
|
|
|
|
public static IQueryable<Person> RestrictAgainstAgeRestriction(this IQueryable<Person> queryable, AgeRestriction restriction)
|
|
{
|
|
if (restriction.AgeRating == AgeRating.NotApplicable) return queryable;
|
|
|
|
if (restriction.IncludeUnknowns)
|
|
{
|
|
return queryable.Where(c => c.SeriesMetadatas.All(sm =>
|
|
sm.AgeRating <= restriction.AgeRating));
|
|
}
|
|
|
|
return queryable.Where(c => c.SeriesMetadatas.All(sm =>
|
|
sm.AgeRating <= restriction.AgeRating && sm.AgeRating > AgeRating.Unknown));
|
|
}
|
|
|
|
public static IQueryable<ReadingList> RestrictAgainstAgeRestriction(this IQueryable<ReadingList> queryable, AgeRestriction restriction)
|
|
{
|
|
if (restriction.AgeRating == AgeRating.NotApplicable) return queryable;
|
|
var q = queryable.Where(rl => rl.AgeRating <= restriction.AgeRating);
|
|
|
|
if (!restriction.IncludeUnknowns)
|
|
{
|
|
return q.Where(rl => rl.AgeRating != AgeRating.Unknown);
|
|
}
|
|
|
|
return q;
|
|
}
|
|
|
|
public static Task<AgeRestriction> GetUserAgeRestriction(this DbSet<AppUser> queryable, int userId)
|
|
{
|
|
if (userId < 1)
|
|
{
|
|
return Task.FromResult(new AgeRestriction()
|
|
{
|
|
AgeRating = AgeRating.NotApplicable,
|
|
IncludeUnknowns = true
|
|
});
|
|
}
|
|
return queryable
|
|
.AsNoTracking()
|
|
.Where(u => u.Id == userId)
|
|
.Select(u =>
|
|
new AgeRestriction(){
|
|
AgeRating = u.AgeRestriction,
|
|
IncludeUnknowns = u.AgeRestrictionIncludeUnknowns
|
|
})
|
|
.SingleAsync();
|
|
}
|
|
|
|
public static IQueryable<CollectionTag> Includes(this IQueryable<CollectionTag> queryable,
|
|
CollectionTagIncludes includes)
|
|
{
|
|
if (includes.HasFlag(CollectionTagIncludes.SeriesMetadata))
|
|
{
|
|
queryable = queryable.Include(c => c.SeriesMetadatas);
|
|
}
|
|
|
|
return queryable.AsSplitQuery();
|
|
}
|
|
|
|
public static IQueryable<Chapter> Includes(this IQueryable<Chapter> queryable,
|
|
ChapterIncludes includes)
|
|
{
|
|
if (includes.HasFlag(ChapterIncludes.Volumes))
|
|
{
|
|
queryable = queryable.Include(v => v.Volume);
|
|
}
|
|
|
|
if (includes.HasFlag(ChapterIncludes.Files))
|
|
{
|
|
queryable = queryable
|
|
.Include(c => c.Files);
|
|
}
|
|
|
|
|
|
return queryable.AsSplitQuery();
|
|
}
|
|
|
|
public static IQueryable<Series> Includes(this IQueryable<Series> query,
|
|
SeriesIncludes includeFlags)
|
|
{
|
|
if (includeFlags.HasFlag(SeriesIncludes.Library))
|
|
{
|
|
query = query.Include(u => u.Library);
|
|
}
|
|
|
|
if (includeFlags.HasFlag(SeriesIncludes.Volumes))
|
|
{
|
|
query = query.Include(s => s.Volumes);
|
|
}
|
|
|
|
if (includeFlags.HasFlag(SeriesIncludes.Chapters))
|
|
{
|
|
query = query
|
|
.Include(s => s.Volumes)
|
|
.ThenInclude(v => v.Chapters);
|
|
}
|
|
|
|
if (includeFlags.HasFlag(SeriesIncludes.Related))
|
|
{
|
|
query = query.Include(s => s.Relations)
|
|
.ThenInclude(r => r.TargetSeries)
|
|
.Include(s => s.RelationOf);
|
|
}
|
|
|
|
if (includeFlags.HasFlag(SeriesIncludes.Metadata))
|
|
{
|
|
query = query.Include(s => s.Metadata)
|
|
.ThenInclude(m => m.CollectionTags.OrderBy(g => g.NormalizedTitle))
|
|
.Include(s => s.Metadata)
|
|
.ThenInclude(m => m.Genres.OrderBy(g => g.NormalizedTitle))
|
|
.Include(s => s.Metadata)
|
|
.ThenInclude(m => m.People)
|
|
.Include(s => s.Metadata)
|
|
.ThenInclude(m => m.Tags.OrderBy(g => g.NormalizedTitle));
|
|
}
|
|
|
|
|
|
return query.AsSplitQuery();
|
|
}
|
|
|
|
public static IQueryable<AppUser> Includes(this IQueryable<AppUser> query, AppUserIncludes includeFlags)
|
|
{
|
|
if (includeFlags.HasFlag(AppUserIncludes.Bookmarks))
|
|
{
|
|
query = query.Include(u => u.Bookmarks);
|
|
}
|
|
|
|
if (includeFlags.HasFlag(AppUserIncludes.Progress))
|
|
{
|
|
query = query.Include(u => u.Progresses);
|
|
}
|
|
|
|
if (includeFlags.HasFlag(AppUserIncludes.ReadingLists))
|
|
{
|
|
query = query.Include(u => u.ReadingLists);
|
|
}
|
|
|
|
if (includeFlags.HasFlag(AppUserIncludes.ReadingListsWithItems))
|
|
{
|
|
query = query.Include(u => u.ReadingLists)
|
|
.ThenInclude(r => r.Items);
|
|
}
|
|
|
|
if (includeFlags.HasFlag(AppUserIncludes.Ratings))
|
|
{
|
|
query = query.Include(u => u.Ratings);
|
|
}
|
|
|
|
if (includeFlags.HasFlag(AppUserIncludes.UserPreferences))
|
|
{
|
|
query = query.Include(u => u.UserPreferences);
|
|
}
|
|
|
|
if (includeFlags.HasFlag(AppUserIncludes.WantToRead))
|
|
{
|
|
query = query.Include(u => u.WantToRead);
|
|
}
|
|
|
|
if (includeFlags.HasFlag(AppUserIncludes.Devices))
|
|
{
|
|
query = query.Include(u => u.Devices);
|
|
}
|
|
|
|
return query.AsSplitQuery();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Applies restriction based on if the Library has restrictions (like include in search)
|
|
/// </summary>
|
|
/// <param name="query"></param>
|
|
/// <param name="context"></param>
|
|
/// <returns></returns>
|
|
public static IQueryable<Library> IsRestricted(this IQueryable<Library> query, QueryContext context)
|
|
{
|
|
if (context.HasFlag(QueryContext.None)) return query;
|
|
|
|
if (context.HasFlag(QueryContext.Dashboard))
|
|
{
|
|
query = query.Where(l => l.IncludeInDashboard);
|
|
}
|
|
|
|
if (context.HasFlag(QueryContext.Recommended))
|
|
{
|
|
query = query.Where(l => l.IncludeInRecommended);
|
|
}
|
|
|
|
if (context.HasFlag(QueryContext.Search))
|
|
{
|
|
query = query.Where(l => l.IncludeInSearch);
|
|
}
|
|
|
|
return query;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns all libraries for a given user
|
|
/// </summary>
|
|
/// <param name="library"></param>
|
|
/// <param name="userId"></param>
|
|
/// <param name="queryContext"></param>
|
|
/// <returns></returns>
|
|
public static IQueryable<int> GetUserLibraries(this IQueryable<Library> library, int userId, QueryContext queryContext = QueryContext.None)
|
|
{
|
|
return library
|
|
.Include(l => l.AppUsers)
|
|
.Where(lib => lib.AppUsers.Any(user => user.Id == userId))
|
|
.IsRestricted(queryContext)
|
|
.AsNoTracking()
|
|
.AsSplitQuery()
|
|
.Select(lib => lib.Id);
|
|
}
|
|
|
|
public static IEnumerable<DateTime> Range(this DateTime startDate, int numberOfDays) =>
|
|
Enumerable.Range(0, numberOfDays).Select(e => startDate.AddDays(e));
|
|
|
|
public static IQueryable<T> WhereIf<T>(this IQueryable<T> queryable, bool condition,
|
|
Expression<Func<T, bool>> predicate)
|
|
{
|
|
return condition ? queryable.Where(predicate) : queryable;
|
|
}
|
|
}
|