Adds user Alias. Make StoredDataUrls optionals.

This commit is contained in:
Jonathan Bourdon
2024-07-22 00:42:27 -04:00
parent 8551398edc
commit 0faf5a9a0e
31 changed files with 1720 additions and 174 deletions

View File

@@ -7,6 +7,7 @@ public class UserModel
public string Id { get; set; } = string.Empty; public string Id { get; set; } = string.Empty;
public string? CreatorAlias { get; set; } public string? CreatorAlias { get; set; }
public string UserName { get; set; } = string.Empty; public string UserName { get; set; } = string.Empty;
public string? Alias { get; set; }
public string FirstName { get; set; } = string.Empty; public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty; public string LastName { get; set; } = string.Empty;
public string Occupation { get; set; } = string.Empty; public string Occupation { get; set; } = string.Empty;

View File

@@ -2,7 +2,7 @@
public class StoredDataUrlsModel public class StoredDataUrlsModel
{ {
public string BannerPictureUrl { get; set; } = string.Empty; public string? BannerPictureUrl { get; set; }
public string ProfilePictureUrl { get; set; } = string.Empty; public string? ProfilePictureUrl { get; set; }
public string WebsiteIconUrl { get; set; } = string.Empty; public string? WebsiteIconUrl { get; set; }
} }

View File

