Основа + покупатель

This commit is contained in:
2025-03-09 10:53:04 +04:00
parent ba060a7e21
commit 9ed1d7b442
31 changed files with 1216 additions and 44 deletions

View File

@@ -8,6 +8,7 @@
<ItemGroup>
<InternalsVisibleTo Include="CatHasPawsTests" />
<InternalsVisibleTo Include="CatHasPawsWebApi" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>

View File

@@ -0,0 +1,17 @@
using CatHasPawsContratcs.AdapterContracts.OperationResponses;
using CatHasPawsContratcs.BindingModels;
namespace CatHasPawsContratcs.AdapterContracts;
public interface IBuyerAdapter
{
BuyerOperationResponse GetList();
BuyerOperationResponse GetElement(string data);
BuyerOperationResponse RegisterBuyer(BuyerBindingModel buyerModel);
BuyerOperationResponse ChangeBuyerInfo(BuyerBindingModel buyerModel);
BuyerOperationResponse RemoveBuyer(string id);
}

View File

@@ -0,0 +1,19 @@
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.ViewModels;
namespace CatHasPawsContratcs.AdapterContracts.OperationResponses;
public class BuyerOperationResponse : OperationResponse
{
public static BuyerOperationResponse OK(List<BuyerViewModel> data) => OK<BuyerOperationResponse, List<BuyerViewModel>>(data);
public static BuyerOperationResponse OK(BuyerViewModel data) => OK<BuyerOperationResponse, BuyerViewModel>(data);
public static BuyerOperationResponse NoContent() => NoContent<BuyerOperationResponse>();
public static BuyerOperationResponse BadRequest(string message) => BadRequest<BuyerOperationResponse>(message);
public static BuyerOperationResponse NotFound(string message) => NotFound<BuyerOperationResponse>(message);
public static BuyerOperationResponse InternalServerError(string message) => InternalServerError<BuyerOperationResponse>(message);
}

View File

@@ -0,0 +1,12 @@
namespace CatHasPawsContratcs.BindingModels;
public class BuyerBindingModel
{
public string? Id { get; set; }
public string? FIO { get; set; }
public string? PhoneNumber { get; set; }
public double DiscountSize { get; set; }
}

View File

@@ -6,4 +6,9 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.3.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,37 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Net;
namespace CatHasPawsContratcs.Infrastructure;
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<TResult, TData>(TData data) where TResult : OperationResponse, new() => new() { StatusCode = HttpStatusCode.OK, Result = data };
protected static TResult NoContent<TResult>() where TResult : OperationResponse, new() => new() { StatusCode = HttpStatusCode.NoContent };
protected static TResult BadRequest<TResult>(string? errorMessage = null) where TResult : OperationResponse, new() => new() { StatusCode = HttpStatusCode.BadRequest, Result = errorMessage };
protected static TResult NotFound<TResult>(string? errorMessage = null) where TResult : OperationResponse, new() => new() { StatusCode = HttpStatusCode.NotFound, Result = errorMessage };
protected static TResult InternalServerError<TResult>(string? errorMessage = null) where TResult : OperationResponse, new() => new() { StatusCode = HttpStatusCode.InternalServerError, Result = errorMessage };
}

View File

@@ -0,0 +1,12 @@
namespace CatHasPawsContratcs.ViewModels;
public class BuyerViewModel
{
public required string Id { get; set; }
public required string FIO { get; set; }
public required string PhoneNumber { get; set; }
public double DiscountSize { get; set; }
}

View File

@@ -18,6 +18,7 @@
<ItemGroup>
<InternalsVisibleTo Include="CatHasPawsTests" />
<InternalsVisibleTo Include="CatHasPawsWebApi" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>

View File

@@ -20,6 +20,11 @@ internal class CatHasPawsDbContext(IConfigurationDatabase configurationDatabase)
modelBuilder.Entity<Buyer>().HasIndex(x => x.PhoneNumber).IsUnique();
modelBuilder.Entity<Sale>()
.HasOne(e => e.Buyer)
.WithMany(e => e.Sales)
.OnDelete(DeleteBehavior.SetNull);
modelBuilder.Entity<Manufacturer>().HasIndex(x => x.ManufacturerName).IsUnique();
modelBuilder.Entity<Post>()

View File

@@ -8,6 +8,8 @@ internal class SaleProduct
public int Count { get; set; }
public double Price { get; set; }
public Sale? Sale { get; set; }
public Product? Product { get; set; }

View File

