using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using API.Constants; using API.DTOs; using API.DTOs.Reader; using API.Entities; using API.Interfaces.Repositories; using AutoMapper; using AutoMapper.QueryableExtensions; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; namespace API.Data.Repositories { [Flags] public enum AppUserIncludes { None = 1, Progress = 2, Bookmarks = 4, ReadingLists = 8, Ratings = 16 } public class UserRepository : IUserRepository { private readonly DataContext _context; private readonly UserManager _userManager; private readonly IMapper _mapper; public UserRepository(DataContext context, UserManager userManager, IMapper mapper) { _context = context; _userManager = userManager; _mapper = mapper; } public void Update(AppUser user) { _context.Entry(user).State = EntityState.Modified; } public void Update(AppUserPreferences preferences) { _context.Entry(preferences).State = EntityState.Modified; } public void Update(AppUserBookmark bookmark) { _context.Entry(bookmark).State = EntityState.Modified; } public void Delete(AppUser user) { _context.AppUser.Remove(user); } /// /// A one stop shop to get a tracked AppUser instance with any number of JOINs generated by passing bitwise flags. /// /// /// Includes() you want. Pass multiple with flag1 | flag2 /// public async Task GetUserByUsernameAsync(string username, AppUserIncludes includeFlags = AppUserIncludes.None) { var query = _context.Users .Where(x => x.UserName == username); query = AddIncludesToQuery(query, includeFlags); return await query.SingleOrDefaultAsync(); } /// /// A one stop shop to get a tracked AppUser instance with any number of JOINs generated by passing bitwise flags. /// /// /// Includes() you want. Pass multiple with flag1 | flag2 /// public async Task GetUserByIdAsync(int userId, AppUserIncludes includeFlags = AppUserIncludes.None) { var query = _context.Users .Where(x => x.Id == userId); query = AddIncludesToQuery(query, includeFlags); return await query.SingleOrDefaultAsync(); } public async Task GetBookmarkForPage(int page, int chapterId, int userId) { return await _context.AppUserBookmark .Where(b => b.Page == page && b.ChapterId == chapterId && b.AppUserId == userId) .SingleOrDefaultAsync(); } private static IQueryable AddIncludesToQuery(IQueryable 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.Ratings)) { query = query.Include(u => u.Ratings); } return query; } /// /// This fetches the Id for a user. Use whenever you just need an ID. /// /// /// public async Task GetUserIdByUsernameAsync(string username) { return await _context.Users .Where(x => x.UserName == username) .Select(u => u.Id) .SingleOrDefaultAsync(); } /// /// Gets an AppUser by username. Returns back Reading List and their Items. /// /// /// public async Task GetUserWithReadingListsByUsernameAsync(string username) { return await _context.Users .Include(u => u.ReadingLists) .ThenInclude(l => l.Items) .SingleOrDefaultAsync(x => x.UserName == username); } public async Task> GetAdminUsersAsync() { return await _userManager.GetUsersInRoleAsync(PolicyConstants.AdminRole); } public async Task GetUserRating(int seriesId, int userId) { return await _context.AppUserRating.Where(r => r.SeriesId == seriesId && r.AppUserId == userId) .SingleOrDefaultAsync(); } public async Task GetPreferencesAsync(string username) { return await _context.AppUserPreferences .Include(p => p.AppUser) .SingleOrDefaultAsync(p => p.AppUser.UserName == username); } public async Task> GetBookmarkDtosForSeries(int userId, int seriesId) { return await _context.AppUserBookmark .Where(x => x.AppUserId == userId && x.SeriesId == seriesId) .OrderBy(x => x.Page) .AsNoTracking() .ProjectTo(_mapper.ConfigurationProvider) .ToListAsync(); } public async Task> GetBookmarkDtosForVolume(int userId, int volumeId) { return await _context.AppUserBookmark .Where(x => x.AppUserId == userId && x.VolumeId == volumeId) .OrderBy(x => x.Page) .AsNoTracking() .ProjectTo(_mapper.ConfigurationProvider) .ToListAsync(); } public async Task> GetBookmarkDtosForChapter(int userId, int chapterId) { return await _context.AppUserBookmark .Where(x => x.AppUserId == userId && x.ChapterId == chapterId) .OrderBy(x => x.Page) .AsNoTracking() .ProjectTo(_mapper.ConfigurationProvider) .ToListAsync(); } public async Task> GetAllBookmarkDtos(int userId) { return await _context.AppUserBookmark .Where(x => x.AppUserId == userId) .OrderBy(x => x.Page) .AsNoTracking() .ProjectTo(_mapper.ConfigurationProvider) .ToListAsync(); } /// /// Fetches the UserId by API Key. This does not include any extra information /// /// /// public async Task GetUserIdByApiKeyAsync(string apiKey) { return await _context.AppUser .Where(u => u.ApiKey.Equals(apiKey)) .Select(u => u.Id) .SingleOrDefaultAsync(); } public async Task> GetMembersAsync() { return await _context.Users .Include(x => x.Libraries) .Include(r => r.UserRoles) .ThenInclude(r => r.Role) .OrderBy(u => u.UserName) .Select(u => new MemberDto { Id = u.Id, Username = u.UserName, Created = u.Created, LastActive = u.LastActive, Roles = u.UserRoles.Select(r => r.Role.Name).ToList(), Libraries = u.Libraries.Select(l => new LibraryDto { Name = l.Name, Type = l.Type, LastScanned = l.LastScanned, Folders = l.Folders.Select(x => x.Path).ToList() }).ToList() }) .AsNoTracking() .ToListAsync(); } } }