# Developing the Web API Business Rules

# START FROM PREVIOUS MODULE'S END

Developing the API endpoints

# ADD CROSS ORIGIN RESOURCE SHARING (CORS) TO API PROJECT

# ADD ADDCORS() TO SERVICESCONFIGURATION.CS

public static void AddCORS(this IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy("CorsPolicy",
            builder => builder.AllowAnyOrigin()
                .AllowAnyMethod()
                .AllowAnyHeader());
    });
}

UPDATE STARTUP.CS

using Chinook.API.Configurations;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddConnectionProvider(builder.Configuration);
builder.Services.AddAppSettings(builder.Configuration);
builder.Services.ConfigureRepositories();
builder.Services.ConfigureSupervisor();
builder.Services.ConfigureValidators();
builder.Services.AddAPILogging();
builder.Services.AddCORS();

var app = builder.Build();
app.UseHttpLogging();
app.UseHttpsRedirection();
app.UseCors();
app.MapControllers();

app.Run();

# ADD CORS TO CONTROLLERS

[Route("api/[controller]")]
[ApiController]
[EnableCors("CorsPolicy")]
public class AlbumController : ControllerBase
{

# ADD FLUENTVALIDATION NUGET TO API AND DOMAIN PROJECTS

dos
dotnet add package FluentValidation
dotnet add package FluentValidation.AspNetCore

# ADD VALIDATORS TO DOMAIN PROJECT

using Chinook.Domain.ApiModels;
using FluentValidation;

namespace Chinook.Domain.Validation;

public class AlbumValidator : AbstractValidator<AlbumApiModel>
{
    public AlbumValidator()
    {
        RuleFor(a => a.Title).NotNull();
        RuleFor(a => a.Title).MinimumLength(3);
        RuleFor(a => a.Title).MaximumLength(160);
        RuleFor(a => a.ArtistId).NotNull();
    }
}

# ADD VALIDATORS TO DEPENDENCY INJECTION IN STARTUP IN API PROJECT

public static void ConfigureValidators(this IServiceCollection services)
{
    services.AddFluentValidationAutoValidation()
        .AddTransient<IValidator<AlbumApiModel>, AlbumValidator>()
        .AddTransient<IValidator<ArtistApiModel>, ArtistValidator>()
        .AddTransient<IValidator<CustomerApiModel>, CustomerValidator>()
        .AddTransient<IValidator<EmployeeApiModel>, EmployeeValidator>()
        .AddTransient<IValidator<GenreApiModel>, GenreValidator>()
        .AddTransient<IValidator<InvoiceApiModel>, InvoiceValidator>()
        .AddTransient<IValidator<InvoiceLineApiModel>, InvoiceLineValidator>()
        .AddTransient<IValidator<MediaTypeApiModel>, MediaTypeValidator>()
        .AddTransient<IValidator<PlaylistApiModel>, PlaylistValidator>()
        .AddTransient<IValidator<TrackApiModel>, TrackValidator>();
}

# ADD CONFIGUREVALIDATORS TO CONFIGURESERVICES

builder.Services.ConfigureValidators();

# GET VALIDATORS FROM DI IN SUPERVISOR

public partial class ChinookSupervisor : IChinookSupervisor
{
    private readonly IAlbumRepository _albumRepository;
    private readonly IArtistRepository _artistRepository;
    private readonly ICustomerRepository _customerRepository;
    private readonly IEmployeeRepository _employeeRepository;
    private readonly IGenreRepository _genreRepository;
    private readonly IInvoiceLineRepository _invoiceLineRepository;
    private readonly IInvoiceRepository _invoiceRepository;
    private readonly IMediaTypeRepository _mediaTypeRepository;
    private readonly IPlaylistRepository _playlistRepository;
    private readonly ITrackRepository _trackRepository;

    private readonly IValidator<AlbumApiModel> _albumValidator;
    private readonly IValidator<ArtistApiModel> _artistValidator;
    private readonly IValidator<CustomerApiModel> _customerValidator;
    private readonly IValidator<EmployeeApiModel> _employeeValidator;
    private readonly IValidator<GenreApiModel> _genreValidator;
    private readonly IValidator<InvoiceApiModel> _invoiceValidator;
    private readonly IValidator<InvoiceLineApiModel> _invoiceLineValidator;
    private readonly IValidator<MediaTypeApiModel> _mediaTypeValidator;
    private readonly IValidator<PlaylistApiModel> _playlistValidator;
    private readonly IValidator<TrackApiModel> _trackValidator;

