Users and Finance Ops work wip

This commit is contained in:
CodeLiturgy 2022-08-19 04:18:50 +01:00
parent 3bf0d27946
commit 4d56898bce
21 changed files with 302 additions and 194 deletions

View File

@ -7,119 +7,139 @@ using Microsoft.AspNetCore.Mvc;
namespace BlueWest.WebApi.Controllers
{
/// <summary>
/// Controller responsible to get country data
/// </summary>
[ApiController]
[Route("[controller]")]
public class CountryController : ControllerBase
{
private readonly CountriesDbContext _dbContext;
/// <summary>
/// Controller responsible for handling country data in the Country table
/// Controller responsible to get country data
/// </summary>
/// <param name="dbContext"></param>
public CountryController(CountriesDbContext dbContext)
[ApiController]
[Route("[controller]")]
public class CountryController : ControllerBase
{
_dbContext = dbContext;
}
private readonly CountriesDbContext _dbContext;
/// <summary>
/// Add Country
/// </summary>
/// <param name="countryToCreate">The country data to create</param>
/// <returns>The newly created country</returns>
/// /// <summary>
/// Creates a Country.
/// </summary>
/// <remarks>
/// Sample request:
///
/// POST /Countries
/// {
/// "code": 1,
/// "stateName": "United States of America",
/// "tld": "us"
/// }
///
/// </remarks>
/// <response code="201">Returns the newly created country</response>
[ProducesResponseType(StatusCodes.Status201Created)]
[HttpPost]
public ActionResult AddCountry(CountryCreate countryToCreate)
{
var newCountry = countryToCreate.ToCountry();
_dbContext.Countries.Add(newCountry);
_dbContext.SaveChanges();
return CreatedAtRoute(nameof(GetCountryById), new {countryId = newCountry.Code}, newCountry);
}
/// <summary>
/// Updates a Country
/// </summary>
/// <param name="countryCode">The country ISO 3166 code</param>
/// <param name="countryToUpdate">Country payload data</param>
/// <returns></returns>
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[HttpPut("{countryCode}")]
public ActionResult UpdateCountry(int countryCode, CountryUpdate countryToUpdate)
{
var country = _dbContext.Countries.FirstOrDefault(x => x.Code == countryCode);
if (country != null)
/// <summary>
/// Controller responsible for handling country data in the Country table
/// </summary>
/// <param name="dbContext"></param>
public CountryController(CountriesDbContext dbContext)
{
var updatedCountry = new Country(countryToUpdate, countryCode, null);
_dbContext.Countries.Update(updatedCountry);
return Ok(updatedCountry);
_dbContext = dbContext;
}
return new NotFoundResult();
}
/// <summary>
/// Get countries
/// </summary>
/// <returns></returns>
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[HttpGet]
public ActionResult GetCountries()
{
var array = _dbContext.Countries;
if (array != null)
/// <summary>
/// Add Country
/// </summary>
/// <param name="countryToCreate">The country data to create</param>
/// <returns>The newly created country</returns>
/// /// <summary>
/// Creates a Country.
/// </summary>
/// <remarks>
/// Sample request:
///
/// POST /Countries
/// {
/// "code": 1,
/// "stateName": "United States of America",
/// "tld": "us"
/// }
///
/// </remarks>
/// <response code="201">Returns the newly created country</response>
[ProducesResponseType(StatusCodes.Status201Created)]
[HttpPost]
public ActionResult AddCountry(CountryCreate countryToCreate)
{
return Ok(array.ToArray());
var newCountry = countryToCreate.ToCountry();
_dbContext.Countries.Add(newCountry);
_dbContext.SaveChanges();
return CreatedAtRoute(nameof(GetCountryById), new {countryId = newCountry.Code}, newCountry);
}
return new NotFoundResult();
}
/// <summary>
/// Get Country by Id
/// </summary>
/// <param name="countryId">ISO 3166-1 country numeric code</param>
/// <returns></returns>
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[HttpGet("{countryId}", Name = nameof(GetCountryById))]
public ActionResult GetCountryById(int countryId)
{
var array = _dbContext.Countries.FirstOrDefault(x => x.Code == countryId);
if (array != null)
/// <summary>
/// Updates a Country
/// </summary>
/// <param name="countryCode">The country ISO 3166 code</param>
/// <param name="countryToUpdate">Country payload data</param>
/// <returns></returns>
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[HttpPut("{countryCode}")]
public ActionResult UpdateCountry(int countryCode, CountryUpdate countryToUpdate)
{
var country = _dbContext.Countries.FirstOrDefault(x => x.Code == countryCode);
if (country != null)
{
var updatedCountry = new Country(countryToUpdate, countryCode, null, null);
_dbContext.Countries.Update(updatedCountry);
return Ok(updatedCountry);
}
return new NotFoundResult();
}
/// <summary>
/// Get countries
/// </summary>
/// <returns></returns>
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[HttpGet]
public ActionResult GetCountries()
{
var array = _dbContext.Countries;
if (array != null)
{
return Ok(array.ToArray());
}
return new NotFoundResult();
}
/// <summary>
/// Get Country by Id
/// </summary>
/// <param name="countryId">ISO 3166-1 country numeric code</param>
/// <returns></returns>
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[HttpGet("{countryId}", Name = nameof(GetCountryById))]
public ActionResult GetCountryById(int countryId)
{
var array = _dbContext.Countries.FirstOrDefault(x => x.Code == countryId);
if (array != null)
{
return Ok(array);
}
return new NotFoundResult();
}
/// <summary>
/// Get currencies of a country
/// </summary>
/// <param name="countryId"></param>
/// <returns></returns>
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[HttpGet("{countryId}/currencies")]
public ActionResult GetCountryCurrencies(int countryId)
{
var country = _dbContext.Countries.FirstOrDefault(d => d.Code == countryId);
if (country == null) return new NotFoundResult();
var array = _dbContext
.Countries
.Where(data => data.Code == countryId)
.SelectMany(o => o.Currencies)
.ToArray();
return Ok(array);
}
return new NotFoundResult();
}
}
}
}

