Add TodosController with GET/POST/PATCH/DELETE endpoints
This commit is contained in:
77
backend/Controllers/TodosController.cs
Normal file
77
backend/Controllers/TodosController.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using System.Security.Claims;
|
||||
using Backend.Data;
|
||||
using Backend.Dtos;
|
||||
using Backend.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Backend.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/todos")]
|
||||
[Authorize]
|
||||
public class TodosController(AppDbContext db) : ControllerBase
|
||||
{
|
||||
private Guid CurrentUserId()
|
||||
{
|
||||
var sub = User.FindFirstValue("sub")
|
||||
?? throw new InvalidOperationException("Authenticated request missing 'sub' claim");
|
||||
return Guid.Parse(sub);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<IEnumerable<TodoDto>>> List()
|
||||
{
|
||||
var userId = CurrentUserId();
|
||||
var todos = await db.Todos
|
||||
.Where(t => t.UserId == userId)
|
||||
.OrderByDescending(t => t.CreatedAt)
|
||||
.Select(t => new TodoDto(t.Id, t.Title, t.Completed, t.CreatedAt))
|
||||
.ToListAsync();
|
||||
return Ok(todos);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<TodoDto>> Create([FromBody] CreateTodoRequest body)
|
||||
{
|
||||
if (!ModelState.IsValid) return ValidationProblem(ModelState);
|
||||
|
||||
var userId = CurrentUserId();
|
||||
var todo = new Todo
|
||||
{
|
||||
UserId = userId,
|
||||
Title = body.Title,
|
||||
Completed = false,
|
||||
CreatedAt = DateTimeOffset.UtcNow
|
||||
};
|
||||
db.Todos.Add(todo);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
var dto = new TodoDto(todo.Id, todo.Title, todo.Completed, todo.CreatedAt);
|
||||
return CreatedAtAction(nameof(List), new { id = todo.Id }, dto);
|
||||
}
|
||||
|
||||
[HttpPatch("{id:long}")]
|
||||
public async Task<ActionResult<TodoDto>> Update(long id, [FromBody] UpdateTodoRequest body)
|
||||
{
|
||||
var userId = CurrentUserId();
|
||||
var todo = await db.Todos.FirstOrDefaultAsync(t => t.Id == id && t.UserId == userId);
|
||||
if (todo is null) return NotFound();
|
||||
|
||||
if (body.Completed.HasValue) todo.Completed = body.Completed.Value;
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
return Ok(new TodoDto(todo.Id, todo.Title, todo.Completed, todo.CreatedAt));
|
||||
}
|
||||
|
||||
[HttpDelete("{id:long}")]
|
||||
public async Task<IActionResult> Delete(long id)
|
||||
{
|
||||
var userId = CurrentUserId();
|
||||
var affected = await db.Todos
|
||||
.Where(t => t.Id == id && t.UserId == userId)
|
||||
.ExecuteDeleteAsync();
|
||||
return affected == 0 ? NotFound() : NoContent();
|
||||
}
|
||||
}
|
||||
5
backend/Dtos/CreateTodoRequest.cs
Normal file
5
backend/Dtos/CreateTodoRequest.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Backend.Dtos;
|
||||
|
||||
public record CreateTodoRequest([Required, StringLength(500, MinimumLength = 1)] string Title);
|
||||
3
backend/Dtos/TodoDto.cs
Normal file
3
backend/Dtos/TodoDto.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace Backend.Dtos;
|
||||
|
||||
public record TodoDto(long Id, string Title, bool Completed, DateTimeOffset CreatedAt);
|
||||
3
backend/Dtos/UpdateTodoRequest.cs
Normal file
3
backend/Dtos/UpdateTodoRequest.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace Backend.Dtos;
|
||||
|
||||
public record UpdateTodoRequest(bool? Completed);
|
||||
Reference in New Issue
Block a user