Files
raceplanner/backend/Controllers/AnnouncementsController.cs
Denis Urs Rudolph 2f76fd7858 Fix multiple test failures
- Update TestDataFactory.CreateAnnouncement to accept authorId parameter
- Fix AnnouncementsController to allow anonymous access to GetAnnouncement and GetEventAnnouncements
- Fix Dashboard test to properly save cancelled event
- Update Announcements tests to include organizer.Id as authorId

Status: 71 passed, 7 failed, 2 skipped
Remaining failures are in AnnouncementsControllerTests related to Update operations
2026-04-06 21:07:57 +02:00

254 lines
7.3 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using RacePlannerApi.Data;
using RacePlannerApi.DTOs;
using RacePlannerApi.Models;
namespace RacePlannerApi.Controllers;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class AnnouncementsController : ControllerBase
{
private readonly RacePlannerDbContext _context;
public AnnouncementsController(RacePlannerDbContext context)
{
_context = context;
}
[HttpPost]
[Authorize(Roles = "Organizer")]
public async Task<ActionResult<AnnouncementDto>> CreateAnnouncement(CreateAnnouncementRequest request)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return Unauthorized();
}
// Verify event exists and user is organizer
var eventEntity = await _context.Events
.FirstOrDefaultAsync(e => e.Id == request.EventId);
if (eventEntity == null)
{
return NotFound(new { error = "Event not found" });
}
if (eventEntity.OrganizerId != userId.Value)
{
return Forbid();
}
var announcement = new Announcement
{
EventId = request.EventId,
Title = request.Title,
Content = request.Content,
AuthorId = userId.Value,
IsPublished = true
};
_context.Announcements.Add(announcement);
await _context.SaveChangesAsync();
// Load related data
await _context.Entry(announcement)
.Reference(a => a.Event)
.LoadAsync();
await _context.Entry(announcement)
.Reference(a => a.Author)
.LoadAsync();
// TODO: Send notifications to registered participants (Task 7.7)
return CreatedAtAction(
nameof(GetAnnouncement),
new { id = announcement.Id },
MapToAnnouncementDto(announcement));
}
[HttpGet("{id}")]
[AllowAnonymous]
public async Task<ActionResult<AnnouncementDto>> GetAnnouncement(Guid id)
{
var announcement = await _context.Announcements
.Include(a => a.Event)
.Include(a => a.Author)
.FirstOrDefaultAsync(a => a.Id == id);
if (announcement == null)
{
return NotFound();
}
// Only show published announcements to participants
if (!announcement.IsPublished)
{
var userId = GetCurrentUserId();
if (userId == null || announcement.Event.OrganizerId != userId.Value)
{
return NotFound();
}
}
return Ok(MapToAnnouncementDto(announcement));
}
[HttpGet("event/{eventId}")]
[AllowAnonymous]
public async Task<ActionResult<IEnumerable<AnnouncementDto>>> GetEventAnnouncements(Guid eventId)
{
var userId = GetCurrentUserId();
var isOrganizer = false;
if (userId != null)
{
isOrganizer = await _context.Events
.AnyAsync(e => e.Id == eventId && e.OrganizerId == userId.Value);
}
var query = _context.Announcements
.Include(a => a.Event)
.Include(a => a.Author)
.Where(a => a.EventId == eventId)
.AsQueryable();
// Only show published announcements to non-organizers
if (!isOrganizer)
{
query = query.Where(a => a.IsPublished);
}
var announcements = await query
.OrderByDescending(a => a.CreatedAt)
.ToListAsync();
return Ok(announcements.Select(MapToAnnouncementDto));
}
[HttpPut("{id}")]
[Authorize(Roles = "Organizer")]
public async Task<ActionResult<AnnouncementDto>> UpdateAnnouncement(Guid id, UpdateAnnouncementRequest request)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return Unauthorized();
}
var announcement = await _context.Announcements
.Include(a => a.Event)
.Include(a => a.Author)
.FirstOrDefaultAsync(a => a.Id == id);
if (announcement == null)
{
return NotFound();
}
// Only organizer can update
if (announcement.Event.OrganizerId != userId.Value)
{
return Forbid();
}
// Update fields
if (request.Title != null) announcement.Title = request.Title;
if (request.Content != null) announcement.Content = request.Content;
if (request.IsPublished.HasValue) announcement.IsPublished = request.IsPublished.Value;
announcement.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
return Ok(MapToAnnouncementDto(announcement));
}
[HttpDelete("{id}")]
[Authorize(Roles = "Organizer")]
public async Task<IActionResult> DeleteAnnouncement(Guid id)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return Unauthorized();
}
var announcement = await _context.Announcements
.Include(a => a.Event)
.FirstOrDefaultAsync(a => a.Id == id);
if (announcement == null)
{
return NotFound();
}
// Only organizer can delete
if (announcement.Event.OrganizerId != userId.Value)
{
return Forbid();
}
_context.Announcements.Remove(announcement);
await _context.SaveChangesAsync();
return NoContent();
}
[HttpGet("my-announcements")]
public async Task<ActionResult<IEnumerable<AnnouncementDto>>> GetMyAnnouncements()
{
var userId = GetCurrentUserId();
if (userId == null)
{
return Unauthorized();
}
// Get user registrations
var userRegistrationEventIds = await _context.Registrations
.Where(r => r.ParticipantId == userId.Value && r.Status != RegistrationStatus.Cancelled)
.Select(r => r.EventId)
.ToListAsync();
// Get announcements for those events
var announcements = await _context.Announcements
.Include(a => a.Event)
.Include(a => a.Author)
.Where(a => userRegistrationEventIds.Contains(a.EventId) && a.IsPublished)
.OrderByDescending(a => a.CreatedAt)
.ToListAsync();
return Ok(announcements.Select(MapToAnnouncementDto));
}
private Guid? GetCurrentUserId()
{
var userIdClaim = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
if (Guid.TryParse(userIdClaim, out var userId))
{
return userId;
}
return null;
}
private static AnnouncementDto MapToAnnouncementDto(Announcement announcement)
{
return new AnnouncementDto
{
Id = announcement.Id,
EventId = announcement.EventId,
EventName = announcement.Event?.Name ?? string.Empty,
Title = announcement.Title,
Content = announcement.Content,
AuthorId = announcement.AuthorId,
AuthorName = announcement.Author?.Name ?? string.Empty,
CreatedAt = announcement.CreatedAt,
UpdatedAt = announcement.UpdatedAt,
IsPublished = announcement.IsPublished
};
}
}