shoc-backend/SeaHaven.DataServices/Implementation/TeamPermissionOverrideDataService.cs
Alexandre Brandizzi 8a4de283dc Merge remote-tracking branch 'origin/dev' into feat/ab/sh-326-team-member
# Conflicts:
#	Api.SeaHavenIndustries.Tests/TeamMemberServiceTests.cs
#	SeaHaven.DataServices/Implementation/TeamPermissionOverrideDataService.cs
2026-09-17 14:06:12 -03:00

124 lines
4.1 KiB
C#

using Data.SeaHavenIndustries;
using Data.SeaHavenIndustries.Enums;
using Microsoft.EntityFrameworkCore;
using SeaHaven.DataServices.Dto;
using SeaHaven.DataServices.Interfaces;
namespace SeaHaven.DataServices.Implementation;
public sealed class TeamPermissionOverrideDataService : ITeamPermissionOverrideDataService
{
private readonly ApplicationDbContext _context;
public TeamPermissionOverrideDataService(ApplicationDbContext context)
{
_context = context;
}
public async Task<TeamPermissionUserData?> GetUserAsync(
string userId,
CancellationToken cancellationToken)
{
var userExists = await _context.Users
.AsNoTracking()
.AnyAsync(user => user.Id == userId, cancellationToken);
if (!userExists)
return null;
var roleName = await (
from userRole in _context.UserRoles.AsNoTracking()
join role in _context.Roles.AsNoTracking()
on userRole.RoleId equals role.Id
where userRole.UserId == userId
select role.Name)
.FirstOrDefaultAsync(cancellationToken);
var overrides = await _context.UserPermissionOverrides
.AsNoTracking()
.Where(permission => permission.UserId == userId)
.ToDictionaryAsync(
permission => permission.PermissionKey,
permission => permission.State,
StringComparer.OrdinalIgnoreCase,
cancellationToken);
return new TeamPermissionUserData
{
UserId = userId,
RoleName = roleName,
Overrides = overrides
};
}
public Task SetOverrideAsync(
string userId,
string permissionKey,
UserPermissionState state,
CancellationToken cancellationToken)
{
return SetOverrideCoreAsync(userId, permissionKey, state, cancellationToken);
}
public async Task SetOverridesAsync(
string userId,
IReadOnlyDictionary<string, UserPermissionState> overrides,
CancellationToken cancellationToken)
{
foreach (var pair in overrides)
await SetOverrideCoreAsync(userId, pair.Key, pair.Value, cancellationToken);
}
private async Task SetOverrideCoreAsync(
string userId,
string permissionKey,
UserPermissionState state,
CancellationToken cancellationToken)
{
var existing = await _context.UserPermissionOverrides
.SingleOrDefaultAsync(
permission => permission.UserId == userId
&& permission.PermissionKey == permissionKey,
cancellationToken);
if (existing is not null)
{
existing.State = state;
await _context.SaveChangesAsync(cancellationToken);
return;
}
var addition = new UserPermissionOverride
{
UserId = userId,
PermissionKey = permissionKey,
State = state
};
await _context.UserPermissionOverrides.AddAsync(addition, cancellationToken);
try
{
await _context.SaveChangesAsync(cancellationToken);
}
catch (DbUpdateException)
{
// A concurrent write (create or PUT) inserted the same composite key
// between our existence check and this save. Converge on the caller's intent.
_context.Entry(addition).State = EntityState.Detached;
var winner = await _context.UserPermissionOverrides
.SingleAsync(
permission => permission.UserId == userId
&& permission.PermissionKey == permissionKey,
cancellationToken);
winner.State = state;
await _context.SaveChangesAsync(cancellationToken);
}
}
public async Task ClearOverridesAsync(string userId, CancellationToken cancellationToken)
{
await _context.UserPermissionOverrides
.Where(item => item.UserId == userId)
.ExecuteDeleteAsync(cancellationToken);
}
}