Files
social-media/backend/src/Socialize.Api/Modules/Organizations/Handlers/GetOrganization.cs

168 lines
5.9 KiB
C#

using FastEndpoints;
using Microsoft.EntityFrameworkCore;
using Socialize.Api.Data;
using Socialize.Api.Modules.Identity.Data;
using Socialize.Api.Modules.Organizations.Data;
using Socialize.Api.Modules.Organizations.Services;
using Socialize.Api.Modules.Workspaces.Handlers;
namespace Socialize.Api.Modules.Organizations.Handlers;
internal class GetOrganizationHandler(
AppDbContext dbContext,
OrganizationAccessService organizationAccessService)
: EndpointWithoutRequest<OrganizationDto>
{
public override void Configure()
{
Get("/api/organizations/{organizationId:guid}");
Options(o => o.WithTags("Organizations"));
}
public override async Task HandleAsync(CancellationToken ct)
{
Guid organizationId = Route<Guid>("organizationId");
Organization? organization = await dbContext.Organizations
.SingleOrDefaultAsync(candidate => candidate.Id == organizationId, ct);
if (organization is null)
{
await SendNotFoundAsync(ct);
return;
}
if (!await organizationAccessService.CanAccessOrganizationAsync(User, organizationId, ct))
{
await SendForbiddenAsync(ct);
return;
}
IReadOnlyCollection<string> currentUserPermissions = await organizationAccessService.GetUserOrganizationPermissionsAsync(
User,
organizationId,
ct);
IReadOnlyCollection<OrganizationMemberDto> members = await GetMembersAsync(organizationId, ct);
IReadOnlyCollection<WorkspaceDto> workspaces = await GetWorkspacesAsync(organizationId, ct);
OrganizationUsageDto usage = await GetUsageAsync(organization, ct);
await SendOkAsync(
OrganizationDto.FromOrganization(
organization,
currentUserPermissions,
members,
workspaces,
usage),
ct);
}
private async Task<IReadOnlyCollection<OrganizationMemberDto>> GetMembersAsync(
Guid organizationId,
CancellationToken ct)
{
var rows = await dbContext.OrganizationMemberships
.Where(membership => membership.OrganizationId == organizationId)
.Join(
dbContext.Users,
membership => membership.UserId,
user => user.Id,
(membership, user) => new { Membership = membership, User = user })
.OrderBy(row => row.User.Lastname)
.ThenBy(row => row.User.Firstname)
.ThenBy(row => row.User.Email)
.ToListAsync(ct);
return rows
.Select(row => new OrganizationMemberDto(
row.User.Id,
BuildDisplayName(row.User),
row.User.Email ?? string.Empty,
row.User.PortraitUrl,
row.Membership.Role,
OrganizationPermissionRules.GetPermissionsForRole(row.Membership.Role),
row.Membership.CreatedAt))
.ToArray();
}
private async Task<IReadOnlyCollection<WorkspaceDto>> GetWorkspacesAsync(
Guid organizationId,
CancellationToken ct)
{
var workspaces = await dbContext.Workspaces
.Where(workspace => workspace.OrganizationId == organizationId)
.OrderBy(workspace => workspace.Name)
.ToListAsync(ct);
return workspaces
.Select(workspace => WorkspaceDto.FromWorkspace(workspace, []))
.ToArray();
}
private async Task<OrganizationUsageDto> GetUsageAsync(
Organization organization,
CancellationToken ct)
{
Guid[] workspaceIds = await dbContext.Workspaces
.Where(workspace => workspace.OrganizationId == organization.Id)
.Select(workspace => workspace.Id)
.ToArrayAsync(ct);
Guid[] memberUserIds = await dbContext.OrganizationMemberships
.Where(membership => membership.OrganizationId == organization.Id)
.Select(membership => membership.UserId)
.Distinct()
.ToArrayAsync(ct);
int userCount = memberUserIds
.Append(organization.OwnerUserId)
.Distinct()
.Count();
int activeContentItemCount = workspaceIds.Length == 0
? 0
: await dbContext.ContentItems
.Where(contentItem => workspaceIds.Contains(contentItem.WorkspaceId) &&
contentItem.Status != "Approved" &&
contentItem.Status != "Scheduled")
.CountAsync(ct);
OrganizationUsageLimits limits = GetUsageLimits(organization.Name);
return new OrganizationUsageDto(
limits.PlanName,
[
new OrganizationUsageItemDto("users", userCount, limits.UserLimit),
new OrganizationUsageItemDto("workspaces", workspaceIds.Length, limits.WorkspaceLimit),
new OrganizationUsageItemDto("activeContent", activeContentItemCount, limits.ActiveContentLimit),
]);
}
private static OrganizationUsageLimits GetUsageLimits(string organizationName)
{
return string.Equals(organizationName, "Northstar Agency", StringComparison.OrdinalIgnoreCase)
? new OrganizationUsageLimits("Agency", 25, 15, 250)
: new OrganizationUsageLimits("Free", 2, 1, 3);
}
private sealed record OrganizationUsageLimits(
string PlanName,
int UserLimit,
int WorkspaceLimit,
int ActiveContentLimit);
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();
}
}