@@ -29,6 +29,7 @@ public class GetCurrentUserQueryHandler(
var user = new UserDto var user = new UserDto
{ {
Id = currentUserId, Id = currentUserId,
Alias = identityUser.Alias,
FirstName = identityUser.FirstName, FirstName = identityUser.FirstName,
LastName = identityUser.LastName, LastName = identityUser.LastName,
UserName = identityUser.UserName, UserName = identityUser.UserName,

View File

@@ -5,6 +5,7 @@ namespace Hutopy.Application.Users.Queries.GetCurrentUser;
public class UserDto public class UserDto
{ {
public Guid Id { get; init; } public Guid Id { get; init; }
public string? Alias { get; init; }
public required string FirstName { get; init; } public required string FirstName { get; init; }
public required string LastName { get; init; } public required string LastName { get; init; }
public string? CreatorAlias { get; set; } public string? CreatorAlias { get; set; }

View File

@@ -5,8 +5,9 @@ namespace Hutopy.Infrastructure.Identity;
public class ApplicationUser : IdentityUser public class ApplicationUser : IdentityUser
{ {
public string FirstName { get; set; } = string.Empty; public string? Alias { get; set; }
public string LastName { get; set; } = string.Empty; public required string FirstName { get; set; }
public required string LastName { get; set; }
public string? CreatorAlias { get; set; } public string? CreatorAlias { get; set; }
public string Occupation { get; set; } = string.Empty; public string Occupation { get; set; } = string.Empty;
public string BirthDate { get; set; } = string.Empty; public string BirthDate { get; set; } = string.Empty;

View File

@@ -2,8 +2,6 @@ using Google.Apis.Oauth2.v2.Data;
using System.Security.Claims; using System.Security.Claims;
using Hutopy.Application.Common.Interfaces; using Hutopy.Application.Common.Interfaces;
using Hutopy.Application.Common.Models; using Hutopy.Application.Common.Models;
using Hutopy.Application.Users.Models;
using Hutopy.Infrastructure.Identity.OwnedEntities;
using Hutopy.Infrastructure.Utils; using Hutopy.Infrastructure.Utils;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@@ -31,53 +29,52 @@ public class IdentityService(
public async Task<UserModel?> GetUserByUserNameAsync(string userName) public async Task<UserModel?> 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, Id = user.Id,
CreatorAlias = response.CreatorAlias, CreatorAlias = user.CreatorAlias,
UserName = response.UserName ?? string.Empty, UserName = user.UserName ?? string.Empty,
FirstName = response.FirstName, Alias = user.Alias,
LastName = response.LastName, FirstName = user.FirstName,
Email = response.Email ?? string.Empty, LastName = user.LastName,
Occupation = response.Occupation, Email = user.Email ?? string.Empty,
PhoneNumber = response.PhoneNumber ?? string.Empty, Occupation = user.Occupation,
BirthDate = response.BirthDate, PhoneNumber = user.PhoneNumber ?? string.Empty,
Country = response.Country, BirthDate = user.BirthDate,
City = response.City, Country = user.Country,
Address = response.Address, City = user.City,
About = response.About, Address = user.Address,
Description = response.Description, About = user.About,
SocialNetworks = new SocialNetworksModel Description = user.Description,
SocialNetworks = new()
{ {
FacebookUrl = response.SocialNetworks.FacebookUrl, FacebookUrl = user.SocialNetworks.FacebookUrl,
InstagramUrl = response.SocialNetworks.InstagramUrl, InstagramUrl = user.SocialNetworks.InstagramUrl,
XUrl = response.SocialNetworks.XUrl, XUrl = user.SocialNetworks.XUrl,
LinkedInUrl = response.SocialNetworks.LinkedInUrl, LinkedInUrl = user.SocialNetworks.LinkedInUrl,
TikTokUrl = response.SocialNetworks.TikTokUrl, TikTokUrl = user.SocialNetworks.TikTokUrl,
YoutubeUrl = response.SocialNetworks.YoutubeUrl, YoutubeUrl = user.SocialNetworks.YoutubeUrl,
RedditUrl = response.SocialNetworks.RedditUrl, RedditUrl = user.SocialNetworks.RedditUrl,
YourWebsiteUrl = response.SocialNetworks.YourWebsiteUrl, YourWebsiteUrl = user.SocialNetworks.YourWebsiteUrl,
}, },
ProfileColors = new ProfileColorsModel ProfileColors = new()
{ {
BannerTop = response.ProfileColors.BannerTop, BannerTop = user.ProfileColors.BannerTop,
BannerBottom = response.ProfileColors.BannerBottom, BannerBottom = user.ProfileColors.BannerBottom,
Accent = response.ProfileColors.Accent, Accent = user.ProfileColors.Accent,
Menu = response.ProfileColors.Menu Menu = user.ProfileColors.Menu
}, },
StoredDataUrls = new StoredDataUrlsModel StoredDataUrls = new()
{ {
ProfilePictureUrl = response.StoredDataUrls.ProfilePictureUrl, ProfilePictureUrl = user.StoredDataUrls.ProfilePictureUrl,
BannerPictureUrl = response.StoredDataUrls.BannerPictureUrl, BannerPictureUrl = user.StoredDataUrls.BannerPictureUrl,
WebsiteIconUrl = response.StoredDataUrls.WebsiteIconUrl, WebsiteIconUrl = user.StoredDataUrls.WebsiteIconUrl,
} }
}; };
return userModel;
} }
public async Task<Result<string>> CreateUserAsync(Userinfo userInfo) public async Task<Result<string>> CreateUserAsync(Userinfo userInfo)
@@ -134,7 +131,7 @@ public class IdentityService(
applicationUser.Address = userModel.Address; applicationUser.Address = userModel.Address;
applicationUser.About = userModel.About; applicationUser.About = userModel.About;
applicationUser.Description = userModel.Description; applicationUser.Description = userModel.Description;
applicationUser.SocialNetworks = new SocialNetworks applicationUser.SocialNetworks = new()
{ {
FacebookUrl = userModel.SocialNetworks.FacebookUrl, FacebookUrl = userModel.SocialNetworks.FacebookUrl,
InstagramUrl = userModel.SocialNetworks.InstagramUrl, InstagramUrl = userModel.SocialNetworks.InstagramUrl,
@@ -145,7 +142,7 @@ public class IdentityService(
RedditUrl = userModel.SocialNetworks.RedditUrl, RedditUrl = userModel.SocialNetworks.RedditUrl,
YourWebsiteUrl = userModel.SocialNetworks.YourWebsiteUrl YourWebsiteUrl = userModel.SocialNetworks.YourWebsiteUrl
}; };
applicationUser.ProfileColors = new ProfileColors applicationUser.ProfileColors = new()
{ {
BannerTop = userModel.ProfileColors.BannerTop, BannerTop = userModel.ProfileColors.BannerTop,
BannerBottom = userModel.ProfileColors.BannerBottom, BannerBottom = userModel.ProfileColors.BannerBottom,
@@ -192,6 +189,7 @@ public class IdentityService(
Id = response.Id, Id = response.Id,
CreatorAlias = response.CreatorAlias, CreatorAlias = response.CreatorAlias,
UserName = response.UserName ?? string.Empty, UserName = response.UserName ?? string.Empty,
Alias = response.Alias,
FirstName = response.FirstName, FirstName = response.FirstName,
LastName = response.LastName, LastName = response.LastName,
Email = response.Email ?? string.Empty, Email = response.Email ?? string.Empty,
@@ -203,7 +201,7 @@ public class IdentityService(
Address = response.Address, Address = response.Address,
About = response.About, About = response.About,
Description = response.Description, Description = response.Description,
SocialNetworks = new SocialNetworksModel SocialNetworks = new()
{ {
FacebookUrl = response.SocialNetworks.FacebookUrl, FacebookUrl = response.SocialNetworks.FacebookUrl,
InstagramUrl = response.SocialNetworks.InstagramUrl, InstagramUrl = response.SocialNetworks.InstagramUrl,
@@ -214,14 +212,14 @@ public class IdentityService(
RedditUrl = response.SocialNetworks.RedditUrl, RedditUrl = response.SocialNetworks.RedditUrl,
YourWebsiteUrl = response.SocialNetworks.YourWebsiteUrl, YourWebsiteUrl = response.SocialNetworks.YourWebsiteUrl,
}, },
ProfileColors = new ProfileColorsModel ProfileColors = new()
{ {
BannerTop = response.ProfileColors.BannerTop, BannerTop = response.ProfileColors.BannerTop,
BannerBottom = response.ProfileColors.BannerBottom, BannerBottom = response.ProfileColors.BannerBottom,
Accent = response.ProfileColors.Accent, Accent = response.ProfileColors.Accent,
Menu = response.ProfileColors.Menu Menu = response.ProfileColors.Menu
}, },
StoredDataUrls = new StoredDataUrlsModel StoredDataUrls = new()
{ {
ProfilePictureUrl = response.StoredDataUrls.ProfilePictureUrl, ProfilePictureUrl = response.StoredDataUrls.ProfilePictureUrl,
BannerPictureUrl = response.StoredDataUrls.BannerPictureUrl, BannerPictureUrl = response.StoredDataUrls.BannerPictureUrl,
@@ -253,7 +251,7 @@ public class IdentityService(
Address = response.Address, Address = response.Address,
About = response.About, About = response.About,
Description = response.Description, Description = response.Description,
SocialNetworks = new SocialNetworksModel SocialNetworks = new()
{ {
FacebookUrl = response.SocialNetworks.FacebookUrl, FacebookUrl = response.SocialNetworks.FacebookUrl,
InstagramUrl = response.SocialNetworks.InstagramUrl, InstagramUrl = response.SocialNetworks.InstagramUrl,
@@ -264,14 +262,14 @@ public class IdentityService(
RedditUrl = response.SocialNetworks.RedditUrl, RedditUrl = response.SocialNetworks.RedditUrl,
YourWebsiteUrl = response.SocialNetworks.YourWebsiteUrl, YourWebsiteUrl = response.SocialNetworks.YourWebsiteUrl,
}, },
ProfileColors = new ProfileColorsModel ProfileColors = new()
{ {
BannerTop = response.ProfileColors.BannerTop, BannerTop = response.ProfileColors.BannerTop,
BannerBottom = response.ProfileColors.BannerBottom, BannerBottom = response.ProfileColors.BannerBottom,
Accent = response.ProfileColors.Accent, Accent = response.ProfileColors.Accent,
Menu = response.ProfileColors.Menu Menu = response.ProfileColors.Menu
}, },
StoredDataUrls = new StoredDataUrlsModel StoredDataUrls = new()
{ {
ProfilePictureUrl = response.StoredDataUrls.ProfilePictureUrl, ProfilePictureUrl = response.StoredDataUrls.ProfilePictureUrl,
BannerPictureUrl = response.StoredDataUrls.BannerPictureUrl, BannerPictureUrl = response.StoredDataUrls.BannerPictureUrl,
@@ -431,6 +429,7 @@ public class IdentityService(
key: jwtOptions.Value.Key, key: jwtOptions.Value.Key,
userId: user.Id, userId: user.Id,
email: user.Email, email: user.Email,
alias: user.Alias,
firstname: user.FirstName, firstname: user.FirstName,
lastname: user.LastName, lastname: user.LastName,
profilePictureUrl: user.StoredDataUrls.ProfilePictureUrl); profilePictureUrl: user.StoredDataUrls.ProfilePictureUrl);

View File

@@ -1,8 +1,10 @@
namespace Hutopy.Infrastructure.Identity.OwnedEntities; using System.ComponentModel.DataAnnotations;
namespace Hutopy.Infrastructure.Identity.OwnedEntities;
public class StoredDataUrls public class StoredDataUrls
{ {
public string BannerPictureUrl { get; set; } = string.Empty; [MaxLength(255)] public string? BannerPictureUrl { get; set; }
public string ProfilePictureUrl { get; set; } = string.Empty; [MaxLength(255)] public string? ProfilePictureUrl { get; set; }
public string WebsiteIconUrl { get; set; } = string.Empty; [MaxLength(255)] public string? WebsiteIconUrl { get; set; }
} }

View File

@@ -0,0 +1,563 @@
// <auto-generated />
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
{
/// <inheritdoc />
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<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTimeOffset>("Created")
.HasColumnType("datetimeoffset");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("EmailAddress")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTimeOffset>("LastModified")
.HasColumnType("datetimeoffset");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ReasonToJoin")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("SocialNetworkAccount")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("FutureCreators");
});
modelBuilder.Entity("Hutopy.Domain.Entities.UserTransaction", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<decimal>("Amount")
.HasPrecision(18, 2)
.HasColumnType("decimal(18,2)");
b.Property<string>("ApplicationUserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<DateTimeOffset>("Created")
.HasColumnType("datetimeoffset");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("Currency")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsConfirmed")
.HasColumnType("bit");
b.Property<DateTimeOffset>("LastModified")
.HasColumnType("datetimeoffset");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<bool>("Paid")
.HasColumnType("bit");
b.Property<string>("StripeBillingDetailEmail")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripeBillingDetailName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripeChargeId")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripeEventId")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripePaymentIntent")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripePaymentMethod")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripeReceiptUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("TipMessage")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("ApplicationUserId");
b.ToTable("UserTransactions");
});
modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("About")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("Address")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Alias")
.HasColumnType("nvarchar(max)");
b.Property<string>("BirthDate")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("City")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Country")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("CreatorAlias")
.HasColumnType("nvarchar(max)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Occupation")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("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<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("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<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("RoleId")
.HasColumnType("nvarchar(450)");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("Name")
.HasColumnType("nvarchar(450)");
b.Property<string>("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<string>("ApplicationUserId")
.HasColumnType("nvarchar(450)");
b1.Property<string>("Accent")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("BannerBottom")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("BannerTop")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("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<string>("ApplicationUserId")
.HasColumnType("nvarchar(450)");
b1.Property<string>("FacebookUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("InstagramUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("LinkedInUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("RedditUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("TikTokUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("XUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("YourWebsiteUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("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<string>("ApplicationUserId")
.HasColumnType("nvarchar(450)");
b1.Property<string>("BannerPictureUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("ProfilePictureUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("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<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", 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<string>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Hutopy.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class AddAlias : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Alias",
table: "AspNetUsers",
type: "nvarchar(max)",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Alias",
table: "AspNetUsers");
}
}
}

View File

@@ -0,0 +1,563 @@
// <auto-generated />
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
{
/// <inheritdoc />
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<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTimeOffset>("Created")
.HasColumnType("datetimeoffset");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("EmailAddress")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTimeOffset>("LastModified")
.HasColumnType("datetimeoffset");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ReasonToJoin")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("SocialNetworkAccount")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("FutureCreators");
});
modelBuilder.Entity("Hutopy.Domain.Entities.UserTransaction", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<decimal>("Amount")
.HasPrecision(18, 2)
.HasColumnType("decimal(18,2)");
b.Property<string>("ApplicationUserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<DateTimeOffset>("Created")
.HasColumnType("datetimeoffset");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("Currency")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsConfirmed")
.HasColumnType("bit");
b.Property<DateTimeOffset>("LastModified")
.HasColumnType("datetimeoffset");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<bool>("Paid")
.HasColumnType("bit");
b.Property<string>("StripeBillingDetailEmail")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripeBillingDetailName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripeChargeId")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripeEventId")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripePaymentIntent")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripePaymentMethod")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripeReceiptUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("TipMessage")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("ApplicationUserId");
b.ToTable("UserTransactions");
});
modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("About")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("Address")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Alias")
.HasColumnType("nvarchar(max)");
b.Property<string>("BirthDate")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("City")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Country")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("CreatorAlias")
.HasColumnType("nvarchar(max)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Occupation")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("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<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("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<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("RoleId")
.HasColumnType("nvarchar(450)");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("Name")
.HasColumnType("nvarchar(450)");
b.Property<string>("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<string>("ApplicationUserId")
.HasColumnType("nvarchar(450)");
b1.Property<string>("Accent")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("BannerBottom")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("BannerTop")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("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<string>("ApplicationUserId")
.HasColumnType("nvarchar(450)");
b1.Property<string>("FacebookUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("InstagramUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("LinkedInUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("RedditUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("TikTokUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("XUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("YourWebsiteUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("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<string>("ApplicationUserId")
.HasColumnType("nvarchar(450)");
b1.Property<string>("BannerPictureUrl")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b1.Property<string>("ProfilePictureUrl")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b1.Property<string>("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<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", 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<string>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,78 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Hutopy.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class MadeOptional_StoredDataUrls : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "WebsiteIconUrl",
table: "ApplicationUser_StoredDataUrls",
type: "nvarchar(255)",
maxLength: 255,
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
migrationBuilder.AlterColumn<string>(
name: "ProfilePictureUrl",
table: "ApplicationUser_StoredDataUrls",
type: "nvarchar(255)",
maxLength: 255,
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
migrationBuilder.AlterColumn<string>(
name: "BannerPictureUrl",
table: "ApplicationUser_StoredDataUrls",
type: "nvarchar(255)",
maxLength: 255,
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "WebsiteIconUrl",
table: "ApplicationUser_StoredDataUrls",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(255)",
oldMaxLength: 255,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ProfilePictureUrl",
table: "ApplicationUser_StoredDataUrls",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(255)",
oldMaxLength: 255,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "BannerPictureUrl",
table: "ApplicationUser_StoredDataUrls",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(255)",
oldMaxLength: 255,
oldNullable: true);
}
}
}

View File

@@ -17,7 +17,7 @@ namespace Hutopy.Infrastructure.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "8.0.3") .HasAnnotation("ProductVersion", "8.0.4")
.HasAnnotation("Relational:MaxIdentifierLength", 128); .HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
@@ -160,6 +160,9 @@ namespace Hutopy.Infrastructure.Migrations
.IsRequired() .IsRequired()
.HasColumnType("nvarchar(max)"); .HasColumnType("nvarchar(max)");
b.Property<string>("Alias")
.HasColumnType("nvarchar(max)");
b.Property<string>("BirthDate") b.Property<string>("BirthDate")
.IsRequired() .IsRequired()
.HasColumnType("nvarchar(max)"); .HasColumnType("nvarchar(max)");
@@ -472,16 +475,16 @@ namespace Hutopy.Infrastructure.Migrations
.HasColumnType("nvarchar(450)"); .HasColumnType("nvarchar(450)");
b1.Property<string>("BannerPictureUrl") b1.Property<string>("BannerPictureUrl")
.IsRequired() .HasMaxLength(255)
.HasColumnType("nvarchar(max)"); .HasColumnType("nvarchar(255)");
b1.Property<string>("ProfilePictureUrl") b1.Property<string>("ProfilePictureUrl")
.IsRequired() .HasMaxLength(255)
.HasColumnType("nvarchar(max)"); .HasColumnType("nvarchar(255)");
b1.Property<string>("WebsiteIconUrl") b1.Property<string>("WebsiteIconUrl")
.IsRequired() .HasMaxLength(255)
.HasColumnType("nvarchar(max)"); .HasColumnType("nvarchar(255)");
b1.HasKey("ApplicationUserId"); b1.HasKey("ApplicationUserId");

View File

@@ -12,10 +12,11 @@ public static class JwtTokenHelper
string issuer, string issuer,
string audience, string audience,
string key, string key,
string? userId, string userId,
string? email, string email,
string? firstname, string? alias,
string? lastname, string firstname,
string lastname,
string? profilePictureUrl) string? profilePictureUrl)
{ {
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)); var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
@@ -25,16 +26,19 @@ public static class JwtTokenHelper
{ {
new Claim(JwtRegisteredClaimNames.Sub, userId), new Claim(JwtRegisteredClaimNames.Sub, userId),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(ClaimTypes.NameIdentifier, userId), new Claim(ClaimTypes.NameIdentifier, userId), new Claim(ClaimTypes.Email, email),
new Claim(ClaimTypes.Email, email), new Claim(ClaimTypes.Name, email), new Claim(ClaimTypes.GivenName, firstname),
new Claim(ClaimTypes.Name, email), new Claim(ClaimTypes.Surname, lastname)
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) if (profilePictureUrl is not null)
{ {
claims.Add(new Claim("portrait-url", profilePictureUrl)); claims.Add(new(KnownClaims.PortraitUrl, profilePictureUrl));
} }
var token = new JwtSecurityToken( var token = new JwtSecurityToken(

View File

@@ -0,0 +1,7 @@
namespace Hutopy.Infrastructure.Utils;
public static class KnownClaims
{
public const string Alias = nameof(Alias);
public const string PortraitUrl = nameof(PortraitUrl);
}

View File

@@ -1,4 +1,5 @@
using System.Security.Claims; using System.Security.Claims;
using Hutopy.Infrastructure.Utils;
namespace Hutopy.Web.Common; namespace Hutopy.Web.Common;
@@ -6,25 +7,48 @@ public static class ClaimsPrincipalExtensions
{ {
public static Guid GetUserId(this ClaimsPrincipal claims) public static Guid GetUserId(this ClaimsPrincipal claims)
{ {
return (Guid)claims.GetFirstValue<Guid>(ClaimTypes.NameIdentifier); return (Guid)claims.GetRequiredClaim<Guid>(ClaimTypes.NameIdentifier);
} }
public static string GetName(this ClaimsPrincipal claims)
{
return (string)claims.GetRequiredClaim<string>(ClaimTypes.Name);
}
public static string? GetAlias(this ClaimsPrincipal claims)
{
return (string?)claims.GetClaim<string?>(KnownClaims.Alias);
}
public static string? GetPortraitUrl(this ClaimsPrincipal claims)
{
return (string?)claims.GetClaim<string?>(KnownClaims.PortraitUrl);
}
public static string GetFirstName(this ClaimsPrincipal claims) public static string GetFirstName(this ClaimsPrincipal claims)
{ {
return (string)claims.GetFirstValue<string>(ClaimTypes.GivenName); return (string)claims.GetRequiredClaim<string>(ClaimTypes.GivenName);
} }
public static string GetLastName(this ClaimsPrincipal claims) public static string GetLastName(this ClaimsPrincipal claims)
{ {
return (string)claims.GetFirstValue<string>(ClaimTypes.Surname); return (string)claims.GetRequiredClaim<string>(ClaimTypes.Surname);
} }
public static string GetEmail(this ClaimsPrincipal claims) public static string GetEmail(this ClaimsPrincipal claims)
{ {
return (string)claims.GetFirstValue<string>(ClaimTypes.Email); return (string)claims.GetRequiredClaim<string>(ClaimTypes.Email);
} }
public static object GetFirstValue<TValue>(this ClaimsPrincipal claims, string key) public static object? GetClaim<TValue>(this ClaimsPrincipal claims, string key)
{
var claim = claims.FindFirst(key);
if (claim is null) return default;
return claims.GetRequiredClaim<TValue>(key);
}
public static object GetRequiredClaim<TValue>(this ClaimsPrincipal claims, string key)
{ {
var claim = claims.FindFirst(key); var claim = claims.FindFirst(key);

View File

@@ -74,6 +74,7 @@ public class GoogleController(
jwtOptions.Value.Key, jwtOptions.Value.Key,
user.Id, user.Id,
user.Email, user.Email,
user.Alias,
user.FirstName, user.FirstName,
user.LastName, user.LastName,
user.StoredDataUrls.ProfilePictureUrl); user.StoredDataUrls.ProfilePictureUrl);

View File

@@ -1,11 +1,15 @@
namespace Hutopy.Web.Features.Messages.Data; using System.ComponentModel.DataAnnotations;
namespace Hutopy.Web.Features.Messages.Data;
public class Message public class Message
{ {
public Guid Id { get; init; } public Guid Id { get; set; }
public Guid SubjectId { get; init; } public Guid SubjectId { get; set; }
public Guid CreatedBy { get; init; } public Guid CreatedBy { get; set; }
public DateTimeOffset CreatedAt { get; init; } [MaxLength(64)] public required string CreatedByName { get; set; }
public Guid? ParentId { get; init; } [MaxLength(256)] public string? CreatedByPortraitUrl { get; set; }
public string Value { get; init; } public DateTimeOffset CreatedAt { get; set; }
public Guid? ParentId { get; set; }
public required string Value { get; set; }
} }

View File

@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore; using Hutopy.Web.Features.Messages.Handlers.Models;
using Microsoft.EntityFrameworkCore;
namespace Hutopy.Web.Features.Messages.Data; namespace Hutopy.Web.Features.Messages.Data;
@@ -9,7 +10,7 @@ public class MessagingDbContext(
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {
modelBuilder.HasDefaultSchema("Messaging"); modelBuilder.HasDefaultSchema("Messaging");
modelBuilder modelBuilder
.Entity<Message>() .Entity<Message>()
.Property(c => c.CreatedAt) .Property(c => c.CreatedAt)
@@ -17,5 +18,41 @@ public class MessagingDbContext(
.HasDefaultValueSql("CURRENT_TIMESTAMP"); .HasDefaultValueSql("CURRENT_TIMESTAMP");
} }
public DbSet<Message> Messages { get; set; } public DbSet<Message> Messages { get; set; }
public async Task<List<MessageDto>> 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;
}
} }

View File

@@ -1,14 +1,30 @@
using FastEndpoints; using FastEndpoints;
using FluentValidation;
using Hutopy.Web.Common; using Hutopy.Web.Common;
using Hutopy.Web.Features.Messages.Data; using Hutopy.Web.Features.Messages.Data;
namespace Hutopy.Web.Features.Messages.Handlers; namespace Hutopy.Web.Features.Messages.Handlers;
public class AddMessageRequest public sealed class AddMessageRequest
{ {
public Guid? Id { get; set; } public Guid? Id { get; set; }
public Guid SubjectId { get; set; } public required Guid SubjectId { get; set; }
public string Message { get; set; } public required string Message { get; set; }
}
internal sealed class AddMessageRequestValidator
: Validator<AddMessageRequest>
{
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( public class AddMessage(
@@ -30,6 +46,8 @@ public class AddMessage(
Id = req.Id ?? GuidHelper.GenerateUuidV7(), Id = req.Id ?? GuidHelper.GenerateUuidV7(),
SubjectId = req.SubjectId, SubjectId = req.SubjectId,
CreatedBy = User.GetUserId(), CreatedBy = User.GetUserId(),
CreatedByName = User.GetAlias() ?? $"{User.GetFirstName()} {User.GetLastName()}",
CreatedByPortraitUrl = User.GetPortraitUrl(),
Value = req.Message Value = req.Message
}; };

View File

@@ -1,16 +1,37 @@
using FastEndpoints; using FastEndpoints;
using FluentValidation;
using Hutopy.Web.Common; using Hutopy.Web.Common;
using Hutopy.Web.Features.Messages.Data; using Hutopy.Web.Features.Messages.Data;
namespace Hutopy.Web.Features.Messages.Handlers; 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 ParentId { get; set; }
public required Guid SubjectId { get; set; }
public required string Message { get; set; } public required string Message { get; set; }
} }
internal sealed class AddReplyRequestValidator
: Validator<AddReplyRequest>
{
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( internal sealed class AddReply(
MessagingDbContext context) MessagingDbContext context)
: Endpoint<AddReplyRequest> : Endpoint<AddReplyRequest>
@@ -28,9 +49,10 @@ internal sealed class AddReply(
var message = new Message var message = new Message
{ {
Id = GuidHelper.GenerateUuidV7(), Id = GuidHelper.GenerateUuidV7(),
SubjectId = req.SubjectId, SubjectId = req.SubjectId,
ParentId = req.ParentId, ParentId = req.ParentId,
CreatedBy = User.GetUserId(), CreatedBy = User.GetUserId(),
CreatedByName = User.GetName(),
Value = req.Message Value = req.Message
}; };

View File

@@ -1,19 +1,24 @@
using FastEndpoints; using FastEndpoints;
using Hutopy.Web.Features.Messages.Data; using Hutopy.Web.Features.Messages.Data;
using Microsoft.EntityFrameworkCore; using Hutopy.Web.Features.Messages.Handlers.Models;
namespace Hutopy.Web.Features.Messages.Handlers; namespace Hutopy.Web.Features.Messages.Handlers;
public class GetMessagesRequest public sealed class GetMessagesRequest
{ {
public Guid SubjectId { get; set; } public Guid SubjectId { get; set; }
[BindFrom("page_size")] public int PageSize { get; set; } = 10; [BindFrom("page_size")] public int PageSize { get; set; } = 10;
[BindFrom("last_id")] public Guid? LastId { get; set; } [BindFrom("last_id")] public Guid? LastId { get; set; }
} }
public record struct GetMessagesResponse
{
public required List<MessageDto> Messages { get; init; }
}
public class GetMessages( public class GetMessages(
MessagingDbContext context) MessagingDbContext context)
: Endpoint<GetMessagesRequest, List<Message>> : Endpoint<GetMessagesRequest, GetMessagesResponse>
{ {
public override void Configure() public override void Configure()
{ {
@@ -26,34 +31,18 @@ public class GetMessages(
GetMessagesRequest req, GetMessagesRequest req,
CancellationToken ct) CancellationToken ct)
{ {
var query = context var messages = await context.GetMessagesAsync(
.Messages req.SubjectId,
.Where(c => c.SubjectId == req.SubjectId) null,
.Where(c => c.ParentId == null); req.LastId,
req.PageSize,
if (req.LastId.HasValue) ct);
{
var lastMessage = await context await SendAsync(
.Messages new()
.Where(c => c.Id == req.LastId.Value)
.Select(c => new { c.CreatedAt, c.Id })
.FirstOrDefaultAsync(cancellationToken: ct);
if (lastMessage != null)
{ {
query = query Messages = messages
.Where(c => c.CreatedAt < lastMessage.CreatedAt },
|| (c.CreatedAt == lastMessage.CreatedAt && c.Id < lastMessage.Id)); cancellation: ct);
}
}
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);
} }
} }

View File

@@ -1,5 +1,6 @@
using FastEndpoints; using FastEndpoints;
using Hutopy.Web.Features.Messages.Data; using Hutopy.Web.Features.Messages.Data;
using Hutopy.Web.Features.Messages.Handlers.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Hutopy.Web.Features.Messages.Handlers; namespace Hutopy.Web.Features.Messages.Handlers;
@@ -9,9 +10,14 @@ public class GetMessagesByUserRequest
public Guid UserId { get; set; } public Guid UserId { get; set; }
} }
public record struct GetMessagesByUserResponse
{
public required List<MessageDto> Messages { get; init; }
}
public class GetMessagesByUser( public class GetMessagesByUser(
MessagingDbContext context) MessagingDbContext context)
: Endpoint<GetMessagesByUserRequest, List<Message>> : Endpoint<GetMessagesByUserRequest, GetMessagesByUserResponse>
{ {
public override void Configure() public override void Configure()
{ {
@@ -24,12 +30,19 @@ public class GetMessagesByUser(
GetMessagesByUserRequest req, GetMessagesByUserRequest req,
CancellationToken ct) CancellationToken ct)
{ {
var posts = await context var messages = await context
.Messages .Messages
.Where(c => c.CreatedBy == req.UserId) .Where(c => c.CreatedBy == req.UserId)
.Where(c => c.ParentId == null) .Where(c => c.ParentId == null)
.ToListAsync(cancellationToken: ct); .ToListAsync(cancellationToken: ct);
await SendAsync(posts, cancellation: ct); await SendAsync(
new()
{
Messages = messages
.Select(message => message.ToDto())
.ToList()
},
cancellation: ct);
} }
} }

View File

@@ -1,6 +1,6 @@
using FastEndpoints; using FastEndpoints;
using Hutopy.Web.Features.Messages.Data; using Hutopy.Web.Features.Messages.Data;
using Microsoft.EntityFrameworkCore; using Hutopy.Web.Features.Messages.Handlers.Models;
namespace Hutopy.Web.Features.Messages.Handlers; namespace Hutopy.Web.Features.Messages.Handlers;
@@ -12,9 +12,14 @@ public class GetRepliesRequest
[BindFrom("last_id")] public Guid? LastId { get; set; } [BindFrom("last_id")] public Guid? LastId { get; set; }
} }
public record struct GetRepliesResponse
{
public required List<MessageDto> Messages { get; init; }
}
public class GetReplies( public class GetReplies(
MessagingDbContext context) MessagingDbContext context)
: Endpoint<GetRepliesRequest, List<Message>> : Endpoint<GetRepliesRequest, GetRepliesResponse>
{ {
public override void Configure() public override void Configure()
{ {
@@ -27,20 +32,18 @@ public class GetReplies(
GetRepliesRequest req, GetRepliesRequest req,
CancellationToken ct) CancellationToken ct)
{ {
var query = context var replies = await context.GetMessagesAsync(
.Messages req.SubjectId,
.Where(c => c.SubjectId == req.SubjectId) req.ParentId,
.Where(c => c.ParentId == req.ParentId); req.LastId,
req.PageSize,
ct);
query = query.OrderByDescending(c => c.CreatedAt); await SendAsync(
new()
if (req.LastId is not null) {
query = query.Where(c => c.Id < req.LastId.Value); Messages = replies,
},
query = query.Take(req.PageSize); cancellation: ct);
var replies = await query.ToListAsync(cancellationToken: ct);
await SendAsync(replies, cancellation: ct);
} }
} }

View File

@@ -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
};
}

View File

@@ -9,10 +9,10 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable #nullable disable
namespace Hutopy.Web.Messages.Migrations namespace Hutopy.Web.Features.Messages.Migrations
{ {
[DbContext(typeof(MessagingDbContext))] [DbContext(typeof(MessagingDbContext))]
[Migration("20240718173016_Initial")] [Migration("20240721041322_Initial")]
partial class Initial partial class Initial
{ {
/// <inheritdoc /> /// <inheritdoc />
@@ -26,7 +26,7 @@ namespace Hutopy.Web.Messages.Migrations
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Hutopy.Web.Messages.Data.Message", b => modelBuilder.Entity("Hutopy.Web.Features.Messages.Data.Message", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@@ -40,6 +40,13 @@ namespace Hutopy.Web.Messages.Migrations
b.Property<Guid>("CreatedBy") b.Property<Guid>("CreatedBy")
.HasColumnType("uuid"); .HasColumnType("uuid");
b.Property<string>("CreatedByName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("CreatedByPortraitUrl")
.HasColumnType("text");
b.Property<Guid?>("ParentId") b.Property<Guid?>("ParentId")
.HasColumnType("uuid"); .HasColumnType("uuid");

View File

@@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable #nullable disable
namespace Hutopy.Web.Messages.Migrations namespace Hutopy.Web.Features.Messages.Migrations
{ {
/// <inheritdoc /> /// <inheritdoc />
public partial class Initial : Migration public partial class Initial : Migration
@@ -22,6 +22,8 @@ namespace Hutopy.Web.Messages.Migrations
Id = table.Column<Guid>(type: "uuid", nullable: false), Id = table.Column<Guid>(type: "uuid", nullable: false),
SubjectId = table.Column<Guid>(type: "uuid", nullable: false), SubjectId = table.Column<Guid>(type: "uuid", nullable: false),
CreatedBy = table.Column<Guid>(type: "uuid", nullable: false), CreatedBy = table.Column<Guid>(type: "uuid", nullable: false),
CreatedByName = table.Column<string>(type: "text", nullable: false),
CreatedByPortraitUrl = table.Column<string>(type: "text", nullable: true),
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP"), CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP"),
ParentId = table.Column<Guid>(type: "uuid", nullable: true), ParentId = table.Column<Guid>(type: "uuid", nullable: true),
Value = table.Column<string>(type: "text", nullable: false) Value = table.Column<string>(type: "text", nullable: false)

View File

@@ -0,0 +1,69 @@
// <auto-generated />
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
{
/// <inheritdoc />
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<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTimeOffset>("CreatedAt")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasDefaultValueSql("CURRENT_TIMESTAMP");
b.Property<Guid>("CreatedBy")
.HasColumnType("uuid");
b.Property<string>("CreatedByName")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)");
b.Property<string>("CreatedByPortraitUrl")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<Guid?>("ParentId")
.HasColumnType("uuid");
b.Property<Guid>("SubjectId")
.HasColumnType("uuid");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Messages", "Messaging");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,60 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Hutopy.Web.Features.Messages.Migrations
{
/// <inheritdoc />
public partial class ChangedAuthorDefinition : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "CreatedByPortraitUrl",
schema: "Messaging",
table: "Messages",
type: "character varying(256)",
maxLength: 256,
nullable: true,
oldClrType: typeof(string),
oldType: "text",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "CreatedByName",
schema: "Messaging",
table: "Messages",
type: "character varying(64)",
maxLength: 64,
nullable: false,
oldClrType: typeof(string),
oldType: "text");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "CreatedByPortraitUrl",
schema: "Messaging",
table: "Messages",
type: "text",
nullable: true,
oldClrType: typeof(string),
oldType: "character varying(256)",
oldMaxLength: 256,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "CreatedByName",
schema: "Messaging",
table: "Messages",
type: "text",
nullable: false,
oldClrType: typeof(string),
oldType: "character varying(64)",
oldMaxLength: 64);
}
}
}

View File

@@ -8,7 +8,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable #nullable disable
namespace Hutopy.Web.Messages.Migrations namespace Hutopy.Web.Features.Messages.Migrations
{ {
[DbContext(typeof(MessagingDbContext))] [DbContext(typeof(MessagingDbContext))]
partial class MessagingDbContextModelSnapshot : ModelSnapshot partial class MessagingDbContextModelSnapshot : ModelSnapshot
@@ -23,7 +23,7 @@ namespace Hutopy.Web.Messages.Migrations
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Hutopy.Web.Messages.Data.Message", b => modelBuilder.Entity("Hutopy.Web.Features.Messages.Data.Message", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@@ -37,6 +37,15 @@ namespace Hutopy.Web.Messages.Migrations
b.Property<Guid>("CreatedBy") b.Property<Guid>("CreatedBy")
.HasColumnType("uuid"); .HasColumnType("uuid");
b.Property<string>("CreatedByName")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)");
b.Property<string>("CreatedByPortraitUrl")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<Guid?>("ParentId") b.Property<Guid?>("ParentId")
.HasColumnType("uuid"); .HasColumnType("uuid");

View File

@@ -35,13 +35,13 @@ internal class TestDataSeeder(
{ {
if (contentContext.Contents.Any()) return; if (contentContext.Contents.Any()) return;
_ = await CreateAdministratorAsync("admin"); _users.Add(await CreateUserAsync("admin", Roles.Administrator));
_ = await CreateUserAsync("userA"); _users.Add(await CreateUserAsync("userA"));
_ = await CreateUserAsync("userB"); _users.Add(await CreateUserAsync("userB"));
foreach (var creator in _creators) foreach (var creator in _creators)
{ {
_ = await CreateCreatorAsync(creator); _users.Add(await CreateCreatorAsync(creator));
var contents = GenerateContent(creator, 100); var contents = GenerateContent(creator, 100);
foreach (var content in contents) foreach (var content in contents)
@@ -96,15 +96,17 @@ internal class TestDataSeeder(
for (var m = messageCount; m > 0; m--) for (var m = messageCount; m > 0; m--)
{ {
currentDate = currentDate.AddSeconds(-Random.Shared.Next(100, 100_000)); 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 var message = new Message
{ {
Id = GuidHelper.GenerateUuidV7(), Id = GuidHelper.GenerateUuidV7(),
SubjectId = content.Id, SubjectId = content.Id,
CreatedBy = Guid.Parse(author.Id),
CreatedAt = currentDate, 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); messagingContext.Messages.Add(message);
@@ -124,7 +126,7 @@ internal class TestDataSeeder(
{ {
currentDate = currentDate.AddSeconds(-Random.Shared.Next(100, 100_000)); 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 var message = new Message
{ {
@@ -132,8 +134,10 @@ internal class TestDataSeeder(
SubjectId = content.Id, SubjectId = content.Id,
ParentId = parent.Id, ParentId = parent.Id,
CreatedBy = Guid.Parse(author.Id), CreatedBy = Guid.Parse(author.Id),
CreatedByName = author.Alias ?? $"{author.FirstName} {author.LastName}",
CreatedByPortraitUrl = author.StoredDataUrls.ProfilePictureUrl,
CreatedAt = currentDate, CreatedAt = currentDate,
Value = $"Reply {r} to {parent.Value}" Value = $"Reply {r} to {parent.Value} on {content.Title}"
}; };
messagingContext.Messages.Add(message); messagingContext.Messages.Add(message);
@@ -144,31 +148,22 @@ internal class TestDataSeeder(
return replies; return replies;
} }
private async Task<ApplicationUser> CreateAdministratorAsync(string name) private async Task<ApplicationUser> CreateUserAsync(string name, params string[] roles)
{
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<ApplicationUser> CreateUserAsync(string name)
{ {
var user = new ApplicationUser var user = new ApplicationUser
{ {
UserName = $"{name}@test", UserName = $"{name}@test",
Email = $"{name}@test", Email = $"{name}@test",
EmailConfirmed = true, EmailConfirmed = true,
Alias = name,
FirstName = $"FirstName of {name}", FirstName = $"FirstName of {name}",
LastName = $"LastName of {name}" LastName = $"LastName of {name}"
}; };
await userManager.CreateAsync(user, DefaultPassword); await userManager.CreateAsync(user, DefaultPassword);
if (roles.Length > 0) await userManager.AddToRolesAsync(user, roles);
return user; return user;
} }
@@ -180,12 +175,19 @@ internal class TestDataSeeder(
} }
private readonly List<ApplicationUser> _users =
[
];
private readonly static ApplicationUser Hutopy = new() private readonly static ApplicationUser Hutopy = new()
{ {
UserName = "hutopy@test", UserName = "hutopy@test",
Email = "hutopy@test", Email = "hutopy@test",
EmailConfirmed = true, EmailConfirmed = true,
CreatorAlias = "hutopy", CreatorAlias = "hutopy",
FirstName = "FirstName of a Brand/Creator",
LastName = "LastName of a Brand/Creator",
About = "Page officielle", About = "Page officielle",
Description = "Site officiel pour Hutopy. Venez-nous-y retrouver avec tous vos fans!", Description = "Site officiel pour Hutopy. Venez-nous-y retrouver avec tous vos fans!",
ProfileColors = new ProfileColors ProfileColors = new ProfileColors

View File

@@ -65,7 +65,12 @@ public partial class Testing
var userManager = scope.ServiceProvider.GetRequiredService<ApplicationUserManager>(); var userManager = scope.ServiceProvider.GetRequiredService<ApplicationUserManager>();
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); var result = await userManager.CreateAsync(user, password);