Split creators out of identity
This commit is contained in:
@@ -28,8 +28,10 @@ public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, IResu
|
||||
|
||||
var user = await _identityService.FindUserByEmailAsync(request.EmailAddress);
|
||||
|
||||
if (user is null) throw new InvalidOperationException("This should never happen, we just created the user.");
|
||||
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return Results.Ok(new Guid(user?.Id ?? string.Empty));
|
||||
return Results.Ok(user.Id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,21 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Hutopy.Application.Common.Interfaces;
|
||||
using Hutopy.Application.Common.Models;
|
||||
using Hutopy.Application.Users.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Hutopy.Application.Users.Commands;
|
||||
|
||||
public class UpdateCurrentUserCommand : IRequest<IResult>
|
||||
{
|
||||
public required string FirstName { get; init; }
|
||||
public required string LastName { get; init; }
|
||||
public required string Occupation { get; init; }
|
||||
public required string PhoneNumber { get; init; }
|
||||
public required string BirthDate { get; init; }
|
||||
public required string Country { get; init; }
|
||||
public required string City { get; init; }
|
||||
public required string Address { get; init; }
|
||||
public required string About { get; init; }
|
||||
public required string Description { get; init; }
|
||||
public required SocialNetworksModel SocialNetworks { get; init; }
|
||||
public required ProfileColorsModel ProfileColors { get; init; }
|
||||
public required string? Alias { get; init; }
|
||||
public required string? FirstName { get; init; }
|
||||
public required string? LastName { get; init; }
|
||||
public required string? Occupation { get; init; }
|
||||
public required string? BirthDate { get; init; }
|
||||
public required string? Country { get; init; }
|
||||
public required string? City { get; init; }
|
||||
public required string? Address { get; init; }
|
||||
|
||||
|
||||
[NotMapped]
|
||||
private class Mapping : Profile
|
||||
{
|
||||
@@ -32,8 +26,11 @@ public class UpdateCurrentUserCommand : IRequest<IResult>
|
||||
}
|
||||
}
|
||||
|
||||
public class UpdateCurrentUserCommandHandler(IApplicationDbContext context, IIdentityService identityService, IMapper mapper) :
|
||||
IRequestHandler<UpdateCurrentUserCommand, IResult>
|
||||
public class UpdateCurrentUserCommandHandler(
|
||||
IApplicationDbContext context,
|
||||
IIdentityService identityService,
|
||||
IMapper mapper)
|
||||
: IRequestHandler<UpdateCurrentUserCommand, IResult>
|
||||
{
|
||||
public async Task<IResult> Handle(UpdateCurrentUserCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -43,12 +40,11 @@ public class UpdateCurrentUserCommandHandler(IApplicationDbContext context, IIde
|
||||
|
||||
var userModel = mapper.Map<UserModel>(request);
|
||||
userModel.Id = identityUser.Id;
|
||||
|
||||
|
||||
var result = await identityService.UpdateCurrentUserAsync(userModel);
|
||||
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
|
||||
return result.Succeeded ? Results.Ok(result.GetValueOrDefault()) : Results.Problem(result.GetErrorsAsString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
using Hutopy.Application.AzureBlobStorage.Constants;
|
||||
using Hutopy.Application.Common.Interfaces;
|
||||
using Hutopy.Application.Utils;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Hutopy.Application.Users.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Upload a banner picture. If the user has the url already, set the BannerPictureUrl in the user only without upload.
|
||||
/// </summary>
|
||||
public class UploadBannerPictureCommand : IRequest<IResult>
|
||||
{
|
||||
public required MemoryStream BannerPicture { get; init; }
|
||||
public string BannerPictureUrl { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
public class UploadBannerPictureCommandHandler(IHttpContextAccessor contextAccessor, IIdentityService identityService, IAzureBlobStorageService azureBlobStorageService) : IRequestHandler<UploadBannerPictureCommand, IResult>
|
||||
{
|
||||
public async Task<IResult> Handle(UploadBannerPictureCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// If an url to the picture is provided, use it right away and don't upload anything.
|
||||
if (!string.IsNullOrEmpty(request.BannerPictureUrl))
|
||||
{
|
||||
await identityService.UpdateCurrentUserBannerPictureUrlAsync(request.BannerPictureUrl);
|
||||
return Results.Ok(request.BannerPictureUrl);
|
||||
}
|
||||
|
||||
var contentType = contextAccessor.EnsureContentType();
|
||||
|
||||
var identityUser = await identityService.GetCurrentUserAsync();
|
||||
var currentUserId = new Guid(identityUser?.Id ?? "").ToString();
|
||||
|
||||
var blobName = $"{currentUserId}/{SubDirectoryNames.Profile}/{CommonFileNames.BannerPicture}";
|
||||
|
||||
var url = await azureBlobStorageService.UploadFileAsync(
|
||||
ContainerNames.Users,
|
||||
blobName,
|
||||
request.BannerPicture,
|
||||
contentType,
|
||||
cancellationToken);
|
||||
|
||||
await identityService.UpdateCurrentUserBannerPictureUrlAsync(url);
|
||||
|
||||
return Results.Ok(url);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,38 +10,29 @@ namespace Hutopy.Application.Users.Commands;
|
||||
/// </summary>
|
||||
public class UploadProfilePictureCommand : IRequest<IResult>
|
||||
{
|
||||
public required MemoryStream ProfilePicture { get; init; }
|
||||
public string ProfilePictureUrl { get; init; } = string.Empty;
|
||||
public required IFormFile File { get; init; }
|
||||
}
|
||||
|
||||
public class UploadProfilePictureCommandHandler(IHttpContextAccessor contextAccessor, IIdentityService identityService, IAzureBlobStorageService azureBlobStorageService) : IRequestHandler<UploadProfilePictureCommand, IResult>
|
||||
public class UploadProfilePictureCommandHandler(
|
||||
IHttpContextAccessor contextAccessor,
|
||||
IIdentityService identityService,
|
||||
IAzureBlobStorageService azureBlobStorageService) : IRequestHandler<UploadProfilePictureCommand, IResult>
|
||||
{
|
||||
public async Task<IResult> Handle(UploadProfilePictureCommand request, CancellationToken ct)
|
||||
{
|
||||
// If an url to the picture is provided, use it right away and don't upload anything.
|
||||
if (!string.IsNullOrEmpty(request.ProfilePictureUrl))
|
||||
{
|
||||
await identityService.UpdateCurrentUserProfilePictureUrlAsync(request.ProfilePictureUrl);
|
||||
return Results.Ok(request.ProfilePictureUrl);
|
||||
}
|
||||
|
||||
var contentType = contextAccessor.EnsureContentType();
|
||||
|
||||
|
||||
var identityUser = await identityService.GetCurrentUserAsync();
|
||||
var currentUserId = new Guid(identityUser?.Id ?? "").ToString();
|
||||
|
||||
var blobName = $"{currentUserId}/{SubDirectoryNames.Profile}/{CommonFileNames.ProfilePicture}";
|
||||
|
||||
|
||||
var url = await azureBlobStorageService.UploadFileAsync(
|
||||
ContainerNames.Users,
|
||||
blobName,
|
||||
request.ProfilePicture,
|
||||
ContainerNames.Users,
|
||||
$"{identityUser.Id}/{SubDirectoryNames.Profile}/{CommonFileNames.ProfilePicture}",
|
||||
request.File.OpenReadStream(),
|
||||
contentType,
|
||||
ct);
|
||||
|
||||
await identityService.UpdateCurrentUserProfilePictureUrlAsync(url);
|
||||
|
||||
await identityService.UpdateCurrentUserPortraitUrlAsync(url);
|
||||
|
||||
return Results.Ok(url);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
using Hutopy.Application.AzureBlobStorage.Constants;
|
||||
using Hutopy.Application.Common.Interfaces;
|
||||
using Hutopy.Application.Utils;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Hutopy.Application.Users.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Upload a website icon. If the user has the url already, set the WebsitePictureUrl in the user only without upload.
|
||||
/// </summary>
|
||||
public class UploadWebsiteIconCommand : IRequest<IResult>
|
||||
{
|
||||
public required MemoryStream WebsiteIcon { get; init; }
|
||||
|
||||
public string WebsitePictureUrl { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
public class UploadWebsiteIconCommandHandler(
|
||||
IHttpContextAccessor contextAccessor,
|
||||
IIdentityService identityService,
|
||||
IAzureBlobStorageService azureBlobStorageService) : IRequestHandler<UploadWebsiteIconCommand, IResult>
|
||||
{
|
||||
public async Task<IResult> Handle(UploadWebsiteIconCommand request, CancellationToken ct)
|
||||
{
|
||||
// If an url to the picture is provided, use it right away and don't upload anything.
|
||||
if (!string.IsNullOrEmpty(request.WebsitePictureUrl))
|
||||
{
|
||||
await identityService.UpdateCurrentUserWebsiteIconUrlAsync(request.WebsitePictureUrl);
|
||||
return Results.Ok(request.WebsitePictureUrl);
|
||||
}
|
||||
|
||||
var contentType = contextAccessor.EnsureContentType();
|
||||
|
||||
var identityUser = await identityService.GetCurrentUserAsync();
|
||||
var currentUserId = new Guid(identityUser?.Id ?? "").ToString();
|
||||
|
||||
var blobName = $"{currentUserId}/{SubDirectoryNames.Profile}/{CommonFileNames.WebsiteIcon}";
|
||||
|
||||
var url = await azureBlobStorageService.UploadFileAsync(
|
||||
ContainerNames.Users,
|
||||
blobName,
|
||||
request.WebsiteIcon,
|
||||
contentType,
|
||||
ct);
|
||||
|
||||
await identityService.UpdateCurrentUserWebsiteIconUrlAsync(url);
|
||||
|
||||
return Results.Ok(request.WebsitePictureUrl);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Hutopy.Application.Users.Models;
|
||||
|
||||
public class ProfileColorsModel
|
||||
{
|
||||
public string BannerTop { get; init; } = String.Empty;
|
||||
public string BannerBottom { get; init; } = String.Empty;
|
||||
public string Accent { get; init; } = String.Empty;
|
||||
public string Menu { get; init; } = String.Empty;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
namespace Hutopy.Application.Users.Models;
|
||||
|
||||
public class SocialNetworksModel
|
||||
{
|
||||
public string FacebookUrl { get; init; } = string.Empty;
|
||||
public string InstagramUrl { get; init; } = string.Empty;
|
||||
public string XUrl { get; init; } = string.Empty;
|
||||
public string LinkedInUrl { get; init; } = string.Empty;
|
||||
public string TikTokUrl { get; init; } = string.Empty;
|
||||
public string YoutubeUrl { get; init; } = string.Empty;
|
||||
public string RedditUrl { get; init; } = string.Empty;
|
||||
public string YourWebsiteUrl { get; init; } = string.Empty;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Hutopy.Application.Users.Models;
|
||||
|
||||
public class StoredDataUrlsModel
|
||||
{
|
||||
public string? BannerPictureUrl { get; set; }
|
||||
public string? ProfilePictureUrl { get; set; }
|
||||
public string? WebsiteIconUrl { get; set; }
|
||||
}
|
||||
@@ -11,15 +11,16 @@ public class GetCurrentUserQueryHandler(
|
||||
)
|
||||
: IRequestHandler<GetCurrentUserQuery, UserDto>
|
||||
{
|
||||
public async Task<UserDto> Handle(GetCurrentUserQuery request, CancellationToken cancellationToken)
|
||||
public async Task<UserDto?> Handle(GetCurrentUserQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var identityUser = await identityService.GetCurrentUserAsync();
|
||||
var currentUserId = Guid.Parse(identityUser!.Id!);
|
||||
var userModel = await identityService.GetCurrentUserAsync();
|
||||
|
||||
if (userModel is null) return null;
|
||||
|
||||
var transactions = await context
|
||||
.UserTransactions
|
||||
.Where(x => x.ApplicationUserId == currentUserId.ToString())
|
||||
.OrderBy(x => x.LastModified)
|
||||
.Where(x => x.ApplicationUserId == userModel.Id)
|
||||
.OrderBy(x => x.LastModifiedAt)
|
||||
.ProjectTo<UserTransactionDto>(mapper.ConfigurationProvider)
|
||||
.Where(x => x.IsConfirmed == true)
|
||||
.ToListAsync(cancellationToken);
|
||||
@@ -28,24 +29,17 @@ public class GetCurrentUserQueryHandler(
|
||||
|
||||
var user = new UserDto
|
||||
{
|
||||
Id = currentUserId,
|
||||
Alias = identityUser.Alias,
|
||||
FirstName = identityUser.FirstName,
|
||||
LastName = identityUser.LastName,
|
||||
UserName = identityUser.UserName,
|
||||
CreatorAlias= identityUser.CreatorAlias,
|
||||
Occupation = identityUser.Occupation,
|
||||
PhoneNumber = identityUser.PhoneNumber,
|
||||
Email = identityUser.Email,
|
||||
BirthDate = identityUser.BirthDate,
|
||||
Country = identityUser.Country,
|
||||
City = identityUser.City,
|
||||
Address = identityUser.Address,
|
||||
About = identityUser.About,
|
||||
Description = identityUser.Description,
|
||||
SocialNetworks = identityUser.SocialNetworks,
|
||||
ProfileColors = identityUser.ProfileColors,
|
||||
StoredDataUrls = identityUser.StoredDataUrls,
|
||||
Id = userModel.Id,
|
||||
Alias = userModel.Alias,
|
||||
PortraitUrl = userModel.PortraitUrl,
|
||||
FirstName = userModel.FirstName,
|
||||
LastName = userModel.LastName,
|
||||
UserName = userModel.UserName,
|
||||
Occupation = userModel.Occupation,
|
||||
PhoneNumber = userModel.PhoneNumber,
|
||||
Email = userModel.Email,
|
||||
BirthDate = userModel.BirthDate,
|
||||
Address = userModel.Address,
|
||||
UserTransactions = transactions,
|
||||
TotalBalance = transactions.Sum(x => x.Amount),
|
||||
UserRoles = roles,
|
||||
|
||||
@@ -8,16 +8,16 @@ public record GetCurrentUserProfilePictureQuery : IRequest<Stream>;
|
||||
public class GetCurrentUserProfilePictureQueryHandler(
|
||||
IIdentityService identityService,
|
||||
IAzureBlobStorageService azureBlobStorageService
|
||||
)
|
||||
)
|
||||
: IRequestHandler<GetCurrentUserProfilePictureQuery, Stream>
|
||||
{
|
||||
public async Task<Stream> Handle(GetCurrentUserProfilePictureQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var identityUser = await identityService.GetCurrentUserAsync();
|
||||
var currentUserId = new Guid(identityUser?.Id ?? "");
|
||||
|
||||
var blobName = $"{currentUserId.ToString()}/{SubDirectoryNames.Profile}/{CommonFileNames.ProfilePicture}";
|
||||
|
||||
return await azureBlobStorageService.DownloadFileAsync(ContainerNames.Users, blobName);
|
||||
|
||||
return await azureBlobStorageService.DownloadFileAsync(
|
||||
ContainerNames.Users,
|
||||
$"{identityUser.Id.ToString()}/{SubDirectoryNames.Profile}/{CommonFileNames.ProfilePicture}",
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,19 @@
|
||||
using Hutopy.Application.Users.Models;
|
||||
|
||||
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; }
|
||||
public string UserName { get; init; } = string.Empty;
|
||||
public string Occupation { get; init; } = string.Empty;
|
||||
public string Email { get; init; } = string.Empty;
|
||||
public string PhoneNumber { get; init; } = string.Empty;
|
||||
public string BirthDate { get; init; } = string.Empty;
|
||||
public string Country { get; init; } = string.Empty;
|
||||
public string City { get; init; } = string.Empty;
|
||||
public string Address { get; init; } = string.Empty;
|
||||
public string About { get; init; } = string.Empty;
|
||||
public string Description { get; init; } = string.Empty;
|
||||
public SocialNetworksModel SocialNetworks { get; init; } = new();
|
||||
public ProfileColorsModel ProfileColors { get; init; } = new();
|
||||
public StoredDataUrlsModel StoredDataUrls { get; init; } = new();
|
||||
public List<UserTransactionDto> UserTransactions { get; init; } = [];
|
||||
public IList<string> UserRoles { get; init; } = [];
|
||||
public string UserName { get; init; } = null!;
|
||||
public string? Alias { get; init; }
|
||||
public string? PortraitUrl { get; init; }
|
||||
public string? FirstName { get; init; }
|
||||
public string? LastName { get; init; }
|
||||
public string? Occupation { get; init; }
|
||||
public string? Email { get; init; }
|
||||
public string? PhoneNumber { get; init; }
|
||||
public string? BirthDate { get; init; }
|
||||
public string? Address { get; init; }
|
||||
public List<UserTransactionDto> UserTransactions { get; init; } = [];
|
||||
public required decimal TotalBalance { get; init; }
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
using Hutopy.Application.Common.Interfaces;
|
||||
using Hutopy.Application.Common.Models;
|
||||
|
||||
namespace Hutopy.Application.Users.Queries.GetUser;
|
||||
|
||||
public record GetUserQuery : IRequest<UserDto>
|
||||
{
|
||||
public string? UserId { get; set; } = string.Empty;
|
||||
public string? UserName { get; set; } = string.Empty;
|
||||
};
|
||||
|
||||
public class GetUserQueryHandler(
|
||||
IIdentityService identityService
|
||||
)
|
||||
: IRequestHandler<GetUserQuery, UserDto>
|
||||
{
|
||||
public async Task<UserDto> Handle(GetUserQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
UserModel? identityUser = null;
|
||||
|
||||
if (request.UserId != string.Empty)
|
||||
{
|
||||
identityUser = await identityService.FindUserByIdAsync(request.UserId);
|
||||
|
||||
}
|
||||
|
||||
if (request.UserName != string.Empty)
|
||||
{
|
||||
identityUser = await identityService.GetUserByUserNameAsync(request.UserName);
|
||||
}
|
||||
|
||||
var user = new UserDto
|
||||
{
|
||||
Id = identityUser?.Id ?? string.Empty,
|
||||
FirstName = identityUser?.FirstName ?? string.Empty,
|
||||
LastName = identityUser?.LastName ?? string.Empty,
|
||||
UserName = identityUser?.UserName ?? string.Empty,
|
||||
Occupation = identityUser?.Occupation ?? string.Empty,
|
||||
SocialNetworks = identityUser?.SocialNetworks ?? new(),
|
||||
ProfileColors = identityUser?.ProfileColors ?? new(),
|
||||
StoredDataUrls = identityUser?.StoredDataUrls ?? new(),
|
||||
};
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
23
src/Application/Users/Queries/GetUser/GetUserById.cs
Normal file
23
src/Application/Users/Queries/GetUser/GetUserById.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Hutopy.Application.Common.Interfaces;
|
||||
|
||||
namespace Hutopy.Application.Users.Queries.GetUser;
|
||||
|
||||
public record GetUserByIdQuery : IRequest<UserDto>
|
||||
{
|
||||
public required string UserId { get; init; }
|
||||
}
|
||||
|
||||
public class GetUserByIdHandler(
|
||||
IIdentityService identityService
|
||||
)
|
||||
: IRequestHandler<GetUserByIdQuery, UserDto>
|
||||
{
|
||||
public async Task<UserDto> Handle(GetUserByIdQuery query, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await identityService.FindUserByIdAsync(query.UserId);
|
||||
|
||||
if (user is null) throw new InvalidOperationException();
|
||||
|
||||
return user.ToDto();
|
||||
}
|
||||
}
|
||||
23
src/Application/Users/Queries/GetUser/GetUserByUserName.cs
Normal file
23
src/Application/Users/Queries/GetUser/GetUserByUserName.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Hutopy.Application.Common.Interfaces;
|
||||
|
||||
namespace Hutopy.Application.Users.Queries.GetUser;
|
||||
|
||||
public record GetUserByUserNameQuery : IRequest<UserDto>
|
||||
{
|
||||
public required string UserName { get; init; }
|
||||
};
|
||||
|
||||
public class GetUserByUserNameQueryHandler(
|
||||
IIdentityService identityService
|
||||
)
|
||||
: IRequestHandler<GetUserByUserNameQuery, UserDto>
|
||||
{
|
||||
public async Task<UserDto> Handle(GetUserByUserNameQuery query, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await identityService.GetUserByUserNameAsync(query.UserName);
|
||||
|
||||
if (user is null) throw new InvalidOperationException();
|
||||
|
||||
return user.ToDto();
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,25 @@
|
||||
using Hutopy.Application.Users.Models;
|
||||
using Hutopy.Application.Common.Models;
|
||||
|
||||
namespace Hutopy.Application.Users.Queries.GetUser;
|
||||
|
||||
public class UserDto
|
||||
{
|
||||
public required string Id { get; init; }
|
||||
public required string FirstName { get; init; }
|
||||
public required string LastName { get; init; }
|
||||
public string CreatorAlias { get; set; }
|
||||
public required string UserName { get; init; } = String.Empty;
|
||||
public required string Occupation { get; init; } = String.Empty;
|
||||
|
||||
public SocialNetworksModel SocialNetworks { get; init; } = new();
|
||||
public ProfileColorsModel ProfileColors { get; init; } = new();
|
||||
public StoredDataUrlsModel StoredDataUrls { get; init; } = new();
|
||||
public required Guid Id { get; init; }
|
||||
public required string UserName { get; init; }
|
||||
public string? FirstName { get; init; }
|
||||
public string? LastName { get; init; }
|
||||
public string? Occupation { get; init; }
|
||||
}
|
||||
|
||||
public static class UserDtoExtensions
|
||||
{
|
||||
public static UserDto ToDto(this UserModel model) =>
|
||||
new()
|
||||
{
|
||||
Id = model.Id,
|
||||
FirstName = model.FirstName,
|
||||
LastName = model.LastName,
|
||||
UserName = model.UserName,
|
||||
Occupation = model.Occupation
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user