@@ -8,8 +8,17 @@
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="NUnit" Version="4.2.2" />
@@ -21,6 +30,7 @@
<ProjectReference Include="..\CatHasPawsBusinessLogic\CatHasPawsBusinessLogic.csproj" />
<ProjectReference Include="..\CatHasPawsContratcs\CatHasPawsContratcs.csproj" />
<ProjectReference Include="..\CatHasPawsDatabase\CatHasPawsDatabase.csproj" />
<ProjectReference Include="..\CatHasPawsWebApi\CatHasPawsWebApi.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -0,0 +1,52 @@
using CatHasPawsContratcs.Enums;
using CatHasPawsDatabase;
using CatHasPawsDatabase.Models;
using Microsoft.EntityFrameworkCore;
namespace CatHasPawsTests.Infrastructure;
internal static class CatHasPawsDbContextExtensions
{
public static Buyer InsertBuyerToDatabaseAndReturn(this CatHasPawsDbContext dbContext, string? id = null, string fio = "test", string phoneNumber = "+7-777-777-77-77", double discountSize = 10)
{
var buyer = new Buyer() { Id = id ?? Guid.NewGuid().ToString(), FIO = fio, PhoneNumber = phoneNumber, DiscountSize = discountSize };
dbContext.Buyers.Add(buyer);
dbContext.SaveChanges();
return buyer;
}
public static Sale InsertSaleToDatabaseAndReturn(this CatHasPawsDbContext dbContext, string workerId, string? buyerId = null, DateTime? saleDate = null, double sum = 1, DiscountType discountType = DiscountType.OnSale, double discount = 0, bool isCancel = false, List<(string, int, double)>? products = null)
{
var sale = new Sale() { WorkerId = workerId, BuyerId = buyerId, SaleDate = saleDate ?? DateTime.UtcNow, Sum = sum, DiscountType = discountType, Discount = discount, IsCancel = isCancel, SaleProducts = [] };
if (products is not null)
{
foreach (var elem in products)
{
sale.SaleProducts.Add(new SaleProduct { ProductId = elem.Item1, SaleId = sale.Id, Count = elem.Item2, Price = elem.Item3 });
}
}
dbContext.Sales.Add(sale);
dbContext.SaveChanges();
return sale;
}
public static Worker InsertWorkerToDatabaseAndReturn(this CatHasPawsDbContext dbContext, string? id = null, string fio = "test", string? postId = null, DateTime? birthDate = null, DateTime? employmentDate = null, bool isDeleted = false)
{
var worker = new Worker() { Id = id ?? Guid.NewGuid().ToString(), FIO = fio, PostId = postId ?? Guid.NewGuid().ToString(), BirthDate = birthDate ?? DateTime.UtcNow.AddYears(-20), EmploymentDate = employmentDate ?? DateTime.UtcNow, IsDeleted = isDeleted };
dbContext.Workers.Add(worker);
dbContext.SaveChanges();
return worker;
}
public static Buyer? GetBuyerFromDatabase(this CatHasPawsDbContext dbContext, string id) => dbContext.Buyers.FirstOrDefault(x => x.Id == id);
public static Sale[] GetSalesByBuyerId(this CatHasPawsDbContext dbContext, string? buyerId) => [.. dbContext.Sales.Include(x => x.SaleProducts).Where(x => x.BuyerId == buyerId)];
public static void RemoveBuyersFromDatabase(this CatHasPawsDbContext dbContext) => dbContext.ExecuteSqlRaw("TRUNCATE \"Buyers\" CASCADE;");
public static void RemoveSalesFromDatabase(this CatHasPawsDbContext dbContext) => dbContext.ExecuteSqlRaw("TRUNCATE \"Sales\" CASCADE;");
public static void RemoveWorkersFromDatabase(this CatHasPawsDbContext dbContext) => dbContext.ExecuteSqlRaw("TRUNCATE \"Workers\" CASCADE;");
private static void ExecuteSqlRaw(this CatHasPawsDbContext dbContext, string command) => dbContext.Database.ExecuteSqlRaw(command);
}

View File

@@ -0,0 +1,31 @@
using CatHasPawsContratcs.Infrastructure;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace CatHasPawsTests.Infrastructure;
internal class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<TProgram>
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<IConfigurationDatabase, ConfigurationDatabaseTest>();
});
builder.UseEnvironment("Development");
base.ConfigureWebHost(builder);
}
}

View File

