From ec03ac62a400526b6ec45d14cf20df2be78162f2 Mon Sep 17 00:00:00 2001 From: Kristoffer Blucher Date: Fri, 24 Jan 2025 14:52:15 +0100 Subject: [PATCH 1/3] Almost done with core + 1 extension. Just need to fix books added with authors. --- .gitignore | 2 + exercise.webapi/DTO/AuthorDTO.cs | 10 + exercise.webapi/DTO/BookDTO.cs | 10 + exercise.webapi/DTO/PublisherDTO.cs | 8 + exercise.webapi/Data/DataContext.cs | 12 +- exercise.webapi/Data/Seeder.cs | 14 +- exercise.webapi/Endpoints/AuthorApi.cs | 76 ++++++++ exercise.webapi/Endpoints/BookApi.cs | 171 +++++++++++++++++- exercise.webapi/Endpoints/PublisherApi.cs | 67 +++++++ exercise.webapi/Models/Author.cs | 3 +- exercise.webapi/Models/Book.cs | 10 +- exercise.webapi/Models/Publisher.cs | 11 ++ exercise.webapi/Program.cs | 7 + .../Repository/AuthorRepository.cs | 27 +++ exercise.webapi/Repository/BookRepository.cs | 56 +++++- .../Repository/IAuthorRepository.cs | 13 ++ exercise.webapi/Repository/IBookRepository.cs | 7 +- .../Repository/IPublisherRepository.cs | 13 ++ .../Repository/PublisherRepository.cs | 28 +++ exercise.webapi/ViewModels/BookPost.cs | 12 ++ exercise.webapi/appsettings.Development.json | 24 +-- exercise.webapi/appsettings.json | 6 +- exercise.webapi/exercise.webapi.csproj | 9 +- 23 files changed, 564 insertions(+), 32 deletions(-) create mode 100644 exercise.webapi/DTO/AuthorDTO.cs create mode 100644 exercise.webapi/DTO/BookDTO.cs create mode 100644 exercise.webapi/DTO/PublisherDTO.cs create mode 100644 exercise.webapi/Endpoints/AuthorApi.cs create mode 100644 exercise.webapi/Endpoints/PublisherApi.cs create mode 100644 exercise.webapi/Models/Publisher.cs create mode 100644 exercise.webapi/Repository/AuthorRepository.cs create mode 100644 exercise.webapi/Repository/IAuthorRepository.cs create mode 100644 exercise.webapi/Repository/IPublisherRepository.cs create mode 100644 exercise.webapi/Repository/PublisherRepository.cs create mode 100644 exercise.webapi/ViewModels/BookPost.cs diff --git a/.gitignore b/.gitignore index 46d3f4b..3597991 100644 --- a/.gitignore +++ b/.gitignore @@ -367,5 +367,7 @@ FodyWeavers.xsd */**/obj/Debug */**/obj/Release */Migrations +/exercise.wwwapi/appsettings.json +exercise.wwwapi/appsettings.Development.json /workshop.wwwapi/appsettings.json /workshop.wwwapi/appsettings.Development.json \ No newline at end of file diff --git a/exercise.webapi/DTO/AuthorDTO.cs b/exercise.webapi/DTO/AuthorDTO.cs new file mode 100644 index 0000000..e8b6155 --- /dev/null +++ b/exercise.webapi/DTO/AuthorDTO.cs @@ -0,0 +1,10 @@ +namespace exercise.webapi.DTO +{ + public class AuthorDTO + { + public string Name { 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..8076deb --- /dev/null +++ b/exercise.webapi/DTO/BookDTO.cs @@ -0,0 +1,10 @@ +namespace exercise.webapi.DTO +{ + public class BookDTO + { + public string Title { get; set; } + public string AuthorName { get; set; } + public string AuthorEmail { get; set; } + public string PublisherName { get; set; } + } +} diff --git a/exercise.webapi/DTO/PublisherDTO.cs b/exercise.webapi/DTO/PublisherDTO.cs new file mode 100644 index 0000000..9b796fc --- /dev/null +++ b/exercise.webapi/DTO/PublisherDTO.cs @@ -0,0 +1,8 @@ +namespace exercise.webapi.DTO +{ + public class PublisherDTO + { + public string Name { get; set; } + public List Books { get; set; } + } +} diff --git a/exercise.webapi/Data/DataContext.cs b/exercise.webapi/Data/DataContext.cs index b6be7a9..5f17371 100644 --- a/exercise.webapi/Data/DataContext.cs +++ b/exercise.webapi/Data/DataContext.cs @@ -1,6 +1,7 @@ using exercise.webapi.Models; using Microsoft.EntityFrameworkCore; using System.Collections.Generic; +using System.Diagnostics; using System.Reflection.Emit; namespace exercise.webapi.Data @@ -8,9 +9,12 @@ namespace exercise.webapi.Data public class DataContext : DbContext { + private readonly string _connectionString; public DataContext(DbContextOptions options) : base(options) { - + var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); + _connectionString = configuration.GetValue("ConnectionStrings:DefaultConnectionString")!; + } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { @@ -24,9 +28,15 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity().HasData(seeder.Authors); modelBuilder.Entity().HasData(seeder.Books); + modelBuilder.Entity().HasData(seeder.Publishers); + + } + + public DbSet Authors { get; set; } public DbSet Books { get; set; } + public DbSet Publishers { get; set; } } } diff --git a/exercise.webapi/Data/Seeder.cs b/exercise.webapi/Data/Seeder.cs index 955e3c8..897c48a 100644 --- a/exercise.webapi/Data/Seeder.cs +++ b/exercise.webapi/Data/Seeder.cs @@ -79,12 +79,14 @@ public class Seeder private List _authors = new List(); private List _books = new List(); + private List _publishers = new List(); public Seeder() { Random authorRandom = new Random(); Random bookRandom = new Random(); + Random publisherRandom = new Random(); @@ -105,13 +107,23 @@ public Seeder() book.Id = y; book.Title = $"{_firstword[bookRandom.Next(_firstword.Count)]} {_secondword[bookRandom.Next(_secondword.Count)]} {_thirdword[bookRandom.Next(_thirdword.Count)]}"; book.AuthorId = _authors[authorRandom.Next(_authors.Count)].Id; - //book.Author = authors[book.AuthorId-1]; + book.PublisherId = publisherRandom.Next(250); + // book.Author = _authors[book.AuthorId-1]; _books.Add(book); } + for (int p = 1; p < 250; p++) + { + Publisher publisher = new Publisher(); + publisher.Id = p; + publisher.Name = $"{_firstword[publisherRandom.Next(_firstword.Count)]} {_secondword[publisherRandom.Next(_secondword.Count)]} {_thirdword[publisherRandom.Next(_thirdword.Count)]}"; + _publishers.Add(publisher); + + } } public List Authors { get { return _authors; } } public List Books { get { return _books; } } + public List Publishers { get { return _publishers; } } } } diff --git a/exercise.webapi/Endpoints/AuthorApi.cs b/exercise.webapi/Endpoints/AuthorApi.cs new file mode 100644 index 0000000..234b07c --- /dev/null +++ b/exercise.webapi/Endpoints/AuthorApi.cs @@ -0,0 +1,76 @@ +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 +{ + public static class AuthorApi + { + public static void ConfigureAuthorApi(this WebApplication app) + { + var authors = app.MapGroup("authors"); + authors.MapGet("/authors", GetAuthorById); + authors.MapGet("/", GetAllAuthors); + } + + + [ProducesResponseType(StatusCodes.Status200OK)] + private static async Task GetAllAuthors(IAuthorRepository authorRepository) + { + + try + { + var authors = await authorRepository.GetAllAuthors(); + + var bookDtos = authors.Select(x => new AuthorDTO + { + Name = $"{x.FirstName} {x.LastName}", + Email = x.Email, + Books = x.Books.Select(b => new BookDTO + { + Title = b.Title, + AuthorName = $"{b.Author.FirstName} {b.Author.LastName}", + AuthorEmail = b.Author.Email + }).ToList() + + }).ToList(); + + + return TypedResults.Ok(bookDtos); + } + + catch (Exception ex) + { + + return TypedResults.BadRequest(ex.Message); + + } + } + + + [ProducesResponseType(StatusCodes.Status200OK)] + private static async Task GetAuthorById(IAuthorRepository authorRepository, int id) + { + var author = await authorRepository.GetAuthorById(id); + + if (author == null) + return TypedResults.NotFound($"Author with id {id} does not exist"); + + + var authorDto = new AuthorDTO + { + Name = $"{author.FirstName} {author.LastName}", + Email = author.Email, + Books = author.Books.Select(b => new BookDTO + { + Title = b.Title, + PublisherName = b.Publisher.Name + }).ToList() + }; + return TypedResults.Ok(authorDto); + } + + } +} diff --git a/exercise.webapi/Endpoints/BookApi.cs b/exercise.webapi/Endpoints/BookApi.cs index 6758215..3eebeb5 100644 --- a/exercise.webapi/Endpoints/BookApi.cs +++ b/exercise.webapi/Endpoints/BookApi.cs @@ -1,5 +1,10 @@ -using exercise.webapi.Models; +using System.Reflection; +using exercise.webapi.DTO; +using exercise.webapi.Models; using exercise.webapi.Repository; +using exercise.webapi.ViewModels; +using Microsoft.AspNetCore.Http.HttpResults; +using Microsoft.AspNetCore.Mvc; using static System.Reflection.Metadata.BlobBuilder; namespace exercise.webapi.Endpoints @@ -8,13 +13,167 @@ public static class BookApi { public static void ConfigureBooksApi(this WebApplication app) { - app.MapGet("/books", GetBooks); + var books = app.MapGroup("books"); + books.MapGet("/books", GetAllBooks); + books.MapGet("/books{id}", GetBookById); + books.MapPost("/books/{id}", CreateBook); + books.MapDelete("/books{id}", DeleteBook); + books.MapPut("/books{id}", UpdateBookAuthor); } - private static async Task GetBooks(IBookRepository bookRepository) + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + private static async Task GetAllBooks(IBookRepository repository) { - var books = await bookRepository.GetAllBooks(); - return TypedResults.Ok(books); + + try + { + var books = await repository.GetAllBooks(); + var bookDto = books.Select(b => new BookDTO + { + Title = b.Title, + AuthorName = $"{b.Author.FirstName} {b.Author.LastName}", + AuthorEmail = b.Author.Email, + PublisherName = b.Publisher.Name}).ToList(); + + + return TypedResults.Ok(bookDto); + } + + catch (Exception ex) + { + + return TypedResults.BadRequest(ex.Message); + + } + } + + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + private static async Task GetBookById(IBookRepository repository, int id) + { + var book = await repository.GetBookById(id); + if (book == null) + { + + return TypedResults.NotFound("Book not found"); + + } + + var bookDto = new BookDTO + { + Title = book.Title, + AuthorEmail = book.Author.Email, + AuthorName = $"{book.Author.FirstName} {book.Author.LastName}", + PublisherName = book.Publisher.Name + + + + }; + + return TypedResults.Ok(bookDto); + } + + + + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType (StatusCodes.Status400BadRequest)] + private static async Task CreateBook(IBookRepository bookRepository, IAuthorRepository authorRepository, IPublisherRepository publisherRepository, BookPost model) + { + var author = await authorRepository.GetAuthorById(model.AuthorId); + var publisher = await publisherRepository.GetPublisherById(model.PublisherId); + if (author == null || publisher == null) + { + return TypedResults.NotFound("Author or Publisher not found"); + } + + if (model.Title == null) + { + return TypedResults.BadRequest("Book object not valid"); + + } + + Book book = new Book() + { + Title = model.Title, + AuthorId = author.Id, + PublisherId = publisher.Id, + Author = author, + Publisher = publisher + }; + + // Save the book to the repository + await bookRepository.CreateBook(book); + + var bookDto = new BookDTO + { + Title = book.Title, + AuthorName = $"{book.Author.FirstName} {book.Author.LastName}", + AuthorEmail = book.Author.Email, + PublisherName = book.Publisher.Name + }; + + return TypedResults.Created($"/neondb/books/{book.Id}", bookDto); + } + + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task DeleteBook(IBookRepository repository, int id) + { + var book = await repository.GetBookById(id); + + if (book == null) + { + return TypedResults.NotFound("Book not found"); + } + + await repository.DeleteBook(book.Id); + + return TypedResults.Ok(book); + + + } + + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + private static async Task UpdateBookAuthor(IBookRepository bookRepository, IAuthorRepository authorRepository, IPublisherRepository publisherRepository, int id, BookPost model) + { + var book = await bookRepository.GetBookById(id); + var author = await authorRepository.GetAuthorById(model.AuthorId); + var publisher = await publisherRepository.GetPublisherById(model.PublisherId); + + + if (book == null) + return TypedResults.NotFound($"Book with the id: {id} does not exist"); + + if (author == null) + return TypedResults.NotFound($"Author with the id: {model.AuthorId} not found"); + + if (publisher == null) + return TypedResults.NotFound($"Publisher with id {model.PublisherId} not found"); + + book.Title = model.Title; + book.AuthorId = model.AuthorId; + book.Author = author; + book.PublisherId = model.PublisherId; + book.Publisher = publisher; + + var response = await bookRepository.UpdateBookAuthor(book.Id, book.AuthorId); + var bookDTO = new BookDTO + { + Title = response.Title, + AuthorName = response.Author.FirstName + " " + response.Author.LastName, + AuthorEmail = response.Author.Email, + PublisherName = response.Publisher.Name + }; + return TypedResults.Ok(bookDTO); + } + } -} + +} \ No newline at end of file diff --git a/exercise.webapi/Endpoints/PublisherApi.cs b/exercise.webapi/Endpoints/PublisherApi.cs new file mode 100644 index 0000000..1c5fdda --- /dev/null +++ b/exercise.webapi/Endpoints/PublisherApi.cs @@ -0,0 +1,67 @@ +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 +{ + public static class PublisherApi + { + public static void ConfigurePublisherApi(this WebApplication app) + { + var publisher = app.MapGroup("publishers"); + publisher.MapGet("/publishers", GetAllPublishers); + publisher.MapGet("/{id:int}", GetPublisherById); + } + [ProducesResponseType(StatusCodes.Status200OK)] + private static async Task GetAllPublishers(IPublisherRepository publisherRepository) + { + + + var publishers = await publisherRepository.GetAllPublishers(); + + + var dtoList = publishers.Select(p => new PublisherDTO + { + Name = p.Name, + Books = p.Books.Select(b => new BookDTO + { + Title = b.Title, + AuthorName = $"{b.Author.FirstName} {b.Author.LastName}", + AuthorEmail = b.Author.Email, + PublisherName = b.Publisher.Name + }).ToList() + }).ToList(); + + return TypedResults.Ok(dtoList); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + private static async Task GetPublisherById(IPublisherRepository publisherRepository, int id) + { + var publisher = await publisherRepository.GetPublisherById(id); + if (publisher == null) + { + return TypedResults.NotFound(); + } + + var dto = new PublisherDTO + { + Name = publisher.Name, + Books = publisher.Books.Select(b => new BookDTO + { + Title = b.Title, + AuthorName = $"{b.Author.FirstName} {b.Author.LastName}", + AuthorEmail = b.Author.Email + }).ToList() + }; + + return TypedResults.Ok(dto); + } + + + } + +} + \ No newline at end of file diff --git a/exercise.webapi/Models/Author.cs b/exercise.webapi/Models/Author.cs index 9f47878..2f6ee07 100644 --- a/exercise.webapi/Models/Author.cs +++ b/exercise.webapi/Models/Author.cs @@ -8,8 +8,7 @@ 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..e87e809 100644 --- a/exercise.webapi/Models/Book.cs +++ b/exercise.webapi/Models/Book.cs @@ -6,8 +6,16 @@ public class Book { public int Id { get; set; } public string Title { get; set; } - + + [ForeignKey("Author")] public int AuthorId { get; set; } public Author Author { get; set; } + + [ForeignKey("Publisher")] + public int PublisherId { get; set; } + public Publisher Publisher { get; set; } + + + } } diff --git a/exercise.webapi/Models/Publisher.cs b/exercise.webapi/Models/Publisher.cs new file mode 100644 index 0000000..65c32e5 --- /dev/null +++ b/exercise.webapi/Models/Publisher.cs @@ -0,0 +1,11 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace exercise.webapi.Models +{ + public class Publisher + { + public int Id { get; set; } + public string Name { get; set; } + public ICollection Books { get; set; } + } +} diff --git a/exercise.webapi/Program.cs b/exercise.webapi/Program.cs index 43dec56..52a0920 100644 --- a/exercise.webapi/Program.cs +++ b/exercise.webapi/Program.cs @@ -2,6 +2,7 @@ using exercise.webapi.Endpoints; using exercise.webapi.Repository; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; var builder = WebApplication.CreateBuilder(args); @@ -11,6 +12,8 @@ builder.Services.AddSwaggerGen(); builder.Services.AddDbContext(opt => opt.UseInMemoryDatabase("Library")); builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); var app = builder.Build(); @@ -27,6 +30,10 @@ app.UseSwaggerUI(); } + + app.UseHttpsRedirection(); app.ConfigureBooksApi(); +app.ConfigureAuthorApi(); +app.ConfigurePublisherApi(); app.Run(); diff --git a/exercise.webapi/Repository/AuthorRepository.cs b/exercise.webapi/Repository/AuthorRepository.cs new file mode 100644 index 0000000..ddc4111 --- /dev/null +++ b/exercise.webapi/Repository/AuthorRepository.cs @@ -0,0 +1,27 @@ +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 GetAuthorById(int id) + { + return await _db.Authors.FirstOrDefaultAsync(a => a.Id == id); + } + + public async Task> GetAllAuthors() + { + return await _db.Authors.Include(b => b.Books).ThenInclude(p => p.Publisher).ToListAsync(); + + } + } +} \ No newline at end of file diff --git a/exercise.webapi/Repository/BookRepository.cs b/exercise.webapi/Repository/BookRepository.cs index 1f5e64a..8749df2 100644 --- a/exercise.webapi/Repository/BookRepository.cs +++ b/exercise.webapi/Repository/BookRepository.cs @@ -4,19 +4,67 @@ namespace exercise.webapi.Repository { - public class BookRepository: IBookRepository + public class BookRepository : IBookRepository { DataContext _db; - + public BookRepository(DataContext db) { _db = db; } - public async Task> GetAllBooks() + public async Task> GetAllBooks() { - return await _db.Books.Include(b => b.Author).ToListAsync(); + return await _db.Books.Include(b => b.Author).Include(b => b.Publisher).ToListAsync(); + + } + + public async Task GetBookById(int id) + { + return await _db.Books.Include(b => b.Author).Include(b => b.Publisher).FirstOrDefaultAsync(book => book.Id == id); + } + + public async Task CreateBook(Book entity) + { + await _db.Books.AddAsync(entity); + await _db.SaveChangesAsync(); + return entity; + + + } + + public async Task UpdateBookAuthor(int bookId, int authorId) + + { + + var book = await _db.Books.FirstOrDefaultAsync( b => b.Id == bookId); + var author = await _db.Authors.FirstOrDefaultAsync( a => a.Id == authorId); + book.AuthorId = authorId; + book.Author = author; + + if (book != null || author != null) + { + _db.Books.Update(book); + await _db.SaveChangesAsync(); + + return book; + } + + else return null; + + } + + public async Task DeleteBook(int id) + { + Book book = GetBookById(id).Result; + _db.Books.Remove(book); + await _db.SaveChangesAsync(); + return book; + } + + + } } diff --git a/exercise.webapi/Repository/IAuthorRepository.cs b/exercise.webapi/Repository/IAuthorRepository.cs new file mode 100644 index 0000000..01dac0a --- /dev/null +++ b/exercise.webapi/Repository/IAuthorRepository.cs @@ -0,0 +1,13 @@ +using exercise.webapi.Models; + +namespace exercise.webapi.Repository +{ + public interface IAuthorRepository + { + Task> GetAllAuthors(); + + Task GetAuthorById(int id); + + } + +} diff --git a/exercise.webapi/Repository/IBookRepository.cs b/exercise.webapi/Repository/IBookRepository.cs index f860016..d82bdc8 100644 --- a/exercise.webapi/Repository/IBookRepository.cs +++ b/exercise.webapi/Repository/IBookRepository.cs @@ -4,6 +4,11 @@ namespace exercise.webapi.Repository { public interface IBookRepository { - public Task> GetAllBooks(); + public Task> GetAllBooks(); + Task GetBookById(int id); + Task UpdateBookAuthor(int bookId, int authorId); + Task CreateBook(Book book); + Task DeleteBook(int id); + } } diff --git a/exercise.webapi/Repository/IPublisherRepository.cs b/exercise.webapi/Repository/IPublisherRepository.cs new file mode 100644 index 0000000..8a620c0 --- /dev/null +++ b/exercise.webapi/Repository/IPublisherRepository.cs @@ -0,0 +1,13 @@ +using exercise.webapi.Models; + +namespace exercise.webapi.Repository +{ + public interface IPublisherRepository + { + Task GetPublisherById(int id); + Task> GetAllPublishers(); + + } + + +} diff --git a/exercise.webapi/Repository/PublisherRepository.cs b/exercise.webapi/Repository/PublisherRepository.cs new file mode 100644 index 0000000..24425d2 --- /dev/null +++ b/exercise.webapi/Repository/PublisherRepository.cs @@ -0,0 +1,28 @@ +using exercise.webapi.Data; +using exercise.webapi.Models; +using Microsoft.EntityFrameworkCore; + +namespace exercise.webapi.Repository +{ + public class PublisherRepository : IPublisherRepository + { + DataContext _db; + + public PublisherRepository(DataContext db) + { + _db = db; + } + + public async Task GetPublisherById(int id) + { + return await _db.Publishers.Include(p => p.Books).ThenInclude(b => b.Author).FirstOrDefaultAsync(p => p.Id == id); + } + + public async Task> GetAllPublishers() + { + return await _db.Publishers.Include(b => b.Books).ThenInclude(b => b.Author).ToListAsync(); + + } + } +} + diff --git a/exercise.webapi/ViewModels/BookPost.cs b/exercise.webapi/ViewModels/BookPost.cs new file mode 100644 index 0000000..39b30f3 --- /dev/null +++ b/exercise.webapi/ViewModels/BookPost.cs @@ -0,0 +1,12 @@ +using exercise.webapi.Models; + +namespace exercise.webapi.ViewModels +{ + public class BookPost + { + + public string Title { get; set; } + public int AuthorId { get; set; } + public int PublisherId { get; set; } + } +} diff --git a/exercise.webapi/appsettings.Development.json b/exercise.webapi/appsettings.Development.json index 63d13d3..aa0a4d9 100644 --- a/exercise.webapi/appsettings.Development.json +++ b/exercise.webapi/appsettings.Development.json @@ -1,14 +1,16 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*", + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + + "ConnectionStrings": { + "DefaultConnectionString": "Host=ep-summer-glade-a826fmci-pooler.eastus2.azure.neon.tech; Database=neondb; Username=neondb_owner; Password=npg_NhisoQg8jv6b; SSL Mode=Require" + - "ConnectionStrings": { - "DefaultConnectionString": "Host=ep-winter-silence-a8e5oju7.eastus2.azure.neon.tech; Database=neondb; Username=neondb_owner; Password=npg_nBmYdDo0Lr3z;" + } +} - } -} \ No newline at end of file diff --git a/exercise.webapi/appsettings.json b/exercise.webapi/appsettings.json index 63d13d3..1a153ca 100644 --- a/exercise.webapi/appsettings.json +++ b/exercise.webapi/appsettings.json @@ -7,8 +7,8 @@ }, "AllowedHosts": "*", - "ConnectionStrings": { - "DefaultConnectionString": "Host=ep-winter-silence-a8e5oju7.eastus2.azure.neon.tech; Database=neondb; Username=neondb_owner; Password=npg_nBmYdDo0Lr3z;" + "ConnectionStrings": { + "DefaultConnectionString": "Host=ep-summer-glade-a826fmci-pooler.eastus2.azure.neon.tech; Database=neondb; Username=neondb_owner; Password=npg_NhisoQg8jv6b; SSL Mode=Require" - } + } } \ No newline at end of file diff --git a/exercise.webapi/exercise.webapi.csproj b/exercise.webapi/exercise.webapi.csproj index 0ff4269..75ca71f 100644 --- a/exercise.webapi/exercise.webapi.csproj +++ b/exercise.webapi/exercise.webapi.csproj @@ -8,8 +8,13 @@ - - + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + From 750ff622b5bd30e9c93a5c5476cec61bdb92f12f Mon Sep 17 00:00:00 2001 From: Kristoffer Blucher Date: Fri, 24 Jan 2025 14:56:47 +0100 Subject: [PATCH 2/3] fix --- .gitignore | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 3597991..b357a0c 100644 --- a/.gitignore +++ b/.gitignore @@ -368,6 +368,4 @@ FodyWeavers.xsd */**/obj/Release */Migrations /exercise.wwwapi/appsettings.json -exercise.wwwapi/appsettings.Development.json -/workshop.wwwapi/appsettings.json -/workshop.wwwapi/appsettings.Development.json \ No newline at end of file +/exercise.wwwapi/appsettings.Development.json From 76fb2552cdcfa99d1ea2d1c55cec1ee041bfba5a Mon Sep 17 00:00:00 2001 From: Kristoffer Blucher Date: Mon, 3 Feb 2025 13:47:49 +0100 Subject: [PATCH 3/3] Testing mapster and fix for books --- exercise.webapi/DTO/AuthorDTO.cs | 2 +- exercise.webapi/DTO/AuthorWithBooks.cs | 10 +++++++++ exercise.webapi/DTO/BookResponse.cs | 12 +++++++++++ exercise.webapi/Endpoints/AuthorApi.cs | 7 +++--- exercise.webapi/Endpoints/BookApi.cs | 30 ++++++++++---------------- exercise.webapi/Models/Author.cs | 1 + exercise.webapi/exercise.webapi.csproj | 3 ++- 7 files changed, 40 insertions(+), 25 deletions(-) create mode 100644 exercise.webapi/DTO/AuthorWithBooks.cs create mode 100644 exercise.webapi/DTO/BookResponse.cs diff --git a/exercise.webapi/DTO/AuthorDTO.cs b/exercise.webapi/DTO/AuthorDTO.cs index e8b6155..29a6350 100644 --- a/exercise.webapi/DTO/AuthorDTO.cs +++ b/exercise.webapi/DTO/AuthorDTO.cs @@ -4,7 +4,7 @@ public class AuthorDTO { public string Name { get; set; } public string Email { get; set; } - public List Books { get; set; } + public List Books { get; set; } } } diff --git a/exercise.webapi/DTO/AuthorWithBooks.cs b/exercise.webapi/DTO/AuthorWithBooks.cs new file mode 100644 index 0000000..01673ad --- /dev/null +++ b/exercise.webapi/DTO/AuthorWithBooks.cs @@ -0,0 +1,10 @@ +namespace exercise.webapi.DTO +{ + public class AuthorWithBooks + { + public string Title { get; set; } + public string PublisherName { get; set; } + + + } +} diff --git a/exercise.webapi/DTO/BookResponse.cs b/exercise.webapi/DTO/BookResponse.cs new file mode 100644 index 0000000..48b2e07 --- /dev/null +++ b/exercise.webapi/DTO/BookResponse.cs @@ -0,0 +1,12 @@ +namespace exercise.webapi.DTO +{ + public class BookResponse + { + public string Title { get; set; } + public string AuthorName { get; set; } + public string PublisherName { get; set; } + + + + } +} diff --git a/exercise.webapi/Endpoints/AuthorApi.cs b/exercise.webapi/Endpoints/AuthorApi.cs index 234b07c..18c9edb 100644 --- a/exercise.webapi/Endpoints/AuthorApi.cs +++ b/exercise.webapi/Endpoints/AuthorApi.cs @@ -28,11 +28,10 @@ private static async Task GetAllAuthors(IAuthorRepository authorReposit { Name = $"{x.FirstName} {x.LastName}", Email = x.Email, - Books = x.Books.Select(b => new BookDTO + Books = x.Books.Select(b => new AuthorWithBooks { Title = b.Title, - AuthorName = $"{b.Author.FirstName} {b.Author.LastName}", - AuthorEmail = b.Author.Email + PublisherName = b.Publisher.Name, }).ToList() }).ToList(); @@ -63,7 +62,7 @@ private static async Task GetAuthorById(IAuthorRepository authorReposit { Name = $"{author.FirstName} {author.LastName}", Email = author.Email, - Books = author.Books.Select(b => new BookDTO + Books = author.Books.Select(b => new AuthorWithBooks { Title = b.Title, PublisherName = b.Publisher.Name diff --git a/exercise.webapi/Endpoints/BookApi.cs b/exercise.webapi/Endpoints/BookApi.cs index 3eebeb5..e817370 100644 --- a/exercise.webapi/Endpoints/BookApi.cs +++ b/exercise.webapi/Endpoints/BookApi.cs @@ -3,6 +3,7 @@ using exercise.webapi.Models; using exercise.webapi.Repository; using exercise.webapi.ViewModels; +using Mapster; using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using static System.Reflection.Metadata.BlobBuilder; @@ -29,11 +30,10 @@ private static async Task GetAllBooks(IBookRepository repository) try { var books = await repository.GetAllBooks(); - var bookDto = books.Select(b => new BookDTO + var bookDto = books.Select(b => new BookResponse { Title = b.Title, AuthorName = $"{b.Author.FirstName} {b.Author.LastName}", - AuthorEmail = b.Author.Email, PublisherName = b.Publisher.Name}).ToList(); @@ -56,23 +56,12 @@ private static async Task GetBookById(IBookRepository repository, int i var book = await repository.GetBookById(id); if (book == null) { - return TypedResults.NotFound("Book not found"); } + var response = book.Adapt(); - var bookDto = new BookDTO - { - Title = book.Title, - AuthorEmail = book.Author.Email, - AuthorName = $"{book.Author.FirstName} {book.Author.LastName}", - PublisherName = book.Publisher.Name - - - - }; - - return TypedResults.Ok(bookDto); + return TypedResults.Ok(response); } @@ -96,27 +85,30 @@ private static async Task CreateBook(IBookRepository bookRepository, IA } + Console.WriteLine("Debug:" + author); + Book book = new Book() { Title = model.Title, AuthorId = author.Id, PublisherId = publisher.Id, Author = author, - Publisher = publisher + Publisher = publisher, + + }; // Save the book to the repository await bookRepository.CreateBook(book); - var bookDto = new BookDTO + var response = new BookResponse { Title = book.Title, AuthorName = $"{book.Author.FirstName} {book.Author.LastName}", - AuthorEmail = book.Author.Email, PublisherName = book.Publisher.Name }; - return TypedResults.Created($"/neondb/books/{book.Id}", bookDto); + return TypedResults.Created($"/neondb/books/{book.Id}", response); } diff --git a/exercise.webapi/Models/Author.cs b/exercise.webapi/Models/Author.cs index 2f6ee07..064acf3 100644 --- a/exercise.webapi/Models/Author.cs +++ b/exercise.webapi/Models/Author.cs @@ -10,5 +10,6 @@ public class Author public string Email { get; set; } public ICollection Books { get; set; } = new List(); + } } diff --git a/exercise.webapi/exercise.webapi.csproj b/exercise.webapi/exercise.webapi.csproj index 75ca71f..35847af 100644 --- a/exercise.webapi/exercise.webapi.csproj +++ b/exercise.webapi/exercise.webapi.csproj @@ -1,4 +1,4 @@ - + net9.0 @@ -8,6 +8,7 @@ +