Add book queries
This commit is contained in:
parent
146f8f6ff4
commit
db9f7348fa
12 changed files with 176 additions and 83 deletions
18
docker-compose.yml
Normal file
18
docker-compose.yml
Normal file
|
@ -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
|
30
src/GitHubActionsDemo.Api/Controllers/AuthorsController.cs
Normal file
30
src/GitHubActionsDemo.Api/Controllers/AuthorsController.cs
Normal file
|
@ -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<AuthorsController> _logger;
|
||||||
|
private readonly ILibraryService _libraryService;
|
||||||
|
|
||||||
|
public AuthorsController(
|
||||||
|
ILogger<AuthorsController> logger,
|
||||||
|
ILibraryService libraryService
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
_libraryService = libraryService ?? throw new ArgumentNullException(nameof(libraryService));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<AuthorResponse> AddAuthorAsync(AuthorRequest authorRequest)
|
||||||
|
{
|
||||||
|
var author = await _libraryService.AddAuthorAsync(authorRequest.Map());
|
||||||
|
return author.Map();
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,13 +7,13 @@ namespace GitHubActionsDemo.Api.Controllers;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("[controller]")]
|
[Route("[controller]")]
|
||||||
public class LibraryController : ControllerBase
|
public class BooksController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly ILogger<LibraryController> _logger;
|
private readonly ILogger<BooksController> _logger;
|
||||||
private readonly ILibraryService _libraryService;
|
private readonly ILibraryService _libraryService;
|
||||||
|
|
||||||
public LibraryController(
|
public BooksController(
|
||||||
ILogger<LibraryController> logger,
|
ILogger<BooksController> logger,
|
||||||
ILibraryService libraryService
|
ILibraryService libraryService
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
@ -21,31 +21,24 @@ public class LibraryController : ControllerBase
|
||||||
_libraryService = libraryService ?? throw new ArgumentNullException(nameof(libraryService));
|
_libraryService = libraryService ?? throw new ArgumentNullException(nameof(libraryService));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet(Name = "GetBooks")]
|
[HttpGet]
|
||||||
public async Task<IEnumerable<BookResponse>> GetBooksAsync(int page = 0, int pageSize = 10)
|
public async Task<IEnumerable<BookResponse>> GetBooksAsync(int page = 0, int pageSize = 10)
|
||||||
{
|
{
|
||||||
var books = await _libraryService.GetBooksAsync(page, pageSize);
|
var books = await _libraryService.GetBooksAsync(page, pageSize);
|
||||||
return books.Select(x => x.Map());
|
return books.Select(x => x.Map());
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet(Name = "GetBook")]
|
[HttpGet("{bookId}")]
|
||||||
public async Task<BookResponse> GetBookAsync(int bookId)
|
public async Task<BookResponse> GetBookAsync(int bookId)
|
||||||
{
|
{
|
||||||
var book = await _libraryService.GetBookAsync(bookId);
|
var book = await _libraryService.GetBookAsync(bookId);
|
||||||
return book.Map();
|
return book.Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost(Name = "AddBook")]
|
[HttpPost]
|
||||||
public async Task<BookResponse> AddBookAsync(BookRequest bookRequest)
|
public async Task<BookResponse> AddBookAsync(BookRequest bookRequest)
|
||||||
{
|
{
|
||||||
var book = await _libraryService.AddBookAsync(bookRequest.Map());
|
var book = await _libraryService.AddBookAsync(bookRequest.Map());
|
||||||
return book.Map();
|
return book.Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost(Name = "AddAuthor")]
|
|
||||||
public async Task<AuthorResponse> AddAuthorAsync(AuthorRequest authorRequest)
|
|
||||||
{
|
|
||||||
var author = await _libraryService.AddAuthorAsync(authorRequest.Map());
|
|
||||||
return author.Map();
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"DbSettings": {
|
"DbSettings": {
|
||||||
"ConnectionString": "Server=localhost; Database=library; Uid=root; Pwd=libraryDbPassword;",
|
"ConnectionString": "Server=localhost:3306; Database=library; Uid=root; Pwd=libraryDbPassword;",
|
||||||
"Database": "library"
|
"Database": "library"
|
||||||
},
|
},
|
||||||
"Logging": {
|
"Logging": {
|
||||||
|
|
|
@ -44,13 +44,13 @@ public class DbContext : IDbContext
|
||||||
async Task _initAuthors()
|
async Task _initAuthors()
|
||||||
{
|
{
|
||||||
var sql = """
|
var sql = """
|
||||||
CREATE TABLE IF NOT EXISTS Authors (
|
CREATE TABLE IF NOT EXISTS authors (
|
||||||
AuthorId INT NOT NULL AUTO_INCREMENT,
|
author_id INT NOT NULL AUTO_INCREMENT,
|
||||||
FirstName VARCHAR(255) NOT NULL,
|
first_name VARCHAR(255) NOT NULL,
|
||||||
LastName VARCHAR(255) NOT NULL,
|
last_name VARCHAR(255) NOT NULL,
|
||||||
DateCreated DATETIME NOT NULL,
|
date_created DATETIME NOT NULL,
|
||||||
DateModified DATETIME NOT NULL,
|
date_modified DATETIME NOT NULL,
|
||||||
PRIMARY KEY (AuthorId)
|
PRIMARY KEY (author_id)
|
||||||
);
|
);
|
||||||
""";
|
""";
|
||||||
await connection.ExecuteAsync(sql);
|
await connection.ExecuteAsync(sql);
|
||||||
|
@ -59,16 +59,16 @@ public class DbContext : IDbContext
|
||||||
async Task _initBooks()
|
async Task _initBooks()
|
||||||
{
|
{
|
||||||
var sql = """
|
var sql = """
|
||||||
CREATE TABLE IF NOT EXISTS Books(
|
CREATE TABLE IF NOT EXISTS books(
|
||||||
BookId INT NOT NULL AUTO_INCREMENT,
|
book_id INT NOT NULL AUTO_INCREMENT,
|
||||||
Title VARCHAR(255),
|
title VARCHAR(255),
|
||||||
AuthorId INT NOT NULL,
|
author_id INT NOT NULL,
|
||||||
Isbn VARCHAR(13) NOT NULL,
|
isbn VARCHAR(13) NOT NULL,
|
||||||
DatePublished DATETIME NOT NULL,
|
date_published DATETIME NOT NULL,
|
||||||
DateCreated DATETIME NOT NULL,
|
date_created DATETIME NOT NULL,
|
||||||
DateModified DATETIME NOT NULL,
|
date_modified DATETIME NOT NULL,
|
||||||
PRIMARY KEY(BookId),
|
PRIMARY KEY(book_id),
|
||||||
FOREIGN KEY(AuthorId) REFERENCES Authors(AuthorId)
|
FOREIGN KEY(author_id) REFERENCES authors(author_id)
|
||||||
);
|
);
|
||||||
""";
|
""";
|
||||||
await connection.ExecuteAsync(sql);
|
await connection.ExecuteAsync(sql);
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Dapper" Version="2.0.143" />
|
<PackageReference Include="Dapper" Version="2.0.143" />
|
||||||
|
<PackageReference Include="Dapper.FluentMap" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.5" />
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.5" />
|
||||||
<PackageReference Include="MySql.Data" Version="8.0.33" />
|
<PackageReference Include="MySql.Data" Version="8.0.33" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Dapper.FluentMap;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
@ -16,5 +17,11 @@ public static class InfrastructureExtensions
|
||||||
using var scope = app.Services.CreateScope();
|
using var scope = app.Services.CreateScope();
|
||||||
var context = scope.ServiceProvider.GetRequiredService<IDbContext>();
|
var context = scope.ServiceProvider.GetRequiredService<IDbContext>();
|
||||||
await context.Init();
|
await context.Init();
|
||||||
|
|
||||||
|
FluentMapper.Initialize(config =>
|
||||||
|
{
|
||||||
|
config.AddMap(new AuthorDbMap());
|
||||||
|
config.AddMap(new BookDbMap());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
using GitHubActionsDemo.Persistance.Models;
|
using System;
|
||||||
|
using Dapper;
|
||||||
|
using GitHubActionsDemo.Persistance.Models;
|
||||||
|
|
||||||
namespace GitHubActionsDemo.Persistance;
|
namespace GitHubActionsDemo.Persistance;
|
||||||
|
|
||||||
public class LibraryRespository : ILibraryRespository
|
public class LibraryRespository : ILibraryRespository
|
||||||
{
|
{
|
||||||
public LibraryRespository()
|
private readonly IDbContext _dbContext;
|
||||||
|
public LibraryRespository(IDbContext dbContext)
|
||||||
{
|
{
|
||||||
|
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<AuthorDb> AddAuthorAsync(NewAuthorDb author)
|
public async Task<AuthorDb> AddAuthorAsync(NewAuthorDb author)
|
||||||
|
@ -26,11 +29,64 @@ public class LibraryRespository : ILibraryRespository
|
||||||
|
|
||||||
public async Task<BookDb> GetBookAsync(int bookId)
|
public async Task<BookDb> 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<BookDb, AuthorDb, BookDb>(sql, (book, author) =>
|
||||||
|
{
|
||||||
|
book.Author = author;
|
||||||
|
return book;
|
||||||
|
}, param, splitOn: nameof(AuthorDb.AuthorId));
|
||||||
|
|
||||||
|
return books?.FirstOrDefault();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<BookDb>> GetBooksAsync(int page, int pageSize)
|
public async Task<IEnumerable<BookDb>> 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<BookDb, AuthorDb, BookDb>(sql, (book, author) =>
|
||||||
|
{
|
||||||
|
book.Author = author;
|
||||||
|
return book;
|
||||||
|
}, splitOn: nameof(AuthorDb.AuthorId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
11
src/GitHubActionsDemo.Persistance/Mappings/AuthorDbMap.cs
Normal file
11
src/GitHubActionsDemo.Persistance/Mappings/AuthorDbMap.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
using Dapper.FluentMap.Mapping;
|
||||||
|
using GitHubActionsDemo.Persistance.Models;
|
||||||
|
|
||||||
|
internal class AuthorDbMap : EntityMap<AuthorDb>
|
||||||
|
{
|
||||||
|
internal AuthorDbMap()
|
||||||
|
{
|
||||||
|
Map(u => u.DateCreated).ToColumn($"Author{nameof(AuthorDb.DateCreated)}");
|
||||||
|
Map(u => u.DateModified).ToColumn($"Author{nameof(AuthorDb.DateModified)}");
|
||||||
|
}
|
||||||
|
}
|
11
src/GitHubActionsDemo.Persistance/Mappings/BookDbMap.cs
Normal file
11
src/GitHubActionsDemo.Persistance/Mappings/BookDbMap.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
using Dapper.FluentMap.Mapping;
|
||||||
|
using GitHubActionsDemo.Persistance.Models;
|
||||||
|
|
||||||
|
internal class BookDbMap : EntityMap<BookDb>
|
||||||
|
{
|
||||||
|
internal BookDbMap()
|
||||||
|
{
|
||||||
|
Map(u => u.DateCreated).ToColumn($"Book{nameof(BookDb.DateCreated)}");
|
||||||
|
Map(u => u.DateModified).ToColumn($"Book{nameof(BookDb.DateModified)}");
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,24 +2,9 @@ namespace GitHubActionsDemo.Persistance.Models;
|
||||||
|
|
||||||
public class AuthorDb
|
public class AuthorDb
|
||||||
{
|
{
|
||||||
public AuthorDb(
|
public int AuthorId { get; set; }
|
||||||
int authorId,
|
public string FirstName { get; set; }
|
||||||
string firstName,
|
public string LastName { get; set; }
|
||||||
string lastName,
|
public DateTime DateCreated { get; set; }
|
||||||
DateTime dateCreated,
|
public DateTime DateModified { get; set; }
|
||||||
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; }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,30 +2,11 @@ namespace GitHubActionsDemo.Persistance.Models;
|
||||||
|
|
||||||
public class BookDb
|
public class BookDb
|
||||||
{
|
{
|
||||||
public BookDb(
|
public int BookId { get; set; }
|
||||||
int bookId,
|
public string Title { get; set; }
|
||||||
string title,
|
public AuthorDb Author { get; set; }
|
||||||
AuthorDb author,
|
public string Isbn { get; set; }
|
||||||
string isbn,
|
public DateOnly DatePublished { get; set; }
|
||||||
DateOnly datePublished,
|
public DateTime DateCreated { get; set; }
|
||||||
DateTime dateCreated,
|
public DateTime DateModified { get; set; }
|
||||||
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; }
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue