3 Commits
7 changed files with 417 additions and 90 deletions
@@ -153,5 +153,5 @@ public interface IProjectService
Task<Result<SiteVisit[]>> GetSiteVisitsAsync(string projectIdHash, CancellationToken cancellationToken = default);
[Log]
Task<Result<SiteVisit>> GetSiteVisitAsync(string SiteVisitIdHash, CancellationToken cancellationToken = default);
Task<Result<SiteVisit>> GetSiteVisitAsync(string siteVisitIdHash, CancellationToken cancellationToken = default);
}
@@ -215,26 +215,102 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut
return Result.Ok(cached.FromJson<Programme[]>());
}));
public Task<Result<Tenant>> GetTenantAsync(string tenantIdHash, CancellationToken cancellationToken = default)
public async Task<Result<Tenant>> GetTenantAsync(string tenantIdHash, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<Tenant>>>)(async () =>
{
throw new NotImplementedException();
if (!await featureManager.IsEnabledAsync(ServiceCachingFeature))
return await service.GetTenantAsync(tenantIdHash, cancellationToken);
var cacheKey = HashService.ToMd5Hash($"gettenant-{tenantIdHash}").Value;
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
if (string.IsNullOrWhiteSpace(cached))
{
var result = await service.GetTenantAsync(tenantIdHash, cancellationToken);
if (result.IsFailed) return result;
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
return result.Value;
}
public Task<Result<TenantOrganisation>> GetTenantOrganisationAsync(string tenantOrganisationIdHash, CancellationToken cancellationToken = default)
return Result.Ok(cached.FromJson<Tenant>());
}));
public async Task<Result<TenantOrganisation>> GetTenantOrganisationAsync(string tenantOrganisationIdHash, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<TenantOrganisation>>>)(async () =>
{
throw new NotImplementedException();
if (!await featureManager.IsEnabledAsync(ServiceCachingFeature))
return await service.GetTenantOrganisationAsync(tenantOrganisationIdHash, cancellationToken);
var cacheKey = HashService.ToMd5Hash($"gettenantorganisation-{tenantOrganisationIdHash}").Value;
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
if (string.IsNullOrWhiteSpace(cached))
{
var result = await service.GetTenantOrganisationAsync(tenantOrganisationIdHash, cancellationToken);
if (result.IsFailed) return result;
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
return result.Value;
}
public Task<Result<TenantOrganisation[]>> GetTenantOrganisationsAsync(CancellationToken cancellationToken = default)
return Result.Ok(cached.FromJson<TenantOrganisation>());
}));
public async Task<Result<TenantOrganisation[]>> GetTenantOrganisationsAsync(CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<TenantOrganisation[]>>>)(async () =>
{
throw new NotImplementedException();
if (!await featureManager.IsEnabledAsync(ServiceCachingFeature))
return await service.GetTenantOrganisationsAsync(cancellationToken);
var cacheKey = HashService.ToMd5Hash($"gettenantorganisations").Value;
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
if (string.IsNullOrWhiteSpace(cached))
{
var result = await service.GetTenantOrganisationsAsync(cancellationToken);
if (result.IsFailed) return result;
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
return result.Value;
}
public Task<Result<Tenant[]>> GetTenantsAsync(CancellationToken cancellationToken = default)
return Result.Ok(cached.FromJson<TenantOrganisation[]>());
}));
public async Task<Result<Tenant[]>> GetTenantsAsync(CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<Tenant[]>>>)(async () =>
{
throw new NotImplementedException();
if (!await featureManager.IsEnabledAsync(ServiceCachingFeature))
return await service.GetTenantsAsync(cancellationToken);
var cacheKey = HashService.ToMd5Hash($"gettenants").Value;
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
if (string.IsNullOrWhiteSpace(cached))
{
var result = await service.GetTenantsAsync(cancellationToken);
if (result.IsFailed) return result;
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
return result.Value;
}
return Result.Ok(cached.FromJson<Tenant[]>());
}));
public async Task<Result<Organisation>> UpdateOrganisationAsync(UpdateOrganisationRequest request, CancellationToken cancellationToken = default) =>
await service.UpdateOrganisationAsync(request, cancellationToken);
@@ -56,13 +56,14 @@ public sealed class GovernanceService(IDbContextFactory<ApplicationDbContext> co
{
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var organisations = await context.Organisations.AsNoTracking()
.OrderBy(o => o.Name)
var organisations = await context.TenantOrganisations.AsNoTracking()
.Include(i => i.Organisation)
.OrderBy(o => o.Organisation!.Name)
.Skip((pagination.Page - 1) * pagination.PageSize)
.Take(pagination.PageSize)
.ToListAsync(cancellationToken);
return Result.Ok(organisations.Select(o => o.Map()).ToArray());
return Result.Ok(organisations.Select(o => o.Organisation!.Map()).ToArray());
}));
public async Task<Result<Organisation>> GetOrganisationAsync(string organisationIdHash, CancellationToken cancellationToken = default) =>
@@ -76,11 +77,12 @@ public sealed class GovernanceService(IDbContextFactory<ApplicationDbContext> co
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var organisation = await context.Organisations.AsNoTracking()
.FirstOrDefaultAsync(o => o.Id == decodeResult.Value, cancellationToken);
var tenantOrganisation = await context.TenantOrganisations.AsNoTracking()
.Include(i => i.OrganisationId)
.FirstOrDefaultAsync(o => o.OrganisationId == decodeResult.Value, cancellationToken);
return Result.FailIf(organisation is null, NotFoundError.New)
.ToResult(organisation!.Map());
return Result.FailIf(tenantOrganisation is null, NotFoundError.New)
.ToResult(tenantOrganisation!.Organisation!.Map());
}));
public async Task<Result<Instrument[]>> GetInstrumentsAsync(string programmeIdHash, CancellationToken cancellationToken = default) =>
@@ -86,46 +86,198 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach
return Result.Ok(cached.FromJson<Domain.Models.Activity>());
}));
public Task<Result<Beneficiary[]>> GetBeneficiariesAsync(string projectIdHash, CancellationToken cancellationToken = default)
public async Task<Result<Beneficiary[]>> GetBeneficiariesAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<Beneficiary[]>>>)(async () =>
{
throw new NotImplementedException();
if (!await featureManager.IsEnabledAsync(ServiceCachingFeature))
return await service.GetBeneficiariesAsync(projectIdHash, cancellationToken);
var cacheKey = HashService.ToMd5Hash($"getbeneficiaries-{projectIdHash}").Value;
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
if (string.IsNullOrWhiteSpace(cached))
{
var result = await service.GetBeneficiariesAsync(projectIdHash, cancellationToken);
if (result.IsFailed) return result;
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
return result.Value;
}
public Task<Result<Beneficiary>> GetBeneficiaryAsync(string beneficiaryIdHash, CancellationToken cancellationToken = default)
return Result.Ok(cached.FromJson<Beneficiary[]>());
}));
public async Task<Result<Beneficiary>> GetBeneficiaryAsync(string beneficiaryIdHash, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<Beneficiary>>>)(async () =>
{
throw new NotImplementedException();
if (!await featureManager.IsEnabledAsync(ServiceCachingFeature))
return await service.GetBeneficiaryAsync(beneficiaryIdHash, cancellationToken);
var cacheKey = HashService.ToMd5Hash($"getbeneficiary-{beneficiaryIdHash}").Value;
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
if (string.IsNullOrWhiteSpace(cached))
{
var result = await service.GetBeneficiaryAsync(beneficiaryIdHash, cancellationToken);
if (result.IsFailed) return result;
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
return result.Value;
}
public Task<Result<ChangeRequest>> GetChangeRequestAsync(string changeRequestIdHash, CancellationToken cancellationToken = default)
return Result.Ok(cached.FromJson<Beneficiary>());
}));
public async Task<Result<ChangeRequest>> GetChangeRequestAsync(string changeRequestIdHash, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<ChangeRequest>>>)(async () =>
{
throw new NotImplementedException();
if (!await featureManager.IsEnabledAsync(ServiceCachingFeature))
return await service.GetChangeRequestAsync(changeRequestIdHash, cancellationToken);
var cacheKey = HashService.ToMd5Hash($"getchangerequest-{changeRequestIdHash}").Value;
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
if (string.IsNullOrWhiteSpace(cached))
{
var result = await service.GetChangeRequestAsync(changeRequestIdHash, cancellationToken);
if (result.IsFailed) return result;
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
return result.Value;
}
public Task<Result<ChangeRequest[]>> GetChangeRequestsAsync(string projectIdHash, CancellationToken cancellationToken = default)
return Result.Ok(cached.FromJson<ChangeRequest>());
}));
public async Task<Result<ChangeRequest[]>> GetChangeRequestsAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<ChangeRequest[]>>>)(async () =>
{
throw new NotImplementedException();
if (!await featureManager.IsEnabledAsync(ServiceCachingFeature))
return await service.GetChangeRequestsAsync(projectIdHash, cancellationToken);
var cacheKey = HashService.ToMd5Hash($"getchangerequests-{projectIdHash}").Value;
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
if (string.IsNullOrWhiteSpace(cached))
{
var result = await service.GetChangeRequestsAsync(projectIdHash, cancellationToken);
if (result.IsFailed) return result;
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
return result.Value;
}
public Task<Result<ComplianceItem>> GetComplianceItemAsync(string complianceItemIdHash, CancellationToken cancellationToken = default)
return Result.Ok(cached.FromJson<ChangeRequest[]>());
}));
public async Task<Result<ComplianceItem>> GetComplianceItemAsync(string complianceItemIdHash, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<ComplianceItem>>>)(async () =>
{
throw new NotImplementedException();
if (!await featureManager.IsEnabledAsync(ServiceCachingFeature))
return await service.GetComplianceItemAsync(complianceItemIdHash, cancellationToken);
var cacheKey = HashService.ToMd5Hash($"getcomplianceitem-{complianceItemIdHash}").Value;
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
if (string.IsNullOrWhiteSpace(cached))
{
var result = await service.GetComplianceItemAsync(complianceItemIdHash, cancellationToken);
if (result.IsFailed) return result;
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
return result.Value;
}
public Task<Result<ComplianceItem[]>> GetComplianceItemsAsync(string projectIdHash, CancellationToken cancellationToken = default)
return Result.Ok(cached.FromJson<ComplianceItem>());
}));
public async Task<Result<ComplianceItem[]>> GetComplianceItemsAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<ComplianceItem[]>>>)(async () =>
{
throw new NotImplementedException();
if (!await featureManager.IsEnabledAsync(ServiceCachingFeature))
return await service.GetComplianceItemsAsync(projectIdHash, cancellationToken);
var cacheKey = HashService.ToMd5Hash($"getcomplianceitems-{projectIdHash}").Value;
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
if (string.IsNullOrWhiteSpace(cached))
{
var result = await service.GetComplianceItemsAsync(projectIdHash, cancellationToken);
if (result.IsFailed) return result;
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
return result.Value;
}
public Task<Result<Issue>> GetIssueAsync(string issueIdHash, CancellationToken cancellationToken = default)
return Result.Ok(cached.FromJson<ComplianceItem[]>());
}));
public async Task<Result<Issue>> GetIssueAsync(string issueIdHash, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<Issue>>>)(async () =>
{
throw new NotImplementedException();
if (!await featureManager.IsEnabledAsync(ServiceCachingFeature))
return await service.GetIssueAsync(issueIdHash, cancellationToken);
var cacheKey = HashService.ToMd5Hash($"getissue-{issueIdHash}").Value;
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
if (string.IsNullOrWhiteSpace(cached))
{
var result = await service.GetIssueAsync(issueIdHash, cancellationToken);
if (result.IsFailed) return result;
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
return result.Value;
}
public Task<Result<Issue[]>> GetIssuesAsync(string projectIdHash, CancellationToken cancellationToken = default)
return Result.Ok(cached.FromJson<Issue>());
}));
public async Task<Result<Issue[]>> GetIssuesAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<Issue[]>>>)(async () =>
{
throw new NotImplementedException();
if (!await featureManager.IsEnabledAsync(ServiceCachingFeature))
return await service.GetIssuesAsync(projectIdHash, cancellationToken);
var cacheKey = HashService.ToMd5Hash($"getissues-{projectIdHash}").Value;
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
if (string.IsNullOrWhiteSpace(cached))
{
var result = await service.GetIssuesAsync(projectIdHash, cancellationToken);
if (result.IsFailed) return result;
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
return result.Value;
}
return Result.Ok(cached.FromJson<Issue[]>());
}));
public async Task<Result<Milestone>> GetMilestoneAsync(string milestoneIdHash, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<Milestone>>>)(async () =>
{
@@ -174,16 +326,54 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach
return Result.Ok(cached.FromJson<Milestone[]>());
}));
public Task<Result<Outcome>> GetOutcomeAsync(string outcomeIdHash, CancellationToken cancellationToken = default)
public async Task<Result<Outcome>> GetOutcomeAsync(string outcomeIdHash, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<Outcome>>>)(async () =>
{
throw new NotImplementedException();
if (!await featureManager.IsEnabledAsync(ServiceCachingFeature))
return await service.GetOutcomeAsync(outcomeIdHash, cancellationToken);
var cacheKey = HashService.ToMd5Hash($"getoutcome-{outcomeIdHash}").Value;
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
if (string.IsNullOrWhiteSpace(cached))
{
var result = await service.GetOutcomeAsync(outcomeIdHash, cancellationToken);
if (result.IsFailed) return result;
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
return result.Value;
}
public Task<Result<Outcome[]>> GetOutcomesAsync(string beneficiaryIdHash, CancellationToken cancellationToken = default)
return Result.Ok(cached.FromJson<Outcome>());
}));
public async Task<Result<Outcome[]>> GetOutcomesAsync(string beneficiaryIdHash, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<Outcome[]>>>)(async () =>
{
throw new NotImplementedException();
if (!await featureManager.IsEnabledAsync(ServiceCachingFeature))
return await service.GetOutcomesAsync(beneficiaryIdHash, cancellationToken);
var cacheKey = HashService.ToMd5Hash($"getoutcomes-{beneficiaryIdHash}").Value;
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
if (string.IsNullOrWhiteSpace(cached))
{
var result = await service.GetOutcomesAsync(beneficiaryIdHash, cancellationToken);
if (result.IsFailed) return result;
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
return result.Value;
}
return Result.Ok(cached.FromJson<Outcome[]>());
}));
public async Task<Result<Programme>> GetProgrammeAsync(string programmeIdHash, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<Programme>>>)(async () =>
{
@@ -376,16 +566,54 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach
return Result.Ok(cached.FromJson<Project[]>());
}));
public Task<Result<SiteVisit>> GetSiteVisitAsync(string SiteVisitIdHash, CancellationToken cancellationToken = default)
public async Task<Result<SiteVisit>> GetSiteVisitAsync(string siteVisitIdHash, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<SiteVisit>>>)(async () =>
{
throw new NotImplementedException();
if (!await featureManager.IsEnabledAsync(ServiceCachingFeature))
return await service.GetSiteVisitAsync(siteVisitIdHash, cancellationToken);
var cacheKey = HashService.ToMd5Hash($"getsitevisit-{siteVisitIdHash}").Value;
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
if (string.IsNullOrWhiteSpace(cached))
{
var result = await service.GetSiteVisitAsync(siteVisitIdHash, cancellationToken);
if (result.IsFailed) return result;
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
return result.Value;
}
public Task<Result<SiteVisit[]>> GetSiteVisitsAsync(string projectIdHash, CancellationToken cancellationToken = default)
return Result.Ok(cached.FromJson<SiteVisit>());
}));
public async Task<Result<SiteVisit[]>> GetSiteVisitsAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<SiteVisit[]>>>)(async () =>
{
throw new NotImplementedException();
if (!await featureManager.IsEnabledAsync(ServiceCachingFeature))
return await service.GetSiteVisitsAsync(projectIdHash, cancellationToken);
var cacheKey = HashService.ToMd5Hash($"getsitevisits-{projectIdHash}").Value;
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
if (string.IsNullOrWhiteSpace(cached))
{
var result = await service.GetSiteVisitsAsync(projectIdHash, cancellationToken);
if (result.IsFailed) return result;
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
return result.Value;
}
return Result.Ok(cached.FromJson<SiteVisit[]>());
}));
public async Task<Result> UpdateBeneficiaryItemAsync(UpdateBeneficiaryRequest request, Guid userId, CancellationToken cancellationToken = default) =>
await service.UpdateBeneficiaryItemAsync(request, userId, cancellationToken);
@@ -498,7 +498,7 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> conte
throw new NotImplementedException();
}
public Task<Result<SiteVisit>> GetSiteVisitAsync(string SiteVisitIdHash, CancellationToken cancellationToken = default)
public Task<Result<SiteVisit>> GetSiteVisitAsync(string siteVisitIdHash, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
@@ -12,6 +12,7 @@ public sealed class IdentityService(IDbContextFactory<ApplicationDbContext> cont
{
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
// TODO: Ensure TenantUser status is checked
var userExistsAndActive = await context.Users
.AnyAsync(u => u.Id == userId && u.Status == ActivityStatus.Active, cancellationToken);
@@ -1,5 +1,6 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Domain;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
@@ -7,16 +8,19 @@ using PostFundManagement.Infrastructure.Database;
namespace PostFundManagement.Application.Services.Users;
public sealed class UserService(IDbContextFactory<ApplicationDbContext> contextFactory) : IUserService
public sealed class UserService(IDbContextFactory<ApplicationDbContext> contextFactory, ITenantContextService tenantContext) : IUserService
{
public async Task<Result> SyncUserAsync(Guid userId, string email, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result>>)(async () =>
{
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var user = await context.Users.FirstOrDefaultAsync(u => u.Id == userId, cancellationToken);
var tenantUser = await context.TenantUsers
.Include(i => i.User)
.FirstOrDefaultAsync(u => u.UserId == userId, cancellationToken);
if (user is null)
if (tenantUser is null)
{
context.Users.Add(new Domain.Entities.User
{
Id = userId,
@@ -24,12 +28,22 @@ public sealed class UserService(IDbContextFactory<ApplicationDbContext> contextF
Status = ActivityStatus.Active,
LastLoginAt = DateTime.UtcNow
});
context.TenantUsers.Add(new Domain.Entities.TenantUser
{
CreatedAt = DateTime.UtcNow,
CreatedBy = userId,
Status = ActivityStatus.Active,
TenantId = tenantContext.TenantId,
UserId = userId
});
}
else
{
user!.Email = email;
user.LastLoginAt = DateTime.UtcNow;
tenantUser.User!.Email = email;
tenantUser.User.LastLoginAt = DateTime.UtcNow;
context.Users.Update(user);
context.Users.Update(tenantUser.User);
}
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New);
@@ -40,19 +54,19 @@ public sealed class UserService(IDbContextFactory<ApplicationDbContext> contextF
{
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var query = context.Users.AsNoTracking();
var query = context.TenantUsers.Include(i => i.User).AsNoTracking();
if (request.Status.HasValue)
query = query.Where(u => u.Status == request.Status.Value);
if (!string.IsNullOrWhiteSpace(request.Email))
query = query.Where(u => u.Email != null && u.Email.Contains(request.Email));
query = query.Where(u => u.User!.Email != null && u.User!.Email.Contains(request.Email));
if (!string.IsNullOrWhiteSpace(request.SortBy))
query = request.SortBy.ToLowerInvariant() switch
{
"email" => request.SortDescending ? query.OrderByDescending(u => u.Email) : query.OrderBy(u => u.Email),
"lastloginat" => request.SortDescending ? query.OrderByDescending(u => u.LastLoginAt) : query.OrderBy(u => u.LastLoginAt),
"email" => request.SortDescending ? query.OrderByDescending(u => u.User!.Email) : query.OrderBy(u => u.User!.Email),
"lastloginat" => request.SortDescending ? query.OrderByDescending(u => u.User!.LastLoginAt) : query.OrderBy(u => u.User!.LastLoginAt),
"status" => request.SortDescending ? query.OrderByDescending(u => u.Status) : query.OrderBy(u => u.Status),
_ => request.SortDescending ? query.OrderByDescending(u => u.Id) : query.OrderBy(u => u.Id)
};
@@ -62,7 +76,7 @@ public sealed class UserService(IDbContextFactory<ApplicationDbContext> contextF
var users = await query.Skip((pagination.Page - 1) * pagination.PageSize)
.Take(pagination.PageSize).ToListAsync(cancellationToken);
return Result.Ok(users.Select(u => u.Map()).ToArray());
return Result.Ok(users.Select(u => u.User!.Map()).ToArray());
}));
public async Task<Result> UpdateUserStatusAsync(Guid userId, ActivityStatus status, CancellationToken cancellationToken = default) =>
@@ -70,8 +84,13 @@ public sealed class UserService(IDbContextFactory<ApplicationDbContext> contextF
{
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var changedTotal = await context.Users
.Where(f => f.Id == userId)
var changedTotal = await context.TenantUsers
.Include(i => i.User)
.Where(f => f.UserId == userId)
.ExecuteUpdateAsync(f => f
.SetProperty(u => u.Status, status), cancellationToken);
changedTotal += await context.Users.Where(f => f.Id == userId)
.ExecuteUpdateAsync(f => f.SetProperty(u => u.Status, status), cancellationToken);
return Result.OkIf(changedTotal > 0, UpdateError.New);
@@ -82,10 +101,11 @@ public sealed class UserService(IDbContextFactory<ApplicationDbContext> contextF
{
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var user = await context.Users.AsNoTracking()
.FirstOrDefaultAsync(f => f.Id == userId, cancellationToken);
var tenantUser = await context.TenantUsers.AsNoTracking()
.Include(i => i.User)
.FirstOrDefaultAsync(f => f.UserId == userId, cancellationToken);
return Result.FailIf(user == null, NotFoundError.New)
.ToResult(user?.Map()!);
return Result.FailIf(tenantUser == null, NotFoundError.New)
.ToResult(tenantUser!.User?.Map()!);
}));
}