diff --git a/src/ApplicationCore/ApplicationCore.csproj b/src/ApplicationCore/ApplicationCore.csproj
index 4eb43c5..8e8906c 100644
--- a/src/ApplicationCore/ApplicationCore.csproj
+++ b/src/ApplicationCore/ApplicationCore.csproj
@@ -8,6 +8,7 @@
+
diff --git a/src/ApplicationCore/Entities/CatalogItem.cs b/src/ApplicationCore/Entities/CatalogItem.cs
index caa8562..7fe0149 100644
--- a/src/ApplicationCore/Entities/CatalogItem.cs
+++ b/src/ApplicationCore/Entities/CatalogItem.cs
@@ -1,8 +1,11 @@
using Ardalis.GuardClauses;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
+using System.Collections.Generic;
namespace Microsoft.eShopWeb.ApplicationCore.Entities
{
+
+
public class CatalogItem : BaseEntity, IAggregateRoot
{
public string Name { get; private set; }
@@ -14,7 +17,12 @@ namespace Microsoft.eShopWeb.ApplicationCore.Entities
public int CatalogBrandId { get; private set; }
public CatalogBrand CatalogBrand { get; private set; }
- public CatalogItem(int catalogTypeId, int catalogBrandId, string description, string name, decimal price, string pictureUri)
+ public CatalogItem(int catalogTypeId,
+ int catalogBrandId,
+ string description,
+ string name,
+ decimal price,
+ string pictureUri)
{
CatalogTypeId = catalogTypeId;
CatalogBrandId = catalogBrandId;
@@ -24,11 +32,27 @@ namespace Microsoft.eShopWeb.ApplicationCore.Entities
PictureUri = pictureUri;
}
- public void Update(string name, decimal price)
+ public void UpdateDetails(string name, string description, decimal price)
{
Guard.Against.NullOrEmpty(name, nameof(name));
+ Guard.Against.NullOrEmpty(description, nameof(description));
+ Guard.Against.NegativeOrZero(price, nameof(price));
+
Name = name;
+ Description = description;
Price = price;
}
+
+ public void UpdateBrand(int catalogBrandId)
+ {
+ Guard.Against.Zero(catalogBrandId, nameof(catalogBrandId));
+ CatalogBrandId = catalogBrandId;
+ }
+
+ public void UpdateType(int catalogTypeId)
+ {
+ Guard.Against.Zero(catalogTypeId, nameof(catalogTypeId));
+ CatalogTypeId = catalogTypeId;
+ }
}
}
\ No newline at end of file
diff --git a/src/ApplicationCore/Exceptions/DuplicateCatalogItemNameException.cs b/src/ApplicationCore/Exceptions/DuplicateCatalogItemNameException.cs
new file mode 100644
index 0000000..09fe302
--- /dev/null
+++ b/src/ApplicationCore/Exceptions/DuplicateCatalogItemNameException.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Microsoft.eShopWeb.ApplicationCore.Exceptions
+{
+ public class DuplicateCatalogItemNameException : Exception
+ {
+ public DuplicateCatalogItemNameException(string message, int duplicateItemId) : base(message)
+ {
+ DuplicateItemId = duplicateItemId;
+ }
+
+ public int DuplicateItemId { get; }
+ }
+}
diff --git a/src/PublicApi/CatalogItemEndpoints/Create.CreateCatalogItemRequest.cs b/src/PublicApi/CatalogItemEndpoints/Create.CreateCatalogItemRequest.cs
index 3a8e9a4..e3193d8 100644
--- a/src/PublicApi/CatalogItemEndpoints/Create.CreateCatalogItemRequest.cs
+++ b/src/PublicApi/CatalogItemEndpoints/Create.CreateCatalogItemRequest.cs
@@ -9,4 +9,5 @@
public string PictureUri { get; set; }
public decimal Price { get; set; }
}
+
}
diff --git a/src/PublicApi/CatalogItemEndpoints/Create.cs b/src/PublicApi/CatalogItemEndpoints/Create.cs
index f7a44af..27ce2ba 100644
--- a/src/PublicApi/CatalogItemEndpoints/Create.cs
+++ b/src/PublicApi/CatalogItemEndpoints/Create.cs
@@ -10,6 +10,7 @@ using System.Threading.Tasks;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
{
+
[Authorize(Roles = AuthorizationConstants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class Create : BaseAsyncEndpoint
{
diff --git a/src/PublicApi/CatalogItemEndpoints/Update.UpdateCatalogItemRequest.cs b/src/PublicApi/CatalogItemEndpoints/Update.UpdateCatalogItemRequest.cs
new file mode 100644
index 0000000..30b36b5
--- /dev/null
+++ b/src/PublicApi/CatalogItemEndpoints/Update.UpdateCatalogItemRequest.cs
@@ -0,0 +1,21 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
+{
+ public class UpdateCatalogItemRequest : BaseRequest
+ {
+ [Range(1, 10000)]
+ public int Id { get; set; }
+ [Range(1, 10000)]
+ public int CatalogBrandId { get; set; }
+ [Range(1, 10000)]
+ public int CatalogTypeId { get; set; }
+ [Required]
+ public string Description { get; set; }
+ [Required]
+ public string Name { get; set; }
+ public string PictureUri { get; set; }
+ [Range(0.01, 10000)]
+ public decimal Price { get; set; }
+ }
+}
diff --git a/src/PublicApi/CatalogItemEndpoints/Update.UpdateCatalogItemResponse.cs b/src/PublicApi/CatalogItemEndpoints/Update.UpdateCatalogItemResponse.cs
new file mode 100644
index 0000000..12913b5
--- /dev/null
+++ b/src/PublicApi/CatalogItemEndpoints/Update.UpdateCatalogItemResponse.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
+{
+ public class UpdateCatalogItemResponse : BaseResponse
+ {
+ public UpdateCatalogItemResponse(Guid correlationId) : base(correlationId)
+ {
+ }
+
+ public UpdateCatalogItemResponse()
+ {
+ }
+
+ public CatalogItemDto CatalogItem { get; set; }
+ }
+}
diff --git a/src/PublicApi/CatalogItemEndpoints/Update.cs b/src/PublicApi/CatalogItemEndpoints/Update.cs
new file mode 100644
index 0000000..0672006
--- /dev/null
+++ b/src/PublicApi/CatalogItemEndpoints/Update.cs
@@ -0,0 +1,58 @@
+using Ardalis.ApiEndpoints;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.eShopWeb.ApplicationCore.Constants;
+using Microsoft.eShopWeb.ApplicationCore.Entities;
+using Microsoft.eShopWeb.ApplicationCore.Exceptions;
+using Microsoft.eShopWeb.ApplicationCore.Interfaces;
+using Swashbuckle.AspNetCore.Annotations;
+using System;
+using System.Threading.Tasks;
+
+namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
+{
+ [Authorize(Roles = AuthorizationConstants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
+ public class Update : BaseAsyncEndpoint
+ {
+ private readonly IAsyncRepository _itemRepository;
+
+ public Update(IAsyncRepository itemRepository)
+ {
+ _itemRepository = itemRepository;
+ }
+
+ [HttpPut("api/catalog-items")]
+ [SwaggerOperation(
+ Summary = "Updates a Catalog Item",
+ Description = "Updates a Catalog Item",
+ OperationId = "catalog-items.update",
+ Tags = new[] { "CatalogItemEndpoints" })
+ ]
+ public override async Task> HandleAsync(UpdateCatalogItemRequest request)
+ {
+ var response = new UpdateCatalogItemResponse(request.CorrelationId());
+
+ var existingItem = await _itemRepository.GetByIdAsync(request.Id);
+
+ existingItem.UpdateDetails(request.Name, request.Description, request.Price);
+ existingItem.UpdateBrand(request.CatalogBrandId);
+ existingItem.UpdateType(request.CatalogTypeId);
+
+ await _itemRepository.UpdateAsync(existingItem);
+
+ var dto = new CatalogItemDto
+ {
+ Id = existingItem.Id,
+ CatalogBrandId = existingItem.CatalogBrandId,
+ CatalogTypeId = existingItem.CatalogTypeId,
+ Description = existingItem.Description,
+ Name = existingItem.Name,
+ PictureUri = existingItem.PictureUri,
+ Price = existingItem.Price
+ };
+ response.CatalogItem = dto;
+ return response;
+ }
+ }
+}
diff --git a/src/PublicApi/PublicApi.csproj b/src/PublicApi/PublicApi.csproj
index fb7c28d..7b305d1 100644
--- a/src/PublicApi/PublicApi.csproj
+++ b/src/PublicApi/PublicApi.csproj
@@ -11,6 +11,8 @@
+
+
diff --git a/src/PublicApi/Startup.cs b/src/PublicApi/Startup.cs
index 854565e..fc1d6db 100644
--- a/src/PublicApi/Startup.cs
+++ b/src/PublicApi/Startup.cs
@@ -1,6 +1,9 @@
+using System;
using System.Collections.Generic;
using System.Text;
using AutoMapper;
+using MediatR;
+
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
@@ -8,6 +11,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopWeb.ApplicationCore.Constants;
+using Microsoft.eShopWeb.ApplicationCore.Entities;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.ApplicationCore.Services;
using Microsoft.eShopWeb.Infrastructure.Data;
@@ -119,9 +123,9 @@ namespace Microsoft.eShopWeb.PublicApi
services.AddControllers();
+ services.AddMediatR(typeof(CatalogItem).Assembly);
services.AddAutoMapper(typeof(Startup).Assembly);
-
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
@@ -157,7 +161,6 @@ namespace Microsoft.eShopWeb.PublicApi
}
});
});
-
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
diff --git a/src/Web/Services/CatalogItemViewModelService.cs b/src/Web/Services/CatalogItemViewModelService.cs
index 8d93585..fad2852 100644
--- a/src/Web/Services/CatalogItemViewModelService.cs
+++ b/src/Web/Services/CatalogItemViewModelService.cs
@@ -18,7 +18,7 @@ namespace Microsoft.eShopWeb.Web.Services
public async Task UpdateCatalogItem(CatalogItemViewModel viewModel)
{
var existingCatalogItem = await _catalogItemRepository.GetByIdAsync(viewModel.Id);
- existingCatalogItem.Update(viewModel.Name, viewModel.Price);
+ existingCatalogItem.UpdateDetails(viewModel.Name, existingCatalogItem.Description, viewModel.Price);
await _catalogItemRepository.UpdateAsync(existingCatalogItem);
}
}
diff --git a/src/Web/Web.csproj b/src/Web/Web.csproj
index 8dc02ce..c4185cd 100644
--- a/src/Web/Web.csproj
+++ b/src/Web/Web.csproj
@@ -25,8 +25,8 @@
-
-
+
+
diff --git a/tests/UnitTests/ApplicationCore/Entities/CatalogItemTests/UpdateDetails.cs b/tests/UnitTests/ApplicationCore/Entities/CatalogItemTests/UpdateDetails.cs
new file mode 100644
index 0000000..d1d4600
--- /dev/null
+++ b/tests/UnitTests/ApplicationCore/Entities/CatalogItemTests/UpdateDetails.cs
@@ -0,0 +1,56 @@
+using Xunit;
+using Microsoft.eShopWeb.ApplicationCore.Entities;
+using System;
+
+namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Entities.CatalogItemTests
+{
+ public class UpdateDetails
+ {
+ private CatalogItem _testItem;
+ private int _validTypeId = 1;
+ private int _validBrandId = 2;
+ private string _validDescription = "test description";
+ private string _validName = "test name";
+ private decimal _validPrice = 1.23m;
+ private string _validUri = "/123";
+
+ public UpdateDetails()
+ {
+ _testItem = new CatalogItem(_validTypeId, _validBrandId, _validDescription, _validName, _validPrice, _validUri);
+ }
+
+ [Fact]
+ public void ThrowsArgumentExceptionGivenEmptyName()
+ {
+ string newValue = "";
+ Assert.Throws(() => _testItem.UpdateDetails(newValue, _validDescription, _validPrice));
+ }
+
+ [Fact]
+ public void ThrowsArgumentExceptionGivenEmptyDescription()
+ {
+ string newValue = "";
+ Assert.Throws(() => _testItem.UpdateDetails(_validName, newValue, _validPrice));
+ }
+
+ [Fact]
+ public void ThrowsArgumentNullExceptionGivenNullName()
+ {
+ Assert.Throws(() => _testItem.UpdateDetails(null, _validDescription, _validPrice));
+ }
+
+ [Fact]
+ public void ThrowsArgumentNullExceptionGivenNullDescription()
+ {
+ Assert.Throws(() => _testItem.UpdateDetails(_validName, null, _validPrice));
+ }
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(-1.23)]
+ public void ThrowsArgumentExceptionGivenNonPositivePrice(decimal newPrice)
+ {
+ Assert.Throws(() => _testItem.UpdateDetails(_validName, _validDescription, newPrice));
+ }
+ }
+}