    public ChinookSupervisor(IAlbumRepository albumRepository,
        IArtistRepository artistRepository,
        ICustomerRepository customerRepository,
        IEmployeeRepository employeeRepository,
        IGenreRepository genreRepository,
        IInvoiceLineRepository invoiceLineRepository,
        IInvoiceRepository invoiceRepository,
        IMediaTypeRepository mediaTypeRepository,
        IPlaylistRepository playlistRepository,
        ITrackRepository trackRepository,
        IValidator<AlbumApiModel> albumValidator,
        IValidator<ArtistApiModel> artistValidator,
        IValidator<CustomerApiModel> customerValidator,
        IValidator<EmployeeApiModel> employeeValidator,
        IValidator<GenreApiModel> genreValidator,
        IValidator<InvoiceApiModel> invoiceValidator,
        IValidator<InvoiceLineApiModel> invoiceLineValidator,
        IValidator<MediaTypeApiModel> mediaTypeValidator,
        IValidator<PlaylistApiModel> playlistValidator,
        IValidator<TrackApiModel> trackValidator
    )
    {
        _albumRepository = albumRepository;
        _artistRepository = artistRepository;
        _customerRepository = customerRepository;
        _employeeRepository = employeeRepository;
        _genreRepository = genreRepository;
        _invoiceLineRepository = invoiceLineRepository;
        _invoiceRepository = invoiceRepository;
        _mediaTypeRepository = mediaTypeRepository;
        _playlistRepository = playlistRepository;
        _trackRepository = trackRepository;

        _albumValidator = albumValidator;
        _artistValidator = artistValidator;
        _customerValidator = customerValidator;
        _employeeValidator = employeeValidator;
        _genreValidator = genreValidator;
        _invoiceValidator = invoiceValidator;
        _invoiceLineValidator = invoiceLineValidator;
        _mediaTypeValidator = mediaTypeValidator;
        _playlistValidator = playlistValidator;
        _trackValidator = trackValidator;
    }

# ADD CODE TO CALL VALIDATION IN SUPERVISOR

public async Task<AlbumApiModel> AddAlbum(AlbumApiModel newAlbumApiModel)
{
    await _albumValidator.ValidateAndThrowAsync(newAlbumApiModel);

    var album = newAlbumApiModel.Convert();

    album = await _albumRepository.Add(album);
    newAlbumApiModel.Id = album.Id;
    return newAlbumApiModel;
}

public async Task<bool> UpdateAlbum(AlbumApiModel albumApiModel)
{
    await _albumValidator.ValidateAndThrowAsync(albumApiModel);

    var album = await _albumRepository.GetById(albumApiModel.Id);

    if (album is null) return false;
    album.Id = albumApiModel.Id;
    album.Title = albumApiModel.Title;
    album.ArtistId = albumApiModel.ArtistId;

    return await _albumRepository.Update(album);
}

# ADD ERROR HANDLING IN ACTIONS

[Route("api/[controller]")]
[ApiController]
[EnableCors("CorsPolicy")]
public class AlbumController : ControllerBase
{
    private readonly IChinookSupervisor _chinookSupervisor;
    private readonly ILogger<AlbumController> _logger;

    public AlbumController(IChinookSupervisor chinookSupervisor, ILogger<AlbumController> logger)
    {
        _chinookSupervisor = chinookSupervisor;
        _logger = logger;
    }

    [HttpGet]
    [Produces(typeof(List<AlbumApiModel>))]
    public async Task<ActionResult<List<AlbumApiModel>>> Get()
    {
        try  
        {  
            var albums = await _chinookSupervisor.GetAllAlbum();  

            if (albums.Any())  
            {  
                return Ok(albums);  
            }  
            else  
            {  
                return StatusCode((int)HttpStatusCode.NotFound, "No Albums Could Be Found");  
            }  
        }  
        catch (Exception ex)  
        {  
            _logger.LogError($"Something went wrong inside the AlbumController Get action: {ex}");  
            return StatusCode((int)HttpStatusCode.InternalServerError, "Error occurred while executing Get All Albums");  
        }  
    }

