diff options
Diffstat (limited to 'src/Web')
73 files changed, 4385 insertions, 0 deletions
diff --git a/src/Web/DevHive.Web.Models/Attributes/GoodPasswordAttribute.cs b/src/Web/DevHive.Web.Models/Attributes/GoodPasswordAttribute.cs new file mode 100644 index 0000000..c452499 --- /dev/null +++ b/src/Web/DevHive.Web.Models/Attributes/GoodPasswordAttribute.cs @@ -0,0 +1,23 @@ +using System.ComponentModel.DataAnnotations; + +namespace DevHive.Web.Models.Attributes +{ + public class GoodPasswordAttribute : ValidationAttribute + { + public override bool IsValid(object value) + { + var stringValue = (string)value; + + for (int i = 0; i < stringValue.Length; i++) + { + if (char.IsDigit(stringValue[i])) + { + base.ErrorMessage = "Password must be atleast 5 characters long!"; + return stringValue.Length >= 5; + } + } + base.ErrorMessage = "Password must contain atleast 1 digit!"; + return false; + } + } +} diff --git a/src/Web/DevHive.Web.Models/Attributes/OnlyLettersModelValidation.cs b/src/Web/DevHive.Web.Models/Attributes/OnlyLettersModelValidation.cs new file mode 100644 index 0000000..faaeee4 --- /dev/null +++ b/src/Web/DevHive.Web.Models/Attributes/OnlyLettersModelValidation.cs @@ -0,0 +1,20 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace DevHive.Web.Models.Attributes +{ + public class OnlyLettersAttribute : ValidationAttribute + { + public override bool IsValid(object value) + { + var stringValue = (string)value; + + foreach (char ch in stringValue) + { + if (!char.IsLetter(ch)) + return false; + } + return true; + } + } +} diff --git a/src/Web/DevHive.Web.Models/Comment/CreateCommentWebModel.cs b/src/Web/DevHive.Web.Models/Comment/CreateCommentWebModel.cs new file mode 100644 index 0000000..8b2bf8d --- /dev/null +++ b/src/Web/DevHive.Web.Models/Comment/CreateCommentWebModel.cs @@ -0,0 +1,17 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; + +namespace DevHive.Web.Models.Comment +{ + public class CreateCommentWebModel + { + [NotNull] + [Required] + public Guid PostId { get; set; } + + [NotNull] + [Required] + public string Message { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Comment/ReadCommentWebModel.cs b/src/Web/DevHive.Web.Models/Comment/ReadCommentWebModel.cs new file mode 100644 index 0000000..4d3aff7 --- /dev/null +++ b/src/Web/DevHive.Web.Models/Comment/ReadCommentWebModel.cs @@ -0,0 +1,21 @@ +using System; + +namespace DevHive.Web.Models.Comment +{ + public class ReadCommentWebModel + { + public Guid CommentId { get; set; } + + public Guid PostId { get; set; } + + public string IssuerFirstName { get; set; } + + public string IssuerLastName { get; set; } + + public string IssuerUsername { get; set; } + + public string Message { get; set; } + + public DateTime TimeCreated { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Comment/UpdateCommentWebModel.cs b/src/Web/DevHive.Web.Models/Comment/UpdateCommentWebModel.cs new file mode 100644 index 0000000..b5d7970 --- /dev/null +++ b/src/Web/DevHive.Web.Models/Comment/UpdateCommentWebModel.cs @@ -0,0 +1,13 @@ +using System; + +namespace DevHive.Web.Models.Comment +{ + public class UpdateCommentWebModel + { + public Guid CommentId { get; set; } + + public Guid PostId { get; set; } + + public string NewMessage { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/DevHive.Web.Models.csproj b/src/Web/DevHive.Web.Models/DevHive.Web.Models.csproj new file mode 100644 index 0000000..79c856f --- /dev/null +++ b/src/Web/DevHive.Web.Models/DevHive.Web.Models.csproj @@ -0,0 +1,13 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>net5.0</TargetFramework> + </PropertyGroup> + <ItemGroup> + <ProjectReference Include="..\..\Common\DevHive.Common\DevHive.Common.csproj"/> + <ProjectReference Include="..\..\Common\DevHive.Common.Models\DevHive.Common.Models.csproj"/> + </ItemGroup> + <ItemGroup> + <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2"/> + <PackageReference Include="SonarAnalyzer.CSharp" Version="8.20.0.28934"/> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/src/Web/DevHive.Web.Models/Feed/GetPageWebModel.cs b/src/Web/DevHive.Web.Models/Feed/GetPageWebModel.cs new file mode 100644 index 0000000..4ea44cc --- /dev/null +++ b/src/Web/DevHive.Web.Models/Feed/GetPageWebModel.cs @@ -0,0 +1,19 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace DevHive.Web.Models.Feed +{ + public class GetPageWebModel + { + [Range(1, int.MaxValue)] + public int PageNumber { get; set; } + + [Required] + public DateTime FirstPageTimeIssued { get; set; } + + [DefaultValue(5)] + [Range(1, int.MaxValue)] + public int PageSize { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Feed/ReadPageWebModel.cs b/src/Web/DevHive.Web.Models/Feed/ReadPageWebModel.cs new file mode 100644 index 0000000..f429313 --- /dev/null +++ b/src/Web/DevHive.Web.Models/Feed/ReadPageWebModel.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using DevHive.Web.Models.Post; + +namespace DevHive.Web.Models.Comment +{ + public class ReadPageWebModel + { + public List<ReadPostWebModel> Posts { get; set; } = new(); + } +} diff --git a/src/Web/DevHive.Web.Models/Language/CreateLanguageWebModel.cs b/src/Web/DevHive.Web.Models/Language/CreateLanguageWebModel.cs new file mode 100644 index 0000000..a739e7f --- /dev/null +++ b/src/Web/DevHive.Web.Models/Language/CreateLanguageWebModel.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; + +namespace DevHive.Web.Models.Language +{ + public class CreateLanguageWebModel + { + [NotNull] + [Required] + [MinLength(3)] + [MaxLength(50)] + public string Name { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Language/LanguageWebModel.cs b/src/Web/DevHive.Web.Models/Language/LanguageWebModel.cs new file mode 100644 index 0000000..7515e70 --- /dev/null +++ b/src/Web/DevHive.Web.Models/Language/LanguageWebModel.cs @@ -0,0 +1,13 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; + +namespace DevHive.Web.Models.Language +{ + public class LanguageWebModel + { + [NotNull] + [Required] + public Guid Id { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Language/ReadLanguageWebModel.cs b/src/Web/DevHive.Web.Models/Language/ReadLanguageWebModel.cs new file mode 100644 index 0000000..3d9d5b6 --- /dev/null +++ b/src/Web/DevHive.Web.Models/Language/ReadLanguageWebModel.cs @@ -0,0 +1,11 @@ +using System; + +namespace DevHive.Web.Models.Language +{ + public class ReadLanguageWebModel + { + public Guid Id { get; set; } + + public string Name { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Language/UpdateLanguageWebModel.cs b/src/Web/DevHive.Web.Models/Language/UpdateLanguageWebModel.cs new file mode 100644 index 0000000..128d534 --- /dev/null +++ b/src/Web/DevHive.Web.Models/Language/UpdateLanguageWebModel.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; + +namespace DevHive.Web.Models.Language +{ + public class UpdateLanguageWebModel + { + [NotNull] + [Required] + [MinLength(3)] + [MaxLength(50)] + public string Name { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Post/CreatePostWebModel.cs b/src/Web/DevHive.Web.Models/Post/CreatePostWebModel.cs new file mode 100644 index 0000000..237259d --- /dev/null +++ b/src/Web/DevHive.Web.Models/Post/CreatePostWebModel.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; +using Microsoft.AspNetCore.Http; + +namespace DevHive.Web.Models.Post +{ + public class CreatePostWebModel + { + [NotNull] + [Required] + public string Message { get; set; } + + public List<IFormFile> Files { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Post/ReadPostWebModel.cs b/src/Web/DevHive.Web.Models/Post/ReadPostWebModel.cs new file mode 100644 index 0000000..d6ea1f4 --- /dev/null +++ b/src/Web/DevHive.Web.Models/Post/ReadPostWebModel.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using DevHive.Common.Models.Misc; + +namespace DevHive.Web.Models.Post +{ + public class ReadPostWebModel + { + public Guid PostId { get; set; } + + public string CreatorFirstName { get; set; } + + public string CreatorLastName { get; set; } + + public string CreatorUsername { get; set; } + + public string Message { get; set; } + + public DateTime TimeCreated { get; set; } + + public List<IdModel> Comments { get; set; } + + public List<string> FileUrls { get; set; } + + public int CurrentRating { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Post/UpdatePostWebModel.cs b/src/Web/DevHive.Web.Models/Post/UpdatePostWebModel.cs new file mode 100644 index 0000000..a0c9b61 --- /dev/null +++ b/src/Web/DevHive.Web.Models/Post/UpdatePostWebModel.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; +using Microsoft.AspNetCore.Http; + +namespace DevHive.Web.Models.Post +{ + public class UpdatePostWebModel + { + [Required] + [NotNull] + public Guid PostId { get; set; } + + [NotNull] + [Required] + public string NewMessage { get; set; } + + public List<IFormFile> Files { get; set; } = new(); + } +} diff --git a/src/Web/DevHive.Web.Models/ProfilePicture/ProfilePictureWebModel.cs b/src/Web/DevHive.Web.Models/ProfilePicture/ProfilePictureWebModel.cs new file mode 100644 index 0000000..3fe201c --- /dev/null +++ b/src/Web/DevHive.Web.Models/ProfilePicture/ProfilePictureWebModel.cs @@ -0,0 +1,9 @@ +using Microsoft.AspNetCore.Http; + +namespace DevHive.Web.Models.ProfilePicture +{ + public class ProfilePictureWebModel + { + public IFormFile Picture { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Rating/CreateRatingWebModel.cs b/src/Web/DevHive.Web.Models/Rating/CreateRatingWebModel.cs new file mode 100644 index 0000000..abbb702 --- /dev/null +++ b/src/Web/DevHive.Web.Models/Rating/CreateRatingWebModel.cs @@ -0,0 +1,11 @@ +using System; + +namespace DevHive.Web.Models.Rating +{ + public class CreateRatingWebModel + { + public Guid PostId { get; set; } + + public bool IsLike { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Rating/ReadRatingWebModel.cs b/src/Web/DevHive.Web.Models/Rating/ReadRatingWebModel.cs new file mode 100644 index 0000000..40f4c6f --- /dev/null +++ b/src/Web/DevHive.Web.Models/Rating/ReadRatingWebModel.cs @@ -0,0 +1,15 @@ +using System; + +namespace DevHive.Web.Models.Rating +{ + public class ReadRatingWebModel + { + public Guid Id { get; set; } + + public Guid PostId { get; set; } + + public Guid UserId { get; set; } + + public bool IsLike { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Rating/UpdateRatingWebModel.cs b/src/Web/DevHive.Web.Models/Rating/UpdateRatingWebModel.cs new file mode 100644 index 0000000..176c099 --- /dev/null +++ b/src/Web/DevHive.Web.Models/Rating/UpdateRatingWebModel.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DevHive.Web.Models.Rating +{ + public class UpdateRatingWebModel + { + public bool IsLike { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Role/CreateRoleWebModel.cs b/src/Web/DevHive.Web.Models/Role/CreateRoleWebModel.cs new file mode 100644 index 0000000..9beced4 --- /dev/null +++ b/src/Web/DevHive.Web.Models/Role/CreateRoleWebModel.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; + +namespace DevHive.Web.Models.Role +{ + public class CreateRoleWebModel + { + [NotNull] + [Required] + [MinLength(3)] + [MaxLength(50)] + public string Name { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Role/RoleWebModel.cs b/src/Web/DevHive.Web.Models/Role/RoleWebModel.cs new file mode 100644 index 0000000..680fb05 --- /dev/null +++ b/src/Web/DevHive.Web.Models/Role/RoleWebModel.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; + +namespace DevHive.Web.Models.Role +{ + public class RoleWebModel + { + [NotNull] + [Required] + [MinLength(3)] + [MaxLength(50)] + public string Name { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Role/UpdateRoleWebModel.cs b/src/Web/DevHive.Web.Models/Role/UpdateRoleWebModel.cs new file mode 100644 index 0000000..f32839e --- /dev/null +++ b/src/Web/DevHive.Web.Models/Role/UpdateRoleWebModel.cs @@ -0,0 +1,15 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; + +namespace DevHive.Web.Models.Role +{ + public class UpdateRoleWebModel + { + [NotNull] + [Required] + [MinLength(3)] + [MaxLength(50)] + public string Name { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Technology/CreateTechnologyWebModel.cs b/src/Web/DevHive.Web.Models/Technology/CreateTechnologyWebModel.cs new file mode 100644 index 0000000..ec9ec15 --- /dev/null +++ b/src/Web/DevHive.Web.Models/Technology/CreateTechnologyWebModel.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; + +namespace DevHive.Web.Models.Technology +{ + public class CreateTechnologyWebModel + { + [NotNull] + [Required] + [MinLength(3)] + [MaxLength(50)] + public string Name { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Technology/ReadTechnologyWebModel.cs b/src/Web/DevHive.Web.Models/Technology/ReadTechnologyWebModel.cs new file mode 100644 index 0000000..94542d7 --- /dev/null +++ b/src/Web/DevHive.Web.Models/Technology/ReadTechnologyWebModel.cs @@ -0,0 +1,11 @@ +using System; + +namespace DevHive.Web.Models.Technology +{ + public class ReadTechnologyWebModel + { + public Guid Id { get; set; } + + public string Name { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Technology/TechnologyWebModel.cs b/src/Web/DevHive.Web.Models/Technology/TechnologyWebModel.cs new file mode 100644 index 0000000..6e8273b --- /dev/null +++ b/src/Web/DevHive.Web.Models/Technology/TechnologyWebModel.cs @@ -0,0 +1,13 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; + +namespace DevHive.Web.Models.Technology +{ + public class TechnologyWebModel + { + [NotNull] + [Required] + public Guid Id { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Technology/UpdateTechnologyWebModel.cs b/src/Web/DevHive.Web.Models/Technology/UpdateTechnologyWebModel.cs new file mode 100644 index 0000000..3e9fe2a --- /dev/null +++ b/src/Web/DevHive.Web.Models/Technology/UpdateTechnologyWebModel.cs @@ -0,0 +1,15 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; + +namespace DevHive.Web.Models.Technology +{ + public class UpdateTechnologyWebModel + { + [NotNull] + [Required] + [MinLength(3)] + [MaxLength(50)] + public string Name { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/User/BaseUserWebModel.cs b/src/Web/DevHive.Web.Models/User/BaseUserWebModel.cs new file mode 100644 index 0000000..5fdb757 --- /dev/null +++ b/src/Web/DevHive.Web.Models/User/BaseUserWebModel.cs @@ -0,0 +1,34 @@ +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; +using DevHive.Web.Models.Attributes; + +namespace DevHive.Web.Models.User +{ + public class BaseUserWebModel + { + [NotNull] + [Required] + [MinLength(3)] + [MaxLength(50)] + public string UserName { get; set; } + + [NotNull] + [Required] + [EmailAddress] + public string Email { get; set; } + + [NotNull] + [Required] + [MinLength(3)] + [MaxLength(30)] + [OnlyLetters(ErrorMessage = "First name can only contain letters!")] + public string FirstName { get; set; } + + [NotNull] + [Required] + [MinLength(3)] + [MaxLength(30)] + [OnlyLetters(ErrorMessage = "Last name can only contain letters!")] + public string LastName { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/User/LoginWebModel.cs b/src/Web/DevHive.Web.Models/User/LoginWebModel.cs new file mode 100644 index 0000000..7d4e93a --- /dev/null +++ b/src/Web/DevHive.Web.Models/User/LoginWebModel.cs @@ -0,0 +1,20 @@ +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; +using DevHive.Web.Models.Attributes; + +namespace DevHive.Web.Models.User +{ + public class LoginWebModel + { + [NotNull] + [Required] + [MinLength(3)] + [MaxLength(50)] + public string UserName { get; set; } + + [NotNull] + [Required] + [GoodPassword] + public string Password { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/User/RegisterWebModel.cs b/src/Web/DevHive.Web.Models/User/RegisterWebModel.cs new file mode 100644 index 0000000..999ff00 --- /dev/null +++ b/src/Web/DevHive.Web.Models/User/RegisterWebModel.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; +using DevHive.Web.Models.Attributes; + +namespace DevHive.Web.Models.User +{ + public class RegisterWebModel : BaseUserWebModel + { + [NotNull] + [Required] + [GoodPassword] + public string Password { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/User/TokenWebModel.cs b/src/Web/DevHive.Web.Models/User/TokenWebModel.cs new file mode 100644 index 0000000..7f3361d --- /dev/null +++ b/src/Web/DevHive.Web.Models/User/TokenWebModel.cs @@ -0,0 +1,12 @@ +namespace DevHive.Web.Models.User +{ + public class TokenWebModel + { + public TokenWebModel(string token) + { + this.Token = token; + } + + public string Token { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/User/UpdateUserWebModel.cs b/src/Web/DevHive.Web.Models/User/UpdateUserWebModel.cs new file mode 100644 index 0000000..f74927e --- /dev/null +++ b/src/Web/DevHive.Web.Models/User/UpdateUserWebModel.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; +using DevHive.Web.Models.Attributes; +using DevHive.Web.Models.Role; +using DevHive.Web.Models.Language; +using DevHive.Web.Models.Technology; + +namespace DevHive.Web.Models.User +{ + public class UpdateUserWebModel : BaseUserWebModel + { + [NotNull] + [Required] + [GoodPassword] + public string Password { get; set; } + + [NotNull] + [Required] + public HashSet<UsernameWebModel> Friends { get; set; } + + [NotNull] + [Required] + public HashSet<UpdateRoleWebModel> Roles { get; set; } + + [NotNull] + [Required] + public HashSet<UpdateLanguageWebModel> Languages { get; set; } + + [NotNull] + [Required] + public HashSet<UpdateTechnologyWebModel> Technologies { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/User/UserWebModel.cs b/src/Web/DevHive.Web.Models/User/UserWebModel.cs new file mode 100644 index 0000000..37141d0 --- /dev/null +++ b/src/Web/DevHive.Web.Models/User/UserWebModel.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; +using DevHive.Common.Models.Misc; +using DevHive.Web.Models.Role; +using DevHive.Web.Models.Language; +using DevHive.Web.Models.Technology; + +namespace DevHive.Web.Models.User +{ + public class UserWebModel : BaseUserWebModel + { + public string ProfilePictureURL { get; set; } + + [NotNull] + [Required] + public HashSet<RoleWebModel> Roles { get; set; } = new(); + + [NotNull] + [Required] + public HashSet<UsernameWebModel> Friends { get; set; } = new(); + + [NotNull] + [Required] + public HashSet<LanguageWebModel> Languages { get; set; } = new(); + + [NotNull] + [Required] + public HashSet<TechnologyWebModel> Technologies { get; set; } = new(); + + public List<IdModel> Posts { get; set; } = new(); + } +} diff --git a/src/Web/DevHive.Web.Models/User/UsernameWebModel.cs b/src/Web/DevHive.Web.Models/User/UsernameWebModel.cs new file mode 100644 index 0000000..638cb15 --- /dev/null +++ b/src/Web/DevHive.Web.Models/User/UsernameWebModel.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; +using DevHive.Web.Models.Attributes; + +namespace DevHive.Web.Models.User +{ + public class UsernameWebModel + { + [NotNull] + [Required] + [MinLength(3)] + [MaxLength(50)] + public string UserName { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Tests/CommentController.Tests.cs b/src/Web/DevHive.Web.Tests/CommentController.Tests.cs new file mode 100644 index 0000000..830677e --- /dev/null +++ b/src/Web/DevHive.Web.Tests/CommentController.Tests.cs @@ -0,0 +1,325 @@ +using System; +using System.Linq; +using AutoMapper; +using DevHive.Common.Jwt.Interfaces; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.Comment; +using DevHive.Web.Controllers; +using DevHive.Web.Models.Comment; +using Microsoft.AspNetCore.Mvc; +using Moq; +using NUnit.Framework; + +namespace DevHive.Web.Tests +{ + [TestFixture] + public class CommentControllerTests + { + const string MESSAGE = "Gosho Trapov"; + private Mock<ICommentService> _commentServiceMock; + private Mock<IMapper> _mapperMock; + private Mock<IJwtService> _jwtServiceMock; + private CommentController _commentController; + + #region Setup + [SetUp] + public void SetUp() + { + this._commentServiceMock = new Mock<ICommentService>(); + this._mapperMock = new Mock<IMapper>(); + this._jwtServiceMock = new Mock<IJwtService>(); + this._commentController = new CommentController(this._commentServiceMock.Object, this._mapperMock.Object, this._jwtServiceMock.Object); + } + #endregion + + #region Add + [Test] + public void AddComment_ReturnsOkObjectResult_WhenCommentIsSuccessfullyCreated() + { + Guid id = Guid.NewGuid(); + CreateCommentWebModel createCommentWebModel = new() + { + Message = MESSAGE + }; + CreateCommentServiceModel createCommentServiceModel = new() + { + Message = MESSAGE + }; + + this._mapperMock + .Setup(p => p.Map<CreateCommentServiceModel>(It.IsAny<CreateCommentWebModel>())) + .Returns(createCommentServiceModel); + this._commentServiceMock + .Setup(p => p.AddComment(It.IsAny<CreateCommentServiceModel>())) + .ReturnsAsync(id); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._commentServiceMock + .Setup(p => p.ValidateJwtForCreating(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._commentController.AddComment(Guid.NewGuid(), createCommentWebModel, null).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + var splitted = (result as OkObjectResult).Value + .ToString() + .Split('{', '}', '=', ' ') + .Where(x => !string.IsNullOrEmpty(x)) + .ToArray(); + + Guid resultId = Guid.Parse(splitted[1]); + + Assert.AreEqual(id, resultId); + } + + [Test] + public void AddComment_ReturnsBadRequestObjectResult_WhenCommentIsNotCreatedSuccessfully() + { + CreateCommentWebModel createCommentWebModel = new() + { + Message = MESSAGE + }; + CreateCommentServiceModel createCommentServiceModel = new() + { + Message = MESSAGE + }; + string errorMessage = $"Could not create comment!"; + + + this._mapperMock + .Setup(p => p.Map<CreateCommentServiceModel>(It.IsAny<CreateCommentWebModel>())) + .Returns(createCommentServiceModel); + this._commentServiceMock + .Setup(p => p.AddComment(It.IsAny<CreateCommentServiceModel>())) + .ReturnsAsync(Guid.Empty); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._commentServiceMock + .Setup(p => p.ValidateJwtForCreating(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._commentController.AddComment(Guid.NewGuid(), createCommentWebModel, null).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultMessage = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(errorMessage, resultMessage); + } + + [Test] + public void AddComment_ReturnsUnauthorizedResult_WhenUserIsNotAuthorized() + { + CreateCommentWebModel createCommentWebModel = new() + { + Message = MESSAGE + }; + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._commentServiceMock + .Setup(p => p.ValidateJwtForCreating(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(false); + + IActionResult result = this._commentController.AddComment(Guid.NewGuid(), createCommentWebModel, null).Result; + + Assert.IsInstanceOf<UnauthorizedResult>(result); + } + #endregion + + #region Read + [Test] + public void GetById_ReturnsTheComment_WhenItExists() + { + Guid id = Guid.NewGuid(); + ReadCommentServiceModel readCommentServiceModel = new() + { + Message = MESSAGE + }; + ReadCommentWebModel readCommentWebModel = new() + { + Message = MESSAGE + }; + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._commentServiceMock + .Setup(p => p.GetCommentById(It.IsAny<Guid>())) + .ReturnsAsync(readCommentServiceModel); + this._mapperMock + .Setup(p => p.Map<ReadCommentWebModel>(It.IsAny<ReadCommentServiceModel>())) + .Returns(readCommentWebModel); + + IActionResult result = this._commentController.GetCommentById(id).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + OkObjectResult okObjectResult = result as OkObjectResult; + ReadCommentWebModel resultModel = okObjectResult.Value as ReadCommentWebModel; + + Assert.AreEqual(MESSAGE, resultModel.Message); + } + #endregion + + #region Update + [Test] + public void Update_ShouldReturnOkResult_WhenCommentIsUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + UpdateCommentWebModel updateCommentWebModel = new() + { + NewMessage = MESSAGE + }; + UpdateCommentServiceModel updateCommentServiceModel = new() + { + NewMessage = MESSAGE + }; + + this._commentServiceMock + .Setup(p => p.UpdateComment(It.IsAny<UpdateCommentServiceModel>())) + .ReturnsAsync(id); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._commentServiceMock + .Setup(p => p.ValidateJwtForComment(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + this._mapperMock + .Setup(p => p.Map<UpdateCommentServiceModel>(It.IsAny<UpdateCommentWebModel>())) + .Returns(updateCommentServiceModel); + + IActionResult result = this._commentController.UpdateComment(Guid.Empty, updateCommentWebModel, null).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + OkObjectResult okObjectResult = result as OkObjectResult; + object resultModel = okObjectResult.Value; + string[] resultAsString = resultModel.ToString().Split(' ').ToArray(); + + Assert.AreEqual(id.ToString(), resultAsString[3]); + } + + [Test] + public void Update_ShouldReturnBadObjectResult_WhenCommentIsNotUpdatedSuccessfully() + { + string message = "Unable to update comment!"; + UpdateCommentWebModel updateCommentWebModel = new() + { + NewMessage = MESSAGE + }; + UpdateCommentServiceModel updateCommentServiceModel = new() + { + NewMessage = MESSAGE + }; + + this._commentServiceMock + .Setup(p => p.UpdateComment(It.IsAny<UpdateCommentServiceModel>())) + .ReturnsAsync(Guid.Empty); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._commentServiceMock + .Setup(p => p.ValidateJwtForComment(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + this._mapperMock + .Setup(p => p.Map<UpdateCommentServiceModel>(It.IsAny<UpdateCommentWebModel>())) + .Returns(updateCommentServiceModel); + + IActionResult result = this._commentController.UpdateComment(Guid.Empty, updateCommentWebModel, null).Result; + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + + [Test] + public void Update_ShouldReturnUnauthorizedResult_WhenUserIsNotAuthorized() + { + UpdateCommentWebModel updateCommentWebModel = new() + { + NewMessage = MESSAGE + }; + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(false); + // this.CommentServiceMock.Setup(p => p.ValidateJwtForComment(It.IsAny<Guid>(), It.IsAny<string>())).ReturnsAsync(false)); + + IActionResult result = this._commentController.UpdateComment(Guid.Empty, updateCommentWebModel, null).Result; + + Assert.IsInstanceOf<UnauthorizedResult>(result); + } + #endregion + + #region Delete + [Test] + public void Delete_ReturnsOkResult_WhenCommentIsDeletedSuccessfully() + { + Guid id = Guid.NewGuid(); + + this._commentServiceMock + .Setup(p => p.DeleteComment(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._commentServiceMock + .Setup(p => p.ValidateJwtForComment(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._commentController.DeleteComment(id, null).Result; + + Assert.IsInstanceOf<OkResult>(result); + } + + [Test] + public void DeleteComment_ReturnsBadRequestObjectResult_WhenCommentIsNotDeletedSuccessfully() + { + string message = "Could not delete Comment"; + Guid id = Guid.NewGuid(); + + this._commentServiceMock + .Setup(p => p.DeleteComment(It.IsAny<Guid>())) + .ReturnsAsync(false); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._commentServiceMock + .Setup(p => p.ValidateJwtForComment(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._commentController.DeleteComment(id, null).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + + [Test] + public void DeleteComment_ReturnsUnauthorizedResult_WhenUserIsNotAuthorized() + { + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._commentServiceMock + .Setup(p => p.ValidateJwtForComment(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(false); + + IActionResult result = this._commentController.DeleteComment(Guid.Empty, null).Result; + + Assert.IsInstanceOf<UnauthorizedResult>(result); + } + #endregion + } +} diff --git a/src/Web/DevHive.Web.Tests/DevHive.Web.Tests.csproj b/src/Web/DevHive.Web.Tests/DevHive.Web.Tests.csproj new file mode 100644 index 0000000..49a9173 --- /dev/null +++ b/src/Web/DevHive.Web.Tests/DevHive.Web.Tests.csproj @@ -0,0 +1,22 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>net5.0</TargetFramework> + <IsPackable>false</IsPackable> + </PropertyGroup> + <ItemGroup> + <PackageReference Include="Moq" Version="4.16.1"/> + <PackageReference Include="NUnit" Version="3.13.1"/> + <PackageReference Include="NUnit3TestAdapter" Version="3.17.0"/> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1"/> + <PackageReference Include="SonarAnalyzer.CSharp" Version="8.20.0.28934"/> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\DevHive.Web\DevHive.Web.csproj"/> + <ProjectReference Include="..\..\Common\DevHive.Common\DevHive.Common.csproj"/> + <ProjectReference Include="..\..\Common\DevHive.Common.Models\DevHive.Common.Models.csproj"/> + </ItemGroup> + <PropertyGroup> + <EnableNETAnalyzers>true</EnableNETAnalyzers> + <AnalysisLevel>latest</AnalysisLevel> + </PropertyGroup> +</Project>
\ No newline at end of file diff --git a/src/Web/DevHive.Web.Tests/FeedController.Tests.cs b/src/Web/DevHive.Web.Tests/FeedController.Tests.cs new file mode 100644 index 0000000..2f43b6c --- /dev/null +++ b/src/Web/DevHive.Web.Tests/FeedController.Tests.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Services.Interfaces; +using DevHive.Services.Models; +using DevHive.Web.Controllers; +using DevHive.Web.Models.Comment; +using DevHive.Web.Models.Feed; +using DevHive.Web.Models.Post; +using Microsoft.AspNetCore.Mvc; +using Moq; +using NUnit.Framework; + +namespace DevHive.Web.Tests +{ + [TestFixture] + public class FeedControllerTests + { + private Mock<IFeedService> _feedServiceMock; + private Mock<IMapper> _mapperMock; + private FeedController _feedController; + + #region SetUp + [SetUp] + public void SetUp() + { + this._feedServiceMock = new Mock<IFeedService>(); + this._mapperMock = new Mock<IMapper>(); + this._feedController = new FeedController(this._feedServiceMock.Object, this._mapperMock.Object); + } + #endregion + + #region GetPosts + [Test] + public async Task GetPosts_ReturnsOkObjectResultWithCorrectReadPageWebModel_WhenPostsExist() + { + GetPageWebModel getPageWebModel = new(); + GetPageServiceModel getPageServiceModel = new(); + ReadPageServiceModel readPageServiceModel = new(); + ReadPageWebModel readPageWebModel = new() + { + Posts = new() + { + new ReadPostWebModel(), + new ReadPostWebModel(), + new ReadPostWebModel() + } + }; + + this._feedServiceMock + .Setup(p => p.GetPage(It.IsAny<GetPageServiceModel>())) + .ReturnsAsync(readPageServiceModel); + this._mapperMock + .Setup(p => p.Map<GetPageServiceModel>(It.IsAny<GetPageWebModel>())) + .Returns(getPageServiceModel); + this._mapperMock + .Setup(p => p.Map<ReadPageWebModel>(It.IsAny<ReadPageServiceModel>())) + .Returns(readPageWebModel); + + IActionResult result = await this._feedController.GetPosts(Guid.Empty, getPageWebModel); + + Assert.IsInstanceOf<OkObjectResult>(result); + + OkObjectResult okObjectResult = result as OkObjectResult; + ReadPageWebModel resultModel = okObjectResult.Value as Models.Comment.ReadPageWebModel; + + Assert.AreEqual(3, resultModel.Posts.Count); + } + #endregion + + #region GetUserPosts + [Test] + public async Task GetUserPosts_GetsPostsOfUser_WhenTheyExist() + { + GetPageWebModel getPageWebModel = new(); + GetPageServiceModel getPageServiceModel = new(); + ReadPageServiceModel readPageServiceModel = new(); + ReadPageWebModel readPageWebModel = new() + { + Posts = new() + { + new ReadPostWebModel(), + new ReadPostWebModel(), + new ReadPostWebModel() + } + }; + + this._feedServiceMock + .Setup(p => p.GetUserPage(It.IsAny<GetPageServiceModel>())) + .ReturnsAsync(readPageServiceModel); + this._mapperMock + .Setup(p => p.Map<GetPageServiceModel>(It.IsAny<GetPageWebModel>())) + .Returns(getPageServiceModel); + this._mapperMock + .Setup(p => p.Map<ReadPageWebModel>(It.IsAny<ReadPageServiceModel>())) + .Returns(readPageWebModel); + + IActionResult result = await this._feedController.GetUserPosts(null, getPageWebModel); + + Assert.IsInstanceOf<OkObjectResult>(result); + + OkObjectResult okObjectResult = result as OkObjectResult; + ReadPageWebModel resultModel = okObjectResult.Value as Models.Comment.ReadPageWebModel; + + Assert.AreEqual(3, resultModel.Posts.Count); + } + #endregion + } +} diff --git a/src/Web/DevHive.Web.Tests/LanguageController.Tests.cs b/src/Web/DevHive.Web.Tests/LanguageController.Tests.cs new file mode 100644 index 0000000..52e07fa --- /dev/null +++ b/src/Web/DevHive.Web.Tests/LanguageController.Tests.cs @@ -0,0 +1,224 @@ +using System; +using System.Linq; +using AutoMapper; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.Language; +using DevHive.Web.Controllers; +using DevHive.Web.Models.Language; +using Microsoft.AspNetCore.Mvc; +using Moq; +using NUnit.Framework; + +namespace DevHive.Web.Tests +{ + [TestFixture] + public class LanguageControllerTests + { + const string NAME = "Gosho Trapov"; + private Mock<ILanguageService> _languageServiceMock; + private Mock<IMapper> _mapperMock; + private LanguageController _languageController; + + [SetUp] + public void SetUp() + { + this._languageServiceMock = new Mock<ILanguageService>(); + this._mapperMock = new Mock<IMapper>(); + this._languageController = new LanguageController(this._languageServiceMock.Object, this._mapperMock.Object); + } + + #region Create + [Test] + public void CreateLanguage_ReturnsOkObjectResult_WhenLanguageIsSuccessfullyCreated() + { + CreateLanguageWebModel createLanguageWebModel = new() + { + Name = NAME + }; + CreateLanguageServiceModel createLanguageServiceModel = new() + { + Name = NAME + }; + Guid id = Guid.NewGuid(); + + this._mapperMock + .Setup(p => p.Map<CreateLanguageServiceModel>(It.IsAny<CreateLanguageWebModel>())) + .Returns(createLanguageServiceModel); + this._languageServiceMock + .Setup(p => p.CreateLanguage(It.IsAny<CreateLanguageServiceModel>())) + .ReturnsAsync(id); + + IActionResult result = this._languageController.Create(createLanguageWebModel).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + var splitted = (result as OkObjectResult).Value + .ToString() + .Split('{', '}', '=', ' ') + .Where(x => !string.IsNullOrEmpty(x)) + .ToArray(); + + Guid resultId = Guid.Parse(splitted[1]); + + Assert.AreEqual(id, resultId); + } + + [Test] + public void CreateLanguage_ReturnsBadRequestObjectResult_WhenLanguageIsNotCreatedSuccessfully() + { + CreateLanguageWebModel createTechnologyWebModel = new() + { + Name = NAME + }; + CreateLanguageServiceModel createTechnologyServiceModel = new() + { + Name = NAME + }; + Guid id = Guid.Empty; + string errorMessage = $"Could not create language {NAME}"; + + this._mapperMock + .Setup(p => p.Map<CreateLanguageServiceModel>(It.IsAny<CreateLanguageWebModel>())) + .Returns(createTechnologyServiceModel); + this._languageServiceMock + .Setup(p => p.CreateLanguage(It.IsAny<CreateLanguageServiceModel>())) + .ReturnsAsync(id); + + IActionResult result = this._languageController.Create(createTechnologyWebModel).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultMessage = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(errorMessage, resultMessage); + } + #endregion + + #region Read + [Test] + public void GetById_ReturnsTheLanguage_WhenItExists() + { + Guid id = Guid.NewGuid(); + + ReadLanguageServiceModel readLanguageServiceModel = new() + { + Name = NAME + }; + ReadLanguageWebModel readLanguageWebModel = new() + { + Name = NAME + }; + + this._languageServiceMock + .Setup(p => p.GetLanguageById(It.IsAny<Guid>())) + .ReturnsAsync(readLanguageServiceModel); + this._mapperMock + .Setup(p => p.Map<ReadLanguageWebModel>(It.IsAny<ReadLanguageServiceModel>())) + .Returns(readLanguageWebModel); + + IActionResult result = this._languageController.GetById(id).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + OkObjectResult okObjectResult = result as OkObjectResult; + ReadLanguageWebModel resultModel = okObjectResult.Value as Models.Language.ReadLanguageWebModel; + + Assert.AreEqual(NAME, resultModel.Name); + } + #endregion + + #region Update + [Test] + public void Update_ShouldReturnOkResult_WhenLanguageIsUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + UpdateLanguageWebModel updateLanguageWebModel = new() + { + Name = NAME + }; + UpdateLanguageServiceModel updateLanguageServiceModel = new() + { + Name = NAME + }; + + this._languageServiceMock + .Setup(p => p.UpdateLanguage(It.IsAny<UpdateLanguageServiceModel>())) + .ReturnsAsync(true); + this._mapperMock + .Setup(p => p.Map<UpdateLanguageServiceModel>(It.IsAny<UpdateLanguageWebModel>())) + .Returns(updateLanguageServiceModel); + + IActionResult result = this._languageController.Update(id, updateLanguageWebModel).Result; + + Assert.IsInstanceOf<OkResult>(result); + } + + [Test] + public void Update_ShouldReturnBadObjectResult_WhenLanguageIsNotUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + string message = "Could not update Language"; + UpdateLanguageWebModel updateLanguageWebModel = new() + { + Name = NAME + }; + UpdateLanguageServiceModel updateLanguageServiceModel = new() + { + Name = NAME + }; + + this._languageServiceMock + .Setup(p => p.UpdateLanguage(It.IsAny<UpdateLanguageServiceModel>())) + .ReturnsAsync(false); + this._mapperMock + .Setup(p => p.Map<UpdateLanguageServiceModel>(It.IsAny<UpdateLanguageWebModel>())) + .Returns(updateLanguageServiceModel); + + IActionResult result = this._languageController.Update(id, updateLanguageWebModel).Result; + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + #endregion + + #region Delete + [Test] + public void Delete_ReturnsOkResult_WhenLanguageIsDeletedSuccessfully() + { + Guid id = Guid.NewGuid(); + + this._languageServiceMock + .Setup(p => p.DeleteLanguage(It.IsAny<Guid>())) + .ReturnsAsync(true); + + IActionResult result = this._languageController.Delete(id).Result; + + Assert.IsInstanceOf<OkResult>(result); + } + + [Test] + public void Delet_ReturnsBadRequestObjectResult_WhenLanguageIsNotDeletedSuccessfully() + { + string message = "Could not delete Language"; + Guid id = Guid.NewGuid(); + + this._languageServiceMock + .Setup(p => p.DeleteLanguage(It.IsAny<Guid>())) + .ReturnsAsync(false); + + IActionResult result = this._languageController.Delete(id).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + #endregion + } +} diff --git a/src/Web/DevHive.Web.Tests/PostController.Tests.cs b/src/Web/DevHive.Web.Tests/PostController.Tests.cs new file mode 100644 index 0000000..df95191 --- /dev/null +++ b/src/Web/DevHive.Web.Tests/PostController.Tests.cs @@ -0,0 +1,309 @@ +using System; +using System.Linq; +using AutoMapper; +using DevHive.Common.Jwt.Interfaces; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.Post; +using DevHive.Web.Controllers; +using DevHive.Web.Models.Post; +using Microsoft.AspNetCore.Mvc; +using Moq; +using NUnit.Framework; + +namespace DevHive.Web.Tests +{ + [TestFixture] + public class PostControllerTests + { + const string MESSAGE = "Gosho Trapov"; + private Mock<IPostService> _postServiceMock; + private Mock<IMapper> _mapperMock; + private Mock<IJwtService> _jwtServiceMock; + private PostController _postController; + + [SetUp] + public void SetUp() + { + this._postServiceMock = new Mock<IPostService>(); + this._mapperMock = new Mock<IMapper>(); + this._jwtServiceMock = new Mock<IJwtService>(); + this._postController = new PostController(this._postServiceMock.Object, this._mapperMock.Object, this._jwtServiceMock.Object); + } + + #region Create + [Test] + public void CreatePost_ReturnsOkObjectResult_WhenPostIsSuccessfullyCreated() + { + CreatePostWebModel createPostWebModel = new() + { + Message = MESSAGE + }; + CreatePostServiceModel createPostServiceModel = new() + { + Message = MESSAGE + }; + Guid id = Guid.NewGuid(); + + this._mapperMock + .Setup(p => p.Map<CreatePostServiceModel>(It.IsAny<CreatePostWebModel>())) + .Returns(createPostServiceModel); + this._postServiceMock + .Setup(p => p.CreatePost(It.IsAny<CreatePostServiceModel>())) + .ReturnsAsync(id); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._postServiceMock + .Setup(p => p.ValidateJwtForCreating(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._postController.Create(Guid.Empty, createPostWebModel, null).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + var splitted = (result as OkObjectResult).Value + .ToString() + .Split('{', '}', '=', ' ') + .Where(x => !string.IsNullOrEmpty(x)) + .ToArray(); + + Guid resultId = Guid.Parse(splitted[1]); + + Assert.AreEqual(id, resultId); + } + + [Test] + public void CreatePost_ReturnsBadRequestObjectResult_WhenPostIsNotCreatedSuccessfully() + { + CreatePostWebModel createTechnologyWebModel = new() + { + Message = MESSAGE + }; + CreatePostServiceModel createTechnologyServiceModel = new() + { + Message = MESSAGE + }; + Guid id = Guid.Empty; + string errorMessage = $"Could not create post!"; + + this._mapperMock + .Setup(p => p.Map<CreatePostServiceModel>(It.IsAny<CreatePostWebModel>())) + .Returns(createTechnologyServiceModel); + this._postServiceMock + .Setup(p => p.CreatePost(It.IsAny<CreatePostServiceModel>())) + .ReturnsAsync(id); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._postServiceMock + .Setup(p => p.ValidateJwtForCreating(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._postController.Create(Guid.Empty, createTechnologyWebModel, null).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultMessage = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(errorMessage, resultMessage); + } + + [Test] + public void CreatePost_ReturnsUnauthorizedResult_WhenUserIsNotAuthorized() + { + CreatePostWebModel createPostWebModel = new() + { + Message = MESSAGE + }; + + this._postServiceMock + .Setup(p => p.ValidateJwtForCreating(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(false); + + IActionResult result = this._postController.Create(Guid.NewGuid(), createPostWebModel, null).Result; + + Assert.IsInstanceOf<UnauthorizedResult>(result); + } + #endregion + + #region Read + [Test] + public void GetById_ReturnsThePost_WhenItExists() + { + Guid id = Guid.NewGuid(); + + ReadPostServiceModel readPostServiceModel = new() + { + Message = MESSAGE + }; + ReadPostWebModel readPostWebModel = new() + { + Message = MESSAGE + }; + + this._postServiceMock + .Setup(p => p.GetPostById(It.IsAny<Guid>())) + .ReturnsAsync(readPostServiceModel); + this._mapperMock + .Setup(p => p.Map<ReadPostWebModel>(It.IsAny<ReadPostServiceModel>())) + .Returns(readPostWebModel); + + IActionResult result = this._postController.GetById(id).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + OkObjectResult okObjectResult = result as OkObjectResult; + ReadPostWebModel resultModel = okObjectResult.Value as Models.Post.ReadPostWebModel; + + Assert.AreEqual(MESSAGE, resultModel.Message); + } + #endregion + + #region Update + [Test] + public void Update_ShouldReturnOkResult_WhenPostIsUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + UpdatePostWebModel updatePostWebModel = new() + { + NewMessage = MESSAGE + }; + UpdatePostServiceModel updatePostServiceModel = new() + { + NewMessage = MESSAGE + }; + + this._postServiceMock + .Setup(p => p.UpdatePost(It.IsAny<UpdatePostServiceModel>())) + .ReturnsAsync(id); + this._mapperMock + .Setup(p => p.Map<UpdatePostServiceModel>(It.IsAny<UpdatePostWebModel>())) + .Returns(updatePostServiceModel); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._postServiceMock + .Setup(p => p.ValidateJwtForPost(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._postController.Update(id, updatePostWebModel, null).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + } + + [Test] + public void Update_ShouldReturnBadObjectResult_WhenPostIsNotUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + string message = "Could not update post!"; + UpdatePostWebModel updatePostWebModel = new() + { + NewMessage = MESSAGE + }; + UpdatePostServiceModel updatePostServiceModel = new() + { + NewMessage = MESSAGE + }; + + this._postServiceMock + .Setup(p => p.UpdatePost(It.IsAny<UpdatePostServiceModel>())) + .ReturnsAsync(Guid.Empty); + this._mapperMock + .Setup(p => p.Map<UpdatePostServiceModel>(It.IsAny<UpdatePostWebModel>())) + .Returns(updatePostServiceModel); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._postServiceMock + .Setup(p => p.ValidateJwtForPost(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._postController.Update(id, updatePostWebModel, null).Result; + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + + [Test] + public void Update_ShouldReturnUnauthorizedResult_WhenUserIsNotAuthorized() + { + UpdatePostWebModel updatePostWebModel = new() + { + NewMessage = MESSAGE + }; + + this._postServiceMock + .Setup(p => p.ValidateJwtForPost(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(false); + + IActionResult result = this._postController.Update(Guid.Empty, updatePostWebModel, null).Result; + + Assert.IsInstanceOf<UnauthorizedResult>(result); + } + #endregion + + #region Delete + [Test] + public void Delete_ReturnsOkResult_WhenPostIsDeletedSuccessfully() + { + Guid id = Guid.NewGuid(); + + this._postServiceMock + .Setup(p => p.DeletePost(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._postServiceMock + .Setup(p => p.ValidateJwtForPost(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._postController.Delete(id, null).Result; + + Assert.IsInstanceOf<OkResult>(result); + } + + [Test] + public void Delete_ReturnsBadRequestObjectResult_WhenPostIsNotDeletedSuccessfully() + { + string message = "Could not delete Post"; + Guid id = Guid.NewGuid(); + + this._postServiceMock + .Setup(p => p.DeletePost(It.IsAny<Guid>())) + .ReturnsAsync(false); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._postServiceMock + .Setup(p => p.ValidateJwtForPost(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._postController.Delete(id, null).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + + [Test] + public void DeletePost_ReturnsUnauthorizedResult_WhenUserIsNotAuthorized() + { + this._postServiceMock + .Setup(p => p.ValidateJwtForPost(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(false); + + IActionResult result = this._postController.Delete(Guid.Empty, null).Result; + + Assert.IsInstanceOf<UnauthorizedResult>(result); + } + #endregion + } +} diff --git a/src/Web/DevHive.Web.Tests/RatingController.Tests.cs b/src/Web/DevHive.Web.Tests/RatingController.Tests.cs new file mode 100644 index 0000000..c7340a6 --- /dev/null +++ b/src/Web/DevHive.Web.Tests/RatingController.Tests.cs @@ -0,0 +1,299 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Common.Jwt.Interfaces; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.Rating; +using DevHive.Web.Controllers; +using DevHive.Web.Models.Rating; +using Microsoft.AspNetCore.Mvc; +using Moq; +using NUnit.Framework; + +namespace DevHive.Web.Tests +{ + [TestFixture] + public class RatingControllerTests + { + private Mock<IRatingService> _ratingServiceMock; + private Mock<IMapper> _mapperMock; + private Mock<IJwtService> _jwtServiceMock; + private RatingController _ratingController; + + [SetUp] + public void SetUp() + { + this._ratingServiceMock = new Mock<IRatingService>(); + this._mapperMock = new Mock<IMapper>(); + this._jwtServiceMock = new Mock<IJwtService>(); + this._ratingController = new RatingController(this._ratingServiceMock.Object, this._mapperMock.Object, this._jwtServiceMock.Object); + } + + #region Create + [Test] + public void CreateRating_ReturnsOkObjectResult_WhenRatingIsSuccessfullyCreated() + { + Guid postId = Guid.NewGuid(); + CreateRatingWebModel createRatingWebModel = new CreateRatingWebModel + { + PostId = postId, + IsLike = true + }; + CreateRatingServiceModel createRatingServiceModel = new CreateRatingServiceModel + { + PostId = postId, + IsLike = true + }; + Guid ratingId = Guid.NewGuid(); + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._mapperMock + .Setup(p => p.Map<CreateRatingServiceModel>(It.IsAny<CreateRatingWebModel>())) + .Returns(createRatingServiceModel); + this._ratingServiceMock + .Setup(p => p.RatePost(It.IsAny<CreateRatingServiceModel>())) + .ReturnsAsync(ratingId); + + IActionResult result = this._ratingController.RatePost(Guid.Empty, createRatingWebModel, String.Empty).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + var splitted = (result as OkObjectResult).Value + .ToString() + .Split('{', '}', '=', ' ') + .Where(x => !string.IsNullOrEmpty(x)) + .ToArray(); + + Guid resultId = Guid.Parse(splitted[1]); + + Assert.AreEqual(ratingId, resultId); + } + + [Test] + public void CreateRating_ReturnsBadRequestResult_WhenRatingIsNotSuccessfullyCreated() + { + Guid postId = Guid.NewGuid(); + CreateRatingWebModel createRatingWebModel = new CreateRatingWebModel + { + PostId = postId, + IsLike = true + }; + CreateRatingServiceModel createRatingServiceModel = new CreateRatingServiceModel + { + PostId = postId, + IsLike = true + }; + Guid ratingId = Guid.NewGuid(); + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._mapperMock + .Setup(p => p.Map<CreateRatingServiceModel>(It.IsAny<CreateRatingWebModel>())) + .Returns(createRatingServiceModel); + this._ratingServiceMock + .Setup(p => p.RatePost(It.IsAny<CreateRatingServiceModel>())) + .ReturnsAsync(Guid.Empty); + + IActionResult result = this._ratingController.RatePost(Guid.Empty, createRatingWebModel, String.Empty).Result; + + Assert.IsInstanceOf<BadRequestResult>(result); + } + #endregion + + #region Read + [Test] + public void GetRatingById_ReturnsTheRating_WhenItExists() + { + Guid id = Guid.NewGuid(); + Guid userId = Guid.NewGuid(); + Guid postId = Guid.NewGuid(); + ReadRatingWebModel readRatingWebModel = new ReadRatingWebModel + { + Id = id, + UserId = userId, + PostId = postId, + IsLike = true + }; + ReadRatingServiceModel readRatingServiceModel = new ReadRatingServiceModel + { + Id = id, + UserId = userId, + PostId = postId, + IsLike = true + }; + + this._mapperMock + .Setup(p => p.Map<ReadRatingServiceModel>(It.IsAny<ReadRatingWebModel>())) + .Returns(readRatingServiceModel); + this._ratingServiceMock + .Setup(p => p.GetRatingById(It.IsAny<Guid>())) + .ReturnsAsync(readRatingServiceModel); + + IActionResult result = this._ratingController.GetRatingById(id).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + } + + [Test] + public void GetRatingByUserAndPost_ReturnsTheRating_WhenItExists() + { + Guid id = Guid.NewGuid(); + Guid userId = Guid.NewGuid(); + Guid postId = Guid.NewGuid(); + ReadRatingWebModel readRatingWebModel = new ReadRatingWebModel + { + Id = id, + UserId = userId, + PostId = postId, + IsLike = true + }; + ReadRatingServiceModel readRatingServiceModel = new ReadRatingServiceModel + { + Id = id, + UserId = userId, + PostId = postId, + IsLike = true + }; + + this._mapperMock + .Setup(p => p.Map<ReadRatingServiceModel>(It.IsAny<ReadRatingWebModel>())) + .Returns(readRatingServiceModel); + this._ratingServiceMock + .Setup(p => p.GetRatingByPostAndUser(It.IsAny<Guid>(), It.IsAny<Guid>())) + .ReturnsAsync(readRatingServiceModel); + + IActionResult result = this._ratingController.GetRatingByUserAndPost(userId, postId).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + } + + #endregion + + #region Update + [Test] + public void Update_ShouldReturnOkResult_WhenRatingIsUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + Guid userId = Guid.NewGuid(); + Guid postId = Guid.NewGuid(); + UpdateRatingWebModel updateRatingWebModel = new UpdateRatingWebModel + { + IsLike = true + }; + UpdateRatingServiceModel updateRatingServiceModel = new UpdateRatingServiceModel + { + Id = id, + UserId = userId, + PostId = postId, + IsLike = true + }; + ReadRatingWebModel readRatingWebModel = new ReadRatingWebModel + { + Id = id, + UserId = userId, + PostId = postId, + IsLike = true + }; + ReadRatingServiceModel readRatingServiceModel = new ReadRatingServiceModel + { + Id = id, + UserId = userId, + PostId = postId, + IsLike = true + }; + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._mapperMock + .Setup(p => p.Map<UpdateRatingServiceModel>(It.IsAny<UpdateRatingWebModel>())) + .Returns(updateRatingServiceModel); + this._mapperMock + .Setup(p => p.Map<ReadRatingWebModel>(It.IsAny<ReadRatingServiceModel>())) + .Returns(readRatingWebModel); + this._ratingServiceMock + .Setup(p => p.UpdateRating(It.IsAny<UpdateRatingServiceModel>())) + .ReturnsAsync(readRatingServiceModel); + + IActionResult result = this._ratingController.UpdateRating(userId, postId, updateRatingWebModel, String.Empty).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + } + + [Test] + public void Update_ShouldReturnBadObjectResult_WhenLanguageIsNotUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + UpdateRatingWebModel updateRatingWebModel = new UpdateRatingWebModel + { + IsLike = true + }; + UpdateRatingServiceModel updateRatingServiceModel = new UpdateRatingServiceModel + { + Id = id, + IsLike = true + }; + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._mapperMock + .Setup(p => p.Map<UpdateRatingServiceModel>(It.IsAny<UpdateRatingWebModel>())) + .Returns(updateRatingServiceModel); + this._ratingServiceMock + .Setup(p => p.UpdateRating(It.IsAny<UpdateRatingServiceModel>())) + .Returns(Task.FromResult<ReadRatingServiceModel>(null)); + + IActionResult result = this._ratingController.UpdateRating(Guid.Empty, Guid.Empty, updateRatingWebModel, String.Empty).Result; + + Assert.IsInstanceOf<BadRequestResult>(result); + } + #endregion + + #region Delete + [Test] + public void Delete_ReturnsOkResult_WhenRatingIsDeletedSuccessfully() + { + Guid id = Guid.NewGuid(); + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._ratingServiceMock + .Setup(p => p.DeleteRating(It.IsAny<Guid>())) + .ReturnsAsync(true); + + IActionResult result = this._ratingController.DeleteRating(Guid.Empty, Guid.Empty, String.Empty).Result; + + Assert.IsInstanceOf<OkResult>(result); + } + + [Test] + public void Delete_ReturnsBadRequestObjectResult_WhenRatingIsNotDeletedSuccessfully() + { + string message = "Could not delete Rating"; + Guid id = Guid.NewGuid(); + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._ratingServiceMock + .Setup(p => p.DeleteRating(It.IsAny<Guid>())) + .ReturnsAsync(false); + + IActionResult result = this._ratingController.DeleteRating(Guid.Empty, Guid.Empty, String.Empty).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + #endregion + } +} diff --git a/src/Web/DevHive.Web.Tests/RoleController.Tests.cs b/src/Web/DevHive.Web.Tests/RoleController.Tests.cs new file mode 100644 index 0000000..d1c3381 --- /dev/null +++ b/src/Web/DevHive.Web.Tests/RoleController.Tests.cs @@ -0,0 +1,224 @@ +using System; +using System.Linq; +using AutoMapper; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.Role; +using DevHive.Web.Controllers; +using DevHive.Web.Models.Role; +using Microsoft.AspNetCore.Mvc; +using Moq; +using NUnit.Framework; + +namespace DevHive.Web.Tests +{ + [TestFixture] + public class RoleControllerTests + { + const string NAME = "Gosho Trapov"; + private Mock<IRoleService> _roleServiceMock; + private Mock<IMapper> _mapperMock; + private RoleController _roleController; + + [SetUp] + public void SetUp() + { + this._roleServiceMock = new Mock<IRoleService>(); + this._mapperMock = new Mock<IMapper>(); + this._roleController = new RoleController(this._roleServiceMock.Object, this._mapperMock.Object); + } + + #region Create + [Test] + public void CreateRole_ReturnsOkObjectResult_WhenRoleIsSuccessfullyCreated() + { + CreateRoleWebModel createRoleWebModel = new() + { + Name = NAME + }; + CreateRoleServiceModel createRoleServiceModel = new() + { + Name = NAME + }; + Guid id = Guid.NewGuid(); + + this._mapperMock + .Setup(p => p.Map<CreateRoleServiceModel>(It.IsAny<CreateRoleWebModel>())) + .Returns(createRoleServiceModel); + this._roleServiceMock + .Setup(p => p.CreateRole(It.IsAny<CreateRoleServiceModel>())) + .ReturnsAsync(id); + + IActionResult result = this._roleController.Create(createRoleWebModel).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + var splitted = (result as OkObjectResult).Value + .ToString() + .Split('{', '}', '=', ' ') + .Where(x => !string.IsNullOrEmpty(x)) + .ToArray(); + + Guid resultId = Guid.Parse(splitted[1]); + + Assert.AreEqual(id, resultId); + } + + [Test] + public void CreateRole_ReturnsBadRequestObjectResult_WhenRoleIsNotCreatedSuccessfully() + { + CreateRoleWebModel createTechnologyWebModel = new() + { + Name = NAME + }; + CreateRoleServiceModel createTechnologyServiceModel = new() + { + Name = NAME + }; + Guid id = Guid.Empty; + string errorMessage = $"Could not create role {NAME}"; + + this._mapperMock + .Setup(p => p.Map<CreateRoleServiceModel>(It.IsAny<CreateRoleWebModel>())) + .Returns(createTechnologyServiceModel); + this._roleServiceMock + .Setup(p => p.CreateRole(It.IsAny<CreateRoleServiceModel>())) + .ReturnsAsync(id); + + IActionResult result = this._roleController.Create(createTechnologyWebModel).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultMessage = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(errorMessage, resultMessage); + } + #endregion + + #region Read + [Test] + public void GetById_ReturnsTheRole_WhenItExists() + { + Guid id = Guid.NewGuid(); + + RoleServiceModel roleServiceModel = new() + { + Name = NAME + }; + RoleWebModel roleWebModel = new() + { + Name = NAME + }; + + this._roleServiceMock + .Setup(p => p.GetRoleById(It.IsAny<Guid>())) + .ReturnsAsync(roleServiceModel); + this._mapperMock + .Setup(p => p.Map<RoleWebModel>(It.IsAny<RoleServiceModel>())) + .Returns(roleWebModel); + + IActionResult result = this._roleController.GetById(id).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + OkObjectResult okObjectResult = result as OkObjectResult; + RoleWebModel resultModel = okObjectResult.Value as RoleWebModel; + + Assert.AreEqual(NAME, resultModel.Name); + } + #endregion + + #region Update + [Test] + public void Update_ShouldReturnOkResult_WhenRoleIsUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + UpdateRoleWebModel updateRoleWebModel = new() + { + Name = NAME + }; + UpdateRoleServiceModel updateRoleServiceModel = new() + { + Name = NAME + }; + + this._roleServiceMock + .Setup(p => p.UpdateRole(It.IsAny<UpdateRoleServiceModel>())) + .ReturnsAsync(true); + this._mapperMock + .Setup(p => p.Map<UpdateRoleServiceModel>(It.IsAny<UpdateRoleWebModel>())) + .Returns(updateRoleServiceModel); + + IActionResult result = this._roleController.Update(id, updateRoleWebModel).Result; + + Assert.IsInstanceOf<OkResult>(result); + } + + [Test] + public void Update_ShouldReturnBadObjectResult_WhenRoleIsNotUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + string message = "Could not update role!"; + UpdateRoleWebModel updateRoleWebModel = new() + { + Name = NAME + }; + UpdateRoleServiceModel updateRoleServiceModel = new() + { + Name = NAME + }; + + this._roleServiceMock + .Setup(p => p.UpdateRole(It.IsAny<UpdateRoleServiceModel>())) + .ReturnsAsync(false); + this._mapperMock + .Setup(p => p.Map<UpdateRoleServiceModel>(It.IsAny<UpdateRoleWebModel>())) + .Returns(updateRoleServiceModel); + + IActionResult result = this._roleController.Update(id, updateRoleWebModel).Result; + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + #endregion + + #region Delete + [Test] + public void Delete_ReturnsOkResult_WhenRoleIsDeletedSuccessfully() + { + Guid id = Guid.NewGuid(); + + this._roleServiceMock + .Setup(p => p.DeleteRole(It.IsAny<Guid>())) + .ReturnsAsync(true); + + IActionResult result = this._roleController.Delete(id).Result; + + Assert.IsInstanceOf<OkResult>(result); + } + + [Test] + public void Delete_ReturnsBadRequestObjectResult_WhenRoleIsNotDeletedSuccessfully() + { + string message = "Could not delete role!"; + Guid id = Guid.NewGuid(); + + this._roleServiceMock + .Setup(p => p.DeleteRole(It.IsAny<Guid>())) + .ReturnsAsync(false); + + IActionResult result = this._roleController.Delete(id).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + #endregion + } +} diff --git a/src/Web/DevHive.Web.Tests/TechnologyController.Tests.cs b/src/Web/DevHive.Web.Tests/TechnologyController.Tests.cs new file mode 100644 index 0000000..723fb73 --- /dev/null +++ b/src/Web/DevHive.Web.Tests/TechnologyController.Tests.cs @@ -0,0 +1,226 @@ +using AutoMapper; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.Technology; +using DevHive.Web.Controllers; +using DevHive.Web.Models.Technology; +using Microsoft.AspNetCore.Mvc; +using Moq; +using NUnit.Framework; +using System; +using System.Linq; + +namespace DevHive.Web.Tests +{ + [TestFixture] + public class TechnologyControllerTests + { + const string NAME = "Gosho Trapov"; + private Mock<ITechnologyService> _technologyServiceMock; + private Mock<IMapper> _mapperMock; + private TechnologyController _technologyController; + + #region SetUp + [SetUp] + public void SetUp() + { + this._technologyServiceMock = new Mock<ITechnologyService>(); + this._mapperMock = new Mock<IMapper>(); + this._technologyController = new TechnologyController(this._technologyServiceMock.Object, this._mapperMock.Object); + } + #endregion + + #region Create + [Test] + public void Create_ReturnsOkObjectResult_WhenTechnologyIsSuccessfullyCreated() + { + CreateTechnologyWebModel createTechnologyWebModel = new() + { + Name = NAME + }; + CreateTechnologyServiceModel createTechnologyServiceModel = new() + { + Name = NAME + }; + Guid id = Guid.NewGuid(); + + this._mapperMock + .Setup(p => p.Map<CreateTechnologyServiceModel>(It.IsAny<CreateTechnologyWebModel>())) + .Returns(createTechnologyServiceModel); + this._technologyServiceMock + .Setup(p => p.CreateTechnology(It.IsAny<CreateTechnologyServiceModel>())) + .ReturnsAsync(id); + + IActionResult result = this._technologyController.Create(createTechnologyWebModel).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + var splitted = (result as OkObjectResult).Value + .ToString() + .Split('{', '}', '=', ' ') + .Where(x => !string.IsNullOrEmpty(x)) + .ToArray(); + + Guid resultId = Guid.Parse(splitted[1]); + + Assert.AreEqual(id, resultId); + } + + [Test] + public void Create_ReturnsBadRequestObjectResult_WhenTechnologyIsNotCreatedSuccessfully() + { + CreateTechnologyWebModel createTechnologyWebModel = new() + { + Name = NAME + }; + CreateTechnologyServiceModel createTechnologyServiceModel = new() + { + Name = NAME + }; + Guid id = Guid.Empty; + string errorMessage = $"Could not create technology {NAME}"; + + this._mapperMock + .Setup(p => p.Map<CreateTechnologyServiceModel>(It.IsAny<CreateTechnologyWebModel>())) + .Returns(createTechnologyServiceModel); + this._technologyServiceMock + .Setup(p => p.CreateTechnology(It.IsAny<CreateTechnologyServiceModel>())) + .ReturnsAsync(id); + + IActionResult result = this._technologyController.Create(createTechnologyWebModel).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultMessage = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(errorMessage, resultMessage); + } + #endregion + + #region Read + [Test] + public void GetById_ReturnsTheThecnology_WhenItExists() + { + Guid id = Guid.NewGuid(); + + ReadTechnologyWebModel readTechnologyWebModel = new() + { + Name = NAME + }; + ReadTechnologyServiceModel readTechnologyServiceModel = new() + { + Name = NAME + }; + + this._technologyServiceMock + .Setup(p => p.GetTechnologyById(It.IsAny<Guid>())) + .ReturnsAsync(readTechnologyServiceModel); + this._mapperMock + .Setup(p => p.Map<ReadTechnologyWebModel>(It.IsAny<ReadTechnologyServiceModel>())) + .Returns(readTechnologyWebModel); + + IActionResult result = this._technologyController.GetById(id).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + OkObjectResult okObjectResult = result as OkObjectResult; + ReadTechnologyWebModel resultModel = okObjectResult.Value as Models.Technology.ReadTechnologyWebModel; + + Assert.AreEqual(NAME, resultModel.Name); + } + #endregion + + #region Update + [Test] + public void Update_ShouldReturnOkResult_WhenTechnologyIsUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + UpdateTechnologyWebModel updateTechnologyWebModel = new() + { + Name = NAME + }; + UpdateTechnologyServiceModel updateTechnologyServiceModel = new() + { + Name = NAME + }; + + this._technologyServiceMock + .Setup(p => p.UpdateTechnology(It.IsAny<UpdateTechnologyServiceModel>())) + .ReturnsAsync(true); + this._mapperMock + .Setup(p => p.Map<UpdateTechnologyServiceModel>(It.IsAny<UpdateTechnologyWebModel>())) + .Returns(updateTechnologyServiceModel); + + IActionResult result = this._technologyController.Update(id, updateTechnologyWebModel).Result; + + Assert.IsInstanceOf<OkResult>(result); + } + + [Test] + public void Update_ShouldReturnBadObjectResult_WhenTechnologyIsNotUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + string message = "Could not update Technology"; + UpdateTechnologyWebModel updateTechnologyWebModel = new() + { + Name = NAME + }; + UpdateTechnologyServiceModel updateTechnologyServiceModel = new() + { + Name = NAME + }; + + this._technologyServiceMock + .Setup(p => p.UpdateTechnology(It.IsAny<UpdateTechnologyServiceModel>())) + .ReturnsAsync(false); + this._mapperMock + .Setup(p => p.Map<UpdateTechnologyServiceModel>(It.IsAny<UpdateTechnologyWebModel>())) + .Returns(updateTechnologyServiceModel); + + IActionResult result = this._technologyController.Update(id, updateTechnologyWebModel).Result; + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + #endregion + + #region Delete + [Test] + public void Delete_ReturnsOkResult_WhenTechnologyIsDeletedSuccessfully() + { + Guid id = Guid.NewGuid(); + + this._technologyServiceMock + .Setup(p => p.DeleteTechnology(It.IsAny<Guid>())) + .ReturnsAsync(true); + + IActionResult result = this._technologyController.Delete(id).Result; + + Assert.IsInstanceOf<OkResult>(result); + } + + [Test] + public void Delet_ReturnsBadRequestObjectResult_WhenTechnologyIsNotDeletedSuccessfully() + { + string message = "Could not delete Technology"; + Guid id = Guid.NewGuid(); + + this._technologyServiceMock + .Setup(p => p.DeleteTechnology(It.IsAny<Guid>())) + .ReturnsAsync(false); + + IActionResult result = this._technologyController.Delete(id).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + #endregion + } +} diff --git a/src/Web/DevHive.Web.Tests/UserController.Tests.cs b/src/Web/DevHive.Web.Tests/UserController.Tests.cs new file mode 100644 index 0000000..3f88709 --- /dev/null +++ b/src/Web/DevHive.Web.Tests/UserController.Tests.cs @@ -0,0 +1,257 @@ +using System; +using AutoMapper; +using DevHive.Common.Jwt.Interfaces; +using DevHive.Common.Models.Identity; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.User; +using DevHive.Web.Controllers; +using DevHive.Web.Models.User; +using Microsoft.AspNetCore.Mvc; +using Moq; +using NUnit.Framework; + +namespace DevHive.Web.Tests +{ + [TestFixture] + public class UserControllerTests + { + const string USERNAME = "Gosho Trapov"; + private Mock<IUserService> _userServiceMock; + private Mock<IMapper> _mapperMock; + private Mock<IJwtService> _jwtServiceMock; + private UserController _userController; + + [SetUp] + public void SetUp() + { + this._userServiceMock = new Mock<IUserService>(); + this._mapperMock = new Mock<IMapper>(); + this._jwtServiceMock = new Mock<IJwtService>(); + this._userController = new UserController(this._userServiceMock.Object, this._mapperMock.Object, this._jwtServiceMock.Object); + } + + #region Create + [Test] + public void LoginUser_ReturnsOkObjectResult_WhenUserIsSuccessfullyLoggedIn() + { + LoginWebModel loginWebModel = new() + { + UserName = USERNAME + }; + LoginServiceModel loginServiceModel = new() + { + UserName = USERNAME + }; + string token = "goshotrapov"; + TokenModel tokenModel = new(token); + TokenWebModel tokenWebModel = new(token); + + this._mapperMock + .Setup(p => p.Map<LoginServiceModel>(It.IsAny<LoginWebModel>())) + .Returns(loginServiceModel); + this._mapperMock + .Setup(p => p.Map<TokenWebModel>(It.IsAny<TokenModel>())) + .Returns(tokenWebModel); + this._userServiceMock + .Setup(p => p.LoginUser(It.IsAny<LoginServiceModel>())) + .ReturnsAsync(tokenModel); + + IActionResult result = this._userController.Login(loginWebModel).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + var resultToken = ((result as OkObjectResult).Value as TokenWebModel).Token; + + Assert.AreEqual(token, resultToken); + } + + [Test] + public void RegisterUser_ReturnsOkObjectResult_WhenUserIsSuccessfullyRegistered() + { + RegisterWebModel registerWebModel = new() + { + UserName = USERNAME + }; + RegisterServiceModel registerServiceModel = new() + { + UserName = USERNAME + }; + string token = "goshotrapov"; + TokenModel tokenModel = new(token); + TokenWebModel tokenWebModel = new(token); + + this._mapperMock + .Setup(p => p.Map<RegisterServiceModel>(It.IsAny<RegisterWebModel>())) + .Returns(registerServiceModel); + this._mapperMock + .Setup(p => p.Map<TokenWebModel>(It.IsAny<TokenModel>())) + .Returns(tokenWebModel); + this._userServiceMock + .Setup(p => p.RegisterUser(It.IsAny<RegisterServiceModel>())) + .ReturnsAsync(tokenModel); + + IActionResult result = this._userController.Register(registerWebModel).Result; + + Assert.IsInstanceOf<CreatedResult>(result); + + CreatedResult createdResult = result as CreatedResult; + TokenWebModel resultModel = (createdResult.Value as TokenWebModel); + + Assert.AreEqual(token, resultModel.Token); + } + #endregion + + #region Read + [Test] + public void GetById_ReturnsTheUser_WhenItExists() + { + Guid id = Guid.NewGuid(); + + UserServiceModel userServiceModel = new() + { + UserName = USERNAME + }; + UserWebModel userWebModel = new() + { + UserName = USERNAME + }; + + this._userServiceMock + .Setup(p => p.GetUserById(It.IsAny<Guid>())) + .ReturnsAsync(userServiceModel); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._mapperMock + .Setup(p => p.Map<UserWebModel>(It.IsAny<UserServiceModel>())) + .Returns(userWebModel); + + IActionResult result = this._userController.GetById(id, null).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + OkObjectResult okObjectResult = result as OkObjectResult; + UserWebModel resultModel = okObjectResult.Value as UserWebModel; + + Assert.AreEqual(USERNAME, resultModel.UserName); + } + + [Test] + public void GetById_ReturnsUnauthorizedResult_WhenUserIsNotAuthorized() + { + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(false); + + IActionResult result = this._userController.GetById(Guid.NewGuid(), null).Result; + + Assert.IsInstanceOf<UnauthorizedResult>(result); + } + + [Test] + public void GetUser_ReturnsTheUser_WhenItExists() + { + UserWebModel userWebModel = new() + { + UserName = USERNAME + }; + UserServiceModel userServiceModel = new() + { + UserName = USERNAME + }; + + this._userServiceMock + .Setup(p => p.GetUserByUsername(It.IsAny<string>())) + .ReturnsAsync(userServiceModel); + this._mapperMock + .Setup(p => p.Map<UserWebModel>(It.IsAny<UserServiceModel>())) + .Returns(userWebModel); + + IActionResult result = this._userController.GetUser(null).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + OkObjectResult okObjectResult = result as OkObjectResult; + UserWebModel resultModel = okObjectResult.Value as UserWebModel; + + Assert.AreEqual(USERNAME, resultModel.UserName); + } + #endregion + + #region Update + [Test] + public void Update_ShouldReturnOkResult_WhenUserIsUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + UpdateUserWebModel updateUserWebModel = new() + { + UserName = USERNAME + }; + UpdateUserServiceModel updateUserServiceModel = new() + { + UserName = USERNAME + }; + UserServiceModel userServiceModel = new() + { + UserName = USERNAME + }; + + this._userServiceMock + .Setup(p => p.UpdateUser(It.IsAny<UpdateUserServiceModel>())) + .ReturnsAsync(userServiceModel); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._mapperMock + .Setup(p => p.Map<UpdateUserServiceModel>(It.IsAny<UpdateUserWebModel>())) + .Returns(updateUserServiceModel); + + IActionResult result = this._userController.Update(id, updateUserWebModel, null).Result; + + Assert.IsInstanceOf<AcceptedResult>(result); + } + #endregion + + #region Delete + [Test] + public void Delete_ReturnsOkResult_WhenUserIsDeletedSuccessfully() + { + Guid id = Guid.NewGuid(); + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._userServiceMock + .Setup(p => p.DeleteUser(It.IsAny<Guid>())) + .ReturnsAsync(true); + + IActionResult result = this._userController.Delete(id, null).Result; + + Assert.IsInstanceOf<OkResult>(result); + } + + [Test] + public void Delete_ReturnsBadRequestObjectResult_WhenUserIsNotDeletedSuccessfully() + { + string message = "Could not delete User"; + Guid id = Guid.NewGuid(); + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._userServiceMock + .Setup(p => p.DeleteUser(It.IsAny<Guid>())) + .ReturnsAsync(false); + + IActionResult result = this._userController.Delete(id, null).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + #endregion + } +} diff --git a/src/Web/DevHive.Web/Configurations/Extensions/ConfigureAutoMapper.cs b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureAutoMapper.cs new file mode 100644 index 0000000..cd5679f --- /dev/null +++ b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureAutoMapper.cs @@ -0,0 +1,26 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using AutoMapper; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +namespace DevHive.Web.Configurations.Extensions +{ + public static class ConfigureAutoMapper + { + public static void AutoMapperConfiguration(this IServiceCollection services) + { + services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); + } + + public static void UseAutoMapperConfiguration(this IApplicationBuilder app) + { + _ = new MapperConfiguration(cfg => + { + cfg.AllowNullCollections = true; + }); + } + } +} diff --git a/src/Web/DevHive.Web/Configurations/Extensions/ConfigureDatabase.cs b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureDatabase.cs new file mode 100644 index 0000000..b4c49b4 --- /dev/null +++ b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureDatabase.cs @@ -0,0 +1,98 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using DevHive.Data.Models; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Builder; +using System; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using DevHive.Data; +using System.Linq; +using System.Threading.Tasks; + +namespace DevHive.Web.Configurations.Extensions +{ + public static class DatabaseExtensions + { + public static void DatabaseConfiguration(this IServiceCollection services, IConfiguration configuration) + { + services.AddDbContext<DevHiveContext>(options => + { + // options.EnableSensitiveDataLogging(true); + options.UseNpgsql(configuration.GetConnectionString("DEV"), options => + { + options.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery); + }); + }); + + services.AddIdentity<User, Role>() + .AddRoles<Role>() + .AddEntityFrameworkStores<DevHiveContext>(); + + services.Configure<IdentityOptions>(options => + { + options.User.RequireUniqueEmail = true; + + options.Password.RequireDigit = true; + options.Password.RequiredLength = 5; + options.Password.RequiredUniqueChars = 0; + options.Password.RequireLowercase = false; + options.Password.RequireNonAlphanumeric = false; + options.Password.RequireUppercase = false; + + options.Lockout.AllowedForNewUsers = true; + options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5); + options.Lockout.MaxFailedAccessAttempts = 5; + }); + + services.AddAuthorization(options => + { + options.AddPolicy("User", options => + { + options.RequireAuthenticatedUser(); + options.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme); + options.RequireRole("User"); + }); + + options.AddPolicy("Administrator", options => + { + options.RequireAuthenticatedUser(); + options.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme); + options.RequireRole("Admin"); + }); + }); + } + + public static void UseDatabaseConfiguration(this IApplicationBuilder app) + { + app.UseHttpsRedirection(); + app.UseRouting(); + + app.UseAuthentication(); + app.UseAuthorization(); + + using var serviceScope = app.ApplicationServices.CreateScope(); + using var dbContext = serviceScope.ServiceProvider.GetRequiredService<DevHiveContext>(); + + dbContext.Database.Migrate(); + + var roleManager = (RoleManager<Role>)serviceScope.ServiceProvider.GetService(typeof(RoleManager<Role>)); + + if (!dbContext.Roles.Any(x => x.Name == Role.DefaultRole)) + { + Role defaultRole = new() { Name = Role.DefaultRole }; + + roleManager.CreateAsync(defaultRole).Wait(); + } + + if (!dbContext.Roles.Any(x => x.Name == Role.AdminRole)) + { + Role adminRole = new() { Name = Role.AdminRole }; + + roleManager.CreateAsync(adminRole).Wait(); + } + + dbContext.SaveChanges(); + } + } +} diff --git a/src/Web/DevHive.Web/Configurations/Extensions/ConfigureDependencyInjection.cs b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureDependencyInjection.cs new file mode 100644 index 0000000..f49a335 --- /dev/null +++ b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureDependencyInjection.cs @@ -0,0 +1,51 @@ +using System.Text; +using DevHive.Common.Jwt; +using DevHive.Common.Jwt.Interfaces; +using DevHive.Data.Interfaces; +using DevHive.Data.Repositories; +using DevHive.Services.Interfaces; +using DevHive.Services.Services; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace DevHive.Web.Configurations.Extensions +{ + public static class ConfigureDependencyInjection + { + public static void DependencyInjectionConfiguration(this IServiceCollection services, IConfiguration configuration) + { + services.AddTransient<ILanguageRepository, LanguageRepository>(); + services.AddTransient<IRoleRepository, RoleRepository>(); + services.AddTransient<ITechnologyRepository, TechnologyRepository>(); + services.AddTransient<IPostRepository, PostRepository>(); + services.AddTransient<ICommentRepository, CommentRepository>(); + services.AddTransient<IFeedRepository, FeedRepository>(); + services.AddTransient<IRatingRepository, RatingRepository>(); + services.AddTransient<IProfilePictureRepository, ProfilePictureRepository>(); + services.AddTransient<IUserRepository, UserRepository>(); + + services.AddTransient<ILanguageService, LanguageService>(); + services.AddTransient<IRoleService, RoleService>(); + services.AddTransient<ITechnologyService, TechnologyService>(); + services.AddTransient<IPostService, PostService>(); + services.AddTransient<ICommentService, CommentService>(); + services.AddTransient<IFeedService, FeedService>(); + services.AddTransient<IRatingService, RatingService>(); + services.AddTransient<IProfilePictureService, ProfilePictureService>(); + services.AddTransient<IUserService, UserService>(); + services.AddTransient<IFriendsService, FriendsService>(); + + services.AddTransient<ICloudService, CloudinaryService>(options => + new CloudinaryService( + cloudName: configuration.GetSection("Cloud").GetSection("cloudName").Value, + apiKey: configuration.GetSection("Cloud").GetSection("apiKey").Value, + apiSecret: configuration.GetSection("Cloud").GetSection("apiSecret").Value)); + + services.AddSingleton<IJwtService, JwtService>(options => + new JwtService( + signingKey: Encoding.ASCII.GetBytes(configuration.GetSection("Jwt").GetSection("signingKey").Value), + validationIssuer: configuration.GetSection("Jwt").GetSection("validationIssuer").Value, + audience: configuration.GetSection("Jwt").GetSection("audience").Value)); + } + } +} diff --git a/src/Web/DevHive.Web/Configurations/Extensions/ConfigureExceptionHandlerMiddleware.cs b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureExceptionHandlerMiddleware.cs new file mode 100644 index 0000000..6885f84 --- /dev/null +++ b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureExceptionHandlerMiddleware.cs @@ -0,0 +1,36 @@ +using DevHive.Web.Middleware; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Configuration; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Http; + +namespace DevHive.Web.Configurations.Extensions +{ + public static class ConfigureExceptionHandlerMiddleware + { + public static void ConfigureExceptionHandler(this IServiceCollection services, IConfiguration configuration) + { + services.Configure<ApiBehaviorOptions>(o => + { + o.InvalidModelStateResponseFactory = actionContext => + { + var problemDetails = new ValidationProblemDetails(actionContext.ModelState) + { + Status = StatusCodes.Status422UnprocessableEntity + }; + + return new UnprocessableEntityObjectResult(problemDetails) + { + ContentTypes = { "application/problem+json" } + }; + }; + }); + } + + public static void UseExceptionHandlerMiddlewareConfiguration(this IApplicationBuilder app) + { + app.UseMiddleware<ExceptionMiddleware>(); + } + } +} diff --git a/src/Web/DevHive.Web/Configurations/Extensions/ConfigureJwt.cs b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureJwt.cs new file mode 100644 index 0000000..18127bc --- /dev/null +++ b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureJwt.cs @@ -0,0 +1,46 @@ +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.IdentityModel.Tokens; + +namespace DevHive.Web.Configurations.Extensions +{ + public static class ConfigureJwt + { + public static void JWTConfiguration(this IServiceCollection services, IConfiguration configuration) + { + // Get key from appsettings.json + var signingKey = Encoding.ASCII.GetBytes(configuration + .GetSection("Jwt") + .GetSection("signingKey") + .Value); + + // Setup Jwt Authentication + services.AddAuthentication(x => + { + x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddJwtBearer(x => + { + x.Events = new JwtBearerEvents + { + OnTokenValidated = context => + { + return Task.CompletedTask; + } + }; + x.RequireHttpsMetadata = false; + x.SaveToken = true; + x.TokenValidationParameters = new TokenValidationParameters + { + IssuerSigningKey = new SymmetricSecurityKey(signingKey), + ValidateIssuer = false, + ValidateAudience = false + }; + }); + } + } +} diff --git a/src/Web/DevHive.Web/Configurations/Extensions/ConfigureSwagger.cs b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureSwagger.cs new file mode 100644 index 0000000..1b7182c --- /dev/null +++ b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureSwagger.cs @@ -0,0 +1,65 @@ +using System.Linq; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using NSwag; +using NSwag.Generation.Processors.Security; + +namespace DevHive.Web.Configurations.Extensions +{ + public static class SwaggerExtensions + { +#pragma warning disable S1075 + private const string LicenseName = "GPL-3.0 License"; + private const string LicenseUri = "https://github.com/Team-Kaleidoscope/DevHive/blob/main/LICENSE"; + private const string TermsOfServiceUri = "https://example.com/terms"; +#pragma warning restore S1075 + + public static void SwaggerConfiguration(this IServiceCollection services) + { + services.AddOpenApiDocument(c => + { + c.GenerateXmlObjects = true; + c.UseControllerSummaryAsTagDescription = true; + + c.AllowNullableBodyParameters = false; + c.Description = "DevHive Social Media's API Endpoints"; + + c.PostProcess = doc => + { + doc.Info.Version = "v0.1"; + doc.Info.Title = "API"; + doc.Info.Description = "DevHive Social Media's first official API release"; + doc.Info.TermsOfService = TermsOfServiceUri; + doc.Info.License = new() + { + Name = LicenseName, + Url = LicenseUri + }; + }; + + c.AddSecurity("Bearer", Enumerable.Empty<string>(), new() + { + Type = OpenApiSecuritySchemeType.ApiKey, + Name = "Authorization", + In = OpenApiSecurityApiKeyLocation.Header, + Description = "Type into the textbox: Bearer {your JWT token}." + }); + c.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("Bearer")); + }); + } + + public static void UseSwaggerConfiguration(this IApplicationBuilder app) + { + app.UseOpenApi(c => + { + c.DocumentName = "v0.1"; + }); + app.UseSwaggerUi3(c => + { + c.DocumentTitle = "DevHive API"; + c.EnableTryItOut = false; + c.DocExpansion = "list"; + }); + } + } +} diff --git a/src/Web/DevHive.Web/Configurations/Mapping/CommentMappings.cs b/src/Web/DevHive.Web/Configurations/Mapping/CommentMappings.cs new file mode 100644 index 0000000..b8d6829 --- /dev/null +++ b/src/Web/DevHive.Web/Configurations/Mapping/CommentMappings.cs @@ -0,0 +1,17 @@ +using AutoMapper; +using DevHive.Services.Models.Comment; +using DevHive.Web.Models.Comment; + +namespace DevHive.Web.Configurations.Mapping +{ + public class CommentMappings : Profile + { + public CommentMappings() + { + CreateMap<CreateCommentWebModel, CreateCommentServiceModel>(); + CreateMap<UpdateCommentWebModel, UpdateCommentServiceModel>(); + + CreateMap<ReadCommentServiceModel, ReadCommentWebModel>(); + } + } +} diff --git a/src/Web/DevHive.Web/Configurations/Mapping/FeedMappings.cs b/src/Web/DevHive.Web/Configurations/Mapping/FeedMappings.cs new file mode 100644 index 0000000..0909f6d --- /dev/null +++ b/src/Web/DevHive.Web/Configurations/Mapping/FeedMappings.cs @@ -0,0 +1,18 @@ +using AutoMapper; +using DevHive.Services.Models; +using DevHive.Web.Models.Comment; +using DevHive.Web.Models.Feed; + +namespace DevHive.Web.Configurations.Mapping +{ + public class FeedMappings : Profile + { + public FeedMappings() + { + CreateMap<GetPageWebModel, GetPageServiceModel>() + .ForMember(dest => dest.FirstRequestIssued, src => src.MapFrom(p => p.FirstPageTimeIssued)); + + CreateMap<ReadPageServiceModel, ReadPageWebModel>(); + } + } +} diff --git a/src/Web/DevHive.Web/Configurations/Mapping/LanguageMappings.cs b/src/Web/DevHive.Web/Configurations/Mapping/LanguageMappings.cs new file mode 100644 index 0000000..eca0d1a --- /dev/null +++ b/src/Web/DevHive.Web/Configurations/Mapping/LanguageMappings.cs @@ -0,0 +1,23 @@ +using AutoMapper; +using DevHive.Web.Models.Language; +using DevHive.Services.Models.Language; + +namespace DevHive.Web.Configurations.Mapping +{ + public class LanguageMappings : Profile + { + public LanguageMappings() + { + CreateMap<CreateLanguageWebModel, CreateLanguageServiceModel>(); + CreateMap<ReadLanguageWebModel, ReadLanguageServiceModel>(); + CreateMap<UpdateLanguageWebModel, UpdateLanguageServiceModel>() + .ForMember(src => src.Id, dest => dest.Ignore()); + CreateMap<LanguageWebModel, LanguageServiceModel>(); + + CreateMap<LanguageServiceModel, LanguageWebModel>(); + CreateMap<ReadLanguageServiceModel, ReadLanguageWebModel>(); + CreateMap<CreateLanguageServiceModel, CreateLanguageWebModel>(); + CreateMap<UpdateLanguageServiceModel, UpdateLanguageWebModel>(); + } + } +} diff --git a/src/Web/DevHive.Web/Configurations/Mapping/PostMappings.cs b/src/Web/DevHive.Web/Configurations/Mapping/PostMappings.cs new file mode 100644 index 0000000..a5b46ee --- /dev/null +++ b/src/Web/DevHive.Web/Configurations/Mapping/PostMappings.cs @@ -0,0 +1,17 @@ +using AutoMapper; +using DevHive.Services.Models.Post; +using DevHive.Web.Models.Post; + +namespace DevHive.Web.Configurations.Mapping +{ + public class PostMappings : Profile + { + public PostMappings() + { + CreateMap<CreatePostWebModel, CreatePostServiceModel>(); + CreateMap<UpdatePostWebModel, UpdatePostServiceModel>(); + + CreateMap<ReadPostServiceModel, ReadPostWebModel>(); + } + } +} diff --git a/src/Web/DevHive.Web/Configurations/Mapping/ProfilePictureMappings.cs b/src/Web/DevHive.Web/Configurations/Mapping/ProfilePictureMappings.cs new file mode 100644 index 0000000..8c12a20 --- /dev/null +++ b/src/Web/DevHive.Web/Configurations/Mapping/ProfilePictureMappings.cs @@ -0,0 +1,16 @@ +using System; +using AutoMapper; +using DevHive.Web.Models.ProfilePicture; +using DevHive.Services.Models.ProfilePicture; + +namespace DevHive.Web.Configurations.Mapping +{ + public class ProfilePictureMappings : Profile + { + public ProfilePictureMappings() + { + CreateMap<ProfilePictureWebModel, ProfilePictureServiceModel>() + .ForMember(dest => dest.ProfilePictureFormFile, src => src.MapFrom(p => p.Picture)); + } + } +} diff --git a/src/Web/DevHive.Web/Configurations/Mapping/RatingMappings.cs b/src/Web/DevHive.Web/Configurations/Mapping/RatingMappings.cs new file mode 100644 index 0000000..1d731d8 --- /dev/null +++ b/src/Web/DevHive.Web/Configurations/Mapping/RatingMappings.cs @@ -0,0 +1,18 @@ +using AutoMapper; +using DevHive.Services.Models.Rating; +using DevHive.Web.Models.Rating; + +namespace DevHive.Web.Configurations.Mapping +{ + public class RatingMappings : Profile + { + public RatingMappings() + { + CreateMap<CreateRatingWebModel, CreateRatingServiceModel>(); + + CreateMap<ReadRatingServiceModel, ReadRatingWebModel>(); + + CreateMap<UpdateRatingWebModel, UpdateRatingServiceModel>(); + } + } +} diff --git a/src/Web/DevHive.Web/Configurations/Mapping/RoleMappings.cs b/src/Web/DevHive.Web/Configurations/Mapping/RoleMappings.cs new file mode 100644 index 0000000..60f8503 --- /dev/null +++ b/src/Web/DevHive.Web/Configurations/Mapping/RoleMappings.cs @@ -0,0 +1,21 @@ +using AutoMapper; +using DevHive.Web.Models.Role; +using DevHive.Services.Models.Role; + +namespace DevHive.Web.Configurations.Mapping +{ + public class RoleMappings : Profile + { + public RoleMappings() + { + CreateMap<CreateRoleWebModel, CreateRoleServiceModel>(); + CreateMap<UpdateRoleWebModel, UpdateRoleServiceModel>() + .ForMember(src => src.Id, dest => dest.Ignore()); + CreateMap<RoleWebModel, RoleServiceModel>(); + + CreateMap<CreateRoleServiceModel, CreateRoleWebModel>(); + CreateMap<UpdateRoleServiceModel, UpdateRoleWebModel>(); + CreateMap<RoleServiceModel, RoleWebModel>(); + } + } +} diff --git a/src/Web/DevHive.Web/Configurations/Mapping/TechnologyMappings.cs b/src/Web/DevHive.Web/Configurations/Mapping/TechnologyMappings.cs new file mode 100644 index 0000000..708b6ac --- /dev/null +++ b/src/Web/DevHive.Web/Configurations/Mapping/TechnologyMappings.cs @@ -0,0 +1,23 @@ +using AutoMapper; +using DevHive.Web.Models.Technology; +using DevHive.Services.Models.Technology; + +namespace DevHive.Web.Configurations.Mapping +{ + public class TechnologyMappings : Profile + { + public TechnologyMappings() + { + CreateMap<CreateTechnologyWebModel, CreateTechnologyServiceModel>(); + CreateMap<ReadTechnologyWebModel, ReadTechnologyServiceModel>(); + CreateMap<UpdateTechnologyWebModel, UpdateTechnologyServiceModel>() + .ForMember(src => src.Id, dest => dest.Ignore()); + CreateMap<TechnologyWebModel, TechnologyServiceModel>(); + + CreateMap<CreateTechnologyServiceModel, CreateTechnologyWebModel>(); + CreateMap<ReadTechnologyServiceModel, ReadTechnologyWebModel>(); + CreateMap<UpdateTechnologyServiceModel, UpdateTechnologyWebModel>(); + CreateMap<TechnologyServiceModel, TechnologyWebModel>(); + } + } +} diff --git a/src/Web/DevHive.Web/Configurations/Mapping/UserMappings.cs b/src/Web/DevHive.Web/Configurations/Mapping/UserMappings.cs new file mode 100644 index 0000000..dabec93 --- /dev/null +++ b/src/Web/DevHive.Web/Configurations/Mapping/UserMappings.cs @@ -0,0 +1,30 @@ +using AutoMapper; +using DevHive.Services.Models.User; +using DevHive.Web.Models.User; +using DevHive.Common.Models.Identity; + +namespace DevHive.Web.Configurations.Mapping +{ + public class UserMappings : Profile + { + public UserMappings() + { + CreateMap<LoginWebModel, LoginServiceModel>(); + CreateMap<RegisterWebModel, RegisterServiceModel>(); + CreateMap<UserWebModel, UserServiceModel>(); + CreateMap<UpdateUserWebModel, UpdateUserServiceModel>(); + + CreateMap<UserServiceModel, UserWebModel>(); + + CreateMap<TokenModel, TokenWebModel>(); + + //Update + CreateMap<UpdateUserWebModel, UpdateUserServiceModel>(); + CreateMap<UsernameWebModel, FriendServiceModel>(); + CreateMap<UsernameWebModel, UpdateFriendServiceModel>(); + + CreateMap<UpdateUserServiceModel, UpdateUserWebModel>(); + CreateMap<FriendServiceModel, UsernameWebModel>(); + } + } +} diff --git a/src/Web/DevHive.Web/Controllers/CommentController.cs b/src/Web/DevHive.Web/Controllers/CommentController.cs new file mode 100644 index 0000000..8fa3577 --- /dev/null +++ b/src/Web/DevHive.Web/Controllers/CommentController.cs @@ -0,0 +1,116 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using AutoMapper; +using System; +using DevHive.Web.Models.Comment; +using DevHive.Services.Models.Comment; +using Microsoft.AspNetCore.Authorization; +using DevHive.Services.Interfaces; +using DevHive.Common.Jwt.Interfaces; + +namespace DevHive.Web.Controllers +{ + /// <summary> + /// All endpoints for interacting with the comments layer + /// </summary> + [ApiController] + [Route("/api/[controller]")] + [Authorize(Roles = "User,Admin")] + public class CommentController + { + private readonly ICommentService _commentService; + private readonly IMapper _commentMapper; + private readonly IJwtService _jwtService; + + public CommentController(ICommentService commentService, IMapper commentMapper, IJwtService jwtService) + { + this._commentService = commentService; + this._commentMapper = commentMapper; + this._jwtService = jwtService; + } + + /// <summary> + /// Create a comment and attach it to a post + /// </summary> + /// <param name="userId">The useer's Id</param> + /// <param name="createCommentWebModel">The new comment's parametars</param> + /// <param name="authorization">JWT Bearer token</param> + /// <returns>The comment's Id</returns> + [HttpPost] + public async Task<IActionResult> AddComment(Guid userId, [FromBody] CreateCommentWebModel createCommentWebModel, [FromHeader] string authorization) + { + if (!this._jwtService.ValidateToken(userId, authorization)) + return new UnauthorizedResult(); + + if (!await this._commentService.ValidateJwtForCreating(userId, authorization)) + return new UnauthorizedResult(); + + CreateCommentServiceModel createCommentServiceModel = + this._commentMapper.Map<CreateCommentServiceModel>(createCommentWebModel); + createCommentServiceModel.CreatorId = userId; + + Guid id = await this._commentService.AddComment(createCommentServiceModel); + + return id == Guid.Empty ? + new BadRequestObjectResult("Could not create comment!") : + new OkObjectResult(new { Id = id }); + } + + /// <summary> + /// Query comment's data by it's Id + /// </summary> + /// <param name="commentId">The comment's Id</param> + /// <returns>Full data model of the comment</returns> + [HttpGet] + [AllowAnonymous] + public async Task<IActionResult> GetCommentById(Guid commentId) + { + ReadCommentServiceModel readCommentServiceModel = await this._commentService.GetCommentById(commentId); + ReadCommentWebModel readCommentWebModel = this._commentMapper.Map<ReadCommentWebModel>(readCommentServiceModel); + + return new OkObjectResult(readCommentWebModel); + } + + /// <summary> + /// Update comment's parametars. Comment creator only! + /// </summary> + /// <param name="userId">The comment creator's Id</param> + /// <param name="updateCommentWebModel">New comment's parametars</param> + /// <param name="authorization">JWT Bearer token</param> + /// <returns>Ok result</returns> + [HttpPut] + public async Task<IActionResult> UpdateComment(Guid userId, [FromBody] UpdateCommentWebModel updateCommentWebModel, [FromHeader] string authorization) + { + if (!this._jwtService.ValidateToken(userId, authorization)) + return new UnauthorizedResult(); + + UpdateCommentServiceModel updateCommentServiceModel = + this._commentMapper.Map<UpdateCommentServiceModel>(updateCommentWebModel); + updateCommentServiceModel.CreatorId = userId; + + Guid id = await this._commentService.UpdateComment(updateCommentServiceModel); + + return id == Guid.Empty ? + new BadRequestObjectResult("Unable to update comment!") : + new OkObjectResult(new { Id = id }); + } + + /// <summary> + /// Delete a comment. Comment creator only! + /// </summary> + /// <param name="commentId">Comment's Id</param> + /// <param name="authorization">JWT Bearer token</param> + /// <returns>Ok result</returns> + [HttpDelete] + public async Task<IActionResult> DeleteComment(Guid commentId, [FromHeader] string authorization) + { + if (!await this._commentService.ValidateJwtForComment(commentId, authorization)) + return new UnauthorizedResult(); + + return await this._commentService.DeleteComment(commentId) ? + new OkResult() : + new BadRequestObjectResult("Could not delete Comment"); + } + } +} + diff --git a/src/Web/DevHive.Web/Controllers/FeedController.cs b/src/Web/DevHive.Web/Controllers/FeedController.cs new file mode 100644 index 0000000..37532a9 --- /dev/null +++ b/src/Web/DevHive.Web/Controllers/FeedController.cs @@ -0,0 +1,69 @@ +using System; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Services.Interfaces; +using DevHive.Services.Models; +using DevHive.Web.Models.Comment; +using DevHive.Web.Models.Feed; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace DevHive.Web.Controllers +{ + /// <summary> + /// All endpoints for interacting with the feed layer + /// </summary> + [ApiController] + [Route("/api/[controller]")] + [Authorize(Roles = "User,Admin")] + public class FeedController + { + private readonly IFeedService _feedService; + private readonly IMapper _mapper; + + public FeedController(IFeedService feedService, IMapper mapper) + { + this._feedService = feedService; + this._mapper = mapper; + } + + /// <summary> + /// Query posts for user's feed + /// </summary> + /// <param name="userId">The user's Id, whose feed is begin queried</param> + /// <param name="getPageWebModel">Page parametars</param> + /// <returns>A page of the feed</returns> + [HttpPost] + [Route("GetPosts")] + public async Task<IActionResult> GetPosts(Guid userId, [FromBody] GetPageWebModel getPageWebModel) + { + GetPageServiceModel getPageServiceModel = this._mapper.Map<GetPageServiceModel>(getPageWebModel); + getPageServiceModel.UserId = userId; + + ReadPageServiceModel readPageServiceModel = await this._feedService.GetPage(getPageServiceModel); + ReadPageWebModel readPageWebModel = this._mapper.Map<ReadPageWebModel>(readPageServiceModel); + + return new OkObjectResult(readPageWebModel); + } + + /// <summary> + /// Query a user profile's posts + /// </summary> + /// <param name="username">The user's username, whose posts are being queried</param> + /// <param name="getPageWebModel">Page parametars</param> + /// <returns>A page of the user's posts</returns> + [HttpPost] + [Route("GetUserPosts")] + [AllowAnonymous] + public async Task<IActionResult> GetUserPosts(string username, [FromBody] GetPageWebModel getPageWebModel) + { + GetPageServiceModel getPageServiceModel = this._mapper.Map<GetPageServiceModel>(getPageWebModel); + getPageServiceModel.Username = username; + + ReadPageServiceModel readPageServiceModel = await this._feedService.GetUserPage(getPageServiceModel); + ReadPageWebModel readPageWebModel = this._mapper.Map<ReadPageWebModel>(readPageServiceModel); + + return new OkObjectResult(readPageWebModel); + } + } +} diff --git a/src/Web/DevHive.Web/Controllers/FriendsController.cs b/src/Web/DevHive.Web/Controllers/FriendsController.cs new file mode 100644 index 0000000..318ae64 --- /dev/null +++ b/src/Web/DevHive.Web/Controllers/FriendsController.cs @@ -0,0 +1,54 @@ +using System; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Common.Jwt.Interfaces; +using DevHive.Common.Models.Identity; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.User; +using DevHive.Web.Models.User; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using NSwag.Annotations; + +namespace DevHive.Web.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class FriendsController + { + private readonly IFriendsService _friendsService; + private readonly IMapper _mapper; + private readonly IJwtService _jwtService; + + public FriendsController(IFriendsService friendsService, IMapper mapper, IJwtService jwtService) + { + this._friendsService = friendsService; + this._mapper = mapper; + this._jwtService = jwtService; + } + + [HttpPost] + [Authorize(Roles = "User,Admin")] + public async Task<IActionResult> AddFriend(Guid userId, string friendUsername, [FromHeader] string authorization) + { + if (!this._jwtService.ValidateToken(userId, authorization)) + return new UnauthorizedResult(); + + return (await this._friendsService.AddFriend(userId, friendUsername)) ? + new OkResult() : + new BadRequestResult(); + } + + [HttpDelete] + [Authorize(Roles = "User,Admin")] + public async Task<IActionResult> RemoveFriend(Guid userId, string friendUsername, [FromHeader] string authorization) + { + if (!this._jwtService.ValidateToken(userId, authorization)) + return new UnauthorizedResult(); + + return (await this._friendsService.RemoveFriend(userId, friendUsername)) ? + new OkResult() : + new BadRequestResult(); + } + } +} diff --git a/src/Web/DevHive.Web/Controllers/LanguageController.cs b/src/Web/DevHive.Web/Controllers/LanguageController.cs new file mode 100644 index 0000000..665fb66 --- /dev/null +++ b/src/Web/DevHive.Web/Controllers/LanguageController.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.Language; +using DevHive.Web.Models.Language; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace DevHive.Web.Controllers +{ + /// <summary> + /// All endpoints for interacting with the language layer + /// </summary> + [ApiController] + [Route("/api/[controller]")] + public class LanguageController + { + private readonly ILanguageService _languageService; + private readonly IMapper _languageMapper; + + public LanguageController(ILanguageService languageService, IMapper mapper) + { + this._languageService = languageService; + this._languageMapper = mapper; + } + + /// <summary> + /// Create a new language, so users can have a choice. Admin only! + /// </summary> + /// <param name="createLanguageWebModel">The new language's parametars</param> + /// <returns>The new language's Id</returns> + [HttpPost] + [Authorize(Roles = "Admin")] + public async Task<IActionResult> Create([FromBody] CreateLanguageWebModel createLanguageWebModel) + { + CreateLanguageServiceModel languageServiceModel = this._languageMapper.Map<CreateLanguageServiceModel>(createLanguageWebModel); + + Guid id = await this._languageService.CreateLanguage(languageServiceModel); + + return id == Guid.Empty ? + new BadRequestObjectResult($"Could not create language {createLanguageWebModel.Name}") : + new OkObjectResult(new { Id = id }); + } + + /// <summary> + /// Query full language data by Id + /// </summary> + /// <param name="id">The language's Id</param> + /// <returns>Full language data</returns> + [HttpGet] + [AllowAnonymous] + public async Task<IActionResult> GetById(Guid id) + { + ReadLanguageServiceModel languageServiceModel = await this._languageService.GetLanguageById(id); + ReadLanguageWebModel languageWebModel = this._languageMapper.Map<ReadLanguageWebModel>(languageServiceModel); + + return new OkObjectResult(languageWebModel); + } + + /// <summary> + /// Query all languages in the database + /// </summary> + /// <returns>All languages in the database</returns> + [HttpGet] + [Route("GetLanguages")] + [Authorize(Roles = "User,Admin")] + public IActionResult GetAllExistingLanguages() + { + HashSet<ReadLanguageServiceModel> languageServiceModels = this._languageService.GetLanguages(); + HashSet<ReadLanguageWebModel> languageWebModels = this._languageMapper.Map<HashSet<ReadLanguageWebModel>>(languageServiceModels); + + return new OkObjectResult(languageWebModels); + } + + /// <summary> + /// Alter language's properties. Admin only! + /// </summary> + /// <param name="id">The language's Id</param> + /// <param name="updateModel">The langauge's new parametars</param> + /// <returns>Ok result</returns> + [HttpPut] + [Authorize(Roles = "Admin")] + public async Task<IActionResult> Update(Guid id, [FromBody] UpdateLanguageWebModel updateModel) + { + UpdateLanguageServiceModel updatelanguageServiceModel = this._languageMapper.Map<UpdateLanguageServiceModel>(updateModel); + updatelanguageServiceModel.Id = id; + + bool result = await this._languageService.UpdateLanguage(updatelanguageServiceModel); + + if (!result) + return new BadRequestObjectResult("Could not update Language"); + + return new OkResult(); + } + + /// <summary> + /// Delete a language. Admin only! + /// </summary> + /// <param name="langaugeId">The language's Id</param> + /// <returns>Ok result</returns> + [HttpDelete] + [Authorize(Roles = "Admin")] + public async Task<IActionResult> Delete(Guid langaugeId) + { + bool result = await this._languageService.DeleteLanguage(langaugeId); + + if (!result) + return new BadRequestObjectResult("Could not delete Language"); + + return new OkResult(); + } + } +} diff --git a/src/Web/DevHive.Web/Controllers/PostController.cs b/src/Web/DevHive.Web/Controllers/PostController.cs new file mode 100644 index 0000000..44b291d --- /dev/null +++ b/src/Web/DevHive.Web/Controllers/PostController.cs @@ -0,0 +1,123 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using AutoMapper; +using System; +using DevHive.Web.Models.Post; +using DevHive.Services.Models.Post; +using Microsoft.AspNetCore.Authorization; +using DevHive.Services.Interfaces; +using DevHive.Common.Jwt.Interfaces; + +namespace DevHive.Web.Controllers +{ + /// <summary> + /// All endpoints for interacting with the post layer + /// </summary> + [ApiController] + [Route("/api/[controller]")] + [Authorize(Roles = "User,Admin")] + public class PostController + { + private readonly IPostService _postService; + private readonly IMapper _postMapper; + private readonly IJwtService _jwtService; + + public PostController(IPostService postService, IMapper postMapper, IJwtService jwtService) + { + this._postService = postService; + this._postMapper = postMapper; + this._jwtService = jwtService; + } + + #region Create + /// <summary> + /// Create a new post + /// </summary> + /// <param name="userId">The user's Id</param> + /// <param name="createPostWebModel">The new post's data</param> + /// <param name="authorization">JWT Bearer token</param> + /// <returns>New post's Id</returns> + [HttpPost] + public async Task<IActionResult> Create(Guid userId, [FromForm] CreatePostWebModel createPostWebModel, [FromHeader] string authorization) + { + if (!this._jwtService.ValidateToken(userId, authorization)) + return new UnauthorizedResult(); + + CreatePostServiceModel createPostServiceModel = + this._postMapper.Map<CreatePostServiceModel>(createPostWebModel); + createPostServiceModel.CreatorId = userId; + + Guid id = await this._postService.CreatePost(createPostServiceModel); + + return id == Guid.Empty ? + new BadRequestObjectResult("Could not create post!") : + new OkObjectResult(new { Id = id }); + } + #endregion + + #region Read + /// <summary> + /// Query full post's data by it's Id + /// </summary> + /// <param name="id">The post's Id</param> + /// <returns>Full data model of the post</returns> + [HttpGet] + [AllowAnonymous] + public async Task<IActionResult> GetById(Guid id) + { + ReadPostServiceModel postServiceModel = await this._postService.GetPostById(id); + ReadPostWebModel postWebModel = this._postMapper.Map<ReadPostWebModel>(postServiceModel); + + return new OkObjectResult(postWebModel); + } + #endregion + + #region Update + /// <summary> + /// Update post's data. Creator only! + /// </summary> + /// <param name="userId">The post creator's Id</param> + /// <param name="updatePostWebModel">The new params of the post</param> + /// <param name="authorization">JWT Bearer token</param> + /// <returns>The post's Id</returns> + [HttpPut] + public async Task<IActionResult> Update(Guid userId, [FromForm] UpdatePostWebModel updatePostWebModel, [FromHeader] string authorization) + { + if (!this._jwtService.ValidateToken(userId, authorization)) + return new UnauthorizedResult(); + + if (!await this._postService.ValidateJwtForPost(updatePostWebModel.PostId, authorization)) + return new UnauthorizedResult(); + + UpdatePostServiceModel updatePostServiceModel = + this._postMapper.Map<UpdatePostServiceModel>(updatePostWebModel); + updatePostServiceModel.CreatorId = userId; + + Guid id = await this._postService.UpdatePost(updatePostServiceModel); + + return id == Guid.Empty ? + new BadRequestObjectResult("Could not update post!") : + new OkObjectResult(new { Id = id }); + } + #endregion + + #region Delete + /// <summary> + /// Delete a post. Creator only! + /// </summary> + /// <param name="postId">Post's Id</param> + /// <param name="authorization">JWT Bearer token</param> + /// <returns>Ok result</returns> + [HttpDelete] + public async Task<IActionResult> Delete(Guid postId, [FromHeader] string authorization) + { + if (!await this._postService.ValidateJwtForPost(postId, authorization)) + return new UnauthorizedResult(); + + return await this._postService.DeletePost(postId) ? + new OkResult() : + new BadRequestObjectResult("Could not delete Post"); + } + #endregion + } +} diff --git a/src/Web/DevHive.Web/Controllers/ProfilePictureController.cs b/src/Web/DevHive.Web/Controllers/ProfilePictureController.cs new file mode 100644 index 0000000..8474df5 --- /dev/null +++ b/src/Web/DevHive.Web/Controllers/ProfilePictureController.cs @@ -0,0 +1,66 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.ProfilePicture; +using DevHive.Common.Jwt.Interfaces; +using AutoMapper; +using DevHive.Web.Models.ProfilePicture; + +namespace DevHive.Web.Controllers +{ + /// <summary> + /// All endpoints for interacting with the profile picture layer + /// </summary> + [ApiController] + [Route("api/[controller]")] + public class ProfilePictureController + { + private readonly IProfilePictureService _profilePictureService; + private readonly IJwtService _jwtService; + private readonly IMapper _profilePictureMapper; + + public ProfilePictureController(IProfilePictureService profilePictureService, IJwtService jwtService, IMapper profilePictureMapper) + { + this._profilePictureService = profilePictureService; + this._jwtService = jwtService; + this._profilePictureMapper = profilePictureMapper; + } + + /// <summary> + /// Get the URL of user's profile picture + /// </summary> + /// <param name="profilePictureId">The profile picture's Id</param> + /// <param name="authorization">JWT Bearer Token</param> + /// <returns>The URL of the profile picture</returns> + [HttpGet] + [AllowAnonymous] + public async Task<IActionResult> ReadProfilePicture(Guid profilePictureId) + { + string profilePicURL = await this._profilePictureService.GetProfilePictureById(profilePictureId); + return new OkObjectResult(new { ProfilePictureURL = profilePicURL} ); + } + + /// <summary> + /// Alter the profile picture of a user + /// </summary> + /// <param name="userId">The user's Id</param> + /// <param name="profilePictureWebModel">The new profile picture</param> + /// <param name="authorization">JWT Bearer Token</param> + /// <returns>The URL of the new profile picture</returns> + [HttpPut] + [Authorize(Roles = "User,Admin")] + public async Task<IActionResult> UpdateProfilePicture(Guid userId, [FromForm] ProfilePictureWebModel profilePictureWebModel, [FromHeader] string authorization) + { + if (!this._jwtService.ValidateToken(userId, authorization)) + return new UnauthorizedResult(); + + ProfilePictureServiceModel profilePictureServiceModel = this._profilePictureMapper.Map<ProfilePictureServiceModel>(profilePictureWebModel); + profilePictureServiceModel.UserId = userId; + + string url = await this._profilePictureService.UpdateProfilePicture(profilePictureServiceModel); + return new OkObjectResult(new { URL = url }); + } + } +} diff --git a/src/Web/DevHive.Web/Controllers/RatingController.cs b/src/Web/DevHive.Web/Controllers/RatingController.cs new file mode 100644 index 0000000..7d21795 --- /dev/null +++ b/src/Web/DevHive.Web/Controllers/RatingController.cs @@ -0,0 +1,98 @@ +using System; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Common.Jwt.Interfaces; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.Rating; +using DevHive.Web.Models.Rating; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace DevHive.Web.Controllers +{ + [ApiController] + [Authorize(Roles = "Admin,User")] + [Route("api/[controller]")] + public class RatingController + { + private readonly IRatingService _rateService; + private readonly IMapper _mapper; + private readonly IJwtService _jwtService; + + public RatingController(IRatingService rateService, IMapper mapper, IJwtService jwtService) + { + this._rateService = rateService; + this._mapper = mapper; + this._jwtService = jwtService; + } + + [HttpPost] + public async Task<IActionResult> RatePost(Guid userId, [FromBody] CreateRatingWebModel createRatingWebModel, [FromHeader] string authorization) + { + if (!this._jwtService.ValidateToken(userId, authorization)) + return new UnauthorizedResult(); + + CreateRatingServiceModel ratePostServiceModel = this._mapper.Map<CreateRatingServiceModel>(createRatingWebModel); + ratePostServiceModel.UserId = userId; + + Guid id = await this._rateService.RatePost(ratePostServiceModel); + + if (Guid.Empty == id) + return new BadRequestResult(); + + return new OkObjectResult(new { Id = id }); + } + + [HttpGet] + public async Task<IActionResult> GetRatingById(Guid id) + { + ReadRatingServiceModel readRatingServiceModel = await this._rateService.GetRatingById(id); + ReadRatingWebModel readPostRatingWebModel = this._mapper.Map<ReadRatingWebModel>(readRatingServiceModel); + + return new OkObjectResult(readPostRatingWebModel); + } + + [HttpGet] + [Route("GetByUserAndPost")] + public async Task<IActionResult> GetRatingByUserAndPost(Guid userId, Guid postId) + { + ReadRatingServiceModel readRatingServiceModel = await this._rateService.GetRatingByPostAndUser(userId, postId); + ReadRatingWebModel readPostRatingWebModel = this._mapper.Map<ReadRatingWebModel>(readRatingServiceModel); + + return new OkObjectResult(readPostRatingWebModel); + } + + [HttpPut] + public async Task<IActionResult> UpdateRating(Guid userId, Guid postId, [FromBody] UpdateRatingWebModel updateRatingWebModel, [FromHeader] string authorization) + { + if (!this._jwtService.ValidateToken(userId, authorization)) + return new UnauthorizedResult(); + + UpdateRatingServiceModel updateRatingServiceModel = + this._mapper.Map<UpdateRatingServiceModel>(updateRatingWebModel); + updateRatingServiceModel.UserId = userId; + updateRatingServiceModel.PostId = postId; + + ReadRatingServiceModel readRatingServiceModel = await this._rateService.UpdateRating(updateRatingServiceModel); + + if (readRatingServiceModel == null) + return new BadRequestResult(); + else + { + ReadRatingWebModel readRatingWebModel = this._mapper.Map<ReadRatingWebModel>(readRatingServiceModel); + return new OkObjectResult(readRatingWebModel); + } + } + + [HttpDelete] + public async Task<IActionResult> DeleteRating(Guid userId, Guid ratingId, [FromHeader] string authorization) + { + if (!this._jwtService.ValidateToken(userId, authorization)) + return new UnauthorizedResult(); + + return await this._rateService.DeleteRating(ratingId) ? + new OkResult() : + new BadRequestObjectResult("Could not delete Rating"); + } + } +} diff --git a/src/Web/DevHive.Web/Controllers/RoleController.cs b/src/Web/DevHive.Web/Controllers/RoleController.cs new file mode 100644 index 0000000..ebb305e --- /dev/null +++ b/src/Web/DevHive.Web/Controllers/RoleController.cs @@ -0,0 +1,101 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using DevHive.Web.Models.Role; +using AutoMapper; +using System; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.Role; +using Microsoft.AspNetCore.Authorization; + +namespace DevHive.Web.Controllers +{ + /// <summary> + /// All endpoints for interacting with the roles layer + /// </summary> + [ApiController] + [Route("/api/[controller]")] + public class RoleController + { + private readonly IRoleService _roleService; + private readonly IMapper _roleMapper; + + public RoleController(IRoleService roleService, IMapper mapper) + { + this._roleService = roleService; + this._roleMapper = mapper; + } + + /// <summary> + /// Create a new role for the roles hierarchy. Admin only! + /// </summary> + /// <param name="createRoleWebModel">The new role's parametars</param> + /// <returns>The new role's Id</returns> + [HttpPost] + [Authorize(Roles = "Admin")] + public async Task<IActionResult> Create([FromBody] CreateRoleWebModel createRoleWebModel) + { + CreateRoleServiceModel roleServiceModel = + this._roleMapper.Map<CreateRoleServiceModel>(createRoleWebModel); + + Guid id = await this._roleService.CreateRole(roleServiceModel); + + return id == Guid.Empty ? + new BadRequestObjectResult($"Could not create role {createRoleWebModel.Name}") : + new OkObjectResult(new { Id = id }); + } + + /// <summary> + /// Get a role's full data, querying it by it's Id + /// </summary> + /// <param name="id">The role's Id</param> + /// <returns>Full info of the role</returns> + [HttpGet] + [Authorize(Roles = "User,Admin")] + public async Task<IActionResult> GetById(Guid id) + { + RoleServiceModel roleServiceModel = await this._roleService.GetRoleById(id); + RoleWebModel roleWebModel = this._roleMapper.Map<RoleWebModel>(roleServiceModel); + + return new OkObjectResult(roleWebModel); + } + + /// <summary> + /// Update a role's parametars. Admin only! + /// </summary> + /// <param name="id">The role's Id</param> + /// <param name="updateRoleWebModel">The new parametrats for that role</param> + /// <returns>Ok result</returns> + [HttpPut] + [Authorize(Roles = "Admin")] + public async Task<IActionResult> Update(Guid id, [FromBody] UpdateRoleWebModel updateRoleWebModel) + { + UpdateRoleServiceModel updateRoleServiceModel = + this._roleMapper.Map<UpdateRoleServiceModel>(updateRoleWebModel); + updateRoleServiceModel.Id = id; + + bool result = await this._roleService.UpdateRole(updateRoleServiceModel); + + if (!result) + return new BadRequestObjectResult("Could not update role!"); + + return new OkResult(); + } + + /// <summary> + /// Delete a role. Admin only! + /// </summary> + /// <param name="id">The role's Id</param> + /// <returns>Ok result</returns> + [HttpDelete] + [Authorize(Roles = "Admin")] + public async Task<IActionResult> Delete(Guid id) + { + bool result = await this._roleService.DeleteRole(id); + + if (!result) + return new BadRequestObjectResult("Could not delete role!"); + + return new OkResult(); + } + } +} diff --git a/src/Web/DevHive.Web/Controllers/TechnologyController.cs b/src/Web/DevHive.Web/Controllers/TechnologyController.cs new file mode 100644 index 0000000..ecf2bd7 --- /dev/null +++ b/src/Web/DevHive.Web/Controllers/TechnologyController.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.Technology; +using DevHive.Web.Models.Technology; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace DevHive.Web.Controllers +{ + /// <summary> + /// All endpoints for interacting with the technology layer + /// </summary> + [ApiController] + [Route("/api/[controller]")] + public class TechnologyController + { + private readonly ITechnologyService _technologyService; + private readonly IMapper _technologyMapper; + + public TechnologyController(ITechnologyService technologyService, IMapper technologyMapper) + { + this._technologyService = technologyService; + this._technologyMapper = technologyMapper; + } + + /// <summary> + /// Create a new technology, so users can have a choice. Admin only! + /// </summary> + /// <param name="createTechnologyWebModel">Data for the new technology</param> + /// <returns>The new technology's Id</returns> + [HttpPost] + [Authorize(Roles = "Admin")] + public async Task<IActionResult> Create([FromBody] CreateTechnologyWebModel createTechnologyWebModel) + { + CreateTechnologyServiceModel technologyServiceModel = this._technologyMapper.Map<CreateTechnologyServiceModel>(createTechnologyWebModel); + + Guid id = await this._technologyService.CreateTechnology(technologyServiceModel); + + return id == Guid.Empty ? + new BadRequestObjectResult($"Could not create technology {createTechnologyWebModel.Name}") : + new OkObjectResult(new { Id = id }); + } + + /// <summary> + /// Get technology's data by it's Id + /// </summary> + /// <param name="id">The technology's Id</param> + /// <returns>The technology's full data</returns> + [HttpGet] + [AllowAnonymous] + public async Task<IActionResult> GetById(Guid id) + { + ReadTechnologyServiceModel readTechnologyServiceModel = await this._technologyService.GetTechnologyById(id); + ReadTechnologyWebModel readTechnologyWebModel = this._technologyMapper.Map<ReadTechnologyWebModel>(readTechnologyServiceModel); + + return new OkObjectResult(readTechnologyWebModel); + } + + /// <summary> + /// Get all technologies from our database + /// </summary> + /// <returns>All technologies</returns> + [HttpGet] + [Route("GetTechnologies")] + [Authorize(Roles = "User,Admin")] + public IActionResult GetAllExistingTechnologies() + { + HashSet<ReadTechnologyServiceModel> technologyServiceModels = this._technologyService.GetTechnologies(); + HashSet<ReadTechnologyWebModel> languageWebModels = this._technologyMapper.Map<HashSet<ReadTechnologyWebModel>>(technologyServiceModels); + + return new OkObjectResult(languageWebModels); + } + + /// <summary> + /// Alter a technology's parameters. Admin only! + /// </summary> + /// <param name="id">Technology's Id</param> + /// <param name="updateModel">The new parametars</param> + /// <returns>Ok result</returns> + [HttpPut] + [Authorize(Roles = "Admin")] + public async Task<IActionResult> Update(Guid id, [FromBody] UpdateTechnologyWebModel updateModel) + { + UpdateTechnologyServiceModel updateTechnologyServiceModel = this._technologyMapper.Map<UpdateTechnologyServiceModel>(updateModel); + updateTechnologyServiceModel.Id = id; + + bool result = await this._technologyService.UpdateTechnology(updateTechnologyServiceModel); + + if (!result) + return new BadRequestObjectResult("Could not update Technology"); + + return new OkResult(); + } + + /// <summary> + /// Delete a etchnology from the database. Admin only! + /// </summary> + /// <param name="id">The technology's Id</param> + /// <returns>Ok result</returns> + [HttpDelete] + [Authorize(Roles = "Admin")] + public async Task<IActionResult> Delete(Guid id) + { + bool result = await this._technologyService.DeleteTechnology(id); + + if (!result) + return new BadRequestObjectResult("Could not delete Technology"); + + return new OkResult(); + } + } +} diff --git a/src/Web/DevHive.Web/Controllers/UserController.cs b/src/Web/DevHive.Web/Controllers/UserController.cs new file mode 100644 index 0000000..4d01447 --- /dev/null +++ b/src/Web/DevHive.Web/Controllers/UserController.cs @@ -0,0 +1,172 @@ +using System; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Services.Models.User; +using DevHive.Web.Models.User; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using DevHive.Common.Models.Identity; +using DevHive.Services.Interfaces; +using DevHive.Common.Jwt.Interfaces; +using NSwag.Annotations; + +namespace DevHive.Web.Controllers +{ + /// <summary> + /// All endpoints for integration with the User + /// </summary> + [ApiController] + [Route("/api/[controller]")] + [OpenApiController("User Controller")] + public class UserController : ControllerBase + { + private readonly IUserService _userService; + private readonly IMapper _userMapper; + private readonly IJwtService _jwtService; + + public UserController(IUserService userService, IMapper mapper, IJwtService jwtService) + { + this._userService = userService; + this._userMapper = mapper; + this._jwtService = jwtService; + } + + #region Authentication + /// <summary> + /// Login endpoint for the DevHive Social Platform + /// </summary> + /// <param name="loginModel">Login model with username and password</param> + /// <returns>A JWT Token for further validation</returns> + [HttpPost] + [AllowAnonymous] + [Route("Login")] + [OpenApiTags("Authorization")] + public async Task<IActionResult> Login([FromBody] LoginWebModel loginModel) + { + LoginServiceModel loginServiceModel = this._userMapper.Map<LoginServiceModel>(loginModel); + + TokenModel tokenModel = await this._userService.LoginUser(loginServiceModel); + TokenWebModel tokenWebModel = this._userMapper.Map<TokenWebModel>(tokenModel); + + return new OkObjectResult(tokenWebModel); + } + + /// <summary> + /// Register a new User in the DevHive Social Platform + /// </summary> + /// <param name="registerModel">Register model with the new data to provide</param> + /// <returns>A JWT Token for further validation</returns> + [HttpPost] + [AllowAnonymous] + [Route("Register")] + [OpenApiTag("Authorization")] + public async Task<IActionResult> Register([FromBody] RegisterWebModel registerModel) + { + RegisterServiceModel registerServiceModel = this._userMapper.Map<RegisterServiceModel>(registerModel); + + TokenModel tokenModel = await this._userService.RegisterUser(registerServiceModel); + TokenWebModel tokenWebModel = this._userMapper.Map<TokenWebModel>(tokenModel); + + return new CreatedResult("Register", tokenWebModel); + } + #endregion + + #region Read + /// <summary> + /// Get a User's information using the Guid + /// </summary> + /// <param name="id">User's Id</param> + /// <param name="authorization">The JWT Token, contained in the header and used for validation</param> + /// <returns>A full User's read model</returns> + [HttpGet] + [Authorize(Roles = "User,Admin")] + public async Task<IActionResult> GetById(Guid id, [FromHeader] string authorization) + { + if (!this._jwtService.ValidateToken(id, authorization)) + return new UnauthorizedResult(); + + UserServiceModel userServiceModel = await this._userService.GetUserById(id); + UserWebModel userWebModel = this._userMapper.Map<UserWebModel>(userServiceModel); + + return new OkObjectResult(userWebModel); + } + + /// <summary> + /// Get a User's profile using his username. Does NOT require authorization + /// </summary> + /// <param name="username">User's username</param> + /// <returns>A trimmed version of the full User's read model</returns> + [HttpGet] + [Route("GetUser")] + [AllowAnonymous] + public async Task<IActionResult> GetUser(string username) + { + UserServiceModel friendServiceModel = await this._userService.GetUserByUsername(username); + UserWebModel friend = this._userMapper.Map<UserWebModel>(friendServiceModel); + + return new OkObjectResult(friend); + } + #endregion + + #region Update + /// <summary> + /// Full update on User's data. A PUSTINQK can only edit his account + /// </summary> + /// <param name="id">The User's Id</param> + /// <param name="updateUserWebModel">A full User update model</param> + /// <param name="authorization">The JWT Token, contained in the header and used for validation</param> + /// <returns>A full User's read model</returns> + [HttpPut] + [Authorize(Roles = "User,Admin")] + public async Task<IActionResult> Update(Guid id, [FromBody] UpdateUserWebModel updateUserWebModel, [FromHeader] string authorization) + { + if (!this._jwtService.ValidateToken(id, authorization)) + return new UnauthorizedResult(); + + UpdateUserServiceModel updateUserServiceModel = this._userMapper.Map<UpdateUserServiceModel>(updateUserWebModel); + updateUserServiceModel.Id = id; + + UserServiceModel userServiceModel = await this._userService.UpdateUser(updateUserServiceModel); + UserWebModel userWebModel = this._userMapper.Map<UserWebModel>(userServiceModel); + + return new AcceptedResult("UpdateUser", userWebModel); + } + #endregion + + #region Delete + /// <summary> + /// Delete a User with his Id. A PUSTINQK can only delete his account. An Admin can delete all accounts + /// </summary> + /// <param name="id">The User's Id</param> + /// <param name="authorization">The JWT Token, contained in the header and used for validation</param> + /// <returns>Ok, BadRequest or Unauthorized</returns> + [HttpDelete] + [Authorize(Roles = "User,Admin")] + public async Task<IActionResult> Delete(Guid id, [FromHeader] string authorization) + { + if (!this._jwtService.ValidateToken(id, authorization)) + return new UnauthorizedResult(); + + bool result = await this._userService.DeleteUser(id); + if (!result) + return new BadRequestObjectResult("Could not delete User"); + + return new OkResult(); + } + #endregion + + /// <summary> + /// We don't talk about that, NIGGA! + /// </summary> + /// <param name="userId"></param> + /// <returns></returns> + [HttpPost] + [OpenApiIgnore] + [Authorize(Roles = "User,Admin")] + [Route("SuperSecretPromotionToAdmin")] + public async Task<IActionResult> SuperSecretPromotionToAdmin(Guid userId) + { + return new OkObjectResult(await this._userService.SuperSecretPromotionToAdmin(userId)); + } + } +} diff --git a/src/Web/DevHive.Web/DevHive.Web.csproj b/src/Web/DevHive.Web/DevHive.Web.csproj new file mode 100644 index 0000000..5b3a920 --- /dev/null +++ b/src/Web/DevHive.Web/DevHive.Web.csproj @@ -0,0 +1,43 @@ +<Project Sdk="Microsoft.NET.Sdk.Web"> + <PropertyGroup> + <TargetFramework>net5.0</TargetFramework> + </PropertyGroup> + <PropertyGroup> + <EnableNETAnalyzers>true</EnableNETAnalyzers> + <AnalysisLevel>latest</AnalysisLevel> + <GenerateDocumentationFile>true</GenerateDocumentationFile> + <AllowUntrustedCertificate>true</AllowUntrustedCertificate> + </PropertyGroup> + <ItemGroup> + <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="5.0.4" NoWarn="NU1605"/> + <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.4" NoWarn="NU1605"/> + <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.4"> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + <PrivateAssets>all</PrivateAssets> + </PackageReference> + <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="5.0.2"/> + <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1"/> + <PackageReference Include="AutoMapper" Version="10.1.1"/> + <PackageReference Include="Newtonsoft.Json" Version="13.0.1"/> + <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.4"/> + <PackageReference Include="SonarAnalyzer.CSharp" Version="8.20.0.28934"/> + <PackageReference Include="NSwag.AspNetCore" Version="13.10.8"/> + <PackageReference Include="NSwag.Generation.AspNetCore" Version="13.10.8"/> + <PackageReference Include="NSwag.Annotations" Version="13.10.8"/> + <PackageReference Include="NSwag.Core" Version="13.10.8"/> + <PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.1.1"/> + <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.1.1"/> + <PackageReference Include="NSwag.SwaggerGeneration.WebApi" Version="12.3.1"/> + <PackageReference Include="Serilog.AspNetCore" Version="4.1.0"/> + <PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0"/> + <PackageReference Include="Serilog.Enrichers.Environment" Version="2.1.3"/> + <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0"/> + <PackageReference Include="Serilog.Enrichers.Process" Version="2.0.1"/> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\DevHive.Web.Models\DevHive.Web.Models.csproj"/> + <ProjectReference Include="..\..\Services\DevHive.Services\DevHive.Services.csproj"/> + <ProjectReference Include="..\..\Common\DevHive.Common.Models\DevHive.Common.Models.csproj"/> + <ProjectReference Include="..\..\Common\DevHive.Common\DevHive.Common.csproj"/> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/src/Web/DevHive.Web/Middleware/ExceptionMiddleware.cs b/src/Web/DevHive.Web/Middleware/ExceptionMiddleware.cs new file mode 100644 index 0000000..680d824 --- /dev/null +++ b/src/Web/DevHive.Web/Middleware/ExceptionMiddleware.cs @@ -0,0 +1,44 @@ +using System; +using System.Net; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; + +namespace DevHive.Web.Middleware +{ + public class ExceptionMiddleware + { + private readonly RequestDelegate _next; + + public ExceptionMiddleware(RequestDelegate next) + { + this._next = next; + } + + public async Task InvokeAsync(HttpContext httpContext) + { + try + { + await this._next(httpContext); + } + catch (Exception ex) + { + await HandleExceptionAsync(httpContext, ex); + } + } + + private static Task HandleExceptionAsync(HttpContext context, Exception exception) + { + context.Response.ContentType = "application/json"; + context.Response.StatusCode = (int)HttpStatusCode.BadRequest; + + var problems = new + { + errors = new { Exception = new String[] { exception.Message } } + }; + + string message = JsonConvert.SerializeObject(problems); + return context.Response.WriteAsync(message); + } + } +} diff --git a/src/Web/DevHive.Web/Program.cs b/src/Web/DevHive.Web/Program.cs new file mode 100644 index 0000000..e7c47a9 --- /dev/null +++ b/src/Web/DevHive.Web/Program.cs @@ -0,0 +1,49 @@ +using System; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Configuration; +using Serilog; + +namespace DevHive.Web +{ + #pragma warning disable IDE0055, S1118 + + public class Program + { + private const int HTTP_PORT = 5000; + + public static void Main(string[] args) + { + var config = new ConfigurationBuilder() + .AddJsonFile("appsettings.json") + .Build(); + + Log.Logger = new LoggerConfiguration() + .ReadFrom.Configuration(config) + .CreateLogger(); + + try + { + Log.Information("Application Starting Up"); + CreateHostBuilder(args).Build().Run(); + } + catch (Exception ex) + { + Log.Fatal(ex, "The application failed to start correctly."); + } + finally + { + Log.CloseAndFlush(); + } + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseSerilog() + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.ConfigureKestrel(opt => opt.ListenLocalhost(HTTP_PORT)); + webBuilder.UseStartup<Startup>(); + }); + } +} diff --git a/src/Web/DevHive.Web/Properties/launchSettings.json b/src/Web/DevHive.Web/Properties/launchSettings.json new file mode 100644 index 0000000..2b65d0b --- /dev/null +++ b/src/Web/DevHive.Web/Properties/launchSettings.json @@ -0,0 +1,36 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:1955", + "sslPort": 44326 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "DevHive.Web": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "DevHive.Web Production": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Production" + } + } + } +} diff --git a/src/Web/DevHive.Web/Startup.cs b/src/Web/DevHive.Web/Startup.cs new file mode 100644 index 0000000..49fa408 --- /dev/null +++ b/src/Web/DevHive.Web/Startup.cs @@ -0,0 +1,75 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using DevHive.Web.Configurations.Extensions; +using Newtonsoft.Json; +using Serilog; + +namespace DevHive.Web +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddCors(); + + services.AddControllers() + .AddNewtonsoftJson(x => + { + x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + }); + + services.DependencyInjectionConfiguration(this.Configuration); + services.DatabaseConfiguration(Configuration); + services.SwaggerConfiguration(); + services.JWTConfiguration(Configuration); + services.AutoMapperConfiguration(); + services.ConfigureExceptionHandler(this.Configuration); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + app.UseCors(x => x + .AllowAnyMethod() + .AllowAnyHeader() + .SetIsOriginAllowed(origin => true) // allow any origin + .AllowCredentials()); // allow credentials + + if (env.IsDevelopment()) + { + app.UseExceptionHandlerMiddlewareConfiguration(); + // app.UseDeveloperExceptionPage(); + } + else + { + app.UseHsts(); + app.UseExceptionHandlerMiddlewareConfiguration(); + } + + app.UseSwaggerConfiguration(); + app.UseDatabaseConfiguration(); + app.UseAutoMapperConfiguration(); + + app.UseSerilogRequestLogging(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllerRoute( + name: "default", + pattern: "api/{controller}/{action}" + ); + }); + } + } +} diff --git a/src/Web/DevHive.Web/appsettings.json b/src/Web/DevHive.Web/appsettings.json new file mode 100644 index 0000000..84d534d --- /dev/null +++ b/src/Web/DevHive.Web/appsettings.json @@ -0,0 +1,50 @@ +{ + "Jwt": { + "signingKey": "", + "validationIssuer": "", + "audience": "" + }, + "ConnectionStrings": { + "DEV": "Server=localhost;Port=5432;Database=DevHive_API;User Id=postgres;Password=;" + }, + "Cloud": { + "cloudName": "devhive", + "apiKey": "488664116365813", + "apiSecret": "" + }, + "Serilog": { + "Using": [], + "LevelSwitches": { + "$consoleSwitch": "Verbose", + "$fileSwitch": "Error" + }, + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "System": "Warning" + } + }, + "Enrich": [ + "FromLogContext", + "WithMachineName", + "WithProcessId", + "WithThreadId" + ], + "WriteTo": [ + { + "Name": "Console", + "Args": { + "levelSwitch": "$consoleSwitch" + } + }, + { + "Name": "File", + "Args": { + "path": "./Logs/errors.log", + "levelSwitch": "$fileSwitch" + } + } + ] + } +} |