@@ -1,9 +1,8 @@
using CatHasPawsContratcs.DataModels;
using CatHasPawsContratcs.Enums;
using CatHasPawsContratcs.Exceptions;
using CatHasPawsDatabase.Implementations;
using CatHasPawsDatabase.Models;
using Microsoft.EntityFrameworkCore;
using CatHasPawsTests.Infrastructure;
namespace CatHasPawsTests.StoragesContracts;
@@ -21,17 +20,17 @@ internal class BuyerStorageContractTests : BaseStorageContractTest
[TearDown]
public void TearDown()
{
CatHasPawsDbContext.Database.ExecuteSqlRaw("TRUNCATE \"Sales\" CASCADE;");
CatHasPawsDbContext.Database.ExecuteSqlRaw("TRUNCATE \"Workers\" CASCADE;");
CatHasPawsDbContext.Database.ExecuteSqlRaw("TRUNCATE \"Buyers\" CASCADE;");
CatHasPawsDbContext.RemoveSalesFromDatabase();
CatHasPawsDbContext.RemoveWorkersFromDatabase();
CatHasPawsDbContext.RemoveBuyersFromDatabase();
}
[Test]
public void Try_GetList_WhenHaveRecords_Test()
{
var buyer = InsertBuyerToDatabaseAndReturn(Guid.NewGuid().ToString(), phoneNumber: "+5-555-555-55-55");
InsertBuyerToDatabaseAndReturn(Guid.NewGuid().ToString(), phoneNumber: "+6-666-666-66-66");
InsertBuyerToDatabaseAndReturn(Guid.NewGuid().ToString(), phoneNumber: "+7-777-777-77-77");
var buyer = CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn(phoneNumber: "+5-555-555-55-55");
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn(phoneNumber: "+6-666-666-66-66");
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn(phoneNumber: "+7-777-777-77-77");
var list = _buyerStorageContract.GetList();
Assert.That(list, Is.Not.Null);
Assert.That(list, Has.Count.EqualTo(3));
@@ -49,42 +48,42 @@ internal class BuyerStorageContractTests : BaseStorageContractTest
[Test]
public void Try_GetElementById_WhenHaveRecord_Test()
{
var buyer = InsertBuyerToDatabaseAndReturn(Guid.NewGuid().ToString());
var buyer = CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn();
AssertElement(_buyerStorageContract.GetElementById(buyer.Id), buyer);
}
[Test]
public void Try_GetElementById_WhenNoRecord_Test()
{
InsertBuyerToDatabaseAndReturn(Guid.NewGuid().ToString());
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn();
Assert.That(() => _buyerStorageContract.GetElementById(Guid.NewGuid().ToString()), Is.Null);
}
[Test]
public void Try_GetElementByFIO_WhenHaveRecord_Test()
{
var buyer = InsertBuyerToDatabaseAndReturn(Guid.NewGuid().ToString());
var buyer = CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn();
AssertElement(_buyerStorageContract.GetElementByFIO(buyer.FIO), buyer);
}
[Test]
public void Try_GetElementByFIO_WhenNoRecord_Test()
{
InsertBuyerToDatabaseAndReturn(Guid.NewGuid().ToString());
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn();
Assert.That(() => _buyerStorageContract.GetElementByFIO("New Fio"), Is.Null);
}
[Test]
public void Try_GetElementByPhoneNumber_WhenHaveRecord_Test()
{
var buyer = InsertBuyerToDatabaseAndReturn(Guid.NewGuid().ToString());
var buyer = CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn();
AssertElement(_buyerStorageContract.GetElementByPhoneNumber(buyer.PhoneNumber), buyer);
}
[Test]
public void Try_GetElementByPhoneNumber_WhenNoRecord_Test()
{
InsertBuyerToDatabaseAndReturn(Guid.NewGuid().ToString());
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn();
Assert.That(() => _buyerStorageContract.GetElementByPhoneNumber("+8-888-888-88-88"), Is.Null);
}
@@ -93,14 +92,14 @@ internal class BuyerStorageContractTests : BaseStorageContractTest
{
var buyer = CreateModel(Guid.NewGuid().ToString());
_buyerStorageContract.AddElement(buyer);
AssertElement(GetBuyerFromDatabase(buyer.Id), buyer);
AssertElement(CatHasPawsDbContext.GetBuyerFromDatabase(buyer.Id), buyer);
}
[Test]
public void Try_AddElement_WhenHaveRecordWithSameId_Test()
{
var buyer = CreateModel(Guid.NewGuid().ToString(), "New Fio", "+5-555-555-55-55", 500);
InsertBuyerToDatabaseAndReturn(buyer.Id);
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn(buyer.Id);
Assert.That(() => _buyerStorageContract.AddElement(buyer), Throws.TypeOf<ElementExistsException>());
}
@@ -108,7 +107,7 @@ internal class BuyerStorageContractTests : BaseStorageContractTest
public void Try_AddElement_WhenHaveRecordWithSamePhoneNumber_Test()
{
var buyer = CreateModel(Guid.NewGuid().ToString(), "New Fio", "+5-555-555-55-55", 500);
InsertBuyerToDatabaseAndReturn(Guid.NewGuid().ToString(), phoneNumber: buyer.PhoneNumber);
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn(phoneNumber: buyer.PhoneNumber);
Assert.That(() => _buyerStorageContract.AddElement(buyer), Throws.TypeOf<ElementExistsException>());
}
@@ -116,9 +115,9 @@ internal class BuyerStorageContractTests : BaseStorageContractTest
public void Try_UpdElement_Test()
{
var buyer = CreateModel(Guid.NewGuid().ToString(), "New Fio", "+5-555-555-55-55", 500);
InsertBuyerToDatabaseAndReturn(buyer.Id);
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn(buyer.Id);
_buyerStorageContract.UpdElement(buyer);
AssertElement(GetBuyerFromDatabase(buyer.Id), buyer);
AssertElement(CatHasPawsDbContext.GetBuyerFromDatabase(buyer.Id), buyer);
}
[Test]
@@ -131,39 +130,37 @@ internal class BuyerStorageContractTests : BaseStorageContractTest
public void Try_UpdElement_WhenHaveRecordWithSamePhoneNumber_Test()
{
var buyer = CreateModel(Guid.NewGuid().ToString(), "New Fio", "+5-555-555-55-55", 500);
InsertBuyerToDatabaseAndReturn(buyer.Id, phoneNumber: "+7-777-777-77-77");
InsertBuyerToDatabaseAndReturn(Guid.NewGuid().ToString(), phoneNumber: buyer.PhoneNumber);
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn(buyer.Id, phoneNumber: "+7-777-777-77-77");
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn(phoneNumber: buyer.PhoneNumber);
Assert.That(() => _buyerStorageContract.UpdElement(buyer), Throws.TypeOf<ElementExistsException>());
}
[Test]
public void Try_DelElement_Test()
{
var buyer = InsertBuyerToDatabaseAndReturn(Guid.NewGuid().ToString());
var buyer = CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn();
_buyerStorageContract.DelElement(buyer.Id);
var element = GetBuyerFromDatabase(buyer.Id);
Assert.That(element, Is.Null);
Assert.That(CatHasPawsDbContext.GetBuyerFromDatabase(buyer.Id), Is.Null);
}
[Test]
public void Try_DelElement_WhenHaveSalesByThisBuyer_Test()
{
var buyer = InsertBuyerToDatabaseAndReturn(Guid.NewGuid().ToString());
var workerId = Guid.NewGuid().ToString();
CatHasPawsDbContext.Workers.Add(new Worker() { Id = workerId, FIO = "test", PostId = Guid.NewGuid().ToString() });
CatHasPawsDbContext.Sales.Add(new Sale() { Id = Guid.NewGuid().ToString(), WorkerId = workerId, BuyerId = buyer.Id, Sum = 10, DiscountType = DiscountType.None, Discount = 0 });
CatHasPawsDbContext.Sales.Add(new Sale() { Id = Guid.NewGuid().ToString(), WorkerId = workerId, BuyerId = buyer.Id, Sum = 10, DiscountType = DiscountType.None, Discount = 0 });
CatHasPawsDbContext.SaveChanges();
var salesBeforeDelete = CatHasPawsDbContext.Sales.Where(x => x.BuyerId == buyer.Id).ToArray();
var buyer = CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn();
var worker = CatHasPawsDbContext.InsertWorkerToDatabaseAndReturn();
CatHasPawsDbContext.InsertSaleToDatabaseAndReturn(worker.Id, buyer.Id);
CatHasPawsDbContext.InsertSaleToDatabaseAndReturn(worker.Id, buyer.Id);
var salesBeforeDelete = CatHasPawsDbContext.GetSalesByBuyerId(buyer.Id);
_buyerStorageContract.DelElement(buyer.Id);
var element = GetBuyerFromDatabase(buyer.Id);
var salesAfterDelete = CatHasPawsDbContext.Sales.Where(x => x.BuyerId == buyer.Id).ToArray();
var element = CatHasPawsDbContext.GetBuyerFromDatabase(buyer.Id);
var salesAfterDelete = CatHasPawsDbContext.GetSalesByBuyerId(buyer.Id);
var salesNoBuyers = CatHasPawsDbContext.GetSalesByBuyerId(null);
Assert.Multiple(() =>
{
Assert.That(element, Is.Null);
Assert.That(salesBeforeDelete, Has.Length.EqualTo(2));
Assert.That(salesAfterDelete, Is.Empty);
Assert.That(CatHasPawsDbContext.Sales.Count(), Is.EqualTo(2));
Assert.That(salesNoBuyers, Has.Length.EqualTo(2));
});
}
@@ -173,14 +170,6 @@ internal class BuyerStorageContractTests : BaseStorageContractTest
Assert.That(() => _buyerStorageContract.DelElement(Guid.NewGuid().ToString()), Throws.TypeOf<ElementNotFoundException>());
}
private Buyer InsertBuyerToDatabaseAndReturn(string id, string fio = "test", string phoneNumber = "+7-777-777-77-77", double discountSize = 10)
{
var buyer = new Buyer() { Id = id, FIO = fio, PhoneNumber = phoneNumber, DiscountSize = discountSize };
CatHasPawsDbContext.Buyers.Add(buyer);
CatHasPawsDbContext.SaveChanges();
return buyer;
}
private static void AssertElement(BuyerDataModel? actual, Buyer expected)
{
Assert.That(actual, Is.Not.Null);
@@ -196,8 +185,6 @@ internal class BuyerStorageContractTests : BaseStorageContractTest
private static BuyerDataModel CreateModel(string id, string fio = "test", string phoneNumber = "+7-777-777-77-77", double discountSize = 10)
=> new(id, fio, phoneNumber, discountSize);
private Buyer? GetBuyerFromDatabase(string id) => CatHasPawsDbContext.Buyers.FirstOrDefault(x => x.Id == id);
private static void AssertElement(Buyer? actual, BuyerDataModel expected)
{
Assert.That(actual, Is.Not.Null);

View File

@@ -0,0 +1,68 @@
using CatHasPawsDatabase;
using CatHasPawsTests.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 CatHasPawsTests.WebApiControllersTests;
internal class BaseWebApiControllerTest
{
private WebApplicationFactory<Program> _webApplication;
protected HttpClient HttpClient { get; private set; }
protected CatHasPawsDbContext CatHasPawsDbContext { get; private set; }
protected static readonly JsonSerializerOptions JsonSerializerOptions = new() { PropertyNameCaseInsensitive = true };
[OneTimeSetUp]
public void OneTimeSetUp()
{
_webApplication = new CustomWebApplicationFactory<Program>();
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}");
CatHasPawsDbContext = _webApplication.Services.GetRequiredService<CatHasPawsDbContext>();
CatHasPawsDbContext.Database.EnsureDeleted();
CatHasPawsDbContext.Database.EnsureCreated();
}
[OneTimeTearDown]
public void OneTimeTearDown()
{
CatHasPawsDbContext?.Database.EnsureDeleted();
CatHasPawsDbContext?.Dispose();
HttpClient?.Dispose();
_webApplication?.Dispose();
}
protected static async Task<T?> GetModelFromResponseAsync<T>(HttpResponseMessage response) =>
JsonSerializer.Deserialize<T>(await response.Content.ReadAsStringAsync(), JsonSerializerOptions);
protected static StringContent MakeContent(object model) =>
new(JsonSerializer.Serialize(model), Encoding.UTF8, "application/json");
}

View File

@@ -0,0 +1,371 @@
using CatHasPawsContratcs.BindingModels;
using CatHasPawsContratcs.ViewModels;
using CatHasPawsDatabase;
using CatHasPawsDatabase.Models;
using CatHasPawsTests.Infrastructure;
using System.Net;
namespace CatHasPawsTests.WebApiControllersTests;
[TestFixture]
internal class BuyerControllerTests : BaseWebApiControllerTest
{
[TearDown]
public void TearDown()
{
CatHasPawsDbContext.RemoveSalesFromDatabase();
CatHasPawsDbContext.RemoveWorkersFromDatabase();
CatHasPawsDbContext.RemoveBuyersFromDatabase();
}
[Test]
public async Task GetList_WhenHaveRecords_ShouldSuccess_Test()
{
//Arrange
var buyer = CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn(phoneNumber: "+5-555-555-55-55");
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn(phoneNumber: "+6-666-666-66-66");
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn(phoneNumber: "+7-777-777-77-77");
//Act
var response = await HttpClient.GetAsync("/api/buyers");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
var data = await GetModelFromResponseAsync<List<BuyerViewModel>>(response);
Assert.Multiple(() =>
{
Assert.That(data, Is.Not.Null);
Assert.That(data, Has.Count.EqualTo(3));
});
AssertElement(data.First(x => x.Id == buyer.Id), buyer);
}
[Test]
public async Task GetList_WhenNoRecords_ShouldSuccess_Test()
{
//Act
var response = await HttpClient.GetAsync("/api/buyers");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
var data = await GetModelFromResponseAsync<List<BuyerViewModel>>(response);
Assert.Multiple(() =>
{
Assert.That(data, Is.Not.Null);
Assert.That(data, Has.Count.EqualTo(0));
});
}
[Test]
public async Task GetElement_ById_WhenHaveRecord_ShouldSuccess_Test()
{
//Arrange
var buyer = CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn();
//Act
var response = await HttpClient.GetAsync($"/api/buyers/{buyer.Id}");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
AssertElement(await GetModelFromResponseAsync<BuyerViewModel>(response), buyer);
}
[Test]
public async Task GetElement_ById_WhenNoRecord_ShouldNotFound_Test()
{
//Arrange
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn();
//Act
var response = await HttpClient.GetAsync($"/api/buyers/{Guid.NewGuid()}");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
[Test]
public async Task GetElement_ByFIO_WhenHaveRecord_ShouldSuccess_Test()
{
//Arrange
var buyer = CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn();
//Act
var response = await HttpClient.GetAsync($"/api/buyers/{buyer.FIO}");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
AssertElement(await GetModelFromResponseAsync<BuyerViewModel>(response), buyer);
}
[Test]
public async Task GetElement_ByFIO_WhenNoRecord_ShouldNotFound_Test()
{
//Arrange
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn();
//Act
var response = await HttpClient.GetAsync($"/api/buyers/New%20Fio");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
[Test]
public async Task GetElement_ByPhoneNumber_WhenHaveRecord_ShouldSuccess_Test()
{
//Arrange
var buyer = CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn();
//Act
var response = await HttpClient.GetAsync($"/api/buyers/{buyer.PhoneNumber}");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
AssertElement(await GetModelFromResponseAsync<BuyerViewModel>(response), buyer);
}
[Test]
public async Task GetElement_ByPhoneNumber_WhenNoRecord_ShouldNotFound_Test()
{
//Arrange
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn();
//Act
var response = await HttpClient.GetAsync($"/api/buyers/+8-888-888-88-88");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
[Test]
public async Task Post_ShouldSuccess_Test()
{
//Arrange
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn();
var buyerModel = CreateBindingModel();
//Act
var response = await HttpClient.PostAsync($"/api/buyers", MakeContent(buyerModel));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
AssertElement(CatHasPawsDbContext.GetBuyerFromDatabase(buyerModel.Id!), buyerModel);
}
[Test]
public async Task Post_WhenHaveRecordWithSameId_ShouldBadRequest_Test()
{
//Arrange
var buyerModel = CreateBindingModel();
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn(buyerModel.Id);
//Act
var response = await HttpClient.PostAsync($"/api/buyers", MakeContent(buyerModel));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Post_WhenHaveRecordWithSamePhoneNumber_ShouldBadRequest_Test()
{
//Arrange
var buyerModel = CreateBindingModel();
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn(phoneNumber: buyerModel.PhoneNumber!);
//Act
var response = await HttpClient.PostAsync($"/api/buyers", MakeContent(buyerModel));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Post_WhenDataIsIncorrect_ShouldBadRequest_Test()
{
//Arrange
var buyerModelWithIdIncorrect = new BuyerBindingModel { Id = "Id", FIO = "fio", PhoneNumber = "+7-111-111-11-11", DiscountSize = 10 };
var buyerModelWithFioIncorrect = new BuyerBindingModel { Id = Guid.NewGuid().ToString(), FIO = string.Empty, PhoneNumber = "+7-111-111-11-11", DiscountSize = 10 };
var buyerModelWithPhoneNumberIncorrect = new BuyerBindingModel { Id = Guid.NewGuid().ToString(), FIO = "fio", PhoneNumber = "71", DiscountSize = 10 };
//Act
var responseWithIdIncorrect = await HttpClient.PostAsync($"/api/buyers", MakeContent(buyerModelWithIdIncorrect));
var responseWithFioIncorrect = await HttpClient.PostAsync($"/api/buyers", MakeContent(buyerModelWithFioIncorrect));
var responseWithPhoneNumberIncorrect = await HttpClient.PostAsync($"/api/buyers", MakeContent(buyerModelWithPhoneNumberIncorrect));
//Assert
Assert.Multiple(() =>
{
Assert.That(responseWithIdIncorrect.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest), "Id is incorrect");
Assert.That(responseWithFioIncorrect.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest), "Fio is incorrect");
Assert.That(responseWithPhoneNumberIncorrect.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest), "PhoneNumber is incorrect");
});
}
[Test]
public async Task Post_WhenSendEmptyData_ShouldBadRequest_Test()
{
//Act
var response = await HttpClient.PostAsync($"/api/buyers", MakeContent(string.Empty));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Post_WhenSendWrongFormatData_ShouldBadRequest_Test()
{
//Act
var response = await HttpClient.PostAsync($"/api/buyers", MakeContent(new { Data = "test", Position = 10 }));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Put_ShouldSuccess_Test()
{
//Arrange
var buyerModel = CreateBindingModel(fio: "new fio");
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn(buyerModel.Id);
//Act
var response = await HttpClient.PutAsync($"/api/buyers", MakeContent(buyerModel));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
CatHasPawsDbContext.ChangeTracker.Clear();
AssertElement(CatHasPawsDbContext.GetBuyerFromDatabase(buyerModel.Id!), buyerModel);
}
[Test]
public async Task Put_WhenNoFoundRecord_ShouldBadRequest_Test()
{
//Arrange
var buyerModel = CreateBindingModel(fio: "new fio");
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn();
//Act
var response = await HttpClient.PutAsync($"/api/buyers", MakeContent(buyerModel));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Put_WhenHaveRecordWithSamePhoneNumber_ShouldBadRequest_Test()
{
//Arrange
var buyerModel = CreateBindingModel(fio: "new fio");
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn(buyerModel.Id);
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn(phoneNumber: buyerModel.PhoneNumber!);
//Act
var response = await HttpClient.PostAsync($"/api/buyers", MakeContent(buyerModel));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Put_WhenDataIsIncorrect_ShouldBadRequest_Test()
{
//Arrange
var buyerModelWithIdIncorrect = new BuyerBindingModel { Id = "Id", FIO = "fio", PhoneNumber = "+7-111-111-11-11", DiscountSize = 10 };
var buyerModelWithFioIncorrect = new BuyerBindingModel { Id = Guid.NewGuid().ToString(), FIO = string.Empty, PhoneNumber = "+7-111-111-11-11", DiscountSize = 10 };
var buyerModelWithPhoneNumberIncorrect = new BuyerBindingModel { Id = Guid.NewGuid().ToString(), FIO = "fio", PhoneNumber = "71", DiscountSize = 10 };
//Act
var responseWithIdIncorrect = await HttpClient.PutAsync($"/api/buyers", MakeContent(buyerModelWithIdIncorrect));
var responseWithFioIncorrect = await HttpClient.PutAsync($"/api/buyers", MakeContent(buyerModelWithFioIncorrect));
var responseWithPhoneNumberIncorrect = await HttpClient.PutAsync($"/api/buyers", MakeContent(buyerModelWithPhoneNumberIncorrect));
//Assert
Assert.Multiple(() =>
{
Assert.That(responseWithIdIncorrect.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest), "Id is incorrect");
Assert.That(responseWithFioIncorrect.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest), "Fio is incorrect");
Assert.That(responseWithPhoneNumberIncorrect.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest), "PhoneNumber is incorrect");
});
}
[Test]
public async Task Put_WhenSendEmptyData_ShouldBadRequest_Test()
{
//Act
var response = await HttpClient.PutAsync($"/api/buyers", MakeContent(string.Empty));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Put_WhenSendWrongFormatData_ShouldBadRequest_Test()
{
//Act
var response = await HttpClient.PutAsync($"/api/buyers", MakeContent(new { Data = "test", Position = 10 }));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Delete_ShouldSuccess_Test()
{
//Arrange
var buyerId = Guid.NewGuid().ToString();
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn(buyerId);
//Act
var response = await HttpClient.DeleteAsync($"/api/buyers/{buyerId}");
//Assert
Assert.Multiple(() =>
{
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
Assert.That(CatHasPawsDbContext.GetBuyerFromDatabase(buyerId), Is.Null);
});
}
[Test]
public async Task Delete_WhenHaveSalesByThisBuyer_ShouldSuccess_Test()
{
//Arrange
var buyer = CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn();
var worker = CatHasPawsDbContext.InsertWorkerToDatabaseAndReturn();
CatHasPawsDbContext.InsertSaleToDatabaseAndReturn(worker.Id, buyer.Id);
CatHasPawsDbContext.InsertSaleToDatabaseAndReturn(worker.Id, buyer.Id);
var salesBeforeDelete = CatHasPawsDbContext.GetSalesByBuyerId(buyer.Id);
//Act
var response = await HttpClient.DeleteAsync($"/api/buyers/{buyer.Id}");
//Assert
CatHasPawsDbContext.ChangeTracker.Clear();
var element = CatHasPawsDbContext.GetBuyerFromDatabase(buyer.Id);
var salesAfterDelete = CatHasPawsDbContext.GetSalesByBuyerId(buyer.Id);
var salesNoBuyers = CatHasPawsDbContext.GetSalesByBuyerId(null);
Assert.Multiple(() =>
{
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
Assert.That(element, Is.Null);
Assert.That(salesBeforeDelete, Has.Length.EqualTo(2));
Assert.That(salesAfterDelete, Is.Empty);
Assert.That(salesNoBuyers, Has.Length.EqualTo(2));
});
}
[Test]
public async Task Delete_WhenNoFoundRecord_ShouldBadRequest_Test()
{
//Arrange
CatHasPawsDbContext.InsertBuyerToDatabaseAndReturn();
//Act
var response = await HttpClient.DeleteAsync($"/api/buyers/{Guid.NewGuid()}");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Delete_WhenSendWrongFormatData_ShouldBadRequest_Test()
{
//Act
var response = await HttpClient.DeleteAsync($"/api/buyers/id");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
private static void AssertElement(BuyerViewModel? actual, Buyer expected)
{
Assert.That(actual, Is.Not.Null);
Assert.Multiple(() =>
{
Assert.That(actual.Id, Is.EqualTo(expected.Id));
Assert.That(actual.FIO, Is.EqualTo(expected.FIO));
Assert.That(actual.PhoneNumber, Is.EqualTo(expected.PhoneNumber));
Assert.That(actual.DiscountSize, Is.EqualTo(expected.DiscountSize));
});
}
private static BuyerBindingModel CreateBindingModel(string? id = null, string fio = "fio", string phoneNumber = "+7-666-666-66-66", double discountSize = 10) =>
new()
{
Id = id ?? Guid.NewGuid().ToString(),
FIO = fio,
PhoneNumber = phoneNumber,
DiscountSize = discountSize
};
private static void AssertElement(Buyer? actual, BuyerBindingModel expected)
{
Assert.That(actual, Is.Not.Null);
Assert.Multiple(() =>
{
Assert.That(actual.Id, Is.EqualTo(expected.Id));
Assert.That(actual.FIO, Is.EqualTo(expected.FIO));
Assert.That(actual.PhoneNumber, Is.EqualTo(expected.PhoneNumber));
Assert.That(actual.DiscountSize, Is.EqualTo(expected.DiscountSize));
});
}
}

View File

@@ -0,0 +1,24 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Serilog": {
"Using": [ "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information"
},
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "../logs/cathaspaws-.log",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} {CorrelationId} {Level:u3} {Username} {Message:lj}{Exception}{NewLine}"
}
}
]
}
}

View File

@@ -0,0 +1,189 @@
using AutoMapper;
using CatHasPawsContratcs.AdapterContracts;
using CatHasPawsContratcs.AdapterContracts.OperationResponses;
using CatHasPawsContratcs.BindingModels;
using CatHasPawsContratcs.BusinessLogicsContracts;
using CatHasPawsContratcs.DataModels;
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.ViewModels;
namespace CatHasPawsWebApi.Adapters;
public class BuyerAdapter : IBuyerAdapter
{
private readonly IBuyerBusinessLogicContract _buyerBusinessLogicContract;
private readonly ILogger _logger;
private readonly Mapper _mapper;
public BuyerAdapter(IBuyerBusinessLogicContract buyerBusinessLogicContract, ILogger<BuyerAdapter> logger)
{
_buyerBusinessLogicContract = buyerBusinessLogicContract;
_logger = logger;
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<BuyerBindingModel, BuyerDataModel>();
cfg.CreateMap<BuyerDataModel, BuyerViewModel>();
});
_mapper = new Mapper(config);
}
public BuyerOperationResponse GetList()
{
try
{
return BuyerOperationResponse.OK([.. _buyerBusinessLogicContract.GetAllBuyers().Select(x => _mapper.Map<BuyerViewModel>(x))]);
}
catch (NullListException)
{
_logger.LogError("NullListException");
return BuyerOperationResponse.NotFound("The list is not initialized");
}
catch (StorageException ex)
{
_logger.LogError(ex, "StorageException");
return BuyerOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException!.Message}");
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception");
return BuyerOperationResponse.InternalServerError(ex.Message);
}
}
public BuyerOperationResponse GetElement(string data)
{
try
{
return BuyerOperationResponse.OK(_mapper.Map<BuyerViewModel>(_buyerBusinessLogicContract.GetBuyerByData(data)));
}
catch (ArgumentNullException ex)
{
_logger.LogError(ex, "ArgumentNullException");
return BuyerOperationResponse.BadRequest("Data is empty");
}
catch (ElementNotFoundException ex)
{
_logger.LogError(ex, "ElementNotFoundException");
return BuyerOperationResponse.NotFound($"Not found element by data {data}");
}
catch (StorageException ex)
{
_logger.LogError(ex, "StorageException");
return BuyerOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException!.Message}");
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception");
return BuyerOperationResponse.InternalServerError(ex.Message);
}
}
public BuyerOperationResponse RegisterBuyer(BuyerBindingModel buyerModel)
{
try
{
_buyerBusinessLogicContract.InsertBuyer(_mapper.Map<BuyerDataModel>(buyerModel));
return BuyerOperationResponse.NoContent();
}
catch (ArgumentNullException ex)
{
_logger.LogError(ex, "ArgumentNullException");
return BuyerOperationResponse.BadRequest("Data is empty");
}
catch (ValidationException ex)
{
_logger.LogError(ex, "ValidationException");
return BuyerOperationResponse.BadRequest($"Incorrect data transmitted: {ex.Message}");
}
catch (ElementExistsException ex)
{
_logger.LogError(ex, "ElementExistsException");
return BuyerOperationResponse.BadRequest(ex.Message);
}
catch (StorageException ex)
{
_logger.LogError(ex, "StorageException");
return BuyerOperationResponse.BadRequest($"Error while working with data storage: {ex.InnerException!.Message}");
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception");
return BuyerOperationResponse.InternalServerError(ex.Message);
}
}
public BuyerOperationResponse ChangeBuyerInfo(BuyerBindingModel buyerModel)
{
try
{
_buyerBusinessLogicContract.UpdateBuyer(_mapper.Map<BuyerDataModel>(buyerModel));
return BuyerOperationResponse.NoContent();
}
catch (ArgumentNullException ex)
{
_logger.LogError(ex, "ArgumentNullException");
return BuyerOperationResponse.BadRequest("Data is empty");
}
catch (ValidationException ex)
{
_logger.LogError(ex, "ValidationException");
return BuyerOperationResponse.BadRequest($"Incorrect data transmitted: {ex.Message}");
}
catch (ElementNotFoundException ex)
{
_logger.LogError(ex, "ElementNotFoundException");
return BuyerOperationResponse.BadRequest($"Not found element by Id {buyerModel.Id}");
}
catch (ElementExistsException ex)
{
_logger.LogError(ex, "ElementExistsException");
return BuyerOperationResponse.BadRequest(ex.Message);
}
catch (StorageException ex)
{
_logger.LogError(ex, "StorageException");
return BuyerOperationResponse.BadRequest($"Error while working with data storage: {ex.InnerException!.Message}");
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception");
return BuyerOperationResponse.InternalServerError(ex.Message);
}
}
public BuyerOperationResponse RemoveBuyer(string id)
{
try
{
_buyerBusinessLogicContract.DeleteBuyer(id);
return BuyerOperationResponse.NoContent();
}
catch (ArgumentNullException ex)
{
_logger.LogError(ex, "ArgumentNullException");
return BuyerOperationResponse.BadRequest("Id is empty");
}
catch (ValidationException ex)
{
_logger.LogError(ex, "ValidationException");
return BuyerOperationResponse.BadRequest($"Incorrect data transmitted: {ex.Message}");
}
catch (ElementNotFoundException ex)
{
_logger.LogError(ex, "ElementNotFoundException");
return BuyerOperationResponse.BadRequest($"Not found element by id: {id}");
}
catch (StorageException ex)
{
_logger.LogError(ex, "StorageException");
return BuyerOperationResponse.BadRequest($"Error while working with data storage: {ex.InnerException!.Message}");
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception");
return BuyerOperationResponse.InternalServerError(ex.Message);
}
}
}

