GetDataProductsByManufacturer

This commit is contained in:
2025-04-06 13:41:29 +04:00
parent e561eb91d3
commit dead17cca0
13 changed files with 255 additions and 7 deletions

View File

@@ -1,7 +1,21 @@
using CatHasPawsContratcs.BusinessLogicsContracts;
using CatHasPawsContratcs.DataModels;
using CatHasPawsContratcs.StoragesContracts;
using Microsoft.Extensions.Logging;
namespace CatHasPawsBusinessLogic.Implementations;
internal class ReportContract : IReportContract
internal class ReportContract(IProductStorageContract productStorageContract, ILogger logger) : IReportContract
{
private readonly IProductStorageContract _productStorageContract = productStorageContract;
private readonly ILogger _logger = logger;
public Task<List<ManufacturerProductDataModel>> GetDataProductsByManufacturerAsync(CancellationToken ct)
{
_logger.LogInformation("Get data ProductsByManufacturer");
return GetDataByProductsAsync(ct);
}
private async Task<List<ManufacturerProductDataModel>> GetDataByProductsAsync(CancellationToken ct) => [.. (await _productStorageContract.GetListAsync(ct)).GroupBy(x => x.ManufacturerName).Select(x => new ManufacturerProductDataModel { ManufacturerName = x.Key, Products = [.. x.Select(y => y.ProductName)] })];
}

View File

@@ -1,5 +1,9 @@
namespace CatHasPawsContratcs.AdapterContracts;
using CatHasPawsContratcs.AdapterContracts.OperationResponses;
using CatHasPawsContratcs.DataModels;
namespace CatHasPawsContratcs.AdapterContracts;
public interface IReportAdapter
{
Task<ReportOperationResponse> GetDataProductsByManufacturerAsync(CancellationToken ct);
}

View File

