aboutsummaryrefslogtreecommitdiff
path: root/src/Services/DevHive.Services
diff options
context:
space:
mode:
Diffstat (limited to 'src/Services/DevHive.Services')
-rw-r--r--src/Services/DevHive.Services/Configurations/Mapping/CommentMappings.cs27
-rw-r--r--src/Services/DevHive.Services/Configurations/Mapping/FeedMappings.cs11
-rw-r--r--src/Services/DevHive.Services/Configurations/Mapping/LanguageMappings.cs22
-rw-r--r--src/Services/DevHive.Services/Configurations/Mapping/PostMappings.cs33
-rw-r--r--src/Services/DevHive.Services/Configurations/Mapping/RatingMappings.cs15
-rw-r--r--src/Services/DevHive.Services/Configurations/Mapping/RoleMapings.cs19
-rw-r--r--src/Services/DevHive.Services/Configurations/Mapping/TechnologyMappings.cs21
-rw-r--r--src/Services/DevHive.Services/Configurations/Mapping/UserMappings.cs31
-rw-r--r--src/Services/DevHive.Services/DevHive.Services.csproj31
-rw-r--r--src/Services/DevHive.Services/Interfaces/ICloudService.cs16
-rw-r--r--src/Services/DevHive.Services/Interfaces/ICommentService.cs20
-rw-r--r--src/Services/DevHive.Services/Interfaces/IFeedService.cs11
-rw-r--r--src/Services/DevHive.Services/Interfaces/ILanguageService.cs19
-rw-r--r--src/Services/DevHive.Services/Interfaces/IPostService.cs20
-rw-r--r--src/Services/DevHive.Services/Interfaces/IRateService.cs14
-rw-r--r--src/Services/DevHive.Services/Interfaces/IRoleService.cs17
-rw-r--r--src/Services/DevHive.Services/Interfaces/ITechnologyService.cs19
-rw-r--r--src/Services/DevHive.Services/Interfaces/IUserService.cs25
-rw-r--r--src/Services/DevHive.Services/Options/JWTOptions.cs14
-rw-r--r--src/Services/DevHive.Services/Services/CloudinaryService.cs53
-rw-r--r--src/Services/DevHive.Services/Services/CommentService.cs169
-rw-r--r--src/Services/DevHive.Services/Services/FeedService.cs84
-rw-r--r--src/Services/DevHive.Services/Services/LanguageService.cs89
-rw-r--r--src/Services/DevHive.Services/Services/PostService.cs239
-rw-r--r--src/Services/DevHive.Services/Services/RateService.cs80
-rw-r--r--src/Services/DevHive.Services/Services/RoleService.cs70
-rw-r--r--src/Services/DevHive.Services/Services/TechnologyService.cs90
-rw-r--r--src/Services/DevHive.Services/Services/UserService.cs382
28 files changed, 1641 insertions, 0 deletions
diff --git a/src/Services/DevHive.Services/Configurations/Mapping/CommentMappings.cs b/src/Services/DevHive.Services/Configurations/Mapping/CommentMappings.cs
new file mode 100644
index 0000000..5ca2a9e
--- /dev/null
+++ b/src/Services/DevHive.Services/Configurations/Mapping/CommentMappings.cs
@@ -0,0 +1,27 @@
+using DevHive.Data.Models;
+using AutoMapper;
+using DevHive.Services.Models.Comment;
+using DevHive.Common.Models.Misc;
+
+namespace DevHive.Services.Configurations.Mapping
+{
+ public class CommentMappings : Profile
+ {
+ public CommentMappings()
+ {
+ CreateMap<CreateCommentServiceModel, Comment>();
+ CreateMap<UpdateCommentServiceModel, Comment>()
+ .ForMember(dest => dest.Id, src => src.MapFrom(p => p.CommentId))
+ .ForMember(dest => dest.Message, src => src.MapFrom(p => p.NewMessage));
+
+ CreateMap<Comment, ReadCommentServiceModel>()
+ .ForMember(dest => dest.CommentId, src => src.MapFrom(p => p.Id))
+ .ForMember(dest => dest.IssuerFirstName, src => src.MapFrom(p => p.Creator.FirstName))
+ .ForMember(dest => dest.IssuerLastName, src => src.MapFrom(p => p.Creator.LastName))
+ .ForMember(dest => dest.IssuerUsername, src => src.MapFrom(p => p.Creator.UserName));
+
+ CreateMap<Comment, IdModel>()
+ .ForMember(dest => dest.Id, src => src.MapFrom(p => p.Id));
+ }
+ }
+}
diff --git a/src/Services/DevHive.Services/Configurations/Mapping/FeedMappings.cs b/src/Services/DevHive.Services/Configurations/Mapping/FeedMappings.cs
new file mode 100644
index 0000000..952e480
--- /dev/null
+++ b/src/Services/DevHive.Services/Configurations/Mapping/FeedMappings.cs
@@ -0,0 +1,11 @@
+using AutoMapper;
+
+namespace DevHive.Services.Configurations.Mapping
+{
+ public class FeedMappings : Profile
+ {
+ public FeedMappings()
+ {
+ }
+ }
+}
diff --git a/src/Services/DevHive.Services/Configurations/Mapping/LanguageMappings.cs b/src/Services/DevHive.Services/Configurations/Mapping/LanguageMappings.cs
new file mode 100644
index 0000000..9c572df
--- /dev/null
+++ b/src/Services/DevHive.Services/Configurations/Mapping/LanguageMappings.cs
@@ -0,0 +1,22 @@
+using DevHive.Data.Models;
+using AutoMapper;
+using DevHive.Services.Models.Language;
+
+namespace DevHive.Services.Configurations.Mapping
+{
+ public class LanguageMappings : Profile
+ {
+ public LanguageMappings()
+ {
+ CreateMap<LanguageServiceModel, Language>();
+ CreateMap<ReadLanguageServiceModel, Language>();
+ CreateMap<CreateLanguageServiceModel, Language>();
+ CreateMap<UpdateLanguageServiceModel, Language>();
+
+ CreateMap<Language, LanguageServiceModel>();
+ CreateMap<Language, ReadLanguageServiceModel>();
+ CreateMap<Language, CreateLanguageServiceModel>();
+ CreateMap<Language, UpdateLanguageServiceModel>();
+ }
+ }
+}
diff --git a/src/Services/DevHive.Services/Configurations/Mapping/PostMappings.cs b/src/Services/DevHive.Services/Configurations/Mapping/PostMappings.cs
new file mode 100644
index 0000000..9362f90
--- /dev/null
+++ b/src/Services/DevHive.Services/Configurations/Mapping/PostMappings.cs
@@ -0,0 +1,33 @@
+using DevHive.Data.Models;
+using AutoMapper;
+using DevHive.Services.Models.Post;
+using DevHive.Common.Models.Misc;
+using System.Collections.Generic;
+using DevHive.Services.Models;
+
+namespace DevHive.Services.Configurations.Mapping
+{
+ public class PostMappings : Profile
+ {
+ public PostMappings()
+ {
+ CreateMap<CreatePostServiceModel, Post>();
+ // .ForMember(dest => dest.Files, src => src.Ignore());
+ CreateMap<UpdatePostServiceModel, Post>()
+ .ForMember(dest => dest.Id, src => src.MapFrom(p => p.PostId))
+ // .ForMember(dest => dest.Files, src => src.Ignore())
+ .ForMember(dest => dest.Message, src => src.MapFrom(p => p.NewMessage));
+
+ CreateMap<Post, ReadPostServiceModel>()
+ .ForMember(dest => dest.PostId, src => src.MapFrom(p => p.Id))
+ .ForMember(dest => dest.CreatorFirstName, src => src.MapFrom(p => p.Creator.FirstName))
+ .ForMember(dest => dest.CreatorLastName, src => src.MapFrom(p => p.Creator.LastName))
+ .ForMember(dest => dest.CreatorUsername, src => src.MapFrom(p => p.Creator.UserName));
+
+ CreateMap<Post, IdModel>()
+ .ForMember(dest => dest.Id, src => src.MapFrom(x => x.Id));
+
+ CreateMap<List<Post>, ReadPageServiceModel>();
+ }
+ }
+}
diff --git a/src/Services/DevHive.Services/Configurations/Mapping/RatingMappings.cs b/src/Services/DevHive.Services/Configurations/Mapping/RatingMappings.cs
new file mode 100644
index 0000000..1dbb7b4
--- /dev/null
+++ b/src/Services/DevHive.Services/Configurations/Mapping/RatingMappings.cs
@@ -0,0 +1,15 @@
+using AutoMapper;
+using DevHive.Data.Models;
+using DevHive.Services.Models.Post.Rating;
+
+namespace DevHive.Services.Configurations.Mapping
+{
+ public class RatingMappings : Profile
+ {
+ public RatingMappings()
+ {
+ // CreateMap<Rating, ReadPostRatingServiceModel>()
+ // .ForMember(dest => dest.PostId, src => src.MapFrom(p => p.Post.Id));
+ }
+ }
+}
diff --git a/src/Services/DevHive.Services/Configurations/Mapping/RoleMapings.cs b/src/Services/DevHive.Services/Configurations/Mapping/RoleMapings.cs
new file mode 100644
index 0000000..e61a107
--- /dev/null
+++ b/src/Services/DevHive.Services/Configurations/Mapping/RoleMapings.cs
@@ -0,0 +1,19 @@
+using DevHive.Data.Models;
+using AutoMapper;
+using DevHive.Services.Models.Identity.Role;
+
+namespace DevHive.Services.Configurations.Mapping
+{
+ public class RoleMappings : Profile
+ {
+ public RoleMappings()
+ {
+ CreateMap<CreateRoleServiceModel, Role>();
+ CreateMap<RoleServiceModel, Role>();
+ CreateMap<UpdateRoleServiceModel, Role>();
+
+ CreateMap<Role, RoleServiceModel>();
+ CreateMap<Role, UpdateRoleServiceModel>();
+ }
+ }
+}
diff --git a/src/Services/DevHive.Services/Configurations/Mapping/TechnologyMappings.cs b/src/Services/DevHive.Services/Configurations/Mapping/TechnologyMappings.cs
new file mode 100644
index 0000000..85b57f1
--- /dev/null
+++ b/src/Services/DevHive.Services/Configurations/Mapping/TechnologyMappings.cs
@@ -0,0 +1,21 @@
+using DevHive.Data.Models;
+using AutoMapper;
+using DevHive.Services.Models.Technology;
+
+namespace DevHive.Services.Configurations.Mapping
+{
+ public class TechnologyMappings : Profile
+ {
+ public TechnologyMappings()
+ {
+ CreateMap<CreateTechnologyServiceModel, Technology>();
+ CreateMap<UpdateTechnologyServiceModel, Technology>();
+ CreateMap<TechnologyServiceModel, Technology>();
+
+ CreateMap<Technology, CreateTechnologyServiceModel>();
+ CreateMap<Technology, TechnologyServiceModel>();
+ CreateMap<Technology, ReadTechnologyServiceModel>();
+ CreateMap<Technology, UpdateTechnologyServiceModel>();
+ }
+ }
+}
diff --git a/src/Services/DevHive.Services/Configurations/Mapping/UserMappings.cs b/src/Services/DevHive.Services/Configurations/Mapping/UserMappings.cs
new file mode 100644
index 0000000..2b0f4ed
--- /dev/null
+++ b/src/Services/DevHive.Services/Configurations/Mapping/UserMappings.cs
@@ -0,0 +1,31 @@
+using DevHive.Data.Models;
+using AutoMapper;
+using DevHive.Services.Models.Identity.User;
+using DevHive.Common.Models.Misc;
+using DevHive.Data.RelationModels;
+
+namespace DevHive.Services.Configurations.Mapping
+{
+ public class UserMappings : Profile
+ {
+ public UserMappings()
+ {
+ CreateMap<UserServiceModel, User>();
+ CreateMap<RegisterServiceModel, User>();
+ CreateMap<FriendServiceModel, User>()
+ .ForMember(dest => dest.Friends, src => src.Ignore());
+ CreateMap<UpdateUserServiceModel, User>()
+ .ForMember(dest => dest.Friends, src => src.Ignore())
+ .AfterMap((src, dest) => dest.PasswordHash = PasswordModifications.GeneratePasswordHash(src.Password));
+ CreateMap<UpdateFriendServiceModel, User>();
+
+ CreateMap<User, UserServiceModel>()
+ .ForMember(dest => dest.ProfilePictureURL, src => src.MapFrom(p => p.ProfilePicture.PictureURL))
+ .ForMember(dest => dest.Friends, src => src.MapFrom(p => p.Friends));
+ CreateMap<User, UpdateUserServiceModel>()
+ .ForMember(x => x.Password, opt => opt.Ignore())
+ .ForMember(dest => dest.ProfilePictureURL, src => src.MapFrom(p => p.ProfilePicture.PictureURL));
+ CreateMap<User, FriendServiceModel>();
+ }
+ }
+}
diff --git a/src/Services/DevHive.Services/DevHive.Services.csproj b/src/Services/DevHive.Services/DevHive.Services.csproj
new file mode 100644
index 0000000..7a33166
--- /dev/null
+++ b/src/Services/DevHive.Services/DevHive.Services.csproj
@@ -0,0 +1,31 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <TargetFramework>net5.0</TargetFramework>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.1">
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+ <PrivateAssets>all</PrivateAssets>
+ </PackageReference>
+ <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.8.0" />
+ <ProjectReference Include="..\DevHive.Data\DevHive.Data.csproj" />
+
+ <PackageReference Include="AutoMapper" Version="10.1.1" />
+ <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.0" />
+
+ <PackageReference Include="CloudinaryDotNet" Version="1.14.0" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\DevHive.Common\DevHive.Common.csproj" />
+ <ProjectReference Include="..\DevHive.Services.Models\DevHive.Services.Models.csproj" />
+ <ProjectReference Include="..\..\Common\DevHive.Common.Models\DevHive.Common.csproj" />
+ </ItemGroup>
+
+ <PropertyGroup>
+ <EnableNETAnalyzers>true</EnableNETAnalyzers>
+ <AnalysisLevel>latest</AnalysisLevel>
+ </PropertyGroup>
+</Project>
diff --git a/src/Services/DevHive.Services/Interfaces/ICloudService.cs b/src/Services/DevHive.Services/Interfaces/ICloudService.cs
new file mode 100644
index 0000000..3ae7a24
--- /dev/null
+++ b/src/Services/DevHive.Services/Interfaces/ICloudService.cs
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+
+namespace DevHive.Services.Interfaces
+{
+ public interface ICloudService
+ {
+ Task<List<string>> UploadFilesToCloud(List<IFormFile> formFiles);
+
+ // Task<List<FileContentResult>> GetFilesFromCloud(List<string> fileUrls);
+
+ Task<bool> RemoveFilesFromCloud(List<string> fileUrls);
+ }
+}
diff --git a/src/Services/DevHive.Services/Interfaces/ICommentService.cs b/src/Services/DevHive.Services/Interfaces/ICommentService.cs
new file mode 100644
index 0000000..e7409a8
--- /dev/null
+++ b/src/Services/DevHive.Services/Interfaces/ICommentService.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Threading.Tasks;
+using DevHive.Services.Models.Comment;
+
+namespace DevHive.Services.Interfaces
+{
+ public interface ICommentService
+ {
+ Task<Guid> AddComment(CreateCommentServiceModel createPostServiceModel);
+
+ Task<ReadCommentServiceModel> GetCommentById(Guid id);
+
+ Task<Guid> UpdateComment(UpdateCommentServiceModel updateCommentServiceModel);
+
+ Task<bool> DeleteComment(Guid id);
+
+ Task<bool> ValidateJwtForCreating(Guid userId, string rawTokenData);
+ Task<bool> ValidateJwtForComment(Guid commentId, string rawTokenData);
+ }
+}
diff --git a/src/Services/DevHive.Services/Interfaces/IFeedService.cs b/src/Services/DevHive.Services/Interfaces/IFeedService.cs
new file mode 100644
index 0000000..b507b3b
--- /dev/null
+++ b/src/Services/DevHive.Services/Interfaces/IFeedService.cs
@@ -0,0 +1,11 @@
+using System.Threading.Tasks;
+using DevHive.Services.Models;
+
+namespace DevHive.Services.Interfaces
+{
+ public interface IFeedService
+ {
+ Task<ReadPageServiceModel> GetPage(GetPageServiceModel getPageServiceModel);
+ Task<ReadPageServiceModel> GetUserPage(GetPageServiceModel model);
+ }
+}
diff --git a/src/Services/DevHive.Services/Interfaces/ILanguageService.cs b/src/Services/DevHive.Services/Interfaces/ILanguageService.cs
new file mode 100644
index 0000000..fabbec2
--- /dev/null
+++ b/src/Services/DevHive.Services/Interfaces/ILanguageService.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using DevHive.Services.Models.Language;
+
+namespace DevHive.Services.Interfaces
+{
+ public interface ILanguageService
+ {
+ Task<Guid> CreateLanguage(CreateLanguageServiceModel createLanguageServiceModel);
+
+ Task<ReadLanguageServiceModel> GetLanguageById(Guid id);
+ HashSet<ReadLanguageServiceModel> GetLanguages();
+
+ Task<bool> UpdateLanguage(UpdateLanguageServiceModel languageServiceModel);
+
+ Task<bool> DeleteLanguage(Guid id);
+ }
+}
diff --git a/src/Services/DevHive.Services/Interfaces/IPostService.cs b/src/Services/DevHive.Services/Interfaces/IPostService.cs
new file mode 100644
index 0000000..d35acfd
--- /dev/null
+++ b/src/Services/DevHive.Services/Interfaces/IPostService.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Threading.Tasks;
+using DevHive.Services.Models.Post;
+
+namespace DevHive.Services.Interfaces
+{
+ public interface IPostService
+ {
+ Task<Guid> CreatePost(CreatePostServiceModel createPostServiceModel);
+
+ Task<ReadPostServiceModel> GetPostById(Guid id);
+
+ Task<Guid> UpdatePost(UpdatePostServiceModel updatePostServiceModel);
+
+ Task<bool> DeletePost(Guid id);
+
+ Task<bool> ValidateJwtForCreating(Guid userId, string rawTokenData);
+ Task<bool> ValidateJwtForPost(Guid postId, string rawTokenData);
+ }
+}
diff --git a/src/Services/DevHive.Services/Interfaces/IRateService.cs b/src/Services/DevHive.Services/Interfaces/IRateService.cs
new file mode 100644
index 0000000..359ef55
--- /dev/null
+++ b/src/Services/DevHive.Services/Interfaces/IRateService.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Threading.Tasks;
+using DevHive.Data.Models;
+using DevHive.Services.Models.Post.Rating;
+
+namespace DevHive.Services.Interfaces
+{
+ public interface IRateService
+ {
+ Task<ReadPostRatingServiceModel> RatePost(RatePostServiceModel ratePostServiceModel);
+
+ bool HasUserRatedThisPost(User user, Post post);
+ }
+}
diff --git a/src/Services/DevHive.Services/Interfaces/IRoleService.cs b/src/Services/DevHive.Services/Interfaces/IRoleService.cs
new file mode 100644
index 0000000..d47728c
--- /dev/null
+++ b/src/Services/DevHive.Services/Interfaces/IRoleService.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Threading.Tasks;
+using DevHive.Services.Models.Identity.Role;
+
+namespace DevHive.Services.Interfaces
+{
+ public interface IRoleService
+ {
+ Task<Guid> CreateRole(CreateRoleServiceModel roleServiceModel);
+
+ Task<RoleServiceModel> GetRoleById(Guid id);
+
+ Task<bool> UpdateRole(UpdateRoleServiceModel roleServiceModel);
+
+ Task<bool> DeleteRole(Guid id);
+ }
+}
diff --git a/src/Services/DevHive.Services/Interfaces/ITechnologyService.cs b/src/Services/DevHive.Services/Interfaces/ITechnologyService.cs
new file mode 100644
index 0000000..8f9510c
--- /dev/null
+++ b/src/Services/DevHive.Services/Interfaces/ITechnologyService.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using DevHive.Services.Models.Technology;
+
+namespace DevHive.Services.Interfaces
+{
+ public interface ITechnologyService
+ {
+ Task<Guid> CreateTechnology(CreateTechnologyServiceModel technologyServiceModel);
+
+ Task<ReadTechnologyServiceModel> GetTechnologyById(Guid id);
+ HashSet<ReadTechnologyServiceModel> GetTechnologies();
+
+ Task<bool> UpdateTechnology(UpdateTechnologyServiceModel updateTechnologyServiceModel);
+
+ Task<bool> DeleteTechnology(Guid id);
+ }
+}
diff --git a/src/Services/DevHive.Services/Interfaces/IUserService.cs b/src/Services/DevHive.Services/Interfaces/IUserService.cs
new file mode 100644
index 0000000..9e2b4e3
--- /dev/null
+++ b/src/Services/DevHive.Services/Interfaces/IUserService.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Threading.Tasks;
+using DevHive.Common.Models.Identity;
+using DevHive.Services.Models.Identity.User;
+
+namespace DevHive.Services.Interfaces
+{
+ public interface IUserService
+ {
+ Task<TokenModel> LoginUser(LoginServiceModel loginModel);
+ Task<TokenModel> RegisterUser(RegisterServiceModel registerModel);
+
+ Task<UserServiceModel> GetUserByUsername(string username);
+ Task<UserServiceModel> GetUserById(Guid id);
+
+ Task<UserServiceModel> UpdateUser(UpdateUserServiceModel updateModel);
+ Task<ProfilePictureServiceModel> UpdateProfilePicture(UpdateProfilePictureServiceModel updateProfilePictureServiceModel);
+
+ Task<bool> DeleteUser(Guid id);
+
+ Task<bool> ValidJWT(Guid id, string rawTokenData);
+
+ Task<TokenModel> SuperSecretPromotionToAdmin(Guid userId);
+ }
+}
diff --git a/src/Services/DevHive.Services/Options/JWTOptions.cs b/src/Services/DevHive.Services/Options/JWTOptions.cs
new file mode 100644
index 0000000..95458f5
--- /dev/null
+++ b/src/Services/DevHive.Services/Options/JWTOptions.cs
@@ -0,0 +1,14 @@
+using Microsoft.Extensions.Options;
+
+namespace DevHive.Services.Options
+{
+ public class JWTOptions
+ {
+ public JWTOptions(string secret)
+ {
+ this.Secret = secret;
+ }
+
+ public string Secret { get; init; }
+ }
+}
diff --git a/src/Services/DevHive.Services/Services/CloudinaryService.cs b/src/Services/DevHive.Services/Services/CloudinaryService.cs
new file mode 100644
index 0000000..57955a2
--- /dev/null
+++ b/src/Services/DevHive.Services/Services/CloudinaryService.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using CloudinaryDotNet;
+using CloudinaryDotNet.Actions;
+using DevHive.Services.Interfaces;
+using Microsoft.AspNetCore.Http;
+
+namespace DevHive.Services.Services
+{
+ public class CloudinaryService : ICloudService
+ {
+ private readonly Cloudinary _cloudinary;
+
+ public CloudinaryService(string cloudName, string apiKey, string apiSecret)
+ {
+ this._cloudinary = new Cloudinary(new Account(cloudName, apiKey, apiSecret));
+ }
+
+ public async Task<List<string>> UploadFilesToCloud(List<IFormFile> formFiles)
+ {
+ List<string> fileUrls = new();
+ foreach (var formFile in formFiles)
+ {
+ string formFileId = Guid.NewGuid().ToString();
+
+ using (var ms = new MemoryStream())
+ {
+ formFile.CopyTo(ms);
+ byte[] formBytes = ms.ToArray();
+
+ RawUploadParams rawUploadParams = new()
+ {
+ File = new FileDescription(formFileId, new MemoryStream(formBytes)),
+ PublicId = formFileId,
+ UseFilename = true
+ };
+
+ RawUploadResult rawUploadResult = await this._cloudinary.UploadAsync(rawUploadParams);
+ fileUrls.Add(rawUploadResult.Url.AbsoluteUri);
+ }
+ }
+
+ return fileUrls;
+ }
+
+ public async Task<bool> RemoveFilesFromCloud(List<string> fileUrls)
+ {
+ return true;
+ }
+ }
+}
diff --git a/src/Services/DevHive.Services/Services/CommentService.cs b/src/Services/DevHive.Services/Services/CommentService.cs
new file mode 100644
index 0000000..e2b54c4
--- /dev/null
+++ b/src/Services/DevHive.Services/Services/CommentService.cs
@@ -0,0 +1,169 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using AutoMapper;
+using DevHive.Data.Models;
+using DevHive.Services.Models.Comment;
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using DevHive.Services.Interfaces;
+using DevHive.Data.Interfaces.Repositories;
+using System.Linq;
+
+namespace DevHive.Services.Services
+{
+ public class CommentService : ICommentService
+ {
+ private readonly IUserRepository _userRepository;
+ private readonly IPostRepository _postRepository;
+ private readonly ICommentRepository _commentRepository;
+ private readonly IMapper _postMapper;
+
+ public CommentService(IUserRepository userRepository, IPostRepository postRepository, ICommentRepository commentRepository, IMapper postMapper)
+ {
+ this._userRepository = userRepository;
+ this._postRepository = postRepository;
+ this._commentRepository = commentRepository;
+ this._postMapper = postMapper;
+ }
+
+ #region Create
+ public async Task<Guid> AddComment(CreateCommentServiceModel createCommentServiceModel)
+ {
+ if (!await this._postRepository.DoesPostExist(createCommentServiceModel.PostId))
+ throw new ArgumentException("Post does not exist!");
+
+ Comment comment = this._postMapper.Map<Comment>(createCommentServiceModel);
+ comment.TimeCreated = DateTime.Now;
+
+ comment.Creator = await this._userRepository.GetByIdAsync(createCommentServiceModel.CreatorId);
+ comment.Post = await this._postRepository.GetByIdAsync(createCommentServiceModel.PostId);
+
+ bool success = await this._commentRepository.AddAsync(comment);
+ if (success)
+ {
+ Comment newComment = await this._commentRepository
+ .GetCommentByIssuerAndTimeCreatedAsync(comment.Creator.Id, comment.TimeCreated);
+
+ return newComment.Id;
+ }
+ else
+ return Guid.Empty;
+ }
+ #endregion
+
+ #region Read
+ public async Task<ReadCommentServiceModel> GetCommentById(Guid id)
+ {
+ Comment comment = await this._commentRepository.GetByIdAsync(id) ??
+ throw new ArgumentException("The comment does not exist");
+
+ User user = await this._userRepository.GetByIdAsync(comment.Creator.Id) ??
+ throw new ArgumentException("The user does not exist");
+
+ ReadCommentServiceModel readCommentServiceModel = this._postMapper.Map<ReadCommentServiceModel>(comment);
+ readCommentServiceModel.IssuerFirstName = user.FirstName;
+ readCommentServiceModel.IssuerLastName = user.LastName;
+ readCommentServiceModel.IssuerUsername = user.UserName;
+
+ return readCommentServiceModel;
+ }
+ #endregion
+
+ #region Update
+ public async Task<Guid> UpdateComment(UpdateCommentServiceModel updateCommentServiceModel)
+ {
+ if (!await this._commentRepository.DoesCommentExist(updateCommentServiceModel.CommentId))
+ throw new ArgumentException("Comment does not exist!");
+
+ Comment comment = this._postMapper.Map<Comment>(updateCommentServiceModel);
+ comment.TimeCreated = DateTime.Now;
+
+ comment.Creator = await this._userRepository.GetByIdAsync(updateCommentServiceModel.CreatorId);
+ comment.Post = await this._postRepository.GetByIdAsync(updateCommentServiceModel.PostId);
+
+ bool result = await this._commentRepository.EditAsync(updateCommentServiceModel.CommentId, comment);
+
+ if (result)
+ return (await this._commentRepository.GetByIdAsync(updateCommentServiceModel.CommentId)).Id;
+ else
+ return Guid.Empty;
+ }
+ #endregion
+
+ #region Delete
+ public async Task<bool> DeleteComment(Guid id)
+ {
+ if (!await this._commentRepository.DoesCommentExist(id))
+ throw new ArgumentException("Comment does not exist!");
+
+ Comment comment = await this._commentRepository.GetByIdAsync(id);
+ return await this._commentRepository.DeleteAsync(comment);
+ }
+ #endregion
+
+ #region Validations
+ /// <summary>
+ /// Checks whether the user Id in the token and the given user Id match
+ /// </summary>
+ public async Task<bool> ValidateJwtForCreating(Guid userId, string rawTokenData)
+ {
+ User user = await this.GetUserForValidation(rawTokenData);
+
+ return user.Id == userId;
+ }
+
+ /// <summary>
+ /// Checks whether the comment, gotten with the commentId,
+ /// is made by the user in the token
+ /// or if the user in the token is an admin
+ /// </summary>
+ public async Task<bool> ValidateJwtForComment(Guid commentId, string rawTokenData)
+ {
+ Comment comment = await this._commentRepository.GetByIdAsync(commentId) ??
+ throw new ArgumentException("Comment does not exist!");
+ User user = await this.GetUserForValidation(rawTokenData);
+
+ //If user made the comment
+ if (comment.Creator.Id == user.Id)
+ return true;
+ //If user is admin
+ else if (user.Roles.Any(x => x.Name == Role.AdminRole))
+ return true;
+ else
+ return false;
+ }
+
+ /// <summary>
+ /// Returns the user, via their Id in the token
+ /// </summary>
+ private async Task<User> GetUserForValidation(string rawTokenData)
+ {
+ JwtSecurityToken jwt = new JwtSecurityTokenHandler().ReadJwtToken(rawTokenData.Remove(0, 7));
+
+ Guid jwtUserId = Guid.Parse(this.GetClaimTypeValues("ID", jwt.Claims).First());
+ //HashSet<string> jwtRoleNames = this.GetClaimTypeValues("role", jwt.Claims);
+
+ User user = await this._userRepository.GetByIdAsync(jwtUserId) ??
+ throw new ArgumentException("User does not exist!");
+
+ return user;
+ }
+
+ /// <summary>
+ /// Returns all values from a given claim type
+ /// </summary>
+ private List<string> GetClaimTypeValues(string type, IEnumerable<Claim> claims)
+ {
+ List<string> toReturn = new();
+
+ foreach (var claim in claims)
+ if (claim.Type == type)
+ toReturn.Add(claim.Value);
+
+ return toReturn;
+ }
+ #endregion
+ }
+}
+
diff --git a/src/Services/DevHive.Services/Services/FeedService.cs b/src/Services/DevHive.Services/Services/FeedService.cs
new file mode 100644
index 0000000..f2f4a3a
--- /dev/null
+++ b/src/Services/DevHive.Services/Services/FeedService.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using AutoMapper;
+using DevHive.Data.Interfaces.Repositories;
+using DevHive.Data.Models;
+using DevHive.Services.Interfaces;
+using DevHive.Services.Models;
+using DevHive.Services.Models.Post;
+
+namespace DevHive.Services.Services
+{
+ public class FeedService : IFeedService
+ {
+ private readonly IMapper _mapper;
+ private readonly IFeedRepository _feedRepository;
+ private readonly IUserRepository _userRepository;
+
+ public FeedService(IFeedRepository feedRepository, IUserRepository userRepository, IMapper mapper)
+ {
+ this._feedRepository = feedRepository;
+ this._userRepository = userRepository;
+ this._mapper = mapper;
+ }
+
+ /// <summary>
+ /// This method is used in the feed page.
+ /// See the FeedRepository "GetFriendsPosts" menthod for more information on how it works.
+ /// </summary>
+ public async Task<ReadPageServiceModel> GetPage(GetPageServiceModel model)
+ {
+ User user = null;
+
+ if (model.UserId != Guid.Empty)
+ user = await this._userRepository.GetByIdAsync(model.UserId);
+ else if (!string.IsNullOrEmpty(model.Username))
+ user = await this._userRepository.GetByUsernameAsync(model.Username);
+ else
+ throw new ArgumentException("Invalid given data!");
+
+ if (user == null)
+ throw new ArgumentException("User doesn't exist!");
+
+ if (user.Friends.Count == 0)
+ throw new ArgumentException("User has no friends to get feed from!");
+
+ List<Post> posts = await this._feedRepository
+ .GetFriendsPosts(user.Friends.ToList(), model.FirstRequestIssued, model.PageNumber, model.PageSize);
+
+ ReadPageServiceModel readPageServiceModel = new();
+ foreach (Post post in posts)
+ readPageServiceModel.Posts.Add(this._mapper.Map<ReadPostServiceModel>(post));
+
+ return readPageServiceModel;
+ }
+
+ /// <summary>
+ /// This method is used in the profile pages.
+ /// See the FeedRepository "GetUsersPosts" menthod for more information on how it works.
+ /// </summary>
+ public async Task<ReadPageServiceModel> GetUserPage(GetPageServiceModel model)
+ {
+ User user = null;
+
+ if (!string.IsNullOrEmpty(model.Username))
+ user = await this._userRepository.GetByUsernameAsync(model.Username);
+ else
+ throw new ArgumentException("Invalid given data!");
+
+ if (user == null)
+ throw new ArgumentException("User doesn't exist!");
+
+ List<Post> posts = await this._feedRepository
+ .GetUsersPosts(user, model.FirstRequestIssued, model.PageNumber, model.PageSize);
+
+ ReadPageServiceModel readPageServiceModel = new();
+ foreach (Post post in posts)
+ readPageServiceModel.Posts.Add(this._mapper.Map<ReadPostServiceModel>(post));
+
+ return readPageServiceModel;
+ }
+ }
+}
diff --git a/src/Services/DevHive.Services/Services/LanguageService.cs b/src/Services/DevHive.Services/Services/LanguageService.cs
new file mode 100644
index 0000000..a6364d8
--- /dev/null
+++ b/src/Services/DevHive.Services/Services/LanguageService.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using AutoMapper;
+using DevHive.Data.Interfaces.Repositories;
+using DevHive.Data.Models;
+using DevHive.Services.Interfaces;
+using DevHive.Services.Models.Language;
+
+namespace DevHive.Services.Services
+{
+ public class LanguageService : ILanguageService
+ {
+ private readonly ILanguageRepository _languageRepository;
+ private readonly IMapper _languageMapper;
+
+ public LanguageService(ILanguageRepository languageRepository, IMapper mapper)
+ {
+ this._languageRepository = languageRepository;
+ this._languageMapper = mapper;
+ }
+
+ #region Create
+ public async Task<Guid> CreateLanguage(CreateLanguageServiceModel createLanguageServiceModel)
+ {
+ if (await this._languageRepository.DoesLanguageNameExistAsync(createLanguageServiceModel.Name))
+ throw new ArgumentException("Language already exists!");
+
+ Language language = this._languageMapper.Map<Language>(createLanguageServiceModel);
+ bool success = await this._languageRepository.AddAsync(language);
+
+ if (success)
+ {
+ Language newLanguage = await this._languageRepository.GetByNameAsync(createLanguageServiceModel.Name);
+ return newLanguage.Id;
+ }
+ else
+ return Guid.Empty;
+ }
+ #endregion
+
+ #region Read
+ public async Task<ReadLanguageServiceModel> GetLanguageById(Guid id)
+ {
+ Language language = await this._languageRepository.GetByIdAsync(id);
+
+ if (language == null)
+ throw new ArgumentException("The language does not exist");
+
+ return this._languageMapper.Map<ReadLanguageServiceModel>(language);
+ }
+
+ public HashSet<ReadLanguageServiceModel> GetLanguages()
+ {
+ HashSet<Language> languages = this._languageRepository.GetLanguages();
+
+ return this._languageMapper.Map<HashSet<ReadLanguageServiceModel>>(languages);
+ }
+ #endregion
+
+ #region Update
+ public async Task<bool> UpdateLanguage(UpdateLanguageServiceModel languageServiceModel)
+ {
+ bool langExists = await this._languageRepository.DoesLanguageExistAsync(languageServiceModel.Id);
+ bool newLangNameExists = await this._languageRepository.DoesLanguageNameExistAsync(languageServiceModel.Name);
+
+ if (!langExists)
+ throw new ArgumentException("Language does not exist!");
+
+ if (newLangNameExists)
+ throw new ArgumentException("Language name already exists in our data base!");
+
+ Language lang = this._languageMapper.Map<Language>(languageServiceModel);
+ return await this._languageRepository.EditAsync(languageServiceModel.Id, lang);
+ }
+ #endregion
+
+ #region Delete
+ public async Task<bool> DeleteLanguage(Guid id)
+ {
+ if (!await this._languageRepository.DoesLanguageExistAsync(id))
+ throw new ArgumentException("Language does not exist!");
+
+ Language language = await this._languageRepository.GetByIdAsync(id);
+ return await this._languageRepository.DeleteAsync(language);
+ }
+ #endregion
+ }
+}
diff --git a/src/Services/DevHive.Services/Services/PostService.cs b/src/Services/DevHive.Services/Services/PostService.cs
new file mode 100644
index 0000000..fe91f23
--- /dev/null
+++ b/src/Services/DevHive.Services/Services/PostService.cs
@@ -0,0 +1,239 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using AutoMapper;
+using DevHive.Data.Models;
+using DevHive.Services.Models.Post;
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using DevHive.Services.Interfaces;
+using DevHive.Data.Interfaces.Repositories;
+using System.Linq;
+using DevHive.Data.RelationModels;
+
+namespace DevHive.Services.Services
+{
+ public class PostService : IPostService
+ {
+ private readonly ICloudService _cloudService;
+ private readonly IUserRepository _userRepository;
+ private readonly IPostRepository _postRepository;
+ private readonly ICommentRepository _commentRepository;
+ private readonly IMapper _postMapper;
+
+ public PostService(ICloudService cloudService, IUserRepository userRepository, IPostRepository postRepository, ICommentRepository commentRepository, IMapper postMapper)
+ {
+ this._cloudService = cloudService;
+ this._userRepository = userRepository;
+ this._postRepository = postRepository;
+ this._commentRepository = commentRepository;
+ this._postMapper = postMapper;
+ }
+
+ #region Create
+ public async Task<Guid> CreatePost(CreatePostServiceModel createPostServiceModel)
+ {
+ if (!await this._userRepository.DoesUserExistAsync(createPostServiceModel.CreatorId))
+ throw new ArgumentException("User does not exist!");
+
+ Post post = this._postMapper.Map<Post>(createPostServiceModel);
+
+ if (createPostServiceModel.Files.Count != 0)
+ {
+ List<string> fileUrls = await _cloudService.UploadFilesToCloud(createPostServiceModel.Files);
+ post.Attachments = this.GetPostAttachmentsFromUrls(post, fileUrls);
+ }
+
+ post.Creator = await this._userRepository.GetByIdAsync(createPostServiceModel.CreatorId);
+ post.TimeCreated = DateTime.Now;
+
+ bool success = await this._postRepository.AddAsync(post);
+ if (success)
+ {
+ Post newPost = await this._postRepository
+ .GetPostByCreatorAndTimeCreatedAsync(post.Creator.Id, post.TimeCreated);
+
+ await this._postRepository.AddNewPostToCreator(createPostServiceModel.CreatorId, newPost);
+
+ return newPost.Id;
+ }
+ else
+ return Guid.Empty;
+ }
+ #endregion
+
+ #region Read
+ public async Task<ReadPostServiceModel> GetPostById(Guid id)
+ {
+ Post post = await this._postRepository.GetByIdAsync(id) ??
+ throw new ArgumentException("The post does not exist!");
+
+ // This can't happen in repo, because of how time is usually compared
+ post.Comments = post.Comments
+ .OrderByDescending(x => x.TimeCreated.ToFileTimeUtc())
+ .ToList();
+
+ User user = await this._userRepository.GetByIdAsync(post.Creator.Id) ??
+ throw new ArgumentException("The user does not exist!");
+
+ ReadPostServiceModel readPostServiceModel = this._postMapper.Map<ReadPostServiceModel>(post);
+ readPostServiceModel.CreatorFirstName = user.FirstName;
+ readPostServiceModel.CreatorLastName = user.LastName;
+ readPostServiceModel.CreatorUsername = user.UserName;
+ readPostServiceModel.FileUrls = post.Attachments.Select(x => x.FileUrl).ToList();
+
+ return readPostServiceModel;
+ }
+ #endregion
+
+ #region Update
+ public async Task<Guid> UpdatePost(UpdatePostServiceModel updatePostServiceModel)
+ {
+ if (!await this._postRepository.DoesPostExist(updatePostServiceModel.PostId))
+ throw new ArgumentException("Post does not exist!");
+
+ Post post = this._postMapper.Map<Post>(updatePostServiceModel);
+
+ if (updatePostServiceModel.Files.Count != 0)
+ {
+ if (await this._postRepository.DoesPostHaveFiles(updatePostServiceModel.PostId))
+ {
+ List<string> fileUrlsToRemove = await this._postRepository.GetFileUrls(updatePostServiceModel.PostId);
+ bool success = await _cloudService.RemoveFilesFromCloud(fileUrlsToRemove);
+ if (!success)
+ throw new InvalidCastException("Could not delete files from the post!");
+ }
+
+ List<string> fileUrls = await _cloudService.UploadFilesToCloud(updatePostServiceModel.Files) ??
+ throw new ArgumentNullException("Unable to upload images to cloud");
+ post.Attachments = this.GetPostAttachmentsFromUrls(post, fileUrls);
+ }
+
+ post.Creator = await this._userRepository.GetByIdAsync(updatePostServiceModel.CreatorId);
+ post.Comments = await this._commentRepository.GetPostComments(updatePostServiceModel.PostId);
+ post.TimeCreated = DateTime.Now;
+
+ bool result = await this._postRepository.EditAsync(updatePostServiceModel.PostId, post);
+
+ if (result)
+ return (await this._postRepository.GetByIdAsync(updatePostServiceModel.PostId)).Id;
+ else
+ return Guid.Empty;
+ }
+ #endregion
+
+ #region Delete
+ public async Task<bool> DeletePost(Guid id)
+ {
+ if (!await this._postRepository.DoesPostExist(id))
+ throw new ArgumentException("Post does not exist!");
+
+ Post post = await this._postRepository.GetByIdAsync(id);
+
+ if (await this._postRepository.DoesPostHaveFiles(id))
+ {
+ List<string> fileUrls = await this._postRepository.GetFileUrls(id);
+ bool success = await _cloudService.RemoveFilesFromCloud(fileUrls);
+ if (!success)
+ throw new InvalidCastException("Could not delete files from the post. Please try again");
+ }
+
+ return await this._postRepository.DeleteAsync(post);
+ }
+ #endregion
+
+ #region Validations
+ /// <summary>
+ /// Checks whether the user Id in the token and the given user Id match
+ /// </summary>
+ public async Task<bool> ValidateJwtForCreating(Guid userId, string rawTokenData)
+ {
+ User user = await this.GetUserForValidation(rawTokenData);
+
+ return user.Id == userId;
+ }
+
+ /// <summary>
+ /// Checks whether the post, gotten with the postId,
+ /// is made by the user in the token
+ /// or if the user in the token is an admin
+ /// </summary>
+ public async Task<bool> ValidateJwtForPost(Guid postId, string rawTokenData)
+ {
+ Post post = await this._postRepository.GetByIdAsync(postId) ??
+ throw new ArgumentException("Post does not exist!");
+ User user = await this.GetUserForValidation(rawTokenData);
+
+ //If user made the post
+ if (post.Creator.Id == user.Id)
+ return true;
+ //If user is admin
+ else if (user.Roles.Any(x => x.Name == Role.AdminRole))
+ return true;
+ else
+ return false;
+ }
+
+ /// <summary>
+ /// Checks whether the comment, gotten with the commentId,
+ /// is made by the user in the token
+ /// or if the user in the token is an admin
+ /// </summary>
+ public async Task<bool> ValidateJwtForComment(Guid commentId, string rawTokenData)
+ {
+ Comment comment = await this._commentRepository.GetByIdAsync(commentId) ??
+ throw new ArgumentException("Comment does not exist!");
+ User user = await this.GetUserForValidation(rawTokenData);
+
+ //If user made the comment
+ if (comment.Creator.Id == user.Id)
+ return true;
+ //If user is admin
+ else if (user.Roles.Any(x => x.Name == Role.AdminRole))
+ return true;
+ else
+ return false;
+ }
+
+ /// <summary>
+ /// Returns the user, via their Id in the token
+ /// </summary>
+ private async Task<User> GetUserForValidation(string rawTokenData)
+ {
+ JwtSecurityToken jwt = new JwtSecurityTokenHandler().ReadJwtToken(rawTokenData.Remove(0, 7));
+
+ Guid jwtUserId = Guid.Parse(this.GetClaimTypeValues("ID", jwt.Claims).First());
+ //HashSet<string> jwtRoleNames = this.GetClaimTypeValues("role", jwt.Claims);
+
+ User user = await this._userRepository.GetByIdAsync(jwtUserId) ??
+ throw new ArgumentException("User does not exist!");
+
+ return user;
+ }
+
+ /// <summary>
+ /// Returns all values from a given claim type
+ /// </summary>
+ private List<string> GetClaimTypeValues(string type, IEnumerable<Claim> claims)
+ {
+ List<string> toReturn = new();
+
+ foreach (var claim in claims)
+ if (claim.Type == type)
+ toReturn.Add(claim.Value);
+
+ return toReturn;
+ }
+ #endregion
+
+ #region Misc
+ private List<PostAttachments> GetPostAttachmentsFromUrls(Post post, List<string> fileUrls)
+ {
+ List<PostAttachments> postAttachments = new List<PostAttachments>();
+ foreach (string url in fileUrls)
+ postAttachments.Add(new PostAttachments { Post = post, FileUrl = url });
+ return postAttachments;
+ }
+ #endregion
+ }
+}
diff --git a/src/Services/DevHive.Services/Services/RateService.cs b/src/Services/DevHive.Services/Services/RateService.cs
new file mode 100644
index 0000000..204c550
--- /dev/null
+++ b/src/Services/DevHive.Services/Services/RateService.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using AutoMapper;
+using DevHive.Data.Interfaces.Repositories;
+using DevHive.Data.Models;
+using DevHive.Services.Interfaces;
+using DevHive.Services.Models.Post.Rating;
+
+namespace DevHive.Services.Services
+{
+ public class RateService : IRateService
+ {
+ private readonly IPostRepository _postRepository;
+ private readonly IUserRepository _userRepository;
+ private readonly IRatingRepository _ratingRepository;
+ private readonly IMapper _mapper;
+
+ public RateService(IPostRepository postRepository, IRatingRepository ratingRepository, IUserRepository userRepository, IMapper mapper)
+ {
+ this._postRepository = postRepository;
+ this._ratingRepository = ratingRepository;
+ this._userRepository = userRepository;
+ this._mapper = mapper;
+ }
+
+ public async Task<ReadPostRatingServiceModel> RatePost(RatePostServiceModel ratePostServiceModel)
+ {
+ throw new NotImplementedException();
+ // if (!await this._postRepository.DoesPostExist(ratePostServiceModel.PostId))
+ // throw new ArgumentException("Post does not exist!");
+
+ // if (!await this._userRepository.DoesUserExistAsync(ratePostServiceModel.UserId))
+ // throw new ArgumentException("User does not exist!");
+
+ // Post post = await this._postRepository.GetByIdAsync(ratePostServiceModel.PostId);
+ // User user = await this._userRepository.GetByIdAsync(ratePostServiceModel.UserId);
+
+ // if (this.HasUserRatedThisPost(user, post))
+ // throw new ArgumentException("You can't rate the same post more then one(duh, amigo)");
+
+ // this.Rate(user, post, ratePostServiceModel.Liked);
+
+ // bool success = await this._ratingRepository.EditAsync(post.Rating.Id, post.Rating);
+ // if (!success)
+ // throw new InvalidOperationException("Unable to rate the post!");
+
+ // Rating newRating = await this._ratingRepository.GetByIdAsync(post.Rating.Id);
+ // return this._mapper.Map<ReadPostRatingServiceModel>(newRating);
+ }
+
+ public async Task<ReadPostRatingServiceModel> RemoveUserRateFromPost(Guid userId, Guid postId)
+ {
+ throw new NotImplementedException();
+ // Post post = await this._postRepository.GetByIdAsync(postId);
+ // User user = await this._userRepository.GetByIdAsync(userId);
+
+ // if (!this.HasUserRatedThisPost(user, post))
+ // throw new ArgumentException("You haven't rated this post, lmao!");
+ }
+
+ public bool HasUserRatedThisPost(User user, Post post)
+ {
+ throw new NotImplementedException();
+ // return post.Rating.UsersThatRated
+ // .Any(x => x.Id == user.Id);
+ }
+
+ private void Rate(User user, Post post, bool liked)
+ {
+ throw new NotImplementedException();
+ // if (liked)
+ // post.Rating.Rate++;
+ // else
+ // post.Rating.Rate--;
+
+ // post.Rating.UsersThatRated.Add(user);
+ }
+ }
+}
diff --git a/src/Services/DevHive.Services/Services/RoleService.cs b/src/Services/DevHive.Services/Services/RoleService.cs
new file mode 100644
index 0000000..a8b8e17
--- /dev/null
+++ b/src/Services/DevHive.Services/Services/RoleService.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Threading.Tasks;
+using AutoMapper;
+using DevHive.Data.Interfaces.Repositories;
+using DevHive.Data.Models;
+using DevHive.Services.Interfaces;
+using DevHive.Services.Models.Identity.Role;
+using DevHive.Services.Models.Language;
+
+namespace DevHive.Services.Services
+{
+ public class RoleService : IRoleService
+ {
+ private readonly IRoleRepository _roleRepository;
+ private readonly IMapper _roleMapper;
+
+ public RoleService(IRoleRepository roleRepository, IMapper mapper)
+ {
+ this._roleRepository = roleRepository;
+ this._roleMapper = mapper;
+ }
+
+ public async Task<Guid> CreateRole(CreateRoleServiceModel roleServiceModel)
+ {
+ if (await this._roleRepository.DoesNameExist(roleServiceModel.Name))
+ throw new ArgumentException("Role already exists!");
+
+ Role role = this._roleMapper.Map<Role>(roleServiceModel);
+ bool success = await this._roleRepository.AddAsync(role);
+
+ if (success)
+ {
+ Role newRole = await this._roleRepository.GetByNameAsync(roleServiceModel.Name);
+ return newRole.Id;
+ }
+ else
+ return Guid.Empty;
+
+ }
+
+ public async Task<RoleServiceModel> GetRoleById(Guid id)
+ {
+ Role role = await this._roleRepository.GetByIdAsync(id)
+ ?? throw new ArgumentException("Role does not exist!");
+
+ return this._roleMapper.Map<RoleServiceModel>(role);
+ }
+
+ public async Task<bool> UpdateRole(UpdateRoleServiceModel updateRoleServiceModel)
+ {
+ if (!await this._roleRepository.DoesRoleExist(updateRoleServiceModel.Id))
+ throw new ArgumentException("Role does not exist!");
+
+ if (await this._roleRepository.DoesNameExist(updateRoleServiceModel.Name))
+ throw new ArgumentException("Role name already exists!");
+
+ Role role = this._roleMapper.Map<Role>(updateRoleServiceModel);
+ return await this._roleRepository.EditAsync(updateRoleServiceModel.Id, role);
+ }
+
+ public async Task<bool> DeleteRole(Guid id)
+ {
+ if (!await this._roleRepository.DoesRoleExist(id))
+ throw new ArgumentException("Role does not exist!");
+
+ Role role = await this._roleRepository.GetByIdAsync(id);
+ return await this._roleRepository.DeleteAsync(role);
+ }
+ }
+}
diff --git a/src/Services/DevHive.Services/Services/TechnologyService.cs b/src/Services/DevHive.Services/Services/TechnologyService.cs
new file mode 100644
index 0000000..6dd6286
--- /dev/null
+++ b/src/Services/DevHive.Services/Services/TechnologyService.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using AutoMapper;
+using DevHive.Data.Interfaces.Repositories;
+using DevHive.Data.Models;
+using DevHive.Services.Interfaces;
+using DevHive.Services.Models.Technology;
+
+namespace DevHive.Services.Services
+{
+ public class TechnologyService : ITechnologyService
+ {
+ private readonly ITechnologyRepository _technologyRepository;
+ private readonly IMapper _technologyMapper;
+
+ public TechnologyService(ITechnologyRepository technologyRepository, IMapper technologyMapper)
+ {
+ this._technologyRepository = technologyRepository;
+ this._technologyMapper = technologyMapper;
+ }
+
+ #region Create
+ public async Task<Guid> CreateTechnology(CreateTechnologyServiceModel technologyServiceModel)
+ {
+ if (await this._technologyRepository.DoesTechnologyNameExistAsync(technologyServiceModel.Name))
+ throw new ArgumentException("Technology already exists!");
+
+ Technology technology = this._technologyMapper.Map<Technology>(technologyServiceModel);
+ bool success = await this._technologyRepository.AddAsync(technology);
+
+ if (success)
+ {
+ Technology newTechnology = await this._technologyRepository.GetByNameAsync(technologyServiceModel.Name);
+ return newTechnology.Id;
+ }
+ else
+ return Guid.Empty;
+ }
+ #endregion
+
+ #region Read
+ public async Task<ReadTechnologyServiceModel> GetTechnologyById(Guid id)
+ {
+ Technology technology = await this._technologyRepository.GetByIdAsync(id);
+
+ if (technology == null)
+ throw new ArgumentException("The technology does not exist");
+
+ return this._technologyMapper.Map<ReadTechnologyServiceModel>(technology);
+ }
+
+ public HashSet<ReadTechnologyServiceModel> GetTechnologies()
+ {
+ HashSet<Technology> technologies = this._technologyRepository.GetTechnologies();
+
+ return this._technologyMapper.Map<HashSet<ReadTechnologyServiceModel>>(technologies);
+ }
+ #endregion
+
+ #region Update
+ public async Task<bool> UpdateTechnology(UpdateTechnologyServiceModel updateTechnologyServiceModel)
+ {
+ if (!await this._technologyRepository.DoesTechnologyExistAsync(updateTechnologyServiceModel.Id))
+ throw new ArgumentException("Technology does not exist!");
+
+ if (await this._technologyRepository.DoesTechnologyNameExistAsync(updateTechnologyServiceModel.Name))
+ throw new ArgumentException("Technology name already exists!");
+
+ Technology technology = this._technologyMapper.Map<Technology>(updateTechnologyServiceModel);
+ bool result = await this._technologyRepository.EditAsync(updateTechnologyServiceModel.Id, technology);
+
+ return result;
+ }
+ #endregion
+
+ #region Delete
+ public async Task<bool> DeleteTechnology(Guid id)
+ {
+ if (!await this._technologyRepository.DoesTechnologyExistAsync(id))
+ throw new ArgumentException("Technology does not exist!");
+
+ Technology technology = await this._technologyRepository.GetByIdAsync(id);
+ bool result = await this._technologyRepository.DeleteAsync(technology);
+
+ return result;
+ }
+ #endregion
+ }
+}
diff --git a/src/Services/DevHive.Services/Services/UserService.cs b/src/Services/DevHive.Services/Services/UserService.cs
new file mode 100644
index 0000000..22d5052
--- /dev/null
+++ b/src/Services/DevHive.Services/Services/UserService.cs
@@ -0,0 +1,382 @@
+using AutoMapper;
+using DevHive.Services.Options;
+using DevHive.Services.Models.Identity.User;
+using System.Threading.Tasks;
+using DevHive.Data.Models;
+using System;
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using Microsoft.IdentityModel.Tokens;
+using System.Text;
+using System.Collections.Generic;
+using DevHive.Common.Models.Identity;
+using DevHive.Services.Interfaces;
+using DevHive.Data.Interfaces.Repositories;
+using System.Linq;
+using DevHive.Common.Models.Misc;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Identity;
+
+namespace DevHive.Services.Services
+{
+ public class UserService : IUserService
+ {
+ private readonly IUserRepository _userRepository;
+ private readonly IRoleRepository _roleRepository;
+ private readonly ILanguageRepository _languageRepository;
+ private readonly ITechnologyRepository _technologyRepository;
+ private readonly UserManager<User> _userManager;
+ private readonly RoleManager<Role> _roleManager;
+ private readonly IMapper _userMapper;
+ private readonly JWTOptions _jwtOptions;
+ private readonly ICloudService _cloudService;
+
+ public UserService(IUserRepository userRepository,
+ ILanguageRepository languageRepository,
+ IRoleRepository roleRepository,
+ ITechnologyRepository technologyRepository,
+ UserManager<User> userManager,
+ RoleManager<Role> roleManager,
+ IMapper mapper,
+ JWTOptions jwtOptions,
+ ICloudService cloudService)
+ {
+ this._userRepository = userRepository;
+ this._roleRepository = roleRepository;
+ this._userMapper = mapper;
+ this._jwtOptions = jwtOptions;
+ this._languageRepository = languageRepository;
+ this._technologyRepository = technologyRepository;
+ this._userManager = userManager;
+ this._roleManager = roleManager;
+ this._cloudService = cloudService;
+ }
+
+ #region Authentication
+ /// <summary>
+ /// Adds a new user to the database with the values from the given model.
+ /// Returns a JSON Web Token (that can be used for authorization)
+ /// </summary>
+ public async Task<TokenModel> LoginUser(LoginServiceModel loginModel)
+ {
+ if (!await this._userRepository.DoesUsernameExistAsync(loginModel.UserName))
+ throw new ArgumentException("Invalid username!");
+
+ User user = await this._userRepository.GetByUsernameAsync(loginModel.UserName);
+
+ if (user.PasswordHash != PasswordModifications.GeneratePasswordHash(loginModel.Password))
+ throw new ArgumentException("Incorrect password!");
+
+ return new TokenModel(WriteJWTSecurityToken(user.Id, user.UserName, user.Roles));
+ }
+
+ /// <summary>
+ /// Returns a new JSON Web Token (that can be used for authorization) for the given user
+ /// </summary>
+ public async Task<TokenModel> RegisterUser(RegisterServiceModel registerModel)
+ {
+ if (await this._userRepository.DoesUsernameExistAsync(registerModel.UserName))
+ throw new ArgumentException("Username already exists!");
+
+ if (await this._userRepository.DoesEmailExistAsync(registerModel.Email))
+ throw new ArgumentException("Email already exists!");
+
+ User user = this._userMapper.Map<User>(registerModel);
+ user.PasswordHash = PasswordModifications.GeneratePasswordHash(registerModel.Password);
+ user.ProfilePicture = new ProfilePicture() { PictureURL = "/assets/images/feed/profile-pic.png" };
+
+ // Make sure the default role exists
+ //TODO: Move when project starts
+ if (!await this._roleRepository.DoesNameExist(Role.DefaultRole))
+ await this._roleRepository.AddAsync(new Role { Name = Role.DefaultRole });
+
+ user.Roles.Add(await this._roleRepository.GetByNameAsync(Role.DefaultRole));
+
+ IdentityResult userResult = await this._userManager.CreateAsync(user);
+ User createdUser = await this._userRepository.GetByUsernameAsync(registerModel.UserName);
+
+ if (!userResult.Succeeded)
+ throw new ArgumentException("Unable to create a user");
+
+ return new TokenModel(WriteJWTSecurityToken(createdUser.Id, createdUser.UserName, createdUser.Roles));
+ }
+ #endregion
+
+ #region Read
+ public async Task<UserServiceModel> GetUserById(Guid id)
+ {
+ User user = await this._userRepository.GetByIdAsync(id) ??
+ throw new ArgumentException("User does not exist!");
+
+ return this._userMapper.Map<UserServiceModel>(user);
+ }
+
+ public async Task<UserServiceModel> GetUserByUsername(string username)
+ {
+ User user = await this._userRepository.GetByUsernameAsync(username) ??
+ throw new ArgumentException("User does not exist!");
+
+ return this._userMapper.Map<UserServiceModel>(user);
+ }
+ #endregion
+
+ #region Update
+ public async Task<UserServiceModel> UpdateUser(UpdateUserServiceModel updateUserServiceModel)
+ {
+ await this.ValidateUserOnUpdate(updateUserServiceModel);
+
+ User currentUser = await this._userRepository.GetByIdAsync(updateUserServiceModel.Id);
+ await this.PopulateUserModel(currentUser, updateUserServiceModel);
+
+ if (updateUserServiceModel.Friends.Count() > 0)
+ await this.CreateRelationToFriends(currentUser, updateUserServiceModel.Friends.ToList());
+ else
+ currentUser.Friends.Clear();
+
+ IdentityResult result = await this._userManager.UpdateAsync(currentUser);
+
+ if (!result.Succeeded)
+ throw new InvalidOperationException("Unable to edit user!");
+
+ User newUser = await this._userRepository.GetByIdAsync(currentUser.Id);
+ return this._userMapper.Map<UserServiceModel>(newUser);
+ }
+
+ /// <summary>
+ /// Uploads the given picture and assigns it's link to the user in the database
+ /// </summary>
+ public async Task<ProfilePictureServiceModel> UpdateProfilePicture(UpdateProfilePictureServiceModel updateProfilePictureServiceModel)
+ {
+ User user = await this._userRepository.GetByIdAsync(updateProfilePictureServiceModel.UserId);
+
+ if (!string.IsNullOrEmpty(user.ProfilePicture.PictureURL))
+ {
+ bool success = await _cloudService.RemoveFilesFromCloud(new List<string> { user.ProfilePicture.PictureURL });
+ if (!success)
+ throw new InvalidCastException("Could not delete old profile picture!");
+ }
+
+ string fileUrl = (await this._cloudService.UploadFilesToCloud(new List<IFormFile> { updateProfilePictureServiceModel.Picture }))[0] ??
+ throw new ArgumentNullException("Unable to upload profile picture to cloud");
+
+ bool successful = await this._userRepository.UpdateProfilePicture(updateProfilePictureServiceModel.UserId, fileUrl);
+
+ if (!successful)
+ throw new InvalidOperationException("Unable to change profile picture!");
+
+ return new ProfilePictureServiceModel() { ProfilePictureURL = fileUrl };
+ }
+ #endregion
+
+ #region Delete
+ public async Task<bool> DeleteUser(Guid id)
+ {
+ if (!await this._userRepository.DoesUserExistAsync(id))
+ throw new ArgumentException("User does not exist!");
+
+ User user = await this._userRepository.GetByIdAsync(id);
+ IdentityResult result = await this._userManager.DeleteAsync(user);
+
+ return result.Succeeded;
+ }
+ #endregion
+
+ #region Validations
+ /// <summary>
+ /// Checks whether the given user, gotten by the "id" property,
+ /// is the same user as the one in the token (uness the user in the token has the admin role)
+ /// and the roles in the token are the same as those in the user, gotten by the id in the token
+ /// </summary>
+ public async Task<bool> ValidJWT(Guid id, string rawTokenData)
+ {
+ // There is authorization name in the beginning, i.e. "Bearer eyJh..."
+ var jwt = new JwtSecurityTokenHandler().ReadJwtToken(rawTokenData.Remove(0, 7));
+
+ Guid jwtUserID = new Guid(this.GetClaimTypeValues("ID", jwt.Claims).First());
+ List<string> jwtRoleNames = this.GetClaimTypeValues("role", jwt.Claims);
+
+ User user = await this._userRepository.GetByIdAsync(jwtUserID)
+ ?? throw new ArgumentException("User does not exist!");
+
+ /* Check if user is trying to do something to himself, unless he's an admin */
+
+ /* Check roles */
+ if (!jwtRoleNames.Contains(Role.AdminRole))
+ if (user.Id != id)
+ return false;
+
+ // Check if jwt contains all user roles (if it doesn't, jwt is either old or tampered with)
+ foreach (var role in user.Roles)
+ {
+ if (!jwtRoleNames.Contains(role.Name))
+ return false;
+ }
+
+ // Check if jwt contains only roles of user
+ if (jwtRoleNames.Count != user.Roles.Count)
+ return false;
+
+ return true;
+ }
+
+ /// <summary>
+ /// Returns all values from a given claim type
+ /// </summary>
+ private List<string> GetClaimTypeValues(string type, IEnumerable<Claim> claims)
+ {
+ List<string> toReturn = new();
+
+ foreach (var claim in claims)
+ if (claim.Type == type)
+ toReturn.Add(claim.Value);
+
+ return toReturn;
+ }
+
+ /// <summary>
+ /// Checks whether the user in the model exists
+ /// and whether the username in the model is already taken.
+ /// If the check fails (is false), it throws an exception, otherwise nothing happens
+ /// </summary>
+ private async Task ValidateUserOnUpdate(UpdateUserServiceModel updateUserServiceModel)
+ {
+ if (!await this._userRepository.DoesUserExistAsync(updateUserServiceModel.Id))
+ throw new ArgumentException("User does not exist!");
+
+ if (updateUserServiceModel.Friends.Any(x => x.UserName == updateUserServiceModel.UserName))
+ throw new ArgumentException("You cant add yourself as a friend(sry, bro)!");
+
+ if (!this._userRepository.DoesUserHaveThisUsername(updateUserServiceModel.Id, updateUserServiceModel.UserName)
+ && await this._userRepository.DoesUsernameExistAsync(updateUserServiceModel.UserName))
+ throw new ArgumentException("Username already exists!");
+
+ List<string> usernames = new();
+ foreach (var friend in updateUserServiceModel.Friends)
+ usernames.Add(friend.UserName);
+
+ if (!await this._userRepository.ValidateFriendsCollectionAsync(usernames))
+ throw new ArgumentException("One or more friends do not exist!");
+ }
+
+ /// <summary>
+ /// Return a new JSON Web Token, containing the user id, username and roles.
+ /// Tokens have an expiration time of 7 days.
+ /// </summary>
+ private string WriteJWTSecurityToken(Guid userId, string username, HashSet<Role> roles)
+ {
+ byte[] signingKey = Encoding.ASCII.GetBytes(_jwtOptions.Secret);
+ HashSet<Claim> claims = new()
+ {
+ new Claim("ID", $"{userId}"),
+ new Claim("Username", username)
+ };
+
+ foreach (var role in roles)
+ {
+ claims.Add(new Claim(ClaimTypes.Role, role.Name));
+ }
+
+ SecurityTokenDescriptor tokenDescriptor = new()
+ {
+ Subject = new ClaimsIdentity(claims),
+ Expires = DateTime.Today.AddDays(7),
+ SigningCredentials = new SigningCredentials(
+ new SymmetricSecurityKey(signingKey),
+ SecurityAlgorithms.HmacSha512Signature)
+ };
+
+ JwtSecurityTokenHandler tokenHandler = new();
+ SecurityToken token = tokenHandler.CreateToken(tokenDescriptor);
+ return tokenHandler.WriteToken(token);
+ }
+ #endregion
+
+ #region Misc
+ public async Task<TokenModel> SuperSecretPromotionToAdmin(Guid userId)
+ {
+ User user = await this._userRepository.GetByIdAsync(userId) ??
+ throw new ArgumentException("User does not exist! Can't promote shit in this country...");
+
+ if (!await this._roleRepository.DoesNameExist(Role.AdminRole))
+ {
+ Role adminRole = new()
+ {
+ Name = Role.AdminRole
+ };
+ adminRole.Users.Add(user);
+
+ await this._roleRepository.AddAsync(adminRole);
+ }
+
+ Role admin = await this._roleManager.FindByNameAsync(Role.AdminRole);
+
+ user.Roles.Add(admin);
+ await this._userManager.UpdateAsync(user);
+
+ User newUser = await this._userRepository.GetByIdAsync(userId);
+
+ return new TokenModel(WriteJWTSecurityToken(newUser.Id, newUser.UserName, newUser.Roles));
+ }
+
+ private async Task PopulateUserModel(User user, UpdateUserServiceModel updateUserServiceModel)
+ {
+ user.UserName = updateUserServiceModel.UserName;
+ user.FirstName = updateUserServiceModel.FirstName;
+ user.LastName = updateUserServiceModel.LastName;
+ user.Email = updateUserServiceModel.Email;
+
+ //Do NOT allow a user to change his roles, unless he is an Admin
+ bool isAdmin = await this._userManager.IsInRoleAsync(user, Role.AdminRole);
+
+ if (isAdmin)
+ {
+ HashSet<Role> roles = new();
+ foreach (var role in updateUserServiceModel.Roles)
+ {
+ Role returnedRole = await this._roleRepository.GetByNameAsync(role.Name) ??
+ throw new ArgumentException($"Role {role.Name} does not exist!");
+
+ roles.Add(returnedRole);
+ }
+ user.Roles = roles;
+ }
+
+ HashSet<Language> languages = new();
+ int languagesCount = updateUserServiceModel.Languages.Count;
+ for (int i = 0; i < languagesCount; i++)
+ {
+ Language language = await this._languageRepository.GetByNameAsync(updateUserServiceModel.Languages.ElementAt(i).Name) ??
+ throw new ArgumentException("Invalid language name!");
+
+ languages.Add(language);
+ }
+ user.Languages = languages;
+
+ /* Fetch Technologies and replace model's*/
+ HashSet<Technology> technologies = new();
+ int technologiesCount = updateUserServiceModel.Technologies.Count;
+ for (int i = 0; i < technologiesCount; i++)
+ {
+ Technology technology = await this._technologyRepository.GetByNameAsync(updateUserServiceModel.Technologies.ElementAt(i).Name) ??
+ throw new ArgumentException("Invalid technology name!");
+
+ technologies.Add(technology);
+ }
+ user.Technologies = technologies;
+ }
+
+ private async Task CreateRelationToFriends(User user, List<UpdateFriendServiceModel> friends)
+ {
+ foreach (var friend in friends)
+ {
+ User amigo = await this._userRepository.GetByUsernameAsync(friend.UserName);
+
+ user.Friends.Add(amigo);
+ amigo.Friends.Add(user);
+
+ await this._userManager.UpdateAsync(amigo);
+ }
+ }
+ #endregion
+ }
+}