In progress
This commit is contained in:
190
colaflow-api/src/ColaFlow.API/Controllers/StoriesController.cs
Normal file
190
colaflow-api/src/ColaFlow.API/Controllers/StoriesController.cs
Normal file
@@ -0,0 +1,190 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ColaFlow.Modules.ProjectManagement.Application.DTOs;
|
||||
using ColaFlow.Modules.ProjectManagement.Application.Commands.CreateStory;
|
||||
using ColaFlow.Modules.ProjectManagement.Application.Commands.UpdateStory;
|
||||
using ColaFlow.Modules.ProjectManagement.Application.Commands.DeleteStory;
|
||||
using ColaFlow.Modules.ProjectManagement.Application.Commands.AssignStory;
|
||||
using ColaFlow.Modules.ProjectManagement.Application.Queries.GetStoryById;
|
||||
using ColaFlow.Modules.ProjectManagement.Application.Queries.GetStoriesByEpicId;
|
||||
using ColaFlow.Modules.ProjectManagement.Application.Queries.GetStoriesByProjectId;
|
||||
|
||||
namespace ColaFlow.API.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Stories API Controller
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/v1")]
|
||||
public class StoriesController : ControllerBase
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public StoriesController(IMediator mediator)
|
||||
{
|
||||
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get story by ID
|
||||
/// </summary>
|
||||
[HttpGet("stories/{id:guid}")]
|
||||
[ProducesResponseType(typeof(StoryDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> GetStory(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = new GetStoryByIdQuery(id);
|
||||
var result = await _mediator.Send(query, cancellationToken);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all stories for an epic
|
||||
/// </summary>
|
||||
[HttpGet("epics/{epicId:guid}/stories")]
|
||||
[ProducesResponseType(typeof(List<StoryDto>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> GetEpicStories(Guid epicId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = new GetStoriesByEpicIdQuery(epicId);
|
||||
var result = await _mediator.Send(query, cancellationToken);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all stories for a project
|
||||
/// </summary>
|
||||
[HttpGet("projects/{projectId:guid}/stories")]
|
||||
[ProducesResponseType(typeof(List<StoryDto>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> GetProjectStories(Guid projectId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = new GetStoriesByProjectIdQuery(projectId);
|
||||
var result = await _mediator.Send(query, cancellationToken);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new story
|
||||
/// </summary>
|
||||
[HttpPost("epics/{epicId:guid}/stories")]
|
||||
[ProducesResponseType(typeof(StoryDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> CreateStory(
|
||||
Guid epicId,
|
||||
[FromBody] CreateStoryRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var command = new CreateStoryCommand
|
||||
{
|
||||
EpicId = epicId,
|
||||
Title = request.Title,
|
||||
Description = request.Description,
|
||||
Priority = request.Priority,
|
||||
AssigneeId = request.AssigneeId,
|
||||
EstimatedHours = request.EstimatedHours,
|
||||
CreatedBy = request.CreatedBy
|
||||
};
|
||||
|
||||
var result = await _mediator.Send(command, cancellationToken);
|
||||
return CreatedAtAction(nameof(GetStory), new { id = result.Id }, result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update an existing story
|
||||
/// </summary>
|
||||
[HttpPut("stories/{id:guid}")]
|
||||
[ProducesResponseType(typeof(StoryDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> UpdateStory(
|
||||
Guid id,
|
||||
[FromBody] UpdateStoryRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var command = new UpdateStoryCommand
|
||||
{
|
||||
StoryId = id,
|
||||
Title = request.Title,
|
||||
Description = request.Description,
|
||||
Status = request.Status,
|
||||
Priority = request.Priority,
|
||||
AssigneeId = request.AssigneeId,
|
||||
EstimatedHours = request.EstimatedHours
|
||||
};
|
||||
|
||||
var result = await _mediator.Send(command, cancellationToken);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete a story
|
||||
/// </summary>
|
||||
[HttpDelete("stories/{id:guid}")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
public async Task<IActionResult> DeleteStory(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var command = new DeleteStoryCommand { StoryId = id };
|
||||
await _mediator.Send(command, cancellationToken);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assign a story to a user
|
||||
/// </summary>
|
||||
[HttpPut("stories/{id:guid}/assign")]
|
||||
[ProducesResponseType(typeof(StoryDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> AssignStory(
|
||||
Guid id,
|
||||
[FromBody] AssignStoryRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var command = new AssignStoryCommand
|
||||
{
|
||||
StoryId = id,
|
||||
AssigneeId = request.AssigneeId
|
||||
};
|
||||
|
||||
var result = await _mediator.Send(command, cancellationToken);
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request model for creating a story
|
||||
/// </summary>
|
||||
public record CreateStoryRequest
|
||||
{
|
||||
public string Title { get; init; } = string.Empty;
|
||||
public string Description { get; init; } = string.Empty;
|
||||
public string Priority { get; init; } = "Medium";
|
||||
public Guid? AssigneeId { get; init; }
|
||||
public decimal? EstimatedHours { get; init; }
|
||||
public Guid CreatedBy { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request model for updating a story
|
||||
/// </summary>
|
||||
public record UpdateStoryRequest
|
||||
{
|
||||
public string Title { get; init; } = string.Empty;
|
||||
public string Description { get; init; } = string.Empty;
|
||||
public string? Status { get; init; }
|
||||
public string? Priority { get; init; }
|
||||
public Guid? AssigneeId { get; init; }
|
||||
public decimal? EstimatedHours { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request model for assigning a story
|
||||
/// </summary>
|
||||
public record AssignStoryRequest
|
||||
{
|
||||
public Guid AssigneeId { get; init; }
|
||||
}
|
||||
230
colaflow-api/src/ColaFlow.API/Controllers/TasksController.cs
Normal file
230
colaflow-api/src/ColaFlow.API/Controllers/TasksController.cs
Normal file
@@ -0,0 +1,230 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ColaFlow.Modules.ProjectManagement.Application.DTOs;
|
||||
using ColaFlow.Modules.ProjectManagement.Application.Commands.CreateTask;
|
||||
using ColaFlow.Modules.ProjectManagement.Application.Commands.UpdateTask;
|
||||
using ColaFlow.Modules.ProjectManagement.Application.Commands.DeleteTask;
|
||||
using ColaFlow.Modules.ProjectManagement.Application.Commands.AssignTask;
|
||||
using ColaFlow.Modules.ProjectManagement.Application.Commands.UpdateTaskStatus;
|
||||
using ColaFlow.Modules.ProjectManagement.Application.Queries.GetTaskById;
|
||||
using ColaFlow.Modules.ProjectManagement.Application.Queries.GetTasksByStoryId;
|
||||
using ColaFlow.Modules.ProjectManagement.Application.Queries.GetTasksByProjectId;
|
||||
|
||||
namespace ColaFlow.API.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Tasks API Controller
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/v1")]
|
||||
public class TasksController : ControllerBase
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public TasksController(IMediator mediator)
|
||||
{
|
||||
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get task by ID
|
||||
/// </summary>
|
||||
[HttpGet("tasks/{id:guid}")]
|
||||
[ProducesResponseType(typeof(TaskDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> GetTask(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = new GetTaskByIdQuery(id);
|
||||
var result = await _mediator.Send(query, cancellationToken);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all tasks for a story
|
||||
/// </summary>
|
||||
[HttpGet("stories/{storyId:guid}/tasks")]
|
||||
[ProducesResponseType(typeof(List<TaskDto>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> GetStoryTasks(Guid storyId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = new GetTasksByStoryIdQuery(storyId);
|
||||
var result = await _mediator.Send(query, cancellationToken);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all tasks for a project (for Kanban board)
|
||||
/// </summary>
|
||||
[HttpGet("projects/{projectId:guid}/tasks")]
|
||||
[ProducesResponseType(typeof(List<TaskDto>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> GetProjectTasks(
|
||||
Guid projectId,
|
||||
[FromQuery] string? status = null,
|
||||
[FromQuery] Guid? assigneeId = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = new GetTasksByProjectIdQuery
|
||||
{
|
||||
ProjectId = projectId,
|
||||
Status = status,
|
||||
AssigneeId = assigneeId
|
||||
};
|
||||
var result = await _mediator.Send(query, cancellationToken);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new task
|
||||
/// </summary>
|
||||
[HttpPost("stories/{storyId:guid}/tasks")]
|
||||
[ProducesResponseType(typeof(TaskDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> CreateTask(
|
||||
Guid storyId,
|
||||
[FromBody] CreateTaskRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var command = new CreateTaskCommand
|
||||
{
|
||||
StoryId = storyId,
|
||||
Title = request.Title,
|
||||
Description = request.Description,
|
||||
Priority = request.Priority,
|
||||
EstimatedHours = request.EstimatedHours,
|
||||
AssigneeId = request.AssigneeId,
|
||||
CreatedBy = request.CreatedBy
|
||||
};
|
||||
|
||||
var result = await _mediator.Send(command, cancellationToken);
|
||||
return CreatedAtAction(nameof(GetTask), new { id = result.Id }, result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update an existing task
|
||||
/// </summary>
|
||||
[HttpPut("tasks/{id:guid}")]
|
||||
[ProducesResponseType(typeof(TaskDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> UpdateTask(
|
||||
Guid id,
|
||||
[FromBody] UpdateTaskRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var command = new UpdateTaskCommand
|
||||
{
|
||||
TaskId = id,
|
||||
Title = request.Title,
|
||||
Description = request.Description,
|
||||
Status = request.Status,
|
||||
Priority = request.Priority,
|
||||
EstimatedHours = request.EstimatedHours,
|
||||
AssigneeId = request.AssigneeId
|
||||
};
|
||||
|
||||
var result = await _mediator.Send(command, cancellationToken);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete a task
|
||||
/// </summary>
|
||||
[HttpDelete("tasks/{id:guid}")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
public async Task<IActionResult> DeleteTask(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var command = new DeleteTaskCommand { TaskId = id };
|
||||
await _mediator.Send(command, cancellationToken);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assign a task to a user
|
||||
/// </summary>
|
||||
[HttpPut("tasks/{id:guid}/assign")]
|
||||
[ProducesResponseType(typeof(TaskDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> AssignTask(
|
||||
Guid id,
|
||||
[FromBody] AssignTaskRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var command = new AssignTaskCommand
|
||||
{
|
||||
TaskId = id,
|
||||
AssigneeId = request.AssigneeId
|
||||
};
|
||||
|
||||
var result = await _mediator.Send(command, cancellationToken);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update task status (for Kanban board drag & drop)
|
||||
/// </summary>
|
||||
[HttpPut("tasks/{id:guid}/status")]
|
||||
[ProducesResponseType(typeof(TaskDto), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> UpdateTaskStatus(
|
||||
Guid id,
|
||||
[FromBody] UpdateTaskStatusRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var command = new UpdateTaskStatusCommand
|
||||
{
|
||||
TaskId = id,
|
||||
NewStatus = request.NewStatus
|
||||
};
|
||||
|
||||
var result = await _mediator.Send(command, cancellationToken);
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request model for creating a task
|
||||
/// </summary>
|
||||
public record CreateTaskRequest
|
||||
{
|
||||
public string Title { get; init; } = string.Empty;
|
||||
public string Description { get; init; } = string.Empty;
|
||||
public string Priority { get; init; } = "Medium";
|
||||
public decimal? EstimatedHours { get; init; }
|
||||
public Guid? AssigneeId { get; init; }
|
||||
public Guid CreatedBy { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request model for updating a task
|
||||
/// </summary>
|
||||
public record UpdateTaskRequest
|
||||
{
|
||||
public string Title { get; init; } = string.Empty;
|
||||
public string Description { get; init; } = string.Empty;
|
||||
public string? Status { get; init; }
|
||||
public string? Priority { get; init; }
|
||||
public decimal? EstimatedHours { get; init; }
|
||||
public Guid? AssigneeId { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request model for assigning a task
|
||||
/// </summary>
|
||||
public record AssignTaskRequest
|
||||
{
|
||||
public Guid? AssigneeId { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request model for updating task status
|
||||
/// </summary>
|
||||
public record UpdateTaskStatusRequest
|
||||
{
|
||||
public string NewStatus { get; init; } = string.Empty;
|
||||
}
|
||||
Reference in New Issue
Block a user