diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6d0c20f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +**/bin/ +**/obj/ +**/global.json +**/Dockerfile* +**/.dockerignore* +**/*.user +**/.vs/ + diff --git a/.gitignore b/.gitignore index b14f7ce..f3311e9 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,10 @@ /LoggerService/bin /Repository/obj /Repository/bin +/.vs/AccountOwnerServer/v16 +/AccountOwnerServer/*.user +/Tests/obj +/Tests/bin +/.vs/AccountOwnerServer/DesignTimeBuild/*.dtbcache +/Integration/obj +/Integration/bin diff --git a/AccountOwnerServer.sln b/AccountOwnerServer.sln index 7620715..0515e7d 100644 --- a/AccountOwnerServer.sln +++ b/AccountOwnerServer.sln @@ -1,17 +1,21 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.3 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29424.173 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AccountOwnerServer", "AccountOwnerServer\AccountOwnerServer.csproj", "{73FB08B8-4033-4116-B3AC-EC8253B024D4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AccountOwnerServer", "AccountOwnerServer\AccountOwnerServer.csproj", "{D9142A6E-9687-4BA4-9CF9-A9AFF006E3DF}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Contracts", "Contracts\Contracts.csproj", "{52725034-B791-4DF1-A227-02EDB5BF78E0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Contracts", "Contracts\Contracts.csproj", "{E75B1842-10BD-4BF3-A96A-830220E57332}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LoggerService", "LoggerService\LoggerService.csproj", "{A190AF01-84DC-40A8-BEF3-C015825C8B30}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LoggerService", "LoggerService\LoggerService.csproj", "{DA66E4D3-1CDD-407D-9900-FC27F5AF8F06}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Entities", "Entities\Entities.csproj", "{8CD97B3F-C616-469C-80A2-A1B366611487}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Entities", "Entities\Entities.csproj", "{3466888D-D523-4180-B3E4-69953F45F520}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Repository", "Repository\Repository.csproj", "{CC0B1C81-CB8A-412C-BD48-BCBC3F1A1CD0}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Repository", "Repository\Repository.csproj", "{7EB45A88-65DE-4A64-852A-3B6921B32D2E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{F45A20D5-FE64-49EB-AB95-2C90A0B76164}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration", "Integration\Integration.csproj", "{F0483E07-28FD-4FE9-BBA6-BA456294927F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,31 +23,39 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {73FB08B8-4033-4116-B3AC-EC8253B024D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {73FB08B8-4033-4116-B3AC-EC8253B024D4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {73FB08B8-4033-4116-B3AC-EC8253B024D4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {73FB08B8-4033-4116-B3AC-EC8253B024D4}.Release|Any CPU.Build.0 = Release|Any CPU - {52725034-B791-4DF1-A227-02EDB5BF78E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {52725034-B791-4DF1-A227-02EDB5BF78E0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {52725034-B791-4DF1-A227-02EDB5BF78E0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {52725034-B791-4DF1-A227-02EDB5BF78E0}.Release|Any CPU.Build.0 = Release|Any CPU - {A190AF01-84DC-40A8-BEF3-C015825C8B30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A190AF01-84DC-40A8-BEF3-C015825C8B30}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A190AF01-84DC-40A8-BEF3-C015825C8B30}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A190AF01-84DC-40A8-BEF3-C015825C8B30}.Release|Any CPU.Build.0 = Release|Any CPU - {8CD97B3F-C616-469C-80A2-A1B366611487}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8CD97B3F-C616-469C-80A2-A1B366611487}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8CD97B3F-C616-469C-80A2-A1B366611487}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8CD97B3F-C616-469C-80A2-A1B366611487}.Release|Any CPU.Build.0 = Release|Any CPU - {CC0B1C81-CB8A-412C-BD48-BCBC3F1A1CD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CC0B1C81-CB8A-412C-BD48-BCBC3F1A1CD0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CC0B1C81-CB8A-412C-BD48-BCBC3F1A1CD0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CC0B1C81-CB8A-412C-BD48-BCBC3F1A1CD0}.Release|Any CPU.Build.0 = Release|Any CPU + {D9142A6E-9687-4BA4-9CF9-A9AFF006E3DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D9142A6E-9687-4BA4-9CF9-A9AFF006E3DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D9142A6E-9687-4BA4-9CF9-A9AFF006E3DF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D9142A6E-9687-4BA4-9CF9-A9AFF006E3DF}.Release|Any CPU.Build.0 = Release|Any CPU + {E75B1842-10BD-4BF3-A96A-830220E57332}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E75B1842-10BD-4BF3-A96A-830220E57332}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E75B1842-10BD-4BF3-A96A-830220E57332}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E75B1842-10BD-4BF3-A96A-830220E57332}.Release|Any CPU.Build.0 = Release|Any CPU + {DA66E4D3-1CDD-407D-9900-FC27F5AF8F06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DA66E4D3-1CDD-407D-9900-FC27F5AF8F06}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DA66E4D3-1CDD-407D-9900-FC27F5AF8F06}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DA66E4D3-1CDD-407D-9900-FC27F5AF8F06}.Release|Any CPU.Build.0 = Release|Any CPU + {3466888D-D523-4180-B3E4-69953F45F520}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3466888D-D523-4180-B3E4-69953F45F520}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3466888D-D523-4180-B3E4-69953F45F520}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3466888D-D523-4180-B3E4-69953F45F520}.Release|Any CPU.Build.0 = Release|Any CPU + {7EB45A88-65DE-4A64-852A-3B6921B32D2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7EB45A88-65DE-4A64-852A-3B6921B32D2E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7EB45A88-65DE-4A64-852A-3B6921B32D2E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7EB45A88-65DE-4A64-852A-3B6921B32D2E}.Release|Any CPU.Build.0 = Release|Any CPU + {F45A20D5-FE64-49EB-AB95-2C90A0B76164}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F45A20D5-FE64-49EB-AB95-2C90A0B76164}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F45A20D5-FE64-49EB-AB95-2C90A0B76164}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F45A20D5-FE64-49EB-AB95-2C90A0B76164}.Release|Any CPU.Build.0 = Release|Any CPU + {F0483E07-28FD-4FE9-BBA6-BA456294927F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0483E07-28FD-4FE9-BBA6-BA456294927F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0483E07-28FD-4FE9-BBA6-BA456294927F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0483E07-28FD-4FE9-BBA6-BA456294927F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {86DE07EE-BE1C-4AAB-ADAA-224170CF42B0} + SolutionGuid = {12A94BCC-D511-439A-9D3B-C1253CB1C879} EndGlobalSection EndGlobal diff --git a/AccountOwnerServer/AccountOwnerServer.csproj b/AccountOwnerServer/AccountOwnerServer.csproj index beffa6b..3d26b60 100644 --- a/AccountOwnerServer/AccountOwnerServer.csproj +++ b/AccountOwnerServer/AccountOwnerServer.csproj @@ -1,25 +1,24 @@ - + - netcoreapp2.0 + netcoreapp3.1 - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + - - - - - - - - - - - diff --git a/AccountOwnerServer/Controllers/AccountController.cs b/AccountOwnerServer/Controllers/AccountController.cs new file mode 100644 index 0000000..d9dbd67 --- /dev/null +++ b/AccountOwnerServer/Controllers/AccountController.cs @@ -0,0 +1,78 @@ +using AutoMapper; +using Contracts; +using Entities.DataTransferObjects; +using Entities.Models; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; + +namespace AccountOwnerServer.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class AccountController : Controller + { + private ILoggerManager _logger; + private IRepositoryWrapper _repository; + private IMapper _mapper; + + public AccountController(ILoggerManager logger, IRepositoryWrapper repository, IMapper mapper) + { + _logger = logger; + _repository = repository; + _mapper = mapper; + } + + [HttpGet] + public IActionResult GetAllAccounts() + { + try + { + var accounts = _repository.Account.GetAllAccounts(); + + _logger.LogInfo($"Returned all accounts from database."); + + var accountDtos = _mapper.Map>(accounts); + return Ok(accountDtos); + } + catch (Exception ex) + { + _logger.LogError($"Something went wrong inside GetAllAccounts action: {ex}"); + return StatusCode(500, "Internal server error"); + } + } + + [HttpPost] + public IActionResult CreateAccount([FromBody]AccountForCreationDto account) + { + try + { + if (account == null) + { + _logger.LogError("Object sent from client is null."); + return BadRequest("Object is null"); + } + + if (!ModelState.IsValid) + { + _logger.LogError("Invalid object sent from client."); + return BadRequest("Invalid model object"); + } + + var accountEntity = _mapper.Map(account); + + _repository.Account.CreateAccount(accountEntity); + _repository.Save(); + + var createdAccount = _mapper.Map(accountEntity); + + return CreatedAtRoute("AccountById", new { id = createdAccount.Id }, createdAccount); + } + catch (Exception ex) + { + _logger.LogError($"Something went wrong inside CreateAccount action: {ex}"); + return StatusCode(500, "Internal server error"); + } + } + } +} diff --git a/AccountOwnerServer/Controllers/OwnerController.cs b/AccountOwnerServer/Controllers/OwnerController.cs index dc2850c..1578424 100644 --- a/AccountOwnerServer/Controllers/OwnerController.cs +++ b/AccountOwnerServer/Controllers/OwnerController.cs @@ -1,97 +1,105 @@ -using Contracts; -using Entities.Extensions; +using AutoMapper; +using Contracts; +using Entities.DataTransferObjects; using Entities.Models; using Microsoft.AspNetCore.Mvc; using System; +using System.Collections.Generic; +using System.Linq; namespace AccountOwnerServer.Controllers { - [Route("api/owner")] - public class OwnerController : Controller - { - private ILoggerManager _logger; + [Route("api/owner")] + [ApiController] + public class OwnerController : ControllerBase + { + private ILoggerManager _logger; private IRepositoryWrapper _repository; - - public OwnerController(ILoggerManager logger, IRepositoryWrapper repository) - { - _logger = logger; + private IMapper _mapper; + + public OwnerController(ILoggerManager logger, IRepositoryWrapper repository, IMapper mapper) + { + _logger = logger; _repository = repository; + _mapper = mapper; } - - [HttpGet] - public IActionResult GetAllOwners() - { - try - { - var owners = _repository.Owner.GetAllOwners(); - + + [HttpGet] + public IActionResult GetAllOwners() + { + try + { + var owners = _repository.Owner.GetAllOwners(); _logger.LogInfo($"Returned all owners from database."); - return Ok(owners); - } - catch (Exception ex) - { - _logger.LogError($"Something went wrong inside GetAllOwners action: {ex.Message}"); - return StatusCode(500, "Internal server error"); - } + var ownersResult = _mapper.Map>(owners); + return Ok(ownersResult); + } + catch (Exception ex) + { + _logger.LogError($"Something went wrong inside GetAllOwners action: {ex.Message}"); + return StatusCode(500, "Internal server error"); + } } - [HttpGet("{id}", Name = "OwnerById")] - public IActionResult GetOwnerById(Guid id) - { - try - { - var owner = _repository.Owner.GetOwnerById(id); - - if (owner.IsEmptyObject()) - { - _logger.LogError($"Owner with id: {id}, hasn't been found in db."); - return NotFound(); - } - else - { + [HttpGet("{id}", Name = "OwnerById")] + public IActionResult GetOwnerById(Guid id) + { + try + { + var owner = _repository.Owner.GetOwnerById(id); + if (owner == null) + { + _logger.LogError($"Owner with id: {id}, hasn't been found in db."); + return NotFound(); + } + else + { _logger.LogInfo($"Returned owner with id: {id}"); - return Ok(owner); - } - } - catch (Exception ex) - { - _logger.LogError($"Something went wrong inside GetOwnerById action: {ex.Message}"); - return StatusCode(500, "Internal server error"); - } - } - [HttpGet("{id}/account")] - public IActionResult GetOwnerWithDetails(Guid id) - { - try - { - var owner = _repository.Owner.GetOwnerWithDetails(id); + var ownerResult = _mapper.Map(owner); + return Ok(ownerResult); + } + } + catch (Exception ex) + { + _logger.LogError($"Something went wrong inside GetOwnerById action: {ex.Message}"); + return StatusCode(500, "Internal server error"); + } + } - if (owner.IsEmptyObject()) - { - _logger.LogError($"Owner with id: {id}, hasn't been found in db."); - return NotFound(); - } - else - { + [HttpGet("{id}/account")] + public IActionResult GetOwnerWithDetails(Guid id) + { + try + { + var owner = _repository.Owner.GetOwnerWithDetails(id); + if (owner == null) + { + _logger.LogError($"Owner with id: {id}, hasn't been found in db."); + return NotFound(); + } + else + { _logger.LogInfo($"Returned owner with details for id: {id}"); - return Ok(owner); - } - } - catch (Exception ex) - { - _logger.LogError($"Something went wrong inside GetOwnerWithDetails action: {ex.Message}"); - return StatusCode(500, "Internal server error"); + + var ownerResult = _mapper.Map(owner); + return Ok(ownerResult); + } + } + catch (Exception ex) + { + _logger.LogError($"Something went wrong inside GetOwnerWithDetails action: {ex.Message}"); + return StatusCode(500, "Internal server error"); } } [HttpPost] - public IActionResult CreateOwner([FromBody]Owner owner) + public IActionResult CreateOwner([FromBody]OwnerForCreationDto owner) { try { - if (owner.IsObjectNull()) + if (owner == null) { _logger.LogError("Owner object sent from client is null."); return BadRequest("Owner object is null"); @@ -103,9 +111,14 @@ public IActionResult CreateOwner([FromBody]Owner owner) return BadRequest("Invalid model object"); } - _repository.Owner.CreateOwner(owner); + var ownerEntity = _mapper.Map(owner); + + _repository.Owner.CreateOwner(ownerEntity); + _repository.Save(); - return CreatedAtRoute("OwnerById", new { id = owner.Id }, owner); + var createdOwner = _mapper.Map(ownerEntity); + + return CreatedAtRoute("OwnerById", new { id = createdOwner.Id }, createdOwner); } catch (Exception ex) { @@ -115,11 +128,11 @@ public IActionResult CreateOwner([FromBody]Owner owner) } [HttpPut("{id}")] - public IActionResult UpdateOwner(Guid id, [FromBody]Owner owner) + public IActionResult UpdateOwner(Guid id, [FromBody]OwnerForUpdateDto owner) { try { - if (owner.IsObjectNull()) + if (owner == null) { _logger.LogError("Owner object sent from client is null."); return BadRequest("Owner object is null"); @@ -131,14 +144,17 @@ public IActionResult UpdateOwner(Guid id, [FromBody]Owner owner) return BadRequest("Invalid model object"); } - var dbOwner = _repository.Owner.GetOwnerById(id); - if (dbOwner.IsEmptyObject()) + var ownerEntity = _repository.Owner.GetOwnerById(id); + if (ownerEntity == null) { _logger.LogError($"Owner with id: {id}, hasn't been found in db."); return NotFound(); } - _repository.Owner.UpdateOwner(dbOwner, owner); + _mapper.Map(owner, ownerEntity); + + _repository.Owner.UpdateOwner(ownerEntity); + _repository.Save(); return NoContent(); } @@ -155,13 +171,14 @@ public IActionResult DeleteOwner(Guid id) try { var owner = _repository.Owner.GetOwnerById(id); - if (owner.IsEmptyObject()) + if (owner == null) { _logger.LogError($"Owner with id: {id}, hasn't been found in db."); return NotFound(); } _repository.Owner.DeleteOwner(owner); + _repository.Save(); return NoContent(); } @@ -172,4 +189,4 @@ public IActionResult DeleteOwner(Guid id) } } } -} +} \ No newline at end of file diff --git a/AccountOwnerServer/Controllers/ValuesController.cs b/AccountOwnerServer/Controllers/ValuesController.cs deleted file mode 100644 index 71841b7..0000000 --- a/AccountOwnerServer/Controllers/ValuesController.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Contracts; -using Entities; -using Entities.Enumerations; -using Entities.Models; - -namespace AccountOwnerServer.Controllers -{ - [Route("api/[controller]")] - public class ValuesController : Controller - { - private ILoggerManager _logger; - private IRepositoryWrapper _repoWrapper; - - public ValuesController(ILoggerManager logger, IRepositoryWrapper repoWrapper) - { - _logger = logger; - _repoWrapper = repoWrapper; - } - // GET api/values - [HttpGet] - public IActionResult Get() - { - //var domesticAccounts = _repoWrapper.Account.FindByCondition(x => x.AccountType.Equals("Domestic")); - var owners = _repoWrapper.Owner.GetAllOwners(); - - _logger.LogInfo("Here is info message from our values controller."); - _logger.LogDebug("Here is debug message from our values controller."); - _logger.LogWarn("Here is warn message from our values controller."); - _logger.LogError("Here is error message from our values controller."); - - return Ok(owners); - } - } -} diff --git a/AccountOwnerServer/Extensions/ServiceExtensions.cs b/AccountOwnerServer/Extensions/ServiceExtensions.cs index 89ac3a5..7e31db1 100644 --- a/AccountOwnerServer/Extensions/ServiceExtensions.cs +++ b/AccountOwnerServer/Extensions/ServiceExtensions.cs @@ -6,10 +6,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Repository; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace AccountOwnerServer.Extensions { @@ -22,16 +18,15 @@ public static void ConfigureCors(this IServiceCollection services) options.AddPolicy("CorsPolicy", builder => builder.AllowAnyOrigin() .AllowAnyMethod() - .AllowAnyHeader() - .AllowCredentials()); + .AllowAnyHeader()); }); } public static void ConfigureIISIntegration(this IServiceCollection services) { - services.Configure(options => + services.Configure(options => { - + }); } @@ -40,15 +35,15 @@ public static void ConfigureLoggerService(this IServiceCollection services) services.AddSingleton(); } - public static void ConfigureMySqlContext(this IServiceCollection services, IConfiguration config) + public static void ConfigureMySqlContext(this IServiceCollection services, IConfiguration config) { - var connectionString = config["mysqlconnection:connectionString"]; + var connectionString = config["mysqlconnection:connectionString"]; services.AddDbContext(o => o.UseMySql(connectionString)); } - public static void ConfigureRepositoryWrapper(this IServiceCollection services) - { - services.AddScoped(); + public static void ConfigureRepositoryWrapper(this IServiceCollection services) + { + services.AddScoped(); } } } diff --git a/AccountOwnerServer/MappingProfile.cs b/AccountOwnerServer/MappingProfile.cs new file mode 100644 index 0000000..8bf056e --- /dev/null +++ b/AccountOwnerServer/MappingProfile.cs @@ -0,0 +1,27 @@ +using AutoMapper; +using Entities.DataTransferObjects; +using Entities.Models; +using System.Linq; + +namespace AccountOwnerServer +{ + public class MappingProfile : Profile + { + public MappingProfile() + { + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + } + } +} diff --git a/AccountOwnerServer/Program.cs b/AccountOwnerServer/Program.cs index 0be0d38..7131aeb 100644 --- a/AccountOwnerServer/Program.cs +++ b/AccountOwnerServer/Program.cs @@ -1,11 +1,10 @@ -using System; +using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading.Tasks; -using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace AccountOwnerServer @@ -14,12 +13,14 @@ public class Program { public static void Main(string[] args) { - BuildWebHost(args).Run(); + CreateHostBuilder(args).Build().Run(); } - public static IWebHost BuildWebHost(string[] args) => - WebHost.CreateDefaultBuilder(args) - .UseStartup() - .Build(); + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); } } diff --git a/AccountOwnerServer/Properties/launchSettings.json b/AccountOwnerServer/Properties/launchSettings.json index 348af13..4b82d54 100644 --- a/AccountOwnerServer/Properties/launchSettings.json +++ b/AccountOwnerServer/Properties/launchSettings.json @@ -1,27 +1,28 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:5000/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": false, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "AccountOwnerServer": { - "commandName": "Project", - "launchBrowser": false, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:5000/" - } - } -} +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://0.0.0.0:5000", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AccountOwnerServer": { + "commandName": "Project", + "launchBrowser": false, + "applicationUrl": "http://0.0.0.0:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/AccountOwnerServer/Startup.cs b/AccountOwnerServer/Startup.cs index 3a86fc4..c132c1b 100644 --- a/AccountOwnerServer/Startup.cs +++ b/AccountOwnerServer/Startup.cs @@ -1,75 +1,83 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using AccountOwnerServer.Extensions; +using AutoMapper; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.HttpOverrides; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using AccountOwnerServer.Extensions; -using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.Extensions.Hosting; +using Microsoft.OpenApi.Models; +using NLog; using System.IO; -using NLog.Extensions.Logging; -using Contracts; namespace AccountOwnerServer { - public class Startup - { - public Startup(IConfiguration configuration, ILoggerFactory loggerFactory) - { - loggerFactory.ConfigureNLog(String.Concat(Directory.GetCurrentDirectory(), "/nlog.config")); - Configuration = configuration; - } - - public IConfiguration Configuration { get; } + public class Startup + { + public Startup(IConfiguration configuration) + { + LogManager.LoadConfiguration(string.Concat(Directory.GetCurrentDirectory(), "/nlog.config")); + Configuration = configuration; + } - public void ConfigureServices(IServiceCollection services) - { - services.ConfigureCors(); + public IConfiguration Configuration { get; } - services.ConfigureIISIntegration(); + public void ConfigureServices(IServiceCollection services) + { + services.ConfigureCors(); + services.ConfigureIISIntegration(); + services.ConfigureLoggerService(); + services.ConfigureMySqlContext(Configuration); + services.ConfigureRepositoryWrapper(); + services.AddAutoMapper(typeof(Startup)); - services.ConfigureLoggerService(); + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo + { + Title = "AccountOwner API", + Version = "v1" + }); + }); - services.ConfigureMySqlContext(Configuration); + services.AddControllers(); + } - services.ConfigureRepositoryWrapper(); + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } - services.AddMvc(); - } + app.UseHttpsRedirection(); + app.UseStaticFiles(); - public void Configure(IApplicationBuilder app, IHostingEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } + app.UseCors("CorsPolicy"); - app.UseCors("CorsPolicy"); + app.UseForwardedHeaders(new ForwardedHeadersOptions + { + ForwardedHeaders = ForwardedHeaders.All + }); - app.UseForwardedHeaders(new ForwardedHeadersOptions - { - ForwardedHeaders = ForwardedHeaders.All - }); + app.UseRouting(); - app.Use(async (context, next) => - { - await next(); + app.UseAuthorization(); - if (context.Response.StatusCode == 404 - && !Path.HasExtension(context.Request.Path.Value)) - { - context.Request.Path = "/index.html"; - await next(); - } - }); + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); - app.UseStaticFiles(); + // Enable middleware to serve generated Swagger as a JSON endpoint. + app.UseSwagger(); - app.UseMvc(); - } - } + // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), + // specifying the Swagger JSON endpoint. + app.UseSwaggerUI(c => + { + c.SwaggerEndpoint("/swagger/v1/swagger.json", "AccountOwner API V1"); + }); + } + } } diff --git a/AccountOwnerServer/appsettings.Development.json b/AccountOwnerServer/appsettings.Development.json index fa8ce71..e203e94 100644 --- a/AccountOwnerServer/appsettings.Development.json +++ b/AccountOwnerServer/appsettings.Development.json @@ -1,6 +1,5 @@ -{ +{ "Logging": { - "IncludeScopes": false, "LogLevel": { "Default": "Debug", "System": "Information", diff --git a/AccountOwnerServer/appsettings.json b/AccountOwnerServer/appsettings.json index 934ec46..2c0f849 100644 --- a/AccountOwnerServer/appsettings.json +++ b/AccountOwnerServer/appsettings.json @@ -1,18 +1,13 @@ -{ - "Logging": { - "IncludeScopes": false, - "Debug": { - "LogLevel": { - "Default": "Warning" - } - }, - "Console": { - "LogLevel": { - "Default": "Warning" - } - } - }, - "mysqlconnection": { - "connectionString": "server=localhost;userid=dbuser;password=pass;database=accountowner;" - } +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "mysqlconnection": { + "connectionString": "server=db;port=3306;userid=dbuser;password=dbuserpassword;database=accountowner;" + }, + "AllowedHosts": "*" } diff --git a/AccountOwnerServer/nlog.config b/AccountOwnerServer/nlog.config index 91c2174..47744e0 100644 --- a/AccountOwnerServer/nlog.config +++ b/AccountOwnerServer/nlog.config @@ -5,10 +5,6 @@ internalLogLevel="Trace" internalLogFile="d:Projects\Blog-AccountOwner\Project\internal_logs\internallog.txt"> - - - - - netcoreapp2.0 + netcoreapp3.1 diff --git a/Contracts/IAccountRepository.cs b/Contracts/IAccountRepository.cs index 825a4fb..776ae31 100644 --- a/Contracts/IAccountRepository.cs +++ b/Contracts/IAccountRepository.cs @@ -1,8 +1,11 @@ using Entities.Models; +using System.Collections.Generic; namespace Contracts { - public interface IAccountRepository - { - } + public interface IAccountRepository : IRepositoryBase + { + IEnumerable GetAllAccounts(); + void CreateAccount(Account account); + } } diff --git a/Contracts/ILoggerManager.cs b/Contracts/ILoggerManager.cs index 7dcd872..ee1f081 100644 --- a/Contracts/ILoggerManager.cs +++ b/Contracts/ILoggerManager.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Contracts +namespace Contracts { public interface ILoggerManager { diff --git a/Contracts/IOwnerRepository.cs b/Contracts/IOwnerRepository.cs index da31e25..d82d2a8 100644 --- a/Contracts/IOwnerRepository.cs +++ b/Contracts/IOwnerRepository.cs @@ -1,17 +1,16 @@ -using Entities.ExtendedModels; -using Entities.Models; +using Entities.Models; using System; using System.Collections.Generic; namespace Contracts { - public interface IOwnerRepository + public interface IOwnerRepository : IRepositoryBase { IEnumerable GetAllOwners(); Owner GetOwnerById(Guid ownerId); - OwnerExtended GetOwnerWithDetails(Guid ownerId); + Owner GetOwnerWithDetails(Guid ownerId); void CreateOwner(Owner owner); - void UpdateOwner(Owner dbOwner, Owner owner); + void UpdateOwner(Owner owner); void DeleteOwner(Owner owner); } } diff --git a/Contracts/IRepositoryBase.cs b/Contracts/IRepositoryBase.cs index 336894d..7a2b84b 100644 --- a/Contracts/IRepositoryBase.cs +++ b/Contracts/IRepositoryBase.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; using System.Text; @@ -7,11 +8,10 @@ namespace Contracts { public interface IRepositoryBase { - IEnumerable FindAll(); - IEnumerable FindByCondition(Expression> expression); - void Create(T entity); - void Update(T entity); + IQueryable FindAll(); + IQueryable FindByCondition(Expression> expression); + void Create(T entity); + void Update(T entity); void Delete(T entity); - void Save(); } } diff --git a/Contracts/IRepositoryWrapper.cs b/Contracts/IRepositoryWrapper.cs index f4275a1..445d2f7 100644 --- a/Contracts/IRepositoryWrapper.cs +++ b/Contracts/IRepositoryWrapper.cs @@ -4,9 +4,10 @@ namespace Contracts { - public interface IRepositoryWrapper - { - IOwnerRepository Owner { get; } - IAccountRepository Account { get; } + public interface IRepositoryWrapper + { + IOwnerRepository Owner { get; } + IAccountRepository Account { get; } + void Save(); } } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..eed785a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +FROM mcr.microsoft.com/dotnet/core/sdk:3.1 as build-image + +WORKDIR /home/app + +COPY ./*.sln ./ +COPY ./*/*.csproj ./ +RUN for file in $(ls *.csproj); do mkdir -p ./${file%.*}/ && mv $file ./${file%.*}/; done + +RUN dotnet restore + +COPY . . + +RUN dotnet test --verbosity=normal ./Tests/Tests.csproj + +RUN dotnet publish ./AccountOwnerServer/AccountOwnerServer.csproj -o /publish/ + +FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 + +WORKDIR /publish + +COPY --from=build-image /publish . + +ENV ASPNETCORE_URLS="http://0.0.0.0:5000" +ENV TEAMCITY_PROJECT_NAME = ${TEAMCITY_PROJECT_NAME} + +ENTRYPOINT ["dotnet", "AccountOwnerServer.dll"] \ No newline at end of file diff --git a/Dockerfile.Integration b/Dockerfile.Integration new file mode 100644 index 0000000..33dd2ea --- /dev/null +++ b/Dockerfile.Integration @@ -0,0 +1,15 @@ +FROM mcr.microsoft.com/dotnet/core/sdk:3.1 + +WORKDIR /home/app + +COPY ./*.sln ./ +COPY ./*/*.csproj ./ +RUN for file in $(ls *.csproj); do mkdir -p ./${file%.*}/ && mv $file ./${file%.*}/; done + +RUN dotnet restore + +COPY . . + +WORKDIR /home/app/Integration/ + +ENTRYPOINT ["dotnet", "test", "--verbosity=normal"] \ No newline at end of file diff --git a/Entities/DataTransferObjects/AccountDto.cs b/Entities/DataTransferObjects/AccountDto.cs new file mode 100644 index 0000000..ef800f3 --- /dev/null +++ b/Entities/DataTransferObjects/AccountDto.cs @@ -0,0 +1,11 @@ +using System; + +namespace Entities.DataTransferObjects +{ + public class AccountDto + { + public Guid Id { get; set; } + public DateTime DateCreated { get; set; } + public string AccountType { get; set; } + } +} diff --git a/Entities/DataTransferObjects/AccountForCreationDto.cs b/Entities/DataTransferObjects/AccountForCreationDto.cs new file mode 100644 index 0000000..8fde44f --- /dev/null +++ b/Entities/DataTransferObjects/AccountForCreationDto.cs @@ -0,0 +1,10 @@ +using System; + +namespace Entities.DataTransferObjects +{ + public class AccountForCreationDto + { + public DateTime DateCreated { get; set; } + public string AccountType { get; set; } + } +} diff --git a/Entities/DataTransferObjects/AccountForUpdateDto.cs b/Entities/DataTransferObjects/AccountForUpdateDto.cs new file mode 100644 index 0000000..9d75fa3 --- /dev/null +++ b/Entities/DataTransferObjects/AccountForUpdateDto.cs @@ -0,0 +1,10 @@ +using System; + +namespace Entities.DataTransferObjects +{ + public class AccountForUpdateDto + { + public DateTime DateCreated { get; set; } + public string AccountType { get; set; } + } +} diff --git a/Entities/DataTransferObjects/OwnerDto.cs b/Entities/DataTransferObjects/OwnerDto.cs new file mode 100644 index 0000000..cdbdddc --- /dev/null +++ b/Entities/DataTransferObjects/OwnerDto.cs @@ -0,0 +1,17 @@ +using Entities.Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Entities.DataTransferObjects +{ + public class OwnerDto + { + public Guid Id { get; set; } + public string Name { get; set; } + public DateTime DateOfBirth { get; set; } + public string Address { get; set; } + + public IEnumerable Accounts { get; set; } + } +} diff --git a/Entities/DataTransferObjects/OwnerForCreationDto.cs b/Entities/DataTransferObjects/OwnerForCreationDto.cs new file mode 100644 index 0000000..18595f6 --- /dev/null +++ b/Entities/DataTransferObjects/OwnerForCreationDto.cs @@ -0,0 +1,19 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Entities.DataTransferObjects +{ + public class OwnerForCreationDto + { + [Required(ErrorMessage = "Name is required")] + [StringLength(60, ErrorMessage = "Name can't be longer than 60 characters")] + public string Name { get; set; } + + [Required(ErrorMessage = "Date of birth is required")] + public DateTime DateOfBirth { get; set; } + + [Required(ErrorMessage = "Address is required")] + [StringLength(100, ErrorMessage = "Address cannot be loner then 100 characters")] + public string Address { get; set; } + } +} diff --git a/Entities/DataTransferObjects/OwnerForUpdateDto.cs b/Entities/DataTransferObjects/OwnerForUpdateDto.cs new file mode 100644 index 0000000..5bd7702 --- /dev/null +++ b/Entities/DataTransferObjects/OwnerForUpdateDto.cs @@ -0,0 +1,19 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Entities.DataTransferObjects +{ + public class OwnerForUpdateDto + { + [Required(ErrorMessage = "Name is required")] + [StringLength(60, ErrorMessage = "Name can't be longer than 60 characters")] + public string Name { get; set; } + + [Required(ErrorMessage = "Date of birth is required")] + public DateTime DateOfBirth { get; set; } + + [Required(ErrorMessage = "Address is required")] + [StringLength(100, ErrorMessage = "Address cannot be loner then 100 characters")] + public string Address { get; set; } + } +} diff --git a/Entities/Entities.csproj b/Entities/Entities.csproj index 4de557e..01886ec 100644 --- a/Entities/Entities.csproj +++ b/Entities/Entities.csproj @@ -1,11 +1,11 @@ - netcoreapp2.0 + netcoreapp3.1 - + diff --git a/Entities/Enumerations/AccountType.cs b/Entities/Enumerations/AccountType.cs deleted file mode 100644 index 7cad9c9..0000000 --- a/Entities/Enumerations/AccountType.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Entities.Enumerations -{ - public enum AccountType - { - Domestic, - Savings, - Foreign - } -} diff --git a/Entities/ExtendedModels/OwnerExtended.cs b/Entities/ExtendedModels/OwnerExtended.cs deleted file mode 100644 index 87b8126..0000000 --- a/Entities/ExtendedModels/OwnerExtended.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Entities.Models; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Entities.ExtendedModels -{ - public class OwnerExtended: IEntity - { - public Guid Id { get; set; } - public string Name { get; set; } - public DateTime DateOfBirth { get; set; } - public string Address { get; set; } - - public IEnumerable Accounts { get; set; } - - public OwnerExtended() - { - } - - public OwnerExtended(Owner owner) - { - Id = owner.Id; - Name = owner.Name; - DateOfBirth = owner.DateOfBirth; - Address = owner.Address; - } - } -} diff --git a/Entities/Extensions/IEntityExtensions.cs b/Entities/Extensions/IEntityExtensions.cs deleted file mode 100644 index 992fca0..0000000 --- a/Entities/Extensions/IEntityExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Entities.Extensions -{ - public static class IEntityExtensions - { - public static bool IsObjectNull(this IEntity entity) - { - return entity == null; - } - - public static bool IsEmptyObject(this IEntity entity) - { - return entity.Id.Equals(Guid.Empty); - } - } -} diff --git a/Entities/Extensions/OwnerExtensions.cs b/Entities/Extensions/OwnerExtensions.cs deleted file mode 100644 index 03e8a0a..0000000 --- a/Entities/Extensions/OwnerExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Entities.Models; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Entities.Extensions -{ - public static class OwnerExtensions - { - public static void Map(this Owner dbOwner, Owner owner) - { - dbOwner.Name = owner.Name; - dbOwner.Address = owner.Address; - dbOwner.DateOfBirth = owner.DateOfBirth; - } - } -} diff --git a/Entities/IEntity.cs b/Entities/IEntity.cs deleted file mode 100644 index 8dcadc4..0000000 --- a/Entities/IEntity.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Entities -{ - public interface IEntity - { - Guid Id { get; set; } - } -} diff --git a/Entities/Models/Account.cs b/Entities/Models/Account.cs index 105d588..073d248 100644 --- a/Entities/Models/Account.cs +++ b/Entities/Models/Account.cs @@ -1,26 +1,25 @@ -using Entities.Enumerations; -using System; -using System.Collections.Generic; +using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Text; namespace Entities.Models { - [Table("account")] - public class Account : IEntity + [Table("account")] + public class Account { - [Key] [Column("AccountId")] public Guid Id { get; set; } - [Required(ErrorMessage = "Date created is required")] + [Required(ErrorMessage = "Date created is required")] public DateTime DateCreated { get; set; } - [Required(ErrorMessage = "Account type is required")] + [Required(ErrorMessage = "Account type is required")] public string AccountType { get; set; } - [Required(ErrorMessage = "Owner Id is required")] + [Required(ErrorMessage = "Owner Id is required")] + + [ForeignKey(nameof(Owner))] public Guid OwnerId { get; set; } + public Owner Owner { get; set; } } } diff --git a/Entities/Models/Owner.cs b/Entities/Models/Owner.cs index d52a756..36c3b07 100644 --- a/Entities/Models/Owner.cs +++ b/Entities/Models/Owner.cs @@ -2,26 +2,26 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Text; namespace Entities.Models { - [Table("owner")] - public class Owner: IEntity - { - [Key] + [Table("owner")] + public class Owner + { [Column("OwnerId")] public Guid Id { get; set; } - [Required(ErrorMessage = "Name is required")] - [StringLength(60, ErrorMessage = "Name can't be longer than 60 characters")] + [Required(ErrorMessage = "Name is required")] + [StringLength(60, ErrorMessage = "Name can't be longer than 60 characters")] public string Name { get; set; } - [Required(ErrorMessage = "Date of birth is required")] + [Required(ErrorMessage = "Date of birth is required")] public DateTime DateOfBirth { get; set; } - [Required(ErrorMessage = "Address is required")] - [StringLength(100, ErrorMessage = "Address can not be loner then 100 characters")] + [Required(ErrorMessage = "Address is required")] + [StringLength(100, ErrorMessage = "Address cannot be loner then 100 characters")] public string Address { get; set; } + + public ICollection Accounts { get; set; } } } diff --git a/Entities/RepositoryContext.cs b/Entities/RepositoryContext.cs index 5687078..14a2d7b 100644 --- a/Entities/RepositoryContext.cs +++ b/Entities/RepositoryContext.cs @@ -3,11 +3,11 @@ namespace Entities { - public class RepositoryContext: DbContext + public class RepositoryContext : DbContext { - public RepositoryContext(DbContextOptions options) - :base(options) - { + public RepositoryContext(DbContextOptions options) + : base(options) + { } public DbSet Owners { get; set; } diff --git a/Infrastructure/Registry/docker-compose.yml b/Infrastructure/Registry/docker-compose.yml new file mode 100644 index 0000000..3a8d11b --- /dev/null +++ b/Infrastructure/Registry/docker-compose.yml @@ -0,0 +1,13 @@ +version: '3.0' + +services: + my-registry: + image: registry:latest + container_name: my-registry + volumes: + - registry:/var/lib/registry + ports: + - "50000:5000" + restart: unless-stopped +volumes: + registry: \ No newline at end of file diff --git a/Infrastructure/TeamCity/docker-compose.yml b/Infrastructure/TeamCity/docker-compose.yml new file mode 100644 index 0000000..adda045 --- /dev/null +++ b/Infrastructure/TeamCity/docker-compose.yml @@ -0,0 +1,25 @@ +version: '3.1' + +services: + teamcity: + image: jetbrains/teamcity-server:latest + volumes: + - teamcity-server-datadir:/data/teamcity-server/datadir + - teamcity-server-logs:/opt/teamcity/logs + ports: + - 8111:8111 + teamcity-agent: + image: jetbrains/teamcity-agent:latest + environment: + SERVER_URL: http://teamcity:8111 + volumes: + - teamcity-agent-conf:/data/teamcity_agent/conf + - /opt/buildagent/work:/opt/buildagent/work + - /opt/buildagent/temp:/opt/buildagent/temp + - /var/run/docker.sock:/var/run/docker.sock + + +volumes: + teamcity-server-datadir: + teamcity-server-logs: + teamcity-agent-conf: \ No newline at end of file diff --git a/Integration/Integration.csproj b/Integration/Integration.csproj new file mode 100644 index 0000000..e75e689 --- /dev/null +++ b/Integration/Integration.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + diff --git a/Integration/IntegrationTests.cs b/Integration/IntegrationTests.cs new file mode 100644 index 0000000..61babd4 --- /dev/null +++ b/Integration/IntegrationTests.cs @@ -0,0 +1,43 @@ +using Entities.DataTransferObjects; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; +using Xunit; + +namespace Integration +{ + public class IntegrationTests + { + private readonly TestContext _context; + + public IntegrationTests() + { + _context = new TestContext(); + } + + [Fact] + public async Task GetAllOwners_ReturnsOkResponse() + { + // Act + var response = await _context.Client.GetAsync("/api/owner"); + response.EnsureSuccessStatusCode(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + + [Fact] + public async Task GetAllOwners_ReturnsAListOfOwners() + { + // Act + var response = await _context.Client.GetAsync("/api/owner"); + response.EnsureSuccessStatusCode(); + var responseString = await response.Content.ReadAsStringAsync(); + var owners = JsonConvert.DeserializeObject>(responseString); + + // Assert + Assert.NotEmpty(owners); + } + } +} diff --git a/Integration/TestContext.cs b/Integration/TestContext.cs new file mode 100644 index 0000000..9ec0efe --- /dev/null +++ b/Integration/TestContext.cs @@ -0,0 +1,37 @@ +using AccountOwnerServer; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; +using System; +using System.Net.Http; + +namespace Integration +{ + public class TestContext : IDisposable + { + private TestServer _server; + public HttpClient Client { get; private set; } + + public TestContext() + { + SetUpClient(); + } + + private void SetUpClient() + { + _server = new TestServer(new WebHostBuilder() + .UseConfiguration(new ConfigurationBuilder() + .AddJsonFile("appsettings.json") + .Build()) + .UseStartup()); + + Client = _server.CreateClient(); + } + + public void Dispose() + { + _server?.Dispose(); + Client?.Dispose(); + } + } +} diff --git a/LoggerService/LoggerManager.cs b/LoggerService/LoggerManager.cs index b5d93f1..bf2f7be 100644 --- a/LoggerService/LoggerManager.cs +++ b/LoggerService/LoggerManager.cs @@ -1,6 +1,5 @@ using Contracts; using NLog; -using System; namespace LoggerService { @@ -8,10 +7,6 @@ public class LoggerManager : ILoggerManager { private static ILogger logger = LogManager.GetCurrentClassLogger(); - public LoggerManager() - { - } - public void LogDebug(string message) { logger.Debug(message); diff --git a/LoggerService/LoggerService.csproj b/LoggerService/LoggerService.csproj index 885b749..f24e41a 100644 --- a/LoggerService/LoggerService.csproj +++ b/LoggerService/LoggerService.csproj @@ -1,11 +1,11 @@ - netcoreapp2.0 + netcoreapp3.1 - + diff --git a/README.md b/README.md index 226a577..2640ded 100644 --- a/README.md +++ b/README.md @@ -1 +1,5 @@ # Docker Series +https://code-maze.com/docker-series/ + +## Part 7 of the Docker Series on CodeMaze blog +https://code-maze.com/ci-aspnetcoreapp-docker/ diff --git a/Repository/AccountRepository.cs b/Repository/AccountRepository.cs index 9dcb2d5..4fa3623 100644 --- a/Repository/AccountRepository.cs +++ b/Repository/AccountRepository.cs @@ -1,14 +1,29 @@ using Contracts; using Entities; using Entities.Models; +using System; +using System.Collections.Generic; +using System.Linq; namespace Repository { - public class AccountRepository: RepositoryBase, IAccountRepository - { - public AccountRepository(RepositoryContext repositoryContext) - :base(repositoryContext) + public class AccountRepository : RepositoryBase, IAccountRepository + { + public AccountRepository(RepositoryContext repositoryContext) + : base(repositoryContext) + { + } + + public IEnumerable GetAllAccounts() + { + return FindAll() + .OrderBy(ac => ac.DateCreated); + } + + public void CreateAccount(Account account) { + account.Id = Guid.NewGuid(); + Create(account); } } } diff --git a/Repository/OwnerRepository.cs b/Repository/OwnerRepository.cs index 60422c8..ac72bb2 100644 --- a/Repository/OwnerRepository.cs +++ b/Repository/OwnerRepository.cs @@ -1,61 +1,53 @@ using Contracts; using Entities; using Entities.Models; +using Microsoft.EntityFrameworkCore; +using System; using System.Collections.Generic; using System.Linq; -using System; -using Entities.ExtendedModels; -using Entities.Extensions; namespace Repository { - public class OwnerRepository: RepositoryBase, IOwnerRepository - { - public OwnerRepository(RepositoryContext repositoryContext) - :base(repositoryContext) - { + public class OwnerRepository : RepositoryBase, IOwnerRepository + { + public OwnerRepository(RepositoryContext repositoryContext) + : base(repositoryContext) + { } - public IEnumerable GetAllOwners() - { + public IEnumerable GetAllOwners() + { return FindAll() - .OrderBy(ow => ow.Name); + .OrderBy(ow => ow.Name) + .ToList(); } public Owner GetOwnerById(Guid ownerId) { return FindByCondition(owner => owner.Id.Equals(ownerId)) - .DefaultIfEmpty(new Owner()) - .FirstOrDefault(); + .FirstOrDefault(); } - public OwnerExtended GetOwnerWithDetails(Guid ownerId) + public Owner GetOwnerWithDetails(Guid ownerId) { - return new OwnerExtended(GetOwnerById(ownerId)) - { - Accounts = RepositoryContext.Accounts - .Where(a => a.OwnerId == ownerId) - }; + return FindByCondition(owner => owner.Id.Equals(ownerId)) + .Include(ac => ac.Accounts) + .FirstOrDefault(); } public void CreateOwner(Owner owner) { - owner.Id = Guid.NewGuid(); Create(owner); - Save(); } - public void UpdateOwner(Owner dbOwner, Owner owner) + public void UpdateOwner(Owner owner) { - dbOwner.Map(owner); - Update(dbOwner); - Save(); + Update(owner); } public void DeleteOwner(Owner owner) { Delete(owner); - Save(); } } } diff --git a/Repository/Repository.csproj b/Repository/Repository.csproj index e1eecf3..a2fe898 100644 --- a/Repository/Repository.csproj +++ b/Repository/Repository.csproj @@ -1,16 +1,11 @@ - netcoreapp2.0 + netcoreapp3.1 - - - - - diff --git a/Repository/RepositoryBase.cs b/Repository/RepositoryBase.cs index 64aa6af..5fdacee 100644 --- a/Repository/RepositoryBase.cs +++ b/Repository/RepositoryBase.cs @@ -1,7 +1,7 @@ using Contracts; using Entities; +using Microsoft.EntityFrameworkCore; using System; -using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; @@ -9,41 +9,35 @@ namespace Repository { public abstract class RepositoryBase : IRepositoryBase where T : class { - protected RepositoryContext RepositoryContext { get; set; } - - public RepositoryBase(RepositoryContext repositoryContext) - { - this.RepositoryContext = repositoryContext; - } - - public IEnumerable FindAll() - { - return this.RepositoryContext.Set(); - } - - public IEnumerable FindByCondition(Expression> expression) - { - return this.RepositoryContext.Set().Where(expression); - } - - public void Create(T entity) + protected RepositoryContext RepositoryContext { get; set; } + public RepositoryBase(RepositoryContext repositoryContext) { - this.RepositoryContext.Set().Add(entity); - } - - public void Update(T entity) + RepositoryContext = repositoryContext; + } + + public IQueryable FindAll() + { + return RepositoryContext.Set().AsNoTracking(); + } + + public IQueryable FindByCondition(Expression> expression) + { + return RepositoryContext.Set().Where(expression).AsNoTracking(); + } + + public void Create(T entity) + { + RepositoryContext.Set().Add(entity); + } + + public void Update(T entity) { - this.RepositoryContext.Set().Update(entity); - } - + RepositoryContext.Set().Update(entity); + } + public void Delete(T entity) - { - this.RepositoryContext.Set().Remove(entity); - } - - public void Save() - { - this.RepositoryContext.SaveChanges(); + { + RepositoryContext.Set().Remove(entity); } } } diff --git a/Repository/RepositoryWrapper.cs b/Repository/RepositoryWrapper.cs index d8be3f9..01a8b10 100644 --- a/Repository/RepositoryWrapper.cs +++ b/Repository/RepositoryWrapper.cs @@ -3,41 +3,43 @@ namespace Repository { - public class RepositoryWrapper : IRepositoryWrapper - { - private RepositoryContext _repoContext; - private IOwnerRepository _owner; - private IAccountRepository _account; - - public IOwnerRepository Owner - { - get - { - if (_owner == null) - { - _owner = new OwnerRepository(_repoContext); - } - - return _owner; - } - } - - public IAccountRepository Account - { - get - { - if (_account == null) - { - _account = new AccountRepository(_repoContext); - } - - return _account; - } - } - - public RepositoryWrapper(RepositoryContext repositoryContext) + public class RepositoryWrapper : IRepositoryWrapper + { + private RepositoryContext _repoContext; + private IOwnerRepository _owner; + private IAccountRepository _account; + public IOwnerRepository Owner + { + get + { + if (_owner == null) + { + _owner = new OwnerRepository(_repoContext); + } + return _owner; + } + } + + public IAccountRepository Account + { + get + { + if (_account == null) + { + _account = new AccountRepository(_repoContext); + } + return _account; + } + } + + public RepositoryWrapper(RepositoryContext repositoryContext) + { + _repoContext = repositoryContext; + } + + public void Save() { - _repoContext = repositoryContext; - } + _repoContext.SaveChanges(); + } } } diff --git a/Tests/OwnerRepositoryTests.cs b/Tests/OwnerRepositoryTests.cs new file mode 100644 index 0000000..0d5e4bf --- /dev/null +++ b/Tests/OwnerRepositoryTests.cs @@ -0,0 +1,42 @@ +using Contracts; +using Entities.Models; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace Tests +{ + public class OwnerRepositoryTests + { + [Fact] + public void GetAllOwners_ReturnsListOfOwners_WithSingleOwner() + { + // Arrange + var mockRepo = new Mock(); + mockRepo.Setup(repo => (repo.GetAllOwners())).Returns(GetOwners()); + + // Act + var result = mockRepo.Object.GetAllOwners().ToList(); + + // Assert + Assert.IsType>(result); + Assert.Single(result); + } + + public List GetOwners() + { + return new List + { + new Owner + { + Id = Guid.NewGuid(), + Name = "John Keen", + DateOfBirth = new DateTime(1980, 12, 05), + Address = "61 Wellfield Road" + } + }; + } + } +} diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj new file mode 100644 index 0000000..61e4405 --- /dev/null +++ b/Tests/Tests.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + diff --git a/_MySQL_Init_Script/init.sql b/_MySQL_Init_Script/init.sql deleted file mode 100644 index 648857d..0000000 --- a/_MySQL_Init_Script/init.sql +++ /dev/null @@ -1,81 +0,0 @@ --- MySQL dump 10.13 Distrib 5.7.17, for Win64 (x86_64) --- --- Host: localhost Database: accountowner --- ------------------------------------------------------ --- Server version 5.7.17-log - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `account` --- - -DROP TABLE IF EXISTS `account`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `account` ( - `AccountId` char(36) NOT NULL, - `DateCreated` date NOT NULL, - `AccountType` varchar(45) NOT NULL, - `OwnerId` char(36) NOT NULL, - PRIMARY KEY (`AccountId`), - KEY `fk_Account_Owner_idx` (`OwnerId`), - CONSTRAINT `fk_Account_Owner` FOREIGN KEY (`OwnerId`) REFERENCES `owner` (`OwnerId`) ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `account` --- - -LOCK TABLES `account` WRITE; -/*!40000 ALTER TABLE `account` DISABLE KEYS */; -INSERT INTO `account` VALUES ('03e91478-5608-4132-a753-d494dafce00b','2003-12-15','Domestic','f98e4d74-0f68-4aac-89fd-047f1aaca6b6'),('356a5a9b-64bf-4de0-bc84-5395a1fdc9c4','1996-02-15','Domestic','261e1685-cf26-494c-b17c-3546e65f5620'),('371b93f2-f8c5-4a32-894a-fc672741aa5b','1999-05-04','Domestic','24fd81f8-d58a-4bcc-9f35-dc6cd5641906'),('670775db-ecc0-4b90-a9ab-37cd0d8e2801','1999-12-21','Savings','24fd81f8-d58a-4bcc-9f35-dc6cd5641906'),('a3fbad0b-7f48-4feb-8ac0-6d3bbc997bfc','2010-05-28','Domestic','a3c1880c-674c-4d18-8f91-5d3608a2c937'),('aa15f658-04bb-4f73-82af-82db49d0fbef','1999-05-12','Foreign','24fd81f8-d58a-4bcc-9f35-dc6cd5641906'),('c6066eb0-53ca-43e1-97aa-3c2169eec659','1996-02-16','Foreign','261e1685-cf26-494c-b17c-3546e65f5620'),('eccadf79-85fe-402f-893c-32d3f03ed9b1','2010-06-20','Foreign','a3c1880c-674c-4d18-8f91-5d3608a2c937'); -/*!40000 ALTER TABLE `account` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `owner` --- - -DROP TABLE IF EXISTS `owner`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `owner` ( - `OwnerId` char(36) NOT NULL, - `Name` varchar(60) NOT NULL, - `DateOfBirth` date NOT NULL, - `Address` varchar(100) NOT NULL, - PRIMARY KEY (`OwnerId`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `owner` --- - -LOCK TABLES `owner` WRITE; -/*!40000 ALTER TABLE `owner` DISABLE KEYS */; -INSERT INTO `owner` VALUES ('24fd81f8-d58a-4bcc-9f35-dc6cd5641906','John Keen','1980-12-05','61 Wellfield Road'),('261e1685-cf26-494c-b17c-3546e65f5620','Anna Bosh','1974-11-14','27 Colored Row'),('66774006-2371-4d5b-8518-2177bcf3f73e','Nick Somion','1998-12-15','North sunny address 102'),('a3c1880c-674c-4d18-8f91-5d3608a2c937','Sam Query','1990-04-22','91 Western Roads'),('f98e4d74-0f68-4aac-89fd-047f1aaca6b6','Martin Miller','1983-05-21','3 Edgar Buildings'); -/*!40000 ALTER TABLE `owner` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2017-12-24 15:53:17 diff --git a/docker-compose.integration.yml b/docker-compose.integration.yml new file mode 100644 index 0000000..69d07ed --- /dev/null +++ b/docker-compose.integration.yml @@ -0,0 +1,30 @@ +version: '3.1' + +services: + db: + image: mysql:5.7 + environment: + MYSQL_RANDOM_ROOT_PASSWORD: 1 + MYSQL_DATABASE: accountowner + MYSQL_USER: dbuser + MYSQL_PASSWORD: dbuserpassword + volumes: + - dbdata:/var/lib/mysql + - ./_MySQL_Init_Script:/docker-entrypoint-initdb.d + restart: always + accountownerapp: + depends_on: + - db + image: my-registry:50000/codemazeblog/accountowner:build-${BUILD_NUMBER_ACCOUNTOWNER} + integration: + depends_on: + - accountownerapp + image: my-registry:50000/codemazeblog/accountowner:test + build: + context: . + dockerfile: Dockerfile.Integration + environment: + - TEAMCITY_PROJECT_NAME + +volumes: + dbdata: \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6d95a75 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,26 @@ +version: '3.0' + +services: + db: + image: mysql:5.7 + environment: + MYSQL_RANDOM_ROOT_PASSWORD: 1 + MYSQL_DATABASE: accountowner + MYSQL_USER: dbuser + MYSQL_PASSWORD: dbuserpassword + volumes: + - dbdata:/var/lib/mysql + - ./_MySQL_Init_Script:/docker-entrypoint-initdb.d + restart: always + + accountownerapp: + depends_on: + - db + image: codemazeblog/accountowner:runtime + build: + context: . + ports: + - "8080:5000" + +volumes: + dbdata: \ No newline at end of file