View File

@ -16,18 +16,25 @@ namespace BlueWest.WebApi.Controllers
private readonly UserDbContext _dbContext;
/// <summary>
/// Controller responsible to handle user data
/// </summary>
/// <param name="dbContext"></param>
public UserController(UserDbContext dbContext)
{
_dbContext = dbContext;
}
/// <summary>
/// Gets all the users in the user table12312
/// </summary>
/// <returns></returns>
[ProducesResponseType(StatusCodes.Status200OK)]
[HttpGet]
public ActionResult Get()
{
var users = _dbContext.Users.ToImmutableArray();
var users = _dbContext.Users.ToArray();
return Ok(users);
}
@ -41,7 +48,7 @@ namespace BlueWest.WebApi.Controllers
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[HttpGet("{userId}", Name = nameof(GetUserById))]
public ActionResult GetUserById(TimeSpan userId)
public ActionResult GetUserById(int userId)
{
var user = _dbContext.Users.FirstOrDefault(x => x.Id == userId);
@ -54,29 +61,45 @@ namespace BlueWest.WebApi.Controllers
}
/// <summary>
/// Adds a user to the database
/// </summary>
/// <param name="userCreate">User to add </param>
/// <returns></returns>
[ProducesResponseType(StatusCodes.Status201Created)]
[HttpPost]
public ActionResult AddUser(UserUpdateDto userUpdateDto)
public ActionResult AddUser(UserCreate userCreate)
{
var user = new User(userUpdateDto, DateTime.Now.TimeOfDay, new List<FinanceOp>());
var user = new User(userCreate, 0, new List<FinanceOp>(), null);
_dbContext.Users.Add(user);
_dbContext.SaveChanges();
return CreatedAtRoute(nameof(GetUserById), new {userId = user.Id}, user);
}
/// <summary>
/// Updates user data
/// </summary>
/// <param name="userId">User id</param>
/// <param name="userCreate"></param>
/// <returns></returns>
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[HttpPut("{userId}")]
public ActionResult UpdateUser(int userId, UserUpdateDto userUpdate)
[HttpPut($"{{userId:int}}")]
public ActionResult UpdateUser(int userId, UserCreate userCreate)
{
return new NotFoundResult();
}
/// <summary>
/// Deletes a user from the database
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[HttpDelete("{id}")]
public ActionResult DeleteUser(TimeSpan id)
[HttpDelete("{id:int}")]
public ActionResult DeleteUser(int id)
{
var user = _dbContext.Users.FirstOrDefault(u => u.Id == id);
if (user == null)

View File

@ -8,9 +8,9 @@ FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["BlueWest.Api/BlueWest.Api.csproj", "BlueWest.Api/"]
RUN dotnet restore "BlueWest.Api/BlueWest.Api.csproj"
COPY [".", "."]
RUN dotnet restore "BlueWest.Api/BlueWest.Api.csproj"
RUN dotnet build "BlueWest.Api/BlueWest.Api.csproj" -c Release -o /app/build
FROM build AS publish

View File

@ -13,8 +13,9 @@ public class CountriesDbContext : DbContext
public DbSet<Country> Countries { get; set; }
public DbSet<Currency> Currencies { get; set; }
public DbSet<User> Users { get; set; }
public IConfiguration Configuration;
@ -44,23 +45,6 @@ public class CountriesDbContext : DbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Country>(builder =>
{
builder.HasKey(x => x.Code);
});
modelBuilder.Entity<Currency>(builder =>
{
builder.HasKey(x => x.Num);
});
modelBuilder.Entity<Currency>()
.HasMany(ub => ub.Countries)
.WithMany(au => au.Currencies);
modelBuilder.Entity<Country>()
.HasMany(ub => ub.Currencies)
.WithMany(au => au.Countries);
modelBuilder.ProjectDatabaseModel();
}
}

