diff --git a/back/src/Kyoo.Abstractions/Controllers/IRepository.cs b/back/src/Kyoo.Abstractions/Controllers/IRepository.cs index 56b9b26b..df64bbd7 100644 --- a/back/src/Kyoo.Abstractions/Controllers/IRepository.cs +++ b/back/src/Kyoo.Abstractions/Controllers/IRepository.cs @@ -249,7 +249,7 @@ namespace Kyoo.Abstractions.Controllers /// The id of the show /// The season's number /// The season found - Task GetOrDefault(int showID, int seasonNumber); + Task GetOrDefault(int showID, int seasonNumber); /// /// Get a season from it's show slug and it's seasonNumber or null if it is not found. @@ -257,7 +257,7 @@ namespace Kyoo.Abstractions.Controllers /// The slug of the show /// The season's number /// The season found - Task GetOrDefault(string showSlug, int seasonNumber); + Task GetOrDefault(string showSlug, int seasonNumber); } /// @@ -294,7 +294,7 @@ namespace Kyoo.Abstractions.Controllers /// The season's number /// The episode's number /// The episode found - Task GetOrDefault(int showID, int seasonNumber, int episodeNumber); + Task GetOrDefault(int showID, int seasonNumber, int episodeNumber); /// /// Get a episode from it's show slug, it's seasonNumber and it's episode number or null if it is not found. @@ -303,7 +303,7 @@ namespace Kyoo.Abstractions.Controllers /// The season's number /// The episode's number /// The episode found - Task GetOrDefault(string showSlug, int seasonNumber, int episodeNumber); + Task GetOrDefault(string showSlug, int seasonNumber, int episodeNumber); /// /// Get a episode from it's showID and it's absolute number. diff --git a/back/src/Kyoo.Abstractions/Models/Utils/Sort.cs b/back/src/Kyoo.Abstractions/Models/Utils/Sort.cs index e34efcd1..f08720b4 100644 --- a/back/src/Kyoo.Abstractions/Models/Utils/Sort.cs +++ b/back/src/Kyoo.Abstractions/Models/Utils/Sort.cs @@ -46,7 +46,7 @@ namespace Kyoo.Abstractions.Controllers /// /// If this is set to true, items will be sorted in descend order else, they will be sorted in ascendant order. /// - public By(Expression> key, bool desendant = false) + public By(Expression> key, bool desendant = false) : this(Utility.GetPropertyName(key), desendant) { } } diff --git a/back/src/Kyoo.Core/Controllers/IdentifierRouteConstraint.cs b/back/src/Kyoo.Core/Controllers/IdentifierRouteConstraint.cs index cda79146..c8a96c75 100644 --- a/back/src/Kyoo.Core/Controllers/IdentifierRouteConstraint.cs +++ b/back/src/Kyoo.Core/Controllers/IdentifierRouteConstraint.cs @@ -28,8 +28,8 @@ namespace Kyoo.Core.Controllers public class IdentifierRouteConstraint : IRouteConstraint { /// - public bool Match(HttpContext httpContext, - IRouter route, + public bool Match(HttpContext? httpContext, + IRouter? route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) diff --git a/back/src/Kyoo.Core/Controllers/LibraryManager.cs b/back/src/Kyoo.Core/Controllers/LibraryManager.cs index 3d3f77bf..664f56bb 100644 --- a/back/src/Kyoo.Core/Controllers/LibraryManager.cs +++ b/back/src/Kyoo.Core/Controllers/LibraryManager.cs @@ -74,15 +74,15 @@ namespace Kyoo.Core.Controllers public LibraryManager(IEnumerable repositories) { _repositories = repositories.ToArray(); - LibraryItemRepository = GetRepository() as ILibraryItemRepository; - CollectionRepository = GetRepository() as ICollectionRepository; - MovieRepository = GetRepository() as IMovieRepository; - ShowRepository = GetRepository() as IShowRepository; - SeasonRepository = GetRepository() as ISeasonRepository; - EpisodeRepository = GetRepository() as IEpisodeRepository; - PeopleRepository = GetRepository() as IPeopleRepository; - StudioRepository = GetRepository() as IStudioRepository; - UserRepository = GetRepository() as IUserRepository; + LibraryItemRepository = (ILibraryItemRepository)GetRepository(); + CollectionRepository = (ICollectionRepository)GetRepository(); + MovieRepository = (IMovieRepository)GetRepository(); + ShowRepository = (IShowRepository)GetRepository(); + SeasonRepository = (ISeasonRepository)GetRepository(); + EpisodeRepository = (IEpisodeRepository)GetRepository(); + PeopleRepository = (IPeopleRepository)GetRepository(); + StudioRepository = (IStudioRepository)GetRepository(); + UserRepository = (IUserRepository)GetRepository(); } /// @@ -140,46 +140,46 @@ namespace Kyoo.Core.Controllers } /// - public async Task GetOrDefault(int id) + public async Task GetOrDefault(int id) where T : class, IResource { return await GetRepository().GetOrDefault(id); } /// - public async Task GetOrDefault(string slug) + public async Task GetOrDefault(string slug) where T : class, IResource { return await GetRepository().GetOrDefault(slug); } /// - public async Task GetOrDefault(Expression> where, Sort sortBy) + public async Task GetOrDefault(Expression> where, Sort? sortBy) where T : class, IResource { return await GetRepository().GetOrDefault(where, sortBy); } /// - public async Task GetOrDefault(int showID, int seasonNumber) + public async Task GetOrDefault(int showID, int seasonNumber) { return await SeasonRepository.GetOrDefault(showID, seasonNumber); } /// - public async Task GetOrDefault(string showSlug, int seasonNumber) + public async Task GetOrDefault(string showSlug, int seasonNumber) { return await SeasonRepository.GetOrDefault(showSlug, seasonNumber); } /// - public async Task GetOrDefault(int showID, int seasonNumber, int episodeNumber) + public async Task GetOrDefault(int showID, int seasonNumber, int episodeNumber) { return await EpisodeRepository.GetOrDefault(showID, seasonNumber, episodeNumber); } /// - public async Task GetOrDefault(string showSlug, int seasonNumber, int episodeNumber) + public async Task GetOrDefault(string showSlug, int seasonNumber, int episodeNumber) { return await EpisodeRepository.GetOrDefault(showSlug, seasonNumber, episodeNumber); } @@ -238,7 +238,7 @@ namespace Kyoo.Core.Controllers if (obj == null) throw new ArgumentNullException(nameof(obj)); - object existingValue = obj.GetType() + object? existingValue = obj.GetType() .GetProperties() .FirstOrDefault(x => string.Equals(x.Name, memberName, StringComparison.InvariantCultureIgnoreCase)) ?.GetValue(obj); @@ -248,11 +248,11 @@ namespace Kyoo.Core.Controllers return (obj, member: memberName) switch { (Collection c, nameof(Collection.Shows)) => ShowRepository - .GetAll(x => x.Collections.Any(y => y.Id == obj.Id)) + .GetAll(x => x.Collections!.Any(y => y.Id == obj.Id)) .Then(x => c.Shows = x), (Collection c, nameof(Collection.Movies)) => MovieRepository - .GetAll(x => x.Collections.Any(y => y.Id == obj.Id)) + .GetAll(x => x.Collections!.Any(y => y.Id == obj.Id)) .Then(x => c.Movies = x), @@ -261,11 +261,11 @@ namespace Kyoo.Core.Controllers .Then(x => m.People = x), (Movie m, nameof(Movie.Collections)) => CollectionRepository - .GetAll(x => x.Movies.Any(y => y.Id == obj.Id)) + .GetAll(x => x.Movies!.Any(y => y.Id == obj.Id)) .Then(x => m.Collections = x), (Movie m, nameof(Movie.Studio)) => StudioRepository - .GetOrDefault(x => x.Movies.Any(y => y.Id == obj.Id)) + .GetOrDefault(x => x.Movies!.Any(y => y.Id == obj.Id)) .Then(x => { m.Studio = x; @@ -278,21 +278,21 @@ namespace Kyoo.Core.Controllers .Then(x => s.People = x), (Show s, nameof(Show.Seasons)) => _SetRelation(s, - SeasonRepository.GetAll(x => x.Show.Id == obj.Id), + SeasonRepository.GetAll(x => x.Show!.Id == obj.Id), (x, y) => x.Seasons = y, (x, y) => { x.Show = y; x.ShowId = y.Id; }), (Show s, nameof(Show.Episodes)) => _SetRelation(s, - EpisodeRepository.GetAll(x => x.Show.Id == obj.Id), + EpisodeRepository.GetAll(x => x.Show!.Id == obj.Id), (x, y) => x.Episodes = y, (x, y) => { x.Show = y; x.ShowId = y.Id; }), (Show s, nameof(Show.Collections)) => CollectionRepository - .GetAll(x => x.Shows.Any(y => y.Id == obj.Id)) + .GetAll(x => x.Shows!.Any(y => y.Id == obj.Id)) .Then(x => s.Collections = x), (Show s, nameof(Show.Studio)) => StudioRepository - .GetOrDefault(x => x.Shows.Any(y => y.Id == obj.Id)) + .GetOrDefault(x => x.Shows!.Any(y => y.Id == obj.Id)) .Then(x => { s.Studio = x; @@ -301,12 +301,12 @@ namespace Kyoo.Core.Controllers (Season s, nameof(Season.Episodes)) => _SetRelation(s, - EpisodeRepository.GetAll(x => x.Season.Id == obj.Id), + EpisodeRepository.GetAll(x => x.Season!.Id == obj.Id), (x, y) => x.Episodes = y, (x, y) => { x.Season = y; x.SeasonId = y.Id; }), (Season s, nameof(Season.Show)) => ShowRepository - .GetOrDefault(x => x.Seasons.Any(y => y.Id == obj.Id)) + .GetOrDefault(x => x.Seasons!.Any(y => y.Id == obj.Id)) .Then(x => { s.Show = x; @@ -315,7 +315,7 @@ namespace Kyoo.Core.Controllers (Episode e, nameof(Episode.Show)) => ShowRepository - .GetOrDefault(x => x.Episodes.Any(y => y.Id == obj.Id)) + .GetOrDefault(x => x.Episodes!.Any(y => y.Id == obj.Id)) .Then(x => { e.Show = x; @@ -323,7 +323,7 @@ namespace Kyoo.Core.Controllers }), (Episode e, nameof(Episode.Season)) => SeasonRepository - .GetOrDefault(x => x.Episodes.Any(y => y.Id == e.Id)) + .GetOrDefault(x => x.Episodes!.Any(y => y.Id == e.Id)) .Then(x => { e.Season = x; @@ -344,11 +344,11 @@ namespace Kyoo.Core.Controllers (Studio s, nameof(Studio.Shows)) => ShowRepository - .GetAll(x => x.Studio.Id == obj.Id) + .GetAll(x => x.Studio!.Id == obj.Id) .Then(x => s.Shows = x), (Studio s, nameof(Studio.Movies)) => MovieRepository - .GetAll(x => x.Studio.Id == obj.Id) + .GetAll(x => x.Studio!.Id == obj.Id) .Then(x => s.Movies = x), @@ -362,51 +362,51 @@ namespace Kyoo.Core.Controllers /// public Task> GetPeopleFromShow(int showID, - Expression> where = null, - Sort sort = default, - Pagination limit = default) + Expression>? where = null, + Sort? sort = default, + Pagination? limit = default) { return PeopleRepository.GetFromShow(showID, where, sort, limit); } /// public Task> GetPeopleFromShow(string showSlug, - Expression> where = null, - Sort sort = default, - Pagination limit = default) + Expression>? where = null, + Sort? sort = default, + Pagination? limit = default) { return PeopleRepository.GetFromShow(showSlug, where, sort, limit); } /// public Task> GetRolesFromPeople(int id, - Expression> where = null, - Sort sort = default, - Pagination limit = default) + Expression>? where = null, + Sort? sort = default, + Pagination? limit = default) { return PeopleRepository.GetFromPeople(id, where, sort, limit); } /// public Task> GetRolesFromPeople(string slug, - Expression> where = null, - Sort sort = default, - Pagination limit = default) + Expression>? where = null, + Sort? sort = default, + Pagination? limit = default) { return PeopleRepository.GetFromPeople(slug, where, sort, limit); } /// - public Task> GetAll(Expression> where = null, - Sort sort = default, - Pagination limit = default) + public Task> GetAll(Expression>? where = null, + Sort? sort = default, + Pagination? limit = default) where T : class, IResource { return GetRepository().GetAll(where, sort, limit); } /// - public Task GetCount(Expression> where = null) + public Task GetCount(Expression>? where = null) where T : class, IResource { return GetRepository().GetCount(where); diff --git a/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs index 933c2bb7..153c855d 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs @@ -77,7 +77,7 @@ namespace Kyoo.Core.Controllers } /// - public Task GetOrDefault(int showID, int seasonNumber, int episodeNumber) + public Task GetOrDefault(int showID, int seasonNumber, int episodeNumber) { return _database.Episodes.FirstOrDefaultAsync(x => x.ShowId == showID && x.SeasonNumber == seasonNumber @@ -85,9 +85,9 @@ namespace Kyoo.Core.Controllers } /// - public Task GetOrDefault(string showSlug, int seasonNumber, int episodeNumber) + public Task GetOrDefault(string showSlug, int seasonNumber, int episodeNumber) { - return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug + return _database.Episodes.FirstOrDefaultAsync(x => x.Show!.Slug == showSlug && x.SeasonNumber == seasonNumber && x.EpisodeNumber == episodeNumber).Then(SetBackingImage); } @@ -95,7 +95,7 @@ namespace Kyoo.Core.Controllers /// public async Task Get(int showID, int seasonNumber, int episodeNumber) { - Episode ret = await GetOrDefault(showID, seasonNumber, episodeNumber); + Episode? ret = await GetOrDefault(showID, seasonNumber, episodeNumber); if (ret == null) throw new ItemNotFoundException($"No episode S{seasonNumber}E{episodeNumber} found on the show {showID}."); return ret; @@ -104,24 +104,30 @@ namespace Kyoo.Core.Controllers /// public async Task Get(string showSlug, int seasonNumber, int episodeNumber) { - Episode ret = await GetOrDefault(showSlug, seasonNumber, episodeNumber); + Episode? ret = await GetOrDefault(showSlug, seasonNumber, episodeNumber); if (ret == null) throw new ItemNotFoundException($"No episode S{seasonNumber}E{episodeNumber} found on the show {showSlug}."); return ret; } /// - public Task GetAbsolute(int showID, int absoluteNumber) + public async Task GetAbsolute(int showID, int absoluteNumber) { - return _database.Episodes.FirstOrDefaultAsync(x => x.ShowId == showID + Episode? ret = await _database.Episodes.FirstOrDefaultAsync(x => x.ShowId == showID && x.AbsoluteNumber == absoluteNumber).Then(SetBackingImage); + if (ret == null) + throw new ItemNotFoundException(); + return ret; } /// - public Task GetAbsolute(string showSlug, int absoluteNumber) + public async Task GetAbsolute(string showSlug, int absoluteNumber) { - return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug + Episode? ret = await _database.Episodes.FirstOrDefaultAsync(x => x.Show!.Slug == showSlug && x.AbsoluteNumber == absoluteNumber).Then(SetBackingImage); + if (ret == null) + throw new ItemNotFoundException(); + return ret; } /// @@ -131,13 +137,13 @@ namespace Kyoo.Core.Controllers _database.Episodes .Include(x => x.Show) .Where(x => x.EpisodeNumber != null || x.AbsoluteNumber != null) - .Where(_database.Like(x => x.Name, $"%{query}%")) + .Where(_database.Like(x => x.Name!, $"%{query}%")) ) .Take(20) .ToListAsync(); foreach (Episode ep in ret) { - ep.Show.Episodes = null; + ep.Show!.Episodes = null; SetBackingImage(ep); } return ret; @@ -152,7 +158,7 @@ namespace Kyoo.Core.Controllers await _database.SaveChangesAsync(() => obj.SeasonNumber != null && obj.EpisodeNumber != null ? Get(obj.ShowId, obj.SeasonNumber.Value, obj.EpisodeNumber.Value) - : GetAbsolute(obj.ShowId, obj.AbsoluteNumber.Value)); + : GetAbsolute(obj.ShowId, obj.AbsoluteNumber!.Value)); OnResourceCreated(obj); return obj; } diff --git a/back/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs index 5c9ed0a3..a8b50d66 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs @@ -85,7 +85,7 @@ namespace Kyoo.Core.Controllers /// The query to sort. /// How to sort the query /// The newly sorted query. - protected IOrderedQueryable Sort(IQueryable query, Sort sortBy = null) + protected IOrderedQueryable Sort(IQueryable query, Sort? sortBy = null) { sortBy ??= DefaultSort; @@ -138,7 +138,7 @@ namespace Kyoo.Core.Controllers : (greaterThan ? Expression.GreaterThan : Expression.LessThan); } - private record SortIndicator(string key, bool desc, string? seed); + private record SortIndicator(string Key, bool Desc, string? Seed); /// /// Create a filter (where) expression on the query to skip everything before/after the referenceID. @@ -158,7 +158,7 @@ namespace Kyoo.Core.Controllers /// True if the following page should be returned, false for the previous. /// An expression ready to be added to a Where close of a sorted query to handle the AfterID protected Expression> KeysetPaginate( - Sort sort, + Sort? sort, T reference, bool next = true) { @@ -170,7 +170,7 @@ namespace Kyoo.Core.Controllers void GetRandomSortKeys(string seed, out Expression left, out Expression right) { - MethodInfo concat = typeof(string).GetMethod(nameof(string.Concat), new[] { typeof(string), typeof(string) }); + MethodInfo concat = typeof(string).GetMethod(nameof(string.Concat), new[] { typeof(string), typeof(string) })!; Expression id = Expression.Call(Expression.Property(x, "ID"), nameof(int.ToString), null); Expression xrng = Expression.Call(concat, Expression.Constant(seed), id); right = Expression.Call(typeof(DatabaseContext), nameof(DatabaseContext.MD5), null, Expression.Constant($"{seed}{reference.Id}")); @@ -193,14 +193,14 @@ namespace Kyoo.Core.Controllers IEnumerable sorts = GetSortsBy(sort) .Append(new SortIndicator("Id", false, null)); - BinaryExpression filter = null; + BinaryExpression? filter = null; List previousSteps = new(); // TODO: Add an outer query >= for perf // PERF: See https://use-the-index-luke.com/sql/partial-results/fetch-next-page#sb-equivalent-logic foreach ((string key, bool desc, string? seed) in sorts) { - BinaryExpression compare = null; - PropertyInfo property = key != "random" + BinaryExpression? compare = null; + PropertyInfo? property = key != "random" ? typeof(T).GetProperty(key) : null; @@ -268,7 +268,7 @@ namespace Kyoo.Core.Controllers return Expression.Lambda>(filter!, x); } - protected void SetBackingImage(T obj) + protected void SetBackingImage(T? obj) { if (obj is not IThumbnails thumbs) return; @@ -298,7 +298,7 @@ namespace Kyoo.Core.Controllers /// The tracked resource with the given ID protected virtual async Task GetWithTracking(int id) { - T ret = await Database.Set().AsTracking().FirstOrDefaultAsync(x => x.Id == id); + T? ret = await Database.Set().AsTracking().FirstOrDefaultAsync(x => x.Id == id); SetBackingImage(ret); if (ret == null) throw new ItemNotFoundException($"No {typeof(T).Name} found with the id {id}"); @@ -308,7 +308,7 @@ namespace Kyoo.Core.Controllers /// public virtual async Task Get(int id) { - T ret = await GetOrDefault(id); + T? ret = await GetOrDefault(id); if (ret == null) throw new ItemNotFoundException($"No {typeof(T).Name} found with the id {id}"); return ret; @@ -317,7 +317,7 @@ namespace Kyoo.Core.Controllers /// public virtual async Task Get(string slug) { - T ret = await GetOrDefault(slug); + T? ret = await GetOrDefault(slug); if (ret == null) throw new ItemNotFoundException($"No {typeof(T).Name} found with the slug {slug}"); return ret; @@ -326,20 +326,20 @@ namespace Kyoo.Core.Controllers /// public virtual async Task Get(Expression> where) { - T ret = await GetOrDefault(where); + T? ret = await GetOrDefault(where); if (ret == null) throw new ItemNotFoundException($"No {typeof(T).Name} found with the given predicate."); return ret; } /// - public virtual Task GetOrDefault(int id) + public virtual Task GetOrDefault(int id) { return Database.Set().FirstOrDefaultAsync(x => x.Id == id).Then(SetBackingImage); } /// - public virtual Task GetOrDefault(string slug) + public virtual Task GetOrDefault(string slug) { if (slug == "random") return Database.Set().OrderBy(x => EF.Functions.Random()).FirstOrDefaultAsync().Then(SetBackingImage); @@ -347,7 +347,7 @@ namespace Kyoo.Core.Controllers } /// - public virtual Task GetOrDefault(Expression> where, Sort sortBy = default) + public virtual Task GetOrDefault(Expression> where, Sort? sortBy = default) { return Sort(Database.Set(), sortBy).FirstOrDefaultAsync(where).Then(SetBackingImage); } @@ -356,9 +356,9 @@ namespace Kyoo.Core.Controllers public abstract Task> Search(string query); /// - public virtual async Task> GetAll(Expression> where = null, - Sort sort = default, - Pagination limit = default) + public virtual async Task> GetAll(Expression>? where = null, + Sort? sort = default, + Pagination? limit = default) { return (await ApplyFilters(Database.Set(), where, sort, limit)) .Select(SetBackingImageSelf).ToList(); @@ -373,9 +373,9 @@ namespace Kyoo.Core.Controllers /// Pagination information (where to start and how many to get) /// The filtered query protected async Task> ApplyFilters(IQueryable query, - Expression> where = null, - Sort sort = default, - Pagination limit = default) + Expression>? where = null, + Sort? sort = default, + Pagination? limit = default) { query = Sort(query, sort); if (where != null) @@ -395,7 +395,7 @@ namespace Kyoo.Core.Controllers } /// - public virtual Task GetCount(Expression> where = null) + public virtual Task GetCount(Expression>? where = null) { IQueryable query = Database.Set(); if (where != null) @@ -411,11 +411,11 @@ namespace Kyoo.Core.Controllers { await _thumbs.DownloadImages(thumbs); if (thumbs.Poster != null) - Database.Entry(thumbs).Reference(x => x.Poster).TargetEntry.State = EntityState.Added; + Database.Entry(thumbs).Reference(x => x.Poster).TargetEntry!.State = EntityState.Added; if (thumbs.Thumbnail != null) - Database.Entry(thumbs).Reference(x => x.Thumbnail).TargetEntry.State = EntityState.Added; + Database.Entry(thumbs).Reference(x => x.Thumbnail).TargetEntry!.State = EntityState.Added; if (thumbs.Logo != null) - Database.Entry(thumbs).Reference(x => x.Logo).TargetEntry.State = EntityState.Added; + Database.Entry(thumbs).Reference(x => x.Logo).TargetEntry!.State = EntityState.Added; } SetBackingImage(obj); return obj; @@ -444,7 +444,7 @@ namespace Kyoo.Core.Controllers { try { - T old = await GetOrDefault(obj.Slug); + T? old = await GetOrDefault(obj.Slug); if (old != null) return old; @@ -544,7 +544,7 @@ namespace Kyoo.Core.Controllers { try { - MethodInfo setter = typeof(T).GetProperty(nameof(resource.Slug))!.GetSetMethod(); + MethodInfo? setter = typeof(T).GetProperty(nameof(resource.Slug))!.GetSetMethod(); if (setter != null) setter.Invoke(resource, new object[] { resource.Slug + '!' }); else diff --git a/back/src/Kyoo.Core/Controllers/Repositories/MovieRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/MovieRepository.cs index 02d0ba84..328c3cd8 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/MovieRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/MovieRepository.cs @@ -126,7 +126,7 @@ namespace Kyoo.Core.Controllers if (changed.People != null) { - await Database.Entry(resource).Collection(x => x.People).LoadAsync(); + await Database.Entry(resource).Collection(x => x.People!).LoadAsync(); resource.People = changed.People; } } diff --git a/back/src/Kyoo.Core/Controllers/Repositories/PeopleRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/PeopleRepository.cs index 7c53b609..deee3aaf 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/PeopleRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/PeopleRepository.cs @@ -94,7 +94,7 @@ namespace Kyoo.Core.Controllers { foreach (PeopleRole role in resource.Roles) { - role.Show = _database.LocalEntity(role.Show.Slug) + role.Show = _database.LocalEntity(role.Show!.Slug) ?? await _shows.Value.CreateIfNotExists(role.Show); role.ShowID = role.Show.Id; _database.Entry(role).State = EntityState.Added; @@ -109,7 +109,7 @@ namespace Kyoo.Core.Controllers if (changed.Roles != null) { - await Database.Entry(resource).Collection(x => x.Roles).LoadAsync(); + await Database.Entry(resource).Collection(x => x.Roles!).LoadAsync(); resource.Roles = changed.Roles; } } @@ -125,9 +125,9 @@ namespace Kyoo.Core.Controllers /// public Task> GetFromShow(int showID, - Expression> where = null, - Sort sort = default, - Pagination limit = default) + Expression>? where = null, + Sort? sort = default, + Pagination? limit = default) { return Task.FromResult>(new List()); // ICollection people = await ApplyFilters(_database.PeopleRoles @@ -146,9 +146,9 @@ namespace Kyoo.Core.Controllers /// public Task> GetFromShow(string showSlug, - Expression> where = null, - Sort sort = default, - Pagination limit = default) + Expression>? where = null, + Sort? sort = default, + Pagination? limit = default) { return Task.FromResult>(new List()); // ICollection people = await ApplyFilters(_database.PeopleRoles @@ -169,9 +169,9 @@ namespace Kyoo.Core.Controllers /// public Task> GetFromPeople(int id, - Expression> where = null, - Sort sort = default, - Pagination limit = default) + Expression>? where = null, + Sort? sort = default, + Pagination? limit = default) { return Task.FromResult>(new List()); // ICollection roles = await ApplyFilters(_database.PeopleRoles @@ -189,9 +189,9 @@ namespace Kyoo.Core.Controllers /// public Task> GetFromPeople(string slug, - Expression> where = null, - Sort sort = default, - Pagination limit = default) + Expression>? where = null, + Sort? sort = default, + Pagination? limit = default) { return Task.FromResult>(new List()); // ICollection roles = await ApplyFilters(_database.PeopleRoles diff --git a/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs index 801b4876..417539cc 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs @@ -71,7 +71,7 @@ namespace Kyoo.Core.Controllers /// public async Task Get(int showID, int seasonNumber) { - Season ret = await GetOrDefault(showID, seasonNumber); + Season? ret = await GetOrDefault(showID, seasonNumber); if (ret == null) throw new ItemNotFoundException($"No season {seasonNumber} found for the show {showID}"); return ret; @@ -80,23 +80,23 @@ namespace Kyoo.Core.Controllers /// public async Task Get(string showSlug, int seasonNumber) { - Season ret = await GetOrDefault(showSlug, seasonNumber); + Season? ret = await GetOrDefault(showSlug, seasonNumber); if (ret == null) throw new ItemNotFoundException($"No season {seasonNumber} found for the show {showSlug}"); return ret; } /// - public Task GetOrDefault(int showID, int seasonNumber) + public Task GetOrDefault(int showID, int seasonNumber) { return _database.Seasons.FirstOrDefaultAsync(x => x.ShowId == showID && x.SeasonNumber == seasonNumber).Then(SetBackingImage); } /// - public Task GetOrDefault(string showSlug, int seasonNumber) + public Task GetOrDefault(string showSlug, int seasonNumber) { - return _database.Seasons.FirstOrDefaultAsync(x => x.Show.Slug == showSlug + return _database.Seasons.FirstOrDefaultAsync(x => x.Show!.Slug == showSlug && x.SeasonNumber == seasonNumber).Then(SetBackingImage); } @@ -105,7 +105,7 @@ namespace Kyoo.Core.Controllers { return (await Sort( _database.Seasons - .Where(_database.Like(x => x.Name, $"%{query}%")) + .Where(_database.Like(x => x.Name!, $"%{query}%")) ) .Take(20) .ToListAsync()) diff --git a/back/src/Kyoo.Core/Controllers/Repositories/ShowRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/ShowRepository.cs index 11887286..f52f0ce1 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/ShowRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/ShowRepository.cs @@ -21,6 +21,7 @@ using System.Linq; using System.Threading.Tasks; using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models; +using Kyoo.Abstractions.Models.Exceptions; using Kyoo.Postgresql; using Kyoo.Utils; using Microsoft.EntityFrameworkCore; @@ -129,17 +130,20 @@ namespace Kyoo.Core.Controllers if (changed.People != null) { - await Database.Entry(resource).Collection(x => x.People).LoadAsync(); + await Database.Entry(resource).Collection(x => x.People!).LoadAsync(); resource.People = changed.People; } } /// - public Task GetSlug(int showID) + public async Task GetSlug(int showID) { - return _database.Shows.Where(x => x.Id == showID) + string? ret = await _database.Shows.Where(x => x.Id == showID) .Select(x => x.Slug) .FirstOrDefaultAsync(); + if (ret == null) + throw new ItemNotFoundException(); + return ret; } /// diff --git a/back/src/Kyoo.Core/Controllers/Repositories/UserRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/UserRepository.cs index 6e4ea323..8e40646e 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/UserRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/UserRepository.cs @@ -69,7 +69,7 @@ namespace Kyoo.Core.Controllers await base.Create(obj); _database.Entry(obj).State = EntityState.Added; if (obj.Logo != null) - _database.Entry(obj).Reference(x => x.Logo).TargetEntry.State = EntityState.Added; + _database.Entry(obj).Reference(x => x.Logo).TargetEntry!.State = EntityState.Added; await _database.SaveChangesAsync(() => Get(obj.Slug)); OnResourceCreated(obj); return obj; diff --git a/back/src/Kyoo.Core/CoreModule.cs b/back/src/Kyoo.Core/CoreModule.cs index ff6e9535..cd0b6d1f 100644 --- a/back/src/Kyoo.Core/CoreModule.cs +++ b/back/src/Kyoo.Core/CoreModule.cs @@ -84,7 +84,7 @@ namespace Kyoo.Core options.InvalidModelStateResponseFactory = ctx => { string[] errors = ctx.ModelState - .SelectMany(x => x.Value.Errors) + .SelectMany(x => x.Value!.Errors) .Select(x => x.ErrorMessage) .ToArray(); return new BadRequestObjectResult(new RequestError(errors)); diff --git a/back/src/Kyoo.Core/Helper.cs b/back/src/Kyoo.Core/Helper.cs deleted file mode 100644 index ad45835d..00000000 --- a/back/src/Kyoo.Core/Helper.cs +++ /dev/null @@ -1,47 +0,0 @@ -// 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 . - -using System.Net.Http; -using System.Threading.Tasks; -using Newtonsoft.Json; - -namespace Kyoo.Core -{ - /// - /// A class containing helper methods. - /// - public static class Helper - { - /// - /// An helper method to get json content from an http server. This is a temporary thing and will probably be - /// replaced by a call to the function of the same name in the System.Net.Http.Json namespace when .net6 - /// gets released. - /// - /// The http server to use. - /// The url to retrieve - /// The type of object to convert - /// A T representing the json contained at the given url. - public static async Task GetFromJsonAsync(this HttpClient client, string url) - { - HttpResponseMessage ret = await client.GetAsync(url); - ret.EnsureSuccessStatusCode(); - string content = await ret.Content.ReadAsStringAsync(); - return JsonConvert.DeserializeObject(content); - } - } -} diff --git a/back/src/Kyoo.Core/Kyoo.Core.csproj b/back/src/Kyoo.Core/Kyoo.Core.csproj index b4061a1c..72d5e1e6 100644 --- a/back/src/Kyoo.Core/Kyoo.Core.csproj +++ b/back/src/Kyoo.Core/Kyoo.Core.csproj @@ -2,6 +2,7 @@ Kyoo.Core Kyoo.Core + enable diff --git a/back/src/Kyoo.Core/Views/Helper/ApiHelper.cs b/back/src/Kyoo.Core/Views/Helper/ApiHelper.cs index 432a7a54..0dec46a3 100644 --- a/back/src/Kyoo.Core/Views/Helper/ApiHelper.cs +++ b/back/src/Kyoo.Core/Views/Helper/ApiHelper.cs @@ -48,9 +48,9 @@ namespace Kyoo.Core.Api /// The second parameter to compare. /// A comparison expression compatible with strings public static BinaryExpression StringCompatibleExpression( - [NotNull] Func operand, - [NotNull] Expression left, - [NotNull] Expression right) + Func operand, + Expression left, + Expression right) { if (left.Type != typeof(string)) return operand(left, right); @@ -69,14 +69,14 @@ namespace Kyoo.Core.Api /// The type to create filters for. /// A filter is invalid. /// An expression representing the filters that can be used anywhere or compiled - public static Expression> ParseWhere(Dictionary where, - Expression> defaultWhere = null) + public static Expression>? ParseWhere(Dictionary? where, + Expression>? defaultWhere = null) { if (where == null || where.Count == 0) return defaultWhere; ParameterExpression param = defaultWhere?.Parameters.First() ?? Expression.Parameter(typeof(T)); - Expression expression = defaultWhere?.Body; + Expression? expression = defaultWhere?.Body; foreach ((string key, string desired) in where) { @@ -91,17 +91,17 @@ namespace Kyoo.Core.Api value = desired.Substring(desired.IndexOf(':') + 1); } - PropertyInfo property = typeof(T).GetProperty(key, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase); + PropertyInfo? property = typeof(T).GetProperty(key, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase); if (property == null) throw new ArgumentException($"No filterable parameter with the name {key}."); MemberExpression propertyExpr = Expression.Property(param, property); - ConstantExpression valueExpr = null; + ConstantExpression? valueExpr = null; bool isList = typeof(IEnumerable).IsAssignableFrom(propertyExpr.Type) && propertyExpr.Type != typeof(string); if (operand != "ctn" && !typeof(IResource).IsAssignableFrom(propertyExpr.Type) && !isList) { Type propertyType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType; - object val; + object? val; try { val = string.IsNullOrEmpty(value) || value.Equals("null", StringComparison.OrdinalIgnoreCase) @@ -110,7 +110,7 @@ namespace Kyoo.Core.Api } catch (InvalidCastException) { - throw new ArgumentException("Comparing two differents value's type."); + throw new ArgumentException("Comparing two different value's type."); } valueExpr = Expression.Constant(val, property.PropertyType); @@ -166,7 +166,7 @@ namespace Kyoo.Core.Api if (xProperty.Type == typeof(string)) { // x.PROPRETY.Contains(value); - return Expression.Call(xProperty, typeof(string).GetMethod("Contains", new[] { typeof(string) }), Expression.Constant(value)); + return Expression.Call(xProperty, typeof(string).GetMethod("Contains", new[] { typeof(string) })!, Expression.Constant(value)); } // x.PROPERTY is either a List<> or a [] @@ -176,7 +176,8 @@ namespace Kyoo.Core.Api { return Expression.Call(typeof(Enumerable), "Contains", new[] { inner }, xProperty, Expression.Constant(value)); } - else if (inner.IsEnum && Enum.TryParse(inner, value, true, out object enumValue)) + + if (inner.IsEnum && Enum.TryParse(inner, value, true, out object? enumValue)) { return Expression.Call(typeof(Enumerable), "Contains", new[] { inner }, xProperty, Expression.Constant(enumValue)); } @@ -185,7 +186,7 @@ namespace Kyoo.Core.Api throw new ArgumentException("Contain (ctn) not appliable for this property."); // x => x.PROPERTY.Any(y => y.Slug == value) - Expression ret = null; + Expression? ret = null; ParameterExpression y = Expression.Parameter(inner, "y"); foreach (string val in value.Split(',')) { @@ -197,7 +198,7 @@ namespace Kyoo.Core.Api else ret = Expression.AndAlso(ret, iteration); } - return ret; + return ret!; } } } diff --git a/back/src/Kyoo.Core/Views/Helper/CrudApi.cs b/back/src/Kyoo.Core/Views/Helper/CrudApi.cs index a593ac9a..c89d8650 100644 --- a/back/src/Kyoo.Core/Views/Helper/CrudApi.cs +++ b/back/src/Kyoo.Core/Views/Helper/CrudApi.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Linq.Expressions; using System.Threading.Tasks; using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models; @@ -69,7 +70,7 @@ namespace Kyoo.Core.Api [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> Get(Identifier identifier) { - T ret = await identifier.Match( + T? ret = await identifier.Match( id => Repository.GetOrDefault(id), slug => Repository.GetOrDefault(slug) ); @@ -235,7 +236,11 @@ namespace Kyoo.Core.Api [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] public async Task Delete([FromQuery] Dictionary where) { - await Repository.DeleteAll(ApiHelper.ParseWhere(where)); + Expression>? w = ApiHelper.ParseWhere(where); + if (w == null) + return BadRequest(new RequestError("Incule a filter to delete items, all items won't be deleted.")); + + await Repository.DeleteAll(w); return NoContent(); } } diff --git a/back/src/Kyoo.Core/Views/Helper/CrudThumbsApi.cs b/back/src/Kyoo.Core/Views/Helper/CrudThumbsApi.cs index 697e75cc..a8143ebc 100644 --- a/back/src/Kyoo.Core/Views/Helper/CrudThumbsApi.cs +++ b/back/src/Kyoo.Core/Views/Helper/CrudThumbsApi.cs @@ -59,14 +59,14 @@ namespace Kyoo.Core.Api private async Task _GetImage(Identifier identifier, string image, ImageQuality? quality) { - T resource = await identifier.Match( + T? resource = await identifier.Match( id => Repository.GetOrDefault(id), slug => Repository.GetOrDefault(slug) ); if (resource == null) return NotFound(); string path = _thumbs.GetImagePath(resource, image, quality ?? ImageQuality.High); - if (path == null || !System.IO.File.Exists(path)) + if (!System.IO.File.Exists(path)) return NotFound(); if (!identifier.Match(id => false, slug => slug == "random")) diff --git a/back/src/Kyoo.Core/Views/Helper/ResourceViewAttribute.cs b/back/src/Kyoo.Core/Views/Helper/ResourceViewAttribute.cs index b28dc802..c95e51af 100644 --- a/back/src/Kyoo.Core/Views/Helper/ResourceViewAttribute.cs +++ b/back/src/Kyoo.Core/Views/Helper/ResourceViewAttribute.cs @@ -42,7 +42,7 @@ namespace Kyoo.Core.Api /// public override void OnActionExecuting(ActionExecutingContext context) { - if (context.ActionArguments.TryGetValue("where", out object dic) && dic is Dictionary where) + if (context.ActionArguments.TryGetValue("where", out object? dic) && dic is Dictionary where) { Dictionary nWhere = new(where, StringComparer.InvariantCultureIgnoreCase); nWhere.Remove("fields"); @@ -55,7 +55,7 @@ namespace Kyoo.Core.Api } List fields = context.HttpContext.Request.Query["fields"] - .SelectMany(x => x.Split(',')) + .SelectMany(x => x!.Split(',')) .ToList(); if (context.ActionDescriptor is ControllerActionDescriptor descriptor) @@ -77,7 +77,7 @@ namespace Kyoo.Core.Api fields = fields .Select(x => { - string property = properties + string? property = properties .FirstOrDefault(y => string.Equals(x, y.Name, StringComparison.InvariantCultureIgnoreCase)) ?.Name; @@ -88,6 +88,7 @@ namespace Kyoo.Core.Api ); return null; }) + .OfType() .ToList(); if (context.Result != null) return; @@ -111,12 +112,12 @@ namespace Kyoo.Core.Api return; ILibraryManager library = context.HttpContext.RequestServices.GetRequiredService(); - ICollection fields = (ICollection)context.HttpContext.Items["fields"]; - Type pageType = Utility.GetGenericDefinition(result.DeclaredType, typeof(Page<>)); + ICollection fields = (ICollection)context.HttpContext.Items["fields"]!; + Type? pageType = Utility.GetGenericDefinition(result.DeclaredType, typeof(Page<>)); if (pageType != null) { - foreach (IResource resource in ((dynamic)result.Value).Items) + foreach (IResource resource in ((dynamic)result.Value!).Items) { foreach (string field in fields!) await library.Load(resource, field); @@ -125,7 +126,7 @@ namespace Kyoo.Core.Api else if (result.DeclaredType.IsAssignableTo(typeof(IResource))) { foreach (string field in fields!) - await library.Load((IResource)result.Value, field); + await library.Load((IResource)result.Value!, field); } } } diff --git a/back/src/Kyoo.Core/Views/Helper/Serializers/JsonSerializerContract.cs b/back/src/Kyoo.Core/Views/Helper/Serializers/JsonSerializerContract.cs index a5daeec4..bda5db67 100644 --- a/back/src/Kyoo.Core/Views/Helper/Serializers/JsonSerializerContract.cs +++ b/back/src/Kyoo.Core/Views/Helper/Serializers/JsonSerializerContract.cs @@ -53,16 +53,16 @@ namespace Kyoo.Core.Api { JsonProperty property = base.CreateProperty(member, memberSerialization); - LoadableRelationAttribute relation = member.GetCustomAttribute(); + LoadableRelationAttribute? relation = member.GetCustomAttribute(); if (relation != null) { property.ShouldSerialize = _ => { - string resType = (string)_httpContextAccessor.HttpContext!.Items["ResourceType"]; + string resType = (string)_httpContextAccessor.HttpContext!.Items["ResourceType"]!; if (member.DeclaringType!.Name != resType) return false; - ICollection fields = (ICollection)_httpContextAccessor.HttpContext!.Items["fields"]; - return fields!.Contains(member.Name); + ICollection fields = (ICollection)_httpContextAccessor.HttpContext!.Items["fields"]!; + return fields.Contains(member.Name); }; } diff --git a/back/src/Kyoo.Core/Views/Helper/Serializers/PeopleRoleConverter.cs b/back/src/Kyoo.Core/Views/Helper/Serializers/PeopleRoleConverter.cs index f4db9075..3aa4304b 100644 --- a/back/src/Kyoo.Core/Views/Helper/Serializers/PeopleRoleConverter.cs +++ b/back/src/Kyoo.Core/Views/Helper/Serializers/PeopleRoleConverter.cs @@ -31,10 +31,16 @@ namespace Kyoo.Core.Api public class PeopleRoleConverter : JsonConverter { /// - public override void WriteJson(JsonWriter writer, PeopleRole value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, PeopleRole? value, JsonSerializer serializer) { - ICollection oldPeople = value.Show?.People; - ICollection oldRoles = value.People?.Roles; + if (value == null) + { + writer.WriteNull(); + return; + } + + ICollection? oldPeople = value.Show?.People; + ICollection? oldRoles = value.People?.Roles; if (value.Show != null) value.Show.People = null; if (value.People != null) @@ -54,7 +60,7 @@ namespace Kyoo.Core.Api /// public override PeopleRole ReadJson(JsonReader reader, Type objectType, - PeopleRole existingValue, + PeopleRole? existingValue, bool hasExistingValue, JsonSerializer serializer) { diff --git a/back/src/Kyoo.Core/Views/Metadata/StaffApi.cs b/back/src/Kyoo.Core/Views/Metadata/StaffApi.cs index afcfe3b0..7918cf4f 100644 --- a/back/src/Kyoo.Core/Views/Metadata/StaffApi.cs +++ b/back/src/Kyoo.Core/Views/Metadata/StaffApi.cs @@ -85,7 +85,7 @@ namespace Kyoo.Core.Api [FromQuery] Dictionary where, [FromQuery] Pagination pagination) { - Expression> whereQuery = ApiHelper.ParseWhere(where); + Expression>? whereQuery = ApiHelper.ParseWhere(where); Sort sort = Sort.From(sortBy); ICollection resources = await identifier.Match( diff --git a/back/src/Kyoo.Core/Views/Metadata/StudioApi.cs b/back/src/Kyoo.Core/Views/Metadata/StudioApi.cs index 90c72539..6e3776ab 100644 --- a/back/src/Kyoo.Core/Views/Metadata/StudioApi.cs +++ b/back/src/Kyoo.Core/Views/Metadata/StudioApi.cs @@ -83,7 +83,7 @@ namespace Kyoo.Core.Api [FromQuery] Pagination pagination) { ICollection resources = await _libraryManager.GetAll( - ApiHelper.ParseWhere(where, identifier.Matcher(x => x.StudioId, x => x.Studio.Slug)), + ApiHelper.ParseWhere(where, identifier.Matcher(x => x.StudioId, x => x.Studio!.Slug)), Sort.From(sortBy), pagination ); diff --git a/back/src/Kyoo.Core/Views/Resources/CollectionApi.cs b/back/src/Kyoo.Core/Views/Resources/CollectionApi.cs index bc15368a..5005bdf2 100644 --- a/back/src/Kyoo.Core/Views/Resources/CollectionApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/CollectionApi.cs @@ -84,7 +84,7 @@ namespace Kyoo.Core.Api [FromQuery] Pagination pagination) { ICollection resources = await _libraryManager.GetAll( - ApiHelper.ParseWhere(where, identifier.IsContainedIn(x => x.Collections)), + ApiHelper.ParseWhere(where, identifier.IsContainedIn(x => x.Collections!)), Sort.From(sortBy), pagination ); diff --git a/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs b/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs index 6b73a246..18c77ba8 100644 --- a/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs @@ -73,7 +73,7 @@ namespace Kyoo.Core.Api [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> GetShow(Identifier identifier) { - return await _libraryManager.Get(identifier.IsContainedIn(x => x.Episodes)); + return await _libraryManager.Get(identifier.IsContainedIn(x => x.Episodes!)); } /// @@ -93,10 +93,10 @@ namespace Kyoo.Core.Api [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> GetSeason(Identifier identifier) { - Season ret = await _libraryManager.GetOrDefault(identifier.IsContainedIn(x => x.Episodes)); + Season? ret = await _libraryManager.GetOrDefault(identifier.IsContainedIn(x => x.Episodes!)); if (ret != null) return ret; - Episode episode = await identifier.Match( + Episode? episode = await identifier.Match( id => _libraryManager.GetOrDefault(id), slug => _libraryManager.GetOrDefault(slug) ); diff --git a/back/src/Kyoo.Core/Views/Resources/MovieApi.cs b/back/src/Kyoo.Core/Views/Resources/MovieApi.cs index a1825517..ab3977ad 100644 --- a/back/src/Kyoo.Core/Views/Resources/MovieApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/MovieApi.cs @@ -108,7 +108,7 @@ namespace Kyoo.Core.Api [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> GetStudio(Identifier identifier) { - return await _libraryManager.Get(identifier.IsContainedIn(x => x.Movies)); + return await _libraryManager.Get(identifier.IsContainedIn(x => x.Movies!)); } /// @@ -136,7 +136,7 @@ namespace Kyoo.Core.Api [FromQuery] Pagination pagination) { ICollection resources = await _libraryManager.GetAll( - ApiHelper.ParseWhere(where, identifier.IsContainedIn(x => x.Movies)), + ApiHelper.ParseWhere(where, identifier.IsContainedIn(x => x.Movies!)), Sort.From(sortBy), pagination ); diff --git a/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs b/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs index f26bbe30..b2dccc81 100644 --- a/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs @@ -84,7 +84,7 @@ namespace Kyoo.Core.Api [FromQuery] Pagination pagination) { ICollection resources = await _libraryManager.GetAll( - ApiHelper.ParseWhere(where, identifier.Matcher(x => x.SeasonId, x => x.Season.Slug)), + ApiHelper.ParseWhere(where, identifier.Matcher(x => x.SeasonId, x => x.Season!.Slug)), Sort.From(sortBy), pagination ); @@ -109,7 +109,7 @@ namespace Kyoo.Core.Api [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> GetShow(Identifier identifier) { - Show ret = await _libraryManager.GetOrDefault(identifier.IsContainedIn(x => x.Seasons)); + Show? ret = await _libraryManager.GetOrDefault(identifier.IsContainedIn(x => x.Seasons!)); if (ret == null) return NotFound(); return ret; diff --git a/back/src/Kyoo.Core/Views/Resources/ShowApi.cs b/back/src/Kyoo.Core/Views/Resources/ShowApi.cs index 2084291e..5b55a5d1 100644 --- a/back/src/Kyoo.Core/Views/Resources/ShowApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/ShowApi.cs @@ -86,7 +86,7 @@ namespace Kyoo.Core.Api [FromQuery] Pagination pagination) { ICollection resources = await _libraryManager.GetAll( - ApiHelper.ParseWhere(where, identifier.Matcher(x => x.ShowId, x => x.Show.Slug)), + ApiHelper.ParseWhere(where, identifier.Matcher(x => x.ShowId, x => x.Show!.Slug)), Sort.From(sortBy), pagination ); @@ -121,7 +121,7 @@ namespace Kyoo.Core.Api [FromQuery] Pagination pagination) { ICollection resources = await _libraryManager.GetAll( - ApiHelper.ParseWhere(where, identifier.Matcher(x => x.ShowId, x => x.Show.Slug)), + ApiHelper.ParseWhere(where, identifier.Matcher(x => x.ShowId, x => x.Show!.Slug)), Sort.From(sortBy), pagination ); @@ -155,7 +155,7 @@ namespace Kyoo.Core.Api [FromQuery] Dictionary where, [FromQuery] Pagination pagination) { - Expression> whereQuery = ApiHelper.ParseWhere(where); + Expression>? whereQuery = ApiHelper.ParseWhere(where); Sort sort = Sort.From(sortBy); ICollection resources = await identifier.Match( @@ -180,7 +180,7 @@ namespace Kyoo.Core.Api [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> GetStudio(Identifier identifier) { - return await _libraryManager.Get(identifier.IsContainedIn(x => x.Shows)); + return await _libraryManager.Get(identifier.IsContainedIn(x => x.Shows!)); } /// @@ -208,7 +208,7 @@ namespace Kyoo.Core.Api [FromQuery] Pagination pagination) { ICollection resources = await _libraryManager.GetAll( - ApiHelper.ParseWhere(where, identifier.IsContainedIn(x => x.Shows)), + ApiHelper.ParseWhere(where, identifier.IsContainedIn(x => x.Shows!)), Sort.From(sortBy), pagination ); diff --git a/back/src/Kyoo.Postgresql/DatabaseContext.cs b/back/src/Kyoo.Postgresql/DatabaseContext.cs index a4adb84b..bf93273c 100644 --- a/back/src/Kyoo.Postgresql/DatabaseContext.cs +++ b/back/src/Kyoo.Postgresql/DatabaseContext.cs @@ -180,8 +180,8 @@ namespace Kyoo.Postgresql modelBuilder.Entity() .Property(x => x.ExternalId) .HasConversion( - v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null), - v => JsonSerializer.Deserialize>(v, (JsonSerializerOptions)null) + v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null), + v => JsonSerializer.Deserialize>(v, (JsonSerializerOptions?)null)! ) .HasColumnType("json"); } @@ -215,8 +215,8 @@ namespace Kyoo.Postgresql /// The owning type of the relationship /// The owned type of the relationship private void _HasManyToMany(ModelBuilder modelBuilder, - Expression>> firstNavigation, - Expression>> secondNavigation) + Expression?>> firstNavigation, + Expression?>> secondNavigation) where T : class, IResource where T2 : class, IResource { @@ -360,7 +360,7 @@ namespace Kyoo.Postgresql public T GetTemporaryObject(T model) where T : class, IResource { - T tmp = Set().Local.FirstOrDefault(x => x.Id == model.Id); + T? tmp = Set().Local.FirstOrDefault(x => x.Id == model.Id); if (tmp != null) return tmp; Entry(model).State = EntityState.Unchanged; @@ -509,8 +509,7 @@ namespace Kyoo.Postgresql /// The slug of the resource to check /// The type of entity to check /// The local entity representing the resource with the given slug if it exists or null. - [CanBeNull] - public T LocalEntity(string slug) + public T? LocalEntity(string slug) where T : class, IResource { return ChangeTracker.Entries() diff --git a/back/src/Kyoo.Postgresql/PostgresContext.cs b/back/src/Kyoo.Postgresql/PostgresContext.cs index 64fde1ed..ab185f43 100644 --- a/back/src/Kyoo.Postgresql/PostgresContext.cs +++ b/back/src/Kyoo.Postgresql/PostgresContext.cs @@ -106,7 +106,7 @@ namespace Kyoo.Postgresql modelBuilder.HasPostgresEnum(); modelBuilder.HasPostgresEnum(); - modelBuilder.HasDbFunction(typeof(DatabaseContext).GetMethod(nameof(MD5))) + modelBuilder.HasDbFunction(typeof(DatabaseContext).GetMethod(nameof(MD5))!) .HasTranslation(args => new SqlFunctionExpression( "md5",