    [HttpGet("{id}", Name = "GetAlbumById")]
    public async Task<ActionResult<AlbumApiModel>> Get(int id)
    {
        try  
        {  
            var album = await _chinookSupervisor.GetAlbumById(id);  

            if (album != null)  
            {  
                return Ok(album);  
            }  
            else  
            {  
                return StatusCode((int)HttpStatusCode.NotFound, "Album Not Found");  
            }  
        }  
        catch (Exception ex)  
        {  
            _logger.LogError($"Something went wrong inside the AlbumController GetById action: {ex}");  
            return StatusCode((int)HttpStatusCode.InternalServerError, "Error occurred while executing Get Album By Id");  
        }
    }

    [HttpPost]
    [Produces("application/json")]
    [Consumes("application/json")]
    public async Task<ActionResult<AlbumApiModel>> Post([FromBody] AlbumApiModel input)
    {
        try
        {
            if (input == null)
            {
                return StatusCode((int)HttpStatusCode.BadRequest, "Given Album is null");
            }
            else
            {
                return Ok(await _chinookSupervisor.AddAlbum(input));
            }
        }
        catch (ValidationException  ex)  
        {  
            _logger.LogError($"The Album could not validated: Add Album action: {ex}");  
            return StatusCode((int)HttpStatusCode.InternalServerError, "Validation error while executing Add Albums");  
        }
        catch (Exception ex)  
        {  
            _logger.LogError($"Something went wrong inside the AlbumController Add Album action: {ex}");  
            return StatusCode((int)HttpStatusCode.InternalServerError, "Error occurred while executing Add Albums");  
        }
    }

    [HttpPut("{id}")]
    [Produces("application/json")]
    [Consumes("application/json")]
    public async Task<ActionResult<AlbumApiModel>> Put(int id, [FromBody] AlbumApiModel input)
    {
        try  
        {  
            if (input == null)  
            {  
                return StatusCode((int)HttpStatusCode.BadRequest, "Given Album is null");
            }  
            else  
            {
                return Ok(await _chinookSupervisor.UpdateAlbum(input));
            }  
        }
        catch (ValidationException  ex)  
        {  
            _logger.LogError($"The Album could not validated: Update Album action: {ex}");  
            return StatusCode((int)HttpStatusCode.InternalServerError, "Validation error while executing Update Albums");  
        }
        catch (Exception ex)  
        {  
            _logger.LogError($"Something went wrong inside the AlbumController Update Album action: {ex}");  
            return StatusCode((int)HttpStatusCode.InternalServerError, "Error occurred while executing Update Albums");  
        }
    }

    [HttpDelete("{id}")]
    public async Task<ActionResult> Delete(int id)
    {
        try  
        {  
            return Ok(await _chinookSupervisor.DeleteAlbum(id)); 
        }  
        catch (Exception ex)  
        {  
            _logger.LogError($"Something went wrong inside the AlbumController Delete action: {ex}");  
            return StatusCode((int)HttpStatusCode.InternalServerError, "Error occurred while executing Delete Album");  
        }
    }

    [HttpGet("artist/{id}")]
    public async Task<ActionResult<List<AlbumApiModel>>> GetByArtistId(int id)
    {
        try  
        {  
            var albums = await _chinookSupervisor.GetAlbumByArtistId(id);  

            if (albums.Any())  
            {  
                return Ok(albums);  
            }  
            else  
            {  
                return StatusCode((int)HttpStatusCode.NotFound, "No Albums Could Be Found for the Artist");  
            }  
        }  
        catch (Exception ex)  
        {  
            _logger.LogError($"Something went wrong inside the AlbumController Get By Artist action: {ex}");  
            return StatusCode((int)HttpStatusCode.InternalServerError, "Error occurred while executing Get All Albums for Artist");  
        }  
    }
}

# ERROR HANDLING AND RETURNING PROBLEM DETAILS

For more details look at the Chinook6WebAPI repo in GitHub.