diff --git a/exercise.webapi/DTO/AddBookDTO.cs b/exercise.webapi/DTO/AddBookDTO.cs new file mode 100644 index 0000000..131ca4f --- /dev/null +++ b/exercise.webapi/DTO/AddBookDTO.cs @@ -0,0 +1,14 @@ +using exercise.webapi.Models; + +namespace exercise.webapi.DTO +{ + public class AddBookDTO + { + public string Title { get; set; } + + public int AuthorId { get; set; } + + //public Author Author { get; set; } + + } +} diff --git a/exercise.webapi/DTO/AuthorDTO.cs b/exercise.webapi/DTO/AuthorDTO.cs new file mode 100644 index 0000000..919ade2 --- /dev/null +++ b/exercise.webapi/DTO/AuthorDTO.cs @@ -0,0 +1,13 @@ +using exercise.webapi.Models; + +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/AuthorListDTO.cs b/exercise.webapi/DTO/AuthorListDTO.cs new file mode 100644 index 0000000..e7f2876 --- /dev/null +++ b/exercise.webapi/DTO/AuthorListDTO.cs @@ -0,0 +1,13 @@ +using exercise.webapi.Models; + +namespace exercise.webapi.DTO +{ + public class AuthorListDTO + { + 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; } = new List(); + } +} diff --git a/exercise.webapi/DTO/BookDTO.cs b/exercise.webapi/DTO/BookDTO.cs new file mode 100644 index 0000000..f91b3cb --- /dev/null +++ b/exercise.webapi/DTO/BookDTO.cs @@ -0,0 +1,13 @@ +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 AuthorDTO Author { get; set; } + } +} diff --git a/exercise.webapi/DTO/BookListDTO.cs b/exercise.webapi/DTO/BookListDTO.cs new file mode 100644 index 0000000..507655f --- /dev/null +++ b/exercise.webapi/DTO/BookListDTO.cs @@ -0,0 +1,8 @@ +namespace exercise.webapi.DTO +{ + public class BookListDTO + { + public int Id { get; set; } + public string Title { get; set; } + } +} diff --git a/exercise.webapi/DTO/BookOnlyDTO.cs b/exercise.webapi/DTO/BookOnlyDTO.cs new file mode 100644 index 0000000..bc3be11 --- /dev/null +++ b/exercise.webapi/DTO/BookOnlyDTO.cs @@ -0,0 +1,7 @@ +namespace exercise.webapi.DTO +{ + public class BookOnlyDTO + { + public string Title { get; set; } + } +} diff --git a/exercise.webapi/Endpoints/AuthorApi.cs b/exercise.webapi/Endpoints/AuthorApi.cs new file mode 100644 index 0000000..fd212fd --- /dev/null +++ b/exercise.webapi/Endpoints/AuthorApi.cs @@ -0,0 +1,127 @@ +using exercise.webapi.DTO; +using exercise.webapi.Mapper; +using exercise.webapi.Models; +using exercise.webapi.Repository; +using Microsoft.AspNetCore.Http.HttpResults; +using Microsoft.AspNetCore.Mvc; + +namespace exercise.webapi.Endpoints +{ + public static class AuthorApi + { + public static void ConfigureAuthorEndpoint(this WebApplication app) + { + var pets = app.MapGroup("authors"); + + pets.MapGet("/getId{id}", GetAuthor); + pets.MapGet("/getAll", GetAllAuthors); + + } + + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task GetAllAuthors(IRepository repository) + { + var students = await repository.GetAllAuthor(); + var booksDTO = students.Select(s => s.ToAuthorDto()); + return Results.Ok(booksDTO); + } + + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task GetAuthor(IRepository repository, [FromQuery] int id) + { + var author = await repository.GetAuthor(id); + if (author == null) + { + return Results.NotFound("No Authors of the provided Id were found."); + } + + var productDTO = new AuthorListDTO + { + Id = author.Id, + FirstName = author.FirstName, + LastName = author.LastName, + Email = author.Email, + Books = author.Books?.Select(s => new BookListDTO + { + Id = s.Id, + Title = s.Title, + //ProfileId = s.ProfileId + }).ToList() + }; + + + return Results.Ok(productDTO); + } + + + //[ProducesResponseType(StatusCodes.Status201Created)] + //[ProducesResponseType(StatusCodes.Status400BadRequest)] + //public static async Task AddBook(IRepository repository, AddBookDTO model) + //{ + // try + // { + + // Book book = new Book() + // { + // Title = model.Title, + // AuthorId = model.AuthorId + // }; + // await repository.CreateAsync(book); + + // return TypedResults.Created($"https://localhost:7010/books/{book.Id}", book); + + + // } + // catch (Exception ex) + // { + // return TypedResults.Problem(ex.Message); + // } + //} + ////public static async Task Create(IRepository repository, [FromBody] AddBookDTO bookDto) + ////{ + //// var model = bookDto.ToSkillFromCreateDto(); + //// await repository.CreateAsync(model); + + //// return Results.CreatedAtRoute(nameof(GetBook), new { id = model.Id }, model.ToSkillDto()); + + ////} + //[ProducesResponseType(StatusCodes.Status200OK)] + //[ProducesResponseType(StatusCodes.Status400BadRequest)] + //[ProducesResponseType(StatusCodes.Status404NotFound)] + //public static async Task DeleteBook(IRepository repository, int id) + //{ + // try + // { + // var deletedBook = await repository.DeleteAsync(id); + // if (deletedBook != null) return Results.Ok(new { When = DateTime.Now, Status = "Deleted", deletedBook.Title }); + // return TypedResults.NotFound(); + // } + // catch (Exception ex) + // { + // return TypedResults.Problem(ex.Message); + // } + //} + //[ProducesResponseType(StatusCodes.Status200OK)] + //[ProducesResponseType(StatusCodes.Status400BadRequest)] + //[ProducesResponseType(StatusCodes.Status404NotFound)] + //public static async Task UpdateBook(IRepository repository, int id, AddBookDTO model) + //{ + // try + // { + // var target = await repository.GetBook(id); + // if (target == null) return Results.NotFound("Product not found"); + // if (model.Title != null) target.Title = model.Title; else return Results.BadRequest("error"); + // await repository.UpdateBook(target); + // return Results.Ok(target); + // } + // catch (Exception ex) + // { + // return TypedResults.Problem(ex.Message); + // } + //} + + } +} diff --git a/exercise.webapi/Endpoints/BookApi.cs b/exercise.webapi/Endpoints/BookApi.cs index 6758215..1fb73c5 100644 --- a/exercise.webapi/Endpoints/BookApi.cs +++ b/exercise.webapi/Endpoints/BookApi.cs @@ -1,20 +1,126 @@ -using exercise.webapi.Models; +using exercise.webapi.DTO; +using exercise.webapi.Mapper; +using exercise.webapi.Models; using exercise.webapi.Repository; -using static System.Reflection.Metadata.BlobBuilder; +using Microsoft.AspNetCore.Http.HttpResults; +using Microsoft.AspNetCore.Mvc; namespace exercise.webapi.Endpoints { public static class BookApi { - public static void ConfigureBooksApi(this WebApplication app) + public static void ConfigureBookEndpoint(this WebApplication app) { - app.MapGet("/books", GetBooks); + var pets = app.MapGroup("books"); + + pets.MapGet("/getId{id}", GetBook); + pets.MapGet("/getAll", GetAllBooks); + pets.MapPost("/add", AddBook); + pets.MapDelete("/{id}", DeleteBook); + pets.MapPut("/{id}", UpdateBook); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task GetAllBooks(IRepository repository) + { + var students = await repository.GetAllBooks(); + var booksDTO = students.Select(s => s.ToBookDto()); + return Results.Ok(booksDTO); + } + + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task GetBook(IRepository repository, [FromQuery] int id) + { + var book = await repository.GetBook(id); + if (book == null) + { + return Results.NotFound("No Books of the provided Id were found."); + } + + var productDTO = new BookDTO + { + Id = book.Id, + Title = book.Title, + AuthorId = book.AuthorId, + Author = new AuthorDTO + { + Id = book.Author.Id, + FirstName = book.Author.FirstName, + LastName = book.Author.LastName, + Email = book.Author.Email + } + }; + + + return Results.Ok(productDTO); + } + + + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task AddBook(IRepository repository, AddBookDTO model) + { + try + { + Book book = new Book() + { + Title = model.Title, + AuthorId = model.AuthorId + }; + await repository.CreateAsync(book); + + return TypedResults.Created($"https://localhost:7010/books/{book.Id}", book); + } + catch (Exception ex) + { + return TypedResults.Problem(ex.Message); + } } + //public static async Task Create(IRepository repository, [FromBody] AddBookDTO bookDto) + //{ + // var model = bookDto.ToSkillFromCreateDto(); + // await repository.CreateAsync(model); + + // return Results.CreatedAtRoute(nameof(GetBook), new { id = model.Id }, model.ToSkillDto()); - private static async Task GetBooks(IBookRepository bookRepository) + //} + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task DeleteBook(IRepository repository, int id) { - var books = await bookRepository.GetAllBooks(); - return TypedResults.Ok(books); + try + { + var deletedBook = await repository.DeleteAsync(id); + if (deletedBook != null) return Results.Ok(new { When = DateTime.Now, Status = "Deleted", deletedBook.Title }); + return TypedResults.NotFound(); + } + catch (Exception ex) + { + return TypedResults.Problem(ex.Message); + } } + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task UpdateBook(IRepository repository, int id, AddBookDTO model) + { + try + { + var target = await repository.GetBook(id); + if (target == null) return Results.NotFound("Product not found"); + if (model.Title != null) target.Title = model.Title; else return Results.BadRequest("error"); + await repository.UpdateBook(target); + return Results.Ok(target); + } + catch (Exception ex) + { + return TypedResults.Problem(ex.Message); + } + } + } } diff --git a/exercise.webapi/Mapper/MappAuthor.cs b/exercise.webapi/Mapper/MappAuthor.cs new file mode 100644 index 0000000..a0e96de --- /dev/null +++ b/exercise.webapi/Mapper/MappAuthor.cs @@ -0,0 +1,37 @@ +using exercise.webapi.DTO; +using exercise.webapi.Models; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace exercise.webapi.Mapper +{ + public static class MappAuthor + { + public static AuthorDTO ToProfileNoListsDto(this Author model) + { + return new AuthorDTO + { + Id = model.Id, + FirstName = model.FirstName, + LastName = model.LastName, + Email = model.Email, + }; + } + + public static AuthorListDTO ToAuthorDto(this Author profileModel) + { + return new AuthorListDTO + { + Id = profileModel.Id, + FirstName = profileModel.FirstName, + LastName = profileModel.LastName, + Email = profileModel.Email, + Books = profileModel.Books?.Select(s => new BookListDTO + { + Id = s.Id, + Title = s.Title, + //ProfileId = s.ProfileId + }).ToList() + }; + } + } +} diff --git a/exercise.webapi/Mapper/MappBook.cs b/exercise.webapi/Mapper/MappBook.cs new file mode 100644 index 0000000..3abd19b --- /dev/null +++ b/exercise.webapi/Mapper/MappBook.cs @@ -0,0 +1,30 @@ +using exercise.webapi.DTO; +using exercise.webapi.Models; + +namespace exercise.webapi.Mapper +{ + public static class MappBook + { + public static BookDTO ToBookDto(this Book skillModel) + { + return new BookDTO + { + Id = skillModel.Id, + Title = skillModel.Title, + AuthorId = skillModel.AuthorId, + Author = skillModel.Author.ToProfileNoListsDto() + }; + } + + + public static Book ToBookFromCreateDto(this AddBookDTO bookDTO) + { + return new Book + { + Title = bookDTO.Title, + AuthorId = bookDTO.AuthorId + }; + } + + } +} diff --git a/exercise.webapi/Models/Author.cs b/exercise.webapi/Models/Author.cs index 9f47878..5698331 100644 --- a/exercise.webapi/Models/Author.cs +++ b/exercise.webapi/Models/Author.cs @@ -9,7 +9,7 @@ public class Author public string LastName { get; set; } public string Email { get; set; } - [JsonIgnore] // Todo: replace this with DTO approach + //[JsonIgnore] // Todo: replace this with DTO approach public ICollection Books { get; set; } = new List(); } } diff --git a/exercise.webapi/Program.cs b/exercise.webapi/Program.cs index 43dec56..549d8e3 100644 --- a/exercise.webapi/Program.cs +++ b/exercise.webapi/Program.cs @@ -10,7 +10,7 @@ builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddDbContext(opt => opt.UseInMemoryDatabase("Library")); -builder.Services.AddScoped(); +builder.Services.AddScoped(); var app = builder.Build(); @@ -28,5 +28,6 @@ } app.UseHttpsRedirection(); -app.ConfigureBooksApi(); +app.ConfigureBookEndpoint(); +app.ConfigureAuthorEndpoint(); app.Run(); diff --git a/exercise.webapi/Repository/BookRepository.cs b/exercise.webapi/Repository/BookRepository.cs deleted file mode 100644 index 1f5e64a..0000000 --- a/exercise.webapi/Repository/BookRepository.cs +++ /dev/null @@ -1,22 +0,0 @@ -using exercise.webapi.Data; -using exercise.webapi.Models; -using Microsoft.EntityFrameworkCore; - -namespace exercise.webapi.Repository -{ - public class BookRepository: IBookRepository - { - DataContext _db; - - public BookRepository(DataContext db) - { - _db = db; - } - - public async Task> GetAllBooks() - { - return await _db.Books.Include(b => b.Author).ToListAsync(); - - } - } -} diff --git a/exercise.webapi/Repository/IBookRepository.cs b/exercise.webapi/Repository/IBookRepository.cs deleted file mode 100644 index f860016..0000000 --- a/exercise.webapi/Repository/IBookRepository.cs +++ /dev/null @@ -1,9 +0,0 @@ -using exercise.webapi.Models; - -namespace exercise.webapi.Repository -{ - public interface IBookRepository - { - public Task> GetAllBooks(); - } -} diff --git a/exercise.webapi/Repository/IRepository.cs b/exercise.webapi/Repository/IRepository.cs new file mode 100644 index 0000000..9a23485 --- /dev/null +++ b/exercise.webapi/Repository/IRepository.cs @@ -0,0 +1,26 @@ +using exercise.webapi.DTO; +using exercise.webapi.Models; + +namespace exercise.webapi.Repository +{ + public interface IRepository + { + public Task> GetAllBooks(); + + //Task AddBook(Book book); + + Task GetBook(int id); + + Task CreateAsync(Book model); + + //Task UpdatedAsync(int id, AddBookDTO model); + Task UpdateBook(Book book); + Task DeleteAsync(int id); + + + + public Task> GetAllAuthor(); + + Task GetAuthor(int id); + } +} diff --git a/exercise.webapi/Repository/Repository.cs b/exercise.webapi/Repository/Repository.cs new file mode 100644 index 0000000..f42d5aa --- /dev/null +++ b/exercise.webapi/Repository/Repository.cs @@ -0,0 +1,71 @@ +using exercise.webapi.Data; +using exercise.webapi.DTO; +using exercise.webapi.Models; +using Microsoft.EntityFrameworkCore; + +namespace exercise.webapi.Repository +{ + public class Repository: IRepository + { + DataContext _db; + + public Repository(DataContext db) + { + _db = db; + } + + //public async Task AddBook(Book book) + //{ + // await _db.Books.AddAsync(book); + // await _db.SaveChangesAsync(); + // return book; + //} + + + public async Task> GetAllBooks() + { + return await _db.Books.Include(s => s.Author).ToListAsync(); + } + public async Task GetBook(int id) + { + return await _db.Books.Include(a => a.Author).FirstAsync( b => b.Id == id); + } + + public async Task CreateAsync(Book model) + { + await _db.Books.AddAsync(model); + await _db.SaveChangesAsync(); + return model; + } + public async Task DeleteAsync(int id) + { + var skillModel = await _db.Books.FirstOrDefaultAsync(x => x.Id == id); + if (skillModel == null) + { + return null; + } + + _db.Books.Remove(skillModel); + await _db.SaveChangesAsync(); + return skillModel; + } + + + public async Task UpdateBook(Book book) + { + _db.Books.Update(book); + await _db.SaveChangesAsync(); + return book; + } + + public async Task> GetAllAuthor() + { + return await _db.Authors.Include(s => s.Books).ToListAsync(); + } + + public async Task GetAuthor(int id) + { + return await _db.Authors.Include(a => a.Books).FirstAsync(b => b.Id == id); + } + } +} diff --git a/exercise.webapi/exercise.webapi.csproj b/exercise.webapi/exercise.webapi.csproj index 0ff4269..fab038d 100644 --- a/exercise.webapi/exercise.webapi.csproj +++ b/exercise.webapi/exercise.webapi.csproj @@ -8,10 +8,10 @@ - - + + - +