View File

@@ -0,0 +1,12 @@
using Microsoft.IdentityModel.Tokens;
using System.Text;
namespace CatHasPawsWebApi;
public class AuthOptions
{
public const string ISSUER = "CatHasPaws_AuthServer"; // издатель токена
public const string AUDIENCE = "CatHasPaws_AuthClient"; // потребитель токена
const string KEY = "catsecret_secretsecretsecretkey!123"; // ключ для шифрации
public static SymmetricSecurityKey GetSymmetricSecurityKey() => new(Encoding.UTF8.GetBytes(KEY));
}

View File

@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.2" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.2" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CatHasPawsBusinessLogic\CatHasPawsBusinessLogic.csproj" />
<ProjectReference Include="..\CatHasPawsContratcs\CatHasPawsContratcs.csproj" />
<ProjectReference Include="..\CatHasPawsDatabase\CatHasPawsDatabase.csproj" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="CatHasPawsTests" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,6 @@
@CatHasPawsWebApi_HostAddress = http://localhost:5037
GET {{CatHasPawsWebApi_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@@ -0,0 +1,45 @@
using CatHasPawsContratcs.AdapterContracts;
using CatHasPawsContratcs.BindingModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace CatHasPawsWebApi.Controllers;
[Authorize]
[Route("api/[controller]")]
[ApiController]
[Produces("application/json")]
public class BuyersController(IBuyerAdapter adapter) : ControllerBase
{
private readonly IBuyerAdapter _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] BuyerBindingModel model)
{
return _adapter.RegisterBuyer(model).GetResponse(Request, Response);
}
[HttpPut]
public IActionResult ChangeInfo([FromBody] BuyerBindingModel model)
{
return _adapter.ChangeBuyerInfo(model).GetResponse(Request, Response);
}
[HttpDelete("{id}")]
public IActionResult Delete(string id)
{
return _adapter.RemoveBuyer(id).GetResponse(Request, Response);
}
}

