using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Users.APP.Features.Groups;
namespace Users.API.Controllers
{
/// <summary>
/// API controller for managing group-related operations.
/// Handles HTTP requests for inserting, updating, deleting or retrieving group data.
/// </summary>
[Route("api/[controller]")]
[ApiController]
// Way 1:
//[Authorize(Roles = "Admin,User")] // Only authenticated users with Admin or User role can execute all of the actions of this controller.
// Way 2:
[Authorize] // Only authenticated users can execute all of the actions of this controller.
// Since we have only 2 roles Admin and User, we can use Authorize to check auhenticated users without defining roles.
public class GroupsController : ControllerBase
{
private readonly IMediator _mediator;
/// <summary>
/// Initializes a new instance of the <see cref="GroupsController"/> class.
/// </summary>
/// <param name="mediator">
/// The <see cref="IMediator"/> instance used to send requests to MediatR handlers.
/// Enables decoupled request/response processing for queries and commands.
/// </param>
public GroupsController(IMediator mediator)
{
_mediator = mediator;
}
/// <summary>
/// Handles HTTP GET requests to retrieve all groups as <see cref="GroupQueryResponse"/> list.
/// </summary>
/// <returns>
/// An <see cref="IActionResult"/> containing a list of group query response data.
/// Returns HTTP 200 OK with the list of groups on success.
/// </returns>
/// <remarks>
/// This action uses MediatR to send a <see cref="GroupQueryRequest"/> to its handler (<see cref="GroupQueryHandler"/>).
/// The handler returns an <see cref="IQueryable{GroupQueryResponse}"/>, which is then
/// asynchronously materialized into a list using Entity Framework Core's <c>ToListAsync</c>.
/// The result is returned as a JSON array in the HTTP response body.
/// </remarks>
[HttpGet] // HttpGet: attribute also called action method, get route: /Groups
//[AllowAnonymous] // Can be used to allow authenticated and unauthenticated users (everyone) to execute this action.
// Overrides the Authorize defined for the controller.
public async Task<IActionResult> Get()
{
// Send a GroupQueryRequest to MediatR, which dispatches it to the appropriate handler (GroupQueryHandler).
var query = await _mediator.Send(new GroupQueryRequest());
// Execute the query and retrieve the results as a list asynchronously.
var list = await query.ToListAsync();
// Return the list of groups with HTTP 200 OK.
return Ok(list);
/*
ActionResult inheritance:
IActionResult: general return type of actions in a controller
|
ActionResult: base class that implements IActionResult
|
OkObjectResult (returned by Ok method) - NotFoundResult (returned by NotFound method) -
BadRequestObjectResult (returned by BadRequest method) - etc.
*/
}
/// <summary>
/// Handles HTTP GET requests to retrieve a single group by its unique identifier.
/// </summary>
/// <param name="id">The integer unique identifier of the group item to retrieve.</param>
/// <returns>
/// An <see cref="IActionResult"/> containing an item of group query response data if found.
/// Returns HTTP 200 OK with the group data if the group exists, or HTTP 404 Not Found if no group matches the specified ID.
/// </returns>
/// <remarks>
/// This action uses MediatR to send a <see cref="GroupQueryRequest"/> to its handler (<see cref="GroupQueryHandler"/>).
/// The handler returns an <see cref="IQueryable{GroupQueryResponse}"/> representing all groups.
/// The method then filters this query to find a group with the specified <paramref name="id"/>
/// using Entity Framework Core's <c>SingleOrDefaultAsync</c>.
/// If a matching group is found, it is returned as a JSON object with HTTP 200 OK.
/// If no group is found, HTTP 404 Not Found is returned.
/// </remarks>
[HttpGet("{id}")] // get route: /Groups/5 (name defined in {} must be same as the action's parameter name, id will be 5)
// Only authenticated users (since Authorize is defined at the controller level) can execute this action.
public async Task<IActionResult> Get(int id)
{
// Send a GroupQueryRequest to MediatR, which dispatches it to the appropriate handler (GroupQueryHandler).
// The handler returns an IQueryable<GroupQueryResponse> representing all groups.
var query = await _mediator.Send(new GroupQueryRequest());
// Asynchronously find the group with the specified ID.
// If no group matches, item will be null.
var item = await query.SingleOrDefaultAsync(groupResponse => groupResponse.Id == id);
// If the group is not found, return HTTP 404 Not Found.
if (item is null)
return NotFound();
// If the group is found, return it with HTTP 200 OK.
return Ok(item);
}
/// <summary>
/// Handles HTTP POST requests to create a new group.
/// </summary>
/// <param name="request">The <see cref="GroupCreateRequest"/> containing the Title data for the new group.</param>
/// <returns>
/// An <see cref="IActionResult"/> indicating the result of the create operation.
/// - Returns HTTP 201 Created Status Code with the location of the new group if successful.
/// - Returns HTTP 400 Bad Request Status Code with validation or business error details if unsuccessful.
/// </returns>
/// <remarks>
/// This action receives a group creation request from the client and first checks if the model state is valid
/// (i.e., all data annotations are satisfied). If valid, it sends the request to the MediatR pipeline,
/// which dispatches it to the appropriate handler (<see cref="GroupCreateHandler"/>).
/// The handler processes the creation logic and returns a <see cref="CORE.APP.Models.CommandResponse"/> indicating success or failure.
/// - If the operation is successful, the method returns HTTP 201 Created, using <c>CreatedAtAction</c> to provide the new group.
/// - If the operation fails (e.g., duplicate group title), it returns HTTP 400 Bad Request with the error message.
/// - If the model state is invalid, it returns HTTP 400 Bad Request with validation errors.
/// </remarks>
[HttpPost] // post route: /Groups
[Authorize(Roles = "Admin")] // Only authenticated users with role Admin can execute this action.
// Overrides the Authorize defined for the controller.
public async Task<IActionResult> Post(GroupCreateRequest request)
{
// Check if the incoming request model passes validations through data annotations.
if (ModelState.IsValid)
{
// Send the creation request to MediatR, which will route it to the appropriate handler (GroupCreateHandler).
var response = await _mediator.Send(request);
// If the group was created successfully
if (response.IsSuccessful)
{
// Way 1: return HTTP 200 OK with the success command response.
//return Ok(response);
// Way 2: return HTTP 201 Created with the location of the new group and the success command response.
return CreatedAtAction(nameof(Get), new { id = response.Id }, response);
}
// If the creation failed due to business logic (e.g., duplicate title), return HTTP 400 Bad Request with the command response.
return BadRequest(response);
}
// If the model state is invalid, return HTTP 400 Bad Request with validation error details.
return BadRequest(ModelState);
}
/// <summary>
/// Handles HTTP PUT requests to update an existing group.
/// </summary>
/// <param name="request">The <see cref="GroupUpdateRequest"/> containing the group ID and the new title.</param>
/// <returns>
/// An <see cref="IActionResult"/> indicating the result of the update operation.
/// - Returns HTTP 204 No Content Status Code if the update is successful.
/// - Returns HTTP 400 Bad Request Status Code with validation or business error details if unsuccessful.
/// </returns>
/// <remarks>
/// This action receives a group update request from the client and first checks if the model state is valid
/// (i.e., all data annotations are satisfied). If valid, it sends the request to the MediatR pipeline,
/// which dispatches it to the appropriate handler (<see cref="GroupUpdateHandler"/>).
/// The handler processes the update logic and returns a <see cref="CORE.APP.Models.CommandResponse"/> indicating success or failure.
/// - If the operation is successful, the method returns HTTP 204 No Content, indicating the update was successful but no content is returned.
/// - If the operation fails (e.g., duplicate group title or group not found), it returns HTTP 400 Bad Request with the command response.
/// - If the model state is invalid, it returns HTTP 400 Bad Request with validation errors.
/// </remarks>
[HttpPut] // put route: /Groups
[Authorize(Roles = "Admin")] // Only authenticated users with role Admin can execute this action.
// Overrides the Authorize defined for the controller.
public async Task<IActionResult> Put(GroupUpdateRequest request)
{
// Check if the incoming request model passes validations through data annotations.
if (ModelState.IsValid)
{
// Send the update request to MediatR, which will route it to the appropriate handler (GroupUpdateHandler).
var response = await _mediator.Send(request);
// If the group was updated successfully
if (response.IsSuccessful)
{
// Way 1: return HTTP 200 OK with the success command response.
//return Ok(response);
// Way 2: return HTTP 204 No Content (no response body).
return NoContent();
}
// If the update failed due to business logic (e.g., duplicate title or group not found), return HTTP 400 Bad Request with the command response.
return BadRequest(response);
}
// If the model state is invalid, return HTTP 400 Bad Request with validation error details.
return BadRequest(ModelState);
}
/// <summary>
/// Handles HTTP DELETE requests to remove a group by its unique identifier.
/// </summary>
/// <param name="id">
/// The integer unique identifier of the group to delete. This value is provided in the route (e.g., /Groups/5).
/// </param>
/// <returns>
/// An <see cref="IActionResult"/> indicating the result of the delete operation:
/// - Returns HTTP 204 No Content Status Code if the group was deleted successfully.
/// - Returns HTTP 400 Bad Request Status Code with error details if the deletion failed (e.g., group not found, relational records found).
/// </returns>
/// <remarks>
/// This action uses MediatR to send a <see cref="GroupDeleteRequest"/> to its handler.
/// The handler performs the deletion logic and returns a <see cref="CORE.APP.Models.CommandResponse"/> indicating success or failure.
/// - If the operation is successful, the method returns HTTP 204 No Content, indicating the group was deleted.
/// - If the operation fails (e.g., group does not exist, relational records found), it returns HTTP 400 Bad Request with error command response.
/// </remarks>
[HttpDelete("{id}")] // delete route: /Groups/5 (name defined in {} must be same as the action's parameter name, id will be 5)
[Authorize(Roles = "Admin")] // Only authenticated users with role Admin can execute this action.
// Overrides the Authorize defined for the controller.
public async Task<IActionResult> Delete(int id)
{
// Create a GroupDeleteRequest with the specified group ID and send it to MediatR.
// MediatR dispatches the request to the appropriate handler (GroupDeleteHandler).
var response = await _mediator.Send(new GroupDeleteRequest() { Id = id });
// If group was deleted successfully
if (response.IsSuccessful)
{
// Way 1: return HTTP 200 OK with the success command response.
// return Ok(response);
// Way 2: return HTTP 204 No Content (no response body).
return NoContent();
}
// If the deletion failed (e.g., group not found, relational records found), return HTTP 400 Bad Request with error details.
return BadRequest(response);
}
}
}