View File

@ -10,11 +10,21 @@ namespace BlueWest.WebApi.MySQL
{
public DbSet<FinanceOp> Transactions { get; set; }
public DbSet<FinanceOpType> TransactionTypes { get; set; }
public FinanceDbContext(DbContextOptions<UserDbContext> options) : base(options)
/// <summary>
/// Finance transactions context
/// </summary>
/// <param name="options"></param>
public FinanceDbContext(DbContextOptions<FinanceDbContext> options) : base(options)
{
Database.EnsureCreated();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ProjectDatabaseModel();
}
}
}

View File

@ -0,0 +1,62 @@
using BlueWest.Data;
using Microsoft.EntityFrameworkCore;
namespace BlueWest.WebApi.MySQL
{
public static class ModelBuilderExtensions
{
public static void ProjectDatabaseModel(this ModelBuilder modelBuilder)
{
// Keys
CountryCurrencyModel(modelBuilder);
UserModel(modelBuilder);
}
private static void CountryCurrencyModel(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Country>(builder =>
{
builder.HasKey(x => x.Code);
});
modelBuilder.Entity<Currency>(builder =>
{
builder.HasKey(x => x.Num);
});
// Relationships
modelBuilder.Entity<Currency>()
.HasMany(ub => ub.Countries)
.WithMany(au => au.Currencies);
modelBuilder.Entity<Country>()
.HasMany(ub => ub.Currencies)
.WithMany(au => au.Countries);
}
private static void UserModel(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Country>(builder =>
builder
.HasMany<User>()
.WithOne(user => user.Country));
modelBuilder
.Entity<User>(builder => builder
.HasOne<Country>()
.WithMany(co => co.Users));
modelBuilder.Entity<FinanceOp>(builder =>
{
builder.HasOne<User>()
.WithMany(x => x.FinanceTransactions)
.HasForeignKey(x => x.UserId);
});
}
}
}

View File

