diff options
| author | Kamen Mladenov <kamen.d.mladenov@protonmail.com> | 2021-04-09 19:51:35 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-04-09 19:51:35 +0300 |
| commit | 233f38915ba0079079233eff55434ef349c05c45 (patch) | |
| tree | 6c5f69017865bcab87355e910c87339453da1406 /src/Data/DevHive.Data/Repositories | |
| parent | f4a70c6430db923af9fa9958a11c2d6612cb52cc (diff) | |
| parent | a992357efcf1bc1ece81b95ecee5e05a0b73bfdc (diff) | |
| download | DevHive-0.2.tar DevHive-0.2.tar.gz DevHive-0.2.zip | |
Merge pull request #28 from Team-Kaleidoscope/devHEADv0.2mainheroku/main
Second stage: Complete
Diffstat (limited to 'src/Data/DevHive.Data/Repositories')
10 files changed, 729 insertions, 0 deletions
diff --git a/src/Data/DevHive.Data/Repositories/BaseRepository.cs b/src/Data/DevHive.Data/Repositories/BaseRepository.cs new file mode 100644 index 0000000..239a3bc --- /dev/null +++ b/src/Data/DevHive.Data/Repositories/BaseRepository.cs @@ -0,0 +1,59 @@ +using System; +using System.Threading.Tasks; +using DevHive.Data.Interfaces; +using Microsoft.EntityFrameworkCore; + +namespace DevHive.Data.Repositories +{ + public class BaseRepository<TEntity> : IRepository<TEntity> + where TEntity : class + { + private readonly DbContext _context; + + public BaseRepository(DbContext context) + { + this._context = context; + } + + public virtual async Task<bool> AddAsync(TEntity entity) + { + await this._context + .Set<TEntity>() + .AddAsync(entity); + + return await this.SaveChangesAsync(); + } + + public virtual async Task<TEntity> GetByIdAsync(Guid id) + { + return await this._context + .Set<TEntity>() + .FindAsync(id); + } + + public virtual async Task<bool> EditAsync(Guid id, TEntity newEntity) + { + var entry = this._context.Entry(newEntity); + if (entry.State == EntityState.Detached) + this._context.Attach(newEntity); + + entry.State = EntityState.Modified; + + return await this.SaveChangesAsync(); + } + + public virtual async Task<bool> DeleteAsync(TEntity entity) + { + this._context.Remove(entity); + + return await this.SaveChangesAsync(); + } + + public virtual async Task<bool> SaveChangesAsync() + { + int result = await _context.SaveChangesAsync(); + + return result >= 1; + } + } +} diff --git a/src/Data/DevHive.Data/Repositories/CommentRepository.cs b/src/Data/DevHive.Data/Repositories/CommentRepository.cs new file mode 100644 index 0000000..9364776 --- /dev/null +++ b/src/Data/DevHive.Data/Repositories/CommentRepository.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using Microsoft.EntityFrameworkCore; + +namespace DevHive.Data.Repositories +{ + public class CommentRepository : BaseRepository<Comment>, ICommentRepository + { + private readonly DevHiveContext _context; + + public CommentRepository(DevHiveContext context) + : base(context) + { + this._context = context; + } + + #region Read + public override async Task<Comment> GetByIdAsync(Guid id) + { + return await this._context.Comments + .Include(x => x.Creator) + .Include(x => x.Post) + .FirstOrDefaultAsync(x => x.Id == id); + } + + /// <summary> + /// This method returns the comment that is made at exactly the given time and by the given creator + /// </summary> + public async Task<Comment> GetCommentByIssuerAndTimeCreatedAsync(Guid issuerId, DateTime timeCreated) + { + return await this._context.Comments + .FirstOrDefaultAsync(p => p.Creator.Id == issuerId && + p.TimeCreated == timeCreated); + } + + public async Task<List<Comment>> GetPostComments(Guid postId) + { + return await this._context.Posts + .SelectMany(x => x.Comments) + .Where(x => x.Post.Id == postId) + .ToListAsync(); + } + #endregion + + #region Update + public override async Task<bool> EditAsync(Guid id, Comment newEntity) + { + Comment comment = await this.GetByIdAsync(id); + + this._context + .Entry(comment) + .CurrentValues + .SetValues(newEntity); + + return await this.SaveChangesAsync(); + } + #endregion + + + #region Validations + public async Task<bool> DoesCommentExist(Guid id) + { + return await this._context.Comments + .AsNoTracking() + .AnyAsync(r => r.Id == id); + } + #endregion + } +} diff --git a/src/Data/DevHive.Data/Repositories/FeedRepository.cs b/src/Data/DevHive.Data/Repositories/FeedRepository.cs new file mode 100644 index 0000000..d3312d7 --- /dev/null +++ b/src/Data/DevHive.Data/Repositories/FeedRepository.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using AutoMapper.Internal; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using Microsoft.EntityFrameworkCore; + +namespace DevHive.Data.Repositories +{ + public class FeedRepository : IFeedRepository + { + private readonly DevHiveContext _context; + + public FeedRepository(DevHiveContext context) + { + this._context = context; + } + + /// <summary> + /// This returns a given amount of posts of all given friends, created before "firstRequestIssued", + /// ordered from latest to oldest (time created). + /// PageSize specifies how many posts to get, and pageNumber specifices how many posts to skip (pageNumber * pageSize). + /// + /// This method is used in the feed page. + /// Posts from friends are meant to be gotten in chunks, meaning you get X posts, and then get another amount of posts, + /// that are after the first X posts. + /// </summary> + public async Task<List<Post>> GetFriendsPosts(List<User> friendsList, DateTime firstRequestIssued, int pageNumber, int pageSize) + { + List<Guid> friendsIds = friendsList.Select(f => f.Id).ToList(); + + List<Post> posts = await this._context.Posts + .Where(post => post.TimeCreated < firstRequestIssued) + .Where(p => friendsIds.Contains(p.Creator.Id)) + .ToListAsync(); + + // Ordering by descending can't happen in query, because it doesn't order it + // completely correctly (example: in query these two times are ordered + // like this: 2021-01-30T11:49:45, 2021-01-28T21:37:40.701244) + posts = posts + .OrderByDescending(x => x.TimeCreated.ToFileTime()) + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize) + .ToList(); + + return posts; + } + + /// <summary> + /// This returns a given amount of posts, that a user has made, created before "firstRequestIssued", + /// ordered from latest to oldest (time created). + /// PageSize specifies how many posts to get, and pageNumber specifices how many posts to skip (pageNumber * pageSize). + /// + /// This method is used in the profile page. + /// Posts from friends are meant to be gotten in chunks, meaning you get X posts, and then get another amount of posts, + /// that are after the first X posts. + /// </summary> + public async Task<List<Post>> GetUsersPosts(User user, DateTime firstRequestIssued, int pageNumber, int pageSize) + { + List<Post> posts = await this._context.Posts + .Where(post => post.TimeCreated < firstRequestIssued) + .Where(p => p.Creator.Id == user.Id) + .ToListAsync(); + + // Look at GetFriendsPosts on why this is done like this + posts = posts + .OrderByDescending(x => x.TimeCreated.ToFileTime()) + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize) + .ToList(); + + return posts; + } + } +} diff --git a/src/Data/DevHive.Data/Repositories/LanguageRepository.cs b/src/Data/DevHive.Data/Repositories/LanguageRepository.cs new file mode 100644 index 0000000..3528ea8 --- /dev/null +++ b/src/Data/DevHive.Data/Repositories/LanguageRepository.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using Microsoft.EntityFrameworkCore; + +namespace DevHive.Data.Repositories +{ + public class LanguageRepository : BaseRepository<Language>, ILanguageRepository + { + private readonly DevHiveContext _context; + + public LanguageRepository(DevHiveContext context) + : base(context) + { + this._context = context; + } + + #region Read + public async Task<Language> GetByNameAsync(string languageName) + { + return await this._context.Languages + .FirstOrDefaultAsync(x => x.Name == languageName); + } + + /// <summary> + /// Returns all technologies that exist in the database + /// </summary> + public HashSet<Language> GetLanguages() + { + return this._context.Languages.ToHashSet(); + } + #endregion + + #region Validations + public async Task<bool> DoesLanguageNameExistAsync(string languageName) + { + return await this._context.Languages + .AsNoTracking() + .AnyAsync(r => r.Name == languageName); + } + + public async Task<bool> DoesLanguageExistAsync(Guid id) + { + return await this._context.Languages + .AsNoTracking() + .AnyAsync(r => r.Id == id); + } + #endregion + } +} diff --git a/src/Data/DevHive.Data/Repositories/PostRepository.cs b/src/Data/DevHive.Data/Repositories/PostRepository.cs new file mode 100644 index 0000000..b5228c2 --- /dev/null +++ b/src/Data/DevHive.Data/Repositories/PostRepository.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using DevHive.Data.Models.Relational; +using Microsoft.EntityFrameworkCore; + +namespace DevHive.Data.Repositories +{ + public class PostRepository : BaseRepository<Post>, IPostRepository + { + private readonly DevHiveContext _context; + private readonly IUserRepository _userRepository; + + public PostRepository(DevHiveContext context, IUserRepository userRepository) + : base(context) + { + this._context = context; + this._userRepository = userRepository; + } + + public async Task<bool> AddNewPostToCreator(Guid userId, Post post) + { + User user = await this._userRepository.GetByIdAsync(userId); + user.Posts.Add(post); + + return await this.SaveChangesAsync(); + } + + #region Read + public override async Task<Post> GetByIdAsync(Guid id) + { + return await this._context.Posts + .Include(x => x.Comments) + .Include(x => x.Creator) + .Include(x => x.Attachments) + .Include(x => x.Ratings) + .FirstOrDefaultAsync(x => x.Id == id); + } + + /// <summary> + /// This method returns the post that is made at exactly the given time and by the given creator + /// </summary> + public async Task<Post> GetPostByCreatorAndTimeCreatedAsync(Guid creatorId, DateTime timeCreated) + { + return await this._context.Posts + .FirstOrDefaultAsync(p => p.Creator.Id == creatorId && + p.TimeCreated == timeCreated); + } + + public async Task<List<string>> GetFileUrls(Guid postId) + { + return (await this.GetByIdAsync(postId)).Attachments.Select(x => x.FileUrl).ToList(); + } + #endregion + + #region Update + public override async Task<bool> EditAsync(Guid id, Post newEntity) + { + Post post = await this.GetByIdAsync(id); + + this._context + .Entry(post) + .CurrentValues + .SetValues(newEntity); + + List<PostAttachments> postAttachments = new(); + foreach (var attachment in newEntity.Attachments) + postAttachments.Add(attachment); + post.Attachments = postAttachments; + + post.Comments.Clear(); + foreach (var comment in newEntity.Comments) + post.Comments.Add(comment); + + this._context.Entry(post).State = EntityState.Modified; + + return await this.SaveChangesAsync(); + } + #endregion + + #region Validations + public async Task<bool> DoesPostExist(Guid postId) + { + return await this._context.Posts + .AsNoTracking() + .AnyAsync(r => r.Id == postId); + } + + public async Task<bool> DoesPostHaveFiles(Guid postId) + { + return await this._context.Posts + .AsNoTracking() + .Where(x => x.Id == postId) + .Select(x => x.Attachments) + .AnyAsync(); + } + #endregion + } +} diff --git a/src/Data/DevHive.Data/Repositories/ProfilePictureRepository.cs b/src/Data/DevHive.Data/Repositories/ProfilePictureRepository.cs new file mode 100644 index 0000000..7284464 --- /dev/null +++ b/src/Data/DevHive.Data/Repositories/ProfilePictureRepository.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using Microsoft.EntityFrameworkCore; + +namespace DevHive.Data.Repositories +{ + public class ProfilePictureRepository : BaseRepository<ProfilePicture>, IProfilePictureRepository + { + private readonly DevHiveContext _context; + + public ProfilePictureRepository(DevHiveContext context) + : base(context) + { + this._context = context; + } + + public async Task<ProfilePicture> GetByURLAsync(string picUrl) + { + return await this._context + .ProfilePicture + .FirstOrDefaultAsync(x => x.PictureURL == picUrl); + } + } +} diff --git a/src/Data/DevHive.Data/Repositories/RatingRepository.cs b/src/Data/DevHive.Data/Repositories/RatingRepository.cs new file mode 100644 index 0000000..2048c3f --- /dev/null +++ b/src/Data/DevHive.Data/Repositories/RatingRepository.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using Microsoft.EntityFrameworkCore; + +namespace DevHive.Data.Repositories +{ + public class RatingRepository : BaseRepository<Rating>, IRatingRepository + { + private readonly DevHiveContext _context; + + public RatingRepository(DevHiveContext context) + : base(context) + { + this._context = context; + } + + public override async Task<Rating> GetByIdAsync(Guid id) + { + return await this._context.Rating + .Include(x => x.User) + .Include(x => x.Post) + .FirstOrDefaultAsync(x => x.Id == id); + } + /// <summary> + /// Gets all the ratings for a post. + /// </summary> + /// <param name="postId">Id of the post.</param> + /// <returns></returns> + public async Task<List<Rating>> GetRatingsByPostId(Guid postId) + { + return await this._context.Rating + .Include(x => x.User) + .Include(x => x.Post) + .Where(x => x.Post.Id == postId).ToListAsync(); + } + /// <summary> + /// Checks if a user rated a given post. In DevHive every user has one or no rating for every post. + /// </summary> + /// <param name="userId">Id of the user.</param> + /// <param name="postId">Id of the post.</param> + /// <returns>True if the user has already rated the post and false if he hasn't.</returns> + public async Task<bool> UserRatedPost(Guid userId, Guid postId) + { + return await this._context.Rating + .Where(x => x.Post.Id == postId) + .AnyAsync(x => x.User.Id == userId); + } + /// <summary> + /// Gets a rating by the post to which the rating corresponds and the user who created it. + /// </summary> + /// <param name="userId">Id of the user.</param> + /// <param name="postId">Id of the post.</param> + /// <returns>Rating for the given post by the given user.</returns> + public async Task<Rating> GetRatingByUserAndPostId(Guid userId, Guid postId) + { + return await this._context.Rating + .Include(x => x.User) + .Include(x => x.Post) + .FirstOrDefaultAsync(x => x.Post.Id == postId && x.User.Id == userId); + } + + /// <summary> + /// Checks if a given rating already exist + /// </summary> + /// <param name="id">Id of the rating</param> + /// <returns>True if the rating exists and false if it does not.</returns> + public async Task<bool> DoesRatingExist(Guid id) + { + return await this._context.Rating + .AsNoTracking() + .AnyAsync(r => r.Id == id); + } + } +} + diff --git a/src/Data/DevHive.Data/Repositories/RoleRepository.cs b/src/Data/DevHive.Data/Repositories/RoleRepository.cs new file mode 100644 index 0000000..99e0634 --- /dev/null +++ b/src/Data/DevHive.Data/Repositories/RoleRepository.cs @@ -0,0 +1,56 @@ +using System; +using System.Threading.Tasks; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; + +namespace DevHive.Data.Repositories +{ + public class RoleRepository : BaseRepository<Role>, IRoleRepository + { + private readonly RoleManager<Role> _roleManager; + + public RoleRepository(DevHiveContext context, RoleManager<Role> roleManager) + : base(context) + { + this._roleManager = roleManager; + } + + #region Create + public override async Task<bool> AddAsync(Role entity) + { + IdentityResult result = await this._roleManager.CreateAsync(entity); + + return result.Succeeded; + } + #endregion + + #region Read + public async Task<Role> GetByNameAsync(string name) + { + return await this._roleManager.FindByNameAsync(name); + } + #endregion + + public override async Task<bool> EditAsync(Guid id, Role newEntity) + { + newEntity.Id = id; + IdentityResult result = await this._roleManager.UpdateAsync(newEntity); + + return result.Succeeded; + } + + #region Validations + public async Task<bool> DoesNameExist(string name) + { + return await this._roleManager.RoleExistsAsync(name); + } + + public async Task<bool> DoesRoleExist(Guid id) + { + return await this._roleManager.Roles.AnyAsync(r => r.Id == id); + } + #endregion + } +} diff --git a/src/Data/DevHive.Data/Repositories/TechnologyRepository.cs b/src/Data/DevHive.Data/Repositories/TechnologyRepository.cs new file mode 100644 index 0000000..d0d1f3f --- /dev/null +++ b/src/Data/DevHive.Data/Repositories/TechnologyRepository.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using Microsoft.EntityFrameworkCore; + +namespace DevHive.Data.Repositories +{ + public class TechnologyRepository : BaseRepository<Technology>, ITechnologyRepository + { + private readonly DevHiveContext _context; + + public TechnologyRepository(DevHiveContext context) + : base(context) + { + this._context = context; + } + + #region Read + public async Task<Technology> GetByNameAsync(string technologyName) + { + return await this._context.Technologies + .FirstOrDefaultAsync(x => x.Name == technologyName); + } + + /// <summary> + /// Returns all technologies that exist in the database + /// </summary> + public HashSet<Technology> GetTechnologies() + { + return this._context.Technologies.ToHashSet(); + } + #endregion + + #region Validations + public async Task<bool> DoesTechnologyNameExistAsync(string technologyName) + { + return await this._context.Technologies + .AsNoTracking() + .AnyAsync(r => r.Name == technologyName); + } + + public async Task<bool> DoesTechnologyExistAsync(Guid id) + { + return await this._context.Technologies + .AsNoTracking() + .AnyAsync(x => x.Id == id); + } + #endregion + } +} diff --git a/src/Data/DevHive.Data/Repositories/UserRepository.cs b/src/Data/DevHive.Data/Repositories/UserRepository.cs new file mode 100644 index 0000000..d570480 --- /dev/null +++ b/src/Data/DevHive.Data/Repositories/UserRepository.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; + +namespace DevHive.Data.Repositories +{ + public class UserRepository : BaseRepository<User>, IUserRepository + { + private readonly UserManager<User> _userManager; + private readonly RoleManager<Role> _roleManager; + + public UserRepository(DevHiveContext context, UserManager<User> userManager, RoleManager<Role> roleManager) + : base(context) + { + this._userManager = userManager; + this._roleManager = roleManager; + } + + #region Create + public override async Task<bool> AddAsync(User entity) + { + entity.PasswordHash = this._userManager.PasswordHasher.HashPassword(entity, entity.PasswordHash).ToString(); + IdentityResult result = await this._userManager.CreateAsync(entity); + + return result.Succeeded; + } + + public async Task<bool> AddRoleToUser(User user, string roleName) + { + bool succeeded = (await this._userManager.AddToRoleAsync(user, roleName)).Succeeded; + if (succeeded) + { + user.Roles.Add(await this._roleManager.FindByNameAsync(roleName)); + succeeded = await this.SaveChangesAsync(); + } + + return succeeded; + } + #endregion + + #region Read + public override async Task<User> GetByIdAsync(Guid id) + { + return await this._userManager.Users + .Include(x => x.Roles) + .Include(x => x.Languages) + .Include(x => x.Technologies) + .Include(x => x.Posts) + .Include(x => x.Friends) + .Include(x => x.ProfilePicture) + .FirstOrDefaultAsync(x => x.Id == id); + } + + public async Task<User> GetByUsernameAsync(string username) + { + return await this._userManager.Users + .Include(x => x.Roles) + .Include(x => x.Languages) + .Include(x => x.Technologies) + .Include(x => x.Posts) + .Include(x => x.Friends) + .Include(x => x.ProfilePicture) + .FirstOrDefaultAsync(x => x.UserName == username); + } + #endregion + + #region Update + public override async Task<bool> EditAsync(Guid id, User newEntity) + { + newEntity.Id = id; + IdentityResult result = await this._userManager.UpdateAsync(newEntity); + + return result.Succeeded; + } + + public async Task<bool> UpdateProfilePicture(Guid userId, string pictureUrl) + { + User user = await this.GetByIdAsync(userId); + + user.ProfilePicture.PictureURL = pictureUrl; + + return await this.SaveChangesAsync(); + } + #endregion + + #region Delete + public override async Task<bool> DeleteAsync(User entity) + { + IdentityResult result = await this._userManager.DeleteAsync(entity); + + return result.Succeeded; + } + #endregion + + #region Validations + public async Task<bool> VerifyPassword(User user, string password) + { + return await this._userManager.CheckPasswordAsync(user, password); + } + + public async Task<bool> IsInRoleAsync(User user, string roleName) + { + return await this._userManager.IsInRoleAsync(user, roleName); + } + + public async Task<bool> DoesUserExistAsync(Guid id) + { + return await this._userManager.Users.AnyAsync(x => x.Id == id); + } + + public async Task<bool> DoesUsernameExistAsync(string username) + { + return await this._userManager.Users + .AsNoTracking() + .AnyAsync(u => u.UserName == username); + } + + public async Task<bool> DoesEmailExistAsync(string email) + { + return await this._userManager.Users + .AsNoTracking() + .AnyAsync(u => u.Email == email); + } + + public async Task<bool> ValidateFriendsCollectionAsync(List<string> usernames) + { + bool valid = true; + + foreach (var username in usernames) + { + if (!await this.DoesUsernameExistAsync(username)) + { + valid = false; + break; + } + } + return valid; + } + + public async Task<bool> DoesUserHaveThisUsernameAsync(Guid id, string username) + { + return await this._userManager.Users + .AnyAsync(x => x.Id == id && + x.UserName == username); + } + #endregion + } +} |
