diff --git a/src/Application/Common/Models/UserModel.cs b/src/Application/Common/Models/UserModel.cs index d7324a1..f67d8a9 100644 --- a/src/Application/Common/Models/UserModel.cs +++ b/src/Application/Common/Models/UserModel.cs @@ -7,6 +7,7 @@ public class UserModel public string Id { get; set; } = string.Empty; public string? CreatorAlias { get; set; } public string UserName { get; set; } = string.Empty; + public string? Alias { get; set; } public string FirstName { get; set; } = string.Empty; public string LastName { get; set; } = string.Empty; public string Occupation { get; set; } = string.Empty; diff --git a/src/Application/Users/Models/StoredDataUrlsModel.cs b/src/Application/Users/Models/StoredDataUrlsModel.cs index 0539508..06eb528 100644 --- a/src/Application/Users/Models/StoredDataUrlsModel.cs +++ b/src/Application/Users/Models/StoredDataUrlsModel.cs @@ -2,7 +2,7 @@ public class StoredDataUrlsModel { - public string BannerPictureUrl { get; set; } = string.Empty; - public string ProfilePictureUrl { get; set; } = string.Empty; - public string WebsiteIconUrl { get; set; } = string.Empty; + public string? BannerPictureUrl { get; set; } + public string? ProfilePictureUrl { get; set; } + public string? WebsiteIconUrl { get; set; } } diff --git a/src/Application/Users/Queries/GetCurrentUser/GetCurrentUser.cs b/src/Application/Users/Queries/GetCurrentUser/GetCurrentUser.cs index 6a90585..4429bed 100644 --- a/src/Application/Users/Queries/GetCurrentUser/GetCurrentUser.cs +++ b/src/Application/Users/Queries/GetCurrentUser/GetCurrentUser.cs @@ -29,6 +29,7 @@ public class GetCurrentUserQueryHandler( var user = new UserDto { Id = currentUserId, + Alias = identityUser.Alias, FirstName = identityUser.FirstName, LastName = identityUser.LastName, UserName = identityUser.UserName, diff --git a/src/Application/Users/Queries/GetCurrentUser/UserDto.cs b/src/Application/Users/Queries/GetCurrentUser/UserDto.cs index 4964a3b..3560de2 100644 --- a/src/Application/Users/Queries/GetCurrentUser/UserDto.cs +++ b/src/Application/Users/Queries/GetCurrentUser/UserDto.cs @@ -5,6 +5,7 @@ namespace Hutopy.Application.Users.Queries.GetCurrentUser; public class UserDto { public Guid Id { get; init; } + public string? Alias { get; init; } public required string FirstName { get; init; } public required string LastName { get; init; } public string? CreatorAlias { get; set; } diff --git a/src/Infrastructure/Identity/ApplicationUser.cs b/src/Infrastructure/Identity/ApplicationUser.cs index 74ad0cf..9b7ebdd 100644 --- a/src/Infrastructure/Identity/ApplicationUser.cs +++ b/src/Infrastructure/Identity/ApplicationUser.cs @@ -5,8 +5,9 @@ namespace Hutopy.Infrastructure.Identity; public class ApplicationUser : IdentityUser { - public string FirstName { get; set; } = string.Empty; - public string LastName { get; set; } = string.Empty; + public string? Alias { get; set; } + public required string FirstName { get; set; } + public required string LastName { get; set; } public string? CreatorAlias { get; set; } public string Occupation { get; set; } = string.Empty; public string BirthDate { get; set; } = string.Empty; diff --git a/src/Infrastructure/Identity/IdentityService.cs b/src/Infrastructure/Identity/IdentityService.cs index 747fff4..beaab6b 100644 --- a/src/Infrastructure/Identity/IdentityService.cs +++ b/src/Infrastructure/Identity/IdentityService.cs @@ -2,8 +2,6 @@ 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; @@ -31,53 +29,52 @@ public class IdentityService( public async Task GetUserByUserNameAsync(string userName) { - var response = await userManager.FindByNameAsync(userName); + var user = await userManager.FindByNameAsync(userName); - if (response == null) return null; + if (user == null) return null; - var userModel = new UserModel() + return new() { - Id = response.Id, - CreatorAlias = response.CreatorAlias, - UserName = response.UserName ?? string.Empty, - FirstName = response.FirstName, - LastName = response.LastName, - Email = response.Email ?? string.Empty, - Occupation = response.Occupation, - PhoneNumber = response.PhoneNumber ?? string.Empty, - BirthDate = response.BirthDate, - Country = response.Country, - City = response.City, - Address = response.Address, - About = response.About, - Description = response.Description, - SocialNetworks = new SocialNetworksModel + Id = user.Id, + CreatorAlias = user.CreatorAlias, + UserName = user.UserName ?? string.Empty, + Alias = user.Alias, + FirstName = user.FirstName, + LastName = user.LastName, + Email = user.Email ?? string.Empty, + Occupation = user.Occupation, + PhoneNumber = user.PhoneNumber ?? string.Empty, + BirthDate = user.BirthDate, + Country = user.Country, + City = user.City, + Address = user.Address, + About = user.About, + Description = user.Description, + SocialNetworks = new() { - 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, + FacebookUrl = user.SocialNetworks.FacebookUrl, + InstagramUrl = user.SocialNetworks.InstagramUrl, + XUrl = user.SocialNetworks.XUrl, + LinkedInUrl = user.SocialNetworks.LinkedInUrl, + TikTokUrl = user.SocialNetworks.TikTokUrl, + YoutubeUrl = user.SocialNetworks.YoutubeUrl, + RedditUrl = user.SocialNetworks.RedditUrl, + YourWebsiteUrl = user.SocialNetworks.YourWebsiteUrl, }, - ProfileColors = new ProfileColorsModel + ProfileColors = new() { - BannerTop = response.ProfileColors.BannerTop, - BannerBottom = response.ProfileColors.BannerBottom, - Accent = response.ProfileColors.Accent, - Menu = response.ProfileColors.Menu + BannerTop = user.ProfileColors.BannerTop, + BannerBottom = user.ProfileColors.BannerBottom, + Accent = user.ProfileColors.Accent, + Menu = user.ProfileColors.Menu }, - StoredDataUrls = new StoredDataUrlsModel + StoredDataUrls = new() { - ProfilePictureUrl = response.StoredDataUrls.ProfilePictureUrl, - BannerPictureUrl = response.StoredDataUrls.BannerPictureUrl, - WebsiteIconUrl = response.StoredDataUrls.WebsiteIconUrl, + ProfilePictureUrl = user.StoredDataUrls.ProfilePictureUrl, + BannerPictureUrl = user.StoredDataUrls.BannerPictureUrl, + WebsiteIconUrl = user.StoredDataUrls.WebsiteIconUrl, } }; - - return userModel; } public async Task> CreateUserAsync(Userinfo userInfo) @@ -134,7 +131,7 @@ public class IdentityService( applicationUser.Address = userModel.Address; applicationUser.About = userModel.About; applicationUser.Description = userModel.Description; - applicationUser.SocialNetworks = new SocialNetworks + applicationUser.SocialNetworks = new() { FacebookUrl = userModel.SocialNetworks.FacebookUrl, InstagramUrl = userModel.SocialNetworks.InstagramUrl, @@ -145,7 +142,7 @@ public class IdentityService( RedditUrl = userModel.SocialNetworks.RedditUrl, YourWebsiteUrl = userModel.SocialNetworks.YourWebsiteUrl }; - applicationUser.ProfileColors = new ProfileColors + applicationUser.ProfileColors = new() { BannerTop = userModel.ProfileColors.BannerTop, BannerBottom = userModel.ProfileColors.BannerBottom, @@ -192,6 +189,7 @@ public class IdentityService( Id = response.Id, CreatorAlias = response.CreatorAlias, UserName = response.UserName ?? string.Empty, + Alias = response.Alias, FirstName = response.FirstName, LastName = response.LastName, Email = response.Email ?? string.Empty, @@ -203,7 +201,7 @@ public class IdentityService( Address = response.Address, About = response.About, Description = response.Description, - SocialNetworks = new SocialNetworksModel + SocialNetworks = new() { FacebookUrl = response.SocialNetworks.FacebookUrl, InstagramUrl = response.SocialNetworks.InstagramUrl, @@ -214,14 +212,14 @@ public class IdentityService( RedditUrl = response.SocialNetworks.RedditUrl, YourWebsiteUrl = response.SocialNetworks.YourWebsiteUrl, }, - ProfileColors = new ProfileColorsModel + ProfileColors = new() { BannerTop = response.ProfileColors.BannerTop, BannerBottom = response.ProfileColors.BannerBottom, Accent = response.ProfileColors.Accent, Menu = response.ProfileColors.Menu }, - StoredDataUrls = new StoredDataUrlsModel + StoredDataUrls = new() { ProfilePictureUrl = response.StoredDataUrls.ProfilePictureUrl, BannerPictureUrl = response.StoredDataUrls.BannerPictureUrl, @@ -253,7 +251,7 @@ public class IdentityService( Address = response.Address, About = response.About, Description = response.Description, - SocialNetworks = new SocialNetworksModel + SocialNetworks = new() { FacebookUrl = response.SocialNetworks.FacebookUrl, InstagramUrl = response.SocialNetworks.InstagramUrl, @@ -264,14 +262,14 @@ public class IdentityService( RedditUrl = response.SocialNetworks.RedditUrl, YourWebsiteUrl = response.SocialNetworks.YourWebsiteUrl, }, - ProfileColors = new ProfileColorsModel + ProfileColors = new() { BannerTop = response.ProfileColors.BannerTop, BannerBottom = response.ProfileColors.BannerBottom, Accent = response.ProfileColors.Accent, Menu = response.ProfileColors.Menu }, - StoredDataUrls = new StoredDataUrlsModel + StoredDataUrls = new() { ProfilePictureUrl = response.StoredDataUrls.ProfilePictureUrl, BannerPictureUrl = response.StoredDataUrls.BannerPictureUrl, @@ -431,6 +429,7 @@ public class IdentityService( key: jwtOptions.Value.Key, userId: user.Id, email: user.Email, + alias: user.Alias, firstname: user.FirstName, lastname: user.LastName, profilePictureUrl: user.StoredDataUrls.ProfilePictureUrl); diff --git a/src/Infrastructure/Identity/OwnedEntities/StoredDataUrls.cs b/src/Infrastructure/Identity/OwnedEntities/StoredDataUrls.cs index bea6099..53d5346 100644 --- a/src/Infrastructure/Identity/OwnedEntities/StoredDataUrls.cs +++ b/src/Infrastructure/Identity/OwnedEntities/StoredDataUrls.cs @@ -1,8 +1,10 @@ -namespace Hutopy.Infrastructure.Identity.OwnedEntities; +using System.ComponentModel.DataAnnotations; + +namespace Hutopy.Infrastructure.Identity.OwnedEntities; public class StoredDataUrls { - public string BannerPictureUrl { get; set; } = string.Empty; - public string ProfilePictureUrl { get; set; } = string.Empty; - public string WebsiteIconUrl { get; set; } = string.Empty; + [MaxLength(255)] public string? BannerPictureUrl { get; set; } + [MaxLength(255)] public string? ProfilePictureUrl { get; set; } + [MaxLength(255)] public string? WebsiteIconUrl { get; set; } } diff --git a/src/Infrastructure/Migrations/20240721045147_AddAlias.Designer.cs b/src/Infrastructure/Migrations/20240721045147_AddAlias.Designer.cs new file mode 100644 index 0000000..397d04b --- /dev/null +++ b/src/Infrastructure/Migrations/20240721045147_AddAlias.Designer.cs @@ -0,0 +1,563 @@ +// +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("20240721045147_AddAlias")] + partial class AddAlias + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.4") + .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("Alias") + .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("CreatorAlias") + .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.ProfileColors", "ProfileColors", b1 => + { + b1.Property("ApplicationUserId") + .HasColumnType("nvarchar(450)"); + + b1.Property("Accent") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("BannerBottom") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("BannerTop") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("Menu") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.HasKey("ApplicationUserId"); + + b1.ToTable("ApplicationUser_ProfileColors", (string)null); + + b1.WithOwner() + .HasForeignKey("ApplicationUserId"); + }); + + 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.OwnsOne("Hutopy.Infrastructure.Identity.OwnedEntities.StoredDataUrls", "StoredDataUrls", b1 => + { + b1.Property("ApplicationUserId") + .HasColumnType("nvarchar(450)"); + + b1.Property("BannerPictureUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("ProfilePictureUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("WebsiteIconUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.HasKey("ApplicationUserId"); + + b1.ToTable("ApplicationUser_StoredDataUrls", (string)null); + + b1.WithOwner() + .HasForeignKey("ApplicationUserId"); + }); + + b.Navigation("ProfileColors") + .IsRequired(); + + b.Navigation("SocialNetworks") + .IsRequired(); + + b.Navigation("StoredDataUrls") + .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/20240721045147_AddAlias.cs b/src/Infrastructure/Migrations/20240721045147_AddAlias.cs new file mode 100644 index 0000000..9c8ffc9 --- /dev/null +++ b/src/Infrastructure/Migrations/20240721045147_AddAlias.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Hutopy.Infrastructure.Migrations +{ + /// + public partial class AddAlias : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Alias", + table: "AspNetUsers", + type: "nvarchar(max)", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Alias", + table: "AspNetUsers"); + } + } +} diff --git a/src/Infrastructure/Migrations/20240721063535_MadeOptional_StoredDataUrls.Designer.cs b/src/Infrastructure/Migrations/20240721063535_MadeOptional_StoredDataUrls.Designer.cs new file mode 100644 index 0000000..142d953 --- /dev/null +++ b/src/Infrastructure/Migrations/20240721063535_MadeOptional_StoredDataUrls.Designer.cs @@ -0,0 +1,563 @@ +// +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("20240721063535_MadeOptional_StoredDataUrls")] + partial class MadeOptional_StoredDataUrls + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.4") + .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("Alias") + .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("CreatorAlias") + .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.ProfileColors", "ProfileColors", b1 => + { + b1.Property("ApplicationUserId") + .HasColumnType("nvarchar(450)"); + + b1.Property("Accent") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("BannerBottom") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("BannerTop") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("Menu") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.HasKey("ApplicationUserId"); + + b1.ToTable("ApplicationUser_ProfileColors", (string)null); + + b1.WithOwner() + .HasForeignKey("ApplicationUserId"); + }); + + 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.OwnsOne("Hutopy.Infrastructure.Identity.OwnedEntities.StoredDataUrls", "StoredDataUrls", b1 => + { + b1.Property("ApplicationUserId") + .HasColumnType("nvarchar(450)"); + + b1.Property("BannerPictureUrl") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b1.Property("ProfilePictureUrl") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b1.Property("WebsiteIconUrl") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b1.HasKey("ApplicationUserId"); + + b1.ToTable("ApplicationUser_StoredDataUrls", (string)null); + + b1.WithOwner() + .HasForeignKey("ApplicationUserId"); + }); + + b.Navigation("ProfileColors") + .IsRequired(); + + b.Navigation("SocialNetworks") + .IsRequired(); + + b.Navigation("StoredDataUrls") + .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/20240721063535_MadeOptional_StoredDataUrls.cs b/src/Infrastructure/Migrations/20240721063535_MadeOptional_StoredDataUrls.cs new file mode 100644 index 0000000..b3085ae --- /dev/null +++ b/src/Infrastructure/Migrations/20240721063535_MadeOptional_StoredDataUrls.cs @@ -0,0 +1,78 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Hutopy.Infrastructure.Migrations +{ + /// + public partial class MadeOptional_StoredDataUrls : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "WebsiteIconUrl", + table: "ApplicationUser_StoredDataUrls", + type: "nvarchar(255)", + maxLength: 255, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "ProfilePictureUrl", + table: "ApplicationUser_StoredDataUrls", + type: "nvarchar(255)", + maxLength: 255, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "BannerPictureUrl", + table: "ApplicationUser_StoredDataUrls", + type: "nvarchar(255)", + maxLength: 255, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "WebsiteIconUrl", + table: "ApplicationUser_StoredDataUrls", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(255)", + oldMaxLength: 255, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ProfilePictureUrl", + table: "ApplicationUser_StoredDataUrls", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(255)", + oldMaxLength: 255, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "BannerPictureUrl", + table: "ApplicationUser_StoredDataUrls", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(255)", + oldMaxLength: 255, + oldNullable: true); + } + } +} diff --git a/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs index 3d3f44a..be95cf4 100644 --- a/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ namespace Hutopy.Infrastructure.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("ProductVersion", "8.0.4") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -160,6 +160,9 @@ namespace Hutopy.Infrastructure.Migrations .IsRequired() .HasColumnType("nvarchar(max)"); + b.Property("Alias") + .HasColumnType("nvarchar(max)"); + b.Property("BirthDate") .IsRequired() .HasColumnType("nvarchar(max)"); @@ -472,16 +475,16 @@ namespace Hutopy.Infrastructure.Migrations .HasColumnType("nvarchar(450)"); b1.Property("BannerPictureUrl") - .IsRequired() - .HasColumnType("nvarchar(max)"); + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); b1.Property("ProfilePictureUrl") - .IsRequired() - .HasColumnType("nvarchar(max)"); + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); b1.Property("WebsiteIconUrl") - .IsRequired() - .HasColumnType("nvarchar(max)"); + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); b1.HasKey("ApplicationUserId"); diff --git a/src/Infrastructure/Utils/GenerateJwtToken.cs b/src/Infrastructure/Utils/GenerateJwtToken.cs index a5613d6..7992e14 100644 --- a/src/Infrastructure/Utils/GenerateJwtToken.cs +++ b/src/Infrastructure/Utils/GenerateJwtToken.cs @@ -12,10 +12,11 @@ public static class JwtTokenHelper string issuer, string audience, string key, - string? userId, - string? email, - string? firstname, - string? lastname, + string userId, + string email, + string? alias, + string firstname, + string lastname, string? profilePictureUrl) { var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)); @@ -25,16 +26,19 @@ public static class JwtTokenHelper { new Claim(JwtRegisteredClaimNames.Sub, userId), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), - new Claim(ClaimTypes.NameIdentifier, userId), - new Claim(ClaimTypes.Email, email), - new Claim(ClaimTypes.Name, email), - new Claim(ClaimTypes.GivenName, firstname), - new Claim(ClaimTypes.Surname, lastname), + new Claim(ClaimTypes.NameIdentifier, userId), new Claim(ClaimTypes.Email, email), + new Claim(ClaimTypes.Name, email), new Claim(ClaimTypes.GivenName, firstname), + new Claim(ClaimTypes.Surname, lastname) }); + if (alias is not null) + { + claims.Add(new(KnownClaims.Alias, alias)); + } + if (profilePictureUrl is not null) { - claims.Add(new Claim("portrait-url", profilePictureUrl)); + claims.Add(new(KnownClaims.PortraitUrl, profilePictureUrl)); } var token = new JwtSecurityToken( diff --git a/src/Infrastructure/Utils/KnownClaims.cs b/src/Infrastructure/Utils/KnownClaims.cs new file mode 100644 index 0000000..96b1de9 --- /dev/null +++ b/src/Infrastructure/Utils/KnownClaims.cs @@ -0,0 +1,7 @@ +namespace Hutopy.Infrastructure.Utils; + +public static class KnownClaims +{ + public const string Alias = nameof(Alias); + public const string PortraitUrl = nameof(PortraitUrl); +} diff --git a/src/Web/Common/ClaimsPrincipalExtensions.cs b/src/Web/Common/ClaimsPrincipalExtensions.cs index 3f57da4..d633d6c 100644 --- a/src/Web/Common/ClaimsPrincipalExtensions.cs +++ b/src/Web/Common/ClaimsPrincipalExtensions.cs @@ -1,4 +1,5 @@ using System.Security.Claims; +using Hutopy.Infrastructure.Utils; namespace Hutopy.Web.Common; @@ -6,25 +7,48 @@ public static class ClaimsPrincipalExtensions { public static Guid GetUserId(this ClaimsPrincipal claims) { - return (Guid)claims.GetFirstValue(ClaimTypes.NameIdentifier); + return (Guid)claims.GetRequiredClaim(ClaimTypes.NameIdentifier); } - + + public static string GetName(this ClaimsPrincipal claims) + { + return (string)claims.GetRequiredClaim(ClaimTypes.Name); + } + + public static string? GetAlias(this ClaimsPrincipal claims) + { + return (string?)claims.GetClaim(KnownClaims.Alias); + } + + public static string? GetPortraitUrl(this ClaimsPrincipal claims) + { + return (string?)claims.GetClaim(KnownClaims.PortraitUrl); + } + public static string GetFirstName(this ClaimsPrincipal claims) { - return (string)claims.GetFirstValue(ClaimTypes.GivenName); + return (string)claims.GetRequiredClaim(ClaimTypes.GivenName); } public static string GetLastName(this ClaimsPrincipal claims) { - return (string)claims.GetFirstValue(ClaimTypes.Surname); + return (string)claims.GetRequiredClaim(ClaimTypes.Surname); } public static string GetEmail(this ClaimsPrincipal claims) { - return (string)claims.GetFirstValue(ClaimTypes.Email); + return (string)claims.GetRequiredClaim(ClaimTypes.Email); } - public static object GetFirstValue(this ClaimsPrincipal claims, string key) + public static object? GetClaim(this ClaimsPrincipal claims, string key) + { + var claim = claims.FindFirst(key); + + if (claim is null) return default; + return claims.GetRequiredClaim(key); + } + + public static object GetRequiredClaim(this ClaimsPrincipal claims, string key) { var claim = claims.FindFirst(key); diff --git a/src/Web/Controllers/GoogleController.cs b/src/Web/Controllers/GoogleController.cs index db86eb4..f45006a 100644 --- a/src/Web/Controllers/GoogleController.cs +++ b/src/Web/Controllers/GoogleController.cs @@ -74,6 +74,7 @@ public class GoogleController( jwtOptions.Value.Key, user.Id, user.Email, + user.Alias, user.FirstName, user.LastName, user.StoredDataUrls.ProfilePictureUrl); diff --git a/src/Web/Features/Messages/Data/Message.cs b/src/Web/Features/Messages/Data/Message.cs index 9368718..97dde47 100644 --- a/src/Web/Features/Messages/Data/Message.cs +++ b/src/Web/Features/Messages/Data/Message.cs @@ -1,11 +1,15 @@ -namespace Hutopy.Web.Features.Messages.Data; +using System.ComponentModel.DataAnnotations; + +namespace Hutopy.Web.Features.Messages.Data; public class Message { - public Guid Id { get; init; } - public Guid SubjectId { get; init; } - public Guid CreatedBy { get; init; } - public DateTimeOffset CreatedAt { get; init; } - public Guid? ParentId { get; init; } - public string Value { get; init; } + public Guid Id { get; set; } + public Guid SubjectId { get; set; } + public Guid CreatedBy { get; set; } + [MaxLength(64)] public required string CreatedByName { get; set; } + [MaxLength(256)] public string? CreatedByPortraitUrl { get; set; } + public DateTimeOffset CreatedAt { get; set; } + public Guid? ParentId { get; set; } + public required string Value { get; set; } } diff --git a/src/Web/Features/Messages/Data/MessagingDbContext.cs b/src/Web/Features/Messages/Data/MessagingDbContext.cs index 16fc14a..8cf5bf2 100644 --- a/src/Web/Features/Messages/Data/MessagingDbContext.cs +++ b/src/Web/Features/Messages/Data/MessagingDbContext.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +using Hutopy.Web.Features.Messages.Handlers.Models; +using Microsoft.EntityFrameworkCore; namespace Hutopy.Web.Features.Messages.Data; @@ -9,7 +10,7 @@ public class MessagingDbContext( protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasDefaultSchema("Messaging"); - + modelBuilder .Entity() .Property(c => c.CreatedAt) @@ -17,5 +18,41 @@ public class MessagingDbContext( .HasDefaultValueSql("CURRENT_TIMESTAMP"); } - public DbSet Messages { get; set; } + public DbSet Messages { get; set; } + + public async Task> GetMessagesAsync( + Guid subjectId, + Guid? parentId, + Guid? lastId, + int pageSize, + CancellationToken ct = default) + { + var query = Messages + .Where(c => c.SubjectId == subjectId) + .Where(c => c.ParentId == parentId); + + if (lastId.HasValue) + { + var lastMessage = await Messages + .Where(c => c.Id == lastId.Value) + .Select(c => new { c.CreatedAt, c.Id }) + .FirstOrDefaultAsync(cancellationToken: ct); + + if (lastMessage != null) + { + query = query + .Where(c => c.CreatedAt < lastMessage.CreatedAt + || (c.CreatedAt == lastMessage.CreatedAt && c.Id < lastMessage.Id)); + } + } + + var messages = await query + .OrderByDescending(c => c.CreatedAt) + .ThenByDescending(c => c.Id) + .Take(pageSize) + .Select(message => message.ToDto()) + .ToListAsync(cancellationToken: ct); + + return messages; + } } diff --git a/src/Web/Features/Messages/Handlers/AddMessage.cs b/src/Web/Features/Messages/Handlers/AddMessage.cs index fcb6df3..dc7c9e2 100644 --- a/src/Web/Features/Messages/Handlers/AddMessage.cs +++ b/src/Web/Features/Messages/Handlers/AddMessage.cs @@ -1,14 +1,30 @@ using FastEndpoints; +using FluentValidation; using Hutopy.Web.Common; using Hutopy.Web.Features.Messages.Data; namespace Hutopy.Web.Features.Messages.Handlers; -public class AddMessageRequest +public sealed class AddMessageRequest { public Guid? Id { get; set; } - public Guid SubjectId { get; set; } - public string Message { get; set; } + public required Guid SubjectId { get; set; } + public required string Message { get; set; } +} + +internal sealed class AddMessageRequestValidator + : Validator +{ + public AddMessageRequestValidator() + { + RuleFor(r => r.SubjectId) + .NotNull().WithMessage("You must specify a SubjectId") + .NotEmpty().WithMessage("You must specify a non-empty SubjectId"); + + RuleFor(r => r.Message) + .NotNull().WithMessage("You must specify a Message") + .NotEmpty().WithMessage("You must specify a non-empty Message"); + } } public class AddMessage( @@ -30,6 +46,8 @@ public class AddMessage( Id = req.Id ?? GuidHelper.GenerateUuidV7(), SubjectId = req.SubjectId, CreatedBy = User.GetUserId(), + CreatedByName = User.GetAlias() ?? $"{User.GetFirstName()} {User.GetLastName()}", + CreatedByPortraitUrl = User.GetPortraitUrl(), Value = req.Message }; diff --git a/src/Web/Features/Messages/Handlers/AddReply.cs b/src/Web/Features/Messages/Handlers/AddReply.cs index 1ab824d..8b2ea91 100644 --- a/src/Web/Features/Messages/Handlers/AddReply.cs +++ b/src/Web/Features/Messages/Handlers/AddReply.cs @@ -1,16 +1,37 @@ using FastEndpoints; +using FluentValidation; using Hutopy.Web.Common; using Hutopy.Web.Features.Messages.Data; namespace Hutopy.Web.Features.Messages.Handlers; -internal sealed class AddReplyRequest +public sealed class AddReplyRequest { - public required Guid SubjectId { get; set; } + public Guid? Id { get; set; } public required Guid ParentId { get; set; } + public required Guid SubjectId { get; set; } public required string Message { get; set; } } +internal sealed class AddReplyRequestValidator + : Validator +{ + public AddReplyRequestValidator() + { + RuleFor(r => r.ParentId) + .NotNull().WithMessage("You must specify a ParentId") + .NotEmpty().WithMessage("You must specify a non-empty ParentId"); + + RuleFor(r => r.SubjectId) + .NotNull().WithMessage("You must specify a SubjectId") + .NotEmpty().WithMessage("You must specify a non-empty SubjectId"); + + RuleFor(r => r.Message) + .NotNull().WithMessage("You must specify a Message") + .NotEmpty().WithMessage("You must specify a non-empty Message"); + } +} + internal sealed class AddReply( MessagingDbContext context) : Endpoint @@ -28,9 +49,10 @@ internal sealed class AddReply( var message = new Message { Id = GuidHelper.GenerateUuidV7(), - SubjectId = req.SubjectId, + SubjectId = req.SubjectId, ParentId = req.ParentId, CreatedBy = User.GetUserId(), + CreatedByName = User.GetName(), Value = req.Message }; diff --git a/src/Web/Features/Messages/Handlers/GetMessages.cs b/src/Web/Features/Messages/Handlers/GetMessages.cs index 242558f..de98a5e 100644 --- a/src/Web/Features/Messages/Handlers/GetMessages.cs +++ b/src/Web/Features/Messages/Handlers/GetMessages.cs @@ -1,19 +1,24 @@ using FastEndpoints; using Hutopy.Web.Features.Messages.Data; -using Microsoft.EntityFrameworkCore; +using Hutopy.Web.Features.Messages.Handlers.Models; namespace Hutopy.Web.Features.Messages.Handlers; -public class GetMessagesRequest +public sealed class GetMessagesRequest { public Guid SubjectId { get; set; } [BindFrom("page_size")] public int PageSize { get; set; } = 10; [BindFrom("last_id")] public Guid? LastId { get; set; } } +public record struct GetMessagesResponse +{ + public required List Messages { get; init; } +} + public class GetMessages( MessagingDbContext context) - : Endpoint> + : Endpoint { public override void Configure() { @@ -26,34 +31,18 @@ public class GetMessages( GetMessagesRequest req, CancellationToken ct) { - var query = context - .Messages - .Where(c => c.SubjectId == req.SubjectId) - .Where(c => c.ParentId == null); - - if (req.LastId.HasValue) - { - var lastMessage = await context - .Messages - .Where(c => c.Id == req.LastId.Value) - .Select(c => new { c.CreatedAt, c.Id }) - .FirstOrDefaultAsync(cancellationToken: ct); - - if (lastMessage != null) + var messages = await context.GetMessagesAsync( + req.SubjectId, + null, + req.LastId, + req.PageSize, + ct); + + await SendAsync( + new() { - query = query - .Where(c => c.CreatedAt < lastMessage.CreatedAt - || (c.CreatedAt == lastMessage.CreatedAt && c.Id < lastMessage.Id)); - } - } - - query = query - .OrderByDescending(c => c.CreatedAt) - .ThenByDescending(c => c.Id) - .Take(req.PageSize); - - var messages = await query.ToListAsync(cancellationToken: ct); - - await SendAsync(messages, cancellation: ct); + Messages = messages + }, + cancellation: ct); } } diff --git a/src/Web/Features/Messages/Handlers/GetMessagesByUser.cs b/src/Web/Features/Messages/Handlers/GetMessagesByUser.cs index 0b39cef..db611a0 100644 --- a/src/Web/Features/Messages/Handlers/GetMessagesByUser.cs +++ b/src/Web/Features/Messages/Handlers/GetMessagesByUser.cs @@ -1,5 +1,6 @@ using FastEndpoints; using Hutopy.Web.Features.Messages.Data; +using Hutopy.Web.Features.Messages.Handlers.Models; using Microsoft.EntityFrameworkCore; namespace Hutopy.Web.Features.Messages.Handlers; @@ -9,9 +10,14 @@ public class GetMessagesByUserRequest public Guid UserId { get; set; } } +public record struct GetMessagesByUserResponse +{ + public required List Messages { get; init; } +} + public class GetMessagesByUser( MessagingDbContext context) - : Endpoint> + : Endpoint { public override void Configure() { @@ -24,12 +30,19 @@ public class GetMessagesByUser( GetMessagesByUserRequest req, CancellationToken ct) { - var posts = await context + var messages = await context .Messages .Where(c => c.CreatedBy == req.UserId) .Where(c => c.ParentId == null) .ToListAsync(cancellationToken: ct); - await SendAsync(posts, cancellation: ct); + await SendAsync( + new() + { + Messages = messages + .Select(message => message.ToDto()) + .ToList() + }, + cancellation: ct); } } diff --git a/src/Web/Features/Messages/Handlers/GetReplies.cs b/src/Web/Features/Messages/Handlers/GetReplies.cs index 891dbb3..9c39fef 100644 --- a/src/Web/Features/Messages/Handlers/GetReplies.cs +++ b/src/Web/Features/Messages/Handlers/GetReplies.cs @@ -1,6 +1,6 @@ using FastEndpoints; using Hutopy.Web.Features.Messages.Data; -using Microsoft.EntityFrameworkCore; +using Hutopy.Web.Features.Messages.Handlers.Models; namespace Hutopy.Web.Features.Messages.Handlers; @@ -12,9 +12,14 @@ public class GetRepliesRequest [BindFrom("last_id")] public Guid? LastId { get; set; } } +public record struct GetRepliesResponse +{ + public required List Messages { get; init; } +} + public class GetReplies( MessagingDbContext context) - : Endpoint> + : Endpoint { public override void Configure() { @@ -27,20 +32,18 @@ public class GetReplies( GetRepliesRequest req, CancellationToken ct) { - var query = context - .Messages - .Where(c => c.SubjectId == req.SubjectId) - .Where(c => c.ParentId == req.ParentId); + var replies = await context.GetMessagesAsync( + req.SubjectId, + req.ParentId, + req.LastId, + req.PageSize, + ct); - query = query.OrderByDescending(c => c.CreatedAt); - - if (req.LastId is not null) - query = query.Where(c => c.Id < req.LastId.Value); - - query = query.Take(req.PageSize); - - var replies = await query.ToListAsync(cancellationToken: ct); - - await SendAsync(replies, cancellation: ct); + await SendAsync( + new() + { + Messages = replies, + }, + cancellation: ct); } } diff --git a/src/Web/Features/Messages/Handlers/Models/MessageDto.cs b/src/Web/Features/Messages/Handlers/Models/MessageDto.cs new file mode 100644 index 0000000..16b9e78 --- /dev/null +++ b/src/Web/Features/Messages/Handlers/Models/MessageDto.cs @@ -0,0 +1,30 @@ +using Hutopy.Web.Features.Messages.Data; + +namespace Hutopy.Web.Features.Messages.Handlers.Models; + +public record struct MessageDto( + Guid Id, + Guid SubjectId, + Guid CreatedBy, + string CreatedByName, + string? CreatedByPortraitUrl, + DateTimeOffset CreatedAt, + Guid? ParentId, + string Value +); + +public static class MessageExtensions +{ + public static MessageDto ToDto(this Message message) => + new() + { + Id = message.Id, + ParentId = message.ParentId, + CreatedAt = message.CreatedAt, + CreatedBy = message.CreatedBy, + CreatedByName = message.CreatedByName, + CreatedByPortraitUrl = message.CreatedByPortraitUrl, + SubjectId = message.SubjectId, + Value = message.Value + }; +} diff --git a/src/Web/Features/Messages/Migrations/20240718173016_Initial.Designer.cs b/src/Web/Features/Messages/Migrations/20240721041322_Initial.Designer.cs similarity index 81% rename from src/Web/Features/Messages/Migrations/20240718173016_Initial.Designer.cs rename to src/Web/Features/Messages/Migrations/20240721041322_Initial.Designer.cs index 21a45b2..b864487 100644 --- a/src/Web/Features/Messages/Migrations/20240718173016_Initial.Designer.cs +++ b/src/Web/Features/Messages/Migrations/20240721041322_Initial.Designer.cs @@ -9,10 +9,10 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable -namespace Hutopy.Web.Messages.Migrations +namespace Hutopy.Web.Features.Messages.Migrations { [DbContext(typeof(MessagingDbContext))] - [Migration("20240718173016_Initial")] + [Migration("20240721041322_Initial")] partial class Initial { /// @@ -26,7 +26,7 @@ namespace Hutopy.Web.Messages.Migrations NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - modelBuilder.Entity("Hutopy.Web.Messages.Data.Message", b => + modelBuilder.Entity("Hutopy.Web.Features.Messages.Data.Message", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -40,6 +40,13 @@ namespace Hutopy.Web.Messages.Migrations b.Property("CreatedBy") .HasColumnType("uuid"); + b.Property("CreatedByName") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedByPortraitUrl") + .HasColumnType("text"); + b.Property("ParentId") .HasColumnType("uuid"); diff --git a/src/Web/Features/Messages/Migrations/20240718173016_Initial.cs b/src/Web/Features/Messages/Migrations/20240721041322_Initial.cs similarity index 86% rename from src/Web/Features/Messages/Migrations/20240718173016_Initial.cs rename to src/Web/Features/Messages/Migrations/20240721041322_Initial.cs index 6a194da..7287c65 100644 --- a/src/Web/Features/Messages/Migrations/20240718173016_Initial.cs +++ b/src/Web/Features/Messages/Migrations/20240721041322_Initial.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Migrations; #nullable disable -namespace Hutopy.Web.Messages.Migrations +namespace Hutopy.Web.Features.Messages.Migrations { /// public partial class Initial : Migration @@ -22,6 +22,8 @@ namespace Hutopy.Web.Messages.Migrations Id = table.Column(type: "uuid", nullable: false), SubjectId = table.Column(type: "uuid", nullable: false), CreatedBy = table.Column(type: "uuid", nullable: false), + CreatedByName = table.Column(type: "text", nullable: false), + CreatedByPortraitUrl = table.Column(type: "text", nullable: true), CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP"), ParentId = table.Column(type: "uuid", nullable: true), Value = table.Column(type: "text", nullable: false) diff --git a/src/Web/Features/Messages/Migrations/20240721064224_ChangedAuthorDefinition.Designer.cs b/src/Web/Features/Messages/Migrations/20240721064224_ChangedAuthorDefinition.Designer.cs new file mode 100644 index 0000000..1a92049 --- /dev/null +++ b/src/Web/Features/Messages/Migrations/20240721064224_ChangedAuthorDefinition.Designer.cs @@ -0,0 +1,69 @@ +// +using System; +using Hutopy.Web.Features.Messages.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Hutopy.Web.Features.Messages.Migrations +{ + [DbContext(typeof(MessagingDbContext))] + [Migration("20240721064224_ChangedAuthorDefinition")] + partial class ChangedAuthorDefinition + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("Messaging") + .HasAnnotation("ProductVersion", "8.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Hutopy.Web.Features.Messages.Data.Message", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("CreatedByName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CreatedByPortraitUrl") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("SubjectId") + .HasColumnType("uuid"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Messages", "Messaging"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Web/Features/Messages/Migrations/20240721064224_ChangedAuthorDefinition.cs b/src/Web/Features/Messages/Migrations/20240721064224_ChangedAuthorDefinition.cs new file mode 100644 index 0000000..bf04dbe --- /dev/null +++ b/src/Web/Features/Messages/Migrations/20240721064224_ChangedAuthorDefinition.cs @@ -0,0 +1,60 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Hutopy.Web.Features.Messages.Migrations +{ + /// + public partial class ChangedAuthorDefinition : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "CreatedByPortraitUrl", + schema: "Messaging", + table: "Messages", + type: "character varying(256)", + maxLength: 256, + nullable: true, + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreatedByName", + schema: "Messaging", + table: "Messages", + type: "character varying(64)", + maxLength: 64, + nullable: false, + oldClrType: typeof(string), + oldType: "text"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "CreatedByPortraitUrl", + schema: "Messaging", + table: "Messages", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "character varying(256)", + oldMaxLength: 256, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreatedByName", + schema: "Messaging", + table: "Messages", + type: "text", + nullable: false, + oldClrType: typeof(string), + oldType: "character varying(64)", + oldMaxLength: 64); + } + } +} diff --git a/src/Web/Features/Messages/Migrations/MessagingDbContextModelSnapshot.cs b/src/Web/Features/Messages/Migrations/MessagingDbContextModelSnapshot.cs index c01bd22..111cb88 100644 --- a/src/Web/Features/Messages/Migrations/MessagingDbContextModelSnapshot.cs +++ b/src/Web/Features/Messages/Migrations/MessagingDbContextModelSnapshot.cs @@ -8,7 +8,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable -namespace Hutopy.Web.Messages.Migrations +namespace Hutopy.Web.Features.Messages.Migrations { [DbContext(typeof(MessagingDbContext))] partial class MessagingDbContextModelSnapshot : ModelSnapshot @@ -23,7 +23,7 @@ namespace Hutopy.Web.Messages.Migrations NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - modelBuilder.Entity("Hutopy.Web.Messages.Data.Message", b => + modelBuilder.Entity("Hutopy.Web.Features.Messages.Data.Message", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -37,6 +37,15 @@ namespace Hutopy.Web.Messages.Migrations b.Property("CreatedBy") .HasColumnType("uuid"); + b.Property("CreatedByName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CreatedByPortraitUrl") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + b.Property("ParentId") .HasColumnType("uuid"); diff --git a/src/Web/TestDataSeeder.cs b/src/Web/TestDataSeeder.cs index c357ade..cb713f9 100644 --- a/src/Web/TestDataSeeder.cs +++ b/src/Web/TestDataSeeder.cs @@ -35,13 +35,13 @@ internal class TestDataSeeder( { if (contentContext.Contents.Any()) return; - _ = await CreateAdministratorAsync("admin"); - _ = await CreateUserAsync("userA"); - _ = await CreateUserAsync("userB"); + _users.Add(await CreateUserAsync("admin", Roles.Administrator)); + _users.Add(await CreateUserAsync("userA")); + _users.Add(await CreateUserAsync("userB")); foreach (var creator in _creators) { - _ = await CreateCreatorAsync(creator); + _users.Add(await CreateCreatorAsync(creator)); var contents = GenerateContent(creator, 100); foreach (var content in contents) @@ -96,15 +96,17 @@ internal class TestDataSeeder( for (var m = messageCount; m > 0; m--) { currentDate = currentDate.AddSeconds(-Random.Shared.Next(100, 100_000)); - var author = Random.Shared.GetItems(_creators, 1)[0]; + var author = Random.Shared.GetItems(_users.ToArray(), 1).First(); var message = new Message { Id = GuidHelper.GenerateUuidV7(), SubjectId = content.Id, - CreatedBy = Guid.Parse(author.Id), CreatedAt = currentDate, - Value = $"Message #{m} from {author.UserName} on {content.Title}" + CreatedBy = Guid.Parse(author.Id), + CreatedByName = author.Alias ?? $"{author.FirstName} {author.LastName}", + CreatedByPortraitUrl = author.StoredDataUrls.ProfilePictureUrl, + Value = $"Message #{m} on {content.Title}" }; messagingContext.Messages.Add(message); @@ -124,7 +126,7 @@ internal class TestDataSeeder( { currentDate = currentDate.AddSeconds(-Random.Shared.Next(100, 100_000)); - var author = Random.Shared.GetItems(_creators, 1)[0]; + var author = Random.Shared.GetItems(_users.ToArray(), 1).First(); var message = new Message { @@ -132,8 +134,10 @@ internal class TestDataSeeder( SubjectId = content.Id, ParentId = parent.Id, CreatedBy = Guid.Parse(author.Id), + CreatedByName = author.Alias ?? $"{author.FirstName} {author.LastName}", + CreatedByPortraitUrl = author.StoredDataUrls.ProfilePictureUrl, CreatedAt = currentDate, - Value = $"Reply {r} to {parent.Value}" + Value = $"Reply {r} to {parent.Value} on {content.Title}" }; messagingContext.Messages.Add(message); @@ -144,31 +148,22 @@ internal class TestDataSeeder( return replies; } - private async Task CreateAdministratorAsync(string name) - { - ArgumentException.ThrowIfNullOrWhiteSpace(name); - - var administrator = new ApplicationUser { UserName = $"{name}@test", Email = $"{name}@test" }; - - await userManager.CreateAsync(administrator, DefaultPassword); - await userManager.AddToRolesAsync(administrator, new[] { Roles.Administrator }); - - return administrator; - } - - private async Task CreateUserAsync(string name) + private async Task CreateUserAsync(string name, params string[] roles) { var user = new ApplicationUser { UserName = $"{name}@test", Email = $"{name}@test", EmailConfirmed = true, + Alias = name, FirstName = $"FirstName of {name}", LastName = $"LastName of {name}" }; await userManager.CreateAsync(user, DefaultPassword); + if (roles.Length > 0) await userManager.AddToRolesAsync(user, roles); + return user; } @@ -180,12 +175,19 @@ internal class TestDataSeeder( } + private readonly List _users = + [ + ]; + + private readonly static ApplicationUser Hutopy = new() { UserName = "hutopy@test", Email = "hutopy@test", EmailConfirmed = true, CreatorAlias = "hutopy", + FirstName = "FirstName of a Brand/Creator", + LastName = "LastName of a Brand/Creator", About = "Page officielle", Description = "Site officiel pour Hutopy. Venez-nous-y retrouver avec tous vos fans!", ProfileColors = new ProfileColors diff --git a/tests/Application.FunctionalTests/Testing.cs b/tests/Application.FunctionalTests/Testing.cs index 8270718..8c31c56 100644 --- a/tests/Application.FunctionalTests/Testing.cs +++ b/tests/Application.FunctionalTests/Testing.cs @@ -65,7 +65,12 @@ public partial class Testing var userManager = scope.ServiceProvider.GetRequiredService(); - var user = new ApplicationUser { UserName = userName, Email = userName }; + var user = new ApplicationUser { + UserName = userName, + Email = userName, + FirstName = "FirstName", + LastName = "LastName" + }; var result = await userManager.CreateAsync(user, password);