using Features.Breweries.Commands.CreateBrewery; using Features.Breweries.Commands.DeleteBrewery; using Features.Breweries.Commands.UpdateBrewery; using Features.Breweries.Dtos; using Features.Breweries.Queries.GetAllBreweries; using Features.Breweries.Queries.GetAllBreweryLocations; using Features.Breweries.Queries.GetBreweryById; using Features.Breweries.Queries.GetBreweryLocationsWithinRange; using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Shared.Contracts; namespace Features.Breweries.Controllers; /// /// Provides HTTP endpoints for reading and managing brewery posts. /// /// /// Requests require JWT authentication unless the endpoint allows anonymous access. /// /// [ApiController] [Route("api/[controller]")] [Authorize(AuthenticationSchemes = "JWT")] public class BreweryController(IMediator mediator) : ControllerBase { /// /// Returns the brewery post identified by . /// /// This endpoint permits anonymous access. /// /// A successful response containing the post, or 404 Not Found when it is absent. /// [AllowAnonymous] [HttpGet("{id:guid}")] public async Task>> GetById(Guid id) { BreweryDto? brewery = await mediator.Send(new GetBreweryByIdQuery(id)); if (brewery is null) return NotFound(new ResponseBody { Message = $"Brewery with ID {id} not found." }); return Ok( new ResponseBody { Message = "Brewery retrieved successfully.", Payload = brewery, } ); } /// /// Returns brewery posts in reverse chronological order. /// /// /// Limits the number of returned posts; leaves it unlimited. /// /// /// Skips this many posts; means none. /// /// This endpoint permits anonymous access. [AllowAnonymous] [HttpGet] public async Task>>> GetAll( [FromQuery] int? limit, [FromQuery] int? offset ) { IEnumerable breweries = await mediator.Send( new GetAllBreweriesQuery(limit, offset) ); return Ok( new ResponseBody> { Message = "Breweries retrieved successfully.", Payload = breweries, } ); } /// /// Returns all brewery posts with an associated location. /// /// This endpoint permits anonymous access. [AllowAnonymous] [HttpGet("locations")] public async Task< ActionResult>> > GetAllLocations() { IEnumerable breweries = await mediator.Send( new GetAllBreweryLocationsQuery() ); return Ok( new ResponseBody> { Message = "Brewery locations retrieved successfully.", Payload = breweries, } ); } /// /// Returns located brewery posts within the requested radius, ordered by proximity. /// /// /// Identifies the origin latitude in decimal degrees. /// /// /// Identifies the origin longitude in decimal degrees. /// /// /// Sets the maximum distance from the origin in metres. /// /// This endpoint permits anonymous access. [AllowAnonymous] [HttpGet("locations/nearby")] public async Task< ActionResult>> > GetLocationsWithinRange( [FromQuery] double latitude, [FromQuery] double longitude, [FromQuery] double rangeInMetres ) { IEnumerable breweries = await mediator.Send( new GetBreweryLocationsWithinRangeQuery(latitude, longitude, rangeInMetres) ); return Ok( new ResponseBody> { Message = "Brewery locations retrieved successfully.", Payload = breweries, } ); } /// /// Creates a brewery post for the authenticated user. /// /// A 201 Created response containing the new post. [HttpPost] public async Task>> Create( [FromBody] CreateBreweryRequest request ) { BreweryDto brewery = await mediator.Send( new CreateBreweryCommand( User.GetAuthenticatedUserId(), request.BreweryName, request.Description, request.Location ) ); return Created( $"/api/brewery/{brewery.BreweryPostId}", new ResponseBody { Message = "Brewery created successfully.", Payload = brewery, } ); } /// /// Updates a brewery post owned by the authenticated user. /// /// /// Must equal the brewery post identifier in . /// /// /// A successful response containing the updated post, or an error for an invalid ID, /// missing resource, or stale row version. /// [HttpPut("{id:guid}")] public async Task>> Update( Guid id, [FromBody] UpdateBreweryRequest request ) { if (request.BreweryPostId != id) return BadRequest(new ResponseBody { Message = "Route ID does not match payload ID." }); BreweryDto breweryUpdated = await mediator.Send( new UpdateBreweryCommand( request.BreweryPostId, User.GetAuthenticatedUserId(), request.RowVersion, request.BreweryName, request.Description, request.Location ) ); return Ok( new ResponseBody { Message = "Brewery updated successfully.", Payload = breweryUpdated, } ); } /// /// Deletes a brewery post owned by the authenticated user. /// /// /// A confirmation response, or 404 Not Found if the post is absent. /// [HttpDelete("{id:guid}")] public async Task> Delete(Guid id) { await mediator.Send(new DeleteBreweryCommand(id, User.GetAuthenticatedUserId())); return Ok(new ResponseBody { Message = "Brewery deleted successfully." }); } }