@ -6,7 +6,7 @@ using Microsoft.Extensions.Configuration;
namespace BlueWest.WebApi.MySQL
{
public sealed class UserDbContext : DbContext
public class UserDbContext : DbContext
{
public DbSet<User> Users { get; set; }
@ -21,7 +21,6 @@ namespace BlueWest.WebApi.MySQL
public UserDbContext(DbContextOptions<UserDbContext> options) : base(options)
{
Database.EnsureCreated();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
@ -35,21 +34,7 @@ namespace BlueWest.WebApi.MySQL
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>(builder =>
{
builder.HasKey(x => x.Id);
});
modelBuilder.Entity<FinanceOp>(builder =>
{
builder.HasOne<User>()
.WithMany(x => x.FinanceTransactions)
.HasForeignKey(x => x.UserId);
});
modelBuilder.ProjectDatabaseModel();
}
}
}

View File

@ -85,18 +85,13 @@ namespace BlueWest.WebApi
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
options.IncludeXmlComments(xmlPath);
});
services.AddDbContextPool<UserDbContext>(options =>
options.GetSqlSettings(Configuration))
.AddDbContextPool<CountriesDbContext>(options =>
options.GetSqlSettings(Configuration))
.AddDbContextPool<FinanceDbContext>(options =>
options.GetSqlSettings(Configuration))
.AddDbContextPool<CountriesDbContext>(options =>
options.GetSqlSettings(Configuration));
services
.AddDbContextPool<CountriesDbContext>(options => options.GetSqlSettings(Configuration))
.AddDbContextPool<UserDbContext>(options => options.GetSqlSettings(Configuration))
.AddDbContextPool<FinanceDbContext>(options => options.GetSqlSettings(Configuration))
;
// services.AddGrpc();
}

View File

@ -1,6 +1,7 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace BlueWest.WebApi;
@ -9,6 +10,11 @@ public static class StartupExtensions
public static void GetSqlSettings(this DbContextOptionsBuilder optionsBuilder, IConfiguration configuration)
{
optionsBuilder.UseMySql(configuration.GetConnectionString("LocalMySQL"),
new MySqlServerVersion(new Version(8, 0, 11)));
new MySqlServerVersion(new Version(8, 0, 11)))
// The following three options help with debugging, but should
// be changed or removed for production.
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
}

View File

@ -14,14 +14,23 @@ namespace BlueWest.Data
public partial class Country
{
// ISO 3166-1 numeric code
public int Code { get; set; } // Primary key
/// <summary>
/// ISO 3166-1 numeric code
/// Primary key.
/// </summary>
public int Code { get; set; }
/// <summary>
/// ISO 3166-1 State Name
/// </summary>
public string StateName { get; set; }
[MaxLength(2)] public string Alpha2Code { get; set; }
public string TLD { get; set; }
public List<Currency> Currencies { get; set; }
public List<User> Users { get; set; }
[JsonConstructor]
public Country(int code, string stateName, string tld, List<Currency> currencies)

View File

@ -27,8 +27,8 @@ namespace BlueWest.Data
{
currencies.Add(new Currency(currencyCreate, null));
}
return new Country(this, currencies);
return new Country(this, currencies, null);
}
}

View File

@ -18,7 +18,7 @@ namespace BlueWest.Data
foreach (var countryCreate in CountriesToCreate)
{
var newCountry = new Country(countryCreate, null);
var newCountry = new Country(countryCreate, null, null);
countries.Add(newCountry);
}

View File

