diff --git a/exercise.webapi/DTO/AuthorDTO.cs b/exercise.webapi/DTO/AuthorDTO.cs new file mode 100644 index 0000000..fc9adf3 --- /dev/null +++ b/exercise.webapi/DTO/AuthorDTO.cs @@ -0,0 +1,11 @@ +namespace exercise.webapi.DTO +{ + public class AuthorDTO + { + public int Id { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string Email { get; set; } + + } +} diff --git a/exercise.webapi/DTO/AuthorDTONoID.cs b/exercise.webapi/DTO/AuthorDTONoID.cs new file mode 100644 index 0000000..f95295e --- /dev/null +++ b/exercise.webapi/DTO/AuthorDTONoID.cs @@ -0,0 +1,11 @@ +namespace exercise.webapi.DTO +{ + public class AuthorDTONoId + { + public string FirstName { get; set; } + + public string LastName { get; set; } + + public string Email { get; set; } + } +} diff --git a/exercise.webapi/DTO/BookDTO.cs b/exercise.webapi/DTO/BookDTO.cs new file mode 100644 index 0000000..e2b7ed3 --- /dev/null +++ b/exercise.webapi/DTO/BookDTO.cs @@ -0,0 +1,10 @@ +namespace exercise.webapi.DTO +{ + public class BookDTO + { + public int Id { get; set; } + public string Title { get; set; } + + public AuthorDTO author { get; set; } + } +} diff --git a/exercise.webapi/DTO/BookDTONoAuthorID.cs b/exercise.webapi/DTO/BookDTONoAuthorID.cs new file mode 100644 index 0000000..25bb918 --- /dev/null +++ b/exercise.webapi/DTO/BookDTONoAuthorID.cs @@ -0,0 +1,10 @@ +namespace exercise.webapi.DTO +{ + public class BookDTONoAuthorID + { + public int Id { get; set; } + public string Title { get; set; } + + public AuthorDTONoId author { get; set; } + } +} diff --git a/exercise.webapi/Endpoints/BookApi.cs b/exercise.webapi/Endpoints/BookApi.cs index 6758215..26e25e9 100644 --- a/exercise.webapi/Endpoints/BookApi.cs +++ b/exercise.webapi/Endpoints/BookApi.cs @@ -1,5 +1,7 @@ -using exercise.webapi.Models; +using exercise.webapi.DTO; +using exercise.webapi.Models; using exercise.webapi.Repository; +using Microsoft.AspNetCore.Mvc; using static System.Reflection.Metadata.BlobBuilder; namespace exercise.webapi.Endpoints @@ -9,12 +11,206 @@ public static class BookApi public static void ConfigureBooksApi(this WebApplication app) { app.MapGet("/books", GetBooks); + app.MapGet("/books/{id}", GetBook); + app.MapPost("/books", AddBook); + app.MapPut("/books/{author.id}", UpdateBook); + app.MapGet("/authors", GetAuthors); + app.MapGet("/authors/{id}", GetAuthorById); + } - private static async Task GetBooks(IBookRepository bookRepository) + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task GetBooks(IBookRepository bookRepository) { var books = await bookRepository.GetAllBooks(); - return TypedResults.Ok(books); + List bookList = new List(); + + foreach (var book in books) + { + Author auth = await bookRepository.GetAuthor(book.AuthorId); + + BookDTO book1 = new BookDTO + { + Id = book.Id, + Title = book.Title, + author = new AuthorDTO + { + Id = auth.Id, + FirstName = auth.FirstName, + LastName = auth.LastName, + Email = auth.Email + } + }; + bookList.Add(book1); + + } + + return TypedResults.Ok(bookList); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task GetAuthors(IBookRepository bookRepository) + { + var authors = await bookRepository.GetAllAuthors(); + List authorList = new List(); + + foreach (var author in authors) + { + IEnumerable books = await bookRepository.GetBooksByAuthorId(author.Id); + + // Convert book entities to DTOs + List bookDTOs = books.Select(b => new BookDTO + { + Id = b.Id, + Title = b.Title + }).ToList(); + + // Create an AuthorDTO with books + AuthorDTO authorDTO = new AuthorDTO + { + Id = author.Id, + FirstName = author.FirstName, + LastName = author.LastName, + Email = author.Email, + }; + authorList.Add(authorDTO); + } + + return TypedResults.Ok(authorList); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task GetAuthorById(IBookRepository bookRepository, int authorId) + { + if (authorId <= 0) + { + return TypedResults.BadRequest("Invalid author ID."); + } + + var author = await bookRepository.GetAuthor(authorId); + + if (author == null) + { + return TypedResults.NotFound($"Author with ID {authorId} not found."); + } + + IEnumerable books = await bookRepository.GetBooksByAuthorId(author.Id); + + + List bookDTOs = books.Select(b => new BookDTO + { + Id = b.Id, + Title = b.Title + }).ToList(); + + AuthorDTO authorDTO = new AuthorDTO + { + Id = author.Id, + FirstName = author.FirstName, + LastName = author.LastName, + Email = author.Email, + }; + + return TypedResults.Ok(authorDTO); + } + + + + [ProducesResponseType(StatusCodes.Status200OK)] + + public static async Task GetBook(IBookRepository bookRepository, int id) + { + var book = await bookRepository.GetBook(id); + if (book == null) + { + return Results.NotFound(); + } + + Author auth = await bookRepository.GetAuthor(book.AuthorId); + + BookDTO book1 = new BookDTO + { + Id = book.Id, + Title = book.Title, + author = new AuthorDTO + { + Id = auth.Id, + FirstName = auth.FirstName, + LastName = auth.LastName, + Email = auth.Email + } + }; + + return TypedResults.Ok(book1); + } + + [ProducesResponseType(StatusCodes.Status201Created)] + public static async Task UpdateBook(IBookRepository bookRepository, int id, AuthorDTONoId auth) + { + var book = await bookRepository.GetBook(id); + + BookDTONoAuthorID book1 = new BookDTONoAuthorID + { + Id = book.Id, + Title = book.Title, + author = new AuthorDTONoId + { + FirstName = auth.FirstName, + LastName = auth.LastName, + Email = auth.Email + } + }; + + book.Author.FirstName = auth.FirstName; + book.Author.LastName = auth.LastName; + book.Author.Email = auth.Email; + + await bookRepository.UpdateBook(book); + + return TypedResults.Created($"/books/{book1.Id}", book1); + } + + [ProducesResponseType(StatusCodes.Status201Created)] + public static async Task AddBook(IBookRepository bookRepository, string title, int author_id) + { + Author auth = await bookRepository.GetAuthor(author_id); + + Book book = new Book + { + Title = title, + Author = auth + }; + + if (book.Author.Id == 0 || auth == null) + { + return TypedResults.BadRequest("Author ID is invalid"); + } + + if (string.IsNullOrWhiteSpace(title)) + { + return TypedResults.BadRequest("Title cannot be empty."); + } + + + await bookRepository.AddBook(book); + + AuthorDTO authDTO = new AuthorDTO { + Id = book.AuthorId, + FirstName = book.Author.FirstName, + LastName = book.Author.LastName, + Email = book.Author.Email + }; + + BookDTO bookDTO = new BookDTO + { + Id = book.Id, + Title = title, + author = authDTO + }; + + return TypedResults.Created($"/books/{book.Id}", bookDTO); + } } } diff --git a/exercise.webapi/Models/Author.cs b/exercise.webapi/Models/Author.cs index 9f47878..ee33def 100644 --- a/exercise.webapi/Models/Author.cs +++ b/exercise.webapi/Models/Author.cs @@ -1,4 +1,5 @@ -using System.Text.Json.Serialization; +using exercise.webapi.DTO; +using System.Text.Json.Serialization; namespace exercise.webapi.Models { @@ -9,7 +10,6 @@ public class Author public string LastName { get; set; } public string Email { get; set; } - [JsonIgnore] // Todo: replace this with DTO approach - public ICollection Books { get; set; } = new List(); + public List Books { get; set; } = new List(); } } diff --git a/exercise.webapi/Repository/BookRepository.cs b/exercise.webapi/Repository/BookRepository.cs index 1f5e64a..bf9eafa 100644 --- a/exercise.webapi/Repository/BookRepository.cs +++ b/exercise.webapi/Repository/BookRepository.cs @@ -1,4 +1,5 @@ using exercise.webapi.Data; +using exercise.webapi.DTO; using exercise.webapi.Models; using Microsoft.EntityFrameworkCore; @@ -18,5 +19,38 @@ public async Task> GetAllBooks() return await _db.Books.Include(b => b.Author).ToListAsync(); } + public async Task GetBook(int id) + { + return await _db.Books.Include(b => b.Author).FirstOrDefaultAsync(b => b.Id == id); + } + + public async Task> GetAllAuthors() + { + return await _db.Authors.ToListAsync(); + } + + public async Task GetAuthor(int id) + { + return await _db.Authors.FirstOrDefaultAsync(a => a.Id == id); + } + public async Task UpdateBook(Book book) + { + _db.Books.Update(book); + await _db.SaveChangesAsync(); + return book; + } + + public async Task AddBook(Book book) + { + _db.Books.Add(book); + await _db.SaveChangesAsync(); + return book; + } + + public async Task> GetBooksByAuthorId(int id) + { + return await _db.Books.Where(b => b.AuthorId == id).ToListAsync(); + } + } } diff --git a/exercise.webapi/Repository/IBookRepository.cs b/exercise.webapi/Repository/IBookRepository.cs index f860016..8b9685e 100644 --- a/exercise.webapi/Repository/IBookRepository.cs +++ b/exercise.webapi/Repository/IBookRepository.cs @@ -1,9 +1,21 @@ -using exercise.webapi.Models; +using exercise.webapi.DTO; +using exercise.webapi.Models; namespace exercise.webapi.Repository { public interface IBookRepository { public Task> GetAllBooks(); + public Task GetBook(int id); + public Task> GetAllAuthors(); + + + public Task GetAuthor(int id); + public Task AddBook(Book book); + + public Task UpdateBook(Book book); + + public Task> GetBooksByAuthorId(int id); + } } diff --git a/exercise.webapi/exercise.webapi.csproj b/exercise.webapi/exercise.webapi.csproj index 0ff4269..ec8316d 100644 --- a/exercise.webapi/exercise.webapi.csproj +++ b/exercise.webapi/exercise.webapi.csproj @@ -9,9 +9,14 @@ - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + - +