From d53caaea6094136bac3d01ce9dd2782bb1819fe2 Mon Sep 17 00:00:00 2001 From: transtrike Date: Thu, 25 Mar 2021 12:22:54 +0200 Subject: Profile Picture implemented; Tests and Front end work await --- src/Data/DevHive.Data.Models/ProfilePicture.cs | 3 - src/Data/DevHive.Data/DevHiveContext.cs | 5 +- .../Interfaces/IProfilePictureRepository.cs | 11 + ...0210325081526_Profile_Picture_Layer.Designer.cs | 667 +++++++++++++++++++++ .../20210325081526_Profile_Picture_Layer.cs | 78 +++ .../Migrations/DevHiveContextModelSnapshot.cs | 30 +- .../Repositories/ProfilePictureRepository.cs | 25 + .../ProfilePicture/ProfilePictureServiceModel.cs | 11 + .../User/ProfilePictureServiceModel.cs | 7 - .../User/UpdateProfilePictureServiceModel.cs | 12 - .../Interfaces/IProfilePictureService.cs | 22 + .../DevHive.Services/Interfaces/IUserService.cs | 8 - .../Services/ProfilePictureService.cs | 99 +++ .../DevHive.Services/Services/UserService.cs | 22 - .../ProfilePicture/ProfilePictureWebModel.cs | 9 + .../User/ProfilePictureWebModel.cs | 7 - .../User/UpdateProfilePictureWebModel.cs | 9 - .../Extensions/ConfigureDependencyInjection.cs | 7 +- .../Mapping/ProfilePictureMappings.cs | 16 + .../Configurations/Mapping/UserMappings.cs | 3 - .../Controllers/ProfilePictureController.cs | 42 +- 21 files changed, 978 insertions(+), 115 deletions(-) create mode 100644 src/Data/DevHive.Data/Interfaces/IProfilePictureRepository.cs create mode 100644 src/Data/DevHive.Data/Migrations/20210325081526_Profile_Picture_Layer.Designer.cs create mode 100644 src/Data/DevHive.Data/Migrations/20210325081526_Profile_Picture_Layer.cs create mode 100644 src/Data/DevHive.Data/Repositories/ProfilePictureRepository.cs create mode 100644 src/Services/DevHive.Services.Models/ProfilePicture/ProfilePictureServiceModel.cs delete mode 100644 src/Services/DevHive.Services.Models/User/ProfilePictureServiceModel.cs delete mode 100644 src/Services/DevHive.Services.Models/User/UpdateProfilePictureServiceModel.cs create mode 100644 src/Services/DevHive.Services/Interfaces/IProfilePictureService.cs create mode 100644 src/Services/DevHive.Services/Services/ProfilePictureService.cs create mode 100644 src/Web/DevHive.Web.Models/ProfilePicture/ProfilePictureWebModel.cs delete mode 100644 src/Web/DevHive.Web.Models/User/ProfilePictureWebModel.cs delete mode 100644 src/Web/DevHive.Web.Models/User/UpdateProfilePictureWebModel.cs create mode 100644 src/Web/DevHive.Web/Configurations/Mapping/ProfilePictureMappings.cs (limited to 'src') diff --git a/src/Data/DevHive.Data.Models/ProfilePicture.cs b/src/Data/DevHive.Data.Models/ProfilePicture.cs index e2ef04b..4b629c2 100644 --- a/src/Data/DevHive.Data.Models/ProfilePicture.cs +++ b/src/Data/DevHive.Data.Models/ProfilePicture.cs @@ -6,9 +6,6 @@ namespace DevHive.Data.Models { public Guid Id { get; set; } - public Guid UserId { get; set; } - public User User { get; set; } - public string PictureURL { get; set; } } } diff --git a/src/Data/DevHive.Data/DevHiveContext.cs b/src/Data/DevHive.Data/DevHiveContext.cs index b0bbdc6..c3adf2d 100644 --- a/src/Data/DevHive.Data/DevHiveContext.cs +++ b/src/Data/DevHive.Data/DevHiveContext.cs @@ -17,6 +17,7 @@ namespace DevHive.Data public DbSet PostAttachments { get; set; } public DbSet Comments { get; set; } public DbSet Rating { get; set; } + public DbSet ProfilePicture { get; set; } public DbSet RatedPost { get; set; } public DbSet UserRate { get; set; } @@ -28,9 +29,7 @@ namespace DevHive.Data .IsUnique(); builder.Entity() - .HasOne(x => x.ProfilePicture) - .WithOne(x => x.User) - .HasForeignKey(x => x.UserId); + .HasOne(x => x.ProfilePicture); /* Roles */ builder.Entity() diff --git a/src/Data/DevHive.Data/Interfaces/IProfilePictureRepository.cs b/src/Data/DevHive.Data/Interfaces/IProfilePictureRepository.cs new file mode 100644 index 0000000..45beaa0 --- /dev/null +++ b/src/Data/DevHive.Data/Interfaces/IProfilePictureRepository.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading.Tasks; +using DevHive.Data.Models; + +namespace DevHive.Data.Interfaces +{ + public interface IProfilePictureRepository : IRepository + { + Task GetByURLAsync(string picUrl); + } +} diff --git a/src/Data/DevHive.Data/Migrations/20210325081526_Profile_Picture_Layer.Designer.cs b/src/Data/DevHive.Data/Migrations/20210325081526_Profile_Picture_Layer.Designer.cs new file mode 100644 index 0000000..189060d --- /dev/null +++ b/src/Data/DevHive.Data/Migrations/20210325081526_Profile_Picture_Layer.Designer.cs @@ -0,0 +1,667 @@ +// +using System; +using DevHive.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +namespace DevHive.Data.Migrations +{ + [DbContext(typeof(DevHiveContext))] + [Migration("20210325081526_Profile_Picture_Layer")] + partial class Profile_Picture_Layer + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Relational:MaxIdentifierLength", 63) + .HasAnnotation("ProductVersion", "5.0.3") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + modelBuilder.Entity("DevHive.Data.Models.Comment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Message") + .HasColumnType("text"); + + b.Property("PostId") + .HasColumnType("uuid"); + + b.Property("TimeCreated") + .HasColumnType("timestamp without time zone"); + + b.HasKey("Id"); + + b.HasIndex("CreatorId"); + + b.HasIndex("PostId"); + + b.ToTable("Comments"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Language", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Languages"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Post", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Message") + .HasColumnType("text"); + + b.Property("TimeCreated") + .HasColumnType("timestamp without time zone"); + + b.HasKey("Id"); + + b.HasIndex("CreatorId"); + + b.ToTable("Posts"); + }); + + modelBuilder.Entity("DevHive.Data.Models.ProfilePicture", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("PictureURL") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("ProfilePicture"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Rating", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsLike") + .HasColumnType("boolean"); + + b.Property("PostId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PostId"); + + b.HasIndex("UserId"); + + b.ToTable("Rating"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Relational.PostAttachments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("FileUrl") + .HasColumnType("text"); + + b.Property("PostId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PostId"); + + b.ToTable("PostAttachments"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Relational.RatedPost", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("PostId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "PostId"); + + b.HasIndex("PostId"); + + b.ToTable("RatedPosts"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Relational.UserRate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Liked") + .HasColumnType("boolean"); + + b.Property("PostId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PostId"); + + b.HasIndex("UserId"); + + b.ToTable("UserRates"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Technology", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Technologies"); + }); + + modelBuilder.Entity("DevHive.Data.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("FirstName") + .HasColumnType("text"); + + b.Property("LastName") + .HasColumnType("text"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("ProfilePictureId") + .HasColumnType("uuid"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("ProfilePictureId"); + + b.HasIndex("UserId"); + + b.HasIndex("UserName") + .IsUnique(); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("LanguageUser", b => + { + b.Property("LanguagesId") + .HasColumnType("uuid"); + + b.Property("UsersId") + .HasColumnType("uuid"); + + b.HasKey("LanguagesId", "UsersId"); + + b.HasIndex("UsersId"); + + b.ToTable("LanguageUser"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("RoleUser", b => + { + b.Property("RolesId") + .HasColumnType("uuid"); + + b.Property("UsersId") + .HasColumnType("uuid"); + + b.HasKey("RolesId", "UsersId"); + + b.HasIndex("UsersId"); + + b.ToTable("RoleUser"); + }); + + modelBuilder.Entity("TechnologyUser", b => + { + b.Property("TechnologiesId") + .HasColumnType("uuid"); + + b.Property("UsersId") + .HasColumnType("uuid"); + + b.HasKey("TechnologiesId", "UsersId"); + + b.HasIndex("UsersId"); + + b.ToTable("TechnologyUser"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Comment", b => + { + b.HasOne("DevHive.Data.Models.User", "Creator") + .WithMany("Comments") + .HasForeignKey("CreatorId"); + + b.HasOne("DevHive.Data.Models.Post", "Post") + .WithMany("Comments") + .HasForeignKey("PostId"); + + b.Navigation("Creator"); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Post", b => + { + b.HasOne("DevHive.Data.Models.User", "Creator") + .WithMany("Posts") + .HasForeignKey("CreatorId"); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Rating", b => + { + b.HasOne("DevHive.Data.Models.Post", "Post") + .WithMany("Ratings") + .HasForeignKey("PostId"); + + b.HasOne("DevHive.Data.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Post"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Relational.PostAttachments", b => + { + b.HasOne("DevHive.Data.Models.Post", "Post") + .WithMany("Attachments") + .HasForeignKey("PostId"); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Relational.RatedPost", b => + { + b.HasOne("DevHive.Data.Models.Post", "Post") + .WithMany() + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DevHive.Data.Models.User", "User") + .WithMany("RatedPosts") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Post"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("DevHive.Data.Models.Relational.UserRate", b => + { + b.HasOne("DevHive.Data.Models.Post", "Post") + .WithMany() + .HasForeignKey("PostId"); + + b.HasOne("DevHive.Data.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Post"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("DevHive.Data.Models.User", b => + { + b.HasOne("DevHive.Data.Models.ProfilePicture", "ProfilePicture") + .WithMany() + .HasForeignKey("ProfilePictureId"); + + b.HasOne("DevHive.Data.Models.User", null) + .WithMany("Friends") + .HasForeignKey("UserId"); + + b.Navigation("ProfilePicture"); + }); + + modelBuilder.Entity("LanguageUser", b => + { + b.HasOne("DevHive.Data.Models.Language", null) + .WithMany() + .HasForeignKey("LanguagesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DevHive.Data.Models.User", null) + .WithMany() + .HasForeignKey("UsersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("DevHive.Data.Models.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("DevHive.Data.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("DevHive.Data.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("DevHive.Data.Models.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DevHive.Data.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("DevHive.Data.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("RoleUser", b => + { + b.HasOne("DevHive.Data.Models.Role", null) + .WithMany() + .HasForeignKey("RolesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DevHive.Data.Models.User", null) + .WithMany() + .HasForeignKey("UsersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("TechnologyUser", b => + { + b.HasOne("DevHive.Data.Models.Technology", null) + .WithMany() + .HasForeignKey("TechnologiesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DevHive.Data.Models.User", null) + .WithMany() + .HasForeignKey("UsersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("DevHive.Data.Models.Post", b => + { + b.Navigation("Attachments"); + + b.Navigation("Comments"); + + b.Navigation("Ratings"); + }); + + modelBuilder.Entity("DevHive.Data.Models.User", b => + { + b.Navigation("Comments"); + + b.Navigation("Friends"); + + b.Navigation("Posts"); + + b.Navigation("RatedPosts"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Data/DevHive.Data/Migrations/20210325081526_Profile_Picture_Layer.cs b/src/Data/DevHive.Data/Migrations/20210325081526_Profile_Picture_Layer.cs new file mode 100644 index 0000000..0ee1fb7 --- /dev/null +++ b/src/Data/DevHive.Data/Migrations/20210325081526_Profile_Picture_Layer.cs @@ -0,0 +1,78 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace DevHive.Data.Migrations +{ + public partial class Profile_Picture_Layer : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ProfilePicture_AspNetUsers_UserId", + table: "ProfilePicture"); + + migrationBuilder.DropIndex( + name: "IX_ProfilePicture_UserId", + table: "ProfilePicture"); + + migrationBuilder.DropColumn( + name: "UserId", + table: "ProfilePicture"); + + migrationBuilder.AddColumn( + name: "ProfilePictureId", + table: "AspNetUsers", + type: "uuid", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUsers_ProfilePictureId", + table: "AspNetUsers", + column: "ProfilePictureId"); + + migrationBuilder.AddForeignKey( + name: "FK_AspNetUsers_ProfilePicture_ProfilePictureId", + table: "AspNetUsers", + column: "ProfilePictureId", + principalTable: "ProfilePicture", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_AspNetUsers_ProfilePicture_ProfilePictureId", + table: "AspNetUsers"); + + migrationBuilder.DropIndex( + name: "IX_AspNetUsers_ProfilePictureId", + table: "AspNetUsers"); + + migrationBuilder.DropColumn( + name: "ProfilePictureId", + table: "AspNetUsers"); + + migrationBuilder.AddColumn( + name: "UserId", + table: "ProfilePicture", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.CreateIndex( + name: "IX_ProfilePicture_UserId", + table: "ProfilePicture", + column: "UserId", + unique: true); + + migrationBuilder.AddForeignKey( + name: "FK_ProfilePicture_AspNetUsers_UserId", + table: "ProfilePicture", + column: "UserId", + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/src/Data/DevHive.Data/Migrations/DevHiveContextModelSnapshot.cs b/src/Data/DevHive.Data/Migrations/DevHiveContextModelSnapshot.cs index 80e3ac0..8877fab 100644 --- a/src/Data/DevHive.Data/Migrations/DevHiveContextModelSnapshot.cs +++ b/src/Data/DevHive.Data/Migrations/DevHiveContextModelSnapshot.cs @@ -91,14 +91,8 @@ namespace DevHive.Data.Migrations b.Property("PictureURL") .HasColumnType("text"); - b.Property("UserId") - .HasColumnType("uuid"); - b.HasKey("Id"); - b.HasIndex("UserId") - .IsUnique(); - b.ToTable("ProfilePicture"); }); @@ -274,6 +268,9 @@ namespace DevHive.Data.Migrations b.Property("PhoneNumberConfirmed") .HasColumnType("boolean"); + b.Property("ProfilePictureId") + .HasColumnType("uuid"); + b.Property("SecurityStamp") .HasColumnType("text"); @@ -296,6 +293,8 @@ namespace DevHive.Data.Migrations .IsUnique() .HasDatabaseName("UserNameIndex"); + b.HasIndex("ProfilePictureId"); + b.HasIndex("UserId"); b.HasIndex("UserName") @@ -474,17 +473,6 @@ namespace DevHive.Data.Migrations b.Navigation("Creator"); }); - modelBuilder.Entity("DevHive.Data.Models.ProfilePicture", b => - { - b.HasOne("DevHive.Data.Models.User", "User") - .WithOne("ProfilePicture") - .HasForeignKey("DevHive.Data.Models.ProfilePicture", "UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - modelBuilder.Entity("DevHive.Data.Models.Rating", b => { b.HasOne("DevHive.Data.Models.Post", "Post") @@ -545,9 +533,15 @@ namespace DevHive.Data.Migrations modelBuilder.Entity("DevHive.Data.Models.User", b => { + b.HasOne("DevHive.Data.Models.ProfilePicture", "ProfilePicture") + .WithMany() + .HasForeignKey("ProfilePictureId"); + b.HasOne("DevHive.Data.Models.User", null) .WithMany("Friends") .HasForeignKey("UserId"); + + b.Navigation("ProfilePicture"); }); modelBuilder.Entity("LanguageUser", b => @@ -663,8 +657,6 @@ namespace DevHive.Data.Migrations b.Navigation("Posts"); - b.Navigation("ProfilePicture"); - b.Navigation("RatedPosts"); }); #pragma warning restore 612, 618 diff --git a/src/Data/DevHive.Data/Repositories/ProfilePictureRepository.cs b/src/Data/DevHive.Data/Repositories/ProfilePictureRepository.cs new file mode 100644 index 0000000..7284464 --- /dev/null +++ b/src/Data/DevHive.Data/Repositories/ProfilePictureRepository.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using Microsoft.EntityFrameworkCore; + +namespace DevHive.Data.Repositories +{ + public class ProfilePictureRepository : BaseRepository, IProfilePictureRepository + { + private readonly DevHiveContext _context; + + public ProfilePictureRepository(DevHiveContext context) + : base(context) + { + this._context = context; + } + + public async Task GetByURLAsync(string picUrl) + { + return await this._context + .ProfilePicture + .FirstOrDefaultAsync(x => x.PictureURL == picUrl); + } + } +} diff --git a/src/Services/DevHive.Services.Models/ProfilePicture/ProfilePictureServiceModel.cs b/src/Services/DevHive.Services.Models/ProfilePicture/ProfilePictureServiceModel.cs new file mode 100644 index 0000000..5e69d13 --- /dev/null +++ b/src/Services/DevHive.Services.Models/ProfilePicture/ProfilePictureServiceModel.cs @@ -0,0 +1,11 @@ +using System; +using Microsoft.AspNetCore.Http; + +namespace DevHive.Services.Models.ProfilePicture +{ + public class ProfilePictureServiceModel + { + public Guid UserId { get; set; } + public IFormFile ProfilePictureFormFile { get; set; } + } +} diff --git a/src/Services/DevHive.Services.Models/User/ProfilePictureServiceModel.cs b/src/Services/DevHive.Services.Models/User/ProfilePictureServiceModel.cs deleted file mode 100644 index cbe64b2..0000000 --- a/src/Services/DevHive.Services.Models/User/ProfilePictureServiceModel.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace DevHive.Services.Models.User -{ - public class ProfilePictureServiceModel - { - public string ProfilePictureURL { get; set; } - } -} diff --git a/src/Services/DevHive.Services.Models/User/UpdateProfilePictureServiceModel.cs b/src/Services/DevHive.Services.Models/User/UpdateProfilePictureServiceModel.cs deleted file mode 100644 index 19ba08f..0000000 --- a/src/Services/DevHive.Services.Models/User/UpdateProfilePictureServiceModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using Microsoft.AspNetCore.Http; - -namespace DevHive.Services.Models.User -{ - public class UpdateProfilePictureServiceModel - { - public Guid UserId { get; set; } - - public IFormFile Picture { get; set; } - } -} diff --git a/src/Services/DevHive.Services/Interfaces/IProfilePictureService.cs b/src/Services/DevHive.Services/Interfaces/IProfilePictureService.cs new file mode 100644 index 0000000..b0673ac --- /dev/null +++ b/src/Services/DevHive.Services/Interfaces/IProfilePictureService.cs @@ -0,0 +1,22 @@ +using System; +using System.Threading.Tasks; +using DevHive.Services.Models.ProfilePicture; + +namespace DevHive.Services.Interfaces +{ + public interface IProfilePictureService + { + Task InsertProfilePicture(ProfilePictureServiceModel profilePictureServiceModel); + + Task GetProfilePictureById(Guid id); + + /// + /// Uploads the given picture and assigns it's link to the user in the database + /// + /// Contains User's Guid and the new picture to be updated + /// The new picture's URL + Task UpdateProfilePicture(ProfilePictureServiceModel profilePictureServiceModel); + + Task DeleteProfilePicture(Guid id); + } +} diff --git a/src/Services/DevHive.Services/Interfaces/IUserService.cs b/src/Services/DevHive.Services/Interfaces/IUserService.cs index a55f9dd..da07507 100644 --- a/src/Services/DevHive.Services/Interfaces/IUserService.cs +++ b/src/Services/DevHive.Services/Interfaces/IUserService.cs @@ -44,14 +44,6 @@ namespace DevHive.Services.Interfaces /// Read model of the new user Task UpdateUser(UpdateUserServiceModel updateUserServiceModel); - /// - /// Uploads the given picture and assigns it's link to the user in the database - /// Requires authenticated user - /// - /// Contains User's Guid and the new picture to be updated - /// The new picture's URL - Task UpdateProfilePicture(UpdateProfilePictureServiceModel updateProfilePictureServiceModel); - /// /// Deletes a user from the database and removes his data entirely /// Requires authenticated user diff --git a/src/Services/DevHive.Services/Services/ProfilePictureService.cs b/src/Services/DevHive.Services/Services/ProfilePictureService.cs new file mode 100644 index 0000000..0636f5c --- /dev/null +++ b/src/Services/DevHive.Services/Services/ProfilePictureService.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.ProfilePicture; +using Microsoft.AspNetCore.Http; + +namespace DevHive.Services.Services +{ + public class ProfilePictureService : IProfilePictureService + { + private readonly IUserRepository _userRepository; + private readonly IProfilePictureRepository _profilePictureRepository; + private readonly ICloudService _cloudinaryService; + + public ProfilePictureService(IUserRepository userRepository, IProfilePictureRepository profilePictureRepository, ICloudService cloudinaryService) + { + this._userRepository = userRepository; + this._profilePictureRepository = profilePictureRepository; + this._cloudinaryService = cloudinaryService; + } + + public async Task InsertProfilePicture(ProfilePictureServiceModel profilePictureServiceModel) + { + await ValidateServiceModel(profilePictureServiceModel); + + return await SaveProfilePictureInDatabase(profilePictureServiceModel); + } + + public async Task GetProfilePictureById(Guid id) + { + return (await this._profilePictureRepository.GetByIdAsync(id)).PictureURL; + } + + public async Task UpdateProfilePicture(ProfilePictureServiceModel profilePictureServiceModel) + { + await ValidateServiceModel(profilePictureServiceModel); + + User user = await this._userRepository.GetByIdAsync(profilePictureServiceModel.UserId); + if (!string.IsNullOrEmpty(user.ProfilePicture.PictureURL)) + { + List file = new() { user.ProfilePicture.PictureURL }; + bool removed = await this._cloudinaryService.RemoveFilesFromCloud(file); + + if (!removed) + throw new ArgumentException("Cannot delete old picture"); + } + + return await SaveProfilePictureInDatabase(profilePictureServiceModel); + } + + public async Task DeleteProfilePicture(Guid id) + { + ProfilePicture profilePic = await this._profilePictureRepository.GetByIdAsync(id) ?? + throw new ArgumentException("Such picture doesn't exist!"); + + bool removedFromDb = await this._profilePictureRepository.DeleteAsync(profilePic); + if (!removedFromDb) + throw new ArgumentException("Cannot delete picture from database!"); + + List file = new() { profilePic.PictureURL }; + bool removedFromCloud = await this._cloudinaryService.RemoveFilesFromCloud(file); + if (!removedFromCloud) + throw new ArgumentException("Cannot delete picture from cloud!"); + + return true; + } + + private async Task SaveProfilePictureInDatabase(ProfilePictureServiceModel profilePictureServiceModel) + { + List file = new() { profilePictureServiceModel.ProfilePictureFormFile }; + string picUrl = (await this._cloudinaryService.UploadFilesToCloud(file))[0]; + ProfilePicture profilePic = new() { PictureURL = picUrl }; + + bool success = await this._profilePictureRepository.AddAsync(profilePic); + if (!success) + throw new ArgumentException("Unable to upload picture!"); + + User user = await this._userRepository.GetByIdAsync(profilePictureServiceModel.UserId); + user.ProfilePicture = await this._profilePictureRepository.GetByURLAsync(picUrl); + bool userProfilePicAlter = await this._userRepository.EditAsync(user.Id, user); + if (!userProfilePicAlter) + throw new ArgumentException("Unable to alter user's profile picture"); + + return picUrl; + } + + private async Task ValidateServiceModel(ProfilePictureServiceModel profilePictureServiceModel) + { + if (profilePictureServiceModel.ProfilePictureFormFile.Length == 0) + throw new ArgumentException("Picture cannot be null"); + + if (!await this._userRepository.DoesUserExistAsync(profilePictureServiceModel.UserId)) + throw new ArgumentException("User does not exist!"); + } + } +} diff --git a/src/Services/DevHive.Services/Services/UserService.cs b/src/Services/DevHive.Services/Services/UserService.cs index 4f74b06..d487de4 100644 --- a/src/Services/DevHive.Services/Services/UserService.cs +++ b/src/Services/DevHive.Services/Services/UserService.cs @@ -119,28 +119,6 @@ namespace DevHive.Services.Services User newUser = await this._userRepository.GetByIdAsync(user.Id); return this._userMapper.Map(newUser); } - - public async Task UpdateProfilePicture(UpdateProfilePictureServiceModel updateProfilePictureServiceModel) - { - User user = await this._userRepository.GetByIdAsync(updateProfilePictureServiceModel.UserId); - - if (!string.IsNullOrEmpty(user.ProfilePicture.PictureURL)) - { - bool success = await _cloudService.RemoveFilesFromCloud(new List { user.ProfilePicture.PictureURL }); - if (!success) - throw new InvalidCastException("Could not delete old profile picture!"); - } - - string fileUrl = (await this._cloudService.UploadFilesToCloud(new List { updateProfilePictureServiceModel.Picture }))[0] ?? - throw new ArgumentException("Unable to upload profile picture to cloud"); - - bool successful = await this._userRepository.UpdateProfilePicture(updateProfilePictureServiceModel.UserId, fileUrl); - - if (!successful) - throw new InvalidOperationException("Unable to change profile picture!"); - - return new ProfilePictureServiceModel() { ProfilePictureURL = fileUrl }; - } #endregion #region Delete 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/User/ProfilePictureWebModel.cs b/src/Web/DevHive.Web.Models/User/ProfilePictureWebModel.cs deleted file mode 100644 index e09ee08..0000000 --- a/src/Web/DevHive.Web.Models/User/ProfilePictureWebModel.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace DevHive.Web.Models.User -{ - public class ProfilePictureWebModel - { - public string ProfilePictureURL { get; set; } - } -} diff --git a/src/Web/DevHive.Web.Models/User/UpdateProfilePictureWebModel.cs b/src/Web/DevHive.Web.Models/User/UpdateProfilePictureWebModel.cs deleted file mode 100644 index 196d1e7..0000000 --- a/src/Web/DevHive.Web.Models/User/UpdateProfilePictureWebModel.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Microsoft.AspNetCore.Http; - -namespace DevHive.Web.Models.User -{ - public class UpdateProfilePictureWebModel - { - public IFormFile Picture { get; set; } - } -} diff --git a/src/Web/DevHive.Web/Configurations/Extensions/ConfigureDependencyInjection.cs b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureDependencyInjection.cs index a0d0979..20b66e1 100644 --- a/src/Web/DevHive.Web/Configurations/Extensions/ConfigureDependencyInjection.cs +++ b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureDependencyInjection.cs @@ -17,20 +17,22 @@ namespace DevHive.Web.Configurations.Extensions services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddTransient(options => new CloudinaryService( @@ -43,7 +45,6 @@ namespace DevHive.Web.Configurations.Extensions signingKey: Encoding.ASCII.GetBytes(configuration.GetSection("Jwt").GetSection("signingKey").Value), validationIssuer: configuration.GetSection("Jwt").GetSection("validationIssuer").Value, audience: configuration.GetSection("Jwt").GetSection("audience").Value)); - services.AddTransient(); } } } 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() + .ForMember(dest => dest.ProfilePictureFormFile, src => src.MapFrom(p => p.Picture)); + } + } +} diff --git a/src/Web/DevHive.Web/Configurations/Mapping/UserMappings.cs b/src/Web/DevHive.Web/Configurations/Mapping/UserMappings.cs index 14aaa3a..dabec93 100644 --- a/src/Web/DevHive.Web/Configurations/Mapping/UserMappings.cs +++ b/src/Web/DevHive.Web/Configurations/Mapping/UserMappings.cs @@ -23,9 +23,6 @@ namespace DevHive.Web.Configurations.Mapping CreateMap(); CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); CreateMap(); } diff --git a/src/Web/DevHive.Web/Controllers/ProfilePictureController.cs b/src/Web/DevHive.Web/Controllers/ProfilePictureController.cs index 2eec99e..1c736f6 100644 --- a/src/Web/DevHive.Web/Controllers/ProfilePictureController.cs +++ b/src/Web/DevHive.Web/Controllers/ProfilePictureController.cs @@ -1,8 +1,12 @@ using System; using System.Threading.Tasks; -using DevHive.Web.Models.User; 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 { @@ -13,36 +17,36 @@ namespace DevHive.Web.Controllers [Route("api/[controller]")] public class ProfilePictureController { - // private readonly ProfilePictureService _profilePictureService; + private readonly IProfilePictureService _profilePictureService; + private readonly IJwtService _jwtService; + private readonly IMapper _profilePictureMapper; - // public ProfilePictureController(ProfilePictureService profilePictureService) - // { - // this._profilePictureService = profilePictureService; - // } + public ProfilePictureController(IProfilePictureService profilePictureService, IJwtService jwtService, IMapper profilePictureMapper) + { + this._profilePictureService = profilePictureService; + this._jwtService = jwtService; + this._profilePictureMapper = profilePictureMapper; + } /// /// Alter the profile picture of a user /// /// The user's Id - /// The new profile picture + /// The new profile picture /// JWT Bearer Token - /// ??? + /// The URL of the new profile picture [HttpPut] - [Route("ProfilePicture")] [Authorize(Roles = "User,Admin")] - public async Task UpdateProfilePicture(Guid userId, [FromForm] UpdateProfilePictureWebModel updateProfilePictureWebModel, [FromHeader] string authorization) + public async Task UpdateProfilePicture(Guid userId, [FromForm] ProfilePictureWebModel profilePictureWebModel, [FromHeader] string authorization) { - throw new NotImplementedException(); - // if (!await this._userService.ValidJWT(userId, authorization)) - // return new UnauthorizedResult(); - - // UpdateProfilePictureServiceModel updateProfilePictureServiceModel = this._userMapper.Map(updateProfilePictureWebModel); - // updateProfilePictureServiceModel.UserId = userId; + if (!this._jwtService.ValidateToken(userId, authorization)) + return new UnauthorizedResult(); - // ProfilePictureServiceModel profilePictureServiceModel = await this._userService.UpdateProfilePicture(updateProfilePictureServiceModel); - // ProfilePictureWebModel profilePictureWebModel = this._userMapper.Map(profilePictureServiceModel); + ProfilePictureServiceModel profilePictureServiceModel = this._profilePictureMapper.Map(profilePictureWebModel); + profilePictureServiceModel.UserId = userId; - // return new AcceptedResult("UpdateProfilePicture", profilePictureWebModel); + string url = await this._profilePictureService.UpdateProfilePicture(profilePictureServiceModel); + return new OkObjectResult(new { URL = url }); } } } -- cgit v1.2.3