diff --git a/src/Application/Common/Interfaces/IIdentityService.cs b/src/Application/Common/Interfaces/IIdentityService.cs index cd97117..d92663e 100644 --- a/src/Application/Common/Interfaces/IIdentityService.cs +++ b/src/Application/Common/Interfaces/IIdentityService.cs @@ -1,21 +1,27 @@ using Google.Apis.Oauth2.v2.Data; using Hutopy.Application.Common.Models; +using Hutopy.Application.Users.Models; namespace Hutopy.Application.Common.Interfaces; public interface IIdentityService { - Task GetUserNameAsync(string userId); - Task CreateUserAsync(string email, string userName, string firstName, string lastName, string password); - Task FindUserByIdAsync(string id); + Task> CreateUserAsync(Userinfo userInfo); + Task> CreateUserAsync(string email, string userName, string firstName, string lastName, string password); Task GetCurrentUserAsync(); - Task FindUserByEmailAsync(string id); - Task LoginAsync(string email, string password); + Task> UpdateCurrentUserAsync(string id, string firstName, string lastName, string occupation, + string phoneNumber, string birthDate, string country, string city, string address, string about, + string description, + SocialNetworksModel socialNetworks); + Task> GetCurrentUserRolesAsync(); + Task FindUserByIdAsync(string id); + Task FindUserByEmailAsync(string email); Task GetUserByUserNameAsync(string userName); + Task LoginAsync(string email, string password); Task IsInRoleAsync(string userId, string role); Task AuthorizeAsync(string userId, string policyName); + Task GetUserNameAsync(string userId); + Task AddRoleAsync(string userId, string role); - Task> GetCurrentUserRolesAsync(); - Task<(Result Result, string UserId)> CreateUserAsync(Userinfo userInfo); Task DeleteUserAsync(string userId); } diff --git a/src/Application/Common/Models/Result.cs b/src/Application/Common/Models/Result.cs index f99bb27..9552531 100644 --- a/src/Application/Common/Models/Result.cs +++ b/src/Application/Common/Models/Result.cs @@ -5,9 +5,8 @@ public class Result( IEnumerable errors) { public bool Succeeded { get; init; } = succeeded; - public string[] Errors { get; init; } = errors.ToArray(); - + public static Result Success() { return new Result(true, Array.Empty()); @@ -18,3 +17,27 @@ public class Result( return new Result(false, errors); } } + +public class Result( + bool succeeded, + IEnumerable errors) +{ + public bool Succeeded { get; init; } = succeeded; + public string[] Errors { get; init; } = errors.ToArray(); + public T? Value { get; set; } + + public T GetValueOrDefault() + { + return Value ?? default(T)!; + } + + public static Result Success() + { + return new Result(true, Array.Empty()); + } + + public static Result Failure(IEnumerable errors) + { + return new Result(false, errors); + } +} diff --git a/src/Application/Common/Models/UserModel.cs b/src/Application/Common/Models/UserModel.cs index 71a2680..ac09db9 100644 --- a/src/Application/Common/Models/UserModel.cs +++ b/src/Application/Common/Models/UserModel.cs @@ -1,3 +1,5 @@ +using Hutopy.Application.Users.Models; + namespace Hutopy.Application.Common.Models; // TODO: Review nullable affectation here @@ -7,6 +9,15 @@ public class UserModel public string? UserName { get; set; } public string? FirstName { get; set; } public string? LastName { get; set; } - public string? Email { get; set; } + public string? Occupation { get; set; } + public string? Email { get; init; } = String.Empty; + public string? Phone { get; init; } = String.Empty; + public string? BirthDate { get; init; } = String.Empty; + public string? Country { get; init; } = String.Empty; + public string? City { get; init; } = String.Empty; + public string? Address { get; init; } = String.Empty; + public string? About { get; init; } = String.Empty; + public string? Description { get; init; } = String.Empty; + public SocialNetworksModel SocialNetworks { get; init; } = new(); public string? PortraitUrl { get; set; } } diff --git a/src/Application/Users/Commands/UpdateCurrentUserCommand.cs b/src/Application/Users/Commands/UpdateCurrentUserCommand.cs new file mode 100644 index 0000000..3a23915 --- /dev/null +++ b/src/Application/Users/Commands/UpdateCurrentUserCommand.cs @@ -0,0 +1,40 @@ +using Hutopy.Application.Common.Interfaces; +using Hutopy.Application.Users.Models; + +namespace Hutopy.Application.Users.Commands; + +public class UpdateCurrentUserCommand : IRequest +{ + public required string FirstName { get; init; } + public required string LastName { get; init; } + public required string Occupation { get; init; } + public required string PhoneNumber { get; init; } + public required string BirthDate { get; init; } + public required string Country { get; init; } + public required string City { get; init; } + public required string Address { get; init; } + public required string About { get; init; } + public required string Description { get; init; } + public required SocialNetworksModel SocialNetworks { get; init; } +} + +public class UpdateCurrentUserCommandHandler(IApplicationDbContext context, IIdentityService identityService) : + IRequestHandler +{ + public async Task Handle(UpdateCurrentUserCommand request, CancellationToken cancellationToken) + { + var identityUser = await identityService.GetCurrentUserAsync(); + + if (identityUser?.Id is null) return string.Empty; + + var result = await identityService.UpdateCurrentUserAsync(identityUser.Id, request.FirstName, request.LastName, + request.Occupation, request.PhoneNumber, request.BirthDate, + request.Country, request.City, request.Address, request.About, + request.Description, request.SocialNetworks); + + await context.SaveChangesAsync(cancellationToken); + + return result.GetValueOrDefault(); + } +} + diff --git a/src/Application/Users/Models/SocialNetworksModel.cs b/src/Application/Users/Models/SocialNetworksModel.cs new file mode 100644 index 0000000..edd3cce --- /dev/null +++ b/src/Application/Users/Models/SocialNetworksModel.cs @@ -0,0 +1,13 @@ +namespace Hutopy.Application.Users.Models; + +public class SocialNetworksModel +{ + public string FacebookUrl { get; init; } = String.Empty; + public string InstagramUrl { get; init; } = String.Empty; + public string XUrl { get; init; } = String.Empty; + public string LinkedInUrl { get; init; } = String.Empty; + public string TikTokUrl { get; init; } = String.Empty; + public string YoutubeUrl { get; init; } = String.Empty; + public string RedditUrl { get; init; } = String.Empty; + public string YourWebsiteUrl { get; init; } = String.Empty; +} diff --git a/src/Application/Users/Queries/GetCurrentUser/GetCurrentUser.cs b/src/Application/Users/Queries/GetCurrentUser/GetCurrentUser.cs index 7cbabd6..eeb9f7c 100644 --- a/src/Application/Users/Queries/GetCurrentUser/GetCurrentUser.cs +++ b/src/Application/Users/Queries/GetCurrentUser/GetCurrentUser.cs @@ -28,12 +28,22 @@ public class GetCurrentUserQueryHandler( var user = new UserDto { Id = currentUserId, - FirstName = identityUser?.FirstName ?? "", - LastName = identityUser?.LastName ?? "", - UserName = identityUser?.UserName ?? "", + FirstName = identityUser.FirstName ?? "", + LastName = identityUser.LastName ?? "", + UserName = identityUser.UserName ?? "", + Occupation = identityUser.Occupation ?? "", + Phone = identityUser.Phone ?? "", + Email = identityUser.Email ?? "", + BirthDate = identityUser.BirthDate ?? "", + Country = identityUser.Country ?? "", + City = identityUser.City ?? "", + Address = identityUser.Address ?? "", + About = identityUser.About ?? "", + Description = identityUser.Description ?? "", + SocialNetworks = identityUser.SocialNetworks, UserTransactions = transactions, TotalBalance = transactions.Sum(x => x.Amount), - UserRoles = roles + UserRoles = roles, }; return user; diff --git a/src/Application/Users/Queries/GetCurrentUser/UserDto.cs b/src/Application/Users/Queries/GetCurrentUser/UserDto.cs index c7d121d..c0b34c1 100644 --- a/src/Application/Users/Queries/GetCurrentUser/UserDto.cs +++ b/src/Application/Users/Queries/GetCurrentUser/UserDto.cs @@ -1,3 +1,5 @@ +using Hutopy.Application.Users.Models; + namespace Hutopy.Application.Users.Queries.GetCurrentUser; public class UserDto @@ -6,6 +8,16 @@ public class UserDto public required string FirstName { get; init; } public required string LastName { get; init; } public string UserName { get; init; } = String.Empty; + public string Occupation { get; init; } = String.Empty; + public string Email { get; init; } = String.Empty; + public string Phone { get; init; } = String.Empty; + public string BirthDate { get; init; } = String.Empty; + public string Country { get; init; } = String.Empty; + public string City { get; init; } = String.Empty; + public string Address { get; init; } = String.Empty; + public string About { get; init; } = String.Empty; + public string Description { get; init; } = String.Empty; + public SocialNetworksModel SocialNetworks { get; init; } = new(); public List UserTransactions { get; init; } = []; public IList UserRoles { get; init; } = []; public required decimal TotalBalance { get; init; } diff --git a/src/Infrastructure/Identity/ApplicationUser.cs b/src/Infrastructure/Identity/ApplicationUser.cs index 700a074..d956a9b 100644 --- a/src/Infrastructure/Identity/ApplicationUser.cs +++ b/src/Infrastructure/Identity/ApplicationUser.cs @@ -7,5 +7,12 @@ public class ApplicationUser : IdentityUser { public string FirstName { get; set; } = string.Empty; public string LastName { get; set; } = string.Empty; + public string Occupation { get; set; } = string.Empty; + public string BirthDate { get; set; } = string.Empty; + public string Country { get; set; } = string.Empty; + public string City { get; set; } = string.Empty; + public string Address { get; set; } = string.Empty; + public string About { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; public SocialNetworks SocialNetworks { get; set; } = new(); } diff --git a/src/Infrastructure/Identity/IdentityService.cs b/src/Infrastructure/Identity/IdentityService.cs index 7cdf080..d512ae5 100644 --- a/src/Infrastructure/Identity/IdentityService.cs +++ b/src/Infrastructure/Identity/IdentityService.cs @@ -3,6 +3,8 @@ using Google.Apis.Oauth2.v2.Data; using System.Security.Claims; using Hutopy.Application.Common.Interfaces; using Hutopy.Application.Common.Models; +using Hutopy.Application.Users.Models; +using Hutopy.Infrastructure.Identity.OwnedEntities; using Hutopy.Infrastructure.Utils; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; @@ -45,23 +47,10 @@ public class IdentityService( return userModel; } - - public async Task<(Result Result, string UserId)> CreateUserAsync(string userName, string password) - { - var user = new ApplicationUser - { - UserName = userName, - Email = userName, - }; - - var result = await userManager.CreateAsync(user, password); - - return (result.ToApplicationResult(), user.Id); - } - public async Task<(Result Result, string UserId)> CreateUserAsync(Userinfo userInfo) + public async Task> CreateUserAsync(Userinfo userInfo) { - var user = new ApplicationUser + var applicationUser = new ApplicationUser { UserName = userInfo.Name, Email = userInfo.Email, @@ -71,12 +60,18 @@ public class IdentityService( var password = Guid.NewGuid().ToString("N")[..32]; - var result = await userManager.CreateAsync(user, password); + var identityResult = await userManager.CreateAsync(applicationUser, password); - return (result.ToApplicationResult(), user.Id); + var applicationResult = identityResult.ToApplicationResult(); + + var result = new Result(applicationResult.Succeeded, applicationResult.Errors); + + result.Value = applicationUser.Id; + + return result; } - public async Task CreateUserAsync(string email, string userName, string firstName, string lastName, string password) + public async Task> CreateUserAsync(string email, string userName, string firstName, string lastName, string password) { var applicationUser = new ApplicationUser { @@ -88,7 +83,52 @@ public class IdentityService( var response = await userManager.CreateAsync(applicationUser, password); - return response.ToApplicationResult(); + var result = new Result(response.Succeeded, response.ToApplicationResult().Errors); + result.Value = applicationUser.Id; + + return result; + } + + public async Task> UpdateCurrentUserAsync(string id, string firstName, string lastName, string occupation, + string phoneNumber, string birthDate, string country, string city, string address, string about, string description, + SocialNetworksModel socialNetworks) + { + var applicationUser = await userManager.FindByIdAsync(id); + + if (applicationUser is null) return Result.Failure(new[] { "User not found." }); + + applicationUser.FirstName = firstName; + applicationUser.LastName = lastName; + applicationUser.Occupation = occupation; + applicationUser.PhoneNumber = phoneNumber; + applicationUser.BirthDate = birthDate; + applicationUser.Country = country; + applicationUser.City = city; + applicationUser.Address = address; + applicationUser.About = about; + applicationUser.Description = description; + applicationUser.SocialNetworks = new SocialNetworks() + { + FacebookUrl = socialNetworks.FacebookUrl, + InstagramUrl = socialNetworks.InstagramUrl, + XUrl = socialNetworks.XUrl, + LinkedInUrl = socialNetworks.LinkedInUrl, + TikTokUrl = socialNetworks.TikTokUrl, + YoutubeUrl = socialNetworks.YoutubeUrl, + RedditUrl = socialNetworks.RedditUrl, + YourWebsiteUrl = socialNetworks.YourWebsiteUrl + }; + + var response = await userManager.UpdateAsync(applicationUser); + + var applicationResult = response.ToApplicationResult(); + + var result = new Result(applicationResult.Succeeded, + applicationResult.Errors); + + result.Value = id; + + return result; } public async Task FindUserByIdAsync(string id) @@ -97,13 +137,32 @@ public class IdentityService( if (response == null) return null; - var userModel = new UserModel() + var userModel = new UserModel { Id = response.Id, UserName = response.UserName, FirstName = response.FirstName, LastName = response.LastName, Email = response.Email, + Occupation = response.Occupation, + Phone = response.PhoneNumber, + BirthDate = response.BirthDate, + Country = response.Country, + City = response.City, + Address = response.Address, + About = response.About, + Description = response.Description, + SocialNetworks = new SocialNetworksModel + { + FacebookUrl = response.SocialNetworks.FacebookUrl, + InstagramUrl = response.SocialNetworks.InstagramUrl, + XUrl = response.SocialNetworks.XUrl, + LinkedInUrl = response.SocialNetworks.LinkedInUrl, + TikTokUrl = response.SocialNetworks.TikTokUrl, + YoutubeUrl = response.SocialNetworks.YoutubeUrl, + RedditUrl = response.SocialNetworks.RedditUrl, + YourWebsiteUrl = response.SocialNetworks.YourWebsiteUrl, + } }; return userModel; diff --git a/src/Infrastructure/Migrations/20240630001806_AddMissingInformationsToUser.Designer.cs b/src/Infrastructure/Migrations/20240630001806_AddMissingInformationsToUser.Designer.cs new file mode 100644 index 0000000..2a9623e --- /dev/null +++ b/src/Infrastructure/Migrations/20240630001806_AddMissingInformationsToUser.Designer.cs @@ -0,0 +1,497 @@ +// +using System; +using Hutopy.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Hutopy.Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20240630001806_AddMissingInformationsToUser")] + partial class AddMissingInformationsToUser + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Hutopy.Domain.Entities.FutureCreator", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("EmailAddress") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ReasonToJoin") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SocialNetworkAccount") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("FutureCreators"); + }); + + modelBuilder.Entity("Hutopy.Domain.Entities.UserTransaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("ApplicationUserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Currency") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsConfirmed") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Paid") + .HasColumnType("bit"); + + b.Property("StripeBillingDetailEmail") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripeBillingDetailName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripeChargeId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripeEventId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripePaymentIntent") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripePaymentMethod") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripeReceiptUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TipMessage") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationUserId"); + + b.ToTable("UserTransactions"); + }); + + modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("About") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("Address") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("BirthDate") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("City") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Country") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Occupation") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Hutopy.Domain.Entities.UserTransaction", b => + { + b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("ApplicationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationUser", b => + { + b.OwnsOne("Hutopy.Infrastructure.Identity.OwnedEntities.SocialNetworks", "SocialNetworks", b1 => + { + b1.Property("ApplicationUserId") + .HasColumnType("nvarchar(450)"); + + b1.Property("FacebookUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("InstagramUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("LinkedInUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("RedditUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("TikTokUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("XUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("YourWebsiteUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("YoutubeUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.HasKey("ApplicationUserId"); + + b1.ToTable("ApplicationUser_SocialNetworks", (string)null); + + b1.WithOwner() + .HasForeignKey("ApplicationUserId"); + }); + + b.Navigation("SocialNetworks") + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Infrastructure/Migrations/20240630001806_AddMissingInformationsToUser.cs b/src/Infrastructure/Migrations/20240630001806_AddMissingInformationsToUser.cs new file mode 100644 index 0000000..cd1a612 --- /dev/null +++ b/src/Infrastructure/Migrations/20240630001806_AddMissingInformationsToUser.cs @@ -0,0 +1,95 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Hutopy.Infrastructure.Migrations +{ + /// + public partial class AddMissingInformationsToUser : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "About", + table: "AspNetUsers", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "Address", + table: "AspNetUsers", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "BirthDate", + table: "AspNetUsers", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "City", + table: "AspNetUsers", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "Country", + table: "AspNetUsers", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "Description", + table: "AspNetUsers", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "Occupation", + table: "AspNetUsers", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "About", + table: "AspNetUsers"); + + migrationBuilder.DropColumn( + name: "Address", + table: "AspNetUsers"); + + migrationBuilder.DropColumn( + name: "BirthDate", + table: "AspNetUsers"); + + migrationBuilder.DropColumn( + name: "City", + table: "AspNetUsers"); + + migrationBuilder.DropColumn( + name: "Country", + table: "AspNetUsers"); + + migrationBuilder.DropColumn( + name: "Description", + table: "AspNetUsers"); + + migrationBuilder.DropColumn( + name: "Occupation", + table: "AspNetUsers"); + } + } +} diff --git a/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs index fcb6687..cd78f54 100644 --- a/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs @@ -149,13 +149,37 @@ namespace Hutopy.Infrastructure.Migrations b.Property("Id") .HasColumnType("nvarchar(450)"); + b.Property("About") + .IsRequired() + .HasColumnType("nvarchar(max)"); + b.Property("AccessFailedCount") .HasColumnType("int"); + b.Property("Address") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("BirthDate") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("City") + .IsRequired() + .HasColumnType("nvarchar(max)"); + b.Property("ConcurrencyStamp") .IsConcurrencyToken() .HasColumnType("nvarchar(max)"); + b.Property("Country") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + b.Property("Email") .HasMaxLength(256) .HasColumnType("nvarchar(256)"); @@ -185,6 +209,10 @@ namespace Hutopy.Infrastructure.Migrations .HasMaxLength(256) .HasColumnType("nvarchar(256)"); + b.Property("Occupation") + .IsRequired() + .HasColumnType("nvarchar(max)"); + b.Property("PasswordHash") .HasColumnType("nvarchar(max)"); diff --git a/src/Web/Endpoints/UpdateMyUser.cs b/src/Web/Endpoints/UpdateMyUser.cs new file mode 100644 index 0000000..60121be --- /dev/null +++ b/src/Web/Endpoints/UpdateMyUser.cs @@ -0,0 +1,20 @@ +using Hutopy.Application.Users.Commands; + +namespace Hutopy.Web.Endpoints; + +public class UpdateMyUser : EndpointGroupBase +{ + public override void Map(WebApplication app) + { + app.MapGroup(this) + .RequireAuthorization() + .MapPatch("/profile", UpdateCurrentUser); + } + + private static async Task UpdateCurrentUser(ISender sender, UpdateCurrentUserCommand command) + { + return await sender.Send(command); + } + + +}