This commit completes Day 15's primary objectives: 1. Database Migration - Add TenantId columns to Epic, Story, and WorkTask entities 2. TenantContext Service - Implement tenant context retrieval from JWT claims Changes: - Added TenantId property to Epic, Story, and WorkTask domain entities - Updated entity factory methods to require TenantId parameter - Modified Project.CreateEpic to pass TenantId from parent aggregate - Modified Epic.CreateStory and Story.CreateTask to propagate TenantId - Added EF Core configurations for TenantId mapping with proper indexes - Created EF Core migration: AddTenantIdToEpicStoryTask * Adds tenant_id columns to Epics, Stories, and Tasks tables * Creates indexes: ix_epics_tenant_id, ix_stories_tenant_id, ix_tasks_tenant_id * Uses default Guid.Empty for existing data (backward compatible) - Implemented ITenantContext interface in Application layer - Implemented TenantContext service in Infrastructure layer * Retrieves tenant ID from JWT claims (tenant_id or tenantId) * Throws UnauthorizedAccessException if tenant context unavailable - Registered TenantContext as scoped service in DI container - Added Global Query Filters for Epic, Story, and WorkTask entities * Ensures automatic tenant isolation at database query level * Prevents cross-tenant data access Architecture: - Follows the same pattern as Issue Management Module (Day 14) - Maintains consistency with Project entity multi-tenancy implementation - Ensures data isolation through both domain logic and database filters Note: Unit tests require updates to pass TenantId parameter - will be addressed in follow-up commits Reference: Day 15 roadmap (DAY15-22-PROJECTMANAGEMENT-ROADMAP.md) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
99 lines
3.0 KiB
C#
99 lines
3.0 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
|
using ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate;
|
|
using ColaFlow.Modules.ProjectManagement.Domain.ValueObjects;
|
|
using ColaFlow.Shared.Kernel.Common;
|
|
|
|
namespace ColaFlow.Modules.ProjectManagement.Infrastructure.Persistence.Configurations;
|
|
|
|
/// <summary>
|
|
/// Entity configuration for WorkTask entity
|
|
/// </summary>
|
|
public class WorkTaskConfiguration : IEntityTypeConfiguration<WorkTask>
|
|
{
|
|
public void Configure(EntityTypeBuilder<WorkTask> builder)
|
|
{
|
|
builder.ToTable("Tasks");
|
|
|
|
// Primary key
|
|
builder.HasKey("Id");
|
|
|
|
// Id conversion
|
|
builder.Property(t => t.Id)
|
|
.HasConversion(
|
|
id => id.Value,
|
|
value => TaskId.From(value))
|
|
.IsRequired()
|
|
.ValueGeneratedNever();
|
|
|
|
// TenantId (required for multi-tenancy)
|
|
builder.Property(t => t.TenantId)
|
|
.HasConversion(
|
|
id => id.Value,
|
|
value => TenantId.From(value))
|
|
.IsRequired()
|
|
.HasColumnName("tenant_id");
|
|
|
|
// StoryId (foreign key)
|
|
builder.Property(t => t.StoryId)
|
|
.HasConversion(
|
|
id => id.Value,
|
|
value => StoryId.From(value))
|
|
.IsRequired();
|
|
|
|
// Basic properties
|
|
builder.Property(t => t.Title)
|
|
.HasMaxLength(200)
|
|
.IsRequired();
|
|
|
|
builder.Property(t => t.Description)
|
|
.HasMaxLength(4000);
|
|
|
|
// Status enumeration
|
|
builder.Property(t => t.Status)
|
|
.HasConversion(
|
|
s => s.Name,
|
|
name => Enumeration.FromDisplayName<WorkItemStatus>(name))
|
|
.HasMaxLength(50)
|
|
.IsRequired();
|
|
|
|
// Priority enumeration
|
|
builder.Property(t => t.Priority)
|
|
.HasConversion(
|
|
p => p.Name,
|
|
name => Enumeration.FromDisplayName<TaskPriority>(name))
|
|
.HasMaxLength(50)
|
|
.IsRequired();
|
|
|
|
// CreatedBy conversion
|
|
builder.Property(t => t.CreatedBy)
|
|
.HasConversion(
|
|
id => id.Value,
|
|
value => UserId.From(value))
|
|
.IsRequired();
|
|
|
|
// AssigneeId (optional)
|
|
builder.Property(t => t.AssigneeId)
|
|
.HasConversion(
|
|
id => id != null ? id.Value : (Guid?)null,
|
|
value => value.HasValue ? UserId.From(value.Value) : null);
|
|
|
|
// Effort tracking
|
|
builder.Property(t => t.EstimatedHours);
|
|
builder.Property(t => t.ActualHours);
|
|
|
|
// Timestamps
|
|
builder.Property(t => t.CreatedAt)
|
|
.IsRequired();
|
|
|
|
builder.Property(t => t.UpdatedAt);
|
|
|
|
// Indexes
|
|
builder.HasIndex(t => t.TenantId)
|
|
.HasDatabaseName("ix_tasks_tenant_id");
|
|
builder.HasIndex(t => t.StoryId);
|
|
builder.HasIndex(t => t.AssigneeId);
|
|
builder.HasIndex(t => t.CreatedAt);
|
|
}
|
|
}
|