View File

@@ -0,0 +1,34 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace CatHasPawsWebApi.Controllers;
[Authorize]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}

View File

@@ -0,0 +1,13 @@
using CatHasPawsContratcs.Infrastructure;
namespace CatHasPawsWebApi.Infrastructure;
public class ConfigurationDatabase(IConfiguration configuration) : IConfigurationDatabase
{
private readonly Lazy<DataBaseSettings> _dataBaseSettings = new(() =>
{
return configuration.GetValue<DataBaseSettings>("DataBaseSettings") ?? throw new InvalidDataException(nameof(DataBaseSettings));
});
public string ConnectionString => _dataBaseSettings.Value.ConnectionString;
}

View File

@@ -0,0 +1,6 @@
namespace CatHasPawsWebApi.Infrastructure;
public class DataBaseSettings
{
public required string ConnectionString { get; set; }
}

View File

@@ -0,0 +1,109 @@
using CatHasPawsBusinessLogic.Implementations;
using CatHasPawsContratcs.AdapterContracts;
using CatHasPawsContratcs.BusinessLogicsContracts;
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.StoragesContracts;
using CatHasPawsDatabase;
using CatHasPawsDatabase.Implementations;
using CatHasPawsWebApi;
using CatHasPawsWebApi.Adapters;
using CatHasPawsWebApi.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
{
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ValidateIssuer = true,
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ValidIssuer = AuthOptions.ISSUER,
// <20><><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ValidateAudience = true,
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ValidAudience = AuthOptions.AUDIENCE,
// <20><><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ValidateLifetime = true,
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
IssuerSigningKey = AuthOptions.GetSymmetricSecurityKey(),
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ValidateIssuerSigningKey = true,
};
});
builder.Services.AddSingleton<IConfigurationDatabase, ConfigurationDatabase>();
builder.Services.AddTransient<IBuyerBusinessLogicContract, BuyerBusinessLogicContract>();
builder.Services.AddTransient<IManufacturerBusinessLogicContract, ManufacturerBusinessLogicContract>();
builder.Services.AddTransient<IPostBusinessLogicContract, PostBusinessLogicContract>();
builder.Services.AddTransient<IProductBusinessLogicContract, ProductBusinessLogicContract>();
builder.Services.AddTransient<ISalaryBusinessLogicContract, SalaryBusinessLogicContract>();
builder.Services.AddTransient<ISaleBusinessLogicContract, SaleBusinessLogicContract>();
builder.Services.AddTransient<IWorkerBusinessLogicContract, WorkerBusinessLogicContract>();
builder.Services.AddTransient<CatHasPawsDbContext>();
builder.Services.AddTransient<IBuyerStorageContract, BuyerStorageContract>();
builder.Services.AddTransient<IManufacturerStorageContract, ManufacturerStorageContract>();
builder.Services.AddTransient<IPostStorageContract, PostStorageContract>();
builder.Services.AddTransient<IProductStorageContract, ProductStorageContract>();
builder.Services.AddTransient<ISalaryStorageContract, SalaryStorageContract>();
builder.Services.AddTransient<ISaleStorageContract, SaleStorageContract>();
builder.Services.AddTransient<IWorkerStorageContract, WorkerStorageContract>();
builder.Services.AddTransient<IBuyerAdapter, BuyerAdapter>();
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
if (app.Environment.IsProduction())
{
var dbContext = app.Services.GetRequiredService<CatHasPawsDbContext>();
if (dbContext.Database.CanConnect())
{
dbContext.Database.EnsureCreated();
dbContext.Database.Migrate();
}
}
app.UseHttpsRedirection();
app.UseAuthentication();
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();

