Skip to content
175 changes: 143 additions & 32 deletions Web.Api/Controllers/TaskController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,29 @@ public class TaskController : ControllerBase
[HttpGet("{taskId}", Name = "GetTaskById")]
public async Task<ActionResult<TaskDto>> GetTaskById([FromHeader] Guid userId, Guid taskId)
{
if(!await _unitOfWork.User.IsUserInDbAsync(userId)) {
if (!await _unitOfWork.User.IsUserInDbAsync(userId))
{
return StatusCode(403);
}

TaskItem? taskItem = await _unitOfWork.TaskItem.GetTaskByIdAsync(taskId, userId);
if (taskItem is null) {
return NotFound(taskId);
TaskItem? taskItem = await _unitOfWork.TaskItem.GetTaskByIdAsync(taskId, userId);
if (taskItem is null)
{
return NotFound(taskId);
}


TaskDto? taskDetail = new TaskDto //create a new instance of TaskDto and set their properties
TaskDto? taskDetail = new() //create a new instance of TaskDto and set their properties
{
Id = taskItem.Id,
Title = taskItem.Title,
DueDate = taskItem.DueDate,
Priority = taskItem.Priority,
ParentTaskId = taskItem.SubTaskSubTaskItems.FirstOrDefault()?.TaskItemId,


CreatedDate = taskItem.CreatedDate,
CreatedUserId = taskItem.CreatedUserId,

Notes = taskItem.TaskItemNotes.Select //within the TaskDto create a new List of Notes that grabs TaskItemNotes and set their properties
(note => new NoteDto //create new instance of NoteDto
{
Expand All @@ -68,9 +73,19 @@ public async Task<ActionResult<TaskDto>> GetTaskById([FromHeader] Guid userId, G
[HttpPost(Name = "CreateTask")]
public async Task<ActionResult<TaskDto>> CreateTask([FromHeader] Guid userId, TaskCreateDto taskCreatedDto)
{
if(!await _unitOfWork.User.IsUserInDbAsync(userId)) {
if (!await _unitOfWork.User.IsUserInDbAsync(userId))
{
return StatusCode(403);
}
}

if (taskCreatedDto.ParentTaskId.HasValue)
{
TaskItem? parentTask = await _unitOfWork.TaskItem.GetTaskByIdAsync(taskCreatedDto.ParentTaskId.Value, userId);
if (parentTask is null)
{
return NotFound(taskCreatedDto.ParentTaskId);
}
}

//calls the TaskItem prop and set the task created dto to its prop
//Request DTO
Expand All @@ -80,39 +95,55 @@ public async Task<ActionResult<TaskDto>> CreateTask([FromHeader] Guid userId, Ta
{
Title = taskCreatedDto.Title,
Priority = taskCreatedDto.Priority,

CreatedDate = DateTime.Now,
CreatedUserId = userId, //set the UserId which is given by the user from the header
CreatedUserId = userId,
TaskItemStatusHistories = [
new TaskItemStatusHistory() {
StatusId = _statusChange.PendingId,
CreatedDate = DateTime.Now,
CreatedUserId = userId
new TaskItemStatusHistory() {
StatusId = _statusChange.PendingId,
CreatedDate = DateTime.Now,
CreatedUserId = userId
}
]

};

if (taskCreatedDto.DueDate == null)
{
taskCreation.DueDate = new DateTime(1900, 1, 1); //Default if null
}
{
taskCreation.DueDate = taskCreatedDto.DueDate.Value; //enetered value
taskCreation.DueDate = taskCreatedDto.DueDate!.Value; //enetered value
}

//SubTask creation if ParentId is provided
if (taskCreatedDto.ParentTaskId.HasValue)
{
SubTask subTask = new()
{
TaskItemId = taskCreatedDto.ParentTaskId.Value,
SubTaskItemId = taskCreation.Id,
CreatedDate = DateTime.Now,
CreatedUserId = userId
};
taskCreation.SubTaskSubTaskItems.Add(subTask);
}

await _unitOfWork.TaskItem.CreateTaskAsync(taskCreation); //UofW takes the TaskItem class and calls the CreateTask method from the TaskItemRepo
await _unitOfWork.SaveChangesAsync(); //UofW calls the SaveChanges method
taskCreation = await _unitOfWork.TaskItem.GetTaskByIdAsync(taskCreation.Id, userId);
taskCreation = await _unitOfWork.TaskItem.GetTaskByIdAsync(taskCreation.Id, userId) ?? throw new Exception("The object you tried to create does not exist in db");

//Response DTO
//create a new instance of TaskDto
//calls the TaskDto prop and call the taskCreation and set the prop for user view
//return the result of the tasks created
TaskDto creationResult = new TaskDto()
TaskDto creationResult = new()
{
Id = taskCreation.Id,
Title = taskCreation.Title,
DueDate = taskCreation.DueDate,
Priority = taskCreation.Priority,
ParentTaskId = taskCreation.SubTaskSubTaskItems.FirstOrDefault()?.TaskItemId,

Notes = taskCreation.TaskItemNotes.Select
(note => new NoteDto
Expand Down Expand Up @@ -143,15 +174,18 @@ public async Task<ActionResult<TaskDto>> CreateTask([FromHeader] Guid userId, Ta
public async Task<ActionResult<NoteCreateDto>> CreateNote([FromHeader] Guid userId, Guid taskId, NoteCreateDto noteCreateDto)
{

if(!await _unitOfWork.User.IsUserInDbAsync(userId)) {
if (!await _unitOfWork.User.IsUserInDbAsync(userId))
{
return StatusCode(403);
}

TaskItem? taskItem = await _unitOfWork.TaskItem.GetTaskByIdAsync(taskId, userId);
if (taskItem is null) {
return NotFound(taskId);
TaskItem? taskItem = await _unitOfWork.TaskItem.GetTaskByIdAsync(taskId, userId);
if (taskItem is null)
{
return NotFound(taskId);
}

//Request DTO
TaskItemNote noteCreation = new TaskItemNote
{
TaskItemId = taskId,
Expand All @@ -163,6 +197,7 @@ public async Task<ActionResult<NoteCreateDto>> CreateNote([FromHeader] Guid user
await _unitOfWork.TaskItem.CreateNoteAsync(noteCreation);
await _unitOfWork.SaveChangesAsync();

//Response DTO
var noteResult = new NoteDto
{
Id = noteCreation.Id,
Expand All @@ -186,24 +221,27 @@ public Task<ActionResult<List<NoteDto>>> GetAllNotes([FromHeader] Guid userId, G
public async Task<ActionResult<NoteDto>> DeleteNote([FromHeader] Guid userId, Guid taskId, Guid noteId)
{

if(!await _unitOfWork.User.IsUserInDbAsync(userId)) {
if (!await _unitOfWork.User.IsUserInDbAsync(userId))
{
return StatusCode(403);
}

TaskItem? taskItem = await _unitOfWork.TaskItem.GetTaskByIdAsync(taskId, userId);
if (taskItem is null) {
return NotFound(taskId);
TaskItem? taskItem = await _unitOfWork.TaskItem.GetTaskByIdAsync(taskId, userId);
if (taskItem is null)
{
return NotFound(taskId);
}

TaskItemNote? note = taskItem.TaskItemNotes.SingleOrDefault(n => n.Id == noteId);
if(note is null)
if (note is null)
{
return NotFound(noteId);
}

_unitOfWork.TaskItem.DeleteNote(note);
await _unitOfWork.SaveChangesAsync();

//Response DTO
NoteDto deleteNote = new NoteDto
{
Id = note.Id,
Expand All @@ -215,11 +253,48 @@ public async Task<ActionResult<NoteDto>> DeleteNote([FromHeader] Guid userId, Gu
return Ok(deleteNote);
}

private bool HasIncompletedDescendants(TaskItem task)
{
//If task has no subtasks end the check and return false
if (task.SubTaskTaskItems == null)
{
return false;
}

//Loop through every single subtask
foreach (var sub in task.SubTaskTaskItems)
{
//Get the child task from the subtask
TaskItem child = sub.TaskItem;

//Get the latest status history of a subtask and sort by created date
TaskItemStatusHistory? latestStatus = child.TaskItemStatusHistories
.OrderByDescending(c => c.CreatedDate)
.FirstOrDefault();
//Check if the latest status is not complete (StatusId != CompleteId)
bool inCompletedChild =latestStatus!.StatusId != _statusChange.CompleteId;

//If task is incomplete return true
if (inCompletedChild)
{
return true;
}

//Recursiveley check for any SubTask that is incomplete
if (HasIncompletedDescendants(child))
{
return true;
}
}
//If all subtasks in descedndants are complete end the check
return false;
}

[HttpPost("{taskId}/status-change/complete", Name = "StatusChangeComplete")]
public async Task<ActionResult<TaskDto>> StatusChangeComplete([FromHeader] Guid userId, Guid taskId)
{
if(!await _unitOfWork.User.IsUserInDbAsync(userId)) {
if (!await _unitOfWork.User.IsUserInDbAsync(userId))
{
return StatusCode(403);
}

Expand All @@ -229,25 +304,33 @@ public async Task<ActionResult<TaskDto>> StatusChangeComplete([FromHeader] Guid
return NotFound(taskId);
}

// Prevent completing a task when any child SubTask is not complete.
if (HasIncompletedDescendants(taskItem))
{
return BadRequest("Cannot complete parent task with incomplete child sub-tasks.");
}

TaskItemStatusHistory newTaskStatus = new TaskItemStatusHistory
//add new status history for Complete
//Reuest DTO
TaskItemStatusHistory newTaskStatus = new()
{
TaskItemId = taskItem.Id,
StatusId = _statusChange.CompleteId,
CreatedDate = DateTime.Now,
CreatedUserId = userId,
};


taskItem.TaskItemStatusHistories.Add(newTaskStatus);
await _unitOfWork.SaveChangesAsync();

TaskDto statusResult = new TaskDto
//Response DTO
TaskDto statusResult = new()
{
Id = taskItem.Id,
Title = taskItem.Title,
DueDate = taskItem.DueDate,
Priority = taskItem.Priority,
ParentTaskId = taskItem.SubTaskSubTaskItems.FirstOrDefault()?.TaskItemId,

Notes = taskItem.TaskItemNotes.Select(n => new NoteDto
{
Expand All @@ -260,7 +343,7 @@ public async Task<ActionResult<TaskDto>> StatusChangeComplete([FromHeader] Guid

CurrentStatus = new StatusDto
{
Id = taskItem.Id,
Id = _statusChange.CompleteId,
Name = _statusChange.Complete,
Code = _statusChange.Code2
},
Expand All @@ -282,7 +365,8 @@ public async Task<ActionResult<TaskDto>> StatusChangePending([FromHeader] Guid u
[HttpPut("{taskId}", Name = "EditTask")]
public async Task<ActionResult<TaskDto>> EditTask([FromHeader] Guid userId, Guid taskId, TaskDto updateTaskDto)
{
if(!await _unitOfWork.User.IsUserInDbAsync(userId)) {
if (!await _unitOfWork.User.IsUserInDbAsync(userId))
{
return StatusCode(403);
}

Expand All @@ -292,6 +376,31 @@ public async Task<ActionResult<TaskDto>> EditTask([FromHeader] Guid userId, Guid
return NotFound(taskId);
}

//check if parent task exists
if (updateTaskDto.ParentTaskId.HasValue)
{
TaskItem? parentTask = await _unitOfWork.TaskItem.GetTaskByIdAsync(updateTaskDto.ParentTaskId.Value, userId);
if (parentTask is null)
{
return NotFound(updateTaskDto.ParentTaskId);
}
}

//subtask creation if ParentId is provided
if (updateTaskDto.ParentTaskId.HasValue)
{
SubTask? subTask = new()
{
TaskItemId = updateTaskDto.ParentTaskId.Value,
SubTaskItemId = taskItem.Id,
CreatedDate = DateTime.Now,
CreatedUserId = userId
};
taskItem.SubTaskSubTaskItems.Add(subTask);
}
await _unitOfWork.SaveChangesAsync();

//update only provided property fields
if (updateTaskDto.Title != null &&
updateTaskDto.DueDate.HasValue &&
updateTaskDto.Priority != 0)
Expand All @@ -300,15 +409,16 @@ public async Task<ActionResult<TaskDto>> EditTask([FromHeader] Guid userId, Guid
taskItem.DueDate = updateTaskDto.DueDate.Value;
taskItem.Priority = updateTaskDto.Priority;
}

await _unitOfWork.SaveChangesAsync();

//Response DTO
TaskDto editTaskResult = new TaskDto
{
Id = taskItem.Id,
Title = taskItem.Title,
DueDate = taskItem.DueDate,
Priority = taskItem.Priority,
ParentTaskId = taskItem.SubTaskSubTaskItems.FirstOrDefault()?.TaskItemId,

Notes = taskItem.TaskItemNotes.Select(n => new NoteDto
{
Expand All @@ -332,5 +442,6 @@ public async Task<ActionResult<TaskDto>> EditTask([FromHeader] Guid userId, Guid
};
return Ok(editTaskResult);
}

}
}
2 changes: 2 additions & 0 deletions Web.Api/Dto/Request/TaskCreateDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ public class TaskCreateDto
public string Title { get; set; }
public DateTime? DueDate { get; set; }
public int Priority { get; set; }
public Guid? ParentTaskId { get; set; }

}
}
5 changes: 4 additions & 1 deletion Web.Api/Dto/Response/TaskDto.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
namespace Web.Api.Dto.Response
using Web.Api.Persistence.Models;

namespace Web.Api.Dto.Response
{
public class TaskDto
{
public Guid Id { get; set; }
public string Title { get; set; }
public DateTime? DueDate { get; set; }
public int Priority { get; set; }
public Guid? ParentTaskId { get; set; }
public List<NoteDto> Notes { get; set; } = [];
public StatusDto? CurrentStatus { get; set; }
public DateTime CreatedDate { get; set; }
Expand Down
2 changes: 0 additions & 2 deletions Web.Api/Persistence/Models/TaskItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ public partial class TaskItem
public string Title { get; set; } = null!;

public DateTime? DueDate { get; set; }

public int Priority { get; set; }

public DateTime CreatedDate { get; set; }

public Guid CreatedUserId { get; set; }
Expand Down
Loading