From 1854214aa9ec335f8ab6a8bc2f874f2ac73cc463 Mon Sep 17 00:00:00 2001 From: DjonniStorm Date: Thu, 1 May 2025 15:57:00 +0400 Subject: [PATCH 01/11] =?UTF-8?q?fix:=20=D1=82=D0=B5=D1=81=D1=82=20Try=5FG?= =?UTF-8?q?etElementById=5FWhenHaveRecord=5FTest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit в тесте проходило, что список пустой и это не одно и то же, если он null, для нашей реализации это одно и то же --- .../StorageContactsTests/CreditProgramStorageContractTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TheBank/BankTests/StorageContactsTests/CreditProgramStorageContractTests.cs b/TheBank/BankTests/StorageContactsTests/CreditProgramStorageContractTests.cs index 22356b8..7bd8e6c 100644 --- a/TheBank/BankTests/StorageContactsTests/CreditProgramStorageContractTests.cs +++ b/TheBank/BankTests/StorageContactsTests/CreditProgramStorageContractTests.cs @@ -170,7 +170,7 @@ internal class CreditProgramStorageContractTests : BaseStorageContractTest Assert.That(actual.StorekeeperId, Is.EqualTo(expected.StorekeeperId)); Assert.That(actual.PeriodId, Is.EqualTo(expected.PeriodId)); }); - if (actual.Currencies is not null) + if (actual.Currencies is not null && actual.Currencies.Count > 0) { Assert.That(expected.CurrencyCreditPrograms, Is.Not.Null); Assert.That( @@ -210,7 +210,7 @@ internal class CreditProgramStorageContractTests : BaseStorageContractTest Assert.That(actual.StorekeeperId, Is.EqualTo(expected.StorekeeperId)); Assert.That(actual.PeriodId, Is.EqualTo(expected.PeriodId)); }); - if (actual.CurrencyCreditPrograms is not null) + if (actual.CurrencyCreditPrograms is not null && actual.CurrencyCreditPrograms.Count > 0) { Assert.That(expected.Currencies, Is.Not.Null); Assert.That( From fa9dbb3f60124c712d58fdda01b5cfd92aad99fd Mon Sep 17 00:00:00 2001 From: DjonniStorm Date: Thu, 1 May 2025 17:25:49 +0400 Subject: [PATCH 02/11] =?UTF-8?q?feat!:=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0?= =?UTF-8?q?=20web=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit создание проекта для web api, конфигурации бд и тп, была ошибка с чтением конфигурации из json, решилась --- .../BankBusinessLogic.csproj | 5 + .../AdapterContracts/IClerkAdapter.cs | 18 ++ .../ClerkOperationResponse.cs | 24 +++ TheBank/BankContracts/BankContracts.csproj | 6 + .../BindingModels/ClerkBindingModel.cs | 23 +++ .../Infrastructure/OperationResponse.cs | 49 +++++ .../ViewModels/ClerkViewModel.cs | 23 +++ TheBank/BankDatabase/BankDatabase.csproj | 4 + TheBank/BankWebApi/Adapters/ClerkAdapter.cs | 173 ++++++++++++++++++ TheBank/BankWebApi/AuthOptions.cs | 16 ++ TheBank/BankWebApi/BankWebApi.csproj | 26 +++ TheBank/BankWebApi/BankWebApi.http | 6 + .../Controllers/ClerksController.cs | 39 ++++ .../Infrastructure/ConfigurationDatabase.cs | 14 ++ .../Infrastructure/DataBaseSettings.cs | 6 + TheBank/BankWebApi/Program.cs | 93 ++++++++++ .../BankWebApi/Properties/launchSettings.json | 23 +++ .../BankWebApi/appsettings.Development.json | 8 + TheBank/BankWebApi/appsettings.json | 28 +++ TheBank/TheBank.sln | 6 + 20 files changed, 590 insertions(+) create mode 100644 TheBank/BankContracts/AdapterContracts/IClerkAdapter.cs create mode 100644 TheBank/BankContracts/AdapterContracts/OperationResponses/ClerkOperationResponse.cs create mode 100644 TheBank/BankContracts/BindingModels/ClerkBindingModel.cs create mode 100644 TheBank/BankContracts/Infrastructure/OperationResponse.cs create mode 100644 TheBank/BankContracts/ViewModels/ClerkViewModel.cs create mode 100644 TheBank/BankWebApi/Adapters/ClerkAdapter.cs create mode 100644 TheBank/BankWebApi/AuthOptions.cs create mode 100644 TheBank/BankWebApi/BankWebApi.csproj create mode 100644 TheBank/BankWebApi/BankWebApi.http create mode 100644 TheBank/BankWebApi/Controllers/ClerksController.cs create mode 100644 TheBank/BankWebApi/Infrastructure/ConfigurationDatabase.cs create mode 100644 TheBank/BankWebApi/Infrastructure/DataBaseSettings.cs create mode 100644 TheBank/BankWebApi/Program.cs create mode 100644 TheBank/BankWebApi/Properties/launchSettings.json create mode 100644 TheBank/BankWebApi/appsettings.Development.json create mode 100644 TheBank/BankWebApi/appsettings.json diff --git a/TheBank/BankBusinessLogic/BankBusinessLogic.csproj b/TheBank/BankBusinessLogic/BankBusinessLogic.csproj index 83b7798..1060896 100644 --- a/TheBank/BankBusinessLogic/BankBusinessLogic.csproj +++ b/TheBank/BankBusinessLogic/BankBusinessLogic.csproj @@ -14,4 +14,9 @@ + + + + + diff --git a/TheBank/BankContracts/AdapterContracts/IClerkAdapter.cs b/TheBank/BankContracts/AdapterContracts/IClerkAdapter.cs new file mode 100644 index 0000000..265696e --- /dev/null +++ b/TheBank/BankContracts/AdapterContracts/IClerkAdapter.cs @@ -0,0 +1,18 @@ +using BankContracts.AdapterContracts.OperationResponses; +using BankContracts.BindingModels; + +namespace BankContracts.AdapterContracts; + +/// +/// контракт адаптера для клерка +/// +public interface IClerkAdapter +{ + ClerkOperationResponse GetList(); + + ClerkOperationResponse GetElement(string data); + + ClerkOperationResponse RegisterClerk(ClerkBindingModel clerkDataModel); + + ClerkOperationResponse ChangeClerkInfo(ClerkBindingModel clerkDataModel); +} diff --git a/TheBank/BankContracts/AdapterContracts/OperationResponses/ClerkOperationResponse.cs b/TheBank/BankContracts/AdapterContracts/OperationResponses/ClerkOperationResponse.cs new file mode 100644 index 0000000..223b6ad --- /dev/null +++ b/TheBank/BankContracts/AdapterContracts/OperationResponses/ClerkOperationResponse.cs @@ -0,0 +1,24 @@ +using BankContracts.Infrastructure; +using BankContracts.ViewModels; + +namespace BankContracts.AdapterContracts.OperationResponses; + +public class ClerkOperationResponse : OperationResponse +{ + public static ClerkOperationResponse OK(List data) => + OK>(data); + + public static ClerkOperationResponse OK(ClerkViewModel data) => + OK(data); + + public static ClerkOperationResponse NoContent() => NoContent(); + + public static ClerkOperationResponse NotFound(string message) => + NotFound(message); + + public static ClerkOperationResponse BadRequest(string message) => + BadRequest(message); + + public static ClerkOperationResponse InternalServerError(string message) => + InternalServerError(message); +} diff --git a/TheBank/BankContracts/BankContracts.csproj b/TheBank/BankContracts/BankContracts.csproj index 125f4c9..1728919 100644 --- a/TheBank/BankContracts/BankContracts.csproj +++ b/TheBank/BankContracts/BankContracts.csproj @@ -6,4 +6,10 @@ enable + + + + + + diff --git a/TheBank/BankContracts/BindingModels/ClerkBindingModel.cs b/TheBank/BankContracts/BindingModels/ClerkBindingModel.cs new file mode 100644 index 0000000..7bdd24e --- /dev/null +++ b/TheBank/BankContracts/BindingModels/ClerkBindingModel.cs @@ -0,0 +1,23 @@ +namespace BankContracts.BindingModels; + +/// +/// модель ответа от клиента для клерка +/// +public class ClerkBindingModel +{ + public string? Id { get; set; } + + public string? Name { get; set; } + + public string? Surname { get; set; } + + public string? MiddleName { get; set; } + + public string? Login { get; set; } + + public string? Password { get; set; } + + public string? Email { get; set; } + + public string? PhoneNumber { get; set; } +} diff --git a/TheBank/BankContracts/Infrastructure/OperationResponse.cs b/TheBank/BankContracts/Infrastructure/OperationResponse.cs new file mode 100644 index 0000000..d3eafe6 --- /dev/null +++ b/TheBank/BankContracts/Infrastructure/OperationResponse.cs @@ -0,0 +1,49 @@ +using System.Net; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace BankContracts.Infrastructure; + +/// +/// класс для http ответов +/// +public class OperationResponse +{ + protected HttpStatusCode StatusCode { get; set; } + + protected object? Result { get; set; } + + public IActionResult GetResponse(HttpRequest request, HttpResponse response) + { + ArgumentNullException.ThrowIfNull(request); + ArgumentNullException.ThrowIfNull(response); + + response.StatusCode = (int)StatusCode; + + if (Result is null) + { + return new StatusCodeResult((int)StatusCode); + } + + return new ObjectResult(Result); + } + + protected static TResult OK(TData data) + where TResult : OperationResponse, new() => + new() { StatusCode = HttpStatusCode.OK, Result = data }; + + protected static TResult NoContent() + where TResult : OperationResponse, new() => new() { StatusCode = HttpStatusCode.NoContent }; + + protected static TResult BadRequest(string? errorMessage = null) + where TResult : OperationResponse, new() => + new() { StatusCode = HttpStatusCode.BadRequest, Result = errorMessage }; + + protected static TResult NotFound(string? errorMessage = null) + where TResult : OperationResponse, new() => + new() { StatusCode = HttpStatusCode.NotFound, Result = errorMessage }; + + protected static TResult InternalServerError(string? errorMessage = null) + where TResult : OperationResponse, new() => + new() { StatusCode = HttpStatusCode.InternalServerError, Result = errorMessage }; +} diff --git a/TheBank/BankContracts/ViewModels/ClerkViewModel.cs b/TheBank/BankContracts/ViewModels/ClerkViewModel.cs new file mode 100644 index 0000000..cc2ab42 --- /dev/null +++ b/TheBank/BankContracts/ViewModels/ClerkViewModel.cs @@ -0,0 +1,23 @@ +namespace BankContracts.ViewModels; + +/// +/// модель представления для клерка +/// +public class ClerkViewModel +{ + public required string Id { get; set; } + + public required string Name { get; set; } + + public required string Surname { get; set; } + + public required string MiddleName { get; set; } + + public required string Login { get; set; } + + public required string Password { get; set; } + + public required string Email { get; set; } + + public required string PhoneNumber { get; set; } +} diff --git a/TheBank/BankDatabase/BankDatabase.csproj b/TheBank/BankDatabase/BankDatabase.csproj index c4e8569..909458d 100644 --- a/TheBank/BankDatabase/BankDatabase.csproj +++ b/TheBank/BankDatabase/BankDatabase.csproj @@ -20,4 +20,8 @@ + + + + diff --git a/TheBank/BankWebApi/Adapters/ClerkAdapter.cs b/TheBank/BankWebApi/Adapters/ClerkAdapter.cs new file mode 100644 index 0000000..889d349 --- /dev/null +++ b/TheBank/BankWebApi/Adapters/ClerkAdapter.cs @@ -0,0 +1,173 @@ +using AutoMapper; +using BankContracts.AdapterContracts; +using BankContracts.AdapterContracts.OperationResponses; +using BankContracts.BindingModels; +using BankContracts.BusinessLogicContracts; +using BankContracts.DataModels; +using BankContracts.Exceptions; +using BankContracts.ViewModels; + +namespace BankWebApi.Adapters; + +public class ClerkAdapter : IClerkAdapter +{ + private readonly IClerkBusinessLogicContract _clerkBusinessLogicContract; + + private readonly ILogger _logger; + + private readonly Mapper _mapper; + + public ClerkAdapter(IClerkBusinessLogicContract clerkBusinessLogicContract, ILogger logger) + { + _clerkBusinessLogicContract = clerkBusinessLogicContract; + _logger = logger; + var config = new MapperConfiguration(cfg => + { + cfg.CreateMap(); + cfg.CreateMap(); + }); + _mapper = new Mapper(config); + } + + public ClerkOperationResponse GetList() + { + try + { + return ClerkOperationResponse.OK( + [ + .. _clerkBusinessLogicContract + .GetAllClerks() + .Select(x => _mapper.Map(x)), + ] + ); + } + catch (NullListException) + { + _logger.LogError("NullListException"); + return ClerkOperationResponse.NotFound("The list is not initialized"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return ClerkOperationResponse.InternalServerError( + $"Error while working with data storage:{ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return ClerkOperationResponse.InternalServerError(ex.Message); + } + } + + public ClerkOperationResponse GetElement(string data) + { + try + { + return ClerkOperationResponse.OK( + _mapper.Map(_clerkBusinessLogicContract.GetClerkByData(data)) + ); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return ClerkOperationResponse.BadRequest("Data is empty"); + } + catch (ElementNotFoundException ex) + { + _logger.LogError(ex, "ElementNotFoundException"); + return ClerkOperationResponse.NotFound($"Not found element by data {data}"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return ClerkOperationResponse.InternalServerError( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return ClerkOperationResponse.InternalServerError(ex.Message); + } + } + + public ClerkOperationResponse RegisterClerk(ClerkBindingModel clerkDataModel) + { + try + { + _clerkBusinessLogicContract.InsertClerk(_mapper.Map(clerkDataModel)); + return ClerkOperationResponse.NoContent(); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return ClerkOperationResponse.BadRequest("Data is empty"); + } + catch (ValidationException ex) + { + _logger.LogError(ex, "ValidationException"); + return ClerkOperationResponse.BadRequest($"Incorrect data transmitted: {ex.Message}"); + } + catch (ElementExistsException ex) + { + _logger.LogError(ex, "ElementExistsException"); + return ClerkOperationResponse.BadRequest(ex.Message); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return ClerkOperationResponse.BadRequest( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return ClerkOperationResponse.InternalServerError(ex.Message); + } + } + + public ClerkOperationResponse ChangeClerkInfo(ClerkBindingModel clerkDataModel) + { + try + { + _clerkBusinessLogicContract.UpdateClerk(_mapper.Map(clerkDataModel)); + return ClerkOperationResponse.NoContent(); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return ClerkOperationResponse.BadRequest("Data is empty"); + } + catch (ValidationException ex) + { + _logger.LogError(ex, "ValidationException"); + return ClerkOperationResponse.BadRequest($"Incorrect data transmitted: {ex.Message}"); + } + catch (ElementNotFoundException ex) + { + _logger.LogError(ex, "ElementNotFoundException"); + return ClerkOperationResponse.BadRequest( + $"Not found element by Id {clerkDataModel.Id}" + ); + } + catch (ElementExistsException ex) + { + _logger.LogError(ex, "ElementExistsException"); + return ClerkOperationResponse.BadRequest(ex.Message); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return ClerkOperationResponse.BadRequest( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return ClerkOperationResponse.InternalServerError(ex.Message); + } + } +} diff --git a/TheBank/BankWebApi/AuthOptions.cs b/TheBank/BankWebApi/AuthOptions.cs new file mode 100644 index 0000000..f9e7702 --- /dev/null +++ b/TheBank/BankWebApi/AuthOptions.cs @@ -0,0 +1,16 @@ +using Microsoft.IdentityModel.Tokens; +using System.Text; + +namespace BankWebApi; + +/// +/// настройки для авторизации +/// переделаем потом если не будет впадлу +/// +public class AuthOptions +{ + public const string ISSUER = "Bank_AuthServer"; // издатель токена + public const string AUDIENCE = "Bank_AuthClient"; // потребитель токена + const string KEY = "banksuperpupersecret_secretsecretsecretkey!"; // ключ для шифрации + public static SymmetricSecurityKey GetSymmetricSecurityKey() => new(Encoding.UTF8.GetBytes(KEY)); +} diff --git a/TheBank/BankWebApi/BankWebApi.csproj b/TheBank/BankWebApi/BankWebApi.csproj new file mode 100644 index 0000000..5abea56 --- /dev/null +++ b/TheBank/BankWebApi/BankWebApi.csproj @@ -0,0 +1,26 @@ + + + + net9.0 + enable + enable + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + diff --git a/TheBank/BankWebApi/BankWebApi.http b/TheBank/BankWebApi/BankWebApi.http new file mode 100644 index 0000000..d07a81d --- /dev/null +++ b/TheBank/BankWebApi/BankWebApi.http @@ -0,0 +1,6 @@ +@BankWebApi_HostAddress = http://localhost:5189 + +GET {{BankWebApi_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/TheBank/BankWebApi/Controllers/ClerksController.cs b/TheBank/BankWebApi/Controllers/ClerksController.cs new file mode 100644 index 0000000..45245ab --- /dev/null +++ b/TheBank/BankWebApi/Controllers/ClerksController.cs @@ -0,0 +1,39 @@ +using BankContracts.AdapterContracts; +using BankContracts.BindingModels; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace BankWebApi.Controllers; + +[Authorize] +[Route("api/[controller]")] +[ApiController] +[Produces("application/json")] +public class ClerksController(IClerkAdapter adapter) : ControllerBase +{ + private readonly IClerkAdapter _adapter = adapter; + + [HttpGet] + public IActionResult GetAllRecords() + { + return _adapter.GetList().GetResponse(Request, Response); + } + + [HttpGet("{data}")] + public IActionResult GetRecord(string data) + { + return _adapter.GetElement(data).GetResponse(Request, Response); + } + + [HttpPost] + public IActionResult Register([FromBody] ClerkBindingModel model) + { + return _adapter.RegisterClerk(model).GetResponse(Request, Response); + } + + [HttpPut] + public IActionResult ChangeInfo([FromBody] ClerkBindingModel model) + { + return _adapter.ChangeClerkInfo(model).GetResponse(Request, Response); + } +} diff --git a/TheBank/BankWebApi/Infrastructure/ConfigurationDatabase.cs b/TheBank/BankWebApi/Infrastructure/ConfigurationDatabase.cs new file mode 100644 index 0000000..3c9e747 --- /dev/null +++ b/TheBank/BankWebApi/Infrastructure/ConfigurationDatabase.cs @@ -0,0 +1,14 @@ +using BankContracts.Infrastructure; + +namespace BankWebApi.Infrastructure; + +public class ConfigurationDatabase(IConfiguration configuration) : IConfigurationDatabase +{ + private readonly Lazy _dataBaseSettings = new(() => + { + return configuration.GetSection("DataBaseSettings").Get() + ?? throw new InvalidDataException("DataBaseSettings section is missing or invalid."); + }); + + public string ConnectionString => _dataBaseSettings.Value.ConnectionString; +} diff --git a/TheBank/BankWebApi/Infrastructure/DataBaseSettings.cs b/TheBank/BankWebApi/Infrastructure/DataBaseSettings.cs new file mode 100644 index 0000000..c38498e --- /dev/null +++ b/TheBank/BankWebApi/Infrastructure/DataBaseSettings.cs @@ -0,0 +1,6 @@ +namespace BankWebApi.Infrastructure; + +public class DataBaseSettings +{ + public required string ConnectionString { get; set; } +} diff --git a/TheBank/BankWebApi/Program.cs b/TheBank/BankWebApi/Program.cs new file mode 100644 index 0000000..751ffa7 --- /dev/null +++ b/TheBank/BankWebApi/Program.cs @@ -0,0 +1,93 @@ +using BankBusinessLogic.Implementations; +using BankContracts.AdapterContracts; +using BankContracts.BusinessLogicContracts; +using BankContracts.Infrastructure; +using BankContracts.StorageContracts; +using BankDatabase; +using BankDatabase.Implementations; +using BankWebApi; +using BankWebApi.Adapters; +using BankWebApi.Infrastructure; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.EntityFrameworkCore; +using Microsoft.IdentityModel.Tokens; +using Serilog; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); +using var loggerFactory = new LoggerFactory(); +loggerFactory.AddSerilog(new LoggerConfiguration().ReadFrom.Configuration(builder.Configuration).CreateLogger()); +builder.Services.AddSingleton(loggerFactory.CreateLogger("Any")); + +builder.Services.AddAuthorization(); +builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + options.TokenValidationParameters = new TokenValidationParameters + { + + ValidateIssuer = true, + + ValidIssuer = AuthOptions.ISSUER, + + ValidateAudience = true, + + ValidAudience = AuthOptions.AUDIENCE, + + ValidateLifetime = true, + + IssuerSigningKey = AuthOptions.GetSymmetricSecurityKey(), + + ValidateIssuerSigningKey = true, + }; + }); + +// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi +builder.Services.AddOpenApi(); +builder.Services.AddSingleton(); +// +builder.Services.AddTransient(); +// +builder.Services.AddTransient(); +builder.Services.AddTransient(); +// +builder.Services.AddTransient(); +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.MapOpenApi(); +} +if (app.Environment.IsProduction()) +{ + var dbContext = app.Services.GetRequiredService(); + if (dbContext.Database.CanConnect()) + { + dbContext.Database.EnsureCreated(); + dbContext.Database.Migrate(); + } +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.Map("/login/{username}", (string username) => +{ + return new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken( + issuer: AuthOptions.ISSUER, + audience: AuthOptions.AUDIENCE, + claims: [new(ClaimTypes.Name, username)], + expires: DateTime.UtcNow.Add(TimeSpan.FromMinutes(2)), + signingCredentials: new SigningCredentials(AuthOptions.GetSymmetricSecurityKey(), SecurityAlgorithms.HmacSha256))); +}); + +app.MapControllers(); + +app.Run(); diff --git a/TheBank/BankWebApi/Properties/launchSettings.json b/TheBank/BankWebApi/Properties/launchSettings.json new file mode 100644 index 0000000..13b957b --- /dev/null +++ b/TheBank/BankWebApi/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5189", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7204;http://localhost:5189", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/TheBank/BankWebApi/appsettings.Development.json b/TheBank/BankWebApi/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/TheBank/BankWebApi/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/TheBank/BankWebApi/appsettings.json b/TheBank/BankWebApi/appsettings.json new file mode 100644 index 0000000..758c5cb --- /dev/null +++ b/TheBank/BankWebApi/appsettings.json @@ -0,0 +1,28 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "Serilog": { + "Using": [ "Serilog.Sinks.File" ], + "MinimumLevel": { + "Default": "Information" + }, + "WriteTo": [ + { + "Name": "File", + "Args": { + "path": "../logs/bank-.log", + "rollingInterval": "Day", + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} {CorrelationId} {Level:u3} {Username} {Message:lj} {Exception} {NewLine}," + } + } + ] + }, + "DataBaseSettings": { + "ConnectionString": "Host=127.0.0.1;Port=5432;Database=BankTest;Username=postgres;Password=admin123;" + }, + "AllowedHosts": "*" +} \ No newline at end of file diff --git a/TheBank/TheBank.sln b/TheBank/TheBank.sln index d28ceae..3b390b3 100644 --- a/TheBank/TheBank.sln +++ b/TheBank/TheBank.sln @@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BankDatabase", "BankDatabas EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BankTests", "BankTests\BankTests.csproj", "{7C898FC8-6BC2-41E8-9538-D42ACC263A97}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BankWebApi", "BankWebApi\BankWebApi.csproj", "{C8A3A3BB-A096-429F-A763-5465C5CB735F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,6 +35,10 @@ Global {7C898FC8-6BC2-41E8-9538-D42ACC263A97}.Debug|Any CPU.Build.0 = Debug|Any CPU {7C898FC8-6BC2-41E8-9538-D42ACC263A97}.Release|Any CPU.ActiveCfg = Release|Any CPU {7C898FC8-6BC2-41E8-9538-D42ACC263A97}.Release|Any CPU.Build.0 = Release|Any CPU + {C8A3A3BB-A096-429F-A763-5465C5CB735F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C8A3A3BB-A096-429F-A763-5465C5CB735F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8A3A3BB-A096-429F-A763-5465C5CB735F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C8A3A3BB-A096-429F-A763-5465C5CB735F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 6273bff7fb5fa135aef7b24bfa2fecf969cc9ff7 Mon Sep 17 00:00:00 2001 From: DjonniStorm Date: Thu, 1 May 2025 18:06:01 +0400 Subject: [PATCH 03/11] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20swagger=20=D0=B4=D0=BE=D0=BA?= =?UTF-8?q?=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TheBank/BankWebApi/BankWebApi.csproj | 7 +++- .../Controllers/ClerksController.cs | 19 +++++++++ TheBank/BankWebApi/Program.cs | 42 ++++++++++++++++++- 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/TheBank/BankWebApi/BankWebApi.csproj b/TheBank/BankWebApi/BankWebApi.csproj index 5abea56..fd68b27 100644 --- a/TheBank/BankWebApi/BankWebApi.csproj +++ b/TheBank/BankWebApi/BankWebApi.csproj @@ -1,4 +1,4 @@ - + net9.0 @@ -15,6 +15,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + @@ -23,4 +24,8 @@ + + true + $(NoWarn);1591 + diff --git a/TheBank/BankWebApi/Controllers/ClerksController.cs b/TheBank/BankWebApi/Controllers/ClerksController.cs index 45245ab..b2c0039 100644 --- a/TheBank/BankWebApi/Controllers/ClerksController.cs +++ b/TheBank/BankWebApi/Controllers/ClerksController.cs @@ -13,24 +13,43 @@ public class ClerksController(IClerkAdapter adapter) : ControllerBase { private readonly IClerkAdapter _adapter = adapter; + /// + /// получение всех записей клерков + /// + /// список клерков [HttpGet] public IActionResult GetAllRecords() { return _adapter.GetList().GetResponse(Request, Response); } + /// + /// получние записи о клерке по данным + /// + /// уникальный идентификатор или другое поле + /// запись клерка [HttpGet("{data}")] public IActionResult GetRecord(string data) { return _adapter.GetElement(data).GetResponse(Request, Response); } + /// + /// создание записи клерка + /// + /// модель от пользователя + /// [HttpPost] public IActionResult Register([FromBody] ClerkBindingModel model) { return _adapter.RegisterClerk(model).GetResponse(Request, Response); } + /// + /// изменение записи клерка + /// + /// новая модель + /// [HttpPut] public IActionResult ChangeInfo([FromBody] ClerkBindingModel model) { diff --git a/TheBank/BankWebApi/Program.cs b/TheBank/BankWebApi/Program.cs index 751ffa7..5e1dc63 100644 --- a/TheBank/BankWebApi/Program.cs +++ b/TheBank/BankWebApi/Program.cs @@ -11,6 +11,7 @@ using BankWebApi.Infrastructure; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi.Models; using Serilog; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; @@ -23,7 +24,40 @@ builder.Services.AddControllers(); using var loggerFactory = new LoggerFactory(); loggerFactory.AddSerilog(new LoggerConfiguration().ReadFrom.Configuration(builder.Configuration).CreateLogger()); builder.Services.AddSingleton(loggerFactory.CreateLogger("Any")); +builder.Services.AddSwaggerGen(c => +{ + c.SwaggerDoc("v1", new OpenApiInfo { Title = "Bank API", Version = "v1" }); + // XML- ( ) + var xmlFile = $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml"; + var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); + c.IncludeXmlComments(xmlPath, includeControllerXmlComments: true); + + // JWT- Swagger UI + c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = "JWT Authorization header using the Bearer scheme. Example: 'Bearer {token}'", + Name = "Authorization", + In = ParameterLocation.Header, + Type = SecuritySchemeType.Http, + Scheme = "bearer", + BearerFormat = "JWT" + }); + c.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + } + }, + [] + } + }); +}); builder.Services.AddAuthorization(); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => @@ -63,6 +97,12 @@ var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.MapOpenApi(); + app.UseSwagger(); + app.UseSwaggerUI(c => + { + c.SwaggerEndpoint("/swagger/v1/swagger.json", "Bank API V1"); + c.RoutePrefix = "swagger"; // Swagger UI /swagger + }); } if (app.Environment.IsProduction()) { @@ -75,7 +115,7 @@ if (app.Environment.IsProduction()) } app.UseHttpsRedirection(); - +app.UseAuthentication(); app.UseAuthorization(); app.Map("/login/{username}", (string username) => From 5b25dcf97618fe47fb8291551bf766e5e08b4e44 Mon Sep 17 00:00:00 2001 From: DjonniStorm Date: Fri, 2 May 2025 00:56:35 +0400 Subject: [PATCH 04/11] =?UTF-8?q?fix:=20=D1=84=D0=B8=D0=BA=D1=81=20=D0=B4?= =?UTF-8?q?=D0=B0=D1=82=20=D0=B2=20=D0=B1=D0=B8=D0=B7=D0=BD=D0=B5=D1=81=20?= =?UTF-8?q?=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D0=B5=20=D1=81=D1=80=D0=BE=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=B8=20=D0=BD=D0=B0=D0=B7=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B9=20=D0=B2=20=D0=BA=D0=BE=D0=BD=D1=82=D1=80=D0=B0?= =?UTF-8?q?=D0=BA=D1=82=D0=B0=D1=85=20=D0=BA=D0=BB=D0=B5=D1=80=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PeriodBusinessLogicContract.cs | 17 ++++++----------- .../AdapterContracts/IClerkAdapter.cs | 4 ++-- .../IPeriodBusinessLogicContract.cs | 4 ++-- TheBank/BankWebApi/Adapters/ClerkAdapter.cs | 10 +++++----- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/TheBank/BankBusinessLogic/Implementations/PeriodBusinessLogicContract.cs b/TheBank/BankBusinessLogic/Implementations/PeriodBusinessLogicContract.cs index 3da86b1..23141bc 100644 --- a/TheBank/BankBusinessLogic/Implementations/PeriodBusinessLogicContract.cs +++ b/TheBank/BankBusinessLogic/Implementations/PeriodBusinessLogicContract.cs @@ -27,24 +27,19 @@ internal class PeriodBusinessLogicContract( _logger.LogInformation("get all periods"); return _periodStorageContract.GetList(); } - - public List GetAllPeriodsByEndTime(DateTime fromDate, DateTime toDate) + public List GetAllPeriodsByStartTime(DateTime fromDate) { - if (toDate.IsDateNotOlder(toDate)) + if (fromDate.IsDateNotOlder(DateTime.UtcNow)) { - throw new IncorrectDatesException(fromDate, toDate); + throw new IncorrectDatesException(fromDate, DateTime.UtcNow); } - return _periodStorageContract.GetList(fromDate, toDate).OrderBy(x => x.EndTime).ToList() + return _periodStorageContract.GetList(startDate: fromDate).OrderBy(x => x.StartTime).ToList() ?? throw new NullListException(nameof(PeriodDataModel)); } - public List GetAllPeriodsByStartTime(DateTime fromDate, DateTime toDate) + public List GetAllPeriodsByEndTime(DateTime toDate) { - if (toDate.IsDateNotOlder(toDate)) - { - throw new IncorrectDatesException(fromDate, toDate); - } - return _periodStorageContract.GetList(fromDate, toDate).OrderBy(x => x.StartTime).ToList() + return _periodStorageContract.GetList(endDate: toDate).OrderBy(x => x.EndTime).ToList() ?? throw new NullListException(nameof(PeriodDataModel)); } diff --git a/TheBank/BankContracts/AdapterContracts/IClerkAdapter.cs b/TheBank/BankContracts/AdapterContracts/IClerkAdapter.cs index 265696e..dbef05c 100644 --- a/TheBank/BankContracts/AdapterContracts/IClerkAdapter.cs +++ b/TheBank/BankContracts/AdapterContracts/IClerkAdapter.cs @@ -12,7 +12,7 @@ public interface IClerkAdapter ClerkOperationResponse GetElement(string data); - ClerkOperationResponse RegisterClerk(ClerkBindingModel clerkDataModel); + ClerkOperationResponse RegisterClerk(ClerkBindingModel clerkModel); - ClerkOperationResponse ChangeClerkInfo(ClerkBindingModel clerkDataModel); + ClerkOperationResponse ChangeClerkInfo(ClerkBindingModel clerkModel); } diff --git a/TheBank/BankContracts/BusinessLogicContracts/IPeriodBusinessLogicContract.cs b/TheBank/BankContracts/BusinessLogicContracts/IPeriodBusinessLogicContract.cs index 6a9f743..e658ffd 100644 --- a/TheBank/BankContracts/BusinessLogicContracts/IPeriodBusinessLogicContract.cs +++ b/TheBank/BankContracts/BusinessLogicContracts/IPeriodBusinessLogicContract.cs @@ -10,9 +10,9 @@ public interface IPeriodBusinessLogicContract List GetAllPeriodsByStorekeeper(string storekeeperId); - List GetAllPeriodsByStartTime(DateTime fromDate, DateTime toDate); + List GetAllPeriodsByStartTime(DateTime fromDate); - List GetAllPeriodsByEndTime(DateTime fromDate, DateTime toDate); + List GetAllPeriodsByEndTime(DateTime toDate); void InsertPeriod(PeriodDataModel periodataModel); diff --git a/TheBank/BankWebApi/Adapters/ClerkAdapter.cs b/TheBank/BankWebApi/Adapters/ClerkAdapter.cs index 889d349..bf97e01 100644 --- a/TheBank/BankWebApi/Adapters/ClerkAdapter.cs +++ b/TheBank/BankWebApi/Adapters/ClerkAdapter.cs @@ -92,11 +92,11 @@ public class ClerkAdapter : IClerkAdapter } } - public ClerkOperationResponse RegisterClerk(ClerkBindingModel clerkDataModel) + public ClerkOperationResponse RegisterClerk(ClerkBindingModel clerkModel) { try { - _clerkBusinessLogicContract.InsertClerk(_mapper.Map(clerkDataModel)); + _clerkBusinessLogicContract.InsertClerk(_mapper.Map(clerkModel)); return ClerkOperationResponse.NoContent(); } catch (ArgumentNullException ex) @@ -128,11 +128,11 @@ public class ClerkAdapter : IClerkAdapter } } - public ClerkOperationResponse ChangeClerkInfo(ClerkBindingModel clerkDataModel) + public ClerkOperationResponse ChangeClerkInfo(ClerkBindingModel clerkModel) { try { - _clerkBusinessLogicContract.UpdateClerk(_mapper.Map(clerkDataModel)); + _clerkBusinessLogicContract.UpdateClerk(_mapper.Map(clerkModel)); return ClerkOperationResponse.NoContent(); } catch (ArgumentNullException ex) @@ -149,7 +149,7 @@ public class ClerkAdapter : IClerkAdapter { _logger.LogError(ex, "ElementNotFoundException"); return ClerkOperationResponse.BadRequest( - $"Not found element by Id {clerkDataModel.Id}" + $"Not found element by Id {clerkModel.Id}" ); } catch (ElementExistsException ex) From bb1432fa4dbb70e52ebfb6458bf60387e26e8a7b Mon Sep 17 00:00:00 2001 From: DjonniStorm Date: Fri, 2 May 2025 00:56:52 +0400 Subject: [PATCH 05/11] =?UTF-8?q?feat:=20=D0=B0=D0=BF=D0=B8=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D1=81=D1=80=D0=BE=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AdapterContracts/IPeriodAdapter.cs | 21 ++ .../PeriodOperationResponse.cs | 24 ++ .../BindingModels/PeriodBindingModel.cs | 15 + .../ViewModels/PeriodViewModel.cs | 15 + TheBank/BankWebApi/Adapters/PeriodAdapter.cs | 275 ++++++++++++++++++ .../Controllers/PeriodController.cs | 92 ++++++ TheBank/BankWebApi/Program.cs | 4 + 7 files changed, 446 insertions(+) create mode 100644 TheBank/BankContracts/AdapterContracts/IPeriodAdapter.cs create mode 100644 TheBank/BankContracts/AdapterContracts/OperationResponses/PeriodOperationResponse.cs create mode 100644 TheBank/BankContracts/BindingModels/PeriodBindingModel.cs create mode 100644 TheBank/BankContracts/ViewModels/PeriodViewModel.cs create mode 100644 TheBank/BankWebApi/Adapters/PeriodAdapter.cs create mode 100644 TheBank/BankWebApi/Controllers/PeriodController.cs diff --git a/TheBank/BankContracts/AdapterContracts/IPeriodAdapter.cs b/TheBank/BankContracts/AdapterContracts/IPeriodAdapter.cs new file mode 100644 index 0000000..233aff2 --- /dev/null +++ b/TheBank/BankContracts/AdapterContracts/IPeriodAdapter.cs @@ -0,0 +1,21 @@ +using BankContracts.AdapterContracts.OperationResponses; +using BankContracts.BindingModels; + +namespace BankContracts.AdapterContracts; + +public interface IPeriodAdapter +{ + PeriodOperationResponse GetList(); + + PeriodOperationResponse GetElement(string data); + + PeriodOperationResponse GetListByStorekeeper(string storekeeperId); + + PeriodOperationResponse GetListByStartTime(DateTime fromDate); + + PeriodOperationResponse GetListByEndTime(DateTime toDate); + + PeriodOperationResponse RegisterPeriod(PeriodBindingModel periodModel); + + PeriodOperationResponse ChangePeriodInfo(PeriodBindingModel periodModel); +} diff --git a/TheBank/BankContracts/AdapterContracts/OperationResponses/PeriodOperationResponse.cs b/TheBank/BankContracts/AdapterContracts/OperationResponses/PeriodOperationResponse.cs new file mode 100644 index 0000000..8d0d2d6 --- /dev/null +++ b/TheBank/BankContracts/AdapterContracts/OperationResponses/PeriodOperationResponse.cs @@ -0,0 +1,24 @@ +using BankContracts.Infrastructure; +using BankContracts.ViewModels; + +namespace BankContracts.AdapterContracts.OperationResponses; + +public class PeriodOperationResponse : OperationResponse +{ + public static PeriodOperationResponse OK(List data) => + OK>(data); + + public static PeriodOperationResponse OK(PeriodViewModel data) => + OK(data); + + public static PeriodOperationResponse NoContent() => NoContent(); + + public static PeriodOperationResponse NotFound(string message) => + NotFound(message); + + public static PeriodOperationResponse BadRequest(string message) => + BadRequest(message); + + public static PeriodOperationResponse InternalServerError(string message) => + InternalServerError(message); +} diff --git a/TheBank/BankContracts/BindingModels/PeriodBindingModel.cs b/TheBank/BankContracts/BindingModels/PeriodBindingModel.cs new file mode 100644 index 0000000..72b3e30 --- /dev/null +++ b/TheBank/BankContracts/BindingModels/PeriodBindingModel.cs @@ -0,0 +1,15 @@ +namespace BankContracts.BindingModels; + +/// +/// модель ответа от клиента для срока +/// +public class PeriodBindingModel +{ + public string? Id { get; set; } + + public DateTime StartTime { get; set; } + + public DateTime EndTime { get; set; } + + public string? StorekeeperId { get; set; } +} diff --git a/TheBank/BankContracts/ViewModels/PeriodViewModel.cs b/TheBank/BankContracts/ViewModels/PeriodViewModel.cs new file mode 100644 index 0000000..9b41b53 --- /dev/null +++ b/TheBank/BankContracts/ViewModels/PeriodViewModel.cs @@ -0,0 +1,15 @@ +namespace BankContracts.ViewModels; + +/// +/// модель представления для срока +/// +public class PeriodViewModel +{ + public required string Id { get; set; } + + public DateTime StartTime { get; set; } + + public DateTime EndTime { get; set; } + + public required string StorekeeperId { get; set; } +} diff --git a/TheBank/BankWebApi/Adapters/PeriodAdapter.cs b/TheBank/BankWebApi/Adapters/PeriodAdapter.cs new file mode 100644 index 0000000..ff6a782 --- /dev/null +++ b/TheBank/BankWebApi/Adapters/PeriodAdapter.cs @@ -0,0 +1,275 @@ +using AutoMapper; +using BankContracts.AdapterContracts; +using BankContracts.AdapterContracts.OperationResponses; +using BankContracts.BindingModels; +using BankContracts.BusinessLogicContracts; +using BankContracts.DataModels; +using BankContracts.Exceptions; +using BankContracts.ViewModels; + +namespace BankWebApi.Adapters; + +public class PeriodAdapter : IPeriodAdapter +{ + private readonly IPeriodBusinessLogicContract _periodBusinessLogicContract; + + private readonly ILogger _logger; + + private readonly Mapper _mapper; + + public PeriodAdapter(IPeriodBusinessLogicContract periodBusinessLogicContract, ILogger logger) + { + _periodBusinessLogicContract = periodBusinessLogicContract; + _logger = logger; + var config = new MapperConfiguration(cfg => + { + cfg.CreateMap(); + cfg.CreateMap(); + }); + _mapper = new Mapper(config); + } + + public PeriodOperationResponse GetList() + { + try + { + return PeriodOperationResponse.OK( + [ + .. _periodBusinessLogicContract + .GetAllPeriods() + .Select(x => _mapper.Map(x)), + ] + ); + } + catch (NullListException) + { + _logger.LogError("NullListException"); + return PeriodOperationResponse.NotFound("The list is not initialized"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return PeriodOperationResponse.InternalServerError( + $"Error while working with data storage:{ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return PeriodOperationResponse.InternalServerError(ex.Message); + } + } + public PeriodOperationResponse GetElement(string data) + { + try + { + return PeriodOperationResponse.OK( + _mapper.Map(_periodBusinessLogicContract.GetPeriodByData(data)) + ); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return PeriodOperationResponse.BadRequest("Data is empty"); + } + catch (ElementNotFoundException ex) + { + _logger.LogError(ex, "ElementNotFoundException"); + return PeriodOperationResponse.NotFound($"Not found element by data {data}"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return PeriodOperationResponse.InternalServerError( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return PeriodOperationResponse.InternalServerError(ex.Message); + } + } + + public PeriodOperationResponse GetListByStorekeeper(string storekeeperId) + { + try + { + return PeriodOperationResponse.OK( + [ + .. _periodBusinessLogicContract + .GetAllPeriodsByStorekeeper(storekeeperId) + .Select(x => _mapper.Map(x)), + ] + ); + } + catch (NullListException) + { + _logger.LogError("NullListException"); + return PeriodOperationResponse.NotFound("The list is not initialized"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return PeriodOperationResponse.InternalServerError( + $"Error while working with data storage:{ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return PeriodOperationResponse.InternalServerError(ex.Message); + } + } + + public PeriodOperationResponse GetListByStartTime(DateTime fromDate) + { + try + { + return PeriodOperationResponse.OK( + [ + .. _periodBusinessLogicContract + .GetAllPeriodsByStartTime(fromDate) + .Select(x => _mapper.Map(x)), + ] + ); + } + catch (NullListException) + { + _logger.LogError("NullListException"); + return PeriodOperationResponse.NotFound("The list is not initialized"); + } + catch (IncorrectDatesException) + { + _logger.LogError("IncorrectDatesException"); + return PeriodOperationResponse.BadRequest("Incorrect dates"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return PeriodOperationResponse.InternalServerError( + $"Error while working with data storage:{ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return PeriodOperationResponse.InternalServerError(ex.Message); + } + } + + public PeriodOperationResponse GetListByEndTime(DateTime toDate) + { + try + { + return PeriodOperationResponse.OK( + [ + .. _periodBusinessLogicContract + .GetAllPeriodsByStartTime(toDate) + .Select(x => _mapper.Map(x)), + ] + ); + } + catch (NullListException) + { + _logger.LogError("NullListException"); + return PeriodOperationResponse.NotFound("The list is not initialized"); + } + catch (IncorrectDatesException) + { + _logger.LogError("IncorrectDatesException"); + return PeriodOperationResponse.BadRequest("Incorrect dates"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return PeriodOperationResponse.InternalServerError( + $"Error while working with data storage:{ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return PeriodOperationResponse.InternalServerError(ex.Message); + } + } + + public PeriodOperationResponse RegisterPeriod(PeriodBindingModel periodModel) + { + try + { + _periodBusinessLogicContract.InsertPeriod(_mapper.Map(periodModel)); + return PeriodOperationResponse.NoContent(); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return PeriodOperationResponse.BadRequest("Data is empty"); + } + catch (ValidationException ex) + { + _logger.LogError(ex, "ValidationException"); + return PeriodOperationResponse.BadRequest($"Incorrect data transmitted: {ex.Message}"); + } + catch (ElementExistsException ex) + { + _logger.LogError(ex, "ElementExistsException"); + return PeriodOperationResponse.BadRequest(ex.Message); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return PeriodOperationResponse.BadRequest( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return PeriodOperationResponse.InternalServerError(ex.Message); + } + } + + public PeriodOperationResponse ChangePeriodInfo(PeriodBindingModel periodModel) + { + try + { + _periodBusinessLogicContract.UpdatePeriod(_mapper.Map(periodModel)); + return PeriodOperationResponse.NoContent(); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return PeriodOperationResponse.BadRequest("Data is empty"); + } + catch (ValidationException ex) + { + _logger.LogError(ex, "ValidationException"); + return PeriodOperationResponse.BadRequest($"Incorrect data transmitted: {ex.Message}"); + } + catch (ElementNotFoundException ex) + { + _logger.LogError(ex, "ElementNotFoundException"); + return PeriodOperationResponse.BadRequest( + $"Not found element by Id {periodModel.Id}" + ); + } + catch (ElementExistsException ex) + { + _logger.LogError(ex, "ElementExistsException"); + return PeriodOperationResponse.BadRequest(ex.Message); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return PeriodOperationResponse.BadRequest( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return PeriodOperationResponse.InternalServerError(ex.Message); + } + } +} diff --git a/TheBank/BankWebApi/Controllers/PeriodController.cs b/TheBank/BankWebApi/Controllers/PeriodController.cs new file mode 100644 index 0000000..3203689 --- /dev/null +++ b/TheBank/BankWebApi/Controllers/PeriodController.cs @@ -0,0 +1,92 @@ +using BankContracts.AdapterContracts; +using BankContracts.BindingModels; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace BankWebApi.Controllers; + +[Authorize] +[Route("api/[controller]/[action]")] +[ApiController] +[Produces("application/json")] +public class PeriodController(IPeriodAdapter adapter) : ControllerBase +{ + private readonly IPeriodAdapter _adapter = adapter; + + /// + /// получение всех записей сроков + /// + /// список сроков + [HttpGet] + public IActionResult GetAllRecords() + { + return _adapter.GetList().GetResponse(Request, Response); + } + + /// + /// получние записи о сроке по данным + /// + /// уникальный идентификатор или другое поле + /// запись срока + [HttpGet("{data}")] + public IActionResult GetRecord(string data) + { + return _adapter.GetElement(data).GetResponse(Request, Response); + } + + /// + /// получение записей сроков по уникальному идентификатору кладовщика + /// + /// уникальный идентификатор кладовщика + /// список сроков + [HttpGet("{data}")] + public IActionResult GetRecordByStorekeeper(string data) + { + return _adapter.GetListByStorekeeper(data).GetResponse(Request, Response); + } + + /// + /// получение записей сроков по дате начала + /// + /// дата начала + /// список сроков + [HttpGet("{data}")] + public IActionResult GetRecordByStartTime(DateTime data) + { + return _adapter.GetListByStartTime(data).GetResponse(Request, Response); + } + + /// + /// получение записей сроков по дате конца + /// + /// дата конца + /// список сроков + [HttpGet("{data}")] + public IActionResult GetRecordByEndTime(DateTime data) + { + return _adapter.GetListByEndTime(data).GetResponse(Request, Response); + } + + /// + /// создание записи срока + /// + /// модель от пользователя + /// + [HttpPost] + public IActionResult Register([FromBody] PeriodBindingModel model) + { + return _adapter.RegisterPeriod(model).GetResponse(Request, Response); + } + + /// + /// изменение записи срока + /// + /// новая модель + /// + [HttpPut] + public IActionResult ChangeInfo([FromBody] PeriodBindingModel model) + { + return _adapter.ChangePeriodInfo(model).GetResponse(Request, Response); + } +} diff --git a/TheBank/BankWebApi/Program.cs b/TheBank/BankWebApi/Program.cs index 5e1dc63..2423f30 100644 --- a/TheBank/BankWebApi/Program.cs +++ b/TheBank/BankWebApi/Program.cs @@ -86,11 +86,15 @@ builder.Services.AddOpenApi(); builder.Services.AddSingleton(); // builder.Services.AddTransient(); +builder.Services.AddTransient(); // builder.Services.AddTransient(); builder.Services.AddTransient(); +builder.Services.AddTransient(); // builder.Services.AddTransient(); +builder.Services.AddTransient(); + var app = builder.Build(); // Configure the HTTP request pipeline. From d6ded9f5e0f08607b52416707181cec3d0b946fb Mon Sep 17 00:00:00 2001 From: xom9k Date: Fri, 2 May 2025 12:36:22 +0400 Subject: [PATCH 06/11] =?UTF-8?q?fix:=20=D1=83=D0=B1=D1=80=D0=B0=D0=BB=20?= =?UTF-8?q?=D0=BD=D0=B5=20=D0=BD=D1=83=D0=B6=D0=BD=D1=8B=D0=B5=20=D1=8E?= =?UTF-8?q?=D0=B7=D0=B8=D0=BD=D0=B3=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TheBank/BankDatabase/Models/DepositCurrency.cs | 8 +------- TheBank/BankDatabase/Models/Storekeeper.cs | 3 +-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/TheBank/BankDatabase/Models/DepositCurrency.cs b/TheBank/BankDatabase/Models/DepositCurrency.cs index b086970..c6dae95 100644 --- a/TheBank/BankDatabase/Models/DepositCurrency.cs +++ b/TheBank/BankDatabase/Models/DepositCurrency.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BankDatabase.Models; +namespace BankDatabase.Models; class DepositCurrency { diff --git a/TheBank/BankDatabase/Models/Storekeeper.cs b/TheBank/BankDatabase/Models/Storekeeper.cs index 466bbc7..0ff6863 100644 --- a/TheBank/BankDatabase/Models/Storekeeper.cs +++ b/TheBank/BankDatabase/Models/Storekeeper.cs @@ -1,5 +1,4 @@ -using BankContracts.DataModels; -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations.Schema; namespace BankDatabase.Models; From d827ade7635532cccb9b73e22325264963ad5904 Mon Sep 17 00:00:00 2001 From: xom9k Date: Fri, 2 May 2025 14:45:07 +0400 Subject: [PATCH 07/11] =?UTF-8?q?feat:=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D0=B8,=20=D0=B0=D0=B4=D0=B0=D0=BF=D1=82=D0=B5=D1=80=D1=8B,=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D1=82=D1=80=D0=BE=D0=BB=D0=BB=D0=B5=D1=80?= =?UTF-8?q?=D1=8B=20=D0=B4=D0=BB=D1=8F=20=D0=BA=D0=BB=D0=B8=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B0,=20=D0=B2=D0=BA=D0=BB=D0=B0=D0=B4=D0=B0,=20=D0=BA?= =?UTF-8?q?=D1=80=D0=B5=D0=B4=D0=B8=D1=82=D0=BD=D0=BE=D0=B9=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B3=D1=80=D0=B0=D0=BC=D0=BC=D1=8B.=20=D0=BC=D0=BE?= =?UTF-8?q?=D0=B4=D0=B5=D0=BB=D0=B8=20=D0=B4=D0=BB=D1=8F=20=D1=81=D0=B2?= =?UTF-8?q?=D1=8F=D0=B7=D0=B5=D0=B9=20=D0=BC=D0=BD=D0=BE=D0=B3=D0=B8=D0=B5?= =?UTF-8?q?=20=D0=BA=D0=BE=20=D0=BC=D0=BD=D0=BE=D0=B3=D0=B8=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AdapterContracts/IClientAdapter.cs | 25 ++ .../AdapterContracts/ICreditProgramAdapter.cs | 19 ++ .../AdapterContracts/IDepositAdapter.cs | 20 ++ .../ClientOperationResponse.cs | 29 +++ .../CreditProgramOperationResponse.cs | 24 ++ .../DepositOperationResponse.cs | 24 ++ .../BindingModels/ClientBindingModel.cs | 19 ++ .../ClientCreditProgramBindingModel.cs | 8 + .../CreditProgramBindingModel.cs | 18 ++ .../CreditProgramCurrencyBindingModel.cs | 8 + .../BindingModels/CurrencyBindingModel.cs | 5 + .../BindingModels/DepositBindingModel.cs | 19 ++ .../DepositClientBindingModel.cs | 8 + .../DepositCurrencyBindingModel.cs | 8 + .../ClientCreditProgramViewModel.cs | 8 + .../ViewModels/ClientViewModel.cs | 18 ++ .../CreditProgramCurrencyViewModel.cs | 8 + .../ViewModels/CreditProgramViewModel.cs | 21 ++ .../ViewModels/CurrencyViewModel.cs | 5 + .../ViewModels/DepositClientViewModel.cs | 8 + .../ViewModels/DepositCurrencyViewModel.cs | 8 + .../ViewModels/DepositViewModel.cs | 20 ++ TheBank/BankWebApi/Adapters/ClientAdapter.cs | 209 +++++++++++++++ .../Adapters/CreditProgramAdapter.cs | 238 ++++++++++++++++++ TheBank/BankWebApi/Adapters/DepositAdapter.cs | 203 +++++++++++++++ .../Controllers/ClientsController.cs | 69 +++++ .../Controllers/CreditProgramsController.cs | 80 ++++++ .../Controllers/DepositsController.cs | 69 +++++ TheBank/BankWebApi/Program.cs | 9 + 29 files changed, 1207 insertions(+) create mode 100644 TheBank/BankContracts/AdapterContracts/IClientAdapter.cs create mode 100644 TheBank/BankContracts/AdapterContracts/ICreditProgramAdapter.cs create mode 100644 TheBank/BankContracts/AdapterContracts/IDepositAdapter.cs create mode 100644 TheBank/BankContracts/AdapterContracts/OperationResponses/ClientOperationResponse.cs create mode 100644 TheBank/BankContracts/AdapterContracts/OperationResponses/CreditProgramOperationResponse.cs create mode 100644 TheBank/BankContracts/AdapterContracts/OperationResponses/DepositOperationResponse.cs create mode 100644 TheBank/BankContracts/BindingModels/ClientBindingModel.cs create mode 100644 TheBank/BankContracts/BindingModels/ClientCreditProgramBindingModel.cs create mode 100644 TheBank/BankContracts/BindingModels/CreditProgramBindingModel.cs create mode 100644 TheBank/BankContracts/BindingModels/CreditProgramCurrencyBindingModel.cs create mode 100644 TheBank/BankContracts/BindingModels/CurrencyBindingModel.cs create mode 100644 TheBank/BankContracts/BindingModels/DepositBindingModel.cs create mode 100644 TheBank/BankContracts/BindingModels/DepositClientBindingModel.cs create mode 100644 TheBank/BankContracts/BindingModels/DepositCurrencyBindingModel.cs create mode 100644 TheBank/BankContracts/ViewModels/ClientCreditProgramViewModel.cs create mode 100644 TheBank/BankContracts/ViewModels/ClientViewModel.cs create mode 100644 TheBank/BankContracts/ViewModels/CreditProgramCurrencyViewModel.cs create mode 100644 TheBank/BankContracts/ViewModels/CreditProgramViewModel.cs create mode 100644 TheBank/BankContracts/ViewModels/CurrencyViewModel.cs create mode 100644 TheBank/BankContracts/ViewModels/DepositClientViewModel.cs create mode 100644 TheBank/BankContracts/ViewModels/DepositCurrencyViewModel.cs create mode 100644 TheBank/BankContracts/ViewModels/DepositViewModel.cs create mode 100644 TheBank/BankWebApi/Adapters/ClientAdapter.cs create mode 100644 TheBank/BankWebApi/Adapters/CreditProgramAdapter.cs create mode 100644 TheBank/BankWebApi/Adapters/DepositAdapter.cs create mode 100644 TheBank/BankWebApi/Controllers/ClientsController.cs create mode 100644 TheBank/BankWebApi/Controllers/CreditProgramsController.cs create mode 100644 TheBank/BankWebApi/Controllers/DepositsController.cs diff --git a/TheBank/BankContracts/AdapterContracts/IClientAdapter.cs b/TheBank/BankContracts/AdapterContracts/IClientAdapter.cs new file mode 100644 index 0000000..8d07ceb --- /dev/null +++ b/TheBank/BankContracts/AdapterContracts/IClientAdapter.cs @@ -0,0 +1,25 @@ +using BankContracts.AdapterContracts.OperationResponses; +using BankContracts.BindingModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BankContracts.AdapterContracts; + +/// +/// контракт адаптера для клиента +/// +public interface IClientAdapter +{ + ClientOperationResponse GetList(); + + ClientOperationResponse GetElement(string data); + + ClientOperationResponse GetListByClerk(string clerkId); + + ClientOperationResponse RegisterClient(ClientBindingModel clientModel); + + ClientOperationResponse ChangeClientInfo(ClientBindingModel clientModel); +} diff --git a/TheBank/BankContracts/AdapterContracts/ICreditProgramAdapter.cs b/TheBank/BankContracts/AdapterContracts/ICreditProgramAdapter.cs new file mode 100644 index 0000000..bedb1f7 --- /dev/null +++ b/TheBank/BankContracts/AdapterContracts/ICreditProgramAdapter.cs @@ -0,0 +1,19 @@ +using BankContracts.AdapterContracts.OperationResponses; +using BankContracts.BindingModels; + +namespace BankContracts.AdapterContracts; + +public interface ICreditProgramAdapter +{ + CreditProgramOperationResponse GetList(); + + CreditProgramOperationResponse GetElement(string data); + + CreditProgramOperationResponse GetListByStorekeeper(string storekeeperId); + + CreditProgramOperationResponse GetListByPeriod(string periodId); + + CreditProgramOperationResponse RegisterCreditProgram(CreditProgramBindingModel creditProgramModel); + + CreditProgramOperationResponse ChangeCreditProgramInfo(CreditProgramBindingModel creditProgramModel); +} diff --git a/TheBank/BankContracts/AdapterContracts/IDepositAdapter.cs b/TheBank/BankContracts/AdapterContracts/IDepositAdapter.cs new file mode 100644 index 0000000..6d61124 --- /dev/null +++ b/TheBank/BankContracts/AdapterContracts/IDepositAdapter.cs @@ -0,0 +1,20 @@ +using BankContracts.AdapterContracts.OperationResponses; +using BankContracts.BindingModels; + +namespace BankContracts.AdapterContracts; + +/// +/// контракт адаптера для вклада +/// +public interface IDepositAdapter +{ + DepositOperationResponse GetList(); + + DepositOperationResponse GetElement(string data); + + DepositOperationResponse GetListByClerk(string clerkId); + + DepositOperationResponse MakeDeposit(DepositBindingModel depositModel); + + DepositOperationResponse ChangeDepositInfo(DepositBindingModel depositModel); +} diff --git a/TheBank/BankContracts/AdapterContracts/OperationResponses/ClientOperationResponse.cs b/TheBank/BankContracts/AdapterContracts/OperationResponses/ClientOperationResponse.cs new file mode 100644 index 0000000..7967c25 --- /dev/null +++ b/TheBank/BankContracts/AdapterContracts/OperationResponses/ClientOperationResponse.cs @@ -0,0 +1,29 @@ +using BankContracts.Infrastructure; +using BankContracts.ViewModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BankContracts.AdapterContracts.OperationResponses; + +public class ClientOperationResponse : OperationResponse +{ + public static ClientOperationResponse OK(List data) => + OK>(data); + + public static ClientOperationResponse OK(ClientViewModel data) => + OK(data); + + public static ClientOperationResponse NoContent() => NoContent(); + + public static ClientOperationResponse NotFound(string message) => + NotFound(message); + + public static ClientOperationResponse BadRequest(string message) => + BadRequest(message); + + public static ClientOperationResponse InternalServerError(string message) => + InternalServerError(message); +} diff --git a/TheBank/BankContracts/AdapterContracts/OperationResponses/CreditProgramOperationResponse.cs b/TheBank/BankContracts/AdapterContracts/OperationResponses/CreditProgramOperationResponse.cs new file mode 100644 index 0000000..efb1a28 --- /dev/null +++ b/TheBank/BankContracts/AdapterContracts/OperationResponses/CreditProgramOperationResponse.cs @@ -0,0 +1,24 @@ +using BankContracts.Infrastructure; +using BankContracts.ViewModels; + +namespace BankContracts.AdapterContracts.OperationResponses; + +public class CreditProgramOperationResponse : OperationResponse +{ + public static CreditProgramOperationResponse OK(List data) => + OK>(data); + + public static CreditProgramOperationResponse OK(CreditProgramViewModel data) => + OK(data); + + public static CreditProgramOperationResponse NoContent() => NoContent(); + + public static CreditProgramOperationResponse NotFound(string message) => + NotFound(message); + + public static CreditProgramOperationResponse BadRequest(string message) => + BadRequest(message); + + public static CreditProgramOperationResponse InternalServerError(string message) => + InternalServerError(message); +} diff --git a/TheBank/BankContracts/AdapterContracts/OperationResponses/DepositOperationResponse.cs b/TheBank/BankContracts/AdapterContracts/OperationResponses/DepositOperationResponse.cs new file mode 100644 index 0000000..1dfb7ba --- /dev/null +++ b/TheBank/BankContracts/AdapterContracts/OperationResponses/DepositOperationResponse.cs @@ -0,0 +1,24 @@ +using BankContracts.Infrastructure; +using BankContracts.ViewModels; + +namespace BankContracts.AdapterContracts.OperationResponses; + +public class DepositOperationResponse : OperationResponse +{ + public static DepositOperationResponse OK(List data) => + OK>(data); + + public static DepositOperationResponse OK(DepositViewModel data) => + OK(data); + + public static DepositOperationResponse NoContent() => NoContent(); + + public static DepositOperationResponse NotFound(string message) => + NotFound(message); + + public static DepositOperationResponse BadRequest(string message) => + BadRequest(message); + + public static DepositOperationResponse InternalServerError(string message) => + InternalServerError(message); +} diff --git a/TheBank/BankContracts/BindingModels/ClientBindingModel.cs b/TheBank/BankContracts/BindingModels/ClientBindingModel.cs new file mode 100644 index 0000000..b33edfe --- /dev/null +++ b/TheBank/BankContracts/BindingModels/ClientBindingModel.cs @@ -0,0 +1,19 @@ +using BankContracts.ViewModels; +namespace BankContracts.BindingModels; + +public class ClientBindingModel +{ + public string? Id { get; set; } + + public string? Name { get; set; } + + public string? Surname { get; set; } + + public decimal Balance { get; set; } + + public string? ClerkId { get; set; } + + public List? DepositClients { get; set; } + + public List? CreditProgramClients { get; set; } +} diff --git a/TheBank/BankContracts/BindingModels/ClientCreditProgramBindingModel.cs b/TheBank/BankContracts/BindingModels/ClientCreditProgramBindingModel.cs new file mode 100644 index 0000000..961c261 --- /dev/null +++ b/TheBank/BankContracts/BindingModels/ClientCreditProgramBindingModel.cs @@ -0,0 +1,8 @@ +namespace BankContracts.BindingModels; + +public class ClientCreditProgramBindingModel +{ + public string? CreditProgramId { get; set; } + + public string? ClientId { get; set; } +} \ No newline at end of file diff --git a/TheBank/BankContracts/BindingModels/CreditProgramBindingModel.cs b/TheBank/BankContracts/BindingModels/CreditProgramBindingModel.cs new file mode 100644 index 0000000..23c8021 --- /dev/null +++ b/TheBank/BankContracts/BindingModels/CreditProgramBindingModel.cs @@ -0,0 +1,18 @@ +namespace BankContracts.BindingModels; + +public class CreditProgramBindingModel +{ + public required string Id { get; set; } + + public required string Name { get; set; } + + public decimal Cost { get; set; } + + public decimal MaxCost { get; set; } + + public required string StorekeeperId { get; set; } + + public required string PeriodId { get; set; } + + public List? CurrencyCreditPrograms { get; set; } +} diff --git a/TheBank/BankContracts/BindingModels/CreditProgramCurrencyBindingModel.cs b/TheBank/BankContracts/BindingModels/CreditProgramCurrencyBindingModel.cs new file mode 100644 index 0000000..3d560b6 --- /dev/null +++ b/TheBank/BankContracts/BindingModels/CreditProgramCurrencyBindingModel.cs @@ -0,0 +1,8 @@ +namespace BankContracts.BindingModels; + +public class CreditProgramCurrencyBindingModel +{ + public string? CreditProgramId { get; set; } + + public string? CurrencyId { get; set; } +} diff --git a/TheBank/BankContracts/BindingModels/CurrencyBindingModel.cs b/TheBank/BankContracts/BindingModels/CurrencyBindingModel.cs new file mode 100644 index 0000000..2c3699e --- /dev/null +++ b/TheBank/BankContracts/BindingModels/CurrencyBindingModel.cs @@ -0,0 +1,5 @@ +namespace BankContracts.BindingModels; + +public class CurrencyBindingModel +{ +} diff --git a/TheBank/BankContracts/BindingModels/DepositBindingModel.cs b/TheBank/BankContracts/BindingModels/DepositBindingModel.cs new file mode 100644 index 0000000..6619a42 --- /dev/null +++ b/TheBank/BankContracts/BindingModels/DepositBindingModel.cs @@ -0,0 +1,19 @@ +using BankContracts.ViewModels; + +namespace BankContracts.BindingModels; + + +public class DepositBindingModel +{ + public string? Id { get; set; } + + public float InterestRate { get; set; } + + public decimal Cost { get; set; } + + public int Period { get; set; } + + public string? ClerkId { get; set; } + + public List? DepositClients { get; set; } +} diff --git a/TheBank/BankContracts/BindingModels/DepositClientBindingModel.cs b/TheBank/BankContracts/BindingModels/DepositClientBindingModel.cs new file mode 100644 index 0000000..447bec8 --- /dev/null +++ b/TheBank/BankContracts/BindingModels/DepositClientBindingModel.cs @@ -0,0 +1,8 @@ +namespace BankContracts.BindingModels; + +public class DepositClientBindingModel +{ + public string? DepositId { get; set; } + + public string? ClientId { get; set; } +} diff --git a/TheBank/BankContracts/BindingModels/DepositCurrencyBindingModel.cs b/TheBank/BankContracts/BindingModels/DepositCurrencyBindingModel.cs new file mode 100644 index 0000000..29ce546 --- /dev/null +++ b/TheBank/BankContracts/BindingModels/DepositCurrencyBindingModel.cs @@ -0,0 +1,8 @@ +namespace BankContracts.BindingModels; + +public class DepositCurrencyBindingModel +{ + public string? DepositId { get; set; } + + public string? CurrencyId { get; set; } +} diff --git a/TheBank/BankContracts/ViewModels/ClientCreditProgramViewModel.cs b/TheBank/BankContracts/ViewModels/ClientCreditProgramViewModel.cs new file mode 100644 index 0000000..84d3263 --- /dev/null +++ b/TheBank/BankContracts/ViewModels/ClientCreditProgramViewModel.cs @@ -0,0 +1,8 @@ +namespace BankContracts.ViewModels; + +public class ClientCreditProgramViewModel +{ + public required string? CreditProgramId { get; set; } + + public required string? ClientId { get; set; } +} \ No newline at end of file diff --git a/TheBank/BankContracts/ViewModels/ClientViewModel.cs b/TheBank/BankContracts/ViewModels/ClientViewModel.cs new file mode 100644 index 0000000..1293fc8 --- /dev/null +++ b/TheBank/BankContracts/ViewModels/ClientViewModel.cs @@ -0,0 +1,18 @@ +namespace BankContracts.ViewModels; + +public class ClientViewModel +{ + public required string Id { get; set; } + + public required string Name { get; set; } + + public required string Surname { get; set; } + + public required decimal Balance { get; set; } + + public required string ClerkId { get; set; } + + public required List DepositClients { get; set; } + + public required List? CreditProgramClients { get; set; } +} diff --git a/TheBank/BankContracts/ViewModels/CreditProgramCurrencyViewModel.cs b/TheBank/BankContracts/ViewModels/CreditProgramCurrencyViewModel.cs new file mode 100644 index 0000000..d07e2dd --- /dev/null +++ b/TheBank/BankContracts/ViewModels/CreditProgramCurrencyViewModel.cs @@ -0,0 +1,8 @@ +namespace BankContracts.ViewModels; + +public class CreditProgramCurrencyViewModel +{ + public required string CreditProgramId { get; set; } + + public required string CurrencyId { get; set; } +} \ No newline at end of file diff --git a/TheBank/BankContracts/ViewModels/CreditProgramViewModel.cs b/TheBank/BankContracts/ViewModels/CreditProgramViewModel.cs new file mode 100644 index 0000000..8cd63b3 --- /dev/null +++ b/TheBank/BankContracts/ViewModels/CreditProgramViewModel.cs @@ -0,0 +1,21 @@ +namespace BankContracts.ViewModels; + +/// +/// модель представления для кредитной программы +/// +public class CreditProgramViewModel +{ + public required string Id { get; set; } + + public required string Name { get; set; } + + public decimal Cost { get; set; } + + public decimal MaxCost { get; set; } + + public required string StorekeeperId { get; set; } + + public required string PeriodId { get; set; } + + public required List? CurrencyCreditPrograms { get; set; } +} diff --git a/TheBank/BankContracts/ViewModels/CurrencyViewModel.cs b/TheBank/BankContracts/ViewModels/CurrencyViewModel.cs new file mode 100644 index 0000000..2ddd3c8 --- /dev/null +++ b/TheBank/BankContracts/ViewModels/CurrencyViewModel.cs @@ -0,0 +1,5 @@ +namespace BankContracts.ViewModels; + +public class CurrencyViewModel +{ +} diff --git a/TheBank/BankContracts/ViewModels/DepositClientViewModel.cs b/TheBank/BankContracts/ViewModels/DepositClientViewModel.cs new file mode 100644 index 0000000..761c2cf --- /dev/null +++ b/TheBank/BankContracts/ViewModels/DepositClientViewModel.cs @@ -0,0 +1,8 @@ +namespace BankContracts.ViewModels; + +public class DepositClientViewModel +{ + public required string DepositId { get; set; } + + public required string ClientId { get; set; } +} \ No newline at end of file diff --git a/TheBank/BankContracts/ViewModels/DepositCurrencyViewModel.cs b/TheBank/BankContracts/ViewModels/DepositCurrencyViewModel.cs new file mode 100644 index 0000000..28b6641 --- /dev/null +++ b/TheBank/BankContracts/ViewModels/DepositCurrencyViewModel.cs @@ -0,0 +1,8 @@ +namespace BankContracts.ViewModels; + +public class DepositCurrencyViewModel +{ + public required string DepositId { get; set; } + + public required string CurrencyId { get; set; } +} \ No newline at end of file diff --git a/TheBank/BankContracts/ViewModels/DepositViewModel.cs b/TheBank/BankContracts/ViewModels/DepositViewModel.cs new file mode 100644 index 0000000..22b4368 --- /dev/null +++ b/TheBank/BankContracts/ViewModels/DepositViewModel.cs @@ -0,0 +1,20 @@ +namespace BankContracts.ViewModels; + +/// +/// модель представления для вклада +/// +public class DepositViewModel +{ + public required string Id { get; set; } + + public required float InterestRate { get; set; } + + public required decimal Cost { get; set; } + + public required int Period { get; set; } + + public required string ClerkId { get; set; } + + public required List? DepositClients { get; set; } + +} diff --git a/TheBank/BankWebApi/Adapters/ClientAdapter.cs b/TheBank/BankWebApi/Adapters/ClientAdapter.cs new file mode 100644 index 0000000..ec0c141 --- /dev/null +++ b/TheBank/BankWebApi/Adapters/ClientAdapter.cs @@ -0,0 +1,209 @@ +using AutoMapper; +using BankBusinessLogic.Implementations; +using BankContracts.AdapterContracts; +using BankContracts.AdapterContracts.OperationResponses; +using BankContracts.BindingModels; +using BankContracts.BusinessLogicContracts; +using BankContracts.DataModels; +using BankContracts.Exceptions; +using BankContracts.ViewModels; + +namespace BankWebApi.Adapters; + +public class ClientAdapter : IClientAdapter +{ + private readonly IClientBusinessLogicContract _clientBusinessLogicContract; + + private readonly ILogger _logger; + + private readonly Mapper _mapper; + + public ClientAdapter(IClientBusinessLogicContract clientBusinessLogicContract, ILogger logger) + { + _clientBusinessLogicContract = clientBusinessLogicContract; + _logger = logger; + var config = new MapperConfiguration(cfg => + { + cfg.CreateMap(); + cfg.CreateMap(); + cfg.CreateMap(); + cfg.CreateMap(); + cfg.CreateMap(); + cfg.CreateMap(); + }); + _mapper = new Mapper(config); + } + + public ClientOperationResponse GetList() + { + try + { + return ClientOperationResponse.OK( + [ + .. _clientBusinessLogicContract + .GetAllClients() + .Select(x => _mapper.Map(x)), + ] + ); + } + catch (NullListException) + { + _logger.LogError("NullListException"); + return ClientOperationResponse.NotFound("The list is not initialized"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return ClientOperationResponse.InternalServerError( + $"Error while working with data storage:{ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return ClientOperationResponse.InternalServerError(ex.Message); + } + } + + public ClientOperationResponse GetElement(string data) + { + try + { + return ClientOperationResponse.OK( + _mapper.Map(_clientBusinessLogicContract.GetClientByData(data)) + ); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return ClientOperationResponse.BadRequest("Data is empty"); + } + catch (ElementNotFoundException ex) + { + _logger.LogError(ex, "ElementNotFoundException"); + return ClientOperationResponse.NotFound($"Not found element by data {data}"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return ClientOperationResponse.InternalServerError( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return ClientOperationResponse.InternalServerError(ex.Message); + } + } + + public ClientOperationResponse RegisterClient(ClientBindingModel clientModel) + { + try + { + _clientBusinessLogicContract.InsertClient(_mapper.Map(clientModel)); + return ClientOperationResponse.NoContent(); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return ClientOperationResponse.BadRequest("Data is empty"); + } + catch (ValidationException ex) + { + _logger.LogError(ex, "ValidationException"); + return ClientOperationResponse.BadRequest($"Incorrect data transmitted: {ex.Message}"); + } + catch (ElementExistsException ex) + { + _logger.LogError(ex, "ElementExistsException"); + return ClientOperationResponse.BadRequest(ex.Message); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return ClientOperationResponse.BadRequest( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return ClientOperationResponse.InternalServerError(ex.Message); + } + } + + public ClientOperationResponse ChangeClientInfo(ClientBindingModel clientModel) + { + try + { + _clientBusinessLogicContract.UpdateClient(_mapper.Map(clientModel)); + return ClientOperationResponse.NoContent(); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return ClientOperationResponse.BadRequest("Data is empty"); + } + catch (ValidationException ex) + { + _logger.LogError(ex, "ValidationException"); + return ClientOperationResponse.BadRequest($"Incorrect data transmitted: {ex.Message}"); + } + catch (ElementNotFoundException ex) + { + _logger.LogError(ex, "ElementNotFoundException"); + return ClientOperationResponse.BadRequest( + $"Not found element by Id {clientModel.Id}" + ); + } + catch (ElementExistsException ex) + { + _logger.LogError(ex, "ElementExistsException"); + return ClientOperationResponse.BadRequest(ex.Message); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return ClientOperationResponse.BadRequest( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return ClientOperationResponse.InternalServerError(ex.Message); + } + } + + public ClientOperationResponse GetListByClerk(string clerkId) + { + try + { + return ClientOperationResponse.OK( + [ + .. _clientBusinessLogicContract + .GetClientByClerk(clerkId) + .Select(x => _mapper.Map(x)), + ] + ); + } + catch (NullListException) + { + _logger.LogError("NullListException"); + return ClientOperationResponse.NotFound("The list is not initialized"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return ClientOperationResponse.InternalServerError( + $"Error while working with data storage:{ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return ClientOperationResponse.InternalServerError(ex.Message); + } + } +} diff --git a/TheBank/BankWebApi/Adapters/CreditProgramAdapter.cs b/TheBank/BankWebApi/Adapters/CreditProgramAdapter.cs new file mode 100644 index 0000000..e752285 --- /dev/null +++ b/TheBank/BankWebApi/Adapters/CreditProgramAdapter.cs @@ -0,0 +1,238 @@ +using AutoMapper; +using BankBusinessLogic.Implementations; +using BankContracts.AdapterContracts; +using BankContracts.AdapterContracts.OperationResponses; +using BankContracts.BindingModels; +using BankContracts.BusinessLogicContracts; +using BankContracts.DataModels; +using BankContracts.Exceptions; +using BankContracts.ViewModels; +using BankDatabase.Models; + +namespace BankWebApi.Adapters; + +public class CreditProgramAdapter : ICreditProgramAdapter +{ + private readonly ICreditProgramBusinessLogicContract _creditProgramBusinessLogicContract; + + private readonly ILogger _logger; + + private readonly Mapper _mapper; + + public CreditProgramAdapter(ICreditProgramBusinessLogicContract creditProgramBusinessLogicContract, ILogger logger) + { + _creditProgramBusinessLogicContract = creditProgramBusinessLogicContract; + _logger = logger; + var config = new MapperConfiguration(cfg => + { + cfg.CreateMap(); + cfg.CreateMap(); + cfg.CreateMap(); + cfg.CreateMap(); + }); + _mapper = new Mapper(config); + } + + public CreditProgramOperationResponse GetList() + { + try + { + return CreditProgramOperationResponse.OK( + [ + .. _creditProgramBusinessLogicContract + .GetAllCreditPrograms() + .Select(x => _mapper.Map(x)), + ] + ); + } + catch (NullListException) + { + _logger.LogError("NullListException"); + return CreditProgramOperationResponse.NotFound("The list is not initialized"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return CreditProgramOperationResponse.InternalServerError( + $"Error while working with data storage:{ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return CreditProgramOperationResponse.InternalServerError(ex.Message); + } + } + + public CreditProgramOperationResponse GetElement(string data) + { + try + { + return CreditProgramOperationResponse.OK( + _mapper.Map(_creditProgramBusinessLogicContract.GetCreditProgramByData(data)) + ); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return CreditProgramOperationResponse.BadRequest("Data is empty"); + } + catch (ElementNotFoundException ex) + { + _logger.LogError(ex, "ElementNotFoundException"); + return CreditProgramOperationResponse.NotFound($"Not found element by data {data}"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return CreditProgramOperationResponse.InternalServerError( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return CreditProgramOperationResponse.InternalServerError(ex.Message); + } + } + + public CreditProgramOperationResponse RegisterCreditProgram(CreditProgramBindingModel creditProgramModel) + { + try + { + _creditProgramBusinessLogicContract.InsertCreditProgram(_mapper.Map(creditProgramModel)); + return CreditProgramOperationResponse.NoContent(); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return CreditProgramOperationResponse.BadRequest("Data is empty"); + } + catch (ValidationException ex) + { + _logger.LogError(ex, "ValidationException"); + return CreditProgramOperationResponse.BadRequest($"Incorrect data transmitted: {ex.Message}"); + } + catch (ElementExistsException ex) + { + _logger.LogError(ex, "ElementExistsException"); + return CreditProgramOperationResponse.BadRequest(ex.Message); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return CreditProgramOperationResponse.BadRequest( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return CreditProgramOperationResponse.InternalServerError(ex.Message); + } + } + public CreditProgramOperationResponse ChangeCreditProgramInfo(CreditProgramBindingModel creditProgramModel) + { + try + { + _creditProgramBusinessLogicContract.UpdateCreditProgram(_mapper.Map(creditProgramModel)); + return CreditProgramOperationResponse.NoContent(); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return CreditProgramOperationResponse.BadRequest("Data is empty"); + } + catch (ValidationException ex) + { + _logger.LogError(ex, "ValidationException"); + return CreditProgramOperationResponse.BadRequest($"Incorrect data transmitted: {ex.Message}"); + } + catch (ElementNotFoundException ex) + { + _logger.LogError(ex, "ElementNotFoundException"); + return CreditProgramOperationResponse.BadRequest( + $"Not found element by Id {creditProgramModel.Id}" + ); + } + catch (ElementExistsException ex) + { + _logger.LogError(ex, "ElementExistsException"); + return CreditProgramOperationResponse.BadRequest(ex.Message); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return CreditProgramOperationResponse.BadRequest( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return CreditProgramOperationResponse.InternalServerError(ex.Message); + } + } + + public CreditProgramOperationResponse GetListByPeriod(string periodId) + { + try + { + return CreditProgramOperationResponse.OK( + [ + .. _creditProgramBusinessLogicContract + .GetCreditProgramByPeriod(periodId) + .Select(x => _mapper.Map(x)), + ] + ); + } + catch (NullListException) + { + _logger.LogError("NullListException"); + return CreditProgramOperationResponse.NotFound("The list is not initialized"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return CreditProgramOperationResponse.InternalServerError( + $"Error while working with data storage:{ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return CreditProgramOperationResponse.InternalServerError(ex.Message); + } + } + + public CreditProgramOperationResponse GetListByStorekeeper(string storekeeperId) + { + try + { + return CreditProgramOperationResponse.OK( + [ + .. _creditProgramBusinessLogicContract + .GetCreditProgramByStorekeeper(storekeeperId) + .Select(x => _mapper.Map(x)), + ] + ); + } + catch (NullListException) + { + _logger.LogError("NullListException"); + return CreditProgramOperationResponse.NotFound("The list is not initialized"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return CreditProgramOperationResponse.InternalServerError( + $"Error while working with data storage:{ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return CreditProgramOperationResponse.InternalServerError(ex.Message); + } + } +} diff --git a/TheBank/BankWebApi/Adapters/DepositAdapter.cs b/TheBank/BankWebApi/Adapters/DepositAdapter.cs new file mode 100644 index 0000000..5f01544 --- /dev/null +++ b/TheBank/BankWebApi/Adapters/DepositAdapter.cs @@ -0,0 +1,203 @@ +using AutoMapper; +using BankContracts.AdapterContracts; +using BankContracts.AdapterContracts.OperationResponses; +using BankContracts.BindingModels; +using BankContracts.BusinessLogicContracts; +using BankContracts.DataModels; +using BankContracts.Exceptions; +using BankContracts.ViewModels; + +namespace BankWebApi.Adapters; + +public class DepositAdapter : IDepositAdapter +{ + private readonly IDepositBusinessLogicContract _depositBusinessLogicContract; + + private readonly ILogger _logger; + + private readonly Mapper _mapper; + + public DepositAdapter(IDepositBusinessLogicContract depositBusinessLogicContract, ILogger logger) + { + _depositBusinessLogicContract = depositBusinessLogicContract; + _logger = logger; + var config = new MapperConfiguration(cfg => + { + cfg.CreateMap(); + cfg.CreateMap(); + cfg.CreateMap(); + cfg.CreateMap(); + }); + _mapper = new Mapper(config); + } + public DepositOperationResponse GetList() + { + try + { + return DepositOperationResponse.OK( + [ + .. _depositBusinessLogicContract + .GetAllDeposits() + .Select(x => _mapper.Map(x)), + ] + ); + } + catch (NullListException) + { + _logger.LogError("NullListException"); + return DepositOperationResponse.NotFound("The list is not initialized"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return DepositOperationResponse.InternalServerError( + $"Error while working with data storage:{ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return DepositOperationResponse.InternalServerError(ex.Message); + } + } + public DepositOperationResponse GetElement(string data) + { + try + { + return DepositOperationResponse.OK( + _mapper.Map(_depositBusinessLogicContract.GetDepositByData(data)) + ); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return DepositOperationResponse.BadRequest("Data is empty"); + } + catch (ElementNotFoundException ex) + { + _logger.LogError(ex, "ElementNotFoundException"); + return DepositOperationResponse.NotFound($"Not found element by data {data}"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return DepositOperationResponse.InternalServerError( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return DepositOperationResponse.InternalServerError(ex.Message); + } + } + public DepositOperationResponse MakeDeposit(DepositBindingModel depositModel) + { + try + { + _depositBusinessLogicContract.InsertDeposit(_mapper.Map(depositModel)); + return DepositOperationResponse.NoContent(); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return DepositOperationResponse.BadRequest("Data is empty"); + } + catch (ValidationException ex) + { + _logger.LogError(ex, "ValidationException"); + return DepositOperationResponse.BadRequest($"Incorrect data transmitted: {ex.Message}"); + } + catch (ElementExistsException ex) + { + _logger.LogError(ex, "ElementExistsException"); + return DepositOperationResponse.BadRequest(ex.Message); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return DepositOperationResponse.BadRequest( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return DepositOperationResponse.InternalServerError(ex.Message); + } + } + + public DepositOperationResponse ChangeDepositInfo(DepositBindingModel depositModel) + { + try + { + _depositBusinessLogicContract.UpdateDeposit(_mapper.Map(depositModel)); + return DepositOperationResponse.NoContent(); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return DepositOperationResponse.BadRequest("Data is empty"); + } + catch (ValidationException ex) + { + _logger.LogError(ex, "ValidationException"); + return DepositOperationResponse.BadRequest($"Incorrect data transmitted: {ex.Message}"); + } + catch (ElementNotFoundException ex) + { + _logger.LogError(ex, "ElementNotFoundException"); + return DepositOperationResponse.BadRequest( + $"Not found element by Id {depositModel.Id}" + ); + } + catch (ElementExistsException ex) + { + _logger.LogError(ex, "ElementExistsException"); + return DepositOperationResponse.BadRequest(ex.Message); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return DepositOperationResponse.BadRequest( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return DepositOperationResponse.InternalServerError(ex.Message); + } + } + + public DepositOperationResponse GetListByClerk(string clerkId) + { + try + { + return DepositOperationResponse.OK( + [ + .. _depositBusinessLogicContract + .GetDepositByClerk(clerkId) + .Select(x => _mapper.Map(x)), + ] + ); + } + catch (NullListException) + { + _logger.LogError("NullListException"); + return DepositOperationResponse.NotFound("The list is not initialized"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return DepositOperationResponse.InternalServerError( + $"Error while working with data storage:{ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return DepositOperationResponse.InternalServerError(ex.Message); + } + } +} diff --git a/TheBank/BankWebApi/Controllers/ClientsController.cs b/TheBank/BankWebApi/Controllers/ClientsController.cs new file mode 100644 index 0000000..c48839d --- /dev/null +++ b/TheBank/BankWebApi/Controllers/ClientsController.cs @@ -0,0 +1,69 @@ +using BankContracts.AdapterContracts; +using BankContracts.BindingModels; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace BankWebApi.Controllers; + +[Authorize] +[Route("api/[controller]/[action]")] +[ApiController] +[Produces("application/json")] +public class ClientsController(IClientAdapter adapter) : ControllerBase +{ + private readonly IClientAdapter _adapter = adapter; + + /// + /// получение всех записей клиентов + /// + /// список клиентов + [HttpGet] + public IActionResult GetAllRecords() + { + return _adapter.GetList().GetResponse(Request, Response); + } + + /// + /// получние записи о клиенте по данным + /// + /// уникальный идентификатор или другое поле + /// запись клиента + [HttpGet("{data}")] + public IActionResult GetRecord(string data) + { + return _adapter.GetElement(data).GetResponse(Request, Response); + } + + /// + /// получение записей клиента по уникальному идентификатору клерка + /// + /// уникальный идентификатор клерка + /// список клиентов + [HttpGet("{data}")] + public IActionResult GetRecordByClerk(string data) + { + return _adapter.GetListByClerk(data).GetResponse(Request, Response); + } + + /// + /// создание записи клиента + /// + /// модель от пользователя + /// + [HttpPost] + public IActionResult Register([FromBody] ClientBindingModel model) + { + return _adapter.RegisterClient(model).GetResponse(Request, Response); + } + + /// + /// изменение записи клиента + /// + /// новая модель + /// + [HttpPut] + public IActionResult ChangeInfo([FromBody] ClientBindingModel model) + { + return _adapter.ChangeClientInfo(model).GetResponse(Request, Response); + } +} diff --git a/TheBank/BankWebApi/Controllers/CreditProgramsController.cs b/TheBank/BankWebApi/Controllers/CreditProgramsController.cs new file mode 100644 index 0000000..da7ca35 --- /dev/null +++ b/TheBank/BankWebApi/Controllers/CreditProgramsController.cs @@ -0,0 +1,80 @@ +using BankContracts.AdapterContracts; +using BankContracts.BindingModels; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace BankWebApi.Controllers; + +[Authorize] +[Route("api/[controller]/[action]")] +[ApiController] +[Produces("application/json")] +public class CreditProgramsController(ICreditProgramAdapter adapter) : ControllerBase +{ + private readonly ICreditProgramAdapter _adapter = adapter; + + /// + /// получение всех записей кредитных программ + /// + /// список кредитных программ + [HttpGet] + public IActionResult GetAllRecords() + { + return _adapter.GetList().GetResponse(Request, Response); + } + + /// + /// получние записи о кредитной программе по данным + /// + /// уникальный идентификатор или другое поле + /// запись кредитной программы + [HttpGet("{data}")] + public IActionResult GetRecord(string data) + { + return _adapter.GetElement(data).GetResponse(Request, Response); + } + + /// + /// получение записей кредитных программ по уникальному идентификатору кладовщика + /// + /// уникальный идентификатор кладовщика + /// список кредитных программ + [HttpGet("{data}")] + public IActionResult GetRecordByStorekeeper(string data) + { + return _adapter.GetListByStorekeeper(data).GetResponse(Request, Response); + } + + /// + /// получение записей кредитных программ по уникальному идентификатору периода + /// + /// уникальный идентификатор периода + /// список кредитных программ + [HttpGet("{data}")] + public IActionResult GetRecordByPeriod(string data) + { + return _adapter.GetListByPeriod(data).GetResponse(Request, Response); + } + + /// + /// создание записи кредитной программы + /// + /// модель от пользователя + /// + [HttpPost] + public IActionResult Register([FromBody] CreditProgramBindingModel model) + { + return _adapter.RegisterCreditProgram(model).GetResponse(Request, Response); + } + + /// + /// изменение записи кредитной программы + /// + /// новая модель + /// + [HttpPut] + public IActionResult ChangeInfo([FromBody] CreditProgramBindingModel model) + { + return _adapter.ChangeCreditProgramInfo(model).GetResponse(Request, Response); + } +} diff --git a/TheBank/BankWebApi/Controllers/DepositsController.cs b/TheBank/BankWebApi/Controllers/DepositsController.cs new file mode 100644 index 0000000..beba8ef --- /dev/null +++ b/TheBank/BankWebApi/Controllers/DepositsController.cs @@ -0,0 +1,69 @@ +using BankContracts.AdapterContracts; +using BankContracts.BindingModels; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace BankWebApi.Controllers; + +[Authorize] +[Route("api/[controller]/[action]")] +[ApiController] +[Produces("application/json")] +public class DepositsController(IDepositAdapter adapter) : ControllerBase +{ + private readonly IDepositAdapter _adapter = adapter; + + /// + /// получение всех записей вкладов + /// + /// список вкладов + [HttpGet] + public IActionResult GetAllRecords() + { + return _adapter.GetList().GetResponse(Request, Response); + } + + /// + /// получние записи о вкладе по данным + /// + /// уникальный идентификатор или другое поле + /// запись вклада + [HttpGet("{data}")] + public IActionResult GetRecord(string data) + { + return _adapter.GetElement(data).GetResponse(Request, Response); + } + + /// + /// получение записей вкладов по уникальному идентификатору клерка + /// + /// уникальный идентификатор клерка + /// список вкладов + [HttpGet("{data}")] + public IActionResult GetRecordByClerk(string data) + { + return _adapter.GetListByClerk(data).GetResponse(Request, Response); + } + + /// + /// создание записи вклада + /// + /// модель от пользователя + /// + [HttpPost] + public IActionResult Register([FromBody] DepositBindingModel model) + { + return _adapter.MakeDeposit(model).GetResponse(Request, Response); + } + + /// + /// изменение записи вклада + /// + /// новая модель + /// + [HttpPut] + public IActionResult ChangeInfo([FromBody] DepositBindingModel model) + { + return _adapter.ChangeDepositInfo(model).GetResponse(Request, Response); + } +} diff --git a/TheBank/BankWebApi/Program.cs b/TheBank/BankWebApi/Program.cs index 2423f30..8434017 100644 --- a/TheBank/BankWebApi/Program.cs +++ b/TheBank/BankWebApi/Program.cs @@ -87,13 +87,22 @@ builder.Services.AddSingleton(); // builder.Services.AddTransient(); builder.Services.AddTransient(); +builder.Services.AddTransient(); +builder.Services.AddTransient(); +builder.Services.AddTransient(); // builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); +builder.Services.AddTransient(); +builder.Services.AddTransient(); +builder.Services.AddTransient(); // builder.Services.AddTransient(); builder.Services.AddTransient(); +builder.Services.AddTransient(); +builder.Services.AddTransient(); +builder.Services.AddTransient(); var app = builder.Build(); From fbba1613903239a07a9c2286131486b784b55846 Mon Sep 17 00:00:00 2001 From: xom9k Date: Sat, 3 May 2025 15:18:21 +0400 Subject: [PATCH 08/11] =?UTF-8?q?fix:=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BD=D0=B0=D0=B7=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Implementations/CurrencyBusinessLogicContract.cs | 2 +- .../BusinessLogicContracts/ICurrencyBusinessLogicContract.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TheBank/BankBusinessLogic/Implementations/CurrencyBusinessLogicContract.cs b/TheBank/BankBusinessLogic/Implementations/CurrencyBusinessLogicContract.cs index 8ca49be..51da436 100644 --- a/TheBank/BankBusinessLogic/Implementations/CurrencyBusinessLogicContract.cs +++ b/TheBank/BankBusinessLogic/Implementations/CurrencyBusinessLogicContract.cs @@ -22,7 +22,7 @@ internal class CurrencyBusinessLogicContract( private readonly ILogger _logger = logger; - public List GetAllCurrencys() + public List GetAllCurrencies() { _logger.LogInformation("get all currencys programs"); return _currencyStorageContract.GetList(); diff --git a/TheBank/BankContracts/BusinessLogicContracts/ICurrencyBusinessLogicContract.cs b/TheBank/BankContracts/BusinessLogicContracts/ICurrencyBusinessLogicContract.cs index 71a5695..23d03f1 100644 --- a/TheBank/BankContracts/BusinessLogicContracts/ICurrencyBusinessLogicContract.cs +++ b/TheBank/BankContracts/BusinessLogicContracts/ICurrencyBusinessLogicContract.cs @@ -3,7 +3,7 @@ namespace BankContracts.BusinessLogicContracts; public interface ICurrencyBusinessLogicContract { - List GetAllCurrencys(); + List GetAllCurrencies(); List GetCurrencyByStorekeeper(string storekeeperId); From f4ad6ba98ed978dfa3731a9d6d9821c800d2c518 Mon Sep 17 00:00:00 2001 From: xom9k Date: Sat, 3 May 2025 16:26:07 +0400 Subject: [PATCH 09/11] =?UTF-8?q?feat:=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D0=B8,=20=D0=B0=D0=B4=D0=B0=D0=BF=D1=82=D0=B5=D1=80=D1=8B,=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D1=82=D1=80=D0=BE=D0=BB=D0=BB=D0=B5=D1=80?= =?UTF-8?q?=D1=8B=20=D0=B4=D0=BB=D1=8F=20=D0=BA=D0=BB=D0=B0=D0=B4=D0=BE?= =?UTF-8?q?=D0=B2=D1=89=D0=B8=D0=BA=D0=B0,=20=D0=B2=D0=B0=D0=BB=D1=8E?= =?UTF-8?q?=D1=82=D1=8B,=20=D0=BF=D0=BE=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AdapterContracts/ICurrencyAdapter.cs | 20 ++ .../AdapterContracts/IReplenishmentAdapter.cs | 21 ++ .../AdapterContracts/IStorekeeperAdapter.cs | 18 ++ .../CurrencyOperationResponse.cs | 24 ++ .../ReplenishmentOperationResponse.cs | 24 ++ .../StorekeeperOperationResponse.cs | 24 ++ .../BindingModels/CurrencyBindingModel.cs | 13 +- .../ReplenishmentBindingModel.cs | 14 + .../BindingModels/StorekeeperBindingModel.cs | 20 ++ .../ViewModels/CurrencyViewModel.cs | 9 + .../ViewModels/ReplenishmentViewModel.cs | 14 + .../ViewModels/StorekeeperViewModel.cs | 20 ++ .../BankWebApi/Adapters/CurrencyAdapter.cs | 206 +++++++++++++ .../Adapters/ReplenishmentAdapter.cs | 272 ++++++++++++++++++ .../BankWebApi/Adapters/StorekeeperAdapter.cs | 174 +++++++++++ .../Controllers/CurrenciesController.cs | 69 +++++ .../Controllers/ReplenishmentsController.cs | 92 ++++++ .../Controllers/StorekeepersController.cs | 58 ++++ TheBank/BankWebApi/Program.cs | 9 + 19 files changed, 1100 insertions(+), 1 deletion(-) create mode 100644 TheBank/BankContracts/AdapterContracts/ICurrencyAdapter.cs create mode 100644 TheBank/BankContracts/AdapterContracts/IReplenishmentAdapter.cs create mode 100644 TheBank/BankContracts/AdapterContracts/IStorekeeperAdapter.cs create mode 100644 TheBank/BankContracts/AdapterContracts/OperationResponses/CurrencyOperationResponse.cs create mode 100644 TheBank/BankContracts/AdapterContracts/OperationResponses/ReplenishmentOperationResponse.cs create mode 100644 TheBank/BankContracts/AdapterContracts/OperationResponses/StorekeeperOperationResponse.cs create mode 100644 TheBank/BankContracts/BindingModels/ReplenishmentBindingModel.cs create mode 100644 TheBank/BankContracts/BindingModels/StorekeeperBindingModel.cs create mode 100644 TheBank/BankContracts/ViewModels/ReplenishmentViewModel.cs create mode 100644 TheBank/BankContracts/ViewModels/StorekeeperViewModel.cs create mode 100644 TheBank/BankWebApi/Adapters/CurrencyAdapter.cs create mode 100644 TheBank/BankWebApi/Adapters/ReplenishmentAdapter.cs create mode 100644 TheBank/BankWebApi/Adapters/StorekeeperAdapter.cs create mode 100644 TheBank/BankWebApi/Controllers/CurrenciesController.cs create mode 100644 TheBank/BankWebApi/Controllers/ReplenishmentsController.cs create mode 100644 TheBank/BankWebApi/Controllers/StorekeepersController.cs diff --git a/TheBank/BankContracts/AdapterContracts/ICurrencyAdapter.cs b/TheBank/BankContracts/AdapterContracts/ICurrencyAdapter.cs new file mode 100644 index 0000000..21f2ff3 --- /dev/null +++ b/TheBank/BankContracts/AdapterContracts/ICurrencyAdapter.cs @@ -0,0 +1,20 @@ +using BankContracts.AdapterContracts.OperationResponses; +using BankContracts.BindingModels; + +namespace BankContracts.AdapterContracts; + +/// +/// контракт адаптера для валюыты +/// +public interface ICurrencyAdapter +{ + CurrencyOperationResponse GetList(); + + CurrencyOperationResponse GetElement(string data); + + CurrencyOperationResponse GetListByStorekeeper(string storekeeperId); + + CurrencyOperationResponse MakeCurrency(CurrencyBindingModel currencyModel); + + CurrencyOperationResponse ChangeCurrencyInfo(CurrencyBindingModel currencyModel); +} diff --git a/TheBank/BankContracts/AdapterContracts/IReplenishmentAdapter.cs b/TheBank/BankContracts/AdapterContracts/IReplenishmentAdapter.cs new file mode 100644 index 0000000..fd4f705 --- /dev/null +++ b/TheBank/BankContracts/AdapterContracts/IReplenishmentAdapter.cs @@ -0,0 +1,21 @@ +using BankContracts.AdapterContracts.OperationResponses; +using BankContracts.BindingModels; + +namespace BankContracts.AdapterContracts; + +public interface IReplenishmentAdapter +{ + ReplenishmentOperationResponse GetList(); + + ReplenishmentOperationResponse GetElement(string data); + + ReplenishmentOperationResponse GetListByClerk(string clerkId); + + ReplenishmentOperationResponse GetListByDeposit(string depositId); + + ReplenishmentOperationResponse GetListByDate(DateTime fromDate, DateTime toDate); + + ReplenishmentOperationResponse RegisterReplenishment(ReplenishmentBindingModel replenishmentModel); + + ReplenishmentOperationResponse ChangeReplenishmentInfo(ReplenishmentBindingModel replenishmentModel); +} diff --git a/TheBank/BankContracts/AdapterContracts/IStorekeeperAdapter.cs b/TheBank/BankContracts/AdapterContracts/IStorekeeperAdapter.cs new file mode 100644 index 0000000..e69e34c --- /dev/null +++ b/TheBank/BankContracts/AdapterContracts/IStorekeeperAdapter.cs @@ -0,0 +1,18 @@ +using BankContracts.AdapterContracts.OperationResponses; +using BankContracts.BindingModels; + +namespace BankContracts.AdapterContracts; + +/// +/// контракт адаптера для кладовщика +/// +public interface IStorekeeperAdapter +{ + StorekeeperOperationResponse GetList(); + + StorekeeperOperationResponse GetElement(string data); + + StorekeeperOperationResponse RegisterStorekeeper(StorekeeperBindingModel storekeeperModel); + + StorekeeperOperationResponse ChangeStorekeeperInfo(StorekeeperBindingModel storekeeperModel); +} diff --git a/TheBank/BankContracts/AdapterContracts/OperationResponses/CurrencyOperationResponse.cs b/TheBank/BankContracts/AdapterContracts/OperationResponses/CurrencyOperationResponse.cs new file mode 100644 index 0000000..bde6448 --- /dev/null +++ b/TheBank/BankContracts/AdapterContracts/OperationResponses/CurrencyOperationResponse.cs @@ -0,0 +1,24 @@ +using BankContracts.Infrastructure; +using BankContracts.ViewModels; + +namespace BankContracts.AdapterContracts.OperationResponses; + +public class CurrencyOperationResponse : OperationResponse +{ + public static CurrencyOperationResponse OK(List data) => + OK>(data); + + public static CurrencyOperationResponse OK(CurrencyViewModel data) => + OK(data); + + public static CurrencyOperationResponse NoContent() => NoContent(); + + public static CurrencyOperationResponse NotFound(string message) => + NotFound(message); + + public static CurrencyOperationResponse BadRequest(string message) => + BadRequest(message); + + public static CurrencyOperationResponse InternalServerError(string message) => + InternalServerError(message); +} diff --git a/TheBank/BankContracts/AdapterContracts/OperationResponses/ReplenishmentOperationResponse.cs b/TheBank/BankContracts/AdapterContracts/OperationResponses/ReplenishmentOperationResponse.cs new file mode 100644 index 0000000..735a591 --- /dev/null +++ b/TheBank/BankContracts/AdapterContracts/OperationResponses/ReplenishmentOperationResponse.cs @@ -0,0 +1,24 @@ +using BankContracts.Infrastructure; +using BankContracts.ViewModels; + +namespace BankContracts.AdapterContracts.OperationResponses; + +public class ReplenishmentOperationResponse : OperationResponse +{ + public static ReplenishmentOperationResponse OK(List data) => + OK>(data); + + public static ReplenishmentOperationResponse OK(ReplenishmentViewModel data) => + OK(data); + + public static ReplenishmentOperationResponse NoContent() => NoContent(); + + public static ReplenishmentOperationResponse NotFound(string message) => + NotFound(message); + + public static ReplenishmentOperationResponse BadRequest(string message) => + BadRequest(message); + + public static ReplenishmentOperationResponse InternalServerError(string message) => + InternalServerError(message); +} diff --git a/TheBank/BankContracts/AdapterContracts/OperationResponses/StorekeeperOperationResponse.cs b/TheBank/BankContracts/AdapterContracts/OperationResponses/StorekeeperOperationResponse.cs new file mode 100644 index 0000000..85ccb7d --- /dev/null +++ b/TheBank/BankContracts/AdapterContracts/OperationResponses/StorekeeperOperationResponse.cs @@ -0,0 +1,24 @@ +using BankContracts.Infrastructure; +using BankContracts.ViewModels; + +namespace BankContracts.AdapterContracts.OperationResponses; + +public class StorekeeperOperationResponse : OperationResponse +{ + public static StorekeeperOperationResponse OK(List data) => + OK>(data); + + public static StorekeeperOperationResponse OK(StorekeeperViewModel data) => + OK(data); + + public static StorekeeperOperationResponse NoContent() => NoContent(); + + public static StorekeeperOperationResponse NotFound(string message) => + NotFound(message); + + public static StorekeeperOperationResponse BadRequest(string message) => + BadRequest(message); + + public static StorekeeperOperationResponse InternalServerError(string message) => + InternalServerError(message); +} diff --git a/TheBank/BankContracts/BindingModels/CurrencyBindingModel.cs b/TheBank/BankContracts/BindingModels/CurrencyBindingModel.cs index 2c3699e..65761f0 100644 --- a/TheBank/BankContracts/BindingModels/CurrencyBindingModel.cs +++ b/TheBank/BankContracts/BindingModels/CurrencyBindingModel.cs @@ -1,5 +1,16 @@ -namespace BankContracts.BindingModels; +using System.Xml.Linq; + +namespace BankContracts.BindingModels; public class CurrencyBindingModel { + public string? Id { get; set; } + + public string? Name { get; set; } + + public string? Abbreviation { get; set; } + + public decimal Cost { get; set; } + + public string? StorekeeperId { get; set; } } diff --git a/TheBank/BankContracts/BindingModels/ReplenishmentBindingModel.cs b/TheBank/BankContracts/BindingModels/ReplenishmentBindingModel.cs new file mode 100644 index 0000000..6694fca --- /dev/null +++ b/TheBank/BankContracts/BindingModels/ReplenishmentBindingModel.cs @@ -0,0 +1,14 @@ +namespace BankContracts.BindingModels; + +public class ReplenishmentBindingModel +{ + public string? Id { get; set; } + + public decimal Amount { get; set; } + + public DateTime Date { get; set; } + + public string? DepositId { get; set; } + + public string? ClerkId { get; set; } +} diff --git a/TheBank/BankContracts/BindingModels/StorekeeperBindingModel.cs b/TheBank/BankContracts/BindingModels/StorekeeperBindingModel.cs new file mode 100644 index 0000000..014bcbd --- /dev/null +++ b/TheBank/BankContracts/BindingModels/StorekeeperBindingModel.cs @@ -0,0 +1,20 @@ +namespace BankContracts.BindingModels; + +public class StorekeeperBindingModel +{ + public string? Id { get; set; } + + public string? Name { get; set; } + + public string? Surname { get; set; } + + public string? MiddleName { get; set; } + + public string? Login { get; set; } + + public string? Password { get; set; } + + public string? Email { get; set; } + + public string? PhoneNumber { get; set; } +} diff --git a/TheBank/BankContracts/ViewModels/CurrencyViewModel.cs b/TheBank/BankContracts/ViewModels/CurrencyViewModel.cs index 2ddd3c8..bb54757 100644 --- a/TheBank/BankContracts/ViewModels/CurrencyViewModel.cs +++ b/TheBank/BankContracts/ViewModels/CurrencyViewModel.cs @@ -2,4 +2,13 @@ public class CurrencyViewModel { + public required string Id { get; set; } + + public required string Name { get; set; } + + public required string Abbreviation { get; set; } + + public required decimal Cost { get; set; } + + public required string StorekeeperId { get; set; } } diff --git a/TheBank/BankContracts/ViewModels/ReplenishmentViewModel.cs b/TheBank/BankContracts/ViewModels/ReplenishmentViewModel.cs new file mode 100644 index 0000000..83fab26 --- /dev/null +++ b/TheBank/BankContracts/ViewModels/ReplenishmentViewModel.cs @@ -0,0 +1,14 @@ +namespace BankContracts.ViewModels; + +public class ReplenishmentViewModel +{ + public required string Id { get; set; } + + public required decimal Amount { get; set; } + + public required DateTime Date { get; set; } + + public required string DepositId { get; set; } + + public required string ClerkId { get; set; } +} diff --git a/TheBank/BankContracts/ViewModels/StorekeeperViewModel.cs b/TheBank/BankContracts/ViewModels/StorekeeperViewModel.cs new file mode 100644 index 0000000..94322a0 --- /dev/null +++ b/TheBank/BankContracts/ViewModels/StorekeeperViewModel.cs @@ -0,0 +1,20 @@ +namespace BankContracts.ViewModels; + +public class StorekeeperViewModel +{ + public required string Id { get; set; } + + public required string Name { get; set; } + + public required string Surname { get; set; } + + public required string MiddleName { get; set; } + + public required string Login { get; set; } + + public required string Password { get; set; } + + public required string Email { get; set; } + + public required string PhoneNumber { get; set; } +} diff --git a/TheBank/BankWebApi/Adapters/CurrencyAdapter.cs b/TheBank/BankWebApi/Adapters/CurrencyAdapter.cs new file mode 100644 index 0000000..ab4085e --- /dev/null +++ b/TheBank/BankWebApi/Adapters/CurrencyAdapter.cs @@ -0,0 +1,206 @@ +using AutoMapper; +using BankBusinessLogic.Implementations; +using BankContracts.AdapterContracts; +using BankContracts.AdapterContracts.OperationResponses; +using BankContracts.BindingModels; +using BankContracts.BusinessLogicContracts; +using BankContracts.DataModels; +using BankContracts.Exceptions; +using BankContracts.ViewModels; +using BankDatabase.Models; + +namespace BankWebApi.Adapters; + +public class CurrencyAdapter : ICurrencyAdapter +{ + private readonly ICurrencyBusinessLogicContract _currencyBusinessLogicContract; + + private readonly ILogger _logger; + + private readonly Mapper _mapper; + + public CurrencyAdapter(ICurrencyBusinessLogicContract currencyBusinessLogicContract, ILogger logger) + { + _currencyBusinessLogicContract = currencyBusinessLogicContract; + _logger = logger; + var config = new MapperConfiguration(cfg => + { + cfg.CreateMap(); + cfg.CreateMap(); + }); + _mapper = new Mapper(config); + } + + public CurrencyOperationResponse GetList() + { + try + { + return CurrencyOperationResponse.OK( + [ + .. _currencyBusinessLogicContract + .GetAllCurrencies() + .Select(x => _mapper.Map(x)), + ] + ); + } + catch (NullListException) + { + _logger.LogError("NullListException"); + return CurrencyOperationResponse.NotFound("The list is not initialized"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return CurrencyOperationResponse.InternalServerError( + $"Error while working with data storage:{ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return CurrencyOperationResponse.InternalServerError(ex.Message); + } + } + + public CurrencyOperationResponse GetElement(string data) + { + try + { + return CurrencyOperationResponse.OK( + _mapper.Map(_currencyBusinessLogicContract.GetCurrencyByData(data)) + ); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return CurrencyOperationResponse.BadRequest("Data is empty"); + } + catch (ElementNotFoundException ex) + { + _logger.LogError(ex, "ElementNotFoundException"); + return CurrencyOperationResponse.NotFound($"Not found element by data {data}"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return CurrencyOperationResponse.InternalServerError( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return CurrencyOperationResponse.InternalServerError(ex.Message); + } + } + + public CurrencyOperationResponse MakeCurrency(CurrencyBindingModel currencyModel) + { + try + { + _currencyBusinessLogicContract.InsertCurrency(_mapper.Map(currencyModel)); + return CurrencyOperationResponse.NoContent(); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return CurrencyOperationResponse.BadRequest("Data is empty"); + } + catch (ValidationException ex) + { + _logger.LogError(ex, "ValidationException"); + return CurrencyOperationResponse.BadRequest($"Incorrect data transmitted: {ex.Message}"); + } + catch (ElementExistsException ex) + { + _logger.LogError(ex, "ElementExistsException"); + return CurrencyOperationResponse.BadRequest(ex.Message); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return CurrencyOperationResponse.BadRequest( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return CurrencyOperationResponse.InternalServerError(ex.Message); + } + } + + public CurrencyOperationResponse ChangeCurrencyInfo(CurrencyBindingModel currencyModel) + { + try + { + _currencyBusinessLogicContract.UpdateCurrency(_mapper.Map(currencyModel)); + return CurrencyOperationResponse.NoContent(); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return CurrencyOperationResponse.BadRequest("Data is empty"); + } + catch (ValidationException ex) + { + _logger.LogError(ex, "ValidationException"); + return CurrencyOperationResponse.BadRequest($"Incorrect data transmitted: {ex.Message}"); + } + catch (ElementNotFoundException ex) + { + _logger.LogError(ex, "ElementNotFoundException"); + return CurrencyOperationResponse.BadRequest( + $"Not found element by Id {currencyModel.Id}" + ); + } + catch (ElementExistsException ex) + { + _logger.LogError(ex, "ElementExistsException"); + return CurrencyOperationResponse.BadRequest(ex.Message); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return CurrencyOperationResponse.BadRequest( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return CurrencyOperationResponse.InternalServerError(ex.Message); + } + } + + public CurrencyOperationResponse GetListByStorekeeper(string storekeeperId) + { + try + { + return CurrencyOperationResponse.OK( + [ + .. _currencyBusinessLogicContract + .GetCurrencyByStorekeeper(storekeeperId) + .Select(x => _mapper.Map(x)), + ] + ); + } + catch (NullListException) + { + _logger.LogError("NullListException"); + return CurrencyOperationResponse.NotFound("The list is not initialized"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return CurrencyOperationResponse.InternalServerError( + $"Error while working with data storage:{ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return CurrencyOperationResponse.InternalServerError(ex.Message); + } + } +} diff --git a/TheBank/BankWebApi/Adapters/ReplenishmentAdapter.cs b/TheBank/BankWebApi/Adapters/ReplenishmentAdapter.cs new file mode 100644 index 0000000..7cadec8 --- /dev/null +++ b/TheBank/BankWebApi/Adapters/ReplenishmentAdapter.cs @@ -0,0 +1,272 @@ +using AutoMapper; +using BankBusinessLogic.Implementations; +using BankContracts.AdapterContracts; +using BankContracts.AdapterContracts.OperationResponses; +using BankContracts.BindingModels; +using BankContracts.BusinessLogicContracts; +using BankContracts.DataModels; +using BankContracts.Exceptions; +using BankContracts.ViewModels; + +namespace BankWebApi.Adapters; + +public class ReplenishmentAdapter : IReplenishmentAdapter +{ + private readonly IReplenishmentBusinessLogicContract _replenishmentBusinessLogicContract; + + private readonly ILogger _logger; + + private readonly Mapper _mapper; + + public ReplenishmentAdapter(IReplenishmentBusinessLogicContract replenishmentBusinessLogicContract, ILogger logger) + { + _replenishmentBusinessLogicContract = replenishmentBusinessLogicContract; + _logger = logger; + var config = new MapperConfiguration(cfg => + { + cfg.CreateMap(); + cfg.CreateMap(); + }); + _mapper = new Mapper(config); + } + + public ReplenishmentOperationResponse GetList() + { + try + { + return ReplenishmentOperationResponse.OK( + [ + .. _replenishmentBusinessLogicContract + .GetAllReplenishments() + .Select(x => _mapper.Map(x)), + ] + ); + } + catch (NullListException) + { + _logger.LogError("NullListException"); + return ReplenishmentOperationResponse.NotFound("The list is not initialized"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return ReplenishmentOperationResponse.InternalServerError( + $"Error while working with data storage:{ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return ReplenishmentOperationResponse.InternalServerError(ex.Message); + } + } + + public ReplenishmentOperationResponse GetElement(string data) + { + try + { + return ReplenishmentOperationResponse.OK( + _mapper.Map(_replenishmentBusinessLogicContract.GetReplenishmentByData(data)) + ); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return ReplenishmentOperationResponse.BadRequest("Data is empty"); + } + catch (ElementNotFoundException ex) + { + _logger.LogError(ex, "ElementNotFoundException"); + return ReplenishmentOperationResponse.NotFound($"Not found element by data {data}"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return ReplenishmentOperationResponse.InternalServerError( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return ReplenishmentOperationResponse.InternalServerError(ex.Message); + } + } + + public ReplenishmentOperationResponse GetListByClerk(string clerkId) + { + try + { + return ReplenishmentOperationResponse.OK( + [ + .. _replenishmentBusinessLogicContract + .GetAllReplenishmentsByClerk(clerkId) + .Select(x => _mapper.Map(x)), + ] + ); + } + catch (NullListException) + { + _logger.LogError("NullListException"); + return ReplenishmentOperationResponse.NotFound("The list is not initialized"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return ReplenishmentOperationResponse.InternalServerError( + $"Error while working with data storage:{ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return ReplenishmentOperationResponse.InternalServerError(ex.Message); + } + } + + public ReplenishmentOperationResponse GetListByDeposit(string depositId) + { + try + { + return ReplenishmentOperationResponse.OK( + [ + .. _replenishmentBusinessLogicContract + .GetAllReplenishmentsByDeposit(depositId) + .Select(x => _mapper.Map(x)), + ] + ); + } + catch (NullListException) + { + _logger.LogError("NullListException"); + return ReplenishmentOperationResponse.NotFound("The list is not initialized"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return ReplenishmentOperationResponse.InternalServerError( + $"Error while working with data storage:{ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return ReplenishmentOperationResponse.InternalServerError(ex.Message); + } + } + + public ReplenishmentOperationResponse GetListByDate(DateTime fromDate, DateTime toDate) + { + try + { + return ReplenishmentOperationResponse.OK( + [ + .. _replenishmentBusinessLogicContract + .GetAllReplenishmentsByDate(fromDate, toDate) + .Select(x => _mapper.Map(x)), + ] + ); + } + catch (NullListException) + { + _logger.LogError("NullListException"); + return ReplenishmentOperationResponse.NotFound("The list is not initialized"); + } + catch (IncorrectDatesException) + { + _logger.LogError("IncorrectDatesException"); + return ReplenishmentOperationResponse.BadRequest("Incorrect dates"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return ReplenishmentOperationResponse.InternalServerError( + $"Error while working with data storage:{ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return ReplenishmentOperationResponse.InternalServerError(ex.Message); + } + } + + public ReplenishmentOperationResponse RegisterReplenishment(ReplenishmentBindingModel replenishmentModel) + { + try + { + _replenishmentBusinessLogicContract.InsertReplenishment(_mapper.Map(replenishmentModel)); + return ReplenishmentOperationResponse.NoContent(); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return ReplenishmentOperationResponse.BadRequest("Data is empty"); + } + catch (ValidationException ex) + { + _logger.LogError(ex, "ValidationException"); + return ReplenishmentOperationResponse.BadRequest($"Incorrect data transmitted: {ex.Message}"); + } + catch (ElementExistsException ex) + { + _logger.LogError(ex, "ElementExistsException"); + return ReplenishmentOperationResponse.BadRequest(ex.Message); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return ReplenishmentOperationResponse.BadRequest( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return ReplenishmentOperationResponse.InternalServerError(ex.Message); + } + } + + public ReplenishmentOperationResponse ChangeReplenishmentInfo(ReplenishmentBindingModel replenishmentModel) + { + try + { + _replenishmentBusinessLogicContract.UpdateReplenishment(_mapper.Map(replenishmentModel)); + return ReplenishmentOperationResponse.NoContent(); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return ReplenishmentOperationResponse.BadRequest("Data is empty"); + } + catch (ValidationException ex) + { + _logger.LogError(ex, "ValidationException"); + return ReplenishmentOperationResponse.BadRequest($"Incorrect data transmitted: {ex.Message}"); + } + catch (ElementNotFoundException ex) + { + _logger.LogError(ex, "ElementNotFoundException"); + return ReplenishmentOperationResponse.BadRequest( + $"Not found element by Id {replenishmentModel.Id}" + ); + } + catch (ElementExistsException ex) + { + _logger.LogError(ex, "ElementExistsException"); + return ReplenishmentOperationResponse.BadRequest(ex.Message); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return ReplenishmentOperationResponse.BadRequest( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return ReplenishmentOperationResponse.InternalServerError(ex.Message); + } + } +} diff --git a/TheBank/BankWebApi/Adapters/StorekeeperAdapter.cs b/TheBank/BankWebApi/Adapters/StorekeeperAdapter.cs new file mode 100644 index 0000000..d287373 --- /dev/null +++ b/TheBank/BankWebApi/Adapters/StorekeeperAdapter.cs @@ -0,0 +1,174 @@ +using AutoMapper; +using BankBusinessLogic.Implementations; +using BankContracts.AdapterContracts; +using BankContracts.AdapterContracts.OperationResponses; +using BankContracts.BindingModels; +using BankContracts.BusinessLogicContracts; +using BankContracts.DataModels; +using BankContracts.Exceptions; +using BankContracts.ViewModels; + +namespace BankWebApi.Adapters; + +public class StorekeeperAdapter : IStorekeeperAdapter +{ + private readonly IStorekeeperBusinessLogicContract _storekeeperBusinessLogicContract; + + private readonly ILogger _logger; + + private readonly Mapper _mapper; + + public StorekeeperAdapter(IStorekeeperBusinessLogicContract storekeeperBusinessLogicContract, ILogger logger) + { + _storekeeperBusinessLogicContract = storekeeperBusinessLogicContract; + _logger = logger; + var config = new MapperConfiguration(cfg => + { + cfg.CreateMap(); + cfg.CreateMap(); + }); + _mapper = new Mapper(config); + } + + public StorekeeperOperationResponse GetList() + { + try + { + return StorekeeperOperationResponse.OK( + [ + .. _storekeeperBusinessLogicContract + .GetAllStorekeepers() + .Select(x => _mapper.Map(x)), + ] + ); + } + catch (NullListException) + { + _logger.LogError("NullListException"); + return StorekeeperOperationResponse.NotFound("The list is not initialized"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return StorekeeperOperationResponse.InternalServerError( + $"Error while working with data storage:{ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return StorekeeperOperationResponse.InternalServerError(ex.Message); + } + } + + public StorekeeperOperationResponse GetElement(string data) + { + try + { + return StorekeeperOperationResponse.OK( + _mapper.Map(_storekeeperBusinessLogicContract.GetStorekeeperByData(data)) + ); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return StorekeeperOperationResponse.BadRequest("Data is empty"); + } + catch (ElementNotFoundException ex) + { + _logger.LogError(ex, "ElementNotFoundException"); + return StorekeeperOperationResponse.NotFound($"Not found element by data {data}"); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return StorekeeperOperationResponse.InternalServerError( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return StorekeeperOperationResponse.InternalServerError(ex.Message); + } + } + + public StorekeeperOperationResponse RegisterStorekeeper(StorekeeperBindingModel storekeeperModel) + { + try + { + _storekeeperBusinessLogicContract.InsertStorekeeper(_mapper.Map(storekeeperModel)); + return StorekeeperOperationResponse.NoContent(); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return StorekeeperOperationResponse.BadRequest("Data is empty"); + } + catch (ValidationException ex) + { + _logger.LogError(ex, "ValidationException"); + return StorekeeperOperationResponse.BadRequest($"Incorrect data transmitted: {ex.Message}"); + } + catch (ElementExistsException ex) + { + _logger.LogError(ex, "ElementExistsException"); + return StorekeeperOperationResponse.BadRequest(ex.Message); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return StorekeeperOperationResponse.BadRequest( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return StorekeeperOperationResponse.InternalServerError(ex.Message); + } + } + + public StorekeeperOperationResponse ChangeStorekeeperInfo(StorekeeperBindingModel storekeeperModel) + { + try + { + _storekeeperBusinessLogicContract.UpdateStorekeeper(_mapper.Map(storekeeperModel)); + return StorekeeperOperationResponse.NoContent(); + } + catch (ArgumentNullException ex) + { + _logger.LogError(ex, "ArgumentNullException"); + return StorekeeperOperationResponse.BadRequest("Data is empty"); + } + catch (ValidationException ex) + { + _logger.LogError(ex, "ValidationException"); + return StorekeeperOperationResponse.BadRequest($"Incorrect data transmitted: {ex.Message}"); + } + catch (ElementNotFoundException ex) + { + _logger.LogError(ex, "ElementNotFoundException"); + return StorekeeperOperationResponse.BadRequest( + $"Not found element by Id {storekeeperModel.Id}" + ); + } + catch (ElementExistsException ex) + { + _logger.LogError(ex, "ElementExistsException"); + return StorekeeperOperationResponse.BadRequest(ex.Message); + } + catch (StorageException ex) + { + _logger.LogError(ex, "StorageException"); + return StorekeeperOperationResponse.BadRequest( + $"Error while working with data storage: {ex.InnerException!.Message}" + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Exception"); + return StorekeeperOperationResponse.InternalServerError(ex.Message); + } + } +} diff --git a/TheBank/BankWebApi/Controllers/CurrenciesController.cs b/TheBank/BankWebApi/Controllers/CurrenciesController.cs new file mode 100644 index 0000000..94ee286 --- /dev/null +++ b/TheBank/BankWebApi/Controllers/CurrenciesController.cs @@ -0,0 +1,69 @@ +using BankContracts.AdapterContracts; +using BankContracts.BindingModels; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace BankWebApi.Controllers; + +[Authorize] +[Route("api/[controller]/[action]")] +[ApiController] +[Produces("application/json")] +public class CurrenciesController(ICurrencyAdapter adapter) : ControllerBase +{ + private readonly ICurrencyAdapter _adapter = adapter; + + /// + /// получение всех записей валют + /// + /// список валют + [HttpGet] + public IActionResult GetAllRecords() + { + return _adapter.GetList().GetResponse(Request, Response); + } + + /// + /// получние записи о валюте по данным + /// + /// уникальный идентификатор или другое поле + /// запись вклада + [HttpGet("{data}")] + public IActionResult GetRecord(string data) + { + return _adapter.GetElement(data).GetResponse(Request, Response); + } + + /// + /// получение записей валюте по уникальному идентификатору кладовщика + /// + /// уникальный идентификатор кладовщика + /// список валют + [HttpGet("{data}")] + public IActionResult GetRecordByStorekeeper(string data) + { + return _adapter.GetListByStorekeeper(data).GetResponse(Request, Response); + } + + /// + /// создание записи валюты + /// + /// модель от пользователя + /// + [HttpPost] + public IActionResult Register([FromBody] CurrencyBindingModel model) + { + return _adapter.MakeCurrency(model).GetResponse(Request, Response); + } + + /// + /// изменение записи валюты + /// + /// новая модель + /// + [HttpPut] + public IActionResult ChangeInfo([FromBody] CurrencyBindingModel model) + { + return _adapter.ChangeCurrencyInfo(model).GetResponse(Request, Response); + } +} diff --git a/TheBank/BankWebApi/Controllers/ReplenishmentsController.cs b/TheBank/BankWebApi/Controllers/ReplenishmentsController.cs new file mode 100644 index 0000000..011e76b --- /dev/null +++ b/TheBank/BankWebApi/Controllers/ReplenishmentsController.cs @@ -0,0 +1,92 @@ +using BankContracts.AdapterContracts; +using BankContracts.BindingModels; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace BankWebApi.Controllers; + +[Authorize] +[Route("api/[controller]/[action]")] +[ApiController] +[Produces("application/json")] +public class ReplenishmentsController(IReplenishmentAdapter adapter) : ControllerBase +{ + private readonly IReplenishmentAdapter _adapter = adapter; + + /// + /// получение всех записей пополнений + /// + /// список пополнений + [HttpGet] + public IActionResult GetAllRecords() + { + return _adapter.GetList().GetResponse(Request, Response); + } + + /// + /// получние записи о пополнени по данным + /// + /// уникальный идентификатор или другое поле + /// запись пополнения + [HttpGet("{data}")] + public IActionResult GetRecord(string data) + { + return _adapter.GetElement(data).GetResponse(Request, Response); + } + + /// + /// получение записей пополнений по уникальному идентификатору клерка + /// + /// уникальный идентификатор клерка + /// список пополнений + [HttpGet("{data}")] + public IActionResult GetRecordByClerk(string data) + { + return _adapter.GetListByClerk(data).GetResponse(Request, Response); + } + + /// + /// получение записей пополнений по уникальному идентификатору вклада + /// + /// уникальный идентификатор вклада + /// список пополнений + [HttpGet("{data}")] + public IActionResult GetRecordByDeposit(string data) + { + return _adapter.GetListByDeposit(data).GetResponse(Request, Response); + } + + /// + /// получение записей пополнений по дате + /// + /// + /// + /// список пополнений + [HttpGet("{data}")] + public IActionResult GetRecordByDate(DateTime fromDate, DateTime toDate) + { + return _adapter.GetListByDate(fromDate, toDate).GetResponse(Request, Response); + } + + /// + /// создание записи пополнения + /// + /// модель от пользователя + /// + [HttpPost] + public IActionResult Register([FromBody] ReplenishmentBindingModel model) + { + return _adapter.RegisterReplenishment(model).GetResponse(Request, Response); + } + + /// + /// изменение записи пополнения + /// + /// новая модель + /// + [HttpPut] + public IActionResult ChangeInfo([FromBody] ReplenishmentBindingModel model) + { + return _adapter.ChangeReplenishmentInfo(model).GetResponse(Request, Response); + } +} diff --git a/TheBank/BankWebApi/Controllers/StorekeepersController.cs b/TheBank/BankWebApi/Controllers/StorekeepersController.cs new file mode 100644 index 0000000..97726ea --- /dev/null +++ b/TheBank/BankWebApi/Controllers/StorekeepersController.cs @@ -0,0 +1,58 @@ +using BankContracts.AdapterContracts; +using BankContracts.BindingModels; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace BankWebApi.Controllers; + +[Authorize] +[Route("api/[controller]")] +[ApiController] +[Produces("application/json")] +public class StorekeepersController(IStorekeeperAdapter adapter) : ControllerBase +{ + private readonly IStorekeeperAdapter _adapter = adapter; + + /// + /// получение всех записей кладовщика + /// + /// список кладовщиков + [HttpGet] + public IActionResult GetAllRecords() + { + return _adapter.GetList().GetResponse(Request, Response); + } + + /// + /// получние записи о кладовщике по данным + /// + /// уникальный идентификатор или другое поле + /// запись кладовщика + [HttpGet("{data}")] + public IActionResult GetRecord(string data) + { + return _adapter.GetElement(data).GetResponse(Request, Response); + } + + /// + /// создание записи кладовщика + /// + /// модель от пользователя + /// + [HttpPost] + public IActionResult Register([FromBody] StorekeeperBindingModel model) + { + return _adapter.RegisterStorekeeper(model).GetResponse(Request, Response); + } + + /// + /// изменение записи кладовщика + /// + /// новая модель + /// + [HttpPut] + public IActionResult ChangeInfo([FromBody] StorekeeperBindingModel model) + { + return _adapter.ChangeStorekeeperInfo(model).GetResponse(Request, Response); + } +} diff --git a/TheBank/BankWebApi/Program.cs b/TheBank/BankWebApi/Program.cs index 8434017..6f7f794 100644 --- a/TheBank/BankWebApi/Program.cs +++ b/TheBank/BankWebApi/Program.cs @@ -90,6 +90,9 @@ builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); +builder.Services.AddTransient(); +builder.Services.AddTransient(); +builder.Services.AddTransient(); // builder.Services.AddTransient(); builder.Services.AddTransient(); @@ -97,12 +100,18 @@ builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); +builder.Services.AddTransient(); +builder.Services.AddTransient(); +builder.Services.AddTransient(); // builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); +builder.Services.AddTransient(); +builder.Services.AddTransient(); +builder.Services.AddTransient(); var app = builder.Build(); From 1cbde02887bc6f374c2d96163e8baa6740373321 Mon Sep 17 00:00:00 2001 From: xom9k Date: Sat, 10 May 2025 13:12:28 +0400 Subject: [PATCH 10/11] =?UTF-8?q?fix:=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=8E=D0=B7=D0=B8=D0=BD=D0=B3=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TheBank/BankContracts/BindingModels/ClientBindingModel.cs | 3 +-- TheBank/BankWebApi/BankWebApi.csproj | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/TheBank/BankContracts/BindingModels/ClientBindingModel.cs b/TheBank/BankContracts/BindingModels/ClientBindingModel.cs index b33edfe..0101526 100644 --- a/TheBank/BankContracts/BindingModels/ClientBindingModel.cs +++ b/TheBank/BankContracts/BindingModels/ClientBindingModel.cs @@ -1,5 +1,4 @@ -using BankContracts.ViewModels; -namespace BankContracts.BindingModels; +namespace BankContracts.BindingModels; public class ClientBindingModel { diff --git a/TheBank/BankWebApi/BankWebApi.csproj b/TheBank/BankWebApi/BankWebApi.csproj index fd68b27..dbae776 100644 --- a/TheBank/BankWebApi/BankWebApi.csproj +++ b/TheBank/BankWebApi/BankWebApi.csproj @@ -24,6 +24,11 @@ + + + + + true $(NoWarn);1591 From 24833faba0b0326a447402955d81dd605c90a4bc Mon Sep 17 00:00:00 2001 From: xom9k Date: Sat, 10 May 2025 13:13:38 +0400 Subject: [PATCH 11/11] =?UTF-8?q?feat:=20=D0=B4=D0=B2=D0=B0=20=D0=BA=D0=BB?= =?UTF-8?q?=D0=B0=D1=81=D1=81=D0=B0=20=D1=82=D0=B5=D1=81=D1=82=D0=BE=D0=B2?= =?UTF-8?q?=20+=20=D0=B1=D0=B0=D0=B7=D0=BE=D0=B2=D1=8B=D0=B9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TheBank/BankTests/BankTests.csproj | 3 + .../Infrastructure/ConfigurationDatabase.cs | 2 +- .../CustomWebApplicationFactory.cs | 31 +++ .../BaseWebApiControllerTest.cs | 68 +++++++ .../ClerkControllerTests.cs | 177 ++++++++++++++++++ .../ClientControllerTests.cs | 174 +++++++++++++++++ 6 files changed, 454 insertions(+), 1 deletion(-) create mode 100644 TheBank/BankTests/Infrastructure/CustomWebApplicationFactory.cs create mode 100644 TheBank/BankTests/WebApiControllersTests/BaseWebApiControllerTest.cs create mode 100644 TheBank/BankTests/WebApiControllersTests/ClerkControllerTests.cs create mode 100644 TheBank/BankTests/WebApiControllersTests/ClientControllerTests.cs diff --git a/TheBank/BankTests/BankTests.csproj b/TheBank/BankTests/BankTests.csproj index 6c2bc65..659bd2c 100644 --- a/TheBank/BankTests/BankTests.csproj +++ b/TheBank/BankTests/BankTests.csproj @@ -12,15 +12,18 @@ + + + diff --git a/TheBank/BankTests/Infrastructure/ConfigurationDatabase.cs b/TheBank/BankTests/Infrastructure/ConfigurationDatabase.cs index 665c4a0..f319e22 100644 --- a/TheBank/BankTests/Infrastructure/ConfigurationDatabase.cs +++ b/TheBank/BankTests/Infrastructure/ConfigurationDatabase.cs @@ -5,5 +5,5 @@ namespace BankTests.Infrastructure; internal class ConfigurationDatabase : IConfigurationDatabase { public string ConnectionString => - "Host=127.0.0.1;Port=5432;Database=TitanicTest;Username=postgres;Password=admin123;Include Error Detail=true"; + "Host=127.0.0.1;Port=5432;Database=TitanicTest;Username=postgres;Password=postgres;Include Error Detail=true"; } diff --git a/TheBank/BankTests/Infrastructure/CustomWebApplicationFactory.cs b/TheBank/BankTests/Infrastructure/CustomWebApplicationFactory.cs new file mode 100644 index 0000000..90ddbc7 --- /dev/null +++ b/TheBank/BankTests/Infrastructure/CustomWebApplicationFactory.cs @@ -0,0 +1,31 @@ +using BankContracts.Infrastructure; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace BankTests.Infrastructure; + +internal class CustomWebApplicationFactory : WebApplicationFactory + where TProgram : class +{ + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + builder.ConfigureServices(services => + { + var databaseConfig = services.SingleOrDefault(x => x.ServiceType == typeof(IConfigurationDatabase)); + if (databaseConfig is not null) + services.Remove(databaseConfig); + + var loggerFactory = services.SingleOrDefault(x => x.ServiceType == typeof(LoggerFactory)); + if (loggerFactory is not null) + services.Remove(loggerFactory); + + services.AddSingleton(); + }); + + builder.UseEnvironment("Development"); + + base.ConfigureWebHost(builder); + } +} diff --git a/TheBank/BankTests/WebApiControllersTests/BaseWebApiControllerTest.cs b/TheBank/BankTests/WebApiControllersTests/BaseWebApiControllerTest.cs new file mode 100644 index 0000000..38d38b8 --- /dev/null +++ b/TheBank/BankTests/WebApiControllersTests/BaseWebApiControllerTest.cs @@ -0,0 +1,68 @@ +using BankDatabase; +using BankTests.Infrastructure; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Serilog; +using System.Text; +using System.Text.Json; + +namespace BankTests.WebApiControllersTests; + +internal class BaseWebApiControllerTest +{ + private WebApplicationFactory _webApplication; + + protected HttpClient HttpClient { get; private set; } + + protected BankDbContext BankDbContext { get; private set; } + + protected static readonly JsonSerializerOptions JsonSerializerOptions = new() { PropertyNameCaseInsensitive = true }; + + [OneTimeSetUp] + public void OneTimeSetUp() + { + _webApplication = new CustomWebApplicationFactory(); + HttpClient = _webApplication + .WithWebHostBuilder(builder => + { + builder.ConfigureTestServices(services => + { + using var loggerFactory = new LoggerFactory(); + loggerFactory.AddSerilog(new LoggerConfiguration() + .ReadFrom.Configuration(new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json") + .Build()) + .CreateLogger()); + services.AddSingleton(loggerFactory); + }); + }) + .CreateClient(); + + var request = HttpClient.GetAsync("/login/user").GetAwaiter().GetResult(); + var data = request.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + HttpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {data}"); + + BankDbContext = _webApplication.Services.GetRequiredService(); + BankDbContext.Database.EnsureDeleted(); + BankDbContext.Database.EnsureCreated(); + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { + BankDbContext?.Database.EnsureDeleted(); + BankDbContext?.Dispose(); + HttpClient?.Dispose(); + _webApplication?.Dispose(); + } + + protected static async Task GetModelFromResponseAsync(HttpResponseMessage response) => + JsonSerializer.Deserialize(await response.Content.ReadAsStringAsync(), JsonSerializerOptions); + + protected static StringContent MakeContent(object model) => + new(JsonSerializer.Serialize(model), Encoding.UTF8, "application/json"); +} diff --git a/TheBank/BankTests/WebApiControllersTests/ClerkControllerTests.cs b/TheBank/BankTests/WebApiControllersTests/ClerkControllerTests.cs new file mode 100644 index 0000000..aba4299 --- /dev/null +++ b/TheBank/BankTests/WebApiControllersTests/ClerkControllerTests.cs @@ -0,0 +1,177 @@ +using BankContracts.BindingModels; +using BankContracts.ViewModels; +using BankDatabase.Models; +using BankTests.Infrastructure; +using System.Net; +using System.Net.Http.Json; + +namespace BankTests.WebApiControllersTests; + +[TestFixture] +internal class ClerkControllerTests : BaseWebApiControllerTest +{ + [TearDown] + public void TearDown() + { + BankDbContext.RemoveClerksFromDatabase(); + } + + [Test] + public async Task GetAllRecords_WhenHaveRecords_ShouldSuccess_Test() + { + // Arrange + var clerk1 = BankDbContext.InsertClerkToDatabaseAndReturn(name: "Иван", surname: "Иванов", email: "ivanov1@mail.com", login: "zal***", phone: "+7-777-777-78-90"); + var clerk2 = BankDbContext.InsertClerkToDatabaseAndReturn(name: "Петр", surname: "Петров", email: "petrov2@mail.com", login: "nepupkin", phone: "+7-777-777-78-91"); + // Act + var response = await HttpClient.GetAsync("/api/clerks"); + // Assert + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); + var data = await GetModelFromResponseAsync>(response); + Assert.That(data, Is.Not.Null); + Assert.That(data, Has.Count.EqualTo(2)); + AssertElement(data.First(x => x.Id == clerk1.Id), clerk1); + AssertElement(data.First(x => x.Id == clerk2.Id), clerk2); + } + + [Test] + public async Task GetAllRecords_WhenNoRecords_ShouldSuccess_Test() + { + // Act + var response = await HttpClient.GetAsync("/api/clerks"); + // Assert + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); + var data = await GetModelFromResponseAsync>(response); + Assert.That(data, Is.Not.Null); + Assert.That(data, Has.Count.EqualTo(0)); + } + + [Test] + public async Task GetRecord_ById_WhenHaveRecord_ShouldSuccess_Test() + { + // Arrange + var clerk = BankDbContext.InsertClerkToDatabaseAndReturn(); + // Act + var response = await HttpClient.GetAsync($"/api/clerks/{clerk.Id}"); + // Assert + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); + AssertElement(await GetModelFromResponseAsync(response), clerk); + } + + [Test] + public async Task GetRecord_ById_WhenNoRecord_ShouldNotFound_Test() + { + // Act + var response = await HttpClient.GetAsync($"/api/clerks/{Guid.NewGuid()}"); + // Assert + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound)); + } + + [Test] + public async Task Post_ShouldSuccess_Test() + { + // Arrange + var model = CreateModel(); + // Act + var response = await HttpClient.PostAsJsonAsync("/api/clerks", model); + // Assert + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent)); + AssertElement(BankDbContext.GetClerkFromDatabase(model.Id!), model); + } + + [Test] + public async Task Post_WhenHaveRecordWithSameId_ShouldBadRequest_Test() + { + // Arrange + var model = CreateModel(); + BankDbContext.InsertClerkToDatabaseAndReturn(id: model.Id); + // Act + var response = await HttpClient.PostAsJsonAsync("/api/clerks", model); + // Assert + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest)); + } + + [Test] + public async Task Post_WhenSendEmptyData_ShouldBadRequest_Test() + { + // Act + var response = await HttpClient.PostAsync("/api/clerks", MakeContent(string.Empty)); + // Assert + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest)); + } + + [Test] + public async Task Put_ShouldSuccess_Test() + { + // Arrange + var model = CreateModel(); + BankDbContext.InsertClerkToDatabaseAndReturn(id: model.Id); + // Act + var response = await HttpClient.PutAsJsonAsync("/api/clerks", model); + // Assert + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent)); + BankDbContext.ChangeTracker.Clear(); + AssertElement(BankDbContext.GetClerkFromDatabase(model.Id!), model); + } + + [Test] + public async Task Put_WhenNoFoundRecord_ShouldBadRequest_Test() + { + // Arrange + var model = CreateModel(); + // Act + var response = await HttpClient.PutAsJsonAsync("/api/clerks", model); + // Assert + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest)); + } + + private static void AssertElement(ClerkViewModel? actual, Clerk expected) + { + Assert.That(actual, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(actual.Id, Is.EqualTo(expected.Id)); + Assert.That(actual.Name, Is.EqualTo(expected.Name)); + Assert.That(actual.Surname, Is.EqualTo(expected.Surname)); + Assert.That(actual.MiddleName, Is.EqualTo(expected.MiddleName)); + Assert.That(actual.Login, Is.EqualTo(expected.Login)); + Assert.That(actual.Email, Is.EqualTo(expected.Email)); + Assert.That(actual.PhoneNumber, Is.EqualTo(expected.PhoneNumber)); + }); + } + + private static ClerkBindingModel CreateModel( + string? id = null, + string name = "Иван", + string surname = "Иванов", + string middleName = "Иванович", + string login = "ivanov", + string password = "password", + string email = "ivanov@mail.com", + string phoneNumber = "+79999999999") + => new() + { + Id = id ?? Guid.NewGuid().ToString(), + Name = name, + Surname = surname, + MiddleName = middleName, + Login = login, + Password = password, + Email = email, + PhoneNumber = phoneNumber + }; + + private static void AssertElement(Clerk? actual, ClerkBindingModel expected) + { + Assert.That(actual, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(actual.Id, Is.EqualTo(expected.Id)); + Assert.That(actual.Name, Is.EqualTo(expected.Name)); + Assert.That(actual.Surname, Is.EqualTo(expected.Surname)); + Assert.That(actual.MiddleName, Is.EqualTo(expected.MiddleName)); + Assert.That(actual.Login, Is.EqualTo(expected.Login)); + Assert.That(actual.Email, Is.EqualTo(expected.Email)); + Assert.That(actual.PhoneNumber, Is.EqualTo(expected.PhoneNumber)); + }); + } +} diff --git a/TheBank/BankTests/WebApiControllersTests/ClientControllerTests.cs b/TheBank/BankTests/WebApiControllersTests/ClientControllerTests.cs new file mode 100644 index 0000000..9e7c698 --- /dev/null +++ b/TheBank/BankTests/WebApiControllersTests/ClientControllerTests.cs @@ -0,0 +1,174 @@ +using BankContracts.BindingModels; +using BankContracts.ViewModels; +using BankDatabase; +using BankDatabase.Models; +using BankTests.Infrastructure; +using System.Net; +using System.Net.Http.Json; + +namespace BankTests.WebApiControllersTests; + +[TestFixture] +internal class ClientControllerTests : BaseWebApiControllerTest +{ + private Clerk _clerk; + + [SetUp] + public void SetUp() + { + _clerk = BankDbContext.InsertClerkToDatabaseAndReturn(); + } + + + [TearDown] + public void TearDown() + { + BankDbContext.RemoveClientsFromDatabase(); + BankDbContext.RemoveClerksFromDatabase(); + } + + [Test] + public async Task GetList_WhenHaveRecords_ShouldSuccess_Test() + { + // Arrange + var client1 = BankDbContext.InsertClientToDatabaseAndReturn(name: "Иван", surname: "Иванов", clerkId: _clerk.Id); + var client2 = BankDbContext.InsertClientToDatabaseAndReturn(name: "Петр", surname: "Петров", clerkId: _clerk.Id); + // Act + var response = await HttpClient.GetAsync("/api/clients/getrecords"); + // Assert + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); + var data = await GetModelFromResponseAsync>(response); + Assert.That(data, Is.Not.Null); + Assert.That(data, Has.Count.EqualTo(2)); + AssertElement(data.First(x => x.Id == client1.Id), client1); + AssertElement(data.First(x => x.Id == client2.Id), client2); + } + + [Test] + public async Task GetList_WhenNoRecords_ShouldSuccess_Test() + { + // Act + var response = await HttpClient.GetAsync("/api/clients/getrecords"); + // Assert + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); + var data = await GetModelFromResponseAsync>(response); + Assert.That(data, Is.Not.Null); + Assert.That(data, Has.Count.EqualTo(0)); + } + + [Test] + public async Task GetElement_ById_WhenHaveRecord_ShouldSuccess_Test() + { + // Arrange + var client = BankDbContext.InsertClientToDatabaseAndReturn(clerkId: _clerk.Id); + // Act + var response = await HttpClient.GetAsync($"/api/clients/getrecord/{client.Id}"); + // Assert + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); + AssertElement(await GetModelFromResponseAsync(response), client); + } + + [Test] + public async Task GetElement_ById_WhenNoRecord_ShouldNotFound_Test() + { + // Act + var response = await HttpClient.GetAsync($"/api/clients/getrecord/{Guid.NewGuid()}"); + // Assert + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound)); + } + + [Test] + public async Task Post_ShouldSuccess_Test() + { + // Arrange + var model = CreateModel(); + // Act + var response = await HttpClient.PostAsJsonAsync("/api/clients/register", model); + // Assert + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent)); + AssertElement(BankDbContext.GetClientFromDatabase(model.Id!), model); + } + + [Test] + public async Task Post_WhenHaveRecordWithSameId_ShouldBadRequest_Test() + { + // Arrange + var model = CreateModel(); + BankDbContext.InsertClientToDatabaseAndReturn(id: model.Id,clerkId: _clerk.Id); + // Act + var response = await HttpClient.PostAsJsonAsync("/api/clients/register", model); + // Assert + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest)); + } + + [Test] + public async Task Post_WhenSendEmptyData_ShouldBadRequest_Test() + { + // Act + var response = await HttpClient.PostAsync("/api/clients/register", MakeContent(string.Empty)); + // Assert + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest)); + } + + [Test] + public async Task Put_ShouldSuccess_Test() + { + // Arrange + var model = CreateModel(); + BankDbContext.InsertClientToDatabaseAndReturn(id: model.Id, clerkId: _clerk.Id); + // Act + var response = await HttpClient.PutAsJsonAsync("/api/clients/changeinfo", model); + // Assert + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent)); + BankDbContext.ChangeTracker.Clear(); + AssertElement(BankDbContext.GetClientFromDatabase(model.Id!), model); + } + + [Test] + public async Task ChangeInfo_WhenNoFoundRecord_ShouldBadRequest_Test() + { + // Arrange + var model = CreateModel(); + // Act + var response = await HttpClient.PutAsJsonAsync("/api/clients/changeinfo", model); + // Assert + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest)); + } + + private static void AssertElement(ClientViewModel? actual, Client expected) + { + Assert.That(actual, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(actual.Id, Is.EqualTo(expected.Id)); + Assert.That(actual.Name, Is.EqualTo(expected.Name)); + Assert.That(actual.Surname, Is.EqualTo(expected.Surname)); + Assert.That(actual.Balance, Is.EqualTo(expected.Balance)); + Assert.That(actual.ClerkId, Is.EqualTo(expected.ClerkId)); + }); + } + + private static ClientBindingModel CreateModel(string? id = null, string name = "Иван", string surname = "Иванов", decimal balance = 1000, string? clerkId = null) + => new() + { + Id = id ?? Guid.NewGuid().ToString(), + Name = name, + Surname = surname, + Balance = balance, + ClerkId = clerkId + }; + + private static void AssertElement(Client? actual, ClientBindingModel expected) + { + Assert.That(actual, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(actual.Id, Is.EqualTo(expected.Id)); + Assert.That(actual.Name, Is.EqualTo(expected.Name)); + Assert.That(actual.Surname, Is.EqualTo(expected.Surname)); + Assert.That(actual.Balance, Is.EqualTo(expected.Balance)); + Assert.That(actual.ClerkId, Is.EqualTo(expected.ClerkId)); + }); + } + +}