Files
tiku-backend.net/Tiku.Infrastructure/QuestionBanks/QuestionReferenceService.cs

100 lines
3.9 KiB
C#

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Tiku.Application.QuestionBanks;
using Tiku.Application.Security;
using Tiku.Domain.Content;
using Tiku.Domain.QuestionBanks;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Persistence;
namespace Tiku.Infrastructure.QuestionBanks;
public sealed class QuestionReferenceService(
TikuDbContext dbContext,
IPublicQuestionAccessPolicy accessPolicy,
ITenantExecutionScope tenantExecutionScope) : IQuestionReferenceService
{
public async Task<TenantQuestionReference> ResolveAsync(
Guid tenantId,
Guid? userId,
QuestionLocator locator,
CancellationToken cancellationToken = default)
{
var ownerTenantId = locator.Source switch
{
QuestionSource.Tenant => await ResolveTenantQuestionAsync(tenantId, locator.QuestionId, cancellationToken),
QuestionSource.Platform => await ResolvePlatformQuestionAsync(tenantId, locator.QuestionId, cancellationToken),
_ => throw new QuestionLocatorException("question_source_invalid", "Question source is invalid.")
};
var existing = await dbContext.TenantQuestionReferences.SingleOrDefaultAsync(
reference =>
reference.TenantId == tenantId &&
reference.QuestionOwnerTenantId == ownerTenantId &&
reference.QuestionId == locator.QuestionId,
cancellationToken);
if (existing is not null)
{
return existing;
}
var reference = new TenantQuestionReference
{
TenantId = tenantId,
QuestionOwnerTenantId = ownerTenantId,
QuestionId = locator.QuestionId,
Source = locator.Source,
CreatedBy = userId
};
dbContext.TenantQuestionReferences.Add(reference);
return reference;
}
private async Task<Guid> ResolveTenantQuestionAsync(
Guid tenantId,
Guid questionId,
CancellationToken cancellationToken)
{
var exists = await dbContext.Questions.AsNoTracking().AnyAsync(
question =>
question.TenantId == tenantId &&
question.Id == questionId &&
question.Status == QuestionStatus.Published,
cancellationToken);
return exists
? tenantId
: throw new QuestionLocatorException("question_not_found", "Tenant question was not found.");
}
private async Task<Guid> ResolvePlatformQuestionAsync(
Guid tenantId,
Guid questionId,
CancellationToken cancellationToken)
{
await accessPolicy.EnsureCanStartAsync(tenantId, cancellationToken);
var ownerTenantId = await tenantExecutionScope.ExecuteAsync(
new SystemScopeRequest(
tenantId, SystemScopeCallerType.PublicQuestionBank, nameof(QuestionReferenceService),
"Resolve a platform question for an entitled tenant", Guid.NewGuid().ToString("N")),
async (provider, token) =>
{
var systemDbContext = provider.GetRequiredService<TikuDbContext>();
return await systemDbContext.Questions.AsNoTracking()
.Where(question =>
question.Id == questionId &&
question.Status == QuestionStatus.Published)
.Join(
systemDbContext.Tenants.AsNoTracking().Where(tenant => tenant.Mode == TenantMode.PlatformOwned),
question => question.TenantId,
tenant => tenant.Id,
(question, tenant) => (Guid?)tenant.Id)
.SingleOrDefaultAsync(token);
},
cancellationToken);
return ownerTenantId ?? throw new QuestionLocatorException(
"question_not_found",
"Platform question was not found.");
}
}