@@ -1,9 +1,11 @@
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.ViewModels;
namespace CatHasPawsContratcs.AdapterContracts.OperationResponses;
public class ReportOperationResponse : OperationResponse
{
public static ReportOperationResponse OK(List<ManufacturerProductViewModel> data) => OK<ReportOperationResponse, List<ManufacturerProductViewModel>>(data);
public static ReportOperationResponse BadRequest(string message) => BadRequest<ReportOperationResponse>(message);

View File

@@ -1,5 +1,8 @@
namespace CatHasPawsContratcs.BusinessLogicsContracts;
using CatHasPawsContratcs.DataModels;
namespace CatHasPawsContratcs.BusinessLogicsContracts;
public interface IReportContract
{
Task<List<ManufacturerProductDataModel>> GetDataProductsByManufacturerAsync(CancellationToken ct);
}

View File

@@ -0,0 +1,8 @@
namespace CatHasPawsContratcs.DataModels;
public class ManufacturerProductDataModel
{
public required string ManufacturerName { get; set; }
public required List<string> Products { get; set; }
}

View File

@@ -6,6 +6,8 @@ public interface IProductStorageContract
{
List<ProductDataModel> GetList(bool onlyActive = true, string? manufacturerId = null);
Task<List<ProductDataModel>> GetListAsync(CancellationToken ct);
List<ProductHistoryDataModel> GetHistoryByProductId(string productId);
ProductDataModel? GetElementById(string id);

View File

@@ -0,0 +1,8 @@
namespace CatHasPawsContratcs.ViewModels;
public class ManufacturerProductViewModel
{
public required string ManufacturerName { get; set; }
public required List<string> Products { get; set; }
}

View File

@@ -49,6 +49,19 @@ internal class ProductStorageContract : IProductStorageContract
}
}
public async Task<List<ProductDataModel>> GetListAsync(CancellationToken ct)
{
try
{
return [.. await _dbContext.Products.Include(x => x.Manufacturer).Select(x => _mapper.Map<ProductDataModel>(x)).ToListAsync(ct)];
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex);
}
}
public List<ProductHistoryDataModel> GetHistoryByProductId(string productId)
{
try

View File

@@ -0,0 +1,79 @@
using CatHasPawsBusinessLogic.Implementations;
using CatHasPawsContratcs.DataModels;
using CatHasPawsContratcs.Enums;
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.StoragesContracts;
using Microsoft.Extensions.Logging;
using Moq;
namespace CatHasPawsTests.BusinessLogicsContractsTests;
[TestFixture]
internal class ReportContractTests
{
private ReportContract _reportContract;
private Mock<IProductStorageContract> _productStorageContract;
[OneTimeSetUp]
public void OneTimeSetUp()
{
_productStorageContract = new Mock<IProductStorageContract>();
_reportContract = new ReportContract(_productStorageContract.Object, new Mock<ILogger>().Object);
}
[SetUp]
public void SetUp()
{
_productStorageContract.Reset();
}
[Test]
public async Task GetDataProductsByManufacturer_ShouldSuccess_Test()
{
//Arrange
var manufacturer1 = new ManufacturerDataModel(Guid.NewGuid().ToString(), "name1");
var manufacturer2 = new ManufacturerDataModel(Guid.NewGuid().ToString(), "name2");
_productStorageContract.Setup(x => x.GetListAsync(It.IsAny<CancellationToken>())).Returns(Task.FromResult(new List<ProductDataModel>()
{
new(Guid.NewGuid().ToString(), "name 1.1", ProductType.Accessory, manufacturer1.Id, 10, false, manufacturer1),
new(Guid.NewGuid().ToString(), "name 2.1", ProductType.Accessory, manufacturer2.Id, 10, false, manufacturer2),
new(Guid.NewGuid().ToString(), "name 1.2", ProductType.Accessory, manufacturer1.Id, 10, false, manufacturer1),
new(Guid.NewGuid().ToString(), "name 1.3", ProductType.Accessory, manufacturer1.Id, 10, false, manufacturer1),
new(Guid.NewGuid().ToString(), "name 2.2", ProductType.Accessory, manufacturer2.Id, 10, false, manufacturer2)
}));
//Act
var data = await _reportContract.GetDataProductsByManufacturerAsync(CancellationToken.None);
//Assert
Assert.That(data, Is.Not.Null);
Assert.That(data, Has.Count.EqualTo(2));
Assert.Multiple(() =>
{
Assert.That(data.First(x => x.ManufacturerName == manufacturer1.ManufacturerName).Products, Has.Count.EqualTo(3));
Assert.That(data.First(x => x.ManufacturerName == manufacturer2.ManufacturerName).Products, Has.Count.EqualTo(2));
});
_productStorageContract.Verify(x => x.GetListAsync(It.IsAny<CancellationToken>()), Times.Once);
}
[Test]
public async Task GetDataProductsByManufacturer_WhenNoRecords_ShouldSuccess_Test()
{
//Arrange
_productStorageContract.Setup(x => x.GetListAsync(It.IsAny<CancellationToken>())).Returns(Task.FromResult(new List<ProductDataModel>()));
//Act
var data = await _reportContract.GetDataProductsByManufacturerAsync(CancellationToken.None);
//Assert
Assert.That(data, Is.Not.Null);
Assert.That(data, Has.Count.EqualTo(0));
_productStorageContract.Verify(x => x.GetListAsync(It.IsAny<CancellationToken>()), Times.Once);
}
[Test]
public void GetDataProductsByManufacturer_WhenStorageThrowError_ShouldFail_Test()
{
//Arrange
_productStorageContract.Setup(x => x.GetListAsync(It.IsAny<CancellationToken>())).Throws(new StorageException(new InvalidOperationException()));
//Act&Assert
Assert.That(async () => await _reportContract.GetDataProductsByManufacturerAsync(CancellationToken.None), Throws.TypeOf<StorageException>());
_productStorageContract.Verify(x => x.GetListAsync(It.IsAny<CancellationToken>()), Times.Once);
}
}

View File

@@ -47,6 +47,26 @@ internal class ProductStorageContractTests : BaseStorageContractTest
Assert.That(list, Is.Empty);
}
[Test]
public async Task Try_GetListAsync_WhenHaveRecords_Test()
{
var product = CatHasPawsDbContext.InsertProductToDatabaseAndReturn(_manufacturer.Id, productName: "name 1");
CatHasPawsDbContext.InsertProductToDatabaseAndReturn(_manufacturer.Id, productName: "name 2");
CatHasPawsDbContext.InsertProductToDatabaseAndReturn(_manufacturer.Id, productName: "name 3");
var list = await _productStorageContract.GetListAsync(CancellationToken.None);
Assert.That(list, Is.Not.Null);
Assert.That(list, Has.Count.EqualTo(3));
AssertElement(list.First(x => x.Id == product.Id), product);
}
[Test]
public async Task Try_GetListAsync_WhenNoRecords_Test()
{
var list = await _productStorageContract.GetListAsync(CancellationToken.None);
Assert.That(list, Is.Not.Null);
Assert.That(list, Is.Empty);
}
[Test]
public void Try_GetList_OnlyActual_Test()
{

View File

@@ -0,0 +1,41 @@
using CatHasPawsContratcs.ViewModels;
using CatHasPawsTests.Infrastructure;
using System.Net;
namespace CatHasPawsTests.WebApiControllersTests;
[TestFixture]
internal class ReportControllerTests : BaseWebApiControllerTest
{
[TearDown]
public void TearDown()
{
CatHasPawsDbContext.RemoveProductsFromDatabase();
CatHasPawsDbContext.RemoveManufacturersFromDatabase();
}
[Test]
public async Task GetProducts_WhenHaveRecords_ShouldSuccess_Test()
{
//Arrange
var manufacturer1 = CatHasPawsDbContext.InsertManufacturerToDatabaseAndReturn(manufacturerName: "name 1");
var manufacturer2 = CatHasPawsDbContext.InsertManufacturerToDatabaseAndReturn(manufacturerName: "name 2");
CatHasPawsDbContext.InsertProductToDatabaseAndReturn(manufacturer1.Id, productName: "name 1.1");
CatHasPawsDbContext.InsertProductToDatabaseAndReturn(manufacturer1.Id, productName: "name 1.2");
CatHasPawsDbContext.InsertProductToDatabaseAndReturn(manufacturer1.Id, productName: "name 1.3");
CatHasPawsDbContext.InsertProductToDatabaseAndReturn(manufacturer2.Id, productName: "name 2.1");
CatHasPawsDbContext.InsertProductToDatabaseAndReturn(manufacturer2.Id, productName: "name 2.2");
//Act
var response = await HttpClient.GetAsync("/api/report/getproducts");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
var data = await GetModelFromResponseAsync<List<ManufacturerProductViewModel>>(response);
Assert.That(data, Is.Not.Null);
Assert.Multiple(() =>
{
Assert.That(data, Has.Count.EqualTo(2));
Assert.That(data.First(x => x.ManufacturerName == manufacturer1.ManufacturerName).Products, Has.Count.EqualTo(3));
Assert.That(data.First(x => x.ManufacturerName == manufacturer2.ManufacturerName).Products, Has.Count.EqualTo(2));
});
}
}

View File

@@ -1,7 +1,52 @@
using CatHasPawsContratcs.AdapterContracts;
using AutoMapper;
using CatHasPawsContratcs.AdapterContracts;
using CatHasPawsContratcs.AdapterContracts.OperationResponses;
using CatHasPawsContratcs.BusinessLogicsContracts;
using CatHasPawsContratcs.DataModels;
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.ViewModels;
namespace CatHasPawsWebApi.Adapters;
public class ReportAdapter : IReportAdapter
{
private IReportContract _reportContract;
private readonly ILogger _logger;
private readonly Mapper _mapper;
public ReportAdapter(IReportContract reportContract, ILogger<ProductAdapter> logger)
{
_reportContract = reportContract;
_logger = logger;
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<ManufacturerProductDataModel, ManufacturerProductViewModel>();
});
_mapper = new Mapper(config);
}
public async Task<ReportOperationResponse> GetDataProductsByManufacturerAsync(CancellationToken ct)
{
try
{
return ReportOperationResponse.OK([.. (await _reportContract.GetDataProductsByManufacturerAsync(ct)).Select(x => _mapper.Map<ManufacturerProductViewModel>(x))]);
}
catch (InvalidOperationException ex)
{
_logger.LogError(ex, "InvalidOperationException");
return ReportOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException!.Message}");
}
catch (StorageException ex)
{
_logger.LogError(ex, "StorageException");
return ReportOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException!.Message}");
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception");
return ReportOperationResponse.InternalServerError(ex.Message);
}
}
}

View File

@@ -1,11 +1,20 @@
using CatHasPawsContratcs.AdapterContracts;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace CatHasPawsWebApi.Controllers;
[Route("api/[controller]")]
[Authorize]
[Route("api/[controller]/[action]")]
[ApiController]
public class ReportController(IReportAdapter reportAdapter) : ControllerBase
public class ReportController(IReportAdapter adapter) : ControllerBase
{
private IReportAdapter _reportAdapter = reportAdapter;
private readonly IReportAdapter _adapter = adapter;
[HttpGet]
[Consumes("application/json")]
public async Task<IActionResult> GetProducts(CancellationToken ct)
{
return (await _adapter.GetDataProductsByManufacturerAsync(ct)).GetResponse(Request, Response);
}
}