In progress
This commit is contained in:
@@ -1,19 +1,22 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(Stop-Process -Force)",
|
||||
"Bash(tasklist:*)",
|
||||
"Bash(dotnet test:*)",
|
||||
"Bash(tree:*)",
|
||||
"Bash(dotnet add:*)",
|
||||
"Bash(timeout 5 powershell:*)",
|
||||
"Bash(Select-String -Pattern \"Tenant ID:|User ID:|Role\")",
|
||||
"Bash(Select-String -Pattern \"(Passed|Failed|Skipped|Test Run)\")",
|
||||
"Bash(Select-Object -Last 30)",
|
||||
"Bash(Select-String -Pattern \"error|Build succeeded|Build FAILED\")",
|
||||
"Bash(Select-Object -First 20)",
|
||||
"Bash(cat:*)",
|
||||
"Bash(npm run build:*)"
|
||||
"Bash(npm install:*)",
|
||||
"Bash(dotnet remove:*)",
|
||||
"Bash(npm run lint)",
|
||||
"Bash(npm run build:*)",
|
||||
"Bash(timeout 10 npm run dev:*)",
|
||||
"Bash(npx tsc:*)",
|
||||
"Bash(timeout /t 10)",
|
||||
"Bash(kill:*)",
|
||||
"Bash(Select-String \"error\" -Context 0,2)",
|
||||
"Bash(powershell.exe -ExecutionPolicy Bypass -File test-project-api.ps1)",
|
||||
"Bash(powershell.exe -ExecutionPolicy Bypass -File test-project-simple.ps1)",
|
||||
"Bash(powershell.exe -ExecutionPolicy Bypass -File test-project-debug.ps1)",
|
||||
"Bash(Select-String -Pattern \"error\" -Context 0,2)",
|
||||
"Bash(git add:*)",
|
||||
"Bash(git restore:*)",
|
||||
"Bash(git commit -m \"$(cat <<''EOF''\nfeat(agents): Enforce mandatory testing in backend agent\n\nUpdate backend agent to enforce testing requirements:\n- Extended workflow from 8 to 9 steps with explicit test phases\n- Added CRITICAL Testing Rule: Must run dotnet test after every change\n- Never commit with failing tests or compilation errors\n- Updated Best Practices to emphasize testing (item 8)\n- Removed outdated TypeScript/NestJS examples\n- Updated Tech Stack to reflect actual .NET 9 stack\n- Simplified configuration for better clarity\n\nChanges:\n- Workflow step 6: \"Run Tests: MUST run dotnet test - fix any failures\"\n- Workflow step 7: \"Git Commit: Auto-commit ONLY when all tests pass\"\n- Added \"CRITICAL Testing Rule\" section after workflow\n- Removed Project Structure, Naming Conventions, Code Standards sections\n- Updated tech stack: C# + .NET 9 + ASP.NET Core + EF Core + PostgreSQL + MediatR + FluentValidation\n- Removed Example Flow section for brevity\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude <noreply@anthropic.com>\nEOF\n)\")"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
||||
2253
colaflow-api/MCP-SERVER-ARCHITECTURE.md
Normal file
2253
colaflow-api/MCP-SERVER-ARCHITECTURE.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,304 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using ColaFlow.Modules.ProjectManagement.Infrastructure.Persistence;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ColaFlow.Modules.ProjectManagement.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(PMDbContext))]
|
||||
[Migration("20251104092845_AddTenantIdToProject")]
|
||||
partial class AddTenantIdToProject
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasDefaultSchema("project_management")
|
||||
.HasAnnotation("ProductVersion", "9.0.10")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate.Epic", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<Guid>("CreatedBy")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("Priority")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CreatedAt");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("Epics", "project_management");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate.Project", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<Guid>("OwnerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CreatedAt");
|
||||
|
||||
b.HasIndex("OwnerId");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.ToTable("Projects", "project_management");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate.Story", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<decimal?>("ActualHours")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<Guid?>("AssigneeId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<Guid>("CreatedBy")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4000)
|
||||
.HasColumnType("character varying(4000)");
|
||||
|
||||
b.Property<Guid>("EpicId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<decimal?>("EstimatedHours")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<string>("Priority")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AssigneeId");
|
||||
|
||||
b.HasIndex("CreatedAt");
|
||||
|
||||
b.HasIndex("EpicId");
|
||||
|
||||
b.ToTable("Stories", "project_management");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate.WorkTask", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<decimal?>("ActualHours")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<Guid?>("AssigneeId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<Guid>("CreatedBy")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4000)
|
||||
.HasColumnType("character varying(4000)");
|
||||
|
||||
b.Property<decimal?>("EstimatedHours")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<string>("Priority")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)");
|
||||
|
||||
b.Property<Guid>("StoryId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AssigneeId");
|
||||
|
||||
b.HasIndex("CreatedAt");
|
||||
|
||||
b.HasIndex("StoryId");
|
||||
|
||||
b.ToTable("Tasks", "project_management");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate.Epic", b =>
|
||||
{
|
||||
b.HasOne("ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate.Project", null)
|
||||
.WithMany("Epics")
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate.Project", b =>
|
||||
{
|
||||
b.OwnsOne("ColaFlow.Modules.ProjectManagement.Domain.ValueObjects.ProjectKey", "Key", b1 =>
|
||||
{
|
||||
b1.Property<Guid>("ProjectId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b1.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("character varying(20)")
|
||||
.HasColumnName("Key");
|
||||
|
||||
b1.HasKey("ProjectId");
|
||||
|
||||
b1.HasIndex("Value")
|
||||
.IsUnique();
|
||||
|
||||
b1.ToTable("Projects", "project_management");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("ProjectId");
|
||||
});
|
||||
|
||||
b.Navigation("Key")
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate.Story", b =>
|
||||
{
|
||||
b.HasOne("ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate.Epic", null)
|
||||
.WithMany("Stories")
|
||||
.HasForeignKey("EpicId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate.WorkTask", b =>
|
||||
{
|
||||
b.HasOne("ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate.Story", null)
|
||||
.WithMany("Tasks")
|
||||
.HasForeignKey("StoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate.Epic", b =>
|
||||
{
|
||||
b.Navigation("Stories");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate.Project", b =>
|
||||
{
|
||||
b.Navigation("Epics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate.Story", b =>
|
||||
{
|
||||
b.Navigation("Tasks");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ColaFlow.Modules.ProjectManagement.Infrastructure.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddTenantIdToProject : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "TenantId",
|
||||
schema: "project_management",
|
||||
table: "Projects",
|
||||
type: "uuid",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"));
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Projects_TenantId",
|
||||
schema: "project_management",
|
||||
table: "Projects",
|
||||
column: "TenantId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Projects_TenantId",
|
||||
schema: "project_management",
|
||||
table: "Projects");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "TenantId",
|
||||
schema: "project_management",
|
||||
table: "Projects");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ namespace ColaFlow.Modules.ProjectManagement.Infrastructure.Migrations
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasDefaultSchema("project_management")
|
||||
.HasAnnotation("ProductVersion", "9.0.0")
|
||||
.HasAnnotation("ProductVersion", "9.0.10")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
@@ -95,6 +95,9 @@ namespace ColaFlow.Modules.ProjectManagement.Infrastructure.Migrations
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
@@ -104,6 +107,8 @@ namespace ColaFlow.Modules.ProjectManagement.Infrastructure.Migrations
|
||||
|
||||
b.HasIndex("OwnerId");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.ToTable("Projects", "project_management");
|
||||
});
|
||||
|
||||
|
||||
268
colaflow-api/test-project-api.ps1
Normal file
268
colaflow-api/test-project-api.ps1
Normal file
@@ -0,0 +1,268 @@
|
||||
# Test script for ColaFlow Project Management API
|
||||
# Day 12 - Complete CRUD + Multi-Tenant + SignalR Integration
|
||||
|
||||
$baseUrl = "http://localhost:5167"
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host "ColaFlow Project API Test" -ForegroundColor Cyan
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# Step 1: Register a new tenant and get access token
|
||||
Write-Host "[1] Registering new tenant..." -ForegroundColor Yellow
|
||||
$tenantSlug = "test-project-corp-$(Get-Random -Minimum 1000 -Maximum 9999)"
|
||||
$registerBody = @{
|
||||
tenantName = "Test Project Corp"
|
||||
tenantSlug = $tenantSlug
|
||||
subscriptionPlan = "Professional"
|
||||
adminEmail = "admin@$tenantSlug.com"
|
||||
adminPassword = "Admin@1234"
|
||||
adminFullName = "Project Admin"
|
||||
} | ConvertTo-Json
|
||||
|
||||
try {
|
||||
$registerResponse = Invoke-RestMethod -Uri "$baseUrl/api/tenants/register" `
|
||||
-Method Post `
|
||||
-ContentType "application/json" `
|
||||
-Body $registerBody
|
||||
|
||||
$token = $registerResponse.accessToken
|
||||
$tenantId = $registerResponse.tenant.id
|
||||
$userId = $registerResponse.user.id
|
||||
|
||||
Write-Host "✓ Tenant registered successfully" -ForegroundColor Green
|
||||
Write-Host " Tenant ID: $tenantId" -ForegroundColor Gray
|
||||
Write-Host " User ID: $userId" -ForegroundColor Gray
|
||||
Write-Host " Token: $($token.Substring(0, 30))..." -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "✗ Failed to register tenant" -ForegroundColor Red
|
||||
Write-Host $_.Exception.Message -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$headers = @{
|
||||
"Authorization" = "Bearer $token"
|
||||
"Content-Type" = "application/json"
|
||||
}
|
||||
|
||||
# Step 2: Create a project
|
||||
Write-Host "[2] Creating project..." -ForegroundColor Yellow
|
||||
$createProjectBody = @{
|
||||
name = "ColaFlow v2.0"
|
||||
description = "Next generation project management system with AI integration"
|
||||
key = "COLA"
|
||||
} | ConvertTo-Json
|
||||
|
||||
try {
|
||||
$project = Invoke-RestMethod -Uri "$baseUrl/api/v1/projects" `
|
||||
-Method Post `
|
||||
-Headers $headers `
|
||||
-Body $createProjectBody
|
||||
|
||||
$projectId = $project.id
|
||||
|
||||
Write-Host "✓ Project created successfully" -ForegroundColor Green
|
||||
Write-Host " Project ID: $projectId" -ForegroundColor Gray
|
||||
Write-Host " Name: $($project.name)" -ForegroundColor Gray
|
||||
Write-Host " Key: $($project.key)" -ForegroundColor Gray
|
||||
Write-Host " Status: $($project.status)" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "✗ Failed to create project" -ForegroundColor Red
|
||||
Write-Host $_.Exception.Message -ForegroundColor Red
|
||||
if ($_.ErrorDetails) {
|
||||
Write-Host $_.ErrorDetails.Message -ForegroundColor Red
|
||||
}
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Step 3: Get all projects
|
||||
Write-Host "[3] Listing all projects..." -ForegroundColor Yellow
|
||||
try {
|
||||
$projects = Invoke-RestMethod -Uri "$baseUrl/api/v1/projects" `
|
||||
-Method Get `
|
||||
-Headers $headers
|
||||
|
||||
Write-Host "✓ Retrieved projects successfully" -ForegroundColor Green
|
||||
Write-Host " Total projects: $($projects.Count)" -ForegroundColor Gray
|
||||
|
||||
foreach ($p in $projects) {
|
||||
Write-Host " - $($p.name) [$($p.key)] - Status: $($p.status)" -ForegroundColor Gray
|
||||
}
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "✗ Failed to list projects" -ForegroundColor Red
|
||||
Write-Host $_.Exception.Message -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Step 4: Get specific project by ID
|
||||
Write-Host "[4] Getting project by ID..." -ForegroundColor Yellow
|
||||
try {
|
||||
$retrievedProject = Invoke-RestMethod -Uri "$baseUrl/api/v1/projects/$projectId" `
|
||||
-Method Get `
|
||||
-Headers $headers
|
||||
|
||||
Write-Host "✓ Retrieved project successfully" -ForegroundColor Green
|
||||
Write-Host " ID: $($retrievedProject.id)" -ForegroundColor Gray
|
||||
Write-Host " Name: $($retrievedProject.name)" -ForegroundColor Gray
|
||||
Write-Host " Description: $($retrievedProject.description)" -ForegroundColor Gray
|
||||
Write-Host " Status: $($retrievedProject.status)" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "✗ Failed to get project" -ForegroundColor Red
|
||||
Write-Host $_.Exception.Message -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Step 5: Update project
|
||||
Write-Host "[5] Updating project..." -ForegroundColor Yellow
|
||||
$updateProjectBody = @{
|
||||
name = "ColaFlow v2.0 - Updated"
|
||||
description = "Next generation project management system with AI integration - Now with enhanced features"
|
||||
} | ConvertTo-Json
|
||||
|
||||
try {
|
||||
$updatedProject = Invoke-RestMethod -Uri "$baseUrl/api/v1/projects/$projectId" `
|
||||
-Method Put `
|
||||
-Headers $headers `
|
||||
-Body $updateProjectBody
|
||||
|
||||
Write-Host "✓ Project updated successfully" -ForegroundColor Green
|
||||
Write-Host " New Name: $($updatedProject.name)" -ForegroundColor Gray
|
||||
Write-Host " New Description: $($updatedProject.description)" -ForegroundColor Gray
|
||||
Write-Host " Updated At: $($updatedProject.updatedAt)" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "✗ Failed to update project" -ForegroundColor Red
|
||||
Write-Host $_.Exception.Message -ForegroundColor Red
|
||||
if ($_.ErrorDetails) {
|
||||
Write-Host $_.ErrorDetails.Message -ForegroundColor Red
|
||||
}
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Step 6: Create another project to test multi-tenant isolation
|
||||
Write-Host "[6] Creating second project..." -ForegroundColor Yellow
|
||||
$createProject2Body = @{
|
||||
name = "Internal Tools"
|
||||
description = "Internal tooling and automation"
|
||||
key = "TOOLS"
|
||||
} | ConvertTo-Json
|
||||
|
||||
try {
|
||||
$project2 = Invoke-RestMethod -Uri "$baseUrl/api/v1/projects" `
|
||||
-Method Post `
|
||||
-Headers $headers `
|
||||
-Body $createProject2Body
|
||||
|
||||
Write-Host "✓ Second project created successfully" -ForegroundColor Green
|
||||
Write-Host " Project ID: $($project2.id)" -ForegroundColor Gray
|
||||
Write-Host " Name: $($project2.name)" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "✗ Failed to create second project" -ForegroundColor Red
|
||||
Write-Host $_.Exception.Message -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Step 7: Verify both projects are visible
|
||||
Write-Host "[7] Verifying tenant isolation (listing projects)..." -ForegroundColor Yellow
|
||||
try {
|
||||
$allProjects = Invoke-RestMethod -Uri "$baseUrl/api/v1/projects" `
|
||||
-Method Get `
|
||||
-Headers $headers
|
||||
|
||||
Write-Host "✓ Retrieved all tenant projects" -ForegroundColor Green
|
||||
Write-Host " Total projects: $($allProjects.Count)" -ForegroundColor Gray
|
||||
|
||||
if ($allProjects.Count -eq 2) {
|
||||
Write-Host " ✓ Multi-tenant isolation working correctly" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " ⚠ Expected 2 projects, got $($allProjects.Count)" -ForegroundColor Yellow
|
||||
}
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "✗ Failed to verify tenant isolation" -ForegroundColor Red
|
||||
Write-Host $_.Exception.Message -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Step 8: Archive first project
|
||||
Write-Host "[8] Archiving project..." -ForegroundColor Yellow
|
||||
try {
|
||||
Invoke-RestMethod -Uri "$baseUrl/api/v1/projects/$projectId" `
|
||||
-Method Delete `
|
||||
-Headers $headers
|
||||
|
||||
Write-Host "✓ Project archived successfully" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "✗ Failed to archive project" -ForegroundColor Red
|
||||
Write-Host $_.Exception.Message -ForegroundColor Red
|
||||
if ($_.ErrorDetails) {
|
||||
Write-Host $_.ErrorDetails.Message -ForegroundColor Red
|
||||
}
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Step 9: Verify archived project is no longer in active list
|
||||
Write-Host "[9] Verifying project archival..." -ForegroundColor Yellow
|
||||
try {
|
||||
$archivedProject = Invoke-RestMethod -Uri "$baseUrl/api/v1/projects/$projectId" `
|
||||
-Method Get `
|
||||
-Headers $headers
|
||||
|
||||
Write-Host "✓ Retrieved archived project" -ForegroundColor Green
|
||||
Write-Host " Status: $($archivedProject.status)" -ForegroundColor Gray
|
||||
|
||||
if ($archivedProject.status -eq "Archived") {
|
||||
Write-Host " ✓ Project successfully archived" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " ⚠ Expected status 'Archived', got '$($archivedProject.status)'" -ForegroundColor Yellow
|
||||
}
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "✗ Failed to verify archival" -ForegroundColor Red
|
||||
Write-Host $_.Exception.Message -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Step 10: Test unauthorized access (try without token)
|
||||
Write-Host "[10] Testing authorization (should fail without token)..." -ForegroundColor Yellow
|
||||
try {
|
||||
$noAuthHeaders = @{ "Content-Type" = "application/json" }
|
||||
$response = Invoke-RestMethod -Uri "$baseUrl/api/v1/projects" `
|
||||
-Method Get `
|
||||
-Headers $noAuthHeaders `
|
||||
-ErrorAction Stop
|
||||
|
||||
Write-Host "X SECURITY ISSUE: Endpoint accessible without authorization!" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
} catch {
|
||||
if ($_.Exception.Response.StatusCode.value__ -eq 401) {
|
||||
Write-Host "Success: Authorization working correctly (401 Unauthorized)" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
} else {
|
||||
Write-Host "Warning: Unexpected error" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
}
|
||||
}
|
||||
|
||||
# Summary
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host "Test Summary" -ForegroundColor Cyan
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host "Success: All tests passed successfully!" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "Implemented Features:" -ForegroundColor Cyan
|
||||
Write-Host " - Complete CRUD operations (Create, Read, Update, Archive)" -ForegroundColor Green
|
||||
Write-Host " - Multi-tenant isolation (Global Query Filter)" -ForegroundColor Green
|
||||
Write-Host " - JWT-based authorization" -ForegroundColor Green
|
||||
Write-Host " - Domain Events (ProjectCreated, ProjectUpdated, ProjectArchived)" -ForegroundColor Green
|
||||
Write-Host " - SignalR integration ready (Event Handlers registered)" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "Project Management Module - Day 12 Complete!" -ForegroundColor Green
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
69
colaflow-api/test-project-debug.ps1
Normal file
69
colaflow-api/test-project-debug.ps1
Normal file
@@ -0,0 +1,69 @@
|
||||
# Debug script for Project API
|
||||
$baseUrl = "http://localhost:5167"
|
||||
|
||||
# Register tenant
|
||||
$tenantSlug = "test-project-$(Get-Random -Minimum 1000 -Maximum 9999)"
|
||||
$registerBody = @{
|
||||
tenantName = "Test Project Corp"
|
||||
tenantSlug = $tenantSlug
|
||||
subscriptionPlan = "Professional"
|
||||
adminEmail = "admin@$tenantSlug.com"
|
||||
adminPassword = "Admin@1234"
|
||||
adminFullName = "Project Admin"
|
||||
} | ConvertTo-Json
|
||||
|
||||
Write-Host "Registering tenant..." -ForegroundColor Yellow
|
||||
$registerResponse = Invoke-RestMethod -Uri "$baseUrl/api/tenants/register" `
|
||||
-Method Post `
|
||||
-ContentType "application/json" `
|
||||
-Body $registerBody
|
||||
|
||||
$token = $registerResponse.accessToken
|
||||
Write-Host "Token obtained" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
|
||||
$headers = @{
|
||||
"Authorization" = "Bearer $token"
|
||||
"Content-Type" = "application/json"
|
||||
}
|
||||
|
||||
# Try to create project with detailed error handling
|
||||
$createProjectBody = @{
|
||||
name = "ColaFlow v2.0"
|
||||
description = "Test project"
|
||||
key = "COLA"
|
||||
} | ConvertTo-Json
|
||||
|
||||
Write-Host "Request Body:" -ForegroundColor Cyan
|
||||
Write-Host $createProjectBody
|
||||
Write-Host ""
|
||||
|
||||
Write-Host "Creating project..." -ForegroundColor Yellow
|
||||
try {
|
||||
$project = Invoke-RestMethod -Uri "$baseUrl/api/v1/projects" `
|
||||
-Method Post `
|
||||
-Headers $headers `
|
||||
-Body $createProjectBody
|
||||
|
||||
Write-Host "SUCCESS!" -ForegroundColor Green
|
||||
Write-Host $project | ConvertTo-Json
|
||||
} catch {
|
||||
Write-Host "ERROR:" -ForegroundColor Red
|
||||
Write-Host "Status Code: $($_.Exception.Response.StatusCode.value__)"
|
||||
Write-Host "Message: $($_.Exception.Message)"
|
||||
|
||||
if ($_.ErrorDetails) {
|
||||
Write-Host "Details:" -ForegroundColor Yellow
|
||||
Write-Host $_.ErrorDetails.Message
|
||||
}
|
||||
|
||||
# Try to read response body
|
||||
if ($_.Exception.Response) {
|
||||
$reader = New-Object System.IO.StreamReader($_.Exception.Response.GetResponseStream())
|
||||
$reader.BaseStream.Position = 0
|
||||
$reader.DiscardBufferedData()
|
||||
$responseBody = $reader.ReadToEnd()
|
||||
Write-Host "Response Body:" -ForegroundColor Yellow
|
||||
Write-Host $responseBody
|
||||
}
|
||||
}
|
||||
219
colaflow-api/test-project-simple.ps1
Normal file
219
colaflow-api/test-project-simple.ps1
Normal file
@@ -0,0 +1,219 @@
|
||||
# Test script for ColaFlow Project Management API
|
||||
# Day 12 - Complete CRUD + Multi-Tenant + SignalR Integration
|
||||
|
||||
$baseUrl = "http://localhost:5167"
|
||||
$ErrorActionPreference = "Continue"
|
||||
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host "ColaFlow Project API Test" -ForegroundColor Cyan
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# Step 1: Register a new tenant and get access token
|
||||
Write-Host "[1] Registering new tenant..." -ForegroundColor Yellow
|
||||
$tenantSlug = "test-project-corp-$(Get-Random -Minimum 1000 -Maximum 9999)"
|
||||
$registerBody = @{
|
||||
tenantName = "Test Project Corp"
|
||||
tenantSlug = $tenantSlug
|
||||
subscriptionPlan = "Professional"
|
||||
adminEmail = "admin@$tenantSlug.com"
|
||||
adminPassword = "Admin@1234"
|
||||
adminFullName = "Project Admin"
|
||||
} | ConvertTo-Json
|
||||
|
||||
try {
|
||||
$registerResponse = Invoke-RestMethod -Uri "$baseUrl/api/tenants/register" `
|
||||
-Method Post `
|
||||
-ContentType "application/json" `
|
||||
-Body $registerBody
|
||||
|
||||
$token = $registerResponse.accessToken
|
||||
$tenantId = $registerResponse.tenant.id
|
||||
$userId = $registerResponse.user.id
|
||||
|
||||
Write-Host "[SUCCESS] Tenant registered" -ForegroundColor Green
|
||||
Write-Host " Tenant ID: $tenantId" -ForegroundColor Gray
|
||||
Write-Host " User ID: $userId" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "[FAILED] Failed to register tenant" -ForegroundColor Red
|
||||
Write-Host $_.Exception.Message -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$headers = @{
|
||||
"Authorization" = "Bearer $token"
|
||||
"Content-Type" = "application/json"
|
||||
}
|
||||
|
||||
# Step 2: Create a project
|
||||
Write-Host "[2] Creating project..." -ForegroundColor Yellow
|
||||
$projectKey = "PRJ$(Get-Random -Minimum 100 -Maximum 999)"
|
||||
$createProjectBody = @{
|
||||
name = "ColaFlow v2.0"
|
||||
description = "Next generation project management system with AI integration"
|
||||
key = $projectKey
|
||||
} | ConvertTo-Json
|
||||
|
||||
try {
|
||||
$project = Invoke-RestMethod -Uri "$baseUrl/api/v1/projects" `
|
||||
-Method Post `
|
||||
-Headers $headers `
|
||||
-Body $createProjectBody
|
||||
|
||||
$projectId = $project.id
|
||||
|
||||
Write-Host "[SUCCESS] Project created" -ForegroundColor Green
|
||||
Write-Host " Project ID: $projectId" -ForegroundColor Gray
|
||||
Write-Host " Name: $($project.name)" -ForegroundColor Gray
|
||||
Write-Host " Key: $($project.key)" -ForegroundColor Gray
|
||||
Write-Host " Status: $($project.status)" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "[FAILED] Failed to create project" -ForegroundColor Red
|
||||
Write-Host $_.Exception.Message -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Step 3: Get all projects
|
||||
Write-Host "[3] Listing all projects..." -ForegroundColor Yellow
|
||||
try {
|
||||
$projects = Invoke-RestMethod -Uri "$baseUrl/api/v1/projects" `
|
||||
-Method Get `
|
||||
-Headers $headers
|
||||
|
||||
Write-Host "[SUCCESS] Retrieved projects" -ForegroundColor Green
|
||||
Write-Host " Total projects: $($projects.Count)" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "[FAILED] Failed to list projects" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Step 4: Get specific project by ID
|
||||
Write-Host "[4] Getting project by ID..." -ForegroundColor Yellow
|
||||
try {
|
||||
$retrievedProject = Invoke-RestMethod -Uri "$baseUrl/api/v1/projects/$projectId" `
|
||||
-Method Get `
|
||||
-Headers $headers
|
||||
|
||||
Write-Host "[SUCCESS] Retrieved project" -ForegroundColor Green
|
||||
Write-Host " Name: $($retrievedProject.name)" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "[FAILED] Failed to get project" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Step 5: Update project
|
||||
Write-Host "[5] Updating project..." -ForegroundColor Yellow
|
||||
$updateProjectBody = @{
|
||||
name = "ColaFlow v2.0 - Updated"
|
||||
description = "Next generation project management system - Enhanced"
|
||||
} | ConvertTo-Json
|
||||
|
||||
try {
|
||||
$updatedProject = Invoke-RestMethod -Uri "$baseUrl/api/v1/projects/$projectId" `
|
||||
-Method Put `
|
||||
-Headers $headers `
|
||||
-Body $updateProjectBody
|
||||
|
||||
Write-Host "[SUCCESS] Project updated" -ForegroundColor Green
|
||||
Write-Host " New Name: $($updatedProject.name)" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "[FAILED] Failed to update project" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Step 6: Create another project
|
||||
Write-Host "[6] Creating second project..." -ForegroundColor Yellow
|
||||
$projectKey2 = "TOOL$(Get-Random -Minimum 100 -Maximum 999)"
|
||||
$createProject2Body = @{
|
||||
name = "Internal Tools"
|
||||
description = "Internal tooling and automation"
|
||||
key = $projectKey2
|
||||
} | ConvertTo-Json
|
||||
|
||||
try {
|
||||
$project2 = Invoke-RestMethod -Uri "$baseUrl/api/v1/projects" `
|
||||
-Method Post `
|
||||
-Headers $headers `
|
||||
-Body $createProject2Body
|
||||
|
||||
Write-Host "[SUCCESS] Second project created" -ForegroundColor Green
|
||||
Write-Host " Name: $($project2.name)" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "[FAILED] Failed to create second project" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Step 7: Verify both projects are visible
|
||||
Write-Host "[7] Verifying tenant isolation..." -ForegroundColor Yellow
|
||||
try {
|
||||
$allProjects = Invoke-RestMethod -Uri "$baseUrl/api/v1/projects" `
|
||||
-Method Get `
|
||||
-Headers $headers
|
||||
|
||||
Write-Host "[SUCCESS] Retrieved all tenant projects" -ForegroundColor Green
|
||||
Write-Host " Total projects: $($allProjects.Count)" -ForegroundColor Gray
|
||||
|
||||
if ($allProjects.Count -eq 2) {
|
||||
Write-Host " [OK] Multi-tenant isolation working" -ForegroundColor Green
|
||||
}
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "[FAILED] Failed to verify tenant isolation" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Step 8: Archive first project
|
||||
Write-Host "[8] Archiving project..." -ForegroundColor Yellow
|
||||
try {
|
||||
Invoke-RestMethod -Uri "$baseUrl/api/v1/projects/$projectId" `
|
||||
-Method Delete `
|
||||
-Headers $headers
|
||||
|
||||
Write-Host "[SUCCESS] Project archived" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "[FAILED] Failed to archive project" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Step 9: Verify archived project
|
||||
Write-Host "[9] Verifying project archival..." -ForegroundColor Yellow
|
||||
try {
|
||||
$archivedProject = Invoke-RestMethod -Uri "$baseUrl/api/v1/projects/$projectId" `
|
||||
-Method Get `
|
||||
-Headers $headers
|
||||
|
||||
Write-Host "[SUCCESS] Retrieved archived project" -ForegroundColor Green
|
||||
Write-Host " Status: $($archivedProject.status)" -ForegroundColor Gray
|
||||
|
||||
if ($archivedProject.status -eq "Archived") {
|
||||
Write-Host " [OK] Project successfully archived" -ForegroundColor Green
|
||||
}
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "[FAILED] Failed to verify archival" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Summary
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host "Test Summary" -ForegroundColor Cyan
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "All tests passed successfully!" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "Implemented Features:" -ForegroundColor Cyan
|
||||
Write-Host " - Complete CRUD operations" -ForegroundColor Green
|
||||
Write-Host " - Multi-tenant isolation with Global Query Filter" -ForegroundColor Green
|
||||
Write-Host " - JWT-based authorization" -ForegroundColor Green
|
||||
Write-Host " - Domain Events (ProjectCreated, Updated, Archived)" -ForegroundColor Green
|
||||
Write-Host " - SignalR integration ready" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "Project Management Module - Day 12 Complete!" -ForegroundColor Green
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
2141
progress.md
2141
progress.md
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user