diff --git a/README.md b/README.md
index d7466a9..60591b0 100644
--- a/README.md
+++ b/README.md
@@ -55,18 +55,13 @@ You can also run the samples in Docker (see below).
### Configuring the sample to use SQL Server
-1. Update `Startup.cs`'s `ConfigureDevelopmentServices` method as follows:
+1. By default, the project uses a real database. If you want an in memory database, you can add in `appsettings.json`
- ```csharp
- public void ConfigureDevelopmentServices(IServiceCollection services)
- {
- // use in-memory database
- //ConfigureTestingServices(services);
+ ```json
+ {
+ "UseOnlyInMemoryDatabase": true
+ }
- // use real database
- ConfigureProductionServices(services);
-
- }
```
1. Ensure your connection strings in `appsettings.json` point to a local SQL Server instance.
diff --git a/eShopOnWeb.sln b/eShopOnWeb.sln
index 0394e30..2adaaf8 100755
--- a/eShopOnWeb.sln
+++ b/eShopOnWeb.sln
@@ -38,6 +38,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorAdmin", "src\BlazorAd
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorShared", "src\BlazorShared\BlazorShared.csproj", "{715CF7AF-A1EE-40A6-94A0-8DA3F3B2CAE9}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PublicApiIntegrationTests", "tests\PublicApiIntegrationTests\PublicApiIntegrationTests.csproj", "{D53EF010-8F8C-4337-A059-456E19D8AE63}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -84,6 +86,10 @@ Global
{715CF7AF-A1EE-40A6-94A0-8DA3F3B2CAE9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{715CF7AF-A1EE-40A6-94A0-8DA3F3B2CAE9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{715CF7AF-A1EE-40A6-94A0-8DA3F3B2CAE9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D53EF010-8F8C-4337-A059-456E19D8AE63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D53EF010-8F8C-4337-A059-456E19D8AE63}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D53EF010-8F8C-4337-A059-456E19D8AE63}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D53EF010-8F8C-4337-A059-456E19D8AE63}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -98,6 +104,7 @@ Global
{B5E4F33C-4667-4A55-AF6A-740F84C4CF3A} = {419A6ACE-0419-4315-A6FB-B0E63D39432E}
{71368733-80A4-4869-B215-3A7001878577} = {419A6ACE-0419-4315-A6FB-B0E63D39432E}
{715CF7AF-A1EE-40A6-94A0-8DA3F3B2CAE9} = {419A6ACE-0419-4315-A6FB-B0E63D39432E}
+ {D53EF010-8F8C-4337-A059-456E19D8AE63} = {15EA4737-125B-4E6E-A806-E13B7EBCDCCF}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {49813262-5DA3-4D61-ABD3-493C74CE8C2B}
diff --git a/src/ApplicationCore/ApplicationCore.csproj b/src/ApplicationCore/ApplicationCore.csproj
index 6b310b5..3e939d2 100644
--- a/src/ApplicationCore/ApplicationCore.csproj
+++ b/src/ApplicationCore/ApplicationCore.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/src/BlazorAdmin/BlazorAdmin.csproj b/src/BlazorAdmin/BlazorAdmin.csproj
index f74f102..19347c3 100644
--- a/src/BlazorAdmin/BlazorAdmin.csproj
+++ b/src/BlazorAdmin/BlazorAdmin.csproj
@@ -7,11 +7,11 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/src/BlazorShared/BlazorShared.csproj b/src/BlazorShared/BlazorShared.csproj
index a8f1800..9717973 100644
--- a/src/BlazorShared/BlazorShared.csproj
+++ b/src/BlazorShared/BlazorShared.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/src/Infrastructure/Dependencies.cs b/src/Infrastructure/Dependencies.cs
new file mode 100644
index 0000000..d049a96
--- /dev/null
+++ b/src/Infrastructure/Dependencies.cs
@@ -0,0 +1,40 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.eShopWeb.Infrastructure.Data;
+using Microsoft.eShopWeb.Infrastructure.Identity;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Microsoft.eShopWeb.Infrastructure;
+
+public static class Dependencies
+{
+ public static void ConfigureServices(IConfiguration configuration, IServiceCollection services)
+ {
+ var useOnlyInMemoryDatabase = false;
+ if (configuration["UseOnlyInMemoryDatabase"] != null)
+ {
+ useOnlyInMemoryDatabase = bool.Parse(configuration["UseOnlyInMemoryDatabase"]);
+ }
+
+ if (useOnlyInMemoryDatabase)
+ {
+ services.AddDbContext(c =>
+ c.UseInMemoryDatabase("Catalog"));
+
+ services.AddDbContext(options =>
+ options.UseInMemoryDatabase("Identity"));
+ }
+ else
+ {
+ // use real database
+ // Requires LocalDB which can be installed with SQL Server Express 2016
+ // https://www.microsoft.com/en-us/download/details.aspx?id=54284
+ services.AddDbContext(c =>
+ c.UseSqlServer(configuration.GetConnectionString("CatalogConnection")));
+
+ // Add Identity DbContext
+ services.AddDbContext(options =>
+ options.UseSqlServer(configuration.GetConnectionString("IdentityConnection")));
+ }
+ }
+}
diff --git a/src/Infrastructure/Infrastructure.csproj b/src/Infrastructure/Infrastructure.csproj
index 41ac093..9222276 100644
--- a/src/Infrastructure/Infrastructure.csproj
+++ b/src/Infrastructure/Infrastructure.csproj
@@ -7,8 +7,9 @@
-
-
+
+
+
diff --git a/src/PublicApi/AuthEndpoints/Authenticate.AuthenticateRequest.cs b/src/PublicApi/AuthEndpoints/AuthenticateEndpoint.AuthenticateRequest.cs
similarity index 100%
rename from src/PublicApi/AuthEndpoints/Authenticate.AuthenticateRequest.cs
rename to src/PublicApi/AuthEndpoints/AuthenticateEndpoint.AuthenticateRequest.cs
diff --git a/src/PublicApi/AuthEndpoints/Authenticate.AuthenticateResponse.cs b/src/PublicApi/AuthEndpoints/AuthenticateEndpoint.AuthenticateResponse.cs
similarity index 100%
rename from src/PublicApi/AuthEndpoints/Authenticate.AuthenticateResponse.cs
rename to src/PublicApi/AuthEndpoints/AuthenticateEndpoint.AuthenticateResponse.cs
diff --git a/src/PublicApi/AuthEndpoints/Authenticate.ClaimValue.cs b/src/PublicApi/AuthEndpoints/AuthenticateEndpoint.ClaimValue.cs
similarity index 100%
rename from src/PublicApi/AuthEndpoints/Authenticate.ClaimValue.cs
rename to src/PublicApi/AuthEndpoints/AuthenticateEndpoint.ClaimValue.cs
diff --git a/src/PublicApi/AuthEndpoints/Authenticate.UserInfo.cs b/src/PublicApi/AuthEndpoints/AuthenticateEndpoint.UserInfo.cs
similarity index 100%
rename from src/PublicApi/AuthEndpoints/Authenticate.UserInfo.cs
rename to src/PublicApi/AuthEndpoints/AuthenticateEndpoint.UserInfo.cs
diff --git a/src/PublicApi/AuthEndpoints/Authenticate.cs b/src/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs
similarity index 90%
rename from src/PublicApi/AuthEndpoints/Authenticate.cs
rename to src/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs
index eee5218..0d5bece 100644
--- a/src/PublicApi/AuthEndpoints/Authenticate.cs
+++ b/src/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs
@@ -9,14 +9,17 @@ using Swashbuckle.AspNetCore.Annotations;
namespace Microsoft.eShopWeb.PublicApi.AuthEndpoints;
-public class Authenticate : EndpointBaseAsync
+///
+/// Authenticates a user
+///
+public class AuthenticateEndpoint : EndpointBaseAsync
.WithRequest
.WithActionResult
{
private readonly SignInManager _signInManager;
private readonly ITokenClaimsService _tokenClaimsService;
- public Authenticate(SignInManager signInManager,
+ public AuthenticateEndpoint(SignInManager signInManager,
ITokenClaimsService tokenClaimsService)
{
_signInManager = signInManager;
@@ -30,7 +33,7 @@ public class Authenticate : EndpointBaseAsync
OperationId = "auth.authenticate",
Tags = new[] { "AuthEndpoints" })
]
- public override async Task> HandleAsync(AuthenticateRequest request, CancellationToken cancellationToken)
+ public override async Task> HandleAsync(AuthenticateRequest request, CancellationToken cancellationToken = default)
{
var response = new AuthenticateResponse(request.CorrelationId());
diff --git a/src/PublicApi/CatalogBrandEndpoints/List.ListCatalogBrandsResponse.cs b/src/PublicApi/CatalogBrandEndpoints/CatalogBrandListEndpoint.ListCatalogBrandsResponse.cs
similarity index 100%
rename from src/PublicApi/CatalogBrandEndpoints/List.ListCatalogBrandsResponse.cs
rename to src/PublicApi/CatalogBrandEndpoints/CatalogBrandListEndpoint.ListCatalogBrandsResponse.cs
diff --git a/src/PublicApi/CatalogBrandEndpoints/CatalogBrandListEndpoint.cs b/src/PublicApi/CatalogBrandEndpoints/CatalogBrandListEndpoint.cs
new file mode 100644
index 0000000..dce823e
--- /dev/null
+++ b/src/PublicApi/CatalogBrandEndpoints/CatalogBrandListEndpoint.cs
@@ -0,0 +1,48 @@
+using System.Linq;
+using System.Threading.Tasks;
+using AutoMapper;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.eShopWeb.ApplicationCore.Entities;
+using Microsoft.eShopWeb.ApplicationCore.Interfaces;
+using MinimalApi.Endpoint;
+
+namespace Microsoft.eShopWeb.PublicApi.CatalogBrandEndpoints;
+
+///
+/// List Catalog Brands
+///
+public class CatalogBrandListEndpoint : IEndpoint
+{
+ private IRepository _catalogBrandRepository;
+ private readonly IMapper _mapper;
+
+ public CatalogBrandListEndpoint(IMapper mapper)
+ {
+ _mapper = mapper;
+ }
+
+ public void AddRoute(IEndpointRouteBuilder app)
+ {
+ app.MapGet("api/catalog-brands",
+ async (IRepository catalogBrandRepository) =>
+ {
+ _catalogBrandRepository = catalogBrandRepository;
+ return await HandleAsync();
+ })
+ .Produces()
+ .WithTags("CatalogBrandEndpoints");
+ }
+
+ public async Task HandleAsync()
+ {
+ var response = new ListCatalogBrandsResponse();
+
+ var items = await _catalogBrandRepository.ListAsync();
+
+ response.CatalogBrands.AddRange(items.Select(_mapper.Map));
+
+ return Results.Ok(response);
+ }
+}
diff --git a/src/PublicApi/CatalogBrandEndpoints/List.cs b/src/PublicApi/CatalogBrandEndpoints/List.cs
deleted file mode 100644
index d64b8ca..0000000
--- a/src/PublicApi/CatalogBrandEndpoints/List.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Ardalis.ApiEndpoints;
-using AutoMapper;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.eShopWeb.ApplicationCore.Entities;
-using Microsoft.eShopWeb.ApplicationCore.Interfaces;
-using Swashbuckle.AspNetCore.Annotations;
-
-namespace Microsoft.eShopWeb.PublicApi.CatalogBrandEndpoints;
-
-public class List : EndpointBaseAsync
- .WithoutRequest
- .WithActionResult
-{
- private readonly IRepository _catalogBrandRepository;
- private readonly IMapper _mapper;
-
- public List(IRepository catalogBrandRepository,
- IMapper mapper)
- {
- _catalogBrandRepository = catalogBrandRepository;
- _mapper = mapper;
- }
-
- [HttpGet("api/catalog-brands")]
- [SwaggerOperation(
- Summary = "List Catalog Brands",
- Description = "List Catalog Brands",
- OperationId = "catalog-brands.List",
- Tags = new[] { "CatalogBrandEndpoints" })
- ]
- public override async Task> HandleAsync(CancellationToken cancellationToken)
- {
- var response = new ListCatalogBrandsResponse();
-
- var items = await _catalogBrandRepository.ListAsync(cancellationToken);
-
- response.CatalogBrands.AddRange(items.Select(_mapper.Map));
-
- return Ok(response);
- }
-}
diff --git a/src/PublicApi/CatalogItemEndpoints/CatalogItemGetByIdEndpoint.GetByIdCatalogItemRequest.cs b/src/PublicApi/CatalogItemEndpoints/CatalogItemGetByIdEndpoint.GetByIdCatalogItemRequest.cs
new file mode 100644
index 0000000..a53f5ef
--- /dev/null
+++ b/src/PublicApi/CatalogItemEndpoints/CatalogItemGetByIdEndpoint.GetByIdCatalogItemRequest.cs
@@ -0,0 +1,11 @@
+namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
+
+public class GetByIdCatalogItemRequest : BaseRequest
+{
+ public int CatalogItemId { get; init; }
+
+ public GetByIdCatalogItemRequest(int catalogItemId)
+ {
+ CatalogItemId = catalogItemId;
+ }
+}
diff --git a/src/PublicApi/CatalogItemEndpoints/GetById.GetByIdCatalogItemResponse.cs b/src/PublicApi/CatalogItemEndpoints/CatalogItemGetByIdEndpoint.GetByIdCatalogItemResponse.cs
similarity index 100%
rename from src/PublicApi/CatalogItemEndpoints/GetById.GetByIdCatalogItemResponse.cs
rename to src/PublicApi/CatalogItemEndpoints/CatalogItemGetByIdEndpoint.GetByIdCatalogItemResponse.cs
diff --git a/src/PublicApi/CatalogItemEndpoints/CatalogItemGetByIdEndpoint.cs b/src/PublicApi/CatalogItemEndpoints/CatalogItemGetByIdEndpoint.cs
new file mode 100644
index 0000000..a1f6011
--- /dev/null
+++ b/src/PublicApi/CatalogItemEndpoints/CatalogItemGetByIdEndpoint.cs
@@ -0,0 +1,56 @@
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.eShopWeb.ApplicationCore.Entities;
+using Microsoft.eShopWeb.ApplicationCore.Interfaces;
+using MinimalApi.Endpoint;
+
+namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
+
+///
+/// Get a Catalog Item by Id
+///
+public class CatalogItemGetByIdEndpoint : IEndpoint
+{
+ private IRepository _itemRepository;
+ private readonly IUriComposer _uriComposer;
+
+ public CatalogItemGetByIdEndpoint(IUriComposer uriComposer)
+ {
+ _uriComposer = uriComposer;
+ }
+
+ public void AddRoute(IEndpointRouteBuilder app)
+ {
+ app.MapGet("api/catalog-items/{catalogItemId}",
+ async (int catalogItemId, IRepository itemRepository) =>
+ {
+ _itemRepository = itemRepository;
+ return await HandleAsync(new GetByIdCatalogItemRequest(catalogItemId));
+ })
+ .Produces()
+ .WithTags("CatalogItemEndpoints");
+ }
+
+ public async Task HandleAsync(GetByIdCatalogItemRequest request)
+ {
+ var response = new GetByIdCatalogItemResponse(request.CorrelationId());
+
+ var item = await _itemRepository.GetByIdAsync(request.CatalogItemId);
+ if (item is null)
+ return Results.NotFound();
+
+ response.CatalogItem = new CatalogItemDto
+ {
+ Id = item.Id,
+ CatalogBrandId = item.CatalogBrandId,
+ CatalogTypeId = item.CatalogTypeId,
+ Description = item.Description,
+ Name = item.Name,
+ PictureUri = _uriComposer.ComposePicUri(item.PictureUri),
+ Price = item.Price
+ };
+ return Results.Ok(response);
+ }
+}
diff --git a/src/PublicApi/CatalogItemEndpoints/CatalogItemListPagedEndpoint.ListPagedCatalogItemRequest.cs b/src/PublicApi/CatalogItemEndpoints/CatalogItemListPagedEndpoint.ListPagedCatalogItemRequest.cs
new file mode 100644
index 0000000..e9744d8
--- /dev/null
+++ b/src/PublicApi/CatalogItemEndpoints/CatalogItemListPagedEndpoint.ListPagedCatalogItemRequest.cs
@@ -0,0 +1,17 @@
+namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
+
+public class ListPagedCatalogItemRequest : BaseRequest
+{
+ public int? PageSize { get; init; }
+ public int? PageIndex { get; init; }
+ public int? CatalogBrandId { get; init; }
+ public int? CatalogTypeId { get; init; }
+
+ public ListPagedCatalogItemRequest(int? pageSize, int? pageIndex, int? catalogBrandId, int? catalogTypeId)
+ {
+ PageSize = pageSize ?? 0;
+ PageIndex = pageIndex ?? 0;
+ CatalogBrandId = catalogBrandId;
+ CatalogTypeId = catalogTypeId;
+ }
+}
diff --git a/src/PublicApi/CatalogItemEndpoints/ListPaged.ListPagedCatalogItemResponse.cs b/src/PublicApi/CatalogItemEndpoints/CatalogItemListPagedEndpoint.ListPagedCatalogItemResponse.cs
similarity index 100%
rename from src/PublicApi/CatalogItemEndpoints/ListPaged.ListPagedCatalogItemResponse.cs
rename to src/PublicApi/CatalogItemEndpoints/CatalogItemListPagedEndpoint.ListPagedCatalogItemResponse.cs
diff --git a/src/PublicApi/CatalogItemEndpoints/ListPaged.cs b/src/PublicApi/CatalogItemEndpoints/CatalogItemListPagedEndpoint.cs
similarity index 50%
rename from src/PublicApi/CatalogItemEndpoints/ListPaged.cs
rename to src/PublicApi/CatalogItemEndpoints/CatalogItemListPagedEndpoint.cs
index c7c20f7..308d310 100644
--- a/src/PublicApi/CatalogItemEndpoints/ListPaged.cs
+++ b/src/PublicApi/CatalogItemEndpoints/CatalogItemListPagedEndpoint.cs
@@ -1,55 +1,58 @@
using System;
using System.Linq;
-using System.Threading;
using System.Threading.Tasks;
-using Ardalis.ApiEndpoints;
using AutoMapper;
-using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.ApplicationCore.Specifications;
-using Swashbuckle.AspNetCore.Annotations;
+using MinimalApi.Endpoint;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
-public class ListPaged : EndpointBaseAsync
- .WithRequest
- .WithActionResult
+///
+/// List Catalog Items (paged)
+///
+public class CatalogItemListPagedEndpoint : IEndpoint
{
- private readonly IRepository _itemRepository;
+ private IRepository _itemRepository;
private readonly IUriComposer _uriComposer;
private readonly IMapper _mapper;
- public ListPaged(IRepository itemRepository,
- IUriComposer uriComposer,
- IMapper mapper)
+ public CatalogItemListPagedEndpoint(IUriComposer uriComposer, IMapper mapper)
{
- _itemRepository = itemRepository;
_uriComposer = uriComposer;
_mapper = mapper;
}
- [HttpGet("api/catalog-items")]
- [SwaggerOperation(
- Summary = "List Catalog Items (paged)",
- Description = "List Catalog Items (paged)",
- OperationId = "catalog-items.ListPaged",
- Tags = new[] { "CatalogItemEndpoints" })
- ]
- public override async Task> HandleAsync([FromQuery] ListPagedCatalogItemRequest request, CancellationToken cancellationToken)
+ public void AddRoute(IEndpointRouteBuilder app)
+ {
+ app.MapGet("api/catalog-items",
+ async (int? pageSize, int? pageIndex, int? catalogBrandId, int? catalogTypeId, IRepository itemRepository) =>
+ {
+ _itemRepository = itemRepository;
+ return await HandleAsync(new ListPagedCatalogItemRequest(pageSize, pageIndex, catalogBrandId, catalogTypeId));
+ })
+ .Produces()
+ .WithTags("CatalogItemEndpoints");
+ }
+
+ public async Task HandleAsync(ListPagedCatalogItemRequest request)
{
var response = new ListPagedCatalogItemResponse(request.CorrelationId());
var filterSpec = new CatalogFilterSpecification(request.CatalogBrandId, request.CatalogTypeId);
- int totalItems = await _itemRepository.CountAsync(filterSpec, cancellationToken);
+ int totalItems = await _itemRepository.CountAsync(filterSpec);
var pagedSpec = new CatalogFilterPaginatedSpecification(
- skip: request.PageIndex * request.PageSize,
- take: request.PageSize,
+ skip: request.PageIndex.Value * request.PageSize.Value,
+ take: request.PageSize.Value,
brandId: request.CatalogBrandId,
typeId: request.CatalogTypeId);
- var items = await _itemRepository.ListAsync(pagedSpec, cancellationToken);
+ var items = await _itemRepository.ListAsync(pagedSpec);
response.CatalogItems.AddRange(items.Select(_mapper.Map));
foreach (CatalogItemDto item in response.CatalogItems)
@@ -59,13 +62,13 @@ public class ListPaged : EndpointBaseAsync
if (request.PageSize > 0)
{
- response.PageCount = int.Parse(Math.Ceiling((decimal)totalItems / request.PageSize).ToString());
+ response.PageCount = int.Parse(Math.Ceiling((decimal)totalItems / request.PageSize.Value).ToString());
}
else
{
response.PageCount = totalItems > 0 ? 1 : 0;
}
- return Ok(response);
+ return Results.Ok(response);
}
}
diff --git a/src/PublicApi/CatalogItemEndpoints/Create.CreateCatalogItemRequest.cs b/src/PublicApi/CatalogItemEndpoints/CreateCatalogItemEndpoint.CreateCatalogItemRequest.cs
similarity index 100%
rename from src/PublicApi/CatalogItemEndpoints/Create.CreateCatalogItemRequest.cs
rename to src/PublicApi/CatalogItemEndpoints/CreateCatalogItemEndpoint.CreateCatalogItemRequest.cs
diff --git a/src/PublicApi/CatalogItemEndpoints/Create.CreateCatalogItemResponse.cs b/src/PublicApi/CatalogItemEndpoints/CreateCatalogItemEndpoint.CreateCatalogItemResponse.cs
similarity index 100%
rename from src/PublicApi/CatalogItemEndpoints/Create.CreateCatalogItemResponse.cs
rename to src/PublicApi/CatalogItemEndpoints/CreateCatalogItemEndpoint.CreateCatalogItemResponse.cs
diff --git a/src/PublicApi/CatalogItemEndpoints/Create.cs b/src/PublicApi/CatalogItemEndpoints/CreateCatalogItemEndpoint.cs
similarity index 59%
rename from src/PublicApi/CatalogItemEndpoints/Create.cs
rename to src/PublicApi/CatalogItemEndpoints/CreateCatalogItemEndpoint.cs
index da9b885..25527f9 100644
--- a/src/PublicApi/CatalogItemEndpoints/Create.cs
+++ b/src/PublicApi/CatalogItemEndpoints/CreateCatalogItemEndpoint.cs
@@ -1,52 +1,56 @@
-using System.Threading;
-using System.Threading.Tasks;
-using Ardalis.ApiEndpoints;
+using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using Microsoft.eShopWeb.ApplicationCore.Exceptions;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.ApplicationCore.Specifications;
-using Swashbuckle.AspNetCore.Annotations;
+using MinimalApi.Endpoint;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
-[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
-public class Create : EndpointBaseAsync
- .WithRequest
- .WithActionResult
+///
+/// Creates a new Catalog Item
+///
+public class CreateCatalogItemEndpoint : IEndpoint
{
- private readonly IRepository _itemRepository;
+ private IRepository _itemRepository;
private readonly IUriComposer _uriComposer;
- public Create(IRepository itemRepository,
- IUriComposer uriComposer)
+ public CreateCatalogItemEndpoint(IUriComposer uriComposer)
{
- _itemRepository = itemRepository;
_uriComposer = uriComposer;
}
- [HttpPost("api/catalog-items")]
- [SwaggerOperation(
- Summary = "Creates a new Catalog Item",
- Description = "Creates a new Catalog Item",
- OperationId = "catalog-items.create",
- Tags = new[] { "CatalogItemEndpoints" })
- ]
- public override async Task> HandleAsync(CreateCatalogItemRequest request, CancellationToken cancellationToken)
+ public void AddRoute(IEndpointRouteBuilder app)
+ {
+ app.MapPost("api/catalog-items",
+ [Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] async
+ (CreateCatalogItemRequest request, IRepository itemRepository) =>
+ {
+ _itemRepository = itemRepository;
+ return await HandleAsync(request);
+ })
+ .Produces()
+ .WithTags("CatalogItemEndpoints");
+ }
+
+ public async Task HandleAsync(CreateCatalogItemRequest request)
{
var response = new CreateCatalogItemResponse(request.CorrelationId());
var catalogItemNameSpecification = new CatalogItemNameSpecification(request.Name);
- var existingCataloogItem = await _itemRepository.CountAsync(catalogItemNameSpecification, cancellationToken);
+ var existingCataloogItem = await _itemRepository.CountAsync(catalogItemNameSpecification);
if (existingCataloogItem > 0)
{
throw new DuplicateException($"A catalogItem with name {request.Name} already exists");
}
var newItem = new CatalogItem(request.CatalogTypeId, request.CatalogBrandId, request.Description, request.Name, request.Price, request.PictureUri);
- newItem = await _itemRepository.AddAsync(newItem, cancellationToken);
+ newItem = await _itemRepository.AddAsync(newItem);
if (newItem.Id != 0)
{
@@ -55,7 +59,7 @@ public class Create : EndpointBaseAsync
// In production, we recommend uploading to a blob storage and deliver the image via CDN after a verification process.
newItem.UpdatePictureUri("eCatalog-item-default.png");
- await _itemRepository.UpdateAsync(newItem, cancellationToken);
+ await _itemRepository.UpdateAsync(newItem);
}
var dto = new CatalogItemDto
@@ -69,8 +73,6 @@ public class Create : EndpointBaseAsync
Price = newItem.Price
};
response.CatalogItem = dto;
- return response;
+ return Results.Created($"api/catalog-items/{dto.Id}", response);
}
-
-
}
diff --git a/src/PublicApi/CatalogItemEndpoints/Delete.DeleteCatalogItemRequest.cs b/src/PublicApi/CatalogItemEndpoints/Delete.DeleteCatalogItemRequest.cs
deleted file mode 100644
index 4fee76f..0000000
--- a/src/PublicApi/CatalogItemEndpoints/Delete.DeleteCatalogItemRequest.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-
-namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
-
-public class DeleteCatalogItemRequest : BaseRequest
-{
- //[FromRoute]
- public int CatalogItemId { get; set; }
-}
diff --git a/src/PublicApi/CatalogItemEndpoints/Delete.cs b/src/PublicApi/CatalogItemEndpoints/Delete.cs
deleted file mode 100644
index 1724bc4..0000000
--- a/src/PublicApi/CatalogItemEndpoints/Delete.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using System.Threading;
-using System.Threading.Tasks;
-using Ardalis.ApiEndpoints;
-using Microsoft.AspNetCore.Authentication.JwtBearer;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.eShopWeb.ApplicationCore.Entities;
-using Microsoft.eShopWeb.ApplicationCore.Interfaces;
-using Swashbuckle.AspNetCore.Annotations;
-
-namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
-
-[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
-public class Delete : EndpointBaseAsync
- .WithRequest
- .WithActionResult
-{
- private readonly IRepository _itemRepository;
-
- public Delete(IRepository itemRepository)
- {
- _itemRepository = itemRepository;
- }
-
- [HttpDelete("api/catalog-items/{CatalogItemId}")]
- [SwaggerOperation(
- Summary = "Deletes a Catalog Item",
- Description = "Deletes a Catalog Item",
- OperationId = "catalog-items.Delete",
- Tags = new[] { "CatalogItemEndpoints" })
- ]
- public override async Task> HandleAsync([FromRoute] DeleteCatalogItemRequest request, CancellationToken cancellationToken)
- {
- var response = new DeleteCatalogItemResponse(request.CorrelationId());
-
- var itemToDelete = await _itemRepository.GetByIdAsync(request.CatalogItemId, cancellationToken);
- if (itemToDelete is null) return NotFound();
-
- await _itemRepository.DeleteAsync(itemToDelete, cancellationToken);
-
- return Ok(response);
- }
-}
diff --git a/src/PublicApi/CatalogItemEndpoints/DeleteCatalogItemEndpoint.DeleteCatalogItemRequest.cs b/src/PublicApi/CatalogItemEndpoints/DeleteCatalogItemEndpoint.DeleteCatalogItemRequest.cs
new file mode 100644
index 0000000..cfdbb15
--- /dev/null
+++ b/src/PublicApi/CatalogItemEndpoints/DeleteCatalogItemEndpoint.DeleteCatalogItemRequest.cs
@@ -0,0 +1,11 @@
+namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
+
+public class DeleteCatalogItemRequest : BaseRequest
+{
+ public int CatalogItemId { get; init; }
+
+ public DeleteCatalogItemRequest(int catalogItemId)
+ {
+ CatalogItemId = catalogItemId;
+ }
+}
diff --git a/src/PublicApi/CatalogItemEndpoints/Delete.DeleteCatalogItemResponse.cs b/src/PublicApi/CatalogItemEndpoints/DeleteCatalogItemEndpoint.DeleteCatalogItemResponse.cs
similarity index 100%
rename from src/PublicApi/CatalogItemEndpoints/Delete.DeleteCatalogItemResponse.cs
rename to src/PublicApi/CatalogItemEndpoints/DeleteCatalogItemEndpoint.DeleteCatalogItemResponse.cs
diff --git a/src/PublicApi/CatalogItemEndpoints/DeleteCatalogItemEndpoint.cs b/src/PublicApi/CatalogItemEndpoints/DeleteCatalogItemEndpoint.cs
new file mode 100644
index 0000000..2a0d3e6
--- /dev/null
+++ b/src/PublicApi/CatalogItemEndpoints/DeleteCatalogItemEndpoint.cs
@@ -0,0 +1,45 @@
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.eShopWeb.ApplicationCore.Entities;
+using Microsoft.eShopWeb.ApplicationCore.Interfaces;
+using MinimalApi.Endpoint;
+
+namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
+
+///
+/// Deletes a Catalog Item
+///
+public class DeleteCatalogItemEndpoint : IEndpoint
+{
+ private IRepository _itemRepository;
+
+ public void AddRoute(IEndpointRouteBuilder app)
+ {
+ app.MapDelete("api/catalog-items/{catalogItemId}",
+ [Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] async
+ (int catalogItemId, IRepository itemRepository) =>
+ {
+ _itemRepository = itemRepository;
+ return await HandleAsync(new DeleteCatalogItemRequest(catalogItemId));
+ })
+ .Produces()
+ .WithTags("CatalogItemEndpoints");
+ }
+
+ public async Task HandleAsync(DeleteCatalogItemRequest request)
+ {
+ var response = new DeleteCatalogItemResponse(request.CorrelationId());
+
+ var itemToDelete = await _itemRepository.GetByIdAsync(request.CatalogItemId);
+ if (itemToDelete is null)
+ return Results.NotFound();
+
+ await _itemRepository.DeleteAsync(itemToDelete);
+
+ return Results.Ok(response);
+ }
+}
diff --git a/src/PublicApi/CatalogItemEndpoints/GetById.GetByIdCatalogItemRequest.cs b/src/PublicApi/CatalogItemEndpoints/GetById.GetByIdCatalogItemRequest.cs
deleted file mode 100644
index ab0b2fb..0000000
--- a/src/PublicApi/CatalogItemEndpoints/GetById.GetByIdCatalogItemRequest.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
-
-public class GetByIdCatalogItemRequest : BaseRequest
-{
- public int CatalogItemId { get; set; }
-}
diff --git a/src/PublicApi/CatalogItemEndpoints/GetById.cs b/src/PublicApi/CatalogItemEndpoints/GetById.cs
deleted file mode 100644
index 62352cf..0000000
--- a/src/PublicApi/CatalogItemEndpoints/GetById.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using System.Threading;
-using System.Threading.Tasks;
-using Ardalis.ApiEndpoints;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.eShopWeb.ApplicationCore.Entities;
-using Microsoft.eShopWeb.ApplicationCore.Interfaces;
-using Swashbuckle.AspNetCore.Annotations;
-
-namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
-
-public class GetById : EndpointBaseAsync
- .WithRequest
- .WithActionResult
-{
- private readonly IRepository _itemRepository;
- private readonly IUriComposer _uriComposer;
-
- public GetById(IRepository itemRepository, IUriComposer uriComposer)
- {
- _itemRepository = itemRepository;
- _uriComposer = uriComposer;
- }
-
- [HttpGet("api/catalog-items/{CatalogItemId}")]
- [SwaggerOperation(
- Summary = "Get a Catalog Item by Id",
- Description = "Gets a Catalog Item by Id",
- OperationId = "catalog-items.GetById",
- Tags = new[] { "CatalogItemEndpoints" })
- ]
- public override async Task> HandleAsync([FromRoute] GetByIdCatalogItemRequest request, CancellationToken cancellationToken)
- {
- var response = new GetByIdCatalogItemResponse(request.CorrelationId());
-
- var item = await _itemRepository.GetByIdAsync(request.CatalogItemId, cancellationToken);
- if (item is null) return NotFound();
-
- response.CatalogItem = new CatalogItemDto
- {
- Id = item.Id,
- CatalogBrandId = item.CatalogBrandId,
- CatalogTypeId = item.CatalogTypeId,
- Description = item.Description,
- Name = item.Name,
- PictureUri = _uriComposer.ComposePicUri(item.PictureUri),
- Price = item.Price
- };
- return Ok(response);
- }
-}
diff --git a/src/PublicApi/CatalogItemEndpoints/ListPaged.ListPagedCatalogItemRequest.cs b/src/PublicApi/CatalogItemEndpoints/ListPaged.ListPagedCatalogItemRequest.cs
deleted file mode 100644
index 7c48e9b..0000000
--- a/src/PublicApi/CatalogItemEndpoints/ListPaged.ListPagedCatalogItemRequest.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
-
-public class ListPagedCatalogItemRequest : BaseRequest
-{
- public int PageSize { get; set; }
- public int PageIndex { get; set; }
- public int? CatalogBrandId { get; set; }
- public int? CatalogTypeId { get; set; }
-}
diff --git a/src/PublicApi/CatalogItemEndpoints/Update.cs b/src/PublicApi/CatalogItemEndpoints/Update.cs
deleted file mode 100644
index a960162..0000000
--- a/src/PublicApi/CatalogItemEndpoints/Update.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using Ardalis.ApiEndpoints;
-using Microsoft.AspNetCore.Authentication.JwtBearer;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.eShopWeb.ApplicationCore.Entities;
-using Microsoft.eShopWeb.ApplicationCore.Interfaces;
-using Swashbuckle.AspNetCore.Annotations;
-
-namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
-
-[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
-public class Update : EndpointBaseAsync
- .WithRequest
- .WithActionResult
-{
- private readonly IRepository _itemRepository;
- private readonly IUriComposer _uriComposer;
-
- public Update(IRepository itemRepository, IUriComposer uriComposer)
- {
- _itemRepository = itemRepository;
- _uriComposer = uriComposer;
- }
-
- [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, CancellationToken cancellationToken)
- {
- var response = new UpdateCatalogItemResponse(request.CorrelationId());
-
- var existingItem = await _itemRepository.GetByIdAsync(request.Id, cancellationToken);
-
- existingItem.UpdateDetails(request.Name, request.Description, request.Price);
- existingItem.UpdateBrand(request.CatalogBrandId);
- existingItem.UpdateType(request.CatalogTypeId);
-
- await _itemRepository.UpdateAsync(existingItem, cancellationToken);
-
- var dto = new CatalogItemDto
- {
- Id = existingItem.Id,
- CatalogBrandId = existingItem.CatalogBrandId,
- CatalogTypeId = existingItem.CatalogTypeId,
- Description = existingItem.Description,
- Name = existingItem.Name,
- PictureUri = _uriComposer.ComposePicUri(existingItem.PictureUri),
- Price = existingItem.Price
- };
- response.CatalogItem = dto;
- return response;
- }
-}
diff --git a/src/PublicApi/CatalogItemEndpoints/Update.UpdateCatalogItemRequest.cs b/src/PublicApi/CatalogItemEndpoints/UpdateCatalogItemEndpoint.UpdateCatalogItemRequest.cs
similarity index 100%
rename from src/PublicApi/CatalogItemEndpoints/Update.UpdateCatalogItemRequest.cs
rename to src/PublicApi/CatalogItemEndpoints/UpdateCatalogItemEndpoint.UpdateCatalogItemRequest.cs
diff --git a/src/PublicApi/CatalogItemEndpoints/Update.UpdateCatalogItemResponse.cs b/src/PublicApi/CatalogItemEndpoints/UpdateCatalogItemEndpoint.UpdateCatalogItemResponse.cs
similarity index 100%
rename from src/PublicApi/CatalogItemEndpoints/Update.UpdateCatalogItemResponse.cs
rename to src/PublicApi/CatalogItemEndpoints/UpdateCatalogItemEndpoint.UpdateCatalogItemResponse.cs
diff --git a/src/PublicApi/CatalogItemEndpoints/UpdateCatalogItemEndpoint.cs b/src/PublicApi/CatalogItemEndpoints/UpdateCatalogItemEndpoint.cs
new file mode 100644
index 0000000..7bc715b
--- /dev/null
+++ b/src/PublicApi/CatalogItemEndpoints/UpdateCatalogItemEndpoint.cs
@@ -0,0 +1,64 @@
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.eShopWeb.ApplicationCore.Entities;
+using Microsoft.eShopWeb.ApplicationCore.Interfaces;
+using MinimalApi.Endpoint;
+
+namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
+
+///
+/// Updates a Catalog Item
+///
+public class UpdateCatalogItemEndpoint : IEndpoint
+{
+ private IRepository _itemRepository;
+ private readonly IUriComposer _uriComposer;
+
+ public UpdateCatalogItemEndpoint(IUriComposer uriComposer)
+ {
+ _uriComposer = uriComposer;
+ }
+
+ public void AddRoute(IEndpointRouteBuilder app)
+ {
+ app.MapPut("api/catalog-items",
+ [Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] async
+ (UpdateCatalogItemRequest request, IRepository itemRepository) =>
+ {
+ _itemRepository = itemRepository;
+ return await HandleAsync(request);
+ })
+ .Produces()
+ .WithTags("CatalogItemEndpoints");
+ }
+
+ public 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 = _uriComposer.ComposePicUri(existingItem.PictureUri),
+ Price = existingItem.Price
+ };
+ response.CatalogItem = dto;
+ return Results.Ok(response);
+ }
+}
diff --git a/src/PublicApi/CatalogTypeEndpoints/List.ListCatalogTypesResponse.cs b/src/PublicApi/CatalogTypeEndpoints/CatalogTypeListEndpoint.ListCatalogTypesResponse.cs
similarity index 100%
rename from src/PublicApi/CatalogTypeEndpoints/List.ListCatalogTypesResponse.cs
rename to src/PublicApi/CatalogTypeEndpoints/CatalogTypeListEndpoint.ListCatalogTypesResponse.cs
diff --git a/src/PublicApi/CatalogTypeEndpoints/CatalogTypeListEndpoint.cs b/src/PublicApi/CatalogTypeEndpoints/CatalogTypeListEndpoint.cs
new file mode 100644
index 0000000..87aa035
--- /dev/null
+++ b/src/PublicApi/CatalogTypeEndpoints/CatalogTypeListEndpoint.cs
@@ -0,0 +1,48 @@
+using System.Linq;
+using System.Threading.Tasks;
+using AutoMapper;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.eShopWeb.ApplicationCore.Entities;
+using Microsoft.eShopWeb.ApplicationCore.Interfaces;
+using MinimalApi.Endpoint;
+
+namespace Microsoft.eShopWeb.PublicApi.CatalogTypeEndpoints;
+
+///
+/// List Catalog Types
+///
+public class CatalogTypeListEndpoint : IEndpoint
+{
+ private IRepository _catalogTypeRepository;
+ private readonly IMapper _mapper;
+
+ public CatalogTypeListEndpoint(IMapper mapper)
+ {
+ _mapper = mapper;
+ }
+
+ public void AddRoute(IEndpointRouteBuilder app)
+ {
+ app.MapGet("api/catalog-types",
+ async (IRepository catalogTypeRepository) =>
+ {
+ _catalogTypeRepository = catalogTypeRepository;
+ return await HandleAsync();
+ })
+ .Produces()
+ .WithTags("CatalogTypeEndpoints");
+ }
+
+ public async Task HandleAsync()
+ {
+ var response = new ListCatalogTypesResponse();
+
+ var items = await _catalogTypeRepository.ListAsync();
+
+ response.CatalogTypes.AddRange(items.Select(_mapper.Map));
+
+ return Results.Ok(response);
+ }
+}
diff --git a/src/PublicApi/CatalogTypeEndpoints/List.cs b/src/PublicApi/CatalogTypeEndpoints/List.cs
deleted file mode 100644
index eb1b212..0000000
--- a/src/PublicApi/CatalogTypeEndpoints/List.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Ardalis.ApiEndpoints;
-using AutoMapper;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.eShopWeb.ApplicationCore.Entities;
-using Microsoft.eShopWeb.ApplicationCore.Interfaces;
-using Swashbuckle.AspNetCore.Annotations;
-
-namespace Microsoft.eShopWeb.PublicApi.CatalogTypeEndpoints;
-
-public class List : EndpointBaseAsync
- .WithoutRequest
- .WithActionResult
-{
- private readonly IRepository _catalogTypeRepository;
- private readonly IMapper _mapper;
-
- public List(IRepository catalogTypeRepository,
- IMapper mapper)
- {
- _catalogTypeRepository = catalogTypeRepository;
- _mapper = mapper;
- }
-
- [HttpGet("api/catalog-types")]
- [SwaggerOperation(
- Summary = "List Catalog Types",
- Description = "List Catalog Types",
- OperationId = "catalog-types.List",
- Tags = new[] { "CatalogTypeEndpoints" })
- ]
- public override async Task> HandleAsync(CancellationToken cancellationToken)
- {
- var response = new ListCatalogTypesResponse();
-
- var items = await _catalogTypeRepository.ListAsync(cancellationToken);
-
- response.CatalogTypes.AddRange(items.Select(_mapper.Map));
-
- return Ok(response);
- }
-}
diff --git a/src/PublicApi/Program.cs b/src/PublicApi/Program.cs
index 176d9be..aeb11bc 100644
--- a/src/PublicApi/Program.cs
+++ b/src/PublicApi/Program.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text;
-using System.Threading.Tasks;
using BlazorShared;
using BlazorShared.Models;
using MediatR;
@@ -9,7 +8,6 @@ using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
-using Microsoft.EntityFrameworkCore;
using Microsoft.eShopWeb;
using Microsoft.eShopWeb.ApplicationCore.Constants;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
@@ -25,20 +23,18 @@ using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
+using MinimalApi.Endpoint.Configurations.Extensions;
+using MinimalApi.Endpoint.Extensions;
var builder = WebApplication.CreateBuilder(args);
+builder.Services.AddEndpoints();
+
+//Use to force loading of appsettings.json of test project
+builder.Configuration.AddConfigurationFile();
builder.Logging.AddConsole();
-// use real database
-// Requires LocalDB which can be installed with SQL Server Express 2016
-// https://www.microsoft.com/en-us/download/details.aspx?id=54284
-builder.Services.AddDbContext(c =>
- c.UseSqlServer(builder.Configuration.GetConnectionString("CatalogConnection")));
-
-// Add Identity DbContext
-builder.Services.AddDbContext(options =>
- options.UseSqlServer(builder.Configuration.GetConnectionString("IdentityConnection")));
+Microsoft.eShopWeb.Infrastructure.Dependencies.ConfigureServices(builder.Configuration, builder.Services);
builder.Services.AddIdentity()
.AddEntityFrameworkStores()
@@ -92,6 +88,7 @@ builder.Services.AddControllers();
builder.Services.AddMediatR(typeof(CatalogItem).Assembly);
builder.Services.AddAutoMapper(typeof(MappingProfile).Assembly);
+builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
@@ -182,5 +179,8 @@ using (var scope = app.Services.CreateScope())
}
}
+app.MapEndpoints();
app.Logger.LogInformation("LAUNCHING PublicApi");
app.Run();
+
+public partial class Program { }
diff --git a/src/PublicApi/PublicApi.csproj b/src/PublicApi/PublicApi.csproj
index e650086..c8e0952 100644
--- a/src/PublicApi/PublicApi.csproj
+++ b/src/PublicApi/PublicApi.csproj
@@ -13,21 +13,22 @@
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/src/Web/Pages/Basket/BasketItemViewModel.cs b/src/Web/Pages/Basket/BasketItemViewModel.cs
index b8f65f5..c102dfb 100644
--- a/src/Web/Pages/Basket/BasketItemViewModel.cs
+++ b/src/Web/Pages/Basket/BasketItemViewModel.cs
@@ -6,12 +6,12 @@ public class BasketItemViewModel
{
public int Id { get; set; }
public int CatalogItemId { get; set; }
- public string ProductName { get; set; }
+ public string? ProductName { get; set; }
public decimal UnitPrice { get; set; }
public decimal OldUnitPrice { get; set; }
[Range(0, int.MaxValue, ErrorMessage = "Quantity must be bigger than 0")]
public int Quantity { get; set; }
- public string PictureUrl { get; set; }
+ public string? PictureUrl { get; set; }
}
diff --git a/src/Web/Program.cs b/src/Web/Program.cs
index 7e04235..2e78ab3 100644
--- a/src/Web/Program.cs
+++ b/src/Web/Program.cs
@@ -8,7 +8,6 @@ using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
-using Microsoft.EntityFrameworkCore;
using Microsoft.eShopWeb;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.Infrastructure.Data;
@@ -22,15 +21,7 @@ var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddConsole();
-// use real database
-// Requires LocalDB which can be installed with SQL Server Express 2016
-// https://www.microsoft.com/en-us/download/details.aspx?id=54284
-builder.Services.AddDbContext(c =>
- c.UseSqlServer(builder.Configuration.GetConnectionString("CatalogConnection")));
-
-// Add Identity DbContext
-builder.Services.AddDbContext(options =>
- options.UseSqlServer(builder.Configuration.GetConnectionString("IdentityConnection")));
+Microsoft.eShopWeb.Infrastructure.Dependencies.ConfigureServices(builder.Configuration, builder.Services);
builder.Services.AddCookieSettings();
diff --git a/src/Web/Services/BasketViewModelService.cs b/src/Web/Services/BasketViewModelService.cs
index 06cfdf4..e07ca61 100644
--- a/src/Web/Services/BasketViewModelService.cs
+++ b/src/Web/Services/BasketViewModelService.cs
@@ -1,7 +1,4 @@
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.eShopWeb.ApplicationCore.Entities;
+using Microsoft.eShopWeb.ApplicationCore.Entities;
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.ApplicationCore.Specifications;
diff --git a/src/Web/Web.csproj b/src/Web/Web.csproj
index ee60c65..aac5b0e 100644
--- a/src/Web/Web.csproj
+++ b/src/Web/Web.csproj
@@ -20,16 +20,16 @@
-
-
-
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/Web/appsettings.json b/src/Web/appsettings.json
index c2bc659..70989a6 100644
--- a/src/Web/appsettings.json
+++ b/src/Web/appsettings.json
@@ -17,4 +17,4 @@
},
"AllowedHosts": "*"
}
-}
+}
\ No newline at end of file
diff --git a/tests/FunctionalTests/FunctionalTests.csproj b/tests/FunctionalTests/FunctionalTests.csproj
index 2599f18..1c6a574 100644
--- a/tests/FunctionalTests/FunctionalTests.csproj
+++ b/tests/FunctionalTests/FunctionalTests.csproj
@@ -4,6 +4,8 @@
net6.0
Microsoft.eShopWeb.FunctionalTests
false
+ enable
+ enable
@@ -13,14 +15,14 @@
-
+
all
runtime; build; native; contentfiles; analyzers
-
+
diff --git a/tests/FunctionalTests/PublicApi/ApiTestFixture.cs b/tests/FunctionalTests/PublicApi/ApiTestFixture.cs
index 4513284..ec8170f 100644
--- a/tests/FunctionalTests/PublicApi/ApiTestFixture.cs
+++ b/tests/FunctionalTests/PublicApi/ApiTestFixture.cs
@@ -9,7 +9,7 @@ using Microsoft.Extensions.Hosting;
namespace Microsoft.eShopWeb.FunctionalTests.PublicApi;
-public class TestApiApplication : WebApplicationFactory
+public class TestApiApplication : WebApplicationFactory
{
private readonly string _environment = "Testing";
diff --git a/tests/FunctionalTests/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs b/tests/FunctionalTests/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs
index d695245..4cec643 100644
--- a/tests/FunctionalTests/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs
+++ b/tests/FunctionalTests/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs
@@ -1,43 +1,43 @@
-using System.Net.Http;
-using System.Text;
-using System.Text.Json;
-using System.Threading.Tasks;
-using Microsoft.eShopWeb.ApplicationCore.Constants;
-using Microsoft.eShopWeb.FunctionalTests.PublicApi;
-using Microsoft.eShopWeb.PublicApi.AuthEndpoints;
-using Xunit;
+//using System.Net.Http;
+//using System.Text;
+//using System.Text.Json;
+//using System.Threading.Tasks;
+//using Microsoft.eShopWeb.ApplicationCore.Constants;
+//using Microsoft.eShopWeb.FunctionalTests.PublicApi;
+//using Microsoft.eShopWeb.PublicApi.AuthEndpoints;
+//using Xunit;
-namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers;
+//namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers;
-[Collection("Sequential")]
-public class AuthenticateEndpoint : IClassFixture
-{
- JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
+//[Collection("Sequential")]
+//public class AuthenticateEndpoint : IClassFixture
+//{
+// JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
- public AuthenticateEndpoint(TestApiApplication factory)
- {
- Client = factory.CreateClient();
- }
+// public AuthenticateEndpoint(TestApiApplication factory)
+// {
+// Client = factory.CreateClient();
+// }
- public HttpClient Client { get; }
+// public HttpClient Client { get; }
- [Theory]
- [InlineData("demouser@microsoft.com", AuthorizationConstants.DEFAULT_PASSWORD, true)]
- [InlineData("demouser@microsoft.com", "badpassword", false)]
- [InlineData("baduser@microsoft.com", "badpassword", false)]
- public async Task ReturnsExpectedResultGivenCredentials(string testUsername, string testPassword, bool expectedResult)
- {
- var request = new AuthenticateRequest()
- {
- Username = testUsername,
- Password = testPassword
- };
- var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json");
- var response = await Client.PostAsync("api/authenticate", jsonContent);
- response.EnsureSuccessStatusCode();
- var stringResponse = await response.Content.ReadAsStringAsync();
- var model = stringResponse.FromJson();
+// [Theory]
+// [InlineData("demouser@microsoft.com", AuthorizationConstants.DEFAULT_PASSWORD, true)]
+// [InlineData("demouser@microsoft.com", "badpassword", false)]
+// [InlineData("baduser@microsoft.com", "badpassword", false)]
+// public async Task ReturnsExpectedResultGivenCredentials(string testUsername, string testPassword, bool expectedResult)
+// {
+// var request = new AuthenticateRequest()
+// {
+// Username = testUsername,
+// Password = testPassword
+// };
+// var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json");
+// var response = await Client.PostAsync("api/authenticate", jsonContent);
+// response.EnsureSuccessStatusCode();
+// var stringResponse = await response.Content.ReadAsStringAsync();
+// var model = stringResponse.FromJson();
- Assert.Equal(expectedResult, model.Result);
- }
-}
+// Assert.Equal(expectedResult, model.Result);
+// }
+//}
diff --git a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/ApiCatalogControllerList.cs b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/ApiCatalogControllerList.cs
deleted file mode 100644
index 1870e73..0000000
--- a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/ApiCatalogControllerList.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using System.Linq;
-using System.Net.Http;
-using System.Threading.Tasks;
-using Microsoft.eShopWeb.FunctionalTests.PublicApi;
-using Microsoft.eShopWeb.Web.ViewModels;
-using Xunit;
-
-namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers;
-
-[Collection("Sequential")]
-public class ApiCatalogControllerList : IClassFixture
-{
- public ApiCatalogControllerList(TestApiApplication factory)
- {
- Client = factory.CreateClient();
- }
-
- public HttpClient Client { get; }
-
- [Fact]
- public async Task ReturnsFirst10CatalogItems()
- {
- var response = await Client.GetAsync("/api/catalog-items?pageSize=10");
- response.EnsureSuccessStatusCode();
- var stringResponse = await response.Content.ReadAsStringAsync();
- var model = stringResponse.FromJson();
-
- Assert.Equal(10, model.CatalogItems.Count());
- }
-
- [Fact]
- public async Task ReturnsLast2CatalogItemsGivenPageIndex1()
- {
- var response = await Client.GetAsync("/api/catalog-items?pageSize=10&pageIndex=1");
- response.EnsureSuccessStatusCode();
- var stringResponse = await response.Content.ReadAsStringAsync();
- var model = stringResponse.FromJson();
-
- Assert.Equal(2, model.CatalogItems.Count());
- }
-}
diff --git a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/CreateEndpoint.cs b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/CreateEndpoint.cs
deleted file mode 100644
index 3b75095..0000000
--- a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/CreateEndpoint.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using System.Net;
-using System.Net.Http;
-using System.Net.Http.Headers;
-using System.Text;
-using System.Text.Json;
-using System.Threading.Tasks;
-using Microsoft.eShopWeb.FunctionalTests.PublicApi;
-using Microsoft.eShopWeb.FunctionalTests.Web.Api;
-using Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
-using Xunit;
-
-namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers;
-
-[Collection("Sequential")]
-public class CreateEndpoint : IClassFixture
-{
- JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
- private int _testBrandId = 1;
- private int _testTypeId = 2;
- private string _testDescription = "test description";
- private string _testName = "test name";
- private decimal _testPrice = 1.23m;
-
- public CreateEndpoint(TestApiApplication factory)
- {
- Client = factory.CreateClient();
- }
-
- public HttpClient Client { get; }
-
- [Fact]
- public async Task ReturnsNotAuthorizedGivenNormalUserToken()
- {
- var jsonContent = GetValidNewItemJson();
- var token = ApiTokenHelper.GetNormalUserToken();
- Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
- var response = await Client.PostAsync("api/catalog-items", jsonContent);
-
- Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
- }
-
- [Fact]
- public async Task ReturnsSuccessGivenValidNewItemAndAdminUserToken()
- {
- var jsonContent = GetValidNewItemJson();
- var adminToken = ApiTokenHelper.GetAdminUserToken();
- Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", adminToken);
- var response = await Client.PostAsync("api/catalog-items", jsonContent);
- response.EnsureSuccessStatusCode();
- var stringResponse = await response.Content.ReadAsStringAsync();
- var model = stringResponse.FromJson();
-
- Assert.Equal(_testBrandId, model.CatalogItem.CatalogBrandId);
- Assert.Equal(_testTypeId, model.CatalogItem.CatalogTypeId);
- Assert.Equal(_testDescription, model.CatalogItem.Description);
- Assert.Equal(_testName, model.CatalogItem.Name);
- Assert.Equal(_testPrice, model.CatalogItem.Price);
- }
-
- private StringContent GetValidNewItemJson()
- {
- var request = new CreateCatalogItemRequest()
- {
- CatalogBrandId = _testBrandId,
- CatalogTypeId = _testTypeId,
- Description = _testDescription,
- Name = _testName,
- Price = _testPrice
- };
- var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json");
-
- return jsonContent;
- }
-}
diff --git a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/DeleteEndpoint.cs b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/DeleteEndpoint.cs
deleted file mode 100644
index 5d62e12..0000000
--- a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/DeleteEndpoint.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using System.Net;
-using System.Net.Http;
-using System.Net.Http.Headers;
-using System.Text.Json;
-using System.Threading.Tasks;
-using Microsoft.eShopWeb.FunctionalTests.PublicApi;
-using Microsoft.eShopWeb.FunctionalTests.Web.Api;
-using Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
-using Xunit;
-
-namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers;
-
-[Collection("Sequential")]
-public class DeleteEndpoint : IClassFixture
-{
- JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
-
- public DeleteEndpoint(TestApiApplication factory)
- {
- Client = factory.CreateClient();
- }
-
- public HttpClient Client { get; }
-
- [Fact]
- public async Task ReturnsSuccessGivenValidIdAndAdminUserToken()
- {
- var adminToken = ApiTokenHelper.GetAdminUserToken();
- Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", adminToken);
- var response = await Client.DeleteAsync("api/catalog-items/12");
- response.EnsureSuccessStatusCode();
- var stringResponse = await response.Content.ReadAsStringAsync();
- var model = stringResponse.FromJson();
-
- Assert.Equal("Deleted", model.Status);
- }
-
- [Fact]
- public async Task ReturnsNotFoundGivenInvalidIdAndAdminUserToken()
- {
- var adminToken = ApiTokenHelper.GetAdminUserToken();
- Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", adminToken);
- var response = await Client.DeleteAsync("api/catalog-items/0");
-
- Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
- }
-}
diff --git a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/GetByIdEndpoint.cs b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/GetByIdEndpoint.cs
deleted file mode 100644
index 0d7e1b4..0000000
--- a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/GetByIdEndpoint.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using System.Net;
-using System.Net.Http;
-using System.Text.Json;
-using System.Threading.Tasks;
-using Microsoft.eShopWeb.FunctionalTests.PublicApi;
-using Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
-using Xunit;
-
-namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers;
-
-[Collection("Sequential")]
-public class GetByIdEndpoint : IClassFixture
-{
- JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
-
- public GetByIdEndpoint(TestApiApplication factory)
- {
- Client = factory.CreateClient();
- }
-
- public HttpClient Client { get; }
-
- [Fact]
- public async Task ReturnsItemGivenValidId()
- {
- var response = await Client.GetAsync("api/catalog-items/5");
- response.EnsureSuccessStatusCode();
- var stringResponse = await response.Content.ReadAsStringAsync();
- var model = stringResponse.FromJson();
-
- Assert.Equal(5, model.CatalogItem.Id);
- Assert.Equal("Roslyn Red Sheet", model.CatalogItem.Name);
- }
-
- [Fact]
- public async Task ReturnsNotFoundGivenInvalidId()
- {
- var response = await Client.GetAsync("api/catalog-items/0");
-
- Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
- }
-}
diff --git a/tests/IntegrationTests/IntegrationTests.csproj b/tests/IntegrationTests/IntegrationTests.csproj
index 9512dee..98dd06e 100644
--- a/tests/IntegrationTests/IntegrationTests.csproj
+++ b/tests/IntegrationTests/IntegrationTests.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/tests/PublicApiIntegrationTests/ApiTokenHelper.cs b/tests/PublicApiIntegrationTests/ApiTokenHelper.cs
new file mode 100644
index 0000000..75fd1b1
--- /dev/null
+++ b/tests/PublicApiIntegrationTests/ApiTokenHelper.cs
@@ -0,0 +1,50 @@
+using Microsoft.eShopWeb.ApplicationCore.Constants;
+using Microsoft.IdentityModel.Tokens;
+using System;
+using System.Collections.Generic;
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using System.Text;
+
+namespace PublicApiIntegrationTests
+{
+ public class ApiTokenHelper
+ {
+ public static string GetAdminUserToken()
+ {
+ string userName = "admin@microsoft.com";
+ string[] roles = { "Administrators" };
+
+ return CreateToken(userName, roles);
+ }
+
+ public static string GetNormalUserToken()
+ {
+ string userName = "demouser@microsoft.com";
+ string[] roles = { };
+
+ return CreateToken(userName, roles);
+ }
+
+ private static string CreateToken(string userName, string[] roles)
+ {
+ var claims = new List { new Claim(ClaimTypes.Name, userName) };
+
+ foreach (var role in roles)
+ {
+ claims.Add(new Claim(ClaimTypes.Role, role));
+ }
+
+ var key = Encoding.ASCII.GetBytes(AuthorizationConstants.JWT_SECRET_KEY);
+ var tokenDescriptor = new SecurityTokenDescriptor
+ {
+ Subject = new ClaimsIdentity(claims.ToArray()),
+ Expires = DateTime.UtcNow.AddHours(1),
+ SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
+ };
+ var tokenHandler = new JwtSecurityTokenHandler();
+ var token = tokenHandler.CreateToken(tokenDescriptor);
+ return tokenHandler.WriteToken(token);
+ }
+ }
+}
diff --git a/tests/PublicApiIntegrationTests/AuthEndpoints/AuthenticateEndpointTest.cs b/tests/PublicApiIntegrationTests/AuthEndpoints/AuthenticateEndpointTest.cs
new file mode 100644
index 0000000..3d2aa93
--- /dev/null
+++ b/tests/PublicApiIntegrationTests/AuthEndpoints/AuthenticateEndpointTest.cs
@@ -0,0 +1,36 @@
+using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.eShopWeb;
+using Microsoft.eShopWeb.ApplicationCore.Constants;
+using Microsoft.eShopWeb.PublicApi.AuthEndpoints;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Net.Http;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+namespace PublicApiIntegrationTests.AuthEndpoints
+{
+ [TestClass]
+ public class AuthenticateEndpoint
+ {
+ [TestMethod]
+ [DataRow("demouser@microsoft.com", AuthorizationConstants.DEFAULT_PASSWORD, true)]
+ [DataRow("demouser@microsoft.com", "badpassword", false)]
+ [DataRow("baduser@microsoft.com", "badpassword", false)]
+ public async Task ReturnsExpectedResultGivenCredentials(string testUsername, string testPassword, bool expectedResult)
+ {
+ var request = new AuthenticateRequest()
+ {
+ Username = testUsername,
+ Password = testPassword
+ };
+ var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json");
+ var response = await ProgramTest.NewClient.PostAsync("api/authenticate", jsonContent);
+ response.EnsureSuccessStatusCode();
+ var stringResponse = await response.Content.ReadAsStringAsync();
+ var model = stringResponse.FromJson();
+
+ Assert.AreEqual(expectedResult, model.Result);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/PublicApiIntegrationTests/CatalogItemEndpoints/CatalogItemGetByIdEndpointTest.cs b/tests/PublicApiIntegrationTests/CatalogItemEndpoints/CatalogItemGetByIdEndpointTest.cs
new file mode 100644
index 0000000..5882db0
--- /dev/null
+++ b/tests/PublicApiIntegrationTests/CatalogItemEndpoints/CatalogItemGetByIdEndpointTest.cs
@@ -0,0 +1,32 @@
+using Microsoft.eShopWeb;
+using Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Net;
+using System.Threading.Tasks;
+
+namespace PublicApiIntegrationTests.CatalogItemEndpoints
+{
+ [TestClass]
+ public class CatalogItemGetByIdEndpointTest
+ {
+ [TestMethod]
+ public async Task ReturnsItemGivenValidId()
+ {
+ var response = await ProgramTest.NewClient.GetAsync("api/catalog-items/5");
+ response.EnsureSuccessStatusCode();
+ var stringResponse = await response.Content.ReadAsStringAsync();
+ var model = stringResponse.FromJson();
+
+ Assert.AreEqual(5, model.CatalogItem.Id);
+ Assert.AreEqual("Roslyn Red Sheet", model.CatalogItem.Name);
+ }
+
+ [TestMethod]
+ public async Task ReturnsNotFoundGivenInvalidId()
+ {
+ var response = await ProgramTest.NewClient.GetAsync("api/catalog-items/0");
+
+ Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode);
+ }
+ }
+}
diff --git a/tests/PublicApiIntegrationTests/CatalogItemEndpoints/CatalogItemListPagedEndpoint.cs b/tests/PublicApiIntegrationTests/CatalogItemEndpoints/CatalogItemListPagedEndpoint.cs
new file mode 100644
index 0000000..1040463
--- /dev/null
+++ b/tests/PublicApiIntegrationTests/CatalogItemEndpoints/CatalogItemListPagedEndpoint.cs
@@ -0,0 +1,49 @@
+using Microsoft.eShopWeb;
+using Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
+using Microsoft.eShopWeb.Web.ViewModels;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace PublicApiIntegrationTests.CatalogItemEndpoints
+{
+ [TestClass]
+ public class CatalogItemListPagedEndpoint
+ {
+ [TestMethod]
+ public async Task ReturnsFirst10CatalogItems()
+ {
+ var client = ProgramTest.NewClient;
+ var response = await client.GetAsync("/api/catalog-items?pageSize=10");
+ response.EnsureSuccessStatusCode();
+ var stringResponse = await response.Content.ReadAsStringAsync();
+ var model = stringResponse.FromJson();
+
+ Assert.AreEqual(10, model.CatalogItems.Count());
+ }
+
+ [TestMethod]
+ public async Task ReturnsCorrectCatalogItemsGivenPageIndex1()
+ {
+
+ var pageSize = 10;
+ var pageIndex = 1;
+
+ var client = ProgramTest.NewClient;
+ var response = await client.GetAsync($"/api/catalog-items");
+ response.EnsureSuccessStatusCode();
+ var stringResponse = await response.Content.ReadAsStringAsync();
+ var model = stringResponse.FromJson();
+ var totalItem = model.CatalogItems.Count();
+
+ var response2 = await client.GetAsync($"/api/catalog-items?pageSize={pageSize}&pageIndex={pageIndex}");
+ response.EnsureSuccessStatusCode();
+ var stringResponse2 = await response2.Content.ReadAsStringAsync();
+ var model2 = stringResponse2.FromJson();
+
+ var totalExpected = totalItem - (pageSize * pageIndex);
+
+ Assert.AreEqual(totalExpected, model2.CatalogItems.Count());
+ }
+ }
+}
diff --git a/tests/PublicApiIntegrationTests/CatalogItemEndpoints/CreateCatalogItemEndpointTest.cs b/tests/PublicApiIntegrationTests/CatalogItemEndpoints/CreateCatalogItemEndpointTest.cs
new file mode 100644
index 0000000..a85923d
--- /dev/null
+++ b/tests/PublicApiIntegrationTests/CatalogItemEndpoints/CreateCatalogItemEndpointTest.cs
@@ -0,0 +1,69 @@
+using BlazorShared.Models;
+using Microsoft.eShopWeb;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Net;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+namespace PublicApiIntegrationTests.AuthEndpoints
+{
+ [TestClass]
+ public class CreateCatalogItemEndpointTest
+ {
+ private int _testBrandId = 1;
+ private int _testTypeId = 2;
+ private string _testDescription = "test description";
+ private string _testName = "test name";
+ private decimal _testPrice = 1.23m;
+
+
+ [TestMethod]
+ public async Task ReturnsNotAuthorizedGivenNormalUserToken()
+ {
+ var jsonContent = GetValidNewItemJson();
+ var token = ApiTokenHelper.GetNormalUserToken();
+ var client = ProgramTest.NewClient;
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
+ var response = await client.PostAsync("api/catalog-items", jsonContent);
+
+ Assert.AreEqual(HttpStatusCode.Forbidden, response.StatusCode);
+ }
+
+ [TestMethod]
+ public async Task ReturnsSuccessGivenValidNewItemAndAdminUserToken()
+ {
+ var jsonContent = GetValidNewItemJson();
+ var adminToken = ApiTokenHelper.GetAdminUserToken();
+ var client = ProgramTest.NewClient;
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", adminToken);
+ var response = await client.PostAsync("api/catalog-items", jsonContent);
+ response.EnsureSuccessStatusCode();
+ var stringResponse = await response.Content.ReadAsStringAsync();
+ var model = stringResponse.FromJson();
+
+ Assert.AreEqual(_testBrandId, model.CatalogItem.CatalogBrandId);
+ Assert.AreEqual(_testTypeId, model.CatalogItem.CatalogTypeId);
+ Assert.AreEqual(_testDescription, model.CatalogItem.Description);
+ Assert.AreEqual(_testName, model.CatalogItem.Name);
+ Assert.AreEqual(_testPrice, model.CatalogItem.Price);
+ }
+
+ private StringContent GetValidNewItemJson()
+ {
+ var request = new CreateCatalogItemRequest()
+ {
+ CatalogBrandId = _testBrandId,
+ CatalogTypeId = _testTypeId,
+ Description = _testDescription,
+ Name = _testName,
+ Price = _testPrice
+ };
+ var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json");
+
+ return jsonContent;
+ }
+ }
+}
diff --git a/tests/PublicApiIntegrationTests/CatalogItemEndpoints/DeleteCatalogItemEndpointTest.cs b/tests/PublicApiIntegrationTests/CatalogItemEndpoints/DeleteCatalogItemEndpointTest.cs
new file mode 100644
index 0000000..f41976e
--- /dev/null
+++ b/tests/PublicApiIntegrationTests/CatalogItemEndpoints/DeleteCatalogItemEndpointTest.cs
@@ -0,0 +1,38 @@
+using BlazorShared.Models;
+using Microsoft.eShopWeb;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Net;
+using System.Net.Http.Headers;
+using System.Threading.Tasks;
+
+namespace PublicApiIntegrationTests.CatalogItemEndpoints
+{
+ [TestClass]
+ public class DeleteCatalogItemEndpointTest
+ {
+ [TestMethod]
+ public async Task ReturnsSuccessGivenValidIdAndAdminUserToken()
+ {
+ var adminToken = ApiTokenHelper.GetAdminUserToken();
+ var client = ProgramTest.NewClient;
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", adminToken);
+ var response = await client.DeleteAsync("api/catalog-items/12");
+ response.EnsureSuccessStatusCode();
+ var stringResponse = await response.Content.ReadAsStringAsync();
+ var model = stringResponse.FromJson();
+
+ Assert.AreEqual("Deleted", model.Status);
+ }
+
+ [TestMethod]
+ public async Task ReturnsNotFoundGivenInvalidIdAndAdminUserToken()
+ {
+ var adminToken = ApiTokenHelper.GetAdminUserToken();
+ var client = ProgramTest.NewClient;
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", adminToken);
+ var response = await client.DeleteAsync("api/catalog-items/0");
+
+ Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode);
+ }
+ }
+}
diff --git a/tests/PublicApiIntegrationTests/ProgramTest.cs b/tests/PublicApiIntegrationTests/ProgramTest.cs
new file mode 100644
index 0000000..ca92234
--- /dev/null
+++ b/tests/PublicApiIntegrationTests/ProgramTest.cs
@@ -0,0 +1,27 @@
+using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Net.Http;
+
+namespace PublicApiIntegrationTests
+{
+ [TestClass]
+ public class ProgramTest
+ {
+ private static WebApplicationFactory _application;
+
+ public static HttpClient NewClient
+ {
+ get
+ {
+ return _application.CreateClient();
+ }
+ }
+
+ [AssemblyInitialize]
+ public static void AssemblyInitialize(TestContext _)
+ {
+ _application = new WebApplicationFactory();
+
+ }
+ }
+}
diff --git a/tests/PublicApiIntegrationTests/PublicApiIntegrationTests.csproj b/tests/PublicApiIntegrationTests/PublicApiIntegrationTests.csproj
new file mode 100644
index 0000000..1b3a9e6
--- /dev/null
+++ b/tests/PublicApiIntegrationTests/PublicApiIntegrationTests.csproj
@@ -0,0 +1,35 @@
+
+
+
+ net6.0
+ enable
+
+ false
+
+
+
+
+
+
+
+
+ Always
+ true
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/PublicApiIntegrationTests/appsettings.json b/tests/PublicApiIntegrationTests/appsettings.json
new file mode 100644
index 0000000..417e68b
--- /dev/null
+++ b/tests/PublicApiIntegrationTests/appsettings.json
@@ -0,0 +1,3 @@
+{
+ "UseOnlyInMemoryDatabase": true
+}