aboutsummaryrefslogtreecommitdiff
path: root/src/Web
diff options
context:
space:
mode:
authorKamen Mladenov <kamen.d.mladenov@protonmail.com>2021-04-09 19:51:35 +0300
committerGitHub <noreply@github.com>2021-04-09 19:51:35 +0300
commit233f38915ba0079079233eff55434ef349c05c45 (patch)
tree6c5f69017865bcab87355e910c87339453da1406 /src/Web
parentf4a70c6430db923af9fa9958a11c2d6612cb52cc (diff)
parenta992357efcf1bc1ece81b95ecee5e05a0b73bfdc (diff)
downloadDevHive-main.tar
DevHive-main.tar.gz
DevHive-main.zip
Merge pull request #28 from Team-Kaleidoscope/devHEADv0.2mainheroku/main
Second stage: Complete
Diffstat (limited to 'src/Web')
-rw-r--r--src/Web/DevHive.Web.Models/Attributes/GoodPasswordAttribute.cs23
-rw-r--r--src/Web/DevHive.Web.Models/Attributes/OnlyLettersModelValidation.cs20
-rw-r--r--src/Web/DevHive.Web.Models/Comment/CreateCommentWebModel.cs17
-rw-r--r--src/Web/DevHive.Web.Models/Comment/ReadCommentWebModel.cs21
-rw-r--r--src/Web/DevHive.Web.Models/Comment/UpdateCommentWebModel.cs13
-rw-r--r--src/Web/DevHive.Web.Models/DevHive.Web.Models.csproj13
-rw-r--r--src/Web/DevHive.Web.Models/Feed/GetPageWebModel.cs19
-rw-r--r--src/Web/DevHive.Web.Models/Feed/ReadPageWebModel.cs10
-rw-r--r--src/Web/DevHive.Web.Models/Language/CreateLanguageWebModel.cs14
-rw-r--r--src/Web/DevHive.Web.Models/Language/LanguageWebModel.cs13
-rw-r--r--src/Web/DevHive.Web.Models/Language/ReadLanguageWebModel.cs11
-rw-r--r--src/Web/DevHive.Web.Models/Language/UpdateLanguageWebModel.cs14
-rw-r--r--src/Web/DevHive.Web.Models/Post/CreatePostWebModel.cs16
-rw-r--r--src/Web/DevHive.Web.Models/Post/ReadPostWebModel.cs27
-rw-r--r--src/Web/DevHive.Web.Models/Post/UpdatePostWebModel.cs21
-rw-r--r--src/Web/DevHive.Web.Models/ProfilePicture/ProfilePictureWebModel.cs9
-rw-r--r--src/Web/DevHive.Web.Models/Rating/CreateRatingWebModel.cs11
-rw-r--r--src/Web/DevHive.Web.Models/Rating/ReadRatingWebModel.cs15
-rw-r--r--src/Web/DevHive.Web.Models/Rating/UpdateRatingWebModel.cs13
-rw-r--r--src/Web/DevHive.Web.Models/Role/CreateRoleWebModel.cs14
-rw-r--r--src/Web/DevHive.Web.Models/Role/RoleWebModel.cs14
-rw-r--r--src/Web/DevHive.Web.Models/Role/UpdateRoleWebModel.cs15
-rw-r--r--src/Web/DevHive.Web.Models/Technology/CreateTechnologyWebModel.cs14
-rw-r--r--src/Web/DevHive.Web.Models/Technology/ReadTechnologyWebModel.cs11
-rw-r--r--src/Web/DevHive.Web.Models/Technology/TechnologyWebModel.cs13
-rw-r--r--src/Web/DevHive.Web.Models/Technology/UpdateTechnologyWebModel.cs15
-rw-r--r--src/Web/DevHive.Web.Models/User/BaseUserWebModel.cs34
-rw-r--r--src/Web/DevHive.Web.Models/User/LoginWebModel.cs20
-rw-r--r--src/Web/DevHive.Web.Models/User/RegisterWebModel.cs14
-rw-r--r--src/Web/DevHive.Web.Models/User/TokenWebModel.cs12
-rw-r--r--src/Web/DevHive.Web.Models/User/UpdateUserWebModel.cs34
-rw-r--r--src/Web/DevHive.Web.Models/User/UserWebModel.cs33
-rw-r--r--src/Web/DevHive.Web.Models/User/UsernameWebModel.cs15
-rw-r--r--src/Web/DevHive.Web.Tests/CommentController.Tests.cs325
-rw-r--r--src/Web/DevHive.Web.Tests/DevHive.Web.Tests.csproj22
-rw-r--r--src/Web/DevHive.Web.Tests/FeedController.Tests.cs110
-rw-r--r--src/Web/DevHive.Web.Tests/LanguageController.Tests.cs224
-rw-r--r--src/Web/DevHive.Web.Tests/PostController.Tests.cs309
-rw-r--r--src/Web/DevHive.Web.Tests/RatingController.Tests.cs299
-rw-r--r--src/Web/DevHive.Web.Tests/RoleController.Tests.cs224
-rw-r--r--src/Web/DevHive.Web.Tests/TechnologyController.Tests.cs226
-rw-r--r--src/Web/DevHive.Web.Tests/UserController.Tests.cs257
-rw-r--r--src/Web/DevHive.Web/Configurations/Extensions/ConfigureAutoMapper.cs26
-rw-r--r--src/Web/DevHive.Web/Configurations/Extensions/ConfigureDatabase.cs98
-rw-r--r--src/Web/DevHive.Web/Configurations/Extensions/ConfigureDependencyInjection.cs51
-rw-r--r--src/Web/DevHive.Web/Configurations/Extensions/ConfigureExceptionHandlerMiddleware.cs36
-rw-r--r--src/Web/DevHive.Web/Configurations/Extensions/ConfigureJwt.cs46
-rw-r--r--src/Web/DevHive.Web/Configurations/Extensions/ConfigureSwagger.cs65
-rw-r--r--src/Web/DevHive.Web/Configurations/Mapping/CommentMappings.cs17
-rw-r--r--src/Web/DevHive.Web/Configurations/Mapping/FeedMappings.cs18
-rw-r--r--src/Web/DevHive.Web/Configurations/Mapping/LanguageMappings.cs23
-rw-r--r--src/Web/DevHive.Web/Configurations/Mapping/PostMappings.cs17
-rw-r--r--src/Web/DevHive.Web/Configurations/Mapping/ProfilePictureMappings.cs16
-rw-r--r--src/Web/DevHive.Web/Configurations/Mapping/RatingMappings.cs18
-rw-r--r--src/Web/DevHive.Web/Configurations/Mapping/RoleMappings.cs21
-rw-r--r--src/Web/DevHive.Web/Configurations/Mapping/TechnologyMappings.cs23
-rw-r--r--src/Web/DevHive.Web/Configurations/Mapping/UserMappings.cs30
-rw-r--r--src/Web/DevHive.Web/Controllers/CommentController.cs116
-rw-r--r--src/Web/DevHive.Web/Controllers/FeedController.cs69
-rw-r--r--src/Web/DevHive.Web/Controllers/FriendsController.cs54
-rw-r--r--src/Web/DevHive.Web/Controllers/LanguageController.cs115
-rw-r--r--src/Web/DevHive.Web/Controllers/PostController.cs123
-rw-r--r--src/Web/DevHive.Web/Controllers/ProfilePictureController.cs66
-rw-r--r--src/Web/DevHive.Web/Controllers/RatingController.cs98
-rw-r--r--src/Web/DevHive.Web/Controllers/RoleController.cs101
-rw-r--r--src/Web/DevHive.Web/Controllers/TechnologyController.cs115
-rw-r--r--src/Web/DevHive.Web/Controllers/UserController.cs172
-rw-r--r--src/Web/DevHive.Web/DevHive.Web.csproj43
-rw-r--r--src/Web/DevHive.Web/Middleware/ExceptionMiddleware.cs44
-rw-r--r--src/Web/DevHive.Web/Program.cs49
-rw-r--r--src/Web/DevHive.Web/Properties/launchSettings.json36
-rw-r--r--src/Web/DevHive.Web/Startup.cs75
-rw-r--r--src/Web/DevHive.Web/appsettings.json50
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"
+ }
+ }
+ ]
+ }
+}