From fa9dbb3f60124c712d58fdda01b5cfd92aad99fd Mon Sep 17 00:00:00 2001 From: DjonniStorm Date: Thu, 1 May 2025 17:25:49 +0400 Subject: [PATCH] =?UTF-8?q?feat!:=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0=20web?= =?UTF-8?q?=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