View File

@@ -0,0 +1,23 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5037",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:7008;http://localhost:5037",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,13 @@
namespace CatHasPawsWebApi
{
public class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string? Summary { get; set; }
}
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -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/cathaspaws-.log",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} {CorrelationId} {Level:u3} {Username} {Message:lj}{Exception}{NewLine}"
}
}
]
},
"AllowedHosts": "*",
"DataBaseSettings": {
"ConnectionString": "Host=127.0.0.1;Port=5432;Database=CatHasPaws;Username=postgres;Password=postgres;"
}
}

View File

@@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatHasPawsBusinessLogic", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatHasPawsDatabase", "CatHasPawsDatabase\CatHasPawsDatabase.csproj", "{54BFB74B-3DA5-47C7-BF70-E8F6C3FFA334}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatHasPawsWebApi", "CatHasPawsWebApi\CatHasPawsWebApi.csproj", "{F6280B3C-8CF7-442F-A249-565690EE36CA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -33,6 +35,10 @@ Global
{54BFB74B-3DA5-47C7-BF70-E8F6C3FFA334}.Debug|Any CPU.Build.0 = Debug|Any CPU
{54BFB74B-3DA5-47C7-BF70-E8F6C3FFA334}.Release|Any CPU.ActiveCfg = Release|Any CPU
{54BFB74B-3DA5-47C7-BF70-E8F6C3FFA334}.Release|Any CPU.Build.0 = Release|Any CPU
{F6280B3C-8CF7-442F-A249-565690EE36CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F6280B3C-8CF7-442F-A249-565690EE36CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F6280B3C-8CF7-442F-A249-565690EE36CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F6280B3C-8CF7-442F-A249-565690EE36CA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE