diff --git a/exercise.webapi/DTO/AuthorDTO.cs b/exercise.webapi/DTO/AuthorDTO.cs new file mode 100644 index 0000000..140d0af --- /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; } + public List Books { get; set; } + } +} diff --git a/exercise.webapi/DTO/BookDTO.cs b/exercise.webapi/DTO/BookDTO.cs new file mode 100644 index 0000000..d7558dd --- /dev/null +++ b/exercise.webapi/DTO/BookDTO.cs @@ -0,0 +1,15 @@ +using exercise.webapi.Models; + +namespace exercise.webapi.DTO +{ + public class BookDTO + { + public int Id { get; set; } + public string Title { get; set; } + public int AuthorId { get; set; } + public MinimalAuthorDTO Author { get; set; } + + + } +} + diff --git a/exercise.webapi/DTO/MinimalAuthorDTO.cs b/exercise.webapi/DTO/MinimalAuthorDTO.cs new file mode 100644 index 0000000..863f2bc --- /dev/null +++ b/exercise.webapi/DTO/MinimalAuthorDTO.cs @@ -0,0 +1,10 @@ +namespace exercise.webapi.DTO +{ + public class MinimalAuthorDTO + { + 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/MinimalBookDTO.cs b/exercise.webapi/DTO/MinimalBookDTO.cs new file mode 100644 index 0000000..7d15f7e --- /dev/null +++ b/exercise.webapi/DTO/MinimalBookDTO.cs @@ -0,0 +1,8 @@ +namespace exercise.webapi.DTO +{ + public class MinimalBookDTO + { + public int Id { get; set; } + public string Title { get; set; } + } +} diff --git a/exercise.webapi/DTO/PostBook.cs b/exercise.webapi/DTO/PostBook.cs new file mode 100644 index 0000000..2bf9d1b --- /dev/null +++ b/exercise.webapi/DTO/PostBook.cs @@ -0,0 +1,8 @@ +namespace exercise.webapi.DTO +{ + public class PostBook + { + public string Title { get; set; } + public int AuthorId { get; set; } + } +} diff --git a/exercise.webapi/DTO/PutBook.cs b/exercise.webapi/DTO/PutBook.cs new file mode 100644 index 0000000..0156130 --- /dev/null +++ b/exercise.webapi/DTO/PutBook.cs @@ -0,0 +1,8 @@ +namespace exercise.webapi.DTO +{ + public class PutBook + { + public string? Title { get; set; } + public int? AuthorId { get; set; } + } +} diff --git a/exercise.webapi/Endpoints/AuthorAPI.cs b/exercise.webapi/Endpoints/AuthorAPI.cs new file mode 100644 index 0000000..5771644 --- /dev/null +++ b/exercise.webapi/Endpoints/AuthorAPI.cs @@ -0,0 +1,52 @@ +using exercise.webapi.DTO; +using exercise.webapi.Models; +using exercise.webapi.Repository; + +namespace exercise.webapi.Endpoints +{ + public static class AuthorAPI + { + public static void ConfigureAuthorAPI(this WebApplication app) + { + app.MapGet("/authors", GetAuthors); + app.MapGet("/authors/{id}", Get); + } + + private static async Task GetAuthors(IAuthorRepository authorRepository) + { + var authors = await authorRepository.GetAllAuthors(); + List authorsDTOList = authors.Select(authors => new AuthorDTO + { + Id = authors.Id, + FirstName = authors.FirstName, + LastName = authors.LastName, + Email = authors.Email, + Books = authors.Books.Select(books => new MinimalBookDTO + { + Id = books.Id, + Title = books.Title, + }).ToList() + }).ToList(); + return TypedResults.Ok(authorsDTOList); + } + + private static async Task Get(IAuthorRepository authorRepository, int id) + { + var author = await authorRepository.Get(id); + if (author == null) { return TypedResults.NotFound("Author Not Found"); } + var AuthorDTO = new AuthorDTO + { + Id = author.Id, + FirstName = author.FirstName, + LastName = author.LastName, + Email = author.Email, + Books = author.Books.Select(book => new MinimalBookDTO + { + Id = book.Id, + Title = book.Title, + }).ToList() + }; + return TypedResults.Ok(AuthorDTO); + } + } +} diff --git a/exercise.webapi/Endpoints/BookApi.cs b/exercise.webapi/Endpoints/BookApi.cs index 6758215..c3c1c53 100644 --- a/exercise.webapi/Endpoints/BookApi.cs +++ b/exercise.webapi/Endpoints/BookApi.cs @@ -1,4 +1,5 @@ -using exercise.webapi.Models; +using exercise.webapi.DTO; +using exercise.webapi.Models; using exercise.webapi.Repository; using static System.Reflection.Metadata.BlobBuilder; @@ -9,12 +10,143 @@ public static class BookApi public static void ConfigureBooksApi(this WebApplication app) { app.MapGet("/books", GetBooks); + app.MapGet("/books/{id}", Get); + app.MapPost("/books", Create); + app.MapPut("/books/{id}", Put); + app.MapDelete("/books/{id}", Delete); + } private static async Task GetBooks(IBookRepository bookRepository) { var books = await bookRepository.GetAllBooks(); - return TypedResults.Ok(books); + List bookDTOList = books.Select(book => new BookDTO + { + Id = book.Id, + Title = book.Title, + AuthorId = book.AuthorId, + Author = new MinimalAuthorDTO + { + Id = book.Author.Id, + FirstName = book.Author.FirstName, + LastName = book.Author.LastName, + Email = book.Author.Email + } + }).ToList(); + return TypedResults.Ok(bookDTOList); + } + + + private static async Task Get(IBookRepository bookRepository, int id) + { + var book = await bookRepository.Get(id); + if (book == null) { return TypedResults.NotFound("Book Not Found"); } + var bookDTO = new BookDTO + { + Id = book.Id, + Title = book.Title, + AuthorId = book.AuthorId, + Author = new MinimalAuthorDTO + { + Id = book.Author.Id, + FirstName = book.Author.FirstName, + LastName = book.Author.LastName, + Email = book.Author.Email + } + }; + return TypedResults.Ok(bookDTO); + } + + private static async Task Create(IBookRepository bookRepository, IAuthorRepository authorRepository, PostBook book) + { + if(String.IsNullOrEmpty(book.Title)) + { + return TypedResults.BadRequest(); + } + Author authorFound = await authorRepository.Get(book.AuthorId); + if(authorFound == null) { return TypedResults.NotFound(); } + + Book newBook = new Book + { + Title = book.Title, + AuthorId = book.AuthorId, + + }; + bookRepository.Create(newBook); + return TypedResults.Ok(new BookDTO + { + Id = newBook.Id, + Title = newBook.Title, + AuthorId = newBook.AuthorId, + Author = new MinimalAuthorDTO + { + Id = authorFound.Id, + FirstName = authorFound.FirstName, + LastName = authorFound.LastName, + Email = authorFound.Email + } + }); + + + } + + private static async Task Put(IBookRepository bookRepository, IAuthorRepository authorRepository, int id, PutBook book) + { + Author foundAuthor = null; + Book bookToBeUpdated = null; + if(book.AuthorId.HasValue) { + foundAuthor = await authorRepository.Get(book.AuthorId.Value); + } + if (String.IsNullOrEmpty(book.Title) && !book.AuthorId.HasValue) + { + return TypedResults.BadRequest("Empty payload"); + } + if(foundAuthor == null && book.AuthorId.HasValue) { return TypedResults.NotFound("Author not found"); }; + bookToBeUpdated = bookRepository.Get(id).Result; + if (bookToBeUpdated == null) + { + return TypedResults.NotFound("Book not found"); + } + bookToBeUpdated.Title = !string.IsNullOrEmpty(book.Title) ? book.Title : bookToBeUpdated.Title; + if (foundAuthor != null) + { + bookToBeUpdated.AuthorId = (int)book.AuthorId; + bookToBeUpdated.Author = foundAuthor; + } + bookRepository.Update(bookToBeUpdated); + return TypedResults.Ok(new BookDTO + { + Id = bookToBeUpdated.Id, + Title = bookToBeUpdated.Title, + AuthorId = bookToBeUpdated.AuthorId, + Author = new MinimalAuthorDTO + { + Id = bookToBeUpdated.Author.Id, + FirstName = bookToBeUpdated.Author.FirstName, + LastName = bookToBeUpdated.Author.LastName, + Email = bookToBeUpdated.Author.Email + } + }); + + } + + private static async Task Delete(IBookRepository bookRepository, IAuthorRepository authorRepository, int id) + { + Book book = bookRepository.Get(id).Result; + bool isDeleted = await bookRepository.Delete(book); + return TypedResults.Ok(new BookDTO + { + Id = book.Id, + Title = book.Title, + AuthorId = book.AuthorId, + Author = new MinimalAuthorDTO + { + Id = book.Author.Id, + FirstName = book.Author.FirstName, + LastName = book.Author.LastName, + Email = book.Author.Email + } + }); } } } diff --git a/exercise.webapi/Models/Author.cs b/exercise.webapi/Models/Author.cs index 9f47878..6da6f42 100644 --- a/exercise.webapi/Models/Author.cs +++ b/exercise.webapi/Models/Author.cs @@ -8,8 +8,6 @@ public class Author public string FirstName { get; set; } public string LastName { get; set; } public string Email { get; set; } - - [JsonIgnore] // Todo: replace this with DTO approach public ICollection Books { get; set; } = new List(); } } diff --git a/exercise.webapi/Models/Book.cs b/exercise.webapi/Models/Book.cs index 9534929..56138e3 100644 --- a/exercise.webapi/Models/Book.cs +++ b/exercise.webapi/Models/Book.cs @@ -9,5 +9,5 @@ public class Book public int AuthorId { get; set; } public Author Author { get; set; } - } + } } diff --git a/exercise.webapi/Program.cs b/exercise.webapi/Program.cs index 43dec56..e254153 100644 --- a/exercise.webapi/Program.cs +++ b/exercise.webapi/Program.cs @@ -11,6 +11,7 @@ builder.Services.AddSwaggerGen(); builder.Services.AddDbContext(opt => opt.UseInMemoryDatabase("Library")); builder.Services.AddScoped(); +builder.Services.AddScoped(); var app = builder.Build(); @@ -29,4 +30,5 @@ app.UseHttpsRedirection(); app.ConfigureBooksApi(); +app.ConfigureAuthorAPI(); app.Run(); diff --git a/exercise.webapi/Repository/AuthorRepository.cs b/exercise.webapi/Repository/AuthorRepository.cs new file mode 100644 index 0000000..ac7ab73 --- /dev/null +++ b/exercise.webapi/Repository/AuthorRepository.cs @@ -0,0 +1,24 @@ +using exercise.webapi.Data; +using exercise.webapi.Models; +using Microsoft.EntityFrameworkCore; + +namespace exercise.webapi.Repository +{ + public class AuthorRepository : IAuthorRepository + { + DataContext _db; + + public AuthorRepository(DataContext db) + { + _db = db; + } + public async Task> GetAllAuthors() + { + return await _db.Authors.Include(b => b.Books).ToListAsync(); + } + public async Task Get(int id) + { + return await _db.Authors.Include(b => b.Books).FirstOrDefaultAsync(x => x.Id == id); + } + } +} diff --git a/exercise.webapi/Repository/BookRepository.cs b/exercise.webapi/Repository/BookRepository.cs index 1f5e64a..83b6bd6 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,31 @@ public async Task> GetAllBooks() return await _db.Books.Include(b => b.Author).ToListAsync(); } + + public async Task Get(int id) + { + return await _db.Books.Include(b => b.Author).FirstOrDefaultAsync(x => x.Id == id); + } + + public async Task Create(Book book) + { + _db.Books.Add(book); + await _db.SaveChangesAsync(); + return book; + } + + public async Task Update(Book book) + { + _db.Update(book); + _db.SaveChangesAsync(); + return true; + } + + public async Task Delete(Book book) + { + _db.Books.Remove(book); + _db.SaveChangesAsync(); + return true; + } } } diff --git a/exercise.webapi/Repository/IAuthorRepository.cs b/exercise.webapi/Repository/IAuthorRepository.cs new file mode 100644 index 0000000..ab9ca2a --- /dev/null +++ b/exercise.webapi/Repository/IAuthorRepository.cs @@ -0,0 +1,10 @@ +using exercise.webapi.Models; + +namespace exercise.webapi.Repository +{ + public interface IAuthorRepository + { + public Task> GetAllAuthors(); + public Task Get(int id); + } +} diff --git a/exercise.webapi/Repository/IBookRepository.cs b/exercise.webapi/Repository/IBookRepository.cs index f860016..bebb16b 100644 --- a/exercise.webapi/Repository/IBookRepository.cs +++ b/exercise.webapi/Repository/IBookRepository.cs @@ -5,5 +5,9 @@ namespace exercise.webapi.Repository public interface IBookRepository { public Task> GetAllBooks(); + public Task Get(int id); + public Task Create(Book book); + public Task Update(Book book); + public Task Delete(Book book); } }