From db9f7348fa9c0ec8e9ee5aa5576797b321274354 Mon Sep 17 00:00:00 2001 From: Alex Hyett Date: Mon, 3 Jul 2023 14:50:35 +0100 Subject: [PATCH] Add book queries --- docker-compose.yml | 18 +++++ .../Controllers/AuthorsController.cs | 30 +++++++++ ...ibraryController.cs => BooksController.cs} | 21 ++---- src/GitHubActionsDemo.Api/appsettings.json | 2 +- .../DbContext.cs | 34 +++++----- .../GitHubActionsDemo.Persistance.csproj | 1 + .../InfrastructureExtensions.cs | 7 ++ .../LibraryRespository.cs | 66 +++++++++++++++++-- .../Mappings/AuthorDbMap.cs | 11 ++++ .../Mappings/BookDbMap.cs | 11 ++++ .../Models/AuthorDb.cs | 25 ++----- .../Models/BookDb.cs | 33 ++-------- 12 files changed, 176 insertions(+), 83 deletions(-) create mode 100644 docker-compose.yml create mode 100644 src/GitHubActionsDemo.Api/Controllers/AuthorsController.cs rename src/GitHubActionsDemo.Api/Controllers/{LibraryController.cs => BooksController.cs} (65%) create mode 100644 src/GitHubActionsDemo.Persistance/Mappings/AuthorDbMap.cs create mode 100644 src/GitHubActionsDemo.Persistance/Mappings/BookDbMap.cs diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..53b72fc --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,18 @@ +version: '3.8' +services: + db: + image: mysql:8.0 + cap_add: + - SYS_NICE + restart: always + environment: + - MYSQL_DATABASE=library + - MYSQL_ROOT_PASSWORD=libraryDbPassword + ports: + - '3306:3306' + volumes: + - db:/var/lib/mysql + - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql +volumes: + db: + driver: local \ No newline at end of file diff --git a/src/GitHubActionsDemo.Api/Controllers/AuthorsController.cs b/src/GitHubActionsDemo.Api/Controllers/AuthorsController.cs new file mode 100644 index 0000000..0f4bd1e --- /dev/null +++ b/src/GitHubActionsDemo.Api/Controllers/AuthorsController.cs @@ -0,0 +1,30 @@ +using GitHubActionsDemo.Api.Models; +using GitHubActionsDemo.Api.Mappers; +using GitHubActionsDemo.Service; +using Microsoft.AspNetCore.Mvc; + +namespace GitHubActionsDemo.Api.Controllers; + +[ApiController] +[Route("[controller]")] +public class AuthorsController : ControllerBase +{ + private readonly ILogger _logger; + private readonly ILibraryService _libraryService; + + public AuthorsController( + ILogger logger, + ILibraryService libraryService + ) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _libraryService = libraryService ?? throw new ArgumentNullException(nameof(libraryService)); + } + + [HttpPost] + public async Task AddAuthorAsync(AuthorRequest authorRequest) + { + var author = await _libraryService.AddAuthorAsync(authorRequest.Map()); + return author.Map(); + } +} diff --git a/src/GitHubActionsDemo.Api/Controllers/LibraryController.cs b/src/GitHubActionsDemo.Api/Controllers/BooksController.cs similarity index 65% rename from src/GitHubActionsDemo.Api/Controllers/LibraryController.cs rename to src/GitHubActionsDemo.Api/Controllers/BooksController.cs index f9e1fce..3d20072 100644 --- a/src/GitHubActionsDemo.Api/Controllers/LibraryController.cs +++ b/src/GitHubActionsDemo.Api/Controllers/BooksController.cs @@ -7,13 +7,13 @@ namespace GitHubActionsDemo.Api.Controllers; [ApiController] [Route("[controller]")] -public class LibraryController : ControllerBase +public class BooksController : ControllerBase { - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly ILibraryService _libraryService; - public LibraryController( - ILogger logger, + public BooksController( + ILogger logger, ILibraryService libraryService ) { @@ -21,31 +21,24 @@ public class LibraryController : ControllerBase _libraryService = libraryService ?? throw new ArgumentNullException(nameof(libraryService)); } - [HttpGet(Name = "GetBooks")] + [HttpGet] public async Task> GetBooksAsync(int page = 0, int pageSize = 10) { var books = await _libraryService.GetBooksAsync(page, pageSize); return books.Select(x => x.Map()); } - [HttpGet(Name = "GetBook")] + [HttpGet("{bookId}")] public async Task GetBookAsync(int bookId) { var book = await _libraryService.GetBookAsync(bookId); return book.Map(); } - [HttpPost(Name = "AddBook")] + [HttpPost] public async Task AddBookAsync(BookRequest bookRequest) { var book = await _libraryService.AddBookAsync(bookRequest.Map()); return book.Map(); } - - [HttpPost(Name = "AddAuthor")] - public async Task AddAuthorAsync(AuthorRequest authorRequest) - { - var author = await _libraryService.AddAuthorAsync(authorRequest.Map()); - return author.Map(); - } } diff --git a/src/GitHubActionsDemo.Api/appsettings.json b/src/GitHubActionsDemo.Api/appsettings.json index cb136aa..b3330a5 100644 --- a/src/GitHubActionsDemo.Api/appsettings.json +++ b/src/GitHubActionsDemo.Api/appsettings.json @@ -1,6 +1,6 @@ { "DbSettings": { - "ConnectionString": "Server=localhost; Database=library; Uid=root; Pwd=libraryDbPassword;", + "ConnectionString": "Server=localhost:3306; Database=library; Uid=root; Pwd=libraryDbPassword;", "Database": "library" }, "Logging": { diff --git a/src/GitHubActionsDemo.Persistance/DbContext.cs b/src/GitHubActionsDemo.Persistance/DbContext.cs index 64449a0..15c6d41 100644 --- a/src/GitHubActionsDemo.Persistance/DbContext.cs +++ b/src/GitHubActionsDemo.Persistance/DbContext.cs @@ -44,13 +44,13 @@ public class DbContext : IDbContext async Task _initAuthors() { var sql = """ - CREATE TABLE IF NOT EXISTS Authors ( - AuthorId INT NOT NULL AUTO_INCREMENT, - FirstName VARCHAR(255) NOT NULL, - LastName VARCHAR(255) NOT NULL, - DateCreated DATETIME NOT NULL, - DateModified DATETIME NOT NULL, - PRIMARY KEY (AuthorId) + CREATE TABLE IF NOT EXISTS authors ( + author_id INT NOT NULL AUTO_INCREMENT, + first_name VARCHAR(255) NOT NULL, + last_name VARCHAR(255) NOT NULL, + date_created DATETIME NOT NULL, + date_modified DATETIME NOT NULL, + PRIMARY KEY (author_id) ); """; await connection.ExecuteAsync(sql); @@ -59,16 +59,16 @@ public class DbContext : IDbContext async Task _initBooks() { var sql = """ - CREATE TABLE IF NOT EXISTS Books( - BookId INT NOT NULL AUTO_INCREMENT, - Title VARCHAR(255), - AuthorId INT NOT NULL, - Isbn VARCHAR(13) NOT NULL, - DatePublished DATETIME NOT NULL, - DateCreated DATETIME NOT NULL, - DateModified DATETIME NOT NULL, - PRIMARY KEY(BookId), - FOREIGN KEY(AuthorId) REFERENCES Authors(AuthorId) + CREATE TABLE IF NOT EXISTS books( + book_id INT NOT NULL AUTO_INCREMENT, + title VARCHAR(255), + author_id INT NOT NULL, + isbn VARCHAR(13) NOT NULL, + date_published DATETIME NOT NULL, + date_created DATETIME NOT NULL, + date_modified DATETIME NOT NULL, + PRIMARY KEY(book_id), + FOREIGN KEY(author_id) REFERENCES authors(author_id) ); """; await connection.ExecuteAsync(sql); diff --git a/src/GitHubActionsDemo.Persistance/GitHubActionsDemo.Persistance.csproj b/src/GitHubActionsDemo.Persistance/GitHubActionsDemo.Persistance.csproj index 4132621..8ba0767 100644 --- a/src/GitHubActionsDemo.Persistance/GitHubActionsDemo.Persistance.csproj +++ b/src/GitHubActionsDemo.Persistance/GitHubActionsDemo.Persistance.csproj @@ -8,6 +8,7 @@ + diff --git a/src/GitHubActionsDemo.Persistance/Infrastructure/InfrastructureExtensions.cs b/src/GitHubActionsDemo.Persistance/Infrastructure/InfrastructureExtensions.cs index ca31f7a..354dc98 100644 --- a/src/GitHubActionsDemo.Persistance/Infrastructure/InfrastructureExtensions.cs +++ b/src/GitHubActionsDemo.Persistance/Infrastructure/InfrastructureExtensions.cs @@ -1,3 +1,4 @@ +using Dapper.FluentMap; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; @@ -16,5 +17,11 @@ public static class InfrastructureExtensions using var scope = app.Services.CreateScope(); var context = scope.ServiceProvider.GetRequiredService(); await context.Init(); + + FluentMapper.Initialize(config => + { + config.AddMap(new AuthorDbMap()); + config.AddMap(new BookDbMap()); + }); } } diff --git a/src/GitHubActionsDemo.Persistance/LibraryRespository.cs b/src/GitHubActionsDemo.Persistance/LibraryRespository.cs index 45f6d83..23591ef 100644 --- a/src/GitHubActionsDemo.Persistance/LibraryRespository.cs +++ b/src/GitHubActionsDemo.Persistance/LibraryRespository.cs @@ -1,12 +1,15 @@ -using GitHubActionsDemo.Persistance.Models; +using System; +using Dapper; +using GitHubActionsDemo.Persistance.Models; namespace GitHubActionsDemo.Persistance; public class LibraryRespository : ILibraryRespository { - public LibraryRespository() + private readonly IDbContext _dbContext; + public LibraryRespository(IDbContext dbContext) { - + _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); } public async Task AddAuthorAsync(NewAuthorDb author) @@ -26,11 +29,64 @@ public class LibraryRespository : ILibraryRespository public async Task GetBookAsync(int bookId) { - throw new NotImplementedException(); + var sql = @$"SELECT + b.book_id AS {nameof(BookDb.BookId)}, + b.title AS {nameof(BookDb.Title)}, + b.isbn AS {nameof(BookDb.Isbn)}, + b.date_published AS {nameof(BookDb.DatePublished)}, + b.date_created AS Book{nameof(BookDb.DateCreated)}, + b.date_modified AS Book{nameof(BookDb.DateModified)}, + a.author_id AS {nameof(AuthorDb.AuthorId)}, + a.first_name AS {nameof(AuthorDb.FirstName)}, + a.last_name AS {nameof(AuthorDb.LastName)}, + a.date_created AS Author{nameof(AuthorDb.DateCreated)}, + a.date_modified AS Author{nameof(AuthorDb.DateModified)} + FROM books b + INNER JOIN authors a ON a.author_id = b.author_id + WHERE b.book_id = @BookId;"; + + var param = new + { + BookId = bookId + }; + + using (var connection = _dbContext.CreateConnection()) + { + var books = await connection.QueryAsync(sql, (book, author) => + { + book.Author = author; + return book; + }, param, splitOn: nameof(AuthorDb.AuthorId)); + + return books?.FirstOrDefault(); + } } public async Task> GetBooksAsync(int page, int pageSize) { - throw new NotImplementedException(); + var sql = @$"SELECT + b.book_id AS {nameof(BookDb.BookId)}, + b.title AS {nameof(BookDb.Title)}, + b.isbn AS {nameof(BookDb.Isbn)}, + b.date_published AS {nameof(BookDb.DatePublished)}, + b.date_created AS Book{nameof(BookDb.DateCreated)}, + b.date_modified AS Book{nameof(BookDb.DateModified)}, + a.author_id AS {nameof(AuthorDb.AuthorId)}, + a.first_name AS {nameof(AuthorDb.FirstName)}, + a.last_name AS {nameof(AuthorDb.LastName)}, + a.date_created AS Author{nameof(AuthorDb.DateCreated)}, + a.date_modified AS Author{nameof(AuthorDb.DateModified)} + FROM books b + INNER JOIN authors a ON a.author_id = b.author_id + ORDER BY b.book_id;"; + + using (var connection = _dbContext.CreateConnection()) + { + return await connection.QueryAsync(sql, (book, author) => + { + book.Author = author; + return book; + }, splitOn: nameof(AuthorDb.AuthorId)); + } } } diff --git a/src/GitHubActionsDemo.Persistance/Mappings/AuthorDbMap.cs b/src/GitHubActionsDemo.Persistance/Mappings/AuthorDbMap.cs new file mode 100644 index 0000000..a9e9bd8 --- /dev/null +++ b/src/GitHubActionsDemo.Persistance/Mappings/AuthorDbMap.cs @@ -0,0 +1,11 @@ +using Dapper.FluentMap.Mapping; +using GitHubActionsDemo.Persistance.Models; + +internal class AuthorDbMap : EntityMap +{ + internal AuthorDbMap() + { + Map(u => u.DateCreated).ToColumn($"Author{nameof(AuthorDb.DateCreated)}"); + Map(u => u.DateModified).ToColumn($"Author{nameof(AuthorDb.DateModified)}"); + } +} \ No newline at end of file diff --git a/src/GitHubActionsDemo.Persistance/Mappings/BookDbMap.cs b/src/GitHubActionsDemo.Persistance/Mappings/BookDbMap.cs new file mode 100644 index 0000000..ef52e59 --- /dev/null +++ b/src/GitHubActionsDemo.Persistance/Mappings/BookDbMap.cs @@ -0,0 +1,11 @@ +using Dapper.FluentMap.Mapping; +using GitHubActionsDemo.Persistance.Models; + +internal class BookDbMap : EntityMap +{ + internal BookDbMap() + { + Map(u => u.DateCreated).ToColumn($"Book{nameof(BookDb.DateCreated)}"); + Map(u => u.DateModified).ToColumn($"Book{nameof(BookDb.DateModified)}"); + } +} \ No newline at end of file diff --git a/src/GitHubActionsDemo.Persistance/Models/AuthorDb.cs b/src/GitHubActionsDemo.Persistance/Models/AuthorDb.cs index f587a5a..4d257e3 100644 --- a/src/GitHubActionsDemo.Persistance/Models/AuthorDb.cs +++ b/src/GitHubActionsDemo.Persistance/Models/AuthorDb.cs @@ -2,24 +2,9 @@ namespace GitHubActionsDemo.Persistance.Models; public class AuthorDb { - public AuthorDb( - int authorId, - string firstName, - string lastName, - DateTime dateCreated, - DateTime dateModified - ) - { - AuthorId = authorId; - FirstName = firstName; - LastName = lastName; - DateCreated = dateCreated; - DateModified = dateModified; - } - - public int AuthorId { get; } - public string FirstName { get; } - public string LastName { get; } - public DateTime DateCreated { get; } - public DateTime DateModified { get; } + public int AuthorId { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public DateTime DateCreated { get; set; } + public DateTime DateModified { get; set; } } diff --git a/src/GitHubActionsDemo.Persistance/Models/BookDb.cs b/src/GitHubActionsDemo.Persistance/Models/BookDb.cs index 5bad5ff..23bb74a 100644 --- a/src/GitHubActionsDemo.Persistance/Models/BookDb.cs +++ b/src/GitHubActionsDemo.Persistance/Models/BookDb.cs @@ -2,30 +2,11 @@ namespace GitHubActionsDemo.Persistance.Models; public class BookDb { - public BookDb( - int bookId, - string title, - AuthorDb author, - string isbn, - DateOnly datePublished, - DateTime dateCreated, - DateTime dateModified - ) - { - BookId = bookId; - Title = title; - Author = author; - Isbn = isbn; - DatePublished = datePublished; - DateCreated = dateCreated; - DateModified = dateModified; - } - - public int BookId { get; } - public string Title { get; } - public AuthorDb Author { get; } - public string Isbn { get; } - public DateOnly DatePublished { get; } - public DateTime DateCreated { get; } - public DateTime DateModified { get; } + public int BookId { get; set; } + public string Title { get; set; } + public AuthorDb Author { get; set; } + public string Isbn { get; set; } + public DateOnly DatePublished { get; set; } + public DateTime DateCreated { get; set; } + public DateTime DateModified { get; set; } }