Implemented complete Sprint data access layer: - Extended IProjectRepository with Sprint operations - Created SprintConfiguration for EF Core mapping - Added Sprint DbSet and multi-tenant query filter to PMDbContext - Implemented 4 Sprint repository methods (Get, GetByProject, GetActive, GetProjectWithSprint) - Created EF Core migration for Sprints table with JSONB TaskIds column - Multi-tenant isolation enforced via Global Query Filter Database schema: - Sprints table with indexes on (TenantId, ProjectId), (TenantId, Status), StartDate, EndDate - TaskIds stored as JSONB array for performance Story 3 Task 2/6 completed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
75 lines
2.6 KiB
C#
75 lines
2.6 KiB
C#
using System.Reflection;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate;
|
|
using ColaFlow.Modules.ProjectManagement.Domain.Entities;
|
|
using ColaFlow.Modules.ProjectManagement.Domain.ValueObjects;
|
|
|
|
namespace ColaFlow.Modules.ProjectManagement.Infrastructure.Persistence;
|
|
|
|
/// <summary>
|
|
/// Project Management Module DbContext
|
|
/// </summary>
|
|
public class PMDbContext : DbContext
|
|
{
|
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
|
|
|
public PMDbContext(DbContextOptions<PMDbContext> options, IHttpContextAccessor httpContextAccessor)
|
|
: base(options)
|
|
{
|
|
_httpContextAccessor = httpContextAccessor;
|
|
}
|
|
|
|
public DbSet<Project> Projects => Set<Project>();
|
|
public DbSet<Epic> Epics => Set<Epic>();
|
|
public DbSet<Story> Stories => Set<Story>();
|
|
public DbSet<WorkTask> Tasks => Set<WorkTask>();
|
|
public DbSet<Sprint> Sprints => Set<Sprint>();
|
|
public DbSet<AuditLog> AuditLogs => Set<AuditLog>();
|
|
|
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
{
|
|
base.OnModelCreating(modelBuilder);
|
|
|
|
// Set default schema for this module (must be before configurations)
|
|
modelBuilder.HasDefaultSchema("project_management");
|
|
|
|
// Apply all entity configurations from this assembly
|
|
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
|
|
|
|
// Multi-tenant Global Query Filters
|
|
modelBuilder.Entity<Project>().HasQueryFilter(p =>
|
|
p.TenantId == GetCurrentTenantId());
|
|
|
|
modelBuilder.Entity<Epic>().HasQueryFilter(e =>
|
|
e.TenantId == GetCurrentTenantId());
|
|
|
|
modelBuilder.Entity<Story>().HasQueryFilter(s =>
|
|
s.TenantId == GetCurrentTenantId());
|
|
|
|
modelBuilder.Entity<WorkTask>().HasQueryFilter(t =>
|
|
t.TenantId == GetCurrentTenantId());
|
|
|
|
modelBuilder.Entity<Sprint>().HasQueryFilter(s =>
|
|
s.TenantId == GetCurrentTenantId());
|
|
|
|
modelBuilder.Entity<AuditLog>().HasQueryFilter(a =>
|
|
a.TenantId == GetCurrentTenantId());
|
|
}
|
|
|
|
private TenantId GetCurrentTenantId()
|
|
{
|
|
var tenantIdClaim = _httpContextAccessor?.HttpContext?.User
|
|
.FindFirst("tenant_id")?.Value;
|
|
|
|
if (Guid.TryParse(tenantIdClaim, out var tenantId) && tenantId != Guid.Empty)
|
|
{
|
|
return TenantId.From(tenantId);
|
|
}
|
|
|
|
// Return a dummy value for queries outside HTTP context (e.g., migrations)
|
|
// These will return no results due to the filter
|
|
return TenantId.From(Guid.Empty);
|
|
}
|
|
}
|