using MediatR; using ColaFlow.Modules.ProjectManagement.Application.DTOs; using ColaFlow.Modules.ProjectManagement.Application.Common.Interfaces; using ColaFlow.Modules.ProjectManagement.Domain.Repositories; using ColaFlow.Modules.ProjectManagement.Domain.ValueObjects; using ColaFlow.Modules.ProjectManagement.Domain.Exceptions; using ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate; namespace ColaFlow.Modules.ProjectManagement.Application.Queries.GetTaskById; /// /// Handler for GetTaskByIdQuery /// public sealed class GetTaskByIdQueryHandler( IProjectRepository projectRepository, ITenantContext tenantContext) : IRequestHandler { private readonly IProjectRepository _projectRepository = projectRepository ?? throw new ArgumentNullException(nameof(projectRepository)); private readonly ITenantContext _tenantContext = tenantContext ?? throw new ArgumentNullException(nameof(tenantContext)); public async Task Handle(GetTaskByIdQuery request, CancellationToken cancellationToken) { // Get current tenant ID (Defense in Depth - Layer 2) var currentTenantId = _tenantContext.GetCurrentTenantId(); // Use read-only method for query (AsNoTracking for better performance) var taskId = TaskId.From(request.TaskId); var task = await _projectRepository.GetTaskByIdReadOnlyAsync(taskId, cancellationToken); if (task == null) throw new NotFoundException("Task", request.TaskId); // CRITICAL SECURITY: Explicit TenantId validation (Defense in Depth) if (task.TenantId.Value != currentTenantId) throw new NotFoundException("Task", request.TaskId); // Map to DTO return new TaskDto { Id = task.Id.Value, Title = task.Title, Description = task.Description, StoryId = task.StoryId.Value, Status = task.Status.Name, Priority = task.Priority.Name, AssigneeId = task.AssigneeId?.Value, EstimatedHours = task.EstimatedHours, ActualHours = task.ActualHours, CreatedBy = task.CreatedBy.Value, CreatedAt = task.CreatedAt, UpdatedAt = task.UpdatedAt }; } }