@ -8,7 +8,7 @@ namespace BlueWest.Data
public partial class FinanceOp
{
public TimeSpan UserId { get; set; }
public int UserId { get; set; }
[Key] public TimeSpan CreationDate { get; set; }
@ -21,7 +21,7 @@ namespace BlueWest.Data
public FinanceOp() { }
public FinanceOp(TimeSpan creationDate, TimeSpan userId,
public FinanceOp(TimeSpan creationDate, int userId,
Currency currency, FinanceOpType financeOpType)
{
CreationDate = creationDate;

View File

@ -9,7 +9,7 @@ namespace BlueWest.Data
public partial class FinanceOpCreate
{
public TimeSpan UserId { get; set; }
public int UserId { get; set; }
public Currency Currency { get; }
@ -17,7 +17,7 @@ namespace BlueWest.Data
public FinanceOpCreate(
TimeSpan userId,
int userId,
Currency currency ,
FinanceOpType financeOpType
)

View File

@ -9,7 +9,7 @@ namespace BlueWest.Data
public partial class FinanceTransactionReadDto
{
public TimeSpan UserId { get; set; }
public int UserId { get; set; }
public Currency Currency { get; }

View File

@ -1,12 +1,19 @@
using System.ComponentModel.DataAnnotations;
namespace BlueWest.Data;
public class FinanceOpType
namespace BlueWest.Data
{
[Key] public int Id { get; set; }
public partial class FinanceOpType
{
[Key] public int Id { get; set; }
public string Name;
public string Name;
private string Description;
public FinanceOpType()
{
}
}
}
private string Description;
}

View File

@ -1,22 +1,24 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using MapTo;
namespace BlueWest.Data
{
[MapFrom( typeof(UserUpdateDto))]
[MapFrom( typeof(UserCreate))]
/*
[UseUpdate]
*/
public partial class User
{
public TimeSpan Id { get; } = TimeSpan.Zero;
public int Id { get; set; }
public string Name { get; set; }
public List<FinanceOp> FinanceTransactions { get; }
public List<FinanceOp> FinanceTransactions { get; set; }
public Country Country { get; set; }
public User(TimeSpan id, string name)
public User(int id, string name)
{
Id = id;
Name = name;

View File

@ -4,8 +4,14 @@ namespace BlueWest.Data
{
[MapFrom(typeof(User))]
public partial class UserUpdateDto
public partial class UserCreate
{
public string Name { get; set; }
public UserCreate()
{
}
}
}

View File

@ -15,8 +15,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleExpressionEvaluator",
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "include", "include", "{A1606EEC-6AC5-4779-B140-F57089F5A05F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "data", "data", "{19577B27-7EDF-4DBA-83D5-E047467BDFEF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleExpressionEvaluator.Tests", "include\Math-Expression-Evaluator\SimpleExpressionEvaluator.Tests\SimpleExpressionEvaluator.Tests.csproj", "{08F4484E-5FD8-4590-A8D7-12FBE47120C8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlueWest.MapTo.Tests", "include\BlueWest.MapTo\test\BlueWest.MapTo.Tests\BlueWest.MapTo.Tests.csproj", "{5BE0A68C-B3ED-4FA1-B74B-3E857504899B}"
@ -27,6 +25,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docker", "docker", "{D7BF4A
BlueWest.Api\Dockerfile = BlueWest.Api\Dockerfile
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "README", "README", "{E9E3CEB0-D00C-46E3-B497-B4ED7B291190}"
ProjectSection(SolutionItems) = preProject
README.md = README.md
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -71,7 +74,6 @@ Global
GlobalSection(NestedProjects) = preSolution
{30637214-EDE9-4C2E-BFD6-E4B163FA308B} = {A1606EEC-6AC5-4779-B140-F57089F5A05F}
{72B37540-A12F-466E-A58F-7BA2B247CB74} = {A1606EEC-6AC5-4779-B140-F57089F5A05F}
{E518C62D-768C-4885-9C9D-FD5761605B54} = {19577B27-7EDF-4DBA-83D5-E047467BDFEF}
{08F4484E-5FD8-4590-A8D7-12FBE47120C8} = {A1606EEC-6AC5-4779-B140-F57089F5A05F}
{5BE0A68C-B3ED-4FA1-B74B-3E857504899B} = {A1606EEC-6AC5-4779-B140-F57089F5A05F}
EndGlobalSection

View File

@ -16,4 +16,4 @@ Copy the generated hash, and run:
Run the following command to instance a MySQL database and the API:
`docker-compose up --build --force-recreate`
`docker-compose up --build -d`

View File

@ -14,9 +14,7 @@ services:
container_name: BW1_API
db:
container_name: BW1_DB_MYSQL
image: mysql/mysql-server:8.0
tty: true
environment:
MYSQL_ROOT_HOST: db
MYSQL_USER_HOST: db
@ -28,7 +26,6 @@ services:
- ./docker-entrypoint-initdb.d/:/docker-entrypoint-initdb.d/
phpmyadmin:
container_name: BW_PHPMYADMIN
tty: true
image: phpmyadmin/phpmyadmin
ports:
- 80:80