public async Task <IHttpActionResult> UpdateProduct([FromBody] ItemCatalog product) { var item = _catalogContext.ItemCatalogs.FirstOrDefault(i => i.ID == product.ID); if (ModelState.IsValid) { _catalogContext.ItemCatalogs.Add(item); _catalogContext.SaveChanges(); var oldPrice = item.ItemValue; item.ItemValue = product.ItemValue; _catalogContext.ItemCatalogs.Add(item); var @event = new ProductPriceChangedIntegrationEvent(item.ID, item.ItemValue, oldPrice); _eventBus.Publish(@event); return(Ok(item)); } else { return(BadRequest()); throw new HttpResponseException(HttpStatusCode.PaymentRequired); } }
public async Task UpdateProductAsync(CatalogItem productToUpdate) { //TODO: checking for necessity of publishing event PriceChanged //TODO: Other possibilities for state of event is not considered //To update the price of a product and guarantee consistency, //(that the publishing of it's related event has definitely happened after persisting in current service, //first we need to update CatalogContext(and product) //and we need to store details of the related event in database (IntegrationEventLogContext) //IN THE SAME TRANSACTION //then we will publish the event //and if everything goes as expected, we change the state of event to published in db //TODO don't send productToUpdate to repo. fetch the current object, change it's price then send it (if fetched in admin panel before update there is no problem var oldPrice = _catalogContext.CatalogItems.AsNoTracking().Single(e => e.Id == productToUpdate.Id).Price; if (oldPrice != productToUpdate.Price) { ProductPriceChangedIntegrationEvent evt = new ProductPriceChangedIntegrationEvent(productToUpdate.Id, productToUpdate.Price, oldPrice); using (var transaction = _catalogContext.Database.BeginTransaction()) { _catalogContext.CatalogItems.Update(productToUpdate); await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(evt); transaction.Commit(); } _eventBus.Publish(evt); await _integrationEventLogService.MarkEventAsPublished(evt.Id); } else { _catalogContext.CatalogItems.Update(productToUpdate); _catalogContext.SaveChanges(); } }
public async Task <IActionResult> UpdateProduct([FromBody] CatalogItem productToUpdate) { var catalogItem = await _catalogContext.CatalogItems.SingleOrDefaultAsync(i => i.Id == productToUpdate.Id); if (catalogItem == null) { return(NotFound()); } var raiseProductPriceChangedEvent = catalogItem.Price != productToUpdate.Price; var oldPrice = catalogItem.Price; // Update current product catalogItem = productToUpdate; _catalogContext.CatalogItems.Update(catalogItem); if (raiseProductPriceChangedEvent) // Save and publish integration event if price has changed { //Create Integration Event to be published through the Event Bus var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice); // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(priceChangedEvent); // Publish through the Event Bus and mark the saved event as published await _catalogIntegrationEventService.PublishThroughEventBusAsync(priceChangedEvent); } else // Save updated product { await _catalogContext.SaveChangesAsync(); } return(Ok()); }
public async Task <IActionResult> UpdateProduct([FromBody] CatalogItem item) { var @event = new ProductPriceChangedIntegrationEvent(item.ProductId, item.NewPrice, item.OldPrice); _eventBus.Publish(@event); return(Ok(@event)); }
public async Task <ActionResult> UpdateProductAsync([FromBody] CatalogItem productToUpdate) { var catalogItem = await _catalogRepository.GetAsync(productToUpdate.Id); if (catalogItem == null) { return(NotFound(new { Message = $"Item with id {productToUpdate.Id} not found." })); } var oldPrice = catalogItem.Price; var raiseProductPriceChangedEvent = oldPrice != productToUpdate.Price; catalogItem = productToUpdate; if (raiseProductPriceChangedEvent) { var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice); _eventBus.Publish(priceChangedEvent); await _catalogRepository.UpdateItemAsync(catalogItem); } else { await _catalogRepository.UpdateItemAsync(catalogItem); } return(CreatedAtAction(nameof(ItemByIdAsync), new { id = productToUpdate.Id }, null)); }
public async Task <ActionResult> UpdateProductAsync([FromBody] CatalogItem productToUpdate) { var catalogItem = await _catalogContext.CatalogItems.SingleOrDefaultAsync(i => i.Id == productToUpdate.Id); if (catalogItem == null) { return(NotFound(new { Message = $"Item with id {productToUpdate.Id} not found." })); } var oldPrice = catalogItem.Price; var raiseProductPriceChangedEvent = oldPrice != productToUpdate.Price; // Update current product catalogItem = productToUpdate; _catalogContext.CatalogItems.Update(catalogItem); if (raiseProductPriceChangedEvent) // Save product's data and publish integration event through the Event Bus if price has changed { //Create Integration Event to be published through the Event Bus var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice); // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(priceChangedEvent); // Publish through the Event Bus and mark the saved event as published await _catalogIntegrationEventService.PublishThroughEventBusAsync(priceChangedEvent); } else // Just save the updated product because the Product's Price hasn't changed. { await _catalogContext.SaveChangesAsync(); } return(CreatedAtAction(nameof(ItemByIdAsync), new { id = productToUpdate.Id }, null)); }
public async Task Handle_BasketHasProductWithOldPrice_UpdateUnitPrice( [Frozen] Mock <IBasketRepository> mockBasketRepository, ProductPriceChangedIntegrationEventHandler sut, ProductPriceChangedIntegrationEvent productPriceChangedEvent, string user1, string user2 ) { //Arrange mockBasketRepository.Setup(_ => _.GetUsers()) .Returns(new string[] { user1, user2 }); var basket_user1 = new CustomerBasket { Items = new List <BasketItem> { new BasketItem { ProductNumber = productPriceChangedEvent.ProductNumber, UnitPrice = productPriceChangedEvent.OldPrice } } }; var basket_user2 = new CustomerBasket { Items = new List <BasketItem> { new BasketItem { ProductNumber = productPriceChangedEvent.ProductNumber, UnitPrice = productPriceChangedEvent.OldPrice } } }; mockBasketRepository.Setup(_ => _.GetBasketAsync(It.Is <string>(_ => _ == user1))) .ReturnsAsync(basket_user1); mockBasketRepository.Setup(_ => _.GetBasketAsync(It.Is <string>(_ => _ == user2))) .ReturnsAsync(basket_user2); //Act await sut.Handle(productPriceChangedEvent); //Assert mockBasketRepository.Verify(_ => _.UpdateBasketAsync( It.Is <CustomerBasket>(_ => _ == basket_user1) )); mockBasketRepository.Verify(_ => _.UpdateBasketAsync( It.Is <CustomerBasket>(_ => _ == basket_user2) )); basket_user1.Items[0].UnitPrice.Should().Be(productPriceChangedEvent.NewPrice); basket_user1.Items[0].OldUnitPrice.Should().Be(productPriceChangedEvent.OldPrice); basket_user2.Items[0].UnitPrice.Should().Be(productPriceChangedEvent.NewPrice); basket_user2.Items[0].OldUnitPrice.Should().Be(productPriceChangedEvent.OldPrice); }
public async Task Handle_BasketDoesNotHaveProduct_BasketNotUpdated( [Frozen] Mock <IBasketRepository> mockBasketRepository, ProductPriceChangedIntegrationEventHandler sut, ProductPriceChangedIntegrationEvent productPriceChangedEvent, string user1, CustomerBasket basket ) { //Arrange mockBasketRepository.Setup(_ => _.GetUsers()) .Returns(new string[] { user1 }); mockBasketRepository.Setup(_ => _.GetBasketAsync(It.Is <string>(_ => _ == user1))) .ReturnsAsync(basket); //Act await sut.Handle(productPriceChangedEvent); //Assert mockBasketRepository.Verify(_ => _.UpdateBasketAsync( It.IsAny <CustomerBasket>() ), Times.Never ); }
public async Task <IActionResult> UpdateProduct([FromBody] CatalogItem productToUpdate) { var catalogItem = await _catalogContext.CatalogItems .SingleOrDefaultAsync(c => c.Id == productToUpdate.Id); if (null == catalogItem) { return(NotFound(new { Message = $"未找到ID为{productToUpdate.Id}的目录项" })); } var oldPrice = catalogItem.Price; var raiseProductPriceChangedEvent = oldPrice != productToUpdate.Price; //更新当前产品 catalogItem = productToUpdate; _catalogContext.CatalogItems.Update(catalogItem); //价格改变,保存和发布事件。 if (raiseProductPriceChangedEvent) { //创建价格改变事件 var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice); //保存目录数据和事件发布日志--此步骤需要通过本地事务保证原子性。 await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(priceChangedEvent); //发布事件 await _catalogIntegrationEventService.PublishThroughEventBusAsync(priceChangedEvent); } else { await _catalogContext.SaveChangesAsync(); } return(CreatedAtAction(nameof(GetItemById), new { id = productToUpdate.Id }, null)); }
public async Task <IActionResult> UpdateProduct([FromBody] CatalogItem productToUpdate) { var catalogItem = await _catalogContext.CatalogItems .SingleOrDefaultAsync(i => i.Id == productToUpdate.Id); if (catalogItem == null) { return(NotFound(new { Message = $"Item with id {productToUpdate.Id} not found." })); } var oldPrice = catalogItem.Price; var raiseProductPriceChangedEvent = oldPrice != productToUpdate.Price; // Update current product catalogItem = productToUpdate; _catalogContext.CatalogItems.Update(catalogItem); if (raiseProductPriceChangedEvent) // Save and publish integration event if price has changed { //Create Integration Event to be published through the Event Bus var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice); // Publish through the Event Bus await _endpoint.Publish(priceChangedEvent); } else // Save updated product { await _catalogContext.SaveChangesAsync(); } return(CreatedAtAction(nameof(GetItemById), new { id = productToUpdate.Id }, null)); }
public IActionResult IsAlive() { var priceChangedEvent = new ProductPriceChangedIntegrationEvent(1, 10, 12); _catalogIntegrationEventService.PublishThroughEventBusAsync(priceChangedEvent); return(this.NoContent()); }
public async Task <IActionResult> UpdateProduct([FromBody] CatalogItem productToUpdate) { var catalogItem = await _catalogContext.CatalogItems.SingleOrDefaultAsync(i => i.Id == productToUpdate.Id); if (catalogItem == null) { return(NotFound()); } bool raiseProductPriceChangedEvent = false; IntegrationEvent priceChangedEvent = null; if (catalogItem.Price != productToUpdate.Price) { raiseProductPriceChangedEvent = true; } if (raiseProductPriceChangedEvent) // Create event if price has changed { var oldPrice = catalogItem.Price; priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice); } //Update current product catalogItem = productToUpdate; //Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction(): //See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency var strategy = _catalogContext.Database.CreateExecutionStrategy(); var eventLogService = _integrationEventLogServiceFactory(_catalogContext.Database.GetDbConnection()); await strategy.ExecuteAsync(async() => { // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction using (var transaction = _catalogContext.Database.BeginTransaction()) { _catalogContext.CatalogItems.Update(catalogItem); await _catalogContext.SaveChangesAsync(); //Save to EventLog only if product price changed if (raiseProductPriceChangedEvent) { await eventLogService.SaveEventAsync(priceChangedEvent, _catalogContext.Database.CurrentTransaction.GetDbTransaction()); } transaction.Commit(); } }); //Publish to Event Bus only if product price changed if (raiseProductPriceChangedEvent) { _eventBus.Publish(priceChangedEvent); await eventLogService.MarkEventAsPublishedAsync(priceChangedEvent); } return(Ok()); }
public async Task <IActionResult> CheckoutBasketAsync([FromBody] BasketCheckoutDto basketCheckout) { try { var basket = await _basketRepository.GetBasketById(basketCheckout.BasketId); if (basket == null) { return(BadRequest()); } BasketCheckoutMessage basketCheckoutMessage = _mapper.Map <BasketCheckoutMessage>(basketCheckout); basketCheckoutMessage.BasketLines = new List <BasketLineMessage>(); int total = 0; foreach (var b in basket.BasketLines) { var basketLineMessage = new BasketLineMessage { BasketLineId = b.BasketLineId, Price = b.Price, TicketAmount = b.TicketAmount }; total += b.Price * b.TicketAmount; basketCheckoutMessage.BasketLines.Add(basketLineMessage); } Discount discount = null; if (basket.DiscountId.HasValue) { discount = await _discountService.GetDiscount(basket.DiscountId.Value); } if (discount != null) { basketCheckoutMessage.BasketTotal = total - discount.Amount; } else { basketCheckoutMessage.BasketTotal = total; } try { ProductPriceChangedIntegrationEvent queueData = new ProductPriceChangedIntegrationEvent (basketCheckoutMessage.BasketId, basketCheckoutMessage.BasketTotal, basketCheckoutMessage.UserId); _eventBus.Publish(queueData); } catch (Exception e) { Console.WriteLine(e); throw; } await _basketRepository.ClearBasket(basketCheckout.BasketId); return(Accepted(basketCheckoutMessage)); } catch (Exception e) { return(StatusCode(StatusCodes.Status500InternalServerError, e.StackTrace)); } }
public ActionResult SimulatePriceChange(string productId, [FromQuery] decimal newPrice) { var @event = new ProductPriceChangedIntegrationEvent(productId, newPrice); // Publish integration event to the event bus // (RabbitMQ or a service bus underneath) _eventBus.Publish(@event); return(Ok()); }
public ActionResult <IEnumerable <CatalogItem> > Get() { var priceChangedEvent = new ProductPriceChangedIntegrationEvent(1, 34, 55); _eventBus.Publish(priceChangedEvent); var result = _catalogContext.CatalogItems.ToList(); return(result); }
public async Task <ActionResult> UpdateProductAsync([FromBody] CatalogItem productToUpdate) { var catalogItem = await _catalogContext.CatalogItems.SingleOrDefaultAsync(i => i.Id == productToUpdate.Id); if (catalogItem == null) { return(NotFound(new { Message = $"Item with id {productToUpdate.Id} not found." })); } var oldPrice = catalogItem.Price; var raiseProductPriceChangedEvent = oldPrice != productToUpdate.Price; // Update current product catalogItem = productToUpdate; _catalogContext.CatalogItems.Update(catalogItem); if (raiseProductPriceChangedEvent) // Save product's data and publish integration event through the Event Bus if price has changed { _logger.LogInformation(" [x] CatalogController.UpdateProduct(): Price has changed, integration event is being prepared..."); var productPriceChangeEvent = new ProductPriceChangedIntegrationEvent(productToUpdate.Id, oldPrice, productToUpdate.Price); var strategy = _catalogContext.Database.CreateExecutionStrategy(); _logger.LogInformation(" [x] CatalogController.UpdateProductAsync(): Beginning new transaction to save event and commit changes."); await strategy.Execute(async() => { using (var transaction = _catalogContext.Database.BeginTransaction()) { await _eventLogService.SaveEventAsync(productPriceChangeEvent, transaction); await _catalogContext.SaveChangesAsync(); transaction.Commit(); _logger.LogInformation(" [x] CatalogController.UpdateProductAsync(): Transaction ({0}) has been committed.", transaction.TransactionId); } }); try { await _eventLogService.MarkEventAsInProgressAsync(productPriceChangeEvent.Id); _eventBus.Publish(productPriceChangeEvent); await _eventLogService.MarkEventAsPublishedAsync(productPriceChangeEvent.Id); } catch (Exception e) { _logger.LogError(e, " [x] CatalogController.UpdateProductAsync(): Fail when publishing integration event {0}.", productPriceChangeEvent.Id); await _eventLogService.MarkEventAsFailedAsync(productPriceChangeEvent.Id); } } else // Just save the updated product because the Product's Price hasn't changed. { await _catalogContext.SaveChangesAsync(); } return(Ok()); }
public async Task <ActionResult> UpdateProductAsync([FromBody] CatalogItem productToUpdate, [FromServices] ICapPublisher capBus) { var catalogItem = await _catalogContext.CatalogItems.SingleOrDefaultAsync(i => i.Id == productToUpdate.Id); if (catalogItem == null) { return(NotFound(new { Message = $"Item with id {productToUpdate.Id} not found." })); } var oldPrice = catalogItem.Price; var raiseProductPriceChangedEvent = oldPrice != productToUpdate.Price; catalogItem = productToUpdate; // Save product's data and publish integration event through the Event Bus if price has changed if (raiseProductPriceChangedEvent) { var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice); var curTransaction = _catalogContext.Database.BeginTransaction(capBus); try { _catalogContext.CatalogItems.Update(catalogItem); //publish integration event capBus.Publish(nameof(ProductPriceChangedIntegrationEvent), priceChangedEvent); await _catalogContext.SaveChangesAsync(); curTransaction.Commit(); } catch { try { curTransaction?.Rollback(); } finally { curTransaction?.Dispose(); } throw; } finally { curTransaction?.Dispose(); } } else // Just save the updated product because the Product's Price hasn't changed. { _catalogContext.CatalogItems.Update(catalogItem); await _catalogContext.SaveChangesAsync(); } return(CreatedAtAction(nameof(ItemByIdAsync), new { id = productToUpdate.Id }, null)); }
public async Task Handle(ProductPriceChangedIntegrationEvent @event) { using (LogContext.PushProperty("IntegrationEventContext", $"{Program.AppName}")) { _logger.LogInformation("----- Handling integration event: {AppName} - ({@IntegrationEvent})", Program.AppName, @event); var userIds = _repository.GetUsers(); foreach (var id in userIds) { var basket = await _repository.GetBasketAsync(id); await UpdatePriceInBasketItems(@event.ProductId, @event.NewPrice, @event.OldPrice, basket); } } }
public async Task Handle(ProductPriceChangedIntegrationEvent @event) { var users = await repository.GetAllUsersAsync(); foreach (var Id in users) { var basket = await repository.GetBasketAsync(Id); //TODO SingleOrDefault or Where? var item = basket.Items.SingleOrDefault(x => x.ProductId == @event.ProductId); if (item != null) { item.UnitPrice = @event.NewPrice; item.OldPrice = @event.OldPrice; } await repository.UpdateBasketAsync(basket); } }
public async Task <IActionResult> PutCatalogItem(int id, CatalogItem productToUpdate) { if (id != productToUpdate.Id) { return(BadRequest()); } try { var catalogItem = await _context.CatalogItems.SingleOrDefaultAsync(i => i.Id == productToUpdate.Id); var oldPrice = catalogItem.Price; var raiseProductPriceChangedEvent = oldPrice != productToUpdate.Price; if (raiseProductPriceChangedEvent) { var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice); catalogIntegrationEventService.PublishThroughEventBusAsync(priceChangedEvent); } _context.Entry(productToUpdate).State = EntityState.Modified; await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!CatalogItemExists(id)) { return(NotFound()); } else { throw; } } return(NoContent()); }
public void Change_Price_Integration_Event() { var @event = new ProductPriceChangedIntegrationEvent("SKU-1234", 25, 17); _service.PublishThroughEventBusAsync(@event); }
public void EventBusTry([FromServices] IEventBus _eventBus, int blogId = 1) { var piblishEvent = new ProductPriceChangedIntegrationEvent(blogId); //声明事件源 _eventBus.Publish(piblishEvent); //发布事件 }
public async Task <IActionResult> UpdateProductAsync(string id, [FromBody] ProductViewModel updateModel, CancellationToken cancellationToken) { try { var item = await _catalogContext.Products .Include(x => x.ProductImages) .Include(x => x.ProductColors) .SingleOrDefaultAsync(i => i.Id == id, cancellationToken); if (item == null) { return(NotFound(new { Message = $"Item with id {updateModel.Id} not found." })); } if (updateModel.CategoryId <= 0 || updateModel.ManufacturerId <= 0) { return(BadRequest(new { Message = $"Cant update Product when category and manufacturer is not assign." })); } if (updateModel.ProductImages == null || updateModel.ProductImages.Length <= 0) { return(BadRequest(new { Message = "Cant update product with 0 image" })); } var oldPrice = item.Price; var raiseProductPriceChangedEvent = oldPrice != updateModel.Price; updateModel.Id = id; #region Clean all Images and colors _catalogContext.ProductImages.RemoveRange(item.ProductImages); _catalogContext.ProductColors.RemoveRange(item.ProductColors); // Delete Previous Images foreach (var image in item.ProductImages) { _fileUtility.DeleteFile("ProductImage/" + id, image.ImageName); } // Insert new images foreach (var image in updateModel.ProductImages) { await InsertProductImageAsync(updateModel, cancellationToken, image); } await _catalogContext.SaveChangesAsync(cancellationToken); #endregion #region Mapping // Update current product item.Description = updateModel.Description; item.LastUpdatedBy = updateModel.ActorId; item.LastUpdated = DateTime.Now; item.Name = updateModel.Name; item.CategoryId = updateModel.CategoryId; item.ManufacturerId = updateModel.ManufacturerId; item.Price = updateModel.Price; item.ProductImages = updateModel.ProductImages.Select(x => new ProductImage { ImageName = x.ImageName, ImageUrl = x.ImageUrl, ProductId = x.ProductId }) .ToList(); item.ProductColors = updateModel.ProductColors .Select(x => new ProductColor { Name = x.Name, ProductId = x.ProductId }).ToList(); #endregion // Update current product _catalogContext.Products.Update(item); if (raiseProductPriceChangedEvent) // Save product's data and publish integration event through the Event Bus if price has changed { //Create Integration Event to be published through the Event Bus var priceChangedEvent = new ProductPriceChangedIntegrationEvent(item.Id, updateModel.Price, oldPrice); // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(0, "PriceChanged", priceChangedEvent); // Publish through the Event Bus and mark the saved event as published await _catalogIntegrationEventService.PublishThroughEventBusAsync(priceChangedEvent); } else // Just save the updated product because the Product's Price hasn't changed. { await _catalogContext.SaveChangesAsync(cancellationToken); } return(CreatedAtAction(nameof(UpdateProductAsync), new { id = item.Id }, null)); } catch (Exception e) { Console.WriteLine(e); throw; } }