Files
ColaFlow/docs/plans/sprint_2_story_2_task_2.md
Yaojia Wang ebb56cc9f8 feat(backend): Create Sprint 2 backend Stories and Tasks
Created detailed implementation plans for Sprint 2 backend work:

Story 1: Audit Log Foundation (Phase 1)
- Task 1: Design AuditLog database schema and create migration
- Task 2: Create AuditLog entity and Repository
- Task 3: Implement EF Core SaveChangesInterceptor
- Task 4: Write unit tests for audit logging
- Task 5: Integrate with ProjectManagement Module

Story 2: Audit Log Core Features (Phase 2)
- Task 1: Implement Changed Fields Detection (JSON Diff)
- Task 2: Integrate User Context Tracking
- Task 3: Add Multi-Tenant Isolation
- Task 4: Implement Audit Query API
- Task 5: Write Integration Tests

Story 3: Sprint Management Module
- Task 1: Create Sprint Aggregate Root and Domain Events
- Task 2: Implement Sprint Repository and EF Core Configuration
- Task 3: Create CQRS Commands and Queries
- Task 4: Implement Burndown Chart Calculation
- Task 5: Add SignalR Real-Time Notifications
- Task 6: Write Integration Tests

Total: 3 Stories, 16 Tasks, 24 Story Points (8+8+8)
Estimated Duration: 10-12 days

All tasks include:
- Detailed technical implementation guidance
- Code examples and file paths
- Testing requirements (>= 90% coverage)
- Performance benchmarks (< 5ms audit overhead)
- Multi-tenant security validation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 22:56:31 +01:00

4.2 KiB

task_id, story, status, estimated_hours, created_date, assignee
task_id story status estimated_hours created_date assignee
sprint_2_story_2_task_2 sprint_2_story_2 not_started 3 2025-11-05 Backend Team

Task 2: Integrate User Context Tracking

Story: Story 2 - Audit Log Core Features (Phase 2) Estimated: 3 hours

Description

Enhance audit logging to automatically capture the current user (UserId) from HTTP context for every operation. This provides accountability and traceability.

Acceptance Criteria

  • UserId automatically captured from JWT token
  • System operations (null user) handled correctly
  • User information enriched in audit logs
  • Integration tests verify user tracking
  • Performance not impacted

Implementation Details

Already Implemented in Story 1!

The AuditLogInterceptor already captures UserId from HTTP context:

private Guid? GetCurrentUserId()
{
    var userIdClaim = _httpContextAccessor.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier);
    return userIdClaim != null ? Guid.Parse(userIdClaim.Value) : null;
}

This Task: Add User Information Enrichment

  1. Add User Navigation Property: colaflow-api/src/ColaFlow.Domain/Entities/AuditLog.cs
public class AuditLog
{
    // ... existing properties ...
    public Guid? UserId { get; set; }

    // Navigation property
    public User? User { get; set; }
}
  1. Update EF Configuration: colaflow-api/src/ColaFlow.Infrastructure/Data/Configurations/AuditLogConfiguration.cs
public void Configure(EntityTypeBuilder<AuditLog> builder)
{
    // ... existing configuration ...

    // User relationship (optional foreign key)
    builder.HasOne(a => a.User)
        .WithMany()
        .HasForeignKey(a => a.UserId)
        .OnDelete(DeleteBehavior.SetNull); // Don't delete audit logs when user is deleted
}
  1. Enrich Query Results: colaflow-api/src/ColaFlow.Infrastructure/Repositories/AuditLogRepository.cs
public async Task<List<AuditLog>> GetByEntityAsync(string entityType, Guid entityId)
{
    var tenantId = _tenantContext.TenantId;
    return await _context.AuditLogs
        .Include(a => a.User) // Include user info
        .Where(a => a.TenantId == tenantId && a.EntityType == entityType && a.EntityId == entityId)
        .OrderByDescending(a => a.Timestamp)
        .ToListAsync();
}
  1. Handle System Operations: Update AuditLogInterceptor to handle null users gracefully:
private Guid? GetCurrentUserId()
{
    try
    {
        var userIdClaim = _httpContextAccessor.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier);
        if (userIdClaim != null && Guid.TryParse(userIdClaim.Value, out var userId))
        {
            return userId;
        }
    }
    catch (Exception ex)
    {
        _logger.LogWarning(ex, "Failed to get current user ID for audit log");
    }

    return null; // System operation or anonymous
}

Example Audit Log with User Info:

{
  "Id": "abc-123",
  "EntityType": "Project",
  "Action": "Update",
  "UserId": "user-456",
  "User": {
    "Id": "user-456",
    "UserName": "john.doe@example.com",
    "DisplayName": "John Doe"
  },
  "Timestamp": "2025-11-05T10:30:00Z",
  "ChangedFields": {
    "Title": { "OldValue": "Old", "NewValue": "New" }
  }
}

Technical Notes

  • Use OnDelete(DeleteBehavior.SetNull) to preserve audit logs when users are deleted
  • Handle null users gracefully (system operations, background jobs)
  • Use Include(a => a.User) for enriched query results
  • Consider caching user info for performance (future optimization)

Testing

Integration Tests:

[Fact]
public async Task CreateProject_ShouldCaptureCurrentUser()
{
    // Arrange
    var userId = Guid.NewGuid();
    SetCurrentUser(userId); // Helper to set HTTP context user

    // Act
    var projectId = await Mediator.Send(new CreateProjectCommand { /* ... */ });

    // Assert
    var auditLog = await Context.AuditLogs
        .Include(a => a.User)
        .FirstAsync(a => a.EntityId == projectId);

    Assert.Equal(userId, auditLog.UserId);
    Assert.NotNull(auditLog.User);
}

[Fact]
public async Task SystemOperation_ShouldHaveNullUser()
{
    // Test background job or system operation
    // ...
}

Created: 2025-11-05 by Backend Agent