From 776c6ad9860d14bfc8be728feaae0dcbfa8bb0ca Mon Sep 17 00:00:00 2001 From: Teun Cortooms Date: Sun, 17 Jan 2021 22:15:13 +0100 Subject: [PATCH 1/5] Add feature filter by tag --- InventoryAPI/InventoryAPI.xml | 5 +++++ InventoryAPI/Products/ProductsController.cs | 14 ++++++++++++++ .../Products/Repository/ProductsRepository.cs | 7 +++++++ InventoryLogic/Facade/ProductsFacade.cs | 15 +++++++++++++++ .../DALInterfaces/IProductsRepository.cs | 1 + 5 files changed, 42 insertions(+) diff --git a/InventoryAPI/InventoryAPI.xml b/InventoryAPI/InventoryAPI.xml index 9b1ed37..d28d0e6 100644 --- a/InventoryAPI/InventoryAPI.xml +++ b/InventoryAPI/InventoryAPI.xml @@ -39,6 +39,11 @@ List of all Product definitions + + + Get a list of all Product definitions with a specified tag + + Get a specified Product definition diff --git a/InventoryAPI/Products/ProductsController.cs b/InventoryAPI/Products/ProductsController.cs index e9f56f2..287db4f 100644 --- a/InventoryAPI/Products/ProductsController.cs +++ b/InventoryAPI/Products/ProductsController.cs @@ -34,6 +34,20 @@ public List GetAll() return productRequestModels; } + /// + /// Get a list of all Product definitions with a specified tag + /// + [HttpGet] + [Route("filter/{tagId}")] + public List GetAll(int tagId) + { + var products = productsFacade.GetAll(tagId); + + var productRequestModels = products.ConvertAll(new System.Converter(ProductRequestModel.ProductDTOToProductRequestModel)); + + return productRequestModels; + } + /// /// Get a specified Product definition /// diff --git a/InventoryDAL/Products/Repository/ProductsRepository.cs b/InventoryDAL/Products/Repository/ProductsRepository.cs index aae2a62..893c339 100644 --- a/InventoryDAL/Products/Repository/ProductsRepository.cs +++ b/InventoryDAL/Products/Repository/ProductsRepository.cs @@ -40,6 +40,13 @@ public List GetAll() return productCache.Keys.ToList(); } + public List GetAll(int tagId) + { + List products = this.GetAll(); + + return products.Where(product => product.Tags.Any(tag => tag.Id == tagId)).ToList(); + } + public Product Get(int id) { Product product = productCache.Keys.Where(p => p.Id == id).FirstOrDefault(); diff --git a/InventoryLogic/Facade/ProductsFacade.cs b/InventoryLogic/Facade/ProductsFacade.cs index 0533ea2..12ca19d 100644 --- a/InventoryLogic/Facade/ProductsFacade.cs +++ b/InventoryLogic/Facade/ProductsFacade.cs @@ -13,6 +13,21 @@ public ProductsFacade(IRepositoryFactory repoFactory) { } + public List GetAll(int tagId) + { + List dtos = new List(); + List products = repoFactory.ProductsRepository.GetAll(tagId); + + foreach (Product product in products) + { + ProductDTO newDto = new ProductDTO(); + product.ConvertToDTO(newDto); + dtos.Add(newDto); + } + + return dtos; + } + public bool ApplyTag(int productId, int tagId) { Product product = repoFactory.GetCrudRepository().Get(productId); diff --git a/InventoryLogic/Interfaces/DALInterfaces/IProductsRepository.cs b/InventoryLogic/Interfaces/DALInterfaces/IProductsRepository.cs index eeb9886..b0a2124 100644 --- a/InventoryLogic/Interfaces/DALInterfaces/IProductsRepository.cs +++ b/InventoryLogic/Interfaces/DALInterfaces/IProductsRepository.cs @@ -7,5 +7,6 @@ namespace InventoryDAL.Products { public interface IProductsRepository : IRepository { + List GetAll(int tagId); } } \ No newline at end of file From d9df38ba01bd7622afbfdcd382b3c205ccd5b43c Mon Sep 17 00:00:00 2001 From: Teun Cortooms Date: Mon, 18 Jan 2021 14:38:40 +0100 Subject: [PATCH 2/5] Refactor tests --- InventoryDAL/Factories/ConverterFactory.cs | 15 +-- .../Products/Repository/ProductsRepository.cs | 2 +- .../Products/Builders/ProductBuilderTests.cs | 106 ++++++++---------- 3 files changed, 50 insertions(+), 73 deletions(-) diff --git a/InventoryDAL/Factories/ConverterFactory.cs b/InventoryDAL/Factories/ConverterFactory.cs index c034cee..4c0fdd7 100644 --- a/InventoryDAL/Factories/ConverterFactory.cs +++ b/InventoryDAL/Factories/ConverterFactory.cs @@ -3,18 +3,11 @@ using InventoryDAL.Products; using InventoryDAL.Stocks; using InventoryLogic.Interfaces; -using InventoryLogic.Products; -using InventoryLogic.Stocks; -using InventoryLogic.Tags; namespace InventoryDAL.Factories { public class ConverterFactory : IConverterFactory { - private readonly IDomainFactory domainFactory; - private readonly IRepositoryFactory repositoryFactory; - private readonly IEntityFactory entityFactory; - private readonly IDAOFactory daoFactory; public ProductConverter productConverter { get; private set; } public ProductEntityConverter productEntityConverter { get; private set; } public StockConverter stockConverter { get; private set; } @@ -27,18 +20,12 @@ public ConverterFactory(IDomainFactory domainFactory, IRepositoryFactory repositoryFactory, IDAOFactory daoFactory) { - this.domainFactory = domainFactory; - this.entityFactory = entityFactory; - this.repositoryFactory = repositoryFactory; - this.daoFactory = daoFactory; - productConverter = new ProductConverter(this.domainFactory, this.repositoryFactory); + productConverter = new ProductConverter(domainFactory, repositoryFactory); productEntityConverter = new ProductEntityConverter(entityFactory, daoFactory); stockConverter = new StockConverter(domainFactory, repositoryFactory); stockEntityConverter = new StockEntityConverter(entityFactory, daoFactory); tagConverter = new TagConverter(domainFactory, repositoryFactory); tagEntityConverter = new TagEntityConverter(entityFactory, daoFactory); - } - } } diff --git a/InventoryDAL/Products/Repository/ProductsRepository.cs b/InventoryDAL/Products/Repository/ProductsRepository.cs index 893c339..a36b680 100644 --- a/InventoryDAL/Products/Repository/ProductsRepository.cs +++ b/InventoryDAL/Products/Repository/ProductsRepository.cs @@ -77,7 +77,7 @@ public void Remove(int id) productCache.Remove(productCache.Where(cacheEntity => cacheEntity.Key.Id == id).First().Key); productEntityDAO.Remove(id); - //TODO : cleanup childs. + //TODO: cleanup childs. } public Product CreateNew() diff --git a/InventoryDALTests/Products/Builders/ProductBuilderTests.cs b/InventoryDALTests/Products/Builders/ProductBuilderTests.cs index 336dc07..dffe144 100644 --- a/InventoryDALTests/Products/Builders/ProductBuilderTests.cs +++ b/InventoryDALTests/Products/Builders/ProductBuilderTests.cs @@ -27,104 +27,107 @@ public void CreateMocks() this.mockProductEntity = new Mock().SetupAllProperties(); mockProductEntity.Setup(pe => pe.ProductTagEntities).Returns(new List()); mockProductEntity.Setup(pe => pe.StockEntities).Returns(new List()); - } [TestMethod()] - public void ProductBuilder_ShouldHaveSame_Id_AsEntity_WhenCreated() + public void Product_ShouldHaveSame_Id_AsEntity_WhenConverted() { int expected = 123; mockProductEntity.Setup(pe => pe.Id).Returns(expected); - ProductConverter builder = CreateProductBuilderWithMocks(); - int actual = builder.Convert(mockProductEntity.Object, (a, b) => { }).Id; + ProductConverter converter = CreateConverter(); + Product product = converter.Convert(mockProductEntity.Object, (a, b) => { }); + int actual = product.Id; Assert.AreEqual(expected, actual); } - private ProductConverter CreateProductBuilderWithMocks() + private ProductConverter CreateConverter() { return new ProductConverter(mockDomainFactory.Object, mockRepositoryFactory.Object); - - } [TestMethod()] - public void ProductBuilder_ShouldHaveSame_Name_AsEntity_WhenCreated() + public void Product_ShouldHaveSame_Name_AsEntity_WhenConverted() { string expected = "Name123"; mockProductEntity.Setup(pe => pe.Name).Returns(expected); - ProductConverter builder = CreateProductBuilderWithMocks(); - string actual = builder.Convert(mockProductEntity.Object, (a, b) => { }).Name; + ProductConverter converter = CreateConverter(); + Product product = converter.Convert(mockProductEntity.Object, (a, b) => { }); + string actual = product.Name; Assert.AreEqual(expected, actual); } [TestMethod()] - public void ProductBuilder_ShouldHaveSame_Price_AsEntity_WhenCreated() + public void Product_ShouldHaveSame_Price_AsEntity_WhenConverted() { Decimal expected = 12.50M; mockProductEntity.Setup(pe => pe.Price).Returns(expected); - ProductConverter builder = CreateProductBuilderWithMocks(); - Decimal actual = builder.Convert(mockProductEntity.Object, (a, b) => { }).Price; + ProductConverter converter = CreateConverter(); + Product product = converter.Convert(mockProductEntity.Object, (a, b) => { }); + Decimal actual = product.Price; Assert.AreEqual(expected, actual); } [TestMethod()] - public void ProductBuilder_ShouldHaveSame_Sku_AsEntity_WhenCreated() + public void Product_ShouldHaveSame_Sku_AsEntity_WhenConverted() { string expected = "Sku123"; mockProductEntity.Setup(pe => pe.Sku).Returns(expected); - ProductConverter builder = CreateProductBuilderWithMocks(); - string actual = builder.Convert(mockProductEntity.Object, (a, b) => { }).Sku; + ProductConverter converter = CreateConverter(); + Product product = converter.Convert(mockProductEntity.Object, (a, b) => { }); + string actual = product.Sku; Assert.AreEqual(expected, actual); } [TestMethod()] - public void ProductBuilder_ShouldHaveEmpty_Tags_List_WhenCreated() + public void Product_ShouldHaveSame_NumberOfTags_AsEntity_WhenConverted() { - int expected = 0; + int expected = mockProductEntity.Object.ProductTagEntities.Count; - ProductConverter builder = CreateProductBuilderWithMocks(); - int actual = builder.Convert(mockProductEntity.Object, (a, b) => { }).Tags.Count; + ProductConverter converter = CreateConverter(); + Product product = converter.Convert(mockProductEntity.Object, (a, b) => { }); + int actual = product.Tags.Count; Assert.AreEqual(expected, actual); } [TestMethod()] - public void ProductBuilder_ShouldHaveEmpty_Stocks_List_WhenCreated() + public void Product_ShouldHaveSame_NumberOfStocks_AsEntity_WhenConverted() { - int expected = 0; + int expected = mockProductEntity.Object.StockEntities.Count; - ProductConverter builder = CreateProductBuilderWithMocks(); - int actual = builder.Convert(mockProductEntity.Object, (a, b) => { }).Stocks.Count; + ProductConverter converter = CreateConverter(); + Product product = converter.Convert(mockProductEntity.Object, (a, b) => { }); + int actual = product.Stocks.Count; Assert.AreEqual(expected, actual); } [TestMethod()] - public void BuildTags_ShouldSet_TagIds_ToMatchIdsIn_ProductTags() + public void Product_Should_Contain_Tags_With_Matching_Ids_WhenConverted() { /* ARRANGE */ int[] expected = { 111, 222, 333 }; - SetProductTagsInMockProductEntity(expected); + SetupProductEntityToReturnProductTags(expected); SetupMockRepositoryFactoryToReturnTags(); /* ACT */ - ProductConverter builder = CreateProductBuilderWithMocks(); - //builder.BuildTags(); + ProductConverter converter = CreateConverter(); + Product product = converter.Convert(mockProductEntity.Object, (a, b) => { }); /* ASSERT */ for (int i = 0; i < expected.Length; i++) { - int actual = builder.Convert(mockProductEntity.Object, (a, b) => { }).Tags[i].Id; + int actual = product.Tags[i].Id; Assert.AreEqual(expected[i], actual); } } @@ -136,59 +139,46 @@ private void SetupMockRepositoryFactoryToReturnTags() .Returns((int id) => new Tag(id, "TestTag")); } - private void SetProductTagsInMockProductEntity(int[] expected) - { - IList productTagsList = CreateProductTags(expected); - this.mockProductEntity.Setup(pe => pe.ProductTagEntities).Returns(productTagsList); - } - - private IList CreateProductTags(int[] expected) + private void SetupProductEntityToReturnProductTags(int[] ids) { IList productTagsList = new List(); - for (int i = 0; i < expected.Length; i++) + for (int i = 0; i < ids.Length; i++) { - productTagsList.Add(new ProductTagEntity { TagId = expected[i] }); + productTagsList.Add(new ProductTagEntity { TagId = ids[i] }); } - return productTagsList; + this.mockProductEntity.Setup(pe => pe.ProductTagEntities).Returns(productTagsList); } [TestMethod()] - public void BuildStocks_ShouldSet_IdsInStocks_ToMatch_IdsInStockEntities() + public void Product_Should_Contain_Stocks_With_Matching_Ids_WhenConverted() { /* ARRANGE */ int[] expected = { 111, 222, 333 }; - GiveStockEntitiesToMockProductEntity(expected); + SetupProductEntityToReturnStocks(expected); SetupMockRepositoryFactoryToReturnStocks(); /* ACT */ - ProductConverter builder = CreateProductBuilderWithMocks(); - //builder.BuildStocks(); + ProductConverter converter = CreateConverter(); + Product product = converter.Convert(mockProductEntity.Object, (a, b) => { }); /* ASSERT */ for (int i = 0; i < expected.Length; i++) { - int actual = builder.Convert(mockProductEntity.Object, (a, b) => { }).Stocks[i].Id; + int actual = product.Stocks[i].Id; Assert.AreEqual(expected[i], actual); } } - private void GiveStockEntitiesToMockProductEntity(int[] expected) - { - IList stockEntitiesList = CreateStockEntities(expected); - this.mockProductEntity.Setup(pe => pe.StockEntities).Returns(stockEntitiesList); - } - - private static IList CreateStockEntities(int[] expected) + private void SetupProductEntityToReturnStocks(int[] expected) { IList stockEntitiesList = new List(); for (int i = 0; i < expected.Length; i++) { stockEntitiesList.Add(new StockEntity { Id = expected[i] }); } - return stockEntitiesList; + this.mockProductEntity.Setup(pe => pe.StockEntities).Returns(stockEntitiesList); } - private void SetupMockRepositoryFactoryToReturnStocks() { this.mockRepositoryFactory.Setup(rf => rf.StocksRepository @@ -205,7 +195,7 @@ public void GetResult_ShouldReturnProduct_WithSameBasicProperties_AsProductEntit SetupMockDomainFactoryToReturnProduct(); /* ACT */ - ProductConverter builder = CreateProductBuilderWithMocks(); + ProductConverter builder = CreateConverter(); IProduct product = builder.Convert(expectedProps, (a, b) => { }); /* ASSERT */ @@ -243,12 +233,12 @@ public void GetResult_ShouldReturnProduct_WithSameStocks_AsProductEntity_WhenUse int[] expectedStockIds = { 111, 222, 333 }; SetBasicPropertiesInMockProductEntity(basicProps); - GiveStockEntitiesToMockProductEntity(expectedStockIds); + SetupProductEntityToReturnStocks(expectedStockIds); SetupMockDomainFactoryToReturnProduct(); SetupMockRepositoryFactoryToReturnStocks(); /* ACT */ - ProductConverter builder = CreateProductBuilderWithMocks(); + ProductConverter builder = CreateConverter(); //builder.BuildStocks(); IProduct product = builder.Convert(mockProductEntity.Object, (a, b) => { }); @@ -267,12 +257,12 @@ public void GetResult_ShouldReturnProduct_WithSameTagAssociations_AsProductEntit ProductEntity basicProps = new ProductEntity { Id = 100, Name = "Name", Price = 10.00M, Sku = "Sku" }; int[] expectedTagIds = { 111, 222, 333 }; SetBasicPropertiesInMockProductEntity(basicProps); - SetProductTagsInMockProductEntity(expectedTagIds); + SetupProductEntityToReturnProductTags(expectedTagIds); SetupMockDomainFactoryToReturnProduct(); SetupMockRepositoryFactoryToReturnTags(); /* ACT */ - ProductConverter builder = CreateProductBuilderWithMocks(); + ProductConverter builder = CreateConverter(); //builder.BuildTags(); IProduct product = builder.Convert(mockProductEntity.Object, (a, b) => { }); From 07a5ba5419261b0ca55808cff307984bbaa5d3ab Mon Sep 17 00:00:00 2001 From: Teun Cortooms Date: Mon, 18 Jan 2021 14:50:17 +0100 Subject: [PATCH 3/5] refactor tests --- .../Products/Converters/ProductConverter.cs | 10 ++- .../Products/Builders/ProductBuilderTests.cs | 88 +++++-------------- 2 files changed, 27 insertions(+), 71 deletions(-) diff --git a/InventoryDAL/Products/Converters/ProductConverter.cs b/InventoryDAL/Products/Converters/ProductConverter.cs index 7b83796..93e31bf 100644 --- a/InventoryDAL/Products/Converters/ProductConverter.cs +++ b/InventoryDAL/Products/Converters/ProductConverter.cs @@ -30,10 +30,12 @@ public Product Convert(IProductEntity productEntity, OnObjectCreation onObjectCr List tags = new List(); List stocks = new List(); //compose product - Product product = new Product(productEntity.Id - , productEntity.Name - , productEntity.Price - , productEntity.Sku,tags,stocks); + Product product = domainFactory.CreateProduct(productEntity.Id, + productEntity.Name, + productEntity.Price, + productEntity.Sku, + tags, + stocks); // handle instantiated products. Needed by repository to prevent looping. onObjectCreation(product, productEntity); diff --git a/InventoryDALTests/Products/Builders/ProductBuilderTests.cs b/InventoryDALTests/Products/Builders/ProductBuilderTests.cs index dffe144..3f316d2 100644 --- a/InventoryDALTests/Products/Builders/ProductBuilderTests.cs +++ b/InventoryDALTests/Products/Builders/ProductBuilderTests.cs @@ -45,10 +45,24 @@ public void Product_ShouldHaveSame_Id_AsEntity_WhenConverted() private ProductConverter CreateConverter() { + SetupMockDomainFactoryToReturnProduct(); + return new ProductConverter(mockDomainFactory.Object, mockRepositoryFactory.Object); } + private void SetupMockDomainFactoryToReturnProduct() + { + this.mockDomainFactory.Setup(df => df.CreateProduct(It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny>())) + .Returns((int id, string name, Decimal price, string sku, List tags, List stocks) + => new Product(id, name, price, sku, tags, stocks)); + } + [TestMethod()] public void Product_ShouldHaveSame_Name_AsEntity_WhenConverted() { @@ -187,16 +201,17 @@ private void SetupMockRepositoryFactoryToReturnStocks() } [TestMethod()] - public void GetResult_ShouldReturnProduct_WithSameBasicProperties_AsProductEntity() + public void Product_ShouldHave_SameBasicProperties_AsProductEntity_WhenConverted() { + // really a duplicate of other tests + /* ARRANGE */ ProductEntity expectedProps = new ProductEntity { Id = 100, Name = "Name", Price = 10.00M, Sku = "Sku" }; - SetBasicPropertiesInMockProductEntity(expectedProps); - SetupMockDomainFactoryToReturnProduct(); + SetupProductEntityWithBasicProperties(expectedProps); /* ACT */ - ProductConverter builder = CreateConverter(); - IProduct product = builder.Convert(expectedProps, (a, b) => { }); + ProductConverter converter = CreateConverter(); + IProduct product = converter.Convert(expectedProps, (a, b) => { }); /* ASSERT */ Assert.AreEqual(product.Id, expectedProps.Id); @@ -205,73 +220,12 @@ public void GetResult_ShouldReturnProduct_WithSameBasicProperties_AsProductEntit Assert.AreEqual(product.Sku, expectedProps.Sku); } - private void SetBasicPropertiesInMockProductEntity(ProductEntity props) + private void SetupProductEntityWithBasicProperties(ProductEntity props) { this.mockProductEntity.Object.Id = props.Id; this.mockProductEntity.Object.Name = props.Name; this.mockProductEntity.Object.Price = props.Price; this.mockProductEntity.Object.Sku = props.Sku; } - - private void SetupMockDomainFactoryToReturnProduct() - { - this.mockDomainFactory.Setup(df => df.CreateProduct(It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny>(), - It.IsAny>())) - .Returns((int id, string name, Decimal price, string sku, List tags, List stocks) - => new Product(id, name, price, sku, tags, stocks)); - } - - [TestMethod()] - public void GetResult_ShouldReturnProduct_WithSameStocks_AsProductEntity_WhenUsedAfter_BuildStocks() - { - /* ARRANGE */ - ProductEntity basicProps = new ProductEntity { Id = 100, Name = "Name", Price = 10.00M, Sku = "Sku" }; - int[] expectedStockIds = { 111, 222, 333 }; - - SetBasicPropertiesInMockProductEntity(basicProps); - SetupProductEntityToReturnStocks(expectedStockIds); - SetupMockDomainFactoryToReturnProduct(); - SetupMockRepositoryFactoryToReturnStocks(); - - /* ACT */ - ProductConverter builder = CreateConverter(); - //builder.BuildStocks(); - IProduct product = builder.Convert(mockProductEntity.Object, (a, b) => { }); - - /* ASSERT */ - for (int i = 0; i < expectedStockIds.Length; i++) - { - int actual = product.Stocks[i].Id; - Assert.AreEqual(expectedStockIds[i], actual); - } - } - - [TestMethod()] - public void GetResult_ShouldReturnProduct_WithSameTagAssociations_AsProductEntity_WhenUsedAfter_BuildTags() - { - /* ARRANGE */ - ProductEntity basicProps = new ProductEntity { Id = 100, Name = "Name", Price = 10.00M, Sku = "Sku" }; - int[] expectedTagIds = { 111, 222, 333 }; - SetBasicPropertiesInMockProductEntity(basicProps); - SetupProductEntityToReturnProductTags(expectedTagIds); - SetupMockDomainFactoryToReturnProduct(); - SetupMockRepositoryFactoryToReturnTags(); - - /* ACT */ - ProductConverter builder = CreateConverter(); - //builder.BuildTags(); - IProduct product = builder.Convert(mockProductEntity.Object, (a, b) => { }); - - /* ASSERT */ - for (int i = 0; i < expectedTagIds.Length; i++) - { - int actual = product.Tags[i].Id; - Assert.AreEqual(expectedTagIds[i], actual); - } - } } } \ No newline at end of file From 6e0e90de8340034054d56d601f0dbd3e817aa44e Mon Sep 17 00:00:00 2001 From: Teun Cortooms Date: Mon, 18 Jan 2021 19:54:38 +0100 Subject: [PATCH 4/5] remove children from cache after modify product --- .../Products/Repository/ProductsRepository.cs | 21 ++++++++++++------- .../Tags/Repository/TagsRepository.cs | 7 +++++++ InventoryLogic/Facade/ProductsFacade.cs | 13 ++++++++---- .../DALInterfaces/ICrudRepository.cs | 3 ++- .../DALInterfaces/ITagsRepository.cs | 1 + RabbitMQ/MessageHandler.cs | 2 +- 6 files changed, 34 insertions(+), 13 deletions(-) diff --git a/InventoryDAL/Products/Repository/ProductsRepository.cs b/InventoryDAL/Products/Repository/ProductsRepository.cs index a36b680..547aff1 100644 --- a/InventoryDAL/Products/Repository/ProductsRepository.cs +++ b/InventoryDAL/Products/Repository/ProductsRepository.cs @@ -10,7 +10,7 @@ public class ProductsRepository : IProductsRepository private readonly IConverterFactory converterFactory; private readonly IProductEntityDAO productEntityDAO; - private readonly Dictionary productCache; + private readonly Dictionary productCache; public ProductsRepository(IProductEntityDAO productEntityDAO, @@ -28,14 +28,16 @@ private void OnObjectCreation(Product product, IProductEntity productEntity) } public List GetAll() - { + { List productEntities = productEntityDAO.GetAll(); // Trigger with where, only products not cached, and then select all uncached product entities to convert Products that will be added // to the cache with the OnObjectCreation delegate. - productEntities.Where(productEntity => productCache.Values.Any(cacheEntity => cacheEntity.Id == productEntity.Id) == false - ).ToList().ForEach(productEntity => converterFactory.productConverter.Convert(productEntity, OnObjectCreation)); - + var entitiesNotCached = productEntities + .Where(productEntity => productCache.Values.Any(cacheEntity => cacheEntity.Id == productEntity.Id) == false) + .ToList(); + entitiesNotCached.ForEach(productEntity => converterFactory.productConverter.Convert(productEntity, OnObjectCreation)); + return productCache.Keys.ToList(); } @@ -50,10 +52,12 @@ public List GetAll(int tagId) public Product Get(int id) { Product product = productCache.Keys.Where(p => p.Id == id).FirstOrDefault(); - if (product == null) { + if (product == null) + { ProductEntity productEntity = productEntityDAO.Get(id); return converterFactory.productConverter.Convert(productEntity, OnObjectCreation); - } else + } + else { return product; } @@ -68,6 +72,9 @@ public Product Add(Product product) public void Modify(Product product) { + Product productInCache = productCache.Keys.Where(p => p.Id == product.Id).FirstOrDefault(); + productCache.Remove(productInCache); + ProductEntity productEntity = converterFactory.productEntityConverter.Convert(product); productEntityDAO.Modify(productEntity); } diff --git a/InventoryDAL/Tags/Repository/TagsRepository.cs b/InventoryDAL/Tags/Repository/TagsRepository.cs index 181b577..cacba8f 100644 --- a/InventoryDAL/Tags/Repository/TagsRepository.cs +++ b/InventoryDAL/Tags/Repository/TagsRepository.cs @@ -56,10 +56,17 @@ public Tag Add(Tag tag) public void Modify(Tag tag) { + RemoveFromCache(tag); TagEntity tagEntity = converterFactory.tagEntityConverter.Convert(tag); tagEntityDAO.Modify(tagEntity); } + public void RemoveFromCache(Tag tag) // used in Facade. TODO: solve in DAL + { + Tag tagInCache = tagCache.Keys.Where(t => t.Id == tag.Id).FirstOrDefault(); + tagCache.Remove(tagInCache); + } + public void Remove(int id) { tagCache.Remove(tagCache.Where(cacheEntity => cacheEntity.Key.Id == id).First().Key); diff --git a/InventoryLogic/Facade/ProductsFacade.cs b/InventoryLogic/Facade/ProductsFacade.cs index 12ca19d..07cdc54 100644 --- a/InventoryLogic/Facade/ProductsFacade.cs +++ b/InventoryLogic/Facade/ProductsFacade.cs @@ -30,15 +30,20 @@ public List GetAll(int tagId) public bool ApplyTag(int productId, int tagId) { - Product product = repoFactory.GetCrudRepository().Get(productId); - Tag tag = repoFactory.GetCrudRepository().Get(tagId); + var productsRepo = repoFactory.ProductsRepository; + var tagsRepo = repoFactory.TagsRepository; + + Product product = productsRepo.Get(productId); + Tag tag = tagsRepo.Get(tagId); - if (product == null) throw new ArgumentException("Product not found."); + if (product == null) throw new ArgumentException("Product not found."); if (tag == null) throw new ArgumentException("Tag not found."); if (product.Tags.Contains(tag)) return false; product.Tags.Add(tag); - repoFactory.GetCrudRepository().Modify(product); + productsRepo.Modify(product); + tagsRepo.RemoveFromCache(tag); // TODO: solve in DAL! + return true; } diff --git a/InventoryLogic/Interfaces/DALInterfaces/ICrudRepository.cs b/InventoryLogic/Interfaces/DALInterfaces/ICrudRepository.cs index 77ffc57..d2a4b97 100644 --- a/InventoryLogic/Interfaces/DALInterfaces/ICrudRepository.cs +++ b/InventoryLogic/Interfaces/DALInterfaces/ICrudRepository.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using InventoryLogic.Tags; +using System.Collections.Generic; namespace InventoryLogic.Interfaces { diff --git a/InventoryLogic/Interfaces/DALInterfaces/ITagsRepository.cs b/InventoryLogic/Interfaces/DALInterfaces/ITagsRepository.cs index 3eaa68c..fc05af9 100644 --- a/InventoryLogic/Interfaces/DALInterfaces/ITagsRepository.cs +++ b/InventoryLogic/Interfaces/DALInterfaces/ITagsRepository.cs @@ -7,5 +7,6 @@ namespace InventoryDAL.Tags { public interface ITagsRepository : IRepository { + void RemoveFromCache(Tag tag); } } \ No newline at end of file diff --git a/RabbitMQ/MessageHandler.cs b/RabbitMQ/MessageHandler.cs index 0865ce0..b7efc29 100644 --- a/RabbitMQ/MessageHandler.cs +++ b/RabbitMQ/MessageHandler.cs @@ -99,7 +99,7 @@ private void HandleScan(string message) productsFacade.Modify(product); scannerMessage.ProductStock += 1; stock.Date = DateTime.Now; // adjust time to now to reflect change. - if (true) + if (true) // TODO: fix { // send message back scannerMessage.ScannerResult = ScannerResult.AddedToStock; // indicate success From caa5a96a57b102d05ecf1e87c97a077b6f287eef Mon Sep 17 00:00:00 2001 From: Teun Cortooms Date: Mon, 18 Jan 2021 20:39:03 +0100 Subject: [PATCH 5/5] More dirty last minute fixes (for circular reference bugs) --- InventoryAPI/Products/ProductsController.cs | 10 ++++----- .../Products/Repository/ProductsRepository.cs | 10 +++++++-- .../Stocks/Repository/StocksRepository.cs | 22 +++++++++++++------ .../Tags/Repository/TagsRepository.cs | 6 +++-- InventoryLogic/Facade/ProductsFacade.cs | 10 ++++++--- 5 files changed, 39 insertions(+), 19 deletions(-) diff --git a/InventoryAPI/Products/ProductsController.cs b/InventoryAPI/Products/ProductsController.cs index 287db4f..9929aa8 100644 --- a/InventoryAPI/Products/ProductsController.cs +++ b/InventoryAPI/Products/ProductsController.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Authorization; using InventoryDAL.Interfaces; using System.Collections.Generic; +using System; namespace InventoryAPI.Products { @@ -29,7 +30,7 @@ public List GetAll() { var products = productsFacade.GetAll(); - var productRequestModels = products.ConvertAll(new System.Converter(ProductRequestModel.ProductDTOToProductRequestModel)); + var productRequestModels = products.ConvertAll(new Converter(ProductRequestModel.ProductDTOToProductRequestModel)); return productRequestModels; } @@ -43,9 +44,8 @@ public List GetAll(int tagId) { var products = productsFacade.GetAll(tagId); - var productRequestModels = products.ConvertAll(new System.Converter(ProductRequestModel.ProductDTOToProductRequestModel)); - - return productRequestModels; + return products.ConvertAll(new Converter( + ProductRequestModel.ProductDTOToProductRequestModel)); } /// @@ -105,7 +105,7 @@ public bool ApplyTag(int id, int tagId) /// /// Apply a tag to a product /// - [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "inventory_product_removetag")] + //[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "inventory_product_removetag")] [HttpPost] [Route("{id}/removetag")] public bool RemoveTag(int id, int tagId) diff --git a/InventoryDAL/Products/Repository/ProductsRepository.cs b/InventoryDAL/Products/Repository/ProductsRepository.cs index 547aff1..f7cd1b6 100644 --- a/InventoryDAL/Products/Repository/ProductsRepository.cs +++ b/InventoryDAL/Products/Repository/ProductsRepository.cs @@ -24,6 +24,7 @@ public ProductsRepository(IProductEntityDAO productEntityDAO, // Handle cacheing of object on instantiation private void OnObjectCreation(Product product, IProductEntity productEntity) { + RemoveFromCache(product); productCache.Add(product, productEntity); } @@ -72,13 +73,18 @@ public Product Add(Product product) public void Modify(Product product) { - Product productInCache = productCache.Keys.Where(p => p.Id == product.Id).FirstOrDefault(); - productCache.Remove(productInCache); + RemoveFromCache(product); ProductEntity productEntity = converterFactory.productEntityConverter.Convert(product); productEntityDAO.Modify(productEntity); } + private void RemoveFromCache(Product product) // used in Facade. TODO: solve in DAL + { + Product productInCache = productCache.Keys.Where(p => p.Id == product.Id).FirstOrDefault(); + if (productInCache != null) productCache.Remove(productInCache); + } + public void Remove(int id) { productCache.Remove(productCache.Where(cacheEntity => cacheEntity.Key.Id == id).First().Key); diff --git a/InventoryDAL/Stocks/Repository/StocksRepository.cs b/InventoryDAL/Stocks/Repository/StocksRepository.cs index 0e9ba44..8a64a14 100644 --- a/InventoryDAL/Stocks/Repository/StocksRepository.cs +++ b/InventoryDAL/Stocks/Repository/StocksRepository.cs @@ -8,14 +8,14 @@ namespace InventoryDAL.Stocks { public class StocksRepository : IStocksRepository { - private readonly IConverterFactory converterFactory; + private readonly IConverterFactory converterFactory; private readonly IStockEntityDAO stockEntityDAO; private readonly Dictionary stockCache; public StocksRepository(IStockEntityDAO stockEntityDAO, IConverterFactory converterFactory) { - this.converterFactory = converterFactory; + this.converterFactory = converterFactory; this.stockEntityDAO = stockEntityDAO; stockCache = new Dictionary(); } @@ -24,19 +24,19 @@ public StocksRepository(IStockEntityDAO stockEntityDAO, IConverterFactory conver // Handle cacheing of object on instantiation private void OnObjectCreation(Stock stock, IStockEntity stockEntity) { - if(!stockCache.ContainsValue(stockEntity)) - stockCache.Add(stock, stockEntity); + RemoveFromCache(stock); + stockCache.Add(stock, stockEntity); } public List GetAll() { List stockEntities = stockEntityDAO.GetAll(); - + // Trigger with where, only stocks not cached, and then select all uncached stock entities to convert Stocks that will be added // to the cache with the OnObjectCreation delegate. stockEntities.Where(stockEntity => stockCache.Values.Any(cacheEntity => stockEntity.Id == cacheEntity.Id) == false) - .ToList().ForEach(stockEntity => converterFactory.stockConverter.Convert(stockEntity,OnObjectCreation)); - + .ToList().ForEach(stockEntity => converterFactory.stockConverter.Convert(stockEntity, OnObjectCreation)); + return stockCache.Keys.ToList(); } @@ -64,10 +64,18 @@ public Stock Add(Stock stock) public void Modify(Stock stock) { + RemoveFromCache(stock); + StockEntity stockEntity = converterFactory.stockEntityConverter.Convert(stock); stockEntityDAO.Modify(stockEntity); } + private void RemoveFromCache(Stock stock) // used in Facade. TODO: solve in DAL + { + Stock stockInCache = stockCache.Keys.Where(s => s.Id == stock.Id).FirstOrDefault(); + if (stockInCache != null) stockCache.Remove(stockInCache); + } + public void Remove(int id) { stockCache.Remove(stockCache.Where(cacheEntity => cacheEntity.Key.Id == id).First().Key); diff --git a/InventoryDAL/Tags/Repository/TagsRepository.cs b/InventoryDAL/Tags/Repository/TagsRepository.cs index cacba8f..894a7fd 100644 --- a/InventoryDAL/Tags/Repository/TagsRepository.cs +++ b/InventoryDAL/Tags/Repository/TagsRepository.cs @@ -21,6 +21,7 @@ public TagsRepository(ITagEntityDAO tagEntityDAO, IConverterFactory converterFac // Handle cacheing of object on instantiation private void OnObjectCreation(Tag tag, ITagEntity tagEntity) { + RemoveFromCache(tag); tagCache.Add(tag, tagEntity); } @@ -64,12 +65,13 @@ public void Modify(Tag tag) public void RemoveFromCache(Tag tag) // used in Facade. TODO: solve in DAL { Tag tagInCache = tagCache.Keys.Where(t => t.Id == tag.Id).FirstOrDefault(); - tagCache.Remove(tagInCache); + if (tagInCache != null) tagCache.Remove(tagInCache); } public void Remove(int id) { - tagCache.Remove(tagCache.Where(cacheEntity => cacheEntity.Key.Id == id).First().Key); + Tag tagInCache = tagCache.Keys.Where(t => t.Id == id).FirstOrDefault(); + if (tagInCache != null) tagCache.Remove(tagInCache); tagEntityDAO.Remove(id); } diff --git a/InventoryLogic/Facade/ProductsFacade.cs b/InventoryLogic/Facade/ProductsFacade.cs index 07cdc54..29fd966 100644 --- a/InventoryLogic/Facade/ProductsFacade.cs +++ b/InventoryLogic/Facade/ProductsFacade.cs @@ -49,15 +49,19 @@ public bool ApplyTag(int productId, int tagId) public bool RemoveTag(int productId, int tagId) { - Product product = repoFactory.GetCrudRepository().Get(productId); - Tag tag = repoFactory.GetCrudRepository().Get(tagId); + var productsRepo = repoFactory.ProductsRepository; + var tagsRepo = repoFactory.TagsRepository; + + Product product = productsRepo.Get(productId); + Tag tag = tagsRepo.Get(tagId); if (product == null) throw new ArgumentException("Product not found."); if (tag == null) throw new ArgumentException("Tag not found."); if (!product.Tags.Contains(tag)) return false; product.Tags.Remove(tag); - repoFactory.GetCrudRepository().Modify(product); + productsRepo.Modify(product); + return true; } }