Files
social-media/backend/src/Socialize.Api/Modules/Workspaces/Handlers/GetWorkspaceMembers.cs

122 lines
4.3 KiB
C#

using FastEndpoints;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims;
using Socialize.Api.Data;
using Socialize.Api.Modules.Identity.Data;
using Socialize.Api.Infrastructure.Security;
using Socialize.Api.Modules.Workspaces.Data;
namespace Socialize.Api.Modules.Workspaces.Handlers;
public record WorkspaceMemberDto(
Guid Id,
string DisplayName,
string Email,
string? PortraitUrl,
string RelationshipCategory,
IReadOnlyCollection<string> Roles);
public class GetWorkspaceMembersHandler(
AppDbContext dbContext,
AccessScopeService accessScopeService)
: EndpointWithoutRequest<IReadOnlyCollection<WorkspaceMemberDto>>
{
public override void Configure()
{
Get("/api/workspaces/{workspaceId:guid}/members");
Options(o => o.WithTags("Workspaces"));
}
public override async Task HandleAsync(CancellationToken ct)
{
Guid workspaceId = Route<Guid>("workspaceId");
if (!await accessScopeService.CanManageWorkspaceAsync(User, workspaceId, ct))
{
await SendForbiddenAsync(ct);
return;
}
Workspace? workspace = await dbContext.Workspaces
.SingleOrDefaultAsync(candidate => candidate.Id == workspaceId, ct);
if (workspace is null)
{
await SendNotFoundAsync(ct);
return;
}
string workspaceClaimValue = workspaceId.ToString();
var users = await dbContext.Users
.Where(candidate =>
dbContext.UserClaims.Any(claim =>
claim.UserId == candidate.Id &&
claim.ClaimType == KnownClaims.WorkspaceScope &&
claim.ClaimValue == workspaceClaimValue) ||
dbContext.OrganizationMemberships.Any(membership =>
membership.UserId == candidate.Id &&
membership.OrganizationId == workspace.OrganizationId) ||
candidate.Id == workspace.OwnerUserId)
.OrderBy(candidate => candidate.Lastname)
.ThenBy(candidate => candidate.Firstname)
.ThenBy(candidate => candidate.Email)
.ToListAsync(ct);
var userIds = users
.Select(candidate => candidate.Id)
.ToList();
Dictionary<Guid, IReadOnlyCollection<string>> rolesByUserId = await dbContext.UserRoles
.Where(candidate => userIds.Contains(candidate.UserId))
.Join(
dbContext.Roles,
userRole => userRole.RoleId,
role => role.Id,
(userRole, role) => new { userRole.UserId, role.Name })
.GroupBy(candidate => candidate.UserId)
.ToDictionaryAsync(
group => group.Key,
group => (IReadOnlyCollection<string>)group
.Select(candidate => candidate.Name)
.Where(name => !string.IsNullOrWhiteSpace(name))
.Cast<string>()
.OrderBy(name => name)
.ToArray(),
ct);
HashSet<Guid> organizationMemberUserIds = await dbContext.OrganizationMemberships
.Where(membership => membership.OrganizationId == workspace.OrganizationId)
.Select(membership => membership.UserId)
.ToHashSetAsync(ct);
organizationMemberUserIds.Add(workspace.OwnerUserId);
var members = users
.Select(candidate => new WorkspaceMemberDto(
candidate.Id,
BuildDisplayName(candidate),
candidate.Email ?? string.Empty,
candidate.PortraitUrl,
organizationMemberUserIds.Contains(candidate.Id) ? "Organization Member" : "External Collaborator",
rolesByUserId.GetValueOrDefault(candidate.Id) ?? Array.Empty<string>()))
.ToList();
await SendOkAsync(members, ct);
}
private static string BuildDisplayName(User user)
{
if (!string.IsNullOrWhiteSpace(user.Alias))
{
return user.Alias;
}
string fullName = $"{user.Firstname} {user.Lastname}".Trim();
if (!string.IsNullOrWhiteSpace(fullName))
{
return fullName;
}
return user.Email ?? user.UserName ?? user.Id.ToString();
}
}